@parall/daemon 1.41.0 → 1.42.1

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.
@@ -1,6 +1,6 @@
1
1
  {
2
- "version": "1.41.0",
3
- "built_at": "2026-07-10T05:52:54.072Z",
2
+ "version": "1.42.1",
3
+ "built_at": "2026-07-11T03:40:29.108Z",
4
4
  "min_node_version": "20.0.0",
5
5
  "files": {
6
6
  "bb-browser-daemon.js": {
@@ -16,25 +16,25 @@
16
16
  "size": 18
17
17
  },
18
18
  "parall-browser-pod.js": {
19
- "sha256": "6d5aade09173b16e332dcd29d0d1b1d68cfb424c4297d031b36069b07d3077cc",
20
- "size": 1698807
19
+ "sha256": "4e98a70b593c352f4fa99cd2ff8b350346437942bd1134e3d500211585c5a94a",
20
+ "size": 1699246
21
21
  },
22
22
  "parall-claude-agent.js": {
23
- "sha256": "e83d963acd70069ee4e4f564df91154d3172325f6681925ee0cbca0330c90099",
24
- "size": 1623952
23
+ "sha256": "8d7d9a8c8b6d1d90f7e84489fd05c059c5c5d24d321274ff3916f12454df2e19",
24
+ "size": 1626635
25
25
  },
26
26
  "parall-codex-agent.js": {
27
- "sha256": "13ccc915e9b18f6a300d8d27a07221f7d0bcf2ac3edce719abc85bbd57db9fc1",
28
- "size": 1654762
27
+ "sha256": "5ab229def8061ab05bc040571d5cb175bb7c9d8fce565682ab04c6b1a81b8649",
28
+ "size": 1664012
29
29
  },
30
30
  "parall-daemon.js": {
31
- "sha256": "eeef6afe86012476ccbb8ec7e2e0e9228f66515a044e4bba69445fbeb5850a6a",
32
- "size": 1719056
31
+ "sha256": "354bdc719c5bde1e7f4eaae19f1d895fced7b1a6feee8590d727812b6e5fe2f4",
32
+ "size": 1719706
33
33
  },
34
34
  "parall-openclaw-agent.js": {
35
35
  "sha256": "505433b4bf3a4b8dbb7b96978d8693d6c48079f4513282ff1ee59a6d13b3eddf",
36
36
  "size": 9801
37
37
  }
38
38
  },
39
- "signature": "Gl4UXAqlHYlaqbK5L48tDAHYjBs6yGSAd0zg2HsNj3yTvqxQgSyDQtO++hPKdkR7VdVdM/weOj6qoct4UXVZDA=="
39
+ "signature": "T/TtjoBJx65Xb8MftcuGyi+F0J8kpPBmzKIl8hMLP+igdhlAzKTLEHvDN4gB72BOd7lUHOFLwufiyyUye/TbCg=="
40
40
  }
