266 lines
20 KiB
Markdown
266 lines
20 KiB
Markdown
# v0.1 装配 SPEC(RuntimeAssembly)
|
||
|
||
本文是 AgentRun `v0.1` runner/backend 启动前的权威装配 SPEC。所有会进入运行时容器、进程、文件系统或环境变量的执行输入,都必须先落到本文定义的装配模型,再由 manager/runner 渲染为受控 Job 输入;不得在 CLI、Queue、runner Job、issue 过程或临时热补丁中绕过装配模型直接拼接 credential、host path 或隐式环境。
|
||
|
||
`RuntimeAssembly` 只回答一个问题:一次 run 到底用哪份 backend 镜像、哪个 profile/credential scope、哪份 session、哪份代码、初始 prompt、skill 和工具 credential。`BackendImageRef`、`ProfileRef`、`SessionRef`、`ResourceBundleRef` 仍是四个一等运行时要素;credential 注入不是第五个杂项要素,而是挂在 `ProfileRef`、`ResourceBundleRef` 或 tool scope 上的 SecretRef 装配引用。初始 prompt 与 skill 注入也不是第五条运行时路径,它们属于 `ResourceBundleRef` 指向的 Git-only 非敏感内容。
|
||
|
||
## 最简四要素
|
||
|
||
`v0.1` 只保留四个一等要素:
|
||
|
||
| 要素 | 最小字段 | v0.1 含义 | 不包含 |
|
||
| --- | --- | --- | --- |
|
||
| `BackendImageRef` | `image` | digest-pinned backend/runner 镜像。 | API KEY、profile config、用户代码、session 文件。 |
|
||
| `ProfileRef` | `profile`、`secretRef` | provider profile 和 API KEY/配置 SecretRef。 | backend 镜像、session、repo 文件、GitHub/业务工具 credential。 |
|
||
| `SessionRef` | `sessionId` 或 `null` | backend 会话文件持久化引用;P0 可以为 `null`。 | API KEY、完整 `CODEX_HOME`、Git workspace。 |
|
||
| `ResourceBundleRef` | `repoUrl`、`commitId`,可选 `toolAliases`、`promptRefs`、`skillRefs` | 初始代码/文件输入,以及同一 commit 下的非敏感工具别名、初始 prompt 和 skill manifest;P0 固定 Git-only。 | 上传文件、对象存储 artifact、inline env、Secret value、会话历史。 |
|
||
|
||
P0 最小 JSON 形态:
|
||
|
||
```json
|
||
{
|
||
"backendImageRef": {
|
||
"image": "127.0.0.1:5000/agentrun/agentrun-mgr@sha256:..."
|
||
},
|
||
"profileRef": {
|
||
"profile": "codex",
|
||
"secretRef": { "name": "agentrun-v01-provider-codex", "keys": ["auth.json", "config.toml"] }
|
||
},
|
||
"sessionRef": null,
|
||
"resourceBundleRef": {
|
||
"repoUrl": "git@github.com:pikasTech/unidesk.git",
|
||
"commitId": "<full commit sha>",
|
||
"toolAliases": [],
|
||
"promptRefs": [],
|
||
"skillRefs": []
|
||
}
|
||
}
|
||
```
|
||
|
||
`executionPolicy`、`observabilityPolicy`、tenant identity、network、GC、failureKind、provenance、resource limit、tool credential scope、初始 prompt 装配和 skill 装配都不是新的运行时要素;它们分别挂在四要素或 run policy 上,并且必须能追溯到 SecretRef、Git commit/path/hash、配置引用或显式 null/deferred 状态。
|
||
|
||
## 装配对象与 credential 归属
|
||
|
||
任何 credential 注入都必须先归类,再进入对应装配路径:
|
||
|
||
| credential 类别 | 装配归属 | 运行时投影 | 规则 |
|
||
| --- | --- | --- | --- |
|
||
| Provider credential | `ProfileRef` / `executionPolicy.secretScope.providerCredentials[]` | profile-scoped 只读 Secret projection,再复制到 per-run writable `CODEX_HOME` | 只服务 `codex`/`deepseek`/`minimax-m3` backend profile;缺失为 `secret-unavailable`,不得 fallback。 |
|
||
| Git resource credential | `ResourceBundleRef.credentialRef` | 只服务 resource materialization 的 Git fetch/checkout | 只能用于拉取 `ResourceBundleRef.repoUrl` 对应代码,不得暴露给 agent shell 作为通用 GitHub token。 |
|
||
| Tool credential | `executionPolicy.secretScope.toolCredentials[]` | 由 runner 按 tool scope 投影为文件或 env,并只暴露给当前 run/command 允许的工具 | 用于 GitHub PR、issue、UniDesk SSH passthrough、artifact registry 等 agent shell 工具能力;不等同于 AgentRun integration,不触发 GitHub sink/OA/Event 之类外部动作记录。 |
|
||
| Short-lived execution context | runner-job `transientEnv` | 单次 Job env,response/dry-run/event 只显示 name/hash | 只用于业务 dispatcher 生成的短期上下文,例如 HWLAB device-pod session token 和非敏感服务地址;不得承载 provider credential、GitHub token、UniDesk SSH client token、长期 SSH key 或可复用 API key。 |
|
||
|
||
`toolCredentials` 是装配 SPEC 中的受控扩展槽位,用于把 agent 运行时需要的外部工具授权从“临时 env”收敛为 SecretRef。`v0.1` 支持 GitHub PR/issue 与 UniDesk SSH passthrough 所需的最小 env projection,例如:
|
||
|
||
```json
|
||
{
|
||
"toolCredentials": [
|
||
{
|
||
"tool": "github",
|
||
"purpose": "pull-request",
|
||
"secretRef": {
|
||
"namespace": "agentrun-v01",
|
||
"name": "agentrun-v01-tool-github-pr",
|
||
"keys": ["GH_TOKEN"]
|
||
},
|
||
"projection": { "kind": "env", "envName": "GH_TOKEN" }
|
||
},
|
||
{
|
||
"tool": "unidesk-ssh",
|
||
"purpose": "ssh-passthrough",
|
||
"secretRef": {
|
||
"namespace": "agentrun-v01",
|
||
"name": "agentrun-v01-tool-unidesk-ssh",
|
||
"keys": ["UNIDESK_SSH_CLIENT_TOKEN"]
|
||
},
|
||
"projection": { "kind": "env", "envName": "UNIDESK_SSH_CLIENT_TOKEN" }
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
规则:
|
||
|
||
- `toolCredentials` 只能保存 SecretRef、tool、purpose、projection intent 和 redacted metadata,不保存 Secret value。
|
||
- manager 只校验引用形态和 tenant/policy 边界,不读取 Secret value 后存库。
|
||
- runner 渲染 Job 时只能按当前 run 的 `secretScope` 投影被授权的 tool credential;不能枚举 namespace 内所有 Secret。
|
||
- dry-run manifest、runner job record、event、trace、日志和 CLI 输出只能显示 tool、purpose、SecretRef 名称/key、projection kind 和 `valuesPrinted=false`。
|
||
- GitHub PR 能力属于 agent shell/tool 运行能力,不是 AgentRun Queue integration,也不要求新增 GitHub sink、OA sink、notification 或 Event Flow。
|
||
- `tool=unidesk-ssh` 只允许投影 Secret key `UNIDESK_SSH_CLIENT_TOKEN` 到同名 env。该 token 是 UniDesk frontend `/ws/ssh` 的 scoped client token,route allowlist 由 UniDesk frontend 配置约束;它不得携带 provider token、主 server SSH key 或完整 frontend 登录态。
|
||
- 发现 agent shell 缺少 `gh`、`curl`、UniDesk SSH passthrough token 或其他工具凭证时,只能记录为装配能力缺口;不得用 `transientEnv` 或 issue 评论里的明文 token 绕过。
|
||
|
||
## HWLAB v0.2 承接口径
|
||
|
||
HWLAB v0.2 原有 Code Agent 已经验证了 profile、session、workspace 和 Secret 的基本边界。RuntimeAssembly 需要把这些边界固化为 AgentRun 自身四要素,避免 runner 继续依赖 HWLAB cloud-api Pod 内路径或本地进程状态。
|
||
|
||
| HWLAB v0.2 基线能力 | HWLAB 参考入口 | RuntimeAssembly 承接字段 | 承接规则 |
|
||
| --- | --- | --- | --- |
|
||
| provider profile 可切换 | `internal/cloud/code-agent-contract.ts` | `ProfileRef.profile`、`ProfileRef.secretRef` | `deepseek`、`minimax-m3` 与 `codex` 只选择 profile/config/SecretRef,不复制 backend 协议;缺失 Secret 必须失败,不 fallback。 |
|
||
| Codex app-server thread 复用 | `internal/cloud/codex-stdio-session.ts`、`internal/cloud/code-agent-session-registry.ts` | `SessionRef.sessionId`、`conversationId`、`threadId` | AgentRun 保存 backend thread/session 摘要;不保存 API KEY、`auth.json`、`config.toml` 或完整 `CODEX_HOME`。 |
|
||
| 固定 `/workspace/hwlab` 代码上下文 | `internal/cloud/code-agent-contract.ts` | `ResourceBundleRef.repoUrl`、`commitId` | 用 Git-only full commit 取代 HWLAB Pod 内固定路径;runner checkout 到隔离 workspace。 |
|
||
| writable `CODEX_HOME` 与 Secret 投影分离 | `docs/reference/code-agent-chat-readiness.md` | `ProfileRef` + runner runtime home | Secret 只读投影,复制到当前 run/profile writable runtime home;profile 间不共享。 |
|
||
| runner/image 可追溯 | HWLAB live build/source metadata | `BackendImageRef.image` | runner/backend image 必须可追溯 digest/source commit,不能由调用方任意传未受控镜像。 |
|
||
|
||
## 四要素边界
|
||
|
||
### BackendImageRef
|
||
|
||
- `image` 必须是 digest-pinned image。
|
||
- image 来源必须是 CI/CD artifact catalog、GitOps manifest 或 manager allowlist;客户端不能在 run payload 中传任意镜像。
|
||
- v0.1 可以继续使用现有 agentrun runner 镜像,不要求立即拆独立 backend image。
|
||
- 验收时只需要能追溯实际 Deployment/Job image digest 和 source commit。
|
||
|
||
### ProfileRef
|
||
|
||
- `profile` 在 v0.1 只允许 `codex`、`deepseek` 或 `minimax-m3`。
|
||
- `secretRef` 只保存 Secret 名称和 key,不保存值。
|
||
- 当前 profile 只能读取当前 profile 的 SecretRef;缺失必须 `secret-unavailable`,不能 fallback 到另一个 profile。
|
||
- profile Secret 只读投影,backend 需要可写目录时复制到 per-run/profile runtime home。
|
||
|
||
### SessionRef
|
||
|
||
- P0 允许 `sessionRef=null`,表示不持久化 backend session 文件。
|
||
- 面向 HWLAB v0.2 原有长会话能力,SessionRef 是承接 Code Agent thread 复用的核心字段:需要支持 `conversationId/sessionId/threadId` 到 backend session identity 的稳定映射。
|
||
- thread 复用只认标准 `threadId`:单个 command 显式提供 `payload.threadId` 时优先使用,否则使用 `SessionRef.threadId`。协议字段、events、result 和 session record 都以该字段为唯一 thread identity;缺失标准 `threadId` 就按新 thread 启动并在 result/sessionRef 中回写标准字段。
|
||
- 一旦启用 session,必须只保存 backend session/cache,不保存 API KEY、`auth.json`、`config.toml` 或完整 `CODEX_HOME`。
|
||
- session 文件目录必须和 profile credential、Git workspace 分开。
|
||
- runner 启动时,有 SessionRef 则执行 `thread/resume`,没有 SessionRef 则执行 `thread/start`;profile 切换不得复用另一 profile 的 session。
|
||
- v0.1 先定义边界;持久 session store、TTL、GC 和 resume 验收按 [spec-v01-hwlab-manual-dispatch.md](spec-v01-hwlab-manual-dispatch.md) 分阶段推进。
|
||
|
||
### ResourceBundleRef
|
||
|
||
- P0 固定 Git-only,由 `repoUrl + full commitId` 决定内容身份。
|
||
- `commitId` 必须是不可变 full commit sha,不能是 branch、tag 或 `HEAD`。
|
||
- 可选扩展只允许 `subdir`、`sparsePaths`、`submodules=false`、`lfs=false`、`credentialRef`、`toolAliases`、`promptRefs`、`skillRefs`;默认不启用。
|
||
- `credentialRef` 只用于拉取私有 Git repo,不等同于 backend API KEY。
|
||
- 不支持上传文件、对象存储 artifact、任意 ConfigMap 文件袋或 inline env;后续需要时另写版本规格。
|
||
- 面向 HWLAB 手动调度 canary,runner materialization 必须把 Git-only bundle checkout 到允许 workspace 前缀,并在 event/result 中记录 repo、full commit、checkout path 和 tree 摘要;不得隐式使用 manager Pod、host path 或镜像内旧代码。
|
||
|
||
#### toolAliases
|
||
|
||
`toolAliases` 用于把 bundle 内的受控脚本暴露为 runner PATH 中的短命令。每个 alias 只能指向当前 checkout 内的相对路径,不能覆盖 runner 镜像里已有非 AgentRun wrapper 命令;materialization event 只输出 alias 名称、kind、目标 path hash 和 wrapper 摘要,不输出脚本全文。缺少 required 工具入口时必须返回 `resource-tool-unavailable` 或等价 blocker,不能要求业务 prompt 改走长路径 wrapper。
|
||
|
||
#### promptRefs
|
||
|
||
`promptRefs` 用于按 `repoUrl + full commitId + path` 装配初始 prompt。它承载业务域稳定 runtime/developer instruction,例如某个项目的标准入口、禁止路径和工具使用纪律;它不承载用户本轮 message,也不承载历史会话。
|
||
|
||
最小形态:
|
||
|
||
```json
|
||
{
|
||
"name": "hwlab-v02-runtime",
|
||
"path": "internal/agent/prompts/hwlab-v02-runtime.md",
|
||
"inject": "thread-start",
|
||
"required": true
|
||
}
|
||
```
|
||
|
||
规则:
|
||
|
||
- `path` 只能是 bundle 内相对路径,不能是绝对路径,不能包含 `..`。
|
||
- `inject` 的 v0.1 默认和唯一正式语义是 `thread-start`:只有没有既有 `threadId`、runner 执行 `thread/start` 的第一条 turn 会注入;已有 `SessionRef.threadId` 或 command `payload.threadId` 并执行 `thread/resume` 时不得再次注入。
|
||
- 多个 `promptRefs` 按数组顺序拼装;单个 prompt 和总 assembled prompt 必须有大小上限,超限返回 `prompt-unavailable` 或 `prompt-too-large`,不能静默截断成新语义。
|
||
- prompt 文件内容进入 Codex turn input,但 event/result 只输出 `name`、`path`、`sha256`、`bytes`、`inject`、`required` 和 `injected=true/false`,不得默认输出全文。
|
||
- `promptRefs` 不得读取 Secret、env、token、profile config 或 session 文件;需要 credential 的内容必须通过 `ProfileRef`、`toolCredentials` 或 `transientEnv` 的正式路径装配。
|
||
- `promptRefs` 缺失且 `required=true` 时,run/command 必须 blocked;不得 fallback 到用户 prompt、旧硬编码 prompt、模型默认 system prompt 或历史上下文拼接。
|
||
|
||
#### skillRefs
|
||
|
||
`skillRefs` 用于按同一 `ResourceBundleRef` checkout 装配 skill manifest。它只描述非敏感 skill 文件、聚合方式和 required 语义;运行时 credential 仍走 `toolCredentials` 或其他 SecretRef 路径。
|
||
|
||
最小形态:
|
||
|
||
```json
|
||
{
|
||
"name": "device-pod-cli",
|
||
"path": "skills/device-pod-cli/SKILL.md",
|
||
"required": true,
|
||
"aggregateAs": "device-pod-cli"
|
||
}
|
||
```
|
||
|
||
规则:
|
||
|
||
- `path` 必须指向当前 checkout 内的 `SKILL.md`,或后续规格显式允许的 skill root;不能引用 runner 镜像、host path、ConfigMap 或外部 URL。
|
||
- runner 必须把 skill 聚合到当前 workspace 的标准 skill registry,例如 `$WORKSPACE/.agents/skills/<aggregateAs>/SKILL.md`,并设置 backend 可见的 skill dirs 环境或等价配置。
|
||
- skill discovery fact 只输出 skill name、summary、manifest path、hash、version/commit metadata 和 count;不得输出大段 manifest 正文,除非 agent 在 turn 中按需读取文件。
|
||
- `required=true` 的 skill 缺失、不可读或 manifest 无法解析时,run/command 必须 blocked 为 `skill-unavailable`,不能让模型凭默认 Codex skill registry 猜测,也不能把用户长 prompt 当作替代 skill。
|
||
- `skillRefs` 与 `promptRefs` 必须来自同一 `repoUrl + commitId`,以避免业务 prompt、skill manifest 和工具 alias 版本漂移。
|
||
|
||
#### 初始 prompt 与 session 边界
|
||
|
||
初始 prompt 装配只发生在新 thread 的首轮 turn。后续 turn 的历史上下文必须由 Codex stdio 原生 `thread/resume` 恢复;AgentRun 不得为了补 prompt、补 skill facts 或修复 stale thread 而拼接旧用户消息、旧 assistant 回复、旧 skill 列表或旧业务事实。`thread/resume` 失败时按 [spec-v01-backend-codex.md](spec-v01-backend-codex.md) 直接失败,不启动替代 `thread/start`。
|
||
|
||
## 最简装配顺序
|
||
|
||
1. Manager 根据 run 解析四要素引用。
|
||
2. Manager 根据 `executionPolicy.secretScope` 解析 provider/tool SecretRef 和 resource credential 引用;只保存引用,不读取值。
|
||
3. Manager 或 runner Job render 只使用解析后的 image、SecretRef、sessionRef、Git commit 和 projection intent。
|
||
4. Runner materialize profile Secret 到 writable runtime home。
|
||
5. Runner materialize tool credential 到该 run 允许的 env/file projection;未实现的 tool scope 必须显式 failed/blocked,不能静默跳过后让 agent 自己猜凭据。
|
||
6. Runner materialize Git-only resource bundle 到 workspace;P0 未实现时必须显式记录为 deferred 或 null,不能猜测 host path。
|
||
7. Runner 在 materialized bundle 内解析 `toolAliases`、`promptRefs` 和 `skillRefs`:创建工具 wrapper、聚合 skill registry、读取并校验 thread-start prompt,写入有界 assembly event。
|
||
8. Runner 启动 backend,并在 event 中记录 image digest、profile、SecretRef 名称/key、tool credential scope、sessionRef、repoUrl/commitId、promptRefs 和 skillRefs 的脱敏摘要。
|
||
|
||
任何一个要素缺失或不合法,都必须按该要素失败;不得静默 fallback。
|
||
|
||
## v0.1 验收标准
|
||
|
||
### A1 BackendImageRef 验收
|
||
|
||
- 实际 manager Deployment 和 runner Job 使用 digest-pinned image。
|
||
- event、CLI 或诊断输出能看到 image digest 或可追溯到 GitOps/catalog。
|
||
- run payload 不能传任意 image 字符串。
|
||
|
||
### A2 ProfileRef 验收
|
||
|
||
- `codex` run 只挂载 `agentrun-v01-provider-codex`。
|
||
- `deepseek` run 只挂载 `agentrun-v01-provider-deepseek`。
|
||
- `minimax-m3` run 只挂载 `agentrun-v01-provider-minimax-m3`。
|
||
- `codex -> deepseek -> minimax-m3 -> codex` 切换后,`CODEX_HOME`、SecretRef、backend_status 不互相污染。
|
||
- 删除或缺失 `deepseek`/`minimax-m3` SecretRef 时必须 `secret-unavailable`,不能 fallback 到 `codex`。
|
||
- 所有输出不得包含 Secret value、`auth.json` 或 `config.toml` 明文。
|
||
|
||
### A2b Tool credential 验收
|
||
|
||
- GitHub PR、issue、UniDesk SSH passthrough 或其他 shell/tool 授权只能通过 `executionPolicy.secretScope.toolCredentials[]` 的 SecretRef 装配进入 runner。
|
||
- CLI、Queue task、runner job response、dry-run manifest、event 和日志不得输出 token、SSH private key 或 credential 文件正文。
|
||
- 缺少 tool credential 时,run/command 必须返回可判定的 `secret-unavailable`、`tenant-policy-denied` 或明确 blocker,不能伪装成 agent 业务失败。
|
||
- `transientEnv` 不得用于 GitHub token、UniDesk SSH client token、长期 SSH key、provider API key 或其他可复用 credential。
|
||
|
||
### A3 SessionRef 验收
|
||
|
||
- P0 若未启用 session,run/manifest 必须显式表现为 `sessionRef=null` 或 equivalent deferred 状态。
|
||
- runner Job 不得把完整 `CODEX_HOME`、Secret projection 或 host path 当成 session store。
|
||
- 后续启用 session 前,必须补充真实验收:session 目录不包含 API KEY 或 profile credential。
|
||
|
||
### A4 ResourceBundleRef 验收
|
||
|
||
- P0 ResourceBundle 只能是 Git-only:`repoUrl + full commitId`。
|
||
- `commitId` 不是 branch/tag/HEAD。
|
||
- checkout 只能进入允许 workspace 前缀,不能覆盖 `/app`、Secret projection、profile runtime home 或 session 目录。
|
||
- run payload 不携带文件正文、env dump、Secret value 或大型 artifact。
|
||
- 若提供 `promptRefs`,必须能看到每个 prompt 的 `name/path/sha256/bytes/inject`,新 thread 首轮 `initialPromptInjected=true`,resume turn `initialPromptInjected=false`。
|
||
- 若提供 `skillRefs`,必须能看到 skill registry 聚合摘要、required skill 名称和 manifest hash;required skill 缺失必须 blocked,不能显示模型默认 skill 列表当作业务 skill。
|
||
|
||
### A5 综合验收
|
||
|
||
一次真实 runner Job 或 dry-run manifest 必须能同时回答:
|
||
|
||
1. 用哪一个 image digest。
|
||
2. 用哪一个 profile 和 SecretRef。
|
||
3. 是否使用 session;若不用,必须明确为 `null`/deferred。
|
||
4. 使用哪一个 Git repo 和 full commit;若 P0 尚未 materialize,必须明确为 deferred,不能隐式使用 host path。
|
||
5. 是否装配 tool aliases、初始 prompt 和 skill refs;若提供,必须能回答 name/path/hash/inject/required 和是否注入,不能只依赖模型默认 prompt 或默认 skill registry。
|
||
6. 是否装配 tool credential;若需要 GitHub PR 能力,必须能回答 tool、purpose、SecretRef 和 projection kind,不能只在运行时 shell 中偶然存在 token。
|
||
|
||
## 实现状态
|
||
|
||
| 要素 | v0.1 状态 | 说明 |
|
||
| --- | --- | --- |
|
||
| `BackendImageRef` | 部分实现 | CI/CD 已使用 digest-pinned runtime image;当前 runner/backend 仍复用 agentrun 镜像。 |
|
||
| `ProfileRef` | 已实现/待 MiniMax-M3 主闭环 | `codex` 与 `deepseek` 已通过 SecretRef、writable runtime home 和真实 stdio turn 验证;`minimax-m3` 已进入 profile/SecretRef 装配,需要完成真实 CLI 手动验收。 |
|
||
| `SessionRef` | 已实现最小持久化 | manager 持久化 `sessionId/conversationId/threadId`,run 创建会解析既有 session,runner 按 threadId resume;session 不保存 credential 文件,TTL/GC 后续细化。 |
|
||
| `ResourceBundleRef` | 已实现 Git-only materialization/待 promptRefs 与 skillRefs 实现 | `repoUrl + full commitId` 已进入 run schema 和 runner checkout,workspace 受 `AGENTRUN_WORKSPACE_ROOT` 限制,event/result 记录 commit/tree/workspace 摘要;`toolAliases` 已实现,`promptRefs` thread-start 注入和 `skillRefs` registry 聚合待实现。 |
|
||
| `toolCredentials` | 已实现最小 env projection | GitHub PR 和 UniDesk SSH passthrough 等 agent shell/tool 授权通过装配 SPEC 的 SecretRef 进入 runner;v0.1 支持 `tool=github` 与 `tool=unidesk-ssh`、`projection.kind=env`,runner Job 使用 `valueFrom.secretKeyRef` 注入,不用 `transientEnv` 绕过。 |
|