@parall/daemon 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/bundle/manifest.json +11 -11
- package/bundle/parall-browser-pod.js +4 -1
- package/bundle/parall-claude-agent.js +455 -102
- package/bundle/parall-codex-agent.js +1044 -1313
- package/bundle/parall-daemon.js +870 -312
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +229 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/runtime-bin-resolver.d.ts +7 -1
- package/dist/runtime-bin-resolver.d.ts.map +1 -1
- package/dist/runtime-bin-resolver.js +57 -22
- package/dist/runtimes.d.ts +15 -4
- package/dist/runtimes.d.ts.map +1 -1
- package/dist/runtimes.js +60 -5
- package/dist/supervisor.d.ts.map +1 -1
- package/dist/supervisor.js +4 -1
- package/dist/win-lifecycle.d.ts +96 -0
- package/dist/win-lifecycle.d.ts.map +1 -0
- package/dist/win-lifecycle.js +229 -0
- package/dist/win-service.d.ts +119 -0
- package/dist/win-service.d.ts.map +1 -0
- package/dist/win-service.js +226 -0
- package/package.json +6 -6
|
@@ -26487,9 +26487,6 @@ function laneContextFilePath(contextDir, targetUri, threadRootId) {
|
|
|
26487
26487
|
return path.join(contextDir, `${laneKeyForTarget(targetUri, threadRootId)}.json`);
|
|
26488
26488
|
}
|
|
26489
26489
|
|
|
26490
|
-
// ts/agent-core/dist/lane-ledger.js
|
|
26491
|
-
import * as fs from "node:fs";
|
|
26492
|
-
|
|
26493
26490
|
// ts/sdk/dist/types.js
|
|
26494
26491
|
var MENTION_ALL_USER_ID = "all";
|
|
26495
26492
|
|
|
@@ -27944,7 +27941,6 @@ var ParallClient = class _ParallClient {
|
|
|
27944
27941
|
async steerDispatch(orgId, req) {
|
|
27945
27942
|
return this.request("POST", ENDPOINTS.DISPATCH_STEER(orgId), req);
|
|
27946
27943
|
}
|
|
27947
|
-
/** End a turn: no_action sweep of the lane's members + lane release + re-drive check. */
|
|
27948
27944
|
async completeDispatch(orgId, req) {
|
|
27949
27945
|
return this.request("POST", ENDPOINTS.DISPATCH_COMPLETE(orgId), req);
|
|
27950
27946
|
}
|
|
@@ -27952,6 +27948,10 @@ var ParallClient = class _ParallClient {
|
|
|
27952
27948
|
* End a turn for a lane-less runtime: resolve the turn's folded WorkItems
|
|
27953
27949
|
* by source — broad-cover to the turn's reply Effect when one exists,
|
|
27954
27950
|
* no_action sweep otherwise. Idempotent.
|
|
27951
|
+
*
|
|
27952
|
+
* @deprecated Legacy ok-only alias — use {@link completeDispatch} with the
|
|
27953
|
+
* `sources` form, which also carries `turn_outcome`. The endpoint retires
|
|
27954
|
+
* at S3b (dispatch-convergence-design.md §3).
|
|
27955
27955
|
*/
|
|
27956
27956
|
async completeDispatchSources(orgId, req) {
|
|
27957
27957
|
return this.request("POST", ENDPOINTS.DISPATCH_COMPLETE_SOURCES(orgId), req);
|
|
@@ -29098,6 +29098,7 @@ var ParallWs = class {
|
|
|
29098
29098
|
};
|
|
29099
29099
|
|
|
29100
29100
|
// ts/agent-core/dist/lane-ledger.js
|
|
29101
|
+
import * as fs from "node:fs";
|
|
29101
29102
|
var LedgerUnsupportedError = class extends Error {
|
|
29102
29103
|
};
|
|
29103
29104
|
function isStaleLane(err) {
|
|
@@ -29157,7 +29158,11 @@ var LaneLedger = class {
|
|
|
29157
29158
|
throw err;
|
|
29158
29159
|
}
|
|
29159
29160
|
if (!res.claimed || !res.lane) {
|
|
29160
|
-
|
|
29161
|
+
if (res.reason === "empty") {
|
|
29162
|
+
this.opts.log?.warn(`claim for ${targetUri} came back empty \u2014 nothing foldable; leaving to the reconciler`);
|
|
29163
|
+
} else {
|
|
29164
|
+
this.opts.log?.info(`lane for ${targetUri} held by a healthy incumbent \u2014 leaving events pending for re-drive`);
|
|
29165
|
+
}
|
|
29161
29166
|
return null;
|
|
29162
29167
|
}
|
|
29163
29168
|
const leaseUntilMs = Date.parse(res.lease_until ?? "");
|
|
@@ -29234,6 +29239,18 @@ var LaneLedger = class {
|
|
|
29234
29239
|
* re-drives any same-target pending work. A STALE_LANE answer means a
|
|
29235
29240
|
* takeover already owns the resource — local state is dropped either way.
|
|
29236
29241
|
*/
|
|
29242
|
+
/**
|
|
29243
|
+
* Record that the turn on this lane surfaced a runtime error. The flow
|
|
29244
|
+
* settles an errored lane immediately (dispatchLaneGroup returns 'failed'
|
|
29245
|
+
* after a forced complete), so the bit normally lives for one turn only —
|
|
29246
|
+
* it is the transport between the gateway's per-session error signal and
|
|
29247
|
+
* this lane's complete request.
|
|
29248
|
+
*/
|
|
29249
|
+
markTurnError(laneKey) {
|
|
29250
|
+
const lane = this.lanes.get(laneKey);
|
|
29251
|
+
if (lane)
|
|
29252
|
+
lane.turnError = true;
|
|
29253
|
+
}
|
|
29237
29254
|
async completeIfIdle(laneKey, hasMoreLocal) {
|
|
29238
29255
|
const lane = this.lanes.get(laneKey);
|
|
29239
29256
|
if (!lane || hasMoreLocal)
|
|
@@ -29244,7 +29261,10 @@ var LaneLedger = class {
|
|
|
29244
29261
|
const res = await this.opts.client.completeDispatch(this.opts.orgId, {
|
|
29245
29262
|
lane: lane.lane,
|
|
29246
29263
|
target_uri: lane.targetUri,
|
|
29247
|
-
thread_root_id: lane.threadRootId
|
|
29264
|
+
thread_root_id: lane.threadRootId,
|
|
29265
|
+
// An error turn releases its members for retry instead of sweeping
|
|
29266
|
+
// them as handled (ignored by older servers).
|
|
29267
|
+
turn_outcome: lane.turnError ? "error" : "ok"
|
|
29248
29268
|
});
|
|
29249
29269
|
if (res.swept_no_action > 0 || res.redriven) {
|
|
29250
29270
|
this.opts.log?.info(`lane complete for ${lane.targetUri}: swept ${res.swept_no_action} no_action, redriven=${res.redriven}`);
|
|
@@ -29257,6 +29277,17 @@ var LaneLedger = class {
|
|
|
29257
29277
|
this.opts.log?.warn(`lane complete failed for ${lane.targetUri}: ${String(err)}`);
|
|
29258
29278
|
}
|
|
29259
29279
|
}
|
|
29280
|
+
/**
|
|
29281
|
+
* Renew one lane by its key — the external runtime-activity hook for
|
|
29282
|
+
* adapters whose tool traffic bypasses the RuntimeEvent stream (openclaw
|
|
29283
|
+
* hooks). Scoped to the session's own lane: renewing every lane would let
|
|
29284
|
+
* one busy fork keep an unrelated stalled fork's lane leased forever.
|
|
29285
|
+
*/
|
|
29286
|
+
renewByKey(laneKey) {
|
|
29287
|
+
const lane = this.lanes.get(laneKey);
|
|
29288
|
+
if (lane)
|
|
29289
|
+
this.maybeRenew(lane);
|
|
29290
|
+
}
|
|
29260
29291
|
/**
|
|
29261
29292
|
* Long-turn keepalive: renew the lane's lease on runtime activity, throttled
|
|
29262
29293
|
* so a chatty turn doesn't spam the server. Without this, a legitimately
|
|
@@ -29351,6 +29382,20 @@ var LaneLedger = class {
|
|
|
29351
29382
|
this.lanes.set(lane.laneKey, lane);
|
|
29352
29383
|
return lane;
|
|
29353
29384
|
}
|
|
29385
|
+
/**
|
|
29386
|
+
* Drop a lane's local record without any server call — for a typed lane
|
|
29387
|
+
* whose member the by-id complete just resolved (the server dropped the
|
|
29388
|
+
* vacated lane row in the same transaction). Calling completeIfIdle
|
|
29389
|
+
* instead would fire a lane-form Complete at a lane that no longer exists
|
|
29390
|
+
* and burn an RPC on the guaranteed STALE_LANE answer.
|
|
29391
|
+
*/
|
|
29392
|
+
dropLocal(laneKey) {
|
|
29393
|
+
const lane = this.lanes.get(laneKey);
|
|
29394
|
+
if (!lane)
|
|
29395
|
+
return;
|
|
29396
|
+
this.lanes.delete(laneKey);
|
|
29397
|
+
this.removeLaneContext(lane);
|
|
29398
|
+
}
|
|
29354
29399
|
/**
|
|
29355
29400
|
* Remove the per-lane context file (and its CLI sidecar) when the lane
|
|
29356
29401
|
* ends. A leftover file would make a later cross-context send to the same
|
|
@@ -29398,25 +29443,136 @@ async function dispatchLaneGroup(host, opts) {
|
|
|
29398
29443
|
}
|
|
29399
29444
|
return "foreign";
|
|
29400
29445
|
}
|
|
29446
|
+
host.noteSessionLane(opts.sessionKey, lane.laneKey);
|
|
29401
29447
|
let dispatched = false;
|
|
29402
29448
|
try {
|
|
29403
29449
|
dispatched = await host.runDispatch(event, opts.sessionKey, opts.body, opts.earlier, opts.captureText);
|
|
29404
29450
|
} catch (err) {
|
|
29451
|
+
host.noteSessionLane(opts.sessionKey, null);
|
|
29405
29452
|
await ledger.release(lane.laneKey).catch(() => {
|
|
29406
29453
|
});
|
|
29407
29454
|
throw err;
|
|
29455
|
+
} finally {
|
|
29456
|
+
if (dispatched)
|
|
29457
|
+
host.noteSessionLane(opts.sessionKey, null);
|
|
29408
29458
|
}
|
|
29409
29459
|
if (!dispatched) {
|
|
29410
29460
|
return "shutdown";
|
|
29411
29461
|
}
|
|
29462
|
+
if (host.consumeTurnError(opts.sessionKey)) {
|
|
29463
|
+
ledger.markTurnError(lane.laneKey);
|
|
29464
|
+
for (const msgId of lane.folded.keys()) {
|
|
29465
|
+
host.dispatchedMessages.delete(msgId);
|
|
29466
|
+
}
|
|
29467
|
+
try {
|
|
29468
|
+
host.opts.dispatchAdapter.abortDispatch?.(opts.sessionKey);
|
|
29469
|
+
} catch {
|
|
29470
|
+
}
|
|
29471
|
+
await ledger.completeIfIdle(lane.laneKey, false);
|
|
29472
|
+
return "failed";
|
|
29473
|
+
}
|
|
29412
29474
|
const pendingInjections = host.opts.dispatchAdapter.hasPendingInjections?.(opts.sessionKey) ?? false;
|
|
29413
29475
|
await ledger.completeIfIdle(lane.laneKey, pendingInjections || opts.hasMoreLocal());
|
|
29414
29476
|
return "dispatched";
|
|
29415
29477
|
}
|
|
29478
|
+
function typedLedgerEventIds(host, events) {
|
|
29479
|
+
if (!host.laneLedger || host.ledgerDisabled)
|
|
29480
|
+
return null;
|
|
29481
|
+
const ids = [];
|
|
29482
|
+
for (const ev of events) {
|
|
29483
|
+
if (!ev.dispatchEventId || host.usesLaneLedger(ev))
|
|
29484
|
+
return null;
|
|
29485
|
+
ids.push(ev.dispatchEventId);
|
|
29486
|
+
}
|
|
29487
|
+
return ids.length > 0 ? ids : null;
|
|
29488
|
+
}
|
|
29489
|
+
function isByIDCompleteUnsupported(err) {
|
|
29490
|
+
return err instanceof ApiError && err.status === 400;
|
|
29491
|
+
}
|
|
29492
|
+
async function resolveDispatchByID(host, dispatchEventId, lane) {
|
|
29493
|
+
try {
|
|
29494
|
+
await host.opts.client.completeDispatch(host.opts.config.org_id, {
|
|
29495
|
+
dispatch_event_id: dispatchEventId,
|
|
29496
|
+
...lane ? { lane } : {},
|
|
29497
|
+
turn_outcome: "ok"
|
|
29498
|
+
});
|
|
29499
|
+
return "ok";
|
|
29500
|
+
} catch (err) {
|
|
29501
|
+
if (err instanceof ApiError && err.status === 409)
|
|
29502
|
+
return "stale";
|
|
29503
|
+
if (isByIDCompleteUnsupported(err))
|
|
29504
|
+
return "unsupported";
|
|
29505
|
+
host.opts.log?.warn(`by-id complete failed for ${dispatchEventId} \u2014 leaving for re-drive: ${String(err)}`);
|
|
29506
|
+
return "failed";
|
|
29507
|
+
}
|
|
29508
|
+
}
|
|
29509
|
+
function clearTypedDedupeForEvent(host, event) {
|
|
29510
|
+
const sourceId = event.ackSourceId;
|
|
29511
|
+
if (!sourceId)
|
|
29512
|
+
return;
|
|
29513
|
+
switch (event.ackSourceType) {
|
|
29514
|
+
case "task_activity": {
|
|
29515
|
+
const prefix = `${event.targetId}:`;
|
|
29516
|
+
for (const key of host.dispatchedTasks) {
|
|
29517
|
+
if (key.startsWith(prefix))
|
|
29518
|
+
host.dispatchedTasks.delete(key);
|
|
29519
|
+
}
|
|
29520
|
+
break;
|
|
29521
|
+
}
|
|
29522
|
+
case "comment":
|
|
29523
|
+
host.dispatchedTasks.delete(`comment:${sourceId}`);
|
|
29524
|
+
break;
|
|
29525
|
+
case "schedule_run":
|
|
29526
|
+
host.dispatchedTasks.delete(`schedule_run:${sourceId}`);
|
|
29527
|
+
break;
|
|
29528
|
+
case "external_trigger_run":
|
|
29529
|
+
host.dispatchedTasks.delete(`external_trigger_run:${sourceId}`);
|
|
29530
|
+
break;
|
|
29531
|
+
case "channel_message":
|
|
29532
|
+
host.dispatchedMessages.delete(`channel_message:${sourceId}`);
|
|
29533
|
+
break;
|
|
29534
|
+
case "approval":
|
|
29535
|
+
host.dispatchedTasks.delete(`approval:${sourceId}`);
|
|
29536
|
+
break;
|
|
29537
|
+
}
|
|
29538
|
+
}
|
|
29539
|
+
async function settleDrainedTypedGroup(host, events, ids, turnErrored) {
|
|
29540
|
+
if (turnErrored) {
|
|
29541
|
+
host.opts.log?.info(`buffered typed turn for ${events[events.length - 1]?.messageId} surfaced a runtime error \u2014 leaving for re-drive`);
|
|
29542
|
+
for (const event of events)
|
|
29543
|
+
clearTypedDedupeForEvent(host, event);
|
|
29544
|
+
return;
|
|
29545
|
+
}
|
|
29546
|
+
const legacyAckFrom = async (start) => {
|
|
29547
|
+
for (const [j, id] of ids.slice(start).entries()) {
|
|
29548
|
+
try {
|
|
29549
|
+
await host.opts.client.ackDispatchByID(host.opts.config.org_id, id);
|
|
29550
|
+
} catch {
|
|
29551
|
+
clearTypedDedupeForEvent(host, events[start + j]);
|
|
29552
|
+
}
|
|
29553
|
+
}
|
|
29554
|
+
};
|
|
29555
|
+
if (host.typedByIdCompleteUnsupported) {
|
|
29556
|
+
await legacyAckFrom(0);
|
|
29557
|
+
return;
|
|
29558
|
+
}
|
|
29559
|
+
for (const [i, id] of ids.entries()) {
|
|
29560
|
+
const outcome = await resolveDispatchByID(host, id);
|
|
29561
|
+
if (outcome === "unsupported") {
|
|
29562
|
+
host.typedByIdCompleteUnsupported = true;
|
|
29563
|
+
host.opts.log?.warn("server predates the by-id dispatch complete \u2014 falling back to legacy typed acks");
|
|
29564
|
+
await legacyAckFrom(i);
|
|
29565
|
+
return;
|
|
29566
|
+
}
|
|
29567
|
+
if (outcome !== "ok") {
|
|
29568
|
+
clearTypedDedupeForEvent(host, events[i]);
|
|
29569
|
+
}
|
|
29570
|
+
}
|
|
29571
|
+
}
|
|
29416
29572
|
var TYPED_BACKOFF_BASE_MS = 2e3;
|
|
29417
29573
|
var TYPED_BACKOFF_CAP_MS = 5 * 6e4;
|
|
29418
29574
|
var TYPED_BACKOFF_MAP_CAP = 512;
|
|
29419
|
-
async function consumeTypedDispatch(host, ref, run,
|
|
29575
|
+
async function consumeTypedDispatch(host, ref, run, hooks) {
|
|
29420
29576
|
const backoffKey = ref.dispatchEventId ?? `${ref.sourceType}:${ref.sourceId}`;
|
|
29421
29577
|
const armed = host.typedRedriveBackoff.get(backoffKey);
|
|
29422
29578
|
if (armed) {
|
|
@@ -29432,8 +29588,8 @@ async function consumeTypedDispatch(host, ref, run, ack) {
|
|
|
29432
29588
|
return;
|
|
29433
29589
|
}
|
|
29434
29590
|
const settleAck = (ackResult) => ackResult !== false;
|
|
29435
|
-
const settle = (
|
|
29436
|
-
if (
|
|
29591
|
+
const settle = (resolved2) => {
|
|
29592
|
+
if (resolved2) {
|
|
29437
29593
|
host.typedRedriveBackoff.delete(backoffKey);
|
|
29438
29594
|
return;
|
|
29439
29595
|
}
|
|
@@ -29447,13 +29603,13 @@ async function consumeTypedDispatch(host, ref, run, ack) {
|
|
|
29447
29603
|
host.typedRedriveBackoff.set(backoffKey, { failures, until: Date.now() + backoffMs });
|
|
29448
29604
|
};
|
|
29449
29605
|
const runLegacy = async () => {
|
|
29450
|
-
let
|
|
29606
|
+
let acked = false;
|
|
29451
29607
|
try {
|
|
29452
29608
|
if (await run(ref.dispatchEventId)) {
|
|
29453
|
-
|
|
29609
|
+
acked = settleAck(await hooks.legacyAck(ref.dispatchEventId));
|
|
29454
29610
|
}
|
|
29455
29611
|
} finally {
|
|
29456
|
-
settle(
|
|
29612
|
+
settle(acked);
|
|
29457
29613
|
}
|
|
29458
29614
|
};
|
|
29459
29615
|
if (!host.laneLedger || host.ledgerDisabled) {
|
|
@@ -29475,15 +29631,39 @@ async function consumeTypedDispatch(host, ref, run, ack) {
|
|
|
29475
29631
|
host.opts.log?.info(`typed dispatch ${ref.dispatchEventId ?? `${ref.sourceType}:${ref.sourceId}`} not claimable (held elsewhere or already resolved) \u2014 skipping`);
|
|
29476
29632
|
return;
|
|
29477
29633
|
}
|
|
29478
|
-
let
|
|
29634
|
+
let resolved = false;
|
|
29635
|
+
let viaLegacyAck = false;
|
|
29479
29636
|
try {
|
|
29480
29637
|
if (await run(lane.typedDispatchEventId)) {
|
|
29481
|
-
|
|
29638
|
+
if (host.typedByIdCompleteUnsupported || !lane.typedDispatchEventId) {
|
|
29639
|
+
viaLegacyAck = true;
|
|
29640
|
+
resolved = settleAck(await hooks.legacyAck(lane.typedDispatchEventId));
|
|
29641
|
+
} else {
|
|
29642
|
+
const outcome = await resolveDispatchByID(host, lane.typedDispatchEventId, lane.lane);
|
|
29643
|
+
if (outcome === "unsupported") {
|
|
29644
|
+
host.typedByIdCompleteUnsupported = true;
|
|
29645
|
+
host.opts.log?.warn("server predates the by-id dispatch complete \u2014 falling back to the legacy typed ack");
|
|
29646
|
+
viaLegacyAck = true;
|
|
29647
|
+
resolved = settleAck(await hooks.legacyAck(lane.typedDispatchEventId));
|
|
29648
|
+
} else if (outcome === "stale") {
|
|
29649
|
+
hooks.clearDedupe?.();
|
|
29650
|
+
resolved = true;
|
|
29651
|
+
} else {
|
|
29652
|
+
resolved = outcome === "ok";
|
|
29653
|
+
if (!resolved) {
|
|
29654
|
+
hooks.clearDedupe?.();
|
|
29655
|
+
}
|
|
29656
|
+
}
|
|
29657
|
+
}
|
|
29482
29658
|
}
|
|
29483
29659
|
} finally {
|
|
29484
|
-
settle(
|
|
29485
|
-
|
|
29486
|
-
|
|
29660
|
+
settle(resolved);
|
|
29661
|
+
if (resolved && !viaLegacyAck) {
|
|
29662
|
+
host.laneLedger.dropLocal(lane.laneKey);
|
|
29663
|
+
} else {
|
|
29664
|
+
await host.laneLedger.completeIfIdle(lane.laneKey, false).catch(() => {
|
|
29665
|
+
});
|
|
29666
|
+
}
|
|
29487
29667
|
}
|
|
29488
29668
|
}
|
|
29489
29669
|
async function consumeMessageWorkItem(host, item) {
|
|
@@ -29492,6 +29672,16 @@ async function consumeMessageWorkItem(host, item) {
|
|
|
29492
29672
|
if (!host.tryClaimMessage(item.source_id))
|
|
29493
29673
|
return;
|
|
29494
29674
|
const ackItem = () => {
|
|
29675
|
+
if (host.laneLedger && !host.ledgerDisabled && !host.typedByIdCompleteUnsupported) {
|
|
29676
|
+
void resolveDispatchByID(host, item.id).then((outcome) => {
|
|
29677
|
+
if (outcome === "unsupported") {
|
|
29678
|
+
host.typedByIdCompleteUnsupported = true;
|
|
29679
|
+
host.opts.client.ackDispatchByID(host.opts.config.org_id, item.id).catch(() => {
|
|
29680
|
+
});
|
|
29681
|
+
}
|
|
29682
|
+
});
|
|
29683
|
+
return;
|
|
29684
|
+
}
|
|
29495
29685
|
host.opts.client.ackDispatchByID(host.opts.config.org_id, item.id).catch(() => {
|
|
29496
29686
|
});
|
|
29497
29687
|
};
|
|
@@ -30671,20 +30861,23 @@ var ParallAgentGateway = class {
|
|
|
30671
30861
|
if (data.status !== "todo" && data.status !== "in_progress")
|
|
30672
30862
|
return;
|
|
30673
30863
|
try {
|
|
30674
|
-
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),
|
|
30675
|
-
|
|
30676
|
-
|
|
30864
|
+
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), {
|
|
30865
|
+
legacyAck: (dispatchEventId) => {
|
|
30866
|
+
if (dispatchEventId) {
|
|
30867
|
+
return this.ackDispatchEvent(dispatchEventId, () => {
|
|
30868
|
+
this.dispatchedTasks.delete(`${data.id}:${data.updated_at}`);
|
|
30869
|
+
});
|
|
30870
|
+
}
|
|
30871
|
+
return this.opts.client.ackDispatch(this.opts.config.org_id, {
|
|
30872
|
+
source_type: "task_activity",
|
|
30873
|
+
source_id: data.id
|
|
30874
|
+
}).then(() => true, (err) => {
|
|
30677
30875
|
this.dispatchedTasks.delete(`${data.id}:${data.updated_at}`);
|
|
30876
|
+
this.opts.log?.warn(`dispatch ack failed for task ${data.id}, releasing for re-drive: ${String(err)}`);
|
|
30877
|
+
return false;
|
|
30678
30878
|
});
|
|
30679
|
-
}
|
|
30680
|
-
|
|
30681
|
-
source_type: "task_activity",
|
|
30682
|
-
source_id: data.id
|
|
30683
|
-
}).then(() => true, (err) => {
|
|
30684
|
-
this.dispatchedTasks.delete(`${data.id}:${data.updated_at}`);
|
|
30685
|
-
this.opts.log?.warn(`dispatch ack failed for task ${data.id}, releasing for re-drive: ${String(err)}`);
|
|
30686
|
-
return false;
|
|
30687
|
-
});
|
|
30879
|
+
},
|
|
30880
|
+
clearDedupe: () => this.dispatchedTasks.delete(`${data.id}:${data.updated_at}`)
|
|
30688
30881
|
});
|
|
30689
30882
|
} catch (err) {
|
|
30690
30883
|
this.opts.log?.error(`task dispatch failed for ${data.id}: ${String(err)}`);
|
|
@@ -30703,7 +30896,10 @@ var ParallAgentGateway = class {
|
|
|
30703
30896
|
if (!data.source_id || !data.task_id)
|
|
30704
30897
|
return;
|
|
30705
30898
|
try {
|
|
30706
|
-
await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.handleTaskComment(data.source_id, data.task_id ?? "", data.actor_id, data.delivery_reason
|
|
30899
|
+
await this.consumeTypedDispatch({ dispatchEventId: data.id }, (dispatchEventId) => this.handleTaskComment(data.source_id, data.task_id ?? "", data.actor_id, data.delivery_reason, dispatchEventId), {
|
|
30900
|
+
legacyAck: () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)),
|
|
30901
|
+
clearDedupe: () => this.clearTypedDispatchDedupe(data)
|
|
30902
|
+
});
|
|
30707
30903
|
} catch (err) {
|
|
30708
30904
|
this.opts.log?.error(`task comment dispatch failed for ${data.source_id}: ${String(err)}`);
|
|
30709
30905
|
}
|
|
@@ -30711,7 +30907,10 @@ var ParallAgentGateway = class {
|
|
|
30711
30907
|
if (!data.source_id)
|
|
30712
30908
|
return;
|
|
30713
30909
|
try {
|
|
30714
|
-
await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.handleWikiComment(data.source_id, data.actor_id, data.delivery_reason
|
|
30910
|
+
await this.consumeTypedDispatch({ dispatchEventId: data.id }, (dispatchEventId) => this.handleWikiComment(data.source_id, data.actor_id, data.delivery_reason, dispatchEventId), {
|
|
30911
|
+
legacyAck: () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)),
|
|
30912
|
+
clearDedupe: () => this.clearTypedDispatchDedupe(data)
|
|
30913
|
+
});
|
|
30715
30914
|
} catch (err) {
|
|
30716
30915
|
this.opts.log?.error(`wiki comment dispatch failed for ${data.source_id}: ${String(err)}`);
|
|
30717
30916
|
}
|
|
@@ -30722,7 +30921,10 @@ var ParallAgentGateway = class {
|
|
|
30722
30921
|
await this.consumeTypedDispatch({ dispatchEventId: data.id }, (dispatchEventId) => this.handleTaskDispatch(data.task_id ?? "", data.source_id ?? data.task_id ?? "", {
|
|
30723
30922
|
allowCreator: true,
|
|
30724
30923
|
dispatchEventId
|
|
30725
|
-
}),
|
|
30924
|
+
}), {
|
|
30925
|
+
legacyAck: () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)),
|
|
30926
|
+
clearDedupe: () => this.clearTypedDispatchDedupe(data)
|
|
30927
|
+
});
|
|
30726
30928
|
} catch (err) {
|
|
30727
30929
|
this.opts.log?.error(`task update dispatch failed for ${data.task_id}: ${String(err)}`);
|
|
30728
30930
|
}
|
|
@@ -30730,7 +30932,10 @@ var ParallAgentGateway = class {
|
|
|
30730
30932
|
if (!data.source_id)
|
|
30731
30933
|
return;
|
|
30732
30934
|
try {
|
|
30733
|
-
await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleScheduleFire(data.source_id, data.actor_id
|
|
30935
|
+
await this.consumeTypedDispatch({ dispatchEventId: data.id }, (dispatchEventId) => this.fetchAndHandleScheduleFire(data.source_id, data.actor_id, dispatchEventId), {
|
|
30936
|
+
legacyAck: () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)),
|
|
30937
|
+
clearDedupe: () => this.clearTypedDispatchDedupe(data)
|
|
30938
|
+
});
|
|
30734
30939
|
} catch (err) {
|
|
30735
30940
|
this.opts.log?.error(`schedule fire dispatch failed for ${data.source_id}: ${String(err)}`);
|
|
30736
30941
|
}
|
|
@@ -30738,7 +30943,10 @@ var ParallAgentGateway = class {
|
|
|
30738
30943
|
if (!data.source_id)
|
|
30739
30944
|
return;
|
|
30740
30945
|
try {
|
|
30741
|
-
await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleExternalTriggerRun(data.source_id
|
|
30946
|
+
await this.consumeTypedDispatch({ dispatchEventId: data.id }, (dispatchEventId) => this.fetchAndHandleExternalTriggerRun(data.source_id, dispatchEventId), {
|
|
30947
|
+
legacyAck: () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)),
|
|
30948
|
+
clearDedupe: () => this.clearTypedDispatchDedupe(data)
|
|
30949
|
+
});
|
|
30742
30950
|
} catch (err) {
|
|
30743
30951
|
this.opts.log?.error(`external trigger dispatch failed for ${data.source_id}: ${String(err)}`);
|
|
30744
30952
|
}
|
|
@@ -30746,7 +30954,10 @@ var ParallAgentGateway = class {
|
|
|
30746
30954
|
if (!data.source_id)
|
|
30747
30955
|
return;
|
|
30748
30956
|
try {
|
|
30749
|
-
await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleChannelMessage(data.source_id
|
|
30957
|
+
await this.consumeTypedDispatch({ dispatchEventId: data.id }, (dispatchEventId) => this.fetchAndHandleChannelMessage(data.source_id, dispatchEventId), {
|
|
30958
|
+
legacyAck: () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)),
|
|
30959
|
+
clearDedupe: () => this.clearTypedDispatchDedupe(data)
|
|
30960
|
+
});
|
|
30750
30961
|
} catch (err) {
|
|
30751
30962
|
this.opts.log?.error(`channel message dispatch failed for ${data.source_id}: ${String(err)}`);
|
|
30752
30963
|
}
|
|
@@ -30754,7 +30965,10 @@ var ParallAgentGateway = class {
|
|
|
30754
30965
|
if (!data.source_id)
|
|
30755
30966
|
return;
|
|
30756
30967
|
try {
|
|
30757
|
-
await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleApprovalDecided(data.source_id, data.actor_id, data.chat_id ?? null
|
|
30968
|
+
await this.consumeTypedDispatch({ dispatchEventId: data.id }, (dispatchEventId) => this.fetchAndHandleApprovalDecided(data.source_id, data.actor_id, data.chat_id ?? null, dispatchEventId), {
|
|
30969
|
+
legacyAck: () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)),
|
|
30970
|
+
clearDedupe: () => this.clearTypedDispatchDedupe(data)
|
|
30971
|
+
});
|
|
30758
30972
|
} catch (err) {
|
|
30759
30973
|
this.opts.log?.error(`approval decided dispatch failed for ${data.source_id}: ${String(err)}`);
|
|
30760
30974
|
}
|
|
@@ -30791,6 +31005,44 @@ var ParallAgentGateway = class {
|
|
|
30791
31005
|
this.dispatchedMessages.add(id);
|
|
30792
31006
|
return true;
|
|
30793
31007
|
}
|
|
31008
|
+
/**
|
|
31009
|
+
* Lane currently being dispatched per session — lets external activity
|
|
31010
|
+
* signals renew exactly the caller's lane (renewing all lanes would keep
|
|
31011
|
+
* an unrelated stalled fork's lane leased forever).
|
|
31012
|
+
*/
|
|
31013
|
+
sessionActiveLanes = /* @__PURE__ */ new Map();
|
|
31014
|
+
noteSessionLane(sessionKey, laneKey) {
|
|
31015
|
+
if (laneKey == null)
|
|
31016
|
+
this.sessionActiveLanes.delete(sessionKey);
|
|
31017
|
+
else
|
|
31018
|
+
this.sessionActiveLanes.set(sessionKey, laneKey);
|
|
31019
|
+
}
|
|
31020
|
+
/**
|
|
31021
|
+
* External runtime-activity signal for adapters whose tool activity does
|
|
31022
|
+
* not flow through the RuntimeEvent stream (openclaw hooks call this from
|
|
31023
|
+
* the tool-call lifecycle): renews the session's OWN active ledger lane so
|
|
31024
|
+
* a long tool call cannot outlive the lease and get dethroned mid-turn.
|
|
31025
|
+
* No-op without an active ledger lane for the session.
|
|
31026
|
+
*/
|
|
31027
|
+
touchRuntimeActivity(sessionKey) {
|
|
31028
|
+
if (this.ledgerDisabled)
|
|
31029
|
+
return;
|
|
31030
|
+
const laneKey = this.sessionActiveLanes.get(sessionKey);
|
|
31031
|
+
if (laneKey)
|
|
31032
|
+
this.laneLedger?.renewByKey(laneKey);
|
|
31033
|
+
}
|
|
31034
|
+
/** Sessions whose in-flight turn surfaced a runtime error event. */
|
|
31035
|
+
turnErrorSessions = /* @__PURE__ */ new Set();
|
|
31036
|
+
/**
|
|
31037
|
+
* Consume (read-and-clear) the error marker for sessionKey's last turn.
|
|
31038
|
+
* Feeds complete's turn_outcome so an error turn's lane members are
|
|
31039
|
+
* released for retry instead of no_action-swept (design §3). Consuming
|
|
31040
|
+
* (rather than peeking) keeps one-shot fork session keys from accumulating
|
|
31041
|
+
* in the set forever.
|
|
31042
|
+
*/
|
|
31043
|
+
consumeTurnError(sessionKey) {
|
|
31044
|
+
return this.turnErrorSessions.delete(sessionKey);
|
|
31045
|
+
}
|
|
30794
31046
|
async emitDispatchReceived(event) {
|
|
30795
31047
|
const sourceType = event.ackSourceType ?? (event.type === "task" ? "task_activity" : "message");
|
|
30796
31048
|
const sourceId = event.ackSourceId ?? event.messageId;
|
|
@@ -30799,6 +31051,20 @@ var ParallAgentGateway = class {
|
|
|
30799
31051
|
source_id: sourceId
|
|
30800
31052
|
});
|
|
30801
31053
|
}
|
|
31054
|
+
/**
|
|
31055
|
+
* Sticky: the server answered a by-id complete with 400 (predates the
|
|
31056
|
+
* form). Typed resolution falls back to the legacy ack for the rest of
|
|
31057
|
+
* the process lifetime.
|
|
31058
|
+
*/
|
|
31059
|
+
typedByIdCompleteUnsupported = false;
|
|
31060
|
+
// Typed-face ledger helpers live in gateway-lane-flow.ts; thin delegates
|
|
31061
|
+
// keep the site code and tests on the class surface.
|
|
31062
|
+
typedLedgerEventIds(events) {
|
|
31063
|
+
return typedLedgerEventIds(this.laneFlowHost(), events);
|
|
31064
|
+
}
|
|
31065
|
+
resolveDispatchByID(dispatchEventId, lane) {
|
|
31066
|
+
return resolveDispatchByID(this.laneFlowHost(), dispatchEventId, lane);
|
|
31067
|
+
}
|
|
30802
31068
|
/** True when this event's lifecycle is owned by the dispatch lane ledger. */
|
|
30803
31069
|
usesLaneLedger(event) {
|
|
30804
31070
|
return this.laneLedger != null && !this.ledgerDisabled && this.laneLedger.handles(event);
|
|
@@ -30829,15 +31095,16 @@ var ParallAgentGateway = class {
|
|
|
30829
31095
|
dispatchLaneGroup(opts) {
|
|
30830
31096
|
return dispatchLaneGroup(this.laneFlowHost(), opts);
|
|
30831
31097
|
}
|
|
30832
|
-
consumeTypedDispatch(ref, run,
|
|
30833
|
-
return consumeTypedDispatch(this.laneFlowHost(), ref, run,
|
|
31098
|
+
consumeTypedDispatch(ref, run, hooks) {
|
|
31099
|
+
return consumeTypedDispatch(this.laneFlowHost(), ref, run, hooks);
|
|
30834
31100
|
}
|
|
30835
|
-
//
|
|
30836
|
-
//
|
|
30837
|
-
//
|
|
30838
|
-
//
|
|
30839
|
-
//
|
|
30840
|
-
//
|
|
31101
|
+
// Legacy administrative ack (ledger-disabled fallback only). Typed
|
|
31102
|
+
// completion must wait until the ack has either committed or failed.
|
|
31103
|
+
// Errors stay best-effort: a failed ack leaves the row received, so
|
|
31104
|
+
// Complete releases and re-drives it safely. The boolean outcome feeds the
|
|
31105
|
+
// typed-consume backoff — an ack that failed must count as a failed
|
|
31106
|
+
// consume, or an ack outage would clear the backoff entry and let the
|
|
31107
|
+
// release re-drive spin at wire speed.
|
|
30841
31108
|
ackDispatchEvent(dispatchEventId, onFailure) {
|
|
30842
31109
|
return this.opts.client.ackDispatchByID(this.opts.config.org_id, dispatchEventId).then(() => true, (err) => {
|
|
30843
31110
|
onFailure?.();
|
|
@@ -30845,6 +31112,9 @@ var ParallAgentGateway = class {
|
|
|
30845
31112
|
return false;
|
|
30846
31113
|
});
|
|
30847
31114
|
}
|
|
31115
|
+
// PARITY: this switch and gateway-lane-flow's clearTypedDedupeForEvent must
|
|
31116
|
+
// handle the same typed source families — extend BOTH when adding a typed
|
|
31117
|
+
// event type (same dedupe entries, keyed from different event shapes).
|
|
30848
31118
|
clearTypedDispatchDedupe(item) {
|
|
30849
31119
|
switch (item.event_type) {
|
|
30850
31120
|
case "task_assign":
|
|
@@ -31140,6 +31410,7 @@ var ParallAgentGateway = class {
|
|
|
31140
31410
|
this.pendingRestartNotification = null;
|
|
31141
31411
|
}
|
|
31142
31412
|
resetDispatchMetrics(sessionKey);
|
|
31413
|
+
this.turnErrorSessions.delete(sessionKey);
|
|
31143
31414
|
return runWithSessionKey(sessionKey, async () => {
|
|
31144
31415
|
let dispatchSpan = null;
|
|
31145
31416
|
setSessionChatId(sessionKey, event.targetId);
|
|
@@ -31247,6 +31518,9 @@ var ParallAgentGateway = class {
|
|
|
31247
31518
|
} else if (runtimeEvent.type === "tool_result" && pendingSendCallIds.delete(runtimeEvent.callId)) {
|
|
31248
31519
|
recordMessageSend(sessionKey, !runtimeEvent.error);
|
|
31249
31520
|
}
|
|
31521
|
+
if (runtimeEvent.type === "error") {
|
|
31522
|
+
this.turnErrorSessions.add(sessionKey);
|
|
31523
|
+
}
|
|
31250
31524
|
await this.createRuntimeStep(binding.agentSessionId, event, runtimeEvent, stepIdFilePath, contextFilePath, laneContextFilePath2);
|
|
31251
31525
|
}
|
|
31252
31526
|
if (!binding) {
|
|
@@ -31394,16 +31668,32 @@ var ParallAgentGateway = class {
|
|
|
31394
31668
|
body: buildForkScopePrefix(last) + buildEventBody(last),
|
|
31395
31669
|
earlier,
|
|
31396
31670
|
captureText: batchText,
|
|
31397
|
-
|
|
31671
|
+
// Per-LANE residue check (parity with the main-buffer path):
|
|
31672
|
+
// the fork queue can hold several lanes (channel + thread of
|
|
31673
|
+
// the same chat). A whole-queue check would defer THIS lane's
|
|
31674
|
+
// complete behind another lane's items and never revisit it —
|
|
31675
|
+
// its members would sit received until lease expiry.
|
|
31676
|
+
hasMoreLocal: () => fork.queue.some((it) => this.dispatchGroupKey(it.event) === this.dispatchGroupKey(last))
|
|
31398
31677
|
});
|
|
31399
31678
|
if (outcome === "foreign") {
|
|
31400
31679
|
for (const item of items)
|
|
31401
31680
|
item.resolve(false);
|
|
31402
31681
|
break;
|
|
31403
31682
|
}
|
|
31683
|
+
if (outcome === "failed") {
|
|
31684
|
+
for (const item of items)
|
|
31685
|
+
item.resolve(false);
|
|
31686
|
+
continue;
|
|
31687
|
+
}
|
|
31404
31688
|
dispatched = outcome === "dispatched";
|
|
31405
31689
|
} else {
|
|
31406
31690
|
dispatched = await this.runDispatch(last, fork.fork.sessionKey, buildForkScopePrefix(last) + buildEventBody(last), earlier, batchText);
|
|
31691
|
+
if (dispatched && this.typedLedgerEventIds(events) && this.consumeTurnError(fork.fork.sessionKey)) {
|
|
31692
|
+
this.opts.log?.info(`typed fork turn for ${last.messageId} surfaced a runtime error \u2014 releasing for retry`);
|
|
31693
|
+
for (const item of items)
|
|
31694
|
+
item.resolve(false);
|
|
31695
|
+
continue;
|
|
31696
|
+
}
|
|
31407
31697
|
}
|
|
31408
31698
|
if (!dispatched) {
|
|
31409
31699
|
for (const item of items) {
|
|
@@ -31434,6 +31724,7 @@ var ParallAgentGateway = class {
|
|
|
31434
31724
|
remaining.resolve(false);
|
|
31435
31725
|
}
|
|
31436
31726
|
} finally {
|
|
31727
|
+
this.turnErrorSessions.delete(fork.fork.sessionKey);
|
|
31437
31728
|
if (fork.deadlineTimer) {
|
|
31438
31729
|
clearTimeout(fork.deadlineTimer);
|
|
31439
31730
|
fork.deadlineTimer = null;
|
|
@@ -31546,15 +31837,22 @@ var ParallAgentGateway = class {
|
|
|
31546
31837
|
this.dispatchState.pendingForkResults.unshift(...pendingFork);
|
|
31547
31838
|
continue;
|
|
31548
31839
|
}
|
|
31840
|
+
if (outcome === "failed") {
|
|
31841
|
+
this.dispatchState.pendingForkResults.unshift(...pendingFork);
|
|
31842
|
+
continue;
|
|
31843
|
+
}
|
|
31549
31844
|
continue;
|
|
31550
31845
|
}
|
|
31551
|
-
|
|
31552
|
-
|
|
31553
|
-
|
|
31554
|
-
|
|
31555
|
-
|
|
31556
|
-
|
|
31557
|
-
|
|
31846
|
+
const typedRefs = this.typedLedgerEventIds(events);
|
|
31847
|
+
if (!typedRefs) {
|
|
31848
|
+
try {
|
|
31849
|
+
await this.emitDispatchReceived(event);
|
|
31850
|
+
} catch (err) {
|
|
31851
|
+
this.opts.log?.warn?.(`mark-received failed for buffered dispatch, leaving unacked for retry: ${String(err)}`);
|
|
31852
|
+
this.dispatchState.mainBuffer.unshift(...events);
|
|
31853
|
+
this.dispatchState.pendingForkResults.unshift(...pendingFork);
|
|
31854
|
+
break;
|
|
31855
|
+
}
|
|
31558
31856
|
}
|
|
31559
31857
|
const dispatched = await this.runDispatch(event, this.opts.runtimeKey, forkPrefix + buildEventBody(event), earlier);
|
|
31560
31858
|
if (!dispatched) {
|
|
@@ -31562,6 +31860,10 @@ var ParallAgentGateway = class {
|
|
|
31562
31860
|
this.dispatchState.pendingForkResults.unshift(...pendingFork);
|
|
31563
31861
|
break;
|
|
31564
31862
|
}
|
|
31863
|
+
if (typedRefs) {
|
|
31864
|
+
await settleDrainedTypedGroup(this.laneFlowHost(), events, typedRefs, this.consumeTurnError(this.opts.runtimeKey));
|
|
31865
|
+
continue;
|
|
31866
|
+
}
|
|
31565
31867
|
for (const bufferedEvent of events) {
|
|
31566
31868
|
const sourceType = bufferedEvent.ackSourceType ?? (bufferedEvent.type === "task" ? "task_activity" : "message");
|
|
31567
31869
|
const sourceId = bufferedEvent.ackSourceId ?? bufferedEvent.messageId;
|
|
@@ -31621,20 +31923,27 @@ var ParallAgentGateway = class {
|
|
|
31621
31923
|
}
|
|
31622
31924
|
return outcome === "dispatched";
|
|
31623
31925
|
}
|
|
31624
|
-
|
|
31625
|
-
|
|
31626
|
-
|
|
31627
|
-
|
|
31628
|
-
|
|
31629
|
-
|
|
31630
|
-
|
|
31631
|
-
|
|
31632
|
-
|
|
31633
|
-
|
|
31926
|
+
const typedRefs = this.typedLedgerEventIds([event]);
|
|
31927
|
+
if (!typedRefs) {
|
|
31928
|
+
try {
|
|
31929
|
+
await this.emitDispatchReceived(event);
|
|
31930
|
+
} catch (err) {
|
|
31931
|
+
this.opts.log?.warn?.(`mark-received failed, leaving unacked for retry: ${String(err)}`);
|
|
31932
|
+
this.dispatchState.mainDispatching = false;
|
|
31933
|
+
this.dispatchState.mainCurrentTargetId = void 0;
|
|
31934
|
+
this.mainCurrentGroupKey = void 0;
|
|
31935
|
+
this.dispatchState.mainPreDispatchBranchPoint = void 0;
|
|
31936
|
+
this.dispatchState.pendingForkResults.unshift(...pendingFork);
|
|
31937
|
+
return false;
|
|
31938
|
+
}
|
|
31634
31939
|
}
|
|
31635
31940
|
let dispatched = false;
|
|
31636
31941
|
try {
|
|
31637
31942
|
dispatched = await this.runDispatch(event, this.opts.runtimeKey, forkPrefix + buildEventBody(event));
|
|
31943
|
+
if (dispatched && typedRefs && this.consumeTurnError(this.opts.runtimeKey)) {
|
|
31944
|
+
this.opts.log?.info(`typed dispatch turn for ${event.messageId} surfaced a runtime error \u2014 releasing for retry`);
|
|
31945
|
+
dispatched = false;
|
|
31946
|
+
}
|
|
31638
31947
|
if (!dispatched) {
|
|
31639
31948
|
this.dispatchState.pendingForkResults.unshift(...pendingFork);
|
|
31640
31949
|
}
|
|
@@ -31649,7 +31958,7 @@ var ParallAgentGateway = class {
|
|
|
31649
31958
|
}
|
|
31650
31959
|
this.dispatchState.mainBuffer.push(event);
|
|
31651
31960
|
if (this.usesLaneLedger(event)) {
|
|
31652
|
-
if (this.mainCurrentGroupKey === this.dispatchGroupKey(event) && await this.laneLedger?.steerLive(event) && await this.opts.dispatchAdapter.enqueueDuringDispatch
|
|
31961
|
+
if (this.mainCurrentGroupKey === this.dispatchGroupKey(event) && this.opts.dispatchAdapter.enqueueDuringDispatch != null && await this.laneLedger?.steerLive(event) && await this.opts.dispatchAdapter.enqueueDuringDispatch(this.opts.runtimeKey, buildEventBody(event))) {
|
|
31653
31962
|
this.opts.log?.info(`steer folded+injected for ${event.messageId} (will drain for bookkeeping)`);
|
|
31654
31963
|
}
|
|
31655
31964
|
} else if (this.dispatchState.mainCurrentTargetId === event.targetId && await this.opts.dispatchAdapter.enqueueDuringDispatch?.(this.opts.runtimeKey, buildEventBody(event))) {
|
|
@@ -31676,12 +31985,14 @@ var ParallAgentGateway = class {
|
|
|
31676
31985
|
this.dispatchState.mainBuffer.push(event);
|
|
31677
31986
|
return false;
|
|
31678
31987
|
}
|
|
31679
|
-
|
|
31680
|
-
|
|
31681
|
-
|
|
31682
|
-
|
|
31683
|
-
|
|
31684
|
-
|
|
31988
|
+
if (!this.usesLaneLedger(event) && !this.typedLedgerEventIds([event])) {
|
|
31989
|
+
try {
|
|
31990
|
+
await this.emitDispatchReceived(event);
|
|
31991
|
+
} catch (err) {
|
|
31992
|
+
this.opts.log?.warn?.(`mark-received failed for fork dispatch, leaving unacked for retry: ${String(err)}`);
|
|
31993
|
+
this.dispatchState.mainBuffer.push(event);
|
|
31994
|
+
return false;
|
|
31995
|
+
}
|
|
31685
31996
|
}
|
|
31686
31997
|
const fork = await this.opts.dispatchAdapter.forkSession({
|
|
31687
31998
|
sessionKey: this.opts.runtimeKey,
|
|
@@ -31855,7 +32166,10 @@ var ParallAgentGateway = class {
|
|
|
31855
32166
|
return;
|
|
31856
32167
|
await this.consumeTypedDispatch({ dispatchEventId: item.id }, (dispatchEventId) => this.handleTaskDispatch(item.task_id ?? "", item.source_id ?? item.task_id ?? "", {
|
|
31857
32168
|
dispatchEventId
|
|
31858
|
-
}),
|
|
32169
|
+
}), {
|
|
32170
|
+
legacyAck: (dispatchEventId) => this.ackDispatchEvent(dispatchEventId ?? item.id, () => this.clearTypedDispatchDedupe(item)),
|
|
32171
|
+
clearDedupe: () => this.clearTypedDispatchDedupe(item)
|
|
32172
|
+
});
|
|
31859
32173
|
}
|
|
31860
32174
|
consumeMessageWorkItem(item) {
|
|
31861
32175
|
return consumeMessageWorkItem(this.laneFlowHost(), item);
|
|
@@ -31916,7 +32230,7 @@ var ParallAgentGateway = class {
|
|
|
31916
32230
|
}
|
|
31917
32231
|
return this.handleTaskAssignment(task, ackSourceId, opts.dispatchEventId);
|
|
31918
32232
|
}
|
|
31919
|
-
async handleTaskComment(commentId, taskId, actorId, deliveryReason) {
|
|
32233
|
+
async handleTaskComment(commentId, taskId, actorId, deliveryReason, dispatchEventId) {
|
|
31920
32234
|
if (this.shuttingDown)
|
|
31921
32235
|
return false;
|
|
31922
32236
|
const dedupeKey = `comment:${commentId}`;
|
|
@@ -31972,7 +32286,8 @@ var ParallAgentGateway = class {
|
|
|
31972
32286
|
body: parts.join("\n"),
|
|
31973
32287
|
deliveryReason: deliveryReason ?? void 0,
|
|
31974
32288
|
ackSourceType: "comment",
|
|
31975
|
-
ackSourceId: commentId
|
|
32289
|
+
ackSourceId: commentId,
|
|
32290
|
+
dispatchEventId
|
|
31976
32291
|
};
|
|
31977
32292
|
let dispatched;
|
|
31978
32293
|
try {
|
|
@@ -31986,7 +32301,7 @@ var ParallAgentGateway = class {
|
|
|
31986
32301
|
}
|
|
31987
32302
|
return dispatched;
|
|
31988
32303
|
}
|
|
31989
|
-
async handleWikiComment(commentId, actorId, deliveryReason) {
|
|
32304
|
+
async handleWikiComment(commentId, actorId, deliveryReason, dispatchEventId) {
|
|
31990
32305
|
if (this.shuttingDown)
|
|
31991
32306
|
return false;
|
|
31992
32307
|
const dedupeKey = `comment:${commentId}`;
|
|
@@ -32032,7 +32347,8 @@ var ParallAgentGateway = class {
|
|
|
32032
32347
|
deliveryReason: deliveryReason ?? void 0,
|
|
32033
32348
|
replyTargetUri: comment.target_uri,
|
|
32034
32349
|
ackSourceType: "comment",
|
|
32035
|
-
ackSourceId: commentId
|
|
32350
|
+
ackSourceId: commentId,
|
|
32351
|
+
dispatchEventId
|
|
32036
32352
|
};
|
|
32037
32353
|
let dispatched;
|
|
32038
32354
|
try {
|
|
@@ -32054,7 +32370,7 @@ var ParallAgentGateway = class {
|
|
|
32054
32370
|
* access to already-delivered run snapshots, and the runtime must not crash
|
|
32055
32371
|
* or retry forever in that case.
|
|
32056
32372
|
*/
|
|
32057
|
-
async fetchAndHandleScheduleFire(runId, actorId) {
|
|
32373
|
+
async fetchAndHandleScheduleFire(runId, actorId, dispatchEventId) {
|
|
32058
32374
|
let run = null;
|
|
32059
32375
|
try {
|
|
32060
32376
|
run = await this.opts.client.getScheduleRun(this.opts.config.org_id, runId);
|
|
@@ -32069,9 +32385,9 @@ var ParallAgentGateway = class {
|
|
|
32069
32385
|
}
|
|
32070
32386
|
if (!run)
|
|
32071
32387
|
return true;
|
|
32072
|
-
return this.handleScheduleFire(run, actorId);
|
|
32388
|
+
return this.handleScheduleFire(run, actorId, dispatchEventId);
|
|
32073
32389
|
}
|
|
32074
|
-
async handleScheduleFire(run, actorId) {
|
|
32390
|
+
async handleScheduleFire(run, actorId, dispatchEventId) {
|
|
32075
32391
|
if (this.shuttingDown)
|
|
32076
32392
|
return false;
|
|
32077
32393
|
const dedupeKey = `schedule_run:${run.id}`;
|
|
@@ -32094,7 +32410,8 @@ var ParallAgentGateway = class {
|
|
|
32094
32410
|
scheduledFireAt: run.scheduled_fire_at,
|
|
32095
32411
|
attachedUri: run.fired_attached_uri ?? void 0,
|
|
32096
32412
|
ackSourceType: "schedule_run",
|
|
32097
|
-
ackSourceId: run.id
|
|
32413
|
+
ackSourceId: run.id,
|
|
32414
|
+
dispatchEventId
|
|
32098
32415
|
};
|
|
32099
32416
|
let dispatched;
|
|
32100
32417
|
try {
|
|
@@ -32108,7 +32425,7 @@ var ParallAgentGateway = class {
|
|
|
32108
32425
|
}
|
|
32109
32426
|
return dispatched;
|
|
32110
32427
|
}
|
|
32111
|
-
async fetchAndHandleExternalTriggerRun(runId) {
|
|
32428
|
+
async fetchAndHandleExternalTriggerRun(runId, dispatchEventId) {
|
|
32112
32429
|
let run = null;
|
|
32113
32430
|
try {
|
|
32114
32431
|
run = await this.opts.client.getExternalTriggerRun(this.opts.config.org_id, runId);
|
|
@@ -32123,14 +32440,14 @@ var ParallAgentGateway = class {
|
|
|
32123
32440
|
}
|
|
32124
32441
|
if (!run)
|
|
32125
32442
|
return true;
|
|
32126
|
-
return this.handleExternalTriggerRun(run);
|
|
32443
|
+
return this.handleExternalTriggerRun(run, dispatchEventId);
|
|
32127
32444
|
}
|
|
32128
32445
|
// fetchAndHandleChannelMessage resolves a channel_message dispatch to its
|
|
32129
32446
|
// durable ChannelMessage + conversation and hands it to the inbound
|
|
32130
32447
|
// pipeline. targetId = the ChannelConversation id, so per-conversation
|
|
32131
32448
|
// multi-turn continuity rides the same per-target session mechanics as
|
|
32132
32449
|
// chats. Design: docs/engineering-design/external-im-channel-design.md.
|
|
32133
|
-
async fetchAndHandleChannelMessage(messageId) {
|
|
32450
|
+
async fetchAndHandleChannelMessage(messageId, dispatchEventId) {
|
|
32134
32451
|
if (this.shuttingDown)
|
|
32135
32452
|
return false;
|
|
32136
32453
|
const claimKey = `channel_message:${messageId}`;
|
|
@@ -32183,7 +32500,8 @@ var ParallAgentGateway = class {
|
|
|
32183
32500
|
channelExternalMessageId: msg.external_message_id,
|
|
32184
32501
|
channelCliCapable: cliCapable,
|
|
32185
32502
|
ackSourceType: "channel_message",
|
|
32186
|
-
ackSourceId: msg.id
|
|
32503
|
+
ackSourceId: msg.id,
|
|
32504
|
+
dispatchEventId
|
|
32187
32505
|
};
|
|
32188
32506
|
let dispatched;
|
|
32189
32507
|
try {
|
|
@@ -32197,7 +32515,7 @@ var ParallAgentGateway = class {
|
|
|
32197
32515
|
}
|
|
32198
32516
|
return dispatched;
|
|
32199
32517
|
}
|
|
32200
|
-
async handleExternalTriggerRun(run) {
|
|
32518
|
+
async handleExternalTriggerRun(run, dispatchEventId) {
|
|
32201
32519
|
if (this.shuttingDown)
|
|
32202
32520
|
return false;
|
|
32203
32521
|
const dedupeKey = `external_trigger_run:${run.id}`;
|
|
@@ -32222,7 +32540,8 @@ var ParallAgentGateway = class {
|
|
|
32222
32540
|
externalIngressEventType: run.ingress_event_type || void 0,
|
|
32223
32541
|
attachedUri,
|
|
32224
32542
|
ackSourceType: "external_trigger_run",
|
|
32225
|
-
ackSourceId: run.id
|
|
32543
|
+
ackSourceId: run.id,
|
|
32544
|
+
dispatchEventId
|
|
32226
32545
|
};
|
|
32227
32546
|
let dispatched;
|
|
32228
32547
|
try {
|
|
@@ -32236,7 +32555,7 @@ var ParallAgentGateway = class {
|
|
|
32236
32555
|
}
|
|
32237
32556
|
return dispatched;
|
|
32238
32557
|
}
|
|
32239
|
-
async fetchAndHandleApprovalDecided(approvalId, actorId, chatId) {
|
|
32558
|
+
async fetchAndHandleApprovalDecided(approvalId, actorId, chatId, dispatchEventId) {
|
|
32240
32559
|
let approval = null;
|
|
32241
32560
|
try {
|
|
32242
32561
|
approval = await this.opts.client.getApproval(approvalId);
|
|
@@ -32267,7 +32586,12 @@ var ParallAgentGateway = class {
|
|
|
32267
32586
|
senderId: actorId ?? approval.decided_by ?? "system",
|
|
32268
32587
|
senderName: "approver",
|
|
32269
32588
|
messageId: approval.id,
|
|
32270
|
-
body
|
|
32589
|
+
body,
|
|
32590
|
+
// The WorkItem's real source pair. Without it the legacy fallback
|
|
32591
|
+
// guessed ('message', approval_id) — a pair no row matches.
|
|
32592
|
+
ackSourceType: "approval",
|
|
32593
|
+
ackSourceId: approval.id,
|
|
32594
|
+
dispatchEventId
|
|
32271
32595
|
};
|
|
32272
32596
|
let dispatched;
|
|
32273
32597
|
try {
|
|
@@ -32314,7 +32638,17 @@ var ParallAgentGateway = class {
|
|
|
32314
32638
|
break;
|
|
32315
32639
|
if (overflowMode && processed >= CATCHUP_MAX) {
|
|
32316
32640
|
try {
|
|
32317
|
-
|
|
32641
|
+
if (this.laneLedger && !this.ledgerDisabled && !this.typedByIdCompleteUnsupported) {
|
|
32642
|
+
const outcome = await this.resolveDispatchByID(item.id);
|
|
32643
|
+
if (outcome === "unsupported") {
|
|
32644
|
+
this.typedByIdCompleteUnsupported = true;
|
|
32645
|
+
await this.opts.client.ackDispatchByID(this.opts.config.org_id, item.id);
|
|
32646
|
+
} else if (outcome === "failed") {
|
|
32647
|
+
throw new Error("by-id complete failed");
|
|
32648
|
+
}
|
|
32649
|
+
} else {
|
|
32650
|
+
await this.opts.client.ackDispatchByID(this.opts.config.org_id, item.id);
|
|
32651
|
+
}
|
|
32318
32652
|
skipped++;
|
|
32319
32653
|
const key = item.event_type;
|
|
32320
32654
|
skippedByType.set(key, (skippedByType.get(key) ?? 0) + 1);
|
|
@@ -32325,12 +32659,15 @@ var ParallAgentGateway = class {
|
|
|
32325
32659
|
}
|
|
32326
32660
|
processed++;
|
|
32327
32661
|
try {
|
|
32328
|
-
const
|
|
32662
|
+
const typedHooks = {
|
|
32663
|
+
legacyAck: () => this.ackDispatchEvent(item.id, () => this.clearTypedDispatchDedupe(item)),
|
|
32664
|
+
clearDedupe: () => this.clearTypedDispatchDedupe(item)
|
|
32665
|
+
};
|
|
32329
32666
|
if (item.event_type === "task_assign" && item.task_id) {
|
|
32330
32667
|
try {
|
|
32331
32668
|
await this.consumeTypedDispatch({ dispatchEventId: item.id }, (dispatchEventId) => this.handleTaskDispatch(item.task_id ?? "", item.source_id ?? item.task_id ?? "", {
|
|
32332
32669
|
dispatchEventId
|
|
32333
|
-
}),
|
|
32670
|
+
}), typedHooks);
|
|
32334
32671
|
} catch (err) {
|
|
32335
32672
|
this.opts.log?.warn(`catch-up task fetch failed for ${item.task_id}, leaving pending: ${String(err)}`);
|
|
32336
32673
|
continue;
|
|
@@ -32340,23 +32677,23 @@ var ParallAgentGateway = class {
|
|
|
32340
32677
|
await this.consumeTypedDispatch({ dispatchEventId: item.id }, (dispatchEventId) => this.handleTaskDispatch(item.task_id ?? "", item.source_id ?? item.task_id ?? "", {
|
|
32341
32678
|
allowCreator: true,
|
|
32342
32679
|
dispatchEventId
|
|
32343
|
-
}),
|
|
32680
|
+
}), typedHooks);
|
|
32344
32681
|
} catch (err) {
|
|
32345
32682
|
this.opts.log?.warn(`catch-up task fetch failed for ${item.task_id}, leaving pending: ${String(err)}`);
|
|
32346
32683
|
continue;
|
|
32347
32684
|
}
|
|
32348
32685
|
} else if (item.event_type === "task_comment" && item.source_id && item.task_id) {
|
|
32349
|
-
await this.consumeTypedDispatch({ dispatchEventId: item.id }, () => this.handleTaskComment(item.source_id, item.task_id ?? "", item.actor_id, item.delivery_reason),
|
|
32686
|
+
await this.consumeTypedDispatch({ dispatchEventId: item.id }, (dispatchEventId) => this.handleTaskComment(item.source_id, item.task_id ?? "", item.actor_id, item.delivery_reason, dispatchEventId), typedHooks);
|
|
32350
32687
|
} else if (item.event_type === "wiki_comment" && item.source_id) {
|
|
32351
|
-
await this.consumeTypedDispatch({ dispatchEventId: item.id }, () => this.handleWikiComment(item.source_id, item.actor_id, item.delivery_reason),
|
|
32688
|
+
await this.consumeTypedDispatch({ dispatchEventId: item.id }, (dispatchEventId) => this.handleWikiComment(item.source_id, item.actor_id, item.delivery_reason, dispatchEventId), typedHooks);
|
|
32352
32689
|
} else if (item.event_type === "schedule.fire" && item.source_id) {
|
|
32353
|
-
await this.consumeTypedDispatch({ dispatchEventId: item.id }, () => this.fetchAndHandleScheduleFire(item.source_id, item.actor_id),
|
|
32690
|
+
await this.consumeTypedDispatch({ dispatchEventId: item.id }, (dispatchEventId) => this.fetchAndHandleScheduleFire(item.source_id, item.actor_id, dispatchEventId), typedHooks);
|
|
32354
32691
|
} else if (item.event_type === "external_trigger" && item.source_id) {
|
|
32355
|
-
await this.consumeTypedDispatch({ dispatchEventId: item.id }, () => this.fetchAndHandleExternalTriggerRun(item.source_id),
|
|
32692
|
+
await this.consumeTypedDispatch({ dispatchEventId: item.id }, (dispatchEventId) => this.fetchAndHandleExternalTriggerRun(item.source_id, dispatchEventId), typedHooks);
|
|
32356
32693
|
} else if (item.event_type === "channel_message" && item.source_id) {
|
|
32357
|
-
await this.consumeTypedDispatch({ dispatchEventId: item.id }, () => this.fetchAndHandleChannelMessage(item.source_id),
|
|
32694
|
+
await this.consumeTypedDispatch({ dispatchEventId: item.id }, (dispatchEventId) => this.fetchAndHandleChannelMessage(item.source_id, dispatchEventId), typedHooks);
|
|
32358
32695
|
} else if (item.event_type === "approval_decided" && item.source_id) {
|
|
32359
|
-
await this.consumeTypedDispatch({ dispatchEventId: item.id }, () => this.fetchAndHandleApprovalDecided(item.source_id, item.actor_id, item.chat_id ?? null),
|
|
32696
|
+
await this.consumeTypedDispatch({ dispatchEventId: item.id }, (dispatchEventId) => this.fetchAndHandleApprovalDecided(item.source_id, item.actor_id, item.chat_id ?? null, dispatchEventId), typedHooks);
|
|
32360
32697
|
} else if (item.event_type === "message" && item.source_id && item.chat_id) {
|
|
32361
32698
|
await this.consumeMessageWorkItem({
|
|
32362
32699
|
id: item.id,
|
|
@@ -33054,16 +33391,32 @@ parall tasks list --assignee-id prll://usr_xxx # first page only (default 20)
|
|
|
33054
33391
|
parall tasks subtasks prll://tsk_xxx # children of a single parent task
|
|
33055
33392
|
|
|
33056
33393
|
# Create a task (add --parent-id to make it a SUBTASK of another task)
|
|
33057
|
-
parall tasks create --title "Task title" [--assignee-id prll://usr_xxx] [--parent-id prll://tsk_xxx] [--project-id prll://prj_xxx]
|
|
33394
|
+
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]
|
|
33395
|
+
|
|
33396
|
+
# Update task status \u2014 add --placement end so the task lands at the end of
|
|
33397
|
+
# its NEW status column (a bare --status keeps the old column's sort_order)
|
|
33398
|
+
parall tasks update prll://tsk_xxx --status in_progress --placement end
|
|
33399
|
+
parall tasks update prll://tsk_xxx --status done --placement end
|
|
33058
33400
|
|
|
33059
|
-
#
|
|
33060
|
-
parall tasks update prll://tsk_xxx --
|
|
33061
|
-
parall tasks update prll://tsk_xxx --
|
|
33401
|
+
# Due date \u2014 a plain YYYY-MM-DD date (no timestamps); "none" clears it
|
|
33402
|
+
parall tasks update prll://tsk_xxx --due-date 2026-08-01
|
|
33403
|
+
parall tasks update prll://tsk_xxx --due-date none
|
|
33404
|
+
|
|
33405
|
+
# Move a task to the end of its status column
|
|
33406
|
+
parall tasks update prll://tsk_xxx --placement end
|
|
33062
33407
|
|
|
33063
33408
|
# Add a comment
|
|
33064
33409
|
parall tasks comments add prll://tsk_xxx --body "Progress update..."
|
|
33065
33410
|
\`\`\`
|
|
33066
33411
|
|
|
33412
|
+
Ordering: to append a task to the end of a status column, always use
|
|
33413
|
+
\`--placement end\` \u2014 the server resolves the position atomically. This
|
|
33414
|
+
includes status changes: a bare \`--status\` keeps the task's old
|
|
33415
|
+
\`sort_order\`, which may collide inside the new column. Do NOT compute a
|
|
33416
|
+
\`sort_order\` value yourself from listed tasks (your view may be stale or
|
|
33417
|
+
partial). \`--sort-order\` is only for pinpoint insertion between two cards
|
|
33418
|
+
you just listed, and it cannot be combined with \`--placement\`.
|
|
33419
|
+
|
|
33067
33420
|
Subtasks are just tasks with a parent: create one with \`tasks create --parent-id\`,
|
|
33068
33421
|
re-parent with \`tasks update --parent-id\`, list a parent's children with
|
|
33069
33422
|
\`tasks subtasks\`. \`tasks list\` without \`--parent-id\` already returns both
|
|
@@ -33098,7 +33451,7 @@ watcher.
|
|
|
33098
33451
|
When you receive \`[Event: task.assigned]\`:
|
|
33099
33452
|
|
|
33100
33453
|
1. Acknowledge with a comment: \`tasks comments add prll://tsk_xxx --body "On it"\`
|
|
33101
|
-
2. Update status: \`tasks update prll://tsk_xxx --status in_progress\`
|
|
33454
|
+
2. Update status: \`tasks update prll://tsk_xxx --status in_progress --placement end\`
|
|
33102
33455
|
3. Do the work
|
|
33103
33456
|
4. Report results in a comment. If a gate remains \u2014 review, merge, deploy,
|
|
33104
33457
|
requester acceptance \u2014 set \`in_review\` and name the gate; set \`done\`
|
|
@@ -33344,7 +33697,7 @@ parall schedules create \\
|
|
|
33344
33697
|
--run-at <FUTURE_RFC3339_TIME>
|
|
33345
33698
|
\`\`\`
|
|
33346
33699
|
|
|
33347
|
-
\`--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 \u2014 when that resource is archived or deleted, the schedule auto-cancels (\`
|
|
33700
|
+
\`--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 \u2014 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).
|
|
33348
33701
|
|
|
33349
33702
|
### Reminders for someone else
|
|
33350
33703
|
|