Commit Graph

10 Commits

Author SHA1 Message Date
Frank Song 91f99d8194 fix(oauth): serialize Anthropic env fallback reads 2026-05-07 02:47:19 +00:00
Michael Lam e509faec44 feat: link Claude Code OAuth in onboarding 2026-05-06 06:26:43 +00:00
Nathan Esquenazi b34ce63c97 fix(oauth): honor cancel during Codex device-token exchange (follow-up to #1652)
The Codex OAuth onboarding worker introduced in #1652 had a cancel-vs-worker
race: a `cancel_onboarding_oauth_flow` request that arrived while the worker
was mid-network-call (between the `live = dict(...)` snapshot and the next
status check) would be silently overridden:

  1. User clicks Cancel → server sets flow.status = "cancelled" and drops
     sensitive lifecycle fields under the lock.
  2. Worker is mid-`_poll_codex_authorization` / `_exchange_codex_authorization`
     using the local `live` snapshot it captured before the cancel.
  3. Worker calls `_persist_codex_credentials(...)` — auth.json gets written.
  4. Worker calls `_set_flow_status(flow_id, "success")` — overrides the
     cancelled status.

Net effect: the user's explicit cancel is ignored, credentials are persisted,
and the UI reports success. Reproduced with a behavioural harness that drove
a real worker thread against patched network helpers and confirmed:

  pre-fix : flow status `success`, auth.json written despite cancel
  post-fix: flow status `cancelled`, auth.json NOT written

The fix re-checks the flow status under `_OAUTH_FLOWS_LOCK` after the token
exchange completes and before persisting. If the status is no longer
`pending`, the worker exits without persisting credentials and without
overwriting the terminal status.

Regression test `test_cancel_during_token_exchange_does_not_persist_credentials`
drives the worker against threading.Event-gated network stubs to reproduce
the race deterministically and lock the new invariant.

Trace verified against fresh hermes-agent tarball — credential_pool entry
shape (`auth_type=oauth`, `source=manual:device_code`, `priority=0`, base_url)
remains compatible with `agent.credential_pool.load_pool("openai-codex")` and
the agent CLI's `_save_codex_tokens` legacy fallback path.

Tests:
- 10/10 in tests/test_issue1362_codex_oauth_onboarding.py
- Full suite: 4230 passed, 57 skipped, 3 xpassed, 0 failed in 33.82s

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 14:49:38 -07:00
Hermes Agent db54dc594e chore(release): stamp v0.50.296 — 3-PR batch + Opus pass + 2 follow-ups absorbed
Constituent PRs (all by @Michaelyklam):
  #1640 — show TPS in assistant message headers (closes #1617) — Aaron UX APPROVED
  #1648 — session save mode config (closes #1406)
  #1650 — Codex OAuth onboarding flow (refs #1362)

Opus advisor SHIP verdict on stage-296. 14-question audit passed including
focused OAuth security review on #1650. Two minor follow-ups absorbed:
- _get_active_hermes_home() exception fallback now logs warning
- Codex credential pool find-loop accepts both legacy and current source values

#1640 has @aronprins UX gate APPROVED (default-off TPS toggle in Preferences).
#1650 ships first in-app OAuth flow — server-owned device-code lifecycle,
profile-scoped credential storage, atomic chmod-before-rename writes.

4255 → 4284 tests passing (+29).
2026-05-04 21:38:26 +00:00
Michael Lam 259c5c4afb feat: add Codex OAuth onboarding flow 2026-05-04 14:07:16 -07:00
bergeouss 8fe593fa38 feat: silent credential self-heal on 401 errors (#1401) 2026-05-03 18:32:53 +00:00
nesquena-hermes f8007d43f3 v0.50.257: 4 Opus pre-release follow-ups + CHANGELOG + test fixes for #1415
stage-257 batch (PRs #1402 + #1415):

Opus pre-release advisor caught 4 issues in stage-257:

1. MUST-FIX (security): api/oauth.py::_write_auth_json — tmp.replace()
   preserves the temp file umask (0644 default), so OAuth access/refresh
   tokens landed world-readable on shared systems. Fix: tmp.chmod(0o600)
   BEFORE rename, with try/except OSError that warns but does not abort.

2. SHOULD-FIX: _handle_cron_history and _handle_cron_run_detail accepted
   job_id as a path component without validation. Mirrors the rollback
   path-traversal vector caught in v0.50.255 (#1405). Path() / .. does NOT
   normalize. New regex ^[A-Za-z0-9_-][A-Za-z0-9_.-]{0,63}$ with explicit
   . / .. rejection.

3. SHOULD-FIX: _handle_cron_history int(offset)/int(limit) raised
   ValueError on malformed input → confusing 500. Now try/except + clamp
   to (max(0, offset), max(1, min(500, limit))).

4. NIT: same regex applied to _handle_cron_run_detail (defense-in-depth
   even though path-resolve check would catch it downstream).

PR #1415 follow-up: 8 pre-existing tests in test_issue1106 and
test_custom_provider_display_name asserted bare model IDs but #1415
changes named-custom-provider IDs to @custom:NAME:model form when active
provider differs. Tests updated to use _strip_at_prefix helper to keep
checking the same invariant in the new shape.

4 regression tests in test_v050257_opus_followups.py + 8 fixed pre-existing
tests. Full suite: 3602 passed, 0 failed.
2026-05-01 18:30:41 +00:00
bergeouss 3bff26037f fix: improve auth.json warning message and prevent credential ID collision
- Include file path and exception details in _read_auth_json warning log
- Add retry-on-collision (up to 3 attempts) for credential UUID generation

Addresses PR #1402 review feedback points 1 and 2.
2026-05-01 15:46:50 +00:00
bergeouss f4bfd9dca7 fix: address PR #1402 review feedback — cron sort, path traversal, OAuth robustness
- Cron history: sort by mtime instead of lexicographic filename (more robust)
- Path traversal: use resolve() + is_relative_to() instead of brittle string checks
- _cron_output_snippet: document the contract for response heading extraction
- _read_auth_json: catch JSONDecodeError specifically, log warning instead of silent swallow
- OAuth timestamps: use ISO strings consistently (created_at, updated_at)
- Credential id: use uuid4 instead of time-based truncated int (collision-safe)
2026-05-01 13:38:14 +00:00
bergeouss 8ae198e88c feat: P2 improvements — cron history, toolsets per session, Codex OAuth
- #468: Cron run history — GET /api/crons/history (metadata listing)
  + GET /api/crons/run (full output), lazy-load on click in Tasks panel
- #493: Per-session toolset override — Session.enabled_toolsets field,
  POST /api/session/toolsets endpoint, streaming handler override,
  composer chip UI with dropdown (matches reasoning chip pattern)
- #1362: In-app Codex OAuth — device-code flow (stdlib only, no httpx),
  SSE polling endpoint, onboarding wizard login button
- #1240: Design proposal comment for provider/model source-of-truth
2026-05-01 12:42:21 +00:00