VS Code Sessions: surface live Codex thread status and remaining usage from existing app-server events
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
<html>
<body>
<!--StartFragment--><html><head></head><body><p>for example:</p><blockquote><p><strong>Working</strong> · 5h 74% left · week 43% left</p></blockquote><p>This is related to #38759.</p><h3>Existing Codex protocol already supports the required state</h3><p>After inspecting the current open-source <code inline="">openai/codex</code> implementation, the required backend status contract appears to already exist in <code inline="">codex app-server</code>.</p><p><code inline="">ThreadStatus</code> currently supports:</p><pre><code class="language-ts">export type ThreadStatus =
| { type: "notLoaded" }
| { type: "idle" }
| { type: "systemError" }
| {
type: "active";
activeFlags: Array<ThreadActiveFlag>;
};</code></pre><p>with:</p><pre><code class="language-ts">export type ThreadActiveFlag =
| "waitingOnApproval"
| "waitingOnUserInput";</code></pre><p>The app-server also emits:</p><pre><code class="language-text">thread/status/changed</code></pre><p>with:</p><pre><code class="language-ts">export type ThreadStatusChangedNotification = {
threadId: string;
status: ThreadStatus;
};</code></pre><p><code inline="">thread/list</code> already returns the current <code inline="">Thread.status</code>, so the Sessions sidebar can initialize each item immediately and then update it incrementally through <code inline="">thread/status/changed</code>.</p>
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 |
<p>Terminal state should not be inferred from <code inline="">ThreadStatus::Idle</code> alone.</p><p>Codex separately exposes turn outcome:</p><pre><code class="language-ts">export type TurnStatus =
| "completed"
| "interrupted"
| "failed"
| "inProgress";</code></pre><p>The extension can therefore combine:</p><pre><code class="language-text">thread/status/changed -> live activity
turn/completed -> terminal outcome</code></pre><p>This should make the Sessions sidebar fully event-driven without polling or introducing another backend status model.</p><h2>Additional information</h2>
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
1 Comment
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action