@xmanrui/dsh-im 4.28.1 → 4.29.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.
@@ -272,6 +272,7 @@ export class HarnessApprovalQueue {
272
272
  requiresMention: context.requiresMention === true,
273
273
  send,
274
274
  render,
275
+ onResolved: typeof context.onResolved === 'function' ? context.onResolved : null,
275
276
  text,
276
277
  presented: false,
277
278
  presentationTask: null,
@@ -328,8 +329,7 @@ export class HarnessApprovalQueue {
328
329
  delivered = await presentationTask.then(() => true, () => false);
329
330
  }
330
331
  if (shouldNotify && delivered) {
331
- pending.resolutionNotified = true;
332
- await send(approvalOutcomeText(resolution.outcome)).catch(() => undefined);
332
+ await this.#notifyResolved(pending, resolution.outcome, send);
333
333
  }
334
334
  });
335
335
  return true;
@@ -347,16 +347,14 @@ export class HarnessApprovalQueue {
347
347
  { signal: AbortSignal.timeout(5_000) },
348
348
  );
349
349
  pending.closedOutcome = 'rejected';
350
- if ((pending.presented || pending.deliveryCompleted) && !pending.resolutionNotified) {
351
- pending.resolutionNotified = true;
352
- await pending.send(approvalOutcomeText('rejected')).catch(() => undefined);
350
+ if (pending.presented || pending.deliveryCompleted) {
351
+ await this.#notifyResolved(pending, 'rejected');
353
352
  }
354
353
  } catch (error) {
355
354
  if (error?.code === 'interaction-not-pending') {
356
355
  pending.closedOutcome = 'resolved';
357
- if ((pending.presented || pending.deliveryCompleted) && !pending.resolutionNotified) {
358
- pending.resolutionNotified = true;
359
- await pending.send(t(APPROVAL_RESOLVED_TEXT)).catch(() => undefined);
356
+ if (pending.presented || pending.deliveryCompleted) {
357
+ await this.#notifyResolved(pending, 'resolved');
360
358
  }
361
359
  } else {
362
360
  this.#logger.warn?.(`[dsh-im:${this.#label}] failed to reject a closing approval:`, error);
@@ -383,9 +381,8 @@ export class HarnessApprovalQueue {
383
381
  pending.deliveryCompleted = true;
384
382
  if (!pending.inactive) {
385
383
  pending.presented = true;
386
- } else if (pending.closedOutcome && !pending.resolutionNotified) {
387
- pending.resolutionNotified = true;
388
- await pending.send(approvalOutcomeText(pending.closedOutcome)).catch(() => undefined);
384
+ } else if (pending.closedOutcome) {
385
+ await this.#notifyResolved(pending, pending.closedOutcome);
389
386
  }
390
387
  } finally {
391
388
  if (pending.presentationTask === task) pending.presentationTask = null;
@@ -401,9 +398,7 @@ export class HarnessApprovalQueue {
401
398
  const send = pending.send;
402
399
  const next = this.#remove(pending);
403
400
  await this.#transition(next, async () => {
404
- if (!pending.resolutionNotified) {
405
- await send(t(APPROVAL_RESOLVED_TEXT)).catch(() => undefined);
406
- }
401
+ await this.#notifyResolved(pending, 'resolved', send);
407
402
  });
408
403
  return;
409
404
  }
@@ -417,12 +412,24 @@ export class HarnessApprovalQueue {
417
412
  const send = pending.send;
418
413
  const next = this.#remove(pending);
419
414
  await this.#transition(next, async () => {
420
- if (!pending.resolutionNotified) {
421
- await send(approvalOutcomeText(outcome)).catch(() => undefined);
422
- }
415
+ await this.#notifyResolved(pending, outcome, send);
423
416
  });
424
417
  }
425
418
 
419
+ async #notifyResolved(pending, outcome, send = pending.send) {
420
+ if (pending.resolutionNotified) return;
421
+ // Claim notification before awaiting the channel, so a concurrent close or
422
+ // resolved event cannot replace the confirmed result or notify twice.
423
+ pending.resolutionNotified = true;
424
+ const text = approvalOutcomeText(outcome);
425
+ try {
426
+ await pending.onResolved?.(text);
427
+ } catch (error) {
428
+ this.#logger.warn?.(`[dsh-im:${this.#label}] failed to update a resolved approval:`, error);
429
+ }
430
+ await send(text).catch(() => undefined);
431
+ }
432
+
426
433
  async #transition(next, work) {
427
434
  let release;
428
435
  const barrier = new Promise((resolve) => { release = resolve; });