@@ -32,79 +32,153 @@ import { streamSessionTranscriptLines } from "./transcript-stream.js";
3232import { resolveSessionTranscriptFile } from "./transcript.js" ;
3333import type { SessionEntry } from "./types.js" ;
3434
35+ /**
36+ * Identifies a session entry within one agent's session store.
37+ *
38+ * `sessionKey` is the caller-facing routing key. Accessors may normalize aliases
39+ * when reading or writing unless the exact-key variant is used. `agentId` and
40+ * `storePath` are explicit owner hints for callers that already know the target
41+ * store; otherwise the owner is derived from the session key and runtime config.
42+ */
3543export type SessionAccessScope = {
44+ /** Agent that owns the session store, when the caller already knows it. */
3645 agentId ?: string ;
46+ /** Return the stored object by reference when false; default reads are cloned. */
3747 clone ?: boolean ;
48+ /** Environment used only while resolving the configured session store path. */
3849 env ?: NodeJS . ProcessEnv ;
50+ /** Disable skill prompt hydration for callers that need raw stored fields. */
3951 hydrateSkillPromptRefs ?: boolean ;
52+ /** Session routing key requested by the caller. */
4053 sessionKey : string ;
54+ /** Explicit store path for scoped tools, tests, or already-resolved callers. */
4155 storePath ?: string ;
4256} ;
4357
58+ /**
59+ * Identifies transcript content for read-only access.
60+ *
61+ * A read can target a named transcript artifact directly with `sessionFile`, or
62+ * resolve the artifact from `sessionId` plus store/agent/thread identity. Reads
63+ * do not require `sessionKey` because historical projections often already have
64+ * the persisted session id but not the original routing key.
65+ */
4466export type SessionTranscriptReadScope = Omit < SessionAccessScope , "sessionKey" > & {
67+ /** Explicit transcript artifact path for compatibility and import/export callers. */
4568 sessionFile ?: string ;
69+ /** Stable session identifier stored in the session entry and transcript header. */
4670 sessionId : string ;
71+ /** Routing key when available; used to preserve scoped agent/thread semantics. */
4772 sessionKey ?: string ;
73+ /** Optional thread discriminator for thread-scoped transcript artifacts. */
4874 threadId ?: string | number ;
4975} ;
5076
77+ /**
78+ * Identifies transcript content for operations that must also know the session key.
79+ */
5180export type SessionTranscriptAccessScope = SessionTranscriptReadScope & {
5281 sessionKey : string ;
5382} ;
5483
84+ /**
85+ * Identifies transcript content for append/update operations.
86+ *
87+ * Writers may create or persist the transcript target, so they require
88+ * `sessionKey` and either an existing `sessionId` or enough entry context for the
89+ * caller to supply one later. Direct `sessionFile` remains an explicit artifact
90+ * target, not the normal identity for runtime callers.
91+ */
5592export type SessionTranscriptWriteScope = Omit < SessionTranscriptAccessScope , "sessionId" > & {
5693 sessionId ?: string ;
5794} ;
5895
96+ /** One listed session entry paired with the routing key used to find it. */
5997export type SessionEntrySummary = {
6098 sessionKey : string ;
6199 entry : SessionEntry ;
62100} ;
63101
64- /** Session entry read by the exact persisted session key, without alias resolution. */
102+ /**
103+ * Session entry read by exact persisted key.
104+ *
105+ * Use this when the caller must not cross aliases or normalized account/session
106+ * keys, such as approval routing and account binding decisions.
107+ */
65108export type ExactSessionEntry = {
66109 sessionKey : string ;
67110 entry : SessionEntry ;
68111} ;
69112
113+ /** Raw transcript record as stored by the transcript append pipeline. */
70114export type TranscriptEvent = unknown ;
71115
116+ /** Options for appending one projected message to a transcript. */
72117export type TranscriptMessageAppendOptions < TMessage > = {
118+ /** Runtime config used by message normalization and transcript append helpers. */
73119 config ?: OpenClawConfig ;
120+ /** Working directory for resolving relative media paths in appended messages. */
74121 cwd ?: string ;
122+ /** Idempotency strategy for callers that already performed duplicate checks. */
75123 idempotencyLookup ?: "scan" | "caller-checked" ;
124+ /** Message payload to append after optional idempotency preparation. */
76125 message : TMessage ;
126+ /** Timestamp override for deterministic tests or replayed events. */
77127 now ?: number ;
128+ /** Last chance to transform or drop a message after idempotency checks pass. */
78129 prepareMessageAfterIdempotencyCheck ?: ( message : TMessage ) => TMessage | undefined ;
130+ /** Preserve raw message bytes when the existing transcript is linear. */
79131 useRawWhenLinear ?: boolean ;
80132} ;
81133
134+ /** Result returned when a message append either writes or reuses an idempotent hit. */
82135export type TranscriptMessageAppendResult < TMessage > = {
83136 appended : boolean ;
84137 message : TMessage ;
85138 messageId : string ;
86139} ;
87140
141+ /**
142+ * Transcript update payload supplied by callers.
143+ *
144+ * The accessor resolves the concrete transcript artifact before publishing, so
145+ * callers provide session identity fields and optional update metadata only.
146+ */
88147export type TranscriptUpdatePayload = Omit < SessionTranscriptUpdate , "sessionFile" > ;
89148
149+ /** Options for updating an existing entry without creating a fallback entry. */
90150export type SessionEntryUpdateOptions = {
151+ /** Skip pruning or other maintenance work owned by the backing store. */
91152 skipMaintenance ?: boolean ;
153+ /** Let the update take ownership of any store cache invalidation. */
92154 takeCacheOwnership ?: boolean ;
93155} ;
94156
157+ /** Options for patching or creating one session entry. */
95158export type SessionEntryPatchOptions = {
159+ /** Entry to create when the target key does not currently exist. */
96160 fallbackEntry ?: SessionEntry ;
161+ /** Keep the existing activity timestamp unless the patch changes it explicitly. */
97162 preserveActivity ?: boolean ;
163+ /** Replace the entry with the update result instead of merging fields. */
98164 replaceEntry ?: boolean ;
99165} ;
100166
167+ /** Existing entry snapshot supplied to patch callbacks. */
101168export type SessionEntryPatchContext = {
102169 existingEntry ?: SessionEntry ;
103170} ;
104171
105172export type { SessionLifecycleArtifactCleanupParams , SessionLifecycleArtifactCleanupResult } ;
106173
107- /** Loads one session entry through the storage-neutral accessor seam. */
174+ /**
175+ * Loads one session entry.
176+ *
177+ * The returned entry follows normal session-key aliasing rules. Pass
178+ * `clone: false` only for tightly scoped callers that intentionally mutate or
179+ * compare the live store object; most callers should use the default cloned
180+ * value.
181+ */
108182export function loadSessionEntry ( scope : SessionAccessScope ) : SessionEntry | undefined {
109183 if ( scope . clone === false ) {
110184 const store = loadSessionStore ( resolveAccessStorePath ( scope ) , {
@@ -133,7 +207,12 @@ export function loadExactSessionEntry(scope: SessionAccessScope): ExactSessionEn
133207 return entry ? { sessionKey, entry } : undefined ;
134208}
135209
136- /** Lists session entries through the storage-neutral accessor seam. */
210+ /**
211+ * Lists all session entries visible in the resolved store.
212+ *
213+ * Each result includes the persisted routing key so callers can preserve the
214+ * exact key in UI projections, maintenance tasks, and export surfaces.
215+ */
137216export function listSessionEntries (
138217 scope : Partial < Omit < SessionAccessScope , "sessionKey" > > = { } ,
139218) : SessionEntrySummary [ ] {
@@ -148,7 +227,9 @@ export function listSessionEntries(
148227 return listFileSessionEntries ( scope ) ;
149228}
150229
151- /** Reads a session activity timestamp through the storage-neutral accessor seam. */
230+ /**
231+ * Reads the stored session activity timestamp without requiring a full entry projection.
232+ */
152233export function readSessionUpdatedAt ( scope : SessionAccessScope ) : number | undefined {
153234 if ( scope . storePath ) {
154235 return readFileSessionUpdatedAt ( {
@@ -159,7 +240,12 @@ export function readSessionUpdatedAt(scope: SessionAccessScope): number | undefi
159240 return loadSessionEntry ( scope ) ?. updatedAt ;
160241}
161242
162- /** Applies a partial entry update through the storage-neutral accessor seam. */
243+ /**
244+ * Creates or patches one session entry by merging a partial update.
245+ *
246+ * Missing entries are created from the supplied patch, including a generated
247+ * `sessionId` and current `updatedAt` when the patch does not provide them.
248+ */
163249export async function upsertSessionEntry (
164250 scope : SessionAccessScope ,
165251 patch : Partial < SessionEntry > ,
@@ -171,7 +257,9 @@ export async function upsertSessionEntry(
171257 } ) ;
172258}
173259
174- /** Replaces one entry through the storage-neutral accessor seam. */
260+ /**
261+ * Creates or replaces one session entry with the supplied entry shape.
262+ */
175263export async function replaceSessionEntry (
176264 scope : SessionAccessScope ,
177265 entry : SessionEntry ,
@@ -184,7 +272,14 @@ export async function replaceSessionEntry(
184272 } ) ;
185273}
186274
187- /** Patches one entry atomically through the storage-neutral accessor seam. */
275+ /**
276+ * Applies a callback-driven patch to one session entry.
277+ *
278+ * The callback receives the current entry and may return a partial update, a
279+ * replacement entry when `replaceEntry` is true, or `null` to leave the entry
280+ * unchanged. Store-level locking and timestamp handling are owned by the
281+ * backing implementation.
282+ */
188283export async function patchSessionEntry (
189284 scope : SessionAccessScope ,
190285 update : (
@@ -202,7 +297,13 @@ export async function patchSessionEntry(
202297 } ) ;
203298}
204299
205- /** Updates an existing session entry through the storage-neutral accessor seam. */
300+ /**
301+ * Updates an existing session entry without creating a fallback entry.
302+ *
303+ * Use this when absence is meaningful and the caller should not materialize a
304+ * new session row. The update callback returns `null` to leave the entry
305+ * unchanged.
306+ */
206307export async function updateSessionEntry (
207308 scope : SessionAccessScope ,
208309 update : (
@@ -219,14 +320,27 @@ export async function updateSessionEntry(
219320 } ) ;
220321}
221322
222- /** Cleans scoped session lifecycle entries and transcript artifacts through the accessor seam. */
323+ /**
324+ * Removes lifecycle-owned session records and transcript artifacts for one scoped target.
325+ *
326+ * This operation is for owner-managed cleanup such as reset, archive, delete,
327+ * or temporary run/session reclamation. It must stay scoped to the requested
328+ * session identity and report what was removed so callers can make cleanup
329+ * idempotent.
330+ */
223331export async function cleanupSessionLifecycleArtifacts (
224332 params : SessionLifecycleArtifactCleanupParams ,
225333) : Promise < SessionLifecycleArtifactCleanupResult > {
226334 return await cleanupFileSessionLifecycleArtifacts ( params ) ;
227335}
228336
229- /** Loads raw transcript events through the storage-neutral accessor seam. */
337+ /**
338+ * Loads raw transcript events for a resolved transcript target.
339+ *
340+ * This is intentionally an event-level API: callers that need projected chat
341+ * messages should use the transcript reader helpers that preserve projection,
342+ * bounding, and visibility rules.
343+ */
230344export async function loadTranscriptEvents (
231345 scope : SessionTranscriptReadScope ,
232346) : Promise < TranscriptEvent [ ] > {
@@ -238,7 +352,12 @@ export async function loadTranscriptEvents(
238352 return events ;
239353}
240354
241- /** Appends one raw transcript event through the storage-neutral accessor seam. */
355+ /**
356+ * Appends one raw transcript event to the resolved transcript target.
357+ *
358+ * Prefer `appendTranscriptMessage` for normal assistant/user message writes so
359+ * idempotency and message normalization remain centralized.
360+ */
242361export async function appendTranscriptEvent (
243362 scope : SessionTranscriptAccessScope ,
244363 event : TranscriptEvent ,
@@ -250,7 +369,14 @@ export async function appendTranscriptEvent(
250369 } ) ;
251370}
252371
253- /** Appends one transcript message through the storage-neutral writer seam. */
372+ /**
373+ * Appends one projected transcript message to the resolved transcript target.
374+ *
375+ * The overload with `prepareMessageAfterIdempotencyCheck` may return
376+ * `undefined` when the preparation callback drops the message. Without that
377+ * callback, successful calls always return the appended or idempotently reused
378+ * message result.
379+ */
254380export async function appendTranscriptMessage < TMessage > (
255381 scope : SessionTranscriptWriteScope ,
256382 options : TranscriptMessageAppendOptions < TMessage > & {
@@ -283,7 +409,13 @@ export async function appendTranscriptMessage<TMessage>(
283409 } ) ;
284410}
285411
286- /** Publishes a transcript update after resolving the current storage target. */
412+ /**
413+ * Publishes a transcript update for subscribers of the resolved transcript target.
414+ *
415+ * Callers provide storage-neutral identity and update metadata. The accessor
416+ * resolves the concrete transcript artifact needed by current subscribers and
417+ * includes it in the emitted event.
418+ */
287419export async function publishTranscriptUpdate (
288420 scope : SessionTranscriptWriteScope ,
289421 update : TranscriptUpdatePayload = { } ,
0 commit comments