Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
6524ce8
chore(release): open dev at 2.83.0 before releasing 2.82.0 (#6875)
github-actions[bot] Oct 10, 2026
696dadc
fix(opengateway): lower routed custom tools on the Responses wire (#6…
lidge-jun Oct 10, 2026
be23d01
fix(config): coordinate Grok and link writers (#6869)
luvs01 Oct 10, 2026
4222577
fix(claude): fill the unset Sonnet tier with claude-sonnet-5-5 (#6874)
lhj6102 Oct 10, 2026
fca9cbf
fix(codex): never discard a rotated native-main refresh token on canc…
MarcTCruz Oct 10, 2026
0dd7451
fix: release active-turn admission when the client cancels (#6850)
vibewriter-hand Oct 10, 2026
aa95a48
fix(ollama-native): key response tool calls by native id before index…
MarcTCruz Oct 10, 2026
aa09c2c
fix(zcode): carry reasoning ladders and image input into the provider…
Blazetes Oct 10, 2026
3354119
fix(responses): warn once when a custom provider drops replayed plain…
lidge-jun Oct 10, 2026
7751208
fix(google): let the empty-completion retry replay a pre-output Verte…
lidge-jun Oct 10, 2026
71adcc1
fix(server): release inbound body admission when the client disconnec…
lidge-jun Oct 10, 2026
6c826bd
fix(logs): attribute proxy replay refusals as transport-ambiguous (#6…
lidge-jun Oct 10, 2026
17dbbac
docs(devlog): archive 205 landed units from _plan to _fin (#6884)
lidge-jun Oct 10, 2026
84b9e88
fix(desktop): judge machine-path-conflict from a fresh logon PATH (#6…
lidge-jun Oct 10, 2026
f169bcf
fix(copilot): negotiate Auto for positively restricted accounts (carr…
lidge-jun Oct 10, 2026
2d56558
fix(logs): keep recognized bare upstream error classes in synthesized…
lidge-jun Oct 10, 2026
9f34ea7
test: isolate host settings and bound native Windows fixtures (#6864)
mashfromband Oct 10, 2026
0025374
fix(chat): promote caller x-session-id on Chat Completions ingress (#…
lidge-jun Oct 10, 2026
5c937a4
fix(codex): keep rediscovering main-account credits after an unavaila…
lidge-jun Oct 10, 2026
fe8864d
fix(responses): report spend-ledger storage refusals as local 503s (#…
lidge-jun Oct 10, 2026
3649d58
test: report the sibling-recycle child's state when it stalls (#6893)
lidge-jun Oct 10, 2026
03b865e
docs(devlog): archive release-train coordination records to _fin (#6896)
lidge-jun Oct 10, 2026
d49df2f
fix(grok): claim coupon redemptions before dispatch and reconcile unc…
luvs01 Oct 10, 2026
ea678a6
fix(logs): drop credential-shaped upstream diagnostics before they re…
lidge-jun Oct 10, 2026
c61c3f6
fix(copilot): restore the configured wire before each Auto negotiatio…
lidge-jun Oct 10, 2026
eb06a2c
Merge main into 2.83.0 promotion
lidge-jun Oct 10, 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
  •  
  •  
  •  
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.82.0"
version = "2.83.0"
description = "OpenCodex desktop shell"
authors = ["OpenCodex contributors"]
license = "MIT"
Expand Down
10 changes: 10 additions & 0 deletions desktop/src-tauri/src/cli_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,16 @@ fn perform_in(
issues.push(e);
}
}
#[cfg(windows)]
if !remove && r.enabled {
if let Some(owned) = &r.windows {
match windows::machine_conflict(&owned.entry) {
Ok(true) => issues.push("machine-path-conflict".into()),
Ok(false) => {}
Err(e) => issues.push(e),
}
}
}
let configured = r.enabled
&& r.bundle.is_some()
&& (r.posix.is_some() || r.windows.is_some())
Expand Down
258 changes: 249 additions & 9 deletions desktop/src-tauri/src/cli_command_windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,76 @@ fn normalized(s: &str) -> String {
.to_ascii_lowercase()
}
#[cfg(any(windows, test))]
fn path_from_environment_block(block: &[u16]) -> Result<Option<Vec<String>>> {
let end = block
.windows(2)
.position(|pair| pair == [0, 0])
.ok_or("machine-path-unobserved")?;
let mut start = 0;
while start < end {
let finish = block[start..end]
.iter()
.position(|unit| *unit == 0)
.map(|offset| start + offset)
.unwrap_or(end);
let variable = &block[start..finish];
// A drive pseudo-variable such as =C:=C:\x is not an environment name.
if variable.first() != Some(&(b'=' as u16)) {
if let Some(separator) = variable.iter().position(|unit| *unit == b'=' as u16) {
if String::from_utf16(&variable[..separator])
.is_ok_and(|name| name.eq_ignore_ascii_case("PATH"))
{
let value = String::from_utf16(&variable[separator + 1..])
.map_err(|_| "machine-path-unobserved")?;
return Ok(Some(parts(&value)));
}
}
}
start = finish + 1;
}
Ok(None)
}
#[cfg(any(windows, test))]
fn unquoted_entry(entry: &str) -> &str {
entry
.strip_prefix('"')
.and_then(|s| s.strip_suffix('"'))
.unwrap_or(entry)
}
#[cfg(any(windows, test))]
fn searchable_entry(entry: &str) -> Option<&str> {
let entry = unquoted_entry(entry);
let bytes = entry.as_bytes();
let absolute = (bytes.len() >= 3
&& bytes[0].is_ascii_alphabetic()
&& bytes[1] == b':'
&& matches!(bytes[2], b'\\' | b'/'))
|| entry.starts_with("\\\\")
|| entry.starts_with("//");
// cmd searches the working directory before PATH; relative and %-literal entries
// are cwd-dependent, so probing them cannot establish a PATH conflict.
(!entry.is_empty() && !entry.contains('%') && absolute).then_some(entry)
}
#[cfg(any(windows, test))]
fn conflict_before_desktop(
entries: &[String],
desktop_dir: &str,
mut has_ocx: impl FnMut(&str) -> bool,
) -> Result<bool> {
for entry in entries {
let unquoted = unquoted_entry(entry);
if normalized(unquoted) == normalized(desktop_dir) {
return Ok(false);
}
if let Some(searchable) = searchable_entry(entry) {
if has_ocx(searchable) {
return Ok(true);
}
}
}
Err("machine-path-unobserved".into())
}
#[cfg(any(windows, test))]
fn parts(s: &str) -> Vec<String> {
if s.is_empty() {
Vec::new()
Expand Down Expand Up @@ -802,7 +872,7 @@ mod os {
},
}
}
pub fn machine_conflict() -> Result<bool> {
fn legacy_machine_conflict() -> Result<bool> {
let k = RegKey::predef(HKEY_LOCAL_MACHINE)
.open_subkey_with_flags(
"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment",
Expand All @@ -826,6 +896,76 @@ mod os {
}
Ok(false)
}
fn fresh_logon_path() -> Result<Option<Vec<String>>> {
use std::{ffi::c_void, ptr};
type Ptr = *mut c_void;
#[link(name = "advapi32")]
unsafe extern "system" {
fn OpenProcessToken(process: Ptr, access: u32, token: *mut Ptr) -> i32;
}
#[link(name = "userenv")]
unsafe extern "system" {
fn CreateEnvironmentBlock(block: *mut Ptr, token: Ptr, inherit: i32) -> i32;
fn DestroyEnvironmentBlock(block: Ptr) -> i32;
}
#[link(name = "kernel32")]
unsafe extern "system" {
fn GetCurrentProcess() -> Ptr;
fn CloseHandle(handle: Ptr) -> i32;
}
struct Token(Ptr);
impl Drop for Token {
fn drop(&mut self) {
unsafe { CloseHandle(self.0) };
}
}
struct Block(Ptr);
impl Drop for Block {
fn drop(&mut self) {
if !self.0.is_null() {
unsafe { DestroyEnvironmentBlock(self.0) };
}
}
}
let mut token = ptr::null_mut();
if unsafe { OpenProcessToken(GetCurrentProcess(), 0x8 | 0x2, &mut token) } == 0 {
return Err("machine-path-unobserved".into());
}
let token = Token(token);
let mut block = ptr::null_mut();
let created = unsafe { CreateEnvironmentBlock(&mut block, token.0, 0) };
let block = Block(block);
if created == 0 || block.0.is_null() {
return Err("machine-path-unobserved".into());
}
let mut words = Vec::new();
for index in 0..(1 << 20) {
let unit = unsafe { *block.0.cast::<u16>().add(index) };
words.push(unit);
if unit == 0 && (index == 0 || words[index - 1] == 0) {
if index == 0 {
words.push(0);
}
return path_from_environment_block(&words);
}
}
Err("machine-path-unobserved".into())
}
fn has_ocx(dir: &str) -> bool {
["ocx.exe", "ocx.com", "ocx.cmd", "ocx.bat"]
.iter()
.any(|name| Path::new(dir).join(name).is_file())
}
pub fn machine_conflict(desktop_dir: &str) -> Result<bool> {
match fresh_logon_path() {
Ok(Some(entries)) => conflict_before_desktop(&entries, desktop_dir, has_ocx),
Err(_) | Ok(None) => match legacy_machine_conflict() {
Ok(false) => Ok(false),
Ok(true) => Err("machine-path-unobserved".into()),
Err(e) => Err(e),
},
}
}
fn expand(s: &str) -> Result<String> {
#[link(name = "kernel32")]
unsafe extern "system" {
Expand Down Expand Up @@ -900,6 +1040,10 @@ pub fn apply_change(c: &Change) -> Result<()> {
}
os::write(c)
}
#[cfg(windows)]
pub fn machine_conflict(desktop_dir: &str) -> Result<bool> {
os::machine_conflict(desktop_dir)
}
#[cfg(not(windows))]
pub fn apply_change(_: &Change) -> Result<()> {
Err("registry-unsupported".into())
Expand All @@ -918,7 +1062,7 @@ pub fn plan(
.transpose()?
.unwrap_or_default();
let mut next = current.clone();
let mut issues = Vec::new();
let issues = Vec::new();
let result = if remove_owned {
if let Some(owned) = &current.windows {
remove(&raw, owned)?
Expand Down Expand Up @@ -962,13 +1106,6 @@ pub fn plan(
backup_path: None,
}]
};
if !remove_owned {
match os::machine_conflict() {
Ok(true) => issues.push("machine-path-conflict".into()),
Ok(false) => {}
Err(e) => issues.push(e),
}
}
Ok((next, changes, issues))
}
#[cfg(windows)]
Expand All @@ -982,6 +1119,109 @@ pub fn notify() -> Result<()> {
#[cfg(test)]
mod tests {
use super::*;
fn environment_block(variables: &[&str]) -> Vec<u16> {
let mut block = Vec::new();
for variable in variables {
block.extend(variable.encode_utf16());
block.push(0);
}
block.push(0);
if variables.is_empty() {
block.push(0);
}
block
}
#[test]
fn fresh_logon_environment_block_path_parsing() {
let block = environment_block(&["=C:=C:\\x", "TEMP=C:\\tmp", "Path=C:\\one;;C:\\two;"]);
assert_eq!(
path_from_environment_block(&block).unwrap(),
Some(vec![
"C:\\one".into(),
"".into(),
"C:\\two".into(),
"".into()
])
);
assert_eq!(
path_from_environment_block(&environment_block(&["PATH=C:\\upper"])).unwrap(),
Some(vec!["C:\\upper".into()])
);
assert_eq!(
path_from_environment_block(&environment_block(&["=C:=C:\\x", "TEMP=x"])).unwrap(),
None
);
assert_eq!(
path_from_environment_block(&environment_block(&["Path="])).unwrap(),
Some(vec![])
);
assert_eq!(path_from_environment_block(&[0, 0]).unwrap(), None);
assert_eq!(
path_from_environment_block(&[]).unwrap_err(),
"machine-path-unobserved"
);
assert_eq!(
path_from_environment_block(&"PATH=x\0".encode_utf16().collect::<Vec<_>>())
.unwrap_err(),
"machine-path-unobserved"
);
let mut invalid = environment_block(&["PATH="]);
invalid.insert(5, 0xd800);
assert_eq!(
path_from_environment_block(&invalid).unwrap_err(),
"machine-path-unobserved"
);
}
#[test]
fn fresh_logon_conflict_respects_desktop_order() {
let desktop = r"C:\Program Files\OpenCodex";
let entries = [
r"C:\Windows\system32",
"%NVM_HOME%",
"%NVM_SYMLINK%",
desktop,
r"C:\nvm4w\nodejs",
];
let entries: Vec<String> = entries.iter().map(|entry| (*entry).into()).collect();
assert_eq!(
conflict_before_desktop(&entries, desktop, |dir| dir == r"C:\nvm4w\nodejs"),
Ok(false)
);
let before = vec![r"C:\nvm4w\nodejs".into(), desktop.into()];
assert_eq!(
conflict_before_desktop(&before, desktop, |dir| dir == r"C:\nvm4w\nodejs"),
Ok(true)
);
let after = vec![desktop.into(), r"C:\nvm4w\nodejs".into()];
assert_eq!(
conflict_before_desktop(&after, desktop, |dir| dir == r"C:\nvm4w\nodejs"),
Ok(false)
);
assert_eq!(
conflict_before_desktop(&[r"C:\other".into()], desktop, |_| false).unwrap_err(),
"machine-path-unobserved"
);
}
#[test]
fn fresh_logon_conflict_ignores_cwd_dependent_entries() {
let desktop = r"C:\Program Files\OpenCodex";
let entries = vec![
"bin".into(),
"".into(),
"%NVM_HOME%".into(),
r"C:\%NVM_HOME%\bin".into(),
r#""c:/PROGRAM FILES/OpenCodex/""#.into(),
];
let mut probed = Vec::new();
assert_eq!(
conflict_before_desktop(&entries, desktop, |dir| {
probed.push(dir.to_owned());
true
}),
Ok(false)
);
assert!(probed.is_empty());
}
#[test]
fn prepend_preserves_raw_expansions_type_and_empty_entries() {
let raw = r"%APPDATA%\npm;;C:\Tools;";
Expand Down
2 changes: 1 addition & 1 deletion desktop/src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "OpenCodex",
"version": "2.82.0",
"version": "2.83.0",
"identifier": "com.opencodex.desktop",
"build": {
"frontendDist": "../ui",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ Do not implement two slices in one B. Do not start wp1 until this research cycle

## IN / OUT

IN: this `devlog/_plan/260822_senpi_cursor_transfer/` directory.
IN: this `devlog/_fin/260822_senpi_cursor_transfer/` directory.
OUT: `src/`, `tests/`, `gui/`, `docs-site/`; senpi vendored proto copy; CLI spawn of `cursor-agent`.

## Already-have headline
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ catalog refresh introduces new models already enabled.
## Loop-spec

- Loop archetype: verifier-defined (issues exist + PR merged = done).
- Write scope: `devlog/_plan/260824_model_ux_aliases_and_defaults/**` only.
- Write scope: `devlog/_fin/260824_model_ux_aliases_and_defaults/**` only.
Out of scope: any `src/`, `gui/`, `docs-site/` change; implementation.
- External writes: 3 issues on lidge-jun/opencodex; 1 PR to `dev` + merge
(explicitly user-authorized in the request).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ live probe campaign against `cursor/grok-4.6` through the local proxy
- Goal: severity-ranked gap catalog with file:line citations + >=10 probe
evidence rows.
- Non-goals: fixing the adapter, provider config changes, issues/PRs, pushes.
- Verifier: `ls devlog/_plan/260826_cursor_responses_gap` (docs),
- Verifier: `ls devlog/_fin/260826_cursor_responses_gap` (docs),
`git status --short src/` (must be empty), probe-count grep in 010 doc.
All three run and read the change target (this unit's files / src tree).
- Stop: all three docs written and consistent; bounds = ~40min probing,
Expand Down Expand Up @@ -98,7 +98,7 @@ probes toward the >=10 minimum.
secondary references. durationMs:0 is plausibly Codex-client-side —
001 carries it as UNKNOWN with the evidence gap stated.
4. (Medium) Probe-count verifier is now concrete:
`rg -c '^\| (P|C|S)[0-9]' devlog/_plan/260826_cursor_responses_gap/010_probe_campaign.md`
`rg -c '^\| (P|C|S)[0-9]' devlog/_fin/260826_cursor_responses_gap/010_probe_campaign.md`
must report >= 10 evidence rows (pass condition), and each row carries a
pass/fail column.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ cursor-model workers (S1), plugin-surface exposure (S2), all fenced to
read-only tasks or mktemp scratch.

Verifier (from 000 plan):
`rg -c '^\| (P|C|S)[0-9]' devlog/_plan/260826_cursor_responses_gap/010_probe_campaign.md`
`rg -c '^\| (P|C|S)[0-9]' devlog/_fin/260826_cursor_responses_gap/010_probe_campaign.md`
pass condition >= 10 evidence rows.

Bounds: <=30 live probes, <=3 retries/class, ~40min wall clock.
Expand Down
Loading
Loading