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