@parall/parall 1.44.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/index.bundle.mjs
CHANGED
|
@@ -26486,9 +26486,6 @@ function laneContextFilePath(contextDir, targetUri, threadRootId) {
|
|
|
26486
26486
|
return path.join(contextDir, `${laneKeyForTarget(targetUri, threadRootId)}.json`);
|
|
26487
26487
|
}
|
|
26488
26488
|
|
|
26489
|
-
// ../agent-core/dist/lane-ledger.js
|
|
26490
|
-
import * as fs from "node:fs";
|
|
26491
|
-
|
|
26492
26489
|
// ../sdk/dist/types.js
|
|
26493
26490
|
var MENTION_ALL_USER_ID = "all";
|
|
26494
26491
|
|
|
@@ -29100,6 +29097,7 @@ var ParallWs = class {
|
|
|
29100
29097
|
};
|
|
29101
29098
|
|
|
29102
29099
|
// ../agent-core/dist/lane-ledger.js
|
|
29100
|
+
import * as fs from "node:fs";
|
|
29103
29101
|
var LedgerUnsupportedError = class extends Error {
|
|
29104
29102
|
};
|
|
29105
29103
|
function isStaleLane(err) {
|
|
@@ -29383,6 +29381,20 @@ var LaneLedger = class {
|
|
|
29383
29381
|
this.lanes.set(lane.laneKey, lane);
|
|
29384
29382
|
return lane;
|
|
29385
29383
|
}
|
|
29384
|
+
/**
|
|
29385
|
+
* Drop a lane's local record without any server call — for a typed lane
|
|
29386
|
+
* whose member the by-id complete just resolved (the server dropped the
|
|
29387
|
+
* vacated lane row in the same transaction). Calling completeIfIdle
|
|
29388
|
+
* instead would fire a lane-form Complete at a lane that no longer exists
|
|
29389
|
+
* and burn an RPC on the guaranteed STALE_LANE answer.
|
|
29390
|
+
*/
|
|
29391
|
+
dropLocal(laneKey) {
|
|
29392
|
+
const lane = this.lanes.get(laneKey);
|
|
29393
|
+
if (!lane)
|
|
29394
|
+
return;
|
|
29395
|
+
this.lanes.delete(laneKey);
|
|
29396
|
+
this.removeLaneContext(lane);
|
|
29397
|
+
}
|
|
29386
29398
|
/**
|
|
29387
29399
|
* Remove the per-lane context file (and its CLI sidecar) when the lane
|
|
29388
29400
|
* ends. A leftover file would make a later cross-context send to the same
|
|
@@ -29462,10 +29474,104 @@ async function dispatchLaneGroup(host, opts) {
|
|
|
29462
29474
|
await ledger.completeIfIdle(lane.laneKey, pendingInjections || opts.hasMoreLocal());
|
|
29463
29475
|
return "dispatched";
|
|
29464
29476
|
}
|
|
29477
|
+
function typedLedgerEventIds(host, events) {
|
|
29478
|
+
if (!host.laneLedger || host.ledgerDisabled)
|
|
29479
|
+
return null;
|
|
29480
|
+
const ids = [];
|
|
29481
|
+
for (const ev of events) {
|
|
29482
|
+
if (!ev.dispatchEventId || host.usesLaneLedger(ev))
|
|
29483
|
+
return null;
|
|
29484
|
+
ids.push(ev.dispatchEventId);
|
|
29485
|
+
}
|
|
29486
|
+
return ids.length > 0 ? ids : null;
|
|
29487
|
+
}
|
|
29488
|
+
function isByIDCompleteUnsupported(err) {
|
|
29489
|
+
return err instanceof ApiError && err.status === 400;
|
|
29490
|
+
}
|
|
29491
|
+
async function resolveDispatchByID(host, dispatchEventId, lane) {
|
|
29492
|
+
try {
|
|
29493
|
+
await host.opts.client.completeDispatch(host.opts.config.org_id, {
|
|
29494
|
+
dispatch_event_id: dispatchEventId,
|
|
29495
|
+
...lane ? { lane } : {},
|
|
29496
|
+
turn_outcome: "ok"
|
|
29497
|
+
});
|
|
29498
|
+
return "ok";
|
|
29499
|
+
} catch (err) {
|
|
29500
|
+
if (err instanceof ApiError && err.status === 409)
|
|
29501
|
+
return "stale";
|
|
29502
|
+
if (isByIDCompleteUnsupported(err))
|
|
29503
|
+
return "unsupported";
|
|
29504
|
+
host.opts.log?.warn(`by-id complete failed for ${dispatchEventId} \u2014 leaving for re-drive: ${String(err)}`);
|
|
29505
|
+
return "failed";
|
|
29506
|
+
}
|
|
29507
|
+
}
|
|
29508
|
+
function clearTypedDedupeForEvent(host, event) {
|
|
29509
|
+
const sourceId = event.ackSourceId;
|
|
29510
|
+
if (!sourceId)
|
|
29511
|
+
return;
|
|
29512
|
+
switch (event.ackSourceType) {
|
|
29513
|
+
case "task_activity": {
|
|
29514
|
+
const prefix = `${event.targetId}:`;
|
|
29515
|
+
for (const key of host.dispatchedTasks) {
|
|
29516
|
+
if (key.startsWith(prefix))
|
|
29517
|
+
host.dispatchedTasks.delete(key);
|
|
29518
|
+
}
|
|
29519
|
+
break;
|
|
29520
|
+
}
|
|
29521
|
+
case "comment":
|
|
29522
|
+
host.dispatchedTasks.delete(`comment:${sourceId}`);
|
|
29523
|
+
break;
|
|
29524
|
+
case "schedule_run":
|
|
29525
|
+
host.dispatchedTasks.delete(`schedule_run:${sourceId}`);
|
|
29526
|
+
break;
|
|
29527
|
+
case "external_trigger_run":
|
|
29528
|
+
host.dispatchedTasks.delete(`external_trigger_run:${sourceId}`);
|
|
29529
|
+
break;
|
|
29530
|
+
case "channel_message":
|
|
29531
|
+
host.dispatchedMessages.delete(`channel_message:${sourceId}`);
|
|
29532
|
+
break;
|
|
29533
|
+
case "approval":
|
|
29534
|
+
host.dispatchedTasks.delete(`approval:${sourceId}`);
|
|
29535
|
+
break;
|
|
29536
|
+
}
|
|
29537
|
+
}
|
|
29538
|
+
async function settleDrainedTypedGroup(host, events, ids, turnErrored) {
|
|
29539
|
+
if (turnErrored) {
|
|
29540
|
+
host.opts.log?.info(`buffered typed turn for ${events[events.length - 1]?.messageId} surfaced a runtime error \u2014 leaving for re-drive`);
|
|
29541
|
+
for (const event of events)
|
|
29542
|
+
clearTypedDedupeForEvent(host, event);
|
|
29543
|
+
return;
|
|
29544
|
+
}
|
|
29545
|
+
const legacyAckFrom = async (start) => {
|
|
29546
|
+
for (const [j, id] of ids.slice(start).entries()) {
|
|
29547
|
+
try {
|
|
29548
|
+
await host.opts.client.ackDispatchByID(host.opts.config.org_id, id);
|
|
29549
|
+
} catch {
|
|
29550
|
+
clearTypedDedupeForEvent(host, events[start + j]);
|
|
29551
|
+
}
|
|
29552
|
+
}
|
|
29553
|
+
};
|
|
29554
|
+
if (host.typedByIdCompleteUnsupported) {
|
|
29555
|
+
await legacyAckFrom(0);
|
|
29556
|
+
return;
|
|
29557
|
+
}
|
|
29558
|
+
for (const [i, id] of ids.entries()) {
|
|
29559
|
+
const outcome = await resolveDispatchByID(host, id);
|
|
29560
|
+
if (outcome === "unsupported") {
|
|
29561
|
+
host.typedByIdCompleteUnsupported = true;
|
|
29562
|
+
host.opts.log?.warn("server predates the by-id dispatch complete \u2014 falling back to legacy typed acks");
|
|
29563
|
+
await legacyAckFrom(i);
|
|
29564
|
+
return;
|
|
29565
|
+
}
|
|
29566
|
+
if (outcome !== "ok") {
|
|
29567
|
+
clearTypedDedupeForEvent(host, events[i]);
|
|
29568
|
+
}
|
|
29569
|
+
}
|
|
29570
|
+
}
|
|
29465
29571
|
var TYPED_BACKOFF_BASE_MS = 2e3;
|
|
29466
29572
|
var TYPED_BACKOFF_CAP_MS = 5 * 6e4;
|
|
29467
29573
|
var TYPED_BACKOFF_MAP_CAP = 512;
|
|
29468
|
-
async function consumeTypedDispatch(host, ref, run,
|
|
29574
|
+
async function consumeTypedDispatch(host, ref, run, hooks) {
|
|
29469
29575
|
const backoffKey = ref.dispatchEventId ?? `${ref.sourceType}:${ref.sourceId}`;
|
|
29470
29576
|
const armed = host.typedRedriveBackoff.get(backoffKey);
|
|
29471
29577
|
if (armed) {
|
|
@@ -29481,8 +29587,8 @@ async function consumeTypedDispatch(host, ref, run, ack) {
|
|
|
29481
29587
|
return;
|
|
29482
29588
|
}
|
|
29483
29589
|
const settleAck = (ackResult) => ackResult !== false;
|
|
29484
|
-
const settle = (
|
|
29485
|
-
if (
|
|
29590
|
+
const settle = (resolved2) => {
|
|
29591
|
+
if (resolved2) {
|
|
29486
29592
|
host.typedRedriveBackoff.delete(backoffKey);
|
|
29487
29593
|
return;
|
|
29488
29594
|
}
|
|
@@ -29496,13 +29602,13 @@ async function consumeTypedDispatch(host, ref, run, ack) {
|
|
|
29496
29602
|
host.typedRedriveBackoff.set(backoffKey, { failures, until: Date.now() + backoffMs });
|
|
29497
29603
|
};
|
|
29498
29604
|
const runLegacy = async () => {
|
|
29499
|
-
let
|
|
29605
|
+
let acked = false;
|
|
29500
29606
|
try {
|
|
29501
29607
|
if (await run(ref.dispatchEventId)) {
|
|
29502
|
-
|
|
29608
|
+
acked = settleAck(await hooks.legacyAck(ref.dispatchEventId));
|
|
29503
29609
|
}
|
|
29504
29610
|
} finally {
|
|
29505
|
-
settle(
|
|
29611
|
+
settle(acked);
|
|
29506
29612
|
}
|
|
29507
29613
|
};
|
|
29508
29614
|
if (!host.laneLedger || host.ledgerDisabled) {
|
|
@@ -29524,15 +29630,39 @@ async function consumeTypedDispatch(host, ref, run, ack) {
|
|
|
29524
29630
|
host.opts.log?.info(`typed dispatch ${ref.dispatchEventId ?? `${ref.sourceType}:${ref.sourceId}`} not claimable (held elsewhere or already resolved) \u2014 skipping`);
|
|
29525
29631
|
return;
|
|
29526
29632
|
}
|
|
29527
|
-
let
|
|
29633
|
+
let resolved = false;
|
|
29634
|
+
let viaLegacyAck = false;
|
|
29528
29635
|
try {
|
|
29529
29636
|
if (await run(lane.typedDispatchEventId)) {
|
|
29530
|
-
|
|
29637
|
+
if (host.typedByIdCompleteUnsupported || !lane.typedDispatchEventId) {
|
|
29638
|
+
viaLegacyAck = true;
|
|
29639
|
+
resolved = settleAck(await hooks.legacyAck(lane.typedDispatchEventId));
|
|
29640
|
+
} else {
|
|
29641
|
+
const outcome = await resolveDispatchByID(host, lane.typedDispatchEventId, lane.lane);
|
|
29642
|
+
if (outcome === "unsupported") {
|
|
29643
|
+
host.typedByIdCompleteUnsupported = true;
|
|
29644
|
+
host.opts.log?.warn("server predates the by-id dispatch complete \u2014 falling back to the legacy typed ack");
|
|
29645
|
+
viaLegacyAck = true;
|
|
29646
|
+
resolved = settleAck(await hooks.legacyAck(lane.typedDispatchEventId));
|
|
29647
|
+
} else if (outcome === "stale") {
|
|
29648
|
+
hooks.clearDedupe?.();
|
|
29649
|
+
resolved = true;
|
|
29650
|
+
} else {
|
|
29651
|
+
resolved = outcome === "ok";
|
|
29652
|
+
if (!resolved) {
|
|
29653
|
+
hooks.clearDedupe?.();
|
|
29654
|
+
}
|
|
29655
|
+
}
|
|
29656
|
+
}
|
|
29531
29657
|
}
|
|
29532
29658
|
} finally {
|
|
29533
|
-
settle(
|
|
29534
|
-
|
|
29535
|
-
|
|
29659
|
+
settle(resolved);
|
|
29660
|
+
if (resolved && !viaLegacyAck) {
|
|
29661
|
+
host.laneLedger.dropLocal(lane.laneKey);
|
|
29662
|
+
} else {
|
|
29663
|
+
await host.laneLedger.completeIfIdle(lane.laneKey, false).catch(() => {
|
|
29664
|
+
});
|
|
29665
|
+
}
|
|
29536
29666
|
}
|
|
29537
29667
|
}
|
|
29538
29668
|
async function consumeMessageWorkItem(host, item) {
|
|
@@ -29541,6 +29671,16 @@ async function consumeMessageWorkItem(host, item) {
|
|
|
29541
29671
|
if (!host.tryClaimMessage(item.source_id))
|
|
29542
29672
|
return;
|
|
29543
29673
|
const ackItem = () => {
|
|
29674
|
+
if (host.laneLedger && !host.ledgerDisabled && !host.typedByIdCompleteUnsupported) {
|
|
29675
|
+
void resolveDispatchByID(host, item.id).then((outcome) => {
|
|
29676
|
+
if (outcome === "unsupported") {
|
|
29677
|
+
host.typedByIdCompleteUnsupported = true;
|
|
29678
|
+
host.opts.client.ackDispatchByID(host.opts.config.org_id, item.id).catch(() => {
|
|
29679
|
+
});
|
|
29680
|
+
}
|
|
29681
|
+
});
|
|
29682
|
+
return;
|
|
29683
|
+
}
|
|
29544
29684
|
host.opts.client.ackDispatchByID(host.opts.config.org_id, item.id).catch(() => {
|
|
29545
29685
|
});
|
|
29546
29686
|
};
|
|
@@ -30661,20 +30801,23 @@ var ParallAgentGateway = class {
|
|
|
30661
30801
|
if (data.status !== "todo" && data.status !== "in_progress")
|
|
30662
30802
|
return;
|
|
30663
30803
|
try {
|
|
30664
|
-
await this.consumeTypedDispatch(data.dispatch_event_id ? { dispatchEventId: data.dispatch_event_id } : { sourceType: "task_activity", sourceId: data.id }, (dispatchEventId) => this.handleTaskAssignment(data, data.id, dispatchEventId),
|
|
30665
|
-
|
|
30666
|
-
|
|
30804
|
+
await this.consumeTypedDispatch(data.dispatch_event_id ? { dispatchEventId: data.dispatch_event_id } : { sourceType: "task_activity", sourceId: data.id }, (dispatchEventId) => this.handleTaskAssignment(data, data.id, dispatchEventId), {
|
|
30805
|
+
legacyAck: (dispatchEventId) => {
|
|
30806
|
+
if (dispatchEventId) {
|
|
30807
|
+
return this.ackDispatchEvent(dispatchEventId, () => {
|
|
30808
|
+
this.dispatchedTasks.delete(`${data.id}:${data.updated_at}`);
|
|
30809
|
+
});
|
|
30810
|
+
}
|
|
30811
|
+
return this.opts.client.ackDispatch(this.opts.config.org_id, {
|
|
30812
|
+
source_type: "task_activity",
|
|
30813
|
+
source_id: data.id
|
|
30814
|
+
}).then(() => true, (err) => {
|
|
30667
30815
|
this.dispatchedTasks.delete(`${data.id}:${data.updated_at}`);
|
|
30816
|
+
this.opts.log?.warn(`dispatch ack failed for task ${data.id}, releasing for re-drive: ${String(err)}`);
|
|
30817
|
+
return false;
|
|
30668
30818
|
});
|
|
30669
|
-
}
|
|
30670
|
-
|
|
30671
|
-
source_type: "task_activity",
|
|
30672
|
-
source_id: data.id
|
|
30673
|
-
}).then(() => true, (err) => {
|
|
30674
|
-
this.dispatchedTasks.delete(`${data.id}:${data.updated_at}`);
|
|
30675
|
-
this.opts.log?.warn(`dispatch ack failed for task ${data.id}, releasing for re-drive: ${String(err)}`);
|
|
30676
|
-
return false;
|
|
30677
|
-
});
|
|
30819
|
+
},
|
|
30820
|
+
clearDedupe: () => this.dispatchedTasks.delete(`${data.id}:${data.updated_at}`)
|
|
30678
30821
|
});
|
|
30679
30822
|
} catch (err) {
|
|
30680
30823
|
this.opts.log?.error(`task dispatch failed for ${data.id}: ${String(err)}`);
|
|
@@ -30693,7 +30836,10 @@ var ParallAgentGateway = class {
|
|
|
30693
30836
|
if (!data.source_id || !data.task_id)
|
|
30694
30837
|
return;
|
|
30695
30838
|
try {
|
|
30696
|
-
await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.handleTaskComment(data.source_id, data.task_id ?? "", data.actor_id, data.delivery_reason
|
|
30839
|
+
await this.consumeTypedDispatch({ dispatchEventId: data.id }, (dispatchEventId) => this.handleTaskComment(data.source_id, data.task_id ?? "", data.actor_id, data.delivery_reason, dispatchEventId), {
|
|
30840
|
+
legacyAck: () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)),
|
|
30841
|
+
clearDedupe: () => this.clearTypedDispatchDedupe(data)
|
|
30842
|
+
});
|
|
30697
30843
|
} catch (err) {
|
|
30698
30844
|
this.opts.log?.error(`task comment dispatch failed for ${data.source_id}: ${String(err)}`);
|
|
30699
30845
|
}
|
|
@@ -30701,7 +30847,10 @@ var ParallAgentGateway = class {
|
|
|
30701
30847
|
if (!data.source_id)
|
|
30702
30848
|
return;
|
|
30703
30849
|
try {
|
|
30704
|
-
await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.handleWikiComment(data.source_id, data.actor_id, data.delivery_reason
|
|
30850
|
+
await this.consumeTypedDispatch({ dispatchEventId: data.id }, (dispatchEventId) => this.handleWikiComment(data.source_id, data.actor_id, data.delivery_reason, dispatchEventId), {
|
|
30851
|
+
legacyAck: () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)),
|
|
30852
|
+
clearDedupe: () => this.clearTypedDispatchDedupe(data)
|
|
30853
|
+
});
|
|
30705
30854
|
} catch (err) {
|
|
30706
30855
|
this.opts.log?.error(`wiki comment dispatch failed for ${data.source_id}: ${String(err)}`);
|
|
30707
30856
|
}
|
|
@@ -30712,7 +30861,10 @@ var ParallAgentGateway = class {
|
|
|
30712
30861
|
await this.consumeTypedDispatch({ dispatchEventId: data.id }, (dispatchEventId) => this.handleTaskDispatch(data.task_id ?? "", data.source_id ?? data.task_id ?? "", {
|
|
30713
30862
|
allowCreator: true,
|
|
30714
30863
|
dispatchEventId
|
|
30715
|
-
}),
|
|
30864
|
+
}), {
|
|
30865
|
+
legacyAck: () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)),
|
|
30866
|
+
clearDedupe: () => this.clearTypedDispatchDedupe(data)
|
|
30867
|
+
});
|
|
30716
30868
|
} catch (err) {
|
|
30717
30869
|
this.opts.log?.error(`task update dispatch failed for ${data.task_id}: ${String(err)}`);
|
|
30718
30870
|
}
|
|
@@ -30720,7 +30872,10 @@ var ParallAgentGateway = class {
|
|
|
30720
30872
|
if (!data.source_id)
|
|
30721
30873
|
return;
|
|
30722
30874
|
try {
|
|
30723
|
-
await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleScheduleFire(data.source_id, data.actor_id
|
|
30875
|
+
await this.consumeTypedDispatch({ dispatchEventId: data.id }, (dispatchEventId) => this.fetchAndHandleScheduleFire(data.source_id, data.actor_id, dispatchEventId), {
|
|
30876
|
+
legacyAck: () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)),
|
|
30877
|
+
clearDedupe: () => this.clearTypedDispatchDedupe(data)
|
|
30878
|
+
});
|
|
30724
30879
|
} catch (err) {
|
|
30725
30880
|
this.opts.log?.error(`schedule fire dispatch failed for ${data.source_id}: ${String(err)}`);
|
|
30726
30881
|
}
|
|
@@ -30728,7 +30883,10 @@ var ParallAgentGateway = class {
|
|
|
30728
30883
|
if (!data.source_id)
|
|
30729
30884
|
return;
|
|
30730
30885
|
try {
|
|
30731
|
-
await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleExternalTriggerRun(data.source_id
|
|
30886
|
+
await this.consumeTypedDispatch({ dispatchEventId: data.id }, (dispatchEventId) => this.fetchAndHandleExternalTriggerRun(data.source_id, dispatchEventId), {
|
|
30887
|
+
legacyAck: () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)),
|
|
30888
|
+
clearDedupe: () => this.clearTypedDispatchDedupe(data)
|
|
30889
|
+
});
|
|
30732
30890
|
} catch (err) {
|
|
30733
30891
|
this.opts.log?.error(`external trigger dispatch failed for ${data.source_id}: ${String(err)}`);
|
|
30734
30892
|
}
|
|
@@ -30736,7 +30894,10 @@ var ParallAgentGateway = class {
|
|
|
30736
30894
|
if (!data.source_id)
|
|
30737
30895
|
return;
|
|
30738
30896
|
try {
|
|
30739
|
-
await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleChannelMessage(data.source_id
|
|
30897
|
+
await this.consumeTypedDispatch({ dispatchEventId: data.id }, (dispatchEventId) => this.fetchAndHandleChannelMessage(data.source_id, dispatchEventId), {
|
|
30898
|
+
legacyAck: () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)),
|
|
30899
|
+
clearDedupe: () => this.clearTypedDispatchDedupe(data)
|
|
30900
|
+
});
|
|
30740
30901
|
} catch (err) {
|
|
30741
30902
|
this.opts.log?.error(`channel message dispatch failed for ${data.source_id}: ${String(err)}`);
|
|
30742
30903
|
}
|
|
@@ -30744,7 +30905,10 @@ var ParallAgentGateway = class {
|
|
|
30744
30905
|
if (!data.source_id)
|
|
30745
30906
|
return;
|
|
30746
30907
|
try {
|
|
30747
|
-
await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleApprovalDecided(data.source_id, data.actor_id, data.chat_id ?? null
|
|
30908
|
+
await this.consumeTypedDispatch({ dispatchEventId: data.id }, (dispatchEventId) => this.fetchAndHandleApprovalDecided(data.source_id, data.actor_id, data.chat_id ?? null, dispatchEventId), {
|
|
30909
|
+
legacyAck: () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)),
|
|
30910
|
+
clearDedupe: () => this.clearTypedDispatchDedupe(data)
|
|
30911
|
+
});
|
|
30748
30912
|
} catch (err) {
|
|
30749
30913
|
this.opts.log?.error(`approval decided dispatch failed for ${data.source_id}: ${String(err)}`);
|
|
30750
30914
|
}
|
|
@@ -30827,6 +30991,20 @@ var ParallAgentGateway = class {
|
|
|
30827
30991
|
source_id: sourceId
|
|
30828
30992
|
});
|
|
30829
30993
|
}
|
|
30994
|
+
/**
|
|
30995
|
+
* Sticky: the server answered a by-id complete with 400 (predates the
|
|
30996
|
+
* form). Typed resolution falls back to the legacy ack for the rest of
|
|
30997
|
+
* the process lifetime.
|
|
30998
|
+
*/
|
|
30999
|
+
typedByIdCompleteUnsupported = false;
|
|
31000
|
+
// Typed-face ledger helpers live in gateway-lane-flow.ts; thin delegates
|
|
31001
|
+
// keep the site code and tests on the class surface.
|
|
31002
|
+
typedLedgerEventIds(events) {
|
|
31003
|
+
return typedLedgerEventIds(this.laneFlowHost(), events);
|
|
31004
|
+
}
|
|
31005
|
+
resolveDispatchByID(dispatchEventId, lane) {
|
|
31006
|
+
return resolveDispatchByID(this.laneFlowHost(), dispatchEventId, lane);
|
|
31007
|
+
}
|
|
30830
31008
|
/** True when this event's lifecycle is owned by the dispatch lane ledger. */
|
|
30831
31009
|
usesLaneLedger(event) {
|
|
30832
31010
|
return this.laneLedger != null && !this.ledgerDisabled && this.laneLedger.handles(event);
|
|
@@ -30857,15 +31035,16 @@ var ParallAgentGateway = class {
|
|
|
30857
31035
|
dispatchLaneGroup(opts) {
|
|
30858
31036
|
return dispatchLaneGroup(this.laneFlowHost(), opts);
|
|
30859
31037
|
}
|
|
30860
|
-
consumeTypedDispatch(ref, run,
|
|
30861
|
-
return consumeTypedDispatch(this.laneFlowHost(), ref, run,
|
|
31038
|
+
consumeTypedDispatch(ref, run, hooks) {
|
|
31039
|
+
return consumeTypedDispatch(this.laneFlowHost(), ref, run, hooks);
|
|
30862
31040
|
}
|
|
30863
|
-
//
|
|
30864
|
-
//
|
|
30865
|
-
//
|
|
30866
|
-
//
|
|
30867
|
-
//
|
|
30868
|
-
//
|
|
31041
|
+
// Legacy administrative ack (ledger-disabled fallback only). Typed
|
|
31042
|
+
// completion must wait until the ack has either committed or failed.
|
|
31043
|
+
// Errors stay best-effort: a failed ack leaves the row received, so
|
|
31044
|
+
// Complete releases and re-drives it safely. The boolean outcome feeds the
|
|
31045
|
+
// typed-consume backoff — an ack that failed must count as a failed
|
|
31046
|
+
// consume, or an ack outage would clear the backoff entry and let the
|
|
31047
|
+
// release re-drive spin at wire speed.
|
|
30869
31048
|
ackDispatchEvent(dispatchEventId, onFailure) {
|
|
30870
31049
|
return this.opts.client.ackDispatchByID(this.opts.config.org_id, dispatchEventId).then(() => true, (err) => {
|
|
30871
31050
|
onFailure?.();
|
|
@@ -30873,6 +31052,9 @@ var ParallAgentGateway = class {
|
|
|
30873
31052
|
return false;
|
|
30874
31053
|
});
|
|
30875
31054
|
}
|
|
31055
|
+
// PARITY: this switch and gateway-lane-flow's clearTypedDedupeForEvent must
|
|
31056
|
+
// handle the same typed source families — extend BOTH when adding a typed
|
|
31057
|
+
// event type (same dedupe entries, keyed from different event shapes).
|
|
30876
31058
|
clearTypedDispatchDedupe(item) {
|
|
30877
31059
|
switch (item.event_type) {
|
|
30878
31060
|
case "task_assign":
|
|
@@ -31446,6 +31628,12 @@ var ParallAgentGateway = class {
|
|
|
31446
31628
|
dispatched = outcome === "dispatched";
|
|
31447
31629
|
} else {
|
|
31448
31630
|
dispatched = await this.runDispatch(last, fork.fork.sessionKey, buildForkScopePrefix(last) + buildEventBody(last), earlier, batchText);
|
|
31631
|
+
if (dispatched && this.typedLedgerEventIds(events) && this.consumeTurnError(fork.fork.sessionKey)) {
|
|
31632
|
+
this.opts.log?.info(`typed fork turn for ${last.messageId} surfaced a runtime error \u2014 releasing for retry`);
|
|
31633
|
+
for (const item of items)
|
|
31634
|
+
item.resolve(false);
|
|
31635
|
+
continue;
|
|
31636
|
+
}
|
|
31449
31637
|
}
|
|
31450
31638
|
if (!dispatched) {
|
|
31451
31639
|
for (const item of items) {
|
|
@@ -31595,13 +31783,16 @@ var ParallAgentGateway = class {
|
|
|
31595
31783
|
}
|
|
31596
31784
|
continue;
|
|
31597
31785
|
}
|
|
31598
|
-
|
|
31599
|
-
|
|
31600
|
-
|
|
31601
|
-
|
|
31602
|
-
|
|
31603
|
-
|
|
31604
|
-
|
|
31786
|
+
const typedRefs = this.typedLedgerEventIds(events);
|
|
31787
|
+
if (!typedRefs) {
|
|
31788
|
+
try {
|
|
31789
|
+
await this.emitDispatchReceived(event);
|
|
31790
|
+
} catch (err) {
|
|
31791
|
+
this.opts.log?.warn?.(`mark-received failed for buffered dispatch, leaving unacked for retry: ${String(err)}`);
|
|
31792
|
+
this.dispatchState.mainBuffer.unshift(...events);
|
|
31793
|
+
this.dispatchState.pendingForkResults.unshift(...pendingFork);
|
|
31794
|
+
break;
|
|
31795
|
+
}
|
|
31605
31796
|
}
|
|
31606
31797
|
const dispatched = await this.runDispatch(event, this.opts.runtimeKey, forkPrefix + buildEventBody(event), earlier);
|
|
31607
31798
|
if (!dispatched) {
|
|
@@ -31609,6 +31800,10 @@ var ParallAgentGateway = class {
|
|
|
31609
31800
|
this.dispatchState.pendingForkResults.unshift(...pendingFork);
|
|
31610
31801
|
break;
|
|
31611
31802
|
}
|
|
31803
|
+
if (typedRefs) {
|
|
31804
|
+
await settleDrainedTypedGroup(this.laneFlowHost(), events, typedRefs, this.consumeTurnError(this.opts.runtimeKey));
|
|
31805
|
+
continue;
|
|
31806
|
+
}
|
|
31612
31807
|
for (const bufferedEvent of events) {
|
|
31613
31808
|
const sourceType = bufferedEvent.ackSourceType ?? (bufferedEvent.type === "task" ? "task_activity" : "message");
|
|
31614
31809
|
const sourceId = bufferedEvent.ackSourceId ?? bufferedEvent.messageId;
|
|
@@ -31668,20 +31863,27 @@ var ParallAgentGateway = class {
|
|
|
31668
31863
|
}
|
|
31669
31864
|
return outcome === "dispatched";
|
|
31670
31865
|
}
|
|
31671
|
-
|
|
31672
|
-
|
|
31673
|
-
|
|
31674
|
-
|
|
31675
|
-
|
|
31676
|
-
|
|
31677
|
-
|
|
31678
|
-
|
|
31679
|
-
|
|
31680
|
-
|
|
31866
|
+
const typedRefs = this.typedLedgerEventIds([event]);
|
|
31867
|
+
if (!typedRefs) {
|
|
31868
|
+
try {
|
|
31869
|
+
await this.emitDispatchReceived(event);
|
|
31870
|
+
} catch (err) {
|
|
31871
|
+
this.opts.log?.warn?.(`mark-received failed, leaving unacked for retry: ${String(err)}`);
|
|
31872
|
+
this.dispatchState.mainDispatching = false;
|
|
31873
|
+
this.dispatchState.mainCurrentTargetId = void 0;
|
|
31874
|
+
this.mainCurrentGroupKey = void 0;
|
|
31875
|
+
this.dispatchState.mainPreDispatchBranchPoint = void 0;
|
|
31876
|
+
this.dispatchState.pendingForkResults.unshift(...pendingFork);
|
|
31877
|
+
return false;
|
|
31878
|
+
}
|
|
31681
31879
|
}
|
|
31682
31880
|
let dispatched = false;
|
|
31683
31881
|
try {
|
|
31684
31882
|
dispatched = await this.runDispatch(event, this.opts.runtimeKey, forkPrefix + buildEventBody(event));
|
|
31883
|
+
if (dispatched && typedRefs && this.consumeTurnError(this.opts.runtimeKey)) {
|
|
31884
|
+
this.opts.log?.info(`typed dispatch turn for ${event.messageId} surfaced a runtime error \u2014 releasing for retry`);
|
|
31885
|
+
dispatched = false;
|
|
31886
|
+
}
|
|
31685
31887
|
if (!dispatched) {
|
|
31686
31888
|
this.dispatchState.pendingForkResults.unshift(...pendingFork);
|
|
31687
31889
|
}
|
|
@@ -31723,7 +31925,7 @@ var ParallAgentGateway = class {
|
|
|
31723
31925
|
this.dispatchState.mainBuffer.push(event);
|
|
31724
31926
|
return false;
|
|
31725
31927
|
}
|
|
31726
|
-
if (!this.usesLaneLedger(event)) {
|
|
31928
|
+
if (!this.usesLaneLedger(event) && !this.typedLedgerEventIds([event])) {
|
|
31727
31929
|
try {
|
|
31728
31930
|
await this.emitDispatchReceived(event);
|
|
31729
31931
|
} catch (err) {
|
|
@@ -31904,7 +32106,10 @@ var ParallAgentGateway = class {
|
|
|
31904
32106
|
return;
|
|
31905
32107
|
await this.consumeTypedDispatch({ dispatchEventId: item.id }, (dispatchEventId) => this.handleTaskDispatch(item.task_id ?? "", item.source_id ?? item.task_id ?? "", {
|
|
31906
32108
|
dispatchEventId
|
|
31907
|
-
}),
|
|
32109
|
+
}), {
|
|
32110
|
+
legacyAck: (dispatchEventId) => this.ackDispatchEvent(dispatchEventId ?? item.id, () => this.clearTypedDispatchDedupe(item)),
|
|
32111
|
+
clearDedupe: () => this.clearTypedDispatchDedupe(item)
|
|
32112
|
+
});
|
|
31908
32113
|
}
|
|
31909
32114
|
consumeMessageWorkItem(item) {
|
|
31910
32115
|
return consumeMessageWorkItem(this.laneFlowHost(), item);
|
|
@@ -31965,7 +32170,7 @@ var ParallAgentGateway = class {
|
|
|
31965
32170
|
}
|
|
31966
32171
|
return this.handleTaskAssignment(task, ackSourceId, opts.dispatchEventId);
|
|
31967
32172
|
}
|
|
31968
|
-
async handleTaskComment(commentId, taskId, actorId, deliveryReason) {
|
|
32173
|
+
async handleTaskComment(commentId, taskId, actorId, deliveryReason, dispatchEventId) {
|
|
31969
32174
|
if (this.shuttingDown)
|
|
31970
32175
|
return false;
|
|
31971
32176
|
const dedupeKey = `comment:${commentId}`;
|
|
@@ -32021,7 +32226,8 @@ var ParallAgentGateway = class {
|
|
|
32021
32226
|
body: parts.join("\n"),
|
|
32022
32227
|
deliveryReason: deliveryReason ?? void 0,
|
|
32023
32228
|
ackSourceType: "comment",
|
|
32024
|
-
ackSourceId: commentId
|
|
32229
|
+
ackSourceId: commentId,
|
|
32230
|
+
dispatchEventId
|
|
32025
32231
|
};
|
|
32026
32232
|
let dispatched;
|
|
32027
32233
|
try {
|
|
@@ -32035,7 +32241,7 @@ var ParallAgentGateway = class {
|
|
|
32035
32241
|
}
|
|
32036
32242
|
return dispatched;
|
|
32037
32243
|
}
|
|
32038
|
-
async handleWikiComment(commentId, actorId, deliveryReason) {
|
|
32244
|
+
async handleWikiComment(commentId, actorId, deliveryReason, dispatchEventId) {
|
|
32039
32245
|
if (this.shuttingDown)
|
|
32040
32246
|
return false;
|
|
32041
32247
|
const dedupeKey = `comment:${commentId}`;
|
|
@@ -32081,7 +32287,8 @@ var ParallAgentGateway = class {
|
|
|
32081
32287
|
deliveryReason: deliveryReason ?? void 0,
|
|
32082
32288
|
replyTargetUri: comment.target_uri,
|
|
32083
32289
|
ackSourceType: "comment",
|
|
32084
|
-
ackSourceId: commentId
|
|
32290
|
+
ackSourceId: commentId,
|
|
32291
|
+
dispatchEventId
|
|
32085
32292
|
};
|
|
32086
32293
|
let dispatched;
|
|
32087
32294
|
try {
|
|
@@ -32103,7 +32310,7 @@ var ParallAgentGateway = class {
|
|
|
32103
32310
|
* access to already-delivered run snapshots, and the runtime must not crash
|
|
32104
32311
|
* or retry forever in that case.
|
|
32105
32312
|
*/
|
|
32106
|
-
async fetchAndHandleScheduleFire(runId, actorId) {
|
|
32313
|
+
async fetchAndHandleScheduleFire(runId, actorId, dispatchEventId) {
|
|
32107
32314
|
let run = null;
|
|
32108
32315
|
try {
|
|
32109
32316
|
run = await this.opts.client.getScheduleRun(this.opts.config.org_id, runId);
|
|
@@ -32118,9 +32325,9 @@ var ParallAgentGateway = class {
|
|
|
32118
32325
|
}
|
|
32119
32326
|
if (!run)
|
|
32120
32327
|
return true;
|
|
32121
|
-
return this.handleScheduleFire(run, actorId);
|
|
32328
|
+
return this.handleScheduleFire(run, actorId, dispatchEventId);
|
|
32122
32329
|
}
|
|
32123
|
-
async handleScheduleFire(run, actorId) {
|
|
32330
|
+
async handleScheduleFire(run, actorId, dispatchEventId) {
|
|
32124
32331
|
if (this.shuttingDown)
|
|
32125
32332
|
return false;
|
|
32126
32333
|
const dedupeKey = `schedule_run:${run.id}`;
|
|
@@ -32143,7 +32350,8 @@ var ParallAgentGateway = class {
|
|
|
32143
32350
|
scheduledFireAt: run.scheduled_fire_at,
|
|
32144
32351
|
attachedUri: run.fired_attached_uri ?? void 0,
|
|
32145
32352
|
ackSourceType: "schedule_run",
|
|
32146
|
-
ackSourceId: run.id
|
|
32353
|
+
ackSourceId: run.id,
|
|
32354
|
+
dispatchEventId
|
|
32147
32355
|
};
|
|
32148
32356
|
let dispatched;
|
|
32149
32357
|
try {
|
|
@@ -32157,7 +32365,7 @@ var ParallAgentGateway = class {
|
|
|
32157
32365
|
}
|
|
32158
32366
|
return dispatched;
|
|
32159
32367
|
}
|
|
32160
|
-
async fetchAndHandleExternalTriggerRun(runId) {
|
|
32368
|
+
async fetchAndHandleExternalTriggerRun(runId, dispatchEventId) {
|
|
32161
32369
|
let run = null;
|
|
32162
32370
|
try {
|
|
32163
32371
|
run = await this.opts.client.getExternalTriggerRun(this.opts.config.org_id, runId);
|
|
@@ -32172,14 +32380,14 @@ var ParallAgentGateway = class {
|
|
|
32172
32380
|
}
|
|
32173
32381
|
if (!run)
|
|
32174
32382
|
return true;
|
|
32175
|
-
return this.handleExternalTriggerRun(run);
|
|
32383
|
+
return this.handleExternalTriggerRun(run, dispatchEventId);
|
|
32176
32384
|
}
|
|
32177
32385
|
// fetchAndHandleChannelMessage resolves a channel_message dispatch to its
|
|
32178
32386
|
// durable ChannelMessage + conversation and hands it to the inbound
|
|
32179
32387
|
// pipeline. targetId = the ChannelConversation id, so per-conversation
|
|
32180
32388
|
// multi-turn continuity rides the same per-target session mechanics as
|
|
32181
32389
|
// chats. Design: docs/engineering-design/external-im-channel-design.md.
|
|
32182
|
-
async fetchAndHandleChannelMessage(messageId) {
|
|
32390
|
+
async fetchAndHandleChannelMessage(messageId, dispatchEventId) {
|
|
32183
32391
|
if (this.shuttingDown)
|
|
32184
32392
|
return false;
|
|
32185
32393
|
const claimKey = `channel_message:${messageId}`;
|
|
@@ -32232,7 +32440,8 @@ var ParallAgentGateway = class {
|
|
|
32232
32440
|
channelExternalMessageId: msg.external_message_id,
|
|
32233
32441
|
channelCliCapable: cliCapable,
|
|
32234
32442
|
ackSourceType: "channel_message",
|
|
32235
|
-
ackSourceId: msg.id
|
|
32443
|
+
ackSourceId: msg.id,
|
|
32444
|
+
dispatchEventId
|
|
32236
32445
|
};
|
|
32237
32446
|
let dispatched;
|
|
32238
32447
|
try {
|
|
@@ -32246,7 +32455,7 @@ var ParallAgentGateway = class {
|
|
|
32246
32455
|
}
|
|
32247
32456
|
return dispatched;
|
|
32248
32457
|
}
|
|
32249
|
-
async handleExternalTriggerRun(run) {
|
|
32458
|
+
async handleExternalTriggerRun(run, dispatchEventId) {
|
|
32250
32459
|
if (this.shuttingDown)
|
|
32251
32460
|
return false;
|
|
32252
32461
|
const dedupeKey = `external_trigger_run:${run.id}`;
|
|
@@ -32271,7 +32480,8 @@ var ParallAgentGateway = class {
|
|
|
32271
32480
|
externalIngressEventType: run.ingress_event_type || void 0,
|
|
32272
32481
|
attachedUri,
|
|
32273
32482
|
ackSourceType: "external_trigger_run",
|
|
32274
|
-
ackSourceId: run.id
|
|
32483
|
+
ackSourceId: run.id,
|
|
32484
|
+
dispatchEventId
|
|
32275
32485
|
};
|
|
32276
32486
|
let dispatched;
|
|
32277
32487
|
try {
|
|
@@ -32285,7 +32495,7 @@ var ParallAgentGateway = class {
|
|
|
32285
32495
|
}
|
|
32286
32496
|
return dispatched;
|
|
32287
32497
|
}
|
|
32288
|
-
async fetchAndHandleApprovalDecided(approvalId, actorId, chatId) {
|
|
32498
|
+
async fetchAndHandleApprovalDecided(approvalId, actorId, chatId, dispatchEventId) {
|
|
32289
32499
|
let approval = null;
|
|
32290
32500
|
try {
|
|
32291
32501
|
approval = await this.opts.client.getApproval(approvalId);
|
|
@@ -32316,7 +32526,12 @@ var ParallAgentGateway = class {
|
|
|
32316
32526
|
senderId: actorId ?? approval.decided_by ?? "system",
|
|
32317
32527
|
senderName: "approver",
|
|
32318
32528
|
messageId: approval.id,
|
|
32319
|
-
body
|
|
32529
|
+
body,
|
|
32530
|
+
// The WorkItem's real source pair. Without it the legacy fallback
|
|
32531
|
+
// guessed ('message', approval_id) — a pair no row matches.
|
|
32532
|
+
ackSourceType: "approval",
|
|
32533
|
+
ackSourceId: approval.id,
|
|
32534
|
+
dispatchEventId
|
|
32320
32535
|
};
|
|
32321
32536
|
let dispatched;
|
|
32322
32537
|
try {
|
|
@@ -32363,7 +32578,17 @@ var ParallAgentGateway = class {
|
|
|
32363
32578
|
break;
|
|
32364
32579
|
if (overflowMode && processed >= CATCHUP_MAX) {
|
|
32365
32580
|
try {
|
|
32366
|
-
|
|
32581
|
+
if (this.laneLedger && !this.ledgerDisabled && !this.typedByIdCompleteUnsupported) {
|
|
32582
|
+
const outcome = await this.resolveDispatchByID(item.id);
|
|
32583
|
+
if (outcome === "unsupported") {
|
|
32584
|
+
this.typedByIdCompleteUnsupported = true;
|
|
32585
|
+
await this.opts.client.ackDispatchByID(this.opts.config.org_id, item.id);
|
|
32586
|
+
} else if (outcome === "failed") {
|
|
32587
|
+
throw new Error("by-id complete failed");
|
|
32588
|
+
}
|
|
32589
|
+
} else {
|
|
32590
|
+
await this.opts.client.ackDispatchByID(this.opts.config.org_id, item.id);
|
|
32591
|
+
}
|
|
32367
32592
|
skipped++;
|
|
32368
32593
|
const key = item.event_type;
|
|
32369
32594
|
skippedByType.set(key, (skippedByType.get(key) ?? 0) + 1);
|
|
@@ -32374,12 +32599,15 @@ var ParallAgentGateway = class {
|
|
|
32374
32599
|
}
|
|
32375
32600
|
processed++;
|
|
32376
32601
|
try {
|
|
32377
|
-
const
|
|
32602
|
+
const typedHooks = {
|
|
32603
|
+
legacyAck: () => this.ackDispatchEvent(item.id, () => this.clearTypedDispatchDedupe(item)),
|
|
32604
|
+
clearDedupe: () => this.clearTypedDispatchDedupe(item)
|
|
32605
|
+
};
|
|
32378
32606
|
if (item.event_type === "task_assign" && item.task_id) {
|
|
32379
32607
|
try {
|
|
32380
32608
|
await this.consumeTypedDispatch({ dispatchEventId: item.id }, (dispatchEventId) => this.handleTaskDispatch(item.task_id ?? "", item.source_id ?? item.task_id ?? "", {
|
|
32381
32609
|
dispatchEventId
|
|
32382
|
-
}),
|
|
32610
|
+
}), typedHooks);
|
|
32383
32611
|
} catch (err) {
|
|
32384
32612
|
this.opts.log?.warn(`catch-up task fetch failed for ${item.task_id}, leaving pending: ${String(err)}`);
|
|
32385
32613
|
continue;
|
|
@@ -32389,23 +32617,23 @@ var ParallAgentGateway = class {
|
|
|
32389
32617
|
await this.consumeTypedDispatch({ dispatchEventId: item.id }, (dispatchEventId) => this.handleTaskDispatch(item.task_id ?? "", item.source_id ?? item.task_id ?? "", {
|
|
32390
32618
|
allowCreator: true,
|
|
32391
32619
|
dispatchEventId
|
|
32392
|
-
}),
|
|
32620
|
+
}), typedHooks);
|
|
32393
32621
|
} catch (err) {
|
|
32394
32622
|
this.opts.log?.warn(`catch-up task fetch failed for ${item.task_id}, leaving pending: ${String(err)}`);
|
|
32395
32623
|
continue;
|
|
32396
32624
|
}
|
|
32397
32625
|
} else if (item.event_type === "task_comment" && item.source_id && item.task_id) {
|
|
32398
|
-
await this.consumeTypedDispatch({ dispatchEventId: item.id }, () => this.handleTaskComment(item.source_id, item.task_id ?? "", item.actor_id, item.delivery_reason),
|
|
32626
|
+
await this.consumeTypedDispatch({ dispatchEventId: item.id }, (dispatchEventId) => this.handleTaskComment(item.source_id, item.task_id ?? "", item.actor_id, item.delivery_reason, dispatchEventId), typedHooks);
|
|
32399
32627
|
} else if (item.event_type === "wiki_comment" && item.source_id) {
|
|
32400
|
-
await this.consumeTypedDispatch({ dispatchEventId: item.id }, () => this.handleWikiComment(item.source_id, item.actor_id, item.delivery_reason),
|
|
32628
|
+
await this.consumeTypedDispatch({ dispatchEventId: item.id }, (dispatchEventId) => this.handleWikiComment(item.source_id, item.actor_id, item.delivery_reason, dispatchEventId), typedHooks);
|
|
32401
32629
|
} else if (item.event_type === "schedule.fire" && item.source_id) {
|
|
32402
|
-
await this.consumeTypedDispatch({ dispatchEventId: item.id }, () => this.fetchAndHandleScheduleFire(item.source_id, item.actor_id),
|
|
32630
|
+
await this.consumeTypedDispatch({ dispatchEventId: item.id }, (dispatchEventId) => this.fetchAndHandleScheduleFire(item.source_id, item.actor_id, dispatchEventId), typedHooks);
|
|
32403
32631
|
} else if (item.event_type === "external_trigger" && item.source_id) {
|
|
32404
|
-
await this.consumeTypedDispatch({ dispatchEventId: item.id }, () => this.fetchAndHandleExternalTriggerRun(item.source_id),
|
|
32632
|
+
await this.consumeTypedDispatch({ dispatchEventId: item.id }, (dispatchEventId) => this.fetchAndHandleExternalTriggerRun(item.source_id, dispatchEventId), typedHooks);
|
|
32405
32633
|
} else if (item.event_type === "channel_message" && item.source_id) {
|
|
32406
|
-
await this.consumeTypedDispatch({ dispatchEventId: item.id }, () => this.fetchAndHandleChannelMessage(item.source_id),
|
|
32634
|
+
await this.consumeTypedDispatch({ dispatchEventId: item.id }, (dispatchEventId) => this.fetchAndHandleChannelMessage(item.source_id, dispatchEventId), typedHooks);
|
|
32407
32635
|
} else if (item.event_type === "approval_decided" && item.source_id) {
|
|
32408
|
-
await this.consumeTypedDispatch({ dispatchEventId: item.id }, () => this.fetchAndHandleApprovalDecided(item.source_id, item.actor_id, item.chat_id ?? null),
|
|
32636
|
+
await this.consumeTypedDispatch({ dispatchEventId: item.id }, (dispatchEventId) => this.fetchAndHandleApprovalDecided(item.source_id, item.actor_id, item.chat_id ?? null, dispatchEventId), typedHooks);
|
|
32409
32637
|
} else if (item.event_type === "message" && item.source_id && item.chat_id) {
|
|
32410
32638
|
await this.consumeMessageWorkItem({
|
|
32411
32639
|
id: item.id,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@parall/parall",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.45.0",
|
|
4
4
|
"description": "OpenClaw channel plugin for Parall IM",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
"openclaw.plugin.json"
|
|
17
17
|
],
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@parall/
|
|
20
|
-
"@parall/
|
|
19
|
+
"@parall/sdk": "1.45.0",
|
|
20
|
+
"@parall/agent-core": "1.45.0"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"@types/node": "^22.0.0",
|
|
@@ -40,7 +40,7 @@ parall schedules create \
|
|
|
40
40
|
--run-at <FUTURE_RFC3339_TIME>
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
-
`--target-ids` is who receives the fire (usually yourself when you're self-scheduling; another agent or human when delegating). `--attached-to-uri` optionally anchors the schedule to a task / chat / project / wiki page — when that resource is archived or deleted, the schedule auto-cancels (`
|
|
43
|
+
`--target-ids` is who receives the fire (usually yourself when you're self-scheduling; another agent or human when delegating). `--attached-to-uri` optionally anchors the schedule to a task / chat / project / wiki page — when that resource is archived or deleted, the schedule auto-cancels (`status_reason=attached_gone`). A schedule whose agent targets all sit on a terminated machine is auto-paused by the platform (`status_reason=attendee_unreachable`) instead of firing into a void; resuming a recurring schedule while the machine is still terminated just pauses it again on the next slot (a one-shot resumed past its catch-up window instead follows the normal missed semantics and completes).
|
|
44
44
|
|
|
45
45
|
### Reminders for someone else
|
|
46
46
|
|
|
@@ -39,16 +39,32 @@ parall tasks list --assignee-id prll://usr_xxx # first page only (default 20)
|
|
|
39
39
|
parall tasks subtasks prll://tsk_xxx # children of a single parent task
|
|
40
40
|
|
|
41
41
|
# Create a task (add --parent-id to make it a SUBTASK of another task)
|
|
42
|
-
parall tasks create --title "Task title" [--assignee-id prll://usr_xxx] [--parent-id prll://tsk_xxx] [--project-id prll://prj_xxx]
|
|
42
|
+
parall tasks create --title "Task title" [--assignee-id prll://usr_xxx] [--parent-id prll://tsk_xxx] [--project-id prll://prj_xxx] [--due-date 2026-08-01]
|
|
43
43
|
|
|
44
|
-
# Update task status
|
|
45
|
-
|
|
46
|
-
parall tasks update prll://tsk_xxx --status
|
|
44
|
+
# Update task status — add --placement end so the task lands at the end of
|
|
45
|
+
# its NEW status column (a bare --status keeps the old column's sort_order)
|
|
46
|
+
parall tasks update prll://tsk_xxx --status in_progress --placement end
|
|
47
|
+
parall tasks update prll://tsk_xxx --status done --placement end
|
|
48
|
+
|
|
49
|
+
# Due date — a plain YYYY-MM-DD date (no timestamps); "none" clears it
|
|
50
|
+
parall tasks update prll://tsk_xxx --due-date 2026-08-01
|
|
51
|
+
parall tasks update prll://tsk_xxx --due-date none
|
|
52
|
+
|
|
53
|
+
# Move a task to the end of its status column
|
|
54
|
+
parall tasks update prll://tsk_xxx --placement end
|
|
47
55
|
|
|
48
56
|
# Add a comment
|
|
49
57
|
parall tasks comments add prll://tsk_xxx --body "Progress update..."
|
|
50
58
|
```
|
|
51
59
|
|
|
60
|
+
Ordering: to append a task to the end of a status column, always use
|
|
61
|
+
`--placement end` — the server resolves the position atomically. This
|
|
62
|
+
includes status changes: a bare `--status` keeps the task's old
|
|
63
|
+
`sort_order`, which may collide inside the new column. Do NOT compute a
|
|
64
|
+
`sort_order` value yourself from listed tasks (your view may be stale or
|
|
65
|
+
partial). `--sort-order` is only for pinpoint insertion between two cards
|
|
66
|
+
you just listed, and it cannot be combined with `--placement`.
|
|
67
|
+
|
|
52
68
|
Subtasks are just tasks with a parent: create one with `tasks create --parent-id`,
|
|
53
69
|
re-parent with `tasks update --parent-id`, list a parent's children with
|
|
54
70
|
`tasks subtasks`. `tasks list` without `--parent-id` already returns both
|
|
@@ -83,7 +99,7 @@ watcher.
|
|
|
83
99
|
When you receive `[Event: task.assigned]`:
|
|
84
100
|
|
|
85
101
|
1. Acknowledge with a comment: `tasks comments add prll://tsk_xxx --body "On it"`
|
|
86
|
-
2. Update status: `tasks update prll://tsk_xxx --status in_progress`
|
|
102
|
+
2. Update status: `tasks update prll://tsk_xxx --status in_progress --placement end`
|
|
87
103
|
3. Do the work
|
|
88
104
|
4. Report results in a comment. If a gate remains — review, merge, deploy,
|
|
89
105
|
requester acceptance — set `in_review` and name the gate; set `done`
|