Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
7640a94
chore(release): open dev at 2.87.0 before releasing 2.86.0 (#7109)
github-actions[bot] Oct 14, 2026
1008302
test(codex): allow child generation diagnostics before outer timeout …
luvs01 Oct 15, 2026
c37161c
fix(codex): show last Windows desktop build assessment (#7113)
luvs01 Oct 15, 2026
65ad37d
fix(catalog): preserve context inference through provider edits (#7077)
lcxhh521 Oct 15, 2026
0b13396
fix(protocols): enforce explicit-empty tool catalogs across delivery …
luvs01 Oct 15, 2026
60bf4c7
fix(gui): select Advisor model from the routed catalog (#7114)
luvs01 Oct 15, 2026
ade27f4
fix(usage): keep usage-ledger file ids exact so Windows NTFS ids stop…
lidge-jun Oct 15, 2026
d73e9ab
fix(desktop): serialize shim publication with production process crea…
lidge-jun Oct 15, 2026
8066328
fix(claude): verify Desktop backup and optional metadata reads (carry…
lidge-jun Oct 15, 2026
c410b77
fix(anthropic): pair native beta schemas with effective headers (#7132)
lidge-jun Oct 15, 2026
9f09568
fix(oauth): preserve operator model policy during reconciliation (#7141)
lidge-jun Oct 15, 2026
a460e75
fix(cli): refuse uncertain ownership before publishing sibling runtim…
lidge-jun Oct 15, 2026
ee6cbe2
feat(codex): route hard-locked main-account traffic to a configured f…
lidge-jun Oct 15, 2026
bca6184
test(codex): use shared Windows attest spawn budgets (#7151)
lidge-jun Oct 15, 2026
113eded
feat(cli): configure usage model table row limits (carry #6436) (#7156)
lidge-jun Oct 15, 2026
d9ef862
feat(antigravity): show canonical plan badge in account rows (carry #…
lidge-jun Oct 15, 2026
83bf0c1
feat(codex): opt in to remaining included quota (carry #6629) (#7158)
lidge-jun Oct 15, 2026
408f1d4
feat(combos): require explicit consent for metered targets (carry #68…
lidge-jun Oct 15, 2026
318e483
feat(pricing): support per-model prompt-length overrides (carry #6738…
lidge-jun Oct 15, 2026
b538531
feat(combos): enroll text-only members in vision sidecar (carry #4932…
lidge-jun Oct 15, 2026
afd0bcf
feat(oauth): reset-first strategy for generic OAuth pools (carry #540…
lidge-jun Oct 15, 2026
3f6c696
fix(oauth): print fixed CLI login errors instead of provider bodies (…
lidge-jun Oct 15, 2026
041241a
test(claude): end the linger upload pump when the socket closes (#7180)
lidge-jun Oct 15, 2026
9499167
fix(codex): protect the desktop authority key in-process on Windows x…
lidge-jun Oct 15, 2026
6fa4228
test(google): guard Argon thought separation across client protocols …
lidge-jun Oct 15, 2026
359d31b
fix(codex): carry canonical subagent limits across modes (#7125) (#7128)
lidge-jun Oct 15, 2026
ccb9191
fix(catalog): preserve uncapped native maximum context windows (#7130)
lidge-jun Oct 15, 2026
93133af
fix(responses): strip Google item metadata from native OpenAI request…
lidge-jun Oct 15, 2026
f5c0f50
fix(startup): launch the registered Desktop bundle and executable (#7…
lidge-jun Oct 15, 2026
a3dfe75
fix(responses): frame retained compaction requests as history (#7138)
lidge-jun Oct 15, 2026
9d4c82a
fix(chat): restore DeepSeek reasoning on native tool continuations (#…
lidge-jun Oct 15, 2026
2cd4029
fix(anthropic): surface Opus 5.5 progress as signed commentary (#7143)
lidge-jun Oct 15, 2026
ae95f2d
fix(test): isolate service state from the shared startup home (#7161)
lidge-jun Oct 15, 2026
1549a74
feat(providers): add DeepSeek Harness authenticated account provider …
lidge-jun Oct 15, 2026
6d2ee6d
feat(claude-cli): stable conversation replay and capture-only client …
lidge-jun Oct 15, 2026
973204b
feat(claude-cli): refuse launches when the Claude subscription is exh…
lidge-jun Oct 15, 2026
37fcd24
feat(oauth): code-display login mode for hosts with no reachable loca…
lidge-jun Oct 15, 2026
f089fdf
fix(claude): bind intercept test ports without probe-and-release race…
lidge-jun Oct 15, 2026
fc6502b
test(cli): isolate sibling ensure from a running Desktop app (#7182)
lidge-jun Oct 15, 2026
8ac597a
test(cli): keep audio readiness waits inside their test budget (#7183)
lidge-jun Oct 15, 2026
f3fbf82
fix(oauth): bound refresh consumers and retain stale owners (carry #7…
lidge-jun Oct 15, 2026
a738a5e
fix(oauth): allow Muse Keychain approval and surface actionable login…
lidge-jun Oct 15, 2026
243bf2c
feat(oauth): pin generic pool conversations to request-local accounts…
lidge-jun Oct 15, 2026
b6dffa0
feat(codex): preserve signed-in quota across duplicate pool entries (…
lidge-jun Oct 15, 2026
17e307d
fix(gui): fold Advisor into the Subagents row and collapse its notice…
lidge-jun Oct 15, 2026
49ffccd
feat(vertex): accept credential JSON with secure marker lifecycle (ca…
lidge-jun Oct 15, 2026
b43f931
fix(service): bind Linux health sentinel recovery to pidfd (#7133)
lidge-jun Oct 15, 2026
4ae4936
fix(codex): keep the desktop app open on codex-restart when its picke…
lidge-jun Oct 15, 2026
f997eb8
fix(providers): carry Zed, Qoder, and OpenCode Gemini compatibility (…
lidge-jun Oct 16, 2026
f6e6f1b
fix(catalog): carry Ultra Fast discovery, vision sidecar, model alias…
lidge-jun Oct 16, 2026
d3a84c2
fix(service): carry log rotation identities, Windows lock folder and …
lidge-jun Oct 16, 2026
3785915
fix(responses): carry incomplete reasons, terminal refusals, offline …
lidge-jun Oct 16, 2026
329d91b
feat(advisor): carry bounded retention and adaptive consultation (#71…
lidge-jun Oct 16, 2026
68fad32
feat(antigravity): preserve diagnostic per-model quota evidence (carr…
lidge-jun Oct 16, 2026
9e0ed51
feat(providers): carry opt-in API key round-robin and per-key pacing …
lidge-jun Oct 16, 2026
c03eafd
refactor(proxy): share opaque transport with desktop relay (#7120)
lcxhh521 Oct 16, 2026
6ccb47a
fix(combos): carry OAuth combo cooldown, account rotation and health …
lidge-jun Oct 16, 2026
a8d1bb8
feat(gui): carry Combo visibility, workspace panes, Windows picker an…
lidge-jun Oct 16, 2026
300e426
fix(claude): carry translated-lane server tools, refusal/cap/TTL fixe…
lidge-jun Oct 16, 2026
65ef8b3
feat(responses): carry compaction capabilities and reasoning retentio…
lidge-jun Oct 16, 2026
b3cd913
refactor: break config-facade import cycles (189 -> 59 static cyclic …
lcxhh521 Oct 16, 2026
7a42860
refactor(oauth): break the ChatGPT OAuth and auth-api import cycles (…
lcxhh521 Oct 16, 2026
4ed45e9
feat(cli): carry usage comparison, log history, restart and Codex con…
lidge-jun Oct 16, 2026
050bb44
fix(gui): restore keep-native-v1 qualifier placement (#7237)
lidge-jun Oct 16, 2026
5170724
fix(providers): charge API-key cooldown to the credential sent (#7240)
lidge-jun Oct 16, 2026
64ed834
test: fix Windows default-home isolation fixtures (#7242)
lidge-jun Oct 16, 2026
e498fde
fix(config): preserve legacy OpenCode Go configs after native wire pi…
lidge-jun Oct 16, 2026
a7d3be1
fix(ci): preserve import cycles through macOS path aliases (#7241)
lidge-jun Oct 16, 2026
d3cb552
fix(compact): preserve retained reasoning across native combo failove…
lidge-jun Oct 16, 2026
1f96e40
fix(claude): activate Windows picker after dormant startup (#7243)
lidge-jun Oct 16, 2026
0a568eb
fix(desktop): use Waker::noop for the Rust 1.99 clippy lint (#7246)
lidge-jun Oct 16, 2026
de315e7
fix(cli): preserve Codex opt-out and usage comparison ownership (#7244)
lidge-jun Oct 16, 2026
447c58a
release: promote dev de315e76ad to main as 2.87.0
lidge-jun Oct 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
15 changes: 9 additions & 6 deletions app/Sources/MenuBarCore/ProxyModels.swift
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ public struct UsageAccountRow: Decodable, Equatable, Sendable {
}

public struct QuotaWindow: Decodable, Equatable, Sendable {
public let modelId: String?
public let label: String?
public let percent: Double?
public let resetAt: Double?
Expand All @@ -188,6 +189,7 @@ public struct QuotaReport: Decodable, Equatable, Sendable {

/// A provider-agnostic view of quota, since the window key differs per provider.
public struct NormalizedQuota: Equatable, Sendable {
public let modelId: String?
public let provider: String
public let providerLabel: String
public let percent: Double?
Expand Down Expand Up @@ -219,10 +221,10 @@ public extension QuotaReport {
let name = label ?? provider
var windows: [NormalizedQuota] = []

func append(_ percent: Double?, _ windowLabel: String, _ resetAt: Double?) {
func append(_ percent: Double?, _ windowLabel: String, _ resetAt: Double?, _ modelId: String? = nil) {
guard percent != nil || resetAt != nil else { return }
windows.append(NormalizedQuota(
provider: provider, providerLabel: name, percent: percent,
modelId: modelId, provider: provider, providerLabel: name, percent: percent,
windowLabel: windowLabel, resetAt: Self.date(from: resetAt)
))
}
Expand All @@ -232,15 +234,16 @@ public extension QuotaReport {
append(quota?.monthlyPercent, "month", quota?.monthlyResetAt)

for window in quota?.customWindows ?? [] {
append(window.percent, window.label ?? "window", window.resetAt)
append(window.percent, window.label ?? "window", window.resetAt, window.modelId)
}

return windows
}

/// The single window that best represents current pressure, for the compact row.
///
/// Selection is **highest reported usage**, not longest horizon. Every window can
/// Diagnostic model rows are retained in details and excluded here. Selection is
/// **highest reported usage**, not longest horizon. Every aggregate window can
/// stop work: a provider at 99% of a five-hour limit and 10% of its monthly limit is
/// blocked right now, and showing the monthly 10% would paint that row green while
/// the user cannot make a request. Ties break toward the longer horizon, since that
Expand All @@ -250,7 +253,7 @@ public extension QuotaReport {
/// em dash rather than a misleading zero.
func normalized() -> NormalizedQuota {
let name = label ?? provider
let windows = normalizedWindows()
let windows = normalizedWindows().filter { $0.modelId == nil }

// Longer horizons rank higher only as a tie-breaker.
func horizonRank(_ label: String) -> Int {
Expand All @@ -271,7 +274,7 @@ public extension QuotaReport {
} ?? windows.first

return preferred ?? NormalizedQuota(
provider: provider, providerLabel: name, percent: nil,
modelId: nil, provider: provider, providerLabel: name, percent: nil,
windowLabel: "—", resetAt: nil
)
}
Expand Down
17 changes: 17 additions & 0 deletions app/Sources/MenuBarCoreTests/ModelDecodingSuite.swift
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,23 @@ enum ModelDecodingSuite {
t.equal(report.normalized().percent, 12)
}

t.test("quotas: model diagnostics stay detailed and cannot become compact pressure") {
let json = """
{"provider":"google-antigravity","quota":{"customWindows":[
{"label":"aux","modelId":"aux","percent":100},
{"label":"Gem","percent":20}]}}
"""
let report = try decode(QuotaReport.self, json)
t.equal(report.normalized().percent, 20)
t.equal(report.normalized().windowLabel, "Gem")
t.equal(report.normalizedWindows().count, 2)
t.equal(report.quota?.customWindows?.first?.modelId, "aux")
t.equal(report.normalizedWindows().first?.modelId, "aux")
let onlyModel = try decode(QuotaReport.self,
#"{"provider":"p","quota":{"customWindows":[{"label":"aux","modelId":"aux","percent":100}]}}"#)
t.isNil(onlyModel.normalized().percent, "model-only compact percent")
}

t.test("quotas: an absent quota normalizes to a nil percent") {
let normalized = try decode(QuotaReport.self, #"{"provider":"p","label":"P"}"#).normalized()
t.isNil(normalized.percent, "percent")
Expand Down
2 changes: 1 addition & 1 deletion desktop/src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion desktop/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "opencodex-desktop"
version = "2.86.0"
version = "2.87.0"
description = "OpenCodex desktop shell"
authors = ["OpenCodex contributors"]
license = "MIT"
Expand Down
7 changes: 6 additions & 1 deletion desktop/src-tauri/src/claim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,12 @@ pub async fn run(app: &AppHandle, argv: Vec<String>, deadline: Instant) -> Claim
return ClaimResult::Failed(format!("the bundled CLI could not be started ({error})"))
}
};
match timeout_at(deadline, command.output()).await {
match timeout_at(
deadline,
crate::process_spawn::with_spawn_async(command.output()),
)
.await
{
Ok(Ok(output)) => read(output.status.code(), &output.stdout, &output.stderr),
Ok(Err(error)) => {
ClaimResult::Failed(format!("the bundled CLI could not be run ({error})"))
Expand Down
48 changes: 26 additions & 22 deletions desktop/src-tauri/src/cli_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -454,18 +454,20 @@ mod tests {
}
fn selected_observation_case(case: &str) {
let t = Temp::new();
let result = std::process::Command::new(std::env::current_exe().unwrap())
.args([
"--exact",
"cli_command::tests::selected_observation_child",
"--nocapture",
])
.env("OCX_CLI_OBSERVATION_TEST", case)
.env("HOME", &t.0)
.env("ZDOTDIR", &t.0)
.env("XDG_CONFIG_HOME", t.0.join(".config"))
.output()
.unwrap();
let result = crate::process_spawn::with_spawn(|| {
std::process::Command::new(std::env::current_exe().unwrap())
.args([
"--exact",
"cli_command::tests::selected_observation_child",
"--nocapture",
])
.env("OCX_CLI_OBSERVATION_TEST", case)
.env("HOME", &t.0)
.env("ZDOTDIR", &t.0)
.env("XDG_CONFIG_HOME", t.0.join(".config"))
.output()
})
.unwrap();
assert!(result.status.success(), "selected-observation-failed");
assert!(
String::from_utf8_lossy(&result.stdout).contains("1 passed"),
Expand Down Expand Up @@ -578,16 +580,18 @@ mod tests {
}
}
fn disabled_selector_case(action: &str) {
let result = std::process::Command::new(std::env::current_exe().unwrap())
.args([
"--exact",
"cli_command::tests::disabled_selector_child",
"--nocapture",
])
.env("OCX_CLI_SELECTOR_TEST", action)
.env("ZDOTDIR", "/invalid:zdotdir")
.output()
.unwrap();
let result = crate::process_spawn::with_spawn(|| {
std::process::Command::new(std::env::current_exe().unwrap())
.args([
"--exact",
"cli_command::tests::disabled_selector_child",
"--nocapture",
])
.env("OCX_CLI_SELECTOR_TEST", action)
.env("ZDOTDIR", "/invalid:zdotdir")
.output()
})
.unwrap();
assert!(result.status.success(), "disabled-selector-failed");
assert!(
String::from_utf8_lossy(&result.stdout).contains("1 passed"),
Expand Down
70 changes: 39 additions & 31 deletions desktop/src-tauri/src/cli_command_posix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -673,6 +673,7 @@ pub fn remove_plan(store: &Store, current: &Record) -> Result<(Record, Vec<Chang
#[cfg(all(test, unix))]
mod tests {
use super::*;
use crate::process_spawn::with_spawn;
use record::tests::{bundle, Temp};
#[cfg(target_os = "macos")]
fn stable_bundle_fixture() -> Temp {
Expand Down Expand Up @@ -701,13 +702,13 @@ mod tests {
command.arg("-N");
}
assert!(
command
with_spawn(|| command
.arg(path)
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.status()
.unwrap()
.success(),
.status())
.unwrap()
.success(),
"acl-command-failed"
);
}
Expand Down Expand Up @@ -969,7 +970,7 @@ mod tests {
if let Some(path) = path {
command.env("PATH", path);
}
let output = command.output().unwrap();
let output = with_spawn(|| command.output()).unwrap();
assert!(output.status.success());
assert_eq!(String::from_utf8(output.stdout).unwrap(), expected);
}
Expand All @@ -988,14 +989,16 @@ mod tests {
0o700,
)
.unwrap();
let out = std::process::Command::new(&shim)
.env_clear()
.env("ANTHROPIC_AUTH_TOKEN", "dummy")
.env("ANTHROPIC_API_KEY", "")
.env("OCX_NODE_LAUNCH_CONTEXT", "untrusted")
.arg("status")
.output()
.unwrap();
let out = with_spawn(|| {
std::process::Command::new(&shim)
.env_clear()
.env("ANTHROPIC_AUTH_TOKEN", "dummy")
.env("ANTHROPIC_API_KEY", "")
.env("OCX_NODE_LAUNCH_CONTEXT", "untrusted")
.arg("status")
.output()
})
.unwrap();
assert!(out.status.success());
let text = String::from_utf8(out.stdout).unwrap();
let lines: Vec<_> = text.lines().collect();
Expand All @@ -1011,9 +1014,7 @@ mod tests {
assert!(!text.contains("dummy"));
fs::remove_file(cli).unwrap();
assert_eq!(
std::process::Command::new(&shim)
.env_clear()
.output()
with_spawn(|| std::process::Command::new(&shim).env_clear().output())
.unwrap()
.status
.code(),
Expand All @@ -1035,13 +1036,18 @@ mod tests {
.replace("-N32", "-N1"),
] {
record::atomic(&t.0.join("cli"), None, inspect, 0o700).unwrap();
fs::write(&shim, rendered).unwrap();
let out = std::process::Command::new(&shim)
.env_clear()
.env("OCX_NODE_LAUNCH_CONTEXT", "inherited")
.arg("status")
.output()
.unwrap();
{
let _writer = crate::process_spawn::executable_write();
fs::write(&shim, rendered).unwrap();
}
let out = with_spawn(|| {
std::process::Command::new(&shim)
.env_clear()
.env("OCX_NODE_LAUNCH_CONTEXT", "inherited")
.arg("status")
.output()
})
.unwrap();
assert!(out.status.success());
assert_eq!(out.stdout, b"\nstatus\n");
fs::remove_file(t.0.join("cli")).unwrap();
Expand Down Expand Up @@ -1197,15 +1203,17 @@ mod tests {
// System rc files may print to stdout (Ubuntu's /etc/bash.bashrc sudo hint), so only
// the marked result lines count; a failed lookup or CLI run still fails the shell, and
// the end marker makes any extra CLI output line fail the comparison.
let output = command
.args([
"-i",
"-c",
"p=$(command -v ocx) || exit 11; o=$(ocx hello) || exit 12; \
let output = with_spawn(|| {
command
.args([
"-i",
"-c",
"p=$(command -v ocx) || exit 11; o=$(ocx hello) || exit 12; \
printf 'OCX-PATH:%s\\nOCX-OUT:%s:OCX-END\\n' \"$p\" \"$o\"",
])
.output()
.expect("cannot start real shell");
])
.output()
})
.expect("cannot start real shell");
assert!(output.status.success(), "{shell}: command failed");
let stdout = String::from_utf8(output.stdout).expect("shell stdout is not UTF-8");
let marked = |tag: &str| {
Expand Down
Loading
Loading