dsh-lcx-codex 0.4.3 → 0.4.4-pre.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.
@@ -1,4 +1,5 @@
1
1
  import { AsyncLocalStorage } from "node:async_hooks";
2
+ import { abortError } from "./abort-error.js";
2
3
  const activePolicy = new AsyncLocalStorage();
3
4
  const projections = new WeakMap();
4
5
  function scopedValue(values, target, key) {
@@ -118,11 +119,6 @@ function restoreProjection(record) {
118
119
  }
119
120
  catch { }
120
121
  }
121
- function operationError(signal) {
122
- if (signal?.reason instanceof Error)
123
- return signal.reason;
124
- return new DOMException("The operation was aborted", "AbortError");
125
- }
126
122
  export class InvocationPolicyScope {
127
123
  records = new Set();
128
124
  queue = [];
@@ -147,7 +143,7 @@ export class InvocationPolicyScope {
147
143
  if (this.closed)
148
144
  return Promise.reject(this.closeReason ?? new Error("invocation policy scope closed"));
149
145
  if (signal?.aborted)
150
- return Promise.reject(operationError(signal));
146
+ return Promise.reject(abortError(signal));
151
147
  if (this.queue.length === 0 &&
152
148
  !this.exclusiveActive &&
153
149
  (mode === "shared" || this.sharedActive === 0)) {
@@ -165,7 +161,7 @@ export class InvocationPolicyScope {
165
161
  if (index >= 0)
166
162
  this.queue.splice(index, 1);
167
163
  signal.removeEventListener("abort", onAbort);
168
- reject(operationError(signal));
164
+ reject(abortError(signal));
169
165
  this.flushQueue();
170
166
  };
171
167
  waiter.onAbort = onAbort;
@@ -193,7 +189,7 @@ export class InvocationPolicyScope {
193
189
  this.queue.shift();
194
190
  if (waiter.onAbort)
195
191
  waiter.signal.removeEventListener("abort", waiter.onAbort);
196
- waiter.reject(operationError(waiter.signal));
192
+ waiter.reject(abortError(waiter.signal));
197
193
  continue;
198
194
  }
199
195
  if (waiter.mode === "exclusive") {
@@ -485,16 +485,55 @@ export function checkpointStateForMessage(session, message) {
485
485
  export function stateRouteCompatible(state, route, ctx) {
486
486
  return routeCompatible(state, route, ctx);
487
487
  }
488
+ const DSH_PROJECTION_UNAVAILABLE_PREFIX = 'session message projection "';
489
+ const DSH_PROJECTION_UNAVAILABLE_SUFFIX = '" was removed or replaced; restore the session with its owning plugin';
488
490
  /**
491
+ * DSH 0.1.6 attaches no error code or subclass to this failure and exposes no
492
+ * public inventory of required or active projections, so message shape is the
493
+ * only available signal. Both classes fail closed, so a misclassification
494
+ * costs precision in the error code, not correctness.
495
+ * @param {unknown} cause
496
+ */
497
+ function isMissingOrReplacedProjectionError(cause) {
498
+ if (!(cause instanceof Error))
499
+ return false;
500
+ const { message } = cause;
501
+ if (!message.startsWith(DSH_PROJECTION_UNAVAILABLE_PREFIX) ||
502
+ !message.endsWith(DSH_PROJECTION_UNAVAILABLE_SUFFIX))
503
+ return false;
504
+ const projectionType = message.slice(DSH_PROJECTION_UNAVAILABLE_PREFIX.length, message.length - DSH_PROJECTION_UNAVAILABLE_SUFFIX.length);
505
+ return projectionType.length > 0;
506
+ }
507
+ function checkpointHistoryReadError(cause) {
508
+ return Object.assign(new Error("Unsupported or invalid LCX checkpoint history; start a new session", {
509
+ cause,
510
+ }), { code: "LCX_CHECKPOINT_UNSUPPORTED" });
511
+ }
512
+ /**
513
+ * DSH 0.1.6 derives a message only while every `SessionMessageProjection` the
514
+ * durable log committed is still registered; an absent or replaced owner throws
515
+ * instead of yielding pre-projection content. Reconstructing the shadowed
516
+ * transcript without that owner would resurrect content a durable decision
517
+ * (for example `image/offload`) removed, so this is a checkpoint
518
+ * incompatibility rather than a recoverable read.
489
519
  * @param {Session} session
490
520
  * @param {number} seq
491
521
  * @returns {Message | undefined}
492
522
  */
493
523
  function eventMessage(session, seq) {
494
524
  if (!Number.isSafeInteger(seq) || seq < 0)
495
- return undefined;
496
- const event = session.eventAt(SessionSeq(seq));
497
- return event ? (session.deriveEventMessage(event) ?? undefined) : undefined;
525
+ throw checkpointHistoryReadError(new Error("Portable checkpoint history contains an invalid event sequence"));
526
+ try {
527
+ const event = session.eventAt(SessionSeq(seq));
528
+ if (!event)
529
+ throw new Error(`Portable checkpoint history is missing event at seq ${seq}`);
530
+ return session.deriveEventMessage(event) ?? undefined;
531
+ }
532
+ catch (cause) {
533
+ if (isMissingOrReplacedProjectionError(cause))
534
+ throw Object.assign(new Error("Session history requires a message projection this runtime does not provide; restore the owning plugin to reuse this checkpoint", { cause }), { code: "LCX_CHECKPOINT_PROJECTION_UNAVAILABLE" });
535
+ throw checkpointHistoryReadError(cause);
536
+ }
498
537
  }
499
538
  /** @param {Message} message */
500
539
  function estimateChars(message) {