@parall/agent-core 1.44.0 → 1.45.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -146,6 +146,14 @@ export declare class ParallAgentGateway {
146
146
  */
147
147
  consumeTurnError(sessionKey: string): boolean;
148
148
  private emitDispatchReceived;
149
+ /**
150
+ * Sticky: the server answered a by-id complete with 400 (predates the
151
+ * form). Typed resolution falls back to the legacy ack for the rest of
152
+ * the process lifetime.
153
+ */
154
+ typedByIdCompleteUnsupported: boolean;
155
+ private typedLedgerEventIds;
156
+ private resolveDispatchByID;
149
157
  /** True when this event's lifecycle is owned by the dispatch lane ledger. */
150
158
  private usesLaneLedger;
151
159
  private disableLedger;
@@ -1 +1 @@
1
- {"version":3,"file":"gateway-base.d.ts","sourceRoot":"","sources":["../src/gateway-base.ts"],"names":[],"mappings":"AAGA,OAAO,EAAiC,YAAY,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACpF,OAAO,KAAK,EACV,qBAAqB,EAkBtB,MAAM,aAAa,CAAC;AAOrB,OAAO,EAGL,KAAK,eAAe,EAGpB,KAAK,aAAa,EAEnB,MAAM,uBAAuB,CAAC;AAqC/B,OAAO,KAAK,EAAiB,WAAW,EAAE,MAAM,YAAY,CAAC;AAwB7D,MAAM,MAAM,mBAAmB,GAAG;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE;QAAE,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IAC1C,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,KAAK,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IACtC,WAAW,CAAC,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC7F,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAC/B;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,WAAW,CAAA;CAAE,GAC1C;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,GAClB;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,CAAC;AAExB,MAAM,MAAM,oBAAoB,GAAG;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,YAAY,CAAC;IACrB,EAAE,EAAE,QAAQ,CAAC;IACb,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE;QACN,UAAU,EAAE,MAAM,CAAC;QACnB,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,eAAe,EAAE,eAAe,CAAC;IACjC,GAAG,CAAC,EAAE,aAAa,CAAC;IACpB,kGAAkG;IAClG,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAI3B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,yBAAyB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IACvE,0FAA0F;IAC1F,wBAAwB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IACtE;;;;;OAKG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,MAAM,MAAM,EAAE,CAAC;IACnC,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,qBAAqB,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACvE,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE;QACvB,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,EAAE,EAAE,QAAQ,CAAC;QACb,UAAU,EAAE,MAAM,CAAC;KACpB,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC3B,gBAAgB,CAAC,EAAE,CAAC,KAAK,EAAE;QACzB,UAAU,EAAE,MAAM,CAAC;QACnB,cAAc,EAAE,MAAM,CAAC;QACvB,gBAAgB,EAAE,MAAM,CAAC;QACzB,cAAc,EAAE,MAAM,CAAC;KACxB,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC3B,kBAAkB,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAChD,YAAY,CAAC,EAAE,CAAC,iBAAiB,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACnE,cAAc,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CAC/D,CAAC;AAkBF,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAKnF;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAK/E;AAED,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAKnF;AAsFD,qBAAa,kBAAkB;IAoDjB,OAAO,CAAC,QAAQ,CAAC,IAAI;IAnDjC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA+B;IAC3D,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAqB;IAGrD,OAAO,CAAC,QAAQ,CAAC,0BAA0B,CAA6B;IACxE,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAqB;IAGxD,QAAQ,CAAC,mBAAmB;kBAA+B,MAAM;eAAS,MAAM;OAAM;IACtF,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAsC;IACjE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAK5B;IAEF,OAAO,CAAC,SAAS,CAAM;IACvB,OAAO,CAAC,eAAe,CAAqB;IAC5C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAA0C;IAC1E,OAAO,CAAC,cAAc,CAA+C;IAErE,OAAO,CAAC,eAAe,CAAc;IACrC,OAAO,CAAC,QAAQ,CAAS;IAMzB,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,kBAAkB,CAAK;IAC/B,OAAO,CAAC,cAAc,CAAyB;IAC/C,OAAO,CAAC,0BAA0B,CAAuB;IAEzD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAa;IAGzC,OAAO,CAAC,cAAc,CAAS;IAI/B,OAAO,CAAC,mBAAmB,CAAC,CAAS;IAErC,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAAQ;IAIhD,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAS;IAC9C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAS;gBAEjB,IAAI,EAAE,oBAAoB;IAmBjD,GAAG,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAuQlD,OAAO,CAAC,eAAe;IAavB;;;;OAIG;IACH,OAAO,CAAC,kBAAkB,CAA6B;IAEvD,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAKjE;;;;;;OAMG;IACH,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAM9C,oEAAoE;IACpE,OAAO,CAAC,iBAAiB,CAAqB;IAE9C;;;;;;OAMG;IACH,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;YAI/B,oBAAoB;IASlC,6EAA6E;IAC7E,OAAO,CAAC,cAAc;IAItB,OAAO,CAAC,aAAa;IAQrB;;;;;OAKG;IACH,OAAO,CAAC,gBAAgB;IAaxB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,iBAAiB;IAWzB,OAAO,CAAC,oBAAoB;IAc5B,OAAO,CAAC,gBAAgB;IAgBxB,OAAO,CAAC,wBAAwB;IA8BhC,OAAO,CAAC,oBAAoB;IAsB5B,OAAO,CAAC,qBAAqB;YAQf,eAAe;YAmEf,iBAAiB;IA+H/B,OAAO,CAAC,gBAAgB;IASxB,OAAO,CAAC,uBAAuB;IAW/B,OAAO,CAAC,0BAA0B;IAWlC,kEAAkE;IAClE,OAAO,CAAC,eAAe;IASvB,kEAAkE;IAClE,OAAO,CAAC,eAAe;YAQT,gCAAgC;YAMhC,kBAAkB;YA6ElB,WAAW;IA8SzB,OAAO,CAAC,SAAS;YA6CH,gBAAgB;YAkLhB,eAAe;YA8If,kBAAkB;YA2MlB,kBAAkB;YAkBlB,4BAA4B;YAwF5B,aAAa;YAmCb,oBAAoB;YAapB,2BAA2B;IAezC,OAAO,CAAC,sBAAsB;YAQhB,oBAAoB;YA0CpB,kBAAkB;YAwBlB,iBAAiB;YAqFjB,iBAAiB;IA2E/B;;;;;;;OAOG;YACW,0BAA0B;YA2B1B,kBAAkB;YAsClB,gCAAgC;YA0BhC,4BAA4B;YAqG5B,wBAAwB;YA2CxB,6BAA6B;YAuD7B,mBAAmB;IA8LjC,OAAO,CAAC,mBAAmB;IA8B3B,OAAO,CAAC,SAAS;YAQH,WAAW;IAkEzB,OAAO,CAAC,YAAY;YAiBN,QAAQ;CAsCvB"}
1
+ {"version":3,"file":"gateway-base.d.ts","sourceRoot":"","sources":["../src/gateway-base.ts"],"names":[],"mappings":"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"}
@@ -4,7 +4,7 @@ import * as path from 'node:path';
4
4
  import { ApiError, MENTION_ALL_USER_ID } from '@parall/sdk';
5
5
  import { buildEventBody, buildEventBodyForForkResult, buildForkResultPrefix, buildForkScopePrefix, } from './event-format.js';
6
6
  import { buildErrorStepContent, } from './dispatch-adapter.js';
7
- import { consumeMessageWorkItem, consumeTypedDispatch, dispatchLaneGroup, } from './gateway-lane-flow.js';
7
+ import { consumeMessageWorkItem, consumeTypedDispatch, dispatchLaneGroup, resolveDispatchByID, settleDrainedTypedGroup, typedLedgerEventIds, } from './gateway-lane-flow.js';
8
8
  import { LaneLedger } from './lane-ledger.js';
9
9
  import { routeTrigger } from './routing.js';
10
10
  import { clearDispatchMessageId, clearDispatchMetrics, clearDispatchNoReply, clearSessionMessageId, getDispatchMetrics, recordDeliverText, recordMessageSend, recordNoReply, recordToolCall, resetDispatchMetrics, setDispatchMessageId, setDispatchNoReply, setSessionChatId, setSessionMessageId, } from './session-state.js';
@@ -256,25 +256,28 @@ export class ParallAgentGateway {
256
256
  // level claim/ack would consume or clear the wrong sibling.
257
257
  await this.consumeTypedDispatch(data.dispatch_event_id
258
258
  ? { dispatchEventId: data.dispatch_event_id }
259
- : { sourceType: 'task_activity', sourceId: data.id }, (dispatchEventId) => this.handleTaskAssignment(data, data.id, dispatchEventId), (dispatchEventId) => {
260
- if (dispatchEventId) {
261
- return this.ackDispatchEvent(dispatchEventId, () => {
259
+ : { sourceType: 'task_activity', sourceId: data.id }, (dispatchEventId) => this.handleTaskAssignment(data, data.id, dispatchEventId), {
260
+ legacyAck: (dispatchEventId) => {
261
+ if (dispatchEventId) {
262
+ return this.ackDispatchEvent(dispatchEventId, () => {
263
+ this.dispatchedTasks.delete(`${data.id}:${data.updated_at}`);
264
+ });
265
+ }
266
+ return this.opts.client
267
+ .ackDispatch(this.opts.config.org_id, {
268
+ source_type: 'task_activity',
269
+ source_id: data.id,
270
+ })
271
+ .then(() => true, (err) => {
272
+ // Same contract as ackDispatchEvent: a failed ack is a
273
+ // failed consume (arms backoff) and must free the hot-path
274
+ // dedupe so the re-drive isn't rejected by this pod forever.
262
275
  this.dispatchedTasks.delete(`${data.id}:${data.updated_at}`);
276
+ this.opts.log?.warn(`dispatch ack failed for task ${data.id}, releasing for re-drive: ${String(err)}`);
277
+ return false;
263
278
  });
264
- }
265
- return this.opts.client
266
- .ackDispatch(this.opts.config.org_id, {
267
- source_type: 'task_activity',
268
- source_id: data.id,
269
- })
270
- .then(() => true, (err) => {
271
- // Same contract as ackDispatchEvent: a failed ack is a
272
- // failed consume (arms backoff) and must free the hot-path
273
- // dedupe so the re-drive isn't rejected by this pod forever.
274
- this.dispatchedTasks.delete(`${data.id}:${data.updated_at}`);
275
- this.opts.log?.warn(`dispatch ack failed for task ${data.id}, releasing for re-drive: ${String(err)}`);
276
- return false;
277
- });
279
+ },
280
+ clearDedupe: () => this.dispatchedTasks.delete(`${data.id}:${data.updated_at}`),
278
281
  });
279
282
  }
280
283
  catch (err) {
@@ -296,7 +299,10 @@ export class ParallAgentGateway {
296
299
  if (!data.source_id || !data.task_id)
297
300
  return;
298
301
  try {
299
- 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)));
302
+ await this.consumeTypedDispatch({ dispatchEventId: data.id }, (dispatchEventId) => this.handleTaskComment(data.source_id, data.task_id ?? '', data.actor_id, data.delivery_reason, dispatchEventId), {
303
+ legacyAck: () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)),
304
+ clearDedupe: () => this.clearTypedDispatchDedupe(data),
305
+ });
300
306
  }
301
307
  catch (err) {
302
308
  this.opts.log?.error(`task comment dispatch failed for ${data.source_id}: ${String(err)}`);
@@ -306,7 +312,10 @@ export class ParallAgentGateway {
306
312
  if (!data.source_id)
307
313
  return;
308
314
  try {
309
- await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.handleWikiComment(data.source_id, data.actor_id, data.delivery_reason), () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)));
315
+ await this.consumeTypedDispatch({ dispatchEventId: data.id }, (dispatchEventId) => this.handleWikiComment(data.source_id, data.actor_id, data.delivery_reason, dispatchEventId), {
316
+ legacyAck: () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)),
317
+ clearDedupe: () => this.clearTypedDispatchDedupe(data),
318
+ });
310
319
  }
