Skip to content

Agent Node ​

Agent Node 是 CommHub 与模型 runtime 之间的执行进程。它负责连接 Hub、接收任务、调用选定 runtime、回传结果,并维护节点日志、会话和定时目标。

Runtime 的安装、鉴权和能力差异以 Runtime 对比表为准;完整命令以 CLI 参考为准。本页只讲节点本身的通用行为。

安装并启动 ​

需要 Node.js ≥ 22.13 和 Bun:

bash
npm install -g bun @sleep2agi/agent-network @sleep2agi/agent-node

先按上手指南启动并登录 Hub,然后在准备让 Agent 工作的项目目录中:

bash
anet node create my-agent
anet node start my-agent

node create 会让你选择 runtime,并为节点向 Hub 注册独立身份。node start 默认前台运行;日志出现 SSE connected 后才表示任务推送链路已连通。

常用管理命令:

bash
anet node ls
anet info my-agent
anet logs my-agent --follow
anet node stop my-agent

工作目录很重要 ​

Agent 的文件工具以启动目录为工作区。请在目标项目目录创建、启动节点,不要从 $HOME 或包含其他项目/凭据的目录运行。需要后台运行时使用:

bash
anet node start my-agent --tmux

从终端启动会 attach 到 tmux;用 Ctrl-B D detach。无 TTY 的环境会以 detached 模式启动。长期守护和开机恢复见干净服务器部署 · 持久化。

选择 Runtime ​

不要在本页根据旧版本号或固定数量选 runtime。当前可安装组合由 npm 发布频道决定:

  • Claude Code、Claude Agent SDK、Codex SDK、Grok ACP 等路径见 Runtime 对比。
  • Codex TUI 共存是 preview 功能,必须使用 --copresence 的完整启动/恢复路径,见 Codex TUI 共存。
  • OpenCode 当前是任务 runtime,不是共享 TUI。
  • Grok 共享 TUI 尚未发布;当前可用的 grok-build-acp 不支持 attach,见 Grok 节点。

切换 runtime 前先停止旧进程。不要让两个不同进程以同一 alias / node identity 同时连接 Hub。

节点文件 ​

每个项目的节点状态位于 .anet/nodes/<alias>/:

路径用途
config.jsonHub、runtime、node identity、token、model、flags
.env可选 secret;明文文件,权限应为 0600
logs/运行日志。本机看用上面的 anet logs;从客户端看见 节点运行日志
goals.json本节点的循环任务状态

启动时的环境变量优先级 ​

agent-node(包括直接 agent-node --config <path> 启动)按下面的顺序取值,高者胜(实现见 node-secrets.ts 的 loadNodeSecrets;文件顶部注释也写着这三层):

  1. 进程启动时已经存在的环境变量(KEY= 空串也算已设置,会被保留);
  2. config.json 的 env 里的键(明文或 _envRef);
  3. 节点目录下的 secrets.env(anet node secret set 写的那份)——只填空位,不会覆盖前两层。

secrets.env 的约束:必须 0600 的普通文件且属于当前用户;符号链接、非普通文件、权限不对时整份不生效(不是部分生效)。保留键(PATH、LD_*、NODE_* 等)会被跳过。文件不存在是正常状态。

Hub 金库不是上面的第四层。list_network_secrets 和 GET /api/secrets 只返回名字;agent-node --config 启动时不会向金库要明文。create_node 的 env_refs 目前也不读金库,所以「令牌页上有这个名字」不等于这个进程里有这个值。

Hub 下发的密钥要先落成本地文件,再在下次完整启动时按第 3 层补空位(不会覆盖已经存在的进程环境,也不会覆盖 config.json 的 env):

  • 节点私有通道写入 node-secrets.json;
  • Daemon 范围密钥写入 scoped-secrets.json,节点同名值优先;
  • 创建节点时的 env_blob 写成 secrets.env。

