@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.mjs CHANGED
@@ -508,11 +508,11 @@ var ToolLatticeManager = class _ToolLatticeManager extends BaseLatticeManager {
508
508
  * @param key Lattice键名
509
509
  * @param tool 已有的StructuredTool实例
510
510
  */
511
- registerExistingTool(key, tool36) {
511
+ registerExistingTool(key, tool38) {
512
512
  const config = {
513
- name: tool36.name,
514
- description: tool36.description,
515
- schema: tool36.schema,
513
+ name: tool38.name,
514
+ description: tool38.description,
515
+ schema: tool38.schema,
516
516
  // StructuredTool的schema已经是Zod兼容的
517
517
  needUserApprove: false
518
518
  // MCP工具默认不需要用户批准
@@ -520,7 +520,7 @@ var ToolLatticeManager = class _ToolLatticeManager extends BaseLatticeManager {
520
520
  const toolLattice = {
521
521
  key,
522
522
  config,
523
- client: tool36
523
+ client: tool38
524
524
  };
525
525
  this.register(key, toolLattice);
526
526
  }
@@ -546,7 +546,7 @@ var ToolLatticeManager = class _ToolLatticeManager extends BaseLatticeManager {
546
546
  };
547
547
  var toolLatticeManager = ToolLatticeManager.getInstance();
548
548
  var registerToolLattice = (key, config, executor) => toolLatticeManager.registerLattice(key, config, executor);
549
- var registerExistingTool = (key, tool36) => toolLatticeManager.registerExistingTool(key, tool36);
549
+ var registerExistingTool = (key, tool38) => toolLatticeManager.registerExistingTool(key, tool38);
550
550
  var getToolLattice = (key) => toolLatticeManager.getToolLattice(key);
551
551
  var getToolDefinition = (key) => toolLatticeManager.getToolDefinition(key);
552
552
  var getToolClient = (key) => toolLatticeManager.getToolClient(key);
@@ -618,7 +618,7 @@ var PostgresDatabase = class {
618
618
  this.config = config;
619
619
  }
620
620
  async connect() {
621
- if (this.connected) return;
621
+ if (this.connected && this.pool) return;
622
622
  try {
623
623
  const { Pool } = await import("pg");
624
624
  const poolConfig = this.config.connectionString ? { connectionString: this.config.connectionString } : {
@@ -631,16 +631,27 @@ var PostgresDatabase = class {
631
631
  };
632
632
  this.pool = new Pool(poolConfig);
633
633
  const client = await this.pool.connect();
634
- client.release();
634
+ try {
635
+ await client.query("SELECT 1");
636
+ } finally {
637
+ client.release();
638
+ }
635
639
  this.connected = true;
636
640
  } catch (error) {
641
+ this.connected = false;
637
642
  throw new Error(`Failed to connect to PostgreSQL: ${error}`);
638
643
  }
639
644
  }
640
645
  async disconnect() {
641
646
  if (this.pool) {
642
- await this.pool.end();
643
- this.connected = false;
647
+ try {
648
+ await this.pool.end();
649
+ } catch (error) {
650
+ console.warn("Warning: Error closing PostgreSQL pool:", error);
651
+ } finally {
652
+ this.pool = null;
653
+ this.connected = false;
654
+ }
644
655
  }
645
656
  }
646
657
  async listTables() {
@@ -836,45 +847,55 @@ var SqlDatabaseManager = class _SqlDatabaseManager {
836
847
  await database.disconnect();
837
848
  }
838
849
  }
839
- };
840
- function parseConnectionString(connectionString) {
841
- const url = new URL(connectionString);
842
- const protocol = url.protocol.replace(":", "");
843
- if (protocol !== "postgres" && protocol !== "postgresql") {
844
- throw new Error(`Unsupported protocol: ${protocol}. Only postgresql:// is supported.`);
850
+ /**
851
+ * Load database configurations from a DatabaseConfigStore
852
+ * and register them with this manager
853
+ *
854
+ * @param store - The database configuration store
855
+ * @param tenantId - Tenant identifier
856
+ */
857
+ async loadConfigsFromStore(store, tenantId) {
858
+ const configs = await store.getAllConfigs(tenantId);
859
+ for (const entry of configs) {
860
+ this.registerDatabase(entry.key, entry.config);
861
+ }
845
862
  }
846
- return {
847
- host: url.hostname || "localhost",
848
- port: parseInt(url.port || "5432"),
849
- database: url.pathname.slice(1) || url.pathname,
850
- user: url.username,
851
- password: url.password,
852
- ssl: url.searchParams.get("sslmode") === "require"
853
- };
854
- }
863
+ /**
864
+ * Load all database configurations from a DatabaseConfigStore
865
+ * across all tenants and register them with this manager
866
+ *
867
+ * @param store - The database configuration store
868
+ */
869
+ async loadAllConfigsFromStore(store) {
870
+ const configs = await store.getAllConfigsWithoutTenant();
871
+ for (const entry of configs) {
872
+ this.registerDatabase(entry.key, entry.config);
873
+ }
874
+ }
875
+ };
855
876
  var sqlDatabaseManager = SqlDatabaseManager.getInstance();
856
877
 
857
878
  // src/tool_lattice/sql/list_tables_sql.ts
858
879
  import z3 from "zod";
859
880
  import { tool as tool2 } from "langchain";
860
881
  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.`;
861
- var createListTablesSqlTool = ({ databaseKey, connectionString }) => {
882
+ var createListTablesSqlTool = ({ databaseKeys, databaseDescriptions }) => {
883
+ const availableDbsText = databaseKeys.length > 0 ? `
884
+
885
+ Available databases:
886
+ ${databaseKeys.map(
887
+ (key) => `- ${key}${databaseDescriptions?.[key] ? `: ${databaseDescriptions[key]}` : ""}`
888
+ ).join("\n")}` : "";
862
889
  return tool2(
863
- async (_input, exe_config) => {
890
+ async ({ databaseKey }, exe_config) => {
864
891
  try {
865
- let dbKey;
866
- if (databaseKey) {
867
- dbKey = databaseKey;
868
- } else if (connectionString) {
869
- dbKey = connectionString;
870
- if (!sqlDatabaseManager.hasDatabase(dbKey)) {
871
- const config = parseConnectionString(connectionString);
872
- sqlDatabaseManager.registerDatabase(dbKey, { ...config, type: "postgres" });
873
- }
874
- } else {
875
- return "Error: Must provide databaseKey or connectionString";
892
+ if (!databaseKey) {
893
+ return "Error: databaseKey parameter is required. Available databases: " + databaseKeys.join(", ");
876
894
  }
877
- const database = sqlDatabaseManager.getDatabase(dbKey);
895
+ if (!databaseKeys.includes(databaseKey)) {
896
+ return `Error: databaseKey "${databaseKey}" is not in the allowed list: [${databaseKeys.join(", ")}]`;
897
+ }
898
+ const database = sqlDatabaseManager.getDatabase(databaseKey);
878
899
  const tables = await database.listTables();
879
900
  if (tables.length === 0) {
880
901
  return "No tables found in the database.";
@@ -889,8 +910,10 @@ var createListTablesSqlTool = ({ databaseKey, connectionString }) => {
889
910
  },
890
911
  {
891
912
  name: "list_tables_sql",
892
- description: LIST_TABLES_SQL_DESCRIPTION,
893
- schema: z3.object({})
913
+ description: `${LIST_TABLES_SQL_DESCRIPTION}${availableDbsText}`,
914
+ schema: z3.object({
915
+ databaseKey: z3.string().describe(`Target database to list tables. Choose from: ${databaseKeys.join(", ")}`)
916
+ })
894
917
  }
895
918
  );
896
919
  };
@@ -899,49 +922,26 @@ var createListTablesSqlTool = ({ databaseKey, connectionString }) => {
899
922
  import z4 from "zod";
900
923
  import { tool as tool3 } from "langchain";
901
924
  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.`;
902
- function formatTableSchema(schema) {
903
- const lines = [];
904
- lines.push(`
905
- Table: ${schema.tableName}`);
906
- lines.push("-".repeat(40));
907
- lines.push("Columns:");
908
- for (const col of schema.columns) {
909
- const constraints = [];
910
- if (col.isPrimaryKey) constraints.push("PRIMARY KEY");
911
- if (col.isForeignKey && col.foreignKeyRef)
912
- constraints.push(`FK -> ${col.foreignKeyRef}`);
913
- if (!col.nullable) constraints.push("NOT NULL");
914
- const constraintStr = constraints.length > 0 ? ` [${constraints.join(", ")}]` : "";
915
- lines.push(` - ${col.name}: ${col.type}${constraintStr}`);
916
- }
917
- if (schema.sampleRows && schema.sampleRows.length > 0) {
918
- lines.push("\nSample Rows (up to 3):");
919
- for (const row of schema.sampleRows) {
920
- const rowStr = Object.entries(row).map(([k, v]) => `${k}=${JSON.stringify(v)}`).join(", ");
921
- lines.push(` ${rowStr}`);
922
- }
923
- }
924
- return lines.join("\n");
925
- }
926
- var createInfoSqlTool = ({ databaseKey, connectionString }) => {
925
+ var createInfoSqlTool = ({ databaseKeys, databaseDescriptions }) => {
926
+ const availableDbsText = databaseKeys.length > 0 ? `
927
+
928
+ Available databases:
929
+ ${databaseKeys.map(
930
+ (key) => `- ${key}${databaseDescriptions?.[key] ? `: ${databaseDescriptions[key]}` : ""}`
931
+ ).join("\n")}` : "";
927
932
  return tool3(
928
933
  async ({
929
- tables
934
+ tables,
935
+ databaseKey
930
936
  }, exe_config) => {
931
937
  try {
932
- let dbKey;
933
- if (databaseKey) {
934
- dbKey = databaseKey;
935
- } else if (connectionString) {
936
- dbKey = connectionString;
937
- if (!sqlDatabaseManager.hasDatabase(dbKey)) {
938
- const config = parseConnectionString(connectionString);
939
- sqlDatabaseManager.registerDatabase(dbKey, { ...config, type: "postgres" });
940
- }
941
- } else {
942
- return "Error: Must provide databaseKey or connectionString";
938
+ if (!databaseKey) {
939
+ return "Error: databaseKey parameter is required. Available databases: " + databaseKeys.join(", ");
940
+ }
941
+ if (!databaseKeys.includes(databaseKey)) {
942
+ return `Error: databaseKey "${databaseKey}" is not in the allowed list: [${databaseKeys.join(", ")}]`;
943
943
  }
944
- const database = sqlDatabaseManager.getDatabase(dbKey);
944
+ const database = sqlDatabaseManager.getDatabase(databaseKey);
945
945
  const tableNames = tables.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
946
946
  if (tableNames.length === 0) {
947
947
  return "Error: No table names provided. Please provide a comma-separated list of table names.";
@@ -958,15 +958,40 @@ var createInfoSqlTool = ({ databaseKey, connectionString }) => {
958
958
  },
959
959
  {
960
960
  name: "info_sql",
961
- description: INFO_SQL_DESCRIPTION,
961
+ description: `${INFO_SQL_DESCRIPTION}${availableDbsText}`,
962
962
  schema: z4.object({
963
963
  tables: z4.string().describe(
964
964
  "Comma-separated list of table names to get information for. Example: 'users, orders, products'"
965
- )
965
+ ),
966
+ databaseKey: z4.string().describe(`Target database to get table info. Choose from: ${databaseKeys.join(", ")}`)
966
967
  })
967
968
  }
968
969
  );
969
970
  };
971
+ function formatTableSchema(schema) {
972
+ const lines = [];
973
+ lines.push(`
974
+ Table: ${schema.tableName}`);
975
+ lines.push("-".repeat(40));
976
+ lines.push("Columns:");
977
+ for (const col of schema.columns) {
978
+ const constraints = [];
979
+ if (col.isPrimaryKey) constraints.push("PRIMARY KEY");
980
+ if (col.isForeignKey && col.foreignKeyRef)
981
+ constraints.push(`FK -> ${col.foreignKeyRef}`);
982
+ if (!col.nullable) constraints.push("NOT NULL");
983
+ const constraintStr = constraints.length > 0 ? ` [${constraints.join(", ")}]` : "";
984
+ lines.push(` - ${col.name}: ${col.type}${constraintStr}`);
985
+ }
986
+ if (schema.sampleRows && schema.sampleRows.length > 0) {
987
+ lines.push("\nSample Rows (up to 3):");
988
+ for (const row of schema.sampleRows) {
989
+ const rowStr = Object.entries(row).map(([k, v]) => `${k}=${JSON.stringify(v)}`).join(", ");
990
+ lines.push(` ${rowStr}`);
991
+ }
992
+ }
993
+ return lines.join("\n");
994
+ }
970
995
 
971
996
  // src/tool_lattice/sql/query_checker_sql.ts
972
997
  import z5 from "zod";
@@ -1028,10 +1053,17 @@ function checkDangerousOperations(query) {
1028
1053
  }
1029
1054
  return warnings;
1030
1055
  }
1031
- var createQueryCheckerSqlTool = ({ databaseKey, connectionString }) => {
1056
+ var createQueryCheckerSqlTool = ({ databaseKeys, databaseDescriptions }) => {
1057
+ const availableDbsText = databaseKeys.length > 0 ? `
1058
+
1059
+ Available databases:
1060
+ ${databaseKeys.map(
1061
+ (key) => `- ${key}${databaseDescriptions?.[key] ? `: ${databaseDescriptions[key]}` : ""}`
1062
+ ).join("\n")}` : "";
1032
1063
  return tool4(
1033
1064
  async ({
1034
- query
1065
+ query,
1066
+ databaseKey
1035
1067
  }, exe_config) => {
1036
1068
  try {
1037
1069
  const trimmedQuery = query.trim();
@@ -1063,19 +1095,9 @@ ${trimmedQuery}
1063
1095
  } else {
1064
1096
  results.push("Safety: No dangerous operations detected");
1065
1097
  }
1066
- try {
1067
- let dbKey;
1068
- if (databaseKey) {
1069
- dbKey = databaseKey;
1070
- } else if (connectionString) {
1071
- dbKey = connectionString;
1072
- if (!sqlDatabaseManager.hasDatabase(dbKey)) {
1073
- const config = parseConnectionString(connectionString);
1074
- sqlDatabaseManager.registerDatabase(dbKey, { ...config, type: "postgres" });
1075
- }
1076
- }
1077
- if (dbKey) {
1078
- const database = sqlDatabaseManager.getDatabase(dbKey);
1098
+ if (databaseKey && databaseKeys.includes(databaseKey)) {
1099
+ try {
1100
+ const database = sqlDatabaseManager.getDatabase(databaseKey);
1079
1101
  const dbType = database.getDatabaseType();
1080
1102
  if (dbType === "postgres") {
1081
1103
  try {
@@ -1087,10 +1109,14 @@ ${trimmedQuery}
1087
1109
  );
1088
1110
  }
1089
1111
  }
1112
+ } catch {
1113
+ results.push(
1114
+ "Database Validation: Skipped (database connection unavailable)"
1115
+ );
1090
1116
  }
1091
- } catch {
1117
+ } else {
1092
1118
  results.push(
1093
- "Database Validation: Skipped (no database connection available)"
1119
+ "Database Validation: Skipped (no valid databaseKey provided)"
1094
1120
  );
1095
1121
  }
1096
1122
  const hasErrors = syntaxIssues.some((i) => !i.startsWith("Warning:")) || dangerWarnings.length > 0;
@@ -1109,9 +1135,10 @@ ${trimmedQuery}
1109
1135
  },
1110
1136
  {
1111
1137
  name: "query_checker_sql",
1112
- 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.",
1138
+ 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}`,
1113
1139
  schema: z5.object({
1114
- query: z5.string().describe("The SQL query to check and validate.")
1140
+ query: z5.string().describe("The SQL query to check and validate."),
1141
+ databaseKey: z5.string().describe(`Target database to validate query against. Choose from: ${databaseKeys.join(", ")}`)
1115
1142
  })
1116
1143
  }
1117
1144
  );
@@ -1120,7 +1147,6 @@ ${trimmedQuery}
1120
1147
  // src/tool_lattice/sql/query_sql.ts
1121
1148
  import z6 from "zod";
1122
1149
  import { tool as tool5 } from "langchain";
1123
- 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.`;
1124
1150
  function formatQueryResult(rows, fields) {
1125
1151
  if (rows.length === 0) {
1126
1152
  return "Query executed successfully. No rows returned.";
@@ -1148,29 +1174,30 @@ function formatQueryResult(rows, fields) {
1148
1174
  Total rows: ${rows.length}`);
1149
1175
  return lines.join("\n");
1150
1176
  }
1151
- var createQuerySqlTool = ({ databaseKey, connectionString }) => {
1177
+ var createQuerySqlTool = ({ databaseKeys, databaseDescriptions }) => {
1178
+ const availableDbsText = databaseKeys.length > 0 ? `
1179
+
1180
+ Available databases:
1181
+ ${databaseKeys.map(
1182
+ (key) => `- ${key}${databaseDescriptions?.[key] ? `: ${databaseDescriptions[key]}` : ""}`
1183
+ ).join("\n")}` : "";
1152
1184
  return tool5(
1153
1185
  async ({
1154
- query
1186
+ query,
1187
+ databaseKey
1155
1188
  }, exe_config) => {
1156
1189
  try {
1157
- let dbKey;
1158
- if (databaseKey) {
1159
- dbKey = databaseKey;
1160
- } else if (connectionString) {
1161
- dbKey = connectionString;
1162
- if (!sqlDatabaseManager.hasDatabase(dbKey)) {
1163
- const config = parseConnectionString(connectionString);
1164
- sqlDatabaseManager.registerDatabase(dbKey, { ...config, type: "postgres" });
1165
- }
1166
- } else {
1167
- return "Error: Must provide databaseKey or connectionString";
1190
+ if (!databaseKey) {
1191
+ return "Error: databaseKey parameter is required. Available databases: " + databaseKeys.join(", ");
1192
+ }
1193
+ if (!databaseKeys.includes(databaseKey)) {
1194
+ return `Error: databaseKey "${databaseKey}" is not in the allowed list: [${databaseKeys.join(", ")}]`;
1168
1195
  }
1169
1196
  const trimmedQuery = query.trim();
1170
1197
  if (!trimmedQuery) {
1171
1198
  return "Error: Empty query provided. Please provide a valid SQL query.";
1172
1199
  }
1173
- const database = sqlDatabaseManager.getDatabase(dbKey);
1200
+ const database = sqlDatabaseManager.getDatabase(databaseKey);
1174
1201
  const result = await database.executeQuery(trimmedQuery);
1175
1202
  return formatQueryResult(result.rows, result.fields);
1176
1203
  } catch (error) {
@@ -1179,11 +1206,12 @@ var createQuerySqlTool = ({ databaseKey, connectionString }) => {
1179
1206
  },
1180
1207
  {
1181
1208
  name: "query_sql",
1182
- description: QUERY_SQL_DESCRIPTION,
1209
+ description: `Execute a SQL query against the database and return the results.${availableDbsText}`,
1183
1210
  schema: z6.object({
1184
1211
  query: z6.string().describe(
1185
1212
  "The SQL query to execute. Should be a valid SELECT, INSERT, UPDATE, or DELETE statement."
1186
- )
1213
+ ),
1214
+ databaseKey: z6.string().describe(`Target database to execute the query. Choose from: ${databaseKeys.join(", ")}`)
1187
1215
  })
1188
1216
  }
1189
1217
  );
@@ -2471,10 +2499,13 @@ import {
2471
2499
  AgentConfig,
2472
2500
  ReactAgentConfig,
2473
2501
  DeepAgentConfig,
2502
+ TeamAgentConfig,
2503
+ TeamTeammateConfig,
2474
2504
  AgentConfigWithTools,
2475
2505
  GraphBuildOptions,
2476
2506
  hasTools,
2477
2507
  isDeepAgentConfig,
2508
+ isTeamAgentConfig,
2478
2509
  getToolsFromConfig,
2479
2510
  getSubAgentsFromConfig
2480
2511
  } from "@axiom-lattice/protocols";
@@ -2610,8 +2641,8 @@ var SandboxFilesystem = class {
2610
2641
  */
2611
2642
  toVirtualPath(realPath) {
2612
2643
  const rootPath = path2.join(this.homeDir, this.workingDirectory);
2613
- const relative2 = path2.relative(rootPath, realPath);
2614
- const normalized = relative2.split(path2.sep).join("/");
2644
+ const relative3 = path2.relative(rootPath, realPath);
2645
+ const normalized = relative3.split(path2.sep).join("/");
2615
2646
  return "/" + normalized;
2616
2647
  }
2617
2648
  /**
@@ -2893,17 +2924,17 @@ function createBrowserMiddleware(params = { isolatedLevel: "global" }) {
2893
2924
  // src/middlewares/sqlMiddleware.ts
2894
2925
  import { createMiddleware as createMiddleware3 } from "langchain";
2895
2926
  function createSqlMiddleware(params) {
2896
- const { connectionMode, databaseKey, connectionString } = params;
2897
- const toolParams = {};
2898
- if (connectionMode === "databaseKey" && databaseKey) {
2899
- toolParams.databaseKey = databaseKey;
2900
- } else if (connectionString) {
2901
- toolParams.connectionString = connectionString;
2902
- } else if (databaseKey) {
2903
- toolParams.databaseKey = databaseKey;
2904
- } else if (connectionString) {
2905
- toolParams.connectionString = connectionString;
2927
+ const { databaseKeys, databaseDescriptions } = params;
2928
+ if (!databaseKeys || databaseKeys.length === 0) {
2929
+ return createMiddleware3({
2930
+ name: "sqlMiddleware",
2931
+ tools: []
2932
+ });
2906
2933
  }
2934
+ const toolParams = {
2935
+ databaseKeys,
2936
+ databaseDescriptions
2937
+ };
2907
2938
  return createMiddleware3({
2908
2939
  name: "sqlMiddleware",
2909
2940
  tools: [
@@ -3568,6 +3599,258 @@ ${body}` : `${frontmatter}
3568
3599
  }
3569
3600
  };
3570
3601
 
3602
+ // src/store_lattice/InMemoryWorkspaceStore.ts
3603
+ var InMemoryWorkspaceStore = class {
3604
+ constructor() {
3605
+ this.workspaces = /* @__PURE__ */ new Map();
3606
+ this.initDefaultData();
3607
+ }
3608
+ initDefaultData() {
3609
+ const defaultTenantId = "default";
3610
+ const now = /* @__PURE__ */ new Date();
3611
+ const defaultWorkspace = {
3612
+ id: "default-workspace",
3613
+ tenantId: defaultTenantId,
3614
+ name: "\u9ED8\u8BA4\u5DE5\u4F5C\u533A",
3615
+ description: "\u7CFB\u7EDF\u9ED8\u8BA4\u5DE5\u4F5C\u533A",
3616
+ storageType: "sandbox",
3617
+ createdAt: now,
3618
+ updatedAt: now
3619
+ };
3620
+ this.workspaces.set(`${defaultTenantId}:default-workspace`, defaultWorkspace);
3621
+ }
3622
+ getKey(tenantId, id) {
3623
+ return `${tenantId}:${id}`;
3624
+ }
3625
+ async getAllWorkspaces(tenantId) {
3626
+ return Array.from(this.workspaces.values()).filter(
3627
+ (w) => w.tenantId === tenantId
3628
+ );
3629
+ }
3630
+ async getWorkspaceById(tenantId, id) {
3631
+ const key = this.getKey(tenantId, id);
3632
+ return this.workspaces.get(key) || null;
3633
+ }
3634
+ async createWorkspace(tenantId, id, data) {
3635
+ const now = /* @__PURE__ */ new Date();
3636
+ const workspace = {
3637
+ id,
3638
+ tenantId,
3639
+ name: data.name,
3640
+ description: data.description,
3641
+ storageType: data.storageType,
3642
+ createdAt: now,
3643
+ updatedAt: now
3644
+ };
3645
+ const key = this.getKey(tenantId, id);
3646
+ this.workspaces.set(key, workspace);
3647
+ return workspace;
3648
+ }
3649
+ async updateWorkspace(tenantId, id, updates) {
3650
+ const key = this.getKey(tenantId, id);
3651
+ const existing = this.workspaces.get(key);
3652
+ if (!existing) {
3653
+ return null;
3654
+ }
3655
+ const updated = {
3656
+ ...existing,
3657
+ ...updates,
3658
+ updatedAt: /* @__PURE__ */ new Date()
3659
+ };
3660
+ this.workspaces.set(key, updated);
3661
+ return updated;
3662
+ }
3663
+ async deleteWorkspace(tenantId, id) {
3664
+ const key = this.getKey(tenantId, id);
3665
+ return this.workspaces.delete(key);
3666
+ }
3667
+ clear() {
3668
+ this.workspaces.clear();
3669
+ }
3670
+ };
3671
+
3672
+ // src/store_lattice/InMemoryProjectStore.ts
3673
+ var InMemoryProjectStore = class {
3674
+ constructor() {
3675
+ this.projects = /* @__PURE__ */ new Map();
3676
+ this.initDefaultData();
3677
+ }
3678
+ initDefaultData() {
3679
+ const defaultTenantId = "default";
3680
+ const defaultWorkspaceId = "default-workspace";
3681
+ const now = /* @__PURE__ */ new Date();
3682
+ const defaultProject = {
3683
+ id: "default-project",
3684
+ tenantId: defaultTenantId,
3685
+ workspaceId: defaultWorkspaceId,
3686
+ name: "\u9ED8\u8BA4\u9879\u76EE",
3687
+ description: "\u7CFB\u7EDF\u9ED8\u8BA4\u9879\u76EE",
3688
+ createdAt: now,
3689
+ updatedAt: now
3690
+ };
3691
+ this.projects.set(`${defaultTenantId}:default-project`, defaultProject);
3692
+ }
3693
+ getKey(tenantId, id) {
3694
+ return `${tenantId}:${id}`;
3695
+ }
3696
+ async getProjectsByWorkspace(tenantId, workspaceId) {
3697
+ return Array.from(this.projects.values()).filter(
3698
+ (p) => p.tenantId === tenantId && p.workspaceId === workspaceId
3699
+ );
3700
+ }
3701
+ async getProjectById(tenantId, id) {
3702
+ const key = this.getKey(tenantId, id);
3703
+ return this.projects.get(key) || null;
3704
+ }
3705
+ async createProject(tenantId, workspaceId, id, data) {
3706
+ const now = /* @__PURE__ */ new Date();
3707
+ const project = {
3708
+ id,
3709
+ tenantId,
3710
+ workspaceId,
3711
+ name: data.name,
3712
+ description: data.description,
3713
+ createdAt: now,
3714
+ updatedAt: now
3715
+ };
3716
+ const key = this.getKey(tenantId, id);
3717
+ this.projects.set(key, project);
3718
+ return project;
3719
+ }
3720
+ async updateProject(tenantId, id, updates) {
3721
+ const key = this.getKey(tenantId, id);
3722
+ const existing = this.projects.get(key);
3723
+ if (!existing) {
3724
+ return null;
3725
+ }
3726
+ const updated = {
3727
+ ...existing,
3728
+ ...updates,
3729
+ updatedAt: /* @__PURE__ */ new Date()
3730
+ };
3731
+ this.projects.set(key, updated);
3732
+ return updated;
3733
+ }
3734
+ async deleteProject(tenantId, id) {
3735
+ const key = this.getKey(tenantId, id);
3736
+ return this.projects.delete(key);
3737
+ }
3738
+ clear() {
3739
+ this.projects.clear();
3740
+ }
3741
+ };
3742
+
3743
+ // src/store_lattice/InMemoryDatabaseConfigStore.ts
3744
+ var InMemoryDatabaseConfigStore = class {
3745
+ constructor() {
3746
+ this.configs = /* @__PURE__ */ new Map();
3747
+ }
3748
+ /**
3749
+ * Get composite key for storage
3750
+ */
3751
+ getKey(tenantId, id) {
3752
+ return `${tenantId}:${id}`;
3753
+ }
3754
+ /**
3755
+ * Get all database configurations for a tenant
3756
+ */
3757
+ async getAllConfigs(tenantId) {
3758
+ return Array.from(this.configs.values()).filter(
3759
+ (config) => config.tenantId === tenantId
3760
+ );
3761
+ }
3762
+ /**
3763
+ * Get all database configurations across all tenants
3764
+ */
3765
+ async getAllConfigsWithoutTenant() {
3766
+ return Array.from(this.configs.values());
3767
+ }
3768
+ /**
3769
+ * Get database configuration by ID
3770
+ */
3771
+ async getConfigById(tenantId, id) {
3772
+ const key = this.getKey(tenantId, id);
3773
+ return this.configs.get(key) || null;
3774
+ }
3775
+ /**
3776
+ * Get database configuration by business key
3777
+ */
3778
+ async getConfigByKey(tenantId, key) {
3779
+ const configs = await this.getAllConfigs(tenantId);
3780
+ return configs.find((config) => config.key === key) || null;
3781
+ }
3782
+ /**
3783
+ * Create a new database configuration
3784
+ */
3785
+ async createConfig(tenantId, id, data) {
3786
+ const now = /* @__PURE__ */ new Date();
3787
+ const entry = {
3788
+ id,
3789
+ tenantId,
3790
+ key: data.key,
3791
+ config: data.config,
3792
+ name: data.name,
3793
+ description: data.description,
3794
+ createdAt: now,
3795
+ updatedAt: now
3796
+ };
3797
+ const storageKey = this.getKey(tenantId, id);
3798
+ this.configs.set(storageKey, entry);
3799
+ return entry;
3800
+ }
3801
+ /**
3802
+ * Update an existing database configuration
3803
+ */
3804
+ async updateConfig(tenantId, id, updates) {
3805
+ const key = this.getKey(tenantId, id);
3806
+ const existing = this.configs.get(key);
3807
+ if (!existing) {
3808
+ return null;
3809
+ }
3810
+ const updated = {
3811
+ ...existing,
3812
+ ...updates,
3813
+ config: updates.config ? { ...existing.config, ...updates.config } : existing.config,
3814
+ key: updates.key || existing.key,
3815
+ name: updates.name !== void 0 ? updates.name : existing.name,
3816
+ description: updates.description !== void 0 ? updates.description : existing.description,
3817
+ updatedAt: /* @__PURE__ */ new Date()
3818
+ };
3819
+ this.configs.set(key, updated);
3820
+ return updated;
3821
+ }
3822
+ /**
3823
+ * Delete a database configuration by ID
3824
+ */
3825
+ async deleteConfig(tenantId, id) {
3826
+ const key = this.getKey(tenantId, id);
3827
+ return this.configs.delete(key);
3828
+ }
3829
+ /**
3830
+ * Check if configuration exists
3831
+ */
3832
+ async hasConfig(tenantId, id) {
3833
+ const key = this.getKey(tenantId, id);
3834
+ return this.configs.has(key);
3835
+ }
3836
+ /**
3837
+ * Clear all configurations (useful for testing)
3838
+ */
3839
+ clear() {
3840
+ this.configs.clear();
3841
+ }
3842
+ /**
3843
+ * Clear configurations for a specific tenant
3844
+ */
3845
+ clearByTenant(tenantId) {
3846
+ for (const key of this.configs.keys()) {
3847
+ if (key.startsWith(`${tenantId}:`)) {
3848
+ this.configs.delete(key);
3849
+ }
3850
+ }
3851
+ }
3852
+ };
3853
+
3571
3854
  // src/store_lattice/StoreLatticeManager.ts
3572
3855
  var StoreLatticeManager = class _StoreLatticeManager extends BaseLatticeManager {
3573
3856
  /**
@@ -3695,6 +3978,9 @@ var getStoreLattice = (key, type) => storeLatticeManager.getStoreLattice(key, ty
3695
3978
  var defaultThreadStore = new InMemoryThreadStore();
3696
3979
  var defaultAssistantStore = new InMemoryAssistantStore();
3697
3980
  var defaultSkillStore = new FileSystemSkillStore();
3981
+ var defaultWorkspaceStore = new InMemoryWorkspaceStore();
3982
+ var defaultProjectStore = new InMemoryProjectStore();
3983
+ var defaultDatabaseConfigStore = new InMemoryDatabaseConfigStore();
3698
3984
  storeLatticeManager.registerLattice("default", "thread", defaultThreadStore);
3699
3985
  storeLatticeManager.registerLattice(
3700
3986
  "default",
@@ -3702,6 +3988,9 @@ storeLatticeManager.registerLattice(
3702
3988
  defaultAssistantStore
3703
3989
  );
3704
3990
  storeLatticeManager.registerLattice("default", "skill", defaultSkillStore);
3991
+ storeLatticeManager.registerLattice("default", "workspace", defaultWorkspaceStore);
3992
+ storeLatticeManager.registerLattice("default", "project", defaultProjectStore);
3993
+ storeLatticeManager.registerLattice("default", "database", defaultDatabaseConfigStore);
3705
3994
 
3706
3995
  // src/tool_lattice/skill/load_skills.ts
3707
3996
  import z32 from "zod";
@@ -3798,6 +4087,7 @@ var DEFAULT_EXTRA_NOTE = "Use the load_skill_content tool when you need detailed
3798
4087
  function createSkillMiddleware(params = {}) {
3799
4088
  const {
3800
4089
  skills = [],
4090
+ readAll = false,
3801
4091
  heading = DEFAULT_HEADING,
3802
4092
  extraNote = DEFAULT_EXTRA_NOTE
3803
4093
  } = params;
@@ -3805,14 +4095,16 @@ function createSkillMiddleware(params = {}) {
3805
4095
  return createMiddleware4({
3806
4096
  name: "skillMiddleware",
3807
4097
  tools: [
3808
- createLoadSkillsTool({ skills }),
4098
+ createLoadSkillsTool({ skills: readAll ? void 0 : skills }),
3809
4099
  createLoadSkillContentTool()
3810
4100
  ],
3811
4101
  beforeAgent: async () => {
3812
4102
  try {
3813
- if (skills && skills.length > 0) {
3814
- const storeLattice = getStoreLattice("default", "skill");
3815
- const skillStore = storeLattice?.store;
4103
+ const storeLattice = getStoreLattice("default", "skill");
4104
+ const skillStore = storeLattice?.store;
4105
+ if (readAll) {
4106
+ latestSkills = await skillStore.getAllSkills();
4107
+ } else if (skills && skills.length > 0) {
3816
4108
  const skillLatticePromises = skills.map(
3817
4109
  (skillId) => skillStore.getSkillById(skillId)
3818
4110
  );
@@ -3854,6 +4146,8 @@ import { basename } from "path";
3854
4146
  var EMPTY_CONTENT_WARNING = "System reminder: File exists but has empty contents";
3855
4147
  var MAX_LINE_LENGTH = 1e4;
3856
4148
  var LINE_NUMBER_WIDTH = 6;
4149
+ var TOOL_RESULT_TOKEN_LIMIT = 2e4;
4150
+ var TRUNCATION_GUIDANCE = "... [results truncated, try being more specific with your parameters]";
3857
4151
  function sanitizeToolCallId(toolCallId) {
3858
4152
  return toolCallId.replace(/\./g, "_").replace(/\//g, "_").replace(/\\/g, "_");
3859
4153
  }
@@ -3949,8 +4243,24 @@ function performStringReplacement(content, oldString, newString, replaceAll) {
3949
4243
  const newContent = content.split(oldString).join(newString);
3950
4244
  return [newContent, occurrences];
3951
4245
  }
3952
- function validatePath(path4) {
3953
- const pathStr = path4 || "/";
4246
+ function truncateIfTooLong(result) {
4247
+ if (Array.isArray(result)) {
4248
+ const totalChars = result.reduce((sum, item) => sum + item.length, 0);
4249
+ if (totalChars > TOOL_RESULT_TOKEN_LIMIT * 4) {
4250
+ const truncateAt = Math.floor(
4251
+ result.length * TOOL_RESULT_TOKEN_LIMIT * 4 / totalChars
4252
+ );
4253
+ return [...result.slice(0, truncateAt), TRUNCATION_GUIDANCE];
4254
+ }
4255
+ return result;
4256
+ }
4257
+ if (result.length > TOOL_RESULT_TOKEN_LIMIT * 4) {
4258
+ return result.substring(0, TOOL_RESULT_TOKEN_LIMIT * 4) + "\n" + TRUNCATION_GUIDANCE;
4259
+ }
4260
+ return result;
4261
+ }
4262
+ function validatePath(path5) {
4263
+ const pathStr = path5 || "/";
3954
4264
  if (!pathStr || pathStr.trim() === "") {
3955
4265
  throw new Error("Path cannot be empty");
3956
4266
  }
@@ -3960,10 +4270,10 @@ function validatePath(path4) {
3960
4270
  }
3961
4271
  return normalized;
3962
4272
  }
3963
- function globSearchFiles(files, pattern, path4 = "/") {
4273
+ function globSearchFiles(files, pattern, path5 = "/") {
3964
4274
  let normalizedPath;
3965
4275
  try {
3966
- normalizedPath = validatePath(path4);
4276
+ normalizedPath = validatePath(path5);
3967
4277
  } catch {
3968
4278
  return "No files found";
3969
4279
  }
@@ -3973,15 +4283,15 @@ function globSearchFiles(files, pattern, path4 = "/") {
3973
4283
  const effectivePattern = pattern;
3974
4284
  const matches = [];
3975
4285
  for (const [filePath, fileData] of Object.entries(filtered)) {
3976
- let relative2 = filePath.substring(normalizedPath.length);
3977
- if (relative2.startsWith("/")) {
3978
- relative2 = relative2.substring(1);
4286
+ let relative3 = filePath.substring(normalizedPath.length);
4287
+ if (relative3.startsWith("/")) {
4288
+ relative3 = relative3.substring(1);
3979
4289
  }
3980
- if (!relative2) {
4290
+ if (!relative3) {
3981
4291
  const parts = filePath.split("/");
3982
- relative2 = parts[parts.length - 1] || "";
4292
+ relative3 = parts[parts.length - 1] || "";
3983
4293
  }
3984
- if (micromatch.isMatch(relative2, effectivePattern, {
4294
+ if (micromatch.isMatch(relative3, effectivePattern, {
3985
4295
  dot: true,
3986
4296
  nobrace: false
3987
4297
  })) {
@@ -3994,7 +4304,28 @@ function globSearchFiles(files, pattern, path4 = "/") {
3994
4304
  }
3995
4305
  return matches.map(([fp]) => fp).join("\n");
3996
4306
  }
3997
- function grepMatchesFromFiles(files, pattern, path4 = null, glob = null) {
4307
+ function formatGrepResults(results, outputMode) {
4308
+ if (outputMode === "files_with_matches") {
4309
+ return Object.keys(results).sort().join("\n");
4310
+ }
4311
+ if (outputMode === "count") {
4312
+ const lines2 = [];
4313
+ for (const filePath of Object.keys(results).sort()) {
4314
+ const count = results[filePath].length;
4315
+ lines2.push(`${filePath}: ${count}`);
4316
+ }
4317
+ return lines2.join("\n");
4318
+ }
4319
+ const lines = [];
4320
+ for (const filePath of Object.keys(results).sort()) {
4321
+ lines.push(`${filePath}:`);
4322
+ for (const [lineNum, line] of results[filePath]) {
4323
+ lines.push(` ${lineNum}: ${line}`);
4324
+ }
4325
+ }
4326
+ return lines.join("\n");
4327
+ }
4328
+ function grepSearchFiles(files, pattern, path5 = null, glob = null, outputMode = "files_with_matches") {
3998
4329
  let regex;
3999
4330
  try {
4000
4331
  regex = new RegExp(pattern);
@@ -4003,9 +4334,9 @@ function grepMatchesFromFiles(files, pattern, path4 = null, glob = null) {
4003
4334
  }
4004
4335
  let normalizedPath;
4005
4336
  try {
4006
- normalizedPath = validatePath(path4);
4337
+ normalizedPath = validatePath(path5);
4007
4338
  } catch {
4008
- return [];
4339
+ return "No matches found";
4009
4340
  }
4010
4341
  let filtered = Object.fromEntries(
4011
4342
  Object.entries(files).filter(([fp]) => fp.startsWith(normalizedPath))
@@ -4017,49 +4348,106 @@ function grepMatchesFromFiles(files, pattern, path4 = null, glob = null) {
4017
4348
  )
4018
4349
  );
4019
4350
  }
4020
- const matches = [];
4351
+ const results = {};
4021
4352
  for (const [filePath, fileData] of Object.entries(filtered)) {
4022
4353
  for (let i = 0; i < fileData.content.length; i++) {
4023
4354
  const line = fileData.content[i];
4024
4355
  const lineNum = i + 1;
4025
4356
  if (regex.test(line)) {
4026
- matches.push({ path: filePath, line: lineNum, text: line });
4357
+ if (!results[filePath]) {
4358
+ results[filePath] = [];
4359
+ }
4360
+ results[filePath].push([lineNum, line]);
4027
4361
  }
4028
4362
  }
4029
4363
  }
4030
- return matches;
4031
- }
4032
-
4033
- // src/deep_agent_new/backends/state.ts
4034
- var StateBackend = class {
4035
- constructor(stateAndStore) {
4036
- this.stateAndStore = stateAndStore;
4364
+ if (Object.keys(results).length === 0) {
4365
+ return "No matches found";
4037
4366
  }
4038
- /**
4039
- * Get files from current state.
4040
- */
4041
- getFiles() {
4042
- return this.stateAndStore.state.files || {};
4367
+ return formatGrepResults(results, outputMode);
4368
+ }
4369
+ function grepMatchesFromFiles(files, pattern, path5 = null, glob = null) {
4370
+ let regex;
4371
+ try {
4372
+ regex = new RegExp(pattern);
4373
+ } catch (e) {
4374
+ return `Invalid regex pattern: ${e.message}`;
4043
4375
  }
4044
- /**
4376
+ let normalizedPath;
4377
+ try {
4378
+ normalizedPath = validatePath(path5);
4379
+ } catch {
4380
+ return [];
4381
+ }
4382
+ let filtered = Object.fromEntries(
4383
+ Object.entries(files).filter(([fp]) => fp.startsWith(normalizedPath))
4384
+ );
4385
+ if (glob) {
4386
+ filtered = Object.fromEntries(
4387
+ Object.entries(filtered).filter(
4388
+ ([fp]) => micromatch.isMatch(basename(fp), glob, { dot: true, nobrace: false })
4389
+ )
4390
+ );
4391
+ }
4392
+ const matches = [];
4393
+ for (const [filePath, fileData] of Object.entries(filtered)) {
4394
+ for (let i = 0; i < fileData.content.length; i++) {
4395
+ const line = fileData.content[i];
4396
+ const lineNum = i + 1;
4397
+ if (regex.test(line)) {
4398
+ matches.push({ path: filePath, line: lineNum, text: line });
4399
+ }
4400
+ }
4401
+ }
4402
+ return matches;
4403
+ }
4404
+ function buildGrepResultsDict(matches) {
4405
+ const grouped = {};
4406
+ for (const m of matches) {
4407
+ if (!grouped[m.path]) {
4408
+ grouped[m.path] = [];
4409
+ }
4410
+ grouped[m.path].push([m.line, m.text]);
4411
+ }
4412
+ return grouped;
4413
+ }
4414
+ function formatGrepMatches(matches, outputMode) {
4415
+ if (matches.length === 0) {
4416
+ return "No matches found";
4417
+ }
4418
+ return formatGrepResults(buildGrepResultsDict(matches), outputMode);
4419
+ }
4420
+
4421
+ // src/deep_agent_new/backends/state.ts
4422
+ var StateBackend = class {
4423
+ constructor(stateAndStore) {
4424
+ this.stateAndStore = stateAndStore;
4425
+ }
4426
+ /**
4427
+ * Get files from current state.
4428
+ */
4429
+ getFiles() {
4430
+ return this.stateAndStore.state.files || {};
4431
+ }
4432
+ /**
4045
4433
  * List files and directories in the specified directory (non-recursive).
4046
4434
  *
4047
4435
  * @param path - Absolute path to directory
4048
4436
  * @returns List of FileInfo objects for files and directories directly in the directory.
4049
4437
  * Directories have a trailing / in their path and is_dir=true.
4050
4438
  */
4051
- lsInfo(path4) {
4439
+ lsInfo(path5) {
4052
4440
  const files = this.getFiles();
4053
4441
  const infos = [];
4054
4442
  const subdirs = /* @__PURE__ */ new Set();
4055
- const normalizedPath = path4.endsWith("/") ? path4 : path4 + "/";
4443
+ const normalizedPath = path5.endsWith("/") ? path5 : path5 + "/";
4056
4444
  for (const [k, fd] of Object.entries(files)) {
4057
4445
  if (!k.startsWith(normalizedPath)) {
4058
4446
  continue;
4059
4447
  }
4060
- const relative2 = k.substring(normalizedPath.length);
4061
- if (relative2.includes("/")) {
4062
- const subdirName = relative2.split("/")[0];
4448
+ const relative3 = k.substring(normalizedPath.length);
4449
+ if (relative3.includes("/")) {
4450
+ const subdirName = relative3.split("/")[0];
4063
4451
  subdirs.add(normalizedPath + subdirName + "/");
4064
4452
  continue;
4065
4453
  }
@@ -4158,16 +4546,16 @@ var StateBackend = class {
4158
4546
  /**
4159
4547
  * Structured search results or error string for invalid input.
4160
4548
  */
4161
- grepRaw(pattern, path4 = "/", glob = null) {
4549
+ grepRaw(pattern, path5 = "/", glob = null) {
4162
4550
  const files = this.getFiles();
4163
- return grepMatchesFromFiles(files, pattern, path4, glob);
4551
+ return grepMatchesFromFiles(files, pattern, path5, glob);
4164
4552
  }
4165
4553
  /**
4166
4554
  * Structured glob matching returning FileInfo objects.
4167
4555
  */
4168
- globInfo(pattern, path4 = "/") {
4556
+ globInfo(pattern, path5 = "/") {
4169
4557
  const files = this.getFiles();
4170
- const result = globSearchFiles(files, pattern, path4);
4558
+ const result = globSearchFiles(files, pattern, path5);
4171
4559
  if (result === "No files found") {
4172
4560
  return [];
4173
4561
  }
@@ -4248,15 +4636,17 @@ function createLsTool(backend, options) {
4248
4636
  const { customDescription } = options;
4249
4637
  return tool33(
4250
4638
  async (input, config) => {
4639
+ const { runConfig } = config.configurable;
4251
4640
  const stateAndStore = {
4252
4641
  state: getCurrentTaskInput(config),
4253
- store: config.store
4642
+ store: config.store,
4643
+ ...runConfig
4254
4644
  };
4255
4645
  const resolvedBackend = await getBackend(backend, stateAndStore);
4256
- const path4 = input.path || "/";
4257
- const infos = await resolvedBackend.lsInfo(path4);
4646
+ const path5 = input.path || "/";
4647
+ const infos = await resolvedBackend.lsInfo(path5);
4258
4648
  if (infos.length === 0) {
4259
- return `No files found in ${path4}`;
4649
+ return `No files found in ${path5}`;
4260
4650
  }
4261
4651
  const lines = [];
4262
4652
  for (const info of infos) {
@@ -4282,9 +4672,11 @@ function createReadFileTool(backend, options) {
4282
4672
  const { customDescription } = options;
4283
4673
  return tool33(
4284
4674
  async (input, config) => {
4675
+ const { runConfig } = config.configurable;
4285
4676
  const stateAndStore = {
4286
4677
  state: getCurrentTaskInput(config),
4287
- store: config.store
4678
+ store: config.store,
4679
+ ...runConfig
4288
4680
  };
4289
4681
  const resolvedBackend = await getBackend(backend, stateAndStore);
4290
4682
  const { file_path, offset = 0, limit = 2e3 } = input;
@@ -4305,9 +4697,11 @@ function createWriteFileTool(backend, options) {
4305
4697
  const { customDescription } = options;
4306
4698
  return tool33(
4307
4699
  async (input, config) => {
4700
+ const { runConfig } = config.configurable;
4308
4701
  const stateAndStore = {
4309
4702
  state: getCurrentTaskInput(config),
4310
- store: config.store
4703
+ store: config.store,
4704
+ ...runConfig
4311
4705
  };
4312
4706
  const resolvedBackend = await getBackend(backend, stateAndStore);
4313
4707
  const { file_path, content } = input;
@@ -4342,9 +4736,11 @@ function createEditFileTool(backend, options) {
4342
4736
  const { customDescription } = options;
4343
4737
  return tool33(
4344
4738
  async (input, config) => {
4739
+ const { runConfig } = config.configurable;
4345
4740
  const stateAndStore = {
4346
4741
  state: getCurrentTaskInput(config),
4347
- store: config.store
4742
+ store: config.store,
4743
+ ...runConfig
4348
4744
  };
4349
4745
  const resolvedBackend = await getBackend(backend, stateAndStore);
4350
4746
  const { file_path, old_string, new_string, replace_all = false } = input;
@@ -4386,13 +4782,15 @@ function createGlobTool(backend, options) {
4386
4782
  const { customDescription } = options;
4387
4783
  return tool33(
4388
4784
  async (input, config) => {
4785
+ const { runConfig } = config.configurable;
4389
4786
  const stateAndStore = {
4390
4787
  state: getCurrentTaskInput(config),
4391
- store: config.store
4788
+ store: config.store,
4789
+ ...runConfig
4392
4790
  };
4393
4791
  const resolvedBackend = await getBackend(backend, stateAndStore);
4394
- const { pattern, path: path4 = "/" } = input;
4395
- const infos = await resolvedBackend.globInfo(pattern, path4);
4792
+ const { pattern, path: path5 = "/" } = input;
4793
+ const infos = await resolvedBackend.globInfo(pattern, path5);
4396
4794
  if (infos.length === 0) {
4397
4795
  return `No files found matching pattern '${pattern}'`;
4398
4796
  }
@@ -4412,13 +4810,15 @@ function createGrepTool(backend, options) {
4412
4810
  const { customDescription } = options;
4413
4811
  return tool33(
4414
4812
  async (input, config) => {
4813
+ const { runConfig } = config.configurable;
4415
4814
  const stateAndStore = {
4416
4815
  state: getCurrentTaskInput(config),
4417
- store: config.store
4816
+ store: config.store,
4817
+ ...runConfig
4418
4818
  };
4419
4819
  const resolvedBackend = await getBackend(backend, stateAndStore);
4420
- const { pattern, path: path4 = "/", glob = null } = input;
4421
- const result = await resolvedBackend.grepRaw(pattern, path4, glob);
4820
+ const { pattern, path: path5 = "/", glob = null } = input;
4821
+ const result = await resolvedBackend.grepRaw(pattern, path5, glob);
4422
4822
  if (typeof result === "string") {
4423
4823
  return result;
4424
4824
  }
@@ -4588,7 +4988,22 @@ function createCommonMiddlewares(middlewareConfigs, filesystemBackend) {
4588
4988
  middlewares.push(createBrowserMiddleware(config.config));
4589
4989
  break;
4590
4990
  case "sql":
4591
- middlewares.push(createSqlMiddleware(config.config));
4991
+ {
4992
+ const sqlConfig = config.config;
4993
+ if (sqlConfig.databaseKeys && sqlConfig.databaseKeys.length > 0) {
4994
+ const databaseConfigs = global.__DATABASE_CONFIGS__ || [];
4995
+ const descriptions = {};
4996
+ for (const db of databaseConfigs) {
4997
+ if (db.key && sqlConfig.databaseKeys.includes(db.key)) {
4998
+ descriptions[db.key] = db.description || db.name || "";
4999
+ }
5000
+ }
5001
+ middlewares.push(createSqlMiddleware({
5002
+ databaseKeys: sqlConfig.databaseKeys,
5003
+ databaseDescriptions: descriptions
5004
+ }));
5005
+ }
5006
+ }
4592
5007
  break;
4593
5008
  case "skill":
4594
5009
  middlewares.push(createSkillMiddleware(config.config));
@@ -4607,6 +5022,7 @@ var ReActAgentGraphBuilder = class {
4607
5022
  }
4608
5023
  const isolatedLevel = filesystemConfig.config?.isolatedLevel || "global";
4609
5024
  return async (config) => {
5025
+ const { workspaceId, projectId } = config;
4610
5026
  let sandboxName = "global";
4611
5027
  if (isolatedLevel === "agent") {
4612
5028
  sandboxName = "agent";
@@ -4618,7 +5034,8 @@ var ReActAgentGraphBuilder = class {
4618
5034
  throw new Error("Sandbox manager not found");
4619
5035
  }
4620
5036
  return new SandboxFilesystem({
4621
- sandboxInstance: await sandboxManager.createSandbox(sandboxName)
5037
+ sandboxInstance: await sandboxManager.createSandbox(sandboxName),
5038
+ workingDirectory: workspaceId && projectId ? `/${workspaceId}/${projectId}` : "/"
4622
5039
  });
4623
5040
  };
4624
5041
  }
@@ -4634,9 +5051,9 @@ var ReActAgentGraphBuilder = class {
4634
5051
  */
4635
5052
  build(agentLattice, params) {
4636
5053
  const tools = params.tools.map((t) => {
4637
- const tool36 = getToolClient(t.key);
4638
- return tool36;
4639
- }).filter((tool36) => tool36 !== void 0);
5054
+ const tool38 = getToolClient(t.key);
5055
+ return tool38;
5056
+ }).filter((tool38) => tool38 !== void 0);
4640
5057
  const stateSchema2 = createReactAgentSchema(params.stateSchema);
4641
5058
  const middlewareConfigs = params.middleware || [];
4642
5059
  const filesystemBackend = this.createFilesystemBackendFactory(middlewareConfigs);
@@ -4937,12 +5354,12 @@ var AgentManager = class _AgentManager {
4937
5354
  return _AgentManager.instance;
4938
5355
  }
4939
5356
  callAgentInQueue(queue, return_agent_state) {
4940
- return new Promise((resolve2, reject) => {
5357
+ return new Promise((resolve3, reject) => {
4941
5358
  const callback_event = `${queue.assistant_id}::${queue.thread_id}`;
4942
5359
  if (return_agent_state) {
4943
5360
  event_bus_default.subscribeOnce(callback_event, (data) => {
4944
5361
  if (data.success) {
4945
- resolve2(data.state);
5362
+ resolve3(data.state);
4946
5363
  } else {
4947
5364
  reject(data.error);
4948
5365
  }
@@ -4957,7 +5374,7 @@ var AgentManager = class _AgentManager {
4957
5374
  },
4958
5375
  true
4959
5376
  );
4960
- !return_agent_state && resolve2({ callback_event_id: callback_event, success: true });
5377
+ !return_agent_state && resolve3({ callback_event_id: callback_event, success: true });
4961
5378
  } catch (error) {
4962
5379
  !return_agent_state && reject({
4963
5380
  callback_event_id: callback_event,
@@ -5451,59 +5868,1144 @@ function createPatchToolCallsMiddleware() {
5451
5868
  });
5452
5869
  }
5453
5870
 
5871
+ // src/deep_agent_new/backends/store.ts
5872
+ var StoreBackend = class {
5873
+ constructor(stateAndStore) {
5874
+ this.stateAndStore = stateAndStore;
5875
+ }
5876
+ /**
5877
+ * Get the store instance.
5878
+ *
5879
+ * @returns BaseStore instance
5880
+ * @throws Error if no store is available
5881
+ */
5882
+ getStore() {
5883
+ const store = this.stateAndStore.store;
5884
+ if (!store) {
5885
+ throw new Error("Store is required but not available in StateAndStore");
5886
+ }
5887
+ return store;
5888
+ }
5889
+ /**
5890
+ * Get the namespace for store operations.
5891
+ *
5892
+ * If an assistant_id is available in stateAndStore, return
5893
+ * [assistant_id, "filesystem"] to provide per-assistant isolation.
5894
+ * Otherwise return ["filesystem"].
5895
+ */
5896
+ getNamespace() {
5897
+ const namespace = "filesystem";
5898
+ const assistantId = this.stateAndStore.assistantId;
5899
+ if (assistantId) {
5900
+ return [assistantId, namespace];
5901
+ }
5902
+ return [namespace];
5903
+ }
5904
+ /**
5905
+ * Convert a store Item to FileData format.
5906
+ *
5907
+ * @param storeItem - The store Item containing file data
5908
+ * @returns FileData object
5909
+ * @throws Error if required fields are missing or have incorrect types
5910
+ */
5911
+ convertStoreItemToFileData(storeItem) {
5912
+ const value = storeItem.value;
5913
+ if (!value.content || !Array.isArray(value.content) || typeof value.created_at !== "string" || typeof value.modified_at !== "string") {
5914
+ throw new Error(
5915
+ `Store item does not contain valid FileData fields. Got keys: ${Object.keys(value).join(", ")}`
5916
+ );
5917
+ }
5918
+ return {
5919
+ content: value.content,
5920
+ created_at: value.created_at,
5921
+ modified_at: value.modified_at
5922
+ };
5923
+ }
5924
+ /**
5925
+ * Convert FileData to a value suitable for store.put().
5926
+ *
5927
+ * @param fileData - The FileData to convert
5928
+ * @returns Object with content, created_at, and modified_at fields
5929
+ */
5930
+ convertFileDataToStoreValue(fileData) {
5931
+ return {
5932
+ content: fileData.content,
5933
+ created_at: fileData.created_at,
5934
+ modified_at: fileData.modified_at
5935
+ };
5936
+ }
5937
+ /**
5938
+ * Search store with automatic pagination to retrieve all results.
5939
+ *
5940
+ * @param store - The store to search
5941
+ * @param namespace - Hierarchical path prefix to search within
5942
+ * @param options - Optional query, filter, and page_size
5943
+ * @returns List of all items matching the search criteria
5944
+ */
5945
+ async searchStorePaginated(store, namespace, options = {}) {
5946
+ const { query, filter, pageSize = 100 } = options;
5947
+ const allItems = [];
5948
+ let offset = 0;
5949
+ while (true) {
5950
+ const pageItems = await store.search(namespace, {
5951
+ query,
5952
+ filter,
5953
+ limit: pageSize,
5954
+ offset
5955
+ });
5956
+ if (!pageItems || pageItems.length === 0) {
5957
+ break;
5958
+ }
5959
+ allItems.push(...pageItems);
5960
+ if (pageItems.length < pageSize) {
5961
+ break;
5962
+ }
5963
+ offset += pageSize;
5964
+ }
5965
+ return allItems;
5966
+ }
5967
+ /**
5968
+ * List files and directories in the specified directory (non-recursive).
5969
+ *
5970
+ * @param path - Absolute path to directory
5971
+ * @returns List of FileInfo objects for files and directories directly in the directory.
5972
+ * Directories have a trailing / in their path and is_dir=true.
5973
+ */
5974
+ async lsInfo(path5) {
5975
+ const store = this.getStore();
5976
+ const namespace = this.getNamespace();
5977
+ const items = await this.searchStorePaginated(store, namespace);
5978
+ const infos = [];
5979
+ const subdirs = /* @__PURE__ */ new Set();
5980
+ const normalizedPath = path5.endsWith("/") ? path5 : path5 + "/";
5981
+ for (const item of items) {
5982
+ const itemKey = String(item.key);
5983
+ if (!itemKey.startsWith(normalizedPath)) {
5984
+ continue;
5985
+ }
5986
+ const relative3 = itemKey.substring(normalizedPath.length);
5987
+ if (relative3.includes("/")) {
5988
+ const subdirName = relative3.split("/")[0];
5989
+ subdirs.add(normalizedPath + subdirName + "/");
5990
+ continue;
5991
+ }
5992
+ try {
5993
+ const fd = this.convertStoreItemToFileData(item);
5994
+ const size = fd.content.join("\n").length;
5995
+ infos.push({
5996
+ path: itemKey,
5997
+ is_dir: false,
5998
+ size,
5999
+ modified_at: fd.modified_at
6000
+ });
6001
+ } catch {
6002
+ continue;
6003
+ }
6004
+ }
6005
+ for (const subdir of Array.from(subdirs).sort()) {
6006
+ infos.push({
6007
+ path: subdir,
6008
+ is_dir: true,
6009
+ size: 0,
6010
+ modified_at: ""
6011
+ });
6012
+ }
6013
+ infos.sort((a, b) => a.path.localeCompare(b.path));
6014
+ return infos;
6015
+ }
6016
+ /**
6017
+ * Read file content with line numbers.
6018
+ *
6019
+ * @param filePath - Absolute file path
6020
+ * @param offset - Line offset to start reading from (0-indexed)
6021
+ * @param limit - Maximum number of lines to read
6022
+ * @returns Formatted file content with line numbers, or error message
6023
+ */
6024
+ async read(filePath, offset = 0, limit = 2e3) {
6025
+ try {
6026
+ const fileData = await this.readRaw(filePath);
6027
+ return formatReadResponse(fileData, offset, limit);
6028
+ } catch (e) {
6029
+ return `Error: ${e.message}`;
6030
+ }
6031
+ }
6032
+ /**
6033
+ * Read file content as raw FileData.
6034
+ *
6035
+ * @param filePath - Absolute file path
6036
+ * @returns Raw file content as FileData
6037
+ */
6038
+ async readRaw(filePath) {
6039
+ const store = this.getStore();
6040
+ const namespace = this.getNamespace();
6041
+ const item = await store.get(namespace, filePath);
6042
+ if (!item) throw new Error(`File '${filePath}' not found`);
6043
+ return this.convertStoreItemToFileData(item);
6044
+ }
6045
+ /**
6046
+ * Create a new file with content.
6047
+ * Returns WriteResult. External storage sets filesUpdate=null.
6048
+ */
6049
+ async write(filePath, content) {
6050
+ const store = this.getStore();
6051
+ const namespace = this.getNamespace();
6052
+ const existing = await store.get(namespace, filePath);
6053
+ if (existing) {
6054
+ return {
6055
+ error: `Cannot write to ${filePath} because it already exists. Read and then make an edit, or write to a new path.`
6056
+ };
6057
+ }
6058
+ const fileData = createFileData(content);
6059
+ const storeValue = this.convertFileDataToStoreValue(fileData);
6060
+ await store.put(namespace, filePath, storeValue);
6061
+ return { path: filePath, filesUpdate: null };
6062
+ }
6063
+ /**
6064
+ * Edit a file by replacing string occurrences.
6065
+ * Returns EditResult. External storage sets filesUpdate=null.
6066
+ */
6067
+ async edit(filePath, oldString, newString, replaceAll = false) {
6068
+ const store = this.getStore();
6069
+ const namespace = this.getNamespace();
6070
+ const item = await store.get(namespace, filePath);
6071
+ if (!item) {
6072
+ return { error: `Error: File '${filePath}' not found` };
6073
+ }
6074
+ try {
6075
+ const fileData = this.convertStoreItemToFileData(item);
6076
+ const content = fileDataToString(fileData);
6077
+ const result = performStringReplacement(
6078
+ content,
6079
+ oldString,
6080
+ newString,
6081
+ replaceAll
6082
+ );
6083
+ if (typeof result === "string") {
6084
+ return { error: result };
6085
+ }
6086
+ const [newContent, occurrences] = result;
6087
+ const newFileData = updateFileData(fileData, newContent);
6088
+ const storeValue = this.convertFileDataToStoreValue(newFileData);
6089
+ await store.put(namespace, filePath, storeValue);
6090
+ return { path: filePath, filesUpdate: null, occurrences };
6091
+ } catch (e) {
6092
+ return { error: `Error: ${e.message}` };
6093
+ }
6094
+ }
6095
+ /**
6096
+ * Structured search results or error string for invalid input.
6097
+ */
6098
+ async grepRaw(pattern, path5 = "/", glob = null) {
6099
+ const store = this.getStore();
6100
+ const namespace = this.getNamespace();
6101
+ const items = await this.searchStorePaginated(store, namespace);
6102
+ const files = {};
6103
+ for (const item of items) {
6104
+ try {
6105
+ files[item.key] = this.convertStoreItemToFileData(item);
6106
+ } catch {
6107
+ continue;
6108
+ }
6109
+ }
6110
+ return grepMatchesFromFiles(files, pattern, path5, glob);
6111
+ }
6112
+ /**
6113
+ * Structured glob matching returning FileInfo objects.
6114
+ */
6115
+ async globInfo(pattern, path5 = "/") {
6116
+ const store = this.getStore();
6117
+ const namespace = this.getNamespace();
6118
+ const items = await this.searchStorePaginated(store, namespace);
6119
+ const files = {};
6120
+ for (const item of items) {
6121
+ try {
6122
+ files[item.key] = this.convertStoreItemToFileData(item);
6123
+ } catch {
6124
+ continue;
6125
+ }
6126
+ }
6127
+ const result = globSearchFiles(files, pattern, path5);
6128
+ if (result === "No files found") {
6129
+ return [];
6130
+ }
6131
+ const paths = result.split("\n");
6132
+ const infos = [];
6133
+ for (const p of paths) {
6134
+ const fd = files[p];
6135
+ const size = fd ? fd.content.join("\n").length : 0;
6136
+ infos.push({
6137
+ path: p,
6138
+ is_dir: false,
6139
+ size,
6140
+ modified_at: fd?.modified_at || ""
6141
+ });
6142
+ }
6143
+ return infos;
6144
+ }
6145
+ };
6146
+
5454
6147
  // src/deep_agent_new/backends/filesystem.ts
6148
+ import * as fs2 from "fs/promises";
5455
6149
  import * as fsSync from "fs";
6150
+ import * as path4 from "path";
6151
+ import { spawn } from "child_process";
5456
6152
  import fg from "fast-glob";
5457
6153
  import micromatch2 from "micromatch";
5458
6154
  var SUPPORTS_NOFOLLOW = fsSync.constants.O_NOFOLLOW !== void 0;
5459
-
5460
- // src/deep_agent_new/middleware/todos.ts
5461
- import { Command as Command3 } from "@langchain/langgraph";
5462
- import { z as z36 } from "zod";
5463
- import { createMiddleware as createMiddleware8, tool as tool35, ToolMessage as ToolMessage4 } from "langchain";
5464
- 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.
5465
- It also helps the user understand the progress of the task and overall progress of their requests.
5466
- 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.
5467
-
5468
- ## When to Use This Tool
5469
- Use this tool in these scenarios:
5470
-
5471
- 1. Complex multi-step tasks - When a task requires 3 or more distinct steps or actions
5472
- 2. Non-trivial and complex tasks - Tasks that require careful planning or multiple operations
5473
- 3. User explicitly requests todo list - When the user directly asks you to use the todo list
5474
- 4. User provides multiple tasks - When users provide a list of things to be done (numbered or comma-separated)
5475
- 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.
5476
-
5477
- ## How to Use This Tool
5478
- 1. When you start working on a task - Mark it as in_progress BEFORE beginning work.
5479
- 2. After completing a task - Mark it as completed and add any new follow-up tasks discovered during implementation.
5480
- 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.
5481
- 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.
5482
-
5483
- ## When NOT to Use This Tool
5484
- It is important to skip using this tool when:
5485
- 1. There is only a single, straightforward task
5486
- 2. The task is trivial and tracking it provides no benefit
5487
- 3. The task can be completed in less than 3 trivial steps
5488
- 4. The task is purely conversational or informational
5489
-
5490
- ## Examples of When to Use the Todo List
5491
-
5492
- <example>
5493
- 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!
5494
- Assistant: I'll help add a dark mode toggle to your application settings. Let me create a todo list to track this implementation.
5495
- *Creates todo list with the following items:*
5496
- 1. Create dark mode toggle component in Settings page
5497
- 2. Add dark mode state management (context/store)
5498
- 3. Implement CSS-in-JS styles for dark theme
5499
- 4. Update existing components to support theme switching
5500
- 5. Run tests and build process, addressing any failures or errors that occur
5501
- *Begins working on the first task*
5502
-
5503
- <reasoning>
5504
- The assistant used the todo list because:
5505
- 1. Adding dark mode in it of itself is a multi-step feature requiring UI, state management, and styling changes
5506
- 2. The assistant inferred that tests and build need to pass by adding "Ensure tests and build succeed" as the final task
6155
+ var FilesystemBackend = class {
6156
+ constructor(options = {}) {
6157
+ const { rootDir, virtualMode = false, maxFileSizeMb = 10 } = options;
6158
+ this.cwd = rootDir ? path4.resolve(rootDir) : process.cwd();
6159
+ this.virtualMode = virtualMode;
6160
+ this.maxFileSizeBytes = maxFileSizeMb * 1024 * 1024;
6161
+ }
6162
+ /**
6163
+ * Resolve a file path with security checks.
6164
+ *
6165
+ * When virtualMode=true, treat incoming paths as virtual absolute paths under
6166
+ * this.cwd, disallow traversal (.., ~) and ensure resolved path stays within root.
6167
+ * When virtualMode=false, preserve legacy behavior: absolute paths are allowed
6168
+ * as-is; relative paths resolve under cwd.
6169
+ *
6170
+ * @param key - File path (absolute, relative, or virtual when virtualMode=true)
6171
+ * @returns Resolved absolute path string
6172
+ * @throws Error if path traversal detected or path outside root
6173
+ */
6174
+ resolvePath(key) {
6175
+ if (this.virtualMode) {
6176
+ const vpath = key.startsWith("/") ? key : "/" + key;
6177
+ if (vpath.includes("..") || vpath.startsWith("~")) {
6178
+ throw new Error("Path traversal not allowed");
6179
+ }
6180
+ const full = path4.resolve(this.cwd, vpath.substring(1));
6181
+ const relative3 = path4.relative(this.cwd, full);
6182
+ if (relative3.startsWith("..") || path4.isAbsolute(relative3)) {
6183
+ throw new Error(`Path: ${full} outside root directory: ${this.cwd}`);
6184
+ }
6185
+ return full;
6186
+ }
6187
+ if (path4.isAbsolute(key)) {
6188
+ return key;
6189
+ }
6190
+ return path4.resolve(this.cwd, key);
6191
+ }
6192
+ /**
6193
+ * List files and directories in the specified directory (non-recursive).
6194
+ *
6195
+ * @param dirPath - Absolute directory path to list files from
6196
+ * @returns List of FileInfo objects for files and directories directly in the directory.
6197
+ * Directories have a trailing / in their path and is_dir=true.
6198
+ */
6199
+ async lsInfo(dirPath) {
6200
+ try {
6201
+ const resolvedPath = this.resolvePath(dirPath);
6202
+ const stat3 = await fs2.stat(resolvedPath);
6203
+ if (!stat3.isDirectory()) {
6204
+ return [];
6205
+ }
6206
+ const entries = await fs2.readdir(resolvedPath, { withFileTypes: true });
6207
+ const results = [];
6208
+ const cwdStr = this.cwd.endsWith(path4.sep) ? this.cwd : this.cwd + path4.sep;
6209
+ for (const entry of entries) {
6210
+ const fullPath = path4.join(resolvedPath, entry.name);
6211
+ try {
6212
+ const entryStat = await fs2.stat(fullPath);
6213
+ const isFile = entryStat.isFile();
6214
+ const isDir = entryStat.isDirectory();
6215
+ if (!this.virtualMode) {
6216
+ if (isFile) {
6217
+ results.push({
6218
+ path: fullPath,
6219
+ is_dir: false,
6220
+ size: entryStat.size,
6221
+ modified_at: entryStat.mtime.toISOString()
6222
+ });
6223
+ } else if (isDir) {
6224
+ results.push({
6225
+ path: fullPath + path4.sep,
6226
+ is_dir: true,
6227
+ size: 0,
6228
+ modified_at: entryStat.mtime.toISOString()
6229
+ });
6230
+ }
6231
+ } else {
6232
+ let relativePath;
6233
+ if (fullPath.startsWith(cwdStr)) {
6234
+ relativePath = fullPath.substring(cwdStr.length);
6235
+ } else if (fullPath.startsWith(this.cwd)) {
6236
+ relativePath = fullPath.substring(this.cwd.length).replace(/^[/\\]/, "");
6237
+ } else {
6238
+ relativePath = fullPath;
6239
+ }
6240
+ relativePath = relativePath.split(path4.sep).join("/");
6241
+ const virtPath = "/" + relativePath;
6242
+ if (isFile) {
6243
+ results.push({
6244
+ path: virtPath,
6245
+ is_dir: false,
6246
+ size: entryStat.size,
6247
+ modified_at: entryStat.mtime.toISOString()
6248
+ });
6249
+ } else if (isDir) {
6250
+ results.push({
6251
+ path: virtPath + "/",
6252
+ is_dir: true,
6253
+ size: 0,
6254
+ modified_at: entryStat.mtime.toISOString()
6255
+ });
6256
+ }
6257
+ }
6258
+ } catch {
6259
+ continue;
6260
+ }
6261
+ }
6262
+ results.sort((a, b) => a.path.localeCompare(b.path));
6263
+ return results;
6264
+ } catch (e) {
6265
+ console.error(`Error listing files in ${dirPath}:`, e);
6266
+ return [];
6267
+ }
6268
+ }
6269
+ /**
6270
+ * Read file content with line numbers.
6271
+ *
6272
+ * @param filePath - Absolute or relative file path
6273
+ * @param offset - Line offset to start reading from (0-indexed)
6274
+ * @param limit - Maximum number of lines to read
6275
+ * @returns Formatted file content with line numbers, or error message
6276
+ */
6277
+ async read(filePath, offset = 0, limit = 2e3) {
6278
+ try {
6279
+ const resolvedPath = this.resolvePath(filePath);
6280
+ let content;
6281
+ if (SUPPORTS_NOFOLLOW) {
6282
+ const stat3 = await fs2.stat(resolvedPath);
6283
+ if (!stat3.isFile()) {
6284
+ return `Error: File '${filePath}' not found`;
6285
+ }
6286
+ const fd = await fs2.open(
6287
+ resolvedPath,
6288
+ fsSync.constants.O_RDONLY | fsSync.constants.O_NOFOLLOW
6289
+ );
6290
+ try {
6291
+ content = await fd.readFile({ encoding: "utf-8" });
6292
+ } finally {
6293
+ await fd.close();
6294
+ }
6295
+ } else {
6296
+ const stat3 = await fs2.lstat(resolvedPath);
6297
+ if (stat3.isSymbolicLink()) {
6298
+ return `Error: Symlinks are not allowed: ${filePath}`;
6299
+ }
6300
+ if (!stat3.isFile()) {
6301
+ return `Error: File '${filePath}' not found`;
6302
+ }
6303
+ content = await fs2.readFile(resolvedPath, "utf-8");
6304
+ }
6305
+ const emptyMsg = checkEmptyContent(content);
6306
+ if (emptyMsg) {
6307
+ return emptyMsg;
6308
+ }
6309
+ const lines = content.split("\n");
6310
+ const startIdx = offset;
6311
+ const endIdx = Math.min(startIdx + limit, lines.length);
6312
+ if (startIdx >= lines.length) {
6313
+ return `Error: Line offset ${offset} exceeds file length (${lines.length} lines)`;
6314
+ }
6315
+ const selectedLines = lines.slice(startIdx, endIdx);
6316
+ return formatContentWithLineNumbers(selectedLines, startIdx + 1);
6317
+ } catch (e) {
6318
+ return `Error reading file '${filePath}': ${e.message}`;
6319
+ }
6320
+ }
6321
+ /**
6322
+ * Read file content as raw FileData.
6323
+ *
6324
+ * @param filePath - Absolute file path
6325
+ * @returns Raw file content as FileData
6326
+ */
6327
+ async readRaw(filePath) {
6328
+ const resolvedPath = this.resolvePath(filePath);
6329
+ let content;
6330
+ let stat3;
6331
+ if (SUPPORTS_NOFOLLOW) {
6332
+ stat3 = await fs2.stat(resolvedPath);
6333
+ if (!stat3.isFile()) throw new Error(`File '${filePath}' not found`);
6334
+ const fd = await fs2.open(
6335
+ resolvedPath,
6336
+ fsSync.constants.O_RDONLY | fsSync.constants.O_NOFOLLOW
6337
+ );
6338
+ try {
6339
+ content = await fd.readFile({ encoding: "utf-8" });
6340
+ } finally {
6341
+ await fd.close();
6342
+ }
6343
+ } else {
6344
+ stat3 = await fs2.lstat(resolvedPath);
6345
+ if (stat3.isSymbolicLink()) {
6346
+ throw new Error(`Symlinks are not allowed: ${filePath}`);
6347
+ }
6348
+ if (!stat3.isFile()) throw new Error(`File '${filePath}' not found`);
6349
+ content = await fs2.readFile(resolvedPath, "utf-8");
6350
+ }
6351
+ return {
6352
+ content: content.split("\n"),
6353
+ created_at: stat3.ctime.toISOString(),
6354
+ modified_at: stat3.mtime.toISOString()
6355
+ };
6356
+ }
6357
+ /**
6358
+ * Create a new file with content.
6359
+ * Returns WriteResult. External storage sets filesUpdate=null.
6360
+ */
6361
+ async write(filePath, content) {
6362
+ try {
6363
+ const resolvedPath = this.resolvePath(filePath);
6364
+ try {
6365
+ const stat3 = await fs2.lstat(resolvedPath);
6366
+ if (stat3.isSymbolicLink()) {
6367
+ return {
6368
+ error: `Cannot write to ${filePath} because it is a symlink. Symlinks are not allowed.`
6369
+ };
6370
+ }
6371
+ return {
6372
+ error: `Cannot write to ${filePath} because it already exists. Read and then make an edit, or write to a new path.`
6373
+ };
6374
+ } catch {
6375
+ }
6376
+ await fs2.mkdir(path4.dirname(resolvedPath), { recursive: true });
6377
+ if (SUPPORTS_NOFOLLOW) {
6378
+ const flags = fsSync.constants.O_WRONLY | fsSync.constants.O_CREAT | fsSync.constants.O_TRUNC | fsSync.constants.O_NOFOLLOW;
6379
+ const fd = await fs2.open(resolvedPath, flags, 420);
6380
+ try {
6381
+ await fd.writeFile(content, "utf-8");
6382
+ } finally {
6383
+ await fd.close();
6384
+ }
6385
+ } else {
6386
+ await fs2.writeFile(resolvedPath, content, "utf-8");
6387
+ }
6388
+ return { path: filePath, filesUpdate: null };
6389
+ } catch (e) {
6390
+ return { error: `Error writing file '${filePath}': ${e.message}` };
6391
+ }
6392
+ }
6393
+ /**
6394
+ * Edit a file by replacing string occurrences.
6395
+ * Returns EditResult. External storage sets filesUpdate=null.
6396
+ */
6397
+ async edit(filePath, oldString, newString, replaceAll = false) {
6398
+ try {
6399
+ const resolvedPath = this.resolvePath(filePath);
6400
+ let content;
6401
+ if (SUPPORTS_NOFOLLOW) {
6402
+ const stat3 = await fs2.stat(resolvedPath);
6403
+ if (!stat3.isFile()) {
6404
+ return { error: `Error: File '${filePath}' not found` };
6405
+ }
6406
+ const fd = await fs2.open(
6407
+ resolvedPath,
6408
+ fsSync.constants.O_RDONLY | fsSync.constants.O_NOFOLLOW
6409
+ );
6410
+ try {
6411
+ content = await fd.readFile({ encoding: "utf-8" });
6412
+ } finally {
6413
+ await fd.close();
6414
+ }
6415
+ } else {
6416
+ const stat3 = await fs2.lstat(resolvedPath);
6417
+ if (stat3.isSymbolicLink()) {
6418
+ return { error: `Error: Symlinks are not allowed: ${filePath}` };
6419
+ }
6420
+ if (!stat3.isFile()) {
6421
+ return { error: `Error: File '${filePath}' not found` };
6422
+ }
6423
+ content = await fs2.readFile(resolvedPath, "utf-8");
6424
+ }
6425
+ const result = performStringReplacement(
6426
+ content,
6427
+ oldString,
6428
+ newString,
6429
+ replaceAll
6430
+ );
6431
+ if (typeof result === "string") {
6432
+ return { error: result };
6433
+ }
6434
+ const [newContent, occurrences] = result;
6435
+ if (SUPPORTS_NOFOLLOW) {
6436
+ const flags = fsSync.constants.O_WRONLY | fsSync.constants.O_TRUNC | fsSync.constants.O_NOFOLLOW;
6437
+ const fd = await fs2.open(resolvedPath, flags);
6438
+ try {
6439
+ await fd.writeFile(newContent, "utf-8");
6440
+ } finally {
6441
+ await fd.close();
6442
+ }
6443
+ } else {
6444
+ await fs2.writeFile(resolvedPath, newContent, "utf-8");
6445
+ }
6446
+ return { path: filePath, filesUpdate: null, occurrences };
6447
+ } catch (e) {
6448
+ return { error: `Error editing file '${filePath}': ${e.message}` };
6449
+ }
6450
+ }
6451
+ /**
6452
+ * Structured search results or error string for invalid input.
6453
+ */
6454
+ async grepRaw(pattern, dirPath = "/", glob = null) {
6455
+ try {
6456
+ new RegExp(pattern);
6457
+ } catch (e) {
6458
+ return `Invalid regex pattern: ${e.message}`;
6459
+ }
6460
+ let baseFull;
6461
+ try {
6462
+ baseFull = this.resolvePath(dirPath || ".");
6463
+ } catch {
6464
+ return [];
6465
+ }
6466
+ try {
6467
+ await fs2.stat(baseFull);
6468
+ } catch {
6469
+ return [];
6470
+ }
6471
+ let results = await this.ripgrepSearch(pattern, baseFull, glob);
6472
+ if (results === null) {
6473
+ results = await this.pythonSearch(pattern, baseFull, glob);
6474
+ }
6475
+ const matches = [];
6476
+ for (const [fpath, items] of Object.entries(results)) {
6477
+ for (const [lineNum, lineText] of items) {
6478
+ matches.push({ path: fpath, line: lineNum, text: lineText });
6479
+ }
6480
+ }
6481
+ return matches;
6482
+ }
6483
+ /**
6484
+ * Try to use ripgrep for fast searching.
6485
+ * Returns null if ripgrep is not available or fails.
6486
+ */
6487
+ async ripgrepSearch(pattern, baseFull, includeGlob) {
6488
+ return new Promise((resolve3) => {
6489
+ const args = ["--json"];
6490
+ if (includeGlob) {
6491
+ args.push("--glob", includeGlob);
6492
+ }
6493
+ args.push("--", pattern, baseFull);
6494
+ const proc = spawn("rg", args, { timeout: 3e4 });
6495
+ const results = {};
6496
+ let output = "";
6497
+ proc.stdout.on("data", (data) => {
6498
+ output += data.toString();
6499
+ });
6500
+ proc.on("close", (code) => {
6501
+ if (code !== 0 && code !== 1) {
6502
+ resolve3(null);
6503
+ return;
6504
+ }
6505
+ for (const line of output.split("\n")) {
6506
+ if (!line.trim()) continue;
6507
+ try {
6508
+ const data = JSON.parse(line);
6509
+ if (data.type !== "match") continue;
6510
+ const pdata = data.data || {};
6511
+ const ftext = pdata.path?.text;
6512
+ if (!ftext) continue;
6513
+ let virtPath;
6514
+ if (this.virtualMode) {
6515
+ try {
6516
+ const resolved = path4.resolve(ftext);
6517
+ const relative3 = path4.relative(this.cwd, resolved);
6518
+ if (relative3.startsWith("..")) continue;
6519
+ const normalizedRelative = relative3.split(path4.sep).join("/");
6520
+ virtPath = "/" + normalizedRelative;
6521
+ } catch {
6522
+ continue;
6523
+ }
6524
+ } else {
6525
+ virtPath = ftext;
6526
+ }
6527
+ const ln = pdata.line_number;
6528
+ const lt = pdata.lines?.text?.replace(/\n$/, "") || "";
6529
+ if (ln === void 0) continue;
6530
+ if (!results[virtPath]) {
6531
+ results[virtPath] = [];
6532
+ }
6533
+ results[virtPath].push([ln, lt]);
6534
+ } catch {
6535
+ continue;
6536
+ }
6537
+ }
6538
+ resolve3(results);
6539
+ });
6540
+ proc.on("error", () => {
6541
+ resolve3(null);
6542
+ });
6543
+ });
6544
+ }
6545
+ /**
6546
+ * Fallback regex search implementation.
6547
+ */
6548
+ async pythonSearch(pattern, baseFull, includeGlob) {
6549
+ let regex;
6550
+ try {
6551
+ regex = new RegExp(pattern);
6552
+ } catch {
6553
+ return {};
6554
+ }
6555
+ const results = {};
6556
+ const stat3 = await fs2.stat(baseFull);
6557
+ const root = stat3.isDirectory() ? baseFull : path4.dirname(baseFull);
6558
+ const files = await fg("**/*", {
6559
+ cwd: root,
6560
+ absolute: true,
6561
+ onlyFiles: true,
6562
+ dot: true
6563
+ });
6564
+ for (const fp of files) {
6565
+ try {
6566
+ if (includeGlob && !micromatch2.isMatch(path4.basename(fp), includeGlob)) {
6567
+ continue;
6568
+ }
6569
+ const stat4 = await fs2.stat(fp);
6570
+ if (stat4.size > this.maxFileSizeBytes) {
6571
+ continue;
6572
+ }
6573
+ const content = await fs2.readFile(fp, "utf-8");
6574
+ const lines = content.split("\n");
6575
+ for (let i = 0; i < lines.length; i++) {
6576
+ const line = lines[i];
6577
+ if (regex.test(line)) {
6578
+ let virtPath;
6579
+ if (this.virtualMode) {
6580
+ try {
6581
+ const relative3 = path4.relative(this.cwd, fp);
6582
+ if (relative3.startsWith("..")) continue;
6583
+ const normalizedRelative = relative3.split(path4.sep).join("/");
6584
+ virtPath = "/" + normalizedRelative;
6585
+ } catch {
6586
+ continue;
6587
+ }
6588
+ } else {
6589
+ virtPath = fp;
6590
+ }
6591
+ if (!results[virtPath]) {
6592
+ results[virtPath] = [];
6593
+ }
6594
+ results[virtPath].push([i + 1, line]);
6595
+ }
6596
+ }
6597
+ } catch {
6598
+ continue;
6599
+ }
6600
+ }
6601
+ return results;
6602
+ }
6603
+ /**
6604
+ * Structured glob matching returning FileInfo objects.
6605
+ */
6606
+ async globInfo(pattern, searchPath = "/") {
6607
+ if (pattern.startsWith("/")) {
6608
+ pattern = pattern.substring(1);
6609
+ }
6610
+ const resolvedSearchPath = searchPath === "/" ? this.cwd : this.resolvePath(searchPath);
6611
+ try {
6612
+ const stat3 = await fs2.stat(resolvedSearchPath);
6613
+ if (!stat3.isDirectory()) {
6614
+ return [];
6615
+ }
6616
+ } catch {
6617
+ return [];
6618
+ }
6619
+ const results = [];
6620
+ try {
6621
+ const matches = await fg(pattern, {
6622
+ cwd: resolvedSearchPath,
6623
+ absolute: true,
6624
+ onlyFiles: true,
6625
+ dot: true
6626
+ });
6627
+ for (const matchedPath of matches) {
6628
+ try {
6629
+ const stat3 = await fs2.stat(matchedPath);
6630
+ if (!stat3.isFile()) continue;
6631
+ const normalizedPath = matchedPath.split("/").join(path4.sep);
6632
+ if (!this.virtualMode) {
6633
+ results.push({
6634
+ path: normalizedPath,
6635
+ is_dir: false,
6636
+ size: stat3.size,
6637
+ modified_at: stat3.mtime.toISOString()
6638
+ });
6639
+ } else {
6640
+ const cwdStr = this.cwd.endsWith(path4.sep) ? this.cwd : this.cwd + path4.sep;
6641
+ let relativePath;
6642
+ if (normalizedPath.startsWith(cwdStr)) {
6643
+ relativePath = normalizedPath.substring(cwdStr.length);
6644
+ } else if (normalizedPath.startsWith(this.cwd)) {
6645
+ relativePath = normalizedPath.substring(this.cwd.length).replace(/^[/\\]/, "");
6646
+ } else {
6647
+ relativePath = normalizedPath;
6648
+ }
6649
+ relativePath = relativePath.split(path4.sep).join("/");
6650
+ const virt = "/" + relativePath;
6651
+ results.push({
6652
+ path: virt,
6653
+ is_dir: false,
6654
+ size: stat3.size,
6655
+ modified_at: stat3.mtime.toISOString()
6656
+ });
6657
+ }
6658
+ } catch {
6659
+ continue;
6660
+ }
6661
+ }
6662
+ } catch {
6663
+ }
6664
+ results.sort((a, b) => a.path.localeCompare(b.path));
6665
+ return results;
6666
+ }
6667
+ };
6668
+
6669
+ // src/deep_agent_new/backends/composite.ts
6670
+ var CompositeBackend = class {
6671
+ constructor(defaultBackend, routes) {
6672
+ this.default = defaultBackend;
6673
+ this.routes = routes;
6674
+ this.sortedRoutes = Object.entries(routes).sort(
6675
+ (a, b) => b[0].length - a[0].length
6676
+ );
6677
+ }
6678
+ /**
6679
+ * Determine which backend handles this key and strip prefix.
6680
+ *
6681
+ * @param key - Original file path
6682
+ * @returns Tuple of [backend, stripped_key] where stripped_key has the route
6683
+ * prefix removed (but keeps leading slash).
6684
+ */
6685
+ getBackendAndKey(key) {
6686
+ for (const [prefix, backend] of this.sortedRoutes) {
6687
+ if (key.startsWith(prefix)) {
6688
+ const suffix = key.substring(prefix.length);
6689
+ const strippedKey = suffix ? "/" + suffix : "/";
6690
+ return [backend, strippedKey];
6691
+ }
6692
+ }
6693
+ return [this.default, key];
6694
+ }
6695
+ /**
6696
+ * List files and directories in the specified directory (non-recursive).
6697
+ *
6698
+ * @param path - Absolute path to directory
6699
+ * @returns List of FileInfo objects with route prefixes added, for files and directories
6700
+ * directly in the directory. Directories have a trailing / in their path and is_dir=true.
6701
+ */
6702
+ async lsInfo(path5) {
6703
+ for (const [routePrefix, backend] of this.sortedRoutes) {
6704
+ if (path5.startsWith(routePrefix.replace(/\/$/, ""))) {
6705
+ const suffix = path5.substring(routePrefix.length);
6706
+ const searchPath = suffix ? "/" + suffix : "/";
6707
+ const infos = await backend.lsInfo(searchPath);
6708
+ const prefixed = [];
6709
+ for (const fi of infos) {
6710
+ prefixed.push({
6711
+ ...fi,
6712
+ path: routePrefix.slice(0, -1) + fi.path
6713
+ });
6714
+ }
6715
+ return prefixed;
6716
+ }
6717
+ }
6718
+ if (path5 === "/") {
6719
+ const results = [];
6720
+ const defaultInfos = await this.default.lsInfo(path5);
6721
+ results.push(...defaultInfos);
6722
+ for (const [routePrefix] of this.sortedRoutes) {
6723
+ results.push({
6724
+ path: routePrefix,
6725
+ is_dir: true,
6726
+ size: 0,
6727
+ modified_at: ""
6728
+ });
6729
+ }
6730
+ results.sort((a, b) => a.path.localeCompare(b.path));
6731
+ return results;
6732
+ }
6733
+ return await this.default.lsInfo(path5);
6734
+ }
6735
+ /**
6736
+ * Read file content, routing to appropriate backend.
6737
+ *
6738
+ * @param filePath - Absolute file path
6739
+ * @param offset - Line offset to start reading from (0-indexed)
6740
+ * @param limit - Maximum number of lines to read
6741
+ * @returns Formatted file content with line numbers, or error message
6742
+ */
6743
+ async read(filePath, offset = 0, limit = 2e3) {
6744
+ const [backend, strippedKey] = this.getBackendAndKey(filePath);
6745
+ return await backend.read(strippedKey, offset, limit);
6746
+ }
6747
+ /**
6748
+ * Read file content as raw FileData.
6749
+ *
6750
+ * @param filePath - Absolute file path
6751
+ * @returns Raw file content as FileData
6752
+ */
6753
+ async readRaw(filePath) {
6754
+ const [backend, strippedKey] = this.getBackendAndKey(filePath);
6755
+ return await backend.readRaw(strippedKey);
6756
+ }
6757
+ /**
6758
+ * Structured search results or error string for invalid input.
6759
+ */
6760
+ async grepRaw(pattern, path5 = "/", glob = null) {
6761
+ for (const [routePrefix, backend] of this.sortedRoutes) {
6762
+ if (path5.startsWith(routePrefix.replace(/\/$/, ""))) {
6763
+ const searchPath = path5.substring(routePrefix.length - 1);
6764
+ const raw = await backend.grepRaw(pattern, searchPath || "/", glob);
6765
+ if (typeof raw === "string") {
6766
+ return raw;
6767
+ }
6768
+ return raw.map((m) => ({
6769
+ ...m,
6770
+ path: routePrefix.slice(0, -1) + m.path
6771
+ }));
6772
+ }
6773
+ }
6774
+ const allMatches = [];
6775
+ const rawDefault = await this.default.grepRaw(pattern, path5, glob);
6776
+ if (typeof rawDefault === "string") {
6777
+ return rawDefault;
6778
+ }
6779
+ allMatches.push(...rawDefault);
6780
+ for (const [routePrefix, backend] of Object.entries(this.routes)) {
6781
+ const raw = await backend.grepRaw(pattern, "/", glob);
6782
+ if (typeof raw === "string") {
6783
+ return raw;
6784
+ }
6785
+ allMatches.push(
6786
+ ...raw.map((m) => ({
6787
+ ...m,
6788
+ path: routePrefix.slice(0, -1) + m.path
6789
+ }))
6790
+ );
6791
+ }
6792
+ return allMatches;
6793
+ }
6794
+ /**
6795
+ * Structured glob matching returning FileInfo objects.
6796
+ */
6797
+ async globInfo(pattern, path5 = "/") {
6798
+ const results = [];
6799
+ for (const [routePrefix, backend] of this.sortedRoutes) {
6800
+ if (path5.startsWith(routePrefix.replace(/\/$/, ""))) {
6801
+ const searchPath = path5.substring(routePrefix.length - 1);
6802
+ const infos = await backend.globInfo(pattern, searchPath || "/");
6803
+ return infos.map((fi) => ({
6804
+ ...fi,
6805
+ path: routePrefix.slice(0, -1) + fi.path
6806
+ }));
6807
+ }
6808
+ }
6809
+ const defaultInfos = await this.default.globInfo(pattern, path5);
6810
+ results.push(...defaultInfos);
6811
+ for (const [routePrefix, backend] of Object.entries(this.routes)) {
6812
+ const infos = await backend.globInfo(pattern, "/");
6813
+ results.push(
6814
+ ...infos.map((fi) => ({
6815
+ ...fi,
6816
+ path: routePrefix.slice(0, -1) + fi.path
6817
+ }))
6818
+ );
6819
+ }
6820
+ results.sort((a, b) => a.path.localeCompare(b.path));
6821
+ return results;
6822
+ }
6823
+ /**
6824
+ * Create a new file, routing to appropriate backend.
6825
+ *
6826
+ * @param filePath - Absolute file path
6827
+ * @param content - File content as string
6828
+ * @returns WriteResult with path or error
6829
+ */
6830
+ async write(filePath, content) {
6831
+ const [backend, strippedKey] = this.getBackendAndKey(filePath);
6832
+ return await backend.write(strippedKey, content);
6833
+ }
6834
+ /**
6835
+ * Edit a file, routing to appropriate backend.
6836
+ *
6837
+ * @param filePath - Absolute file path
6838
+ * @param oldString - String to find and replace
6839
+ * @param newString - Replacement string
6840
+ * @param replaceAll - If true, replace all occurrences
6841
+ * @returns EditResult with path, occurrences, or error
6842
+ */
6843
+ async edit(filePath, oldString, newString, replaceAll = false) {
6844
+ const [backend, strippedKey] = this.getBackendAndKey(filePath);
6845
+ return await backend.edit(strippedKey, oldString, newString, replaceAll);
6846
+ }
6847
+ };
6848
+
6849
+ // src/deep_agent_new/backends/memory.ts
6850
+ var MemoryBackend = class {
6851
+ constructor(files) {
6852
+ this.files = files ?? /* @__PURE__ */ new Map();
6853
+ }
6854
+ getFiles() {
6855
+ return Object.fromEntries(this.files);
6856
+ }
6857
+ lsInfo(path5) {
6858
+ const files = this.getFiles();
6859
+ const infos = [];
6860
+ const subdirs = /* @__PURE__ */ new Set();
6861
+ const normalizedPath = path5.endsWith("/") ? path5 : path5 + "/";
6862
+ for (const [k, fd] of Object.entries(files)) {
6863
+ if (!k.startsWith(normalizedPath)) {
6864
+ continue;
6865
+ }
6866
+ const relative3 = k.substring(normalizedPath.length);
6867
+ if (relative3.includes("/")) {
6868
+ const subdirName = relative3.split("/")[0];
6869
+ subdirs.add(normalizedPath + subdirName + "/");
6870
+ continue;
6871
+ }
6872
+ const size = fd.content.join("\n").length;
6873
+ infos.push({
6874
+ path: k,
6875
+ is_dir: false,
6876
+ size,
6877
+ modified_at: fd.modified_at
6878
+ });
6879
+ }
6880
+ for (const subdir of Array.from(subdirs).sort()) {
6881
+ infos.push({
6882
+ path: subdir,
6883
+ is_dir: true,
6884
+ size: 0,
6885
+ modified_at: ""
6886
+ });
6887
+ }
6888
+ infos.sort((a, b) => a.path.localeCompare(b.path));
6889
+ return infos;
6890
+ }
6891
+ read(filePath, offset = 0, limit = 2e3) {
6892
+ const files = this.getFiles();
6893
+ const fileData = files[filePath];
6894
+ if (!fileData) {
6895
+ return `Error: File '${filePath}' not found`;
6896
+ }
6897
+ return formatReadResponse(fileData, offset, limit);
6898
+ }
6899
+ readRaw(filePath) {
6900
+ const fileData = this.files.get(filePath);
6901
+ if (!fileData) {
6902
+ throw new Error(`File '${filePath}' not found`);
6903
+ }
6904
+ return fileData;
6905
+ }
6906
+ write(filePath, content) {
6907
+ if (this.files.has(filePath)) {
6908
+ return {
6909
+ error: `Cannot write to ${filePath} because it already exists. Read and then make an edit, or write to a new path.`
6910
+ };
6911
+ }
6912
+ const newFileData = createFileData(content);
6913
+ this.files.set(filePath, newFileData);
6914
+ return { path: filePath, filesUpdate: null };
6915
+ }
6916
+ edit(filePath, oldString, newString, replaceAll = false) {
6917
+ const fileData = this.files.get(filePath);
6918
+ if (!fileData) {
6919
+ return { error: `Error: File '${filePath}' not found` };
6920
+ }
6921
+ const content = fileDataToString(fileData);
6922
+ const result = performStringReplacement(
6923
+ content,
6924
+ oldString,
6925
+ newString,
6926
+ replaceAll
6927
+ );
6928
+ if (typeof result === "string") {
6929
+ return { error: result };
6930
+ }
6931
+ const [newContent, occurrences] = result;
6932
+ const newFileData = updateFileData(fileData, newContent);
6933
+ this.files.set(filePath, newFileData);
6934
+ return { path: filePath, filesUpdate: null, occurrences };
6935
+ }
6936
+ grepRaw(pattern, path5 = "/", glob = null) {
6937
+ const files = this.getFiles();
6938
+ return grepMatchesFromFiles(files, pattern, path5, glob);
6939
+ }
6940
+ globInfo(pattern, path5 = "/") {
6941
+ const files = this.getFiles();
6942
+ const result = globSearchFiles(files, pattern, path5);
6943
+ if (result === "No files found") {
6944
+ return [];
6945
+ }
6946
+ const paths = result.split("\n");
6947
+ const infos = [];
6948
+ for (const p of paths) {
6949
+ const fd = files[p];
6950
+ const size = fd ? fd.content.join("\n").length : 0;
6951
+ infos.push({
6952
+ path: p,
6953
+ is_dir: false,
6954
+ size,
6955
+ modified_at: fd?.modified_at || ""
6956
+ });
6957
+ }
6958
+ return infos;
6959
+ }
6960
+ };
6961
+
6962
+ // src/deep_agent_new/middleware/todos.ts
6963
+ import { Command as Command3 } from "@langchain/langgraph";
6964
+ import { z as z36 } from "zod";
6965
+ import { createMiddleware as createMiddleware8, tool as tool35, ToolMessage as ToolMessage4 } from "langchain";
6966
+ 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.
6967
+ It also helps the user understand the progress of the task and overall progress of their requests.
6968
+ 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.
6969
+
6970
+ ## When to Use This Tool
6971
+ Use this tool in these scenarios:
6972
+
6973
+ 1. Complex multi-step tasks - When a task requires 3 or more distinct steps or actions
6974
+ 2. Non-trivial and complex tasks - Tasks that require careful planning or multiple operations
6975
+ 3. User explicitly requests todo list - When the user directly asks you to use the todo list
6976
+ 4. User provides multiple tasks - When users provide a list of things to be done (numbered or comma-separated)
6977
+ 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.
6978
+
6979
+ ## How to Use This Tool
6980
+ 1. When you start working on a task - Mark it as in_progress BEFORE beginning work.
6981
+ 2. After completing a task - Mark it as completed and add any new follow-up tasks discovered during implementation.
6982
+ 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.
6983
+ 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.
6984
+
6985
+ ## When NOT to Use This Tool
6986
+ It is important to skip using this tool when:
6987
+ 1. There is only a single, straightforward task
6988
+ 2. The task is trivial and tracking it provides no benefit
6989
+ 3. The task can be completed in less than 3 trivial steps
6990
+ 4. The task is purely conversational or informational
6991
+
6992
+ ## Examples of When to Use the Todo List
6993
+
6994
+ <example>
6995
+ 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!
6996
+ Assistant: I'll help add a dark mode toggle to your application settings. Let me create a todo list to track this implementation.
6997
+ *Creates todo list with the following items:*
6998
+ 1. Create dark mode toggle component in Settings page
6999
+ 2. Add dark mode state management (context/store)
7000
+ 3. Implement CSS-in-JS styles for dark theme
7001
+ 4. Update existing components to support theme switching
7002
+ 5. Run tests and build process, addressing any failures or errors that occur
7003
+ *Begins working on the first task*
7004
+
7005
+ <reasoning>
7006
+ The assistant used the todo list because:
7007
+ 1. Adding dark mode in it of itself is a multi-step feature requiring UI, state management, and styling changes
7008
+ 2. The assistant inferred that tests and build need to pass by adding "Ensure tests and build succeed" as the final task
5507
7009
  3. Both of the user's requests are complex and require multiple steps to complete.
5508
7010
  </reasoning>
5509
7011
  </example>
@@ -5609,289 +7111,1896 @@ Assistant: I'll check the current time in Tokyo for you.
5609
7111
 
5610
7112
  *Searches for current time in Tokyo*
5611
7113
 
5612
- The current time in Tokyo, Japan is [current time]. Tokyo is in the Japan Standard Time (JST) zone, which is UTC+9.
7114
+ The current time in Tokyo, Japan is [current time]. Tokyo is in the Japan Standard Time (JST) zone, which is UTC+9.
7115
+
7116
+ <reasoning>
7117
+ 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.
7118
+ </reasoning>
7119
+ </example>
7120
+
7121
+ <example>
7122
+ User: I need to write a function that checks if a number is prime and then test it out.
7123
+ Assistant: I'll help you write a function that checks if a number is prime and then test it out.
7124
+ *Writes function that checks if a number is prime*
7125
+ *Tests the function*
7126
+
7127
+ <reasoning>
7128
+ 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.
7129
+ </reasoning>
7130
+ </example>
7131
+
7132
+ <example>
7133
+ User: I want you to order a pizza from Dominos, order a burger from McDonald's, and order a salad from Subway.
7134
+ Assistant: I'll help you order a pizza from Dominos, order a burger from McDonald's, and order a salad from Subway.
7135
+ *Orders a pizza from Dominos*
7136
+ *Orders a burger from McDonald's*
7137
+ *Orders a salad from Subway*
7138
+
7139
+ <reasoning>
7140
+ 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.
7141
+ Using the todo list here is overkill and wastes time and tokens. These three tool calls should be made in parallel, in fact.
7142
+ </reasoning>
7143
+ </example>
7144
+
7145
+
7146
+ ## Task States and Management
7147
+
7148
+ 1. **Task States**: Use these states to track progress:
7149
+ - pending: Task not yet started
7150
+ - 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)
7151
+ - completed: Task finished successfully
7152
+
7153
+ 2. **Task Management**:
7154
+ - Update task status in real-time as you work
7155
+ - Mark tasks complete IMMEDIATELY after finishing (don't batch completions)
7156
+ - Complete current tasks before starting new ones
7157
+ - Remove tasks that are no longer relevant from the list entirely
7158
+ - IMPORTANT: When you write this todo list, you should mark your first task (or tasks) as in_progress immediately!.
7159
+ - 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.
7160
+
7161
+ 3. **Task Completion Requirements**:
7162
+ - ONLY mark a task as completed when you have FULLY accomplished it
7163
+ - If you encounter errors, blockers, or cannot finish, keep the task as in_progress
7164
+ - When blocked, create a new task describing what needs to be resolved
7165
+ - Never mark a task as completed if:
7166
+ - There are unresolved issues or errors
7167
+ - Work is partial or incomplete
7168
+ - You encountered blockers that prevent completion
7169
+ - You couldn't find necessary resources or dependencies
7170
+ - Quality standards haven't been met
7171
+
7172
+ 4. **Task Breakdown**:
7173
+ - Create specific, actionable items
7174
+ - Break complex tasks into smaller, manageable steps
7175
+ - Use clear, descriptive task names
7176
+
7177
+ Being proactive with task management demonstrates attentiveness and ensures you complete all requirements successfully
7178
+ 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.`;
7179
+ var TODO_LIST_MIDDLEWARE_SYSTEM_PROMPT = `## \`write_todos\`
7180
+
7181
+ You have access to the \`write_todos\` tool to help you manage and plan complex objectives.
7182
+ Use this tool for complex objectives to ensure that you are tracking each necessary step and giving the user visibility into your progress.
7183
+ This tool is very helpful for planning complex objectives, and for breaking down these larger complex objectives into smaller steps.
7184
+
7185
+ 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.
7186
+ For simple objectives that only require a few steps, it is better to just complete the objective directly and NOT use this tool.
7187
+ 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.
5613
7188
 
5614
- <reasoning>
5615
- 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.
5616
- </reasoning>
5617
- </example>
7189
+ ## Important To-Do List Usage Notes to Remember
7190
+ - The \`write_todos\` tool should never be called multiple times in parallel.
7191
+ - 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.`;
7192
+ var TodoStatus = z36.enum(["pending", "in_progress", "completed"]).describe("Status of the todo");
7193
+ var TodoSchema = z36.object({
7194
+ content: z36.string().describe("Content of the todo item"),
7195
+ status: TodoStatus
7196
+ });
7197
+ var stateSchema = z36.object({ todos: z36.array(TodoSchema).default([]) });
7198
+ function todoListMiddleware(options) {
7199
+ const writeTodos = tool35(
7200
+ ({ todos }, config) => {
7201
+ return new Command3({
7202
+ update: {
7203
+ todos,
7204
+ messages: [
7205
+ new ToolMessage4({
7206
+ content: genUIMarkdown("todo_list", todos),
7207
+ tool_call_id: config.toolCall?.id
7208
+ })
7209
+ ]
7210
+ }
7211
+ });
7212
+ },
7213
+ {
7214
+ name: "write_todos",
7215
+ description: options?.toolDescription ?? WRITE_TODOS_DESCRIPTION,
7216
+ schema: z36.object({
7217
+ todos: z36.array(TodoSchema).describe("List of todo items to update")
7218
+ })
7219
+ }
7220
+ );
7221
+ return createMiddleware8({
7222
+ name: "todoListMiddleware",
7223
+ stateSchema,
7224
+ tools: [writeTodos],
7225
+ wrapModelCall: (request, handler) => handler({
7226
+ ...request,
7227
+ systemPrompt: (request.systemPrompt ? `${request.systemPrompt}
5618
7228
 
5619
- <example>
5620
- User: I need to write a function that checks if a number is prime and then test it out.
5621
- Assistant: I'll help you write a function that checks if a number is prime and then test it out.
5622
- *Writes function that checks if a number is prime*
5623
- *Tests the function*
7229
+ ` : "") + (options?.systemPrompt ?? TODO_LIST_MIDDLEWARE_SYSTEM_PROMPT)
7230
+ })
7231
+ });
7232
+ }
5624
7233
 
5625
- <reasoning>
5626
- 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.
5627
- </reasoning>
5628
- </example>
7234
+ // src/deep_agent_new/agent.ts
7235
+ var BASE_PROMPT = `In order to complete the objective that the user asks of you, you have access to a number of standard tools.`;
7236
+ function createDeepAgent(params = {}) {
7237
+ const {
7238
+ model = "claude-sonnet-4-5-20250929",
7239
+ tools = [],
7240
+ systemPrompt,
7241
+ middleware: customMiddleware = [],
7242
+ subagents = [],
7243
+ responseFormat,
7244
+ contextSchema,
7245
+ checkpointer,
7246
+ store,
7247
+ backend,
7248
+ interruptOn,
7249
+ name,
7250
+ skills
7251
+ } = params;
7252
+ const finalSystemPrompt = systemPrompt ? `${systemPrompt}
5629
7253
 
5630
- <example>
5631
- User: I want you to order a pizza from Dominos, order a burger from McDonald's, and order a salad from Subway.
5632
- Assistant: I'll help you order a pizza from Dominos, order a burger from McDonald's, and order a salad from Subway.
5633
- *Orders a pizza from Dominos*
5634
- *Orders a burger from McDonald's*
5635
- *Orders a salad from Subway*
7254
+ ${BASE_PROMPT}` : BASE_PROMPT;
7255
+ const filesystemBackend = backend ? backend : async (config) => new StateBackend(config);
7256
+ const middleware = [
7257
+ // Provides todo list management capabilities for tracking tasks
7258
+ todoListMiddleware(),
7259
+ // Enables filesystem operations and optional long-term memory storage
7260
+ createFilesystemMiddleware({ backend: filesystemBackend }),
7261
+ // Enables delegation to specialized subagents for complex tasks
7262
+ createSubAgentMiddleware({
7263
+ defaultModel: model,
7264
+ defaultTools: tools,
7265
+ defaultMiddleware: [
7266
+ // Subagent middleware: Todo list management
7267
+ todoListMiddleware(),
7268
+ // Subagent middleware: Filesystem operations
7269
+ createFilesystemMiddleware({
7270
+ backend: filesystemBackend
7271
+ }),
7272
+ // Subagent middleware: Automatic conversation summarization when token limits are approached
7273
+ summarizationMiddleware({
7274
+ model,
7275
+ trigger: { tokens: 17e4 },
7276
+ keep: { messages: 6 }
7277
+ }),
7278
+ // Subagent middleware: Anthropic prompt caching for improved performance
7279
+ anthropicPromptCachingMiddleware({
7280
+ unsupportedModelBehavior: "ignore"
7281
+ }),
7282
+ // Subagent middleware: Patches tool calls for compatibility
7283
+ createPatchToolCallsMiddleware()
7284
+ ],
7285
+ defaultInterruptOn: interruptOn,
7286
+ subagents,
7287
+ generalPurposeAgent: false
7288
+ }),
7289
+ // Automatically summarizes conversation history when token limits are approached
7290
+ summarizationMiddleware({
7291
+ model,
7292
+ trigger: { tokens: 17e4 },
7293
+ keep: { messages: 6 }
7294
+ }),
7295
+ // Enables Anthropic prompt caching for improved performance and reduced costs
7296
+ anthropicPromptCachingMiddleware({
7297
+ unsupportedModelBehavior: "ignore"
7298
+ }),
7299
+ // Patches tool calls to ensure compatibility across different model providers
7300
+ createPatchToolCallsMiddleware()
7301
+ ];
7302
+ if (interruptOn) {
7303
+ middleware.push(humanInTheLoopMiddleware2({ interruptOn }));
7304
+ }
7305
+ middleware.push(...customMiddleware);
7306
+ return createAgent3({
7307
+ model,
7308
+ systemPrompt: finalSystemPrompt,
7309
+ tools,
7310
+ middleware,
7311
+ responseFormat,
7312
+ contextSchema,
7313
+ checkpointer,
7314
+ store,
7315
+ name
7316
+ });
7317
+ }
5636
7318
 
5637
- <reasoning>
5638
- 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.
5639
- Using the todo list here is overkill and wastes time and tokens. These three tool calls should be made in parallel, in fact.
5640
- </reasoning>
5641
- </example>
7319
+ // src/agent_lattice/builders/filesystemBackend.ts
7320
+ function createFilesystemBackendFactory(middlewareConfigs) {
7321
+ const filesystemConfig = middlewareConfigs.find((m) => m.type === "filesystem");
7322
+ if (!filesystemConfig || !filesystemConfig.enabled) {
7323
+ return void 0;
7324
+ }
7325
+ const isolatedLevel = filesystemConfig.config?.isolatedLevel || "global";
7326
+ return async (config) => {
7327
+ const { workspaceId, projectId } = config;
7328
+ let sandboxName = "global";
7329
+ if (isolatedLevel === "agent") {
7330
+ sandboxName = "agent";
7331
+ } else if (isolatedLevel === "thread") {
7332
+ sandboxName = "thread";
7333
+ }
7334
+ const sandboxManager = sandboxLatticeManager.getSandboxLattice("default");
7335
+ if (!sandboxManager) {
7336
+ throw new Error("Sandbox manager not found");
7337
+ }
7338
+ return new SandboxFilesystem({
7339
+ sandboxInstance: await sandboxManager.createSandbox(sandboxName),
7340
+ workingDirectory: workspaceId && projectId ? `/workspaces/${workspaceId}/${projectId}` : "/"
7341
+ });
7342
+ };
7343
+ }
5642
7344
 
7345
+ // src/agent_lattice/builders/DeepAgentGraphBuilder.ts
7346
+ var DeepAgentGraphBuilder = class {
7347
+ /**
7348
+ * 根据 middleware 配置创建 middlewares
7349
+ */
7350
+ createMiddlewares(middlewareConfigs) {
7351
+ return createCommonMiddlewares(middlewareConfigs);
7352
+ }
7353
+ /**
7354
+ * 构建Deep Agent Graph
7355
+ *
7356
+ * @param agentLattice Agent Lattice对象
7357
+ * @param params Agent构建参数
7358
+ * @returns 返回CompiledGraph对象
7359
+ */
7360
+ build(agentLattice, params) {
7361
+ const tools = params.tools.map((t) => {
7362
+ const toolClient = getToolClient(t.key);
7363
+ return toolClient;
7364
+ }).filter((tool38) => tool38 !== void 0);
7365
+ const subagents = params.subAgents.map((sa) => {
7366
+ if (sa.client) {
7367
+ return {
7368
+ name: sa.config.name,
7369
+ description: sa.config.description,
7370
+ runnable: sa.client
7371
+ };
7372
+ } else {
7373
+ const subagentClient = createAgentClientFromAgentLattice({
7374
+ config: sa.config
7375
+ });
7376
+ return {
7377
+ name: sa.config.name,
7378
+ description: sa.config.description,
7379
+ runnable: subagentClient
7380
+ };
7381
+ }
7382
+ });
7383
+ const middlewareConfigs = params.middleware || [];
7384
+ const filesystemBackend = createFilesystemBackendFactory(middlewareConfigs);
7385
+ const middlewares = this.createMiddlewares(middlewareConfigs);
7386
+ const deepAgent = createDeepAgent({
7387
+ tools,
7388
+ model: params.model,
7389
+ contextSchema: params.stateSchema,
7390
+ systemPrompt: params.prompt,
7391
+ subagents,
7392
+ checkpointer: getCheckpointSaver("default"),
7393
+ skills: params.skillCategories,
7394
+ backend: filesystemBackend,
7395
+ middleware: middlewares
7396
+ });
7397
+ return deepAgent;
7398
+ }
7399
+ };
5643
7400
 
5644
- ## Task States and Management
7401
+ // src/agent_team/agent_team.ts
7402
+ import { z as z39 } from "zod/v3";
7403
+ import { createAgent as createAgent5 } from "langchain";
7404
+
7405
+ // src/agent_team/types.ts
7406
+ var TaskStatus = /* @__PURE__ */ ((TaskStatus3) => {
7407
+ TaskStatus3["PENDING"] = "pending";
7408
+ TaskStatus3["CLAIMED"] = "claimed";
7409
+ TaskStatus3["IN_PROGRESS"] = "in_progress";
7410
+ TaskStatus3["COMPLETED"] = "completed";
7411
+ TaskStatus3["FAILED"] = "failed";
7412
+ return TaskStatus3;
7413
+ })(TaskStatus || {});
7414
+ var MessageType = /* @__PURE__ */ ((MessageType2) => {
7415
+ MessageType2["DIRECT_MESSAGE"] = "direct_message";
7416
+ MessageType2["BROADCAST"] = "broadcast";
7417
+ MessageType2["PLAN_REQUEST"] = "plan_request";
7418
+ MessageType2["PLAN_FEEDBACK"] = "plan_feedback";
7419
+ MessageType2["SHUTDOWN_REQUEST"] = "shutdown_request";
7420
+ MessageType2["SHUTDOWN_RESPONSE"] = "shutdown_response";
7421
+ MessageType2["TASK_FEEDBACK"] = "task_feedback";
7422
+ MessageType2["STATUS_UPDATE"] = "status_update";
7423
+ return MessageType2;
7424
+ })(MessageType || {});
7425
+
7426
+ // src/agent_team/stores/InMemoryTaskListStore.ts
7427
+ import { EventEmitter as EventEmitter2 } from "events";
7428
+ var InMemoryTaskListStore = class {
7429
+ constructor() {
7430
+ /** Map<teamId, Map<taskId, TeamTask>> */
7431
+ this.tasks = /* @__PURE__ */ new Map();
7432
+ /** EventEmitter for task lifecycle events */
7433
+ this.emitter = new EventEmitter2();
7434
+ /** Auto-incrementing ID counter */
7435
+ this.idCounter = 0;
7436
+ this.emitter.setMaxListeners(200);
7437
+ }
7438
+ // -------------------------------------------------------------------------
7439
+ // Helpers
7440
+ // -------------------------------------------------------------------------
7441
+ /** Get or create the task map for a team. */
7442
+ getTeamTasks(teamId) {
7443
+ let map = this.tasks.get(teamId);
7444
+ if (!map) {
7445
+ map = /* @__PURE__ */ new Map();
7446
+ this.tasks.set(teamId, map);
7447
+ }
7448
+ return map;
7449
+ }
7450
+ /** Generate a unique task ID. */
7451
+ nextId() {
7452
+ this.idCounter += 1;
7453
+ return `task-${this.idCounter}`;
7454
+ }
7455
+ /** Emit a scoped event: `${teamId}:${event}`. */
7456
+ emit(teamId, event, task) {
7457
+ this.emitter.emit(`${teamId}:${event}`, task);
7458
+ }
7459
+ /**
7460
+ * Resolve dependency references by task IDs.
7461
+ */
7462
+ resolveDependencies(teamTasks, depIds) {
7463
+ const resolved = [];
7464
+ for (const ref of depIds) {
7465
+ if (teamTasks.has(ref)) {
7466
+ resolved.push(ref);
7467
+ }
7468
+ }
7469
+ return resolved;
7470
+ }
7471
+ /**
7472
+ * Check whether all dependencies of a task are in COMPLETED status.
7473
+ */
7474
+ areDependenciesSatisfied(teamTasks, task) {
7475
+ if (task.dependencies.length === 0) return true;
7476
+ return task.dependencies.every((depId) => {
7477
+ const dep = teamTasks.get(depId);
7478
+ return dep !== void 0 && dep.status === "completed" /* COMPLETED */;
7479
+ });
7480
+ }
7481
+ // -------------------------------------------------------------------------
7482
+ // Lifecycle
7483
+ // -------------------------------------------------------------------------
7484
+ async addTask(teamId, spec) {
7485
+ const teamTasks = this.getTeamTasks(teamId);
7486
+ if (!/^task-\d+$/.test(spec.id)) {
7487
+ throw new Error(`Invalid task ID format: ${spec.id}. Expected format: task-01, task-02, etc.`);
7488
+ }
7489
+ if (teamTasks.has(spec.id)) {
7490
+ throw new Error(`Task ID already exists: ${spec.id}`);
7491
+ }
7492
+ const now = /* @__PURE__ */ new Date();
7493
+ const task = {
7494
+ id: spec.id,
7495
+ title: spec.title,
7496
+ description: spec.description,
7497
+ assignee: spec.assignee,
7498
+ status: "pending" /* PENDING */,
7499
+ dependencies: spec.dependencies ? this.resolveDependencies(teamTasks, spec.dependencies) : [],
7500
+ createdAt: now,
7501
+ updatedAt: now
7502
+ };
7503
+ teamTasks.set(task.id, task);
7504
+ this.emit(teamId, "task:added", task);
7505
+ return task;
7506
+ }
7507
+ async addTasks(teamId, specs) {
7508
+ const teamTasks = this.getTeamTasks(teamId);
7509
+ const now = /* @__PURE__ */ new Date();
7510
+ const created = [];
7511
+ const seenIds = /* @__PURE__ */ new Set();
7512
+ for (const spec of specs) {
7513
+ if (!/^task-\d+$/.test(spec.id)) {
7514
+ throw new Error(`Invalid task ID format: ${spec.id}. Expected format: task-01, task-02, etc.`);
7515
+ }
7516
+ if (seenIds.has(spec.id)) {
7517
+ throw new Error(`Duplicate task ID: ${spec.id}`);
7518
+ }
7519
+ if (teamTasks.has(spec.id)) {
7520
+ throw new Error(`Task ID already exists: ${spec.id}`);
7521
+ }
7522
+ seenIds.add(spec.id);
7523
+ }
7524
+ for (const spec of specs) {
7525
+ const task = {
7526
+ id: spec.id,
7527
+ title: spec.title,
7528
+ description: spec.description,
7529
+ assignee: spec.assignee,
7530
+ status: "pending" /* PENDING */,
7531
+ dependencies: [],
7532
+ createdAt: now,
7533
+ updatedAt: now
7534
+ };
7535
+ teamTasks.set(task.id, task);
7536
+ created.push(task);
7537
+ }
7538
+ for (let i = 0; i < specs.length; i++) {
7539
+ if (specs[i].dependencies && specs[i].dependencies.length > 0) {
7540
+ created[i].dependencies = this.resolveDependencies(
7541
+ teamTasks,
7542
+ specs[i].dependencies
7543
+ );
7544
+ }
7545
+ }
7546
+ for (const task of created) {
7547
+ this.emit(teamId, "task:added", task);
7548
+ }
7549
+ return created;
7550
+ }
7551
+ async updateTask(teamId, taskId, updates) {
7552
+ const teamTasks = this.getTeamTasks(teamId);
7553
+ const task = teamTasks.get(taskId);
7554
+ if (!task) return null;
7555
+ if (updates.title !== void 0) task.title = updates.title;
7556
+ if (updates.description !== void 0) task.description = updates.description;
7557
+ if (updates.assignee !== void 0) task.assignee = updates.assignee;
7558
+ if (updates.status !== void 0) task.status = updates.status;
7559
+ if (updates.dependencies !== void 0) {
7560
+ task.dependencies = this.resolveDependencies(teamTasks, updates.dependencies);
7561
+ }
7562
+ task.updatedAt = /* @__PURE__ */ new Date();
7563
+ return task;
7564
+ }
7565
+ async removeTask(teamId, taskId) {
7566
+ const teamTasks = this.getTeamTasks(teamId);
7567
+ const task = teamTasks.get(taskId);
7568
+ if (!task) return false;
7569
+ teamTasks.delete(taskId);
7570
+ this.emit(teamId, "task:removed", task);
7571
+ return true;
7572
+ }
7573
+ // -------------------------------------------------------------------------
7574
+ // Claim / Complete
7575
+ // -------------------------------------------------------------------------
7576
+ /**
7577
+ * Claim a specific task by ID: set assignee to agentId and status to CLAIMED.
7578
+ * Only succeeds if the task is PENDING and all dependencies are COMPLETED.
7579
+ */
7580
+ async claimTaskById(teamId, taskId, agentId) {
7581
+ const teamTasks = this.getTeamTasks(teamId);
7582
+ const task = teamTasks.get(taskId);
7583
+ if (!task) return null;
7584
+ if (task.status !== "pending" /* PENDING */) return null;
7585
+ if (!this.areDependenciesSatisfied(teamTasks, task)) return null;
7586
+ task.status = "claimed" /* CLAIMED */;
7587
+ task.assignee = agentId;
7588
+ task.updatedAt = /* @__PURE__ */ new Date();
7589
+ this.emit(teamId, "task:claimed", task);
7590
+ return task;
7591
+ }
7592
+ async completeTask(teamId, taskId, result) {
7593
+ const teamTasks = this.getTeamTasks(teamId);
7594
+ const task = teamTasks.get(taskId);
7595
+ if (!task) return null;
7596
+ task.status = "completed" /* COMPLETED */;
7597
+ task.result = result;
7598
+ task.updatedAt = /* @__PURE__ */ new Date();
7599
+ this.emit(teamId, "task:completed", task);
7600
+ return task;
7601
+ }
7602
+ async failTask(teamId, taskId, error) {
7603
+ const teamTasks = this.getTeamTasks(teamId);
7604
+ const task = teamTasks.get(taskId);
7605
+ if (!task) return null;
7606
+ task.status = "failed" /* FAILED */;
7607
+ task.error = error;
7608
+ task.updatedAt = /* @__PURE__ */ new Date();
7609
+ this.emit(teamId, "task:failed", task);
7610
+ return task;
7611
+ }
7612
+ // -------------------------------------------------------------------------
7613
+ // Query
7614
+ // -------------------------------------------------------------------------
7615
+ async getTask(teamId, taskId) {
7616
+ const teamTasks = this.tasks.get(teamId);
7617
+ if (!teamTasks) return null;
7618
+ return teamTasks.get(taskId) ?? null;
7619
+ }
7620
+ async getTasksByStatus(teamId, status) {
7621
+ const teamTasks = this.tasks.get(teamId);
7622
+ if (!teamTasks) return [];
7623
+ return Array.from(teamTasks.values()).filter((t) => t.status === status);
7624
+ }
7625
+ async getAllTasks(teamId) {
7626
+ const teamTasks = this.tasks.get(teamId);
7627
+ if (!teamTasks) return [];
7628
+ return Array.from(teamTasks.values());
7629
+ }
7630
+ async isAllDone(teamId) {
7631
+ const teamTasks = this.tasks.get(teamId);
7632
+ if (!teamTasks || teamTasks.size === 0) return true;
7633
+ for (const task of teamTasks.values()) {
7634
+ if (task.status === "pending" /* PENDING */ || task.status === "claimed" /* CLAIMED */ || task.status === "in_progress" /* IN_PROGRESS */) {
7635
+ return false;
7636
+ }
7637
+ }
7638
+ return true;
7639
+ }
7640
+ async hasClaimable(teamId) {
7641
+ const teamTasks = this.tasks.get(teamId);
7642
+ if (!teamTasks) return false;
7643
+ for (const task of teamTasks.values()) {
7644
+ if (task.status === "pending" /* PENDING */ && this.areDependenciesSatisfied(teamTasks, task)) {
7645
+ return true;
7646
+ }
7647
+ }
7648
+ return false;
7649
+ }
7650
+ // -------------------------------------------------------------------------
7651
+ // Events
7652
+ // -------------------------------------------------------------------------
7653
+ onTaskEvent(teamId, event, callback) {
7654
+ this.emitter.on(`${teamId}:${event}`, callback);
7655
+ }
7656
+ offTaskEvent(teamId, event, callback) {
7657
+ this.emitter.off(`${teamId}:${event}`, callback);
7658
+ }
7659
+ // -------------------------------------------------------------------------
7660
+ // Utilities
7661
+ // -------------------------------------------------------------------------
7662
+ /** Remove all tasks for a team (useful for cleanup / testing). */
7663
+ clearTeam(teamId) {
7664
+ this.tasks.delete(teamId);
7665
+ }
7666
+ /** Remove all tasks across all teams (useful for testing). */
7667
+ clear() {
7668
+ this.tasks.clear();
7669
+ }
7670
+ };
5645
7671
 
5646
- 1. **Task States**: Use these states to track progress:
5647
- - pending: Task not yet started
5648
- - 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)
5649
- - completed: Task finished successfully
7672
+ // src/agent_team/stores/InMemoryMailboxStore.ts
7673
+ import { EventEmitter as EventEmitter3 } from "events";
7674
+ var InMemoryMailboxStore = class {
7675
+ constructor() {
7676
+ /** Map<teamId, Map<agentId, MailboxMessage[]>> */
7677
+ this.messages = /* @__PURE__ */ new Map();
7678
+ /** Map<teamId, Set<agentId>> -- registered agents for broadcast */
7679
+ this.agents = /* @__PURE__ */ new Map();
7680
+ /** EventEmitter for real-time message notifications */
7681
+ this.emitter = new EventEmitter3();
7682
+ /** Auto-incrementing message ID counter */
7683
+ this.idCounter = 0;
7684
+ this.emitter.setMaxListeners(200);
7685
+ }
7686
+ // -------------------------------------------------------------------------
7687
+ // Helpers
7688
+ // -------------------------------------------------------------------------
7689
+ /** Get or create the agent message list for a team. */
7690
+ getAgentMessages(teamId, agentId) {
7691
+ let teamMap = this.messages.get(teamId);
7692
+ if (!teamMap) {
7693
+ teamMap = /* @__PURE__ */ new Map();
7694
+ this.messages.set(teamId, teamMap);
7695
+ }
7696
+ let list = teamMap.get(agentId);
7697
+ if (!list) {
7698
+ list = [];
7699
+ teamMap.set(agentId, list);
7700
+ }
7701
+ return list;
7702
+ }
7703
+ /** Generate a unique message ID. */
7704
+ nextId() {
7705
+ this.idCounter += 1;
7706
+ return `msg-${this.idCounter}`;
7707
+ }
7708
+ /** Store a message and emit a real-time event for the recipient. */
7709
+ storeAndNotify(teamId, message) {
7710
+ const list = this.getAgentMessages(teamId, message.to);
7711
+ list.push(message);
7712
+ const preview = message.content.length > 80 ? message.content.slice(0, 80) + "..." : message.content;
7713
+ console.log(
7714
+ `[Mailbox] ${teamId} | ${message.from} \u2192 ${message.to} [${message.type}] ${message.id}: ${preview}`
7715
+ );
7716
+ this.emitter.emit(`${teamId}:${message.to}:message`, message);
7717
+ this.emitter.emit(`${teamId}:message`, message);
7718
+ }
7719
+ // -------------------------------------------------------------------------
7720
+ // Messaging
7721
+ // -------------------------------------------------------------------------
7722
+ async sendMessage(teamId, from, to, content, type = "direct_message" /* DIRECT_MESSAGE */) {
7723
+ const message = {
7724
+ id: this.nextId(),
7725
+ from,
7726
+ to,
7727
+ content,
7728
+ timestamp: /* @__PURE__ */ new Date(),
7729
+ type,
7730
+ read: false
7731
+ };
7732
+ this.storeAndNotify(teamId, message);
7733
+ return message;
7734
+ }
7735
+ async broadcastMessage(teamId, from, content, type = "broadcast" /* BROADCAST */) {
7736
+ const agentSet = this.agents.get(teamId);
7737
+ if (!agentSet || agentSet.size === 0) return [];
7738
+ const created = [];
7739
+ for (const agentId of agentSet) {
7740
+ if (agentId === from) continue;
7741
+ const message = {
7742
+ id: this.nextId(),
7743
+ from,
7744
+ to: agentId,
7745
+ content,
7746
+ timestamp: /* @__PURE__ */ new Date(),
7747
+ type,
7748
+ read: false
7749
+ };
7750
+ this.storeAndNotify(teamId, message);
7751
+ created.push(message);
7752
+ }
7753
+ return created;
7754
+ }
7755
+ async getMessages(teamId, agentId) {
7756
+ const teamMap = this.messages.get(teamId);
7757
+ if (!teamMap) return [];
7758
+ return teamMap.get(agentId) ?? [];
7759
+ }
7760
+ async getUnreadMessages(teamId, agentId) {
7761
+ const all = await this.getMessages(teamId, agentId);
7762
+ return all.filter((m) => !m.read);
7763
+ }
7764
+ async markAsRead(teamId, agentId, messageId) {
7765
+ const all = await this.getMessages(teamId, agentId);
7766
+ const msg = all.find((m) => m.id === messageId);
7767
+ if (msg) {
7768
+ msg.read = true;
7769
+ console.log(
7770
+ `[Mailbox] ${teamId} | ${agentId} marked ${messageId} as read (from: ${msg.from})`
7771
+ );
7772
+ }
7773
+ }
7774
+ // -------------------------------------------------------------------------
7775
+ // Agent registration
7776
+ // -------------------------------------------------------------------------
7777
+ async registerAgent(teamId, agentId) {
7778
+ let agentSet = this.agents.get(teamId);
7779
+ if (!agentSet) {
7780
+ agentSet = /* @__PURE__ */ new Set();
7781
+ this.agents.set(teamId, agentSet);
7782
+ }
7783
+ agentSet.add(agentId);
7784
+ }
7785
+ async unregisterAgent(teamId, agentId) {
7786
+ const agentSet = this.agents.get(teamId);
7787
+ if (agentSet) {
7788
+ agentSet.delete(agentId);
7789
+ }
7790
+ }
7791
+ async getRegisteredAgents(teamId) {
7792
+ const agentSet = this.agents.get(teamId);
7793
+ if (!agentSet) return [];
7794
+ return Array.from(agentSet);
7795
+ }
7796
+ // -------------------------------------------------------------------------
7797
+ // Events
7798
+ // -------------------------------------------------------------------------
7799
+ onMessage(teamId, agentId, callback) {
7800
+ this.emitter.on(`${teamId}:${agentId}:message`, callback);
7801
+ }
7802
+ offMessage(teamId, agentId, callback) {
7803
+ this.emitter.off(`${teamId}:${agentId}:message`, callback);
7804
+ }
7805
+ onTeamMessage(teamId, callback) {
7806
+ this.emitter.on(`${teamId}:message`, callback);
7807
+ }
7808
+ offTeamMessage(teamId, callback) {
7809
+ this.emitter.off(`${teamId}:message`, callback);
7810
+ }
7811
+ async getAllTeamMessages(teamId) {
7812
+ const teamMap = this.messages.get(teamId);
7813
+ if (!teamMap) return [];
7814
+ const allMessages = [];
7815
+ for (const messages of teamMap.values()) {
7816
+ allMessages.push(...messages);
7817
+ }
7818
+ return allMessages.sort(
7819
+ (a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()
7820
+ );
7821
+ }
7822
+ // -------------------------------------------------------------------------
7823
+ // Utilities
7824
+ // -------------------------------------------------------------------------
7825
+ /** Remove all data for a team (messages + registrations). */
7826
+ clearTeam(teamId) {
7827
+ this.messages.delete(teamId);
7828
+ this.agents.delete(teamId);
7829
+ }
7830
+ /** Remove all data across all teams. */
7831
+ clear() {
7832
+ this.messages.clear();
7833
+ this.agents.clear();
7834
+ }
7835
+ };
5650
7836
 
5651
- 2. **Task Management**:
5652
- - Update task status in real-time as you work
5653
- - Mark tasks complete IMMEDIATELY after finishing (don't batch completions)
5654
- - Complete current tasks before starting new ones
5655
- - Remove tasks that are no longer relevant from the list entirely
5656
- - IMPORTANT: When you write this todo list, you should mark your first task (or tasks) as in_progress immediately!.
5657
- - 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.
7837
+ // src/agent_team/middleware/team.ts
7838
+ import { z as z38 } from "zod/v3";
7839
+ import { createMiddleware as createMiddleware9, createAgent as createAgent4, tool as tool37, ToolMessage as ToolMessage6 } from "langchain";
7840
+ import { Command as Command5, getCurrentTaskInput as getCurrentTaskInput3 } from "@langchain/langgraph";
7841
+ import { v4 as uuidv4 } from "uuid";
7842
+
7843
+ // src/agent_team/middleware/teammate_tools.ts
7844
+ import { z as z37 } from "zod/v3";
7845
+ import { tool as tool36, ToolMessage as ToolMessage5 } from "langchain";
7846
+ import { Command as Command4 } from "@langchain/langgraph";
7847
+
7848
+ // src/agent_team/middleware/formatMessages.ts
7849
+ function formatMessagesAsMarkdown(msgs) {
7850
+ if (msgs.length === 0) return "No unread messages.";
7851
+ const header = `## Unread messages (${msgs.length})
7852
+ `;
7853
+ const sections = msgs.map((m, i) => {
7854
+ const title = `### ${i + 1}. From: **${m.from}**`;
7855
+ const meta = `- **Type:** \`${m.type}\`
7856
+ - **Time:** ${m.timestamp.toISOString()}`;
7857
+ const body = m.content.trim() ? `
7858
+
7859
+ ${m.content.trim()}` : "";
7860
+ return `---
7861
+ ${title}
7862
+ ${meta}${body}`;
7863
+ });
7864
+ return header + sections.join("\n\n");
7865
+ }
5658
7866
 
5659
- 3. **Task Completion Requirements**:
5660
- - ONLY mark a task as completed when you have FULLY accomplished it
5661
- - If you encounter errors, blockers, or cannot finish, keep the task as in_progress
5662
- - When blocked, create a new task describing what needs to be resolved
5663
- - Never mark a task as completed if:
5664
- - There are unresolved issues or errors
5665
- - Work is partial or incomplete
5666
- - You encountered blockers that prevent completion
5667
- - You couldn't find necessary resources or dependencies
5668
- - Quality standards haven't been met
7867
+ // src/agent_team/middleware/teammate_tools.ts
7868
+ function createTeammateTools(options) {
7869
+ const { teamId, agentId, taskListStore, mailboxStore } = options;
7870
+ const claimTaskTool = tool36(
7871
+ async (input) => {
7872
+ const task = await taskListStore.claimTaskById(
7873
+ teamId,
7874
+ input.task_id,
7875
+ agentId
7876
+ );
7877
+ if (!task) {
7878
+ 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.`;
7879
+ }
7880
+ 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}".
7881
+ ` + JSON.stringify(
7882
+ {
7883
+ task_id: task.id,
7884
+ title: task.title,
7885
+ description: task.description,
7886
+ dependencies: task.dependencies
7887
+ },
7888
+ null,
7889
+ 2
7890
+ );
7891
+ },
7892
+ {
7893
+ name: "claim_task",
7894
+ 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.",
7895
+ schema: z37.object({
7896
+ task_id: z37.string().describe("ID of the task to claim (e.g. task-01). Use check_tasks to see IDs.")
7897
+ })
7898
+ }
7899
+ );
7900
+ const completeTaskTool = tool36(
7901
+ async (input) => {
7902
+ const task = await taskListStore.completeTask(
7903
+ teamId,
7904
+ input.task_id,
7905
+ input.result
7906
+ );
7907
+ if (!task) {
7908
+ return `Task ${input.task_id} not found.`;
7909
+ }
7910
+ await mailboxStore.broadcastMessage(
7911
+ teamId,
7912
+ agentId,
7913
+ `Completed task ${task.id} "${task.title}": ${input.result}`,
7914
+ "broadcast" /* BROADCAST */
7915
+ );
7916
+ return `Task ${task.id} "${task.title}" marked as completed.`;
7917
+ },
7918
+ {
7919
+ name: "complete_task",
7920
+ description: "Mark a claimed task as completed with a result summary. Call this after you have finished working on a task.",
7921
+ schema: z37.object({
7922
+ task_id: z37.string().describe("ID of the task to complete"),
7923
+ result: z37.string().describe("Summary of the task result")
7924
+ })
7925
+ }
7926
+ );
7927
+ const failTaskTool = tool36(
7928
+ async (input) => {
7929
+ const task = await taskListStore.failTask(
7930
+ teamId,
7931
+ input.task_id,
7932
+ input.error
7933
+ );
7934
+ if (!task) {
7935
+ return `Task ${input.task_id} not found.`;
7936
+ }
7937
+ await mailboxStore.broadcastMessage(
7938
+ teamId,
7939
+ agentId,
7940
+ `Failed task ${task.id} "${task.title}": ${input.error}`,
7941
+ "broadcast" /* BROADCAST */
7942
+ );
7943
+ return `Task ${task.id} "${task.title}" marked as failed: ${input.error}`;
7944
+ },
7945
+ {
7946
+ name: "fail_task",
7947
+ description: "Mark a claimed task as failed with an error description. Call this if you cannot complete the task.",
7948
+ schema: z37.object({
7949
+ task_id: z37.string().describe("ID of the task to fail"),
7950
+ error: z37.string().describe("Description of why the task failed")
7951
+ })
7952
+ }
7953
+ );
7954
+ const sendMessageTool = tool36(
7955
+ async (input) => {
7956
+ await mailboxStore.sendMessage(
7957
+ teamId,
7958
+ agentId,
7959
+ input.to,
7960
+ input.content,
7961
+ "direct_message" /* DIRECT_MESSAGE */
7962
+ );
7963
+ return `Message sent to ${input.to}.`;
7964
+ },
7965
+ {
7966
+ name: "send_message",
7967
+ 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.',
7968
+ schema: z37.object({
7969
+ to: z37.string().describe(
7970
+ 'Recipient agent name (e.g. "team_lead" or a teammate name)'
7971
+ ),
7972
+ content: z37.string().describe("Message content")
7973
+ })
7974
+ }
7975
+ );
7976
+ const READ_MESSAGES_TIMEOUT_MS2 = 18e4;
7977
+ const getRelevantMessagesForState = async () => {
7978
+ const allMsgs = await mailboxStore.getAllTeamMessages(teamId);
7979
+ const relevantMsgs = allMsgs.filter(
7980
+ (msg) => msg.from === agentId || msg.to === agentId
7981
+ );
7982
+ return relevantMsgs.map((msg) => ({
7983
+ id: msg.id,
7984
+ from: msg.from,
7985
+ to: msg.to,
7986
+ content: msg.content,
7987
+ timestamp: msg.timestamp instanceof Date ? msg.timestamp.toISOString() : msg.timestamp,
7988
+ type: msg.type,
7989
+ read: msg.read
7990
+ }));
7991
+ };
7992
+ const readMessagesTool = tool36(
7993
+ async (input, config) => {
7994
+ const formatAndMarkAsRead = async (msgs2) => {
7995
+ for (const msg of msgs2) {
7996
+ await mailboxStore.markAsRead(teamId, agentId, msg.id);
7997
+ }
7998
+ return formatMessagesAsMarkdown(msgs2);
7999
+ };
8000
+ let msgs = await mailboxStore.getUnreadMessages(teamId, agentId);
8001
+ if (msgs.length > 0) {
8002
+ const formatted2 = await formatAndMarkAsRead(msgs);
8003
+ const relevantMsgs2 = await getRelevantMessagesForState();
8004
+ const toolMessage2 = new ToolMessage5({
8005
+ content: formatted2,
8006
+ tool_call_id: config.toolCall?.id,
8007
+ name: "read_messages"
8008
+ });
8009
+ return new Command4({
8010
+ update: { team_mailbox: relevantMsgs2, messages: [toolMessage2] }
8011
+ });
8012
+ }
8013
+ const messagePromise = new Promise((resolve3) => {
8014
+ const handler = async (msg) => {
8015
+ mailboxStore.offMessage(teamId, agentId, handler);
8016
+ const allMsgs = await mailboxStore.getUnreadMessages(teamId, agentId);
8017
+ resolve3(allMsgs);
8018
+ };
8019
+ mailboxStore.onMessage(teamId, agentId, handler);
8020
+ });
8021
+ const timeoutPromise = new Promise((resolve3) => {
8022
+ setTimeout(() => resolve3([]), READ_MESSAGES_TIMEOUT_MS2);
8023
+ });
8024
+ msgs = await Promise.race([messagePromise, timeoutPromise]);
8025
+ mailboxStore.offMessage(teamId, agentId, () => {
8026
+ });
8027
+ const relevantMsgs = await getRelevantMessagesForState();
8028
+ if (msgs.length === 0) {
8029
+ const toolMessage2 = new ToolMessage5({
8030
+ content: "No unread messages.",
8031
+ tool_call_id: config.toolCall?.id,
8032
+ name: "read_messages"
8033
+ });
8034
+ return new Command4({
8035
+ update: { team_mailbox: relevantMsgs, messages: [toolMessage2] }
8036
+ });
8037
+ }
8038
+ const formatted = await formatAndMarkAsRead(msgs);
8039
+ const toolMessage = new ToolMessage5({
8040
+ content: formatted,
8041
+ tool_call_id: config.toolCall?.id,
8042
+ name: "read_messages"
8043
+ });
8044
+ return new Command4({
8045
+ update: { team_mailbox: relevantMsgs, messages: [toolMessage] }
8046
+ });
8047
+ },
8048
+ {
8049
+ name: "read_messages",
8050
+ description: "Read unread messages from the mailbox. Returns immediately if messages exist, otherwise waits for up to 3 minutes for new messages.",
8051
+ schema: z37.object({})
8052
+ }
8053
+ );
8054
+ const checkTasksTool = tool36(
8055
+ async () => {
8056
+ const tasks = await taskListStore.getAllTasks(teamId);
8057
+ return formatTaskSummary(tasks);
8058
+ },
8059
+ {
8060
+ name: "check_tasks",
8061
+ description: "Use this tool to get the current status of all tasks in a team. This is your primary way to monitor task progress.",
8062
+ schema: z37.object({})
8063
+ }
8064
+ );
8065
+ const broadcastMessageTool = tool36(
8066
+ async (input) => {
8067
+ const allAgents = await mailboxStore.getRegisteredAgents(teamId);
8068
+ const recipients = allAgents.filter((a) => a !== agentId);
8069
+ const promises = recipients.map(
8070
+ (recipient) => mailboxStore.sendMessage(
8071
+ teamId,
8072
+ agentId,
8073
+ recipient,
8074
+ input.content,
8075
+ "broadcast" /* BROADCAST */
8076
+ )
8077
+ );
8078
+ await Promise.all(promises);
8079
+ return `Broadcast message sent to ${recipients.length} recipient(s): ${recipients.join(", ")}`;
8080
+ },
8081
+ {
8082
+ name: "broadcast_message",
8083
+ 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.",
8084
+ schema: z37.object({
8085
+ content: z37.string().describe("Message content to broadcast to others")
8086
+ })
8087
+ }
8088
+ );
8089
+ return [
8090
+ claimTaskTool,
8091
+ completeTaskTool,
8092
+ failTaskTool,
8093
+ sendMessageTool,
8094
+ readMessagesTool,
8095
+ checkTasksTool,
8096
+ broadcastMessageTool
8097
+ ];
8098
+ }
5669
8099
 
5670
- 4. **Task Breakdown**:
5671
- - Create specific, actionable items
5672
- - Break complex tasks into smaller, manageable steps
5673
- - Use clear, descriptive task names
8100
+ // src/agent_team/middleware/team.ts
8101
+ var TEAM_LEAD_AGENT_ID = "team_lead";
8102
+ var DEFAULT_POLL_INTERVAL_MS = 5e3;
8103
+ var DEFAULT_SCHEDULE_LATTICE_KEY = "default";
8104
+ var READ_MESSAGES_TIMEOUT_MS = 18e4;
8105
+ var TEAM_COMMUNICATION_NORMS = `
8106
+ ### Team communication norms
8107
+
8108
+ - **Roles**: One \`team_lead\` coordinates the work; all others are teammates. Teammates report to team_lead and collaborate with each other as needed.
8109
+ - **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.
8110
+ - **Teammate \u2192 all**: When a teammate needs to share something with the whole team, use \`broadcast_message\`.
8111
+ - **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).
8112
+ - **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.
8113
+ - **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\`.
8114
+ - **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.`;
8115
+ var TEAM_SYSTEM_PROMPT = `## Agent Team
8116
+
8117
+ You are the team_lead. You have tools to manage a team of specialized agents that work in parallel:
8118
+
8119
+ - \`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.
8120
+ - \`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.
8121
+ - \`assign_task\`: Set a task's assignee (reassign work to a specific teammate).
8122
+ - \`set_task_status\`: Set a task's status (pending, claimed, in_progress, completed, failed). Use to reopen a task or mark it failed.
8123
+ - \`set_task_dependencies\`: Set which task IDs must complete before this task can be claimed.
8124
+ - \`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.
8125
+ - \`send_message\`: Send a message to a specific teammate (one-to-one).
8126
+ - \`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.
8127
+ - \`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.
8128
+ - \`disband_team\`: Disband the team when all work is done. This notifies all teammates and cleans up resources.
8129
+ ${TEAM_COMMUNICATION_NORMS}
8130
+
8131
+ ### Workflow
8132
+
8133
+ 1. Analyze the user's request and plan the initial tasks + which teammates to assign.
8134
+ 2. Call \`create_team\` to create the team with tasks.
8135
+ 3. **Request execution plans**: After creating the team, use \`broadcast_message\` once to tell all teammates: "Please submit your execution plan for review."
8136
+ 4. **Wait for all plans**: Use \`read_messages\` until ALL teammates have submitted their plans. Do not proceed until every teammate has submitted.
8137
+ 5. **Add plans to team tasks and assign to teammates (required)**:
8138
+ - 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.
8139
+ - 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).
8140
+ - 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.
8141
+ - 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.").
8142
+ - 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.
8143
+ 6. **Monitor execution**: Once approved, teammates will start working. Periodically call \`check_tasks\` and \`read_messages\` to monitor progress.
8144
+ 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.
8145
+ 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\`.
8146
+ 9. Report status to the user based on \`check_tasks\` results. Proceed to final synthesis when all tasks are done.
8147
+ 10. **Cleanup \u2014 only after verifying task state and team alignment**: Before shutting down the team:
8148
+ - Call \`check_tasks\` to get the current task state. If any tasks are still **pending**, **claimed**, or **in_progress**, do **not** broadcast shutdown yet.
8149
+ - 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?
8150
+ - If the team agrees to continue: let them work until tasks are done, then re-check with \`check_tasks\`.
8151
+ - 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.
8152
+ - 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\`.
8153
+
8154
+ ### Important Notes
8155
+
8156
+ - The teammates you specify in \`create_team\` are created dynamically from name, role, and description.
8157
+ - Tasks can have dependencies -- a task won't be claimable until all its dependency tasks are completed.
8158
+ - You can add tasks and communicate with teammates at any time, even while they are working.
8159
+ - **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.
8160
+ - **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\`.
8161
+ - **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.
8162
+ - **Permissions**: All teammates inherit your permission settings.
8163
+ - **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.`;
8164
+ function formatTaskSummary(tasks) {
8165
+ if (tasks.length === 0) return "No tasks in the task list.";
8166
+ const statusCounts = {};
8167
+ for (const t of tasks) {
8168
+ statusCounts[t.status] = (statusCounts[t.status] || 0) + 1;
8169
+ }
8170
+ const taskBlocks = tasks.map((t) => {
8171
+ let block = `## ${t.id}
8172
+ `;
8173
+ block += `- **Status**: ${t.status.toUpperCase()}
8174
+ `;
8175
+ block += `- **Title**: ${t.title}
8176
+ `;
8177
+ if (t.description) block += `- **Description**: ${t.description}
8178
+ `;
8179
+ if (t.assignee) block += `- **Assignee**: ${t.assignee}
8180
+ `;
8181
+ if (t.dependencies.length > 0) block += `- **Dependencies**: ${t.dependencies.join(", ")}
8182
+ `;
8183
+ if (t.result) block += `- **Result**: ${t.result.slice(0, 500)}
8184
+ `;
8185
+ if (t.error) block += `- **Error**: ${t.error.slice(0, 500)}
8186
+ `;
8187
+ return block;
8188
+ });
8189
+ const summary = Object.entries(statusCounts).map(([s, c]) => `- ${s}: ${c}`).join(" | ");
8190
+ return `${taskBlocks.join("\n---\n\n")}
5674
8191
 
5675
- Being proactive with task management demonstrates attentiveness and ensures you complete all requirements successfully
5676
- 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.`;
5677
- var TODO_LIST_MIDDLEWARE_SYSTEM_PROMPT = `## \`write_todos\`
8192
+ ## Summary
8193
+ ${summary}`;
8194
+ }
8195
+ function getTeammateAssistantId(teamId, agentName) {
8196
+ return `team:${teamId}:${agentName}`;
8197
+ }
8198
+ async function getOrCreateTeammateAgent(ctx, spec, teamTools, teamMiddleware) {
8199
+ const cacheKey = `${ctx.teamId}:${spec.name}`;
8200
+ let agent = ctx.agentCache.get(cacheKey);
8201
+ if (agent) return agent;
8202
+ const builtinTeammateTools = createTeammateTools({
8203
+ teamId: ctx.teamId,
8204
+ agentId: spec.name,
8205
+ taskListStore: ctx.taskListStore,
8206
+ mailboxStore: ctx.mailboxStore
8207
+ });
8208
+ const allTools = [...teamTools ?? [], ...spec.tools ?? [], ...builtinTeammateTools];
8209
+ const others = (ctx.allTeammateSpecs ?? []).filter((s) => s.name !== spec.name).map((s) => `${s.name} (role: ${s.role}${s.description ? `; ${s.description}` : ""})`);
8210
+ const teammatesBlock = others.length > 0 ? `
8211
+
8212
+ Your teammates in this team (you can \`send_message\` to them):
8213
+ - team_lead (lead)
8214
+ ${others.map((o) => `- ${o}`).join("\n")}
8215
+ ` : "\n\nYour teammates in this team: team_lead (lead).\n";
8216
+ 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}
8217
+ ${TEAM_COMMUNICATION_NORMS}
8218
+
8219
+ Your job:
8220
+ 1. First, call \`check_tasks\` to see all tasks in the team.
8221
+ 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.
8222
+ 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.
8223
+ 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.
8224
+ 5. Use your tools to complete the task thoroughly.
8225
+ 6. Call \`complete_task\` with a summary of your result, or \`fail_task\` if you cannot complete it.
8226
+ 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).
8227
+ 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.
8228
+ 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.
8229
+ 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"):
8230
+ - **Do NOT shut down immediately.** First call \`check_tasks\` to check the current task state.
8231
+ - 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?"
8232
+ - Wait for team alignment (from \`read_messages\`): either (a) continue working until tasks are done, or (b) team_lead confirms tasks are stopped/cancelled.
8233
+ - 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.
8234
+
8235
+ Important workflow:
8236
+ - NEVER start working on tasks before the team_lead has approved (you will see "Plan approved" or similar in \`read_messages\`)
8237
+ - Always submit your plan first via \`send_message\` to "team_lead"
8238
+ - If you need new tasks, notify team_lead via \`send_message\` to "team_lead"; only the lead adds tasks
8239
+ - If you receive feedback on your plan, revise and resubmit to team_lead
8240
+
8241
+ You have access to these tools:
8242
+ - \`claim_task\`: Pick a task by task_id to claim it (sets assignee to you; ONLY after plan is approved). Use check_tasks first.
8243
+ - \`complete_task\`: Mark a task as completed
8244
+ - \`fail_task\`: Mark a task as failed
8245
+ - \`send_message\`: Send a message to team_lead or a specific teammate
8246
+ - \`broadcast_message\`: Send a message to everyone in the team except yourself (use when sharing with the whole team)
8247
+ - \`read_messages\`: Read messages from team_lead or teammates
8248
+ - \`check_tasks\`: Get current status of all tasks in the team`;
8249
+ const assistantId = getTeammateAssistantId(ctx.teamId, spec.name);
8250
+ agent = createAgent4({
8251
+ model: spec.model ?? ctx.defaultModel,
8252
+ systemPrompt: teammatePrompt,
8253
+ tools: allTools,
8254
+ description: spec.description,
8255
+ middleware: [...teamMiddleware ?? [], ...spec.middleware ?? []],
8256
+ checkpointer: getCheckpointSaver("default")
8257
+ });
8258
+ registerTeammateAgent(assistantId, agent);
8259
+ const teammateThreadId = ctx.parentThreadId ? `${ctx.parentThreadId}____${ctx.teamId}_${spec.name}` : `standalone_${ctx.teamId}_${spec.name}`;
8260
+ const startMessage = "Start working";
8261
+ try {
8262
+ await agentWorkerGraph.invoke({
8263
+ assistant_id: assistantId,
8264
+ thread_id: teammateThreadId,
8265
+ input: { message: startMessage, messages: [{ role: "human", content: startMessage }] }
8266
+ });
8267
+ } catch (err) {
8268
+ const errorMsg = err instanceof Error ? err.message : "Unknown error during teammate execution";
8269
+ await ctx.mailboxStore.sendMessage(
8270
+ ctx.teamId,
8271
+ spec.name,
8272
+ TEAM_LEAD_AGENT_ID,
8273
+ `Teammate execution failed: ${errorMsg}`,
8274
+ "status_update" /* STATUS_UPDATE */
8275
+ );
8276
+ }
8277
+ return agent;
8278
+ }
8279
+ async function spawnTeammate(options) {
8280
+ const {
8281
+ teamId,
8282
+ spec,
8283
+ allTeammateSpecs,
8284
+ taskListStore,
8285
+ mailboxStore,
8286
+ defaultModel,
8287
+ parentThreadId = "",
8288
+ teamTools,
8289
+ teamMiddleware
8290
+ } = options;
8291
+ const ctx = {
8292
+ teamId,
8293
+ parentThreadId,
8294
+ taskListStore,
8295
+ mailboxStore,
8296
+ teammates: /* @__PURE__ */ new Map(),
8297
+ allTeammateSpecs,
8298
+ defaultModel,
8299
+ pollIntervalMs: DEFAULT_POLL_INTERVAL_MS,
8300
+ scheduleLatticeKey: DEFAULT_SCHEDULE_LATTICE_KEY,
8301
+ agentCache: /* @__PURE__ */ new Map()
8302
+ };
8303
+ await getOrCreateTeammateAgent(ctx, spec, teamTools, teamMiddleware);
8304
+ }
8305
+ function createTeamMiddleware(options) {
8306
+ const { teamConfig, taskListStore, mailboxStore } = options;
8307
+ const defaultModel = teamConfig.model ?? "claude-sonnet-4-5-20250929";
8308
+ const createTeamTool = tool37(
8309
+ async (input, config) => {
8310
+ const state = getCurrentTaskInput3();
8311
+ if (state?.team?.teamId) {
8312
+ const existingId = state.team.teamId;
8313
+ const msg = new ToolMessage6({
8314
+ 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.`,
8315
+ tool_call_id: config.toolCall?.id,
8316
+ name: "create_team"
8317
+ });
8318
+ return msg;
8319
+ }
8320
+ const teamId = uuidv4();
8321
+ const createdTasks = await taskListStore.addTasks(
8322
+ teamId,
8323
+ input.tasks.map((t) => ({
8324
+ id: t.id,
8325
+ title: t.title,
8326
+ description: t.description,
8327
+ assignee: t.assignee,
8328
+ dependencies: t.dependencies ?? []
8329
+ }))
8330
+ );
8331
+ const matchedSpecs = [];
8332
+ const unmatchedNames = [];
8333
+ for (const req of input.teammates) {
8334
+ const spec = teamConfig.teammates.find(
8335
+ (s) => s.name === req.name || s.role === req.role
8336
+ );
8337
+ if (spec) {
8338
+ matchedSpecs.push(spec);
8339
+ } else {
8340
+ matchedSpecs.push({
8341
+ name: req.name,
8342
+ role: req.role,
8343
+ description: req.description
8344
+ });
8345
+ unmatchedNames.push(req.name);
8346
+ }
8347
+ }
8348
+ await mailboxStore.registerAgent(teamId, TEAM_LEAD_AGENT_ID);
8349
+ for (const spec of matchedSpecs) {
8350
+ await mailboxStore.registerAgent(teamId, spec.name);
8351
+ }
8352
+ const parentThreadId = config.configurable?.thread_id ?? "";
8353
+ matchedSpecs.forEach((spec) => {
8354
+ void spawnTeammate({
8355
+ teamId,
8356
+ spec,
8357
+ allTeammateSpecs: matchedSpecs,
8358
+ taskListStore,
8359
+ mailboxStore,
8360
+ defaultModel,
8361
+ parentThreadId,
8362
+ teamTools: teamConfig.tools,
8363
+ teamMiddleware: teamConfig.middleware
8364
+ });
8365
+ });
8366
+ const teamJson = JSON.stringify({
8367
+ teamId,
8368
+ teamLeadId: TEAM_LEAD_AGENT_ID,
8369
+ teammates: matchedSpecs.map((s) => ({
8370
+ name: s.name,
8371
+ role: s.role,
8372
+ description: s.description
8373
+ })),
8374
+ tasks: createdTasks.map((t) => ({
8375
+ id: t.id,
8376
+ title: t.title,
8377
+ description: t.description,
8378
+ status: t.status
8379
+ }))
8380
+ }, null, 2);
8381
+ let summary = `Team created (id: ${teamId}):
8382
+ `;
8383
+ summary += `- ${matchedSpecs.length} teammate(s): ${matchedSpecs.map((s) => s.name).join(", ")}
8384
+ `;
8385
+ summary += `- ${createdTasks.length} initial task(s): ${createdTasks.map((t) => `"${t.title}"`).join(", ")}
8386
+ `;
8387
+ if (unmatchedNames.length > 0) {
8388
+ summary += `- Teammates created with default prompts and tools.
8389
+ `;
8390
+ }
8391
+ summary += `
8392
+ Teammates are now working in the background. Keep calling \`check_tasks\` and \`read_messages\` until all tasks are completed or failed.`;
8393
+ summary += `
5678
8394
 
5679
- You have access to the \`write_todos\` tool to help you manage and plan complex objectives.
5680
- Use this tool for complex objectives to ensure that you are tracking each necessary step and giving the user visibility into your progress.
5681
- This tool is very helpful for planning complex objectives, and for breaking down these larger complex objectives into smaller steps.
8395
+ \`\`\`json
8396
+ ${teamJson}
8397
+ \`\`\``;
8398
+ const toolMessage = new ToolMessage6({
8399
+ content: summary,
8400
+ tool_call_id: config.toolCall?.id,
8401
+ name: "create_team"
8402
+ });
8403
+ const teamState = {
8404
+ teamId,
8405
+ teamLeadId: TEAM_LEAD_AGENT_ID,
8406
+ teammates: matchedSpecs.map((s) => ({
8407
+ name: s.name,
8408
+ role: s.role,
8409
+ description: s.description
8410
+ })),
8411
+ tasks: createdTasks.map((t) => ({
8412
+ id: t.id,
8413
+ title: t.title,
8414
+ description: t.description,
8415
+ status: t.status
8416
+ })),
8417
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
8418
+ };
8419
+ return new Command5({
8420
+ update: { team: teamState, messages: [toolMessage] }
8421
+ });
8422
+ },
8423
+ {
8424
+ name: "create_team",
8425
+ 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.
5682
8426
 
5683
- 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.
5684
- For simple objectives that only require a few steps, it is better to just complete the objective directly and NOT use this tool.
5685
- 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.
8427
+ When to Use This Tool
8428
+ Use this tool in these scenarios:
5686
8429
 
5687
- ## Important To-Do List Usage Notes to Remember
5688
- - The \`write_todos\` tool should never be called multiple times in parallel.
5689
- - 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.`;
5690
- var TodoStatus = z36.enum(["pending", "in_progress", "completed"]).describe("Status of the todo");
5691
- var TodoSchema = z36.object({
5692
- content: z36.string().describe("Content of the todo item"),
5693
- status: TodoStatus
5694
- });
5695
- var stateSchema = z36.object({ todos: z36.array(TodoSchema).default([]) });
5696
- function todoListMiddleware(options) {
5697
- const writeTodos = tool35(
5698
- ({ todos }, config) => {
5699
- return new Command3({
8430
+ Complex multi-step objectives - When the user's goal requires breaking into multiple tasks that can be executed in parallel or sequence
8431
+ Parallel work needed - When multiple independent subtasks can be done simultaneously by different teammates
8432
+ Diverse expertise required - When the objective needs different skills (research, writing, coding, review, etc.)
8433
+ Long-running tasks - When tasks may take time and you want to monitor progress without blocking
8434
+
8435
+ When NOT to Use This Tool
8436
+ Skip using this tool when:
8437
+
8438
+ Simple single task - The objective can be completed in one straightforward step
8439
+ Already have an active team - A team is already running for this session. Use check_tasks and read_messages to monitor instead
8440
+ Quick question or simple request - No need to create a team for trivial tasks
8441
+
8442
+ IMPORTANT: Task ID Format
8443
+ - Task IDs MUST follow the format: task-01, task-02, task-03, etc.
8444
+ - Each task needs a unique ID starting from task-01
8445
+ - Use sequential IDs to help track task order and dependencies
8446
+ - Example: { id: "task-01", title: "Research topic", description: "..." }
8447
+
8448
+ IMPORTANT: Dependencies
8449
+ - Use dependencies to express which tasks must complete before others can start
8450
+ - Dependencies should reference task IDs (e.g., ["task-01"])
8451
+ - Tasks without dependencies can be worked on immediately in parallel
8452
+
8453
+ IMPORTANT: After Creating Team
8454
+ After calling create_team, you MUST:
8455
+ 1. Use read_messages to receive updates from teammates (it waits up to 3 minutes for new messages)
8456
+ 2. When messages indicate task changes, call check_tasks to get full task status
8457
+ 3. Continue until all tasks show "completed" or "failed"
8458
+ 4. Do NOT assume tasks are done - always verify with check_tasks`,
8459
+ schema: z38.object({
8460
+ tasks: z38.array(
8461
+ z38.object({
8462
+ id: z38.string().describe("Task ID in format task-01, task-02, etc."),
8463
+ title: z38.string().describe("Short task title"),
8464
+ description: z38.string().describe("Detailed task description - what exactly needs to be done"),
8465
+ dependencies: z38.array(z38.string()).optional().default([]).describe('Array of task IDs that must complete before this task (e.g. ["task-01"])')
8466
+ })
8467
+ ).describe("List of tasks for teammates to work on. Each task needs unique ID (task-01, task-02, etc.)."),
8468
+ teammates: z38.array(
8469
+ z38.object({
8470
+ name: z38.string().describe("Teammate name (must match a pre-configured teammate type)"),
8471
+ role: z38.string().describe("Role category (e.g. researcher, writer, coder, reviewer)"),
8472
+ description: z38.string().describe("What this teammate will focus on - specific instructions for their work")
8473
+ })
8474
+ ).describe("Teammate agents to create. Each should have a clear role and focus.")
8475
+ })
8476
+ }
8477
+ );
8478
+ const resolveTeamId = () => {
8479
+ const state = getCurrentTaskInput3();
8480
+ if (state?.team?.teamId) return state.team.teamId;
8481
+ throw new Error("No team_id provided and no team in state. Call create_team first.");
8482
+ };
8483
+ const addTasksTool = tool37(
8484
+ async (input, config) => {
8485
+ const teamId = resolveTeamId();
8486
+ const created = await taskListStore.addTasks(
8487
+ teamId,
8488
+ input.tasks.map((t) => ({
8489
+ id: t.id,
8490
+ title: t.title,
8491
+ description: t.description,
8492
+ assignee: t.assignee,
8493
+ dependencies: t.dependencies ?? []
8494
+ }))
8495
+ );
8496
+ const summary = created.map((t) => `- ${t.id}: "${t.title}"`).join("\n");
8497
+ return new ToolMessage6({
8498
+ content: `Added ${created.length} task(s) to team ${teamId}:
8499
+ ${summary}
8500
+ Sleeping teammates will wake up and claim these.`,
8501
+ tool_call_id: config.toolCall?.id,
8502
+ name: "add_tasks"
8503
+ });
8504
+ },
8505
+ {
8506
+ name: "add_tasks",
8507
+ description: `Use this tool to add new tasks to an existing team's task list.
8508
+
8509
+ When to Use This Tool
8510
+ Use this tool when:
8511
+
8512
+ User requests additional work - After the initial team was created, the user asks for more tasks
8513
+ Teammate feedback reveals new needs - A teammate's message indicates additional tasks are needed
8514
+ Scope expansion - The original objective grows and requires more subtasks
8515
+ Follow-up tasks discovered - While working, teammates discover tasks that need to be done
8516
+
8517
+ When NOT to Use This Tool
8518
+ Skip using this tool when:
8519
+
8520
+ No team exists - Use create_team first to create a team
8521
+ Simple message response - Just respond to teammates via send_message
8522
+ Task modification needed - Use assign_task, set_task_status, or set_task_dependencies to change existing tasks instead
8523
+
8524
+ IMPORTANT: Task ID Format
8525
+ - New task IDs must be unique and follow format: task-01, task-02, etc.
8526
+ - If the team already has tasks (e.g., task-01, task-02), continue the sequence (task-03, task-04)
8527
+ - Check existing tasks first using check_tasks to determine the next ID
8528
+
8529
+ IMPORTANT: Dependencies
8530
+ - Use dependencies to specify which existing tasks must complete before new tasks can start
8531
+ - Reference task IDs (e.g., ["task-01", "task-02"])
8532
+
8533
+ IMPORTANT: Assigning to a specific teammate
8534
+ - 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.`,
8535
+ schema: z38.object({
8536
+ tasks: z38.array(
8537
+ z38.object({
8538
+ id: z38.string().describe("Task ID in format task-01, task-02, etc. Must be unique."),
8539
+ title: z38.string().describe("Short task title"),
8540
+ description: z38.string().describe("Detailed task description - what needs to be done"),
8541
+ assignee: z38.string().optional().describe("Teammate name to assign this task to (use when you need that person to do the work)"),
8542
+ dependencies: z38.array(z38.string()).optional().default([]).describe("Array of task IDs that must complete before this task")
8543
+ })
8544
+ ).describe("New tasks to add to the team")
8545
+ })
8546
+ }
8547
+ );
8548
+ const assignTaskTool = tool37(
8549
+ async (input, config) => {
8550
+ const teamId = resolveTeamId();
8551
+ const task = await taskListStore.updateTask(teamId, input.task_id, {
8552
+ assignee: input.assignee
8553
+ });
8554
+ if (!task) {
8555
+ return new ToolMessage6({
8556
+ content: `Task ${input.task_id} not found in team ${teamId}.`,
8557
+ tool_call_id: config.toolCall?.id,
8558
+ name: "assign_task"
8559
+ });
8560
+ }
8561
+ return new ToolMessage6({
8562
+ content: `Task "${task.title}" (${task.id}) assigned to ${input.assignee}.`,
8563
+ tool_call_id: config.toolCall?.id,
8564
+ name: "assign_task"
8565
+ });
8566
+ },
8567
+ {
8568
+ name: "assign_task",
8569
+ 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.",
8570
+ schema: z38.object({
8571
+ task_id: z38.string().describe("Task ID to assign"),
8572
+ assignee: z38.string().describe("Teammate name to assign this task to")
8573
+ })
8574
+ }
8575
+ );
8576
+ const setTaskStatusTool = tool37(
8577
+ async (input, config) => {
8578
+ const teamId = resolveTeamId();
8579
+ const task = await taskListStore.updateTask(teamId, input.task_id, {
8580
+ status: input.status
8581
+ });
8582
+ if (!task) {
8583
+ return new ToolMessage6({
8584
+ content: `Task ${input.task_id} not found in team ${teamId}.`,
8585
+ tool_call_id: config.toolCall?.id,
8586
+ name: "set_task_status"
8587
+ });
8588
+ }
8589
+ return new ToolMessage6({
8590
+ content: `Task "${task.title}" (${task.id}) status set to ${input.status}.`,
8591
+ tool_call_id: config.toolCall?.id,
8592
+ name: "set_task_status"
8593
+ });
8594
+ },
8595
+ {
8596
+ name: "set_task_status",
8597
+ 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.",
8598
+ schema: z38.object({
8599
+ task_id: z38.string().describe("Task ID to update"),
8600
+ status: z38.enum(["pending", "claimed", "in_progress", "completed", "failed"]).describe("New status for the task")
8601
+ })
8602
+ }
8603
+ );
8604
+ const setTaskDependenciesTool = tool37(
8605
+ async (input, config) => {
8606
+ const teamId = resolveTeamId();
8607
+ const task = await taskListStore.updateTask(teamId, input.task_id, {
8608
+ dependencies: input.dependencies
8609
+ });
8610
+ if (!task) {
8611
+ return new ToolMessage6({
8612
+ content: `Task ${input.task_id} not found in team ${teamId}.`,
8613
+ tool_call_id: config.toolCall?.id,
8614
+ name: "set_task_dependencies"
8615
+ });
8616
+ }
8617
+ return new ToolMessage6({
8618
+ content: `Task "${task.title}" (${task.id}) dependencies set to [${input.dependencies.join(", ")}].`,
8619
+ tool_call_id: config.toolCall?.id,
8620
+ name: "set_task_dependencies"
8621
+ });
8622
+ },
8623
+ {
8624
+ name: "set_task_dependencies",
8625
+ 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.',
8626
+ schema: z38.object({
8627
+ task_id: z38.string().describe("Task ID to update"),
8628
+ dependencies: z38.array(z38.string()).describe("Task IDs that must complete before this task can be claimed")
8629
+ })
8630
+ }
8631
+ );
8632
+ const checkTasksTool = tool37(
8633
+ async (input, config) => {
8634
+ const teamId = resolveTeamId();
8635
+ const tasks = await taskListStore.getAllTasks(teamId);
8636
+ const tasksSnapshot = tasks;
8637
+ return new Command5({
5700
8638
  update: {
5701
- todos,
5702
- messages: [
5703
- new ToolMessage4({
5704
- content: genUIMarkdown("todo_list", todos),
5705
- tool_call_id: config.toolCall?.id
8639
+ tasks: tasksSnapshot,
8640
+ messages: [
8641
+ new ToolMessage6({
8642
+ content: formatTaskSummary(tasks),
8643
+ tool_call_id: config.toolCall?.id,
8644
+ name: "check_tasks"
5706
8645
  })
5707
8646
  ]
5708
8647
  }
5709
8648
  });
5710
8649
  },
5711
8650
  {
5712
- name: "write_todos",
5713
- description: options?.toolDescription ?? WRITE_TODOS_DESCRIPTION,
5714
- schema: z36.object({
5715
- todos: z36.array(TodoSchema).describe("List of todo items to update")
8651
+ name: "check_tasks",
8652
+ description: `Use this tool to get the current status of all tasks in a team. This is your primary way to monitor teammate progress.
8653
+
8654
+ When to Use This Tool
8655
+ Use this tool:
8656
+
8657
+ After creating a team - Check task status after create_team to verify tasks are being worked on
8658
+ When read_messages indicates task changes - Teammates report status via messages; call check_tasks to get full task list details
8659
+ Before final synthesis - Verify all tasks are complete before reporting results to user
8660
+
8661
+ IMPORTANT: How to Monitor
8662
+ 1. Use read_messages to wait for updates from teammates (it polls internally).
8663
+ 2. When you receive messages suggesting task progress or completion, call check_tasks to get the current task status.
8664
+ 3. Look at each task's status: pending, in_progress, completed, or failed.
8665
+ 4. NEVER assume tasks are done - always verify with check_tasks.
8666
+
8667
+ Task Status Values:
8668
+ - pending: Task created but not yet claimed by any teammate
8669
+ - in_progress: Teammate is actively working on this task
8670
+ - completed: Task finished successfully
8671
+ - failed: Task encountered an error`,
8672
+ schema: z38.object({
8673
+ team_id: z38.string().optional().describe("Team ID (omit to use active team)")
5716
8674
  })
5717
8675
  }
5718
8676
  );
5719
- return createMiddleware8({
5720
- name: "todoListMiddleware",
5721
- stateSchema,
5722
- tools: [writeTodos],
5723
- wrapModelCall: (request, handler) => handler({
5724
- ...request,
5725
- systemPrompt: (request.systemPrompt ? `${request.systemPrompt}
8677
+ const sendMessageTool = tool37(
8678
+ async (input, config) => {
8679
+ const teamId = resolveTeamId();
8680
+ await mailboxStore.sendMessage(
8681
+ teamId,
8682
+ TEAM_LEAD_AGENT_ID,
8683
+ input.to,
8684
+ input.content,
8685
+ "direct_message" /* DIRECT_MESSAGE */
8686
+ );
8687
+ return new ToolMessage6({
8688
+ content: `Message sent to ${input.to}.`,
8689
+ tool_call_id: config.toolCall?.id,
8690
+ name: "send_message"
8691
+ });
8692
+ },
8693
+ {
8694
+ name: "send_message",
8695
+ description: "Send a message to a specific teammate in the team. Omit team_id to use the active team from state.",
8696
+ schema: z38.object({
8697
+ to: z38.string().describe("Recipient teammate name"),
8698
+ content: z38.string().describe("Message content")
8699
+ })
8700
+ }
8701
+ );
8702
+ const readMessagesTool = tool37(
8703
+ async (input, config) => {
8704
+ const teamId = resolveTeamId();
8705
+ const formatAndMarkAsRead = async (msgs2) => {
8706
+ for (const msg of msgs2) {
8707
+ await mailboxStore.markAsRead(teamId, TEAM_LEAD_AGENT_ID, msg.id);
8708
+ }
8709
+ return formatMessagesAsMarkdown(msgs2);
8710
+ };
8711
+ const getAllTeamMessagesForState = async () => {
8712
+ const allMsgs = await mailboxStore.getAllTeamMessages(teamId);
8713
+ return allMsgs.map((msg) => ({
8714
+ id: msg.id,
8715
+ from: msg.from,
8716
+ to: msg.to,
8717
+ content: msg.content,
8718
+ timestamp: msg.timestamp instanceof Date ? msg.timestamp.toISOString() : msg.timestamp,
8719
+ type: msg.type,
8720
+ read: msg.read
8721
+ }));
8722
+ };
8723
+ let msgs = await mailboxStore.getUnreadMessages(
8724
+ teamId,
8725
+ TEAM_LEAD_AGENT_ID
8726
+ );
8727
+ if (msgs.length > 0) {
8728
+ const formatted2 = await formatAndMarkAsRead(msgs);
8729
+ const allTeamMessages2 = await getAllTeamMessagesForState();
8730
+ const toolMessage2 = new ToolMessage6({
8731
+ content: formatted2,
8732
+ tool_call_id: config.toolCall?.id,
8733
+ name: "read_messages"
8734
+ });
8735
+ return new Command5({
8736
+ update: { team_mailbox: allTeamMessages2, messages: [toolMessage2] }
8737
+ });
8738
+ }
8739
+ const messagePromise = new Promise((resolve3) => {
8740
+ const handler = async (msg) => {
8741
+ mailboxStore.offMessage(teamId, TEAM_LEAD_AGENT_ID, handler);
8742
+ const allMsgs = await mailboxStore.getUnreadMessages(
8743
+ teamId,
8744
+ TEAM_LEAD_AGENT_ID
8745
+ );
8746
+ resolve3(allMsgs);
8747
+ };
8748
+ mailboxStore.onMessage(teamId, TEAM_LEAD_AGENT_ID, handler);
8749
+ });
8750
+ const timeoutPromise = new Promise((resolve3) => {
8751
+ setTimeout(() => resolve3([]), READ_MESSAGES_TIMEOUT_MS);
8752
+ });
8753
+ msgs = await Promise.race([messagePromise, timeoutPromise]);
8754
+ mailboxStore.offMessage(
8755
+ teamId,
8756
+ TEAM_LEAD_AGENT_ID,
8757
+ () => {
8758
+ }
8759
+ );
8760
+ const allTeamMessages = await getAllTeamMessagesForState();
8761
+ if (msgs.length === 0) {
8762
+ const toolMessage2 = new ToolMessage6({
8763
+ content: "No unread messages from teammates.",
8764
+ tool_call_id: config.toolCall?.id,
8765
+ name: "read_messages"
8766
+ });
8767
+ return new Command5({
8768
+ update: { team_mailbox: allTeamMessages, messages: [toolMessage2] }
8769
+ });
8770
+ }
8771
+ const formatted = await formatAndMarkAsRead(msgs);
8772
+ const toolMessage = new ToolMessage6({
8773
+ content: formatted,
8774
+ tool_call_id: config.toolCall?.id,
8775
+ name: "read_messages"
8776
+ });
8777
+ return new Command5({
8778
+ update: { team_mailbox: allTeamMessages, messages: [toolMessage] }
8779
+ });
8780
+ },
8781
+ {
8782
+ name: "read_messages",
8783
+ description: "Read unread messages from teammates. Returns immediately if messages exist, otherwise waits for up to 3 minutes for new messages.",
8784
+ schema: z38.object({
8785
+ team_id: z38.string().optional().describe("Team ID (omit to use active team)")
8786
+ })
8787
+ }
8788
+ );
8789
+ const disbandTeamTool = tool37(
8790
+ async (input, config) => {
8791
+ const teamId = resolveTeamId();
8792
+ await mailboxStore.broadcastMessage(
8793
+ teamId,
8794
+ TEAM_LEAD_AGENT_ID,
8795
+ "All tasks are complete. You may now shut down gracefully. Thank you for your work!",
8796
+ "shutdown_request" /* SHUTDOWN_REQUEST */
8797
+ );
8798
+ await new Promise((r) => setTimeout(r, 2e3));
8799
+ return new ToolMessage6({
8800
+ content: `Team ${teamId} has been disbanded. All teammates notified and resources cleaned up.`,
8801
+ tool_call_id: config.toolCall?.id,
8802
+ name: "disband_team"
8803
+ });
8804
+ },
8805
+ {
8806
+ name: "disband_team",
8807
+ 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."
8808
+ }
8809
+ );
8810
+ const broadcastMessageTool = tool37(
8811
+ async (input, config) => {
8812
+ const teamId = resolveTeamId();
8813
+ await mailboxStore.broadcastMessage(
8814
+ teamId,
8815
+ TEAM_LEAD_AGENT_ID,
8816
+ input.content,
8817
+ "broadcast" /* BROADCAST */
8818
+ );
8819
+ return new ToolMessage6({
8820
+ content: `Broadcast message sent to all teammates.`,
8821
+ tool_call_id: config.toolCall?.id,
8822
+ name: "broadcast_message"
8823
+ });
8824
+ },
8825
+ {
8826
+ name: "broadcast_message",
8827
+ 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.",
8828
+ schema: z38.object({
8829
+ content: z38.string().describe("Message content to broadcast to all teammates")
8830
+ })
8831
+ }
8832
+ );
8833
+ return createMiddleware9({
8834
+ name: "teamMiddleware",
8835
+ tools: [
8836
+ createTeamTool,
8837
+ addTasksTool,
8838
+ assignTaskTool,
8839
+ setTaskStatusTool,
8840
+ setTaskDependenciesTool,
8841
+ checkTasksTool,
8842
+ sendMessageTool,
8843
+ readMessagesTool,
8844
+ disbandTeamTool,
8845
+ broadcastMessageTool
8846
+ ],
8847
+ wrapModelCall: async (request, handler) => {
8848
+ const currentPrompt = request.systemPrompt || "";
8849
+ const newPrompt = currentPrompt ? `${currentPrompt}
5726
8850
 
5727
- ` : "") + (options?.systemPrompt ?? TODO_LIST_MIDDLEWARE_SYSTEM_PROMPT)
5728
- })
8851
+ ${TEAM_SYSTEM_PROMPT}` : TEAM_SYSTEM_PROMPT;
8852
+ return handler({
8853
+ ...request,
8854
+ systemPrompt: newPrompt
8855
+ });
8856
+ }
5729
8857
  });
5730
8858
  }
5731
8859
 
5732
- // src/deep_agent_new/agent.ts
5733
- var BASE_PROMPT = `In order to complete the objective that the user asks of you, you have access to a number of standard tools.`;
5734
- function createDeepAgent(params = {}) {
5735
- const {
5736
- model = "claude-sonnet-4-5-20250929",
5737
- tools = [],
5738
- systemPrompt,
5739
- middleware: customMiddleware = [],
5740
- subagents = [],
5741
- responseFormat,
5742
- contextSchema,
5743
- checkpointer,
5744
- store,
5745
- backend,
5746
- interruptOn,
5747
- name,
5748
- skills
5749
- } = params;
5750
- const finalSystemPrompt = systemPrompt ? `${systemPrompt}
5751
-
5752
- ${BASE_PROMPT}` : BASE_PROMPT;
5753
- const filesystemBackend = backend ? backend : async (config) => new StateBackend(config);
8860
+ // src/agent_team/agent_team.ts
8861
+ var TeammateInfoSchema = z39.object({
8862
+ name: z39.string().describe("Teammate name"),
8863
+ role: z39.string().describe("Role category (e.g. research, writing, review)"),
8864
+ description: z39.string().describe("What this teammate focuses on")
8865
+ });
8866
+ var TeamTaskInfoSchema = z39.object({
8867
+ id: z39.string(),
8868
+ title: z39.string(),
8869
+ description: z39.string(),
8870
+ status: z39.string().optional()
8871
+ });
8872
+ var MailboxMessageSchema = z39.object({
8873
+ id: z39.string().describe("Unique message identifier"),
8874
+ from: z39.string().describe("Sender agent name"),
8875
+ to: z39.string().describe("Recipient agent name"),
8876
+ content: z39.string().describe("Message content"),
8877
+ timestamp: z39.string().describe("ISO timestamp when the message was sent"),
8878
+ type: z39.nativeEnum(MessageType).describe("Message type"),
8879
+ read: z39.boolean().describe("Whether the recipient has read this message")
8880
+ });
8881
+ var TeamInfoSchema = z39.object({
8882
+ teamId: z39.string().describe("Unique team identifier"),
8883
+ teamLeadId: z39.string().default("team_lead").describe("Team lead agent ID"),
8884
+ teammates: z39.array(TeammateInfoSchema).describe("Active teammates in this team"),
8885
+ tasks: z39.array(TeamTaskInfoSchema).optional().describe("Initial tasks snapshot"),
8886
+ createdAt: z39.string().optional().describe("ISO timestamp when team was created")
8887
+ });
8888
+ var TEAM_STATE_SCHEMA = z39.object({
8889
+ team: TeamInfoSchema.optional().describe("Team info: teamId, teamLeadId, teammates, tasks. Set when create_team succeeds."),
8890
+ tasks: z39.array(TeamTaskInfoSchema).optional().describe("Current tasks snapshot from check_tasks. Updated on each check."),
8891
+ team_mailbox: z39.array(MailboxMessageSchema).optional().describe("All team mailbox messages for display")
8892
+ });
8893
+ 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:
8894
+
8895
+ 1. Understand the user's objective and break it into concrete tasks.
8896
+ 2. Create a team by calling \`create_team\` with the tasks and teammate assignments.
8897
+ 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.
8898
+ 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:
8899
+ - Acknowledge their message
8900
+ - If they need guidance, provide it using \`send_message\`
8901
+ - If they reported a problem, address it (add tasks, adjust plan, etc.)
8902
+ - If they made a discovery, incorporate it into your plan
8903
+ Do not just read messages - always respond or take action!
8904
+ 5. **Task assignment**: You can explicitly assign tasks to specific teammates via \`send_message\`, or let them self-claim from available tasks.
8905
+ 6. **Plan approval** (optional): If a teammate needs approval before implementing, wait for their plan and approve/reject with feedback via \`send_message\`.
8906
+ 7. Respond to the user with status updates and final results.
8907
+ 8. When the user asks for changes, use \`add_tasks\`, \`assign_task\`, \`set_task_status\`, or \`set_task_dependencies\` to evolve the task list.
8908
+ 9. When teammates report discoveries or problems, adjust the plan accordingly.
8909
+ 10. **Cleanup**: When all tasks are done, tell teammates to shut down, then clean up the team resources.
8910
+
8911
+ ## Coordination Rules
8912
+
8913
+ - **Single point of contact**: User only talks to YOU - you are the only interface between user and team
8914
+ - **Don't parallel implement**: Do not start working on tasks yourself while teammates are working - wait for them to finish
8915
+ - **Always verify**: Never assume tasks are done - always verify with \`check_tasks\`
8916
+ - **Permissions**: All teammates inherit your permission settings. Adjust individual teammate modes if needed after spawning.
8917
+
8918
+ Always plan tasks with clear, actionable descriptions. Use dependencies to express ordering constraints.`;
8919
+ function createAgentTeam(config) {
8920
+ const taskListStore = config.taskListStore ?? new InMemoryTaskListStore();
8921
+ const mailboxStore = config.mailboxStore ?? new InMemoryMailboxStore();
8922
+ const filesystemMiddleware = createFilesystemMiddleware({
8923
+ backend: config.backend
8924
+ });
8925
+ const allMiddleware = [filesystemMiddleware, ...config.middleware ?? []];
8926
+ const teamConfigWithFs = {
8927
+ ...config,
8928
+ middleware: allMiddleware
8929
+ };
8930
+ const teamMiddleware = createTeamMiddleware({
8931
+ teamConfig: teamConfigWithFs,
8932
+ taskListStore,
8933
+ mailboxStore
8934
+ });
5754
8935
  const middleware = [
5755
- // Provides todo list management capabilities for tracking tasks
5756
- todoListMiddleware(),
5757
- // Enables filesystem operations and optional long-term memory storage
5758
- createFilesystemMiddleware({ backend: filesystemBackend }),
5759
- // Enables delegation to specialized subagents for complex tasks
5760
- createSubAgentMiddleware({
5761
- defaultModel: model,
5762
- defaultTools: tools,
5763
- defaultMiddleware: [
5764
- // Subagent middleware: Todo list management
5765
- todoListMiddleware(),
5766
- // Subagent middleware: Filesystem operations
5767
- createFilesystemMiddleware({
5768
- backend: filesystemBackend
5769
- }),
5770
- // Subagent middleware: Automatic conversation summarization when token limits are approached
5771
- summarizationMiddleware({
5772
- model,
5773
- trigger: { tokens: 17e4 },
5774
- keep: { messages: 6 }
5775
- }),
5776
- // Subagent middleware: Anthropic prompt caching for improved performance
5777
- anthropicPromptCachingMiddleware({
5778
- unsupportedModelBehavior: "ignore"
5779
- }),
5780
- // Subagent middleware: Patches tool calls for compatibility
5781
- createPatchToolCallsMiddleware()
5782
- ],
5783
- defaultInterruptOn: interruptOn,
5784
- subagents,
5785
- generalPurposeAgent: false
5786
- }),
5787
- // Automatically summarizes conversation history when token limits are approached
5788
- summarizationMiddleware({
5789
- model,
5790
- trigger: { tokens: 17e4 },
5791
- keep: { messages: 6 }
5792
- }),
5793
- // Enables Anthropic prompt caching for improved performance and reduced costs
5794
- anthropicPromptCachingMiddleware({
5795
- unsupportedModelBehavior: "ignore"
5796
- }),
5797
- // Patches tool calls to ensure compatibility across different model providers
5798
- createPatchToolCallsMiddleware()
8936
+ teamMiddleware,
8937
+ ...allMiddleware
5799
8938
  ];
5800
- if (interruptOn) {
5801
- middleware.push(humanInTheLoopMiddleware2({ interruptOn }));
5802
- }
5803
- middleware.push(...customMiddleware);
5804
- return createAgent3({
5805
- model,
5806
- systemPrompt: finalSystemPrompt,
5807
- tools,
8939
+ const systemPrompt = config.systemPrompt + "\n\n" + TEAM_LEAD_BASE_PROMPT;
8940
+ const stateSchema2 = createReactAgentSchema(TEAM_STATE_SCHEMA);
8941
+ return createAgent5({
8942
+ model: config.model ?? "claude-sonnet-4-5-20250929",
8943
+ systemPrompt,
8944
+ tools: [],
8945
+ // all tools come from middleware
5808
8946
  middleware,
5809
- responseFormat,
5810
- contextSchema,
5811
- checkpointer,
5812
- store,
5813
- name
8947
+ checkpointer: config.checkpointer,
8948
+ stateSchema: stateSchema2
5814
8949
  });
5815
8950
  }
5816
8951
 
5817
- // src/agent_lattice/builders/DeepAgentGraphBuilder.ts
5818
- var DeepAgentGraphBuilder = class {
5819
- /**
5820
- * 根据 middleware 配置创建 backend factory
5821
- */
5822
- createFilesystemBackendFactory(middlewareConfigs) {
5823
- const filesystemConfig = middlewareConfigs.find((m) => m.type === "filesystem");
5824
- if (!filesystemConfig || !filesystemConfig.enabled) {
5825
- return void 0;
5826
- }
5827
- const isolatedLevel = filesystemConfig.config?.isolatedLevel || "global";
5828
- return async (config) => {
5829
- let sandboxName = "global";
5830
- if (isolatedLevel === "agent") {
5831
- sandboxName = "agent";
5832
- } else if (isolatedLevel === "thread") {
5833
- sandboxName = "thread";
5834
- }
5835
- const sandboxManager = sandboxLatticeManager.getSandboxLattice("default");
5836
- if (!sandboxManager) {
5837
- throw new Error("Sandbox manager not found");
5838
- }
5839
- return new SandboxFilesystem({
5840
- sandboxInstance: await sandboxManager.createSandbox(sandboxName)
5841
- });
5842
- };
5843
- }
5844
- /**
5845
- * 根据 middleware 配置创建 middlewares
5846
- */
5847
- createMiddlewares(middlewareConfigs) {
5848
- return createCommonMiddlewares(middlewareConfigs);
5849
- }
8952
+ // src/agent_team/builders/TeamAgentGraphBuilder.ts
8953
+ var TeamAgentGraphBuilder = class {
5850
8954
  /**
5851
- * 构建Deep Agent Graph
8955
+ * Build a Team agent from the registered AgentLattice config.
5852
8956
  *
5853
- * @param agentLattice Agent Lattice对象
5854
- * @param params Agent构建参数
5855
- * @returns 返回CompiledGraph对象
8957
+ * @param agentLattice - The AgentLattice containing the TeamAgentConfig
8958
+ * @param params - Build params with resolved tools and model
8959
+ * @returns AgentClient (the TeamLead ReactAgent)
5856
8960
  */
5857
8961
  build(agentLattice, params) {
8962
+ const config = agentLattice.config;
8963
+ if (!isTeamAgentConfig(config)) {
8964
+ throw new Error(
8965
+ `TeamAgentGraphBuilder received non-TEAM config: ${config.type}`
8966
+ );
8967
+ }
5858
8968
  const tools = params.tools.map((t) => {
5859
8969
  const toolClient = getToolClient(t.key);
5860
8970
  return toolClient;
5861
- }).filter((tool36) => tool36 !== void 0);
5862
- const subagents = params.subAgents.map((sa) => {
5863
- if (sa.client) {
5864
- return {
5865
- name: sa.config.name,
5866
- description: sa.config.description,
5867
- runnable: sa.client
5868
- };
5869
- } else {
5870
- const subagentClient = createAgentClientFromAgentLattice({
5871
- config: sa.config
5872
- });
5873
- return {
5874
- name: sa.config.name,
5875
- description: sa.config.description,
5876
- runnable: subagentClient
5877
- };
5878
- }
8971
+ }).filter((tool38) => tool38 !== void 0);
8972
+ const teammates = params.subAgents.map((sa) => {
8973
+ const baseConfig = sa.config;
8974
+ return {
8975
+ name: baseConfig.name,
8976
+ role: baseConfig.name,
8977
+ description: baseConfig.description,
8978
+ systemPrompt: baseConfig.prompt,
8979
+ tools
8980
+ };
5879
8981
  });
5880
8982
  const middlewareConfigs = params.middleware || [];
5881
- const filesystemBackend = this.createFilesystemBackendFactory(middlewareConfigs);
5882
- const middlewares = this.createMiddlewares(middlewareConfigs);
5883
- const deepAgent = createDeepAgent({
5884
- tools,
8983
+ let filesystemBackend = createFilesystemBackendFactory(middlewareConfigs);
8984
+ const middlewares = createCommonMiddlewares(middlewareConfigs);
8985
+ if (!filesystemBackend) {
8986
+ filesystemBackend = async (config2) => {
8987
+ return new StateBackend(config2);
8988
+ };
8989
+ }
8990
+ const teamConfig = {
8991
+ teammates,
8992
+ maxConcurrency: config.maxConcurrency,
5885
8993
  model: params.model,
5886
- contextSchema: params.stateSchema,
5887
- systemPrompt: params.prompt,
5888
- subagents,
8994
+ systemPrompt: config.prompt || void 0,
8995
+ tools,
5889
8996
  checkpointer: getCheckpointSaver("default"),
5890
- skills: params.skillCategories,
8997
+ scheduleLatticeKey: config.scheduleLatticeKey,
8998
+ pollIntervalMs: config.pollIntervalMs,
5891
8999
  backend: filesystemBackend,
5892
9000
  middleware: middlewares
5893
- });
5894
- return deepAgent;
9001
+ };
9002
+ const teamLead = createAgentTeam(teamConfig);
9003
+ return teamLead;
5895
9004
  }
5896
9005
  };
5897
9006
 
@@ -5916,6 +9025,7 @@ var AgentGraphBuilderFactory = class _AgentGraphBuilderFactory {
5916
9025
  registerDefaultBuilders() {
5917
9026
  this.builders.set(AgentType.REACT, new ReActAgentGraphBuilder());
5918
9027
  this.builders.set(AgentType.DEEP_AGENT, new DeepAgentGraphBuilder());
9028
+ this.builders.set(AgentType.TEAM, new TeamAgentGraphBuilder());
5919
9029
  }
5920
9030
  /**
5921
9031
  * 注册自定义Builder
@@ -6127,6 +9237,32 @@ var AgentLatticeManager = class _AgentLatticeManager extends BaseLatticeManager
6127
9237
  };
6128
9238
  this.register(config.key, agentLattice);
6129
9239
  }
9240
+ /**
9241
+ * Register a pre-built teammate agent client for dynamic team spawning.
9242
+ * Used when spawning teammates via agentWorkerGraph - the agent must exist
9243
+ * in the lattice before the worker invokes it.
9244
+ *
9245
+ * @param key Unique key (e.g. `team:${teamId}:${agentName}`)
9246
+ * @param client Pre-built AgentClient (ReactAgent or CompiledGraph)
9247
+ */
9248
+ registerTeammateAgent(key, client) {
9249
+ const agentLattice = {
9250
+ config: {
9251
+ key,
9252
+ name: key,
9253
+ description: `Teammate agent: ${key}`,
9254
+ type: AgentType.REACT
9255
+ },
9256
+ client
9257
+ };
9258
+ this.register(key, agentLattice);
9259
+ }
9260
+ /**
9261
+ * Unregister a teammate agent. Call when a team is done to clean up.
9262
+ */
9263
+ unregisterTeammateAgent(key) {
9264
+ return this.remove(key);
9265
+ }
6130
9266
  /**
6131
9267
  * 获取AgentLattice
6132
9268
  * @param key Lattice键名
@@ -6288,6 +9424,8 @@ var getAgentLattice = (key) => agentLatticeManager.getAgentLattice(key);
6288
9424
  var getAgentConfig = (key) => agentLatticeManager.getAgentConfig(key);
6289
9425
  var getAllAgentConfigs = () => agentLatticeManager.getAllAgentConfigs();
6290
9426
  var validateAgentInput = (key, input) => agentLatticeManager.validateAgentInput(key, input);
9427
+ var registerTeammateAgent = (key, client) => agentLatticeManager.registerTeammateAgent(key, client);
9428
+ var unregisterTeammateAgent = (key) => agentLatticeManager.unregisterTeammateAgent(key);
6291
9429
  var getAgentClient = (key, options) => agentLatticeManager.initializeClient(key, options);
6292
9430
  var createAgentClientFromAgentLattice = (agentLattice, options) => agentLatticeManager.createAgentClientFromConfig(agentLattice, options);
6293
9431
 
@@ -6488,9 +9626,9 @@ var InMemoryChunkBuffer = class extends ChunkBuffer {
6488
9626
  next: (chunk) => {
6489
9627
  queue.push(chunk);
6490
9628
  if (resolveNext) {
6491
- const resolve2 = resolveNext;
9629
+ const resolve3 = resolveNext;
6492
9630
  resolveNext = null;
6493
- resolve2();
9631
+ resolve3();
6494
9632
  }
6495
9633
  },
6496
9634
  error: (err) => {
@@ -6500,17 +9638,17 @@ var InMemoryChunkBuffer = class extends ChunkBuffer {
6500
9638
  errorNext = null;
6501
9639
  reject(err);
6502
9640
  } else if (resolveNext) {
6503
- const resolve2 = resolveNext;
9641
+ const resolve3 = resolveNext;
6504
9642
  resolveNext = null;
6505
- resolve2();
9643
+ resolve3();
6506
9644
  }
6507
9645
  },
6508
9646
  complete: () => {
6509
9647
  isCompleted = true;
6510
9648
  if (resolveNext) {
6511
- const resolve2 = resolveNext;
9649
+ const resolve3 = resolveNext;
6512
9650
  resolveNext = null;
6513
- resolve2();
9651
+ resolve3();
6514
9652
  }
6515
9653
  }
6516
9654
  });
@@ -6522,8 +9660,8 @@ var InMemoryChunkBuffer = class extends ChunkBuffer {
6522
9660
  }
6523
9661
  if (queue.length === 0) {
6524
9662
  if (isCompleted) break;
6525
- await new Promise((resolve2, reject) => {
6526
- resolveNext = resolve2;
9663
+ await new Promise((resolve3, reject) => {
9664
+ resolveNext = resolve3;
6527
9665
  errorNext = reject;
6528
9666
  });
6529
9667
  }
@@ -8507,10 +11645,10 @@ var McpLatticeManager = class _McpLatticeManager extends BaseLatticeManager {
8507
11645
  }
8508
11646
  const tools = await this.getAllTools();
8509
11647
  console.log(`[MCP] Registering ${tools.length} tools to Tool Lattice...`);
8510
- for (const tool36 of tools) {
8511
- const toolKey = prefix ? `${prefix}_${tool36.name}` : tool36.name;
8512
- tool36.name = toolKey;
8513
- toolLatticeManager.registerExistingTool(toolKey, tool36);
11648
+ for (const tool38 of tools) {
11649
+ const toolKey = prefix ? `${prefix}_${tool38.name}` : tool38.name;
11650
+ tool38.name = toolKey;
11651
+ toolLatticeManager.registerExistingTool(toolKey, tool38);
8514
11652
  console.log(`[MCP] Registered tool: ${toolKey}`);
8515
11653
  }
8516
11654
  console.log(`[MCP] Successfully registered ${tools.length} tools to Tool Lattice`);
@@ -8520,6 +11658,70 @@ var mcpManager = McpLatticeManager.getInstance();
8520
11658
 
8521
11659
  // src/index.ts
8522
11660
  import * as Protocols from "@axiom-lattice/protocols";
11661
+
11662
+ // src/util/encryption.ts
11663
+ import { createCipheriv, createDecipheriv, randomBytes, pbkdf2Sync } from "crypto";
11664
+ var ALGORITHM = "aes-256-gcm";
11665
+ var IV_LENGTH = 16;
11666
+ var SALT_LENGTH = 32;
11667
+ var ITERATIONS = 1e5;
11668
+ var DEFAULT_ENCRYPTION_KEY = "lattice-default-encryption-key-dev-only-32b!";
11669
+ var cachedKey = null;
11670
+ var keyValidated = false;
11671
+ function getEncryptionKey() {
11672
+ if (cachedKey) {
11673
+ return cachedKey;
11674
+ }
11675
+ const key = process.env.LATTICE_ENCRYPTION_KEY || DEFAULT_ENCRYPTION_KEY;
11676
+ cachedKey = pbkdf2Sync(key, "lattice-encryption-salt", ITERATIONS, 32, "sha256");
11677
+ if (!keyValidated) {
11678
+ keyValidated = true;
11679
+ validateEncryptionKey();
11680
+ }
11681
+ return cachedKey;
11682
+ }
11683
+ function encrypt(plaintext, key) {
11684
+ const actualKey = key || getEncryptionKey();
11685
+ const salt = randomBytes(SALT_LENGTH);
11686
+ const iv = randomBytes(IV_LENGTH);
11687
+ const derivedKey = pbkdf2Sync(actualKey, salt, ITERATIONS, 32, "sha256");
11688
+ const cipher = createCipheriv(ALGORITHM, derivedKey, iv);
11689
+ const encrypted = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
11690
+ const authTag = cipher.getAuthTag();
11691
+ return Buffer.concat([salt, iv, encrypted, authTag]).toString("base64");
11692
+ }
11693
+ function decrypt(encrypted, key) {
11694
+ const actualKey = key || getEncryptionKey();
11695
+ const data = Buffer.from(encrypted, "base64");
11696
+ const salt = data.subarray(0, SALT_LENGTH);
11697
+ const iv = data.subarray(SALT_LENGTH, SALT_LENGTH + IV_LENGTH);
11698
+ const authTag = data.subarray(-16);
11699
+ const ciphertext = data.subarray(SALT_LENGTH + IV_LENGTH, -16);
11700
+ const derivedKey = pbkdf2Sync(actualKey, salt, ITERATIONS, 32, "sha256");
11701
+ const decipher = createDecipheriv(ALGORITHM, derivedKey, iv);
11702
+ decipher.setAuthTag(authTag);
11703
+ return Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString("utf8");
11704
+ }
11705
+ function isUsingDefaultKey() {
11706
+ return !process.env.LATTICE_ENCRYPTION_KEY;
11707
+ }
11708
+ function validateEncryptionKey() {
11709
+ if (isUsingDefaultKey()) {
11710
+ if (process.env.NODE_ENV === "production") {
11711
+ console.warn(
11712
+ "\u26A0\uFE0F WARNING: Using default encryption key in production environment. Set LATTICE_ENCRYPTION_KEY environment variable for security."
11713
+ );
11714
+ } else {
11715
+ console.warn(
11716
+ "\u26A0\uFE0F WARNING: Using default encryption key. Set LATTICE_ENCRYPTION_KEY environment variable in production."
11717
+ );
11718
+ }
11719
+ }
11720
+ }
11721
+ function clearEncryptionKeyCache() {
11722
+ cachedKey = null;
11723
+ keyValidated = false;
11724
+ }
8523
11725
  export {
8524
11726
  AGENT_TASK_EVENT,
8525
11727
  AgentConfig,
@@ -8528,42 +11730,73 @@ export {
8528
11730
  AgentType,
8529
11731
  ChunkBuffer,
8530
11732
  ChunkBufferLatticeManager,
11733
+ CompositeBackend,
8531
11734
  ConsoleLoggerClient,
8532
11735
  DefaultScheduleClient,
11736
+ EMPTY_CONTENT_WARNING,
8533
11737
  EmbeddingsLatticeManager,
8534
11738
  FileSystemSkillStore,
11739
+ FilesystemBackend,
8535
11740
  GraphBuildOptions,
8536
11741
  InMemoryAssistantStore,
8537
11742
  InMemoryChunkBuffer,
11743
+ InMemoryDatabaseConfigStore,
11744
+ InMemoryMailboxStore,
11745
+ InMemoryTaskListStore,
8538
11746
  InMemoryThreadStore,
11747
+ LINE_NUMBER_WIDTH,
8539
11748
  LoggerLatticeManager,
11749
+ MAX_LINE_LENGTH,
8540
11750
  McpLatticeManager,
11751
+ MemoryBackend,
8541
11752
  MemoryLatticeManager,
8542
11753
  MemoryQueueClient,
8543
11754
  MemoryScheduleStorage,
8544
11755
  MemoryType,
11756
+ MessageType,
8545
11757
  ModelLatticeManager,
8546
11758
  PinoLoggerClient,
8547
11759
  PostgresDatabase,
8548
11760
  Protocols,
8549
11761
  QueueLatticeManager,
11762
+ SandboxFilesystem,
8550
11763
  SandboxLatticeManager,
8551
11764
  ScheduleLatticeManager,
8552
11765
  SkillLatticeManager,
8553
11766
  SqlDatabaseManager,
11767
+ StateBackend,
11768
+ StoreBackend,
8554
11769
  StoreLatticeManager,
11770
+ TOOL_RESULT_TOKEN_LIMIT,
11771
+ TRUNCATION_GUIDANCE,
11772
+ TaskStatus,
11773
+ TeamAgentGraphBuilder,
8555
11774
  ThreadStatus,
8556
11775
  ToolLatticeManager,
8557
11776
  VectorStoreLatticeManager,
8558
11777
  agentLatticeManager,
11778
+ buildGrepResultsDict,
11779
+ checkEmptyContent,
11780
+ clearEncryptionKeyCache,
11781
+ createAgentTeam,
11782
+ createFileData,
8559
11783
  createInfoSqlTool,
8560
11784
  createListTablesSqlTool,
8561
11785
  createQueryCheckerSqlTool,
8562
11786
  createQuerySqlTool,
11787
+ createTeamMiddleware,
11788
+ createTeammateTools,
11789
+ decrypt,
8563
11790
  describeCronExpression,
8564
11791
  embeddingsLatticeManager,
11792
+ encrypt,
8565
11793
  eventBus,
8566
11794
  event_bus_default as eventBusDefault,
11795
+ fileDataToString,
11796
+ formatContentWithLineNumbers,
11797
+ formatGrepMatches,
11798
+ formatGrepResults,
11799
+ formatReadResponse,
8567
11800
  getAgentClient,
8568
11801
  getAgentConfig,
8569
11802
  getAgentLattice,
@@ -8573,6 +11806,7 @@ export {
8573
11806
  getChunkBuffer,
8574
11807
  getEmbeddingsClient,
8575
11808
  getEmbeddingsLattice,
11809
+ getEncryptionKey,
8576
11810
  getLoggerLattice,
8577
11811
  getModelLattice,
8578
11812
  getNextCronTime,
@@ -8585,7 +11819,11 @@ export {
8585
11819
  getToolLattice,
8586
11820
  getVectorStoreClient,
8587
11821
  getVectorStoreLattice,
11822
+ globSearchFiles,
11823
+ grepMatchesFromFiles,
11824
+ grepSearchFiles,
8588
11825
  hasChunkBuffer,
11826
+ isUsingDefaultKey,
8589
11827
  isValidCronExpression,
8590
11828
  isValidSandboxName,
8591
11829
  isValidSkillName,
@@ -8594,6 +11832,7 @@ export {
8594
11832
  modelLatticeManager,
8595
11833
  normalizeSandboxName,
8596
11834
  parseCronExpression,
11835
+ performStringReplacement,
8597
11836
  queueLatticeManager,
8598
11837
  registerAgentLattice,
8599
11838
  registerAgentLattices,
@@ -8606,15 +11845,22 @@ export {
8606
11845
  registerQueueLattice,
8607
11846
  registerScheduleLattice,
8608
11847
  registerStoreLattice,
11848
+ registerTeammateAgent,
8609
11849
  registerToolLattice,
8610
11850
  registerVectorStoreLattice,
8611
11851
  sandboxLatticeManager,
11852
+ sanitizeToolCallId,
8612
11853
  scheduleLatticeManager,
8613
11854
  skillLatticeManager,
8614
11855
  sqlDatabaseManager,
8615
11856
  storeLatticeManager,
8616
11857
  toolLatticeManager,
11858
+ truncateIfTooLong,
11859
+ unregisterTeammateAgent,
11860
+ updateFileData,
8617
11861
  validateAgentInput,
11862
+ validateEncryptionKey,
11863
+ validatePath,
8618
11864
  validateSkillName,
8619
11865
  validateToolInput,
8620
11866
  vectorStoreLatticeManager