feat: add web sentinel session invariance checks

This commit is contained in:
Codex
2026-06-26 12:00:13 +00:00
parent ad7043f3f4
commit dee36326b9
9 changed files with 622 additions and 25 deletions
+10
View File
@@ -122,6 +122,9 @@ export type NodeWebProbeObserveCommandType =
| "cancel"
| "selectProvider"
| "clickSession"
| "refreshCurrentSession"
| "switchAwayAndBack"
| "assertSessionInvariant"
| "selectProjectSource"
| "selectMdtodoSource"
| "selectMdtodoFile"
@@ -186,6 +189,13 @@ export interface NodeWebProbeObserveOptions {
commandLabel: string | null;
commandSessionId: string | null;
commandProvider: string | null;
commandAfterRound: number | null;
commandSeverity: string | null;
commandAlternateSessionStrategy: string | null;
commandExpectedSentinelRange: string | null;
commandRequireComposerReady: boolean;
commandFindingId: string | null;
commandBlocking: boolean | null;
commandSourceId: string | null;
commandFileRef: string | null;
commandTaskRef: string | null;
+53 -17
View File
@@ -200,22 +200,27 @@ export function parseNodeWebProbeObserveOptions(
"--text",
"--path",
"--label",
"--session-id",
"--provider",
"--source-id",
"--file-ref",
"--task-ref",
"--task",
"--task-id",
"--title",
"--body",
"--status",
"--hwpod-id",
"--node-id",
"--workspace-root",
"--workspace-root-ref",
"--root",
]), new Set(["--force", "--full", "--raw", "--text-stdin"]));
"--session-id",
"--provider",
"--after-round",
"--severity",
"--alternate-session-strategy",
"--expected-sentinel-range",
"--finding-id",
"--source-id",
"--file-ref",
"--task-ref",
"--task",
"--task-id",
"--title",
"--body",
"--status",
"--hwpod-id",
"--node-id",
"--workspace-root",
"--workspace-root-ref",
"--root",
]), new Set(["--force", "--full", "--raw", "--text-stdin", "--require-composer-ready", "--blocking", "--non-blocking"]));
const commandTypeRaw = optionValue(args, "--type") ?? null;
const commandType = commandTypeRaw === null ? null : parseNodeWebProbeObserveCommandType(commandTypeRaw);
const stateDir = optionValue(args, "--state-dir") ?? indexed?.stateDir ?? null;
@@ -274,7 +279,21 @@ export function parseNodeWebProbeObserveOptions(
const commandNodeId = optionValue(args, "--node-id") ?? null;
const commandWorkspaceRoot = optionValue(args, "--workspace-root") ?? optionValue(args, "--workspace-root-ref") ?? null;
const commandRoot = optionValue(args, "--root") ?? null;
const commandAfterRoundRaw = optionValue(args, "--after-round") ?? null;
const commandAfterRound = commandAfterRoundRaw === null ? null : Number(commandAfterRoundRaw);
if (commandAfterRound !== null && (!Number.isInteger(commandAfterRound) || commandAfterRound < 0 || commandAfterRound > 1000)) {
throw new Error("unsafe web-probe observe --after-round: expected integer 0-1000");
}
const commandSeverity = optionValue(args, "--severity") ?? null;
const commandAlternateSessionStrategy = optionValue(args, "--alternate-session-strategy") ?? null;
const commandExpectedSentinelRange = optionValue(args, "--expected-sentinel-range") ?? null;
const commandFindingId = optionValue(args, "--finding-id") ?? null;
const commandBlocking = args.includes("--blocking") ? true : args.includes("--non-blocking") ? false : null;
for (const [label, value] of [
["--severity", commandSeverity],
["--alternate-session-strategy", commandAlternateSessionStrategy],
["--expected-sentinel-range", commandExpectedSentinelRange],
["--finding-id", commandFindingId],
["--source-id", commandSourceId],
["--file-ref", commandFileRef],
["--task-ref", commandTaskRef],
@@ -331,6 +350,13 @@ export function parseNodeWebProbeObserveOptions(
commandLabel: optionValue(args, "--label") ?? null,
commandSessionId: optionValue(args, "--session-id") ?? null,
commandProvider: optionValue(args, "--provider") ?? null,
commandAfterRound,
commandSeverity,
commandAlternateSessionStrategy,
commandExpectedSentinelRange,
commandRequireComposerReady: args.includes("--require-composer-ready"),
commandFindingId,
commandBlocking,
commandSourceId,
commandFileRef,
commandTaskRef,
@@ -356,6 +382,9 @@ export function parseNodeWebProbeObserveCommandType(value: string): NodeWebProbe
|| value === "cancel"
|| value === "selectProvider"
|| value === "clickSession"
|| value === "refreshCurrentSession"
|| value === "switchAwayAndBack"
|| value === "assertSessionInvariant"
|| value === "gotoProjectMdtodo"
|| value === "selectProjectSource"
|| value === "selectMdtodoSource"
@@ -379,7 +408,7 @@ export function parseNodeWebProbeObserveCommandType(value: string): NodeWebProbe
|| value === "mark"
|| value === "stop"
) return value;
throw new Error(`web-probe observe command --type must be login, preflight, goto, gotoProjectMdtodo, newSession, sendPrompt, steer, cancel, selectProvider, clickSession, selectProjectSource, selectMdtodoSource, selectMdtodoFile, selectMdtodoTask, expandMdtodoTask, openMdtodoSourceConfig, configureMdtodoHwpodSource, probeMdtodoSource, reindexMdtodoSource, editMdtodoTaskTitle, editMdtodoTaskBody, toggleMdtodoTaskStatus, addMdtodoRootTask, addMdtodoSubTask, continueMdtodoTask, deleteMdtodoTask, launchWorkbenchFromTask, launchWorkbenchFromMdtodo, screenshot, mark, or stop; got ${value}`);
throw new Error(`web-probe observe command --type must be login, preflight, goto, gotoProjectMdtodo, newSession, sendPrompt, steer, cancel, selectProvider, clickSession, refreshCurrentSession, switchAwayAndBack, assertSessionInvariant, selectProjectSource, selectMdtodoSource, selectMdtodoFile, selectMdtodoTask, expandMdtodoTask, openMdtodoSourceConfig, configureMdtodoHwpodSource, probeMdtodoSource, reindexMdtodoSource, editMdtodoTaskTitle, editMdtodoTaskBody, toggleMdtodoTaskStatus, addMdtodoRootTask, addMdtodoSubTask, continueMdtodoTask, deleteMdtodoTask, launchWorkbenchFromTask, launchWorkbenchFromMdtodo, screenshot, mark, or stop; got ${value}`);
}
export function parseWebProbeBrowserProxyMode(value: string | undefined): WebProbeBrowserProxyMode {
@@ -1331,6 +1360,13 @@ export function runNodeWebProbeObserveCommand(options: NodeWebProbeObserveOption
label: options.commandLabel,
sessionId: options.commandSessionId,
provider: options.commandProvider,
afterRound: options.commandAfterRound,
severity: options.commandSeverity,
alternateSessionStrategy: options.commandAlternateSessionStrategy,
expectedSentinelRange: options.commandExpectedSentinelRange,
requireComposerReady: options.commandRequireComposerReady,
findingId: options.commandFindingId,
blocking: options.commandBlocking,
sourceId: options.commandSourceId,
fileRef: options.commandFileRef,
taskRef: options.commandTaskRef,