{"version":3,"file":"memory.mjs","names":[],"sources":["../../../../../../../ai/src/checkpoint/memory.ts"],"sourcesContent":["import type {\n  CheckpointRecord,\n  CheckpointStore,\n} from \"../contracts/orchestrator/checkpoint-store.contract\";\n\n/**\n * Composite key for the in-memory session index — one bucket per\n * `(orchestratorName, sessionId)` pair. The separator can't appear in\n * the segments in practice (names are identifiers, sessionIds are\n * dev-owned opaque strings), so a simple join is collision-safe enough\n * for an in-process store.\n */\nfunction bucketKey(orchestratorName: string, sessionId: string): string {\n  return `${orchestratorName}\u0000${sessionId}`;\n}\n\n/**\n * In-memory {@link CheckpointStore} — append-only session checkpoints\n * held in a process-local `Map`, never persisted to disk.\n *\n * Owns: the per-session bucket of {@link CheckpointRecord} rows and the\n * \"latest turn wins\" load semantics. Does NOT own: durability,\n * cross-process sharing, or TTL eviction — it is the zero-config\n * default for dev, tests, and single-process apps that don't need\n * resume across restarts. Reach for `ai.checkpoint.pg()` /\n * `ai.checkpoint.redis()` (Phase 2) when durability matters.\n *\n * Front it with the {@link memory} factory — callers never `new` it.\n */\nclass MemoryCheckpointStore implements CheckpointStore {\n  /** Per-session append-only row buckets, keyed by `(orchestratorName, sessionId)`. */\n  private readonly sessions = new Map<string, CheckpointRecord[]>();\n\n  /**\n   * Return the latest checkpoint (highest `turn_index`) for a session,\n   * or `undefined` when the session has no rows. Rows are appended in\n   * turn order, so the last element is the latest — no scan needed.\n   */\n  public async load(\n    orchestratorName: string,\n    sessionId: string,\n  ): Promise<CheckpointRecord | undefined> {\n    const rows = this.sessions.get(bucketKey(orchestratorName, sessionId));\n\n    if (!rows || rows.length === 0) {\n      return undefined;\n    }\n\n    return rows[rows.length - 1];\n  }\n\n  /**\n   * Append a checkpoint row to its session bucket. Append-only — an\n   * existing `turn_index` is never overwritten; a fresh row is pushed.\n   */\n  public async save(record: CheckpointRecord): Promise<void> {\n    const key = bucketKey(record.orchestrator_name, record.session_id);\n    const rows = this.sessions.get(key);\n\n    if (rows) {\n      rows.push(record);\n\n      return;\n    }\n\n    this.sessions.set(key, [record]);\n  }\n\n  /**\n   * Drop every row for a session, ending it.\n   */\n  public async delete(\n    orchestratorName: string,\n    sessionId: string,\n  ): Promise<void> {\n    this.sessions.delete(bucketKey(orchestratorName, sessionId));\n  }\n\n  /**\n   * List the session ids known for an orchestrator, optionally filtered\n   * by a session-id prefix. Reads the latest row of each bucket so the\n   * authoritative `session_id` is returned even if the key encoding\n   * ever changes.\n   */\n  public async list(\n    orchestratorName: string,\n    prefix?: string,\n  ): Promise<string[]> {\n    const sessionIds: string[] = [];\n\n    for (const rows of this.sessions.values()) {\n      const latest = rows[rows.length - 1];\n\n      if (!latest || latest.orchestrator_name !== orchestratorName) {\n        continue;\n      }\n\n      if (prefix !== undefined && !latest.session_id.startsWith(prefix)) {\n        continue;\n      }\n\n      sessionIds.push(latest.session_id);\n    }\n\n    return sessionIds;\n  }\n\n  /**\n   * Drop every row for a session whose `turn_index` is strictly below\n   * `keepBeforeTurnIndex`, enforcing the `keepSnapshots` retention bound\n   * (§4 Phase 6, Q20). Rebuilds the bucket in place with the surviving\n   * tail; an emptied bucket is removed so `list()` no longer reports the\n   * session.\n   */\n  public async prune(\n    orchestratorName: string,\n    sessionId: string,\n    keepBeforeTurnIndex: number,\n  ): Promise<void> {\n    const key = bucketKey(orchestratorName, sessionId);\n    const rows = this.sessions.get(key);\n\n    if (!rows) {\n      return;\n    }\n\n    const kept = rows.filter((row) => row.turn_index >= keepBeforeTurnIndex);\n\n    if (kept.length === 0) {\n      this.sessions.delete(key);\n\n      return;\n    }\n\n    if (kept.length !== rows.length) {\n      this.sessions.set(key, kept);\n    }\n  }\n\n  /**\n   * The memory store has no backing table — there is nothing to\n   * migrate. Returns an empty string so callers can treat `schema()`\n   * uniformly across drivers.\n   */\n  public schema(): string {\n    return \"\";\n  }\n}\n\n/**\n * Create an in-memory {@link CheckpointStore}. Zero-config — no client,\n * no connection. Suitable for dev, tests, and single-process apps that\n * don't need resume across restarts.\n *\n * @example\n * import { ai } from \"@warlock.js/ai\";\n *\n * const orchestrator = ai.orchestrator({\n *   name: \"support\",\n *   intents: { ... },\n *   checkpointStore: ai.checkpoint.memory(),\n * });\n */\nexport function memory(): CheckpointStore {\n  return new MemoryCheckpointStore();\n}\n"],"mappings":";;;;;;;;AAYA,SAAS,UAAU,kBAA0B,WAA2B;CACtE,OAAO,GAAG,iBAAiB,GAAG;AAChC;;;;;;;;;;;;;;AAeA,IAAM,wBAAN,MAAuD;;kCAEzB,IAAI,IAAgC;;;;;;;CAOhE,MAAa,KACX,kBACA,WACuC;EACvC,MAAM,OAAO,KAAK,SAAS,IAAI,UAAU,kBAAkB,SAAS,CAAC;EAErE,IAAI,CAAC,QAAQ,KAAK,WAAW,GAC3B;EAGF,OAAO,KAAK,KAAK,SAAS;CAC5B;;;;;CAMA,MAAa,KAAK,QAAyC;EACzD,MAAM,MAAM,UAAU,OAAO,mBAAmB,OAAO,UAAU;EACjE,MAAM,OAAO,KAAK,SAAS,IAAI,GAAG;EAElC,IAAI,MAAM;GACR,KAAK,KAAK,MAAM;GAEhB;EACF;EAEA,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,CAAC;CACjC;;;;CAKA,MAAa,OACX,kBACA,WACe;EACf,KAAK,SAAS,OAAO,UAAU,kBAAkB,SAAS,CAAC;CAC7D;;;;;;;CAQA,MAAa,KACX,kBACA,QACmB;EACnB,MAAM,aAAuB,CAAC;EAE9B,KAAK,MAAM,QAAQ,KAAK,SAAS,OAAO,GAAG;GACzC,MAAM,SAAS,KAAK,KAAK,SAAS;GAElC,IAAI,CAAC,UAAU,OAAO,sBAAsB,kBAC1C;GAGF,IAAI,WAAW,UAAa,CAAC,OAAO,WAAW,WAAW,MAAM,GAC9D;GAGF,WAAW,KAAK,OAAO,UAAU;EACnC;EAEA,OAAO;CACT;;;;;;;;CASA,MAAa,MACX,kBACA,WACA,qBACe;EACf,MAAM,MAAM,UAAU,kBAAkB,SAAS;EACjD,MAAM,OAAO,KAAK,SAAS,IAAI,GAAG;EAElC,IAAI,CAAC,MACH;EAGF,MAAM,OAAO,KAAK,QAAQ,QAAQ,IAAI,cAAc,mBAAmB;EAEvE,IAAI,KAAK,WAAW,GAAG;GACrB,KAAK,SAAS,OAAO,GAAG;GAExB;EACF;EAEA,IAAI,KAAK,WAAW,KAAK,QACvB,KAAK,SAAS,IAAI,KAAK,IAAI;CAE/B;;;;;;CAOA,AAAO,SAAiB;EACtB,OAAO;CACT;AACF;;;;;;;;;;;;;;;AAgBA,SAAgB,SAA0B;CACxC,OAAO,IAAI,sBAAsB;AACnC"}