{"version":3,"file":"in-memory.mjs","names":[],"sources":["../../../../src/v2/runtime/runner/in-memory.ts"],"sourcesContent":["import type {\n  AgentRunnerConnectRequest,\n  AgentRunnerIsRunningRequest,\n  AgentRunnerRunRequest,\n} from \"./agent-runner\";\nimport { AgentRunner } from \"./agent-runner\";\nimport type { AgentRunnerStopRequest } from \"./agent-runner\";\nimport type { Observable } from \"rxjs\";\nimport { ReplaySubject } from \"rxjs\";\nimport type {\n  AbstractAgent,\n  BaseEvent,\n  Message,\n  RunStartedEvent,\n  StateSnapshotEvent,\n} from \"@ag-ui/client\";\nimport { EventType, compactEvents } from \"@ag-ui/client\";\nimport { finalizeRunEvents } from \"@copilotkit/shared\";\n\nexport interface InMemoryLimits {\n  /** LRU cap on distinct threads. */\n  maxThreads?: number;\n  /** FIFO cap on runs kept per thread. `Infinity` or `0` disables the cap. */\n  maxRunsPerThread?: number;\n  /**\n   * Approximate byte ceiling on RETAINED thread/run history. Enforced at run\n   * completion (in `appendRun`), where LRU non-running threads are evicted to\n   * keep the total under this limit.\n   *\n   * Limitation: this bounds only history that has already been committed. A\n   * single in-flight run's buffered events (`currentRunEvents` and the two\n   * `ReplaySubject<BaseEvent>(Infinity)` buffers in `run()`) are NOT counted\n   * until that run completes, so `maxBytes` does not bound a single runaway\n   * run mid-stream.\n   *\n   * Limitation: byte eviction drops only other LRU non-running threads and\n   * never self-evicts the active/just-appended thread, so a single dominant\n   * thread's own retained history is not byte-trimmed (bounded only by\n   * `maxRunsPerThread`). `maxBytes` is thus a cross-thread ceiling enforced by\n   * evicting OTHER threads, not a per-thread cap.\n   */\n  maxBytes?: number;\n}\n\n/**\n * Constructor options for {@link InMemoryAgentRunner}.\n *\n * Extends {@link InMemoryLimits} so bounds can be passed inline alongside the\n * per-runner behavior flags. Be aware of the scope difference: the limits\n * reconfigure the process-global store shared by every runner, whereas\n * `onConcurrentRun` applies only to the runner instance it is passed to.\n */\nexport interface InMemoryAgentRunnerOptions extends InMemoryLimits {\n  /**\n   * How to handle a `run()` for a thread that already has an in-flight run.\n   * `\"throw\"` (default) rejects with \"Thread already running\". `\"supersede\"`\n   * aborts the prior run and starts the new one.\n   */\n  onConcurrentRun?: \"throw\" | \"supersede\";\n}\n\nexport const ɵINMEMORY_DEFAULTS: Required<InMemoryLimits> = {\n  maxThreads: 1000,\n  maxRunsPerThread: 100,\n  maxBytes: 512 * 1024 ** 2,\n};\n\n/**\n * A limit value is well-formed iff it is a non-negative integer OR `+Infinity`.\n * `+Infinity` is the documented \"disabled/unbounded\" sentinel and `0` is the\n * documented run-cap disable sentinel; both are non-negative and pass. Every\n * enforcement site (`evictThreadsIfNeeded`, `enforceRunCap`,\n * `evictByBytesIfNeeded`) compares its counter against the limit with `>` in a\n * `while`/`if` guard, so only these shapes keep those loops finite and correct.\n * Rejected: negatives (drive `count > -1` true on an empty collection, so\n * `enforceRunCap` `shift()!`s `undefined` and throws), `-Infinity` (loops never\n * terminate their intent — always \"over\"), `NaN` (every `>` is false, silently\n * disabling the bound), and non-integer finites (fractional caps are nonsense).\n */\nfunction ɵisValidLimit(value: number): boolean {\n  return value === Infinity || (Number.isInteger(value) && value >= 0);\n}\n\n/**\n * Normalize a fully-resolved limits bag so every field is well-formed before it\n * can reach an enforcement loop. Each field is validated independently against\n * {@link ɵisValidLimit}; an invalid value is CLAMPED to its\n * {@link ɵINMEMORY_DEFAULTS} floor and a single `console.warn` naming the field\n * and the received value is emitted.\n *\n * Clamp-and-warn (rather than throw) is deliberate and matches this file's\n * established posture toward bad input: `ɵestimateBytes` swallows serialization\n * failures and returns 0, the limits-clobber path warns rather than throwing,\n * and both the eviction and clobber logs are wrapped so \"logging must never\n * break construction/a run\". Constructing a bounded in-memory runner is a\n * best-effort, non-durable convenience; a typo'd bound must degrade to a safe\n * default, never abort construction or (worse) surface later as an unhandled\n * rejection from the fire-and-forget finalize path.\n */\nexport function ɵnormalizeLimits(\n  limits: Required<InMemoryLimits>,\n): Required<InMemoryLimits> {\n  const normalized = { ...limits };\n  for (const field of Object.keys(\n    ɵINMEMORY_DEFAULTS,\n  ) as (keyof InMemoryLimits)[]) {\n    const value = limits[field];\n    if (!ɵisValidLimit(value)) {\n      const fallback = ɵINMEMORY_DEFAULTS[field];\n      normalized[field] = fallback;\n      try {\n        console.warn(\n          `[CopilotKit] InMemoryAgentRunner: invalid ${field} value ` +\n            `${String(value)} (expected a non-negative integer or Infinity); ` +\n            `falling back to ${String(fallback)}.`,\n        );\n      } catch {\n        // best-effort: logging must never break construction\n      }\n    }\n  }\n  return normalized;\n}\n\nconst EVICTION_GUIDANCE =\n  \"[CopilotKit] InMemoryAgentRunner evicted in-memory thread history to stay \" +\n  \"under memory limits. This runner is bounded and non-durable by design. For \" +\n  \"durable or production threads, configure an Intelligence backend.\";\n\nconst LIMITS_CLOBBER_GUIDANCE =\n  \"[CopilotKit] InMemoryAgentRunner was constructed with in-memory limits that \" +\n  \"differ from the already-configured process-global store; the last-constructed \" +\n  \"runner's limits apply to ALL in-memory threads (the store is shared per-process). \" +\n  \"Configure a single consistent set of limits, or use an Intelligence backend for \" +\n  \"isolated bounds.\";\n\n/**\n * Best-effort approximate byte size of a value, via serialized length.\n * Never throws — returns 0 when the value cannot be serialized. This is an\n * approximation (UTF-16 length, not exact heap bytes), used only for relative\n * accounting against `maxBytes`.\n */\nexport function ɵestimateBytes(value: unknown): number {\n  try {\n    return JSON.stringify(value)?.length ?? 0;\n  } catch {\n    return 0;\n  }\n}\n\n/**\n * Per-run finalize intent, captured once when a run starts and mutated (only)\n * by whoever aborts THAT run — `stop()` or a superseding `run()`. The run's own\n * teardown reads this captured holder instead of the shared, mutable\n * `store.stopRequested`, so a later run that resets store state can never cause\n * an intentionally-stopped run to be finalized as an error (or vice versa).\n */\ninterface RunFinalizeControl {\n  /** True once THIS run has been asked to stop (clean stop, not an error). */\n  stopRequested: boolean;\n}\n\ninterface HistoricRun {\n  threadId: string;\n  runId: string;\n  /** ID of the agent that executed this run. */\n  agentId: string;\n  parentRunId: string | null;\n  events: BaseEvent[];\n  /**\n   * Snapshot of all messages (input + generated) at the end of this run, as\n   * passed in by the caller. NOTE: `BoundedThreadStore.appendRun` moves this\n   * snapshot to the THREAD level (`InMemoryEventStore.messagesSnapshot`) and\n   * clears this field to `[]`, so a stored HistoricRun never carries messages.\n   * The thread-messages fallback reads the thread-level snapshot, not this.\n   */\n  messages: Message[];\n  createdAt: number;\n  /** Approximate retained byte size of `events`; set by BoundedThreadStore at append. */\n  approxEventBytes?: number;\n  /**\n   * Legacy field retained for shape compatibility. `appendRun` always zeroes it\n   * because message bytes are accounted at the thread level, not per run.\n   */\n  approxMessageBytes?: number;\n}\n\n/**\n * Lightweight thread summary returned by {@link InMemoryAgentRunner.listThreads}.\n * Shape matches the Intelligence platform's ThreadRecord so the same HTTP\n * response envelope can be used for both backends.\n */\nexport interface InMemoryThread {\n  id: string;\n  name: string | null;\n  agentId: string;\n  organizationId: \"\"; // always empty in in-memory mode\n  createdById: \"\"; // always empty in in-memory mode\n  archived: false; // always false in in-memory mode\n  createdAt: string;\n  updatedAt: string;\n}\n\nclass InMemoryEventStore {\n  constructor(public threadId: string) {}\n\n  /** The subject that current consumers subscribe to. */\n  subject: ReplaySubject<BaseEvent> | null = null;\n\n  /** True while a run is actively producing events. */\n  isRunning = false;\n\n  /** Current run ID */\n  currentRunId: string | null = null;\n\n  /** Historic completed runs */\n  historicRuns: HistoricRun[] = [];\n\n  /** Currently running agent instance (if any). */\n  agent: AbstractAgent | null = null;\n\n  /** Subject returned from run() while the run is active. */\n  runSubject: ReplaySubject<BaseEvent> | null = null;\n\n  /**\n   * Thread-level lifecycle flag: true once a stop/supersede has been requested\n   * for the currently-owning run but that run has not yet finalized. Drives\n   * eviction protection, the connect() bridge, and stop() de-dup. This is NOT\n   * the finalize intent read by a run's teardown — that lives per-run on\n   * {@link activeFinalize}, so a superseding run resetting this field cannot\n   * mislabel the run it replaced. A new run resets this to false when it takes\n   * ownership.\n   */\n  stopRequested = false;\n\n  /**\n   * Finalize control of the currently-owning run. `stop()` and a superseding\n   * `run()` flip the owning run's flag through this reference; each run also\n   * captures the SAME object in its closure, so its teardown finalizes against\n   * its own intent regardless of what a later run does to the store.\n   */\n  activeFinalize: RunFinalizeControl | null = null;\n\n  /** Reference to the events emitted in the current run. */\n  currentEvents: BaseEvent[] | null = null;\n\n  /**\n   * The thread's single latest NON-EMPTY message snapshot, held at the THREAD\n   * level (independent of `historicRuns` lifecycle). Decoupling the snapshot\n   * from per-run storage means run-cap FIFO eviction and interleaved\n   * empty-snapshot runs can never drop or pin the thread's message history.\n   */\n  messagesSnapshot: Message[] = [];\n\n  /** Approximate retained byte size of `messagesSnapshot`. */\n  approxMessagesSnapshotBytes = 0;\n\n  /**\n   * The thread's true creation timestamp (epoch ms), captured from the FIRST\n   * run ever appended and held at the THREAD level (independent of\n   * `historicRuns` lifecycle). Decoupling it from per-run storage means run-cap\n   * FIFO eviction — which shifts the oldest entries off `historicRuns` — can\n   * never move the reported creation time forward. `null` until the first run\n   * lands. Mirrors the `messagesSnapshot` thread-level decoupling.\n   */\n  createdAt: number | null = null;\n}\n\nexport class ɵBoundedThreadStore {\n  private readonly map = new Map<string, InMemoryEventStore>();\n  private totalBytes = 0;\n  private warned = false;\n  /** True once limits have been EXPLICITLY set (via setLimits), not just the constructor default. */\n  private limitsExplicitlySet = false;\n  /** Warn-once latch for the clobber warning, kept distinct from the eviction `warned` latch. */\n  private clobberWarned = false;\n\n  private limits: Required<InMemoryLimits>;\n\n  constructor(limits: Required<InMemoryLimits>) {\n    // Normalize once at construction so `this.limits` is ALWAYS well-formed,\n    // regardless of entry point (direct construction or a later `setLimits`).\n    this.limits = ɵnormalizeLimits(limits);\n  }\n\n  get byteTotal(): number {\n    return this.totalBytes;\n  }\n\n  /**\n   * The store's CURRENT effective bounds. Exposed (with the `ɵ` internal-API\n   * prefix) so a partial `setLimits` can coalesce unspecified fields against the\n   * live config rather than the hardcoded {@link ɵINMEMORY_DEFAULTS} — a partial\n   * update must be a partial update, never a silent reset of the fields the\n   * caller did not mention. Returns a copy so callers cannot mutate the store's\n   * bounds through it.\n   */\n  get ɵlimits(): Required<InMemoryLimits> {\n    return { ...this.limits };\n  }\n\n  /**\n   * Reconfigure the process-global store's bounds. Called by the\n   * {@link InMemoryAgentRunner} constructor when limits are passed. Because the\n   * store is a per-process singleton, this replaces the bounds for ALL in-memory\n   * threads. Emits {@link LIMITS_CLOBBER_GUIDANCE} at most ONCE per store when a\n   * SECOND (or later) explicit set arrives whose resolved values differ from the\n   * prior explicit set — i.e. a genuine clobber of an already-customized config.\n   * The first explicit customization (defaults → custom) is the intended\n   * override and never warns; identical re-sets never warn.\n   */\n  setLimits(limits: Required<InMemoryLimits>): void {\n    // Normalize FIRST so invalid fields can never reach an enforcement loop and\n    // so the clobber comparison below is against the EFFECTIVE (clamped) values,\n    // not the raw ones — a typo'd bound that clamps to the current default is not\n    // a genuine clobber and must not warn.\n    const normalized = ɵnormalizeLimits(limits);\n    if (\n      this.limitsExplicitlySet &&\n      !this.clobberWarned &&\n      (normalized.maxThreads !== this.limits.maxThreads ||\n        normalized.maxRunsPerThread !== this.limits.maxRunsPerThread ||\n        normalized.maxBytes !== this.limits.maxBytes)\n    ) {\n      this.clobberWarned = true;\n      try {\n        console.warn(LIMITS_CLOBBER_GUIDANCE);\n      } catch {\n        // best-effort: logging must never break construction\n      }\n    }\n    this.limitsExplicitlySet = true;\n    this.limits = normalized;\n  }\n\n  get size(): number {\n    return this.map.size;\n  }\n\n  /** Re-insert at the tail so Map iteration order stays LRU-first. */\n  private touchOrder(threadId: string, store: InMemoryEventStore): void {\n    this.map.delete(threadId);\n    this.map.set(threadId, store);\n  }\n\n  getOrCreate(threadId: string): InMemoryEventStore {\n    const existing = this.map.get(threadId);\n    if (existing) {\n      this.touchOrder(threadId, existing);\n      return existing;\n    }\n    const store = new InMemoryEventStore(threadId);\n    this.map.set(threadId, store);\n    this.evictThreadsIfNeeded(threadId);\n    return store;\n  }\n\n  get(\n    threadId: string,\n    opts: { touch: boolean },\n  ): InMemoryEventStore | undefined {\n    const store = this.map.get(threadId);\n    if (store && opts.touch) this.touchOrder(threadId, store);\n    return store;\n  }\n\n  peek(threadId: string): InMemoryEventStore | undefined {\n    return this.map.get(threadId);\n  }\n\n  /**\n   * Evict the least-recently-used thread that is neither running NOR\n   * mid-finalization. Returns false if none evictable. The `protect` thread\n   * (typically the one just created) is never evicted, so a fresh thread is not\n   * immediately dropped when it is the only non-running candidate.\n   *\n   * A thread is skipped while `isRunning` OR `stopRequested` is set.\n   * `stop()` flips `isRunning` to false the moment it aborts the agent, but the\n   * run keeps finalizing asynchronously (the abort trips the `catch` in\n   * `runAgent`, which later calls `appendRun`). During that window\n   * `stopRequested` stays true; evicting the thread then would make the pending\n   * `appendRun` hit `if (!store) return` and silently drop the aborted run's\n   * history. Guarding on `stopRequested` keeps the thread alive until\n   * finalization completes.\n   */\n  private evictOneLru(protect?: string): boolean {\n    for (const [threadId, store] of this.map) {\n      if (threadId === protect) continue; // never evict the just-created thread\n      // never evict a running or still-finalizing (stop-requested) thread\n      if (store.isRunning || store.stopRequested) continue;\n      this.removeThread(threadId, store);\n      this.noteEviction();\n      return true;\n    }\n    return false;\n  }\n\n  appendRun(threadId: string, run: HistoricRun): void {\n    const store = this.map.get(threadId);\n    if (!store) return; // best-effort: nothing to append to\n\n    // Thread-level creation timestamp: capture the FIRST run's createdAt once\n    // and never overwrite it. Held on the store (not derived from\n    // `historicRuns[0]`) so run-cap FIFO eviction of the oldest runs cannot\n    // drift the thread's reported creation time forward. Mirrors the\n    // thread-level `messagesSnapshot` decoupling below.\n    if (store.createdAt === null) {\n      store.createdAt = run.createdAt;\n    }\n\n    // Thread-level message snapshot: keep the single latest NON-EMPTY snapshot\n    // on the store, decoupled from `historicRuns`. When the incoming run\n    // carries a non-empty snapshot, replace the thread's snapshot (adjusting\n    // byte accounting). When it's empty (non-array `agent.messages` or an\n    // error-path run), leave the existing thread snapshot untouched so history\n    // is never lost. The snapshot never lives on a HistoricRun, so run-cap FIFO\n    // eviction can never drop it and an interleaved empty run can never pin it.\n    if (run.messages.length > 0) {\n      this.totalBytes -= store.approxMessagesSnapshotBytes;\n      // Store the incoming array directly (SHALLOW, array-level copy). `run.messages`\n      // is already a fresh `[...agent.messages]` array created in run(), so we own the\n      // array and it is decoupled from `agent.messages` at the array level (push/splice\n      // on the agent's array cannot mutate our snapshot). We deliberately do NOT deep-copy\n      // here: `structuredClone` throws DataCloneError on a non-cloneable message field,\n      // which would wedge the thread and hang SSE — inconsistent with `ɵestimateBytes`,\n      // which tolerates the same bad-payload class. The tradeoff is that the inner\n      // `Message` objects remain shared by reference with `agent.messages`, so an agent\n      // that mutates its own message objects IN PLACE after the run can still be observed\n      // through this snapshot. That inner-object isolation is a known limitation tracked as\n      // follow-up; callers must treat returned messages as read-only. Estimate bytes on the\n      // same value so accounting matches exactly what is retained.\n      store.messagesSnapshot = run.messages;\n      store.approxMessagesSnapshotBytes = ɵestimateBytes(run.messages);\n      this.totalBytes += store.approxMessagesSnapshotBytes;\n    }\n\n    // Do not carry message bytes on the HistoricRun: the snapshot is now tracked\n    // at the thread level, so historicRuns must never account message bytes.\n    run.messages = [];\n    run.approxMessageBytes = 0;\n\n    // Compute this run's approximate event size once, at append time.\n    run.approxEventBytes = ɵestimateBytes(run.events);\n    store.historicRuns.push(run);\n    this.totalBytes += run.approxEventBytes;\n    this.touchOrder(threadId, store);\n\n    this.enforceRunCap(store);\n    this.evictByBytesIfNeeded(threadId);\n  }\n\n  private enforceRunCap(store: InMemoryEventStore): void {\n    const cap = this.limits.maxRunsPerThread;\n    if (!cap || cap === Infinity) return; // 0 or Infinity → disabled\n    while (store.historicRuns.length > cap) {\n      const dropped = store.historicRuns.shift()!;\n      // Only event bytes live on a HistoricRun; the message snapshot is tracked\n      // at the thread level and survives run-cap eviction.\n      this.totalBytes -= dropped.approxEventBytes ?? 0;\n      // Per-thread run-cap trimming is also eviction — history is being dropped.\n      // Route it through the SAME warn-once latch as whole-thread LRU eviction so\n      // this shows up in logs rather than as silent data loss. `noteEviction` is\n      // latched (one warning per store, reset by `clear()`), so a hot thread that\n      // trims on every subsequent append warns once, never per dropped run. The\n      // loop only runs when a run is ACTUALLY over the cap, so a disabled or\n      // under-cap `enforceRunCap` stays silent (it returned / never entered here).\n      this.noteEviction();\n    }\n  }\n\n  /**\n   * Trim the store back under the byte ceiling by evicting LRU non-running\n   * threads. `protect` (the just-appended thread) is never self-evicted, so a\n   * fresh run pushes OTHER threads out rather than dropping itself.\n   */\n  private evictByBytesIfNeeded(protect?: string): void {\n    while (this.totalBytes > this.limits.maxBytes) {\n      if (!this.evictOneLru(protect)) break; // only protected/running threads left → accept overage\n    }\n  }\n\n  private removeThread(threadId: string, store: InMemoryEventStore): void {\n    for (const run of store.historicRuns) {\n      this.totalBytes -= run.approxEventBytes ?? 0;\n    }\n    // The thread's message snapshot is tracked at the store level, so it must\n    // be reclaimed here in addition to the per-run event bytes.\n    this.totalBytes -= store.approxMessagesSnapshotBytes;\n    this.map.delete(threadId);\n  }\n\n  private evictThreadsIfNeeded(protect?: string): void {\n    while (this.map.size > this.limits.maxThreads) {\n      if (!this.evictOneLru(protect)) break; // everything evictable is running → accept overage\n    }\n  }\n\n  private noteEviction(): void {\n    if (this.warned) return;\n    this.warned = true;\n    try {\n      console.warn(EVICTION_GUIDANCE);\n    } catch {\n      // best-effort: logging must never break a run\n    }\n  }\n\n  listThreads(): InMemoryThread[] {\n    const threads: InMemoryThread[] = [];\n    for (const [threadId, store] of this.map) {\n      if (store.historicRuns.length === 0) continue;\n      const lastRun = store.historicRuns[store.historicRuns.length - 1]!;\n      // Creation time comes from the thread-level `store.createdAt` (the first\n      // run ever appended), NOT `historicRuns[0]` (the oldest RETAINED run):\n      // run-cap FIFO eviction drops the oldest retained runs, so deriving it\n      // from `historicRuns[0]` would silently drift the timestamp forward over\n      // a thread's lifetime. `updatedAt` stays on `lastRun` because FIFO\n      // eviction removes from the FRONT, so the newest run is never evicted.\n      // The `?? lastRun.createdAt` fallback is defensive only: any thread with\n      // runs has had `store.createdAt` set by `appendRun`.\n      threads.push({\n        id: threadId,\n        name: null,\n        agentId: lastRun.agentId,\n        organizationId: \"\",\n        createdById: \"\",\n        archived: false,\n        createdAt: new Date(store.createdAt ?? lastRun.createdAt).toISOString(),\n        updatedAt: new Date(lastRun.createdAt).toISOString(),\n      });\n    }\n    return threads.sort(\n      (a, b) =>\n        new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime(),\n    );\n  }\n\n  clear(): void {\n    this.map.clear();\n    this.totalBytes = 0;\n    this.warned = false;\n  }\n}\n\n/**\n * Process-wide singleton backing every {@link InMemoryAgentRunner}. Exported\n * (with the `ɵ` internal-API prefix) so tests can inspect the exact store the\n * runner writes to; not part of the public API.\n */\nexport const ɵGLOBAL_STORE = new ɵBoundedThreadStore(ɵINMEMORY_DEFAULTS);\nconst sharedStore = ɵGLOBAL_STORE;\n\nexport class InMemoryAgentRunner extends AgentRunner {\n  readonly ɵsupportsLocalThreadEndpoints = true;\n\n  /**\n   * How to handle a `run()` for a thread that already has an in-flight run.\n   * `\"throw\"` (default) preserves the historic behavior. `\"supersede\"` aborts\n   * the prior run (mirroring `stop()`) and starts the new one — opted into by\n   * the hosted-bot listener so a fast follow-up turn on the same thread cleanly\n   * replaces a still-running (or wedged) prior turn instead of erroring with\n   * \"Thread already running\".\n   */\n  private readonly onConcurrentRun: \"throw\" | \"supersede\";\n\n  /**\n   * @param options Per-runner behavior (`onConcurrentRun`) plus optional bounds\n   * for the in-memory store ({@link InMemoryLimits}).\n   *\n   * Note the differing scopes: `onConcurrentRun` is per-runner instance, while\n   * the limits reconfigure the PROCESS-GLOBAL store shared by every\n   * `InMemoryAgentRunner`. Omit the limits for safe defaults\n   * ({@link ɵINMEMORY_DEFAULTS}); passing none leaves the store untouched. When\n   * multiple runners are constructed with differing limits, the last-constructed\n   * wins — in practice the OSS/SSE default construction passes nothing. If a\n   * second (or later) runner is constructed with limits that DIFFER from an\n   * already-customized store, a one-time `console.warn` is emitted to signal that\n   * the shared store's bounds are being clobbered for ALL in-memory threads.\n   */\n  constructor(options?: InMemoryAgentRunnerOptions) {\n    super();\n    const { onConcurrentRun, ...limits } = options ?? {};\n    this.onConcurrentRun = onConcurrentRun ?? \"throw\";\n\n    // Only reconfigure the shared store when a bound was actually supplied.\n    // `new InMemoryAgentRunner({ onConcurrentRun: \"supersede\" })` must stay\n    // inert with respect to limits.\n    if (\n      limits.maxThreads !== undefined ||\n      limits.maxRunsPerThread !== undefined ||\n      limits.maxBytes !== undefined\n    ) {\n      // Coalesce each unspecified field against the store's CURRENT effective\n      // limits, NOT ɵINMEMORY_DEFAULTS. The store is process-global, so tuning\n      // one bound must leave every previously-customized sibling bound intact —\n      // a partial update stays a partial update instead of silently resetting the\n      // fields the caller never mentioned. Passing all three (e.g.\n      // ɵINMEMORY_DEFAULTS) still fully replaces the config, so the defaults-\n      // restore path is unaffected.\n      const current = sharedStore.ɵlimits;\n      sharedStore.setLimits({\n        maxThreads: limits.maxThreads ?? current.maxThreads,\n        maxRunsPerThread: limits.maxRunsPerThread ?? current.maxRunsPerThread,\n        maxBytes: limits.maxBytes ?? current.maxBytes,\n      });\n    }\n  }\n\n  run(request: AgentRunnerRunRequest): Observable<BaseEvent> {\n    const store = sharedStore.getOrCreate(request.threadId);\n\n    // Enter the concurrency branch whenever a prior run still owns the thread —\n    // either actively running OR still finalizing after a stop()/supersede.\n    // `stop()` flips `isRunning` to false the instant it aborts the agent, but\n    // the run keeps finalizing asynchronously (`stopRequested` stays true).\n    // Gating on `isRunning` alone let a `run()` slip through that window\n    // unhandled: no supersede/throw, no per-run intent capture, and a leaked\n    // bridge from the dying run's subject.\n    if (store.isRunning || store.stopRequested) {\n      if (this.onConcurrentRun !== \"supersede\") {\n        throw new Error(\"Thread already running\");\n      }\n      // Supersede: abort the prior (possibly wedged) run so this one can start.\n      // Mirrors stop(). Record the prior run's OWN finalize intent on its\n      // captured control BEFORE resetting the shared store flags for the new\n      // run: a supersede is a clean stop of the prior run, so its async teardown\n      // must finalize as RUN_FINISHED, never a synthetic RUN_ERROR. The prior\n      // run's async finalization is prevented from clobbering this run's state\n      // by the run-id guard below.\n      const priorAgent = store.agent;\n      const priorFinalize = store.activeFinalize;\n      if (priorFinalize) {\n        priorFinalize.stopRequested = true;\n      }\n      store.isRunning = false;\n      if (priorAgent) {\n        try {\n          priorAgent.abortRun();\n        } catch (error) {\n          console.error(\"Failed to abort superseded run\", error);\n        }\n      }\n    }\n    store.isRunning = true;\n    store.currentRunId = request.input.runId;\n    store.agent = request.agent;\n    store.stopRequested = false;\n\n    // Per-run finalize control. This run's teardown reads THIS captured holder\n    // (never the shared `store.stopRequested`, which a later run resets), so an\n    // aborted run is always finalized against its own stop-intent.\n    const finalizeControl: RunFinalizeControl = { stopRequested: false };\n    store.activeFinalize = finalizeControl;\n\n    // Track seen message IDs and current run events for this run\n    const seenMessageIds = new Set<string>();\n    const currentRunEvents: BaseEvent[] = [];\n    store.currentEvents = currentRunEvents;\n\n    // Get all previously seen message IDs from historic runs\n    const historicMessageIds = new Set<string>();\n    for (const run of store.historicRuns) {\n      for (const event of run.events) {\n        if (\"messageId\" in event && typeof event.messageId === \"string\") {\n          historicMessageIds.add(event.messageId);\n        }\n        if (event.type === EventType.RUN_STARTED) {\n          const runStarted = event as RunStartedEvent;\n          const messages = runStarted.input?.messages ?? [];\n          for (const message of messages) {\n            historicMessageIds.add(message.id);\n          }\n        }\n      }\n    }\n\n    const nextSubject = new ReplaySubject<BaseEvent>(Infinity);\n\n    // Update the store's subject immediately. We intentionally do NOT capture\n    // and bridge the previous subject: see the note before `runAgent()` below.\n    store.subject = nextSubject;\n\n    // Create a subject for run() return value\n    const runSubject = new ReplaySubject<BaseEvent>(Infinity);\n    store.runSubject = runSubject;\n\n    // Helper function to run the agent and handle errors\n    const runAgent = async () => {\n      // Get parent run ID for chaining\n      const lastRun = store.historicRuns[store.historicRuns.length - 1];\n      const parentRunId = lastRun?.runId ?? null;\n\n      // Shared teardown for both the success and error paths. Keeping this one\n      // helper means the two paths cannot drift apart (they were near-identical\n      // and must stay symmetric). `interruptionMessage` is set only on the error\n      // path; its presence is what distinguishes the two.\n      const finalizeRun = (opts: { interruptionMessage?: string }) => {\n        const isError = opts.interruptionMessage !== undefined;\n\n        // Capture the count of REAL (agent-emitted) events BEFORE finalizing.\n        // `finalizeRunEvents` mutates `currentRunEvents` IN PLACE — it always\n        // pushes a synthetic terminal (and any closers) when the stream ended\n        // without one — so after the call `currentRunEvents.length` is never 0.\n        // The persistence guard below must gate on this pre-finalize count, or\n        // the \"skip an immediate throw that emitted nothing\" check is dead.\n        const preFinalizeEventCount = currentRunEvents.length;\n\n        // Finalize against THIS run's own captured stop-intent — never the\n        // shared `store.stopRequested`, which a superseding run resets. An\n        // aborted run is thus finalized as a clean RUN_FINISHED, not a synthetic\n        // RUN_ERROR.\n        const appendedEvents = finalizeRunEvents(currentRunEvents, {\n          stopRequested: finalizeControl.stopRequested,\n          ...(isError ? { interruptionMessage: opts.interruptionMessage } : {}),\n        });\n        for (const event of appendedEvents) {\n          runSubject.next(event);\n          nextSubject.next(event);\n        }\n\n        // Does this run still own the thread? A superseding run has changed\n        // `currentRunId`, so the run it replaced no longer owns the store.\n        const ownsThread = store.currentRunId === request.input.runId;\n\n        // Store this run's events. Guard on the per-run id (not the shared\n        // `store.currentRunId`): a superseded run no longer owns the store, so\n        // it must not push history — and never under a newer run's id, which\n        // would corrupt the thread's history. On the error path also require at\n        // least one real (pre-finalize) event, so an immediate throw with\n        // nothing emitted does not create a phantom historic run holding only\n        // the synthetic terminal.\n        if (ownsThread && (!isError || preFinalizeEventCount > 0)) {\n          // Compact the events before storing (like SQLite does)\n          const compactedEvents = compactEvents(currentRunEvents);\n          sharedStore.appendRun(request.threadId, {\n            threadId: request.threadId,\n            runId: request.input.runId,\n            agentId: request.agent.agentId ?? \"default\",\n            parentRunId,\n            events: compactedEvents,\n            // Snapshot all messages (input + generated) for the thread-messages endpoint\n            messages: Array.isArray(request.agent.messages)\n              ? [...request.agent.messages]\n              : [],\n            createdAt: Date.now(),\n          });\n        }\n\n        // Complete the run. Guard the shared-store reset: if a newer run has\n        // superseded this one (`currentRunId` changed), that run now owns the\n        // store — don't clobber its state. Always complete THIS run's subjects.\n        if (ownsThread) {\n          store.currentEvents = null;\n          store.currentRunId = null;\n          store.agent = null;\n          store.runSubject = null;\n          store.stopRequested = false;\n          store.isRunning = false;\n          store.activeFinalize = null;\n        }\n        runSubject.complete();\n        nextSubject.complete();\n        // Time-scoped release: this run's events are now in historicRuns, so its\n        // infinite ReplaySubject buffer is pure duplication — drop the store's\n        // reference so it becomes collectable. The identity guard is what makes\n        // this correct, and it does so differently on each path:\n        //\n        //   - Owning path: no newer run superseded this one, so store.subject is\n        //     still nextSubject and the guard passes. The `if (ownsThread)` block\n        //     above just cleared isRunning and stopRequested, so connect() — which\n        //     bridges store.subject only while isRunning || stopRequested — will\n        //     not re-subscribe; it rebuilds this run's events from historicRuns\n        //     instead. Nulling the reference is therefore safe.\n        //\n        //   - Superseded path (`onConcurrentRun: \"supersede\"`): a newer run has\n        //     already installed ITS subject and run id on the store, so the guard\n        //     fails and we leave store.subject untouched. Here isRunning/\n        //     stopRequested describe that live run (isRunning is typically true),\n        //     so it is precisely the identity guard — not those flags — that\n        //     prevents us from nulling the live run's subject and cutting\n        //     connect() off from the in-flight stream. This run's own buffer is no\n        //     longer referenced by the store and becomes collectable regardless.\n        if (store.subject === nextSubject) {\n          store.subject = null;\n        }\n      };\n\n      try {\n        await request.agent.runAgent(request.input, {\n          onEvent: ({ event }) => {\n            let processedEvent: BaseEvent = event;\n            if (event.type === EventType.RUN_STARTED) {\n              const runStartedEvent = event as RunStartedEvent;\n              if (!runStartedEvent.input) {\n                const sanitizedMessages = request.input.messages\n                  ? request.input.messages.filter(\n                      (message) => !historicMessageIds.has(message.id),\n                    )\n                  : undefined;\n                const updatedInput = {\n                  ...request.input,\n                  ...(sanitizedMessages !== undefined\n                    ? { messages: sanitizedMessages }\n                    : {}),\n                };\n                runStartedEvent.input = updatedInput;\n                processedEvent = runStartedEvent;\n              }\n            }\n\n            runSubject.next(processedEvent); // For run() return - only agent events\n            nextSubject.next(processedEvent); // For connect() / store - all events\n            currentRunEvents.push(processedEvent); // Accumulate for storage\n          },\n          onNewMessage: ({ message }) => {\n            // Called for each new message\n            if (!seenMessageIds.has(message.id)) {\n              seenMessageIds.add(message.id);\n            }\n          },\n          onRunStartedEvent: () => {\n            // Mark any messages from the input as seen so they aren't emitted twice\n            if (request.input.messages) {\n              for (const message of request.input.messages) {\n                if (!seenMessageIds.has(message.id)) {\n                  seenMessageIds.add(message.id);\n                }\n              }\n            }\n          },\n        });\n\n        finalizeRun({});\n      } catch (error) {\n        const interruptionMessage =\n          error instanceof Error ? error.message : String(error);\n        finalizeRun({ interruptionMessage });\n      }\n    };\n\n    // NOTE: we deliberately do NOT bridge the previous store subject into\n    // `nextSubject`. `store.subject` is nulled the moment a run fully tears down\n    // (identity guard in `finalizeRun`), so the previous subject is non-null\n    // ONLY when this run is superseding a prior run that is still in flight or\n    // finalizing. Forwarding that dying run's subject would replay its buffered\n    // RUN_STARTED and push its terminal event (RUN_FINISHED/RUN_ERROR) into THIS\n    // live run's stream — an invalid AG-UI sequence on a healthy run. A\n    // superseded run's stream must stay isolated: it reaches only its own\n    // connect() subscribers via its own (now-detached) subject, never the\n    // superseding run's.\n\n    // Start the agent execution immediately (not lazily)\n    runAgent();\n\n    // Return the run subject (only agent events, no injected messages)\n    return runSubject.asObservable();\n  }\n\n  connect(request: AgentRunnerConnectRequest): Observable<BaseEvent> {\n    const store = sharedStore.get(request.threadId, { touch: true });\n    const connectionSubject = new ReplaySubject<BaseEvent>(Infinity);\n\n    if (!store) {\n      // No store means no events\n      connectionSubject.complete();\n      return connectionSubject.asObservable();\n    }\n\n    // Collect all historic events from memory\n    const allHistoricEvents: BaseEvent[] = [];\n    for (const run of store.historicRuns) {\n      allHistoricEvents.push(...run.events);\n    }\n\n    // Apply compaction to all historic events together (like SQLite)\n    const compactedEvents = compactEvents(allHistoricEvents);\n\n    // Emit compacted events and track message IDs\n    const emittedMessageIds = new Set<string>();\n    for (const event of compactedEvents) {\n      connectionSubject.next(event);\n      if (\"messageId\" in event && typeof event.messageId === \"string\") {\n        emittedMessageIds.add(event.messageId);\n      }\n    }\n\n    // Bridge active run to connection if exists\n    if (store.subject && (store.isRunning || store.stopRequested)) {\n      store.subject.subscribe({\n        next: (event) => {\n          // Skip message events that we've already emitted from historic\n          if (\n            \"messageId\" in event &&\n            typeof event.messageId === \"string\" &&\n            emittedMessageIds.has(event.messageId)\n          ) {\n            return;\n          }\n          connectionSubject.next(event);\n        },\n        complete: () => connectionSubject.complete(),\n        error: (err) => connectionSubject.error(err),\n      });\n    } else {\n      // No active run, complete after historic events\n      connectionSubject.complete();\n    }\n\n    return connectionSubject.asObservable();\n  }\n\n  isRunning(request: AgentRunnerIsRunningRequest): Promise<boolean> {\n    const store = sharedStore.peek(request.threadId);\n    return Promise.resolve(store?.isRunning ?? false);\n  }\n\n  stop(request: AgentRunnerStopRequest): Promise<boolean | undefined> {\n    const store = sharedStore.peek(request.threadId);\n    if (!store || !store.isRunning) {\n      return Promise.resolve(false);\n    }\n    if (request.runId !== undefined && store.currentRunId !== request.runId) {\n      return Promise.resolve(false);\n    }\n    if (store.stopRequested) {\n      return Promise.resolve(false);\n    }\n\n    store.stopRequested = true;\n    store.isRunning = false;\n    // Record the stop on the running run's OWN finalize control so its async\n    // teardown finalizes as a clean RUN_FINISHED. This is the same object that\n    // run's closure reads, so a later run cannot mislabel this stop.\n    const finalizeControl = store.activeFinalize;\n    if (finalizeControl) {\n      finalizeControl.stopRequested = true;\n    }\n\n    const agent = store.agent;\n    if (!agent) {\n      store.stopRequested = false;\n      store.isRunning = false;\n      if (finalizeControl) {\n        finalizeControl.stopRequested = false;\n      }\n      return Promise.resolve(false);\n    }\n\n    try {\n      agent.abortRun();\n      return Promise.resolve(true);\n    } catch (error) {\n      console.error(\"Failed to abort agent run\", error);\n      store.stopRequested = false;\n      store.isRunning = true;\n      if (finalizeControl) {\n        finalizeControl.stopRequested = false;\n      }\n      return Promise.resolve(false);\n    }\n  }\n\n  /**\n   * Returns a summary of every thread that has been run through this runner.\n   *\n   * This powers the local-dev fallback for `GET /threads` when the Intelligence\n   * platform is not configured. Each entry mirrors the shape of a platform\n   * `ThreadRecord` so the HTTP handler can use the same response envelope.\n   */\n  listThreads(): InMemoryThread[] {\n    return sharedStore.listThreads();\n  }\n\n  /**\n   * Returns all messages for a thread, using the snapshot captured at the end\n   * of the most recent run.\n   *\n   * This powers the local-dev fallback for `GET /threads/:threadId/messages`\n   * when the Intelligence platform is not configured. The returned `Message[]`\n   * objects come directly from the ag-ui agent, so their shape is compatible\n   * with the Intelligence platform's `ThreadMessage` type.\n   */\n  getThreadMessages(threadId: string): Message[] {\n    const store = sharedStore.peek(threadId);\n    if (!store) return [];\n    // The thread's latest non-empty snapshot is held at the store level,\n    // independent of `historicRuns` lifecycle, so run-cap eviction and\n    // interleaved empty-snapshot runs can never lose it. Return a SHALLOW\n    // (array-level) copy: a fresh array so a caller mutating array STRUCTURE\n    // (push/splice/reassign elements) cannot affect the stored snapshot. We\n    // deliberately do NOT deep-copy: `structuredClone` throws DataCloneError on a\n    // non-cloneable message field, which would wedge the thread and hang SSE —\n    // inconsistent with `ɵestimateBytes`, which tolerates the same bad-payload class.\n    // The tradeoff is that the inner `Message` objects remain shared by reference with\n    // the stored snapshot, so mutating a returned message's FIELD\n    // (e.g. `getThreadMessages(t)[0].content = \"x\"`) is NOT isolated and would corrupt\n    // the stored snapshot. That inner-object isolation is a known limitation tracked as\n    // follow-up; callers must treat returned messages as read-only.\n    return [...store.messagesSnapshot];\n  }\n\n  /**\n   * Returns all AG-UI events for a thread, compacted across historic runs.\n   *\n   * Powers the local-dev fallback for `GET /threads/:threadId/events` when the\n   * Intelligence platform is not configured. The compaction logic matches\n   * the connection-replay path in {@link connect}, so the stream a\n   * late-joining inspector sees matches what this method returns.\n   */\n  getThreadEvents(threadId: string): BaseEvent[] {\n    const store = sharedStore.peek(threadId);\n    if (!store || store.historicRuns.length === 0) return [];\n    const all: BaseEvent[] = [];\n    for (const run of store.historicRuns) all.push(...run.events);\n    return compactEvents(all);\n  }\n\n  /**\n   * Returns the agent state snapshot for a thread.\n   *\n   * Derived from the last `STATE_SNAPSHOT` in the compacted event stream. The\n   * AG-UI `compactEvents` helper consolidates STATE_DELTA events and produces\n   * a single trailing STATE_SNAPSHOT when state changes exist, so this is a\n   * faithful view of state at the end of the most recent run.\n   *\n   * Returns `null` when the thread has never emitted a STATE_SNAPSHOT.\n   */\n  getThreadState(threadId: string): Record<string, unknown> | null {\n    const events = this.getThreadEvents(threadId);\n    // Walk backwards — the last snapshot wins.\n    for (let i = events.length - 1; i >= 0; i--) {\n      const event = events[i]!;\n      if (event.type === EventType.STATE_SNAPSHOT) {\n        const snapshot = (event as StateSnapshotEvent).snapshot;\n        // Only plain objects satisfy the Record<string, unknown> contract.\n        // `typeof [] === \"object\"` is true, so arrays must be rejected\n        // explicitly to avoid returning an array typed as a Record.\n        if (\n          snapshot &&\n          typeof snapshot === \"object\" &&\n          !Array.isArray(snapshot)\n        ) {\n          // Return a defensive shallow copy so callers can't mutate the\n          // snapshot object held inside the stored event (matches the\n          // getThreadMessages defensive-copy approach).\n          return { ...(snapshot as Record<string, unknown>) };\n        }\n        return null;\n      }\n    }\n    return null;\n  }\n\n  /**\n   * Clears all in-memory thread history.\n   *\n   * Powers the local-dev fallback for `POST /threads/clear`, letting consumers\n   * (e.g. the demo's Clear button) reset to an empty thread list without\n   * restarting the runtime. Intentionally not exposed on the Intelligence\n   * platform path: there, thread history lives in a real database and must\n   * not be wiped this way.\n   */\n  clearThreads(): void {\n    sharedStore.clear();\n  }\n}\n"],"mappings":"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"}