fix: harden v0.2 code agent transport

This commit is contained in:
Codex
2026-05-31 22:54:50 +08:00
parent 88225c16ae
commit 9a14ed06ae
8 changed files with 553 additions and 10 deletions
@@ -206,6 +206,21 @@ Workbench trace 对已知 JSON-RPC gateway 响应应按普通 tool call 展示
## 短连接 result 轮询
## Codex app-server stdio 请求处理与 trace 可见性
repo-owned Codex app-server stdio client 必须把 app-server 发来的 JSON-RPC client request 当作一等协议处理,不能只等待 notification。请求 id 可能是数字也可能是字符串;只支持数字 id 会导致 approval/requestUserInput 等请求永远得不到响应,最终表现为已有 assistant partial output 后卡在 `waitingFor=app-server-notification`,直到 idle timeout。
非交互 HWLAB Code Agent 的处理规则如下:
- `item/commandExecution/requestApproval``item/fileChange/requestApproval``item/permissions/requestApproval` 必须自动返回可用的 approved 决策,优先使用 `availableDecisions` 中的 `approved``approved_for_session` 或其他 `approved*`,不能因为旧 approval gate 把真实工具调用拒绝掉。
- `item/tool/requestUserInput``mcpServer/elicitation/request` 必须返回结构化 denied/canceled;聊天运行中不能弹人工输入,也不能静默等待用户。
- 未知 client request 必须返回 JSON-RPC unsupported error,并在 trace 中记录 `client/request:unsupported`;禁止忽略请求。
- 每个已处理 client request 都必须追加 `client/request/handled` trace 事件,包含 method、decision、itemId/targetItemId,并把 `waitingFor` 推进到 `turn/completed`,避免前端只显示未知 `providerTrace 缺失``app-server-notification`
commandExecution trace 必须 bounded:命令文本和 stdout/stderr summary 默认只展示有界摘要,但必须保留 `commandBytes``commandTruncated``outputBytes` 和 stderr 摘要,复制/下载 trace 仍可按后端保留策略展开原始事件。前端和 CLI 不能为了避免输出爆炸而要求 Code Agent 默认加 `| head``grep` 或 shell pipe;输出裁剪应在 trace/result 层完成,不能把 stdout pipe 行为变成工具调用是否完成的隐式前提。
在 Linux container 内启动 Codex app-server 时,应优先直接执行 `@openai/codex-linux-*` native binary,并把同包 `path` 目录加入 `PATH`;只有找不到 native binary 时才回退到 npm wrapper。直接启动 native binary 可以减少 Node wrapper 双进程、孤儿进程和 close 后未清理进程组导致的 session 卡死风险。关闭 stdio client 时必须终止整个子进程组,不只关闭父进程 stdin。
Workbench 与 Code Agent 的用户请求必须是短连接 submit + 短连接 result/trace 轮询;浏览器或 cloud-web 不应持有一次长 HTTP 请求等待整个 Codex turn 结束。`POST /v1/agent/chat` 返回 `202` 后,前端轮询 `/v1/agent/chat/result/<traceId>` 获取终态,并用 `/v1/agent/chat/trace/<traceId>` 刷新可视 trace。
Code Agent backend 的 completed 语义只能来自真实 Codex app-server `turn/completed` 成功事件。`item/agentMessage/delta``item/completed`、已有 assistant 文本、transport close 或 activity idle timeout 都不能单独升级成 `status: "completed"`。如果已经收到部分 assistant 文本但没有收到 `turn/completed`,终态必须是 timeout/partial blocker,并保留 trace、session、thread、partial output 摘要和可重试提示;Workbench 只能显示“部分回复/超时”,不能标 DEV-LIVE reply pass。
+10 -1
View File
@@ -8,11 +8,12 @@
- 提供 WEB 等价的非视觉业务入口:登录鉴权、session 恢复、Device Pod 看板、Code Agent 对话、trace/result 轮询、logout 和工作台 live summary。
- 只走 Cloud Web 同源 API surface;默认 base URL 是 `http://74.48.78.17:19666`,也可通过 `--base-url``HWLAB_CLIENT_BASE_URL` 指向其他 Cloud Web 入口。
- 不直连 Postgres、Kubernetes Service、Secret、device-pod 内部 Service、gateway RPC 或本地 fixture;需要鉴权的请求使用 `/auth/*` 返回的 cookie 或显式 `--cookie`
- 默认业务子命令不直连 Postgres、Kubernetes Service、Secret、device-pod 内部 Service、gateway RPC 或本地 fixture;需要鉴权的请求使用 `/auth/*` 返回的 cookie 或显式 `--cookie`唯一例外是 `client gateway` 诊断族:它允许显式 `--api-base-url` 指向 Cloud API,用于短连接观测 gateway session、单次 shell invoke 和 transport 压测;该入口只验证底层传输稳定性,不替代 Web 用户流程授权,也不发布镜像或常驻服务。
- Pod 内透传执行不放进 `hwlab-cli`;需要进入正在工作的 Code Agent/Cloud API pod 时,`hwlab-cli` 只查询并输出 UniDesk 标准 route,实际透传由 UniDesk `bun scripts/cli.ts ssh 'G14:k3s:hwlab-v02:pod:<pod>:<container>' ...` 完成。`pod:` 是 route 语法,`/` 只用于 pod 内文件系统路径。
- `client runtime routes` 必须按当前运行 profile/lane 的数据生成 UniDesk `pod:` route;实现不得硬编码 `dev``v0.2``v0.3`、namespace 或 catalog path。新增版本只允许通过 `deploy.json.lanes[profile]` 声明 namespace、artifact catalog 和 service overrides,不为每个版本新增代码分支。
- 运行时不做内部证明型校验、旧健康诊断或重断言;CI/CD 只保留能证明代码可构建、语法正确和最小冒烟可用的校验。功能正确性通过 `hwlab-cli client` 短连接真实业务 E2E 暴露和修复。
- 专用子命令覆盖高频用户工作台;`client request METHOD /path` 覆盖 WEB 同源代理允许的其他非视觉 API。`client request` 只接受以 `/` 开头的 Cloud Web 相对路径,禁止绝对 URL,避免绕过 Cloud Web 直接打内部服务。
- `client gateway pressure` 是 device-pod/gateway 高频故障的真实业务传输压测入口;必须覆盖 small stdout、大 stdout、长单行 stdout、stderr flood、结构化 timeout 和超出 gateway inflight 上限的并发请求。所有场景必须返回 JSON、HTTP/route/traceId/requestId、字节数、truncated 标记、sha256 和 bounded preview;失败必须明确是 `http_*``stdout_not_truncated``stderr_not_truncated``timeout_not_observed``structured_gateway_busy` 等可定位原因,禁止无输出、长时间黑洞或只靠 shell pipe 截断。
- 输出默认是 JSON;任何失败都要有 `ok:false``action``status`、HTTP 状态、route 和可定位错误,不允许无 stdout 成功。可能返回大对象的 `client` 子命令默认返回紧凑摘要,避免高频排障输出爆炸;需要完整响应体时显式加 `--full`
- `device-pod-cli`/`hwpod``job output` 默认也必须返回紧凑 JSON:保留 job/status/blocker/freshness/text/evidence 摘要,省略嵌套 gateway dispatch 和长命令;需要完整 payload 时显式加 `--full`。Code Agent 和人工不得用 `| head``grep` 或 shell 管道作为默认输出压缩方式,避免 stdout pipe、子进程信号转发或长输出造成 commandExecution 黑洞。
- Code Agent 交互必须默认暴露 `traceId``resultUrl`、终态和 assistant 回复文本摘要;不能要求用户先拉全量 trace 再手工查找回复。
@@ -40,6 +41,9 @@
| `hwlab-cli client device-pods events POD` | `GET /v1/device-pods/{pod}/events` | 对应纯文本事件流。 |
| `hwlab-cli client device-pods probe POD` | `/debug-probe/chip-id``/io-probe/uart/1``/tail` | 对应 Target/Debug/IO 摘要。 |
| `hwlab-cli client runtime routes` | `GET /v1/live-builds` | 查询当前工作面 pod,并输出 UniDesk 标准 `pod:` route;不执行透传、不调用 kubectl、不内嵌 UniDesk。 |
| `hwlab-cli client gateway sessions` | `GET Cloud API /v1/gateway/sessions` | 显式 Cloud API 诊断入口,观察 gateway online/stale、inflight 和 capability;默认不带 Web cookie。 |
| `hwlab-cli client gateway invoke` | `POST Cloud API /v1/rpc/hardware.invoke.shell` | 显式 Cloud API 诊断入口,执行一次 bounded shell dispatch 并返回结构化 dispatch 摘要。 |
| `hwlab-cli client gateway pressure` | `POST Cloud API /v1/rpc/hardware.invoke.shell` | 显式 Cloud API 压测入口,真实验证大输出、长单行、stderr、timeout 和并发超容量不会造成黑洞。 |
| `hwlab-cli client agent send` | `POST /v1/agent/chat` + `GET /result/{trace}` | 以 short connection 提交 Code Agent 消息并轮询结果,默认输出 assistant 回复文本摘要。 |
| `hwlab-cli client agent trace TRACE` | `GET /v1/agent/chat/trace/{trace}` | 回放 trace,默认输出状态、事件摘要和 assistant stream 文本。 |
| `hwlab-cli client agent cancel TRACE` | `POST /v1/agent/chat/cancel` | 取消当前 Code Agent 请求。 |
@@ -78,6 +82,10 @@
阅读 docs/reference/spec-v02-hwlab-cli.md,然后在 `G14:/root/hwlab-v02` 用 cli 手动测试以下内容:运行 `hwpod job output --pod-id D601-F103-V2 <jobId> --api-base-url http://74.48.78.17:19667`,确认默认输出包含 `body.compacted=true`、状态、job 摘要和 bounded text,且不包含嵌套 `dispatch.command`;再加 `--full` 确认完整 payload 可按需展开。通过 `client harness submit` 让 Code Agent 执行同一 `hwpod job output`,确认 trace 中 commandExecution 可以完成,不需要 `| head`
## T7
阅读 docs/reference/spec-v02-hwlab-cli.md,然后在 `G14:/root/hwlab-v02` 用 cli 手动测试以下内容:运行 `node scripts/run-bun.mjs tools/hwlab-cli/bin/hwlab-cli.ts client gateway pressure --api-base-url http://74.48.78.17:19667 --gateway-session-id gws_D601_F103 --large-bytes 131072 --parallel 8 --request-timeout-ms 60000 --timeout-scenario-ms 1000 --no-auth --full`。确认 small stdout、大 stdout、长单行 stdout、stderr flood、timeout 和并发超容量全部返回结构化 JSON;大输出应显示 `stdoutBytes``stderrBytes``stdoutTruncated``stderrTruncated`、sha256 和 preview,超出 `maxInflightRequests` 的请求必须显示 `structured_gateway_busy`,不能出现无输出或卡死。
## 规格的实现情况
| 规格项 | 状态 | 说明 |
@@ -87,6 +95,7 @@
| JSON-RPC 同源 API | 目标状态 | `client rpc` 自动补齐 Web JSON-RPC envelope 的 `meta` 字段。 |
| 通用同源 API request | 目标状态 | `client request` 用于追平低频和新增 WEB API,禁止绝对 URL。 |
| G14 harness-ops 短连接能力 | 目标状态 | `client harness` / `client harness-ops` / `client harness-opt` 覆盖 submit/result/trace/wait/audit,只作为业务 API client。 |
| Gateway transport 压测 | 已实现 | `client gateway pressure` 只作为显式短连接诊断入口,覆盖大输出、timeout 和并发超容量的结构化返回。 |
| Device Pod job output 紧凑输出 | 已实现 | `hwpod job output` 默认省略嵌套 dispatch`--full` 才展开完整 payload,防止 Code Agent 通过 shell pipe 压输出。 |
| 本地 cookie session | 目标状态 | `.state/hwlab-cli/session.json` 只保存 cookie/session 摘要。 |
| 镜像/Service/Job template | 已废弃 | 相关 deploy、GitOps、artifact 和 Tekton 口径必须删除。 |
+8
View File
@@ -7,12 +7,15 @@
-`hwlab-cloud-api` 注册 gateway session、resource 和 capability。
- 通过主动轮询 `/v1/gateway/poll` 获取 cloud-api 分发的 `hardware.invoke.shell` 请求,并把结果回传 `/v1/gateway/result`
- 只做 transport 和 bounded command execution,不理解用户权限、device-pod grant 或业务授权。
- transport 稳定性是 P0 基础能力:大 stdout、长单行 stdout、stderr flood、命令 timeout、gateway 超容量并发和结果回传失败都必须返回结构化 JSON 状态;禁止让 poll/result、Cloud API request、Code Agent commandExecution 或 `hwlab-cli` 调用进入无 stdout、无 trace、无 terminal status 的黑洞。
## 内部架构
- `cmd/hwlab-gateway/main.ts` 维护 gateway state、outbound poll loop、inflight request map 和 command execution,运行入口必须使用 Bun。
- `internal/cloud/gateway-demo-registry.ts` 在 cloud-api 内保存 gateway session、队列和 pending result。
- command execution 默认关闭;只有显式 `HWLAB_GATEWAY_CMD_EXEC_ENABLED=1` 或 demo open 时才执行 shell。
- command execution 必须同时读取 stdout/stderr,并对每路输出做有界收集和 `stdoutTruncated`/`stderrTruncated` 标记,避免任一路 pipe 填满导致子进程或 gateway result 卡死。默认 bounded body 可以截断,但必须保留字节数、truncated 标记和 enough preview;需要完整超大输出时应另设计 evidence/spool,不得把完整无限输出塞进同步 JSON 响应。
- gateway inflight 上限是背压机制,不是黑洞机制。超过 `maxInflightRequests` 时,Cloud API/gateway 必须返回包含 `reason=gateway_busy``inflightCount``maxInflightRequests` 的结构化结果,调用方可重试或排队。
## API 接口说明
@@ -34,6 +37,10 @@
阅读 docs/reference/spec-v02-hwlab-gateway.md,然后用 cli 手动测试以下内容:通过 cloud-api `/v1/gateway/sessions` 观察 gateway online/stale 状态;不要直接从前端或普通用户请求 gateway shell。
## T3
阅读 docs/reference/spec-v02-hwlab-gateway.md,然后在 `G14:/root/hwlab-v02``hwlab-cli client gateway pressure` 手动测试以下内容:对目标 gateway 运行 small stdout、至少 128KiB stdout、至少 128KiB 单行 stdout、至少 128KiB stderr、短 timeout 和超过 gateway `maxInflightRequests` 的并发请求。验收条件是全部场景在 CLI timeout 内返回结构化 JSON;大输出显示 truncated 标记,timeout 显示 `timed_out`,超容量显示 `gateway_busy`,不能出现无输出、HTTP transport timeout 或 Code Agent trace 黑洞。
## 规格的实现情况
| 规格项 | 状态 | 说明 |
@@ -41,6 +48,7 @@
| health/status/capabilities | 已实现 | gateway service 提供只读观测。 |
| outbound poll/result | 已实现 | 与 cloud-api registry 对接。 |
| bounded shell execution | 已实现 | 受 env 开关、timeout 和 output limit 约束。 |
| gateway 压测闭环 | 已实现 | `hwlab-cli client gateway pressure` 覆盖大输出、timeout 和并发背压,不依赖 shell pipe 裁剪。 |
| device-pod grant/lease | 不在本服务 | 由 cloud-api/device-pod 负责。 |
| 生产级 gateway 多租户隔离 | 未完全实现 | 当前是 demo/transport skeleton。 |