Skip to content

[Windows] Completed subagents leave STDIO MCP Node.js processes running (245 processes / 18 GB) #34658

Open

Description

@zhang8630

What version of the Codex App are you using (From “About Codex” dialog)?

Codex Desktop 26.715.8383.0 (x64)

What subscription do you have?

Chatgpt pro

What platform is your computer?

Windows 11 Home, 64-bit, version 10.0.26200 (build 26200)

What issue are you seeing?

Node.js version

Node.js v24.15.0, installed at %ProgramFiles%\nodejs\node.exe
When a Codex task spawns subagents while multiple local STDIO MCP servers are enabled, each subagent starts its own copy of every enabled Node-based MCP server. After a subagent records task_complete, those MCP processes remain alive instead of being stopped.

The process count therefore grows approximately linearly with the number of subagents created during the lifetime of the Codex Desktop process. In the observed session, this reached 245 Node.js processes and approximately 18.3 GB of working-set memory.

What steps can reproduce the bug?

  1. On Windows, enable several local Node-based STDIO MCP servers. My configuration included:
    • npx -y @cyanheads/pubmed-mcp-server@latest
    • npx -y clinicaltrialsgov-mcp-server@latest
    • a local cas_journal Node MCP server
    • Node-based MCP servers bundled with the Data Analytics, Sites, and OpenAI Developers plugins
  2. Start Codex Desktop.
  3. Run a task that creates multiple subagents.
  4. Wait for the subagents to finish and record task_complete in their session transcripts.
  5. Inspect the remaining Node processes and their command lines:
$nodes = Get-CimInstance Win32_Process |
    Where-Object Name -EQ 'node.exe'

$nodes |
    Group-Object CommandLine |
    Sort-Object Count -Descending |
    Select-Object Count, Name

$workingSet = Get-Process -Name node -ErrorAction SilentlyContinue |
    Measure-Object WorkingSet64 -Sum

[math]::Round($workingSet.Sum / 1GB, 2)
  1. Observe that each new subagent adds another repeated set of MCP processes, but completing the subagent does not reduce the process count.

Expected behavior

When a subagent finishes, Codex should close or dispose of that subagent's STDIO MCP clients and terminate their child processes. Alternatively, MCP clients could be shared or pooled with a bounded lifetime across subagents.

The number of local MCP processes should not continue increasing after subagents complete.

Actual behavior

Each subagent created approximately eight Node processes:

MCP source Node processes per subagent Why
PubMed MCP 2 One npx-cli.js process and one server process
ClinicalTrials MCP 2 One npx-cli.js process and one server process
CAS Journal MCP 1 Direct Node server
Data Analytics plugin 1 Plugin-provided server.cjs
Sites plugin 1 Plugin-provided server.mjs
OpenAI Developers plugin 1 Plugin-provided server.mjs
Total 8 Per subagent/session

Observed totals:

  • 30 repeated MCP process sets × 8 Node processes = 240 Node processes attributable to Codex MCP initialization.
  • 5 additional unrelated/orphaned tests/run_fixture_validators.js processes were present, resulting in 245 Node processes overall.
  • Total Node working-set memory was approximately 18.3 GB.
  • The count remained at 245 across repeated samples several minutes apart.

Evidence that completed subagents retain their MCP processes

  • Codex Desktop's codex.exe process started at 09:23:19.
  • MCP process sets were then created repeatedly from 09:23:57 through 10:19:05.
  • Their creation timestamps closely matched the creation timestamps of subagent session files.
  • 25 inspected subagent transcripts ended with an event_msg whose payload type was task_complete.
  • The corresponding MCP process sets were still alive after those completion records.
  • For one concrete example:
    • Subagent and its MCP processes started at 09:26:18.
    • The subagent recorded task_complete at 09:30:24.
    • Its CAS Journal MCP process was still alive when checked at 10:24:23.
  • Process ancestry showed the MCP processes were direct or indirect descendants of Codex Desktop's codex.exe (some npx servers had intermediate cmd.exe and node.exe processes).

Process distribution at the time of capture

Command Count Approx. working set
PubMed server 30 3,349 MB
PubMed npx-cli.js 30 2,488 MB
ClinicalTrials server 30 3,222 MB
ClinicalTrials npx-cli.js 30 2,484 MB
Plugin server.mjs processes 60 3,007 MB
CAS Journal server 30 1,912 MB
Data Analytics server.cjs 30 1,593 MB

Why this does not appear to be a Node.js process-spawning issue

All 240 MCP-related Node processes followed the same Codex/subagent creation pattern and were launched as descendants of the active Codex Desktop process. Multiple independent MCP implementations remained alive in identical per-subagent groups. This points to the Codex session/MCP manager lifecycle rather than one MCP package spawning copies of itself.

I have not changed Node versions yet, because the multiplication is initiated by Codex when sessions/subagents are created. Testing with another supported Node version may still be useful as a secondary compatibility check.

Impact

  • Hundreds of Node processes remain after the related subagent work is complete.
  • Memory usage can grow into tens of gigabytes during multi-agent workflows.
  • The machine becomes slow and may experience memory pressure or paging.
  • Fully restarting Codex is currently the practical way to reset the accumulated MCP processes.

