{"version":3,"file":"config.mjs","names":[],"sources":["../../../../../../ai/src/config.ts"],"sourcesContent":["import type { CacheDriver } from \"@warlock.js/cache\";\nimport { log } from \"@warlock.js/logger\";\nimport type { CheckpointStore } from \"./contracts/orchestrator/checkpoint-store.contract\";\nimport type { SnapshotStore } from \"./contracts/orchestrator/snapshot-store.contract\";\n\n/**\n * Process-wide `@warlock.js/ai` configuration. **Intentionally tiny.**\n * Lives here only for genuinely cross-cutting defaults that would\n * otherwise force users to wire the same value into every consumer.\n *\n * **What lives here.** A field earns a slot only when it satisfies\n * all three:\n * 1. Multiple unrelated consumers need the same value.\n * 2. The value is infrastructure (drivers, clients, pools), not\n *    behavior (kill-switches, mode flags).\n * 3. Per-call override doesn't make sense for the use case.\n *\n * **What does NOT live here.** Logger config (use\n * `@warlock.js/logger` directly). Per-primitive feature flags\n * (live on the relevant config type). Anything that's really one\n * consumer's concern (lives on that consumer).\n *\n * Phase 3.2 deliberately removed the previous `configureAI()` bag\n * because it was growing unbounded. Treat new fields here with the\n * same suspicion.\n *\n * **Augmentable.** Declared as an `interface` (not a `type` alias) so\n * observability/tooling packages can attach their own opaque config slot\n * via declaration merging WITHOUT core importing them — keeping core\n * dependency-free. For example `@warlock.js/ai-panoptic` adds a\n * `panoptic?` field:\n *\n * ```ts\n * declare module \"@warlock.js/ai\" {\n *   interface AIConfig {\n *     panoptic?: PanopticConfig;\n *   }\n * }\n * ```\n *\n * `setAIConfig` stores the whole object via `Object.assign`, so any\n * augmented field is preserved even though core never reads it.\n */\nexport interface AIConfig {\n  /**\n   * Default `@warlock.js/cache` driver for cache-backed consumers that\n   * didn't supply their own `store` — currently the `semanticCache`\n   * middleware's vector store. Declaring it once here removes the\n   * repetition across middleware declarations.\n   *\n   * NOT the snapshot-persistence fallback anymore. Supervisor /\n   * workflow / orchestrator resume snapshots resolve through\n   * {@link AIConfig.defaultSnapshotStore} (a {@link SnapshotStore}),\n   * never this driver.\n   *\n   * Per-declaration overrides (`semanticCache({ store })`) win when\n   * supplied. Set this once at app boot, *after* you've constructed\n   * your driver.\n   *\n   * @example\n   * import { cache } from \"@warlock.js/cache\";\n   * import { ai } from \"@warlock.js/ai\";\n   *\n   * ai.config({\n   *   defaultStore: cache.driver(\"redis\", { client: redisClient }),\n   * });\n   */\n  defaultStore?: CacheDriver<any, any>;\n\n  /**\n   * Default {@link CheckpointStore} for every orchestrator that didn't\n   * supply its own `checkpointStore` (orchestrator.md §15.2). Holds\n   * durable session state — `state`, `turn_index`, drift `signature`,\n   * compaction locks. Per-orchestrator `checkpointStore` wins when\n   * supplied. Set once at app boot.\n   *\n   * @example\n   * import { ai } from \"@warlock.js/ai\";\n   *\n   * ai.config({ defaultCheckpointStore: ai.checkpoint.memory() });\n   */\n  defaultCheckpointStore?: CheckpointStore;\n\n  /**\n   * Default {@link SnapshotStore} for every orchestrator that didn't\n   * supply its own `snapshotStore` (orchestrator.md §15.2). Holds the\n   * internal supervisor run state used to resume an interrupted\n   * `iterate: true` turn. Per-orchestrator `snapshotStore` wins when\n   * supplied. Set once at app boot.\n   *\n   * @example\n   * import { ai } from \"@warlock.js/ai\";\n   *\n   * ai.config({ defaultSnapshotStore: ai.snapshot.memory() });\n   */\n  defaultSnapshotStore?: SnapshotStore;\n};\n\nconst aiConfig: AIConfig = {};\n\n/** A listener notified after every `setAIConfig` merge. */\ntype ConfigListener = (config: AIConfig) => void;\n\nconst configListeners: ConfigListener[] = [];\n\n/**\n * Subscribe to config changes. The listener fires after every\n * {@link setAIConfig} merge with a fresh snapshot of the full config —\n * the seam observability/tooling packages use to react when their\n * augmented slot (e.g. `panoptic`) is set, WITHOUT core importing them.\n *\n * Mirrors the dependency-inversion of the `Observer` registry: core\n * exposes the structural hook; the tool subscribes on its side-effect\n * import. To also catch config that was applied *before* the subscription,\n * read {@link getAIConfig} once right after subscribing.\n *\n * @example\n * import { onConfigApplied, getAIConfig } from \"@warlock.js/ai\";\n * onConfigApplied((config) => applyPanopticConfig(config.panoptic));\n * applyPanopticConfig(getAIConfig().panoptic); // catch pre-set config\n */\nexport function onConfigApplied(listener: ConfigListener): void {\n  configListeners.push(listener);\n}\n\n/**\n * Set or extend process-wide AI configuration. Merges over existing\n * values — fields not present in `partial` keep whatever was set\n * before (or stay unset). Call once at app boot, before constructing\n * any agent / supervisor / middleware that should pick up the\n * defaults.\n *\n * Returns the merged config so callers can verify what landed.\n *\n * @example\n * import { cache } from \"@warlock.js/cache\";\n * import { ai } from \"@warlock.js/ai\";\n *\n * ai.config({ defaultStore: cache.driver(\"redis\", { client }) });\n */\nexport function setAIConfig(partial: Partial<AIConfig>): AIConfig {\n  Object.assign(aiConfig, partial);\n  const snapshot = { ...aiConfig };\n\n  // Notify subscribers (e.g. panoptic) after the merge. Errors are\n  // swallowed so a misbehaving listener never breaks config application,\n  // mirroring the observer / onUsage swallow-on-throw discipline.\n  for (const listener of configListeners) {\n    try {\n      listener(snapshot);\n    } catch (error) {\n      log.error(\"ai\", \"configListener\", error as Error);\n    }\n  }\n\n  return snapshot;\n}\n\n/**\n * Read the current AI config snapshot. Returns a shallow copy so\n * callers can't accidentally mutate the source of truth. Used\n * internally by consumers to resolve their `defaultStore` fallback.\n */\nexport function getAIConfig(): AIConfig {\n  return { ...aiConfig };\n}\n\n/**\n * Resolve the effective `@warlock.js/cache` driver for a cache-backed\n * consumer that didn't receive an explicit one. Returns the global\n * `defaultStore` if set, otherwise `undefined`. The semantic-cache\n * middleware treats `undefined` as fatal and throws. Snapshot\n * persistence no longer consults this — it resolves through\n * {@link resolveDefaultSnapshotStore}.\n */\nexport function resolveDefaultStore(): CacheDriver<any, any> | undefined {\n  return aiConfig.defaultStore;\n}\n\n/**\n * Resolve the global default {@link CheckpointStore} for an\n * orchestrator that didn't receive an explicit `checkpointStore`.\n * Returns `undefined` when none is configured — the orchestrator\n * factory decides whether that's fatal.\n */\nexport function resolveDefaultCheckpointStore(): CheckpointStore | undefined {\n  return aiConfig.defaultCheckpointStore;\n}\n\n/**\n * Resolve the global default {@link SnapshotStore} for an orchestrator\n * that didn't receive an explicit `snapshotStore`. Returns `undefined`\n * when none is configured — the orchestrator factory decides whether\n * that's fatal (it is, when `iterate: true`).\n */\nexport function resolveDefaultSnapshotStore(): SnapshotStore | undefined {\n  return aiConfig.defaultSnapshotStore;\n}\n"],"mappings":";;;AAkGA,MAAM,WAAqB,CAAC;AAK5B,MAAM,kBAAoC,CAAC;;;;;;;;;;;;;;;;;AAkB3C,SAAgB,gBAAgB,UAAgC;CAC9D,gBAAgB,KAAK,QAAQ;AAC/B;;;;;;;;;;;;;;;;AAiBA,SAAgB,YAAY,SAAsC;CAChE,OAAO,OAAO,UAAU,OAAO;CAC/B,MAAM,WAAW,EAAE,GAAG,SAAS;CAK/B,KAAK,MAAM,YAAY,iBACrB,IAAI;EACF,SAAS,QAAQ;CACnB,SAAS,OAAO;EACd,IAAI,MAAM,MAAM,kBAAkB,KAAc;CAClD;CAGF,OAAO;AACT;;;;;;AAOA,SAAgB,cAAwB;CACtC,OAAO,EAAE,GAAG,SAAS;AACvB;;;;;;;;;AAUA,SAAgB,sBAAyD;CACvE,OAAO,SAAS;AAClB;;;;;;;AAQA,SAAgB,gCAA6D;CAC3E,OAAO,SAAS;AAClB;;;;;;;AAQA,SAAgB,8BAAyD;CACvE,OAAO,SAAS;AAClB"}