diff --git a/docs/help/testing.md b/docs/help/testing.md index c7b012fa725..800255e9489 100644 --- a/docs/help/testing.md +++ b/docs/help/testing.md @@ -172,6 +172,10 @@ runs the same lanes before release approval. - Use `--platform macos`, `--platform windows`, or `--platform linux` while iterating on one guest. Use `--json` for the summary artifact path and per-lane status. + - The OpenAI lane uses `openai/gpt-5.5` for the live agent-turn proof by + default. Pass `--model ` or set + `OPENCLAW_PARALLELS_OPENAI_MODEL` when deliberately validating another + OpenAI model. - Wrap long local runs in a host timeout so Parallels transport stalls cannot consume the rest of the testing window: diff --git a/scripts/e2e/parallels-linux-smoke.sh b/scripts/e2e/parallels-linux-smoke.sh index 2d6837c26c4..4b0712a24be 100644 --- a/scripts/e2e/parallels-linux-smoke.sh +++ b/scripts/e2e/parallels-linux-smoke.sh @@ -13,6 +13,7 @@ API_KEY_ENV="" AUTH_CHOICE="" AUTH_KEY_FLAG="" MODEL_ID="" +MODEL_ID_EXPLICIT=0 INSTALL_URL="https://openclaw.ai/install.sh" HOST_PORT="18427" HOST_PORT_EXPLICIT=0 @@ -103,6 +104,8 @@ Options: --mode --provider Provider auth/model lane. Default: openai + --model Override the model used for the agent-turn smoke. + Default: openai/gpt-5.5 for the OpenAI lane --api-key-env Host env var name for provider API key. Default: OPENAI_API_KEY for openai, ANTHROPIC_API_KEY for anthropic --openai-api-key-env Alias for --api-key-env (backward compatible) @@ -142,6 +145,11 @@ while [[ $# -gt 0 ]]; do PROVIDER="$2" shift 2 ;; + --model) + MODEL_ID="$2" + MODEL_ID_EXPLICIT=1 + shift 2 + ;; --api-key-env|--openai-api-key-env) API_KEY_ENV="$2" shift 2 @@ -200,19 +208,19 @@ case "$PROVIDER" in openai) AUTH_CHOICE="openai-api-key" AUTH_KEY_FLAG="openai-api-key" - MODEL_ID="openai/gpt-5.5" + [[ "$MODEL_ID_EXPLICIT" -eq 1 ]] || MODEL_ID="${OPENCLAW_PARALLELS_OPENAI_MODEL:-openai/gpt-5.5}" [[ -n "$API_KEY_ENV" ]] || API_KEY_ENV="OPENAI_API_KEY" ;; anthropic) AUTH_CHOICE="apiKey" AUTH_KEY_FLAG="anthropic-api-key" - MODEL_ID="anthropic/claude-sonnet-4-6" + [[ "$MODEL_ID_EXPLICIT" -eq 1 ]] || MODEL_ID="${OPENCLAW_PARALLELS_ANTHROPIC_MODEL:-anthropic/claude-sonnet-4-6}" [[ -n "$API_KEY_ENV" ]] || API_KEY_ENV="ANTHROPIC_API_KEY" ;; minimax) AUTH_CHOICE="minimax-global-api" AUTH_KEY_FLAG="minimax-api-key" - MODEL_ID="minimax/MiniMax-M2.7" + [[ "$MODEL_ID_EXPLICIT" -eq 1 ]] || MODEL_ID="${OPENCLAW_PARALLELS_MINIMAX_MODEL:-minimax/MiniMax-M2.7}" [[ -n "$API_KEY_ENV" ]] || API_KEY_ENV="MINIMAX_API_KEY" ;; *) @@ -764,13 +772,38 @@ verify_gateway_status() { return 1 } +prepare_agent_workspace() { + guest_exec /bin/sh -lc 'set -eu +workspace="${OPENCLAW_WORKSPACE_DIR:-$HOME/.openclaw/workspace}" +mkdir -p "$workspace/.openclaw" +cat > "$workspace/IDENTITY.md" <<'"'"'IDENTITY_EOF'"'"' +# Identity + +- Name: OpenClaw +- Purpose: Parallels Linux smoke test assistant. +IDENTITY_EOF +cat > "$workspace/.openclaw/workspace-state.json" <<'"'"'STATE_EOF'"'"' +{ + "version": 1, + "setupCompletedAt": "2026-01-01T00:00:00.000Z" +} +STATE_EOF +rm -f "$workspace/BOOTSTRAP.md"' +} + verify_local_turn() { guest_exec openclaw models set "$MODEL_ID" - guest_exec /usr/bin/env "$API_KEY_ENV=$API_KEY_VALUE" openclaw agent \ - --local \ - --agent main \ - --message ping \ - --json + guest_exec openclaw config set agents.defaults.skipBootstrap true --strict-json + prepare_agent_workspace + guest_exec /bin/sh -lc "$(cat < Provider auth/model lane. Default: openai + --model Override the model used for the agent-turn smoke. + Default: openai/gpt-5.5 for the OpenAI lane --api-key-env Host env var name for provider API key. Default: OPENAI_API_KEY for openai, ANTHROPIC_API_KEY for anthropic --openai-api-key-env Alias for --api-key-env (backward compatible) @@ -184,6 +187,11 @@ while [[ $# -gt 0 ]]; do PROVIDER="$2" shift 2 ;; + --model) + MODEL_ID="$2" + MODEL_ID_EXPLICIT=1 + shift 2 + ;; --api-key-env|--openai-api-key-env) API_KEY_ENV="$2" shift 2 @@ -258,19 +266,19 @@ case "$PROVIDER" in openai) AUTH_CHOICE="openai-api-key" AUTH_KEY_FLAG="openai-api-key" - MODEL_ID="openai/gpt-5.5" + [[ "$MODEL_ID_EXPLICIT" -eq 1 ]] || MODEL_ID="${OPENCLAW_PARALLELS_OPENAI_MODEL:-openai/gpt-5.5}" [[ -n "$API_KEY_ENV" ]] || API_KEY_ENV="OPENAI_API_KEY" ;; anthropic) AUTH_CHOICE="apiKey" AUTH_KEY_FLAG="anthropic-api-key" - MODEL_ID="anthropic/claude-sonnet-4-6" + [[ "$MODEL_ID_EXPLICIT" -eq 1 ]] || MODEL_ID="${OPENCLAW_PARALLELS_ANTHROPIC_MODEL:-anthropic/claude-sonnet-4-6}" [[ -n "$API_KEY_ENV" ]] || API_KEY_ENV="ANTHROPIC_API_KEY" ;; minimax) AUTH_CHOICE="minimax-global-api" AUTH_KEY_FLAG="minimax-api-key" - MODEL_ID="minimax/MiniMax-M2.7" + [[ "$MODEL_ID_EXPLICIT" -eq 1 ]] || MODEL_ID="${OPENCLAW_PARALLELS_MINIMAX_MODEL:-minimax/MiniMax-M2.7}" [[ -n "$API_KEY_ENV" ]] || API_KEY_ENV="MINIMAX_API_KEY" ;; *) @@ -1474,11 +1482,28 @@ show_gateway_status_compat() { verify_turn() { guest_current_user_exec "$GUEST_NODE_BIN" "$GUEST_OPENCLAW_ENTRY" models set "$MODEL_ID" + guest_current_user_exec "$GUEST_NODE_BIN" "$GUEST_OPENCLAW_ENTRY" config set agents.defaults.skipBootstrap true --strict-json guest_current_user_sh "$(cat < "\$workspace/IDENTITY.md" <<'IDENTITY_EOF' +# Identity + +- Name: OpenClaw +- Purpose: Parallels macOS smoke test assistant. +IDENTITY_EOF +cat > "\$workspace/.openclaw/workspace-state.json" <<'STATE_EOF' +{ + "version": 1, + "setupCompletedAt": "2026-01-01T00:00:00.000Z" +} +STATE_EOF +rm -f "\$workspace/BOOTSTRAP.md" exec /usr/bin/env $(shell_quote "$API_KEY_ENV=$API_KEY_VALUE") \ $(shell_quote "$GUEST_NODE_BIN") $(shell_quote "$GUEST_OPENCLAW_ENTRY") agent \ --agent main \ + --session-id parallels-macos-smoke \ --message $(shell_quote "Reply with exact ASCII text OK only.") \ --json EOF diff --git a/scripts/e2e/parallels-npm-update-smoke.sh b/scripts/e2e/parallels-npm-update-smoke.sh index 64566b87d4f..7dfb84e3335 100755 --- a/scripts/e2e/parallels-npm-update-smoke.sh +++ b/scripts/e2e/parallels-npm-update-smoke.sh @@ -13,6 +13,7 @@ API_KEY_ENV="" AUTH_CHOICE="" AUTH_KEY_FLAG="" MODEL_ID="" +MODEL_ID_EXPLICIT=0 PYTHON_BIN="${PYTHON_BIN:-}" PACKAGE_SPEC="" UPDATE_TARGET="" @@ -120,6 +121,8 @@ Options: Default: all --provider Provider auth/model lane. Default: openai + --model Override the model used for agent-turn smoke checks. + Default: openai/gpt-5.5 for the OpenAI lane --api-key-env Host env var name for provider API key. Default: OPENAI_API_KEY for openai, ANTHROPIC_API_KEY for anthropic --openai-api-key-env Alias for --api-key-env (backward compatible) @@ -149,6 +152,11 @@ while [[ $# -gt 0 ]]; do PROVIDER="$2" shift 2 ;; + --model) + MODEL_ID="$2" + MODEL_ID_EXPLICIT=1 + shift 2 + ;; --api-key-env|--openai-api-key-env) API_KEY_ENV="$2" shift 2 @@ -206,19 +214,19 @@ case "$PROVIDER" in openai) AUTH_CHOICE="openai-api-key" AUTH_KEY_FLAG="openai-api-key" - MODEL_ID="openai/gpt-5.5" + [[ "$MODEL_ID_EXPLICIT" -eq 1 ]] || MODEL_ID="${OPENCLAW_PARALLELS_OPENAI_MODEL:-openai/gpt-5.5}" [[ -n "$API_KEY_ENV" ]] || API_KEY_ENV="OPENAI_API_KEY" ;; anthropic) AUTH_CHOICE="apiKey" AUTH_KEY_FLAG="anthropic-api-key" - MODEL_ID="anthropic/claude-sonnet-4-6" + [[ "$MODEL_ID_EXPLICIT" -eq 1 ]] || MODEL_ID="${OPENCLAW_PARALLELS_ANTHROPIC_MODEL:-anthropic/claude-sonnet-4-6}" [[ -n "$API_KEY_ENV" ]] || API_KEY_ENV="ANTHROPIC_API_KEY" ;; minimax) AUTH_CHOICE="minimax-global-api" AUTH_KEY_FLAG="minimax-api-key" - MODEL_ID="minimax/MiniMax-M2.7" + [[ "$MODEL_ID_EXPLICIT" -eq 1 ]] || MODEL_ID="${OPENCLAW_PARALLELS_MINIMAX_MODEL:-minimax/MiniMax-M2.7}" [[ -n "$API_KEY_ENV" ]] || API_KEY_ENV="MINIMAX_API_KEY" ;; *) @@ -1104,7 +1112,8 @@ cat > "\$workspace/.openclaw/workspace-state.json" <<'STATE_EOF' } STATE_EOF rm -f "\$workspace/BOOTSTRAP.md" -/opt/homebrew/bin/openclaw models set "$MODEL_ID" + /opt/homebrew/bin/openclaw models set "$MODEL_ID" + /opt/homebrew/bin/openclaw config set agents.defaults.skipBootstrap true --strict-json /opt/homebrew/bin/openclaw agent --agent main --session-id "parallels-npm-update-macos-transport-recovery-$expected_needle" --message "Reply with exact ASCII text OK only." --json EOF macos_desktop_user_exec /bin/bash "$script_path" @@ -1235,7 +1244,8 @@ if (-not \$gatewayReady) { \$providerBytes = [Convert]::FromBase64String('$provider_key_b64') \$providerValue = [Text.Encoding]::UTF8.GetString(\$providerBytes) Set-Item -Path ('Env:' + '$API_KEY_ENV') -Value \$providerValue -& \$openclaw models set '$MODEL_ID' + & \$openclaw models set '$MODEL_ID' + & \$openclaw config set agents.defaults.skipBootstrap true --strict-json \$workspace = \$env:OPENCLAW_WORKSPACE_DIR if (-not \$workspace) { \$workspace = Join-Path \$env:USERPROFILE '.openclaw\\workspace' @@ -1692,7 +1702,8 @@ if [ -n "$expected_needle" ]; then esac fi /opt/homebrew/bin/openclaw update status --json -/opt/homebrew/bin/openclaw models set "$MODEL_ID" + /opt/homebrew/bin/openclaw models set "$MODEL_ID" + /opt/homebrew/bin/openclaw config set agents.defaults.skipBootstrap true --strict-json # Same-guest npm upgrades can leave launchd holding the old gateway process or # module graph briefly; wait for a fresh RPC-ready restart before the agent turn. # Fresh npm installs may not have a launchd service yet, so fall back to the @@ -1826,6 +1837,7 @@ if [ -n "$expected_needle" ]; then fi openclaw update status --json openclaw models set "$MODEL_ID" +openclaw config set agents.defaults.skipBootstrap true --strict-json workspace="\${OPENCLAW_WORKSPACE_DIR:-\$HOME/.openclaw/workspace}" mkdir -p "\$workspace/.openclaw" cat > "\$workspace/IDENTITY.md" <<'IDENTITY_EOF' @@ -1911,6 +1923,7 @@ if platform_enabled macos; then bash "$ROOT_DIR/scripts/e2e/parallels-macos-smoke.sh" \ --mode fresh \ --provider "$PROVIDER" \ + --model "$MODEL_ID" \ --api-key-env "$API_KEY_ENV" \ --target-package-spec "$PACKAGE_SPEC" \ --json >"$RUN_DIR/macos-fresh.log" 2>&1 & @@ -1922,6 +1935,7 @@ if platform_enabled windows; then bash "$ROOT_DIR/scripts/e2e/parallels-windows-smoke.sh" \ --mode fresh \ --provider "$PROVIDER" \ + --model "$MODEL_ID" \ --api-key-env "$API_KEY_ENV" \ --target-package-spec "$PACKAGE_SPEC" \ --json >"$RUN_DIR/windows-fresh.log" 2>&1 & @@ -1933,6 +1947,7 @@ if platform_enabled linux; then bash "$ROOT_DIR/scripts/e2e/parallels-linux-smoke.sh" \ --mode fresh \ --provider "$PROVIDER" \ + --model "$MODEL_ID" \ --api-key-env "$API_KEY_ENV" \ --target-package-spec "$PACKAGE_SPEC" \ --json >"$RUN_DIR/linux-fresh.log" 2>&1 & diff --git a/scripts/e2e/parallels-windows-smoke.sh b/scripts/e2e/parallels-windows-smoke.sh index a7799cd3e86..63088e20709 100644 --- a/scripts/e2e/parallels-windows-smoke.sh +++ b/scripts/e2e/parallels-windows-smoke.sh @@ -12,6 +12,7 @@ API_KEY_ENV="" AUTH_CHOICE="" AUTH_KEY_FLAG="" MODEL_ID="" +MODEL_ID_EXPLICIT=0 INSTALL_URL="https://openclaw.ai/install.ps1" HOST_PORT="18426" HOST_PORT_EXPLICIT=0 @@ -138,6 +139,8 @@ Options: --mode --provider Provider auth/model lane. Default: openai + --model Override the model used for the agent-turn smoke. + Default: openai/gpt-5.5 for the OpenAI lane --api-key-env Host env var name for provider API key. Default: OPENAI_API_KEY for openai, ANTHROPIC_API_KEY for anthropic --openai-api-key-env Alias for --api-key-env (backward compatible) @@ -183,6 +186,11 @@ while [[ $# -gt 0 ]]; do PROVIDER="$2" shift 2 ;; + --model) + MODEL_ID="$2" + MODEL_ID_EXPLICIT=1 + shift 2 + ;; --api-key-env|--openai-api-key-env) API_KEY_ENV="$2" shift 2 @@ -249,19 +257,19 @@ case "$PROVIDER" in openai) AUTH_CHOICE="openai-api-key" AUTH_KEY_FLAG="openai-api-key" - MODEL_ID="openai/gpt-5.5" + [[ "$MODEL_ID_EXPLICIT" -eq 1 ]] || MODEL_ID="${OPENCLAW_PARALLELS_OPENAI_MODEL:-openai/gpt-5.5}" [[ -n "$API_KEY_ENV" ]] || API_KEY_ENV="OPENAI_API_KEY" ;; anthropic) AUTH_CHOICE="apiKey" AUTH_KEY_FLAG="anthropic-api-key" - MODEL_ID="anthropic/claude-sonnet-4-6" + [[ "$MODEL_ID_EXPLICIT" -eq 1 ]] || MODEL_ID="${OPENCLAW_PARALLELS_ANTHROPIC_MODEL:-anthropic/claude-sonnet-4-6}" [[ -n "$API_KEY_ENV" ]] || API_KEY_ENV="ANTHROPIC_API_KEY" ;; minimax) AUTH_CHOICE="minimax-global-api" AUTH_KEY_FLAG="minimax-api-key" - MODEL_ID="minimax/MiniMax-M2.7" + [[ "$MODEL_ID_EXPLICIT" -eq 1 ]] || MODEL_ID="${OPENCLAW_PARALLELS_MINIMAX_MODEL:-minimax/MiniMax-M2.7}" [[ -n "$API_KEY_ENV" ]] || API_KEY_ENV="MINIMAX_API_KEY" ;; *) @@ -2367,8 +2375,31 @@ show_gateway_status_compat() { verify_turn() { guest_run_openclaw "" "" models set "$MODEL_ID" + guest_run_openclaw "" "" config set agents.defaults.skipBootstrap true --strict-json + guest_powershell "$(cat <<'EOF' +$workspace = $env:OPENCLAW_WORKSPACE_DIR +if (-not $workspace) { + $workspace = Join-Path $env:USERPROFILE '.openclaw\workspace' +} +$stateDir = Join-Path $workspace '.openclaw' +New-Item -ItemType Directory -Path $stateDir -Force | Out-Null +@' +# Identity + +- Name: OpenClaw +- Purpose: Parallels Windows smoke test assistant. +'@ | Set-Content -Path (Join-Path $workspace 'IDENTITY.md') -Encoding UTF8 +@' +{ + "version": 1, + "setupCompletedAt": "2026-01-01T00:00:00.000Z" +} +'@ | Set-Content -Path (Join-Path $stateDir 'workspace-state.json') -Encoding UTF8 +Remove-Item (Join-Path $workspace 'BOOTSTRAP.md') -Force -ErrorAction SilentlyContinue +EOF +)" guest_run_openclaw "$API_KEY_ENV" "$API_KEY_VALUE" \ - agent --agent main --message "Reply with exact ASCII text OK only." --json + agent --agent main --session-id parallels-windows-smoke --message "Reply with exact ASCII text OK only." --json } capture_latest_ref_failure() { diff --git a/test/scripts/parallels-smoke-model.test.ts b/test/scripts/parallels-smoke-model.test.ts new file mode 100644 index 00000000000..ca176796ad2 --- /dev/null +++ b/test/scripts/parallels-smoke-model.test.ts @@ -0,0 +1,43 @@ +import { readFileSync } from "node:fs"; +import { describe, expect, it } from "vitest"; + +const OS_SCRIPT_PATHS = [ + "scripts/e2e/parallels-linux-smoke.sh", + "scripts/e2e/parallels-macos-smoke.sh", + "scripts/e2e/parallels-windows-smoke.sh", +]; +const NPM_UPDATE_SCRIPT_PATH = "scripts/e2e/parallels-npm-update-smoke.sh"; + +describe("Parallels smoke model selection", () => { + it("keeps the OpenAI smoke lane on the stable direct API model by default", () => { + for (const scriptPath of [...OS_SCRIPT_PATHS, NPM_UPDATE_SCRIPT_PATH]) { + const script = readFileSync(scriptPath, "utf8"); + + expect(script, scriptPath).toContain( + 'MODEL_ID="${OPENCLAW_PARALLELS_OPENAI_MODEL:-openai/gpt-5.5}"', + ); + expect(script, scriptPath).toContain("--model "); + expect(script, scriptPath).toContain("MODEL_ID_EXPLICIT=1"); + } + }); + + it("seeds agent workspace state before OS smoke agent turns", () => { + for (const scriptPath of OS_SCRIPT_PATHS) { + const script = readFileSync(scriptPath, "utf8"); + + expect(script, scriptPath).toContain("workspace-state.json"); + expect(script, scriptPath).toContain("IDENTITY.md"); + expect(script, scriptPath).toContain("BOOTSTRAP.md"); + expect(script, scriptPath).toContain("--session-id parallels-"); + expect(script, scriptPath).toContain("agents.defaults.skipBootstrap true --strict-json"); + } + }); + + it("passes aggregate model overrides into each OS fresh lane", () => { + const script = readFileSync(NPM_UPDATE_SCRIPT_PATH, "utf8"); + + expect(script).toMatch(/parallels-macos-smoke\.sh"[\s\S]*?--model "\$MODEL_ID"/); + expect(script).toMatch(/parallels-windows-smoke\.sh"[\s\S]*?--model "\$MODEL_ID"/); + expect(script).toMatch(/parallels-linux-smoke\.sh"[\s\S]*?--model "\$MODEL_ID"/); + }); +});