Skip to content

multiplexer

The tmux abstraction: pane discovery, window splitting, keystroke delivery, and capture used by the spawn and push-notification paths. Read this page to change how cafleet drives tmux. Like every API page, it is for contributors changing cafleet and embedders driving it from Python; CLI users find the command surface in CLI options.

base

Multiplexer Protocol contract for terminal-pane hosting backends.

The :class:Multiplexer Protocol defines the surface CAFleet uses to spawn coding-agent panes, push keystrokes for message delivery, and reap dead panes. Only the tmux impl is currently shipped (cafleet.multiplexer.tmux), but the Protocol exists so alternative backends (e.g. a screen-based or in-process fake) can be substituted under test or in future host environments.

MultiplexerContext dataclass

Resolved pane identity, returned by Multiplexer.context_discovery().

Attributes:

Name Type Description
session str

Multiplexer session name (e.g. tmux session_name).

window_id str

Multiplexer window id (e.g. tmux @N).

pane_id str

Multiplexer pane id (e.g. tmux %N).

Source code in cafleet/src/cafleet/multiplexer/base.py
@dataclass(frozen=True)
class MultiplexerContext:
    """Resolved pane identity, returned by ``Multiplexer.context_discovery()``.

    Attributes:
        session: Multiplexer session name (e.g. tmux ``session_name``).
        window_id: Multiplexer window id (e.g. tmux ``@N``).
        pane_id: Multiplexer pane id (e.g. tmux ``%N``).
    """

    session: str
    window_id: str
    pane_id: str

Multiplexer

Bases: Protocol

Terminal multiplexer that hosts coding-agent panes.

