@dsh-enhanced/assistant-delivery 0.1.62 → 0.1.63
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/README.md +27 -10
- package/bin/dsh-restart-supervisor.js +76 -0
- package/lib/agent-runtime.d.ts +19 -1
- package/lib/agent-runtime.d.ts.map +1 -1
- package/lib/agent-runtime.js +122 -15
- package/lib/agent-runtime.js.map +1 -1
- package/lib/operator-snapshot.d.ts +1 -1
- package/lib/operator-snapshot.d.ts.map +1 -1
- package/lib/operator-snapshot.js +4 -4
- package/lib/operator-snapshot.js.map +1 -1
- package/lib/restart-native.d.ts +47 -0
- package/lib/restart-native.d.ts.map +1 -0
- package/lib/restart-native.js +170 -0
- package/lib/restart-native.js.map +1 -0
- package/lib/restart-runtime.d.ts +38 -0
- package/lib/restart-runtime.d.ts.map +1 -0
- package/lib/restart-runtime.js +335 -0
- package/lib/restart-runtime.js.map +1 -0
- package/lib/restart-store.d.ts +132 -0
- package/lib/restart-store.d.ts.map +1 -0
- package/lib/restart-store.js +757 -0
- package/lib/restart-store.js.map +1 -0
- package/lib/restart-supervisor.d.ts +129 -0
- package/lib/restart-supervisor.d.ts.map +1 -0
- package/lib/restart-supervisor.js +615 -0
- package/lib/restart-supervisor.js.map +1 -0
- package/lib/service.d.ts +42 -1
- package/lib/service.d.ts.map +1 -1
- package/lib/service.js +455 -6
- package/lib/service.js.map +1 -1
- package/lib/sqlite.d.ts +1 -1
- package/lib/sqlite.d.ts.map +1 -1
- package/lib/sqlite.js +44 -2
- package/lib/sqlite.js.map +1 -1
- package/lib/store.d.ts +23 -0
- package/lib/store.d.ts.map +1 -1
- package/lib/store.js +222 -85
- package/lib/store.js.map +1 -1
- package/lib/tools.d.ts.map +1 -1
- package/lib/tools.js +21 -2
- package/lib/tools.js.map +1 -1
- package/lib/types.d.ts +2 -0
- package/lib/types.d.ts.map +1 -1
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +11 -10
package/lib/store.js
CHANGED
|
@@ -2649,6 +2649,94 @@ export class DeliveryStore {
|
|
|
2649
2649
|
`).all(afterSequence, limit);
|
|
2650
2650
|
return rows.map(inboxFromRow);
|
|
2651
2651
|
}
|
|
2652
|
+
/** Scan durable, unacknowledged dispatched turns without replaying their Agent work. */
|
|
2653
|
+
listInterruptedInboxNotices(input = {}) {
|
|
2654
|
+
this.assertOpen();
|
|
2655
|
+
const afterSequence = input.afterSequence ?? 0;
|
|
2656
|
+
if (!Number.isSafeInteger(afterSequence) || afterSequence < 0) {
|
|
2657
|
+
throw new DeliveryStoreError('conflict', 'invalid interrupted Inbox cursor');
|
|
2658
|
+
}
|
|
2659
|
+
const limit = boundedMaintenanceLimit(input.limit, 100, 'interrupted Inbox');
|
|
2660
|
+
const rows = this.database.prepare(`${inboxSelect}
|
|
2661
|
+
WHERE inbox_messages.status = 'dead_letter'
|
|
2662
|
+
AND inbox_messages.failure_code = 'dispatch-ambiguous'
|
|
2663
|
+
AND json_extract(inbox_messages.envelope_json, '$.kind') = 'text'
|
|
2664
|
+
AND inbox_messages.channel != 'web'
|
|
2665
|
+
AND inbox_messages.binding_id IS NOT NULL
|
|
2666
|
+
AND EXISTS (
|
|
2667
|
+
SELECT 1 FROM delivery_foreground_executions AS execution
|
|
2668
|
+
WHERE execution.inbox_id = inbox_messages.id
|
|
2669
|
+
)
|
|
2670
|
+
AND admission_sequence > ?
|
|
2671
|
+
AND NOT EXISTS (
|
|
2672
|
+
SELECT 1 FROM dead_letter_resolutions AS resolution
|
|
2673
|
+
WHERE resolution.kind = 'inbox' AND resolution.message_id = inbox_messages.id
|
|
2674
|
+
AND resolution.attempt_count = inbox_messages.attempt_count
|
|
2675
|
+
)
|
|
2676
|
+
AND NOT EXISTS (
|
|
2677
|
+
SELECT 1 FROM outbox_messages AS reply
|
|
2678
|
+
WHERE reply.idempotency_key IN (
|
|
2679
|
+
'inbound:' || inbox_messages.id || ':reply',
|
|
2680
|
+
'inbound:' || inbox_messages.event_id || ':reply',
|
|
2681
|
+
'inbound:' || inbox_messages.id || ':interrupted'
|
|
2682
|
+
)
|
|
2683
|
+
)
|
|
2684
|
+
ORDER BY admission_sequence LIMIT ?
|
|
2685
|
+
`).all(afterSequence, limit);
|
|
2686
|
+
return rows.map(inboxFromRow);
|
|
2687
|
+
}
|
|
2688
|
+
/** The final notice decision and Outbox insert share one SQLite writer fence. */
|
|
2689
|
+
enqueueInterruptedInboxNotice(input) {
|
|
2690
|
+
this.assertOpen();
|
|
2691
|
+
return this.transaction(() => {
|
|
2692
|
+
if (!this.interruptedInboxNoticeCurrent(input)) {
|
|
2693
|
+
throw new DeliveryStoreError('version-conflict', 'interrupted Inbox notice lost its exact owner or reply fence');
|
|
2694
|
+
}
|
|
2695
|
+
return this.enqueueIntent(input.intent, false);
|
|
2696
|
+
});
|
|
2697
|
+
}
|
|
2698
|
+
/** Recheck a queued interruption notice immediately before an adapter send. */
|
|
2699
|
+
canSendInterruptedInboxNotice(record) {
|
|
2700
|
+
this.assertOpen();
|
|
2701
|
+
const key = record.intent.idempotencyKey;
|
|
2702
|
+
if (!key.startsWith('inbound:') || !key.endsWith(':interrupted'))
|
|
2703
|
+
return false;
|
|
2704
|
+
const inboxId = key.slice('inbound:'.length, -':interrupted'.length);
|
|
2705
|
+
const binding = this.getBinding(record.intent.bindingId);
|
|
2706
|
+
return binding !== undefined
|
|
2707
|
+
&& this.getOutboxByIdempotencyKey(key)?.id === record.id
|
|
2708
|
+
&& this.interruptedInboxNoticeCurrent({ inboxId, binding, intent: record.intent });
|
|
2709
|
+
}
|
|
2710
|
+
interruptedInboxNoticeCurrent(input) {
|
|
2711
|
+
const inbox = this.getInbox(input.inboxId);
|
|
2712
|
+
const binding = inbox?.bindingId === undefined ? undefined : this.getBinding(inbox.bindingId);
|
|
2713
|
+
const owner = binding === undefined ? undefined : this.getBindingPrincipal(binding.id);
|
|
2714
|
+
const execution = this.database.prepare(`
|
|
2715
|
+
SELECT principal_record_id, principal_version, binding_id, binding_version,
|
|
2716
|
+
binding_generation, workspace, preset
|
|
2717
|
+
FROM delivery_foreground_executions WHERE inbox_id = ?
|
|
2718
|
+
`).get(input.inboxId);
|
|
2719
|
+
return inbox?.status === 'dead_letter' && inbox.failureCode === 'dispatch-ambiguous'
|
|
2720
|
+
&& inbox.envelope.kind === 'text' && inbox.channel !== 'web'
|
|
2721
|
+
&& binding?.status === 'active' && binding.id === input.binding.id
|
|
2722
|
+
&& binding.version === input.binding.version && binding.generation === input.binding.generation
|
|
2723
|
+
&& binding.sessionId === input.binding.sessionId
|
|
2724
|
+
&& this.getActiveBinding(inbox.envelope.conversation)?.id === binding.id
|
|
2725
|
+
&& owner?.status === 'active' && owner.role === 'owner'
|
|
2726
|
+
&& execution?.principal_record_id === owner.id && execution.principal_version === owner.version
|
|
2727
|
+
&& execution.binding_id === binding.id && execution.binding_version === binding.version
|
|
2728
|
+
&& execution.binding_generation === binding.generation
|
|
2729
|
+
&& execution.workspace === binding.workspace && execution.preset === binding.agentPreset
|
|
2730
|
+
&& principalHash(inbox.envelope.principal) === principalHash(binding.principal)
|
|
2731
|
+
&& conversationHash(inbox.envelope.conversation) === conversationHash(binding.conversation)
|
|
2732
|
+
&& input.intent.bindingId === binding.id
|
|
2733
|
+
&& input.intent.replyToEventId === inbox.eventId
|
|
2734
|
+
&& input.intent.idempotencyKey === `inbound:${inbox.id}:interrupted`
|
|
2735
|
+
&& this.getDeadLetterResolution({ kind: 'inbox', id: inbox.id,
|
|
2736
|
+
attemptCount: inbox.attemptCount }) === undefined
|
|
2737
|
+
&& this.getOutboxByIdempotencyKey(`inbound:${inbox.id}:reply`) === undefined
|
|
2738
|
+
&& this.getOutboxByIdempotencyKey(`inbound:${inbox.eventId}:reply`) === undefined;
|
|
2739
|
+
}
|
|
2652
2740
|
listInbox(input = {}) {
|
|
2653
2741
|
this.assertOpen();
|
|
2654
2742
|
const limit = Math.max(1, Math.min(100, input.limit ?? 20));
|
|
@@ -2804,6 +2892,40 @@ export class DeliveryStore {
|
|
|
2804
2892
|
}
|
|
2805
2893
|
return this.getInbox(inboxId);
|
|
2806
2894
|
}
|
|
2895
|
+
/** A durable control message wins even while its async binding transition is waiting. */
|
|
2896
|
+
hasRestartCancellationAfter(inboxId, binding) {
|
|
2897
|
+
this.assertOpen();
|
|
2898
|
+
const rows = this.database.prepare(`SELECT message.envelope_json FROM inbox_messages AS message
|
|
2899
|
+
JOIN delivery_inbox_admissions AS admission ON admission.inbox_id = message.id
|
|
2900
|
+
WHERE admission.admission_sequence > (SELECT admission_sequence FROM delivery_inbox_admissions WHERE inbox_id = ?)
|
|
2901
|
+
AND message.status != 'dead_letter'
|
|
2902
|
+
AND json_extract(message.envelope_json, '$.kind') = 'command'
|
|
2903
|
+
AND json_extract(message.envelope_json, '$.conversation') = json(?)
|
|
2904
|
+
AND json_extract(message.envelope_json, '$.principal') = json(?)`)
|
|
2905
|
+
.all(inboxId, conversationJson(binding.conversation), principalJson(binding.principal));
|
|
2906
|
+
return rows.some(row => isExactDeliveryCommand(parseDeliveryCommand(JSON.parse(row.envelope_json)), 'stop', 'new', 'clear'));
|
|
2907
|
+
}
|
|
2908
|
+
/** The fence and Inbox claims share this database's write transaction. */
|
|
2909
|
+
setRestartFence(requestId, sourceInvocationId) {
|
|
2910
|
+
this.assertOpen();
|
|
2911
|
+
this.transaction(() => {
|
|
2912
|
+
const current = this.getRestartFence();
|
|
2913
|
+
if (current !== undefined && (current.requestId !== requestId || current.sourceInvocationId !== sourceInvocationId)) {
|
|
2914
|
+
throw new DeliveryStoreError('conflict', 'another restart owns the delivery drain fence');
|
|
2915
|
+
}
|
|
2916
|
+
this.database.prepare('INSERT OR IGNORE INTO delivery_restart_fence VALUES (1, ?, ?)').run(requestId, sourceInvocationId);
|
|
2917
|
+
});
|
|
2918
|
+
}
|
|
2919
|
+
getRestartFence() {
|
|
2920
|
+
this.assertOpen();
|
|
2921
|
+
const row = this.database.prepare('SELECT request_id, source_invocation_id FROM delivery_restart_fence WHERE singleton = 1')
|
|
2922
|
+
.get();
|
|
2923
|
+
return row === undefined ? undefined : { requestId: row.request_id, sourceInvocationId: row.source_invocation_id };
|
|
2924
|
+
}
|
|
2925
|
+
clearRestartFence(requestId) {
|
|
2926
|
+
this.assertOpen();
|
|
2927
|
+
this.database.prepare('DELETE FROM delivery_restart_fence WHERE singleton = 1 AND request_id = ?').run(requestId);
|
|
2928
|
+
}
|
|
2807
2929
|
claimInbox(input) {
|
|
2808
2930
|
this.assertOpen();
|
|
2809
2931
|
const ownerId = validateBindingText(input.ownerId, 'ownerId', 256);
|
|
@@ -2819,6 +2941,8 @@ export class DeliveryStore {
|
|
|
2819
2941
|
const now = this.now();
|
|
2820
2942
|
const claims = [];
|
|
2821
2943
|
this.transaction(() => {
|
|
2944
|
+
if (this.getRestartFence() !== undefined)
|
|
2945
|
+
return;
|
|
2822
2946
|
this.database.prepare(`
|
|
2823
2947
|
UPDATE inbox_messages SET status = 'dead_letter', failure_code = 'attempts-exhausted',
|
|
2824
2948
|
next_attempt_at = NULL, updated_at = ?
|
|
@@ -4397,93 +4521,98 @@ export class DeliveryStore {
|
|
|
4397
4521
|
const claims = [];
|
|
4398
4522
|
const validatedOwnerRouteIds = [];
|
|
4399
4523
|
this.transaction(() => {
|
|
4400
|
-
this.
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
|
|
4425
|
-
|
|
4426
|
-
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
const
|
|
4444
|
-
|
|
4445
|
-
|
|
4446
|
-
|
|
4447
|
-
|
|
4448
|
-
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
|
|
4456
|
-
|
|
4457
|
-
|
|
4458
|
-
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
|
|
4524
|
+
const restartFenceActive = this.getRestartFence() !== undefined;
|
|
4525
|
+
// Drain without changing queued retries or unresolved sends. Only a
|
|
4526
|
+
// plain maintenance acknowledgement may start during this fence.
|
|
4527
|
+
if (!restartFenceActive) {
|
|
4528
|
+
this.database.prepare(`
|
|
4529
|
+
UPDATE outbox_messages SET status = 'dead', failure_code = 'attempts-exhausted',
|
|
4530
|
+
next_attempt_at = NULL, updated_at = ?
|
|
4531
|
+
WHERE rowid IN (
|
|
4532
|
+
SELECT candidate.rowid FROM outbox_messages AS candidate
|
|
4533
|
+
WHERE candidate.status = 'retry_wait' AND candidate.next_attempt_at <= ?
|
|
4534
|
+
AND (
|
|
4535
|
+
SELECT COUNT(*) FROM outbox_attempts AS history
|
|
4536
|
+
WHERE history.outbox_id = candidate.id AND history.operation = 'send'
|
|
4537
|
+
) >= ?
|
|
4538
|
+
ORDER BY candidate.rowid LIMIT ?
|
|
4539
|
+
)
|
|
4540
|
+
`).run(now, now, input.maxAttempts, maintenanceLimit);
|
|
4541
|
+
this.database.prepare(`
|
|
4542
|
+
UPDATE outbox_messages SET status = 'dead', failure_code = 'reconcile-attempts-exhausted',
|
|
4543
|
+
next_attempt_at = NULL, updated_at = ?
|
|
4544
|
+
WHERE rowid IN (
|
|
4545
|
+
SELECT candidate.rowid FROM outbox_messages AS candidate
|
|
4546
|
+
WHERE candidate.status = 'unknown_after_send'
|
|
4547
|
+
AND NOT EXISTS (
|
|
4548
|
+
SELECT 1 FROM dead_letter_resolutions AS resolution
|
|
4549
|
+
WHERE resolution.kind = 'outbox' AND resolution.message_id = candidate.id
|
|
4550
|
+
AND resolution.attempt_count = candidate.attempt_count AND resolution.resolution = 'cancel'
|
|
4551
|
+
)
|
|
4552
|
+
AND (
|
|
4553
|
+
SELECT COUNT(*) FROM outbox_attempts AS history
|
|
4554
|
+
WHERE history.outbox_id = candidate.id AND history.operation = 'reconcile'
|
|
4555
|
+
) >= ?
|
|
4556
|
+
ORDER BY candidate.rowid LIMIT ?
|
|
4557
|
+
)
|
|
4558
|
+
`).run(now, input.maxAttempts, maintenanceLimit);
|
|
4559
|
+
const ownerRouteRows = this.database.prepare(`
|
|
4560
|
+
SELECT id FROM outbox_messages
|
|
4561
|
+
WHERE status IN ('pending', 'retry_wait') AND claimed_by IS NULL
|
|
4562
|
+
AND (next_attempt_at IS NULL OR next_attempt_at <= ?)
|
|
4563
|
+
AND json_type(intent_json, '$.metadata."dsh.route.authority"') IS NOT NULL
|
|
4564
|
+
ORDER BY rowid LIMIT ?
|
|
4565
|
+
`).all(now, maintenanceLimit);
|
|
4566
|
+
const ownerRouteGuard = input.ownerRouteGuard ?? { ownerRoutes: [], authorize: () => false };
|
|
4567
|
+
for (const row of ownerRouteRows) {
|
|
4568
|
+
const record = this.getOutbox(row.id);
|
|
4569
|
+
if (record === undefined)
|
|
4570
|
+
continue;
|
|
4571
|
+
const inspected = this.inspectOwnerRouteDispatch(record, ownerRouteGuard);
|
|
4572
|
+
if (inspected.kind === 'deferred') {
|
|
4573
|
+
this.deferOwnerRouteBeforeClaim(record, inspected.failureCode, now, now + Math.max(1, Math.min(input.leaseMs, 30_000)));
|
|
4574
|
+
continue;
|
|
4575
|
+
}
|
|
4576
|
+
if (inspected.kind !== 'authorized') {
|
|
4577
|
+
const failureCode = inspected.kind === 'denied'
|
|
4578
|
+
? inspected.failureCode
|
|
4579
|
+
: 'owner-route-receipt-invalid';
|
|
4580
|
+
this.deadLetterOwnerRouteBeforeClaim(record, ownerId, failureCode, now);
|
|
4581
|
+
continue;
|
|
4582
|
+
}
|
|
4583
|
+
if (!this.ownerRouteIsProvablyUnsent(record)) {
|
|
4584
|
+
this.deadLetterOwnerRouteBeforeClaim(record, ownerId, 'owner-route-send-proof-missing', now);
|
|
4585
|
+
continue;
|
|
4586
|
+
}
|
|
4587
|
+
if (!this.rebindOwnerRouteBeforeClaim(record, inspected)) {
|
|
4588
|
+
this.deadLetterOwnerRouteBeforeClaim(record, ownerId, 'owner-route-rebind-conflict', now);
|
|
4589
|
+
continue;
|
|
4590
|
+
}
|
|
4591
|
+
validatedOwnerRouteIds.push(record.id);
|
|
4462
4592
|
}
|
|
4463
|
-
|
|
4593
|
+
this.database.prepare(`
|
|
4594
|
+
UPDATE outbox_messages SET status = 'dead', failure_code = CASE
|
|
4595
|
+
WHEN status = 'unknown_after_send' THEN 'binding-revoked-unknown'
|
|
4596
|
+
ELSE 'binding-revoked'
|
|
4597
|
+
END,
|
|
4598
|
+
next_attempt_at = NULL, updated_at = ?
|
|
4599
|
+
WHERE rowid IN (
|
|
4600
|
+
SELECT candidate.rowid FROM outbox_messages AS candidate
|
|
4601
|
+
WHERE candidate.status IN ('pending', 'retry_wait', 'unknown_after_send')
|
|
4602
|
+
AND json_type(candidate.intent_json, '$.metadata."dsh.route.authority"') IS NULL
|
|
4603
|
+
AND NOT EXISTS (
|
|
4604
|
+
SELECT 1 FROM dead_letter_resolutions AS resolution
|
|
4605
|
+
WHERE resolution.kind = 'outbox' AND resolution.message_id = candidate.id
|
|
4606
|
+
AND resolution.attempt_count = candidate.attempt_count AND resolution.resolution = 'cancel'
|
|
4607
|
+
)
|
|
4608
|
+
AND NOT EXISTS (
|
|
4609
|
+
SELECT 1 FROM conversation_bindings AS binding
|
|
4610
|
+
WHERE binding.id = candidate.binding_id AND binding.status = 'active'
|
|
4611
|
+
)
|
|
4612
|
+
ORDER BY candidate.rowid LIMIT ?
|
|
4613
|
+
)
|
|
4614
|
+
`).run(now, maintenanceLimit);
|
|
4464
4615
|
}
|
|
4465
|
-
this.database.prepare(`
|
|
4466
|
-
UPDATE outbox_messages SET status = 'dead', failure_code = CASE
|
|
4467
|
-
WHEN status = 'unknown_after_send' THEN 'binding-revoked-unknown'
|
|
4468
|
-
ELSE 'binding-revoked'
|
|
4469
|
-
END,
|
|
4470
|
-
next_attempt_at = NULL, updated_at = ?
|
|
4471
|
-
WHERE rowid IN (
|
|
4472
|
-
SELECT candidate.rowid FROM outbox_messages AS candidate
|
|
4473
|
-
WHERE candidate.status IN ('pending', 'retry_wait', 'unknown_after_send')
|
|
4474
|
-
AND json_type(candidate.intent_json, '$.metadata."dsh.route.authority"') IS NULL
|
|
4475
|
-
AND NOT EXISTS (
|
|
4476
|
-
SELECT 1 FROM dead_letter_resolutions AS resolution
|
|
4477
|
-
WHERE resolution.kind = 'outbox' AND resolution.message_id = candidate.id
|
|
4478
|
-
AND resolution.attempt_count = candidate.attempt_count AND resolution.resolution = 'cancel'
|
|
4479
|
-
)
|
|
4480
|
-
AND NOT EXISTS (
|
|
4481
|
-
SELECT 1 FROM conversation_bindings AS binding
|
|
4482
|
-
WHERE binding.id = candidate.binding_id AND binding.status = 'active'
|
|
4483
|
-
)
|
|
4484
|
-
ORDER BY candidate.rowid LIMIT ?
|
|
4485
|
-
)
|
|
4486
|
-
`).run(now, maintenanceLimit);
|
|
4487
4616
|
const ownerRouteClaimClause = validatedOwnerRouteIds.length === 0
|
|
4488
4617
|
? `AND (
|
|
4489
4618
|
json_type(candidate.intent_json, '$.metadata."dsh.route.authority"') IS NULL
|
|
@@ -4498,6 +4627,13 @@ export class DeliveryStore {
|
|
|
4498
4627
|
SELECT candidate.id, candidate.status FROM outbox_messages AS candidate
|
|
4499
4628
|
WHERE candidate.status IN ('pending', 'retry_wait', 'unknown_after_send')
|
|
4500
4629
|
AND candidate.claimed_by IS NULL
|
|
4630
|
+
${restartFenceActive ? `AND candidate.status IN ('pending', 'retry_wait')
|
|
4631
|
+
AND candidate.idempotency_key GLOB 'inbound:*:maintenance'
|
|
4632
|
+
AND json_extract(candidate.intent_json, '$.format') = 'plain'
|
|
4633
|
+
AND json_type(candidate.intent_json, '$.metadata') IS NULL
|
|
4634
|
+
AND json_type(candidate.intent_json, '$.approval') IS NULL
|
|
4635
|
+
AND json_type(candidate.intent_json, '$.modelPicker') IS NULL
|
|
4636
|
+
AND json_type(candidate.intent_json, '$.permissionPicker') IS NULL` : ''}
|
|
4501
4637
|
AND NOT EXISTS (
|
|
4502
4638
|
SELECT 1 FROM dead_letter_resolutions AS resolution
|
|
4503
4639
|
WHERE resolution.kind = 'outbox' AND resolution.message_id = candidate.id
|
|
@@ -4532,6 +4668,7 @@ export class DeliveryStore {
|
|
|
4532
4668
|
WHERE earlier.lane_hash = candidate.lane_hash
|
|
4533
4669
|
AND earlier.rowid < candidate.rowid
|
|
4534
4670
|
AND earlier.status NOT IN ('accepted', 'delivered', 'read', 'dead')
|
|
4671
|
+
${restartFenceActive ? "AND earlier.idempotency_key GLOB 'inbound:*:maintenance'" : ''}
|
|
4535
4672
|
AND NOT EXISTS (
|
|
4536
4673
|
SELECT 1 FROM dead_letter_resolutions AS resolution
|
|
4537
4674
|
WHERE resolution.kind = 'outbox' AND resolution.message_id = earlier.id
|