@parall/daemon 1.43.0 → 1.44.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 +120 -11
- package/bundle/parall-codex-agent.js +120 -11
- package/bundle/parall-daemon.js +4 -1
- package/package.json +6 -6
package/bundle/manifest.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.
|
|
3
|
-
"built_at": "2026-07-
|
|
2
|
+
"version": "1.44.0",
|
|
3
|
+
"built_at": "2026-07-14T07:01:33.876Z",
|
|
4
4
|
"min_node_version": "20.0.0",
|
|
5
5
|
"files": {
|
|
6
6
|
"bb-browser-daemon.js": {
|
|
@@ -16,29 +16,29 @@
|
|
|
16
16
|
"size": 18
|
|
17
17
|
},
|
|
18
18
|
"parall-browser-pod.js": {
|
|
19
|
-
"sha256": "
|
|
20
|
-
"size":
|
|
19
|
+
"sha256": "81dbdef2606b3295d94101d672f514f6fab6d7c105716b4fe9f917bb9b404b1a",
|
|
20
|
+
"size": 1699775
|
|
21
21
|
},
|
|
22
22
|
"parall-channel-exec.js": {
|
|
23
23
|
"sha256": "32c438151df40ccda7491c57bfa0cd9d6c4d762755353c6c751d5793af57b971",
|
|
24
24
|
"size": 7097
|
|
25
25
|
},
|
|
26
26
|
"parall-claude-agent.js": {
|
|
27
|
-
"sha256": "
|
|
28
|
-
"size":
|
|
27
|
+
"sha256": "f7dadfab076bcb55a7aea0d79fdd59b93200dad8c8ef674ac1879e53720fd946",
|
|
28
|
+
"size": 1644021
|
|
29
29
|
},
|
|
30
30
|
"parall-codex-agent.js": {
|
|
31
|
-
"sha256": "
|
|
32
|
-
"size":
|
|
31
|
+
"sha256": "4f77525ca21caa389106feb7aef1f630613ec91dedbf366ed52fc3c70b391607",
|
|
32
|
+
"size": 1681351
|
|
33
33
|
},
|
|
34
34
|
"parall-daemon.js": {
|
|
35
|
-
"sha256": "
|
|
36
|
-
"size":
|
|
35
|
+
"sha256": "b880b10ae5b932968178ff1f216ad13859afdd02cca332fdd900279dc3ba87ea",
|
|
36
|
+
"size": 1727004
|
|
37
37
|
},
|
|
38
38
|
"parall-openclaw-agent.js": {
|
|
39
39
|
"sha256": "505433b4bf3a4b8dbb7b96978d8693d6c48079f4513282ff1ee59a6d13b3eddf",
|
|
40
40
|
"size": 9801
|
|
41
41
|
}
|
|
42
42
|
},
|
|
43
|
-
"signature": "
|
|
43
|
+
"signature": "WChU4cNRetS9GZz1kISiZkkXjvwR5fSi4QeMiIyZx9r1FBio4CuLqLI7aUFoSEyTzTDSAH9mJS7jdV/csZvfBQ=="
|
|
44
44
|
}
|
|
@@ -36443,7 +36443,6 @@ var ParallClient = class _ParallClient {
|
|
|
36443
36443
|
async steerDispatch(orgId, req) {
|
|
36444
36444
|
return this.request("POST", ENDPOINTS.DISPATCH_STEER(orgId), req);
|
|
36445
36445
|
}
|
|
36446
|
-
/** End a turn: no_action sweep of the lane's members + lane release + re-drive check. */
|
|
36447
36446
|
async completeDispatch(orgId, req) {
|
|
36448
36447
|
return this.request("POST", ENDPOINTS.DISPATCH_COMPLETE(orgId), req);
|
|
36449
36448
|
}
|
|
@@ -36451,6 +36450,10 @@ var ParallClient = class _ParallClient {
|
|
|
36451
36450
|
* End a turn for a lane-less runtime: resolve the turn's folded WorkItems
|
|
36452
36451
|
* by source — broad-cover to the turn's reply Effect when one exists,
|
|
36453
36452
|
* no_action sweep otherwise. Idempotent.
|
|
36453
|
+
*
|
|
36454
|
+
* @deprecated Legacy ok-only alias — use {@link completeDispatch} with the
|
|
36455
|
+
* `sources` form, which also carries `turn_outcome`. The endpoint retires
|
|
36456
|
+
* at S3b (dispatch-convergence-design.md §3).
|
|
36454
36457
|
*/
|
|
36455
36458
|
async completeDispatchSources(orgId, req) {
|
|
36456
36459
|
return this.request("POST", ENDPOINTS.DISPATCH_COMPLETE_SOURCES(orgId), req);
|
|
@@ -27944,7 +27944,6 @@ var ParallClient = class _ParallClient {
|
|
|
27944
27944
|
async steerDispatch(orgId, req) {
|
|
27945
27945
|
return this.request("POST", ENDPOINTS.DISPATCH_STEER(orgId), req);
|
|
27946
27946
|
}
|
|
27947
|
-
/** End a turn: no_action sweep of the lane's members + lane release + re-drive check. */
|
|
27948
27947
|
async completeDispatch(orgId, req) {
|
|
27949
27948
|
return this.request("POST", ENDPOINTS.DISPATCH_COMPLETE(orgId), req);
|
|
27950
27949
|
}
|
|
@@ -27952,6 +27951,10 @@ var ParallClient = class _ParallClient {
|
|
|
27952
27951
|
* End a turn for a lane-less runtime: resolve the turn's folded WorkItems
|
|
27953
27952
|
* by source — broad-cover to the turn's reply Effect when one exists,
|
|
27954
27953
|
* no_action sweep otherwise. Idempotent.
|
|
27954
|
+
*
|
|
27955
|
+
* @deprecated Legacy ok-only alias — use {@link completeDispatch} with the
|
|
27956
|
+
* `sources` form, which also carries `turn_outcome`. The endpoint retires
|
|
27957
|
+
* at S3b (dispatch-convergence-design.md §3).
|
|
27955
27958
|
*/
|
|
27956
27959
|
async completeDispatchSources(orgId, req) {
|
|
27957
27960
|
return this.request("POST", ENDPOINTS.DISPATCH_COMPLETE_SOURCES(orgId), req);
|
|
@@ -29157,7 +29160,11 @@ var LaneLedger = class {
|
|
|
29157
29160
|
throw err;
|
|
29158
29161
|
}
|
|
29159
29162
|
if (!res.claimed || !res.lane) {
|
|
29160
|
-
|
|
29163
|
+
if (res.reason === "empty") {
|
|
29164
|
+
this.opts.log?.warn(`claim for ${targetUri} came back empty \u2014 nothing foldable; leaving to the reconciler`);
|
|
29165
|
+
} else {
|
|
29166
|
+
this.opts.log?.info(`lane for ${targetUri} held by a healthy incumbent \u2014 leaving events pending for re-drive`);
|
|
29167
|
+
}
|
|
29161
29168
|
return null;
|
|
29162
29169
|
}
|
|
29163
29170
|
const leaseUntilMs = Date.parse(res.lease_until ?? "");
|
|
@@ -29234,6 +29241,18 @@ var LaneLedger = class {
|
|
|
29234
29241
|
* re-drives any same-target pending work. A STALE_LANE answer means a
|
|
29235
29242
|
* takeover already owns the resource — local state is dropped either way.
|
|
29236
29243
|
*/
|
|
29244
|
+
/**
|
|
29245
|
+
* Record that the turn on this lane surfaced a runtime error. The flow
|
|
29246
|
+
* settles an errored lane immediately (dispatchLaneGroup returns 'failed'
|
|
29247
|
+
* after a forced complete), so the bit normally lives for one turn only —
|
|
29248
|
+
* it is the transport between the gateway's per-session error signal and
|
|
29249
|
+
* this lane's complete request.
|
|
29250
|
+
*/
|
|
29251
|
+
markTurnError(laneKey) {
|
|
29252
|
+
const lane = this.lanes.get(laneKey);
|
|
29253
|
+
if (lane)
|
|
29254
|
+
lane.turnError = true;
|
|
29255
|
+
}
|
|
29237
29256
|
async completeIfIdle(laneKey, hasMoreLocal) {
|
|
29238
29257
|
const lane = this.lanes.get(laneKey);
|
|
29239
29258
|
if (!lane || hasMoreLocal)
|
|
@@ -29244,7 +29263,10 @@ var LaneLedger = class {
|
|
|
29244
29263
|
const res = await this.opts.client.completeDispatch(this.opts.orgId, {
|
|
29245
29264
|
lane: lane.lane,
|
|
29246
29265
|
target_uri: lane.targetUri,
|
|
29247
|
-
thread_root_id: lane.threadRootId
|
|
29266
|
+
thread_root_id: lane.threadRootId,
|
|
29267
|
+
// An error turn releases its members for retry instead of sweeping
|
|
29268
|
+
// them as handled (ignored by older servers).
|
|
29269
|
+
turn_outcome: lane.turnError ? "error" : "ok"
|
|
29248
29270
|
});
|
|
29249
29271
|
if (res.swept_no_action > 0 || res.redriven) {
|
|
29250
29272
|
this.opts.log?.info(`lane complete for ${lane.targetUri}: swept ${res.swept_no_action} no_action, redriven=${res.redriven}`);
|
|
@@ -29257,6 +29279,17 @@ var LaneLedger = class {
|
|
|
29257
29279
|
this.opts.log?.warn(`lane complete failed for ${lane.targetUri}: ${String(err)}`);
|
|
29258
29280
|
}
|
|
29259
29281
|
}
|
|
29282
|
+
/**
|
|
29283
|
+
* Renew one lane by its key — the external runtime-activity hook for
|
|
29284
|
+
* adapters whose tool traffic bypasses the RuntimeEvent stream (openclaw
|
|
29285
|
+
* hooks). Scoped to the session's own lane: renewing every lane would let
|
|
29286
|
+
* one busy fork keep an unrelated stalled fork's lane leased forever.
|
|
29287
|
+
*/
|
|
29288
|
+
renewByKey(laneKey) {
|
|
29289
|
+
const lane = this.lanes.get(laneKey);
|
|
29290
|
+
if (lane)
|
|
29291
|
+
this.maybeRenew(lane);
|
|
29292
|
+
}
|
|
29260
29293
|
/**
|
|
29261
29294
|
* Long-turn keepalive: renew the lane's lease on runtime activity, throttled
|
|
29262
29295
|
* so a chatty turn doesn't spam the server. Without this, a legitimately
|
|
@@ -29398,17 +29431,34 @@ async function dispatchLaneGroup(host, opts) {
|
|
|
29398
29431
|
}
|
|
29399
29432
|
return "foreign";
|
|
29400
29433
|
}
|
|
29434
|
+
host.noteSessionLane(opts.sessionKey, lane.laneKey);
|
|
29401
29435
|
let dispatched = false;
|
|
29402
29436
|
try {
|
|
29403
29437
|
dispatched = await host.runDispatch(event, opts.sessionKey, opts.body, opts.earlier, opts.captureText);
|
|
29404
29438
|
} catch (err) {
|
|
29439
|
+
host.noteSessionLane(opts.sessionKey, null);
|
|
29405
29440
|
await ledger.release(lane.laneKey).catch(() => {
|
|
29406
29441
|
});
|
|
29407
29442
|
throw err;
|
|
29443
|
+
} finally {
|
|
29444
|
+
if (dispatched)
|
|
29445
|
+
host.noteSessionLane(opts.sessionKey, null);
|
|
29408
29446
|
}
|
|
29409
29447
|
if (!dispatched) {
|
|
29410
29448
|
return "shutdown";
|
|
29411
29449
|
}
|
|
29450
|
+
if (host.consumeTurnError(opts.sessionKey)) {
|
|
29451
|
+
ledger.markTurnError(lane.laneKey);
|
|
29452
|
+
for (const msgId of lane.folded.keys()) {
|
|
29453
|
+
host.dispatchedMessages.delete(msgId);
|
|
29454
|
+
}
|
|
29455
|
+
try {
|
|
29456
|
+
host.opts.dispatchAdapter.abortDispatch?.(opts.sessionKey);
|
|
29457
|
+
} catch {
|
|
29458
|
+
}
|
|
29459
|
+
await ledger.completeIfIdle(lane.laneKey, false);
|
|
29460
|
+
return "failed";
|
|
29461
|
+
}
|
|
29412
29462
|
const pendingInjections = host.opts.dispatchAdapter.hasPendingInjections?.(opts.sessionKey) ?? false;
|
|
29413
29463
|
await ledger.completeIfIdle(lane.laneKey, pendingInjections || opts.hasMoreLocal());
|
|
29414
29464
|
return "dispatched";
|
|
@@ -30791,6 +30841,44 @@ var ParallAgentGateway = class {
|
|
|
30791
30841
|
this.dispatchedMessages.add(id);
|
|
30792
30842
|
return true;
|
|
30793
30843
|
}
|
|
30844
|
+
/**
|
|
30845
|
+
* Lane currently being dispatched per session — lets external activity
|
|
30846
|
+
* signals renew exactly the caller's lane (renewing all lanes would keep
|
|
30847
|
+
* an unrelated stalled fork's lane leased forever).
|
|
30848
|
+
*/
|
|
30849
|
+
sessionActiveLanes = /* @__PURE__ */ new Map();
|
|
30850
|
+
noteSessionLane(sessionKey, laneKey) {
|
|
30851
|
+
if (laneKey == null)
|
|
30852
|
+
this.sessionActiveLanes.delete(sessionKey);
|
|
30853
|
+
else
|
|
30854
|
+
this.sessionActiveLanes.set(sessionKey, laneKey);
|
|
30855
|
+
}
|
|
30856
|
+
/**
|
|
30857
|
+
* External runtime-activity signal for adapters whose tool activity does
|
|
30858
|
+
* not flow through the RuntimeEvent stream (openclaw hooks call this from
|
|
30859
|
+
* the tool-call lifecycle): renews the session's OWN active ledger lane so
|
|
30860
|
+
* a long tool call cannot outlive the lease and get dethroned mid-turn.
|
|
30861
|
+
* No-op without an active ledger lane for the session.
|
|
30862
|
+
*/
|
|
30863
|
+
touchRuntimeActivity(sessionKey) {
|
|
30864
|
+
if (this.ledgerDisabled)
|
|
30865
|
+
return;
|
|
30866
|
+
const laneKey = this.sessionActiveLanes.get(sessionKey);
|
|
30867
|
+
if (laneKey)
|
|
30868
|
+
this.laneLedger?.renewByKey(laneKey);
|
|
30869
|
+
}
|
|
30870
|
+
/** Sessions whose in-flight turn surfaced a runtime error event. */
|
|
30871
|
+
turnErrorSessions = /* @__PURE__ */ new Set();
|
|
30872
|
+
/**
|
|
30873
|
+
* Consume (read-and-clear) the error marker for sessionKey's last turn.
|
|
30874
|
+
* Feeds complete's turn_outcome so an error turn's lane members are
|
|
30875
|
+
* released for retry instead of no_action-swept (design §3). Consuming
|
|
30876
|
+
* (rather than peeking) keeps one-shot fork session keys from accumulating
|
|
30877
|
+
* in the set forever.
|
|
30878
|
+
*/
|
|
30879
|
+
consumeTurnError(sessionKey) {
|
|
30880
|
+
return this.turnErrorSessions.delete(sessionKey);
|
|
30881
|
+
}
|
|
30794
30882
|
async emitDispatchReceived(event) {
|
|
30795
30883
|
const sourceType = event.ackSourceType ?? (event.type === "task" ? "task_activity" : "message");
|
|
30796
30884
|
const sourceId = event.ackSourceId ?? event.messageId;
|
|
@@ -31140,6 +31228,7 @@ var ParallAgentGateway = class {
|
|
|
31140
31228
|
this.pendingRestartNotification = null;
|
|
31141
31229
|
}
|
|
31142
31230
|
resetDispatchMetrics(sessionKey);
|
|
31231
|
+
this.turnErrorSessions.delete(sessionKey);
|
|
31143
31232
|
return runWithSessionKey(sessionKey, async () => {
|
|
31144
31233
|
let dispatchSpan = null;
|
|
31145
31234
|
setSessionChatId(sessionKey, event.targetId);
|
|
@@ -31247,6 +31336,9 @@ var ParallAgentGateway = class {
|
|
|
31247
31336
|
} else if (runtimeEvent.type === "tool_result" && pendingSendCallIds.delete(runtimeEvent.callId)) {
|
|
31248
31337
|
recordMessageSend(sessionKey, !runtimeEvent.error);
|
|
31249
31338
|
}
|
|
31339
|
+
if (runtimeEvent.type === "error") {
|
|
31340
|
+
this.turnErrorSessions.add(sessionKey);
|
|
31341
|
+
}
|
|
31250
31342
|
await this.createRuntimeStep(binding.agentSessionId, event, runtimeEvent, stepIdFilePath, contextFilePath, laneContextFilePath2);
|
|
31251
31343
|
}
|
|
31252
31344
|
if (!binding) {
|
|
@@ -31394,13 +31486,23 @@ var ParallAgentGateway = class {
|
|
|
31394
31486
|
body: buildForkScopePrefix(last) + buildEventBody(last),
|
|
31395
31487
|
earlier,
|
|
31396
31488
|
captureText: batchText,
|
|
31397
|
-
|
|
31489
|
+
// Per-LANE residue check (parity with the main-buffer path):
|
|
31490
|
+
// the fork queue can hold several lanes (channel + thread of
|
|
31491
|
+
// the same chat). A whole-queue check would defer THIS lane's
|
|
31492
|
+
// complete behind another lane's items and never revisit it —
|
|
31493
|
+
// its members would sit received until lease expiry.
|
|
31494
|
+
hasMoreLocal: () => fork.queue.some((it) => this.dispatchGroupKey(it.event) === this.dispatchGroupKey(last))
|
|
31398
31495
|
});
|
|
31399
31496
|
if (outcome === "foreign") {
|
|
31400
31497
|
for (const item of items)
|
|
31401
31498
|
item.resolve(false);
|
|
31402
31499
|
break;
|
|
31403
31500
|
}
|
|
31501
|
+
if (outcome === "failed") {
|
|
31502
|
+
for (const item of items)
|
|
31503
|
+
item.resolve(false);
|
|
31504
|
+
continue;
|
|
31505
|
+
}
|
|
31404
31506
|
dispatched = outcome === "dispatched";
|
|
31405
31507
|
} else {
|
|
31406
31508
|
dispatched = await this.runDispatch(last, fork.fork.sessionKey, buildForkScopePrefix(last) + buildEventBody(last), earlier, batchText);
|
|
@@ -31434,6 +31536,7 @@ var ParallAgentGateway = class {
|
|
|
31434
31536
|
remaining.resolve(false);
|
|
31435
31537
|
}
|
|
31436
31538
|
} finally {
|
|
31539
|
+
this.turnErrorSessions.delete(fork.fork.sessionKey);
|
|
31437
31540
|
if (fork.deadlineTimer) {
|
|
31438
31541
|
clearTimeout(fork.deadlineTimer);
|
|
31439
31542
|
fork.deadlineTimer = null;
|
|
@@ -31546,6 +31649,10 @@ var ParallAgentGateway = class {
|
|
|
31546
31649
|
this.dispatchState.pendingForkResults.unshift(...pendingFork);
|
|
31547
31650
|
continue;
|
|
31548
31651
|
}
|
|
31652
|
+
if (outcome === "failed") {
|
|
31653
|
+
this.dispatchState.pendingForkResults.unshift(...pendingFork);
|
|
31654
|
+
continue;
|
|
31655
|
+
}
|
|
31549
31656
|
continue;
|
|
31550
31657
|
}
|
|
31551
31658
|
try {
|
|
@@ -31649,7 +31756,7 @@ var ParallAgentGateway = class {
|
|
|
31649
31756
|
}
|
|
31650
31757
|
this.dispatchState.mainBuffer.push(event);
|
|
31651
31758
|
if (this.usesLaneLedger(event)) {
|
|
31652
|
-
if (this.mainCurrentGroupKey === this.dispatchGroupKey(event) && await this.laneLedger?.steerLive(event) && await this.opts.dispatchAdapter.enqueueDuringDispatch
|
|
31759
|
+
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
31760
|
this.opts.log?.info(`steer folded+injected for ${event.messageId} (will drain for bookkeeping)`);
|
|
31654
31761
|
}
|
|
31655
31762
|
} else if (this.dispatchState.mainCurrentTargetId === event.targetId && await this.opts.dispatchAdapter.enqueueDuringDispatch?.(this.opts.runtimeKey, buildEventBody(event))) {
|
|
@@ -31676,12 +31783,14 @@ var ParallAgentGateway = class {
|
|
|
31676
31783
|
this.dispatchState.mainBuffer.push(event);
|
|
31677
31784
|
return false;
|
|
31678
31785
|
}
|
|
31679
|
-
|
|
31680
|
-
|
|
31681
|
-
|
|
31682
|
-
|
|
31683
|
-
|
|
31684
|
-
|
|
31786
|
+
if (!this.usesLaneLedger(event)) {
|
|
31787
|
+
try {
|
|
31788
|
+
await this.emitDispatchReceived(event);
|
|
31789
|
+
} catch (err) {
|
|
31790
|
+
this.opts.log?.warn?.(`mark-received failed for fork dispatch, leaving unacked for retry: ${String(err)}`);
|
|
31791
|
+
this.dispatchState.mainBuffer.push(event);
|
|
31792
|
+
return false;
|
|
31793
|
+
}
|
|
31685
31794
|
}
|
|
31686
31795
|
const fork = await this.opts.dispatchAdapter.forkSession({
|
|
31687
31796
|
sessionKey: this.opts.runtimeKey,
|
|
@@ -27944,7 +27944,6 @@ var ParallClient = class _ParallClient {
|
|
|
27944
27944
|
async steerDispatch(orgId, req) {
|
|
27945
27945
|
return this.request("POST", ENDPOINTS.DISPATCH_STEER(orgId), req);
|
|
27946
27946
|
}
|
|
27947
|
-
/** End a turn: no_action sweep of the lane's members + lane release + re-drive check. */
|
|
27948
27947
|
async completeDispatch(orgId, req) {
|
|
27949
27948
|
return this.request("POST", ENDPOINTS.DISPATCH_COMPLETE(orgId), req);
|
|
27950
27949
|
}
|
|
@@ -27952,6 +27951,10 @@ var ParallClient = class _ParallClient {
|
|
|
27952
27951
|
* End a turn for a lane-less runtime: resolve the turn's folded WorkItems
|
|
27953
27952
|
* by source — broad-cover to the turn's reply Effect when one exists,
|
|
27954
27953
|
* no_action sweep otherwise. Idempotent.
|
|
27954
|
+
*
|
|
27955
|
+
* @deprecated Legacy ok-only alias — use {@link completeDispatch} with the
|
|
27956
|
+
* `sources` form, which also carries `turn_outcome`. The endpoint retires
|
|
27957
|
+
* at S3b (dispatch-convergence-design.md §3).
|
|
27955
27958
|
*/
|
|
27956
27959
|
async completeDispatchSources(orgId, req) {
|
|
27957
27960
|
return this.request("POST", ENDPOINTS.DISPATCH_COMPLETE_SOURCES(orgId), req);
|
|
@@ -29157,7 +29160,11 @@ var LaneLedger = class {
|
|
|
29157
29160
|
throw err;
|
|
29158
29161
|
}
|
|
29159
29162
|
if (!res.claimed || !res.lane) {
|
|
29160
|
-
|
|
29163
|
+
if (res.reason === "empty") {
|
|
29164
|
+
this.opts.log?.warn(`claim for ${targetUri} came back empty \u2014 nothing foldable; leaving to the reconciler`);
|
|
29165
|
+
} else {
|
|
29166
|
+
this.opts.log?.info(`lane for ${targetUri} held by a healthy incumbent \u2014 leaving events pending for re-drive`);
|
|
29167
|
+
}
|
|
29161
29168
|
return null;
|
|
29162
29169
|
}
|
|
29163
29170
|
const leaseUntilMs = Date.parse(res.lease_until ?? "");
|
|
@@ -29234,6 +29241,18 @@ var LaneLedger = class {
|
|
|
29234
29241
|
* re-drives any same-target pending work. A STALE_LANE answer means a
|
|
29235
29242
|
* takeover already owns the resource — local state is dropped either way.
|
|
29236
29243
|
*/
|
|
29244
|
+
/**
|
|
29245
|
+
* Record that the turn on this lane surfaced a runtime error. The flow
|
|
29246
|
+
* settles an errored lane immediately (dispatchLaneGroup returns 'failed'
|
|
29247
|
+
* after a forced complete), so the bit normally lives for one turn only —
|
|
29248
|
+
* it is the transport between the gateway's per-session error signal and
|
|
29249
|
+
* this lane's complete request.
|
|
29250
|
+
*/
|
|
29251
|
+
markTurnError(laneKey) {
|
|
29252
|
+
const lane = this.lanes.get(laneKey);
|
|
29253
|
+
if (lane)
|
|
29254
|
+
lane.turnError = true;
|
|
29255
|
+
}
|
|
29237
29256
|
async completeIfIdle(laneKey, hasMoreLocal) {
|
|
29238
29257
|
const lane = this.lanes.get(laneKey);
|
|
29239
29258
|
if (!lane || hasMoreLocal)
|
|
@@ -29244,7 +29263,10 @@ var LaneLedger = class {
|
|
|
29244
29263
|
const res = await this.opts.client.completeDispatch(this.opts.orgId, {
|
|
29245
29264
|
lane: lane.lane,
|
|
29246
29265
|
target_uri: lane.targetUri,
|
|
29247
|
-
thread_root_id: lane.threadRootId
|
|
29266
|
+
thread_root_id: lane.threadRootId,
|
|
29267
|
+
// An error turn releases its members for retry instead of sweeping
|
|
29268
|
+
// them as handled (ignored by older servers).
|
|
29269
|
+
turn_outcome: lane.turnError ? "error" : "ok"
|
|
29248
29270
|
});
|
|
29249
29271
|
if (res.swept_no_action > 0 || res.redriven) {
|
|
29250
29272
|
this.opts.log?.info(`lane complete for ${lane.targetUri}: swept ${res.swept_no_action} no_action, redriven=${res.redriven}`);
|
|
@@ -29257,6 +29279,17 @@ var LaneLedger = class {
|
|
|
29257
29279
|
this.opts.log?.warn(`lane complete failed for ${lane.targetUri}: ${String(err)}`);
|
|
29258
29280
|
}
|
|
29259
29281
|
}
|
|
29282
|
+
/**
|
|
29283
|
+
* Renew one lane by its key — the external runtime-activity hook for
|
|
29284
|
+
* adapters whose tool traffic bypasses the RuntimeEvent stream (openclaw
|
|
29285
|
+
* hooks). Scoped to the session's own lane: renewing every lane would let
|
|
29286
|
+
* one busy fork keep an unrelated stalled fork's lane leased forever.
|
|
29287
|
+
*/
|
|
29288
|
+
renewByKey(laneKey) {
|
|
29289
|
+
const lane = this.lanes.get(laneKey);
|
|
29290
|
+
if (lane)
|
|
29291
|
+
this.maybeRenew(lane);
|
|
29292
|
+
}
|
|
29260
29293
|
/**
|
|
29261
29294
|
* Long-turn keepalive: renew the lane's lease on runtime activity, throttled
|
|
29262
29295
|
* so a chatty turn doesn't spam the server. Without this, a legitimately
|
|
@@ -29398,17 +29431,34 @@ async function dispatchLaneGroup(host, opts) {
|
|
|
29398
29431
|
}
|
|
29399
29432
|
return "foreign";
|
|
29400
29433
|
}
|
|
29434
|
+
host.noteSessionLane(opts.sessionKey, lane.laneKey);
|
|
29401
29435
|
let dispatched = false;
|
|
29402
29436
|
try {
|
|
29403
29437
|
dispatched = await host.runDispatch(event, opts.sessionKey, opts.body, opts.earlier, opts.captureText);
|
|
29404
29438
|
} catch (err) {
|
|
29439
|
+
host.noteSessionLane(opts.sessionKey, null);
|
|
29405
29440
|
await ledger.release(lane.laneKey).catch(() => {
|
|
29406
29441
|
});
|
|
29407
29442
|
throw err;
|
|
29443
|
+
} finally {
|
|
29444
|
+
if (dispatched)
|
|
29445
|
+
host.noteSessionLane(opts.sessionKey, null);
|
|
29408
29446
|
}
|
|
29409
29447
|
if (!dispatched) {
|
|
29410
29448
|
return "shutdown";
|
|
29411
29449
|
}
|
|
29450
|
+
if (host.consumeTurnError(opts.sessionKey)) {
|
|
29451
|
+
ledger.markTurnError(lane.laneKey);
|
|
29452
|
+
for (const msgId of lane.folded.keys()) {
|
|
29453
|
+
host.dispatchedMessages.delete(msgId);
|
|
29454
|
+
}
|
|
29455
|
+
try {
|
|
29456
|
+
host.opts.dispatchAdapter.abortDispatch?.(opts.sessionKey);
|
|
29457
|
+
} catch {
|
|
29458
|
+
}
|
|
29459
|
+
await ledger.completeIfIdle(lane.laneKey, false);
|
|
29460
|
+
return "failed";
|
|
29461
|
+
}
|
|
29412
29462
|
const pendingInjections = host.opts.dispatchAdapter.hasPendingInjections?.(opts.sessionKey) ?? false;
|
|
29413
29463
|
await ledger.completeIfIdle(lane.laneKey, pendingInjections || opts.hasMoreLocal());
|
|
29414
29464
|
return "dispatched";
|
|
@@ -30791,6 +30841,44 @@ var ParallAgentGateway = class {
|
|
|
30791
30841
|
this.dispatchedMessages.add(id);
|
|
30792
30842
|
return true;
|
|
30793
30843
|
}
|
|
30844
|
+
/**
|
|
30845
|
+
* Lane currently being dispatched per session — lets external activity
|
|
30846
|
+
* signals renew exactly the caller's lane (renewing all lanes would keep
|
|
30847
|
+
* an unrelated stalled fork's lane leased forever).
|
|
30848
|
+
*/
|
|
30849
|
+
sessionActiveLanes = /* @__PURE__ */ new Map();
|
|
30850
|
+
noteSessionLane(sessionKey, laneKey) {
|
|
30851
|
+
if (laneKey == null)
|
|
30852
|
+
this.sessionActiveLanes.delete(sessionKey);
|
|
30853
|
+
else
|
|
30854
|
+
this.sessionActiveLanes.set(sessionKey, laneKey);
|
|
30855
|
+
}
|
|
30856
|
+
/**
|
|
30857
|
+
* External runtime-activity signal for adapters whose tool activity does
|
|
30858
|
+
* not flow through the RuntimeEvent stream (openclaw hooks call this from
|
|
30859
|
+
* the tool-call lifecycle): renews the session's OWN active ledger lane so
|
|
30860
|
+
* a long tool call cannot outlive the lease and get dethroned mid-turn.
|
|
30861
|
+
* No-op without an active ledger lane for the session.
|
|
30862
|
+
*/
|
|
30863
|
+
touchRuntimeActivity(sessionKey) {
|
|
30864
|
+
if (this.ledgerDisabled)
|
|
30865
|
+
return;
|
|
30866
|
+
const laneKey = this.sessionActiveLanes.get(sessionKey);
|
|
30867
|
+
if (laneKey)
|
|
30868
|
+
this.laneLedger?.renewByKey(laneKey);
|
|
30869
|
+
}
|
|
30870
|
+
/** Sessions whose in-flight turn surfaced a runtime error event. */
|
|
30871
|
+
turnErrorSessions = /* @__PURE__ */ new Set();
|
|
30872
|
+
/**
|
|
30873
|
+
* Consume (read-and-clear) the error marker for sessionKey's last turn.
|
|
30874
|
+
* Feeds complete's turn_outcome so an error turn's lane members are
|
|
30875
|
+
* released for retry instead of no_action-swept (design §3). Consuming
|
|
30876
|
+
* (rather than peeking) keeps one-shot fork session keys from accumulating
|
|
30877
|
+
* in the set forever.
|
|
30878
|
+
*/
|
|
30879
|
+
consumeTurnError(sessionKey) {
|
|
30880
|
+
return this.turnErrorSessions.delete(sessionKey);
|
|
30881
|
+
}
|
|
30794
30882
|
async emitDispatchReceived(event) {
|
|
30795
30883
|
const sourceType = event.ackSourceType ?? (event.type === "task" ? "task_activity" : "message");
|
|
30796
30884
|
const sourceId = event.ackSourceId ?? event.messageId;
|
|
@@ -31140,6 +31228,7 @@ var ParallAgentGateway = class {
|
|
|
31140
31228
|
this.pendingRestartNotification = null;
|
|
31141
31229
|
}
|
|
31142
31230
|
resetDispatchMetrics(sessionKey);
|
|
31231
|
+
this.turnErrorSessions.delete(sessionKey);
|
|
31143
31232
|
return runWithSessionKey(sessionKey, async () => {
|
|
31144
31233
|
let dispatchSpan = null;
|
|
31145
31234
|
setSessionChatId(sessionKey, event.targetId);
|
|
@@ -31247,6 +31336,9 @@ var ParallAgentGateway = class {
|
|
|
31247
31336
|
} else if (runtimeEvent.type === "tool_result" && pendingSendCallIds.delete(runtimeEvent.callId)) {
|
|
31248
31337
|
recordMessageSend(sessionKey, !runtimeEvent.error);
|
|
31249
31338
|
}
|
|
31339
|
+
if (runtimeEvent.type === "error") {
|
|
31340
|
+
this.turnErrorSessions.add(sessionKey);
|
|
31341
|
+
}
|
|
31250
31342
|
await this.createRuntimeStep(binding.agentSessionId, event, runtimeEvent, stepIdFilePath, contextFilePath, laneContextFilePath2);
|
|
31251
31343
|
}
|
|
31252
31344
|
if (!binding) {
|
|
@@ -31394,13 +31486,23 @@ var ParallAgentGateway = class {
|
|
|
31394
31486
|
body: buildForkScopePrefix(last) + buildEventBody(last),
|
|
31395
31487
|
earlier,
|
|
31396
31488
|
captureText: batchText,
|
|
31397
|
-
|
|
31489
|
+
// Per-LANE residue check (parity with the main-buffer path):
|
|
31490
|
+
// the fork queue can hold several lanes (channel + thread of
|
|
31491
|
+
// the same chat). A whole-queue check would defer THIS lane's
|
|
31492
|
+
// complete behind another lane's items and never revisit it —
|
|
31493
|
+
// its members would sit received until lease expiry.
|
|
31494
|
+
hasMoreLocal: () => fork.queue.some((it) => this.dispatchGroupKey(it.event) === this.dispatchGroupKey(last))
|
|
31398
31495
|
});
|
|
31399
31496
|
if (outcome === "foreign") {
|
|
31400
31497
|
for (const item of items)
|
|
31401
31498
|
item.resolve(false);
|
|
31402
31499
|
break;
|
|
31403
31500
|
}
|
|
31501
|
+
if (outcome === "failed") {
|
|
31502
|
+
for (const item of items)
|
|
31503
|
+
item.resolve(false);
|
|
31504
|
+
continue;
|
|
31505
|
+
}
|
|
31404
31506
|
dispatched = outcome === "dispatched";
|
|
31405
31507
|
} else {
|
|
31406
31508
|
dispatched = await this.runDispatch(last, fork.fork.sessionKey, buildForkScopePrefix(last) + buildEventBody(last), earlier, batchText);
|
|
@@ -31434,6 +31536,7 @@ var ParallAgentGateway = class {
|
|
|
31434
31536
|
remaining.resolve(false);
|
|
31435
31537
|
}
|
|
31436
31538
|
} finally {
|
|
31539
|
+
this.turnErrorSessions.delete(fork.fork.sessionKey);
|
|
31437
31540
|
if (fork.deadlineTimer) {
|
|
31438
31541
|
clearTimeout(fork.deadlineTimer);
|
|
31439
31542
|
fork.deadlineTimer = null;
|
|
@@ -31546,6 +31649,10 @@ var ParallAgentGateway = class {
|
|
|
31546
31649
|
this.dispatchState.pendingForkResults.unshift(...pendingFork);
|
|
31547
31650
|
continue;
|
|
31548
31651
|
}
|
|
31652
|
+
if (outcome === "failed") {
|
|
31653
|
+
this.dispatchState.pendingForkResults.unshift(...pendingFork);
|
|
31654
|
+
continue;
|
|
31655
|
+
}
|
|
31549
31656
|
continue;
|
|
31550
31657
|
}
|
|
31551
31658
|
try {
|
|
@@ -31649,7 +31756,7 @@ var ParallAgentGateway = class {
|
|
|
31649
31756
|
}
|
|
31650
31757
|
this.dispatchState.mainBuffer.push(event);
|
|
31651
31758
|
if (this.usesLaneLedger(event)) {
|
|
31652
|
-
if (this.mainCurrentGroupKey === this.dispatchGroupKey(event) && await this.laneLedger?.steerLive(event) && await this.opts.dispatchAdapter.enqueueDuringDispatch
|
|
31759
|
+
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
31760
|
this.opts.log?.info(`steer folded+injected for ${event.messageId} (will drain for bookkeeping)`);
|
|
31654
31761
|
}
|
|
31655
31762
|
} else if (this.dispatchState.mainCurrentTargetId === event.targetId && await this.opts.dispatchAdapter.enqueueDuringDispatch?.(this.opts.runtimeKey, buildEventBody(event))) {
|
|
@@ -31676,12 +31783,14 @@ var ParallAgentGateway = class {
|
|
|
31676
31783
|
this.dispatchState.mainBuffer.push(event);
|
|
31677
31784
|
return false;
|
|
31678
31785
|
}
|
|
31679
|
-
|
|
31680
|
-
|
|
31681
|
-
|
|
31682
|
-
|
|
31683
|
-
|
|
31684
|
-
|
|
31786
|
+
if (!this.usesLaneLedger(event)) {
|
|
31787
|
+
try {
|
|
31788
|
+
await this.emitDispatchReceived(event);
|
|
31789
|
+
} catch (err) {
|
|
31790
|
+
this.opts.log?.warn?.(`mark-received failed for fork dispatch, leaving unacked for retry: ${String(err)}`);
|
|
31791
|
+
this.dispatchState.mainBuffer.push(event);
|
|
31792
|
+
return false;
|
|
31793
|
+
}
|
|
31685
31794
|
}
|
|
31686
31795
|
const fork = await this.opts.dispatchAdapter.forkSession({
|
|
31687
31796
|
sessionKey: this.opts.runtimeKey,
|
package/bundle/parall-daemon.js
CHANGED
|
@@ -1625,7 +1625,6 @@ var init_client = __esm({
|
|
|
1625
1625
|
async steerDispatch(orgId, req) {
|
|
1626
1626
|
return this.request("POST", ENDPOINTS.DISPATCH_STEER(orgId), req);
|
|
1627
1627
|
}
|
|
1628
|
-
/** End a turn: no_action sweep of the lane's members + lane release + re-drive check. */
|
|
1629
1628
|
async completeDispatch(orgId, req) {
|
|
1630
1629
|
return this.request("POST", ENDPOINTS.DISPATCH_COMPLETE(orgId), req);
|
|
1631
1630
|
}
|
|
@@ -1633,6 +1632,10 @@ var init_client = __esm({
|
|
|
1633
1632
|
* End a turn for a lane-less runtime: resolve the turn's folded WorkItems
|
|
1634
1633
|
* by source — broad-cover to the turn's reply Effect when one exists,
|
|
1635
1634
|
* no_action sweep otherwise. Idempotent.
|
|
1635
|
+
*
|
|
1636
|
+
* @deprecated Legacy ok-only alias — use {@link completeDispatch} with the
|
|
1637
|
+
* `sources` form, which also carries `turn_outcome`. The endpoint retires
|
|
1638
|
+
* at S3b (dispatch-convergence-design.md §3).
|
|
1636
1639
|
*/
|
|
1637
1640
|
async completeDispatchSources(orgId, req) {
|
|
1638
1641
|
return this.request("POST", ENDPOINTS.DISPATCH_COMPLETE_SOURCES(orgId), req);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@parall/daemon",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.44.0",
|
|
4
4
|
"description": "Parall local agent runtime — daemon supervisor + bridge runtimes, bundled as standalone JS files",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -32,11 +32,11 @@
|
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@aws-sdk/client-s3": "3.984.0",
|
|
34
34
|
"@pinixai/bb-browser-pro": "0.15.0",
|
|
35
|
-
"@parall/agent-core": "1.
|
|
36
|
-
"@parall/
|
|
37
|
-
"@parall/
|
|
38
|
-
"@parall/codex-agent": "1.
|
|
39
|
-
"@parall/openclaw-agent": "1.
|
|
35
|
+
"@parall/agent-core": "1.44.0",
|
|
36
|
+
"@parall/claude-agent": "1.44.0",
|
|
37
|
+
"@parall/sdk": "1.44.0",
|
|
38
|
+
"@parall/codex-agent": "1.44.0",
|
|
39
|
+
"@parall/openclaw-agent": "1.44.0"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@types/node": "^22.0.0",
|