@axiom-lattice/core 2.1.13 → 2.1.15
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 +444 -40
- package/dist/index.d.ts +444 -40
- package/dist/index.js +1751 -150
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1769 -175
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -2
package/dist/index.mjs
CHANGED
|
@@ -588,6 +588,538 @@ registerToolLattice(
|
|
|
588
588
|
}
|
|
589
589
|
);
|
|
590
590
|
|
|
591
|
+
// src/tool_lattice/sql/list_tables_sql.ts
|
|
592
|
+
import z3 from "zod";
|
|
593
|
+
|
|
594
|
+
// src/tool_lattice/sql/SqlDatabaseManager.ts
|
|
595
|
+
var PostgresDatabase = class {
|
|
596
|
+
constructor(config) {
|
|
597
|
+
// pg.Pool
|
|
598
|
+
this.connected = false;
|
|
599
|
+
this.config = config;
|
|
600
|
+
}
|
|
601
|
+
async connect() {
|
|
602
|
+
if (this.connected) return;
|
|
603
|
+
try {
|
|
604
|
+
const { Pool } = await import("pg");
|
|
605
|
+
const poolConfig = this.config.connectionString ? { connectionString: this.config.connectionString } : {
|
|
606
|
+
host: this.config.host || "localhost",
|
|
607
|
+
port: this.config.port || 5432,
|
|
608
|
+
database: this.config.database,
|
|
609
|
+
user: this.config.user,
|
|
610
|
+
password: this.config.password,
|
|
611
|
+
ssl: this.config.ssl ? { rejectUnauthorized: false } : void 0
|
|
612
|
+
};
|
|
613
|
+
this.pool = new Pool(poolConfig);
|
|
614
|
+
const client = await this.pool.connect();
|
|
615
|
+
client.release();
|
|
616
|
+
this.connected = true;
|
|
617
|
+
} catch (error) {
|
|
618
|
+
throw new Error(`Failed to connect to PostgreSQL: ${error}`);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
async disconnect() {
|
|
622
|
+
if (this.pool) {
|
|
623
|
+
await this.pool.end();
|
|
624
|
+
this.connected = false;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
async listTables() {
|
|
628
|
+
await this.ensureConnected();
|
|
629
|
+
const query = `
|
|
630
|
+
SELECT table_name, table_schema
|
|
631
|
+
FROM information_schema.tables
|
|
632
|
+
WHERE table_schema NOT IN ('pg_catalog', 'information_schema')
|
|
633
|
+
AND table_type = 'BASE TABLE'
|
|
634
|
+
ORDER BY table_schema, table_name;
|
|
635
|
+
`;
|
|
636
|
+
const result = await this.pool.query(query);
|
|
637
|
+
return result.rows.map((row) => ({
|
|
638
|
+
name: row.table_name,
|
|
639
|
+
schema: row.table_schema
|
|
640
|
+
}));
|
|
641
|
+
}
|
|
642
|
+
async getTableInfo(tables) {
|
|
643
|
+
await this.ensureConnected();
|
|
644
|
+
const schemas = [];
|
|
645
|
+
for (const tableName of tables) {
|
|
646
|
+
const columnQuery = `
|
|
647
|
+
SELECT
|
|
648
|
+
c.column_name,
|
|
649
|
+
c.data_type,
|
|
650
|
+
c.is_nullable,
|
|
651
|
+
c.column_default,
|
|
652
|
+
CASE WHEN pk.column_name IS NOT NULL THEN true ELSE false END as is_primary_key,
|
|
653
|
+
CASE WHEN fk.column_name IS NOT NULL THEN true ELSE false END as is_foreign_key,
|
|
654
|
+
fk.foreign_table_name,
|
|
655
|
+
fk.foreign_column_name
|
|
656
|
+
FROM information_schema.columns c
|
|
657
|
+
LEFT JOIN (
|
|
658
|
+
SELECT ku.column_name, ku.table_name
|
|
659
|
+
FROM information_schema.table_constraints tc
|
|
660
|
+
JOIN information_schema.key_column_usage ku
|
|
661
|
+
ON tc.constraint_name = ku.constraint_name
|
|
662
|
+
WHERE tc.constraint_type = 'PRIMARY KEY'
|
|
663
|
+
) pk ON c.column_name = pk.column_name AND c.table_name = pk.table_name
|
|
664
|
+
LEFT JOIN (
|
|
665
|
+
SELECT
|
|
666
|
+
kcu.column_name,
|
|
667
|
+
kcu.table_name,
|
|
668
|
+
ccu.table_name as foreign_table_name,
|
|
669
|
+
ccu.column_name as foreign_column_name
|
|
670
|
+
FROM information_schema.table_constraints tc
|
|
671
|
+
JOIN information_schema.key_column_usage kcu
|
|
672
|
+
ON tc.constraint_name = kcu.constraint_name
|
|
673
|
+
JOIN information_schema.constraint_column_usage ccu
|
|
674
|
+
ON tc.constraint_name = ccu.constraint_name
|
|
675
|
+
WHERE tc.constraint_type = 'FOREIGN KEY'
|
|
676
|
+
) fk ON c.column_name = fk.column_name AND c.table_name = fk.table_name
|
|
677
|
+
WHERE c.table_name = $1
|
|
678
|
+
ORDER BY c.ordinal_position;
|
|
679
|
+
`;
|
|
680
|
+
const columnResult = await this.pool.query(columnQuery, [tableName]);
|
|
681
|
+
const columns = columnResult.rows.map((row) => ({
|
|
682
|
+
name: row.column_name,
|
|
683
|
+
type: row.data_type,
|
|
684
|
+
nullable: row.is_nullable === "YES",
|
|
685
|
+
default: row.column_default,
|
|
686
|
+
isPrimaryKey: row.is_primary_key,
|
|
687
|
+
isForeignKey: row.is_foreign_key,
|
|
688
|
+
foreignKeyRef: row.is_foreign_key ? `${row.foreign_table_name}.${row.foreign_column_name}` : void 0
|
|
689
|
+
}));
|
|
690
|
+
let sampleRows = [];
|
|
691
|
+
try {
|
|
692
|
+
const sampleQuery = `SELECT * FROM "${tableName}" LIMIT 3`;
|
|
693
|
+
const sampleResult = await this.pool.query(sampleQuery);
|
|
694
|
+
sampleRows = sampleResult.rows;
|
|
695
|
+
} catch {
|
|
696
|
+
}
|
|
697
|
+
schemas.push({
|
|
698
|
+
tableName,
|
|
699
|
+
columns,
|
|
700
|
+
sampleRows
|
|
701
|
+
});
|
|
702
|
+
}
|
|
703
|
+
return schemas;
|
|
704
|
+
}
|
|
705
|
+
async executeQuery(query) {
|
|
706
|
+
await this.ensureConnected();
|
|
707
|
+
const result = await this.pool.query(query);
|
|
708
|
+
return {
|
|
709
|
+
rows: result.rows,
|
|
710
|
+
rowCount: result.rowCount || result.rows.length,
|
|
711
|
+
fields: result.fields?.map((f) => f.name)
|
|
712
|
+
};
|
|
713
|
+
}
|
|
714
|
+
getDatabaseType() {
|
|
715
|
+
return "postgres";
|
|
716
|
+
}
|
|
717
|
+
async ensureConnected() {
|
|
718
|
+
if (!this.connected) {
|
|
719
|
+
await this.connect();
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
};
|
|
723
|
+
var SqlDatabaseManager = class _SqlDatabaseManager {
|
|
724
|
+
constructor() {
|
|
725
|
+
this.databases = /* @__PURE__ */ new Map();
|
|
726
|
+
this.defaultDatabaseKey = null;
|
|
727
|
+
}
|
|
728
|
+
/**
|
|
729
|
+
* Get the singleton instance
|
|
730
|
+
*/
|
|
731
|
+
static getInstance() {
|
|
732
|
+
if (!_SqlDatabaseManager.instance) {
|
|
733
|
+
_SqlDatabaseManager.instance = new _SqlDatabaseManager();
|
|
734
|
+
}
|
|
735
|
+
return _SqlDatabaseManager.instance;
|
|
736
|
+
}
|
|
737
|
+
/**
|
|
738
|
+
* Register a database connection
|
|
739
|
+
* @param key - Unique identifier for the database
|
|
740
|
+
* @param config - Database configuration
|
|
741
|
+
*/
|
|
742
|
+
registerDatabase(key, config) {
|
|
743
|
+
let database;
|
|
744
|
+
switch (config.type) {
|
|
745
|
+
case "postgres":
|
|
746
|
+
database = new PostgresDatabase(config);
|
|
747
|
+
break;
|
|
748
|
+
case "mysql":
|
|
749
|
+
throw new Error("MySQL support not yet implemented");
|
|
750
|
+
case "sqlite":
|
|
751
|
+
throw new Error("SQLite support not yet implemented");
|
|
752
|
+
default:
|
|
753
|
+
throw new Error(`Unsupported database type: ${config.type}`);
|
|
754
|
+
}
|
|
755
|
+
this.databases.set(key, database);
|
|
756
|
+
if (this.defaultDatabaseKey === null) {
|
|
757
|
+
this.defaultDatabaseKey = key;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
/**
|
|
761
|
+
* Set the default database
|
|
762
|
+
* @param key - Database key to set as default
|
|
763
|
+
*/
|
|
764
|
+
setDefaultDatabase(key) {
|
|
765
|
+
if (!this.databases.has(key)) {
|
|
766
|
+
throw new Error(`Database '${key}' not found`);
|
|
767
|
+
}
|
|
768
|
+
this.defaultDatabaseKey = key;
|
|
769
|
+
}
|
|
770
|
+
/**
|
|
771
|
+
* Get a database by key
|
|
772
|
+
* @param key - Database key (optional, uses default if not provided)
|
|
773
|
+
*/
|
|
774
|
+
getDatabase(key) {
|
|
775
|
+
const dbKey = key || this.defaultDatabaseKey;
|
|
776
|
+
if (!dbKey) {
|
|
777
|
+
throw new Error("No database registered");
|
|
778
|
+
}
|
|
779
|
+
const database = this.databases.get(dbKey);
|
|
780
|
+
if (!database) {
|
|
781
|
+
throw new Error(`Database '${dbKey}' not found`);
|
|
782
|
+
}
|
|
783
|
+
return database;
|
|
784
|
+
}
|
|
785
|
+
/**
|
|
786
|
+
* Check if a database is registered
|
|
787
|
+
* @param key - Database key
|
|
788
|
+
*/
|
|
789
|
+
hasDatabase(key) {
|
|
790
|
+
return this.databases.has(key);
|
|
791
|
+
}
|
|
792
|
+
/**
|
|
793
|
+
* Get all registered database keys
|
|
794
|
+
*/
|
|
795
|
+
getDatabaseKeys() {
|
|
796
|
+
return Array.from(this.databases.keys());
|
|
797
|
+
}
|
|
798
|
+
/**
|
|
799
|
+
* Remove a database connection
|
|
800
|
+
* @param key - Database key
|
|
801
|
+
*/
|
|
802
|
+
async removeDatabase(key) {
|
|
803
|
+
const database = this.databases.get(key);
|
|
804
|
+
if (database) {
|
|
805
|
+
await database.disconnect();
|
|
806
|
+
this.databases.delete(key);
|
|
807
|
+
if (this.defaultDatabaseKey === key) {
|
|
808
|
+
this.defaultDatabaseKey = this.databases.size > 0 ? this.databases.keys().next().value || null : null;
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
/**
|
|
813
|
+
* Disconnect all databases
|
|
814
|
+
*/
|
|
815
|
+
async disconnectAll() {
|
|
816
|
+
for (const database of this.databases.values()) {
|
|
817
|
+
await database.disconnect();
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
};
|
|
821
|
+
var sqlDatabaseManager = SqlDatabaseManager.getInstance();
|
|
822
|
+
|
|
823
|
+
// src/tool_lattice/sql/list_tables_sql.ts
|
|
824
|
+
function getDatabaseKeyFromConfig(config) {
|
|
825
|
+
const runConfig = config.configurable?.runConfig;
|
|
826
|
+
return runConfig?.databaseKey;
|
|
827
|
+
}
|
|
828
|
+
registerToolLattice(
|
|
829
|
+
"list_tables_sql",
|
|
830
|
+
{
|
|
831
|
+
name: "list_tables_sql",
|
|
832
|
+
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.",
|
|
833
|
+
needUserApprove: false,
|
|
834
|
+
schema: z3.object({})
|
|
835
|
+
},
|
|
836
|
+
async (_input, config) => {
|
|
837
|
+
try {
|
|
838
|
+
const databaseKey = getDatabaseKeyFromConfig(config);
|
|
839
|
+
const database = sqlDatabaseManager.getDatabase(databaseKey);
|
|
840
|
+
const tables = await database.listTables();
|
|
841
|
+
if (tables.length === 0) {
|
|
842
|
+
return "No tables found in the database.";
|
|
843
|
+
}
|
|
844
|
+
const tableNames = tables.map(
|
|
845
|
+
(t) => t.schema && t.schema !== "public" ? `${t.schema}.${t.name}` : t.name
|
|
846
|
+
);
|
|
847
|
+
return tableNames.join(", ");
|
|
848
|
+
} catch (error) {
|
|
849
|
+
return `Error listing tables: ${error instanceof Error ? error.message : String(error)}`;
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
);
|
|
853
|
+
|
|
854
|
+
// src/tool_lattice/sql/info_sql.ts
|
|
855
|
+
import z4 from "zod";
|
|
856
|
+
function getDatabaseKeyFromConfig2(config) {
|
|
857
|
+
const runConfig = config.configurable?.runConfig;
|
|
858
|
+
return runConfig?.databaseKey;
|
|
859
|
+
}
|
|
860
|
+
function formatTableSchema(schema) {
|
|
861
|
+
const lines = [];
|
|
862
|
+
lines.push(`
|
|
863
|
+
Table: ${schema.tableName}`);
|
|
864
|
+
lines.push("-".repeat(40));
|
|
865
|
+
lines.push("Columns:");
|
|
866
|
+
for (const col of schema.columns) {
|
|
867
|
+
const constraints = [];
|
|
868
|
+
if (col.isPrimaryKey) constraints.push("PRIMARY KEY");
|
|
869
|
+
if (col.isForeignKey && col.foreignKeyRef)
|
|
870
|
+
constraints.push(`FK -> ${col.foreignKeyRef}`);
|
|
871
|
+
if (!col.nullable) constraints.push("NOT NULL");
|
|
872
|
+
const constraintStr = constraints.length > 0 ? ` [${constraints.join(", ")}]` : "";
|
|
873
|
+
lines.push(` - ${col.name}: ${col.type}${constraintStr}`);
|
|
874
|
+
}
|
|
875
|
+
if (schema.sampleRows && schema.sampleRows.length > 0) {
|
|
876
|
+
lines.push("\nSample Rows (up to 3):");
|
|
877
|
+
for (const row of schema.sampleRows) {
|
|
878
|
+
const rowStr = Object.entries(row).map(([k, v]) => `${k}=${JSON.stringify(v)}`).join(", ");
|
|
879
|
+
lines.push(` ${rowStr}`);
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
return lines.join("\n");
|
|
883
|
+
}
|
|
884
|
+
registerToolLattice(
|
|
885
|
+
"info_sql",
|
|
886
|
+
{
|
|
887
|
+
name: "info_sql",
|
|
888
|
+
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.",
|
|
889
|
+
needUserApprove: false,
|
|
890
|
+
schema: z4.object({
|
|
891
|
+
tables: z4.string().describe(
|
|
892
|
+
"Comma-separated list of table names to get information for. Example: 'users, orders, products'"
|
|
893
|
+
)
|
|
894
|
+
})
|
|
895
|
+
},
|
|
896
|
+
async ({
|
|
897
|
+
tables
|
|
898
|
+
}, config) => {
|
|
899
|
+
try {
|
|
900
|
+
const databaseKey = getDatabaseKeyFromConfig2(config);
|
|
901
|
+
const database = sqlDatabaseManager.getDatabase(databaseKey);
|
|
902
|
+
const tableNames = tables.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
903
|
+
if (tableNames.length === 0) {
|
|
904
|
+
return "Error: No table names provided. Please provide a comma-separated list of table names.";
|
|
905
|
+
}
|
|
906
|
+
const schemas = await database.getTableInfo(tableNames);
|
|
907
|
+
if (schemas.length === 0) {
|
|
908
|
+
return `No schema information found for tables: ${tableNames.join(", ")}`;
|
|
909
|
+
}
|
|
910
|
+
const output = schemas.map(formatTableSchema).join("\n");
|
|
911
|
+
return output;
|
|
912
|
+
} catch (error) {
|
|
913
|
+
return `Error getting table info: ${error instanceof Error ? error.message : String(error)}`;
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
);
|
|
917
|
+
|
|
918
|
+
// src/tool_lattice/sql/query_sql.ts
|
|
919
|
+
import z5 from "zod";
|
|
920
|
+
function getDatabaseKeyFromConfig3(config) {
|
|
921
|
+
const runConfig = config.configurable?.runConfig;
|
|
922
|
+
return runConfig?.databaseKey;
|
|
923
|
+
}
|
|
924
|
+
function formatQueryResult(rows, fields) {
|
|
925
|
+
if (rows.length === 0) {
|
|
926
|
+
return "Query executed successfully. No rows returned.";
|
|
927
|
+
}
|
|
928
|
+
const lines = [];
|
|
929
|
+
const columns = fields || Object.keys(rows[0]);
|
|
930
|
+
lines.push(columns.join(" | "));
|
|
931
|
+
lines.push("-".repeat(columns.join(" | ").length));
|
|
932
|
+
const maxRows = 50;
|
|
933
|
+
const displayRows = rows.slice(0, maxRows);
|
|
934
|
+
for (const row of displayRows) {
|
|
935
|
+
const values = columns.map((col) => {
|
|
936
|
+
const val = row[col];
|
|
937
|
+
if (val === null) return "NULL";
|
|
938
|
+
if (typeof val === "object") return JSON.stringify(val);
|
|
939
|
+
return String(val);
|
|
940
|
+
});
|
|
941
|
+
lines.push(values.join(" | "));
|
|
942
|
+
}
|
|
943
|
+
if (rows.length > maxRows) {
|
|
944
|
+
lines.push(`
|
|
945
|
+
... (${rows.length - maxRows} more rows not shown)`);
|
|
946
|
+
}
|
|
947
|
+
lines.push(`
|
|
948
|
+
Total rows: ${rows.length}`);
|
|
949
|
+
return lines.join("\n");
|
|
950
|
+
}
|
|
951
|
+
registerToolLattice(
|
|
952
|
+
"query_sql",
|
|
953
|
+
{
|
|
954
|
+
name: "query_sql",
|
|
955
|
+
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.",
|
|
956
|
+
needUserApprove: false,
|
|
957
|
+
// SQL queries should require user approval for safety
|
|
958
|
+
schema: z5.object({
|
|
959
|
+
query: z5.string().describe(
|
|
960
|
+
"The SQL query to execute. Should be a valid SELECT, INSERT, UPDATE, or DELETE statement."
|
|
961
|
+
)
|
|
962
|
+
})
|
|
963
|
+
},
|
|
964
|
+
async ({
|
|
965
|
+
query
|
|
966
|
+
}, config) => {
|
|
967
|
+
try {
|
|
968
|
+
const databaseKey = getDatabaseKeyFromConfig3(config);
|
|
969
|
+
const database = sqlDatabaseManager.getDatabase(databaseKey);
|
|
970
|
+
const trimmedQuery = query.trim();
|
|
971
|
+
if (!trimmedQuery) {
|
|
972
|
+
return "Error: Empty query provided. Please provide a valid SQL query.";
|
|
973
|
+
}
|
|
974
|
+
const result = await database.executeQuery(trimmedQuery);
|
|
975
|
+
return formatQueryResult(result.rows, result.fields);
|
|
976
|
+
} catch (error) {
|
|
977
|
+
return `Error executing query: ${error instanceof Error ? error.message : String(error)}`;
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
);
|
|
981
|
+
|
|
982
|
+
// src/tool_lattice/sql/query_checker_sql.ts
|
|
983
|
+
import z6 from "zod";
|
|
984
|
+
function getDatabaseKeyFromConfig4(config) {
|
|
985
|
+
const runConfig = config.configurable?.runConfig;
|
|
986
|
+
return runConfig?.databaseKey;
|
|
987
|
+
}
|
|
988
|
+
var DANGEROUS_KEYWORDS = [
|
|
989
|
+
"DROP",
|
|
990
|
+
"TRUNCATE",
|
|
991
|
+
"DELETE",
|
|
992
|
+
"ALTER",
|
|
993
|
+
"CREATE",
|
|
994
|
+
"GRANT",
|
|
995
|
+
"REVOKE"
|
|
996
|
+
];
|
|
997
|
+
function checkSyntax(query) {
|
|
998
|
+
const issues = [];
|
|
999
|
+
const upperQuery = query.toUpperCase();
|
|
1000
|
+
let parenCount = 0;
|
|
1001
|
+
for (const char of query) {
|
|
1002
|
+
if (char === "(") parenCount++;
|
|
1003
|
+
if (char === ")") parenCount--;
|
|
1004
|
+
if (parenCount < 0) {
|
|
1005
|
+
issues.push("Unbalanced parentheses: found closing ) without matching (");
|
|
1006
|
+
break;
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
if (parenCount > 0) {
|
|
1010
|
+
issues.push("Unbalanced parentheses: missing closing )");
|
|
1011
|
+
}
|
|
1012
|
+
const singleQuotes = (query.match(/'/g) || []).length;
|
|
1013
|
+
if (singleQuotes % 2 !== 0) {
|
|
1014
|
+
issues.push("Unbalanced single quotes");
|
|
1015
|
+
}
|
|
1016
|
+
if (upperQuery.includes("SELECT *") && upperQuery.includes("JOIN")) {
|
|
1017
|
+
issues.push(
|
|
1018
|
+
"Warning: SELECT * with JOIN may return duplicate column names. Consider specifying columns explicitly."
|
|
1019
|
+
);
|
|
1020
|
+
}
|
|
1021
|
+
if ((upperQuery.includes("UPDATE ") || upperQuery.includes("DELETE FROM ")) && !upperQuery.includes("WHERE ")) {
|
|
1022
|
+
issues.push(
|
|
1023
|
+
"Warning: UPDATE or DELETE without WHERE clause will affect all rows in the table."
|
|
1024
|
+
);
|
|
1025
|
+
}
|
|
1026
|
+
if (upperQuery.includes("GROUP BY") && !upperQuery.match(/\b(COUNT|SUM|AVG|MAX|MIN|ARRAY_AGG|STRING_AGG)\s*\(/i)) {
|
|
1027
|
+
issues.push(
|
|
1028
|
+
"Warning: GROUP BY used but no aggregate functions found. Make sure all non-aggregated columns are in GROUP BY."
|
|
1029
|
+
);
|
|
1030
|
+
}
|
|
1031
|
+
return issues;
|
|
1032
|
+
}
|
|
1033
|
+
function checkDangerousOperations(query) {
|
|
1034
|
+
const warnings = [];
|
|
1035
|
+
const upperQuery = query.toUpperCase();
|
|
1036
|
+
for (const keyword of DANGEROUS_KEYWORDS) {
|
|
1037
|
+
if (upperQuery.includes(keyword)) {
|
|
1038
|
+
warnings.push(
|
|
1039
|
+
`Warning: Query contains potentially dangerous operation: ${keyword}`
|
|
1040
|
+
);
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
return warnings;
|
|
1044
|
+
}
|
|
1045
|
+
registerToolLattice(
|
|
1046
|
+
"query_checker_sql",
|
|
1047
|
+
{
|
|
1048
|
+
name: "query_checker_sql",
|
|
1049
|
+
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.",
|
|
1050
|
+
needUserApprove: false,
|
|
1051
|
+
schema: z6.object({
|
|
1052
|
+
query: z6.string().describe("The SQL query to check and validate.")
|
|
1053
|
+
})
|
|
1054
|
+
},
|
|
1055
|
+
async ({
|
|
1056
|
+
query
|
|
1057
|
+
}, config) => {
|
|
1058
|
+
try {
|
|
1059
|
+
const trimmedQuery = query.trim();
|
|
1060
|
+
if (!trimmedQuery) {
|
|
1061
|
+
return "Error: Empty query provided. Please provide a SQL query to check.";
|
|
1062
|
+
}
|
|
1063
|
+
const results = [];
|
|
1064
|
+
results.push("SQL Query Check Results:");
|
|
1065
|
+
results.push("=".repeat(40));
|
|
1066
|
+
results.push(`
|
|
1067
|
+
Query:
|
|
1068
|
+
${trimmedQuery}
|
|
1069
|
+
`);
|
|
1070
|
+
const syntaxIssues = checkSyntax(trimmedQuery);
|
|
1071
|
+
if (syntaxIssues.length > 0) {
|
|
1072
|
+
results.push("Syntax Issues:");
|
|
1073
|
+
for (const issue of syntaxIssues) {
|
|
1074
|
+
results.push(` - ${issue}`);
|
|
1075
|
+
}
|
|
1076
|
+
} else {
|
|
1077
|
+
results.push("Syntax: \u2713 No obvious syntax issues found");
|
|
1078
|
+
}
|
|
1079
|
+
const dangerWarnings = checkDangerousOperations(trimmedQuery);
|
|
1080
|
+
if (dangerWarnings.length > 0) {
|
|
1081
|
+
results.push("\nSafety Warnings:");
|
|
1082
|
+
for (const warning of dangerWarnings) {
|
|
1083
|
+
results.push(` - ${warning}`);
|
|
1084
|
+
}
|
|
1085
|
+
} else {
|
|
1086
|
+
results.push("Safety: \u2713 No dangerous operations detected");
|
|
1087
|
+
}
|
|
1088
|
+
try {
|
|
1089
|
+
const databaseKey = getDatabaseKeyFromConfig4(config);
|
|
1090
|
+
const database = sqlDatabaseManager.getDatabase(databaseKey);
|
|
1091
|
+
const dbType = database.getDatabaseType();
|
|
1092
|
+
if (dbType === "postgres") {
|
|
1093
|
+
try {
|
|
1094
|
+
await database.executeQuery(`EXPLAIN ${trimmedQuery}`);
|
|
1095
|
+
results.push("Database Validation: \u2713 Query is valid against database schema");
|
|
1096
|
+
} catch (explainError) {
|
|
1097
|
+
results.push(
|
|
1098
|
+
`Database Validation: \u2717 ${explainError instanceof Error ? explainError.message : String(explainError)}`
|
|
1099
|
+
);
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
} catch {
|
|
1103
|
+
results.push(
|
|
1104
|
+
"Database Validation: Skipped (no database connection available)"
|
|
1105
|
+
);
|
|
1106
|
+
}
|
|
1107
|
+
const hasErrors = syntaxIssues.some((i) => !i.startsWith("Warning:")) || dangerWarnings.length > 0;
|
|
1108
|
+
results.push("\n" + "=".repeat(40));
|
|
1109
|
+
if (hasErrors) {
|
|
1110
|
+
results.push(
|
|
1111
|
+
"Overall: \u26A0\uFE0F Query has potential issues. Review warnings before execution."
|
|
1112
|
+
);
|
|
1113
|
+
} else {
|
|
1114
|
+
results.push("Overall: \u2713 Query appears to be safe to execute.");
|
|
1115
|
+
}
|
|
1116
|
+
return results.join("\n");
|
|
1117
|
+
} catch (error) {
|
|
1118
|
+
return `Error checking query: ${error instanceof Error ? error.message : String(error)}`;
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
);
|
|
1122
|
+
|
|
591
1123
|
// src/agent_lattice/types.ts
|
|
592
1124
|
import {
|
|
593
1125
|
AgentType,
|
|
@@ -729,7 +1261,7 @@ import {
|
|
|
729
1261
|
// src/deep_agent_new/middleware/fs.ts
|
|
730
1262
|
import { createMiddleware, tool as tool2, ToolMessage } from "langchain";
|
|
731
1263
|
import { Command, isCommand, getCurrentTaskInput } from "@langchain/langgraph";
|
|
732
|
-
import { z as
|
|
1264
|
+
import { z as z32 } from "zod/v3";
|
|
733
1265
|
import { withLangGraph } from "@langchain/langgraph/zod";
|
|
734
1266
|
|
|
735
1267
|
// src/deep_agent_new/backends/utils.ts
|
|
@@ -1072,10 +1604,10 @@ var StateBackend = class {
|
|
|
1072
1604
|
};
|
|
1073
1605
|
|
|
1074
1606
|
// src/deep_agent_new/middleware/fs.ts
|
|
1075
|
-
var FileDataSchema =
|
|
1076
|
-
content:
|
|
1077
|
-
created_at:
|
|
1078
|
-
modified_at:
|
|
1607
|
+
var FileDataSchema = z32.object({
|
|
1608
|
+
content: z32.array(z32.string()),
|
|
1609
|
+
created_at: z32.string(),
|
|
1610
|
+
modified_at: z32.string()
|
|
1079
1611
|
});
|
|
1080
1612
|
function fileDataReducer(left, right) {
|
|
1081
1613
|
if (left === void 0) {
|
|
@@ -1097,13 +1629,13 @@ function fileDataReducer(left, right) {
|
|
|
1097
1629
|
}
|
|
1098
1630
|
return result;
|
|
1099
1631
|
}
|
|
1100
|
-
var FilesystemStateSchema =
|
|
1632
|
+
var FilesystemStateSchema = z32.object({
|
|
1101
1633
|
files: withLangGraph(
|
|
1102
|
-
|
|
1634
|
+
z32.record(z32.string(), FileDataSchema).default({}),
|
|
1103
1635
|
{
|
|
1104
1636
|
reducer: {
|
|
1105
1637
|
fn: fileDataReducer,
|
|
1106
|
-
schema:
|
|
1638
|
+
schema: z32.record(z32.string(), FileDataSchema.nullable())
|
|
1107
1639
|
}
|
|
1108
1640
|
}
|
|
1109
1641
|
)
|
|
@@ -1156,8 +1688,8 @@ function createLsTool(backend, options) {
|
|
|
1156
1688
|
{
|
|
1157
1689
|
name: "ls",
|
|
1158
1690
|
description: customDescription || LS_TOOL_DESCRIPTION,
|
|
1159
|
-
schema:
|
|
1160
|
-
path:
|
|
1691
|
+
schema: z32.object({
|
|
1692
|
+
path: z32.string().optional().default("/").describe("Directory path to list (default: /)")
|
|
1161
1693
|
})
|
|
1162
1694
|
}
|
|
1163
1695
|
);
|
|
@@ -1177,10 +1709,10 @@ function createReadFileTool(backend, options) {
|
|
|
1177
1709
|
{
|
|
1178
1710
|
name: "read_file",
|
|
1179
1711
|
description: customDescription || READ_FILE_TOOL_DESCRIPTION,
|
|
1180
|
-
schema:
|
|
1181
|
-
file_path:
|
|
1182
|
-
offset:
|
|
1183
|
-
limit:
|
|
1712
|
+
schema: z32.object({
|
|
1713
|
+
file_path: z32.string().describe("Absolute path to the file to read"),
|
|
1714
|
+
offset: z32.number({ coerce: true }).optional().default(0).describe("Line offset to start reading from (0-indexed)"),
|
|
1715
|
+
limit: z32.number({ coerce: true }).optional().default(2e3).describe("Maximum number of lines to read")
|
|
1184
1716
|
})
|
|
1185
1717
|
}
|
|
1186
1718
|
);
|
|
@@ -1215,9 +1747,9 @@ function createWriteFileTool(backend, options) {
|
|
|
1215
1747
|
{
|
|
1216
1748
|
name: "write_file",
|
|
1217
1749
|
description: customDescription || WRITE_FILE_TOOL_DESCRIPTION,
|
|
1218
|
-
schema:
|
|
1219
|
-
file_path:
|
|
1220
|
-
content:
|
|
1750
|
+
schema: z32.object({
|
|
1751
|
+
file_path: z32.string().describe("Absolute path to the file to write"),
|
|
1752
|
+
content: z32.string().describe("Content to write to the file")
|
|
1221
1753
|
})
|
|
1222
1754
|
}
|
|
1223
1755
|
);
|
|
@@ -1257,11 +1789,11 @@ function createEditFileTool(backend, options) {
|
|
|
1257
1789
|
{
|
|
1258
1790
|
name: "edit_file",
|
|
1259
1791
|
description: customDescription || EDIT_FILE_TOOL_DESCRIPTION,
|
|
1260
|
-
schema:
|
|
1261
|
-
file_path:
|
|
1262
|
-
old_string:
|
|
1263
|
-
new_string:
|
|
1264
|
-
replace_all:
|
|
1792
|
+
schema: z32.object({
|
|
1793
|
+
file_path: z32.string().describe("Absolute path to the file to edit"),
|
|
1794
|
+
old_string: z32.string().describe("String to be replaced (must match exactly)"),
|
|
1795
|
+
new_string: z32.string().describe("String to replace with"),
|
|
1796
|
+
replace_all: z32.boolean().optional().default(false).describe("Whether to replace all occurrences")
|
|
1265
1797
|
})
|
|
1266
1798
|
}
|
|
1267
1799
|
);
|
|
@@ -1285,9 +1817,9 @@ function createGlobTool(backend, options) {
|
|
|
1285
1817
|
{
|
|
1286
1818
|
name: "glob",
|
|
1287
1819
|
description: customDescription || GLOB_TOOL_DESCRIPTION,
|
|
1288
|
-
schema:
|
|
1289
|
-
pattern:
|
|
1290
|
-
path:
|
|
1820
|
+
schema: z32.object({
|
|
1821
|
+
pattern: z32.string().describe("Glob pattern (e.g., '*.py', '**/*.ts')"),
|
|
1822
|
+
path: z32.string().optional().default("/").describe("Base path to search from (default: /)")
|
|
1291
1823
|
})
|
|
1292
1824
|
}
|
|
1293
1825
|
);
|
|
@@ -1324,10 +1856,10 @@ ${currentFile}:`);
|
|
|
1324
1856
|
{
|
|
1325
1857
|
name: "grep",
|
|
1326
1858
|
description: customDescription || GREP_TOOL_DESCRIPTION,
|
|
1327
|
-
schema:
|
|
1328
|
-
pattern:
|
|
1329
|
-
path:
|
|
1330
|
-
glob:
|
|
1859
|
+
schema: z32.object({
|
|
1860
|
+
pattern: z32.string().describe("Regex pattern to search for"),
|
|
1861
|
+
path: z32.string().optional().default("/").describe("Base path to search from (default: /)"),
|
|
1862
|
+
glob: z32.string().optional().nullable().describe("Optional glob pattern to filter files (e.g., '*.py')")
|
|
1331
1863
|
})
|
|
1332
1864
|
}
|
|
1333
1865
|
);
|
|
@@ -1455,7 +1987,7 @@ ${systemPrompt}` : systemPrompt;
|
|
|
1455
1987
|
}
|
|
1456
1988
|
|
|
1457
1989
|
// src/deep_agent_new/middleware/subagents.ts
|
|
1458
|
-
import { z as
|
|
1990
|
+
import { z as z7 } from "zod/v3";
|
|
1459
1991
|
import {
|
|
1460
1992
|
createMiddleware as createMiddleware2,
|
|
1461
1993
|
createAgent as createAgent2,
|
|
@@ -2132,9 +2664,9 @@ function createTaskTool(options) {
|
|
|
2132
2664
|
{
|
|
2133
2665
|
name: "task",
|
|
2134
2666
|
description: finalTaskDescription,
|
|
2135
|
-
schema:
|
|
2136
|
-
description:
|
|
2137
|
-
subagent_type:
|
|
2667
|
+
schema: z7.object({
|
|
2668
|
+
description: z7.string().describe("The task to execute with the selected agent"),
|
|
2669
|
+
subagent_type: z7.string().describe(
|
|
2138
2670
|
`Name of the agent to use. Available: ${Object.keys(
|
|
2139
2671
|
subagentGraphs
|
|
2140
2672
|
).join(", ")}`
|
|
@@ -2238,7 +2770,7 @@ var SUPPORTS_NOFOLLOW = fsSync.constants.O_NOFOLLOW !== void 0;
|
|
|
2238
2770
|
|
|
2239
2771
|
// src/deep_agent_new/middleware/todos.ts
|
|
2240
2772
|
import { Command as Command3 } from "@langchain/langgraph";
|
|
2241
|
-
import { z as
|
|
2773
|
+
import { z as z8 } from "zod";
|
|
2242
2774
|
import { createMiddleware as createMiddleware4, tool as tool4, ToolMessage as ToolMessage4 } from "langchain";
|
|
2243
2775
|
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.
|
|
2244
2776
|
It also helps the user understand the progress of the task and overall progress of their requests.
|
|
@@ -2466,12 +2998,12 @@ Writing todos takes time and tokens, use it when it is helpful for managing comp
|
|
|
2466
2998
|
## Important To-Do List Usage Notes to Remember
|
|
2467
2999
|
- The \`write_todos\` tool should never be called multiple times in parallel.
|
|
2468
3000
|
- 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.`;
|
|
2469
|
-
var TodoStatus =
|
|
2470
|
-
var TodoSchema =
|
|
2471
|
-
content:
|
|
3001
|
+
var TodoStatus = z8.enum(["pending", "in_progress", "completed"]).describe("Status of the todo");
|
|
3002
|
+
var TodoSchema = z8.object({
|
|
3003
|
+
content: z8.string().describe("Content of the todo item"),
|
|
2472
3004
|
status: TodoStatus
|
|
2473
3005
|
});
|
|
2474
|
-
var stateSchema =
|
|
3006
|
+
var stateSchema = z8.object({ todos: z8.array(TodoSchema).default([]) });
|
|
2475
3007
|
function todoListMiddleware(options) {
|
|
2476
3008
|
const writeTodos = tool4(
|
|
2477
3009
|
({ todos }, config) => {
|
|
@@ -2490,8 +3022,8 @@ function todoListMiddleware(options) {
|
|
|
2490
3022
|
{
|
|
2491
3023
|
name: "write_todos",
|
|
2492
3024
|
description: options?.toolDescription ?? WRITE_TODOS_DESCRIPTION,
|
|
2493
|
-
schema:
|
|
2494
|
-
todos:
|
|
3025
|
+
schema: z8.object({
|
|
3026
|
+
todos: z8.array(TodoSchema).describe("List of todo items to update")
|
|
2495
3027
|
})
|
|
2496
3028
|
}
|
|
2497
3029
|
);
|
|
@@ -3231,200 +3763,931 @@ import {
|
|
|
3231
3763
|
ScheduleType
|
|
3232
3764
|
} from "@axiom-lattice/protocols";
|
|
3233
3765
|
|
|
3234
|
-
// src/schedule_lattice/
|
|
3235
|
-
|
|
3766
|
+
// src/schedule_lattice/DefaultScheduleClient.ts
|
|
3767
|
+
import {
|
|
3768
|
+
ScheduledTaskStatus as ScheduledTaskStatus2,
|
|
3769
|
+
ScheduleExecutionType as ScheduleExecutionType2
|
|
3770
|
+
} from "@axiom-lattice/protocols";
|
|
3771
|
+
|
|
3772
|
+
// src/schedule_lattice/MemoryScheduleStorage.ts
|
|
3773
|
+
import {
|
|
3774
|
+
ScheduledTaskStatus
|
|
3775
|
+
} from "@axiom-lattice/protocols";
|
|
3776
|
+
var MemoryScheduleStorage = class {
|
|
3236
3777
|
constructor() {
|
|
3237
3778
|
this.tasks = /* @__PURE__ */ new Map();
|
|
3238
3779
|
}
|
|
3239
3780
|
/**
|
|
3240
|
-
*
|
|
3781
|
+
* Save a new task
|
|
3241
3782
|
*/
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
_MemoryScheduleClient.instance = new _MemoryScheduleClient();
|
|
3245
|
-
}
|
|
3246
|
-
return _MemoryScheduleClient.instance;
|
|
3783
|
+
async save(task) {
|
|
3784
|
+
this.tasks.set(task.taskId, { ...task });
|
|
3247
3785
|
}
|
|
3248
3786
|
/**
|
|
3249
|
-
*
|
|
3250
|
-
* @param taskId - Unique identifier for the task
|
|
3251
|
-
* @param callback - Function to execute when timeout expires
|
|
3252
|
-
* @param timeoutMs - Delay in milliseconds before execution
|
|
3253
|
-
* @returns true if registered successfully
|
|
3787
|
+
* Get task by ID
|
|
3254
3788
|
*/
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
}
|
|
3259
|
-
const timerId = setTimeout(async () => {
|
|
3260
|
-
try {
|
|
3261
|
-
console.log("scheduler callback", taskId, "start");
|
|
3262
|
-
await callback();
|
|
3263
|
-
console.log("scheduler callback", taskId, "success");
|
|
3264
|
-
} catch (error) {
|
|
3265
|
-
console.error(`Scheduler: Error executing task "${taskId}":`, error);
|
|
3266
|
-
} finally {
|
|
3267
|
-
this.tasks.delete(taskId);
|
|
3268
|
-
console.log("scheduler callback", taskId, "end");
|
|
3269
|
-
}
|
|
3270
|
-
}, timeoutMs);
|
|
3271
|
-
this.tasks.set(taskId, {
|
|
3272
|
-
timerId,
|
|
3273
|
-
callback,
|
|
3274
|
-
scheduledAt: Date.now(),
|
|
3275
|
-
timeoutMs
|
|
3276
|
-
});
|
|
3277
|
-
console.log("scheduler register", taskId);
|
|
3278
|
-
return true;
|
|
3789
|
+
async get(taskId) {
|
|
3790
|
+
const task = this.tasks.get(taskId);
|
|
3791
|
+
return task ? { ...task } : null;
|
|
3279
3792
|
}
|
|
3280
3793
|
/**
|
|
3281
|
-
*
|
|
3282
|
-
* @param taskId - The task identifier to cancel
|
|
3283
|
-
* @returns true if task was found and cancelled, false otherwise
|
|
3794
|
+
* Update task
|
|
3284
3795
|
*/
|
|
3285
|
-
|
|
3286
|
-
console.log("scheduler cancel", taskId);
|
|
3796
|
+
async update(taskId, updates) {
|
|
3287
3797
|
const task = this.tasks.get(taskId);
|
|
3288
3798
|
if (task) {
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3799
|
+
this.tasks.set(taskId, {
|
|
3800
|
+
...task,
|
|
3801
|
+
...updates,
|
|
3802
|
+
updatedAt: Date.now()
|
|
3803
|
+
});
|
|
3292
3804
|
}
|
|
3293
|
-
return false;
|
|
3294
3805
|
}
|
|
3295
3806
|
/**
|
|
3296
|
-
*
|
|
3297
|
-
* @param taskId - The task identifier to check
|
|
3807
|
+
* Delete task
|
|
3298
3808
|
*/
|
|
3299
|
-
|
|
3300
|
-
|
|
3809
|
+
async delete(taskId) {
|
|
3810
|
+
this.tasks.delete(taskId);
|
|
3301
3811
|
}
|
|
3302
3812
|
/**
|
|
3303
|
-
* Get
|
|
3304
|
-
* @param taskId - The task identifier
|
|
3305
|
-
* @returns Remaining time in ms, or -1 if task not found
|
|
3813
|
+
* Get all active tasks (pending or paused)
|
|
3306
3814
|
*/
|
|
3307
|
-
|
|
3308
|
-
const
|
|
3309
|
-
|
|
3310
|
-
|
|
3815
|
+
async getActiveTasks() {
|
|
3816
|
+
const result = [];
|
|
3817
|
+
for (const task of this.tasks.values()) {
|
|
3818
|
+
if (task.status === ScheduledTaskStatus.PENDING || task.status === ScheduledTaskStatus.PAUSED) {
|
|
3819
|
+
result.push({ ...task });
|
|
3820
|
+
}
|
|
3311
3821
|
}
|
|
3312
|
-
|
|
3313
|
-
return Math.max(0, task.timeoutMs - elapsed);
|
|
3822
|
+
return result;
|
|
3314
3823
|
}
|
|
3315
3824
|
/**
|
|
3316
|
-
* Get
|
|
3825
|
+
* Get tasks by type
|
|
3317
3826
|
*/
|
|
3318
|
-
|
|
3319
|
-
|
|
3827
|
+
async getTasksByType(taskType) {
|
|
3828
|
+
const result = [];
|
|
3829
|
+
for (const task of this.tasks.values()) {
|
|
3830
|
+
if (task.taskType === taskType) {
|
|
3831
|
+
result.push({ ...task });
|
|
3832
|
+
}
|
|
3833
|
+
}
|
|
3834
|
+
return result;
|
|
3320
3835
|
}
|
|
3321
3836
|
/**
|
|
3322
|
-
* Get
|
|
3837
|
+
* Get tasks by status
|
|
3323
3838
|
*/
|
|
3324
|
-
|
|
3325
|
-
|
|
3839
|
+
async getTasksByStatus(status) {
|
|
3840
|
+
const result = [];
|
|
3841
|
+
for (const task of this.tasks.values()) {
|
|
3842
|
+
if (task.status === status) {
|
|
3843
|
+
result.push({ ...task });
|
|
3844
|
+
}
|
|
3845
|
+
}
|
|
3846
|
+
return result;
|
|
3326
3847
|
}
|
|
3327
3848
|
/**
|
|
3328
|
-
*
|
|
3849
|
+
* Get tasks by execution type
|
|
3329
3850
|
*/
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3851
|
+
async getTasksByExecutionType(executionType) {
|
|
3852
|
+
const result = [];
|
|
3853
|
+
for (const task of this.tasks.values()) {
|
|
3854
|
+
if (task.executionType === executionType) {
|
|
3855
|
+
result.push({ ...task });
|
|
3856
|
+
}
|
|
3333
3857
|
}
|
|
3334
|
-
|
|
3858
|
+
return result;
|
|
3335
3859
|
}
|
|
3336
3860
|
/**
|
|
3337
|
-
* Get
|
|
3338
|
-
* @param taskId - The task identifier
|
|
3339
|
-
* @returns Task info or null if not found
|
|
3861
|
+
* Get tasks by assistant ID
|
|
3340
3862
|
*/
|
|
3341
|
-
|
|
3342
|
-
const
|
|
3343
|
-
|
|
3344
|
-
|
|
3863
|
+
async getTasksByAssistantId(assistantId) {
|
|
3864
|
+
const result = [];
|
|
3865
|
+
for (const task of this.tasks.values()) {
|
|
3866
|
+
if (task.assistantId === assistantId) {
|
|
3867
|
+
result.push({ ...task });
|
|
3868
|
+
}
|
|
3345
3869
|
}
|
|
3346
|
-
return
|
|
3347
|
-
taskId,
|
|
3348
|
-
scheduledAt: task.scheduledAt,
|
|
3349
|
-
timeoutMs: task.timeoutMs,
|
|
3350
|
-
remainingMs: this.getRemainingTime(taskId)
|
|
3351
|
-
};
|
|
3870
|
+
return result;
|
|
3352
3871
|
}
|
|
3353
3872
|
/**
|
|
3354
|
-
*
|
|
3873
|
+
* Get tasks by thread ID
|
|
3355
3874
|
*/
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3875
|
+
async getTasksByThreadId(threadId) {
|
|
3876
|
+
const result = [];
|
|
3877
|
+
for (const task of this.tasks.values()) {
|
|
3878
|
+
if (task.threadId === threadId) {
|
|
3879
|
+
result.push({ ...task });
|
|
3880
|
+
}
|
|
3360
3881
|
}
|
|
3882
|
+
return result;
|
|
3361
3883
|
}
|
|
3362
|
-
};
|
|
3363
|
-
_MemoryScheduleClient.instance = null;
|
|
3364
|
-
var MemoryScheduleClient = _MemoryScheduleClient;
|
|
3365
|
-
|
|
3366
|
-
// src/schedule_lattice/ScheduleLatticeManager.ts
|
|
3367
|
-
var ScheduleLatticeManager = class _ScheduleLatticeManager extends BaseLatticeManager {
|
|
3368
3884
|
/**
|
|
3369
|
-
* Get
|
|
3885
|
+
* Get all tasks with optional filters
|
|
3370
3886
|
*/
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3887
|
+
async getAllTasks(filters) {
|
|
3888
|
+
let result = [];
|
|
3889
|
+
for (const task of this.tasks.values()) {
|
|
3890
|
+
let match = true;
|
|
3891
|
+
if (filters?.status !== void 0 && task.status !== filters.status) {
|
|
3892
|
+
match = false;
|
|
3893
|
+
}
|
|
3894
|
+
if (filters?.executionType !== void 0 && task.executionType !== filters.executionType) {
|
|
3895
|
+
match = false;
|
|
3896
|
+
}
|
|
3897
|
+
if (filters?.taskType !== void 0 && task.taskType !== filters.taskType) {
|
|
3898
|
+
match = false;
|
|
3899
|
+
}
|
|
3900
|
+
if (filters?.assistantId !== void 0 && task.assistantId !== filters.assistantId) {
|
|
3901
|
+
match = false;
|
|
3902
|
+
}
|
|
3903
|
+
if (filters?.threadId !== void 0 && task.threadId !== filters.threadId) {
|
|
3904
|
+
match = false;
|
|
3905
|
+
}
|
|
3906
|
+
if (match) {
|
|
3907
|
+
result.push({ ...task });
|
|
3908
|
+
}
|
|
3374
3909
|
}
|
|
3375
|
-
|
|
3910
|
+
result.sort((a, b) => b.createdAt - a.createdAt);
|
|
3911
|
+
const offset = filters?.offset || 0;
|
|
3912
|
+
const limit = filters?.limit;
|
|
3913
|
+
if (limit !== void 0) {
|
|
3914
|
+
result = result.slice(offset, offset + limit);
|
|
3915
|
+
} else if (offset > 0) {
|
|
3916
|
+
result = result.slice(offset);
|
|
3917
|
+
}
|
|
3918
|
+
return result;
|
|
3376
3919
|
}
|
|
3377
3920
|
/**
|
|
3378
|
-
*
|
|
3921
|
+
* Count tasks with optional filters
|
|
3379
3922
|
*/
|
|
3380
|
-
|
|
3381
|
-
|
|
3923
|
+
async countTasks(filters) {
|
|
3924
|
+
let count = 0;
|
|
3925
|
+
for (const task of this.tasks.values()) {
|
|
3926
|
+
let match = true;
|
|
3927
|
+
if (filters?.status !== void 0 && task.status !== filters.status) {
|
|
3928
|
+
match = false;
|
|
3929
|
+
}
|
|
3930
|
+
if (filters?.executionType !== void 0 && task.executionType !== filters.executionType) {
|
|
3931
|
+
match = false;
|
|
3932
|
+
}
|
|
3933
|
+
if (filters?.taskType !== void 0 && task.taskType !== filters.taskType) {
|
|
3934
|
+
match = false;
|
|
3935
|
+
}
|
|
3936
|
+
if (filters?.assistantId !== void 0 && task.assistantId !== filters.assistantId) {
|
|
3937
|
+
match = false;
|
|
3938
|
+
}
|
|
3939
|
+
if (filters?.threadId !== void 0 && task.threadId !== filters.threadId) {
|
|
3940
|
+
match = false;
|
|
3941
|
+
}
|
|
3942
|
+
if (match) {
|
|
3943
|
+
count++;
|
|
3944
|
+
}
|
|
3945
|
+
}
|
|
3946
|
+
return count;
|
|
3382
3947
|
}
|
|
3383
3948
|
/**
|
|
3384
|
-
*
|
|
3385
|
-
* @param key Lattice key name
|
|
3386
|
-
* @param config Schedule configuration
|
|
3387
|
-
* @param client Optional schedule client. If not provided, will create based on config type.
|
|
3949
|
+
* Delete completed/cancelled tasks older than specified time
|
|
3388
3950
|
*/
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
} else {
|
|
3397
|
-
scheduleClient = MemoryScheduleClient.getInstance();
|
|
3951
|
+
async deleteOldTasks(olderThanMs) {
|
|
3952
|
+
const cutoff = Date.now() - olderThanMs;
|
|
3953
|
+
let deleted = 0;
|
|
3954
|
+
for (const [taskId, task] of this.tasks.entries()) {
|
|
3955
|
+
if ((task.status === ScheduledTaskStatus.COMPLETED || task.status === ScheduledTaskStatus.CANCELLED || task.status === ScheduledTaskStatus.FAILED) && task.updatedAt < cutoff) {
|
|
3956
|
+
this.tasks.delete(taskId);
|
|
3957
|
+
deleted++;
|
|
3398
3958
|
}
|
|
3399
3959
|
}
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
|
|
3960
|
+
return deleted;
|
|
3961
|
+
}
|
|
3962
|
+
/**
|
|
3963
|
+
* Clear all tasks (useful for testing)
|
|
3964
|
+
*/
|
|
3965
|
+
clear() {
|
|
3966
|
+
this.tasks.clear();
|
|
3967
|
+
}
|
|
3968
|
+
/**
|
|
3969
|
+
* Get total task count (useful for debugging)
|
|
3970
|
+
*/
|
|
3971
|
+
size() {
|
|
3972
|
+
return this.tasks.size;
|
|
3973
|
+
}
|
|
3974
|
+
};
|
|
3975
|
+
|
|
3976
|
+
// src/schedule_lattice/CronParser.ts
|
|
3977
|
+
function parseCronExpression(expression) {
|
|
3978
|
+
const parts = expression.trim().split(/\s+/);
|
|
3979
|
+
if (parts.length !== 5) {
|
|
3980
|
+
throw new Error(
|
|
3981
|
+
`Invalid cron expression: expected 5 fields, got ${parts.length}`
|
|
3982
|
+
);
|
|
3983
|
+
}
|
|
3984
|
+
return {
|
|
3985
|
+
minute: parseField(parts[0], 0, 59),
|
|
3986
|
+
hour: parseField(parts[1], 0, 23),
|
|
3987
|
+
dayOfMonth: parseField(parts[2], 1, 31),
|
|
3988
|
+
month: parseField(parts[3], 1, 12),
|
|
3989
|
+
dayOfWeek: parseField(parts[4], 0, 7).map((d) => d === 7 ? 0 : d)
|
|
3990
|
+
// Normalize Sunday
|
|
3991
|
+
};
|
|
3992
|
+
}
|
|
3993
|
+
function parseField(field, min, max) {
|
|
3994
|
+
const values = /* @__PURE__ */ new Set();
|
|
3995
|
+
const segments = field.split(",");
|
|
3996
|
+
for (const segment of segments) {
|
|
3997
|
+
const [range, stepStr] = segment.split("/");
|
|
3998
|
+
const step = stepStr ? parseInt(stepStr, 10) : 1;
|
|
3999
|
+
if (isNaN(step) || step < 1) {
|
|
4000
|
+
throw new Error(`Invalid step value: ${stepStr}`);
|
|
4001
|
+
}
|
|
4002
|
+
let rangeStart;
|
|
4003
|
+
let rangeEnd;
|
|
4004
|
+
if (range === "*") {
|
|
4005
|
+
rangeStart = min;
|
|
4006
|
+
rangeEnd = max;
|
|
4007
|
+
} else if (range.includes("-")) {
|
|
4008
|
+
const [startStr, endStr] = range.split("-");
|
|
4009
|
+
rangeStart = parseInt(startStr, 10);
|
|
4010
|
+
rangeEnd = parseInt(endStr, 10);
|
|
4011
|
+
if (isNaN(rangeStart) || isNaN(rangeEnd)) {
|
|
4012
|
+
throw new Error(`Invalid range: ${range}`);
|
|
4013
|
+
}
|
|
4014
|
+
} else {
|
|
4015
|
+
const value = parseInt(range, 10);
|
|
4016
|
+
if (isNaN(value)) {
|
|
4017
|
+
throw new Error(`Invalid value: ${range}`);
|
|
4018
|
+
}
|
|
4019
|
+
rangeStart = value;
|
|
4020
|
+
rangeEnd = value;
|
|
4021
|
+
}
|
|
4022
|
+
if (rangeStart < min || rangeEnd > max || rangeStart > rangeEnd) {
|
|
4023
|
+
throw new Error(`Value out of range: ${range} (expected ${min}-${max})`);
|
|
4024
|
+
}
|
|
4025
|
+
for (let i = rangeStart; i <= rangeEnd; i += step) {
|
|
4026
|
+
values.add(i);
|
|
4027
|
+
}
|
|
4028
|
+
}
|
|
4029
|
+
return Array.from(values).sort((a, b) => a - b);
|
|
4030
|
+
}
|
|
4031
|
+
function getNextCronTime(expression, after = /* @__PURE__ */ new Date(), timezone) {
|
|
4032
|
+
const fields = parseCronExpression(expression);
|
|
4033
|
+
const next = new Date(after.getTime());
|
|
4034
|
+
next.setSeconds(0, 0);
|
|
4035
|
+
next.setMinutes(next.getMinutes() + 1);
|
|
4036
|
+
const maxIterations = 366 * 24 * 60;
|
|
4037
|
+
let iterations = 0;
|
|
4038
|
+
while (iterations < maxIterations) {
|
|
4039
|
+
iterations++;
|
|
4040
|
+
const month = next.getMonth() + 1;
|
|
4041
|
+
if (!fields.month.includes(month)) {
|
|
4042
|
+
const nextMonth = fields.month.find((m) => m > month);
|
|
4043
|
+
if (nextMonth !== void 0) {
|
|
4044
|
+
next.setMonth(nextMonth - 1, 1);
|
|
4045
|
+
} else {
|
|
4046
|
+
next.setFullYear(next.getFullYear() + 1);
|
|
4047
|
+
next.setMonth(fields.month[0] - 1, 1);
|
|
4048
|
+
}
|
|
4049
|
+
next.setHours(fields.hour[0], fields.minute[0], 0, 0);
|
|
4050
|
+
continue;
|
|
4051
|
+
}
|
|
4052
|
+
const dayOfMonth = next.getDate();
|
|
4053
|
+
const maxDayOfMonth = new Date(
|
|
4054
|
+
next.getFullYear(),
|
|
4055
|
+
next.getMonth() + 1,
|
|
4056
|
+
0
|
|
4057
|
+
).getDate();
|
|
4058
|
+
const dayOfWeek = next.getDay();
|
|
4059
|
+
const dayOfMonthMatch = fields.dayOfMonth.includes(dayOfMonth);
|
|
4060
|
+
const dayOfWeekMatch = fields.dayOfWeek.includes(dayOfWeek);
|
|
4061
|
+
const dayOfMonthWildcard = fields.dayOfMonth.length === 31 && fields.dayOfMonth[0] === 1 && fields.dayOfMonth[30] === 31;
|
|
4062
|
+
const dayOfWeekWildcard = fields.dayOfWeek.length >= 7 || fields.dayOfWeek.length === 1 && fields.dayOfWeek.includes(0);
|
|
4063
|
+
let dayMatch;
|
|
4064
|
+
if (dayOfMonthWildcard && dayOfWeekWildcard) {
|
|
4065
|
+
dayMatch = true;
|
|
4066
|
+
} else if (dayOfMonthWildcard) {
|
|
4067
|
+
dayMatch = dayOfWeekMatch;
|
|
4068
|
+
} else if (dayOfWeekWildcard) {
|
|
4069
|
+
dayMatch = dayOfMonthMatch;
|
|
4070
|
+
} else {
|
|
4071
|
+
dayMatch = dayOfMonthMatch || dayOfWeekMatch;
|
|
4072
|
+
}
|
|
4073
|
+
if (!dayMatch || dayOfMonth > maxDayOfMonth) {
|
|
4074
|
+
next.setDate(next.getDate() + 1);
|
|
4075
|
+
next.setHours(fields.hour[0], fields.minute[0], 0, 0);
|
|
4076
|
+
continue;
|
|
4077
|
+
}
|
|
4078
|
+
const hour = next.getHours();
|
|
4079
|
+
if (!fields.hour.includes(hour)) {
|
|
4080
|
+
const nextHour = fields.hour.find((h) => h > hour);
|
|
4081
|
+
if (nextHour !== void 0) {
|
|
4082
|
+
next.setHours(nextHour, fields.minute[0], 0, 0);
|
|
4083
|
+
} else {
|
|
4084
|
+
next.setDate(next.getDate() + 1);
|
|
4085
|
+
next.setHours(fields.hour[0], fields.minute[0], 0, 0);
|
|
4086
|
+
}
|
|
4087
|
+
continue;
|
|
4088
|
+
}
|
|
4089
|
+
const minute = next.getMinutes();
|
|
4090
|
+
if (!fields.minute.includes(minute)) {
|
|
4091
|
+
const nextMinute = fields.minute.find((m) => m > minute);
|
|
4092
|
+
if (nextMinute !== void 0) {
|
|
4093
|
+
next.setMinutes(nextMinute, 0, 0);
|
|
4094
|
+
} else {
|
|
4095
|
+
next.setHours(next.getHours() + 1, fields.minute[0], 0, 0);
|
|
4096
|
+
}
|
|
4097
|
+
continue;
|
|
4098
|
+
}
|
|
4099
|
+
return next;
|
|
4100
|
+
}
|
|
4101
|
+
throw new Error("Could not find next cron time within 1 year");
|
|
4102
|
+
}
|
|
4103
|
+
function isValidCronExpression(expression) {
|
|
4104
|
+
try {
|
|
4105
|
+
parseCronExpression(expression);
|
|
4106
|
+
return true;
|
|
4107
|
+
} catch {
|
|
4108
|
+
return false;
|
|
4109
|
+
}
|
|
4110
|
+
}
|
|
4111
|
+
function describeCronExpression(expression) {
|
|
4112
|
+
const fields = parseCronExpression(expression);
|
|
4113
|
+
const parts = [];
|
|
4114
|
+
if (fields.minute.length === 60) {
|
|
4115
|
+
parts.push("every minute");
|
|
4116
|
+
} else if (fields.minute.length === 1) {
|
|
4117
|
+
parts.push(`at minute ${fields.minute[0]}`);
|
|
4118
|
+
} else {
|
|
4119
|
+
parts.push(`at minutes ${fields.minute.join(", ")}`);
|
|
4120
|
+
}
|
|
4121
|
+
if (fields.hour.length === 24) {
|
|
4122
|
+
parts.push("of every hour");
|
|
4123
|
+
} else if (fields.hour.length === 1) {
|
|
4124
|
+
parts.push(`of hour ${fields.hour[0]}`);
|
|
4125
|
+
} else {
|
|
4126
|
+
parts.push(`of hours ${fields.hour.join(", ")}`);
|
|
4127
|
+
}
|
|
4128
|
+
if (fields.dayOfMonth.length < 31) {
|
|
4129
|
+
if (fields.dayOfMonth.length === 1) {
|
|
4130
|
+
parts.push(`on day ${fields.dayOfMonth[0]}`);
|
|
4131
|
+
} else {
|
|
4132
|
+
parts.push(`on days ${fields.dayOfMonth.join(", ")}`);
|
|
4133
|
+
}
|
|
4134
|
+
}
|
|
4135
|
+
const monthNames = [
|
|
4136
|
+
"",
|
|
4137
|
+
"Jan",
|
|
4138
|
+
"Feb",
|
|
4139
|
+
"Mar",
|
|
4140
|
+
"Apr",
|
|
4141
|
+
"May",
|
|
4142
|
+
"Jun",
|
|
4143
|
+
"Jul",
|
|
4144
|
+
"Aug",
|
|
4145
|
+
"Sep",
|
|
4146
|
+
"Oct",
|
|
4147
|
+
"Nov",
|
|
4148
|
+
"Dec"
|
|
4149
|
+
];
|
|
4150
|
+
if (fields.month.length < 12) {
|
|
4151
|
+
const months = fields.month.map((m) => monthNames[m]);
|
|
4152
|
+
parts.push(`in ${months.join(", ")}`);
|
|
4153
|
+
}
|
|
4154
|
+
const dayNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
|
4155
|
+
const uniqueDays = [...new Set(fields.dayOfWeek)];
|
|
4156
|
+
if (uniqueDays.length < 7) {
|
|
4157
|
+
const days = uniqueDays.map((d) => dayNames[d]);
|
|
4158
|
+
parts.push(`on ${days.join(", ")}`);
|
|
4159
|
+
}
|
|
4160
|
+
return parts.join(" ");
|
|
4161
|
+
}
|
|
4162
|
+
|
|
4163
|
+
// src/schedule_lattice/DefaultScheduleClient.ts
|
|
4164
|
+
var _DefaultScheduleClient = class _DefaultScheduleClient {
|
|
4165
|
+
constructor(storage) {
|
|
4166
|
+
this.handlers = /* @__PURE__ */ new Map();
|
|
4167
|
+
this.timers = /* @__PURE__ */ new Map();
|
|
4168
|
+
this.storage = storage || new MemoryScheduleStorage();
|
|
4169
|
+
}
|
|
4170
|
+
/**
|
|
4171
|
+
* Get the singleton instance of DefaultScheduleClient
|
|
4172
|
+
*/
|
|
4173
|
+
static getInstance(storage) {
|
|
4174
|
+
if (!_DefaultScheduleClient.instance) {
|
|
4175
|
+
_DefaultScheduleClient.instance = new _DefaultScheduleClient(storage);
|
|
4176
|
+
}
|
|
4177
|
+
return _DefaultScheduleClient.instance;
|
|
4178
|
+
}
|
|
4179
|
+
/**
|
|
4180
|
+
* Reset the singleton instance (useful for testing)
|
|
4181
|
+
*/
|
|
4182
|
+
static resetInstance() {
|
|
4183
|
+
if (_DefaultScheduleClient.instance) {
|
|
4184
|
+
_DefaultScheduleClient.instance.cancelAll();
|
|
4185
|
+
_DefaultScheduleClient.instance = null;
|
|
4186
|
+
}
|
|
4187
|
+
}
|
|
4188
|
+
// ===== Handler Registration =====
|
|
4189
|
+
registerHandler(taskType, handler) {
|
|
4190
|
+
this.handlers.set(taskType, handler);
|
|
4191
|
+
console.log(`[Scheduler] Handler registered for task type: ${taskType}`);
|
|
4192
|
+
}
|
|
4193
|
+
unregisterHandler(taskType) {
|
|
4194
|
+
const result = this.handlers.delete(taskType);
|
|
4195
|
+
if (result) {
|
|
4196
|
+
console.log(
|
|
4197
|
+
`[Scheduler] Handler unregistered for task type: ${taskType}`
|
|
4198
|
+
);
|
|
4199
|
+
}
|
|
4200
|
+
return result;
|
|
4201
|
+
}
|
|
4202
|
+
hasHandler(taskType) {
|
|
4203
|
+
return this.handlers.has(taskType);
|
|
4204
|
+
}
|
|
4205
|
+
getHandlerTypes() {
|
|
4206
|
+
return Array.from(this.handlers.keys());
|
|
4207
|
+
}
|
|
4208
|
+
// ===== One-time Task Scheduling =====
|
|
4209
|
+
async scheduleOnce(taskId, taskType, payload, options) {
|
|
4210
|
+
if (!this.handlers.has(taskType)) {
|
|
4211
|
+
console.error(
|
|
4212
|
+
`[Scheduler] No handler registered for task type: ${taskType}`
|
|
4213
|
+
);
|
|
4214
|
+
return false;
|
|
4215
|
+
}
|
|
4216
|
+
let executeAt;
|
|
4217
|
+
if (options.executeAt !== void 0) {
|
|
4218
|
+
executeAt = options.executeAt;
|
|
4219
|
+
} else if (options.delayMs !== void 0) {
|
|
4220
|
+
executeAt = Date.now() + options.delayMs;
|
|
4221
|
+
} else {
|
|
4222
|
+
console.error("[Scheduler] Either executeAt or delayMs must be provided");
|
|
4223
|
+
return false;
|
|
4224
|
+
}
|
|
4225
|
+
if (await this.has(taskId)) {
|
|
4226
|
+
await this.cancel(taskId);
|
|
4227
|
+
}
|
|
4228
|
+
const now = Date.now();
|
|
4229
|
+
const task = {
|
|
4230
|
+
taskId,
|
|
4231
|
+
taskType,
|
|
4232
|
+
payload,
|
|
4233
|
+
assistantId: options.assistantId,
|
|
4234
|
+
threadId: options.threadId,
|
|
4235
|
+
executionType: ScheduleExecutionType2.ONCE,
|
|
4236
|
+
executeAt,
|
|
4237
|
+
delayMs: options.delayMs,
|
|
4238
|
+
status: ScheduledTaskStatus2.PENDING,
|
|
4239
|
+
runCount: 0,
|
|
4240
|
+
retryCount: 0,
|
|
4241
|
+
maxRetries: options.maxRetries ?? 0,
|
|
4242
|
+
createdAt: now,
|
|
4243
|
+
updatedAt: now,
|
|
4244
|
+
metadata: options.metadata
|
|
4245
|
+
};
|
|
4246
|
+
await this.storage.save(task);
|
|
4247
|
+
this.scheduleTimer(task);
|
|
4248
|
+
console.log(
|
|
4249
|
+
`[Scheduler] One-time task scheduled: ${taskId}, executes at ${new Date(
|
|
4250
|
+
executeAt
|
|
4251
|
+
).toISOString()}`
|
|
4252
|
+
);
|
|
4253
|
+
return true;
|
|
4254
|
+
}
|
|
4255
|
+
// ===== Cron Task Scheduling =====
|
|
4256
|
+
async scheduleCron(taskId, taskType, payload, options) {
|
|
4257
|
+
if (!this.handlers.has(taskType)) {
|
|
4258
|
+
console.error(
|
|
4259
|
+
`[Scheduler] No handler registered for task type: ${taskType}`
|
|
4260
|
+
);
|
|
4261
|
+
return false;
|
|
4262
|
+
}
|
|
4263
|
+
if (!isValidCronExpression(options.cronExpression)) {
|
|
4264
|
+
console.error(
|
|
4265
|
+
`[Scheduler] Invalid cron expression: ${options.cronExpression}`
|
|
4266
|
+
);
|
|
4267
|
+
return false;
|
|
4268
|
+
}
|
|
4269
|
+
if (await this.has(taskId)) {
|
|
4270
|
+
await this.cancel(taskId);
|
|
4271
|
+
}
|
|
4272
|
+
const now = Date.now();
|
|
4273
|
+
const nextRunAt = getNextCronTime(options.cronExpression, new Date(now));
|
|
4274
|
+
const task = {
|
|
4275
|
+
taskId,
|
|
4276
|
+
taskType,
|
|
4277
|
+
payload,
|
|
4278
|
+
assistantId: options.assistantId,
|
|
4279
|
+
threadId: options.threadId,
|
|
4280
|
+
executionType: ScheduleExecutionType2.CRON,
|
|
4281
|
+
cronExpression: options.cronExpression,
|
|
4282
|
+
timezone: options.timezone,
|
|
4283
|
+
nextRunAt: nextRunAt.getTime(),
|
|
4284
|
+
status: ScheduledTaskStatus2.PENDING,
|
|
4285
|
+
runCount: 0,
|
|
4286
|
+
maxRuns: options.maxRuns,
|
|
4287
|
+
retryCount: 0,
|
|
4288
|
+
maxRetries: options.maxRetries ?? 0,
|
|
4289
|
+
createdAt: now,
|
|
4290
|
+
updatedAt: now,
|
|
4291
|
+
expiresAt: options.expiresAt,
|
|
4292
|
+
metadata: options.metadata
|
|
4293
|
+
};
|
|
4294
|
+
await this.storage.save(task);
|
|
4295
|
+
this.scheduleTimer(task);
|
|
4296
|
+
console.log(
|
|
4297
|
+
`[Scheduler] Cron task scheduled: ${taskId}, expression: ${options.cronExpression}, next run: ${nextRunAt.toISOString()}`
|
|
4298
|
+
);
|
|
4299
|
+
return true;
|
|
4300
|
+
}
|
|
4301
|
+
// ===== Task Management =====
|
|
4302
|
+
async cancel(taskId) {
|
|
4303
|
+
const timer = this.timers.get(taskId);
|
|
4304
|
+
if (timer) {
|
|
4305
|
+
clearTimeout(timer.timerId);
|
|
4306
|
+
this.timers.delete(taskId);
|
|
4307
|
+
}
|
|
4308
|
+
const task = await this.storage.get(taskId);
|
|
4309
|
+
if (task) {
|
|
4310
|
+
await this.storage.update(taskId, {
|
|
4311
|
+
status: ScheduledTaskStatus2.CANCELLED
|
|
4312
|
+
});
|
|
4313
|
+
console.log(`[Scheduler] Task cancelled: ${taskId}`);
|
|
4314
|
+
return true;
|
|
4315
|
+
}
|
|
4316
|
+
return false;
|
|
4317
|
+
}
|
|
4318
|
+
async pause(taskId) {
|
|
4319
|
+
const task = await this.storage.get(taskId);
|
|
4320
|
+
if (!task || task.executionType !== ScheduleExecutionType2.CRON) {
|
|
4321
|
+
console.error(`[Scheduler] Can only pause CRON tasks: ${taskId}`);
|
|
4322
|
+
return false;
|
|
4323
|
+
}
|
|
4324
|
+
if (task.status !== ScheduledTaskStatus2.PENDING) {
|
|
4325
|
+
console.error(`[Scheduler] Can only pause PENDING tasks: ${taskId}`);
|
|
4326
|
+
return false;
|
|
4327
|
+
}
|
|
4328
|
+
const timer = this.timers.get(taskId);
|
|
4329
|
+
if (timer) {
|
|
4330
|
+
clearTimeout(timer.timerId);
|
|
4331
|
+
this.timers.delete(taskId);
|
|
4332
|
+
}
|
|
4333
|
+
await this.storage.update(taskId, {
|
|
4334
|
+
status: ScheduledTaskStatus2.PAUSED
|
|
4335
|
+
});
|
|
4336
|
+
console.log(`[Scheduler] Task paused: ${taskId}`);
|
|
4337
|
+
return true;
|
|
4338
|
+
}
|
|
4339
|
+
async resume(taskId) {
|
|
4340
|
+
const task = await this.storage.get(taskId);
|
|
4341
|
+
if (!task || task.executionType !== ScheduleExecutionType2.CRON) {
|
|
4342
|
+
console.error(`[Scheduler] Can only resume CRON tasks: ${taskId}`);
|
|
4343
|
+
return false;
|
|
4344
|
+
}
|
|
4345
|
+
if (task.status !== ScheduledTaskStatus2.PAUSED) {
|
|
4346
|
+
console.error(`[Scheduler] Can only resume PAUSED tasks: ${taskId}`);
|
|
4347
|
+
return false;
|
|
4348
|
+
}
|
|
4349
|
+
const nextRunAt = getNextCronTime(task.cronExpression);
|
|
4350
|
+
await this.storage.update(taskId, {
|
|
4351
|
+
status: ScheduledTaskStatus2.PENDING,
|
|
4352
|
+
nextRunAt: nextRunAt.getTime()
|
|
4353
|
+
});
|
|
4354
|
+
const updatedTask = await this.storage.get(taskId);
|
|
4355
|
+
if (updatedTask) {
|
|
4356
|
+
this.scheduleTimer(updatedTask);
|
|
4357
|
+
}
|
|
4358
|
+
console.log(`[Scheduler] Task resumed: ${taskId}`);
|
|
4359
|
+
return true;
|
|
4360
|
+
}
|
|
4361
|
+
async has(taskId) {
|
|
4362
|
+
const task = await this.storage.get(taskId);
|
|
4363
|
+
return task !== null;
|
|
4364
|
+
}
|
|
4365
|
+
async getTask(taskId) {
|
|
4366
|
+
return this.storage.get(taskId);
|
|
4367
|
+
}
|
|
4368
|
+
async getRemainingTime(taskId) {
|
|
4369
|
+
const task = await this.storage.get(taskId);
|
|
4370
|
+
if (!task) {
|
|
4371
|
+
return -1;
|
|
4372
|
+
}
|
|
4373
|
+
const targetTime = task.executionType === ScheduleExecutionType2.CRON ? task.nextRunAt : task.executeAt;
|
|
4374
|
+
if (targetTime === void 0) {
|
|
4375
|
+
return -1;
|
|
4376
|
+
}
|
|
4377
|
+
return Math.max(0, targetTime - Date.now());
|
|
4378
|
+
}
|
|
4379
|
+
async getActiveTaskCount() {
|
|
4380
|
+
const tasks = await this.storage.getActiveTasks();
|
|
4381
|
+
return tasks.length;
|
|
4382
|
+
}
|
|
4383
|
+
async getActiveTaskIds() {
|
|
4384
|
+
const tasks = await this.storage.getActiveTasks();
|
|
4385
|
+
return tasks.map((t) => t.taskId);
|
|
4386
|
+
}
|
|
4387
|
+
async cancelAll() {
|
|
4388
|
+
for (const timer of this.timers.values()) {
|
|
4389
|
+
clearTimeout(timer.timerId);
|
|
4390
|
+
}
|
|
4391
|
+
this.timers.clear();
|
|
4392
|
+
const activeTasks = await this.storage.getActiveTasks();
|
|
4393
|
+
for (const task of activeTasks) {
|
|
4394
|
+
await this.storage.update(task.taskId, {
|
|
4395
|
+
status: ScheduledTaskStatus2.CANCELLED
|
|
4396
|
+
});
|
|
4397
|
+
}
|
|
4398
|
+
console.log(`[Scheduler] All tasks cancelled`);
|
|
4399
|
+
}
|
|
4400
|
+
// ===== Recovery =====
|
|
4401
|
+
async restore() {
|
|
4402
|
+
const activeTasks = await this.storage.getActiveTasks();
|
|
4403
|
+
let restored = 0;
|
|
4404
|
+
for (const task of activeTasks) {
|
|
4405
|
+
if (!this.handlers.has(task.taskType)) {
|
|
4406
|
+
console.warn(
|
|
4407
|
+
`[Scheduler] No handler for task type ${task.taskType}, skipping restore for task ${task.taskId}`
|
|
4408
|
+
);
|
|
4409
|
+
continue;
|
|
4410
|
+
}
|
|
4411
|
+
if (task.status === ScheduledTaskStatus2.PAUSED) {
|
|
4412
|
+
continue;
|
|
4413
|
+
}
|
|
4414
|
+
if (task.executionType === ScheduleExecutionType2.ONCE) {
|
|
4415
|
+
if (task.executeAt && task.executeAt <= Date.now()) {
|
|
4416
|
+
console.log(
|
|
4417
|
+
`[Scheduler] Executing overdue one-time task: ${task.taskId}`
|
|
4418
|
+
);
|
|
4419
|
+
this.executeTask(task);
|
|
4420
|
+
} else {
|
|
4421
|
+
this.scheduleTimer(task);
|
|
4422
|
+
}
|
|
4423
|
+
} else if (task.executionType === ScheduleExecutionType2.CRON) {
|
|
4424
|
+
const nextRunAt = getNextCronTime(task.cronExpression);
|
|
4425
|
+
await this.storage.update(task.taskId, {
|
|
4426
|
+
nextRunAt: nextRunAt.getTime()
|
|
4427
|
+
});
|
|
4428
|
+
const updatedTask = await this.storage.get(task.taskId);
|
|
4429
|
+
if (updatedTask) {
|
|
4430
|
+
this.scheduleTimer(updatedTask);
|
|
4431
|
+
}
|
|
4432
|
+
}
|
|
4433
|
+
restored++;
|
|
4434
|
+
}
|
|
4435
|
+
console.log(`[Scheduler] Restored ${restored} tasks`);
|
|
4436
|
+
return restored;
|
|
4437
|
+
}
|
|
4438
|
+
// ===== Storage =====
|
|
4439
|
+
setStorage(storage) {
|
|
4440
|
+
this.storage = storage;
|
|
4441
|
+
}
|
|
4442
|
+
getStorage() {
|
|
4443
|
+
return this.storage;
|
|
4444
|
+
}
|
|
4445
|
+
// ===== Private Methods =====
|
|
4446
|
+
scheduleTimer(task) {
|
|
4447
|
+
const targetTime = task.executionType === ScheduleExecutionType2.CRON ? task.nextRunAt : task.executeAt;
|
|
4448
|
+
if (targetTime === void 0) {
|
|
4449
|
+
console.error(`[Scheduler] No execution time for task: ${task.taskId}`);
|
|
4450
|
+
return;
|
|
4451
|
+
}
|
|
4452
|
+
const delay = Math.max(0, targetTime - Date.now());
|
|
4453
|
+
const timerId = setTimeout(() => {
|
|
4454
|
+
this.executeTask(task);
|
|
4455
|
+
}, delay);
|
|
4456
|
+
this.timers.set(task.taskId, {
|
|
4457
|
+
timerId,
|
|
4458
|
+
taskId: task.taskId
|
|
4459
|
+
});
|
|
4460
|
+
}
|
|
4461
|
+
async executeTask(task) {
|
|
4462
|
+
const handler = this.handlers.get(task.taskType);
|
|
4463
|
+
if (!handler) {
|
|
4464
|
+
console.error(
|
|
4465
|
+
`[Scheduler] No handler for task type: ${task.taskType}, task: ${task.taskId}`
|
|
4466
|
+
);
|
|
4467
|
+
await this.storage.update(task.taskId, {
|
|
4468
|
+
status: ScheduledTaskStatus2.FAILED,
|
|
4469
|
+
lastError: `No handler registered for task type: ${task.taskType}`
|
|
4470
|
+
});
|
|
4471
|
+
return;
|
|
4472
|
+
}
|
|
4473
|
+
await this.storage.update(task.taskId, {
|
|
4474
|
+
status: ScheduledTaskStatus2.RUNNING
|
|
4475
|
+
});
|
|
4476
|
+
console.log(`[Scheduler] Executing task: ${task.taskId}`);
|
|
4477
|
+
try {
|
|
4478
|
+
const latestTask = await this.storage.get(task.taskId);
|
|
4479
|
+
if (!latestTask) {
|
|
4480
|
+
console.error(`[Scheduler] Task not found: ${task.taskId}`);
|
|
4481
|
+
return;
|
|
4482
|
+
}
|
|
4483
|
+
await handler(latestTask.payload, latestTask);
|
|
4484
|
+
const newRunCount = latestTask.runCount + 1;
|
|
4485
|
+
console.log(`[Scheduler] Task completed: ${task.taskId}`);
|
|
4486
|
+
if (task.executionType === ScheduleExecutionType2.ONCE) {
|
|
4487
|
+
await this.storage.update(task.taskId, {
|
|
4488
|
+
status: ScheduledTaskStatus2.COMPLETED,
|
|
4489
|
+
runCount: newRunCount,
|
|
4490
|
+
lastRunAt: Date.now()
|
|
4491
|
+
});
|
|
4492
|
+
this.timers.delete(task.taskId);
|
|
4493
|
+
} else if (task.executionType === ScheduleExecutionType2.CRON) {
|
|
4494
|
+
if (task.maxRuns !== void 0 && newRunCount >= task.maxRuns) {
|
|
4495
|
+
await this.storage.update(task.taskId, {
|
|
4496
|
+
status: ScheduledTaskStatus2.COMPLETED,
|
|
4497
|
+
runCount: newRunCount,
|
|
4498
|
+
lastRunAt: Date.now()
|
|
4499
|
+
});
|
|
4500
|
+
this.timers.delete(task.taskId);
|
|
4501
|
+
console.log(
|
|
4502
|
+
`[Scheduler] Cron task completed (max runs): ${task.taskId}`
|
|
4503
|
+
);
|
|
4504
|
+
return;
|
|
4505
|
+
}
|
|
4506
|
+
if (task.expiresAt !== void 0 && Date.now() >= task.expiresAt) {
|
|
4507
|
+
await this.storage.update(task.taskId, {
|
|
4508
|
+
status: ScheduledTaskStatus2.COMPLETED,
|
|
4509
|
+
runCount: newRunCount,
|
|
4510
|
+
lastRunAt: Date.now()
|
|
4511
|
+
});
|
|
4512
|
+
this.timers.delete(task.taskId);
|
|
4513
|
+
console.log(
|
|
4514
|
+
`[Scheduler] Cron task completed (expired): ${task.taskId}`
|
|
4515
|
+
);
|
|
4516
|
+
return;
|
|
4517
|
+
}
|
|
4518
|
+
const nextRunAt = getNextCronTime(task.cronExpression);
|
|
4519
|
+
await this.storage.update(task.taskId, {
|
|
4520
|
+
status: ScheduledTaskStatus2.PENDING,
|
|
4521
|
+
runCount: newRunCount,
|
|
4522
|
+
lastRunAt: Date.now(),
|
|
4523
|
+
nextRunAt: nextRunAt.getTime(),
|
|
4524
|
+
retryCount: 0
|
|
4525
|
+
// Reset retry count on success
|
|
4526
|
+
});
|
|
4527
|
+
const updatedTask = await this.storage.get(task.taskId);
|
|
4528
|
+
if (updatedTask) {
|
|
4529
|
+
this.scheduleTimer(updatedTask);
|
|
4530
|
+
console.log(
|
|
4531
|
+
`[Scheduler] Next cron run scheduled: ${task.taskId} at ${nextRunAt.toISOString()}`
|
|
4532
|
+
);
|
|
4533
|
+
}
|
|
4534
|
+
}
|
|
4535
|
+
} catch (error) {
|
|
4536
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4537
|
+
console.error(`[Scheduler] Task failed: ${task.taskId}`, error);
|
|
4538
|
+
const latestTask = await this.storage.get(task.taskId);
|
|
4539
|
+
if (!latestTask) return;
|
|
4540
|
+
const newRetryCount = latestTask.retryCount + 1;
|
|
4541
|
+
if (newRetryCount <= latestTask.maxRetries) {
|
|
4542
|
+
console.log(
|
|
4543
|
+
`[Scheduler] Retrying task: ${task.taskId} (attempt ${newRetryCount}/${latestTask.maxRetries})`
|
|
4544
|
+
);
|
|
4545
|
+
await this.storage.update(task.taskId, {
|
|
4546
|
+
status: ScheduledTaskStatus2.PENDING,
|
|
4547
|
+
retryCount: newRetryCount,
|
|
4548
|
+
lastError: errorMessage
|
|
4549
|
+
});
|
|
4550
|
+
const retryDelay = Math.min(
|
|
4551
|
+
1e3 * Math.pow(2, newRetryCount - 1),
|
|
4552
|
+
6e4
|
|
4553
|
+
);
|
|
4554
|
+
const retryTask = await this.storage.get(task.taskId);
|
|
4555
|
+
if (retryTask) {
|
|
4556
|
+
if (task.executionType === ScheduleExecutionType2.ONCE) {
|
|
4557
|
+
retryTask.executeAt = Date.now() + retryDelay;
|
|
4558
|
+
} else {
|
|
4559
|
+
retryTask.nextRunAt = Date.now() + retryDelay;
|
|
4560
|
+
}
|
|
4561
|
+
await this.storage.update(task.taskId, {
|
|
4562
|
+
executeAt: retryTask.executeAt,
|
|
4563
|
+
nextRunAt: retryTask.nextRunAt
|
|
4564
|
+
});
|
|
4565
|
+
this.scheduleTimer(retryTask);
|
|
4566
|
+
}
|
|
4567
|
+
} else {
|
|
4568
|
+
await this.storage.update(task.taskId, {
|
|
4569
|
+
status: ScheduledTaskStatus2.FAILED,
|
|
4570
|
+
retryCount: newRetryCount,
|
|
4571
|
+
lastError: errorMessage
|
|
4572
|
+
});
|
|
4573
|
+
this.timers.delete(task.taskId);
|
|
4574
|
+
console.error(
|
|
4575
|
+
`[Scheduler] Task failed permanently: ${task.taskId} (max retries exceeded)`
|
|
4576
|
+
);
|
|
4577
|
+
}
|
|
4578
|
+
}
|
|
4579
|
+
}
|
|
4580
|
+
};
|
|
4581
|
+
_DefaultScheduleClient.instance = null;
|
|
4582
|
+
var DefaultScheduleClient = _DefaultScheduleClient;
|
|
4583
|
+
|
|
4584
|
+
// src/schedule_lattice/ScheduleLatticeManager.ts
|
|
4585
|
+
var ScheduleLatticeManager = class _ScheduleLatticeManager extends BaseLatticeManager {
|
|
4586
|
+
/**
|
|
4587
|
+
* Get ScheduleLatticeManager singleton instance
|
|
4588
|
+
*/
|
|
4589
|
+
static getInstance() {
|
|
4590
|
+
if (!_ScheduleLatticeManager._instance) {
|
|
4591
|
+
_ScheduleLatticeManager._instance = new _ScheduleLatticeManager();
|
|
4592
|
+
}
|
|
4593
|
+
return _ScheduleLatticeManager._instance;
|
|
4594
|
+
}
|
|
4595
|
+
/**
|
|
4596
|
+
* Get Lattice type prefix
|
|
4597
|
+
*/
|
|
4598
|
+
getLatticeType() {
|
|
4599
|
+
return "schedules";
|
|
4600
|
+
}
|
|
4601
|
+
/**
|
|
4602
|
+
* Register schedule Lattice
|
|
4603
|
+
* @param key Lattice key name
|
|
4604
|
+
* @param config Schedule configuration
|
|
4605
|
+
* @param client Optional schedule client. If not provided, will create based on config type.
|
|
4606
|
+
*/
|
|
4607
|
+
registerLattice(key, config, client) {
|
|
4608
|
+
let scheduleClient;
|
|
4609
|
+
if (client) {
|
|
4610
|
+
scheduleClient = client;
|
|
4611
|
+
if (config.storage) {
|
|
4612
|
+
scheduleClient.setStorage(config.storage);
|
|
4613
|
+
}
|
|
4614
|
+
} else {
|
|
4615
|
+
let storage;
|
|
4616
|
+
if (config.storage) {
|
|
4617
|
+
storage = config.storage;
|
|
4618
|
+
} else {
|
|
4619
|
+
storage = new MemoryScheduleStorage();
|
|
4620
|
+
}
|
|
4621
|
+
if (config.type === ScheduleType.MEMORY) {
|
|
4622
|
+
scheduleClient = new DefaultScheduleClient(storage);
|
|
4623
|
+
} else if (config.type === ScheduleType.POSTGRES) {
|
|
4624
|
+
if (!config.storage) {
|
|
4625
|
+
throw new Error(
|
|
4626
|
+
`PostgreSQL schedule storage must be provided. Please install @axiom-lattice/pg-stores and pass the storage in config.storage.`
|
|
4627
|
+
);
|
|
4628
|
+
}
|
|
4629
|
+
scheduleClient = new DefaultScheduleClient(storage);
|
|
4630
|
+
} else {
|
|
4631
|
+
scheduleClient = new DefaultScheduleClient(storage);
|
|
4632
|
+
}
|
|
4633
|
+
}
|
|
4634
|
+
const scheduleLattice = {
|
|
4635
|
+
key,
|
|
4636
|
+
config,
|
|
4637
|
+
client: scheduleClient,
|
|
4638
|
+
// Handler registration
|
|
4639
|
+
registerHandler: (taskType, handler) => {
|
|
4640
|
+
scheduleClient.registerHandler(taskType, handler);
|
|
3409
4641
|
},
|
|
3410
|
-
|
|
4642
|
+
unregisterHandler: (taskType) => {
|
|
4643
|
+
return scheduleClient.unregisterHandler(taskType);
|
|
4644
|
+
},
|
|
4645
|
+
hasHandler: (taskType) => {
|
|
4646
|
+
return scheduleClient.hasHandler(taskType);
|
|
4647
|
+
},
|
|
4648
|
+
getHandlerTypes: () => {
|
|
4649
|
+
return scheduleClient.getHandlerTypes();
|
|
4650
|
+
},
|
|
4651
|
+
// One-time task scheduling
|
|
4652
|
+
scheduleOnce: async (taskId, taskType, payload, options) => {
|
|
4653
|
+
return scheduleClient.scheduleOnce(taskId, taskType, payload, options);
|
|
4654
|
+
},
|
|
4655
|
+
// Cron task scheduling
|
|
4656
|
+
scheduleCron: async (taskId, taskType, payload, options) => {
|
|
4657
|
+
return scheduleClient.scheduleCron(taskId, taskType, payload, options);
|
|
4658
|
+
},
|
|
4659
|
+
// Task management
|
|
4660
|
+
cancel: async (taskId) => {
|
|
4661
|
+
return scheduleClient.cancel(taskId);
|
|
4662
|
+
},
|
|
4663
|
+
pause: async (taskId) => {
|
|
4664
|
+
return scheduleClient.pause(taskId);
|
|
4665
|
+
},
|
|
4666
|
+
resume: async (taskId) => {
|
|
4667
|
+
return scheduleClient.resume(taskId);
|
|
4668
|
+
},
|
|
4669
|
+
has: async (taskId) => {
|
|
3411
4670
|
return scheduleClient.has(taskId);
|
|
3412
4671
|
},
|
|
3413
|
-
|
|
4672
|
+
getTask: async (taskId) => {
|
|
4673
|
+
return scheduleClient.getTask(taskId);
|
|
4674
|
+
},
|
|
4675
|
+
getRemainingTime: async (taskId) => {
|
|
3414
4676
|
return scheduleClient.getRemainingTime(taskId);
|
|
3415
4677
|
},
|
|
3416
|
-
|
|
3417
|
-
return scheduleClient.
|
|
4678
|
+
getActiveTaskCount: async () => {
|
|
4679
|
+
return scheduleClient.getActiveTaskCount();
|
|
3418
4680
|
},
|
|
3419
|
-
|
|
3420
|
-
return scheduleClient.
|
|
4681
|
+
getActiveTaskIds: async () => {
|
|
4682
|
+
return scheduleClient.getActiveTaskIds();
|
|
3421
4683
|
},
|
|
3422
|
-
cancelAll: () => {
|
|
4684
|
+
cancelAll: async () => {
|
|
3423
4685
|
return scheduleClient.cancelAll();
|
|
3424
4686
|
},
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
4687
|
+
// Recovery
|
|
4688
|
+
restore: async () => {
|
|
4689
|
+
return scheduleClient.restore();
|
|
4690
|
+
}
|
|
3428
4691
|
};
|
|
3429
4692
|
this.register(key, scheduleLattice);
|
|
3430
4693
|
}
|
|
@@ -3974,6 +5237,323 @@ var registerVectorStoreLattice = (key, vectorStore) => vectorStoreLatticeManager
|
|
|
3974
5237
|
var getVectorStoreLattice = (key) => vectorStoreLatticeManager.getVectorStoreLattice(key);
|
|
3975
5238
|
var getVectorStoreClient = (key) => vectorStoreLatticeManager.getVectorStoreClient(key);
|
|
3976
5239
|
|
|
5240
|
+
// src/logger_lattice/LoggerLatticeManager.ts
|
|
5241
|
+
import {
|
|
5242
|
+
LoggerType
|
|
5243
|
+
} from "@axiom-lattice/protocols";
|
|
5244
|
+
|
|
5245
|
+
// src/logger_lattice/PinoLoggerClient.ts
|
|
5246
|
+
import pino from "pino";
|
|
5247
|
+
import "pino-pretty";
|
|
5248
|
+
import "pino-roll";
|
|
5249
|
+
var PinoLoggerClient = class _PinoLoggerClient {
|
|
5250
|
+
constructor(config) {
|
|
5251
|
+
this.config = config;
|
|
5252
|
+
this.context = config.context || {};
|
|
5253
|
+
const loggerConfig = {
|
|
5254
|
+
// Custom timestamp format
|
|
5255
|
+
timestamp: () => `,"@timestamp":"${(/* @__PURE__ */ new Date()).toISOString()}"`,
|
|
5256
|
+
// Base metadata
|
|
5257
|
+
base: {
|
|
5258
|
+
"@version": "1",
|
|
5259
|
+
app_name: "lattice",
|
|
5260
|
+
service_name: config.serviceName || "lattice-service",
|
|
5261
|
+
thread_name: "main",
|
|
5262
|
+
logger_name: config.loggerName || config.name || "lattice-logger"
|
|
5263
|
+
},
|
|
5264
|
+
formatters: {
|
|
5265
|
+
level: (label, number) => {
|
|
5266
|
+
return {
|
|
5267
|
+
level: label.toUpperCase(),
|
|
5268
|
+
level_value: number * 1e3
|
|
5269
|
+
};
|
|
5270
|
+
}
|
|
5271
|
+
}
|
|
5272
|
+
};
|
|
5273
|
+
const useFileLogging = this.shouldUseFileLogging(config);
|
|
5274
|
+
const fileOptions = this.getFileOptions(config);
|
|
5275
|
+
if (useFileLogging && fileOptions) {
|
|
5276
|
+
try {
|
|
5277
|
+
this.pinoLogger = pino(
|
|
5278
|
+
loggerConfig,
|
|
5279
|
+
pino.transport({
|
|
5280
|
+
target: "pino-roll",
|
|
5281
|
+
options: {
|
|
5282
|
+
file: fileOptions.file || "./logs/app",
|
|
5283
|
+
frequency: fileOptions.frequency || "daily",
|
|
5284
|
+
mkdir: fileOptions.mkdir !== false,
|
|
5285
|
+
// Default to true
|
|
5286
|
+
size: fileOptions.size,
|
|
5287
|
+
maxFiles: fileOptions.maxFiles
|
|
5288
|
+
}
|
|
5289
|
+
})
|
|
5290
|
+
);
|
|
5291
|
+
} catch (error) {
|
|
5292
|
+
console.error(
|
|
5293
|
+
"Failed to initialize pino-roll logger, falling back to console",
|
|
5294
|
+
error
|
|
5295
|
+
);
|
|
5296
|
+
this.pinoLogger = pino({
|
|
5297
|
+
...loggerConfig,
|
|
5298
|
+
transport: {
|
|
5299
|
+
target: "pino-pretty",
|
|
5300
|
+
options: {
|
|
5301
|
+
colorize: true
|
|
5302
|
+
}
|
|
5303
|
+
}
|
|
5304
|
+
});
|
|
5305
|
+
}
|
|
5306
|
+
} else {
|
|
5307
|
+
this.pinoLogger = pino({
|
|
5308
|
+
...loggerConfig,
|
|
5309
|
+
transport: {
|
|
5310
|
+
target: "pino-pretty",
|
|
5311
|
+
options: {
|
|
5312
|
+
colorize: true
|
|
5313
|
+
}
|
|
5314
|
+
}
|
|
5315
|
+
});
|
|
5316
|
+
}
|
|
5317
|
+
}
|
|
5318
|
+
/**
|
|
5319
|
+
* Determine if file logging should be used
|
|
5320
|
+
*/
|
|
5321
|
+
shouldUseFileLogging(config) {
|
|
5322
|
+
const hasFileConfig = config.file !== void 0;
|
|
5323
|
+
const isProduction = process.env.NODE_ENV === "production";
|
|
5324
|
+
return hasFileConfig || isProduction;
|
|
5325
|
+
}
|
|
5326
|
+
/**
|
|
5327
|
+
* Get file options from config
|
|
5328
|
+
*/
|
|
5329
|
+
getFileOptions(config) {
|
|
5330
|
+
if (!config.file) {
|
|
5331
|
+
if (process.env.NODE_ENV === "production") {
|
|
5332
|
+
return {
|
|
5333
|
+
file: "./logs/app",
|
|
5334
|
+
frequency: "daily",
|
|
5335
|
+
mkdir: true
|
|
5336
|
+
};
|
|
5337
|
+
}
|
|
5338
|
+
return null;
|
|
5339
|
+
}
|
|
5340
|
+
if (typeof config.file === "string") {
|
|
5341
|
+
return {
|
|
5342
|
+
file: config.file,
|
|
5343
|
+
frequency: "daily",
|
|
5344
|
+
mkdir: true
|
|
5345
|
+
};
|
|
5346
|
+
}
|
|
5347
|
+
return config.file;
|
|
5348
|
+
}
|
|
5349
|
+
/**
|
|
5350
|
+
* Get contextual logger with merged context
|
|
5351
|
+
*/
|
|
5352
|
+
getContextualLogger(additionalContext) {
|
|
5353
|
+
const contextObj = {
|
|
5354
|
+
"x-user-id": this.context["x-user-id"] || "",
|
|
5355
|
+
"x-tenant-id": this.context["x-tenant-id"] || "",
|
|
5356
|
+
"x-request-id": this.context["x-request-id"] || "",
|
|
5357
|
+
"x-thread-id": this.context["x-thread-id"] || "",
|
|
5358
|
+
service_name: this.config.serviceName || "lattice-service",
|
|
5359
|
+
logger_name: this.config.loggerName || this.config.name || "lattice-logger",
|
|
5360
|
+
...additionalContext
|
|
5361
|
+
};
|
|
5362
|
+
return this.pinoLogger.child(contextObj);
|
|
5363
|
+
}
|
|
5364
|
+
info(msg, obj) {
|
|
5365
|
+
this.getContextualLogger(obj).info(msg);
|
|
5366
|
+
}
|
|
5367
|
+
error(msg, obj) {
|
|
5368
|
+
this.getContextualLogger(obj).error(msg);
|
|
5369
|
+
}
|
|
5370
|
+
warn(msg, obj) {
|
|
5371
|
+
this.getContextualLogger(obj).warn(msg);
|
|
5372
|
+
}
|
|
5373
|
+
debug(msg, obj) {
|
|
5374
|
+
this.getContextualLogger(obj).debug(msg);
|
|
5375
|
+
}
|
|
5376
|
+
updateContext(context) {
|
|
5377
|
+
this.context = {
|
|
5378
|
+
...this.context,
|
|
5379
|
+
...context
|
|
5380
|
+
};
|
|
5381
|
+
}
|
|
5382
|
+
child(options) {
|
|
5383
|
+
return new _PinoLoggerClient({
|
|
5384
|
+
...this.config,
|
|
5385
|
+
...options,
|
|
5386
|
+
context: {
|
|
5387
|
+
...this.context,
|
|
5388
|
+
...options.context
|
|
5389
|
+
}
|
|
5390
|
+
});
|
|
5391
|
+
}
|
|
5392
|
+
};
|
|
5393
|
+
|
|
5394
|
+
// src/logger_lattice/ConsoleLoggerClient.ts
|
|
5395
|
+
var ConsoleLoggerClient = class _ConsoleLoggerClient {
|
|
5396
|
+
constructor(config) {
|
|
5397
|
+
this.config = config;
|
|
5398
|
+
this.context = config.context || {};
|
|
5399
|
+
}
|
|
5400
|
+
formatMessage(level, msg, obj) {
|
|
5401
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
5402
|
+
const contextStr = Object.keys(this.context).length > 0 ? ` [${JSON.stringify(this.context)}]` : "";
|
|
5403
|
+
const objStr = obj ? ` ${JSON.stringify(obj)}` : "";
|
|
5404
|
+
return `[${timestamp}] [${level}]${contextStr} ${msg}${objStr}`;
|
|
5405
|
+
}
|
|
5406
|
+
info(msg, obj) {
|
|
5407
|
+
console.log(this.formatMessage("INFO", msg, obj));
|
|
5408
|
+
}
|
|
5409
|
+
error(msg, obj) {
|
|
5410
|
+
console.error(this.formatMessage("ERROR", msg, obj));
|
|
5411
|
+
}
|
|
5412
|
+
warn(msg, obj) {
|
|
5413
|
+
console.warn(this.formatMessage("WARN", msg, obj));
|
|
5414
|
+
}
|
|
5415
|
+
debug(msg, obj) {
|
|
5416
|
+
console.debug(this.formatMessage("DEBUG", msg, obj));
|
|
5417
|
+
}
|
|
5418
|
+
updateContext(context) {
|
|
5419
|
+
this.context = {
|
|
5420
|
+
...this.context,
|
|
5421
|
+
...context
|
|
5422
|
+
};
|
|
5423
|
+
}
|
|
5424
|
+
child(options) {
|
|
5425
|
+
const childClient = new _ConsoleLoggerClient({
|
|
5426
|
+
...this.config,
|
|
5427
|
+
...options,
|
|
5428
|
+
context: {
|
|
5429
|
+
...this.context,
|
|
5430
|
+
...options.context
|
|
5431
|
+
}
|
|
5432
|
+
});
|
|
5433
|
+
return childClient;
|
|
5434
|
+
}
|
|
5435
|
+
};
|
|
5436
|
+
|
|
5437
|
+
// src/logger_lattice/LoggerLatticeManager.ts
|
|
5438
|
+
var LoggerLatticeManager = class _LoggerLatticeManager extends BaseLatticeManager {
|
|
5439
|
+
/**
|
|
5440
|
+
* Get LoggerLatticeManager singleton instance
|
|
5441
|
+
*/
|
|
5442
|
+
static getInstance() {
|
|
5443
|
+
if (!_LoggerLatticeManager._instance) {
|
|
5444
|
+
_LoggerLatticeManager._instance = new _LoggerLatticeManager();
|
|
5445
|
+
}
|
|
5446
|
+
return _LoggerLatticeManager._instance;
|
|
5447
|
+
}
|
|
5448
|
+
/**
|
|
5449
|
+
* Get Lattice type prefix
|
|
5450
|
+
*/
|
|
5451
|
+
getLatticeType() {
|
|
5452
|
+
return "loggers";
|
|
5453
|
+
}
|
|
5454
|
+
/**
|
|
5455
|
+
* Register logger Lattice
|
|
5456
|
+
* @param key Lattice key name
|
|
5457
|
+
* @param config Logger configuration
|
|
5458
|
+
* @param client Optional logger client. If not provided, will create based on config type.
|
|
5459
|
+
*/
|
|
5460
|
+
registerLattice(key, config, client) {
|
|
5461
|
+
let loggerClient;
|
|
5462
|
+
if (client) {
|
|
5463
|
+
loggerClient = client;
|
|
5464
|
+
} else {
|
|
5465
|
+
if (config.type === LoggerType.PINO) {
|
|
5466
|
+
loggerClient = new PinoLoggerClient(config);
|
|
5467
|
+
} else if (config.type === LoggerType.CONSOLE) {
|
|
5468
|
+
loggerClient = new ConsoleLoggerClient(config);
|
|
5469
|
+
} else if (config.type === LoggerType.CUSTOM) {
|
|
5470
|
+
throw new Error(
|
|
5471
|
+
`Custom logger client must be provided. Please pass the client to registerLattice.`
|
|
5472
|
+
);
|
|
5473
|
+
} else {
|
|
5474
|
+
loggerClient = new PinoLoggerClient(config);
|
|
5475
|
+
}
|
|
5476
|
+
}
|
|
5477
|
+
const loggerLattice = {
|
|
5478
|
+
key,
|
|
5479
|
+
config,
|
|
5480
|
+
client: loggerClient,
|
|
5481
|
+
// Logger operations
|
|
5482
|
+
info: (msg, obj) => {
|
|
5483
|
+
loggerClient.info(msg, obj);
|
|
5484
|
+
},
|
|
5485
|
+
error: (msg, obj) => {
|
|
5486
|
+
loggerClient.error(msg, obj);
|
|
5487
|
+
},
|
|
5488
|
+
warn: (msg, obj) => {
|
|
5489
|
+
loggerClient.warn(msg, obj);
|
|
5490
|
+
},
|
|
5491
|
+
debug: (msg, obj) => {
|
|
5492
|
+
loggerClient.debug(msg, obj);
|
|
5493
|
+
},
|
|
5494
|
+
updateContext: loggerClient.updateContext ? (context) => {
|
|
5495
|
+
loggerClient.updateContext(context);
|
|
5496
|
+
} : void 0,
|
|
5497
|
+
child: loggerClient.child ? (options) => {
|
|
5498
|
+
return loggerClient.child(options);
|
|
5499
|
+
} : void 0
|
|
5500
|
+
};
|
|
5501
|
+
this.register(key, loggerLattice);
|
|
5502
|
+
}
|
|
5503
|
+
/**
|
|
5504
|
+
* Get LoggerLattice
|
|
5505
|
+
* @param key Lattice key name
|
|
5506
|
+
*/
|
|
5507
|
+
getLoggerLattice(key) {
|
|
5508
|
+
const loggerLattice = this.get(key);
|
|
5509
|
+
if (!loggerLattice) {
|
|
5510
|
+
throw new Error(`LoggerLattice ${key} not found`);
|
|
5511
|
+
}
|
|
5512
|
+
return loggerLattice;
|
|
5513
|
+
}
|
|
5514
|
+
/**
|
|
5515
|
+
* Get all Lattices
|
|
5516
|
+
*/
|
|
5517
|
+
getAllLattices() {
|
|
5518
|
+
return this.getAll();
|
|
5519
|
+
}
|
|
5520
|
+
/**
|
|
5521
|
+
* Check if Lattice exists
|
|
5522
|
+
* @param key Lattice key name
|
|
5523
|
+
*/
|
|
5524
|
+
hasLattice(key) {
|
|
5525
|
+
return this.has(key);
|
|
5526
|
+
}
|
|
5527
|
+
/**
|
|
5528
|
+
* Remove Lattice
|
|
5529
|
+
* @param key Lattice key name
|
|
5530
|
+
*/
|
|
5531
|
+
removeLattice(key) {
|
|
5532
|
+
return this.remove(key);
|
|
5533
|
+
}
|
|
5534
|
+
/**
|
|
5535
|
+
* Clear all Lattices
|
|
5536
|
+
*/
|
|
5537
|
+
clearLattices() {
|
|
5538
|
+
this.clear();
|
|
5539
|
+
}
|
|
5540
|
+
/**
|
|
5541
|
+
* Get Lattice count
|
|
5542
|
+
*/
|
|
5543
|
+
getLatticeCount() {
|
|
5544
|
+
return this.count();
|
|
5545
|
+
}
|
|
5546
|
+
/**
|
|
5547
|
+
* Get Lattice key list
|
|
5548
|
+
*/
|
|
5549
|
+
getLatticeKeys() {
|
|
5550
|
+
return this.keys();
|
|
5551
|
+
}
|
|
5552
|
+
};
|
|
5553
|
+
var loggerLatticeManager = LoggerLatticeManager.getInstance();
|
|
5554
|
+
var registerLoggerLattice = (key, config, client) => loggerLatticeManager.registerLattice(key, config, client);
|
|
5555
|
+
var getLoggerLattice = (key) => loggerLatticeManager.getLoggerLattice(key);
|
|
5556
|
+
|
|
3977
5557
|
// src/index.ts
|
|
3978
5558
|
import * as Protocols from "@axiom-lattice/protocols";
|
|
3979
5559
|
export {
|
|
@@ -3984,24 +5564,31 @@ export {
|
|
|
3984
5564
|
AgentType,
|
|
3985
5565
|
ChunkBuffer,
|
|
3986
5566
|
ChunkBufferLatticeManager,
|
|
5567
|
+
ConsoleLoggerClient,
|
|
5568
|
+
DefaultScheduleClient,
|
|
3987
5569
|
EmbeddingsLatticeManager,
|
|
3988
5570
|
GraphBuildOptions,
|
|
3989
5571
|
InMemoryAssistantStore,
|
|
3990
5572
|
InMemoryChunkBuffer,
|
|
3991
5573
|
InMemoryThreadStore,
|
|
5574
|
+
LoggerLatticeManager,
|
|
3992
5575
|
MemoryLatticeManager,
|
|
3993
5576
|
MemoryQueueClient,
|
|
3994
|
-
|
|
5577
|
+
MemoryScheduleStorage,
|
|
3995
5578
|
MemoryType,
|
|
3996
5579
|
ModelLatticeManager,
|
|
5580
|
+
PinoLoggerClient,
|
|
5581
|
+
PostgresDatabase,
|
|
3997
5582
|
Protocols,
|
|
3998
5583
|
QueueLatticeManager,
|
|
3999
5584
|
ScheduleLatticeManager,
|
|
5585
|
+
SqlDatabaseManager,
|
|
4000
5586
|
StoreLatticeManager,
|
|
4001
5587
|
ThreadStatus,
|
|
4002
5588
|
ToolLatticeManager,
|
|
4003
5589
|
VectorStoreLatticeManager,
|
|
4004
5590
|
agentLatticeManager,
|
|
5591
|
+
describeCronExpression,
|
|
4005
5592
|
embeddingsLatticeManager,
|
|
4006
5593
|
eventBus,
|
|
4007
5594
|
event_bus_default as eventBusDefault,
|
|
@@ -4014,7 +5601,9 @@ export {
|
|
|
4014
5601
|
getChunkBuffer,
|
|
4015
5602
|
getEmbeddingsClient,
|
|
4016
5603
|
getEmbeddingsLattice,
|
|
5604
|
+
getLoggerLattice,
|
|
4017
5605
|
getModelLattice,
|
|
5606
|
+
getNextCronTime,
|
|
4018
5607
|
getQueueLattice,
|
|
4019
5608
|
getScheduleLattice,
|
|
4020
5609
|
getStoreLattice,
|
|
@@ -4024,13 +5613,17 @@ export {
|
|
|
4024
5613
|
getVectorStoreClient,
|
|
4025
5614
|
getVectorStoreLattice,
|
|
4026
5615
|
hasChunkBuffer,
|
|
5616
|
+
isValidCronExpression,
|
|
5617
|
+
loggerLatticeManager,
|
|
4027
5618
|
modelLatticeManager,
|
|
5619
|
+
parseCronExpression,
|
|
4028
5620
|
queueLatticeManager,
|
|
4029
5621
|
registerAgentLattice,
|
|
4030
5622
|
registerAgentLattices,
|
|
4031
5623
|
registerCheckpointSaver,
|
|
4032
5624
|
registerChunkBuffer,
|
|
4033
5625
|
registerEmbeddingsLattice,
|
|
5626
|
+
registerLoggerLattice,
|
|
4034
5627
|
registerModelLattice,
|
|
4035
5628
|
registerQueueLattice,
|
|
4036
5629
|
registerScheduleLattice,
|
|
@@ -4038,6 +5631,7 @@ export {
|
|
|
4038
5631
|
registerToolLattice,
|
|
4039
5632
|
registerVectorStoreLattice,
|
|
4040
5633
|
scheduleLatticeManager,
|
|
5634
|
+
sqlDatabaseManager,
|
|
4041
5635
|
storeLatticeManager,
|
|
4042
5636
|
toolLatticeManager,
|
|
4043
5637
|
validateAgentInput,
|