311
320
  catch (err) {
312
321
  this.opts.log?.error(`wiki comment dispatch failed for ${data.source_id}: ${String(err)}`);
@@ -319,7 +328,10 @@ export class ParallAgentGateway {
319
328
  await this.consumeTypedDispatch({ dispatchEventId: data.id }, (dispatchEventId) => this.handleTaskDispatch(data.task_id ?? '', data.source_id ?? data.task_id ?? '', {
320
329
  allowCreator: true,
321
330
  dispatchEventId,
322
- }), () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)));
331
+ }), {
332
+ legacyAck: () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)),
333
+ clearDedupe: () => this.clearTypedDispatchDedupe(data),
334
+ });
323
335
  }
324
336
  catch (err) {
325
337
  this.opts.log?.error(`task update dispatch failed for ${data.task_id}: ${String(err)}`);
@@ -329,7 +341,10 @@ export class ParallAgentGateway {
329
341
  if (!data.source_id)
330
342
  return;
331
343
  try {
332
- await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleScheduleFire(data.source_id, data.actor_id), () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)));
344
+ await this.consumeTypedDispatch({ dispatchEventId: data.id }, (dispatchEventId) => this.fetchAndHandleScheduleFire(data.source_id, data.actor_id, dispatchEventId), {
345
+ legacyAck: () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)),
346
+ clearDedupe: () => this.clearTypedDispatchDedupe(data),
347
+ });
333
348
  }
334
349
  catch (err) {
335
350
  this.opts.log?.error(`schedule fire dispatch failed for ${data.source_id}: ${String(err)}`);
@@ -339,7 +354,10 @@ export class ParallAgentGateway {
339
354
  if (!data.source_id)
340
355
  return;
341
356
  try {
342
- await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleExternalTriggerRun(data.source_id), () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)));
357
+ await this.consumeTypedDispatch({ dispatchEventId: data.id }, (dispatchEventId) => this.fetchAndHandleExternalTriggerRun(data.source_id, dispatchEventId), {
358
+ legacyAck: () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)),
359
+ clearDedupe: () => this.clearTypedDispatchDedupe(data),
360
+ });
343
361
  }
344
362
  catch (err) {
345
363
  this.opts.log?.error(`external trigger dispatch failed for ${data.source_id}: ${String(err)}`);
@@ -349,7 +367,10 @@ export class ParallAgentGateway {
349
367
  if (!data.source_id)
350
368
  return;
351
369
  try {
352
- await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleChannelMessage(data.source_id), () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)));
370
+ await this.consumeTypedDispatch({ dispatchEventId: data.id }, (dispatchEventId) => this.fetchAndHandleChannelMessage(data.source_id, dispatchEventId), {
371
+ legacyAck: () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)),
372
+ clearDedupe: () => this.clearTypedDispatchDedupe(data),
373
+ });
353
374
  }
354
375
  catch (err) {
355
376
  this.opts.log?.error(`channel message dispatch failed for ${data.source_id}: ${String(err)}`);
@@ -359,7 +380,10 @@ export class ParallAgentGateway {
359
380
  if (!data.source_id)
360
381
  return;
361
382
  try {
362
- 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)));
383
+ await this.consumeTypedDispatch({ dispatchEventId: data.id }, (dispatchEventId) => this.fetchAndHandleApprovalDecided(data.source_id, data.actor_id, data.chat_id ?? null, dispatchEventId), {
384
+ legacyAck: () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)),
385
+ clearDedupe: () => this.clearTypedDispatchDedupe(data),
386
+ });
363
387
  }
