Increase cap of sessions in VS Code extension

Open 💬 16 comments Opened Mar 21, 2026 by iqdoctor
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

What version of the IDE extension are you using?

26.5318.11754

What subscription do you have?

Pro

Which IDE are you using?

VS Code

What platform is your computer?

Linux

What issue are you seeing?

Codex VS Code Plugin Feedback

Evidence in local extension code:

  • /root/.vscode-server/extensions/openai.chatgpt-26.5318.11754-linux-x64/webview/assets/index-BX_E-kXD.js.map
  • embedded source ../../src/feedback/feedback-command.tsx
  • embedded source ../../src/feedback/feedback-success.tsx

Environment

  • VS Code extension: openai.chatgpt-26.5318.11754-linux-x64
  • Bundled extension CLI: codex-cli 0.116.0-alpha.10
  • System CLI: /usr/bin/codex -> codex-cli 0.116.0
  • Host: remote VS Code server on Linux
  • Codex data dir: /root/.codex

Problems

1. Session list is capped too aggressively

With intensive Codex usage, the session/thread list in the VS Code plugin becomes incomplete. In my case the plugin initially showed only 50 sessions, then after local bundle patching it showed 100, and only after forcing auto-pagination it showed 313. This makes the sidebar/task history unreliable for real workloads.

Expected:

  • the plugin should either load all recent local threads needed for search/resume,
  • or clearly support pagination / load more in the UI,
  • or at minimum not silently stop at a fixed hidden cap.

Observed local code paths:

  • /root/.vscode-server/extensions/openai.chatgpt-26.5318.11754-linux-x64/webview/assets/app-server-manager-hooks-DC8Cu72L.js.map
  • embedded source ../../src/app-server/app-server-manager-constants.ts:9
  • RECENT_CONVERSATIONS_PAGE_SIZE = 50
  • embedded source ../../src/app-server/app-server-manager.ts:1161-1181
  • refreshRecentConversations() requests thread/list with limit = RECENT_CONVERSATIONS_PAGE_SIZE * recentConversationsPageCount
  • embedded source ../../src/app-server/app-server-manager.ts:1298-1315
  • loadMoreRecentConversations() requests thread/list with limit: RECENT_CONVERSATIONS_PAGE_SIZE
  • /root/.vscode-server/extensions/openai.chatgpt-26.5318.11754-linux-x64/webview/assets/index-BX_E-kXD.js.map
  • embedded source ../../src/sidebar/sidebar-electron.tsx:144
  • sidebar pulls local threads via useConversationsMeta()
  • embedded source ../../src/header/recent-tasks-menu/recent-tasks-menu.tsx
  • recent/local tasks UI renders what it gets; there is no visible explicit paging UX for local conversations there

Local temporary workaround applied:

  • runtime bundle patched in:
  • /root/.vscode-server/extensions/openai.chatgpt-26.5318.11754-linux-x64/webview/assets/app-server-manager-hooks-DC8Cu72L.js
  • changes:
  • raised page size from 50 to 500
  • added auto-pagination during recent conversation refresh until nextCursor is exhausted or 500 items are collected

2. Lost titles without disk fallback

Some threads lose their title in the plugin/local state DB even though a valid thread name still exists on disk. This makes sessions effectively disappear from normal search/use in the plugin.

Concrete example:

  • thread id: 019ca226-b30c-7181-bdf0-62b891464a25
  • disk/session index had:
  • /root/.codex/session_index.jsonl
  • thread_name = "Worker"
  • but plugin/local DB had:
  • /root/.codex/state_5.sqlite
  • title = ''
  • first_user_message = ''

As a result, the CLI could still find the session as Worker, but the VS Code plugin could not reliably surface it by name.

Observed local code paths:

  • /root/.vscode-server/extensions/openai.chatgpt-26.5318.11754-linux-x64/webview/assets/app-server-manager-hooks-DC8Cu72L.js.map
  • embedded source ../../src/app-server/app-server-manager.ts:1389-1415
  • upsertRecentConversationState() uses thread.name or persisted title cache
  • no fallback to session_index.jsonl, rollout JSONL, or recovery history when metadata title is blank
  • embedded source ../../src/app-server/app-server-manager.ts:1472
  • new conversation state gets title: persistedTitle
  • /root/.vscode-server/extensions/openai.chatgpt-26.5318.11754-linux-x64/webview/assets/index-BX_E-kXD.js.map
  • embedded source ../../src/local-conversation/get-local-conversation-title.ts
  • fallback logic uses only:
  • conversationState.title
  • first turn input text
  • parent subagent prompt
  • there is no disk/index fallback

