Skip to content

Opt-in reaper for idle subscription-engine tmux sessions #141

Closed

Description

@malys

Problem

On the subscription engine, every chat keeps its tmux session (ccs-<id>) alive
indefinitely: the interactive claude plus every MCP server it spawned. Nothing
reaps an idle one; CLAUDE_IDLE_TIMEOUT_MS only bounds a single turn. Memory
therefore grows with the number of chats opened since the last restart.

Measured on Docker (CLI 2.x, a typical MCP set): ~1.2 GB per open session
(claude ~300 MB + MCP servers ~900 MB). Two idle chats ~2.4 GB, on a
12 GB host.

Proposal

An opt-in idle reaper, e.g. CCS_ENGINE_IDLE_KILL_MS (unset = current behaviour):

  • kill ccs-<id> when no turn ran and no viewer is attached for that long;
  • skip a session with a running turn, a pending interactive prompt (Terminal Access For Prompt Interactions #20), or an
    attached view=engine viewer;
  • the next message respawns with --resume <claude_session_id>, the path the
    config-change respawn already uses, so no history is lost.

Activity

  1. Lexus2016 commented on Nov 12, 2026

    @Lexus2016
    Owner

    Implemented on main in eb26917a (PR #142). It ships in the next release.

    CCS_ENGINE_IDLE_KILL_MS is opt-in — unset or 0 keeps today's behaviour. When set, a ccs-<id> pane with no turn and no attached viewer for that long is killed, and the next message respawns it with --resume <cid> — the same path the config-change respawn already uses — so no history is lost.

    The skip rules are the three you listed, plus one:

    • a running turn — read from the studio's own liveness registries (activeTasks / activeChatSessions / an in_progress Kanban task), which also covers the autoSkill window before the subprocess exists;
    • a pending interactive prompt (Terminal Access For Prompt Interactions #20) or a spinner still animating;
    • an attached view=engine viewer — and while attached the clock keeps being refreshed, so closing the pane does not hand the reaper an expired timer;
    • a pane with no recorded age (created before this feature, or after a restart that lost the tmux environment) — its clock is started instead of trusted, so nothing is killed on the first tick after an upgrade.

    The idle clock is stamped when a turn ends, not when the pane spawns, so a long first turn never counts as idle time.

    Docs list the variable in .env.example and the three READMEs. test/engine-idle-reaper.test.js covers the decision table and drives a real tmux pane, where a control-mode client must count as a viewer. CI is green on both matrix legs (Node 20 and 24).

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

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions