File size: 5,827 Bytes
499d3d5 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | /**
* Stable failures exposed by the session-persistence service and its handles,
* including the format refusals shared by every backend: a stored log this
* build cannot faithfully interpret is refused, never misread, and the
* refusal points at the raw artifact when the backend keeps one per session.
* @module @deepseek-ai/dsh-session-persistence/errors
*/
import { SESSION_FORMAT_VERSION } from '@deepseek-ai/dsh-session'
import type { SessionId } from '@deepseek-ai/dsh-session'
/** The requested Session identity has no durable log visible to this caller. */
export class SessionPersistenceNotFoundError extends Error {
/** @param sessionId - absent durable Session identity. */
constructor(readonly sessionId: SessionId) {
super(`session "${sessionId}" not found`)
this.name = 'SessionPersistenceNotFoundError'
}
}
/** `create` targeted a Session identity that already exists in this backend. */
export class SessionAlreadyExistsError extends Error {
/** @param sessionId - the occupied durable Session identity. */
constructor(readonly sessionId: SessionId) {
super(`session "${sessionId}" already exists`)
this.name = 'SessionAlreadyExistsError'
}
}
/** A write open found the session already bound to an active write handle. */
export class SessionAlreadyOwnedError extends Error {
/** @param sessionId - the session whose write ownership is taken. */
constructor(readonly sessionId: SessionId) {
super(`session "${sessionId}" is already owned by an active write handle`)
this.name = 'SessionAlreadyOwnedError'
}
}
/** A mutation (`append`/`flush`) was called on a read handle. */
export class SessionReadOnlyError extends Error {
/**
* @param sessionId - the session the read handle observes.
* @param operation - the refused mutating operation name.
*/
constructor(readonly sessionId: SessionId, operation: string) {
super(`session "${sessionId}": ${operation} is not available on a read handle`)
this.name = 'SessionReadOnlyError'
}
}
/**
* A write handle's ownership is permanently gone: its lease expired, a renewal
* failed, or the durable ownership record no longer names this handle. The
* handle never re-acquires ownership — close it and reopen for write.
*
* Declared for the cross-process lease layer; the shipped in-process backends
* never throw it yet.
*/
export class SessionOwnershipLostError extends Error {
/** @param sessionId - the session whose write ownership this handle lost. */
constructor(readonly sessionId: SessionId) {
super(`session "${sessionId}": write ownership was lost; close this handle and reopen`)
this.name = 'SessionOwnershipLostError'
}
}
/** An operation was called on a handle after `close()` was called. */
export class SessionHandleClosedError extends Error {
/**
* @param sessionId - the session the closed handle addressed.
* @param operation - the refused operation name.
*/
constructor(readonly sessionId: SessionId, operation: string) {
super(`session "${sessionId}": ${operation} on a closed handle`)
this.name = 'SessionHandleClosedError'
}
}
/**
* A backend-resolved, per-session local artifact location. Carried only by
* refusal diagnostics ({@link SessionFormatUnsupportedError}) so a user can
* find the raw log a build refused to interpret; it is not a consumer-facing
* query — log access goes through a session handle's `read`.
*/
export interface SessionLocation {
/** Backend-specific artifact kind, for example `jsonl`. */
readonly kind: string
/** Absolute path to this session's backend-owned artifact. */
readonly path: string
}
/** Durable session contents failed validation after a successful backend read. */
export class SessionPersistenceCorruptionError extends Error {
/**
* @param message - stable corruption context.
* @param options - original validation failure.
*/
constructor(message: string, options: ErrorOptions) {
super(message, options)
this.name = 'SessionPersistenceCorruptionError'
}
}
/**
* The stored log is intact but this runtime cannot faithfully interpret it:
* the header carries an unsupported format version, or an event's type is
* unknown to this build. Distinct from {@link SessionPersistenceCorruptionError}
* — nothing is damaged; the raw log remains readable at {@link location} when
* the backend keeps one artifact per session.
*/
export class SessionFormatUnsupportedError extends Error {
/**
* @param message - stable reason the log cannot be interpreted, already
* including the raw-log path when one exists.
* @param location - the backend's artifact location, when one exists.
*/
constructor(message: string, readonly location?: SessionLocation) {
super(message)
this.name = 'SessionFormatUnsupportedError'
}
}
/**
* Direction-aware refusal text for a stored session whose format version this
* build does not read. Shared by load-time checks and by backends that must
* refuse BEFORE decoding version-dependent structure (a future format may not
* satisfy this build's structural checks at all, and the user must see
* "upgrade the harness", never "corrupt").
* @param id - the stored session id, for message context.
* @param version - the stored format version.
* @returns the stable refusal text, without a raw-log path suffix.
*/
export function sessionFormatVersionRefusal(id: string, version: number): string {
return version > SESSION_FORMAT_VERSION
? `session "${id}" uses log format v${version}, but this harness reads only v${SESSION_FORMAT_VERSION}: the log was written by a newer harness — upgrade the harness to open it`
: `session "${id}" uses log format v${version}, older than the supported v${SESSION_FORMAT_VERSION}, and this build ships no upgrade path for it`
}
|