71 lines
3.1 KiB
TypeScript
71 lines
3.1 KiB
TypeScript
/**
|
|
* Lineage: the project's conversation trees. Roots keep a tally of what happened, branches fork from them for one
|
|
* feature or fix, and notes carry news between conversations without waking them.
|
|
*/
|
|
import type { Context } from '@earendil-works/chord'
|
|
import { defineDoc, ROOT_CONVERSATION_ID } from '@earendil-works/pi-durable'
|
|
import { defineApi } from '../messages/compat.ts'
|
|
import { RootsDoc } from './refs.ts'
|
|
import type { MemoryItem } from './refs.ts'
|
|
import type { BranchInput, leaveNote, rootOf, startBranch } from './server.ts'
|
|
|
|
/** The project's roots, each the top of one conversation tree; a fresh project's first conversation is its first. */
|
|
export const Roots = defineDoc<{ roots: number[] }>({
|
|
kind: RootsDoc.kind,
|
|
checkpointWhen: () => false,
|
|
version: 1,
|
|
scope: 'conversation',
|
|
history: 'latest',
|
|
fork: 'initial',
|
|
initial: () => ({ roots: [ROOT_CONVERSATION_ID] }),
|
|
})
|
|
|
|
/** `brief`: the branch's context starts at its project's memory brief instead of the full tally. */
|
|
export type BranchState = { from: number; at: number; intent: string; brief?: true }
|
|
/**
|
|
* A branch's origin and prompt, written in its forking commit; absent on roots and on conversations made otherwise.
|
|
* Read it to tell a lineage branch from other conversations, before its first run.
|
|
*/
|
|
export const Branch = defineDoc<{ branch?: BranchState }>({
|
|
kind: 'lineage.branch',
|
|
checkpointWhen: () => false,
|
|
version: 1,
|
|
scope: 'conversation',
|
|
history: 'latest',
|
|
fork: 'initial',
|
|
initial: () => ({}),
|
|
})
|
|
|
|
export type LineageService = {
|
|
/** Start a branch from conversation `input.from` with its prompt as first input; returns the new conversation id. */
|
|
branch: (input: BranchInput, ctx: Context) => ReturnType<typeof startBranch>
|
|
/** The root of the tree conversation `id` belongs to, or undefined outside any tree. */
|
|
rootOf: (id: number, ctx: Context) => ReturnType<typeof rootOf>
|
|
/** Leave `text` in conversation `to` (or the root of the source's tree) for its next turn, without waking it. */
|
|
leaveNote: (
|
|
to: number | 'root',
|
|
text: string,
|
|
source?: Parameters<typeof leaveNote>[3],
|
|
) => ReturnType<typeof leaveNote>
|
|
/** Apply memory `ops` to root `root` in one atomic batch, written at `now`; returns what changed. */
|
|
remember: (root: number, ops: readonly unknown[], ctx: Context, now?: number) => Promise<string>
|
|
/**
|
|
* The memory brief a branch starts from (memory index and recent landings) of conversation `id`'s tree root, else
|
|
* of the project's only root; undefined without either or while it has no memories.
|
|
*/
|
|
memoryBrief: (
|
|
id: number,
|
|
ctx: Context,
|
|
) => Promise<{ root: number; text: string; memories: number } | undefined>
|
|
/** The memories of conversation `id`'s tree root, else of the project's only root; undefined without either. */
|
|
memoriesOf: (
|
|
id: number,
|
|
ctx: Context,
|
|
) => Promise<{ root: number; memories: Record<string, MemoryItem> } | undefined>
|
|
}
|
|
export { Branched, Rooted } from './refs.ts'
|
|
|
|
/** Trees, branches and notes; core, so `need` it. */
|
|
export const LineageApi = defineApi<LineageService>()('lineage')
|
|
export const tier = 'core'
|