Additional context

The strongest current hypothesis is that each subagent initializes its own MCP manager, but the manager or its STDIO child processes are not disposed when the subagent reaches task_complete.

Additional information

No response

Activity

  1. added
    appIssues related to the Codex desktop app
    on Jul 22, 2026
  2. added
    bugSomething isn't working
    windows-osIssues related to Codex on Windows systems
    mcpIssues related to the use of model context protocol (MCP) servers
    subagentIssues involving subagents or multi-agent features
    on Jul 22, 2026
  3. zhang8630 commented on Jul 23, 2026

    @zhang8630
    Author

    Independent adjacent reproduction on Windows 11 using a separate projectless Codex task (rather than a subagent) shows the same lifecycle shape.

    Privacy-safe A/B method:

    • Process acquisition requested only Name, ProcessId, ParentProcessId, and CreationDate from Win32_Process.
    • Process instances were matched by PID plus creation time to avoid PID-reuse errors.
    • Output retained aggregate counts only. No PIDs, command lines, paths, usernames, environment data, or prompt content were recorded or shared.
    • Treatment task had no file, shell, tool, connector, or web access; it returned one fixed line and was then archived.

    Treatment result:

    Phase Node total Nearest observed codex.exe ancestor Delta vs baseline
    Baseline 305 273 0
    Active 353 321 +48
    Completed, before archive 351 319 +46
    Immediate after archive 341 309 +36
    2/4/6/8 minutes after archive 344 312 +39
    A measurement-only control used the same primary task and corresponding snapshot timings, but created no secondary task and performed no archive action. Its delta was +1 at the corresponding active point, then -5 to -6 through the eight-minute endpoint.

    Claude-attributed and unattributed Node branches remained unchanged in both series. Every immediate parent was live and its process instance validated by creation time; no invalid-instance, unknown-timestamp, or cycle case was observed.

    This supports Codex task-related Node/MCP fan-out that persists for at least eight minutes and is not reproduced by the measurement calls alone. It does not by itself prove orphan processes or a resource leak because the parent subtrees remain live and the intended pool lifetime is unknown. No processes were stopped during the experiment.

    The result suggests the retention may apply to general task/session lifecycle, not only subagents. Please let me know if a maintainer needs a specific additional lifecycle interval or version field collected in a similarly sanitized follow-up.

    Thanks for the additional reproduction. I see the same behavior: simply creating a new task, before it does any actual work, causes many new Node.js processes to appear. They are repeated instances of the configured MCP servers, so this seems related to MCP servers being started again for each task and not being cleaned up properly afterward.

  4. grtninja commented on Jul 23, 2026

    @grtninja

    Independent corroboration from a separate Windows 11 Pro / ChatGPT Pro long-running Codex Desktop session on July 23, 2026:

    • the Codex-associated process tree grew to roughly 23.8 GB RAM during a multi-task, MCP-heavy workload;
    • completed/abandoned work did not return the process tree to a normal baseline;
    • the app became unstable enough to require a hard restart;
    • the operator separately observed task/session disruption and large usage waste while the process fanout remained active.

    I did not capture a clean per-command process inventory before restarting, so this is corroborating impact evidence rather than a second exact reproduction of the 245-Node-process count. The visible symptom and recovery pattern match this issue closely: multi-agent/MCP activity accumulates unreaped descendants, memory rises into tens of gigabytes, and restarting Codex is the practical cleanup.

    A useful built-in diagnostic would expose, per root thread/subagent: MCP client PID tree, ownership, creation time, task completion time, disposal status, and aggregate working set. Codex should also enforce a bounded process/memory ceiling and terminate orphaned MCP clients after the owning subagent reaches a terminal state.

  5. SergeyPomelov commented on Aug 16, 2026

    @SergeyPomelov

    Reproduced on Windows with ChatGPT/Codex Desktop 26.810.7004.0 the best flagship vibecoded application.
    Task Manager showed 253 ChatGPT processes using 8.1 GB RAM. Shortly afterward, there were 139 node.exe processes using 12.5 GB, mostly repeated MCP servers such as GitLab, Jira, Confluence, and Chrome DevTools.

  6. oski350bpm commented on Oct 12, 2026

    @oski350bpm

    Additional macOS evidence: completed worker runtimes retained for 74 min 44 s

    This is a macOS reproduction candidate for the completed-subagent retention reported here, with ordinary desktop-thread shutdown as a positive control. I am adding the prepared report and sanitized logs here rather than opening a duplicate issue. The separate prediction-fork startup behavior is also related to #21984.

    Title: Desktop retains completed native-worker tool runtimes; eager MCP initialization also occurs in composer prediction forks

    Status: Additional macOS evidence for this completed-subagent lifecycle report. Please investigate lifecycle ownership rather than reducing worker concurrency.

    Environment

    • macOS 27.0.1 (26A434), 32 GB physical RAM.
    • Codex desktop 26.1007.30912, build 20156; configured updater reported up_to_date during the investigation.
    • Bundled CLI 0.162.0-alpha.17.2.
    • Native agents; concurrency limit 6 (preserved). stdio MCPs were configured globally.
    • Incident repeated on October 11–12, 2026, including after a computer restart.

    Actual behavior

    Completing a native worker does not appear to release its tool runtime automatically. A uniquely matched launch-time cohort of 6 tool processes was still alive 74 min 44 s after task_complete, under the same live desktop app-server. Classified processes included node_repl, CUA and codex-security. No Shutdown event for that worker was found in the inspected thread-tagged backend logs.

    This attribution is based on a uniquely matched birth-second cohort, not explicit PID-to-thread ownership metadata. It demonstrates prolonged post-completion retention; it does not establish an indefinite leak or identify the exact retaining reference.

    Ordinary desktop conversations provide a positive control: 5 inactive_thread_unsubscribed events were followed by op.dispatch.shutdown approximately 60 s later. Therefore the report is not claiming all cleanup is absent. The installed desktop 3-hour / maximum-10 inactivity policy applies to eligible owner/history streams, not automatically to all passive native workers.

    Separately, 13 automatic composer prediction forks initialized globally enabled integrations even though the user did not open those sessions or use those integrations. All 13 received Shutdown, so this is unnecessary startup churn, not proof that these 13 instances leaked indefinitely.

    Observed timeline (UTC, October 12)

    Event Time
    Worker A tool birth cohort 13:51:32
    Worker A task_complete 13:54:46.127
    Snapshot: 6 cohort processes remain 15:09:30.488
    Retention after completion 74 min 44 s

    The public evidence bundle replaces thread UUIDs with stable aliases, omits local paths/usernames, message bodies, environment variables, resume tokens and full argv. It includes the exact lifecycle timestamps and sanitized process measurements.

    Impact and limits of attribution

    The screenshot provided by the user in the conversation shows 63.32 GB system swap on a 32 GB Mac. A separate previous-day diagnostic snapshot showed approximately 950 processes in the Codex app family. Following staged removal of selected old tool runtimes, app-family counts eventually fell to about 256 and system swap from 43822.94 to 22289.12 MiB. These are observations from ongoing work, not a controlled A/B test, and system swap cannot be attributed exclusively to Codex.

    Project preview servers and the desktop renderer also contributed. A separate local project backend suffered a V8 OOM. Those project problems are being addressed independently; they should not be used to explain away the completed-worker cohort retention.

    Reproduction procedure to investigate

    1. Start the desktop app with several globally enabled stdio integrations.
    2. Record the existing app-server process tree and thread-tagged backend logs.
    3. Create a native worker with a short bounded task, and record its runtime/MCP launch timestamps and process identities.
    4. Wait for task_complete, with no followup pending and no active worker turn.
    5. Continue inspecting the same app-server subtree and thread logs after 60 s and several minutes. Record which runtime references retain the worker and whether Shutdown occurs.
    6. Use an ordinary inactive conversation unsubscribe as a positive control. Distinguish ready-status replay from new MCP starting events.
    7. Separately trace automatic composer prediction forks and verify whether their integrations are necessary before tool use.

    These steps are a proposed controlled reproduction of the observed condition, not a guarantee that every completion exhibits it.

    Expected behavior / requested fix

    • Expose a supported close/unload action for completed native agents in the desktop agent tool surface. This session exposes interrupt/followup/list/wait, but no close_agent; public CLI documentation is not a callable desktop tool.
    • Release idle native-agent runtime residency and its MCP/tool processes when no active turn, pending request or authorized followup needs them.
    • Keep persisted history readable and allow a later followup to resume with a fresh or correctly resumed runtime.
    • Avoid full MCP initialization in helper prediction forks unless required.
    • Log explicit runtime ownership and release reasons, enabling PID-to-thread verification rather than inference from birth times.
    • Do not reduce the number of useful concurrent workers as a substitute for releasing completed runtimes.

    Acceptance checks

    Completed worker → release → owned tool processes exit; transcript remains readable; later followup works; active siblings retain their runtimes. Ordinary-root positive-control shutdown still works. Deliberately handed-off project previews remain available.

    Related public reports, for comparison only:

    Sanitized evidence files from codex-lifecycle-evidence.zip are reproduced below in full. Raw local logs are not uploaded.

    The earlier temporary screenshot source was unavailable when the bundle was prepared. A new user screenshot reports continued accumulation; its numeric values are transcribed below.

    Latest user observation and read-only snapshot

    The user reports continued accumulation after a restart. New screenshot: 28.19 GB system swap, 26.76 GB used RAM, 13.3 GB compressed, 84% used. Screenshot time is unknown. These figures do not by themselves attribute swap to Codex or prove a monotonic trend. Project server lifecycle fixes have been merged separately, apply to future launches, and existing previews were deliberately kept running.

    Latest sanitized read-only snapshot
    {
      "captured_utc": "2026-10-12T19:50:10.954315+00:00",
      "platform": "Darwin 27.0.0 arm64 arm",
      "worker_cap": 6,
      "system_swap": "vm.swapusage: total = 32768.00M  used = 31937.06M  free = 830.94M  (encrypted)",
      "vm_stat": "Mach Virtual Memory Statistics: (page size of 16384 bytes)\nPages free:                                     1918.\nPages active:                                 372145.\nPages inactive:                               351757.\nPages speculative:                             19157.\nPages throttled:                                   0.\nPages wired down:                             481357.\nPages purgeable:                                   0.\n\"Translation faults\":                     1677277674.\nPages copy-on-write:                       106591457.\nPages zero filled:                        1082218693.\nPages reactivated:                         340242201.\nPages purged:                               13027144.\nFile-backed pages:                            259868.\nAnonymous pages:                              483191.\nPages stored in compressor:                  1253106.\nPages occupied by compressor:                 754428.\nDecompressions:                            271505510.\nCompressions:                              324064866.\nPageins:                                   104744290.\nPageouts:                                     205283.\nSwapins:                                     3358477.\nSwapouts:                                    6314987.\n",
      "app_family_process_count": 778,
      "user_screenshot": {
        "swap_gb": 28.19,
        "used_ram_gb": 26.76,
        "compressed_gb": 13.3,
        "memory_percent": 84,
        "capture_time": "unknown"
      },
      "limitations": [
        "family includes active tasks, previews and measurement subprocesses; count does not establish abandonment",
        "RSS does not attribute compressed/swapped memory or exclusive RAM",
        "user screenshot and current command measurements are separate observations"
      ],
      "mutations": {
        "existing_process_signals": 0,
        "server_restarts": 0,
        "config_changes": 0
      },
      "desktop_roots": [
        99504
      ],
      "current_desktop_version": {
        "CFBundleShortVersionString": "26.1007.30912",
        "CFBundleVersion": "20156"
      },
      "largest_20_family_rss_processes": [
        {
          "pid": 51517,
          "ppid": 51503,
          "rss_kib": 2201392,
          "cpu_percent": 212.6,
          "elapsed": "00:16",
          "birth": "Mon Oct 12 21:49:54 2026",
          "executable": "node"
        },
        {
          "pid": 99563,
          "ppid": 99504,
          "rss_kib": 1377376,
          "cpu_percent": 29.3,
          "elapsed": "02:46:21",
          "birth": "Mon Oct 12 19:03:49 2026",
          "executable": "Codex (Renderer)"
        },
        {
          "pid": 99564,
          "ppid": 99504,
          "rss_kib": 910512,
          "cpu_percent": 45.9,
          "elapsed": "02:46:21",
          "birth": "Mon Oct 12 19:03:49 2026",
          "executable": "codex"
        },
        {
          "pid": 99562,
          "ppid": 99504,
          "rss_kib": 461936,
          "cpu_percent": 4.6,
          "elapsed": "02:46:21",
          "birth": "Mon Oct 12 19:03:49 2026",
          "executable": "Codex (Renderer)"
        },
        {
          "pid": 99504,
          "ppid": 1,
          "rss_kib": 333280,
          "cpu_percent": 9.3,
          "elapsed": "02:46:23",
          "birth": "Mon Oct 12 19:03:47 2026",
          "executable": "ChatGPT"
        },
        {
          "pid": 31423,
          "ppid": 31403,
          "rss_kib": 149248,
          "cpu_percent": 0.1,
          "elapsed": "05:42",
          "birth": "Mon Oct 12 21:44:28 2026",
          "executable": "next-server (v16.3.3)"
        },
        {
          "pid": 30676,
          "ppid": 67608,
          "rss_kib": 125952,
          "cpu_percent": 0.0,
          "elapsed": "59:54",
          "birth": "Mon Oct 12 20:50:16 2026",
          "executable": "node"
        },
        {
          "pid": 16536,
          "ppid": 99504,
          "rss_kib": 118208,
          "cpu_percent": 0.0,
          "elapsed": "01:07:50",
          "birth": "Mon Oct 12 20:42:20 2026",
          "executable": "Codex (Renderer)"
        },
        {
          "pid": 96155,
          "ppid": 99504,
          "rss_kib": 117216,
          "cpu_percent": 0.0,
          "elapsed": "11:54",
          "birth": "Mon Oct 12 21:38:16 2026",
          "executable": "Codex (Renderer)"
        },
        {
          "pid": 68505,
          "ppid": 51378,
          "rss_kib": 81136,
          "cpu_percent": 0.0,
          "elapsed": "46:31",
          "birth": "Mon Oct 12 21:03:39 2026",
          "executable": "Google Chrome"
        },
        {
          "pid": 46955,
          "ppid": 99564,
          "rss_kib": 78064,
          "cpu_percent": 0.0,
          "elapsed": "01:17",
          "birth": "Mon Oct 12 21:48:53 2026",
          "executable": "npm exec chrome-devtools-mcp@1.6.0"
        },
        {
          "pid": 8979,
          "ppid": 99504,
          "rss_kib": 76800,
          "cpu_percent": 0.0,
          "elapsed": "02:43:40",
          "birth": "Mon Oct 12 19:06:30 2026",
          "executable": "Codex (Renderer)"
        },
        {
          "pid": 99846,
          "ppid": 99504,
          "rss_kib": 69552,
          "cpu_percent": 0.0,
          "elapsed": "02:46:15",
          "birth": "Mon Oct 12 19:03:55 2026",
          "executable": "Codex (Renderer)"
        },
        {
          "pid": 99529,
          "ppid": 99504,
          "rss_kib": 67072,
          "cpu_percent": 13.8,
          "elapsed": "02:46:23",
          "birth": "Mon Oct 12 19:03:47 2026",
          "executable": "Codex (Service)"
        },
        {
          "pid": 99530,
          "ppid": 99504,
          "rss_kib": 57040,
          "cpu_percent": 0.0,
          "elapsed": "02:46:23",
          "birth": "Mon Oct 12 19:03:47 2026",
          "executable": "Codex (Service)"
        },
        {
          "pid": 82956,
          "ppid": 99504,
          "rss_kib": 50992,
          "cpu_percent": 0.0,
          "elapsed": "02:01:43",
          "birth": "Mon Oct 12 19:48:27 2026",
          "executable": "Codex (Renderer)"
        },
        {
          "pid": 3668,
          "ppid": 99564,
          "rss_kib": 50192,
          "cpu_percent": 1.3,
          "elapsed": "02:45:41",
          "birth": "Mon Oct 12 19:04:29 2026",
          "executable": "codex-code-mode-host"
        },
        {
          "pid": 42815,
          "ppid": 34659,
          "rss_kib": 47472,
          "cpu_percent": 4.1,
          "elapsed": "24:29",
          "birth": "Mon Oct 12 21:25:41 2026",
          "executable": "codex"
        },
        {
          "pid": 45152,
          "ppid": 22281,
          "rss_kib": 46672,
          "cpu_percent": 0.0,
          "elapsed": "01:48",
          "birth": "Mon Oct 12 21:48:22 2026",
          "executable": "node"
        },
        {
          "pid": 42813,
          "ppid": 34659,
          "rss_kib": 45296,
          "cpu_percent": 2.4,
          "elapsed": "24:29",
          "birth": "Mon Oct 12 21:25:41 2026",
          "executable": "node"
        }
      ]
    }

    Original sanitized evidence bundle, reproduced inline

    The bundle SHA256 is c04ac41d53f33192aca67d637df2ec68c78279cdeb2785db4b7d6e3ac99bff8d. The manifest applies to the original archived files; the report above has only its submission status/attachment wording updated.

    SHA256.json
    {
      "REPORT.md": "a3c5e9be5bc484880759bd7cff1c8e5d35e37d2fd5c4426c55713fb256ff57c3",
      "worker-cohorts.json": "ce2c07329f46610e756ce25e55f4cb37b414e457054c9135042cd97575632822",
      "positive-controls.json": "5884b138ac4403262bb3ba4209b6f3f322c4ff6ad2aa03ac5427d43628d9ed39",
      "historical-memory.json": "576bd8f550adc2f29d4aa143b28b83fc4416966d600f1b61cdaaec2a118818bf"
    }
    
    REPORT.md
    Title: Desktop retains completed native-worker tool runtimes; eager MCP initialization also occurs in composer prediction forks
    
    Status: Prepared draft, not submitted. Please investigate lifecycle ownership rather than reducing worker concurrency.
    
    ## Environment
    
    - macOS 27.0.1 (26A434), 32 GB physical RAM.
    - Codex desktop 26.1007.30912, build 20156; configured updater reported up_to_date during the investigation.
    - Bundled CLI 0.162.0-alpha.17.2.
    - Native agents; concurrency limit 6 (preserved). stdio MCPs were configured globally.
    - Incident repeated on October 11–12, 2026, including after a computer restart.
    
    ## Actual behavior
    
    Completing a native worker does not appear to release its tool runtime automatically. A uniquely matched launch-time cohort of 6 tool processes was still alive 74 min 44 s after task_complete, under the same live desktop app-server. Classified processes included node_repl, CUA and codex-security. No Shutdown event for that worker was found in the inspected thread-tagged backend logs.
    
    This attribution is based on a uniquely matched birth-second cohort, not explicit PID-to-thread ownership metadata. It demonstrates prolonged post-completion retention; it does not establish an indefinite leak or identify the exact retaining reference.
    
    Ordinary desktop conversations provide a positive control: 5 inactive_thread_unsubscribed events were followed by op.dispatch.shutdown approximately 60 s later. Therefore the report is not claiming all cleanup is absent. The installed desktop 3-hour / maximum-10 inactivity policy applies to eligible owner/history streams, not automatically to all passive native workers.
    
    Separately, 13 automatic composer prediction forks initialized globally enabled integrations even though the user did not open those sessions or use those integrations. All 13 received Shutdown, so this is unnecessary startup churn, not proof that these 13 instances leaked indefinitely.
    
    ## Observed timeline (UTC, October 12)
    
    | Event | Time |
    |---|---|
    | Worker A tool birth cohort | 13:51:32 |
    | Worker A task_complete | 13:54:46.127 |
    | Snapshot: 6 cohort processes remain | 15:09:30.488 |
    | Retention after completion | 74 min 44 s |
    
    The public evidence bundle replaces thread UUIDs with stable aliases, omits local paths/usernames, message bodies, environment variables, resume tokens and full argv. It includes the exact lifecycle timestamps and sanitized process measurements.
    
    ## Impact and limits of attribution
    
    The screenshot provided by the user in the conversation shows 63.32 GB system swap on a 32 GB Mac. A separate previous-day diagnostic snapshot showed approximately 950 processes in the Codex app family. Following staged removal of selected old tool runtimes, app-family counts eventually fell to about 256 and system swap from 43822.94 to 22289.12 MiB. These are observations from ongoing work, not a controlled A/B test, and system swap cannot be attributed exclusively to Codex.
    
    Project preview servers and the desktop renderer also contributed. A separate local project backend suffered a V8 OOM. Those project problems are being addressed independently; they should not be used to explain away the completed-worker cohort retention.
    
    ## Reproduction procedure to investigate
    
    1. Start the desktop app with several globally enabled stdio integrations.
    2. Record the existing app-server process tree and thread-tagged backend logs.
    3. Create a native worker with a short bounded task, and record its runtime/MCP launch timestamps and process identities.
    4. Wait for task_complete, with no followup pending and no active worker turn.
    5. Continue inspecting the same app-server subtree and thread logs after 60 s and several minutes. Record which runtime references retain the worker and whether Shutdown occurs.
    6. Use an ordinary inactive conversation unsubscribe as a positive control. Distinguish ready-status replay from new MCP starting events.
    7. Separately trace automatic composer prediction forks and verify whether their integrations are necessary before tool use.
    
    These steps are a proposed controlled reproduction of the observed condition, not a guarantee that every completion exhibits it.
    
    ## Expected behavior / requested fix
    
    - Expose a supported close/unload action for completed native agents in the desktop agent tool surface. This session exposes interrupt/followup/list/wait, but no close_agent; public CLI documentation is not a callable desktop tool.
    - Release idle native-agent runtime residency and its MCP/tool processes when no active turn, pending request or authorized followup needs them.
    - Keep persisted history readable and allow a later followup to resume with a fresh or correctly resumed runtime.
    - Avoid full MCP initialization in helper prediction forks unless required.
    - Log explicit runtime ownership and release reasons, enabling PID-to-thread verification rather than inference from birth times.
    - Do not reduce the number of useful concurrent workers as a substitute for releasing completed runtimes.
    
    ## Acceptance checks
    
    Completed worker → release → owned tool processes exit; transcript remains readable; later followup works; active siblings retain their runtimes. Ordinary-root positive-control shutdown still works. Deliberately handed-off project previews remain available.
    
    Related public reports, for comparison only:
    - https://github.com/openai/codex/issues/30408
    - https://github.com/openai/codex/issues/34658
    - https://github.com/openai/codex/issues/21984
    
    Attachment: codex-lifecycle-evidence.zip. Raw local logs have not been uploaded.
    
    The temporary screenshot source file is no longer available locally, so the bundle contains measurements and sanitized lifecycle excerpts without the image.
    
    
    worker-cohorts.json
    [
      {
        "worker_thread": "thread-13",
        "completion": "2026-10-12T13:54:46.127Z",
        "snapshot_utc": "2026-10-12T15:09:30.488098+00:00",
        "association": "Birth-second cohort inference, not direct runtime ownership metadata",
        "matching_live_processes": [
          {
            "pid": 39226,
            "ppid": 1070,
            "start": "Mon Oct 12 15:51:32 2026",
            "etime": "01:17:58",
            "rss_kib": 7200
          },
          {
            "pid": 39228,
            "ppid": 1070,
            "start": "Mon Oct 12 15:51:32 2026",
            "etime": "01:17:58",
            "rss_kib": 14608
          },
          {
            "pid": 39230,
            "ppid": 1070,
            "start": "Mon Oct 12 15:51:32 2026",
            "etime": "01:17:58",
            "rss_kib": 13952
          },
          {
            "pid": 39231,
            "ppid": 1070,
            "start": "Mon Oct 12 15:51:32 2026",
            "etime": "01:17:58",
            "rss_kib": 14560
          },
          {
            "pid": 39252,
            "ppid": 1070,
            "start": "Mon Oct 12 15:51:32 2026",
            "etime": "01:17:58",
            "rss_kib": 15504
          },
          {
            "pid": 39279,
            "ppid": 39230,
            "start": "Mon Oct 12 15:51:32 2026",
            "etime": "01:17:58",
            "rss_kib": 7200
          }
        ]
      },
      {
        "worker_thread": "thread-14",
        "completion": "2026-10-12T14:04:44.688Z",
        "snapshot_utc": "2026-10-12T15:09:30.488098+00:00",
        "association": "Birth-second cohort inference, not direct runtime ownership metadata",
        "matching_live_processes": [
          {
            "pid": 53179,
            "ppid": 1070,
            "start": "Mon Oct 12 15:54:54 2026",
            "etime": "01:14:36",
            "rss_kib": 15456
          }
        ]
      }
    ]
    
    positive-controls.json
    [
      {
        "id": "thread-1",
        "desktop_unsubscribe": {
          "line": 1624,
          "text": "2026-10-12T14:58:33.120Z info [electron-message-handler] inactive_thread_unsubscribed conversationId=thread-1 rendererWebContentsId=1 rendererWindowAppearance=primary rendererWindowFocused=true rendererWindowId=1 rendererWindowVisible=true status=unsubscribed"
        },
        "app_server_shutdown": [
          {
            "utc": "2026-10-12T13:42:38.215354+00:00",
            "target": "codex_core::session::handlers",
            "body": "session_loop{thread_id=thread-1}: Submission sub=Submission { id: \"thread-2\", op: Shutdown, turn_extension_init: None, trace: None, parent_turn_id: None, root_turn_id: None, residency_guard: None }"
          },
          {
            "utc": "2026-10-12T14:59:33.100827+00:00",
            "target": "codex_core::session::handlers",
            "body": "session_loop{thread_id=thread-1}: Submission sub=Submission { id: \"thread-3\", op: Shutdown, turn_extension_init: None, trace: None, parent_turn_id: None, root_turn_id: None, residency_guard: None }"
          },
          {
            "utc": "2026-10-12T14:59:33.102281+00:00",
            "target": "codex_core::session::handlers",
            "body": "session_loop{thread_id=thread-1}:submission_dispatch{otel.name=\"op.dispatch.shutdown\" submission.id=\"thread-3\" codex.op=\"shutdown\"}: Shutting down Codex instance"
          }
        ],
        "prior_ableton_matches": [
          {
            "pid": 4495,
            "birth": "2026-10-12T13:45:47+00:00",
            "absent_now": true
          },
          {
            "pid": 4832,
            "birth": "2026-10-12T13:45:49+00:00",
            "absent_now": true
          }
        ]
      },
      {
        "id": "thread-4",
        "desktop_unsubscribe": {
          "line": 1812,
          "text": "2026-10-12T14:59:06.043Z info [electron-message-handler] inactive_thread_unsubscribed conversationId=thread-4 rendererWebContentsId=1 rendererWindowAppearance=primary rendererWindowFocused=true rendererWindowId=1 rendererWindowVisible=true status=unsubscribed"
        },
        "app_server_shutdown": [
          {
            "utc": "2026-10-12T13:42:37.076237+00:00",
            "target": "codex_core::session::handlers",
            "body": "session_loop{thread_id=thread-4}: Submission sub=Submission { id: \"thread-5\", op: Shutdown, turn_extension_init: None, trace: None, parent_turn_id: None, root_turn_id: None, residency_guard: None }"
          },
          {
            "utc": "2026-10-12T15:00:06.022654+00:00",
            "target": "codex_core::session::handlers",
            "body": "session_loop{thread_id=thread-4}: Submission sub=Submission { id: \"thread-6\", op: Shutdown, turn_extension_init: None, trace: None, parent_turn_id: None, root_turn_id: None, residency_guard: None }"
          },
          {
            "utc": "2026-10-12T15:00:06.023768+00:00",
            "target": "codex_core::session::handlers",
            "body": "session_loop{thread_id=thread-4}:submission_dispatch{otel.name=\"op.dispatch.shutdown\" submission.id=\"thread-6\" codex.op=\"shutdown\"}: Shutting down Codex instance"
          }
        ],
        "prior_ableton_matches": [
          {
            "pid": 4832,
            "birth": "2026-10-12T13:45:49+00:00",
            "absent_now": true
          }
        ]
      },
      {
        "id": "thread-7",
        "desktop_unsubscribe": {
          "line": 21759,
          "text": "2026-10-12T14:18:04.328Z info [electron-message-handler] inactive_thread_unsubscribed conversationId=thread-7 rendererWebContentsId=1 rendererWindowAppearance=primary rendererWindowFocused=true rendererWindowId=1 rendererWindowVisible=true status=unsubscribed"
        },
        "app_server_shutdown": [
          {
            "utc": "2026-10-12T14:19:04.325733+00:00",
            "target": "codex_core::session::handlers",
            "body": "session_loop{thread_id=thread-7}: Submission sub=Submission { id: \"thread-8\", op: Shutdown, turn_extension_init: None, trace: None, parent_turn_id: None, root_turn_id: None, residency_guard: None }"
          },
          {
            "utc": "2026-10-12T14:19:04.328921+00:00",
            "target": "codex_core::session::handlers",
            "body": "session_loop{thread_id=thread-7}:submission_dispatch{otel.name=\"op.dispatch.shutdown\" submission.id=\"thread-8\" codex.op=\"shutdown\"}: Shutting down Codex instance"
          }
        ],
        "prior_ableton_matches": [
          {
            "pid": 1871,
            "birth": "2026-10-12T13:45:02+00:00",
            "absent_now": true
          },
          {
            "pid": 1928,
            "birth": "2026-10-12T13:45:02+00:00",
            "absent_now": true
          },
          {
            "pid": 2195,
            "birth": "2026-10-12T13:45:03+00:00",
            "absent_now": true
          }
        ]
      },
      {
        "id": "thread-9",
        "desktop_unsubscribe": {
          "line": 25027,
          "text": "2026-10-12T14:29:27.837Z info [electron-message-handler] inactive_thread_unsubscribed conversationId=thread-9 rendererWebContentsId=1 rendererWindowAppearance=primary rendererWindowFocused=true rendererWindowId=1 rendererWindowVisible=true status=unsubscribed"
        },
        "app_server_shutdown": [
          {
            "utc": "2026-10-12T14:30:27.833268+00:00",
            "target": "codex_core::session::handlers",
            "body": "session_loop{thread_id=thread-9}: Submission sub=Submission { id: \"thread-10\", op: Shutdown, turn_extension_init: None, trace: None, parent_turn_id: None, root_turn_id: None, residency_guard: None }"
          },
          {
            "utc": "2026-10-12T14:30:27.836886+00:00",
            "target": "codex_core::session::handlers",
            "body": "session_loop{thread_id=thread-9}:submission_dispatch{otel.name=\"op.dispatch.shutdown\" submission.id=\"thread-10\" codex.op=\"shutdown\"}: Shutting down Codex instance"
          }
        ],
        "prior_ableton_matches": [
          {
            "pid": 2195,
            "birth": "2026-10-12T13:45:03+00:00",
            "absent_now": true
          }
        ]
      },
      {
        "id": "thread-11",
        "desktop_unsubscribe": {
          "line": 25068,
          "text": "2026-10-12T14:29:52.496Z info [electron-message-handler] inactive_thread_unsubscribed conversationId=thread-11 rendererWebContentsId=1 rendererWindowAppearance=primary rendererWindowFocused=true rendererWindowId=1 rendererWindowVisible=true status=unsubscribed"
        },
        "app_server_shutdown": [
          {
            "utc": "2026-10-12T14:30:52.500898+00:00",
            "target": "codex_core::session::handlers",
            "body": "session_loop{thread_id=thread-11}: Submission sub=Submission { id: \"thread-12\", op: Shutdown, turn_extension_init: None, trace: None, parent_turn_id: None, root_turn_id: None, residency_guard: None }"
          },
          {
            "utc": "2026-10-12T14:30:52.503998+00:00",
            "target": "codex_core::session::handlers",
            "body": "session_loop{thread_id=thread-11}:submission_dispatch{otel.name=\"op.dispatch.shutdown\" submission.id=\"thread-12\" codex.op=\"shutdown\"}: Shutting down Codex instance"
          }
        ],
        "prior_ableton_matches": [
          {
            "pid": 3805,
            "birth": "2026-10-12T13:45:41+00:00",
            "absent_now": true
          },
          {
            "pid": 4088,
            "birth": "2026-10-12T13:45:44+00:00",
            "absent_now": true
          }
        ]
      }
    ]
    
    historical-memory.json
    {
      "before": {
        "line": 36,
        "swap_mib": 43822.94,
        "system_processes": 1870,
        "threads": 16242,
        "pressure_level": 2,
        "app_family_approx": 950,
        "largest_renderer_top_mem_mib": 4425,
        "largest_renderer_peak_cpu_percent_of_one_core": 205.7,
        "flow_preview_top_mem_mib": 4008,
        "zr_preview_top_mem_mib": 2680
      },
      "after": {
        "line": 404,
        "swap_mib": 22289.12,
        "system_processes": 1198,
        "threads": 10182,
        "cpu_idle_percent": 0.3,
        "app_family_approx": 256
      },
      "limitations": [
        "top MEM footprint and ps RSS are different accounting measures; do not sum them as exclusive RAM",
        "before and after snapshots include ongoing unrelated work; not a controlled test",
        "swap is system-wide; cleanup benefit does not attribute all swap to one process family"
      ]
    }
    

    Follow-up: supported manual archival releases completed runtimes

    We have now verified a safe manual workaround on the installed desktop build: target an idle completed session using the supported set_thread_archived action. This preserves the transcript in the archive and lets the backend resolve resource ownership instead of guessing PID ownership.

    Bulk readback: 37 sessions (34 native workers, 3 idle older root conversations), with 37 explicit op.dispatch.shutdown events, 37 teardown records stating had_active_turn=false, no had_active_turn=true, and 37 final API states notLoaded. All 37 archived transcripts remain available. Worktree owners, scheduled/pinned tasks, unfinished goals, active/pending turns, active descendants and preview owners were excluded. No manual PID signals, server restarts or configuration changes were made; concurrency remains 6 and all 9 protected preview process identities were preserved.

    Observed app-family counts fell from 852 before the batch to 570 afterwards. System swap was 37360.75 MiB earlier, reached 25815.25 MiB during cleanup, and was 27656 MiB at 20:28:18 UTC. The final kernel pressure level was normal, but swap I/O continued while other active conversations worked. These are concurrent observations, not a controlled attribution of all system swap or all exits to this batch.

    This corrects the earlier evidence boundary: a supported manual archive/unload path exists and demonstrably works. The remaining bug is that task_complete does not automatically perform equivalent runtime release; a dedicated close/unload operation preserving normal session-list placement would still be preferable to requiring archival. No automatic reaper or app patch was installed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    appIssues related to the Codex desktop appbugSomething isn't workingmcpIssues related to the use of model context protocol (MCP) serversperformancesubagentIssues involving subagents or multi-agent featureswindows-osIssues related to Codex on Windows systems

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions