* fix(platform): support live system proxy changes
* fix(platform): support live system proxy changes
Generated-By: looper 0.9.1 (runner=fixer, agent=opencode)
* fix(platform): support live system proxy changes
Generated-By: looper 0.9.1 (runner=fixer, agent=opencode)
* fix(platform): support live system proxy changes
Honor lowercase proxy env vars within a single source before merging proxy-aware envs.\n\nGenerated-By: looper 0.9.1 (runner=fixer, agent=opencode)
* fix(platform): support live system proxy changes
Generated-By: looper 0.9.1 (runner=fixer, agent=opencode)
* fix(platform): support live system proxy changes
Generated-By: looper 0.9.1 (runner=fixer, agent=opencode)
* fix(platform): support live system proxy changes
Generated-By: looper 0.9.1 (runner=fixer, agent=opencode)
* fix(platform): support live system proxy changes
Generated-By: looper 0.9.1 (runner=fixer, agent=opencode)
* fix(platform): support live system proxy changes
Generated-By: looper 0.9.1 (runner=fixer, agent=opencode)
* fix(platform): support live system proxy changes
Generated-By: looper 0.9.1 (runner=fixer, agent=opencode)
* fix(platform): support live system proxy changes
Generated-By: looper 0.9.1 (runner=fixer, agent=opencode)
* fix(platform): support live system proxy changes
Generated-By: looper 0.9.1 (runner=fixer, agent=opencode)
* fix(platform): support live system proxy changes
Generated-By: looper 0.9.1 (runner=fixer, agent=opencode)
* fix(platform): support live system proxy changes
Generated-By: looper 0.9.1 (runner=fixer, agent=opencode)
* fix(platform): support live system proxy changes
Generated-By: looper 0.9.1 (runner=fixer, agent=opencode)
* fix(platform): support live system proxy changes
Generated-By: looper 0.9.1 (runner=fixer, agent=opencode)
* fix(platform): support live system proxy changes
Generated-By: looper 0.9.1 (runner=fixer, agent=opencode)
* fix(platform): support live system proxy changes
Generated-By: looper 0.9.1 (runner=fixer, agent=opencode)
* fix(platform): support live system proxy changes
Refresh provider request proxy env on each dispatcher creation and cover it with a focused regression test.
Generated-By: looper 0.9.1 (runner=fixer, agent=opencode)
* fix(platform): support live system proxy changes
Generated-By: looper 0.9.1 (runner=fixer, agent=opencode)
* fix(platform): support live system proxy changes
Generated-By: looper 0.9.1 (runner=fixer, agent=opencode)
* fix(platform): support live system proxy changes
Generated-By: looper 0.9.1 (runner=fixer, agent=opencode)
* fix(platform): support live system proxy changes
Generated-By: looper 0.9.1 (runner=fixer, agent=opencode)
* fix(platform): support live system proxy changes
Generated-By: looper 0.9.1 (runner=fixer, agent=opencode)
* fix(platform): support live system proxy changes
Generated-By: looper 0.9.1 (runner=fixer, agent=opencode)
* fix(platform): support live system proxy changes
Generated-By: looper 0.9.1 (runner=fixer, agent=opencode)
* fix(platform): support live system proxy changes
Generated-By: looper 0.9.1 (runner=fixer, agent=opencode)
* fix(platform): support live system proxy changes
Generated-By: looper 0.9.1 (runner=fixer, agent=opencode)
* fix(platform): support live system proxy changes
Generated-By: looper 0.9.1 (runner=fixer, agent=opencode)
* fix(platform): support live system proxy changes
Generated-By: looper 0.9.1 (runner=fixer, agent=opencode)
* fix(platform): support live system proxy changes
Generated-By: looper 0.9.1 (runner=fixer, agent=opencode)
* fix(platform): support live system proxy changes
Generated-By: looper 0.9.1 (runner=fixer, agent=opencode)
* fix(platform): support live system proxy changes
Generated-By: looper 0.9.1 (runner=fixer, agent=opencode)
* fix(platform): support live system proxy changes
Generated-By: looper 0.9.1 (runner=fixer, agent=opencode)
* fix(platform): support live system proxy changes
Generated-By: looper 0.9.1 (runner=fixer, agent=opencode)
* fix(platform): enable node env proxy for user proxy vars
* fix(platform): support live system proxy changes
Generated-By: looper 0.9.1 (runner=fixer, agent=opencode)
* fix(platform): support live system proxy changes
Generated-By: looper 0.9.1 (runner=fixer, agent=opencode)
* fix(platform): support live system proxy changes
Generated-By: looper 0.9.1 (runner=fixer, agent=opencode)
* feat(daemon): attach structured diagnostics to agent connection test results
Local agent connection-test failures currently flatten everything into
a single free-form `detail` string (e.g. "exit 1"). Settings UI and CLI
consumers can't tell what phase failed, which binary the daemon picked,
or what the child's exit metadata looked like — they have to scrape the
human-readable text.
Add an optional `diagnostics` block on the connection-test response so
callers can read structured fields instead. The existing `kind` and
`detail` strings are kept bit-for-bit identical, so older UIs keep
rendering unchanged.
- packages/contracts: add `ConnectionTestPhase`
(binary_resolution / version_probe / model_list / spawn /
connection_smoke_test / output_parse) and a `ConnectionTestDiagnostics`
interface with optional `binaryPath`, `binaryVersion`, `exitCode`,
`signal`, `stdoutTail`, `stderrTail`; extend
`ConnectionTestResponse.diagnostics?` to carry it.
- apps/daemon/connectionTest.ts: thread a `phase` tracker through
testAgentConnectionInternal, flip it at the meaningful boundaries
(binary_resolution → spawn → connection_smoke_test / output_parse),
and stamp diagnostics into every result return point — the four
result helpers plus both early returns. Tail data already buffered
by `createAgentSink` is reused; nothing new is captured.
- tests: three regressions per #2248 — success path attaches
phase='connection_smoke_test' + exitCode 0, exit-failed path
attaches phase='spawn' + the failing exitCode + the stderr tail,
and a missing-CLI path attaches an early-phase diagnostics block.
This is PR 1 of the #2248 plan (contracts + minimum daemon fill);
follow-ups will introduce a normalized failure classifier
(binary_not_found, unsupported_version, auth_failed, quota_exceeded,
network_failed, unsupported_flags, no_text_output, output_parse_failed,
spawn_failed), candidate-alternative reporting via
inspectAgentExecutableResolution, and the Settings "View details"
disclosure.
Refs #2248.
* fix(connectionTest): honor diagnostics contract on all local return paths
Two follow-ups from review of #2419:
- packages/contracts/src/api/connectionTest.ts advertises diagnostics
as 'Always set on local agent test responses', but three local
returns still bypassed buildDiagnostics(): the buildArgs failure
around 1295, the preflight probeAgentAuthStatus().status === 'missing'
branch around 1317, and the outer catch around 1566. Thread
buildDiagnostics() through all three; phase is still 'binary_resolution'
at the first two and whatever the runtime advanced to at the catch.
- resultFromAgentText() hard-coded exitCode: 0 even though
resultFromChildExit() routes ACP clean-SIGTERM completion through
this success helper (winner.code === null, winner.signal ===
'SIGTERM' with acpCleanCompletion). Add an optional exit argument
threaded from both call sites so the diagnostics reflect the actual
child code/signal pair instead of a synthesized 0 that masks the
SIGTERM teardown. Only synthesize 0 when no exit context is
available (theoretical text-without-exit path).
Tests:
- regression locking the diagnostics contract for the preflight auth
path on Cursor Agent (phase: binary_resolution, binaryPath set)
* docs(contracts): widen diagnostics contract to match early-failure paths
Reviewer flagged that the JSDoc-style comment on
ConnectionTestResponse.diagnostics still said 'Populated only when the
test actually spawned an agent CLI', but the previous follow-up made
the daemon stamp diagnostics on three pre-spawn local-agent failures
too: the unknown-agent and unresolved-binary branches around
connectionTest.ts:1123-1148 and the preflight auth return around
1338-1353. Reword the contract so Settings/CLI consumers do not
incorrectly special-case those early local failures as
diagnostics === undefined.
* fix(connectionTest): keep contracts browser-safe and fold probe output into preflight diagnostics
Two follow-ups from review of #2419:
- ConnectionTestDiagnostics.signal was typed as
`NodeJS.Signals | string | null`, which made the generated .d.ts of
the shared @open-design/contracts surface depend on ambient Node
types. Downstream consumers reading a plain HTTP response shape
should not need @types/node. Narrow to `string | null` (NodeJS.Signals
literals are strings, so the daemon write site is unchanged) and
document the boundary in the field comment.
- The Cursor-style preflight auth path stamped diagnostics built from
the smoke-test sink, which is always empty at that point because the
smoke spawn never happened. As a result the diagnostics block
silently dropped `cursor-agent status`'s own stderr/stdout/exit
context — the only structured failure information available on that
path. Thread the probe output back out of probeAgentAuthStatus()
via new optional stdoutTail/stderrTail/exitCode/signal fields, then
merge them into the diagnostics overrides in connectionTest.ts so
Settings/CLI consumers can render the auth-failure context instead
of just the guidance string.
Tests:
- extended the Cursor preflight regression to assert that diagnostics
carries the probe's stderr ("Not logged in") and exit code (1).
Mirror the issue #398 fix the claude adapter already has: when
spawning Codex CLI without a custom OPENAI_BASE_URL, strip both
OPENAI_API_KEY and CODEX_API_KEY from the child env so Codex CLI's
own `~/.codex/auth.json` (codex login) wins.
Without this guard, a stale BYOK key left behind in
`agentCliEnv.codex.OPENAI_API_KEY` (e.g. after the user clears the
BYOK dialog and switches execution mode back to Local CLI) silently
flows through `spawnEnvForAgent` and trips 401 invalid_api_key.
The stripping is gated on OPENAI_BASE_URL so users who intentionally
route Codex CLI through a third-party OpenAI-compatible gateway keep
the credential that authenticates against it. Comparison is
case-insensitive to close the Windows mixed-case env name hole that
issue #398 already documents for ANTHROPIC_API_KEY.
Fixes#2420
* fix(security): resolve hostname before approving external API base URLs
Before this change the daemon-side base-URL validator only inspected the
literal hostname string. A public DNS record that points at an internal
address ('internal.example.com' → 10.0.0.5) passed validation, and the
daemon would issue the upstream request anyway — turning the BYOK proxy
into an SSRF primitive against internal infrastructure.
Add a small companion ('validateBaseUrlResolved') that runs the existing
sync check, resolves the hostname with 'dns.lookup({ all: true })', and
re-applies the block-list against every resolved address. Wire it into
the wrapper the daemon already uses ('validateExternalApiBaseUrl'), so
every proxy/finalize handler picks it up without further edits.
Carve-outs match the existing sync validator:
- Loopback hostnames skip DNS (Ollama-style local LLMs still work,
including '*.localhost' / 'lvh.me'-style names that resolve to 127.0.0.1
per RFC 6761).
- IP literals are already vetted by the sync pass; no need to re-resolve.
- DNS resolver errors fall through to the existing fetch error path —
a transient ENOTFOUND should not turn into a 403.
The 6 callers that previously consumed the sync result now 'await' the
async wrapper. All call sites are already inside async route handlers.
Vitest coverage in apps/daemon/tests/connection-test.test.ts covers:
sync rejection passthrough, loopback / IP-literal short-circuits,
private IPv4 and IPv6 resolution, dual-stack with one private record,
public→public passes (api.openai.com), '*.localhost' resolved→loopback,
and resolver-error fallback.
Detected by: aeon (manual review + semgrep + osv + trufflehog).
Class: CWE-918 (SSRF) — DNS-based bypass.
* fix(security): cover remaining daemon baseUrl fetch surfaces + Ollama redirects
Addresses PR #1176 review feedback (lefarcen / mrcfps): the resolved-IP
wrapper only covered the proxy/finalize routes, leaving three adjacent
SSRF gaps open.
- testProviderConnection (/api/test/connection provider mode): switch
from sync validateBaseUrl to await validateBaseUrlResolved so a
hostname that resolves to a private IP is rejected before the daemon
POSTs the smoke prompt upstream.
- listProviderModels (/api/provider/models): same swap. Import the
DNS-aware helper from ./connectionTest.js since it carries the dns
binding the daemon owns; contracts stays pure.
- Ollama proxy stream fetch: align with the other four proxy routes
by setting redirect: 'error', so a validated public host cannot 3xx
the daemon to a private/internal URL after the pre-fetch check.
Regression coverage:
- POST /api/provider/models — DNS spy returns 10.0.0.5 for a synthetic
hostname; route must respond { ok: false, kind: 'forbidden' } and
must not invoke upstream fetch.
- POST /api/test/connection provider mode — same shape.
- /api/proxy/ollama/stream — fetch mock asserts redirect: 'error' on
the upstream Ollama call.
The existing /api/provider/models timeout test now stubs dnsPromises
so it doesn't race the probe timer against real DNS.
---------
Co-authored-by: aeon <aeon@aaronjmars.com>
* feat(daemon): make connection-test timeouts configurable
Provider and agent connection tests had hardcoded 12s / 45s budgets,
which are too tight for slow networks or distant providers (the user
sees "timeout" in Settings with no way to extend the budget).
- Add OD_CONNECTION_TEST_PROVIDER_TIMEOUT_MS (default 12_000)
- Add OD_CONNECTION_TEST_AGENT_TIMEOUT_MS (default 45_000)
- Invalid values (non-numeric, zero, negative, fractional) emit a
console.warn and fall back to the default, so a typo in the env
never silently disables the safety timeout.
- Export resolveConnectionTestTimeoutMs for unit testing; cover the
three resolution paths (fallback / honored override / invalid).
41 connection-test tests pass (+3 new), full daemon suite 1170/1170.
* fix(daemon): reject connection-test timeout overrides above Node's setTimeout maximum
Node's `setTimeout` silently clamps any delay above `2^31-1` ms
(2_147_483_647) to ~1 ms with a TimeoutOverflowWarning. The previous
`Number.isInteger(n) && n >= 1` check accepted oversized values
unchanged and passed them straight to `setTimeout`, so an override
that *intended* to raise the budget — e.g.
`OD_CONNECTION_TEST_AGENT_TIMEOUT_MS=3000000000` — instead caused
every connection test to fail almost immediately. The safety
timeout was effectively disarmed.
Add `MAX_CONNECTION_TEST_TIMEOUT_MS = 2_147_483_647` and switch the
guard to `Number.isSafeInteger(n) && n >= 1 && n <= MAX...`. The
boundary value is still accepted; one millisecond past it falls
back with a warn. Regression test exercises `3_000_000_000`,
`2_147_483_647`, and `2_147_483_648`.
Addresses #1222 review feedback from @chatgpt-codex-connector,
@mrcfps, and @lefarcen.
* Optimize Windows packaged web output
* Fix packaged contracts runtime build
* Optimize Windows packaged size pruning
* Prune Windows root Next payload
* Remove Windows bundled Node runtime
* Prune Windows standalone duplicate Next
* Add tools-pack cache foundation
* Cache Windows packaged build layers
* Cache Windows workspace builds
* Cache Electron-ready Windows app
* Split Windows tools-pack module
* Cache Windows dir build outputs
* Split Windows pack build modules
* Document Windows NSIS smoke namespace limits
* Move Windows NSIS smoke note to agents guide
* Optimize Windows beta packaging
* Bump packaged beta base version
* Improve Windows installer namespace UX
* Improve Windows tools-pack cache keys
* Stabilize Windows beta cache version keys
* Cache Windows workspace build outputs
* Optimize windows release beta cache layers
* Cache windows release dependencies
* Trim windows release cache before save
* Refresh windows tools-pack cache key
* Improve windows installer preflight prompts
* Fallback NSIS installer strings to English
* Fix Windows installer cleanup and preflight
* Improve Windows NSIS state logging
* Fix system NSIS Persian language alias
* Use long-path removal for Windows uninstall
* Fix mac tools-pack tests on Windows
* Address Windows packaging review feedback
* Fix Windows installer cache namespace isolation
* Include web output mode in Windows tarball cache key
* Use unique Windows release cache save keys
* Support overriding the Codex executable path
* Replace save-as-template prompts with an in-app dialog
* Seed local packaged app config from workspace
* Fix packaged config and connection test overrides
* Keep tools-pack mac config seeding self-contained
* Require absolute CODEX_BIN overrides
* feat(settings): add connection test for providers and CLI agents
Adds a "Test" action in the Settings dialog that verifies the configured
provider (Anthropic/OpenAI/Azure/Google) or CLI agent without sending a
real chat. Backed by a new daemon endpoint and shared contracts, with
categorized inline statuses and i18n strings across all supported locales.
* fix(settings): address connection test review feedback
* fix(daemon): pass empty MCP servers for connection probes
* fix(connection-test): address review blockers
* fix(daemon): fail json stream runs on structured errors
* fix(contracts): build connection test subpath export
* Use draft CLI env in agent connection tests
* fix(i18n): add fallback ids for new curated content