mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-09-13 04:03:30 +00:00
202 lines
8.0 KiB
TypeScript
202 lines
8.0 KiB
TypeScript
/**
|
|
* Durable session-persistence Service Definition (`ctx.sessionPersistence`). Backends store
|
|
* {@link SessionEvent}s as the event-sourced log and carry non-replayable
|
|
* {@link SessionHeader} metadata separately; callers address one stored
|
|
* session through a {@link SessionHandle} obtained from `create`/`open`.
|
|
* @module @deepseek-ai/dsh-session-persistence
|
|
*/
|
|
|
|
import { Context, Service } from '@deepseek-ai/cordis'
|
|
import type { SessionEvent, SessionHeader, SessionId, SessionLogOffset } from '@deepseek-ai/dsh-session'
|
|
import type { SessionHandle, SessionAccess } from './handle.ts'
|
|
import type { SessionPersistenceRevision } from './revision.ts'
|
|
|
|
// Re-export the metadata vocabulary so Consumers import it from the Service Definition.
|
|
export type { SessionHeader } from '@deepseek-ai/dsh-session'
|
|
export { SessionPersistenceRevision } from './revision.ts'
|
|
export type {
|
|
SessionAccess,
|
|
SessionHandle,
|
|
SessionHandleAppendOptions,
|
|
SessionHandleFlushOptions,
|
|
SessionHandleReadOptions,
|
|
SessionHandleReadResult,
|
|
} from './handle.ts'
|
|
export {
|
|
SessionAlreadyExistsError,
|
|
SessionAlreadyOwnedError,
|
|
SessionFormatUnsupportedError,
|
|
SessionHandleClosedError,
|
|
SessionOwnershipLostError,
|
|
SessionPersistenceCorruptionError,
|
|
SessionPersistenceNotFoundError,
|
|
SessionReadOnlyError,
|
|
sessionFormatVersionRefusal,
|
|
} from './errors.ts'
|
|
export type { SessionLocation } from './errors.ts'
|
|
export {
|
|
assertContiguous,
|
|
assertStoredId,
|
|
assertVersion,
|
|
materializeAppendBatch,
|
|
materializeCreateHeader,
|
|
validateStoredEvents,
|
|
} from './storage-contract.ts'
|
|
|
|
/**
|
|
* Lightweight stored-session observation returned by {@link SessionPersistence.stat}
|
|
* and {@link SessionPersistence.list} without reading the full event log.
|
|
*/
|
|
export interface SessionPersistenceSnapshot {
|
|
/** Detached metadata for one stored session. */
|
|
readonly header: SessionHeader
|
|
/** Opaque change token; see {@link SessionPersistence.stat}. */
|
|
readonly revision: SessionPersistenceRevision
|
|
/** Logical event count, when the backend can provide it cheaply from metadata; otherwise absent. */
|
|
readonly eventCount?: number
|
|
/** Physical artifact byte size, when the backend can provide it cheaply (JSONL); otherwise absent. */
|
|
readonly sizeBytes?: number
|
|
}
|
|
|
|
/** Options for {@link SessionPersistence.create}. */
|
|
export interface SessionPersistenceCreateOptions {
|
|
/** Optional cancellation observed before backend work starts. */
|
|
readonly signal?: AbortSignal
|
|
/**
|
|
* Exact fork-inherited prefix length. Required when `header.isSeeded` is
|
|
* true and must be omitted (or `0`) otherwise; the backend refuses a
|
|
* mismatch at create.
|
|
*/
|
|
readonly inheritedEventCount?: SessionLogOffset
|
|
}
|
|
|
|
/**
|
|
* Logical Session header paired with its exact inherited cut for body-bearing
|
|
* storage operations. `isSeeded` marks fork lineage on the header; the
|
|
* numeric cut travels beside it, never inside the replayable event log.
|
|
*/
|
|
export interface SessionStorageMetadata {
|
|
/** Validated immutable Session header. */
|
|
readonly meta: SessionHeader
|
|
/** Number of leading events inherited from the Session's fork parent. */
|
|
readonly inheritedEventCount: SessionLogOffset
|
|
}
|
|
|
|
/** Immutable logical session read: storage metadata plus the complete validated event log. */
|
|
export interface SessionInspection extends SessionStorageMetadata {
|
|
/** Contiguous validated events from seq 0. */
|
|
readonly events: readonly SessionEvent[]
|
|
}
|
|
|
|
/** Options for {@link SessionPersistence.open}. */
|
|
export interface SessionPersistenceOpenOptions {
|
|
/** Optional cancellation observed before backend work starts. */
|
|
readonly signal?: AbortSignal
|
|
}
|
|
|
|
/** Options for {@link SessionPersistence.stat}. */
|
|
export interface SessionPersistenceStatOptions {
|
|
/** Optional cancellation for backend metadata reads. */
|
|
readonly signal?: AbortSignal
|
|
}
|
|
|
|
/** Options for {@link SessionPersistence.list}. */
|
|
export interface SessionPersistenceListOptions {
|
|
/** Optional cancellation for backend listing work. */
|
|
readonly signal?: AbortSignal
|
|
}
|
|
|
|
declare module '@deepseek-ai/cordis' {
|
|
interface Context {
|
|
sessionPersistence: SessionPersistence
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Durable append-only session storage addressed through per-session handles.
|
|
*
|
|
* Storage semantics shared by every backend: events are contiguous from seq 0
|
|
* and never rewritten; a torn physical tail is never returned to a reader and
|
|
* is truncated by the write path before its first append; reads validate
|
|
* current-format records only and refuse unknown vocabulary fail-closed.
|
|
* `append` persists best-effort; `flush` — per handle or service-wide — is
|
|
* the durability barrier.
|
|
*
|
|
* Visibility: a created session is observable through `stat`/`list`/`open`
|
|
* in this process from the moment `create` resolves, even while a backend
|
|
* defers physical materialization (a pure optimization); other processes see
|
|
* the session only once it materializes, and a session that never
|
|
* materialized before a crash never existed. `SessionHandle.flush` forces
|
|
* materialization.
|
|
*
|
|
* Freshness: once an `append` or `flush` resolves, reads started afterwards
|
|
* on this backend instance observe at least that prefix.
|
|
*/
|
|
export abstract class SessionPersistence extends Service {
|
|
constructor(ctx: Context) {
|
|
super(ctx, 'sessionPersistence')
|
|
}
|
|
|
|
/**
|
|
* Create a new stored session and take its write ownership.
|
|
* @param header - the immutable header (id, version, cwd, lineage) to store.
|
|
* @param options - optional cancellation.
|
|
* @returns a `write` handle owned by the caller; close it to release ownership.
|
|
* @throws {SessionAlreadyExistsError} when the id already exists.
|
|
*/
|
|
abstract create(header: SessionHeader, options?: SessionPersistenceCreateOptions): Promise<SessionHandle>
|
|
|
|
/**
|
|
* Open an existing stored session.
|
|
*
|
|
* `read` never takes ownership and works while another handle (or process)
|
|
* holds write ownership. `write` atomically claims single-writer ownership;
|
|
* an existing active owner rejects.
|
|
* @param id - the stored session to open.
|
|
* @param access - `read` or `write`.
|
|
* @param options - optional cancellation.
|
|
* @returns the open handle.
|
|
* @throws {SessionPersistenceNotFoundError} when the session does not exist.
|
|
* @throws {SessionAlreadyOwnedError} for `write` when ownership is taken.
|
|
*/
|
|
abstract open(id: SessionId, access: SessionAccess, options?: SessionPersistenceOpenOptions): Promise<SessionHandle>
|
|
|
|
/**
|
|
* Flush every active write handle owned by this service instance in one
|
|
* durability barrier: each handle's routed live events drain durably and
|
|
* its session materializes, exactly as that handle's own
|
|
* `SessionHandle.flush` would. Read handles buffer nothing and are
|
|
* untouched. A handle closed concurrently counts as flushed — close itself
|
|
* drains durably.
|
|
* @returns resolution once every write handle active at the call has flushed.
|
|
* @throws {AggregateError} naming each session whose flush failed; the
|
|
* remaining handles still flush.
|
|
*/
|
|
abstract flush(): Promise<void>
|
|
|
|
/**
|
|
* Observe one stored session without reading its event log or taking
|
|
* ownership.
|
|
*
|
|
* The snapshot's `revision` is an opaque change token comparable only
|
|
* against revisions from the same service instance and session id: equal
|
|
* revisions may be treated as an unchanged log; unequal revisions promise
|
|
* nothing. Write-ownership churn does not change a revision. It exists for
|
|
* derived read-model caches keyed off `stat`/`list`; it plays no part in
|
|
* open, read, or resume.
|
|
* @param id - the stored session to observe.
|
|
* @param options - optional cancellation.
|
|
* @returns the snapshot, or `undefined` when the session does not exist.
|
|
*/
|
|
abstract stat(id: SessionId, options?: SessionPersistenceStatOptions): Promise<SessionPersistenceSnapshot | undefined>
|
|
|
|
/**
|
|
* List every stored session visible to this process, in no promised order.
|
|
* @param options - optional cancellation.
|
|
* @returns one snapshot per stored session.
|
|
*/
|
|
abstract list(options?: SessionPersistenceListOptions): Promise<readonly SessionPersistenceSnapshot[]>
|
|
}
|
|
|
|
export default SessionPersistence
|