feat(codex): expose personality controls#1009
Conversation
There was a problem hiding this comment.
Findings
-
[Major] Restored personality is dropped before Codex sees it —
runCodexnow seedscurrentPersonalityfromsessionInfo.personality, but the CLI API mapper still returns only throughcollaborationMode, so the parsed hub field is discarded before this added line uses it. Resuming an existing Codex session therefore starts with the default personality even when the hub session hasfriendly/pragmatic/none. Evidence:cli/src/codex/runCodex.ts:98, related mapper contextcli/src/api/api.ts:83andcli/src/api/api.ts:133.
Suggested fix:return { ..., collaborationMode: raw.collaborationMode, personality: raw.personality }
-
[Major] Keepalive personality updates are not broadcast to web clients — this branch mutates
session.personality, but thesession-updatedpatch emitted below still stops atcollaborationMode. Since/personality ...is handled locally and only pushes keepalive, the selector stays stale until a full refetch. Evidence:hub/src/sync/sessionCache.ts:253and omitted patch field athub/src/sync/sessionCache.ts:283.
Suggested fix:data: { active: true, activeAt: session.activeAt, thinking: session.thinking, permissionMode: session.permissionMode, model: session.model, modelReasoningEffort: session.modelReasoningEffort, effort: session.effort, serviceTier: session.serviceTier, collaborationMode: session.collaborationMode, personality: session.personality } satisfies SessionPatch
Questions
- None.
Summary
- Review mode: initial
- Two runtime gaps found: resume drops personality before Codex receives it, and keepalive broadcasts omit personality so web state can become stale.
Testing
- Not run (automation):
bun typecheckfailed becausebunis not on PATH in this environment.git diff --checkpassed.
HAPI Bot
There was a problem hiding this comment.
Findings
- [Minor] Personality selector can render stale state — the memoized overlay now reads
personalityandonPersonalityChange, but the dependency list was not updated. After changing personality, the session refetch/SSE can updateprops.session.personalitywhile the open menu keeps showing the old selected option because the overlay memo does not recompute. Evidence:web/src/components/AssistantChat/HappyComposer.tsx:923.
Suggested fix:handleServiceTierChange, handleSuggestionSelect, personality, onPersonalityChange, t
Questions
- None.
Summary
- Review mode: follow-up after new commits
- One minor UI state issue found in the new personality menu. The two previous bot findings appear addressed in the follow-up diff.
Testing
- Not run (automation):
bunis not available on PATH, so targeted tests andbun typecheckcould not run. git diff --checkpassed.
HAPI Bot
There was a problem hiding this comment.
Findings
- None.
Questions
- None.
Summary
- Review mode: follow-up after new commits
- No issues found in the current diff. The previous HAPI Bot memo-dependency finding is addressed in the follow-up commit. Residual risk: I could not run the Bun-based test/typecheck commands in this runner.
Testing
- Not run (automation):
bunis not available on PATH, sobun typecheckand targeted tests could not run.
HAPI Bot
There was a problem hiding this comment.
Findings
- [Major] Personality is dropped when an inactive Codex session is resumed from the web — the new
personalityconfig is accepted and cached for active sessions, butresumeSession()still seeds the runner only with model/effort/permission/service tier. After a user selectsfriendly/pragmatic, lets the session end, then resumes it remotely, the spawnedhapi codex resume ...process starts without that personality and the first resumed turn runs with the default. Evidence:hub/src/sync/syncEngine.ts:677adds personality to the config surface, but the resume spawn path still has no personality argument/payload.
Suggested fix:// hub/src/sync/syncEngine.ts await this.rpcGateway.spawnSession( targetMachine.id, directory, flavor, session.model ?? undefined, session.modelReasoningEffort ?? undefined, undefined, undefined, undefined, resumeToken, session.effort ?? undefined, preferredPermissionMode, session.serviceTier ?? undefined, session.personality ?? undefined ) // also thread `personality` through RpcGateway.spawnSession -> SpawnHappySession // -> cli SpawnSessionOptions -> buildCliArgs(`--personality`, value) -> codexCommand -> runCodex.
Questions
- None.
Summary
- Review mode: follow-up after new commits
- Found one resume-path regression in the current full diff. The live active-session RPC path has tests, but the remote resume/runner spawn path needs coverage for carrying personality into the new Codex process.
Testing
- Not run (automation): review only; did not execute PR code.
HAPI Bot
| effort?: string | null | ||
| serviceTier?: string | null | ||
| collaborationMode?: CodexCollaborationMode | ||
| personality?: Session['personality'] |
There was a problem hiding this comment.
[Major] Personality is dropped when an inactive Codex session is resumed from the web. This diff adds personality to the session config surface, but the resume spawn path still seeds the runner only with model/effort/permission/service tier. A user can select friendly/pragmatic, let the session end, and then resume it remotely; the spawned hapi codex resume ... process receives no personality, so the first resumed turn runs with the default.
Suggested fix:
// hub/src/sync/syncEngine.ts
await this.rpcGateway.spawnSession(
targetMachine.id,
directory,
flavor,
session.model ?? undefined,
session.modelReasoningEffort ?? undefined,
undefined,
undefined,
undefined,
resumeToken,
session.effort ?? undefined,
preferredPermissionMode,
session.serviceTier ?? undefined,
session.personality ?? undefined
)
// also thread `personality` through RpcGateway.spawnSession -> SpawnHappySession
// -> cli SpawnSessionOptions -> buildCliArgs(`--personality`, value) -> codexCommand -> runCodex.There was a problem hiding this comment.
Findings
- [Major] Personality selection is lost across hub restart — the diff adds
personalityto runtime session config, butapplySessionConfigonly mutates the in-memorySession. There is no SQLite column/session-store setter in this PR, andrefreshSession()rebuilds sessions from stored fields while carryingpersonalityonly from an existing cache entry. A user who selectsfriendly/pragmatic, lets the session go inactive, restarts the hub, then resumes the session will spawn Codex without the selected personality. Evidence:hub/src/sync/sessionCache.ts:525.
Suggested fix:// hub/src/store/index.ts const SCHEMA_VERSION: number = 11 // add 10: () => this.migrateFromV10ToV11() private migrateFromV10ToV11(): void { const columns = this.getSessionColumnNames() if (columns.size === 0) return if (!columns.has('personality')) { this.db.exec('ALTER TABLE sessions ADD COLUMN personality TEXT') } } // include personality TEXT in createSchema(), StoredSession, DbSessionRow, and toStoredSession(). // hub/src/store/sessions.ts export function setSessionPersonality(db: Database, id: string, personality: string | null, namespace: string, options?: { touchUpdatedAt?: boolean }): boolean { const now = Date.now() const touchUpdatedAt = options?.touchUpdatedAt === true const result = db.prepare(` UPDATE sessions SET personality = @personality, updated_at = CASE WHEN @touch_updated_at = 1 THEN @updated_at ELSE updated_at END, seq = seq + 1 WHERE id = @id AND namespace = @namespace AND personality IS NOT @personality `).run({ id, namespace, personality, updated_at: now, touch_updated_at: touchUpdatedAt ? 1 : 0 }) return result.changes === 1 }
Questions
- None.
Summary
- Review mode: follow-up after new commits
- Found one persistence gap in the current full diff. The earlier resume-spawn threading issue is resolved, but the selected personality still does not survive a hub restart before inactive resume.
Testing
- Not run (automation): review only; did not execute PR code.
HAPI Bot
| this.markRuntimeConfigUpdated(sessionId, 'collaborationMode', appliedAt) | ||
| } | ||
| if (config.personality !== undefined) { | ||
| session.personality = config.personality |
There was a problem hiding this comment.
[Major] Personality selection is lost across hub restart. This added branch updates only the in-memory session, while the store schema/types/mappers have no personality field and refreshSession() can only carry it from an existing cache entry. After selecting friendly/pragmatic, ending the session, restarting the hub, and resuming, resumeSession() sees session.personality as undefined and spawns Codex without the selected personality.
Suggested fix:
// hub/src/store/index.ts
const SCHEMA_VERSION: number = 11
// add 10: () => this.migrateFromV10ToV11()
private migrateFromV10ToV11(): void {
const columns = this.getSessionColumnNames()
if (columns.size === 0) return
if (!columns.has('personality')) {
this.db.exec('ALTER TABLE sessions ADD COLUMN personality TEXT')
}
}
// include personality TEXT in createSchema(), StoredSession, DbSessionRow, and toStoredSession().
// hub/src/sync/sessionCache.ts
if (config.personality !== undefined) {
if (config.personality !== session.personality) {
const updated = this.store.sessions.setSessionPersonality(sessionId, config.personality, session.namespace, {
touchUpdatedAt: false
})
if (!updated) throw new Error('Failed to update session personality')
}
session.personality = config.personality
this.markRuntimeConfigUpdated(sessionId, 'personality', appliedAt)
}There was a problem hiding this comment.
Findings
- No findings.
Questions
- None.
Summary
- Review mode: follow-up after new commits
- No issues found in the latest full diff. The prior persistence concern appears addressed by the schema migration, stored session mapping/setter, keepalive persistence, and resume spawn threading.
- Residual risk: review-only pass; I did not execute PR code.
Testing
- Not run (automation)
HAPI Bot
Closes #1003
Summary
friendly,pragmatic, andnonepersonality state across shared schemas, keepalive, Hub config RPC, REST, and Web mutations/personalityquery/set/reset commandsnull/Default override instead of falling back to a persisted selectionWhy
Codex CLI exposes personality selection and app-server accepts it, but HAPI previously had no remote session state, composer control, slash command, or launcher wiring.
Validation
upstream/main: personality config/slash tests failed as expected and the shared catalog export was absentcodex-cli 0.144.1:model/listselectedgpt-5.5withsupportsPersonality: truepragmaticfriendlyand retained the same thread id4eebcec; selector memo refresh resolved in50e905fgit diff --check