@@ -35360,6 +35360,7 @@ var ENDPOINTS = {
35360
35360
  DISPATCH_CLAIM: (orgId) => `${API_BASE}/orgs/${orgId}/dispatch/claim`,
35361
35361
  DISPATCH_STEER: (orgId) => `${API_BASE}/orgs/${orgId}/dispatch/steer`,
35362
35362
  DISPATCH_COMPLETE: (orgId) => `${API_BASE}/orgs/${orgId}/dispatch/complete`,
35363
+ DISPATCH_COMPLETE_SOURCES: (orgId) => `${API_BASE}/orgs/${orgId}/dispatch/complete-sources`,
35363
35364
  DISPATCH_RELEASE: (orgId) => `${API_BASE}/orgs/${orgId}/dispatch/release`,
35364
35365
  DISPATCH_HEARTBEAT: (orgId) => `${API_BASE}/orgs/${orgId}/dispatch/heartbeat`,
35365
35366
  // Unread
@@ -36212,9 +36213,9 @@ var ParallClient = class _ParallClient {
36212
36213
  async retryAgentWorkspaceSetup(orgId, machineId, agentId) {
36213
36214
  return this.request("POST", ENDPOINTS.MACHINE_AGENT_WORKSPACE_SETUP(orgId, machineId, agentId));
36214
36215
  }
36215
- async patchMachineLLMSource(orgId, machineId, llmSource) {
36216
+ async patchMachineLLMSource(orgId, machineId, llmSource2) {
36216
36217
  return this.request("PATCH", ENDPOINTS.MACHINE_LLM_SOURCE(orgId, machineId), {
36217
- llm_source: llmSource
36218
+ llm_source: llmSource2
36218
36219
  });
36219
36220
  }
36220
36221
  /**
@@ -36441,7 +36442,7 @@ var ParallClient = class _ParallClient {
36441
36442
  async claimDispatch(orgId, req) {
36442
36443
  return this.request("POST", ENDPOINTS.DISPATCH_CLAIM(orgId), req);
36443
36444
  }
36444
- /** Fold a pending same-target WorkItem into a live lane (409 STALE_LANE when dethroned). */
36445
+ /** Fold into a live lane: 409 STALE_LANE when dethroned; 409 INELIGIBLE_REF for a bad ref. */
36445
36446
  async steerDispatch(orgId, req) {
36446
36447
  return this.request("POST", ENDPOINTS.DISPATCH_STEER(orgId), req);
36447
36448
  }
@@ -36449,6 +36450,14 @@ var ParallClient = class _ParallClient {
36449
36450
  async completeDispatch(orgId, req) {
36450
36451
  return this.request("POST", ENDPOINTS.DISPATCH_COMPLETE(orgId), req);
36451
36452
  }
36453
+ /**
36454
+ * End a turn for a lane-less runtime: resolve the turn's folded WorkItems
36455
+ * by source — broad-cover to the turn's reply Effect when one exists,
36456
+ * no_action sweep otherwise. Idempotent.
36457
+ */
36458
+ async completeDispatchSources(orgId, req) {
36459
+ return this.request("POST", ENDPOINTS.DISPATCH_COMPLETE_SOURCES(orgId), req);
36460
+ }
36452
36461
  /** Release a lane on graceful shutdown — members return to pending immediately. */
36453
36462
  async releaseDispatchLane(orgId, lane) {
36454
36463
  return this.request("POST", ENDPOINTS.DISPATCH_RELEASE(orgId), { lane });
@@ -27475,6 +27475,7 @@ var ENDPOINTS = {
27475
27475
  DISPATCH_CLAIM: (orgId) => `${API_BASE}/orgs/${orgId}/dispatch/claim`,
27476
27476
  DISPATCH_STEER: (orgId) => `${API_BASE}/orgs/${orgId}/dispatch/steer`,
27477
27477
  DISPATCH_COMPLETE: (orgId) => `${API_BASE}/orgs/${orgId}/dispatch/complete`,
27478
+ DISPATCH_COMPLETE_SOURCES: (orgId) => `${API_BASE}/orgs/${orgId}/dispatch/complete-sources`,
27478
27479
  DISPATCH_RELEASE: (orgId) => `${API_BASE}/orgs/${orgId}/dispatch/release`,
27479
27480
  DISPATCH_HEARTBEAT: (orgId) => `${API_BASE}/orgs/${orgId}/dispatch/heartbeat`,
27480
27481
  // Unread
@@ -28640,7 +28641,7 @@ var ParallClient = class _ParallClient {
28640
28641
  async claimDispatch(orgId, req) {
28641
28642
  return this.request("POST", ENDPOINTS.DISPATCH_CLAIM(orgId), req);
28642
28643
  }
28643
- /** Fold a pending same-target WorkItem into a live lane (409 STALE_LANE when dethroned). */
28644
+ /** Fold into a live lane: 409 STALE_LANE when dethroned; 409 INELIGIBLE_REF for a bad ref. */
28644
28645
  async steerDispatch(orgId, req) {
28645
28646
  return this.request("POST", ENDPOINTS.DISPATCH_STEER(orgId), req);
28646
28647
  }
@@ -28648,6 +28649,14 @@ var ParallClient = class _ParallClient {
28648
28649
  async completeDispatch(orgId, req) {
28649
28650
  return this.request("POST", ENDPOINTS.DISPATCH_COMPLETE(orgId), req);
28650
28651
  }
28652
+ /**
28653
+ * End a turn for a lane-less runtime: resolve the turn's folded WorkItems
28654
+ * by source — broad-cover to the turn's reply Effect when one exists,
28655
+ * no_action sweep otherwise. Idempotent.
28656
+ */
28657
+ async completeDispatchSources(orgId, req) {
28658
+ return this.request("POST", ENDPOINTS.DISPATCH_COMPLETE_SOURCES(orgId), req);
28659
+ }
28651
28660
  /** Release a lane on graceful shutdown — members return to pending immediately. */
28652
28661
  async releaseDispatchLane(orgId, lane) {
28653
28662
  return this.request("POST", ENDPOINTS.DISPATCH_RELEASE(orgId), { lane });
@@ -30109,7 +30118,7 @@ async function dispatchLaneGroup(host, opts) {
30109
30118
  async function consumeTypedDispatch(host, ref, run, ack) {
30110
30119
  if (!host.laneLedger || host.ledgerDisabled) {
30111
30120
  if (await run(ref.dispatchEventId))
30112
- ack(ref.dispatchEventId);
30121
+ await ack(ref.dispatchEventId);
30113
30122
  return;
30114
30123
  }
30115
30124
  let lane;
@@ -30119,7 +30128,7 @@ async function consumeTypedDispatch(host, ref, run, ack) {
30119
30128
  if (err instanceof LedgerUnsupportedError) {
30120
30129
  host.disableLedger("claim endpoint missing");
30121
30130
  if (await run(ref.dispatchEventId))
30122
- ack(ref.dispatchEventId);
30131
+ await ack(ref.dispatchEventId);
30123
30132
  return;
30124
30133
  }
30125
30134
  throw err;
@@ -30130,7 +30139,7 @@ async function consumeTypedDispatch(host, ref, run, ack) {
30130
30139
  }
30131
30140
  try {
30132
30141
  if (await run(lane.typedDispatchEventId))
30133
- ack(lane.typedDispatchEventId);
30142
+ await ack(lane.typedDispatchEventId);
30134
30143
  } finally {
30135
30144
  await host.laneLedger.completeIfIdle(lane.laneKey, false).catch(() => {
30136
30145
  });
@@ -30584,29 +30593,33 @@ var ParallAgentGateway = class {
30584
30593
  try {
30585
30594
  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), (dispatchEventId) => {
30586
30595
  if (dispatchEventId) {
30587
- this.opts.client.ackDispatchByID(this.opts.config.org_id, dispatchEventId).catch(() => {
30596
+ return this.ackDispatchEvent(dispatchEventId, () => {
30597
+ this.dispatchedTasks.delete(`${data.id}:${data.updated_at}`);
30588
30598
  });
30589
- return;
30590
30599
  }
30591
- this.opts.client.ackDispatch(this.opts.config.org_id, {
30600
+ return this.opts.client.ackDispatch(this.opts.config.org_id, {
30592
30601
  source_type: "task_activity",
30593
30602
  source_id: data.id
30594
- }).catch(() => {
30595
- });
30603
+ }).then(() => void 0, () => void 0);
30596
30604
  });
30597
30605
  } catch (err) {
30598
30606
  this.opts.log?.error(`task dispatch failed for ${data.id}: ${String(err)}`);
30599
30607
  }
30600
30608
  });
30601
30609
  ws.on("dispatch.new", async (data) => {
30602
- if (data.event_type === "task_comment") {
30610
+ if (data.event_type === "task_assign") {
30611
+ if (!data.task_id)
30612
+ return;
30613
+ try {
30614
+ await this.handleTaskAssignmentRedrive(data);
30615
+ } catch (err) {
30616
+ this.opts.log?.error(`task assignment re-drive failed for ${data.task_id}: ${String(err)}`);
30617
+ }
30618
+ } else if (data.event_type === "task_comment") {
30603
30619
  if (!data.source_id || !data.task_id)
30604
30620
  return;
30605
30621
  try {
30606
- await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.handleTaskComment(data.source_id, data.task_id ?? "", data.actor_id, data.delivery_reason), () => {
30607
- this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {
30608
- });
30609
- });
30622
+ await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.handleTaskComment(data.source_id, data.task_id ?? "", data.actor_id, data.delivery_reason), () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)));
30610
30623
  } catch (err) {
30611
30624
  this.opts.log?.error(`task comment dispatch failed for ${data.source_id}: ${String(err)}`);
30612
30625
  }
@@ -30614,10 +30627,7 @@ var ParallAgentGateway = class {
30614
30627
  if (!data.source_id)
30615
30628
  return;
30616
30629
  try {
30617
- await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.handleWikiComment(data.source_id, data.actor_id, data.delivery_reason), () => {
30618
- this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {
30619
- });
30620
- });
30630
+ await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.handleWikiComment(data.source_id, data.actor_id, data.delivery_reason), () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)));
30621
30631
  } catch (err) {
30622
30632
  this.opts.log?.error(`wiki comment dispatch failed for ${data.source_id}: ${String(err)}`);
30623
30633
  }
@@ -30628,10 +30638,7 @@ var ParallAgentGateway = class {
30628
30638
  await this.consumeTypedDispatch({ dispatchEventId: data.id }, (dispatchEventId) => this.handleTaskDispatch(data.task_id ?? "", data.source_id ?? data.task_id ?? "", {
30629
30639
  allowCreator: true,
30630
30640
  dispatchEventId
30631
- }), () => {
30632
- this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {
30633
- });
30634
- });
30641
+ }), () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)));
30635
30642
  } catch (err) {
30636
30643
  this.opts.log?.error(`task update dispatch failed for ${data.task_id}: ${String(err)}`);
30637
30644
  }
@@ -30639,10 +30646,7 @@ var ParallAgentGateway = class {
30639
30646
  if (!data.source_id)
30640
30647
  return;
30641
30648
  try {
30642
- await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleScheduleFire(data.source_id, data.actor_id), () => {
30643
- this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {
30644
- });
30645
- });
30649
+ await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleScheduleFire(data.source_id, data.actor_id), () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)));
30646
30650
  } catch (err) {
30647
30651
  this.opts.log?.error(`schedule fire dispatch failed for ${data.source_id}: ${String(err)}`);
30648
30652
  }
@@ -30650,10 +30654,7 @@ var ParallAgentGateway = class {
30650
30654
  if (!data.source_id)
30651
30655
  return;
30652
30656
  try {
30653
- await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleExternalTriggerRun(data.source_id), () => {
30654
- this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {
30655
- });
30656
- });
30657
+ await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleExternalTriggerRun(data.source_id), () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)));
30657
30658
  } catch (err) {
30658
30659
  this.opts.log?.error(`external trigger dispatch failed for ${data.source_id}: ${String(err)}`);
30659
30660
  }
