{"version":3,"file":"pipeline.mjs","names":[],"sources":["../../../../../../../ai/src/middleware/pipeline.ts"],"sourcesContent":["import type { Logger } from \"@warlock.js/logger\";\nimport type {\n  AgentMiddleware,\n  MiddlewareExecuteContext,\n  MiddlewareSupervisorContext,\n  MiddlewareToolContext,\n  MiddlewareTripContext,\n} from \"../contracts/middleware\";\n\nconst LOG_MODULE = \"ai.middleware\";\n\n/**\n * The four levels at which middleware can hook — mirrors\n * `AgentMiddleware`'s optional `execute` / `trip` / `tool` /\n * `supervisor` keys. Kept as a single named union so callers can pass\n * it around without inline-duplicating the literals. The first three\n * fire on the agent pipeline; `supervisor` fires once around a whole\n * `supervisor.execute()` run.\n */\nexport type MiddlewareLevel = \"execute\" | \"trip\" | \"tool\" | \"supervisor\";\n\n/**\n * Shape of the context object for each level. The pipeline is\n * level-parameterized on the ctx type via this mapping so callers\n * get compile-time narrowing when they instantiate `runPipeline`.\n */\nexport type MiddlewareContextByLevel = {\n  execute: MiddlewareExecuteContext;\n  trip: MiddlewareTripContext;\n  tool: MiddlewareToolContext;\n  supervisor: MiddlewareSupervisorContext;\n};\n\n/**\n * Run an inner async operation through a stack of agent middlewares\n * at a single level, applying the onion-model before/after/onError\n * semantics documented on `AgentMiddleware`.\n *\n * **Semantics.**\n * - `before` hooks run in registration order (top-down).\n *   Returning a defined value from a `before` hook short-circuits the\n *   pipeline with that value as the result, skipping `inner()` and\n *   all deeper `before` / `after` hooks — but outer middleware\n *   `after` hooks (registered earlier) still run on the synthetic\n *   value.\n * - `after` hooks run in reverse registration order (bottom-up).\n *   Returning a defined value replaces the result before it\n *   propagates further out. Returning `void` / `undefined` keeps the\n *   existing result.\n * - `onError` hooks also run in reverse (bottom-up) — any error\n *   thrown by `inner()`, by a `before` hook, or by an `after` hook\n *   unwinds through each frame's `onError` in turn. Returning a\n *   defined value from `onError` recovers: the error is cleared and\n *   the returned value becomes the new result (which then flows\n *   through outer `after` hooks). Returning `void` propagates the\n *   error to the next outer frame.\n *\n * **Implementation.** Built by folding the middleware array from the\n * end inward: each middleware produces a closure that wraps the\n * previous closure (the deeper pipeline). The outermost wrap is\n * middleware index 0 — so registration order matches onion order\n * without any reverse iteration at call time.\n *\n * **No magic.** The pipeline does not swallow, retry, or translate\n * errors. Hooks that throw propagate unchanged (subject to `onError`\n * recovery). Pipeline-level logging is debug-only and respects each\n * middleware's `log: false` kill-switch.\n *\n * @example\n * const response = await runPipeline(\n *   middlewares,\n *   \"trip\",\n *   tripContext,\n *   () => model.complete(messages, callOptions),\n *   logger,\n * );\n */\nexport async function runPipeline<Level extends MiddlewareLevel, TResult>(\n  middlewares: ReadonlyArray<AgentMiddleware>,\n  level: Level,\n  context: MiddlewareContextByLevel[Level],\n  inner: () => Promise<TResult>,\n  logger?: Logger,\n): Promise<TResult> {\n  if (middlewares.length === 0) {\n    return inner();\n  }\n\n  let next: () => Promise<TResult> = inner;\n\n  for (let index = middlewares.length - 1; index >= 0; index--) {\n    const middleware = middlewares[index];\n    const hooks = middleware[level];\n\n    if (!hooks) {\n      continue;\n    }\n\n    const previous = next;\n\n    next = async () => {\n      const logEnabled = middleware.log !== false && logger !== undefined;\n\n      if (hooks.before) {\n        if (logEnabled) {\n          logger!.debug(LOG_MODULE, `${level}.before`, middleware.name, {\n            middleware: middleware.name,\n            level,\n          });\n        }\n\n        const shortCircuit = await (\n          hooks.before as (ctx: unknown) => Promise<unknown> | unknown\n        )(context);\n\n        if (shortCircuit !== undefined) {\n          if (logEnabled) {\n            logger!.debug(\n              LOG_MODULE,\n              `${level}.short-circuit`,\n              middleware.name,\n              {\n                middleware: middleware.name,\n                level,\n              },\n            );\n          }\n\n          return shortCircuit as TResult;\n        }\n      }\n\n      let result: TResult;\n\n      try {\n        result = await previous();\n      } catch (thrown) {\n        if (!hooks.onError) {\n          throw thrown;\n        }\n\n        const recovered = await (\n          hooks.onError as (\n            ctx: unknown,\n            error: unknown,\n          ) => Promise<unknown> | unknown\n        )(context, thrown);\n\n        if (recovered === undefined) {\n          throw thrown;\n        }\n\n        if (logEnabled) {\n          logger!.debug(LOG_MODULE, `${level}.recovered`, middleware.name, {\n            middleware: middleware.name,\n            level,\n          });\n        }\n\n        result = recovered as TResult;\n      }\n\n      if (hooks.after) {\n        const replacement = await (\n          hooks.after as (\n            ctx: unknown,\n            value: unknown,\n          ) => Promise<unknown> | unknown\n        )(context, result);\n\n        if (replacement !== undefined) {\n          result = replacement as TResult;\n        }\n\n        if (logEnabled) {\n          logger!.debug(LOG_MODULE, `${level}.after`, middleware.name, {\n            middleware: middleware.name,\n            level,\n          });\n        }\n      }\n\n      return result;\n    };\n  }\n\n  return next();\n}\n"],"mappings":";AASA,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoEnB,eAAsB,YACpB,aACA,OACA,SACA,OACA,QACkB;CAClB,IAAI,YAAY,WAAW,GACzB,OAAO,MAAM;CAGf,IAAI,OAA+B;CAEnC,KAAK,IAAI,QAAQ,YAAY,SAAS,GAAG,SAAS,GAAG,SAAS;EAC5D,MAAM,aAAa,YAAY;EAC/B,MAAM,QAAQ,WAAW;EAEzB,IAAI,CAAC,OACH;EAGF,MAAM,WAAW;EAEjB,OAAO,YAAY;GACjB,MAAM,aAAa,WAAW,QAAQ,SAAS,WAAW;GAE1D,IAAI,MAAM,QAAQ;IAChB,IAAI,YACF,OAAQ,MAAM,YAAY,GAAG,MAAM,UAAU,WAAW,MAAM;KAC5D,YAAY,WAAW;KACvB;IACF,CAAC;IAGH,MAAM,eAAe,MACnB,MAAM,OACN,OAAO;IAET,IAAI,iBAAiB,QAAW;KAC9B,IAAI,YACF,OAAQ,MACN,YACA,GAAG,MAAM,iBACT,WAAW,MACX;MACE,YAAY,WAAW;MACvB;KACF,CACF;KAGF,OAAO;IACT;GACF;GAEA,IAAI;GAEJ,IAAI;IACF,SAAS,MAAM,SAAS;GAC1B,SAAS,QAAQ;IACf,IAAI,CAAC,MAAM,SACT,MAAM;IAGR,MAAM,YAAY,MAChB,MAAM,QAIN,SAAS,MAAM;IAEjB,IAAI,cAAc,QAChB,MAAM;IAGR,IAAI,YACF,OAAQ,MAAM,YAAY,GAAG,MAAM,aAAa,WAAW,MAAM;KAC/D,YAAY,WAAW;KACvB;IACF,CAAC;IAGH,SAAS;GACX;GAEA,IAAI,MAAM,OAAO;IACf,MAAM,cAAc,MAClB,MAAM,MAIN,SAAS,MAAM;IAEjB,IAAI,gBAAgB,QAClB,SAAS;IAGX,IAAI,YACF,OAAQ,MAAM,YAAY,GAAG,MAAM,SAAS,WAAW,MAAM;KAC3D,YAAY,WAAW;KACvB;IACF,CAAC;GAEL;GAEA,OAAO;EACT;CACF;CAEA,OAAO,KAAK;AACd"}