Supporting local evidence:

  • thread index on disk:
  • /root/.codex/session_index.jsonl
  • thread state DB:
  • /root/.codex/state_5.sqlite
  • rollout file exists:
  • /root/.codex/sessions/2026/02/28/rollout-2026-02-28T05-49-31-019ca226-b30c-7181-bdf0-62b891464a25.jsonl
  • recovery text exists:
  • /root/.codex/recovery/019ca226-b30c-7181-bdf0-62b891464a25/history.jsonl

Local temporary workaround applied:

  • repaired blank titles directly in:
  • /root/.codex/state_5.sqlite
  • source of repair:
  • first from /root/.codex/session_index.jsonl
  • fallback from rollout/history on disk
  • backup created:
  • /root/.codex/state_5.sqlite.bak-20260321-042851

Suggested fixes

Session list cap

  • Avoid hidden hard caps for local recent threads in the extension UI.
  • Either:
  • auto-page until all recent threads are loaded,
  • or expose an explicit local "load more" UX,
  • or use the existing full traversal path analogous to listAllThreads() instead of only the first recent page.

Lost title fallback

  • On recent-thread hydration, if thread.name is blank and the persisted title cache is blank:
  • check session_index.jsonl for thread_name
  • if still blank, derive a title from rollout/recovery first user text
  • write repaired title back into the local thread state DB/index
  • This should be a metadata-layer repair, not a UI-render-time disk read.

Short version

Two plugin issues become visible under heavy real use:

  1. local session list silently truncates because recent conversations are page-capped;
  2. blank local DB titles are not repaired from disk, even when CLI-visible thread names still exist.

I reproduced both locally and applied temporary workarounds, but both should be fixed in the extension/app-server metadata path rather than by local runtime patching.

019d0ddb-8fb3-7873-a5e0-688f9437ef61

What steps can reproduce the bug?

Uploaded thread: 019d0ddb-8fb3-7873-a5e0-688f9437ef61

What is the expected behavior?

Open Tasks in Plugin, see View All (50) - hard limited 50 records

Additional information

_No response_

View original on GitHub ↗

16 Comments

github-actions[bot] contributor · 4 months ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #15060
  • #14751
  • #14812
  • #14389

Powered by Codex Action

iqdoctor · 4 months ago

Added focused follow-up comments to two related issues:

Why this may help a coding agent process the duplicates better:

  • it gives a concrete constant and exact functions to inspect for the fix point
  • it separates a client-side visible cap from a deeper pagination / hydration boundary
  • it adds a validated local experiment, which narrows the search space
  • it turns the report from a generic UX complaint into a traced engineering bug

What I intentionally did not do:

  • I did not comment on #14389, because it is already closed
  • I did not paste the same long note into every potential duplicate, to avoid diluting the signal
iqdoctor · 4 months ago

Codex VS Code Plugin Session Repair Notes

Date: 2026-03-21

Scope

This note captures a repeatable workflow for debugging and hot-patching future Codex VS Code extension releases when:

  • the local session/thread list is silently truncated
  • sessions exist on disk but are missing or undiscoverable in the plugin because titles are blank

This was validated on:

  • VS Code extension openai.chatgpt-26.5318.11754-linux-x64
  • bundled extension CLI codex-cli 0.116.0-alpha.10
  • system CLI /usr/bin/codex -> codex-cli 0.116.0
  • Linux remote VS Code server

Symptoms Seen

1. Hidden recent-thread cap

Observed sequence:

  • plugin initially showed only 50 local sessions
  • after increasing the client page size, plugin showed 100
  • after forcing auto-pagination across nextCursor, plugin showed 313

Conclusion:

  • there is a client-side recent-thread page size
  • there is also a deeper pagination boundary if the client does not continue loading

2. Blank-title sessions

Concrete example:

  • thread id 019ca226-b30c-7181-bdf0-62b891464a25
  • ~/.codex/session_index.jsonl still had thread_name = "Worker"
  • rollout file still existed on disk
  • but ~/.codex/state_5.sqlite had:
  • title = ''
  • first_user_message = ''

Effect:

  • Codex CLI could still find the session as Worker
  • VS Code extension could not reliably surface it by name

Primary Local Paths

  • extension bundle directory:
  • /root/.vscode-server/extensions/openai.chatgpt-*/webview/assets/
  • current runtime-patched bundle:
  • /root/.vscode-server/extensions/openai.chatgpt-26.5318.11754-linux-x64/webview/assets/app-server-manager-hooks-DC8Cu72L.js
  • current sourcemaps:
  • /root/.vscode-server/extensions/openai.chatgpt-26.5318.11754-linux-x64/webview/assets/app-server-manager-hooks-DC8Cu72L.js.map
  • /root/.vscode-server/extensions/openai.chatgpt-26.5318.11754-linux-x64/webview/assets/index-BX_E-kXD.js.map
  • local Codex state:
  • /root/.codex/state_5.sqlite
  • /root/.codex/session_index.jsonl
  • /root/.codex/sessions/
  • /root/.codex/recovery/

Root Cause Notes

Recent-thread cap

From the embedded sources in the sourcemap:

  • src/app-server/app-server-manager-constants.ts
  • RECENT_CONVERSATIONS_PAGE_SIZE = 50
  • src/app-server/app-server-manager.ts
  • refreshRecentConversations() requests thread/list with:
  • limit = RECENT_CONVERSATIONS_PAGE_SIZE * recentConversationsPageCount
  • loadMoreRecentConversations() requests thread/list with:
  • limit: RECENT_CONVERSATIONS_PAGE_SIZE
  • src/sidebar/sidebar-electron.tsx
  • sidebar uses useConversationsMeta()

Interpretation:

  • UI is built from recent loaded conversations, not from the full on-disk session set
  • the extension does not automatically page through all local recent threads in this path

Blank-title sessions

From the embedded sources in the sourcemap:

  • src/app-server/app-server-manager.ts
  • upsertRecentConversationState() relies on:
  • thread.name
  • persisted title cache
  • it does not fall back to:
  • session_index.jsonl
  • rollout JSONL
  • recovery history
  • src/local-conversation/get-local-conversation-title.ts
  • fallback chain is:
  • conversationState.title
  • first turn input text
  • parent subagent prompt
  • no disk/index fallback

Interpretation:

  • if recent-thread metadata loses title, the plugin can fail to surface a session by name even when disk data still exists

How To Inspect New Releases

1. Find the current hashed asset names

find /root/.vscode-server/extensions/openai.chatgpt-* -type f | rg 'app-server-manager-hooks|index-.*\\.js\\.map|index-.*\\.js$'

2. Check the embedded source map for the hardcoded page size

python - <<'PY'
import json
p='/root/.vscode-server/extensions/openai.chatgpt-26.5318.11754-linux-x64/webview/assets/app-server-manager-hooks-DC8Cu72L.js.map'
with open(p) as f: m=json.load(f)
i=m['sources'].index('../../src/app-server/app-server-manager-constants.ts')
print(m['sourcesContent'][i])
PY

Look for:

  • RECENT_CONVERSATIONS_PAGE_SIZE = 50

3. Check the live compiled JS bundle for patch needles

node - <<'NODE'
const fs=require('fs');
const p='/root/.vscode-server/extensions/openai.chatgpt-26.5318.11754-linux-x64/webview/assets/app-server-manager-hooks-DC8Cu72L.js';
const s=fs.readFileSync(p,'utf8');
for (const needle of [
  'let t=50*this.recentConversationsPageCount',
  '{limit:50,cursor:this.recentConversationsNextCursor',
  's.length>0&&(this.recentConversationIds=[...s,...this.recentConversationIds]),this.notifyAnyConversationCallbacks()}async hydratePinnedThreads'
]) {
  const idx=s.indexOf(needle);
  console.log('needle', needle, 'idx', idx);
}
NODE

Quick Hot Patch For Future Versions

A. Raise the recent-thread page size to 500

Use the actual current hashed bundle path.

node - <<'NODE'
const fs=require('fs');
const p='/root/.vscode-server/extensions/openai.chatgpt-26.5318.11754-linux-x64/webview/assets/app-server-manager-hooks-DC8Cu72L.js';
let s=fs.readFileSync(p,'utf8');
const before1='let t=50*this.recentConversationsPageCount';
const after1='let t=500*this.recentConversationsPageCount';
const before2='{limit:50,cursor:this.recentConversationsNextCursor';
const after2='{limit:500,cursor:this.recentConversationsNextCursor';
if (!s.includes(before1)) throw new Error('missing before1');
if (!s.includes(before2)) throw new Error('missing before2');
fs.copyFileSync(p, p + '.bak-quickhack');
s=s.replace(before1, after1).replace(before2, after2);
fs.writeFileSync(p, s);
console.log('patched', p);
NODE

B. Force auto-pagination until 500 items or no nextCursor

This is what was needed after the simple 50 -> 500 patch still only surfaced 100.

node - <<'NODE'
const fs=require('fs');
const p='/root/.vscode-server/extensions/openai.chatgpt-26.5318.11754-linux-x64/webview/assets/app-server-manager-hooks-DC8Cu72L.js';
let s=fs.readFileSync(p,'utf8');
const before='s.length>0&&(this.recentConversationIds=[...s,...this.recentConversationIds]),this.notifyAnyConversationCallbacks()}async hydratePinnedThreads';
const after='s.length>0&&(this.recentConversationIds=[...s,...this.recentConversationIds]);for(;this.recentConversationsNextCursor!=null&&this.recentConversationIds.length<500;)await this.loadMoreRecentConversations();this.notifyAnyConversationCallbacks()}async hydratePinnedThreads';
if (!s.includes(before)) throw new Error('missing refresh tail');
s=s.replace(before, after);
fs.writeFileSync(p, s);
console.log('patched refresh tail for auto-pagination');
NODE

C. Syntax check

node --check /root/.vscode-server/extensions/openai.chatgpt-26.5318.11754-linux-x64/webview/assets/app-server-manager-hooks-DC8Cu72L.js

D. Reload VS Code

In VS Code:

  • Developer: Reload Window

Blank Title Repair

Goal

Repair threads.title in state_5.sqlite from disk-backed sources:

  1. session_index.jsonl
  2. rollout JSONL
  3. recovery history.jsonl

One-off repair script

python - <<'PY'
import sqlite3, json, shutil, re
from pathlib import Path
from datetime import datetime

STATE = Path('/root/.codex/state_5.sqlite')
BACKUP = Path(f'/root/.codex/state_5.sqlite.bak-{datetime.now().strftime("%Y%m%d-%H%M%S")}')
IDX = Path('/root/.codex/session_index.jsonl')

shutil.copy2(STATE, BACKUP)
print('backup', BACKUP)

idx_titles = {}
if IDX.exists():
    with IDX.open() as f:
        for line in f:
            line=line.strip()
            if not line:
                continue
            try:
                obj = json.loads(line)
            except Exception:
                continue
            tid = obj.get('id')
            title = (obj.get('thread_name') or '').strip()
            if tid and title:
                idx_titles[tid] = title

md_link_re = re.compile(r'\[([^\]]+)\]\([^)]+\)')

def normalize_title(text: str) -> str:
    if not text:
        return ''
    text = md_link_re.sub(r'\1', text)
    text = re.sub(r'\s+', ' ', text).strip()
    if not text:
        return ''
    return text if len(text) <= 80 else text[:79].rstrip() + '…'

def title_from_rollout(path: Path) -> str:
    if not path.exists():
        return ''
    try:
        with path.open() as f:
            for line in f:
                try:
                    obj = json.loads(line)
                except Exception:
                    continue
                if obj.get('type') == 'event_msg' and obj.get('payload', {}).get('type') == 'user_message':
                    title = normalize_title(obj.get('payload', {}).get('message', ''))
                    if title:
                        return title
                if obj.get('type') == 'response_item':
                    payload = obj.get('payload', {})
                    if payload.get('type') == 'message' and payload.get('role') == 'user':
                        texts = []
                        for part in payload.get('content') or []:
                            if isinstance(part, dict) and 'text' in part:
                                texts.append(part.get('text', ''))
                        title = normalize_title(' '.join(texts))
                        if title:
                            return title
    except Exception:
        return ''
    return ''

def title_from_recovery(tid: str) -> str:
    p = Path(f'/root/.codex/recovery/{tid}/history.jsonl')
    if not p.exists():
        return ''
    try:
        with p.open() as f:
            for line in f:
                try:
                    obj = json.loads(line)
                except Exception:
                    continue
                title = normalize_title(obj.get('text', ''))
                if title:
                    return title
    except Exception:
        return ''
    return ''

conn = sqlite3.connect(STATE)
cur = conn.cursor()
cur.execute("select id, rollout_path from threads where title='' or title is null")
rows = cur.fetchall()

for tid, rollout_path in rows:
    title = idx_titles.get(tid, '')
    if not title and rollout_path:
        title = title_from_rollout(Path(rollout_path))
    if not title:
        title = title_from_recovery(tid)
    if title:
        cur.execute(
            "update threads set title=?, first_user_message=coalesce(nullif(first_user_message,''), ?) where id=?",
            (title, title, tid),
        )

conn.commit()
cur.execute("select count(*) from threads where title='' or title is null")
print('blank_remaining', cur.fetchone()[0])
PY

Validation Commands

Count blank titles

sqlite3 /root/.codex/state_5.sqlite "select count(*) from threads where title='' or title is null;"

Inspect a specific thread

sqlite3 /root/.codex/state_5.sqlite "select id,title,first_user_message,cwd,source from threads where id='019ca226-b30c-7181-bdf0-62b891464a25';"
rg -n '019ca226-b30c-7181-bdf0-62b891464a25' /root/.codex/session_index.jsonl

Check how many repaired titles still look bad

sqlite3 /root/.codex/state_5.sqlite "select count(*) from threads where title like '# AGENTS.md instructions%';"

Notes

  • The quick repair filled blank titles fast, but some reconstructed titles may still be noisy if the only available on-disk source is a wrapped prompt payload.
  • A proper upstream fix should do read-repair in the metadata layer, not in the UI row renderer.
  • Extension updates will overwrite the patched webview bundle, so future versions need the same inspection + reapplication flow.

Related GitHub Issues

  • head issue:
  • https://github.com/openai/codex/issues/15368
  • related comments added:
  • https://github.com/openai/codex/issues/14812#issuecomment-4102065616
  • https://github.com/openai/codex/issues/15060#issuecomment-4102065633
  • https://github.com/openai/codex/issues/15368#issuecomment-4102425406
iqdoctor · 4 months ago

Follow-up from 2026-03-22.

The extension version was still unchanged on my machine:

  • openai.chatgpt-26.5318.11754-linux-x64

I verified that the previous hot patches were still present in the active bundle:

  • 50 -> 500 page-size change in refreshRecentConversations()
  • 50 -> 500 page-size change in loadMoreRecentConversations()
  • auto-pagination loop over nextCursor

Despite that, the visible local session count had dropped again, this time to 200 instead of the previously observed 313.

That suggests the issue is deeper than just RECENT_CONVERSATIONS_PAGE_SIZE = 50. The more reliable workaround was to bypass the initial recent-page request entirely and make refreshRecentConversations() use the existing full traversal path:

  • listAllThreads({ modelProviders: null, archived: false })

So the refined diagnosis is:

  • there is a visible client-side recent-page cap,
  • but there is also a deeper instability/boundary in the thread/list recent-loading path,
  • and listAllThreads() is currently a stronger workaround than only raising the page size and chasing nextCursor.
iqdoctor · 3 months ago

Follow-up: after another VS Code extension auto-update, I re-applied the same local runtime patch to the new bundle and the full thread list became visible again.

Current extension version:

  • 26.5325.31654

Patched bundle:

  • /root/.vscode-server/extensions/openai.chatgpt-26.5325.31654-linux-x64/webview/assets/app-server-manager-hooks-XL0eRatK.js

Local workaround re-applied:

  • refreshRecentConversations() switched from the initial thread/list page to listAllThreads({ modelProviders: null, archived: false })
  • loadMoreRecentConversations() raised from 50 to 500

Observed result after Developer: Reload Window:

  • the sidebar now shows 414 sessions again

This is another data point that the store update simply overwrites the patched webview bundle, and that the underlying issue is still the same recent-thread loading path rather than missing local session data.

iqdoctor · 3 months ago

Follow-up: in a newer VS Code extension build, the underlying issue is still reproducible, but the recent-thread loading logic has moved to a different compiled asset/module.

Current extension version:

  • 26.5415.20818

Relevant asset now:

  • /root/.vscode-server/extensions/openai.chatgpt-26.5415.20818-linux-x64/webview/assets/app-server-manager-signals-Dcn6qX09.js

In older builds, the patch point was in app-server-manager-hooks-*.
In this build, the relevant patch points moved to the delegated recent-thread loader methods:

  • refetchThreadList(...)
  • loadNextThreadListPage(...)

Local workaround re-applied successfully:

  • refetchThreadList(...) switched from the initial listRecentThreads({ limit: 50 * this.pageCount, cursor: null }) path to a full traversal through listAllThreadsRequest (lv(...) in the compiled bundle)
  • loadNextThreadListPage(...) raised from 50 to 500

This looks like the same product bug, but it is useful to note that the implementation moved. That makes the regression easy to carry forward across releases even when the symptom stays identical.

iqdoctor · 3 months ago

Follow-up after another VS Code extension update: same symptom and same conceptual workaround, but the compiled asset/module moved again.

Current extension version:

  • 26.5417.40842

Relevant asset now:

  • /root/.vscode-server/extensions/openai.chatgpt-26.5417.40842-linux-x64/webview/assets/send-app-server-request-C98EYioR.js

Previously the recent-thread loader had moved to app-server-manager-signals-*; in this build it is now bundled under send-app-server-request-*.

The conceptual patch points are still the same:

  • refetchThreadList(...)
  • loadNextThreadListPage(...)

Local workaround re-applied successfully:

  • initial refresh now uses the full traversal helper (mv(...) in this minified bundle, corresponding to listAllThreadsRequest)
  • load-more page size raised from 50 to 500

So the underlying bug appears unchanged, but the implementation keeps moving between compiled assets. This is useful for any coding agent working on the issue because searching only the old bundle names will miss the current patch point.

thephotocrv · 2 months ago

I am seeing the same issue on macOS with Codex Desktop 26.422.21637 (CFBundleVersion 2056).

The important part: local data is not missing. The threads exist in ~/.codex/state_5.sqlite, but Codex Desktop only loads a global window of recent threads and then projects appear incomplete or as if they had no chats.

Current local evidence:

  • ~/.codex/state_5.sqlite has the expected project threads:
  • Crv_PM: 14 active threads
  • WebPhotoCRV: 8 active threads
  • AyudaCodex: 5 active threads
  • ~/.codex/.codex-global-state.json is not empty:
  • active_roots: 3
  • thread_titles: 27
  • thread_order: 27
  • ~/.codex/session_index.jsonl: 27 entries
  • Codex telemetry in ~/Library/Application Support/Codex/sentry/scope_v3.json repeatedly reports:
  • thread_count_total: 50
  • thread_count_loaded_recent: 50

I also inspected /Applications/Codex.app/Contents/Resources/app.asar and found the recent thread loader starts with:

pageCount=1
refetchThreadList(...) -> limit: 50 * this.pageCount
loadNextThreadListPage(...) -> limit: 50

This matches the observed behavior: if a workspace/project has threads outside the initial global recent window, the Desktop UI does not show the complete project even though the local SQLite data is present.

I tried the usual safe steps:

  • reinstalling Codex.app
  • clearing app cache / local app support
  • checking global-state and session_index
  • reorganizing project folders

None of those fixed the issue because the underlying thread data was already present.

Expected behavior:

  • When expanding a project/workspace, Desktop should fetch/list threads for that specific cwd/workspace, not only rely on the initial global recent-thread window.
  • Alternatively, RECENT_CONVERSATIONS_PAGE_SIZE should be configurable or much larger.
  • By project should not show incomplete projects just because their threads fell outside the initial recent window.

This is a serious workflow blocker for projects with more than a handful of threads.

thephotocrv · 2 months ago

Additional local finding after deeper testing. This may help isolate the actual Desktop reindexing bug:
the issue is not only the 50 global recent-thread limit. There is also a reindexing/source-of-truth mismatch.

Root cause observed locally:

Codex Desktop does not reliably render project thread visibility from state_5.sqlite or session_index.jsonl alone. The UI state appears to be derived from the mtime of the rollout-*.jsonl files under:

~/.codex/sessions/
combined with the global recent-conversations preload limit of 50.

Concrete reproduction:

I had a project with 14 active threads in state_5.sqlite.
All 14 had the same exact cwd.
None were archived.
git_branch, git_sha, and worktree-related fields were not different.
Updating only state_5.sqlite updated_at / updated_at_ms did not make the hidden threads appear in Codex Desktop.
Updating session_index.jsonl and .codex-global-state.json also did not make them appear.
Codex Desktop reverted/recomputed the SQLite updated_at values on launch.
The values it restored matched the filesystem modification times of the corresponding rollout-*.jsonl files.
After updating both:
SQLite updated_at / updated_at_ms
the mtime of each corresponding rollout-*.jsonl
The project immediately showed all 14 threads in the UI.
So the effective UI source of truth seems to be:

rollout-*.jsonl mtime -> reindex -> SQLite/session index -> UI
or at least the rollout file mtime participates in reindexing strongly enough to override manual SQLite/index changes.

This explains why users see projects with missing threads even when local data exists and SQLite counts are correct.

Expected behavior:

Project thread lists should be resolved by project/workspace cwd, not only by a global recent window.
The UI should not report a project as empty or partially empty when valid local threads exist for that exact cwd.
Reindexing should use a stable thread metadata source, not filesystem mtime as a hidden effective source of truth.
If mtime is intentionally used, it should be documented and kept consistent with SQLite/session metadata.
Requested fix:

Increase the global recent conversations preload from 50 to at least 200.
Ideally make it configurable, e.g. in ~/.codex/config.toml:
recent_conversations_page_size = 200
or allow an unlimited / “load all local project threads” mode.

Even better:

When expanding a project, query local threads by cwd / project root directly.
Do not depend on whether those threads happen to be in the latest 50 global recent conversations.
Add proper pagination/search for local project threads.
Make “By project” actually load all local threads for that project.
Current workaround:

I created a local script that, with Codex Desktop closed:

backs up state_5.sqlite
backs up .codex-global-state.json
backs up session_index.jsonl
backs up the affected rollout-*.jsonl
updates SQLite updated_at
updates the corresponding rollout file mtime
rebuilds session_index.jsonl
limits visible projects to the active project plus a helper project
After doing this, the project becomes visible again in Codex Desktop.

This is not a real fix. It is only a workaround to force Codex Desktop to reindex hidden local threads.

Please fix this in Desktop. A hardcoded global recent-thread window of 50 is far too low for real Codex usage, because many users create many threads due to context limits, not because they want fragmented project history.
iqdoctor · 2 months ago

Follow-up after another VS Code extension update: same symptom and same conceptual workaround, but the compiled asset/helper moved again.

Current extension version:

  • 26.5422.21459

Relevant asset now:

  • /root/.vscode-server/extensions/openai.chatgpt-26.5422.21459-linux-x64/webview/assets/app-server-manager-signals-DaIWbgQQ.js

In the previous reapply, the relevant logic had moved under send-app-server-request-*.
In this build, the active patch target is back under app-server-manager-signals-*.

The conceptual patch points are still the same:

  • refetchThreadList(...)
  • loadNextThreadListPage(...)

Local workaround re-applied successfully:

  • initial refresh switched from listRecentThreads({ limit: 50 * this.pageCount, cursor: null }) to the full traversal helper (ey(...) in this minified bundle)
  • load-more page size raised from 50 to 500

So the underlying bug still appears unchanged, but the implementation keeps moving between compiled assets and helper names (lv(...) -> mv(...) -> ey(...)). That makes the regression easy to carry forward across releases, and it means searching only older bundle names will miss the current patch point.

thephotocrv · 2 months ago

Additional external confirmation: after another update, the same bug remains but the compiled patch target moved again.

In extension version 26.5422.21459, the relevant logic moved to:

webview/assets/app-server-manager-signals-DaIWbgQQ.js

The conceptual patch points are still:

  • refetchThreadList(...)
  • loadNextThreadListPage(...)
  • global recent thread list limit around 50

A local workaround reportedly changed the initial refresh from:

listRecentThreads({ limit: 50 * this.pageCount, cursor: null })

to a full traversal helper, and raised load-more page size from 50 to 500.

This reinforces that the underlying bug is unchanged, but the compiled implementation keeps moving between builds. A local bundle patch is therefore not a maintainable solution.

Please expose an official setting or fix project thread loading so project views query all local threads by project/cwd instead of depending on the latest 50 global recent threads.

iqdoctor · 2 months ago

Follow-up after another VS Code extension auto-update: the regression happened again, but this time the compiled asset/helper did not move.

Current extension version:

  • 26.5422.30944

