Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 14 additions & 10 deletions agent-network/bin/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,12 @@ interface Profile {

// Re-export from the pure helper module (src/normalize-runtime.ts) so
// unit tests can import without dragging in CLI side-effects.
import { normalizeRuntime, type RuntimeName } from "../src/normalize-runtime";
import {
normalizeRuntime,
runtimeSkipsCreateVendorPicker,
runtimeUsesAgentNode,
type RuntimeName,
} from "../src/normalize-runtime";
import { findEnvironAliasMatches } from "../src/environ-alias";
export { normalizeRuntime, type RuntimeName };

Expand Down Expand Up @@ -1099,6 +1104,7 @@ anet — AI Agent Network CLI (V2)

Node Management:
anet node create <name> Create a new agent node
--runtime <type> claude-agent-sdk | claude-code-cli | codex-sdk | codex-app-server | grok-build-acp | opencode-cli
anet node start <name> Start a node
anet node start --all Start every node in cwd (= anet project up)
anet node stop <name> Stop a running node
Expand Down Expand Up @@ -2045,7 +2051,7 @@ async function createInteractiveCommand() {

This wizard creates one agent node for this project:
- node config: .anet/nodes/<node-name>/config.json
- runtime: claude-code-cli / codex-sdk / claude-agent-sdk / grok-build-acp
- runtime: claude-code-cli / codex-sdk / codex-app-server / claude-agent-sdk / grok-build-acp / opencode-cli
- optional Telegram channel: text + images from an allowlist user
`);

Expand Down Expand Up @@ -2246,7 +2252,7 @@ async function createCommand(idOverride?: string) {
const id = idOverride || args[1];
if (!id) return createInteractiveCommand();
if (id.startsWith("--")) {
console.error("Usage: anet node create <node-name> [--runtime claude-code-cli|codex-sdk|claude-agent-sdk|grok-build-acp] [--model ...] [--tools ...]");
console.error("Usage: anet node create <node-name> [--runtime claude-code-cli|codex-sdk|codex-app-server|claude-agent-sdk|grok-build-acp|opencode-cli] [--model ...] [--tools ...]");
console.error("Or run fully interactive: anet node create");
process.exit(1);
}
Expand Down Expand Up @@ -2291,7 +2297,7 @@ async function createCommand(idOverride?: string) {
const credAlreadyProvided = !!process.env.ANTHROPIC_AUTH_TOKEN
|| !!process.env.ANTHROPIC_API_KEY || envFlagHasAuth;
const explicitRuntime = opts.runtime ? normalizeRuntime(opts.runtime) : undefined;
const runtimeAlreadyExplicit = explicitRuntime === "codex-sdk" || explicitRuntime === "claude-code-cli" || explicitRuntime === "grok-build-acp";
const runtimeAlreadyExplicit = explicitRuntime ? runtimeSkipsCreateVendorPicker(explicitRuntime) : false;
const skipInteractive = credAlreadyProvided || runtimeAlreadyExplicit;

// #133 selectRuntime — runtime-first, exported as a helper so create paths
Expand All @@ -2305,6 +2311,7 @@ async function createCommand(idOverride?: string) {
{ value: "claude-agent-sdk", name: "claude-agent-sdk — 任意 OpenAI/Anthropic-compat vendor (intern / MiniMax / Claude / GLM / ...)" },
{ value: "claude-code-cli", name: "claude-code-cli — Anthropic Claude (Max/Pro plan), 复用 `claude` CLI 登录态" },
{ value: "codex-sdk", name: "codex-sdk — OpenAI Codex, 复用 `codex auth login` 登录态" },
{ value: "codex-app-server", name: "codex-app-server — OpenAI Codex TUI runtime (RFC-030 preview; production locked)" },
{ value: "grok-build-acp", name: "grok-build-acp — Grok Build ACP, 复用 `grok` CLI 登录态" },
],
});
Expand All @@ -2327,6 +2334,8 @@ async function createCommand(idOverride?: string) {
console.log("[anet] 请确保已安装 Claude Code CLI 并登录: claude auth login");
} else if (opts.runtime === "codex-sdk") {
console.log("[anet] 请确保已执行: codex auth login");
} else if (opts.runtime === "codex-app-server") {
console.log("[anet] 请确保已执行: codex auth login(RFC-030 preview;生产门仍锁定)");
} else if (opts.runtime === "grok-build-acp") {
console.log("[anet] 请确保已安装并登录 Grok Build CLI: grok auth login");
} else {
Expand Down Expand Up @@ -2854,12 +2863,7 @@ async function launchAgent(id: string, forceNewSession = false) {
}
} catch {}

if (
runtime === "codex-sdk" ||
runtime === "claude-agent-sdk" ||
runtime === "grok-build-acp" ||
runtime === "opencode-cli"
) {
if (runtimeUsesAgentNode(runtime)) {
// spawn agent-node
const agentArgs = [
"--config", join(nodesDir(), nodeId, "config.json"),
Expand Down
38 changes: 37 additions & 1 deletion agent-network/src/normalize-runtime.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@
// Explicit `claude-code-cli` choice still works.

import { describe, expect, test } from "bun:test";
import { normalizeRuntime } from "./normalize-runtime";
import {
normalizeRuntime,
runtimeSkipsCreateVendorPicker,
runtimeUsesAgentNode,
} from "./normalize-runtime";

describe("normalizeRuntime — fallback default is claude-agent-sdk (Vincent no-Max)", () => {
test("unknown string → claude-agent-sdk (was claude-code-cli pre-2026-06-28)", () => {
Expand Down Expand Up @@ -137,3 +141,35 @@ describe("normalizeRuntime — profile object paths", () => {
expect(normalizeRuntime({ runtime: "bogus-runtime-name" } as any)).toBe("claude-agent-sdk");
});
});

describe("runtimeUsesAgentNode — launcher routing", () => {
test("codex-app-server uses agent-node, never the Claude CLI branch", () => {
expect(runtimeUsesAgentNode("codex-app-server")).toBe(true);
});

test("all non-Claude-Code runtimes use agent-node", () => {
for (const runtime of [
"claude-agent-sdk",
"codex-sdk",
"codex-app-server",
"grok-build-acp",
"opencode-cli",
] as const) {
expect(runtimeUsesAgentNode(runtime)).toBe(true);
}
});

test("claude-code-cli keeps its dedicated launcher", () => {
expect(runtimeUsesAgentNode("claude-code-cli")).toBe(false);
});
});

describe("runtimeSkipsCreateVendorPicker — explicit create flag", () => {
test("codex-app-server remains explicit in a TTY", () => {
expect(runtimeSkipsCreateVendorPicker("codex-app-server")).toBe(true);
});

test("claude-agent-sdk still enters its provider picker", () => {
expect(runtimeSkipsCreateVendorPicker("claude-agent-sdk")).toBe(false);
});
});
17 changes: 17 additions & 0 deletions agent-network/src/normalize-runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,23 @@ export type RuntimeName =
/** Operator-facing default for any runtime slot that comes in empty / missing / unrecognized. */
export const DEFAULT_RUNTIME: RuntimeName = "claude-agent-sdk";

/**
* Runtimes launched through the agent-node supervisor rather than by spawning
* the Claude Code CLI directly. Keep this predicate exhaustive so adding a
* runtime cannot silently fall into the Claude launcher branch.
*/
export function runtimeUsesAgentNode(runtime: RuntimeName): boolean {
return runtime !== "claude-code-cli";
}

/** Runtimes whose explicit CLI choice must bypass the API-vendor picker. */
export function runtimeSkipsCreateVendorPicker(runtime: RuntimeName): boolean {
return runtime === "claude-code-cli"
|| runtime === "codex-sdk"
|| runtime === "codex-app-server"
|| runtime === "grok-build-acp";
}

// Subset of Profile fields this helper inspects. Keeping it narrow so
// the test fixture doesn't need the full Profile shape and so callers
// (bin/cli.ts uses the full Profile) can pass anything structurally
Expand Down
3 changes: 2 additions & 1 deletion agent-node/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ CLI flags:
|---|---|---|
| `--alias` | required | unique name in the hub |
| `--hub` | `http://127.0.0.1:9200` | CommHub URL |
| `--runtime` | `claude-agent-sdk` | `claude-agent-sdk` / `codex-sdk` / `claude-code-cli` / `grok-build-acp` / `http-api` |
| `--runtime` | `claude-agent-sdk` | `claude-agent-sdk` / `codex-sdk` / `codex-app-server` / `claude-code-cli` / `grok-build-acp` / `http-api` |
| `--model` | runtime default | passed through to the SDK |
| `--tools` | (none) | `all` or comma-separated list |
| `--max-turns` | `50` | upper bound per task |
Expand All @@ -56,6 +56,7 @@ CLI flags:
|---|---|---|---|
| `claude-agent-sdk` | [@anthropic-ai/claude-agent-sdk](https://www.npmjs.com/package/@anthropic-ai/claude-agent-sdk) | verified | Anthropic-compatible API; works with MiniMax, DeepSeek, GLM, Kimi, Anthropic, OpenRouter, or custom endpoints |
| `codex-sdk` | [@openai/codex-sdk](https://www.npmjs.com/package/@openai/codex-sdk) | unverified end-to-end | unit tests pass, no full E2E with real codex auth |
| `codex-app-server` | local `codex app-server` + TUI bridge | preview; production locked | canonical RFC-030 runtime. The current CLI entry starts the Phase-0 direct path; Policy Gateway, §8 review, merge and production approval remain locked |
| `claude-code-cli` | local `claude` CLI | unverified end-to-end | runs locally for Claude Pro subscribers (v0.8.2 fixed the session-resume default-loss bug; see [changelog](https://anet.sh/en/changelog)) |
| `grok-build-acp` | local `grok agent stdio` | stable runtime, native MCP injection boundary remains preview | requires Grok Build CLI login; stable for receive/reply, session persistence, and explicit CommHub delegation handled by agent-node |
| `http-api` | OpenAI/Anthropic-compatible HTTP | experimental | reads `ANTHROPIC_*`, `OPENAI_*`, or `MINIMAX_CODING_API_KEY` environment variables |
Expand Down
4 changes: 3 additions & 1 deletion agent-node/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* Runtime:
* --runtime claude-agent-sdk → Claude Agent SDK (Claude/MiniMax)
* --runtime codex-sdk → Codex SDK (GPT-5.4)
* --runtime codex-app-server → Codex app-server/TUI runtime (RFC-030 preview)
* --runtime grok-build-acp → Grok Build ACP (xAI)
*
* 配置加载: --config > CLI args > env > .anet/nodes/<name>/config.json > ~/.anet/config.json > defaults
Expand Down Expand Up @@ -115,7 +116,7 @@ for (let i = 0; i < argv.length; i++) {
选项:
--config <path> 配置文件 (.anet/nodes/<name>/config.json)
--alias <name> Agent 别名 / CommHub alias (必需)
--runtime <type> claude-agent-sdk (default) | codex-sdk | grok-build-acp
--runtime <type> claude-agent-sdk (default) | codex-sdk | codex-app-server | grok-build-acp
--model <name> AI 模型 (codex 默认: gpt-5.5, claude-agent-sdk 默认: claude-sonnet-4-6)
--hub <url> CommHub URL
--tools <list> 工具列表,逗号分隔 ("all" = 全部)
Expand All @@ -131,6 +132,7 @@ for (let i = 0; i < argv.length; i++) {
Runtime:
claude-agent-sdk Claude Agent SDK — Claude/MiniMax/Anthropic 兼容 API
codex-sdk Codex SDK — GPT-5.4,复用 codex 登录态
codex-app-server Codex app-server/TUI runtime — RFC-030 preview(生产门锁定)
grok-build-acp Grok Build ACP — xAI Grok Build via "grok agent stdio"
`);
process.exit(0);
Expand Down
27 changes: 26 additions & 1 deletion docs/rfcs/RFC-030-codex-tui-bridge.md
Original file line number Diff line number Diff line change
Expand Up @@ -658,7 +658,11 @@ Hub 必须先在 loopback 完成管理员初始化,再置于 TLS 反向代理

## 11. 启动与绑定 UX

以下命令是拟议产品 UX,当前主干尚未实现:
> **历史方案,禁止复制。** 本节的 `codex-sdk --codex-transport
> app-server` 形态已被 Vincent 2026-07-10 的独立 runtime 决策取代。
> 当前 canonical CLI 见 §18.1a;生产门状态见文首与配套设计 §8。

以下命令仅保留为被取代的设计记录:

```bash
# 创建一个普通 codex-sdk 节点,但选择共享 app-server transport
Expand Down Expand Up @@ -870,6 +874,27 @@ codex app-server generate-json-schema --out ./schemas
| 网络闭环:`send_task` → 桥 → 真 codex → `send_task` 回 | ✅ | 隔离 hub 真节点 e2e PASS |
| `anet node create --runtime codex-app-server` | ✅ | 隔离 hub 真建节点,config 写 `runtime:"codex-app-server"` |

### 18.1a Canonical `anet` CLI(preview 路径)

Canonical runtime 值是 **`codex-app-server`**,不是 `codex-sdk`。完成
`codex auth login` 后,可复制粘贴:

```bash
anet node create codex-human --runtime codex-app-server && anet node start codex-human
```

等价的分步命令是:

```bash
anet node create codex-human --runtime codex-app-server
anet node start codex-human
```

`node create` 将 canonical 值写入节点配置;`node start` 通过
`agent-node --runtime codex-app-server` 启动该 runtime。当前这条入口只启动
§18 记录的 Phase-0 direct preview path;它**不代表** Policy Gateway、Wave 2、
§8、merge、production 或 `latest` 已解锁。

### 18.1b 明确**未**落地(生产前置, 设计文档 §3.2/§4/§8)

- ❌ **Policy Gateway(方案 B)**:唯一 upstream app-server 连接、TUI 经 gateway UDS、request-id 双向重写、事件转发。当前是 TUI 与桥**直接双连接**。
Expand Down
Loading