test(parallels): harden smoke agent model setup

This commit is contained in:
Peter Steinberger
2026-04-26 11:38:20 +01:00
parent 8344fae387
commit cd8187d7ce
6 changed files with 173 additions and 22 deletions

View File

@@ -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 <provider/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:

View File

@@ -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 <fresh|upgrade|both>
--provider <openai|anthropic|minimax>
Provider auth/model lane. Default: openai
--model <provider/model> Override the model used for the agent-turn smoke.
Default: openai/gpt-5.5 for the OpenAI lane
--api-key-env <var> Host env var name for provider API key.
Default: OPENAI_API_KEY for openai, ANTHROPIC_API_KEY for anthropic
--openai-api-key-env <var> 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 <<EOF
exec /usr/bin/env $(shell_quote "$API_KEY_ENV=$API_KEY_VALUE") openclaw agent \
--local \
--agent main \
--session-id parallels-linux-smoke \
--message $(shell_quote "Reply with exact ASCII text OK only.") \
--json
EOF
)"
}
phase_log_path() {

View File

@@ -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="18425"
HOST_PORT_EXPLICIT=0
@@ -52,7 +53,7 @@ TIMEOUT_UPDATE_DEV_S="${OPENCLAW_PARALLELS_MACOS_UPDATE_DEV_TIMEOUT_S:-1200}"
TIMEOUT_VERIFY_S=60
TIMEOUT_ONBOARD_S=180
TIMEOUT_GATEWAY_S=180
TIMEOUT_AGENT_S=240
TIMEOUT_AGENT_S="${OPENCLAW_PARALLELS_MACOS_AGENT_TIMEOUT_S:-240}"
TIMEOUT_PERMISSION_S=60
TIMEOUT_DASHBOARD_S=180
TIMEOUT_SNAPSHOT_S=360
@@ -142,6 +143,8 @@ Options:
both = run both lanes
--provider <openai|anthropic|minimax>
Provider auth/model lane. Default: openai
--model <provider/model> Override the model used for the agent-turn smoke.
Default: openai/gpt-5.5 for the OpenAI lane
--api-key-env <var> Host env var name for provider API key.
Default: OPENAI_API_KEY for openai, ANTHROPIC_API_KEY for anthropic
--openai-api-key-env <var> 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 <<EOF
export PATH=$(shell_quote "$GUEST_EXEC_PATH")
workspace="\${OPENCLAW_WORKSPACE_DIR:-\$HOME/.openclaw/workspace}"
mkdir -p "\$workspace/.openclaw"
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

View File

@@ -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 <openai|anthropic|minimax>
Provider auth/model lane. Default: openai
--model <provider/model> Override the model used for agent-turn smoke checks.
Default: openai/gpt-5.5 for the OpenAI lane
--api-key-env <var> Host env var name for provider API key.
Default: OPENAI_API_KEY for openai, ANTHROPIC_API_KEY for anthropic
--openai-api-key-env <var> 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 &

View File

@@ -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 <fresh|upgrade|both>
--provider <openai|anthropic|minimax>
Provider auth/model lane. Default: openai
--model <provider/model> Override the model used for the agent-turn smoke.
Default: openai/gpt-5.5 for the OpenAI lane
--api-key-env <var> Host env var name for provider API key.
Default: OPENAI_API_KEY for openai, ANTHROPIC_API_KEY for anthropic
--openai-api-key-env <var> 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() {

View File

@@ -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 <provider/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"/);
});
});