@creatornader/agent-bridge 0.6.1 → 0.6.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.
@@ -27,12 +27,18 @@ var EDGE_SQLITE_APPLICATION_ID = 1094862661;
27
27
  var SQLITE_DATABASE_SCHEMA_VERSION = 1;
28
28
  var SQLITE_METADATA_TABLE = "agent_bridge_metadata";
29
29
  var LOCAL_SQLITE_SCHEMA_CONTRACTS = Object.freeze([
30
+ Object.freeze({ id: "current-upgraded-write-coordinator", sha256: "d8504fc1092045ac5dc50aa64b0291a68ccb57af1cfebe5d1997b79f433b2eab" }),
31
+ Object.freeze({ id: "current-upgraded-project-column-write-coordinator", sha256: "37845f7e49b4c56dffe40295ffb31320d4b33c2acc2873e48514a1b8e36a490b" }),
32
+ Object.freeze({ id: "current-upgraded-delivery-policy-write-coordinator", sha256: "a0079dfe925a529b613425f779b5d978464a4c6b23e6cb7439fbf74f70da89bb" }),
33
+ Object.freeze({ id: "current-upgraded-delivery-events-write-coordinator", sha256: "a7de84c001b5f467a10c3aefdb6a92c86c7ce621cb5889cde90bc3c9f0b2f2ab" }),
30
34
  Object.freeze({ id: "current-created-schema", sha256: "e2f978c27ea5d151a4b4b3ec349166833441e42401fedfe60dc3ba20f3d713aa" }),
31
35
  Object.freeze({ id: "current-upgraded-project-column", sha256: "cb0975bbc1ccc7a2a66d8f4d76df619804449cf81119972337afad6cdab64451" }),
32
36
  Object.freeze({ id: "current-upgraded-delivery-policy", sha256: "d4fd8905ea73057b27662454c16c038834f5a1bf017db9cfc041792104aa2e6d" }),
33
37
  Object.freeze({ id: "current-upgraded-delivery-events", sha256: "35bdab0cbdf4ce619ba1c7d36032379ae4d9692bd7509e49bb90b409bbbcf632" })
34
38
  ]);
35
39
  var EDGE_SQLITE_SCHEMA_CONTRACTS = Object.freeze([
40
+ Object.freeze({ id: "current-upgraded-write-coordinator", sha256: "973d010ff81a726d6520dc482a4450697ef2295f58f42a45dee30b6174b7b9f9" }),
41
+ Object.freeze({ id: "current-upgraded-project-column-migration-gate-write-coordinator", sha256: "06c159fe10fade817b211197dea14f178e75f54c2f55b69d63f8fafb629e731c" }),
36
42
  Object.freeze({ id: "current-created-schema", sha256: "27f22b2f4024585c87e8d6f76f8999a8df92bb1f585d46594a7a1e852fd53c4c" }),
37
43
  Object.freeze({ id: "current-upgraded-project-column", sha256: "171ae11f520b4963f517d3102de64cb8a5f45c080078ff02e20aeb17891b585c" }),
38
44
  Object.freeze({ id: "current-upgraded-project-column-migration-gate", sha256: "790b4bfed373ff776ba6700065154f7a3cbbecd94f37ea09a654768ac2a8455c" }),
@@ -115,7 +121,8 @@ var localColumns = {
115
121
  "action",
116
122
  "created_at"
117
123
  ],
118
- bridge_presence: ["workspace", "agent", "instance", "runtime_type", "capabilities", "lease_expires_at", "last_seen_at"]
124
+ bridge_presence: ["workspace", "agent", "instance", "runtime_type", "capabilities", "lease_expires_at", "last_seen_at"],
125
+ bridge_write_gates: ["gate_key", "lease_token", "lease_expires_at"]
119
126
  };
120
127
  var localObjects = /* @__PURE__ */ new Map([
121
128
  ["agent_bridge_metadata", ["table", "agent_bridge_metadata"]],
@@ -124,6 +131,7 @@ var localObjects = /* @__PURE__ */ new Map([
124
131
  ["bridge_messages", ["table", "bridge_messages"]],
125
132
  ["bridge_presence", ["table", "bridge_presence"]],
126
133
  ["bridge_receipts", ["table", "bridge_receipts"]],
134
+ ["bridge_write_gates", ["table", "bridge_write_gates"]],
127
135
  ["bridge_deliveries_claim", ["index", "bridge_deliveries"]],
128
136
  ["bridge_deliveries_publisher", ["index", "bridge_deliveries"]],
129
137
  ["bridge_deliveries_terminal", ["index", "bridge_deliveries"]],
@@ -149,7 +157,8 @@ var edgeColumns = {
149
157
  edge_scopes: ["scope_key", "endpoint_hash", "workspace", "agent", "pull_cursor", "last_sync_at", "last_error", "last_outbound_sync_at", "last_inbound_sync_at", "last_attempt_at", "cache_contract"],
150
158
  edge_migration_gates: ["scope_key", "state", "operation_id", "lease_token", "lease_expires_at", "updated_at"],
151
159
  edge_outbox: ["position", "scope_key", "message_id", "idempotency_key", "payload_hash", "draft_json", "state", "attempts", "available_at", "lease_token", "lease_expires_at", "last_error", "blocked_at", "created_at"],
152
- edge_inbox: ["scope_key", "message_id", "remote_sequence", "sequence_key", "workspace", "project", "source", "type", "thread_id", "created_at", "expires_at", "message_json"]
160
+ edge_inbox: ["scope_key", "message_id", "remote_sequence", "sequence_key", "workspace", "project", "source", "type", "thread_id", "created_at", "expires_at", "message_json"],
161
+ edge_write_gates: ["gate_key", "lease_token", "lease_expires_at"]
153
162
  };
154
163
  var edgeObjects = /* @__PURE__ */ new Map([
155
164
  ["agent_bridge_metadata", ["table", "agent_bridge_metadata"]],
@@ -157,6 +166,7 @@ var edgeObjects = /* @__PURE__ */ new Map([
157
166
  ["edge_migration_gates", ["table", "edge_migration_gates"]],
158
167
  ["edge_outbox", ["table", "edge_outbox"]],
159
168
  ["edge_inbox", ["table", "edge_inbox"]],
169
+ ["edge_write_gates", ["table", "edge_write_gates"]],
160
170
  ["edge_inbox_created", ["index", "edge_inbox"]],
161
171
  ["edge_inbox_cursor", ["index", "edge_inbox"]],
162
172
  ["edge_inbox_project", ["index", "edge_inbox"]],
@@ -385,9 +395,6 @@ function openDatabase(path) {
385
395
  return new DatabaseSync(path);
386
396
  }
387
397
  var schema = `
388
- PRAGMA journal_mode = WAL;
389
- PRAGMA synchronous = FULL;
390
- PRAGMA foreign_keys = ON;
391
398
  CREATE TABLE IF NOT EXISTS bridge_messages (
392
399
  sequence INTEGER PRIMARY KEY AUTOINCREMENT, id TEXT NOT NULL UNIQUE, workspace TEXT NOT NULL, project TEXT, source TEXT NOT NULL,
393
400
  type TEXT NOT NULL, content TEXT NOT NULL, content_type TEXT NOT NULL, data TEXT, targets TEXT NOT NULL DEFAULT '[]',
@@ -452,6 +459,7 @@ CREATE INDEX IF NOT EXISTS bridge_presence_active ON bridge_presence(workspace,
452
459
  CREATE TRIGGER IF NOT EXISTS bridge_messages_no_update BEFORE UPDATE ON bridge_messages BEGIN SELECT RAISE(ABORT, 'bridge messages are immutable'); END;
453
460
  CREATE TRIGGER IF NOT EXISTS bridge_messages_no_delete BEFORE DELETE ON bridge_messages BEGIN SELECT RAISE(ABORT, 'bridge messages are immutable'); END;
454
461
  `;
462
+ var writeGateSchema = "CREATE TABLE IF NOT EXISTS bridge_write_gates (gate_key TEXT PRIMARY KEY, lease_token TEXT, lease_expires_at TEXT)";
455
463
  function stringify(value) {
456
464
  return value === void 0 ? null : JSON.stringify(value);
457
465
  }
@@ -497,6 +505,70 @@ var SQLiteBridgeStore = class {
497
505
  throw error;
498
506
  }
499
507
  }
508
+ async write(work) {
509
+ if (this.databasePath === ":memory:") return work();
510
+ const token = randomUUID();
511
+ const waitMs = process.platform === "win32" ? 6e4 : SQLITE_INITIALIZATION_BUSY_TIMEOUT_MS;
512
+ const deadline = Date.now() + waitMs;
513
+ let pause = 2;
514
+ while (true) {
515
+ const now = /* @__PURE__ */ new Date();
516
+ const claimed = await retrySqliteBusy(() => {
517
+ this.db.exec("BEGIN IMMEDIATE");
518
+ try {
519
+ this.db.prepare("INSERT INTO bridge_write_gates (gate_key,lease_token,lease_expires_at) VALUES ('local',NULL,NULL) ON CONFLICT(gate_key) DO NOTHING").run();
520
+ const result2 = this.db.prepare("UPDATE bridge_write_gates SET lease_token=?,lease_expires_at=? WHERE gate_key='local' AND (lease_token IS NULL OR lease_expires_at<=?)").run(token, new Date(now.getTime() + 3e4).toISOString(), now.toISOString());
521
+ this.db.exec("COMMIT");
522
+ return result2.changes === 1;
523
+ } catch (error) {
524
+ this.db.exec("ROLLBACK");
525
+ throw error;
526
+ }
527
+ }, Math.max(1, deadline - Date.now()));
528
+ if (claimed) break;
529
+ if (Date.now() >= deadline) throw new Error("local write coordinator remained busy");
530
+ await new Promise((resolve) => setTimeout(resolve, pause));
531
+ pause = Math.min(Math.ceil(pause * 1.7), 25);
532
+ }
533
+ let completed = false;
534
+ let result;
535
+ try {
536
+ result = await work();
537
+ completed = true;
538
+ } finally {
539
+ try {
540
+ await retrySqliteBusy(() => {
541
+ this.db.exec("BEGIN IMMEDIATE");
542
+ try {
543
+ this.db.prepare("UPDATE bridge_write_gates SET lease_token=NULL,lease_expires_at=NULL WHERE gate_key='local' AND lease_token=?").run(token);
544
+ this.db.exec("COMMIT");
545
+ } catch (error) {
546
+ this.db.exec("ROLLBACK");
547
+ throw error;
548
+ }
549
+ }, waitMs);
550
+ } catch (error) {
551
+ if (!completed) throw error;
552
+ }
553
+ }
554
+ return result;
555
+ }
556
+ async bootstrapWriteGate(timeoutMs) {
557
+ await retrySqliteBusy(() => this.db.exec("PRAGMA journal_mode = WAL"), timeoutMs);
558
+ const exists = () => Boolean(this.db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name='bridge_write_gates'").get());
559
+ if (exists()) return;
560
+ await retrySqliteBusy(() => {
561
+ if (exists()) return;
562
+ this.db.exec("BEGIN IMMEDIATE");
563
+ try {
564
+ if (!exists()) this.db.exec(writeGateSchema);
565
+ this.db.exec("COMMIT");
566
+ } catch (error) {
567
+ this.db.exec("ROLLBACK");
568
+ throw error;
569
+ }
570
+ }, timeoutMs);
571
+ }
500
572
  restrictFiles(includeSidecars = true) {
501
573
  if (this.databasePath === ":memory:") return;
502
574
  const paths = includeSidecars ? [this.databasePath, `${this.databasePath}-wal`, `${this.databasePath}-shm`] : [this.databasePath];
@@ -526,33 +598,36 @@ var SQLiteBridgeStore = class {
526
598
  if (encoded < 3051003) throw new Error("SQLite 3.51.3 or newer is required");
527
599
  assertLocalUpgradeCandidate(this.db);
528
600
  const initializationTimeoutMs = Math.max(this.busyTimeoutMs, SQLITE_INITIALIZATION_BUSY_TIMEOUT_MS);
529
- await retrySqliteBusy(() => this.db.exec(schema), initializationTimeoutMs);
530
- await retrySqliteBusy(() => this.db.exec("BEGIN IMMEDIATE"), initializationTimeoutMs);
531
- try {
532
- const columns = this.db.prepare("PRAGMA table_info(bridge_messages)").all();
533
- if (!columns.some((column) => column.name === "project")) {
534
- this.db.exec("ALTER TABLE bridge_messages ADD COLUMN project TEXT");
535
- }
536
- const addedDeliveryPolicy = !columns.some((column) => column.name === "delivery_policy");
537
- if (addedDeliveryPolicy) {
538
- this.db.exec("DROP TRIGGER IF EXISTS bridge_messages_no_update");
539
- this.db.exec(`ALTER TABLE bridge_messages ADD COLUMN delivery_policy TEXT NOT NULL DEFAULT '{"mode":"leased","maxAttempts":5,"retryBaseDelayMs":1000,"retryMaxDelayMs":60000,"retryJitterRatio":0.2}'`);
540
- this.db.exec("CREATE TRIGGER bridge_messages_no_update BEFORE UPDATE ON bridge_messages BEGIN SELECT RAISE(ABORT, 'bridge messages are immutable'); END");
541
- }
542
- const legacyPolicy = addedDeliveryPolicy || Boolean(this.db.prepare(`SELECT 1 FROM bridge_messages
601
+ await this.bootstrapWriteGate(initializationTimeoutMs);
602
+ this.db.exec("PRAGMA synchronous = FULL; PRAGMA foreign_keys = ON");
603
+ await this.write(async () => {
604
+ await retrySqliteBusy(() => this.db.exec("BEGIN IMMEDIATE"), initializationTimeoutMs);
605
+ try {
606
+ this.db.exec(schema);
607
+ const columns = this.db.prepare("PRAGMA table_info(bridge_messages)").all();
608
+ if (!columns.some((column) => column.name === "project")) {
609
+ this.db.exec("ALTER TABLE bridge_messages ADD COLUMN project TEXT");
610
+ }
611
+ const addedDeliveryPolicy = !columns.some((column) => column.name === "delivery_policy");
612
+ if (addedDeliveryPolicy) {
613
+ this.db.exec("DROP TRIGGER IF EXISTS bridge_messages_no_update");
614
+ this.db.exec(`ALTER TABLE bridge_messages ADD COLUMN delivery_policy TEXT NOT NULL DEFAULT '{"mode":"leased","maxAttempts":5,"retryBaseDelayMs":1000,"retryMaxDelayMs":60000,"retryJitterRatio":0.2}'`);
615
+ this.db.exec("CREATE TRIGGER bridge_messages_no_update BEFORE UPDATE ON bridge_messages BEGIN SELECT RAISE(ABORT, 'bridge messages are immutable'); END");
616
+ }
617
+ const legacyPolicy = addedDeliveryPolicy || Boolean(this.db.prepare(`SELECT 1 FROM bridge_messages
543
618
  WHERE json_type(delivery_policy,'$.publisherOwned') IS NOT NULL
544
619
  OR json_type(delivery_policy,'$.baseDelayMs') IS NOT NULL
545
620
  OR json_type(delivery_policy,'$.maxDelayMs') IS NOT NULL
546
621
  OR json_type(delivery_policy,'$.jitterRatio') IS NOT NULL
547
622
  LIMIT 1`).get());
548
- if (legacyPolicy) {
549
- this.db.exec("DROP TRIGGER IF EXISTS bridge_messages_no_update");
550
- if (addedDeliveryPolicy) {
551
- this.db.exec(`UPDATE bridge_messages SET delivery_policy=CASE
623
+ if (legacyPolicy) {
624
+ this.db.exec("DROP TRIGGER IF EXISTS bridge_messages_no_update");
625
+ if (addedDeliveryPolicy) {
626
+ this.db.exec(`UPDATE bridge_messages SET delivery_policy=CASE
552
627
  WHEN json_array_length(targets)=0 THEN '{"mode":"mailbox"}'
553
628
  ELSE '{"mode":"leased","maxAttempts":5,"retryBaseDelayMs":1000,"retryMaxDelayMs":60000,"retryJitterRatio":0.2}' END`);
554
- } else {
555
- this.db.exec(`UPDATE bridge_messages SET delivery_policy=CASE
629
+ } else {
630
+ this.db.exec(`UPDATE bridge_messages SET delivery_policy=CASE
556
631
  WHEN json_extract(delivery_policy,'$.mode')='mailbox' THEN
557
632
  json_remove(delivery_policy,'$.publisherOwned','$.maxAttempts','$.baseDelayMs','$.maxDelayMs','$.jitterRatio','$.retryBaseDelayMs','$.retryMaxDelayMs','$.retryJitterRatio','$.notBefore')
558
633
  ELSE json_set(
@@ -567,21 +642,21 @@ var SQLiteBridgeStore = class {
567
642
  OR json_type(delivery_policy,'$.baseDelayMs') IS NOT NULL
568
643
  OR json_type(delivery_policy,'$.maxDelayMs') IS NOT NULL
569
644
  OR json_type(delivery_policy,'$.jitterRatio') IS NOT NULL`);
645
+ }
646
+ this.db.exec("CREATE TRIGGER bridge_messages_no_update BEFORE UPDATE ON bridge_messages BEGIN SELECT RAISE(ABORT, 'bridge messages are immutable'); END");
570
647
  }
571
- this.db.exec("CREATE TRIGGER bridge_messages_no_update BEFORE UPDATE ON bridge_messages BEGIN SELECT RAISE(ABORT, 'bridge messages are immutable'); END");
572
- }
573
- const deliveryColumns = this.db.prepare("PRAGMA table_info(bridge_deliveries)").all();
574
- if (!deliveryColumns.some((column) => column.name === "last_action")) {
575
- this.db.exec("DROP TRIGGER IF EXISTS bridge_delivery_events_insert; DROP TRIGGER IF EXISTS bridge_delivery_events_update; DROP INDEX IF EXISTS bridge_deliveries_claim");
576
- const eventColumns = this.db.prepare("PRAGMA table_info(bridge_delivery_events)").all();
577
- this.db.exec(`CREATE TABLE bridge_delivery_events_v10 (
648
+ const deliveryColumns = this.db.prepare("PRAGMA table_info(bridge_deliveries)").all();
649
+ if (!deliveryColumns.some((column) => column.name === "last_action")) {
650
+ this.db.exec("DROP TRIGGER IF EXISTS bridge_delivery_events_insert; DROP TRIGGER IF EXISTS bridge_delivery_events_update; DROP INDEX IF EXISTS bridge_deliveries_claim");
651
+ const eventColumns = this.db.prepare("PRAGMA table_info(bridge_delivery_events)").all();
652
+ this.db.exec(`CREATE TABLE bridge_delivery_events_v10 (
578
653
  sequence INTEGER PRIMARY KEY AUTOINCREMENT, delivery_id TEXT NOT NULL,
579
654
  message_id TEXT NOT NULL, workspace TEXT NOT NULL, recipient TEXT NOT NULL,
580
655
  from_state TEXT, to_state TEXT NOT NULL, attempt INTEGER NOT NULL,
581
656
  cycle_attempt INTEGER NOT NULL, requeue_count INTEGER NOT NULL,
582
657
  lease_owner TEXT, error TEXT, actor TEXT NOT NULL, action TEXT NOT NULL, created_at TEXT NOT NULL)`);
583
- if (eventColumns.length) {
584
- this.db.exec(`INSERT INTO bridge_delivery_events_v10
658
+ if (eventColumns.length) {
659
+ this.db.exec(`INSERT INTO bridge_delivery_events_v10
585
660
  (sequence,delivery_id,message_id,workspace,recipient,from_state,to_state,attempt,cycle_attempt,requeue_count,lease_owner,error,actor,action,created_at)
586
661
  SELECT event.sequence,event.delivery_id,event.message_id,event.workspace,event.recipient,
587
662
  event.from_state,event.to_state,event.attempt,event.attempt,0,event.lease_owner,event.error,
@@ -609,9 +684,9 @@ var SQLiteBridgeStore = class {
609
684
  ELSE 'created' END,event.created_at
610
685
  FROM bridge_delivery_events event JOIN bridge_messages message
611
686
  ON message.workspace=event.workspace AND message.id=event.message_id`);
612
- this.db.exec("DROP TABLE bridge_delivery_events");
613
- }
614
- this.db.exec(`CREATE TABLE bridge_deliveries_v10 (
687
+ this.db.exec("DROP TABLE bridge_delivery_events");
688
+ }
689
+ this.db.exec(`CREATE TABLE bridge_deliveries_v10 (
615
690
  id TEXT PRIMARY KEY, message_id TEXT NOT NULL, workspace TEXT NOT NULL, recipient TEXT NOT NULL,
616
691
  state TEXT NOT NULL, attempt INTEGER NOT NULL DEFAULT 0, cycle_attempt INTEGER NOT NULL DEFAULT 0,
617
692
  requeue_count INTEGER NOT NULL DEFAULT 0, created_at TEXT NOT NULL, priority_rank INTEGER NOT NULL,
@@ -622,7 +697,7 @@ var SQLiteBridgeStore = class {
622
697
  CHECK(attempt>=0 AND cycle_attempt>=0 AND cycle_attempt<=attempt AND requeue_count>=0),
623
698
  CHECK(priority_rank BETWEEN 0 AND 2),
624
699
  CHECK((state='claimed' AND lease_token IS NOT NULL AND lease_expires_at IS NOT NULL) OR (state<>'claimed' AND lease_token IS NULL AND lease_owner IS NULL AND lease_expires_at IS NULL)))`);
625
- this.db.exec(`INSERT INTO bridge_deliveries_v10
700
+ this.db.exec(`INSERT INTO bridge_deliveries_v10
626
701
  (id,message_id,workspace,recipient,state,attempt,cycle_attempt,requeue_count,created_at,priority_rank,available_at,lease_token,lease_owner,lease_expires_at,last_error,last_actor,last_action)
627
702
  SELECT delivery.id,delivery.message_id,delivery.workspace,delivery.recipient,delivery.state,delivery.attempt,delivery.attempt,0,
628
703
  message.created_at,CASE message.priority WHEN 'urgent' THEN 0 WHEN 'high' THEN 1 ELSE 2 END,
@@ -649,13 +724,13 @@ var SQLiteBridgeStore = class {
649
724
  ELSE 'created' END
650
725
  FROM bridge_deliveries delivery JOIN bridge_messages message
651
726
  ON message.workspace=delivery.workspace AND message.id=delivery.message_id`);
652
- this.db.exec("DROP TABLE bridge_deliveries; ALTER TABLE bridge_deliveries_v10 RENAME TO bridge_deliveries");
653
- this.db.exec("ALTER TABLE bridge_delivery_events_v10 RENAME TO bridge_delivery_events");
654
- }
655
- this.db.exec("CREATE INDEX IF NOT EXISTS bridge_deliveries_claim ON bridge_deliveries(workspace,recipient,priority_rank,available_at,created_at,id) WHERE state IN ('pending','retrying','claimed'); CREATE INDEX IF NOT EXISTS bridge_deliveries_publisher ON bridge_deliveries(workspace,message_id,created_at,id); CREATE INDEX IF NOT EXISTS bridge_deliveries_terminal ON bridge_deliveries(workspace,recipient,state,created_at,id) WHERE state IN ('dead','cancelled'); CREATE INDEX IF NOT EXISTS bridge_delivery_events_lookup ON bridge_delivery_events(workspace,delivery_id,sequence)");
656
- this.db.exec(`CREATE TRIGGER IF NOT EXISTS bridge_delivery_events_insert AFTER INSERT ON bridge_deliveries BEGIN INSERT INTO bridge_delivery_events (delivery_id,message_id,workspace,recipient,from_state,to_state,attempt,cycle_attempt,requeue_count,lease_owner,error,actor,action,created_at) VALUES (NEW.id,NEW.message_id,NEW.workspace,NEW.recipient,NULL,NEW.state,NEW.attempt,NEW.cycle_attempt,NEW.requeue_count,NEW.lease_owner,NEW.last_error,coalesce(NEW.last_actor,NEW.recipient),NEW.last_action,strftime('%Y-%m-%dT%H:%M:%fZ','now')); END;
727
+ this.db.exec("DROP TABLE bridge_deliveries; ALTER TABLE bridge_deliveries_v10 RENAME TO bridge_deliveries");
728
+ this.db.exec("ALTER TABLE bridge_delivery_events_v10 RENAME TO bridge_delivery_events");
729
+ }
730
+ this.db.exec("CREATE INDEX IF NOT EXISTS bridge_deliveries_claim ON bridge_deliveries(workspace,recipient,priority_rank,available_at,created_at,id) WHERE state IN ('pending','retrying','claimed'); CREATE INDEX IF NOT EXISTS bridge_deliveries_publisher ON bridge_deliveries(workspace,message_id,created_at,id); CREATE INDEX IF NOT EXISTS bridge_deliveries_terminal ON bridge_deliveries(workspace,recipient,state,created_at,id) WHERE state IN ('dead','cancelled'); CREATE INDEX IF NOT EXISTS bridge_delivery_events_lookup ON bridge_delivery_events(workspace,delivery_id,sequence)");
731
+ this.db.exec(`CREATE TRIGGER IF NOT EXISTS bridge_delivery_events_insert AFTER INSERT ON bridge_deliveries BEGIN INSERT INTO bridge_delivery_events (delivery_id,message_id,workspace,recipient,from_state,to_state,attempt,cycle_attempt,requeue_count,lease_owner,error,actor,action,created_at) VALUES (NEW.id,NEW.message_id,NEW.workspace,NEW.recipient,NULL,NEW.state,NEW.attempt,NEW.cycle_attempt,NEW.requeue_count,NEW.lease_owner,NEW.last_error,coalesce(NEW.last_actor,NEW.recipient),NEW.last_action,strftime('%Y-%m-%dT%H:%M:%fZ','now')); END;
657
732
  CREATE TRIGGER IF NOT EXISTS bridge_delivery_events_update AFTER UPDATE ON bridge_deliveries WHEN OLD.state IS NOT NEW.state OR OLD.attempt IS NOT NEW.attempt OR OLD.requeue_count IS NOT NEW.requeue_count OR OLD.last_action IS NOT NEW.last_action BEGIN INSERT INTO bridge_delivery_events (delivery_id,message_id,workspace,recipient,from_state,to_state,attempt,cycle_attempt,requeue_count,lease_owner,error,actor,action,created_at) VALUES (NEW.id,NEW.message_id,NEW.workspace,NEW.recipient,OLD.state,NEW.state,NEW.attempt,NEW.cycle_attempt,NEW.requeue_count,NEW.lease_owner,NEW.last_error,coalesce(NEW.last_actor,NEW.recipient),NEW.last_action,strftime('%Y-%m-%dT%H:%M:%fZ','now')); END`);
658
- this.db.exec(`CREATE TRIGGER IF NOT EXISTS bridge_messages_policy_insert BEFORE INSERT ON bridge_messages
733
+ this.db.exec(`CREATE TRIGGER IF NOT EXISTS bridge_messages_policy_insert BEFORE INSERT ON bridge_messages
659
734
  WHEN NOT (
660
735
  json_valid(NEW.delivery_policy) AND json_type(NEW.delivery_policy)='object'
661
736
  AND NOT EXISTS (SELECT 1 FROM json_each(NEW.delivery_policy) WHERE key NOT IN ('mode','maxAttempts','retryBaseDelayMs','retryMaxDelayMs','retryJitterRatio','notBefore'))
@@ -676,17 +751,18 @@ var SQLiteBridgeStore = class {
676
751
  )
677
752
  )
678
753
  ) BEGIN SELECT RAISE(ABORT,'invalid delivery policy'); END`);
679
- this.db.exec(`CREATE TRIGGER IF NOT EXISTS bridge_messages_domain_insert BEFORE INSERT ON bridge_messages
754
+ this.db.exec(`CREATE TRIGGER IF NOT EXISTS bridge_messages_domain_insert BEFORE INSERT ON bridge_messages
680
755
  WHEN NOT (json_valid(NEW.targets) AND json_type(NEW.targets)='array' AND NEW.priority IN ('info','high','urgent'))
681
756
  BEGIN SELECT RAISE(ABORT,'invalid message domain'); END`);
682
- this.db.exec("CREATE INDEX IF NOT EXISTS bridge_messages_project ON bridge_messages(workspace, project, sequence)");
683
- installLocalAuthorityMarkers(this.db);
684
- this.db.exec("COMMIT");
685
- } catch (error) {
686
- this.db.exec("ROLLBACK");
687
- throw error;
688
- }
689
- this.restrictFiles();
757
+ this.db.exec("CREATE INDEX IF NOT EXISTS bridge_messages_project ON bridge_messages(workspace, project, sequence)");
758
+ installLocalAuthorityMarkers(this.db);
759
+ this.db.exec("COMMIT");
760
+ } catch (error) {
761
+ this.db.exec("ROLLBACK");
762
+ throw error;
763
+ }
764
+ this.restrictFiles();
765
+ });
690
766
  }
691
767
  async ready() {
692
768
  await this.initialize();
@@ -695,32 +771,34 @@ var SQLiteBridgeStore = class {
695
771
  await this.ready();
696
772
  const createdAt = (/* @__PURE__ */ new Date()).toISOString();
697
773
  const storedPolicy = input.deliveryPolicy ?? (input.targets.length ? { mode: "leased", maxAttempts: 5, retryBaseDelayMs: 1e3, retryMaxDelayMs: 6e4, retryJitterRatio: 0.2 } : { mode: "mailbox" });
698
- this.db.exec("BEGIN IMMEDIATE");
699
- try {
700
- if (input.idempotencyKey) {
701
- const existing = this.db.prepare("SELECT * FROM bridge_messages WHERE workspace = ? AND source = ? AND idempotency_key = ?").get(input.workspace, input.source, input.idempotencyKey);
702
- if (existing) {
703
- const replay = message(existing);
704
- assertIdempotentReplay(replay, input);
705
- this.db.exec("COMMIT");
706
- return { message: replay, created: false };
774
+ return this.write(() => {
775
+ this.db.exec("BEGIN IMMEDIATE");
776
+ try {
777
+ if (input.idempotencyKey) {
778
+ const existing = this.db.prepare("SELECT * FROM bridge_messages WHERE workspace = ? AND source = ? AND idempotency_key = ?").get(input.workspace, input.source, input.idempotencyKey);
779
+ if (existing) {
780
+ const replay = message(existing);
781
+ assertIdempotentReplay(replay, input);
782
+ this.db.exec("COMMIT");
783
+ return { message: replay, created: false };
784
+ }
707
785
  }
708
- }
709
- this.db.prepare(`INSERT INTO bridge_messages (id,workspace,project,source,type,content,content_type,data,targets,thread_id,reply_to_id,correlation_id,causation_id,priority,expires_at,idempotency_key,atrib_receipt_id,informed_by,metadata,delivery_policy,created_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`).run(input.id, input.workspace, input.project ?? null, input.source, input.type, input.content, input.contentType, stringify(input.data), JSON.stringify(input.targets), input.threadId ?? null, input.replyToId ?? null, input.correlationId ?? null, input.causationId ?? null, input.priority, input.expiresAt ?? null, input.idempotencyKey ?? null, input.atribReceiptId ?? null, stringify(input.informedBy), stringify(input.metadata), JSON.stringify(storedPolicy), createdAt);
710
- if (storedPolicy.mode === "leased") {
711
- const availableAt = storedPolicy.notBefore && storedPolicy.notBefore > createdAt ? storedPolicy.notBefore : createdAt;
712
- const priorityRank = input.priority === "urgent" ? 0 : input.priority === "high" ? 1 : 2;
713
- for (const recipient of input.targets) {
714
- this.db.prepare("INSERT INTO bridge_deliveries (id,message_id,workspace,recipient,state,created_at,priority_rank,available_at,last_actor,last_action) VALUES (?,?,?,?,?,?,?,?,?,?)").run(randomUUID(), input.id, input.workspace, recipient, "pending", createdAt, priorityRank, availableAt, input.source, "created");
786
+ this.db.prepare(`INSERT INTO bridge_messages (id,workspace,project,source,type,content,content_type,data,targets,thread_id,reply_to_id,correlation_id,causation_id,priority,expires_at,idempotency_key,atrib_receipt_id,informed_by,metadata,delivery_policy,created_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`).run(input.id, input.workspace, input.project ?? null, input.source, input.type, input.content, input.contentType, stringify(input.data), JSON.stringify(input.targets), input.threadId ?? null, input.replyToId ?? null, input.correlationId ?? null, input.causationId ?? null, input.priority, input.expiresAt ?? null, input.idempotencyKey ?? null, input.atribReceiptId ?? null, stringify(input.informedBy), stringify(input.metadata), JSON.stringify(storedPolicy), createdAt);
787
+ if (storedPolicy.mode === "leased") {
788
+ const availableAt = storedPolicy.notBefore && storedPolicy.notBefore > createdAt ? storedPolicy.notBefore : createdAt;
789
+ const priorityRank = input.priority === "urgent" ? 0 : input.priority === "high" ? 1 : 2;
790
+ for (const recipient of input.targets) {
791
+ this.db.prepare("INSERT INTO bridge_deliveries (id,message_id,workspace,recipient,state,created_at,priority_rank,available_at,last_actor,last_action) VALUES (?,?,?,?,?,?,?,?,?,?)").run(randomUUID(), input.id, input.workspace, recipient, "pending", createdAt, priorityRank, availableAt, input.source, "created");
792
+ }
715
793
  }
794
+ const row = this.db.prepare("SELECT * FROM bridge_messages WHERE id = ?").get(input.id);
795
+ this.db.exec("COMMIT");
796
+ return { message: message(row), created: true };
797
+ } catch (error) {
798
+ this.db.exec("ROLLBACK");
799
+ throw error;
716
800
  }
717
- const row = this.db.prepare("SELECT * FROM bridge_messages WHERE id = ?").get(input.id);
718
- this.db.exec("COMMIT");
719
- return { message: message(row), created: true };
720
- } catch (error) {
721
- this.db.exec("ROLLBACK");
722
- throw error;
723
- }
801
+ });
724
802
  }
725
803
  async listMessages(principal, query = {}) {
726
804
  await this.ready();
@@ -777,17 +855,19 @@ var SQLiteBridgeStore = class {
777
855
  }
778
856
  async recordReceipt(principal, messageIds, readAt = /* @__PURE__ */ new Date()) {
779
857
  await this.ready();
780
- const stmt = this.db.prepare("INSERT OR IGNORE INTO bridge_receipts (workspace,message_id,principal,read_at) SELECT workspace,id,?,? FROM bridge_messages WHERE workspace=? AND id=? AND (targets='[]' OR EXISTS (SELECT 1 FROM json_each(targets) WHERE value=?))");
781
- let changed = 0;
782
- this.db.exec("BEGIN IMMEDIATE");
783
- try {
784
- for (const id of messageIds) changed += Number(stmt.run(principal.agent, readAt.toISOString(), principal.workspace, id, principal.agent).changes);
785
- this.db.exec("COMMIT");
786
- return changed;
787
- } catch (e) {
788
- this.db.exec("ROLLBACK");
789
- throw e;
790
- }
858
+ return this.write(() => {
859
+ const stmt = this.db.prepare("INSERT OR IGNORE INTO bridge_receipts (workspace,message_id,principal,read_at) SELECT workspace,id,?,? FROM bridge_messages WHERE workspace=? AND id=? AND (targets='[]' OR EXISTS (SELECT 1 FROM json_each(targets) WHERE value=?))");
860
+ let changed = 0;
861
+ this.db.exec("BEGIN IMMEDIATE");
862
+ try {
863
+ for (const id of messageIds) changed += Number(stmt.run(principal.agent, readAt.toISOString(), principal.workspace, id, principal.agent).changes);
864
+ this.db.exec("COMMIT");
865
+ return changed;
866
+ } catch (e) {
867
+ this.db.exec("ROLLBACK");
868
+ throw e;
869
+ }
870
+ });
791
871
  }
792
872
  async claimDelivery(principal, options) {
793
873
  await this.ready();
@@ -795,63 +875,69 @@ var SQLiteBridgeStore = class {
795
875
  const nowText = now.toISOString();
796
876
  const expires = new Date(now.getTime() + options.leaseMs).toISOString();
797
877
  const owner = principal.instance ?? principal.agent;
798
- this.db.exec("BEGIN IMMEDIATE");
799
- try {
800
- this.db.prepare("UPDATE bridge_deliveries SET state='dead',last_error='message expired',lease_token=NULL,lease_owner=NULL,lease_expires_at=NULL,last_actor='agent-bridge',last_action='message_expired' WHERE workspace=? AND recipient=? AND (? IS NULL OR message_id=?) AND state IN ('pending','retrying','claimed') AND EXISTS (SELECT 1 FROM bridge_messages message WHERE message.workspace=bridge_deliveries.workspace AND message.id=bridge_deliveries.message_id AND message.expires_at IS NOT NULL AND message.expires_at<=?)").run(principal.workspace, principal.agent, options.messageId ?? null, options.messageId ?? null, nowText);
801
- this.db.prepare("UPDATE bridge_deliveries SET state='dead',last_error='maximum attempts reached',lease_token=NULL,lease_owner=NULL,lease_expires_at=NULL,last_actor='agent-bridge',last_action='attempts_exhausted' WHERE workspace=? AND recipient=? AND (? IS NULL OR message_id=?) AND cycle_attempt>=(SELECT json_extract(delivery_policy,'$.maxAttempts') FROM bridge_messages WHERE workspace=bridge_deliveries.workspace AND id=bridge_deliveries.message_id) AND (state IN ('pending','retrying') OR (state='claimed' AND lease_expires_at<=?))").run(principal.workspace, principal.agent, options.messageId ?? null, options.messageId ?? null, nowText);
802
- this.db.prepare("UPDATE bridge_deliveries SET state='retrying',available_at=?,last_error='lease expired',lease_token=NULL,lease_owner=NULL,lease_expires_at=NULL,last_actor='agent-bridge',last_action='lease_expired' WHERE workspace=? AND recipient=? AND (? IS NULL OR message_id=?) AND state='claimed' AND lease_expires_at<=? AND cycle_attempt<(SELECT json_extract(delivery_policy,'$.maxAttempts') FROM bridge_messages WHERE workspace=bridge_deliveries.workspace AND id=bridge_deliveries.message_id)").run(nowText, principal.workspace, principal.agent, options.messageId ?? null, options.messageId ?? null, nowText);
803
- const candidate = this.db.prepare("SELECT delivery.id FROM bridge_deliveries delivery JOIN bridge_messages message ON message.workspace=delivery.workspace AND message.id=delivery.message_id WHERE delivery.workspace=? AND delivery.recipient=? AND (? IS NULL OR delivery.message_id=?) AND delivery.state IN ('pending','retrying') AND delivery.available_at<=? AND delivery.cycle_attempt<json_extract(message.delivery_policy,'$.maxAttempts') AND (message.expires_at IS NULL OR message.expires_at>?) ORDER BY delivery.priority_rank,delivery.available_at,delivery.created_at,delivery.id LIMIT 1").get(principal.workspace, principal.agent, options.messageId ?? null, options.messageId ?? null, nowText, nowText);
804
- if (!candidate) {
878
+ return this.write(() => {
879
+ this.db.exec("BEGIN IMMEDIATE");
880
+ try {
881
+ this.db.prepare("UPDATE bridge_deliveries SET state='dead',last_error='message expired',lease_token=NULL,lease_owner=NULL,lease_expires_at=NULL,last_actor='agent-bridge',last_action='message_expired' WHERE workspace=? AND recipient=? AND (? IS NULL OR message_id=?) AND state IN ('pending','retrying','claimed') AND EXISTS (SELECT 1 FROM bridge_messages message WHERE message.workspace=bridge_deliveries.workspace AND message.id=bridge_deliveries.message_id AND message.expires_at IS NOT NULL AND message.expires_at<=?)").run(principal.workspace, principal.agent, options.messageId ?? null, options.messageId ?? null, nowText);
882
+ this.db.prepare("UPDATE bridge_deliveries SET state='dead',last_error='maximum attempts reached',lease_token=NULL,lease_owner=NULL,lease_expires_at=NULL,last_actor='agent-bridge',last_action='attempts_exhausted' WHERE workspace=? AND recipient=? AND (? IS NULL OR message_id=?) AND cycle_attempt>=(SELECT json_extract(delivery_policy,'$.maxAttempts') FROM bridge_messages WHERE workspace=bridge_deliveries.workspace AND id=bridge_deliveries.message_id) AND (state IN ('pending','retrying') OR (state='claimed' AND lease_expires_at<=?))").run(principal.workspace, principal.agent, options.messageId ?? null, options.messageId ?? null, nowText);
883
+ this.db.prepare("UPDATE bridge_deliveries SET state='retrying',available_at=?,last_error='lease expired',lease_token=NULL,lease_owner=NULL,lease_expires_at=NULL,last_actor='agent-bridge',last_action='lease_expired' WHERE workspace=? AND recipient=? AND (? IS NULL OR message_id=?) AND state='claimed' AND lease_expires_at<=? AND cycle_attempt<(SELECT json_extract(delivery_policy,'$.maxAttempts') FROM bridge_messages WHERE workspace=bridge_deliveries.workspace AND id=bridge_deliveries.message_id)").run(nowText, principal.workspace, principal.agent, options.messageId ?? null, options.messageId ?? null, nowText);
884
+ const candidate = this.db.prepare("SELECT delivery.id FROM bridge_deliveries delivery JOIN bridge_messages message ON message.workspace=delivery.workspace AND message.id=delivery.message_id WHERE delivery.workspace=? AND delivery.recipient=? AND (? IS NULL OR delivery.message_id=?) AND delivery.state IN ('pending','retrying') AND delivery.available_at<=? AND delivery.cycle_attempt<json_extract(message.delivery_policy,'$.maxAttempts') AND (message.expires_at IS NULL OR message.expires_at>?) ORDER BY delivery.priority_rank,delivery.available_at,delivery.created_at,delivery.id LIMIT 1").get(principal.workspace, principal.agent, options.messageId ?? null, options.messageId ?? null, nowText, nowText);
885
+ if (!candidate) {
886
+ this.db.exec("COMMIT");
887
+ return null;
888
+ }
889
+ const candidateId = String(candidate.id);
890
+ const token = randomUUID();
891
+ this.db.prepare("UPDATE bridge_deliveries SET state='claimed',attempt=attempt+1,cycle_attempt=cycle_attempt+1,lease_token=?,lease_owner=?,lease_expires_at=?,last_error=NULL,last_actor=?,last_action='claim' WHERE id=?").run(token, owner, expires, principal.agent, candidateId);
892
+ const claimed = this.db.prepare("SELECT * FROM bridge_deliveries WHERE id=?").get(candidateId);
805
893
  this.db.exec("COMMIT");
806
- return null;
894
+ return delivery(claimed);
895
+ } catch (e) {
896
+ this.db.exec("ROLLBACK");
897
+ throw e;
807
898
  }
808
- const candidateId = String(candidate.id);
809
- const token = randomUUID();
810
- this.db.prepare("UPDATE bridge_deliveries SET state='claimed',attempt=attempt+1,cycle_attempt=cycle_attempt+1,lease_token=?,lease_owner=?,lease_expires_at=?,last_error=NULL,last_actor=?,last_action='claim' WHERE id=?").run(token, owner, expires, principal.agent, candidateId);
811
- const claimed = this.db.prepare("SELECT * FROM bridge_deliveries WHERE id=?").get(candidateId);
812
- this.db.exec("COMMIT");
813
- return delivery(claimed);
814
- } catch (e) {
815
- this.db.exec("ROLLBACK");
816
- throw e;
817
- }
899
+ });
818
900
  }
819
901
  async renewDelivery(principal, id, token, leaseMs) {
820
902
  await this.ready();
821
- const now = /* @__PURE__ */ new Date();
822
- const expires = new Date(now.getTime() + leaseMs).toISOString();
823
- const owner = principal.instance ?? principal.agent;
824
- const result = this.db.prepare("UPDATE bridge_deliveries SET lease_expires_at=? WHERE workspace=? AND recipient=? AND lease_owner=? AND id=? AND lease_token=? AND state='claimed' AND lease_expires_at > ?").run(expires, principal.workspace, principal.agent, owner, id, token, now.toISOString());
825
- if (!result.changes) return null;
826
- return delivery(this.db.prepare("SELECT * FROM bridge_deliveries WHERE id=?").get(id));
903
+ return this.write(() => {
904
+ const now = /* @__PURE__ */ new Date();
905
+ const expires = new Date(now.getTime() + leaseMs).toISOString();
906
+ const owner = principal.instance ?? principal.agent;
907
+ const result = this.db.prepare("UPDATE bridge_deliveries SET lease_expires_at=? WHERE workspace=? AND recipient=? AND lease_owner=? AND id=? AND lease_token=? AND state='claimed' AND lease_expires_at > ?").run(expires, principal.workspace, principal.agent, owner, id, token, now.toISOString());
908
+ if (!result.changes) return null;
909
+ return delivery(this.db.prepare("SELECT * FROM bridge_deliveries WHERE id=?").get(id));
910
+ });
827
911
  }
828
912
  async settleDelivery(principal, id, token, state, error, _retryPolicy) {
829
913
  await this.ready();
830
914
  const now = /* @__PURE__ */ new Date();
831
915
  const owner = principal.instance ?? principal.agent;
832
- this.db.exec("BEGIN IMMEDIATE");
833
- try {
834
- const current = this.db.prepare("SELECT delivery.*,message.delivery_policy FROM bridge_deliveries delivery JOIN bridge_messages message ON message.workspace=delivery.workspace AND message.id=delivery.message_id WHERE delivery.workspace=? AND delivery.recipient=? AND delivery.lease_owner=? AND delivery.id=? AND delivery.lease_token=? AND delivery.state='claimed' AND delivery.lease_expires_at>?").get(principal.workspace, principal.agent, owner, id, token, now.toISOString());
835
- if (!current) {
916
+ return this.write(() => {
917
+ this.db.exec("BEGIN IMMEDIATE");
918
+ try {
919
+ const current = this.db.prepare("SELECT delivery.*,message.delivery_policy FROM bridge_deliveries delivery JOIN bridge_messages message ON message.workspace=delivery.workspace AND message.id=delivery.message_id WHERE delivery.workspace=? AND delivery.recipient=? AND delivery.lease_owner=? AND delivery.id=? AND delivery.lease_token=? AND delivery.state='claimed' AND delivery.lease_expires_at>?").get(principal.workspace, principal.agent, owner, id, token, now.toISOString());
920
+ if (!current) {
921
+ this.db.exec("COMMIT");
922
+ return null;
923
+ }
924
+ const policy = parse(current.delivery_policy);
925
+ const exhausted = state === "retrying" && Number(current.cycle_attempt) >= Number(policy.maxAttempts);
926
+ const nextState = exhausted ? "dead" : state;
927
+ const exponential = Math.min(policy.retryMaxDelayMs, policy.retryBaseDelayMs * 2 ** Math.max(0, Number(current.cycle_attempt) - 1));
928
+ const jitter = 1 + (Math.random() * 2 - 1) * policy.retryJitterRatio;
929
+ const delay = nextState === "retrying" ? Math.max(1, Math.round(exponential * jitter)) : 0;
930
+ const available = nextState === "retrying" ? new Date(now.getTime() + delay).toISOString() : String(current.available_at);
931
+ const action = state === "acked" ? "ack" : state === "dead" ? "nack_dead" : exhausted ? "attempts_exhausted" : "nack_retry";
932
+ this.db.prepare("UPDATE bridge_deliveries SET state=?,available_at=?,last_error=?,lease_token=NULL,lease_owner=NULL,lease_expires_at=NULL,last_actor=?,last_action=? WHERE id=?").run(nextState, available, error?.slice(0, 1024) ?? null, principal.agent, action, id);
933
+ const result = delivery(this.db.prepare("SELECT * FROM bridge_deliveries WHERE id=?").get(id));
836
934
  this.db.exec("COMMIT");
837
- return null;
935
+ return result;
936
+ } catch (caught) {
937
+ this.db.exec("ROLLBACK");
938
+ throw caught;
838
939
  }
839
- const policy = parse(current.delivery_policy);
840
- const exhausted = state === "retrying" && Number(current.cycle_attempt) >= Number(policy.maxAttempts);
841
- const nextState = exhausted ? "dead" : state;
842
- const exponential = Math.min(policy.retryMaxDelayMs, policy.retryBaseDelayMs * 2 ** Math.max(0, Number(current.cycle_attempt) - 1));
843
- const jitter = 1 + (Math.random() * 2 - 1) * policy.retryJitterRatio;
844
- const delay = nextState === "retrying" ? Math.max(1, Math.round(exponential * jitter)) : 0;
845
- const available = nextState === "retrying" ? new Date(now.getTime() + delay).toISOString() : String(current.available_at);
846
- const action = state === "acked" ? "ack" : state === "dead" ? "nack_dead" : exhausted ? "attempts_exhausted" : "nack_retry";
847
- this.db.prepare("UPDATE bridge_deliveries SET state=?,available_at=?,last_error=?,lease_token=NULL,lease_owner=NULL,lease_expires_at=NULL,last_actor=?,last_action=? WHERE id=?").run(nextState, available, error?.slice(0, 1024) ?? null, principal.agent, action, id);
848
- const result = delivery(this.db.prepare("SELECT * FROM bridge_deliveries WHERE id=?").get(id));
849
- this.db.exec("COMMIT");
850
- return result;
851
- } catch (caught) {
852
- this.db.exec("ROLLBACK");
853
- throw caught;
854
- }
940
+ });
855
941
  }
856
942
  async diagnostics(principal) {
857
943
  await this.ready();
@@ -877,31 +963,33 @@ var SQLiteBridgeStore = class {
877
963
  const now = /* @__PURE__ */ new Date();
878
964
  const nowText = now.toISOString();
879
965
  const expires = new Date(now.getTime() + leaseMs).toISOString();
880
- this.db.exec("BEGIN IMMEDIATE");
881
- try {
882
- this.db.prepare("DELETE FROM bridge_presence WHERE workspace=? AND lease_expires_at<=?").run(principal.workspace, nowText);
883
- const current = this.db.prepare("SELECT 1 FROM bridge_presence WHERE workspace=? AND agent=? AND instance=?").get(principal.workspace, principal.agent, principal.instance);
884
- const agentCount = this.db.prepare("SELECT count(*) AS count FROM bridge_presence WHERE workspace=? AND agent=?").get(principal.workspace, principal.agent);
885
- const workspaceCount = this.db.prepare("SELECT count(*) AS count FROM bridge_presence WHERE workspace=?").get(principal.workspace);
886
- if (!current && (Number(agentCount.count) >= 128 || Number(workspaceCount.count) >= 4096)) {
887
- throw Object.assign(new Error("active instance limit reached"), {
888
- status: 429,
889
- code: "presence_limit"
890
- });
966
+ return this.write(() => {
967
+ this.db.exec("BEGIN IMMEDIATE");
968
+ try {
969
+ this.db.prepare("DELETE FROM bridge_presence WHERE workspace=? AND lease_expires_at<=?").run(principal.workspace, nowText);
970
+ const current = this.db.prepare("SELECT 1 FROM bridge_presence WHERE workspace=? AND agent=? AND instance=?").get(principal.workspace, principal.agent, principal.instance);
971
+ const agentCount = this.db.prepare("SELECT count(*) AS count FROM bridge_presence WHERE workspace=? AND agent=?").get(principal.workspace, principal.agent);
972
+ const workspaceCount = this.db.prepare("SELECT count(*) AS count FROM bridge_presence WHERE workspace=?").get(principal.workspace);
973
+ if (!current && (Number(agentCount.count) >= 128 || Number(workspaceCount.count) >= 4096)) {
974
+ throw Object.assign(new Error("active instance limit reached"), {
975
+ status: 429,
976
+ code: "presence_limit"
977
+ });
978
+ }
979
+ this.db.prepare("INSERT INTO bridge_presence (workspace,agent,instance,runtime_type,capabilities,lease_expires_at,last_seen_at) VALUES (?,?,?,?,?,?,?) ON CONFLICT(workspace,agent,instance) DO UPDATE SET runtime_type=excluded.runtime_type, capabilities=excluded.capabilities, lease_expires_at=excluded.lease_expires_at, last_seen_at=excluded.last_seen_at").run(principal.workspace, principal.agent, principal.instance, runtimeType ?? null, JSON.stringify(capabilities), expires, nowText);
980
+ const result = presence(this.db.prepare("SELECT * FROM bridge_presence WHERE workspace=? AND agent=? AND instance=?").get(principal.workspace, principal.agent, principal.instance));
981
+ this.db.exec("COMMIT");
982
+ return result;
983
+ } catch (error) {
984
+ this.db.exec("ROLLBACK");
985
+ throw error;
891
986
  }
892
- this.db.prepare("INSERT INTO bridge_presence (workspace,agent,instance,runtime_type,capabilities,lease_expires_at,last_seen_at) VALUES (?,?,?,?,?,?,?) ON CONFLICT(workspace,agent,instance) DO UPDATE SET runtime_type=excluded.runtime_type, capabilities=excluded.capabilities, lease_expires_at=excluded.lease_expires_at, last_seen_at=excluded.last_seen_at").run(principal.workspace, principal.agent, principal.instance, runtimeType ?? null, JSON.stringify(capabilities), expires, nowText);
893
- const result = presence(this.db.prepare("SELECT * FROM bridge_presence WHERE workspace=? AND agent=? AND instance=?").get(principal.workspace, principal.agent, principal.instance));
894
- this.db.exec("COMMIT");
895
- return result;
896
- } catch (error) {
897
- this.db.exec("ROLLBACK");
898
- throw error;
899
- }
987
+ });
900
988
  }
901
989
  async listPresence(principal) {
902
990
  await this.ready();
903
991
  const now = (/* @__PURE__ */ new Date()).toISOString();
904
- this.db.prepare("DELETE FROM bridge_presence WHERE workspace=? AND lease_expires_at<=?").run(principal.workspace, now);
992
+ await this.write(() => this.db.prepare("DELETE FROM bridge_presence WHERE workspace=? AND lease_expires_at<=?").run(principal.workspace, now));
905
993
  const rows = this.db.prepare("SELECT * FROM bridge_presence WHERE workspace=? ORDER BY agent,instance").all(principal.workspace);
906
994
  return rows.map(presence);
907
995
  }
@@ -950,49 +1038,53 @@ var SQLiteBridgeStore = class {
950
1038
  }
951
1039
  async cancelDelivery(principal, id) {
952
1040
  await this.ready();
953
- this.db.exec("BEGIN IMMEDIATE");
954
- try {
955
- const current = this.db.prepare("SELECT delivery.* FROM bridge_deliveries delivery JOIN bridge_messages message ON message.workspace=delivery.workspace AND message.id=delivery.message_id WHERE delivery.id=? AND delivery.workspace=? AND message.source=?").get(id, principal.workspace, principal.agent);
956
- if (!current) {
957
- this.db.exec("COMMIT");
958
- return null;
959
- }
960
- if (current.state === "cancelled") {
1041
+ return this.write(() => {
1042
+ this.db.exec("BEGIN IMMEDIATE");
1043
+ try {
1044
+ const current = this.db.prepare("SELECT delivery.* FROM bridge_deliveries delivery JOIN bridge_messages message ON message.workspace=delivery.workspace AND message.id=delivery.message_id WHERE delivery.id=? AND delivery.workspace=? AND message.source=?").get(id, principal.workspace, principal.agent);
1045
+ if (!current) {
1046
+ this.db.exec("COMMIT");
1047
+ return null;
1048
+ }
1049
+ if (current.state === "cancelled") {
1050
+ this.db.exec("COMMIT");
1051
+ return delivery(current);
1052
+ }
1053
+ if (!["pending", "retrying", "claimed"].includes(String(current.state))) throw new DeliveryStateConflictError(`cannot cancel a ${current.state} delivery`);
1054
+ this.db.prepare("UPDATE bridge_deliveries SET state='cancelled',lease_token=NULL,lease_owner=NULL,lease_expires_at=NULL,last_error=NULL,last_actor=?,last_action='cancel' WHERE id=?").run(principal.agent, id);
1055
+ const result = delivery(this.db.prepare("SELECT * FROM bridge_deliveries WHERE id=?").get(id));
961
1056
  this.db.exec("COMMIT");
962
- return delivery(current);
1057
+ return result;
1058
+ } catch (error) {
1059
+ this.db.exec("ROLLBACK");
1060
+ throw error;
963
1061
  }
964
- if (!["pending", "retrying", "claimed"].includes(String(current.state))) throw new DeliveryStateConflictError(`cannot cancel a ${current.state} delivery`);
965
- this.db.prepare("UPDATE bridge_deliveries SET state='cancelled',lease_token=NULL,lease_owner=NULL,lease_expires_at=NULL,last_error=NULL,last_actor=?,last_action='cancel' WHERE id=?").run(principal.agent, id);
966
- const result = delivery(this.db.prepare("SELECT * FROM bridge_deliveries WHERE id=?").get(id));
967
- this.db.exec("COMMIT");
968
- return result;
969
- } catch (error) {
970
- this.db.exec("ROLLBACK");
971
- throw error;
972
- }
1062
+ });
973
1063
  }
974
1064
  async requeueDelivery(principal, id) {
975
1065
  await this.ready();
976
- this.db.exec("BEGIN IMMEDIATE");
977
- try {
978
- const current = this.db.prepare("SELECT delivery.*,message.expires_at,message.delivery_policy FROM bridge_deliveries delivery JOIN bridge_messages message ON message.workspace=delivery.workspace AND message.id=delivery.message_id WHERE delivery.id=? AND delivery.workspace=? AND message.source=?").get(id, principal.workspace, principal.agent);
979
- if (!current) {
1066
+ return this.write(() => {
1067
+ this.db.exec("BEGIN IMMEDIATE");
1068
+ try {
1069
+ const current = this.db.prepare("SELECT delivery.*,message.expires_at,message.delivery_policy FROM bridge_deliveries delivery JOIN bridge_messages message ON message.workspace=delivery.workspace AND message.id=delivery.message_id WHERE delivery.id=? AND delivery.workspace=? AND message.source=?").get(id, principal.workspace, principal.agent);
1070
+ if (!current) {
1071
+ this.db.exec("COMMIT");
1072
+ return null;
1073
+ }
1074
+ if (!["dead", "cancelled"].includes(String(current.state))) throw new DeliveryStateConflictError(`cannot requeue a ${current.state} delivery`);
1075
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1076
+ if (current.expires_at && String(current.expires_at) <= now) throw new DeliveryStateConflictError("cannot requeue an expired message");
1077
+ const policy = parse(current.delivery_policy);
1078
+ const availableAt = policy.notBefore && policy.notBefore > now ? policy.notBefore : now;
1079
+ this.db.prepare("UPDATE bridge_deliveries SET state='pending',available_at=?,cycle_attempt=0,requeue_count=requeue_count+1,lease_token=NULL,lease_owner=NULL,lease_expires_at=NULL,last_error=NULL,last_actor=?,last_action='requeue' WHERE id=?").run(availableAt, principal.agent, id);
1080
+ const result = delivery(this.db.prepare("SELECT * FROM bridge_deliveries WHERE id=?").get(id));
980
1081
  this.db.exec("COMMIT");
981
- return null;
1082
+ return result;
1083
+ } catch (error) {
1084
+ this.db.exec("ROLLBACK");
1085
+ throw error;
982
1086
  }
983
- if (!["dead", "cancelled"].includes(String(current.state))) throw new DeliveryStateConflictError(`cannot requeue a ${current.state} delivery`);
984
- const now = (/* @__PURE__ */ new Date()).toISOString();
985
- if (current.expires_at && String(current.expires_at) <= now) throw new DeliveryStateConflictError("cannot requeue an expired message");
986
- const policy = parse(current.delivery_policy);
987
- const availableAt = policy.notBefore && policy.notBefore > now ? policy.notBefore : now;
988
- this.db.prepare("UPDATE bridge_deliveries SET state='pending',available_at=?,cycle_attempt=0,requeue_count=requeue_count+1,lease_token=NULL,lease_owner=NULL,lease_expires_at=NULL,last_error=NULL,last_actor=?,last_action='requeue' WHERE id=?").run(availableAt, principal.agent, id);
989
- const result = delivery(this.db.prepare("SELECT * FROM bridge_deliveries WHERE id=?").get(id));
990
- this.db.exec("COMMIT");
991
- return result;
992
- } catch (error) {
993
- this.db.exec("ROLLBACK");
994
- throw error;
995
- }
1087
+ });
996
1088
  }
997
1089
  async close() {
998
1090
  this.db.close();
@@ -1022,4 +1114,4 @@ export {
1022
1114
  verifySqliteHealth,
1023
1115
  SQLiteBridgeStore
1024
1116
  };
1025
- //# sourceMappingURL=chunk-IVG4XWWS.js.map
1117
+ //# sourceMappingURL=chunk-7PYY3S27.js.map