{"version":3,"file":"agui-mock.cjs","names":["Logger","buildTextResponse","buildToolCallResponse","buildStateUpdate","buildReasoningResponse","readBody","findFixture","writeAGUIEventStream","proxyAndRecordAGUI","http","flattenHeaders"],"sources":["../src/agui-mock.ts"],"sourcesContent":["import * as http from \"node:http\";\nimport type { Mountable } from \"./types.js\";\nimport type { Journal } from \"./journal.js\";\nimport type { MetricsRegistry } from \"./metrics.js\";\nimport type {\n  AGUIFixture,\n  AGUIMockOptions,\n  AGUIRecordConfig,\n  AGUIEvent,\n  AGUIRunAgentInput,\n} from \"./agui-types.js\";\nimport {\n  findFixture,\n  buildTextResponse,\n  buildToolCallResponse,\n  buildStateUpdate,\n  buildReasoningResponse,\n  writeAGUIEventStream,\n} from \"./agui-handler.js\";\nimport { flattenHeaders, readBody } from \"./helpers.js\";\nimport { proxyAndRecordAGUI } from \"./agui-recorder.js\";\nimport { Logger, type LogLevel } from \"./logger.js\";\n\nexport class AGUIMock implements Mountable {\n  private fixtures: AGUIFixture[] = [];\n  private server: http.Server | null = null;\n  private journal: Journal | null = null;\n  private registry: MetricsRegistry | null = null;\n  private options: AGUIMockOptions;\n  private baseUrl = \"\";\n  private recordConfig: AGUIRecordConfig | undefined;\n  private logger: Logger;\n\n  constructor(options?: AGUIMockOptions) {\n    this.options = options ?? {};\n    this.logger = new Logger((options?.logLevel as LogLevel) ?? \"warn\");\n  }\n\n  // ---- Fluent registration API ----\n\n  addFixture(fixture: AGUIFixture): this {\n    this.fixtures.push(fixture);\n    return this;\n  }\n\n  onMessage(pattern: string | RegExp, text: string, opts?: { delayMs?: number }): this {\n    const events = buildTextResponse(text);\n    this.fixtures.push({\n      match: { message: pattern },\n      events,\n      delayMs: opts?.delayMs,\n    });\n    return this;\n  }\n\n  onRun(pattern: string | RegExp, events: AGUIEvent[], delayMs?: number): this {\n    this.fixtures.push({\n      match: { message: pattern },\n      events,\n      delayMs,\n    });\n    return this;\n  }\n\n  onToolCall(\n    pattern: string | RegExp,\n    toolName: string,\n    args: string,\n    opts?: { result?: string; delayMs?: number },\n  ): this {\n    const events = buildToolCallResponse(toolName, args, {\n      result: opts?.result,\n    });\n    this.fixtures.push({\n      match: { message: pattern },\n      events,\n      delayMs: opts?.delayMs,\n    });\n    return this;\n  }\n\n  onStateKey(key: string, snapshot: Record<string, unknown>, delayMs?: number): this {\n    const events = buildStateUpdate(snapshot);\n    this.fixtures.push({\n      match: { stateKey: key },\n      events,\n      delayMs,\n    });\n    return this;\n  }\n\n  onReasoning(pattern: string | RegExp, text: string, opts?: { delayMs?: number }): this {\n    const events = buildReasoningResponse(text);\n    this.fixtures.push({\n      match: { message: pattern },\n      events,\n      delayMs: opts?.delayMs,\n    });\n    return this;\n  }\n\n  onPredicate(\n    predicate: (input: AGUIRunAgentInput) => boolean,\n    events: AGUIEvent[],\n    delayMs?: number,\n  ): this {\n    this.fixtures.push({\n      match: { predicate },\n      events,\n      delayMs,\n    });\n    return this;\n  }\n\n  onToolResult(toolCallId: string, events: AGUIEvent[], delayMs?: number): this {\n    this.fixtures.push({\n      match: { toolCallId },\n      events,\n      delayMs,\n    });\n    return this;\n  }\n\n  enableRecording(config: AGUIRecordConfig): this {\n    this.recordConfig = config;\n    return this;\n  }\n\n  reset(): this {\n    this.fixtures = [];\n    this.recordConfig = undefined;\n    return this;\n  }\n\n  // ---- Mountable interface ----\n\n  async handleRequest(\n    req: http.IncomingMessage,\n    res: http.ServerResponse,\n    pathname: string,\n  ): Promise<boolean> {\n    if (req.method !== \"POST\" || (pathname !== \"/\" && pathname !== \"\")) {\n      return false;\n    }\n\n    if (this.registry) {\n      this.registry.incrementCounter(\"aimock_agui_requests_total\", { method: \"POST\" });\n    }\n\n    let body: string;\n    try {\n      body = await readBody(req);\n    } catch (err) {\n      res.writeHead(400, { \"Content-Type\": \"application/json\" });\n      const detail = err instanceof Error ? err.message : \"body read failed\";\n      res.end(JSON.stringify({ error: `Failed to read request body: ${detail}` }));\n      this.journalRequest(req, pathname, 400);\n      return true;\n    }\n\n    let input: AGUIRunAgentInput;\n    try {\n      input = JSON.parse(body) as AGUIRunAgentInput;\n    } catch (err) {\n      res.writeHead(400, { \"Content-Type\": \"application/json\" });\n      const detail = err instanceof Error ? err.message : \"unknown parse error\";\n      res.end(JSON.stringify({ error: `Invalid JSON body: ${detail}` }));\n      this.journalRequest(req, pathname, 400);\n      return true;\n    }\n\n    if (input.messages !== undefined && !Array.isArray(input.messages)) {\n      res.writeHead(400, { \"Content-Type\": \"application/json\" });\n      res.end(\n        JSON.stringify({\n          error: \"Invalid input: 'messages' must be an array when provided\",\n        }),\n      );\n      this.journalRequest(req, pathname, 400);\n      return true;\n    }\n\n    const fixture = findFixture(input, this.fixtures);\n\n    if (fixture) {\n      await writeAGUIEventStream(res, fixture.events, { delayMs: fixture.delayMs });\n      this.journalRequest(req, pathname, 200);\n      return true;\n    }\n\n    // No match — if recording is enabled, proxy to upstream\n    if (this.recordConfig) {\n      const result = await proxyAndRecordAGUI(\n        req,\n        res,\n        input,\n        this.fixtures,\n        this.recordConfig,\n        this.logger,\n      );\n      if (result !== false) {\n        this.journalRequest(req, pathname, result);\n        return true;\n      }\n    }\n\n    // No match, no recorder — 404\n    res.writeHead(404, { \"Content-Type\": \"application/json\" });\n    res.end(JSON.stringify({ error: \"No matching AG-UI fixture\" }));\n    this.journalRequest(req, pathname, 404);\n    return true;\n  }\n\n  health(): { status: string; fixtures: number } {\n    return {\n      status: \"ok\",\n      fixtures: this.fixtures.length,\n    };\n  }\n\n  setJournal(journal: Journal): void {\n    this.journal = journal;\n  }\n\n  setBaseUrl(url: string): void {\n    this.baseUrl = url;\n  }\n\n  setRegistry(registry: MetricsRegistry): void {\n    this.registry = registry;\n  }\n\n  // ---- Standalone mode ----\n\n  async start(): Promise<string> {\n    if (this.server) {\n      throw new Error(\"AGUIMock server already started\");\n    }\n\n    const host = this.options.host ?? \"127.0.0.1\";\n    const port = this.options.port ?? 0;\n\n    return new Promise<string>((resolve, reject) => {\n      const srv = http.createServer(async (req, res) => {\n        const url = new URL(req.url ?? \"/\", `http://${req.headers.host ?? \"localhost\"}`);\n        const handled = await this.handleRequest(req, res, url.pathname).catch((err) => {\n          this.logger.error(`AGUIMock request error: ${err instanceof Error ? err.message : err}`);\n          if (!res.headersSent) {\n            res.writeHead(500);\n            res.end(\"Internal server error\");\n          } else if (!res.writableEnded) {\n            res.end();\n          }\n          return true;\n        });\n        if (!handled && !res.headersSent) {\n          res.writeHead(404, { \"Content-Type\": \"application/json\" });\n          res.end(JSON.stringify({ error: \"Not found\" }));\n        }\n      });\n\n      srv.on(\"error\", reject);\n\n      srv.listen(port, host, () => {\n        const addr = srv.address();\n        if (typeof addr === \"object\" && addr !== null) {\n          this.baseUrl = `http://${host}:${addr.port}`;\n        }\n        this.server = srv;\n        resolve(this.baseUrl);\n      });\n    });\n  }\n\n  async stop(): Promise<void> {\n    if (!this.server) {\n      throw new Error(\"AGUIMock server not started\");\n    }\n    const srv = this.server;\n    await new Promise<void>((resolve, reject) => {\n      srv.close((err: Error | undefined) => (err ? reject(err) : resolve()));\n    });\n    this.server = null;\n  }\n\n  get url(): string {\n    if (!this.server) {\n      throw new Error(\"AGUIMock server not started\");\n    }\n    return this.baseUrl;\n  }\n\n  // ---- Private helpers ----\n\n  private journalRequest(req: http.IncomingMessage, pathname: string, status: number): void {\n    if (this.journal) {\n      this.journal.add({\n        method: req.method ?? \"POST\",\n        path: req.url ?? pathname,\n        headers: flattenHeaders(req.headers),\n        body: null,\n        service: \"agui\",\n        response: { status, fixture: null },\n      });\n    }\n  }\n}\n"],"mappings":";;;;;;;;;AAuBA,IAAa,WAAb,MAA2C;CACzC,AAAQ,WAA0B,EAAE;CACpC,AAAQ,SAA6B;CACrC,AAAQ,UAA0B;CAClC,AAAQ,WAAmC;CAC3C,AAAQ;CACR,AAAQ,UAAU;CAClB,AAAQ;CACR,AAAQ;CAER,YAAY,SAA2B;AACrC,OAAK,UAAU,WAAW,EAAE;AAC5B,OAAK,SAAS,IAAIA,sBAAQ,SAAS,YAAyB,OAAO;;CAKrE,WAAW,SAA4B;AACrC,OAAK,SAAS,KAAK,QAAQ;AAC3B,SAAO;;CAGT,UAAU,SAA0B,MAAc,MAAmC;EACnF,MAAM,SAASC,uCAAkB,KAAK;AACtC,OAAK,SAAS,KAAK;GACjB,OAAO,EAAE,SAAS,SAAS;GAC3B;GACA,SAAS,MAAM;GAChB,CAAC;AACF,SAAO;;CAGT,MAAM,SAA0B,QAAqB,SAAwB;AAC3E,OAAK,SAAS,KAAK;GACjB,OAAO,EAAE,SAAS,SAAS;GAC3B;GACA;GACD,CAAC;AACF,SAAO;;CAGT,WACE,SACA,UACA,MACA,MACM;EACN,MAAM,SAASC,2CAAsB,UAAU,MAAM,EACnD,QAAQ,MAAM,QACf,CAAC;AACF,OAAK,SAAS,KAAK;GACjB,OAAO,EAAE,SAAS,SAAS;GAC3B;GACA,SAAS,MAAM;GAChB,CAAC;AACF,SAAO;;CAGT,WAAW,KAAa,UAAmC,SAAwB;EACjF,MAAM,SAASC,sCAAiB,SAAS;AACzC,OAAK,SAAS,KAAK;GACjB,OAAO,EAAE,UAAU,KAAK;GACxB;GACA;GACD,CAAC;AACF,SAAO;;CAGT,YAAY,SAA0B,MAAc,MAAmC;EACrF,MAAM,SAASC,4CAAuB,KAAK;AAC3C,OAAK,SAAS,KAAK;GACjB,OAAO,EAAE,SAAS,SAAS;GAC3B;GACA,SAAS,MAAM;GAChB,CAAC;AACF,SAAO;;CAGT,YACE,WACA,QACA,SACM;AACN,OAAK,SAAS,KAAK;GACjB,OAAO,EAAE,WAAW;GACpB;GACA;GACD,CAAC;AACF,SAAO;;CAGT,aAAa,YAAoB,QAAqB,SAAwB;AAC5E,OAAK,SAAS,KAAK;GACjB,OAAO,EAAE,YAAY;GACrB;GACA;GACD,CAAC;AACF,SAAO;;CAGT,gBAAgB,QAAgC;AAC9C,OAAK,eAAe;AACpB,SAAO;;CAGT,QAAc;AACZ,OAAK,WAAW,EAAE;AAClB,OAAK,eAAe;AACpB,SAAO;;CAKT,MAAM,cACJ,KACA,KACA,UACkB;AAClB,MAAI,IAAI,WAAW,UAAW,aAAa,OAAO,aAAa,GAC7D,QAAO;AAGT,MAAI,KAAK,SACP,MAAK,SAAS,iBAAiB,8BAA8B,EAAE,QAAQ,QAAQ,CAAC;EAGlF,IAAI;AACJ,MAAI;AACF,UAAO,MAAMC,yBAAS,IAAI;WACnB,KAAK;AACZ,OAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;GAC1D,MAAM,SAAS,eAAe,QAAQ,IAAI,UAAU;AACpD,OAAI,IAAI,KAAK,UAAU,EAAE,OAAO,gCAAgC,UAAU,CAAC,CAAC;AAC5E,QAAK,eAAe,KAAK,UAAU,IAAI;AACvC,UAAO;;EAGT,IAAI;AACJ,MAAI;AACF,WAAQ,KAAK,MAAM,KAAK;WACjB,KAAK;AACZ,OAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;GAC1D,MAAM,SAAS,eAAe,QAAQ,IAAI,UAAU;AACpD,OAAI,IAAI,KAAK,UAAU,EAAE,OAAO,sBAAsB,UAAU,CAAC,CAAC;AAClE,QAAK,eAAe,KAAK,UAAU,IAAI;AACvC,UAAO;;AAGT,MAAI,MAAM,aAAa,UAAa,CAAC,MAAM,QAAQ,MAAM,SAAS,EAAE;AAClE,OAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;AAC1D,OAAI,IACF,KAAK,UAAU,EACb,OAAO,4DACR,CAAC,CACH;AACD,QAAK,eAAe,KAAK,UAAU,IAAI;AACvC,UAAO;;EAGT,MAAM,UAAUC,iCAAY,OAAO,KAAK,SAAS;AAEjD,MAAI,SAAS;AACX,SAAMC,0CAAqB,KAAK,QAAQ,QAAQ,EAAE,SAAS,QAAQ,SAAS,CAAC;AAC7E,QAAK,eAAe,KAAK,UAAU,IAAI;AACvC,UAAO;;AAIT,MAAI,KAAK,cAAc;GACrB,MAAM,SAAS,MAAMC,yCACnB,KACA,KACA,OACA,KAAK,UACL,KAAK,cACL,KAAK,OACN;AACD,OAAI,WAAW,OAAO;AACpB,SAAK,eAAe,KAAK,UAAU,OAAO;AAC1C,WAAO;;;AAKX,MAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;AAC1D,MAAI,IAAI,KAAK,UAAU,EAAE,OAAO,6BAA6B,CAAC,CAAC;AAC/D,OAAK,eAAe,KAAK,UAAU,IAAI;AACvC,SAAO;;CAGT,SAA+C;AAC7C,SAAO;GACL,QAAQ;GACR,UAAU,KAAK,SAAS;GACzB;;CAGH,WAAW,SAAwB;AACjC,OAAK,UAAU;;CAGjB,WAAW,KAAmB;AAC5B,OAAK,UAAU;;CAGjB,YAAY,UAAiC;AAC3C,OAAK,WAAW;;CAKlB,MAAM,QAAyB;AAC7B,MAAI,KAAK,OACP,OAAM,IAAI,MAAM,kCAAkC;EAGpD,MAAM,OAAO,KAAK,QAAQ,QAAQ;EAClC,MAAM,OAAO,KAAK,QAAQ,QAAQ;AAElC,SAAO,IAAI,SAAiB,SAAS,WAAW;GAC9C,MAAM,MAAMC,UAAK,aAAa,OAAO,KAAK,QAAQ;IAChD,MAAM,MAAM,IAAI,IAAI,IAAI,OAAO,KAAK,UAAU,IAAI,QAAQ,QAAQ,cAAc;AAWhF,QAAI,CAVY,MAAM,KAAK,cAAc,KAAK,KAAK,IAAI,SAAS,CAAC,OAAO,QAAQ;AAC9E,UAAK,OAAO,MAAM,2BAA2B,eAAe,QAAQ,IAAI,UAAU,MAAM;AACxF,SAAI,CAAC,IAAI,aAAa;AACpB,UAAI,UAAU,IAAI;AAClB,UAAI,IAAI,wBAAwB;gBACvB,CAAC,IAAI,cACd,KAAI,KAAK;AAEX,YAAO;MACP,IACc,CAAC,IAAI,aAAa;AAChC,SAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;AAC1D,SAAI,IAAI,KAAK,UAAU,EAAE,OAAO,aAAa,CAAC,CAAC;;KAEjD;AAEF,OAAI,GAAG,SAAS,OAAO;AAEvB,OAAI,OAAO,MAAM,YAAY;IAC3B,MAAM,OAAO,IAAI,SAAS;AAC1B,QAAI,OAAO,SAAS,YAAY,SAAS,KACvC,MAAK,UAAU,UAAU,KAAK,GAAG,KAAK;AAExC,SAAK,SAAS;AACd,YAAQ,KAAK,QAAQ;KACrB;IACF;;CAGJ,MAAM,OAAsB;AAC1B,MAAI,CAAC,KAAK,OACR,OAAM,IAAI,MAAM,8BAA8B;EAEhD,MAAM,MAAM,KAAK;AACjB,QAAM,IAAI,SAAe,SAAS,WAAW;AAC3C,OAAI,OAAO,QAA4B,MAAM,OAAO,IAAI,GAAG,SAAS,CAAE;IACtE;AACF,OAAK,SAAS;;CAGhB,IAAI,MAAc;AAChB,MAAI,CAAC,KAAK,OACR,OAAM,IAAI,MAAM,8BAA8B;AAEhD,SAAO,KAAK;;CAKd,AAAQ,eAAe,KAA2B,UAAkB,QAAsB;AACxF,MAAI,KAAK,QACP,MAAK,QAAQ,IAAI;GACf,QAAQ,IAAI,UAAU;GACtB,MAAM,IAAI,OAAO;GACjB,SAASC,+BAAe,IAAI,QAAQ;GACpC,MAAM;GACN,SAAS;GACT,UAAU;IAAE;IAAQ,SAAS;IAAM;GACpC,CAAC"}