Skip to content

feat(codex): expose personality controls#1009

Open
swear01 wants to merge 7 commits into
tiann:mainfrom
swear01:agent/codex-personality
Open

feat(codex): expose personality controls#1009
swear01 wants to merge 7 commits into
tiann:mainfrom
swear01:agent/codex-personality

Conversation

@swear01

@swear01 swear01 commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Closes #1003

Summary

  • add Codex friendly, pragmatic, and none personality state across shared schemas, keepalive, Hub config RPC, REST, and Web mutations
  • expose a Codex-only Personality selector for active remote sessions
  • support /personality query/set/reset commands
  • pass selected personality to Codex app-server thread start/resume and turn parameters
  • preserve Codex config behavior when personality is unset
  • preserve an explicit null/Default override instead of falling back to a persisted selection
  • restore personality through both CLI API session mappers and broadcast keepalive changes to web clients

Why

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

  • retroactive test-only check on upstream/main: personality config/slash tests failed as expected and the shared catalog export was absent
  • CLI API mapping, personality startup/reset, slash, and app-server config tests — 53 passed
  • Hub alive-event/session route tests — 61 passed, including SSE propagation, select, clear, and local-session rejection
  • shared mode tests — 15 passed
  • repository typecheck passed
  • local non-runner suites passed before the final focused fix: CLI 1103, Hub 453, Web 1131, Shared 106; final changed-area suites were rerun after the fix
  • real-machine smoke with authenticated codex-cli 0.144.1:
    • model/list selected gpt-5.5 with supportsPersonality: true
    • thread start and turn start accepted pragmatic
    • the real model completed a turn
    • thread resume accepted friendly and retained the same thread id
  • follow-up review threads for CLI mapping and SSE propagation resolved in 4eebcec; selector memo refresh resolved in 50e905f
  • git diff --check

@swear01
swear01 marked this pull request as ready for review July 11, 2026 03:52

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Findings

  • [Major] Restored personality is dropped before Codex sees it — runCodex now seeds currentPersonality from sessionInfo.personality, but the CLI API mapper still returns only through collaborationMode, 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 has friendly/pragmatic/none. Evidence: cli/src/codex/runCodex.ts:98, related mapper context cli/src/api/api.ts:83 and cli/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 the session-updated patch emitted below still stops at collaborationMode. Since /personality ... is handled locally and only pushes keepalive, the selector stays stale until a full refetch. Evidence: hub/src/sync/sessionCache.ts:253 and omitted patch field at hub/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 typecheck failed because bun is not on PATH in this environment. git diff --check passed.

HAPI Bot

Comment thread cli/src/codex/runCodex.ts
Comment thread hub/src/sync/sessionCache.ts Outdated

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Findings

  • [Minor] Personality selector can render stale state — the memoized overlay now reads personality and onPersonalityChange, but the dependency list was not updated. After changing personality, the session refetch/SSE can update props.session.personality while 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): bun is not available on PATH, so targeted tests and bun typecheck could not run.
  • git diff --check passed.

HAPI Bot

Comment thread web/src/components/AssistantChat/HappyComposer.tsx

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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): bun is not available on PATH, so bun typecheck and targeted tests could not run.

HAPI Bot

swear01 added a commit to swear01/hapi that referenced this pull request Jul 11, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Findings

  • [Major] Personality is dropped when an inactive Codex session is resumed from the web — the new personality config is accepted and cached for active sessions, but resumeSession() still seeds the runner only with model/effort/permission/service tier. After a user selects friendly/pragmatic, lets the session end, then resumes it remotely, the spawned hapi codex resume ... process starts without that personality and the first resumed turn runs with the default. Evidence: hub/src/sync/syncEngine.ts:677 adds 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']

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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.

swear01 added a commit to swear01/hapi that referenced this pull request Jul 15, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Findings

  • [Major] Personality selection is lost across hub restart — the diff adds personality to runtime session config, but applySessionConfig only mutates the in-memory Session. There is no SQLite column/session-store setter in this PR, and refreshSession() rebuilds sessions from stored fields while carrying personality only from an existing cache entry. A user who selects friendly/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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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)
}

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(codex): expose CLI personality selection in remote sessions

1 participant