Source code in cafleet/src/cafleet/multiplexer/base.py
@runtime_checkable
class Multiplexer(Protocol):
    """Terminal multiplexer that hosts coding-agent panes."""

    @property
    def name(self) -> str:
        """Registry key (e.g. ``"tmux"``)."""
        ...

    def ensure_available(self) -> None:
        """Raise if the multiplexer binary is missing or the runtime context
        is not a live multiplexer session.

        Raises:
            Exception: A backend-specific exception when the precondition
                fails (e.g. :class:`TmuxError` from
                :class:`~cafleet.multiplexer.tmux.TmuxMultiplexer`).
        """
        ...

    def context_discovery(self) -> MultiplexerContext:
        """Resolve the calling shell's pane identity.

        Returns:
            :class:`MultiplexerContext` carrying ``session``, ``window_id``,
            and ``pane_id`` for the pane the caller is running in.
        """
        ...

    def split_window(
        self,
        *,
        target_window_id: str,
        env: dict[str, str],
        command: list[str],
    ) -> str:
        """Spawn a new pane inside ``target_window_id`` running ``command``.

        Args:
            target_window_id: Multiplexer window id to split.
            env: Extra environment variables exported into the new pane.
            command: argv list executed in the new pane.

        Returns:
            The new pane's id (e.g. tmux ``%N``).
        """
        ...

    def kill_pane(self, *, target_pane_id: str, ignore_missing: bool = False) -> None:
        """Forcibly close ``target_pane_id``.

        Args:
            target_pane_id: Pane id to kill.
            ignore_missing: If True, do not raise when the pane is already
                gone — useful for idempotent teardown paths.
        """
        ...

    def pane_exists(self, *, target_pane_id: str) -> bool:
        """Return True iff ``target_pane_id`` is currently alive."""
        ...

    def list_pane_ids(self) -> set[str]:
        """Return the set of every live pane id across the multiplexer server.

        The per-tick liveness query for ``cafleet monitor``: one call resolves
        pane liveness for every agent in a tick (e.g. ``tmux list-panes -a``).

        Returns:
            A set of pane ids (e.g. ``{"%0", "%7"}``).
        """
        ...

    def wait_for_pane_gone(
        self,
        *,
        target_pane_id: str,
        timeout: float = 15.0,
        interval: float = 0.5,
    ) -> bool:
        """Block until ``target_pane_id`` disappears or ``timeout`` elapses.

        Args:
            target_pane_id: Pane id to watch.
            timeout: Maximum seconds to wait.
            interval: Poll interval in seconds.

        Returns:
            ``True`` if the pane disappeared before ``timeout``, ``False``
            on timeout.
        """
        ...

    def send_exit(self, *, target_pane_id: str, ignore_missing: bool = False) -> None:
        """Push ``/exit`` + Enter into the pane to gracefully terminate the
        coding-agent process.

        Args:
            target_pane_id: Pane id to signal.
            ignore_missing: If True, do not raise when the pane is already
                gone.
        """
        ...

    def send_poll_trigger(
        self, *, target_pane_id: str, fleet_id: int, agent_id: int
    ) -> bool:
        """Keystroke a ``cafleet message poll`` shortcut into the pane.

        Args:
            target_pane_id: Pane id of the agent to nudge.
            fleet_id: Fleet id embedded in the keystroked command.
            agent_id: Recipient agent id embedded in the keystroked
                command.

        Returns:
            ``True`` if the keystroke landed; ``False`` if the pane is dead
            or the multiplexer is unreachable.
        """
        ...

    def send_resume_trigger(
        self, *, target_pane_id: str, fleet_id: int, agent_id: int
    ) -> bool:
        """Keystroke a single-line *resume nudge* into a member's pane.

        Unlike :meth:`send_poll_trigger` (a bare ``cafleet … message poll``),
        this carries the poll command **plus** an instruction to review the
        member's current task and continue working — so a member that
        unexpectedly stopped resumes rather than going idle on an empty inbox.
        The Director receives the bare poll-trigger; only members receive this.

        Args:
            target_pane_id: Pane id of the member to nudge.
            fleet_id: Fleet id embedded in the keystroked poll command.
            agent_id: Recipient agent id embedded in the keystroked poll
                command.

        Returns:
            ``True`` if the keystroke landed; ``False`` if the pane is dead or
            the multiplexer is unreachable.
        """
        ...

    def send_inline_preview(
        self,
        *,
        target_pane_id: str,
        task_id: int,
        sender_id: int,
        ts: str,
        text: str,
    ) -> bool:
        """Keystroke a 2-line message preview into the recipient's pane.

        The first line carries an ``[cafleet msg <task_id> from <sender_id>
        <ts>]`` header; the second line carries the (possibly truncated)
        body. The recipient's coding agent processes the keystrokes as a
        fresh user-turn input.

        Args:
            target_pane_id: Recipient pane id.
            task_id: Task id of the delivered message.
            sender_id: Sender's agent id.
            ts: Status timestamp string included in the header.
            text: Message body (caller is responsible for truncation).

        Returns:
            ``True`` if the keystroke landed; ``False`` otherwise.
        """
        ...

    def send_choice_key(self, *, target_pane_id: str, digit: int) -> None:
        """Keystroke a single digit (no Enter) into the pane.

        Used by Director-driven AskUserQuestion answers to select one of the
        first three options. The tmux backend rejects digits outside
        ``{1, 2, 3}``. Enter is not sent because the AskUserQuestion frame
        commits the selection on digit press.

        Args:
            target_pane_id: Recipient pane id.
            digit: Decimal digit ``1``, ``2``, or ``3``.
        """
        ...

    def send_freetext_and_submit(self, *, target_pane_id: str, text: str) -> None:
        """Keystroke ``4`` + literal ``text`` + Enter into the pane.

        Used by Director-driven AskUserQuestion freetext answers. The leading
        ``4`` selects the "Type something" option in the AskUserQuestion
        frame; the literal text is then typed in and Enter submits it. The
        tmux backend rejects newlines in ``text`` so each call is exactly
        one prompt submission.

        Args:
            target_pane_id: Recipient pane id.
            text: Literal text to type into the pane (no newlines).
        """
        ...

    def send_bash_command(self, *, target_pane_id: str, command: str) -> None:
        """Keystroke ``! <command>`` + Enter into the pane.

        Uses the leading-``!`` shell-shortcut convention honored by every
        supported coding-agent backend (``claude``, ``codex``, ``opencode``),
        so a single keystroke path serves all backends.

        Args:
            target_pane_id: Recipient pane id.
            command: Shell command to dispatch on the member's behalf.
        """
        ...

    def capture_pane(self, *, target_pane_id: str, lines: int = 30) -> str:
        """Return the last ``lines`` of the pane's visible buffer as text.

        Args:
            target_pane_id: Pane id to capture from.
            lines: Number of trailing lines to capture (default 30).

        Returns:
            Captured pane text, newline-joined.
        """
        ...

name property

name: str

Registry key (e.g. "tmux").

ensure_available

ensure_available() -> None

Raise if the multiplexer binary is missing or the runtime context is not a live multiplexer session.

Raises:

Type Description
Exception

A backend-specific exception when the precondition fails (e.g. :class:TmuxError from :class:~cafleet.multiplexer.tmux.TmuxMultiplexer).

Source code in cafleet/src/cafleet/multiplexer/base.py
def ensure_available(self) -> None:
    """Raise if the multiplexer binary is missing or the runtime context
    is not a live multiplexer session.

    Raises:
        Exception: A backend-specific exception when the precondition
            fails (e.g. :class:`TmuxError` from
            :class:`~cafleet.multiplexer.tmux.TmuxMultiplexer`).
    """
    ...

context_discovery

context_discovery() -> MultiplexerContext

Resolve the calling shell's pane identity.

Returns:

Type Description
MultiplexerContext

class:MultiplexerContext carrying session, window_id,

MultiplexerContext

and pane_id for the pane the caller is running in.

Source code in cafleet/src/cafleet/multiplexer/base.py
def context_discovery(self) -> MultiplexerContext:
    """Resolve the calling shell's pane identity.

    Returns:
        :class:`MultiplexerContext` carrying ``session``, ``window_id``,
        and ``pane_id`` for the pane the caller is running in.
    """
    ...

split_window

split_window(*, target_window_id: str, env: dict[str, str], command: list[str]) -> str

Spawn a new pane inside target_window_id running command.

Parameters:

Name Type Description Default
target_window_id str

Multiplexer window id to split.

required
env dict[str, str]

Extra environment variables exported into the new pane.

required
command list[str]

argv list executed in the new pane.

required

Returns:

Type Description
str

The new pane's id (e.g. tmux %N).

Source code in cafleet/src/cafleet/multiplexer/base.py
def split_window(
    self,
    *,
    target_window_id: str,
    env: dict[str, str],
    command: list[str],
) -> str:
    """Spawn a new pane inside ``target_window_id`` running ``command``.

    Args:
        target_window_id: Multiplexer window id to split.
        env: Extra environment variables exported into the new pane.
        command: argv list executed in the new pane.

    Returns:
        The new pane's id (e.g. tmux ``%N``).
    """
    ...

kill_pane

kill_pane(*, target_pane_id: str, ignore_missing: bool = False) -> None

Forcibly close target_pane_id.

Parameters:

Name Type Description Default
target_pane_id str

Pane id to kill.

required
ignore_missing bool

If True, do not raise when the pane is already gone — useful for idempotent teardown paths.

False
Source code in cafleet/src/cafleet/multiplexer/base.py
def kill_pane(self, *, target_pane_id: str, ignore_missing: bool = False) -> None:
    """Forcibly close ``target_pane_id``.

    Args:
        target_pane_id: Pane id to kill.
        ignore_missing: If True, do not raise when the pane is already
            gone — useful for idempotent teardown paths.
    """
    ...

pane_exists

pane_exists(*, target_pane_id: str) -> bool

Return True iff target_pane_id is currently alive.

Source code in cafleet/src/cafleet/multiplexer/base.py
def pane_exists(self, *, target_pane_id: str) -> bool:
    """Return True iff ``target_pane_id`` is currently alive."""
    ...

list_pane_ids

list_pane_ids() -> set[str]

Return the set of every live pane id across the multiplexer server.

