diff --git a/devlog/_plan/260821_260821-windows-picker-full-restart/000_plan.md b/devlog/_plan/260821_260821-windows-picker-full-restart/000_plan.md new file mode 100644 index 00000000000..b8b0900c67f --- /dev/null +++ b/devlog/_plan/260821_260821-windows-picker-full-restart/000_plan.md @@ -0,0 +1,91 @@ +# 000 Plan: Windows model-picker full-restart path + +## Problem + +ocx sync --restart-codex rewrites the Codex catalog JSON and restarts the Codex +app-server (codex.exe app-server). Observed behavior: + +- macOS: the desktop app model picker reflects the new catalog right away. +- Windows (stable/beta, MSIX package OpenAI.Codex_26.818.3698.0): the picker + keeps the stale list until the whole desktop app is quit and relaunched. + +Local evidence (2026-08-21): + +- Desktop UI processes are ChatGPT.exe (Electron shell), installed as MSIX + package family OpenAI.Codex_2p2nqsd0c76g0, start app id (AUMID) + OpenAI.Codex_2p2nqsd0c76g0!App. +- ocx sync --restart-codex matches only codex.exe app-server and + codex-code-mode-host.exe command lines + (src/codex/app-server-processes.ts, isCodexAppServerCommandLine). The + Electron UI is never signalled, so its cached picker survives. +- After the 20:57 sync + restart, codex.exe (PID 8592) started fresh at 20:59 + while all ChatGPT.exe UI processes kept their earlier start time, and the + picker still showed only OpenAI models. + +Research findings (subagent, bundle inspection of app.asar): + +- The renderer fetches model/list and config/read over stdio JSON-RPC into a + TanStack Query cache; there is no filesystem watcher on the catalog file. +- The UI invalidates those queries only on a codex-app-server-initialized + event. On Windows, externally killing the codex.exe child may not produce + that event reliably (hypothesis, untested from inside this session), which + would explain why ocx restart alone does not refresh the picker here while + macOS recovers. +- Official docs say to restart the desktop app after changing model_catalog_json; + no supported refresh hook exists. Known upstream cluster: openai/codex + issues 19694, 26308, 32349, 34487 (desktop picker vs CLI catalog divergence). +- Relaunch must go through MSIX activation (shell:AppsFolder AUMID), not the + exe path under WindowsApps (ACL-restricted, no package identity). + +## Scope + +IN (audit amendments folded in): + +- A supported, documented way to fully restart the Windows Codex desktop app + after a catalog sync: graceful WM_CLOSE first, bounded taskkill /T /F + fallback, relaunch via AUMID. Targets resolve InstallLocation at runtime + via Get-AppxPackage -PackageFamilyName (the family string is NOT a + substring of the install path); only the root ChatGPT.exe whose parent lies + outside the package is selected so taskkill /T cascades to codex.exe and + codex-code-mode-host.exe; the script refuses to kill its own ancestry. +- A GitHub issue on lidge-jun/opencodex recording the platform gap, the beta + caveat, upstream issue links, and the requested UX (sync should offer a full + app restart on Windows). The issue MUST include Version (installed + @bitkyc08/opencodex version) and Operating system fields, which + enforce-issue-quality hard-requires once Client or integration is present; + Reproduction carries the PID/start-time evidence; upstream issues are cited + as related-but-unverified. + +OUT: + +- Changing ocx sync runtime behavior in this unit (the issue proposes it; + implementation is a later unit). +- Killing processes outside the OpenAI.Codex_2p2nqsd0c76g0 package family. +- Testing the unverified stdio-respawn hypothesis by killing codex.exe from + inside this session (would kill our own host); recorded as an open question + for an external terminal test. + +## Work phases + +- wp1 (010): add scripts/restart-codex-desktop-app.ps1 with -DryRun/-Force, + graceful-close then bounded forced fallback, relaunch via AUMID; file the + templated GitHub issue; record evidence. + +## Accept criteria + +- Script -DryRun exits 0 AND lists the specific live root PID(s) it would + stop and the relaunch command, without stopping anything (an exit-0 no-op + does not pass). Focused probe evidence per scripts/AGENTS.md is the real + gate (tsconfig includes only src/); bun x tsc --noEmit still runs as a + no-regression check. +- Issue exists on origin with bug_report template headings. + +## Safety notes + +- Running the restart from inside a Codex conversation kills that conversation + host app; the script warns and docs say to run it from an external terminal. +- Forced kill is limited to processes whose Path is under the runtime-resolved + InstallLocation. Close-to-tray behavior is explicitly checked: if + CloseMainWindow() only hides the window, the wait expires and the forced + path runs; record observed behavior. Record the PowerShell edition the + probe ran under (Get-AppxPackage differs between 5.1 and 7). diff --git a/devlog/_plan/260821_260821-windows-picker-full-restart/010_phase1.md b/devlog/_plan/260821_260821-windows-picker-full-restart/010_phase1.md new file mode 100644 index 00000000000..1c243fd97b1 --- /dev/null +++ b/devlog/_plan/260821_260821-windows-picker-full-restart/010_phase1.md @@ -0,0 +1,63 @@ +# 010 wp1: Restart script + issue (diff level) + +## NEW: scripts/restart-codex-desktop-app.ps1 (amended per audit) + +PowerShell 5.1-compatible script: + +- param([switch]$DryRun, [switch]$Force). +- Constants: package family OpenAI.Codex_2p2nqsd0c76g0, AUMID + OpenAI.Codex_2p2nqsd0c76g0!App, process names ChatGPT, codex, + codex-code-mode-host. +- Resolve $installLoc = (Get-AppxPackage -PackageFamilyName + OpenAI.Codex_2p2nqsd0c76g0).InstallLocation at runtime; fail with an + actionable message when empty. Wrap process Path access in try/catch + (Access denied for other users processes). +- Select ONLY the root ChatGPT.exe whose ParentProcessId lies outside + $installLoc (Win32_Process via Get-CimInstance). taskkill /PID /T /F + cascades to codex.exe and its codex-code-mode-host.exe child. Never list + code-mode-host as an independent target. +- Self-kill guard: walk $PID ancestry; abort with a clear message when any + selected target is in it. +- Warn: active Codex turns are interrupted; run from an external terminal. +- Graceful pass: CloseMainWindow() on the process with a MainWindowHandle, + wait up to 15 s in 1 s polls for all targets to exit. If the process + survives past the timeout, print that close-to-tray behavior is suspected + before escalating. +- Forced pass (remaining targets, or immediately with -Force): + taskkill /PID /T /F per remaining PID (/T covers child tree so + codex.exe is not orphaned). +- Relaunch: Start-Process "shell:AppsFolder\" unless -DryRun. +- -DryRun: print planned actions (targets, method, relaunch command), touch + nothing, exit 0. + +## MODIFY: none (runtime untouched in this unit) + +## Verification + +## Cycle 2 addendum (2026-08-21, provider verification + push) + +- command-code stealth/ox-alpha re-probed after credit purchase: /v1/chat/completions + and /v1/responses both return 200 with valid completions. No code change needed. +- opencode-go upstream (https://opencode.ai/zen/go/v1) returns 500 Internal server + error for every model probed directly (kimi-k2.7-code, ox-alpha-free); the proxy + 502 "upstream stream ended" is an upstream outage, not an adapter defect. + ox-alpha-free is also absent from models.dev opencode-go roster and from + scripts/model-metadata.source.json, so the opencode-go/ox-alpha-free slug was + never a registered catalog model; opencode-free/x-preview-f-free is the working + free-tier route (verified 200 on both endpoints). +- Direct push to origin/dev rejected by ruleset 20763889 (pull_request rule, admin + bypass = pull_requests_only). Fallback per user intent: branch + codex/windows-restart-helper pushed, PR #2293 opened targeting dev (MERGEABLE). + +- powershell -File scripts/restart-codex-desktop-app.ps1 -DryRun -> exit 0 + AND output names the live root PID (e.g. 9928) and its child codex.exe; + nothing stopped. Record $PSVersionTable.PSVersion. +- bun x tsc --noEmit -> exit 0. +- gh issue create with bug_report.yml headings: Client or integration = Codex + App; Area = Platform (Windows / macOS / Linux); Version = installed + @bitkyc08/opencodex version (package.json); Operating system = Windows 11 + (build from systeminfo); Reproduction includes the 20:57 sync / 20:59 fresh + codex.exe vs stale UI start-time evidence; upstream issues + 19694/26308/32349/34487 cited as related-unverified; beta-channel caveat + stated. After creation, re-read state with gh issue view until the + enforce-issue-quality workflow settles (creation alone can auto-close). diff --git a/scripts/restart-codex-desktop-app.ps1 b/scripts/restart-codex-desktop-app.ps1 new file mode 100644 index 00000000000..8675f694182 --- /dev/null +++ b/scripts/restart-codex-desktop-app.ps1 @@ -0,0 +1,102 @@ +#Requires -Version 5.1 +<# +.SYNOPSIS + Fully restarts the Windows Codex desktop app (MSIX package) so the model + picker re-reads the on-disk catalog after ocx sync. +.NOTES + Run this from an external terminal. Running it from inside a Codex + conversation kills the app hosting that conversation. +#> +[CmdletBinding()] +param( + [switch]$DryRun, + [switch]$Force +) + +$ErrorActionPreference = "Stop" + +$PackageFamily = "OpenAI.Codex_2p2nqsd0c76g0" +$Aumid = "OpenAI.Codex_2p2nqsd0c76g0!App" + +Import-Module Appx -ErrorAction SilentlyContinue +$pkg = Get-AppxPackage -Name OpenAI.Codex | Where-Object { $_.PackageFamilyName -eq $PackageFamily } +if (-not $pkg -or -not $pkg.InstallLocation) { + Write-Error "MSIX package $PackageFamily was not found; nothing to restart." + exit 1 +} +$InstallLoc = $pkg.InstallLocation + +$nameFilter = "Name='ChatGPT.exe' OR Name='codex.exe' OR Name='codex-code-mode-host.exe'" +$all = @(Get-CimInstance -ClassName Win32_Process -Filter $nameFilter) +$targets = @($all | Where-Object { + $_.ExecutablePath -and $_.ExecutablePath.StartsWith($InstallLoc, [System.StringComparison]::OrdinalIgnoreCase) +}) + +if ($targets.Count -eq 0) { + Write-Host "Codex desktop app is not running." + exit 0 +} + +$targetIds = @{} +foreach ($t in $targets) { $targetIds[[uint32]$t.ProcessId] = $t } + +# Roots are targets whose parent is outside the package tree; killing each +# root with taskkill /T cascades to codex.exe and its code-mode-host child. +$roots = @($targets | Where-Object { -not $targetIds.ContainsKey([uint32]$_.ParentProcessId) }) + +# Self-kill guard: never target our own ancestry. Skipped under -DryRun so the +# report stays useful when Codex itself launched this script. +$ancestry = @{} +if (-not $DryRun) { + $cursor = $PID + while ($cursor) { + $ancestry[[uint32]$cursor] = $true + $parent = (Get-CimInstance -ClassName Win32_Process -Filter "ProcessId=$cursor").ParentProcessId + if ($parent -and -not $ancestry.ContainsKey([uint32]$parent)) { $cursor = $parent } else { break } + } + foreach ($r in $roots) { + if ($ancestry.ContainsKey([uint32]$r.ProcessId)) { + Write-Error "Refusing to restart: selected root PID $($r.ProcessId) is an ancestor of this script." + exit 1 + } + } +} + +Write-Host ("Targets ({0}):" -f $targets.Count) +foreach ($t in $targets) { + Write-Host (" PID {0} {1} parent={2}" -f $t.ProcessId, $t.Name, $t.ParentProcessId) +} +Write-Host ("Root(s) to stop: {0}" -f (($roots | ForEach-Object { $_.ProcessId }) -join ", ")) +Write-Host ('Relaunch command: Start-Process "shell:AppsFolder\{0}"' -f $Aumid) + +if ($DryRun) { + Write-Host "Dry run: nothing was stopped or launched." + exit 0 +} + +foreach ($r in $roots) { + $rootPid = [uint32]$r.ProcessId + $stopped = $false + if (-not $Force) { + $proc = Get-Process -Id $rootPid -ErrorAction SilentlyContinue + if ($proc -and $proc.MainWindowHandle -ne 0) { + Write-Host "Sending graceful close to PID $rootPid..." + [void]$proc.CloseMainWindow() + for ($i = 0; $i -lt 15; $i++) { + Start-Sleep -Seconds 1 + if (-not (Get-Process -Id $rootPid -ErrorAction SilentlyContinue)) { $stopped = $true; break } + } + if (-not $stopped) { + Write-Host "PID $rootPid survived graceful close (close-to-tray suspected); forcing." + } + } + } + if (-not $stopped) { + Write-Host "Force-stopping process tree at PID $rootPid..." + & "$env:SystemRoot\System32\taskkill.exe" /PID $rootPid /T /F | Out-Null + } +} + +Start-Sleep -Seconds 1 +Start-Process "shell:AppsFolder\$Aumid" +Write-Host "Codex desktop app restarted."