{"version":3,"file":"orchestrator.mjs","names":["orchestratorAsTool"],"sources":["../../../../../../../ai/src/orchestrator/orchestrator.ts"],"sourcesContent":["import type { OrchestratorConfig } from \"../contracts/orchestrator/orchestrator-config.type\";\nimport type {\n  OrchestratorEventHandler,\n  OrchestratorEventName,\n} from \"../contracts/orchestrator/orchestrator-event.type\";\nimport type {\n  OrchestratorAsToolOptions,\n  OrchestratorContract,\n} from \"../contracts/orchestrator/orchestrator.contract\";\nimport type { OrchestratorCommands } from \"../contracts/orchestrator/orchestrator-commands.type\";\nimport type {\n  OrchestratorExecuteOptions,\n  OrchestratorResumeOptions,\n} from \"../contracts/orchestrator/orchestrator-execute-options.type\";\nimport type { OrchestratorEvent } from \"../contracts/orchestrator/orchestrator-event.type\";\nimport type { OrchestratorResult } from \"../contracts/result/orchestrator-result.type\";\nimport type { StreamContract } from \"../contracts/stream/stream.contract\";\nimport type { SupervisorIntentValue } from \"../contracts/supervisor/intent-entry.type\";\nimport type { SupervisorInput } from \"../contracts/supervisor/supervisor-input.type\";\nimport { resolveDefaultSnapshotStore } from \"../config\";\nimport { OrchestratorConfigError } from \"../errors/orchestrator-config-error\";\nimport { resolveIntentEntries, type ResolvedIntentEntry } from \"../supervisor/entries\";\nimport { SupervisorFailedError } from \"../errors\";\nimport type { ToolContract } from \"../tool/tool\";\nimport type { SessionLock } from \"../contracts/orchestrator/session-lock.contract\";\nimport { asTool as orchestratorAsTool } from \"./as-tool\";\nimport { createCommandDispatcher } from \"./commands\";\nimport { OrchestratorEmitter } from \"./emitter\";\nimport { OrchestratorExecution } from \"./execution\";\nimport { createOrchestratorStream } from \"./orchestrator-stream\";\nimport { inProcessSessionLock, noopSessionLock } from \"./session-lock\";\nimport { computeOrchestratorSignature } from \"./signature\";\n\n/**\n * `ai.orchestrator(config)` — construct an {@link OrchestratorContract}:\n * a session-state manager wrapped around a supervisor (orchestrator.md\n * §1, §15). Validates the config at author time (throws\n * {@link OrchestratorConfigError} on bad shape), resolves the intent\n * entries, computes a stable structural signature for drift detection\n * (§10.1), wires the three-tier event emitter, and returns a handle that\n * runs one durable session turn per `execute` / `stream` call, resumes\n * an interrupted `iterate: true` turn via `resume`, and exposes typed\n * built-in commands plus an `asTool` wrapper.\n *\n * The \"what runs\" fields (`intents`, `route` / `router`, `evaluate`,\n * `state`, `output`, `initialAgent`, `maxIterations`) are the\n * supervisor's surface spread directly — the lifecycle builds the\n * supervisor lazily per turn and delegates to it (§3 Phase 5). Users\n * never see the supervisor object.\n *\n * @example\n * const supportBot = ai.orchestrator<SessionState>({\n *   name: \"refund-support\",\n *   intents: { classify, lookup, process, compose },\n *   route: (ctx) => (ctx.iteration === 0 ? \"classify\" : END),\n *   iterate: true,\n *   checkpointStore: ai.checkpoint.pg({ client: pg }),\n *   snapshotStore: ai.snapshot.pg({ client: pg }),\n * });\n *\n * const result = await supportBot.execute(message, { sessionId, history });\n */\nexport function orchestrator<\n  TOutput = unknown,\n  TState = TOutput,\n  TIntents extends Record<string, SupervisorIntentValue> = Record<\n    string,\n    SupervisorIntentValue\n  >,\n>(\n  config: OrchestratorConfig<TOutput, TState, TIntents>,\n): OrchestratorContract<TOutput, TState> {\n  validateFactoryConfig(config as unknown as OrchestratorConfig<unknown>);\n\n  const entries = resolveEntries(config as unknown as OrchestratorConfig<unknown>);\n\n  assertInitialAgent(config as unknown as OrchestratorConfig<unknown>, entries);\n\n  const signature = computeOrchestratorSignature(\n    config as unknown as OrchestratorConfig<unknown>,\n    entries,\n  );\n  const emitter = new OrchestratorEmitter(config.on);\n\n  // Per-session turn serialization (C4). Resolved ONCE so every turn on\n  // this orchestrator shares the same lock — that's what lets the\n  // in-process default actually serialize concurrent same-session calls.\n  const sessionLock = resolveSessionLock(config as unknown as OrchestratorConfig<unknown>);\n  warnOnUnlockedDurableStore(config as unknown as OrchestratorConfig<unknown>);\n\n  async function execute(\n    input: SupervisorInput,\n    options: OrchestratorExecuteOptions<TState>,\n  ): Promise<OrchestratorResult<TOutput>> {\n    const execution = new OrchestratorExecution<TOutput, TState>({\n      config: config as unknown as OrchestratorConfig<TOutput, TState>,\n      entries,\n      signature,\n      emitter,\n      input,\n      options,\n    });\n\n    // Serialize the whole turn (load → dispatch → persist) against any\n    // concurrent turn for the same session, so the checkpoint's\n    // read-modify-write can't lose an update.\n    return sessionLock.withLock(options.sessionId, () => execution.run(), {\n      signal: options.signal,\n    });\n  }\n\n  function stream(\n    input: SupervisorInput,\n    options: OrchestratorExecuteOptions<TState>,\n  ): StreamContract<OrchestratorResult<TOutput>, OrchestratorEvent> {\n    const { controller, stream: contract } = createOrchestratorStream<\n      OrchestratorResult<TOutput>\n    >();\n\n    const execution = new OrchestratorExecution<TOutput, TState>({\n      config: config as unknown as OrchestratorConfig<TOutput, TState>,\n      entries,\n      signature,\n      emitter,\n      input,\n      options,\n      streamController: controller,\n    });\n\n    // The background run waits for the session lock before it starts —\n    // same serialization guarantee as `execute`; the stream contract is\n    // still returned synchronously.\n    void sessionLock.withLock(options.sessionId, () => execution.run(), {\n      signal: options.signal,\n    });\n\n    return contract;\n  }\n\n  async function resume(\n    sessionId: string,\n    options?: OrchestratorResumeOptions,\n  ): Promise<OrchestratorResult<TOutput> | null> {\n    const execution = new OrchestratorExecution<TOutput, TState>({\n      config: config as unknown as OrchestratorConfig<TOutput, TState>,\n      entries,\n      signature,\n      emitter,\n      resumeSessionId: sessionId,\n      resumeOptions: options,\n    });\n\n    return sessionLock.withLock(sessionId, () => execution.resume(), {\n      signal: options?.signal,\n    });\n  }\n\n  // The dispatcher owns command ROUTING only; the `compact` handler\n  // delegates to the shared compaction code path on the lifecycle engine\n  // (§11 / §12.2 — manual compact reuses the post-turn compaction path).\n  const command = createCommandDispatcher({\n    compact: (args: OrchestratorCommands[\"compact\"][\"args\"]) => {\n      const execution = new OrchestratorExecution<TOutput, TState>({\n        config: config as unknown as OrchestratorConfig<TOutput, TState>,\n        entries,\n        signature,\n        emitter,\n      });\n\n      return execution.compact(args);\n    },\n  });\n\n  const instance: OrchestratorContract<TOutput, TState> = {\n    name: config.name,\n    signature,\n    version: config.version,\n    execute,\n    stream,\n    resume,\n    command,\n    asTool<TToolInput = string>(\n      options: OrchestratorAsToolOptions<TToolInput>,\n    ): ToolContract<TToolInput, TOutput> {\n      return orchestratorAsTool<TOutput, TState, TToolInput>(instance, options);\n    },\n    on<K extends OrchestratorEventName>(\n      event: K,\n      handler: OrchestratorEventHandler<K>,\n    ): () => void {\n      return emitter.on(event, handler);\n    },\n    off<K extends OrchestratorEventName>(\n      event: K,\n      handler: OrchestratorEventHandler<K>,\n    ): void {\n      emitter.off(event, handler);\n    },\n  };\n\n  return instance;\n}\n\n/**\n * Author-time validation (orchestrator.md §17). Enforces the rules that\n * must fail at construction rather than on the first turn:\n *\n * - `name` present and a string.\n * - `intents` present.\n * - `route` XOR `router` (mutually exclusive; at least one required) —\n *   the supervisor's dispatch-source rule, surfaced as an orchestrator\n *   config error.\n * - `router` is a valid agent contract or `{ agent, ... }` entry.\n * - `maxIterations >= 1` when set.\n * - `snapshotStore` resolvable when `iterate: true` — explicit field or\n *   the global `ai.config({ defaultSnapshotStore })` fallback.\n *\n * `initialAgent` membership is checked separately once the intent\n * entries are resolved.\n */\nfunction validateFactoryConfig(config: OrchestratorConfig<unknown>): void {\n  if (!config.name || typeof config.name !== \"string\") {\n    throw new OrchestratorConfigError(\n      \"ai.orchestrator: `name` is required and must be a string\",\n      { context: { authoring: true } },\n    );\n  }\n\n  if (!config.intents || typeof config.intents !== \"object\") {\n    throw new OrchestratorConfigError(\n      `ai.orchestrator(\"${config.name}\"): \\`intents\\` is required`,\n      { context: { authoring: true } },\n    );\n  }\n\n  const hasRoute = typeof config.route === \"function\";\n  const hasRouter = Boolean(config.router);\n\n  if (hasRouter) {\n    const router = config.router;\n    const isBareAgent = typeof (router as { execute?: unknown }).execute === \"function\";\n    const isEntryForm =\n      !isBareAgent &&\n      typeof (router as { agent?: { execute?: unknown } }).agent === \"object\" &&\n      typeof (router as { agent?: { execute?: unknown } }).agent?.execute === \"function\";\n\n    if (!isBareAgent && !isEntryForm) {\n      throw new OrchestratorConfigError(\n        `ai.orchestrator(\"${config.name}\"): \\`router\\` must be an agent contract or a \\`{ agent, placeholders?, input? }\\` entry`,\n        { context: { authoring: true } },\n      );\n    }\n  }\n\n  if (hasRoute && hasRouter) {\n    throw new OrchestratorConfigError(\n      `ai.orchestrator(\"${config.name}\"): \\`route\\` and \\`router\\` are mutually exclusive — configure exactly one`,\n      { context: { authoring: true } },\n    );\n  }\n\n  if (!hasRoute && !hasRouter) {\n    throw new OrchestratorConfigError(\n      `ai.orchestrator(\"${config.name}\"): one of \\`route\\` or \\`router\\` is required`,\n      { context: { authoring: true } },\n    );\n  }\n\n  if (config.maxIterations !== undefined && config.maxIterations < 1) {\n    throw new OrchestratorConfigError(\n      `ai.orchestrator(\"${config.name}\"): \\`maxIterations\\` must be >= 1`,\n      { context: { authoring: true, maxIterations: config.maxIterations } },\n    );\n  }\n\n  if (config.iterate && !config.snapshotStore && !resolveDefaultSnapshotStore()) {\n    throw new OrchestratorConfigError(\n      `ai.orchestrator(\"${config.name}\"): \\`iterate: true\\` requires a \\`snapshotStore\\` (or \\`ai.config({ defaultSnapshotStore })\\`) for mid-turn resume`,\n      { context: { authoring: true } },\n    );\n  }\n}\n\n/**\n * Resolve the `intents` map into the supervisor's internal entry shape,\n * re-wrapping the supervisor's authoring failure as an\n * {@link OrchestratorConfigError} so misuse surfaces under the\n * orchestrator's error family rather than the supervisor's.\n */\nfunction resolveEntries(\n  config: OrchestratorConfig<unknown>,\n): Map<string, ResolvedIntentEntry> {\n  try {\n    return resolveIntentEntries(config.intents, config.name);\n  } catch (error) {\n    if (error instanceof SupervisorFailedError) {\n      throw new OrchestratorConfigError(error.message, {\n        context: { authoring: true },\n        cause: error,\n      });\n    }\n\n    throw error;\n  }\n}\n\n/**\n * Enforce the `initialAgent` membership rule (§17) once entries are\n * resolved — `initialAgent`, when set, must name a key in `intents`.\n */\nfunction assertInitialAgent(\n  config: OrchestratorConfig<unknown>,\n  entries: Map<string, ResolvedIntentEntry>,\n): void {\n  if (config.initialAgent && !entries.has(config.initialAgent)) {\n    throw new OrchestratorConfigError(\n      `ai.orchestrator(\"${config.name}\"): \\`initialAgent\\` \"${config.initialAgent}\" is not a key in \\`intents\\``,\n      { context: { authoring: true } },\n    );\n  }\n}\n\n/**\n * Resolve the per-session lock (C4): an explicit {@link SessionLock} when\n * supplied, a no-op when `sessionLock: false`, otherwise the framework\n * default in-process mutex.\n */\nfunction resolveSessionLock(config: OrchestratorConfig<unknown>): SessionLock {\n  if (config.sessionLock === false) return noopSessionLock();\n  if (config.sessionLock) return config.sessionLock;\n  return inProcessSessionLock();\n}\n\n/** Orchestrator names already warned about an unlocked durable store. */\nconst warnedUnlockedStores = new Set<string>();\n\n/**\n * Warn once when a durable `checkpointStore` is configured but no explicit\n * `sessionLock` was supplied (C4). The in-process default serializes\n * same-session turns within one process only — a horizontally-scaled\n * deployment needs a distributed lock or sticky routing. Suppressed in\n * tests and when the dev explicitly chose a lock (or `sessionLock: false`).\n */\nfunction warnOnUnlockedDurableStore(config: OrchestratorConfig<unknown>): void {\n  if (config.sessionLock !== undefined) return;\n  if (!config.checkpointStore) return;\n  if (process.env.NODE_ENV === \"test\" || process.env.VITEST) return;\n  if (warnedUnlockedStores.has(config.name)) return;\n  warnedUnlockedStores.add(config.name);\n\n  console.warn(\n    `[warlock-ai] orchestrator \"${config.name}\" uses a durable checkpointStore with the default in-process sessionLock. ` +\n      \"That serializes same-session turns within ONE process only; in a horizontally-scaled deployment supply a distributed \" +\n      \"`sessionLock` (Redis/Postgres advisory locks) or use sticky routing. Pass `sessionLock: false` to silence this.\",\n  );\n}\n"],"mappings":"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"}