@axiom-lattice/local-stores 3.1.23 → 3.2.0
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/.turbo/turbo-build.log +10 -10
- package/CHANGELOG.md +64 -0
- package/dist/index.d.mts +12 -1
- package/dist/index.d.ts +12 -1
- package/dist/index.js +66 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +66 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/__tests__/LocalScheduleStorage.test.ts +143 -0
- package/src/__tests__/LocalThreadMessageQueueStore.test.ts +40 -0
- package/src/stores/LocalScheduleStorage.ts +23 -0
- package/src/stores/LocalThreadMessageQueueStore.ts +56 -1
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @axiom-lattice/local-stores@3.
|
|
2
|
+
> @axiom-lattice/local-stores@3.2.0 build /home/runner/work/agentic/agentic/packages/local-stores
|
|
3
3
|
> tsup src/index.ts --format cjs,esm --dts --sourcemap
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: src/index.ts
|
|
@@ -8,13 +8,13 @@
|
|
|
8
8
|
[34mCLI[39m Target: es2020
|
|
9
9
|
[34mCJS[39m Build start
|
|
10
10
|
[34mESM[39m Build start
|
|
11
|
-
[
|
|
12
|
-
[
|
|
13
|
-
[
|
|
14
|
-
[
|
|
15
|
-
[
|
|
16
|
-
[
|
|
11
|
+
[32mESM[39m [1mdist/index.mjs [22m[32m237.70 KB[39m
|
|
12
|
+
[32mESM[39m [1mdist/index.mjs.map [22m[32m436.10 KB[39m
|
|
13
|
+
[32mESM[39m ⚡️ Build success in 568ms
|
|
14
|
+
[32mCJS[39m [1mdist/index.js [22m[32m242.32 KB[39m
|
|
15
|
+
[32mCJS[39m [1mdist/index.js.map [22m[32m439.84 KB[39m
|
|
16
|
+
[32mCJS[39m ⚡️ Build success in 569ms
|
|
17
17
|
[34mDTS[39m Build start
|
|
18
|
-
[32mDTS[39m ⚡️ Build success in
|
|
19
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[32m44.
|
|
20
|
-
[32mDTS[39m [1mdist/index.d.mts [22m[32m44.
|
|
18
|
+
[32mDTS[39m ⚡️ Build success in 12893ms
|
|
19
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m44.67 KB[39m
|
|
20
|
+
[32mDTS[39m [1mdist/index.d.mts [22m[32m44.67 KB[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,69 @@
|
|
|
1
1
|
# @axiom-lattice/local-stores
|
|
2
2
|
|
|
3
|
+
## 3.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- eafaac8: Multi-gateway (PostgreSQL-only) coordination primitives — Plans 1 & 2.
|
|
8
|
+
|
|
9
|
+
**Plan 1 — replica identity + distributed leases**
|
|
10
|
+
|
|
11
|
+
- `getReplicaId()` stable per-process replica identity (`AXIOM_REPLICA_ID` or `hostname:pid`).
|
|
12
|
+
- `LeaseStore` / `LeaseRecord` protocol, `InMemoryLeaseStore` (registered as the default `"lease"` store), and `PostgreSQLLeaseStore` (migration v185 `lattice_leases`; atomic self-expiring upsert).
|
|
13
|
+
- `withLease(store, key, ownerLabel, fn)` — distributed critical-section helper with a unique per-invocation holder, bounded acquisition, heartbeat, and release-in-finally.
|
|
14
|
+
|
|
15
|
+
**Plan 2 — atomic thread-message-queue claim + lease**
|
|
16
|
+
|
|
17
|
+
- `ClaimMessagesOptions` and `IMessageQueueStore.claimPendingMessages` / `reclaimExpiredProcessing`.
|
|
18
|
+
- PostgreSQL migration v186 adds `locked_by` / `locked_at` / `lease_expires_at`; claim is an atomic `UPDATE … WHERE id IN (SELECT … FOR UPDATE SKIP LOCKED)` so concurrent Gateway replicas receive disjoint sets.
|
|
19
|
+
- In-memory and local SQLite implementations match the same semantics.
|
|
20
|
+
|
|
21
|
+
No existing behavior changes: the legacy `markProcessing` path is untouched; `Agent` is not yet rewired to the new claim (that is a follow-up plan).
|
|
22
|
+
|
|
23
|
+
- 6080389: Scheduler exactly-once across replicas (claim-before-execute).
|
|
24
|
+
|
|
25
|
+
- `ScheduleStorage.claimScheduledRun(taskId)` atomically transitions `pending -> running` (Memory/Local/PostgreSQL implementations).
|
|
26
|
+
- `DefaultScheduleClient.executeTask` claims before running the handler, so a fire armed on multiple replicas executes only once (losers skip). Retry/CRON-rearm paths re-set `PENDING`, so later fires are claimable. See ADR-158.
|
|
27
|
+
|
|
28
|
+
- 7ecbd01: Recover scheduled tasks stuck `running` after a crash.
|
|
29
|
+
|
|
30
|
+
- `ScheduleStorage.requeueStaleRunning(olderThanMs)` requeues `running` tasks older than a threshold back to `pending` (Memory/Local/PostgreSQL).
|
|
31
|
+
- `DefaultScheduleClient.restore()` requeues stale `running` schedules (30-minute heuristic) before re-arming, so a CRON schedule is no longer permanently stalled by a crash that left an unsettled claim. See ADR-158.
|
|
32
|
+
|
|
33
|
+
### Patch Changes
|
|
34
|
+
|
|
35
|
+
- Updated dependencies [6addbbf]
|
|
36
|
+
- Updated dependencies [66e6080]
|
|
37
|
+
- Updated dependencies [360f9e9]
|
|
38
|
+
- Updated dependencies [eafaac8]
|
|
39
|
+
- Updated dependencies [b8c4c92]
|
|
40
|
+
- Updated dependencies [4c99955]
|
|
41
|
+
- Updated dependencies [167620b]
|
|
42
|
+
- Updated dependencies [d3cd2d5]
|
|
43
|
+
- Updated dependencies [396ad50]
|
|
44
|
+
- Updated dependencies [eec9db7]
|
|
45
|
+
- Updated dependencies [d4429f1]
|
|
46
|
+
- Updated dependencies [6080389]
|
|
47
|
+
- Updated dependencies [7ecbd01]
|
|
48
|
+
- Updated dependencies [e5e7b0f]
|
|
49
|
+
- Updated dependencies [00b179d]
|
|
50
|
+
- Updated dependencies [5f3d14b]
|
|
51
|
+
- @axiom-lattice/core@6.3.0
|
|
52
|
+
- @axiom-lattice/protocols@4.7.0
|
|
53
|
+
|
|
54
|
+
## 3.1.24
|
|
55
|
+
|
|
56
|
+
### Patch Changes
|
|
57
|
+
|
|
58
|
+
- Updated dependencies [2290163]
|
|
59
|
+
- Updated dependencies [c5e45fb]
|
|
60
|
+
- Updated dependencies [89c1f03]
|
|
61
|
+
- Updated dependencies [3b757a0]
|
|
62
|
+
- Updated dependencies [3450e85]
|
|
63
|
+
- Updated dependencies [73961ef]
|
|
64
|
+
- @axiom-lattice/core@6.2.0
|
|
65
|
+
- @axiom-lattice/protocols@4.6.1
|
|
66
|
+
|
|
3
67
|
## 3.1.23
|
|
4
68
|
|
|
5
69
|
### Patch Changes
|
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { SqlJsStatic, Database } from 'sql.js';
|
|
2
2
|
import { SqliteSaver } from '@langchain/langgraph-checkpoint-sqlite';
|
|
3
3
|
import { ThreadStore, Thread, CreateThreadRequest, AssistantStore, Assistant, CreateAssistantRequest, WorkspaceStore, Workspace, CreateWorkspaceRequest, UpdateWorkspaceRequest, ProjectStore, ProjectFilter, Project, CreateProjectRequest, UpdateProjectRequest, UpdateProjectCapabilityBundlesResult, ProjectRoomStore, ProjectRoom, ProjectMembershipStore, ProjectMembership, ProjectHumanRole, ProjectMembershipMutationResult, ProjectBotMembershipStore, ProjectBotMembership, ProjectRoomMessageStore, ProjectRoomMessage, ProjectRoomReadStateStore, ProjectRoomReadState, UserStore, User, CreateUserRequest, UpdateUserRequest, TenantStore, Tenant, CreateTenantRequest, UpdateTenantRequest, UserTenantLinkStore, UserTenantLink, CreateUserTenantLinkRequest, UpdateUserTenantLinkRequest, DatabaseConfigStore, DatabaseConfigEntry, CreateDatabaseConfigRequest, UpdateDatabaseConfigRequest, ConnectionStore, ConnectionEntry, MetricsServerConfigStore, MetricsServerConfigEntry, CreateMetricsServerConfigRequest, UpdateMetricsServerConfigRequest, McpServerConfigStore, McpServerConfigEntry, CreateMcpServerConfigRequest, UpdateMcpServerConfigRequest, ModelProviderStore, ModelProviderEntry, CreateModelProviderRequest, UpdateModelProviderRequest, ModelProviderStatus, ModelProviderModelEntry, ProviderModelInput, WorkflowTrackingStore, CreateWorkflowRunRequest, WorkflowRun, UpdateWorkflowRunRequest, QueryWorkflowRunsOptions, QueryWorkflowRunsResult, CreateRunStepRequest, RunStep, UpdateRunStepRequest, StepType, EvalStore, EvalProject, CreateEvalProjectRequest, EvalSuite, CreateEvalSuiteRequest, EvalCase, CreateEvalCaseRequest, EvalRun, CreateEvalRunRequest, EvalRunResult, EvalProjectReport, BindingRegistry, Binding, CreateBindingInput, BindingMutablePatch, BindingListParams, ChannelInstallationStore, ChannelInstallation, ChannelInstallationType, CreateChannelInstallationInput, UpdateChannelInstallationRequest, A2AApiKeyStore, A2AApiKeyRecord, CreateA2AApiKeyInput, UpdateA2AApiKeyInput, A2AApiKeyEntry, OpenAuditStore, OpenAuditQuery, OpenAuditRecord, OpenAuditAppendInput, SkillStore, SkillStoreContext, Skill, CreateSkillRequest, ScheduleStorage, ScheduledTaskDefinition, ScheduledTaskStatus, ScheduleExecutionType, TaskStore, CreateTaskRequest, TaskItem, TaskListFilter, TaskDependentListQuery, UpdateTaskRequest, TaskMutationSnapshot, TaskWorkItemStore, CreateWorkItemRequest, TaskWorkItem, CreateWorkItemIfAbsentRequest, TaskWorkItemListFilter, ProjectLifecycleEventQuery, CapabilityBundleStore, CapabilityBundle, InternalCreateCapabilityBundleInput, InternalUpdateCapabilityBundleInput, CapabilityBundleDeleteResult, IConversationStore, ConversationRecord, CreateConversationInput } from '@axiom-lattice/protocols';
|
|
4
|
-
import { IMessageQueueStore, AddMessageParams, PendingMessage, QueueScope, ThreadInfo } from '@axiom-lattice/core';
|
|
4
|
+
import { IMessageQueueStore, AddMessageParams, PendingMessage, QueueScope, ClaimMessagesOptions, ThreadInfo } from '@axiom-lattice/core';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Shared SQLite database connection for local stores using sql.js (WASM).
|
|
@@ -720,6 +720,8 @@ declare class LocalThreadMessageQueueStore implements IMessageQueueStore {
|
|
|
720
720
|
addMessageAtHead(params: AddMessageParams): Promise<PendingMessage>;
|
|
721
721
|
getPendingMessages(threadId: string, scope?: QueueScope): Promise<PendingMessage[]>;
|
|
722
722
|
getProcessingMessages(threadId: string, scope?: QueueScope): Promise<PendingMessage[]>;
|
|
723
|
+
claimPendingMessages(threadId: string, scope: QueueScope | undefined, options: ClaimMessagesOptions): Promise<PendingMessage[]>;
|
|
724
|
+
reclaimExpiredProcessing(threadId: string, scope?: QueueScope): Promise<number>;
|
|
723
725
|
getQueueSize(threadId: string, scope?: QueueScope): Promise<number>;
|
|
724
726
|
getThreadsWithPendingMessages(): Promise<ThreadInfo[]>;
|
|
725
727
|
removeMessage(messageId: string, scope?: QueueScope): Promise<boolean>;
|
|
@@ -760,6 +762,15 @@ declare class LocalScheduleStorage implements ScheduleStorage {
|
|
|
760
762
|
save(task: ScheduledTaskDefinition): Promise<void>;
|
|
761
763
|
get(taskId: string): Promise<ScheduledTaskDefinition | null>;
|
|
762
764
|
update(taskId: string, updates: Partial<ScheduledTaskDefinition>): Promise<void>;
|
|
765
|
+
claimScheduledRun(taskId: string): Promise<boolean>;
|
|
766
|
+
/**
|
|
767
|
+
* Requeues `running` tasks whose `updated_at` is older than `olderThanMs` back
|
|
768
|
+
* to `pending` (recovery after a crash that left a claim unsettled).
|
|
769
|
+
*
|
|
770
|
+
* @param olderThanMs Age threshold in milliseconds.
|
|
771
|
+
* @returns The number of tasks requeued.
|
|
772
|
+
*/
|
|
773
|
+
requeueStaleRunning(olderThanMs: number): Promise<number>;
|
|
763
774
|
delete(taskId: string): Promise<void>;
|
|
764
775
|
getActiveTasks(): Promise<ScheduledTaskDefinition[]>;
|
|
765
776
|
getTasksByType(taskType: string): Promise<ScheduledTaskDefinition[]>;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { SqlJsStatic, Database } from 'sql.js';
|
|
2
2
|
import { SqliteSaver } from '@langchain/langgraph-checkpoint-sqlite';
|
|
3
3
|
import { ThreadStore, Thread, CreateThreadRequest, AssistantStore, Assistant, CreateAssistantRequest, WorkspaceStore, Workspace, CreateWorkspaceRequest, UpdateWorkspaceRequest, ProjectStore, ProjectFilter, Project, CreateProjectRequest, UpdateProjectRequest, UpdateProjectCapabilityBundlesResult, ProjectRoomStore, ProjectRoom, ProjectMembershipStore, ProjectMembership, ProjectHumanRole, ProjectMembershipMutationResult, ProjectBotMembershipStore, ProjectBotMembership, ProjectRoomMessageStore, ProjectRoomMessage, ProjectRoomReadStateStore, ProjectRoomReadState, UserStore, User, CreateUserRequest, UpdateUserRequest, TenantStore, Tenant, CreateTenantRequest, UpdateTenantRequest, UserTenantLinkStore, UserTenantLink, CreateUserTenantLinkRequest, UpdateUserTenantLinkRequest, DatabaseConfigStore, DatabaseConfigEntry, CreateDatabaseConfigRequest, UpdateDatabaseConfigRequest, ConnectionStore, ConnectionEntry, MetricsServerConfigStore, MetricsServerConfigEntry, CreateMetricsServerConfigRequest, UpdateMetricsServerConfigRequest, McpServerConfigStore, McpServerConfigEntry, CreateMcpServerConfigRequest, UpdateMcpServerConfigRequest, ModelProviderStore, ModelProviderEntry, CreateModelProviderRequest, UpdateModelProviderRequest, ModelProviderStatus, ModelProviderModelEntry, ProviderModelInput, WorkflowTrackingStore, CreateWorkflowRunRequest, WorkflowRun, UpdateWorkflowRunRequest, QueryWorkflowRunsOptions, QueryWorkflowRunsResult, CreateRunStepRequest, RunStep, UpdateRunStepRequest, StepType, EvalStore, EvalProject, CreateEvalProjectRequest, EvalSuite, CreateEvalSuiteRequest, EvalCase, CreateEvalCaseRequest, EvalRun, CreateEvalRunRequest, EvalRunResult, EvalProjectReport, BindingRegistry, Binding, CreateBindingInput, BindingMutablePatch, BindingListParams, ChannelInstallationStore, ChannelInstallation, ChannelInstallationType, CreateChannelInstallationInput, UpdateChannelInstallationRequest, A2AApiKeyStore, A2AApiKeyRecord, CreateA2AApiKeyInput, UpdateA2AApiKeyInput, A2AApiKeyEntry, OpenAuditStore, OpenAuditQuery, OpenAuditRecord, OpenAuditAppendInput, SkillStore, SkillStoreContext, Skill, CreateSkillRequest, ScheduleStorage, ScheduledTaskDefinition, ScheduledTaskStatus, ScheduleExecutionType, TaskStore, CreateTaskRequest, TaskItem, TaskListFilter, TaskDependentListQuery, UpdateTaskRequest, TaskMutationSnapshot, TaskWorkItemStore, CreateWorkItemRequest, TaskWorkItem, CreateWorkItemIfAbsentRequest, TaskWorkItemListFilter, ProjectLifecycleEventQuery, CapabilityBundleStore, CapabilityBundle, InternalCreateCapabilityBundleInput, InternalUpdateCapabilityBundleInput, CapabilityBundleDeleteResult, IConversationStore, ConversationRecord, CreateConversationInput } from '@axiom-lattice/protocols';
|
|
4
|
-
import { IMessageQueueStore, AddMessageParams, PendingMessage, QueueScope, ThreadInfo } from '@axiom-lattice/core';
|
|
4
|
+
import { IMessageQueueStore, AddMessageParams, PendingMessage, QueueScope, ClaimMessagesOptions, ThreadInfo } from '@axiom-lattice/core';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Shared SQLite database connection for local stores using sql.js (WASM).
|
|
@@ -720,6 +720,8 @@ declare class LocalThreadMessageQueueStore implements IMessageQueueStore {
|
|
|
720
720
|
addMessageAtHead(params: AddMessageParams): Promise<PendingMessage>;
|
|
721
721
|
getPendingMessages(threadId: string, scope?: QueueScope): Promise<PendingMessage[]>;
|
|
722
722
|
getProcessingMessages(threadId: string, scope?: QueueScope): Promise<PendingMessage[]>;
|
|
723
|
+
claimPendingMessages(threadId: string, scope: QueueScope | undefined, options: ClaimMessagesOptions): Promise<PendingMessage[]>;
|
|
724
|
+
reclaimExpiredProcessing(threadId: string, scope?: QueueScope): Promise<number>;
|
|
723
725
|
getQueueSize(threadId: string, scope?: QueueScope): Promise<number>;
|
|
724
726
|
getThreadsWithPendingMessages(): Promise<ThreadInfo[]>;
|
|
725
727
|
removeMessage(messageId: string, scope?: QueueScope): Promise<boolean>;
|
|
@@ -760,6 +762,15 @@ declare class LocalScheduleStorage implements ScheduleStorage {
|
|
|
760
762
|
save(task: ScheduledTaskDefinition): Promise<void>;
|
|
761
763
|
get(taskId: string): Promise<ScheduledTaskDefinition | null>;
|
|
762
764
|
update(taskId: string, updates: Partial<ScheduledTaskDefinition>): Promise<void>;
|
|
765
|
+
claimScheduledRun(taskId: string): Promise<boolean>;
|
|
766
|
+
/**
|
|
767
|
+
* Requeues `running` tasks whose `updated_at` is older than `olderThanMs` back
|
|
768
|
+
* to `pending` (recovery after a crash that left a claim unsettled).
|
|
769
|
+
*
|
|
770
|
+
* @param olderThanMs Age threshold in milliseconds.
|
|
771
|
+
* @returns The number of tasks requeued.
|
|
772
|
+
*/
|
|
773
|
+
requeueStaleRunning(olderThanMs: number): Promise<number>;
|
|
763
774
|
delete(taskId: string): Promise<void>;
|
|
764
775
|
getActiveTasks(): Promise<ScheduledTaskDefinition[]>;
|
|
765
776
|
getTasksByType(taskType: string): Promise<ScheduledTaskDefinition[]>;
|
package/dist/index.js
CHANGED
|
@@ -4580,6 +4580,9 @@ CREATE TABLE IF NOT EXISTS lt_thread_message_queue (
|
|
|
4580
4580
|
custom_run_config TEXT,
|
|
4581
4581
|
trusted_run_context TEXT,
|
|
4582
4582
|
execution_mode TEXT CHECK (execution_mode IS NULL OR execution_mode = 'followup'),
|
|
4583
|
+
locked_by TEXT,
|
|
4584
|
+
locked_at TEXT,
|
|
4585
|
+
lease_expires_at TEXT,
|
|
4583
4586
|
created_at TEXT NOT NULL
|
|
4584
4587
|
);
|
|
4585
4588
|
CREATE INDEX IF NOT EXISTS idx_lt_tmq_thread ON lt_thread_message_queue(thread_id, status);
|
|
@@ -4598,6 +4601,16 @@ var LocalThreadMessageQueueStore = class {
|
|
|
4598
4601
|
if (!names.has("execution_mode")) {
|
|
4599
4602
|
db.run("ALTER TABLE lt_thread_message_queue ADD COLUMN execution_mode TEXT CHECK (execution_mode IS NULL OR execution_mode = 'followup')");
|
|
4600
4603
|
}
|
|
4604
|
+
if (!names.has("locked_by")) {
|
|
4605
|
+
db.run("ALTER TABLE lt_thread_message_queue ADD COLUMN locked_by TEXT");
|
|
4606
|
+
}
|
|
4607
|
+
if (!names.has("locked_at")) {
|
|
4608
|
+
db.run("ALTER TABLE lt_thread_message_queue ADD COLUMN locked_at TEXT");
|
|
4609
|
+
}
|
|
4610
|
+
if (!names.has("lease_expires_at")) {
|
|
4611
|
+
db.run("ALTER TABLE lt_thread_message_queue ADD COLUMN lease_expires_at TEXT");
|
|
4612
|
+
}
|
|
4613
|
+
db.run("CREATE INDEX IF NOT EXISTS idx_lt_tmq_lease ON lt_thread_message_queue(status, lease_expires_at)");
|
|
4601
4614
|
}
|
|
4602
4615
|
async addMessage(params) {
|
|
4603
4616
|
const trusted = validateQueueTrust(params);
|
|
@@ -4722,6 +4735,37 @@ var LocalThreadMessageQueueStore = class {
|
|
|
4722
4735
|
).all(threadId, ...filter.params);
|
|
4723
4736
|
return rows2.map(rowToMessage);
|
|
4724
4737
|
}
|
|
4738
|
+
async claimPendingMessages(threadId, scope, options) {
|
|
4739
|
+
const filter = scopeClause(scope);
|
|
4740
|
+
const limitClause = options.limit === void 0 ? "" : ` LIMIT ${Math.max(0, Math.floor(options.limit))}`;
|
|
4741
|
+
const claimedRows = transaction(this.db, () => {
|
|
4742
|
+
const candidates = this.db.prepare(
|
|
4743
|
+
`SELECT * FROM lt_thread_message_queue WHERE thread_id = ? AND status = 'pending'${filter.sql} ORDER BY priority DESC, sequence_order ASC${limitClause}`
|
|
4744
|
+
).all(threadId, ...filter.params);
|
|
4745
|
+
const startedAt = Date.now();
|
|
4746
|
+
const lockedAt = new Date(startedAt).toISOString();
|
|
4747
|
+
const leaseExpiresAt = new Date(startedAt + options.ttlMs).toISOString();
|
|
4748
|
+
const claimed = [];
|
|
4749
|
+
for (const row of candidates) {
|
|
4750
|
+
const changes = write(
|
|
4751
|
+
this.db,
|
|
4752
|
+
`UPDATE lt_thread_message_queue SET status = 'processing', locked_by = ?, locked_at = ?, lease_expires_at = ? WHERE id = ? AND status = 'pending'`,
|
|
4753
|
+
[options.owner, lockedAt, leaseExpiresAt, row.id]
|
|
4754
|
+
);
|
|
4755
|
+
if (changes > 0) claimed.push(row);
|
|
4756
|
+
}
|
|
4757
|
+
return claimed;
|
|
4758
|
+
});
|
|
4759
|
+
return claimedRows.map(rowToMessage);
|
|
4760
|
+
}
|
|
4761
|
+
async reclaimExpiredProcessing(threadId, scope) {
|
|
4762
|
+
const filter = scopeClause(scope);
|
|
4763
|
+
return transaction(this.db, () => write(
|
|
4764
|
+
this.db,
|
|
4765
|
+
`UPDATE lt_thread_message_queue SET status = 'pending', locked_by = NULL, locked_at = NULL, lease_expires_at = NULL WHERE thread_id = ? AND status = 'processing' AND lease_expires_at IS NOT NULL AND lease_expires_at <= ?${filter.sql}`,
|
|
4766
|
+
[threadId, nowISO(), ...filter.params]
|
|
4767
|
+
));
|
|
4768
|
+
}
|
|
4725
4769
|
async getQueueSize(threadId, scope) {
|
|
4726
4770
|
const filter = scopeClause(scope);
|
|
4727
4771
|
const row = this.db.prepare(
|
|
@@ -4763,7 +4807,7 @@ var LocalThreadMessageQueueStore = class {
|
|
|
4763
4807
|
async resetProcessingToPending(threadId, scope) {
|
|
4764
4808
|
const filter = scopeClause(scope);
|
|
4765
4809
|
const result = this.db.prepare(
|
|
4766
|
-
`UPDATE lt_thread_message_queue SET status = 'pending' WHERE thread_id = ? AND status = 'processing'${filter.sql}`
|
|
4810
|
+
`UPDATE lt_thread_message_queue SET status = 'pending', locked_by = NULL, locked_at = NULL, lease_expires_at = NULL WHERE thread_id = ? AND status = 'processing'${filter.sql}`
|
|
4767
4811
|
).run(threadId, ...filter.params);
|
|
4768
4812
|
return result.changes;
|
|
4769
4813
|
}
|
|
@@ -5149,6 +5193,27 @@ var LocalScheduleStorage = class {
|
|
|
5149
5193
|
`UPDATE lt_scheduled_tasks SET ${setClauses.join(", ")} WHERE task_id = ?`
|
|
5150
5194
|
).run(...values);
|
|
5151
5195
|
}
|
|
5196
|
+
async claimScheduledRun(taskId) {
|
|
5197
|
+
const result = this.db.prepare(
|
|
5198
|
+
`UPDATE lt_scheduled_tasks SET status = 'running', updated_at = ? WHERE task_id = ? AND status = 'pending'`
|
|
5199
|
+
).run((/* @__PURE__ */ new Date()).toISOString(), taskId);
|
|
5200
|
+
return result.changes > 0;
|
|
5201
|
+
}
|
|
5202
|
+
/**
|
|
5203
|
+
* Requeues `running` tasks whose `updated_at` is older than `olderThanMs` back
|
|
5204
|
+
* to `pending` (recovery after a crash that left a claim unsettled).
|
|
5205
|
+
*
|
|
5206
|
+
* @param olderThanMs Age threshold in milliseconds.
|
|
5207
|
+
* @returns The number of tasks requeued.
|
|
5208
|
+
*/
|
|
5209
|
+
async requeueStaleRunning(olderThanMs) {
|
|
5210
|
+
const now = /* @__PURE__ */ new Date();
|
|
5211
|
+
const cutoff = new Date(now.getTime() - olderThanMs).toISOString();
|
|
5212
|
+
const result = this.db.prepare(
|
|
5213
|
+
`UPDATE lt_scheduled_tasks SET status = 'pending', updated_at = ? WHERE status = 'running' AND updated_at < ?`
|
|
5214
|
+
).run(now.toISOString(), cutoff);
|
|
5215
|
+
return result.changes;
|
|
5216
|
+
}
|
|
5152
5217
|
async delete(taskId) {
|
|
5153
5218
|
this.db.prepare(`DELETE FROM lt_scheduled_tasks WHERE task_id = ?`).run(taskId);
|
|
5154
5219
|
}
|