feat(session-query): checkpoint build round 1

This commit is contained in:
Hypatia May
2026-07-10 16:51:19 +08:00
parent 42ebbfdf8f
commit aa1dc0e2c7
40 changed files with 3174 additions and 102 deletions
+17 -2
View File
@@ -20,8 +20,8 @@ export * from './types.ts'
export { isJsonValue } from './json.ts'
export type { JsonValue } from './json.ts'
export { interruptedTurnClosers } from './repair.ts'
export type { SurfaceNode } from './surface.ts'
export { isSurfaceEvent, isSurfaceEligibleType } from './surface.ts'
export type { SurfaceFoldReplacement, SurfaceFoldResult, SurfaceNode } from './surface.ts'
export { foldSurface, isSurfaceEvent, isSurfaceEligibleType } from './surface.ts'
export { isToolPairingBalanced } from './tool-pairing.ts'
export { applyHeaderDelta, canonicalHeader, diffHeader, foldRequestHeader, headerEquals } from './request-header.ts'
@@ -37,6 +37,14 @@ declare module 'cordis' {
* @mode emit
*/
'session/created'(session: Session): void
/**
* A session left the live store. The header is snapshotted after the store
* entry is removed; listener failures are contained and cannot break the
* owning fiber's teardown.
* @param header - immutable identity and lineage of the removed session.
* @mode parallel
*/
'session/removed'(header: SessionHeader): Promise<void> | void
/**
* An event was appended to a session log (sync, fire-and-forget). This is
* the per-append feed a UI or invariant plugin tails.
@@ -501,8 +509,15 @@ export class SessionStore extends Service {
session.onAppend = (event) => { this.ctx.emit('session/event', session, event) }
this.store.set(session.id, session)
return () => {
if (this.store.get(session.id) !== session) return
session.onAppend = undefined
this.store.delete(session.id)
const header = structuredClone(session.header)
void Promise.resolve()
.then(() => this.ctx.parallel('session/removed', header))
.catch((error: unknown) => {
this.ctx.logger.warn(`session store: session/removed listener failed for "${session.id}": ${String(error)}`)
})
}
}