这三份都只在下次完整启动时读入。单独重启 Bridge 不会让 TUI 或 server 看到新值。回执和 list 只出 key 名。英文同一节:Environment precedence at startup。 全局的用户登录与当前 network 位于 ~/.anet/config.json。不要提交项目 .anet、token 或 .env。

envRef 让 config.json 只保存环境变量引用;实际值仍可能存入节点的 mode-0600 .env。迁移前先检查备份和目标变量:

bash
anet node migrate-token-to-envref my-agent

完整 token 边界见 Token 与权限,配置安全见安全设计。

密钥文件:谁是权威、怎么备份、怎么轮换 ​

权威边界——这三份本地文件是运行时的权威来源,Hub 金库只是投递通道:

文件位置权威范围
secrets.env<节点目录>/secrets.env(config.json 旁边,node-secrets.ts:42 定义路径)该节点自己的环境变量;启动时只填空位(node-secrets.ts:155 loadNodeSecrets)
node-secrets.json节点私有通道写入节点私有密钥(同名时优先于 daemon 范围的值)
scoped-secrets.jsondaemon 目录(0600、revision CAS)该 daemon 下的子节点共享值

读入规则是整份生效或整份拒绝:secrets.env 若是指向别处的符号链接、非普通文件、属主不是当前用户、或权限不是 0600,整份不生效(不是跳过坏行),文件不存在则是正常状态。

备份(自己做,产品不会自动备份):

  • 备份 secrets.env / node-secrets.json / scoped-secrets.json 三个文件,权限保持 0600、属主为运行用户;
  • 存到与节点目录不同的位置(同盘同目录的副本在磁盘损坏时一起丢),并且不要进 git;
  • 备份里是明文值,等同凭据:按密钥对待(不贴聊天、不进工单)。

恢复:

  1. 停掉节点/daemon(完整停止,不是只重启 bridge);
  2. 把备份文件放回原路径,chmod 600、确认属主;
  3. 完整启动一次——三份文件只在完整启动时读入;
  4. 用 anet node secret list <node> 核对键名齐全(值不会回显)。

轮换顺序(避免中途失效):

  1. 先写新值(anet node secret set <node> <KEY>,值走 stdin 或 --from-env,不会进 shell 历史);
  2. 完整重启节点,确认新值生效;
  3. 再在源头吊销旧值;
  4. 最后更新备份。

回滚条件:新值写入后节点无法启动、或任务开始成片失败且错误指向该密钥时,放回上一份备份并完整重启;scoped-secrets.json 有 revision CAS,两次写冲突以 CAS 报错为准,不要手改文件。

团队规则(可选):ANET_TEAM_RULES ​

网络 owner/admin 可以在 Hub 上写一份全网规则(GET/PUT /api/networks/:id/rules)。默认不会自动写进节点;要让某个节点采用它,在该节点的环境里设 ANET_TEAM_RULES=1,然后完整重启:

bash
# 先备份(块会加在文件最前面)
cp AGENTS.md AGENTS.md.bak-$(date +%Y%m%d-%H%M%S)      # claude 节点是 CLAUDE.md
ANET_TEAM_RULES=1 anet node start <alias>              # 或在你自己的启动器里带这个环境变量

启动日志会有一行 [team-rules] injected|unchanged|skipped|error …。文件顶部出现这样的块:

markdown
<!-- ANET:TEAM-RULES v3 BEGIN — 由 CommHub 自动维护,块外内容不会被修改 -->
## 团队规则(CommHub 自动维护)
### 来源:Hub / 网络 net_xxx(revision 3,更新于 …)
…Hub 上的正文…
<!-- ANET:TEAM-RULES v3 END -->

行为约定:

情况结果
块外内容逐字节保留,任何情况下都不改写
同一 revision 再次启动unchanged,文件字节不变(不产生 diff)
Hub 不可达 / 非 2xx文件原样 + 告警;不会删块,也不阻塞启动
文件里标记残缺(只有 BEGIN 或版本不一致)拒绝写入并报原因,文件不动
没设 ANET_TEAM_RULES零行为差异:不发请求、不碰文件

