diff --git a/.antigravity-plugin/hooks.json b/.antigravity-plugin/hooks.json index 985cd89..d56d2ae 100644 --- a/.antigravity-plugin/hooks.json +++ b/.antigravity-plugin/hooks.json @@ -5,12 +5,12 @@ { "type": "command", "command": "scripts\\run-powershell.cmd hooks\\ensure-monk-agent.ps1", - "timeout": 10 + "timeout": 20 }, { "type": "command", "command": "./hooks/ensure-monk-agent.sh", - "timeout": 10 + "timeout": 20 } ] }, diff --git a/.antigravity-plugin/hooks/ensure-monk-agent.ps1 b/.antigravity-plugin/hooks/ensure-monk-agent.ps1 index ddc3c68..a5c7551 100644 --- a/.antigravity-plugin/hooks/ensure-monk-agent.ps1 +++ b/.antigravity-plugin/hooks/ensure-monk-agent.ps1 @@ -132,7 +132,8 @@ if ($Process) { # the health endpoint never responds, report an attempted start with a pointer # to the logs instead of a false "has been started". if ($Process -and -not $Process.HasExited) { - for ($i = 0; $i -lt 10; $i++) { + $ReadyDeadline = [DateTime]::UtcNow.AddSeconds(10) + while ([DateTime]::UtcNow -lt $ReadyDeadline) { Start-Sleep -Seconds 1 if ($Process.HasExited) { break } if (Test-AgentRunning) { diff --git a/.antigravity-plugin/hooks/ensure-monk-agent.sh b/.antigravity-plugin/hooks/ensure-monk-agent.sh index 24463a7..87300ce 100755 --- a/.antigravity-plugin/hooks/ensure-monk-agent.sh +++ b/.antigravity-plugin/hooks/ensure-monk-agent.sh @@ -103,8 +103,8 @@ printf '%s\n' "$agent_pid" >"$pid_file" # Wait briefly for the agent to become reachable. If the process exits early or # the health endpoint never responds, report an attempted start with a pointer # to the logs instead of a false "has been started". -i=0 -while [ "$i" -lt 10 ]; do +ready_deadline=$(( $(date +%s) + 10 )) +while [ "$(date +%s)" -lt "$ready_deadline" ]; do sleep 1 if ! kill -0 "$agent_pid" 2>/dev/null; then break @@ -113,7 +113,6 @@ while [ "$i" -lt 10 ]; do emit_inject_steps "monk-agent was not running and has been started. It may take a few seconds to initialize — use monk.install.status or monk.runtime.status to check readiness before issuing Monk operations." exit 0 fi - i=$((i + 1)) done emit_inject_steps "monk-agent was started but did not become ready within 10 seconds. Check monk.install.status or monk.runtime.status for details, or the launcher logs under $log_dir." diff --git a/tests/antigravity-ensure-hook-timeout-budget.ps1 b/tests/antigravity-ensure-hook-timeout-budget.ps1 new file mode 100644 index 0000000..d1c44a7 --- /dev/null +++ b/tests/antigravity-ensure-hook-timeout-budget.ps1 @@ -0,0 +1,108 @@ +$ErrorActionPreference = "Stop" + +$Repo = Split-Path -Parent (Split-Path -Parent $MyInvocation.MyCommand.Path) +$Root = Join-Path ([IO.Path]::GetTempPath()) ("monk-antigravity-timeout-" + [guid]::NewGuid().ToString("N")) +$AgentPath = Join-Path $Root "fake-agent.exe" +$AgentSource = Join-Path $Root "fake-agent.cs" +$EmptyInput = Join-Path $Root "stdin.txt" +$StdoutPath = Join-Path $Root "hook.stdout.json" +$StderrPath = Join-Path $Root "hook.stderr.log" +$MonkHome = Join-Path $Root "monk-home" +$PidFile = Join-Path $MonkHome "agent\launcher\run\monk-agent.pid" +$EnvironmentNames = @( + "MONK_AGENT_PATH", + "MONK_AGENT_HOME", + "MONK_AGENT_PORT", + "MONK_DISABLE_ANALYTICS" +) +$OriginalEnvironment = @{} + +foreach ($Name in $EnvironmentNames) { + $OriginalEnvironment[$Name] = [Environment]::GetEnvironmentVariable($Name, "Process") +} + +try { + New-Item -ItemType Directory -Force -Path $Root | Out-Null + New-Item -ItemType File -Force -Path $EmptyInput | Out-Null + + @" +using System; +using System.Threading; +class Program { static void Main(string[] args) { Thread.Sleep(TimeSpan.FromSeconds(60)); } } +"@ | Set-Content $AgentSource + + $Csc = Join-Path $env:WINDIR "Microsoft.NET\Framework64\v4.0.30319\csc.exe" + if (-not (Test-Path $Csc)) { + $Csc = Get-ChildItem "$env:WINDIR\Microsoft.NET\Framework64" -Filter csc.exe -Recurse | + Select-Object -First 1 -ExpandProperty FullName + } + if (-not $Csc) { + throw "csc.exe not found; cannot build the fake never-ready agent" + } + + & $Csc /nologo /out:$AgentPath $AgentSource | Out-Null + if (-not (Test-Path $AgentPath)) { + throw "failed to compile fake-agent.exe" + } + + $Hooks = Get-Content -Raw (Join-Path $Repo ".antigravity-plugin\hooks.json") | ConvertFrom-Json + $HostTimeoutSeconds = [int]$Hooks.'ensure-monk-agent'.PreInvocation[0].timeout + $HookPath = Join-Path $Repo ".antigravity-plugin\hooks\ensure-monk-agent.ps1" + + $env:MONK_AGENT_PATH = $AgentPath + $env:MONK_AGENT_HOME = $MonkHome + $env:MONK_AGENT_PORT = "57420" + $env:MONK_DISABLE_ANALYTICS = "1" + + # Force the native PowerShell hook and make each readiness probe consume its + # full two-second budget without touching the network. + $ChildCommand = @" +`$ProgressPreference = 'SilentlyContinue' +`$env:Path = `$PSHOME +function global:Invoke-WebRequest { + Start-Sleep -Seconds 2 + throw 'fixture: companion unavailable' +} +& '$HookPath' +"@ + $EncodedCommand = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($ChildCommand)) + + $HookProcess = Start-Process ` + -FilePath (Get-Process -Id $PID).Path ` + -ArgumentList @("-NoLogo", "-NoProfile", "-NonInteractive", "-EncodedCommand", $EncodedCommand) ` + -WindowStyle Hidden ` + -RedirectStandardInput $EmptyInput ` + -RedirectStandardOutput $StdoutPath ` + -RedirectStandardError $StderrPath ` + -PassThru + + $Finished = $HookProcess.WaitForExit($HostTimeoutSeconds * 1000) + if (-not $Finished) { + Stop-Process -Id $HookProcess.Id -Force -ErrorAction SilentlyContinue + throw "Antigravity's declared $HostTimeoutSeconds-second timeout kills the native hook before it emits readiness JSON" + } + $HookProcess.WaitForExit() + $HookProcess.Refresh() + + $Stdout = Get-Content -Raw $StdoutPath -ErrorAction SilentlyContinue + $Stderr = Get-Content -Raw $StderrPath -ErrorAction SilentlyContinue + if ($HookProcess.ExitCode -is [int] -and $HookProcess.ExitCode -ne 0) { + throw "native ensure hook exited $($HookProcess.ExitCode): $Stderr" + } + if ($Stdout -notmatch "did not become ready") { + throw "native ensure hook completed without the expected readiness diagnostic: $Stdout" + } + + Write-Host "PASS: native Antigravity cold-start hook completes within its declared timeout" +} finally { + if (Test-Path $PidFile) { + $AgentPid = Get-Content $PidFile -ErrorAction SilentlyContinue | Select-Object -First 1 + if ($AgentPid) { + Stop-Process -Id $AgentPid -Force -ErrorAction SilentlyContinue + } + } + foreach ($Name in $EnvironmentNames) { + [Environment]::SetEnvironmentVariable($Name, $OriginalEnvironment[$Name], "Process") + } + Remove-Item -Recurse -Force $Root -ErrorAction SilentlyContinue +} diff --git a/tests/antigravity-ensure-hook-timeout-budget.sh b/tests/antigravity-ensure-hook-timeout-budget.sh new file mode 100755 index 0000000..fd7ce22 --- /dev/null +++ b/tests/antigravity-ensure-hook-timeout-budget.sh @@ -0,0 +1,75 @@ +#!/usr/bin/env bash +set -euo pipefail + +repo_root="$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)" +hook="$repo_root/.antigravity-plugin/hooks/ensure-monk-agent.sh" +hooks_json="$repo_root/.antigravity-plugin/hooks.json" + +tmp="$(mktemp -d)" +agent_pid="" +cleanup() { + if [ -f "$tmp/home/.monk/agent/launcher/run/monk-agent.pid" ]; then + agent_pid="$(cat "$tmp/home/.monk/agent/launcher/run/monk-agent.pid" 2>/dev/null || true)" + fi + if [ -n "$agent_pid" ]; then + kill "$agent_pid" 2>/dev/null || true + fi + rm -rf "$tmp" +} +trap cleanup EXIT + +mkdir -p "$tmp/bin" "$tmp/home" + +# Keep the launched process alive while making every readiness probe use its +# full two-second request budget. This exposes the difference between retry +# count and elapsed wall-clock time without using the network or monk-agent. +cat >"$tmp/monk-agent" <<'EOF' +#!/usr/bin/env sh +exec sleep 60 +EOF +chmod +x "$tmp/monk-agent" + +cat >"$tmp/bin/curl" <<'EOF' +#!/usr/bin/env sh +sleep 2 +exit 1 +EOF +chmod +x "$tmp/bin/curl" + +configured_timeout="$(sed -n 's/.*"timeout"[[:space:]]*:[[:space:]]*\([0-9][0-9]*\).*/\1/p' "$hooks_json" | head -n 1)" +if [ -z "$configured_timeout" ]; then + echo "fixture assumption failed: could not read Antigravity ensure hook timeout" >&2 + exit 2 +fi + +set +e +PATH="$tmp/bin:$PATH" \ +HOME="$tmp/home" \ +MONK_AGENT_PATH="$tmp/monk-agent" \ +MONK_AGENT_HOME="$tmp/home/.monk" \ +timeout "${configured_timeout}s" sh "$hook" "$tmp/stdout" 2>"$tmp/stderr" +status=$? +set -e + +if [ "$status" -eq 124 ]; then + if [ -s "$tmp/stdout" ]; then + echo "unexpected stdout before the host timeout:" >&2 + cat "$tmp/stdout" >&2 + fi + echo "FAIL: Antigravity's declared ${configured_timeout}-second timeout kills the cold-start hook before it emits readiness JSON" >&2 + exit 1 +fi + +if [ "$status" -ne 0 ]; then + echo "FAIL: ensure hook exited with status $status" >&2 + cat "$tmp/stderr" >&2 + exit 1 +fi + +if ! grep -q 'did not become ready' "$tmp/stdout"; then + echo "FAIL: ensure hook completed without the expected readiness diagnostic" >&2 + cat "$tmp/stdout" >&2 + exit 1 +fi + +echo "PASS: Antigravity cold-start hook completes within its declared timeout"