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();