@dorokuma/herdsman-pi 0.14.3 → 0.14.4

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.ts +28 -7
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dorokuma/herdsman-pi",
3
- "version": "0.14.3",
3
+ "version": "0.14.4",
4
4
  "description": "Pi extension bridge for Herdsman agent history.",
5
5
  "type": "module",
6
6
  "keywords": [
package/src/index.ts CHANGED
@@ -622,7 +622,22 @@ export function createHerdsmanPiExtension(options: ExtensionOptions = {}) {
622
622
  if (carriedOver.length > 0) {
623
623
  logHerdsmanPi(
624
624
  "info",
625
- `[herdsman-pi] keeping ${carriedOver.length} unconsumed wake event id(s) suppressed across the scope change eventIds=${carriedOver.join(",")} · daemon redelivery for them is traded away to keep the transcript single-copy`,
625
+ `[herdsman-pi] keeping ${carriedOver.length} unconsumed wake event id(s) suppressed on a delivery reset eventIds=${carriedOver.join(",")} · daemon redelivery for them is traded away to keep the transcript single-copy`,
626
+ );
627
+ }
628
+ // Written-off ids leave with the same reset: they are still in the delivery
629
+ // queue (unconfirmed content must keep blocking the watermark), so without
630
+ // this line nothing would say which dead letters stopped being retried here.
631
+ // This function only copies ids from the awaiting set (`wakeAwaitingConsumption`)
632
+ // into the suppression set, so a written-off id is *not* suppressed: if the
633
+ // daemon redelivers it the update may be injected again. What the reset costs is
634
+ // the local retry bookkeeping for it; this function does not assert whether the
635
+ // daemon still holds the update.
636
+ const writtenOff = [...state.wakeRetryableEventIds].sort((left, right) => left - right);
637
+ if (writtenOff.length > 0) {
638
+ logHerdsmanPi(
639
+ "info",
640
+ `[herdsman-pi] dropping ${writtenOff.length} written-off wake event id(s) on a delivery reset eventIds=${writtenOff.join(",")} · only the local dead-letter retry bookkeeping is dropped here; these ids do not join the suppression set, so if the daemon redelivers them the update may be injected again`,
626
641
  );
627
642
  }
628
643
  state.presentedEventIds.clear();
@@ -862,8 +877,12 @@ export function createHerdsmanPiExtension(options: ExtensionOptions = {}) {
862
877
  }
863
878
  const ackResponse = (await state.client.request("agent.notifications.ack", {
864
879
  eventId: event.id,
865
- })) as { ackedEventId?: number; state?: { ackedEventId?: number } } | undefined;
866
- pruneAcknowledgedEvents(ackResponse?.ackedEventId ?? ackResponse?.state?.ackedEventId);
880
+ })) as { state?: { ackedEventId?: number } } | undefined;
881
+ // The ack contract has no top-level field: the daemon answers
882
+ // `{ acknowledged: true, state: toWireState(state) }` (see
883
+ // `src/daemon/observability-server.ts`), so reading only `state` here
884
+ // is also fail-closed — `pruneAcknowledgedEvents(undefined)` is a no-op.
885
+ pruneAcknowledgedEvents(ackResponse?.state?.ackedEventId);
867
886
  state.pendingEvents = state.pendingEvents.filter((pending) => pending.id !== event.id);
868
887
  // The event is confirmed: it leaves the delivery queue for good, which
869
888
  // is what keeps the acknowledgement watermark monotonic (ids are acked
@@ -1165,8 +1184,10 @@ export function createHerdsmanPiExtension(options: ExtensionOptions = {}) {
1165
1184
  const unconfirmedWatermarkBarrier = ():
1166
1185
  | { blocked: number; eventId: number; reason: string }
1167
1186
  | undefined => {
1168
- // How many ids the barrier holds back: awaiting consumption, or still
1169
- // owed a dead-letter recovery (the two sets this walk checks).
1187
+ // How many ids the barrier sets hold (members of the two sets, not the
1188
+ // number of later ids the watermark is held back over): awaiting
1189
+ // consumption, or still owed a dead-letter recovery (the two sets this
1190
+ // walk checks).
1170
1191
  const blocked = new Set([
1171
1192
  ...state.wakeAwaitingConsumption,
1172
1193
  ...state.wakeRetryableEventIds,
@@ -1197,7 +1218,7 @@ export function createHerdsmanPiExtension(options: ExtensionOptions = {}) {
1197
1218
  * the log shows nothing, so a stalled delivery cannot be told apart from an
1198
1219
  * idle one. The line names the smallest blocked id, why it blocks
1199
1220
  * (dead-letter backoff, spent dead-letter budget, or awaiting consumption)
1200
- * and how many ids it holds back, in the same field style as the write-off
1221
+ * and how many ids form the barrier, in the same field style as the write-off
1201
1222
  * warning and the injection-skip note. Deduplicating on the exact state
1202
1223
  * keeps a long stall from flooding the file; the window keeps a stall that
1203
1224
  * outlives its first line visible.
@@ -1225,7 +1246,7 @@ export function createHerdsmanPiExtension(options: ExtensionOptions = {}) {
1225
1246
  lastWakeBarrierNote = { at: now, key };
1226
1247
  logHerdsmanPi(
1227
1248
  "info",
1228
- `[herdsman-pi] wake acknowledgement held eventId=${barrier.eventId} reason=${barrier.reason} blocked=${barrier.blocked} · the daemon confirms by watermark (id <= ?), so no larger id may be acknowledged while this one is unconfirmed; the held update stays pending in the daemon and recoverable`,
1249
+ `[herdsman-pi] wake acknowledgement held eventId=${barrier.eventId} reason=${barrier.reason} blocked=${barrier.blocked} · the daemon confirms by watermark (id <= ?), so no larger id may be acknowledged while this one is unconfirmed`,
1229
1250
  );
1230
1251
  };
1231
1252