{"version":3,"file":"filesystem-versioned-8Ag_Np8l.cjs","names":["MastraBase","MastraBase","#runInit","StorageDomain"],"sources":["../src/storage/domains/thread-state/base.ts","../src/storage/domains/thread-state/inmemory.ts","../src/storage/base.ts","../src/storage/domains/versioned.ts","../src/storage/git-history.ts","../src/storage/source-control.ts","../src/storage/filesystem-versioned.ts"],"sourcesContent":["import { MastraBase } from '../../../base';\nimport type { PruneOptions, PruneResult, RetentionTablesDescriptor, TableRetentionPolicy } from '../../retention';\n\n/**\n * A single task in an agent's structured task list.\n *\n * Mirrors the task shape used by the built-in task tools. Kept as a plain,\n * self-contained type so the storage domain does not depend on the tools\n * package.\n */\nexport interface TaskRecord {\n  id: string;\n  content: string;\n  status: 'pending' | 'in_progress' | 'completed';\n  activeForm: string;\n}\n\n/**\n * A durable goal objective for an agent thread.\n *\n * Stored in the thread-state domain under `type: 'goal'`. The objective drives\n * the in-loop goal scorer (the agent keeps working until the goal is judged\n * complete or the run budget is exhausted). Goal settings are optional: when\n * absent here they fall back to the Agent's `goal` config at read time, so an\n * objective only persists the settings a caller explicitly provided.\n * `activeDurationMs` is persisted accounting data rather than a goal setting;\n * when absent, consumers treat it as zero. `judgeModelId` is required at runtime\n * for the goal to do anything — when neither this record nor the Agent's `goal.judge`\n * resolves a judge model, the goal step is a no-op.\n */\nexport interface GoalObjectiveRecord {\n  /** Stable objective id, used for per-goal judge memory and UI correlation. */\n  id?: string;\n  /** The prose objective the agent is working toward. */\n  objective: string;\n  status: 'active' | 'paused' | 'done';\n  /** Number of goal evaluations consumed so far. */\n  runsUsed: number;\n  /** Accumulated active-pursuit time in milliseconds. Missing values represent zero. */\n  activeDurationMs?: number;\n  /** Max evaluations before the goal stops. Falls back to agent `goal.maxRuns` (default 50). */\n  maxRuns?: number;\n  /** Judge model id. Falls back to agent `goal.judge`; if neither resolves the goal is a no-op. */\n  judgeModelId?: string;\n  /** Extra judge guidance. Falls back to agent `goal.prompt` (default = built-in goal judge prompt). */\n  prompt?: string;\n  /**\n   * Why the objective is parked (`status === 'paused'`). Set for judge failure\n   * or budget exhaustion. Unset for `active`/`done`.\n   */\n  pausedReason?: string;\n  startedAt: number;\n  updatedAt: number;\n}\n\n/**\n * Abstract base class for the thread-state storage domain.\n *\n * The thread-state domain holds arbitrary, durable, per-thread state keyed by a\n * `type` namespace. Each `(threadId, type)` pair owns one value. Today the only\n * types are `'task'` (the structured task list managed by the built-in task\n * tools) and `'goal'` (the durable {@link GoalObjectiveRecord} that drives the\n * in-loop goal scorer). The domain is intentionally generic so other\n * agent-scoped state can be tracked the same way without a new domain.\n *\n * The built-in task tools read/write the `'task'` slot synchronously within a\n * run (so a `task_update` sees the tasks a prior `task_write` produced), and the\n * task state processor reads it to project the list onto the agent state-signal\n * lane.\n */\nexport abstract class ThreadStateStorage extends MastraBase {\n  /**\n   * Declares which of this domain's tables are eligible for age-based retention.\n   * Adapters that support retention override this; the default is empty.\n   */\n  static readonly retentionTables: RetentionTablesDescriptor = {};\n\n  constructor() {\n    super({\n      component: 'STORAGE',\n      name: 'THREAD_STATE',\n    });\n  }\n\n  /**\n   * Delete rows older than each policy's `maxAge`, batched, bounded, and\n   * cancellable. Default implementation is a no-op (retention not supported).\n   */\n  async prune(_policies: Record<string, TableRetentionPolicy>, _options?: PruneOptions): Promise<PruneResult[]> {\n    return [];\n  }\n\n  /**\n   * Initialize the thread-state store (create tables, indexes, etc).\n   */\n  abstract init(): Promise<void>;\n\n  /**\n   * Get the state value for a `(threadId, type)` pair. Returns `undefined` when\n   * no value has been set.\n   */\n  abstract getState<T = unknown>(args: { threadId: string; type: string }): Promise<T | undefined>;\n\n  /**\n   * Set the state value for a `(threadId, type)` pair. Full-replacement\n   * semantics: the stored value becomes exactly `value`.\n   */\n  abstract setState<T = unknown>(args: { threadId: string; type: string; value: T }): Promise<void>;\n\n  /**\n   * Delete the state value for a `(threadId, type)` pair.\n   */\n  abstract deleteState(args: { threadId: string; type: string }): Promise<void>;\n\n  /**\n   * Delete all thread state. Used for testing.\n   */\n  abstract dangerouslyClearAll(): Promise<void>;\n}\n","import { ThreadStateStorage } from './base';\n\nfunction clone<T>(value: T): T {\n  return value === undefined ? value : (structuredClone(value) as T);\n}\n\n/**\n * In-memory implementation of {@link ThreadStateStorage}.\n *\n * Holds each thread's state in a `Map<threadId, Map<type, value>>`. Stored\n * values are cloned on read and write so callers cannot mutate the backing\n * value.\n *\n * This is the default thread-state store wired by the composite store: task\n * tracking works out of the box without a configured backend. It is **not**\n * durable across process restarts — configure a durable backend (e.g.\n * `@mastra/libsql`) for state that must survive a restart.\n */\nexport class InMemoryThreadStateStorage extends ThreadStateStorage {\n  private readonly stateByThread = new Map<string, Map<string, unknown>>();\n\n  async init(): Promise<void> {\n    // No-op for in-memory store.\n  }\n\n  async getState<T = unknown>({ threadId, type }: { threadId: string; type: string }): Promise<T | undefined> {\n    const value = this.stateByThread.get(threadId)?.get(type);\n    return value === undefined ? undefined : clone(value as T);\n  }\n\n  async setState<T = unknown>({ threadId, type, value }: { threadId: string; type: string; value: T }): Promise<void> {\n    let byType = this.stateByThread.get(threadId);\n    if (!byType) {\n      byType = new Map<string, unknown>();\n      this.stateByThread.set(threadId, byType);\n    }\n    byType.set(type, clone(value));\n  }\n\n  async deleteState({ threadId, type }: { threadId: string; type: string }): Promise<void> {\n    const byType = this.stateByThread.get(threadId);\n    if (!byType) return;\n    byType.delete(type);\n    if (byType.size === 0) this.stateByThread.delete(threadId);\n  }\n\n  async dangerouslyClearAll(): Promise<void> {\n    this.stateByThread.clear();\n  }\n}\n","import { MastraBase } from '../base';\n\nimport type {\n  AgentsStorage,\n  PromptBlocksStorage,\n  ScorerDefinitionsStorage,\n  MCPClientsStorage,\n  MCPServersStorage,\n  WorkspacesStorage,\n  SkillsStorage,\n  FavoritesStorage,\n  ScoresStorage,\n  WorkflowsStorage,\n  MemoryStorage,\n  ObservabilityStorage,\n  BlobStore,\n  DatasetsStorage,\n  ExperimentsStorage,\n  BackgroundTasksStorage,\n  SchedulesStorage,\n  ChannelsStorage,\n  HarnessStorage,\n  ToolProviderConnectionsStorage,\n  NotificationsStorage,\n  ThreadStateStorage,\n  WorkflowDefinitionsStorage,\n} from './domains';\nimport { InMemoryThreadStateStorage } from './domains/thread-state/inmemory';\nimport type { PruneOptions, PruneResult, RetentionConfig, TableRetentionPolicy } from './retention';\n\n/** Map of all storage domain interfaces available in a composite store. */\nexport type StorageDomains = {\n  workflows?: WorkflowsStorage;\n  workflowDefinitions?: WorkflowDefinitionsStorage;\n  scores?: ScoresStorage;\n  memory?: MemoryStorage;\n  channels?: ChannelsStorage;\n  notifications?: NotificationsStorage;\n  observability?: ObservabilityStorage;\n  agents?: AgentsStorage;\n  datasets?: DatasetsStorage;\n  experiments?: ExperimentsStorage;\n  promptBlocks?: PromptBlocksStorage;\n  scorerDefinitions?: ScorerDefinitionsStorage;\n  mcpClients?: MCPClientsStorage;\n  mcpServers?: MCPServersStorage;\n  workspaces?: WorkspacesStorage;\n  skills?: SkillsStorage;\n  favorites?: FavoritesStorage;\n  blobs?: BlobStore;\n  backgroundTasks?: BackgroundTasksStorage;\n  schedules?: SchedulesStorage;\n  harness?: HarnessStorage;\n  toolProviderConnections?: ToolProviderConnectionsStorage;\n  threadState?: ThreadStateStorage;\n};\n\n/**\n * Domain keys used by the Mastra Editor.\n * Used by the `editor` shorthand on MastraCompositeStoreConfig to route\n * all editor-related domains to a single store.\n */\nexport const EDITOR_DOMAINS = [\n  'agents',\n  'promptBlocks',\n  'scorerDefinitions',\n  'mcpClients',\n  'mcpServers',\n  'workspaces',\n  'skills',\n  'favorites',\n  'toolProviderConnections',\n] as const satisfies ReadonlyArray<keyof StorageDomains>;\n\n/**\n * Normalizes perPage input for pagination queries.\n *\n * @param perPageInput - The raw perPage value from the user\n * @param defaultValue - The default perPage value to use when undefined (typically 40 for messages, 100 for threads)\n * @returns A numeric perPage value suitable for queries (false becomes MAX_SAFE_INTEGER)\n * @throws Error if perPage is a negative number\n */\nexport function normalizePerPage(perPageInput: number | false | undefined, defaultValue: number): number {\n  if (perPageInput === false) {\n    return Number.MAX_SAFE_INTEGER; // Get all results\n  } else if (perPageInput === 0) {\n    return 0; // Return zero results\n  } else if (typeof perPageInput === 'number' && perPageInput > 0) {\n    return perPageInput; // Valid positive number\n  } else if (typeof perPageInput === 'number' && perPageInput < 0) {\n    throw new Error('perPage must be >= 0');\n  }\n  // For undefined, use default\n  return defaultValue;\n}\n\n/**\n * Calculates pagination offset and prepares perPage value for response.\n * When perPage is false (fetch all), offset is always 0 regardless of page.\n *\n * @param page - The page number (0-indexed)\n * @param perPageInput - The original perPage input (number, false for all, or undefined)\n * @param normalizedPerPage - The normalized perPage value (from normalizePerPage)\n * @returns Object with offset for query and perPage for response\n */\nexport function calculatePagination(\n  page: number,\n  perPageInput: number | false | undefined,\n  normalizedPerPage: number,\n): { offset: number; perPage: number | false } {\n  return {\n    offset: perPageInput === false ? 0 : page * normalizedPerPage,\n    perPage: perPageInput === false ? false : normalizedPerPage,\n  };\n}\n\n/**\n * Configuration for individual domain overrides.\n * Each domain can be sourced from a different storage adapter.\n *\n * Set a domain to `false` to disable it entirely: the domain resolves to\n * `undefined` instead of falling back to the `editor`/`default` stores, so\n * nothing can read from or write to it through this composite.\n */\nexport type MastraStorageDomains = {\n  [K in keyof StorageDomains]?: StorageDomains[K] | false;\n};\n\n/**\n * Configuration options for MastraCompositeStore.\n *\n * Can be used in two ways:\n * 1. By store implementations: `{ id, name, disableInit? }` - stores set `this.stores` directly\n * 2. For composition: `{ id, default?, domains?, disableInit? }` - compose domains from multiple stores\n */\nexport interface MastraCompositeStoreConfig {\n  /**\n   * Unique identifier for this storage instance.\n   */\n  id: string;\n\n  /**\n   * Name of the storage adapter (used for logging).\n   * Required for store implementations extending MastraCompositeStore.\n   */\n  name?: string;\n\n  /**\n   * Default storage adapter to use for domains not explicitly specified.\n   * If provided, domains from this storage will be used as fallbacks.\n   */\n  default?: MastraCompositeStore;\n\n  /**\n   * Storage adapter for editor-related domains (agents, promptBlocks, scorerDefinitions,\n   * mcpClients, mcpServers, workspaces, skills).\n   *\n   * This is a shorthand that routes all editor domains to a single store instead of\n   * specifying each individually in `domains`. Useful for filesystem-based storage\n   * where editor configs are stored as JSON files in the repository.\n   *\n   * Priority: domains > editor > default\n   *\n   * @example\n   * ```typescript\n   * new MastraCompositeStore({\n   *   id: 'my-store',\n   *   default: postgresStore,\n   *   editor: filesystemStore,\n   * })\n   * ```\n   */\n  editor?: MastraCompositeStore;\n\n  /**\n   * Individual domain overrides. Each domain can come from a different storage adapter.\n   * These take precedence over both `editor` and `default` storage.\n   *\n   * @example\n   * ```typescript\n   * domains: {\n   *   memory: pgStore.stores?.memory,\n   *   workflows: libsqlStore.stores?.workflows,\n   * }\n   * ```\n   */\n  domains?: MastraStorageDomains;\n\n  /**\n   * When true, automatic initialization (table creation/migrations) is disabled.\n   * This is useful for CI/CD pipelines where you want to:\n   * 1. Run migrations explicitly during deployment (not at runtime)\n   * 2. Use different credentials for schema changes vs runtime operations\n   *\n   * When disableInit is true:\n   * - The storage will not automatically create/alter tables on first use\n   * - You must call `storage.init()` explicitly in your CI/CD scripts\n   *\n   * @example\n   * // In CI/CD script:\n   * const storage = new PostgresStore({ ...config, disableInit: false });\n   * await storage.init(); // Explicitly run migrations\n   *\n   * // In runtime application:\n   * const storage = new PostgresStore({ ...config, disableInit: true });\n   * // No auto-init, tables must already exist\n   */\n  disableInit?: boolean;\n\n  /**\n   * Opt-in, table-granular, age-based retention policies.\n   *\n   * Declare per-domain, per-table `maxAge` policies; call `storage.prune()`\n   * to delete rows older than their configured age. Anything left unset is\n   * kept forever (no behavior change by default).\n   *\n   * @example\n   * ```typescript\n   * retention: {\n   *   memory: {\n   *     messages: { maxAge: '30d' },\n   *     threads: { maxAge: '90d' },\n   *   },\n   *   observability: {\n   *     spans: { maxAge: '7d' },\n   *   },\n   * }\n   * ```\n   */\n  retention?: RetentionConfig;\n}\n\n/**\n * Base class for all Mastra storage adapters.\n *\n * Can be used in two ways:\n *\n * 1. **Extended by store implementations** (PostgresStore, LibSQLStore, etc.):\n *    Store implementations extend this class and set `this.stores` with their domain implementations.\n *\n * 2. **Directly instantiated for composition**:\n *    Compose domains from multiple storage backends using `default` and `domains` options.\n *\n * All domain-specific operations should be accessed through `getStore()`:\n *\n * @example\n * ```typescript\n * // Composition: mix domains from different stores\n * const storage = new MastraCompositeStore({\n *   id: 'composite',\n *   default: pgStore,\n *   domains: {\n *     memory: libsqlStore.stores?.memory,\n *   },\n * });\n *\n * // Use `editor` shorthand to route all editor domains to a filesystem store\n * const storage2 = new MastraCompositeStore({\n *   id: 'with-fs-editor',\n *   default: pgStore,\n *   editor: filesystemStore,\n * });\n *\n * // Access domains\n * const memory = await storage.getStore('memory');\n * await memory?.saveThread({ thread });\n * ```\n */\n/**\n * Minimal interface a storage adapter sees from the Mastra instance.\n * Kept narrow on purpose to avoid pulling the full Mastra type into the\n * storage layer (which would create a circular import).\n */\nexport interface StorageMastraRef {\n  getAgentById?: (id: string) => { source?: string; __getEditorConfig?: () => unknown } | undefined;\n  listAgents?: () => Record<string, { id: string; source?: string; __getEditorConfig?: () => unknown }> | undefined;\n  getEditor?: () => { getSource?: () => 'code' | 'db' | undefined } | undefined;\n}\n\n/** A domain that implements the age-based retention `prune()` contract. */\ninterface PruneCapable {\n  prune(policies: Record<string, TableRetentionPolicy>, options?: PruneOptions): Promise<PruneResult[]>;\n}\n\nfunction isPruneCapable(value: unknown): value is PruneCapable {\n  return typeof value === 'object' && value !== null && typeof (value as PruneCapable).prune === 'function';\n}\n\nexport class MastraCompositeStore extends MastraBase {\n  protected hasInitialized: null | Promise<boolean> = null;\n  protected shouldCacheInit = true;\n\n  id: string;\n  stores?: StorageDomains;\n  protected mastra?: StorageMastraRef;\n\n  /**\n   * When true, automatic initialization (table creation/migrations) is disabled.\n   */\n  disableInit: boolean = false;\n\n  /**\n   * Opt-in, table-granular, age-based retention policies. Consumed by\n   * `prune()`. Undefined means nothing is pruned (keep forever).\n   */\n  protected retention?: RetentionConfig;\n\n  /**\n   * Retained references to the parent stores supplied via composition. `init()`\n   * delegates to these so the parent's own `init()` logic (pragmas, ordered\n   * DDL, init coalescing, etc.) runs instead of being bypassed by the\n   * composite iterating the inner domains in parallel — which was the cause\n   * of the SQLITE_BUSY / \"no such table\" races reported in issue #16782.\n   */\n  protected parentDefault?: MastraCompositeStore;\n  protected parentEditor?: MastraCompositeStore;\n\n  constructor(config: MastraCompositeStoreConfig) {\n    const name = config.name ?? 'MastraCompositeStore';\n\n    if (!config.id || typeof config.id !== 'string' || config.id.trim() === '') {\n      throw new Error(`${name}: id must be provided and cannot be empty.`);\n    }\n\n    super({\n      component: 'STORAGE',\n      name,\n    });\n\n    this.id = config.id;\n    this.disableInit = config.disableInit ?? false;\n    this.retention = config.retention;\n\n    // If composition config is provided (default, editor, or domains), compose the stores\n    if (config.default || config.editor || config.domains) {\n      const defaultStores = config.default?.stores;\n      const editorStores = config.editor?.stores;\n      const domainOverrides = config.domains ?? {};\n\n      // Retain the parent store refs so init() can delegate to their own\n      // init() — see field doc above and init() below.\n      this.parentDefault = config.default;\n      this.parentEditor = config.editor;\n\n      // Validate that at least one storage source is provided (a `false`\n      // override disables a domain, so it doesn't count as a source)\n      const hasDefaultDomains = defaultStores && Object.values(defaultStores).some(v => v !== undefined);\n      const hasEditorDomains = editorStores && Object.values(editorStores).some(v => v !== undefined);\n      const hasOverrideDomains = Object.values(domainOverrides).some(v => v !== undefined && v !== false);\n\n      if (!hasDefaultDomains && !hasEditorDomains && !hasOverrideDomains) {\n        throw new Error(\n          'MastraCompositeStore requires at least one storage source. Provide a default storage, an editor storage, or domain overrides.',\n        );\n      }\n\n      const editorDomainSet = new Set<string>(EDITOR_DOMAINS);\n\n      // Helper: resolve a domain with priority: domains > editor (for editor domains) > default.\n      // A `false` override disables the domain — it resolves to undefined\n      // instead of falling through to the editor/default stores.\n      const resolve = <K extends keyof StorageDomains>(key: K): StorageDomains[K] | undefined => {\n        const override: StorageDomains[K] | false | undefined = domainOverrides[key];\n        if (override === false) return undefined;\n        if (override !== undefined) return override;\n        if (editorDomainSet.has(key) && editorStores?.[key] !== undefined) return editorStores[key];\n        return defaultStores?.[key];\n      };\n\n      // Build the composed stores object\n      this.stores = {\n        memory: resolve('memory'),\n        workflows: resolve('workflows'),\n        workflowDefinitions: resolve('workflowDefinitions'),\n        scores: resolve('scores'),\n        observability: resolve('observability'),\n        agents: resolve('agents'),\n        datasets: resolve('datasets'),\n        experiments: resolve('experiments'),\n        promptBlocks: resolve('promptBlocks'),\n        scorerDefinitions: resolve('scorerDefinitions'),\n        mcpClients: resolve('mcpClients'),\n        mcpServers: resolve('mcpServers'),\n        workspaces: resolve('workspaces'),\n        skills: resolve('skills'),\n        favorites: resolve('favorites'),\n        blobs: resolve('blobs'),\n        backgroundTasks: resolve('backgroundTasks'),\n        schedules: resolve('schedules'),\n        channels: resolve('channels'),\n        harness: resolve('harness'),\n        toolProviderConnections: resolve('toolProviderConnections'),\n        notifications: resolve('notifications'),\n        // The thread-state domain always has an in-memory store wired by default\n        // so the built-in task tools work out of the box without a configured\n        // backend. Configure a durable backend for state that must survive a\n        // process restart. An explicit `false` override still disables the\n        // domain entirely — the in-memory fallback only applies when the\n        // domain is left unset.\n        threadState:\n          domainOverrides.threadState === false\n            ? undefined\n            : (resolve('threadState') ?? new InMemoryThreadStateStorage()),\n      } as StorageDomains;\n    }\n    // Otherwise, subclasses set stores themselves\n  }\n\n  /**\n   * Register the Mastra instance with this storage adapter and cascade the\n   * reference to all owned domain stores and parent composites. Storage\n   * adapters that need to look up agents, editor config, etc. can read\n   * `this.mastra` after this is called.\n   * @internal\n   */\n  __registerMastra(mastra: StorageMastraRef, seen: Set<unknown> = new Set<unknown>()): void {\n    if (seen.has(this)) return;\n    seen.add(this);\n    this.mastra = mastra;\n    const cascade = (target: unknown) => {\n      if (!target || typeof target !== 'object' || seen.has(target)) return;\n      const fn = (target as { __registerMastra?: (m: StorageMastraRef, s?: Set<unknown>) => void }).__registerMastra;\n      if (typeof fn === 'function') {\n        fn.call(target, mastra, seen);\n      } else {\n        seen.add(target);\n      }\n    };\n    if (this.parentDefault) cascade(this.parentDefault);\n    if (this.parentEditor) cascade(this.parentEditor);\n    if (this.stores) {\n      for (const domain of Object.values(this.stores)) cascade(domain);\n    }\n  }\n\n  /**\n   * Get a domain-specific storage interface.\n   *\n   * @param storeName - The name of the domain to access ('memory', 'workflows', 'scores', 'observability', 'agents')\n   * @returns The domain storage interface, or undefined if not available\n   *\n   * @example\n   * ```typescript\n   * const memory = await storage.getStore('memory');\n   * if (memory) {\n   *   await memory.saveThread({ thread });\n   * }\n   * ```\n   */\n  async getStore<K extends keyof StorageDomains>(storeName: K): Promise<StorageDomains[K] | undefined> {\n    return this.stores?.[storeName];\n  }\n\n  /**\n   * Delete rows older than their configured `maxAge` across all domains that\n   * have a policy declared in `retention`.\n   *\n   * Prune is safe at scale: each domain deletes in bounded, batched, resumable,\n   * cancellable chunks (see {@link PruneOptions}). It only deletes rows. On\n   * SQLite/LibSQL freed pages are reused by future writes so the file stops\n   * growing; handing disk back to the OS is left to the underlying database and\n   * the operator to manage.\n   *\n   * Returns one {@link PruneResult} per table touched. A result with\n   * `done: false` means eligible rows remain — call `prune()` again (e.g. on\n   * the next cron tick) to continue.\n   *\n   * Prune is meant to run unattended (a cron tick), so a failure in one\n   * domain is logged and skipped rather than rejecting the whole call — the\n   * results already gathered for other domains are still returned, and the\n   * failed domain is retried naturally on the next tick.\n   *\n   * With no `retention` configured this is a no-op returning `[]`.\n   *\n   * Pass `options.retention` to replace the configured retention policies for\n   * this call only — e.g. to skip a domain (keep chat history) or prune more\n   * aggressively than the standing config without reconstructing the store.\n   */\n  async prune(options?: PruneOptions): Promise<PruneResult[]> {\n    const retention = options?.retention ?? this.retention;\n    if (!retention) return [];\n\n    const results: PruneResult[] = [];\n    for (const [domainKey, tablePolicies] of Object.entries(retention) as [\n      keyof StorageDomains,\n      Record<string, TableRetentionPolicy> | undefined,\n    ][]) {\n      if (options?.signal?.aborted) break;\n      if (!tablePolicies || Object.keys(tablePolicies).length === 0) continue;\n\n      const domain = this.stores?.[domainKey];\n      if (!isPruneCapable(domain)) continue; // domain not configured / doesn't support retention\n\n      try {\n        const domainResults = await domain.prune(tablePolicies, options);\n        results.push(...domainResults);\n      } catch (error) {\n        this.logger?.error(`prune() failed for domain \"${domainKey}\"`, { error });\n      }\n    }\n    return results;\n  }\n\n  /**\n   * Initialize all domain stores.\n   *\n   * When a parent store was supplied via `default` or `editor`, delegate to\n   * its own `init()` first. Each adapter owns its `init()` contract — it may\n   * apply connection-level setup, run migrations, enforce DDL ordering, or\n   * coalesce concurrent callers. Calling each domain's `init()` directly\n   * against the parent's shared client would bypass all of that and can\n   * corrupt or partially create schema (see issue #16782 for the SQLite\n   * symptom).\n   *\n   * Any remaining domains that did NOT come from a parent (e.g. supplied via\n   * the explicit `domains` override pointing at a different store) are then\n   * initialized individually — but only the ones the parents didn't already\n   * cover, so we never double-init the same domain instance.\n   */\n  async init(): Promise<void> {\n    if (!this.shouldCacheInit) {\n      await this.#runInit();\n      return;\n    }\n\n    if (this.hasInitialized) {\n      await this.hasInitialized;\n      return;\n    }\n\n    const initPromise = this.#runInit().catch(error => {\n      if (this.hasInitialized === initPromise) {\n        this.hasInitialized = null;\n      }\n      throw error;\n    });\n    this.hasInitialized = initPromise;\n    await initPromise;\n  }\n\n  async #runInit(): Promise<boolean> {\n    // 1. Delegate to parent stores. Each parent owns its own init contract\n    //    (setup, migrations, sequencing, coalescing). Dedupe by identity so\n    //    a store passed as both `default` and `editor` only gets init()'d once.\n    const uniqueParents = new Set<MastraCompositeStore>();\n    if (this.parentDefault) uniqueParents.add(this.parentDefault);\n    if (this.parentEditor) uniqueParents.add(this.parentEditor);\n    await Promise.all([...uniqueParents].map(parent => parent.init()));\n\n    // 2. Build a set of domain instances the parents already initialized so\n    //    we don't init them a second time below.\n    const alreadyInitialized = new Set<unknown>();\n    const addParentDomains = (parent?: MastraCompositeStore) => {\n      if (!parent?.stores) return;\n      for (const domain of Object.values(parent.stores)) {\n        if (domain) alreadyInitialized.add(domain);\n      }\n    };\n    addParentDomains(this.parentDefault);\n    addParentDomains(this.parentEditor);\n\n    // 3. Init any remaining domains (typically those provided via the\n    //    explicit `domains` override pointing at a different store, or those\n    //    set directly by a subclass).\n    const initTasks: Promise<void>[] = [];\n    const maybeInit = (domain: { init(): Promise<void> } | undefined) => {\n      if (!domain || alreadyInitialized.has(domain)) return;\n      initTasks.push(domain.init());\n      alreadyInitialized.add(domain);\n    };\n\n    if (this.stores) {\n      maybeInit(this.stores.memory);\n      maybeInit(this.stores.workflows);\n      maybeInit(this.stores.workflowDefinitions);\n      maybeInit(this.stores.scores);\n      maybeInit(this.stores.observability);\n      maybeInit(this.stores.agents);\n      maybeInit(this.stores.datasets);\n      maybeInit(this.stores.experiments);\n      maybeInit(this.stores.promptBlocks);\n      maybeInit(this.stores.scorerDefinitions);\n      maybeInit(this.stores.mcpClients);\n      maybeInit(this.stores.mcpServers);\n      maybeInit(this.stores.workspaces);\n      maybeInit(this.stores.skills);\n      maybeInit(this.stores.favorites);\n      maybeInit(this.stores.blobs);\n      maybeInit(this.stores.backgroundTasks);\n      maybeInit(this.stores.schedules);\n      maybeInit(this.stores.channels);\n      maybeInit(this.stores.harness);\n      maybeInit(this.stores.toolProviderConnections);\n      maybeInit(this.stores.notifications);\n      maybeInit(this.stores.threadState);\n    }\n\n    await Promise.all(initTasks);\n    return true;\n  }\n  /**\n   * Optional lifecycle hook: release underlying client/connection handles.\n   * Implementations (e.g. LibSQLStore) override this to checkpoint WAL files\n   * and close the database client so OS handles are freed synchronously.\n   * Called automatically by Mastra.shutdown().\n   */\n  close?(): Promise<void>;\n}\n\n/**\n * @deprecated Use MastraCompositeStoreConfig instead. This alias will be removed in a future version.\n */\nexport interface MastraStorageConfig extends MastraCompositeStoreConfig {}\n\n/**\n * @deprecated Use MastraCompositeStore instead. This alias will be removed in a future version.\n */\nexport class MastraStorage extends MastraCompositeStore {}\n","import type { StorageOrderBy, ThreadOrderBy, ThreadSortDirection } from '../types';\nimport { StorageDomain } from './base';\n\n// ============================================================================\n// Version Resolution Options\n// ============================================================================\n\n/**\n * Options for resolving which version of an entity to use.\n * Either pick by status (draft/published/archived) or by a specific version ID — not both.\n */\nexport type VersionResolutionOptions =\n  | { status?: 'draft' | 'published' | 'archived'; versionId?: never }\n  | { versionId: string; status?: never };\n\n// ============================================================================\n// Generic Version Types\n// ============================================================================\n\n/**\n * Base interface for version metadata fields that exist on every version row.\n * The `TFkField` parameter controls the name of the foreign key field.\n */\nexport interface VersionBase {\n  /** UUID identifier for this version */\n  id: string;\n  /** Sequential version number (1, 2, 3, ...) */\n  versionNumber: number;\n  /** Array of field names that changed from the previous version */\n  changedFields?: string[];\n  /** Optional message describing the changes */\n  changeMessage?: string;\n  /** When this version was created */\n  createdAt: Date;\n}\n\n/**\n * Base interface for version creation input.\n * Same as VersionBase but without the server-assigned `createdAt` timestamp.\n */\nexport interface CreateVersionInputBase extends Omit<VersionBase, 'createdAt'> {}\n\n/**\n * Sort direction for version listings.\n */\nexport type VersionSortDirectionGeneric = ThreadSortDirection;\n\n/**\n * Fields that can be used for ordering version listings.\n */\nexport type VersionOrderByGeneric = 'versionNumber' | 'createdAt';\n\n/**\n * Input for listing versions with pagination and sorting.\n */\nexport interface ListVersionsInputBase {\n  /** Page number (0-indexed) */\n  page?: number;\n  /**\n   * Number of items per page, or `false` to fetch all records without pagination limit.\n   * Defaults to 20 if not specified.\n   */\n  perPage?: number | false;\n  /** Sorting options */\n  orderBy?: {\n    field?: VersionOrderByGeneric;\n    direction?: VersionSortDirectionGeneric;\n  };\n}\n\n/**\n * Output for listing versions with pagination info.\n */\nexport interface ListVersionsOutputBase<TVersion> {\n  /** Array of versions for the current page */\n  versions: TVersion[];\n  /** Total number of versions */\n  total: number;\n  /** Current page number */\n  page: number;\n  /** Items per page */\n  perPage: number | false;\n  /** Whether there are more pages */\n  hasMore: boolean;\n}\n\n// ============================================================================\n// Entity base — the \"thin record\" must have these fields\n// ============================================================================\n\nexport interface VersionedEntityBase {\n  id: string;\n  activeVersionId?: string;\n}\n\n// ============================================================================\n// Constants for validation (shared across all versioned domains)\n// ============================================================================\n\nconst ENTITY_ORDER_BY_SET: Record<ThreadOrderBy, true> = {\n  createdAt: true,\n  updatedAt: true,\n};\n\nconst SORT_DIRECTION_SET: Record<ThreadSortDirection, true> = {\n  ASC: true,\n  DESC: true,\n};\n\nconst VERSION_ORDER_BY_SET: Record<VersionOrderByGeneric, true> = {\n  versionNumber: true,\n  createdAt: true,\n};\n\n// ============================================================================\n// VersionedStorageDomain — generic base class\n// ============================================================================\n\n/**\n * Generic base class for versioned storage domains (agents, prompt blocks, scorer definitions).\n *\n * Type parameters:\n * - `TEntity`       — Thin record type (e.g. StorageAgentType)\n * - `TSnapshot`     — Snapshot config type (e.g. StorageAgentSnapshotType)\n * - `TResolved`     — Entity + snapshot merged (e.g. StorageResolvedAgentType)\n * - `TVersion`      — Version row (e.g. AgentVersion)\n * - `TCreateVersion` — Input for creating a version\n * - `TListVersionsInput` — Input for listing versions\n * - `TListVersionsOutput` — Output for listing versions\n * - `TCreateInput`  — Input for creating an entity\n * - `TUpdateInput`  — Input for updating an entity\n * - `TListInput`    — Input for listing entities\n * - `TListOutput`   — Output for listing entities (paginated thin records)\n * - `TListResolvedOutput` — Output for listing resolved entities\n */\nexport abstract class VersionedStorageDomain<\n  TEntity extends VersionedEntityBase,\n  TSnapshot,\n  TResolved extends TEntity,\n  TVersion extends VersionBase,\n  TCreateVersion extends CreateVersionInputBase,\n  TListVersionsInput extends ListVersionsInputBase,\n  TListVersionsOutput extends ListVersionsOutputBase<TVersion>,\n  TCreateInput,\n  TUpdateInput,\n  TListInput,\n  TListOutput,\n  TListResolvedOutput,\n> extends StorageDomain {\n  /**\n   * The key name used in list outputs (e.g. 'agents', 'promptBlocks', 'scorerDefinitions').\n   * Subclasses must provide this so the generic resolution logic can build the correct output shape.\n   */\n  protected abstract readonly listKey: string;\n\n  /**\n   * The set of version metadata field names (including the FK field) to strip\n   * when extracting snapshot config from a version row.\n   * e.g. ['id', 'agentId', 'versionNumber', 'changedFields', 'changeMessage', 'createdAt']\n   */\n  protected abstract readonly versionMetadataFields: string[];\n\n  // ==========================================================================\n  // Entity CRUD (abstract — implemented by concrete store classes)\n  // ==========================================================================\n\n  abstract getById(id: string): Promise<TEntity | null>;\n  abstract create(input: TCreateInput): Promise<TEntity>;\n  abstract update(input: TUpdateInput): Promise<TEntity>;\n  abstract delete(id: string): Promise<void>;\n  abstract list(args?: TListInput): Promise<TListOutput>;\n\n  // ==========================================================================\n  // Version methods (abstract — implemented by concrete store classes)\n  // ==========================================================================\n\n  abstract createVersion(input: TCreateVersion): Promise<TVersion>;\n  abstract getVersion(id: string): Promise<TVersion | null>;\n  abstract getVersionByNumber(entityId: string, versionNumber: number): Promise<TVersion | null>;\n  abstract getLatestVersion(entityId: string): Promise<TVersion | null>;\n  abstract listVersions(input: TListVersionsInput): Promise<TListVersionsOutput>;\n  abstract deleteVersion(id: string): Promise<void>;\n  abstract deleteVersionsByParentId(entityId: string): Promise<void>;\n  abstract countVersions(entityId: string): Promise<number>;\n\n  // ==========================================================================\n  // Concrete resolution methods\n  // ==========================================================================\n\n  /**\n   * Strips version metadata fields from a version row, leaving only snapshot config fields.\n   */\n  protected extractSnapshotConfig(version: TVersion): Partial<TSnapshot> {\n    const result: Record<string, unknown> = {};\n    const metadataSet = new Set(this.versionMetadataFields);\n\n    for (const [key, value] of Object.entries(version)) {\n      if (!metadataSet.has(key)) {\n        result[key] = value;\n      }\n    }\n\n    return result as Partial<TSnapshot>;\n  }\n\n  /**\n   * Resolves an entity by merging its thin record with the active or latest version config.\n   * - `{ status: 'draft' }` — resolve with the latest version.\n   * - `{ status: 'published' }` (default) — resolve with the active version, falling back to latest.\n   * - `{ versionId: '...' }` — resolve with a specific version by ID.\n   */\n  async getByIdResolved(id: string, options?: VersionResolutionOptions): Promise<TResolved | null> {\n    const entity = await this.getById(id);\n\n    if (!entity) {\n      return null;\n    }\n\n    return this.resolveEntity(entity, options);\n  }\n\n  /**\n   * Lists entities with version resolution.\n   * When `status` is `'draft'`, each entity is resolved with its latest version.\n   * When `status` is `'published'` (default), each entity is resolved with its active version.\n   */\n  async listResolved(args?: TListInput): Promise<TListResolvedOutput> {\n    const result = await this.list(args);\n\n    const status = (args as Record<string, unknown> | undefined)?.status as string | undefined;\n    const entities = (result as Record<string, unknown>)[this.listKey] as TEntity[];\n    const resolved = await Promise.all(\n      entities.map(entity => this.resolveEntity(entity, { status: status as 'draft' | 'published' | 'archived' })),\n    );\n\n    return {\n      ...result,\n      [this.listKey]: resolved,\n    } as TListResolvedOutput;\n  }\n\n  /**\n   * Resolves a single entity by merging it with its active or latest version.\n   * - `{ versionId: '...' }` — resolve with a specific version by ID.\n   * - `{ status: 'published' }` (default) — use activeVersionId, fall back to latest.\n   * - `{ status: 'draft' }` — always use the latest version.\n   */\n  protected async resolveEntity(entity: TEntity, options?: VersionResolutionOptions): Promise<TResolved> {\n    const status = options?.status || 'published';\n    let version: TVersion | null = null;\n\n    if (options?.versionId) {\n      // Specific version resolution: fetch by exact version ID\n      version = await this.getVersion(options.versionId);\n    } else if (status === 'draft') {\n      // Draft resolution: always use the latest version (which may be ahead of activeVersionId)\n      version = await this.getLatestVersion(entity.id);\n    } else {\n      // Published/archived resolution: use activeVersionId, fall back to latest\n      if (entity.activeVersionId) {\n        version = await this.getVersion(entity.activeVersionId);\n\n        if (!version) {\n          this.logger?.warn?.(\n            `Entity ${entity.id} has activeVersionId ${entity.activeVersionId} but version not found. Falling back to latest version.`,\n          );\n        }\n      }\n\n      if (!version) {\n        version = await this.getLatestVersion(entity.id);\n      }\n    }\n\n    if (version) {\n      const snapshotConfig = this.extractSnapshotConfig(version);\n      return {\n        ...entity,\n        ...snapshotConfig,\n        resolvedVersionId: version.id,\n      } as unknown as TResolved;\n    }\n\n    return entity as unknown as TResolved;\n  }\n\n  // ==========================================================================\n  // Protected Helper Methods\n  // ==========================================================================\n\n  protected parseOrderBy(\n    orderBy?: StorageOrderBy,\n    defaultDirection: ThreadSortDirection = 'DESC',\n  ): { field: ThreadOrderBy; direction: ThreadSortDirection } {\n    return {\n      field: orderBy?.field && orderBy.field in ENTITY_ORDER_BY_SET ? orderBy.field : 'createdAt',\n      direction: orderBy?.direction && orderBy.direction in SORT_DIRECTION_SET ? orderBy.direction : defaultDirection,\n    };\n  }\n\n  protected parseVersionOrderBy(\n    orderBy?: TListVersionsInput['orderBy'],\n    defaultDirection: VersionSortDirectionGeneric = 'DESC',\n  ): { field: VersionOrderByGeneric; direction: VersionSortDirectionGeneric } {\n    return {\n      field: orderBy?.field && orderBy.field in VERSION_ORDER_BY_SET ? orderBy.field : 'versionNumber',\n      direction: orderBy?.direction && orderBy.direction in SORT_DIRECTION_SET ? orderBy.direction : defaultDirection,\n    };\n  }\n}\n","import { execFile } from 'node:child_process';\nimport { realpathSync } from 'node:fs';\nimport { relative } from 'node:path';\n\n/**\n * A single Git commit entry parsed from `git log` output.\n */\nexport interface GitCommit {\n  /** Full commit SHA */\n  hash: string;\n  /** Commit author date as a Date object */\n  date: Date;\n  /** Author name */\n  author: string;\n  /** Commit subject line */\n  message: string;\n}\n\n/**\n * Read-only utility for reading Git history of filesystem-stored JSON files.\n *\n * All operations are performed by shelling out to the `git` CLI via\n * `child_process.execFile` (no third-party dependencies). This class never\n * writes to Git — the user manages their own commits.\n *\n * Designed as a singleton shared across all domain helpers via a static field\n * on `FilesystemVersionedHelpers`.\n */\nexport class GitHistory {\n  /** Cache: dir → repo root (string) or `false` if not a repo. */\n  private repoRootCache = new Map<string, string | false>();\n\n  /** Cache: `dir:filename:limit` → ordered commits (newest first). */\n  private commitCache = new Map<string, GitCommit[]>();\n\n  /** Cache: `dir:commitHash:filename` → parsed JSON. Stored as unknown because\n   * shared files are `{ [entityId]: snapshot }` while per-entity files are the\n   * snapshot itself. */\n  private snapshotCache = new Map<string, unknown>();\n\n  // ===========================================================================\n  // Public API\n  // ===========================================================================\n\n  /**\n   * Returns `true` if `dir` is inside a Git repository.\n   * Result is cached after the first call per directory.\n   */\n  async isGitRepo(dir: string): Promise<boolean> {\n    const cached = this.repoRootCache.get(dir);\n    if (cached === false) return false;\n    if (typeof cached === 'string') return true;\n\n    try {\n      const root = (await this.exec(dir, ['rev-parse', '--show-toplevel'])).trim();\n      this.repoRootCache.set(dir, root);\n      return true;\n    } catch {\n      this.repoRootCache.set(dir, false);\n      return false;\n    }\n  }\n\n  /**\n   * Get the list of commits that touched a specific file, newest first.\n   * Returns an empty array if Git is unavailable or the file has no history.\n   *\n   * @param dir      Absolute path to the storage directory\n   * @param filename The JSON filename relative to `dir` (e.g., 'agents.json')\n   * @param limit    Maximum number of commits to retrieve\n   */\n  async getFileHistory(dir: string, filename: string, limit: number = 50): Promise<GitCommit[]> {\n    const cacheKey = `${dir}:${filename}:${limit}`;\n    if (this.commitCache.has(cacheKey)) {\n      return this.commitCache.get(cacheKey)!;\n    }\n\n    if (!(await this.isGitRepo(dir))) {\n      this.commitCache.set(cacheKey, []);\n      return [];\n    }\n\n    try {\n      // `filename` is already relative to `dir`, and `exec` runs with `cwd: dir`,\n      // so `git log -- <filename>` resolves correctly.\n      const raw = await this.exec(dir, [\n        'log',\n        `--max-count=${limit}`,\n        '--format=%H|%aI|%aN|%s',\n        '--follow',\n        '--',\n        filename,\n      ]);\n\n      const commits: GitCommit[] = [];\n      for (const line of raw.split('\\n')) {\n        const trimmed = line.trim();\n        if (!trimmed) continue;\n\n        const pipeIdx1 = trimmed.indexOf('|');\n        const pipeIdx2 = trimmed.indexOf('|', pipeIdx1 + 1);\n        const pipeIdx3 = trimmed.indexOf('|', pipeIdx2 + 1);\n\n        if (pipeIdx1 === -1 || pipeIdx2 === -1 || pipeIdx3 === -1) continue;\n\n        commits.push({\n          hash: trimmed.slice(0, pipeIdx1),\n          date: new Date(trimmed.slice(pipeIdx1 + 1, pipeIdx2)),\n          author: trimmed.slice(pipeIdx2 + 1, pipeIdx3),\n          message: trimmed.slice(pipeIdx3 + 1),\n        });\n      }\n\n      this.commitCache.set(cacheKey, commits);\n      return commits;\n    } catch {\n      this.commitCache.set(cacheKey, []);\n      return [];\n    }\n  }\n\n  /**\n   * Read and parse a JSON file at a specific Git commit.\n   * Returns the parsed entity map, or `null` if the file didn't exist at that commit.\n   *\n   * @param dir        Absolute path to the storage directory\n   * @param commitHash Full or abbreviated commit SHA\n   * @param filename   The JSON filename relative to `dir` (e.g., 'agents.json')\n   */\n  async getFileAtCommit<T = Record<string, Record<string, unknown>>>(\n    dir: string,\n    commitHash: string,\n    filename: string,\n  ): Promise<T | null> {\n    const cacheKey = `${dir}:${commitHash}:${filename}`;\n    if (this.snapshotCache.has(cacheKey)) {\n      return this.snapshotCache.get(cacheKey)! as T;\n    }\n\n    if (!(await this.isGitRepo(dir))) return null;\n\n    try {\n      const relPath = this.relativeToRepo(dir, filename);\n      const raw = await this.exec(dir, ['show', `${commitHash}:${relPath}`]);\n      const parsed = JSON.parse(raw);\n      this.snapshotCache.set(cacheKey, parsed);\n      return parsed as T;\n    } catch {\n      return null;\n    }\n  }\n\n  /**\n   * Invalidate all caches. Call after external operations that change Git state\n   * (e.g., the user commits or pulls).\n   */\n  invalidateCache(): void {\n    this.repoRootCache.clear();\n    this.commitCache.clear();\n    this.snapshotCache.clear();\n  }\n\n  // ===========================================================================\n  // Internals\n  // ===========================================================================\n\n  /**\n   * Get the relative path from the Git repo root to a file in the storage directory.\n   */\n  private relativeToRepo(dir: string, filename: string): string {\n    const root = this.repoRootCache.get(dir);\n    if (!root) {\n      throw new Error(`Not a git repository: ${dir}`);\n    }\n    // Resolve symlinks so that macOS /var → /private/var differences don't break relative()\n    const realRoot = realpathSync(root);\n    const realDir = realpathSync(dir);\n    const relDir = relative(realRoot, realDir);\n    return relDir ? `${relDir}/${filename}` : filename;\n  }\n\n  /**\n   * Execute a git command and return stdout.\n   */\n  private exec(cwd: string, args: string[]): Promise<string> {\n    return new Promise((resolve, reject) => {\n      execFile('git', args, { cwd, maxBuffer: 10 * 1024 * 1024 }, (error, stdout) => {\n        if (error) reject(error);\n        else resolve(stdout);\n      });\n    });\n  }\n}\n","export type SourceControlCapabilityReason =\n  | 'provider-not-configured'\n  | 'provider-unavailable'\n  | 'missing-permissions'\n  | 'project-not-linked'\n  | 'unsupported';\n\nexport type SourceControlCapabilities = {\n  canRead: boolean;\n  canWrite: boolean;\n  canListHistory: boolean;\n  canOpenChangeRequest: boolean;\n  reason?: SourceControlCapabilityReason | string;\n};\n\nexport type SourceProviderInfo = {\n  id: string;\n  displayName: string;\n};\n\nexport type SourceFileRef = {\n  path: string;\n  ref?: string;\n};\n\nexport type SourceFile = SourceFileRef & {\n  content: string;\n  sha?: string;\n};\n\nexport type SourceWriteFileInput = SourceFileRef & {\n  content: string;\n  message?: string;\n  expectedSha?: string;\n};\n\nexport type SourceWriteResult = {\n  path: string;\n  ref?: string;\n  sha?: string;\n  commitSha?: string;\n  url?: string;\n};\n\nexport type SourceFileHistoryInput = {\n  path: string;\n  ref?: string;\n  limit?: number;\n};\n\nexport type SourceFileListInput = {\n  path: string;\n  ref?: string;\n};\n\nexport type SourceFileListEntry = {\n  path: string;\n  sha?: string;\n};\n\nexport type SourceFileHistoryEntry = {\n  id: string;\n  ref?: string;\n  message?: string;\n  author?: string;\n  createdAt: string;\n  url?: string;\n};\n\nexport type SourceChangeRequestInput = {\n  title: string;\n  body?: string;\n  files: SourceWriteFileInput[];\n  baseRef?: string;\n  headRef?: string;\n  inspectOnly?: boolean;\n};\n\nexport type SourceChangeRequestResult = {\n  id?: string | number;\n  url: string;\n  ref?: string;\n};\n\nexport const SOURCE_CONTROL_AGENTS_DIR = 'agents';\n\nexport function getSourceControlEntityFilePath(directory: string, entityId: string): string {\n  return `${directory}/${encodeURIComponent(entityId)}.json`;\n}\n\nexport function getSourceAgentFilePath(agentId: string): string {\n  return getSourceControlEntityFilePath(SOURCE_CONTROL_AGENTS_DIR, agentId);\n}\n\nexport interface SourceControlProvider extends SourceProviderInfo {\n  getCapabilities(): Promise<SourceControlCapabilities>;\n  readFile(input: SourceFileRef): Promise<SourceFile | null>;\n  writeFile(input: SourceWriteFileInput): Promise<SourceWriteResult>;\n  listFileHistory(input: SourceFileHistoryInput): Promise<SourceFileHistoryEntry[]>;\n  listFiles?(input: SourceFileListInput): Promise<SourceFileListEntry[]>;\n  openChangeRequest?(input: SourceChangeRequestInput): Promise<SourceChangeRequestResult>;\n}\n\nexport type EditorSourceCapabilities = {\n  source: 'db' | 'code';\n  storage: 'database' | 'filesystem' | 'source-control' | 'unavailable';\n  provider?: SourceProviderInfo;\n  canSave: boolean;\n  canOpenChangeRequest: boolean;\n  unavailableReason?: string;\n};\n","import { normalizePerPage, calculatePagination } from './base';\nimport type {\n  VersionBase,\n  ListVersionsInputBase,\n  ListVersionsOutputBase,\n  VersionedEntityBase,\n} from './domains/versioned';\n\nimport type { FilesystemDB } from './filesystem-db';\nimport { GitHistory } from './git-history';\nimport { getSourceControlEntityFilePath } from './source-control';\nimport type { StorageOrderBy } from './types';\n\n/**\n * Prefix for version IDs that come from git history.\n * These versions are read-only and cannot be deleted.\n */\nconst GIT_VERSION_PREFIX = 'git-';\n\n/**\n * Recursively sort object keys alphabetically so the on-disk JSON is stable\n * across saves. Arrays preserve order; object entries are emitted in a\n * deterministic order so git diffs only reflect real content changes.\n */\nfunction stableSortKeys(value: unknown): unknown {\n  if (Array.isArray(value)) {\n    return value.map(stableSortKeys);\n  }\n  if (value && typeof value === 'object' && !(value instanceof Date)) {\n    return Object.fromEntries(\n      Object.entries(value as Record<string, unknown>)\n        .filter(([, entry]) => entry !== undefined)\n        .sort(([left], [right]) => left.localeCompare(right))\n        .map(([key, entry]) => [key, stableSortKeys(entry)]),\n    );\n  }\n  return value;\n}\n\n/**\n * Configuration for a filesystem-backed versioned storage domain.\n */\nexport interface FilesystemVersionedConfig {\n  /** The FilesystemDB instance for I/O */\n  db: FilesystemDB;\n  /** Filename for the entities JSON file (e.g., 'agents.json') */\n  entitiesFile: string;\n  /** The key name of the parent FK field on versions (e.g., 'agentId') */\n  parentIdField: string;\n  /** Name for logging/error messages */\n  name: string;\n  /**\n   * Fields that are version metadata (not part of the snapshot config).\n   * These are stripped when writing to disk.\n   * e.g., ['id', 'agentId', 'versionNumber', 'changedFields', 'changeMessage', 'createdAt']\n   */\n  versionMetadataFields: string[];\n  /** Maximum number of git commits to load per file (default: 50) */\n  gitHistoryLimit?: number;\n  /** Directory for published entities that should be persisted as one JSON file per entity. */\n  perEntityFilesDir?: string;\n  /** Return true when an entity should persist as one JSON file instead of inside entitiesFile. */\n  shouldPersistToPerEntityFile?: (entity: VersionedEntityBase) => boolean;\n  /**\n   * Optional snapshot filter applied to per-entity files only.\n   * Lets per-entity files (e.g. code-mode JSON) exclude fields that are not\n   * user-editable from Studio (such as `model`) while keeping the shared\n   * `entitiesFile` snapshot unchanged.\n   */\n  perEntitySnapshotFilter?: (snapshot: Record<string, unknown>, entity: VersionedEntityBase) => Record<string, unknown>;\n}\n\n/**\n * Generic helpers for filesystem-backed versioned storage domains.\n *\n * Versions are kept entirely in memory. Only the published snapshot config\n * (the clean primitive configuration) is persisted to the on-disk JSON file.\n * This means the JSON files are human-readable, Git-friendly, and contain\n * no version metadata like `changedFields` or `changeMessage`.\n *\n * When the storage directory is inside a git repository, committed versions\n * of the JSON file are automatically loaded as read-only version history.\n * Each git commit that touched the file becomes a version record, giving\n * users a full published history in the version panel — powered by git.\n *\n * On-disk format for `agents.json`:\n * ```json\n * {\n *   \"my-agent-id\": {\n *     \"name\": \"My Agent\",\n *     \"instructions\": \"Be helpful\",\n *     \"model\": { \"provider\": \"openai\", \"name\": \"gpt-4\" }\n *   }\n * }\n * ```\n */\nexport class FilesystemVersionedHelpers<\n  TEntity extends VersionedEntityBase & { createdAt: Date; updatedAt: Date; status: string },\n  TVersion extends VersionBase,\n> {\n  readonly db: FilesystemDB;\n  readonly entitiesFile: string;\n  readonly parentIdField: string;\n  readonly name: string;\n  readonly versionMetadataFields: string[];\n  private readonly gitHistoryLimit: number;\n  private readonly perEntityFilesDir?: string;\n  private readonly shouldPersistToPerEntityFile?: (entity: VersionedEntityBase) => boolean;\n  private readonly perEntitySnapshotFilter?: (\n    snapshot: Record<string, unknown>,\n    entity: VersionedEntityBase,\n  ) => Record<string, unknown>;\n\n  /**\n   * In-memory entity records (thin metadata), keyed by entity ID.\n   */\n  private entities = new Map<string, TEntity>();\n\n  /**\n   * In-memory version records, keyed by version ID.\n   * Includes both in-memory/hydrated versions and git-based versions (metadata only).\n   */\n  private versions = new Map<string, TVersion>();\n\n  /**\n   * Whether we've loaded from disk yet.\n   */\n  private hydrated = false;\n\n  /**\n   * Git history utility instance (shared across all helpers).\n   */\n  private static gitHistory = new GitHistory();\n\n  /**\n   * Promise that resolves when git history has been loaded.\n   * null means git history loading hasn't been triggered yet.\n   */\n  private gitHistoryPromise: Promise<void> | null = null;\n\n  /**\n   * The highest version number from git history, per entity ID.\n   * Used to assign version numbers to new in-memory versions that continue\n   * after the git history.\n   */\n  private gitVersionCounts = new Map<string, number>();\n\n  constructor(config: FilesystemVersionedConfig) {\n    this.db = config.db;\n    this.entitiesFile = config.entitiesFile;\n    this.parentIdField = config.parentIdField;\n    this.name = config.name;\n    this.versionMetadataFields = config.versionMetadataFields;\n    this.gitHistoryLimit = config.gitHistoryLimit ?? 50;\n    this.perEntityFilesDir = config.perEntityFilesDir;\n    this.shouldPersistToPerEntityFile = config.shouldPersistToPerEntityFile;\n    this.perEntitySnapshotFilter = config.perEntitySnapshotFilter;\n  }\n\n  private perEntityFilename(entityId: string): string {\n    if (!this.perEntityFilesDir) {\n      throw new Error(`${this.name}: per-entity files directory is not configured`);\n    }\n    return getSourceControlEntityFilePath(this.perEntityFilesDir, entityId);\n  }\n\n  private entityIdFromPerEntityFilename(filename: string): string {\n    const basename = filename.split('/').pop() ?? filename;\n    return decodeURIComponent(basename.replace(/\\.json$/, ''));\n  }\n\n  /**\n   * Check if a version ID represents a git-based version.\n   */\n  static isGitVersion(id: string): boolean {\n    return id.startsWith(GIT_VERSION_PREFIX);\n  }\n\n  /**\n   * Hydrate in-memory state from the on-disk JSON file.\n   * For each entry on disk, creates an in-memory entity (status: 'published')\n   * and a synthetic version with the snapshot config.\n   *\n   * Also kicks off async git history loading in the background.\n   * Version numbers for hydrated entities are assigned as 1 initially,\n   * but will be reassigned after git history loads.\n   */\n  hydrate(): void {\n    if (this.hydrated) return;\n    this.hydrated = true;\n\n    const hydrateSnapshot = (entityId: string, snapshotConfig: Record<string, unknown>) => {\n      const versionId = `hydrated-${entityId}-v1`;\n      const now = new Date();\n\n      // Create a synthetic entity record\n      const entity = {\n        id: entityId,\n        status: 'published',\n        activeVersionId: versionId,\n        createdAt: now,\n        updatedAt: now,\n      } as unknown as TEntity;\n\n      this.entities.set(entityId, entity);\n\n      // Create a synthetic version with the snapshot config.\n      // Version number starts at 1 but may be bumped after git history loads.\n      const version = {\n        id: versionId,\n        [this.parentIdField]: entityId,\n        versionNumber: 1,\n        ...snapshotConfig,\n        createdAt: now,\n      } as TVersion;\n\n      this.versions.set(versionId, version);\n    };\n\n    const diskData = this.db.readDomain<Record<string, unknown>>(this.entitiesFile);\n\n    for (const [entityId, snapshotConfig] of Object.entries(diskData)) {\n      if (!snapshotConfig || typeof snapshotConfig !== 'object') continue;\n      hydrateSnapshot(entityId, snapshotConfig);\n    }\n\n    if (this.perEntityFilesDir) {\n      for (const filename of this.db.listDomainFiles(this.perEntityFilesDir)) {\n        const entityId = this.entityIdFromPerEntityFilename(filename);\n        const snapshotConfig = this.db.readDomain(filename);\n        if (!snapshotConfig || typeof snapshotConfig !== 'object') continue;\n        hydrateSnapshot(entityId, snapshotConfig);\n      }\n    }\n\n    // Kick off async git history loading (fire and forget)\n    this.gitHistoryPromise = this.loadGitHistory();\n  }\n\n  /**\n   * Ensure git history has been loaded before proceeding.\n   * Call this in version-related methods to ensure git versions are available.\n   */\n  private async ensureGitHistory(): Promise<void> {\n    this.hydrate();\n    if (this.gitHistoryPromise) {\n      await this.gitHistoryPromise;\n    }\n  }\n\n  /**\n   * Load git commit history for the domain's JSON file.\n   * Creates read-only version records (metadata + snapshot config) for each\n   * commit where an entity existed. Reassigns version numbers for\n   * hydrated (current disk) versions to sit on top of git history.\n   */\n  private async loadGitHistory(): Promise<void> {\n    const git = FilesystemVersionedHelpers.gitHistory;\n    const dir = this.db.dir;\n\n    // Check if we're in a git repo\n    const isRepo = await git.isGitRepo(dir);\n    if (!isRepo) return;\n\n    // Get commit history for this domain's file\n    const commits = await git.getFileHistory(dir, this.entitiesFile, this.gitHistoryLimit);\n\n    // Process commits from oldest to newest so version numbers are sequential\n    const orderedCommits = [...commits].reverse();\n\n    // Track per-entity version counts from git\n    const entityVersionCount = new Map<string, number>();\n    // Track previous snapshot per entity to skip unchanged entries\n    const previousSnapshots = new Map<string, string>();\n\n    for (let i = 0; i < orderedCommits.length; i++) {\n      const commit = orderedCommits[i]!;\n\n      // Load the file content at this commit\n      const fileContent = await git.getFileAtCommit<Record<string, Record<string, unknown>>>(\n        dir,\n        commit.hash,\n        this.entitiesFile,\n      );\n      if (!fileContent) continue;\n\n      // Create a version record for each entity that actually changed in this commit\n      for (const [entityId, snapshotConfig] of Object.entries(fileContent)) {\n        if (!snapshotConfig || typeof snapshotConfig !== 'object') continue;\n\n        // Skip if entity data is unchanged from the previous commit\n        const serialized = JSON.stringify(snapshotConfig);\n        if (previousSnapshots.get(entityId) === serialized) continue;\n        previousSnapshots.set(entityId, serialized);\n\n        const count = (entityVersionCount.get(entityId) ?? 0) + 1;\n        entityVersionCount.set(entityId, count);\n\n        const versionId = `${GIT_VERSION_PREFIX}${commit.hash}-${entityId}`;\n\n        // Skip if we somehow already have this version\n        if (this.versions.has(versionId)) continue;\n\n        const version = {\n          id: versionId,\n          [this.parentIdField]: entityId,\n          versionNumber: count,\n          changeMessage: commit.message,\n          ...snapshotConfig,\n          createdAt: commit.date,\n        } as TVersion;\n\n        this.versions.set(versionId, version);\n      }\n    }\n\n    // Walk git history for per-entity files (code mode). Each per-entity file\n    // is a standalone snapshot, not an entityId → snapshot map, so each commit\n    // touching the file becomes one version for that entity.\n    if (this.perEntityFilesDir) {\n      const perEntityIds = new Set<string>();\n      // Known entities currently on disk\n      for (const filename of this.db.listDomainFiles(this.perEntityFilesDir)) {\n        perEntityIds.add(this.entityIdFromPerEntityFilename(filename));\n      }\n      // Entities only present in git history (deleted on disk but still in commits)\n      // are discovered lazily by scanning entities map, which is already hydrated.\n      for (const entityId of this.entities.keys()) {\n        perEntityIds.add(entityId);\n      }\n\n      for (const entityId of perEntityIds) {\n        const count = await this.loadPerEntityGitHistory(entityId, entityVersionCount.get(entityId) ?? 0);\n        entityVersionCount.set(entityId, count);\n      }\n    }\n\n    // Save the max git version count per entity\n    this.gitVersionCounts = entityVersionCount;\n\n    // Reassign version numbers for hydrated (current disk) versions\n    // so they sit on top of git history\n    for (const [entityId, gitCount] of entityVersionCount) {\n      const hydratedVersionId = `hydrated-${entityId}-v1`;\n      const version = this.versions.get(hydratedVersionId);\n      if (version) {\n        (version as Record<string, unknown>).versionNumber = gitCount + 1;\n      }\n    }\n  }\n\n  /**\n   * Load git-backed versions for a single per-entity file. Each commit that\n   * changes the file becomes one version. Returns the running version count\n   * for the entity (starting from `startCount`). Used both by the bulk\n   * git-history pass and by `listVersions` to lazily discover entities that\n   * were deleted on disk but still exist in git history.\n   */\n  private async loadPerEntityGitHistory(entityId: string, startCount: number): Promise<number> {\n    const git = FilesystemVersionedHelpers.gitHistory;\n    const dir = this.db.dir;\n    const filename = this.perEntityFilename(entityId);\n    const perEntityCommits = await git.getFileHistory(dir, filename, this.gitHistoryLimit);\n    if (perEntityCommits.length === 0) return startCount;\n\n    const orderedPerEntity = [...perEntityCommits].reverse();\n    let previousSnapshotForEntity: string | undefined;\n    let count = startCount;\n\n    for (const commit of orderedPerEntity) {\n      const snapshotConfig = await git.getFileAtCommit<Record<string, unknown>>(dir, commit.hash, filename);\n      if (!snapshotConfig || typeof snapshotConfig !== 'object') {\n        // The file did not exist at this commit (e.g. it was deleted). Reset the\n        // dedupe baseline so a later restore with identical content is still\n        // recorded as a distinct version rather than skipped.\n        previousSnapshotForEntity = undefined;\n        continue;\n      }\n\n      // The per-entity file IS the snapshot, so flatten one level\n      // compared to the shared-file branch.\n      const serialized = JSON.stringify(snapshotConfig);\n      if (previousSnapshotForEntity === serialized) continue;\n      previousSnapshotForEntity = serialized;\n\n      count += 1;\n\n      const versionId = `${GIT_VERSION_PREFIX}${commit.hash}-${entityId}`;\n      if (this.versions.has(versionId)) continue;\n\n      const version = {\n        id: versionId,\n        [this.parentIdField]: entityId,\n        versionNumber: count,\n        changeMessage: commit.message,\n        ...snapshotConfig,\n        createdAt: commit.date,\n      } as TVersion;\n\n      this.versions.set(versionId, version);\n    }\n\n    return count;\n  }\n\n  // ==========================================================================\n  // Disk persistence — only published snapshot configs\n  // ==========================================================================\n\n  /**\n   * Write the published snapshot config for an entity to disk.\n   * Strips all entity metadata and version metadata fields, leaving only\n   * the clean primitive configuration.\n   */\n  private persistToDisk(): void {\n    const diskData: Record<string, Record<string, unknown>> = {};\n    const perEntityData = new Map<string, Record<string, unknown>>();\n\n    for (const [entityId, entity] of this.entities) {\n      if (entity.status !== 'published' || !entity.activeVersionId) continue;\n\n      const version = this.versions.get(entity.activeVersionId);\n      if (!version) continue;\n\n      const snapshotConfig = this.extractSnapshotConfig(version);\n      if (this.perEntityFilesDir && this.shouldPersistToPerEntityFile?.(entity)) {\n        const filtered = this.perEntitySnapshotFilter\n          ? this.perEntitySnapshotFilter(snapshotConfig, entity)\n          : snapshotConfig;\n        perEntityData.set(entityId, stableSortKeys(filtered) as Record<string, unknown>);\n      } else {\n        // Sort keys here too so shared-file domains get deterministic ordering.\n        // The shared git-history path dedupes with JSON.stringify, so without\n        // stable ordering a reorder-only write could surface as a fake version.\n        diskData[entityId] = stableSortKeys(snapshotConfig) as Record<string, unknown>;\n      }\n    }\n\n    // When every published entity is persisted to per-entity files (code mode)\n    // and the shared file would otherwise be an empty `{}` stub, skip writing\n    // it so projects that only use code mode don't end up tracking an empty\n    // `agents.json` in git. Existing shared files keep being updated so\n    // db-mode and mixed setups behave the same as before.\n    const hasSharedEntries = Object.keys(diskData).length > 0;\n    const sharedFileExists = this.db.domainFileExists(this.entitiesFile);\n    if (hasSharedEntries || !this.perEntityFilesDir || sharedFileExists) {\n      this.db.writeDomain(this.entitiesFile, diskData);\n    }\n\n    if (this.perEntityFilesDir) {\n      for (const filename of this.db.listDomainFiles(this.perEntityFilesDir)) {\n        const entityId = this.entityIdFromPerEntityFilename(filename);\n        if (!perEntityData.has(entityId)) {\n          this.db.removeDomainFile(filename);\n        }\n      }\n\n      for (const [entityId, snapshotConfig] of perEntityData) {\n        this.db.writeDomain(this.perEntityFilename(entityId), snapshotConfig);\n      }\n    }\n  }\n\n  /**\n   * Extract the snapshot config from a version, stripping version metadata fields.\n   */\n  private extractSnapshotConfig(version: TVersion): Record<string, unknown> {\n    const metadataSet = new Set(this.versionMetadataFields);\n    const result: Record<string, unknown> = {};\n\n    for (const [key, value] of Object.entries(version)) {\n      if (!metadataSet.has(key)) {\n        result[key] = value;\n      }\n    }\n\n    return result;\n  }\n\n  // ==========================================================================\n  // Entity CRUD\n  // ==========================================================================\n\n  async getById(id: string): Promise<TEntity | null> {\n    this.hydrate();\n    return this.entities.has(id) ? structuredClone(this.entities.get(id)!) : null;\n  }\n\n  async createEntity(id: string, entity: TEntity): Promise<TEntity> {\n    this.hydrate();\n    if (this.entities.has(id)) {\n      throw new Error(`${this.name}: entity with id ${id} already exists`);\n    }\n    this.entities.set(id, structuredClone(entity));\n    return structuredClone(entity);\n  }\n\n  async updateEntity(id: string, updates: Record<string, unknown>): Promise<TEntity> {\n    this.hydrate();\n    const existing = this.entities.get(id);\n    if (!existing) {\n      throw new Error(`${this.name}: entity with id ${id} not found`);\n    }\n\n    const updated = { ...existing } as Record<string, unknown>;\n\n    for (const [key, value] of Object.entries(updates)) {\n      if (key === 'id') continue;\n      if (value === undefined) continue;\n\n      if (key === 'metadata' && typeof value === 'object' && value !== null) {\n        updated['metadata'] = {\n          ...((updated['metadata'] as Record<string, unknown> | undefined) ?? {}),\n          ...(value as Record<string, unknown>),\n        };\n      } else {\n        updated[key] = value;\n      }\n    }\n    updated['updatedAt'] = new Date();\n\n    const updatedEntity = updated as TEntity;\n    this.entities.set(id, structuredClone(updatedEntity));\n\n    // Persist to disk when publication state changes:\n    // - entity becomes published (write to disk)\n    // - entity was published but status changed (remove from disk)\n    const wasPublished = existing.status === 'published';\n    const isPublished = updatedEntity.status === 'published' && updatedEntity.activeVersionId;\n    if (isPublished || (wasPublished && updates['status'] !== undefined)) {\n      this.persistToDisk();\n    }\n\n    return structuredClone(updatedEntity);\n  }\n\n  async deleteEntity(id: string): Promise<void> {\n    this.hydrate();\n    this.entities.delete(id);\n    await this.deleteVersionsByParentId(id);\n    this.persistToDisk();\n  }\n\n  async listEntities(args: {\n    page?: number;\n    perPage?: number | false;\n    orderBy?: StorageOrderBy;\n    filters?: Record<string, unknown>;\n    listKey: string;\n  }): Promise<Record<string, unknown>> {\n    this.hydrate();\n    const { page = 0, perPage: perPageInput, orderBy, filters, listKey } = args;\n\n    const perPage = normalizePerPage(perPageInput, 100);\n    if (page < 0) throw new Error('page must be >= 0');\n\n    let entities = Array.from(this.entities.values());\n\n    // Apply filters\n    if (filters) {\n      for (const [key, value] of Object.entries(filters)) {\n        if (value === undefined) continue;\n        if (key === 'metadata' && typeof value === 'object' && value !== null) {\n          entities = entities.filter(e => {\n            const meta = (e as Record<string, unknown>)['metadata'] as Record<string, unknown> | undefined;\n            if (!meta) return false;\n            return Object.entries(value as Record<string, unknown>).every(\n              ([k, v]) => JSON.stringify(meta[k]) === JSON.stringify(v),\n            );\n          });\n        } else {\n          entities = entities.filter(e => (e as Record<string, unknown>)[key] === value);\n        }\n      }\n    }\n\n    // Sort\n    const field = (orderBy?.field as string) ?? 'createdAt';\n    const direction = (orderBy?.direction as string) ?? 'DESC';\n    entities.sort((a, b) => {\n      const aVal = new Date((a as Record<string, unknown>)[field] as string | Date).getTime();\n      const bVal = new Date((b as Record<string, unknown>)[field] as string | Date).getTime();\n      return direction === 'ASC' ? aVal - bVal : bVal - aVal;\n    });\n\n    const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);\n\n    return {\n      [listKey]: entities.slice(offset, offset + perPage),\n      total: entities.length,\n      page,\n      perPage: perPageForResponse,\n      hasMore: offset + perPage < entities.length,\n    };\n  }\n\n  // ==========================================================================\n  // Version Methods (in-memory + git history)\n  // ==========================================================================\n\n  async createVersion(input: TVersion): Promise<TVersion> {\n    await this.ensureGitHistory();\n    if (this.versions.has(input.id)) {\n      throw new Error(`${this.name}: version with id ${input.id} already exists`);\n    }\n\n    const parentId = (input as Record<string, unknown>)[this.parentIdField] as string;\n\n    // Check for duplicate (parentId, versionNumber) pair\n    for (const v of this.versions.values()) {\n      if ((v as Record<string, unknown>)[this.parentIdField] === parentId && v.versionNumber === input.versionNumber) {\n        throw new Error(`${this.name}: version number ${input.versionNumber} already exists for entity ${parentId}`);\n      }\n    }\n\n    const version: TVersion = {\n      ...input,\n      createdAt: new Date(),\n    } as TVersion;\n\n    this.versions.set(input.id, structuredClone(version));\n    return structuredClone(version);\n  }\n\n  async getVersion(id: string): Promise<TVersion | null> {\n    await this.ensureGitHistory();\n    return this.versions.has(id) ? structuredClone(this.versions.get(id)!) : null;\n  }\n\n  async getVersionByNumber(entityId: string, versionNumber: number): Promise<TVersion | null> {\n    await this.ensureGitHistory();\n    for (const v of this.versions.values()) {\n      if ((v as Record<string, unknown>)[this.parentIdField] === entityId && v.versionNumber === versionNumber) {\n        return structuredClone(v);\n      }\n    }\n    return null;\n  }\n\n  async getLatestVersion(entityId: string): Promise<TVersion | null> {\n    await this.ensureGitHistory();\n    let latest: TVersion | null = null;\n    for (const v of this.versions.values()) {\n      if ((v as Record<string, unknown>)[this.parentIdField] === entityId) {\n        if (!latest || v.versionNumber > latest.versionNumber) {\n          latest = v;\n        }\n      }\n    }\n    return latest ? structuredClone(latest) : null;\n  }\n\n  async listVersions(input: ListVersionsInputBase, parentIdField: string): Promise<ListVersionsOutputBase<TVersion>> {\n    await this.ensureGitHistory();\n    const { page = 0, perPage: perPageInput, orderBy } = input;\n    const entityId = (input as Record<string, unknown>)[parentIdField] as string;\n\n    const perPage = normalizePerPage(perPageInput, 20);\n    if (page < 0) throw new Error('page must be >= 0');\n\n    // Lazily discover per-entity files that were deleted on disk but still\n    // exist in git history. The bulk git-history pass only walks entities that\n    // are currently on disk or in memory, so a deleted-then-requested entity\n    // would otherwise surface no versions.\n    await this.ensurePerEntityGitHistory(entityId);\n\n    const versions = Array.from(this.versions.values()).filter(\n      v => (v as Record<string, unknown>)[this.parentIdField] === entityId,\n    );\n\n    // Sort\n    const field = (orderBy?.field as string) ?? 'versionNumber';\n    const direction = (orderBy?.direction as string) ?? 'DESC';\n    versions.sort((a, b) => {\n      const aVal = field === 'createdAt' ? new Date(a.createdAt).getTime() : a.versionNumber;\n      const bVal = field === 'createdAt' ? new Date(b.createdAt).getTime() : b.versionNumber;\n      return direction === 'ASC' ? aVal - bVal : bVal - aVal;\n    });\n\n    const { offset, perPage: perPageForResponse } = calculatePagination(page, perPageInput, perPage);\n\n    return {\n      versions: versions.slice(offset, offset + perPage),\n      total: versions.length,\n      page,\n      perPage: perPageForResponse,\n      hasMore: offset + perPage < versions.length,\n    };\n  }\n\n  async deleteVersion(id: string): Promise<void> {\n    await this.ensureGitHistory();\n    // Git-based versions are read-only\n    if (FilesystemVersionedHelpers.isGitVersion(id)) return;\n    this.versions.delete(id);\n  }\n\n  async deleteVersionsByParentId(entityId: string): Promise<void> {\n    await this.ensureGitHistory();\n    for (const [versionId, version] of this.versions) {\n      if ((version as Record<string, unknown>)[this.parentIdField] === entityId) {\n        // Skip git-based versions (read-only)\n        if (FilesystemVersionedHelpers.isGitVersion(versionId)) continue;\n        this.versions.delete(versionId);\n      }\n    }\n  }\n\n  async countVersions(entityId: string): Promise<number> {\n    await this.ensureGitHistory();\n    let count = 0;\n    for (const v of this.versions.values()) {\n      if ((v as Record<string, unknown>)[this.parentIdField] === entityId) {\n        count++;\n      }\n    }\n    return count;\n  }\n\n  /**\n   * Lazily discover per-entity git history for an entity that was deleted on\n   * disk but still exists in git commits. The bulk git-history pass only walks\n   * entities currently on disk or in memory, so without this an entity that has\n   * no in-memory versions would surface no git versions (and `gitVersionCounts`\n   * would stay 0, letting a recreated entity collide with git version numbers).\n   */\n  private async ensurePerEntityGitHistory(entityId: string): Promise<void> {\n    if (!this.perEntityFilesDir || !entityId) return;\n    const hasVersions = Array.from(this.versions.values()).some(\n      v => (v as Record<string, unknown>)[this.parentIdField] === entityId,\n    );\n    if (hasVersions) return;\n\n    const startCount = this.gitVersionCounts.get(entityId) ?? 0;\n    const newCount = await this.loadPerEntityGitHistory(entityId, startCount);\n    if (newCount > startCount) {\n      this.gitVersionCounts.set(entityId, newCount);\n    }\n  }\n\n  async getNextVersionNumber(entityId: string): Promise<number> {\n    await this.ensureGitHistory();\n    await this.ensurePerEntityGitHistory(entityId);\n    return this._getNextVersionNumber(entityId);\n  }\n\n  private _getNextVersionNumber(entityId: string): number {\n    const gitCount = this.gitVersionCounts.get(entityId) ?? 0;\n    let maxVersion = gitCount;\n    for (const v of this.versions.values()) {\n      if ((v as Record<string, unknown>)[this.parentIdField] === entityId) {\n        maxVersion = Math.max(maxVersion, v.versionNumber);\n      }\n    }\n    return maxVersion + 1;\n  }\n\n  async dangerouslyClearAll(): Promise<void> {\n    this.entities.clear();\n    this.versions.clear();\n    this.gitVersionCounts.clear();\n    this.gitHistoryPromise = null;\n    this.hydrated = false;\n    this.db.clearDomain(this.entitiesFile);\n\n    // Per-entity files are real on-disk snapshots; clearing only the shared\n    // file leaves them behind and they get re-imported on the next hydrate.\n    if (this.perEntityFilesDir) {\n      for (const filename of this.db.listDomainFiles(this.perEntityFilesDir)) {\n        this.db.removeDomainFile(filename);\n      }\n    }\n  }\n}\n"],"mappings":"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"}