Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.ko.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ codex -m "xai/grok-4" "이 PR을 리뷰해 줘"
|---|---|---|
| OpenAI (ChatGPT 로그인) | `openai-responses` | forward (키 불필요) |
| OpenAI (API 키) | `openai-responses` | key |
| Umans AI Coding Plan | `anthropic` | key |
| Anthropic Claude | `anthropic` | oauth / key |
| xAI Grok | `openai-chat` | oauth / key |
| Kimi (Moonshot) | `openai-chat` | oauth / key |
Expand Down
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ Routed models also appear in the **Codex App** model picker with per-model reaso
|---|---|---|
| OpenAI (ChatGPT login) | `openai-responses` | forward (no key) |
| OpenAI (API key) | `openai-responses` | key |
| Umans AI Coding Plan | `anthropic` | key |
| Anthropic Claude | `anthropic` | oauth / key |
| xAI Grok | `openai-chat` | oauth / key |
| Kimi (Moonshot) | `openai-chat` | oauth / key |
Expand Down Expand Up @@ -243,6 +244,22 @@ Local models work too. Point opencodex at any OpenAI-compatible server running o

WebSocket transport is off by default. Set `"websockets": true` only if you want Codex to advertise and use the Responses WebSocket path instead of HTTP/SSE.

opencodex leaves existing Codex resume history untouched by default. This avoids changing Codex's
local thread index just because the proxy started, but Codex App may hide old OpenAI-backed project
threads and opencodex-created `exec` threads while `opencodex` is the active provider. If you want
those chats to appear while the proxy is active, enable the reversible compatibility remap with
`"syncResumeHistory": true`. opencodex records the original provider/source metadata in
`~/.opencodex/codex-history-backup.json`. `ocx stop` / `ocx restore` restores backed-up OpenAI rows
to OpenAI, and ejects any remaining opencodex user threads to OpenAI as well so native Codex does not
try to resume a thread whose provider no longer exists in `config.toml`.

If you tested an older development build where `syncResumeHistory` already remapped history before
backup support existed, you can also run the explicit recovery command:

```bash
ocx recover-history --legacy-openai
```

