Skip to content

Cloud: fall back to xterm-256color when the box lacks the host TERM#115

Merged
madarco merged 2 commits into
nightlyfrom
fix/cloud-attach-term-fallback
Jun 25, 2026
Merged

Cloud: fall back to xterm-256color when the box lacks the host TERM#115
madarco merged 2 commits into
nightlyfrom
fix/cloud-attach-term-fallback

Conversation

@madarco

@madarco madarco commented Jun 25, 2026

Copy link
Copy Markdown
Owner

Ports the docker TERM-fallback fix (#113) to the cloud providers — daytona, hetzner, vercel, e2b. Targets nightly (where #113 landed).

Problem

The shared renderInnerCommand (packages/sandbox-cloud/src/cloud-provider.ts) builds the in-box tmux attach command for all four cloud providers, and had no TERM guard:

  • hetzner / daytona attach over ssh -t, which forwards the host TERM as-is. When the box's Ubuntu terminfo lacks that TERM (e.g. xterm-ghostty, added to ncurses after 24.04 shipped), tmux attach exits immediately with "missing or unsuitable terminal" — the same flash-and-quit Fall back to xterm-256color when the box lacks the host TERM #113 fixed for docker. This was the live bug.
  • vercel / e2b hardcoded TERM=xterm-256color, so they never hit the bug but also dropped host-terminal fidelity.

Fix

  • Export TERM_FALLBACK_SNIPPET from sandbox-docker (single source of truth) and prepend it in renderInnerCommand before the tmux commands — one change covers all four providers. The guard is a no-op when the box already knows $TERM, and safely downgrades to xterm-256color when it doesn't (or when infocmp is absent), so it never regresses the providers that hardcoded TERM.
  • Forward the host TERM on vercel (build-attach envPrelude) and e2b (build-attachAGENTBOX_HOST_TERMattach-helper PTY env) so a box that carries the host terminfo renders at full fidelity, matching docker. hetzner/daytona already forward via ssh -t.

Notes

  • Host-side only — no base-image rebake needed. The guard relies on infocmp/ncurses, already present in every base image; and the fallback is safe even if it weren't.
  • Build + lint + typecheck green. Added a cloud guard test (render-inner-command.test.ts) and updated the vercel/e2b build-attach assertions. docker 297 / cloud 81 / vercel 82 / e2b 16 tests pass.

Verification status

Unit-tested + statically verified. Live cloud e2e (create a hetzner/vercel box, attach with TERM=xterm-ghostty, confirm it renders instead of flash-quitting) is cheap here since no rebake is required — can run on request.

https://claude.ai/code/session_01PTY4KwAeZdAVvgSWxjpYfs


Note

Low Risk
Changes only interactive attach command strings and TERM env wiring; behavior is defensive (fallback to xterm-256color) with new unit tests and no security or data-path impact.

Overview
Fixes flash-and-quit cloud attaches when the host forwards a TERM the box’s Ubuntu terminfo does not know (e.g. xterm-ghostty over Hetzner/Daytona ssh -t).

Shared guard: TERM_FALLBACK_SNIPPET is exported from @agentbox/sandbox-docker and prepended to renderInnerCommand for every tmux path (interactive attach and detached pre-start). It runs infocmp and downgrades to xterm-256color when needed; logs and noTmux are unchanged.

Host TERM forwarding: Vercel’s attach prelude and E2B (AGENTBOX_HOST_TERM → in-box PTY) now set TERM from the host instead of always xterm-256color, with the same guard handling unknown terms.

New unit coverage in render-inner-command.test.ts; Vercel/E2B attach tests updated for the new prelude/env.

Reviewed by Cursor Bugbot for commit 051af1b. Configure here.

Ports the docker TERM-fallback fix (PR #113) to the cloud providers. The
shared `renderInnerCommand` (sandbox-cloud) builds the in-box tmux attach
command for all four cloud providers (daytona, hetzner, vercel, e2b), and
had no TERM guard:

- hetzner/daytona attach over `ssh -t`, which forwards the host TERM as-is.
  When the box's Ubuntu terminfo lacks that TERM (e.g. xterm-ghostty, added
  to ncurses after 24.04 shipped), `tmux attach` exits immediately with
  "missing or unsuitable terminal" — the same flash-and-quit the docker fix
  addressed. This was the live bug.
- vercel/e2b hardcoded TERM=xterm-256color, so they never hit the bug but
  also dropped host-terminal fidelity.

Fix:
- Export `TERM_FALLBACK_SNIPPET` from sandbox-docker (single source of truth)
  and prepend it in `renderInnerCommand` before the tmux commands. Covers all
  four providers in one place. The guard is a no-op when the box already
  knows $TERM, and safely downgrades to xterm-256color when it doesn't (or
  when `infocmp` is absent), so it never regresses the hardcoded providers.
