@@ -15,6 +15,13 @@ import {
1515import { buildAgentSessionKey } from "openclaw/plugin-sdk/routing" ;
1616import { fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime" ;
1717import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime" ;
18+ import {
19+ callQaBrowserRequest ,
20+ qaBrowserAct ,
21+ qaBrowserOpenTab ,
22+ qaBrowserSnapshot ,
23+ waitForQaBrowserReady ,
24+ } from "./browser-runtime.js" ;
1825import { ensureRepoBoundDirectory , resolveRepoRelativeOutputDir } from "./cli-paths.js" ;
1926import { waitForCronRunCompletion } from "./cron-run-wait.js" ;
2027import {
@@ -60,6 +67,14 @@ import { qaChannelPlugin, type QaBusMessage } from "./runtime-api.js";
6067import { readQaBootstrapScenarioCatalog } from "./scenario-catalog.js" ;
6168import { runScenarioFlow } from "./scenario-flow-runner.js" ;
6269import { createQaScenarioRuntimeApi } from "./scenario-runtime-api.js" ;
70+ import {
71+ closeAllQaWebSessions ,
72+ qaWebEvaluate ,
73+ qaWebOpenPage ,
74+ qaWebSnapshot ,
75+ qaWebType ,
76+ qaWebWait ,
77+ } from "./web-runtime.js" ;
6378
6479type QaSuiteStep = {
6580 name : string ;
@@ -313,6 +328,18 @@ function collectQaSuiteGatewayConfigPatch(
313328 return merged ;
314329}
315330
331+ function collectQaSuiteGatewayRuntimeOptions (
332+ scenarios : ReturnType < typeof readQaBootstrapScenarioCatalog > [ "scenarios" ] ,
333+ ) {
334+ let forwardHostHome = false ;
335+ for ( const scenario of scenarios ) {
336+ if ( scenario . gatewayRuntime ?. forwardHostHome === true ) {
337+ forwardHostHome = true ;
338+ }
339+ }
340+ return forwardHostHome ? { forwardHostHome : true } : undefined ;
341+ }
342+
316343function liveTurnTimeoutMs ( env : QaSuiteEnvironment , fallbackMs : number ) {
317344 return resolveQaLiveTurnTimeoutMs ( env , fallbackMs ) ;
318345}
@@ -1236,6 +1263,16 @@ function createScenarioFlowApi(
12361263 waitForGatewayHealthy,
12371264 waitForTransportReady,
12381265 waitForQaChannelReady,
1266+ browserRequest : callQaBrowserRequest ,
1267+ waitForBrowserReady : waitForQaBrowserReady ,
1268+ browserOpenTab : qaBrowserOpenTab ,
1269+ browserSnapshot : qaBrowserSnapshot ,
1270+ browserAct : qaBrowserAct ,
1271+ webOpenPage : qaWebOpenPage ,
1272+ webWait : qaWebWait ,
1273+ webType : qaWebType ,
1274+ webSnapshot : qaWebSnapshot ,
1275+ webEvaluate : qaWebEvaluate ,
12391276 waitForConfigRestartSettle,
12401277 patchConfig,
12411278 applyConfig,
@@ -1284,6 +1321,7 @@ function createScenarioFlowApi(
12841321
12851322export const qaSuiteTesting = {
12861323 collectQaSuiteGatewayConfigPatch,
1324+ collectQaSuiteGatewayRuntimeOptions,
12871325 collectQaSuitePluginIds,
12881326 createScenarioWaitForCondition,
12891327 findFailureOutboundMessage,
@@ -1397,6 +1435,7 @@ export async function runQaSuite(params?: QaSuiteRunParams): Promise<QaSuiteResu
13971435 } ) ;
13981436 const enabledPluginIds = collectQaSuitePluginIds ( selectedCatalogScenarios ) ;
13991437 const gatewayConfigPatch = collectQaSuiteGatewayConfigPatch ( selectedCatalogScenarios ) ;
1438+ const gatewayRuntimeOptions = collectQaSuiteGatewayRuntimeOptions ( selectedCatalogScenarios ) ;
14001439 const concurrency = normalizeQaSuiteConcurrency (
14011440 params ?. concurrency ,
14021441 selectedCatalogScenarios . length ,
@@ -1594,6 +1633,7 @@ export async function runQaSuite(params?: QaSuiteRunParams): Promise<QaSuiteResu
15941633 claudeCliAuthMode : params ?. claudeCliAuthMode ,
15951634 controlUiEnabled : params ?. controlUiEnabled ?? true ,
15961635 enabledPluginIds,
1636+ forwardHostHome : gatewayRuntimeOptions ?. forwardHostHome ,
15971637 mutateConfig : gatewayConfigPatch
15981638 ? ( cfg ) => applyQaMergePatch ( cfg , gatewayConfigPatch ) as OpenClawConfig
15991639 : undefined ,
@@ -1606,9 +1646,9 @@ export async function runQaSuite(params?: QaSuiteRunParams): Promise<QaSuiteResu
16061646 lab,
16071647 mock,
16081648 gateway,
1609- cfg : transport . createGatewayConfig ( {
1610- baseUrl : lab . listenUrl ,
1611- } ) ,
1649+ // Markdown scenarios should see the full staged gateway config, not just
1650+ // the transport fragment. Routing/session/plugin assertions depend on it.
1651+ cfg : gateway . cfg ,
16121652 transport,
16131653 repoRoot,
16141654 providerMode,
@@ -1717,6 +1757,7 @@ export async function runQaSuite(params?: QaSuiteRunParams): Promise<QaSuiteResu
17171757 preserveGatewayRuntimeDir = path . join ( outputDir , "artifacts" , "gateway-runtime" ) ;
17181758 throw error ;
17191759 } finally {
1760+ await closeAllQaWebSessions ( ) ;
17201761 const keepTemp = process . env . OPENCLAW_QA_KEEP_TEMP === "1" || false ;
17211762 await gateway . stop ( {
17221763 keepTemp,
0 commit comments