VS Code Sessions: surface live Codex thread status and remaining usage from existing app-server events
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 125k
- Forks
- 19.5k
- PR merge metrics
- PR metrics pending
Description
What variant of Codex are you using?
IDE Extension (VS Code) + Codex app-server
What feature would you like to see?
I would like the Codex VS Code Sessions sidebar to surface the live state of each Codex session and optionally show the remaining Codex usage limits.
This is related to #38759, but after inspecting the current open-source openai/codex implementation, the required backend status contract appears to already exist in codex app-server.
The remaining work appears to be primarily in the VS Code extension: map the existing Codex thread state into VS Code's session status/badge UI.
Desired Sessions sidebar states
Each Codex session should visibly distinguish:
- Working
- Waiting for input
- Waiting for approval
- Idle
- Completed
- Failed / Error
- Interrupted
for example:
Working · 5h 74% left · week 43% left
This is related to #38759.
Existing Codex protocol already supports the required state
After inspecting the current open-source openai/codex implementation, the required backend status contract appears to already exist in codex app-server.
ThreadStatus currently supports:
export type ThreadStatus =
| { type: "notLoaded" }
| { type: "idle" }
| { type: "systemError" }
| {
type: "active";
activeFlags: Array<ThreadActiveFlag>;
};with:
export type ThreadActiveFlag =
| "waitingOnApproval"
| "waitingOnUserInput";The app-server also emits:
thread/status/changedwith:
export type ThreadStatusChangedNotification = {
threadId: string;
status: ThreadStatus;
};thread/list already returns the current Thread.status, so the Sessions sidebar can initialize each item immediately and then update it incrementally through thread/status/changed.
Proposed VS Code mapping
| Codex state | VS Code state | Display badge |
|---|---|---|
active with no flags |
InProgress |
Working |
active + waitingOnUserInput |
NeedsInput |
Waiting for input |
active + waitingOnApproval |
NeedsInput |
Waiting for approval |
systemError |
Failed |
Error |
idle + last turn completed |
Completed |
Completed |
idle + last turn failed |
Failed |
Failed |
idle |
neutral | Idle |
last turn interrupted |
neutral | Interrupted |
notLoaded |
neutral / previous terminal state | No live activity |
Terminal state should not be inferred from ThreadStatus::Idle alone.
Codex separately exposes turn outcome:
export type TurnStatus =
| "completed"
| "interrupted"
| "failed"
| "inProgress";The extension can therefore combine:
thread/status/changed -> live activity
turn/completed -> terminal outcomeThis should make the Sessions sidebar fully event-driven without polling or introducing another backend status model.
Additional information
Additional information
Reference TypeScript implementation
Because the VS Code extension UI code is not currently included in the public Codex repository, this is intended as a reference implementation for the internal extension-side Sessions controller.
import * as vscode from "vscode";
type ThreadActiveFlag =
| "waitingOnApproval"
| "waitingOnUserInput";
type ThreadStatus =
| { type: "notLoaded" }
| { type: "idle" }
| { type: "systemError" }
| {
type: "active";
activeFlags: ThreadActiveFlag[];
};
type TurnStatus =
| "completed"
| "interrupted"
| "failed"
| "inProgress";
interface SessionPresentation {
status?: vscode.ChatSessionStatus;
badge?: string;
tooltip: string;
}
function mapCodexSessionStatus(
threadStatus: ThreadStatus,
lastTurnStatus?: TurnStatus,
): SessionPresentation {
if (threadStatus.type === "active") {
if (
threadStatus.activeFlags.includes("waitingOnApproval")
) {
return {
status: vscode.ChatSessionStatus.NeedsInput,
badge: "Waiting for approval",
tooltip: "Codex is waiting for an approval.",
};
}
if (
threadStatus.activeFlags.includes("waitingOnUserInput")
) {
return {
status: vscode.ChatSessionStatus.NeedsInput,
badge: "Waiting for input",
tooltip: "Codex is waiting for your input.",
};
}
return {
status: vscode.ChatSessionStatus.InProgress,
badge: "Working",
tooltip: "Codex is currently working.",
};
}
if (threadStatus.type === "systemError") {
return {
status: vscode.ChatSessionStatus.Failed,
badge: "Error",
tooltip: "The Codex session encountered a system error.",
};
}
switch (lastTurnStatus) {
case "completed":
return {
status: vscode.ChatSessionStatus.Completed,
badge: "Completed",
tooltip: "The most recent Codex turn completed.",
};
case "failed":
return {
status: vscode.ChatSessionStatus.Failed,
badge: "Failed",
tooltip: "The most recent Codex turn failed.",
};
case "interrupted":
return {
badge: "Interrupted",
tooltip: "The most recent Codex turn was interrupted.",
};
}
if (threadStatus.type === "idle") {
return {
badge: "Idle",
tooltip: "This Codex session is loaded but has no active turn.",
};
}
return {
tooltip: "This Codex session is not currently loaded.",
};
}
Event-driven session updates
Keep the latest terminal turn outcome per thread:
const lastTurnStatusByThread =
new Map<string, TurnStatus>();
Then process the existing app-server events:
function handleServerNotification(
method: string,
params: unknown,
): void {
switch (method) {
case "thread/status/changed": {
const event = params as {
threadId: string;
status: ThreadStatus;
};
const thread = threads.get(event.threadId);
const item = sessionItems.get(event.threadId);
if (!thread || !item) {
return;
}
thread.status = event.status;
if (event.status.type === "active") {
lastTurnStatusByThread.delete(event.threadId);
}
updateSessionItem(item, thread);
return;
}
case "turn/completed": {
const event = params as {
threadId: string;
turn: {
status: TurnStatus;
};
};
lastTurnStatusByThread.set(
event.threadId,
event.turn.status,
);
const thread = threads.get(event.threadId);
const item = sessionItems.get(event.threadId);
if (thread && item) {
updateSessionItem(item, thread);
}
return;
}
}
}
Update the corresponding VS Code session item:
function updateSessionItem(
item: vscode.ChatSessionItem,
thread: {
id: string;
status: ThreadStatus;
},
): void {
const presentation = mapCodexSessionStatus(
thread.status,
lastTurnStatusByThread.get(thread.id),
);
item.status = presentation.status;
item.badge = presentation.badge;
item.tooltip = presentation.tooltip;
sessionController.items.add(item);
}
Remaining 5-hour / weekly usage
Codex already exposes account rate-limit information through the app-server.
A rate-limit window contains:
export type RateLimitWindow = {
usedPercent: number;
windowDurationMins: number | null;
resetsAt: number | null;
};
Remaining usage can be calculated as:
function remainingPercent(
window: RateLimitWindow,
): number {
return Math.max(
0,
Math.min(100, 100 - window.usedPercent),
);
}
The current limits can be fetched through:
account/rateLimits/read
and refreshed when receiving:
account/rateLimits/updated
A compact renderer could be:
function rateLimitLabel(
window: RateLimitWindow,
): string {
const remaining = Math.round(
remainingPercent(window),
);
if (window.windowDurationMins === 300) {
return `5h ${remaining}% left`;
}
if (
window.windowDurationMins ===
7 * 24 * 60
) {
return `week ${remaining}% left`;
}
return `${remaining}% left`;
}
Then the Sessions UI could use:
item.description = [
rateLimits.primary &&
rateLimitLabel(rateLimits.primary),
rateLimits.secondary &&
rateLimitLabel(rateLimits.secondary),
]
.filter(Boolean)
.join(" · ");
Result:
Working
5h 74% left · week 43% left
I would use:
badgefor the per-session runtime statedescriptionfor account-wide remaining usagetooltipfor state details and reset timestamps
The usage limits are account-wide rather than specific to a single thread, so they should not be represented as the session's main state.
Proposed event flow
thread/list
|
+--> initial Thread.status
|
v
VS Code Sessions sidebar
|
+-- thread/status/changed
| +-- Working
| +-- Waiting for input
| +-- Waiting for approval
| +-- Idle
| `-- Error
|
+-- turn/completed
| +-- Completed
| +-- Failed
| `-- Interrupted
|
`-- account/rateLimits/updated
`-- refresh remaining usage
The main finding is that the open-source Codex app-server already provides the necessary status and rate-limit signals. The remaining work appears to be wiring those existing events into the VS Code Sessions UI rather than introducing another extension-specific status model.
Related: #38759
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at the internal VS Code Sessions controller described in the issue and verify whether its code is available, since the extension UI is not in the public repository. Trace thread/list, thread/status/changed, turn/completed, and account/rateLimits/updated, then confirm that session badges and account-wide usage labels update for each proposed state.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust, typescript, vscode
- Domain
- developer-experience, frontend, tooling
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100