Skip to content

Keep CoS TUI agents alive across portos-server restarts #3202

Description

@atomantic

Problem

CoS has a separate portos-cos runner so agent work can remain isolated from the main PortOS web process, but direct TUI sessions currently violate that boundary.

When an agent resolves to the direct TUI path (useRunner: false), its PTY is owned by portos-server. Restarting only portos-server therefore destroys the active agent session even though portos-cos remains online and has uninterrupted uptime. The task can then be recorded as completed or enter orphan retry handling despite never completing its work.

This defeats the purpose of running CoS separately: a safe web/API restart is still destructive to active agent work.

Observed behavior

In a live reproduction:

  1. A user task spawned through the Claude TUI path in an isolated worktree.
  2. portos-cos remained online throughout.
  3. Restarting only portos-server terminated the server-owned TUI session.
  4. The persisted agent output contained only the lifecycle events for starting the TUI and pasting the prompt—no completion output.
  5. The agent record was nevertheless shown as completed; after an automatic retry was interrupted the same way, the task was blocked by orphan retry cooldown.

Identifiers, paths, host details, and task contents are intentionally omitted.

Expected behavior

A portos-server restart must not terminate an active CoS TUI agent. After the server comes back, it should reconnect to the durable agent owner, rediscover the running session, and resume relaying output/state.

A deliberate full-stack restart may still stop agents, but that should be an explicit, distinguishable operation.

Proposed direction

Move PTY/TUI process ownership out of portos-server and into the durable portos-cos runner (or another dedicated process). The server should act as a reconnectable control/relay client rather than the lifecycle owner.

Also harden recovery semantics:

  • Never infer success from a closed/disappeared PTY without a valid completion signal.
  • Classify a host-process interruption as interrupted/orphaned, not completed.
  • Preserve the task worktree/branch and resume pointer for recovery.
  • Do not consume normal agent failure/retry budget for a PortOS host restart.
  • Reconcile runner-owned active sessions when portos-server reconnects.

Acceptance criteria

  • TUI agent processes are owned by a process whose lifecycle is independent of portos-server.
  • Restarting only portos-server does not send a termination signal to active CoS TUI agents.
  • On reconnect, the server lists the still-running agent and resumes output/status relay without spawning a duplicate.
  • If an agent is genuinely interrupted, it is never marked completed without a valid completion result.
  • Interrupted tasks preserve resumable worktree/branch state and follow a non-success recovery path.
  • A server restart does not exhaust ordinary orphan/failure retry budgets.
  • Full-stack restart behavior remains explicit and gracefully terminates agents when requested.
  • Regression coverage exercises a server disconnect/reconnect while a TUI agent is running and verifies process survival plus state reconciliation.

Metadata

Metadata

Assignees

Labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions