@parall/parall 1.42.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.
@@ -28578,7 +28578,7 @@ var ParallClient = class _ParallClient {
28578
28578
  async claimDispatch(orgId, req) {
28579
28579
  return this.request("POST", ENDPOINTS.DISPATCH_CLAIM(orgId), req);
28580
28580
  }
28581
- /** Fold a pending same-target WorkItem into a live lane (409 STALE_LANE when dethroned). */
28581
+ /** Fold into a live lane: 409 STALE_LANE when dethroned; 409 INELIGIBLE_REF for a bad ref. */
28582
28582
  async steerDispatch(orgId, req) {
28583
28583
  return this.request("POST", ENDPOINTS.DISPATCH_STEER(orgId), req);
28584
28584
  }
@@ -30055,7 +30055,7 @@ async function dispatchLaneGroup(host, opts) {
30055
30055
  async function consumeTypedDispatch(host, ref, run, ack) {
30056
30056
  if (!host.laneLedger || host.ledgerDisabled) {
30057
30057
  if (await run(ref.dispatchEventId))
30058
- ack(ref.dispatchEventId);
30058
+ await ack(ref.dispatchEventId);
30059
30059
  return;
30060
30060
  }
30061
30061
  let lane;
@@ -30065,7 +30065,7 @@ async function consumeTypedDispatch(host, ref, run, ack) {
30065
30065
  if (err instanceof LedgerUnsupportedError) {
30066
30066
  host.disableLedger("claim endpoint missing");
30067
30067
  if (await run(ref.dispatchEventId))
30068
- ack(ref.dispatchEventId);
30068
+ await ack(ref.dispatchEventId);
30069
30069
  return;
30070
30070
  }
30071
30071
  throw err;
@@ -30076,7 +30076,7 @@ async function consumeTypedDispatch(host, ref, run, ack) {
30076
30076
  }
30077
30077
  try {
30078
30078
  if (await run(lane.typedDispatchEventId))
30079
- ack(lane.typedDispatchEventId);
30079
+ await ack(lane.typedDispatchEventId);
30080
30080
  } finally {
30081
30081
  await host.laneLedger.completeIfIdle(lane.laneKey, false).catch(() => {
30082
30082
  });
@@ -30530,29 +30530,33 @@ var ParallAgentGateway = class {
30530
30530
  try {
30531
30531
  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) => {
30532
30532
  if (dispatchEventId) {
30533
- this.opts.client.ackDispatchByID(this.opts.config.org_id, dispatchEventId).catch(() => {
30533
+ return this.ackDispatchEvent(dispatchEventId, () => {
30534
+ this.dispatchedTasks.delete(`${data.id}:${data.updated_at}`);
30534
30535
  });
30535
- return;
30536
30536
  }
30537
- this.opts.client.ackDispatch(this.opts.config.org_id, {
30537
+ return this.opts.client.ackDispatch(this.opts.config.org_id, {
30538
30538
  source_type: "task_activity",
30539
30539
  source_id: data.id
30540
- }).catch(() => {
30541
- });
30540
+ }).then(() => void 0, () => void 0);
30542
30541
  });
30543
30542
  } catch (err) {
30544
30543
  this.opts.log?.error(`task dispatch failed for ${data.id}: ${String(err)}`);
30545
30544
  }
30546
30545
  });
30547
30546
  ws.on("dispatch.new", async (data) => {
30548
- if (data.event_type === "task_comment") {
30547
+ if (data.event_type === "task_assign") {
30548
+ if (!data.task_id)
30549
+ return;
30550
+ try {
30551
+ await this.handleTaskAssignmentRedrive(data);
30552
+ } catch (err) {
30553
+ this.opts.log?.error(`task assignment re-drive failed for ${data.task_id}: ${String(err)}`);
30554
+ }
30555
+ } else if (data.event_type === "task_comment") {
30549
30556
  if (!data.source_id || !data.task_id)
30550
30557
  return;
30551
30558
  try {
30552
- await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.handleTaskComment(data.source_id, data.task_id ?? "", data.actor_id, data.delivery_reason), () => {
30553
- this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {
30554
- });
30555
- });
30559
+ 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)));
30556
30560
  } catch (err) {
30557
30561
  this.opts.log?.error(`task comment dispatch failed for ${data.source_id}: ${String(err)}`);
30558
30562
  }
@@ -30560,10 +30564,7 @@ var ParallAgentGateway = class {
30560
30564
  if (!data.source_id)
30561
30565
  return;
30562
30566
  try {
30563
- await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.handleWikiComment(data.source_id, data.actor_id, data.delivery_reason), () => {
30564
- this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {
30565
- });
30566
- });
30567
+ await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.handleWikiComment(data.source_id, data.actor_id, data.delivery_reason), () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)));
30567
30568
  } catch (err) {
30568
30569
  this.opts.log?.error(`wiki comment dispatch failed for ${data.source_id}: ${String(err)}`);
30569
30570
  }
