From 906a0a4504132af6f6bade4b3789aeb232f4a7fc Mon Sep 17 00:00:00 2001 From: Jordan Ritter Date: Fri, 22 May 2026 13:03:28 -0700 Subject: [PATCH 1/5] fix: remove catch-all regex fallback in AG-UI config loader When match.message is absent, the config loader was falling back to /.*/ which silently matched every incoming request and could shadow other fixtures. Now skips message matching when no pattern is specified. --- src/config-loader.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/config-loader.ts b/src/config-loader.ts index 2c26f71..a02e450 100644 --- a/src/config-loader.ts +++ b/src/config-loader.ts @@ -8,6 +8,7 @@ import type { ChaosConfig, RecordConfig } from "./types.js"; import type { MCPToolDefinition, MCPPromptDefinition } from "./mcp-types.js"; import type { A2AAgentDefinition, A2APart, A2AArtifact, A2AStreamEvent } from "./a2a-types.js"; import type { AGUIEvent } from "./agui-types.js"; +import { buildTextResponse } from "./agui-handler.js"; import { VectorMock } from "./vector-mock.js"; import type { QueryResult } from "./vector-types.js"; import { Logger } from "./logger.js"; @@ -226,7 +227,22 @@ export async function startFromConfig( ); } if (f.text) { - agui.onMessage(f.match.message ?? /.*/, f.text, { delayMs: f.delayMs }); + if (f.match.message !== undefined) { + agui.onMessage(f.match.message, f.text, { delayMs: f.delayMs }); + } else { + // No message pattern — register via addFixture so it only matches + // on other criteria (toolCallId, toolName, stateKey) instead of + // becoming a catch-all that matches every request. + agui.addFixture({ + match: { + toolCallId: f.match.toolCallId, + toolName: f.match.toolName, + stateKey: f.match.stateKey, + }, + events: buildTextResponse(f.text), + delayMs: f.delayMs, + }); + } } else if (f.events) { agui.addFixture({ match: { From ec463d52082fef9c8cf54abbda7387dfa7c23b9d Mon Sep 17 00:00:00 2001 From: Jordan Ritter Date: Fri, 22 May 2026 13:03:34 -0700 Subject: [PATCH 2/5] fix: add runtime validation for AG-UI input messages field Check that input.messages is an array after JSON.parse, return 400 with clear error instead of confusing downstream 404. Fix aguiInput() test helper to include threadId, runId, and id on messages. --- src/__tests__/agui-mock.test.ts | 4 +++- src/agui-mock.ts | 11 +++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/__tests__/agui-mock.test.ts b/src/__tests__/agui-mock.test.ts index a78cb23..75d9928 100644 --- a/src/__tests__/agui-mock.test.ts +++ b/src/__tests__/agui-mock.test.ts @@ -102,7 +102,9 @@ function postRaw( function aguiInput(userMessage: string, extra?: Partial): AGUIRunAgentInput { return { - messages: [{ role: "user", content: userMessage }], + threadId: "test-thread", + runId: "test-run", + messages: [{ id: "msg-1", role: "user", content: userMessage }], ...extra, }; } diff --git a/src/agui-mock.ts b/src/agui-mock.ts index 192b2f8..2da50e1 100644 --- a/src/agui-mock.ts +++ b/src/agui-mock.ts @@ -169,6 +169,17 @@ export class AGUIMock implements Mountable { return true; } + if (input.messages !== undefined && !Array.isArray(input.messages)) { + res.writeHead(400, { "Content-Type": "application/json" }); + res.end( + JSON.stringify({ + error: "Invalid input: 'messages' must be an array when provided", + }), + ); + this.journalRequest(req, pathname, 400); + return true; + } + const fixture = findFixture(input, this.fixtures); if (fixture) { From f60da92b5fdea8a3e4994378b12c4f1ace761dcf Mon Sep 17 00:00:00 2001 From: Jordan Ritter Date: Fri, 22 May 2026 13:03:39 -0700 Subject: [PATCH 3/5] refactor: replace console.warn with logger in writeAGUIEventStream Accept optional logger parameter consistent with other functions. Handle non-Error throws instead of silently swallowing them. --- src/agui-handler.ts | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/agui-handler.ts b/src/agui-handler.ts index acdc115..1c5606c 100644 --- a/src/agui-handler.ts +++ b/src/agui-handler.ts @@ -5,6 +5,7 @@ import * as http from "node:http"; import { randomUUID } from "node:crypto"; +import type { Logger } from "./logger.js"; import type { AGUIRunAgentInput, AGUIFixtureMatch, @@ -597,7 +598,7 @@ export function buildReasoningEncryptedValue( export async function writeAGUIEventStream( res: http.ServerResponse, events: AGUIEvent[], - opts?: { delayMs?: number; signal?: AbortSignal }, + opts?: { delayMs?: number; signal?: AbortSignal; logger?: Logger }, ): Promise { const delayMs = opts?.delayMs ?? 0; @@ -616,9 +617,25 @@ export async function writeAGUIEventStream( res.write(`data: ${JSON.stringify(stamped)}\n\n`); } catch (err) { if (err instanceof TypeError || err instanceof RangeError) { - console.warn("AG-UI SSE write failed (serialization):", (err as Error).message); + const msg = (err as Error).message; + if (opts?.logger) { + opts.logger.warn("AG-UI SSE write failed (serialization):", msg); + } else { + console.warn("AG-UI SSE write failed (serialization):", msg); + } } else if (err instanceof Error) { - console.warn("AG-UI SSE write failed:", err.message); + if (opts?.logger) { + opts.logger.warn("AG-UI SSE write failed:", err.message); + } else { + console.warn("AG-UI SSE write failed:", err.message); + } + } else { + const msg = String(err); + if (opts?.logger) { + opts.logger.warn("AG-UI SSE write failed:", msg); + } else { + console.warn("AG-UI SSE write failed:", msg); + } } break; } From 059b553248f25ac3bd6fc28a9f3e858d18881be1 Mon Sep 17 00:00:00 2001 From: Jordan Ritter Date: Fri, 22 May 2026 13:03:44 -0700 Subject: [PATCH 4/5] fix: harden parseDataOnlySSE to handle multi-line data blocks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Align drift test helper with the fixed version in providers.ts — normalize line endings, strip data: prefixes per line, and rejoin. --- src/__tests__/drift/helpers.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/__tests__/drift/helpers.ts b/src/__tests__/drift/helpers.ts index 5b1cbb4..b8ff82b 100644 --- a/src/__tests__/drift/helpers.ts +++ b/src/__tests__/drift/helpers.ts @@ -87,9 +87,17 @@ export async function httpPostRaw( /** Parse data-only SSE blocks (OpenAI Chat Completions, Gemini). */ export function parseDataOnlySSE(body: string): object[] { return body + .replace(/\r\n/g, "\n") .split("\n\n") - .filter((block) => block.startsWith("data: ") && !block.includes("[DONE]")) - .map((block) => JSON.parse(block.slice(6))); + .filter((block) => block.startsWith("data: ") && block.trim() !== "data: [DONE]") + .map((block) => { + // Rejoin continuation lines (data split across multiple lines) + const json = block + .split("\n") + .map((line) => (line.startsWith("data: ") ? line.slice(6) : line)) + .join(""); + return JSON.parse(json); + }); } /** Parse typed SSE blocks with event: + data: (Anthropic, OpenAI Responses). */ From 38edd5332d394147ff312d91a5251875e3c49e3f Mon Sep 17 00:00:00 2001 From: Jordan Ritter Date: Fri, 22 May 2026 13:03:49 -0700 Subject: [PATCH 5/5] chore: release v1.27.1 --- .claude-plugin/marketplace.json | 2 +- .claude-plugin/plugin.json | 2 +- CHANGELOG.md | 6 ++++++ charts/aimock/Chart.yaml | 2 +- package.json | 2 +- 5 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 36bd608..3bc3de6 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -9,7 +9,7 @@ "source": { "source": "npm", "package": "@copilotkit/aimock", - "version": "^1.27.0" + "version": "^1.27.1" }, "description": "Fixture authoring skill for @copilotkit/aimock — LLM, multimedia (image/TTS/transcription/video), MCP, A2A, AG-UI, vector, embeddings, structured output, sequential responses, streaming physics, record/replay, agent loop patterns, and debugging" } diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index c02577c..c80e672 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "aimock", - "version": "1.27.0", + "version": "1.27.1", "description": "Fixture authoring guidance for @copilotkit/aimock — LLM, multimedia, MCP, A2A, AG-UI, vector, and service mocking", "author": { "name": "CopilotKit" diff --git a/CHANGELOG.md b/CHANGELOG.md index 93a0892..c46c481 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## [Unreleased] +## [1.27.1] - 2026-05-22 + ### Fixed - **Router** — systemMessage array exact-match logic was unsatisfiable for 2+ needles; collapsed to substring matching. Added `elevenlabs-tts` and `translation` to endpoint compatibility filter. @@ -13,6 +15,10 @@ - **Helpers** — extended `resolveUsage` with Gemini-native token fields. Preserved error cause in `resolveResponse` factory rethrow. `buildEmbeddingResponse` accepts optional usage. `extractFormField` escapes regex metacharacters. - **Drift test infra** — retry logging with body consumption, broadened `redactUrl` to cover `api_key`/`apikey`/`token`/`access_token` patterns, URL threaded into error messages with redaction, `parseDataOnlySSE` [DONE] filter fix, `parseTypedSSE` multi-line data handling with null guards. - **Drift collector** — invoke vitest directly via npx to avoid pnpm stdout prefix breaking JSON parse; classify raw stack traces as infrastructure errors instead of crashing. +- **AG-UI config loader** — removed `/.*/` catch-all regex fallback when `match.message` is absent; fixtures without a message pattern no longer shadow other fixtures. +- **AG-UI input validation** — runtime check that `input.messages` is an array after JSON parse; returns 400 instead of confusing downstream 404. +- **AG-UI SSE writer** — `writeAGUIEventStream` uses logger abstraction instead of `console.warn`; handles non-Error throws. +- **Drift test helpers** — `parseDataOnlySSE` handles multi-line data blocks, aligned with `providers.ts` implementation. ## [1.27.0] - 2026-05-20 diff --git a/charts/aimock/Chart.yaml b/charts/aimock/Chart.yaml index 0de02f9..5d39bd6 100644 --- a/charts/aimock/Chart.yaml +++ b/charts/aimock/Chart.yaml @@ -3,4 +3,4 @@ name: aimock description: Mock infrastructure for AI application testing (OpenAI, Anthropic, Gemini, MCP, A2A, vector) type: application version: 0.1.0 -appVersion: "1.27.0" +appVersion: "1.27.1" diff --git a/package.json b/package.json index ecf94fb..9f9c624 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@copilotkit/aimock", - "version": "1.27.0", + "version": "1.27.1", "description": "Mock infrastructure for AI application testing — LLM APIs, image generation, text-to-speech, transcription, audio generation, video generation, MCP tools, A2A agents, AG-UI event streams, vector databases, search, rerank, and moderation. One package, one port, zero dependencies.", "license": "MIT", "keywords": [