From 512397c4f55cb2adc3bab6afb1d64cd8c5687313 Mon Sep 17 00:00:00 2001 From: Marco D'Alia Date: Fri, 26 Jun 2026 11:43:57 +0100 Subject: [PATCH 1/2] fix(cloud): start the agent session on --no-attach (cloud providers) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On cloud providers (daytona/hetzner/vercel/e2b) the bare `agentbox claude`/`codex`/`opencode` commands and `agentbox fork` route through `cloudAgentCreate`, which on `--no-attach` returned before the agent's tmux session was ever created. The cloud session is created lazily by the attach step, so skipping attach skipped the agent entirely — the box came up with no agent running, contradicting the documented behavior ("create the box and start the agent session, but do not attach"). Docker was unaffected: it creates the session before the attach check. Fix `cloudAgentCreate` to call `cloudAgentStartDetached` in the `attach === false` branch — the same helper the `-i` queue worker uses, which starts a detached tmux session and verifies it stayed up (fail-loud on immediate exit / credential rejection). The ` start` subcommand cloud branches did the same lazy no-op (printing "started lazily on attach"); they now resolve args first, then start the detached session in background mode. Cloud now matches docker on every path. Verified end-to-end: docker `--no-attach` still starts the session (regression guard), and e2b `--no-attach` now brings up a live, logged-in claude tmux session where before it created an empty box. Claude-Session: https://claude.ai/code/session_01PTY4KwAeZdAVvgSWxjpYfs --- apps/cli/src/commands/_cloud-agent-create.ts | 21 +++++++++++++----- apps/cli/src/commands/claude.ts | 23 +++++++++++++------- apps/cli/src/commands/codex.ts | 23 +++++++++++++------- apps/cli/src/commands/opencode.ts | 21 +++++++++++++----- 4 files changed, 60 insertions(+), 28 deletions(-) diff --git a/apps/cli/src/commands/_cloud-agent-create.ts b/apps/cli/src/commands/_cloud-agent-create.ts index 58cff7d..e3d2c3b 100644 --- a/apps/cli/src/commands/_cloud-agent-create.ts +++ b/apps/cli/src/commands/_cloud-agent-create.ts @@ -23,7 +23,7 @@ import { makeProgressReporter } from '../lib/progress.js'; import { printLaunchRecap } from '../lib/launch-recap.js'; import { buildPromptArgs } from '../lib/queue/build-prompt-args.js'; import { buildResyncWarning } from '../lib/resync-warning.js'; -import { cloudAgentAttach } from './_cloud-attach.js'; +import { cloudAgentAttach, cloudAgentStartDetached } from './_cloud-attach.js'; export interface CloudAgentCreateArgs { /** Pre-resolved provider (from `providerForCreate`). */ @@ -42,11 +42,11 @@ export interface CloudAgentCreateArgs { verbose?: boolean; /** Where to open the attached session; forwarded to `cloudAgentAttach`. */ openIn?: AttachOpenIn; - /** When `false`, create the cloud box and skip the agent attach (background - * mode). Defaults to `true`. On cloud providers the agent's tmux session is - * created lazily by `cloudAgentAttach`; with `attach: false` the session - * isn't started yet — a later `agentbox attach ` starts it on - * first attach. */ + /** When `false`, create the cloud box and start the agent in a detached tmux + * session (background mode) but don't attach the host terminal — mirrors the + * docker path, where the session is always created and only the terminal + * attach is skipped. A later `agentbox attach ` finds the + * running session and attaches to it. Defaults to `true`. */ attach?: boolean; /** * Hook fired AFTER the box is provisioned and BEFORE the agent attach starts @@ -118,6 +118,15 @@ export async function cloudAgentCreate(args: CloudAgentCreateArgs): Promise Date: Fri, 26 Jun 2026 11:53:14 +0100 Subject: [PATCH 2/2] fix(cloud): resume recorded session on background --no-attach start MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cloudAgentStartDetached launched the agent with raw extraArgs only, so a background `agentbox start --no-attach` (and idle-resumed creates) started a fresh agent instead of resuming the box's recorded claude/codex session — the interactive cloudAgentAttach path applies agentResumeArgs when args are empty, but the detached path did not. Apply the same resume-args resolution in cloudAgentStartDetached so the detached path is symmetric with attach. The `-i` queue path always seeds a prompt (non-empty extraArgs), so this no-ops there. Found by Cursor Bugbot on PR #116. Claude-Session: https://claude.ai/code/session_01PTY4KwAeZdAVvgSWxjpYfs --- apps/cli/src/commands/_cloud-attach.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/apps/cli/src/commands/_cloud-attach.ts b/apps/cli/src/commands/_cloud-attach.ts index 91b22d4..71fb48e 100644 --- a/apps/cli/src/commands/_cloud-attach.ts +++ b/apps/cli/src/commands/_cloud-attach.ts @@ -419,7 +419,20 @@ export async function cloudAgentStartDetached(args: { if (state !== 'running') { box = await provider.start(box); } - const command = buildCloudAttachInnerCommand(args.binary, args.extraArgs); + // With no user args, resume the box's recorded session instead of launching + // fresh — same as the interactive `cloudAgentAttach` path. Matters for a + // background `agentbox start --no-attach` (and idle-resumed + // creates): the box already has a claude/codex session to reopen. The `-i` + // queue path always seeds a prompt, so extraArgs is non-empty and this no-ops. + let extraArgs = args.extraArgs; + if ( + (!extraArgs || extraArgs.length === 0) && + (args.binary === 'claude' || args.binary === 'codex') + ) { + const resume = await agentResumeArgs(provider, box, args.binary); + if (resume) extraArgs = resume; + } + const command = buildCloudAttachInnerCommand(args.binary, extraArgs); const { exitCode, stderr } = await startDetachedSession( provider, box,