@parall/agent-core 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.
- package/dist/gateway-base.d.ts +3 -0
- package/dist/gateway-base.d.ts.map +1 -1
- package/dist/gateway-base.js +86 -38
- package/dist/gateway-lane-flow.d.ts +1 -1
- package/dist/gateway-lane-flow.d.ts.map +1 -1
- package/dist/gateway-lane-flow.js +6 -3
- package/package.json +2 -2
- package/src/gateway-base.ts +99 -39
- package/src/gateway-lane-flow.ts +7 -4
package/dist/gateway-base.d.ts
CHANGED
|
@@ -122,6 +122,8 @@ export declare class ParallAgentGateway {
|
|
|
122
122
|
private laneFlowHost;
|
|
123
123
|
private dispatchLaneGroup;
|
|
124
124
|
private consumeTypedDispatch;
|
|
125
|
+
private ackDispatchEvent;
|
|
126
|
+
private clearTypedDispatchDedupe;
|
|
125
127
|
private buildDispatchContext;
|
|
126
128
|
private isSessionNotLiveError;
|
|
127
129
|
private createInputStep;
|
|
@@ -144,6 +146,7 @@ export declare class ParallAgentGateway {
|
|
|
144
146
|
private buildMessageDispatchDecision;
|
|
145
147
|
private handleMessage;
|
|
146
148
|
private handleMessageRedrive;
|
|
149
|
+
private handleTaskAssignmentRedrive;
|
|
147
150
|
private consumeMessageWorkItem;
|
|
148
151
|
private handleTaskAssignment;
|
|
149
152
|
private handleTaskDispatch;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gateway-base.d.ts","sourceRoot":"","sources":["../src/gateway-base.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"gateway-base.d.ts","sourceRoot":"","sources":["../src/gateway-base.ts"],"names":[],"mappings":"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"}
|
package/dist/gateway-base.js
CHANGED
|
@@ -255,17 +255,16 @@ export class ParallAgentGateway {
|
|
|
255
255
|
? { dispatchEventId: data.dispatch_event_id }
|
|
256
256
|
: { sourceType: 'task_activity', sourceId: data.id }, (dispatchEventId) => this.handleTaskAssignment(data, data.id, dispatchEventId), (dispatchEventId) => {
|
|
257
257
|
if (dispatchEventId) {
|
|
258
|
-
this.
|
|
259
|
-
.
|
|
260
|
-
|
|
261
|
-
return;
|
|
258
|
+
return this.ackDispatchEvent(dispatchEventId, () => {
|
|
259
|
+
this.dispatchedTasks.delete(`${data.id}:${data.updated_at}`);
|
|
260
|
+
});
|
|
262
261
|
}
|
|
263
|
-
this.opts.client
|
|
262
|
+
return this.opts.client
|
|
264
263
|
.ackDispatch(this.opts.config.org_id, {
|
|
265
264
|
source_type: 'task_activity',
|
|
266
265
|
source_id: data.id,
|
|
267
266
|
})
|
|
268
|
-
.
|
|
267
|
+
.then(() => undefined, () => undefined);
|
|
269
268
|
});
|
|
270
269
|
}
|
|
271
270
|
catch (err) {
|
|
@@ -273,13 +272,21 @@ export class ParallAgentGateway {
|
|
|
273
272
|
}
|
|
274
273
|
});
|
|
275
274
|
ws.on('dispatch.new', async (data) => {
|
|
276
|
-
if (data.event_type === '
|
|
275
|
+
if (data.event_type === 'task_assign') {
|
|
276
|
+
if (!data.task_id)
|
|
277
|
+
return;
|
|
278
|
+
try {
|
|
279
|
+
await this.handleTaskAssignmentRedrive(data);
|
|
280
|
+
}
|
|
281
|
+
catch (err) {
|
|
282
|
+
this.opts.log?.error(`task assignment re-drive failed for ${data.task_id}: ${String(err)}`);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
else if (data.event_type === 'task_comment') {
|
|
277
286
|
if (!data.source_id || !data.task_id)
|
|
278
287
|
return;
|
|
279
288
|
try {
|
|
280
|
-
await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.handleTaskComment(data.source_id, data.task_id ?? '', data.actor_id, data.delivery_reason), () =>
|
|
281
|
-
this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => { });
|
|
282
|
-
});
|
|
289
|
+
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)));
|
|
283
290
|
}
|
|
284
291
|
catch (err) {
|
|
285
292
|
this.opts.log?.error(`task comment dispatch failed for ${data.source_id}: ${String(err)}`);
|
|
@@ -289,9 +296,7 @@ export class ParallAgentGateway {
|
|
|
289
296
|
if (!data.source_id)
|
|
290
297
|
return;
|
|
291
298
|
try {
|
|
292
|
-
await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.handleWikiComment(data.source_id, data.actor_id, data.delivery_reason), () =>
|
|
293
|
-
this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => { });
|
|
294
|
-
});
|
|
299
|
+
await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.handleWikiComment(data.source_id, data.actor_id, data.delivery_reason), () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)));
|
|
295
300
|
}
|
|
296
301
|
catch (err) {
|
|
297
302
|
this.opts.log?.error(`wiki comment dispatch failed for ${data.source_id}: ${String(err)}`);
|
|
@@ -304,9 +309,7 @@ export class ParallAgentGateway {
|
|
|
304
309
|
await this.consumeTypedDispatch({ dispatchEventId: data.id }, (dispatchEventId) => this.handleTaskDispatch(data.task_id ?? '', data.source_id ?? data.task_id ?? '', {
|
|
305
310
|
allowCreator: true,
|
|
306
311
|
dispatchEventId,
|
|
307
|
-
}), () =>
|
|
308
|
-
this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => { });
|
|
309
|
-
});
|
|
312
|
+
}), () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)));
|
|
310
313
|
}
|
|
311
314
|
catch (err) {
|
|
312
315
|
this.opts.log?.error(`task update dispatch failed for ${data.task_id}: ${String(err)}`);
|
|
@@ -316,9 +319,7 @@ export class ParallAgentGateway {
|
|
|
316
319
|
if (!data.source_id)
|
|
317
320
|
return;
|
|
318
321
|
try {
|
|
319
|
-
await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleScheduleFire(data.source_id, data.actor_id), () =>
|
|
320
|
-
this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => { });
|
|
321
|
-
});
|
|
322
|
+
await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleScheduleFire(data.source_id, data.actor_id), () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)));
|
|
322
323
|
}
|
|
323
324
|
catch (err) {
|
|
324
325
|
this.opts.log?.error(`schedule fire dispatch failed for ${data.source_id}: ${String(err)}`);
|
|
@@ -328,9 +329,7 @@ export class ParallAgentGateway {
|
|
|
328
329
|
if (!data.source_id)
|
|
329
330
|
return;
|
|
330
331
|
try {
|
|
331
|
-
await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleExternalTriggerRun(data.source_id), () =>
|
|
332
|
-
this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => { });
|
|
333
|
-
});
|
|
332
|
+
await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleExternalTriggerRun(data.source_id), () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)));
|
|
334
333
|
}
|
|
335
334
|
catch (err) {
|
|
336
335
|
this.opts.log?.error(`external trigger dispatch failed for ${data.source_id}: ${String(err)}`);
|
|
@@ -340,9 +339,7 @@ export class ParallAgentGateway {
|
|
|
340
339
|
if (!data.source_id)
|
|
341
340
|
return;
|
|
342
341
|
try {
|
|
343
|
-
await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleChannelMessage(data.source_id), () =>
|
|
344
|
-
this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => { });
|
|
345
|
-
});
|
|
342
|
+
await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleChannelMessage(data.source_id), () => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)));
|
|
346
343
|
}
|
|
347
344
|
catch (err) {
|
|
348
345
|
this.opts.log?.error(`channel message dispatch failed for ${data.source_id}: ${String(err)}`);
|
|
@@ -352,9 +349,7 @@ export class ParallAgentGateway {
|
|
|
352
349
|
if (!data.source_id)
|
|
353
350
|
return;
|
|
354
351
|
try {
|
|
355
|
-
await this.consumeTypedDispatch({ dispatchEventId: data.id }, () => this.fetchAndHandleApprovalDecided(data.source_id, data.actor_id, data.chat_id ?? null), () =>
|
|
356
|
-
this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => { });
|
|
357
|
-
});
|
|
352
|
+
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)));
|
|
358
353
|
}
|
|
359
354
|
catch (err) {
|
|
360
355
|
this.opts.log?.error(`approval decided dispatch failed for ${data.source_id}: ${String(err)}`);
|
|
@@ -374,14 +369,11 @@ export class ParallAgentGateway {
|
|
|
374
369
|
this.opts.log?.error(`message re-drive failed for ${data.source_id}: ${String(err)}`);
|
|
375
370
|
}
|
|
376
371
|
}
|
|
377
|
-
else if (data.event_type !== 'message'
|
|
372
|
+
else if (data.event_type !== 'message') {
|
|
378
373
|
// Truly unknown event_type — log so a newly-added dispatch type
|
|
379
|
-
// not yet wired here surfaces during runtime testing. "message"
|
|
380
|
-
//
|
|
381
|
-
//
|
|
382
|
-
// (message.new, task.assigned) above and would otherwise spam
|
|
383
|
-
// info-level logs for every inbound chat message / task
|
|
384
|
-
// assignment on a busy agent.
|
|
374
|
+
// not yet wired here surfaces during runtime testing. "message" is
|
|
375
|
+
// deliberately excluded because its first delivery is owned by the
|
|
376
|
+
// message.new handler; recovered task_assign rows are handled above.
|
|
385
377
|
this.opts.log?.info(`dispatch.new with unhandled event_type=${String(data.event_type)} (id=${data.id}) — no-op`);
|
|
386
378
|
}
|
|
387
379
|
});
|
|
@@ -453,6 +445,53 @@ export class ParallAgentGateway {
|
|
|
453
445
|
consumeTypedDispatch(ref, run, ack) {
|
|
454
446
|
return consumeTypedDispatch(this.laneFlowHost(), ref, run, ack);
|
|
455
447
|
}
|
|
448
|
+
// Typed completion must wait until the administrative ack has either
|
|
449
|
+
// committed or failed. Errors stay best-effort: a failed ack leaves the row
|
|
450
|
+
// received, so Complete releases and re-drives it safely.
|
|
451
|
+
ackDispatchEvent(dispatchEventId, onFailure) {
|
|
452
|
+
return this.opts.client.ackDispatchByID(this.opts.config.org_id, dispatchEventId).then(() => undefined, (err) => {
|
|
453
|
+
// Complete will return the still-received item to pending and publish
|
|
454
|
+
// an immediate hint. Free its hot-path claim first, otherwise that
|
|
455
|
+
// same-process re-drive is rejected forever by the local dedupe set.
|
|
456
|
+
onFailure?.();
|
|
457
|
+
this.opts.log?.warn(`dispatch ack failed for ${dispatchEventId}, releasing for re-drive: ${String(err)}`);
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
clearTypedDispatchDedupe(item) {
|
|
461
|
+
switch (item.event_type) {
|
|
462
|
+
case 'task_assign':
|
|
463
|
+
case 'task_update':
|
|
464
|
+
if (item.task_id) {
|
|
465
|
+
const prefix = `${item.task_id}:`;
|
|
466
|
+
for (const key of this.dispatchedTasks) {
|
|
467
|
+
if (key.startsWith(prefix))
|
|
468
|
+
this.dispatchedTasks.delete(key);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
break;
|
|
472
|
+
case 'task_comment':
|
|
473
|
+
case 'wiki_comment':
|
|
474
|
+
if (item.source_id)
|
|
475
|
+
this.dispatchedTasks.delete(`comment:${item.source_id}`);
|
|
476
|
+
break;
|
|
477
|
+
case 'schedule.fire':
|
|
478
|
+
if (item.source_id)
|
|
479
|
+
this.dispatchedTasks.delete(`schedule_run:${item.source_id}`);
|
|
480
|
+
break;
|
|
481
|
+
case 'external_trigger':
|
|
482
|
+
if (item.source_id)
|
|
483
|
+
this.dispatchedTasks.delete(`external_trigger_run:${item.source_id}`);
|
|
484
|
+
break;
|
|
485
|
+
case 'channel_message':
|
|
486
|
+
if (item.source_id)
|
|
487
|
+
this.dispatchedMessages.delete(`channel_message:${item.source_id}`);
|
|
488
|
+
break;
|
|
489
|
+
case 'approval_decided':
|
|
490
|
+
if (item.source_id)
|
|
491
|
+
this.dispatchedTasks.delete(`approval:${item.source_id}`);
|
|
492
|
+
break;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
456
495
|
buildDispatchContext(event, sessionKey) {
|
|
457
496
|
const binding = this.sessionBindings.get(sessionKey);
|
|
458
497
|
return {
|
|
@@ -1605,6 +1644,17 @@ export class ParallAgentGateway {
|
|
|
1605
1644
|
chat_id: item.chat_id,
|
|
1606
1645
|
});
|
|
1607
1646
|
}
|
|
1647
|
+
// A task assignment's first delivery rides task.assigned, but a typed lane
|
|
1648
|
+
// released without an effect is re-driven as dispatch.new. Reuse the exact
|
|
1649
|
+
// typed claim path used by catch-up so that recovery does not require a
|
|
1650
|
+
// runtime reconnect.
|
|
1651
|
+
async handleTaskAssignmentRedrive(item) {
|
|
1652
|
+
if (!item.task_id)
|
|
1653
|
+
return;
|
|
1654
|
+
await this.consumeTypedDispatch({ dispatchEventId: item.id }, (dispatchEventId) => this.handleTaskDispatch(item.task_id ?? '', item.source_id ?? item.task_id ?? '', {
|
|
1655
|
+
dispatchEventId,
|
|
1656
|
+
}), (dispatchEventId) => this.ackDispatchEvent(dispatchEventId ?? item.id, () => this.clearTypedDispatchDedupe(item)));
|
|
1657
|
+
}
|
|
1608
1658
|
consumeMessageWorkItem(item) {
|
|
1609
1659
|
return consumeMessageWorkItem(this.laneFlowHost(), item);
|
|
1610
1660
|
}
|
|
@@ -2116,9 +2166,7 @@ export class ParallAgentGateway {
|
|
|
2116
2166
|
}
|
|
2117
2167
|
processed++;
|
|
2118
2168
|
try {
|
|
2119
|
-
const ackItem = () =>
|
|
2120
|
-
this.opts.client.ackDispatchByID(this.opts.config.org_id, item.id).catch(() => { });
|
|
2121
|
-
};
|
|
2169
|
+
const ackItem = () => this.ackDispatchEvent(item.id, () => this.clearTypedDispatchDedupe(item));
|
|
2122
2170
|
if (item.event_type === 'task_assign' && item.task_id) {
|
|
2123
2171
|
try {
|
|
2124
2172
|
await this.consumeTypedDispatch({ dispatchEventId: item.id }, (dispatchEventId) => this.handleTaskDispatch(item.task_id ?? '', item.source_id ?? item.task_id ?? '', {
|
|
@@ -57,7 +57,7 @@ export declare function consumeTypedDispatch(host: LaneFlowHost, ref: {
|
|
|
57
57
|
dispatchEventId?: string;
|
|
58
58
|
sourceType?: string;
|
|
59
59
|
sourceId?: string;
|
|
60
|
-
}, run: (dispatchEventId?: string) => Promise<boolean>, ack: (dispatchEventId?: string) => void): Promise<void>;
|
|
60
|
+
}, run: (dispatchEventId?: string) => Promise<boolean>, ack: (dispatchEventId?: string) => void | Promise<void>): Promise<void>;
|
|
61
61
|
/**
|
|
62
62
|
* Shared consumption of a message WorkItem referenced by id — the ONE
|
|
63
63
|
* protocol for dispatch catch-up pages and post-complete re-drive hints
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gateway-lane-flow.d.ts","sourceRoot":"","sources":["../src/gateway-lane-flow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC5E,OAAO,KAAK,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAEtF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C;;;;;GAKG;AAEH,0EAA0E;AAC1E,MAAM,WAAW,YAAY;IAC3B,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,cAAc,EAAE,OAAO,CAAC;IACxB,YAAY,EAAE,OAAO,CAAC;IACtB,kBAAkB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAChC,IAAI,EAAE;QACJ,MAAM,EAAE,YAAY,CAAC;QACrB,GAAG,CAAC,EAAE,aAAa,CAAC;QACpB,MAAM,EAAE;YAAE,MAAM,EAAE,MAAM,CAAA;SAAE,CAAC;QAC3B,eAAe,EAAE,eAAe,CAAC;QACjC,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,cAAc,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC;IAC5C,oBAAoB,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,WAAW,CACT,KAAK,EAAE,WAAW,EAClB,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,EACpB,aAAa,CAAC,EAAE,WAAW,EAAE,EAC7B,WAAW,CAAC,EAAE,MAAM,EAAE,GACrB,OAAO,CAAC,OAAO,CAAC,CAAC;IACpB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;IACrC,4BAA4B,CAC1B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,uBAAuB,CAAC,CAAC;IACpC,kBAAkB,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC1D;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,YAAY,EAClB,IAAI,EAAE;IACJ,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,YAAY,EAAE,MAAM,OAAO,CAAC;CAC7B,GACA,OAAO,CAAC,YAAY,GAAG,SAAS,GAAG,UAAU,CAAC,CA6DhD;AAED;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,YAAY,EAClB,GAAG,EAAE;IAAE,eAAe,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,EACzE,GAAG,EAAE,CAAC,eAAe,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,EACnD,GAAG,EAAE,CAAC,eAAe,CAAC,EAAE,MAAM,KAAK,IAAI,
|
|
1
|
+
{"version":3,"file":"gateway-lane-flow.d.ts","sourceRoot":"","sources":["../src/gateway-lane-flow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC5E,OAAO,KAAK,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAEtF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C;;;;;GAKG;AAEH,0EAA0E;AAC1E,MAAM,WAAW,YAAY;IAC3B,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,cAAc,EAAE,OAAO,CAAC;IACxB,YAAY,EAAE,OAAO,CAAC;IACtB,kBAAkB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAChC,IAAI,EAAE;QACJ,MAAM,EAAE,YAAY,CAAC;QACrB,GAAG,CAAC,EAAE,aAAa,CAAC;QACpB,MAAM,EAAE;YAAE,MAAM,EAAE,MAAM,CAAA;SAAE,CAAC;QAC3B,eAAe,EAAE,eAAe,CAAC;QACjC,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,cAAc,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC;IAC5C,oBAAoB,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,WAAW,CACT,KAAK,EAAE,WAAW,EAClB,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,EACpB,aAAa,CAAC,EAAE,WAAW,EAAE,EAC7B,WAAW,CAAC,EAAE,MAAM,EAAE,GACrB,OAAO,CAAC,OAAO,CAAC,CAAC;IACpB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;IACrC,4BAA4B,CAC1B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,uBAAuB,CAAC,CAAC;IACpC,kBAAkB,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC1D;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,YAAY,EAClB,IAAI,EAAE;IACJ,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,YAAY,EAAE,MAAM,OAAO,CAAC;CAC7B,GACA,OAAO,CAAC,YAAY,GAAG,SAAS,GAAG,UAAU,CAAC,CA6DhD;AAED;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,YAAY,EAClB,GAAG,EAAE;IAAE,eAAe,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,EACzE,GAAG,EAAE,CAAC,eAAe,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,EACnD,GAAG,EAAE,CAAC,eAAe,CAAC,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GACtD,OAAO,CAAC,IAAI,CAAC,CAiCf;AAED;;;;;;;GAOG;AACH,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,YAAY,EAClB,IAAI,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACvD,OAAO,CAAC,IAAI,CAAC,CAuDf"}
|
|
@@ -68,7 +68,7 @@ export async function dispatchLaneGroup(host, opts) {
|
|
|
68
68
|
export async function consumeTypedDispatch(host, ref, run, ack) {
|
|
69
69
|
if (!host.laneLedger || host.ledgerDisabled) {
|
|
70
70
|
if (await run(ref.dispatchEventId))
|
|
71
|
-
ack(ref.dispatchEventId);
|
|
71
|
+
await ack(ref.dispatchEventId);
|
|
72
72
|
return;
|
|
73
73
|
}
|
|
74
74
|
let lane;
|
|
@@ -79,7 +79,7 @@ export async function consumeTypedDispatch(host, ref, run, ack) {
|
|
|
79
79
|
if (err instanceof LedgerUnsupportedError) {
|
|
80
80
|
host.disableLedger('claim endpoint missing');
|
|
81
81
|
if (await run(ref.dispatchEventId))
|
|
82
|
-
ack(ref.dispatchEventId);
|
|
82
|
+
await ack(ref.dispatchEventId);
|
|
83
83
|
return;
|
|
84
84
|
}
|
|
85
85
|
throw err;
|
|
@@ -89,8 +89,11 @@ export async function consumeTypedDispatch(host, ref, run, ack) {
|
|
|
89
89
|
return;
|
|
90
90
|
}
|
|
91
91
|
try {
|
|
92
|
+
// Ack must settle before Complete. A fire-and-forget ack races the lane
|
|
93
|
+
// release: Complete can return the still-received typed item to pending
|
|
94
|
+
// and publish a re-drive while its successful ack is still in flight.
|
|
92
95
|
if (await run(lane.typedDispatchEventId))
|
|
93
|
-
ack(lane.typedDispatchEventId);
|
|
96
|
+
await ack(lane.typedDispatchEventId);
|
|
94
97
|
}
|
|
95
98
|
finally {
|
|
96
99
|
// Release the occupancy row. A buffered dispatch may outlive this guard
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@parall/agent-core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.42.1",
|
|
4
4
|
"description": "Shared agent runtime orchestration helpers for Parall",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"@opentelemetry/sdk-logs": "^0.57.0",
|
|
36
36
|
"@opentelemetry/sdk-metrics": "^1.30.0",
|
|
37
37
|
"@opentelemetry/sdk-trace-node": "^1.30.0",
|
|
38
|
-
"@parall/sdk": "1.
|
|
38
|
+
"@parall/sdk": "1.42.1"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@types/node": "^22.0.0",
|
package/src/gateway-base.ts
CHANGED
|
@@ -438,17 +438,19 @@ export class ParallAgentGateway {
|
|
|
438
438
|
(dispatchEventId) => this.handleTaskAssignment(data, data.id, dispatchEventId),
|
|
439
439
|
(dispatchEventId) => {
|
|
440
440
|
if (dispatchEventId) {
|
|
441
|
-
this.
|
|
442
|
-
.
|
|
443
|
-
|
|
444
|
-
return;
|
|
441
|
+
return this.ackDispatchEvent(dispatchEventId, () => {
|
|
442
|
+
this.dispatchedTasks.delete(`${data.id}:${data.updated_at}`);
|
|
443
|
+
});
|
|
445
444
|
}
|
|
446
|
-
this.opts.client
|
|
445
|
+
return this.opts.client
|
|
447
446
|
.ackDispatch(this.opts.config.org_id, {
|
|
448
447
|
source_type: 'task_activity',
|
|
449
448
|
source_id: data.id,
|
|
450
449
|
})
|
|
451
|
-
.
|
|
450
|
+
.then(
|
|
451
|
+
() => undefined,
|
|
452
|
+
() => undefined,
|
|
453
|
+
);
|
|
452
454
|
},
|
|
453
455
|
);
|
|
454
456
|
} catch (err) {
|
|
@@ -457,7 +459,16 @@ export class ParallAgentGateway {
|
|
|
457
459
|
});
|
|
458
460
|
|
|
459
461
|
ws.on('dispatch.new', async (data: DispatchNewData) => {
|
|
460
|
-
if (data.event_type === '
|
|
462
|
+
if (data.event_type === 'task_assign') {
|
|
463
|
+
if (!data.task_id) return;
|
|
464
|
+
try {
|
|
465
|
+
await this.handleTaskAssignmentRedrive(data);
|
|
466
|
+
} catch (err) {
|
|
467
|
+
this.opts.log?.error(
|
|
468
|
+
`task assignment re-drive failed for ${data.task_id}: ${String(err)}`,
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
} else if (data.event_type === 'task_comment') {
|
|
461
472
|
if (!data.source_id || !data.task_id) return;
|
|
462
473
|
try {
|
|
463
474
|
await this.consumeTypedDispatch(
|
|
@@ -469,9 +480,7 @@ export class ParallAgentGateway {
|
|
|
469
480
|
data.actor_id,
|
|
470
481
|
data.delivery_reason,
|
|
471
482
|
),
|
|
472
|
-
() =>
|
|
473
|
-
this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {});
|
|
474
|
-
},
|
|
483
|
+
() => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)),
|
|
475
484
|
);
|
|
476
485
|
} catch (err) {
|
|
477
486
|
this.opts.log?.error(
|
|
@@ -484,9 +493,7 @@ export class ParallAgentGateway {
|
|
|
484
493
|
await this.consumeTypedDispatch(
|
|
485
494
|
{ dispatchEventId: data.id },
|
|
486
495
|
() => this.handleWikiComment(data.source_id, data.actor_id, data.delivery_reason),
|
|
487
|
-
() =>
|
|
488
|
-
this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {});
|
|
489
|
-
},
|
|
496
|
+
() => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)),
|
|
490
497
|
);
|
|
491
498
|
} catch (err) {
|
|
492
499
|
this.opts.log?.error(
|
|
@@ -503,9 +510,7 @@ export class ParallAgentGateway {
|
|
|
503
510
|
allowCreator: true,
|
|
504
511
|
dispatchEventId,
|
|
505
512
|
}),
|
|
506
|
-
() =>
|
|
507
|
-
this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {});
|
|
508
|
-
},
|
|
513
|
+
() => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)),
|
|
509
514
|
);
|
|
510
515
|
} catch (err) {
|
|
511
516
|
this.opts.log?.error(`task update dispatch failed for ${data.task_id}: ${String(err)}`);
|
|
@@ -516,9 +521,7 @@ export class ParallAgentGateway {
|
|
|
516
521
|
await this.consumeTypedDispatch(
|
|
517
522
|
{ dispatchEventId: data.id },
|
|
518
523
|
() => this.fetchAndHandleScheduleFire(data.source_id, data.actor_id),
|
|
519
|
-
() =>
|
|
520
|
-
this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {});
|
|
521
|
-
},
|
|
524
|
+
() => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)),
|
|
522
525
|
);
|
|
523
526
|
} catch (err) {
|
|
524
527
|
this.opts.log?.error(
|
|
@@ -531,9 +534,7 @@ export class ParallAgentGateway {
|
|
|
531
534
|
await this.consumeTypedDispatch(
|
|
532
535
|
{ dispatchEventId: data.id },
|
|
533
536
|
() => this.fetchAndHandleExternalTriggerRun(data.source_id),
|
|
534
|
-
() =>
|
|
535
|
-
this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {});
|
|
536
|
-
},
|
|
537
|
+
() => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)),
|
|
537
538
|
);
|
|
538
539
|
} catch (err) {
|
|
539
540
|
this.opts.log?.error(
|
|
@@ -546,9 +547,7 @@ export class ParallAgentGateway {
|
|
|
546
547
|
await this.consumeTypedDispatch(
|
|
547
548
|
{ dispatchEventId: data.id },
|
|
548
549
|
() => this.fetchAndHandleChannelMessage(data.source_id),
|
|
549
|
-
() =>
|
|
550
|
-
this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {});
|
|
551
|
-
},
|
|
550
|
+
() => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)),
|
|
552
551
|
);
|
|
553
552
|
} catch (err) {
|
|
554
553
|
this.opts.log?.error(
|
|
@@ -566,9 +565,7 @@ export class ParallAgentGateway {
|
|
|
566
565
|
data.actor_id,
|
|
567
566
|
data.chat_id ?? null,
|
|
568
567
|
),
|
|
569
|
-
() =>
|
|
570
|
-
this.opts.client.ackDispatchByID(this.opts.config.org_id, data.id).catch(() => {});
|
|
571
|
-
},
|
|
568
|
+
() => this.ackDispatchEvent(data.id, () => this.clearTypedDispatchDedupe(data)),
|
|
572
569
|
);
|
|
573
570
|
} catch (err) {
|
|
574
571
|
this.opts.log?.error(
|
|
@@ -589,14 +586,11 @@ export class ParallAgentGateway {
|
|
|
589
586
|
} catch (err) {
|
|
590
587
|
this.opts.log?.error(`message re-drive failed for ${data.source_id}: ${String(err)}`);
|
|
591
588
|
}
|
|
592
|
-
} else if (data.event_type !== 'message'
|
|
589
|
+
} else if (data.event_type !== 'message') {
|
|
593
590
|
// Truly unknown event_type — log so a newly-added dispatch type
|
|
594
|
-
// not yet wired here surfaces during runtime testing. "message"
|
|
595
|
-
//
|
|
596
|
-
//
|
|
597
|
-
// (message.new, task.assigned) above and would otherwise spam
|
|
598
|
-
// info-level logs for every inbound chat message / task
|
|
599
|
-
// assignment on a busy agent.
|
|
591
|
+
// not yet wired here surfaces during runtime testing. "message" is
|
|
592
|
+
// deliberately excluded because its first delivery is owned by the
|
|
593
|
+
// message.new handler; recovered task_assign rows are handled above.
|
|
600
594
|
this.opts.log?.info(
|
|
601
595
|
`dispatch.new with unhandled event_type=${String(data.event_type)} (id=${data.id}) — no-op`,
|
|
602
596
|
);
|
|
@@ -686,11 +680,59 @@ export class ParallAgentGateway {
|
|
|
686
680
|
private consumeTypedDispatch(
|
|
687
681
|
ref: { dispatchEventId?: string; sourceType?: string; sourceId?: string },
|
|
688
682
|
run: (dispatchEventId?: string) => Promise<boolean>,
|
|
689
|
-
ack: (dispatchEventId?: string) => void
|
|
683
|
+
ack: (dispatchEventId?: string) => void | Promise<void>,
|
|
690
684
|
): Promise<void> {
|
|
691
685
|
return consumeTypedDispatch(this.laneFlowHost(), ref, run, ack);
|
|
692
686
|
}
|
|
693
687
|
|
|
688
|
+
// Typed completion must wait until the administrative ack has either
|
|
689
|
+
// committed or failed. Errors stay best-effort: a failed ack leaves the row
|
|
690
|
+
// received, so Complete releases and re-drives it safely.
|
|
691
|
+
private ackDispatchEvent(dispatchEventId: string, onFailure?: () => void): Promise<void> {
|
|
692
|
+
return this.opts.client.ackDispatchByID(this.opts.config.org_id, dispatchEventId).then(
|
|
693
|
+
() => undefined,
|
|
694
|
+
(err) => {
|
|
695
|
+
// Complete will return the still-received item to pending and publish
|
|
696
|
+
// an immediate hint. Free its hot-path claim first, otherwise that
|
|
697
|
+
// same-process re-drive is rejected forever by the local dedupe set.
|
|
698
|
+
onFailure?.();
|
|
699
|
+
this.opts.log?.warn(
|
|
700
|
+
`dispatch ack failed for ${dispatchEventId}, releasing for re-drive: ${String(err)}`,
|
|
701
|
+
);
|
|
702
|
+
},
|
|
703
|
+
);
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
private clearTypedDispatchDedupe(item: DispatchNewData): void {
|
|
707
|
+
switch (item.event_type) {
|
|
708
|
+
case 'task_assign':
|
|
709
|
+
case 'task_update':
|
|
710
|
+
if (item.task_id) {
|
|
711
|
+
const prefix = `${item.task_id}:`;
|
|
712
|
+
for (const key of this.dispatchedTasks) {
|
|
713
|
+
if (key.startsWith(prefix)) this.dispatchedTasks.delete(key);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
break;
|
|
717
|
+
case 'task_comment':
|
|
718
|
+
case 'wiki_comment':
|
|
719
|
+
if (item.source_id) this.dispatchedTasks.delete(`comment:${item.source_id}`);
|
|
720
|
+
break;
|
|
721
|
+
case 'schedule.fire':
|
|
722
|
+
if (item.source_id) this.dispatchedTasks.delete(`schedule_run:${item.source_id}`);
|
|
723
|
+
break;
|
|
724
|
+
case 'external_trigger':
|
|
725
|
+
if (item.source_id) this.dispatchedTasks.delete(`external_trigger_run:${item.source_id}`);
|
|
726
|
+
break;
|
|
727
|
+
case 'channel_message':
|
|
728
|
+
if (item.source_id) this.dispatchedMessages.delete(`channel_message:${item.source_id}`);
|
|
729
|
+
break;
|
|
730
|
+
case 'approval_decided':
|
|
731
|
+
if (item.source_id) this.dispatchedTasks.delete(`approval:${item.source_id}`);
|
|
732
|
+
break;
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
|
|
694
736
|
private buildDispatchContext(event: ParallEvent, sessionKey: string): DispatchContext {
|
|
695
737
|
const binding = this.sessionBindings.get(sessionKey);
|
|
696
738
|
return {
|
|
@@ -2023,6 +2065,25 @@ export class ParallAgentGateway {
|
|
|
2023
2065
|
});
|
|
2024
2066
|
}
|
|
2025
2067
|
|
|
2068
|
+
// A task assignment's first delivery rides task.assigned, but a typed lane
|
|
2069
|
+
// released without an effect is re-driven as dispatch.new. Reuse the exact
|
|
2070
|
+
// typed claim path used by catch-up so that recovery does not require a
|
|
2071
|
+
// runtime reconnect.
|
|
2072
|
+
private async handleTaskAssignmentRedrive(item: DispatchNewData): Promise<void> {
|
|
2073
|
+
if (!item.task_id) return;
|
|
2074
|
+
await this.consumeTypedDispatch(
|
|
2075
|
+
{ dispatchEventId: item.id },
|
|
2076
|
+
(dispatchEventId) =>
|
|
2077
|
+
this.handleTaskDispatch(item.task_id ?? '', item.source_id ?? item.task_id ?? '', {
|
|
2078
|
+
dispatchEventId,
|
|
2079
|
+
}),
|
|
2080
|
+
(dispatchEventId) =>
|
|
2081
|
+
this.ackDispatchEvent(dispatchEventId ?? item.id, () =>
|
|
2082
|
+
this.clearTypedDispatchDedupe(item),
|
|
2083
|
+
),
|
|
2084
|
+
);
|
|
2085
|
+
}
|
|
2086
|
+
|
|
2026
2087
|
private consumeMessageWorkItem(item: {
|
|
2027
2088
|
id: string;
|
|
2028
2089
|
source_id: string;
|
|
@@ -2596,9 +2657,8 @@ export class ParallAgentGateway {
|
|
|
2596
2657
|
|
|
2597
2658
|
processed++;
|
|
2598
2659
|
try {
|
|
2599
|
-
const ackItem = () =>
|
|
2600
|
-
this.
|
|
2601
|
-
};
|
|
2660
|
+
const ackItem = () =>
|
|
2661
|
+
this.ackDispatchEvent(item.id, () => this.clearTypedDispatchDedupe(item));
|
|
2602
2662
|
if (item.event_type === 'task_assign' && item.task_id) {
|
|
2603
2663
|
try {
|
|
2604
2664
|
await this.consumeTypedDispatch(
|
package/src/gateway-lane-flow.ts
CHANGED
|
@@ -133,10 +133,10 @@ export async function consumeTypedDispatch(
|
|
|
133
133
|
host: LaneFlowHost,
|
|
134
134
|
ref: { dispatchEventId?: string; sourceType?: string; sourceId?: string },
|
|
135
135
|
run: (dispatchEventId?: string) => Promise<boolean>,
|
|
136
|
-
ack: (dispatchEventId?: string) => void
|
|
136
|
+
ack: (dispatchEventId?: string) => void | Promise<void>,
|
|
137
137
|
): Promise<void> {
|
|
138
138
|
if (!host.laneLedger || host.ledgerDisabled) {
|
|
139
|
-
if (await run(ref.dispatchEventId)) ack(ref.dispatchEventId);
|
|
139
|
+
if (await run(ref.dispatchEventId)) await ack(ref.dispatchEventId);
|
|
140
140
|
return;
|
|
141
141
|
}
|
|
142
142
|
let lane: Awaited<ReturnType<LaneLedger['claimTyped']>>;
|
|
@@ -145,7 +145,7 @@ export async function consumeTypedDispatch(
|
|
|
145
145
|
} catch (err) {
|
|
146
146
|
if (err instanceof LedgerUnsupportedError) {
|
|
147
147
|
host.disableLedger('claim endpoint missing');
|
|
148
|
-
if (await run(ref.dispatchEventId)) ack(ref.dispatchEventId);
|
|
148
|
+
if (await run(ref.dispatchEventId)) await ack(ref.dispatchEventId);
|
|
149
149
|
return;
|
|
150
150
|
}
|
|
151
151
|
throw err;
|
|
@@ -157,7 +157,10 @@ export async function consumeTypedDispatch(
|
|
|
157
157
|
return;
|
|
158
158
|
}
|
|
159
159
|
try {
|
|
160
|
-
|
|
160
|
+
// Ack must settle before Complete. A fire-and-forget ack races the lane
|
|
161
|
+
// release: Complete can return the still-received typed item to pending
|
|
162
|
+
// and publish a re-drive while its successful ack is still in flight.
|
|
163
|
+
if (await run(lane.typedDispatchEventId)) await ack(lane.typedDispatchEventId);
|
|
161
164
|
} finally {
|
|
162
165
|
// Release the occupancy row. A buffered dispatch may outlive this guard
|
|
163
166
|
// (lane TTL) — acceptable at-least-once; the ledger's resolution paths
|