feat(session-query): add relationship tracing (round 1)
This commit is contained in:
@@ -1,17 +1,19 @@
|
||||
/**
|
||||
* Exact session-history reads over live and optionally persisted logs.
|
||||
* Exact session-history reads and traces over live and optionally persisted logs.
|
||||
*
|
||||
* @module @deepseek-ai/dsh-session-query
|
||||
*/
|
||||
|
||||
import { Context, Service } from 'cordis'
|
||||
import z from 'schemastery'
|
||||
import { foldSurface } from '@deepseek-ai/dsh-session'
|
||||
import type { SessionEvent, SessionId } from '@deepseek-ai/dsh-session'
|
||||
import type { SessionId } from '@deepseek-ai/dsh-session'
|
||||
import type {
|
||||
SessionEventReadRequest,
|
||||
SessionEventRecord,
|
||||
SessionEventTrace,
|
||||
SessionEventTraceRequest,
|
||||
SessionEventWindow,
|
||||
SessionLineageTrace,
|
||||
SessionRecord,
|
||||
} from './types.ts'
|
||||
import {
|
||||
@@ -20,6 +22,7 @@ import {
|
||||
type Config,
|
||||
} from './config.ts'
|
||||
import { SessionCorpus } from './corpus.ts'
|
||||
import { eventRecords, traceEventLog, traceLineage } from './tracing.ts'
|
||||
|
||||
export type * from './types.ts'
|
||||
export type { Config, SessionQueryErrorCode } from './config.ts'
|
||||
@@ -31,7 +34,7 @@ declare module 'cordis' {
|
||||
}
|
||||
}
|
||||
|
||||
/** Live-preferred logical-corpus and exact-event read service. */
|
||||
/** Live-preferred logical-corpus exact-read and relationship-tracing service. */
|
||||
export class SessionQueryService extends Service {
|
||||
static inject = ['sessions']
|
||||
static Config: z<Config> = z.object({
|
||||
@@ -71,6 +74,26 @@ export class SessionQueryService extends Service {
|
||||
return eventRecords(sessionId, loaded.events)
|
||||
}
|
||||
|
||||
/**
|
||||
* Trace known ancestry and descendants from one corpus observation.
|
||||
* @param sessionId - logical session id to trace.
|
||||
* @returns a complete lineage or an explicit unresolved parent boundary.
|
||||
*/
|
||||
async traceSession(sessionId: SessionId): Promise<SessionLineageTrace> {
|
||||
const records = await this._corpus.listSessions()
|
||||
return traceLineage(records, sessionId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Trace one event's direct positional and provenance relationships.
|
||||
* @param request - target session id and event seq.
|
||||
* @returns direct links plus the target's positional replacement chain.
|
||||
*/
|
||||
async traceEvent(request: SessionEventTraceRequest): Promise<SessionEventTrace> {
|
||||
const loaded = await this._corpus.load(request.sessionId)
|
||||
return traceEventLog(request.sessionId, loaded.events, request.seq)
|
||||
}
|
||||
|
||||
/**
|
||||
* Read one full event plus a bounded raw-log context window.
|
||||
* @param request - target session/seq and context sizes.
|
||||
@@ -110,27 +133,4 @@ export class SessionQueryService extends Service {
|
||||
}
|
||||
}
|
||||
|
||||
function eventRecords(sessionId: SessionId, events: readonly SessionEvent[]): SessionEventRecord[] {
|
||||
let folded: ReturnType<typeof foldSurface>
|
||||
try {
|
||||
folded = foldSurface(events)
|
||||
} catch (error: unknown) {
|
||||
throw new SessionQueryError(
|
||||
/* v8 ignore next -- foldSurface throws Error instances */
|
||||
`invalid session surface: ${error instanceof Error ? error.message : 'unknown error'}`,
|
||||
'SESSION_QUERY_INVALID_SURFACE',
|
||||
{ cause: error },
|
||||
)
|
||||
}
|
||||
const current = new Set(folded.nodes.map(node => node.seq))
|
||||
const shadowed = new Set(folded.replacements.flatMap(replacement => replacement.shadowedSeqs))
|
||||
return events.map(event => ({
|
||||
sessionId,
|
||||
seq: event.seq,
|
||||
type: event.type,
|
||||
time: event.time,
|
||||
surface: current.has(event.seq) ? 'current' : shadowed.has(event.seq) ? 'shadowed' : 'log-only',
|
||||
}))
|
||||
}
|
||||
|
||||
export default SessionQueryService
|
||||
|
||||
Reference in New Issue
Block a user