关掉它:去掉环境变量后重启即可;已写入的块不会自动删除(删块等于静默撤回团队规则),需要时手工删掉标记段或还原备份。

边界:这一版只注入 Hub 一层;Daemon 级规则、层间冲突裁决、合并总长上限都还没有实现。规则改动只在节点下次完整启动时生效,不做运行中推送,也没有客户端编辑页。

想知道某个已发布版本是否真的具备这个行为,可以跑:

bash
bash scripts/verify-team-rules-artifact.sh 2.5.0-preview.156

待具名确认:恢复来源指谁(本机备份 / 外部 KMS / 人工重录)——这一条需要人给一句话才能写成确定流程,本文暂按"本机备份 + 人工重录"描述。

不要复制节点身份 ​

跨机器部署时,应在目标机器登录并重新运行 anet node create。复制 config.json 会同时复制 node_id 和 ntok_,可能造成身份、心跳和 SSE 路由冲突。

同一台机器上要复制节点,用 anet node clone / anet node codex fork,见 复制节点(clone / fork)与清理。

任务处理 ​

text
CommHub ──SSE task──▶ Agent Node ──▶ Runtime / model
   ▲                                      │
   └──────────── task result ─────────────┘

节点只把任务类事件交给模型:

  • send_task:需要执行的任务,会触发 runtime。
  • send_reply:任务结果,不再次触发模型。
  • send_message:普通消息,不再次触发模型。

这种区分避免 Agent 因回复彼此触发无限循环。状态转换、父子任务和超时语义见任务生命周期。

codex-app-server 节点一次只跑一个 turn,后来的任务排队。从 runtime 2.5.0-preview.141 / 配套 CLI 2.3.0-preview.175 起,经 Hub 认证的 Dashboard 聊天请求默认可在运行时排队最多 4 小时;节点、定时任务、旧来源或未认证请求仍为 30 分钟。Hub 的到期、取消和权限检查仍生效,可能在运行时期限之前拒绝执行。

从 runtime 2.5.0-preview.142 / 配套 CLI 2.3.0-preview.176 起,认证 Dashboard 消息在收件分发和 bridge 的等待队列中排在机器待办之前,同类请求仍按到达顺序处理。已经执行、领取启动或开始核查 Hub 状态的一项继续运行;分发并发满时要先等一个槽位释放,连续人类消息也可能推迟机器待办,不能承诺立即响应。本片不新增活跃 CommHub 回合注入或队列位置 UI;原有经认证的人类 TUI 回合注入规则不变。

活跃网络回合的人类接续需要独立回执和回复归属;接续契约与当前回退验证已单独记录。当前仍采用上述等待优先规则,契约和测试通过不表示活跃网络回合注入已经实现。

超过相应排队期限仍未开始的任务会以「在队列中等待 N 分钟仍未开始」失败。要显式覆盖这个默认值,在启动节点的环境里设 ANET_QUEUE_TIMEOUT_MS(整数毫秒,例如 6 小时 = 21600000);该配置对两类请求都优先生效,非法值按对应来源默认值回退并警告一次。排队时间不扣除开始处理后的执行时钟。公开新包不代表已有节点已采用,需要原生命周期管理者在正常空闲窗口确认实际版本。

节点启动时要先恢复(resume)它绑定的线程,才开始接任务。线程很大时(rollout 文件上百 MB)这一步可能要几分钟;默认每次最多等 300 秒,超时重试一次,仍失败就先向 hub 报离线再退出,不会改用新线程(那会丢掉对话历史)。同一期限也用于等待新 TUI 线程真正生成 rollout,避免把尚未落盘的 thread id 写成可恢复会话。要改这个上限,设 ANET_CODEX_RESUME_TIMEOUT_MS(整数毫秒,规则同上)。旧版 Codex 会把完整历史生成为一个大响应帧;客户端的有限 maxPayload 只限制接收端,不能限制 Codex 生成这段数据时的内存。因此 Linux 上的恢复阶段另有主机级单路闸门:按 rollout 的 9 倍(最低 4 GiB)预留可用内存,余量不足时排队并打印原因,直到 launcher、bridge 和 TUI 都完成精确线程绑定才释放;持有期间会续租,不会因恢复超过 10 分钟而放进第二个节点。等满 600 秒仍不满足内存要求时,为兼容既有启动流程并避免整批节点永久卡住,闸门只允许一个恢复继续,同时明确 WARN 当前可用量、需求量和 OOM 风险。Windows / macOS 没有这道基于 /proc 和文件租约的保护,启动日志会明确告警。

