Skip to content

Agent Workspace

Agent Workspace 是什么、与 Sandbox 的关系,以及创建、连接与清理方式。

Updated View as Markdown

简要定义

Agent Workspace 把一个 Sandbox(隔离执行环境)和其中一个 Agent Runtime(例如 Pi 或 OpenCode)组合成一个可远程开发的整体。workspaceId 就是 sandboxId——两者是同一个身份,没有第二份生命周期或数据库记录。

flowchart LR
    W[Agent Workspace] -->|等价于| S[Sandbox]
    S --> R[Agent Runtime\nPi / OpenCode]
    R --> T[Task Run\n提交并交付任务]

为什么存在这个概念

Sandbox 本身只是一个受控执行环境,不预设”应该跑什么”。Workspace 把 Sandbox 和一个具体的 Agent Runtime、仓库源码、Terminal Session 组合起来,变成一个用户可以直接远程开发、随时断线重连的环境——这是大多数”让 AI Agent 帮我改代码”场景真正需要的抽象层级。

从本地目录打开 Agent

登录后,团队已有默认 Server 时运行:

appaloft login
appaloft code

默认 appaloft code 占用团队默认已注册 Server 上的 我的 Sandbox。源是远端 SHA。 缺少 Adapter/Profile 时会创建不可见的 appaloft-remote。笔记本路径不是 Workspace 真相,脏树也不会上传。位置参数若是 git remote(https://ssh://git@host:path), 无需本地 clone 即可占用该仓库。TTY 上默认 code 先在普通屏幕打印 Loading your project, 再打开占用 Workspace TUI。等待面板是 preparing the agent,步骤是 Using your credential / Including your skills / Waking the agent,并会显示 生成的 agent 名(adjective-noun)。不是 Checking login / Preparing disk。 occupy / copy skills / attach 作为左对齐步骤(Braille spinner,完成后 ✓)。attach 在同一全屏窗格里接上。workspace 打开同一个 TUI 作为导航。不以刷 stderr 状态行为主 UX。 HOME skill 复制限时且失败软处理,超时不会阻断 occupy。--serverworkspace open --server 语义相同,用来钉死已登记 Server。--no-attach 和非 TTY 脚本仍用一行 stderr 进度(含进程 启动后的第一字节状态行),并打印 Remote 横幅以及 connecting 三行: Using your {Grok|Codex|Claude} credential on the agentIncluding N of your skillsWoke agent {name}N 是实际复制成功的 skill 数。 源码检出(appaloftdev)第一次 TTY 运行会查找或 cargo build 占用 TUI sidecar。 查找范围包括正在执行的树和常见兄弟检出(例如 appaloft-cloud/community/appaloftappaloft),以及 APPALOFT_WORKSPACE_TUI_BINARY。 若缺少 appaloft-workspace-tui 且 Rust 低于 1.88,CLI 会点名该二进制,并打印 rustup toolchain install stablecargo build --locked --manifest-path apps/workspace-control-tui/Cargo.toml--no-attach 不需要 TUI 二进制。 attach 之后,可选的 list/detail chrome 不得在 footer 画出 conflict at workspace-control-select 或残留的 bootstrap 冲突。 横幅是:

Remote · agent <name> · <repo@sha> · <server> · <project>

<name> 是 Agent 的可读名,不是 sbx_…。git 占用会生成 kebab 名,例如 resonant-silencefolder.local 可以用目录名。repo@short-sha 只出现在横幅的 git pin 上,不是把手。再次打开仍是同一个名字。Cloud Agents 列表和详情用这个名字。 JSON / --json 仍可带 workspaceIdworkspace show|pause|resume|terminate 接受这个展示名。

占用成功后,CLI 会把公开的 Appaloft skill 写入 /workspace/skills/appaloft/workspace/.agents/skills/appaloft。如果这台笔记本 HOME 里有匹配的 skill 目录,占用还会 只新增 复制到 /workspace/skills/<name>/workspace/.agents/skills/<name>

对齐 Railway 云 Agent 文档的根是 ~/.claude/skills~/.codex/skills~/.grok/skills~/.agents/skills。Appaloft 还会提供 ~/.cursor/skills~/.config/opencode/skills, 因为这是用户实际使用的根;这两项超出 Railway 文档范围。Railway 文档没有写 plugins、 MCP 配置、或这两个额外根。

只复制包含 SKILL.md 的目录。没有 SKILL.md 的文件夹(例如只有 references 的 use-railway)整棵跳过。已存在的沙箱文件不会被覆盖。skill 树不会复制 mcp.json、token、 cookie、.env 或编辑器插件二进制。沙箱进程看不到笔记本 HOME;复制只发生在本机 occupy 已经在写文件的那条 CLI 路径上。

在 Cloud Agents 上选择 Claude、Codex 或 Grok

appaloft code 每次只接受一个 agent 别名:

  • 我们的:--opencode--pi--omp
  • 对齐 Railway 的:--claude--codex--grok

--claude--codex--grok 在远端 Sandbox 启动对应厂商 CLI,并把本机凭证写到会话 HOME。--opencode--pi--omp 选择对应 Cloud Agents harness。--harness opencode|pi|omp|claude|codex|grok 只作兼容。未传别名时,code 使用已保存的 Cloud Agents 偏好,然后跟随这台笔记本已登录或已安装的厂商。

占用会把该凭证写到会话磁盘(HOME=/workspace),而不是沙箱环境变量:

  • Grok:~/.grok/auth.json → 会话 .grok/auth.json
  • Codex:~/.codex/auth.json → 会话 .codex/auth.json
  • Claude:setup-token(~/.appaloft/claude-setup-token~/.claude/setup-token,或把笔记本上的 CLAUDE_CODE_OAUTH_TOKEN 写成文件)。不会复制 Claude 聊天 cookie。

会话仍提供公开 Appaloft skill 和第一方 Appaloft MCP,让远端 agent 可以列出 workspace 并部署。不会复制笔记本 mcp.json 密钥。永远不会打印 token 值。

占用结束后若要删除 Codex 凭证的远端副本,运行:

appaloft sandbox file remove <sandboxId> --path .codex/auth.json

删除远端文件不会撤销对应的上游登录会话。若凭证可能已经泄露,还要在 Codex / OpenAI 账号安全设置中撤销对应会话。

多个已注册 Server 时,用 --server 钉死其中一个:

appaloft code --server srv_4lifk0yrcecy

Preparing disk 遇到暂时的 Cloud 网关失败(HTTP 502 / 503,包括 Cloudflare bad-gateway 或 origin 不完整响应),Cloud Agents 等待画面会保持, 将该磁盘步骤标为重试中,并在同一台已登记 Server 上继续重试磁盘准备,直到进入可输入的会话,或等待超时。 只有磁盘准备成功并接上会话后才算成功。短重试突发失败不会为此离开等待画面、恢复终端,或打印 folder 路径。 若截止时间到、进行中的磁盘准备被取消,或从未接上会话,CLI 会失败闭合:恢复终端一次,打印 Cloud 暂时不可达 / 磁盘准备未完成,退出非 0,不会打印 folder 路径。等待画面上的 Ctrl-C / 退出会中止进行中的打开,看起来不像成功。这是同一次 occupy 重试,不是再打开一次本地 folder。--omp 是 OpenCode harness;若你打开的是 Pi,用 --pi。CLI 会保留 HTTP 状态,不会倾倒网关页面。

未登录、没有 Server,或显式 --claude / --codex / --grok 缺少凭证,会 fail closed 并给出下一步,不会变成 Scratch。本机 Scratch 必须显式指定:

appaloft code --local

--local 仍是本机 OpenCode,否则 Pi;不要求 Git、登录、Binding 或 Cloud。横幅仍是 Local scratch · this Mac · not saved remotely

交付用的 durable open 仍是:

appaloft workspace open [path|git-remote]

这条路径继续走 workspaces.open,定位参数与 code 相同:本地路径(可以不是 Git worktree) 或 git remote(https://ssh://git@host:path,以及不是本地目录时的 owner/repo)。 非 git 目录只能通过 git remote 占用当前目录,不会静默复用别人的会话。如果路径是 Git worktree,dirty / detached / 缺 upstream / remote tip 不一致仍会在远端创建前失败。默认 appaloft code 优先选 live 会话,同名 leftover 不能挡住第一次成功, 也不要求记住 installation id。--profile 只是显式撞名时的兜底,不同于全局 --control-plane-profile--new 仍占用 cwd 源,不会静默 resume whoami 会话。 如果默认 code 命中的首选 Workspace 停在部分创建状态,CLI 会保留那个 Workspace 供诊断, 并自动创建一个隔离替代 Workspace;不需要退出后再手工补 --new。低层 workspace open / workspace create 仍会返回原始 partial 恢复证据,不会自动创建替代。 打开失败时会写出缺什么、正在打开哪个仓库,而不会把 Cloud 激活失败伪装成 CLI 成功。 已注册 BYOS Server 时,workspace opencode 放到那台 Server 上(--server <id> 可钉死),不会再索要 managed 容量。V1 不做隐式 sync 或 patch upload。

workspaces.open 会先验证来源,再读取现有 Repository Binding 和 Project 默认 Profile。如果部署方 组合了可选的 activation initializer,缺少的 Project、Binding 或默认 Profile 可以在同一次显式 activation 中幂等创建或复用,随后必须重新读取公共权威状态;没有 initializer 的 Community/local 部署继续 fail closed。initializer 必须在创建前完成授权与 admission,且不能覆盖已有、冲突、 disabled 或未授权状态。

deploy 方的 entitlement、target policy 或容量失败必须在 Sandbox/provider effect 前失败。错误可以 提示等待、重试或显式接入其他执行位置,但 Appaloft 不会把一次 managed 请求静默改成本机 Scratch。

管理正在运行的 Workspace

在受支持的 macOS/Linux 交互终端中,无子命令运行:

appaloft workspace

会打开 Appaloft Workspace control TUI。进入 TUI 本身不会创建、暂停、恢复或终止任何 Workspace;它通过现有公开查询显示 Workspace、Agent Runtime、Preview port、Task 和 Promotion 摘要。选择带 attach capability 的 Runtime 后,Agent 自己的 TUI 会作为原生终端字节流嵌入右侧 窗格,Appaloft 不解析对话、tool call 或 hidden reasoning。

详情头部同时显示安全的 target class/source/reason 和 activation created/reused 状态;它与 headless sandbox show、HTTP 和 SDK 使用同一 readback,不显示主机或 provider identity。

  • / j / k:选择 Workspace;
  • Enter:attach 或重新聚焦已经连接的 Agent;
  • a:打开所选 Workspace 的生命周期操作。ready 状态可暂停或终止,paused 状态可恢复或 终止;终止还需要单独按 y 确认;
  • d:打开从当前 detail 推导的交付操作。可以创建默认 private、明确选择 1 小时/8 小时/24 小时 TTL 的 Preview,撤销已有 Preview,批准或交付符合状态的 Task,以及接受/重试符合状态的 Promotion;撤销、批准、Git/PR 交付和 Promotion 操作都需要单独按 y 确认;
  • s:打开恢复操作。可以用 filesystemfilesystem-memory capability 创建保留 1、7 或 30 天的 Snapshot,也可以删除当前 detail 中状态允许删除的精确 Snapshot;创建和删除都需要 单独按 y 确认;
  • Ctrl+]:把按键所有权从 Agent 交还给 Workspace 导航,不停止或 detach Agent;
  • f:在同一个 Terminal Session/本地 PTY 上切换 Focus Mode,不启动第二个 Agent;
  • r:刷新现有公开 read model;R:用同一 Session identity 手动重连;
  • q:仅在 Workspace 导航获得焦点时退出;Agent 获得焦点时,普通按键仍发送给 Agent。

TUI 断开或退出只 detach 客户端。TUI 生命周期操作调用与显式 headless 子命令相同的公开命令; 暂停或终止前会先 detach 当前 Agent viewport,之后重新读取 Workspace 状态,而不是在本地乐观 修改状态。交付和 Snapshot 操作都不会 detach 当前 Agent Session。恢复区域还显示请求/实际隔离、 provision attempts、暂停连续性和当前 Workspace 的 Snapshot。终止/过期 Workspace 的 Workspace-owned cleanup 只根据有界 Runtime 与 Preview 查询判定 clearresidual;它不是 主机或 provider 零残留证明。带 deployment identity 的 Promotion 会查询 权威 Deployment Proof,并显示 verdict、mismatch 数和 unavailable evidence 数;Promotion status 不会被伪装成 proof。交付失败会保留有界表单值供修改或重试,credential 不会进入 renderer 协议。脚本、CI、无 TTY 环境或不希望加载 renderer 时使用:

appaloft workspace --no-tui
appaloft workspace --json
appaloft workspace list

等价的 headless 入口仍是 workspace previewsandbox port revokeworkspace task approve/deliversandbox promote accept/retrydeployment proof,以及 sandbox showsandbox snapshot list/create/show/delete

这些路径不会初始化 renderer,并返回稳定的 headless 状态或继续执行现有子命令。Windows 当前仅 保证 help/headless 命令安全;嵌入模式需要独立的 release 与终端验收。Windows,或 TERM 缺失、为 dumb/unknown 的交互环境,会在 renderer 启动前返回 platform-unsupported/terminal-unsupported,避免把控制序列写入不支持的宿主终端。

使用 Profile 显式创建

需要在没有本地 Git context 的自动化中创建新 Workspace 时,使用 credential-free HTTPS repository 和显式 Profile:

appaloft workspace create \
  --profile opencode-default \
  --repo https://github.com/acme/web.git \
  --ref refs/heads/feature/login \
  --branch feature/login \
  --attach

--profile 接受安装 id、Profile id 或唯一显示名。Appaloft 会先编译 Profile 并解析 Sandbox Template、隔离级别、资源/网络策略、初始化步骤、默认端口、Adapter capability 和安装时配置的 named Credential Connections,再创建 Sandbox。缺失、disabled、stale、ambiguous 或无权限的 Profile/Credential/Template/capability,以及 placement 无容量,都在 Sandbox 创建前失败。 API Key、Token 和 Secret 不允许出现在 argv。

SDK 提供等价的组合式创建:

const workspace = await appaloft.workspaces.open({
  repository: "https://github.com/acme/web.git",
  repositoryIdentity: "github.com/acme/web",
  ref: "refs/heads/feature/login",
  branch: "feature/login",
  commitSha: "0123456789abcdef0123456789abcdef01234567",
  profile: "opencode-default",
});

console.log(
  workspace.workspaceId,
  workspace.agent.runtimeId,
  workspace.targetSelection,
  workspace.activation,
);

如果 Sandbox identity 产生后的步骤失败,错误会包含精确 phase、已有的 workspaceId/runtimeId、是否可重试以及恢复/终止入口;再次 open 会协调同一个部分创建身份, 不会静默创建重复 Sandbox。

断线重连

Terminal Session 由 Appaloft 托管的 PTY 支撑——客户端断线只是”detach”,只要 Session 的存活时间和 Sandbox 仍然有效,再次运行 appaloft code 就会重用 Session、重放有限历史输出并继续同一个 Agent 进程:

appaloft code

已有脚本可以继续使用 appaloft workspace open .;它不会被弃用或改变机器可读行为。

底层排障命令仍然保留:

appaloft workspace connect <workspaceId>
appaloft workspace attach <workspaceId>

native attach 只签发短期、可撤销的私有访问 capability;返回值不会包含原始服务器地址、 SSH Key 或长期凭据。

临时开发预览

appaloft workspace preview <workspaceId> \
  --port 3000 \
  --visibility private \
  --expires-at 2026-07-24T12:00:00.000Z

这是一个实时开发预览,不是不可变的候选预览(Promotion Candidate Preview)。URL、TLS、鉴权和路由由 Provider/网关适配器提供;到期、被撤销或 Sandbox 被清理后,地址必须立即失效。不同团队成员使用各自的 Sandbox 时,端口暴露、文件和进程都有独立身份,互不冲突。

生命周期

appaloft workspace list
appaloft workspace show <workspaceId>
appaloft workspace pause <workspaceId>
appaloft workspace resume <workspaceId>
appaloft workspace terminate <workspaceId>

workspace list 是 Sandbox 清单的组合视图,每一项都带 agentRuntimes;如果这个数组为空,说明它是一个可以重试或清理的”部分创建”状态,而不是隐藏在另一张表里的状态。

pause / resume 保留 Sandbox 身份(详见休眠与恢复);terminate 会终止 Sandbox 及其拥有的全部 Runtime 状态,这一步不可逆。

提交一次 Task

在 Workspace 里给 Agent 布置一个任务、观察进度、批准并交付代码,使用 workspace task 子命令族:

appaloft workspace task run <workspaceId> \
  --runtime-id <runtimeId> \
  --task "修复 Issue #123 并运行测试" \
  --check-arg bun --check-arg test

appaloft workspace task show <workspaceId> <taskRunId>
appaloft workspace task deliver <workspaceId> <taskRunId> \
  --branch fix/issue-123 \
  --commit-message "fix: resolve issue 123" \
  --pull-request-title "Fix issue 123"

Task Run 由服务端持久化——客户端断开连接不会取消 Agent 的执行。批准和交付源码必须由外部用户或可信 CLI 操作者发起,Sandbox 内的 Runtime 身份不能自我批准自己的变更。

常见误区

  • 把 Workspace 当成独立于 Sandbox 的资源workspaceIdsandboxId 是同一个 id,理解 Sandbox 的隔离和生命周期模型(见 Sandbox 模型)就理解了 Workspace 的底层行为。
  • 认为客户端断线会取消正在运行的 Task:Task Run 由服务端持久化和恢复,断线只影响观察,不影响执行。
  • 把开发预览当成生产访问地址:开发预览是临时、可过期的实时预览,和生成的访问地址是完全不同的机制。
  • . 当成上传目录:它只用于解析 Git context;未提交文件从不隐式上传。
  • 在 HEAD 未 push 时继续创建:Workspace 的来源必须是远端可解析的精确 SHA;先 push, 或明确选择正确的 upstream,再重试。

相关任务

进阶细节

如果第一次创建 Task Run 时 Runtime 已经在忙于处理另一个 Task,运营方可以在适配器层配置并发策略;具体行为取决于所选 Agent 适配器,详见 Agent 适配器

Navigation

Type to search…

↑↓ navigate↵ selectEsc close