- Forward the host TERM on vercel (build-attach envPrelude) and e2b
  (build-attach -> AGENTBOX_HOST_TERM -> attach-helper pty env) so a box that
  carries the host terminfo renders at full fidelity, matching docker.
  hetzner/daytona already forward via ssh -t.

Host-side only — no base-image rebake needed (infocmp/ncurses already in
every base). Build + lint + typecheck green; added a cloud guard test and
updated the vercel/e2b build-attach assertions.

Claude-Session: https://claude.ai/code/session_01PTY4KwAeZdAVvgSWxjpYfs
@vercel

vercel Bot commented Jun 25, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
agentbox-web Skipped Skipped Jun 25, 2026 3:22pm

Request Review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 051af1b. Configure here.

// renderInnerCommand's TERM guard downgrades to xterm-256color when it
// doesn't, so an exotic host TERM (e.g. xterm-ghostty) never breaks attach.
const hostTerm = process.env['TERM'] ?? 'xterm-256color';
const envPrelude = `export LANG=C.UTF-8 LC_ALL=C.UTF-8 TERM=${hostTerm}; `;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unquoted host TERM in prelude

Medium Severity

The Vercel attach prelude sets TERM by interpolating process.env.TERM directly into the bash -lc script without quoting. Shell metacharacters or spaces in TERM can split the export statement or run extra commands in the sandbox, whereas the previous hardcoded xterm-256color value was always safe.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 051af1b. Configure here.

@madarco

madarco commented Jun 25, 2026

Copy link
Copy Markdown
Owner Author

Live cloud verification ✅

Tested on every runnable cloud provider against a real box (no rebake needed — host-side only). For each: confirmed the box's terminfo lacks xterm-ghostty (so the bug triggers), has infocmp + xterm-256color (guard + fallback target), then drove a real PTY (script) attach with TERM=xterm-ghostty forwarded.

Provider pre-fix (raw xterm-ghostty) with guard verdict
hetzner missing or unsuitable terminal: xterm-ghostty (flash-quit) resolves TERM=xterm-256color, tmux attaches PASS
e2b missing or unsuitable terminal: xterm-ghostty resolves xterm-256color, attaches PASS
vercel missing or unsuitable terminal: xterm-ghostty resolves xterm-256color, attaches PASS
daytona — (no creds in this env) shares the identical renderInnerCommand guard + ssh -t transport as hetzner covered by shared code path

Also confirmed via the real CLI attach path (drive harness, TERM=xterm-256color) that the normal case still attaches cleanly on all three (client_term=xterm-256color, session attached) — no regression.

Note: the drive PTY harness pins node-pty name: 'xterm-256color', so it can't itself carry an exotic TERM; the script-based in-box probe was used to exercise the guard with a real xterm-ghostty.

https://claude.ai/code/session_01PTY4KwAeZdAVvgSWxjpYfs

Cursor Bugbot flagged the vercel attach prelude interpolating
`process.env.TERM` straight into a `bash -lc` string — a TERM with a space
or shell metacharacters could split the export or inject a command. Add
`hostTermForCloud()` (sandbox-cloud) which only passes a well-formed terminfo
name (`[A-Za-z0-9][A-Za-z0-9._+-]*`) and otherwise falls back to
xterm-256color, and route both vercel and e2b through it. The box's terminfo
is still the real filter (renderInnerCommand's guard downgrades anything the
box doesn't carry). Added sanitization unit tests.

Claude-Session: https://claude.ai/code/session_01PTY4KwAeZdAVvgSWxjpYfs
@madarco

madarco commented Jun 25, 2026

Copy link
Copy Markdown
Owner Author

Addressed the Bugbot finding (unquoted host TERM in the vercel bash -lc prelude) in 167c5b8: added hostTermForCloud() which only forwards a well-formed terminfo name ([A-Za-z0-9][A-Za-z0-9._+-]*) and falls back to xterm-256color otherwise. Both vercel and e2b now route through it. Added sanitization unit tests (rejects spaces / $(...) / backticks / ;).

@madarco madarco merged commit f187832 into nightly Jun 25, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant