# AgentRun Code Agent Dispatch

本文定义 HWLAB Code Agent 通过 AgentRun `v0.1` 调度 API 执行时的装配边界，也是 nested/child Code Agent provider profile 继承语义的唯一权威出处。它只描述稳定的 payload、SecretRef、credential、session/profile 归属和验收判定；CLI/Web 的用户入口只交叉引用本文，不重复定义继承规则。

## 装配入口

- 源码入口是 `internal/agent/agentrun-dispatch.mjs`。
- `createHwlabAgentRunDispatchAssembly()` 生成 AgentRun `POST /api/v1/runs`、`POST /api/v1/runs/:runId/commands` 和 `POST /api/v1/runs/:runId/runner-jobs` 三段 payload。
- `dispatchHwlabAgentRun()` 使用同一 assembly 顺序调用 AgentRun manager；调用方可以注入 `fetchImpl` 做合同测试或接入真实 manager。
- 默认 manager URL 是 `http://agentrun-mgr.agentrun-v01.svc.cluster.local:8080`，默认 namespace 是 `agentrun-v01`，默认 providerId 是 `G14`，默认 workspace branch 是 `v0.2`。
- 人工和 agent 触发 HWLAB Code Agent 任务时，优先使用已成型的 `hwlab-cli client agent session create/send/result/trace`、Workbench 同路径 API 或 CaseRun 封装入口；只有在修 adapter/dispatcher/合同测试时才手写 AgentRun payload。排查 provider、API key、session 或 trace 问题时，先用这些 CLI 原入口确认 runtime config、SecretRef、session、trace 和 result，再定位具体组件，不把问题绕成 generic provider API、runner shell、直连 Cloud API 或临时脚本路径。

## 会话和执行边界

- HWLAB `conversationId` / `sessionId` / `threadId` 是用户可见业务会话 authority；AgentRun `runId` / `commandId` / `runnerJobId` 是执行尝试 identity；AgentRun `SessionRef` 和 per-session PVC 承载 backend/profile 的续接状态。不要把新建 AgentRun run/job 等同于新建 HWLAB session，也不要把复用 workspace selection 当作 session state。
- AgentRun `type=steer` command 是作用在既有 target trace/target command 上的控制命令，不创建新的 HWLAB session，也不改变业务 `conversationId/sessionId/threadId`。HWLAB closeout 和 UI 展示必须同时保留 target command identity 与 `steerCommandId`，并从原 target trace 观察 `agentrun:steer:accepted` / `agentrun:steer:command-created`。steer command 创建成功只证明短连接控制动作已被 AgentRun 接收；目标 turn 后续 terminal 状态按原 command/result 判定。
- HWLAB adapter 调 AgentRun 时必须固定使用 AgentRun policy 边界字段：`tenantId=hwlab`、`projectId=pikasTech/HWLAB`、`providerId=G14`。HWLAB Workbench 的 project/workspace 标识只能作为 `metadata.hwlabProjectId`、`metadata.hwlabWorkspaceId` 或 `workspaceRef` 子字段保存，不能写入 AgentRun `projectId`。如果运行面出现 `tenant-policy-denied`、project mismatch 或 workspace project 污染，临时处理是修 adapter 的字段归一化并重放最小真实请求，不放宽 AgentRun tenant policy。
- 显式 HWLAB session 是用户可见 provider profile authority。`client agent session create --provider-profile <profile>` 建立 session 的 `providerProfile`，Cloud API 在 dispatch 时映射为 AgentRun `backendProfile`；后续 `client agent send --session-id <sessionId>` 在未显式传 `--provider-profile` 时继承该 session 的 `providerProfile`。workspace 只记录当前已选 session 和展示 seed，不能作为 child agent profile 继承来源，也不得用旧 workspace 状态覆盖显式 session。
- 同一 HWLAB session 的 resume 判定看同一个 `sessionId`、`threadId`、`providerProfile/backendProfile`、AgentRun `SessionRef` 和 PVC，而不是只看是否复用了同一个 AgentRun `runId` 或 runner Job。runner pod 被删、Job 被重建或 lease 失效后的临时恢复可以创建 replacement run/job，但只有在复用同一 `SessionRef`/PVC/thread、没有拼接历史 prompt 且 assistant 能看到前序上下文时，才算 session 持久化恢复证据；它不替代 runner reuse window 内复用同一 run/runner 的长期目标。

## Provider Profile 与 Child Agent 继承

Nested/child `hwlab-code-agent spawn` 的 provider profile 继承只走环境变量通道。有效来源和优先级固定为：

1. 显式 `spawn --profile <profile>`。
2. runner 环境变量 `HWLAB_CODE_AGENT_PROVIDER_PROFILE`。