@@ -30574,10 +30575,7 @@ var ParallAgentGateway = class {
30574
30575
  await this.consumeTypedDispatch({ dispatchEventId: data.id }, (dispatchEventId) => this.handleTaskDispatch(data.task_id ?? "", data.source_id ?? data.task_id ?? "", {
30575
30576
  allowCreator: true,
30576
30577
  dispatchEventId
30577
- }), () => {
30578
- this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {
30579
- });
30580
- });
30578
+ }), () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)));
30581
30579
  } catch (err) {
30582
30580
  this.opts.log?.error(`task update dispatch failed for ${data.task_id}: ${String(err)}`);
30583
30581
  }
@@ -30585,10 +30583,7 @@ var ParallAgentGateway = class {
30585
30583
  if (!data.source_id)
30586
30584
  return;
30587
30585
  try {
30588
- await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleScheduleFire(data.source_id, data.actor_id), () => {
30589
- this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {
30590
- });
30591
- });
30586
+ await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleScheduleFire(data.source_id, data.actor_id), () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)));
30592
30587
  } catch (err) {
30593
30588
  this.opts.log?.error(`schedule fire dispatch failed for ${data.source_id}: ${String(err)}`);
30594
30589
  }
@@ -30596,10 +30591,7 @@ var ParallAgentGateway = class {
30596
30591
  if (!data.source_id)
30597
30592
  return;
30598
30593
  try {
30599
- await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleExternalTriggerRun(data.source_id), () => {
30600
- this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {
30601
- });
30602
- });
30594
+ await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleExternalTriggerRun(data.source_id), () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)));
30603
30595
  } catch (err) {
30604
30596
  this.opts.log?.error(`external trigger dispatch failed for ${data.source_id}: ${String(err)}`);
30605
30597
  }
@@ -30607,10 +30599,7 @@ var ParallAgentGateway = class {
30607
30599
  if (!data.source_id)
30608
30600
  return;
30609
30601
  try {
30610
- await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleChannelMessage(data.source_id), () => {
30611
- this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {
30612
- });
30613
- });
30602
+ await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleChannelMessage(data.source_id), () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)));
30614
30603
  } catch (err) {
30615
30604
  this.opts.log?.error(`channel message dispatch failed for ${data.source_id}: ${String(err)}`);
30616
30605
  }
@@ -30618,10 +30607,7 @@ var ParallAgentGateway = class {
30618
30607
  if (!data.source_id)
30619
30608
  return;
30620
30609
  try {
30621
- await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleApprovalDecided(data.source_id, data.actor_id, data.chat_id ?? null), () => {
30622
- this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {
30623
- });
30624
- });
30610
+ 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)));
30625
30611
  } catch (err) {
30626
30612
  this.opts.log?.error(`approval decided dispatch failed for ${data.source_id}: ${String(err)}`);
30627
30613
  }
@@ -30631,7 +30617,7 @@ var ParallAgentGateway = class {
30631
30617
  } catch (err) {
30632
30618
  this.opts.log?.error(`message re-drive failed for ${data.source_id}: ${String(err)}`);
30633
30619
  }
30634
- } else if (data.event_type !== "message" && data.event_type !== "task_assign") {
30620
+ } else if (data.event_type !== "message") {
30635
30621
  this.opts.log?.info(`dispatch.new with unhandled event_type=${String(data.event_type)} (id=${data.id}) \u2014 no-op`);
30636
30622
  }
30637
30623
  });
