docs(session-title): record contracts and generated catalogs

This commit is contained in:
Tianyi Cui
2026-07-21 01:54:00 +08:00
parent f71f96d292
commit 77df80359a
32 changed files with 783 additions and 47 deletions

View File

@@ -343,6 +343,10 @@ export const SERVICE_API: readonly ServiceApiEntry[] = [
signature: 'listSessions(): Promise<SessionRecord[]>',
jsDoc: '/**\n * List the complete logical corpus using live-preferred records.\n * @returns deterministic newest-first cloned session records.\n */',
},
{
signature: 'async readTitle(sessionId: SessionId): Promise<SessionTitleSnapshot | undefined>',
jsDoc: '/**\n * Fold the latest log-backed title from one live-preferred logical session.\n * @param sessionId - live or persisted session id to read.\n * @returns latest title snapshot, or `undefined` when the log has no title event.\n */',
},
{
signature: 'async listEvents(sessionId: SessionId): Promise<SessionEventRecord[]>',
jsDoc: '/**\n * List lightweight raw-log event records for one logical session.\n * @param sessionId - live-preferred session id to read.\n * @returns event records in ascending seq order.\n */',
@@ -385,6 +389,10 @@ export const SERVICE_API: readonly ServiceApiEntry[] = [
signature: 'async flush(session: Session): Promise<void>',
jsDoc: '/**\n * Dispatch the awaited `session/flush` durability checkpoint for `session`,\n * with the carrier captured at {@link enter}. THE flush entry point: the\n * store owns the carrier, so callers (the loop\'s turn-end checkpoint, idle\n * injection, teardown drains) must come through here rather than dispatch a\n * raw `ctx.parallel(\'session/flush\', …)` — one owner, one spelling, and the\n * scoped-dispatch invariant can pin it.\n * @param session - the session whose buffered events must reach durable storage.\n * @returns resolves when every flush listener has settled; after all settle,\n * rejects with the first registered listener failure if any listener failed.\n */',
},
{
signature: 'async appendOutOfBand<T extends OutOfBandSessionEventType>( session: Session, type: T, data: SessionEventMap[T], trigger: TurnTrigger, ): Promise<SessionEvent<T>>',
jsDoc: '/**\n * Append one plugin-declared log-only event without borrowing the agent\n * loop\'s lifecycle. An open turn receives the event directly and remains\n * responsible for its ordinary checkpoint. A closed log receives one\n * zero-step turn around the event, followed by an awaited flush.\n *\n * Once the synthetic `turn/start` commits, this method always attempts its\n * matching `turn/end` and flush, including when the target append fails.\n * Detachment requested by an event or flush listener is deferred until that\n * sequence settles, so publication cannot switch from a live scoped session\n * to an unobserved bare `Session` halfway through the update.\n *\n * @param session - exact live session that owns the target log.\n * @param type - event type opted into {@link OutOfBandSessionEventMap} by its owner.\n * @param data - typed JSON payload for the target event.\n * @param trigger - plugin-owned turn trigger used only when the log is closed.\n * @returns the accepted target event with its assigned sequence and timestamp.\n * @throws when the session is detached, another out-of-band append is active,\n * event acceptance fails, the synthetic turn cannot close, or flushing fails.\n */',
},
{
signature: 'get(id: SessionId): Session | undefined',
jsDoc: '/**\n * Look up a live session.\n * @param id - the session id to look up.\n * @returns the session, or undefined when no live session has that id.\n */',
@@ -399,6 +407,24 @@ export const SERVICE_API: readonly ServiceApiEntry[] = [
},
],
},
{
key: 'sessionTitle',
summary: 'Log-backed title fold plus asynchronous fallback generation.',
methods: [
{
signature: 'get(session: Session): SessionTitleSnapshot | undefined',
jsDoc: '/**\n * Read the latest folded title from one live or replayed session.\n * @param session - session whose log is the title source of truth.\n * @returns latest title snapshot, or `undefined` before eligible input.\n */',
},
{
signature: 'async refresh(session: Session, signal?: AbortSignal): Promise<SessionTitleSnapshot | undefined>',
jsDoc: '/**\n * Explicitly retry the registered provider, or materialize the built-in\n * fallback when no provider is registered.\n * @param session - exact live session to refresh.\n * @param signal - optional caller cancellation.\n * @returns latest accepted title, or `undefined` when no eligible text exists.\n */',
},
{
signature: 'register(provider: SessionTitleProvider): () => void',
jsDoc: '/**\n * Register the sole optional title provider. Disposal aborts its pending and\n * active work before another provider may register.\n * @param provider - provider identity, cadence, and generation function.\n * @returns exact Cordis effect disposer for HMR-safe unregistration.\n */',
},
],
},
{
key: 'skills',
summary: 'Registry of skill providers.',
@@ -1227,6 +1253,14 @@ export const TYPE_API: readonly TypeApiEntry[] = [
name: 'MessageSourceMap',
declaration: 'export interface MessageSourceMap {\n user: {\n kind: \'user\';\n };\n plugin: {\n kind: \'plugin\';\n plugin: string;\n };\n}',
},
{
name: 'OutOfBandSessionEventMap',
declaration: 'export interface OutOfBandSessionEventMap {\n}',
},
{
name: 'OutOfBandSessionEventType',
declaration: 'export type OutOfBandSessionEventType = Exclude<Extract<SessionEventType, keyof OutOfBandSessionEventMap>, SurfaceEventType>;',
},
{
name: 'PresetOption',
declaration: 'export interface PresetOption {\n value: string;\n name: string;\n description?: string;\n}',
@@ -1351,6 +1385,46 @@ export const TYPE_API: readonly TypeApiEntry[] = [
name: 'SessionRecord',
declaration: 'export interface SessionRecord {\n header: SessionHeader;\n live: boolean;\n persisted: boolean;\n}',
},
{
name: 'SessionTitleAutomaticMode',
declaration: 'export type SessionTitleAutomaticMode = \'first-message\' | \'all-user-messages\';',
},
{
name: 'SessionTitleEventData',
declaration: 'export interface SessionTitleEventData {\n readonly title: string;\n readonly messageSeqs: number[];\n readonly source: SessionTitleSource;\n}',
},
{
name: 'SessionTitleModelProvenance',
declaration: 'export interface SessionTitleModelProvenance {\n readonly provider: string;\n readonly model: string;\n}',
},
{
name: 'SessionTitleProvider',
declaration: 'export interface SessionTitleProvider {\n readonly id: SessionTitleProviderId;\n readonly automatic: SessionTitleAutomaticMode;\n generate(request: SessionTitleProviderRequest): Promise<SessionTitleProviderResult>;\n}',
},
{
name: 'SessionTitleProviderId',
declaration: 'export type SessionTitleProviderId = Branded<\'SessionTitleProviderId\'>;',
},
{
name: 'SessionTitleProviderRequest',
declaration: 'export interface SessionTitleProviderRequest {\n readonly session: Session;\n readonly messages: readonly SessionTitleUserMessage[];\n readonly route?: SessionTitleModelProvenance;\n readonly signal: AbortSignal;\n}',
},
{
name: 'SessionTitleProviderResult',
declaration: 'export interface SessionTitleProviderResult {\n readonly title: string;\n readonly messageSeqs: readonly number[];\n readonly model?: SessionTitleModelProvenance;\n}',
},
{
name: 'SessionTitleSnapshot',
declaration: 'export interface SessionTitleSnapshot extends SessionTitleEventData {\n readonly eventSeq: number;\n readonly updatedAt: number;\n}',
},
{
name: 'SessionTitleSource',
declaration: 'export type SessionTitleSource = {\n readonly kind: \'fallback\';\n} | {\n readonly kind: \'provider\';\n readonly provider: SessionTitleProviderId;\n readonly model?: SessionTitleModelProvenance;\n};',
},
{
name: 'SessionTitleUserMessage',
declaration: 'export interface SessionTitleUserMessage {\n readonly seq: number;\n readonly text: string;\n}',
},
{
name: 'SkillCandidate',
declaration: 'export interface SkillCandidate extends SkillSummary {\n readonly rank: number;\n readonly locator: unknown;\n readonly path?: string;\n readonly metadata?: Readonly<Record<string, unknown>>;\n}',