@parall/agent-core 1.43.0 → 1.45.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/dist/gateway-base.d.ts +33 -0
- package/dist/gateway-base.d.ts.map +1 -1
- package/dist/gateway-base.js +283 -82
- package/dist/gateway-lane-flow.d.ts +78 -6
- package/dist/gateway-lane-flow.d.ts.map +1 -1
- package/dist/gateway-lane-flow.js +277 -18
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/lane-ledger.d.ts +30 -0
- package/dist/lane-ledger.d.ts.map +1 -1
- package/dist/lane-ledger.js +50 -1
- package/dist/skills/parall-schedules.d.ts +1 -1
- package/dist/skills/parall-schedules.d.ts.map +1 -1
- package/dist/skills/parall-schedules.js +1 -1
- package/dist/skills/parall-tasks.d.ts +1 -1
- package/dist/skills/parall-tasks.d.ts.map +1 -1
- package/dist/skills/parall-tasks.js +21 -5
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/gateway-base.ts +372 -104
- package/src/gateway-lane-flow.ts +313 -19
- package/src/index.ts +1 -1
- package/src/lane-ledger.ts +60 -3
- package/src/skills/parall-schedules.ts +1 -1
- package/src/skills/parall-tasks.ts +21 -5
- package/src/types.ts +2 -1
package/src/gateway-base.ts
CHANGED
|
@@ -41,8 +41,11 @@ import {
|
|
|
41
41
|
consumeMessageWorkItem,
|
|
42
42
|
consumeTypedDispatch,
|
|
43
43
|
dispatchLaneGroup,
|
|
44
|
+
resolveDispatchByID,
|
|
45
|
+
settleDrainedTypedGroup,
|
|
46
|
+
typedLedgerEventIds,
|
|
44
47
|
} from './gateway-lane-flow.js';
|
|
45
|
-
import type { LaneFlowHost } from './gateway-lane-flow.js';
|
|
48
|
+
import type { LaneFlowHost, TypedConsumeHooks } from './gateway-lane-flow.js';
|
|
46
49
|
import { LaneLedger } from './lane-ledger.js';
|
|
47
50
|
import { routeTrigger } from './routing.js';
|
|
48
51
|
import {
|
|
@@ -447,30 +450,33 @@ export class ParallAgentGateway {
|
|
|
447
450
|
? { dispatchEventId: data.dispatch_event_id }
|
|
448
451
|
: { sourceType: 'task_activity', sourceId: data.id },
|
|
449
452
|
(dispatchEventId) => this.handleTaskAssignment(data, data.id, dispatchEventId),
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
this.
|
|
454
|
-
});
|
|
455
|
-
}
|
|
456
|
-
return this.opts.client
|
|
457
|
-
.ackDispatch(this.opts.config.org_id, {
|
|
458
|
-
source_type: 'task_activity',
|
|
459
|
-
source_id: data.id,
|
|
460
|
-
})
|
|
461
|
-
.then(
|
|
462
|
-
() => true,
|
|
463
|
-
(err) => {
|
|
464
|
-
// Same contract as ackDispatchEvent: a failed ack is a
|
|
465
|
-
// failed consume (arms backoff) and must free the hot-path
|
|
466
|
-
// dedupe so the re-drive isn't rejected by this pod forever.
|
|
453
|
+
{
|
|
454
|
+
legacyAck: (dispatchEventId) => {
|
|
455
|
+
if (dispatchEventId) {
|
|
456
|
+
return this.ackDispatchEvent(dispatchEventId, () => {
|
|
467
457
|
this.dispatchedTasks.delete(`${data.id}:${data.updated_at}`);
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
return this.opts.client
|
|
461
|
+
.ackDispatch(this.opts.config.org_id, {
|
|
462
|
+
source_type: 'task_activity',
|
|
463
|
+
source_id: data.id,
|
|
464
|
+
})
|
|
465
|
+
.then(
|
|
466
|
+
() => true,
|
|
467
|
+
(err) => {
|
|
468
|
+
// Same contract as ackDispatchEvent: a failed ack is a
|
|
469
|
+
// failed consume (arms backoff) and must free the hot-path
|
|
470
|
+
// dedupe so the re-drive isn't rejected by this pod forever.
|
|
471
|
+
this.dispatchedTasks.delete(`${data.id}:${data.updated_at}`);
|
|
472
|
+
this.opts.log?.warn(
|
|
473
|
+
`dispatch ack failed for task ${data.id}, releasing for re-drive: ${String(err)}`,
|
|
474
|
+
);
|
|
475
|
+
return false;
|
|
476
|
+
},
|
|
477
|
+
);
|
|
478
|
+
},
|
|
479
|
+
clearDedupe: () => this.dispatchedTasks.delete(`${data.id}:${data.updated_at}`),
|
|
474
480
|
},
|
|
475
481
|
);
|
|
476
482
|
} catch (err) {
|
|
@@ -493,14 +499,19 @@ export class ParallAgentGateway {
|
|
|
493
499
|
try {
|
|
494
500
|
await this.consumeTypedDispatch(
|
|
495
501
|
{ dispatchEventId: data.id },
|
|
496
|
-
() =>
|
|
502
|
+
(dispatchEventId) =>
|
|
497
503
|
this.handleTaskComment(
|
|
498
504
|
data.source_id,
|
|
499
505
|
data.task_id ?? '',
|
|
500
506
|
data.actor_id,
|
|
501
507
|
data.delivery_reason,
|
|
508
|
+
dispatchEventId,
|
|
502
509
|
),
|
|
503
|
-
|
|
510
|
+
{
|
|
511
|
+
legacyAck: () =>
|
|
512
|
+
this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)),
|
|
513
|
+
clearDedupe: () => this.clearTypedDispatchDedupe(data),
|
|
514
|
+
},
|
|
504
515
|
);
|
|
505
516
|
} catch (err) {
|
|
506
517
|
this.opts.log?.error(
|
|
@@ -512,8 +523,18 @@ export class ParallAgentGateway {
|
|
|
512
523
|
try {
|
|
513
524
|
await this.consumeTypedDispatch(
|
|
514
525
|
{ dispatchEventId: data.id },
|
|
515
|
-
() =>
|
|
516
|
-
|
|
526
|
+
(dispatchEventId) =>
|
|
527
|
+
this.handleWikiComment(
|
|
528
|
+
data.source_id,
|
|
529
|
+
data.actor_id,
|
|
530
|
+
data.delivery_reason,
|
|
531
|
+
dispatchEventId,
|
|
532
|
+
),
|
|
533
|
+
{
|
|
534
|
+
legacyAck: () =>
|
|
535
|
+
this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)),
|
|
536
|
+
clearDedupe: () => this.clearTypedDispatchDedupe(data),
|
|
537
|
+
},
|
|
517
538
|
);
|
|
518
539
|
} catch (err) {
|
|
519
540
|
this.opts.log?.error(
|
|
@@ -530,7 +551,11 @@ export class ParallAgentGateway {
|
|
|
530
551
|
allowCreator: true,
|
|
531
552
|
dispatchEventId,
|
|
532
553
|
}),
|
|
533
|
-
|
|
554
|
+
{
|
|
555
|
+
legacyAck: () =>
|
|
556
|
+
this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)),
|
|
557
|
+
clearDedupe: () => this.clearTypedDispatchDedupe(data),
|
|
558
|
+
},
|
|
534
559
|
);
|
|
535
560
|
} catch (err) {
|
|
536
561
|
this.opts.log?.error(`task update dispatch failed for ${data.task_id}: ${String(err)}`);
|
|
@@ -540,8 +565,13 @@ export class ParallAgentGateway {
|
|
|
540
565
|
try {
|
|
541
566
|
await this.consumeTypedDispatch(
|
|
542
567
|
{ dispatchEventId: data.id },
|
|
543
|
-
() =>
|
|
544
|
-
|
|
568
|
+
(dispatchEventId) =>
|
|
569
|
+
this.fetchAndHandleScheduleFire(data.source_id, data.actor_id, dispatchEventId),
|
|
570
|
+
{
|
|
571
|
+
legacyAck: () =>
|
|
572
|
+
this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)),
|
|
573
|
+
clearDedupe: () => this.clearTypedDispatchDedupe(data),
|
|
574
|
+
},
|
|
545
575
|
);
|
|
546
576
|
} catch (err) {
|
|
547
577
|
this.opts.log?.error(
|
|
@@ -553,8 +583,13 @@ export class ParallAgentGateway {
|
|
|
553
583
|
try {
|
|
554
584
|
await this.consumeTypedDispatch(
|
|
555
585
|
{ dispatchEventId: data.id },
|
|
556
|
-
() =>
|
|
557
|
-
|
|
586
|
+
(dispatchEventId) =>
|
|
587
|
+
this.fetchAndHandleExternalTriggerRun(data.source_id, dispatchEventId),
|
|
588
|
+
{
|
|
589
|
+
legacyAck: () =>
|
|
590
|
+
this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)),
|
|
591
|
+
clearDedupe: () => this.clearTypedDispatchDedupe(data),
|
|
592
|
+
},
|
|
558
593
|
);
|
|
559
594
|
} catch (err) {
|
|
560
595
|
this.opts.log?.error(
|
|
@@ -566,8 +601,12 @@ export class ParallAgentGateway {
|
|
|
566
601
|
try {
|
|
567
602
|
await this.consumeTypedDispatch(
|
|
568
603
|
{ dispatchEventId: data.id },
|
|
569
|
-
() => this.fetchAndHandleChannelMessage(data.source_id),
|
|
570
|
-
|
|
604
|
+
(dispatchEventId) => this.fetchAndHandleChannelMessage(data.source_id, dispatchEventId),
|
|
605
|
+
{
|
|
606
|
+
legacyAck: () =>
|
|
607
|
+
this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)),
|
|
608
|
+
clearDedupe: () => this.clearTypedDispatchDedupe(data),
|
|
609
|
+
},
|
|
571
610
|
);
|
|
572
611
|
} catch (err) {
|
|
573
612
|
this.opts.log?.error(
|
|
@@ -579,13 +618,18 @@ export class ParallAgentGateway {
|
|
|
579
618
|
try {
|
|
580
619
|
await this.consumeTypedDispatch(
|
|
581
620
|
{ dispatchEventId: data.id },
|
|
582
|
-
() =>
|
|
621
|
+
(dispatchEventId) =>
|
|
583
622
|
this.fetchAndHandleApprovalDecided(
|
|
584
623
|
data.source_id,
|
|
585
624
|
data.actor_id,
|
|
586
625
|
data.chat_id ?? null,
|
|
626
|
+
dispatchEventId,
|
|
587
627
|
),
|
|
588
|
-
|
|
628
|
+
{
|
|
629
|
+
legacyAck: () =>
|
|
630
|
+
this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)),
|
|
631
|
+
clearDedupe: () => this.clearTypedDispatchDedupe(data),
|
|
632
|
+
},
|
|
589
633
|
);
|
|
590
634
|
} catch (err) {
|
|
591
635
|
this.opts.log?.error(
|
|
@@ -641,6 +685,45 @@ export class ParallAgentGateway {
|
|
|
641
685
|
return true;
|
|
642
686
|
}
|
|
643
687
|
|
|
688
|
+
/**
|
|
689
|
+
* Lane currently being dispatched per session — lets external activity
|
|
690
|
+
* signals renew exactly the caller's lane (renewing all lanes would keep
|
|
691
|
+
* an unrelated stalled fork's lane leased forever).
|
|
692
|
+
*/
|
|
693
|
+
private sessionActiveLanes = new Map<string, string>();
|
|
694
|
+
|
|
695
|
+
noteSessionLane(sessionKey: string, laneKey: string | null): void {
|
|
696
|
+
if (laneKey == null) this.sessionActiveLanes.delete(sessionKey);
|
|
697
|
+
else this.sessionActiveLanes.set(sessionKey, laneKey);
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
/**
|
|
701
|
+
* External runtime-activity signal for adapters whose tool activity does
|
|
702
|
+
* not flow through the RuntimeEvent stream (openclaw hooks call this from
|
|
703
|
+
* the tool-call lifecycle): renews the session's OWN active ledger lane so
|
|
704
|
+
* a long tool call cannot outlive the lease and get dethroned mid-turn.
|
|
705
|
+
* No-op without an active ledger lane for the session.
|
|
706
|
+
*/
|
|
707
|
+
touchRuntimeActivity(sessionKey: string): void {
|
|
708
|
+
if (this.ledgerDisabled) return;
|
|
709
|
+
const laneKey = this.sessionActiveLanes.get(sessionKey);
|
|
710
|
+
if (laneKey) this.laneLedger?.renewByKey(laneKey);
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
/** Sessions whose in-flight turn surfaced a runtime error event. */
|
|
714
|
+
private turnErrorSessions = new Set<string>();
|
|
715
|
+
|
|
716
|
+
/**
|
|
717
|
+
* Consume (read-and-clear) the error marker for sessionKey's last turn.
|
|
718
|
+
* Feeds complete's turn_outcome so an error turn's lane members are
|
|
719
|
+
* released for retry instead of no_action-swept (design §3). Consuming
|
|
720
|
+
* (rather than peeking) keeps one-shot fork session keys from accumulating
|
|
721
|
+
* in the set forever.
|
|
722
|
+
*/
|
|
723
|
+
consumeTurnError(sessionKey: string): boolean {
|
|
724
|
+
return this.turnErrorSessions.delete(sessionKey);
|
|
725
|
+
}
|
|
726
|
+
|
|
644
727
|
private async emitDispatchReceived(event: ParallEvent): Promise<void> {
|
|
645
728
|
const sourceType = event.ackSourceType ?? (event.type === 'task' ? 'task_activity' : 'message');
|
|
646
729
|
const sourceId = event.ackSourceId ?? event.messageId;
|
|
@@ -650,6 +733,26 @@ export class ParallAgentGateway {
|
|
|
650
733
|
});
|
|
651
734
|
}
|
|
652
735
|
|
|
736
|
+
/**
|
|
737
|
+
* Sticky: the server answered a by-id complete with 400 (predates the
|
|
738
|
+
* form). Typed resolution falls back to the legacy ack for the rest of
|
|
739
|
+
* the process lifetime.
|
|
740
|
+
*/
|
|
741
|
+
typedByIdCompleteUnsupported = false;
|
|
742
|
+
|
|
743
|
+
// Typed-face ledger helpers live in gateway-lane-flow.ts; thin delegates
|
|
744
|
+
// keep the site code and tests on the class surface.
|
|
745
|
+
private typedLedgerEventIds(events: ParallEvent[]): string[] | null {
|
|
746
|
+
return typedLedgerEventIds(this.laneFlowHost(), events);
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
private resolveDispatchByID(
|
|
750
|
+
dispatchEventId: string,
|
|
751
|
+
lane?: string,
|
|
752
|
+
): ReturnType<typeof resolveDispatchByID> {
|
|
753
|
+
return resolveDispatchByID(this.laneFlowHost(), dispatchEventId, lane);
|
|
754
|
+
}
|
|
755
|
+
|
|
653
756
|
/** True when this event's lifecycle is owned by the dispatch lane ledger. */
|
|
654
757
|
private usesLaneLedger(event: ParallEvent): boolean {
|
|
655
758
|
return this.laneLedger != null && !this.ledgerDisabled && this.laneLedger.handles(event);
|
|
@@ -693,24 +796,25 @@ export class ParallAgentGateway {
|
|
|
693
796
|
earlier: ParallEvent[];
|
|
694
797
|
captureText?: string[];
|
|
695
798
|
hasMoreLocal: () => boolean;
|
|
696
|
-
}): Promise<'dispatched' | 'foreign' | 'shutdown'> {
|
|
799
|
+
}): Promise<'dispatched' | 'foreign' | 'shutdown' | 'failed'> {
|
|
697
800
|
return dispatchLaneGroup(this.laneFlowHost(), opts);
|
|
698
801
|
}
|
|
699
802
|
|
|
700
803
|
private consumeTypedDispatch(
|
|
701
804
|
ref: { dispatchEventId?: string; sourceType?: string; sourceId?: string },
|
|
702
805
|
run: (dispatchEventId?: string) => Promise<boolean>,
|
|
703
|
-
|
|
806
|
+
hooks: TypedConsumeHooks,
|
|
704
807
|
): Promise<void> {
|
|
705
|
-
return consumeTypedDispatch(this.laneFlowHost(), ref, run,
|
|
808
|
+
return consumeTypedDispatch(this.laneFlowHost(), ref, run, hooks);
|
|
706
809
|
}
|
|
707
810
|
|
|
708
|
-
//
|
|
709
|
-
//
|
|
710
|
-
//
|
|
711
|
-
//
|
|
712
|
-
//
|
|
713
|
-
//
|
|
811
|
+
// Legacy administrative ack (ledger-disabled fallback only). Typed
|
|
812
|
+
// completion must wait until the ack has either committed or failed.
|
|
813
|
+
// Errors stay best-effort: a failed ack leaves the row received, so
|
|
814
|
+
// Complete releases and re-drives it safely. The boolean outcome feeds the
|
|
815
|
+
// typed-consume backoff — an ack that failed must count as a failed
|
|
816
|
+
// consume, or an ack outage would clear the backoff entry and let the
|
|
817
|
+
// release re-drive spin at wire speed.
|
|
714
818
|
private ackDispatchEvent(dispatchEventId: string, onFailure?: () => void): Promise<boolean> {
|
|
715
819
|
return this.opts.client.ackDispatchByID(this.opts.config.org_id, dispatchEventId).then(
|
|
716
820
|
() => true,
|
|
@@ -727,6 +831,9 @@ export class ParallAgentGateway {
|
|
|
727
831
|
);
|
|
728
832
|
}
|
|
729
833
|
|
|
834
|
+
// PARITY: this switch and gateway-lane-flow's clearTypedDedupeForEvent must
|
|
835
|
+
// handle the same typed source families — extend BOTH when adding a typed
|
|
836
|
+
// event type (same dedupe entries, keyed from different event shapes).
|
|
730
837
|
private clearTypedDispatchDedupe(item: DispatchNewData): void {
|
|
731
838
|
switch (item.event_type) {
|
|
732
839
|
case 'task_assign':
|
|
@@ -1137,6 +1244,7 @@ export class ParallAgentGateway {
|
|
|
1137
1244
|
}
|
|
1138
1245
|
|
|
1139
1246
|
resetDispatchMetrics(sessionKey);
|
|
1247
|
+
this.turnErrorSessions.delete(sessionKey);
|
|
1140
1248
|
return runWithSessionKey(sessionKey, async () => {
|
|
1141
1249
|
let dispatchSpan: ReturnType<typeof startDispatchSpan> = null;
|
|
1142
1250
|
|
|
@@ -1299,6 +1407,9 @@ export class ParallAgentGateway {
|
|
|
1299
1407
|
) {
|
|
1300
1408
|
recordMessageSend(sessionKey, !runtimeEvent.error);
|
|
1301
1409
|
}
|
|
1410
|
+
if (runtimeEvent.type === 'error') {
|
|
1411
|
+
this.turnErrorSessions.add(sessionKey);
|
|
1412
|
+
}
|
|
1302
1413
|
await this.createRuntimeStep(
|
|
1303
1414
|
binding.agentSessionId,
|
|
1304
1415
|
event,
|
|
@@ -1495,13 +1606,29 @@ export class ParallAgentGateway {
|
|
|
1495
1606
|
body: buildForkScopePrefix(last) + buildEventBody(last),
|
|
1496
1607
|
earlier,
|
|
1497
1608
|
captureText: batchText,
|
|
1498
|
-
|
|
1609
|
+
// Per-LANE residue check (parity with the main-buffer path):
|
|
1610
|
+
// the fork queue can hold several lanes (channel + thread of
|
|
1611
|
+
// the same chat). A whole-queue check would defer THIS lane's
|
|
1612
|
+
// complete behind another lane's items and never revisit it —
|
|
1613
|
+
// its members would sit received until lease expiry.
|
|
1614
|
+
hasMoreLocal: () =>
|
|
1615
|
+
fork.queue.some(
|
|
1616
|
+
(it) => this.dispatchGroupKey(it.event) === this.dispatchGroupKey(last),
|
|
1617
|
+
),
|
|
1499
1618
|
});
|
|
1500
1619
|
if (outcome === 'foreign') {
|
|
1501
1620
|
// Another pod owns the lane — the events stay pending server-side.
|
|
1502
1621
|
for (const item of items) item.resolve(false);
|
|
1503
1622
|
break;
|
|
1504
1623
|
}
|
|
1624
|
+
if (outcome === 'failed') {
|
|
1625
|
+
// Error turn: complete(error) already released the members for
|
|
1626
|
+
// redelivery. Do NOT push them to processedEvents — the redrive
|
|
1627
|
+
// must not arrive wearing an "already handled" prefix. Keep
|
|
1628
|
+
// draining; the released work rejoins the next claim.
|
|
1629
|
+
for (const item of items) item.resolve(false);
|
|
1630
|
+
continue;
|
|
1631
|
+
}
|
|
1505
1632
|
dispatched = outcome === 'dispatched';
|
|
1506
1633
|
} else {
|
|
1507
1634
|
dispatched = await this.runDispatch(
|
|
@@ -1511,6 +1638,22 @@ export class ParallAgentGateway {
|
|
|
1511
1638
|
earlier,
|
|
1512
1639
|
batchText,
|
|
1513
1640
|
);
|
|
1641
|
+
if (
|
|
1642
|
+
dispatched &&
|
|
1643
|
+
this.typedLedgerEventIds(events) &&
|
|
1644
|
+
this.consumeTurnError(fork.fork.sessionKey)
|
|
1645
|
+
) {
|
|
1646
|
+
// Typed error turn in a fork: report failure to the awaiting
|
|
1647
|
+
// typed consumes so the members release for a budgeted retry,
|
|
1648
|
+
// and do NOT record the events as handled — the redrive must
|
|
1649
|
+
// not arrive wearing an "already handled" prefix. Keep draining
|
|
1650
|
+
// (an error turn is not a shutdown).
|
|
1651
|
+
this.opts.log?.info(
|
|
1652
|
+
`typed fork turn for ${last.messageId} surfaced a runtime error — releasing for retry`,
|
|
1653
|
+
);
|
|
1654
|
+
for (const item of items) item.resolve(false);
|
|
1655
|
+
continue;
|
|
1656
|
+
}
|
|
1514
1657
|
}
|
|
1515
1658
|
if (!dispatched) {
|
|
1516
1659
|
// Shutdown short-circuit — resolve un-acked so the server requeues
|
|
@@ -1542,6 +1685,10 @@ export class ParallAgentGateway {
|
|
|
1542
1685
|
remaining.resolve(false);
|
|
1543
1686
|
}
|
|
1544
1687
|
} finally {
|
|
1688
|
+
// One-shot fork keys are never dispatched again — drop any error marker
|
|
1689
|
+
// the legacy (non-ledger) path left unconsumed, or the set grows with
|
|
1690
|
+
// every failed ephemeral fork.
|
|
1691
|
+
this.turnErrorSessions.delete(fork.fork.sessionKey);
|
|
1545
1692
|
if (fork.deadlineTimer) {
|
|
1546
1693
|
clearTimeout(fork.deadlineTimer);
|
|
1547
1694
|
fork.deadlineTimer = null;
|
|
@@ -1654,7 +1801,7 @@ export class ParallAgentGateway {
|
|
|
1654
1801
|
this.opts.runtimeKey,
|
|
1655
1802
|
);
|
|
1656
1803
|
if (this.usesLaneLedger(event)) {
|
|
1657
|
-
let outcome: 'dispatched' | 'foreign' | 'shutdown';
|
|
1804
|
+
let outcome: 'dispatched' | 'foreign' | 'shutdown' | 'failed';
|
|
1658
1805
|
try {
|
|
1659
1806
|
outcome = await this.dispatchLaneGroup({
|
|
1660
1807
|
events,
|
|
@@ -1681,19 +1828,33 @@ export class ParallAgentGateway {
|
|
|
1681
1828
|
this.dispatchState.pendingForkResults.unshift(...pendingFork);
|
|
1682
1829
|
continue;
|
|
1683
1830
|
}
|
|
1831
|
+
if (outcome === 'failed') {
|
|
1832
|
+
// Error turn settled with complete(error) — members released for
|
|
1833
|
+
// redelivery. The fork results were not consumed by the failed
|
|
1834
|
+
// turn; keep them for the next real dispatch.
|
|
1835
|
+
this.dispatchState.pendingForkResults.unshift(...pendingFork);
|
|
1836
|
+
continue;
|
|
1837
|
+
}
|
|
1684
1838
|
// Dispatched — resolution happened server-side (reply cover or
|
|
1685
1839
|
// no_action sweep); no legacy acks.
|
|
1686
1840
|
continue;
|
|
1687
1841
|
}
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1842
|
+
// Buffered typed events are the one wrapper-less dispatch path: their
|
|
1843
|
+
// consumeTypedDispatch guard returned long ago (buffer-main resolves
|
|
1844
|
+
// false) and released the claims, so THIS site owns their resolution.
|
|
1845
|
+
// Message groups here are the ledger-disabled legacy flow.
|
|
1846
|
+
const typedRefs = this.typedLedgerEventIds(events);
|
|
1847
|
+
if (!typedRefs) {
|
|
1848
|
+
try {
|
|
1849
|
+
await this.emitDispatchReceived(event);
|
|
1850
|
+
} catch (err) {
|
|
1851
|
+
this.opts.log?.warn?.(
|
|
1852
|
+
`mark-received failed for buffered dispatch, leaving unacked for retry: ${String(err)}`,
|
|
1853
|
+
);
|
|
1854
|
+
this.dispatchState.mainBuffer.unshift(...events);
|
|
1855
|
+
this.dispatchState.pendingForkResults.unshift(...pendingFork);
|
|
1856
|
+
break;
|
|
1857
|
+
}
|
|
1697
1858
|
}
|
|
1698
1859
|
const dispatched = await this.runDispatch(
|
|
1699
1860
|
event,
|
|
@@ -1709,6 +1870,18 @@ export class ParallAgentGateway {
|
|
|
1709
1870
|
this.dispatchState.pendingForkResults.unshift(...pendingFork);
|
|
1710
1871
|
break;
|
|
1711
1872
|
}
|
|
1873
|
+
if (typedRefs) {
|
|
1874
|
+
// Wrapper-less resolution of the buffered typed group — protocol
|
|
1875
|
+
// lives in gateway-lane-flow.ts. The turn-error marker is consumed
|
|
1876
|
+
// HERE, before this loop can start another turn on the session.
|
|
1877
|
+
await settleDrainedTypedGroup(
|
|
1878
|
+
this.laneFlowHost(),
|
|
1879
|
+
events,
|
|
1880
|
+
typedRefs,
|
|
1881
|
+
this.consumeTurnError(this.opts.runtimeKey),
|
|
1882
|
+
);
|
|
1883
|
+
continue;
|
|
1884
|
+
}
|
|
1712
1885
|
for (const bufferedEvent of events) {
|
|
1713
1886
|
const sourceType =
|
|
1714
1887
|
bufferedEvent.ackSourceType ??
|
|
@@ -1769,7 +1942,7 @@ export class ParallAgentGateway {
|
|
|
1769
1942
|
if (this.usesLaneLedger(event)) {
|
|
1770
1943
|
// Ledger flow: claim replaces mark-received; complete/reply replace
|
|
1771
1944
|
// acks. A foreign incumbent leaves the event pending for re-drive.
|
|
1772
|
-
let outcome: 'dispatched' | 'foreign' | 'shutdown' = 'shutdown';
|
|
1945
|
+
let outcome: 'dispatched' | 'foreign' | 'shutdown' | 'failed' = 'shutdown';
|
|
1773
1946
|
try {
|
|
1774
1947
|
try {
|
|
1775
1948
|
outcome = await this.dispatchLaneGroup({
|
|
@@ -1796,16 +1969,24 @@ export class ParallAgentGateway {
|
|
|
1796
1969
|
}
|
|
1797
1970
|
return outcome === 'dispatched';
|
|
1798
1971
|
}
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1972
|
+
// Ledger-claimed typed events skip the legacy received write — the
|
|
1973
|
+
// claim already marked the row received, and a second mark here would
|
|
1974
|
+
// wipe its lease owner (the 0713 black-hole shape on the typed face).
|
|
1975
|
+
const typedRefs = this.typedLedgerEventIds([event]);
|
|
1976
|
+
if (!typedRefs) {
|
|
1977
|
+
try {
|
|
1978
|
+
await this.emitDispatchReceived(event);
|
|
1979
|
+
} catch (err) {
|
|
1980
|
+
this.opts.log?.warn?.(
|
|
1981
|
+
`mark-received failed, leaving unacked for retry: ${String(err)}`,
|
|
1982
|
+
);
|
|
1983
|
+
this.dispatchState.mainDispatching = false;
|
|
1984
|
+
this.dispatchState.mainCurrentTargetId = undefined;
|
|
1985
|
+
this.mainCurrentGroupKey = undefined;
|
|
1986
|
+
this.dispatchState.mainPreDispatchBranchPoint = undefined;
|
|
1987
|
+
this.dispatchState.pendingForkResults.unshift(...pendingFork);
|
|
1988
|
+
return false;
|
|
1989
|
+
}
|
|
1809
1990
|
}
|
|
1810
1991
|
let dispatched = false;
|
|
1811
1992
|
try {
|
|
@@ -1814,6 +1995,17 @@ export class ParallAgentGateway {
|
|
|
1814
1995
|
this.opts.runtimeKey,
|
|
1815
1996
|
forkPrefix + buildEventBody(event),
|
|
1816
1997
|
);
|
|
1998
|
+
if (dispatched && typedRefs && this.consumeTurnError(this.opts.runtimeKey)) {
|
|
1999
|
+
// Typed error turn: report failure to the enclosing typed consume
|
|
2000
|
+
// so the member releases for a budgeted retry instead of being
|
|
2001
|
+
// terminally resolved (tech-debt: typed-dispatch-error-outcome).
|
|
2002
|
+
// Consumed HERE, before the finally's drain can start another
|
|
2003
|
+
// turn on this session and clear the marker.
|
|
2004
|
+
this.opts.log?.info(
|
|
2005
|
+
`typed dispatch turn for ${event.messageId} surfaced a runtime error — releasing for retry`,
|
|
2006
|
+
);
|
|
2007
|
+
dispatched = false;
|
|
2008
|
+
}
|
|
1817
2009
|
if (!dispatched) {
|
|
1818
2010
|
// Shutdown short-circuit — restore the fork results so a future
|
|
1819
2011
|
// pod can replay them, and return false so handleMessage skips ack.
|
|
@@ -1840,10 +2032,17 @@ export class ParallAgentGateway {
|
|
|
1840
2032
|
// fold leaves the event buffered — the drain claims it as its own
|
|
1841
2033
|
// turn. Injection requires an exact lane match (same chat AND same
|
|
1842
2034
|
// thread) — a thread message never rides a channel turn.
|
|
2035
|
+
// The adapter-capability check comes BEFORE the fold: on a
|
|
2036
|
+
// buffer-only adapter (openclaw — no enqueueDuringDispatch) a
|
|
2037
|
+
// folded-but-never-injected member would be broad-covered by the
|
|
2038
|
+
// current turn's reply without the model ever seeing it. Leaving
|
|
2039
|
+
// the event un-folded keeps it buffered; the drain claims it as
|
|
2040
|
+
// its own turn and folds it there.
|
|
1843
2041
|
if (
|
|
1844
2042
|
this.mainCurrentGroupKey === this.dispatchGroupKey(event) &&
|
|
2043
|
+
this.opts.dispatchAdapter.enqueueDuringDispatch != null &&
|
|
1845
2044
|
(await this.laneLedger?.steerLive(event)) &&
|
|
1846
|
-
(await this.opts.dispatchAdapter.enqueueDuringDispatch
|
|
2045
|
+
(await this.opts.dispatchAdapter.enqueueDuringDispatch(
|
|
1847
2046
|
this.opts.runtimeKey,
|
|
1848
2047
|
buildEventBody(event),
|
|
1849
2048
|
))
|
|
@@ -1892,14 +2091,22 @@ export class ParallAgentGateway {
|
|
|
1892
2091
|
return false;
|
|
1893
2092
|
}
|
|
1894
2093
|
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
2094
|
+
// Ledger-managed events must never touch the legacy received surface
|
|
2095
|
+
// — the fork's lane claim marks them received. A mark-received here
|
|
2096
|
+
// outruns the claim, strands the row ownerless, and the chat goes
|
|
2097
|
+
// silent (the 0713 black hole; dispatch-convergence-design.md §6).
|
|
2098
|
+
// Same rule for ledger-claimed typed events: their dsp-lane claim
|
|
2099
|
+
// owns received-ness, and a re-mark would wipe the lease owner.
|
|
2100
|
+
if (!this.usesLaneLedger(event) && !this.typedLedgerEventIds([event])) {
|
|
2101
|
+
try {
|
|
2102
|
+
await this.emitDispatchReceived(event);
|
|
2103
|
+
} catch (err) {
|
|
2104
|
+
this.opts.log?.warn?.(
|
|
2105
|
+
`mark-received failed for fork dispatch, leaving unacked for retry: ${String(err)}`,
|
|
2106
|
+
);
|
|
2107
|
+
this.dispatchState.mainBuffer.push(event);
|
|
2108
|
+
return false;
|
|
2109
|
+
}
|
|
1903
2110
|
}
|
|
1904
2111
|
const fork = await this.opts.dispatchAdapter.forkSession({
|
|
1905
2112
|
sessionKey: this.opts.runtimeKey,
|
|
@@ -2101,10 +2308,13 @@ export class ParallAgentGateway {
|
|
|
2101
2308
|
this.handleTaskDispatch(item.task_id ?? '', item.source_id ?? item.task_id ?? '', {
|
|
2102
2309
|
dispatchEventId,
|
|
2103
2310
|
}),
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
this.
|
|
2107
|
-
|
|
2311
|
+
{
|
|
2312
|
+
legacyAck: (dispatchEventId) =>
|
|
2313
|
+
this.ackDispatchEvent(dispatchEventId ?? item.id, () =>
|
|
2314
|
+
this.clearTypedDispatchDedupe(item),
|
|
2315
|
+
),
|
|
2316
|
+
clearDedupe: () => this.clearTypedDispatchDedupe(item),
|
|
2317
|
+
},
|
|
2108
2318
|
);
|
|
2109
2319
|
}
|
|
2110
2320
|
|
|
@@ -2187,6 +2397,7 @@ export class ParallAgentGateway {
|
|
|
2187
2397
|
taskId: string,
|
|
2188
2398
|
actorId: string | null,
|
|
2189
2399
|
deliveryReason?: DispatchDeliveryReason | null,
|
|
2400
|
+
dispatchEventId?: string,
|
|
2190
2401
|
): Promise<boolean> {
|
|
2191
2402
|
if (this.shuttingDown) return false; // drain window — let server requeue via catch-up
|
|
2192
2403
|
const dedupeKey = `comment:${commentId}`;
|
|
@@ -2251,6 +2462,7 @@ export class ParallAgentGateway {
|
|
|
2251
2462
|
deliveryReason: deliveryReason ?? undefined,
|
|
2252
2463
|
ackSourceType: 'comment',
|
|
2253
2464
|
ackSourceId: commentId,
|
|
2465
|
+
dispatchEventId,
|
|
2254
2466
|
};
|
|
2255
2467
|
|
|
2256
2468
|
let dispatched: boolean;
|
|
@@ -2271,6 +2483,7 @@ export class ParallAgentGateway {
|
|
|
2271
2483
|
commentId: string,
|
|
2272
2484
|
actorId: string | null,
|
|
2273
2485
|
deliveryReason?: DispatchDeliveryReason | null,
|
|
2486
|
+
dispatchEventId?: string,
|
|
2274
2487
|
): Promise<boolean> {
|
|
2275
2488
|
if (this.shuttingDown) return false; // drain window — let server requeue via catch-up
|
|
2276
2489
|
// Shares the comment dedupe namespace with handleTaskComment; comment IDs
|
|
@@ -2326,6 +2539,7 @@ export class ParallAgentGateway {
|
|
|
2326
2539
|
replyTargetUri: comment.target_uri,
|
|
2327
2540
|
ackSourceType: 'comment',
|
|
2328
2541
|
ackSourceId: commentId,
|
|
2542
|
+
dispatchEventId,
|
|
2329
2543
|
};
|
|
2330
2544
|
|
|
2331
2545
|
let dispatched: boolean;
|
|
@@ -2353,6 +2567,7 @@ export class ParallAgentGateway {
|
|
|
2353
2567
|
private async fetchAndHandleScheduleFire(
|
|
2354
2568
|
runId: string,
|
|
2355
2569
|
actorId: string | null,
|
|
2570
|
+
dispatchEventId?: string,
|
|
2356
2571
|
): Promise<boolean> {
|
|
2357
2572
|
let run: ScheduleRun | null = null;
|
|
2358
2573
|
try {
|
|
@@ -2374,10 +2589,14 @@ export class ParallAgentGateway {
|
|
|
2374
2589
|
return false;
|
|
2375
2590
|
}
|
|
2376
2591
|
if (!run) return true;
|
|
2377
|
-
return this.handleScheduleFire(run, actorId);
|
|
2592
|
+
return this.handleScheduleFire(run, actorId, dispatchEventId);
|
|
2378
2593
|
}
|
|
2379
2594
|
|
|
2380
|
-
private async handleScheduleFire(
|
|
2595
|
+
private async handleScheduleFire(
|
|
2596
|
+
run: ScheduleRun,
|
|
2597
|
+
actorId: string | null,
|
|
2598
|
+
dispatchEventId?: string,
|
|
2599
|
+
): Promise<boolean> {
|
|
2381
2600
|
if (this.shuttingDown) return false; // drain window — let server requeue via catch-up
|
|
2382
2601
|
const dedupeKey = `schedule_run:${run.id}`;
|
|
2383
2602
|
if (this.dispatchedTasks.has(dedupeKey)) return false;
|
|
@@ -2400,6 +2619,7 @@ export class ParallAgentGateway {
|
|
|
2400
2619
|
attachedUri: run.fired_attached_uri ?? undefined,
|
|
2401
2620
|
ackSourceType: 'schedule_run',
|
|
2402
2621
|
ackSourceId: run.id,
|
|
2622
|
+
dispatchEventId,
|
|
2403
2623
|
};
|
|
2404
2624
|
|
|
2405
2625
|
let dispatched: boolean;
|
|
@@ -2415,7 +2635,10 @@ export class ParallAgentGateway {
|
|
|
2415
2635
|
return dispatched;
|
|
2416
2636
|
}
|
|
2417
2637
|
|
|
2418
|
-
private async fetchAndHandleExternalTriggerRun(
|
|
2638
|
+
private async fetchAndHandleExternalTriggerRun(
|
|
2639
|
+
runId: string,
|
|
2640
|
+
dispatchEventId?: string,
|
|
2641
|
+
): Promise<boolean> {
|
|
2419
2642
|
let run: ExternalTriggerRun | null = null;
|
|
2420
2643
|
try {
|
|
2421
2644
|
run = await this.opts.client.getExternalTriggerRun(this.opts.config.org_id, runId);
|
|
@@ -2433,7 +2656,7 @@ export class ParallAgentGateway {
|
|
|
2433
2656
|
return false;
|
|
2434
2657
|
}
|
|
2435
2658
|
if (!run) return true;
|
|
2436
|
-
return this.handleExternalTriggerRun(run);
|
|
2659
|
+
return this.handleExternalTriggerRun(run, dispatchEventId);
|
|
2437
2660
|
}
|
|
2438
2661
|
|
|
2439
2662
|
// fetchAndHandleChannelMessage resolves a channel_message dispatch to its
|
|
@@ -2441,7 +2664,10 @@ export class ParallAgentGateway {
|
|
|
2441
2664
|
// pipeline. targetId = the ChannelConversation id, so per-conversation
|
|
2442
2665
|
// multi-turn continuity rides the same per-target session mechanics as
|
|
2443
2666
|
// chats. Design: docs/engineering-design/external-im-channel-design.md.
|
|
2444
|
-
private async fetchAndHandleChannelMessage(
|
|
2667
|
+
private async fetchAndHandleChannelMessage(
|
|
2668
|
+
messageId: string,
|
|
2669
|
+
dispatchEventId?: string,
|
|
2670
|
+
): Promise<boolean> {
|
|
2445
2671
|
if (this.shuttingDown) return false;
|
|
2446
2672
|
// Capped dedupe (the chat-message path, not the unbounded task set): a busy
|
|
2447
2673
|
// external IM conversation would otherwise retain one key per message ever
|
|
@@ -2525,6 +2751,7 @@ export class ParallAgentGateway {
|
|
|
2525
2751
|
channelCliCapable: cliCapable,
|
|
2526
2752
|
ackSourceType: 'channel_message',
|
|
2527
2753
|
ackSourceId: msg.id,
|
|
2754
|
+
dispatchEventId,
|
|
2528
2755
|
};
|
|
2529
2756
|
|
|
2530
2757
|
// Release the claim if the event isn't actually dispatched (or throws) so
|
|
@@ -2542,7 +2769,10 @@ export class ParallAgentGateway {
|
|
|
2542
2769
|
return dispatched;
|
|
2543
2770
|
}
|
|
2544
2771
|
|
|
2545
|
-
private async handleExternalTriggerRun(
|
|
2772
|
+
private async handleExternalTriggerRun(
|
|
2773
|
+
run: ExternalTriggerRun,
|
|
2774
|
+
dispatchEventId?: string,
|
|
2775
|
+
): Promise<boolean> {
|
|
2546
2776
|
if (this.shuttingDown) return false;
|
|
2547
2777
|
const dedupeKey = `external_trigger_run:${run.id}`;
|
|
2548
2778
|
if (this.dispatchedTasks.has(dedupeKey)) return false;
|
|
@@ -2570,6 +2800,7 @@ export class ParallAgentGateway {
|
|
|
2570
2800
|
attachedUri,
|
|
2571
2801
|
ackSourceType: 'external_trigger_run',
|
|
2572
2802
|
ackSourceId: run.id,
|
|
2803
|
+
dispatchEventId,
|
|
2573
2804
|
};
|
|
2574
2805
|
|
|
2575
2806
|
let dispatched: boolean;
|
|
@@ -2589,6 +2820,7 @@ export class ParallAgentGateway {
|
|
|
2589
2820
|
approvalId: string,
|
|
2590
2821
|
actorId: string | null,
|
|
2591
2822
|
chatId: string | null,
|
|
2823
|
+
dispatchEventId?: string,
|
|
2592
2824
|
): Promise<boolean> {
|
|
2593
2825
|
let approval: Approval | null = null;
|
|
2594
2826
|
try {
|
|
@@ -2625,6 +2857,11 @@ export class ParallAgentGateway {
|
|
|
2625
2857
|
senderName: 'approver',
|
|
2626
2858
|
messageId: approval.id,
|
|
2627
2859
|
body,
|
|
2860
|
+
// The WorkItem's real source pair. Without it the legacy fallback
|
|
2861
|
+
// guessed ('message', approval_id) — a pair no row matches.
|
|
2862
|
+
ackSourceType: 'approval',
|
|
2863
|
+
ackSourceId: approval.id,
|
|
2864
|
+
dispatchEventId,
|
|
2628
2865
|
};
|
|
2629
2866
|
|
|
2630
2867
|
let dispatched: boolean;
|
|
@@ -2683,7 +2920,24 @@ export class ParallAgentGateway {
|
|
|
2683
2920
|
|
|
2684
2921
|
if (overflowMode && processed >= CATCHUP_MAX) {
|
|
2685
2922
|
try {
|
|
2686
|
-
|
|
2923
|
+
// Administrative drop of the overflow backlog (summarized to the
|
|
2924
|
+
// agent instead of dispatched). Ledger path: by-id complete —
|
|
2925
|
+
// closes the pending row and refuses (stale) one a live lane
|
|
2926
|
+
// owns; the legacy ack stays as the ledger-disabled / pre-by-id
|
|
2927
|
+
// fallback.
|
|
2928
|
+
if (this.laneLedger && !this.ledgerDisabled && !this.typedByIdCompleteUnsupported) {
|
|
2929
|
+
const outcome = await this.resolveDispatchByID(item.id);
|
|
2930
|
+
if (outcome === 'unsupported') {
|
|
2931
|
+
this.typedByIdCompleteUnsupported = true;
|
|
2932
|
+
await this.opts.client.ackDispatchByID(this.opts.config.org_id, item.id);
|
|
2933
|
+
} else if (outcome === 'failed') {
|
|
2934
|
+
throw new Error('by-id complete failed');
|
|
2935
|
+
}
|
|
2936
|
+
// 'stale' counts as skipped: a live lane owns the row and its
|
|
2937
|
+
// turn will resolve it — nothing left for catch-up to do.
|
|
2938
|
+
} else {
|
|
2939
|
+
await this.opts.client.ackDispatchByID(this.opts.config.org_id, item.id);
|
|
2940
|
+
}
|
|
2687
2941
|
skipped++;
|
|
2688
2942
|
const key = item.event_type;
|
|
2689
2943
|
skippedByType.set(key, (skippedByType.get(key) ?? 0) + 1);
|
|
@@ -2695,8 +2949,11 @@ export class ParallAgentGateway {
|
|
|
2695
2949
|
|
|
2696
2950
|
processed++;
|
|
2697
2951
|
try {
|
|
2698
|
-
const
|
|
2699
|
-
|
|
2952
|
+
const typedHooks: TypedConsumeHooks = {
|
|
2953
|
+
legacyAck: () =>
|
|
2954
|
+
this.ackDispatchEvent(item.id, () => this.clearTypedDispatchDedupe(item)),
|
|
2955
|
+
clearDedupe: () => this.clearTypedDispatchDedupe(item),
|
|
2956
|
+
};
|
|
2700
2957
|
if (item.event_type === 'task_assign' && item.task_id) {
|
|
2701
2958
|
try {
|
|
2702
2959
|
await this.consumeTypedDispatch(
|
|
@@ -2709,7 +2966,7 @@ export class ParallAgentGateway {
|
|
|
2709
2966
|
dispatchEventId,
|
|
2710
2967
|
},
|
|
2711
2968
|
),
|
|
2712
|
-
|
|
2969
|
+
typedHooks,
|
|
2713
2970
|
);
|
|
2714
2971
|
} catch (err: unknown) {
|
|
2715
2972
|
this.opts.log?.warn(
|
|
@@ -2730,7 +2987,7 @@ export class ParallAgentGateway {
|
|
|
2730
2987
|
dispatchEventId,
|
|
2731
2988
|
},
|
|
2732
2989
|
),
|
|
2733
|
-
|
|
2990
|
+
typedHooks,
|
|
2734
2991
|
);
|
|
2735
2992
|
} catch (err: unknown) {
|
|
2736
2993
|
this.opts.log?.warn(
|
|
@@ -2741,49 +2998,60 @@ export class ParallAgentGateway {
|
|
|
2741
2998
|
} else if (item.event_type === 'task_comment' && item.source_id && item.task_id) {
|
|
2742
2999
|
await this.consumeTypedDispatch(
|
|
2743
3000
|
{ dispatchEventId: item.id },
|
|
2744
|
-
() =>
|
|
3001
|
+
(dispatchEventId) =>
|
|
2745
3002
|
this.handleTaskComment(
|
|
2746
3003
|
item.source_id,
|
|
2747
3004
|
item.task_id ?? '',
|
|
2748
3005
|
item.actor_id,
|
|
2749
3006
|
item.delivery_reason,
|
|
3007
|
+
dispatchEventId,
|
|
2750
3008
|
),
|
|
2751
|
-
|
|
3009
|
+
typedHooks,
|
|
2752
3010
|
);
|
|
2753
3011
|
} else if (item.event_type === 'wiki_comment' && item.source_id) {
|
|
2754
3012
|
await this.consumeTypedDispatch(
|
|
2755
3013
|
{ dispatchEventId: item.id },
|
|
2756
|
-
() =>
|
|
2757
|
-
|
|
3014
|
+
(dispatchEventId) =>
|
|
3015
|
+
this.handleWikiComment(
|
|
3016
|
+
item.source_id,
|
|
3017
|
+
item.actor_id,
|
|
3018
|
+
item.delivery_reason,
|
|
3019
|
+
dispatchEventId,
|
|
3020
|
+
),
|
|
3021
|
+
typedHooks,
|
|
2758
3022
|
);
|
|
2759
3023
|
} else if (item.event_type === 'schedule.fire' && item.source_id) {
|
|
2760
3024
|
await this.consumeTypedDispatch(
|
|
2761
3025
|
{ dispatchEventId: item.id },
|
|
2762
|
-
() =>
|
|
2763
|
-
|
|
3026
|
+
(dispatchEventId) =>
|
|
3027
|
+
this.fetchAndHandleScheduleFire(item.source_id, item.actor_id, dispatchEventId),
|
|
3028
|
+
typedHooks,
|
|
2764
3029
|
);
|
|
2765
3030
|
} else if (item.event_type === 'external_trigger' && item.source_id) {
|
|
2766
3031
|
await this.consumeTypedDispatch(
|
|
2767
3032
|
{ dispatchEventId: item.id },
|
|
2768
|
-
() =>
|
|
2769
|
-
|
|
3033
|
+
(dispatchEventId) =>
|
|
3034
|
+
this.fetchAndHandleExternalTriggerRun(item.source_id, dispatchEventId),
|
|
3035
|
+
typedHooks,
|
|
2770
3036
|
);
|
|
2771
3037
|
} else if (item.event_type === 'channel_message' && item.source_id) {
|
|
2772
3038
|
await this.consumeTypedDispatch(
|
|
2773
3039
|
{ dispatchEventId: item.id },
|
|
2774
|
-
() =>
|
|
2775
|
-
|
|
3040
|
+
(dispatchEventId) =>
|
|
3041
|
+
this.fetchAndHandleChannelMessage(item.source_id, dispatchEventId),
|
|
3042
|
+
typedHooks,
|
|
2776
3043
|
);
|
|
2777
3044
|
} else if (item.event_type === 'approval_decided' && item.source_id) {
|
|
2778
3045
|
await this.consumeTypedDispatch(
|
|
2779
3046
|
{ dispatchEventId: item.id },
|
|
2780
|
-
() =>
|
|
3047
|
+
(dispatchEventId) =>
|
|
2781
3048
|
this.fetchAndHandleApprovalDecided(
|
|
2782
3049
|
item.source_id,
|
|
2783
3050
|
item.actor_id,
|
|
2784
3051
|
item.chat_id ?? null,
|
|
3052
|
+
dispatchEventId,
|
|
2785
3053
|
),
|
|
2786
|
-
|
|
3054
|
+
typedHooks,
|
|
2787
3055
|
);
|
|
2788
3056
|
} else if (item.event_type === 'message' && item.source_id && item.chat_id) {
|
|
2789
3057
|
await this.consumeMessageWorkItem({
|