@@ -30699,6 +30685,50 @@ var ParallAgentGateway = class {
30699
30685
  consumeTypedDispatch(ref, run, ack) {
30700
30686
  return consumeTypedDispatch(this.laneFlowHost(), ref, run, ack);
30701
30687
  }
30688
+ // Typed completion must wait until the administrative ack has either
30689
+ // committed or failed. Errors stay best-effort: a failed ack leaves the row
30690
+ // received, so Complete releases and re-drives it safely.
30691
+ ackDispatchEvent(dispatchEventId, onFailure) {
30692
+ return this.opts.client.ackDispatchByID(this.opts.config.org_id, dispatchEventId).then(() => void 0, (err) => {
30693
+ onFailure?.();
30694
+ this.opts.log?.warn(`dispatch ack failed for ${dispatchEventId}, releasing for re-drive: ${String(err)}`);
30695
+ });
30696
+ }
30697
+ clearTypedDispatchDedupe(item) {
30698
+ switch (item.event_type) {
30699
+ case "task_assign":
30700
+ case "task_update":
30701
+ if (item.task_id) {
30702
+ const prefix = `${item.task_id}:`;
30703
+ for (const key of this.dispatchedTasks) {
30704
+ if (key.startsWith(prefix))
30705
+ this.dispatchedTasks.delete(key);
30706
+ }
30707
+ }
30708
+ break;
30709
+ case "task_comment":
30710
+ case "wiki_comment":
30711
+ if (item.source_id)
30712
+ this.dispatchedTasks.delete(`comment:${item.source_id}`);
30713
+ break;
30714
+ case "schedule.fire":
30715
+ if (item.source_id)
30716
+ this.dispatchedTasks.delete(`schedule_run:${item.source_id}`);
30717
+ break;
30718
+ case "external_trigger":
30719
+ if (item.source_id)
30720
+ this.dispatchedTasks.delete(`external_trigger_run:${item.source_id}`);
30721
+ break;
30722
+ case "channel_message":
30723
+ if (item.source_id)
30724
+ this.dispatchedMessages.delete(`channel_message:${item.source_id}`);
30725
+ break;
30726
+ case "approval_decided":
30727
+ if (item.source_id)
30728
+ this.dispatchedTasks.delete(`approval:${item.source_id}`);
30729
+ break;
30730
+ }
30731
+ }
30702
30732
  buildDispatchContext(event, sessionKey) {
30703
30733
  const binding = this.sessionBindings.get(sessionKey);
30704
30734
  return {
@@ -31665,6 +31695,17 @@ var ParallAgentGateway = class {
31665
31695
  chat_id: item.chat_id
31666
31696
  });
31667
31697
  }
31698
+ // A task assignment's first delivery rides task.assigned, but a typed lane
31699
+ // released without an effect is re-driven as dispatch.new. Reuse the exact
31700
+ // typed claim path used by catch-up so that recovery does not require a
31701
+ // runtime reconnect.
31702
+ async handleTaskAssignmentRedrive(item) {
31703
+ if (!item.task_id)
31704
+ return;
31705
+ await this.consumeTypedDispatch({ dispatchEventId: item.id }, (dispatchEventId) => this.handleTaskDispatch(item.task_id ?? "", item.source_id ?? item.task_id ?? "", {
31706
+ dispatchEventId
31707
+ }), (dispatchEventId) => this.ackDispatchEvent(dispatchEventId ?? item.id, () => this.clearTypedDispatchDedupe(item)));
31708
+ }
31668
31709
  consumeMessageWorkItem(item) {
31669
31710
  return consumeMessageWorkItem(this.laneFlowHost(), item);
31670
31711
  }
@@ -32130,10 +32171,7 @@ var ParallAgentGateway = class {
32130
32171
  }
32131
32172
  processed++;
32132
32173
  try {
32133
- const ackItem = () => {
32134
- this.opts.client.ackDispatchByID(this.opts.config.org_id, item.id).catch(() => {
32135
- });
32136
- };
32174
+ const ackItem = () => this.ackDispatchEvent(item.id, () => this.clearTypedDispatchDedupe(item));
32137
32175
  if (item.event_type === "task_assign" && item.task_id) {
32138
32176
  try {
32139
32177
  await this.consumeTypedDispatch({ dispatchEventId: item.id }, (dispatchEventId) => this.handleTaskDispatch(item.task_id ?? "", item.source_id ?? item.task_id ?? "", {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parall/parall",
3
- "version": "1.42.0",
3
+ "version": "1.42.1",
4
4
  "description": "OpenClaw channel plugin for Parall IM",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -16,8 +16,8 @@
16
16
  "openclaw.plugin.json"
17
17
  ],
18
18
  "dependencies": {
19
- "@parall/agent-core": "1.42.0",
20
- "@parall/sdk": "1.42.0"
19
+ "@parall/agent-core": "1.42.1",
20
+ "@parall/sdk": "1.42.1"
21
21
  },
22
22
  "devDependencies": {
23
23
  "@types/node": "^22.0.0",