@@ -30661,10 +30662,7 @@ var ParallAgentGateway = class {
30661
30662
  if (!data.source_id)
30662
30663
  return;
30663
30664
  try {
30664
- await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleChannelMessage(data.source_id), () => {
30665
- this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {
30666
- });
30667
- });
30665
+ await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleChannelMessage(data.source_id), () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)));
30668
30666
  } catch (err) {
30669
30667
  this.opts.log?.error(`channel message dispatch failed for ${data.source_id}: ${String(err)}`);
30670
30668
  }
@@ -30672,10 +30670,7 @@ var ParallAgentGateway = class {
30672
30670
  if (!data.source_id)
30673
30671
  return;
30674
30672
  try {
30675
- await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleApprovalDecided(data.source_id, data.actor_id, data.chat_id ?? null), () => {
30676
- this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {
30677
- });
30678
- });
30673
+ await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleApprovalDecided(data.source_id, data.actor_id, data.chat_id ?? null), () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)));
30679
30674
  } catch (err) {
30680
30675
  this.opts.log?.error(`approval decided dispatch failed for ${data.source_id}: ${String(err)}`);
30681
30676
  }
@@ -30685,7 +30680,7 @@ var ParallAgentGateway = class {
30685
30680
  } catch (err) {
30686
30681
  this.opts.log?.error(`message re-drive failed for ${data.source_id}: ${String(err)}`);
30687
30682
  }
30688
- } else if (data.event_type !== "message" && data.event_type !== "task_assign") {
30683
+ } else if (data.event_type !== "message") {
30689
30684
  this.opts.log?.info(`dispatch.new with unhandled event_type=${String(data.event_type)} (id=${data.id}) \u2014 no-op`);
30690
30685
  }
30691
30686
  });
