190 lines
15 KiB
Markdown
190 lines
15 KiB
Markdown
# Code Agent Chat Readiness Runbook
|
||
|
||
本文定义 Cloud Workbench `POST /v1/agent/chat` 的 readiness 判定。它只处理
|
||
Code Agent 回复链路是否具备复测条件,不修复、不读取、不打印、不创建也不修改任何
|
||
Secret 或 token。
|
||
|
||
## 运行边界
|
||
|
||
- DEV Cloud Web 入口是 `http://74.48.78.17:16666/`,同源代理到
|
||
`/v1/agent/chat`。
|
||
- DEV API/edge 入口是 `http://74.48.78.17:16667/`;它不能替代 Workbench
|
||
同源聊天入口的真实回复证据。
|
||
- `internal/cloud/code-agent-chat.mjs` 是 `/v1/agent/chat` 的后端处理入口。
|
||
- `scripts/code-agent-chat-smoke.mjs` 是 Code Agent chat schema 与 readiness
|
||
gate。
|
||
- `scripts/dev-cloud-workbench-smoke.mjs --static` 只验证 Workbench 源码合同和
|
||
`/v1/agent/chat` 前端接线;它不是 DEV-LIVE 回复证明。
|
||
|
||
## Provider 前置条件
|
||
|
||
当前 DEV 部署合同中 `hwlab-cloud-api` 的 Code Agent provider 是
|
||
`HWLAB_CODE_AGENT_PROVIDER=codex-stdio`。运行时必须具备 repo-owned Codex MCP
|
||
stdio session supervisor,并证明 `/workspace/hwlab` 可读写、`CODEX_HOME=/codex-home`
|
||
可写、`/app/node_modules/.bin/codex --version` 可执行、`codex mcp-server`
|
||
可通过 MCP initialize/tools/list 观测到 `codex` 与 `codex-reply`。
|
||
|
||
Codex token boundary 仍由授权路径把 `OPENAI_API_KEY` 注入到 DEV runtime。DEV Pod
|
||
必须使用 `HWLAB_CODE_AGENT_OPENAI_BASE_URL` 指向受控 DEV egress/proxy 路径
|
||
`http://172.26.26.227:17680/v1/responses`,不能直接指向 public `api.openai.com`。
|
||
|
||
Runner 不得尝试修补、读取、回显或替换该 Secret。若 DEV runtime 缺少该授权凭证注入,
|
||
`provider_unavailable` 且 `error.missingEnv` 包含 `OPENAI_API_KEY` 必须判为
|
||
`BLOCKED/credential`。
|
||
|
||
部署前和部署后的自动化只允许证明 env 名称、`secretKeyRef` 的 Secret 名和 key 名、以及
|
||
DEV egress/base-url 合同是否声明和保留;不得读取 Secret data,也不得把 Secret 值写入
|
||
report、issue、PR 或截图。
|
||
|
||
## 判定标准
|
||
|
||
| 观测结果 | readiness |
|
||
| --- | --- |
|
||
| `status: "failed"`,`error.code: "provider_unavailable"`,且 `error.missingEnv` 包含 `OPENAI_API_KEY` | `BLOCKED/credential`;provider 凭证缺失,不能标真实回复通过。 |
|
||
| `provider: "codex-readonly-runner"` 且 `sessionMode: "controlled-readonly-session-registry"`、`capabilityLevel: "read-only-session-tools"`、`session.status` 为 `idle/ready/busy`、`session.idleTimeoutMs` 和 `session.lastTraceId` 存在、`session.longLivedSession: true`、`longLivedSessionGate.status: "blocked"`、`runnerLimitations` 包含 `not-codex-stdio` / `not-write-capable` / `process-local-session-registry` | 可标为 #317 read-only long-lived session registry pass;不能关闭 full Codex stdio/session capability blocker。 |
|
||
| `codexStdioFeasibility.status: "blocked"`,或 blocker 包含 `codex_cli_binary_missing`、`codex_cli_not_executable`、`codex_cli_native_dependency_missing`、`runner_lifecycle_missing`、`stdio_protocol_not_wired`、`workspace_mount_missing`、`workspace_write_boundary_blocked`、`codex_home_missing`、`codex_home_write_blocked`、`provider_token_boundary` | 真实 Codex stdio / 等价 long-lived runner 未具备;必须按 blocker 处理,不能表述为完整 Codex session。 |
|
||
| `status: "completed"`,但来自 mock、fixture、本地 stub、source-only smoke、浏览器本地回显或人工拼接 | 不是 DEV-LIVE reply pass。 |
|
||
| 真实 DEV `POST /v1/agent/chat` 返回 `status: "completed"`,且 `reply.content` 是非空 assistant 回复 | 可标 DEV-LIVE reply pass。 |
|
||
| 传输失败、schema 不完整、HTTP 非预期、`reply.content` 为空或缺失 | `BLOCKED`,按 runtime/schema/transport 分析。 |
|
||
|
||
只有“真实 DEV 路由 + `completed` + 非空 assistant reply”能作为 DEV-LIVE 回复通过依据。
|
||
不得把 mock、fixture、本地 echo、source report、静态检查或前端状态当作通过。
|
||
|
||
## Runner 能力边界
|
||
|
||
`/v1/agent/chat` 可以先落地受控只读能力,但必须诚实区分:
|
||
|
||
- `controlled-readonly-session-registry`:由 cloud-api 进程内 registry 保存
|
||
`conversationId/sessionId` 映射、`status`、workspace、sandbox、`createdAt`、`updatedAt`、
|
||
`idleTimeoutMs`、`lastTraceId`、`turn` 计数与只读工具 trace。它可以覆盖 `pwd`、
|
||
`skills.discover`、`ls`、`rg --files` 和 bounded `cat`,输出必须限长和脱敏。该模式是
|
||
read-only long-lived session,但 `longLivedSessionGate` 必须保持 `blocked`,直到 Codex
|
||
stdio 或等价 full Code Agent 协议通道真实接通。
|
||
- 该模式必须同时标记 `not-codex-stdio`、`not-write-capable`、`process-local-session-registry`。它不是
|
||
Codex stdio / workspace-write session,不提供写文件、任意 shell、硬件写、Secret/kubeconfig/DB URL
|
||
读取,也不证明 M3/M4/M5 trusted green。
|
||
- OpenAI Responses fallback 只能标记为 `openai-responses-fallback` /
|
||
`text-chat-only`,不得满足 Codex runner capability gate。默认路径不得用 fallback
|
||
冒充完整 Code Agent;仅明确允许 fallback 时才可作为普通文本备用通道返回。
|
||
- 已登记 PC gateway `shell.exec` capability 是受控硬件能力例外:当请求能从显式
|
||
`projectId/gatewaySessionId/resourceId/capabilityId` 或唯一 DEV MVP topology 解析时,
|
||
`/v1/agent/chat` 可以选择 `hwlab-hardware-capability` runner,经 cloud-api 进程内
|
||
JSON-RPC helper 调用 `hardware.invoke.shell`。该 helper 必须使用冻结的
|
||
`hwlab-cloud-api` service id 常量,返回 payload 需包含 `toolCalls[]`、`operationId`、
|
||
`dispatchStatus`、`exitCode`、stdout/stderr 摘要、`runnerTrace.eventLabels` 与
|
||
`capabilityLevel`。拓扑缺失、不唯一、gateway offline、session/capability missing、
|
||
dispatch failed 或 JSON-RPC meta/serviceId 无效时必须返回结构化 blocker,不能转入
|
||
Codex stdio 或 OpenAI 文本 fallback 冒充成功。
|
||
- `security.hardware-boundary` 仍然阻断直接 gateway/box-simu/patch-panel URL、`/invoke`、
|
||
`/sync/tick` 和泛化硬件写绕过;受控 `hardware.invoke.shell` capability route 只允许
|
||
已登记、可解析的 cloud-api 调用,不开放任意 shell。JSON-RPC/REST bridge 拒绝未知
|
||
`serviceId` 时,错误原因应可见为 `unknown serviceId ...` 或等价结构化 reason,且不得泄露
|
||
Secret/token。
|
||
|
||
当前 DEV/runtime 若要升级为完整 #275 runner,至少需要 repo-owned 的 Codex CLI/stdio 或等价
|
||
runner 二进制/协议适配、session supervisor 生命周期、workspace mount 与 sandbox 合同、token/Secret
|
||
注入边界、trace/cancel/reap 机制,以及与 cloud-api/workbench 的持久 session 映射。缺任一项时,必须
|
||
在 `codexStdioFeasibility` 中报告 blocker。
|
||
|
||
## PC Gateway Windows Skill 调用
|
||
|
||
Code Agent 通过已登记 PC gateway 执行 Windows 侧命令时,必须让 Codex turn 自己调用仓库 wrapper,不能由 cloud-api 字符串匹配短路到 gateway,也不能直接访问 gateway URL:
|
||
|
||
```sh
|
||
node /app/tools/hwlab-gateway-shell.mjs --json --timeout-ms <ms> --powershell-stdin <<'PS1'
|
||
<bounded PowerShell script>
|
||
PS1
|
||
```
|
||
|
||
PowerShell 默认使用 wrapper 的 `-EncodedCommand` 路径;不要手写 `cmd /c powershell ...` 的管道、引号或中文转义。简单 `cmd` 命令仍可用 `--command "cmd /d /s /c ..."`,但涉及目录枚举、Unicode、管道、排序或 JSON 输出时优先用 `--powershell-stdin`。
|
||
|
||
Workbench 会把“Gateway 命令超时”控件的毫秒值随 `/v1/agent/chat` 传入 `gatewayShellTimeoutMs`。Codex prompt 必须把该值落实到 wrapper 的 `--timeout-ms`,cloud-api `hardware.invoke.shell` dispatch timeout 必须取环境配置、请求 `input.timeoutMs` 和 120s 默认值中的较大值并加 grace;不得让 20s/30s 的旧默认提前返回 `dispatchStatus=timed_out`。wrapper 自身 HTTP request timeout 要比 shell timeout 稍长,确保用户看到的是 gateway/cloud 返回的结构化 `status/operationId/dispatch`,不是 wrapper 先超时丢失结果。
|
||
|
||
调大 timeout 不能替代正确的长任务控制语义。Gateway poll loop 必须支持后台 in-flight 执行,长 Keil/UV4 命令运行期间仍能处理短 `job-status`、state/log 读取和健康探测;如果 trace 出现 `shellExecuted=false` 的 dispatch timeout,优先检查 gateway 是否队头阻塞或离线,而不是把所有 wrapper 调用改成长等待。
|
||
|
||
Workbench trace 对已知 JSON-RPC gateway 响应应按普通 tool call 展示:首行展示 `tool hardware.invoke.shell status=<status> op=<operationId> exit=<exitCode> s=<duration>`,正文展示 request、gateway/resource/capability、dispatch、command、audit/evidence 以及有界 stdout/stderr。不要把整段 JSON 原样刷屏;复制/下载完整 trace 仍保留原始 JSON。
|
||
|
||
## 短连接 result 轮询
|
||
|
||
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。
|
||
|
||
`/v1/agent/chat/result/<traceId>` 是终态摘要接口,不是完整 trace 下载接口。它可以携带压缩后的 `runnerTrace` 窗口用于当前 UI 刷新,但必须保留 `eventCount`、`lastEvent`、`providerTrace`、`threadId/sessionId` 和终态 reply/blocker;完整 trace 只能从 `/v1/agent/chat/trace/<traceId>`、复制 JSON 或下载 trace 入口取得。默认 result trace 窗口上限由 `HWLAB_CODE_AGENT_RESULT_TRACE_EVENT_LIMIT` 控制;不要把数百个大 chunk 原样塞进 result 响应,避免 cloud-web 代理层或浏览器 fetch 把“正常执行中的大响应”表现成 503、非 JSON 或空响应。
|
||
|
||
result 轮询的 408/425/429/5xx、浏览器 timeout、非 JSON 或空响应应按“可恢复传输抖动”处理:前端先拉取一次 trace 刷新活性,再带退避继续轮询,只有后端返回结构化 terminal blocker、真实终态失败,或 trace 按无新事件 idle timeout 超时,才向用户显示失败。只要 `/trace` 仍显示新事件或 `waitingFor` 仍在推进,就不能把一次 result poll 失败标成“Code Agent API 错误”并停止。
|
||
|
||
Windows 侧 skill、编译器、脚本工具和多参数命令都应走同一个通用传输模式,不新增某个工具的专用 wrapper 子命令:
|
||
|
||
- 工作目录优先用 wrapper 的 `--cwd <windows path>` 或脚本内 `Set-Location -LiteralPath <path>` 表达,不要在 prompt 里拼 `cmd /c "cd ... && ..."`。
|
||
- 命令参数在 PowerShell 脚本里用数组或显式变量传递,例如 `$args = @("subcommand", "-p", $projectPath)` 后 `& $exe @args`,避免让模型手动嵌套引号。
|
||
- 对所有 Windows skill 都先复用 `C:\Users\liang\.agents\skills\<skill>\SKILL.md` 和该 skill 自带 CLI;Code Agent 只负责通过 repo wrapper 调用,不把 skill 行为复制到 cloud-api 或 wrapper。
|
||
- 一次探测命令失败后,只做一次更窄、更结构化的修正;若仍失败,返回失败 `operationId`、stderr 摘要和下一步,而不是连续试错。
|
||
|
||
Windows 文件系统探测必须是有界小输出:
|
||
|
||
- 对 `F:\work`、`F:\work\ConStart` 或同类目录先做顶层目录/项目标记探测,不要读取 Secret、env、kubeconfig、DB URL 或完整源码内容。
|
||
- 使用 `-LiteralPath`、`Select-Object -First <N>`、`ConvertTo-Json -Compress -Depth <N>`,stdout 目标控制在约 12 KB 内。
|
||
- wrapper 的 PowerShell prologue 已设置 UTF-8 console/output,并提供 `Read-HwlabText`、`Select-HwlabText`、`ConvertTo-HwlabJson`。读取中文 `SKILL.md`、Keil 日志或 manifest 时优先用这些 helper,避免 `Get-Content`/`Select-String` 的扩展对象字段和系统代码页造成乱码。
|
||
- 不要先输出完整目录 JSON 再依赖终端截断;需要更多信息时按明确候选项目二次查询。
|
||
- 如果命令已经到达 gateway 但因脚本语法或输出大小失败,只允许简化修正一次;最终回复要记录失败 `operationId`、修正后的成功 `operationId` 和有界输出摘要。
|
||
|
||
Keil 编译、下载或探测请求必须优先使用 Windows 侧 skill,而不是在 prompt 中重写 Keil 调用逻辑:
|
||
|
||
```sh
|
||
cd C:\Users\liang\.agents\skills\keil
|
||
py -3 keil-cli.py build -p <project.uvprojx> -t <target>
|
||
py -3 keil-cli.py job-status <job_id>
|
||
```
|
||
|
||
多 probe、烧录和 reset-run 的具体参数以 Windows 侧 `C:\Users\liang\.agents\skills\keil\SKILL.md` 为准;Code Agent 只负责通过 repo wrapper 调用该 skill CLI 并返回 trace、operation/evidence 和 bounded stdout/stderr 摘要。
|
||
|
||
对 build/download 这类长任务,Code Agent 应优先使用 skill 自带的异步 job 语义:启动命令用短 wrapper timeout 拿到 job id 或明确的启动失败,再用短 `job-status`、state 文件和日志读取轮询进展。除非用户明确要求同步等待并设置了足够大的 Gateway 命令超时,不要通过 gateway 执行 `--wait` 长轮询;同步等待会占用一个 in-flight 槽位,旧 gateway 还会造成队头阻塞。
|
||
|
||
## Smoke Gate
|
||
|
||
本地合同检查:
|
||
|
||
```sh
|
||
node scripts/code-agent-chat-smoke.mjs
|
||
```
|
||
|
||
该命令验证 schema、`provider_unavailable` provider gap、`OPENAI_API_KEY`
|
||
missing-env 分类,以及本地 stub completion 不能升级为 DEV-LIVE pass。
|
||
|
||
Workbench 静态接线检查:
|
||
|
||
```sh
|
||
node scripts/dev-cloud-workbench-smoke.mjs --static
|
||
node scripts/dev-cloud-workbench-smoke.mjs --dom-only --url http://74.48.78.17:16666/
|
||
```
|
||
|
||
该命令验证 Workbench 默认页、同源只读边界和 `/v1/agent/chat` 前端主流程接线。它只产出
|
||
`SOURCE` 级证据。`--dom-only` 会保留部署 runtime/web-asset identity preflight,
|
||
但只做真实 DEV DOM/help 只读观察;它不会发送 `/v1/agent/chat`,Code Agent journey
|
||
必须记录为 `not_applicable`,不能冒充真实 DEV-LIVE reply。
|
||
|
||
授权凭证注入后的真实 DEV 复测:
|
||
|
||
```sh
|
||
node scripts/code-agent-chat-smoke.mjs --live --url http://74.48.78.17:16666/
|
||
```
|
||
|
||
`--live` 会向真实 DEV `/v1/agent/chat` 发送一条最小聊天请求。输出只包含
|
||
readiness、provider/model/backend、assistant 回复是否非空和长度、错误分类等摘要;不打印
|
||
assistant 回复正文,不读取或打印任何 Secret 值。
|
||
|
||
## 复测结果解释
|
||
|
||
- 若输出 `readiness.level: "BLOCKED/credential"`,后续动作是由授权路径注入
|
||
`hwlab-code-agent-provider/openai-api-key`,不是由 runner 临时补 Secret。
|
||
- 若输出 `readiness.level: "#143 DEV-LIVE reply pass"`,只说明真实回复链路通过;
|
||
它不自动证明 M3、M4、M5 或硬件闭环通过。
|
||
- 若 `scripts/dev-cloud-workbench-smoke.mjs --static` 通过,而 `--live` 未通过,结论是
|
||
Workbench 接线和源合同通过,但真实 provider readiness 仍 blocked。
|
||
|
||
## 稳定来源
|
||
|
||
- [docs/reference/cloud-workbench.md](cloud-workbench.md):Cloud Workbench 默认页和同源边界。
|
||
- [docs/reference/dev-runtime-boundary.md](dev-runtime-boundary.md):DEV 端口、k3s 与运行态边界。
|
||
- [docs/reference/architecture.md](architecture.md):`SOURCE`、`LOCAL`、`DRY-RUN`、
|
||
`DEV-LIVE`、`BLOCKED` 证据分级。
|