Repository Public
1/**2 * The provider-native transcript of a run, rebuilt on every claim.3 *4 * Replay rule (CONTRACT §5): the transcript is a pure function of journaled step RESULTS. The logic5 * re-runs from the top; each `ctx.step` returns its recorded result and the same appends are made in6 * the same order, so nothing else is state. Every value appended here comes from a step result (i.e.7 * went through the kernel's canonical JSON round-trip), from the run contract, or from constants.8 *9 * The transcript itself is never written to a step request (it would make the journal quadratic). A10 * model_call step carries a rolling digest instead: h0 = sha256(canonical(first entries)),11 * h(n) = sha256(h(n-1) + canonical(entries appended since)). The kernel's request-hash check therefore12 * still detects any divergence of the rebuilt transcript.13 */14import { canonicalJson } from '@aos/kernel';15import type { ProviderAdapter } from '@aos/providers';16import type { ToolResult } from '@aos/shared';17import { sha256Hex } from '@aos/tools';1819export class Transcript {20 private items: unknown[];21 private digest = '';2223 constructor(24 private readonly adapter: ProviderAdapter,25 firstUserMessage: string,26 ) {27 this.items = [];28 this.set(adapter.initTranscript(firstUserMessage));29 }3031 private set(next: unknown[]): void {32 const added = next.slice(this.items.length);33 this.items = next;34 if (added.length > 0) this.digest = sha256Hex(this.digest + canonicalJson(added));35 }3637 /** Native entries, to hand to `adapter.call`. Treat as read-only. */38 get entries(): unknown[] {39 return this.items;40 }41 get sha256(): string {42 return this.digest;43 }44 get length(): number {45 return this.items.length;46 }4748 assistant(native: unknown): void {49 this.set(this.adapter.appendAssistant(this.items, native));50 }51 toolResults(results: ToolResult[]): void {52 if (results.length > 0) this.set(this.adapter.appendToolResults(this.items, results));53 }54 /** Steering message from the owner. */55 owner(text: string): void {56 this.set(this.adapter.appendUser(this.items, `Message from the owner:\n${text}`));57 }58 /** Operator-authority note from the runtime (budget, context, stall, nudges). */59 note(text: string): void {60 this.set(this.adapter.appendOperatorNote(this.items, text));61 }62}63