MCPcopy Create free account
hub / github.com/TanStack/ai / InMemoryRunEventLog

Class InMemoryRunEventLog

packages/ai-sandbox/src/run-log.ts:112–224  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

110 * backend mirrors.
111 */
112export class InMemoryRunEventLog implements RunEventLog {
113 private readonly runs = new Map<string, RunState>()
114
115 private now(): number {
116 return Date.now()
117 }
118
119 private require(runId: string): RunState {
120 const state = this.runs.get(runId)
121 if (!state) throw new Error(`run-log: unknown runId "${runId}"`)
122 return state
123 }
124
125 private wake(state: RunState): void {
126 const waiters = [...state.waiters]
127 state.waiters.clear()
128 for (const resolve of waiters) resolve()
129 }
130
131 // Mutators return a Promise without `async` so contract violations REJECT
132 // (rather than throwing synchronously from a Promise-typed method — a
133 // `.catch()` footgun) without an `await`-less async body.
134 open(input: { runId: string; threadId?: string }): Promise<RunRecord> {
135 const existing = this.runs.get(input.runId)
136 if (existing) return Promise.resolve({ ...existing.record })
137 const now = this.now()
138 const record: RunRecord = {
139 runId: input.runId,
140 ...(input.threadId !== undefined ? { threadId: input.threadId } : {}),
141 status: 'running',
142 lastSeq: -1,
143 createdAt: now,
144 updatedAt: now,
145 }
146 this.runs.set(input.runId, { record, chunks: [], waiters: new Set() })
147 return Promise.resolve({ ...record })
148 }
149
150 append(runId: string, chunk: StreamChunk): Promise<number> {
151 const state = this.runs.get(runId)
152 if (!state) {
153 return Promise.reject(new Error(`run-log: unknown runId "${runId}"`))
154 }
155 if (isTerminalRunStatus(state.record.status)) {
156 return Promise.reject(
157 new Error(
158 `run-log: cannot append to terminal run "${runId}" (status=${state.record.status})`,
159 ),
160 )
161 }
162 // Derive seq from the record's cursor (not `chunks.length`) so the gap-free
163 // invariant holds the same way the durable backend computes it, even if the
164 // backlog is ever trimmed/compacted.
165 const seq = state.record.lastSeq + 1
166 state.chunks.push(chunk)
167 state.record.lastSeq = seq
168 state.record.updatedAt = this.now()
169 this.wake(state)

Callers

nothing calls this directly

Calls

no outgoing calls

Tested by

no test coverage detected