未提供这两项时，`spawn` 必须 fail fast 返回 `provider_profile_required`。不得从 `skills/hwlab-code-agent/config.json`、账号 workspace、Cloud API 默认值、runner 默认 `deepseek`、历史 session、模型名或 profile 静态 allowlist 推断 child profile；也不得用 silent fallback 掩盖缺失配置。

Parent -> child 传播链固定如下：外层 HWLAB request/session 解析出 `providerProfile` -> Cloud API/AgentRun adapter 解析当前 AgentRun `backendProfile` -> runner job `transientEnv` 注入 `HWLAB_CODE_AGENT_PROVIDER_PROFILE=<backendProfile>` 和 `HWLAB_CODE_AGENT_PARENT_TRACE_ID=<traceId>` -> runner 内的 `hwlab-code-agent spawn` 在没有显式 `--profile` 时读取该环境变量。`HWLAB_CODE_AGENT_PARENT_TRACE_ID` 只用于 trace/diagnostic 关联，不参与 profile 选择。

可见性必须跟随同一链路：`spawn` JSON 输出暴露 `resolvedProviderProfile`、`profileSource` 和 `parentTraceId`；child trace/result 必须暴露真实 `providerProfile/backendProfile`、AgentRun infrastructure backend、provider 和 model。CaseRun `d601-f103-v2-leader-review` 使用 `dsflash-go` 时，Leader -> Coder/Reviewer child traces 应落到 `trc_dsflash-go_*`，child result backend 为 `agentrun-v01/dsflash-go`，model 为 `deepseek-v4-flash`，并由 compile check 与 reviewer verdict 形成业务通过证据。

Provider profile slug 与配置是 AgentRun profile 管理数据，不是 Cloud API/Web 每次新增 slug 都要修改代码的枚举。HWLAB 已鉴权 CLI/Web/API 通过 [spec-v02-provider-management.md](spec-v02-provider-management.md) 委托 AgentRun 管理 profile config/credential/validate；只有共享运行面、bridge、SecretRef 约定或镜像变化才进入 GitOps/CI/CD。

## 架构混乱处理

- 临时处理：排查 CLI/Web/Cloud API/AgentRun 对 session、project、provider 或 run/job 的口径不一致时，先收集 HWLAB session status、workspace selection、trace/result、AgentRun run/job env、`SessionRef`、PVC phase 和 command events；以单变量热补丁证明最小链路，再回到源码 PR/CI/CD/原入口复测。不得通过长 prompt、历史 messages、fake `thread/resume:completed`、放宽 tenant policy 或手工改 DB lease 来掩盖缺口。
- 长期建议：跨仓库 schema 必须把 `hwlabProjectId` / `agentRunProjectId`、`providerProfile` / `backendProfile`、`hwlabSessionId` / `agentRunSessionId`、`runId` / `sessionId` 分成不同字段；Web 和 CLI 应同时展示业务 session/thread/provider 与执行 run/job/command，避免 operator 把执行尝试误判为业务会话迁移。

## Credential 边界

- Provider credential 只通过 `executionPolicy.secretScope.providerCredentials[]` 的 AgentRun provider SecretRef 引用。默认 SecretRef 随 `backendProfile` 解析为 `agentrun-v01-provider-<backendProfile>`；历史 alias 只允许表达已有 Secret 命名事实，例如 `codex-api -> codex`。动态 profile（如 `dsflash-go`）不得要求 HWLAB service 代码新增 allowlist 后才能 dispatch。
- Provider API Key 的 Web 配置面归属 HWLAB `v0.2` 管理页，HWLAB Cloud API 通过已鉴权的管理接口委托 AgentRun 后端更新 profile Secret/配置；dispatch 路径只消费 AgentRun 返回的 SecretRef，不接收浏览器提交的 API Key 原文。完整管理规格见 [spec-v02-provider-management.md](spec-v02-provider-management.md)。
- GitHub PR/issue 能力通过 `toolCredentials[].tool=github` 注入，默认 SecretRef 是 `agentrun-v01-tool-github-pr` key `GH_TOKEN`。
- UniDesk SSH passthrough 通过 `toolCredentials[].tool=unidesk-ssh` 注入，默认 SecretRef 是 `agentrun-v01-tool-unidesk-ssh` key `UNIDESK_SSH_CLIENT_TOKEN`。
- `UNIDESK_SSH_CLIENT_TOKEN` 只授予 UniDesk frontend `/ws/ssh` scoped client 能力；route allowlist 由 UniDesk frontend 配置控制。HWLAB 不持有 provider token、主 server SSH key 或完整 frontend 登录态。
- `runnerJob.transientEnv` 只能承接本次 runner job 需要的短期执行上下文，例如 owner-scoped `HWLAB_API_KEY`、`HWLAB_RUNTIME_API_URL` 和 `UNIDESK_MAIN_SERVER_IP`；敏感项必须标记 sensitive，不得承载 GitHub token、UniDesk SSH client token、provider key、长期 SSH key 或 registry token。
- HWLAB v0.2 HWPOD 能力在 runner 内只通过 `hwpod -> hwpod-compiler-cli -> HWLAB_RUNTIME_API_URL -> hwlab-cloud-api /v1/hwpod-node-ops` 进入。`HWLAB_RUNTIME_API_URL` 指向当前 namespace 的 `hwlab-cloud-api` Service；`HWLAB_RUNTIME_WEB_URL` 仅供需要浏览器同源语义的工具查看 Cloud Web。AgentRun 不以 Cloud Web 代理替代 HWPOD API。

