diff --git a/docker-compose.e2e.yml b/docker-compose.e2e.yml index daacaadec..23cdf032b 100644 --- a/docker-compose.e2e.yml +++ b/docker-compose.e2e.yml @@ -54,6 +54,11 @@ services: - LLM_SERVER_KEY=e2e-dummy-key - LLM_SERVER_MODEL=e2e-mock - EMBEDDING_PROVIDER=none + # Creating a bridge for the nested worker changes the runner's network + # topology, which makes Chromium drop the flow's HTTP/WebSocket traffic. + # This fixed mock scenario only executes `uname -a`, so it does not need + # an isolated worker network. + - DOCKER_NETWORK=host # The base service has no healthcheck, so `up --wait` would return before # goose migrations finish and the API serves. The runtime image is alpine — # probe with busybox wget, not curl. diff --git a/frontend/e2e/mock-llm/scenario.mjs b/frontend/e2e/mock-llm/scenario.mjs index f845d0ded..4e8cd40bf 100644 --- a/frontend/e2e/mock-llm/scenario.mjs +++ b/frontend/e2e/mock-llm/scenario.mjs @@ -104,6 +104,10 @@ export const RULES = [ ], }, { + // Flow creation starts its worker before the mutation returns. Give the + // browser time to route to the flow and attach the live log subscriptions + // before this fixture emits its only sandbox command and final messages. + delayMs: 1_000, label: 'agent-terminal', match: /"terminal"/, toolCalls: [ diff --git a/frontend/e2e/mock-llm/server.mjs b/frontend/e2e/mock-llm/server.mjs index a8354ada2..ef2070d66 100644 --- a/frontend/e2e/mock-llm/server.mjs +++ b/frontend/e2e/mock-llm/server.mjs @@ -4,6 +4,7 @@ // answers stay deterministic: the whole request JSON is matched against // scenario.mjs rules in order, first hit wins. import { createServer } from 'node:http'; +import { setTimeout as sleep } from 'node:timers/promises'; import { FALLBACK, RULES } from './scenario.mjs'; @@ -143,6 +144,11 @@ createServer(async (request, response) => { const toolNames = toolList.map((tool) => tool?.function?.name ?? tool?.type ?? '?').join(','); console.log(`[mock-llm] ${rule.label}: ${payload.stream ? 'stream' : 'plain'} tools=[${toolNames}]`); + + if (rule.delayMs) { + await sleep(rule.delayMs); + } + respondCompletion(response, payload, rule); return;