File size: 1,980 Bytes
391c43e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | /**
* Request snapshot store — debug capture of the exact message history sent to
* the provider on the most recent LLM call.
*
* Deliberately separate from the debug event stream: capture is a single
* assignment per provider call (never per token), is session-only (never
* persisted), and is off by default. The Messages tab in the debug panel
* subscribes via useSyncExternalStore and re-renders only when a new request
* is captured.
*/
import type { Message } from './core/types';
export interface RequestSnapshot {
messages: Message[];
provider: string;
model: string;
timestamp: number;
}
type Listener = () => void;
class RequestSnapshotStore {
private enabled = false;
private snapshot: RequestSnapshot | null = null;
private listeners = new Set<Listener>();
isEnabled(): boolean {
return this.enabled;
}
setEnabled(enabled: boolean): void {
if (this.enabled === enabled) return;
this.enabled = enabled;
this.notify();
}
/** Capture the latest outgoing request. No-op when disabled. */
capture(req: { messages: Message[]; provider: string; model: string }): void {
if (!this.enabled) return;
this.snapshot = {
// Deep copy — the live message objects are mutated by later turns
messages: JSON.parse(JSON.stringify(req.messages)),
provider: req.provider,
model: req.model,
timestamp: Date.now(),
};
this.notify();
}
/** Stable reference between captures (useSyncExternalStore contract). */
getSnapshot(): RequestSnapshot | null {
return this.snapshot;
}
clear(): void {
if (this.snapshot === null) return;
this.snapshot = null;
this.notify();
}
subscribe(listener: Listener): () => void {
this.listeners.add(listener);
return () => this.listeners.delete(listener);
}
private notify(): void {
for (const listener of this.listeners) listener();
}
}
export const requestSnapshotStore = new RequestSnapshotStore();
|