The per-tick liveness query for cafleet monitor: one call resolves pane liveness for every agent in a tick (e.g. tmux list-panes -a).

Returns:

Type Description
set[str]

A set of pane ids (e.g. {"%0", "%7"}).

Source code in cafleet/src/cafleet/multiplexer/base.py
def list_pane_ids(self) -> set[str]:
    """Return the set of every live pane id across the multiplexer server.

    The per-tick liveness query for ``cafleet monitor``: one call resolves
    pane liveness for every agent in a tick (e.g. ``tmux list-panes -a``).

    Returns:
        A set of pane ids (e.g. ``{"%0", "%7"}``).
    """
    ...

wait_for_pane_gone

wait_for_pane_gone(*, target_pane_id: str, timeout: float = 15.0, interval: float = 0.5) -> bool

Block until target_pane_id disappears or timeout elapses.

Parameters:

Name Type Description Default
target_pane_id str

Pane id to watch.

required
timeout float

Maximum seconds to wait.

15.0
interval float

Poll interval in seconds.

0.5

Returns:

Type Description
bool

True if the pane disappeared before timeout, False

bool

on timeout.

Source code in cafleet/src/cafleet/multiplexer/base.py
def wait_for_pane_gone(
    self,
    *,
    target_pane_id: str,
    timeout: float = 15.0,
    interval: float = 0.5,
) -> bool:
    """Block until ``target_pane_id`` disappears or ``timeout`` elapses.

    Args:
        target_pane_id: Pane id to watch.
        timeout: Maximum seconds to wait.
        interval: Poll interval in seconds.

    Returns:
        ``True`` if the pane disappeared before ``timeout``, ``False``
        on timeout.
    """
    ...

send_exit

send_exit(*, target_pane_id: str, ignore_missing: bool = False) -> None

Push /exit + Enter into the pane to gracefully terminate the coding-agent process.

Parameters:

Name Type Description Default
target_pane_id str

Pane id to signal.

required
ignore_missing bool

If True, do not raise when the pane is already gone.

False
Source code in cafleet/src/cafleet/multiplexer/base.py
def send_exit(self, *, target_pane_id: str, ignore_missing: bool = False) -> None:
    """Push ``/exit`` + Enter into the pane to gracefully terminate the
    coding-agent process.

    Args:
        target_pane_id: Pane id to signal.
        ignore_missing: If True, do not raise when the pane is already
            gone.
    """
    ...

send_poll_trigger

send_poll_trigger(*, target_pane_id: str, fleet_id: int, agent_id: int) -> bool

Keystroke a cafleet message poll shortcut into the pane.

Parameters:

Name Type Description Default
target_pane_id str

Pane id of the agent to nudge.

required
fleet_id int

Fleet id embedded in the keystroked command.

required
agent_id int

Recipient agent id embedded in the keystroked command.

required

Returns:

Type Description
bool

True if the keystroke landed; False if the pane is dead

bool

or the multiplexer is unreachable.

Source code in cafleet/src/cafleet/multiplexer/base.py
def send_poll_trigger(
    self, *, target_pane_id: str, fleet_id: int, agent_id: int
) -> bool:
    """Keystroke a ``cafleet message poll`` shortcut into the pane.

    Args:
        target_pane_id: Pane id of the agent to nudge.
        fleet_id: Fleet id embedded in the keystroked command.
        agent_id: Recipient agent id embedded in the keystroked
            command.

    Returns:
        ``True`` if the keystroke landed; ``False`` if the pane is dead
        or the multiplexer is unreachable.
    """
    ...

send_resume_trigger

send_resume_trigger(*, target_pane_id: str, fleet_id: int, agent_id: int) -> bool

Keystroke a single-line resume nudge into a member's pane.

Unlike :meth:send_poll_trigger (a bare cafleet … message poll), this carries the poll command plus an instruction to review the member's current task and continue working — so a member that unexpectedly stopped resumes rather than going idle on an empty inbox. The Director receives the bare poll-trigger; only members receive this.

Parameters:

Name Type Description Default
target_pane_id str

