@axiom-lattice/pg-stores 1.0.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -573,11 +573,556 @@ var PostgreSQLAssistantStore = class {
573
573
  };
574
574
  }
575
575
  };
576
+
577
+ // src/stores/PostgreSQLScheduleStorage.ts
578
+ import { Pool as Pool3 } from "pg";
579
+ import {
580
+ ScheduledTaskStatus
581
+ } from "@axiom-lattice/protocols";
582
+
583
+ // src/migrations/schedule_migrations.ts
584
+ var createScheduledTasksTable = {
585
+ name: "create_scheduled_tasks_table",
586
+ version: 2,
587
+ up: async (client) => {
588
+ await client.query(`
589
+ CREATE TABLE IF NOT EXISTS lattice_scheduled_tasks (
590
+ -- Primary key
591
+ task_id VARCHAR(255) PRIMARY KEY,
592
+
593
+ -- Task type (maps to handler)
594
+ task_type VARCHAR(255) NOT NULL,
595
+
596
+ -- Payload (JSON-serializable data)
597
+ payload JSONB NOT NULL DEFAULT '{}',
598
+
599
+ -- Context fields for querying
600
+ assistant_id VARCHAR(255),
601
+ thread_id VARCHAR(255),
602
+
603
+ -- Execution type: 'once' or 'cron'
604
+ execution_type VARCHAR(50) NOT NULL,
605
+
606
+ -- For ONCE type
607
+ execute_at TIMESTAMPTZ,
608
+ delay_ms BIGINT,
609
+
610
+ -- For CRON type
611
+ cron_expression VARCHAR(255),
612
+ timezone VARCHAR(100),
613
+ next_run_at TIMESTAMPTZ,
614
+ last_run_at TIMESTAMPTZ,
615
+
616
+ -- Status
617
+ status VARCHAR(50) NOT NULL DEFAULT 'pending',
618
+
619
+ -- Execution tracking
620
+ run_count INTEGER NOT NULL DEFAULT 0,
621
+ max_runs INTEGER,
622
+
623
+ -- Error handling
624
+ retry_count INTEGER NOT NULL DEFAULT 0,
625
+ max_retries INTEGER NOT NULL DEFAULT 0,
626
+ last_error TEXT,
627
+
628
+ -- Timestamps
629
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
630
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
631
+ expires_at TIMESTAMPTZ,
632
+
633
+ -- Metadata
634
+ metadata JSONB
635
+ );
636
+
637
+ -- Index for querying active tasks
638
+ CREATE INDEX IF NOT EXISTS idx_scheduled_tasks_status
639
+ ON lattice_scheduled_tasks (status);
640
+
641
+ -- Index for querying by task type
642
+ CREATE INDEX IF NOT EXISTS idx_scheduled_tasks_task_type
643
+ ON lattice_scheduled_tasks (task_type);
644
+
645
+ -- Index for querying by execution type
646
+ CREATE INDEX IF NOT EXISTS idx_scheduled_tasks_execution_type
647
+ ON lattice_scheduled_tasks (execution_type);
648
+
649
+ -- Index for querying by assistant ID
650
+ CREATE INDEX IF NOT EXISTS idx_scheduled_tasks_assistant_id
651
+ ON lattice_scheduled_tasks (assistant_id)
652
+ WHERE assistant_id IS NOT NULL;
653
+
654
+ -- Index for querying by thread ID
655
+ CREATE INDEX IF NOT EXISTS idx_scheduled_tasks_thread_id
656
+ ON lattice_scheduled_tasks (thread_id)
657
+ WHERE thread_id IS NOT NULL;
658
+
659
+ -- Index for querying pending tasks by next run time
660
+ CREATE INDEX IF NOT EXISTS idx_scheduled_tasks_next_run
661
+ ON lattice_scheduled_tasks (next_run_at)
662
+ WHERE status = 'pending';
663
+
664
+ -- Index for querying pending one-time tasks by execute time
665
+ CREATE INDEX IF NOT EXISTS idx_scheduled_tasks_execute_at
666
+ ON lattice_scheduled_tasks (execute_at)
667
+ WHERE status = 'pending' AND execution_type = 'once';
668
+
669
+ -- Index for cleanup queries (old completed/cancelled tasks)
670
+ CREATE INDEX IF NOT EXISTS idx_scheduled_tasks_cleanup
671
+ ON lattice_scheduled_tasks (updated_at)
672
+ WHERE status IN ('completed', 'cancelled', 'failed');
673
+ `);
674
+ },
675
+ down: async (client) => {
676
+ await client.query(`
677
+ DROP INDEX IF EXISTS idx_scheduled_tasks_cleanup;
678
+ DROP INDEX IF EXISTS idx_scheduled_tasks_execute_at;
679
+ DROP INDEX IF EXISTS idx_scheduled_tasks_next_run;
680
+ DROP INDEX IF EXISTS idx_scheduled_tasks_thread_id;
681
+ DROP INDEX IF EXISTS idx_scheduled_tasks_assistant_id;
682
+ DROP INDEX IF EXISTS idx_scheduled_tasks_execution_type;
683
+ DROP INDEX IF EXISTS idx_scheduled_tasks_task_type;
684
+ DROP INDEX IF EXISTS idx_scheduled_tasks_status;
685
+ DROP TABLE IF EXISTS lattice_scheduled_tasks;
686
+ `);
687
+ }
688
+ };
689
+
690
+ // src/stores/PostgreSQLScheduleStorage.ts
691
+ var PostgreSQLScheduleStorage = class {
692
+ constructor(options) {
693
+ this.initialized = false;
694
+ if (typeof options.poolConfig === "string") {
695
+ this.pool = new Pool3({ connectionString: options.poolConfig });
696
+ } else {
697
+ this.pool = new Pool3(options.poolConfig);
698
+ }
699
+ this.migrationManager = new MigrationManager(this.pool);
700
+ this.migrationManager.register(createScheduledTasksTable);
701
+ if (options.autoMigrate !== false) {
702
+ this.initialize().catch((error) => {
703
+ console.error("Failed to initialize PostgreSQLScheduleStorage:", error);
704
+ throw error;
705
+ });
706
+ }
707
+ }
708
+ /**
709
+ * Dispose resources and close the connection pool
710
+ */
711
+ async dispose() {
712
+ if (this.pool) {
713
+ await this.pool.end();
714
+ }
715
+ }
716
+ /**
717
+ * Initialize the store and run migrations
718
+ */
719
+ async initialize() {
720
+ if (this.initialized) {
721
+ return;
722
+ }
723
+ await this.migrationManager.migrate();
724
+ this.initialized = true;
725
+ }
726
+ /**
727
+ * Ensure store is initialized
728
+ */
729
+ async ensureInitialized() {
730
+ if (!this.initialized) {
731
+ await this.initialize();
732
+ }
733
+ }
734
+ /**
735
+ * Save a new task
736
+ */
737
+ async save(task) {
738
+ await this.ensureInitialized();
739
+ await this.pool.query(
740
+ `
741
+ INSERT INTO lattice_scheduled_tasks (
742
+ task_id, task_type, payload, assistant_id, thread_id, execution_type,
743
+ execute_at, delay_ms, cron_expression, timezone,
744
+ next_run_at, last_run_at, status, run_count, max_runs,
745
+ retry_count, max_retries, last_error,
746
+ created_at, updated_at, expires_at, metadata
747
+ ) VALUES (
748
+ $1, $2, $3, $4, $5, $6,
749
+ $7, $8, $9, $10,
750
+ $11, $12, $13, $14, $15,
751
+ $16, $17, $18,
752
+ $19, $20, $21, $22
753
+ )
754
+ ON CONFLICT (task_id) DO UPDATE SET
755
+ task_type = EXCLUDED.task_type,
756
+ payload = EXCLUDED.payload,
757
+ assistant_id = EXCLUDED.assistant_id,
758
+ thread_id = EXCLUDED.thread_id,
759
+ execution_type = EXCLUDED.execution_type,
760
+ execute_at = EXCLUDED.execute_at,
761
+ delay_ms = EXCLUDED.delay_ms,
762
+ cron_expression = EXCLUDED.cron_expression,
763
+ timezone = EXCLUDED.timezone,
764
+ next_run_at = EXCLUDED.next_run_at,
765
+ last_run_at = EXCLUDED.last_run_at,
766
+ status = EXCLUDED.status,
767
+ run_count = EXCLUDED.run_count,
768
+ max_runs = EXCLUDED.max_runs,
769
+ retry_count = EXCLUDED.retry_count,
770
+ max_retries = EXCLUDED.max_retries,
771
+ last_error = EXCLUDED.last_error,
772
+ updated_at = EXCLUDED.updated_at,
773
+ expires_at = EXCLUDED.expires_at,
774
+ metadata = EXCLUDED.metadata
775
+ `,
776
+ [
777
+ task.taskId,
778
+ task.taskType,
779
+ JSON.stringify(task.payload),
780
+ task.assistantId ?? null,
781
+ task.threadId ?? null,
782
+ task.executionType,
783
+ task.executeAt ? new Date(task.executeAt) : null,
784
+ task.delayMs ?? null,
785
+ task.cronExpression ?? null,
786
+ task.timezone ?? null,
787
+ task.nextRunAt ? new Date(task.nextRunAt) : null,
788
+ task.lastRunAt ? new Date(task.lastRunAt) : null,
789
+ task.status,
790
+ task.runCount,
791
+ task.maxRuns ?? null,
792
+ task.retryCount,
793
+ task.maxRetries,
794
+ task.lastError ?? null,
795
+ new Date(task.createdAt),
796
+ new Date(task.updatedAt),
797
+ task.expiresAt ? new Date(task.expiresAt) : null,
798
+ task.metadata ? JSON.stringify(task.metadata) : null
799
+ ]
800
+ );
801
+ }
802
+ /**
803
+ * Get task by ID
804
+ */
805
+ async get(taskId) {
806
+ await this.ensureInitialized();
807
+ const result = await this.pool.query(
808
+ `SELECT * FROM lattice_scheduled_tasks WHERE task_id = $1`,
809
+ [taskId]
810
+ );
811
+ if (result.rows.length === 0) {
812
+ return null;
813
+ }
814
+ return this.mapRowToTask(result.rows[0]);
815
+ }
816
+ /**
817
+ * Update task
818
+ */
819
+ async update(taskId, updates) {
820
+ await this.ensureInitialized();
821
+ const setClauses = [];
822
+ const values = [];
823
+ let paramIndex = 1;
824
+ if (updates.taskType !== void 0) {
825
+ setClauses.push(`task_type = $${paramIndex++}`);
826
+ values.push(updates.taskType);
827
+ }
828
+ if (updates.payload !== void 0) {
829
+ setClauses.push(`payload = $${paramIndex++}`);
830
+ values.push(JSON.stringify(updates.payload));
831
+ }
832
+ if (updates.assistantId !== void 0) {
833
+ setClauses.push(`assistant_id = $${paramIndex++}`);
834
+ values.push(updates.assistantId);
835
+ }
836
+ if (updates.threadId !== void 0) {
837
+ setClauses.push(`thread_id = $${paramIndex++}`);
838
+ values.push(updates.threadId);
839
+ }
840
+ if (updates.executionType !== void 0) {
841
+ setClauses.push(`execution_type = $${paramIndex++}`);
842
+ values.push(updates.executionType);
843
+ }
844
+ if (updates.executeAt !== void 0) {
845
+ setClauses.push(`execute_at = $${paramIndex++}`);
846
+ values.push(updates.executeAt ? new Date(updates.executeAt) : null);
847
+ }
848
+ if (updates.delayMs !== void 0) {
849
+ setClauses.push(`delay_ms = $${paramIndex++}`);
850
+ values.push(updates.delayMs);
851
+ }
852
+ if (updates.cronExpression !== void 0) {
853
+ setClauses.push(`cron_expression = $${paramIndex++}`);
854
+ values.push(updates.cronExpression);
855
+ }
856
+ if (updates.timezone !== void 0) {
857
+ setClauses.push(`timezone = $${paramIndex++}`);
858
+ values.push(updates.timezone);
859
+ }
860
+ if (updates.nextRunAt !== void 0) {
861
+ setClauses.push(`next_run_at = $${paramIndex++}`);
862
+ values.push(updates.nextRunAt ? new Date(updates.nextRunAt) : null);
863
+ }
864
+ if (updates.lastRunAt !== void 0) {
865
+ setClauses.push(`last_run_at = $${paramIndex++}`);
866
+ values.push(updates.lastRunAt ? new Date(updates.lastRunAt) : null);
867
+ }
868
+ if (updates.status !== void 0) {
869
+ setClauses.push(`status = $${paramIndex++}`);
870
+ values.push(updates.status);
871
+ }
872
+ if (updates.runCount !== void 0) {
873
+ setClauses.push(`run_count = $${paramIndex++}`);
874
+ values.push(updates.runCount);
875
+ }
876
+ if (updates.maxRuns !== void 0) {
877
+ setClauses.push(`max_runs = $${paramIndex++}`);
878
+ values.push(updates.maxRuns);
879
+ }
880
+ if (updates.retryCount !== void 0) {
881
+ setClauses.push(`retry_count = $${paramIndex++}`);
882
+ values.push(updates.retryCount);
883
+ }
884
+ if (updates.maxRetries !== void 0) {
885
+ setClauses.push(`max_retries = $${paramIndex++}`);
886
+ values.push(updates.maxRetries);
887
+ }
888
+ if (updates.lastError !== void 0) {
889
+ setClauses.push(`last_error = $${paramIndex++}`);
890
+ values.push(updates.lastError);
891
+ }
892
+ if (updates.expiresAt !== void 0) {
893
+ setClauses.push(`expires_at = $${paramIndex++}`);
894
+ values.push(updates.expiresAt ? new Date(updates.expiresAt) : null);
895
+ }
896
+ if (updates.metadata !== void 0) {
897
+ setClauses.push(`metadata = $${paramIndex++}`);
898
+ values.push(updates.metadata ? JSON.stringify(updates.metadata) : null);
899
+ }
900
+ setClauses.push(`updated_at = $${paramIndex++}`);
901
+ values.push(/* @__PURE__ */ new Date());
902
+ values.push(taskId);
903
+ if (setClauses.length === 0) {
904
+ return;
905
+ }
906
+ await this.pool.query(
907
+ `UPDATE lattice_scheduled_tasks SET ${setClauses.join(
908
+ ", "
909
+ )} WHERE task_id = $${paramIndex}`,
910
+ values
911
+ );
912
+ }
913
+ /**
914
+ * Delete task
915
+ */
916
+ async delete(taskId) {
917
+ await this.ensureInitialized();
918
+ await this.pool.query(
919
+ `DELETE FROM lattice_scheduled_tasks WHERE task_id = $1`,
920
+ [taskId]
921
+ );
922
+ }
923
+ /**
924
+ * Get all active tasks (pending or paused)
925
+ */
926
+ async getActiveTasks() {
927
+ await this.ensureInitialized();
928
+ const result = await this.pool.query(
929
+ `SELECT * FROM lattice_scheduled_tasks WHERE status IN ($1, $2) ORDER BY created_at ASC`,
930
+ [ScheduledTaskStatus.PENDING, ScheduledTaskStatus.PAUSED]
931
+ );
932
+ return result.rows.map((row) => this.mapRowToTask(row));
933
+ }
934
+ /**
935
+ * Get tasks by type
936
+ */
937
+ async getTasksByType(taskType) {
938
+ await this.ensureInitialized();
939
+ const result = await this.pool.query(
940
+ `SELECT * FROM lattice_scheduled_tasks WHERE task_type = $1 ORDER BY created_at DESC`,
941
+ [taskType]
942
+ );
943
+ return result.rows.map((row) => this.mapRowToTask(row));
944
+ }
945
+ /**
946
+ * Get tasks by status
947
+ */
948
+ async getTasksByStatus(status) {
949
+ await this.ensureInitialized();
950
+ const result = await this.pool.query(
951
+ `SELECT * FROM lattice_scheduled_tasks WHERE status = $1 ORDER BY created_at DESC`,
952
+ [status]
953
+ );
954
+ return result.rows.map((row) => this.mapRowToTask(row));
955
+ }
956
+ /**
957
+ * Get tasks by execution type
958
+ */
959
+ async getTasksByExecutionType(executionType) {
960
+ await this.ensureInitialized();
961
+ const result = await this.pool.query(
962
+ `SELECT * FROM lattice_scheduled_tasks WHERE execution_type = $1 ORDER BY created_at DESC`,
963
+ [executionType]
964
+ );
965
+ return result.rows.map((row) => this.mapRowToTask(row));
966
+ }
967
+ /**
968
+ * Get tasks by assistant ID
969
+ */
970
+ async getTasksByAssistantId(assistantId) {
971
+ await this.ensureInitialized();
972
+ const result = await this.pool.query(
973
+ `SELECT * FROM lattice_scheduled_tasks WHERE assistant_id = $1 ORDER BY created_at DESC`,
974
+ [assistantId]
975
+ );
976
+ return result.rows.map((row) => this.mapRowToTask(row));
977
+ }
978
+ /**
979
+ * Get tasks by thread ID
980
+ */
981
+ async getTasksByThreadId(threadId) {
982
+ await this.ensureInitialized();
983
+ const result = await this.pool.query(
984
+ `SELECT * FROM lattice_scheduled_tasks WHERE thread_id = $1 ORDER BY created_at DESC`,
985
+ [threadId]
986
+ );
987
+ return result.rows.map((row) => this.mapRowToTask(row));
988
+ }
989
+ /**
990
+ * Get all tasks with optional filters
991
+ */
992
+ async getAllTasks(filters) {
993
+ await this.ensureInitialized();
994
+ const whereClauses = [];
995
+ const values = [];
996
+ let paramIndex = 1;
997
+ if (filters?.status !== void 0) {
998
+ whereClauses.push(`status = $${paramIndex++}`);
999
+ values.push(filters.status);
1000
+ }
1001
+ if (filters?.executionType !== void 0) {
1002
+ whereClauses.push(`execution_type = $${paramIndex++}`);
1003
+ values.push(filters.executionType);
1004
+ }
1005
+ if (filters?.taskType !== void 0) {
1006
+ whereClauses.push(`task_type = $${paramIndex++}`);
1007
+ values.push(filters.taskType);
1008
+ }
1009
+ if (filters?.assistantId !== void 0) {
1010
+ whereClauses.push(`assistant_id = $${paramIndex++}`);
1011
+ values.push(filters.assistantId);
1012
+ }
1013
+ if (filters?.threadId !== void 0) {
1014
+ whereClauses.push(`thread_id = $${paramIndex++}`);
1015
+ values.push(filters.threadId);
1016
+ }
1017
+ let query = `SELECT * FROM lattice_scheduled_tasks`;
1018
+ if (whereClauses.length > 0) {
1019
+ query += ` WHERE ${whereClauses.join(" AND ")}`;
1020
+ }
1021
+ query += ` ORDER BY created_at DESC`;
1022
+ if (filters?.limit !== void 0) {
1023
+ query += ` LIMIT $${paramIndex++}`;
1024
+ values.push(filters.limit);
1025
+ }
1026
+ if (filters?.offset !== void 0) {
1027
+ query += ` OFFSET $${paramIndex++}`;
1028
+ values.push(filters.offset);
1029
+ }
1030
+ const result = await this.pool.query(query, values);
1031
+ return result.rows.map((row) => this.mapRowToTask(row));
1032
+ }
1033
+ /**
1034
+ * Count tasks with optional filters
1035
+ */
1036
+ async countTasks(filters) {
1037
+ await this.ensureInitialized();
1038
+ const whereClauses = [];
1039
+ const values = [];
1040
+ let paramIndex = 1;
1041
+ if (filters?.status !== void 0) {
1042
+ whereClauses.push(`status = $${paramIndex++}`);
1043
+ values.push(filters.status);
1044
+ }
1045
+ if (filters?.executionType !== void 0) {
1046
+ whereClauses.push(`execution_type = $${paramIndex++}`);
1047
+ values.push(filters.executionType);
1048
+ }
1049
+ if (filters?.taskType !== void 0) {
1050
+ whereClauses.push(`task_type = $${paramIndex++}`);
1051
+ values.push(filters.taskType);
1052
+ }
1053
+ if (filters?.assistantId !== void 0) {
1054
+ whereClauses.push(`assistant_id = $${paramIndex++}`);
1055
+ values.push(filters.assistantId);
1056
+ }
1057
+ if (filters?.threadId !== void 0) {
1058
+ whereClauses.push(`thread_id = $${paramIndex++}`);
1059
+ values.push(filters.threadId);
1060
+ }
1061
+ let query = `SELECT COUNT(*) as count FROM lattice_scheduled_tasks`;
1062
+ if (whereClauses.length > 0) {
1063
+ query += ` WHERE ${whereClauses.join(" AND ")}`;
1064
+ }
1065
+ const result = await this.pool.query(query, values);
1066
+ return parseInt(result.rows[0].count, 10);
1067
+ }
1068
+ /**
1069
+ * Delete completed/cancelled/failed tasks older than specified time
1070
+ */
1071
+ async deleteOldTasks(olderThanMs) {
1072
+ await this.ensureInitialized();
1073
+ const cutoff = new Date(Date.now() - olderThanMs);
1074
+ const result = await this.pool.query(
1075
+ `
1076
+ DELETE FROM lattice_scheduled_tasks
1077
+ WHERE status IN ($1, $2, $3)
1078
+ AND updated_at < $4
1079
+ `,
1080
+ [
1081
+ ScheduledTaskStatus.COMPLETED,
1082
+ ScheduledTaskStatus.CANCELLED,
1083
+ ScheduledTaskStatus.FAILED,
1084
+ cutoff
1085
+ ]
1086
+ );
1087
+ return result.rowCount ?? 0;
1088
+ }
1089
+ /**
1090
+ * Map database row to ScheduledTaskDefinition
1091
+ */
1092
+ mapRowToTask(row) {
1093
+ return {
1094
+ taskId: row.task_id,
1095
+ taskType: row.task_type,
1096
+ payload: typeof row.payload === "string" ? JSON.parse(row.payload) : row.payload || {},
1097
+ assistantId: row.assistant_id ?? void 0,
1098
+ threadId: row.thread_id ?? void 0,
1099
+ executionType: row.execution_type,
1100
+ executeAt: row.execute_at ? row.execute_at.getTime() : void 0,
1101
+ delayMs: row.delay_ms ?? void 0,
1102
+ cronExpression: row.cron_expression ?? void 0,
1103
+ timezone: row.timezone ?? void 0,
1104
+ nextRunAt: row.next_run_at ? row.next_run_at.getTime() : void 0,
1105
+ lastRunAt: row.last_run_at ? row.last_run_at.getTime() : void 0,
1106
+ status: row.status,
1107
+ runCount: row.run_count,
1108
+ maxRuns: row.max_runs ?? void 0,
1109
+ retryCount: row.retry_count,
1110
+ maxRetries: row.max_retries,
1111
+ lastError: row.last_error ?? void 0,
1112
+ createdAt: row.created_at.getTime(),
1113
+ updatedAt: row.updated_at.getTime(),
1114
+ expiresAt: row.expires_at ? row.expires_at.getTime() : void 0,
1115
+ metadata: row.metadata === null ? void 0 : typeof row.metadata === "string" ? JSON.parse(row.metadata) : row.metadata
1116
+ };
1117
+ }
1118
+ };
576
1119
  export {
577
1120
  MigrationManager,
578
1121
  PostgreSQLAssistantStore,
1122
+ PostgreSQLScheduleStorage,
579
1123
  PostgreSQLThreadStore,
580
1124
  createAssistantsTable,
1125
+ createScheduledTasksTable,
581
1126
  createThreadsTable
582
1127
  };
583
1128
  //# sourceMappingURL=index.mjs.map