## ResourceBundle

- AgentRun run 必须使用 `ResourceBundleRef.kind="gitbundle"`，默认 repo 是 `http://git-mirror-http.devops-infra.svc.cluster.local/pikasTech/HWLAB.git`。
- `commitId` 必须是完整 40 字符小写 SHA，不接受 branch、tag、`HEAD` 或短 SHA；`workspaceRef` 只描述目标 repo/branch，默认 branch 是 `v0.2`，实际 checkout 身份以 `resourceBundleRef.repoUrl + commitId` 为准。
- 默认 `bundles[]` 只有两项：`{ name: "hwlab-tools", subpath: "tools", target_path: "tools" }` 和 `{ name: "hwlab-agent-skills", subpath: "skills", target_path: ".agents/skills" }`。不得再发送 `toolAliases`、`skillRefs`、`workspaceFiles`、`subdir` 或 `sparsePaths`。
- repo 自身必须提供 extensionless 工具入口，例如 `tools/hwpod`、`tools/hwpod-ctl`、`tools/hwpod-compiler`、`tools/unidesk-ssh` 和 `tools/hwlab-code-agent`；AgentRun 只把 materialized `tools/` 加入 PATH，不再生成 alias wrapper。
- HWPOD 快速阶段没有独立 lease 控制命令；标准 `hwpod` surface 只提交 HWPOD intent，经 compiler 生成 node-ops。Agent 不得新增 gateway shell 或其他 fallback 来绕过 `hwpod -> hwpod-compiler -> /v1/hwpod-node-ops -> hwpod-node`。
- 默认 `promptRefs` 指向 `internal/agent/prompts/hwlab-v02-runtime.md`，`inject=thread-start` 且 `required=true`。该 prompt 只在 AgentRun/Codex stdio 新 thread 首轮注入；后续 turn 必须依赖原生 `thread/resume`，不得在 command payload 中拼接历史、旧 skill 列表或长业务 prompt。
- skills 由 `skills/` subtree 作为整体复制到 `.agents/skills`。HWLAB 不再依赖 `/app/skills`、hostPath、默认 Codex skill registry、ConfigMap、workspace seed files、`workspaceFiles` 或用户长 prompt 作为 AgentRun runner 的 skill 注入 fallback。
- AgentRun runtime image 已预装 `gh`，结合 `tool=github` 注入的 `GH_TOKEN` 即可访问 HWLAB/UniDesk PR 与 issue；不得把 GitHub token 放入 prompt 或 `transientEnv`。

## 验收

- 源码合同测试：`node --test internal/agent/agentrun-dispatch.test.mjs`。
- 语法检查：`node --check internal/agent/agentrun-dispatch.mjs && node --check internal/agent/agentrun-dispatch.test.mjs`。
- 合同必须证明 `UNIDESK_SSH_CLIENT_TOKEN` 不出现在 `transientEnv`，GitHub/UniDesk SSH 能力都通过 AgentRun `toolCredentials` SecretRef 装配，且 runner resource bundle 默认使用 `kind="gitbundle"`，trace 暴露 `bundles`、`tools`、`promptRefs` 和 `skillDirs`。
- 真实 CLI 验收必须使用目标 profile 显式创建 session，例如 `--provider-profile dsflash-go`，再发送至少一个不传 `--provider-profile` 的 turn，证明 send 继承 session profile；不得因为某个 provider 失败自动切换 profile 并把结果记为原 profile 通过。
- Nested profile 继承验收必须覆盖 runner env：父 AgentRun result/trace 显示目标 `backendProfile`，child `spawn` 输出 `resolvedProviderProfile=<profile>`、`profileSource=env`、`parentTraceId=<outerTraceId>`，child trace/result 使用同一个 profile/model/backend。缺少任意字段时先修可见性，不新增旧 fallback、旧门禁或兼容分支。
