diff --git a/scripts/src/hwlab-node-web-observe-runner-source.ts b/scripts/src/hwlab-node-web-observe-runner-source.ts index 83548fa0..3379a421 100644 --- a/scripts/src/hwlab-node-web-observe-runner-source.ts +++ b/scripts/src/hwlab-node-web-observe-runner-source.ts @@ -358,8 +358,9 @@ async function processCommand(command) { alternateResponsePaths: ["/v1/agent/chat/steer"], noActiveReason: "send-no-turn-composer", throwOnActionMismatch: true, + expectedActionWaitMs: command.expectedActionWaitMs, }), "sendPrompt"); - case "steer": return withObserverSync(await sendPrompt(String(command.text || ""), { expectedAction: "steer", responsePath: "/v1/agent/chat/steer", noActiveReason: "steer-no-active-turn" }), "steer"); + case "steer": return withObserverSync(await sendPrompt(String(command.text || ""), { expectedAction: "steer", responsePath: "/v1/agent/chat/steer", noActiveReason: "steer-no-active-turn", expectedActionWaitMs: command.expectedActionWaitMs }), "steer"); case "cancel": return withObserverSync(await cancelRunningTurn(), "cancel"); case "selectProvider": return withObserverSync(await selectProvider(String(command.provider || command.value || command.text || "")), "selectProvider"); case "clickSession": return withObserverSync(await clickSession(String(command.sessionId || command.value || "")), "clickSession"); @@ -1520,8 +1521,11 @@ async function sendPrompt(text, options = {}) { ].join(", ")).last(); await submit.waitFor({ state: "visible", timeout: 15000 }); if (options.expectedAction) { - const actionWaitMs = Number.isFinite(Number(options.expectedActionWaitMs)) - ? Math.max(1000, Math.trunc(Number(options.expectedActionWaitMs))) + const configuredActionWaitMs = options.expectedActionWaitMs === null || options.expectedActionWaitMs === undefined || options.expectedActionWaitMs === "" + ? null + : Number(options.expectedActionWaitMs); + const actionWaitMs = Number.isFinite(configuredActionWaitMs) + ? Math.max(1000, Math.trunc(configuredActionWaitMs)) : options.expectedAction === "turn" ? 180000 : 1000; const actionDeadline = Date.now() + actionWaitMs; let composer = null; @@ -3798,6 +3802,7 @@ function commandInputSummary(command) { severity: command.severity || null, alternateSessionStrategy: command.alternateSessionStrategy || null, expectedSentinelRange: command.expectedSentinelRange || null, + expectedActionWaitMs: command.expectedActionWaitMs === null || command.expectedActionWaitMs === undefined || command.expectedActionWaitMs === "" ? null : Number(command.expectedActionWaitMs), requireComposerReady: command.requireComposerReady === true, findingId: command.findingId || null, blocking: command.blocking === true ? true : command.blocking === false ? false : null, diff --git a/scripts/src/hwlab-node-web-sentinel-cicd.ts b/scripts/src/hwlab-node-web-sentinel-cicd.ts index c94e1457..5b1a3a39 100644 --- a/scripts/src/hwlab-node-web-sentinel-cicd.ts +++ b/scripts/src/hwlab-node-web-sentinel-cicd.ts @@ -2317,6 +2317,7 @@ function runSentinelQuickVerify(state: SentinelCicdState, reason: string, timeou } if (type === "sendPrompt") { args.push("--text", prompts.prompts[promptIndex % prompts.prompts.length] ?? ""); + args.push("--expected-action-wait-ms", String(numberAtNullable(item, "expectedActionWaitMs") ?? 45000)); promptIndex += 1; } appendScenarioObserveCommandArgs(args, item, { skipText: type === "sendPrompt" }); diff --git a/scripts/src/hwlab-node/entry.ts b/scripts/src/hwlab-node/entry.ts index a46a7ca4..1737b9f2 100644 --- a/scripts/src/hwlab-node/entry.ts +++ b/scripts/src/hwlab-node/entry.ts @@ -202,6 +202,7 @@ export interface NodeWebProbeObserveOptions { commandSeverity: string | null; commandAlternateSessionStrategy: string | null; commandExpectedSentinelRange: string | null; + commandExpectedActionWaitMs: number | null; commandRequireComposerReady: boolean; commandFindingId: string | null; commandBlocking: boolean | null; diff --git a/scripts/src/hwlab-node/web-probe-observe.ts b/scripts/src/hwlab-node/web-probe-observe.ts index 53ad3f8c..e8358398 100644 --- a/scripts/src/hwlab-node/web-probe-observe.ts +++ b/scripts/src/hwlab-node/web-probe-observe.ts @@ -259,6 +259,7 @@ export function parseNodeWebProbeObserveOptions( "--severity", "--alternate-session-strategy", "--expected-sentinel-range", + "--expected-action-wait-ms", "--finding-id", "--source-id", "--file-ref", @@ -349,6 +350,11 @@ export function parseNodeWebProbeObserveOptions( const commandSeverity = optionValue(args, "--severity") ?? null; const commandAlternateSessionStrategy = optionValue(args, "--alternate-session-strategy") ?? null; const commandExpectedSentinelRange = optionValue(args, "--expected-sentinel-range") ?? null; + const commandExpectedActionWaitMsRaw = optionValue(args, "--expected-action-wait-ms") ?? null; + const commandExpectedActionWaitMs = commandExpectedActionWaitMsRaw === null ? null : Number(commandExpectedActionWaitMsRaw); + if (commandExpectedActionWaitMs !== null && (!Number.isInteger(commandExpectedActionWaitMs) || commandExpectedActionWaitMs < 1000 || commandExpectedActionWaitMs > 600000)) { + throw new Error("unsafe web-probe observe --expected-action-wait-ms: expected integer 1000-600000"); + } const commandFindingId = optionValue(args, "--finding-id") ?? null; const commandBlocking = args.includes("--blocking") ? true : args.includes("--non-blocking") ? false : null; for (const [label, value] of [ @@ -422,6 +428,7 @@ export function parseNodeWebProbeObserveOptions( commandSeverity, commandAlternateSessionStrategy, commandExpectedSentinelRange, + commandExpectedActionWaitMs, commandRequireComposerReady: args.includes("--require-composer-ready"), commandFindingId, commandBlocking, @@ -1492,6 +1499,7 @@ export function runNodeWebProbeObserveCommand(options: NodeWebProbeObserveOption severity: options.commandSeverity, alternateSessionStrategy: options.commandAlternateSessionStrategy, expectedSentinelRange: options.commandExpectedSentinelRange, + expectedActionWaitMs: options.commandExpectedActionWaitMs, requireComposerReady: options.commandRequireComposerReady, findingId: options.commandFindingId, blocking: options.commandBlocking,