@@ -30753,6 +30748,50 @@ var ParallAgentGateway = class {
30753
30748
  consumeTypedDispatch(ref, run, ack) {
30754
30749
  return consumeTypedDispatch(this.laneFlowHost(), ref, run, ack);
30755
30750
  }
30751
+ // Typed completion must wait until the administrative ack has either
30752
+ // committed or failed. Errors stay best-effort: a failed ack leaves the row
30753
+ // received, so Complete releases and re-drives it safely.
30754
+ ackDispatchEvent(dispatchEventId, onFailure) {
30755
+ return this.opts.client.ackDispatchByID(this.opts.config.org_id, dispatchEventId).then(() => void 0, (err) => {
30756
+ onFailure?.();
30757
+ this.opts.log?.warn(`dispatch ack failed for ${dispatchEventId}, releasing for re-drive: ${String(err)}`);
30758
+ });
30759
+ }
30760
+ clearTypedDispatchDedupe(item) {
30761
+ switch (item.event_type) {
30762
+ case "task_assign":
30763
+ case "task_update":
30764
+ if (item.task_id) {
30765
+ const prefix = `${item.task_id}:`;
30766
+ for (const key of this.dispatchedTasks) {
30767
+ if (key.startsWith(prefix))
30768
+ this.dispatchedTasks.delete(key);
30769
+ }
30770
+ }
30771
+ break;
30772
+ case "task_comment":
30773
+ case "wiki_comment":
30774
+ if (item.source_id)
30775
+ this.dispatchedTasks.delete(`comment:${item.source_id}`);
30776
+ break;
30777
+ case "schedule.fire":
30778
+ if (item.source_id)
30779
+ this.dispatchedTasks.delete(`schedule_run:${item.source_id}`);
30780
+ break;
30781
+ case "external_trigger":
30782
+ if (item.source_id)
30783
+ this.dispatchedTasks.delete(`external_trigger_run:${item.source_id}`);
30784
+ break;
30785
+ case "channel_message":
30786
+ if (item.source_id)
30787
+ this.dispatchedMessages.delete(`channel_message:${item.source_id}`);
30788
+ break;
30789
+ case "approval_decided":
30790
+ if (item.source_id)
30791
+ this.dispatchedTasks.delete(`approval:${item.source_id}`);
30792
+ break;
30793
+ }
30794
+ }
30756
30795
  buildDispatchContext(event, sessionKey) {
30757
30796
  const binding = this.sessionBindings.get(sessionKey);
30758
30797
  return {
@@ -31719,6 +31758,17 @@ var ParallAgentGateway = class {
31719
31758
  chat_id: item.chat_id
31720
31759
  });
31721
31760
  }
31761
+ // A task assignment's first delivery rides task.assigned, but a typed lane
31762
+ // released without an effect is re-driven as dispatch.new. Reuse the exact
31763
+ // typed claim path used by catch-up so that recovery does not require a
31764
+ // runtime reconnect.
31765
+ async handleTaskAssignmentRedrive(item) {
31766
+ if (!item.task_id)
31767
+ return;
31768
+ await this.consumeTypedDispatch({ dispatchEventId: item.id }, (dispatchEventId) => this.handleTaskDispatch(item.task_id ?? "", item.source_id ?? item.task_id ?? "", {
31769
+ dispatchEventId
31770
+ }), (dispatchEventId) => this.ackDispatchEvent(dispatchEventId ?? item.id, () => this.clearTypedDispatchDedupe(item)));
31771
+ }
31722
31772
  consumeMessageWorkItem(item) {
31723
31773
  return consumeMessageWorkItem(this.laneFlowHost(), item);
31724
31774
  }
@@ -32184,10 +32234,7 @@ var ParallAgentGateway = class {
32184
32234
  }
32185
32235
  processed++;
32186
32236
  try {
32187
- const ackItem = () => {
32188
- this.opts.client.ackDispatchByID(this.opts.config.org_id, item.id).catch(() => {
32189
- });
32190
- };
32237
+ const ackItem = () => this.ackDispatchEvent(item.id, () => this.clearTypedDispatchDedupe(item));
32191
32238
  if (item.event_type === "task_assign" && item.task_id) {
32192
32239
  try {
32193
32240
  await this.consumeTypedDispatch({ dispatchEventId: item.id }, (dispatchEventId) => this.handleTaskDispatch(item.task_id ?? "", item.source_id ?? item.task_id ?? "", {
@@ -27475,6 +27475,7 @@ var ENDPOINTS = {
27475
27475
  DISPATCH_CLAIM: (orgId) => `${API_BASE}/orgs/${orgId}/dispatch/claim`,
27476
27476
  DISPATCH_STEER: (orgId) => `${API_BASE}/orgs/${orgId}/dispatch/steer`,
27477
27477
  DISPATCH_COMPLETE: (orgId) => `${API_BASE}/orgs/${orgId}/dispatch/complete`,
27478
+ DISPATCH_COMPLETE_SOURCES: (orgId) => `${API_BASE}/orgs/${orgId}/dispatch/complete-sources`,
27478
27479
  DISPATCH_RELEASE: (orgId) => `${API_BASE}/orgs/${orgId}/dispatch/release`,
27479
27480
  DISPATCH_HEARTBEAT: (orgId) => `${API_BASE}/orgs/${orgId}/dispatch/heartbeat`,
27480
27481
  // Unread
@@ -28640,7 +28641,7 @@ var ParallClient = class _ParallClient {
28640
28641
  async claimDispatch(orgId, req) {
28641
28642
  return this.request("POST", ENDPOINTS.DISPATCH_CLAIM(orgId), req);
28642
28643
  }
28643
- /** Fold a pending same-target WorkItem into a live lane (409 STALE_LANE when dethroned). */
28644
+ /** Fold into a live lane: 409 STALE_LANE when dethroned; 409 INELIGIBLE_REF for a bad ref. */
28644
28645
  async steerDispatch(orgId, req) {
28645
28646
  return this.request("POST", ENDPOINTS.DISPATCH_STEER(orgId), req);
28646
28647
  }
@@ -28648,6 +28649,14 @@ var ParallClient = class _ParallClient {
28648
28649
  async completeDispatch(orgId, req) {
28649
28650
  return this.request("POST", ENDPOINTS.DISPATCH_COMPLETE(orgId), req);
28650
28651
  }
28652
+ /**
28653
+ * End a turn for a lane-less runtime: resolve the turn's folded WorkItems
28654
+ * by source — broad-cover to the turn's reply Effect when one exists,
28655
+ * no_action sweep otherwise. Idempotent.
28656
+ */
28657
+ async completeDispatchSources(orgId, req) {
28658
+ return this.request("POST", ENDPOINTS.DISPATCH_COMPLETE_SOURCES(orgId), req);
28659
+ }
28651
28660
  /** Release a lane on graceful shutdown — members return to pending immediately. */
28652
28661
  async releaseDispatchLane(orgId, lane) {
28653
28662
  return this.request("POST", ENDPOINTS.DISPATCH_RELEASE(orgId), { lane });
@@ -30109,7 +30118,7 @@ async function dispatchLaneGroup(host, opts) {
30109
30118
  async function consumeTypedDispatch(host, ref, run, ack) {
30110
30119
  if (!host.laneLedger || host.ledgerDisabled) {
30111
30120
  if (await run(ref.dispatchEventId))
30112
- ack(ref.dispatchEventId);
30121
+ await ack(ref.dispatchEventId);
30113
30122
  return;
30114
30123
  }
30115
30124
  let lane;
@@ -30119,7 +30128,7 @@ async function consumeTypedDispatch(host, ref, run, ack) {
30119
30128
  if (err instanceof LedgerUnsupportedError) {
30120
30129
  host.disableLedger("claim endpoint missing");
30121
30130
  if (await run(ref.dispatchEventId))
30122
- ack(ref.dispatchEventId);
30131
+ await ack(ref.dispatchEventId);
30123
30132
  return;
30124
30133
  }
30125
30134
  throw err;
@@ -30130,7 +30139,7 @@ async function consumeTypedDispatch(host, ref, run, ack) {
30130
30139
  }
30131
30140
  try {
30132
30141
  if (await run(lane.typedDispatchEventId))
30133
- ack(lane.typedDispatchEventId);
30142
+ await ack(lane.typedDispatchEventId);
30134
30143
  } finally {
30135
30144
  await host.laneLedger.completeIfIdle(lane.laneKey, false).catch(() => {
30136
30145
  });
@@ -30584,29 +30593,33 @@ var ParallAgentGateway = class {
30584
30593
  try {
30585
30594
  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), (dispatchEventId) => {
30586
30595
  if (dispatchEventId) {
30587
- this.opts.client.ackDispatchByID(this.opts.config.org_id, dispatchEventId).catch(() => {
30596
+ return this.ackDispatchEvent(dispatchEventId, () => {
30597
+ this.dispatchedTasks.delete(`${data.id}:${data.updated_at}`);
30588
30598
  });
30589
- return;
30590
30599
  }
30591
- this.opts.client.ackDispatch(this.opts.config.org_id, {
30600
+ return this.opts.client.ackDispatch(this.opts.config.org_id, {
30592
30601
  source_type: "task_activity",
30593
30602
  source_id: data.id
30594
- }).catch(() => {
30595
- });
30603
+ }).then(() => void 0, () => void 0);
30596
30604
  });
30597
30605
  } catch (err) {
30598
30606
  this.opts.log?.error(`task dispatch failed for ${data.id}: ${String(err)}`);
30599
30607
  }
30600
30608
  });
30601
30609
  ws.on("dispatch.new", async (data) => {
30602
- if (data.event_type === "task_comment") {
30610
+ if (data.event_type === "task_assign") {
30611
+ if (!data.task_id)
30612
+ return;
30613
+ try {
30614
+ await this.handleTaskAssignmentRedrive(data);
30615
+ } catch (err) {
30616
+ this.opts.log?.error(`task assignment re-drive failed for ${data.task_id}: ${String(err)}`);
30617
+ }
30618
+ } else if (data.event_type === "task_comment") {
30603
30619
  if (!data.source_id || !data.task_id)
30604
30620
  return;
30605
30621
  try {
30606
- await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.handleTaskComment(data.source_id, data.task_id ?? "", data.actor_id, data.delivery_reason), () => {
30607
- this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {
30608
- });
30609
- });
30622
+ await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.handleTaskComment(data.source_id, data.task_id ?? "", data.actor_id, data.delivery_reason), () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)));
30610
30623
  } catch (err) {
30611
30624
  this.opts.log?.error(`task comment dispatch failed for ${data.source_id}: ${String(err)}`);
30612
30625
  }
@@ -30614,10 +30627,7 @@ var ParallAgentGateway = class {
30614
30627
  if (!data.source_id)
30615
30628
  return;
30616
30629
  try {
30617
- await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.handleWikiComment(data.source_id, data.actor_id, data.delivery_reason), () => {
30618
- this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {
30619
- });
30620
- });
30630
+ await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.handleWikiComment(data.source_id, data.actor_id, data.delivery_reason), () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)));
30621
30631
  } catch (err) {
30622
30632
  this.opts.log?.error(`wiki comment dispatch failed for ${data.source_id}: ${String(err)}`);
30623
30633
  }
@@ -30628,10 +30638,7 @@ var ParallAgentGateway = class {
30628
30638
  await this.consumeTypedDispatch({ dispatchEventId: data.id }, (dispatchEventId) => this.handleTaskDispatch(data.task_id ?? "", data.source_id ?? data.task_id ?? "", {
30629
30639
  allowCreator: true,
30630
30640
  dispatchEventId
30631
- }), () => {
30632
- this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {
30633
- });
30634
- });
30641
+ }), () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)));
30635
30642
  } catch (err) {
30636
30643
  this.opts.log?.error(`task update dispatch failed for ${data.task_id}: ${String(err)}`);
30637
30644
  }
@@ -30639,10 +30646,7 @@ var ParallAgentGateway = class {
30639
30646
  if (!data.source_id)
30640
30647
  return;
30641
30648
  try {
30642
- await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleScheduleFire(data.source_id, data.actor_id), () => {
30643
- this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {
30644
- });
30645
- });
30649
+ await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleScheduleFire(data.source_id, data.actor_id), () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)));
30646
30650
  } catch (err) {
30647
30651
  this.opts.log?.error(`schedule fire dispatch failed for ${data.source_id}: ${String(err)}`);
30648
30652
  }
@@ -30650,10 +30654,7 @@ var ParallAgentGateway = class {
30650
30654
  if (!data.source_id)
30651
30655
  return;
30652
30656
  try {
30653
- await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleExternalTriggerRun(data.source_id), () => {
30654
- this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {
30655
- });
30656
- });
30657
+ await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleExternalTriggerRun(data.source_id), () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)));
30657
30658
  } catch (err) {
30658
30659
  this.opts.log?.error(`external trigger dispatch failed for ${data.source_id}: ${String(err)}`);
30659
30660
  }
