@axiom-lattice/core 2.1.12 → 2.1.14
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 +417 -2
- package/dist/index.d.ts +417 -2
- package/dist/index.js +1548 -10
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1572 -40
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -2
package/dist/index.js
CHANGED
|
@@ -37,6 +37,7 @@ __export(index_exports, {
|
|
|
37
37
|
AgentType: () => import_protocols.AgentType,
|
|
38
38
|
ChunkBuffer: () => ChunkBuffer,
|
|
39
39
|
ChunkBufferLatticeManager: () => ChunkBufferLatticeManager,
|
|
40
|
+
DefaultScheduleClient: () => DefaultScheduleClient,
|
|
40
41
|
EmbeddingsLatticeManager: () => EmbeddingsLatticeManager,
|
|
41
42
|
GraphBuildOptions: () => import_protocols.GraphBuildOptions,
|
|
42
43
|
InMemoryAssistantStore: () => InMemoryAssistantStore,
|
|
@@ -44,15 +45,20 @@ __export(index_exports, {
|
|
|
44
45
|
InMemoryThreadStore: () => InMemoryThreadStore,
|
|
45
46
|
MemoryLatticeManager: () => MemoryLatticeManager,
|
|
46
47
|
MemoryQueueClient: () => MemoryQueueClient,
|
|
48
|
+
MemoryScheduleStorage: () => MemoryScheduleStorage,
|
|
47
49
|
MemoryType: () => import_protocols2.MemoryType,
|
|
48
50
|
ModelLatticeManager: () => ModelLatticeManager,
|
|
51
|
+
PostgresDatabase: () => PostgresDatabase,
|
|
49
52
|
Protocols: () => Protocols,
|
|
50
53
|
QueueLatticeManager: () => QueueLatticeManager,
|
|
54
|
+
ScheduleLatticeManager: () => ScheduleLatticeManager,
|
|
55
|
+
SqlDatabaseManager: () => SqlDatabaseManager,
|
|
51
56
|
StoreLatticeManager: () => StoreLatticeManager,
|
|
52
57
|
ThreadStatus: () => ThreadStatus,
|
|
53
58
|
ToolLatticeManager: () => ToolLatticeManager,
|
|
54
59
|
VectorStoreLatticeManager: () => VectorStoreLatticeManager,
|
|
55
60
|
agentLatticeManager: () => agentLatticeManager,
|
|
61
|
+
describeCronExpression: () => describeCronExpression,
|
|
56
62
|
embeddingsLatticeManager: () => embeddingsLatticeManager,
|
|
57
63
|
eventBus: () => eventBus,
|
|
58
64
|
eventBusDefault: () => event_bus_default,
|
|
@@ -66,7 +72,9 @@ __export(index_exports, {
|
|
|
66
72
|
getEmbeddingsClient: () => getEmbeddingsClient,
|
|
67
73
|
getEmbeddingsLattice: () => getEmbeddingsLattice,
|
|
68
74
|
getModelLattice: () => getModelLattice,
|
|
75
|
+
getNextCronTime: () => getNextCronTime,
|
|
69
76
|
getQueueLattice: () => getQueueLattice,
|
|
77
|
+
getScheduleLattice: () => getScheduleLattice,
|
|
70
78
|
getStoreLattice: () => getStoreLattice,
|
|
71
79
|
getToolClient: () => getToolClient,
|
|
72
80
|
getToolDefinition: () => getToolDefinition,
|
|
@@ -74,7 +82,9 @@ __export(index_exports, {
|
|
|
74
82
|
getVectorStoreClient: () => getVectorStoreClient,
|
|
75
83
|
getVectorStoreLattice: () => getVectorStoreLattice,
|
|
76
84
|
hasChunkBuffer: () => hasChunkBuffer,
|
|
85
|
+
isValidCronExpression: () => isValidCronExpression,
|
|
77
86
|
modelLatticeManager: () => modelLatticeManager,
|
|
87
|
+
parseCronExpression: () => parseCronExpression,
|
|
78
88
|
queueLatticeManager: () => queueLatticeManager,
|
|
79
89
|
registerAgentLattice: () => registerAgentLattice,
|
|
80
90
|
registerAgentLattices: () => registerAgentLattices,
|
|
@@ -83,9 +93,12 @@ __export(index_exports, {
|
|
|
83
93
|
registerEmbeddingsLattice: () => registerEmbeddingsLattice,
|
|
84
94
|
registerModelLattice: () => registerModelLattice,
|
|
85
95
|
registerQueueLattice: () => registerQueueLattice,
|
|
96
|
+
registerScheduleLattice: () => registerScheduleLattice,
|
|
86
97
|
registerStoreLattice: () => registerStoreLattice,
|
|
87
98
|
registerToolLattice: () => registerToolLattice,
|
|
88
99
|
registerVectorStoreLattice: () => registerVectorStoreLattice,
|
|
100
|
+
scheduleLatticeManager: () => scheduleLatticeManager,
|
|
101
|
+
sqlDatabaseManager: () => sqlDatabaseManager,
|
|
89
102
|
storeLatticeManager: () => storeLatticeManager,
|
|
90
103
|
toolLatticeManager: () => toolLatticeManager,
|
|
91
104
|
validateAgentInput: () => validateAgentInput,
|
|
@@ -680,6 +693,538 @@ registerToolLattice(
|
|
|
680
693
|
}
|
|
681
694
|
);
|
|
682
695
|
|
|
696
|
+
// src/tool_lattice/sql/list_tables_sql.ts
|
|
697
|
+
var import_zod3 = __toESM(require("zod"));
|
|
698
|
+
|
|
699
|
+
// src/tool_lattice/sql/SqlDatabaseManager.ts
|
|
700
|
+
var PostgresDatabase = class {
|
|
701
|
+
constructor(config) {
|
|
702
|
+
// pg.Pool
|
|
703
|
+
this.connected = false;
|
|
704
|
+
this.config = config;
|
|
705
|
+
}
|
|
706
|
+
async connect() {
|
|
707
|
+
if (this.connected) return;
|
|
708
|
+
try {
|
|
709
|
+
const { Pool } = await import("pg");
|
|
710
|
+
const poolConfig = this.config.connectionString ? { connectionString: this.config.connectionString } : {
|
|
711
|
+
host: this.config.host || "localhost",
|
|
712
|
+
port: this.config.port || 5432,
|
|
713
|
+
database: this.config.database,
|
|
714
|
+
user: this.config.user,
|
|
715
|
+
password: this.config.password,
|
|
716
|
+
ssl: this.config.ssl ? { rejectUnauthorized: false } : void 0
|
|
717
|
+
};
|
|
718
|
+
this.pool = new Pool(poolConfig);
|
|
719
|
+
const client = await this.pool.connect();
|
|
720
|
+
client.release();
|
|
721
|
+
this.connected = true;
|
|
722
|
+
} catch (error) {
|
|
723
|
+
throw new Error(`Failed to connect to PostgreSQL: ${error}`);
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
async disconnect() {
|
|
727
|
+
if (this.pool) {
|
|
728
|
+
await this.pool.end();
|
|
729
|
+
this.connected = false;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
async listTables() {
|
|
733
|
+
await this.ensureConnected();
|
|
734
|
+
const query = `
|
|
735
|
+
SELECT table_name, table_schema
|
|
736
|
+
FROM information_schema.tables
|
|
737
|
+
WHERE table_schema NOT IN ('pg_catalog', 'information_schema')
|
|
738
|
+
AND table_type = 'BASE TABLE'
|
|
739
|
+
ORDER BY table_schema, table_name;
|
|
740
|
+
`;
|
|
741
|
+
const result = await this.pool.query(query);
|
|
742
|
+
return result.rows.map((row) => ({
|
|
743
|
+
name: row.table_name,
|
|
744
|
+
schema: row.table_schema
|
|
745
|
+
}));
|
|
746
|
+
}
|
|
747
|
+
async getTableInfo(tables) {
|
|
748
|
+
await this.ensureConnected();
|
|
749
|
+
const schemas = [];
|
|
750
|
+
for (const tableName of tables) {
|
|
751
|
+
const columnQuery = `
|
|
752
|
+
SELECT
|
|
753
|
+
c.column_name,
|
|
754
|
+
c.data_type,
|
|
755
|
+
c.is_nullable,
|
|
756
|
+
c.column_default,
|
|
757
|
+
CASE WHEN pk.column_name IS NOT NULL THEN true ELSE false END as is_primary_key,
|
|
758
|
+
CASE WHEN fk.column_name IS NOT NULL THEN true ELSE false END as is_foreign_key,
|
|
759
|
+
fk.foreign_table_name,
|
|
760
|
+
fk.foreign_column_name
|
|
761
|
+
FROM information_schema.columns c
|
|
762
|
+
LEFT JOIN (
|
|
763
|
+
SELECT ku.column_name, ku.table_name
|
|
764
|
+
FROM information_schema.table_constraints tc
|
|
765
|
+
JOIN information_schema.key_column_usage ku
|
|
766
|
+
ON tc.constraint_name = ku.constraint_name
|
|
767
|
+
WHERE tc.constraint_type = 'PRIMARY KEY'
|
|
768
|
+
) pk ON c.column_name = pk.column_name AND c.table_name = pk.table_name
|
|
769
|
+
LEFT JOIN (
|
|
770
|
+
SELECT
|
|
771
|
+
kcu.column_name,
|
|
772
|
+
kcu.table_name,
|
|
773
|
+
ccu.table_name as foreign_table_name,
|
|
774
|
+
ccu.column_name as foreign_column_name
|
|
775
|
+
FROM information_schema.table_constraints tc
|
|
776
|
+
JOIN information_schema.key_column_usage kcu
|
|
777
|
+
ON tc.constraint_name = kcu.constraint_name
|
|
778
|
+
JOIN information_schema.constraint_column_usage ccu
|
|
779
|
+
ON tc.constraint_name = ccu.constraint_name
|
|
780
|
+
WHERE tc.constraint_type = 'FOREIGN KEY'
|
|
781
|
+
) fk ON c.column_name = fk.column_name AND c.table_name = fk.table_name
|
|
782
|
+
WHERE c.table_name = $1
|
|
783
|
+
ORDER BY c.ordinal_position;
|
|
784
|
+
`;
|
|
785
|
+
const columnResult = await this.pool.query(columnQuery, [tableName]);
|
|
786
|
+
const columns = columnResult.rows.map((row) => ({
|
|
787
|
+
name: row.column_name,
|
|
788
|
+
type: row.data_type,
|
|
789
|
+
nullable: row.is_nullable === "YES",
|
|
790
|
+
default: row.column_default,
|
|
791
|
+
isPrimaryKey: row.is_primary_key,
|
|
792
|
+
isForeignKey: row.is_foreign_key,
|
|
793
|
+
foreignKeyRef: row.is_foreign_key ? `${row.foreign_table_name}.${row.foreign_column_name}` : void 0
|
|
794
|
+
}));
|
|
795
|
+
let sampleRows = [];
|
|
796
|
+
try {
|
|
797
|
+
const sampleQuery = `SELECT * FROM "${tableName}" LIMIT 3`;
|
|
798
|
+
const sampleResult = await this.pool.query(sampleQuery);
|
|
799
|
+
sampleRows = sampleResult.rows;
|
|
800
|
+
} catch {
|
|
801
|
+
}
|
|
802
|
+
schemas.push({
|
|
803
|
+
tableName,
|
|
804
|
+
columns,
|
|
805
|
+
sampleRows
|
|
806
|
+
});
|
|
807
|
+
}
|
|
808
|
+
return schemas;
|
|
809
|
+
}
|
|
810
|
+
async executeQuery(query) {
|
|
811
|
+
await this.ensureConnected();
|
|
812
|
+
const result = await this.pool.query(query);
|
|
813
|
+
return {
|
|
814
|
+
rows: result.rows,
|
|
815
|
+
rowCount: result.rowCount || result.rows.length,
|
|
816
|
+
fields: result.fields?.map((f) => f.name)
|
|
817
|
+
};
|
|
818
|
+
}
|
|
819
|
+
getDatabaseType() {
|
|
820
|
+
return "postgres";
|
|
821
|
+
}
|
|
822
|
+
async ensureConnected() {
|
|
823
|
+
if (!this.connected) {
|
|
824
|
+
await this.connect();
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
};
|
|
828
|
+
var SqlDatabaseManager = class _SqlDatabaseManager {
|
|
829
|
+
constructor() {
|
|
830
|
+
this.databases = /* @__PURE__ */ new Map();
|
|
831
|
+
this.defaultDatabaseKey = null;
|
|
832
|
+
}
|
|
833
|
+
/**
|
|
834
|
+
* Get the singleton instance
|
|
835
|
+
*/
|
|
836
|
+
static getInstance() {
|
|
837
|
+
if (!_SqlDatabaseManager.instance) {
|
|
838
|
+
_SqlDatabaseManager.instance = new _SqlDatabaseManager();
|
|
839
|
+
}
|
|
840
|
+
return _SqlDatabaseManager.instance;
|
|
841
|
+
}
|
|
842
|
+
/**
|
|
843
|
+
* Register a database connection
|
|
844
|
+
* @param key - Unique identifier for the database
|
|
845
|
+
* @param config - Database configuration
|
|
846
|
+
*/
|
|
847
|
+
registerDatabase(key, config) {
|
|
848
|
+
let database;
|
|
849
|
+
switch (config.type) {
|
|
850
|
+
case "postgres":
|
|
851
|
+
database = new PostgresDatabase(config);
|
|
852
|
+
break;
|
|
853
|
+
case "mysql":
|
|
854
|
+
throw new Error("MySQL support not yet implemented");
|
|
855
|
+
case "sqlite":
|
|
856
|
+
throw new Error("SQLite support not yet implemented");
|
|
857
|
+
default:
|
|
858
|
+
throw new Error(`Unsupported database type: ${config.type}`);
|
|
859
|
+
}
|
|
860
|
+
this.databases.set(key, database);
|
|
861
|
+
if (this.defaultDatabaseKey === null) {
|
|
862
|
+
this.defaultDatabaseKey = key;
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
/**
|
|
866
|
+
* Set the default database
|
|
867
|
+
* @param key - Database key to set as default
|
|
868
|
+
*/
|
|
869
|
+
setDefaultDatabase(key) {
|
|
870
|
+
if (!this.databases.has(key)) {
|
|
871
|
+
throw new Error(`Database '${key}' not found`);
|
|
872
|
+
}
|
|
873
|
+
this.defaultDatabaseKey = key;
|
|
874
|
+
}
|
|
875
|
+
/**
|
|
876
|
+
* Get a database by key
|
|
877
|
+
* @param key - Database key (optional, uses default if not provided)
|
|
878
|
+
*/
|
|
879
|
+
getDatabase(key) {
|
|
880
|
+
const dbKey = key || this.defaultDatabaseKey;
|
|
881
|
+
if (!dbKey) {
|
|
882
|
+
throw new Error("No database registered");
|
|
883
|
+
}
|
|
884
|
+
const database = this.databases.get(dbKey);
|
|
885
|
+
if (!database) {
|
|
886
|
+
throw new Error(`Database '${dbKey}' not found`);
|
|
887
|
+
}
|
|
888
|
+
return database;
|
|
889
|
+
}
|
|
890
|
+
/**
|
|
891
|
+
* Check if a database is registered
|
|
892
|
+
* @param key - Database key
|
|
893
|
+
*/
|
|
894
|
+
hasDatabase(key) {
|
|
895
|
+
return this.databases.has(key);
|
|
896
|
+
}
|
|
897
|
+
/**
|
|
898
|
+
* Get all registered database keys
|
|
899
|
+
*/
|
|
900
|
+
getDatabaseKeys() {
|
|
901
|
+
return Array.from(this.databases.keys());
|
|
902
|
+
}
|
|
903
|
+
/**
|
|
904
|
+
* Remove a database connection
|
|
905
|
+
* @param key - Database key
|
|
906
|
+
*/
|
|
907
|
+
async removeDatabase(key) {
|
|
908
|
+
const database = this.databases.get(key);
|
|
909
|
+
if (database) {
|
|
910
|
+
await database.disconnect();
|
|
911
|
+
this.databases.delete(key);
|
|
912
|
+
if (this.defaultDatabaseKey === key) {
|
|
913
|
+
this.defaultDatabaseKey = this.databases.size > 0 ? this.databases.keys().next().value || null : null;
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
/**
|
|
918
|
+
* Disconnect all databases
|
|
919
|
+
*/
|
|
920
|
+
async disconnectAll() {
|
|
921
|
+
for (const database of this.databases.values()) {
|
|
922
|
+
await database.disconnect();
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
};
|
|
926
|
+
var sqlDatabaseManager = SqlDatabaseManager.getInstance();
|
|
927
|
+
|
|
928
|
+
// src/tool_lattice/sql/list_tables_sql.ts
|
|
929
|
+
function getDatabaseKeyFromConfig(config) {
|
|
930
|
+
const runConfig = config.configurable?.runConfig;
|
|
931
|
+
return runConfig?.databaseKey;
|
|
932
|
+
}
|
|
933
|
+
registerToolLattice(
|
|
934
|
+
"list_tables_sql",
|
|
935
|
+
{
|
|
936
|
+
name: "list_tables_sql",
|
|
937
|
+
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.",
|
|
938
|
+
needUserApprove: false,
|
|
939
|
+
schema: import_zod3.default.object({})
|
|
940
|
+
},
|
|
941
|
+
async (_input, config) => {
|
|
942
|
+
try {
|
|
943
|
+
const databaseKey = getDatabaseKeyFromConfig(config);
|
|
944
|
+
const database = sqlDatabaseManager.getDatabase(databaseKey);
|
|
945
|
+
const tables = await database.listTables();
|
|
946
|
+
if (tables.length === 0) {
|
|
947
|
+
return "No tables found in the database.";
|
|
948
|
+
}
|
|
949
|
+
const tableNames = tables.map(
|
|
950
|
+
(t) => t.schema && t.schema !== "public" ? `${t.schema}.${t.name}` : t.name
|
|
951
|
+
);
|
|
952
|
+
return tableNames.join(", ");
|
|
953
|
+
} catch (error) {
|
|
954
|
+
return `Error listing tables: ${error instanceof Error ? error.message : String(error)}`;
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
);
|
|
958
|
+
|
|
959
|
+
// src/tool_lattice/sql/info_sql.ts
|
|
960
|
+
var import_zod4 = __toESM(require("zod"));
|
|
961
|
+
function getDatabaseKeyFromConfig2(config) {
|
|
962
|
+
const runConfig = config.configurable?.runConfig;
|
|
963
|
+
return runConfig?.databaseKey;
|
|
964
|
+
}
|
|
965
|
+
function formatTableSchema(schema) {
|
|
966
|
+
const lines = [];
|
|
967
|
+
lines.push(`
|
|
968
|
+
Table: ${schema.tableName}`);
|
|
969
|
+
lines.push("-".repeat(40));
|
|
970
|
+
lines.push("Columns:");
|
|
971
|
+
for (const col of schema.columns) {
|
|
972
|
+
const constraints = [];
|
|
973
|
+
if (col.isPrimaryKey) constraints.push("PRIMARY KEY");
|
|
974
|
+
if (col.isForeignKey && col.foreignKeyRef)
|
|
975
|
+
constraints.push(`FK -> ${col.foreignKeyRef}`);
|
|
976
|
+
if (!col.nullable) constraints.push("NOT NULL");
|
|
977
|
+
const constraintStr = constraints.length > 0 ? ` [${constraints.join(", ")}]` : "";
|
|
978
|
+
lines.push(` - ${col.name}: ${col.type}${constraintStr}`);
|
|
979
|
+
}
|
|
980
|
+
if (schema.sampleRows && schema.sampleRows.length > 0) {
|
|
981
|
+
lines.push("\nSample Rows (up to 3):");
|
|
982
|
+
for (const row of schema.sampleRows) {
|
|
983
|
+
const rowStr = Object.entries(row).map(([k, v]) => `${k}=${JSON.stringify(v)}`).join(", ");
|
|
984
|
+
lines.push(` ${rowStr}`);
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
return lines.join("\n");
|
|
988
|
+
}
|
|
989
|
+
registerToolLattice(
|
|
990
|
+
"info_sql",
|
|
991
|
+
{
|
|
992
|
+
name: "info_sql",
|
|
993
|
+
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.",
|
|
994
|
+
needUserApprove: false,
|
|
995
|
+
schema: import_zod4.default.object({
|
|
996
|
+
tables: import_zod4.default.string().describe(
|
|
997
|
+
"Comma-separated list of table names to get information for. Example: 'users, orders, products'"
|
|
998
|
+
)
|
|
999
|
+
})
|
|
1000
|
+
},
|
|
1001
|
+
async ({
|
|
1002
|
+
tables
|
|
1003
|
+
}, config) => {
|
|
1004
|
+
try {
|
|
1005
|
+
const databaseKey = getDatabaseKeyFromConfig2(config);
|
|
1006
|
+
const database = sqlDatabaseManager.getDatabase(databaseKey);
|
|
1007
|
+
const tableNames = tables.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
1008
|
+
if (tableNames.length === 0) {
|
|
1009
|
+
return "Error: No table names provided. Please provide a comma-separated list of table names.";
|
|
1010
|
+
}
|
|
1011
|
+
const schemas = await database.getTableInfo(tableNames);
|
|
1012
|
+
if (schemas.length === 0) {
|
|
1013
|
+
return `No schema information found for tables: ${tableNames.join(", ")}`;
|
|
1014
|
+
}
|
|
1015
|
+
const output = schemas.map(formatTableSchema).join("\n");
|
|
1016
|
+
return output;
|
|
1017
|
+
} catch (error) {
|
|
1018
|
+
return `Error getting table info: ${error instanceof Error ? error.message : String(error)}`;
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
);
|
|
1022
|
+
|
|
1023
|
+
// src/tool_lattice/sql/query_sql.ts
|
|
1024
|
+
var import_zod5 = __toESM(require("zod"));
|
|
1025
|
+
function getDatabaseKeyFromConfig3(config) {
|
|
1026
|
+
const runConfig = config.configurable?.runConfig;
|
|
1027
|
+
return runConfig?.databaseKey;
|
|
1028
|
+
}
|
|
1029
|
+
function formatQueryResult(rows, fields) {
|
|
1030
|
+
if (rows.length === 0) {
|
|
1031
|
+
return "Query executed successfully. No rows returned.";
|
|
1032
|
+
}
|
|
1033
|
+
const lines = [];
|
|
1034
|
+
const columns = fields || Object.keys(rows[0]);
|
|
1035
|
+
lines.push(columns.join(" | "));
|
|
1036
|
+
lines.push("-".repeat(columns.join(" | ").length));
|
|
1037
|
+
const maxRows = 50;
|
|
1038
|
+
const displayRows = rows.slice(0, maxRows);
|
|
1039
|
+
for (const row of displayRows) {
|
|
1040
|
+
const values = columns.map((col) => {
|
|
1041
|
+
const val = row[col];
|
|
1042
|
+
if (val === null) return "NULL";
|
|
1043
|
+
if (typeof val === "object") return JSON.stringify(val);
|
|
1044
|
+
return String(val);
|
|
1045
|
+
});
|
|
1046
|
+
lines.push(values.join(" | "));
|
|
1047
|
+
}
|
|
1048
|
+
if (rows.length > maxRows) {
|
|
1049
|
+
lines.push(`
|
|
1050
|
+
... (${rows.length - maxRows} more rows not shown)`);
|
|
1051
|
+
}
|
|
1052
|
+
lines.push(`
|
|
1053
|
+
Total rows: ${rows.length}`);
|
|
1054
|
+
return lines.join("\n");
|
|
1055
|
+
}
|
|
1056
|
+
registerToolLattice(
|
|
1057
|
+
"query_sql",
|
|
1058
|
+
{
|
|
1059
|
+
name: "query_sql",
|
|
1060
|
+
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.",
|
|
1061
|
+
needUserApprove: false,
|
|
1062
|
+
// SQL queries should require user approval for safety
|
|
1063
|
+
schema: import_zod5.default.object({
|
|
1064
|
+
query: import_zod5.default.string().describe(
|
|
1065
|
+
"The SQL query to execute. Should be a valid SELECT, INSERT, UPDATE, or DELETE statement."
|
|
1066
|
+
)
|
|
1067
|
+
})
|
|
1068
|
+
},
|
|
1069
|
+
async ({
|
|
1070
|
+
query
|
|
1071
|
+
}, config) => {
|
|
1072
|
+
try {
|
|
1073
|
+
const databaseKey = getDatabaseKeyFromConfig3(config);
|
|
1074
|
+
const database = sqlDatabaseManager.getDatabase(databaseKey);
|
|
1075
|
+
const trimmedQuery = query.trim();
|
|
1076
|
+
if (!trimmedQuery) {
|
|
1077
|
+
return "Error: Empty query provided. Please provide a valid SQL query.";
|
|
1078
|
+
}
|
|
1079
|
+
const result = await database.executeQuery(trimmedQuery);
|
|
1080
|
+
return formatQueryResult(result.rows, result.fields);
|
|
1081
|
+
} catch (error) {
|
|
1082
|
+
return `Error executing query: ${error instanceof Error ? error.message : String(error)}`;
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
);
|
|
1086
|
+
|
|
1087
|
+
// src/tool_lattice/sql/query_checker_sql.ts
|
|
1088
|
+
var import_zod6 = __toESM(require("zod"));
|
|
1089
|
+
function getDatabaseKeyFromConfig4(config) {
|
|
1090
|
+
const runConfig = config.configurable?.runConfig;
|
|
1091
|
+
return runConfig?.databaseKey;
|
|
1092
|
+
}
|
|
1093
|
+
var DANGEROUS_KEYWORDS = [
|
|
1094
|
+
"DROP",
|
|
1095
|
+
"TRUNCATE",
|
|
1096
|
+
"DELETE",
|
|
1097
|
+
"ALTER",
|
|
1098
|
+
"CREATE",
|
|
1099
|
+
"GRANT",
|
|
1100
|
+
"REVOKE"
|
|
1101
|
+
];
|
|
1102
|
+
function checkSyntax(query) {
|
|
1103
|
+
const issues = [];
|
|
1104
|
+
const upperQuery = query.toUpperCase();
|
|
1105
|
+
let parenCount = 0;
|
|
1106
|
+
for (const char of query) {
|
|
1107
|
+
if (char === "(") parenCount++;
|
|
1108
|
+
if (char === ")") parenCount--;
|
|
1109
|
+
if (parenCount < 0) {
|
|
1110
|
+
issues.push("Unbalanced parentheses: found closing ) without matching (");
|
|
1111
|
+
break;
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
if (parenCount > 0) {
|
|
1115
|
+
issues.push("Unbalanced parentheses: missing closing )");
|
|
1116
|
+
}
|
|
1117
|
+
const singleQuotes = (query.match(/'/g) || []).length;
|
|
1118
|
+
if (singleQuotes % 2 !== 0) {
|
|
1119
|
+
issues.push("Unbalanced single quotes");
|
|
1120
|
+
}
|
|
1121
|
+
if (upperQuery.includes("SELECT *") && upperQuery.includes("JOIN")) {
|
|
1122
|
+
issues.push(
|
|
1123
|
+
"Warning: SELECT * with JOIN may return duplicate column names. Consider specifying columns explicitly."
|
|
1124
|
+
);
|
|
1125
|
+
}
|
|
1126
|
+
if ((upperQuery.includes("UPDATE ") || upperQuery.includes("DELETE FROM ")) && !upperQuery.includes("WHERE ")) {
|
|
1127
|
+
issues.push(
|
|
1128
|
+
"Warning: UPDATE or DELETE without WHERE clause will affect all rows in the table."
|
|
1129
|
+
);
|
|
1130
|
+
}
|
|
1131
|
+
if (upperQuery.includes("GROUP BY") && !upperQuery.match(/\b(COUNT|SUM|AVG|MAX|MIN|ARRAY_AGG|STRING_AGG)\s*\(/i)) {
|
|
1132
|
+
issues.push(
|
|
1133
|
+
"Warning: GROUP BY used but no aggregate functions found. Make sure all non-aggregated columns are in GROUP BY."
|
|
1134
|
+
);
|
|
1135
|
+
}
|
|
1136
|
+
return issues;
|
|
1137
|
+
}
|
|
1138
|
+
function checkDangerousOperations(query) {
|
|
1139
|
+
const warnings = [];
|
|
1140
|
+
const upperQuery = query.toUpperCase();
|
|
1141
|
+
for (const keyword of DANGEROUS_KEYWORDS) {
|
|
1142
|
+
if (upperQuery.includes(keyword)) {
|
|
1143
|
+
warnings.push(
|
|
1144
|
+
`Warning: Query contains potentially dangerous operation: ${keyword}`
|
|
1145
|
+
);
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
return warnings;
|
|
1149
|
+
}
|
|
1150
|
+
registerToolLattice(
|
|
1151
|
+
"query_checker_sql",
|
|
1152
|
+
{
|
|
1153
|
+
name: "query_checker_sql",
|
|
1154
|
+
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.",
|
|
1155
|
+
needUserApprove: false,
|
|
1156
|
+
schema: import_zod6.default.object({
|
|
1157
|
+
query: import_zod6.default.string().describe("The SQL query to check and validate.")
|
|
1158
|
+
})
|
|
1159
|
+
},
|
|
1160
|
+
async ({
|
|
1161
|
+
query
|
|
1162
|
+
}, config) => {
|
|
1163
|
+
try {
|
|
1164
|
+
const trimmedQuery = query.trim();
|
|
1165
|
+
if (!trimmedQuery) {
|
|
1166
|
+
return "Error: Empty query provided. Please provide a SQL query to check.";
|
|
1167
|
+
}
|
|
1168
|
+
const results = [];
|
|
1169
|
+
results.push("SQL Query Check Results:");
|
|
1170
|
+
results.push("=".repeat(40));
|
|
1171
|
+
results.push(`
|
|
1172
|
+
Query:
|
|
1173
|
+
${trimmedQuery}
|
|
1174
|
+
`);
|
|
1175
|
+
const syntaxIssues = checkSyntax(trimmedQuery);
|
|
1176
|
+
if (syntaxIssues.length > 0) {
|
|
1177
|
+
results.push("Syntax Issues:");
|
|
1178
|
+
for (const issue of syntaxIssues) {
|
|
1179
|
+
results.push(` - ${issue}`);
|
|
1180
|
+
}
|
|
1181
|
+
} else {
|
|
1182
|
+
results.push("Syntax: \u2713 No obvious syntax issues found");
|
|
1183
|
+
}
|
|
1184
|
+
const dangerWarnings = checkDangerousOperations(trimmedQuery);
|
|
1185
|
+
if (dangerWarnings.length > 0) {
|
|
1186
|
+
results.push("\nSafety Warnings:");
|
|
1187
|
+
for (const warning of dangerWarnings) {
|
|
1188
|
+
results.push(` - ${warning}`);
|
|
1189
|
+
}
|
|
1190
|
+
} else {
|
|
1191
|
+
results.push("Safety: \u2713 No dangerous operations detected");
|
|
1192
|
+
}
|
|
1193
|
+
try {
|
|
1194
|
+
const databaseKey = getDatabaseKeyFromConfig4(config);
|
|
1195
|
+
const database = sqlDatabaseManager.getDatabase(databaseKey);
|
|
1196
|
+
const dbType = database.getDatabaseType();
|
|
1197
|
+
if (dbType === "postgres") {
|
|
1198
|
+
try {
|
|
1199
|
+
await database.executeQuery(`EXPLAIN ${trimmedQuery}`);
|
|
1200
|
+
results.push("Database Validation: \u2713 Query is valid against database schema");
|
|
1201
|
+
} catch (explainError) {
|
|
1202
|
+
results.push(
|
|
1203
|
+
`Database Validation: \u2717 ${explainError instanceof Error ? explainError.message : String(explainError)}`
|
|
1204
|
+
);
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
} catch {
|
|
1208
|
+
results.push(
|
|
1209
|
+
"Database Validation: Skipped (no database connection available)"
|
|
1210
|
+
);
|
|
1211
|
+
}
|
|
1212
|
+
const hasErrors = syntaxIssues.some((i) => !i.startsWith("Warning:")) || dangerWarnings.length > 0;
|
|
1213
|
+
results.push("\n" + "=".repeat(40));
|
|
1214
|
+
if (hasErrors) {
|
|
1215
|
+
results.push(
|
|
1216
|
+
"Overall: \u26A0\uFE0F Query has potential issues. Review warnings before execution."
|
|
1217
|
+
);
|
|
1218
|
+
} else {
|
|
1219
|
+
results.push("Overall: \u2713 Query appears to be safe to execute.");
|
|
1220
|
+
}
|
|
1221
|
+
return results.join("\n");
|
|
1222
|
+
} catch (error) {
|
|
1223
|
+
return `Error checking query: ${error instanceof Error ? error.message : String(error)}`;
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
);
|
|
1227
|
+
|
|
683
1228
|
// src/agent_lattice/types.ts
|
|
684
1229
|
var import_protocols = require("@axiom-lattice/protocols");
|
|
685
1230
|
|
|
@@ -764,7 +1309,7 @@ var memory = new import_langgraph2.MemorySaver();
|
|
|
764
1309
|
registerCheckpointSaver("default", memory);
|
|
765
1310
|
|
|
766
1311
|
// src/agent_lattice/builders/state.ts
|
|
767
|
-
var
|
|
1312
|
+
var import_zod7 = require("@langchain/langgraph/zod");
|
|
768
1313
|
var import_langgraph3 = require("@langchain/langgraph");
|
|
769
1314
|
var createReactAgentSchema = (schema) => {
|
|
770
1315
|
return schema ? import_langgraph3.MessagesZodState.extend(schema.shape) : void 0;
|
|
@@ -804,7 +1349,7 @@ var import_langchain6 = require("langchain");
|
|
|
804
1349
|
var import_langchain2 = require("langchain");
|
|
805
1350
|
var import_langgraph4 = require("@langchain/langgraph");
|
|
806
1351
|
var import_v3 = require("zod/v3");
|
|
807
|
-
var
|
|
1352
|
+
var import_zod8 = require("@langchain/langgraph/zod");
|
|
808
1353
|
|
|
809
1354
|
// src/deep_agent_new/backends/utils.ts
|
|
810
1355
|
var import_micromatch = __toESM(require("micromatch"));
|
|
@@ -1172,7 +1717,7 @@ function fileDataReducer(left, right) {
|
|
|
1172
1717
|
return result;
|
|
1173
1718
|
}
|
|
1174
1719
|
var FilesystemStateSchema = import_v3.z.object({
|
|
1175
|
-
files: (0,
|
|
1720
|
+
files: (0, import_zod8.withLangGraph)(
|
|
1176
1721
|
import_v3.z.record(import_v3.z.string(), FileDataSchema).default({}),
|
|
1177
1722
|
{
|
|
1178
1723
|
reducer: {
|
|
@@ -2295,7 +2840,7 @@ var SUPPORTS_NOFOLLOW = fsSync.constants.O_NOFOLLOW !== void 0;
|
|
|
2295
2840
|
|
|
2296
2841
|
// src/deep_agent_new/middleware/todos.ts
|
|
2297
2842
|
var import_langgraph8 = require("@langchain/langgraph");
|
|
2298
|
-
var
|
|
2843
|
+
var import_zod9 = require("zod");
|
|
2299
2844
|
var import_langchain5 = require("langchain");
|
|
2300
2845
|
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.
|
|
2301
2846
|
It also helps the user understand the progress of the task and overall progress of their requests.
|
|
@@ -2523,12 +3068,12 @@ Writing todos takes time and tokens, use it when it is helpful for managing comp
|
|
|
2523
3068
|
## Important To-Do List Usage Notes to Remember
|
|
2524
3069
|
- The \`write_todos\` tool should never be called multiple times in parallel.
|
|
2525
3070
|
- 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.`;
|
|
2526
|
-
var TodoStatus =
|
|
2527
|
-
var TodoSchema =
|
|
2528
|
-
content:
|
|
3071
|
+
var TodoStatus = import_zod9.z.enum(["pending", "in_progress", "completed"]).describe("Status of the todo");
|
|
3072
|
+
var TodoSchema = import_zod9.z.object({
|
|
3073
|
+
content: import_zod9.z.string().describe("Content of the todo item"),
|
|
2529
3074
|
status: TodoStatus
|
|
2530
3075
|
});
|
|
2531
|
-
var stateSchema =
|
|
3076
|
+
var stateSchema = import_zod9.z.object({ todos: import_zod9.z.array(TodoSchema).default([]) });
|
|
2532
3077
|
function todoListMiddleware(options) {
|
|
2533
3078
|
const writeTodos = (0, import_langchain5.tool)(
|
|
2534
3079
|
({ todos }, config) => {
|
|
@@ -2547,8 +3092,8 @@ function todoListMiddleware(options) {
|
|
|
2547
3092
|
{
|
|
2548
3093
|
name: "write_todos",
|
|
2549
3094
|
description: options?.toolDescription ?? WRITE_TODOS_DESCRIPTION,
|
|
2550
|
-
schema:
|
|
2551
|
-
todos:
|
|
3095
|
+
schema: import_zod9.z.object({
|
|
3096
|
+
todos: import_zod9.z.array(TodoSchema).describe("List of todo items to update")
|
|
2552
3097
|
})
|
|
2553
3098
|
}
|
|
2554
3099
|
);
|
|
@@ -3275,6 +3820,986 @@ var getChunkBuffer = (key) => ChunkBufferLatticeManager.getInstance().get(key);
|
|
|
3275
3820
|
var registerChunkBuffer = (key, buffer) => ChunkBufferLatticeManager.getInstance().register(key, buffer);
|
|
3276
3821
|
var hasChunkBuffer = (key) => ChunkBufferLatticeManager.getInstance().has(key);
|
|
3277
3822
|
|
|
3823
|
+
// src/schedule_lattice/ScheduleLatticeManager.ts
|
|
3824
|
+
var import_protocols7 = require("@axiom-lattice/protocols");
|
|
3825
|
+
|
|
3826
|
+
// src/schedule_lattice/DefaultScheduleClient.ts
|
|
3827
|
+
var import_protocols6 = require("@axiom-lattice/protocols");
|
|
3828
|
+
|
|
3829
|
+
// src/schedule_lattice/MemoryScheduleStorage.ts
|
|
3830
|
+
var import_protocols5 = require("@axiom-lattice/protocols");
|
|
3831
|
+
var MemoryScheduleStorage = class {
|
|
3832
|
+
constructor() {
|
|
3833
|
+
this.tasks = /* @__PURE__ */ new Map();
|
|
3834
|
+
}
|
|
3835
|
+
/**
|
|
3836
|
+
* Save a new task
|
|
3837
|
+
*/
|
|
3838
|
+
async save(task) {
|
|
3839
|
+
this.tasks.set(task.taskId, { ...task });
|
|
3840
|
+
}
|
|
3841
|
+
/**
|
|
3842
|
+
* Get task by ID
|
|
3843
|
+
*/
|
|
3844
|
+
async get(taskId) {
|
|
3845
|
+
const task = this.tasks.get(taskId);
|
|
3846
|
+
return task ? { ...task } : null;
|
|
3847
|
+
}
|
|
3848
|
+
/**
|
|
3849
|
+
* Update task
|
|
3850
|
+
*/
|
|
3851
|
+
async update(taskId, updates) {
|
|
3852
|
+
const task = this.tasks.get(taskId);
|
|
3853
|
+
if (task) {
|
|
3854
|
+
this.tasks.set(taskId, {
|
|
3855
|
+
...task,
|
|
3856
|
+
...updates,
|
|
3857
|
+
updatedAt: Date.now()
|
|
3858
|
+
});
|
|
3859
|
+
}
|
|
3860
|
+
}
|
|
3861
|
+
/**
|
|
3862
|
+
* Delete task
|
|
3863
|
+
*/
|
|
3864
|
+
async delete(taskId) {
|
|
3865
|
+
this.tasks.delete(taskId);
|
|
3866
|
+
}
|
|
3867
|
+
/**
|
|
3868
|
+
* Get all active tasks (pending or paused)
|
|
3869
|
+
*/
|
|
3870
|
+
async getActiveTasks() {
|
|
3871
|
+
const result = [];
|
|
3872
|
+
for (const task of this.tasks.values()) {
|
|
3873
|
+
if (task.status === import_protocols5.ScheduledTaskStatus.PENDING || task.status === import_protocols5.ScheduledTaskStatus.PAUSED) {
|
|
3874
|
+
result.push({ ...task });
|
|
3875
|
+
}
|
|
3876
|
+
}
|
|
3877
|
+
return result;
|
|
3878
|
+
}
|
|
3879
|
+
/**
|
|
3880
|
+
* Get tasks by type
|
|
3881
|
+
*/
|
|
3882
|
+
async getTasksByType(taskType) {
|
|
3883
|
+
const result = [];
|
|
3884
|
+
for (const task of this.tasks.values()) {
|
|
3885
|
+
if (task.taskType === taskType) {
|
|
3886
|
+
result.push({ ...task });
|
|
3887
|
+
}
|
|
3888
|
+
}
|
|
3889
|
+
return result;
|
|
3890
|
+
}
|
|
3891
|
+
/**
|
|
3892
|
+
* Get tasks by status
|
|
3893
|
+
*/
|
|
3894
|
+
async getTasksByStatus(status) {
|
|
3895
|
+
const result = [];
|
|
3896
|
+
for (const task of this.tasks.values()) {
|
|
3897
|
+
if (task.status === status) {
|
|
3898
|
+
result.push({ ...task });
|
|
3899
|
+
}
|
|
3900
|
+
}
|
|
3901
|
+
return result;
|
|
3902
|
+
}
|
|
3903
|
+
/**
|
|
3904
|
+
* Get tasks by execution type
|
|
3905
|
+
*/
|
|
3906
|
+
async getTasksByExecutionType(executionType) {
|
|
3907
|
+
const result = [];
|
|
3908
|
+
for (const task of this.tasks.values()) {
|
|
3909
|
+
if (task.executionType === executionType) {
|
|
3910
|
+
result.push({ ...task });
|
|
3911
|
+
}
|
|
3912
|
+
}
|
|
3913
|
+
return result;
|
|
3914
|
+
}
|
|
3915
|
+
/**
|
|
3916
|
+
* Get tasks by assistant ID
|
|
3917
|
+
*/
|
|
3918
|
+
async getTasksByAssistantId(assistantId) {
|
|
3919
|
+
const result = [];
|
|
3920
|
+
for (const task of this.tasks.values()) {
|
|
3921
|
+
if (task.assistantId === assistantId) {
|
|
3922
|
+
result.push({ ...task });
|
|
3923
|
+
}
|
|
3924
|
+
}
|
|
3925
|
+
return result;
|
|
3926
|
+
}
|
|
3927
|
+
/**
|
|
3928
|
+
* Get tasks by thread ID
|
|
3929
|
+
*/
|
|
3930
|
+
async getTasksByThreadId(threadId) {
|
|
3931
|
+
const result = [];
|
|
3932
|
+
for (const task of this.tasks.values()) {
|
|
3933
|
+
if (task.threadId === threadId) {
|
|
3934
|
+
result.push({ ...task });
|
|
3935
|
+
}
|
|
3936
|
+
}
|
|
3937
|
+
return result;
|
|
3938
|
+
}
|
|
3939
|
+
/**
|
|
3940
|
+
* Get all tasks with optional filters
|
|
3941
|
+
*/
|
|
3942
|
+
async getAllTasks(filters) {
|
|
3943
|
+
let result = [];
|
|
3944
|
+
for (const task of this.tasks.values()) {
|
|
3945
|
+
let match = true;
|
|
3946
|
+
if (filters?.status !== void 0 && task.status !== filters.status) {
|
|
3947
|
+
match = false;
|
|
3948
|
+
}
|
|
3949
|
+
if (filters?.executionType !== void 0 && task.executionType !== filters.executionType) {
|
|
3950
|
+
match = false;
|
|
3951
|
+
}
|
|
3952
|
+
if (filters?.taskType !== void 0 && task.taskType !== filters.taskType) {
|
|
3953
|
+
match = false;
|
|
3954
|
+
}
|
|
3955
|
+
if (filters?.assistantId !== void 0 && task.assistantId !== filters.assistantId) {
|
|
3956
|
+
match = false;
|
|
3957
|
+
}
|
|
3958
|
+
if (filters?.threadId !== void 0 && task.threadId !== filters.threadId) {
|
|
3959
|
+
match = false;
|
|
3960
|
+
}
|
|
3961
|
+
if (match) {
|
|
3962
|
+
result.push({ ...task });
|
|
3963
|
+
}
|
|
3964
|
+
}
|
|
3965
|
+
result.sort((a, b) => b.createdAt - a.createdAt);
|
|
3966
|
+
const offset = filters?.offset || 0;
|
|
3967
|
+
const limit = filters?.limit;
|
|
3968
|
+
if (limit !== void 0) {
|
|
3969
|
+
result = result.slice(offset, offset + limit);
|
|
3970
|
+
} else if (offset > 0) {
|
|
3971
|
+
result = result.slice(offset);
|
|
3972
|
+
}
|
|
3973
|
+
return result;
|
|
3974
|
+
}
|
|
3975
|
+
/**
|
|
3976
|
+
* Count tasks with optional filters
|
|
3977
|
+
*/
|
|
3978
|
+
async countTasks(filters) {
|
|
3979
|
+
let count = 0;
|
|
3980
|
+
for (const task of this.tasks.values()) {
|
|
3981
|
+
let match = true;
|
|
3982
|
+
if (filters?.status !== void 0 && task.status !== filters.status) {
|
|
3983
|
+
match = false;
|
|
3984
|
+
}
|
|
3985
|
+
if (filters?.executionType !== void 0 && task.executionType !== filters.executionType) {
|
|
3986
|
+
match = false;
|
|
3987
|
+
}
|
|
3988
|
+
if (filters?.taskType !== void 0 && task.taskType !== filters.taskType) {
|
|
3989
|
+
match = false;
|
|
3990
|
+
}
|
|
3991
|
+
if (filters?.assistantId !== void 0 && task.assistantId !== filters.assistantId) {
|
|
3992
|
+
match = false;
|
|
3993
|
+
}
|
|
3994
|
+
if (filters?.threadId !== void 0 && task.threadId !== filters.threadId) {
|
|
3995
|
+
match = false;
|
|
3996
|
+
}
|
|
3997
|
+
if (match) {
|
|
3998
|
+
count++;
|
|
3999
|
+
}
|
|
4000
|
+
}
|
|
4001
|
+
return count;
|
|
4002
|
+
}
|
|
4003
|
+
/**
|
|
4004
|
+
* Delete completed/cancelled tasks older than specified time
|
|
4005
|
+
*/
|
|
4006
|
+
async deleteOldTasks(olderThanMs) {
|
|
4007
|
+
const cutoff = Date.now() - olderThanMs;
|
|
4008
|
+
let deleted = 0;
|
|
4009
|
+
for (const [taskId, task] of this.tasks.entries()) {
|
|
4010
|
+
if ((task.status === import_protocols5.ScheduledTaskStatus.COMPLETED || task.status === import_protocols5.ScheduledTaskStatus.CANCELLED || task.status === import_protocols5.ScheduledTaskStatus.FAILED) && task.updatedAt < cutoff) {
|
|
4011
|
+
this.tasks.delete(taskId);
|
|
4012
|
+
deleted++;
|
|
4013
|
+
}
|
|
4014
|
+
}
|
|
4015
|
+
return deleted;
|
|
4016
|
+
}
|
|
4017
|
+
/**
|
|
4018
|
+
* Clear all tasks (useful for testing)
|
|
4019
|
+
*/
|
|
4020
|
+
clear() {
|
|
4021
|
+
this.tasks.clear();
|
|
4022
|
+
}
|
|
4023
|
+
/**
|
|
4024
|
+
* Get total task count (useful for debugging)
|
|
4025
|
+
*/
|
|
4026
|
+
size() {
|
|
4027
|
+
return this.tasks.size;
|
|
4028
|
+
}
|
|
4029
|
+
};
|
|
4030
|
+
|
|
4031
|
+
// src/schedule_lattice/CronParser.ts
|
|
4032
|
+
function parseCronExpression(expression) {
|
|
4033
|
+
const parts = expression.trim().split(/\s+/);
|
|
4034
|
+
if (parts.length !== 5) {
|
|
4035
|
+
throw new Error(
|
|
4036
|
+
`Invalid cron expression: expected 5 fields, got ${parts.length}`
|
|
4037
|
+
);
|
|
4038
|
+
}
|
|
4039
|
+
return {
|
|
4040
|
+
minute: parseField(parts[0], 0, 59),
|
|
4041
|
+
hour: parseField(parts[1], 0, 23),
|
|
4042
|
+
dayOfMonth: parseField(parts[2], 1, 31),
|
|
4043
|
+
month: parseField(parts[3], 1, 12),
|
|
4044
|
+
dayOfWeek: parseField(parts[4], 0, 7).map((d) => d === 7 ? 0 : d)
|
|
4045
|
+
// Normalize Sunday
|
|
4046
|
+
};
|
|
4047
|
+
}
|
|
4048
|
+
function parseField(field, min, max) {
|
|
4049
|
+
const values = /* @__PURE__ */ new Set();
|
|
4050
|
+
const segments = field.split(",");
|
|
4051
|
+
for (const segment of segments) {
|
|
4052
|
+
const [range, stepStr] = segment.split("/");
|
|
4053
|
+
const step = stepStr ? parseInt(stepStr, 10) : 1;
|
|
4054
|
+
if (isNaN(step) || step < 1) {
|
|
4055
|
+
throw new Error(`Invalid step value: ${stepStr}`);
|
|
4056
|
+
}
|
|
4057
|
+
let rangeStart;
|
|
4058
|
+
let rangeEnd;
|
|
4059
|
+
if (range === "*") {
|
|
4060
|
+
rangeStart = min;
|
|
4061
|
+
rangeEnd = max;
|
|
4062
|
+
} else if (range.includes("-")) {
|
|
4063
|
+
const [startStr, endStr] = range.split("-");
|
|
4064
|
+
rangeStart = parseInt(startStr, 10);
|
|
4065
|
+
rangeEnd = parseInt(endStr, 10);
|
|
4066
|
+
if (isNaN(rangeStart) || isNaN(rangeEnd)) {
|
|
4067
|
+
throw new Error(`Invalid range: ${range}`);
|
|
4068
|
+
}
|
|
4069
|
+
} else {
|
|
4070
|
+
const value = parseInt(range, 10);
|
|
4071
|
+
if (isNaN(value)) {
|
|
4072
|
+
throw new Error(`Invalid value: ${range}`);
|
|
4073
|
+
}
|
|
4074
|
+
rangeStart = value;
|
|
4075
|
+
rangeEnd = value;
|
|
4076
|
+
}
|
|
4077
|
+
if (rangeStart < min || rangeEnd > max || rangeStart > rangeEnd) {
|
|
4078
|
+
throw new Error(`Value out of range: ${range} (expected ${min}-${max})`);
|
|
4079
|
+
}
|
|
4080
|
+
for (let i = rangeStart; i <= rangeEnd; i += step) {
|
|
4081
|
+
values.add(i);
|
|
4082
|
+
}
|
|
4083
|
+
}
|
|
4084
|
+
return Array.from(values).sort((a, b) => a - b);
|
|
4085
|
+
}
|
|
4086
|
+
function getNextCronTime(expression, after = /* @__PURE__ */ new Date(), timezone) {
|
|
4087
|
+
const fields = parseCronExpression(expression);
|
|
4088
|
+
const next = new Date(after.getTime());
|
|
4089
|
+
next.setSeconds(0, 0);
|
|
4090
|
+
next.setMinutes(next.getMinutes() + 1);
|
|
4091
|
+
const maxIterations = 366 * 24 * 60;
|
|
4092
|
+
let iterations = 0;
|
|
4093
|
+
while (iterations < maxIterations) {
|
|
4094
|
+
iterations++;
|
|
4095
|
+
const month = next.getMonth() + 1;
|
|
4096
|
+
if (!fields.month.includes(month)) {
|
|
4097
|
+
const nextMonth = fields.month.find((m) => m > month);
|
|
4098
|
+
if (nextMonth !== void 0) {
|
|
4099
|
+
next.setMonth(nextMonth - 1, 1);
|
|
4100
|
+
} else {
|
|
4101
|
+
next.setFullYear(next.getFullYear() + 1);
|
|
4102
|
+
next.setMonth(fields.month[0] - 1, 1);
|
|
4103
|
+
}
|
|
4104
|
+
next.setHours(fields.hour[0], fields.minute[0], 0, 0);
|
|
4105
|
+
continue;
|
|
4106
|
+
}
|
|
4107
|
+
const dayOfMonth = next.getDate();
|
|
4108
|
+
const maxDayOfMonth = new Date(
|
|
4109
|
+
next.getFullYear(),
|
|
4110
|
+
next.getMonth() + 1,
|
|
4111
|
+
0
|
|
4112
|
+
).getDate();
|
|
4113
|
+
const dayOfWeek = next.getDay();
|
|
4114
|
+
const dayOfMonthMatch = fields.dayOfMonth.includes(dayOfMonth);
|
|
4115
|
+
const dayOfWeekMatch = fields.dayOfWeek.includes(dayOfWeek);
|
|
4116
|
+
const dayOfMonthWildcard = fields.dayOfMonth.length === 31 && fields.dayOfMonth[0] === 1 && fields.dayOfMonth[30] === 31;
|
|
4117
|
+
const dayOfWeekWildcard = fields.dayOfWeek.length >= 7 || fields.dayOfWeek.length === 1 && fields.dayOfWeek.includes(0);
|
|
4118
|
+
let dayMatch;
|
|
4119
|
+
if (dayOfMonthWildcard && dayOfWeekWildcard) {
|
|
4120
|
+
dayMatch = true;
|
|
4121
|
+
} else if (dayOfMonthWildcard) {
|
|
4122
|
+
dayMatch = dayOfWeekMatch;
|
|
4123
|
+
} else if (dayOfWeekWildcard) {
|
|
4124
|
+
dayMatch = dayOfMonthMatch;
|
|
4125
|
+
} else {
|
|
4126
|
+
dayMatch = dayOfMonthMatch || dayOfWeekMatch;
|
|
4127
|
+
}
|
|
4128
|
+
if (!dayMatch || dayOfMonth > maxDayOfMonth) {
|
|
4129
|
+
next.setDate(next.getDate() + 1);
|
|
4130
|
+
next.setHours(fields.hour[0], fields.minute[0], 0, 0);
|
|
4131
|
+
continue;
|
|
4132
|
+
}
|
|
4133
|
+
const hour = next.getHours();
|
|
4134
|
+
if (!fields.hour.includes(hour)) {
|
|
4135
|
+
const nextHour = fields.hour.find((h) => h > hour);
|
|
4136
|
+
if (nextHour !== void 0) {
|
|
4137
|
+
next.setHours(nextHour, fields.minute[0], 0, 0);
|
|
4138
|
+
} else {
|
|
4139
|
+
next.setDate(next.getDate() + 1);
|
|
4140
|
+
next.setHours(fields.hour[0], fields.minute[0], 0, 0);
|
|
4141
|
+
}
|
|
4142
|
+
continue;
|
|
4143
|
+
}
|
|
4144
|
+
const minute = next.getMinutes();
|
|
4145
|
+
if (!fields.minute.includes(minute)) {
|
|
4146
|
+
const nextMinute = fields.minute.find((m) => m > minute);
|
|
4147
|
+
if (nextMinute !== void 0) {
|
|
4148
|
+
next.setMinutes(nextMinute, 0, 0);
|
|
4149
|
+
} else {
|
|
4150
|
+
next.setHours(next.getHours() + 1, fields.minute[0], 0, 0);
|
|
4151
|
+
}
|
|
4152
|
+
continue;
|
|
4153
|
+
}
|
|
4154
|
+
return next;
|
|
4155
|
+
}
|
|
4156
|
+
throw new Error("Could not find next cron time within 1 year");
|
|
4157
|
+
}
|
|
4158
|
+
function isValidCronExpression(expression) {
|
|
4159
|
+
try {
|
|
4160
|
+
parseCronExpression(expression);
|
|
4161
|
+
return true;
|
|
4162
|
+
} catch {
|
|
4163
|
+
return false;
|
|
4164
|
+
}
|
|
4165
|
+
}
|
|
4166
|
+
function describeCronExpression(expression) {
|
|
4167
|
+
const fields = parseCronExpression(expression);
|
|
4168
|
+
const parts = [];
|
|
4169
|
+
if (fields.minute.length === 60) {
|
|
4170
|
+
parts.push("every minute");
|
|
4171
|
+
} else if (fields.minute.length === 1) {
|
|
4172
|
+
parts.push(`at minute ${fields.minute[0]}`);
|
|
4173
|
+
} else {
|
|
4174
|
+
parts.push(`at minutes ${fields.minute.join(", ")}`);
|
|
4175
|
+
}
|
|
4176
|
+
if (fields.hour.length === 24) {
|
|
4177
|
+
parts.push("of every hour");
|
|
4178
|
+
} else if (fields.hour.length === 1) {
|
|
4179
|
+
parts.push(`of hour ${fields.hour[0]}`);
|
|
4180
|
+
} else {
|
|
4181
|
+
parts.push(`of hours ${fields.hour.join(", ")}`);
|
|
4182
|
+
}
|
|
4183
|
+
if (fields.dayOfMonth.length < 31) {
|
|
4184
|
+
if (fields.dayOfMonth.length === 1) {
|
|
4185
|
+
parts.push(`on day ${fields.dayOfMonth[0]}`);
|
|
4186
|
+
} else {
|
|
4187
|
+
parts.push(`on days ${fields.dayOfMonth.join(", ")}`);
|
|
4188
|
+
}
|
|
4189
|
+
}
|
|
4190
|
+
const monthNames = [
|
|
4191
|
+
"",
|
|
4192
|
+
"Jan",
|
|
4193
|
+
"Feb",
|
|
4194
|
+
"Mar",
|
|
4195
|
+
"Apr",
|
|
4196
|
+
"May",
|
|
4197
|
+
"Jun",
|
|
4198
|
+
"Jul",
|
|
4199
|
+
"Aug",
|
|
4200
|
+
"Sep",
|
|
4201
|
+
"Oct",
|
|
4202
|
+
"Nov",
|
|
4203
|
+
"Dec"
|
|
4204
|
+
];
|
|
4205
|
+
if (fields.month.length < 12) {
|
|
4206
|
+
const months = fields.month.map((m) => monthNames[m]);
|
|
4207
|
+
parts.push(`in ${months.join(", ")}`);
|
|
4208
|
+
}
|
|
4209
|
+
const dayNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
|
4210
|
+
const uniqueDays = [...new Set(fields.dayOfWeek)];
|
|
4211
|
+
if (uniqueDays.length < 7) {
|
|
4212
|
+
const days = uniqueDays.map((d) => dayNames[d]);
|
|
4213
|
+
parts.push(`on ${days.join(", ")}`);
|
|
4214
|
+
}
|
|
4215
|
+
return parts.join(" ");
|
|
4216
|
+
}
|
|
4217
|
+
|
|
4218
|
+
// src/schedule_lattice/DefaultScheduleClient.ts
|
|
4219
|
+
var _DefaultScheduleClient = class _DefaultScheduleClient {
|
|
4220
|
+
constructor(storage) {
|
|
4221
|
+
this.handlers = /* @__PURE__ */ new Map();
|
|
4222
|
+
this.timers = /* @__PURE__ */ new Map();
|
|
4223
|
+
this.storage = storage || new MemoryScheduleStorage();
|
|
4224
|
+
}
|
|
4225
|
+
/**
|
|
4226
|
+
* Get the singleton instance of DefaultScheduleClient
|
|
4227
|
+
*/
|
|
4228
|
+
static getInstance(storage) {
|
|
4229
|
+
if (!_DefaultScheduleClient.instance) {
|
|
4230
|
+
_DefaultScheduleClient.instance = new _DefaultScheduleClient(storage);
|
|
4231
|
+
}
|
|
4232
|
+
return _DefaultScheduleClient.instance;
|
|
4233
|
+
}
|
|
4234
|
+
/**
|
|
4235
|
+
* Reset the singleton instance (useful for testing)
|
|
4236
|
+
*/
|
|
4237
|
+
static resetInstance() {
|
|
4238
|
+
if (_DefaultScheduleClient.instance) {
|
|
4239
|
+
_DefaultScheduleClient.instance.cancelAll();
|
|
4240
|
+
_DefaultScheduleClient.instance = null;
|
|
4241
|
+
}
|
|
4242
|
+
}
|
|
4243
|
+
// ===== Handler Registration =====
|
|
4244
|
+
registerHandler(taskType, handler) {
|
|
4245
|
+
this.handlers.set(taskType, handler);
|
|
4246
|
+
console.log(`[Scheduler] Handler registered for task type: ${taskType}`);
|
|
4247
|
+
}
|
|
4248
|
+
unregisterHandler(taskType) {
|
|
4249
|
+
const result = this.handlers.delete(taskType);
|
|
4250
|
+
if (result) {
|
|
4251
|
+
console.log(
|
|
4252
|
+
`[Scheduler] Handler unregistered for task type: ${taskType}`
|
|
4253
|
+
);
|
|
4254
|
+
}
|
|
4255
|
+
return result;
|
|
4256
|
+
}
|
|
4257
|
+
hasHandler(taskType) {
|
|
4258
|
+
return this.handlers.has(taskType);
|
|
4259
|
+
}
|
|
4260
|
+
getHandlerTypes() {
|
|
4261
|
+
return Array.from(this.handlers.keys());
|
|
4262
|
+
}
|
|
4263
|
+
// ===== One-time Task Scheduling =====
|
|
4264
|
+
async scheduleOnce(taskId, taskType, payload, options) {
|
|
4265
|
+
if (!this.handlers.has(taskType)) {
|
|
4266
|
+
console.error(
|
|
4267
|
+
`[Scheduler] No handler registered for task type: ${taskType}`
|
|
4268
|
+
);
|
|
4269
|
+
return false;
|
|
4270
|
+
}
|
|
4271
|
+
let executeAt;
|
|
4272
|
+
if (options.executeAt !== void 0) {
|
|
4273
|
+
executeAt = options.executeAt;
|
|
4274
|
+
} else if (options.delayMs !== void 0) {
|
|
4275
|
+
executeAt = Date.now() + options.delayMs;
|
|
4276
|
+
} else {
|
|
4277
|
+
console.error("[Scheduler] Either executeAt or delayMs must be provided");
|
|
4278
|
+
return false;
|
|
4279
|
+
}
|
|
4280
|
+
if (await this.has(taskId)) {
|
|
4281
|
+
await this.cancel(taskId);
|
|
4282
|
+
}
|
|
4283
|
+
const now = Date.now();
|
|
4284
|
+
const task = {
|
|
4285
|
+
taskId,
|
|
4286
|
+
taskType,
|
|
4287
|
+
payload,
|
|
4288
|
+
assistantId: options.assistantId,
|
|
4289
|
+
threadId: options.threadId,
|
|
4290
|
+
executionType: import_protocols6.ScheduleExecutionType.ONCE,
|
|
4291
|
+
executeAt,
|
|
4292
|
+
delayMs: options.delayMs,
|
|
4293
|
+
status: import_protocols6.ScheduledTaskStatus.PENDING,
|
|
4294
|
+
runCount: 0,
|
|
4295
|
+
retryCount: 0,
|
|
4296
|
+
maxRetries: options.maxRetries ?? 0,
|
|
4297
|
+
createdAt: now,
|
|
4298
|
+
updatedAt: now,
|
|
4299
|
+
metadata: options.metadata
|
|
4300
|
+
};
|
|
4301
|
+
await this.storage.save(task);
|
|
4302
|
+
this.scheduleTimer(task);
|
|
4303
|
+
console.log(
|
|
4304
|
+
`[Scheduler] One-time task scheduled: ${taskId}, executes at ${new Date(
|
|
4305
|
+
executeAt
|
|
4306
|
+
).toISOString()}`
|
|
4307
|
+
);
|
|
4308
|
+
return true;
|
|
4309
|
+
}
|
|
4310
|
+
// ===== Cron Task Scheduling =====
|
|
4311
|
+
async scheduleCron(taskId, taskType, payload, options) {
|
|
4312
|
+
if (!this.handlers.has(taskType)) {
|
|
4313
|
+
console.error(
|
|
4314
|
+
`[Scheduler] No handler registered for task type: ${taskType}`
|
|
4315
|
+
);
|
|
4316
|
+
return false;
|
|
4317
|
+
}
|
|
4318
|
+
if (!isValidCronExpression(options.cronExpression)) {
|
|
4319
|
+
console.error(
|
|
4320
|
+
`[Scheduler] Invalid cron expression: ${options.cronExpression}`
|
|
4321
|
+
);
|
|
4322
|
+
return false;
|
|
4323
|
+
}
|
|
4324
|
+
if (await this.has(taskId)) {
|
|
4325
|
+
await this.cancel(taskId);
|
|
4326
|
+
}
|
|
4327
|
+
const now = Date.now();
|
|
4328
|
+
const nextRunAt = getNextCronTime(options.cronExpression, new Date(now));
|
|
4329
|
+
const task = {
|
|
4330
|
+
taskId,
|
|
4331
|
+
taskType,
|
|
4332
|
+
payload,
|
|
4333
|
+
assistantId: options.assistantId,
|
|
4334
|
+
threadId: options.threadId,
|
|
4335
|
+
executionType: import_protocols6.ScheduleExecutionType.CRON,
|
|
4336
|
+
cronExpression: options.cronExpression,
|
|
4337
|
+
timezone: options.timezone,
|
|
4338
|
+
nextRunAt: nextRunAt.getTime(),
|
|
4339
|
+
status: import_protocols6.ScheduledTaskStatus.PENDING,
|
|
4340
|
+
runCount: 0,
|
|
4341
|
+
maxRuns: options.maxRuns,
|
|
4342
|
+
retryCount: 0,
|
|
4343
|
+
maxRetries: options.maxRetries ?? 0,
|
|
4344
|
+
createdAt: now,
|
|
4345
|
+
updatedAt: now,
|
|
4346
|
+
expiresAt: options.expiresAt,
|
|
4347
|
+
metadata: options.metadata
|
|
4348
|
+
};
|
|
4349
|
+
await this.storage.save(task);
|
|
4350
|
+
this.scheduleTimer(task);
|
|
4351
|
+
console.log(
|
|
4352
|
+
`[Scheduler] Cron task scheduled: ${taskId}, expression: ${options.cronExpression}, next run: ${nextRunAt.toISOString()}`
|
|
4353
|
+
);
|
|
4354
|
+
return true;
|
|
4355
|
+
}
|
|
4356
|
+
// ===== Task Management =====
|
|
4357
|
+
async cancel(taskId) {
|
|
4358
|
+
const timer = this.timers.get(taskId);
|
|
4359
|
+
if (timer) {
|
|
4360
|
+
clearTimeout(timer.timerId);
|
|
4361
|
+
this.timers.delete(taskId);
|
|
4362
|
+
}
|
|
4363
|
+
const task = await this.storage.get(taskId);
|
|
4364
|
+
if (task) {
|
|
4365
|
+
await this.storage.update(taskId, {
|
|
4366
|
+
status: import_protocols6.ScheduledTaskStatus.CANCELLED
|
|
4367
|
+
});
|
|
4368
|
+
console.log(`[Scheduler] Task cancelled: ${taskId}`);
|
|
4369
|
+
return true;
|
|
4370
|
+
}
|
|
4371
|
+
return false;
|
|
4372
|
+
}
|
|
4373
|
+
async pause(taskId) {
|
|
4374
|
+
const task = await this.storage.get(taskId);
|
|
4375
|
+
if (!task || task.executionType !== import_protocols6.ScheduleExecutionType.CRON) {
|
|
4376
|
+
console.error(`[Scheduler] Can only pause CRON tasks: ${taskId}`);
|
|
4377
|
+
return false;
|
|
4378
|
+
}
|
|
4379
|
+
if (task.status !== import_protocols6.ScheduledTaskStatus.PENDING) {
|
|
4380
|
+
console.error(`[Scheduler] Can only pause PENDING tasks: ${taskId}`);
|
|
4381
|
+
return false;
|
|
4382
|
+
}
|
|
4383
|
+
const timer = this.timers.get(taskId);
|
|
4384
|
+
if (timer) {
|
|
4385
|
+
clearTimeout(timer.timerId);
|
|
4386
|
+
this.timers.delete(taskId);
|
|
4387
|
+
}
|
|
4388
|
+
await this.storage.update(taskId, {
|
|
4389
|
+
status: import_protocols6.ScheduledTaskStatus.PAUSED
|
|
4390
|
+
});
|
|
4391
|
+
console.log(`[Scheduler] Task paused: ${taskId}`);
|
|
4392
|
+
return true;
|
|
4393
|
+
}
|
|
4394
|
+
async resume(taskId) {
|
|
4395
|
+
const task = await this.storage.get(taskId);
|
|
4396
|
+
if (!task || task.executionType !== import_protocols6.ScheduleExecutionType.CRON) {
|
|
4397
|
+
console.error(`[Scheduler] Can only resume CRON tasks: ${taskId}`);
|
|
4398
|
+
return false;
|
|
4399
|
+
}
|
|
4400
|
+
if (task.status !== import_protocols6.ScheduledTaskStatus.PAUSED) {
|
|
4401
|
+
console.error(`[Scheduler] Can only resume PAUSED tasks: ${taskId}`);
|
|
4402
|
+
return false;
|
|
4403
|
+
}
|
|
4404
|
+
const nextRunAt = getNextCronTime(task.cronExpression);
|
|
4405
|
+
await this.storage.update(taskId, {
|
|
4406
|
+
status: import_protocols6.ScheduledTaskStatus.PENDING,
|
|
4407
|
+
nextRunAt: nextRunAt.getTime()
|
|
4408
|
+
});
|
|
4409
|
+
const updatedTask = await this.storage.get(taskId);
|
|
4410
|
+
if (updatedTask) {
|
|
4411
|
+
this.scheduleTimer(updatedTask);
|
|
4412
|
+
}
|
|
4413
|
+
console.log(`[Scheduler] Task resumed: ${taskId}`);
|
|
4414
|
+
return true;
|
|
4415
|
+
}
|
|
4416
|
+
async has(taskId) {
|
|
4417
|
+
const task = await this.storage.get(taskId);
|
|
4418
|
+
return task !== null;
|
|
4419
|
+
}
|
|
4420
|
+
async getTask(taskId) {
|
|
4421
|
+
return this.storage.get(taskId);
|
|
4422
|
+
}
|
|
4423
|
+
async getRemainingTime(taskId) {
|
|
4424
|
+
const task = await this.storage.get(taskId);
|
|
4425
|
+
if (!task) {
|
|
4426
|
+
return -1;
|
|
4427
|
+
}
|
|
4428
|
+
const targetTime = task.executionType === import_protocols6.ScheduleExecutionType.CRON ? task.nextRunAt : task.executeAt;
|
|
4429
|
+
if (targetTime === void 0) {
|
|
4430
|
+
return -1;
|
|
4431
|
+
}
|
|
4432
|
+
return Math.max(0, targetTime - Date.now());
|
|
4433
|
+
}
|
|
4434
|
+
async getActiveTaskCount() {
|
|
4435
|
+
const tasks = await this.storage.getActiveTasks();
|
|
4436
|
+
return tasks.length;
|
|
4437
|
+
}
|
|
4438
|
+
async getActiveTaskIds() {
|
|
4439
|
+
const tasks = await this.storage.getActiveTasks();
|
|
4440
|
+
return tasks.map((t) => t.taskId);
|
|
4441
|
+
}
|
|
4442
|
+
async cancelAll() {
|
|
4443
|
+
for (const timer of this.timers.values()) {
|
|
4444
|
+
clearTimeout(timer.timerId);
|
|
4445
|
+
}
|
|
4446
|
+
this.timers.clear();
|
|
4447
|
+
const activeTasks = await this.storage.getActiveTasks();
|
|
4448
|
+
for (const task of activeTasks) {
|
|
4449
|
+
await this.storage.update(task.taskId, {
|
|
4450
|
+
status: import_protocols6.ScheduledTaskStatus.CANCELLED
|
|
4451
|
+
});
|
|
4452
|
+
}
|
|
4453
|
+
console.log(`[Scheduler] All tasks cancelled`);
|
|
4454
|
+
}
|
|
4455
|
+
// ===== Recovery =====
|
|
4456
|
+
async restore() {
|
|
4457
|
+
const activeTasks = await this.storage.getActiveTasks();
|
|
4458
|
+
let restored = 0;
|
|
4459
|
+
for (const task of activeTasks) {
|
|
4460
|
+
if (!this.handlers.has(task.taskType)) {
|
|
4461
|
+
console.warn(
|
|
4462
|
+
`[Scheduler] No handler for task type ${task.taskType}, skipping restore for task ${task.taskId}`
|
|
4463
|
+
);
|
|
4464
|
+
continue;
|
|
4465
|
+
}
|
|
4466
|
+
if (task.status === import_protocols6.ScheduledTaskStatus.PAUSED) {
|
|
4467
|
+
continue;
|
|
4468
|
+
}
|
|
4469
|
+
if (task.executionType === import_protocols6.ScheduleExecutionType.ONCE) {
|
|
4470
|
+
if (task.executeAt && task.executeAt <= Date.now()) {
|
|
4471
|
+
console.log(
|
|
4472
|
+
`[Scheduler] Executing overdue one-time task: ${task.taskId}`
|
|
4473
|
+
);
|
|
4474
|
+
this.executeTask(task);
|
|
4475
|
+
} else {
|
|
4476
|
+
this.scheduleTimer(task);
|
|
4477
|
+
}
|
|
4478
|
+
} else if (task.executionType === import_protocols6.ScheduleExecutionType.CRON) {
|
|
4479
|
+
const nextRunAt = getNextCronTime(task.cronExpression);
|
|
4480
|
+
await this.storage.update(task.taskId, {
|
|
4481
|
+
nextRunAt: nextRunAt.getTime()
|
|
4482
|
+
});
|
|
4483
|
+
const updatedTask = await this.storage.get(task.taskId);
|
|
4484
|
+
if (updatedTask) {
|
|
4485
|
+
this.scheduleTimer(updatedTask);
|
|
4486
|
+
}
|
|
4487
|
+
}
|
|
4488
|
+
restored++;
|
|
4489
|
+
}
|
|
4490
|
+
console.log(`[Scheduler] Restored ${restored} tasks`);
|
|
4491
|
+
return restored;
|
|
4492
|
+
}
|
|
4493
|
+
// ===== Storage =====
|
|
4494
|
+
setStorage(storage) {
|
|
4495
|
+
this.storage = storage;
|
|
4496
|
+
}
|
|
4497
|
+
getStorage() {
|
|
4498
|
+
return this.storage;
|
|
4499
|
+
}
|
|
4500
|
+
// ===== Private Methods =====
|
|
4501
|
+
scheduleTimer(task) {
|
|
4502
|
+
const targetTime = task.executionType === import_protocols6.ScheduleExecutionType.CRON ? task.nextRunAt : task.executeAt;
|
|
4503
|
+
if (targetTime === void 0) {
|
|
4504
|
+
console.error(`[Scheduler] No execution time for task: ${task.taskId}`);
|
|
4505
|
+
return;
|
|
4506
|
+
}
|
|
4507
|
+
const delay = Math.max(0, targetTime - Date.now());
|
|
4508
|
+
const timerId = setTimeout(() => {
|
|
4509
|
+
this.executeTask(task);
|
|
4510
|
+
}, delay);
|
|
4511
|
+
this.timers.set(task.taskId, {
|
|
4512
|
+
timerId,
|
|
4513
|
+
taskId: task.taskId
|
|
4514
|
+
});
|
|
4515
|
+
}
|
|
4516
|
+
async executeTask(task) {
|
|
4517
|
+
const handler = this.handlers.get(task.taskType);
|
|
4518
|
+
if (!handler) {
|
|
4519
|
+
console.error(
|
|
4520
|
+
`[Scheduler] No handler for task type: ${task.taskType}, task: ${task.taskId}`
|
|
4521
|
+
);
|
|
4522
|
+
await this.storage.update(task.taskId, {
|
|
4523
|
+
status: import_protocols6.ScheduledTaskStatus.FAILED,
|
|
4524
|
+
lastError: `No handler registered for task type: ${task.taskType}`
|
|
4525
|
+
});
|
|
4526
|
+
return;
|
|
4527
|
+
}
|
|
4528
|
+
await this.storage.update(task.taskId, {
|
|
4529
|
+
status: import_protocols6.ScheduledTaskStatus.RUNNING
|
|
4530
|
+
});
|
|
4531
|
+
console.log(`[Scheduler] Executing task: ${task.taskId}`);
|
|
4532
|
+
try {
|
|
4533
|
+
const latestTask = await this.storage.get(task.taskId);
|
|
4534
|
+
if (!latestTask) {
|
|
4535
|
+
console.error(`[Scheduler] Task not found: ${task.taskId}`);
|
|
4536
|
+
return;
|
|
4537
|
+
}
|
|
4538
|
+
await handler(latestTask.payload, latestTask);
|
|
4539
|
+
const newRunCount = latestTask.runCount + 1;
|
|
4540
|
+
console.log(`[Scheduler] Task completed: ${task.taskId}`);
|
|
4541
|
+
if (task.executionType === import_protocols6.ScheduleExecutionType.ONCE) {
|
|
4542
|
+
await this.storage.update(task.taskId, {
|
|
4543
|
+
status: import_protocols6.ScheduledTaskStatus.COMPLETED,
|
|
4544
|
+
runCount: newRunCount,
|
|
4545
|
+
lastRunAt: Date.now()
|
|
4546
|
+
});
|
|
4547
|
+
this.timers.delete(task.taskId);
|
|
4548
|
+
} else if (task.executionType === import_protocols6.ScheduleExecutionType.CRON) {
|
|
4549
|
+
if (task.maxRuns !== void 0 && newRunCount >= task.maxRuns) {
|
|
4550
|
+
await this.storage.update(task.taskId, {
|
|
4551
|
+
status: import_protocols6.ScheduledTaskStatus.COMPLETED,
|
|
4552
|
+
runCount: newRunCount,
|
|
4553
|
+
lastRunAt: Date.now()
|
|
4554
|
+
});
|
|
4555
|
+
this.timers.delete(task.taskId);
|
|
4556
|
+
console.log(
|
|
4557
|
+
`[Scheduler] Cron task completed (max runs): ${task.taskId}`
|
|
4558
|
+
);
|
|
4559
|
+
return;
|
|
4560
|
+
}
|
|
4561
|
+
if (task.expiresAt !== void 0 && Date.now() >= task.expiresAt) {
|
|
4562
|
+
await this.storage.update(task.taskId, {
|
|
4563
|
+
status: import_protocols6.ScheduledTaskStatus.COMPLETED,
|
|
4564
|
+
runCount: newRunCount,
|
|
4565
|
+
lastRunAt: Date.now()
|
|
4566
|
+
});
|
|
4567
|
+
this.timers.delete(task.taskId);
|
|
4568
|
+
console.log(
|
|
4569
|
+
`[Scheduler] Cron task completed (expired): ${task.taskId}`
|
|
4570
|
+
);
|
|
4571
|
+
return;
|
|
4572
|
+
}
|
|
4573
|
+
const nextRunAt = getNextCronTime(task.cronExpression);
|
|
4574
|
+
await this.storage.update(task.taskId, {
|
|
4575
|
+
status: import_protocols6.ScheduledTaskStatus.PENDING,
|
|
4576
|
+
runCount: newRunCount,
|
|
4577
|
+
lastRunAt: Date.now(),
|
|
4578
|
+
nextRunAt: nextRunAt.getTime(),
|
|
4579
|
+
retryCount: 0
|
|
4580
|
+
// Reset retry count on success
|
|
4581
|
+
});
|
|
4582
|
+
const updatedTask = await this.storage.get(task.taskId);
|
|
4583
|
+
if (updatedTask) {
|
|
4584
|
+
this.scheduleTimer(updatedTask);
|
|
4585
|
+
console.log(
|
|
4586
|
+
`[Scheduler] Next cron run scheduled: ${task.taskId} at ${nextRunAt.toISOString()}`
|
|
4587
|
+
);
|
|
4588
|
+
}
|
|
4589
|
+
}
|
|
4590
|
+
} catch (error) {
|
|
4591
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4592
|
+
console.error(`[Scheduler] Task failed: ${task.taskId}`, error);
|
|
4593
|
+
const latestTask = await this.storage.get(task.taskId);
|
|
4594
|
+
if (!latestTask) return;
|
|
4595
|
+
const newRetryCount = latestTask.retryCount + 1;
|
|
4596
|
+
if (newRetryCount <= latestTask.maxRetries) {
|
|
4597
|
+
console.log(
|
|
4598
|
+
`[Scheduler] Retrying task: ${task.taskId} (attempt ${newRetryCount}/${latestTask.maxRetries})`
|
|
4599
|
+
);
|
|
4600
|
+
await this.storage.update(task.taskId, {
|
|
4601
|
+
status: import_protocols6.ScheduledTaskStatus.PENDING,
|
|
4602
|
+
retryCount: newRetryCount,
|
|
4603
|
+
lastError: errorMessage
|
|
4604
|
+
});
|
|
4605
|
+
const retryDelay = Math.min(
|
|
4606
|
+
1e3 * Math.pow(2, newRetryCount - 1),
|
|
4607
|
+
6e4
|
|
4608
|
+
);
|
|
4609
|
+
const retryTask = await this.storage.get(task.taskId);
|
|
4610
|
+
if (retryTask) {
|
|
4611
|
+
if (task.executionType === import_protocols6.ScheduleExecutionType.ONCE) {
|
|
4612
|
+
retryTask.executeAt = Date.now() + retryDelay;
|
|
4613
|
+
} else {
|
|
4614
|
+
retryTask.nextRunAt = Date.now() + retryDelay;
|
|
4615
|
+
}
|
|
4616
|
+
await this.storage.update(task.taskId, {
|
|
4617
|
+
executeAt: retryTask.executeAt,
|
|
4618
|
+
nextRunAt: retryTask.nextRunAt
|
|
4619
|
+
});
|
|
4620
|
+
this.scheduleTimer(retryTask);
|
|
4621
|
+
}
|
|
4622
|
+
} else {
|
|
4623
|
+
await this.storage.update(task.taskId, {
|
|
4624
|
+
status: import_protocols6.ScheduledTaskStatus.FAILED,
|
|
4625
|
+
retryCount: newRetryCount,
|
|
4626
|
+
lastError: errorMessage
|
|
4627
|
+
});
|
|
4628
|
+
this.timers.delete(task.taskId);
|
|
4629
|
+
console.error(
|
|
4630
|
+
`[Scheduler] Task failed permanently: ${task.taskId} (max retries exceeded)`
|
|
4631
|
+
);
|
|
4632
|
+
}
|
|
4633
|
+
}
|
|
4634
|
+
}
|
|
4635
|
+
};
|
|
4636
|
+
_DefaultScheduleClient.instance = null;
|
|
4637
|
+
var DefaultScheduleClient = _DefaultScheduleClient;
|
|
4638
|
+
|
|
4639
|
+
// src/schedule_lattice/ScheduleLatticeManager.ts
|
|
4640
|
+
var ScheduleLatticeManager = class _ScheduleLatticeManager extends BaseLatticeManager {
|
|
4641
|
+
/**
|
|
4642
|
+
* Get ScheduleLatticeManager singleton instance
|
|
4643
|
+
*/
|
|
4644
|
+
static getInstance() {
|
|
4645
|
+
if (!_ScheduleLatticeManager._instance) {
|
|
4646
|
+
_ScheduleLatticeManager._instance = new _ScheduleLatticeManager();
|
|
4647
|
+
}
|
|
4648
|
+
return _ScheduleLatticeManager._instance;
|
|
4649
|
+
}
|
|
4650
|
+
/**
|
|
4651
|
+
* Get Lattice type prefix
|
|
4652
|
+
*/
|
|
4653
|
+
getLatticeType() {
|
|
4654
|
+
return "schedules";
|
|
4655
|
+
}
|
|
4656
|
+
/**
|
|
4657
|
+
* Register schedule Lattice
|
|
4658
|
+
* @param key Lattice key name
|
|
4659
|
+
* @param config Schedule configuration
|
|
4660
|
+
* @param client Optional schedule client. If not provided, will create based on config type.
|
|
4661
|
+
*/
|
|
4662
|
+
registerLattice(key, config, client) {
|
|
4663
|
+
let scheduleClient;
|
|
4664
|
+
if (client) {
|
|
4665
|
+
scheduleClient = client;
|
|
4666
|
+
if (config.storage) {
|
|
4667
|
+
scheduleClient.setStorage(config.storage);
|
|
4668
|
+
}
|
|
4669
|
+
} else {
|
|
4670
|
+
let storage;
|
|
4671
|
+
if (config.storage) {
|
|
4672
|
+
storage = config.storage;
|
|
4673
|
+
} else {
|
|
4674
|
+
storage = new MemoryScheduleStorage();
|
|
4675
|
+
}
|
|
4676
|
+
if (config.type === import_protocols7.ScheduleType.MEMORY) {
|
|
4677
|
+
scheduleClient = new DefaultScheduleClient(storage);
|
|
4678
|
+
} else if (config.type === import_protocols7.ScheduleType.POSTGRES) {
|
|
4679
|
+
if (!config.storage) {
|
|
4680
|
+
throw new Error(
|
|
4681
|
+
`PostgreSQL schedule storage must be provided. Please install @axiom-lattice/pg-stores and pass the storage in config.storage.`
|
|
4682
|
+
);
|
|
4683
|
+
}
|
|
4684
|
+
scheduleClient = new DefaultScheduleClient(storage);
|
|
4685
|
+
} else {
|
|
4686
|
+
scheduleClient = new DefaultScheduleClient(storage);
|
|
4687
|
+
}
|
|
4688
|
+
}
|
|
4689
|
+
const scheduleLattice = {
|
|
4690
|
+
key,
|
|
4691
|
+
config,
|
|
4692
|
+
client: scheduleClient,
|
|
4693
|
+
// Handler registration
|
|
4694
|
+
registerHandler: (taskType, handler) => {
|
|
4695
|
+
scheduleClient.registerHandler(taskType, handler);
|
|
4696
|
+
},
|
|
4697
|
+
unregisterHandler: (taskType) => {
|
|
4698
|
+
return scheduleClient.unregisterHandler(taskType);
|
|
4699
|
+
},
|
|
4700
|
+
hasHandler: (taskType) => {
|
|
4701
|
+
return scheduleClient.hasHandler(taskType);
|
|
4702
|
+
},
|
|
4703
|
+
getHandlerTypes: () => {
|
|
4704
|
+
return scheduleClient.getHandlerTypes();
|
|
4705
|
+
},
|
|
4706
|
+
// One-time task scheduling
|
|
4707
|
+
scheduleOnce: async (taskId, taskType, payload, options) => {
|
|
4708
|
+
return scheduleClient.scheduleOnce(taskId, taskType, payload, options);
|
|
4709
|
+
},
|
|
4710
|
+
// Cron task scheduling
|
|
4711
|
+
scheduleCron: async (taskId, taskType, payload, options) => {
|
|
4712
|
+
return scheduleClient.scheduleCron(taskId, taskType, payload, options);
|
|
4713
|
+
},
|
|
4714
|
+
// Task management
|
|
4715
|
+
cancel: async (taskId) => {
|
|
4716
|
+
return scheduleClient.cancel(taskId);
|
|
4717
|
+
},
|
|
4718
|
+
pause: async (taskId) => {
|
|
4719
|
+
return scheduleClient.pause(taskId);
|
|
4720
|
+
},
|
|
4721
|
+
resume: async (taskId) => {
|
|
4722
|
+
return scheduleClient.resume(taskId);
|
|
4723
|
+
},
|
|
4724
|
+
has: async (taskId) => {
|
|
4725
|
+
return scheduleClient.has(taskId);
|
|
4726
|
+
},
|
|
4727
|
+
getTask: async (taskId) => {
|
|
4728
|
+
return scheduleClient.getTask(taskId);
|
|
4729
|
+
},
|
|
4730
|
+
getRemainingTime: async (taskId) => {
|
|
4731
|
+
return scheduleClient.getRemainingTime(taskId);
|
|
4732
|
+
},
|
|
4733
|
+
getActiveTaskCount: async () => {
|
|
4734
|
+
return scheduleClient.getActiveTaskCount();
|
|
4735
|
+
},
|
|
4736
|
+
getActiveTaskIds: async () => {
|
|
4737
|
+
return scheduleClient.getActiveTaskIds();
|
|
4738
|
+
},
|
|
4739
|
+
cancelAll: async () => {
|
|
4740
|
+
return scheduleClient.cancelAll();
|
|
4741
|
+
},
|
|
4742
|
+
// Recovery
|
|
4743
|
+
restore: async () => {
|
|
4744
|
+
return scheduleClient.restore();
|
|
4745
|
+
}
|
|
4746
|
+
};
|
|
4747
|
+
this.register(key, scheduleLattice);
|
|
4748
|
+
}
|
|
4749
|
+
/**
|
|
4750
|
+
* Get ScheduleLattice
|
|
4751
|
+
* @param key Lattice key name
|
|
4752
|
+
*/
|
|
4753
|
+
getScheduleLattice(key) {
|
|
4754
|
+
const scheduleLattice = this.get(key);
|
|
4755
|
+
if (!scheduleLattice) {
|
|
4756
|
+
throw new Error(`ScheduleLattice ${key} not found`);
|
|
4757
|
+
}
|
|
4758
|
+
return scheduleLattice;
|
|
4759
|
+
}
|
|
4760
|
+
/**
|
|
4761
|
+
* Get all Lattices
|
|
4762
|
+
*/
|
|
4763
|
+
getAllLattices() {
|
|
4764
|
+
return this.getAll();
|
|
4765
|
+
}
|
|
4766
|
+
/**
|
|
4767
|
+
* Check if Lattice exists
|
|
4768
|
+
* @param key Lattice key name
|
|
4769
|
+
*/
|
|
4770
|
+
hasLattice(key) {
|
|
4771
|
+
return this.has(key);
|
|
4772
|
+
}
|
|
4773
|
+
/**
|
|
4774
|
+
* Remove Lattice
|
|
4775
|
+
* @param key Lattice key name
|
|
4776
|
+
*/
|
|
4777
|
+
removeLattice(key) {
|
|
4778
|
+
return this.remove(key);
|
|
4779
|
+
}
|
|
4780
|
+
/**
|
|
4781
|
+
* Clear all Lattices
|
|
4782
|
+
*/
|
|
4783
|
+
clearLattices() {
|
|
4784
|
+
this.clear();
|
|
4785
|
+
}
|
|
4786
|
+
/**
|
|
4787
|
+
* Get Lattice count
|
|
4788
|
+
*/
|
|
4789
|
+
getLatticeCount() {
|
|
4790
|
+
return this.count();
|
|
4791
|
+
}
|
|
4792
|
+
/**
|
|
4793
|
+
* Get Lattice key list
|
|
4794
|
+
*/
|
|
4795
|
+
getLatticeKeys() {
|
|
4796
|
+
return this.keys();
|
|
4797
|
+
}
|
|
4798
|
+
};
|
|
4799
|
+
var scheduleLatticeManager = ScheduleLatticeManager.getInstance();
|
|
4800
|
+
var registerScheduleLattice = (key, config, client) => scheduleLatticeManager.registerLattice(key, config, client);
|
|
4801
|
+
var getScheduleLattice = (key) => scheduleLatticeManager.getScheduleLattice(key);
|
|
4802
|
+
|
|
3278
4803
|
// src/store_lattice/InMemoryThreadStore.ts
|
|
3279
4804
|
var InMemoryThreadStore = class {
|
|
3280
4805
|
constructor() {
|
|
@@ -3778,6 +5303,7 @@ var Protocols = __toESM(require("@axiom-lattice/protocols"));
|
|
|
3778
5303
|
AgentType,
|
|
3779
5304
|
ChunkBuffer,
|
|
3780
5305
|
ChunkBufferLatticeManager,
|
|
5306
|
+
DefaultScheduleClient,
|
|
3781
5307
|
EmbeddingsLatticeManager,
|
|
3782
5308
|
GraphBuildOptions,
|
|
3783
5309
|
InMemoryAssistantStore,
|
|
@@ -3785,15 +5311,20 @@ var Protocols = __toESM(require("@axiom-lattice/protocols"));
|
|
|
3785
5311
|
InMemoryThreadStore,
|
|
3786
5312
|
MemoryLatticeManager,
|
|
3787
5313
|
MemoryQueueClient,
|
|
5314
|
+
MemoryScheduleStorage,
|
|
3788
5315
|
MemoryType,
|
|
3789
5316
|
ModelLatticeManager,
|
|
5317
|
+
PostgresDatabase,
|
|
3790
5318
|
Protocols,
|
|
3791
5319
|
QueueLatticeManager,
|
|
5320
|
+
ScheduleLatticeManager,
|
|
5321
|
+
SqlDatabaseManager,
|
|
3792
5322
|
StoreLatticeManager,
|
|
3793
5323
|
ThreadStatus,
|
|
3794
5324
|
ToolLatticeManager,
|
|
3795
5325
|
VectorStoreLatticeManager,
|
|
3796
5326
|
agentLatticeManager,
|
|
5327
|
+
describeCronExpression,
|
|
3797
5328
|
embeddingsLatticeManager,
|
|
3798
5329
|
eventBus,
|
|
3799
5330
|
eventBusDefault,
|
|
@@ -3807,7 +5338,9 @@ var Protocols = __toESM(require("@axiom-lattice/protocols"));
|
|
|
3807
5338
|
getEmbeddingsClient,
|
|
3808
5339
|
getEmbeddingsLattice,
|
|
3809
5340
|
getModelLattice,
|
|
5341
|
+
getNextCronTime,
|
|
3810
5342
|
getQueueLattice,
|
|
5343
|
+
getScheduleLattice,
|
|
3811
5344
|
getStoreLattice,
|
|
3812
5345
|
getToolClient,
|
|
3813
5346
|
getToolDefinition,
|
|
@@ -3815,7 +5348,9 @@ var Protocols = __toESM(require("@axiom-lattice/protocols"));
|
|
|
3815
5348
|
getVectorStoreClient,
|
|
3816
5349
|
getVectorStoreLattice,
|
|
3817
5350
|
hasChunkBuffer,
|
|
5351
|
+
isValidCronExpression,
|
|
3818
5352
|
modelLatticeManager,
|
|
5353
|
+
parseCronExpression,
|
|
3819
5354
|
queueLatticeManager,
|
|
3820
5355
|
registerAgentLattice,
|
|
3821
5356
|
registerAgentLattices,
|
|
@@ -3824,9 +5359,12 @@ var Protocols = __toESM(require("@axiom-lattice/protocols"));
|
|
|
3824
5359
|
registerEmbeddingsLattice,
|
|
3825
5360
|
registerModelLattice,
|
|
3826
5361
|
registerQueueLattice,
|
|
5362
|
+
registerScheduleLattice,
|
|
3827
5363
|
registerStoreLattice,
|
|
3828
5364
|
registerToolLattice,
|
|
3829
5365
|
registerVectorStoreLattice,
|
|
5366
|
+
scheduleLatticeManager,
|
|
5367
|
+
sqlDatabaseManager,
|
|
3830
5368
|
storeLatticeManager,
|
|
3831
5369
|
toolLatticeManager,
|
|
3832
5370
|
validateAgentInput,
|