275 lines
10 KiB
TypeScript
275 lines
10 KiB
TypeScript
import type { SessionEvent } from '@deepseek-ai/dsh-session/types'
|
|
import type { ToolEventView } from '@deepseek-ai/dsh-client-connection/client'
|
|
|
|
/* oxlint-disable typescript/no-duplicate-type-constituents, typescript/no-redundant-type-constituents --
|
|
* The unaugmented declaration-merge maps intentionally resolve to never in the Runtime program;
|
|
* installed business packages supply their concrete keys in consuming Client programs. */
|
|
|
|
/** One raw log event plus its optional envelope-level presentation view. */
|
|
export interface ConversationEventInput {
|
|
readonly event: SessionEvent
|
|
readonly view: ToolEventView | undefined
|
|
}
|
|
|
|
/** Definition-local identity and lifecycle role extracted from one event. */
|
|
export interface ConversationMatchResult {
|
|
readonly id: string
|
|
readonly role: 'start' | 'update'
|
|
}
|
|
|
|
/** Merge-extensible business values published against one Turn. */
|
|
export interface ConversationTurnDataMap {}
|
|
|
|
/** Merge-extensible business values published against one Step. */
|
|
export interface ConversationStepDataMap {}
|
|
|
|
/** Stable keyed reader for independently owned Location business values. */
|
|
export interface ConversationLocationDataStore<DataMap extends object> {
|
|
/**
|
|
* Read one business value without exposing another owner's mutable State.
|
|
* @param key - declaration-merged business key.
|
|
* @returns latest immutable value, when its owning Context has published one.
|
|
*/
|
|
get<Key extends keyof DataMap & string>(key: Key): Readonly<DataMap[Key]> | undefined
|
|
}
|
|
|
|
interface ConversationLocationDataValue {
|
|
readonly kind: 'turn' | 'step'
|
|
readonly turn: number
|
|
readonly step?: number
|
|
readonly key: string
|
|
readonly value: unknown
|
|
}
|
|
|
|
type RegisteredTurnData = {
|
|
[Key in keyof ConversationTurnDataMap & string]: {
|
|
readonly kind: 'turn'
|
|
readonly turn: number
|
|
readonly key: Key
|
|
readonly value: ConversationTurnDataMap[Key]
|
|
}
|
|
}[keyof ConversationTurnDataMap & string]
|
|
|
|
type RegisteredStepData = {
|
|
[Key in keyof ConversationStepDataMap & string]: {
|
|
readonly kind: 'step'
|
|
readonly turn: number
|
|
readonly step: number
|
|
readonly key: Key
|
|
readonly value: ConversationStepDataMap[Key]
|
|
}
|
|
}[keyof ConversationStepDataMap & string]
|
|
|
|
/** One Definition-owned value attached to an Engine-owned Turn or Step. */
|
|
export type ConversationLocationData =
|
|
[keyof ConversationTurnDataMap | keyof ConversationStepDataMap] extends [never]
|
|
? ConversationLocationDataValue
|
|
: RegisteredTurnData | RegisteredStepData
|
|
|
|
/** Immutable resolved boundary for one Agent step. */
|
|
export interface StepLocation {
|
|
readonly turn: number
|
|
readonly step: number
|
|
readonly start: SessionEvent<'step/start'> | undefined
|
|
readonly end: SessionEvent<'step/end'> | undefined
|
|
readonly status: 'open' | 'closed' | 'unknown'
|
|
/** Stable reader for Step-scoped business values. */
|
|
readonly data: ConversationLocationDataStore<ConversationStepDataMap>
|
|
}
|
|
|
|
/** Immutable resolved boundary for one Agent turn. */
|
|
export interface TurnLocation {
|
|
readonly turn: number
|
|
readonly start: SessionEvent<'turn/start'> | undefined
|
|
readonly end: SessionEvent<'turn/end'> | undefined
|
|
readonly status: 'open' | 'closed' | 'unknown'
|
|
readonly steps: readonly StepLocation[]
|
|
/** Stable reader for Turn-scoped business values. */
|
|
readonly data: ConversationLocationDataStore<ConversationTurnDataMap>
|
|
}
|
|
|
|
/** Engine-owned placement of one matched event in the Session hierarchy. */
|
|
export type ConversationLocation =
|
|
| { readonly kind: 'session' }
|
|
| { readonly kind: 'turn'; readonly turn: TurnLocation }
|
|
| { readonly kind: 'step'; readonly turn: TurnLocation; readonly step: StepLocation }
|
|
| { readonly kind: 'unresolved' }
|
|
|
|
/** One event accepted by a Definition, with its current resolved Location. */
|
|
export interface ConversationMatch extends ConversationEventInput {
|
|
readonly role: 'start' | 'update'
|
|
readonly location: ConversationLocation
|
|
}
|
|
|
|
/** Target-neutral identity returned by a business Definition. */
|
|
export interface ConversationViewNode {
|
|
readonly key: string
|
|
readonly kind: string
|
|
readonly id: string
|
|
readonly target: string
|
|
readonly data: unknown
|
|
}
|
|
|
|
/** Merge-extensible immutable snapshots published by registered view targets. */
|
|
export interface ConversationViewSnapshotMap {}
|
|
|
|
/** Stable reader over the latest snapshot of every registered view target. */
|
|
export interface ConversationViewSnapshotStore {
|
|
/** @param target - registered view target. @returns its current snapshot. */
|
|
get<Target extends Extract<keyof ConversationViewSnapshotMap, string>>(
|
|
target: Target,
|
|
): ConversationViewSnapshotMap[Target] | undefined
|
|
}
|
|
|
|
/** Final Chat render unit produced directly by a business Definition. */
|
|
export interface ChatConversationViewNode extends ConversationViewNode {
|
|
readonly target: 'chat'
|
|
readonly anchorSeq: number
|
|
readonly location: ConversationLocation
|
|
readonly visibility: 'visible' | 'hidden'
|
|
}
|
|
|
|
/** Immutable public view of an assembled business Context. */
|
|
export interface ConversationNodeContext<State = unknown> {
|
|
readonly key: string
|
|
readonly kind: string
|
|
readonly id: string
|
|
readonly matches: readonly ConversationMatch[]
|
|
readonly start: ConversationMatch | undefined
|
|
readonly state: State | undefined
|
|
readonly current: ReadonlyMap<string, ConversationViewNode | null>
|
|
}
|
|
|
|
/** Read-only predecessor returned to a Definition's start function. */
|
|
export interface ConversationPreviousContext<State = unknown> {
|
|
readonly key: string
|
|
readonly kind: string
|
|
readonly id: string
|
|
readonly startSeq: number
|
|
readonly state: Readonly<State>
|
|
readonly matches: readonly ConversationMatch[]
|
|
}
|
|
|
|
/** Strictly-backward Context lookup available while a start is evaluated. */
|
|
export interface ConversationContextReader {
|
|
/**
|
|
* Find the active Context of `kind` with the greatest start seq below the
|
|
* current start event.
|
|
* @param kind - Definition kind to query.
|
|
* @returns the nearest predecessor, or undefined when absent in the current window.
|
|
*/
|
|
previous<State>(kind: string): ConversationPreviousContext<State> | undefined
|
|
}
|
|
|
|
/** Requested cadence for materializing updated business State into view Nodes. */
|
|
export type ConversationPublication = 'none' | 'animation-frame' | 'immediate'
|
|
|
|
/** Engine-owned Location data publication phase. */
|
|
export type ConversationLocationDataScope = 'step' | 'turn'
|
|
|
|
/** One independently registered business Event-to-Node state machine. */
|
|
export interface ConversationNodeDefinition<State = unknown> {
|
|
readonly kind: string
|
|
/** Sole view target owned by this Definition; omitted for state-only Contexts. */
|
|
readonly target?: string
|
|
/**
|
|
* Extract this Definition's stable business identity from one event.
|
|
* @param event - raw Session event; no Context or history access is available.
|
|
* @returns identity and lifecycle role, or null when unrelated.
|
|
*/
|
|
match(event: SessionEvent): ConversationMatchResult | null
|
|
/**
|
|
* Create State from the unique start Match.
|
|
* @param context - complete evidence currently collected for the Context.
|
|
* @param match - the start Match.
|
|
* @param reader - strictly-backward read-only Context lookup.
|
|
* @returns the State adopted by the engine.
|
|
*/
|
|
start(
|
|
context: ConversationNodeContext<State>,
|
|
match: ConversationMatch,
|
|
reader: ConversationContextReader,
|
|
): State
|
|
/**
|
|
* Apply one post-start update Match.
|
|
* @param context - Context with its current State.
|
|
* @param match - update Match in ascending log order.
|
|
* @returns the State adopted by the engine.
|
|
*/
|
|
update(
|
|
context: ConversationNodeContext<State> & { readonly state: State },
|
|
match: ConversationMatch,
|
|
): State
|
|
/**
|
|
* Select publication cadence for one accepted Match.
|
|
* @param match - accepted Match.
|
|
* @returns requested cadence; omission defaults to immediate.
|
|
*/
|
|
publication?(match: ConversationMatch): ConversationPublication
|
|
/**
|
|
* Publish this Definition's read-only business value for one Location phase.
|
|
* The Engine evaluates every Definition first for Step and then for Turn,
|
|
* owns replacement/removal, and rejects another Context trying to publish
|
|
* the same Location key.
|
|
* @param context - latest complete Context.
|
|
* @param scope - Location hierarchy level currently being materialized.
|
|
* @returns current Location value, or null while unavailable.
|
|
*/
|
|
buildLocationData?(
|
|
context: ConversationNodeContext<State>,
|
|
scope: ConversationLocationDataScope,
|
|
): ConversationLocationData | null
|
|
/**
|
|
* Materialize one final Node for this Definition's declared view target.
|
|
* @param context - latest complete Context.
|
|
* @returns final Node, or null when this Context is not currently visible.
|
|
*/
|
|
buildViewNode?(context: ConversationNodeContext<State>): ConversationViewNode | null
|
|
}
|
|
|
|
/** Reference-stable Turn/Step facts published beside view Nodes. */
|
|
export interface ConversationTimelineSnapshot {
|
|
readonly turnOrder: readonly number[]
|
|
readonly turns: ReadonlyMap<number, TurnLocation>
|
|
}
|
|
|
|
/** Per-Session incremental builder for one view target. */
|
|
export interface ConversationViewBuilder<Node extends ConversationViewNode = ConversationViewNode, Snapshot = unknown> {
|
|
readonly empty: Snapshot
|
|
/**
|
|
* Replace the low-frequency complete materialized Node set.
|
|
* @param input - complete Nodes and current timeline.
|
|
* @returns next view snapshot.
|
|
*/
|
|
replace(input: {
|
|
readonly nodes: readonly Node[]
|
|
readonly timeline: ConversationTimelineSnapshot
|
|
}): Snapshot
|
|
/**
|
|
* Apply only Nodes whose materialized values changed in this transaction.
|
|
* @param input - changed Nodes and current timeline.
|
|
* @returns next view snapshot.
|
|
*/
|
|
apply(input: {
|
|
readonly upserts: readonly Node[]
|
|
readonly timeline: ConversationTimelineSnapshot
|
|
}): Snapshot
|
|
}
|
|
|
|
/** Registry contribution that creates one isolated view builder per Session. */
|
|
export interface ConversationViewDefinition<Node extends ConversationViewNode = ConversationViewNode, Snapshot = unknown> {
|
|
readonly target: string
|
|
/** @returns a new Session-owned incremental builder. */
|
|
create(): ConversationViewBuilder<Node, Snapshot>
|
|
}
|
|
|
|
/**
|
|
* Build a stable collision-free key for one Definition-local business identity.
|
|
* @param kind - Definition kind.
|
|
* @param id - Definition-local business identity.
|
|
* @returns engine-owned Context key.
|
|
*/
|
|
export function conversationContextKey(kind: string, id: string): string {
|
|
return `${kind.length}:${kind}${id}`
|
|
}
|