364
388
  catch (err) {
365
389
  this.opts.log?.error(`approval decided dispatch failed for ${data.source_id}: ${String(err)}`);
@@ -456,6 +480,20 @@ export class ParallAgentGateway {
456
480
  source_id: sourceId,
457
481
  });
458
482
  }
483
+ /**
484
+ * Sticky: the server answered a by-id complete with 400 (predates the
485
+ * form). Typed resolution falls back to the legacy ack for the rest of
486
+ * the process lifetime.
487
+ */
488
+ typedByIdCompleteUnsupported = false;
489
+ // Typed-face ledger helpers live in gateway-lane-flow.ts; thin delegates
490
+ // keep the site code and tests on the class surface.
491
+ typedLedgerEventIds(events) {
492
+ return typedLedgerEventIds(this.laneFlowHost(), events);
493
+ }
494
+ resolveDispatchByID(dispatchEventId, lane) {
495
+ return resolveDispatchByID(this.laneFlowHost(), dispatchEventId, lane);
496
+ }
459
497
  /** True when this event's lifecycle is owned by the dispatch lane ledger. */
460
498
  usesLaneLedger(event) {
461
499
  return this.laneLedger != null && !this.ledgerDisabled && this.laneLedger.handles(event);
@@ -490,15 +528,16 @@ export class ParallAgentGateway {
490
528
  dispatchLaneGroup(opts) {
491
529
  return dispatchLaneGroup(this.laneFlowHost(), opts);
492
530
  }
493
- consumeTypedDispatch(ref, run, ack) {
494
- return consumeTypedDispatch(this.laneFlowHost(), ref, run, ack);
531
+ consumeTypedDispatch(ref, run, hooks) {
532
+ return consumeTypedDispatch(this.laneFlowHost(), ref, run, hooks);
495
533
  }
496
- // Typed completion must wait until the administrative ack has either
497
- // committed or failed. Errors stay best-effort: a failed ack leaves the row
498
- // received, so Complete releases and re-drives it safely. The boolean
499
- // outcome feeds the typed-consume backoff an ack that failed must count
500
- // as a failed consume, or an ack outage would clear the backoff entry and
501
- // let the release re-drive spin at wire speed.
534
+ // Legacy administrative ack (ledger-disabled fallback only). Typed
535
+ // completion must wait until the ack has either committed or failed.
536
+ // Errors stay best-effort: a failed ack leaves the row received, so
537
+ // Complete releases and re-drives it safely. The boolean outcome feeds the
538
+ // typed-consume backoff an ack that failed must count as a failed
539
+ // consume, or an ack outage would clear the backoff entry and let the
540
+ // release re-drive spin at wire speed.
502
541
  ackDispatchEvent(dispatchEventId, onFailure) {
503
542
  return this.opts.client.ackDispatchByID(this.opts.config.org_id, dispatchEventId).then(() => true, (err) => {
504
543
  // Complete will return the still-received item to pending and publish
@@ -509,6 +548,9 @@ export class ParallAgentGateway {
509
548
  return false;
510
549
  });
511
550
  }
551
+ // PARITY: this switch and gateway-lane-flow's clearTypedDedupeForEvent must
552
+ // handle the same typed source families — extend BOTH when adding a typed
553
+ // event type (same dedupe entries, keyed from different event shapes).
512
554
  clearTypedDispatchDedupe(item) {
513
555
  switch (item.event_type) {
514
556
  case 'task_assign':
@@ -1190,6 +1232,19 @@ export class ParallAgentGateway {
1190
1232
  }
1191
1233
  else {
1192
1234
  dispatched = await this.runDispatch(last, fork.fork.sessionKey, buildForkScopePrefix(last) + buildEventBody(last), earlier, batchText);
1235
+ if (dispatched &&
1236
+ this.typedLedgerEventIds(events) &&
1237
+ this.consumeTurnError(fork.fork.sessionKey)) {
1238
+ // Typed error turn in a fork: report failure to the awaiting
1239
+ // typed consumes so the members release for a budgeted retry,
1240
+ // and do NOT record the events as handled — the redrive must
1241
+ // not arrive wearing an "already handled" prefix. Keep draining
1242
+ // (an error turn is not a shutdown).
1243
+ this.opts.log?.info(`typed fork turn for ${last.messageId} surfaced a runtime error — releasing for retry`);
1244
+ for (const item of items)
1245
+ item.resolve(false);
1246
+ continue;
1247
+ }
1193
1248
  }
1194
1249
  if (!dispatched) {
1195
1250
  // Shutdown short-circuit — resolve un-acked so the server requeues
@@ -1366,14 +1421,21 @@ export class ParallAgentGateway {
1366
1421
  // no_action sweep); no legacy acks.
1367
1422
  continue;
1368
1423
  }
1369
- try {
1370
- await this.emitDispatchReceived(event);
1371
- }
1372
- catch (err) {
1373
- this.opts.log?.warn?.(`mark-received failed for buffered dispatch, leaving unacked for retry: ${String(err)}`);
1374
- this.dispatchState.mainBuffer.unshift(...events);
1375
- this.dispatchState.pendingForkResults.unshift(...pendingFork);
1376
- break;
1424
+ // Buffered typed events are the one wrapper-less dispatch path: their
1425
+ // consumeTypedDispatch guard returned long ago (buffer-main resolves
1426
+ // false) and released the claims, so THIS site owns their resolution.
1427
+ // Message groups here are the ledger-disabled legacy flow.
1428
+ const typedRefs = this.typedLedgerEventIds(events);
1429
+ if (!typedRefs) {
1430
+ try {
1431
+ await this.emitDispatchReceived(event);
1432
+ }
1433
+ catch (err) {
1434
+ this.opts.log?.warn?.(`mark-received failed for buffered dispatch, leaving unacked for retry: ${String(err)}`);
1435
+ this.dispatchState.mainBuffer.unshift(...events);
1436
+ this.dispatchState.pendingForkResults.unshift(...pendingFork);
1437
+ break;
1438
+ }
1377
1439
  }
1378
1440
  const dispatched = await this.runDispatch(event, this.opts.runtimeKey, forkPrefix + buildEventBody(event), earlier);
1379
1441
  if (!dispatched) {
@@ -1384,6 +1446,13 @@ export class ParallAgentGateway {
1384
1446
  this.dispatchState.pendingForkResults.unshift(...pendingFork);
1385
1447
  break;
1386
1448
  }
1449
+ if (typedRefs) {
1450
+ // Wrapper-less resolution of the buffered typed group — protocol
1451
+ // lives in gateway-lane-flow.ts. The turn-error marker is consumed
1452
+ // HERE, before this loop can start another turn on the session.
1453
+ await settleDrainedTypedGroup(this.laneFlowHost(), events, typedRefs, this.consumeTurnError(this.opts.runtimeKey));
1454
+ continue;
1455
+ }
1387
1456
  for (const bufferedEvent of events) {
1388
1457
  const sourceType = bufferedEvent.ackSourceType ??
1389
1458
  (bufferedEvent.type === 'task' ? 'task_activity' : 'message');
@@ -1464,21 +1533,36 @@ export class ParallAgentGateway {
1464
1533
  }
1465
1534
  return outcome === 'dispatched';
1466
1535
  }
1467
- try {
1468
- await this.emitDispatchReceived(event);
1469
- }
1470
- catch (err) {
1471
- this.opts.log?.warn?.(`mark-received failed, leaving unacked for retry: ${String(err)}`);
1472
- this.dispatchState.mainDispatching = false;
1473
- this.dispatchState.mainCurrentTargetId = undefined;
1474
- this.mainCurrentGroupKey = undefined;
1475
- this.dispatchState.mainPreDispatchBranchPoint = undefined;
1476
- this.dispatchState.pendingForkResults.unshift(...pendingFork);
1477
- return false;
1536
+ // Ledger-claimed typed events skip the legacy received write — the
1537
+ // claim already marked the row received, and a second mark here would
1538
+ // wipe its lease owner (the 0713 black-hole shape on the typed face).
1539
+ const typedRefs = this.typedLedgerEventIds([event]);
1540
+ if (!typedRefs) {
1541
+ try {
1542
+ await this.emitDispatchReceived(event);
1543
+ }
1544
+ catch (err) {
1545
+ this.opts.log?.warn?.(`mark-received failed, leaving unacked for retry: ${String(err)}`);
1546
+ this.dispatchState.mainDispatching = false;
1547
+ this.dispatchState.mainCurrentTargetId = undefined;
1548
+ this.mainCurrentGroupKey = undefined;
1549
+ this.dispatchState.mainPreDispatchBranchPoint = undefined;
1550
+ this.dispatchState.pendingForkResults.unshift(...pendingFork);
1551
+ return false;
1552
+ }
1478
1553
  }
1479
1554
  let dispatched = false;
1480
1555
  try {
1481
1556
  dispatched = await this.runDispatch(event, this.opts.runtimeKey, forkPrefix + buildEventBody(event));
1557
+ if (dispatched && typedRefs && this.consumeTurnError(this.opts.runtimeKey)) {
1558
+ // Typed error turn: report failure to the enclosing typed consume
1559
+ // so the member releases for a budgeted retry instead of being
1560
+ // terminally resolved (tech-debt: typed-dispatch-error-outcome).
1561
+ // Consumed HERE, before the finally's drain can start another
1562
+ // turn on this session and clear the marker.
1563
+ this.opts.log?.info(`typed dispatch turn for ${event.messageId} surfaced a runtime error — releasing for retry`);
1564
+ dispatched = false;
1565
+ }
1482
1566
  if (!dispatched) {
1483
1567
  // Shutdown short-circuit — restore the fork results so a future
1484
1568
  // pod can replay them, and return false so handleMessage skips ack.
@@ -1552,8 +1636,9 @@ export class ParallAgentGateway {
1552
1636
  // — the fork's lane claim marks them received. A mark-received here
1553
1637
  // outruns the claim, strands the row ownerless, and the chat goes
1554
1638
  // silent (the 0713 black hole; dispatch-convergence-design.md §6).
1555
- // This was the only unguarded call site of the three.
1556
- if (!this.usesLaneLedger(event)) {
1639
+ // Same rule for ledger-claimed typed events: their dsp-lane claim
1640
+ // owns received-ness, and a re-mark would wipe the lease owner.
1641
+ if (!this.usesLaneLedger(event) && !this.typedLedgerEventIds([event])) {
1557
1642
  try {
1558
1643
  await this.emitDispatchReceived(event);
1559
1644
  }
@@ -1748,7 +1833,10 @@ export class ParallAgentGateway {
1748
1833
  return;
1749
1834
  await this.consumeTypedDispatch({ dispatchEventId: item.id }, (dispatchEventId) => this.handleTaskDispatch(item.task_id ?? '', item.source_id ?? item.task_id ?? '', {
1750
1835
  dispatchEventId,
1751
- }), (dispatchEventId) => this.ackDispatchEvent(dispatchEventId ?? item.id, () => this.clearTypedDispatchDedupe(item)));
1836
+ }), {
1837
+ legacyAck: (dispatchEventId) => this.ackDispatchEvent(dispatchEventId ?? item.id, () => this.clearTypedDispatchDedupe(item)),
1838
+ clearDedupe: () => this.clearTypedDispatchDedupe(item),
1839
+ });
1752
1840
  }
1753
1841
  consumeMessageWorkItem(item) {
1754
1842
  return consumeMessageWorkItem(this.laneFlowHost(), item);
@@ -1810,7 +1898,7 @@ export class ParallAgentGateway {
1810
1898
  }
1811
1899
  return this.handleTaskAssignment(task, ackSourceId, opts.dispatchEventId);
1812
1900
  }
1813
- async handleTaskComment(commentId, taskId, actorId, deliveryReason) {
1901
+ async handleTaskComment(commentId, taskId, actorId, deliveryReason, dispatchEventId) {
1814
1902
  if (this.shuttingDown)
1815
1903
  return false; // drain window — let server requeue via catch-up
1816
1904
  const dedupeKey = `comment:${commentId}`;
@@ -1872,6 +1960,7 @@ export class ParallAgentGateway {
1872
1960
  deliveryReason: deliveryReason ?? undefined,
1873
1961
  ackSourceType: 'comment',
1874
1962
  ackSourceId: commentId,
1963
+ dispatchEventId,
1875
1964
  };
1876
1965
  let dispatched;
1877
1966
  try {
@@ -1887,7 +1976,7 @@ export class ParallAgentGateway {
1887
1976
  }
1888
1977
  return dispatched;
1889
1978
  }
1890
- async handleWikiComment(commentId, actorId, deliveryReason) {
1979
+ async handleWikiComment(commentId, actorId, deliveryReason, dispatchEventId) {
1891
1980
  if (this.shuttingDown)
1892
1981
  return false; // drain window — let server requeue via catch-up
1893
1982
  // Shares the comment dedupe namespace with handleTaskComment; comment IDs
@@ -1940,6 +2029,7 @@ export class ParallAgentGateway {
1940
2029
  replyTargetUri: comment.target_uri,
1941
2030
  ackSourceType: 'comment',
1942
2031
  ackSourceId: commentId,
2032
+ dispatchEventId,
1943
2033
  };
1944
2034
  let dispatched;
1945
2035
  try {
@@ -1963,7 +2053,7 @@ export class ParallAgentGateway {
1963
2053
  * access to already-delivered run snapshots, and the runtime must not crash
1964
2054
  * or retry forever in that case.
1965
2055
  */
1966
- async fetchAndHandleScheduleFire(runId, actorId) {
2056
+ async fetchAndHandleScheduleFire(runId, actorId, dispatchEventId) {
1967
2057
  let run = null;
1968
2058
  try {
1969
2059
  run = await this.opts.client.getScheduleRun(this.opts.config.org_id, runId);
@@ -1984,9 +2074,9 @@ export class ParallAgentGateway {
1984
2074
  }
1985
2075
  if (!run)
1986
2076
  return true;
1987
- return this.handleScheduleFire(run, actorId);
2077
+ return this.handleScheduleFire(run, actorId, dispatchEventId);
1988
2078
  }
1989
- async handleScheduleFire(run, actorId) {
2079
+ async handleScheduleFire(run, actorId, dispatchEventId) {
1990
2080
  if (this.shuttingDown)
1991
2081
  return false; // drain window — let server requeue via catch-up
1992
2082
  const dedupeKey = `schedule_run:${run.id}`;
@@ -2010,6 +2100,7 @@ export class ParallAgentGateway {
2010
2100
  attachedUri: run.fired_attached_uri ?? undefined,
2011
2101
  ackSourceType: 'schedule_run',
2012
2102
  ackSourceId: run.id,
2103
+ dispatchEventId,
2013
2104
  };
2014
2105
  let dispatched;
2015
2106
  try {
@@ -2024,7 +2115,7 @@ export class ParallAgentGateway {
2024
2115
  }
2025
2116
  return dispatched;
2026
2117
  }
2027
- async fetchAndHandleExternalTriggerRun(runId) {
2118
+ async fetchAndHandleExternalTriggerRun(runId, dispatchEventId) {
2028
2119
  let run = null;
2029
2120
  try {
2030
2121
  run = await this.opts.client.getExternalTriggerRun(this.opts.config.org_id, runId);
@@ -2040,14 +2131,14 @@ export class ParallAgentGateway {
2040
2131
  }
2041
2132
  if (!run)
2042
2133
  return true;
2043
- return this.handleExternalTriggerRun(run);
2134
+ return this.handleExternalTriggerRun(run, dispatchEventId);
2044
2135
  }
2045
2136
  // fetchAndHandleChannelMessage resolves a channel_message dispatch to its
2046
2137
  // durable ChannelMessage + conversation and hands it to the inbound
2047
2138
  // pipeline. targetId = the ChannelConversation id, so per-conversation
2048
2139
  // multi-turn continuity rides the same per-target session mechanics as
2049
2140
  // chats. Design: docs/engineering-design/external-im-channel-design.md.
2050
- async fetchAndHandleChannelMessage(messageId) {
2141
+ async fetchAndHandleChannelMessage(messageId, dispatchEventId) {
2051
2142
  if (this.shuttingDown)
2052
2143
  return false;
2053
2144
  // Capped dedupe (the chat-message path, not the unbounded task set): a busy
@@ -2121,6 +2212,7 @@ export class ParallAgentGateway {
2121
2212
  channelCliCapable: cliCapable,
2122
2213
  ackSourceType: 'channel_message',
2123
2214
  ackSourceId: msg.id,
2215
+ dispatchEventId,
2124
2216
  };
2125
2217
  // Release the claim if the event isn't actually dispatched (or throws) so
2126
2218
  // a retry can re-attempt — same contract as the chat-message path.
@@ -2137,7 +2229,7 @@ export class ParallAgentGateway {
2137
2229
  }
2138
2230
  return dispatched;
2139
2231
  }
2140
- async handleExternalTriggerRun(run) {
2232
+ async handleExternalTriggerRun(run, dispatchEventId) {
2141
2233
  if (this.shuttingDown)
2142
2234
  return false;
2143
2235
  const dedupeKey = `external_trigger_run:${run.id}`;
@@ -2165,6 +2257,7 @@ export class ParallAgentGateway {
2165
2257
  attachedUri,
2166
2258
  ackSourceType: 'external_trigger_run',
2167
2259
  ackSourceId: run.id,
2260
+ dispatchEventId,
2168
2261
  };
2169
2262
  let dispatched;
2170
2263
  try {
@@ -2179,7 +2272,7 @@ export class ParallAgentGateway {
2179
2272
  }
2180
2273
  return dispatched;
2181
2274
  }
2182
- async fetchAndHandleApprovalDecided(approvalId, actorId, chatId) {
2275
+ async fetchAndHandleApprovalDecided(approvalId, actorId, chatId, dispatchEventId) {
2183
2276
  let approval = null;
2184
2277
  try {
2185
2278
  approval = await this.opts.client.getApproval(approvalId);
@@ -2212,6 +2305,11 @@ export class ParallAgentGateway {
2212
2305
  senderName: 'approver',
2213
2306
  messageId: approval.id,
2214
2307
  body,
2308
+ // The WorkItem's real source pair. Without it the legacy fallback
2309
+ // guessed ('message', approval_id) — a pair no row matches.
2310
+ ackSourceType: 'approval',
2311
+ ackSourceId: approval.id,
2312
+ dispatchEventId,
2215
2313
  };
2216
2314
  let dispatched;
2217
2315
  try {
@@ -2262,7 +2360,26 @@ export class ParallAgentGateway {
2262
2360
  break;
2263
2361
  if (overflowMode && processed >= CATCHUP_MAX) {
2264
2362
  try {
2265
- await this.opts.client.ackDispatchByID(this.opts.config.org_id, item.id);
2363
+ // Administrative drop of the overflow backlog (summarized to the
2364
+ // agent instead of dispatched). Ledger path: by-id complete —
2365
+ // closes the pending row and refuses (stale) one a live lane
2366
+ // owns; the legacy ack stays as the ledger-disabled / pre-by-id
2367
+ // fallback.
2368
+ if (this.laneLedger && !this.ledgerDisabled && !this.typedByIdCompleteUnsupported) {
2369
+ const outcome = await this.resolveDispatchByID(item.id);
2370
+ if (outcome === 'unsupported') {
2371
+ this.typedByIdCompleteUnsupported = true;
2372
+ await this.opts.client.ackDispatchByID(this.opts.config.org_id, item.id);
2373
+ }
2374
+ else if (outcome === 'failed') {
2375
+ throw new Error('by-id complete failed');
2376
+ }
2377
+ // 'stale' counts as skipped: a live lane owns the row and its
2378
+ // turn will resolve it — nothing left for catch-up to do.
2379
+ }
2380
+ else {
2381
+ await this.opts.client.ackDispatchByID(this.opts.config.org_id, item.id);
2382
+ }
2266
2383
  skipped++;
2267
2384
  const key = item.event_type;
2268
2385
  skippedByType.set(key, (skippedByType.get(key) ?? 0) + 1);
@@ -2274,12 +2391,15 @@ export class ParallAgentGateway {
2274
2391
  }
2275
2392
  processed++;
2276
2393
  try {
2277
- const ackItem = () => this.ackDispatchEvent(item.id, () => this.clearTypedDispatchDedupe(item));
2394
+ const typedHooks = {
2395
+ legacyAck: () => this.ackDispatchEvent(item.id, () => this.clearTypedDispatchDedupe(item)),
2396
+ clearDedupe: () => this.clearTypedDispatchDedupe(item),
2397
+ };
2278
2398
  if (item.event_type === 'task_assign' && item.task_id) {
2279
2399
  try {
2280
2400
  await this.consumeTypedDispatch({ dispatchEventId: item.id }, (dispatchEventId) => this.handleTaskDispatch(item.task_id ?? '', item.source_id ?? item.task_id ?? '', {
2281
2401
  dispatchEventId,
2282
- }), ackItem);
2402
+ }), typedHooks);
2283
2403
  }
2284
2404
  catch (err) {
2285
2405
  this.opts.log?.warn(`catch-up task fetch failed for ${item.task_id}, leaving pending: ${String(err)}`);
@@ -2291,7 +2411,7 @@ export class ParallAgentGateway {
2291
2411
  await this.consumeTypedDispatch({ dispatchEventId: item.id }, (dispatchEventId) => this.handleTaskDispatch(item.task_id ?? '', item.source_id ?? item.task_id ?? '', {
2292
2412
  allowCreator: true,
2293
2413
  dispatchEventId,
2294
- }), ackItem);
2414
+ }), typedHooks);
2295
2415
  }
2296
2416
  catch (err) {
2297
2417
  this.opts.log?.warn(`catch-up task fetch failed for ${item.task_id}, leaving pending: ${String(err)}`);
@@ -2299,22 +2419,22 @@ export class ParallAgentGateway {
2299
2419
  }
2300
2420
  }
2301
2421
  else if (item.event_type === 'task_comment' && item.source_id && item.task_id) {
2302
- await this.consumeTypedDispatch({ dispatchEventId: item.id }, () => this.handleTaskComment(item.source_id, item.task_id ?? '', item.actor_id, item.delivery_reason), ackItem);
2422
+ await this.consumeTypedDispatch({ dispatchEventId: item.id }, (dispatchEventId) => this.handleTaskComment(item.source_id, item.task_id ?? '', item.actor_id, item.delivery_reason, dispatchEventId), typedHooks);
2303
2423
  }
2304
2424
  else if (item.event_type === 'wiki_comment' && item.source_id) {
2305
- await this.consumeTypedDispatch({ dispatchEventId: item.id }, () => this.handleWikiComment(item.source_id, item.actor_id, item.delivery_reason), ackItem);
2425
+ await this.consumeTypedDispatch({ dispatchEventId: item.id }, (dispatchEventId) => this.handleWikiComment(item.source_id, item.actor_id, item.delivery_reason, dispatchEventId), typedHooks);
2306
2426
  }
2307
2427
  else if (item.event_type === 'schedule.fire' && item.source_id) {
2308
- await this.consumeTypedDispatch({ dispatchEventId: item.id }, () => this.fetchAndHandleScheduleFire(item.source_id, item.actor_id), ackItem);
2428
+ await this.consumeTypedDispatch({ dispatchEventId: item.id }, (dispatchEventId) => this.fetchAndHandleScheduleFire(item.source_id, item.actor_id, dispatchEventId), typedHooks);
2309
2429
  }
2310
2430
  else if (item.event_type === 'external_trigger' && item.source_id) {
2311
- await this.consumeTypedDispatch({ dispatchEventId: item.id }, () => this.fetchAndHandleExternalTriggerRun(item.source_id), ackItem);
2431
+ await this.consumeTypedDispatch({ dispatchEventId: item.id }, (dispatchEventId) => this.fetchAndHandleExternalTriggerRun(item.source_id, dispatchEventId), typedHooks);
2312
2432
  }
2313
2433
  else if (item.event_type === 'channel_message' && item.source_id) {
2314
- await this.consumeTypedDispatch({ dispatchEventId: item.id }, () => this.fetchAndHandleChannelMessage(item.source_id), ackItem);
2434
+ await this.consumeTypedDispatch({ dispatchEventId: item.id }, (dispatchEventId) => this.fetchAndHandleChannelMessage(item.source_id, dispatchEventId), typedHooks);
2315
2435
  }
2316
2436
  else if (item.event_type === 'approval_decided' && item.source_id) {
2317
- await this.consumeTypedDispatch({ dispatchEventId: item.id }, () => this.fetchAndHandleApprovalDecided(item.source_id, item.actor_id, item.chat_id ?? null), ackItem);
2437
+ await this.consumeTypedDispatch({ dispatchEventId: item.id }, (dispatchEventId) => this.fetchAndHandleApprovalDecided(item.source_id, item.actor_id, item.chat_id ?? null, dispatchEventId), typedHooks);
2318
2438
  }
2319
2439
  else if (item.event_type === 'message' && item.source_id && item.chat_id) {
2320
2440
  await this.consumeMessageWorkItem({