Commit Graph
10 Commits
Author SHA1 Message Date
SSU-WEI HUANGandGitHub c311afddca fix(codex): Fast mode (service tier) toggle + /fast command (closes #898) (#904)
* test: reproduce issue #898 (Codex fast mode service tier)

* fix(codex): add Fast mode (service tier) toggle and /fast command (closes #898)

* feat(codex+web): Fast mode UI toggle with full persistence

Wires the Codex Fast mode (service tier) end-to-end so it can be toggled
from the web composer and survives reload/handoff:

- shared: serviceTier on Session/SessionPatch, session-alive payload,
  resume target, and a SessionServiceTierRequest schema
- cli: AgentSessionBase carries serviceTier through keepAlive; runCodex
  syncs it to the session instance
- hub: service_tier column (schema v10 + migration), store setter,
  sessionCache + syncEngine plumbing, POST /sessions/:id/service-tier
- web: api.setServiceTier + mutation, a Fast/Standard toggle in the
  composer settings (gated to Codex GPT-5.5/5.4), and StatusBar now
  reflects the real tier instead of the effort heuristic

Refs #898

* fix(codex): preserve unset/persisted service tier on startup keepalive

Addresses HAPI Bot [Major] on PR #904: applyCurrentConfigToSession ran
setServiceTier(currentServiceTier ?? null) on wrapper-ready, collapsing the
untouched `undefined` state into explicit Standard. The immediate
setCollaborationMode keepalive then persisted serviceTier: null, silently
downgrading resumed Fast sessions and disabling account-default Fast.

- Seed currentServiceTier from the persisted session (sessionInfo.serviceTier),
  so a resumed Fast thread keeps running Fast.
- Only call setServiceTier when the tier is explicit (!== undefined), preserving
  the three-state omit semantics at the keepalive boundary.
- Add regression tests: persisted Fast is re-asserted; untouched omits the tier.

* feat(codex+web): gate Fast toggle on catalog-advertised service tier

The Fast toggle was gated on a model-name regex (gpt-5.5/5.4), which still
showed a no-op control to API-key users — Fast credits only apply with
ChatGPT login. Codex's model/list catalog advertises the service tiers
actually available for each model in the current auth/plan context, so gate
on that instead:

- cli: capture serviceTiers (ids) per model in ModelListItem + normalizeModel
- shared: CodexModelSummary.serviceTiers (flows through the existing
  getSessionCodexModels pass-through; no hub change needed)
- web: codexModelAdvertisesFastTier(sessionModel, models) replaces the regex;
  SessionChat gates the toggle on it (hidden while the catalog is
  loading/errored). The toggle now only appears when toggling it will
  actually take effect.

Refs #898

* fix(codex): make explicit Standard service tier sticky across resume

Addresses HAPI Bot [Major] (round 2): a single persisted null conflated
"untouched" with "explicit Standard". A user who turned Fast off persisted
null, but startup mapped null -> undefined (untouched) and omitted serviceTier,
so an account/thread-default Fast could silently return after restart/resume.

Introduce a distinct stored representation:
- 'fast' / 'standard' are explicit user choices; null/undefined = untouched.
- Translate 'standard' -> Codex app-server serviceTier: null ONLY when building
  thread/turn params (toAppServerServiceTier); untouched omits the field.
- /fast off now stores 'standard'; the web Standard option sends 'standard'.
- Tighten SessionServiceTierRequest to enum(['fast','standard']) so stray tier
  strings are never forwarded.

Tests: sticky-Standard-on-resume regression; turn/thread params translate
'standard'->null and omit on untouched; hub route applies fast/standard and
rejects unsupported values + local sessions.

Refs #898

* fix(codex): recognize real Fast tier (id 'priority', name 'Fast') in catalog gate

Live E2E against an authed Codex session revealed the model catalog advertises
the Fast tier with id 'priority' and display name 'Fast' (not id 'fast'), so the
/fast/i gate — which only saw tier ids — wrongly hid the toggle for valid
ChatGPT users on gpt-5.5/gpt-5.4. Capture both the tier id and name as
lowercased tokens so the existing name-based match recognizes 'Fast'. The sent
value stays 'fast' (the documented service_tier value / raw additionalSpeedTiers
request tier). Verified end-to-end: gpt-5.5/gpt-5.4 gate on, gpt-5.4-mini off.

Refs #898

* fix(codex): preserve service tier across session resume

Resuming a Codex session spawns a fresh session (serviceTier null) and merges
the old one in. Unlike model/effort/permissionMode, serviceTier was neither
threaded through the resume spawn nor preserved in mergeSessionData, so a
resumed Fast (or explicit Standard) session silently reverted to the account
default.

Thread serviceTier through the spawn path like its siblings:
- hub: resumeSession passes session.serviceTier to spawnSession; rpcGateway +
  syncEngine carry it in the spawn RPC payload; mergeSessionData preserves it
  old->new (safety net).
- cli: SpawnSessionOptions.serviceTier; apiMachine forwards it; buildCliArgs
  emits --service-tier for codex; the codex command parses it; runCodex seeds
  currentServiceTier from the spawn override first (opts.serviceTier ??
  sessionInfo.serviceTier), so a resumed thread immediately runs the right tier.

Verified end-to-end: set Fast -> kill process -> reopen -> resumed session (new
id) still runs Fast. Tests: buildCliArgs --service-tier (codex only), runCodex
spawn-override seed, mergeSessionData service-tier preservation.

Refs #898

* fix(codex): send advertised 'priority' tier id for Fast, not 'fast'

The model catalog advertises the Fast tier with request id 'priority' (display
name 'Fast'), and OpenAI docs confirm service_tier='fast' maps to the request
value 'priority'. The app-server serviceTier override is a raw request value
that does not validate unknown strings (a live probe accepted 'bogus-xyz'), so
sending 'fast' risks being silently ignored — no Fast applied.

Translate the stored 'fast' state to app-server 'priority' at the thread/turn
param boundary (toAppServerServiceTier); the stored/UI/command representation
stays 'fast'/'standard'. Verified live: a turn with serviceTier='priority' runs
and consumes the Fast-tier rate budget.

Addresses HAPI Bot [Major]. Refs #898

* fix(codex): validate --service-tier CLI value (fast|standard)

Addresses HAPI Bot [Minor]: the internal --service-tier spawn arg accepted any
non-empty string, unlike the web /service-tier enum, so a malformed value could
be seeded into currentServiceTier and persisted via keepalive. Parse it to
'fast'|'standard' and reject anything else, matching the web endpoint.

Refs #898
2026-06-17 10:27:33 +08:00
weishu d87b6c6996 guard local codex startup by version 2026-04-24 11:17:39 +08:00
Junmo KimandGitHub e6eaff83c5 fix(hub,cli): forward permissionMode on session resume (#460)
* feat(hub,cli): forward permissionMode on session resume

When a session is resumed, the cached permissionMode is now forwarded
through the Hub → Runner → CLI pipeline via a new --permission-mode
flag. Previously the mode was lost on resume, resetting to 'default'.

Each CLI flavor validates the flag value against its own allowed
permission modes (e.g. CLAUDE_PERMISSION_MODES) and rejects unknown
values. The existing --yolo flag is preserved as a shorthand.

* refactor(cli): extract buildCliArgs from startRunner

Extract the CLI argument construction logic into a standalone
exported function so it can be unit-tested independently.
No behavior change.

* test(cli): add buildCliArgs unit tests for --permission-mode

Verify that the runner correctly forwards valid permission modes
via --permission-mode, rejects invalid values, and falls back to
--yolo when no permission mode is set.

* fix(cli): let --permission-mode take precedence over --yolo

When both flags are present, --permission-mode was silently
overwritten by a later --yolo. Guard legacy flag branches with
a hasExplicitPermissionMode check so the explicit flag wins.
2026-04-15 11:13:21 +08:00
weishu 304789620b Fix reason effort setting not working. close #346, close #333 2026-03-24 12:04:52 +08:00
ROOOOandGitHub cb09f0b898 feat: add codex reasoning effort option (#297) 2026-03-17 22:55:55 +08:00
weishu a41d6aa662 feat: add --model parameter support to command handlers
Pass model selection through command pipeline to enable model switching during runtime

close #83, close #64
2026-01-23 11:55:40 +08:00
weishu 0228146b99 refactor: rename daemon to runner throughout codebase 2026-01-19 11:12:48 +08:00
weishu 75de3cd1cb feat: add session resume capability via 'codex resume' command
close #51
2026-01-13 12:29:30 +08:00
weishu 7a487269ea refactor: replace hardcoded permission/model modes with centralized schema validation
Extract permission and model mode validation into reusable functions utilizing
@hapi/protocol schemas. Use isPermissionModeAllowedForFlavor and
isModelModeAllowedForFlavor to validate modes based on session flavor (claude/codex).
Replace inline type definitions with shared types from @hapi/protocol.
2026-01-04 22:07:03 +08:00
weishu aa0c3f2f0d refactor(cli): split command routing and modularize RPC handlers
Restructure CLI entry point and RPC handler registration to improve maintainability:

- Introduce command registry pattern with CommandDefinition interface for consistent command structure
- Extract individual command modules (claude, codex, daemon, doctor, gemini, auth, connect, etc.)
- Move RPC handlers into separate feature modules (bash, files, git, directories, ripgrep, difftastic, slashCommands)
- Create shared RPC types and response helpers (rpcTypes.ts, rpcResponses.ts)
- Move SpawnSessionOptions and SpawnSessionResult to rpcTypes for proper type organization
- Simplify index.ts to minimal entrypoint using command registry
- Update imports in apiMachine.ts and daemon/run.ts to use rpcTypes module

This enables:
- Better code organization by feature/command
- Easier testing of individual commands
- Safer RPC handler registration without side effects
- Clearer separation of concerns between routing and business logic
2026-01-03 17:29:05 +08:00