Pane id of the member to nudge.

required
fleet_id int

Fleet id embedded in the keystroked poll command.

required
agent_id int

Recipient agent id embedded in the keystroked poll command.

required

Returns:

Type Description
bool

True if the keystroke landed; False if the pane is dead or

bool

the multiplexer is unreachable.

Source code in cafleet/src/cafleet/multiplexer/base.py
def send_resume_trigger(
    self, *, target_pane_id: str, fleet_id: int, agent_id: int
) -> bool:
    """Keystroke a single-line *resume nudge* into a member's pane.

    Unlike :meth:`send_poll_trigger` (a bare ``cafleet … message poll``),
    this carries the poll command **plus** an instruction to review the
    member's current task and continue working — so a member that
    unexpectedly stopped resumes rather than going idle on an empty inbox.
    The Director receives the bare poll-trigger; only members receive this.

    Args:
        target_pane_id: Pane id of the member to nudge.
        fleet_id: Fleet id embedded in the keystroked poll command.
        agent_id: Recipient agent id embedded in the keystroked poll
            command.

    Returns:
        ``True`` if the keystroke landed; ``False`` if the pane is dead or
        the multiplexer is unreachable.
    """
    ...

send_inline_preview

send_inline_preview(*, target_pane_id: str, task_id: int, sender_id: int, ts: str, text: str) -> bool

Keystroke a 2-line message preview into the recipient's pane.

The first line carries an [cafleet msg <task_id> from <sender_id> <ts>] header; the second line carries the (possibly truncated) body. The recipient's coding agent processes the keystrokes as a fresh user-turn input.

Parameters:

Name Type Description Default
target_pane_id str

Recipient pane id.

required
task_id int

Task id of the delivered message.

required
sender_id int

Sender's agent id.

required
ts str

Status timestamp string included in the header.

required
text str

Message body (caller is responsible for truncation).

required

Returns:

Type Description
bool

True if the keystroke landed; False otherwise.

Source code in cafleet/src/cafleet/multiplexer/base.py
def send_inline_preview(
    self,
    *,
    target_pane_id: str,
    task_id: int,
    sender_id: int,
    ts: str,
    text: str,
) -> bool:
    """Keystroke a 2-line message preview into the recipient's pane.

    The first line carries an ``[cafleet msg <task_id> from <sender_id>
    <ts>]`` header; the second line carries the (possibly truncated)
    body. The recipient's coding agent processes the keystrokes as a
    fresh user-turn input.

    Args:
        target_pane_id: Recipient pane id.
        task_id: Task id of the delivered message.
        sender_id: Sender's agent id.
        ts: Status timestamp string included in the header.
        text: Message body (caller is responsible for truncation).

    Returns:
        ``True`` if the keystroke landed; ``False`` otherwise.
    """
    ...

send_choice_key

send_choice_key(*, target_pane_id: str, digit: int) -> None

Keystroke a single digit (no Enter) into the pane.

Used by Director-driven AskUserQuestion answers to select one of the first three options. The tmux backend rejects digits outside {1, 2, 3}. Enter is not sent because the AskUserQuestion frame commits the selection on digit press.

Parameters:

Name Type Description Default
target_pane_id str

Recipient pane id.

required
digit int

Decimal digit 1, 2, or 3.

required
Source code in cafleet/src/cafleet/multiplexer/base.py
def send_choice_key(self, *, target_pane_id: str, digit: int) -> None:
    """Keystroke a single digit (no Enter) into the pane.

    Used by Director-driven AskUserQuestion answers to select one of the
    first three options. The tmux backend rejects digits outside
    ``{1, 2, 3}``. Enter is not sent because the AskUserQuestion frame
    commits the selection on digit press.

    Args:
        target_pane_id: Recipient pane id.
        digit: Decimal digit ``1``, ``2``, or ``3``.
    """
    ...

send_freetext_and_submit

send_freetext_and_submit(*, target_pane_id: str, text: str) -> None

Keystroke 4 + literal text + Enter into the pane.

Used by Director-driven AskUserQuestion freetext answers. The leading 4 selects the "Type something" option in the AskUserQuestion frame; the literal text is then typed in and Enter submits it. The tmux backend rejects newlines in text so each call is exactly one prompt submission.

Parameters:

Name Type Description Default
target_pane_id str

Recipient pane id.

required
text str

Literal text to type into the pane (no newlines).

required
Source code in cafleet/src/cafleet/multiplexer/base.py
def send_freetext_and_submit(self, *, target_pane_id: str, text: str) -> None:
    """Keystroke ``4`` + literal ``text`` + Enter into the pane.

    Used by Director-driven AskUserQuestion freetext answers. The leading
    ``4`` selects the "Type something" option in the AskUserQuestion
    frame; the literal text is then typed in and Enter submits it. The
    tmux backend rejects newlines in ``text`` so each call is exactly
    one prompt submission.

    Args:
        target_pane_id: Recipient pane id.
        text: Literal text to type into the pane (no newlines).
    """
    ...

send_bash_command

send_bash_command(*, target_pane_id: str, command: str) -> None

Keystroke ! <command> + Enter into the pane.

Uses the leading-! shell-shortcut convention honored by every supported coding-agent backend (claude, codex, opencode), so a single keystroke path serves all backends.

Parameters:

Name Type Description Default
target_pane_id str

Recipient pane id.

required
command str

Shell command to dispatch on the member's behalf.

required
Source code in cafleet/src/cafleet/multiplexer/base.py
def send_bash_command(self, *, target_pane_id: str, command: str) -> None:
    """Keystroke ``! <command>`` + Enter into the pane.

    Uses the leading-``!`` shell-shortcut convention honored by every
    supported coding-agent backend (``claude``, ``codex``, ``opencode``),
    so a single keystroke path serves all backends.

    Args:
        target_pane_id: Recipient pane id.
        command: Shell command to dispatch on the member's behalf.
    """
    ...

capture_pane

capture_pane(*, target_pane_id: str, lines: int = 30) -> str

Return the last lines of the pane's visible buffer as text.

Parameters:

Name Type Description Default
target_pane_id str

Pane id to capture from.

required
lines int

Number of trailing lines to capture (default 30).

30

Returns:

Type Description
str

Captured pane text, newline-joined.

Source code in cafleet/src/cafleet/multiplexer/base.py
def capture_pane(self, *, target_pane_id: str, lines: int = 30) -> str:
    """Return the last ``lines`` of the pane's visible buffer as text.

    Args:
        target_pane_id: Pane id to capture from.
        lines: Number of trailing lines to capture (default 30).

    Returns:
        Captured pane text, newline-joined.
    """
    ...

poll_until_pane_gone

poll_until_pane_gone(pane_exists_fn: Callable[[], bool], *, timeout: float, interval: float) -> bool

Generic poll-until-False helper for any Multiplexer's wait_for_pane_gone.

Parameters:

Name Type Description Default
pane_exists_fn Callable[[], bool]

Zero-arg callable returning whether the pane is still alive.

required
timeout float

Maximum seconds to wait.

required
interval float

Poll interval in seconds.

required

Returns:

Type Description
bool

True if pane_exists_fn returned False before timeout,

bool

False on timeout.

Source code in cafleet/src/cafleet/multiplexer/base.py
def poll_until_pane_gone(
    pane_exists_fn: Callable[[], bool],
    *,
    timeout: float,
    interval: float,
) -> bool:
    """Generic poll-until-False helper for any Multiplexer's ``wait_for_pane_gone``.

    Args:
        pane_exists_fn: Zero-arg callable returning whether the pane is
            still alive.
        timeout: Maximum seconds to wait.
        interval: Poll interval in seconds.

    Returns:
        ``True`` if ``pane_exists_fn`` returned False before ``timeout``,
        ``False`` on timeout.
    """
    deadline = time.monotonic() + timeout
    while True:
        if not pane_exists_fn():
            return True
        if time.monotonic() >= deadline:
            return False
        time.sleep(interval)