Skip to content

A terminal beside the agent: zellij in every container, generically (no dotfiles, any image) #242

Description

@blooop

Graduated from map blooop/dotfiles#13's terminal-beside-the-agent item after a live investigation (2026-08-16). The earlier shape (dotfiles#14) put this in chezmoi; this ticket replaces that approach with a devlaunch-owned one that works in any image, including third-party devcontainers we do not control.

What this buys

A terminal opened from inside the agent session, rather than by attaching a second one from elsewhere. Proven live: with zellij present in the container, a completely non-interactive command opens a working pane.

Measured facts the design rests on

The capability works with no TTY anywhere. Through real devpod ssh --command (verified [ -t 0 ]no) against mcr.microsoft.com/devcontainers/base:ubuntu:

  1. zellij attach -b agentsess — creates a detached session from a non-TTY command, exit 0.
  2. A separate non-TTY command: zellij list-sessions → the session is there.
  3. A separate non-TTY command: zellij -s agentsess action new-pane -- sh -c "touch /tmp/PANE_PROOF; sleep 30" → printed terminal_1, exit 0, and /tmp/PANE_PROOF existed with sleep 30 running.

ZELLIJ_SESSION_NAME does not need setting — -s <name> is sufficient and is the robust form. Bare zellij action new-pane also works by falling back to the sole active session, which is ambiguous once there are two; do not depend on it.

The one wrap point. dl <spec> -- <cmd> funnels through a single payload = f"bash -lc {shlex.quote(command)}" in workspace_ssh. Wrapping there covers wayfinder and aid without either changing, and because it is a login shell the PATH prepend that tool provisioning writes is already in effect. A bare dl <ws> attach passes command=None down the interactive branch and needs a second small edit in the same function.

Where zellij comes from — two routes, both measured.

Container-side pixi install (recommended). tools.py already provisions tools into any container, dotfiles-independent by design, and its fallback already bootstraps pixi (_pixi_bootstrap). zellij is a conda-forge package, and package downloads now hit the shared cache from #232/#240, so a second container onward pays extraction only. This is a third row in an existing table rather than new machinery, and it adds no new upstream dependency.

Host-side static binary mount (recorded alternative). The upstream zellij-x86_64-unknown-linux-musl release is a genuine single static file (52MB, static-pie). Verified: it runs unchanged in glibc (ubuntu:24.04) and musl (alpine) images as non-root uid 1000; devpod accepts a file-level --mount and passes it through verbatim; the binary lands executable at /usr/local/bin/zellij, which is on PATH in every image tested. Faster (no per-container work) but it needs a host-side download of a GitHub release, which is a new upstream dependency for a cost measured in seconds.

Do not try to lend the host's pixi copy. ~/.pixi/bin/zellij is a pixi trampoline, and the real binary behind it is dynamically linked against libssl/libcrypto inside its own env prefix, so it does not run when copied out. _gh_source already resolves trampolines for gh; that machinery does not rescue zellij. Only the musl release is single-file portable.

Traps already paid for elsewhere

Done when

  1. Every container devlaunch launches has a working zellij on PATH, with no dependence on the user's dotfiles and no edit to any repo's devcontainer.json. Take the container-side pixi route unless measurement contradicts the falsification condition below; record which and why.
  2. Provisioning zellij must not be able to fail a launch — the same "cost the feature, not the launch" rule the launch lock and the pixi cache already follow. A container that ends up without zellij still opens, and the wrap in (3) degrades to running the command directly.
  3. An opt-in wrap, default off: with it enabled, the command dl <spec> -- <cmd> runs inside a zellij session (zellij attach -c <session> -- <cmd> or equivalent), so zellij action new-pane works from within the agent. Default off means no existing invocation changes meaning. A bare dl <ws> attach should behave consistently with whatever is decided for the command form; say what it does either way.
  4. README documents the capability, how to enable it, the -s <name> form for opening a pane from inside a session, and that existing workspaces need dl <workspace> recreate.
  5. CHANGELOG under ## [Unreleased].

Falsification condition

If a warm container-side pixi global install zellij costs more than a few seconds, the cost stops being negligible and the static-binary mount becomes the better trade — every fact needed to switch is recorded above. Measure it and put the number in the PR; do not choose on taste.

Explicitly out of scope

Session persistence across dl <ws> recreate (zellij's serialization lives in the container and dies with it), and any host-side zellij integration — a container-side session is the only architecture that works, since an agent inside the container cannot reach a host socket.

This ticket was generated by AI during investigation follow-through; every measurement above was taken live on 2026-08-16.

Activity

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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions