feat: add Codex fast service tier toggle#780
Conversation
There was a problem hiding this comment.
Findings
- [Major] Local Codex launches never receive the selected service tier —
--service-tieris consumed intooptions.serviceTier, but the local launcher only forwards Codex CLI argv/model-reasoning config to the spawnedcodexprocess. Localhapi codex --service-tier priorityand local resume of a persisted fast tier will show/persist Fast in HAPI while the actual local Codex process runs without that tier. Evidence:cli/src/commands/codex.ts:79.
Suggested fix:// cli/src/codex/codexLocalLauncher.ts await codexLocal({ path: session.path, sessionId: resumeSessionId, modelReasoningEffort: (session.getModelReasoningEffort() ?? undefined) as ReasoningEffort | undefined, serviceTier: session.getServiceTier() ?? undefined, onSessionFound: handleSessionFound, abort: abortSignal, codexArgs, mcpServers, sessionHook: { port: hookServer.port, token: hookServer.token } }) // cli/src/codex/codexLocal.ts serviceTier?: string | null; if (opts.serviceTier) { args.push('--service-tier', opts.serviceTier); }
- [Major] Changing Codex models can leave an unsupported Fast tier stuck on the session — the new tier lookup is model-specific, but
handleModelChangeonly updatesmodel; if the priorserviceTieris stillpriorityand the new model has no matching tier, the toggle becomes disabled while StatusBar still shows Fast and subsequent turns still sendserviceTier: priority. Evidence:web/src/components/SessionChat.tsx:162.
Suggested fix:const supportsServiceTier = useCallback((modelId: string | null, tierId: string | null) => { if (!tierId) return true const selectedModel = modelId ?? codexModelsState.models.find((model) => model.isDefault)?.id ?? null const modelSummary = codexModelsState.models.find((codexModel) => codexModel.id === selectedModel) return (modelSummary?.serviceTiers ?? []).some((tier) => tier.id === tierId) }, [codexModelsState.models]) const handleModelChange = useCallback(async (model: string | null) => { try { await setModel(model) if (agentFlavor === 'codex' && !supportsServiceTier(model, props.session.serviceTier)) { await setServiceTier(null) } haptic.notification('success') props.onRefresh() } catch (e) { haptic.notification('error') console.error('Failed to set model:', e) } }, [setModel, setServiceTier, agentFlavor, supportsServiceTier, props.session.serviceTier, props.onRefresh, haptic])
Summary
Review mode: initial
Two issues found: local Codex service-tier propagation is incomplete, and model changes can leave an invalid/stale Fast tier active. No questions.
Testing
Not run (automation)
HAPI Bot
|
Thanks for the review. I pushed 490065b to fix the local launch/test coverage issue: serviceTier now propagates through codexLocalLauncher/codexLocal and the affected CLI mocks are covered by tests.\n\nFor the model-switch behavior: the current behavior is intentional for this first pass. HAPI stores serviceTier as user intent rather than an effective runtime tier; if a selected model does not advertise Fast, the UI disables the Fast control while still showing the persisted requested tier, and the app-server remains the final validator on the next turn. I can change this to clear unsupported tiers on model switch if maintainers prefer that policy. |
There was a problem hiding this comment.
Findings
- [Major] Changing Codex models can leave an unsupported Fast tier active — the Fast tier lookup is now model-specific, but the model-change path still only persists
model; an existingserviceTieris not cleared when the newly selected model has no matching Fast tier. After enabling Fast on one model and switching to an unsupported model, the toggle becomes disabled while the session still sends the stale tier on future Codex turns. Evidence:web/src/components/SessionChat.tsx:162.
Suggested fix:const supportsServiceTier = useCallback((modelId: string | null, tierId: string | null) => { if (!tierId) return true const selectedModel = modelId ?? codexModelsState.models.find((model) => model.isDefault)?.id ?? null const modelSummary = codexModelsState.models.find((codexModel) => codexModel.id === selectedModel) return (modelSummary?.serviceTiers ?? []).some((tier) => tier.id === tierId) }, [codexModelsState.models]) const handleModelChange = useCallback(async (model: string | null) => { try { await setModel(model) if (agentFlavor === 'codex' && !supportsServiceTier(model, props.session.serviceTier)) { await setServiceTier(null) } haptic.notification('success') props.onRefresh() } catch (e) { haptic.notification('error') console.error('Failed to set model:', e) } }, [setModel, setServiceTier, agentFlavor, supportsServiceTier, props.session.serviceTier, props.onRefresh, haptic])
Questions
- None.
Summary
- Review mode: follow-up after new commits
- The local Codex launcher propagation issue from the previous review was addressed. The stale service-tier-on-model-change issue remains.
Testing
- Not run (automation)
HAPI Bot
There was a problem hiding this comment.
Findings
- [Major] Codex import no longer replays existing transcript history. Evidence:
cli/src/codex/loop.ts:62.
Questions
- None.
Summary
- Review mode: follow-up after new commits
- The previous stale service-tier-on-model-change issue was addressed. One new regression remains: the PR removes the transcript replay flag used when attaching a new HAPI session to an existing Codex thread, and also removes the tests that covered that import path.
Testing
- Not run (automation)
HAPI Bot
d8626ed to
4550340
Compare
There was a problem hiding this comment.
Findings
- [Blocker] Add
serviceTierto the runtime config key union —handleSessionAlivenow callsisStaleRuntimeKeepAlive(session.id, 'serviceTier', t), butRuntimeConfigKeystill excludesserviceTier, sohubstrict TypeScript rejects the new added line andbun typecheckcannot pass. Evidence:hub/src/sync/sessionCache.ts:235.
Suggested fix:type RuntimeConfigKey = 'permissionMode' | 'model' | 'modelReasoningEffort' | 'effort' | 'serviceTier' | 'collaborationMode'
Questions
- None.
Summary
- Review mode: follow-up after new commits
- Current head still has a blocking hub type error in the service-tier keepalive path. Residual testing gap: no test currently exercises the stale keepalive guard for service-tier updates.
Testing
- Not run (automation;
bunis not installed in this runner)
HAPI Bot
There was a problem hiding this comment.
Findings
- [Major] Mark service-tier web updates as fresh runtime config —
handleSessionAlivealready rejects stale keepalives forserviceTier, butapplySessionConfignever records the update time for this new branch. After a web Fast toggle, the next oldersession-alivepacket can pass the stale guard and write the previous tier back to cache/SQLite, making the toggle flicker or silently revert. Evidence:hub/src/sync/sessionCache.ts:484.
Suggested fix:session.serviceTier = config.serviceTier this.markRuntimeConfigUpdated(sessionId, 'serviceTier', appliedAt)
Questions
- None.
Summary
- Review mode: follow-up after new commits
- The previous typecheck blocker is fixed, but the new service-tier runtime config path still misses the stale-keepalive protection used by the adjacent runtime fields.
Testing
- Not run (automation; PR content not executed per security policy)
HAPI Bot
There was a problem hiding this comment.
Findings
- No issues found.
Questions
- None.
Summary
- Review mode: follow-up after new commits
- The prior service-tier stale keepalive issue is fixed at
hub/src/sync/sessionCache.ts:485. I did not find remaining high-confidence correctness/security regressions in the current diff. Residual risk: I did not execute PR code; web behavior for the Fast toggle depends on the live Codexmodel/listshape exposingserviceTierswith a tier namedfast.
Testing
- Not run (automation; PR content not executed per security policy)
HAPI Bot
Summary
serviceTiersession config for Codex and propagate it through spawn/resume, keepalive, RPC config updates, and Codex app-server thread/turn paramsmodel/listand add a web Fast toggle for remote Codex sessionsTests
bun typecheckbun test hub/src/web/routes/sessions.test.ts hub/src/sync/sessionModel.test.tscd cli && bun test src/codex/utils/appServerConfig.test.ts