@@ -30661,10 +30662,7 @@ var ParallAgentGateway = class {
30661
30662
  if (!data.source_id)
30662
30663
  return;
30663
30664
  try {
30664
- await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleChannelMessage(data.source_id), () => {
30665
- this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {
30666
- });
30667
- });
30665
+ await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleChannelMessage(data.source_id), () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)));
30668
30666
  } catch (err) {
30669
30667
  this.opts.log?.error(`channel message dispatch failed for ${data.source_id}: ${String(err)}`);
30670
30668
  }
@@ -30672,10 +30670,7 @@ var ParallAgentGateway = class {
30672
30670
  if (!data.source_id)
30673
30671
  return;
30674
30672
  try {
30675
- await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleApprovalDecided(data.source_id, data.actor_id, data.chat_id ?? null), () => {
30676
- this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {
30677
- });
30678
- });
30673
+ await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleApprovalDecided(data.source_id, data.actor_id, data.chat_id ?? null), () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)));
30679
30674
  } catch (err) {
30680
30675
  this.opts.log?.error(`approval decided dispatch failed for ${data.source_id}: ${String(err)}`);
30681
30676
  }
@@ -30685,7 +30680,7 @@ var ParallAgentGateway = class {
30685
30680
  } catch (err) {
30686
30681
  this.opts.log?.error(`message re-drive failed for ${data.source_id}: ${String(err)}`);
30687
30682
  }
30688
- } else if (data.event_type !== "message" && data.event_type !== "task_assign") {
30683
+ } else if (data.event_type !== "message") {
30689
30684
  this.opts.log?.info(`dispatch.new with unhandled event_type=${String(data.event_type)} (id=${data.id}) \u2014 no-op`);
30690
30685
  }
30691
30686
  });
