@hasna-internal/kai-session-persistence 0.1.1-rc.2

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.
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Bounded sharing and exclusive reservation of unpublished Sessions.
3
+ * @module @hasna-internal/kai-session-persistence/preparations
4
+ */
5
+ import type { Session, SessionId } from '@hasna-internal/kai-session';
6
+ interface PreparedSource {
7
+ readonly session: Session;
8
+ }
9
+ type PreparationPhase = 'loading' | 'ready' | 'committing' | 'reserved';
10
+ interface PreparationEntry<Source, CommitState> {
11
+ readonly id: SessionId;
12
+ readonly result: Promise<Source>;
13
+ phase: PreparationPhase;
14
+ source?: Source;
15
+ reservation?: SessionPreparationReservation<Source, CommitState>;
16
+ reservationSettled?: Promise<void>;
17
+ settleReservation?: () => void;
18
+ }
19
+ /** One exclusively held prepared source and its committed persistence state. */
20
+ export interface SessionPreparationReservation<Source, CommitState> {
21
+ readonly entry: PreparationEntry<Source, CommitState>;
22
+ readonly source: Source;
23
+ readonly state: CommitState;
24
+ }
25
+ /** Per-coordinator cold-read sharing, exclusive reservation, and ready-entry LRU. */
26
+ export declare class SessionPreparations<Source extends PreparedSource, CommitState> {
27
+ private readonly capacity;
28
+ private readonly entries;
29
+ constructor(capacity: number);
30
+ /**
31
+ * Whether this pool currently knows about an unpublished identity.
32
+ * @param id - session identity.
33
+ * @returns whether an entry exists for the identity.
34
+ */
35
+ has(id: SessionId): boolean;
36
+ /**
37
+ * Observe one prepared source, sharing an in-flight read for the same id.
38
+ * @param id - session identity.
39
+ * @param load - cold loader used when no entry exists.
40
+ * @param signal - optional cancellation signal while waiting.
41
+ * @returns the shared prepared source.
42
+ */
43
+ inspect(id: SessionId, load: () => Promise<Source>, signal?: AbortSignal): Promise<Source>;
44
+ /**
45
+ * Reserve one ready source after committing its pending durable repair.
46
+ * @param id - session identity.
47
+ * @param load - cold loader used when no entry exists.
48
+ * @param commit - durable repair and cursor-state commit.
49
+ * @param signal - optional cancellation signal while waiting.
50
+ * @returns the exclusive reservation, or undefined if its entry was invalidated.
51
+ */
52
+ reserve(id: SessionId, load: () => Promise<Source>, commit: (source: Source) => Promise<{
53
+ source: Source;
54
+ state: CommitState;
55
+ } | undefined>, signal?: AbortSignal): Promise<SessionPreparationReservation<Source, CommitState> | undefined>;
56
+ /**
57
+ * Return the exact reservation for Session publication, rejecting aliases.
58
+ * @param session - exact Session candidate for publication.
59
+ * @returns its reservation, or undefined when no preparation exists.
60
+ */
61
+ reservationFor(session: Session): SessionPreparationReservation<Source, CommitState> | undefined;
62
+ /**
63
+ * Consume a reservation after its exact Session has attached.
64
+ * @param reservation - reservation to consume.
65
+ */
66
+ attach(reservation: SessionPreparationReservation<Source, CommitState>): void;
67
+ /**
68
+ * Consume a reservation whose caller only needs the committed inspection.
69
+ * @param reservation - reservation to consume.
70
+ */
71
+ discard(reservation: SessionPreparationReservation<Source, CommitState>): void;
72
+ /**
73
+ * Return a reusable unpublished reservation to the ready LRU.
74
+ * @param reservation - reservation to release.
75
+ * @param reusable - whether the source remains valid for reuse.
76
+ */
77
+ release(reservation: SessionPreparationReservation<Source, CommitState>, reusable: boolean): void;
78
+ /**
79
+ * Discard a prepared view after the durable log changes.
80
+ * @param id - changed session identity.
81
+ */
82
+ invalidate(id: SessionId): void;
83
+ /**
84
+ * Discard an exact stale ready source without disturbing an exclusive owner.
85
+ * @param id - changed session identity.
86
+ * @param expected - exact source observed before its revision check.
87
+ * @returns whether the source was discarded, retained by a reservation, or is absent.
88
+ */
89
+ discardReady(id: SessionId, expected: Source): 'discarded' | 'retained' | 'missing';
90
+ /**
91
+ * Reject writes while an unpublished Session exclusively reserves the id.
92
+ * @param id - session identity to check.
93
+ */
94
+ assertWritable(id: SessionId): void;
95
+ /**
96
+ * Remove a completed entry for an already-serialized append adoption.
97
+ * @param id - adopted session identity.
98
+ * @returns the prepared source, or undefined when no ready entry exists.
99
+ */
100
+ takeReady(id: SessionId): Source | undefined;
101
+ private entryFor;
102
+ private makeReady;
103
+ private remove;
104
+ private touch;
105
+ }
106
+ /**
107
+ * Give a queued observer a prompt cancellation view without cancelling shared work.
108
+ * @param operation - shared operation whose settlement remains authoritative.
109
+ * @param signal - observer-local cancellation signal.
110
+ * @param started - whether the operation has crossed its cancellation cutoff.
111
+ * @returns the operation result or the observer's prompt cancellation.
112
+ */
113
+ export declare function observeQueuedAbort<T>(operation: Promise<T>, signal: AbortSignal, started?: () => boolean): Promise<T>;
114
+ export {};
115
+ //# sourceMappingURL=preparations.d.ts.map
@@ -0,0 +1,14 @@
1
+ /** Opaque revision identity for lightweight persistence observations. */
2
+ import type { Branded } from '@hasna-internal/kai-brand';
3
+ /**
4
+ * Backend-owned token that identifies both one storage source and one revision
5
+ * of a persisted session log.
6
+ */
7
+ export type SessionPersistenceRevision = Branded<'SessionPersistenceRevision'>;
8
+ /**
9
+ * Brand a backend revision for the provider-neutral persistence contract.
10
+ * @param value - backend-owned opaque revision representation.
11
+ * @returns the same runtime string with persistence-revision identity.
12
+ */
13
+ export declare function SessionPersistenceRevision(value: string): SessionPersistenceRevision;
14
+ //# sourceMappingURL=revision.d.ts.map
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Bounded per-session write batching for the shared persistence coordinator.
3
+ * @module @hasna-internal/kai-session-persistence/write-behind
4
+ */
5
+ import type { SessionEvent } from '@hasna-internal/kai-session';
6
+ /** Dependencies and scheduling policy for one live session's write controller. */
7
+ export interface SessionWriteBehindOptions {
8
+ /** Maximum intentional batching wait after an idle queue receives work. */
9
+ readonly maxDelayMs: number;
10
+ /** Persist one stable ordered prefix; resolves only after backend durability. */
11
+ readonly write: (events: readonly SessionEvent[]) => Promise<void>;
12
+ /** Observe a detached background write failure without rejecting the producer. */
13
+ readonly reportBackgroundFailure: (error: unknown) => void;
14
+ }
15
+ /**
16
+ * Owns one live session's pending events, fixed batching deadline, active write,
17
+ * failure retention, and explicit quiescence barrier.
18
+ */
19
+ export declare class SessionWriteBehind {
20
+ private readonly options;
21
+ private pending;
22
+ private timer;
23
+ private active;
24
+ private barrier;
25
+ private deadlineExpired;
26
+ private automaticPaused;
27
+ /**
28
+ * @param options - fixed scheduling policy and durable batch sink.
29
+ */
30
+ constructor(options: SessionWriteBehindOptions);
31
+ /** Whether this controller owns queued events or an active durable write. */
32
+ get hasWork(): boolean;
33
+ /**
34
+ * Copy one event into the persistence-owned queue and start a fixed deadline
35
+ * when the automatic path is idle.
36
+ * @param event - frozen live event to retain independently of its producer.
37
+ */
38
+ enqueue(event: SessionEvent): void;
39
+ /**
40
+ * Cancel the batching wait and durably drain through a quiescent point.
41
+ * Concurrent callers join the same barrier.
42
+ * @returns a promise that rejects if the barrier's durable retry fails.
43
+ */
44
+ flush(): Promise<void>;
45
+ /** Cancel the current automatic deadline without draining retained work. */
46
+ cancelAutomaticWait(): void;
47
+ /** Start the one fixed window for the current pending prefix. */
48
+ private armTimer;
49
+ /** Cancel any pending automatic deadline. */
50
+ private cancelTimer;
51
+ /** Start a background write now, or remember that an active write used the budget. */
52
+ private onDeadline;
53
+ /** Start one detached write whose failure is reported and retained. */
54
+ private startBackground;
55
+ /** Continue immediately after an over-budget active write, otherwise keep its timer. */
56
+ private continueAutomatic;
57
+ /** Await overlapping work, drain to quiescence, and settle the shared barrier. */
58
+ private drainBarrier;
59
+ /** Start one stable pending prefix, retaining it in order if durability fails. */
60
+ private startWrite;
61
+ }
62
+ //# sourceMappingURL=write-behind.d.ts.map
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@hasna-internal/kai-session-persistence",
3
+ "description": "Abstract durable session persistence seam (ctx.sessionPersistence) for the DeepSeek Harness",
4
+ "version": "0.1.1-rc.2",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
11
+ "directory": "packages/session/session-persistence"
12
+ },
13
+ "type": "module",
14
+ "main": "lib/index.js",
15
+ "types": "lib/types/index.d.ts",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./lib/types/index.d.ts",
19
+ "default": "./lib/index.js"
20
+ },
21
+ "./invariant": {
22
+ "types": "./lib/types/invariant.d.ts",
23
+ "default": "./lib/invariant.js"
24
+ },
25
+ "./src/*": "./src/*",
26
+ "./package.json": "./package.json"
27
+ },
28
+ "files": [
29
+ "lib/index.js",
30
+ "lib/invariant.js",
31
+ "lib/types/**/*.d.ts"
32
+ ],
33
+ "license": "MIT",
34
+ "peerDependencies": {
35
+ "@hasna-internal/kai-invariants": "^0.1.1-rc.2",
36
+ "@hasna-internal/kai-session": "^0.1.1-rc.2",
37
+ "@hasna-internal/kai-brand": "^0.1.1-rc.2",
38
+ "@hasna-internal/kai-timeout": "^0.1.1-rc.2",
39
+ "@deepseek-ai/cordis": "^4.0.1"
40
+ },
41
+ "devDependencies": {
42
+ "@hasna-internal/kai-brand": "^0.1.1-rc.2",
43
+ "@hasna-internal/kai-session": "^0.1.1-rc.2",
44
+ "@hasna-internal/kai-invariants": "^0.1.1-rc.2",
45
+ "@hasna-internal/kai-scope": "^0.1.1-rc.2",
46
+ "@hasna-internal/kai-timeout": "^0.1.1-rc.2",
47
+ "@deepseek-ai/cordis": "^4.0.1"
48
+ }
49
+ }