opencode 节点每个任务默认最多等 30 分钟。共存模式(opencodeMode: "copresence")下这是整个任务的总时长;到点后 bridge 只是停止等待,不会中止共享会话里的这一轮。任务会在节点的 TUI 里继续跑完,发送方收到的回复会写明这一点,但最终结果不会再自动回传。headless 模式下这是「连续没有任何进展输出」的时长,到点会结束 opencode 子进程,这一轮就中止了。要改这个上限,设环境变量 OPENCODE_TIMEOUT_MS,或在节点 config.json 里设 flags.timeout / flags.opencodeTimeoutMs(整数毫秒;0 = 不设上限)。优先级依次是环境变量、flags.timeout、flags.opencodeTimeoutMs。节点启动时会打印一行 [opencode] task timeout=… source=…,写明实际取到的值和来源。

附件、图片和 channel 支持随 runtime 不同;不要假设所有 runtime 都能处理媒体。按 Runtime 对比和 Channel 指南确认。

工具与权限 ​

工具来自两层:runtime 自带工具,以及 Agent Network 注入的 CommHub 工具。--tools 只对支持自定义工具列表的 runtime 生效;不要用它推断 Codex、Claude Code 或 Grok 的实际 sandbox。

bash
anet node create reader --runtime claude-agent-sdk --tools Read,Glob,Grep

创建完成后,CLI 会打印行为披露;同时检查节点 config.json 的 permissionMode / dangerouslySkipPermissions / runtime 专用 flags。不同 runtime 默认值并不相同,且 Codex TUI 共存默认只读。

把能够写文件、执行 shell 或联网的节点当作不可信代码:

  • 使用独立、可丢弃的工作目录。
  • 不把生产密钥放进 Agent 可读目录。
  • 只给需要的 Hub/network 权限。
  • 修改权限后用一个无害任务验证实际行为,不只看配置名称。

详细威胁模型和默认值见安全设计。

循环任务 ​

对在线节点创建周期任务:

bash
anet node loop my-agent "检查待处理 Issue" --every 5m
anet goal list my-agent
anet goal cancel my-agent <goal-id>

Dashboard 的 /goal、/loop 会原样进入目标 runtime/TUI;ANet 周期任务统一使用 /aloop(/agoal 为同义入口,二者都要求间隔)。非 Dashboard 的旧 /goal、/loop 暂时兼容并提示迁移。循环状态保存在该节点的 goals.json;它不是高精度 cron,并会真实消耗模型额度。

完整的路由差异、支持的时间格式、执行/停止规则、自管理工具、重启限制与排查方法见 Goal 与 Loop。

生命周期与恢复 ​

  • 启动:读取节点配置,注册/报告状态,连接 SSE。
  • 运行:处理任务、定期上报状态;连接中断后按退避策略重连。
  • 停止:优先使用 anet node stop <alias>,让节点关闭连接并报告离线。
  • 恢复会话:行为随 runtime 不同;先查看 anet info <alias> 和对应 runtime 指南,不要手工改 session/thread id。
  • 改名:使用 anet node rename,不要直接改目录名、alias 或 node_id。

若同一 alias 出现重复结果、runtime 来回变化或状态异常,先检查重复进程:

bash
anet info my-agent
anet logs my-agent --follow
tmux ls

停止旧实例后再启动一个。更多现象见故障排查。

参考 ​

Powered by Sleep2AGI

下载客户端