Relevant asset:

  • /root/.vscode-server/extensions/openai.chatgpt-26.5422.30944-linux-x64/webview/assets/app-server-manager-signals-DaIWbgQQ.js

Compared with 26.5422.21459:

  • asset family/name stayed the same: app-server-manager-signals-DaIWbgQQ.js
  • patch points stayed the same: refetchThreadList(...) and loadNextThreadListPage(...)
  • full traversal helper stayed the same: ey(...)

But the store update still restored the original recent-thread behavior:

  • initial refresh was back to listRecentThreads({ limit: 50 * this.pageCount, cursor: null })
  • load-more was back to limit: 50

Local workaround re-applied successfully:

  • initial refresh now uses full traversal through ey(...)
  • load-more page size raised from 50 to 500
  • node --check passes for the patched bundle

This adds a slightly different data point: the bug/regression is not only caused by implementation moving between compiled assets. Even when the asset and helper remain stable across builds, the extension update overwrites the runtime bundle and brings back the hidden recent-thread cap.

iqdoctor · 2 months ago

Follow-up from the latest local validation on openai.chatgpt-26.5422.30944-linux-x64:

The issue is not only the visible client-side cap. A naive local workaround that replaces the first refetchThreadList(...) page with the existing recursive full traversal helper (ey(...), internally paging thread/list at limit:200) can make startup worse for large local histories.

Observed locally:

  • ~/.codex/state_5.sqlite has 1963 rows in threads.
  • Forcing initial refetchThreadList(...) through full recursive traversal caused the VS Code UI to stay cloud-only while local sessions were still being walked/hydrated.
  • A less blocking workaround was to use a larger first page and preserve cursor-based pagination:
  • refetchThreadList(...): listRecentThreads({ limit: 500 * this.pageCount, cursor: null }), then keep nextCursor = r.nextCursor
  • loadNextThreadListPage(...): limit:500

Why this may help triage:

  • There are two separate boundaries: the current small initial/list page cap, and the deeper pagination/full-hydration behavior.
  • A robust fix probably needs incremental rendering/pagination rather than synchronously loading all local threads before the first local list notify.
  • The relevant current compiled symbols in this build remained refetchThreadList(...), loadNextThreadListPage(...), and helper ey(...) in app-server-manager-signals-DaIWbgQQ.js.
iqdoctor · 2 months ago

Follow-up: I opened a separate issue for the broader VS Code sidebar semantics that this cap/history bug exposed:

  • #19603

Why this may be useful for agent triage:

  • it separates the hidden cap symptom from the desired CLI-like loading semantics;
  • it points to concrete current fix points in the installed extension build (refetchThreadList, loadNextThreadListPage, refreshRecentConversations, inline slice(0,Math.max(3,e.length)), and the View all ({total}) label);
  • it references the Codex CLI resume picker model (PAGE_SIZE = 25, updated-time sort, cursor/background loading, progressive search);
  • it calls out a protocol boundary: current ThreadListResponse has cursors but no exact same-filter total, so the UI should not present loaded rows as if they are total rows.

I intentionally kept this as a cross-reference rather than duplicating the full spec into this thread, so #15368 can remain focused on the disappeared/truncated history symptom.

interconnectedMe · 12 days ago

Truncation also a problem for me on long-running chat sessions with multiple compactions.

Codex (gpt-5.5) tells me that it's an issue with VScode not handling either the compactions, or just the length of some of the chats.

If I know what the last thing I saw was, or roughly where we were, Codex can find the session that is truncated and then I can read or Codex can summarize to pick up where we were.

But can cause a little disorientation while I try to work out what's happened.

gerashegalov · 12 days ago

Confirmed on VS Code extension 26.5623.141536-linux-x64.

The webview sidebar now has paginated/full-traversal paths, but the native VS Code chat-session provider remains capped. provideChatSessionItems() calls requestThreadList() once with:

{
  limit: 50,
  cursor: null,
  sortKey: "created_at",
  archived: false
}

It maps only that response's data and never follows nextCursor. The provider is registered through vscode.chat.registerChatSessionItemProvider("openai-codex", ...).

Expected: follow nextCursor incrementally, or implement load-more/progressive search. Sorting by updated_at would also better represent "recent chats".

This appears to be a remaining extension-host code path after the sidebar pagination improvements, rather than an app-server storage limitation.