@@ -30753,6 +30748,50 @@ var ParallAgentGateway = class {
30753
30748
  consumeTypedDispatch(ref, run, ack) {
30754
30749
  return consumeTypedDispatch(this.laneFlowHost(), ref, run, ack);
30755
30750
  }
30751
+ // Typed completion must wait until the administrative ack has either
30752
+ // committed or failed. Errors stay best-effort: a failed ack leaves the row
30753
+ // received, so Complete releases and re-drives it safely.
30754
+ ackDispatchEvent(dispatchEventId, onFailure) {
30755
+ return this.opts.client.ackDispatchByID(this.opts.config.org_id, dispatchEventId).then(() => void 0, (err) => {
30756
+ onFailure?.();
30757
+ this.opts.log?.warn(`dispatch ack failed for ${dispatchEventId}, releasing for re-drive: ${String(err)}`);
30758
+ });
30759
+ }
30760
+ clearTypedDispatchDedupe(item) {
30761
+ switch (item.event_type) {
30762
+ case "task_assign":
30763
+ case "task_update":
30764
+ if (item.task_id) {
30765
+ const prefix = `${item.task_id}:`;
30766
+ for (const key of this.dispatchedTasks) {
30767
+ if (key.startsWith(prefix))
30768
+ this.dispatchedTasks.delete(key);
30769
+ }
30770
+ }
30771
+ break;
30772
+ case "task_comment":
30773
+ case "wiki_comment":
30774
+ if (item.source_id)
30775
+ this.dispatchedTasks.delete(`comment:${item.source_id}`);
30776
+ break;
30777
+ case "schedule.fire":
30778
+ if (item.source_id)
30779
+ this.dispatchedTasks.delete(`schedule_run:${item.source_id}`);
30780
+ break;
30781
+ case "external_trigger":
30782
+ if (item.source_id)
30783
+ this.dispatchedTasks.delete(`external_trigger_run:${item.source_id}`);
30784
+ break;
30785
+ case "channel_message":
30786
+ if (item.source_id)
30787
+ this.dispatchedMessages.delete(`channel_message:${item.source_id}`);
30788
+ break;
30789
+ case "approval_decided":
30790
+ if (item.source_id)
30791
+ this.dispatchedTasks.delete(`approval:${item.source_id}`);
30792
+ break;
30793
+ }
30794
+ }
30756
30795
  buildDispatchContext(event, sessionKey) {
30757
30796
  const binding = this.sessionBindings.get(sessionKey);
30758
30797
  return {
@@ -31719,6 +31758,17 @@ var ParallAgentGateway = class {
31719
31758
  chat_id: item.chat_id
31720
31759
  });
31721
31760
  }
31761
+ // A task assignment's first delivery rides task.assigned, but a typed lane
31762
+ // released without an effect is re-driven as dispatch.new. Reuse the exact
31763
+ // typed claim path used by catch-up so that recovery does not require a
31764
+ // runtime reconnect.
31765
+ async handleTaskAssignmentRedrive(item) {
31766
+ if (!item.task_id)
31767
+ return;
31768
+ await this.consumeTypedDispatch({ dispatchEventId: item.id }, (dispatchEventId) => this.handleTaskDispatch(item.task_id ?? "", item.source_id ?? item.task_id ?? "", {
31769
+ dispatchEventId
31770
+ }), (dispatchEventId) => this.ackDispatchEvent(dispatchEventId ?? item.id, () => this.clearTypedDispatchDedupe(item)));
31771
+ }
31722
31772
  consumeMessageWorkItem(item) {
31723
31773
  return consumeMessageWorkItem(this.laneFlowHost(), item);
31724
31774
  }
@@ -32184,10 +32234,7 @@ var ParallAgentGateway = class {
32184
32234
  }
32185
32235
  processed++;
32186
32236
  try {
32187
- const ackItem = () => {
32188
- this.opts.client.ackDispatchByID(this.opts.config.org_id, item.id).catch(() => {
32189
- });
32190
- };
32237
+ const ackItem = () => this.ackDispatchEvent(item.id, () => this.clearTypedDispatchDedupe(item));
32191
32238
  if (item.event_type === "task_assign" && item.task_id) {
32192
32239
  try {
32193
32240
  await this.consumeTypedDispatch({ dispatchEventId: item.id }, (dispatchEventId) => this.handleTaskDispatch(item.task_id ?? "", item.source_id ?? item.task_id ?? "", {
@@ -33361,10 +33408,12 @@ function normalizeApprovalPolicy(value) {
33361
33408
  never: "never",
33362
33409
  "on-request": "on-request",
33363
33410
  onrequest: "on-request",
33364
- "unless-trusted": "unless-trusted",
33365
- unlesstrusted: "unless-trusted",
33366
- "on-failure": "on-failure",
33367
- onfailure: "on-failure"
33411
+ untrusted: "untrusted",
33412
+ "unless-trusted": "untrusted",
33413
+ unlesstrusted: "untrusted",
33414
+ granular: "granular",
33415
+ "on-failure": "on-request",
33416
+ onfailure: "on-request"
33368
33417
  };
33369
33418
  return map[value.toLowerCase()] ?? value;
33370
33419
  }
@@ -34497,10 +34546,17 @@ var CodexAppServerAdapter = class {
34497
34546
  log2?.warn?.(`failed to prepare local attachments: ${errToString(err)}`);
34498
34547
  }
34499
34548
  const turnInput = buildTurnInput(preparedBody, preparedImages);
34500
- const startTurn = (targetThreadId) => client.sendRequest("turn/start", {
34501
- threadId: targetThreadId,
34502
- input: turnInput
34503
- });
34549
+ const startTurn = (targetThreadId) => {
34550
+ const turnParams = {
34551
+ threadId: targetThreadId,
34552
+ input: turnInput
34553
+ };
34554
+ if (this.opts.model)
34555
+ turnParams.model = this.opts.model;
34556
+ if (this.opts.reasoningEffort)
34557
+ turnParams.effort = this.opts.reasoningEffort;
34558
+ return client.sendRequest("turn/start", turnParams);
34559
+ };
34504
34560
  let turnStartResult;
34505
34561
  try {
34506
34562
  turnStartResult = await startTurn(threadId);
@@ -34617,7 +34673,7 @@ var CodexAppServerAdapter = class {
34617
34673
  if (this.opts.model)
34618
34674
  forkParams.model = this.opts.model;
34619
34675
  if (this.opts.reasoningEffort) {
34620
- forkParams.config = { modelReasoningEffort: this.opts.reasoningEffort };
34676
+ forkParams.config = { model_reasoning_effort: this.opts.reasoningEffort };
34621
34677
  }
34622
34678
  const result = await client.sendRequest("thread/fork", forkParams);
34623
34679
  const forkedThreadId = extractThreadId(result);
@@ -34771,7 +34827,7 @@ var CodexAppServerAdapter = class {
34771
34827
  if (this.opts.model)
34772
34828
  commonParams.model = this.opts.model;
34773
34829
  if (this.opts.reasoningEffort) {
34774
- commonParams.config = { modelReasoningEffort: this.opts.reasoningEffort };
34830
+ commonParams.config = { model_reasoning_effort: this.opts.reasoningEffort };
34775
34831
  }
34776
34832
  const method = opts.resumeId ? "thread/resume" : "thread/start";
34777
34833
  const params = opts.resumeId ? { threadId: opts.resumeId, ...commonParams } : { cwd: this.opts.workspaceDir, ...commonParams };
@@ -35673,7 +35729,220 @@ function parse(toml, { maxDepth = 1e3, integersAsBigInt } = {}) {
35673
35729
  }
35674
35730
 
35675
35731
  // ts/codex-agent/dist/workspace.js
35732
+ var CONFIG_LOCK_TIMINGS = {
35733
+ staleMs: 1e4,
35734
+ waitMs: 15e3,
35735
+ retryMs: 25
35736
+ };
35737
+ var SLEEP_SIGNAL = new Int32Array(new SharedArrayBuffer(4));
35738
+ function sleepSync(ms) {
35739
+ Atomics.wait(SLEEP_SIGNAL, 0, 0, ms);
35740
+ }
35741
+ function withConfigLock(codexHome, log2, fn) {
35742
+ const queueDir = path8.join(codexHome, "config.toml.lock.d");
35743
+ let ticketName = bakeryEnqueue(queueDir);
35744
+ let ticketPath = ticketName ? path8.join(queueDir, ticketName) : "";
35745
+ let acquired = false;
35746
+ let heldPath = "";
35747
+ const deadline = Date.now() + CONFIG_LOCK_TIMINGS.waitMs;
35748
+ while (ticketName && Date.now() < deadline) {
35749
+ let names;
35750
+ try {
35751
+ names = fs8.readdirSync(queueDir).sort();
35752
+ } catch {
35753
+ break;
35754
+ }
35755
+ if (!names.includes(ticketName)) {
35756
+ ticketName = bakeryEnqueue(queueDir);
35757
+ if (!ticketName)
35758
+ break;
35759
+ ticketPath = path8.join(queueDir, ticketName);
35760
+ continue;
35761
+ }
35762
+ const now = Date.now();
35763
+ let rescan = false;
35764
+ let blocked = false;
35765
+ let head;
35766
+ for (const name of names) {
35767
+ const entryPath = path8.join(queueDir, name);
35768
+ if (name.startsWith("held-")) {
35769
+ const enteredAt = Number.parseInt(name.slice(5, 20), 10);
35770
+ if (Number.isFinite(enteredAt) && now - enteredAt > CONFIG_LOCK_TIMINGS.staleMs) {
35771
+ try {
35772
+ fs8.unlinkSync(entryPath);
35773
+ } catch {
35774
+ }
35775
+ rescan = true;
35776
+ break;
35777
+ }
35778
+ blocked = true;
35779
+ break;
35780
+ }
35781
+ let mtimeMs;
35782
+ try {
35783
+ mtimeMs = fs8.statSync(entryPath).mtimeMs;
35784
+ } catch {
35785
+ rescan = true;
35786
+ break;
35787
+ }
35788
+ if (now - mtimeMs > CONFIG_LOCK_TIMINGS.staleMs) {
35789
+ try {
35790
+ fs8.unlinkSync(entryPath);
35791
+ } catch {
35792
+ }
35793
+ rescan = true;
35794
+ break;
35795
+ }
35796
+ if (name.startsWith("choosing-")) {
35797
+ blocked = true;
35798
+ break;
35799
+ }
35800
+ if (name.startsWith("t-")) {
35801
+ head = name;
35802
+ break;
35803
+ }
35804
+ }
35805
+ if (rescan)
35806
+ continue;
35807
+ if (!blocked && head === ticketName) {
35808
+ CONFIG_LOCK_TEST_HOOKS.beforeTicketEntry?.();
35809
+ const heldName = `held-${String(Date.now()).padStart(15, "0")}-${ticketName.slice(2)}`;
35810
+ const candidateHeldPath = path8.join(queueDir, heldName);
35811
+ try {
35812
+ fs8.renameSync(ticketPath, candidateHeldPath);
35813
+ } catch {
35814
+ continue;
35815
+ }
35816
+ heldPath = candidateHeldPath;
35817
+ acquired = true;
35818
+ break;
35819
+ }
35820
+ try {
35821
+ const t = /* @__PURE__ */ new Date();
35822
+ fs8.utimesSync(ticketPath, t, t);
35823
+ } catch {
35824
+ }
35825
+ sleepSync(CONFIG_LOCK_TIMINGS.retryMs);
35826
+ }
35827
+ if (!acquired) {
35828
+ log2?.warn(`could not acquire ${queueDir}; writing config.toml without lock`);
35829
+ if (ticketName) {
35830
+ try {
35831
+ fs8.unlinkSync(ticketPath);
35832
+ } catch {
35833
+ }
35834
+ }
35835
+ }
35836
+ try {
35837
+ fn();
35838
+ } finally {
35839
+ if (acquired) {
35840
+ try {
35841
+ fs8.unlinkSync(heldPath);
35842
+ } catch {
35843
+ }
35844
+ }
35845
+ }
35846
+ }
35847
+ var CONFIG_LOCK_TEST_HOOKS = {};
35848
+ function bakeryEnqueue(queueDir) {
35849
+ for (let attempt = 0; attempt < 3; attempt++) {
35850
+ const token = `${process.pid}-${Math.random().toString(36).slice(2, 10)}`;
35851
+ const markerPath = path8.join(queueDir, `choosing-${token}`);
35852
+ try {
35853
+ fs8.mkdirSync(queueDir, { recursive: true });
35854
+ CONFIG_LOCK_TEST_HOOKS.beforeChoosingMarker?.();
35855
+ fs8.writeFileSync(markerPath, "", { flag: "wx" });
35856
+ } catch {
35857
+ return null;
35858
+ }
35859
+ try {
35860
+ let maxSeq = 0;
35861
+ for (const name of fs8.readdirSync(queueDir)) {
35862
+ if (!name.startsWith("t-"))
35863
+ continue;
35864
+ const seq = Number.parseInt(name.slice(2, 12), 10);
35865
+ if (Number.isFinite(seq) && seq > maxSeq)
35866
+ maxSeq = seq;
35867
+ }
35868
+ const ticketName = `t-${String(maxSeq + 1).padStart(10, "0")}-${token}`;
35869
+ const ticketPath = path8.join(queueDir, ticketName);
35870
+ CONFIG_LOCK_TEST_HOOKS.beforeTicketPublish?.();
35871
+ fs8.renameSync(markerPath, ticketPath);
35872
+ try {
35873
+ const t = /* @__PURE__ */ new Date();
35874
+ fs8.utimesSync(ticketPath, t, t);
35875
+ } catch {
35876
+ }
35877
+ return ticketName;
35878
+ } catch (err) {
35879
+ try {
35880
+ fs8.unlinkSync(markerPath);
35881
+ } catch {
35882
+ }
35883
+ if (err.code === "ENOENT")
35884
+ continue;
35885
+ return null;
35886
+ }
35887
+ }
35888
+ return null;
35889
+ }
35890
+ function writeConfigAtomic(filePath, content) {
35891
+ const realPath = resolveWriteTarget(filePath);
35892
+ let mode;
35893
+ try {
35894
+ mode = fs8.statSync(realPath).mode & 511;
35895
+ } catch {
35896
+ }
35897
+ const tmpPath = `${realPath}.tmp-${process.pid}-${Math.random().toString(36).slice(2)}`;
35898
+ let tmpCreated = false;
35899
+ try {
35900
+ fs8.writeFileSync(tmpPath, content, { encoding: "utf8", mode: 384, flag: "wx" });
35901
+ tmpCreated = true;
35902
+ if (mode !== void 0)
35903
+ fs8.chmodSync(tmpPath, mode);
35904
+ fs8.renameSync(tmpPath, realPath);
35905
+ } catch (err) {
35906
+ if (tmpCreated || err.code !== "EEXIST") {
35907
+ try {
35908
+ fs8.unlinkSync(tmpPath);
35909
+ } catch {
35910
+ }
35911
+ }
35912
+ throw err;
35913
+ }
35914
+ }
35915
+ function resolveWriteTarget(filePath) {
35916
+ try {
35917
+ return fs8.realpathSync(filePath);
35918
+ } catch (err) {
35919
+ if (err.code !== "ENOENT")
35920
+ throw err;
35921
+ }
35922
+ const seen = /* @__PURE__ */ new Set();
35923
+ let p = path8.resolve(filePath);
35924
+ for (let depth = 0; depth < 40; depth++) {
35925
+ if (seen.has(p)) {
35926
+ throw new Error(`symlink cycle at ${p} while resolving ${filePath}`);
35927
+ }
35928
+ seen.add(p);
35929
+ let link;
35930
+ try {
35931
+ link = fs8.readlinkSync(p);
35932
+ } catch (err) {
35933
+ const code = err.code;
35934
+ if (code === "ENOENT" || code === "EINVAL")
35935
+ return p;
35936
+ throw err;
35937
+ }
35938
+ p = path8.resolve(path8.dirname(p), link);
35939
+ }
35940
+ throw new Error(`symlink chain deeper than 40 while resolving ${filePath}`);
35941
+ }
35676
35942
  function ensureWorkspaceTrusted(codexHome, workspaceDir, log2) {
35943
+ withConfigLock(codexHome, log2, () => ensureWorkspaceTrustedLocked(codexHome, workspaceDir, log2));
35944
+ }
35945
+ function ensureWorkspaceTrustedLocked(codexHome, workspaceDir, log2) {
35677
35946
  const configPath = path8.join(codexHome, "config.toml");
35678
35947
  const normalizedPath = path8.resolve(workspaceDir);
35679
35948
  try {
@@ -35694,8 +35963,13 @@ function ensureWorkspaceTrusted(codexHome, workspaceDir, log2) {
35694
35963
  }
35695
35964
  }
35696
35965
  const projects = parsed?.projects;
35697
- if (projects?.[normalizedPath]?.trust_level === "trusted")
35966
+ const existingTrust = projects?.[normalizedPath]?.trust_level;
35967
+ if (existingTrust === "trusted")
35968
+ return;
35969
+ if (existingTrust !== void 0) {
35970
+ log2?.warn(`Codex config marks ${normalizedPath} as trust_level=${JSON.stringify(existingTrust)}; respecting the explicit decision \u2014 project-level developer_instructions will not load for this workspace`);
35698
35971
  return;
35972
+ }
35699
35973
  const escapedPath = normalizedPath.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
35700
35974
  const sectionHeader = `[projects."${escapedPath}"]`;
35701
35975
  const trustLine = 'trust_level = "trusted"';
@@ -35719,7 +35993,7 @@ ${trustLine}
35719
35993
  content = content.substring(0, afterHeader) + trustLine + "\n" + content.substring(afterHeader);
35720
35994
  }
35721
35995
  }
35722
- fs8.writeFileSync(configPath, content, "utf8");
35996
+ writeConfigAtomic(configPath, content);
35723
35997
  } else if (projects?.[normalizedPath] !== void 0) {
35724
35998
  log2?.warn(`Codex config.toml has non-canonical header for ${normalizedPath}; skipping trust write`);
35725
35999
  } else if (projects && !content.includes("[projects.")) {
@@ -35743,6 +36017,9 @@ function isParallProxyMode(env = process.env) {
35743
36017
  return baseUrl === apiUrl || baseUrl.startsWith(`${apiUrl}/`);
35744
36018
  }
35745
36019
  function ensureParallProvider(codexHome, apiUrl, log2) {
36020
+ withConfigLock(codexHome, log2, () => ensureParallProviderLocked(codexHome, apiUrl));
36021
+ }
36022
+ function ensureParallProviderLocked(codexHome, apiUrl) {
35746
36023
  const configPath = path8.join(codexHome, "config.toml");
35747
36024
  const baseUrl = apiUrl.replace(/\/$/, "") + "/api/llm/v1";
35748
36025
  try {
@@ -35780,7 +36057,7 @@ function ensureParallProvider(codexHome, apiUrl, log2) {
35780
36057
  content = content.trimEnd() + "\n\n" + providerBlock + "\n";
35781
36058
  }
35782
36059
  fs8.mkdirSync(codexHome, { recursive: true });
35783
- fs8.writeFileSync(configPath, content, "utf8");
36060
+ writeConfigAtomic(configPath, content);
35784
36061
  } catch (err) {
35785
36062
  throw new Error(`failed to write Parall provider config: ${String(err)}`);
35786
36063
  }
@@ -83,6 +83,9 @@ var init_daemon_update_mode = __esm({
83
83
  });
84
84
 
85
85
  // ts/agent-core/dist/provider-config.js
86
+ function llmSource(pc) {
87
+ return effectiveLLMSourceExplicit(pc) || "parall";
88
+ }
86
89
  function effectiveLLMSourceExplicit(pc) {
87
90
  if (pc?.llm_source)
88
91
  return pc.llm_source;
@@ -461,6 +464,7 @@ var init_constants = __esm({
461
464
  DISPATCH_CLAIM: (orgId) => `${API_BASE}/orgs/${orgId}/dispatch/claim`,
462
465
  DISPATCH_STEER: (orgId) => `${API_BASE}/orgs/${orgId}/dispatch/steer`,
463
466
  DISPATCH_COMPLETE: (orgId) => `${API_BASE}/orgs/${orgId}/dispatch/complete`,
467
+ DISPATCH_COMPLETE_SOURCES: (orgId) => `${API_BASE}/orgs/${orgId}/dispatch/complete-sources`,
464
468
  DISPATCH_RELEASE: (orgId) => `${API_BASE}/orgs/${orgId}/dispatch/release`,
465
469
  DISPATCH_HEARTBEAT: (orgId) => `${API_BASE}/orgs/${orgId}/dispatch/heartbeat`,
466
470
  // Unread
@@ -1438,9 +1442,9 @@ var init_client = __esm({
1438
1442
  async retryAgentWorkspaceSetup(orgId, machineId, agentId) {
1439
1443
  return this.request("POST", ENDPOINTS.MACHINE_AGENT_WORKSPACE_SETUP(orgId, machineId, agentId));
1440
1444
  }
1441
- async patchMachineLLMSource(orgId, machineId, llmSource) {
1445
+ async patchMachineLLMSource(orgId, machineId, llmSource2) {
1442
1446
  return this.request("PATCH", ENDPOINTS.MACHINE_LLM_SOURCE(orgId, machineId), {
1443
- llm_source: llmSource
1447
+ llm_source: llmSource2
1444
1448
  });
1445
1449
  }
1446
1450
  /**
@@ -1667,7 +1671,7 @@ var init_client = __esm({
1667
1671
  async claimDispatch(orgId, req) {
1668
1672
  return this.request("POST", ENDPOINTS.DISPATCH_CLAIM(orgId), req);
1669
1673
  }
1670
- /** Fold a pending same-target WorkItem into a live lane (409 STALE_LANE when dethroned). */
1674
+ /** Fold into a live lane: 409 STALE_LANE when dethroned; 409 INELIGIBLE_REF for a bad ref. */
1671
1675
  async steerDispatch(orgId, req) {
1672
1676
  return this.request("POST", ENDPOINTS.DISPATCH_STEER(orgId), req);
1673
1677
  }
@@ -1675,6 +1679,14 @@ var init_client = __esm({
1675
1679
  async completeDispatch(orgId, req) {
1676
1680
  return this.request("POST", ENDPOINTS.DISPATCH_COMPLETE(orgId), req);
1677
1681
  }
1682
+ /**
1683
+ * End a turn for a lane-less runtime: resolve the turn's folded WorkItems
1684
+ * by source — broad-cover to the turn's reply Effect when one exists,
1685
+ * no_action sweep otherwise. Idempotent.
1686
+ */
1687
+ async completeDispatchSources(orgId, req) {
1688
+ return this.request("POST", ENDPOINTS.DISPATCH_COMPLETE_SOURCES(orgId), req);
1689
+ }
1678
1690
  /** Release a lane on graceful shutdown — members return to pending immediately. */
1679
1691
  async releaseDispatchLane(orgId, lane) {
1680
1692
  return this.request("POST", ENDPOINTS.DISPATCH_RELEASE(orgId), { lane });
@@ -31144,7 +31156,9 @@ var init_runtimes = __esm({
31144
31156
  args: [],
31145
31157
  buildEnv(baseEnv, agentId, orgId, apiKey, dirs, pc) {
31146
31158
  const env = buildStandardEnv(baseEnv, agentId, orgId, apiKey, dirs, pc);
31147
- env.PRLL_CODEX_HOME = path9.join(dirs.stateDir, ".codex");
31159
+ if (baseEnv.KUBERNETES_SERVICE_HOST || llmSource(pc) !== "runtime_auth") {
31160
+ env.PRLL_CODEX_HOME = path9.join(dirs.stateDir, ".codex");
31161
+ }
31148
31162
  return env;
31149
31163
  }
31150
31164
  };
@@ -1 +1 @@
1
- {"version":3,"file":"runtimes.d.ts","sourceRoot":"","sources":["../src/runtimes.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAGzD,YAAY,EAAE,cAAc,EAAE,CAAC;AAC/B,OAAO,EAAE,qBAAqB,EAAE,CAAC;AAEjC,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,QAAQ,CACN,OAAO,EAAE,MAAM,CAAC,UAAU,EAC1B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,SAAS,EACf,EAAE,CAAC,EAAE,cAAc,GAClB,MAAM,CAAC,UAAU,CAAC;CACtB;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;AAuED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,cAAc,CAerE;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAInD"}
1
+ {"version":3,"file":"runtimes.d.ts","sourceRoot":"","sources":["../src/runtimes.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,qBAAqB,EAAa,MAAM,oBAAoB,CAAC;AACtE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAGzD,YAAY,EAAE,cAAc,EAAE,CAAC;AAC/B,OAAO,EAAE,qBAAqB,EAAE,CAAC;AAEjC,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,QAAQ,CACN,OAAO,EAAE,MAAM,CAAC,UAAU,EAC1B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,SAAS,EACf,EAAE,CAAC,EAAE,cAAc,GAClB,MAAM,CAAC,UAAU,CAAC;CACtB;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;AAoFD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,cAAc,CAerE;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAInD"}
package/dist/runtimes.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as fs from 'node:fs';
2
2
  import * as path from 'node:path';
3
- import { clearAllProviderCreds } from '@parall/agent-core';
3
+ import { clearAllProviderCreds, llmSource } from '@parall/agent-core';
4
4
  import { resolveBundleDir } from './config.js';
5
5
  export { clearAllProviderCreds };
6
6
  function buildStandardEnv(baseEnv, agentId, orgId, apiKey, dirs, pc) {
@@ -31,7 +31,20 @@ const codexAdapter = {
31
31
  args: [],
32
32
  buildEnv(baseEnv, agentId, orgId, apiKey, dirs, pc) {
33
33
  const env = buildStandardEnv(baseEnv, agentId, orgId, apiKey, dirs, pc);
34
- env.PRLL_CODEX_HOME = path.join(dirs.stateDir, '.codex');
34
+ // CODEX_HOME placement: shared with the operator ONLY for local
35
+ // runtime_auth agents — they need the credential `codex login` writes to
36
+ // CODEX_HOME / ~/.codex (a headless child cannot log in itself; an
37
+ // isolated home strands them in a permanent 401 loop), and they never
38
+ // write the [model_providers.parall] block. Every other source keeps the
39
+ // isolated per-agent home: a parall-mode bridge rewrites that provider
40
+ // block with ITS OWN api_url and threads select the provider by fixed
41
+ // name, so through a shared config.toml a staging daemon's rewrite would
42
+ // route a coexisting prod agent's traffic (and its agk_ key) to the
43
+ // staging endpoint. K8s daemons always isolate, mirroring the Claude
44
+ // home policy (supervisor spawnAgentOnce).
45
+ if (baseEnv.KUBERNETES_SERVICE_HOST || llmSource(pc) !== 'runtime_auth') {
46
+ env.PRLL_CODEX_HOME = path.join(dirs.stateDir, '.codex');
47
+ }
35
48
  return env;
36
49
  },
37
50
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parall/daemon",
3
- "version": "1.41.0",
3
+ "version": "1.42.1",
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.41.0",
36
- "@parall/sdk": "1.41.0",
37
- "@parall/claude-agent": "1.41.0",
38
- "@parall/codex-agent": "1.41.0",
39
- "@parall/openclaw-agent": "1.41.0"
35
+ "@parall/sdk": "1.42.1",
36
+ "@parall/claude-agent": "1.42.1",
37
+ "@parall/codex-agent": "1.42.1",
38
+ "@parall/openclaw-agent": "1.42.1",
39
+ "@parall/agent-core": "1.42.1"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/node": "^22.0.0",