See the **[Configuration reference](https://lidge-jun.github.io/opencodex/reference/configuration/)** for every field.

## Documentation
Expand Down
1 change: 1 addition & 0 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ codex -m "deepseek/deepseek-r1" "分析这个性能瓶颈"
|---|---|---|
| OpenAI(ChatGPT 登录) | `openai-responses` | 转发(无需 key) |
| OpenAI(API key) | `openai-responses` | key |
| Umans AI Coding Plan | `anthropic` | key |
| Anthropic Claude | `anthropic` | oauth / key |
| xAI Grok | `openai-chat` | oauth / key |
| Kimi(Moonshot) | `openai-chat` | oauth / key |
Expand Down
115 changes: 115 additions & 0 deletions devlog/220_codex-app-history-visibility/00_plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# 220 Codex App history visibility

## Goal

Investigate and fix the Codex App project-sidebar history visibility regression around `ocx start` / `ocx stop`, issue #11, and PR #13.

The fix must preserve user data by default, document the exact reproduction, explain the Codex App filtering semantics we can infer from `codex-rs` and local state, and avoid broad irreversible mutation of the user's real `~/.codex/state_5.sqlite`.

## User reproduction

The local `ocx` binary is the development checkout:

- `/Users/jun/.local/bin/ocx`
- `/Users/jun/Developer/new/700_projects/opencodex/dist/bin/ocx`
- `/Users/jun/Developer/new/700_projects/opencodex/src/cli.ts`

Observed behavior:

1. `ocx start` makes all Codex App project conversations disappear from the sidebar, including old OpenAI conversations and conversations created while opencodex was active.
2. `ocx stop` makes old OpenAI-side conversations visible again.
3. Conversations created while opencodex was active remain invisible after `ocx stop`.

## Current evidence

Local config evidence:

- `/Users/jun/.opencodex/config.json` currently has no `syncResumeHistory` key, so PR #13's default is "do not rewrite resume history".
- `~/.codex/config.toml` can be in native mode after `ocx stop`, with no root `model_provider = "opencodex"`.

Local Codex state evidence from `~/.codex/state_5.sqlite`:

| Row set | model_provider | source | Count / effect |
| --- | --- | --- | --- |
| App/CLI resumable rows | `openai` | `cli`, `vscode` | Visible when native OpenAI provider is active |
| opencodex-created project rows | `opencodex` | `exec` | Not visible in Codex App project sidebar |
| opencodex `cli`/`vscode` rows | `opencodex` | `cli`, `vscode` | None found in current local state |

Working hypothesis:

- Codex App visibility is not controlled by provider alone.
- It likely filters by both `threads.model_provider` and a resumable/source classification.
- Old OpenAI rows disappear during `ocx start` because the active/root provider becomes `opencodex`.
- opencodex-created rows stay hidden because they are mostly `source = 'exec'`, not `cli` or `vscode`.

## Current PR #13 behavior

PR #13 is already merged locally on `dev` for testing.

It changes the default from automatic resume-history rewriting to explicit opt-in:

- Default: leave history unchanged.
- `syncResumeHistory: true`: remap resumable OpenAI rows to opencodex during `ocx start`.
- `ocx stop`: remap opencodex rows back to OpenAI using the existing restoration path.

Limitation:

- #13 is a safety improvement, not the full #11 fix.
- It only addresses the provider-label side of the problem.
- It does not make opencodex-created `source = 'exec'` rows visible in Codex App.

## Plan

### P / A: document and audit

- Create this devlog file as the durable issue record.
- Correctly notify GitHub issue #11 and PR #13 that investigation is underway with the refined reproduction and current hypothesis.
- Run a read-only audit worker against the plan and the local code anchors before editing behavior.

### B: investigate and implement

- Inspect local `codex-rs` / Codex App sources or installed artifacts for thread filtering semantics around `model_provider`, `source`, `thread_source`, `cwd`, `archived`, and `has_user_event`.
- Re-check the local SQLite schema and representative rows without mutating the real DB.
- Design the smallest opencodex-side compatibility fix that can make old OpenAI rows and opencodex-created rows visible without unsafe broad mutation.
- Preserve reversibility. If metadata must be changed, record original values before changing them and restore only rows opencodex touched.

### C: verify

- Add unit tests against temporary SQLite fixtures only.
- Run targeted tests first, then full `bun test tests`.
- Run `bun run typecheck`.
- Verify no test or script mutates the real `~/.codex/state_5.sqlite`.

### D: report

- Summarize the root cause in plain Korean.
- List exact files changed.
- Include verification commands and results.
- Note remaining Codex App upstream limitation if any behavior can only be fully fixed in `codex-rs`.

## Likely files

Likely code files:

- `/Users/jun/Developer/new/700_projects/opencodex/src/codex-history-provider.ts`
- `/Users/jun/Developer/new/700_projects/opencodex/src/codex-inject.ts`
- `/Users/jun/Developer/new/700_projects/opencodex/src/types.ts`

Likely tests:

- `/Users/jun/Developer/new/700_projects/opencodex/tests/codex-history-provider.test.ts`
- `/Users/jun/Developer/new/700_projects/opencodex/tests/codex-inject.test.ts`

Likely docs:

- `/Users/jun/Developer/new/700_projects/opencodex/README.md`
- `/Users/jun/Developer/new/700_projects/opencodex/docs-site/src/content/docs/reference/configuration.md`
- `/Users/jun/Developer/new/700_projects/opencodex/docs-site/src/content/docs/ko/reference/configuration.md`
- `/Users/jun/Developer/new/700_projects/opencodex/docs-site/src/content/docs/zh/reference/configuration.md`

## Safety rules

- Do not mutate the user's real Codex DB while testing.
- Do not broaden `ocx start` default behavior into silent history rewriting without an explicit config or user action.
- Do not let `ocx stop` broadly rewrite all `opencodex` rows to `openai` if the implementation starts producing genuinely opencodex-owned visible rows.
- Prefer reversible metadata backup over pattern-based rollback.
128 changes: 128 additions & 0 deletions devlog/220_codex-app-history-visibility/01_codex-rs-findings.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# Codex App / codex-rs findings

## Summary

The sidebar disappearance is caused by two independent filters in Codex App / app-server:

1. Provider filter: when `model_providers` is omitted, app-server defaults to the active configured provider.
2. Source filter: when `source_kinds` is omitted or empty, app-server defaults to `INTERACTIVE_SESSION_SOURCES`.

In the local codex-rs checkout at `/Users/jun/Developer/codex/codex-cli/codex-rs`, `INTERACTIVE_SESSION_SOURCES` is:

```text
cli
vscode
custom atlas
custom chatgpt
```

It does not include `exec`.

## Source anchors

codex-rs source anchors:

- `/Users/jun/Developer/codex/codex-cli/codex-rs/rollout/src/lib.rs`
- `INTERACTIVE_SESSION_SOURCES` includes `Cli`, `VSCode`, `Custom("atlas")`, `Custom("chatgpt")`.
- `/Users/jun/Developer/codex/codex-cli/codex-rs/app-server/src/filters.rs`
- `compute_source_filters(None)` returns `INTERACTIVE_SESSION_SOURCES`.
- `compute_source_filters(Some(Vec::new()))` also returns `INTERACTIVE_SESSION_SOURCES`.
- `ThreadSourceKind::Exec` requires an explicit source filter.
- `/Users/jun/Developer/codex/codex-cli/codex-rs/app-server/src/request_processors/thread_processor.rs`
- `model_providers: None` becomes `Some(vec![self.config.model_provider_id.clone()])`.
- `source_kinds` flows through `compute_source_filters()`.
- Those filters are passed to `thread_store.list_threads()`.
- `/Users/jun/Developer/codex/codex-cli/codex-rs/state/src/runtime/threads.rs`
- SQL filter applies `threads.archived = 0`, `threads.preview <> ''`, optional `threads.source IN (...)`, optional `threads.model_provider IN (...)`, and optional `threads.cwd IN (...)`.

## Local DB evidence

Read-only query against `/Users/jun/.codex/state_5.sqlite` for project cwd `/Users/jun/Developer/new/700_projects/opencodex`:

| model_provider | source | count |
| --- | --- | ---: |
| `openai` | `cli` | 7 |
| `openai` | `exec` | 2 |
| `opencodex` | `exec` | 43 |
| `opencodex` | subagent thread-spawn JSON | 2 |

Default Codex App list while opencodex is active:

```sql
WHERE archived = 0
AND preview <> ''
AND source IN ('cli', 'vscode', 'atlas', 'chatgpt')
AND model_provider = 'opencodex'
```

That returns zero rows locally because opencodex-created project rows are `source = 'exec'`.

## Upstream patch direction

The cleaner upstream fix would be in Codex App / codex-rs:

- either request `sourceKinds` including `exec` for the project sidebar,
- or make the project sidebar intentionally provider/source agnostic when the user is browsing project history,
- or expose a UI affordance for source filtering.

opencodex cannot change Codex App's `thread/list` request payload. The opencodex-side fix therefore must be an explicit compatibility mode that temporarily adjusts local metadata and restores it later.

## opencodex fix direction

For `syncResumeHistory: true`:

- backup original thread metadata into `~/.opencodex/codex-history-backup.json`;
- remap old OpenAI `cli`/`vscode` rows to `model_provider = 'opencodex'`;
- promote opencodex-created user `exec` rows to `source = 'cli'`;
- update the rollout JSONL first `session_meta` line consistently so Codex's rollout scanner does not repair the DB back to the hidden state;
- on `ocx stop` / `ocx restore`, restore only rows recorded in the backup manifest.

Default remains unchanged: no history mutation unless the user explicitly enables `syncResumeHistory`.

## Legacy PR #13 upgrade edge

There is one unsafe-to-automate edge case:

1. A user enabled `syncResumeHistory: true` on a development build before backup support existed.
2. That build remapped old `openai` interactive rows to `opencodex`.
3. The user upgrades while those rows are still remapped.
4. The new backup manifest does not exist, so `ocx stop` cannot know which `opencodex` interactive rows were originally OpenAI rows.

The fix must not silently rewrite all `opencodex` `cli`/`vscode` rows to `openai`, because future or app-created rows can legitimately be opencodex-owned. Instead:

- normal `ocx stop` detects and reports ambiguous unbacked rows;
- it leaves them unchanged by default;
- `ocx recover-history --legacy-openai` is the explicit manual recovery path for users who know those rows came from the old remap.

## 2026-06-22 correction: native restore cannot leave opencodex providers behind

Live local testing showed that the conservative "leave unbacked opencodex rows unchanged" approach
breaks Codex App after `ocx stop`:

```text
Codex can't load config.toml, so this thread can't resume.
Fix config.toml: Model provider `opencodex` not found.
```

The root cause is straightforward: `ocx stop` removes `[model_providers.opencodex]` from
`~/.codex/config.toml`. Any remaining `threads.model_provider = 'opencodex'` row can therefore point
Codex App at a provider id that no longer exists. This applies not only to legacy `cli`/`vscode` rows,
but also to opencodex-created `exec` rows and subagent rows if the user resumes them directly.

Revised opencodex invariant:

- while opencodex is active, `syncResumeHistory: true` may remap/promote history so the App sidebar is visible;
- after native restore (`ocx stop`, `ocx restore`, uninstall), no user thread should be left with
`model_provider = 'opencodex'` unless the Codex config still contains that provider;
- backed-up OpenAI rows restore to OpenAI;
- opencodex-owned user rows are ejected to `openai`, and `exec` source is promoted to `cli`, so native
Codex can list/resume them after the proxy provider has been removed;
- root `model = "provider/model"` values are also stripped on native restore because provider-prefixed
routed model ids are invalid without the opencodex provider/catalog.

Local repair evidence after the correction:

- `ocx recover-history --legacy-openai` recovered 218 user thread rows to `openai`;
- `ocx stop` left zero user rows with `model_provider = 'opencodex'`;
- `~/.codex/config.toml` no longer contains `[model_providers.opencodex]`, root
`model_provider = "opencodex"`, or root `model = "provider/model"`.
Loading
Loading