Repository navigation
[Bug] macOS 2.70.0 Startup safety fails in bundled CLI and ignores desktop supervision (patch included) #6222
Description
Client or integration
OpenCodex dashboard
Area
Installation or packaging
Summary
After updating the macOS desktop app to 2.70.0 using the menu-bar Install update action, Startup safety again reports that it cannot read startup protection and that the data is stale. The bundled proxy is healthy and both the app and proxy are on 2.70.0.
The same problem was locally fixed on 2.69.0. Installing the official 2.70.0 release replaced that local patch, and the underlying defects remain in the release.
I want to keep using Install update normally, without rebuilding and patching the application after each update. Please fix this in an official release and add regression coverage for the packaged macOS app and the desktop update path. I am not asking the updater to preserve arbitrary local modifications; I am asking for the official release to include the fix so this does not keep recurring.
Expected: a packaged startup probe returns a fresh result, and a desktop-owned proxy with verified login startup and active desktop supervision is recognized accurately. Missing or stale evidence must still fail closed.
Reproduction
- Run the official macOS OpenCodex desktop application with its bundled
ocxproxy. Enable Start at Login. In the affected setup, the desktop application owns and supervises the proxy; no independent CLI background service or launcher shim is installed. - Update to 2.70.0 through the menu-bar Install update action. The previous installation was a locally patched 2.69.0 that correctly displayed desktop protection.
- Open Dashboard, then Startup safety.
- Observe the Chinese messages
无法读取启动保护状态。and最新启动检查失败。以下数据已过期,不得视为已受保护的证明。. - Check the bundled CLI directly:
/Applications/OpenCodex.app/Contents/MacOS/ocx health --jsonreports healthy./Applications/OpenCodex.app/Contents/MacOS/ocx __startup-healthreturns a fresh diagnostic, but still classifies protection asnonebecause desktop supervision is not represented. - The running management endpoint
/api/startup-healthreportsdiagnosticStale: true.
The installed app bundle was confirmed as 2.70.0, with its bundled proxy serving from /Applications/OpenCodex.app/Contents/MacOS/ocx. The desktop ownership/install identity matched, the login item was registered and enabled, and the live proxy was a child of the desktop application.
Two code paths appear responsible in tag v2.70.0:
src/server/startup-health-cache.ts:runProbecallsexecFile(bun, [cli, "__startup-health"], ...), wherecliis derived fromimport.meta.dir. In a compiled executable this passes a bundled virtual source path instead of re-entering the standalone CLI correctly. The attached patch uses the existingselfLaunchArgv(["__startup-health"], { sourceEntrypoint: cli })helper.src/codex/autostart-health.ts: protection only recognizesservice,shim, andnone. It does not credit the macOS desktop application’s login registration and proxy supervision. The local patch adds a separately nameddesktopprotection with read-only ownership, login-registration, and live parent/child checks, plus corresponding GUI details.
Version
2.70.0 official macOS desktop release; also observed on 2.69.0 before applying a local patch.
Operating system
macOS 27.0, Apple Silicon (arm64)
Provider and model
Not provider-specific. The failure is in local startup diagnostics and desktop lifecycle recognition.
Logs or error output
{
"before_local_patch": {
"proxy_health": "ok",
"startup_health_endpoint": {
"status": "at-risk",
"protection": "none",
"diagnosticStale": true,
"serviceInstalled": false,
"shimInstalled": false
},
"direct_cli_diagnostic": {
"status": "at-risk",
"protection": "none",
"diagnosticStale": false
}
},
"after_local_patch": {
"status": "protected",
"protection": "desktop",
"diagnosticStale": false,
"desktop": {
"owned": true,
"loginEnabled": true,
"running": true,
"viable": true
},
"serviceInstalled": false,
"shimInstalled": false
}
}
The JSON above is a reduced diagnostic comparison, not a complete raw log.Screenshots and supporting files
The complete unified diff against official v2.70.0 is included below. Save the contents of the diff block as OpenCodex-2.70.0-startup-safety.patch for review or application.
The local patch was generated with AI assistance and is offered for maintainer review, not represented as an official or generally validated release.
Validation completed on this machine:
- 57 backend-focused tests passed across
tests/service/autostart-health.test.ts,tests/lib/self-launch-argv.test.ts,tests/gui/startup-health-ui.test.ts, andtests/config/settings-startup-health-seam.test.ts. - 8 GUI-focused tests passed across
gui/tests/startup-minimal.test.tsxandgui/tests/startup-install-result-reconciliation.test.tsx. - Backend typecheck, GUI typecheck/build, and GUI i18n checks passed.
- A compiled smoke executable exercised the actual startup-probe subprocess and returned fresh desktop protection.
- Restarting the patched application through its existing login launch job succeeded. The actual GUI then showed
重启已受保护and本地代理 · 桌面应用 · 已启用with no stale warning. - The provider configuration, service ownership record, and login launch definition were unchanged by hash comparison. No additional background service or shim was installed.
Limits: the complete repository suite was not run; the computer was not rebooted; this is a locally signed build tested on one macOS machine. Please adapt the diagnostic implementation to your supported desktop lifecycle and review all consumers of the protection value before merging.
Suggested official regression coverage: a release-built bundled CLI can run a fresh diagnostic; verified desktop supervision is recognized; stale or missing evidence never retains a protected result; and a normal desktop update followed by restart still gives a correct fresh verdict without any user-applied patch.
Complete proposed patch against v2.70.0
diff --git a/src/service/desktop-startup.ts b/src/service/desktop-startup.ts
--- /dev/null
+++ b/src/service/desktop-startup.ts
@@ -0,0 +1,70 @@
+import { execFileSync } from "node:child_process";
+import { accessSync, constants, readFileSync, realpathSync } from "node:fs";
+import { homedir } from "node:os";
+import { dirname, join } from "node:path";
+import { readPid } from "../config/process-state";
+import { resolveServiceOwnership } from "./state";
+
+export interface DesktopStartupDiagnostic {
+ owned: boolean;
+ loginEnabled: boolean;
+ running: boolean;
+ viable: boolean;
+}
+
+/** A login item alone is insufficient: the same app must own and supervise this proxy. */
+export function deriveDesktopStartup(facts: Omit<DesktopStartupDiagnostic, "viable">): DesktopStartupDiagnostic {
+ return { ...facts, viable: facts.owned && facts.loginEnabled && facts.running };
+}
+
+function run(command: string, args: string[]): string {
+ return execFileSync(command, args, { encoding: "utf8", timeout: 750, maxBuffer: 128 * 1024, stdio: ["ignore", "pipe", "pipe"] }).trim();
+}
+
+function processIdentity(pid: number): { parent: number; executable: string } | null {
+ const row = /^(\d+)\s+(.+)$/.exec(run("/bin/ps", ["-p", String(pid), "-o", "ppid=,comm="]));
+ return row ? { parent: Number(row[1]), executable: realpathSync(row[2]!) } : null;
+}
+
+/** Read-only macOS desktop ownership, login registration and live parent/child checks. */
+export function diagnoseMacDesktopStartup(): DesktopStartupDiagnostic | undefined {
+ if (process.platform !== "darwin") return undefined;
+ const owner = resolveServiceOwnership();
+ if (owner.kind !== "owned" || owner.ownership.owner !== "desktop") return undefined;
+ const facts = { owned: false, loginEnabled: false, running: false };
+ try {
+ const home = homedir();
+ const id = readFileSync(join(home, "Library", "Application Support", "com.opencodex.desktop", "install-id"), "utf8").trim();
+ facts.owned = id === owner.ownership.installId;
+ if (!facts.owned) return deriveDesktopStartup(facts);
+ const path = join(home, "Library", "LaunchAgents", "OpenCodex.plist");
+ const plist = JSON.parse(run("/usr/bin/plutil", ["-convert", "json", "-o", "-", path]));
+ const args = plist.ProgramArguments;
+ if (plist.Label !== "OpenCodex" || plist.RunAtLoad !== true || !Array.isArray(args)
+ || args.length !== 2 || args[1] !== "--autostart" || typeof args[0] !== "string"
+ || !args[0].endsWith("/Contents/MacOS/opencodex-desktop")
+ || (plist.Program !== undefined && plist.Program !== args[0])) return deriveDesktopStartup(facts);
+ const app = realpathSync(args[0]);
+ const proxy = realpathSync(join(dirname(app), "ocx"));
+ accessSync(app, constants.X_OK);
+ accessSync(proxy, constants.X_OK);
+ const domain = `gui/${process.getuid!()}`;
+ const disabled = run("/bin/launchctl", ["print-disabled", domain]);
+ const loaded = run("/bin/launchctl", ["print", `${domain}/OpenCodex`]);
+ const program = /^\s*program = (.+)$/m.exec(loaded)?.[1];
+ const loadedPath = /^\s*path = (.+)$/m.exec(loaded)?.[1];
+ facts.loginEnabled = !/"OpenCodex"\s*=>\s*disabled/.test(disabled)
+ && program !== undefined && realpathSync(program) === app
+ && loadedPath !== undefined && realpathSync(loadedPath) === realpathSync(path);
+ const pid = readPid();
+ if (pid !== null) {
+ const child = processIdentity(pid);
+ const parent = child && child.parent > 1 ? processIdentity(child.parent) : null;
+ facts.running = child?.executable === proxy && parent?.executable === app && readPid() === pid;
+ }
+ return deriveDesktopStartup(facts);
+ } catch {
+ // Unreadable launchd/process evidence never grants protection.
+ return deriveDesktopStartup({ ...facts, running: false });
+ }
+}
diff --git a/src/server/startup-health-cache.ts b/src/server/startup-health-cache.ts
--- a/src/server/startup-health-cache.ts
+++ b/src/server/startup-health-cache.ts
@@ -5,6 +5,7 @@
import { getCodexRoutingKind } from "../codex/inject";
import { diagnoseCodexShim } from "../codex/shim";
import { durableBunPath } from "../lib/bun-runtime";
+import { selfLaunchArgv } from "../lib/self-launch-argv";
import type { OcxConfig } from "../types";
import { truncateRetainedUtf8 } from "../lib/admission";
@@ -121,7 +122,7 @@
const bun = durableBunPath();
const cli = join(import.meta.dir, "..", "cli", "index.ts");
return new Promise(resolve => {
- execFile(bun, [cli, "__startup-health"], {
+ execFile(bun, selfLaunchArgv(["__startup-health"], { sourceEntrypoint: cli }), {
encoding: "utf8",
env: process.env,
timeout: PROBE_TIMEOUT_MS,
diff --git a/src/codex/autostart-health.ts b/src/codex/autostart-health.ts
--- a/src/codex/autostart-health.ts
+++ b/src/codex/autostart-health.ts
@@ -4,12 +4,14 @@
import { getCodexRoutingKind, type CodexRoutingKind } from "./inject";
import { collectRoutingAdoption, type RoutingAdoptionEvidence } from "./routing-adoption";
import { diagnoseCodexShim, type CodexShimDiagnostic } from "./shim";
-
-export type StartupProtection = "service" | "shim" | "none";
+import { diagnoseMacDesktopStartup, type DesktopStartupDiagnostic } from "../service/desktop-startup";
+
+export type StartupProtection = "service" | "desktop" | "shim" | "none";
export type StartupHealthStatus = "native" | "protected" | "at-risk";
export type ShimCoverage = "full" | "cli-only" | "none";
export interface StartupHealthInputs {
+ desktop?: DesktopStartupDiagnostic;
routingKind: CodexRoutingKind;
autostartEnabled: boolean;
serviceInstalled: boolean;
@@ -27,6 +29,7 @@
}
export interface StartupHealth {
+ desktop?: DesktopStartupDiagnostic;
status: StartupHealthStatus;
routingKind: CodexRoutingKind;
routingInjected: boolean;
@@ -77,12 +80,14 @@
// We can only credit an opencodex service/shim for routing that opencodex owns.
// An arbitrary localhost gateway has an independent lifecycle that OCX cannot repair.
const ownsLocalRouting = inputs.routingKind === "opencodex-local";
+ const desktopEffective = inputs.platform === "darwin" && inputs.desktop?.viable === true;
const protection: StartupProtection = ownsLocalRouting && inputs.serviceViable
? "service"
+ : ownsLocalRouting && desktopEffective ? "desktop"
: ownsLocalRouting && shimEffective
? "shim"
: "none";
- const rebootSafe = !localRoutingDependency || (ownsLocalRouting && inputs.serviceViable);
+ const rebootSafe = !localRoutingDependency || (ownsLocalRouting && (inputs.serviceViable || desktopEffective));
const status: StartupHealthStatus = !localRoutingDependency
? "native"
: rebootSafe
@@ -115,6 +120,7 @@
}
export interface StartupHealthDiagnostics {
+ desktop?: DesktopStartupDiagnostic;
routingKind?: CodexRoutingKind;
service?: ServiceDiagnostic;
shim?: CodexShimDiagnostic;
@@ -129,9 +135,11 @@
const shim = diagnostics.shim ?? diagnoseCodexShim();
const service = diagnostics.service ?? diagnoseService();
const routingKind = diagnostics.routingKind ?? getCodexRoutingKind();
+ const desktop = diagnostics.desktop ?? diagnoseMacDesktopStartup();
const routingAdoption = diagnostics.routingAdoption
?? (routingKind === "opencodex-local" ? collectRoutingAdoption({ routingKind }) : undefined);
return deriveStartupHealth({
+ ...(desktop ? { desktop } : {}),
routingKind,
autostartEnabled: codexAutoStartEnabled(config),
serviceInstalled: service.installed,
@@ -187,6 +195,7 @@
? "custom remote Codex routing (no local restart dependency)"
: "native Codex routing (no opencodex restart dependency)";
if (health.protection === "service") return "protected by background service";
+ if (health.protection === "desktop") return "protected by desktop app at login and its proxy supervisor";
const command = health.recommendedCommand ?? health.commands.restoreNative;
if (health.routingKind === "unknown") return `AT RISK after restart (Codex routing could not be verified; run '${command}')`;
if (health.routingKind === "custom-local") return `AT RISK after restart (custom local gateway lifecycle is not managed by opencodex; run '${command}')`;
diff --git a/gui/src/pages/startup-shared.ts b/gui/src/pages/startup-shared.ts
--- a/gui/src/pages/startup-shared.ts
+++ b/gui/src/pages/startup-shared.ts
@@ -1,10 +1,11 @@
import type { TKey } from "../i18n/shared";
export type StartupStatus = "native" | "protected" | "at-risk";
-export type StartupProtection = "service" | "shim" | "none";
+export type StartupProtection = "service" | "desktop" | "shim" | "none";
export type StartupInstallAction = "install-service" | "install-shim";
export interface StartupHealthData {
+ desktop?: { owned: boolean; loginEnabled: boolean; running: boolean; viable: boolean };
status: StartupStatus;
routingKind: "native" | "opencodex-local" | "custom-local" | "custom-remote" | "unknown";
routingInjected: boolean;
@@ -64,6 +65,7 @@
};
export const PROTECTION_KEYS: Record<StartupProtection, TKey> = {
+ desktop: "startup.protection.desktop",
service: "startup.protection.service",
shim: "startup.protection.shim",
none: "startup.protection.none",
diff --git a/gui/src/pages/startup-sections.tsx b/gui/src/pages/startup-sections.tsx
--- a/gui/src/pages/startup-sections.tsx
+++ b/gui/src/pages/startup-sections.tsx
@@ -88,7 +88,7 @@
// Repair only rewrites stale assets — conflict/disabled need uninstall/reinstall, not repair.
const serviceNeedsRepair = data.serviceSupported && data.serviceInstalled && data.serviceStale && !data.serviceConflict;
const shimNeedsRepair = data.shimInstalled && !data.shimHealthy;
- const actionsDisabled = installBusy !== null || failed || loading;
+ const actionsDisabled = installBusy !== null || failed || loading || data.desktop?.owned === true;
return (
<section className="panel startup-details">
@@ -96,6 +96,14 @@
<h3 className="panel-title">{t("startup.details")}</h3>
<span className="muted mono">{data.platform}</span>
</div>
+ {data.desktop && (
+ <div className="startup-detail-row">
+ <div><strong>{t("startup.protection.desktop")}</strong><span>{t("startup.desktopHint")}</span></div>
+ <div className="startup-detail-actions">
+ <StartupStateBadge ok={!failed && data.desktop.viable} yes={t("startup.viable")} no={t("startup.unhealthy")} />
+ </div>
+ </div>
+ )}
<div className="startup-detail-row">
<div><strong>{t("startup.service")}</strong><span>{t("startup.serviceHint")}</span></div>
<div className="startup-detail-actions">
diff --git a/tests/service/autostart-health.test.ts b/tests/service/autostart-health.test.ts
--- a/tests/service/autostart-health.test.ts
+++ b/tests/service/autostart-health.test.ts
@@ -1,3 +1,4 @@
+import { deriveDesktopStartup } from "../../src/service/desktop-startup";
import { describe, expect, test } from "bun:test";
import { collectStartupHealth, deriveStartupHealth, formatStartupRoutingDetail, injectedRoutingRestartWarningLines, startupHealthSummary } from "../../src/codex/autostart-health";
import { unusedProxyWarningLines } from "../../src/cli/status";
@@ -676,3 +677,29 @@
})).toEqual({ status: "enumerated", processes: [{ pid: 4242, commandLine: "codex chat" }] });
});
});
+
+
+describe("macOS desktop startup protection", () => {
+ const facts = { owned: true, loginEnabled: true, running: true };
+ test("credits verified desktop supervision without claiming a CLI service", () => {
+ const desktop = deriveDesktopStartup(facts);
+ const health = deriveStartupHealth({ ...base, platform: "darwin", desktop });
+ expect(health).toMatchObject({ protection: "desktop", rebootSafe: true, status: "protected", serviceInstalled: false });
+ expect(startupHealthSummary(health)).toContain("desktop app");
+ });
+ test("missing ownership, login registration or running supervisor never grants protection", () => {
+ for (const key of ["owned", "loginEnabled", "running"] as const) {
+ const desktop = deriveDesktopStartup({ ...facts, [key]: false });
+ expect(deriveStartupHealth({ ...base, platform: "darwin", desktop }).rebootSafe).toBe(false);
+ }
+ });
+ test("desktop evidence cannot protect another OS or an unrelated gateway", () => {
+ const desktop = deriveDesktopStartup(facts);
+ expect(deriveStartupHealth({ ...base, desktop }).status).toBe("at-risk");
+ expect(deriveStartupHealth({ ...base, platform: "darwin", routingKind: "custom-local", desktop }).status).toBe("at-risk");
+ });
+ test("a failed follow-up probe revokes the desktop protection claim", () => {
+ const health = deriveStartupHealth({ ...base, platform: "darwin", desktop: deriveDesktopStartup(facts) });
+ expect(markStartupHealthDiagnosticStale(health)).toMatchObject({ protection: "none", rebootSafe: false, diagnosticStale: true });
+ });
+});
diff --git a/gui/tests/startup-minimal.test.tsx b/gui/tests/startup-minimal.test.tsx
--- a/gui/tests/startup-minimal.test.tsx
+++ b/gui/tests/startup-minimal.test.tsx
@@ -36,8 +36,10 @@
}
let status: "protected" | "at-risk" = "protected";
+let desktop = false;
beforeEach(() => {
+ desktop = false;
clearClientResourceStoresForTests();
previousGlobals = Object.fromEntries(globals.map(k => [k, Reflect.get(globalThis, k)])) as typeof previousGlobals;
testWindow = new Window({ url: "http://localhost/#startup" });
@@ -54,7 +56,11 @@
configurable: true,
value: async (url: string) => {
const path = new URL(String(url), "http://localhost/").pathname;
- if (path === "/api/startup-health") return response(health(status));
+ if (path === "/api/startup-health") return response(desktop ? {
+ ...health("protected"), protection: "desktop", serviceInstalled: false, serviceViable: false,
+ shimInstalled: false, shimHealthy: false,
+ desktop: { owned: true, loginEnabled: true, running: true, viable: true },
+ } : health(status));
if (path === "/api/settings") return response({ codexAutoStart: true, codexRuntime: { version: "x" } });
return response({});
},
@@ -99,3 +105,13 @@
const details = container.querySelector<HTMLDetailsElement>("details.startup-recovery-details")!;
expect(details.open).toBe(true);
});
+
+test("desktop protection is named separately and cannot install a competing service", async () => {
+ desktop = true;
+ await mount();
+ expect(container.querySelector(".startup-state-line")?.textContent).toContain("Desktop app");
+ expect(container.querySelector(".startup-details")?.textContent).toContain("desktop app supervises");
+ const install = container.querySelector<HTMLButtonElement>('button[aria-label="Background service - Install"]');
+ expect(install).not.toBeNull();
+ expect(install!.disabled).toBe(true);
+});
diff --git a/structure/gui-and-management-api.md b/structure/gui-and-management-api.md
--- a/structure/gui-and-management-api.md
+++ b/structure/gui-and-management-api.md
@@ -437,6 +437,8 @@
## Startup safety
+The local macOS desktop patch credits `desktop` protection only after matching the durable app ownership, enabled and loaded login item, and live app-to-bundled-proxy process relationship. This does not claim an independently installed CLI service. Missing evidence remains at risk. Compiled startup probes use the standalone-aware self-launch argument builder.
+
**Startup safety** is reachable by route (`/#startup`) and rendered by the app, but it is not a
sidebar entry: it is entered from the dashboard's startup-state row, which links there whether the
current state needs remediation or merely reports how routing is protected. Its warning state is derived from active
diff --git a/structure/runtime.md b/structure/runtime.md
--- a/structure/runtime.md
+++ b/structure/runtime.md
@@ -598,3 +598,5 @@
Bun updater lease and recovery behavior follows the [update transaction contract](ops/service-and-sidecars.md#bun-updater-ownership-transaction).
Companion timeline and filtered totals follow the [companion usage contract](companion.md). [Ongoing priority failback](providers/openai-accounts.md#ongoing-priority-failback) reuses request-triggered quota priming and captured-account dispatch; it adds no periodic worker or mid-request account switch. The serving-install census and bounded foreground delegation in `src/config/serving-runtimes.ts` follow [service command selection](ops/service-and-sidecars.md#background-service-command-selection).
+
+Local macOS desktop startup diagnostics use `src/service/desktop-startup.ts` to read the ownership record, launchd login registration and exact parent/child executable paths without mutating them. The startup-health subprocess uses `selfLaunchArgv` to support both source and compiled entrypoints.
diff --git a/gui/src/i18n/en.ts b/gui/src/i18n/en.ts
--- a/gui/src/i18n/en.ts
+++ b/gui/src/i18n/en.ts
@@ -266,6 +266,8 @@
"startup.preference": "On-demand startup",
"startup.enabled": "Enabled",
"startup.disabled": "Disabled",
+ "startup.protection.desktop": "Desktop app",
+ "startup.desktopHint": "Starts at login; the desktop app supervises its bundled proxy. Ownership and live processes are verified.",
"startup.protection.service": "Background service",
"startup.protection.shim": "Launcher shim",
"startup.protection.none": "Not installed",
diff --git a/gui/src/i18n/de.ts b/gui/src/i18n/de.ts
--- a/gui/src/i18n/de.ts
+++ b/gui/src/i18n/de.ts
@@ -257,6 +257,8 @@
"startup.preference": "Start bei Bedarf",
"startup.enabled": "Aktiviert",
"startup.disabled": "Deaktiviert",
+ "startup.protection.desktop": "Desktop-App",
+ "startup.desktopHint": "Startet bei der Anmeldung und überwacht den integrierten Proxy. Zuständigkeit und laufende Prozesse werden geprüft.",
"startup.protection.service": "Hintergrunddienst",
"startup.protection.shim": "Launcher-Shim",
"startup.protection.none": "Nicht installiert",
diff --git a/gui/src/i18n/fr.ts b/gui/src/i18n/fr.ts
--- a/gui/src/i18n/fr.ts
+++ b/gui/src/i18n/fr.ts
@@ -259,6 +259,8 @@
"startup.preference": "Démarrage à la demande",
"startup.enabled": "Activé",
"startup.disabled": "Désactivé",
+ "startup.protection.desktop": "Application de bureau",
+ "startup.desktopHint": "Démarre à la connexion et supervise son proxy intégré. La prise en charge et les processus actifs sont vérifiés.",
"startup.protection.service": "Service en arrière-plan",
"startup.protection.shim": "Mécanisme de lancement",
"startup.protection.none": "Non installé",
diff --git a/gui/src/i18n/ja.ts b/gui/src/i18n/ja.ts
--- a/gui/src/i18n/ja.ts
+++ b/gui/src/i18n/ja.ts
@@ -264,6 +264,8 @@
"startup.preference": "オンデマンド起動",
"startup.enabled": "有効",
"startup.disabled": "無効",
+ "startup.protection.desktop": "デスクトップアプリ",
+ "startup.desktopHint": "ログイン時に起動し、内蔵プロキシを監視します。管理権限の記録と実行中のプロセスを確認します。",
"startup.protection.service": "バックグラウンドサービス",
"startup.protection.shim": "Launcher shim",
"startup.protection.none": "未インストール",
diff --git a/gui/src/i18n/ko.ts b/gui/src/i18n/ko.ts
--- a/gui/src/i18n/ko.ts
+++ b/gui/src/i18n/ko.ts
@@ -259,6 +259,8 @@
"startup.preference": "필요 시 자동 시작",
"startup.enabled": "켜짐",
"startup.disabled": "꺼짐",
+ "startup.protection.desktop": "데스크톱 앱",
+ "startup.desktopHint": "로그인 시 시작하여 내장 프록시를 관리합니다. 소유 기록과 실행 중인 프로세스를 확인합니다.",
"startup.protection.service": "백그라운드 서비스",
"startup.protection.shim": "Launcher shim",
"startup.protection.none": "설치되지 않음",
diff --git a/gui/src/i18n/ru.ts b/gui/src/i18n/ru.ts
--- a/gui/src/i18n/ru.ts
+++ b/gui/src/i18n/ru.ts
@@ -264,6 +264,8 @@
"startup.preference": "Запуск по требованию",
"startup.enabled": "Включён",
"startup.disabled": "Выключен",
+ "startup.protection.desktop": "Настольное приложение",
+ "startup.desktopHint": "Запускается при входе и следит за встроенным прокси. Проверяются владелец и работающие процессы.",
"startup.protection.service": "Фоновая служба",
"startup.protection.shim": "Launcher shim",
"startup.protection.none": "Не установлен",
diff --git a/gui/src/i18n/tr.ts b/gui/src/i18n/tr.ts
--- a/gui/src/i18n/tr.ts
+++ b/gui/src/i18n/tr.ts
@@ -265,6 +265,8 @@
"startup.preference": "İsteğe bağlı başlatma",
"startup.enabled": "Etkin",
"startup.disabled": "Devre dışı",
+ "startup.protection.desktop": "Masaüstü uygulaması",
+ "startup.desktopHint": "Oturum açıldığında başlar ve yerleşik proxy’yi izler. Sahiplik ve çalışan işlemler doğrulanır.",
"startup.protection.service": "Arka plan servisi",
"startup.protection.shim": "Başlatıcı shim",
"startup.protection.none": "Yüklü değil",
diff --git a/gui/src/i18n/vi.ts b/gui/src/i18n/vi.ts
--- a/gui/src/i18n/vi.ts
+++ b/gui/src/i18n/vi.ts
@@ -259,6 +259,8 @@
"startup.preference": "Khởi động theo yêu cầu",
"startup.enabled": "Đã bật",
"startup.disabled": "Đã tắt",
+ "startup.protection.desktop": "Ứng dụng máy tính",
+ "startup.desktopHint": "Khởi chạy khi đăng nhập và giám sát proxy tích hợp. Xác minh quyền quản lý và các tiến trình đang chạy.",
"startup.protection.service": "Service nền",
"startup.protection.shim": "Trình bao bọc khởi chạy",
"startup.protection.none": "Chưa cài đặt",
diff --git a/gui/src/i18n/zh.ts b/gui/src/i18n/zh.ts
--- a/gui/src/i18n/zh.ts
+++ b/gui/src/i18n/zh.ts
@@ -259,6 +259,8 @@
"startup.preference": "按需启动",
"startup.enabled": "已启用",
"startup.disabled": "已禁用",
+ "startup.protection.desktop": "桌面应用",
+ "startup.desktopHint": "登录时启动,由桌面应用守护内置代理;已核对接管记录和实际进程。",
"startup.protection.service": "后台服务",
"startup.protection.shim": "Launcher shim",
"startup.protection.none": "未安装",
diff --git a/gui/src/i18n/zh-TW.ts b/gui/src/i18n/zh-TW.ts
--- a/gui/src/i18n/zh-TW.ts
+++ b/gui/src/i18n/zh-TW.ts
@@ -151,6 +151,8 @@
"startup.preference": "按需啟動",
"startup.enabled": "已啟用",
"startup.disabled": "已停用",
+ "startup.protection.desktop": "桌面應用程式",
+ "startup.desktopHint": "登入時啟動,由桌面應用程式守護內建代理;已核對接管記錄和實際程序。",
"startup.protection.service": "背景服務",
"startup.protection.shim": "Launcher shim",
"startup.protection.none": "未安裝",
Redacted configuration
{
"codexAutoStart": true
}Checks
- I searched existing issues and documentation.
- I removed secrets, tokens, account details, request credentials, and personal data.
Activity
assets: desktop startup safety recovery evidence for #6222
fix(service): recognize macOS desktop startup and supervise bundled p…
fix: packaged macOS startup probes and desktop ownership safety (#6286)
lidge-jun commentedon Sep 30, 2026
Fixed on
devby #6286, merged ase5705866b6bcc1de367cb5f338560ce7af4b7533.The packaged probe now re-enters the bundled executable correctly, verified macOS desktop supervision is recognized, and failed/stale evidence retains desktop ownership without recommending a competing service. Focused packaged and fail-closed regressions are included. The fix is available for the next official desktop release; the installed application was not replaced during this work. Per the owner’s integration rule, full Cross-platform CI is deferred to the final union run.