VS Code Sessions: surface live Codex thread status and remaining usage from existing app-server events

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

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&lt;ThreadActiveFlag&gt;;
};</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 -&gt; live activity
turn/completed -&gt; 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:

  • badge for the per-session runtime state
  • description for account-wide remaining usage
  • tooltip for 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

View original on GitHub ↗

1 Comment

github-actions[bot] contributor · 11 days ago

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

  • #38759

Powered by Codex Action