diff --git a/AGENTS.md b/AGENTS.md index a54632a..6f4eda3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -9,7 +9,7 @@ | `cc-haha/` | **Heicode** 客户端:CLI(Ink)+ 本地 HTTP/WS 服务 + **Desktop**(Tauri + React) | Bun + TypeScript;产品入口 `bin/heicode` | | `heicode/` | **Heicode Manager**:网关 + 管理控制台 | Go(Gin/GORM)+ `web/default` 前端(Bun/Rsbuild/React) | | `website/` | 产品介绍站点 | Next.js;根 `docker-compose.yml` 提供 `heicode-www` :8888 | -| `docs/` | 愿景、**里程碑**(`docs/milestones/`)、**Agnet 集成**(`docs/integration/`) | Markdown | +| `docs/` | 愿景、**里程碑**(`docs/milestones/`)、**Agent 集成**(`docs/integration/`) | Markdown | 根 `package.json` 仅少量 workspace 级依赖(如适配器用到的包);**主要开发依赖在 `cc-haha/package.json`**。 @@ -51,7 +51,7 @@ HEICODE_TAIJIAICLOUD_BASE_URL=http://localhost:3000 bun run src/server/index.ts 1. **改哪一层跟哪篇文档**:Go 行为以 `heicode/AGENTS.md` 为准;客户端 TS/React 以 `cc-haha/AGENTS.md` 为准。 2. **小步提交**:沿用历史风格(如 `feat:` / `fix:` / `docs:`);PR 写清影响面与验证步骤。 -3. **集成与「SK」边界**:平台契约见 [`docs/integration/agnet-platform-api-design.md`](./docs/integration/agnet-platform-api-design.md),里程碑见 `docs/milestones/`。 +3. **集成与「SK」边界**:平台契约见 [`docs/integration/agent-platform-api-design.md`](./docs/integration/agent-platform-api-design.md),里程碑见 `docs/milestones/`。 4. **不要臆测计费**:计费与订阅在平台侧,不在 Heicode 客户端内实现。 ## Docker / 站点 diff --git a/CLAUDE.md b/CLAUDE.md index 42b359b..2b25f1b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -9,7 +9,7 @@ | `cc-haha/` | **Heicode** 客户端:CLI(Ink)+ 本地 HTTP/WS 服务 + **Desktop**(Tauri + React) | Bun + TypeScript;产品入口 `bin/heicode` | | `heicode/` | **Heicode Manager**:网关 + 管理控制台 | Go(Gin/GORM)+ `web/default` 前端(Bun/Rsbuild/React) | | `website/` | 产品介绍站点 | Next.js;根 `docker-compose.yml` 提供 `heicode-www` :8888 | -| `docs/` | 愿景、**里程碑**(`docs/milestones/`)、**Agnet 集成**(`docs/integration/`) | Markdown | +| `docs/` | 愿景、**里程碑**(`docs/milestones/`)、**Agent 集成**(`docs/integration/`) | Markdown | 根 `package.json` 仅少量 workspace 级依赖(如适配器用到的包);**主要开发依赖在 `cc-haha/package.json`**。 @@ -51,7 +51,7 @@ HEICODE_TAIJIAICLOUD_BASE_URL=http://localhost:3000 bun run src/server/index.ts 1. **改哪一层跟哪篇文档**:Go 行为以 `heicode/CLAUDE.md` 为准;客户端 TS/React 以 `cc-haha/AGENTS.md` 为准。 2. **小步提交**:沿用历史风格(如 `feat:` / `fix:` / `docs:`);PR 写清影响面与验证步骤。 -3. **集成与「SK」边界**:平台契约见 [`docs/integration/agnet-platform-api-design.md`](./docs/integration/agnet-platform-api-design.md),里程碑见 `docs/milestones/`。 +3. **集成与「SK」边界**:平台契约见 [`docs/integration/agent-platform-api-design.md`](./docs/integration/agent-platform-api-design.md),里程碑见 `docs/milestones/`。 4. **不要臆测计费**:计费与订阅在平台侧,不在 Heicode 客户端内实现。 ## Docker / 站点 diff --git a/Heicode-Manager-生产配置与账号交接清单.md b/Heicode-Manager-生产配置与账号交接清单.md new file mode 100644 index 0000000..214605f --- /dev/null +++ b/Heicode-Manager-生产配置与账号交接清单.md @@ -0,0 +1,284 @@ +# Heicode Manager 生产配置与账号交接清单 + +更新时间:2026-06-01 +适用范围:Heicode Manager 生产环境、普通 sub Runtime、蜂群 Runtime、NewAPI、Azure 资源和后续 AI/运维交接。 + +## 0. 安全说明 + +本文只记录配置项、用途、位置和获取方式,不记录明文密码、Token、Key、连接串或私钥。 + +| 规则 | 要求 | +|------|------| +| 明文密钥 | 不写入 Markdown、git、聊天记录、日志、截图 | +| 生产 `.env` | 只保存在 VM 或部署平台 Secret 中,不提交仓库 | +| Azure Key Vault | 只保存长期密钥或 callback 签名密钥,代码和数据库只保存 `secret_ref` | +| 转交给其他 AI | 只给本文档;需要明文值时由负责人通过安全渠道单独提供 | +| 已泄漏密钥 | 一律按泄漏处理,立即轮换 | + +## 0.1 配置可读取性结论 + +不是所有配置都能、也不应该在交接 Markdown 中读取和保存明文值。当前结论分三类: + +| 分类 | 含义 | 处理方式 | +|------|------|----------| +| 可直接读取 | 仓库文档、compose、公开地址、非敏感环境变量能直接确认 | 在本文写出真实值 | +| 可确认已配置但不读取明文 | 生产容器 env 中存在,但值是密码、token、连接串或密钥 | 在本文写出配置名、用途、保存位置和 `` | +| 当前无法确认明文或后台状态 | 需要 Azure Portal、NewAPI 后台、Gitee 凭据、VM root 权限或负责人授权 | 在本文明文写出“无法读取项名称、原因、获取入口” | + +当前无法在本文保存明文的配置如下: + +| 配置 / 凭据 | 是否能确认存在 | 为什么不能写明文 | 需要去哪里取 | +|-------------|----------------|------------------|--------------| +| VM SSH 密码/私钥 | 已知需要 | 登录凭据,写入文档会泄漏生产入口 | 负责人安全渠道、SSH keychain 或运维密码库 | +| Gitee 用户密码/token | 已知需要 | Git 凭据,写入文档会泄漏代码仓库权限 | 本机 credential helper、Gitee 个人 token、负责人安全渠道 | +| `SQL_DSN` | 已确认配置项存在 | 内含数据库用户名、密码、host、参数 | VM `.env`、容器 env、Azure PostgreSQL 连接串 | +| `REDIS_CONN_STRING` | 已确认配置项存在 | 内含 Redis 密码和 TLS 连接串 | VM `.env`、容器 env、Azure Redis Access keys | +| `SESSION_SECRET` | 已确认配置项存在 | 会话签名密钥,泄漏会影响登录安全 | VM `.env`、容器 env、部署平台 Secret | +| `CRYPTO_SECRET` | 已确认配置项存在 | Manager 加密密钥,泄漏会影响加密数据安全 | VM `.env`、容器 env、部署平台 Secret | +| `MCP_SERVER_INTERNAL_TOKEN` | 已确认配置项存在 | 内部服务调用 token | VM `.env`、容器 env | +| `AGENT_RUNTIME_SERVICE_TOKEN` | 已确认配置项存在 | Manager 调 Runtime 的 Bearer token | VM `.env`、容器 env、Agent Manager 负责人 | +| `AGENT_CALLBACK_TOKEN` | 已确认配置项存在 | Runtime 回调 Manager 的旧 token 兼容凭据 | VM `.env`、容器 env、Agent Manager 负责人 | +| `AGENT_CALLBACK_SIGNING_SECRET` | 已确认配置项存在 | Runtime 回调 Manager 的 HMAC 签名密钥 | VM `.env`、Key Vault `agent-callback-signing-key` | +| `AGENT_RUNTIME_CALLBACK_SIGNING_SECRET_REF` | 已确认配置项存在 | secret_ref 本身不应暴露完整细节 | VM `.env`、Key Vault 配置 | +| NewAPI 管理员密码 | 当前未在仓库读取 | 后台管理员凭据 | NewAPI 后台、NewAPI DB、负责人安全渠道 | +| NewAPI 渠道 Key | 当前未在仓库读取 | 上游模型供应商 key | NewAPI 渠道管理页面或 DB | +| Azure Portal 账号/MFA | 当前未在仓库读取 | 云账号权限极高 | 负责人 Azure 账号体系 | +| Azure Key Vault secret value | 当前未直接读取 | Key Vault 密钥值只能由授权身份读取 | Azure Portal、Azure CLI、Managed Identity | + +后续 AI 如果必须使用明文值,应只在当前 shell/session 的环境变量中临时使用,不能写入 Markdown、git diff、日志或截图。 + +## 1. 当前生产入口与代码仓库 + +| 项目 | 当前值 / 位置 | 说明 | +|------|---------------|------| +| Manager 生产地址 | `https://code.xinghanlab.com` | 用户和桌面客户端访问入口 | +| Manager 本地仓库 | `/Users/gongzhiyong/go/heicode-mananger` | 当前开发工作区 | +| Manager Git 远程 | `http://gitee.ath.cx:3000/taijibaga/heicode-mananger.git` | 继续开发、提交、上线使用 | +| 主分支 | `main` | 当前工作分支 | +| 旧 bug 仓库 | `HeiCode-issues.git` | 已废弃,后续不用再传 | +| 当前未跟踪文件 | `bak.txt` | 本地文件,不属于本交接文档内容 | + +## 2. Azure / VM 生产资源 + +| 类型 | 当前配置 | 用途 | 备注 | +|------|----------|------|------| +| Azure Resource Group | `HEICODE` | Heicode 相关 Azure 资源组 | 以 Azure Portal 实际为准 | +| 区域 | `southeastasia` | 主要生产资源区域 | 以 Azure Portal 实际为准 | +| Manager VM | `heicode` | 当前承载 Manager Docker 容器 | 生产应用仍在 VM 上 | +| VM SSH Host | `20.24.50.121` | 登录生产 VM | 凭据见“账号与凭据清单” | +| VM SSH User | `heicode` | 生产 VM 运维用户 | 密码/私钥不写入本文 | +| Manager 容器 | `heicode` | 生产 Manager 应用容器 | 端口 `3000:3000` | +| Manager 镜像 | `heicode-manager:local` | VM 本地构建镜像 | 以 `docker ps` 为准 | +| 数据根目录 | `/var/lib/heicode/heicode` | bind mount 根目录 | 由 `HEICODE_DATA_ROOT` 控制 | +| 应用持久目录 | `/data` | 上传文件、桌面安装包、运行持久文件 | 容器内路径,映射到 VM | +| 应用日志目录 | `/app/logs` | Manager 日志 | 容器内路径,映射到 VM | + +当前 VM 上观察到的容器: + +| 容器 | 镜像 | 端口 | 说明 | +|------|------|------|------| +| `heicode` | `heicode-manager:local` | `3000:3000` | Manager 生产应用 | +| `new-api` | `calciumion/new-api:latest` | `3001:3000` | NewAPI 管理和模型网关 | +| `heicode-openbao` | `openbao/openbao:latest` | `127.0.0.1:8200:8200` | 历史/兼容密钥服务,不作为当前正式方案 | +| `postgres` | `postgres:15` | VM 内部 | VM 上存在容器,但 Manager 正式配置应以 Azure PostgreSQL 为准 | +| `redis` | `redis:latest` | VM 内部 | VM 上存在容器,但 Manager 正式配置应以 Azure Redis 为准 | + +重要:仓库约定和部署文档要求生产业务库使用 Azure 托管 PostgreSQL / Redis,不要把 VM 上的 `postgres`、`redis` 容器误认为正式托管实例。 + +## 3. 当前 VM 关键路径 + +| 路径 | 用途 | 备注 | +|------|------|------| +| `/home/heicode/heicode/heicode/.env` | Manager 生产环境变量文件 | **当前真实部署目录**(compose `working_dir` 实锤);仓库远程为 `taijibaga/heicode-mananger` | +| `/home/heicode/heicode/heicode/docker-compose.azure-vm.yml` | Manager Azure VM compose | 当前 active compose(`docker inspect` 的 `config_files` 指向此文件) | +| `/home/heicode/apps/new-api/docker-compose.yml` | NewAPI compose | NewAPI 容器配置入口 | +| `/home/heicode/apps/_ABANDONED_heicode-git_xiaohei_20260601/` | **已废弃的旧 clone**(原 `xiaohei/heicode`,2026-06-01 改名挪走) | 不是部署源;曾误导排查。可后续硬删 | +| `/home/heicode/deploy/` | 历史部署目录 | 可能包含旧版本 `.env`,不要直接当作当前配置 | +| `/home/heicode/releases/` | 历史 release 目录 | 只用于回溯,不作为当前事实 | +| `/var/lib/heicode/heicode/data` | Manager 持久数据 | 对应容器 `/data` | +| `/var/lib/heicode/heicode/logs` | Manager 日志 | 对应容器 `/app/logs` | + +## 4. Manager 基础环境变量 + +| 环境变量 | 当前状态 | 用途 | 值/来源 | +|----------|----------|------|---------| +| `TZ` | 已配置 | 时区 | `Asia/Shanghai` | +| `NODE_NAME` | 已配置 | 节点名 | `heicode-node-1` | +| `ERROR_LOG_ENABLED` | 已配置 | 错误日志开关 | `true` | +| `BATCH_UPDATE_ENABLED` | 已配置 | 批量更新开关 | `true` | +| `HEICODE_DATA_ROOT` | 已配置 | VM bind mount 根目录 | `/var/lib/heicode/heicode` | +| `SQL_DSN` | 已配置,敏感 | PostgreSQL 连接串 | VM `.env` / 容器 env,值 `` | +| `REDIS_CONN_STRING` | 已配置,敏感 | Redis TLS 连接串 | VM `.env` / 容器 env,值 `` | +| `SESSION_SECRET` | 已配置,敏感 | 登录会话签名 | VM `.env` / 容器 env,值 `` | +| `CRYPTO_SECRET` | 已配置,敏感 | Manager 加密密钥 | VM `.env` / 容器 env,值 `` | +| `MCP_SERVER_INTERNAL_TOKEN` | 已配置,敏感 | 内部服务调用 token | VM `.env` / 容器 env,值 `` | +| `HEICODE_AUTH_BASE_URL` | 已配置 | Heicode/Agent 认证网关 | 默认参考 `https://apimtaiji.azure-api.net/api/mcp`,以 VM env 为准 | +| `HEICODE_ROOT_EMAILS` | 已配置 | JIT 自动 root 白名单 | 当前容器 env 可见 `xiaohei@qq.com` | +| `HEICODE_ADMIN_EMAILS` | 空 | JIT 自动 admin 白名单 | 当前未配置 | + +生产数据库和 Redis: + +| 服务 | 地址 | 说明 | +|------|------|------| +| Azure PostgreSQL | `heicode.postgres.database.azure.com` / DB `heicode` | Manager 正式数据库 | +| Azure Redis | `heicode.redis.cache.windows.net:6380` | TLS Redis,连接串走 `REDIS_CONN_STRING` | + +## 5. Azure Key Vault / 密钥保管库配置 + +| 项目 | 当前状态 | 用途 | 备注 | +|------|----------|------|------| +| Key Vault 名称 | `heicode-kv` | 长期密钥和 callback 签名密钥托管 | 以 Azure Portal 为准 | +| Key Vault URL | `https://heicode-kv.vault.azure.net` | Manager 访问密钥保管库 | 对应 `AZURE_KEY_VAULT_URL` | +| `AZURE_KEY_VAULT_URL` | 已配置 | Key Vault 地址 | VM env,值应为上面 URL | +| `AZURE_CLIENT_ID` | 当前为空 | user-assigned managed identity client id | 若使用 system-assigned 可为空;若 health 报 Identity not found 需配置身份 | +| `agent-callback-signing-key` | 必须存在 | Runtime callback HMAC 签名密钥 | 建议通过 Key Vault 存储 | + +当前已知问题: + +| 问题 | 含义 | 处理方式 | +|------|------|----------| +| `Identity not found` | VM 当前没有可用 Managed Identity,或未绑定正确 `AZURE_CLIENT_ID` | 在 VM/容器服务开启 Managed Identity,并给 `heicode-kv` 授权 | + +Key Vault 最低权限: + +| 用途 | 权限 / RBAC | +|------|-------------| +| 读取 callback 签名密钥 | `secrets/get` | +| 健康检查列举 | `secrets/list` | +| Manager 写入资源密钥 PutSecret | `secrets/set` | +| RBAC 参考角色 | `Key Vault Secrets User` 或 `Key Vault Secrets Officer` | + +## 6. 普通 sub Runtime / Agent Manager 配置 + +普通 sub 和蜂群是两套模式,不要混用。普通 sub 当前走 Agent Manager Runtime IP。 + +| 环境变量 | 当前状态 | 当前值 / 位置 | 说明 | +|----------|----------|---------------|------| +| `AGENT_RUNTIME_ENABLED` | 已启用 | `true` | 开启普通 sub Runtime | +| `AGENT_RUNTIME_BASE_URL` | 已配置 | `http://20.212.121.126` | 当前联调和生产使用 IP,域名未切换 | +| `AGENT_RUNTIME_HEALTH_PATH` | 已配置 | `/api/agent/health` | Runtime 健康检查 | +| `AGENT_RUNTIME_CREATE_PATH` | 已配置 | `/api/swarms` | 普通 sub 创建入口 | +| `AGENT_RUNTIME_STOP_PATH` | 已配置 | `/api/swarms/{swarm_id}/stop` | 停止 Runtime run | +| `AGENT_RUNTIME_APPROVAL_DECISION_PATH` | 已配置 | `/api/swarms/{swarm_id}/approvals/{approval_id}` | 审批结果回传 | +| `AGENT_RUNTIME_TIMEOUT_SECONDS` | 已配置 | `15` | Manager 调 Runtime 超时 | +| `AGENT_RUNTIME_SERVICE_TOKEN` | 已配置,敏感 | VM env,值 `` | Manager 调 Runtime 的 Bearer token | +| `AGENT_RUNTIME_CALLBACK_SIGNING_SECRET_REF` | 已配置,敏感引用 | VM env,值 `` | callback HMAC secret_ref,正式应为 `azkv://...` | +| `AGENT_CALLBACK_TOKEN` | 已配置,敏感 | VM env,值 `` | Runtime 回调 Manager 的旧 token 兼容 | +| `AGENT_CALLBACK_SIGNING_SECRET` | 已配置,敏感 | VM env,值 `` | Runtime 回调 Manager 的 HMAC fallback | + +普通 sub 当前标准链路: + +```text +桌面客户端 +-> Heicode Manager /api/agent/user/* +-> Agent Manager Runtime http://20.212.121.126/api/swarms +-> Runtime/Agent 执行 +-> Runtime callback 到 https://code.xinghanlab.com/api/agent/callbacks/swarm-events +-> Manager 落库 +-> 桌面客户端查询 timeline/logs/artifacts/content +``` + +## 7. 蜂群 Runtime 配置 + +蜂群模式和普通 sub 模式是两套部署、两套语义。Manager 当前环境里蜂群 Runtime 开关是关闭状态。 + +| 环境变量 | 当前状态 | 当前值 / 位置 | 说明 | +|----------|----------|---------------|------| +| `SWARM_RUNTIME_ENABLED` | 当前关闭 | `false` | 蜂群 Runtime 独立开关 | +| `SWARM_RUNTIME_BASE_URL` | 当前为空 | `` | 蜂群 Runtime 地址未在 Manager 生产 env 启用 | +| `SWARM_RUNTIME_CREATE_PATH` | 已有默认 | `/api/swarms` | 蜂群创建入口默认值 | +| `SWARM_RUNTIME_HEALTH_PATH` | 已有默认 | `/api/agent/health` | 蜂群健康检查默认值 | +| `SWARM_RUNTIME_STOP_PATH` | 已有默认 | `/api/swarms/{swarm_id}/stop` | 蜂群停止默认值 | +| `SWARM_RUNTIME_APPROVAL_DECISION_PATH` | 已有默认 | `/api/swarms/{swarm_id}/approvals/{approval_id}` | 蜂群审批默认值 | +| `SWARM_RUNTIME_SERVICE_TOKEN` | 当前为空 | `` | 蜂群 Runtime token 未配置 | + +已知蜂群 Orchestrator 地址: + +| 项目 | 地址 | 说明 | +|------|------|------| +| HeiCode-Swarm Orchestrator | `http://52.139.240.116:8000` | 蜂群项目独立 Runtime / Orchestrator | + +## 8. NewAPI / 模型网关配置 + +| 项目 | 当前配置 | 用途 | 备注 | +|------|----------|------|------| +| NewAPI 容器 | `new-api` | 模型网关、渠道、模型 token、用量 | VM 上运行 | +| NewAPI 镜像 | `calciumion/new-api:latest` | NewAPI 服务镜像 | 以 `docker ps` 为准 | +| NewAPI VM 端口 | `20.24.50.121:3001` -> container `3000` | 管理后台或内部访问 | 是否公网开放以 NSG/防火墙为准 | +| NewAPI compose | `/home/heicode/apps/new-api/docker-compose.yml` | NewAPI 部署配置 | 明文密钥不得写入本文 | +| 模型渠道配置 | NewAPI 管理后台 / DB | 渠道 key、base_url、模型映射 | 不在 Manager 文档保存明文 | +| Manager 模型接口 | `https://code.xinghanlab.com/v1/...` | 桌面客户端模型调用入口 | Manager / NewAPI 转发 | + +## 9. 账号与凭据清单 + +| 类别 | 标识 | 用途 | 明文值位置 / 获取方式 | +|------|------|------|-----------------------| +| VM SSH | host `20.24.50.121`, user `heicode` | 登录生产 VM、部署、看日志、清 Docker | 密码或私钥由负责人通过安全渠道提供,本文不保存 | +| Gitee Manager 仓库 | `http://gitee.ath.cx:3000/taijibaga/heicode-mananger.git` | 拉取、提交、推送 Manager 代码 | Gitee 用户名/密码或 token 由负责人提供;可能已在本机 credential helper | +| Manager Root 邮箱 | `xiaohei@qq.com` | 生产 root 白名单 | 只记录邮箱,不记录密码 | +| Manager 测试用户 | `zsbgnw@gmail.com` | 桌面客户端和 Manager 联调测试 | 密码不写入本文,需负责人单独提供 | +| Manager 管理后台用户 | 以生产数据库/后台为准 | 登录 `https://code.xinghanlab.com` 管理配置 | 密码不写入本文 | +| NewAPI 管理员 | 以 NewAPI 后台/DB 为准 | 配置渠道、模型、用户 token、日志 | 密码不写入本文 | +| NewAPI 渠道 Key | NewAPI 后台渠道管理 | 模型上游调用 | Key 不写入本文 | +| Azure Portal | 负责人 Azure 账号 | VM、Key Vault、PostgreSQL、Redis、网络配置 | 账号/密码/MFA 不写入本文 | +| Azure Key Vault Secret | `agent-callback-signing-key` | callback HMAC 签名 | 在 `heicode-kv` 获取/轮换 | +| Runtime Service Token | `AGENT_RUNTIME_SERVICE_TOKEN` | Manager 调 Agent Manager Runtime | VM env 或部署平台 Secret,值 `` | +| Callback Token | `AGENT_CALLBACK_TOKEN` | Runtime 回调 Manager 旧 token 兼容 | VM env 或部署平台 Secret,值 `` | +| Callback HMAC Secret | `AGENT_CALLBACK_SIGNING_SECRET` | Runtime 回调 Manager HMAC fallback | VM env / Key Vault,值 `` | +| PostgreSQL 密码 | `SQL_DSN` 内 | Manager 数据库连接 | VM `.env` / Azure PostgreSQL,值 `` | +| Redis 密码 | `REDIS_CONN_STRING` 内 | Manager Redis 连接 | VM `.env` / Azure Redis,值 `` | + +## 10. 常用排障命令 + +以下命令只列配置名或状态,避免直接打印明文密钥。 + +```bash +sudo docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}' + +sudo docker inspect heicode \ + --format '{{range .Config.Env}}{{println .}}{{end}}' \ + | awk -F= '{print $1}' \ + | sort + +curl -fsS http://127.0.0.1:3000/api/status +curl -fsS https://code.xinghanlab.com/api/status +curl -fsS http://20.212.121.126/api/agent/health +``` + +部署 Manager: + +```bash +cd /home/heicode/heicode/heicode # 真实部署目录(remote: taijibaga/heicode-mananger, 分支 main) +git pull +# 真实部署只有 docker-compose.azure-vm.yml + .env,无 docker-compose.override.yml +docker compose -f docker-compose.azure-vm.yml --env-file .env up -d --build +sudo docker image prune -f +``` + +## 11. 后续接手必须先确认的事项 + +| 序号 | 检查项 | 标准 | +|------|--------|------| +| 1 | 当前线上 commit | VM 工作树和 Gitee `main` 对齐 | +| 2 | Manager 容器健康 | `docker ps` 显示 `heicode` healthy,`/api/status` 返回成功 | +| 3 | 数据库连接 | `SQL_DSN` 指向 Azure PostgreSQL,不误用 VM 本地容器 | +| 4 | Redis 连接 | `REDIS_CONN_STRING` 指向 Azure Redis TLS,不误用 VM 本地容器 | +| 5 | Key Vault | `/api/secret-store/status` 不再报 `Identity not found` | +| 6 | 普通 sub | `AGENT_RUNTIME_BASE_URL=http://20.212.121.126`,创建、callback、artifact content 全链路可跑 | +| 7 | 蜂群 | `SWARM_RUNTIME_ENABLED` 当前为 `false`;如启用必须单独联调 | +| 8 | NewAPI | 模型渠道、token、日志正常;Runtime 失败时核对 `newapi_request_id` | +| 9 | 桌面客户端 | 加密 body、登录、模型列表、普通 sub artifact 展示正常 | +| 10 | 敏感值 | 任何 `.env`、token、密码不得进入 git diff | + +## 12. 交接边界 + +下一位 AI 或工程师可以直接基于本文继续做: + +- Manager 部署、版本更新、Docker 清理。 +- 普通 sub Runtime 联调、artifact content 下载链路验证。 +- 蜂群 Runtime 独立启用前的配置核对。 +- NewAPI 模型调用排障。 +- Azure Key Vault Managed Identity 修复。 +- 桌面客户端对接文档更新。 + +但不能直接从本文获得明文密码或 token。需要真实密钥时,必须由负责人通过安全渠道提供,或在 VM/Azure Portal/NewAPI 后台按权限读取。 diff --git a/Heicode-Manager-项目说明与踩坑交接.md b/Heicode-Manager-项目说明与踩坑交接.md new file mode 100644 index 0000000..8022ae0 --- /dev/null +++ b/Heicode-Manager-项目说明与踩坑交接.md @@ -0,0 +1,208 @@ +# Heicode Manager 项目说明与踩坑交接 + +更新时间:2026-06-01 +用途:给下一位 AI / 工程师快速理解 Heicode Manager 的项目边界、生产拓扑、普通 sub、蜂群、NewAPI 和已踩过的坑。 + +## 1. 一句话说明 + +Heicode Manager 是 Heicode 的服务端控制面和网页管理台,负责用户登录、模型网关配置、用户/设备/模型/渠道管理、普通 sub 敏捷任务控制、Runtime callback 落库、artifact/timeline 展示,以及和桌面客户端、Agent Manager Runtime、NewAPI、Azure Key Vault 之间的编排。 + +它不是桌面客户端本体,也不是 Agent Runtime 本体,也不是 NewAPI 本体。 + +## 2. 仓库结构 + +| 路径 | 作用 | 说明 | +|------|------|------| +| `heicode/` | Manager 后端和默认前端 | Go + Gin/GORM,`web/default` 是当前网页端 | +| `cc-haha/` | Heicode 桌面客户端和本地服务 | Tauri + React + Bun,客户端到 Manager 的请求 body 会走加密/签名流程 | +| `docs/` | 项目文档、部署、集成、进度清单 | 后续交接优先看这里 | +| `docs/deployment/` | 生产部署和迁移文档 | VM、Azure、配置交接 | +| `docs/integration/` | Runtime、桌面客户端、蜂群、普通 sub 对接文档 | 联调时优先看 | + +开发前先看: + +| 范围 | 文档 | +|------|------| +| 根仓库规则 | `AGENTS.md` | +| Manager 规则 | `heicode/AGENTS.md` | +| 生产配置 | `docs/deployment/Heicode-Manager-生产配置与账号交接清单.md` | +| 普通 sub 桌面客户端对接 | `docs/integration/heicode-desktop-sub-agile-api.md` | +| 普通 sub Agent Manager 对接 | `docs/integration/普通sub敏捷模式-AgentManager对接任务清单.md` | +| 蜂群 Agent Manager 对接 | `docs/integration/蜂群模式-AgentManager对接任务清单.md` | + +## 3. 生产拓扑 + +```text +用户/桌面客户端 + -> https://code.xinghanlab.com + -> Heicode Manager Docker container on Azure VM + -> Azure PostgreSQL / Azure Redis + -> NewAPI model gateway + -> 普通 sub Agent Manager Runtime: http://20.212.121.126 + -> 蜂群 Runtime / Orchestrator: http://52.139.240.116:8000 + -> Azure Key Vault: https://heicode-kv.vault.azure.net +``` + +当前 Manager 部署在 Azure VM 上,容器名 `heicode`,端口 `3000:3000`。VM 上也能看到 `new-api`、`postgres`、`redis`、`heicode-openbao` 容器,但正式业务数据库和缓存应以 Azure 托管 PostgreSQL / Redis 配置为准,不要误用 VM 本地容器判断生产数据。 + +## 4. 普通 sub 和蜂群必须分开 + +这是最容易踩坑的点。 + +| 模式 | 含义 | Manager 当前配置 | Runtime | +|------|------|------------------|---------| +| 普通 sub 敏捷模式 | 桌面客户端把一个开发任务拆给若干子 Agent,按需求、设计、开发、测试、部署等阶段推进 | `AGENT_RUNTIME_ENABLED=true` | `http://20.212.121.126` | +| 蜂群模式 | HeiCode-Swarm 的多 Agent swarm run / task graph 模式 | `SWARM_RUNTIME_ENABLED=false` | `http://52.139.240.116:8000`,需单独启用和联调 | + +不要因为两个接口都可能叫 `/api/swarms` 就把它们混成一个概念。普通 sub 是 Heicode 的任务组织方式;蜂群是独立 swarm runtime 形态。 + +## 5. 普通 sub 当前主流程 + +```text +桌面客户端创建/补充任务 + -> Manager 用户态接口生成 deployment draft + -> Manager 调 Agent Manager Runtime POST /api/swarms + -> Runtime 创建 run 并启动子 Agent + -> Runtime 执行中 callback Manager + -> Manager 保存 timeline / events / logs / artifacts / usage + -> 桌面客户端从 Manager 查询展示 +``` + +关键点: + +1. Runtime 执行过程会回调状态、日志、timeline、用量。 +2. 最终业务交付物不是直接塞在聊天文本里,而是通过 `artifact.created` 落库。 +3. 客户端应先查 artifact 列表,再通过 content 接口下载完整产物。 +4. 如果 `usage=0` 或只有失败摘要 artifact,不能算真实业务交付完成。 + +## 6. artifact 展示坑 + +桌面客户端曾出现“以下是作为 Frontend 角色...”这种内容,看起来像交付物,其实多半只是 Runtime 的摘要文本。 + +正常设计应该是: + +| 层级 | 应展示什么 | +|------|------------| +| 聊天时间线 | 阶段进度、Agent 状态、摘要说明 | +| 交付产物卡片 | `artifact_id`、标题、类型、摘要、大小、hash、下载入口 | +| 完整代码/文件 | 通过 `GET /api/agent/user/deployments/{deployment_id}/artifacts/{artifact_id}/content` 下载 | + +如果 artifact 里有 `azblob://...` 或 `runtime://...`,客户端不应该直接暴露云凭据或要求用户自己访问 Blob,而应通过 Manager / Runtime content 代理接口拿完整内容。 + +## 7. NewAPI / 模型调用边界 + +NewAPI 是模型网关和用量计费入口,不是 Heicode Manager 自己的模型执行器。 + +排查模型问题时要区分三条链路: + +| 链路 | 调用方 | 常见问题 | +|------|--------|----------| +| 桌面普通聊天 | 桌面客户端 -> Manager/NewAPI | 用户 token、模型列表、渠道权限、body 加密 | +| 普通 sub Runtime Agent | Runtime 子 Agent -> NewAPI/Manager 模型网关 | Runtime 环境变量、模型名、base_url、请求路径、上游超时 | +| Manager 后台模型配置 | 管理员网页 -> Manager/NewAPI | 渠道配置、分组、可用模型、价格表达式 | + +曾踩过的坑: + +- 某个模型 503/504 时,不一定是客户端参数错,也可能是 Runtime 子 Agent 使用的模型、base_url 或请求格式不对。 +- Runtime 需要回传 `newapi_request_id` 和非零 token usage,方便定位 NewAPI 日志。 +- 一个模型失败时可以切换模型验证,但不能把失败摘要 artifact 当成业务完成。 + +## 8. Azure Key Vault / OpenBao 边界 + +当前正式方向是 Azure Key Vault,不是 OpenBao。 + +| 项目 | 结论 | +|------|------| +| Azure Key Vault | 正式长期密钥托管方案 | +| OpenBao | VM 上存在历史/兼容容器,不作为当前正式方案 | +| Managed Identity | Manager 访问 Key Vault 的推荐方式 | +| 当前已知问题 | 之前健康检查出现过 `Identity not found`,说明 VM 身份或 `AZURE_CLIENT_ID`/Vault 权限未配好 | + +如果后续迁移到 Container Apps / AKS / App Service,不能只切域名。必须重新配置 Managed Identity、Key Vault 权限、环境变量、持久化、数据库/Redis 网络、Runtime callback 地址。 + +## 9. 登录、设备、模型列表问题排查 + +之前遇到过用户登录成功但设备看不到、模型列表拿不到的问题。排查顺序: + +1. Manager 是否有该用户记录。 +2. JWT/session 是否能通过 Manager 校验。 +3. 设备注册/心跳是否入库。 +4. 用户是否绑定 NewAPI channel/group/token。 +5. NewAPI 返回是否 401/403/模型列表为空。 +6. Mac/Windows 客户端请求是否走同一 base_url、同一加密/签名逻辑。 + +不要只看“客户端显示已登录”,已登录不代表模型、设备、NewAPI 绑定都完整。 + +## 10. 网页端 Manager 已做过的重点 + +已处理过的方向包括: + +- 注册页国内邮箱提示。 +- Manager 登录后菜单跳转问题。 +- 任务总览 / deployments 页面部分英文文案中文化。 +- 普通 sub 控制面、部署草稿、运行状态、timeline、events、logs、artifacts 展示。 +- artifact content 获取链路文档。 +- Azure Key Vault secret_ref 接入方向。 +- PayPal 充值和 NewAPI 模型费用关系说明文档。 + +继续改网页端时必须真实点击验证,尤其是: + +- 登录后左侧菜单。 +- 模型、渠道、供应商、支付、部署、任务总览。 +- 创建新运行弹窗/抽屉。 +- 产物卡片和下载入口。 + +## 11. 部署和 git 规则 + +生产部署原则: + +```text +本地修改 +-> git commit +-> git push 到 heicode-mananger main +-> VM 上 git pull +-> docker compose ... up -d --build +-> sudo docker image prune -f +-> 真实接口/页面冒烟 +``` + +注意: + +- 不要用 `scp` 传整份源码到 VM。 +- `HeiCode-issues.git` 已废弃,后续不用再更新。 +- VM 构建后必须清理 Docker 镜像,避免磁盘被旧层占满。 +- 不要提交 `.env`、密钥、token、数据库连接串。 + +## 12. 新 AI 接手建议顺序 + +1. 读 `AGENTS.md` 和 `heicode/AGENTS.md`。 +2. 读 `docs/deployment/Heicode-Manager-生产配置与账号交接清单.md`。 +3. 用 `git status` 确认是否有未提交变更,不要动无关文件。 +4. 区分当前任务是普通 sub、蜂群、网页端、NewAPI、Azure 还是桌面客户端。 +5. 先用接口确认真实状态,再下结论。 +6. 涉及生产前先确认是否需要部署,部署后必须真实冒烟。 +7. 涉及密码、token、连接串时只写配置名和获取位置,不写明文。 + +## 13. 已踩过的典型坑 + +| 坑 | 正确处理 | +|----|----------| +| 把普通 sub 和蜂群混在一起 | 两套模式、两套配置、两套联调清单 | +| artifact 摘要当完整交付 | 必须通过 content 接口拿完整产物 | +| Runtime 返回 completed 就算成功 | 还要看 usage、artifact、日志、是否失败摘要 | +| Key Vault health 报错只改代码 | 先查 Managed Identity 和 Vault 权限 | +| NewAPI 一个模型失败就判 Manager 错 | 查 Runtime 请求路径、模型名、request id、上游状态 | +| VM 上有 postgres/redis 容器就当生产库 | 以 `SQL_DSN`、`REDIS_CONN_STRING` 和 Azure 托管服务为准 | +| 修改网页后不点击验证 | 必须真实打开页面、点菜单、点按钮 | +| 把密钥写入 md 方便交接 | 只能写配置名、用途、位置,不能写明文 | + +## 14. 当前后续重点 + +| 方向 | 后续任务 | +|------|----------| +| 普通 sub | 持续和桌面客户端联调真实开发任务,确认 artifact content 是完整业务产物 | +| 蜂群 | 如需启用,先配置 `SWARM_RUNTIME_*`,单独跑蜂群 E2E | +| Key Vault | 修复 Managed Identity / `AZURE_CLIENT_ID` / Key Vault 权限 | +| NewAPI | 保持 Runtime 回传 `newapi_request_id`、usage、成本信息 | +| 网页端 | 继续中文化、交互完善、真实点击测试 | +| 生产部署 | 每次部署后检查 `/api/status`、登录、deployments、Runtime health | diff --git a/README.md b/README.md index 469ef52..8f90bbe 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Heicode 面向**多人协作、可追溯交付**的软件团队:把需求对 | `cc-haha/` | **Heicode**(终端与桌面客户端及本地服务;此为源码目录名)。 | | `heicode/` | **Heicode Manager**(网关与管理控制台服务端;此为源码目录名)。 | | `website/` | 产品介绍站点(Next.js;可 `pnpm dev` 或 Docker 预览)。 | -| `docs/` | 愿景与范式;**[`docs/milestones/`](./docs/milestones/README.md)** 交付里程碑;**[`docs/integration/`](./docs/integration/README.md)** Agnet 等平台接口设计。 | +| `docs/` | 愿景与范式;**[`docs/milestones/`](./docs/milestones/README.md)** 交付里程碑;**[`docs/integration/`](./docs/integration/README.md)** Agent 等平台接口设计。 | ## 产品在解决什么问题 @@ -68,7 +68,7 @@ cd website && pnpm install && pnpm dev ## 相关外部参考(概念) - [oh-my-claudecode](https://ohmyclaudecode.com/) — Claude Code 类工具的高效实践参考。 -- Agnet 平台以实际部署环境与文档为准。 +- Agent 平台以实际部署环境与文档为准。 ## 许可证 diff --git a/docs/2026-05-02-daily-summary.txt b/docs/2026-05-02-daily-summary.txt index 08806df..74bcdb8 100644 --- a/docs/2026-05-02-daily-summary.txt +++ b/docs/2026-05-02-daily-summary.txt @@ -7,8 +7,8 @@ Heicode 今日工作总结与后续计划 1. 项目主线梳理 - 已确认 docs/heicode.md 与 docs/plan.md 是当前实施主线。 -- 已明确旧 Agnet API 草案、旧 M1-M5 计划、旧架构说明不作为当前实施依据。 -- 已恢复 docs/README.md 为主线文档入口,并加入 Agnet 平台请求契约文档入口。 +- 已明确旧 Agent API 草案、旧 M1-M5 计划、旧架构说明不作为当前实施依据。 +- 已恢复 docs/README.md 为主线文档入口,并加入 Agent 平台请求契约文档入口。 2. P0 文档边界收敛 - 已完成 P0:文档入口与实施依据收敛。 @@ -21,15 +21,15 @@ Heicode 今日工作总结与后续计划 - 已加入 GORM 自动迁移。 - 新增认证 API:/api/resources 与 /api/resource-grants。 - 支持的资源类型包括:git、sk、project_document、cloud_account、cloud_resource。 -- 资源授权可表达 tenant/project/resource 到 role/子 Agnet 的最小权限关系。 +- 资源授权可表达 tenant/project/resource 到 role/子 Agent 的最小权限关系。 - 已实现 secret_ref-only 原则:只保存密钥引用,不保存明文密钥。 - 已加入校验,拒绝 metadata、permission_scope、constraints 中出现 password、token、secret、private_key、access_key、credential 等明文敏感字段。 -4. Agnet 平台请求参数文档 -- 已新增文档:docs/integration/agnet-platform-request-contract.md。 -- 该文档说明 Manager 主动请求 Agnet 平台时需要携带的参数。 +4. Agent 平台请求参数文档 +- 已新增文档:docs/integration/agent-platform-request-contract.md。 +- 该文档说明 Manager 主动请求 Agent 平台时需要携带的参数。 - 覆盖内容包括: - - 创建子 Agent 部署 POST /api/agnet/deployments。 + - 创建子 Agent 部署 POST /api/agent/deployments。 - orchestration_plan、agents、sk_sources、runtime_execution、sk_access_policy、resource_grants 字段说明。 - secret_ref、安全边界、租户/项目边界、预算与模型约束。 - 部署状态查询、停止部署。 @@ -37,7 +37,7 @@ Heicode 今日工作总结与后续计划 - 监控接口:项目 dashboard snapshot、单部署 metrics。 - 事件接口、审计日志接口。 - SK 快照解析与查询接口。 -- 已提交:ab71d5b docs: add agnet platform request contract。 +- 已提交:ab71d5b docs: add agent platform request contract。 5. 团队协作与校验 - 已启动并完成 5 个 OMX worker 的协作任务。 @@ -80,7 +80,7 @@ Heicode 今日工作总结与后续计划 - 当前未执行生产 VM SSH 部署。 - 当前未对 Azure PostgreSQL、Redis、AKS 或其他云资源执行写操作。 - 当前未消耗可量化的线上运行资源。 -- 本次新增的资源模型与 Agnet 请求契约为后续生产部署和平台联调准备。 +- 本次新增的资源模型与 Agent 请求契约为后续生产部署和平台联调准备。 4. Git 资源 - 当前 main 相对 origin/main ahead 15 个提交。 @@ -100,7 +100,7 @@ Heicode 今日工作总结与后续计划 2. 当前可部署基础 - P0 文档边界已收敛。 - P1 Manager 资源模型最小后端能力已实现。 -- Manager → Agnet 平台接口参数文档已补齐,包含日志和监控接口。 +- Manager → Agent 平台接口参数文档已补齐,包含日志和监控接口。 - 变更包 controller/model/router 的测试与 vet 已通过。 3. 部署前阻塞/风险 @@ -124,21 +124,21 @@ Heicode 今日工作总结与后续计划 - 部署后检查服务健康、日志、API 可用性。 3. P1 后续完善 -- 将 Resource Binding / Resource Grant 与现有项目、用户、Agnet 部署流更紧密打通。 +- 将 Resource Binding / Resource Grant 与现有项目、用户、Agent 部署流更紧密打通。 - 完善资源授权 revoke/disable/status transition。 - 补充跨数据库兼容验证,尤其 PostgreSQL 线上迁移验证。 - 补充前端资源绑定与授权页面,避免展示任何 secret。 -4. Agnet 平台联调 -- 按 docs/integration/agnet-platform-request-contract.md 与 Agnet 平台确认最终字段。 +4. Agent 平台联调 +- 按 docs/integration/agent-platform-request-contract.md 与 Agent 平台确认最终字段。 - 联调创建部署、查询状态、停止部署、日志拉取、监控快照、事件、审计日志。 -- 明确哪些接口由 Manager 内部提供,哪些接口由 Agnet 平台提供。 -- 确认 Agnet 平台对日志和监控返回的脱敏策略。 +- 明确哪些接口由 Manager 内部提供,哪些接口由 Agent 平台提供。 +- 确认 Agent 平台对日志和监控返回的脱敏策略。 5. 测试与质量 - 解决或隔离 go test ./... 的既有失败。 - 确保 frontend/web 依赖可安装并完成 typecheck/lint/build。 -- 增加 e2e 验证:创建资源 -> 创建授权 -> 创建 Agnet 部署 -> 查询事件/日志/监控。 +- 增加 e2e 验证:创建资源 -> 创建授权 -> 创建 Agent 部署 -> 查询事件/日志/监控。 六、安全说明 diff --git a/docs/Heicode-Manager-agent统一改造落地计划.md b/docs/Heicode-Manager-agent统一改造落地计划.md new file mode 100644 index 0000000..057ff6c --- /dev/null +++ b/docs/Heicode-Manager-agent统一改造落地计划.md @@ -0,0 +1,183 @@ +# Heicode Manager 统一改造落地计划(Manager 侧) + +> 版本:v0.1(初稿) +> 日期:2026-06-01 +> 负责范围:仅 `heicode-mananger`(Manager 控制面 + `web/default` 前端) +> 性质:内部落地计划,随实现推进持续更新。不代表对外承诺。 + +> **状态更新(2026-06-01):`agnet → agent` 已按"强制全量、不留兼容"完成。** +> 后端 Go(路由 `/api/agent/*`、env `AGENT_*`、结构体/函数/文件名)、前端(`agent-console`/`agent-hub`、`/api/agent` 调用、i18n)、DB(表 `agent_*`、列 `agent_id`,并加幂等迁移 `renameAgnetTablesToAgent` 保住老数据)、文档、compose/.env、脚本均已统一为 `agent`。 +> `go build ./...` 与 `go test ./controller ./model ./router ./middleware` 全绿;前端 `tsc -b` 通过。 +> 本文 §3 原描述的"保留 `agnet` 兼容别名/线缆令牌"策略已**作废**——实际采用一次性硬切。唯一保留 `agnet` 字样的是 `model/main.go` 的迁移源表名(迁移所必需)。 +> **部署待办**:① VM `.env` 的 `AGNET_*` 改名 `AGENT_*`;② 启动时迁移自动 rename 老表(已就绪);③ 其他三仓库(agent_management / HeiCode-Swarm / 客户端)需同步切到 `/api/agent` 与新 env,否则跨服务对接在它们切换前会中断(按你的要求强制先行)。 + +## 0. 基准文档 + +本计划是以下两份的 **Manager 侧拆解执行版**,结论以原文为准: + +| 来源 | 位置 | 作用 | +|---|---|---| +| 统一调用方案 v0.1 | gitee `taijibaga/heicodedebug` → `2026-06-01_heicode客户端相关定义.md` | 四仓库总纲:接口归口、模式拆分、状态裁决、产物、回调 | +| 全链路代码评审报告 | gitee `taijibaga/heicodedebug` → `2026-06-01_Heicode全链路代码评审报告.md` | P1–P7 问题与证据 | +| 桌面 sub 对接文档 | `docs/integration/heicode-desktop-sub-agile-api.md` | 现行普通 sub 契约 | + +四仓库分工(总纲 §4):客户端只调 Manager;**Manager 是唯一接口入口 + Runtime 路由器 + 唯一状态裁判**;`agent_management` 执行 Sub Agile;`HeiCode-Swarm` 执行 Swarm。 + +## 1. 贯穿全局的原则(来自总纲) + +1. `agnet` 是历史拼写错误,统一为 `agent`;旧接口兼容期只做转发。 +2. Sub Agile 与 Swarm 是两套运行时,**不再复用 `/api/swarms` 表达两种模式**,按 `mode` 路由。 +3. Runtime 只上报**结构化事实**;Manager 据结构化字段裁决 `display_status`;客户端只消费结论,**不再用正则自判产物**。 +4. 安全红线:只传 `azkv://` 等 `secret_ref`,禁明文密钥;高危操作 Manager 先审批再下发。 + +## 2. 已完成(分支 `fix/manager-deliverable-and-secret-validation`,commit `12602eb`,已推送) + +| 项 | 文件 | 内容 | 对应总纲 | +|---|---|---|---| +| P2 交付物判定 | `controller/agnet_runtime_client.go` | `runtimeArtifactsAreSummaryOnly` 改读结构化 `artifact_type`+文件信号,弃脆弱正则 | §10.6 | +| P6a secret_ref 校验 | `controller/resource.go` | Resource CRUD 强制 `azkv://` | §16/§17.6 | +| P6b 值级密钥扫描 | `controller/resource.go`、`controller/agnet_control_plane.go` | `containsPlaintextSecret`/`containsSensitiveGrantField` 扫字符串值(sk-/JWT/PEM) | §16 | +| P5 默认模型收敛 | `controller/agnet_role_template.go`、`controller/agnet_task_bridge.go` | 单一来源 `defaultAgnetModelID()`(env `AGNET_DEFAULT_MODEL_ID`,默认 `gpt-5.4`);移除 `agnet-model-` 占位回退 | §9 | +| P3 状态枚举 | `docs/integration/heicode-desktop-sub-agile-api.md` | 补 `completed` 终态 + runtime_state 镜像 + 未知值兜底 | §10.4 | +| 单测 | `controller/agnet_deliverable_secret_test.go` | 覆盖以上行为 | — | + +> 注:`defaultAgnetModelID` 在后续命名统一中应改名 `defaultAgentModelID`,env 改 `AGENT_DEFAULT_MODEL_ID`(保留 `AGNET_*` 回退)。 + +## 3. 命名统一 `agnet → agent` + +### 3.1 爆炸半径(仅本仓库,不含另三仓库与跨团队契约) + +**2155 处 / 63 文件**(`rg -i agnet`)。按「客户端/其他服务是否依赖」分类: + +| 类别 | 规模 | 对外可见 | 处理策略 | 风险 | +|---|---|---|---|---| +| HTTP 路由 `/api/agnet/*` | 路由表 | ✅ 客户端+Runtime | 新增 `/api/agent/*` 别名,旧保留转发 | 🔴 | +| JSON 字段 | 仅 `agnet_id`(3 处,含 DB 列) | ✅ resource grant 契约 | 响应 dual-emit `agent_id`,请求 dual-accept | 🟠 | +| env 变量 `AGNET_*` | compose + 代码读取 | ✅ 部署配置 | 代码先读 `AGENT_*` 回退 `AGNET_*` | 🟠 | +| DB 表 `agnet_*` / `agnet_id` 列 | ~6 表 | ❌ 内部 | **用 GORM `TableName()` 钉住物理名不动**,物理改名作为最后单独迁移 | 🔴 | +| Go 内部标识(`AgnetXxx`、函数、文件名 `agnet_*.go`) | ~1800 | ❌ 编译期可查 | 纯重构,分模块小步改 | 🟢 | +| 前端(`agnet-console/`、组件、i18n key) | ~250 | ❌ 内部(调的是路由) | 重构,随路由切换 | 🟢 | +| 文档 | 多处 | — | 新文档写 `agent`,`agnet` 标 deprecated | 🟢 | + +### 3.2 兼容期规则(总纲 §19) + +1. 旧接口只做转发。2. 新文档只写新接口。3. 新客户端只调新接口。4. Manager 内部存储统一 `agent` 命名。5. 日志可记 legacy route,不展示给普通用户。 + +## 4. 路由拆分与归口(总纲 §5) + +### 4.1 客户端 → Manager(新增) +``` +/api/heicode/capabilities +/api/heicode/sub-agile/tasks/... (或统一 /api/heicode/tasks + body.mode) +/api/heicode/swarm/tasks/... +``` + +### 4.2 Manager → Runtime(新增,按 mode 分流) +``` +/api/agent/sub-agile/deployments -> agent_management (AGENT_RUNTIME_* / 现 AGNET_RUNTIME_*) +/api/agent/swarm/deployments -> HeiCode-Swarm (SWARM_RUNTIME_*) +``` +Manager 已具备双 env 前缀分流机制(`agnetRuntimeClientConfigForMode`);蜂群侧配置就绪只差 `SWARM_RUNTIME_SERVICE_TOKEN`。 + +### 4.3 回调(新增 + 旧转发) +``` +新:POST /api/agent/callbacks/runtime-events +旧:POST /api/agnet/callbacks/swarm-events (兼容转发到同一 handler) +``` + +## 5. 状态裁决:Manager 成为唯一裁判(总纲 §10,关联评审 P1) + +### 5.1 三层状态 + display_status +- `client_task_status`(客户端本地)/ `cloud_deployment_status`(Manager)/ `runtime_execution_status`(Runtime) +- Manager 输出唯一 `display_status` 给客户端。 + +### 5.2 Runtime 必须上报的结构化交付物事实(契约) +```json +"deliverable": { + "has_deliverable": true, + "summary_only": false, + "artifact_ids": ["art_xxx"], + "files_modified": ["src/app.ts"], + "has_diff": true, + "commit_sha": "" +} +``` + +### 5.3 Manager 裁决规则 +| Runtime 事实 | display_status | +|---|---| +| `completed` + has_deliverable + !summary_only | `completed` | +| `completed` + summary_only / 无 artifact | `completed_without_deliverable` 或 `needs_codegen` | +| `failed` | `failed` / `stopped` / `waiting_approval` 同名透传 | + +### 5.4 Manager 落点 +- `controller/agnet_callback.go`(`applyAgnetCallbackDeploymentState` 消费 `deliverable`,算 verdict) +- `model/agnet_deployment.go`(加 `DeliveryVerdict` / display_status 列,三库兼容 `varchar`) +- `controller/agnet_control_plane.go`(detail/timeline 暴露字段) +- 复用已就绪的 `artifactIsSummaryOnly`(P2)+ usage +- **阶段化**:先加派生字段不改 `status`(向后兼容)→ 客户端跟进后再引入新终态值。 + +## 6. 模型策略(总纲 §9) + +- Sub Agile:`per_role` / `default`;Swarm:`primary`。 +- 单一来源默认模型(P5 已起步,待改名 `defaultAgentModelID`)。 +- Manager 校验:模型存在 / 套餐允许 / 角色允许 / 预算 / Runtime 支持。 + +## 7. 项目文件夹产物 `project_folder`(总纲 §12)—— 中期大件 + +- artifact 升为两级:`Project Artifact` → `File Artifacts / Directory Entries`。 +- 新 artifact_type:`project_folder` / `project_archive`(现有 `code_patch/document/...` 保留)。 +- 新增接口:`.../artifacts/{id}/manifest`、`.../files/{path}`、`.../archive`。 +- **本地修改回传 + revision 协议**(§12.7):`local-edits` / `batch` / 冲突 `ARTIFACT_REVISION_CONFLICT` / Manager 维护 current accepted revision。 +- 影响:新 model(artifact revision / project entries)、新 controller、content 代理扩展。**工程量大,单独立项。** + +## 8. 云部署生命周期(总纲 §18)—— 远期 + +`project_folder` → 选 target(Azure/阿里云/AWS) → Manager 校验/审批/凭证/预算 → Deploy Worker/Runtime 执行 → `deployment_manifest` artifact 回传。**远期,本轮不展开。** + +## 9. 安全与审计(总纲 §16/§17) + +| 项 | 现状 | 待办 | +|---|---|---| +| `secret_ref` 强制 `azkv://` | ✅ agnet 路径 + Resource CRUD(P6a) | 覆盖其余写入路径 | +| 值级密钥扫描 | ✅ P6b | — | +| 高危操作 Manager gate | ❌ 现为立即 `accepted`,阻断依赖 Runtime | Manager 侧加 risk_level/pending 审批 gate(关联 P1/§17.1) | +| 短期凭证 lease 真派生 | ❌ 占位(只发 `lease://`,不 mint) | 接 Key Vault 派生短期凭证 | +| revoke 同步 Runtime | ⚠️ approve 有 sync,revoke 无 | 补 revoke→Runtime 通知 | +| 审计 fail-closed | ⚠️ best-effort | 关键审计改 fail-closed 或告警 | +| 客户端禁 inline secret_ref | — | Manager 改为接受 `resource_binding_id`,内部映射 secret_ref(§17.6) | + +## 10. 落地阶段(总纲 §20,Manager 承担) + +| 阶段 | Manager 任务 | 自主性 | 依赖 | 状态 | +|---|---|---|---|---| +| **1 接口/命名** | `/api/agent/*` 别名、`/api/heicode/{sub-agile,swarm}/*`、callback 新路由、`AGENT_*` env 回退、`agnet_id`→`agent_id` 双字段 | ✅ 加法自主 | — | 待开始 | +| **2 模式路由** | 按 `mode` 路由两套 `*_RUNTIME_*` | ✅ | 蜂群 token | 机制已就绪 | +| **3 模型策略** | per_role/primary 校验 | ✅ | — | P5 起步 | +| **4 状态/产物裁决** | display_status + deliverable 字段 + project_folder + revision | 🟡 | Runtime 发结构化事实 | P1/P2 起步 | +| **5 日志/回调/调试** | user_logs/debug_logs 分层 + diagnostics + 统一 callback | ✅ | Runtime callback 切换 | diagnostics 已有雏形 | + +## 11. 跨团队依赖(Manager 做不了,需协调) + +1. **agent_management / HeiCode-Swarm**:上报结构化 `deliverable` 事实;接入 `/api/agent/{sub-agile,swarm}/*` 路由;callback 切 `/api/agent/callbacks/runtime-events`;产出真实 `project_folder`。 +2. **蜂群 Runtime**:`SWARM_RUNTIME_SERVICE_TOKEN` 安全配置 + 修复 single-agent fallback(评审 P7)。 +3. **客户端(macOS/Windows)**:切新接口、消费 display_status、project 文件树展示、本地 edit 上传、禁 mock/直连(§17)。 + +## 12. 风险与红线 + +- **`agnet` 是 load-bearing**(路由/env/DB/跨服务契约)——**严禁全局 sed**,必须加法别名 + 兼容期。 +- DB 物理表/列名暂不改,用 `TableName()` 钉住,避免迁移风险。 +- 跨服务契约字段改动一律 **dual-emit + dual-accept**,且需与调用方协调切换节奏。 +- 状态裁决先加字段不覆盖 `status`,避免误伤「真完成但 Runtime 未回 artifact」的任务。 + +## 13. 待确认事项 + +1. 统一方案仍是 **v0.1**,落地前需与作者对齐版本/范围。 +2. approval decision 回传路径:`/api/agent/{mode}/deployments/{id}/approvals/{approval_id}` 的最终形态。 +3. 客户端→Manager 路由用「分模式」还是「统一 `/api/heicode/tasks` + body.mode」。 +4. `needs_codegen` 与 `completed_without_deliverable` 的判定边界(何时用哪个)。 +5. project_folder / 云部署的优先级与排期(是否本阶段做)。 + +--- + +*维护:本文件随实现进度更新;每完成一项在「已完成/状态」列标注 commit。* diff --git a/docs/README.md b/docs/README.md index adf53a3..f0cd8fc 100644 --- a/docs/README.md +++ b/docs/README.md @@ -10,9 +10,9 @@ | [`heicode-manager-sub-swarm-progress-checklist.md`](./heicode-manager-sub-swarm-progress-checklist.md) | Heicode Manager sub 模式、瀑布/敏捷、蜂群模式的已完成/未完成/依赖/风险/下一步进度清单 | | [`heicode-manager-standalone-execution-plan.md`](./heicode-manager-standalone-execution-plan.md) | Heicode Manager 端可独立完成任务的执行计划、顺序、验收标准和边界 | | [`integration/Heicode-登录接口对接文档.md`](./integration/Heicode-登录接口对接文档.md) | 已上线登录接口对接文档 | -| [`integration/agnet-platform-request-contract.md`](./integration/agnet-platform-request-contract.md) | Manager 请求 Agnet 平台时携带的部署、日志、监控、事件与审计接口参数 | -| [`deployment/azure-production-deploy-guardrails.md`](./deployment/azure-production-deploy-guardrails.md) | Azure VM / PostgreSQL / Redis / Agnet / NewAPI 生产部署前的安全守卫、环境变量注入和验证计划 | +| [`integration/agent-platform-request-contract.md`](./integration/agent-platform-request-contract.md) | Manager 请求 Agent 平台时携带的部署、日志、监控、事件与审计接口参数 | +| [`deployment/azure-production-deploy-guardrails.md`](./deployment/azure-production-deploy-guardrails.md) | Azure VM / PostgreSQL / Redis / Agent / NewAPI 生产部署前的安全守卫、环境变量注入和验证计划 | -旧 Agnet API 草案、旧里程碑、旧架构说明和旧上手材料不再作为实施依据。后续文档和实现以 `heicode.md` 与 `plan.md` 为准;生产部署操作以安全守卫文档约束,且不得覆盖产品/架构主线。 +旧 Agent API 草案、旧里程碑、旧架构说明和旧上手材料不再作为实施依据。后续文档和实现以 `heicode.md` 与 `plan.md` 为准;生产部署操作以安全守卫文档约束,且不得覆盖产品/架构主线。 -代码中的过渡期命名、旧接口注释或旧 UI 文案只作为现状参考;若与 `heicode.md` / `plan.md` 冲突,应先更新实现或另行补充当前主线文档,不得恢复旧 Agnet/M1-M5 草案作为依据。 +代码中的过渡期命名、旧接口注释或旧 UI 文案只作为现状参考;若与 `heicode.md` / `plan.md` 冲突,应先更新实现或另行补充当前主线文档,不得恢复旧 Agent/M1-M5 草案作为依据。 diff --git a/docs/deployment/Heicode-Manager-更换部署服务配置清单.md b/docs/deployment/Heicode-Manager-更换部署服务配置清单.md index aa3f25f..5299c81 100644 --- a/docs/deployment/Heicode-Manager-更换部署服务配置清单.md +++ b/docs/deployment/Heicode-Manager-更换部署服务配置清单.md @@ -43,22 +43,22 @@ HEICODE_PUBLIC_BASE_URL=https://code.xinghanlab.com ### 2.2 Agent Manager / 普通 sub / 蜂群联调配置 ```env -AGNET_RUNTIME_ENABLED=true -AGNET_RUNTIME_BASE_URL=http://20.212.121.126 -AGNET_RUNTIME_CREATE_PATH=/api/swarms -AGNET_RUNTIME_STOP_PATH=/api/swarms/{swarm_id}/stop -AGNET_RUNTIME_APPROVAL_DECISION_PATH=/api/swarms/{swarm_id}/approvals/{approval_id} -AGNET_RUNTIME_HEALTH_PATH=/api/agnet/health -AGNET_RUNTIME_TIMEOUT_SECONDS=15 -AGNET_RUNTIME_SERVICE_TOKEN=<生产 service token> -AGNET_CALLBACK_TOKEN=<生产 callback 兼容 token> -AGNET_RUNTIME_CALLBACK_SIGNING_SECRET_REF=azkv://heicode-kv.vault.azure.net/secrets/agnet-callback-signing-key +AGENT_RUNTIME_ENABLED=true +AGENT_RUNTIME_BASE_URL=http://20.212.121.126 +AGENT_RUNTIME_CREATE_PATH=/api/swarms +AGENT_RUNTIME_STOP_PATH=/api/swarms/{swarm_id}/stop +AGENT_RUNTIME_APPROVAL_DECISION_PATH=/api/swarms/{swarm_id}/approvals/{approval_id} +AGENT_RUNTIME_HEALTH_PATH=/api/agent/health +AGENT_RUNTIME_TIMEOUT_SECONDS=15 +AGENT_RUNTIME_SERVICE_TOKEN=<生产 service token> +AGENT_CALLBACK_TOKEN=<生产 callback 兼容 token> +AGENT_RUNTIME_CALLBACK_SIGNING_SECRET_REF=azkv://heicode-kv.vault.azure.net/secrets/agent-callback-signing-key ``` 当前生产为了让 Agent Manager 自动 callback 先跑通,配置了 HMAC fallback: ```env -AGNET_CALLBACK_SIGNING_SECRET=<生产 callback 签名密钥> +AGENT_CALLBACK_SIGNING_SECRET=<生产 callback 签名密钥> ``` 正式方案建议用 Azure Key Vault 托管该签名密钥,减少明文环境变量。 @@ -108,7 +108,7 @@ Key Vault:`heicode-kv` Key Vault 里必须存在: ```text -agnet-callback-signing-key +agent-callback-signing-key ``` 建议 Secret value 为 JSON: @@ -153,7 +153,7 @@ agnet-callback-signing-key - Redis 防火墙/VNet 是否允许新服务访问。 - Key Vault 防火墙是否允许新服务访问。 - 如果使用 VNet 集成,DNS 解析是否正常。 -- Agent Manager 使用 IP 联调时,后续切域名要同步更新 `AGNET_RUNTIME_BASE_URL`。 +- Agent Manager 使用 IP 联调时,后续切域名要同步更新 `AGENT_RUNTIME_BASE_URL`。 ## 6. 域名和回调地址 @@ -176,7 +176,7 @@ HEICODE_PUBLIC_BASE_URL=https://code.xinghanlab.com 因为 Manager 发给 Agent Manager 的 callback 地址会基于它生成: ```text -https://code.xinghanlab.com/api/agnet/callbacks/swarm-events +https://code.xinghanlab.com/api/agent/callbacks/swarm-events ``` 如果临时域名联调,要么设置临时 `HEICODE_PUBLIC_BASE_URL`,要么确保正式域名已经能路由到新服务。 @@ -219,7 +219,7 @@ curl -fsS https://code.xinghanlab.com/api/status 使用测试用户登录后验证: - 登录成功。 -- `/api/agnet/user/deployments` 返回正常。 +- `/api/agent/user/deployments` 返回正常。 - 页面 `https://code.xinghanlab.com/deployments` 可打开。 ### 8.3 Key Vault 健康 diff --git a/docs/deployment/azure-production-deploy-guardrails.md b/docs/deployment/azure-production-deploy-guardrails.md index 64cb11e..515e827 100644 --- a/docs/deployment/azure-production-deploy-guardrails.md +++ b/docs/deployment/azure-production-deploy-guardrails.md @@ -1,8 +1,8 @@ # Azure 生产部署安全守卫与验证计划 -本文用于 Heicode Manager / Agnet / NewAPI 相关生产发布前的人工执行检查。它只描述安全命令、环境变量名和验证项,不保存任何真实地址、账号、密码、Token、连接串、SSH key 或云访问密钥。 +本文用于 Heicode Manager / Agent / NewAPI 相关生产发布前的人工执行检查。它只描述安全命令、环境变量名和验证项,不保存任何真实地址、账号、密码、Token、连接串、SSH key 或云访问密钥。 -适用范围:Azure VM、Azure PostgreSQL、Azure Redis、Git 同步、Nginx 统一入口、Heicode Manager 容器、Azure Key Vault 密钥保管、Agnet 平台联调、NewAPI 网关能力验证。 +适用范围:Azure VM、Azure PostgreSQL、Azure Redis、Git 同步、Nginx 统一入口、Heicode Manager 容器、Azure Key Vault 密钥保管、Agent 平台联调、NewAPI 网关能力验证。 ## 1. 执行原则 @@ -11,8 +11,8 @@ | 凭据处理 | 只使用 VM 环境、交互式 SSH、未提交的 `.env`、Key Vault 或 `secret_ref`;禁止把真实密钥写入 Git、Markdown、终端报告或 team state。 | | Git 发布 | 仅允许快进同步已审核提交;禁止在生产 VM 上提交代码或保存临时补丁。 | | 数据库/Redis | Azure PostgreSQL / Redis 连接串只写入 VM 本地 `.env` 或 Secret Store;验证时只打印变量名和连通性结果,不打印值。 | -| Agnet / NewAPI | Manager 只传 `secret_ref`、部署计划、资源授权和审计上下文;禁止把明文云账号、数据库密码、模型 Key 放入请求体。 | -| Azure Key Vault | 只允许 Manager/Agnet 通过 Managed Identity 和受控网络访问;如果 Manager 提供客户端验证和绑定接口,Key Vault 不暴露普通公网访问。 | +| Agent / NewAPI | Manager 只传 `secret_ref`、部署计划、资源授权和审计上下文;禁止把明文云账号、数据库密码、模型 Key 放入请求体。 | +| Azure Key Vault | 只允许 Manager/Agent 通过 Managed Identity 和受控网络访问;如果 Manager 提供客户端验证和绑定接口,Key Vault 不暴露普通公网访问。 | | 生产动作 | 执行 `up -d`、迁移、重启、回滚前必须记录当前镜像/提交和健康检查 URL;失败时停止扩大变更。 | ## 2. 本地发布前检查 @@ -97,7 +97,7 @@ ssh "$REMOTE" "cd '$REMOTE_DIR/heicode' && docker compose -f docker-compose.azur - 对外域名统一使用 `heicode.xinghanlab.com`。 - Nginx 负责按路由转发 Manager 与 NewAPI,例如 Manager 主站、NewAPI 受控 API 或健康检查路由。 -- Azure Key Vault 仅供 Manager/Agnet 服务端通过 Managed Identity 访问,不通过 `heicode.xinghanlab.com` 暴露给浏览器用户。 +- Azure Key Vault 仅供 Manager/Agent 服务端通过 Managed Identity 访问,不通过 `heicode.xinghanlab.com` 暴露给浏览器用户。 - Key Vault 应优先使用 Private Endpoint;如临时启用公网访问,必须限制来源并在验证后关闭。 构建和启动: @@ -122,9 +122,9 @@ VM_HOST="$VM_HOST" VM_USER="$VM_USER" REMOTE_DIR="$REMOTE_DIR/heicode" \ ./heicode/bin/azure_vm_deploy.sh ``` -## 7. Agnet 平台联调守卫 +## 7. Agent 平台联调守卫 -Manager 请求 Agnet 平台时遵循 `docs/integration/agnet-platform-request-contract.md`: +Manager 请求 Agent 平台时遵循 `docs/integration/agent-platform-request-contract.md`: - `resource_grants[].secret_ref` 必须是 Secret Store 引用,不能是明文密钥。 - `repo_url` 不能包含用户名、密码或 Token。 @@ -134,22 +134,22 @@ Manager 请求 Agnet 平台时遵循 `docs/integration/agnet-platform-request-co 安全 smoke request 模板: ```bash -export AGNET_BASE_URL='' +export AGENT_BASE_URL='' export MANAGER_SERVICE_TOKEN_SECRET_REF='' export USER_ID='' export BINDING_SCOPE='' # 真实 token 由运行环境注入;禁止把 token 字面值写入命令历史或文档。 -curl -fsS -X POST "$AGNET_BASE_URL/api/agnet/deployments" \ +curl -fsS -X POST "$AGENT_BASE_URL/api/agent/deployments" \ -H 'Content-Type: application/json' \ -H "X-User-Id: $USER_ID" \ -H "X-Binding-Scope: $BINDING_SCOPE" \ -H "Idempotency-Key: deploy-$(date +%Y%m%d%H%M%S)" \ -H "Authorization: Bearer $MANAGER_SERVICE_TOKEN" \ - --data @docs/integration/safe-agnet-deploy-example.json + --data @docs/integration/safe-agent-deploy-example.json ``` -若没有 `safe-agnet-deploy-example.json`,先用本地临时文件生成并确认只包含 `secret_ref`,不要提交包含环境特定值的 payload。 +若没有 `safe-agent-deploy-example.json`,先用本地临时文件生成并确认只包含 `secret_ref`,不要提交包含环境特定值的 payload。 ## 8. 发布后验证清单 @@ -162,7 +162,7 @@ curl -fsS -X POST "$AGNET_BASE_URL/api/agnet/deployments" \ | Git 版本 | `git rev-parse --short HEAD` | 与已审核提交一致。 | | DB/Redis 注入 | `awk -F= ... .env` | 只打印 key,包含 `SQL_DSN`、`REDIS_CONN_STRING`。 | | 登录链路 | 调用登录文档中的生产验证流程 | 不在日志或报告输出 token。 | -| Agnet 部署 | 查询部署状态/事件/审计接口 | 能看到 deployment、events、audit,且无明文凭据。 | +| Agent 部署 | 查询部署状态/事件/审计接口 | 能看到 deployment、events、audit,且无明文凭据。 | | NewAPI 网关能力 | 使用 Manager 受控模型调用或健康接口 | 只记录 request id、状态码、模型名,不记录 provider key。 | ## 9. 回滚与停止条件 @@ -172,7 +172,7 @@ curl -fsS -X POST "$AGNET_BASE_URL/api/agnet/deployments" \ 1. 任一日志、响应、Markdown 或 Git diff 中出现真实密钥。 2. `git merge --ff-only` 失败或 VM 工作区有未知改动。 3. 健康检查连续失败。 -4. Agnet 平台返回的事件/审计中包含明文凭据。 +4. Agent 平台返回的事件/审计中包含明文凭据。 5. 数据库迁移或容器启动错误无法在一次重试内恢复。 回滚指针: @@ -186,4 +186,4 @@ ssh "$REMOTE" "cd '$REMOTE_DIR/heicode' && IMAGE_TAG=\$(cat .last_success_image) ## 10. 本次文档产物状态 -本文件仅准备部署守卫和验证计划;未执行 Git push、SSH 登录、Azure 资源变更、生产容器重启或 Agnet 实际部署。 +本文件仅准备部署守卫和验证计划;未执行 Git push、SSH 登录、Azure 资源变更、生产容器重启或 Agent 实际部署。 diff --git a/docs/heicode-manager-standalone-execution-plan.md b/docs/heicode-manager-standalone-execution-plan.md index e709598..5ea2aed 100644 --- a/docs/heicode-manager-standalone-execution-plan.md +++ b/docs/heicode-manager-standalone-execution-plan.md @@ -6,7 +6,7 @@ ## 一、核查结论 -本文只列 Manager 端能独立完成的任务。判断标准是:不要求 Heicode 客户端新增功能、不要求蜂群 / Agnet Runtime 提供真实接口、不要求 AKS / NATS / Prometheus 等基础设施先上线。 +本文只列 Manager 端能独立完成的任务。判断标准是:不要求 Heicode 客户端新增功能、不要求蜂群 / Agent Runtime 提供真实接口、不要求 AKS / NATS / Prometheus 等基础设施先上线。 | 结论 | 说明 | |---|---| @@ -20,12 +20,12 @@ | 事项 | 排除原因 | 需要谁配合 | |---|---|---| -| 真实创建 Swarm Run 并返回 `swarm_id` | 需要蜂群平台提供 `POST /api/swarms` 或等价生产接口 | 蜂群 / Agnet Runtime | -| 子 Agnet claim、heartbeat、release、timeout | 需要 worker runtime 和任务池 | 蜂群 / Agnet Runtime | -| handoff、retry、blocked 的真实状态机 | 需要 Runtime 产生任务事件 | 蜂群 / Agnet Runtime | +| 真实创建 Swarm Run 并返回 `swarm_id` | 需要蜂群平台提供 `POST /api/swarms` 或等价生产接口 | 蜂群 / Agent Runtime | +| 子 Agent claim、heartbeat、release、timeout | 需要 worker runtime 和任务池 | 蜂群 / Agent Runtime | +| handoff、retry、blocked 的真实状态机 | 需要 Runtime 产生任务事件 | 蜂群 / Agent Runtime | | 真实日志流和 CPU/内存/耗时指标 | 需要日志/指标源 | 蜂群 / 基础设施 | | 客户端高危审批主弹窗 | 产品要求审批主体验在客户端 | Heicode 客户端 | -| 短期凭证真实注入子 Agnet | 需要受控 runtime、身份和网络通道 | 蜂群 / 基础设施 | +| 短期凭证真实注入子 Agent | 需要受控 runtime、身份和网络通道 | 蜂群 / 基础设施 | | SK 工具真实调用结果 | 需要 SK 平台或 Runtime 上报 invocation event | 蜂群 / SK 平台 | | 交付结果回到客户端 | 需要客户端展示和 Runtime artifact 输出 | 客户端 + 蜂群 | @@ -34,9 +34,9 @@ | 顺序 | 任务 | 优先级 | 是否 Manager 独立 | 完成后价值 | |---:|---|---|---|---| | 1 | 增加 sub 模式字段 | P0 | 是 | 已完成:明确任务组织方式,支撑瀑布/敏捷状态展示 | -| 2 | 打通 HeicodeTask 到 Agnet deployment 的 Manager 桥接 | P0 | 是 | 已完成:任务可生成 deployment draft | -| 3 | 增加用户态 Agnet deployment API | P0 | 是 | 已完成:普通用户可以在自己资源范围内创建/查看/停止 deployment | -| 4 | 统一 `/api/swarms` 与 `/api/agnet/deployments` 边界 | P0 | 是 | 已完成:为后续蜂群联调留稳定 adapter | +| 2 | 打通 HeicodeTask 到 Agent deployment 的 Manager 桥接 | P0 | 是 | 已完成:任务可生成 deployment draft | +| 3 | 增加用户态 Agent deployment API | P0 | 是 | 已完成:普通用户可以在自己资源范围内创建/查看/停止 deployment | +| 4 | 统一 `/api/swarms` 与 `/api/agent/deployments` 边界 | P0 | 是 | 已完成:为后续蜂群联调留稳定 adapter | | 5 | 建 callback 接收端骨架 | P0 | 是 | 已完成:先接住事件、artifact、审批请求、timeline | | 6 | 建 artifact 数据模型和 API | P0 | 是 | 已完成:交付物/产物摘要落库并可展示 | | 7 | 增加回调幂等和签名/服务身份校验骨架 | P1 | 是 | 已完成:重复回调不重复写入,支持 token/HMAC | @@ -57,10 +57,10 @@ | 项 | 内容 | |---|---| | 目标 | Manager 能记录任务或部署计划采用 `waterfall` / `agile` 哪种组织方式 | -| 修改文件 | `heicode/model/agnet_deployment.go`、`heicode/controller/agnet_control_plane.go`、`heicode/web/default/src/features/agnet-console/api.ts`、`heicode/web/default/src/features/agnet-console/create-agnet-deployment-sheet.tsx` | +| 修改文件 | `heicode/model/agent_deployment.go`、`heicode/controller/agent_control_plane.go`、`heicode/web/default/src/features/agent-console/api.ts`、`heicode/web/default/src/features/agent-console/create-agent-deployment-sheet.tsx` | | 建议字段 | `sub_mode`,枚举:`waterfall`、`agile`,默认 `agile` | | 验收 | 创建 deployment 后 DB、API response、前端详情都能看到 `sub_mode` | -| 测试 | `go test ./controller -run 'TestAgnet.*SubMode|TestAgnetDeployment'`;`cd web/default && bun run typecheck` | +| 测试 | `go test ./controller -run 'TestAgent.*SubMode|TestAgentDeployment'`;`cd web/default && bun run typecheck` | 验收标准: @@ -68,13 +68,13 @@ - 旧数据无字段时默认按 `agile` 展示。 - 不能把 `sub_mode` 当成蜂群 Runtime 流程,只表示 Heicode 任务组织方式。 -### 任务 2:HeicodeTask 到 Agnet deployment 桥接 +### 任务 2:HeicodeTask 到 Agent deployment 桥接 | 项 | 内容 | |---|---| -| 目标 | Manager 能从 HeicodeTask 的任务卡生成本地 Agnet deployment payload | -| 修改文件 | `heicode/controller/agnet_control_plane.go`、新增 `heicode/controller/agnet_task_bridge.go`、`heicode/web/default/src/lib/heicode-mcp.ts`、`heicode/web/default/src/features/tasks/task-card-view.tsx` | -| 新增 API | `POST /api/agnet/tasks/:task_id/deployment-draft` 或等价 user-scoped endpoint | +| 目标 | Manager 能从 HeicodeTask 的任务卡生成本地 Agent deployment payload | +| 修改文件 | `heicode/controller/agent_control_plane.go`、新增 `heicode/controller/agent_task_bridge.go`、`heicode/web/default/src/lib/heicode-mcp.ts`、`heicode/web/default/src/features/tasks/task-card-view.tsx` | +| 新增 API | `POST /api/agent/tasks/:task_id/deployment-draft` 或等价 user-scoped endpoint | | 输入 | task id、sub_mode、预算、资源范围、角色模板 | | 输出 | deployment draft 或创建后的 `deployment_id` | | 测试 | 新增 controller 单测;前端 typecheck | @@ -85,14 +85,14 @@ - draft 中不能包含明文密钥,只能出现 `secret_ref`。 - 找不到 task 或资源授权不足时返回明确错误。 -### 任务 3:用户态 Agnet deployment API +### 任务 3:用户态 Agent deployment API | 项 | 内容 | |---|---| | 目标 | 普通用户可以创建、查询、停止自己资源范围内的 deployment | -| 修改文件 | `heicode/router/api-router.go`、`heicode/controller/agnet_control_plane.go`、`heicode/controller/agnet_control_plane_test.go` | -| 当前问题 | `/api/agnet/deployments` 走 `AdminAuth` | -| 新增建议 | 保留 admin route;新增 user route:`/api/agnet/user/deployments` 或在同一路由中按 user scope 限制 | +| 修改文件 | `heicode/router/api-router.go`、`heicode/controller/agent_control_plane.go`、`heicode/controller/agent_control_plane_test.go` | +| 当前问题 | `/api/agent/deployments` 走 `AdminAuth` | +| 新增建议 | 保留 admin route;新增 user route:`/api/agent/user/deployments` 或在同一路由中按 user scope 限制 | | 测试 | 普通用户创建成功;越权查询别人 deployment 失败;停止别人 deployment 失败 | 验收标准: @@ -102,13 +102,13 @@ - 请求体伪造别人 `user_context.user_id` 必须被覆盖或拒绝。 - resource grant 必须属于当前用户。 -### 任务 4:统一 `/api/swarms` 与 `/api/agnet/deployments` 边界 +### 任务 4:统一 `/api/swarms` 与 `/api/agent/deployments` 边界 | 项 | 内容 | |---|---| | 目标 | Manager 内部形成生产蜂群接口 adapter,不再让调用方混淆两个口径 | -| 修改文件 | 新增 `heicode/controller/agnet_swarm_adapter.go` 或 `heicode/service/agnet_swarm_adapter.go`,更新 `docs/integration/agnet-platform-request-contract.md` | -| 当前现实 | 本地已有 `/api/agnet/deployments`,蜂群资料包目标接口是 `/api/swarms` | +| 修改文件 | 新增 `heicode/controller/agent_swarm_adapter.go` 或 `heicode/service/agent_swarm_adapter.go`,更新 `docs/integration/agent-platform-request-contract.md` | +| 当前现实 | 本地已有 `/api/agent/deployments`,蜂群资料包目标接口是 `/api/swarms` | | 独立做法 | 先实现 Manager 内部 adapter 和统一 DTO,真实外呼先留配置开关,默认走本地 control-plane | | 测试 | adapter 单测验证 payload 字段、`secret_ref`、correlation_id、sub_mode | @@ -123,8 +123,8 @@ | 项 | 内容 | |---|---| | 目标 | Manager 先具备接收蜂群平台回调的 API 和落库能力 | -| 修改文件 | `heicode/model/agnet_callback.go`、`heicode/controller/agnet_callback.go`、`heicode/router/api-router.go` | -| 新增接口 | `POST /api/agnet/callbacks/swarm-events` | +| 修改文件 | `heicode/model/agent_callback.go`、`heicode/controller/agent_callback.go`、`heicode/router/api-router.go` | +| 新增接口 | `POST /api/agent/callbacks/swarm-events` | | 独立能力 | 本地模拟 payload 可保存、去重、查询;已补普通 sub 任务事件 schema 校验 | | 测试 | controller 单测覆盖正常保存、重复 event_id 幂等、明文密钥拒绝、handoff 缺字段拒绝 | @@ -140,7 +140,7 @@ | 项 | 内容 | |---|---| | 目标 | Manager 保存和展示 artifact 摘要,不等 Runtime 真实输出 | -| 修改文件 | 新增 `heicode/model/agnet_artifact.go`、`heicode/controller/agnet_artifact.go`、`heicode/web/default/src/features/agnet-console/api.ts`、`heicode/web/default/src/features/agnet-console/pages.tsx` | +| 修改文件 | 新增 `heicode/model/agent_artifact.go`、`heicode/controller/agent_artifact.go`、`heicode/web/default/src/features/agent-console/api.ts`、`heicode/web/default/src/features/agent-console/pages.tsx` | | 字段 | `artifact_id`、`deployment_id`、`task_id`、`correlation_id`、`artifact_type`、`title`、`summary`、`uri`、`checksum`、`metadata_json`、`created_at` | | 测试 | model/controller 单测;前端 typecheck | @@ -155,8 +155,8 @@ | 项 | 内容 | |---|---| | 目标 | 生产联调前先有幂等和认证形状 | -| 修改文件 | `heicode/controller/agnet_callback.go`、新增 `heicode/middleware/agnet_callback_auth.go` | -| 机制 | `X-Request-Id`、`X-Correlation-Id`、`Idempotency-Key`、可选 `X-Agnet-Signature` | +| 修改文件 | `heicode/controller/agent_callback.go`、新增 `heicode/middleware/agent_callback_auth.go` | +| 机制 | `X-Request-Id`、`X-Correlation-Id`、`Idempotency-Key`、可选 `X-Agent-Signature` | | 当前阶段 | 可以先用配置开关和本地测试 token,不接真实 Key Vault service token | | 测试 | 缺少服务 token 时拒绝;重复 key 不重复写入 | @@ -170,8 +170,8 @@ | 项 | 内容 | |---|---| -| 目标 | 替换当前 `agnetSnapshots` 内存 map | -| 修改文件 | 新增 `heicode/model/agnet_sk_snapshot.go`,修改 `heicode/controller/agnet_control_plane.go` | +| 目标 | 替换当前 `agentSnapshots` 内存 map | +| 修改文件 | 新增 `heicode/model/agent_sk_snapshot.go`,修改 `heicode/controller/agent_control_plane.go` | | 当前问题 | 容器重启后 `/sk-snapshots` 丢失 | | 测试 | 创建 snapshot 后清空内存,再从 DB 查询仍存在 | @@ -186,9 +186,9 @@ | 项 | 内容 | |---|---| | 目标 | 按 task/deployment/correlation_id 聚合 Manager 已有记录 | -| 修改文件 | `heicode/controller/agnet_control_plane.go`、`heicode/model/agnet_audit.go`、`heicode/web/default/src/features/agnet-console/pages.tsx` | +| 修改文件 | `heicode/controller/agent_control_plane.go`、`heicode/model/agent_audit.go`、`heicode/web/default/src/features/agent-console/pages.tsx` | | 聚合内容 | deployment、audit events、approvals、leases、artifacts、callbacks、resource grants | -| 新增接口 | `GET /api/agnet/tasks/:task_id/timeline` 或 `GET /api/agnet/deployments/:id/timeline` | +| 新增接口 | `GET /api/agent/tasks/:task_id/timeline` 或 `GET /api/agent/deployments/:id/timeline` | | 测试 | 同一 correlation_id 下能聚合多类事件 | 验收标准: @@ -202,10 +202,10 @@ | 项 | 内容 | |---|---| | 目标 | 在蜂群 Runtime 未接入前,Manager 能用模拟事件自测完整链路 | -| 修改文件 | `heicode/controller/agnet_callback.go`、`heicode/router/api-router.go`、可选新增 `heicode/controller/agnet_smoke.go` | -| 接口建议 | admin-only `POST /api/agnet/dev/simulate-run` | +| 修改文件 | `heicode/controller/agent_callback.go`、`heicode/router/api-router.go`、可选新增 `heicode/controller/agent_smoke.go` | +| 接口建议 | admin-only `POST /api/agent/dev/simulate-run` | | 生成内容 | deployment accepted、task.created、task.claimed、task.heartbeat、task.blocked、handoff.requested、handoff.completed、task.retried、task.completed、artifact.created、approval.requested、timeline.updated | -| 测试 | 单测验证模拟后 timeline/artifacts/audit 可查;`scripts/agnet_sub_mode_smoke.py` 可跑生产健康和 deployment timeline 冒烟 | +| 测试 | 单测验证模拟后 timeline/artifacts/audit 可查;`scripts/agent_sub_mode_smoke.py` 可跑生产健康和 deployment timeline 冒烟 | 验收标准: @@ -219,7 +219,7 @@ | 项 | 内容 | |---|---| | 目标 | 用户能看到 Manager 自己可提供的闭环信息 | -| 修改文件 | `heicode/web/default/src/features/agnet-console/pages.tsx`、`api.ts`、必要时新增组件 | +| 修改文件 | `heicode/web/default/src/features/agent-console/pages.tsx`、`api.ts`、必要时新增组件 | | 展示内容 | sub_mode、deployment 来源 task、callbacks、artifacts、timeline、SK snapshots 持久化状态 | | 测试 | `cd heicode/web/default && bun run typecheck`;本地页面点击冒烟 | @@ -263,7 +263,7 @@ |---|---| | 目标 | 桌面客户端调用 Manager 的 sub 流程接口时,支持与模型调用一致的 V2 应用层 body 加密;浏览器后台页面继续兼容普通 HTTPS JSON | | 修改文件 | `docs/integration/heicode-desktop-sub-agile-api.md`、`docs/heicode-runtime-auth-newapi-secret-design.md`、必要时补充 `heicode/middleware` 或客户端 SDK 加解密模块 | -| 当前现实 | `/api/agnet/user/*` 与 `/api/heicode-auth/*` 已支持 `Content-Encoding: heicode-aead-v1`;Manager 会先解密并校验设备签名,再把明文 JSON 交给业务处理或代理给上游 | +| 当前现实 | `/api/agent/user/*` 与 `/api/heicode-auth/*` 已支持 `Content-Encoding: heicode-aead-v1`;Manager 会先解密并校验设备签名,再把明文 JSON 交给业务处理或代理给上游 | | 安全红线 | 请求体、回调、日志、artifact metadata、timeline 中禁止出现明文密码、Token、私钥、连接串、云 access key、模型 key;长期凭证只传 `secret_ref` | | 后续判断 | 桌面客户端需要在 sub 流程调用中复用模型调用的 encryptedFetch/V2 签名能力;服务端保留未加密 JSON 只用于 Manager Web 控制台兼容 | | 测试 | 单测覆盖 V2 malformed 请求拒绝、普通代理请求兼容;生产需用真实已配对设备发起加密 sub 请求冒烟 | @@ -280,12 +280,12 @@ | 项 | 内容 | |---|---| | 目标 | Manager 不依赖客户端或 Runtime 改接口,也能把“callback 已到”和“Runtime/Agent 是否真的产出交付物”分开展示 | -| 修改文件 | `heicode/controller/agnet_runtime_client.go`、`heicode/router/api-router.go`、`heicode/web/default/src/features/agnet-console/api.ts`、`heicode/web/default/src/features/agnet-console/pages.tsx` | -| 新增 API | `GET /api/agnet/user/deployments/:deployment_id/runtime-diagnostics` | -| 诊断来源 | 用 deployment 记录里的 `runtime_swarm_id` / `runtime_deployment_id` 查询 Runtime status;默认路径 `/api/swarms/{swarm_id}/status`,可用 `AGNET_RUNTIME_STATUS_PATH` / `SWARM_RUNTIME_STATUS_PATH` 配置 | +| 修改文件 | `heicode/controller/agent_runtime_client.go`、`heicode/router/api-router.go`、`heicode/web/default/src/features/agent-console/api.ts`、`heicode/web/default/src/features/agent-console/pages.tsx` | +| 新增 API | `GET /api/agent/user/deployments/:deployment_id/runtime-diagnostics` | +| 诊断来源 | 用 deployment 记录里的 `runtime_swarm_id` / `runtime_deployment_id` 查询 Runtime status;默认路径 `/api/swarms/{swarm_id}/status`,可用 `AGENT_RUNTIME_STATUS_PATH` / `SWARM_RUNTIME_STATUS_PATH` 配置 | | 普通 sub / 蜂群边界 | 返回 `runtime_mode`,页面分别显示“普通 sub 模式”或“蜂群模式”,不把二者合并成一个流程 | | 已识别异常 | Runtime agent failed、completed 但存在 failed agents、只有 `Runtime execution summary` 兜底产物、模型 token 用量为 0、Runtime 状态查询失败 | -| 测试 | `go test ./controller -run TestAgnetRuntimeDiagnosticsWarnsOnCompletedRuntimeWithFailedAgents -count=1`;前端 `bun run build:check` | +| 测试 | `go test ./controller -run TestAgentRuntimeDiagnosticsWarnsOnCompletedRuntimeWithFailedAgents -count=1`;前端 `bun run build:check` | 验收标准: @@ -299,11 +299,11 @@ | 项 | 内容 | |---|---| | 目标 | 对齐 Agent Manager v2.1.10 的产物获取流程:Manager / 前端先查 artifact 列表,再通过用户态 content 代理接口获取完整文件 | -| 修改文件 | `heicode/model/agnet_artifact.go`、`heicode/controller/agnet_callback.go`、`heicode/controller/agnet_runtime_client.go`、`heicode/router/api-router.go`、`heicode/web/default/src/features/agnet-console/pages.tsx` | -| 新增 API | `GET /api/agnet/user/deployments/:deployment_id/artifacts/:artifact_id/content` | -| Runtime 目标路径 | 默认 `GET /api/swarms/{swarm_id}/artifacts/{artifact_id}/content`,可用 `AGNET_RUNTIME_ARTIFACT_CONTENT_PATH` / `SWARM_RUNTIME_ARTIFACT_CONTENT_PATH` 配置 | +| 修改文件 | `heicode/model/agent_artifact.go`、`heicode/controller/agent_callback.go`、`heicode/controller/agent_runtime_client.go`、`heicode/router/api-router.go`、`heicode/web/default/src/features/agent-console/pages.tsx` | +| 新增 API | `GET /api/agent/user/deployments/:deployment_id/artifacts/:artifact_id/content` | +| Runtime 目标路径 | 默认 `GET /api/swarms/{swarm_id}/artifacts/{artifact_id}/content`,可用 `AGENT_RUNTIME_ARTIFACT_CONTENT_PATH` / `SWARM_RUNTIME_ARTIFACT_CONTENT_PATH` 配置 | | 安全边界 | Manager 先校验当前用户拥有 deployment,再校验 artifact 属于该 deployment;不暴露 Azure Blob 凭据、SAS URL 或 Runtime 内网信息 | -| 测试 | `go test ./controller -run TestAgnetArtifactContentProxiesRuntimeContent -count=1` | +| 测试 | `go test ./controller -run TestAgentArtifactContentProxiesRuntimeContent -count=1` | 验收标准: @@ -319,7 +319,7 @@ | 顺序 | 任务 | |---:|---| | 1 | sub 模式字段 | -| 2 | 用户态 Agnet deployment API | +| 2 | 用户态 Agent deployment API | | 3 | HeicodeTask 到 deployment 桥接 | | 4 | 本地模拟蜂群事件冒烟入口 | @@ -329,7 +329,7 @@ | 顺序 | 任务 | |---:|---| -| 1 | `/api/swarms` 与 `/api/agnet/deployments` adapter | +| 1 | `/api/swarms` 与 `/api/agent/deployments` adapter | | 2 | callback 接收端骨架 | | 3 | artifact 数据模型和 API | | 4 | 回调幂等和服务身份校验骨架 | diff --git a/docs/heicode-manager-sub-swarm-progress-checklist.md b/docs/heicode-manager-sub-swarm-progress-checklist.md index 82e20ee..10aafed 100644 --- a/docs/heicode-manager-sub-swarm-progress-checklist.md +++ b/docs/heicode-manager-sub-swarm-progress-checklist.md @@ -10,14 +10,14 @@ |---|---| | `http://gitee.ath.cx:3000/taijibaga/fengqun/src/branch/main/docs` | 蜂群设计资料包,定义目标驱动蜂群、任务图、claim、heartbeat、handoff、artifact、审批、审计和三方分工 | | `http://gitee.ath.cx:3000/taijibaga/HeiCode-Swarm` | 蜂群项目实现资料,当前 Orchestrator/Agent/Redis/K8s/桌面演示客户端的实际结构 | -| `docs/product-package/07-integration-boundaries.md` | Heicode、Manager、Agnet 平台、CodeGW、Azure Key Vault 的边界 | +| `docs/product-package/07-integration-boundaries.md` | Heicode、Manager、Agent 平台、CodeGW、Azure Key Vault 的边界 | | `docs/integration/heicode-desktop-sub-agile-api.md` | Heicode 桌面客户端接 Manager 的普通 sub 敏捷流程 | | `docs/integration/Heicode-Manager-PayPal支付接入与计费关系说明.md` | PayPal 收款、Heicode 余额/订阅、NewAPI 模型扣费和 Agent 运行费用边界 | | 当前仓库 `heicode/` 代码 | Manager 端实际实现核查 | ## 一、核心边界 -蜂群模式不是普通 sub 敏捷/瀑布本身。普通 sub 是 Heicode 的任务组织方式;蜂群是 Agnet/Swarm Runtime 的执行方式。 +蜂群模式不是普通 sub 敏捷/瀑布本身。普通 sub 是 Heicode 的任务组织方式;蜂群是 Agent/Swarm Runtime 的执行方式。 | 模式 | Manager 当前职责 | Runtime / Agent Manager 当前职责 | 不能混淆的点 | |---|---|---|---| @@ -28,7 +28,7 @@ |---|---|---|---| | Heicode 桌面客户端 | 用户主体验 | 输入目标、持续补充需求、查看反馈、审批高危操作、接收交付结果 | 直接配置 AKS、模型供应商、完整蜂群 payload | | Heicode Manager | 控制面、记录面和用户侧账本入口 | 资源绑定、`secret_ref`、权限清单、生成启动请求、记录 deployment/swarm 映射、回调、artifact、timeline、审批、审计、余额/订阅展示 | 替代客户端做主开发对话,替代 Runtime 执行任务,或把 PayPal 收款当成模型扣费链路 | -| HeiCode-Swarm / Agnet Runtime | 执行层 | 创建 Swarm Run、任务图、Agent 编队、claim、heartbeat、handoff、执行、结果回传、真实运行 usage 回传 | 保存长期明文密钥,直接暴露给普通用户,或自行决定用户账本扣费 | +| HeiCode-Swarm / Agent Runtime | 执行层 | 创建 Swarm Run、任务图、Agent 编队、claim、heartbeat、handoff、执行、结果回传、真实运行 usage 回传 | 保存长期明文密钥,直接暴露给普通用户,或自行决定用户账本扣费 | ## 二、目标调用链 @@ -38,7 +38,7 @@ Heicode 桌面客户端 - V2 加密请求 body - task/deployment draft - resource_grants / secret_ref / budget / approval_policy - -> Agnet Runtime 或 HeiCode-Swarm + -> Agent Runtime 或 HeiCode-Swarm - POST /api/swarms 或兼容创建入口 - 返回 swarm_id / runtime_deployment_id <- Runtime callback @@ -54,24 +54,24 @@ Heicode 桌面客户端 | 能力 | 当前状态 | 代码证据 | |---|---|---| -| sub 模式字段 | 已支持 `sub_mode`,默认 `agile`,校验 `agile/waterfall` | `heicode/controller/agnet_control_plane.go`、`heicode/model/agnet_deployment.go` | -| 用户态 deployment | 已有 `/api/agnet/user/deployments` 创建、查询、停止、日志、事件、指标、artifact、SK snapshot、timeline | `heicode/router/api-router.go` | -| 任务到 deployment draft | 已有 `/api/agnet/user/tasks/:task_id/deployment-draft` | `heicode/controller/agnet_task_bridge.go` | -| `/api/swarms` 兼容入口 | 已有用户态 `POST /api/swarms`,内部走 Manager deployment 创建,并作为 adapter source 记录 | `heicode/router/api-router.go`、`AgnetCreateUserSwarm` | -| Runtime 创建桥接 | 已能按配置调用 Runtime 创建接口,默认路径 `/api/agnet/deployments`,可用环境变量改为蜂群创建路径 | `heicode/controller/agnet_runtime_client.go` | -| Runtime stop 桥接 | 已能在停止 Manager deployment 时调用 Runtime stop | `heicode/controller/agnet_runtime_client.go` | -| Runtime 状态诊断 | 已新增用户态只读诊断接口,按 `runtime_mode` 区分普通 sub / 蜂群,查询 Runtime status 并识别 failed agent、兜底摘要 artifact、零 token 用量等异常 | `heicode/controller/agnet_runtime_client.go`、`AgnetGetUserDeploymentRuntimeDiagnostics` | -| callback 接收 | 已有 `POST /api/agnet/callbacks/swarm-events` | `heicode/controller/agnet_callback.go` | -| callback 鉴权 | 支持 `X-Agnet-Service-Token` 和 HMAC 签名校验,并可从 Key Vault ref 读取签名密钥 | `heicode/controller/agnet_callback.go` | -| callback 幂等 | `event_id` / `idempotency_key` 去重,重复回调返回成功但不重复写 | `heicode/model/agnet_callback.go` | -| artifact 落库 | `artifact.created` 可生成 artifact 记录,支持用户态列表查询 | `heicode/model/agnet_artifact.go`、`AgnetListUserDeploymentArtifacts` | -| artifact 完整内容代理 | 已新增用户态 content 下载接口,Manager 校验 deployment/artifact 权限后代理 Runtime content 接口读取完整产物 | `AgnetGetUserDeploymentArtifactContent`、`callAgnetRuntimeArtifactContent` | -| approval 回调 | `approval.requested` 可转成 Manager 审批记录 | `heicode/controller/agnet_callback.go` | -| 审批结果回传 Runtime | 用户 approve/reject 后,Manager 可按配置 POST 回 Runtime approval decision,且不发送 `secret_ref` | `heicode/controller/agnet_approval.go`、`heicode/controller/agnet_runtime_client.go` | -| timeline 聚合 | 用户态 timeline 聚合 audit、callbacks、artifacts、sk_snapshots | `AgnetGetUserDeploymentTimeline` | -| SK snapshot 持久化 | 已有 `agnet_sk_snapshots` 模型和列表查询 | `heicode/model/agnet_sk_snapshot.go` | -| 本地模拟事件 | 已有用户态 `simulate-events`;默认模拟会写入 callback、artifact、approval、timeline 记录,用于 Manager 自测展示链路和脱敏检查 | `AgnetSimulateUserDeploymentEvents` | -| V2 body 加密 | `/api/agnet/user/*`、`/api/heicode-auth/*`、`/api/swarms` 已按同一套 V2 设备签名和 body 加密路径设计;未加密 Web 控制台仍兼容 session + `New-Api-User` | `heicode/middleware/auth.go`、`heicode/router/api-router.go` | +| sub 模式字段 | 已支持 `sub_mode`,默认 `agile`,校验 `agile/waterfall` | `heicode/controller/agent_control_plane.go`、`heicode/model/agent_deployment.go` | +| 用户态 deployment | 已有 `/api/agent/user/deployments` 创建、查询、停止、日志、事件、指标、artifact、SK snapshot、timeline | `heicode/router/api-router.go` | +| 任务到 deployment draft | 已有 `/api/agent/user/tasks/:task_id/deployment-draft` | `heicode/controller/agent_task_bridge.go` | +| `/api/swarms` 兼容入口 | 已有用户态 `POST /api/swarms`,内部走 Manager deployment 创建,并作为 adapter source 记录 | `heicode/router/api-router.go`、`AgentCreateUserSwarm` | +| Runtime 创建桥接 | 已能按配置调用 Runtime 创建接口,默认路径 `/api/agent/deployments`,可用环境变量改为蜂群创建路径 | `heicode/controller/agent_runtime_client.go` | +| Runtime stop 桥接 | 已能在停止 Manager deployment 时调用 Runtime stop | `heicode/controller/agent_runtime_client.go` | +| Runtime 状态诊断 | 已新增用户态只读诊断接口,按 `runtime_mode` 区分普通 sub / 蜂群,查询 Runtime status 并识别 failed agent、兜底摘要 artifact、零 token 用量等异常 | `heicode/controller/agent_runtime_client.go`、`AgentGetUserDeploymentRuntimeDiagnostics` | +| callback 接收 | 已有 `POST /api/agent/callbacks/swarm-events` | `heicode/controller/agent_callback.go` | +| callback 鉴权 | 支持 `X-Agent-Service-Token` 和 HMAC 签名校验,并可从 Key Vault ref 读取签名密钥 | `heicode/controller/agent_callback.go` | +| callback 幂等 | `event_id` / `idempotency_key` 去重,重复回调返回成功但不重复写 | `heicode/model/agent_callback.go` | +| artifact 落库 | `artifact.created` 可生成 artifact 记录,支持用户态列表查询 | `heicode/model/agent_artifact.go`、`AgentListUserDeploymentArtifacts` | +| artifact 完整内容代理 | 已新增用户态 content 下载接口,Manager 校验 deployment/artifact 权限后代理 Runtime content 接口读取完整产物 | `AgentGetUserDeploymentArtifactContent`、`callAgentRuntimeArtifactContent` | +| approval 回调 | `approval.requested` 可转成 Manager 审批记录 | `heicode/controller/agent_callback.go` | +| 审批结果回传 Runtime | 用户 approve/reject 后,Manager 可按配置 POST 回 Runtime approval decision,且不发送 `secret_ref` | `heicode/controller/agent_approval.go`、`heicode/controller/agent_runtime_client.go` | +| timeline 聚合 | 用户态 timeline 聚合 audit、callbacks、artifacts、sk_snapshots | `AgentGetUserDeploymentTimeline` | +| SK snapshot 持久化 | 已有 `agent_sk_snapshots` 模型和列表查询 | `heicode/model/agent_sk_snapshot.go` | +| 本地模拟事件 | 已有用户态 `simulate-events`;默认模拟会写入 callback、artifact、approval、timeline 记录,用于 Manager 自测展示链路和脱敏检查 | `AgentSimulateUserDeploymentEvents` | +| V2 body 加密 | `/api/agent/user/*`、`/api/heicode-auth/*`、`/api/swarms` 已按同一套 V2 设备签名和 body 加密路径设计;未加密 Web 控制台仍兼容 session + `New-Api-User` | `heicode/middleware/auth.go`、`heicode/router/api-router.go` | | 生产普通 sub 烟测记录 | 2026-05-31 已用生产 Manager 入口完成真实普通 sub 复核:`dep_1d6d66896cc6` -> `swm_03995f7c7a27`,`gpt-5.4`,`tokens_used=2682`,`newapi_request_id=chatcmpl-DlbZce3VZnsv5DptBILFoiRYy5ZHN`,业务 `code_patch` artifact 可通过 Manager content 接口下载 | `docs/integration/heicode-desktop-sub-agile-api.md` | | PayPal/计费边界文档 | 已明确 PayPal 只是收款渠道;模型调用仍走 Heicode/NewAPI 的钱包或订阅额度;Agent 运行费用目前只有预算字段,真实收费需 Runtime usage 回传 | `docs/integration/Heicode-Manager-PayPal支付接入与计费关系说明.md` | @@ -81,8 +81,8 @@ Heicode 桌面客户端 |---|---|---|---|---| | P0 | 把 `/api/swarms` adapter 文档化并固定字段 | 已完成:`docs/integration/蜂群模式-AgentManager对接任务清单.md` 和 `docs/integration/普通sub敏捷模式-AgentManager对接任务清单.md` 已写清 Runtime create、callback、source、`deployment_id <-> swarm_id` 映射 | 是 | 文档可直接发给 Agent Manager / 蜂群侧核对 | | P0 | 增加 Swarm Run 显示字段 | 已完成:用户态 deployment 详情展示 `runtime_deployment_id`、`runtime_swarm_id`、`runtime_state`,并在相关记录里展示 source | 是 | 用户态 deployment 详情和后台页面能看到 Runtime 映射 | -| P0 | 回调事件类型收敛 | 已完成:`GET /api/agnet/callbacks/swarm-events/schema` 输出事件类型、分类和必填字段;callback 接收端按 schema 校验关键 task/handoff/artifact/approval 字段 | 是 | `task.created/claimed/running/completed/failed/handoff/approval/artifact` 都有 schema | -| P0 | Runtime 联调配置模板 | 已完成:两份 Agent Manager 对接任务清单已写清 `AGNET_RUNTIME_*`、callback URL、service token/HMAC 方式和验收步骤 | 是 | 蜂群项目按模板能调用 Manager callback | +| P0 | 回调事件类型收敛 | 已完成:`GET /api/agent/callbacks/swarm-events/schema` 输出事件类型、分类和必填字段;callback 接收端按 schema 校验关键 task/handoff/artifact/approval 字段 | 是 | `task.created/claimed/running/completed/failed/handoff/approval/artifact` 都有 schema | +| P0 | Runtime 联调配置模板 | 已完成:两份 Agent Manager 对接任务清单已写清 `AGENT_RUNTIME_*`、callback URL、service token/HMAC 方式和验收步骤 | 是 | 蜂群项目按模板能调用 Manager callback | | P1 | 审批结果回传 Runtime 联调 | Manager adapter 已有;仍需要 Runtime 提供接收接口并验证状态继续/停止 | 需要 Runtime 接口 | 审批通过/拒绝后 Runtime 状态能继续或停止 | | P1 | Artifact 展示优化 | Manager 端已完成:页面展示 artifact 类型、摘要和 URI;真实 `code_patch/document/test_report/deployment_manifest` 仍需 Runtime 输出 | 需要 Runtime 数据 | artifact 页面/详情能按类型展示摘要和链接 | | P1 | Artifact 完整内容下载 | 已完成:用户态 `/artifacts/{artifact_id}/content` 代理 Runtime content,页面提供下载入口 | 是 | artifact 属于当前用户 deployment 才能下载,响应透传 Runtime 文件内容 | @@ -91,8 +91,8 @@ Heicode 桌面客户端 | P1 | 日志/指标真实来源标识 | 已完成:logs/metrics API 返回 `data_source`、`runtime_source`,当前明确是 Manager control-plane / estimated,不伪装 Runtime 真实指标 | 需要 Runtime 数据 | 页面和 API 响应能区分来源 | | P1 | Agent 运行费用口径收敛 | 已完成文档口径:`budget.max_tokens/max_cost_usd/max_duration_sec` 是预算约束,不等于真实扣费账本;真实收费必须依赖 Runtime 回传 usage | Manager 已完成文档,真实数据需 Runtime | 页面/文档不把 estimated budget 说成真实扣费 | | P1 | 高危审批客户端联动文档 | 已完成:`docs/integration/heicode-desktop-sub-agile-api.md` 已包含 approval 查询、approve/reject、awaiting_approval 流程 | 是 | 客户端文档补齐 approval flow | -| P2 | 蜂群模式验收脚本 | 已完成:`scripts/agnet_sub_mode_smoke.py` 支持 schema 检查、生产健康检查、可选 simulate-events、可选真实 callback smoke | 是 | 本地/生产能跑出 callback、artifact、approval、timeline 可见 | -| P2 | 生产 schema / 认证链路复测 | 本地代码和测试已覆盖;生产公开 `GET /api/agnet/callbacks/swarm-events/schema` 当前返回 404,认证接口需有效登录态或后台 token 才能测 | 是,部署后复测 | 生产 schema 返回 200,用户态/后台态 smoke 能拿到真实数据 | +| P2 | 蜂群模式验收脚本 | 已完成:`scripts/agent_sub_mode_smoke.py` 支持 schema 检查、生产健康检查、可选 simulate-events、可选真实 callback smoke | 是 | 本地/生产能跑出 callback、artifact、approval、timeline 可见 | +| P2 | 生产 schema / 认证链路复测 | 本地代码和测试已覆盖;生产公开 `GET /api/agent/callbacks/swarm-events/schema` 当前返回 404,认证接口需有效登录态或后台 token 才能测 | 是,部署后复测 | 生产 schema 返回 200,用户态/后台态 smoke 能拿到真实数据 | ## 五、需要蜂群项目配合的事项 diff --git a/docs/heicode-manager-swarm-gap-analysis.md b/docs/heicode-manager-swarm-gap-analysis.md index 6e663af..1bad7c6 100644 --- a/docs/heicode-manager-swarm-gap-analysis.md +++ b/docs/heicode-manager-swarm-gap-analysis.md @@ -17,7 +17,7 @@ Manager 端已经具备蜂群联调需要的控制面基础:用户态 deployme | Manager 是受控入口,不暴露完整 payload 给普通用户 | 用户态 draft/create 已有,可由任务卡生成 plan | 部分满足 | 页面还需要更弱化底层 payload,突出启动摘要和状态 | Manager | | Manager 调用 `POST /api/swarms` 创建 Swarm Run | Manager 已有 `/api/swarms` adapter,Runtime create path 可配置 | 部分满足 | 是否真实创建 Swarm Run 取决于蜂群 Runtime 是否提供生产接口 | 蜂群 + Manager | | 保存 `swarm_id`、状态、请求摘要、`correlation_id` | 模型已有 `runtime_swarm_id`、`runtime_state`、payload JSON、correlation metadata | 基本满足 | 真实 `swarm_id` 需要 Runtime 返回 | 蜂群 | -| 接收 `swarm-events` 回调 | 已有 `POST /api/agnet/callbacks/swarm-events` | 满足接收能力 | 还缺真实 Runtime 持续回调 | 蜂群 | +| 接收 `swarm-events` 回调 | 已有 `POST /api/agent/callbacks/swarm-events` | 满足接收能力 | 还缺真实 Runtime 持续回调 | 蜂群 | | 重复回调幂等 | 已按 `event_id` / `idempotency_key` 去重 | 满足 | 需要蜂群侧稳定传唯一事件 ID | 蜂群 | | 接收 artifact 回调 | `artifact.created` 可落库并查询 | 满足接收能力 | 真实 artifact schema 和文件/分支引用需 Runtime 输出 | 蜂群 | | 展示 Swarm 状态、事件、产物 | deployment detail/events/artifacts/timeline 已有,页面已强化 runtime/simulated 来源和 artifact 类型/URI | 基本满足 | 真实展示内容仍依赖 Runtime 回调真实数据 | 蜂群 | @@ -85,7 +85,7 @@ desktop-client -> Orchestrator(FastAPI) -> Redis -> Agent Pods | 缺失项 | 当前状态 | 处理建议 | |---|---|---| -| 蜂群验收脚本 | 已新增 `scripts/agnet_sub_mode_smoke.py`,可检查生产 Manager、Agent health 和指定 deployment timeline | 后续按真实 Runtime deployment 固化执行参数 | +| 蜂群验收脚本 | 已新增 `scripts/agent_sub_mode_smoke.py`,可检查生产 Manager、Agent health 和指定 deployment timeline | 后续按真实 Runtime deployment 固化执行参数 | | 页面术语统一 | sub/蜂群容易混淆 | 页面和文档统一:sub 是任务组织,swarm 是执行层 | | 兼容 `HeiCode-Swarm` demo client 的说明 | 容易误认为正式 Heicode 桌面客户端 | 文档明确 demo client 不等于 `cc-haha` 正式客户端 | diff --git a/docs/heicode-runtime-auth-newapi-secret-design.md b/docs/heicode-runtime-auth-newapi-secret-design.md index 501be25..ba6feee 100644 --- a/docs/heicode-runtime-auth-newapi-secret-design.md +++ b/docs/heicode-runtime-auth-newapi-secret-design.md @@ -2,26 +2,26 @@ 日期:2026-05-04 -本文修正 Manager、Agnet 平台、NewAPI 与 Azure Key Vault 之间的运行时边界。若本文与旧文档中 `tenant`、`project` 或平台侧批准描述冲突,以本文为准。 +本文修正 Manager、Agent 平台、NewAPI 与 Azure Key Vault 之间的运行时边界。若本文与旧文档中 `tenant`、`project` 或平台侧批准描述冲突,以本文为准。 ## 一、用户输入在哪里 用户输入发生在 Heicode 的用户侧入口,也就是 Manager/客户端的“想法输入”主流程。 -用户登录后输入目标、需求、约束、绑定的 Git/SK/云资源选择,以及是否批准高危操作。Manager 负责把这些输入整理成 Agnet 平台可执行的 work request: +用户登录后输入目标、需求、约束、绑定的 Git/SK/云资源选择,以及是否批准高危操作。Manager 负责把这些输入整理成 Agent 平台可执行的 work request: - 用户想法和自然语言需求。 - 绑定的 Git 仓库、分支、路径范围和写入权限。 - 绑定的 SK 仓库或技能包。 - 绑定的云资源元数据和允许动作。 -- 子 Agnet 角色、数量、运行模型和预算限制。 +- 子 Agent 角色、数量、运行模型和预算限制。 - 客户端已经确认的高危操作审批结果。 -Manager 不应为了团队开发控制额外发明 `tenant/project` 产品概念。当前团队开发边界优先来自绑定的 Git 仓库、允许路径、分支策略、Agnet 角色和资源授权。 +Manager 不应为了团队开发控制额外发明 `tenant/project` 产品概念。当前团队开发边界优先来自绑定的 Git 仓库、允许路径、分支策略、Agent 角色和资源授权。 -## 二、Manager 用户复用 Agnet 登录体系 +## 二、Manager 用户复用 Agent 登录体系 -Manager User 应复用 Heicode/Agnet 已上线登录体系,不再另建一套独立身份。 +Manager User 应复用 Heicode/Agent 已上线登录体系,不再另建一套独立身份。 参考 [`integration/Heicode-登录接口对接文档.md`](./integration/Heicode-登录接口对接文档.md),登录流程已经提供: @@ -51,7 +51,7 @@ JWT 中也包含: 因此 Manager 的认证设计应是: ```text -用户登录 Heicode/Agnet 认证体系 +用户登录 Heicode/Agent 认证体系 -> Manager 校验 token 并读取 /api/auth/me -> Manager 使用 user.id / sub 作为业务用户 ID -> Manager 使用 channelId 关联 NewAPI 余额、用量和扣费查询 @@ -62,7 +62,7 @@ Manager 可以有本地 user cache,但 canonical user identity 应来自登录 ## 三、高危操作审批与 Azure Key Vault 密钥注入 -高危操作审批只在客户端完成。用户在客户端明确批准后,Manager/Agnet 平台才可以执行对应动作。Agnet 平台不是审批主体,不发起额外审批;它只校验 `approval_id`、审批主体、审批范围、TTL、`risk_level` 和策略是否匹配。 +高危操作审批只在客户端完成。用户在客户端明确批准后,Manager/Agent 平台才可以执行对应动作。Agent 平台不是审批主体,不发起额外审批;它只校验 `approval_id`、审批主体、审批范围、TTL、`risk_level` 和策略是否匹配。 密钥处理边界如下: @@ -71,16 +71,16 @@ Manager 可以有本地 user cache,但 canonical user identity 应来自登录 -> 用户授权或绑定资源 -> Manager 通过 VM Managed Identity 写入 Azure Key Vault -> Manager DB 只保存 secret_ref --> Azure Key Vault 通过 Private Endpoint / 防火墙限制,只允许 Manager/Agnet 受控网络访问 +-> Azure Key Vault 通过 Private Endpoint / 防火墙限制,只允许 Manager/Agent 受控网络访问 高危操作 -> 客户端审批 --> Manager/Agnet 平台按 secret_ref 从 Azure Key Vault 获取或派生短期凭证 --> 短期凭证可注入子 Agnet --> 子 Agnet 完成任务后凭证过期或撤销 +-> Manager/Agent 平台按 secret_ref 从 Azure Key Vault 获取或派生短期凭证 +-> 短期凭证可注入子 Agent +-> 子 Agent 完成任务后凭证过期或撤销 ``` -允许注入子 Agnet 的只能是短期、最小权限、可审计的凭证。长期 Git token、云 access key、SSH 私钥、数据库密码、NewAPI key 原文不得进入 Git、Markdown、普通日志或长期 Agnet 状态。 +允许注入子 Agent 的只能是短期、最小权限、可审计的凭证。长期 Git token、云 access key、SSH 私钥、数据库密码、NewAPI key 原文不得进入 Git、Markdown、普通日志或长期 Agent 状态。 公网入口边界: @@ -93,7 +93,7 @@ Azure Key Vault 不暴露普通公网入口的检查口径: - Key Vault 应优先关闭 Public Network Access,并通过 Private Endpoint 接入 `heicode-vnet`。 - 外部客户端只能通过 Manager 的认证、资源绑定、审批和 `secret_ref` 管理接口间接操作密钥引用。 -- Manager、Agnet 平台和子 Agnet 访问 Azure Key Vault 时必须走 Private Endpoint / Workload Identity / Managed Identity 绑定。 +- Manager、Agent 平台和子 Agent 访问 Azure Key Vault 时必须走 Private Endpoint / Workload Identity / Managed Identity 绑定。 - 健康检查和联调报告只能证明受控网络访问可用;不得把公网可访问作为验收口径。 短期凭证注入必须满足: @@ -102,7 +102,7 @@ Azure Key Vault 不暴露普通公网入口的检查口径: - 有 `secret_ref` 来源。 - 有 TTL 或明确撤销机制。 - 有资源范围、路径范围、云资源范围或 API 范围限制。 -- 有 Agnet deployment、agent role、user id、操作类型的审计记录。 +- 有 Agent deployment、agent role、user id、操作类型的审计记录。 ## 四、NewAPI 额度与扣费能力 @@ -125,22 +125,22 @@ NewAPI 官方 skill 文档明确支持: 3. Manager 普通用户侧展示应围绕当前登录用户的模型可用性、余额、额度、调用量和调用日志。 4. 扣费映射应优先使用 `channelId`、NewAPI user、NewAPI token 或 NewAPI group。 5. 若未来需要团队、组织、项目维度账本,应作为独立产品决策重新设计,而不是在当前 Manager 里暗自添加。 -6. 子 Agnet 的运行模型是 Agnet 平台的部署配置,独立于 NewAPI 的用户、Token、Group 扣费映射;NewAPI 不负责决定子 Agnet 使用哪个模型。 +6. 子 Agent 的运行模型是 Agent 平台的部署配置,独立于 NewAPI 的用户、Token、Group 扣费映射;NewAPI 不负责决定子 Agent 使用哪个模型。 推荐映射: -| Heicode/Agnet 字段 | NewAPI 映射 | 用途 | +| Heicode/Agent 字段 | NewAPI 映射 | 用途 | |------|------|------| | `user.id` / JWT `sub` | NewAPI user ref | 标识调用归属用户 | | `channelId` | NewAPI channel/user/group 绑定 | 关联模型渠道、额度或扣费策略 | | 绑定 Git 仓库 | request metadata | 审计某次开发任务来源 | | 预算或用量限制 | token quota 或 group policy | 限制本次任务可消耗额度 | -子 Agnet 角色、运行模型和实例数量应放在 Agnet 平台 deployment/runtime 配置里,不放进 NewAPI 扣费对象里。 +子 Agent 角色、运行模型和实例数量应放在 Agent 平台 deployment/runtime 配置里,不放进 NewAPI 扣费对象里。 -## 五、传给 Agnet 平台的用户与扣费上下文 +## 五、传给 Agent 平台的用户与扣费上下文 -Manager 请求 Agnet 平台部署或执行任务时,应携带登录用户上下文,但不携带真实密钥。 +Manager 请求 Agent 平台部署或执行任务时,应携带登录用户上下文,但不携带真实密钥。 建议传递结构: @@ -161,11 +161,11 @@ Manager 请求 Agnet 平台部署或执行任务时,应携带登录用户上 "quota_ref": "newapi_token_or_group_quota_ref" }, "agent_runtime": { - "platform": "agnet", + "platform": "agent", "agents": [ { "role": "backend", - "model_ref": "agnet_model_profile_backend", + "model_ref": "agent_model_profile_backend", "instance_count": 1 } ] @@ -190,17 +190,17 @@ Manager 请求 Agnet 平台部署或执行任务时,应携带登录用户上 } ``` -这里的 `user_context` 用于确认身份,`billing_context` 用于 NewAPI 余额、用量和扣费映射,`agent_runtime` 用于 Agnet 平台独立选择子 Agnet 模型和实例数量,`work_context` 用于团队开发控制,`secret_context` 只传 `secret_ref` 和审批结果。 +这里的 `user_context` 用于确认身份,`billing_context` 用于 NewAPI 余额、用量和扣费映射,`agent_runtime` 用于 Agent 平台独立选择子 Agent 模型和实例数量,`work_context` 用于团队开发控制,`secret_context` 只传 `secret_ref` 和审批结果。 -Agnet 平台执行时,应把用户、角色、Git 绑定、deployment、Agnet model profile 和 NewAPI 映射写入 metadata 或审计日志。真实 NewAPI key 和 Azure Key Vault 凭证由平台安全通道读取,不进入 Markdown。 +Agent 平台执行时,应把用户、角色、Git 绑定、deployment、Agent model profile 和 NewAPI 映射写入 metadata 或审计日志。真实 NewAPI key 和 Azure Key Vault 凭证由平台安全通道读取,不进入 Markdown。 ## 六、当前主线结论 - 用户输入在 Manager/客户端,不在 NewAPI 后台。 -- Manager User 复用 Heicode/Agnet 登录体系。 +- Manager User 复用 Heicode/Agent 登录体系。 - Manager 不自行发明 tenant/project 作为当前团队或扣费边界。 -- 团队开发控制优先由绑定 Git、允许路径、Agnet 角色、资源授权表达。 -- 子 Agnet 的运行模型由 Agnet 平台独立配置,不和 NewAPI 扣费对象混在一起。 +- 团队开发控制优先由绑定 Git、允许路径、Agent 角色、资源授权表达。 +- 子 Agent 的运行模型由 Agent 平台独立配置,不和 NewAPI 扣费对象混在一起。 - NewAPI 负责模型网关、用户/Token/Group 额度、余额、日志和扣费。 -- Azure Key Vault 负责长期密钥托管,子 Agnet 只拿短期、最小权限、可审计凭证。 -- 高危操作审批发生在客户端,审批结果随任务上下文传给 Manager/Agnet 平台。 +- Azure Key Vault 负责长期密钥托管,子 Agent 只拿短期、最小权限、可审计凭证。 +- 高危操作审批发生在客户端,审批结果随任务上下文传给 Manager/Agent 平台。 diff --git a/docs/heicode.md b/docs/heicode.md index 086376f..cb56daa 100644 --- a/docs/heicode.md +++ b/docs/heicode.md @@ -21,54 +21,54 @@ Heicode 的最终形态是一款覆盖软件生命周期的智能开发 Code 工 | 系统 | 定位 | 负责内容 | |------|------|----------| -| Heicode Manager | SaaS 用户控制台与编排中枢 | 用户输入、资源绑定、权限分配、Agnet 部署、审计、模型与余额展示 | -| Agnet 平台 | 执行与状态平台 | 在 AKS 上部署子 Agnet、运行任务、维护状态、事件、日志和执行元数据 | +| Heicode Manager | SaaS 用户控制台与编排中枢 | 用户输入、资源绑定、权限分配、Agent 部署、审计、模型与余额展示 | +| Agent 平台 | 执行与状态平台 | 在 AKS 上部署子 Agent、运行任务、维护状态、事件、日志和执行元数据 | | NewAPI | 内部模型网关与计费服务 | 模型渠道、模型调用、额度、余额、调用日志;后台不对普通用户开放 | | Secret Store | 凭证保管库 | 保存 Git token、云密钥、SSH key、数据库密码等真实凭证 | -Manager 是用户操作入口;Agnet 是运行时执行层;NewAPI 是模型能力服务;Secret Store 是安全凭证底座。 +Manager 是用户操作入口;Agent 是运行时执行层;NewAPI 是模型能力服务;Secret Store 是安全凭证底座。 ## 三、不可破坏的原则 1. 在需求和边界没有想清楚前,不改代码。 2. NewAPI 保持独立服务,不继续改造成 Manager 的内嵌后台。 3. NewAPI 后台不开放给普通 SaaS 用户,模型管理由内部人员完成。 -4. Manager 只补 NewAPI 没有的后端能力:用户输入编排、资源绑定、权限、Agnet 部署、审计和生命周期管理。 -5. 用户绑定的是 Agnet 可用资源,不只是 Git 来源。 +4. Manager 只补 NewAPI 没有的后端能力:用户输入编排、资源绑定、权限、Agent 部署、审计和生命周期管理。 +5. 用户绑定的是 Agent 可用资源,不只是 Git 来源。 6. 密钥不能进入 Git、Markdown、前端、部署摘要或日志。 7. Manager 负责资源绑定、权限分配和凭证托管能力;真实密钥放入 Secret Store。 -8. 子 Agnet 不保存长期密钥,只接收角色、资源元数据、AGENT.md 和受控访问方式。 -9. Agnet 平台在 AKS 上负责运行时身份、隔离、状态、事件和审计回传。 -10. 高危操作审批只在客户端完成;审批通过后可以把密钥保管器派生的短期、最小权限凭证注入子 Agnet,但不能注入长期密钥。 -11. 子 Agnet 的运行模型是 Agnet 平台的部署配置,独立于 NewAPI 的用户、Token、Group 扣费映射。 +8. 子 Agent 不保存长期密钥,只接收角色、资源元数据、AGENT.md 和受控访问方式。 +9. Agent 平台在 AKS 上负责运行时身份、隔离、状态、事件和审计回传。 +10. 高危操作审批只在客户端完成;审批通过后可以把密钥保管器派生的短期、最小权限凭证注入子 Agent,但不能注入长期密钥。 +11. 子 Agent 的运行模型是 Agent 平台的部署配置,独立于 NewAPI 的用户、Token、Group 扣费映射。 ## 四、Manager 的核心功能 1. 绑定 GitHub、GitLab、Gitea、Gitee、自建 Git 等代码来源。 2. 绑定云资源,例如 AWS、Azure、GCP、虚拟机、数据库、对象存储、Kubernetes 集群。 3. 确认项目仓库、SK 仓库、项目文档仓库或二合一仓库。 -4. 按敏捷或瀑布方法分配子 Agnet 角色。 -5. 为每个子 Agnet 配置 AGENT.md、可用工具、Git 范围、云资源范围、模型和预算。 -6. 部署子 Agnet,设置数量、模型和运行环境。 -7. 观察子 Agnet 活动状态、失败原因、事件和运行日志。 +4. 按敏捷或瀑布方法分配子 Agent 角色。 +5. 为每个子 Agent 配置 AGENT.md、可用工具、Git 范围、云资源范围、模型和预算。 +6. 部署子 Agent,设置数量、模型和运行环境。 +7. 观察子 Agent 活动状态、失败原因、事件和运行日志。 8. 查看审计日志和模型调用日志。 9. 查看可用模型、余额、额度和使用情况。 10. 展示 NewAPI 对普通用户有意义的能力,隐藏渠道、价格、模型后台管理等管理员能力。 ## 五、资源绑定与密钥托管 -绑定不是保存一串密钥,而是创建面向登录用户、绑定 Git/SK/云资源和子 Agnet 角色的 Resource Grant。 +绑定不是保存一串密钥,而是创建面向登录用户、绑定 Git/SK/云资源和子 Agent 角色的 Resource Grant。 ```text 用户授权 Heicode 使用外部资源 -> Manager 记录资源元数据 -> Manager 的 Secret Broker 把真实凭证写入 Secret Store --> Manager 生成可审计、可撤销、可分配给子 Agnet 的资源授权 +-> Manager 生成可审计、可撤销、可分配给子 Agent 的资源授权 ``` 资源类型: -| 类型 | 示例 | 子 Agnet 可见内容 | +| 类型 | 示例 | 子 Agent 可见内容 | |------|------|------------------| | Git 资源 | GitHub repo、自建 Git、SK repo | repo URL、ref、允许路径、读写范围 | | 云账号 | Azure subscription、AWS account、GCP project | account/project/subscription 元数据、允许动作 | @@ -87,11 +87,11 @@ Resource Binding 建议字段: | 字段 | 含义 | 约束 | |------|------|------| | `id` | 资源绑定 ID | Manager 内部生成 | -| `user_id` | 登录用户 ID | 来自 Heicode/Agnet 登录体系的 `user.id` 或 JWT `sub` | +| `user_id` | 登录用户 ID | 来自 Heicode/Agent 登录体系的 `user.id` 或 JWT `sub` | | `type` | 资源类型 | `git`、`sk`、`project_doc`、`cloud_account`、`cloud_resource` | | `name` | 用户可见名称 | 不包含密钥 | | `external_ref` | 外部资源定位 | repo URL、subscription ID、resource ID、文档引用等非密钥标识 | -| `metadata` | 子 Agnet 可见元数据 | 只包含 ref、允许路径、环境、网络边界等非密钥信息 | +| `metadata` | 子 Agent 可见元数据 | 只包含 ref、允许路径、环境、网络边界等非密钥信息 | | `permission_scope` | 可授权动作范围 | 例如 `read`、`write`、`deploy`、`approve_required` | | `constraints` | 使用限制 | 路径、分支、环境、网络、审批要求、TTL 等 | | `secret_ref` | Secret Store 引用 | 可为空;有凭证时只保存引用,不保存原文 | @@ -105,8 +105,8 @@ Resource Grant 建议字段: | `id` | 授权 ID | Manager 内部生成 | | `user_id` / `binding_scope` | 授权归属 | 来自登录用户、绑定 Git/SK/云资源和角色范围 | | `resource_id` | 被授权资源 | 指向 Resource Binding | -| `role` | 子 Agnet 角色 | 例如 product、frontend、backend、reviewer、ops | -| `agent_id` | 子 Agnet 标识 | 可为空;为空表示授予该项目角色下的下一次部署 | +| `role` | 子 Agent 角色 | 例如 product、frontend、backend、reviewer、ops | +| `agent_id` | 子 Agent 标识 | 可为空;为空表示授予该项目角色下的下一次部署 | | `allowed_actions` | 本次授权动作 | 必须是 `permission_scope` 的子集 | | `constraints` | 本次授权限制 | 不得放宽 Resource Binding 的限制 | | `status` | 授权状态 | `active`、`suspended`、`revoked`、`expired` | @@ -146,7 +146,7 @@ SaaS 场景下,凭证不能转嫁给用户手工管理。用户负责授权, | 方案 | 判断 | |------|------| -| HashiCorp Vault | 优先选择。Kubernetes Auth、Policy、TTL、动态密钥、审计能力成熟,适合 AKS 中的子 Agnet 运行时授权 | +| HashiCorp Vault | 优先选择。Kubernetes Auth、Policy、TTL、动态密钥、审计能力成熟,适合 AKS 中的子 Agent 运行时授权 | | Infisical | 可选方案。产品体验较好,但需要验证 SaaS 多用户隔离策略和运行时授权能力 | | Azure Key Vault | 适合 Azure 优先部署,也可以作为 Secret Provider 的一种实现 | @@ -160,9 +160,9 @@ Secret Broker 负责: - 轮换、撤销、禁用凭证。 - 避免密钥进入日志、前端响应、Markdown 和 Git。 -## 七、AKS 上的 Agnet 凭证访问 +## 七、AKS 上的 Agent 凭证访问 -Agnet 平台底层是 AKS,因此运行时权限应和 Kubernetes 身份绑定。 +Agent 平台底层是 AKS,因此运行时权限应和 Kubernetes 身份绑定。 推荐流程: @@ -170,13 +170,13 @@ Agnet 平台底层是 AKS,因此运行时权限应和 Kubernetes 身份绑定 用户在 Manager 授权资源 -> Manager Secret Broker 写入 Secret Store -> Manager 记录 Resource Grant --> Manager 请求 Agnet 平台部署 --> Agnet 平台为 deployment / role 创建 K8s ServiceAccount --> Agnet 平台绑定 Vault policy 或 Workload Identity --> 子 Agnet Pod 运行时只能访问被授权的 secret +-> Manager 请求 Agent 平台部署 +-> Agent 平台为 deployment / role 创建 K8s ServiceAccount +-> Agent 平台绑定 Vault policy 或 Workload Identity +-> 子 Agent Pod 运行时只能访问被授权的 secret ``` -子 Agnet 拿到的是角色、目标、AGENT.md、resource context 和 permission manifest,不拿长期密钥。 +子 Agent 拿到的是角色、目标、AGENT.md、resource context 和 permission manifest,不拿长期密钥。 运行时访问分两类: @@ -209,7 +209,7 @@ Manager 不展示: - 系统管理员用户管理。 - NewAPI 原生管理后台入口。 -用户登录 Manager,不直接登录 NewAPI。Manager User 复用 Heicode/Agnet 登录体系,并维护 `user.id`、`channelId` 到 NewAPI 用户、Token、Group、quota 或 usage 的映射。当前不要把 Manager tenant/project 作为扣费和团队开发控制主轴;团队开发控制优先由绑定 Git、路径范围、资源授权和 Agnet 角色表达。子 Agnet 的模型选择、模型 profile 和实例数属于 Agnet 平台部署配置,不放进 NewAPI 扣费对象里。 +用户登录 Manager,不直接登录 NewAPI。Manager User 复用 Heicode/Agent 登录体系,并维护 `user.id`、`channelId` 到 NewAPI 用户、Token、Group、quota 或 usage 的映射。当前不要把 Manager tenant/project 作为扣费和团队开发控制主轴;团队开发控制优先由绑定 Git、路径范围、资源授权和 Agent 角色表达。子 Agent 的模型选择、模型 profile 和实例数属于 Agent 平台部署配置,不放进 NewAPI 扣费对象里。 ## 九、Markdown 与权限清单 @@ -217,7 +217,7 @@ Manager 不展示: Markdown 可包含: -- 子 Agnet 角色。 +- 子 Agent 角色。 - 目标任务。 - 项目背景。 - AGENT.md 来源。 @@ -240,7 +240,7 @@ Manager 应生成两类产物: | 产物 | 用途 | |------|------| -| AGENT.md / resource context | 给子 Agnet 的启动上下文,说明角色和可用资源 | -| permission manifest | 给 Agnet 平台和审计系统的结构化权限清单 | +| AGENT.md / resource context | 给子 Agent 的启动上下文,说明角色和可用资源 | +| permission manifest | 给 Agent 平台和审计系统的结构化权限清单 | Markdown 面向模型理解,manifest 面向系统强制执行。 diff --git a/docs/integration/AgentManager普通sub产物回调缺失问题.md b/docs/integration/AgentManager普通sub产物回调缺失问题.md index eb42cff..bee5ecf 100644 --- a/docs/integration/AgentManager普通sub产物回调缺失问题.md +++ b/docs/integration/AgentManager普通sub产物回调缺失问题.md @@ -1,7 +1,7 @@ # Agent Manager 普通 sub 产物回调缺失问题 更新时间:2026-05-29 -发给:Agent Manager / Agnet Runtime 负责人 +发给:Agent Manager / Agent Runtime 负责人 范围:普通 sub 敏捷模式,不包含 HeiCode-Swarm 独立蜂群 Runtime。 ## 1. 问题现象 @@ -36,8 +36,8 @@ SK SNAPSHOTS = 0 Heicode Manager 生产配置确认当前普通 sub 走: ```text -AGNET_RUNTIME_BASE_URL=http://20.212.121.126 -AGNET_RUNTIME_CREATE_PATH=/api/swarms +AGENT_RUNTIME_BASE_URL=http://20.212.121.126 +AGENT_RUNTIME_CREATE_PATH=/api/swarms ``` `SWARM_RUNTIME_BASE_URL` 当前为空。 @@ -47,7 +47,7 @@ AGNET_RUNTIME_CREATE_PATH=/api/swarms ```text Heicode 桌面客户端 -> Heicode Manager 生产 - -> Agent Manager / Agnet Runtime 普通 sub 入口 + -> Agent Manager / Agent Runtime 普通 sub 入口 -> Heicode Manager callback -> 桌面客户端查询 artifacts/timeline ``` @@ -102,7 +102,7 @@ sk_tool.completed = 0 Heicode Manager artifact 表查询结果: ```text -agnet_artifacts where deployment_id = 'dep_fa4f43da9e0a' +agent_artifacts where deployment_id = 'dep_fa4f43da9e0a' => 0 rows ``` @@ -113,7 +113,7 @@ agnet_artifacts where deployment_id = 'dep_fa4f43da9e0a' 生产日志中,Agent Manager 在任务完成时间段连续请求: ```text -POST /api/agnet/callbacks/swarm-events +POST /api/agent/callbacks/swarm-events ``` HTTP 状态均为: @@ -188,7 +188,7 @@ Logs will be fetched from K8s in Phase 2 本问题根因不在 Heicode Manager,也不在桌面客户端。 -当前证据指向 Agent Manager / Agnet Runtime: +当前证据指向 Agent Manager / Agent Runtime: 1. Runtime 将 deployment 标记为 `completed`。 2. Runtime 自身返回 `artifacts: []`。 @@ -206,7 +206,7 @@ Logs will be fetched from K8s in Phase 2 普通 sub 任务完成时,Agent Manager 必须向 Heicode Manager callback: ```text -POST https://code.xinghanlab.com/api/agnet/callbacks/swarm-events +POST https://code.xinghanlab.com/api/agent/callbacks/swarm-events event_type = artifact.created ``` @@ -370,7 +370,7 @@ Logs will be fetched from K8s in Phase 2 |---|---| | Runtime 状态 | deployment 和 agents 状态一致 | | callback | Heicode Manager 收到 `artifact.created` | -| Manager artifacts | `/api/agnet/user/deployments/{deployment_id}/artifacts` 返回 total > 0 | +| Manager artifacts | `/api/agent/user/deployments/{deployment_id}/artifacts` 返回 total > 0 | | 客户端展示 | 右侧 `ARTIFACTS` 不再是 0 | | 交付物 | 能看到 Git 分支、提交、预览地址、部署清单或最终结果文档 | | usage | 如果真实调用模型,tokens/cost 不应一直为 0 | @@ -380,7 +380,7 @@ Logs will be fetched from K8s in Phase 2 Heicode Manager 和桌面客户端当前表现是正确反映 Runtime 数据。 -真正缺口在 Agent Manager / Agnet Runtime: +真正缺口在 Agent Manager / Agent Runtime: ```text 任务被标记 completed,但 Runtime 没有生成或回传 artifact.created。 diff --git a/docs/integration/AgentManager普通sub剩余补充要求.md b/docs/integration/AgentManager普通sub剩余补充要求.md index 4ca7bbc..1ffb038 100644 --- a/docs/integration/AgentManager普通sub剩余补充要求.md +++ b/docs/integration/AgentManager普通sub剩余补充要求.md @@ -1,7 +1,7 @@ # Agent Manager 普通 sub 剩余补充要求 更新时间:2026-05-28 -发给:Agent Manager / Agnet Runtime 负责人 +发给:Agent Manager / Agent Runtime 负责人 范围:普通 sub 敏捷开发模式,不包含蜂群模式完整 task graph。 ## 1. 当前已验证事实 @@ -59,7 +59,7 @@ Heicode Manager 生产版本 `1.4.19` 已完成并验证以下链路: Manager callback 地址: ```text -https://code.xinghanlab.com/api/agnet/callbacks/swarm-events +https://code.xinghanlab.com/api/agent/callbacks/swarm-events ``` 创建 run 后,Agent Manager 至少需要主动回调这些事件: @@ -224,7 +224,7 @@ Agent Manager 补完后,按以下步骤验收: - `artifact.created` - `budget.alert` 5. Manager 查询: - - `/api/agnet/user/deployments/{deployment_id}` + - `/api/agent/user/deployments/{deployment_id}` - `/events` - `/timeline` - `/artifacts` diff --git a/docs/integration/AgentManager普通sub联调整改要求.md b/docs/integration/AgentManager普通sub联调整改要求.md index cce9c2b..2bb7449 100644 --- a/docs/integration/AgentManager普通sub联调整改要求.md +++ b/docs/integration/AgentManager普通sub联调整改要求.md @@ -1,7 +1,7 @@ # Agent Manager 普通 sub 联调整改要求 更新时间:2026-05-28 -发给:Agent Manager / Agnet Runtime 负责人 +发给:Agent Manager / Agent Runtime 负责人 范围:Heicode Manager 普通 sub 敏捷模式联调,不包含蜂群模式完整 task graph 的额外能力。 ## 1. 当前结论 @@ -10,7 +10,7 @@ Agent Manager 新版文档 `HEICODE_API_INTEGRATION(5).md` 已经补充了 Runti 按 2026-05-28 生产实测,Manager 与 Agent Manager 的普通 sub 核心通讯链路已经跑通,但还不能认为“真实开发执行结果”完整闭环。当前结论是: -1. Agent Manager 当前联调统一使用 IP `http://20.212.121.126`;域名 `https://agent-manager.taijiagnet.com` 后续解析和证书就绪后再切换,不作为当前联调阻塞项。 +1. Agent Manager 当前联调统一使用 IP `http://20.212.121.126`;域名 `https://agent-manager.taijiagent.com` 后续解析和证书就绪后再切换,不作为当前联调阻塞项。 2. Manager 生产已切到 `/api/swarms` 创建入口,并验证能拿到 `runtime_swarm_id=swm_*`。 3. Agent Manager 自动 callback 已能写入 Manager `events/timeline`,Manager `1.4.19` 开始会把 callback 反写到 deployment 快照。 4. 当前仍缺真实产物和真实用量:Agent Manager status 接口返回 `tokens_used=0`、`artifacts=[]`,并出现整体 `completed` 但 agent 仍 `running` 的状态不一致。 @@ -18,9 +18,9 @@ Agent Manager 新版文档 `HEICODE_API_INTEGRATION(5).md` 已经补充了 Runti Heicode Manager 侧已确认: -- `POST https://code.xinghanlab.com/api/agnet/callbacks/swarm-events` 生产路由在线。 +- `POST https://code.xinghanlab.com/api/agent/callbacks/swarm-events` 生产路由在线。 - callback 支持 HMAC 和旧 token 兼容;当前生产为了先跑通自动 callback,已配置 HMAC fallback。 -- `GET https://code.xinghanlab.com/api/agnet/callbacks/swarm-events/schema` 已由 Manager 提供,用于联调前核对事件类型和必填字段。 +- `GET https://code.xinghanlab.com/api/agent/callbacks/swarm-events/schema` 已由 Manager 提供,用于联调前核对事件类型和必填字段。 - Manager 可接收 `deployment.status_changed`、`phase.changed`、`timeline.updated`、`agent.started/completed/crashed`,并在 `1.4.19` 反写 deployment 详情状态。 ## 2. Agent Manager 必须整改的 P0 @@ -30,7 +30,7 @@ Heicode Manager 侧已确认: | P0 | 固定联调地址 | 当前阶段约定先走 IP,后续再切域名 | 文档和配置先统一使用 `http://20.212.121.126`;域名切换另行确认 | | P0 | 创建普通 sub deployment | `/api/swarms` 已能创建并返回 `swm_*` | 保持幂等和字段稳定,后续域名切换不能破坏 | | P0 | deployment detail | Manager 已能记录 callback;Agent Manager status 仍有 `completed` 与 agent `running` 不一致 | 返回真实 phase、agent 状态、失败原因和更新时间 | -| P0 | deployment stop | `/api/agnet/deployments/{deployment_id}/stop` 已可用 | 停止后 Runtime 真实停止,并回调 stopped 事件 | +| P0 | deployment stop | `/api/agent/deployments/{deployment_id}/stop` 已可用 | 停止后 Runtime 真实停止,并回调 stopped 事件 | | P0 | callback 主动推送 | 已验证 status/phase/timeline/agent/budget callback 能进入 Manager | 继续补 artifact、SK、approval 的真实运行数据 | | P0 | usage 回传 | 当前 budget callback 中 token/cost/runtime 多为 0 | 通过 callback 或事件接口回传真实 token、成本、运行时长、资源使用 | @@ -39,7 +39,7 @@ Heicode Manager 侧已确认: ### 3.1 健康检查 ```http -GET /api/agnet/health +GET /api/agent/health ``` 要求: @@ -50,10 +50,10 @@ GET /api/agnet/health ### 3.2 创建普通 sub 运行 -普通 sub 当前生产联调使用 Agent Manager `/api/agnet/deployments`,蜂群 `/api/swarms` 另见 `AgentManager蜂群Runtime接口实现要求.md`。 +普通 sub 当前生产联调使用 Agent Manager `/api/agent/deployments`,蜂群 `/api/swarms` 另见 `AgentManager蜂群Runtime接口实现要求.md`。 ```http -POST /api/agnet/deployments +POST /api/agent/deployments Authorization: Bearer Content-Type: application/json X-User-ID: @@ -115,8 +115,8 @@ X-Idempotency-Key: "resource_grants": [] }, "callback": { - "url": "https://code.xinghanlab.com/api/agnet/callbacks/swarm-events", - "signing_secret_ref": "azkv://heicode-kv.vault.azure.net/secrets/agnet-callback-signing-key", + "url": "https://code.xinghanlab.com/api/agent/callbacks/swarm-events", + "signing_secret_ref": "azkv://heicode-kv.vault.azure.net/secrets/agent-callback-signing-key", "subscribed_events": [ "deployment.status_changed", "phase.changed", @@ -154,14 +154,14 @@ X-Idempotency-Key: 要求: -1. `deployment_id` 必须可用于 `/api/agnet/deployments/{deployment_id}`、`/api/agnet/deployments/{deployment_id}/stop`。 +1. `deployment_id` 必须可用于 `/api/agent/deployments/{deployment_id}`、`/api/agent/deployments/{deployment_id}/stop`。 2. 同一个 `X-Idempotency-Key` 重复请求必须返回同一个运行,不应重复创建。 3. 请求缺少 `orchestration_plan`、`callback.url`、`sub_mode`、`user_context` 时必须返回 4xx。 ### 3.3 查询普通 sub deployment ```http -GET /api/agnet/deployments/{deployment_id} +GET /api/agent/deployments/{deployment_id} Authorization: Bearer ``` @@ -188,7 +188,7 @@ Authorization: Bearer ### 3.4 停止普通 sub deployment ```http -POST /api/agnet/deployments/{deployment_id}/stop +POST /api/agent/deployments/{deployment_id}/stop Authorization: Bearer Content-Type: application/json ``` @@ -223,7 +223,7 @@ Content-Type: application/json Manager 生产 callback 地址: ```text -https://code.xinghanlab.com/api/agnet/callbacks/swarm-events +https://code.xinghanlab.com/api/agent/callbacks/swarm-events ``` ### 4.1 认证方式 @@ -231,9 +231,9 @@ https://code.xinghanlab.com/api/agnet/callbacks/swarm-events 优先使用 HMAC: ```http -X-Agnet-Event-Id: evt_xxx -X-Agnet-Timestamp: -X-Agnet-Signature: sha256= +X-Agent-Event-Id: evt_xxx +X-Agent-Timestamp: +X-Agent-Signature: sha256= X-Correlation-ID: Content-Type: application/json ``` @@ -248,7 +248,7 @@ signature = HMAC_SHA256(callback_signing_secret, signature_payload) 过渡期可使用旧 token: ```http -X-Agnet-Service-Token: +X-Agent-Service-Token: ``` 不要把真实 token 写入文档、日志或 artifact。 @@ -397,7 +397,7 @@ Manager 端已有 approval、credential lease、approve/reject 和 Runtime decis 推荐接口: ```http -POST /api/agnet/deployments/{deployment_id}/approvals/{approval_id} +POST /api/agent/deployments/{deployment_id}/approvals/{approval_id} Authorization: Bearer Content-Type: application/json ``` @@ -415,7 +415,7 @@ Content-Type: application/json "resource_id": "repo_main", "risk_level": "high", "requires_credential": true, - "credential_ref": "lease://agnet/cred_123", + "credential_ref": "lease://agent/cred_123", "lease_id": "lease_123", "lease_expires_at": 1770000000000, "decided_by": "user_123", @@ -454,10 +454,10 @@ Agent Manager 改完后,按下面步骤验收。 | 步骤 | 操作 | 通过标准 | |---:|---|---| -| 1 | `GET /api/agnet/health` | 返回 healthy | -| 2 | `POST /api/agnet/deployments` 创建普通 sub run | 返回 `deployment_id`、`status` | +| 1 | `GET /api/agent/health` | 返回 healthy | +| 2 | `POST /api/agent/deployments` 创建普通 sub run | 返回 `deployment_id`、`status` | | 3 | 重复同一个 `X-Idempotency-Key` 创建 | 不重复创建,返回同一个 ID | -| 4 | `GET /api/agnet/deployments/{deployment_id}` | 返回真实状态和 agents | +| 4 | `GET /api/agent/deployments/{deployment_id}` | 返回真实状态和 agents | | 5 | Runtime 主动 callback `phase.changed` | Manager callback 返回 success | | 6 | Runtime 主动 callback `timeline.updated` | Manager timeline 可查到 | | 7 | Runtime 主动 callback `artifact.created` | Manager artifacts 可查到 | @@ -465,7 +465,7 @@ Agent Manager 改完后,按下面步骤验收。 | 9 | Runtime 主动 callback usage | Manager 能看到 usage 摘要或 callback event | | 10 | Runtime 主动 callback `approval.requested` | Manager pending approval 生成 | | 11 | Manager approve/reject | Agent Manager 收到 decision | -| 12 | `POST /api/agnet/deployments/{deployment_id}/stop` | Runtime 停止,状态变为 stopped | +| 12 | `POST /api/agent/deployments/{deployment_id}/stop` | Runtime 停止,状态变为 stopped | | 13 | 检查日志和 artifact metadata | 不包含明文密钥 | ## 9. 当前实测记录 @@ -474,12 +474,12 @@ Agent Manager 改完后,按下面步骤验收。 | 接口 | 结果 | 说明 | |---|---|---| -| `GET http://20.212.121.126/api/agnet/health` | 200 | Agent Manager IP 健康检查正常 | -| `GET https://agent-manager.taijiagnet.com/api/agnet/health` | 暂不作为当前验收项 | 当前约定先走 IP,域名后续再切换 | -| `POST https://code.xinghanlab.com/api/agnet/callbacks/swarm-events` 无认证 | 业务返回 `CALLBACK_UNAUTHORIZED` | Manager callback 路由在线 | -| `POST https://code.xinghanlab.com/api/agnet/callbacks/swarm-events` 带旧 token 但空 body | 业务返回 `CALLBACK_INVALID` | 认证通过,进入事件校验 | -| `GET https://code.xinghanlab.com/api/agnet/callbacks/swarm-events/schema` | 404 | Manager schema GET 生产未通,Manager 侧需复核 | -| `POST http://20.212.121.126/api/agnet/deployments` | 200 | 普通 sub Runtime deployment 创建可用 | +| `GET http://20.212.121.126/api/agent/health` | 200 | Agent Manager IP 健康检查正常 | +| `GET https://agent-manager.taijiagent.com/api/agent/health` | 暂不作为当前验收项 | 当前约定先走 IP,域名后续再切换 | +| `POST https://code.xinghanlab.com/api/agent/callbacks/swarm-events` 无认证 | 业务返回 `CALLBACK_UNAUTHORIZED` | Manager callback 路由在线 | +| `POST https://code.xinghanlab.com/api/agent/callbacks/swarm-events` 带旧 token 但空 body | 业务返回 `CALLBACK_INVALID` | 认证通过,进入事件校验 | +| `GET https://code.xinghanlab.com/api/agent/callbacks/swarm-events/schema` | 404 | Manager schema GET 生产未通,Manager 侧需复核 | +| `POST http://20.212.121.126/api/agent/deployments` | 200 | 普通 sub Runtime deployment 创建可用 | ### 9.1 2026-05-28 追加联调记录 @@ -491,17 +491,17 @@ Agent Manager 改完后,按下面步骤验收。 - 因此本次没有宣称“桌面客户端登录后走 Manager 用户态创建 deployment”完整通过。 - 本次完成的是服务到服务联调:Manager/客户端等价 payload -> Agent Manager IP,以及 Agent Manager callback 协议 -> Heicode Manager callback 接收端。 -Agent Manager `/api/agnet/deployments` 服务到服务链路: +Agent Manager `/api/agent/deployments` 服务到服务链路: | 步骤 | 结果 | 证据 | |---|---|---| -| `GET /api/agnet/health` | 通过 | 返回 `success=true`、`status=healthy` | -| `POST /api/agnet/deployments` | 通过 | 返回 `deployment_id=dep_dbafd1ac37c3`,状态 `pending` | -| `GET /api/agnet/deployments/{deployment_id}` | 通过 | 能查到 `user_id`、`binding_scope`、`sub_mode=agile`、`callback_configured=true` | -| `GET /api/agnet/deployments/{deployment_id}/events` | 部分通过 | 只看到 `deployment.accepted`,未看到 `phase.changed/timeline.updated/artifact.created/sk_tool.*` | -| `GET /api/agnet/deployments/{deployment_id}/logs` | 部分通过 | 返回系统日志 `Pod ... has no logs yet` | -| `GET /api/agnet/deployments/{deployment_id}/metrics` | 通过但疑似占位 | 返回 CPU/内存/网络指标,但值看起来是固定模拟值,需要 Runtime 说明来源 | -| `POST /api/agnet/deployments/{deployment_id}/stop` | 通过 | `dep_dbafd1ac37c3` 停止成功,返回 `status=stopped` | +| `GET /api/agent/health` | 通过 | 返回 `success=true`、`status=healthy` | +| `POST /api/agent/deployments` | 通过 | 返回 `deployment_id=dep_dbafd1ac37c3`,状态 `pending` | +| `GET /api/agent/deployments/{deployment_id}` | 通过 | 能查到 `user_id`、`binding_scope`、`sub_mode=agile`、`callback_configured=true` | +| `GET /api/agent/deployments/{deployment_id}/events` | 部分通过 | 只看到 `deployment.accepted`,未看到 `phase.changed/timeline.updated/artifact.created/sk_tool.*` | +| `GET /api/agent/deployments/{deployment_id}/logs` | 部分通过 | 返回系统日志 `Pod ... has no logs yet` | +| `GET /api/agent/deployments/{deployment_id}/metrics` | 通过但疑似占位 | 返回 CPU/内存/网络指标,但值看起来是固定模拟值,需要 Runtime 说明来源 | +| `POST /api/agent/deployments/{deployment_id}/stop` | 通过 | `dep_dbafd1ac37c3` 停止成功,返回 `status=stopped` | 长等待测试: @@ -535,7 +535,7 @@ source=agent-manager-ip-smoke 结论: 1. Heicode Manager callback 接收端可用,旧 token 认证和幂等可用。 -2. Agent Manager IP 的 `/api/agnet/deployments` 创建、查询、停止可用。 +2. Agent Manager IP 的 `/api/agent/deployments` 创建、查询、停止可用。 3. Agent Manager 当前没有在实测中产生普通 sub 所需的 phase/timeline/artifact/SK 主动回调。 4. 因没有可用生产 Manager 用户 session,本次没有完成“客户端登录态 -> Manager 用户态 deployment -> Runtime”的完整端到端测试。 @@ -558,15 +558,15 @@ group: default |---|---|---| | 登录生产 Manager | 通过 | `/api/user/login` 返回 `success=true`,用户 `id=22` | | 查询用户信息 | 通过 | `/api/user/self` 返回用户 `chenchen`、`group=default` | -| 查询 Runtime 健康 | 通过 | `/api/agnet/runtime/health` 返回 `enabled=true`、`configured=true`、`status=healthy`、`create_path=/api/agnet/deployments`、远端为 Agent Manager IP | -| 客户端模拟创建 deployment | 通过 | `POST /api/agnet/user/deployments` 返回 Manager deployment `dep_1ba14ccfb558` | +| 查询 Runtime 健康 | 通过 | `/api/agent/runtime/health` 返回 `enabled=true`、`configured=true`、`status=healthy`、`create_path=/api/agent/deployments`、远端为 Agent Manager IP | +| 客户端模拟创建 deployment | 通过 | `POST /api/agent/user/deployments` 返回 Manager deployment `dep_1ba14ccfb558` | | Manager -> Agent Manager create | 通过 | Manager detail 写回 `runtime_deployment_id=dep_3335e54e9bdf`,`runtime_state=pending` | | Manager 用户态 events | 通过 | 出现 `deployment.accepted`、`runtime.sync.started`、`runtime.sync.accepted` | | Manager 用户态 logs | 通过 | 出现 control-plane 日志和 runtime sync 日志,均为 redacted | | Manager 用户态 metrics | 部分通过 | 返回 `platform_estimated=true`,说明是 Manager 估算/占位,不是 Runtime 真实资源指标 | | Manager 用户态 artifacts | 未产出 | 返回空列表 | | Manager 用户态 timeline | 部分通过 | 能返回 deployment/events,但 callbacks/artifacts 为空 | -| Manager stop | 通过 | `POST /api/agnet/user/deployments/dep_1ba14ccfb558/stop` 返回 `status=stopped` | +| Manager stop | 通过 | `POST /api/agent/user/deployments/dep_1ba14ccfb558/stop` 返回 `status=stopped` | | Runtime stop 结果 | 通过 | 直查 Agent Manager `dep_3335e54e9bdf`,状态为 `stopped` | 本次真实 ID: @@ -582,10 +582,10 @@ Agent Manager 侧直查 `dep_3335e54e9bdf`: | 接口 | 结果 | |---|---| -| `GET /api/agnet/deployments/dep_3335e54e9bdf` | 200,状态 `stopped`,`user_id=22` | -| `GET /api/agnet/deployments/dep_3335e54e9bdf/events` | 200,仅有 `deployment.accepted`、`deployment.stopped` | -| `GET /api/agnet/deployments/dep_3335e54e9bdf/logs` | 200,仅有 `Pod ... has no logs yet` | -| `GET /api/agnet/deployments/dep_3335e54e9bdf/metrics` | 200,返回 CPU/内存/网络固定值 | +| `GET /api/agent/deployments/dep_3335e54e9bdf` | 200,状态 `stopped`,`user_id=22` | +| `GET /api/agent/deployments/dep_3335e54e9bdf/events` | 200,仅有 `deployment.accepted`、`deployment.stopped` | +| `GET /api/agent/deployments/dep_3335e54e9bdf/logs` | 200,仅有 `Pod ... has no logs yet` | +| `GET /api/agent/deployments/dep_3335e54e9bdf/metrics` | 200,返回 CPU/内存/网络固定值 | 本次客户端模拟链路结论: diff --git a/docs/integration/AgentManager蜂群Runtime接口实现要求.md b/docs/integration/AgentManager蜂群Runtime接口实现要求.md index b2a6d8e..cfb6ac2 100644 --- a/docs/integration/AgentManager蜂群Runtime接口实现要求.md +++ b/docs/integration/AgentManager蜂群Runtime接口实现要求.md @@ -31,18 +31,18 @@ Agent Manager / Swarm Runtime 需要负责执行层: 当前生产 Manager Runtime 配置实际为: ```text -AGNET_RUNTIME_BASE_URL=http://20.212.121.126 -AGNET_RUNTIME_CREATE_PATH=/api/agnet/deployments -AGNET_RUNTIME_STOP_PATH=/api/agnet/deployments/{deployment_id}/stop +AGENT_RUNTIME_BASE_URL=http://20.212.121.126 +AGENT_RUNTIME_CREATE_PATH=/api/agent/deployments +AGENT_RUNTIME_STOP_PATH=/api/agent/deployments/{deployment_id}/stop ``` 实测结果: | 项 | 结果 | |---|---| -| `GET /api/agnet/runtime/health` | 通过,远端 Agent Manager IP healthy | -| `POST /api/swarms` 使用 `agent_runtime.platform=agnet-swarm` | 失败,Manager 返回 `agent_runtime.platform must be agnet when runtime context is present` | -| `POST /api/swarms` 使用 `agent_runtime.platform=agnet` | 通过 | +| `GET /api/agent/runtime/health` | 通过,远端 Agent Manager IP healthy | +| `POST /api/swarms` 使用 `agent_runtime.platform=agent-swarm` | 失败,Manager 返回 `agent_runtime.platform must be agent when runtime context is present` | +| `POST /api/swarms` 使用 `agent_runtime.platform=agent` | 通过 | | Manager deployment | `dep_72cdc138b9c8` | | Manager 返回 swarm_id | `dep_72cdc138b9c8`,当前是 Manager 本地 deployment id | | Runtime deployment | `dep_77370f27186f` | @@ -58,7 +58,7 @@ AGNET_RUNTIME_STOP_PATH=/api/agnet/deployments/{deployment_id}/stop 结论: 1. Heicode Manager 的 `/api/swarms` adapter 能接收用户态蜂群请求,并能通过当前 Runtime bridge 调 Agent Manager IP。 -2. 当前生产链路实际走的是 Agent Manager `/api/agnet/deployments`,不是 Agent Manager `/api/swarms`。 +2. 当前生产链路实际走的是 Agent Manager `/api/agent/deployments`,不是 Agent Manager `/api/swarms`。 3. Manager stop 可以传递到 Agent Manager deployment stop。 4. 当前还没有真实蜂群 Runtime callback、task graph、artifact、SK、usage。 5. Agent Manager 如果要满足正式蜂群模式,仍需实现本文后续定义的 `/api/swarms`、`/api/swarms/{swarm_id}`、`/api/swarms/{swarm_id}/stop`、callback 和 usage 约束。 @@ -68,13 +68,13 @@ AGNET_RUNTIME_STOP_PATH=/api/agnet/deployments/{deployment_id}/stop ```text Heicode Desktop Client -> Heicode Manager - POST /api/swarms 或 /api/agnet/user/deployments + POST /api/swarms 或 /api/agent/user/deployments -> Agent Manager / Swarm Runtime POST /api/swarms <- Runtime response runtime_deployment_id / swarm_id / status <- Runtime callback - POST /api/agnet/callbacks/swarm-events + POST /api/agent/callbacks/swarm-events -> Runtime approval decision POST /api/swarms/{swarm_id}/approvals/{approval_id} ``` @@ -98,11 +98,11 @@ Content-Type: application/json | 优先级 | 方法 | 路径 | 必须 | 用途 | |---|---|---|---:|---| -| P0 | `GET` | `/api/agnet/health` | 是 | 健康检查 | +| P0 | `GET` | `/api/agent/health` | 是 | 健康检查 | | P0 | `POST` | `/api/swarms` | 是 | 创建真实 Swarm Run | | P0 | `POST` | `/api/swarms/{swarm_id}/stop` | 是 | 停止 Swarm Run | | P0 | `POST` | `/api/swarms/{swarm_id}/approvals/{approval_id}` | 是 | 接收 Manager 审批决定 | -| P0 | callback | Manager `/api/agnet/callbacks/swarm-events` | 是 | 回写状态、task、handoff、artifact、approval | +| P0 | callback | Manager `/api/agent/callbacks/swarm-events` | 是 | 回写状态、task、handoff、artifact、approval | | P1 | `GET` | `/api/swarms/{swarm_id}` | 建议 | 查询 Runtime 详情 | | P1 | `GET` | `/api/swarms/{swarm_id}/tasks` | 建议 | 查询 task graph | | P1 | `GET` | `/api/swarms/{swarm_id}/logs` | 建议 | 查询日志 | @@ -130,7 +130,7 @@ Content-Type: application/json 3. `X-Idempotency-Key` 必须支持幂等;重复创建请求不能生成多个真实 Swarm Run。 4. Agent Manager 不得要求 Manager 传长期明文密钥。 -## 5. `GET /api/agnet/health` +## 5. `GET /api/agent/health` ### 响应 @@ -227,8 +227,8 @@ Content-Type: application/json } ], "callback": { - "url": "https://code.xinghanlab.com/api/agnet/callbacks/swarm-events", - "signing_secret_ref": "azkv://heicode-kv.vault.azure.net/secrets/agnet-callback-signing-key", + "url": "https://code.xinghanlab.com/api/agent/callbacks/swarm-events", + "signing_secret_ref": "azkv://heicode-kv.vault.azure.net/secrets/agent-callback-signing-key", "subscribed_events": [ "deployment.status_changed", "task.created", @@ -342,9 +342,9 @@ Agent Manager 创建 Swarm Run 后,必须在 Runtime 内部生成任务图, Manager 回调地址: ```http -POST https://code.xinghanlab.com/api/agnet/callbacks/swarm-events -X-Agnet-Service-Token: -X-Agnet-Event-Id: +POST https://code.xinghanlab.com/api/agent/callbacks/swarm-events +X-Agent-Service-Token: +X-Agent-Event-Id: X-Correlation-ID: Content-Type: application/json ``` @@ -352,8 +352,8 @@ Content-Type: application/json 也支持 HMAC: ```http -X-Agnet-Timestamp: -X-Agnet-Signature: sha256= +X-Agent-Timestamp: +X-Agent-Signature: sha256= ``` HMAC 签名内容: @@ -365,7 +365,7 @@ timestamp + "." + event_id + "." + raw_body 联调前可拉取 Manager 当前接受的事件 schema: ```http -GET https://code.xinghanlab.com/api/agnet/callbacks/swarm-events/schema +GET https://code.xinghanlab.com/api/agent/callbacks/swarm-events/schema ``` ### 通用 callback envelope @@ -647,7 +647,7 @@ Runtime 可通过 `budget.alert` 或 `timeline.updated` 回传用量摘要。 | 步骤 | 操作 | 通过标准 | |---:|---|---| -| 1 | Manager 调 `GET /api/agnet/health` | 返回 healthy | +| 1 | Manager 调 `GET /api/agent/health` | 返回 healthy | | 2 | Manager 调 `POST /api/swarms` | 返回真实 `deployment_id` 和 `swarm_id` | | 3 | Runtime 回调 `deployment.status_changed` | Manager timeline 可见 | | 4 | Runtime 回调 `task.created` | Manager task flow / Agent task map 可见 | diff --git a/docs/integration/AgentManager蜂群Runtime联调待确认与补充要求.md b/docs/integration/AgentManager蜂群Runtime联调待确认与补充要求.md index 6478853..882a761 100644 --- a/docs/integration/AgentManager蜂群Runtime联调待确认与补充要求.md +++ b/docs/integration/AgentManager蜂群Runtime联调待确认与补充要求.md @@ -10,10 +10,10 @@ | 模式 | Runtime | 当前已知地址 | 说明 | |---|---|---|---| -| 普通 sub 敏捷模式 | Agent Manager / Agnet Runtime | `http://20.212.121.126` | 用于普通子 Agent 敏捷开发流程 | +| 普通 sub 敏捷模式 | Agent Manager / Agent Runtime | `http://20.212.121.126` | 用于普通子 Agent 敏捷开发流程 | | 蜂群模式 | HeiCode-Swarm Orchestrator | `http://52.139.240.116:8000` | 用于蜂群任务图、Agent 协作、handoff、task graph | -注意:两套服务可能都提供 `/api/swarms` 这类路径,但业务含义不同。Heicode Manager 后续需要按模式分别配置,不应只用一套 `AGNET_RUNTIME_BASE_URL` 混跑。 +注意:两套服务可能都提供 `/api/swarms` 这类路径,但业务含义不同。Heicode Manager 后续需要按模式分别配置,不应只用一套 `AGENT_RUNTIME_BASE_URL` 混跑。 ## 2. 本次读取到的蜂群 Runtime 新能力 @@ -21,9 +21,9 @@ | 能力 | 路径 | 当前判断 | |---|---|---| -| 健康检查 | `GET /api/agnet/health` | 已提供 | +| 健康检查 | `GET /api/agent/health` | 已提供 | | 创建蜂群 run | `POST /api/swarms` | 已提供 | -| 兼容创建入口 | `POST /api/agnet/deployments` | 已提供 | +| 兼容创建入口 | `POST /api/agent/deployments` | 已提供 | | 查询 swarm 详情 | `GET /api/swarms/{swarm_id}` | 已提供 | | 查询任务图 | `GET /api/swarms/{swarm_id}/tasks` | 已提供 | | 查询日志 | `GET /api/swarms/{swarm_id}/logs` | 已提供 | @@ -48,7 +48,7 @@ runtime contract checks passed 请求: ```bash -curl http://52.139.240.116:8000/api/agnet/health +curl http://52.139.240.116:8000/api/agent/health ``` 返回核心内容: @@ -247,7 +247,7 @@ allow_handoff=false | 项 | 需要提供 / 确认 | 原因 | |---|---|---| | 蜂群 Runtime 调用 token | 已提供并验证可用;后续需要通过安全渠道配置到 Manager,不写入 Git | Manager 调 `POST /api/swarms` 需要 Bearer 鉴权 | -| callback 凭据配置 | Runtime 配置 Manager 认可的 `AGNET_CALLBACK_SERVICE_TOKEN` 或 `AGNET_CALLBACK_SIGNING_SECRET` | Runtime 回调 Manager 必须通过鉴权 | +| callback 凭据配置 | Runtime 配置 Manager 认可的 `AGENT_CALLBACK_SERVICE_TOKEN` 或 `AGENT_CALLBACK_SIGNING_SECRET` | Runtime 回调 Manager 必须通过鉴权 | | 两套部署边界 | 明确普通 sub 继续走 `20.212.121.126`,蜂群走 `52.139.240.116:8000` | 防止普通 sub 和蜂群混用 | | 创建响应字段 | `deployment_id`、`runtime_deployment_id`、`swarm_id`、`status` 字段保持稳定 | Manager 需要保存映射 | | callback deployment id 规则 | callback 中 `deployment_id` 应为 Manager deployment id,`runtime_deployment_id` 为 Runtime id,`swarm_id` 为 Runtime swarm id | Manager 根据这些字段落库和展示 | @@ -255,13 +255,13 @@ allow_handoff=false callback 地址: ```text -https://code.xinghanlab.com/api/agnet/callbacks/swarm-events +https://code.xinghanlab.com/api/agent/callbacks/swarm-events ``` callback schema 查询: ```text -GET https://code.xinghanlab.com/api/agnet/callbacks/swarm-events/schema +GET https://code.xinghanlab.com/api/agent/callbacks/swarm-events/schema ``` 说明:具体 token / signing secret 不应写入 Markdown 或 Git,请通过安全渠道提供。 @@ -340,8 +340,8 @@ Content-Type: application/json ] }, "callback": { - "url": "https://code.xinghanlab.com/api/agnet/callbacks/swarm-events", - "signing_secret_ref": "azkv://heicode-kv.vault.azure.net/secrets/agnet-callback-signing-key", + "url": "https://code.xinghanlab.com/api/agent/callbacks/swarm-events", + "signing_secret_ref": "azkv://heicode-kv.vault.azure.net/secrets/agent-callback-signing-key", "subscribed_events": [ "deployment.status_changed", "task.created", @@ -416,8 +416,8 @@ Heicode Manager 需要保留普通 sub 和蜂群两套 Runtime 配置。建议 ```text # 普通 sub 敏捷模式 -AGNET_RUNTIME_BASE_URL=http://20.212.121.126 -AGNET_RUNTIME_CREATE_PATH=/api/swarms +AGENT_RUNTIME_BASE_URL=http://20.212.121.126 +AGENT_RUNTIME_CREATE_PATH=/api/swarms # 蜂群模式 SWARM_RUNTIME_BASE_URL=http://52.139.240.116:8000 @@ -427,7 +427,7 @@ SWARM_RUNTIME_APPROVAL_DECISION_PATH=/api/swarms/{swarm_id}/approvals/{approval_ SWARM_RUNTIME_SERVICE_TOKEN= ``` -如果短期 Manager 还只有一套 `AGNET_RUNTIME_*`,只能临时切换到蜂群 Runtime 做专项联调,不能同时代表普通 sub 和蜂群都在线。 +如果短期 Manager 还只有一套 `AGENT_RUNTIME_*`,只能临时切换到蜂群 Runtime 做专项联调,不能同时代表普通 sub 和蜂群都在线。 ## 8. 蜂群 MVP 验收口径 diff --git a/docs/integration/Heicode-Manager-PayPal支付接入与计费关系说明.md b/docs/integration/Heicode-Manager-PayPal支付接入与计费关系说明.md index bc97a63..c7df832 100644 --- a/docs/integration/Heicode-Manager-PayPal支付接入与计费关系说明.md +++ b/docs/integration/Heicode-Manager-PayPal支付接入与计费关系说明.md @@ -137,13 +137,13 @@ PayPal 接入时有两种做法: | 文档口径 | 含义 | |---|---| -| `budget.max_tokens` | 本次 Agent / Agnet 部署允许消耗的 token 上限。 | -| `budget.max_cost_usd` | 本次 Agent / Agnet 部署允许消耗的美元成本上限。 | -| `budget.max_duration_sec` | 本次 Agent / Agnet 部署允许运行的时间上限。 | +| `budget.max_tokens` | 本次 Agent / Agent 部署允许消耗的 token 上限。 | +| `budget.max_cost_usd` | 本次 Agent / Agent 部署允许消耗的美元成本上限。 | +| `budget.max_duration_sec` | 本次 Agent / Agent 部署允许运行的时间上限。 | | `billing_context.provider = newapi` | 表示模型调用费用应映射到 NewAPI 用户、Token、Group 或 quota。 | | `agent_runtime` | 表示子 Agent 角色、模型 profile、实例数,不能和 NewAPI 扣费对象混在一起。 | -产品文档明确:子 Agnet 的运行模型属于 Agnet 平台部署配置,不等同于 CodeGW 后台模型供应商配置。NewAPI 负责模型网关、余额、用量、日志和扣费;Agnet 平台负责真实执行和运行态。 +产品文档明确:子 Agent 的运行模型属于 Agent 平台部署配置,不等同于 CodeGW 后台模型供应商配置。NewAPI 负责模型网关、余额、用量、日志和扣费;Agent 平台负责真实执行和运行态。 所以当前要分成两类费用: @@ -378,7 +378,7 @@ Heicode Manager 当前已有余额和订阅计费闭环。PayPal 接入不改变 - Heicode 产品资料包:`docs/product-package/` - NewAPI / CodeGW 边界:`docs/heicode-runtime-auth-newapi-secret-design.md` -- Agnet 请求契约:`docs/integration/agnet-platform-request-contract.md` +- Agent 请求契约:`docs/integration/agent-platform-request-contract.md` - PayPal Orders v2:`https://developer.paypal.com/docs/api/orders/v2/` - PayPal Subscriptions:`https://developer.paypal.com/docs/subscriptions/reference/` - PayPal Webhook 事件:`https://developer.paypal.com/api/rest/webhooks/event-names` diff --git a/docs/integration/agent-manager-swarm-runtime-change-request.md b/docs/integration/agent-manager-swarm-runtime-change-request.md index e8146c7..3efc63d 100644 --- a/docs/integration/agent-manager-swarm-runtime-change-request.md +++ b/docs/integration/agent-manager-swarm-runtime-change-request.md @@ -8,11 +8,11 @@ | 能力 | Manager 状态 | |---|---| -| 用户态创建 | `POST /api/agnet/user/deployments`、`POST /api/swarms` 已可创建 Manager deployment | -| Runtime 创建桥接 | 可通过 `AGNET_RUNTIME_BASE_URL` + `AGNET_RUNTIME_CREATE_PATH` 调用 Runtime | -| Runtime 停止桥接 | 可通过 `AGNET_RUNTIME_STOP_PATH` 停止 Runtime deployment | -| 回调接收 | `POST /api/agnet/callbacks/swarm-events` 已支持事件、artifact、approval、timeline 回调 | -| 回调安全 | 支持 `X-Agnet-Service-Token` 或 `X-Agnet-Signature` HMAC | +| 用户态创建 | `POST /api/agent/user/deployments`、`POST /api/swarms` 已可创建 Manager deployment | +| Runtime 创建桥接 | 可通过 `AGENT_RUNTIME_BASE_URL` + `AGENT_RUNTIME_CREATE_PATH` 调用 Runtime | +| Runtime 停止桥接 | 可通过 `AGENT_RUNTIME_STOP_PATH` 停止 Runtime deployment | +| 回调接收 | `POST /api/agent/callbacks/swarm-events` 已支持事件、artifact、approval、timeline 回调 | +| 回调安全 | 支持 `X-Agent-Service-Token` 或 `X-Agent-Signature` HMAC | | 幂等 | `event_id` / `idempotency_key` 去重 | | 审批结果回传 | 用户 approve/reject 后,Manager 可调用 Runtime approval decision 接口 | | 密钥边界 | Manager 只传 `secret_ref` / `credential_ref`,不传长期明文密钥 | @@ -22,7 +22,7 @@ ### 1. 健康检查 ```http -GET /api/agnet/health +GET /api/agent/health ``` 如果蜂群侧沿用现有 Orchestrator,也可以临时配置为: @@ -59,7 +59,7 @@ Content-Type: application/json Manager 当前也可兼容配置: ```text -AGNET_RUNTIME_CREATE_PATH=/api/swarms +AGENT_RUNTIME_CREATE_PATH=/api/swarms ``` 请求核心字段: @@ -97,8 +97,8 @@ AGNET_RUNTIME_CREATE_PATH=/api/swarms } ], "callback": { - "url": "https://code.xinghanlab.com/api/agnet/callbacks/swarm-events", - "signing_secret_ref": "azkv://heicode-kv.vault.azure.net/secrets/agnet-callback-signing-key", + "url": "https://code.xinghanlab.com/api/agent/callbacks/swarm-events", + "signing_secret_ref": "azkv://heicode-kv.vault.azure.net/secrets/agent-callback-signing-key", "subscribed_events": [ "deployment.status_changed", "task.created", @@ -145,7 +145,7 @@ AGNET_RUNTIME_CREATE_PATH=/api/swarms 默认路径: ```http -POST /api/agnet/deployments/{deployment_id}/stop +POST /api/agent/deployments/{deployment_id}/stop Authorization: Bearer Content-Type: application/json ``` @@ -181,7 +181,7 @@ POST /api/swarms/{swarm_id}/stop 则 Manager 可通过环境变量配置: ```text -AGNET_RUNTIME_STOP_PATH=/api/swarms/{swarm_id}/stop +AGENT_RUNTIME_STOP_PATH=/api/swarms/{swarm_id}/stop ``` ## 三、Runtime 回调 Manager @@ -189,9 +189,9 @@ AGNET_RUNTIME_STOP_PATH=/api/swarms/{swarm_id}/stop 统一回调入口: ```http -POST https://code.xinghanlab.com/api/agnet/callbacks/swarm-events -X-Agnet-Service-Token: -X-Agnet-Event-Id: +POST https://code.xinghanlab.com/api/agent/callbacks/swarm-events +X-Agent-Service-Token: +X-Agent-Event-Id: X-Correlation-ID: Content-Type: application/json ``` @@ -199,8 +199,8 @@ Content-Type: application/json 也支持 HMAC: ```http -X-Agnet-Timestamp: -X-Agnet-Signature: sha256= +X-Agent-Timestamp: +X-Agent-Signature: sha256= ``` 签名内容: @@ -310,7 +310,7 @@ Content-Type: application/json Manager 可配置: ```text -AGNET_RUNTIME_APPROVAL_DECISION_PATH=/api/swarms/{swarm_id}/approvals/{approval_id} +AGENT_RUNTIME_APPROVAL_DECISION_PATH=/api/swarms/{swarm_id}/approvals/{approval_id} ``` 请求: @@ -329,7 +329,7 @@ AGNET_RUNTIME_APPROVAL_DECISION_PATH=/api/swarms/{swarm_id}/approvals/{approval_ "target_role": "ops", "risk_level": "high", "requires_credential": true, - "credential_ref": "lease://agnet/lease_xxx", + "credential_ref": "lease://agent/lease_xxx", "lease_id": "lease_xxx", "lease_expires_at": 1770000000000, "decided_by": "user:22", @@ -356,7 +356,7 @@ AGNET_RUNTIME_APPROVAL_DECISION_PATH=/api/swarms/{swarm_id}/approvals/{approval_ 安全要求: 1. Manager 不会在该请求中发送 `secret_ref`。 -2. 如果审批通过且需要凭证,只发送短期 `credential_ref = lease://agnet/...`。 +2. 如果审批通过且需要凭证,只发送短期 `credential_ref = lease://agent/...`。 3. Runtime 不得把 `credential_ref`、service token、签名密钥写入日志或 artifact metadata。 4. Runtime 收到 `approved` 后继续原高危动作;收到 `rejected` 后停止该动作并回调 `timeline.updated` 或 `task.failed/blocked`。 @@ -378,7 +378,7 @@ AGNET_RUNTIME_APPROVAL_DECISION_PATH=/api/swarms/{swarm_id}/approvals/{approval_ | 当前入口 | 可用性 | 建议 | |---|---|---| -| `GET /health` | 可直接作为健康检查 | Manager `AGNET_RUNTIME_HEALTH_PATH=/health` | +| `GET /health` | 可直接作为健康检查 | Manager `AGENT_RUNTIME_HEALTH_PATH=/health` | | `POST /tasks` | 可临时创建任务 | 只能做兼容桥接,不建议作为最终生产契约 | | `GET /tasks` | 可查任务 | 后续需要映射到 callback/timeline | | `GET /agents` | 可查 Agent | 后续需要回调 agent status | @@ -392,7 +392,7 @@ AGNET_RUNTIME_APPROVAL_DECISION_PATH=/api/swarms/{swarm_id}/approvals/{approval_ 最小验收顺序: -1. Manager 调 `GET /api/agnet/health` 或 `/health` 返回 healthy。 +1. Manager 调 `GET /api/agent/health` 或 `/health` 返回 healthy。 2. Manager 调 Runtime create,拿到 `runtime_deployment_id` 和 `swarm_id`。 3. Runtime 回调 `deployment.status_changed`,Manager timeline 可见。 4. Runtime 回调 `task.claimed`、`task.completed`,Manager timeline 可见。 diff --git a/docs/integration/agnet-platform-request-contract.md b/docs/integration/agent-platform-request-contract.md similarity index 86% rename from docs/integration/agnet-platform-request-contract.md rename to docs/integration/agent-platform-request-contract.md index 8d6b000..05e77bc 100644 --- a/docs/integration/agnet-platform-request-contract.md +++ b/docs/integration/agent-platform-request-contract.md @@ -1,15 +1,15 @@ -# Manager → Agnet 平台接口参数文档 +# Manager → Agent 平台接口参数文档 **版本**: v0.3(P1/P5 联调契约) **生效日期**: 2026-05-03 -**状态**: 联调准备;当前仓库提供 Manager 侧最小验证端点,生产 Agnet 平台部署尚未在本文档中宣称完成。 -**方向**: Heicode Manager 主动请求 Agnet 平台;Agnet 平台返回部署、日志、监控与审计状态。 -**范围**: 创建/停止子 Agent 部署、查询部署、获取事件/日志/监控快照、解析 SK 快照、查询审计日志,以及 Agnet 辅助 NewAPI 重建/部署的参数约定。 +**状态**: 联调准备;当前仓库提供 Manager 侧最小验证端点,生产 Agent 平台部署尚未在本文档中宣称完成。 +**方向**: Heicode Manager 主动请求 Agent 平台;Agent 平台返回部署、日志、监控与审计状态。 +**范围**: 创建/停止子 Agent 部署、查询部署、获取事件/日志/监控快照、解析 SK 快照、查询审计日志,以及 Agent 辅助 NewAPI 重建/部署的参数约定。 -> 2026-05-04 边界修正:Manager 当前不把 `tenant/project` 作为产品、认证或扣费主轴。新请求应使用 `user_context.user_id`、`user_context.channel_id`、`resource_grants[].binding_scope`、`billing_context(newapi)` 和 `agent_runtime(agnet)`。本文中仍出现的 `tenant_id/project_id` 只表示旧字段兼容或历史接口命名,不应作为新功能设计依据。 +> 2026-05-04 边界修正:Manager 当前不把 `tenant/project` 作为产品、认证或扣费主轴。新请求应使用 `user_context.user_id`、`user_context.channel_id`、`resource_grants[].binding_scope`、`billing_context(newapi)` 和 `agent_runtime(agent)`。本文中仍出现的 `tenant_id/project_id` 只表示旧字段兼容或历史接口命名,不应作为新功能设计依据。 **安全红线**: 请求体只允许传资源元数据、权限范围与 `secret_ref`/环境变量名;不得传明文密码、Token、私钥、连接串或云访问密钥。 -> 本文档描述 Manager 对 Agnet 平台的出站集成契约。当前仓库中 `/api/agnet/*` 是 Manager 侧最小控制面/模拟端点,用于校验同一套 payload 结构;生产接入时,Manager 应将下列请求发送到 Agnet 平台网关。 +> 本文档描述 Manager 对 Agent 平台的出站集成契约。当前仓库中 `/api/agent/*` 是 Manager 侧最小控制面/模拟端点,用于校验同一套 payload 结构;生产接入时,Manager 应将下列请求发送到 Agent 平台网关。 ## 0. 概述 @@ -17,18 +17,18 @@ | 接口 | 用途 | 当前性质 | |---|---|---| -| `POST /api/agnet/deployments` | 创建子 Agent/运维任务部署,含 NewAPI 重建/部署场景 | 必需 | -| `GET /api/agnet/deployments` | 查询部署列表 | 必需 | -| `GET /api/agnet/deployments/{deployment_id}` | 查询单个部署详情 | 必需 | -| `POST /api/agnet/deployments/{deployment_id}/stop` | 停止部署或取消排队任务 | 必需 | -| `GET /api/agnet/deployments/{deployment_id}/logs` | 拉取部署日志 | 必需 | -| `GET /api/agnet/deployments/{deployment_id}/logs/stream` | 实时日志 SSE | 可选 | -| `GET /api/agnet/projects/{project_id}/dashboard-snapshot` | 资源作用域监控快照;路径名保留旧兼容,参数值按 `binding_scope` 解释 | 必需 | -| `GET /api/agnet/deployments/{deployment_id}/metrics` | 单部署指标序列 | 建议 | -| `GET /api/agnet/deployments/{deployment_id}/events` | 部署事件 | 必需 | -| `GET /api/agnet/audit-logs` | 审计日志 | 必需 | -| `POST /api/agnet/sk-snapshots/resolve` | 触发 SK 快照解析 | 必需 | -| `GET /api/agnet/deployments/{deployment_id}/sk-snapshots` | 查询 SK 快照 | 必需 | +| `POST /api/agent/deployments` | 创建子 Agent/运维任务部署,含 NewAPI 重建/部署场景 | 必需 | +| `GET /api/agent/deployments` | 查询部署列表 | 必需 | +| `GET /api/agent/deployments/{deployment_id}` | 查询单个部署详情 | 必需 | +| `POST /api/agent/deployments/{deployment_id}/stop` | 停止部署或取消排队任务 | 必需 | +| `GET /api/agent/deployments/{deployment_id}/logs` | 拉取部署日志 | 必需 | +| `GET /api/agent/deployments/{deployment_id}/logs/stream` | 实时日志 SSE | 可选 | +| `GET /api/agent/projects/{project_id}/dashboard-snapshot` | 资源作用域监控快照;路径名保留旧兼容,参数值按 `binding_scope` 解释 | 必需 | +| `GET /api/agent/deployments/{deployment_id}/metrics` | 单部署指标序列 | 建议 | +| `GET /api/agent/deployments/{deployment_id}/events` | 部署事件 | 必需 | +| `GET /api/agent/audit-logs` | 审计日志 | 必需 | +| `POST /api/agent/sk-snapshots/resolve` | 触发 SK 快照解析 | 必需 | +| `GET /api/agent/deployments/{deployment_id}/sk-snapshots` | 查询 SK 快照 | 必需 | > 不在本文档范围:真实 Secret Store 写入、生产 SSH 登录、云账号授权回调、NewAPI 管理后台开放。生产部署动作只有实际执行并通过日志/监控/审计验证后,才能在报告中标记为“已部署”。 @@ -41,20 +41,20 @@ 由部署环境配置,不写入仓库。例如: ```text -AGNET_PLATFORM_BASE_URL=https://agnet-platform.example.com +AGENT_PLATFORM_BASE_URL=https://agent-platform.example.com ``` 联调环境建议使用独立域名或内网网关,示例不得包含真实凭据: ```text -AGNET_PLATFORM_BASE_URL=https://staging-agnet.example.com -MANAGER_SERVICE_TOKEN_SECRET_REF=azkv://heicode-kv.vault.azure.net/secrets/manager-service-agnet-platform-service-token +AGENT_PLATFORM_BASE_URL=https://staging-agent.example.com +MANAGER_SERVICE_TOKEN_SECRET_REF=azkv://heicode-kv.vault.azure.net/secrets/manager-service-agent-platform-service-token ``` 完整路径示例: ```http -POST https://agnet-platform.example.com/api/agnet/deployments +POST https://agent-platform.example.com/api/agent/deployments ``` ### 1.2 通用 Header @@ -133,7 +133,7 @@ POST https://agnet-platform.example.com/api/agnet/deployments ### 2.1 Endpoint ```http -POST /api/agnet/deployments +POST /api/agent/deployments ``` ### 2.2 请求体 @@ -164,11 +164,11 @@ POST /api/agnet/deployments "quota_ref": "newapi_token_or_group_quota_ref" }, "agent_runtime": { - "platform": "agnet", + "platform": "agent", "agents": [ { "role": "builder", - "model_ref": "agnet_model_profile_builder", + "model_ref": "agent_model_profile_builder", "instance_count": 1 } ] @@ -200,7 +200,7 @@ POST /api/agnet/deployments ], "runtime_execution": { "profile_id": "aks-codex-standard", - "cloud_principal_refs": ["principal://users/user_123/agnet-runtime"], + "cloud_principal_refs": ["principal://users/user_123/agent-runtime"], "network_policy_ref": "netpol://bindings/repo_default/restricted-egress" }, "sk_access_policy": { @@ -272,15 +272,15 @@ POST /api/agnet/deployments | 字段 | 类型 | 必填 | 约束/说明 | |---|---|---:|---| | `intent_id` | string | 是 | Manager 侧意图 ID,用于幂等、审计和追踪。 | -| `template_hint` | string | 是 | Agnet 平台选择编排模板的提示,如 `manager-resource-binding`。 | +| `template_hint` | string | 是 | Agent 平台选择编排模板的提示,如 `manager-resource-binding`。 | | `objective` | string | 是 | 本次部署目标,应是自然语言但不能含密钥。 | | `risk_level` | enum | 是 | `low` / `medium` / `high`。高风险必须携带客户端审批证据;缺失或不匹配时只能只读或拒绝执行。 | | `budget.max_tokens` | int | 是 | 当前策略上限建议不超过 `500000`。 | | `budget.max_cost_usd` | number | 是 | 当前策略上限建议不超过 `200`。 | | `budget.max_duration_sec` | int | 是 | 当前策略上限建议不超过 `86400`。 | -| `user_context` | object | 建议 | 登录用户上下文,优先使用 Heicode/Agnet 登录返回的 `user.id`/`channelId`。 | -| `billing_context` | object | 条件 | NewAPI 扣费上下文;只表达 user/token/group/quota 映射,不表达子 Agnet 模型或实例数。 | -| `agent_runtime` | object | 条件 | Agnet 平台运行时上下文;表达子 Agnet 角色、模型 profile 和实例数,不承载 NewAPI key 或扣费对象。 | +| `user_context` | object | 建议 | 登录用户上下文,优先使用 Heicode/Agent 登录返回的 `user.id`/`channelId`。 | +| `billing_context` | object | 条件 | NewAPI 扣费上下文;只表达 user/token/group/quota 映射,不表达子 Agent 模型或实例数。 | +| `agent_runtime` | object | 条件 | Agent 平台运行时上下文;表达子 Agent 角色、模型 profile 和实例数,不承载 NewAPI key 或扣费对象。 | | `constraints.allowed_model_ids` | string[] | 否 | agent 的 `default_model_id` 如填写,必须在此列表内。 | | `metadata.tenant_id` | string | 否 | 旧兼容字段;新实现不得作为产品租户边界。 | | `metadata.project_id` | string | 否 | 旧兼容字段;新实现不得作为项目账本边界。 | @@ -297,10 +297,10 @@ POST /api/agnet/deployments | `billing_context.newapi_user_ref` | string | 否 | NewAPI 用户映射引用,不是 NewAPI key。 | | `billing_context.newapi_group` | string | 否 | NewAPI Group 映射,用于额度或策略选择。 | | `billing_context.quota_ref` | string | 否 | Token 或 Group 额度引用,不得包含真实 Token 原文。 | -| `agent_runtime.platform` | enum | 条件 | 当前只允许 `agnet`。 | +| `agent_runtime.platform` | enum | 条件 | 当前只允许 `agent`。 | | `agent_runtime.agents[].role` | string | 条件 | 必须匹配 `agents[].role_template`。 | -| `agent_runtime.agents[].model_ref` | string | 条件 | Agnet 平台模型 profile 引用;不是 NewAPI 扣费字段。 | -| `agent_runtime.agents[].instance_count` | int | 条件 | 子 Agnet 实例数量,必须大于 0。 | +| `agent_runtime.agents[].model_ref` | string | 条件 | Agent 平台模型 profile 引用;不是 NewAPI 扣费字段。 | +| `agent_runtime.agents[].instance_count` | int | 条件 | 子 Agent 实例数量,必须大于 0。 | #### agents[] @@ -330,7 +330,7 @@ POST /api/agnet/deployments | 字段 | 类型 | 必填 | 说明 | |---|---|---:|---| -| `profile_id` | string | 条件必填 | Agnet 平台运行规格,如 AKS profile。 | +| `profile_id` | string | 条件必填 | Agent 平台运行规格,如 AKS profile。 | | `cloud_principal_refs` | string[] | 否 | 运行身份引用,不是明文凭据。 | | `network_policy_ref` | string | 否 | 网络策略引用,用于限制出站/入站。 | @@ -354,9 +354,9 @@ POST /api/agnet/deployments | `secret_ref` | string | 条件必填 | `git`、`sk`、`cloud_account`、`cloud_resource` 必填;`project_doc` 可为空。 | | `audit` | object | 否 | 审计上下文;不得含密钥字段。 | -### 2.4 典型场景:Agnet 辅助 NewAPI 重建/部署 +### 2.4 典型场景:Agent 辅助 NewAPI 重建/部署 -当 Manager 需要让 Agnet 平台协助重建或部署 NewAPI 时,仍使用 `POST /api/agnet/deployments`,但必须把任务表达为受控运维部署,不得把 VM、PostgreSQL、Redis、NewAPI key 等真实凭据写入请求体。 +当 Manager 需要让 Agent 平台协助重建或部署 NewAPI 时,仍使用 `POST /api/agent/deployments`,但必须把任务表达为受控运维部署,不得把 VM、PostgreSQL、Redis、NewAPI key 等真实凭据写入请求体。 请求体示例: @@ -394,7 +394,7 @@ POST /api/agnet/deployments "default_model_id": "gpt-5.4", "runtime_execution": { "profile_id": "aks-codex-ops", - "cloud_principal_refs": ["principal://users/user_123/agnet-ops"], + "cloud_principal_refs": ["principal://users/user_123/agent-ops"], "network_policy_ref": "netpol://bindings/newapi-prod/ops-egress" }, "resource_grants": [ @@ -458,7 +458,7 @@ POST /api/agnet/deployments } ``` -Agnet 平台返回的部署详情、日志、监控和审计中应至少能证明:构建版本/commit、服务重启结果、健康检查结果、资源使用情况、失败回滚状态。未执行真实 SSH/生产动作时,只能返回 `phase=planned` 或 `phase=pending_approval`。 +Agent 平台返回的部署详情、日志、监控和审计中应至少能证明:构建版本/commit、服务重启结果、健康检查结果、资源使用情况、失败回滚状态。未执行真实 SSH/生产动作时,只能返回 `phase=planned` 或 `phase=pending_approval`。 NewAPI 重建/部署的完成判定必须同时满足: @@ -494,7 +494,7 @@ NewAPI 重建/部署的完成判定必须同时满足: ### 3.1 查询部署列表 ```http -GET /api/agnet/deployments?user_id=user_123&binding_scope=repo_default +GET /api/agent/deployments?user_id=user_123&binding_scope=repo_default ``` 返回: @@ -520,15 +520,15 @@ GET /api/agnet/deployments?user_id=user_123&binding_scope=repo_default ### 3.2 查询单个部署 ```http -GET /api/agnet/deployments/{deployment_id} +GET /api/agent/deployments/{deployment_id} ``` -返回应包含部署状态、phase、agent_instances、最近错误、资源授权摘要和预算消耗摘要。Agnet 平台返回时必须对 `secret_ref` 以外的凭据信息做脱敏;原则上不返回任何明文凭据。 +返回应包含部署状态、phase、agent_instances、最近错误、资源授权摘要和预算消耗摘要。Agent 平台返回时必须对 `secret_ref` 以外的凭据信息做脱敏;原则上不返回任何明文凭据。 ### 3.3 停止部署 ```http -POST /api/agnet/deployments/{deployment_id}/stop +POST /api/agent/deployments/{deployment_id}/stop ``` 请求体可为空;如需原因可扩展: @@ -565,12 +565,12 @@ POST /api/agnet/deployments/{deployment_id}/stop --- -## 4. 日志接口(Manager 拉取 Agnet 平台) +## 4. 日志接口(Manager 拉取 Agent 平台) ### 4.1 获取部署日志 ```http -GET /api/agnet/deployments/{deployment_id}/logs?agent_instance_id=agi_abc123&stream=stdout&since=2026-05-02T00:00:00Z&limit=200&cursor=cur_001 +GET /api/agent/deployments/{deployment_id}/logs?agent_instance_id=agi_abc123&stream=stdout&since=2026-05-02T00:00:00Z&limit=200&cursor=cur_001 ``` Query: @@ -609,14 +609,14 @@ Query: 日志要求: -- Agnet 平台必须在返回前完成密钥脱敏。 +- Agent 平台必须在返回前完成密钥脱敏。 - `message` 不得包含密码、Token、私钥、连接串、云访问密钥。 - Manager 只保存必要摘要和审计索引;长日志建议落对象存储并设置保留期。 ### 4.2 实时日志流(可选) ```http -GET /api/agnet/deployments/{deployment_id}/logs/stream?agent_instance_id=agi_abc123 +GET /api/agent/deployments/{deployment_id}/logs/stream?agent_instance_id=agi_abc123 Accept: text/event-stream ``` @@ -646,12 +646,12 @@ SSE 事件类型: --- -## 5. 监控接口(Manager 拉取 Agnet 平台) +## 5. 监控接口(Manager 拉取 Agent 平台) ### 5.1 项目监控快照 ```http -GET /api/agnet/projects/{binding_scope}/dashboard-snapshot?window=1h +GET /api/agent/projects/{binding_scope}/dashboard-snapshot?window=1h ``` 响应: @@ -689,7 +689,7 @@ GET /api/agnet/projects/{binding_scope}/dashboard-snapshot?window=1h ### 5.2 单部署监控快照(建议平台实现) ```http -GET /api/agnet/deployments/{deployment_id}/metrics?window=15m&step=60s +GET /api/agent/deployments/{deployment_id}/metrics?window=15m&step=60s ``` 响应: @@ -725,7 +725,7 @@ GET /api/agnet/deployments/{deployment_id}/metrics?window=15m&step=60s ### 6.1 部署事件 ```http -GET /api/agnet/deployments/{deployment_id}/events?since=2026-05-02T00:00:00Z&limit=200&cursor=cur_001 +GET /api/agent/deployments/{deployment_id}/events?since=2026-05-02T00:00:00Z&limit=200&cursor=cur_001 ``` 响应: @@ -768,7 +768,7 @@ GET /api/agnet/deployments/{deployment_id}/events?since=2026-05-02T00:00:00Z&lim ### 6.2 审计日志 ```http -GET /api/agnet/audit-logs?user_id=user_123&binding_scope=repo_default&actor=agnet_control_plane&action=deployment.accepted&since=2026-05-02T00:00:00Z&limit=200&cursor=cur_001 +GET /api/agent/audit-logs?user_id=user_123&binding_scope=repo_default&actor=agent_control_plane&action=deployment.accepted&since=2026-05-02T00:00:00Z&limit=200&cursor=cur_001 ``` 响应: @@ -780,7 +780,7 @@ GET /api/agnet/audit-logs?user_id=user_123&binding_scope=repo_default&actor=agne "items": [ { "audit_id": "aud_001", - "actor": "agnet_control_plane", + "actor": "agent_control_plane", "action": "deployment.accepted", "resource": "dep_abc123", "user_id": "user_123", @@ -805,7 +805,7 @@ GET /api/agnet/audit-logs?user_id=user_123&binding_scope=repo_default&actor=agne ### 7.1 触发解析/刷新 ```http -POST /api/agnet/sk-snapshots/resolve +POST /api/agent/sk-snapshots/resolve ``` 请求: @@ -842,7 +842,7 @@ POST /api/agnet/sk-snapshots/resolve ### 7.2 查询部署 SK 快照 ```http -GET /api/agnet/deployments/{deployment_id}/sk-snapshots +GET /api/agent/deployments/{deployment_id}/sk-snapshots ``` Query: @@ -893,17 +893,17 @@ Query: ## 8. 安全校验清单 -Manager 发给 Agnet 平台前必须执行: +Manager 发给 Agent 平台前必须执行: 1. `user_id`、`binding_scope`、`target_role` 与部署计划一致。 2. 凭据型资源只传 `secret_ref`,不传明文凭据。 3. `metadata`、`constraints`、`audit` 的 key 中不得出现 `password`、`token`、`secret`、`private_key`、`access_key`、`credential` 等敏感词。 4. `repo_url` 不得包含用户名、密码或访问 Token。 -5. `permission_scope` 使用最小权限;生产写操作必须携带客户端审批记录,Agnet 平台不得自行补批。 -6. 高风险操作(生产部署、云资源修改、删除、扩容)必须设置 `risk_level=high`,Agnet 平台执行前只校验客户端审批证据。 +5. `permission_scope` 使用最小权限;生产写操作必须携带客户端审批记录,Agent 平台不得自行补批。 +6. 高风险操作(生产部署、云资源修改、删除、扩容)必须设置 `risk_level=high`,Agent 平台执行前只校验客户端审批证据。 7. 所有日志/事件/审计返回给 Manager 前必须脱敏。 -Agnet 平台不承担高危操作审批主体。审批只发生在客户端;Agnet 平台只能在执行前校验以下字段和策略是否一致: +Agent 平台不承担高危操作审批主体。审批只发生在客户端;Agent 平台只能在执行前校验以下字段和策略是否一致: | 校验项 | 要求 | |---|---| @@ -914,14 +914,14 @@ Agnet 平台不承担高危操作审批主体。审批只发生在客户端;Ag | `risk_level` | 高危资源写入、生产部署、云资源修改、删除和扩容必须为 `high`。 | | 策略 | 平台 policy、Key Vault 访问策略、Kubernetes/Workload Identity、网络策略和最小权限约束均必须允许本次动作。 | -任一校验不通过时,Agnet 平台应返回 `POLICY_REJECTED` 或 `FORBIDDEN_SCOPE`,不得发起额外批准流程。 +任一校验不通过时,Agent 平台应返回 `POLICY_REJECTED` 或 `FORBIDDEN_SCOPE`,不得发起额外批准流程。 ### 8.1 字段级约束速查 | 对象/接口 | 必填最小集合 | 禁止内容 | |---|---|---| | `orchestration_plan` | `intent_id`、`template_hint`、`objective`、`risk_level`、`budget`、`user_context`、`billing_context`、`agent_runtime`、`metadata.correlation_id`、`agents[]` | 密钥、连接串、真实主机登录密码、NewAPI key 原文。 | -| `agents[]` | `role_template`、`goal` | 让子 Agent 绕过 Manager/Agnet 审计的指令。 | +| `agents[]` | `role_template`、`goal` | 让子 Agent 绕过 Manager/Agent 审计的指令。 | | `runtime_execution` | 任一字段存在时 `profile_id` 必填 | 明文 kubeconfig、SSH key、云访问密钥。 | | `resource_grants[]` | `grant_id`、`resource_id`、`resource_type`、`user_id`、`binding_scope`、`target_role`、`target_agent_ref`、`permission_scope`、`status` | 明文 `password`、`token`、`private_key`、`access_key`、`credential`、数据库 DSN。 | | 日志/事件/审计返回 | `request_id` 或 `correlation_id`,以及发生时间 | 未脱敏命令行、环境变量 dump、密钥片段。 | @@ -934,7 +934,7 @@ Agnet 平台不承担高危操作审批主体。审批只发生在客户端;Ag - `risk_level=high` 的生产运维任务包含 `approval_id` 和回滚引用。 - 日志、事件、监控、审计接口都能通过 `correlation_id` 串联。 - NewAPI 重建/部署只在实际执行并通过健康检查后标记为已部署;未执行时状态只能是 `planned`、`pending_approval`、`accepted` 或 `running`。 -- Manager 本地 `/api/agnet/*` 占位端点通过 payload 校验不等于生产 Agnet 平台已上线。 +- Manager 本地 `/api/agent/*` 占位端点通过 payload 校验不等于生产 Agent 平台已上线。 --- @@ -944,18 +944,18 @@ Agnet 平台不承担高危操作审批主体。审批只发生在客户端;Ag | Manager 路由 | 用途 | |---|---| -| `POST /api/agnet/deployments` | 校验并接受 orchestration_plan。 | -| `GET /api/agnet/deployments` | 按 user/binding scope 查询部署。 | -| `GET /api/agnet/deployments/:deployment_id` | 查询部署详情。 | -| `POST /api/agnet/deployments/:deployment_id/stop` | 停止部署。 | -| `GET /api/agnet/deployments/:deployment_id/logs` | 查询脱敏日志占位/联调日志。 | -| `GET /api/agnet/deployments/:deployment_id/metrics` | 查询单部署指标占位/联调指标。 | -| `GET /api/agnet/deployments/:deployment_id/events` | 查询事件。 | -| `POST /api/agnet/sk-snapshots/resolve` | 解析 SK 快照。 | -| `GET /api/agnet/deployments/:deployment_id/sk-snapshots` | 查询 SK 快照。 | -| `GET /api/agnet/projects/:project_id/dashboard-snapshot` | 资源作用域监控快照;路径名保留旧兼容。 | -| `GET /api/agnet/audit-logs` | 审计日志。 | +| `POST /api/agent/deployments` | 校验并接受 orchestration_plan。 | +| `GET /api/agent/deployments` | 按 user/binding scope 查询部署。 | +| `GET /api/agent/deployments/:deployment_id` | 查询部署详情。 | +| `POST /api/agent/deployments/:deployment_id/stop` | 停止部署。 | +| `GET /api/agent/deployments/:deployment_id/logs` | 查询脱敏日志占位/联调日志。 | +| `GET /api/agent/deployments/:deployment_id/metrics` | 查询单部署指标占位/联调指标。 | +| `GET /api/agent/deployments/:deployment_id/events` | 查询事件。 | +| `POST /api/agent/sk-snapshots/resolve` | 解析 SK 快照。 | +| `GET /api/agent/deployments/:deployment_id/sk-snapshots` | 查询 SK 快照。 | +| `GET /api/agent/projects/:project_id/dashboard-snapshot` | 资源作用域监控快照;路径名保留旧兼容。 | +| `GET /api/agent/audit-logs` | 审计日志。 | -生产对接时,Manager 应把相同契约的请求发送给 Agnet 平台;本地 Manager 端点仅作为最小验证与控制面占位,不代表所有日志/监控平台能力已完整实现。 +生产对接时,Manager 应把相同契约的请求发送给 Agent 平台;本地 Manager 端点仅作为最小验证与控制面占位,不代表所有日志/监控平台能力已完整实现。 -当前本地 `logs` 与 `metrics` 端点只返回脱敏占位/联调数据,用于验证 Manager ↔ Agnet payload、路由和验收流程。生产级实时日志流 `GET /api/agnet/deployments/{deployment_id}/logs/stream` 仍属于 Agnet 平台能力;Manager 不得把“本地占位通过”误报为“生产日志/监控已上线”。 +当前本地 `logs` 与 `metrics` 端点只返回脱敏占位/联调数据,用于验证 Manager ↔ Agent payload、路由和验收流程。生产级实时日志流 `GET /api/agent/deployments/{deployment_id}/logs/stream` 仍属于 Agent 平台能力;Manager 不得把“本地占位通过”误报为“生产日志/监控已上线”。 diff --git a/docs/integration/heicode-desktop-sub-agile-api.md b/docs/integration/heicode-desktop-sub-agile-api.md index 6516cd8..a7ea667 100644 --- a/docs/integration/heicode-desktop-sub-agile-api.md +++ b/docs/integration/heicode-desktop-sub-agile-api.md @@ -45,7 +45,7 @@ Manager 生产地址:`https://code.xinghanlab.com` 重要修正: -1. 桌面客户端不要再使用 `agnet-model-builder`、`agnet-model-reviewer`、`agnet-model-product` 这类占位模型名。生产 NewAPI 没有这些模型,会返回 `No available channel for model ...`。 +1. 桌面客户端不要再使用 `agent-model-builder`、`agent-model-reviewer`、`agent-model-product` 这类占位模型名。生产 NewAPI 没有这些模型,会返回 `No available channel for model ...`。 2. 普通 sub 当前优先建议使用生产已复核通过的 `gpt-5.4`。`claude-sonnet-4-6` 仍可作为 NewAPI 模型存在,但当前普通 sub Runtime 若按普通 `/v1/chat/completions` 方式调用 Claude Code 类模型,可能返回上游 400;客户端不要把该错误误判为加密或 Manager 创建失败。 3. `orchestration_plan.metadata.correlation_id` 是创建 deployment 的必填字段,必须放在 `orchestration_plan` 内,不是顶层 `metadata`。 4. 成功态不能只看 `status=completed`。客户端还应确认 `runtime_state=completed`、`artifacts.length > 0`、artifact 不是失败摘要、`tokens_used > 0`,并优先展示可下载的业务交付物。 @@ -56,7 +56,7 @@ Manager 生产地址:`https://code.xinghanlab.com` 桌面客户端负责用户主体验:输入想法、回答追问、持续推进任务、查看子环节反馈、处理高危审批、接收交付结果。 -Manager 负责辅助控制面:任务草稿桥接、资源/权限、Agnet deployment、状态/timeline/artifact/SK 查询、审批记录和短期凭证 lease。 +Manager 负责辅助控制面:任务草稿桥接、资源/权限、Agent deployment、状态/timeline/artifact/SK 查询、审批记录和短期凭证 lease。 本文只描述普通 sub 敏捷流程,不包含蜂群 task graph、claim、heartbeat、handoff 等蜂群模式能力。 @@ -67,7 +67,7 @@ Manager 负责辅助控制面:任务草稿桥接、资源/权限、Agnet deplo | API 前缀 | 用途 | 认证 | |---|---|---| | `/api/heicode-auth/api/user/tasks/*` | HeicodeTask 任务编排代理,创建任务、追问、查询任务 | V2 加密 body + `Authorization: Bearer ` | -| `/api/agnet/user/*` | Manager 用户态 Agnet 控制面,deployment、timeline、artifact、审批 | V2 加密 body;未加密 Web 控制台请求继续使用 Manager session + `New-Api-User` | +| `/api/agent/user/*` | Manager 用户态 Agent 控制面,deployment、timeline、artifact、审批 | V2 加密 body;未加密 Web 控制台请求继续使用 Manager session + `New-Api-User` | | `/api/swarms` | 蜂群模式创建入口 / Runtime adapter 入口 | V2 加密 body;未加密 Web 控制台请求继续使用 Manager session + `New-Api-User` | | `/api/user/self` | 查询当前 Manager 用户 | Manager 登录 session cookie | @@ -79,7 +79,7 @@ Manager 负责辅助控制面:任务草稿桥接、资源/权限、Agnet deplo | API 前缀 | V2 加密 body | 说明 | |---|---|---| -| `/api/agnet/user/*` | 支持 | Manager 解密并校验设备签名后,按当前设备对应用户执行 | +| `/api/agent/user/*` | 支持 | Manager 解密并校验设备签名后,按当前设备对应用户执行 | | `/api/swarms` | 支持 | Manager 解密并校验设备签名后,按当前设备对应用户创建蜂群/Runtime adapter deployment | | `/api/heicode-auth/*` | 支持 | Manager 解密并校验设备签名后,把明文 body 代理给上游 HeicodeTask 服务;仍需携带 `heicode_access_token` | | 浏览器后台普通页面请求 | 兼容未加密 JSON | 不影响现有 Manager Web 控制台 | @@ -127,14 +127,14 @@ Authorization: Bearer 客户端实现要求: 1. sub 流程调用不要新增一套加密协议,直接复用模型调用的 `encryptedFetch` / V2 设备签名实现。 -2. `aad` 和 `canonical` 中的 `path_with_query` 必须是 Manager 实际收到的 path,例如 `/api/agnet/user/tasks/task-1/deployment-draft`,不能把 origin 写进去。 +2. `aad` 和 `canonical` 中的 `path_with_query` 必须是 Manager 实际收到的 path,例如 `/api/agent/user/tasks/task-1/deployment-draft`,不能把 origin 写进去。 3. 加密前的 plaintext 必须是最终 JSON body;签名里的 `sha256_hex(plaintext_body)` 必须和该 JSON 字节完全一致。 4. 每次请求必须使用新的 `X-Heicode-Nonce` 和新的 X25519 ephemeral key。 5. V2 请求失败时优先读取 `X-Heicode-Auth-Error` 和 `X-Heicode-Server-Time`,用于提示设备未配对、时间漂移、nonce 重放、签名错误或解密失败。 ### 2.3 Manager 用户态 Header -未加密 Web 控制台请求调用 `/api/agnet/user/*` 或 `/api/swarms` 时必须带: +未加密 Web 控制台请求调用 `/api/agent/user/*` 或 `/api/swarms` 时必须带: ```http Cookie: session= @@ -145,7 +145,7 @@ Accept: application/json `New-Api-User` 必须等于当前登录用户 ID,否则会返回未授权。 -使用 V2 加密 body 时,`/api/agnet/user/*` 和 `/api/swarms` 不依赖浏览器 session cookie,也不需要 `New-Api-User`;Manager 会从设备绑定 token 中解析用户身份。为兼容当前 Web 控制台,未加密请求仍按 session cookie + `New-Api-User` 处理。 +使用 V2 加密 body 时,`/api/agent/user/*` 和 `/api/swarms` 不依赖浏览器 session cookie,也不需要 `New-Api-User`;Manager 会从设备绑定 token 中解析用户身份。为兼容当前 Web 控制台,未加密请求仍按 session cookie + `New-Api-User` 处理。 ### 2.4 蜂群入口加密边界 @@ -153,8 +153,8 @@ Accept: application/json | 接口 | 所属模式 | 加密要求 | |---|---|---| -| `POST /api/agnet/user/tasks/{task_id}/deployment-draft` | 普通 sub 敏捷 | V2 body 加密 | -| `POST /api/agnet/user/deployments` | 普通 sub 敏捷 | V2 body 加密 | +| `POST /api/agent/user/tasks/{task_id}/deployment-draft` | 普通 sub 敏捷 | V2 body 加密 | +| `POST /api/agent/user/deployments` | 普通 sub 敏捷 | V2 body 加密 | | `POST /api/swarms` | 蜂群模式 | V2 body 加密 | 注意:本文后续章节仍只描述普通 sub 敏捷主流程;蜂群 task graph、claim、heartbeat、handoff、approval decision 等字段以单独蜂群对接文档为准。 @@ -190,8 +190,8 @@ Accept: application/json 1. 桌面端确认 Manager 登录态,获取 /api/user/self 2. 创建 HeicodeTask:POST /api/heicode-auth/api/user/tasks/intent 3. 如果 status=configuring,回答追问:POST /api/heicode-auth/api/user/tasks/{task_id}/answer -4. 当任务卡生成后,创建 deployment draft:POST /api/agnet/user/tasks/{task_id}/deployment-draft -5. 创建 Manager deployment:POST /api/agnet/user/deployments +4. 当任务卡生成后,创建 deployment draft:POST /api/agent/user/tasks/{task_id}/deployment-draft +5. 创建 Manager deployment:POST /api/agent/user/deployments 6. 轮询 deployment detail / events / timeline 7. 查询 runtime-diagnostics,区分 callback 已到、Runtime 状态、失败 Agent 和兜底摘要 artifact 8. 查询 artifacts 列表;如果需要完整文件,调用 artifact content 代理接口下载 @@ -205,14 +205,14 @@ Accept: application/json 客户端展示普通 sub 结果时,不能只看 deployment `status=completed`。Manager 已提供只读诊断接口,用来识别 Runtime 是否真的完成、是否有 failed agent、是否只返回兜底摘要 artifact。 ```http -GET /api/agnet/user/deployments/{deployment_id}/runtime-diagnostics +GET /api/agent/user/deployments/{deployment_id}/runtime-diagnostics ``` 返回核心字段: | 字段 | 说明 | |---|---| -| `runtime_mode` | `agnet` 表示普通 sub Runtime;`swarm` 表示蜂群 Runtime | +| `runtime_mode` | `agent` 表示普通 sub Runtime;`swarm` 表示蜂群 Runtime | | `runtime_swarm_id` / `runtime_deployment_id` | Manager 保存的 Runtime 映射 | | `data_source` | 当前诊断来源,正常为 `runtime_status` | | `status` / `phase` | Runtime 直接返回的状态和阶段 | @@ -235,7 +235,7 @@ GET /api/agnet/user/deployments/{deployment_id}/runtime-diagnostics 1. deployment detail / timeline / artifacts 仍按原接口展示。 2. 若 `warnings` 包含 `runtime_summary_artifact_only`,需要提示“当前没有最终交付产物,请查看运行日志/等待 Runtime 修复”。 -3. 若 `runtime_mode=agnet`,按普通 sub 敏捷展示;若 `runtime_mode=swarm`,按蜂群模式展示任务图/Agent 编队,不能混用两套文案。 +3. 若 `runtime_mode=agent`,按普通 sub 敏捷展示;若 `runtime_mode=swarm`,按蜂群模式展示任务图/Agent 编队,不能混用两套文案。 4. 该接口只读,失败时不应中断已有 timeline/artifact 展示。 ### 3.2 Artifact 完整内容下载 @@ -245,13 +245,13 @@ GET /api/agnet/user/deployments/{deployment_id}/runtime-diagnostics 列表: ```http -GET /api/agnet/user/deployments/{deployment_id}/artifacts +GET /api/agent/user/deployments/{deployment_id}/artifacts ``` 下载: ```http -GET /api/agnet/user/deployments/{deployment_id}/artifacts/{artifact_id}/content +GET /api/agent/user/deployments/{deployment_id}/artifacts/{artifact_id}/content ``` Manager 行为: @@ -339,26 +339,26 @@ function classifyArtifact(item: Artifact, content: string, contentType?: string) | Manager 版本 | `1.4.19` | | Agent Manager Runtime | `http://20.212.121.126` | | Runtime health | `healthy` | -| Manager callback | `https://code.xinghanlab.com/api/agnet/callbacks/swarm-events` | +| Manager callback | `https://code.xinghanlab.com/api/agent/callbacks/swarm-events` | 已验证成功的链路: ```text Manager 登录 -> /api/user/self --> /api/agnet/runtime/health --> /api/agnet/user/tasks/{task_id}/deployment-draft --> /api/agnet/user/deployments +-> /api/agent/runtime/health +-> /api/agent/user/tasks/{task_id}/deployment-draft +-> /api/agent/user/deployments -> Manager 调 Agent Manager Runtime create -> Runtime 自动 callback 到 Manager --> /api/agnet/user/deployments/{deployment_id} --> /api/agnet/user/deployments/{deployment_id}/metrics --> /api/agnet/user/deployments/{deployment_id}/events --> /api/agnet/user/deployments/{deployment_id}/logs --> /api/agnet/user/deployments/{deployment_id}/artifacts --> /api/agnet/user/deployments/{deployment_id}/sk-snapshots --> /api/agnet/user/deployments/{deployment_id}/timeline --> /api/agnet/user/deployments/{deployment_id}/stop +-> /api/agent/user/deployments/{deployment_id} +-> /api/agent/user/deployments/{deployment_id}/metrics +-> /api/agent/user/deployments/{deployment_id}/events +-> /api/agent/user/deployments/{deployment_id}/logs +-> /api/agent/user/deployments/{deployment_id}/artifacts +-> /api/agent/user/deployments/{deployment_id}/sk-snapshots +-> /api/agent/user/deployments/{deployment_id}/timeline +-> /api/agent/user/deployments/{deployment_id}/stop ``` 最新生产烟测 ID: @@ -431,7 +431,7 @@ Manager 登录 客户端处理: - 保存 `data.id`。 -- 后续 `/api/agnet/user/*` 请求带 `New-Api-User: `。 +- 后续 `/api/agent/user/*` 请求带 `New-Api-User: `。 ## 5. HeicodeTask 任务编排 @@ -594,11 +594,11 @@ Authorization: Bearer - `status=awaiting_approval`:轮询审批接口。 - `status=completed/failed/paused`:停止高频轮询。 -## 6. 从任务生成 Agnet Deployment Draft +## 6. 从任务生成 Agent Deployment Draft -### `POST /api/agnet/user/tasks/{task_id}/deployment-draft` +### `POST /api/agent/user/tasks/{task_id}/deployment-draft` -把 HeicodeTask 快照转换成 Manager 可创建的 Agnet orchestration plan。 +把 HeicodeTask 快照转换成 Manager 可创建的 Agent orchestration plan。 生产已验证:客户端只要能提供 task snapshot,就可以不依赖 Manager 再去拉 task,直接调用本接口生成 draft。 @@ -685,7 +685,7 @@ Authorization: Bearer | `sub_mode` | string | 是 | `agile` 或 `waterfall`,桌面客户端默认传 `agile` | | `binding_scope` | string | 否 | 建议 `task-` | | `role_templates` | string[] | 否 | 默认 `["backend"]` | -| `default_model_id` | string | 否 | 子 Agnet 默认运行模型;生产默认建议 `claude-sonnet-4-6` | +| `default_model_id` | string | 否 | 子 Agent 默认运行模型;生产默认建议 `claude-sonnet-4-6` | | `budget` | object | 否 | 不传使用默认预算 | | `resource_grants` | array | 否 | 不传时 Manager 生成只读 task context grant | @@ -717,7 +717,7 @@ Authorization: Bearer "newapi_group": "default" }, "agent_runtime": { - "platform": "agnet", + "platform": "agent", "agents": [ { "role": "backend", @@ -747,7 +747,7 @@ Authorization: Bearer ## 7. 创建 Manager Deployment -### `POST /api/agnet/user/deployments` +### `POST /api/agent/user/deployments` 使用上一步 `orchestration_plan` 创建 Manager deployment。 @@ -783,7 +783,7 @@ Authorization: Bearer "newapi_group": "default" }, "agent_runtime": { - "platform": "agnet", + "platform": "agent", "agents": [ { "role": "backend", @@ -910,7 +910,7 @@ Authorization: Bearer ### 8.1 列表 -#### `GET /api/agnet/user/deployments` +#### `GET /api/agent/user/deployments` 响应: @@ -938,13 +938,13 @@ Authorization: Bearer ### 8.2 详情 -#### `GET /api/agnet/user/deployments/{deployment_id}` +#### `GET /api/agent/user/deployments/{deployment_id}` 响应字段同列表单项,包含完整 `orchestration_plan`。 ### 8.3 停止 -#### `POST /api/agnet/user/deployments/{deployment_id}/stop` +#### `POST /api/agent/user/deployments/{deployment_id}/stop` 请求: @@ -971,7 +971,7 @@ Authorization: Bearer ### 9.1 Events -#### `GET /api/agnet/user/deployments/{deployment_id}/events` +#### `GET /api/agent/user/deployments/{deployment_id}/events` 用于展示 deployment 事件流。 @@ -996,7 +996,7 @@ Authorization: Bearer ### 9.2 Logs -#### `GET /api/agnet/user/deployments/{deployment_id}/logs` +#### `GET /api/agent/user/deployments/{deployment_id}/logs` 用于展示最近日志。当前 Manager 未接真实 Runtime 时主要是审计日志。 @@ -1020,7 +1020,7 @@ Authorization: Bearer ### 9.3 Metrics -#### `GET /api/agnet/user/deployments/{deployment_id}/metrics` +#### `GET /api/agent/user/deployments/{deployment_id}/metrics` 用于展示成本、耗时、token、资源指标。真实 Runtime 未接入时可能为空或为本地占位。 @@ -1039,7 +1039,7 @@ Authorization: Bearer ### 9.4 Artifacts -#### `GET /api/agnet/user/deployments/{deployment_id}/artifacts` +#### `GET /api/agent/user/deployments/{deployment_id}/artifacts` 查询中间交付物和最终交付物摘要。注意:列表接口只返回摘要和引用,不能证明代码内容有效;客户端需要按需调用 content 接口并按“Artifact 展示和有效性判断”继续分类。 @@ -1070,7 +1070,7 @@ Authorization: Bearer } ``` -#### `GET /api/agnet/user/deployments/{deployment_id}/artifacts/{artifact_id}/content` +#### `GET /api/agent/user/deployments/{deployment_id}/artifacts/{artifact_id}/content` 读取完整产物正文。客户端必须通过该接口判断产物是否真的是代码,而不是只看 `artifact_type`。 @@ -1113,7 +1113,7 @@ summary-only 示例片段: ### 9.5 SK Snapshots -#### `GET /api/agnet/user/deployments/{deployment_id}/sk-snapshots` +#### `GET /api/agent/user/deployments/{deployment_id}/sk-snapshots` 查询本轮任务使用的 SK 快照。 @@ -1147,7 +1147,7 @@ summary-only 示例片段: ### 9.6 Timeline -#### `GET /api/agnet/user/deployments/{deployment_id}/timeline` +#### `GET /api/agent/user/deployments/{deployment_id}/timeline` 聚合审计事件、Runtime callback、artifact、SK snapshot。桌面客户端推荐优先使用这个接口渲染“当前子环节进度”。 @@ -1206,7 +1206,7 @@ summary-only 示例片段: ### 10.1 查询待审批 -#### `GET /api/agnet/approvals?status=pending&deployment_id={deployment_id}` +#### `GET /api/agent/approvals?status=pending&deployment_id={deployment_id}` 查询参数: @@ -1236,7 +1236,7 @@ summary-only 示例片段: "requires_credential": true, "credential_lease_id": "", "status": "pending", - "requested_by": "agnet-runtime", + "requested_by": "agent-runtime", "request_reason": "需要写入功能分支", "ttl_seconds": 900, "expires_at": 1779850900000, @@ -1249,7 +1249,7 @@ summary-only 示例片段: ### 10.2 同意审批 -#### `POST /api/agnet/approvals/{approval_id}/approve` +#### `POST /api/agent/approvals/{approval_id}/approve` 请求: @@ -1270,7 +1270,7 @@ summary-only 示例片段: "credential_lease_id": "lease_xxx", "credential_lease": { "lease_id": "lease_xxx", - "credential_ref": "lease://agnet/lease_xxx", + "credential_ref": "lease://agent/lease_xxx", "status": "active", "expires_at": 1779850900000 } @@ -1282,7 +1282,7 @@ summary-only 示例片段: ### 10.3 拒绝审批 -#### `POST /api/agnet/approvals/{approval_id}/reject` +#### `POST /api/agent/approvals/{approval_id}/reject` 请求: @@ -1307,7 +1307,7 @@ summary-only 示例片段: ### 10.4 查询 lease -#### `GET /api/agnet/credential-leases?status=active&deployment_id={deployment_id}` +#### `GET /api/agent/credential-leases?status=active&deployment_id={deployment_id}` 响应: @@ -1318,7 +1318,7 @@ summary-only 示例片段: "items": [ { "lease_id": "lease_xxx", - "credential_ref": "lease://agnet/lease_xxx", + "credential_ref": "lease://agent/lease_xxx", "approval_id": "appr_xxx", "deployment_id": "dep_464a08b7b751", "resource_id": "res_git_main", @@ -1334,7 +1334,7 @@ summary-only 示例片段: ### 10.5 撤销 lease -#### `POST /api/agnet/credential-leases/{lease_id}/revoke` +#### `POST /api/agent/credential-leases/{lease_id}/revoke` 请求: @@ -1358,9 +1358,9 @@ summary-only 示例片段: ## 11. 角色模板 -### `GET /api/agnet/role-templates` +### `GET /api/agent/role-templates` -获取推荐子 Agnet 角色,桌面端可用于角色选择页。 +获取推荐子 Agent 角色,桌面端可用于角色选择页。 响应: @@ -1371,7 +1371,7 @@ summary-only 示例片段: "items": [ { "key": "backend", - "display_name": "Backend Agnet", + "display_name": "Backend Agent", "summary": "实现后端接口、数据模型和服务逻辑", "default_model": "gpt-5.4", "default_permissions": ["repo:read", "repo:write"], @@ -1382,7 +1382,7 @@ summary-only 示例片段: } ``` -> `default_model` 由 Manager 单一来源 `defaultAgnetModelID()` 提供(环境变量 `AGNET_DEFAULT_MODEL_ID`,默认生产已验证的 `gpt-5.4`)。Manager 不再回退 `agnet-model-` 占位名;客户端如不指定 `default_model_id`,draft/runtime agent 会自动采用该单一默认值。 +> `default_model` 由 Manager 单一来源 `defaultAgentModelID()` 提供(环境变量 `AGENT_DEFAULT_MODEL_ID`,默认生产已验证的 `gpt-5.4`)。Manager 不再回退 `agent-model-` 占位名;客户端如不指定 `default_model_id`,draft/runtime agent 会自动采用该单一默认值。 ## 12. 状态枚举 @@ -1393,12 +1393,12 @@ summary-only 示例片段: | `draft` | 草稿 | 可继续编辑 | | `configuring` | 等待回答追问 | 展示 followups | | `running` | 可推进/运行中 | 创建 deployment 或展示 timeline | -| `awaiting_approval` | 等待审批 | 拉取 `/api/agnet/approvals` | +| `awaiting_approval` | 等待审批 | 拉取 `/api/agent/approvals` | | `completed` | 完成 | 展示交付物 | | `failed` | 失败 | 展示失败原因 | | `paused` | 暂停 | 允许继续或停止 | -### Agnet Deployment.status +### Agent Deployment.status | 状态 | 说明 | |---|---| @@ -1410,7 +1410,7 @@ summary-only 示例片段: > 注意:`status` 由 Manager 接收 Runtime `deployment.status_changed` 后镜像写入。除上述规范值外,Runtime 若回传其他自定义状态字符串,Manager 会透传保存,客户端应对未知值做兜底(按非终态处理或显示原值)。 -### Agnet Deployment.runtime_state +### Agent Deployment.runtime_state | 状态 | 说明 | |---|---| @@ -1508,7 +1508,7 @@ while (current.status === 'configuring') { const draft = await encryptedManagerRequest>( 'POST', - `/api/agnet/user/tasks/${current.id}/deployment-draft`, + `/api/agent/user/tasks/${current.id}/deployment-draft`, { task: current, sub_mode: 'agile', @@ -1520,7 +1520,7 @@ const draft = await encryptedManagerRequest>( const deployment = await encryptedManagerRequest>( 'POST', - '/api/agnet/user/deployments', + '/api/agent/user/deployments', { orchestration_plan: draft.data.orchestration_plan } ) @@ -1528,13 +1528,13 @@ const deploymentId = deployment.data.deployment_id setInterval(async () => { const timeline = await manager.get>( - `/api/agnet/user/deployments/${deploymentId}/timeline`, + `/api/agent/user/deployments/${deploymentId}/timeline`, { headers: { 'New-Api-User': String(userId) } } ) renderTimeline(timeline.data.timeline) const approvals = await manager.get>( - `/api/agnet/approvals?status=pending&deployment_id=${deploymentId}`, + `/api/agent/approvals?status=pending&deployment_id=${deploymentId}`, { headers: { 'New-Api-User': String(userId) } } ) renderApprovals(approvals.data.items) @@ -1542,13 +1542,13 @@ setInterval(async () => { async function refreshSubDelivery(deploymentId: string) { const diagnostics = await manager.get>( - `/api/agnet/user/deployments/${deploymentId}/runtime-diagnostics`, + `/api/agent/user/deployments/${deploymentId}/runtime-diagnostics`, { headers: { 'New-Api-User': String(userId) } } ) renderRuntimeDiagnostics(diagnostics.data) const artifacts = await manager.get>( - `/api/agnet/user/deployments/${deploymentId}/artifacts`, + `/api/agent/user/deployments/${deploymentId}/artifacts`, { headers: { 'New-Api-User': String(userId) } } ) renderArtifacts(artifacts.data.artifacts) @@ -1558,7 +1558,7 @@ async function refreshSubDelivery(deploymentId: string) { ) if (firstDeliverable) { const content = await manager.get( - `/api/agnet/user/deployments/${deploymentId}/artifacts/${firstDeliverable.artifact_id}/content`, + `/api/agent/user/deployments/${deploymentId}/artifacts/${firstDeliverable.artifact_id}/content`, { headers: { 'New-Api-User': String(userId) }, responseType: 'text', @@ -1571,10 +1571,10 @@ async function refreshSubDelivery(deploymentId: string) { 兼容说明: -- 桌面客户端对 `POST` 等有 body 的 sub 请求走 V2 加密时,`/api/agnet/user/*` 不需要 `New-Api-User`,也不依赖浏览器 cookie。 +- 桌面客户端对 `POST` 等有 body 的 sub 请求走 V2 加密时,`/api/agent/user/*` 不需要 `New-Api-User`,也不依赖浏览器 cookie。 - `GET` 查询接口本身没有请求 body,当前生产兼容路径仍使用 Manager session cookie + `New-Api-User`。如果桌面本地服务后续要完全脱离 session cookie 查询 timeline / artifact / approval,需要再补“无 body 的 V2 设备签名 GET”协议。 - `/api/heicode-auth/*` 仍必须带 `Authorization: Bearer `,该 token 只用于上游 HeicodeTask 认证。 -- 如果客户端临时还没有接入 V2 加密,只能作为调试兼容路径使用 Manager session + `New-Api-User` 调 `/api/agnet/user/*`;正式桌面流程不要依赖该路径。 +- 如果客户端临时还没有接入 V2 加密,只能作为调试兼容路径使用 Manager session + `New-Api-User` 调 `/api/agent/user/*`;正式桌面流程不要依赖该路径。 ## 15. 当前生产注意事项 diff --git a/docs/integration/heicode-manager-swarm-runtime-env-template.md b/docs/integration/heicode-manager-swarm-runtime-env-template.md index 6281d5f..51cb02a 100644 --- a/docs/integration/heicode-manager-swarm-runtime-env-template.md +++ b/docs/integration/heicode-manager-swarm-runtime-env-template.md @@ -8,24 +8,24 @@ 当 Runtime 提供正式 `POST /api/swarms` 时,Manager 建议配置: ```bash -AGNET_RUNTIME_ENABLED=true -AGNET_RUNTIME_ASYNC=false -AGNET_RUNTIME_BASE_URL=http:// -AGNET_RUNTIME_SERVICE_TOKEN= -AGNET_RUNTIME_HEALTH_PATH=/api/agnet/health -AGNET_RUNTIME_CREATE_PATH=/api/swarms -AGNET_RUNTIME_STOP_PATH=/api/swarms/{swarm_id}/stop -AGNET_RUNTIME_APPROVAL_DECISION_PATH=/api/swarms/{swarm_id}/approvals/{approval_id} -AGNET_RUNTIME_TIMEOUT_SECONDS=10 +AGENT_RUNTIME_ENABLED=true +AGENT_RUNTIME_ASYNC=false +AGENT_RUNTIME_BASE_URL=http:// +AGENT_RUNTIME_SERVICE_TOKEN= +AGENT_RUNTIME_HEALTH_PATH=/api/agent/health +AGENT_RUNTIME_CREATE_PATH=/api/swarms +AGENT_RUNTIME_STOP_PATH=/api/swarms/{swarm_id}/stop +AGENT_RUNTIME_APPROVAL_DECISION_PATH=/api/swarms/{swarm_id}/approvals/{approval_id} +AGENT_RUNTIME_TIMEOUT_SECONDS=10 HEICODE_PUBLIC_BASE_URL=https://code.xinghanlab.com -AGNET_RUNTIME_CALLBACK_URL=https://code.xinghanlab.com/api/agnet/callbacks/swarm-events -AGNET_CALLBACK_TOKEN= +AGENT_RUNTIME_CALLBACK_URL=https://code.xinghanlab.com/api/agent/callbacks/swarm-events +AGENT_CALLBACK_TOKEN= ``` 说明: -1. `AGNET_RUNTIME_SERVICE_TOKEN` 是 Manager 调 Runtime 的 Bearer token。 -2. `AGNET_CALLBACK_TOKEN` 是 Runtime 回调 Manager 时放在 `X-Agnet-Service-Token` 的 token。 +1. `AGENT_RUNTIME_SERVICE_TOKEN` 是 Manager 调 Runtime 的 Bearer token。 +2. `AGENT_CALLBACK_TOKEN` 是 Runtime 回调 Manager 时放在 `X-Agent-Service-Token` 的 token。 3. 两个 token 可以不同,不要写进前端或日志。 4. 如果 Runtime 返回 `swarm_id`,Manager 会保存到 `runtime_swarm_id`。 5. 如果 Runtime 返回 `deployment_id`,Manager 会保存到 `runtime_deployment_id`。 @@ -35,18 +35,18 @@ AGNET_CALLBACK_TOKEN= 如果短期只用现有 Orchestrator 的 `/health` 和 `/tasks` 先做连通性测试,可以临时配置: ```bash -AGNET_RUNTIME_ENABLED=true -AGNET_RUNTIME_ASYNC=false -AGNET_RUNTIME_BASE_URL=http://52.139.240.116:8000 -AGNET_RUNTIME_SERVICE_TOKEN= -AGNET_RUNTIME_HEALTH_PATH=/health -AGNET_RUNTIME_CREATE_PATH=/tasks -AGNET_RUNTIME_STOP_PATH=/tasks/{runtime_deployment_id}/stop -AGNET_RUNTIME_APPROVAL_DECISION_PATH=/api/swarms/{swarm_id}/approvals/{approval_id} -AGNET_RUNTIME_TIMEOUT_SECONDS=10 +AGENT_RUNTIME_ENABLED=true +AGENT_RUNTIME_ASYNC=false +AGENT_RUNTIME_BASE_URL=http://52.139.240.116:8000 +AGENT_RUNTIME_SERVICE_TOKEN= +AGENT_RUNTIME_HEALTH_PATH=/health +AGENT_RUNTIME_CREATE_PATH=/tasks +AGENT_RUNTIME_STOP_PATH=/tasks/{runtime_deployment_id}/stop +AGENT_RUNTIME_APPROVAL_DECISION_PATH=/api/swarms/{swarm_id}/approvals/{approval_id} +AGENT_RUNTIME_TIMEOUT_SECONDS=10 HEICODE_PUBLIC_BASE_URL=https://code.xinghanlab.com -AGNET_RUNTIME_CALLBACK_URL=https://code.xinghanlab.com/api/agnet/callbacks/swarm-events -AGNET_CALLBACK_TOKEN= +AGENT_RUNTIME_CALLBACK_URL=https://code.xinghanlab.com/api/agent/callbacks/swarm-events +AGENT_CALLBACK_TOKEN= ``` 注意: @@ -60,9 +60,9 @@ AGNET_CALLBACK_TOKEN= 推荐使用 service token: ```http -POST /api/agnet/callbacks/swarm-events -X-Agnet-Service-Token: -X-Agnet-Event-Id: +POST /api/agent/callbacks/swarm-events +X-Agent-Service-Token: +X-Agent-Event-Id: X-Correlation-ID: Content-Type: application/json ``` @@ -70,9 +70,9 @@ Content-Type: application/json 也支持 HMAC: ```http -X-Agnet-Event-Id: -X-Agnet-Timestamp: -X-Agnet-Signature: sha256= +X-Agent-Event-Id: +X-Agent-Timestamp: +X-Agent-Signature: sha256= X-Correlation-ID: Content-Type: application/json ``` @@ -88,13 +88,13 @@ timestamp + "." + event_id + "." + raw_body 健康检查: ```bash -curl -sS "$AGNET_RUNTIME_BASE_URL$AGNET_RUNTIME_HEALTH_PATH" +curl -sS "$AGENT_RUNTIME_BASE_URL$AGENT_RUNTIME_HEALTH_PATH" ``` Manager 本地默认模拟链路: ```bash -curl -sS -X POST "https://code.xinghanlab.com/api/agnet/user/deployments//simulate-events" \ +curl -sS -X POST "https://code.xinghanlab.com/api/agent/user/deployments//simulate-events" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{}' @@ -105,10 +105,10 @@ curl -sS -X POST "https://code.xinghanlab.com/api/agnet/user/deployments/ deployment draft | 已完成 | `POST /api/agnet/user/tasks/{task_id}/deployment-draft` | -| 用户态 deployment 创建 | 已完成 | `POST /api/agnet/user/deployments` | +| HeicodeTask -> deployment draft | 已完成 | `POST /api/agent/user/tasks/{task_id}/deployment-draft` | +| 用户态 deployment 创建 | 已完成 | `POST /api/agent/user/deployments` | | Runtime create bridge | 已完成 | 通过环境变量调用 Agent Manager | | Runtime stop bridge | 已完成 | 停止时同步 Runtime | -| callback 接收 | 已完成 | `POST /api/agnet/callbacks/swarm-events` | +| callback 接收 | 已完成 | `POST /api/agent/callbacks/swarm-events` | | phase / timeline 聚合 | 已完成 | deployment timeline 聚合 audit、callback、artifact、SK | -| artifact 查询 | 已完成 | `GET /api/agnet/user/deployments/{deployment_id}/artifacts` | -| approval 查询/approve/reject | 已完成 | `/api/agnet/approvals` | +| artifact 查询 | 已完成 | `GET /api/agent/user/deployments/{deployment_id}/artifacts` | +| approval 查询/approve/reject | 已完成 | `/api/agent/approvals` | | V2 body 加密 | 已完成 | 桌面客户端到 Manager 的 POST 请求复用模型调用加密协议 | | 生产验证 | 已完成核心链路 | 生产已验证 Manager `1.4.18` 可通过 `/api/swarms` 创建普通 sub run,持久化 `runtime_swarm_id=swm_*`,Agent Manager 自动 callback 可写入 `events/timeline`;`1.4.19` 补齐 callback 后反写 deployment 快照 | | callback 状态反写 | 已完成 | Manager 接收 `deployment.status_changed`、`phase.changed`、`timeline.updated`、`agent.started/completed/crashed` 后,会同步更新 deployment `status/phase/runtime_state/agent_instances`,避免详情页长期停留 `initializing/pending` | @@ -44,7 +44,7 @@ Agent Manager 需要做的是:接收 Manager 生成的 deployment payload, | 任务 | 必需 | 原因 | 验收 | |---|---:|---|---| -| 接收 Manager 创建请求 | 是 | Manager 会把普通 sub deployment 发送给 Agent Manager | `POST /api/agnet/deployments` 或配置的 create path 返回 2xx | +| 接收 Manager 创建请求 | 是 | Manager 会把普通 sub deployment 发送给 Agent Manager | `POST /api/agent/deployments` 或配置的 create path 返回 2xx | | 支持 `sub_mode=agile` | 是 | 普通敏捷模式核心标识 | 不认识时不能按蜂群 task graph 强制处理 | | 支持 `agile_context` | 是 | 用于阶段、检查点、验收标准和下一步动作 | Runtime 能读取并在回调里更新 stage/checkpoint | | 支持阶段状态回传 | 是 | 客户端需要知道当前处在需求/设计/开发/测试/修复/部署哪个环节 | 回调 `phase.changed` 或 `timeline.updated` | @@ -60,13 +60,13 @@ Agent Manager 需要做的是:接收 Manager 生成的 deployment payload, Manager 当前默认 create path: ```text -AGNET_RUNTIME_CREATE_PATH=/api/agnet/deployments +AGENT_RUNTIME_CREATE_PATH=/api/agent/deployments ``` 如果 Agent Manager 统一使用 `/api/swarms`,Manager 也可以配置切过去。但普通 sub 敏捷模式建议先支持: ```http -POST /api/agnet/deployments +POST /api/agent/deployments Authorization: Bearer X-User-ID: X-Binding-Scope: @@ -114,7 +114,7 @@ Content-Type: application/json "requires_user_approval": false }, "agent_runtime": { - "platform": "agnet", + "platform": "agent", "agents": [ { "role": "backend", @@ -163,8 +163,8 @@ Content-Type: application/json } ], "callback": { - "url": "https://code.xinghanlab.com/api/agnet/callbacks/swarm-events", - "signing_secret_ref": "azkv://heicode-kv.vault.azure.net/secrets/agnet-callback-signing-key", + "url": "https://code.xinghanlab.com/api/agent/callbacks/swarm-events", + "signing_secret_ref": "azkv://heicode-kv.vault.azure.net/secrets/agent-callback-signing-key", "subscribed_events": [ "deployment.status_changed", "phase.changed", @@ -260,7 +260,7 @@ Agent Manager 至少要支持以下阶段语义,并通过 callback 回写当 联调前可以先请求 Manager 当前接受的 callback schema: ```http -GET https://code.xinghanlab.com/api/agnet/callbacks/swarm-events/schema +GET https://code.xinghanlab.com/api/agent/callbacks/swarm-events/schema ``` 该接口只返回事件类型、分类和必填字段,不返回任何 token 或密钥。普通 sub 敏捷重点核对 `phase.changed`、`timeline.updated`、`artifact.created`、`approval.requested`、`sk_tool.*` 和 `budget.alert`。 @@ -364,7 +364,7 @@ POST /api/swarms/{swarm_id}/approvals/{approval_id} 如果普通 sub 不使用 `swarm_id`,可让 Manager 配置为: ```text -AGNET_RUNTIME_APPROVAL_DECISION_PATH=/api/agnet/deployments/{deployment_id}/approvals/{approval_id} +AGENT_RUNTIME_APPROVAL_DECISION_PATH=/api/agent/deployments/{deployment_id}/approvals/{approval_id} ``` Runtime 需要接收: @@ -380,7 +380,7 @@ Runtime 需要接收: "resource_type": "git", "target_role": "backend", "requires_credential": true, - "credential_ref": "lease://agnet/lease_xxx", + "credential_ref": "lease://agent/lease_xxx", "lease_id": "lease_xxx", "lease_expires_at": 1779850900000 } @@ -489,7 +489,7 @@ Agent Manager 回传 usage 时建议至少包含: | 9 | Runtime 回调 completed/failed | Manager detail/timeline 显示终态 | | 10 | Runtime 回调用量 | Manager 能看到 model token/cost 或 usage 摘要,且与 deployment/task/role/correlation 关联 | | 11 | 查日志 | 不出现明文密钥,不丢 `correlation_id` | -| 12 | 生产路由复核 | `GET /api/agnet/callbacks/swarm-events/schema` 返回 200,用户态/后台态接口使用有效登录态通过 smoke | +| 12 | 生产路由复核 | `GET /api/agent/callbacks/swarm-events/schema` 返回 200,用户态/后台态接口使用有效登录态通过 smoke | ## 13. Agent Manager 不需要处理的内容 @@ -509,8 +509,8 @@ Agent Manager 回传 usage 时建议至少包含: | 真实 Runtime 是否完整消费 `agile_context` | Agent Manager | 需要确认字段被使用,不只是透传 | | 阶段状态持续回传 | Agent Manager | 2026-05-31 生产复核已收到 running/completed、phase、timeline、task.completed 等真实回调;后续需在更复杂多阶段任务中继续验证 | | artifact 真实产出 | Agent Manager | 2026-05-31 生产复核已收到业务 `code_patch` artifact,并可通过 Manager content 代理下载;Git/部署 URL 类产物仍按具体任务继续验证 | -| approval decision 接收路径 | Agent Manager + Manager 配置 | 需确认最终路径是 `/api/swarms/...` 还是 `/api/agnet/deployments/...` | +| approval decision 接收路径 | Agent Manager + Manager 配置 | 需确认最终路径是 `/api/swarms/...` 还是 `/api/agent/deployments/...` | | 用量回传 | Agent Manager + NewAPI/CodeGW | 2026-05-31 生产复核已回传 `tokens_used=2682`、`newapi_request_id=chatcmpl-DlbZce3VZnsv5DptBILFoiRYy5ZHN`;后续需继续按 user/deployment/task/role 归属 | | Agent 运行费用真实结算 | Agent Manager + Manager | Runtime 已能回传 token usage;真实费用结算仍需按 Manager/NewAPI 订阅/钱包规则和 Runtime usage 口径统一 | -| 生产 callback schema 路由 | Manager 部署/路由 | 本地代码和测试已覆盖,2026-05-28 生产公开访问 `/api/agnet/callbacks/swarm-events/schema` 返回 404,需要重新上线或核对生产镜像/路由 | +| 生产 callback schema 路由 | Manager 部署/路由 | 本地代码和测试已覆盖,2026-05-28 生产公开访问 `/api/agent/callbacks/swarm-events/schema` 返回 404,需要重新上线或核对生产镜像/路由 | | 无 body GET 的 V2 签名 | 客户端 + Manager | 这是客户端全链路无 cookie 的后续项,不阻塞 Agent Manager 创建/回调联调 | diff --git a/docs/integration/蜂群模式-AgentManager对接任务清单.md b/docs/integration/蜂群模式-AgentManager对接任务清单.md index 0165b1d..3a112f9 100644 --- a/docs/integration/蜂群模式-AgentManager对接任务清单.md +++ b/docs/integration/蜂群模式-AgentManager对接任务清单.md @@ -14,10 +14,10 @@ Agent Manager / Swarm Runtime 当前还需要补齐或确认的是:真实创 | 能力 | 状态 | 说明 | |---|---|---| -| Runtime 创建桥接 | 已完成 | Manager 可通过 `AGNET_RUNTIME_BASE_URL` + `AGNET_RUNTIME_CREATE_PATH` 调 Agent Manager | -| Runtime 停止桥接 | 已完成 | 默认 `POST /api/agnet/deployments/{deployment_id}/stop` | +| Runtime 创建桥接 | 已完成 | Manager 可通过 `AGENT_RUNTIME_BASE_URL` + `AGENT_RUNTIME_CREATE_PATH` 调 Agent Manager | +| Runtime 停止桥接 | 已完成 | 默认 `POST /api/agent/deployments/{deployment_id}/stop` | | `/api/swarms` adapter | 已完成 | Manager 用户态入口已存在,后续可切真实 Runtime | -| callback 接收 | 已完成 | `POST /api/agnet/callbacks/swarm-events` | +| callback 接收 | 已完成 | `POST /api/agent/callbacks/swarm-events` | | callback 鉴权 | 已完成 | 支持 service token 或 HMAC | | callback 幂等 | 已完成 | 按 `event_id` / `idempotency_key` 去重 | | task flow 校验 | 已完成 | `task.*` / `handoff.*` 缺关键字段会返回 `CALLBACK_SCHEMA_INVALID` | @@ -58,11 +58,11 @@ Content-Type: application/json Manager 当前可通过环境变量切换: ```text -AGNET_RUNTIME_BASE_URL=http://20.212.121.126 -AGNET_RUNTIME_CREATE_PATH=/api/swarms -AGNET_RUNTIME_HEALTH_PATH=/api/agnet/health -AGNET_RUNTIME_STOP_PATH=/api/agnet/deployments/{deployment_id}/stop -AGNET_RUNTIME_APPROVAL_DECISION_PATH=/api/swarms/{swarm_id}/approvals/{approval_id} +AGENT_RUNTIME_BASE_URL=http://20.212.121.126 +AGENT_RUNTIME_CREATE_PATH=/api/swarms +AGENT_RUNTIME_HEALTH_PATH=/api/agent/health +AGENT_RUNTIME_STOP_PATH=/api/agent/deployments/{deployment_id}/stop +AGENT_RUNTIME_APPROVAL_DECISION_PATH=/api/swarms/{swarm_id}/approvals/{approval_id} ``` 请求体核心形状: @@ -116,8 +116,8 @@ AGNET_RUNTIME_APPROVAL_DECISION_PATH=/api/swarms/{swarm_id}/approvals/{approval_ } ], "callback": { - "url": "https://code.xinghanlab.com/api/agnet/callbacks/swarm-events", - "signing_secret_ref": "azkv://heicode-kv.vault.azure.net/secrets/agnet-callback-signing-key", + "url": "https://code.xinghanlab.com/api/agent/callbacks/swarm-events", + "signing_secret_ref": "azkv://heicode-kv.vault.azure.net/secrets/agent-callback-signing-key", "subscribed_events": [ "deployment.status_changed", "task.created", @@ -180,9 +180,9 @@ Manager 解析规则: 统一入口: ```http -POST https://code.xinghanlab.com/api/agnet/callbacks/swarm-events -X-Agnet-Service-Token: -X-Agnet-Event-Id: +POST https://code.xinghanlab.com/api/agent/callbacks/swarm-events +X-Agent-Service-Token: +X-Agent-Event-Id: X-Correlation-ID: Content-Type: application/json ``` @@ -190,8 +190,8 @@ Content-Type: application/json 也支持 HMAC: ```http -X-Agnet-Timestamp: -X-Agnet-Signature: sha256= +X-Agent-Timestamp: +X-Agent-Signature: sha256= ``` 签名内容: @@ -203,7 +203,7 @@ timestamp + "." + event_id + "." + raw_body Agent Manager / 蜂群侧可以先拉取 Manager 当前接受的事件 schema: ```http -GET https://code.xinghanlab.com/api/agnet/callbacks/swarm-events/schema +GET https://code.xinghanlab.com/api/agent/callbacks/swarm-events/schema ``` 该接口不返回任何 token 或密钥,只返回事件类型、分类和必填字段,用于联调前核对 `task.*`、`handoff.*`、`artifact.created`、`approval.requested` 等 callback payload 是否符合 Manager 接收规则。 @@ -366,7 +366,7 @@ Content-Type: application/json "risk_level": "high", "requires_credential": true, "user_id": "22", - "credential_ref": "lease://agnet/lease_xxx", + "credential_ref": "lease://agent/lease_xxx", "lease_id": "lease_xxx", "lease_expires_at": 1779850900000 } @@ -379,7 +379,7 @@ Content-Type: application/json 默认: ```http -POST /api/agnet/deployments/{runtime_deployment_id}/stop +POST /api/agent/deployments/{runtime_deployment_id}/stop Authorization: Bearer Content-Type: application/json ``` @@ -410,7 +410,7 @@ Content-Type: application/json | 用例 | 操作 | 期望 | |---|---|---| -| health | `GET /api/agnet/health` | 200,`status=healthy` | +| health | `GET /api/agent/health` | 200,`status=healthy` | | create | Manager 创建 deployment | Runtime 返回 `deployment_id` 和 `swarm_id` | | task flow | Runtime 回调 `task.created/claimed/heartbeat/blocked/retried/completed` | Manager timeline 和页面 Sub task flow 可见 | | handoff | Runtime 回调 `handoff.requested/completed` | Manager 页面可见 from/to role | @@ -433,7 +433,7 @@ Content-Type: application/json ## 12. 联调顺序建议 1. Agent Manager 确认 health、create、stop、approval decision 路径。 -2. Manager 配置 `AGNET_RUNTIME_CREATE_PATH=/api/swarms` 后创建一个真实 deployment。 +2. Manager 配置 `AGENT_RUNTIME_CREATE_PATH=/api/swarms` 后创建一个真实 deployment。 3. Agent Manager 回调 `task.created`、`task.claimed`、`task.heartbeat`。 4. 回调 `task.blocked`、`handoff.requested`、`handoff.completed`、`task.retried`。 5. 回调 `artifact.created`。 diff --git a/docs/plan.md b/docs/plan.md index 4dbdb58..1484ec9 100644 --- a/docs/plan.md +++ b/docs/plan.md @@ -4,7 +4,7 @@ ## P0:边界收敛 -状态:文档边界已收敛。当前 `docs/` 只保留 `heicode.md`、`plan.md` 和已上线登录接口文档作为实施依据;旧 Agnet API 草案、旧 M1-M5 计划和旧架构说明不再作为开发输入。现有代码中仍可能存在过渡期的 `sk_sources`、Agnet control plane 或部署计划命名,不能反向覆盖本文档边界。 +状态:文档边界已收敛。当前 `docs/` 只保留 `heicode.md`、`plan.md` 和已上线登录接口文档作为实施依据;旧 Agent API 草案、旧 M1-M5 计划和旧架构说明不再作为开发输入。现有代码中仍可能存在过渡期的 `sk_sources`、Agent control plane 或部署计划命名,不能反向覆盖本文档边界。 目标:让团队只围绕一套产品和架构边界协作。 @@ -12,24 +12,24 @@ - 以 [`heicode.md`](./heicode.md) 作为当前产品与架构共识。 - 保留已上线登录接口文档。 -- 不再维护旧 Agnet API 草案和旧 M1-M5 计划。 -- 后续所有实现前先确认是否符合 Manager / Agnet / NewAPI / Secret Store 的边界。 +- 不再维护旧 Agent API 草案和旧 M1-M5 计划。 +- 后续所有实现前先确认是否符合 Manager / Agent / NewAPI / Secret Store 的边界。 - 需求和边界没有想清楚前,不改代码。 验收: -- `docs/` 中没有多套互相冲突的 Agnet、NewAPI 或 Manager 计划。 +- `docs/` 中没有多套互相冲突的 Agent、NewAPI 或 Manager 计划。 - 新需求讨论先落到文档共识,再进入实现。 ## P1:Manager 资源模型 -目标:把“Git 来源”升级为面向子 Agnet 的统一资源绑定模型。 +目标:把“Git 来源”升级为面向子 Agent 的统一资源绑定模型。 任务: - 将当前 Git 来源抽象为资源绑定模型。 - 增加资源类型:Git、SK、项目文档、云账号、单项云资源。 -- 增加 Resource Grant,用于把资源分配给登录用户、绑定 Git/SK/云资源范围、角色和子 Agnet。 +- 增加 Resource Grant,用于把资源分配给登录用户、绑定 Git/SK/云资源范围、角色和子 Agent。 - 定义资源元数据、权限范围、约束、状态和审计字段。 - 前端从单点功能页逐步走向“绑定资源 -> 分配角色 -> 部署确认”的主流程。 @@ -39,13 +39,13 @@ 1. 后端先落库资源绑定和 Resource Grant 两类记录,不在本阶段实现 Secret Broker 的真实写入。 2. Resource Binding 表达登录用户绑定的资源元数据:资源类型、名称、外部标识、可见元数据、权限范围、约束、状态、`secret_ref` 和审计字段。 -3. Resource Grant 表达授权关系:user、resource、binding scope、role、子 Agnet 标识、允许动作、限制条件、状态、过期时间和审计字段。 +3. Resource Grant 表达授权关系:user、resource、binding scope、role、子 Agent 标识、允许动作、限制条件、状态、过期时间和审计字段。 4. 提供只返回元数据和 `secret_ref` 的列表、详情、创建、授权、撤销接口;任何接口响应、日志和 Markdown 产物都不得包含真实密钥。 -5. 生成一份 permission manifest 示例,用结构化数据证明“某登录用户把某个绑定资源授予某个子 Agnet 角色使用”。 +5. 生成一份 permission manifest 示例,用结构化数据证明“某登录用户把某个绑定资源授予某个子 Agent 角色使用”。 验收: -- Manager 能表达“某登录用户把某个绑定资源授予某个子 Agnet 角色使用”。 +- Manager 能表达“某登录用户把某个绑定资源授予某个子 Agent 角色使用”。 - 数据库不保存明文密钥,只保存 `secret_ref`。 - P1 测试样例能覆盖 Git、SK、项目文档、云账号和单项云资源五类资源的元数据建模。 - 撤销 Resource Grant 后,对应 permission manifest 不再包含该授权。 @@ -68,23 +68,23 @@ - Git token、云密钥、SSH key、数据库密码不会进入 Git、Markdown、前端响应或普通日志。 - 用户可以授权和撤销资源,平台负责实际凭证托管。 -## P3:Agnet 平台 AKS 身份接入 +## P3:Agent 平台 AKS 身份接入 -目标:让子 Agnet 在 AKS 上按最小权限访问被授权资源。 +目标:让子 Agent 在 AKS 上按最小权限访问被授权资源。 任务: -- Agnet 平台支持 deployment / role 到 Kubernetes ServiceAccount 的映射。 +- Agent 平台支持 deployment / role 到 Kubernetes ServiceAccount 的映射。 - 支持 Vault Kubernetes Auth 或等价 Workload Identity。 - 支持按 user / resource binding / role 生成密钥访问策略。 -- 子 Agnet 运行时只能访问被授权的 secret。 +- 子 Agent 运行时只能访问被授权的 secret。 - 普通开发资源支持受控注入。 -- 高危操作审批只在客户端完成;审批通过后允许向子 Agnet 注入密钥保管器派生的短期、最小权限凭证。 +- 高危操作审批只在客户端完成;审批通过后允许向子 Agent 注入密钥保管器派生的短期、最小权限凭证。 验收: -- 子 Agnet 不保存长期密钥。 -- 撤销 Resource Grant 后,子 Agnet 无法继续访问对应资源。 +- 子 Agent 不保存长期密钥。 +- 撤销 Resource Grant 后,子 Agent 无法继续访问对应资源。 - 高危资源访问有审计记录。 ## P4:NewAPI 解耦 @@ -98,8 +98,8 @@ - Manager 通过服务凭据调用 NewAPI。 - Manager 展示普通用户需要的模型、余额、额度、调用日志。 - 隐藏渠道管理、价格配置、模型供应商后台配置和 NewAPI 管理员能力。 -- 建立 Heicode/Agnet 登录用户 `user.id`、`channelId` 与 NewAPI user / token / group / quota / usage 的映射。 -- 子 Agnet 的运行模型、模型 profile 和实例数归 Agnet 平台部署配置管理,不和 NewAPI 扣费映射混用。 +- 建立 Heicode/Agent 登录用户 `user.id`、`channelId` 与 NewAPI user / token / group / quota / usage 的映射。 +- 子 Agent 的运行模型、模型 profile 和实例数归 Agent 平台部署配置管理,不和 NewAPI 扣费映射混用。 验收: @@ -109,18 +109,18 @@ ## P5:部署和审计闭环 -目标:跑通从用户想法到子 Agnet 部署、执行、观测和审计的闭环。 +目标:跑通从用户想法到子 Agent 部署、执行、观测和审计的闭环。 任务: - Manager 生成 AGENT.md、resource context 和 permission manifest。 -- Agnet 平台部署子 Agnet 后回传 deployment、agent instance、状态和事件。 +- Agent 平台部署子 Agent 后回传 deployment、agent instance、状态和事件。 - Manager 展示活动状态、失败原因、资源使用记录、模型调用记录和审计日志。 - 对高危权限增加审批、撤销和运行中失效机制。 - 为每次部署保留可追溯的资源、权限、模型和上下文快照。 验收: -- 用户能看到每个子 Agnet 的角色、模型、资源权限、运行状态和失败原因。 -- 审计能回答谁在什么时候让哪个子 Agnet 使用了什么资源。 +- 用户能看到每个子 Agent 的角色、模型、资源权限、运行状态和失败原因。 +- 审计能回答谁在什么时候让哪个子 Agent 使用了什么资源。 - Markdown 只作为上下文,permission manifest 才是系统执行依据。 diff --git a/docs/product-package/01-product-overview.md b/docs/product-package/01-product-overview.md index d15ff3c..e9dd8d3 100644 --- a/docs/product-package/01-product-overview.md +++ b/docs/product-package/01-product-overview.md @@ -8,7 +8,7 @@ Heicode Heicode 是一款覆盖软件生命周期的智能开发 Code 工具。它面向的是“从想法到上线”的完整开发过程,而不是单个模型聊天窗口、CodeGW 管理后台或简单的 Agent 控制台。 -用户只需要注册登录,输入想法和约束,Heicode 就帮助用户组织子 Agnet 团队,接入用户授权的 Git、SK、项目文档和云资源,完成产品设计、代码开发、代码检查、部署、观测和后续维护。 +用户只需要注册登录,输入想法和约束,Heicode 就帮助用户组织子 Agent 团队,接入用户授权的 Git、SK、项目文档和云资源,完成产品设计、代码开发、代码检查、部署、观测和后续维护。 ## 目标用户 @@ -23,11 +23,11 @@ Heicode 是一款覆盖软件生命周期的智能开发 Code 工具。它面向 1. 把自然语言想法变成可执行的软件开发任务。 2. 把 Git、文档、SK、云资源变成可授权、可撤销、可审计的资源。 -3. 把子 Agnet 的角色、权限、模型和上下文组织成可运行的开发团队。 -4. 把持续开发拆成需求、设计、开发、测试、修复、部署等子环节,由 Agnet 持续推进。 -5. 让 Agnet 在执行过程中可以调用项目 SK 工具和外部能力,而不是只做一次性任务分发。 +3. 把子 Agent 的角色、权限、模型和上下文组织成可运行的开发团队。 +4. 把持续开发拆成需求、设计、开发、测试、修复、部署等子环节,由 Agent 持续推进。 +5. 让 Agent 在执行过程中可以调用项目 SK 工具和外部能力,而不是只做一次性任务分发。 6. 把模型调用、余额、额度和日志统一展示给用户。 -7. 把长期密钥放入密钥保管器,只给子 Agnet 短期、最小权限凭证。 +7. 把长期密钥放入密钥保管器,只给子 Agent 短期、最小权限凭证。 8. 把开发、检查、部署、维护纳入同一个生命周期闭环。 ## 产品形态 @@ -35,12 +35,12 @@ Heicode 是一款覆盖软件生命周期的智能开发 Code 工具。它面向 | 产品面 | 说明 | |--------|------| | Heicode 客户端 | 用户主体验,负责本地对话、输入想法、继续开发、查看执行反馈、接收交付结果和高危审批,只登录 Heicode,只使用 Heicode 提供的模型 | -| Heicode Manager / 浏览器控制台 | 辅助控制台,负责账号与安全、客户端下载、Git/云资源绑定、Agnet 部署、任务状态总览、模型余额与用量、审计与日志查看 | -| Agnet 平台 | 执行层,负责部署和运行子 Agnet,在任务推进过程中完成需求、开发、测试、修复、交付与部署,并回传日志、状态、事件和指标 | +| Heicode Manager / 浏览器控制台 | 辅助控制台,负责账号与安全、客户端下载、Git/云资源绑定、Agent 部署、任务状态总览、模型余额与用量、审计与日志查看 | +| Agent 平台 | 执行层,负责部署和运行子 Agent,在任务推进过程中完成需求、开发、测试、修复、交付与部署,并回传日志、状态、事件和指标 | | CodeGW | 内部模型网关与计费服务,普通用户不直接进入后台 | | 密钥保管器 | Azure Key Vault 实现,保存长期凭证引用,按审批和权限提供短期访问能力 | -说明:Heicode 客户端是用户主体验,用户不在网页上编码。Heicode Manager 是浏览器里的辅助控制台,承担资源、部署、状态、余额、审计和下载等辅助操作。真正持续推进任务的是 Heicode 调度下的 Agnet 执行闭环,Agnet 在过程中还可以调用已授权的 SK 工具和外部能力,最终完成交付与部署并把结果回传到客户端。 +说明:Heicode 客户端是用户主体验,用户不在网页上编码。Heicode Manager 是浏览器里的辅助控制台,承担资源、部署、状态、余额、审计和下载等辅助操作。真正持续推进任务的是 Heicode 调度下的 Agent 执行闭环,Agent 在过程中还可以调用已授权的 SK 工具和外部能力,最终完成交付与部署并把结果回传到客户端。 ## 核心流程 @@ -49,12 +49,12 @@ Heicode 是一款覆盖软件生命周期的智能开发 Code 工具。它面向 -> 输入产品想法 -> 生成产品文档和任务计划 -> 在 Manager 绑定 Git 和云资源 --> 在 Manager 部署 Agnet +-> 在 Manager 部署 Agent -> 客户端继续追加需求和修正方向 --> Agnet 按子环节执行需求、开发、测试、修复 --> Agnet 按需要调用已授权 SK 工具 +-> Agent 按子环节执行需求、开发、测试、修复 +-> Agent 按需要调用已授权 SK 工具 -> 客户端确认高危操作 --> Agnet 完成交付物整理和部署 +-> Agent 完成交付物整理和部署 -> Manager 查看状态、余额、审计和下载 -> 客户端展示执行反馈和交付结果 -> 持续维护和升级 @@ -72,11 +72,11 @@ Heicode 是一款覆盖软件生命周期的智能开发 Code 工具。它面向 ## 产品承诺 1. 用户不需要理解底层模型供应商。 -2. 用户不需要把密钥交给子 Agnet 长期保存。 +2. 用户不需要把密钥交给子 Agent 长期保存。 3. 用户可以清楚知道哪些资源被哪个角色使用。 4. 高危操作必须经过客户端审批。 5. 所有关键动作都能审计。 ## 当前一句话卖点 -> 从一个想法开始,让 Heicode 组织 AI 开发团队,持续调用 Agnet 完成开发、测试、交付与部署,安全接入你的代码和云资源,把产品推进到生产环境。 +> 从一个想法开始,让 Heicode 组织 AI 开发团队,持续调用 Agent 完成开发、测试、交付与部署,安全接入你的代码和云资源,把产品推进到生产环境。 diff --git a/docs/product-package/02-homepage-design-brief.md b/docs/product-package/02-homepage-design-brief.md index 681063c..7bb8490 100644 --- a/docs/product-package/02-homepage-design-brief.md +++ b/docs/product-package/02-homepage-design-brief.md @@ -40,7 +40,7 @@ 首屏辅助信息: - 绑定 Git 与云资源 -- 生成子 Agnet 团队 +- 生成子 Agent 团队 - 密钥保管器托管凭证 - CodeGW 统一模型与用量 @@ -91,7 +91,7 @@ | 想法转任务 | 把自然语言目标转成产品文档、任务和角色 | | 资源绑定 | 接入 Git、SK、项目文档、云账号和云资源 | | 权限控制 | 按角色分配 Resource Grant 和审批规则 | -| 执行闭环 | 子 Agnet 持续推进开发、测试、修复和部署,可调用 SK 工具,Heicode 展示日志和审计 | +| 执行闭环 | 子 Agent 持续推进开发、测试、修复和部署,可调用 SK 工具,Heicode 展示日志和审计 | ### 4. 工作流区 @@ -120,7 +120,7 @@ - 长期密钥进入密钥保管器。 - Heicode 服务端数据库只保存 `secret_ref`。 -- 子 Agnet 只拿短期凭证。 +- 子 Agent 只拿短期凭证。 - 高危操作在客户端审批。 - 日志、Markdown、前端响应不显示明文密钥。 @@ -134,14 +134,14 @@ |------|------| | Heicode Manager | 资源、部署、状态、用量和审计辅助控制台 | | 客户端 | 主对话体验、任务推进和高危审批 | -| Agnet 平台 | AKS 执行层 | +| Agent 平台 | AKS 执行层 | | CodeGW | 模型网关与用量 | | Azure Key Vault | 密钥保管器 | 补充说明: - 用户主要在客户端对话和推进任务。 -- Manager 主要承担资源准备、Agnet 部署、用量和审计辅助。 +- Manager 主要承担资源准备、Agent 部署、用量和审计辅助。 ### 7. 适用场景区 diff --git a/docs/product-package/03-user-journey-and-core-flow.md b/docs/product-package/03-user-journey-and-core-flow.md index d58d75e..955f0f9 100644 --- a/docs/product-package/03-user-journey-and-core-flow.md +++ b/docs/product-package/03-user-journey-and-core-flow.md @@ -21,13 +21,13 @@ | 1. 登录 | 注册或登录 Heicode | 获取用户身份和 channelId | 用户会话 | | 2. 输入想法 | 描述要做的产品或任务 | 生成需求摘要和任务草案 | 需求草案 | | 3. 绑定资源 | 授权 Git、文档、SK、云资源 | 保存资源元数据和 `secret_ref` | Resource Binding | -| 4. 生成团队 | 确认开发方法和角色 | 推荐子 Agnet 角色 | 角色方案 | +| 4. 生成团队 | 确认开发方法和角色 | 推荐子 Agent 角色 | 角色方案 | | 5. 分配权限 | 确认每个角色能用什么 | 生成 Resource Grant | 权限清单 | | 6. 审批高危操作 | 在客户端确认高危动作 | 记录 approval | 审批记录 | -| 7. 执行任务 | 持续追加需求、查看进度 | Agnet 平台运行子 Agnet,按子环节推进开发 | 状态、日志、事件 | -| 8. SK 工具调用 | 允许平台使用技能能力 | Agnet 按权限调用 SK 工具和外部能力 | 中间产物、检查结果 | -| 9. 交付上线 | 确认部署结果 | Agnet 完成交付整理、部署并回写审计 | 生产服务 | -| 10. 维护升级 | 提出迭代或修复 | 复用上下文和权限再次进入 Agnet 闭环 | 新版本计划 | +| 7. 执行任务 | 持续追加需求、查看进度 | Agent 平台运行子 Agent,按子环节推进开发 | 状态、日志、事件 | +| 8. SK 工具调用 | 允许平台使用技能能力 | Agent 按权限调用 SK 工具和外部能力 | 中间产物、检查结果 | +| 9. 交付上线 | 确认部署结果 | Agent 完成交付整理、部署并回写审计 | 生产服务 | +| 10. 维护升级 | 提出迭代或修复 | 复用上下文和权限再次进入 Agent 闭环 | 新版本计划 | ## 关键任务状态流程 @@ -63,7 +63,7 @@ - 产品文档草案。 - 原型描述。 - 推荐资源需求。 -- 推荐子 Agnet 角色。 +- 推荐子 Agent 角色。 ### 3. 任务上下文准备 @@ -84,7 +84,7 @@ Heicode 判断本任务缺少哪些上下文 默认展示角色视角: ```text -Backend Agnet +Backend Agent - 可读写:后端代码路径 - 可读:项目文档 - 可部署:测试环境 @@ -98,20 +98,20 @@ Backend Agnet 展示: - 本次目标。 -- 子 Agnet 数量和角色。 +- 子 Agent 数量和角色。 - 每个角色使用的资源。 - 是否会访问密钥。 - 是否会部署云资源。 - 预计模型预算。 - 审批项。 -用户确认后,Heicode 生成 Agnet 平台 payload。 +用户确认后,Heicode 生成 Agent 平台 payload。 ### 6. 执行中的任务空间 展示: -- 子 Agnet 活跃状态。 +- 子 Agent 活跃状态。 - 当前步骤。 - 最近日志。 - 失败原因。 @@ -127,21 +127,21 @@ Backend Agnet - 撤销资源授权。 - 发起修复或继续迭代。 -### 7. Agnet 执行闭环 +### 7. Agent 执行闭环 -Heicode 不是只把任务丢给 Agnet 一次就结束,而是会在开发过程中持续调用 Agnet 完成子环节。 +Heicode 不是只把任务丢给 Agent 一次就结束,而是会在开发过程中持续调用 Agent 完成子环节。 闭环应表达为: ```text 客户端输入目标或追加需求 -> Heicode 生成下一步任务 --> Agnet 执行需求/设计/开发/测试/修复中的当前子环节 --> Agnet 按需要调用已授权的 SK 工具 +-> Agent 执行需求/设计/开发/测试/修复中的当前子环节 +-> Agent 按需要调用已授权的 SK 工具 -> Heicode 回传中间结果给客户端 -> 用户继续追问、修正或审批 --> Agnet 继续下一子环节 --> 最终由 Agnet 完成交付整理与部署 +-> Agent 继续下一子环节 +-> 最终由 Agent 完成交付整理与部署 ``` 这意味着用户看到的不是一次性“已部署 Agent”,而是一个可连续推进的开发循环。 @@ -177,8 +177,8 @@ MVP 最小路径: 6. 选择推荐角色。 7. 确认资源权限。 8. 预览 manifest。 -9. 创建 Agnet 部署占位任务。 +9. 创建 Agent 部署占位任务。 10. 在客户端持续推进子环节开发与测试。 11. 查看任务状态、日志、审计和交付结果。 -当前唯一可延期项:Heicode 到 Agnet 平台真实部署 API 的完整联调。 +当前唯一可延期项:Heicode 到 Agent 平台真实部署 API 的完整联调。 diff --git a/docs/product-package/04-platform-usage-guide.md b/docs/product-package/04-platform-usage-guide.md index a7c9e74..245ce9b 100644 --- a/docs/product-package/04-platform-usage-guide.md +++ b/docs/product-package/04-platform-usage-guide.md @@ -47,7 +47,7 @@ macOS 提示说明: ## 第三步:在客户端输入想法 -主要在 Heicode 客户端输入要做的事情。Manager 负责下载、资源绑定、Agnet 部署、状态和审计等辅助能力,不承担主开发对话。 +主要在 Heicode 客户端输入要做的事情。Manager 负责下载、资源绑定、Agent 部署、状态和审计等辅助能力,不承担主开发对话。 例如: @@ -93,12 +93,12 @@ macOS 提示说明: 用户可以使用平台推荐角色: -- Product Agnet。 -- Architect Agnet。 -- Frontend Agnet。 -- Backend Agnet。 -- Reviewer Agnet。 -- Ops Agnet。 +- Product Agent。 +- Architect Agent。 +- Frontend Agent。 +- Backend Agent。 +- Reviewer Agent。 +- Ops Agent。 每个角色确认: @@ -120,20 +120,20 @@ macOS 提示说明: - 访问生产密钥。 - 大额模型预算消耗。 -用户确认后,平台才会为子 Agnet 派生短期凭证。 +用户确认后,平台才会为子 Agent 派生短期凭证。 ## 第七步:启动任务 用户确认任务后,Heicode 会生成: -- 子 Agnet 角色。 +- 子 Agent 角色。 - AGENT.md。 - resource context。 - permission manifest。 - 模型和预算配置。 - 审批策略。 -Heicode 将这些内容提交给 Agnet 平台执行。 +Heicode 将这些内容提交给 Agent 平台执行。 ## 第八步:在客户端持续推进 @@ -144,8 +144,8 @@ Heicode 将这些内容提交给 Agnet 平台执行。 ```text 客户端继续补充要求 -> Heicode 生成下一步子任务 --> Agnet 执行需求/开发/测试/修复/部署中的当前环节 --> Agnet 按权限调用需要的 SK 工具 +-> Agent 执行需求/开发/测试/修复/部署中的当前环节 +-> Agent 按权限调用需要的 SK 工具 -> 中间结果和失败原因回到客户端 -> 用户继续修正或审批 -> 最终返回交付物和部署结果 @@ -156,7 +156,7 @@ Heicode 将这些内容提交给 Agnet 平台执行。 用户可以在 Heicode 查看: - 当前任务状态。 -- 子 Agnet 活动状态。 +- 子 Agent 活动状态。 - 运行日志。 - 失败原因。 - 模型用量。 diff --git a/docs/product-package/05-ppt-copy.md b/docs/product-package/05-ppt-copy.md index 9513b43..583d3f6 100644 --- a/docs/product-package/05-ppt-copy.md +++ b/docs/product-package/05-ppt-copy.md @@ -39,8 +39,8 @@ Heicode 是一款全流程智能开发 Code 工具。它让用户输入想法, - 用户输入自然语言目标。 - Heicode 生成任务、角色、权限和执行上下文。 - 客户端是主体验,Manager 负责资源、部署和审计辅助。 -- 子 Agnet 按角色推进开发、测试、修复和部署,并可调用 SK 工具。 -- 密钥进入密钥保管器,子 Agnet 只拿短期凭证。 +- 子 Agent 按角色推进开发、测试、修复和部署,并可调用 SK 工具。 +- 密钥进入密钥保管器,子 Agent 只拿短期凭证。 - Heicode 展示模型用量、日志、事件和审计。 ## Slide 4:产品工作流 @@ -55,11 +55,11 @@ Heicode 是一款全流程智能开发 Code 工具。它让用户输入想法, 登录 -> 客户端输入想法 -> Manager 绑定资源 --> Manager 部署 Agnet +-> Manager 部署 Agent -> 客户端继续推进任务 --> Agnet 执行开发/测试/修复并调用 SK +-> Agent 执行开发/测试/修复并调用 SK -> 审批高危操作 --> Agnet 交付与部署 +-> Agent 交付与部署 -> 观测与维护 ``` @@ -75,7 +75,7 @@ Heicode 是一款全流程智能开发 Code 工具。它让用户输入想法, 1. 想法转开发任务。 2. 资源绑定和权限分配。 -3. 子 Agnet 编排和执行。 +3. 子 Agent 编排和执行。 4. 用量、日志、安全和审计闭环。 ## Slide 6:系统架构 @@ -89,7 +89,7 @@ Heicode 是一款全流程智能开发 Code 工具。它让用户输入想法, ```text 用户 / 客户端 -> Heicode Manager --> Agnet 平台 +-> Agent 平台 -> CodeGW -> Azure Key Vault -> Git / SK / 云资源 @@ -97,7 +97,7 @@ Heicode 是一款全流程智能开发 Code 工具。它让用户输入想法, 讲述: -客户端是主体验,Manager 是辅助控制台。Agnet 平台是执行层,执行过程中可调用 SK。CodeGW 是模型网关和计费服务。Azure Key Vault 是密钥保管器。 +客户端是主体验,Manager 是辅助控制台。Agent 平台是执行层,执行过程中可调用 SK。CodeGW 是模型网关和计费服务。Azure Key Vault 是密钥保管器。 ## Slide 7:安全模型 @@ -109,7 +109,7 @@ Heicode 是一款全流程智能开发 Code 工具。它让用户输入想法, - Heicode 服务端数据库只保存 `secret_ref`。 - 长期密钥进入 Azure Key Vault。 -- 子 Agnet 只获得短期、最小权限凭证。 +- 子 Agent 只获得短期、最小权限凭证。 - 高危操作必须在客户端审批。 - 日志、Markdown、前端响应不包含明文密钥。 @@ -147,7 +147,7 @@ Heicode 是一款全流程智能开发 Code 工具。它让用户输入想法, 延期项: -- Heicode 到 Agnet 平台真实部署 API 的完整联调。 +- Heicode 到 Agent 平台真实部署 API 的完整联调。 ## Slide 10:路线图 @@ -160,7 +160,7 @@ Heicode 是一款全流程智能开发 Code 工具。它让用户输入想法, 1. 资源绑定和密钥保管。 2. CodeGW 用户侧模型和用量展示。 3. 客户端登录、审批和下载闭环。 -4. Agnet 平台真实部署联调。 +4. Agent 平台真实部署联调。 5. 自动维护、升级、成本和质量观测。 ## Slide 11:演示故事 @@ -174,9 +174,9 @@ Heicode 是一款全流程智能开发 Code 工具。它让用户输入想法, 1. 登录 Heicode。 2. 客户端输入产品想法。 3. Manager 绑定 Git 和云资源。 -4. Manager 部署 Agnet 并展示权限。 +4. Manager 部署 Agent 并展示权限。 5. 客户端继续推进任务。 -6. 展示 Agnet 调用 SK 和当前子环节。 +6. 展示 Agent 调用 SK 和当前子环节。 7. 客户端审批高危操作。 8. 查看交付、部署结果、日志和模型用量。 diff --git a/docs/product-package/06-security-and-credential-management.md b/docs/product-package/06-security-and-credential-management.md index 13f0b54..a0e3110 100644 --- a/docs/product-package/06-security-and-credential-management.md +++ b/docs/product-package/06-security-and-credential-management.md @@ -2,14 +2,14 @@ ## 核心原则 -Heicode 是 SaaS 产品,不能把凭证管理转嫁给用户,也不能让密钥散落在 Git、Markdown、日志或子 Agnet 长期状态中。 +Heicode 是 SaaS 产品,不能把凭证管理转嫁给用户,也不能让密钥散落在 Git、Markdown、日志或子 Agent 长期状态中。 安全目标: 1. 用户授权资源,平台托管凭证。 2. Heicode 服务端数据库只保存元数据和 `secret_ref`。 3. 长期密钥进入密钥保管器。 -4. 子 Agnet 只拿短期、最小权限、可审计凭证。 +4. 子 Agent 只拿短期、最小权限、可审计凭证。 5. 高危操作必须由客户端审批。 ## 凭证分类 @@ -43,17 +43,17 @@ Heicode 是 SaaS 产品,不能把凭证管理转嫁给用户,也不能让密 Heicode 接口不得返回明文密钥。日志、错误信息、审计摘要也不得包含明文密钥。 -## 子 Agnet 访问凭证 +## 子 Agent 访问凭证 -子 Agnet 不保存长期密钥。 +子 Agent 不保存长期密钥。 推荐流程: ```text Heicode 生成 Resource Grant --> Agnet 平台部署子 Agnet --> Agnet 平台绑定运行时身份 --> 子 Agnet 请求使用资源 +-> Agent 平台部署子 Agent +-> Agent 平台绑定运行时身份 +-> 子 Agent 请求使用资源 -> 高危操作先走客户端审批 -> 审批通过后派生短期凭证 -> 短期凭证注入运行时 @@ -86,7 +86,7 @@ Heicode 生成 Resource Grant 2. 禁止把密钥写入 Markdown。 3. 禁止前端返回明文密钥。 4. 禁止在日志中打印密钥。 -5. 禁止把长期云密钥注入子 Agnet。 +5. 禁止把长期云密钥注入子 Agent。 6. 禁止把 Azure Key Vault 管理入口作为普通用户入口暴露。 7. 禁止把 CodeGW key 原文交给客户端长期保存。 @@ -94,7 +94,7 @@ Heicode 生成 Resource Grant 推荐文案: -> Heicode 会把你的授权凭证存入密钥保管器。平台只在你授权的范围内,为对应任务和角色生成短期访问能力。密钥不会写入 Git、文档、日志或子 Agnet 长期状态。 +> Heicode 会把你的授权凭证存入密钥保管器。平台只在你授权的范围内,为对应任务和角色生成短期访问能力。密钥不会写入 Git、文档、日志或子 Agent 长期状态。 ## 验收标准 @@ -103,6 +103,6 @@ Heicode 生成 Resource Grant | 数据库 | 只保存 `secret_ref`,不保存明文密钥 | | 前端 | 不显示明文密钥 | | 日志 | 不包含 token、password、private key、access key | -| 子 Agnet | 不保存长期凭证 | +| 子 Agent | 不保存长期凭证 | | 高危操作 | 有客户端审批记录 | | Azure Key Vault | 普通用户不可直接进入;Manager 仅通过受控后端身份访问 | diff --git a/docs/product-package/07-integration-boundaries.md b/docs/product-package/07-integration-boundaries.md index af7b70f..3743b4c 100644 --- a/docs/product-package/07-integration-boundaries.md +++ b/docs/product-package/07-integration-boundaries.md @@ -1,8 +1,8 @@ -# 07. CodeGW、Azure Key Vault、Agnet 平台边界说明 +# 07. CodeGW、Azure Key Vault、Agent 平台边界说明 ## 目标 -本文用于讲清楚 Heicode 与 CodeGW、Azure Key Vault、Agnet 平台之间的产品和技术边界,避免再次变成缝合式系统。 +本文用于讲清楚 Heicode 与 CodeGW、Azure Key Vault、Agent 平台之间的产品和技术边界,避免再次变成缝合式系统。 ## 总体边界 @@ -11,7 +11,7 @@ | Heicode | 产品总入口,包含客户端主体验与 Manager 辅助控制台 | 是 | | CodeGW | 模型网关、模型调用、余额、额度、用量日志 | 否 | | Azure Key Vault | 长期凭证托管、密钥引用、后端受控读取 | 否 | -| Agnet 平台 | AKS 执行层、状态、日志、事件、指标 | 否 | +| Agent 平台 | AKS 执行层、状态、日志、事件、指标 | 否 | ## Heicode 与 CodeGW @@ -51,15 +51,15 @@ Heicode 不做: 产品口径: -> 密钥保管器负责保存凭证,Heicode 只保存引用。子 Agnet 使用资源时,平台按权限和审批结果获取短期访问能力。 +> 密钥保管器负责保存凭证,Heicode 只保存引用。子 Agent 使用资源时,平台按权限和审批结果获取短期访问能力。 -## Heicode 与 Agnet 平台 +## Heicode 与 Agent 平台 -Heicode 传给 Agnet 平台: +Heicode 传给 Agent 平台: - 用户上下文。 - 任务目标。 -- 子 Agnet 角色。 +- 子 Agent 角色。 - AGENT.md。 - resource context。 - permission manifest。 @@ -69,10 +69,10 @@ Heicode 传给 Agnet 平台: - `sk_context` / `tool_permissions`。 - 回调地址。 -Agnet 平台回传 Heicode: +Agent 平台回传 Heicode: - deployment 状态。 -- 子 Agnet 状态。 +- 子 Agent 状态。 - 事件。 - 日志。 - 指标。 @@ -91,7 +91,7 @@ Heicode 不传: 产品口径: -> Heicode 决定用户想做什么、能用什么资源和谁来做;Agnet 平台负责把这些任务安全运行起来,在过程中按权限调用需要的 SK 工具,并把子环节状态、交付物和部署结果回传给 Heicode。 +> Heicode 决定用户想做什么、能用什么资源和谁来做;Agent 平台负责把这些任务安全运行起来,在过程中按权限调用需要的 SK 工具,并把子环节状态、交付物和部署结果回传给 Heicode。 ## 客户端与 Manager 的边界 @@ -109,7 +109,7 @@ Manager 主做: - 账号与安全。 - 客户端下载。 - Git、文档、SK、云资源绑定。 -- Agnet 部署。 +- Agent 部署。 - 任务状态、用量和审计查看。 - 风险与授权范围确认。 @@ -128,7 +128,7 @@ Manager 不做: ## 当前延期项 -Heicode 到 Agnet 平台真实部署 API 的完整联调仍可延期。 +Heicode 到 Agent 平台真实部署 API 的完整联调仍可延期。 但 Heicode 侧必须准备好: @@ -145,4 +145,4 @@ Heicode 到 Agnet 平台真实部署 API 的完整联调仍可延期。 2. 客户端只登录 Heicode。 3. CodeGW 独立服务运行。 4. Azure Key Vault 只通过 Manager 后端受控身份访问,普通用户不直接进入。 -5. Agnet 平台接口只收结构化上下文和 `secret_ref`,不收长期明文密钥。 +5. Agent 平台接口只收结构化上下文和 `secret_ref`,不收长期明文密钥。 diff --git a/docs/product-package/08-client-guide.md b/docs/product-package/08-client-guide.md index 60e767a..aca89af 100644 --- a/docs/product-package/08-client-guide.md +++ b/docs/product-package/08-client-guide.md @@ -4,7 +4,7 @@ Heicode 客户端是用户主体验,承担本地对话、想法输入、任务推进、执行反馈、交付接收和高危审批。它不是独立模型配置工具,也不是 CodeGW 后台客户端。 -Heicode Manager 不是主体验,但它仍然负责账号与安全、客户端下载、Git/云资源绑定、Agnet 部署、任务状态、余额与审计等辅助操作。用户不在网页上编码,主要在客户端里推进开发任务。 +Heicode Manager 不是主体验,但它仍然负责账号与安全、客户端下载、Git/云资源绑定、Agent 部署、任务状态、余额与审计等辅助操作。用户不在网页上编码,主要在客户端里推进开发任务。 客户端必须保持: @@ -41,25 +41,25 @@ Heicode | 功能 | 说明 | |------|------| | 对话输入 | 用户描述产品想法、开发任务或维护需求 | -| 任务推进 | 继续要求、查看 Agnet 执行反馈、接收交付结果 | -| 任务状态 | 查看 Heicode 返回的任务和子 Agnet 进度 | +| 任务推进 | 继续要求、查看 Agent 执行反馈、接收交付结果 | +| 任务状态 | 查看 Heicode 返回的任务和子 Agent 进度 | | 模型选择 | 从 Heicode 提供的模型中选择 | | 高危审批 | 对生产部署、密钥访问、云操作做确认 | | 日志反馈 | 展示关键执行反馈和失败原因 | | 安装更新 | 从 Heicode 下载页获取安装包 | -## 客户端里的 Agnet 闭环 +## 客户端里的 Agent 闭环 客户端里发生的不是单次对话,而是持续推进的任务闭环: ```text 用户输入想法或补充要求 -> Heicode 判断当前子任务 --> 调用 Agnet 执行需求/开发/测试/修复/部署中的某一环 --> Agnet 按权限调用需要的 SK 工具 +-> 调用 Agent 执行需求/开发/测试/修复/部署中的某一环 +-> Agent 按权限调用需要的 SK 工具 -> 中间结果、失败原因和下一步建议回到客户端 -> 用户继续补充、修正或审批 --> Agnet 继续推进 +-> Agent 继续推进 -> 最终返回交付物和部署结果 ``` diff --git a/docs/product-package/09-demo-script.md b/docs/product-package/09-demo-script.md index 25e58a1..4d9aa7b 100644 --- a/docs/product-package/09-demo-script.md +++ b/docs/product-package/09-demo-script.md @@ -85,7 +85,7 @@ 话术: -> 资源绑定不是把密钥贴到页面里。用户授权后,真实凭证进入密钥保管器,Heicode 只保存 secret_ref。子 Agnet 能看到的是资源元数据和权限,不是长期密钥。 +> 资源绑定不是把密钥贴到页面里。用户授权后,真实凭证进入密钥保管器,Heicode 只保存 secret_ref。子 Agent 能看到的是资源元数据和权限,不是长期密钥。 展示: @@ -97,13 +97,13 @@ 操作: -1. 展示推荐子 Agnet 角色。 +1. 展示推荐子 Agent 角色。 2. 打开某个角色权限。 3. 展示 manifest 预览。 话术: -> Heicode 会把资源按角色授权。例如 Backend Agnet 可以读写后端路径,Ops Agnet 可以部署测试环境,生产部署需要客户端审批。 +> Heicode 会把资源按角色授权。例如 Backend Agent 可以读写后端路径,Ops Agent 可以部署测试环境,生产部署需要客户端审批。 展示: @@ -134,18 +134,18 @@ 操作: 1. 切回客户端,追加一句新要求。 -2. 展示当前 Agnet 子环节和 SK 工具调用。 +2. 展示当前 Agent 子环节和 SK 工具调用。 3. 展示任务状态、日志、模型用量和审计。 话术: -> 主体验在客户端。用户继续补充要求后,Heicode 会调用 Agnet 推进下一子环节,例如开发、测试或修复。Agnet 在过程中还可以调用已授权的 SK 工具,最后把中间结果、交付物和部署结果回传回来。Manager 负责辅助查看状态、日志、用量和审计。 +> 主体验在客户端。用户继续补充要求后,Heicode 会调用 Agent 推进下一子环节,例如开发、测试或修复。Agent 在过程中还可以调用已授权的 SK 工具,最后把中间结果、交付物和部署结果回传回来。Manager 负责辅助查看状态、日志、用量和审计。 展示: - 客户端追加需求。 -- 当前 Agnet 子环节和 SK 工具调用。 -- 子 Agnet 状态。 +- 当前 Agent 子环节和 SK 工具调用。 +- 子 Agent 状态。 - 日志。 - 用量。 - 审计记录。 @@ -168,10 +168,10 @@ 2. 不进入 CodeGW 管理后台。 3. 不展示 Azure Key Vault access token。 4. 不强调底层配置复杂度,强调用户流程。 -5. 如果 Agnet 真实部署尚未联调,明确说当前展示的是 Heicode 侧 payload、manifest 和占位控制面。 +5. 如果 Agent 真实部署尚未联调,明确说当前展示的是 Heicode 侧 payload、manifest 和占位控制面。 ## 备用 1 分钟版本 话术: -> Heicode 是从想法到上线的智能开发工具。用户主要在客户端输入想法和继续推进任务,在 Manager 绑定 Git、文档、SK 和云资源并部署 Agnet。Heicode 生成子 Agnet 团队、角色和权限,密钥进入密钥保管器,子 Agnet 只拿短期凭证。Agnet 在执行中可以调用 SK 工具,最后完成交付与部署;CodeGW 提供模型、余额和用量,Heicode 展示日志、状态和审计。用户不需要进入 CodeGW 后台,也不需要管理 Azure Key Vault,只在 Heicode 和客户端完成整个开发流程。 +> Heicode 是从想法到上线的智能开发工具。用户主要在客户端输入想法和继续推进任务,在 Manager 绑定 Git、文档、SK 和云资源并部署 Agent。Heicode 生成子 Agent 团队、角色和权限,密钥进入密钥保管器,子 Agent 只拿短期凭证。Agent 在执行中可以调用 SK 工具,最后完成交付与部署;CodeGW 提供模型、余额和用量,Heicode 展示日志、状态和审计。用户不需要进入 CodeGW 后台,也不需要管理 Azure Key Vault,只在 Heicode 和客户端完成整个开发流程。 diff --git a/docs/product-package/10-frontend-detail-spec.md b/docs/product-package/10-frontend-detail-spec.md index 4e62f9a..b8a2aca 100644 --- a/docs/product-package/10-frontend-detail-spec.md +++ b/docs/product-package/10-frontend-detail-spec.md @@ -177,7 +177,7 @@ Heicode 只追问影响执行路径的少量问题: | 区块 | 内容 | |------|------| | 当前子环节 | 需求、设计、后端、前端、检查、测试、部署的阶段状态 | -| Agnet 调用 | 当前角色、正在调用的 SK 工具、工具输出摘要 | +| Agent 调用 | 当前角色、正在调用的 SK 工具、工具输出摘要 | | 最新动态 | Heicode 翻译后的关键执行事件 | | 当前产物 | 产品文档、接口草案、代码变更、检查报告 | | 继续输入 | 用户继续追加需求或修正方向 | @@ -190,7 +190,7 @@ Heicode 只追问影响执行路径的少量问题: - 失败必须展示可理解原因和建议动作。 - 停止任务要说明影响范围。 - 当前子环节必须可见,不能只显示“运行中”。 -- 如果 Agnet 调用了 SK 工具,前端要展示“调用了什么、产出了什么、是否失败”。 +- 如果 Agent 调用了 SK 工具,前端要展示“调用了什么、产出了什么、是否失败”。 - 交付和部署结果必须回到客户端任务空间,而不是只留在后台日志里。 ## 任务用量与审计 diff --git a/docs/product-package/11-product-prototype-wireframes.md b/docs/product-package/11-product-prototype-wireframes.md index 9b5fed9..faa70c5 100644 --- a/docs/product-package/11-product-prototype-wireframes.md +++ b/docs/product-package/11-product-prototype-wireframes.md @@ -7,19 +7,19 @@ 核心边界: - Heicode 客户端是主体验。用户主要在客户端里对话、描述想法、推进开发任务、查看执行反馈和审批高危操作。 -- Heicode Manager / 浏览器控制台是辅助控制台。它负责账号与安全、客户端下载、Git 绑定、云资源绑定、Agnet 部署、任务状态总览、模型余额与用量、审计与日志查看。 +- Heicode Manager / 浏览器控制台是辅助控制台。它负责账号与安全、客户端下载、Git 绑定、云资源绑定、Agent 部署、任务状态总览、模型余额与用量、审计与日志查看。 - 用户不在网页上编码,也不在网页上完成主要开发对话。 - 用户会在 Manager 里做辅助操作和观测,但主开发体验仍然在客户端。 -- CodeGW、密钥保管器、Agnet 平台后台都不是普通用户入口,只作为 Heicode 的底层能力。 +- CodeGW、密钥保管器、Agent 平台后台都不是普通用户入口,只作为 Heicode 的底层能力。 ## 原型范围 | 范围 | 是否属于本文原型 | 说明 | |------|------------------|------| | Heicode 客户端 | 是,主体验 | 本地对话、想法输入、任务推进、执行反馈、高危审批 | -| Heicode Manager / 浏览器控制台 | 是,辅助控制台 | 账号与安全、客户端下载、Git/云资源绑定、Agnet 部署、任务总览、余额与审计 | +| Heicode Manager / 浏览器控制台 | 是,辅助控制台 | 账号与安全、客户端下载、Git/云资源绑定、Agent 部署、任务总览、余额与审计 | | 网页编码界面 | 否 | 用户不在网页上编码 | -| 底层服务后台 | 否 | CodeGW、密钥保管器、Agnet 平台后台不直接暴露给普通用户 | +| 底层服务后台 | 否 | CodeGW、密钥保管器、Agent 平台后台不直接暴露给普通用户 | ## 原型原则 @@ -27,8 +27,8 @@ 2. Manager 承载资源准备、部署准备、任务总览、用量和审计等辅助能力。 3. 主开发对话、编码推进和高危审批仍然以客户端为主。 4. 高危动作必须在客户端清晰审批。 -5. 继续开发时,Heicode 会持续调用 Agnet 推进需求、开发、测试、修复和部署等子环节。 -6. Agnet 执行过程中可以按权限调用 SK 工具。 +5. 继续开发时,Heicode 会持续调用 Agent 推进需求、开发、测试、修复和部署等子环节。 +6. Agent 执行过程中可以按权限调用 SK 工具。 7. Manager 可以查看任务状态、用量和审计,但不把网页设计成 IDE 或网页编码工作台。 8. 客户端只登录 Heicode,只使用 Heicode 提供的模型。 @@ -39,8 +39,8 @@ flowchart LR Client["Heicode 客户端主体验"] --> Idea["输入想法 / 继续开发"] Idea --> Task["生成任务卡"] Task --> Manager["Manager 准备资源 / 查看辅助信息"] - Manager --> Deploy["Manager 部署 Agnet"] - Deploy --> Loop["Agnet 子环节循环"] + Manager --> Deploy["Manager 部署 Agent"] + Deploy --> Loop["Agent 子环节循环"] Loop --> Dev["需求 / 开发 / 测试 / 修复"] Dev --> SK["按权限调用 SK 工具"] SK --> Run["执行反馈和对话"] @@ -106,7 +106,7 @@ flowchart LR │ │ 后端实现中 │ │ 等待生产部署审批 │ │ │ └────────────────────────────┘ └────────────────────────────┘ │ │ │ -│ 辅助:打开 Manager 绑定资源、部署 Agnet、查看状态 │ +│ 辅助:打开 Manager 绑定资源、部署 Agent、查看状态 │ └──────────────────────────────────────────────────────────────────────────────┘ ``` @@ -162,7 +162,7 @@ Heicode 在客户端里追问关键缺口,并生成任务卡。 │ 产品说明 / 原型描述 / 开发任务 / 检查清单 / 部署步骤 │ │ │ │ 需要 Manager 辅助确认 │ -│ [绑定 Git] [绑定云资源] [部署 Agnet] [查看预算与审计] │ +│ [绑定 Git] [绑定云资源] [部署 Agent] [查看预算与审计] │ │ │ │ [修改目标] [去 Manager 准备] │ └──────────────────────────────────────────────────────────────────────────────┘ @@ -188,10 +188,10 @@ Manager 是网页端辅助控制台,不是编码主体验,但依然承载资 │ │ ¥128.40 / 今日 ¥12.30 │ │ 1 个高危审批待处理 │ │ │ └────────────────────────────┘ └────────────────────────────┘ │ │ ┌────────────────────────────────────────────────────────────────────────┐ │ -│ │ Agnet 部署 │ │ +│ │ Agent 部署 │ │ │ │ 角色:Product / Backend / Frontend / Reviewer / Ops │ │ │ │ 状态:待部署 │ │ -│ │ [部署 Agnet] │ │ +│ │ [部署 Agent] │ │ │ └────────────────────────────────────────────────────────────────────────┘ │ │ │ │ 你可以在这里做辅助操作: │ @@ -209,7 +209,7 @@ Manager 是网页端辅助控制台,不是编码主体验,但依然承载资 ## 5. Manager 绑定 Git 和云资源 -Git 和云资源在 Manager 中绑定,作为部署 Agnet 的前置条件。 +Git 和云资源在 Manager 中绑定,作为部署 Agent 的前置条件。 ```text ┌──────────────────────────────────────────────────────────────────────────────┐ @@ -229,7 +229,7 @@ Git 和云资源在 Manager 中绑定,作为部署 Agnet 的前置条件。 │ │ [选择本任务允许使用的资源] │ │ │ └──────────────────────────────────────────────────────────────────────────┘ │ │ │ -│ [保存资源绑定] [继续部署 Agnet] │ +│ [保存资源绑定] [继续部署 Agent] │ └──────────────────────────────────────────────────────────────────────────────┘ ``` @@ -237,7 +237,7 @@ Git 和云资源在 Manager 中绑定,作为部署 Agnet 的前置条件。 - Git 和云资源绑定属于 Manager。 - 用户不在网页上写代码。 -- 绑定完成后可以继续部署 Agnet,也可以返回查看任务状态与辅助信息。 +- 绑定完成后可以继续部署 Agent,也可以返回查看任务状态与辅助信息。 ## 6. Manager 云资源自动发现 @@ -261,11 +261,11 @@ Git 和云资源在 Manager 中绑定,作为部署 Agnet 的前置条件。 └──────────────────────────────────────────────────────────────────────────────┘ ``` -## 7. Manager 部署 Agnet +## 7. Manager 部署 Agent ```text ┌──────────────────────────────────────────────────────────────────────────────┐ -│ Heicode Manager / 部署 Agnet │ +│ Heicode Manager / 部署 Agent │ ├──────────────────────────────────────────────────────────────────────────────┤ │ 任务:小团队任务管理 SaaS │ │ │ @@ -273,7 +273,7 @@ Git 和云资源在 Manager 中绑定,作为部署 Agnet 的前置条件。 │ - Git:repo-main / main │ │ - 云资源:aks-test、db-dev │ │ │ -│ Agnet 角色 │ +│ Agent 角色 │ │ [x] Product 需求和验收 │ │ [x] Backend API / 数据模型 │ │ [x] Frontend 页面和交互 │ @@ -283,30 +283,30 @@ Git 和云资源在 Manager 中绑定,作为部署 Agnet 的前置条件。 │ 安全边界 │ │ - 生产部署需要客户端审批 │ │ - 生产数据默认禁止 │ -│ - 长期密钥不下发给 Agnet │ +│ - 长期密钥不下发给 Agent │ │ │ -│ [返回修改资源] [部署 Agnet] │ +│ [返回修改资源] [部署 Agent] │ └──────────────────────────────────────────────────────────────────────────────┘ ``` ### 设计意图 -- Manager 的最终动作是部署 Agnet。 +- Manager 的最终动作是部署 Agent。 - Manager 不继续承担开发任务执行。 - 部署完成后主流程回到客户端,但状态和审计仍可在 Manager 查看。 ## 8. 客户端执行空间 -Agnet 部署完成后,主流程回到客户端。 +Agent 部署完成后,主流程回到客户端。 ```text ┌──────────────────────────────────────────────────────────────────────────────┐ │ Heicode Client / 小团队任务管理 SaaS 运行中 ¥12.30 │ ├──────────────────────────────────────────────────────────────────────────────┤ │ │ -│ Heicode:Agnet 已部署,我开始推进当前开发任务。 │ +│ Heicode:Agent 已部署,我开始推进当前开发任务。 │ │ │ -│ 当前 Agnet 子环节 │ +│ 当前 Agent 子环节 │ │ - 需求和原型:已完成 │ │ - 后端开发:进行中 │ │ - 前端开发:等待接口确认 │ @@ -325,15 +325,15 @@ Agnet 部署完成后,主流程回到客户端。 │ └──────────────────────────────────────────────────────────────────────────┘ │ │ [发送] │ │ │ -│ 辅助查看:打开 Manager 查看 Git/云资源绑定、Agnet 部署、用量和审计 │ +│ 辅助查看:打开 Manager 查看 Git/云资源绑定、Agent 部署、用量和审计 │ └──────────────────────────────────────────────────────────────────────────────┘ ``` ### 设计意图 - 主执行反馈在客户端。 -- 用户看到的是 Agnet 持续推进的子环节,而不是一次性执行结果。 -- Agnet 可以在执行过程中调用已授权的 SK 工具。 +- 用户看到的是 Agent 持续推进的子环节,而不是一次性执行结果。 +- Agent 可以在执行过程中调用已授权的 SK 工具。 - Manager 仍然是资源、部署、状态、余额和审计的辅助入口。 - 用户持续通过自然语言推进任务。 @@ -363,7 +363,7 @@ Agnet 部署完成后,主流程回到客户端。 │ Heicode Client / 小团队任务管理 SaaS 已完成 │ ├──────────────────────────────────────────────────────────────────────────────┤ │ │ -│ Heicode:第一版已完成,Agnet 已整理交付物并完成目标环境部署。 │ +│ Heicode:第一版已完成,Agent 已整理交付物并完成目标环境部署。 │ │ │ │ 交付物 │ │ ┌────────────────────┐ ┌────────────────────┐ ┌────────────────────┐ │ @@ -376,7 +376,7 @@ Agnet 部署完成后,主流程回到客户端。 │ - 安全检查:1 个问题已修复 │ │ - 部署:测试环境成功,生产环境待你确认后可继续 │ │ │ -│ [继续迭代] [发起维护任务] [查看 Agnet 部署信息] │ +│ [继续迭代] [发起维护任务] [查看 Agent 部署信息] │ └──────────────────────────────────────────────────────────────────────────────┘ ``` @@ -424,12 +424,12 @@ Agnet 部署完成后,主流程回到客户端。 客户端说目标 -> Heicode 生成任务 -> Manager 绑定 Git 和云资源 --> Manager 部署 Agnet +-> Manager 部署 Agent -> 客户端持续追加需求和修正方向 --> Agnet 执行需求、开发、测试、修复等子环节 --> Agnet 按权限调用 SK 工具 +-> Agent 执行需求、开发、测试、修复等子环节 +-> Agent 按权限调用 SK 工具 -> 客户端接收中间结果并审批高危动作 --> Agnet 完成交付整理和部署 +-> Agent 完成交付整理和部署 -> Manager 提供状态、余额、下载和审计辅助 -> 客户端接收交付结果 ``` @@ -441,8 +441,8 @@ flowchart TD ClientHome["客户端输入想法"] --> ClientAsk["客户端追问缺口"] ClientAsk --> Task["客户端任务卡"] Task --> Manager["Manager 绑定资源 / 查看状态"] - Manager --> Deploy["Manager 部署 Agnet"] - Deploy --> Loop["Agnet 子环节循环"] + Manager --> Deploy["Manager 部署 Agent"] + Deploy --> Loop["Agent 子环节循环"] Loop --> ClientRun["客户端执行空间"] Loop --> SK["SK 工具调用"] SK --> ClientRun diff --git a/docs/product-package/12-narrated-user-operation-flow.md b/docs/product-package/12-narrated-user-operation-flow.md index 2f05267..d37c4d2 100644 --- a/docs/product-package/12-narrated-user-operation-flow.md +++ b/docs/product-package/12-narrated-user-operation-flow.md @@ -2,7 +2,7 @@ ## 使用场景 -本文用于产品演示、培训、录屏旁白和销售讲解。语气以真实用户操作为主,不讲复杂技术实现;当需要解释安全、模型、密钥和 Agnet 平台时,只讲用户需要理解的边界。 +本文用于产品演示、培训、录屏旁白和销售讲解。语气以真实用户操作为主,不讲复杂技术实现;当需要解释安全、模型、密钥和 Agent 平台时,只讲用户需要理解的边界。 ## 一句话开场 @@ -81,7 +81,7 @@ https://code.xinghanlab.com - 第一版包含哪些功能。 - 推荐哪些页面和接口。 - 需要哪些代码仓库、文档、SK 和云资源。 -- 建议使用哪些子 Agnet 角色。 +- 建议使用哪些子 Agent 角色。 - 哪些操作可能属于高危操作。 如果草案不准确,我可以直接修改。确认后,Heicode 会提示我补充这个任务需要的上下文。 @@ -105,7 +105,7 @@ https://code.xinghanlab.com 如果我有产品文档、原型描述或技能仓库,我继续绑定项目文档和 SK。 -项目文档可以作为 Product Agnet 和 Architect Agnet 的上下文。SK 可以作为子 Agnet 可调用的技能来源。 +项目文档可以作为 Product Agent 和 Architect Agent 的上下文。SK 可以作为子 Agent 可调用的技能来源。 绑定后,平台知道: @@ -154,18 +154,18 @@ https://code.xinghanlab.com 我不会看到明文密钥。平台最多展示 `secret_ref` 这种引用,让我知道凭证由密钥保管器管理。 -### 12. 生成子 Agnet 团队 +### 12. 生成子 Agent 团队 -资源准备好后,Heicode 会推荐一组子 Agnet 角色。 +资源准备好后,Heicode 会推荐一组子 Agent 角色。 例如: -- Product Agnet:负责需求和验收标准。 -- Architect Agnet:负责架构和技术方案。 -- Frontend Agnet:负责页面和交互。 -- Backend Agnet:负责 API、数据模型和服务逻辑。 -- Reviewer Agnet:负责代码检查和安全审查。 -- Ops Agnet:负责构建、部署和运行观察。 +- Product Agent:负责需求和验收标准。 +- Architect Agent:负责架构和技术方案。 +- Frontend Agent:负责页面和交互。 +- Backend Agent:负责 API、数据模型和服务逻辑。 +- Reviewer Agent:负责代码检查和安全审查。 +- Ops Agent:负责构建、部署和运行观察。 我可以使用推荐团队,也可以增减角色。 @@ -175,18 +175,18 @@ https://code.xinghanlab.com 例如: -Backend Agnet 可以: +Backend Agent 可以: - 读项目文档。 - 读写后端代码路径。 - 使用开发数据库。 -Frontend Agnet 可以: +Frontend Agent 可以: - 读项目文档。 - 读写前端代码路径。 -Ops Agnet 可以: +Ops Agent 可以: - 部署测试环境。 - 请求生产部署审批。 @@ -212,8 +212,8 @@ manifest 不会显示 Git token、云 access key、SSH 私钥、数据库密码 在开始执行前,Heicode 会用一张确认卡展示本次任务摘要: - 本次要做什么。 -- 会启动哪些子 Agnet。 -- 每个子 Agnet 使用哪些资源。 +- 会启动哪些子 Agent。 +- 每个子 Agent 使用哪些资源。 - 是否会访问密钥。 - 是否会部署云资源。 - 预计模型预算。 @@ -228,7 +228,7 @@ manifest 不会显示 Git token、云 access key、SSH 私钥、数据库密码 例如: ```text -Ops Agnet 请求部署到生产环境。 +Ops Agent 请求部署到生产环境。 目标资源:aks-prod 操作:cloud:deploy 风险等级:高 @@ -237,7 +237,7 @@ Ops Agnet 请求部署到生产环境。 这时我可以批准或拒绝。 -如果我批准,平台会使用密钥保管器派生短期、最小权限凭证。子 Agnet 不会拿到长期密钥。 +如果我批准,平台会使用密钥保管器派生短期、最小权限凭证。子 Agent 不会拿到长期密钥。 ### 17. 在客户端持续推进开发 @@ -248,11 +248,11 @@ Ops Agnet 请求部署到生产环境。 ```text 我在客户端补充要求 -> Heicode 判断下一步要推进哪个子环节 --> Agnet 执行需求、开发、测试、修复或部署中的当前任务 --> Agnet 按权限调用已绑定的 SK 工具 +-> Agent 执行需求、开发、测试、修复或部署中的当前任务 +-> Agent 按权限调用已绑定的 SK 工具 -> 中间结果回到客户端 -> 我继续修正方向或批准高危动作 --> Agnet 继续推进直到交付和部署完成 +-> Agent 继续推进直到交付和部署完成 ``` ### 18. 查看执行状态 @@ -261,11 +261,11 @@ Ops Agnet 请求部署到生产环境。 我能看到: -- Product Agnet 是否完成需求。 -- Backend Agnet 是否正在写代码。 -- Frontend Agnet 是否等待后端接口。 -- Reviewer Agnet 是否发现问题。 -- Ops Agnet 是否等待审批或部署中。 +- Product Agent 是否完成需求。 +- Backend Agent 是否正在写代码。 +- Frontend Agent 是否等待后端接口。 +- Reviewer Agent 是否发现问题。 +- Ops Agent 是否等待审批或部署中。 如果失败,页面应该显示失败原因,而不是只显示一个错误码。 @@ -275,7 +275,7 @@ Ops Agnet 请求部署到生产环境。 这里能看到: -- 子 Agnet 运行日志。 +- 子 Agent 运行日志。 - 模型调用日志。 - 消耗了多少额度。 - 哪个任务使用了哪个模型。 @@ -311,15 +311,15 @@ Ops Agnet 请求部署到生产环境。 并检查现有权限模型是否支持团队成员邀请。 ``` -Heicode 会复用之前的项目上下文、资源绑定和 Agnet 角色,生成新的开发任务。 +Heicode 会复用之前的项目上下文、资源绑定和 Agent 角色,生成新的开发任务。 ## 简短演示口述版 -> 我先登录 Heicode 的浏览器控制台。这里不是 CodeGW 后台,而是资源、部署、状态和审计的辅助入口。登录后,我会在客户端输入一个产品想法,比如做一个小团队任务管理 SaaS。平台会生成需求草案、推荐资源和子 Agnet 团队。接下来我绑定 Git 仓库、项目文档、SK 和云资源。真实密钥会进入密钥保管器,Heicode 只保存 secret_ref。然后我确认每个子 Agnet 的角色和权限,比如 Backend 能写后端路径,Ops 可以部署测试环境,但生产部署需要客户端审批。任务运行后,Agnet 会持续推进开发、测试和部署,并按权限调用需要的 SK 工具;我可以看到每个子 Agnet 的状态、日志、模型用量和审计记录。客户端只登录 code.xinghanlab.com,不选择模型提供方,模型由 Heicode 提供。整个流程的重点是:从一个想法开始,把产品、代码、云资源、模型、密钥和审计组织成一个完整的软件生命周期。 +> 我先登录 Heicode 的浏览器控制台。这里不是 CodeGW 后台,而是资源、部署、状态和审计的辅助入口。登录后,我会在客户端输入一个产品想法,比如做一个小团队任务管理 SaaS。平台会生成需求草案、推荐资源和子 Agent 团队。接下来我绑定 Git 仓库、项目文档、SK 和云资源。真实密钥会进入密钥保管器,Heicode 只保存 secret_ref。然后我确认每个子 Agent 的角色和权限,比如 Backend 能写后端路径,Ops 可以部署测试环境,但生产部署需要客户端审批。任务运行后,Agent 会持续推进开发、测试和部署,并按权限调用需要的 SK 工具;我可以看到每个子 Agent 的状态、日志、模型用量和审计记录。客户端只登录 code.xinghanlab.com,不选择模型提供方,模型由 Heicode 提供。整个流程的重点是:从一个想法开始,把产品、代码、云资源、模型、密钥和审计组织成一个完整的软件生命周期。 ## 极简销售口述版 -> Heicode 让用户从一个想法开始,接入自己的代码和云资源,由 Heicode 生成 AI 开发团队。平台负责资源授权、密钥保管、模型用量和审计;子 Agnet 负责开发、检查和部署。用户只在 Heicode 和客户端中操作,不需要进入 CodeGW 后台,也不会把长期密钥交给子 Agnet。 +> Heicode 让用户从一个想法开始,接入自己的代码和云资源,由 Heicode 生成 AI 开发团队。平台负责资源授权、密钥保管、模型用量和审计;子 Agent 负责开发、检查和部署。用户只在 Heicode 和客户端中操作,不需要进入 CodeGW 后台,也不会把长期密钥交给子 Agent。 ## 口述时必须强调 @@ -328,7 +328,7 @@ Heicode 会复用之前的项目上下文、资源绑定和 Agnet 角色,生 3. CodeGW 是内部模型网关,不是普通用户后台。 4. 密钥进入密钥保管器,不进入 Git、Markdown 或日志。 5. 高危操作在客户端审批。 -6. 子 Agnet 拿短期凭证,不拿长期密钥。 +6. 子 Agent 拿短期凭证,不拿长期密钥。 7. 资源绑定和权限分配要尽量自动化和角色化。 ## 口述时不要说 @@ -338,4 +338,4 @@ Heicode 会复用之前的项目上下文、资源绑定和 Agnet 角色,生 3. 让客户端选择模型提供方。 4. 让用户手写复杂 manifest。 5. 把 tenant/project 当作当前产品主轴。 -6. 把 Agnet 平台说成用户控制台。 +6. 把 Agent 平台说成用户控制台。 diff --git a/docs/product-package/13-platform-description.md b/docs/product-package/13-platform-description.md index 3db6faf..c60ee4f 100644 --- a/docs/product-package/13-platform-description.md +++ b/docs/product-package/13-platform-description.md @@ -14,13 +14,13 @@ Heicode 平台由五个主要部分组成: | 部分 | 用户理解 | 平台职责 | |------|----------|----------| -| Heicode Manager | 浏览器辅助控制台 | 账号与安全、客户端下载、绑定资源、部署 Agnet、查看状态、余额和审计 | +| Heicode Manager | 浏览器辅助控制台 | 账号与安全、客户端下载、绑定资源、部署 Agent、查看状态、余额和审计 | | Heicode 客户端 | 本地主体验和审批入口 | 登录 Heicode、输入任务、继续开发、查看反馈、接收交付结果、审批高危操作 | -| Agnet 平台 | AI 开发团队执行层 | 在 AKS 上运行子 Agnet,回传状态、日志、事件和指标 | +| Agent 平台 | AI 开发团队执行层 | 在 AKS 上运行子 Agent,回传状态、日志、事件和指标 | | CodeGW | 模型与用量底座 | 提供模型调用、余额、额度、用量和调用日志 | | 密钥保管器 | 凭证托管服务 | 保存长期密钥,按审批和权限生成短期访问能力 | -用户主要使用客户端和 Manager。CodeGW、密钥保管器和 Agnet 平台是底层能力,不作为普通用户日常操作后台。 +用户主要使用客户端和 Manager。CodeGW、密钥保管器和 Agent 平台是底层能力,不作为普通用户日常操作后台。 ## 二、平台适合谁 @@ -62,34 +62,34 @@ Heicode 平台由五个主要部分组成: ### 3. AI 开发团队生成 -平台根据任务目标推荐子 Agnet 角色。 +平台根据任务目标推荐子 Agent 角色。 常见角色: -- Product Agnet。 -- Architect Agnet。 -- Frontend Agnet。 -- Backend Agnet。 -- Reviewer Agnet。 -- Ops Agnet。 +- Product Agent。 +- Architect Agent。 +- Frontend Agent。 +- Backend Agent。 +- Reviewer Agent。 +- Ops Agent。 -每个子 Agnet 都有明确职责、可用资源、权限范围、模型配置和审计记录。 +每个子 Agent 都有明确职责、可用资源、权限范围、模型配置和审计记录。 -### 3.1 Agnet 持续执行闭环 +### 3.1 Agent 持续执行闭环 -Heicode 不是把任务分发给 Agnet 一次就结束,而是在继续开发中持续调用 Agnet 完成子环节。 +Heicode 不是把任务分发给 Agent 一次就结束,而是在继续开发中持续调用 Agent 完成子环节。 完整闭环应表达为: ```text 客户端输入想法或追加需求 -> Manager 准备资源和部署条件 --> Heicode 调度 Agnet 执行当前子环节 --> Agnet 完成需求/开发/测试/修复/部署中的一步 --> Agnet 按权限调用 SK 工具 +-> Heicode 调度 Agent 执行当前子环节 +-> Agent 完成需求/开发/测试/修复/部署中的一步 +-> Agent 按权限调用 SK 工具 -> 中间结果和状态回传给客户端与 Manager -> 用户继续补充、修正或审批 --> 最终由 Agnet 完成交付整理和部署 +-> 最终由 Agent 完成交付整理和部署 ``` ### 4. 权限分配 @@ -99,7 +99,7 @@ Heicode 使用 Resource Grant 描述“哪个角色能用哪个资源、能做 示例: ```text -Backend Agnet +Backend Agent -> 可读项目文档 -> 可读写后端代码路径 -> 可访问开发数据库 @@ -120,7 +120,7 @@ Backend Agnet - 访问生产密钥。 - 大额模型预算消耗。 -审批通过后,平台可以从密钥保管器派生短期、最小权限凭证给子 Agnet 使用。 +审批通过后,平台可以从密钥保管器派生短期、最小权限凭证给子 Agent 使用。 ### 6. 模型与余额 @@ -152,7 +152,7 @@ Heicode 统一展示任务执行、模型调用、资源访问、高危审批和 谁 在什么时候 为了哪个任务 -让哪个子 Agnet +让哪个子 Agent 使用了哪个资源 执行了什么操作 是否经过审批 @@ -161,7 +161,7 @@ Heicode 统一展示任务执行、模型调用、资源访问、高危审批和 ### 8. 部署和维护 -Agnet 平台负责执行开发、检查、测试、修复和部署任务。Heicode 展示部署状态、失败原因、日志、指标和审计。 +Agent 平台负责执行开发、检查、测试、修复和部署任务。Heicode 展示部署状态、失败原因、日志、指标和审计。 上线后,用户可以继续输入维护和升级需求,平台复用已有项目上下文、资源绑定和权限规则。 @@ -175,14 +175,14 @@ Agnet 平台负责执行开发、检查、测试、修复和部署任务。Heico -> 客户端输入产品想法 -> 查看任务草案 -> 绑定 Git / 文档 / SK / 云资源 --> 生成子 Agnet 团队 +-> 生成子 Agent 团队 -> 分配角色权限 -> 预览 permission manifest -> 确认部署计划 --> Manager 部署 Agnet +-> Manager 部署 Agent -> 客户端持续推进任务 -> 客户端审批高危操作 --> Agnet 平台执行任务并调用 SK +-> Agent 平台执行任务并调用 SK -> Heicode 查看日志、用量、审计和交付结果 -> 后续维护和升级 ``` @@ -198,7 +198,7 @@ Heicode Manager 是浏览器辅助控制台。 - 登录和查看首页。 - 绑定资源。 - 分配角色和权限。 -- 创建或部署 Agnet 任务。 +- 创建或部署 Agent 任务。 - 查看模型和余额。 - 查看日志与审计。 - 下载客户端。 @@ -225,20 +225,20 @@ Manager 不应该变成 CodeGW 后台,也不应该要求用户直接管理底 - 直接访问密钥保管器。 - 保存长期云密钥。 -### 5.3 Agnet 平台 +### 5.3 Agent 平台 -Agnet 平台是运行时执行层。 +Agent 平台是运行时执行层。 它负责: -- 在 AKS 上部署子 Agnet。 -- 维护子 Agnet 状态。 +- 在 AKS 上部署子 Agent。 +- 维护子 Agent 状态。 - 执行开发、检查、测试、修复、部署和维护任务。 - 在执行过程中按权限调用 SK 工具。 - 回传日志、事件、指标和审计数据。 - 按 Resource Grant 和运行时身份访问资源。 -普通用户不直接操作 Agnet 平台后台,而是在 Heicode 中查看 Agnet 状态和结果。 +普通用户不直接操作 Agent 平台后台,而是在 Heicode 中查看 Agent 状态和结果。 ### 5.4 CodeGW @@ -312,7 +312,7 @@ Heicode 判断当前任务需要哪些上下文 - 调用日志。 - 失败日志。 -子 Agnet 的运行模型属于 Agnet 平台部署配置,不等同于 CodeGW 后台的模型供应商配置。 +子 Agent 的运行模型属于 Agent 平台部署配置,不等同于 CodeGW 后台的模型供应商配置。 ## 九、密钥保管说明 @@ -322,9 +322,9 @@ Heicode 判断当前任务需要哪些上下文 - Markdown。 - 前端响应。 - 普通日志。 -- 子 Agnet 长期状态。 +- 子 Agent 长期状态。 -长期密钥进入密钥保管器。Heicode 保存 `secret_ref`。子 Agnet 使用资源时,只获取短期、最小权限、可审计凭证。 +长期密钥进入密钥保管器。Heicode 保存 `secret_ref`。子 Agent 使用资源时,只获取短期、最小权限、可审计凭证。 ## 十、平台边界 @@ -337,7 +337,7 @@ Heicode 判断当前任务需要哪些上下文 - CodeGW 后台。 - Azure Key Vault 控制台。 -- Agnet 平台后台。 +- Agent 平台后台。 用户不应该看到: @@ -355,7 +355,7 @@ Heicode 判断当前任务需要哪些上下文 ### 2. 我必须绑定 Git 吗? -如果要让子 Agnet 修改代码,建议绑定 Git 仓库。只做产品文档或原型描述时,可以先不绑定代码仓库。 +如果要让子 Agent 修改代码,建议绑定 Git 仓库。只做产品文档或原型描述时,可以先不绑定代码仓库。 ### 3. 我必须绑定云资源吗? @@ -363,7 +363,7 @@ Heicode 判断当前任务需要哪些上下文 ### 4. 密钥安全吗? -平台设计上不把明文密钥保存到 Heicode 服务端数据库、Git、Markdown、日志或前端响应中。真实凭证进入密钥保管器,子 Agnet 只使用短期、最小权限凭证。 +平台设计上不把明文密钥保存到 Heicode 服务端数据库、Git、Markdown、日志或前端响应中。真实凭证进入密钥保管器,子 Agent 只使用短期、最小权限凭证。 ### 5. 客户端必须安装吗? @@ -373,9 +373,9 @@ Heicode 判断当前任务需要哪些上下文 CodeGW 是 Heicode 的模型网关和计费服务。普通用户不需要进入 CodeGW 后台,只在 Heicode 中查看模型、余额、额度和调用日志。 -### 7. Agnet 是什么? +### 7. Agent 是什么? -Agnet 是平台生成的 AI 开发角色。不同 Agnet 负责不同任务,例如产品、架构、前端、后端、审查和部署。 +Agent 是平台生成的 AI 开发角色。不同 Agent 负责不同任务,例如产品、架构、前端、后端、审查和部署。 ### 8. 生产部署怎么保证安全? @@ -391,4 +391,4 @@ Agnet 是平台生成的 AI 开发角色。不同 Agnet 负责不同任务,例 ## 十二、平台说明标准话术 -> Heicode 是一个从想法到上线的软件生命周期平台。用户在 Heicode 中输入产品想法,绑定代码、文档、SK 和云资源。Heicode 生成子 Agnet 团队和权限清单,客户端负责高危审批,Agnet 平台负责执行开发和部署,CodeGW 提供模型和用量,密钥保管器保存长期凭证。用户只需要使用 Heicode 和客户端,不需要直接进入 CodeGW 后台或密钥保管器后台。 +> Heicode 是一个从想法到上线的软件生命周期平台。用户在 Heicode 中输入产品想法,绑定代码、文档、SK 和云资源。Heicode 生成子 Agent 团队和权限清单,客户端负责高危审批,Agent 平台负责执行开发和部署,CodeGW 提供模型和用量,密钥保管器保存长期凭证。用户只需要使用 Heicode 和客户端,不需要直接进入 CodeGW 后台或密钥保管器后台。 diff --git a/docs/product-package/README.md b/docs/product-package/README.md index 5176245..22bf2f5 100644 --- a/docs/product-package/README.md +++ b/docs/product-package/README.md @@ -14,11 +14,11 @@ | [`04-platform-usage-guide.md`](./04-platform-usage-guide.md) | 登录平台和使用平台指南,面向真实用户上手 | | [`05-ppt-copy.md`](./05-ppt-copy.md) | 产品 PPT 文案,面向路演、客户介绍和团队同步 | | [`06-security-and-credential-management.md`](./06-security-and-credential-management.md) | 安全与凭证管理说明,解释密钥保管器、`secret_ref`、高危审批 | -| [`07-integration-boundaries.md`](./07-integration-boundaries.md) | CodeGW、Azure Key Vault、Agnet 平台边界说明 | +| [`07-integration-boundaries.md`](./07-integration-boundaries.md) | CodeGW、Azure Key Vault、Agent 平台边界说明 | | [`08-client-guide.md`](./08-client-guide.md) | Heicode 客户端说明,强调只登录 Heicode、只使用 Heicode 模型 | | [`09-demo-script.md`](./09-demo-script.md) | 5 分钟演示脚本,用于产品演示和录屏 | | [`10-frontend-detail-spec.md`](./10-frontend-detail-spec.md) | 前端细节说明,覆盖 Heicode 和客户端页面、组件、状态、文案规则 | -| [`11-product-prototype-wireframes.md`](./11-product-prototype-wireframes.md) | 产品原型图,说明 Heicode 客户端主体验、Manager 辅助控制台与 Agnet 执行闭环的边界 | +| [`11-product-prototype-wireframes.md`](./11-product-prototype-wireframes.md) | 产品原型图,说明 Heicode 客户端主体验、Manager 辅助控制台与 Agent 执行闭环的边界 | | [`12-narrated-user-operation-flow.md`](./12-narrated-user-operation-flow.md) | 口述用户操作流程,用于演示、培训、录屏旁白和销售讲解 | | [`13-platform-description.md`](./13-platform-description.md) | 平台说明文档,解释平台组成、使用方式、系统边界和常见问题 | @@ -34,15 +34,15 @@ Heicode 是一款面向全流程智能开发的 SaaS Code 工具。用户注册 - 用户侧叫 `Heicode`,不要叫 CodeGW 后台。 - `Heicode 客户端` 是用户主体验,用于对话、输入想法、推进开发任务、查看执行反馈、高危审批和接收交付结果。 -- `Heicode Manager` 是浏览器里的辅助控制台,负责账号与安全、客户端下载、Git/云资源绑定、Agnet 部署、任务状态、余额与审计。 -- 继续开发时,Heicode 会持续调用 Agnet 推进需求、开发、测试、修复和部署等子环节。 -- Agnet 在执行过程中可以调用已授权的 SK 工具,最终完成交付与部署并把结果回传给客户端和 Manager。 +- `Heicode Manager` 是浏览器里的辅助控制台,负责账号与安全、客户端下载、Git/云资源绑定、Agent 部署、任务状态、余额与审计。 +- 继续开发时,Heicode 会持续调用 Agent 推进需求、开发、测试、修复和部署等子环节。 +- Agent 在执行过程中可以调用已授权的 SK 工具,最终完成交付与部署并把结果回传给客户端和 Manager。 - 用户侧叫 `密钥保管器`,技术实现可以说明是 Azure Key Vault;Manager 通过 VM Managed Identity 访问,不要求普通用户进入 Key Vault 后台。 - 用户侧叫 `资源绑定`,不要叫 Git 来源。 - 客户端不出现 `模型提供方`、`旧服务入口`、`第三方路由入口`。 - CodeGW 是内部模型网关和计费服务,不作为普通用户产品入口。 -- Agnet 平台是执行层,不替代 Heicode 的用户控制台。 +- Agent 平台是执行层,不替代 Heicode 的用户控制台。 ## 当前产品边界 -当前资料包以 `docs/heicode.md`、`docs/plan.md`、`docs/product-requirements.md` 为依据。旧 Agnet 草案、旧 M1-M5 计划和旧 UI 命名不作为产品主线。 +当前资料包以 `docs/heicode.md`、`docs/plan.md`、`docs/product-requirements.md` 为依据。旧 Agent 草案、旧 M1-M5 计划和旧 UI 命名不作为产品主线。 diff --git a/env.handoff.download b/env.handoff.download new file mode 100644 index 0000000..930cc64 --- /dev/null +++ b/env.handoff.download @@ -0,0 +1,92 @@ +# Heicode Manager 本地交接敏感配置填写表 +# 更新时间:2026-06-01 +# +# 说明: +# 1. 这个文件只放在本地交接目录,不要提交 git。 +# 2. 我不能凭空写入无法读取/无法确认的明文密码;下面列出所有需要手动取值的项。 +# 3. 真实值建议从生产 VM .env、docker inspect、Azure Portal、NewAPI 后台或负责人安全渠道获取。 +# 4. 填完后只用于人工交接,不要发到公开聊天、截图、Markdown 或仓库。 + +### 1. VM SSH +HEICODE_VM_HOST=20.24.50.121 +HEICODE_VM_USER=heicode +HEICODE_VM_PASSWORD="" +HEICODE_VM_SSH_KEY_PATH="" + +### 2. Gitee 仓库账号 +HEICODE_GIT_REMOTE="http://gitee.ath.cx:3000/taijibaga/heicode-mananger.git" +GITEE_USERNAME="" +GITEE_PASSWORD_OR_TOKEN="" + +### 3. Manager 生产数据库和缓存 +# 获取位置: +# - VM: /home/heicode/apps/heicode-git/heicode/.env +# - 或 docker inspect heicode 的 Config.Env +# - 或 Azure PostgreSQL / Azure Redis 控制台 +SQL_DSN="" +REDIS_CONN_STRING="" + +### 4. Manager 基础安全密钥 +# 获取位置: +# - VM .env +# - docker inspect heicode +SESSION_SECRET="" +CRYPTO_SECRET="" +MCP_SERVER_INTERNAL_TOKEN="" + +### 5. Heicode / Agent 认证网关 +HEICODE_AUTH_BASE_URL="" +HEICODE_ROOT_EMAILS="xiaohei@qq.com" +HEICODE_ADMIN_EMAILS="" + +### 6. Azure Key Vault +AZURE_KEY_VAULT_URL="https://heicode-kv.vault.azure.net" +AZURE_CLIENT_ID="" +AZURE_PORTAL_ACCOUNT="" +AZURE_PORTAL_PASSWORD="" +AZURE_KEY_VAULT_CALLBACK_SECRET_NAME="agent-callback-signing-key" +AZURE_KEY_VAULT_CALLBACK_SECRET_VALUE="" + +### 7. 普通 sub Agent Manager Runtime +AGENT_RUNTIME_ENABLED=true +AGENT_RUNTIME_BASE_URL="http://20.212.121.126" +AGENT_RUNTIME_HEALTH_PATH="/api/agent/health" +AGENT_RUNTIME_CREATE_PATH="/api/swarms" +AGENT_RUNTIME_STOP_PATH="/api/swarms/{swarm_id}/stop" +AGENT_RUNTIME_APPROVAL_DECISION_PATH="/api/swarms/{swarm_id}/approvals/{approval_id}" +AGENT_RUNTIME_TIMEOUT_SECONDS=15 +AGENT_RUNTIME_SERVICE_TOKEN="" +AGENT_RUNTIME_CALLBACK_SIGNING_SECRET_REF="" +AGENT_CALLBACK_TOKEN="" +AGENT_CALLBACK_SIGNING_SECRET="" + +### 8. 蜂群 Runtime / HeiCode-Swarm +SWARM_RUNTIME_ENABLED=false +SWARM_RUNTIME_BASE_URL="http://52.139.240.116:8000" +SWARM_RUNTIME_CREATE_PATH="/api/swarms" +SWARM_RUNTIME_HEALTH_PATH="/api/agent/health" +SWARM_RUNTIME_STOP_PATH="/api/swarms/{swarm_id}/stop" +SWARM_RUNTIME_APPROVAL_DECISION_PATH="/api/swarms/{swarm_id}/approvals/{approval_id}" +SWARM_RUNTIME_SERVICE_TOKEN="" + +### 9. NewAPI +NEWAPI_CONTAINER_NAME="new-api" +NEWAPI_VM_URL="http://20.24.50.121:3001" +NEWAPI_COMPOSE_PATH="/home/heicode/apps/new-api/docker-compose.yml" +NEWAPI_ADMIN_USERNAME="" +NEWAPI_ADMIN_PASSWORD="" +NEWAPI_CHANNEL_KEYS="" + +### 10. Manager 测试/管理员用户 +MANAGER_PUBLIC_URL="https://code.xinghanlab.com" +MANAGER_ROOT_EMAIL="xiaohei@qq.com" +MANAGER_TEST_EMAIL="zsbgnw@gmail.com" +MANAGER_TEST_PASSWORD="" +MANAGER_ADMIN_EMAIL="" +MANAGER_ADMIN_PASSWORD="" + +### 11. 桌面客户端安装包 +HEICODE_DESKTOP_ARTIFACT_DIR="/data/desktop-artifacts" +HEICODE_DESKTOP_CLIENT_VERSION="0.1.0" +HEICODE_DESKTOP_FILE_MACOS_ARM64="/data/desktop-artifacts/HeiCode_0.1.0_macos_arm64.dmg" +HEICODE_DESKTOP_FILE_WINDOWS="/data/desktop-artifacts/Heicode_0.1.0_windows_x64_msi.msi" diff --git a/heicode/controller/agnet_approval.go b/heicode/controller/agent_approval.go similarity index 72% rename from heicode/controller/agnet_approval.go rename to heicode/controller/agent_approval.go index 22f3a6a..f1e8780 100644 --- a/heicode/controller/agnet_approval.go +++ b/heicode/controller/agent_approval.go @@ -13,20 +13,20 @@ import ( ) const ( - agnetApprovalStatusPending = "pending" - agnetApprovalStatusApproved = "approved" - agnetApprovalStatusRejected = "rejected" - agnetApprovalStatusExpired = "expired" + agentApprovalStatusPending = "pending" + agentApprovalStatusApproved = "approved" + agentApprovalStatusRejected = "rejected" + agentApprovalStatusExpired = "expired" - agnetLeaseStatusActive = "active" - agnetLeaseStatusExpired = "expired" - agnetLeaseStatusRevoked = "revoked" + agentLeaseStatusActive = "active" + agentLeaseStatusExpired = "expired" + agentLeaseStatusRevoked = "revoked" - defaultAgnetApprovalTTLSeconds = 15 * 60 - maxAgnetApprovalTTLSeconds = 60 * 60 + defaultAgentApprovalTTLSeconds = 15 * 60 + maxAgentApprovalTTLSeconds = 60 * 60 ) -type agnetApprovalPayload struct { +type agentApprovalPayload struct { DeploymentID string `json:"deployment_id"` BindingScope string `json:"binding_scope"` Operation string `json:"operation"` @@ -41,11 +41,11 @@ type agnetApprovalPayload struct { Reason string `json:"reason"` } -type agnetDecisionPayload struct { +type agentDecisionPayload struct { Reason string `json:"reason"` } -type agnetApprovalResponse struct { +type agentApprovalResponse struct { ApprovalID string `json:"approval_id"` UserId int `json:"user_id"` DeploymentID string `json:"deployment_id"` @@ -68,10 +68,10 @@ type agnetApprovalResponse struct { DecidedAt int64 `json:"decided_at,omitempty"` CreatedAt int64 `json:"created_at"` UpdatedAt int64 `json:"updated_at"` - CredentialLease *agnetCredentialLeaseResponse `json:"credential_lease,omitempty"` + CredentialLease *agentCredentialLeaseResponse `json:"credential_lease,omitempty"` } -type agnetCredentialLeaseResponse struct { +type agentCredentialLeaseResponse struct { LeaseID string `json:"lease_id"` CredentialRef string `json:"credential_ref"` ApprovalID string `json:"approval_id"` @@ -90,13 +90,13 @@ type agnetCredentialLeaseResponse struct { UpdatedAt int64 `json:"updated_at"` } -func CreateAgnetApprovalRequest(c *gin.Context) { - var payload agnetApprovalPayload +func CreateAgentApprovalRequest(c *gin.Context) { + var payload agentApprovalPayload if err := common.DecodeJson(c.Request.Body, &payload); err != nil { common.ApiError(c, err) return } - payload, err := normalizeAgnetApprovalPayload(payload) + payload, err := normalizeAgentApprovalPayload(payload) if err != nil { common.ApiError(c, err) return @@ -109,7 +109,7 @@ func CreateAgnetApprovalRequest(c *gin.Context) { } now := time.Now().UnixMilli() - approval := model.AgnetApprovalRequest{ + approval := model.AgentApprovalRequest{ ApprovalID: "appr_" + common.GetUUID(), UserId: userID, DeploymentID: payload.DeploymentID, @@ -122,8 +122,8 @@ func CreateAgnetApprovalRequest(c *gin.Context) { RiskLevel: payload.RiskLevel, RequiresCredential: payload.RequiresCredential, SecretRef: payload.SecretRef, - Status: agnetApprovalStatusPending, - RequestedBy: agnetActorForUser(userID), + Status: agentApprovalStatusPending, + RequestedBy: agentActorForUser(userID), RequestReason: payload.Reason, TTLSeconds: payload.TTLSeconds, ExpiresAt: now + int64(payload.TTLSeconds)*1000, @@ -132,11 +132,11 @@ func CreateAgnetApprovalRequest(c *gin.Context) { common.ApiError(c, err) return } - recordAgnetApprovalAudit("approval.requested", &approval, nil, "ok", "") - common.ApiSuccess(c, agnetApprovalToResponse(approval, nil)) + recordAgentApprovalAudit("approval.requested", &approval, nil, "ok", "") + common.ApiSuccess(c, agentApprovalToResponse(approval, nil)) } -func ListAgnetApprovalRequests(c *gin.Context) { +func ListAgentApprovalRequests(c *gin.Context) { userID := c.GetInt("id") if userID <= 0 { common.ApiErrorMsg(c, "user authentication required") @@ -151,58 +151,58 @@ func ListAgnetApprovalRequests(c *gin.Context) { q = q.Where("deployment_id = ?", deploymentID) } - var approvals []model.AgnetApprovalRequest + var approvals []model.AgentApprovalRequest if err := q.Order("created_at desc, id desc").Limit(200).Find(&approvals).Error; err != nil { common.ApiError(c, err) return } for i := range approvals { - expireAgnetApprovalIfNeeded(&approvals[i]) + expireAgentApprovalIfNeeded(&approvals[i]) } - items := make([]agnetApprovalResponse, 0, len(approvals)) + items := make([]agentApprovalResponse, 0, len(approvals)) for _, approval := range approvals { if statusFilter != "" && approval.Status != statusFilter { continue } - items = append(items, agnetApprovalToResponse(approval, nil)) + items = append(items, agentApprovalToResponse(approval, nil)) } common.ApiSuccess(c, gin.H{"items": items}) } -func GetAgnetApprovalRequest(c *gin.Context) { - approval, ok := findAgnetApprovalForUser(c) +func GetAgentApprovalRequest(c *gin.Context) { + approval, ok := findAgentApprovalForUser(c) if !ok { return } - expireAgnetApprovalIfNeeded(&approval) - lease := findAgnetCredentialLeaseByApproval(approval.ApprovalID) - common.ApiSuccess(c, agnetApprovalToResponse(approval, lease)) + expireAgentApprovalIfNeeded(&approval) + lease := findAgentCredentialLeaseByApproval(approval.ApprovalID) + common.ApiSuccess(c, agentApprovalToResponse(approval, lease)) } -func ApproveAgnetApprovalRequest(c *gin.Context) { - approval, ok := findAgnetApprovalForUser(c) +func ApproveAgentApprovalRequest(c *gin.Context) { + approval, ok := findAgentApprovalForUser(c) if !ok { return } - if expireAgnetApprovalIfNeeded(&approval) { + if expireAgentApprovalIfNeeded(&approval) { common.ApiErrorMsg(c, "approval request expired") return } - if approval.Status != agnetApprovalStatusPending { + if approval.Status != agentApprovalStatusPending { common.ApiErrorMsg(c, "approval request is not pending") return } - var payload agnetDecisionPayload + var payload agentDecisionPayload _ = common.DecodeJson(c.Request.Body, &payload) now := time.Now().UnixMilli() - approval.Status = agnetApprovalStatusApproved - approval.DecidedBy = agnetActorForUser(c.GetInt("id")) + approval.Status = agentApprovalStatusApproved + approval.DecidedBy = agentActorForUser(c.GetInt("id")) approval.DecisionReason = strings.TrimSpace(payload.Reason) approval.DecidedAt = now - var lease *model.AgnetCredentialLease + var lease *model.AgentCredentialLease err := model.DB.Transaction(func(tx *gorm.DB) error { if err := tx.Save(&approval).Error; err != nil { return err @@ -210,7 +210,7 @@ func ApproveAgnetApprovalRequest(c *gin.Context) { if !approval.RequiresCredential { return nil } - createdLease := model.AgnetCredentialLease{ + createdLease := model.AgentCredentialLease{ LeaseID: "lease_" + common.GetUUID(), ApprovalID: approval.ApprovalID, UserId: approval.UserId, @@ -221,11 +221,11 @@ func ApproveAgnetApprovalRequest(c *gin.Context) { ResourceScope: approval.ResourceScope, TargetRole: approval.TargetRole, SecretRef: approval.SecretRef, - Status: agnetLeaseStatusActive, + Status: agentLeaseStatusActive, TTLSeconds: approval.TTLSeconds, ExpiresAt: now + int64(approval.TTLSeconds)*1000, } - createdLease.CredentialRef = "lease://agnet/" + createdLease.LeaseID + createdLease.CredentialRef = "lease://agent/" + createdLease.LeaseID if err := tx.Create(&createdLease).Error; err != nil { return err } @@ -240,44 +240,44 @@ func ApproveAgnetApprovalRequest(c *gin.Context) { common.ApiError(c, err) return } - recordAgnetApprovalAudit("approval.approved", &approval, nil, "ok", "") + recordAgentApprovalAudit("approval.approved", &approval, nil, "ok", "") if lease != nil { - recordAgnetApprovalAudit("credential_lease.created", &approval, lease, "ok", "") + recordAgentApprovalAudit("credential_lease.created", &approval, lease, "ok", "") } - syncAgnetRuntimeApprovalDecision(c, &approval, lease, agnetApprovalStatusApproved) - common.ApiSuccess(c, agnetApprovalToResponse(approval, lease)) + syncAgentRuntimeApprovalDecision(c, &approval, lease, agentApprovalStatusApproved) + common.ApiSuccess(c, agentApprovalToResponse(approval, lease)) } -func RejectAgnetApprovalRequest(c *gin.Context) { - approval, ok := findAgnetApprovalForUser(c) +func RejectAgentApprovalRequest(c *gin.Context) { + approval, ok := findAgentApprovalForUser(c) if !ok { return } - if expireAgnetApprovalIfNeeded(&approval) { + if expireAgentApprovalIfNeeded(&approval) { common.ApiErrorMsg(c, "approval request expired") return } - if approval.Status != agnetApprovalStatusPending { + if approval.Status != agentApprovalStatusPending { common.ApiErrorMsg(c, "approval request is not pending") return } - var payload agnetDecisionPayload + var payload agentDecisionPayload _ = common.DecodeJson(c.Request.Body, &payload) - approval.Status = agnetApprovalStatusRejected - approval.DecidedBy = agnetActorForUser(c.GetInt("id")) + approval.Status = agentApprovalStatusRejected + approval.DecidedBy = agentActorForUser(c.GetInt("id")) approval.DecisionReason = strings.TrimSpace(payload.Reason) approval.DecidedAt = time.Now().UnixMilli() if err := model.DB.Save(&approval).Error; err != nil { common.ApiError(c, err) return } - recordAgnetApprovalAudit("approval.rejected", &approval, nil, "ok", "") - syncAgnetRuntimeApprovalDecision(c, &approval, nil, agnetApprovalStatusRejected) - common.ApiSuccess(c, agnetApprovalToResponse(approval, nil)) + recordAgentApprovalAudit("approval.rejected", &approval, nil, "ok", "") + syncAgentRuntimeApprovalDecision(c, &approval, nil, agentApprovalStatusRejected) + common.ApiSuccess(c, agentApprovalToResponse(approval, nil)) } -func ListAgnetCredentialLeases(c *gin.Context) { +func ListAgentCredentialLeases(c *gin.Context) { userID := c.GetInt("id") if userID <= 0 { common.ApiErrorMsg(c, "user authentication required") @@ -292,23 +292,23 @@ func ListAgnetCredentialLeases(c *gin.Context) { q = q.Where("deployment_id = ?", deploymentID) } - var leases []model.AgnetCredentialLease + var leases []model.AgentCredentialLease if err := q.Order("created_at desc, id desc").Limit(200).Find(&leases).Error; err != nil { common.ApiError(c, err) return } - items := make([]agnetCredentialLeaseResponse, 0, len(leases)) + items := make([]agentCredentialLeaseResponse, 0, len(leases)) for i := range leases { - expireAgnetCredentialLeaseIfNeeded(&leases[i]) + expireAgentCredentialLeaseIfNeeded(&leases[i]) if statusFilter != "" && leases[i].Status != statusFilter { continue } - items = append(items, agnetLeaseToResponse(leases[i])) + items = append(items, agentLeaseToResponse(leases[i])) } common.ApiSuccess(c, gin.H{"items": items}) } -func RevokeAgnetCredentialLease(c *gin.Context) { +func RevokeAgentCredentialLease(c *gin.Context) { userID := c.GetInt("id") if userID <= 0 { common.ApiErrorMsg(c, "user authentication required") @@ -319,7 +319,7 @@ func RevokeAgnetCredentialLease(c *gin.Context) { common.ApiErrorMsg(c, "lease_id required") return } - var lease model.AgnetCredentialLease + var lease model.AgentCredentialLease if err := model.DB.Where("lease_id = ? AND user_id = ?", leaseID, userID).First(&lease).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { common.ApiErrorMsg(c, "credential lease not found") @@ -328,28 +328,28 @@ func RevokeAgnetCredentialLease(c *gin.Context) { common.ApiError(c, err) return } - if expireAgnetCredentialLeaseIfNeeded(&lease) { - common.ApiSuccess(c, agnetLeaseToResponse(lease)) + if expireAgentCredentialLeaseIfNeeded(&lease) { + common.ApiSuccess(c, agentLeaseToResponse(lease)) return } - if lease.Status != agnetLeaseStatusActive { + if lease.Status != agentLeaseStatusActive { common.ApiErrorMsg(c, "credential lease is not active") return } - lease.Status = agnetLeaseStatusRevoked + lease.Status = agentLeaseStatusRevoked lease.RevokedAt = time.Now().UnixMilli() if err := model.DB.Save(&lease).Error; err != nil { common.ApiError(c, err) return } - var approval model.AgnetApprovalRequest + var approval model.AgentApprovalRequest if err := model.DB.Where("approval_id = ?", lease.ApprovalID).First(&approval).Error; err == nil { - recordAgnetApprovalAudit("credential_lease.revoked", &approval, &lease, "ok", "") + recordAgentApprovalAudit("credential_lease.revoked", &approval, &lease, "ok", "") } - common.ApiSuccess(c, agnetLeaseToResponse(lease)) + common.ApiSuccess(c, agentLeaseToResponse(lease)) } -func normalizeAgnetApprovalPayload(p agnetApprovalPayload) (agnetApprovalPayload, error) { +func normalizeAgentApprovalPayload(p agentApprovalPayload) (agentApprovalPayload, error) { p.DeploymentID = strings.TrimSpace(p.DeploymentID) p.BindingScope = strings.TrimSpace(p.BindingScope) p.Operation = strings.TrimSpace(p.Operation) @@ -380,10 +380,10 @@ func normalizeAgnetApprovalPayload(p agnetApprovalPayload) (agnetApprovalPayload return p, errors.New("risk_level must be low, medium, high, or critical") } if p.TTLSeconds <= 0 { - p.TTLSeconds = defaultAgnetApprovalTTLSeconds + p.TTLSeconds = defaultAgentApprovalTTLSeconds } - if p.TTLSeconds > maxAgnetApprovalTTLSeconds { - p.TTLSeconds = maxAgnetApprovalTTLSeconds + if p.TTLSeconds > maxAgentApprovalTTLSeconds { + p.TTLSeconds = maxAgentApprovalTTLSeconds } if p.RequiresCredential { if p.SecretRef == "" { @@ -396,73 +396,73 @@ func normalizeAgnetApprovalPayload(p agnetApprovalPayload) (agnetApprovalPayload return p, nil } -func findAgnetApprovalForUser(c *gin.Context) (model.AgnetApprovalRequest, bool) { +func findAgentApprovalForUser(c *gin.Context) (model.AgentApprovalRequest, bool) { userID := c.GetInt("id") if userID <= 0 { common.ApiErrorMsg(c, "user authentication required") - return model.AgnetApprovalRequest{}, false + return model.AgentApprovalRequest{}, false } approvalID := strings.TrimSpace(c.Param("approval_id")) if approvalID == "" { common.ApiErrorMsg(c, "approval_id required") - return model.AgnetApprovalRequest{}, false + return model.AgentApprovalRequest{}, false } - var approval model.AgnetApprovalRequest + var approval model.AgentApprovalRequest if err := model.DB.Where("approval_id = ? AND user_id = ?", approvalID, userID).First(&approval).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { common.ApiErrorMsg(c, "approval request not found") - return model.AgnetApprovalRequest{}, false + return model.AgentApprovalRequest{}, false } common.ApiError(c, err) - return model.AgnetApprovalRequest{}, false + return model.AgentApprovalRequest{}, false } return approval, true } -func findAgnetCredentialLeaseByApproval(approvalID string) *model.AgnetCredentialLease { - var lease model.AgnetCredentialLease +func findAgentCredentialLeaseByApproval(approvalID string) *model.AgentCredentialLease { + var lease model.AgentCredentialLease if err := model.DB.Where("approval_id = ?", approvalID).First(&lease).Error; err != nil { return nil } - expireAgnetCredentialLeaseIfNeeded(&lease) + expireAgentCredentialLeaseIfNeeded(&lease) return &lease } -func expireAgnetApprovalIfNeeded(approval *model.AgnetApprovalRequest) bool { - if approval == nil || approval.Status != agnetApprovalStatusPending { +func expireAgentApprovalIfNeeded(approval *model.AgentApprovalRequest) bool { + if approval == nil || approval.Status != agentApprovalStatusPending { return false } if approval.ExpiresAt <= 0 || approval.ExpiresAt > time.Now().UnixMilli() { return false } - approval.Status = agnetApprovalStatusExpired + approval.Status = agentApprovalStatusExpired approval.DecidedAt = time.Now().UnixMilli() if err := model.DB.Save(approval).Error; err == nil { - recordAgnetApprovalAudit("approval.expired", approval, nil, "ok", "") + recordAgentApprovalAudit("approval.expired", approval, nil, "ok", "") } return true } -func expireAgnetCredentialLeaseIfNeeded(lease *model.AgnetCredentialLease) bool { - if lease == nil || lease.Status != agnetLeaseStatusActive { +func expireAgentCredentialLeaseIfNeeded(lease *model.AgentCredentialLease) bool { + if lease == nil || lease.Status != agentLeaseStatusActive { return false } if lease.ExpiresAt <= 0 || lease.ExpiresAt > time.Now().UnixMilli() { return false } - lease.Status = agnetLeaseStatusExpired + lease.Status = agentLeaseStatusExpired if err := model.DB.Save(lease).Error; err != nil { return false } - var approval model.AgnetApprovalRequest + var approval model.AgentApprovalRequest if err := model.DB.Where("approval_id = ?", lease.ApprovalID).First(&approval).Error; err == nil { - recordAgnetApprovalAudit("credential_lease.expired", &approval, lease, "ok", "") + recordAgentApprovalAudit("credential_lease.expired", &approval, lease, "ok", "") } return true } -func agnetApprovalToResponse(approval model.AgnetApprovalRequest, lease *model.AgnetCredentialLease) agnetApprovalResponse { - resp := agnetApprovalResponse{ +func agentApprovalToResponse(approval model.AgentApprovalRequest, lease *model.AgentCredentialLease) agentApprovalResponse { + resp := agentApprovalResponse{ ApprovalID: approval.ApprovalID, UserId: approval.UserId, DeploymentID: approval.DeploymentID, @@ -487,14 +487,14 @@ func agnetApprovalToResponse(approval model.AgnetApprovalRequest, lease *model.A UpdatedAt: approval.UpdatedAt, } if lease != nil { - leaseResp := agnetLeaseToResponse(*lease) + leaseResp := agentLeaseToResponse(*lease) resp.CredentialLease = &leaseResp } return resp } -func agnetLeaseToResponse(lease model.AgnetCredentialLease) agnetCredentialLeaseResponse { - return agnetCredentialLeaseResponse{ +func agentLeaseToResponse(lease model.AgentCredentialLease) agentCredentialLeaseResponse { + return agentCredentialLeaseResponse{ LeaseID: lease.LeaseID, CredentialRef: lease.CredentialRef, ApprovalID: lease.ApprovalID, @@ -514,7 +514,7 @@ func agnetLeaseToResponse(lease model.AgnetCredentialLease) agnetCredentialLease } } -func recordAgnetApprovalAudit(event string, approval *model.AgnetApprovalRequest, lease *model.AgnetCredentialLease, result string, message string) { +func recordAgentApprovalAudit(event string, approval *model.AgentApprovalRequest, lease *model.AgentCredentialLease, result string, message string) { if approval == nil { return } @@ -540,7 +540,7 @@ func recordAgnetApprovalAudit(event string, approval *model.AgnetApprovalRequest if raw, err := common.Marshal(details); err == nil { detailsJSON = string(raw) } - model.InsertAgnetAuditEvent(&model.AgnetAuditEvent{ + model.InsertAgentAuditEvent(&model.AgentAuditEvent{ EventID: "evt_" + common.GetUUID(), Event: event, Actor: "manager", @@ -555,6 +555,6 @@ func recordAgnetApprovalAudit(event string, approval *model.AgnetApprovalRequest }) } -func agnetActorForUser(userID int) string { +func agentActorForUser(userID int) string { return fmt.Sprintf("user:%d", userID) } diff --git a/heicode/controller/agnet_approval_test.go b/heicode/controller/agent_approval_test.go similarity index 77% rename from heicode/controller/agnet_approval_test.go rename to heicode/controller/agent_approval_test.go index 03089db..0fb044c 100644 --- a/heicode/controller/agnet_approval_test.go +++ b/heicode/controller/agent_approval_test.go @@ -17,7 +17,7 @@ import ( "gorm.io/gorm" ) -func setupAgnetApprovalTestDB(t *testing.T) *gorm.DB { +func setupAgentApprovalTestDB(t *testing.T) *gorm.DB { t.Helper() gin.SetMode(gin.TestMode) common.UsingSQLite = true @@ -31,10 +31,10 @@ func setupAgnetApprovalTestDB(t *testing.T) *gorm.DB { model.DB = db model.LOG_DB = db require.NoError(t, db.AutoMigrate( - &model.AgnetApprovalRequest{}, - &model.AgnetCredentialLease{}, - &model.AgnetAuditEvent{}, - &model.AgnetDeployment{}, + &model.AgentApprovalRequest{}, + &model.AgentCredentialLease{}, + &model.AgentAuditEvent{}, + &model.AgentDeployment{}, )) t.Cleanup(func() { sqlDB, err := db.DB() @@ -45,8 +45,8 @@ func setupAgnetApprovalTestDB(t *testing.T) *gorm.DB { return db } -func TestAgnetApprovalApproveNotifiesRuntimeDecision(t *testing.T) { - db := setupAgnetApprovalTestDB(t) +func TestAgentApprovalApproveNotifiesRuntimeDecision(t *testing.T) { + db := setupAgentApprovalTestDB(t) var runtimeBody string var runtimeAuth string var runtimePath string @@ -59,12 +59,12 @@ func TestAgnetApprovalApproveNotifiesRuntimeDecision(t *testing.T) { _, _ = w.Write([]byte(`{"success":true,"data":{"status":"accepted"}}`)) })) defer runtimeServer.Close() - t.Setenv("AGNET_RUNTIME_ENABLED", "true") - t.Setenv("AGNET_RUNTIME_BASE_URL", runtimeServer.URL) - t.Setenv("AGNET_RUNTIME_SERVICE_TOKEN", "runtime-token") - t.Setenv("AGNET_RUNTIME_APPROVAL_DECISION_PATH", "/api/swarms/{swarm_id}/approvals/{approval_id}") + t.Setenv("AGENT_RUNTIME_ENABLED", "true") + t.Setenv("AGENT_RUNTIME_BASE_URL", runtimeServer.URL) + t.Setenv("AGENT_RUNTIME_SERVICE_TOKEN", "runtime-token") + t.Setenv("AGENT_RUNTIME_APPROVAL_DECISION_PATH", "/api/swarms/{swarm_id}/approvals/{approval_id}") - require.NoError(t, db.Create(&model.AgnetDeployment{ + require.NoError(t, db.Create(&model.AgentDeployment{ DeploymentID: "dep_runtime_approval", UserID: "7", BindingScope: "project-alpha", @@ -90,8 +90,8 @@ func TestAgnetApprovalApproveNotifiesRuntimeDecision(t *testing.T) { "ttl_seconds":600 }` - _, approvalID := createAgnetApprovalForTest(t, 7, body) - w := performAgnetApprovalRequest(ApproveAgnetApprovalRequest, 7, http.MethodPost, "/approvals/:approval_id/approve", "/approvals/"+approvalID+"/approve", `{"reason":"允许写入"}`) + _, approvalID := createAgentApprovalForTest(t, 7, body) + w := performAgentApprovalRequest(ApproveAgentApprovalRequest, 7, http.MethodPost, "/approvals/:approval_id/approve", "/approvals/"+approvalID+"/approve", `{"reason":"允许写入"}`) require.Equal(t, http.StatusOK, w.Code) require.Equal(t, "/api/swarms/swarm-runtime-1/approvals/"+approvalID, runtimePath) require.Equal(t, "Bearer runtime-token", runtimeAuth) @@ -100,16 +100,16 @@ func TestAgnetApprovalApproveNotifiesRuntimeDecision(t *testing.T) { require.Contains(t, runtimeBody, `"manager_deployment_id":"dep_runtime_approval"`) require.Contains(t, runtimeBody, `"runtime_deployment_id":"runtime-dep-1"`) require.Contains(t, runtimeBody, `"swarm_id":"swarm-runtime-1"`) - require.Contains(t, runtimeBody, `"credential_ref":"lease://agnet/`) + require.Contains(t, runtimeBody, `"credential_ref":"lease://agent/`) require.NotContains(t, runtimeBody, "azkv://") - var auditRows []model.AgnetAuditEvent + var auditRows []model.AgentAuditEvent require.NoError(t, db.Where("deployment_id = ?", "dep_runtime_approval").Order("id asc").Find(&auditRows).Error) require.NotEmpty(t, auditRows) require.Equal(t, "runtime.approval_decision.accepted", auditRows[len(auditRows)-1].Event) } -func performAgnetApprovalRequest(handler gin.HandlerFunc, userID int, method string, routePath string, requestPath string, body string) *httptest.ResponseRecorder { +func performAgentApprovalRequest(handler gin.HandlerFunc, userID int, method string, routePath string, requestPath string, body string) *httptest.ResponseRecorder { r := gin.New() r.Handle(method, routePath, func(c *gin.Context) { c.Set("id", userID) @@ -122,18 +122,18 @@ func performAgnetApprovalRequest(handler gin.HandlerFunc, userID int, method str return w } -func decodeAgnetApprovalEnvelope(t *testing.T, w *httptest.ResponseRecorder) map[string]any { +func decodeAgentApprovalEnvelope(t *testing.T, w *httptest.ResponseRecorder) map[string]any { t.Helper() var envelope map[string]any require.NoError(t, common.Unmarshal(w.Body.Bytes(), &envelope)) return envelope } -func createAgnetApprovalForTest(t *testing.T, userID int, body string) (map[string]any, string) { +func createAgentApprovalForTest(t *testing.T, userID int, body string) (map[string]any, string) { t.Helper() - w := performAgnetApprovalRequest(CreateAgnetApprovalRequest, userID, http.MethodPost, "/approvals", "/approvals", body) + w := performAgentApprovalRequest(CreateAgentApprovalRequest, userID, http.MethodPost, "/approvals", "/approvals", body) require.Equal(t, http.StatusOK, w.Code) - envelope := decodeAgnetApprovalEnvelope(t, w) + envelope := decodeAgentApprovalEnvelope(t, w) require.Equal(t, true, envelope["success"]) data := envelope["data"].(map[string]any) approvalID := data["approval_id"].(string) @@ -141,8 +141,8 @@ func createAgnetApprovalForTest(t *testing.T, userID int, body string) (map[stri return data, approvalID } -func TestAgnetApprovalApproveCreatesShortLivedLeaseAndAudit(t *testing.T) { - db := setupAgnetApprovalTestDB(t) +func TestAgentApprovalApproveCreatesShortLivedLeaseAndAudit(t *testing.T) { + db := setupAgentApprovalTestDB(t) body := `{ "deployment_id":"dep_approval_1", "binding_scope":"project-alpha", @@ -158,27 +158,27 @@ func TestAgnetApprovalApproveCreatesShortLivedLeaseAndAudit(t *testing.T) { "reason":"需要写入功能分支" }` - _, approvalID := createAgnetApprovalForTest(t, 7, body) - w := performAgnetApprovalRequest(ApproveAgnetApprovalRequest, 7, http.MethodPost, "/approvals/:approval_id/approve", "/approvals/"+approvalID+"/approve", `{"reason":"允许本次任务"}`) + _, approvalID := createAgentApprovalForTest(t, 7, body) + w := performAgentApprovalRequest(ApproveAgentApprovalRequest, 7, http.MethodPost, "/approvals/:approval_id/approve", "/approvals/"+approvalID+"/approve", `{"reason":"允许本次任务"}`) require.Equal(t, http.StatusOK, w.Code) - envelope := decodeAgnetApprovalEnvelope(t, w) + envelope := decodeAgentApprovalEnvelope(t, w) require.Equal(t, true, envelope["success"]) data := envelope["data"].(map[string]any) require.Equal(t, "approved", data["status"]) lease := data["credential_lease"].(map[string]any) require.Equal(t, "active", lease["status"]) - require.Contains(t, lease["credential_ref"], "lease://agnet/") + require.Contains(t, lease["credential_ref"], "lease://agent/") require.NotContains(t, w.Body.String(), "do-not-store") require.NotContains(t, w.Body.String(), "azkv://", "API responses must not expose internal Key Vault secret_ref") - var storedLease model.AgnetCredentialLease + var storedLease model.AgentCredentialLease require.NoError(t, db.Where("approval_id = ?", approvalID).First(&storedLease).Error) require.Equal(t, "azkv://heicode-kv.vault.azure.net/secrets/repo-main", storedLease.SecretRef) require.Equal(t, "active", storedLease.Status) require.Greater(t, storedLease.ExpiresAt, storedLease.CreatedAt) require.NotContains(t, storedLease.CredentialRef, "azkv://") - var auditRows []model.AgnetAuditEvent + var auditRows []model.AgentAuditEvent require.NoError(t, db.Order("id asc").Find(&auditRows).Error) require.Len(t, auditRows, 3) require.Equal(t, "approval.requested", auditRows[0].Event) @@ -187,8 +187,8 @@ func TestAgnetApprovalApproveCreatesShortLivedLeaseAndAudit(t *testing.T) { require.NotContains(t, auditRows[2].DetailsJSON, "repo-main-secret-value") } -func TestAgnetApprovalRejectDoesNotCreateLease(t *testing.T) { - db := setupAgnetApprovalTestDB(t) +func TestAgentApprovalRejectDoesNotCreateLease(t *testing.T) { + db := setupAgentApprovalTestDB(t) body := `{ "deployment_id":"dep_approval_2", "binding_scope":"project-alpha", @@ -203,21 +203,21 @@ func TestAgnetApprovalRejectDoesNotCreateLease(t *testing.T) { "ttl_seconds":300 }` - _, approvalID := createAgnetApprovalForTest(t, 8, body) - w := performAgnetApprovalRequest(RejectAgnetApprovalRequest, 8, http.MethodPost, "/approvals/:approval_id/reject", "/approvals/"+approvalID+"/reject", `{"reason":"风险过高"}`) + _, approvalID := createAgentApprovalForTest(t, 8, body) + w := performAgentApprovalRequest(RejectAgentApprovalRequest, 8, http.MethodPost, "/approvals/:approval_id/reject", "/approvals/"+approvalID+"/reject", `{"reason":"风险过高"}`) require.Equal(t, http.StatusOK, w.Code) - envelope := decodeAgnetApprovalEnvelope(t, w) + envelope := decodeAgentApprovalEnvelope(t, w) require.Equal(t, true, envelope["success"]) data := envelope["data"].(map[string]any) require.Equal(t, "rejected", data["status"]) var leases int64 - require.NoError(t, db.Model(&model.AgnetCredentialLease{}).Count(&leases).Error) + require.NoError(t, db.Model(&model.AgentCredentialLease{}).Count(&leases).Error) require.Equal(t, int64(0), leases) } -func TestAgnetApprovalExpiredRequestCannotBeApproved(t *testing.T) { - db := setupAgnetApprovalTestDB(t) +func TestAgentApprovalExpiredRequestCannotBeApproved(t *testing.T) { + db := setupAgentApprovalTestDB(t) body := `{ "deployment_id":"dep_approval_3", "binding_scope":"project-alpha", @@ -232,24 +232,24 @@ func TestAgnetApprovalExpiredRequestCannotBeApproved(t *testing.T) { "ttl_seconds":1 }` - _, approvalID := createAgnetApprovalForTest(t, 9, body) - require.NoError(t, db.Model(&model.AgnetApprovalRequest{}).Where("approval_id = ?", approvalID).Updates(map[string]any{ + _, approvalID := createAgentApprovalForTest(t, 9, body) + require.NoError(t, db.Model(&model.AgentApprovalRequest{}).Where("approval_id = ?", approvalID).Updates(map[string]any{ "expires_at": 1, }).Error) - w := performAgnetApprovalRequest(ApproveAgnetApprovalRequest, 9, http.MethodPost, "/approvals/:approval_id/approve", "/approvals/"+approvalID+"/approve", `{}`) + w := performAgentApprovalRequest(ApproveAgentApprovalRequest, 9, http.MethodPost, "/approvals/:approval_id/approve", "/approvals/"+approvalID+"/approve", `{}`) require.Equal(t, http.StatusOK, w.Code) - envelope := decodeAgnetApprovalEnvelope(t, w) + envelope := decodeAgentApprovalEnvelope(t, w) require.Equal(t, false, envelope["success"]) require.Contains(t, envelope["message"], "approval request expired") - var approval model.AgnetApprovalRequest + var approval model.AgentApprovalRequest require.NoError(t, db.Where("approval_id = ?", approvalID).First(&approval).Error) require.Equal(t, "expired", approval.Status) } -func TestAgnetCredentialLeaseCanBeRevoked(t *testing.T) { - db := setupAgnetApprovalTestDB(t) +func TestAgentCredentialLeaseCanBeRevoked(t *testing.T) { + db := setupAgentApprovalTestDB(t) body := `{ "deployment_id":"dep_approval_4", "binding_scope":"project-alpha", @@ -264,16 +264,16 @@ func TestAgnetCredentialLeaseCanBeRevoked(t *testing.T) { "ttl_seconds":600 }` - _, approvalID := createAgnetApprovalForTest(t, 10, body) - w := performAgnetApprovalRequest(ApproveAgnetApprovalRequest, 10, http.MethodPost, "/approvals/:approval_id/approve", "/approvals/"+approvalID+"/approve", `{}`) + _, approvalID := createAgentApprovalForTest(t, 10, body) + w := performAgentApprovalRequest(ApproveAgentApprovalRequest, 10, http.MethodPost, "/approvals/:approval_id/approve", "/approvals/"+approvalID+"/approve", `{}`) require.Equal(t, http.StatusOK, w.Code) - var storedLease model.AgnetCredentialLease + var storedLease model.AgentCredentialLease require.NoError(t, db.Where("approval_id = ?", approvalID).First(&storedLease).Error) revokePath := "/credential-leases/" + storedLease.LeaseID + "/revoke" - w = performAgnetApprovalRequest(RevokeAgnetCredentialLease, 10, http.MethodPost, "/credential-leases/:lease_id/revoke", revokePath, `{"reason":"任务结束"}`) + w = performAgentApprovalRequest(RevokeAgentCredentialLease, 10, http.MethodPost, "/credential-leases/:lease_id/revoke", revokePath, `{"reason":"任务结束"}`) require.Equal(t, http.StatusOK, w.Code) - envelope := decodeAgnetApprovalEnvelope(t, w) + envelope := decodeAgentApprovalEnvelope(t, w) require.Equal(t, true, envelope["success"]) data := envelope["data"].(map[string]any) require.Equal(t, "revoked", data["status"]) diff --git a/heicode/controller/agnet_callback.go b/heicode/controller/agent_callback.go similarity index 74% rename from heicode/controller/agnet_callback.go rename to heicode/controller/agent_callback.go index 222f830..2787972 100644 --- a/heicode/controller/agnet_callback.go +++ b/heicode/controller/agent_callback.go @@ -19,7 +19,7 @@ import ( "github.com/heicode/manager/model" ) -type agnetCallbackEnvelope struct { +type agentCallbackEnvelope struct { EventID string `json:"event_id"` IdempotencyKey string `json:"idempotency_key"` EventType string `json:"event_type"` @@ -32,10 +32,10 @@ type agnetCallbackEnvelope struct { Source string `json:"source"` Metadata map[string]any `json:"metadata"` Payload map[string]any `json:"payload"` - Artifact agnetArtifactPayload `json:"artifact"` + Artifact agentArtifactPayload `json:"artifact"` } -type agnetArtifactPayload struct { +type agentArtifactPayload struct { ArtifactID string `json:"artifact_id"` ArtifactType string `json:"artifact_type"` Title string `json:"title"` @@ -45,8 +45,8 @@ type agnetArtifactPayload struct { Metadata map[string]any `json:"metadata"` } -func agnetCallbackTokenFromRequest(c *gin.Context) string { - if token := strings.TrimSpace(c.GetHeader("X-Agnet-Service-Token")); token != "" { +func agentCallbackTokenFromRequest(c *gin.Context) string { + if token := strings.TrimSpace(c.GetHeader("X-Agent-Service-Token")); token != "" { return token } auth := strings.TrimSpace(c.GetHeader("Authorization")) @@ -56,25 +56,25 @@ func agnetCallbackTokenFromRequest(c *gin.Context) string { return "" } -func agnetCallbackSigningSecret() string { - if secret := strings.TrimSpace(common.GetEnvOrDefaultString("AGNET_CALLBACK_SIGNING_SECRET", "")); secret != "" { +func agentCallbackSigningSecret() string { + if secret := strings.TrimSpace(common.GetEnvOrDefaultString("AGENT_CALLBACK_SIGNING_SECRET", "")); secret != "" { return secret } secretRef := firstNonEmpty( - common.GetEnvOrDefaultString("AGNET_CALLBACK_SIGNING_SECRET_REF", ""), - common.GetEnvOrDefaultString("AGNET_RUNTIME_CALLBACK_SIGNING_SECRET_REF", ""), + common.GetEnvOrDefaultString("AGENT_CALLBACK_SIGNING_SECRET_REF", ""), + common.GetEnvOrDefaultString("AGENT_RUNTIME_CALLBACK_SIGNING_SECRET_REF", ""), ) if secretRef == "" { return "" } client, err := newSecretStoreClientFromEnv() if err != nil { - common.SysLog("agnetCallbackSigningSecret: " + err.Error()) + common.SysLog("agentCallbackSigningSecret: " + err.Error()) return "" } data, err := client.getJSONSecret(secretRef) if err != nil { - common.SysLog("agnetCallbackSigningSecret: " + err.Error()) + common.SysLog("agentCallbackSigningSecret: " + err.Error()) return "" } for _, key := range []string{"callback_signing_secret", "signing_secret", "secret", "value"} { @@ -82,38 +82,38 @@ func agnetCallbackSigningSecret() string { return value } } - common.SysLog("agnetCallbackSigningSecret: signing secret is missing from Azure Key Vault payload") + common.SysLog("agentCallbackSigningSecret: signing secret is missing from Azure Key Vault payload") return "" } -func agnetCallbackSignatureTolerance() time.Duration { - seconds := common.GetEnvOrDefault("AGNET_CALLBACK_SIGNATURE_TOLERANCE_SECONDS", 300) +func agentCallbackSignatureTolerance() time.Duration { + seconds := common.GetEnvOrDefault("AGENT_CALLBACK_SIGNATURE_TOLERANCE_SECONDS", 300) if seconds <= 0 { seconds = 300 } return time.Duration(seconds) * time.Second } -func validateAgnetCallbackHMAC(c *gin.Context, rawBody []byte, eventID string) (bool, bool) { - secret := agnetCallbackSigningSecret() +func validateAgentCallbackHMAC(c *gin.Context, rawBody []byte, eventID string) (bool, bool) { + secret := agentCallbackSigningSecret() if secret == "" { return false, false } - timestamp := strings.TrimSpace(c.GetHeader("X-Agnet-Timestamp")) - signature := strings.TrimSpace(c.GetHeader("X-Agnet-Signature")) + timestamp := strings.TrimSpace(c.GetHeader("X-Agent-Timestamp")) + signature := strings.TrimSpace(c.GetHeader("X-Agent-Signature")) if timestamp == "" || signature == "" || eventID == "" { return false, false } tsMs, err := strconv.ParseInt(timestamp, 10, 64) if err != nil { - agnetError(c, "CALLBACK_UNAUTHORIZED", "invalid callback timestamp") + agentError(c, "CALLBACK_UNAUTHORIZED", "invalid callback timestamp") return true, false } now := time.Now() eventTime := time.UnixMilli(tsMs) - tolerance := agnetCallbackSignatureTolerance() + tolerance := agentCallbackSignatureTolerance() if eventTime.Before(now.Add(-tolerance)) || eventTime.After(now.Add(tolerance)) { - agnetError(c, "CALLBACK_UNAUTHORIZED", "callback timestamp outside allowed window") + agentError(c, "CALLBACK_UNAUTHORIZED", "callback timestamp outside allowed window") return true, false } payload := timestamp + "." + eventID + "." + string(rawBody) @@ -121,32 +121,32 @@ func validateAgnetCallbackHMAC(c *gin.Context, rawBody []byte, eventID string) ( mac.Write([]byte(payload)) expected := "sha256=" + hex.EncodeToString(mac.Sum(nil)) if !hmac.Equal([]byte(expected), []byte(signature)) { - agnetError(c, "CALLBACK_UNAUTHORIZED", "invalid callback signature") + agentError(c, "CALLBACK_UNAUTHORIZED", "invalid callback signature") return true, false } return true, true } -func validateAgnetCallbackAuth(c *gin.Context, rawBody []byte, eventID string) bool { - if common.GetEnvOrDefaultBool("AGNET_CALLBACK_AUTH_DISABLED", false) { +func validateAgentCallbackAuth(c *gin.Context, rawBody []byte, eventID string) bool { + if common.GetEnvOrDefaultBool("AGENT_CALLBACK_AUTH_DISABLED", false) { return true } - if attempted, ok := validateAgnetCallbackHMAC(c, rawBody, eventID); attempted { + if attempted, ok := validateAgentCallbackHMAC(c, rawBody, eventID); attempted { return ok } - expected := strings.TrimSpace(os.Getenv("AGNET_CALLBACK_TOKEN")) + expected := strings.TrimSpace(os.Getenv("AGENT_CALLBACK_TOKEN")) if expected == "" { - agnetError(c, "CALLBACK_UNAUTHORIZED", "callback token is not configured") + agentError(c, "CALLBACK_UNAUTHORIZED", "callback token is not configured") return false } - if agnetCallbackTokenFromRequest(c) != expected { - agnetError(c, "CALLBACK_UNAUTHORIZED", "invalid callback service token") + if agentCallbackTokenFromRequest(c) != expected { + agentError(c, "CALLBACK_UNAUTHORIZED", "invalid callback service token") return false } return true } -func agnetCallbackHasPlaintextSecret(payload agnetCallbackEnvelope) bool { +func agentCallbackHasPlaintextSecret(payload agentCallbackEnvelope) bool { if containsPlaintextSecret(payload.Metadata) || containsPlaintextSecret(payload.Payload) || containsPlaintextSecret(payload.Artifact.Metadata) { return true } @@ -158,43 +158,43 @@ func agnetCallbackHasPlaintextSecret(payload agnetCallbackEnvelope) bool { return containsPlaintextSecret(asMap) } -func agnetCallbackDeploymentContext(deploymentID string, swarmID string) (agnetDeploymentRecord, bool) { +func agentCallbackDeploymentContext(deploymentID string, swarmID string) (agentDeploymentRecord, bool) { deploymentID = strings.TrimSpace(deploymentID) swarmID = strings.TrimSpace(swarmID) if deploymentID != "" { - if record, ok := findAgnetDeploymentRecord(deploymentID); ok { + if record, ok := findAgentDeploymentRecord(deploymentID); ok { return record, true } } runtimeID := firstNonEmpty(swarmID, deploymentID) if runtimeID == "" { - return agnetDeploymentRecord{}, false + return agentDeploymentRecord{}, false } - agnetMu.RLock() - for _, record := range agnetDeployments { + agentMu.RLock() + for _, record := range agentDeployments { if strings.TrimSpace(record.RuntimeSwarmID) == runtimeID || strings.TrimSpace(record.RuntimeDeploymentID) == runtimeID { - agnetMu.RUnlock() + agentMu.RUnlock() return record, true } } - agnetMu.RUnlock() + agentMu.RUnlock() if model.DB == nil { - return agnetDeploymentRecord{}, false + return agentDeploymentRecord{}, false } - var row model.AgnetDeployment + var row model.AgentDeployment if err := model.DB.Where("runtime_swarm_id = ? OR runtime_deployment_id = ?", runtimeID, runtimeID).First(&row).Error; err != nil { - return agnetDeploymentRecord{}, false + return agentDeploymentRecord{}, false } - record, err := agnetDeploymentModelToRecord(row) + record, err := agentDeploymentModelToRecord(row) if err != nil { - common.SysLog("agnetCallbackDeploymentContext: " + err.Error()) - return agnetDeploymentRecord{}, false + common.SysLog("agentCallbackDeploymentContext: " + err.Error()) + return agentDeploymentRecord{}, false } - agnetMu.Lock() - agnetDeployments[record.DeploymentID] = record - agnetMu.Unlock() + agentMu.Lock() + agentDeployments[record.DeploymentID] = record + agentMu.Unlock() return record, true } @@ -232,7 +232,7 @@ func callbackMapValue(values map[string]any, key string) map[string]any { return nil } -func agnetCallbackEventRuntimeState(eventType string, payload map[string]any) string { +func agentCallbackEventRuntimeState(eventType string, payload map[string]any) string { if state := callbackStringValue(payload, "status"); state != "" { return state } @@ -248,7 +248,7 @@ func agnetCallbackEventRuntimeState(eventType string, payload map[string]any) st } } -func upsertAgnetCallbackAgentInstance(record *agnetDeploymentRecord, payload agnetCallbackEnvelope, phase string, runtimeState string) bool { +func upsertAgentCallbackAgentInstance(record *agentDeploymentRecord, payload agentCallbackEnvelope, phase string, runtimeState string) bool { if record == nil { return false } @@ -284,7 +284,7 @@ func upsertAgnetCallbackAgentInstance(record *agnetDeploymentRecord, payload agn return true } } - record.AgentInstances = append(record.AgentInstances, agnetAgentInstance{ + record.AgentInstances = append(record.AgentInstances, agentAgentInstance{ InstanceID: firstNonEmpty(instanceID, "agi_"+common.GetUUID()[:12]), Role: role, Phase: firstNonEmpty(phase, record.Phase), @@ -294,7 +294,7 @@ func upsertAgnetCallbackAgentInstance(record *agnetDeploymentRecord, payload agn return true } -func applyAgnetCallbackDeploymentState(payload agnetCallbackEnvelope, record agnetDeploymentRecord) (agnetDeploymentRecord, bool) { +func applyAgentCallbackDeploymentState(payload agentCallbackEnvelope, record agentDeploymentRecord) (agentDeploymentRecord, bool) { if strings.TrimSpace(record.DeploymentID) == "" { return record, false } @@ -342,25 +342,25 @@ func applyAgnetCallbackDeploymentState(payload agnetCallbackEnvelope, record agn } case "agent.started", "agent.completed", "agent.crashed": phase := firstNonEmpty(callbackStringValue(source, "stage"), callbackStringValue(source, "phase"), record.Phase) - runtimeState := agnetCallbackEventRuntimeState(payload.EventType, source) - if upsertAgnetCallbackAgentInstance(&record, payload, phase, runtimeState) { + runtimeState := agentCallbackEventRuntimeState(payload.EventType, source) + if upsertAgentCallbackAgentInstance(&record, payload, phase, runtimeState) { changed = true } } if !changed { return record, false } - record.UpdatedAt = firstNonEmpty(strings.TrimSpace(payload.OccurredAt), agnetNow()) - agnetMu.Lock() - agnetDeployments[record.DeploymentID] = record - agnetMu.Unlock() - if err := updateAgnetDeploymentRecord(record); err != nil { - common.SysLog("applyAgnetCallbackDeploymentState: " + err.Error()) + record.UpdatedAt = firstNonEmpty(strings.TrimSpace(payload.OccurredAt), agentNow()) + agentMu.Lock() + agentDeployments[record.DeploymentID] = record + agentMu.Unlock() + if err := updateAgentDeploymentRecord(record); err != nil { + common.SysLog("applyAgentCallbackDeploymentState: " + err.Error()) } return record, true } -func normalizeCallbackArtifact(payload *agnetCallbackEnvelope) { +func normalizeCallbackArtifact(payload *agentCallbackEnvelope) { if payload == nil || strings.TrimSpace(payload.Artifact.ArtifactID) != "" { return } @@ -371,7 +371,7 @@ func normalizeCallbackArtifact(payload *agnetCallbackEnvelope) { if len(source) == 0 { return } - payload.Artifact = agnetArtifactPayload{ + payload.Artifact = agentArtifactPayload{ ArtifactID: callbackStringValue(source, "artifact_id"), ArtifactType: callbackStringValue(source, "artifact_type"), Title: callbackStringValue(source, "title"), @@ -399,7 +399,7 @@ func normalizeCallbackArtifact(payload *agnetCallbackEnvelope) { } } -func persistAgnetArtifactFromCallback(payload agnetCallbackEnvelope, record agnetDeploymentRecord) error { +func persistAgentArtifactFromCallback(payload agentCallbackEnvelope, record agentDeploymentRecord) error { artifact := payload.Artifact if strings.TrimSpace(artifact.ArtifactID) == "" { return nil @@ -410,7 +410,7 @@ func persistAgnetArtifactFromCallback(payload agnetCallbackEnvelope, record agne metadataJSON = string(data) } } - return model.UpsertAgnetArtifact(&model.AgnetArtifact{ + return model.UpsertAgentArtifact(&model.AgentArtifact{ ArtifactID: strings.TrimSpace(artifact.ArtifactID), DeploymentID: strings.TrimSpace(payload.DeploymentID), TaskID: strings.TrimSpace(payload.TaskID), @@ -460,7 +460,7 @@ func callbackIntValue(values map[string]any, key string) int { } } -var agnetCallbackEventRequiredFields = map[string][]string{ +var agentCallbackEventRequiredFields = map[string][]string{ "deployment.status_changed": {"status"}, "phase.changed": {"stage", "checkpoint"}, "agent.started": {"agent_role"}, @@ -486,7 +486,7 @@ var agnetCallbackEventRequiredFields = map[string][]string{ "budget.alert": {"threshold_pct"}, } -var agnetCallbackEventCategories = map[string]string{ +var agentCallbackEventCategories = map[string]string{ "deployment.status_changed": "deployment", "phase.changed": "ordinary_sub", "agent.started": "ordinary_sub", @@ -512,33 +512,33 @@ var agnetCallbackEventCategories = map[string]string{ "budget.alert": "budget", } -func AgnetGetSwarmEventCallbackSchema(c *gin.Context) { - events := make([]string, 0, len(agnetCallbackEventRequiredFields)) - for eventType := range agnetCallbackEventRequiredFields { +func AgentGetRuntimeEventCallbackSchema(c *gin.Context) { + events := make([]string, 0, len(agentCallbackEventRequiredFields)) + for eventType := range agentCallbackEventRequiredFields { events = append(events, eventType) } sort.Strings(events) items := make([]gin.H, 0, len(events)) for _, eventType := range events { - required := append([]string(nil), agnetCallbackEventRequiredFields[eventType]...) + required := append([]string(nil), agentCallbackEventRequiredFields[eventType]...) sort.Strings(required) items = append(items, gin.H{ "event_type": eventType, - "category": agnetCallbackEventCategories[eventType], + "category": agentCallbackEventCategories[eventType], "required_fields": required, "payload_location": "top-level envelope or payload object; artifact_id may also be in artifact object", }) } common.ApiSuccess(c, gin.H{ - "callback_path": "/api/agnet/callbacks/swarm-events", + "callback_path": "/api/agent/callbacks/runtime-events", "auth": gin.H{ - "service_token_headers": []string{"X-Agnet-Service-Token", "Authorization: Bearer "}, - "hmac_headers": []string{"X-Agnet-Event-Id", "X-Agnet-Timestamp", "X-Agnet-Signature"}, + "service_token_headers": []string{"X-Agent-Service-Token", "Authorization: Bearer "}, + "hmac_headers": []string{"X-Agent-Event-Id", "X-Agent-Timestamp", "X-Agent-Signature"}, "hmac_payload": "timestamp + \".\" + event_id + \".\" + raw_body", }, - "dedupe_keys": []string{"X-Agnet-Event-Id", "event_id", "idempotency_key"}, + "dedupe_keys": []string{"X-Agent-Event-Id", "event_id", "idempotency_key"}, "events": items, "security": gin.H{ "plaintext_secrets_allowed": false, @@ -547,7 +547,7 @@ func AgnetGetSwarmEventCallbackSchema(c *gin.Context) { }) } -func callbackEnvelopeFieldValue(payload agnetCallbackEnvelope, key string) string { +func callbackEnvelopeFieldValue(payload agentCallbackEnvelope, key string) string { switch key { case "task_id": return strings.TrimSpace(payload.TaskID) @@ -558,7 +558,7 @@ func callbackEnvelopeFieldValue(payload agnetCallbackEnvelope, key string) strin } } -func callbackHasFieldValue(payload agnetCallbackEnvelope, key string) bool { +func callbackHasFieldValue(payload agentCallbackEnvelope, key string) bool { if callbackEnvelopeFieldValue(payload, key) != "" { return true } @@ -578,8 +578,8 @@ func callbackHasFieldValue(payload agnetCallbackEnvelope, key string) bool { return false } -func validateAgnetCallbackEventSchema(payload agnetCallbackEnvelope) error { - required, ok := agnetCallbackEventRequiredFields[payload.EventType] +func validateAgentCallbackEventSchema(payload agentCallbackEnvelope) error { + required, ok := agentCallbackEventRequiredFields[payload.EventType] if !ok { return nil } @@ -591,7 +591,7 @@ func validateAgnetCallbackEventSchema(payload agnetCallbackEnvelope) error { return nil } -func persistAgnetApprovalFromCallback(payload agnetCallbackEnvelope, record agnetDeploymentRecord) error { +func persistAgentApprovalFromCallback(payload agentCallbackEnvelope, record agentDeploymentRecord) error { if payload.EventType != "approval.requested" || model.DB == nil { return nil } @@ -601,7 +601,7 @@ func persistAgnetApprovalFromCallback(payload agnetCallbackEnvelope, record agne } approvalID := firstNonEmpty(callbackStringValue(source, "approval_id"), "appr_"+common.GetUUID()) userID, _ := strconv.Atoi(strings.TrimSpace(record.Plan.UserContext.UserID)) - approval := model.AgnetApprovalRequest{ + approval := model.AgentApprovalRequest{ ApprovalID: approvalID, UserId: userID, DeploymentID: strings.TrimSpace(payload.DeploymentID), @@ -614,16 +614,16 @@ func persistAgnetApprovalFromCallback(payload agnetCallbackEnvelope, record agne RiskLevel: firstNonEmpty(callbackStringValue(source, "risk_level"), "high"), RequiresCredential: callbackBoolValue(source, "requires_credential"), SecretRef: strings.TrimSpace(callbackStringValue(source, "secret_ref")), - Status: agnetApprovalStatusPending, - RequestedBy: firstNonEmpty(callbackStringValue(source, "requested_by"), "agnet-runtime"), + Status: agentApprovalStatusPending, + RequestedBy: firstNonEmpty(callbackStringValue(source, "requested_by"), "agent-runtime"), RequestReason: firstNonEmpty(callbackStringValue(source, "reason"), callbackStringValue(source, "summary"), "Runtime requested approval"), TTLSeconds: callbackIntValue(source, "ttl_seconds"), } if approval.TTLSeconds <= 0 { - approval.TTLSeconds = defaultAgnetApprovalTTLSeconds + approval.TTLSeconds = defaultAgentApprovalTTLSeconds } - if approval.TTLSeconds > maxAgnetApprovalTTLSeconds { - approval.TTLSeconds = maxAgnetApprovalTTLSeconds + if approval.TTLSeconds > maxAgentApprovalTTLSeconds { + approval.TTLSeconds = maxAgentApprovalTTLSeconds } if approval.SecretRef != "" && !strings.HasPrefix(approval.SecretRef, "azkv://") { return fmt.Errorf("approval secret_ref must use azkv:// Azure Key Vault reference") @@ -634,59 +634,59 @@ func persistAgnetApprovalFromCallback(payload agnetCallbackEnvelope, record agne now := time.Now().UnixMilli() approval.ExpiresAt = now + int64(approval.TTLSeconds)*1000 - var existing model.AgnetApprovalRequest + var existing model.AgentApprovalRequest if err := model.DB.Where("approval_id = ?", approval.ApprovalID).First(&existing).Error; err == nil { return nil } if err := model.DB.Create(&approval).Error; err != nil { return err } - recordAgnetApprovalAudit("approval.requested", &approval, nil, "ok", "") + recordAgentApprovalAudit("approval.requested", &approval, nil, "ok", "") return nil } -func AgnetReceiveSwarmEventCallback(c *gin.Context) { +func AgentReceiveRuntimeEventCallback(c *gin.Context) { rawBody, err := io.ReadAll(io.LimitReader(c.Request.Body, 1<<20)) if err != nil { - agnetError(c, "CALLBACK_INVALID", "failed to read callback body") + agentError(c, "CALLBACK_INVALID", "failed to read callback body") return } - var payload agnetCallbackEnvelope + var payload agentCallbackEnvelope if err := common.Unmarshal(rawBody, &payload); err != nil { - agnetError(c, "CALLBACK_INVALID", err.Error()) + agentError(c, "CALLBACK_INVALID", err.Error()) return } if payload.EventID == "" { - payload.EventID = strings.TrimSpace(c.GetHeader("X-Agnet-Event-Id")) + payload.EventID = strings.TrimSpace(c.GetHeader("X-Agent-Event-Id")) } if payload.CorrelationID == "" { payload.CorrelationID = strings.TrimSpace(c.GetHeader("X-Correlation-ID")) } payload.EventID = strings.TrimSpace(payload.EventID) payload.EventType = strings.TrimSpace(payload.EventType) - if !validateAgnetCallbackAuth(c, rawBody, payload.EventID) { + if !validateAgentCallbackAuth(c, rawBody, payload.EventID) { return } if payload.EventID == "" || payload.EventType == "" { - agnetError(c, "CALLBACK_INVALID", "event_id and event_type are required") + agentError(c, "CALLBACK_INVALID", "event_id and event_type are required") return } normalizeCallbackArtifact(&payload) if payload.IdempotencyKey == "" { payload.IdempotencyKey = payload.EventID } - if err := validateAgnetCallbackEventSchema(payload); err != nil { - agnetError(c, "CALLBACK_SCHEMA_INVALID", err.Error()) + if err := validateAgentCallbackEventSchema(payload); err != nil { + agentError(c, "CALLBACK_SCHEMA_INVALID", err.Error()) return } - if agnetCallbackHasPlaintextSecret(payload) { - agnetError(c, "CALLBACK_SECRET_REJECTED", "callbacks must not contain plaintext credential fields") + if agentCallbackHasPlaintextSecret(payload) { + agentError(c, "CALLBACK_SECRET_REJECTED", "callbacks must not contain plaintext credential fields") return } incomingDeploymentID := strings.TrimSpace(payload.DeploymentID) incomingSwarmID := strings.TrimSpace(payload.SwarmID) - record, _ := agnetCallbackDeploymentContext(incomingDeploymentID, incomingSwarmID) + record, _ := agentCallbackDeploymentContext(incomingDeploymentID, incomingSwarmID) if strings.TrimSpace(record.DeploymentID) != "" { payload.DeploymentID = record.DeploymentID if strings.TrimSpace(payload.SwarmID) == "" { @@ -697,7 +697,7 @@ func AgnetReceiveSwarmEventCallback(c *gin.Context) { payload.CorrelationID = record.Plan.Metadata.CorrelationID } payloadJSON, _ := common.Marshal(payload) - inserted, err := model.InsertAgnetCallbackEvent(&model.AgnetCallbackEvent{ + inserted, err := model.InsertAgentCallbackEvent(&model.AgentCallbackEvent{ EventID: payload.EventID, IdempotencyKey: strings.TrimSpace(payload.IdempotencyKey), CallbackType: "swarm-event", @@ -716,23 +716,23 @@ func AgnetReceiveSwarmEventCallback(c *gin.Context) { CreatedAtMs: time.Now().UnixMilli(), }) if err != nil { - common.SysLog("AgnetReceiveSwarmEventCallback: " + err.Error()) - agnetError(c, "CALLBACK_PERSIST_FAILED", "failed to persist callback") + common.SysLog("AgentReceiveRuntimeEventCallback: " + err.Error()) + agentError(c, "CALLBACK_PERSIST_FAILED", "failed to persist callback") return } if inserted { - if err := persistAgnetArtifactFromCallback(payload, record); err != nil { - common.SysLog("persistAgnetArtifactFromCallback: " + err.Error()) - agnetError(c, "CALLBACK_PERSIST_FAILED", "failed to persist artifact") + if err := persistAgentArtifactFromCallback(payload, record); err != nil { + common.SysLog("persistAgentArtifactFromCallback: " + err.Error()) + agentError(c, "CALLBACK_PERSIST_FAILED", "failed to persist artifact") return } - if err := persistAgnetApprovalFromCallback(payload, record); err != nil { - common.SysLog("persistAgnetApprovalFromCallback: " + err.Error()) - agnetError(c, "CALLBACK_PERSIST_FAILED", "failed to persist approval request") + if err := persistAgentApprovalFromCallback(payload, record); err != nil { + common.SysLog("persistAgentApprovalFromCallback: " + err.Error()) + agentError(c, "CALLBACK_PERSIST_FAILED", "failed to persist approval request") return } - record, _ = applyAgnetCallbackDeploymentState(payload, record) - recordAgnetAuditEvent(agnetEvent{ + record, _ = applyAgentCallbackDeploymentState(payload, record) + recordAgentAuditEvent(agentEvent{ EventID: "evt_" + common.GetUUID()[:12], Event: "callback." + payload.EventType, SchemaVersion: 1, @@ -741,8 +741,8 @@ func AgnetReceiveSwarmEventCallback(c *gin.Context) { BindingScope: firstPlanBindingScope(record.Plan), DeploymentID: strings.TrimSpace(payload.DeploymentID), CorrelationID: strings.TrimSpace(payload.CorrelationID), - OccurredAt: firstNonEmpty(strings.TrimSpace(payload.OccurredAt), agnetNow()), - }, "agnet_callback", strings.TrimSpace(payload.DeploymentID), agnetRequestID(c), "ok") + OccurredAt: firstNonEmpty(strings.TrimSpace(payload.OccurredAt), agentNow()), + }, "agent_callback", strings.TrimSpace(payload.DeploymentID), agentRequestID(c), "ok") } common.ApiSuccess(c, gin.H{ "event_id": payload.EventID, @@ -753,54 +753,54 @@ func AgnetReceiveSwarmEventCallback(c *gin.Context) { }) } -func AgnetListUserDeploymentArtifacts(c *gin.Context) { - record, ok := requireAuthenticatedUserAgnetDeployment(c) +func AgentListUserDeploymentArtifacts(c *gin.Context) { + record, ok := requireAuthenticatedUserAgentDeployment(c) if !ok { return } - items, err := model.ListAgnetArtifacts(model.ListAgnetArtifactsFilter{ + items, err := model.ListAgentArtifacts(model.ListAgentArtifactsFilter{ DeploymentID: record.DeploymentID, Limit: 500, }) if err != nil { - common.SysLog("AgnetListUserDeploymentArtifacts: " + err.Error()) - agnetError(c, "ARTIFACT_QUERY_FAILED", "failed to query artifacts") + common.SysLog("AgentListUserDeploymentArtifacts: " + err.Error()) + agentError(c, "ARTIFACT_QUERY_FAILED", "failed to query artifacts") return } common.ApiSuccess(c, gin.H{"deployment_id": record.DeploymentID, "artifacts": items, "items": items, "total": len(items)}) } -func AgnetGetUserDeploymentArtifactContent(c *gin.Context) { - record, ok := requireAuthenticatedUserAgnetDeployment(c) +func AgentGetUserDeploymentArtifactContent(c *gin.Context) { + record, ok := requireAuthenticatedUserAgentDeployment(c) if !ok { return } artifactID := strings.TrimSpace(c.Param("artifact_id")) if artifactID == "" { - agnetError(c, "ARTIFACT_ID_REQUIRED", "artifact_id is required") + agentError(c, "ARTIFACT_ID_REQUIRED", "artifact_id is required") return } - artifact, found, err := model.GetAgnetArtifactByDeployment(record.DeploymentID, artifactID) + artifact, found, err := model.GetAgentArtifactByDeployment(record.DeploymentID, artifactID) if err != nil { - common.SysLog("AgnetGetUserDeploymentArtifactContent: " + err.Error()) - agnetError(c, "ARTIFACT_QUERY_FAILED", "failed to query artifact") + common.SysLog("AgentGetUserDeploymentArtifactContent: " + err.Error()) + agentError(c, "ARTIFACT_QUERY_FAILED", "failed to query artifact") return } if !found { - agnetError(c, "ARTIFACT_NOT_FOUND", "artifact not found") + agentError(c, "ARTIFACT_NOT_FOUND", "artifact not found") return } - cfg := agnetRuntimeClientConfigForMode(agnetRuntimeModeForRecord(record)) + cfg := agentRuntimeClientConfigForMode(agentRuntimeModeForRecord(record)) if !cfg.Enabled || strings.TrimSpace(cfg.BaseURL) == "" { - agnetError(c, "RUNTIME_NOT_CONFIGURED", "runtime is not configured") + agentError(c, "RUNTIME_NOT_CONFIGURED", "runtime is not configured") return } ctx, cancel := context.WithTimeout(c.Request.Context(), cfg.Timeout) defer cancel() - resp, err := callAgnetRuntimeArtifactContent(ctx, cfg, record, artifact.ArtifactID) + resp, err := callAgentRuntimeArtifactContent(ctx, cfg, record, artifact.ArtifactID) if err != nil { - common.SysLog("AgnetGetUserDeploymentArtifactContent: " + err.Error()) - agnetError(c, "ARTIFACT_CONTENT_FETCH_FAILED", "failed to fetch artifact content") + common.SysLog("AgentGetUserDeploymentArtifactContent: " + err.Error()) + agentError(c, "ARTIFACT_CONTENT_FETCH_FAILED", "failed to fetch artifact content") return } defer resp.Body.Close() @@ -818,8 +818,8 @@ func AgnetGetUserDeploymentArtifactContent(c *gin.Context) { c.DataFromReader(http.StatusOK, resp.ContentLength, contentType, resp.Body, headers) } -func callbackPayloadMap(row model.AgnetCallbackEvent) map[string]any { - var payload agnetCallbackEnvelope +func callbackPayloadMap(row model.AgentCallbackEvent) map[string]any { + var payload agentCallbackEnvelope if err := common.UnmarshalJsonStr(row.PayloadJSON, &payload); err != nil { return nil } @@ -829,7 +829,7 @@ func callbackPayloadMap(row model.AgnetCallbackEvent) map[string]any { return nil } -func timelineEntryFromCallback(callback model.AgnetCallbackEvent) gin.H { +func timelineEntryFromCallback(callback model.AgentCallbackEvent) gin.H { payload := callbackPayloadMap(callback) entry := gin.H{ "kind": "callback", @@ -852,29 +852,29 @@ func timelineEntryFromCallback(callback model.AgnetCallbackEvent) gin.H { return entry } -func AgnetGetUserDeploymentTimeline(c *gin.Context) { - record, ok := requireAuthenticatedUserAgnetDeployment(c) +func AgentGetUserDeploymentTimeline(c *gin.Context) { + record, ok := requireAuthenticatedUserAgentDeployment(c) if !ok { return } - events, err := model.ListAgnetAuditEventsByDeployment(record.DeploymentID) + events, err := model.ListAgentAuditEventsByDeployment(record.DeploymentID) if err != nil { - agnetError(c, "TIMELINE_QUERY_FAILED", "failed to query audit events") + agentError(c, "TIMELINE_QUERY_FAILED", "failed to query audit events") return } - callbacks, err := model.ListAgnetCallbackEvents(model.ListAgnetCallbackEventsFilter{DeploymentID: record.DeploymentID, Limit: 500}) + callbacks, err := model.ListAgentCallbackEvents(model.ListAgentCallbackEventsFilter{DeploymentID: record.DeploymentID, Limit: 500}) if err != nil { - agnetError(c, "TIMELINE_QUERY_FAILED", "failed to query callbacks") + agentError(c, "TIMELINE_QUERY_FAILED", "failed to query callbacks") return } - artifacts, err := model.ListAgnetArtifacts(model.ListAgnetArtifactsFilter{DeploymentID: record.DeploymentID, Limit: 500}) + artifacts, err := model.ListAgentArtifacts(model.ListAgentArtifactsFilter{DeploymentID: record.DeploymentID, Limit: 500}) if err != nil { - agnetError(c, "TIMELINE_QUERY_FAILED", "failed to query artifacts") + agentError(c, "TIMELINE_QUERY_FAILED", "failed to query artifacts") return } - snapshots, err := model.ListAgnetSKSnapshots(record.DeploymentID) + snapshots, err := model.ListAgentSKSnapshots(record.DeploymentID) if err != nil { - agnetError(c, "TIMELINE_QUERY_FAILED", "failed to query sk snapshots") + agentError(c, "TIMELINE_QUERY_FAILED", "failed to query sk snapshots") return } timeline := make([]gin.H, 0, len(events)+len(callbacks)+len(artifacts)+len(snapshots)) @@ -892,7 +892,7 @@ func AgnetGetUserDeploymentTimeline(c *gin.Context) { } common.ApiSuccess(c, gin.H{ "deployment_id": record.DeploymentID, - "deployment": record, + "deployment": withDisplayStatus(record), "events": events, "callbacks": callbacks, "artifacts": artifacts, diff --git a/heicode/controller/agnet_control_plane.go b/heicode/controller/agent_control_plane.go similarity index 64% rename from heicode/controller/agnet_control_plane.go rename to heicode/controller/agent_control_plane.go index ec04ad5..d40b3cf 100644 --- a/heicode/controller/agnet_control_plane.go +++ b/heicode/controller/agent_control_plane.go @@ -14,32 +14,32 @@ import ( ) const ( - agnetRiskLow = "low" - agnetRiskMedium = "medium" - agnetRiskHigh = "high" + agentRiskLow = "low" + agentRiskMedium = "medium" + agentRiskHigh = "high" - agnetSubModeAgile = "agile" - agnetSubModeWaterfall = "waterfall" + agentSubModeAgile = "agile" + agentSubModeWaterfall = "waterfall" - agnetResourceGit = "git" - agnetResourceSK = "sk" - agnetResourceProjectDoc = "project_doc" - agnetResourceCloudAccount = "cloud_account" - agnetResourceCloudResource = "cloud_resource" + agentResourceGit = "git" + agentResourceSK = "sk" + agentResourceProjectDoc = "project_doc" + agentResourceCloudAccount = "cloud_account" + agentResourceCloudResource = "cloud_resource" - agnetGrantStatusPending = "pending" - agnetGrantStatusActive = "active" - agnetGrantStatusDisabled = "disabled" - agnetGrantStatusRevoked = "revoked" + agentGrantStatusPending = "pending" + agentGrantStatusActive = "active" + agentGrantStatusDisabled = "disabled" + agentGrantStatusRevoked = "revoked" ) -type agnetBudget struct { +type agentBudget struct { MaxTokens int `json:"max_tokens"` MaxCostUSD float64 `json:"max_cost_usd"` MaxDurationSec int `json:"max_duration_sec"` } -type agnetUserContext struct { +type agentUserContext struct { UserID string `json:"user_id"` Email string `json:"email"` Role string `json:"role"` @@ -47,7 +47,7 @@ type agnetUserContext struct { SubscriptionTier string `json:"subscription_tier"` } -type agnetBillingContext struct { +type agentBillingContext struct { Provider string `json:"provider"` NewAPIUserRef string `json:"newapi_user_ref"` NewAPIGroup string `json:"newapi_group"` @@ -57,7 +57,7 @@ type agnetBillingContext struct { SecretRef string `json:"secret_ref"` } -type agnetAgileContext struct { +type agentAgileContext struct { Iteration string `json:"iteration"` Stage string `json:"stage"` Checkpoint string `json:"checkpoint"` @@ -66,49 +66,49 @@ type agnetAgileContext struct { RequiresUserApproval bool `json:"requires_user_approval"` } -type agnetRuntimeAgent struct { +type agentRuntimeAgent struct { Role string `json:"role"` ModelRef string `json:"model_ref"` InstanceCount int `json:"instance_count"` } -type agnetAgentRuntime struct { +type agentAgentRuntime struct { Platform string `json:"platform"` - Agents []agnetRuntimeAgent `json:"agents"` + Agents []agentRuntimeAgent `json:"agents"` } -// agnetRepoRef matches docs/integration/orchestration-plan-contract.md (git sk_sources). -type agnetRepoRef struct { +// agentRepoRef matches docs/integration/orchestration-plan-contract.md (git sk_sources). +type agentRepoRef struct { ConnectionID string `json:"connection_id"` RepoURL string `json:"repo_url"` Ref string `json:"ref"` Paths []string `json:"paths"` } -type agnetSKSource struct { +type agentSKSource struct { Type string `json:"type"` ArtifactID string `json:"artifact_id"` Mime string `json:"mime"` - RepoRef agnetRepoRef `json:"repo_ref"` + RepoRef agentRepoRef `json:"repo_ref"` } -// agnetRuntimeExecution mirrors docs/integration/agnet-platform-api-design.md §5.0 (runtime_execution). -type agnetRuntimeExecution struct { +// agentRuntimeExecution mirrors docs/integration/agent-platform-api-design.md §5.0 (runtime_execution). +type agentRuntimeExecution struct { ProfileID string `json:"profile_id"` CloudPrincipalRefs []string `json:"cloud_principal_refs"` NetworkPolicyRef string `json:"network_policy_ref"` } -// agnetSKAccessPolicy mirrors docs/integration/agnet-platform-api-design.md §5.0 (sk_access_policy). -type agnetSKAccessPolicy struct { +// agentSKAccessPolicy mirrors docs/integration/agent-platform-api-design.md §5.0 (sk_access_policy). +type agentSKAccessPolicy struct { PolicyRef string `json:"policy_ref"` DenySkillIDs []string `json:"deny_skill_ids"` InheritDeploymentDefaults bool `json:"inherit_deployment_defaults"` } -// agnetResourceGrant is the Manager-side resource binding envelope from docs/heicode.md P1. +// agentResourceGrant is the Manager-side resource binding envelope from docs/heicode.md P1. // It deliberately carries only metadata, scoped permissions and secret_ref, never plaintext secrets. -type agnetResourceGrant struct { +type agentResourceGrant struct { GrantID string `json:"grant_id"` ResourceID string `json:"resource_id"` ResourceType string `json:"resource_type"` @@ -123,69 +123,74 @@ type agnetResourceGrant struct { Metadata map[string]string `json:"metadata"` Status string `json:"status"` SecretRef string `json:"secret_ref"` - Audit map[string]string `json:"audit"` + // ResourceBindingID lets the client reference a stored ResourceBinding + // instead of inlining a secret_ref (unified spec §17.6). Manager resolves it + // to the binding's real secret_ref/resource metadata server-side. + ResourceBindingID int `json:"resource_binding_id,omitempty"` + Audit map[string]string `json:"audit"` } -type agnetAgentPlan struct { +type agentAgentPlan struct { RoleTemplate string `json:"role_template"` Goal string `json:"goal"` DefaultModelID string `json:"default_model_id"` - SKSources []agnetSKSource `json:"sk_sources"` - RuntimeExecution agnetRuntimeExecution `json:"runtime_execution"` - SKAccessPolicy agnetSKAccessPolicy `json:"sk_access_policy"` - ResourceGrants []agnetResourceGrant `json:"resource_grants"` + SKSources []agentSKSource `json:"sk_sources"` + RuntimeExecution agentRuntimeExecution `json:"runtime_execution"` + SKAccessPolicy agentSKAccessPolicy `json:"sk_access_policy"` + ResourceGrants []agentResourceGrant `json:"resource_grants"` } -type agnetConstraints struct { +type agentConstraints struct { AllowedModelIDs []string `json:"allowed_model_ids"` } -type agnetMetadata struct { +type agentMetadata struct { TenantID string `json:"tenant_id,omitempty"` // legacy compatibility only. ProjectID string `json:"project_id,omitempty"` // legacy compatibility only. CorrelationID string `json:"correlation_id"` RuntimeMode string `json:"runtime_mode,omitempty"` } -type agnetOrchestrationPlan struct { +type agentOrchestrationPlan struct { IntentID string `json:"intent_id"` TemplateHint string `json:"template_hint"` Objective string `json:"objective"` SubMode string `json:"sub_mode"` RiskLevel string `json:"risk_level"` - Budget agnetBudget `json:"budget"` - UserContext agnetUserContext `json:"user_context"` - BillingContext agnetBillingContext `json:"billing_context"` - AgileContext agnetAgileContext `json:"agile_context"` - AgentRuntime agnetAgentRuntime `json:"agent_runtime"` - Agents []agnetAgentPlan `json:"agents"` - ResourceGrants []agnetResourceGrant `json:"resource_grants"` - Constraints agnetConstraints `json:"constraints"` - Metadata agnetMetadata `json:"metadata"` + Budget agentBudget `json:"budget"` + UserContext agentUserContext `json:"user_context"` + BillingContext agentBillingContext `json:"billing_context"` + AgileContext agentAgileContext `json:"agile_context"` + AgentRuntime agentAgentRuntime `json:"agent_runtime"` + Agents []agentAgentPlan `json:"agents"` + ResourceGrants []agentResourceGrant `json:"resource_grants"` + Constraints agentConstraints `json:"constraints"` + Metadata agentMetadata `json:"metadata"` } -type agnetDeploymentRequest struct { - Plan agnetOrchestrationPlan `json:"orchestration_plan"` +type agentDeploymentRequest struct { + Plan agentOrchestrationPlan `json:"orchestration_plan"` } -type agnetDeploymentRecord struct { +type agentDeploymentRecord struct { DeploymentID string `json:"deployment_id"` SubMode string `json:"sub_mode"` Status string `json:"status"` + DisplayStatus string `json:"display_status,omitempty"` Phase string `json:"phase"` RuntimeState string `json:"runtime_state"` RuntimeDeploymentID string `json:"runtime_deployment_id,omitempty"` RuntimeSwarmID string `json:"runtime_swarm_id,omitempty"` RuntimeLastSyncAt string `json:"runtime_last_sync_at,omitempty"` FailureReason string `json:"failure_reason"` - AgentInstances []agnetAgentInstance `json:"agent_instances"` - ResourceGrantManifest agnetPermissionManifest `json:"permission_manifest"` + AgentInstances []agentAgentInstance `json:"agent_instances"` + ResourceGrantManifest agentPermissionManifest `json:"permission_manifest"` CreatedAt string `json:"created_at"` UpdatedAt string `json:"updated_at"` - Plan agnetOrchestrationPlan `json:"orchestration_plan"` + Plan agentOrchestrationPlan `json:"orchestration_plan"` } -type agnetAgentInstance struct { +type agentAgentInstance struct { InstanceID string `json:"instance_id"` Role string `json:"role"` Phase string `json:"phase"` @@ -193,7 +198,7 @@ type agnetAgentInstance struct { FailureReason string `json:"failure_reason"` } -type agnetManifestGrant struct { +type agentManifestGrant struct { GrantID string `json:"grant_id"` ResourceID string `json:"resource_id"` ResourceType string `json:"resource_type"` @@ -204,15 +209,15 @@ type agnetManifestGrant struct { Status string `json:"status"` } -type agnetPermissionManifest struct { +type agentPermissionManifest struct { UserID string `json:"user_id"` BindingScope string `json:"binding_scope"` AgentRole string `json:"agent_role"` TargetAgentRef string `json:"target_agent_ref"` - ResourceGrants []agnetManifestGrant `json:"resource_grants"` + ResourceGrants []agentManifestGrant `json:"resource_grants"` } -type agnetEvent struct { +type agentEvent struct { EventID string `json:"event_id"` Event string `json:"event"` SchemaVersion int `json:"schema_version"` @@ -224,15 +229,15 @@ type agnetEvent struct { OccurredAt string `json:"occurred_at"` } -type agnetSKSnapshotResolveRequest struct { +type agentSKSnapshotResolveRequest struct { DeploymentID string `json:"deployment_id"` } -type agnetSimulateDeploymentEventsRequest struct { +type agentSimulateDeploymentEventsRequest struct { Events []string `json:"events"` } -type agnetSKSnapshot struct { +type agentSKSnapshot struct { SnapshotID string `json:"snapshot_id"` DeploymentID string `json:"deployment_id"` UserID string `json:"user_id"` @@ -243,25 +248,25 @@ type agnetSKSnapshot struct { } var ( - agnetMu sync.RWMutex - agnetDeployments = make(map[string]agnetDeploymentRecord) - agnetSnapshots = make(map[string][]agnetSKSnapshot) + agentMu sync.RWMutex + agentDeployments = make(map[string]agentDeploymentRecord) + agentSnapshots = make(map[string][]agentSKSnapshot) ) -// recordAgnetAuditEvent persists `evt` to model.AgnetAuditEvent +// recordAgentAuditEvent persists `evt` to model.AgentAuditEvent // (audit table) so dashboards and admin queries survive a container // restart. The previous implementation appended to an in-process -// map[string][]agnetEvent which was lost on every redeploy. +// map[string][]agentEvent which was lost on every redeploy. // // `requestID` and `result` are usually unknown at the producer site // (we're called from inside the deployment lifecycle, not from a // request handler with a c.GetString), so they're free-form here. // Pass "" / "ok" when you don't have specifics. -func recordAgnetAuditEvent(evt agnetEvent, actor, resource, requestID, result string) { +func recordAgentAuditEvent(evt agentEvent, actor, resource, requestID, result string) { if result == "" { result = "ok" } - model.InsertAgnetAuditEvent(&model.AgnetAuditEvent{ + model.InsertAgentAuditEvent(&model.AgentAuditEvent{ EventID: evt.EventID, Event: evt.Event, Actor: actor, @@ -274,39 +279,39 @@ func recordAgnetAuditEvent(evt agnetEvent, actor, resource, requestID, result st RequestID: requestID, Result: result, SchemaVersion: evt.SchemaVersion, - OccurredAt: parseAgnetOccurredAtMs(evt.OccurredAt), + OccurredAt: parseAgentOccurredAtMs(evt.OccurredAt), }) } -// parseAgnetOccurredAtMs converts the RFC3339 string emitted by -// agnetNow() into unix-ms for the DB column. Falls back to "now" so +// parseAgentOccurredAtMs converts the RFC3339 string emitted by +// agentNow() into unix-ms for the DB column. Falls back to "now" so // a malformed timestamp doesn't drop the row. -func parseAgnetOccurredAtMs(s string) int64 { +func parseAgentOccurredAtMs(s string) int64 { if t, err := time.Parse(time.RFC3339, s); err == nil { return t.UnixMilli() } return time.Now().UnixMilli() } -func agnetNow() string { +func agentNow() string { return time.Now().UTC().Format(time.RFC3339) } -func agnetRequestID(c *gin.Context) string { +func agentRequestID(c *gin.Context) string { if reqID := strings.TrimSpace(c.GetString(common.RequestIdKey)); reqID != "" { return reqID } return common.GetUUID() } -func agnetTimestampMs(s string) int64 { +func agentTimestampMs(s string) int64 { if t, err := time.Parse(time.RFC3339, s); err == nil { return t.UnixMilli() } return time.Now().UnixMilli() } -func marshalAgnetSnapshot(v any) (string, error) { +func marshalAgentSnapshot(v any) (string, error) { data, err := common.Marshal(v) if err != nil { return "", err @@ -314,10 +319,10 @@ func marshalAgnetSnapshot(v any) (string, error) { return string(data), nil } -func agnetDeploymentModelToRecord(row model.AgnetDeployment) (agnetDeploymentRecord, error) { - var record agnetDeploymentRecord +func agentDeploymentModelToRecord(row model.AgentDeployment) (agentDeploymentRecord, error) { + var record agentDeploymentRecord record.DeploymentID = row.DeploymentID - record.SubMode = normalizeAgnetSubMode(row.SubMode) + record.SubMode = normalizeAgentSubMode(row.SubMode) record.Status = row.Status record.Phase = row.Phase record.RuntimeState = row.RuntimeState @@ -344,42 +349,42 @@ func agnetDeploymentModelToRecord(row model.AgnetDeployment) (agnetDeploymentRec } } if record.AgentInstances == nil { - record.AgentInstances = []agnetAgentInstance{} + record.AgentInstances = []agentAgentInstance{} } if record.ResourceGrantManifest.ResourceGrants == nil { - record.ResourceGrantManifest.ResourceGrants = []agnetManifestGrant{} + record.ResourceGrantManifest.ResourceGrants = []agentManifestGrant{} } return record, nil } -func persistAgnetDeploymentRecord(record agnetDeploymentRecord, req agnetDeploymentRequest) error { +func persistAgentDeploymentRecord(record agentDeploymentRecord, req agentDeploymentRequest) error { if model.DB == nil { return nil } - planJSON, err := marshalAgnetSnapshot(record.Plan) + planJSON, err := marshalAgentSnapshot(record.Plan) if err != nil { return err } - instancesJSON, err := marshalAgnetSnapshot(record.AgentInstances) + instancesJSON, err := marshalAgentSnapshot(record.AgentInstances) if err != nil { return err } - manifestJSON, err := marshalAgnetSnapshot(record.ResourceGrantManifest) + manifestJSON, err := marshalAgentSnapshot(record.ResourceGrantManifest) if err != nil { return err } - payloadJSON, err := marshalAgnetSnapshot(req) + payloadJSON, err := marshalAgentSnapshot(req) if err != nil { return err } - row := model.AgnetDeployment{ + row := model.AgentDeployment{ DeploymentID: record.DeploymentID, UserID: record.Plan.UserContext.UserID, ChannelID: record.Plan.UserContext.ChannelID, BindingScope: firstPlanBindingScope(record.Plan), CorrelationID: record.Plan.Metadata.CorrelationID, - SubMode: normalizeAgnetSubMode(record.SubMode), + SubMode: normalizeAgentSubMode(record.SubMode), Status: record.Status, Phase: record.Phase, RuntimeState: record.RuntimeState, @@ -389,8 +394,8 @@ func persistAgnetDeploymentRecord(record agnetDeploymentRecord, req agnetDeploym FailureReason: record.FailureReason, CreatedAtText: record.CreatedAt, UpdatedAtText: record.UpdatedAt, - CreatedAtMs: agnetTimestampMs(record.CreatedAt), - UpdatedAtMs: agnetTimestampMs(record.UpdatedAt), + CreatedAtMs: agentTimestampMs(record.CreatedAt), + UpdatedAtMs: agentTimestampMs(record.UpdatedAt), PlanJSON: planJSON, AgentInstancesJSON: instancesJSON, PermissionManifestJSON: manifestJSON, @@ -399,23 +404,23 @@ func persistAgnetDeploymentRecord(record agnetDeploymentRecord, req agnetDeploym return model.DB.Create(&row).Error } -func updateAgnetDeploymentRecord(record agnetDeploymentRecord) error { +func updateAgentDeploymentRecord(record agentDeploymentRecord) error { if model.DB == nil { return nil } - instancesJSON, err := marshalAgnetSnapshot(record.AgentInstances) + instancesJSON, err := marshalAgentSnapshot(record.AgentInstances) if err != nil { return err } - manifestJSON, err := marshalAgnetSnapshot(record.ResourceGrantManifest) + manifestJSON, err := marshalAgentSnapshot(record.ResourceGrantManifest) if err != nil { return err } - return model.DB.Model(&model.AgnetDeployment{}). + return model.DB.Model(&model.AgentDeployment{}). Where("deployment_id = ?", record.DeploymentID). Updates(map[string]any{ "status": record.Status, - "sub_mode": normalizeAgnetSubMode(record.SubMode), + "sub_mode": normalizeAgentSubMode(record.SubMode), "phase": record.Phase, "runtime_state": record.RuntimeState, "runtime_deployment_id": record.RuntimeDeploymentID, @@ -423,69 +428,69 @@ func updateAgnetDeploymentRecord(record agnetDeploymentRecord) error { "runtime_last_sync_at_text": record.RuntimeLastSyncAt, "failure_reason": record.FailureReason, "updated_at_text": record.UpdatedAt, - "updated_at_ms": agnetTimestampMs(record.UpdatedAt), + "updated_at_ms": agentTimestampMs(record.UpdatedAt), "agent_instances_json": instancesJSON, "permission_manifest_json": manifestJSON, }).Error } -func normalizeAgnetSubMode(value string) string { +func normalizeAgentSubMode(value string) string { switch strings.ToLower(strings.TrimSpace(value)) { - case agnetSubModeWaterfall: - return agnetSubModeWaterfall + case agentSubModeWaterfall: + return agentSubModeWaterfall default: - return agnetSubModeAgile + return agentSubModeAgile } } -func isValidAgnetSubMode(value string) bool { +func isValidAgentSubMode(value string) bool { switch strings.ToLower(strings.TrimSpace(value)) { - case "", agnetSubModeAgile, agnetSubModeWaterfall: + case "", agentSubModeAgile, agentSubModeWaterfall: return true default: return false } } -func findAgnetDeploymentRecord(deploymentID string) (agnetDeploymentRecord, bool) { - agnetMu.RLock() - record, ok := agnetDeployments[deploymentID] - agnetMu.RUnlock() +func findAgentDeploymentRecord(deploymentID string) (agentDeploymentRecord, bool) { + agentMu.RLock() + record, ok := agentDeployments[deploymentID] + agentMu.RUnlock() if ok { return record, true } if model.DB == nil { - return agnetDeploymentRecord{}, false + return agentDeploymentRecord{}, false } - var row model.AgnetDeployment + var row model.AgentDeployment if err := model.DB.Where("deployment_id = ?", deploymentID).First(&row).Error; err != nil { - return agnetDeploymentRecord{}, false + return agentDeploymentRecord{}, false } - record, err := agnetDeploymentModelToRecord(row) + record, err := agentDeploymentModelToRecord(row) if err != nil { - common.SysLog("findAgnetDeploymentRecord: " + err.Error()) - return agnetDeploymentRecord{}, false + common.SysLog("findAgentDeploymentRecord: " + err.Error()) + return agentDeploymentRecord{}, false } - agnetMu.Lock() - agnetDeployments[deploymentID] = record - agnetMu.Unlock() + agentMu.Lock() + agentDeployments[deploymentID] = record + agentMu.Unlock() return record, true } -func listAgnetDeploymentRecords(userID string, bindingScope string) []agnetDeploymentRecord { - items := make([]agnetDeploymentRecord, 0) +func listAgentDeploymentRecords(userID string, bindingScope string) []agentDeploymentRecord { + items := make([]agentDeploymentRecord, 0) if model.DB != nil { - q := model.DB.Model(&model.AgnetDeployment{}) + q := model.DB.Model(&model.AgentDeployment{}) if userID != "" { q = q.Where("user_id = ?", userID) } - var rows []model.AgnetDeployment + var rows []model.AgentDeployment if err := q.Order("created_at_ms desc, id desc").Limit(500).Find(&rows).Error; err == nil { for _, row := range rows { - record, err := agnetDeploymentModelToRecord(row) + record, err := agentDeploymentModelToRecord(row) if err != nil { - common.SysLog("listAgnetDeploymentRecords: " + err.Error()) + common.SysLog("listAgentDeploymentRecords: " + err.Error()) continue } if bindingScope != "" && !planHasBindingScope(record.Plan, bindingScope) { @@ -495,12 +500,12 @@ func listAgnetDeploymentRecords(userID string, bindingScope string) []agnetDeplo } return items } else { - common.SysLog("listAgnetDeploymentRecords: " + err.Error()) + common.SysLog("listAgentDeploymentRecords: " + err.Error()) } } - agnetMu.RLock() - for _, record := range agnetDeployments { + agentMu.RLock() + for _, record := range agentDeployments { if userID != "" && record.Plan.UserContext.UserID != userID { continue } @@ -509,18 +514,18 @@ func listAgnetDeploymentRecords(userID string, bindingScope string) []agnetDeplo } items = append(items, record) } - agnetMu.RUnlock() + agentMu.RUnlock() return items } -func agnetError(c *gin.Context, code string, message string) { +func agentError(c *gin.Context, code string, message string) { c.JSON(http.StatusOK, gin.H{ "success": false, "message": message, "error": gin.H{ "code": code, "message": message, - "request_id": agnetRequestID(c), + "request_id": agentRequestID(c), }, }) } @@ -552,34 +557,34 @@ func containsSensitiveGrantField(values map[string]string) bool { return false } -func agnetResourceTypeNeedsSecretRef(resourceType string) bool { +func agentResourceTypeNeedsSecretRef(resourceType string) bool { switch resourceType { - case agnetResourceGit, agnetResourceSK, agnetResourceCloudAccount, agnetResourceCloudResource: + case agentResourceGit, agentResourceSK, agentResourceCloudAccount, agentResourceCloudResource: return true default: return false } } -func agnetRuntimePartiallySet(r agnetRuntimeExecution) bool { +func agentRuntimePartiallySet(r agentRuntimeExecution) bool { return strings.TrimSpace(r.ProfileID) != "" || len(r.CloudPrincipalRefs) > 0 || strings.TrimSpace(r.NetworkPolicyRef) != "" } -func validateAgentRuntimeBindings(c *gin.Context, agent agnetAgentPlan) bool { +func validateAgentRuntimeBindings(c *gin.Context, agent agentAgentPlan) bool { r := agent.RuntimeExecution - if !agnetRuntimePartiallySet(r) { + if !agentRuntimePartiallySet(r) { return true } if strings.TrimSpace(r.ProfileID) == "" { - agnetError(c, "RUNTIME_BINDING_INVALID", "runtime_execution.profile_id is required when runtime bindings are present") + agentError(c, "RUNTIME_BINDING_INVALID", "runtime_execution.profile_id is required when runtime bindings are present") return false } return true } -func skSourceDisplayRef(s agnetSKSource) string { +func skSourceDisplayRef(s agentSKSource) string { switch strings.TrimSpace(s.Type) { case "git": r := s.RepoRef @@ -603,7 +608,7 @@ func skSourceDisplayRef(s agnetSKSource) string { } } -func validateSKSourceEntry(c *gin.Context, source agnetSKSource) bool { +func validateSKSourceEntry(c *gin.Context, source agentSKSource) bool { sourceType := strings.TrimSpace(source.Type) if sourceType == "" { return true @@ -612,70 +617,70 @@ func validateSKSourceEntry(c *gin.Context, source agnetSKSource) bool { case "git": ref := source.RepoRef if strings.TrimSpace(ref.Ref) == "" { - agnetError(c, "SK_SOURCE_UNRESOLVABLE", "git sk_sources.repo_ref.ref is required") + agentError(c, "SK_SOURCE_UNRESOLVABLE", "git sk_sources.repo_ref.ref is required") return false } if len(ref.Paths) == 0 { - agnetError(c, "SK_SOURCE_UNRESOLVABLE", "git sk_sources.repo_ref.paths must not be empty") + agentError(c, "SK_SOURCE_UNRESOLVABLE", "git sk_sources.repo_ref.paths must not be empty") return false } case "upload": if strings.TrimSpace(source.ArtifactID) == "" { - agnetError(c, "SK_SOURCE_UNRESOLVABLE", "upload sk_sources.artifact_id is required") + agentError(c, "SK_SOURCE_UNRESOLVABLE", "upload sk_sources.artifact_id is required") return false } default: - agnetError(c, "SK_SOURCE_UNRESOLVABLE", "unsupported sk source type") + agentError(c, "SK_SOURCE_UNRESOLVABLE", "unsupported sk source type") return false } return true } -func validateAgentSKAccessPolicy(c *gin.Context, agent agnetAgentPlan) bool { +func validateAgentSKAccessPolicy(c *gin.Context, agent agentAgentPlan) bool { p := agent.SKAccessPolicy hasDeny := len(p.DenySkillIDs) > 0 if !hasDeny { return true } if strings.TrimSpace(p.PolicyRef) == "" && !p.InheritDeploymentDefaults { - agnetError(c, "SK_POLICY_REJECTED", "sk_access_policy.policy_ref or inherit_deployment_defaults is required when deny_skill_ids set") + agentError(c, "SK_POLICY_REJECTED", "sk_access_policy.policy_ref or inherit_deployment_defaults is required when deny_skill_ids set") return false } return true } -func validateBillingContext(c *gin.Context, plan agnetOrchestrationPlan) bool { +func validateBillingContext(c *gin.Context, plan agentOrchestrationPlan) bool { billing := plan.BillingContext provider := strings.TrimSpace(strings.ToLower(billing.Provider)) if provider == "" { return true } if provider != "newapi" { - agnetError(c, "BILLING_CONTEXT_INVALID", "billing_context.provider must be newapi when set") + agentError(c, "BILLING_CONTEXT_INVALID", "billing_context.provider must be newapi when set") return false } if strings.TrimSpace(plan.UserContext.ChannelID) == "" && strings.TrimSpace(billing.NewAPIUserRef) == "" && strings.TrimSpace(billing.NewAPIGroup) == "" && strings.TrimSpace(billing.QuotaRef) == "" { - agnetError(c, "BILLING_CONTEXT_INVALID", "newapi billing_context requires channel_id, newapi_user_ref, newapi_group, or quota_ref") + agentError(c, "BILLING_CONTEXT_INVALID", "newapi billing_context requires channel_id, newapi_user_ref, newapi_group, or quota_ref") return false } return true } -func validateAgentRuntimeContext(c *gin.Context, plan agnetOrchestrationPlan) bool { +func validateAgentRuntimeContext(c *gin.Context, plan agentOrchestrationPlan) bool { runtime := plan.AgentRuntime platform := strings.TrimSpace(strings.ToLower(runtime.Platform)) if platform == "" && len(runtime.Agents) == 0 { return true } - if platform != "agnet" { - agnetError(c, "AGENT_RUNTIME_INVALID", "agent_runtime.platform must be agnet when runtime context is present") + if platform != "agent" { + agentError(c, "AGENT_RUNTIME_INVALID", "agent_runtime.platform must be agent when runtime context is present") return false } if len(runtime.Agents) == 0 { - agnetError(c, "AGENT_RUNTIME_INVALID", "agent_runtime.agents must not be empty when runtime context is present") + agentError(c, "AGENT_RUNTIME_INVALID", "agent_runtime.agents must not be empty when runtime context is present") return false } @@ -689,29 +694,29 @@ func validateAgentRuntimeContext(c *gin.Context, plan agnetOrchestrationPlan) bo for _, agent := range runtime.Agents { role := strings.TrimSpace(agent.Role) if role == "" || strings.TrimSpace(agent.ModelRef) == "" || agent.InstanceCount <= 0 { - agnetError(c, "AGENT_RUNTIME_INVALID", "agent_runtime agents require role/model_ref/positive instance_count") + agentError(c, "AGENT_RUNTIME_INVALID", "agent_runtime agents require role/model_ref/positive instance_count") return false } if len(knownRoles) > 0 && !knownRoles[role] { - agnetError(c, "AGENT_RUNTIME_INVALID", "agent_runtime agent role must match an orchestration agent role") + agentError(c, "AGENT_RUNTIME_INVALID", "agent_runtime agent role must match an orchestration agent role") return false } } return true } -func validateResourceGrant(c *gin.Context, plan agnetOrchestrationPlan, agent agnetAgentPlan, grant agnetResourceGrant) bool { +func validateResourceGrant(c *gin.Context, plan agentOrchestrationPlan, agent agentAgentPlan, grant agentResourceGrant) bool { resourceType := strings.TrimSpace(grant.ResourceType) switch resourceType { - case agnetResourceGit, agnetResourceSK, agnetResourceProjectDoc, agnetResourceCloudAccount, agnetResourceCloudResource: + case agentResourceGit, agentResourceSK, agentResourceProjectDoc, agentResourceCloudAccount, agentResourceCloudResource: default: - agnetError(c, "RESOURCE_GRANT_INVALID", "resource_grants.resource_type must be git/sk/project_doc/cloud_account/cloud_resource") + agentError(c, "RESOURCE_GRANT_INVALID", "resource_grants.resource_type must be git/sk/project_doc/cloud_account/cloud_resource") return false } if strings.TrimSpace(grant.GrantID) == "" || strings.TrimSpace(grant.ResourceID) == "" || strings.TrimSpace(grant.TargetRole) == "" || strings.TrimSpace(grant.TargetAgentRef) == "" { - agnetError(c, "RESOURCE_GRANT_INVALID", "resource_grants require grant_id/resource_id/target_role/target_agent_ref") + agentError(c, "RESOURCE_GRANT_INVALID", "resource_grants require grant_id/resource_id/target_role/target_agent_ref") return false } @@ -719,77 +724,77 @@ func validateResourceGrant(c *gin.Context, plan agnetOrchestrationPlan, agent ag planUserID := strings.TrimSpace(plan.UserContext.UserID) bindingScope := strings.TrimSpace(grant.BindingScope) if bindingScope == "" { - agnetError(c, "RESOURCE_GRANT_INVALID", "resource_grants.binding_scope is required") + agentError(c, "RESOURCE_GRANT_INVALID", "resource_grants.binding_scope is required") return false } if planUserID != "" && grantUserID != "" && grantUserID != planUserID { - agnetError(c, "RESOURCE_GRANT_INVALID", "resource_grants.user_id must match orchestration user_context.user_id") + agentError(c, "RESOURCE_GRANT_INVALID", "resource_grants.user_id must match orchestration user_context.user_id") return false } if strings.TrimSpace(grant.TargetRole) != strings.TrimSpace(agent.RoleTemplate) { - agnetError(c, "RESOURCE_GRANT_INVALID", "resource_grants.target_role must match the assigned agent role") + agentError(c, "RESOURCE_GRANT_INVALID", "resource_grants.target_role must match the assigned agent role") return false } if len(grant.PermissionScope) == 0 { - agnetError(c, "RESOURCE_GRANT_INVALID", "resource_grants.permission_scope must not be empty") + agentError(c, "RESOURCE_GRANT_INVALID", "resource_grants.permission_scope must not be empty") return false } switch strings.TrimSpace(grant.Status) { - case agnetGrantStatusPending, agnetGrantStatusActive, agnetGrantStatusDisabled, agnetGrantStatusRevoked: + case agentGrantStatusPending, agentGrantStatusActive, agentGrantStatusDisabled, agentGrantStatusRevoked: default: - agnetError(c, "RESOURCE_GRANT_INVALID", "resource_grants.status must be pending/active/disabled/revoked") + agentError(c, "RESOURCE_GRANT_INVALID", "resource_grants.status must be pending/active/disabled/revoked") return false } - if agnetResourceTypeNeedsSecretRef(resourceType) && strings.TrimSpace(grant.SecretRef) == "" { - agnetError(c, "RESOURCE_GRANT_SECRET_REF_REQUIRED", "resource_grants.secret_ref is required for credential-backed resources") + if agentResourceTypeNeedsSecretRef(resourceType) && strings.TrimSpace(grant.SecretRef) == "" { + agentError(c, "RESOURCE_GRANT_SECRET_REF_REQUIRED", "resource_grants.secret_ref is required for credential-backed resources") return false } if strings.TrimSpace(grant.SecretRef) != "" && !strings.HasPrefix(strings.TrimSpace(grant.SecretRef), "azkv://") { - agnetError(c, "SECRET_REF_INVALID", "resource_grants.secret_ref must use azkv:// Azure Key Vault reference") + agentError(c, "SECRET_REF_INVALID", "resource_grants.secret_ref must use azkv:// Azure Key Vault reference") return false } if containsSensitiveGrantField(grant.Metadata) || containsSensitiveGrantField(grant.Constraints) || containsSensitiveGrantField(grant.Audit) { - agnetError(c, "RESOURCE_GRANT_SECRET_REJECTED", "resource_grants metadata/constraints/audit must not contain plaintext credential fields") + agentError(c, "RESOURCE_GRANT_SECRET_REJECTED", "resource_grants metadata/constraints/audit must not contain plaintext credential fields") return false } return true } -func validateOrchestrationPlan(c *gin.Context, plan agnetOrchestrationPlan) bool { +func validateOrchestrationPlan(c *gin.Context, plan agentOrchestrationPlan) bool { if strings.TrimSpace(plan.IntentID) == "" || strings.TrimSpace(plan.TemplateHint) == "" || strings.TrimSpace(plan.Objective) == "" { - agnetError(c, "POLICY_REJECTED", "intent_id/template_hint/objective is required") + agentError(c, "POLICY_REJECTED", "intent_id/template_hint/objective is required") return false } if len(plan.Agents) == 0 { - agnetError(c, "POLICY_REJECTED", "at least one agent is required") + agentError(c, "POLICY_REJECTED", "at least one agent is required") return false } if strings.TrimSpace(plan.UserContext.UserID) == "" { - agnetError(c, "POLICY_REJECTED", "user_context.user_id is required") + agentError(c, "POLICY_REJECTED", "user_context.user_id is required") return false } if strings.TrimSpace(plan.Metadata.CorrelationID) == "" { - agnetError(c, "POLICY_REJECTED", "metadata.correlation_id is required") + agentError(c, "POLICY_REJECTED", "metadata.correlation_id is required") return false } switch plan.RiskLevel { - case agnetRiskLow, agnetRiskMedium, agnetRiskHigh: + case agentRiskLow, agentRiskMedium, agentRiskHigh: default: - agnetError(c, "POLICY_REJECTED", "risk_level must be low/medium/high") + agentError(c, "POLICY_REJECTED", "risk_level must be low/medium/high") return false } - if !isValidAgnetSubMode(plan.SubMode) { - agnetError(c, "POLICY_REJECTED", "sub_mode must be agile or waterfall") + if !isValidAgentSubMode(plan.SubMode) { + agentError(c, "POLICY_REJECTED", "sub_mode must be agile or waterfall") return false } if plan.Budget.MaxTokens <= 0 || plan.Budget.MaxCostUSD <= 0 || plan.Budget.MaxDurationSec <= 0 { - agnetError(c, "POLICY_REJECTED", "budget.max_tokens/max_cost_usd/max_duration_sec must be positive") + agentError(c, "POLICY_REJECTED", "budget.max_tokens/max_cost_usd/max_duration_sec must be positive") return false } if plan.Budget.MaxTokens > 500000 || plan.Budget.MaxCostUSD > 200 || plan.Budget.MaxDurationSec > 24*3600 { - agnetError(c, "BUDGET_EXCEEDED", "budget exceeds current platform policy limits") + agentError(c, "BUDGET_EXCEEDED", "budget exceeds current platform policy limits") return false } if !validateBillingContext(c, plan) { @@ -802,7 +807,7 @@ func validateOrchestrationPlan(c *gin.Context, plan agnetOrchestrationPlan) bool allowedModels := plan.Constraints.AllowedModelIDs for _, agent := range plan.Agents { if strings.TrimSpace(agent.RoleTemplate) == "" || strings.TrimSpace(agent.Goal) == "" { - agnetError(c, "POLICY_REJECTED", "each agent must contain role_template and goal") + agentError(c, "POLICY_REJECTED", "each agent must contain role_template and goal") return false } for _, source := range agent.SKSources { @@ -812,7 +817,7 @@ func validateOrchestrationPlan(c *gin.Context, plan agnetOrchestrationPlan) bool } modelID := strings.TrimSpace(agent.DefaultModelID) if modelID != "" && len(allowedModels) > 0 && !containsString(allowedModels, modelID) { - agnetError(c, "MODEL_NOT_ALLOWED", "agent default_model_id is outside allowed_model_ids") + agentError(c, "MODEL_NOT_ALLOWED", "agent default_model_id is outside allowed_model_ids") return false } if !validateAgentRuntimeBindings(c, agent) { @@ -827,14 +832,14 @@ func validateOrchestrationPlan(c *gin.Context, plan agnetOrchestrationPlan) bool } } } - agentsByRole := make(map[string]agnetAgentPlan, len(plan.Agents)) + agentsByRole := make(map[string]agentAgentPlan, len(plan.Agents)) for _, agent := range plan.Agents { agentsByRole[strings.TrimSpace(agent.RoleTemplate)] = agent } for _, grant := range plan.ResourceGrants { agent, ok := agentsByRole[strings.TrimSpace(grant.TargetRole)] if !ok { - agnetError(c, "RESOURCE_GRANT_INVALID", "top-level resource_grants.target_role must match an orchestration agent role") + agentError(c, "RESOURCE_GRANT_INVALID", "top-level resource_grants.target_role must match an orchestration agent role") return false } if !validateResourceGrant(c, plan, agent, grant) { @@ -845,7 +850,7 @@ func validateOrchestrationPlan(c *gin.Context, plan agnetOrchestrationPlan) bool return true } -func firstPlanBindingScope(plan agnetOrchestrationPlan) string { +func firstPlanBindingScope(plan agentOrchestrationPlan) string { for _, grant := range plan.ResourceGrants { if scope := strings.TrimSpace(grant.BindingScope); scope != "" { return scope @@ -861,7 +866,7 @@ func firstPlanBindingScope(plan agnetOrchestrationPlan) string { return "" } -func agnetGrantResourceRef(grant agnetResourceGrant) string { +func agentGrantResourceRef(grant agentResourceGrant) string { for _, value := range []string{grant.Metadata["repo_url"], grant.Metadata["doc_ref"], grant.Metadata["resource_ref"], grant.BindingScope, grant.ResourceID} { if strings.TrimSpace(value) != "" { return value @@ -870,8 +875,8 @@ func agnetGrantResourceRef(grant agnetResourceGrant) string { return grant.GrantID } -func appendAgnetManifestGrant(manifest *agnetPermissionManifest, grant agnetResourceGrant) { - if manifest == nil || strings.TrimSpace(grant.Status) != agnetGrantStatusActive { +func appendAgentManifestGrant(manifest *agentPermissionManifest, grant agentResourceGrant) { + if manifest == nil || strings.TrimSpace(grant.Status) != agentGrantStatusActive { return } if manifest.AgentRole == "" { @@ -880,11 +885,11 @@ func appendAgnetManifestGrant(manifest *agnetPermissionManifest, grant agnetReso if manifest.TargetAgentRef == "" { manifest.TargetAgentRef = grant.TargetAgentRef } - manifest.ResourceGrants = append(manifest.ResourceGrants, agnetManifestGrant{ + manifest.ResourceGrants = append(manifest.ResourceGrants, agentManifestGrant{ GrantID: grant.GrantID, ResourceID: grant.ResourceID, ResourceType: grant.ResourceType, - ResourceRef: agnetGrantResourceRef(grant), + ResourceRef: agentGrantResourceRef(grant), AllowedActions: append([]string{}, grant.PermissionScope...), Constraints: grant.Constraints, SecretRef: grant.SecretRef, @@ -892,27 +897,27 @@ func appendAgnetManifestGrant(manifest *agnetPermissionManifest, grant agnetReso }) } -func buildAgnetPermissionManifest(plan agnetOrchestrationPlan) agnetPermissionManifest { - manifest := agnetPermissionManifest{ +func buildAgentPermissionManifest(plan agentOrchestrationPlan) agentPermissionManifest { + manifest := agentPermissionManifest{ UserID: plan.UserContext.UserID, BindingScope: firstPlanBindingScope(plan), } for _, grant := range plan.ResourceGrants { - appendAgnetManifestGrant(&manifest, grant) + appendAgentManifestGrant(&manifest, grant) } for _, agent := range plan.Agents { for _, grant := range agent.ResourceGrants { - appendAgnetManifestGrant(&manifest, grant) + appendAgentManifestGrant(&manifest, grant) } } if manifest.ResourceGrants == nil { - manifest.ResourceGrants = []agnetManifestGrant{} + manifest.ResourceGrants = []agentManifestGrant{} } return manifest } -func buildAgnetAgentInstances(plan agnetOrchestrationPlan, phase string, runtimeState string) []agnetAgentInstance { - instances := make([]agnetAgentInstance, 0) +func buildAgentAgentInstances(plan agentOrchestrationPlan, phase string, runtimeState string) []agentAgentInstance { + instances := make([]agentAgentInstance, 0) if len(plan.AgentRuntime.Agents) > 0 { for _, runtimeAgent := range plan.AgentRuntime.Agents { count := runtimeAgent.InstanceCount @@ -920,7 +925,7 @@ func buildAgnetAgentInstances(plan agnetOrchestrationPlan, phase string, runtime count = 1 } for i := 0; i < count; i++ { - instances = append(instances, agnetAgentInstance{ + instances = append(instances, agentAgentInstance{ InstanceID: "agi_" + common.GetUUID()[:12], Role: runtimeAgent.Role, Phase: phase, @@ -931,7 +936,7 @@ func buildAgnetAgentInstances(plan agnetOrchestrationPlan, phase string, runtime } if len(instances) == 0 { for _, agent := range plan.Agents { - instances = append(instances, agnetAgentInstance{ + instances = append(instances, agentAgentInstance{ InstanceID: "agi_" + common.GetUUID()[:12], Role: agent.RoleTemplate, Phase: phase, @@ -942,7 +947,7 @@ func buildAgnetAgentInstances(plan agnetOrchestrationPlan, phase string, runtime return instances } -func planHasBindingScope(plan agnetOrchestrationPlan, bindingScope string) bool { +func planHasBindingScope(plan agentOrchestrationPlan, bindingScope string) bool { bindingScope = strings.TrimSpace(bindingScope) if bindingScope == "" { return true @@ -962,7 +967,7 @@ func planHasBindingScope(plan agnetOrchestrationPlan, bindingScope string) bool return false } -func applyAuthenticatedManagerUserContext(c *gin.Context, plan *agnetOrchestrationPlan) { +func applyAuthenticatedManagerUserContext(c *gin.Context, plan *agentOrchestrationPlan) { if plan == nil { return } @@ -980,18 +985,18 @@ func applyAuthenticatedManagerUserContext(c *gin.Context, plan *agnetOrchestrati } } -func enforceAuthenticatedAgnetUserContext(c *gin.Context, plan *agnetOrchestrationPlan) bool { +func enforceAuthenticatedAgentUserContext(c *gin.Context, plan *agentOrchestrationPlan) bool { if plan == nil { return false } authenticatedUserID := c.GetInt("id") if authenticatedUserID <= 0 { - agnetError(c, "POLICY_REJECTED", "authenticated user is required") + agentError(c, "POLICY_REJECTED", "authenticated user is required") return false } userID := strconv.Itoa(authenticatedUserID) if requestedUserID := strings.TrimSpace(plan.UserContext.UserID); requestedUserID != "" && requestedUserID != userID { - agnetError(c, "USER_CONTEXT_FORBIDDEN", "user_context.user_id must match authenticated user") + agentError(c, "USER_CONTEXT_FORBIDDEN", "user_context.user_id must match authenticated user") return false } plan.UserContext.UserID = userID @@ -1006,7 +1011,7 @@ func enforceAuthenticatedAgnetUserContext(c *gin.Context, plan *agnetOrchestrati for grantIdx := range plan.Agents[agentIdx].ResourceGrants { grantUserID := strings.TrimSpace(plan.Agents[agentIdx].ResourceGrants[grantIdx].UserID) if grantUserID != "" && grantUserID != userID { - agnetError(c, "RESOURCE_GRANT_FORBIDDEN", "resource_grants.user_id must match authenticated user") + agentError(c, "RESOURCE_GRANT_FORBIDDEN", "resource_grants.user_id must match authenticated user") return false } plan.Agents[agentIdx].ResourceGrants[grantIdx].UserID = userID @@ -1015,7 +1020,7 @@ func enforceAuthenticatedAgnetUserContext(c *gin.Context, plan *agnetOrchestrati for grantIdx := range plan.ResourceGrants { grantUserID := strings.TrimSpace(plan.ResourceGrants[grantIdx].UserID) if grantUserID != "" && grantUserID != userID { - agnetError(c, "RESOURCE_GRANT_FORBIDDEN", "resource_grants.user_id must match authenticated user") + agentError(c, "RESOURCE_GRANT_FORBIDDEN", "resource_grants.user_id must match authenticated user") return false } plan.ResourceGrants[grantIdx].UserID = userID @@ -1023,70 +1028,70 @@ func enforceAuthenticatedAgnetUserContext(c *gin.Context, plan *agnetOrchestrati return true } -func agnetDeploymentBelongsToAuthenticatedUser(c *gin.Context, record agnetDeploymentRecord) bool { +func agentDeploymentBelongsToAuthenticatedUser(c *gin.Context, record agentDeploymentRecord) bool { userID := strconv.Itoa(c.GetInt("id")) return userID != "0" && strings.TrimSpace(record.Plan.UserContext.UserID) == userID } -func requireAuthenticatedUserAgnetDeployment(c *gin.Context) (agnetDeploymentRecord, bool) { +func requireAuthenticatedUserAgentDeployment(c *gin.Context) (agentDeploymentRecord, bool) { deploymentID := strings.TrimSpace(c.Param("deployment_id")) if deploymentID == "" { - agnetError(c, "POLICY_REJECTED", "deployment_id is required") - return agnetDeploymentRecord{}, false + agentError(c, "POLICY_REJECTED", "deployment_id is required") + return agentDeploymentRecord{}, false } - record, ok := findAgnetDeploymentRecord(deploymentID) + record, ok := findAgentDeploymentRecord(deploymentID) if !ok { - agnetError(c, "DEPLOYMENT_CONFLICT", "deployment not found") - return agnetDeploymentRecord{}, false + agentError(c, "DEPLOYMENT_CONFLICT", "deployment not found") + return agentDeploymentRecord{}, false } - if !agnetDeploymentBelongsToAuthenticatedUser(c, record) { - agnetError(c, "DEPLOYMENT_FORBIDDEN", "deployment does not belong to authenticated user") - return agnetDeploymentRecord{}, false + if !agentDeploymentBelongsToAuthenticatedUser(c, record) { + agentError(c, "DEPLOYMENT_FORBIDDEN", "deployment does not belong to authenticated user") + return agentDeploymentRecord{}, false } return record, true } -func createAgnetDeploymentRecord(c *gin.Context, enforceUserScope bool, runtimeMode ...string) (agnetDeploymentRecord, bool) { - var req agnetDeploymentRequest +func createAgentDeploymentRecord(c *gin.Context, enforceUserScope bool, runtimeMode ...string) (agentDeploymentRecord, bool) { + var req agentDeploymentRequest if err := c.ShouldBindJSON(&req); err != nil { - agnetError(c, "POLICY_REJECTED", err.Error()) - return agnetDeploymentRecord{}, false + agentError(c, "POLICY_REJECTED", err.Error()) + return agentDeploymentRecord{}, false } plan := req.Plan if enforceUserScope { - if !enforceAuthenticatedAgnetUserContext(c, &plan) { - return agnetDeploymentRecord{}, false + if !enforceAuthenticatedAgentUserContext(c, &plan) { + return agentDeploymentRecord{}, false } } else { applyAuthenticatedManagerUserContext(c, &plan) } if !validateOrchestrationPlan(c, plan) { - return agnetDeploymentRecord{}, false + return agentDeploymentRecord{}, false } - plan.SubMode = normalizeAgnetSubMode(plan.SubMode) + plan.SubMode = normalizeAgentSubMode(plan.SubMode) if len(runtimeMode) > 0 && strings.TrimSpace(plan.Metadata.RuntimeMode) == "" { - plan.Metadata.RuntimeMode = normalizeAgnetRuntimeMode(runtimeMode[0]) + plan.Metadata.RuntimeMode = normalizeAgentRuntimeMode(runtimeMode[0]) } if strings.TrimSpace(plan.Metadata.RuntimeMode) == "" { - plan.Metadata.RuntimeMode = agnetRuntimeModeAgnet + plan.Metadata.RuntimeMode = agentRuntimeModeAgent } - now := agnetNow() + now := agentNow() deploymentID := "dep_" + common.GetUUID()[:12] - record := agnetDeploymentRecord{ + record := agentDeploymentRecord{ DeploymentID: deploymentID, SubMode: plan.SubMode, Status: "accepted", Phase: "pending", RuntimeState: "queued", FailureReason: "", - AgentInstances: buildAgnetAgentInstances(plan, "pending", "queued"), - ResourceGrantManifest: buildAgnetPermissionManifest(plan), + AgentInstances: buildAgentAgentInstances(plan, "pending", "queued"), + ResourceGrantManifest: buildAgentPermissionManifest(plan), CreatedAt: now, UpdatedAt: now, Plan: plan, } - event := agnetEvent{ + event := agentEvent{ EventID: "evt_" + common.GetUUID()[:12], Event: "deployment.accepted", SchemaVersion: 1, @@ -1098,19 +1103,19 @@ func createAgnetDeploymentRecord(c *gin.Context, enforceUserScope bool, runtimeM OccurredAt: now, } - if err := persistAgnetDeploymentRecord(record, agnetDeploymentRequest{Plan: plan}); err != nil { - common.SysLog("AgnetCreateDeployment persist: " + err.Error()) - agnetError(c, "DEPLOYMENT_PERSIST_FAILED", "failed to persist Agnet deployment") - return agnetDeploymentRecord{}, false + if err := persistAgentDeploymentRecord(record, agentDeploymentRequest{Plan: plan}); err != nil { + common.SysLog("AgentCreateDeployment persist: " + err.Error()) + agentError(c, "DEPLOYMENT_PERSIST_FAILED", "failed to persist Agent deployment") + return agentDeploymentRecord{}, false } - agnetMu.Lock() - agnetDeployments[deploymentID] = record - agnetMu.Unlock() - recordAgnetAuditEvent(event, "agnet_control_plane", deploymentID, agnetRequestID(c), "ok") + agentMu.Lock() + agentDeployments[deploymentID] = record + agentMu.Unlock() + recordAgentAuditEvent(event, "agent_control_plane", deploymentID, agentRequestID(c), "ok") return record, true } -func writeAgnetDeploymentCreateSuccess(c *gin.Context, record agnetDeploymentRecord, extra gin.H) { +func writeAgentDeploymentCreateSuccess(c *gin.Context, record agentDeploymentRecord, extra gin.H) { data := gin.H{ "deployment_id": record.DeploymentID, "sub_mode": record.SubMode, @@ -1130,62 +1135,62 @@ func writeAgnetDeploymentCreateSuccess(c *gin.Context, record agnetDeploymentRec common.ApiSuccess(c, data) } -func createAgnetDeployment(c *gin.Context, enforceUserScope bool) { - record, ok := createAgnetDeploymentRecord(c, enforceUserScope, agnetRuntimeModeAgnet) +func createAgentDeployment(c *gin.Context, enforceUserScope bool) { + record, ok := createAgentDeploymentRecord(c, enforceUserScope, agentRuntimeModeAgent) if !ok { return } - record = maybeDispatchAgnetRuntimeCreate(c, record, "agnet_deployments") - writeAgnetDeploymentCreateSuccess(c, record, nil) + record = maybeDispatchAgentRuntimeCreate(c, record, "agent_deployments") + writeAgentDeploymentCreateSuccess(c, record, nil) } -func AgnetCreateUserSwarm(c *gin.Context) { - record, ok := createAgnetDeploymentRecord(c, true, agnetRuntimeModeSwarm) +func AgentCreateUserSwarm(c *gin.Context) { + record, ok := createAgentDeploymentRecord(c, true, agentRuntimeModeSwarm) if !ok { return } - record = maybeDispatchAgnetRuntimeCreate(c, record, "api_swarms_adapter") - writeAgnetDeploymentCreateSuccess(c, record, gin.H{ + record = maybeDispatchAgentRuntimeCreate(c, record, "api_swarms_adapter") + writeAgentDeploymentCreateSuccess(c, record, gin.H{ "swarm_id": firstNonEmpty(record.RuntimeSwarmID, record.DeploymentID), "adapter": "manager-local-control-plane", "source": "api_swarms_adapter", }) } -func AgnetCreateDeployment(c *gin.Context) { - createAgnetDeployment(c, false) +func AgentCreateDeployment(c *gin.Context) { + createAgentDeployment(c, false) } -func AgnetCreateUserDeployment(c *gin.Context) { - createAgnetDeployment(c, true) +func AgentCreateUserDeployment(c *gin.Context) { + createAgentDeployment(c, true) } -func AgnetGetDeployment(c *gin.Context) { +func AgentGetDeployment(c *gin.Context) { deploymentID := strings.TrimSpace(c.Param("deployment_id")) if deploymentID == "" { - agnetError(c, "POLICY_REJECTED", "deployment_id is required") + agentError(c, "POLICY_REJECTED", "deployment_id is required") return } - record, ok := findAgnetDeploymentRecord(deploymentID) + record, ok := findAgentDeploymentRecord(deploymentID) if !ok { - agnetError(c, "DEPLOYMENT_CONFLICT", "deployment not found") + agentError(c, "DEPLOYMENT_CONFLICT", "deployment not found") return } - common.ApiSuccess(c, record) + common.ApiSuccess(c, withDisplayStatus(record)) } -func AgnetGetUserDeployment(c *gin.Context) { - record, ok := requireAuthenticatedUserAgnetDeployment(c) +func AgentGetUserDeployment(c *gin.Context) { + record, ok := requireAuthenticatedUserAgentDeployment(c) if !ok { return } - common.ApiSuccess(c, record) + common.ApiSuccess(c, withDisplayStatus(record)) } -func AgnetListDeployments(c *gin.Context) { +func AgentListDeployments(c *gin.Context) { userID := strings.TrimSpace(c.Query("user_id")) bindingScope := strings.TrimSpace(c.Query("binding_scope")) - items := listAgnetDeploymentRecords(userID, bindingScope) + items := listAgentDeploymentRecords(userID, bindingScope) common.ApiSuccess(c, gin.H{ "items": items, @@ -1193,26 +1198,26 @@ func AgnetListDeployments(c *gin.Context) { }) } -func AgnetListUserDeployments(c *gin.Context) { +func AgentListUserDeployments(c *gin.Context) { userID := strconv.Itoa(c.GetInt("id")) bindingScope := strings.TrimSpace(c.Query("binding_scope")) - items := listAgnetDeploymentRecords(userID, bindingScope) + items := listAgentDeploymentRecords(userID, bindingScope) common.ApiSuccess(c, gin.H{ "items": items, "total": len(items), }) } -func AgnetStopDeployment(c *gin.Context) { +func AgentStopDeployment(c *gin.Context) { deploymentID := strings.TrimSpace(c.Param("deployment_id")) if deploymentID == "" { - agnetError(c, "POLICY_REJECTED", "deployment_id is required") + agentError(c, "POLICY_REJECTED", "deployment_id is required") return } - record, ok := findAgnetDeploymentRecord(deploymentID) + record, ok := findAgentDeploymentRecord(deploymentID) if !ok { - agnetError(c, "DEPLOYMENT_CONFLICT", "deployment not found") + agentError(c, "DEPLOYMENT_CONFLICT", "deployment not found") return } var stopPayload struct { @@ -1222,7 +1227,7 @@ func AgnetStopDeployment(c *gin.Context) { _ = common.UnmarshalBodyReusable(c, &stopPayload) } var runtimeOK bool - record, runtimeOK = syncAgnetRuntimeStop(c, record, stopPayload.Reason) + record, runtimeOK = syncAgentRuntimeStop(c, record, stopPayload.Reason) if !runtimeOK { return } @@ -1235,16 +1240,16 @@ func AgnetStopDeployment(c *gin.Context) { record.AgentInstances[i].RuntimeState = "stopped" record.AgentInstances[i].FailureReason = "" } - record.UpdatedAt = agnetNow() - agnetMu.Lock() - agnetDeployments[deploymentID] = record - agnetMu.Unlock() - if err := updateAgnetDeploymentRecord(record); err != nil { - common.SysLog("AgnetStopDeployment persist: " + err.Error()) - agnetError(c, "DEPLOYMENT_PERSIST_FAILED", "failed to persist Agnet deployment") + record.UpdatedAt = agentNow() + agentMu.Lock() + agentDeployments[deploymentID] = record + agentMu.Unlock() + if err := updateAgentDeploymentRecord(record); err != nil { + common.SysLog("AgentStopDeployment persist: " + err.Error()) + agentError(c, "DEPLOYMENT_PERSIST_FAILED", "failed to persist Agent deployment") return } - stopEvent := agnetEvent{ + stopEvent := agentEvent{ EventID: "evt_" + common.GetUUID()[:12], Event: "instance.phase_changed", SchemaVersion: 1, @@ -1253,9 +1258,9 @@ func AgnetStopDeployment(c *gin.Context) { BindingScope: firstPlanBindingScope(record.Plan), DeploymentID: deploymentID, CorrelationID: record.Plan.Metadata.CorrelationID, - OccurredAt: agnetNow(), + OccurredAt: agentNow(), } - recordAgnetAuditEvent(stopEvent, "agnet_control_plane", deploymentID, agnetRequestID(c), "ok") + recordAgentAuditEvent(stopEvent, "agent_control_plane", deploymentID, agentRequestID(c), "ok") common.ApiSuccess(c, gin.H{ "deployment_id": deploymentID, @@ -1265,29 +1270,29 @@ func AgnetStopDeployment(c *gin.Context) { }) } -func AgnetStopUserDeployment(c *gin.Context) { - if _, ok := requireAuthenticatedUserAgnetDeployment(c); !ok { +func AgentStopUserDeployment(c *gin.Context) { + if _, ok := requireAuthenticatedUserAgentDeployment(c); !ok { return } - AgnetStopDeployment(c) + AgentStopDeployment(c) } -func AgnetListDeploymentEvents(c *gin.Context) { +func AgentListDeploymentEvents(c *gin.Context) { deploymentID := strings.TrimSpace(c.Param("deployment_id")) if deploymentID == "" { - agnetError(c, "POLICY_REJECTED", "deployment_id is required") + agentError(c, "POLICY_REJECTED", "deployment_id is required") return } // Audit rows are now in PostgreSQL/SQLite/MySQL — survive restarts. - rows, err := model.ListAgnetAuditEventsByDeployment(deploymentID) + rows, err := model.ListAgentAuditEventsByDeployment(deploymentID) if err != nil { - common.SysLog("AgnetListDeploymentEvents: " + err.Error()) - agnetError(c, "POLICY_REJECTED", "internal error") + common.SysLog("AgentListDeploymentEvents: " + err.Error()) + agentError(c, "POLICY_REJECTED", "internal error") return } - events := make([]agnetEvent, 0, len(rows)) + events := make([]agentEvent, 0, len(rows)) for _, r := range rows { - events = append(events, agnetAuditRowToEvent(r)) + events = append(events, agentAuditRowToEvent(r)) } common.ApiSuccess(c, gin.H{ "items": events, @@ -1295,14 +1300,14 @@ func AgnetListDeploymentEvents(c *gin.Context) { }) } -func AgnetListUserDeploymentEvents(c *gin.Context) { - if _, ok := requireAuthenticatedUserAgnetDeployment(c); !ok { +func AgentListUserDeploymentEvents(c *gin.Context) { + if _, ok := requireAuthenticatedUserAgentDeployment(c); !ok { return } - AgnetListDeploymentEvents(c) + AgentListDeploymentEvents(c) } -func normalizeAgnetSimulationEvents(values []string) []string { +func normalizeAgentSimulationEvents(values []string) []string { events := make([]string, 0, len(values)) for _, value := range values { event := strings.TrimSpace(value) @@ -1324,7 +1329,7 @@ func normalizeAgnetSimulationEvents(values []string) []string { return events } -func firstAgnetSimulationGrant(record agnetDeploymentRecord) agnetResourceGrant { +func firstAgentSimulationGrant(record agentDeploymentRecord) agentResourceGrant { for _, agent := range record.Plan.Agents { for _, grant := range agent.ResourceGrants { if strings.TrimSpace(grant.SecretRef) != "" { @@ -1345,10 +1350,10 @@ func firstAgnetSimulationGrant(record agnetDeploymentRecord) agnetResourceGrant if len(record.Plan.ResourceGrants) > 0 { return record.Plan.ResourceGrants[0] } - return agnetResourceGrant{} + return agentResourceGrant{} } -func firstAgnetSimulationRole(record agnetDeploymentRecord) string { +func firstAgentSimulationRole(record agentDeploymentRecord) string { for _, agent := range record.Plan.Agents { if strings.TrimSpace(agent.RoleTemplate) != "" { return strings.TrimSpace(agent.RoleTemplate) @@ -1362,7 +1367,7 @@ func firstAgnetSimulationRole(record agnetDeploymentRecord) string { return "runtime" } -func persistAgnetSimulatedCallback(record agnetDeploymentRecord, payload agnetCallbackEnvelope) error { +func persistAgentSimulatedCallback(record agentDeploymentRecord, payload agentCallbackEnvelope) error { if strings.TrimSpace(payload.EventID) == "" { payload.EventID = "evt-sim-" + common.GetUUID()[:12] } @@ -1379,13 +1384,13 @@ func persistAgnetSimulatedCallback(record agnetDeploymentRecord, payload agnetCa payload.CorrelationID = record.Plan.Metadata.CorrelationID } if strings.TrimSpace(payload.Source) == "" { - payload.Source = "agnet-simulator" + payload.Source = "agent-simulator" } if strings.TrimSpace(payload.OccurredAt) == "" { - payload.OccurredAt = agnetNow() + payload.OccurredAt = agentNow() } payloadJSON, _ := common.Marshal(payload) - inserted, err := model.InsertAgnetCallbackEvent(&model.AgnetCallbackEvent{ + inserted, err := model.InsertAgentCallbackEvent(&model.AgentCallbackEvent{ EventID: strings.TrimSpace(payload.EventID), IdempotencyKey: strings.TrimSpace(payload.IdempotencyKey), CallbackType: "swarm-event", @@ -1409,13 +1414,13 @@ func persistAgnetSimulatedCallback(record agnetDeploymentRecord, payload agnetCa if !inserted { return nil } - if err := persistAgnetArtifactFromCallback(payload, record); err != nil { + if err := persistAgentArtifactFromCallback(payload, record); err != nil { return err } - if err := persistAgnetApprovalFromCallback(payload, record); err != nil { + if err := persistAgentApprovalFromCallback(payload, record); err != nil { return err } - recordAgnetAuditEvent(agnetEvent{ + recordAgentAuditEvent(agentEvent{ EventID: "evt_" + common.GetUUID()[:12], Event: "callback." + payload.EventType, SchemaVersion: 1, @@ -1425,55 +1430,55 @@ func persistAgnetSimulatedCallback(record agnetDeploymentRecord, payload agnetCa DeploymentID: record.DeploymentID, CorrelationID: payload.CorrelationID, OccurredAt: payload.OccurredAt, - }, "agnet_simulator", record.DeploymentID, "", "simulated") + }, "agent_simulator", record.DeploymentID, "", "simulated") return nil } -func persistAgnetDefaultSimulationCallbacks(record agnetDeploymentRecord) error { - role := firstAgnetSimulationRole(record) - grant := firstAgnetSimulationGrant(record) - taskID := "task-sim-" + sanitizeAgnetRef(record.DeploymentID) - agentID := "agi-sim-" + sanitizeAgnetRef(role) - base := func(eventType string, suffix string, payload map[string]any) agnetCallbackEnvelope { - return agnetCallbackEnvelope{ - EventID: "evt-sim-" + sanitizeAgnetRef(record.DeploymentID) + "-" + suffix, - IdempotencyKey: "idem-sim-" + sanitizeAgnetRef(record.DeploymentID) + "-" + suffix, +func persistAgentDefaultSimulationCallbacks(record agentDeploymentRecord) error { + role := firstAgentSimulationRole(record) + grant := firstAgentSimulationGrant(record) + taskID := "task-sim-" + sanitizeAgentRef(record.DeploymentID) + agentID := "agi-sim-" + sanitizeAgentRef(role) + base := func(eventType string, suffix string, payload map[string]any) agentCallbackEnvelope { + return agentCallbackEnvelope{ + EventID: "evt-sim-" + sanitizeAgentRef(record.DeploymentID) + "-" + suffix, + IdempotencyKey: "idem-sim-" + sanitizeAgentRef(record.DeploymentID) + "-" + suffix, EventType: eventType, DeploymentID: record.DeploymentID, SwarmID: firstNonEmpty(record.RuntimeSwarmID, "sim-"+record.DeploymentID), AgentInstanceID: agentID, TaskID: taskID, CorrelationID: record.Plan.Metadata.CorrelationID, - Source: "agnet-simulator", - OccurredAt: agnetNow(), + Source: "agent-simulator", + OccurredAt: agentNow(), Payload: payload, } } - callbacks := []agnetCallbackEnvelope{ + callbacks := []agentCallbackEnvelope{ base("task.created", "task-created", map[string]any{ "title": "Simulated task created", - "summary": "Manager simulator created a child Agnet task for ordinary sub-mode smoke testing", + "summary": "Manager simulator created a child Agent task for ordinary sub-mode smoke testing", "agent_role": role, "severity": "info", "task_id": taskID, }), base("task.claimed", "task-claimed", map[string]any{ "title": "Simulated task claimed", - "summary": "Manager simulator marked a child Agnet task as claimed", + "summary": "Manager simulator marked a child Agent task as claimed", "agent_role": role, "severity": "info", "task_id": taskID, }), base("task.heartbeat", "task-heartbeat", map[string]any{ "title": "Simulated task heartbeat", - "summary": "Manager simulator received a heartbeat from the child Agnet task", + "summary": "Manager simulator received a heartbeat from the child Agent task", "agent_role": role, "severity": "info", "task_id": taskID, }), base("task.blocked", "task-blocked", map[string]any{ "title": "Simulated task blocked", - "summary": "Manager simulator marked a child Agnet task as blocked before handoff", + "summary": "Manager simulator marked a child Agent task as blocked before handoff", "agent_role": role, "severity": "warning", "task_id": taskID, @@ -1481,7 +1486,7 @@ func persistAgnetDefaultSimulationCallbacks(record agnetDeploymentRecord) error }), base("handoff.requested", "handoff-requested", map[string]any{ "title": "Simulated handoff requested", - "summary": "Manager simulator requested handoff between child Agnet roles", + "summary": "Manager simulator requested handoff between child Agent roles", "severity": "info", "task_id": taskID, "from_role": role, @@ -1489,7 +1494,7 @@ func persistAgnetDefaultSimulationCallbacks(record agnetDeploymentRecord) error }), base("handoff.completed", "handoff-completed", map[string]any{ "title": "Simulated handoff completed", - "summary": "Manager simulator completed handoff between child Agnet roles", + "summary": "Manager simulator completed handoff between child Agent roles", "severity": "success", "task_id": taskID, "from_role": role, @@ -1505,7 +1510,7 @@ func persistAgnetDefaultSimulationCallbacks(record agnetDeploymentRecord) error }), base("task.completed", "task-completed", map[string]any{ "title": "Simulated task completed", - "summary": "Manager simulator marked the child Agnet task as completed", + "summary": "Manager simulator marked the child Agent task as completed", "agent_role": role, "severity": "success", "task_id": taskID, @@ -1517,25 +1522,25 @@ func persistAgnetDefaultSimulationCallbacks(record agnetDeploymentRecord) error "checkpoint": "artifact_ready", "severity": "success", }) - artifact.Artifact = agnetArtifactPayload{ - ArtifactID: "art-sim-" + sanitizeAgnetRef(record.DeploymentID), + artifact.Artifact = agentArtifactPayload{ + ArtifactID: "art-sim-" + sanitizeAgentRef(record.DeploymentID), ArtifactType: "test_report", Title: "Simulated test report", Summary: "Manager simulator generated a safe artifact record for smoke testing", URI: "artifact://simulated/" + record.DeploymentID + "/test-report", Metadata: map[string]any{ - "source": "agnet-simulator", + "source": "agent-simulator", "redacted": true, }, } callbacks = append(callbacks, artifact) if strings.TrimSpace(grant.SecretRef) != "" { callbacks = append(callbacks, base("approval.requested", "approval", map[string]any{ - "approval_id": "sim-appr-" + sanitizeAgnetRef(record.DeploymentID), + "approval_id": "sim-appr-" + sanitizeAgentRef(record.DeploymentID), "operation": "simulation.resource_access", "resource_id": firstNonEmpty(grant.ResourceID, record.DeploymentID), "resource_type": firstNonEmpty(grant.ResourceType, "custom"), - "resource_scope": firstNonEmpty(agnetGrantResourceRef(grant), firstPlanBindingScope(record.Plan)), + "resource_scope": firstNonEmpty(agentGrantResourceRef(grant), firstPlanBindingScope(record.Plan)), "target_role": firstNonEmpty(grant.TargetRole, role), "risk_level": "high", "requires_credential": true, @@ -1553,32 +1558,32 @@ func persistAgnetDefaultSimulationCallbacks(record agnetDeploymentRecord) error "next_action": "connect_real_runtime", })) for _, callback := range callbacks { - if err := persistAgnetSimulatedCallback(record, callback); err != nil { + if err := persistAgentSimulatedCallback(record, callback); err != nil { return err } } return nil } -func AgnetSimulateUserDeploymentEvents(c *gin.Context) { - if c.GetInt("role") < common.RoleAdminUser && !common.GetEnvOrDefaultBool("AGNET_SIMULATION_ENABLED", false) { - agnetError(c, "SIMULATION_DISABLED", "simulation endpoint is admin-only unless AGNET_SIMULATION_ENABLED=true") +func AgentSimulateUserDeploymentEvents(c *gin.Context) { + if c.GetInt("role") < common.RoleAdminUser && !common.GetEnvOrDefaultBool("AGENT_SIMULATION_ENABLED", false) { + agentError(c, "SIMULATION_DISABLED", "simulation endpoint is admin-only unless AGENT_SIMULATION_ENABLED=true") return } - record, ok := requireAuthenticatedUserAgnetDeployment(c) + record, ok := requireAuthenticatedUserAgentDeployment(c) if !ok { return } - var req agnetSimulateDeploymentEventsRequest + var req agentSimulateDeploymentEventsRequest if c.Request != nil && c.Request.Body != nil { if err := c.ShouldBindJSON(&req); err != nil { - agnetError(c, "POLICY_REJECTED", err.Error()) + agentError(c, "POLICY_REJECTED", err.Error()) return } } - events := normalizeAgnetSimulationEvents(req.Events) - now := agnetNow() + events := normalizeAgentSimulationEvents(req.Events) + now := agentNow() record.Phase = "simulated" record.RuntimeState = "simulated" record.FailureReason = "" @@ -1588,17 +1593,17 @@ func AgnetSimulateUserDeploymentEvents(c *gin.Context) { record.AgentInstances[i].RuntimeState = record.RuntimeState record.AgentInstances[i].FailureReason = "" } - agnetMu.Lock() - agnetDeployments[record.DeploymentID] = record - agnetMu.Unlock() - if err := updateAgnetDeploymentRecord(record); err != nil { - common.SysLog("AgnetSimulateUserDeploymentEvents persist: " + err.Error()) - agnetError(c, "DEPLOYMENT_PERSIST_FAILED", "failed to persist simulated deployment state") + agentMu.Lock() + agentDeployments[record.DeploymentID] = record + agentMu.Unlock() + if err := updateAgentDeploymentRecord(record); err != nil { + common.SysLog("AgentSimulateUserDeploymentEvents persist: " + err.Error()) + agentError(c, "DEPLOYMENT_PERSIST_FAILED", "failed to persist simulated deployment state") return } for _, eventName := range events { - recordAgnetAuditEvent(agnetEvent{ + recordAgentAuditEvent(agentEvent{ EventID: "evt_" + common.GetUUID()[:12], Event: eventName, SchemaVersion: 1, @@ -1607,13 +1612,13 @@ func AgnetSimulateUserDeploymentEvents(c *gin.Context) { BindingScope: firstPlanBindingScope(record.Plan), DeploymentID: record.DeploymentID, CorrelationID: record.Plan.Metadata.CorrelationID, - OccurredAt: agnetNow(), - }, "agnet_simulator", record.DeploymentID, agnetRequestID(c), "simulated") + OccurredAt: agentNow(), + }, "agent_simulator", record.DeploymentID, agentRequestID(c), "simulated") } if len(req.Events) == 0 { - if err := persistAgnetDefaultSimulationCallbacks(record); err != nil { - common.SysLog("persistAgnetDefaultSimulationCallbacks: " + err.Error()) - agnetError(c, "SIMULATION_PERSIST_FAILED", "failed to persist simulated callback records") + if err := persistAgentDefaultSimulationCallbacks(record); err != nil { + common.SysLog("persistAgentDefaultSimulationCallbacks: " + err.Error()) + agentError(c, "SIMULATION_PERSIST_FAILED", "failed to persist simulated callback records") return } } @@ -1626,25 +1631,98 @@ func AgnetSimulateUserDeploymentEvents(c *gin.Context) { }) } -func AgnetListDeploymentLogs(c *gin.Context) { +// friendlyLogMessage maps a callback event type to a user-facing message for +// the two-layer log split (unified spec §13). +func friendlyLogMessage(eventType string) string { + switch eventType { + case "deployment.status_changed": + return "任务状态已更新" + case "phase.changed": + return "进入新阶段" + case "timeline.updated": + return "进度已更新" + case "agent.started": + return "子代理已开始执行" + case "agent.completed": + return "子代理已完成" + case "agent.crashed", "agent.failed": + return "子代理执行异常" + case "artifact.created": + return "已生成交付产物" + case "approval.requested": + return "需要你审批" + case "task.completed": + return "任务已完成" + case "task.failed": + return "任务执行失败" + default: + return eventType + } +} + +// buildLayeredLogs returns user-facing logs and developer debug logs derived +// from persisted callbacks (unified spec §13). user_logs are friendly summaries; +// debug_logs carry the raw runtime payload for the client debug panel. +func buildLayeredLogs(deploymentID string) ([]gin.H, []gin.H) { + callbacks, err := model.ListAgentCallbackEvents(model.ListAgentCallbackEventsFilter{ + DeploymentID: deploymentID, + Limit: 500, + }) + if err != nil { + common.SysLog("buildLayeredLogs: " + err.Error()) + } + userLogs := make([]gin.H, 0, len(callbacks)) + debugLogs := make([]gin.H, 0, len(callbacks)) + for _, cb := range callbacks { + payload := callbackPayloadMap(cb) + debugLogs = append(debugLogs, gin.H{ + "event_id": cb.EventID, + "event_type": cb.EventType, + "runtime_kind": firstNonEmpty(cb.Source, "agent_management"), + "runtime_deployment_id": cb.SwarmID, + "occurred_at": cb.OccurredAt, + "raw_payload": payload, + }) + msg := "" + if payload != nil { + for _, key := range []string{"summary", "title", "message"} { + if v, ok := payload[key].(string); ok && strings.TrimSpace(v) != "" { + msg = strings.TrimSpace(v) + break + } + } + } + if msg == "" { + msg = friendlyLogMessage(cb.EventType) + } + userLogs = append(userLogs, gin.H{ + "time": firstNonEmpty(cb.OccurredAt, strconv.FormatInt(cb.CreatedAtMs, 10)), + "level": "info", + "message": msg, + }) + } + return userLogs, debugLogs +} + +func AgentListDeploymentLogs(c *gin.Context) { deploymentID := strings.TrimSpace(c.Param("deployment_id")) if deploymentID == "" { - agnetError(c, "POLICY_REJECTED", "deployment_id is required") + agentError(c, "POLICY_REJECTED", "deployment_id is required") return } - record, ok := findAgnetDeploymentRecord(deploymentID) + record, ok := findAgentDeploymentRecord(deploymentID) if !ok { - agnetError(c, "DEPLOYMENT_CONFLICT", "deployment not found") + agentError(c, "DEPLOYMENT_CONFLICT", "deployment not found") return } - rows, err := model.ListAgnetAuditEventsByDeployment(deploymentID) + rows, err := model.ListAgentAuditEventsByDeployment(deploymentID) if err != nil { - common.SysLog("AgnetListDeploymentLogs: " + err.Error()) + common.SysLog("AgentListDeploymentLogs: " + err.Error()) } - events := make([]agnetEvent, 0, len(rows)) + events := make([]agentEvent, 0, len(rows)) for _, r := range rows { - events = append(events, agnetAuditRowToEvent(r)) + events = append(events, agentAuditRowToEvent(r)) } items := make([]gin.H, 0, len(events)+1) @@ -1679,9 +1757,12 @@ func AgnetListDeploymentLogs(c *gin.Context) { }) } + userLogs, debugLogs := buildLayeredLogs(deploymentID) common.ApiSuccess(c, gin.H{ "deployment_id": deploymentID, "items": items, + "user_logs": userLogs, + "debug_logs": debugLogs, "next_cursor": "", "redacted": true, "data_source": "manager_control_plane", @@ -1690,23 +1771,23 @@ func AgnetListDeploymentLogs(c *gin.Context) { }) } -func AgnetListUserDeploymentLogs(c *gin.Context) { - if _, ok := requireAuthenticatedUserAgnetDeployment(c); !ok { +func AgentListUserDeploymentLogs(c *gin.Context) { + if _, ok := requireAuthenticatedUserAgentDeployment(c); !ok { return } - AgnetListDeploymentLogs(c) + AgentListDeploymentLogs(c) } -func AgnetGetDeploymentMetrics(c *gin.Context) { +func AgentGetDeploymentMetrics(c *gin.Context) { deploymentID := strings.TrimSpace(c.Param("deployment_id")) if deploymentID == "" { - agnetError(c, "POLICY_REJECTED", "deployment_id is required") + agentError(c, "POLICY_REJECTED", "deployment_id is required") return } - record, ok := findAgnetDeploymentRecord(deploymentID) + record, ok := findAgentDeploymentRecord(deploymentID) if !ok { - agnetError(c, "DEPLOYMENT_CONFLICT", "deployment not found") + agentError(c, "DEPLOYMENT_CONFLICT", "deployment not found") return } @@ -1732,24 +1813,24 @@ func AgnetGetDeploymentMetrics(c *gin.Context) { }) } -func AgnetGetUserDeploymentMetrics(c *gin.Context) { - if _, ok := requireAuthenticatedUserAgnetDeployment(c); !ok { +func AgentGetUserDeploymentMetrics(c *gin.Context) { + if _, ok := requireAuthenticatedUserAgentDeployment(c); !ok { return } - AgnetGetDeploymentMetrics(c) + AgentGetDeploymentMetrics(c) } -func AgnetProjectDashboardSnapshot(c *gin.Context) { +func AgentProjectDashboardSnapshot(c *gin.Context) { bindingScope := strings.TrimSpace(c.Param("project_id")) if bindingScope == "" { - agnetError(c, "POLICY_REJECTED", "binding_scope is required") + agentError(c, "POLICY_REJECTED", "binding_scope is required") return } active := 0 pending := 0 stopped := 0 - for _, record := range listAgnetDeploymentRecords("", bindingScope) { + for _, record := range listAgentDeploymentRecords("", bindingScope) { if record.Status == "accepted" { active++ } @@ -1770,26 +1851,26 @@ func AgnetProjectDashboardSnapshot(c *gin.Context) { }) } -func AgnetResolveSKSnapshots(c *gin.Context) { - var req agnetSKSnapshotResolveRequest +func AgentResolveSKSnapshots(c *gin.Context) { + var req agentSKSnapshotResolveRequest if err := c.ShouldBindJSON(&req); err != nil { - agnetError(c, "SK_SOURCE_UNRESOLVABLE", err.Error()) + agentError(c, "SK_SOURCE_UNRESOLVABLE", err.Error()) return } deploymentID := strings.TrimSpace(req.DeploymentID) if deploymentID == "" { - agnetError(c, "SK_SOURCE_UNRESOLVABLE", "deployment_id is required") + agentError(c, "SK_SOURCE_UNRESOLVABLE", "deployment_id is required") return } - record, ok := findAgnetDeploymentRecord(deploymentID) + record, ok := findAgentDeploymentRecord(deploymentID) if !ok { - agnetError(c, "DEPLOYMENT_CONFLICT", "deployment not found") + agentError(c, "DEPLOYMENT_CONFLICT", "deployment not found") return } - snapshots := make([]agnetSKSnapshot, 0) - now := agnetNow() + snapshots := make([]agentSKSnapshot, 0) + now := agentNow() for _, agent := range record.Plan.Agents { for _, source := range agent.SKSources { sourceType := strings.TrimSpace(source.Type) @@ -1803,7 +1884,7 @@ func AgnetResolveSKSnapshots(c *gin.Context) { if sourceType == "git" { sourceRef = sourceRef + "@sha_" + common.GetUUID()[:12] } - snapshots = append(snapshots, agnetSKSnapshot{ + snapshots = append(snapshots, agentSKSnapshot{ SnapshotID: "sks_" + common.GetUUID()[:12], DeploymentID: deploymentID, UserID: record.Plan.UserContext.UserID, @@ -1814,12 +1895,12 @@ func AgnetResolveSKSnapshots(c *gin.Context) { }) } } - agnetMu.Lock() - agnetSnapshots[deploymentID] = snapshots - agnetMu.Unlock() - rows := make([]model.AgnetSKSnapshot, 0, len(snapshots)) + agentMu.Lock() + agentSnapshots[deploymentID] = snapshots + agentMu.Unlock() + rows := make([]model.AgentSKSnapshot, 0, len(snapshots)) for _, snapshot := range snapshots { - rows = append(rows, model.AgnetSKSnapshot{ + rows = append(rows, model.AgentSKSnapshot{ SnapshotID: snapshot.SnapshotID, DeploymentID: snapshot.DeploymentID, UserID: snapshot.UserID, @@ -1827,15 +1908,15 @@ func AgnetResolveSKSnapshots(c *gin.Context) { SourceType: snapshot.SourceType, SourceRef: snapshot.SourceRef, ResolvedAt: snapshot.ResolvedAt, - ResolvedAtMs: agnetTimestampMs(snapshot.ResolvedAt), + ResolvedAtMs: agentTimestampMs(snapshot.ResolvedAt), }) } - if err := model.InsertAgnetSKSnapshots(rows); err != nil { - common.SysLog("AgnetResolveSKSnapshots persist: " + err.Error()) - agnetError(c, "SK_SNAPSHOT_PERSIST_FAILED", "failed to persist sk snapshots") + if err := model.InsertAgentSKSnapshots(rows); err != nil { + common.SysLog("AgentResolveSKSnapshots persist: " + err.Error()) + agentError(c, "SK_SNAPSHOT_PERSIST_FAILED", "failed to persist sk snapshots") return } - snapEvent := agnetEvent{ + snapEvent := agentEvent{ EventID: "evt_" + common.GetUUID()[:12], Event: "sk_snapshot_refreshed", SchemaVersion: 1, @@ -1846,7 +1927,7 @@ func AgnetResolveSKSnapshots(c *gin.Context) { CorrelationID: record.Plan.Metadata.CorrelationID, OccurredAt: now, } - recordAgnetAuditEvent(snapEvent, "agnet_control_plane", deploymentID, agnetRequestID(c), "ok") + recordAgentAuditEvent(snapEvent, "agent_control_plane", deploymentID, agentRequestID(c), "ok") common.ApiSuccess(c, gin.H{ "deployment_id": deploymentID, @@ -1855,25 +1936,25 @@ func AgnetResolveSKSnapshots(c *gin.Context) { }) } -func AgnetListSKSnapshots(c *gin.Context) { +func AgentListSKSnapshots(c *gin.Context) { deploymentID := strings.TrimSpace(c.Param("deployment_id")) if deploymentID == "" { - agnetError(c, "SK_SOURCE_UNRESOLVABLE", "deployment_id is required") + agentError(c, "SK_SOURCE_UNRESOLVABLE", "deployment_id is required") return } - agnetMu.RLock() - items := agnetSnapshots[deploymentID] - agnetMu.RUnlock() + agentMu.RLock() + items := agentSnapshots[deploymentID] + agentMu.RUnlock() if len(items) == 0 { - rows, err := model.ListAgnetSKSnapshots(deploymentID) + rows, err := model.ListAgentSKSnapshots(deploymentID) if err != nil { - common.SysLog("AgnetListSKSnapshots: " + err.Error()) - agnetError(c, "SK_SOURCE_UNRESOLVABLE", "failed to query sk snapshots") + common.SysLog("AgentListSKSnapshots: " + err.Error()) + agentError(c, "SK_SOURCE_UNRESOLVABLE", "failed to query sk snapshots") return } - items = make([]agnetSKSnapshot, 0, len(rows)) + items = make([]agentSKSnapshot, 0, len(rows)) for _, row := range rows { - items = append(items, agnetSKSnapshot{ + items = append(items, agentSKSnapshot{ SnapshotID: row.SnapshotID, DeploymentID: row.DeploymentID, UserID: row.UserID, @@ -1892,14 +1973,14 @@ func AgnetListSKSnapshots(c *gin.Context) { }) } -func AgnetListUserSKSnapshots(c *gin.Context) { - if _, ok := requireAuthenticatedUserAgnetDeployment(c); !ok { +func AgentListUserSKSnapshots(c *gin.Context) { + if _, ok := requireAuthenticatedUserAgentDeployment(c); !ok { return } - AgnetListSKSnapshots(c) + AgentListSKSnapshots(c) } -func AgnetListAuditLogs(c *gin.Context) { +func AgentListAuditLogs(c *gin.Context) { // Persistent audit query — survives container restarts. Filters // are optional; the dashboard call usually narrows by user_id + // time window. Pagination is opt-in via ?limit / ?offset. @@ -1915,7 +1996,7 @@ func AgnetListAuditLogs(c *gin.Context) { offset = n } } - rows, total, err := model.ListAgnetAuditEvents(model.ListAgnetAuditEventsFilter{ + rows, total, err := model.ListAgentAuditEvents(model.ListAgentAuditEventsFilter{ UserID: strings.TrimSpace(c.Query("user_id")), BindingScope: strings.TrimSpace(c.Query("binding_scope")), DeploymentID: strings.TrimSpace(c.Query("deployment_id")), @@ -1923,8 +2004,8 @@ func AgnetListAuditLogs(c *gin.Context) { Offset: offset, }) if err != nil { - common.SysLog("AgnetListAuditLogs: " + err.Error()) - agnetError(c, "POLICY_REJECTED", "internal error") + common.SysLog("AgentListAuditLogs: " + err.Error()) + agentError(c, "POLICY_REJECTED", "internal error") return } items := make([]gin.H, 0, len(rows)) @@ -1948,12 +2029,12 @@ func AgnetListAuditLogs(c *gin.Context) { }) } -// agnetAuditRowToEvent reshapes a persisted audit row back into the +// agentAuditRowToEvent reshapes a persisted audit row back into the // in-process event struct so the deployment-detail / log endpoints // can keep returning the same JSON shape they used to emit from the // in-memory map. Avoids breaking any existing dashboard consumer. -func agnetAuditRowToEvent(r model.AgnetAuditEvent) agnetEvent { - return agnetEvent{ +func agentAuditRowToEvent(r model.AgentAuditEvent) agentEvent { + return agentEvent{ EventID: r.EventID, Event: r.Event, SchemaVersion: r.SchemaVersion, diff --git a/heicode/controller/agnet_control_plane_test.go b/heicode/controller/agent_control_plane_test.go similarity index 70% rename from heicode/controller/agnet_control_plane_test.go rename to heicode/controller/agent_control_plane_test.go index 3c8a18d..2b7bd2b 100644 --- a/heicode/controller/agnet_control_plane_test.go +++ b/heicode/controller/agent_control_plane_test.go @@ -21,30 +21,30 @@ import ( "gorm.io/gorm" ) -type agnetCreateTestEnvelope struct { +type agentCreateTestEnvelope struct { Success bool `json:"success"` Message string `json:"message"` - Error agnetTestError `json:"error"` + Error agentTestError `json:"error"` } -type agnetTestError struct { +type agentTestError struct { Code string `json:"code"` Message string `json:"message"` } -func resetAgnetControlPlaneState(t *testing.T) { +func resetAgentControlPlaneState(t *testing.T) { t.Helper() - agnetMu.Lock() - defer agnetMu.Unlock() - agnetDeployments = make(map[string]agnetDeploymentRecord) - agnetSnapshots = make(map[string][]agnetSKSnapshot) + agentMu.Lock() + defer agentMu.Unlock() + agentDeployments = make(map[string]agentDeploymentRecord) + agentSnapshots = make(map[string][]agentSKSnapshot) // Audit rows live in DB now; tests use a fresh SQLite per case // (setupTokenControllerTestDB style) so there is no global map // to flush here. } -func setupAgnetControlPlaneTestDB(t *testing.T) *gorm.DB { +func setupAgentControlPlaneTestDB(t *testing.T) *gorm.DB { t.Helper() gin.SetMode(gin.TestMode) common.UsingSQLite = true @@ -60,12 +60,12 @@ func setupAgnetControlPlaneTestDB(t *testing.T) *gorm.DB { require.NoError(t, db.AutoMigrate( &model.ResourceBinding{}, &model.ResourceGrant{}, - &model.AgnetAuditEvent{}, - &model.AgnetDeployment{}, - &model.AgnetCallbackEvent{}, - &model.AgnetArtifact{}, - &model.AgnetSKSnapshot{}, - &model.AgnetApprovalRequest{}, + &model.AgentAuditEvent{}, + &model.AgentDeployment{}, + &model.AgentCallbackEvent{}, + &model.AgentArtifact{}, + &model.AgentSKSnapshot{}, + &model.AgentApprovalRequest{}, )) t.Cleanup(func() { if model.DB == db { @@ -82,49 +82,49 @@ func setupAgnetControlPlaneTestDB(t *testing.T) *gorm.DB { return db } -func baseAgnetResourceGrantPlan() agnetOrchestrationPlan { - return agnetOrchestrationPlan{ +func baseAgentResourceGrantPlan() agentOrchestrationPlan { + return agentOrchestrationPlan{ IntentID: "intent-resource-grant", TemplateHint: "manager-resource-binding", - Objective: "Grant bounded resources to a child Agnet role", - RiskLevel: agnetRiskLow, - Budget: agnetBudget{ + Objective: "Grant bounded resources to a child Agent role", + RiskLevel: agentRiskLow, + Budget: agentBudget{ MaxTokens: 10000, MaxCostUSD: 10, MaxDurationSec: 3600, }, - UserContext: agnetUserContext{ + UserContext: agentUserContext{ UserID: "user-p1", Email: "builder@example.invalid", Role: "user", ChannelID: "channel-p1", }, - BillingContext: agnetBillingContext{ + BillingContext: agentBillingContext{ Provider: "newapi", NewAPIUserRef: "newapi-user-p1", NewAPIGroup: "development", QuotaRef: "newapi-quota-ref-p1", }, - AgentRuntime: agnetAgentRuntime{ - Platform: "agnet", - Agents: []agnetRuntimeAgent{ + AgentRuntime: agentAgentRuntime{ + Platform: "agent", + Agents: []agentRuntimeAgent{ { Role: "builder", - ModelRef: "agnet-model-profile-builder", + ModelRef: "agent-model-profile-builder", InstanceCount: 1, }, }, }, - Agents: []agnetAgentPlan{ + Agents: []agentAgentPlan{ { RoleTemplate: "builder", Goal: "Use granted resources without receiving plaintext credentials", DefaultModelID: "gpt-resource-test", - ResourceGrants: []agnetResourceGrant{ + ResourceGrants: []agentResourceGrant{ { GrantID: "grant-git-builder", ResourceID: "res-git-main", - ResourceType: agnetResourceGit, + ResourceType: agentResourceGit, UserID: "user-p1", BindingScope: "https://example.invalid/acme/project.git#main", TargetRole: "builder", @@ -138,7 +138,7 @@ func baseAgnetResourceGrantPlan() agnetOrchestrationPlan { "provider": "gitee", "repo_url": "https://example.invalid/acme/project.git", }, - Status: agnetGrantStatusActive, + Status: agentGrantStatusActive, SecretRef: "azkv://heicode-kv.vault.azure.net/secrets/user-p1-project-main-res-git-main", Audit: map[string]string{ "created_by": "manager-test", @@ -147,7 +147,7 @@ func baseAgnetResourceGrantPlan() agnetOrchestrationPlan { { GrantID: "grant-doc-builder", ResourceID: "res-doc-plan", - ResourceType: agnetResourceProjectDoc, + ResourceType: agentResourceProjectDoc, UserID: "user-p1", BindingScope: "docs/heicode.md", TargetRole: "builder", @@ -156,63 +156,63 @@ func baseAgnetResourceGrantPlan() agnetOrchestrationPlan { Metadata: map[string]string{ "doc_ref": "docs/heicode.md", }, - Status: agnetGrantStatusActive, + Status: agentGrantStatusActive, }, }, }, }, - Constraints: agnetConstraints{AllowedModelIDs: []string{"gpt-resource-test"}}, - Metadata: agnetMetadata{ + Constraints: agentConstraints{AllowedModelIDs: []string{"gpt-resource-test"}}, + Metadata: agentMetadata{ CorrelationID: "corr-p1-resource-grant", }, } } -func postAgnetCreateDeployment(t *testing.T, plan agnetOrchestrationPlan) (*httptest.ResponseRecorder, agnetCreateTestEnvelope) { +func postAgentCreateDeployment(t *testing.T, plan agentOrchestrationPlan) (*httptest.ResponseRecorder, agentCreateTestEnvelope) { t.Helper() gin.SetMode(gin.TestMode) - body, err := common.Marshal(agnetDeploymentRequest{Plan: plan}) + body, err := common.Marshal(agentDeploymentRequest{Plan: plan}) require.NoError(t, err) recorder := httptest.NewRecorder() ctx, _ := gin.CreateTestContext(recorder) - ctx.Request = httptest.NewRequest(http.MethodPost, "/api/agnet/deployments", strings.NewReader(string(body))) + ctx.Request = httptest.NewRequest(http.MethodPost, "/api/agent/deployments", strings.NewReader(string(body))) ctx.Request.Header.Set("Content-Type", "application/json") - AgnetCreateDeployment(ctx) + AgentCreateDeployment(ctx) - var envelope agnetCreateTestEnvelope + var envelope agentCreateTestEnvelope require.NoError(t, common.Unmarshal(recorder.Body.Bytes(), &envelope)) return recorder, envelope } -func postAgnetCreateUserDeployment(t *testing.T, userID int, plan agnetOrchestrationPlan) (*httptest.ResponseRecorder, agnetCreateTestEnvelope) { +func postAgentCreateUserDeployment(t *testing.T, userID int, plan agentOrchestrationPlan) (*httptest.ResponseRecorder, agentCreateTestEnvelope) { t.Helper() gin.SetMode(gin.TestMode) - body, err := common.Marshal(agnetDeploymentRequest{Plan: plan}) + body, err := common.Marshal(agentDeploymentRequest{Plan: plan}) require.NoError(t, err) recorder := httptest.NewRecorder() ctx, _ := gin.CreateTestContext(recorder) ctx.Set("id", userID) ctx.Set("group", "development") - ctx.Request = httptest.NewRequest(http.MethodPost, "/api/agnet/user/deployments", strings.NewReader(string(body))) + ctx.Request = httptest.NewRequest(http.MethodPost, "/api/agent/user/deployments", strings.NewReader(string(body))) ctx.Request.Header.Set("Content-Type", "application/json") - AgnetCreateUserDeployment(ctx) + AgentCreateUserDeployment(ctx) - var envelope agnetCreateTestEnvelope + var envelope agentCreateTestEnvelope require.NoError(t, common.Unmarshal(recorder.Body.Bytes(), &envelope)) return recorder, envelope } -func TestAgnetDeploymentSurvivesInProcessStateReset(t *testing.T) { - db := setupAgnetControlPlaneTestDB(t) - resetAgnetControlPlaneState(t) +func TestAgentDeploymentSurvivesInProcessStateReset(t *testing.T) { + db := setupAgentControlPlaneTestDB(t) + resetAgentControlPlaneState(t) - recorder, envelope := postAgnetCreateDeployment(t, baseAgnetResourceGrantPlan()) + recorder, envelope := postAgentCreateDeployment(t, baseAgentResourceGrantPlan()) require.Equal(t, http.StatusOK, recorder.Code) require.True(t, envelope.Success) @@ -221,7 +221,7 @@ func TestAgnetDeploymentSurvivesInProcessStateReset(t *testing.T) { deploymentID := createBody["data"].(map[string]any)["deployment_id"].(string) require.NotEmpty(t, deploymentID) - var stored model.AgnetDeployment + var stored model.AgentDeployment require.NoError(t, db.Where("deployment_id = ?", deploymentID).First(&stored).Error) require.Equal(t, "user-p1", stored.UserID) require.Equal(t, "channel-p1", stored.ChannelID) @@ -232,13 +232,13 @@ func TestAgnetDeploymentSurvivesInProcessStateReset(t *testing.T) { require.NotContains(t, strings.ToLower(stored.PayloadJSON), "private_key") require.NotContains(t, strings.ToLower(stored.PermissionManifestJSON), "access_token") - resetAgnetControlPlaneState(t) + resetAgentControlPlaneState(t) getRecorder := httptest.NewRecorder() getCtx, _ := gin.CreateTestContext(getRecorder) getCtx.Params = gin.Params{{Key: "deployment_id", Value: deploymentID}} - getCtx.Request = httptest.NewRequest(http.MethodGet, "/api/agnet/deployments/"+deploymentID, nil) - AgnetGetDeployment(getCtx) + getCtx.Request = httptest.NewRequest(http.MethodGet, "/api/agent/deployments/"+deploymentID, nil) + AgentGetDeployment(getCtx) var getBody map[string]any require.NoError(t, common.Unmarshal(getRecorder.Body.Bytes(), &getBody)) @@ -249,8 +249,8 @@ func TestAgnetDeploymentSurvivesInProcessStateReset(t *testing.T) { listRecorder := httptest.NewRecorder() listCtx, _ := gin.CreateTestContext(listRecorder) - listCtx.Request = httptest.NewRequest(http.MethodGet, "/api/agnet/deployments?user_id=user-p1", nil) - AgnetListDeployments(listCtx) + listCtx.Request = httptest.NewRequest(http.MethodGet, "/api/agent/deployments?user_id=user-p1", nil) + AgentListDeployments(listCtx) var listBody map[string]any require.NoError(t, common.Unmarshal(listRecorder.Body.Bytes(), &listBody)) @@ -259,14 +259,14 @@ func TestAgnetDeploymentSurvivesInProcessStateReset(t *testing.T) { require.Equal(t, float64(1), listData["total"]) } -func TestAgnetDeploymentPersistsSubMode(t *testing.T) { - db := setupAgnetControlPlaneTestDB(t) - resetAgnetControlPlaneState(t) +func TestAgentDeploymentPersistsSubMode(t *testing.T) { + db := setupAgentControlPlaneTestDB(t) + resetAgentControlPlaneState(t) - plan := baseAgnetResourceGrantPlan() + plan := baseAgentResourceGrantPlan() plan.SubMode = "waterfall" - recorder, envelope := postAgnetCreateDeployment(t, plan) + recorder, envelope := postAgentCreateDeployment(t, plan) require.Equal(t, http.StatusOK, recorder.Code) require.True(t, envelope.Success) require.Contains(t, recorder.Body.String(), `"sub_mode":"waterfall"`) @@ -275,70 +275,70 @@ func TestAgnetDeploymentPersistsSubMode(t *testing.T) { require.NoError(t, common.Unmarshal(recorder.Body.Bytes(), &createBody)) deploymentID := createBody["data"].(map[string]any)["deployment_id"].(string) - var stored model.AgnetDeployment + var stored model.AgentDeployment require.NoError(t, db.Where("deployment_id = ?", deploymentID).First(&stored).Error) require.Equal(t, "waterfall", stored.SubMode) - resetAgnetControlPlaneState(t) + resetAgentControlPlaneState(t) getRecorder := httptest.NewRecorder() getCtx, _ := gin.CreateTestContext(getRecorder) getCtx.Params = gin.Params{{Key: "deployment_id", Value: deploymentID}} - getCtx.Request = httptest.NewRequest(http.MethodGet, "/api/agnet/deployments/"+deploymentID, nil) - AgnetGetDeployment(getCtx) + getCtx.Request = httptest.NewRequest(http.MethodGet, "/api/agent/deployments/"+deploymentID, nil) + AgentGetDeployment(getCtx) require.Equal(t, http.StatusOK, getRecorder.Code) require.Contains(t, getRecorder.Body.String(), `"sub_mode":"waterfall"`) } -func TestAgnetDeploymentDefaultsSubModeToAgile(t *testing.T) { - setupAgnetControlPlaneTestDB(t) - resetAgnetControlPlaneState(t) +func TestAgentDeploymentDefaultsSubModeToAgile(t *testing.T) { + setupAgentControlPlaneTestDB(t) + resetAgentControlPlaneState(t) - plan := baseAgnetResourceGrantPlan() + plan := baseAgentResourceGrantPlan() plan.SubMode = "" - recorder, envelope := postAgnetCreateDeployment(t, plan) + recorder, envelope := postAgentCreateDeployment(t, plan) require.Equal(t, http.StatusOK, recorder.Code) require.True(t, envelope.Success) require.Contains(t, recorder.Body.String(), `"sub_mode":"agile"`) } -func TestAgnetRuntimeHealthNotConfigured(t *testing.T) { - setupAgnetControlPlaneTestDB(t) - resetAgnetControlPlaneState(t) - t.Setenv("AGNET_RUNTIME_BASE_URL", "") +func TestAgentRuntimeHealthNotConfigured(t *testing.T) { + setupAgentControlPlaneTestDB(t) + resetAgentControlPlaneState(t) + t.Setenv("AGENT_RUNTIME_BASE_URL", "") recorder := httptest.NewRecorder() ctx, _ := gin.CreateTestContext(recorder) - ctx.Request = httptest.NewRequest(http.MethodGet, "/api/agnet/runtime/health", nil) + ctx.Request = httptest.NewRequest(http.MethodGet, "/api/agent/runtime/health", nil) - AgnetRuntimeHealth(ctx) + AgentRuntimeHealth(ctx) require.Equal(t, http.StatusOK, recorder.Code) require.Contains(t, recorder.Body.String(), `"status":"not_configured"`) require.Contains(t, recorder.Body.String(), `"configured":false`) } -func TestAgnetRuntimeHealthProxy(t *testing.T) { - setupAgnetControlPlaneTestDB(t) - resetAgnetControlPlaneState(t) +func TestAgentRuntimeHealthProxy(t *testing.T) { + setupAgentControlPlaneTestDB(t) + resetAgentControlPlaneState(t) server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - require.Equal(t, "/api/agnet/health", r.URL.Path) + require.Equal(t, "/api/agent/health", r.URL.Path) require.Equal(t, "Bearer service-token", r.Header.Get("Authorization")) w.Header().Set("Content-Type", "application/json") _, _ = w.Write([]byte(`{"success":true,"data":{"status":"healthy"}}`)) })) defer server.Close() - t.Setenv("AGNET_RUNTIME_BASE_URL", server.URL) - t.Setenv("AGNET_RUNTIME_SERVICE_TOKEN", "service-token") + t.Setenv("AGENT_RUNTIME_BASE_URL", server.URL) + t.Setenv("AGENT_RUNTIME_SERVICE_TOKEN", "service-token") recorder := httptest.NewRecorder() ctx, _ := gin.CreateTestContext(recorder) - ctx.Request = httptest.NewRequest(http.MethodGet, "/api/agnet/runtime/health", nil) + ctx.Request = httptest.NewRequest(http.MethodGet, "/api/agent/runtime/health", nil) - AgnetRuntimeHealth(ctx) + AgentRuntimeHealth(ctx) require.Equal(t, http.StatusOK, recorder.Code) require.Contains(t, recorder.Body.String(), `"status":"healthy"`) @@ -346,12 +346,12 @@ func TestAgnetRuntimeHealthProxy(t *testing.T) { require.Contains(t, recorder.Body.String(), `"remote"`) } -func TestAgnetRuntimeShadowCreateStoresRuntimeMapping(t *testing.T) { - db := setupAgnetControlPlaneTestDB(t) - resetAgnetControlPlaneState(t) +func TestAgentRuntimeShadowCreateStoresRuntimeMapping(t *testing.T) { + db := setupAgentControlPlaneTestDB(t) + resetAgentControlPlaneState(t) server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - require.Equal(t, "/api/agnet/deployments", r.URL.Path) + require.Equal(t, "/api/agent/sub-agile/deployments", r.URL.Path) require.Equal(t, http.MethodPost, r.Method) require.Equal(t, "Bearer service-token", r.Header.Get("Authorization")) require.NotEmpty(t, r.Header.Get("X-Correlation-ID")) @@ -373,14 +373,14 @@ func TestAgnetRuntimeShadowCreateStoresRuntimeMapping(t *testing.T) { })) defer server.Close() - t.Setenv("AGNET_RUNTIME_ENABLED", "true") - t.Setenv("AGNET_RUNTIME_ASYNC", "false") - t.Setenv("AGNET_RUNTIME_BASE_URL", server.URL) - t.Setenv("AGNET_RUNTIME_SERVICE_TOKEN", "service-token") + t.Setenv("AGENT_RUNTIME_ENABLED", "true") + t.Setenv("AGENT_RUNTIME_ASYNC", "false") + t.Setenv("AGENT_RUNTIME_BASE_URL", server.URL) + t.Setenv("AGENT_RUNTIME_SERVICE_TOKEN", "service-token") - plan := baseAgnetResourceGrantPlan() + plan := baseAgentResourceGrantPlan() plan.SubMode = "agile" - recorder, envelope := postAgnetCreateDeployment(t, plan) + recorder, envelope := postAgentCreateDeployment(t, plan) require.Equal(t, http.StatusOK, recorder.Code) require.True(t, envelope.Success) require.Contains(t, recorder.Body.String(), `"runtime_deployment_id":"runtime-dep-1"`) @@ -390,21 +390,21 @@ func TestAgnetRuntimeShadowCreateStoresRuntimeMapping(t *testing.T) { require.NoError(t, common.Unmarshal(recorder.Body.Bytes(), &createBody)) deploymentID := createBody["data"].(map[string]any)["deployment_id"].(string) - var stored model.AgnetDeployment + var stored model.AgentDeployment require.NoError(t, db.Where("deployment_id = ?", deploymentID).First(&stored).Error) require.Equal(t, "runtime-dep-1", stored.RuntimeDeploymentID) require.Equal(t, "swarm-1", stored.RuntimeSwarmID) require.Equal(t, "accepted", stored.RuntimeState) } -func TestAgnetRuntimeDiagnosticsWarnsOnCompletedRuntimeWithFailedAgents(t *testing.T) { - setupAgnetControlPlaneTestDB(t) - resetAgnetControlPlaneState(t) +func TestAgentRuntimeDiagnosticsWarnsOnCompletedRuntimeWithFailedAgents(t *testing.T) { + setupAgentControlPlaneTestDB(t) + resetAgentControlPlaneState(t) server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { require.Equal(t, "Bearer service-token", r.Header.Get("Authorization")) switch { - case r.Method == http.MethodPost && r.URL.Path == "/api/agnet/deployments": + case r.Method == http.MethodPost && r.URL.Path == "/api/agent/sub-agile/deployments": w.Header().Set("Content-Type", "application/json") _, _ = w.Write([]byte(`{"success":true,"data":{"deployment_id":"runtime-dep-1","swarm_id":"swarm-1","status":"running"}}`)) case r.Method == http.MethodGet && r.URL.Path == "/api/swarms/swarm-1/status": @@ -424,12 +424,12 @@ func TestAgnetRuntimeDiagnosticsWarnsOnCompletedRuntimeWithFailedAgents(t *testi })) defer server.Close() - t.Setenv("AGNET_RUNTIME_ENABLED", "true") - t.Setenv("AGNET_RUNTIME_ASYNC", "false") - t.Setenv("AGNET_RUNTIME_BASE_URL", server.URL) - t.Setenv("AGNET_RUNTIME_SERVICE_TOKEN", "service-token") + t.Setenv("AGENT_RUNTIME_ENABLED", "true") + t.Setenv("AGENT_RUNTIME_ASYNC", "false") + t.Setenv("AGENT_RUNTIME_BASE_URL", server.URL) + t.Setenv("AGENT_RUNTIME_SERVICE_TOKEN", "service-token") - plan := baseAgnetResourceGrantPlan() + plan := baseAgentResourceGrantPlan() plan.UserContext.UserID = "7" for idx := range plan.ResourceGrants { plan.ResourceGrants[idx].UserID = "7" @@ -439,7 +439,7 @@ func TestAgnetRuntimeDiagnosticsWarnsOnCompletedRuntimeWithFailedAgents(t *testi plan.Agents[agentIdx].ResourceGrants[grantIdx].UserID = "7" } } - createRecorder, envelope := postAgnetCreateUserDeployment(t, 7, plan) + createRecorder, envelope := postAgentCreateUserDeployment(t, 7, plan) require.True(t, envelope.Success, createRecorder.Body.String()) var createBody map[string]any require.NoError(t, common.Unmarshal(createRecorder.Body.Bytes(), &createBody)) @@ -449,9 +449,9 @@ func TestAgnetRuntimeDiagnosticsWarnsOnCompletedRuntimeWithFailedAgents(t *testi ctx, _ := gin.CreateTestContext(recorder) ctx.Set("id", 7) ctx.Params = gin.Params{{Key: "deployment_id", Value: deploymentID}} - ctx.Request = httptest.NewRequest(http.MethodGet, "/api/agnet/user/deployments/"+deploymentID+"/runtime-diagnostics", nil) + ctx.Request = httptest.NewRequest(http.MethodGet, "/api/agent/user/deployments/"+deploymentID+"/runtime-diagnostics", nil) - AgnetGetUserDeploymentRuntimeDiagnostics(ctx) + AgentGetUserDeploymentRuntimeDiagnostics(ctx) require.Equal(t, http.StatusOK, recorder.Code) require.Contains(t, recorder.Body.String(), `"data_source":"runtime_status"`) @@ -461,14 +461,14 @@ func TestAgnetRuntimeDiagnosticsWarnsOnCompletedRuntimeWithFailedAgents(t *testi require.Contains(t, recorder.Body.String(), `"runtime_zero_model_usage"`) } -func TestAgnetArtifactContentProxiesRuntimeContent(t *testing.T) { - setupAgnetControlPlaneTestDB(t) - resetAgnetControlPlaneState(t) +func TestAgentArtifactContentProxiesRuntimeContent(t *testing.T) { + setupAgentControlPlaneTestDB(t) + resetAgentControlPlaneState(t) server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { require.Equal(t, "Bearer service-token", r.Header.Get("Authorization")) switch { - case r.Method == http.MethodPost && r.URL.Path == "/api/agnet/deployments": + case r.Method == http.MethodPost && r.URL.Path == "/api/agent/sub-agile/deployments": w.Header().Set("Content-Type", "application/json") _, _ = w.Write([]byte(`{"success":true,"data":{"deployment_id":"runtime-dep-1","swarm_id":"swarm-1","status":"running"}}`)) case r.Method == http.MethodGet && r.URL.Path == "/api/swarms/swarm-1/artifacts/art-1/content": @@ -481,12 +481,12 @@ func TestAgnetArtifactContentProxiesRuntimeContent(t *testing.T) { })) defer server.Close() - t.Setenv("AGNET_RUNTIME_ENABLED", "true") - t.Setenv("AGNET_RUNTIME_ASYNC", "false") - t.Setenv("AGNET_RUNTIME_BASE_URL", server.URL) - t.Setenv("AGNET_RUNTIME_SERVICE_TOKEN", "service-token") + t.Setenv("AGENT_RUNTIME_ENABLED", "true") + t.Setenv("AGENT_RUNTIME_ASYNC", "false") + t.Setenv("AGENT_RUNTIME_BASE_URL", server.URL) + t.Setenv("AGENT_RUNTIME_SERVICE_TOKEN", "service-token") - plan := baseAgnetResourceGrantPlan() + plan := baseAgentResourceGrantPlan() plan.UserContext.UserID = "7" for idx := range plan.ResourceGrants { plan.ResourceGrants[idx].UserID = "7" @@ -496,12 +496,12 @@ func TestAgnetArtifactContentProxiesRuntimeContent(t *testing.T) { plan.Agents[agentIdx].ResourceGrants[grantIdx].UserID = "7" } } - createRecorder, envelope := postAgnetCreateUserDeployment(t, 7, plan) + createRecorder, envelope := postAgentCreateUserDeployment(t, 7, plan) require.True(t, envelope.Success, createRecorder.Body.String()) var createBody map[string]any require.NoError(t, common.Unmarshal(createRecorder.Body.Bytes(), &createBody)) deploymentID := createBody["data"].(map[string]any)["deployment_id"].(string) - require.NoError(t, model.UpsertAgnetArtifact(&model.AgnetArtifact{ + require.NoError(t, model.UpsertAgentArtifact(&model.AgentArtifact{ ArtifactID: "art-1", DeploymentID: deploymentID, UserID: "7", @@ -516,9 +516,9 @@ func TestAgnetArtifactContentProxiesRuntimeContent(t *testing.T) { {Key: "deployment_id", Value: deploymentID}, {Key: "artifact_id", Value: "art-1"}, } - ctx.Request = httptest.NewRequest(http.MethodGet, "/api/agnet/user/deployments/"+deploymentID+"/artifacts/art-1/content", nil) + ctx.Request = httptest.NewRequest(http.MethodGet, "/api/agent/user/deployments/"+deploymentID+"/artifacts/art-1/content", nil) - AgnetGetUserDeploymentArtifactContent(ctx) + AgentGetUserDeploymentArtifactContent(ctx) require.Equal(t, http.StatusOK, recorder.Code) require.Equal(t, "artifact body", recorder.Body.String()) @@ -526,20 +526,20 @@ func TestAgnetArtifactContentProxiesRuntimeContent(t *testing.T) { require.Equal(t, `attachment; filename="art-1.txt"`, recorder.Header().Get("Content-Disposition")) } -func TestAgnetRuntimeShadowCreateFailureDoesNotFailLocalDeployment(t *testing.T) { - db := setupAgnetControlPlaneTestDB(t) - resetAgnetControlPlaneState(t) +func TestAgentRuntimeShadowCreateFailureDoesNotFailLocalDeployment(t *testing.T) { + db := setupAgentControlPlaneTestDB(t) + resetAgentControlPlaneState(t) server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { http.Error(w, "runtime unavailable", http.StatusBadGateway) })) defer server.Close() - t.Setenv("AGNET_RUNTIME_ENABLED", "true") - t.Setenv("AGNET_RUNTIME_ASYNC", "false") - t.Setenv("AGNET_RUNTIME_BASE_URL", server.URL) + t.Setenv("AGENT_RUNTIME_ENABLED", "true") + t.Setenv("AGENT_RUNTIME_ASYNC", "false") + t.Setenv("AGENT_RUNTIME_BASE_URL", server.URL) - recorder, envelope := postAgnetCreateDeployment(t, baseAgnetResourceGrantPlan()) + recorder, envelope := postAgentCreateDeployment(t, baseAgentResourceGrantPlan()) require.Equal(t, http.StatusOK, recorder.Code) require.True(t, envelope.Success) require.Contains(t, recorder.Body.String(), `"runtime_state":"runtime_sync_failed"`) @@ -548,15 +548,15 @@ func TestAgnetRuntimeShadowCreateFailureDoesNotFailLocalDeployment(t *testing.T) require.NoError(t, common.Unmarshal(recorder.Body.Bytes(), &createBody)) deploymentID := createBody["data"].(map[string]any)["deployment_id"].(string) - var stored model.AgnetDeployment + var stored model.AgentDeployment require.NoError(t, db.Where("deployment_id = ?", deploymentID).First(&stored).Error) - require.Equal(t, agnetRuntimeStateFailed, stored.RuntimeState) + require.Equal(t, agentRuntimeStateFailed, stored.RuntimeState) require.Contains(t, stored.FailureReason, "HTTP 502") } -func TestAgnetRuntimeShadowCreateTreatsSuccessFalseAsFailure(t *testing.T) { - db := setupAgnetControlPlaneTestDB(t) - resetAgnetControlPlaneState(t) +func TestAgentRuntimeShadowCreateTreatsSuccessFalseAsFailure(t *testing.T) { + db := setupAgentControlPlaneTestDB(t) + resetAgentControlPlaneState(t) server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") @@ -564,11 +564,11 @@ func TestAgnetRuntimeShadowCreateTreatsSuccessFalseAsFailure(t *testing.T) { })) defer server.Close() - t.Setenv("AGNET_RUNTIME_ENABLED", "true") - t.Setenv("AGNET_RUNTIME_ASYNC", "false") - t.Setenv("AGNET_RUNTIME_BASE_URL", server.URL) + t.Setenv("AGENT_RUNTIME_ENABLED", "true") + t.Setenv("AGENT_RUNTIME_ASYNC", "false") + t.Setenv("AGENT_RUNTIME_BASE_URL", server.URL) - recorder, envelope := postAgnetCreateDeployment(t, baseAgnetResourceGrantPlan()) + recorder, envelope := postAgentCreateDeployment(t, baseAgentResourceGrantPlan()) require.Equal(t, http.StatusOK, recorder.Code) require.True(t, envelope.Success) @@ -576,48 +576,48 @@ func TestAgnetRuntimeShadowCreateTreatsSuccessFalseAsFailure(t *testing.T) { require.NoError(t, common.Unmarshal(recorder.Body.Bytes(), &createBody)) deploymentID := createBody["data"].(map[string]any)["deployment_id"].(string) - var stored model.AgnetDeployment + var stored model.AgentDeployment require.NoError(t, db.Where("deployment_id = ?", deploymentID).First(&stored).Error) - require.Equal(t, agnetRuntimeStateFailed, stored.RuntimeState) + require.Equal(t, agentRuntimeStateFailed, stored.RuntimeState) require.Contains(t, stored.FailureReason, "sub mode rejected") } -func TestAgnetCreateDeploymentRejectsInvalidSubMode(t *testing.T) { - setupAgnetControlPlaneTestDB(t) - resetAgnetControlPlaneState(t) +func TestAgentCreateDeploymentRejectsInvalidSubMode(t *testing.T) { + setupAgentControlPlaneTestDB(t) + resetAgentControlPlaneState(t) - plan := baseAgnetResourceGrantPlan() + plan := baseAgentResourceGrantPlan() plan.SubMode = "scrum" - _, envelope := postAgnetCreateDeployment(t, plan) + _, envelope := postAgentCreateDeployment(t, plan) require.False(t, envelope.Success) require.Equal(t, "POLICY_REJECTED", envelope.Error.Code) - require.Empty(t, agnetDeployments) + require.Empty(t, agentDeployments) } -func TestAgnetCreateDeploymentRejectsNonAzureSecretRef(t *testing.T) { - setupAgnetControlPlaneTestDB(t) - resetAgnetControlPlaneState(t) +func TestAgentCreateDeploymentRejectsNonAzureSecretRef(t *testing.T) { + setupAgentControlPlaneTestDB(t) + resetAgentControlPlaneState(t) - plan := baseAgnetResourceGrantPlan() + plan := baseAgentResourceGrantPlan() plan.Agents[0].ResourceGrants[0].SecretRef = "vault:heicode/repo-main" - recorder, envelope := postAgnetCreateDeployment(t, plan) + recorder, envelope := postAgentCreateDeployment(t, plan) require.Equal(t, http.StatusOK, recorder.Code) require.False(t, envelope.Success) require.Equal(t, "SECRET_REF_INVALID", envelope.Error.Code) } -func TestAgnetCreateDeploymentAcceptsTopLevelResourceGrants(t *testing.T) { - setupAgnetControlPlaneTestDB(t) - resetAgnetControlPlaneState(t) +func TestAgentCreateDeploymentAcceptsTopLevelResourceGrants(t *testing.T) { + setupAgentControlPlaneTestDB(t) + resetAgentControlPlaneState(t) - plan := baseAgnetResourceGrantPlan() + plan := baseAgentResourceGrantPlan() plan.ResourceGrants = plan.Agents[0].ResourceGrants plan.Agents[0].ResourceGrants = nil - recorder, envelope := postAgnetCreateDeployment(t, plan) + recorder, envelope := postAgentCreateDeployment(t, plan) require.Equal(t, http.StatusOK, recorder.Code) require.True(t, envelope.Success) @@ -626,11 +626,11 @@ func TestAgnetCreateDeploymentAcceptsTopLevelResourceGrants(t *testing.T) { require.Contains(t, recorder.Body.String(), `"secret_ref":"azkv://heicode-kv.vault.azure.net/secrets/user-p1-project-main-res-git-main"`) } -func TestAgnetUserDeploymentForcesAuthenticatedUserScope(t *testing.T) { - db := setupAgnetControlPlaneTestDB(t) - resetAgnetControlPlaneState(t) +func TestAgentUserDeploymentForcesAuthenticatedUserScope(t *testing.T) { + db := setupAgentControlPlaneTestDB(t) + resetAgentControlPlaneState(t) - plan := baseAgnetResourceGrantPlan() + plan := baseAgentResourceGrantPlan() plan.UserContext.UserID = "" plan.UserContext.ChannelID = "" for agentIdx := range plan.Agents { @@ -639,7 +639,7 @@ func TestAgnetUserDeploymentForcesAuthenticatedUserScope(t *testing.T) { } } - recorder, envelope := postAgnetCreateUserDeployment(t, 7, plan) + recorder, envelope := postAgentCreateUserDeployment(t, 7, plan) require.Equal(t, http.StatusOK, recorder.Code) require.True(t, envelope.Success) @@ -647,7 +647,7 @@ func TestAgnetUserDeploymentForcesAuthenticatedUserScope(t *testing.T) { require.NoError(t, common.Unmarshal(recorder.Body.Bytes(), &createBody)) deploymentID := createBody["data"].(map[string]any)["deployment_id"].(string) - var stored model.AgnetDeployment + var stored model.AgentDeployment require.NoError(t, db.Where("deployment_id = ?", deploymentID).First(&stored).Error) require.Equal(t, "7", stored.UserID) require.Equal(t, "development", stored.ChannelID) @@ -655,46 +655,46 @@ func TestAgnetUserDeploymentForcesAuthenticatedUserScope(t *testing.T) { require.NotContains(t, stored.PlanJSON, `"user_id":"user-p1"`) } -func TestAgnetUserDeploymentRejectsMismatchedUserScope(t *testing.T) { - setupAgnetControlPlaneTestDB(t) - resetAgnetControlPlaneState(t) +func TestAgentUserDeploymentRejectsMismatchedUserScope(t *testing.T) { + setupAgentControlPlaneTestDB(t) + resetAgentControlPlaneState(t) - plan := baseAgnetResourceGrantPlan() + plan := baseAgentResourceGrantPlan() plan.UserContext.UserID = "8" - _, envelope := postAgnetCreateUserDeployment(t, 7, plan) + _, envelope := postAgentCreateUserDeployment(t, 7, plan) require.False(t, envelope.Success) require.Equal(t, "USER_CONTEXT_FORBIDDEN", envelope.Error.Code) - require.Empty(t, agnetDeployments) + require.Empty(t, agentDeployments) } -func TestAgnetUserDeploymentListIsScopedToAuthenticatedUser(t *testing.T) { - setupAgnetControlPlaneTestDB(t) - resetAgnetControlPlaneState(t) +func TestAgentUserDeploymentListIsScopedToAuthenticatedUser(t *testing.T) { + setupAgentControlPlaneTestDB(t) + resetAgentControlPlaneState(t) - user7Plan := baseAgnetResourceGrantPlan() + user7Plan := baseAgentResourceGrantPlan() user7Plan.UserContext.UserID = "7" user7Plan.Metadata.CorrelationID = "corr-user-7" for idx := range user7Plan.Agents[0].ResourceGrants { user7Plan.Agents[0].ResourceGrants[idx].UserID = "7" } - _, user7Envelope := postAgnetCreateDeployment(t, user7Plan) + _, user7Envelope := postAgentCreateDeployment(t, user7Plan) require.True(t, user7Envelope.Success) - user8Plan := baseAgnetResourceGrantPlan() + user8Plan := baseAgentResourceGrantPlan() user8Plan.UserContext.UserID = "8" user8Plan.Metadata.CorrelationID = "corr-user-8" for idx := range user8Plan.Agents[0].ResourceGrants { user8Plan.Agents[0].ResourceGrants[idx].UserID = "8" } - _, user8Envelope := postAgnetCreateDeployment(t, user8Plan) + _, user8Envelope := postAgentCreateDeployment(t, user8Plan) require.True(t, user8Envelope.Success) listRecorder := httptest.NewRecorder() listCtx, _ := gin.CreateTestContext(listRecorder) listCtx.Set("id", 7) - listCtx.Request = httptest.NewRequest(http.MethodGet, "/api/agnet/user/deployments", nil) - AgnetListUserDeployments(listCtx) + listCtx.Request = httptest.NewRequest(http.MethodGet, "/api/agent/user/deployments", nil) + AgentListUserDeployments(listCtx) require.Equal(t, http.StatusOK, listRecorder.Code) var listBody map[string]any @@ -709,16 +709,16 @@ func TestAgnetUserDeploymentListIsScopedToAuthenticatedUser(t *testing.T) { require.Equal(t, "7", userContext["user_id"]) } -func TestAgnetTaskDeploymentDraftBuildsSafePlan(t *testing.T) { - setupAgnetControlPlaneTestDB(t) - resetAgnetControlPlaneState(t) +func TestAgentTaskDeploymentDraftBuildsSafePlan(t *testing.T) { + setupAgentControlPlaneTestDB(t) + resetAgentControlPlaneState(t) body := `{ "task":{"id":"task_bridge_1","name":"Ship Manager sub mode","intent":"Need a scoped implementation","status":"running","card":{"goal":"Implement the Manager-side sub task flow"}}, "sub_mode":"waterfall", "binding_scope":"repo-main", "role_templates":["backend"], - "default_model_id":"agnet-model-builder", + "default_model_id":"agent-model-builder", "budget":{"max_tokens":10000,"max_cost_usd":2,"max_duration_sec":900} }` recorder := httptest.NewRecorder() @@ -726,10 +726,10 @@ func TestAgnetTaskDeploymentDraftBuildsSafePlan(t *testing.T) { ctx.Set("id", 7) ctx.Set("group", "development") ctx.Params = gin.Params{{Key: "task_id", Value: "task_bridge_1"}} - ctx.Request = httptest.NewRequest(http.MethodPost, "/api/agnet/user/tasks/task_bridge_1/deployment-draft", strings.NewReader(body)) + ctx.Request = httptest.NewRequest(http.MethodPost, "/api/agent/user/tasks/task_bridge_1/deployment-draft", strings.NewReader(body)) ctx.Request.Header.Set("Content-Type", "application/json") - AgnetCreateTaskDeploymentDraft(ctx) + AgentCreateTaskDeploymentDraft(ctx) require.Equal(t, http.StatusOK, recorder.Code) require.NotContains(t, strings.ToLower(recorder.Body.String()), "password") @@ -752,35 +752,35 @@ func TestAgnetTaskDeploymentDraftBuildsSafePlan(t *testing.T) { require.Equal(t, "7", grant["user_id"]) } -func TestAgnetTaskDeploymentDraftRequiresTaskSnapshot(t *testing.T) { - setupAgnetControlPlaneTestDB(t) - resetAgnetControlPlaneState(t) +func TestAgentTaskDeploymentDraftRequiresTaskSnapshot(t *testing.T) { + setupAgentControlPlaneTestDB(t) + resetAgentControlPlaneState(t) recorder := httptest.NewRecorder() ctx, _ := gin.CreateTestContext(recorder) ctx.Set("id", 7) ctx.Params = gin.Params{{Key: "task_id", Value: "task_missing"}} - ctx.Request = httptest.NewRequest(http.MethodPost, "/api/agnet/user/tasks/task_missing/deployment-draft", strings.NewReader(`{"sub_mode":"agile"}`)) + ctx.Request = httptest.NewRequest(http.MethodPost, "/api/agent/user/tasks/task_missing/deployment-draft", strings.NewReader(`{"sub_mode":"agile"}`)) ctx.Request.Header.Set("Content-Type", "application/json") - AgnetCreateTaskDeploymentDraft(ctx) + AgentCreateTaskDeploymentDraft(ctx) - var envelope agnetCreateTestEnvelope + var envelope agentCreateTestEnvelope require.NoError(t, common.Unmarshal(recorder.Body.Bytes(), &envelope)) require.False(t, envelope.Success) require.Equal(t, "TASK_NOT_FOUND", envelope.Error.Code) } -func TestAgnetTaskDeploymentDraftRejectsCredentialGrantWithoutSecretRef(t *testing.T) { - setupAgnetControlPlaneTestDB(t) - resetAgnetControlPlaneState(t) +func TestAgentTaskDeploymentDraftRejectsCredentialGrantWithoutSecretRef(t *testing.T) { + setupAgentControlPlaneTestDB(t) + resetAgentControlPlaneState(t) body := `{ "task":{"id":"task_bridge_git","name":"Use a git repo","intent":"Need repo access","status":"running"}, "sub_mode":"agile", "binding_scope":"repo-main", "role_templates":["backend"], - "default_model_id":"agnet-model-builder", + "default_model_id":"agent-model-builder", "resource_grants":[{ "resource_id":"repo-1", "resource_type":"git", @@ -791,27 +791,27 @@ func TestAgnetTaskDeploymentDraftRejectsCredentialGrantWithoutSecretRef(t *testi ctx, _ := gin.CreateTestContext(recorder) ctx.Set("id", 7) ctx.Params = gin.Params{{Key: "task_id", Value: "task_bridge_git"}} - ctx.Request = httptest.NewRequest(http.MethodPost, "/api/agnet/user/tasks/task_bridge_git/deployment-draft", strings.NewReader(body)) + ctx.Request = httptest.NewRequest(http.MethodPost, "/api/agent/user/tasks/task_bridge_git/deployment-draft", strings.NewReader(body)) ctx.Request.Header.Set("Content-Type", "application/json") - AgnetCreateTaskDeploymentDraft(ctx) + AgentCreateTaskDeploymentDraft(ctx) - var envelope agnetCreateTestEnvelope + var envelope agentCreateTestEnvelope require.NoError(t, common.Unmarshal(recorder.Body.Bytes(), &envelope)) require.False(t, envelope.Success) require.Equal(t, "RESOURCE_GRANT_SECRET_REF_REQUIRED", envelope.Error.Code) } -func TestAgnetUserDeploymentSimulatedEventsArePersistedAndPrefixed(t *testing.T) { - setupAgnetControlPlaneTestDB(t) - resetAgnetControlPlaneState(t) +func TestAgentUserDeploymentSimulatedEventsArePersistedAndPrefixed(t *testing.T) { + setupAgentControlPlaneTestDB(t) + resetAgentControlPlaneState(t) - plan := baseAgnetResourceGrantPlan() + plan := baseAgentResourceGrantPlan() plan.UserContext.UserID = "7" for idx := range plan.Agents[0].ResourceGrants { plan.Agents[0].ResourceGrants[idx].UserID = "7" } - createRecorder, createEnvelope := postAgnetCreateUserDeployment(t, 7, plan) + createRecorder, createEnvelope := postAgentCreateUserDeployment(t, 7, plan) require.Equal(t, http.StatusOK, createRecorder.Code) require.True(t, createEnvelope.Success) var createBody map[string]any @@ -823,38 +823,38 @@ func TestAgnetUserDeploymentSimulatedEventsArePersistedAndPrefixed(t *testing.T) simCtx.Set("id", 7) simCtx.Set("role", common.RoleAdminUser) simCtx.Params = gin.Params{{Key: "deployment_id", Value: deploymentID}} - simCtx.Request = httptest.NewRequest(http.MethodPost, "/api/agnet/user/deployments/"+deploymentID+"/simulate-events", strings.NewReader(`{"events":["deployment.started","simulation.agent.done"]}`)) + simCtx.Request = httptest.NewRequest(http.MethodPost, "/api/agent/user/deployments/"+deploymentID+"/simulate-events", strings.NewReader(`{"events":["deployment.started","simulation.agent.done"]}`)) simCtx.Request.Header.Set("Content-Type", "application/json") - AgnetSimulateUserDeploymentEvents(simCtx) + AgentSimulateUserDeploymentEvents(simCtx) require.Equal(t, http.StatusOK, simRecorder.Code) require.Contains(t, simRecorder.Body.String(), `"simulated":true`) require.Contains(t, simRecorder.Body.String(), `"simulation.deployment.started"`) require.Contains(t, simRecorder.Body.String(), `"simulation.agent.done"`) - resetAgnetControlPlaneState(t) + resetAgentControlPlaneState(t) eventsRecorder := httptest.NewRecorder() eventsCtx, _ := gin.CreateTestContext(eventsRecorder) eventsCtx.Set("id", 7) eventsCtx.Params = gin.Params{{Key: "deployment_id", Value: deploymentID}} - eventsCtx.Request = httptest.NewRequest(http.MethodGet, "/api/agnet/user/deployments/"+deploymentID+"/events", nil) - AgnetListUserDeploymentEvents(eventsCtx) + eventsCtx.Request = httptest.NewRequest(http.MethodGet, "/api/agent/user/deployments/"+deploymentID+"/events", nil) + AgentListUserDeploymentEvents(eventsCtx) require.Equal(t, http.StatusOK, eventsRecorder.Code) require.Contains(t, eventsRecorder.Body.String(), `"simulation.deployment.started"`) require.Contains(t, eventsRecorder.Body.String(), `"simulation.agent.done"`) } -func TestAgnetUserDeploymentSimulationRejectsOtherUsersDeployment(t *testing.T) { - setupAgnetControlPlaneTestDB(t) - resetAgnetControlPlaneState(t) +func TestAgentUserDeploymentSimulationRejectsOtherUsersDeployment(t *testing.T) { + setupAgentControlPlaneTestDB(t) + resetAgentControlPlaneState(t) - plan := baseAgnetResourceGrantPlan() + plan := baseAgentResourceGrantPlan() plan.UserContext.UserID = "7" for idx := range plan.Agents[0].ResourceGrants { plan.Agents[0].ResourceGrants[idx].UserID = "7" } - createRecorder, createEnvelope := postAgnetCreateUserDeployment(t, 7, plan) + createRecorder, createEnvelope := postAgentCreateUserDeployment(t, 7, plan) require.Equal(t, http.StatusOK, createRecorder.Code) require.True(t, createEnvelope.Success) var createBody map[string]any @@ -866,26 +866,26 @@ func TestAgnetUserDeploymentSimulationRejectsOtherUsersDeployment(t *testing.T) simCtx.Set("id", 8) simCtx.Set("role", common.RoleAdminUser) simCtx.Params = gin.Params{{Key: "deployment_id", Value: deploymentID}} - simCtx.Request = httptest.NewRequest(http.MethodPost, "/api/agnet/user/deployments/"+deploymentID+"/simulate-events", strings.NewReader(`{}`)) + simCtx.Request = httptest.NewRequest(http.MethodPost, "/api/agent/user/deployments/"+deploymentID+"/simulate-events", strings.NewReader(`{}`)) simCtx.Request.Header.Set("Content-Type", "application/json") - AgnetSimulateUserDeploymentEvents(simCtx) + AgentSimulateUserDeploymentEvents(simCtx) - var envelope agnetCreateTestEnvelope + var envelope agentCreateTestEnvelope require.NoError(t, common.Unmarshal(simRecorder.Body.Bytes(), &envelope)) require.False(t, envelope.Success) require.Equal(t, "DEPLOYMENT_FORBIDDEN", envelope.Error.Code) } -func TestAgnetUserDeploymentSimulationIsDisabledForRegularUserByDefault(t *testing.T) { - setupAgnetControlPlaneTestDB(t) - resetAgnetControlPlaneState(t) +func TestAgentUserDeploymentSimulationIsDisabledForRegularUserByDefault(t *testing.T) { + setupAgentControlPlaneTestDB(t) + resetAgentControlPlaneState(t) - plan := baseAgnetResourceGrantPlan() + plan := baseAgentResourceGrantPlan() plan.UserContext.UserID = "7" for idx := range plan.Agents[0].ResourceGrants { plan.Agents[0].ResourceGrants[idx].UserID = "7" } - createRecorder, createEnvelope := postAgnetCreateUserDeployment(t, 7, plan) + createRecorder, createEnvelope := postAgentCreateUserDeployment(t, 7, plan) require.Equal(t, http.StatusOK, createRecorder.Code) require.True(t, createEnvelope.Success) var createBody map[string]any @@ -897,26 +897,26 @@ func TestAgnetUserDeploymentSimulationIsDisabledForRegularUserByDefault(t *testi simCtx.Set("id", 7) simCtx.Set("role", common.RoleCommonUser) simCtx.Params = gin.Params{{Key: "deployment_id", Value: deploymentID}} - simCtx.Request = httptest.NewRequest(http.MethodPost, "/api/agnet/user/deployments/"+deploymentID+"/simulate-events", strings.NewReader(`{}`)) + simCtx.Request = httptest.NewRequest(http.MethodPost, "/api/agent/user/deployments/"+deploymentID+"/simulate-events", strings.NewReader(`{}`)) simCtx.Request.Header.Set("Content-Type", "application/json") - AgnetSimulateUserDeploymentEvents(simCtx) + AgentSimulateUserDeploymentEvents(simCtx) - var envelope agnetCreateTestEnvelope + var envelope agentCreateTestEnvelope require.NoError(t, common.Unmarshal(simRecorder.Body.Bytes(), &envelope)) require.False(t, envelope.Success) require.Equal(t, "SIMULATION_DISABLED", envelope.Error.Code) } -func TestAgnetUserDeploymentDefaultSimulationCreatesRelatedRecords(t *testing.T) { - setupAgnetControlPlaneTestDB(t) - resetAgnetControlPlaneState(t) +func TestAgentUserDeploymentDefaultSimulationCreatesRelatedRecords(t *testing.T) { + setupAgentControlPlaneTestDB(t) + resetAgentControlPlaneState(t) - plan := baseAgnetResourceGrantPlan() + plan := baseAgentResourceGrantPlan() plan.UserContext.UserID = "7" for idx := range plan.Agents[0].ResourceGrants { plan.Agents[0].ResourceGrants[idx].UserID = "7" } - createRecorder, createEnvelope := postAgnetCreateUserDeployment(t, 7, plan) + createRecorder, createEnvelope := postAgentCreateUserDeployment(t, 7, plan) require.Equal(t, http.StatusOK, createRecorder.Code) require.True(t, createEnvelope.Success) var createBody map[string]any @@ -928,9 +928,9 @@ func TestAgnetUserDeploymentDefaultSimulationCreatesRelatedRecords(t *testing.T) simCtx.Set("id", 7) simCtx.Set("role", common.RoleAdminUser) simCtx.Params = gin.Params{{Key: "deployment_id", Value: deploymentID}} - simCtx.Request = httptest.NewRequest(http.MethodPost, "/api/agnet/user/deployments/"+deploymentID+"/simulate-events", strings.NewReader(`{}`)) + simCtx.Request = httptest.NewRequest(http.MethodPost, "/api/agent/user/deployments/"+deploymentID+"/simulate-events", strings.NewReader(`{}`)) simCtx.Request.Header.Set("Content-Type", "application/json") - AgnetSimulateUserDeploymentEvents(simCtx) + AgentSimulateUserDeploymentEvents(simCtx) require.Equal(t, http.StatusOK, simRecorder.Code) require.Contains(t, simRecorder.Body.String(), `"simulated":true`) @@ -938,8 +938,8 @@ func TestAgnetUserDeploymentDefaultSimulationCreatesRelatedRecords(t *testing.T) artifactCtx, _ := gin.CreateTestContext(artifactRecorder) artifactCtx.Set("id", 7) artifactCtx.Params = gin.Params{{Key: "deployment_id", Value: deploymentID}} - artifactCtx.Request = httptest.NewRequest(http.MethodGet, "/api/agnet/user/deployments/"+deploymentID+"/artifacts", nil) - AgnetListUserDeploymentArtifacts(artifactCtx) + artifactCtx.Request = httptest.NewRequest(http.MethodGet, "/api/agent/user/deployments/"+deploymentID+"/artifacts", nil) + AgentListUserDeploymentArtifacts(artifactCtx) require.Equal(t, http.StatusOK, artifactRecorder.Code) require.Contains(t, artifactRecorder.Body.String(), `"artifact_type":"test_report"`) require.Contains(t, artifactRecorder.Body.String(), `"artifact_id":"art-sim-`) @@ -947,8 +947,8 @@ func TestAgnetUserDeploymentDefaultSimulationCreatesRelatedRecords(t *testing.T) approvalRecorder := httptest.NewRecorder() approvalCtx, _ := gin.CreateTestContext(approvalRecorder) approvalCtx.Set("id", 7) - approvalCtx.Request = httptest.NewRequest(http.MethodGet, "/api/agnet/approvals?deployment_id="+deploymentID+"&status=pending", nil) - ListAgnetApprovalRequests(approvalCtx) + approvalCtx.Request = httptest.NewRequest(http.MethodGet, "/api/agent/approvals?deployment_id="+deploymentID+"&status=pending", nil) + ListAgentApprovalRequests(approvalCtx) require.Equal(t, http.StatusOK, approvalRecorder.Code) require.Contains(t, approvalRecorder.Body.String(), `"approval_id":"sim-appr-`) require.Contains(t, approvalRecorder.Body.String(), `"status":"pending"`) @@ -958,12 +958,12 @@ func TestAgnetUserDeploymentDefaultSimulationCreatesRelatedRecords(t *testing.T) timelineCtx, _ := gin.CreateTestContext(timelineRecorder) timelineCtx.Set("id", 7) timelineCtx.Params = gin.Params{{Key: "deployment_id", Value: deploymentID}} - timelineCtx.Request = httptest.NewRequest(http.MethodGet, "/api/agnet/user/deployments/"+deploymentID+"/timeline", nil) - AgnetGetUserDeploymentTimeline(timelineCtx) + timelineCtx.Request = httptest.NewRequest(http.MethodGet, "/api/agent/user/deployments/"+deploymentID+"/timeline", nil) + AgentGetUserDeploymentTimeline(timelineCtx) require.Equal(t, http.StatusOK, timelineRecorder.Code) require.Contains(t, timelineRecorder.Body.String(), `"event_type":"artifact.created"`) require.Contains(t, timelineRecorder.Body.String(), `"event_type":"approval.requested"`) - require.Contains(t, timelineRecorder.Body.String(), `"source":"agnet-simulator"`) + require.Contains(t, timelineRecorder.Body.String(), `"source":"agent-simulator"`) require.Contains(t, timelineRecorder.Body.String(), `"event_type":"task.created"`) require.Contains(t, timelineRecorder.Body.String(), `"event_type":"task.blocked"`) require.Contains(t, timelineRecorder.Body.String(), `"event_type":"handoff.requested"`) @@ -971,16 +971,16 @@ func TestAgnetUserDeploymentDefaultSimulationCreatesRelatedRecords(t *testing.T) require.Contains(t, timelineRecorder.Body.String(), `"event_type":"task.retried"`) } -func TestAgnetUserSwarmsAdapterCreatesScopedDeployment(t *testing.T) { - setupAgnetControlPlaneTestDB(t) - resetAgnetControlPlaneState(t) +func TestAgentUserSwarmsAdapterCreatesScopedDeployment(t *testing.T) { + setupAgentControlPlaneTestDB(t) + resetAgentControlPlaneState(t) - plan := baseAgnetResourceGrantPlan() + plan := baseAgentResourceGrantPlan() plan.UserContext.UserID = "" for idx := range plan.Agents[0].ResourceGrants { plan.Agents[0].ResourceGrants[idx].UserID = "" } - body, err := common.Marshal(agnetDeploymentRequest{Plan: plan}) + body, err := common.Marshal(agentDeploymentRequest{Plan: plan}) require.NoError(t, err) recorder := httptest.NewRecorder() @@ -990,7 +990,7 @@ func TestAgnetUserSwarmsAdapterCreatesScopedDeployment(t *testing.T) { ctx.Request = httptest.NewRequest(http.MethodPost, "/api/swarms", strings.NewReader(string(body))) ctx.Request.Header.Set("Content-Type", "application/json") - AgnetCreateUserSwarm(ctx) + AgentCreateUserSwarm(ctx) require.Equal(t, http.StatusOK, recorder.Code) require.Contains(t, recorder.Body.String(), `"swarm_id"`) @@ -998,17 +998,17 @@ func TestAgnetUserSwarmsAdapterCreatesScopedDeployment(t *testing.T) { require.Contains(t, recorder.Body.String(), `"user_id":"7"`) } -func TestAgnetCallbackRejectsInvalidTaskEventSchema(t *testing.T) { - setupAgnetControlPlaneTestDB(t) - resetAgnetControlPlaneState(t) - t.Setenv("AGNET_CALLBACK_TOKEN", "callback-token") +func TestAgentCallbackRejectsInvalidTaskEventSchema(t *testing.T) { + setupAgentControlPlaneTestDB(t) + resetAgentControlPlaneState(t) + t.Setenv("AGENT_CALLBACK_TOKEN", "callback-token") - plan := baseAgnetResourceGrantPlan() + plan := baseAgentResourceGrantPlan() plan.UserContext.UserID = "7" for idx := range plan.Agents[0].ResourceGrants { plan.Agents[0].ResourceGrants[idx].UserID = "7" } - createRecorder, createEnvelope := postAgnetCreateUserDeployment(t, 7, plan) + createRecorder, createEnvelope := postAgentCreateUserDeployment(t, 7, plan) require.Equal(t, http.StatusOK, createRecorder.Code) require.True(t, createEnvelope.Success) var createBody map[string]any @@ -1027,29 +1027,29 @@ func TestAgnetCallbackRejectsInvalidTaskEventSchema(t *testing.T) { recorder := httptest.NewRecorder() ctx, _ := gin.CreateTestContext(recorder) - ctx.Request = httptest.NewRequest(http.MethodPost, "/api/agnet/callbacks/swarm-events", strings.NewReader(body)) + ctx.Request = httptest.NewRequest(http.MethodPost, "/api/agent/callbacks/runtime-events", strings.NewReader(body)) ctx.Request.Header.Set("Content-Type", "application/json") - ctx.Request.Header.Set("X-Agnet-Service-Token", "callback-token") - AgnetReceiveSwarmEventCallback(ctx) + ctx.Request.Header.Set("X-Agent-Service-Token", "callback-token") + AgentReceiveRuntimeEventCallback(ctx) - var envelope agnetCreateTestEnvelope + var envelope agentCreateTestEnvelope require.NoError(t, common.Unmarshal(recorder.Body.Bytes(), &envelope)) require.False(t, envelope.Success) require.Equal(t, "CALLBACK_SCHEMA_INVALID", envelope.Error.Code) require.Contains(t, envelope.Error.Message, "to_role") } -func TestAgnetCallbackStoresEventArtifactAndIsIdempotent(t *testing.T) { - db := setupAgnetControlPlaneTestDB(t) - resetAgnetControlPlaneState(t) - t.Setenv("AGNET_CALLBACK_TOKEN", "callback-token") +func TestAgentCallbackStoresEventArtifactAndIsIdempotent(t *testing.T) { + db := setupAgentControlPlaneTestDB(t) + resetAgentControlPlaneState(t) + t.Setenv("AGENT_CALLBACK_TOKEN", "callback-token") - plan := baseAgnetResourceGrantPlan() + plan := baseAgentResourceGrantPlan() plan.UserContext.UserID = "7" for idx := range plan.Agents[0].ResourceGrants { plan.Agents[0].ResourceGrants[idx].UserID = "7" } - createRecorder, createEnvelope := postAgnetCreateUserDeployment(t, 7, plan) + createRecorder, createEnvelope := postAgentCreateUserDeployment(t, 7, plan) require.True(t, createEnvelope.Success) var createBody map[string]any require.NoError(t, common.Unmarshal(createRecorder.Body.Bytes(), &createBody)) @@ -1074,33 +1074,33 @@ func TestAgnetCallbackStoresEventArtifactAndIsIdempotent(t *testing.T) { for i := 0; i < 2; i++ { recorder := httptest.NewRecorder() ctx, _ := gin.CreateTestContext(recorder) - ctx.Request = httptest.NewRequest(http.MethodPost, "/api/agnet/callbacks/swarm-events", strings.NewReader(body)) + ctx.Request = httptest.NewRequest(http.MethodPost, "/api/agent/callbacks/runtime-events", strings.NewReader(body)) ctx.Request.Header.Set("Content-Type", "application/json") - ctx.Request.Header.Set("X-Agnet-Service-Token", "callback-token") - AgnetReceiveSwarmEventCallback(ctx) + ctx.Request.Header.Set("X-Agent-Service-Token", "callback-token") + AgentReceiveRuntimeEventCallback(ctx) require.Equal(t, http.StatusOK, recorder.Code) require.Contains(t, recorder.Body.String(), `"success":true`) } var callbackCount int64 - require.NoError(t, db.Model(&model.AgnetCallbackEvent{}).Where("event_id = ?", "evt-callback-1").Count(&callbackCount).Error) + require.NoError(t, db.Model(&model.AgentCallbackEvent{}).Where("event_id = ?", "evt-callback-1").Count(&callbackCount).Error) require.Equal(t, int64(1), callbackCount) var artifactCount int64 - require.NoError(t, db.Model(&model.AgnetArtifact{}).Where("artifact_id = ?", "art-1").Count(&artifactCount).Error) + require.NoError(t, db.Model(&model.AgentArtifact{}).Where("artifact_id = ?", "art-1").Count(&artifactCount).Error) require.Equal(t, int64(1), artifactCount) } -func TestAgnetCallbackAcceptsDocumentedHMACPayloadArtifact(t *testing.T) { - db := setupAgnetControlPlaneTestDB(t) - resetAgnetControlPlaneState(t) - t.Setenv("AGNET_CALLBACK_SIGNING_SECRET", "callback-signing-secret") +func TestAgentCallbackAcceptsDocumentedHMACPayloadArtifact(t *testing.T) { + db := setupAgentControlPlaneTestDB(t) + resetAgentControlPlaneState(t) + t.Setenv("AGENT_CALLBACK_SIGNING_SECRET", "callback-signing-secret") - plan := baseAgnetResourceGrantPlan() + plan := baseAgentResourceGrantPlan() plan.UserContext.UserID = "7" for idx := range plan.Agents[0].ResourceGrants { plan.Agents[0].ResourceGrants[idx].UserID = "7" } - createRecorder, createEnvelope := postAgnetCreateUserDeployment(t, 7, plan) + createRecorder, createEnvelope := postAgentCreateUserDeployment(t, 7, plan) require.True(t, createEnvelope.Success) var createBody map[string]any require.NoError(t, common.Unmarshal(createRecorder.Body.Bytes(), &createBody)) @@ -1131,33 +1131,33 @@ func TestAgnetCallbackAcceptsDocumentedHMACPayloadArtifact(t *testing.T) { recorder := httptest.NewRecorder() ctx, _ := gin.CreateTestContext(recorder) - ctx.Request = httptest.NewRequest(http.MethodPost, "/api/agnet/callbacks/swarm-events", strings.NewReader(body)) + ctx.Request = httptest.NewRequest(http.MethodPost, "/api/agent/callbacks/runtime-events", strings.NewReader(body)) ctx.Request.Header.Set("Content-Type", "application/json") - ctx.Request.Header.Set("X-Agnet-Event-Id", eventID) - ctx.Request.Header.Set("X-Agnet-Timestamp", timestamp) - ctx.Request.Header.Set("X-Agnet-Signature", signature) + ctx.Request.Header.Set("X-Agent-Event-Id", eventID) + ctx.Request.Header.Set("X-Agent-Timestamp", timestamp) + ctx.Request.Header.Set("X-Agent-Signature", signature) ctx.Request.Header.Set("X-Correlation-ID", "corr-doc-hmac") - AgnetReceiveSwarmEventCallback(ctx) + AgentReceiveRuntimeEventCallback(ctx) require.Equal(t, http.StatusOK, recorder.Code) require.Contains(t, recorder.Body.String(), `"success":true`) - var callback model.AgnetCallbackEvent + var callback model.AgentCallbackEvent require.NoError(t, db.Where("event_id = ?", eventID).First(&callback).Error) require.Equal(t, "swarm-doc-1", callback.SwarmID) require.Equal(t, "agi-backend-1", callback.AgentInstanceID) require.Equal(t, "2026-05-26T10:40:00Z", callback.OccurredAt) - var artifact model.AgnetArtifact + var artifact model.AgentArtifact require.NoError(t, db.Where("artifact_id = ?", "art-doc-payload-1").First(&artifact).Error) require.Equal(t, "code_patch", artifact.ArtifactType) require.Contains(t, artifact.MetadataJSON, "artifact_ready") } -func TestAgnetCallbackHMACCanUseAzureKeyVaultSigningSecretRef(t *testing.T) { - db := setupAgnetControlPlaneTestDB(t) - resetAgnetControlPlaneState(t) +func TestAgentCallbackHMACCanUseAzureKeyVaultSigningSecretRef(t *testing.T) { + db := setupAgentControlPlaneTestDB(t) + resetAgentControlPlaneState(t) tokenServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { require.Equal(t, "true", r.Header.Get("Metadata")) @@ -1174,14 +1174,14 @@ func TestAgnetCallbackHMACCanUseAzureKeyVaultSigningSecretRef(t *testing.T) { defer vaultServer.Close() t.Setenv("AZURE_KEY_VAULT_URL", vaultServer.URL) t.Setenv("AZURE_MANAGED_IDENTITY_TOKEN_URL", tokenServer.URL) - t.Setenv("AGNET_CALLBACK_SIGNING_SECRET_REF", fmt.Sprintf("azkv://%s/secrets/callback-signing", strings.TrimPrefix(vaultServer.URL, "http://"))) + t.Setenv("AGENT_CALLBACK_SIGNING_SECRET_REF", fmt.Sprintf("azkv://%s/secrets/callback-signing", strings.TrimPrefix(vaultServer.URL, "http://"))) - plan := baseAgnetResourceGrantPlan() + plan := baseAgentResourceGrantPlan() plan.UserContext.UserID = "7" for idx := range plan.Agents[0].ResourceGrants { plan.Agents[0].ResourceGrants[idx].UserID = "7" } - createRecorder, createEnvelope := postAgnetCreateUserDeployment(t, 7, plan) + createRecorder, createEnvelope := postAgentCreateUserDeployment(t, 7, plan) require.True(t, createEnvelope.Success) var createBody map[string]any require.NoError(t, common.Unmarshal(createRecorder.Body.Bytes(), &createBody)) @@ -1196,40 +1196,40 @@ func TestAgnetCallbackHMACCanUseAzureKeyVaultSigningSecretRef(t *testing.T) { recorder := httptest.NewRecorder() ctx, _ := gin.CreateTestContext(recorder) - ctx.Request = httptest.NewRequest(http.MethodPost, "/api/agnet/callbacks/swarm-events", strings.NewReader(body)) + ctx.Request = httptest.NewRequest(http.MethodPost, "/api/agent/callbacks/runtime-events", strings.NewReader(body)) ctx.Request.Header.Set("Content-Type", "application/json") - ctx.Request.Header.Set("X-Agnet-Event-Id", eventID) - ctx.Request.Header.Set("X-Agnet-Timestamp", timestamp) - ctx.Request.Header.Set("X-Agnet-Signature", signature) + ctx.Request.Header.Set("X-Agent-Event-Id", eventID) + ctx.Request.Header.Set("X-Agent-Timestamp", timestamp) + ctx.Request.Header.Set("X-Agent-Signature", signature) - AgnetReceiveSwarmEventCallback(ctx) + AgentReceiveRuntimeEventCallback(ctx) require.Equal(t, http.StatusOK, recorder.Code) require.Contains(t, recorder.Body.String(), `"success":true`) - var callback model.AgnetCallbackEvent + var callback model.AgentCallbackEvent require.NoError(t, db.Where("event_id = ?", eventID).First(&callback).Error) } -func TestAgnetCallbackUsesSwarmIDFallbackAndCreatesApproval(t *testing.T) { - db := setupAgnetControlPlaneTestDB(t) - resetAgnetControlPlaneState(t) - t.Setenv("AGNET_CALLBACK_TOKEN", "callback-token") +func TestAgentCallbackUsesSwarmIDFallbackAndCreatesApproval(t *testing.T) { + db := setupAgentControlPlaneTestDB(t) + resetAgentControlPlaneState(t) + t.Setenv("AGENT_CALLBACK_TOKEN", "callback-token") - plan := baseAgnetResourceGrantPlan() + plan := baseAgentResourceGrantPlan() plan.UserContext.UserID = "7" for idx := range plan.Agents[0].ResourceGrants { plan.Agents[0].ResourceGrants[idx].UserID = "7" } - createRecorder, createEnvelope := postAgnetCreateUserDeployment(t, 7, plan) + createRecorder, createEnvelope := postAgentCreateUserDeployment(t, 7, plan) require.True(t, createEnvelope.Success) var createBody map[string]any require.NoError(t, common.Unmarshal(createRecorder.Body.Bytes(), &createBody)) deploymentID := createBody["data"].(map[string]any)["deployment_id"].(string) swarmID := "runtime-swarm-only-1" - require.NoError(t, db.Model(&model.AgnetDeployment{}). + require.NoError(t, db.Model(&model.AgentDeployment{}). Where("deployment_id = ?", deploymentID). Update("runtime_swarm_id", swarmID).Error) - resetAgnetControlPlaneState(t) + resetAgentControlPlaneState(t) body := `{ "event_id":"evt-approval-swarm-only", @@ -1255,10 +1255,10 @@ func TestAgnetCallbackUsesSwarmIDFallbackAndCreatesApproval(t *testing.T) { for i := 0; i < 2; i++ { recorder := httptest.NewRecorder() ctx, _ := gin.CreateTestContext(recorder) - ctx.Request = httptest.NewRequest(http.MethodPost, "/api/agnet/callbacks/swarm-events", strings.NewReader(body)) + ctx.Request = httptest.NewRequest(http.MethodPost, "/api/agent/callbacks/runtime-events", strings.NewReader(body)) ctx.Request.Header.Set("Content-Type", "application/json") - ctx.Request.Header.Set("X-Agnet-Service-Token", "callback-token") - AgnetReceiveSwarmEventCallback(ctx) + ctx.Request.Header.Set("X-Agent-Service-Token", "callback-token") + AgentReceiveRuntimeEventCallback(ctx) require.Equal(t, http.StatusOK, recorder.Code) require.Contains(t, recorder.Body.String(), `"success":true`) if i == 0 { @@ -1268,13 +1268,13 @@ func TestAgnetCallbackUsesSwarmIDFallbackAndCreatesApproval(t *testing.T) { } } - var callback model.AgnetCallbackEvent + var callback model.AgentCallbackEvent require.NoError(t, db.Where("event_id = ?", "evt-approval-swarm-only").First(&callback).Error) require.Equal(t, deploymentID, callback.DeploymentID) require.Equal(t, swarmID, callback.SwarmID) require.Equal(t, "agi-backend-approval", callback.AgentInstanceID) - var approval model.AgnetApprovalRequest + var approval model.AgentApprovalRequest require.NoError(t, db.Where("approval_id = ?", "runtime-approval-1").First(&approval).Error) require.Equal(t, 7, approval.UserId) require.Equal(t, deploymentID, approval.DeploymentID) @@ -1283,12 +1283,12 @@ func TestAgnetCallbackUsesSwarmIDFallbackAndCreatesApproval(t *testing.T) { require.Equal(t, "azkv://heicode-kv.vault.azure.net/secrets/repo-main", approval.SecretRef) } -func TestAgnetCallbackUsesRuntimeDeploymentIDFallback(t *testing.T) { - db := setupAgnetControlPlaneTestDB(t) - resetAgnetControlPlaneState(t) - t.Setenv("AGNET_CALLBACK_TOKEN", "callback-token") +func TestAgentCallbackUsesRuntimeDeploymentIDFallback(t *testing.T) { + db := setupAgentControlPlaneTestDB(t) + resetAgentControlPlaneState(t) + t.Setenv("AGENT_CALLBACK_TOKEN", "callback-token") - plan := baseAgnetResourceGrantPlan() + plan := baseAgentResourceGrantPlan() plan.UserContext.UserID = "7" plan.BillingContext.DefaultModelID = "model-runtime-smoke" plan.BillingContext.AllowedModelIDs = []string{"model-runtime-smoke", "model-runtime-fallback"} @@ -1296,19 +1296,19 @@ func TestAgnetCallbackUsesRuntimeDeploymentIDFallback(t *testing.T) { for idx := range plan.Agents[0].ResourceGrants { plan.Agents[0].ResourceGrants[idx].UserID = "7" } - createRecorder, createEnvelope := postAgnetCreateUserDeployment(t, 7, plan) + createRecorder, createEnvelope := postAgentCreateUserDeployment(t, 7, plan) require.True(t, createEnvelope.Success) var createBody map[string]any require.NoError(t, common.Unmarshal(createRecorder.Body.Bytes(), &createBody)) deploymentID := createBody["data"].(map[string]any)["deployment_id"].(string) runtimeID := "swm-runtime-deployment-only-1" - require.NoError(t, db.Model(&model.AgnetDeployment{}). + require.NoError(t, db.Model(&model.AgentDeployment{}). Where("deployment_id = ?", deploymentID). Updates(map[string]any{ "runtime_deployment_id": runtimeID, "runtime_swarm_id": runtimeID, }).Error) - resetAgnetControlPlaneState(t) + resetAgentControlPlaneState(t) body := `{ "event_id":"evt-runtime-deployment-fallback", @@ -1326,58 +1326,58 @@ func TestAgnetCallbackUsesRuntimeDeploymentIDFallback(t *testing.T) { recorder := httptest.NewRecorder() ctx, _ := gin.CreateTestContext(recorder) - ctx.Request = httptest.NewRequest(http.MethodPost, "/api/agnet/callbacks/swarm-events", strings.NewReader(body)) + ctx.Request = httptest.NewRequest(http.MethodPost, "/api/agent/callbacks/runtime-events", strings.NewReader(body)) ctx.Request.Header.Set("Content-Type", "application/json") - ctx.Request.Header.Set("X-Agnet-Service-Token", "callback-token") - AgnetReceiveSwarmEventCallback(ctx) + ctx.Request.Header.Set("X-Agent-Service-Token", "callback-token") + AgentReceiveRuntimeEventCallback(ctx) require.Equal(t, http.StatusOK, recorder.Code) require.Contains(t, recorder.Body.String(), `"success":true`) require.Contains(t, recorder.Body.String(), `"deployment_id":"`+deploymentID+`"`) - var callback model.AgnetCallbackEvent + var callback model.AgentCallbackEvent require.NoError(t, db.Where("event_id = ?", "evt-runtime-deployment-fallback").First(&callback).Error) require.Equal(t, deploymentID, callback.DeploymentID) require.Equal(t, runtimeID, callback.SwarmID) require.Equal(t, "7", callback.UserID) - var stored model.AgnetDeployment + var stored model.AgentDeployment require.NoError(t, db.Where("deployment_id = ?", deploymentID).First(&stored).Error) require.Contains(t, stored.PlanJSON, `"default_model_id":"model-runtime-smoke"`) require.Contains(t, stored.PlanJSON, `"allowed_model_ids":["model-runtime-smoke","model-runtime-fallback"]`) require.Contains(t, stored.PlanJSON, `"secret_ref":"azkv://heicode-kv.vault.azure.net/secrets/model-gateway-key"`) } -func TestAgnetCallbackUpdatesDeploymentRuntimeSnapshot(t *testing.T) { - db := setupAgnetControlPlaneTestDB(t) - resetAgnetControlPlaneState(t) - t.Setenv("AGNET_CALLBACK_TOKEN", "callback-token") +func TestAgentCallbackUpdatesDeploymentRuntimeSnapshot(t *testing.T) { + db := setupAgentControlPlaneTestDB(t) + resetAgentControlPlaneState(t) + t.Setenv("AGENT_CALLBACK_TOKEN", "callback-token") - plan := baseAgnetResourceGrantPlan() + plan := baseAgentResourceGrantPlan() plan.UserContext.UserID = "7" for idx := range plan.Agents[0].ResourceGrants { plan.Agents[0].ResourceGrants[idx].UserID = "7" } - createRecorder, createEnvelope := postAgnetCreateUserDeployment(t, 7, plan) + createRecorder, createEnvelope := postAgentCreateUserDeployment(t, 7, plan) require.True(t, createEnvelope.Success) var createBody map[string]any require.NoError(t, common.Unmarshal(createRecorder.Body.Bytes(), &createBody)) deploymentID := createBody["data"].(map[string]any)["deployment_id"].(string) runtimeID := "swm-runtime-state-1" - require.NoError(t, db.Model(&model.AgnetDeployment{}). + require.NoError(t, db.Model(&model.AgentDeployment{}). Where("deployment_id = ?", deploymentID). Updates(map[string]any{ "runtime_deployment_id": runtimeID, "runtime_swarm_id": runtimeID, }).Error) - resetAgnetControlPlaneState(t) + resetAgentControlPlaneState(t) postCallback := func(body string) { recorder := httptest.NewRecorder() ctx, _ := gin.CreateTestContext(recorder) - ctx.Request = httptest.NewRequest(http.MethodPost, "/api/agnet/callbacks/swarm-events", strings.NewReader(body)) + ctx.Request = httptest.NewRequest(http.MethodPost, "/api/agent/callbacks/runtime-events", strings.NewReader(body)) ctx.Request.Header.Set("Content-Type", "application/json") - ctx.Request.Header.Set("X-Agnet-Service-Token", "callback-token") - AgnetReceiveSwarmEventCallback(ctx) + ctx.Request.Header.Set("X-Agent-Service-Token", "callback-token") + AgentReceiveRuntimeEventCallback(ctx) require.Equal(t, http.StatusOK, recorder.Code) require.Contains(t, recorder.Body.String(), `"success":true`) } @@ -1408,14 +1408,14 @@ func TestAgnetCallbackUpdatesDeploymentRuntimeSnapshot(t *testing.T) { "payload":{"status":"completed"} }`) - var stored model.AgnetDeployment + var stored model.AgentDeployment require.NoError(t, db.Where("deployment_id = ?", deploymentID).First(&stored).Error) require.Equal(t, "completed", stored.Status) require.Equal(t, "deploy", stored.Phase) require.Equal(t, "completed", stored.RuntimeState) require.Equal(t, "2026-05-28T12:42:00Z", stored.UpdatedAtText) - var instances []agnetAgentInstance + var instances []agentAgentInstance require.NoError(t, common.UnmarshalJsonStr(stored.AgentInstancesJSON, &instances)) require.Len(t, instances, 1) require.Equal(t, "agi-runtime-builder", instances[0].InstanceID) @@ -1424,49 +1424,49 @@ func TestAgnetCallbackUpdatesDeploymentRuntimeSnapshot(t *testing.T) { require.Equal(t, "completed", instances[0].RuntimeState) } -func TestAgnetCallbackRejectsPlaintextSecretsAndMissingToken(t *testing.T) { - setupAgnetControlPlaneTestDB(t) - resetAgnetControlPlaneState(t) - t.Setenv("AGNET_CALLBACK_TOKEN", "callback-token") +func TestAgentCallbackRejectsPlaintextSecretsAndMissingToken(t *testing.T) { + setupAgentControlPlaneTestDB(t) + resetAgentControlPlaneState(t) + t.Setenv("AGENT_CALLBACK_TOKEN", "callback-token") recorder := httptest.NewRecorder() ctx, _ := gin.CreateTestContext(recorder) - ctx.Request = httptest.NewRequest(http.MethodPost, "/api/agnet/callbacks/swarm-events", strings.NewReader(`{"event_id":"evt-no-token","event_type":"status.updated"}`)) + ctx.Request = httptest.NewRequest(http.MethodPost, "/api/agent/callbacks/runtime-events", strings.NewReader(`{"event_id":"evt-no-token","event_type":"status.updated"}`)) ctx.Request.Header.Set("Content-Type", "application/json") - AgnetReceiveSwarmEventCallback(ctx) - var missingToken agnetCreateTestEnvelope + AgentReceiveRuntimeEventCallback(ctx) + var missingToken agentCreateTestEnvelope require.NoError(t, common.Unmarshal(recorder.Body.Bytes(), &missingToken)) require.False(t, missingToken.Success) require.Equal(t, "CALLBACK_UNAUTHORIZED", missingToken.Error.Code) secretRecorder := httptest.NewRecorder() secretCtx, _ := gin.CreateTestContext(secretRecorder) - secretCtx.Request = httptest.NewRequest(http.MethodPost, "/api/agnet/callbacks/swarm-events", strings.NewReader(`{ + secretCtx.Request = httptest.NewRequest(http.MethodPost, "/api/agent/callbacks/runtime-events", strings.NewReader(`{ "event_id":"evt-secret", "event_type":"status.updated", "metadata":{"access_token":"must-not-store"} }`)) secretCtx.Request.Header.Set("Content-Type", "application/json") - secretCtx.Request.Header.Set("X-Agnet-Service-Token", "callback-token") - AgnetReceiveSwarmEventCallback(secretCtx) - var secretEnvelope agnetCreateTestEnvelope + secretCtx.Request.Header.Set("X-Agent-Service-Token", "callback-token") + AgentReceiveRuntimeEventCallback(secretCtx) + var secretEnvelope agentCreateTestEnvelope require.NoError(t, common.Unmarshal(secretRecorder.Body.Bytes(), &secretEnvelope)) require.False(t, secretEnvelope.Success) require.Equal(t, "CALLBACK_SECRET_REJECTED", secretEnvelope.Error.Code) } -func TestAgnetArtifactsAndTimelineReturnPersistedRelatedRecords(t *testing.T) { - setupAgnetControlPlaneTestDB(t) - resetAgnetControlPlaneState(t) - t.Setenv("AGNET_CALLBACK_TOKEN", "callback-token") +func TestAgentArtifactsAndTimelineReturnPersistedRelatedRecords(t *testing.T) { + setupAgentControlPlaneTestDB(t) + resetAgentControlPlaneState(t) + t.Setenv("AGENT_CALLBACK_TOKEN", "callback-token") - plan := baseAgnetResourceGrantPlan() + plan := baseAgentResourceGrantPlan() plan.UserContext.UserID = "7" plan.SubMode = "waterfall" for idx := range plan.Agents[0].ResourceGrants { plan.Agents[0].ResourceGrants[idx].UserID = "7" } - createRecorder, createEnvelope := postAgnetCreateUserDeployment(t, 7, plan) + createRecorder, createEnvelope := postAgentCreateUserDeployment(t, 7, plan) require.True(t, createEnvelope.Success) var createBody map[string]any require.NoError(t, common.Unmarshal(createRecorder.Body.Bytes(), &createBody)) @@ -1483,28 +1483,28 @@ func TestAgnetArtifactsAndTimelineReturnPersistedRelatedRecords(t *testing.T) { }`, deploymentID) callbackRecorder := httptest.NewRecorder() callbackCtx, _ := gin.CreateTestContext(callbackRecorder) - callbackCtx.Request = httptest.NewRequest(http.MethodPost, "/api/agnet/callbacks/swarm-events", strings.NewReader(callbackBody)) + callbackCtx.Request = httptest.NewRequest(http.MethodPost, "/api/agent/callbacks/runtime-events", strings.NewReader(callbackBody)) callbackCtx.Request.Header.Set("Content-Type", "application/json") - callbackCtx.Request.Header.Set("X-Agnet-Service-Token", "callback-token") - AgnetReceiveSwarmEventCallback(callbackCtx) + callbackCtx.Request.Header.Set("X-Agent-Service-Token", "callback-token") + AgentReceiveRuntimeEventCallback(callbackCtx) require.Contains(t, callbackRecorder.Body.String(), `"success":true`) snapshotRecorder := httptest.NewRecorder() snapshotCtx, _ := gin.CreateTestContext(snapshotRecorder) snapshotCtx.Set("id", 7) - snapshotCtx.Request = httptest.NewRequest(http.MethodPost, "/api/agnet/sk-snapshots/resolve", strings.NewReader(fmt.Sprintf(`{"deployment_id":%q}`, deploymentID))) + snapshotCtx.Request = httptest.NewRequest(http.MethodPost, "/api/agent/sk-snapshots/resolve", strings.NewReader(fmt.Sprintf(`{"deployment_id":%q}`, deploymentID))) snapshotCtx.Request.Header.Set("Content-Type", "application/json") - AgnetResolveSKSnapshots(snapshotCtx) + AgentResolveSKSnapshots(snapshotCtx) require.Equal(t, http.StatusOK, snapshotRecorder.Code) - resetAgnetControlPlaneState(t) + resetAgentControlPlaneState(t) artifactRecorder := httptest.NewRecorder() artifactCtx, _ := gin.CreateTestContext(artifactRecorder) artifactCtx.Set("id", 7) artifactCtx.Params = gin.Params{{Key: "deployment_id", Value: deploymentID}} - artifactCtx.Request = httptest.NewRequest(http.MethodGet, "/api/agnet/user/deployments/"+deploymentID+"/artifacts", nil) - AgnetListUserDeploymentArtifacts(artifactCtx) + artifactCtx.Request = httptest.NewRequest(http.MethodGet, "/api/agent/user/deployments/"+deploymentID+"/artifacts", nil) + AgentListUserDeploymentArtifacts(artifactCtx) require.Equal(t, http.StatusOK, artifactRecorder.Code) require.Contains(t, artifactRecorder.Body.String(), `"art-timeline"`) require.Contains(t, artifactRecorder.Body.String(), `"deployment_id":"`+deploymentID+`"`) @@ -1514,8 +1514,8 @@ func TestAgnetArtifactsAndTimelineReturnPersistedRelatedRecords(t *testing.T) { snapshotListCtx, _ := gin.CreateTestContext(snapshotListRecorder) snapshotListCtx.Set("id", 7) snapshotListCtx.Params = gin.Params{{Key: "deployment_id", Value: deploymentID}} - snapshotListCtx.Request = httptest.NewRequest(http.MethodGet, "/api/agnet/user/deployments/"+deploymentID+"/sk-snapshots", nil) - AgnetListUserSKSnapshots(snapshotListCtx) + snapshotListCtx.Request = httptest.NewRequest(http.MethodGet, "/api/agent/user/deployments/"+deploymentID+"/sk-snapshots", nil) + AgentListUserSKSnapshots(snapshotListCtx) require.Equal(t, http.StatusOK, snapshotListRecorder.Code) require.Contains(t, snapshotListRecorder.Body.String(), `"deployment_id":"`+deploymentID+`"`) require.Contains(t, snapshotListRecorder.Body.String(), `"sk_snapshots":[`) @@ -1524,8 +1524,8 @@ func TestAgnetArtifactsAndTimelineReturnPersistedRelatedRecords(t *testing.T) { timelineCtx, _ := gin.CreateTestContext(timelineRecorder) timelineCtx.Set("id", 7) timelineCtx.Params = gin.Params{{Key: "deployment_id", Value: deploymentID}} - timelineCtx.Request = httptest.NewRequest(http.MethodGet, "/api/agnet/user/deployments/"+deploymentID+"/timeline", nil) - AgnetGetUserDeploymentTimeline(timelineCtx) + timelineCtx.Request = httptest.NewRequest(http.MethodGet, "/api/agent/user/deployments/"+deploymentID+"/timeline", nil) + AgentGetUserDeploymentTimeline(timelineCtx) require.Equal(t, http.StatusOK, timelineRecorder.Code) require.Contains(t, timelineRecorder.Body.String(), `"callbacks"`) require.Contains(t, timelineRecorder.Body.String(), `"artifacts"`) @@ -1536,11 +1536,11 @@ func TestAgnetArtifactsAndTimelineReturnPersistedRelatedRecords(t *testing.T) { require.Contains(t, timelineRecorder.Body.String(), `"event_type":"artifact.created"`) } -func TestAgnetStopDeploymentPersistsState(t *testing.T) { - setupAgnetControlPlaneTestDB(t) - resetAgnetControlPlaneState(t) +func TestAgentStopDeploymentPersistsState(t *testing.T) { + setupAgentControlPlaneTestDB(t) + resetAgentControlPlaneState(t) - recorder, envelope := postAgnetCreateDeployment(t, baseAgnetResourceGrantPlan()) + recorder, envelope := postAgentCreateDeployment(t, baseAgentResourceGrantPlan()) require.Equal(t, http.StatusOK, recorder.Code) require.True(t, envelope.Success) var createBody map[string]any @@ -1550,35 +1550,35 @@ func TestAgnetStopDeploymentPersistsState(t *testing.T) { stopRecorder := httptest.NewRecorder() stopCtx, _ := gin.CreateTestContext(stopRecorder) stopCtx.Params = gin.Params{{Key: "deployment_id", Value: deploymentID}} - stopCtx.Request = httptest.NewRequest(http.MethodPost, "/api/agnet/deployments/"+deploymentID+"/stop", nil) - AgnetStopDeployment(stopCtx) + stopCtx.Request = httptest.NewRequest(http.MethodPost, "/api/agent/deployments/"+deploymentID+"/stop", nil) + AgentStopDeployment(stopCtx) require.Equal(t, http.StatusOK, stopRecorder.Code) require.Contains(t, stopRecorder.Body.String(), `"status":"stopped"`) - resetAgnetControlPlaneState(t) + resetAgentControlPlaneState(t) getRecorder := httptest.NewRecorder() getCtx, _ := gin.CreateTestContext(getRecorder) getCtx.Params = gin.Params{{Key: "deployment_id", Value: deploymentID}} - getCtx.Request = httptest.NewRequest(http.MethodGet, "/api/agnet/deployments/"+deploymentID, nil) - AgnetGetDeployment(getCtx) + getCtx.Request = httptest.NewRequest(http.MethodGet, "/api/agent/deployments/"+deploymentID, nil) + AgentGetDeployment(getCtx) require.Equal(t, http.StatusOK, getRecorder.Code) require.Contains(t, getRecorder.Body.String(), `"status":"stopped"`) require.Contains(t, getRecorder.Body.String(), `"runtime_state":"stopped"`) } -func TestAgnetStopDeploymentPropagatesToRuntime(t *testing.T) { - setupAgnetControlPlaneTestDB(t) - resetAgnetControlPlaneState(t) +func TestAgentStopDeploymentPropagatesToRuntime(t *testing.T) { + setupAgentControlPlaneTestDB(t) + resetAgentControlPlaneState(t) stopCalled := false server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch { - case r.Method == http.MethodPost && r.URL.Path == "/api/agnet/deployments": + case r.Method == http.MethodPost && r.URL.Path == "/api/agent/sub-agile/deployments": w.Header().Set("Content-Type", "application/json") _, _ = w.Write([]byte(`{"success":true,"data":{"deployment_id":"runtime-dep-stop","status":"pending"}}`)) - case r.Method == http.MethodPost && r.URL.Path == "/api/agnet/deployments/runtime-dep-stop/stop": + case r.Method == http.MethodPost && r.URL.Path == "/api/agent/sub-agile/deployments/runtime-dep-stop/stop": stopCalled = true require.Equal(t, "Bearer service-token", r.Header.Get("Authorization")) body, err := io.ReadAll(r.Body) @@ -1592,12 +1592,12 @@ func TestAgnetStopDeploymentPropagatesToRuntime(t *testing.T) { })) defer server.Close() - t.Setenv("AGNET_RUNTIME_ENABLED", "true") - t.Setenv("AGNET_RUNTIME_ASYNC", "false") - t.Setenv("AGNET_RUNTIME_BASE_URL", server.URL) - t.Setenv("AGNET_RUNTIME_SERVICE_TOKEN", "service-token") + t.Setenv("AGENT_RUNTIME_ENABLED", "true") + t.Setenv("AGENT_RUNTIME_ASYNC", "false") + t.Setenv("AGENT_RUNTIME_BASE_URL", server.URL) + t.Setenv("AGENT_RUNTIME_SERVICE_TOKEN", "service-token") - recorder, envelope := postAgnetCreateDeployment(t, baseAgnetResourceGrantPlan()) + recorder, envelope := postAgentCreateDeployment(t, baseAgentResourceGrantPlan()) require.Equal(t, http.StatusOK, recorder.Code) require.True(t, envelope.Success) var createBody map[string]any @@ -1607,9 +1607,9 @@ func TestAgnetStopDeploymentPropagatesToRuntime(t *testing.T) { stopRecorder := httptest.NewRecorder() stopCtx, _ := gin.CreateTestContext(stopRecorder) stopCtx.Params = gin.Params{{Key: "deployment_id", Value: deploymentID}} - stopCtx.Request = httptest.NewRequest(http.MethodPost, "/api/agnet/deployments/"+deploymentID+"/stop", strings.NewReader(`{"reason":"runtime stop smoke"}`)) + stopCtx.Request = httptest.NewRequest(http.MethodPost, "/api/agent/deployments/"+deploymentID+"/stop", strings.NewReader(`{"reason":"runtime stop smoke"}`)) stopCtx.Request.Header.Set("Content-Type", "application/json") - AgnetStopDeployment(stopCtx) + AgentStopDeployment(stopCtx) require.Equal(t, http.StatusOK, stopRecorder.Code) require.Contains(t, stopRecorder.Body.String(), `"success":true`) @@ -1617,9 +1617,9 @@ func TestAgnetStopDeploymentPropagatesToRuntime(t *testing.T) { require.True(t, stopCalled) } -func TestManagerOnlyAgnetSmokeFlow(t *testing.T) { - setupAgnetControlPlaneTestDB(t) - resetAgnetControlPlaneState(t) +func TestManagerOnlyAgentSmokeFlow(t *testing.T) { + setupAgentControlPlaneTestDB(t) + resetAgentControlPlaneState(t) resourceBody := `{ "binding_scope":"project-smoke", @@ -1645,7 +1645,7 @@ func TestManagerOnlyAgnetSmokeFlow(t *testing.T) { "binding_scope":"project-smoke", "resource_id":%d, "role":"builder", - "agnet_id":"agent-builder-1", + "agent_id":"agent-builder-1", "permission_scope":{"actions":["repo:read","repo:write:feature-branches"]}, "constraints":{"allowed_paths":"src/**,docs/**","ref":"main"} }`, resourceID) @@ -1662,25 +1662,25 @@ func TestManagerOnlyAgnetSmokeFlow(t *testing.T) { 7, http.MethodGet, "/manifest", - "/manifest?binding_scope=project-smoke&role=builder&agnet_id=agent-builder-1", + "/manifest?binding_scope=project-smoke&role=builder&agent_id=agent-builder-1", "", ) require.Equal(t, http.StatusOK, manifestRecorder.Code) require.Contains(t, manifestRecorder.Body.String(), `"resource_grants":[`) require.Contains(t, manifestRecorder.Body.String(), `"secret_ref":"azkv://heicode-kv.vault.azure.net/secrets/project-smoke"`) - plan := baseAgnetResourceGrantPlan() + plan := baseAgentResourceGrantPlan() plan.UserContext.UserID = "7" plan.UserContext.ChannelID = "smoke-channel" plan.BillingContext.NewAPIUserRef = "newapi-user-smoke" plan.BillingContext.NewAPIGroup = "vip" plan.BillingContext.QuotaRef = "quota-smoke" plan.Metadata.CorrelationID = "corr-manager-smoke" - plan.Agents[0].ResourceGrants = []agnetResourceGrant{ + plan.Agents[0].ResourceGrants = []agentResourceGrant{ { GrantID: fmt.Sprintf("grant-%d", grantID), ResourceID: fmt.Sprintf("%d", resourceID), - ResourceType: agnetResourceGit, + ResourceType: agentResourceGit, UserID: "7", BindingScope: "project-smoke", TargetRole: "builder", @@ -1691,13 +1691,13 @@ func TestManagerOnlyAgnetSmokeFlow(t *testing.T) { "provider": "gitee", "resource_ref": "https://example.invalid/acme/smoke.git", }, - Status: agnetGrantStatusActive, + Status: agentGrantStatusActive, SecretRef: "azkv://heicode-kv.vault.azure.net/secrets/project-smoke", Audit: map[string]string{"confirmed_by": "7"}, }, } - deployRecorder, envelope := postAgnetCreateDeployment(t, plan) + deployRecorder, envelope := postAgentCreateDeployment(t, plan) require.Equal(t, http.StatusOK, deployRecorder.Code) require.True(t, envelope.Success) var deployEnv map[string]any @@ -1708,8 +1708,8 @@ func TestManagerOnlyAgnetSmokeFlow(t *testing.T) { logRecorder := httptest.NewRecorder() logCtx, _ := gin.CreateTestContext(logRecorder) logCtx.Params = gin.Params{{Key: "deployment_id", Value: deploymentID}} - logCtx.Request = httptest.NewRequest(http.MethodGet, "/api/agnet/deployments/"+deploymentID+"/logs", nil) - AgnetListDeploymentLogs(logCtx) + logCtx.Request = httptest.NewRequest(http.MethodGet, "/api/agent/deployments/"+deploymentID+"/logs", nil) + AgentListDeploymentLogs(logCtx) require.Equal(t, http.StatusOK, logRecorder.Code) require.Contains(t, logRecorder.Body.String(), `"redacted":true`) require.NotContains(t, strings.ToLower(logRecorder.Body.String()), "access_token") @@ -1717,17 +1717,17 @@ func TestManagerOnlyAgnetSmokeFlow(t *testing.T) { stopRecorder := httptest.NewRecorder() stopCtx, _ := gin.CreateTestContext(stopRecorder) stopCtx.Params = gin.Params{{Key: "deployment_id", Value: deploymentID}} - stopCtx.Request = httptest.NewRequest(http.MethodPost, "/api/agnet/deployments/"+deploymentID+"/stop", nil) - AgnetStopDeployment(stopCtx) + stopCtx.Request = httptest.NewRequest(http.MethodPost, "/api/agent/deployments/"+deploymentID+"/stop", nil) + AgentStopDeployment(stopCtx) require.Equal(t, http.StatusOK, stopRecorder.Code) require.Contains(t, stopRecorder.Body.String(), `"status":"stopped"`) - resetAgnetControlPlaneState(t) + resetAgentControlPlaneState(t) getRecorder := httptest.NewRecorder() getCtx, _ := gin.CreateTestContext(getRecorder) getCtx.Params = gin.Params{{Key: "deployment_id", Value: deploymentID}} - getCtx.Request = httptest.NewRequest(http.MethodGet, "/api/agnet/deployments/"+deploymentID, nil) - AgnetGetDeployment(getCtx) + getCtx.Request = httptest.NewRequest(http.MethodGet, "/api/agent/deployments/"+deploymentID, nil) + AgentGetDeployment(getCtx) require.Equal(t, http.StatusOK, getRecorder.Code) require.Contains(t, getRecorder.Body.String(), `"status":"stopped"`) @@ -1747,32 +1747,32 @@ func TestManagerOnlyAgnetSmokeFlow(t *testing.T) { 7, http.MethodGet, "/manifest", - "/manifest?binding_scope=project-smoke&role=builder&agnet_id=agent-builder-1", + "/manifest?binding_scope=project-smoke&role=builder&agent_id=agent-builder-1", "", ) require.Equal(t, http.StatusOK, emptyManifestRecorder.Code) require.Contains(t, emptyManifestRecorder.Body.String(), `"resource_grants":[]`) } -func TestAgnetCreateDeploymentAcceptsP1ResourceGrantModel(t *testing.T) { - resetAgnetControlPlaneState(t) +func TestAgentCreateDeploymentAcceptsP1ResourceGrantModel(t *testing.T) { + resetAgentControlPlaneState(t) - recorder, envelope := postAgnetCreateDeployment(t, baseAgnetResourceGrantPlan()) + recorder, envelope := postAgentCreateDeployment(t, baseAgentResourceGrantPlan()) require.Equal(t, http.StatusOK, recorder.Code) require.True(t, envelope.Success) - agnetMu.RLock() - require.Len(t, agnetDeployments, 1) - var stored agnetDeploymentRecord - for _, record := range agnetDeployments { + agentMu.RLock() + require.Len(t, agentDeployments, 1) + var stored agentDeploymentRecord + for _, record := range agentDeployments { stored = record } - agnetMu.RUnlock() + agentMu.RUnlock() grants := stored.Plan.Agents[0].ResourceGrants require.Len(t, grants, 2) - require.Equal(t, agnetResourceGit, grants[0].ResourceType) + require.Equal(t, agentResourceGit, grants[0].ResourceType) require.Equal(t, "user-p1", grants[0].UserID) require.Equal(t, "https://example.invalid/acme/project.git#main", grants[0].BindingScope) require.Equal(t, "builder", grants[0].TargetRole) @@ -1781,8 +1781,8 @@ func TestAgnetCreateDeploymentAcceptsP1ResourceGrantModel(t *testing.T) { require.Empty(t, grants[1].SecretRef, "project document grants should not require credential material") require.Equal(t, "newapi", stored.Plan.BillingContext.Provider) require.Equal(t, "channel-p1", stored.Plan.UserContext.ChannelID) - require.Equal(t, "agnet", stored.Plan.AgentRuntime.Platform) - require.Equal(t, "agnet-model-profile-builder", stored.Plan.AgentRuntime.Agents[0].ModelRef) + require.Equal(t, "agent", stored.Plan.AgentRuntime.Platform) + require.Equal(t, "agent-model-profile-builder", stored.Plan.AgentRuntime.Agents[0].ModelRef) require.Equal(t, "queued", stored.RuntimeState) require.Empty(t, stored.FailureReason) require.Len(t, stored.AgentInstances, 1) @@ -1794,133 +1794,133 @@ func TestAgnetCreateDeploymentAcceptsP1ResourceGrantModel(t *testing.T) { require.Equal(t, "azkv://heicode-kv.vault.azure.net/secrets/user-p1-project-main-res-git-main", stored.ResourceGrantManifest.ResourceGrants[0].SecretRef) } -func TestAgnetPermissionManifestExcludesInactiveResourceGrants(t *testing.T) { - resetAgnetControlPlaneState(t) - plan := baseAgnetResourceGrantPlan() - plan.Agents[0].ResourceGrants[1].Status = agnetGrantStatusRevoked +func TestAgentPermissionManifestExcludesInactiveResourceGrants(t *testing.T) { + resetAgentControlPlaneState(t) + plan := baseAgentResourceGrantPlan() + plan.Agents[0].ResourceGrants[1].Status = agentGrantStatusRevoked - _, envelope := postAgnetCreateDeployment(t, plan) + _, envelope := postAgentCreateDeployment(t, plan) require.True(t, envelope.Success) - agnetMu.RLock() - var stored agnetDeploymentRecord - for _, record := range agnetDeployments { + agentMu.RLock() + var stored agentDeploymentRecord + for _, record := range agentDeployments { stored = record } - agnetMu.RUnlock() + agentMu.RUnlock() require.Len(t, stored.ResourceGrantManifest.ResourceGrants, 1) require.Equal(t, "grant-git-builder", stored.ResourceGrantManifest.ResourceGrants[0].GrantID) } -func TestAgnetCreateDeploymentRejectsPlaintextResourceGrantCredentialFields(t *testing.T) { - resetAgnetControlPlaneState(t) - plan := baseAgnetResourceGrantPlan() +func TestAgentCreateDeploymentRejectsPlaintextResourceGrantCredentialFields(t *testing.T) { + resetAgentControlPlaneState(t) + plan := baseAgentResourceGrantPlan() plan.Agents[0].ResourceGrants[0].Metadata["access_token"] = "must-not-be-stored" - _, envelope := postAgnetCreateDeployment(t, plan) + _, envelope := postAgentCreateDeployment(t, plan) require.False(t, envelope.Success) require.Equal(t, "RESOURCE_GRANT_SECRET_REJECTED", envelope.Error.Code) - require.Empty(t, agnetDeployments) + require.Empty(t, agentDeployments) } -func TestAgnetCreateDeploymentRejectsResourceGrantWithoutSecretRef(t *testing.T) { - resetAgnetControlPlaneState(t) - plan := baseAgnetResourceGrantPlan() +func TestAgentCreateDeploymentRejectsResourceGrantWithoutSecretRef(t *testing.T) { + resetAgentControlPlaneState(t) + plan := baseAgentResourceGrantPlan() plan.Agents[0].ResourceGrants[0].SecretRef = "" - _, envelope := postAgnetCreateDeployment(t, plan) + _, envelope := postAgentCreateDeployment(t, plan) require.False(t, envelope.Success) require.Equal(t, "RESOURCE_GRANT_SECRET_REF_REQUIRED", envelope.Error.Code) - require.Empty(t, agnetDeployments) + require.Empty(t, agentDeployments) } -func TestAgnetCreateDeploymentRejectsMissingUserContext(t *testing.T) { - resetAgnetControlPlaneState(t) - plan := baseAgnetResourceGrantPlan() +func TestAgentCreateDeploymentRejectsMissingUserContext(t *testing.T) { + resetAgentControlPlaneState(t) + plan := baseAgentResourceGrantPlan() plan.UserContext.UserID = "" - _, envelope := postAgnetCreateDeployment(t, plan) + _, envelope := postAgentCreateDeployment(t, plan) require.False(t, envelope.Success) require.Equal(t, "POLICY_REJECTED", envelope.Error.Code) - require.Empty(t, agnetDeployments) + require.Empty(t, agentDeployments) } -func TestAgnetCreateDeploymentRejectsResourceGrantUserMismatch(t *testing.T) { - resetAgnetControlPlaneState(t) - plan := baseAgnetResourceGrantPlan() +func TestAgentCreateDeploymentRejectsResourceGrantUserMismatch(t *testing.T) { + resetAgentControlPlaneState(t) + plan := baseAgentResourceGrantPlan() plan.Agents[0].ResourceGrants[0].UserID = "user-other" - _, envelope := postAgnetCreateDeployment(t, plan) + _, envelope := postAgentCreateDeployment(t, plan) require.False(t, envelope.Success) require.Equal(t, "RESOURCE_GRANT_INVALID", envelope.Error.Code) - require.Empty(t, agnetDeployments) + require.Empty(t, agentDeployments) } -func TestAgnetCreateDeploymentRejectsResourceGrantWithoutBindingScope(t *testing.T) { - resetAgnetControlPlaneState(t) - plan := baseAgnetResourceGrantPlan() +func TestAgentCreateDeploymentRejectsResourceGrantWithoutBindingScope(t *testing.T) { + resetAgentControlPlaneState(t) + plan := baseAgentResourceGrantPlan() plan.Agents[0].ResourceGrants[0].BindingScope = "" - _, envelope := postAgnetCreateDeployment(t, plan) + _, envelope := postAgentCreateDeployment(t, plan) require.False(t, envelope.Success) require.Equal(t, "RESOURCE_GRANT_INVALID", envelope.Error.Code) - require.Empty(t, agnetDeployments) + require.Empty(t, agentDeployments) } -func TestAgnetCreateDeploymentRejectsNewAPIBillingWithoutUserMapping(t *testing.T) { - resetAgnetControlPlaneState(t) - plan := baseAgnetResourceGrantPlan() +func TestAgentCreateDeploymentRejectsNewAPIBillingWithoutUserMapping(t *testing.T) { + resetAgentControlPlaneState(t) + plan := baseAgentResourceGrantPlan() plan.UserContext.ChannelID = "" plan.BillingContext.NewAPIUserRef = "" plan.BillingContext.NewAPIGroup = "" plan.BillingContext.QuotaRef = "" - _, envelope := postAgnetCreateDeployment(t, plan) + _, envelope := postAgentCreateDeployment(t, plan) require.False(t, envelope.Success) require.Equal(t, "BILLING_CONTEXT_INVALID", envelope.Error.Code) - require.Empty(t, agnetDeployments) + require.Empty(t, agentDeployments) } -func TestAgnetCreateDeploymentRejectsRuntimeRoleOutsideAgentPlan(t *testing.T) { - resetAgnetControlPlaneState(t) - plan := baseAgnetResourceGrantPlan() +func TestAgentCreateDeploymentRejectsRuntimeRoleOutsideAgentPlan(t *testing.T) { + resetAgentControlPlaneState(t) + plan := baseAgentResourceGrantPlan() plan.AgentRuntime.Agents[0].Role = "ops" - _, envelope := postAgnetCreateDeployment(t, plan) + _, envelope := postAgentCreateDeployment(t, plan) require.False(t, envelope.Success) require.Equal(t, "AGENT_RUNTIME_INVALID", envelope.Error.Code) - require.Empty(t, agnetDeployments) + require.Empty(t, agentDeployments) } -func TestAgnetDeploymentLogsAndMetricsExposeRedactedReadiness(t *testing.T) { - resetAgnetControlPlaneState(t) +func TestAgentDeploymentLogsAndMetricsExposeRedactedReadiness(t *testing.T) { + resetAgentControlPlaneState(t) - _, envelope := postAgnetCreateDeployment(t, baseAgnetResourceGrantPlan()) + _, envelope := postAgentCreateDeployment(t, baseAgentResourceGrantPlan()) require.True(t, envelope.Success) - agnetMu.RLock() + agentMu.RLock() var deploymentID string - for id := range agnetDeployments { + for id := range agentDeployments { deploymentID = id break } - agnetMu.RUnlock() + agentMu.RUnlock() require.NotEmpty(t, deploymentID) logRecorder := httptest.NewRecorder() logCtx, _ := gin.CreateTestContext(logRecorder) - logCtx.Request = httptest.NewRequest(http.MethodGet, "/api/agnet/deployments/"+deploymentID+"/logs", nil) + logCtx.Request = httptest.NewRequest(http.MethodGet, "/api/agent/deployments/"+deploymentID+"/logs", nil) logCtx.Params = gin.Params{{Key: "deployment_id", Value: deploymentID}} - AgnetListDeploymentLogs(logCtx) + AgentListDeploymentLogs(logCtx) require.Equal(t, http.StatusOK, logRecorder.Code) require.Contains(t, logRecorder.Body.String(), `"redacted":true`) @@ -1929,10 +1929,10 @@ func TestAgnetDeploymentLogsAndMetricsExposeRedactedReadiness(t *testing.T) { metricRecorder := httptest.NewRecorder() metricCtx, _ := gin.CreateTestContext(metricRecorder) - metricCtx.Request = httptest.NewRequest(http.MethodGet, "/api/agnet/deployments/"+deploymentID+"/metrics?window=15m&step=60s", nil) + metricCtx.Request = httptest.NewRequest(http.MethodGet, "/api/agent/deployments/"+deploymentID+"/metrics?window=15m&step=60s", nil) metricCtx.Params = gin.Params{{Key: "deployment_id", Value: deploymentID}} - AgnetGetDeploymentMetrics(metricCtx) + AgentGetDeploymentMetrics(metricCtx) require.Equal(t, http.StatusOK, metricRecorder.Code) require.Contains(t, metricRecorder.Body.String(), `"deployment_id":"`+deploymentID+`"`) @@ -1940,3 +1940,33 @@ func TestAgnetDeploymentLogsAndMetricsExposeRedactedReadiness(t *testing.T) { require.Contains(t, metricRecorder.Body.String(), `"runtime_state":"queued"`) require.Contains(t, metricRecorder.Body.String(), `"failure_reason":""`) } + +func TestAgentDeploymentDisplayStatus_JudgesDeliverable(t *testing.T) { + setupAgentControlPlaneTestDB(t) + rec := agentDeploymentRecord{DeploymentID: "dep_ds_1", Status: "completed"} + + // completed but no artifacts -> completed_without_deliverable + require.Equal(t, "completed_without_deliverable", agentDeploymentDisplayStatus(rec)) + + // only a summary/document artifact -> needs_codegen + require.NoError(t, model.UpsertAgentArtifact(&model.AgentArtifact{ + ArtifactID: "art_ds_doc", DeploymentID: "dep_ds_1", ArtifactType: "document", Title: "plan", CreatedAtMs: 1, + })) + require.Equal(t, "needs_codegen", agentDeploymentDisplayStatus(rec)) + + // a real code deliverable -> completed + require.NoError(t, model.UpsertAgentArtifact(&model.AgentArtifact{ + ArtifactID: "art_ds_code", DeploymentID: "dep_ds_1", ArtifactType: "code_patch", Title: "backend", CreatedAtMs: 2, + })) + require.Equal(t, "completed", agentDeploymentDisplayStatus(rec)) + + // synthesized fallback (even if code_patch typed) is not a real deliverable + require.NoError(t, model.UpsertAgentArtifact(&model.AgentArtifact{ + ArtifactID: "art_ds_synth", DeploymentID: "dep_ds_2", ArtifactType: "code_patch", Title: "fallback", + MetadataJSON: `{"synthesized":true}`, CreatedAtMs: 1, + })) + require.Equal(t, "needs_codegen", agentDeploymentDisplayStatus(agentDeploymentRecord{DeploymentID: "dep_ds_2", Status: "completed"})) + + // non-completed mirrors the runtime status + require.Equal(t, "running", agentDeploymentDisplayStatus(agentDeploymentRecord{DeploymentID: "dep_ds_x", Status: "running"})) +} diff --git a/heicode/controller/agnet_deliverable_secret_test.go b/heicode/controller/agent_deliverable_secret_test.go similarity index 92% rename from heicode/controller/agnet_deliverable_secret_test.go rename to heicode/controller/agent_deliverable_secret_test.go index cd801ae..32c1dcb 100644 --- a/heicode/controller/agnet_deliverable_secret_test.go +++ b/heicode/controller/agent_deliverable_secret_test.go @@ -132,21 +132,21 @@ func TestValueLooksLikeSecretAndPlaintextScan(t *testing.T) { } // P5: a single source of truth for the default model; no placeholder fallback. -func TestDefaultAgnetModelID_SingleSourceNoPlaceholder(t *testing.T) { - def := defaultAgnetModelID() +func TestDefaultAgentModelID_SingleSourceNoPlaceholder(t *testing.T) { + def := defaultAgentModelID() require.Equal(t, "gpt-5.4", def) - // Draft builder must use the single default, never agnet-model-. - plan := buildAgnetDraftAgentPlan("backend", "", nil) + // Draft builder must use the single default, never agent-model-. + plan := buildAgentDraftAgentPlan("backend", "", nil) require.Equal(t, def, plan.DefaultModelID) - require.NotContains(t, plan.DefaultModelID, "agnet-model-") + require.NotContains(t, plan.DefaultModelID, "agent-model-") // Explicit client model is still honored. - plan = buildAgnetDraftAgentPlan("backend", "gpt-5.4-mini", nil) + plan = buildAgentDraftAgentPlan("backend", "gpt-5.4-mini", nil) require.Equal(t, "gpt-5.4-mini", plan.DefaultModelID) // Every role template resolves to the single default, no claude-* hardcoding. - for _, tpl := range agnetRoleTemplates() { + for _, tpl := range agentRoleTemplates() { require.Equal(t, def, tpl.DefaultModel, "role %s", tpl.Key) } } diff --git a/heicode/controller/agnet_role_template.go b/heicode/controller/agent_role_template.go similarity index 71% rename from heicode/controller/agnet_role_template.go rename to heicode/controller/agent_role_template.go index b919679..b257d9c 100644 --- a/heicode/controller/agnet_role_template.go +++ b/heicode/controller/agent_role_template.go @@ -5,7 +5,7 @@ import ( "github.com/heicode/manager/common" ) -// AgnetRoleTemplate is the platform-recommended role catalog Manager +// AgentRoleTemplate is the platform-recommended role catalog Manager // surfaces to users when they assemble an AI development team. The // six canonical roles come from docs/product-package/13-platform- // description.md §3 and 04-platform-usage-guide.md §第五步. @@ -14,7 +14,7 @@ import ( // - Roles are platform-defined contracts, not user-editable data. // Treating them like rows would invite drift between deployments. // - Permission hints below are *recommendations* the UI uses to -// pre-fill the "what can this Agnet do" confirmation card — +// pre-fill the "what can this Agent do" confirmation card — // the actual permission grant still goes through ResourceGrant. // - If we ever need per-tenant role customization, we add an // overlay table; the canonical set still lives here as the @@ -24,7 +24,7 @@ import ( // across persistence and the client picker. Display strings can // be translated, but the key must NEVER change without a coordinated // frontend rollout. -type AgnetRoleTemplate struct { +type AgentRoleTemplate struct { Key string `json:"key"` DisplayName string `json:"display_name"` Summary string `json:"summary"` @@ -33,132 +33,132 @@ type AgnetRoleTemplate struct { RiskLevel string `json:"risk_level"` } -// defaultAgnetModelID is the single source of truth for the default sub-agent +// defaultAgentModelID is the single source of truth for the default sub-agent // model. It is aligned to the production-verified NewAPI model and overridable -// via AGNET_DEFAULT_MODEL_ID, so role templates, deployment drafts and runtime -// agent refs never fall back to placeholder names (e.g. agnet-model-) +// via AGENT_DEFAULT_MODEL_ID, so role templates, deployment drafts and runtime +// agent refs never fall back to placeholder names (e.g. agent-model-) // that production NewAPI cannot route ("No available channel for model ..."). -func defaultAgnetModelID() string { - return common.GetEnvOrDefaultString("AGNET_DEFAULT_MODEL_ID", "gpt-5.4") +func defaultAgentModelID() string { + return common.GetEnvOrDefaultString("AGENT_DEFAULT_MODEL_ID", "gpt-5.4") } -// agnetRoleTemplates returns the canonical six-role catalog. Order +// agentRoleTemplates returns the canonical six-role catalog. Order // matches the typical lifecycle a user walks through when assembling // a team: discover -> design -> build -> review -> operate. // // Permission hints use the verbs from docs §13.3.4 (Resource Grant) // and stay deliberately broad — concrete grants come from the user // resource-binding flow. -func agnetRoleTemplates() []AgnetRoleTemplate { - return []AgnetRoleTemplate{ +func agentRoleTemplates() []AgentRoleTemplate { + return []AgentRoleTemplate{ { Key: "product", - DisplayName: "Product Agnet", + DisplayName: "Product Agent", Summary: "Refines the user idea into product scope, requirements and acceptance criteria.", - DefaultModel: defaultAgnetModelID(), + DefaultModel: defaultAgentModelID(), DefaultPermissions: []string{ "read:project_docs", "write:product_spec", }, - RiskLevel: agnetRiskLow, + RiskLevel: agentRiskLow, }, { Key: "architect", - DisplayName: "Architect Agnet", + DisplayName: "Architect Agent", Summary: "Designs the technical approach, picks frameworks, and breaks work into sub-tasks.", - DefaultModel: defaultAgnetModelID(), + DefaultModel: defaultAgentModelID(), DefaultPermissions: []string{ "read:repo", "write:architecture_doc", }, - RiskLevel: agnetRiskLow, + RiskLevel: agentRiskLow, }, { Key: "frontend", - DisplayName: "Frontend Agnet", + DisplayName: "Frontend Agent", Summary: "Implements UI, components and client-side state per the architect's plan.", - DefaultModel: defaultAgnetModelID(), + DefaultModel: defaultAgentModelID(), DefaultPermissions: []string{ "read:repo", "write:repo:frontend", }, - RiskLevel: agnetRiskMedium, + RiskLevel: agentRiskMedium, }, { Key: "backend", - DisplayName: "Backend Agnet", + DisplayName: "Backend Agent", Summary: "Implements server-side APIs, data models and integrations.", - DefaultModel: defaultAgnetModelID(), + DefaultModel: defaultAgentModelID(), DefaultPermissions: []string{ "read:repo", "write:repo:backend", "read:dev_database", }, - RiskLevel: agnetRiskMedium, + RiskLevel: agentRiskMedium, }, { Key: "reviewer", - DisplayName: "Reviewer Agnet", + DisplayName: "Reviewer Agent", Summary: "Performs code review, security checks and runs the test suite.", - DefaultModel: defaultAgnetModelID(), + DefaultModel: defaultAgentModelID(), DefaultPermissions: []string{ "read:repo", "run:tests", "comment:pull_request", }, - RiskLevel: agnetRiskLow, + RiskLevel: agentRiskLow, }, { Key: "ops", - DisplayName: "Ops Agnet", + DisplayName: "Ops Agent", Summary: "Deploys to test environments, watches logs and prepares production rollouts (production requires approval).", - DefaultModel: defaultAgnetModelID(), + DefaultModel: defaultAgentModelID(), DefaultPermissions: []string{ "read:repo", "deploy:test_env", "read:metrics", "approval_required:deploy_prod", }, - RiskLevel: agnetRiskHigh, + RiskLevel: agentRiskHigh, }, } } -// AgnetListRoleTemplates is the GET /api/agnet/role-templates handler. +// AgentListRoleTemplates is the GET /api/agent/role-templates handler. // Returns the canonical six-role catalog so the deployment-creation // UI can pre-populate role pickers and the documentation page can // render the role overview. // // Auth: requires UserAuth (mounted by router). Anyone logged in to // Manager can read the catalog; there are no secrets in the payload. -func AgnetListRoleTemplates(c *gin.Context) { - tpls := agnetRoleTemplates() +func AgentListRoleTemplates(c *gin.Context) { + tpls := agentRoleTemplates() common.ApiSuccess(c, gin.H{ "items": tpls, "total": len(tpls), }) } -// agnetRoleTemplateKeys is a helper for validation in deployment +// agentRoleTemplateKeys is a helper for validation in deployment // creation — checks whether a user-provided role_template string is // one of the canonical six. Returns true for any of the canonical // keys; returns true for unknown keys too (deployment flow today -// accepts free-form role_template strings, see agnet_control_plane. +// accepts free-form role_template strings, see agent_control_plane. // go:552), so this helper is currently advisory. When we tighten // validation (after frontend ships the new picker), flip the // fallback to false and add a unit test. -func agnetRoleTemplateKeys() map[string]bool { +func agentRoleTemplateKeys() map[string]bool { keys := make(map[string]bool) - for _, t := range agnetRoleTemplates() { + for _, t := range agentRoleTemplates() { keys[t.Key] = true } return keys } -// agnetIsCanonicalRoleKey reports whether `key` matches one of the +// agentIsCanonicalRoleKey reports whether `key` matches one of the // six platform-defined roles. Today the deployment endpoint accepts // any non-empty string; this helper is reserved for the next step // when we move to a closed set. -func agnetIsCanonicalRoleKey(key string) bool { - return agnetRoleTemplateKeys()[key] +func agentIsCanonicalRoleKey(key string) bool { + return agentRoleTemplateKeys()[key] } diff --git a/heicode/controller/agnet_role_template_test.go b/heicode/controller/agent_role_template_test.go similarity index 80% rename from heicode/controller/agnet_role_template_test.go rename to heicode/controller/agent_role_template_test.go index 5a313d8..22e236d 100644 --- a/heicode/controller/agnet_role_template_test.go +++ b/heicode/controller/agent_role_template_test.go @@ -9,13 +9,13 @@ import ( "github.com/heicode/manager/common" ) -func TestAgnetRoleTemplates_CanonicalSetCovered(t *testing.T) { +func TestAgentRoleTemplates_CanonicalSetCovered(t *testing.T) { // Pins the six canonical role keys from docs §13.3.3. Any code // change that adds, removes or renames a key MUST update this // list — guards against accidental drift between Manager and // the product spec. want := []string{"product", "architect", "frontend", "backend", "reviewer", "ops"} - got := agnetRoleTemplates() + got := agentRoleTemplates() if len(got) != len(want) { t.Fatalf("expected %d roles, got %d", len(want), len(got)) } @@ -37,7 +37,7 @@ func TestAgnetRoleTemplates_CanonicalSetCovered(t *testing.T) { } } -func TestAgnetRoleTemplates_RiskLevels(t *testing.T) { +func TestAgentRoleTemplates_RiskLevels(t *testing.T) { // Ops is the only canonical role with high risk (production // deployment intent). Reviewer + Product + Architect stay low // (read-mostly). Frontend + Backend land at medium. Pins the @@ -45,29 +45,29 @@ func TestAgnetRoleTemplates_RiskLevels(t *testing.T) { // flip an ops role to "low" and skip the high-risk approval // gating downstream. wantRisk := map[string]string{ - "product": agnetRiskLow, - "architect": agnetRiskLow, - "reviewer": agnetRiskLow, - "frontend": agnetRiskMedium, - "backend": agnetRiskMedium, - "ops": agnetRiskHigh, + "product": agentRiskLow, + "architect": agentRiskLow, + "reviewer": agentRiskLow, + "frontend": agentRiskMedium, + "backend": agentRiskMedium, + "ops": agentRiskHigh, } - for _, tpl := range agnetRoleTemplates() { + for _, tpl := range agentRoleTemplates() { if want, ok := wantRisk[tpl.Key]; ok && tpl.RiskLevel != want { t.Errorf("role %q: want risk %q, got %q", tpl.Key, want, tpl.RiskLevel) } } } -func TestAgnetListRoleTemplates_HTTPShape(t *testing.T) { +func TestAgentListRoleTemplates_HTTPShape(t *testing.T) { gin.SetMode(gin.TestMode) rec := httptest.NewRecorder() c, _ := gin.CreateTestContext(rec) - c.Request = httptest.NewRequest("GET", "/api/agnet/role-templates", nil) + c.Request = httptest.NewRequest("GET", "/api/agent/role-templates", nil) c.Set("id", 1) c.Set("role", common.RoleCommonUser) - AgnetListRoleTemplates(c) + AgentListRoleTemplates(c) if rec.Code != 200 { t.Fatalf("expected 200, got %d body=%s", rec.Code, rec.Body.String()) @@ -83,17 +83,17 @@ func TestAgnetListRoleTemplates_HTTPShape(t *testing.T) { } } -func TestAgnetIsCanonicalRoleKey(t *testing.T) { +func TestAgentIsCanonicalRoleKey(t *testing.T) { // Defensive helper currently used as advisory — pins the closed // set so the future tighten-up to closed-set validation is one // flip instead of an open-ended audit. for _, ok := range []string{"product", "architect", "frontend", "backend", "reviewer", "ops"} { - if !agnetIsCanonicalRoleKey(ok) { + if !agentIsCanonicalRoleKey(ok) { t.Errorf("%q should be canonical", ok) } } for _, bad := range []string{"", "debugger", "executor", "random_string"} { - if agnetIsCanonicalRoleKey(bad) { + if agentIsCanonicalRoleKey(bad) { t.Errorf("%q should NOT be canonical", bad) } } diff --git a/heicode/controller/agnet_runtime_client.go b/heicode/controller/agent_runtime_client.go similarity index 66% rename from heicode/controller/agnet_runtime_client.go rename to heicode/controller/agent_runtime_client.go index acc4d11..bfc1bb3 100644 --- a/heicode/controller/agnet_runtime_client.go +++ b/heicode/controller/agent_runtime_client.go @@ -17,15 +17,15 @@ import ( ) const ( - agnetRuntimeStateSyncing = "runtime_syncing" - agnetRuntimeStateSynced = "runtime_accepted" - agnetRuntimeStateFailed = "runtime_sync_failed" + agentRuntimeStateSyncing = "runtime_syncing" + agentRuntimeStateSynced = "runtime_accepted" + agentRuntimeStateFailed = "runtime_sync_failed" - agnetRuntimeModeAgnet = "agnet" - agnetRuntimeModeSwarm = "swarm" + agentRuntimeModeAgent = "agent" + agentRuntimeModeSwarm = "swarm" ) -type agnetRuntimeConfig struct { +type agentRuntimeConfig struct { Enabled bool Async bool BaseURL string @@ -39,14 +39,14 @@ type agnetRuntimeConfig struct { Timeout time.Duration } -type agnetRuntimeSyncResult struct { +type agentRuntimeSyncResult struct { RuntimeDeploymentID string RuntimeSwarmID string RuntimeStatus string RawStatusCode int } -type agnetRuntimeDiagnostics struct { +type agentRuntimeDiagnostics struct { DeploymentID string `json:"deployment_id"` RuntimeMode string `json:"runtime_mode"` SubMode string `json:"sub_mode"` @@ -65,41 +65,42 @@ type agnetRuntimeDiagnostics struct { CheckedAt string `json:"checked_at"` } -func normalizeAgnetRuntimeMode(value string) string { +func normalizeAgentRuntimeMode(value string) string { switch strings.ToLower(strings.TrimSpace(value)) { - case agnetRuntimeModeSwarm: - return agnetRuntimeModeSwarm + case agentRuntimeModeSwarm: + return agentRuntimeModeSwarm default: - return agnetRuntimeModeAgnet + return agentRuntimeModeAgent } } -func agnetRuntimeModeForSource(source string) string { +func agentRuntimeModeForSource(source string) string { if strings.TrimSpace(source) == "api_swarms_adapter" { - return agnetRuntimeModeSwarm + return agentRuntimeModeSwarm } - return agnetRuntimeModeAgnet + return agentRuntimeModeAgent } -func agnetRuntimeModeForRecord(record agnetDeploymentRecord) string { - return normalizeAgnetRuntimeMode(record.Plan.Metadata.RuntimeMode) +func agentRuntimeModeForRecord(record agentDeploymentRecord) string { + return normalizeAgentRuntimeMode(record.Plan.Metadata.RuntimeMode) } -func agnetRuntimeClientConfig() agnetRuntimeConfig { - return agnetRuntimeClientConfigForMode(agnetRuntimeModeAgnet) +func agentRuntimeClientConfig() agentRuntimeConfig { + return agentRuntimeClientConfigForMode(agentRuntimeModeAgent) } -func agnetRuntimeClientConfigForMode(mode string) agnetRuntimeConfig { - timeoutSec := common.GetEnvOrDefault("AGNET_RUNTIME_TIMEOUT_SECONDS", 5) +func agentRuntimeClientConfigForMode(mode string) agentRuntimeConfig { + timeoutSec := common.GetEnvOrDefault("AGENT_RUNTIME_TIMEOUT_SECONDS", 5) if timeoutSec <= 0 { timeoutSec = 5 } - mode = normalizeAgnetRuntimeMode(mode) - prefix := "AGNET_RUNTIME_" - defaultCreatePath := "/api/agnet/deployments" - defaultStopPath := "/api/agnet/deployments/{deployment_id}/stop" + mode = normalizeAgentRuntimeMode(mode) + prefix := "AGENT_RUNTIME_" + // Sub Agile primary route per agent_management Sub Mode Runtime §2.2. + defaultCreatePath := "/api/agent/sub-agile/deployments" + defaultStopPath := "/api/agent/sub-agile/deployments/{deployment_id}/stop" defaultApprovalPath := "/api/swarms/{swarm_id}/approvals/{approval_id}" - if mode == agnetRuntimeModeSwarm { + if mode == agentRuntimeModeSwarm { prefix = "SWARM_RUNTIME_" defaultCreatePath = "/api/swarms" defaultStopPath = "/api/swarms/{swarm_id}/stop" @@ -110,18 +111,18 @@ func agnetRuntimeClientConfigForMode(mode string) agnetRuntimeConfig { } baseURL := strings.TrimRight(strings.TrimSpace(common.GetEnvOrDefaultString(prefix+"BASE_URL", "")), "/") enabledDefault := false - if mode == agnetRuntimeModeAgnet { - enabledDefault = common.GetEnvOrDefaultBool("AGNET_RUNTIME_ENABLED", false) + if mode == agentRuntimeModeAgent { + enabledDefault = common.GetEnvOrDefaultBool("AGENT_RUNTIME_ENABLED", false) } else { enabledDefault = baseURL != "" } - return agnetRuntimeConfig{ + return agentRuntimeConfig{ Enabled: common.GetEnvOrDefaultBool(prefix+"ENABLED", enabledDefault), - Async: common.GetEnvOrDefaultBool(prefix+"ASYNC", common.GetEnvOrDefaultBool("AGNET_RUNTIME_ASYNC", true)), + Async: common.GetEnvOrDefaultBool(prefix+"ASYNC", common.GetEnvOrDefaultBool("AGENT_RUNTIME_ASYNC", true)), BaseURL: baseURL, Token: strings.TrimSpace(common.GetEnvOrDefaultString(prefix+"SERVICE_TOKEN", "")), CreatePath: common.GetEnvOrDefaultString(prefix+"CREATE_PATH", defaultCreatePath), - HealthPath: common.GetEnvOrDefaultString(prefix+"HEALTH_PATH", "/api/agnet/health"), + HealthPath: common.GetEnvOrDefaultString(prefix+"HEALTH_PATH", "/api/agent/health"), StatusPath: common.GetEnvOrDefaultString(prefix+"STATUS_PATH", "/api/swarms/{swarm_id}/status"), ArtifactContentPath: common.GetEnvOrDefaultString(prefix+"ARTIFACT_CONTENT_PATH", "/api/swarms/{swarm_id}/artifacts/{artifact_id}/content"), StopPath: common.GetEnvOrDefaultString(prefix+"STOP_PATH", defaultStopPath), @@ -130,19 +131,19 @@ func agnetRuntimeClientConfigForMode(mode string) agnetRuntimeConfig { } } -func agnetRuntimeCallbackURL() string { - if value := strings.TrimSpace(common.GetEnvOrDefaultString("AGNET_RUNTIME_CALLBACK_URL", "")); value != "" { +func agentRuntimeCallbackURL() string { + if value := strings.TrimSpace(common.GetEnvOrDefaultString("AGENT_RUNTIME_CALLBACK_URL", "")); value != "" { return value } baseURL := strings.TrimRight(strings.TrimSpace(common.GetEnvOrDefaultString("HEICODE_PUBLIC_BASE_URL", "https://code.xinghanlab.com")), "/") - return baseURL + "/api/agnet/callbacks/swarm-events" + return baseURL + "/api/agent/callbacks/runtime-events" } -func agnetRuntimeCallbackSigningSecretRef() string { - return strings.TrimSpace(common.GetEnvOrDefaultString("AGNET_RUNTIME_CALLBACK_SIGNING_SECRET_REF", "")) +func agentRuntimeCallbackSigningSecretRef() string { + return strings.TrimSpace(common.GetEnvOrDefaultString("AGENT_RUNTIME_CALLBACK_SIGNING_SECRET_REF", "")) } -func agnetRuntimeSubscribedEvents() []string { +func agentRuntimeSubscribedEvents() []string { return []string{ "deployment.status_changed", "phase.changed", @@ -178,7 +179,7 @@ func firstNonEmpty(values ...string) string { return "" } -func truncateAgnetFailureReason(value string) string { +func truncateAgentFailureReason(value string) string { value = strings.TrimSpace(value) if len(value) <= 480 { return value @@ -186,13 +187,13 @@ func truncateAgnetFailureReason(value string) string { return value[:480] } -func agnetRuntimeURL(baseURL string, path string) (string, error) { +func agentRuntimeURL(baseURL string, path string) (string, error) { if strings.TrimSpace(baseURL) == "" { - return "", errors.New("AGNET_RUNTIME_BASE_URL is not configured") + return "", errors.New("AGENT_RUNTIME_BASE_URL is not configured") } parsed, err := url.Parse(baseURL) if err != nil || parsed.Scheme == "" || parsed.Host == "" { - return "", errors.New("AGNET_RUNTIME_BASE_URL must be an absolute http(s) URL") + return "", errors.New("AGENT_RUNTIME_BASE_URL must be an absolute http(s) URL") } if strings.TrimSpace(path) == "" { path = "/" @@ -203,7 +204,7 @@ func agnetRuntimeURL(baseURL string, path string) (string, error) { return strings.TrimRight(baseURL, "/") + path, nil } -func agnetRuntimeHeaders(req *http.Request, cfg agnetRuntimeConfig, record agnetDeploymentRecord) { +func agentRuntimeHeaders(req *http.Request, cfg agentRuntimeConfig, record agentDeploymentRecord) { req.Header.Set("Content-Type", "application/json") req.Header.Set("X-User-ID", record.Plan.UserContext.UserID) req.Header.Set("X-Binding-Scope", firstPlanBindingScope(record.Plan)) @@ -214,8 +215,8 @@ func agnetRuntimeHeaders(req *http.Request, cfg agnetRuntimeConfig, record agnet } } -func agnetRuntimeRequestAgents(plan agnetOrchestrationPlan) []gin.H { - byRole := make(map[string]agnetAgentPlan, len(plan.Agents)) +func agentRuntimeRequestAgents(plan agentOrchestrationPlan) []gin.H { + byRole := make(map[string]agentAgentPlan, len(plan.Agents)) for _, agent := range plan.Agents { role := strings.TrimSpace(agent.RoleTemplate) if role != "" { @@ -235,7 +236,7 @@ func agnetRuntimeRequestAgents(plan agnetOrchestrationPlan) []gin.H { item["sk_sources"] = agent.SKSources } if len(agent.ResourceGrants) > 0 { - item["resource_grants"] = agnetRuntimeResourceGrantPayloads(agent.ResourceGrants) + item["resource_grants"] = agentRuntimeResourceGrantPayloads(agent.ResourceGrants) } } items = append(items, item) @@ -254,14 +255,14 @@ func agnetRuntimeRequestAgents(plan agnetOrchestrationPlan) []gin.H { item["sk_sources"] = agent.SKSources } if len(agent.ResourceGrants) > 0 { - item["resource_grants"] = agnetRuntimeResourceGrantPayloads(agent.ResourceGrants) + item["resource_grants"] = agentRuntimeResourceGrantPayloads(agent.ResourceGrants) } items = append(items, item) } return items } -func agnetRuntimeRequestSwarmAgents(plan agnetOrchestrationPlan) []gin.H { +func agentRuntimeRequestSwarmAgents(plan agentOrchestrationPlan) []gin.H { runtimeModels := make(map[string]string, len(plan.AgentRuntime.Agents)) for _, runtimeAgent := range plan.AgentRuntime.Agents { role := strings.TrimSpace(runtimeAgent.Role) @@ -276,7 +277,7 @@ func agnetRuntimeRequestSwarmAgents(plan agnetOrchestrationPlan) []gin.H { if role == "" { continue } - taskID := fmt.Sprintf("%s-%d", sanitizeAgnetRef(role), index+1) + taskID := fmt.Sprintf("%s-%d", sanitizeAgentRef(role), index+1) item := gin.H{ "task_id": taskID, "role": role, @@ -291,14 +292,14 @@ func agnetRuntimeRequestSwarmAgents(plan agnetOrchestrationPlan) []gin.H { item["sk_sources"] = agent.SKSources } if len(agent.ResourceGrants) > 0 { - item["resource_grants"] = agnetRuntimeResourceGrantPayloads(agent.ResourceGrants) + item["resource_grants"] = agentRuntimeResourceGrantPayloads(agent.ResourceGrants) } items = append(items, item) } return items } -func agnetRuntimeResourceGrantPayloads(grants []agnetResourceGrant) []gin.H { +func agentRuntimeResourceGrantPayloads(grants []agentResourceGrant) []gin.H { items := make([]gin.H, 0, len(grants)) for _, grant := range grants { secretRef := strings.TrimSpace(grant.SecretRef) @@ -319,8 +320,8 @@ func agnetRuntimeResourceGrantPayloads(grants []agnetResourceGrant) []gin.H { "status": grant.Status, "ref": secretRef, "secret_ref": secretRef, - "allowed_ref": agnetGrantResourceRef(grant), - "resource_ref_hint": agnetGrantResourceRef(grant), + "allowed_ref": agentGrantResourceRef(grant), + "resource_ref_hint": agentGrantResourceRef(grant), } if grant.TenantID != "" { item["tenant_id"] = grant.TenantID @@ -342,23 +343,23 @@ func agnetRuntimeResourceGrantPayloads(grants []agnetResourceGrant) []gin.H { return items } -func agnetRuntimeRequestResourceGrants(plan agnetOrchestrationPlan) []gin.H { - items := make([]agnetResourceGrant, 0, len(plan.ResourceGrants)) +func agentRuntimeRequestResourceGrants(plan agentOrchestrationPlan) []gin.H { + items := make([]agentResourceGrant, 0, len(plan.ResourceGrants)) items = append(items, plan.ResourceGrants...) for _, agent := range plan.Agents { items = append(items, agent.ResourceGrants...) } - return agnetRuntimeResourceGrantPayloads(items) + return agentRuntimeResourceGrantPayloads(items) } -func agnetRuntimeRequestMetadata(record agnetDeploymentRecord, source string) gin.H { +func agentRuntimeRequestMetadata(record agentDeploymentRecord, source string) gin.H { metadata := gin.H{ "correlation_id": record.Plan.Metadata.CorrelationID, "manager_deployment_id": record.DeploymentID, "source": source, "heicode_deployment_id": record.DeploymentID, "heicode_runtime_bridge": true, - "runtime_mode": agnetRuntimeModeForRecord(record), + "runtime_mode": agentRuntimeModeForRecord(record), } if record.Plan.Metadata.TenantID != "" { metadata["tenant_id"] = record.Plan.Metadata.TenantID @@ -369,7 +370,7 @@ func agnetRuntimeRequestMetadata(record agnetDeploymentRecord, source string) gi return metadata } -func agnetRuntimeBudgetPayload(budget agnetBudget) gin.H { +func agentRuntimeBudgetPayload(budget agentBudget) gin.H { return gin.H{ "max_tokens": budget.MaxTokens, "token_limit": budget.MaxTokens, @@ -380,8 +381,8 @@ func agnetRuntimeBudgetPayload(budget agnetBudget) gin.H { } } -func agnetRuntimeOrchestrationPlanPayload(record agnetDeploymentRecord) any { - if agnetRuntimeModeForRecord(record) != agnetRuntimeModeSwarm { +func agentRuntimeOrchestrationPlanPayload(record agentDeploymentRecord) any { + if agentRuntimeModeForRecord(record) != agentRuntimeModeSwarm { return record.Plan } plan := record.Plan @@ -391,50 +392,50 @@ func agnetRuntimeOrchestrationPlanPayload(record agnetDeploymentRecord) any { "objective": plan.Objective, "sub_mode": firstNonEmpty(plan.SubMode, "goal_driven_swarm"), "risk_level": plan.RiskLevel, - "budget": agnetRuntimeBudgetPayload(plan.Budget), + "budget": agentRuntimeBudgetPayload(plan.Budget), "user_context": plan.UserContext, "billing_context": plan.BillingContext, "agile_context": plan.AgileContext, - "agents": agnetRuntimeRequestSwarmAgents(plan), - "resource_grants": agnetRuntimeRequestResourceGrants(plan), + "agents": agentRuntimeRequestSwarmAgents(plan), + "resource_grants": agentRuntimeRequestResourceGrants(plan), "constraints": plan.Constraints, - "metadata": agnetRuntimeRequestMetadata(record, "orchestration_plan"), + "metadata": agentRuntimeRequestMetadata(record, "orchestration_plan"), "agent_runtime": plan.AgentRuntime, "acceptance": plan.AgileContext.AcceptanceCriteria, "acceptance_tests": plan.AgileContext.AcceptanceCriteria, } } -func agnetRuntimeCreatePayload(record agnetDeploymentRecord, source string) gin.H { +func agentRuntimeCreatePayload(record agentDeploymentRecord, source string) gin.H { callback := gin.H{ - "url": agnetRuntimeCallbackURL(), - "subscribed_events": agnetRuntimeSubscribedEvents(), + "url": agentRuntimeCallbackURL(), + "subscribed_events": agentRuntimeSubscribedEvents(), } - if ref := agnetRuntimeCallbackSigningSecretRef(); ref != "" { + if ref := agentRuntimeCallbackSigningSecretRef(); ref != "" { callback["signing_secret_ref"] = ref } return gin.H{ - "orchestration_plan": agnetRuntimeOrchestrationPlanPayload(record), - "agents": agnetRuntimeRequestAgents(record.Plan), + "orchestration_plan": agentRuntimeOrchestrationPlanPayload(record), + "agents": agentRuntimeRequestAgents(record.Plan), "risk_level": record.Plan.RiskLevel, - "budget": agnetRuntimeBudgetPayload(record.Plan.Budget), + "budget": agentRuntimeBudgetPayload(record.Plan.Budget), "billing_context": record.Plan.BillingContext, - "resource_grants": agnetRuntimeRequestResourceGrants(record.Plan), + "resource_grants": agentRuntimeRequestResourceGrants(record.Plan), "callback": callback, "agile_context": record.Plan.AgileContext, "sub_mode": record.Plan.SubMode, - "metadata": agnetRuntimeRequestMetadata(record, source), + "metadata": agentRuntimeRequestMetadata(record, source), } } -func extractAgnetRuntimeData(payload map[string]any) map[string]any { +func extractAgentRuntimeData(payload map[string]any) map[string]any { if data, ok := payload["data"].(map[string]any); ok { return data } return payload } -func agnetRuntimeEnvelopeError(payload map[string]any) string { +func agentRuntimeEnvelopeError(payload map[string]any) string { success, hasSuccess := payload["success"].(bool) if !hasSuccess || success { return "" @@ -463,44 +464,44 @@ func stringFromMap(values map[string]any, keys ...string) string { return "" } -func callAgnetRuntimeCreate(ctx context.Context, cfg agnetRuntimeConfig, record agnetDeploymentRecord, source string) (agnetRuntimeSyncResult, error) { - endpoint, err := agnetRuntimeURL(cfg.BaseURL, cfg.CreatePath) +func callAgentRuntimeCreate(ctx context.Context, cfg agentRuntimeConfig, record agentDeploymentRecord, source string) (agentRuntimeSyncResult, error) { + endpoint, err := agentRuntimeURL(cfg.BaseURL, cfg.CreatePath) if err != nil { - return agnetRuntimeSyncResult{}, err + return agentRuntimeSyncResult{}, err } - payload, err := common.Marshal(agnetRuntimeCreatePayload(record, source)) + payload, err := common.Marshal(agentRuntimeCreatePayload(record, source)) if err != nil { - return agnetRuntimeSyncResult{}, err + return agentRuntimeSyncResult{}, err } req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, bytes.NewReader(payload)) if err != nil { - return agnetRuntimeSyncResult{}, err + return agentRuntimeSyncResult{}, err } - agnetRuntimeHeaders(req, cfg, record) + agentRuntimeHeaders(req, cfg, record) client := &http.Client{Timeout: cfg.Timeout} resp, err := client.Do(req) if err != nil { - return agnetRuntimeSyncResult{}, err + return agentRuntimeSyncResult{}, err } defer resp.Body.Close() body, readErr := io.ReadAll(io.LimitReader(resp.Body, 1<<20)) if readErr != nil { - return agnetRuntimeSyncResult{}, readErr + return agentRuntimeSyncResult{}, readErr } if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices { - return agnetRuntimeSyncResult{RawStatusCode: resp.StatusCode}, fmt.Errorf("runtime create returned HTTP %d: %s", resp.StatusCode, strings.TrimSpace(string(body))) + return agentRuntimeSyncResult{RawStatusCode: resp.StatusCode}, fmt.Errorf("runtime create returned HTTP %d: %s", resp.StatusCode, strings.TrimSpace(string(body))) } var envelope map[string]any if len(body) > 0 { if err := common.Unmarshal(body, &envelope); err != nil { - return agnetRuntimeSyncResult{RawStatusCode: resp.StatusCode}, err + return agentRuntimeSyncResult{RawStatusCode: resp.StatusCode}, err } } - if message := agnetRuntimeEnvelopeError(envelope); message != "" { - return agnetRuntimeSyncResult{RawStatusCode: resp.StatusCode}, errors.New(message) + if message := agentRuntimeEnvelopeError(envelope); message != "" { + return agentRuntimeSyncResult{RawStatusCode: resp.StatusCode}, errors.New(message) } - data := extractAgnetRuntimeData(envelope) - result := agnetRuntimeSyncResult{ + data := extractAgentRuntimeData(envelope) + result := agentRuntimeSyncResult{ RuntimeDeploymentID: stringFromMap(data, "runtime_deployment_id", "deployment_id", "id"), RuntimeSwarmID: stringFromMap(data, "swarm_id", "runtime_swarm_id"), RuntimeStatus: stringFromMap(data, "runtime_status", "status"), @@ -509,16 +510,18 @@ func callAgnetRuntimeCreate(ctx context.Context, cfg agnetRuntimeConfig, record return result, nil } -func agnetRuntimeStopPath(cfg agnetRuntimeConfig, runtimeDeploymentID string) string { +func agentRuntimeStopPath(cfg agentRuntimeConfig, runtimeDeploymentID string) string { path := strings.TrimSpace(cfg.StopPath) if path == "" { - path = "/api/agnet/deployments/{deployment_id}/stop" + path = "/api/agent/deployments/{deployment_id}/stop" } return strings.ReplaceAll(path, "{deployment_id}", url.PathEscape(runtimeDeploymentID)) } -func agnetRuntimeStopPathForRecord(cfg agnetRuntimeConfig, record agnetDeploymentRecord) string { - path := agnetRuntimeStopPath(cfg, strings.TrimSpace(record.RuntimeDeploymentID)) +func agentRuntimeStopPathForRecord(cfg agentRuntimeConfig, record agentDeploymentRecord) string { + // {deployment_id} falls back to the swarm id so sub-agile-style paths still + // resolve for records that only persisted a runtime swarm id. + path := agentRuntimeStopPath(cfg, firstNonEmpty(record.RuntimeDeploymentID, record.RuntimeSwarmID)) replacer := strings.NewReplacer( "{swarm_id}", url.PathEscape(strings.TrimSpace(record.RuntimeSwarmID)), "{runtime_swarm_id}", url.PathEscape(strings.TrimSpace(record.RuntimeSwarmID)), @@ -528,7 +531,7 @@ func agnetRuntimeStopPathForRecord(cfg agnetRuntimeConfig, record agnetDeploymen return replacer.Replace(path) } -func agnetRuntimeStatusPathForRecord(cfg agnetRuntimeConfig, record agnetDeploymentRecord) string { +func agentRuntimeStatusPathForRecord(cfg agentRuntimeConfig, record agentDeploymentRecord) string { path := strings.TrimSpace(cfg.StatusPath) if path == "" { path = "/api/swarms/{swarm_id}/status" @@ -543,7 +546,7 @@ func agnetRuntimeStatusPathForRecord(cfg agnetRuntimeConfig, record agnetDeploym return replacer.Replace(path) } -func agnetRuntimeArtifactContentPathForRecord(cfg agnetRuntimeConfig, record agnetDeploymentRecord, artifactID string) string { +func agentRuntimeArtifactContentPathForRecord(cfg agentRuntimeConfig, record agentDeploymentRecord, artifactID string) string { path := strings.TrimSpace(cfg.ArtifactContentPath) if path == "" { path = "/api/swarms/{swarm_id}/artifacts/{artifact_id}/content" @@ -559,11 +562,11 @@ func agnetRuntimeArtifactContentPathForRecord(cfg agnetRuntimeConfig, record agn return replacer.Replace(path) } -func callAgnetRuntimeStatus(ctx context.Context, cfg agnetRuntimeConfig, record agnetDeploymentRecord) (map[string]any, int, error) { +func callAgentRuntimeStatus(ctx context.Context, cfg agentRuntimeConfig, record agentDeploymentRecord) (map[string]any, int, error) { if strings.TrimSpace(record.RuntimeSwarmID) == "" && strings.TrimSpace(record.RuntimeDeploymentID) == "" { return nil, 0, errors.New("runtime identifiers missing") } - endpoint, err := agnetRuntimeURL(cfg.BaseURL, agnetRuntimeStatusPathForRecord(cfg, record)) + endpoint, err := agentRuntimeURL(cfg.BaseURL, agentRuntimeStatusPathForRecord(cfg, record)) if err != nil { return nil, 0, err } @@ -571,7 +574,7 @@ func callAgnetRuntimeStatus(ctx context.Context, cfg agnetRuntimeConfig, record if err != nil { return nil, 0, err } - agnetRuntimeHeaders(req, cfg, record) + agentRuntimeHeaders(req, cfg, record) client := &http.Client{Timeout: cfg.Timeout} resp, err := client.Do(req) if err != nil { @@ -591,20 +594,20 @@ func callAgnetRuntimeStatus(ctx context.Context, cfg agnetRuntimeConfig, record return nil, resp.StatusCode, err } } - if message := agnetRuntimeEnvelopeError(envelope); message != "" { + if message := agentRuntimeEnvelopeError(envelope); message != "" { return nil, resp.StatusCode, errors.New(message) } - return extractAgnetRuntimeData(envelope), resp.StatusCode, nil + return extractAgentRuntimeData(envelope), resp.StatusCode, nil } -func callAgnetRuntimeArtifactContent(ctx context.Context, cfg agnetRuntimeConfig, record agnetDeploymentRecord, artifactID string) (*http.Response, error) { +func callAgentRuntimeArtifactContent(ctx context.Context, cfg agentRuntimeConfig, record agentDeploymentRecord, artifactID string) (*http.Response, error) { if strings.TrimSpace(artifactID) == "" { return nil, errors.New("artifact_id is required") } if strings.TrimSpace(record.RuntimeSwarmID) == "" && strings.TrimSpace(record.RuntimeDeploymentID) == "" { return nil, errors.New("runtime identifiers missing") } - endpoint, err := agnetRuntimeURL(cfg.BaseURL, agnetRuntimeArtifactContentPathForRecord(cfg, record, artifactID)) + endpoint, err := agentRuntimeURL(cfg.BaseURL, agentRuntimeArtifactContentPathForRecord(cfg, record, artifactID)) if err != nil { return nil, err } @@ -612,7 +615,7 @@ func callAgnetRuntimeArtifactContent(ctx context.Context, cfg agnetRuntimeConfig if err != nil { return nil, err } - agnetRuntimeHeaders(req, cfg, record) + agentRuntimeHeaders(req, cfg, record) req.Header.Set("Accept", "*/*") client := &http.Client{Timeout: cfg.Timeout} resp, err := client.Do(req) @@ -719,6 +722,14 @@ func anyPositiveFileSignal(values map[string]any) bool { // artifact_type="document" (its no-files fallback) under a uri scheme that the // old "/artifacts/summary" heuristic no longer matched. func artifactIsSummaryOnly(artifact gin.H) bool { + // Runtime marks fallback artifacts (no real agent output) with + // metadata.synthesized=true — the authoritative non-deliverable signal + // (agent_management Sub Mode Runtime §7.2). + if meta, ok := artifact["metadata"].(map[string]any); ok { + if synth, ok := meta["synthesized"].(bool); ok && synth { + return true + } + } atype := strings.ToLower(strings.TrimSpace(fmt.Sprint(artifact["artifact_type"]))) if deliverableArtifactTypes[atype] || artifactHasFileChanges(artifact) { return false @@ -749,7 +760,61 @@ func runtimeArtifactsAreSummaryOnly(artifacts []gin.H) bool { return true } -func buildAgnetRuntimeDiagnostics(record agnetDeploymentRecord, data map[string]any, httpStatus int, source string) agnetRuntimeDiagnostics { +// persistedArtifactToGin adapts a stored artifact to the gin.H shape consumed +// by artifactIsSummaryOnly. +func persistedArtifactToGin(a model.AgentArtifact) gin.H { + g := gin.H{ + "artifact_id": a.ArtifactID, + "artifact_type": a.ArtifactType, + "title": a.Title, + "summary": a.Summary, + "uri": a.URI, + } + if strings.TrimSpace(a.MetadataJSON) != "" { + var meta map[string]any + if err := common.UnmarshalJsonStr(a.MetadataJSON, &meta); err == nil && len(meta) > 0 { + g["metadata"] = meta + } + } + return g +} + +// agentDeploymentDisplayStatus is the single status the client should show. +// Manager is the sole judge (unified spec §10.6): a `completed` runtime status +// is only surfaced as `completed` when there is a real (non-summary) +// deliverable; otherwise it is downgraded so an empty result is not shown as +// success — `needs_codegen` when only a plan/summary exists, or +// `completed_without_deliverable` when no artifact exists at all. +func agentDeploymentDisplayStatus(record agentDeploymentRecord) string { + if strings.ToLower(strings.TrimSpace(record.Status)) != "completed" { + return record.Status + } + artifacts, err := model.ListAgentArtifacts(model.ListAgentArtifactsFilter{ + DeploymentID: record.DeploymentID, + Limit: 500, + }) + if err != nil { + common.SysLog("agentDeploymentDisplayStatus: " + err.Error()) + return record.Status + } + for _, a := range artifacts { + if !artifactIsSummaryOnly(persistedArtifactToGin(a)) { + return "completed" + } + } + if len(artifacts) > 0 { + return "needs_codegen" + } + return "completed_without_deliverable" +} + +// withDisplayStatus returns the record with DisplayStatus computed for response. +func withDisplayStatus(record agentDeploymentRecord) agentDeploymentRecord { + record.DisplayStatus = agentDeploymentDisplayStatus(record) + return record +} + +func buildAgentRuntimeDiagnostics(record agentDeploymentRecord, data map[string]any, httpStatus int, source string) agentRuntimeDiagnostics { agents := mapSliceFromAny(data["agents"]) artifacts := mapSliceFromAny(data["artifacts"]) status := stringFromMap(data, "runtime_status", "status") @@ -771,9 +836,9 @@ func buildAgnetRuntimeDiagnostics(record agnetDeploymentRecord, data map[string] warnings = append(warnings, "runtime_zero_model_usage") } } - return agnetRuntimeDiagnostics{ + return agentRuntimeDiagnostics{ DeploymentID: record.DeploymentID, - RuntimeMode: agnetRuntimeModeForRecord(record), + RuntimeMode: agentRuntimeModeForRecord(record), SubMode: record.SubMode, RuntimeDeploymentID: record.RuntimeDeploymentID, RuntimeSwarmID: record.RuntimeSwarmID, @@ -787,36 +852,36 @@ func buildAgnetRuntimeDiagnostics(record agnetDeploymentRecord, data map[string] Artifacts: artifacts, Metrics: metrics, Warnings: warnings, - CheckedAt: agnetNow(), + CheckedAt: agentNow(), } } -func agnetRuntimeDiagnosticsForRecord(ctx context.Context, record agnetDeploymentRecord) agnetRuntimeDiagnostics { - mode := agnetRuntimeModeForRecord(record) - cfg := agnetRuntimeClientConfigForMode(mode) +func agentRuntimeDiagnosticsForRecord(ctx context.Context, record agentDeploymentRecord) agentRuntimeDiagnostics { + mode := agentRuntimeModeForRecord(record) + cfg := agentRuntimeClientConfigForMode(mode) if !cfg.Enabled || strings.TrimSpace(cfg.BaseURL) == "" { - return agnetRuntimeDiagnostics{ + return agentRuntimeDiagnostics{ DeploymentID: record.DeploymentID, RuntimeMode: mode, SubMode: record.SubMode, DataSource: "not_configured", Warnings: []string{"runtime_not_configured"}, - CheckedAt: agnetNow(), + CheckedAt: agentNow(), } } if strings.TrimSpace(record.RuntimeSwarmID) == "" && strings.TrimSpace(record.RuntimeDeploymentID) == "" { - return agnetRuntimeDiagnostics{ + return agentRuntimeDiagnostics{ DeploymentID: record.DeploymentID, RuntimeMode: mode, SubMode: record.SubMode, DataSource: "missing_runtime_id", Warnings: []string{"runtime_identifiers_missing"}, - CheckedAt: agnetNow(), + CheckedAt: agentNow(), } } - data, status, err := callAgnetRuntimeStatus(ctx, cfg, record) + data, status, err := callAgentRuntimeStatus(ctx, cfg, record) if err != nil { - return agnetRuntimeDiagnostics{ + return agentRuntimeDiagnostics{ DeploymentID: record.DeploymentID, RuntimeMode: mode, SubMode: record.SubMode, @@ -824,25 +889,25 @@ func agnetRuntimeDiagnosticsForRecord(ctx context.Context, record agnetDeploymen RuntimeSwarmID: record.RuntimeSwarmID, DataSource: "runtime_status_error", HTTPStatus: status, - ErrorMessage: truncateAgnetFailureReason(err.Error()), + ErrorMessage: truncateAgentFailureReason(err.Error()), Warnings: []string{"runtime_status_query_failed"}, - CheckedAt: agnetNow(), + CheckedAt: agentNow(), } } - return buildAgnetRuntimeDiagnostics(record, data, status, "runtime_status") + return buildAgentRuntimeDiagnostics(record, data, status, "runtime_status") } -func AgnetGetUserDeploymentRuntimeDiagnostics(c *gin.Context) { - record, ok := requireAuthenticatedUserAgnetDeployment(c) +func AgentGetUserDeploymentRuntimeDiagnostics(c *gin.Context) { + record, ok := requireAuthenticatedUserAgentDeployment(c) if !ok { return } - ctx, cancel := context.WithTimeout(c.Request.Context(), agnetRuntimeClientConfigForMode(agnetRuntimeModeForRecord(record)).Timeout) + ctx, cancel := context.WithTimeout(c.Request.Context(), agentRuntimeClientConfigForMode(agentRuntimeModeForRecord(record)).Timeout) defer cancel() - common.ApiSuccess(c, agnetRuntimeDiagnosticsForRecord(ctx, record)) + common.ApiSuccess(c, agentRuntimeDiagnosticsForRecord(ctx, record)) } -func agnetRuntimeApprovalDecisionPath(cfg agnetRuntimeConfig, record agnetDeploymentRecord, approvalID string) string { +func agentRuntimeApprovalDecisionPath(cfg agentRuntimeConfig, record agentDeploymentRecord, approvalID string) string { path := strings.TrimSpace(cfg.ApprovalDecisionPath) if path == "" { path = "/api/swarms/{swarm_id}/approvals/{approval_id}" @@ -858,8 +923,8 @@ func agnetRuntimeApprovalDecisionPath(cfg agnetRuntimeConfig, record agnetDeploy return replacer.Replace(path) } -func callAgnetRuntimeApprovalDecision(ctx context.Context, cfg agnetRuntimeConfig, record agnetDeploymentRecord, approval model.AgnetApprovalRequest, lease *model.AgnetCredentialLease, decision string) error { - endpoint, err := agnetRuntimeURL(cfg.BaseURL, agnetRuntimeApprovalDecisionPath(cfg, record, approval.ApprovalID)) +func callAgentRuntimeApprovalDecision(ctx context.Context, cfg agentRuntimeConfig, record agentDeploymentRecord, approval model.AgentApprovalRequest, lease *model.AgentCredentialLease, decision string) error { + endpoint, err := agentRuntimeURL(cfg.BaseURL, agentRuntimeApprovalDecisionPath(cfg, record, approval.ApprovalID)) if err != nil { return err } @@ -922,87 +987,87 @@ func callAgnetRuntimeApprovalDecision(ctx context.Context, cfg agnetRuntimeConfi if err := common.Unmarshal(respBody, &envelope); err != nil { return err } - if message := agnetRuntimeEnvelopeError(envelope); message != "" { + if message := agentRuntimeEnvelopeError(envelope); message != "" { return errors.New(message) } return nil } -func syncAgnetRuntimeApprovalDecision(c *gin.Context, approval *model.AgnetApprovalRequest, lease *model.AgnetCredentialLease, decision string) { +func syncAgentRuntimeApprovalDecision(c *gin.Context, approval *model.AgentApprovalRequest, lease *model.AgentCredentialLease, decision string) { if approval == nil || strings.TrimSpace(approval.DeploymentID) == "" { return } - if !agnetRuntimeClientConfigForMode(agnetRuntimeModeAgnet).Enabled && !agnetRuntimeClientConfigForMode(agnetRuntimeModeSwarm).Enabled { + if !agentRuntimeClientConfigForMode(agentRuntimeModeAgent).Enabled && !agentRuntimeClientConfigForMode(agentRuntimeModeSwarm).Enabled { return } - record, ok := findAgnetDeploymentRecord(approval.DeploymentID) + record, ok := findAgentDeploymentRecord(approval.DeploymentID) if !ok { - recordAgnetApprovalAudit("runtime.approval_decision.skipped", approval, lease, "skipped", "deployment not found") + recordAgentApprovalAudit("runtime.approval_decision.skipped", approval, lease, "skipped", "deployment not found") return } - cfg := agnetRuntimeClientConfigForMode(agnetRuntimeModeForRecord(record)) + cfg := agentRuntimeClientConfigForMode(agentRuntimeModeForRecord(record)) if !cfg.Enabled { return } if strings.TrimSpace(record.RuntimeSwarmID) == "" && strings.TrimSpace(record.RuntimeDeploymentID) == "" { - recordAgnetApprovalAudit("runtime.approval_decision.skipped", approval, lease, "skipped", "runtime identifiers missing") + recordAgentApprovalAudit("runtime.approval_decision.skipped", approval, lease, "skipped", "runtime identifiers missing") return } ctx, cancel := context.WithTimeout(c.Request.Context(), cfg.Timeout) defer cancel() - if err := callAgnetRuntimeApprovalDecision(ctx, cfg, record, *approval, lease, decision); err != nil { - recordAgnetApprovalAudit("runtime.approval_decision.failed", approval, lease, "failed", truncateAgnetFailureReason(err.Error())) - common.SysLog("Agnet runtime approval decision failed for " + approval.ApprovalID + ": " + err.Error()) + if err := callAgentRuntimeApprovalDecision(ctx, cfg, record, *approval, lease, decision); err != nil { + recordAgentApprovalAudit("runtime.approval_decision.failed", approval, lease, "failed", truncateAgentFailureReason(err.Error())) + common.SysLog("Agent runtime approval decision failed for " + approval.ApprovalID + ": " + err.Error()) return } - recordAgnetApprovalAudit("runtime.approval_decision.accepted", approval, lease, "ok", "") + recordAgentApprovalAudit("runtime.approval_decision.accepted", approval, lease, "ok", "") } -func callAgnetRuntimeStop(ctx context.Context, cfg agnetRuntimeConfig, record agnetDeploymentRecord, reason string) (agnetRuntimeSyncResult, error) { +func callAgentRuntimeStop(ctx context.Context, cfg agentRuntimeConfig, record agentDeploymentRecord, reason string) (agentRuntimeSyncResult, error) { runtimeDeploymentID := strings.TrimSpace(record.RuntimeDeploymentID) if runtimeDeploymentID == "" { - return agnetRuntimeSyncResult{}, nil + return agentRuntimeSyncResult{}, nil } - endpoint, err := agnetRuntimeURL(cfg.BaseURL, agnetRuntimeStopPathForRecord(cfg, record)) + endpoint, err := agentRuntimeURL(cfg.BaseURL, agentRuntimeStopPathForRecord(cfg, record)) if err != nil { - return agnetRuntimeSyncResult{}, err + return agentRuntimeSyncResult{}, err } payload, err := common.Marshal(gin.H{ "reason": firstNonEmpty(strings.TrimSpace(reason), "Heicode Manager requested stop"), "manager_deployment_id": record.DeploymentID, }) if err != nil { - return agnetRuntimeSyncResult{}, err + return agentRuntimeSyncResult{}, err } req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, bytes.NewReader(payload)) if err != nil { - return agnetRuntimeSyncResult{}, err + return agentRuntimeSyncResult{}, err } - agnetRuntimeHeaders(req, cfg, record) + agentRuntimeHeaders(req, cfg, record) client := &http.Client{Timeout: cfg.Timeout} resp, err := client.Do(req) if err != nil { - return agnetRuntimeSyncResult{}, err + return agentRuntimeSyncResult{}, err } defer resp.Body.Close() body, readErr := io.ReadAll(io.LimitReader(resp.Body, 1<<20)) if readErr != nil { - return agnetRuntimeSyncResult{}, readErr + return agentRuntimeSyncResult{}, readErr } if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices { - return agnetRuntimeSyncResult{RawStatusCode: resp.StatusCode}, fmt.Errorf("runtime stop returned HTTP %d: %s", resp.StatusCode, strings.TrimSpace(string(body))) + return agentRuntimeSyncResult{RawStatusCode: resp.StatusCode}, fmt.Errorf("runtime stop returned HTTP %d: %s", resp.StatusCode, strings.TrimSpace(string(body))) } var envelope map[string]any if len(body) > 0 { if err := common.Unmarshal(body, &envelope); err != nil { - return agnetRuntimeSyncResult{RawStatusCode: resp.StatusCode}, err + return agentRuntimeSyncResult{RawStatusCode: resp.StatusCode}, err } } - if message := agnetRuntimeEnvelopeError(envelope); message != "" { - return agnetRuntimeSyncResult{RawStatusCode: resp.StatusCode}, errors.New(message) + if message := agentRuntimeEnvelopeError(envelope); message != "" { + return agentRuntimeSyncResult{RawStatusCode: resp.StatusCode}, errors.New(message) } - data := extractAgnetRuntimeData(envelope) - return agnetRuntimeSyncResult{ + data := extractAgentRuntimeData(envelope) + return agentRuntimeSyncResult{ RuntimeDeploymentID: firstNonEmpty(stringFromMap(data, "runtime_deployment_id", "deployment_id", "id"), runtimeDeploymentID), RuntimeSwarmID: stringFromMap(data, "swarm_id", "runtime_swarm_id"), RuntimeStatus: stringFromMap(data, "runtime_status", "status"), @@ -1010,25 +1075,25 @@ func callAgnetRuntimeStop(ctx context.Context, cfg agnetRuntimeConfig, record ag }, nil } -func syncAgnetRuntimeStop(c *gin.Context, record agnetDeploymentRecord, reason string) (agnetDeploymentRecord, bool) { - cfg := agnetRuntimeClientConfigForMode(agnetRuntimeModeForRecord(record)) +func syncAgentRuntimeStop(c *gin.Context, record agentDeploymentRecord, reason string) (agentDeploymentRecord, bool) { + cfg := agentRuntimeClientConfigForMode(agentRuntimeModeForRecord(record)) if !cfg.Enabled || strings.TrimSpace(record.RuntimeDeploymentID) == "" { return record, true } ctx, cancel := context.WithTimeout(c.Request.Context(), cfg.Timeout) defer cancel() - result, err := callAgnetRuntimeStop(ctx, cfg, record, reason) - record.RuntimeLastSyncAt = agnetNow() + result, err := callAgentRuntimeStop(ctx, cfg, record, reason) + record.RuntimeLastSyncAt = agentNow() if err != nil { - record.RuntimeState = agnetRuntimeStateFailed - record.FailureReason = truncateAgnetFailureReason(err.Error()) - record.UpdatedAt = agnetNow() - _ = updateAgnetDeploymentRecord(record) - agnetMu.Lock() - agnetDeployments[record.DeploymentID] = record - agnetMu.Unlock() - recordAgnetRuntimeSyncAudit(record, "runtime.stop.failed", "failed") - agnetError(c, "RUNTIME_STOP_FAILED", record.FailureReason) + record.RuntimeState = agentRuntimeStateFailed + record.FailureReason = truncateAgentFailureReason(err.Error()) + record.UpdatedAt = agentNow() + _ = updateAgentDeploymentRecord(record) + agentMu.Lock() + agentDeployments[record.DeploymentID] = record + agentMu.Unlock() + recordAgentRuntimeSyncAudit(record, "runtime.stop.failed", "failed") + agentError(c, "RUNTIME_STOP_FAILED", record.FailureReason) return record, false } record.RuntimeState = firstNonEmpty(result.RuntimeStatus, "stopped") @@ -1039,33 +1104,33 @@ func syncAgnetRuntimeStop(c *gin.Context, record agnetDeploymentRecord, reason s record.RuntimeSwarmID = result.RuntimeSwarmID } record.FailureReason = "" - recordAgnetRuntimeSyncAudit(record, "runtime.stop.accepted", "ok") + recordAgentRuntimeSyncAudit(record, "runtime.stop.accepted", "ok") return record, true } -func updateAgnetRuntimeSyncState(record agnetDeploymentRecord, result agnetRuntimeSyncResult, syncErr error) agnetDeploymentRecord { - record.RuntimeLastSyncAt = agnetNow() +func updateAgentRuntimeSyncState(record agentDeploymentRecord, result agentRuntimeSyncResult, syncErr error) agentDeploymentRecord { + record.RuntimeLastSyncAt = agentNow() if syncErr != nil { - record.RuntimeState = agnetRuntimeStateFailed - record.FailureReason = truncateAgnetFailureReason(syncErr.Error()) + record.RuntimeState = agentRuntimeStateFailed + record.FailureReason = truncateAgentFailureReason(syncErr.Error()) } else { - record.RuntimeState = firstNonEmpty(result.RuntimeStatus, agnetRuntimeStateSynced) + record.RuntimeState = firstNonEmpty(result.RuntimeStatus, agentRuntimeStateSynced) record.RuntimeDeploymentID = result.RuntimeDeploymentID record.RuntimeSwarmID = result.RuntimeSwarmID record.FailureReason = "" } - record.UpdatedAt = agnetNow() - if err := updateAgnetDeploymentRecord(record); err != nil { - common.SysLog("updateAgnetRuntimeSyncState: " + err.Error()) + record.UpdatedAt = agentNow() + if err := updateAgentDeploymentRecord(record); err != nil { + common.SysLog("updateAgentRuntimeSyncState: " + err.Error()) } - agnetMu.Lock() - agnetDeployments[record.DeploymentID] = record - agnetMu.Unlock() + agentMu.Lock() + agentDeployments[record.DeploymentID] = record + agentMu.Unlock() return record } -func recordAgnetRuntimeSyncAudit(record agnetDeploymentRecord, event string, result string) { - recordAgnetAuditEvent(agnetEvent{ +func recordAgentRuntimeSyncAudit(record agentDeploymentRecord, event string, result string) { + recordAgentAuditEvent(agentEvent{ EventID: "evt_" + common.GetUUID()[:12], Event: event, SchemaVersion: 1, @@ -1074,79 +1139,79 @@ func recordAgnetRuntimeSyncAudit(record agnetDeploymentRecord, event string, res BindingScope: firstPlanBindingScope(record.Plan), DeploymentID: record.DeploymentID, CorrelationID: record.Plan.Metadata.CorrelationID, - OccurredAt: agnetNow(), - }, "agnet_runtime_bridge", record.DeploymentID, "", result) + OccurredAt: agentNow(), + }, "agent_runtime_bridge", record.DeploymentID, "", result) } -func dispatchAgnetRuntimeCreate(record agnetDeploymentRecord, source string, cfg agnetRuntimeConfig) agnetDeploymentRecord { - recordAgnetRuntimeSyncAudit(record, "runtime.sync.started", "started") +func dispatchAgentRuntimeCreate(record agentDeploymentRecord, source string, cfg agentRuntimeConfig) agentDeploymentRecord { + recordAgentRuntimeSyncAudit(record, "runtime.sync.started", "started") ctx, cancel := context.WithTimeout(context.Background(), cfg.Timeout) defer cancel() - result, err := callAgnetRuntimeCreate(ctx, cfg, record, source) - record = updateAgnetRuntimeSyncState(record, result, err) + result, err := callAgentRuntimeCreate(ctx, cfg, record, source) + record = updateAgentRuntimeSyncState(record, result, err) if err != nil { - common.SysLog("Agnet runtime shadow create failed for " + record.DeploymentID + ": " + err.Error()) - recordAgnetRuntimeSyncAudit(record, "runtime.sync.failed", "failed") + common.SysLog("Agent runtime shadow create failed for " + record.DeploymentID + ": " + err.Error()) + recordAgentRuntimeSyncAudit(record, "runtime.sync.failed", "failed") return record } - recordAgnetRuntimeSyncAudit(record, "runtime.sync.accepted", "ok") + recordAgentRuntimeSyncAudit(record, "runtime.sync.accepted", "ok") return record } -func maybeDispatchAgnetRuntimeCreate(c *gin.Context, record agnetDeploymentRecord, source string) agnetDeploymentRecord { - mode := agnetRuntimeModeForSource(source) +func maybeDispatchAgentRuntimeCreate(c *gin.Context, record agentDeploymentRecord, source string) agentDeploymentRecord { + mode := agentRuntimeModeForSource(source) if strings.TrimSpace(record.Plan.Metadata.RuntimeMode) == "" { record.Plan.Metadata.RuntimeMode = mode } else { - mode = agnetRuntimeModeForRecord(record) + mode = agentRuntimeModeForRecord(record) } - cfg := agnetRuntimeClientConfigForMode(mode) + cfg := agentRuntimeClientConfigForMode(mode) if !cfg.Enabled { return record } - if _, err := agnetRuntimeURL(cfg.BaseURL, cfg.CreatePath); err != nil { - record.RuntimeState = agnetRuntimeStateFailed - record.RuntimeLastSyncAt = agnetNow() - record.FailureReason = truncateAgnetFailureReason(err.Error()) - record.UpdatedAt = agnetNow() - _ = updateAgnetDeploymentRecord(record) - recordAgnetRuntimeSyncAudit(record, "runtime.sync.failed", "failed") + if _, err := agentRuntimeURL(cfg.BaseURL, cfg.CreatePath); err != nil { + record.RuntimeState = agentRuntimeStateFailed + record.RuntimeLastSyncAt = agentNow() + record.FailureReason = truncateAgentFailureReason(err.Error()) + record.UpdatedAt = agentNow() + _ = updateAgentDeploymentRecord(record) + recordAgentRuntimeSyncAudit(record, "runtime.sync.failed", "failed") return record } - record.RuntimeState = agnetRuntimeStateSyncing - record.RuntimeLastSyncAt = agnetNow() - record.UpdatedAt = agnetNow() - if err := updateAgnetDeploymentRecord(record); err != nil { - common.SysLog("maybeDispatchAgnetRuntimeCreate: " + err.Error()) + record.RuntimeState = agentRuntimeStateSyncing + record.RuntimeLastSyncAt = agentNow() + record.UpdatedAt = agentNow() + if err := updateAgentDeploymentRecord(record); err != nil { + common.SysLog("maybeDispatchAgentRuntimeCreate: " + err.Error()) } - agnetMu.Lock() - agnetDeployments[record.DeploymentID] = record - agnetMu.Unlock() + agentMu.Lock() + agentDeployments[record.DeploymentID] = record + agentMu.Unlock() if cfg.Async { sourceCopy := source recordCopy := record - go dispatchAgnetRuntimeCreate(recordCopy, sourceCopy, cfg) + go dispatchAgentRuntimeCreate(recordCopy, sourceCopy, cfg) return record } - return dispatchAgnetRuntimeCreate(record, source, cfg) + return dispatchAgentRuntimeCreate(record, source, cfg) } -func AgnetRuntimeHealth(c *gin.Context) { - cfg := agnetRuntimeClientConfigForMode(c.Query("mode")) +func AgentRuntimeHealth(c *gin.Context) { + cfg := agentRuntimeClientConfigForMode(c.Query("mode")) data := gin.H{ "enabled": cfg.Enabled, "configured": cfg.BaseURL != "", "create_path": cfg.CreatePath, "health_path": cfg.HealthPath, "stop_path": cfg.StopPath, - "mode": normalizeAgnetRuntimeMode(c.Query("mode")), + "mode": normalizeAgentRuntimeMode(c.Query("mode")), } if cfg.BaseURL == "" { data["status"] = "not_configured" common.ApiSuccess(c, data) return } - endpoint, err := agnetRuntimeURL(cfg.BaseURL, cfg.HealthPath) + endpoint, err := agentRuntimeURL(cfg.BaseURL, cfg.HealthPath) if err != nil { data["status"] = "invalid_config" data["message"] = err.Error() diff --git a/heicode/controller/agent_task_bridge.go b/heicode/controller/agent_task_bridge.go new file mode 100644 index 0000000..560abd2 --- /dev/null +++ b/heicode/controller/agent_task_bridge.go @@ -0,0 +1,299 @@ +package controller + +import ( + "fmt" + "strconv" + "strings" + + "github.com/gin-gonic/gin" + "github.com/heicode/manager/common" + "github.com/heicode/manager/model" +) + +type agentTaskDeploymentDraftRequest struct { + Task agentTaskSnapshot `json:"task"` + SubMode string `json:"sub_mode"` + RiskLevel string `json:"risk_level"` + Budget agentBudget `json:"budget"` + BindingScope string `json:"binding_scope"` + RoleTemplates []string `json:"role_templates"` + DefaultModelID string `json:"default_model_id"` + RoleModels map[string]string `json:"role_models"` + ResourceGrants []agentResourceGrant `json:"resource_grants"` +} + +type agentTaskSnapshot struct { + ID string `json:"id"` + Name string `json:"name"` + Intent string `json:"intent"` + Status string `json:"status"` + Card map[string]any `json:"card"` +} + +func stringFromTaskCard(card map[string]any, key string) string { + if card == nil { + return "" + } + if value, ok := card[key].(string); ok { + return strings.TrimSpace(value) + } + return "" +} + +func objectiveFromTaskSnapshot(task agentTaskSnapshot) string { + for _, value := range []string{ + stringFromTaskCard(task.Card, "goal"), + task.Name, + task.Intent, + } { + if strings.TrimSpace(value) != "" { + return strings.TrimSpace(value) + } + } + return "" +} + +func normalizeAgentDraftBudget(budget agentBudget) agentBudget { + if budget.MaxTokens <= 0 { + budget.MaxTokens = 120000 + } + if budget.MaxCostUSD <= 0 { + budget.MaxCostUSD = 8 + } + if budget.MaxDurationSec <= 0 { + budget.MaxDurationSec = 3600 + } + return budget +} + +func normalizeAgentDraftRoleTemplates(values []string) []string { + roles := make([]string, 0, len(values)) + for _, value := range values { + role := strings.TrimSpace(value) + if role == "" { + continue + } + roles = append(roles, role) + } + if len(roles) == 0 { + return []string{"backend"} + } + return roles +} + +func defaultAgentTaskBindingScope(taskID string) string { + bindingScope := "task-" + sanitizeAgentRef(taskID) + if bindingScope == "task-" { + return "task-local" + } + return bindingScope +} + +func defaultTaskDraftResourceGrant(userID string, bindingScope string, role string, taskID string) agentResourceGrant { + return agentResourceGrant{ + GrantID: "grant-" + sanitizeAgentRef(taskID) + "-" + sanitizeAgentRef(role), + ResourceID: "task-" + sanitizeAgentRef(taskID) + "-context", + ResourceType: agentResourceProjectDoc, + UserID: userID, + BindingScope: bindingScope, + TargetRole: role, + TargetAgentRef: "agent-" + sanitizeAgentRef(role) + "-1", + PermissionScope: []string{"doc:read"}, + Constraints: map[string]string{"ref": "task-card"}, + Metadata: map[string]string{"provider": "heicode-task", "resource_ref": taskID}, + Status: agentGrantStatusActive, + Audit: map[string]string{"source": "heicode-task-draft"}, + } +} + +func sanitizeAgentRef(value string) string { + value = strings.ToLower(strings.TrimSpace(value)) + var b strings.Builder + for _, r := range value { + switch { + case r >= 'a' && r <= 'z': + b.WriteRune(r) + case r >= '0' && r <= '9': + b.WriteRune(r) + case r == '-' || r == '_': + b.WriteRune(r) + default: + b.WriteRune('-') + } + } + return strings.Trim(b.String(), "-") +} + +func buildAgentDraftAgentPlan(role string, defaultModelID string, grants []agentResourceGrant) agentAgentPlan { + if defaultModelID == "" { + defaultModelID = defaultAgentModelID() + } + return agentAgentPlan{ + RoleTemplate: role, + Goal: fmt.Sprintf("Execute the Heicode task as %s within the approved resource scope.", role), + DefaultModelID: defaultModelID, + ResourceGrants: grants, + } +} + +func normalizeTaskDraftResourceGrants(userID string, bindingScope string, role string, taskID string, grants []agentResourceGrant) []agentResourceGrant { + if len(grants) == 0 { + return []agentResourceGrant{defaultTaskDraftResourceGrant(userID, bindingScope, role, taskID)} + } + normalized := make([]agentResourceGrant, 0, len(grants)) + for idx, grant := range grants { + grant.UserID = userID + if strings.TrimSpace(grant.GrantID) == "" { + grant.GrantID = fmt.Sprintf("grant-%s-%s-%d", sanitizeAgentRef(taskID), sanitizeAgentRef(role), idx+1) + } + if strings.TrimSpace(grant.BindingScope) == "" { + grant.BindingScope = bindingScope + } + if strings.TrimSpace(grant.TargetRole) == "" { + grant.TargetRole = role + } + if strings.TrimSpace(grant.TargetAgentRef) == "" { + grant.TargetAgentRef = "agent-" + sanitizeAgentRef(role) + "-1" + } + if strings.TrimSpace(grant.Status) == "" { + grant.Status = agentGrantStatusActive + } + grant = resolveResourceBindingIntoGrant(userID, grant) + normalized = append(normalized, grant) + } + return normalized +} + +// resolveResourceBindingIntoGrant injects the stored ResourceBinding's real +// secret_ref and resource metadata when the client referenced a binding by id +// instead of inlining a secret_ref (unified spec §17.6). The binding must be +// owned by the requesting user; unknown/unowned ids are left untouched so the +// existing plan validation surfaces a clear error. +func resolveResourceBindingIntoGrant(userID string, grant agentResourceGrant) agentResourceGrant { + if grant.ResourceBindingID <= 0 || model.DB == nil { + return grant + } + uid, _ := strconv.Atoi(strings.TrimSpace(userID)) + if uid <= 0 { + return grant + } + var binding model.ResourceBinding + if err := model.DB.Where("id = ? AND user_id = ?", grant.ResourceBindingID, uid).First(&binding).Error; err != nil { + return grant + } + if strings.TrimSpace(grant.SecretRef) == "" { + grant.SecretRef = strings.TrimSpace(binding.SecretRef) + } + if strings.TrimSpace(grant.ResourceID) == "" { + grant.ResourceID = fmt.Sprintf("rb_%d", binding.Id) + } + if strings.TrimSpace(grant.ResourceType) == "" { + grant.ResourceType = strings.TrimSpace(binding.ResourceType) + } + if strings.TrimSpace(grant.BindingScope) == "" { + grant.BindingScope = strings.TrimSpace(binding.BindingScope) + } + return grant +} + +func AgentCreateTaskDeploymentDraft(c *gin.Context) { + taskID := strings.TrimSpace(c.Param("task_id")) + if taskID == "" { + agentError(c, "TASK_NOT_FOUND", "task_id is required") + return + } + + var req agentTaskDeploymentDraftRequest + if err := c.ShouldBindJSON(&req); err != nil { + agentError(c, "POLICY_REJECTED", err.Error()) + return + } + if strings.TrimSpace(req.Task.ID) == "" { + agentError(c, "TASK_NOT_FOUND", "task snapshot is required") + return + } + if strings.TrimSpace(req.Task.ID) != taskID { + agentError(c, "TASK_CONFLICT", "task snapshot id must match route task_id") + return + } + if !isValidAgentSubMode(req.SubMode) { + agentError(c, "POLICY_REJECTED", "sub_mode must be agile or waterfall") + return + } + + userID := strconv.Itoa(c.GetInt("id")) + if userID == "0" { + agentError(c, "POLICY_REJECTED", "authenticated user is required") + return + } + objective := objectiveFromTaskSnapshot(req.Task) + if objective == "" { + agentError(c, "POLICY_REJECTED", "task objective is required") + return + } + + bindingScope := strings.TrimSpace(req.BindingScope) + if bindingScope == "" { + bindingScope = defaultAgentTaskBindingScope(taskID) + } + roles := normalizeAgentDraftRoleTemplates(req.RoleTemplates) + riskLevel := strings.TrimSpace(req.RiskLevel) + if riskLevel == "" { + riskLevel = agentRiskLow + } + defaultModelID := strings.TrimSpace(req.DefaultModelID) + group := strings.TrimSpace(c.GetString("group")) + agents := make([]agentAgentPlan, 0, len(roles)) + runtimeAgents := make([]agentRuntimeAgent, 0, len(roles)) + // per_role model selection (unified spec §9): role_models[role] wins, then + // the request default, then the platform default. Every resolved model is + // collected into allowed_model_ids so create-time validation accepts them. + allowedSeen := map[string]bool{} + allowedModels := []string{} + addAllowedModel := func(m string) { + m = strings.TrimSpace(m) + if m == "" || allowedSeen[m] { + return + } + allowedSeen[m] = true + allowedModels = append(allowedModels, m) + } + for _, role := range roles { + grants := normalizeTaskDraftResourceGrants(userID, bindingScope, role, taskID, req.ResourceGrants) + modelRef := firstNonEmpty(req.RoleModels[role], defaultModelID, defaultAgentModelID()) + agents = append(agents, buildAgentDraftAgentPlan(role, modelRef, grants)) + runtimeAgents = append(runtimeAgents, agentRuntimeAgent{Role: role, ModelRef: modelRef, InstanceCount: 1}) + addAllowedModel(modelRef) + } + + plan := agentOrchestrationPlan{ + IntentID: taskID, + TemplateHint: "heicode-task", + Objective: objective, + SubMode: normalizeAgentSubMode(req.SubMode), + RiskLevel: riskLevel, + Budget: normalizeAgentDraftBudget(req.Budget), + UserContext: agentUserContext{ + UserID: userID, + Role: "user", + ChannelID: group, + }, + AgentRuntime: agentAgentRuntime{Platform: "agent", Agents: runtimeAgents}, + Agents: agents, + Constraints: agentConstraints{AllowedModelIDs: allowedModels}, + Metadata: agentMetadata{ + CorrelationID: "task-" + sanitizeAgentRef(taskID) + "-" + common.GetUUID()[:8], + }, + } + if group != "" { + plan.BillingContext = agentBillingContext{Provider: "newapi", NewAPIGroup: group} + } + if !validateOrchestrationPlan(c, plan) { + return + } + + common.ApiSuccess(c, gin.H{ + "task_id": taskID, + "orchestration_plan": plan, + }) +} diff --git a/heicode/controller/agnet_task_bridge.go b/heicode/controller/agnet_task_bridge.go deleted file mode 100644 index 348c89a..0000000 --- a/heicode/controller/agnet_task_bridge.go +++ /dev/null @@ -1,253 +0,0 @@ -package controller - -import ( - "fmt" - "strconv" - "strings" - - "github.com/gin-gonic/gin" - "github.com/heicode/manager/common" -) - -type agnetTaskDeploymentDraftRequest struct { - Task agnetTaskSnapshot `json:"task"` - SubMode string `json:"sub_mode"` - RiskLevel string `json:"risk_level"` - Budget agnetBudget `json:"budget"` - BindingScope string `json:"binding_scope"` - RoleTemplates []string `json:"role_templates"` - DefaultModelID string `json:"default_model_id"` - ResourceGrants []agnetResourceGrant `json:"resource_grants"` -} - -type agnetTaskSnapshot struct { - ID string `json:"id"` - Name string `json:"name"` - Intent string `json:"intent"` - Status string `json:"status"` - Card map[string]any `json:"card"` -} - -func stringFromTaskCard(card map[string]any, key string) string { - if card == nil { - return "" - } - if value, ok := card[key].(string); ok { - return strings.TrimSpace(value) - } - return "" -} - -func objectiveFromTaskSnapshot(task agnetTaskSnapshot) string { - for _, value := range []string{ - stringFromTaskCard(task.Card, "goal"), - task.Name, - task.Intent, - } { - if strings.TrimSpace(value) != "" { - return strings.TrimSpace(value) - } - } - return "" -} - -func normalizeAgnetDraftBudget(budget agnetBudget) agnetBudget { - if budget.MaxTokens <= 0 { - budget.MaxTokens = 120000 - } - if budget.MaxCostUSD <= 0 { - budget.MaxCostUSD = 8 - } - if budget.MaxDurationSec <= 0 { - budget.MaxDurationSec = 3600 - } - return budget -} - -func normalizeAgnetDraftRoleTemplates(values []string) []string { - roles := make([]string, 0, len(values)) - for _, value := range values { - role := strings.TrimSpace(value) - if role == "" { - continue - } - roles = append(roles, role) - } - if len(roles) == 0 { - return []string{"backend"} - } - return roles -} - -func defaultAgnetTaskBindingScope(taskID string) string { - bindingScope := "task-" + sanitizeAgnetRef(taskID) - if bindingScope == "task-" { - return "task-local" - } - return bindingScope -} - -func defaultTaskDraftResourceGrant(userID string, bindingScope string, role string, taskID string) agnetResourceGrant { - return agnetResourceGrant{ - GrantID: "grant-" + sanitizeAgnetRef(taskID) + "-" + sanitizeAgnetRef(role), - ResourceID: "task-" + sanitizeAgnetRef(taskID) + "-context", - ResourceType: agnetResourceProjectDoc, - UserID: userID, - BindingScope: bindingScope, - TargetRole: role, - TargetAgentRef: "agent-" + sanitizeAgnetRef(role) + "-1", - PermissionScope: []string{"doc:read"}, - Constraints: map[string]string{"ref": "task-card"}, - Metadata: map[string]string{"provider": "heicode-task", "resource_ref": taskID}, - Status: agnetGrantStatusActive, - Audit: map[string]string{"source": "heicode-task-draft"}, - } -} - -func sanitizeAgnetRef(value string) string { - value = strings.ToLower(strings.TrimSpace(value)) - var b strings.Builder - for _, r := range value { - switch { - case r >= 'a' && r <= 'z': - b.WriteRune(r) - case r >= '0' && r <= '9': - b.WriteRune(r) - case r == '-' || r == '_': - b.WriteRune(r) - default: - b.WriteRune('-') - } - } - return strings.Trim(b.String(), "-") -} - -func buildAgnetDraftAgentPlan(role string, defaultModelID string, grants []agnetResourceGrant) agnetAgentPlan { - if defaultModelID == "" { - defaultModelID = defaultAgnetModelID() - } - return agnetAgentPlan{ - RoleTemplate: role, - Goal: fmt.Sprintf("Execute the Heicode task as %s within the approved resource scope.", role), - DefaultModelID: defaultModelID, - ResourceGrants: grants, - } -} - -func normalizeTaskDraftResourceGrants(userID string, bindingScope string, role string, taskID string, grants []agnetResourceGrant) []agnetResourceGrant { - if len(grants) == 0 { - return []agnetResourceGrant{defaultTaskDraftResourceGrant(userID, bindingScope, role, taskID)} - } - normalized := make([]agnetResourceGrant, 0, len(grants)) - for idx, grant := range grants { - grant.UserID = userID - if strings.TrimSpace(grant.GrantID) == "" { - grant.GrantID = fmt.Sprintf("grant-%s-%s-%d", sanitizeAgnetRef(taskID), sanitizeAgnetRef(role), idx+1) - } - if strings.TrimSpace(grant.BindingScope) == "" { - grant.BindingScope = bindingScope - } - if strings.TrimSpace(grant.TargetRole) == "" { - grant.TargetRole = role - } - if strings.TrimSpace(grant.TargetAgentRef) == "" { - grant.TargetAgentRef = "agent-" + sanitizeAgnetRef(role) + "-1" - } - if strings.TrimSpace(grant.Status) == "" { - grant.Status = agnetGrantStatusActive - } - normalized = append(normalized, grant) - } - return normalized -} - -func AgnetCreateTaskDeploymentDraft(c *gin.Context) { - taskID := strings.TrimSpace(c.Param("task_id")) - if taskID == "" { - agnetError(c, "TASK_NOT_FOUND", "task_id is required") - return - } - - var req agnetTaskDeploymentDraftRequest - if err := c.ShouldBindJSON(&req); err != nil { - agnetError(c, "POLICY_REJECTED", err.Error()) - return - } - if strings.TrimSpace(req.Task.ID) == "" { - agnetError(c, "TASK_NOT_FOUND", "task snapshot is required") - return - } - if strings.TrimSpace(req.Task.ID) != taskID { - agnetError(c, "TASK_CONFLICT", "task snapshot id must match route task_id") - return - } - if !isValidAgnetSubMode(req.SubMode) { - agnetError(c, "POLICY_REJECTED", "sub_mode must be agile or waterfall") - return - } - - userID := strconv.Itoa(c.GetInt("id")) - if userID == "0" { - agnetError(c, "POLICY_REJECTED", "authenticated user is required") - return - } - objective := objectiveFromTaskSnapshot(req.Task) - if objective == "" { - agnetError(c, "POLICY_REJECTED", "task objective is required") - return - } - - bindingScope := strings.TrimSpace(req.BindingScope) - if bindingScope == "" { - bindingScope = defaultAgnetTaskBindingScope(taskID) - } - roles := normalizeAgnetDraftRoleTemplates(req.RoleTemplates) - riskLevel := strings.TrimSpace(req.RiskLevel) - if riskLevel == "" { - riskLevel = agnetRiskLow - } - defaultModelID := strings.TrimSpace(req.DefaultModelID) - group := strings.TrimSpace(c.GetString("group")) - agents := make([]agnetAgentPlan, 0, len(roles)) - runtimeAgents := make([]agnetRuntimeAgent, 0, len(roles)) - for _, role := range roles { - grants := normalizeTaskDraftResourceGrants(userID, bindingScope, role, taskID, req.ResourceGrants) - agents = append(agents, buildAgnetDraftAgentPlan(role, defaultModelID, grants)) - modelRef := defaultModelID - if modelRef == "" { - modelRef = defaultAgnetModelID() - } - runtimeAgents = append(runtimeAgents, agnetRuntimeAgent{Role: role, ModelRef: modelRef, InstanceCount: 1}) - } - - plan := agnetOrchestrationPlan{ - IntentID: taskID, - TemplateHint: "heicode-task", - Objective: objective, - SubMode: normalizeAgnetSubMode(req.SubMode), - RiskLevel: riskLevel, - Budget: normalizeAgnetDraftBudget(req.Budget), - UserContext: agnetUserContext{ - UserID: userID, - Role: "user", - ChannelID: group, - }, - AgentRuntime: agnetAgentRuntime{Platform: "agnet", Agents: runtimeAgents}, - Agents: agents, - Constraints: agnetConstraints{AllowedModelIDs: []string{}}, - Metadata: agnetMetadata{ - CorrelationID: "task-" + sanitizeAgnetRef(taskID) + "-" + common.GetUUID()[:8], - }, - } - if group != "" { - plan.BillingContext = agnetBillingContext{Provider: "newapi", NewAPIGroup: group} - } - if !validateOrchestrationPlan(c, plan) { - return - } - - common.ApiSuccess(c, gin.H{ - "task_id": taskID, - "orchestration_plan": plan, - }) -} diff --git a/heicode/controller/heicode_agnet_session.go b/heicode/controller/heicode_agent_session.go similarity index 85% rename from heicode/controller/heicode_agnet_session.go rename to heicode/controller/heicode_agent_session.go index 979a3f3..e6046ca 100644 --- a/heicode/controller/heicode_agnet_session.go +++ b/heicode/controller/heicode_agent_session.go @@ -20,14 +20,14 @@ import ( "gorm.io/gorm" ) -// HeicodeAgnetSessionRequest accepts tokens obtained only from Agnet identity platform. +// HeicodeAgentSessionRequest accepts tokens obtained only from Agent identity platform. // Manager verifies them server-side and issues the browser session cookie (same as password login). -type HeicodeAgnetSessionRequest struct { +type HeicodeAgentSessionRequest struct { AccessToken string `json:"access_token"` RefreshToken string `json:"refresh_token"` } -type agnetMeEnvelope struct { +type agentMeEnvelope struct { Success bool `json:"success"` Message string `json:"message"` Detail string `json:"detail"` @@ -41,7 +41,7 @@ type agnetMeEnvelope struct { } `json:"data"` } -type agnetRefreshEnvelope struct { +type agentRefreshEnvelope struct { Success bool `json:"success"` Detail string `json:"detail"` Message string `json:"message"` @@ -80,10 +80,10 @@ func parseEmailList(raw string) map[string]struct{} { return out } -// roleFromAgnetWithEmail decides the local role for a JIT-synced Agnet user. +// roleFromAgentWithEmail decides the local role for a JIT-synced Agent user. // // 安全策略:管理员权限只能通过本地配置(环境变量白名单)显式授予, -// **不信任** Agnet 平台返回的 role 字段。这样防止外部身份平台 +// **不信任** Agent 平台返回的 role 字段。这样防止外部身份平台 // 的角色被直接映射到 Manager 的高权限角色。 // // - 邮箱命中 HEICODE_ROOT_EMAILS -> RoleRootUser @@ -92,7 +92,7 @@ func parseEmailList(raw string) map[string]struct{} { // // 第二参数 `role` 当前未使用,保留是为了未来扩展(例如在策略中允许 // 信任部分上游 role),不破坏调用点签名。 -func roleFromAgnetWithEmail(_ string, email string) int { +func roleFromAgentWithEmail(_ string, email string) int { emailKey := strings.ToLower(strings.TrimSpace(email)) rootEmails := parseEmailList(os.Getenv("HEICODE_ROOT_EMAILS")) if _, ok := rootEmails[emailKey]; ok { @@ -105,26 +105,26 @@ func roleFromAgnetWithEmail(_ string, email string) int { return common.RoleCommonUser } -func statusFromAgnet(status string) int { +func statusFromAgent(status string) int { if strings.EqualFold(strings.TrimSpace(status), "active") { return common.UserStatusEnabled } return common.UserStatusDisabled } -func agnetHTTPClient() *http.Client { +func agentHTTPClient() *http.Client { return &http.Client{Timeout: 15 * time.Second} } -func fetchAgnetMe(baseURL, accessToken string) (agnetMeEnvelope, int, error) { - var out agnetMeEnvelope +func fetchAgentMe(baseURL, accessToken string) (agentMeEnvelope, int, error) { + var out agentMeEnvelope req, err := http.NewRequest(http.MethodGet, baseURL+"/api/auth/me", nil) if err != nil { return out, 0, err } req.Header.Set("Authorization", "Bearer "+accessToken) req.Header.Set("X-Request-Id", common.GetUUID()) - res, err := agnetHTTPClient().Do(req) + res, err := agentHTTPClient().Do(req) if err != nil { return out, 0, err } @@ -134,12 +134,12 @@ func fetchAgnetMe(baseURL, accessToken string) (agnetMeEnvelope, int, error) { return out, res.StatusCode, err } if err := common.Unmarshal(body, &out); err != nil { - return out, res.StatusCode, fmt.Errorf("invalid response from Agnet /me: %w", err) + return out, res.StatusCode, fmt.Errorf("invalid response from Agent /me: %w", err) } return out, res.StatusCode, nil } -func fetchAgnetRefresh(baseURL, refreshToken string) (access string, refresh string, err error) { +func fetchAgentRefresh(baseURL, refreshToken string) (access string, refresh string, err error) { req, err := http.NewRequest(http.MethodPost, baseURL+"/api/auth/refresh", nil) if err != nil { return "", "", err @@ -147,7 +147,7 @@ func fetchAgnetRefresh(baseURL, refreshToken string) (access string, refresh str req.Header.Set("Authorization", "Bearer "+refreshToken) req.Header.Set("Content-Type", "application/json") req.Header.Set("X-Request-Id", common.GetUUID()) - res, err := agnetHTTPClient().Do(req) + res, err := agentHTTPClient().Do(req) if err != nil { return "", "", err } @@ -156,9 +156,9 @@ func fetchAgnetRefresh(baseURL, refreshToken string) (access string, refresh str if err != nil { return "", "", err } - var env agnetRefreshEnvelope + var env agentRefreshEnvelope if err := common.Unmarshal(body, &env); err != nil { - return "", "", fmt.Errorf("invalid response from Agnet /refresh: %w", err) + return "", "", fmt.Errorf("invalid response from Agent /refresh: %w", err) } if !env.Success || env.Data.Token == "" { msg := env.Message @@ -212,7 +212,7 @@ func markBillingProviderNewapi(email string) { req.Header.Set("Authorization", "Bearer "+tok) req.Header.Set("Content-Type", "application/json") req.Header.Set("X-Request-Id", common.GetUUID()) - res, err := agnetHTTPClient().Do(req) + res, err := agentHTTPClient().Do(req) if err != nil { common.SysLog("markBillingProviderNewapi: send failed: " + err.Error()) return @@ -228,10 +228,10 @@ func markBillingProviderNewapi(email string) { }(email, tok) } -func syncLocalUserFromAgnet(me agnetMeEnvelope) (*model.User, error) { +func syncLocalUserFromAgent(me agentMeEnvelope) (*model.User, error) { email := strings.TrimSpace(me.Data.Email) if email == "" { - return nil, errors.New("Agnet account has no email") + return nil, errors.New("Agent account has no email") } var user model.User @@ -241,7 +241,7 @@ func syncLocalUserFromAgnet(me agnetMeEnvelope) (*model.User, error) { if display == "" { display = strings.Split(email, "@")[0] } - // Local model-access bucket — never seed with the Agnet channelId + // Local model-access bucket — never seed with the Agent channelId // (no abilities row matches a random UUID, so the new user would land // with zero models on first /v1/models call). Admins control group // from the NewAPI dashboard after JIT-create. Fix companion to @@ -253,12 +253,12 @@ func syncLocalUserFromAgnet(me agnetMeEnvelope) (*model.User, error) { Password: common.GetRandomString(32), DisplayName: display, Email: email, - Role: roleFromAgnetWithEmail(me.Data.Role, email), - Status: statusFromAgnet(me.Data.Status), + Role: roleFromAgentWithEmail(me.Data.Role, email), + Status: statusFromAgent(me.Data.Status), Group: group, } if nu.Status != common.UserStatusEnabled { - return nil, errors.New("Agnet account is not active") + return nil, errors.New("Agent account is not active") } if err := nu.Insert(0); err != nil { // Possible race: duplicate email/username — reload. @@ -283,10 +283,10 @@ func syncLocalUserFromAgnet(me agnetMeEnvelope) (*model.User, error) { user.DisplayName = name changed = true } - // Don't overwrite the existing user's group with the Agnet channelId on + // Don't overwrite the existing user's group with the Agent channelId on // every login: NewAPI's `users.group` is the **local model-access bucket** // (must match a row in the `abilities` / `channels` group column to expose - // any models). The Agnet channelId is a cross-platform identity that + // any models). The Agent channelId is a cross-platform identity that // rarely matches a NewAPI-side group, so overwriting strands the user // with zero models. mcp-server side already tracks channelId separately // (see markBillingProviderNewapi), so we don't need it duplicated here. @@ -295,8 +295,8 @@ func syncLocalUserFromAgnet(me agnetMeEnvelope) (*model.User, error) { // is new and has no admin-set group yet). After that, NewAPI admins own // the group via the dashboard. _ = me.Data.ChannelID - // Promote role from Agnet / email whitelist on every login (never demote). - desiredRole := roleFromAgnetWithEmail(me.Data.Role, email) + // Promote role from Agent / email whitelist on every login (never demote). + desiredRole := roleFromAgentWithEmail(me.Data.Role, email) if desiredRole > user.Role { user.Role = desiredRole changed = true @@ -313,9 +313,9 @@ func syncLocalUserFromAgnet(me agnetMeEnvelope) (*model.User, error) { return &user, nil } -// HeicodeAgnetSessionLogin establishes Manager session after Agnet identity verified via token(s). -func HeicodeAgnetSessionLogin(c *gin.Context) { - var req HeicodeAgnetSessionRequest +// HeicodeAgentSessionLogin establishes Manager session after Agent identity verified via token(s). +func HeicodeAgentSessionLogin(c *gin.Context) { + var req HeicodeAgentSessionRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusOK, gin.H{"success": false, "message": "invalid params"}) return @@ -328,19 +328,19 @@ func HeicodeAgnetSessionLogin(c *gin.Context) { } baseURL := defaultHeicodeAuthBaseURL() - me, status, err := fetchAgnetMe(baseURL, access) + me, status, err := fetchAgentMe(baseURL, access) newAccess := "" newRefresh := "" if (err != nil || status == http.StatusUnauthorized || !me.Success) && refresh != "" { - na, nr, refErr := fetchAgnetRefresh(baseURL, refresh) + na, nr, refErr := fetchAgentRefresh(baseURL, refresh) if refErr != nil { c.JSON(http.StatusOK, gin.H{"success": false, "message": refErr.Error()}) return } newAccess = na newRefresh = nr - me, _, err = fetchAgnetMe(baseURL, newAccess) + me, _, err = fetchAgentMe(baseURL, newAccess) } if err != nil { @@ -353,13 +353,13 @@ func HeicodeAgnetSessionLogin(c *gin.Context) { msg = me.Detail } if msg == "" { - msg = "unable to verify identity with Agnet" + msg = "unable to verify identity with Agent" } c.JSON(http.StatusOK, gin.H{"success": false, "message": msg}) return } - user, err := syncLocalUserFromAgnet(me) + user, err := syncLocalUserFromAgent(me) if err != nil { c.JSON(http.StatusOK, gin.H{"success": false, "message": err.Error()}) return diff --git a/heicode/controller/heicode_capabilities.go b/heicode/controller/heicode_capabilities.go new file mode 100644 index 0000000..5d4e1d9 --- /dev/null +++ b/heicode/controller/heicode_capabilities.go @@ -0,0 +1,50 @@ +package controller + +import ( + "github.com/gin-gonic/gin" + + "github.com/heicode/manager/common" +) + +// HeicodeCapabilities exposes the client-facing mode + model catalog so the +// desktop client can discover which modes (Sub Agile / Swarm) are available and +// how each selects models (unified spec §6: GET /api/heicode/capabilities). +// +// It is intentionally unauthenticated catalog data: it returns no user-specific +// information, only the static mode contract plus whether each runtime is +// currently wired, and the production-aligned default model. +func HeicodeCapabilities(c *gin.Context) { + subCfg := agentRuntimeClientConfigForMode(agentRuntimeModeAgent) + swarmCfg := agentRuntimeClientConfigForMode(agentRuntimeModeSwarm) + defaultModel := defaultAgentModelID() + + common.ApiSuccess(c, gin.H{ + "modes": []gin.H{ + { + "id": "sub_agile", + "name": "Sub Agile", + "runtime_kind": "agent_management", + "model_selection": "per_role", + "supports_roles": true, + "supports_task_graph": false, + "supports_artifacts": true, + "supports_continue_chat": true, + "enabled": subCfg.Enabled, + }, + { + "id": "swarm", + "name": "Swarm", + "runtime_kind": "heicode_swarm", + "model_selection": "primary", + "supports_roles": false, + "supports_task_graph": true, + "supports_artifacts": true, + "supports_continue_chat": true, + "enabled": swarmCfg.Enabled, + }, + }, + "models": []gin.H{ + {"id": defaultModel, "name": defaultModel, "available": true}, + }, + }) +} diff --git a/heicode/controller/heicode_client_routes.go b/heicode/controller/heicode_client_routes.go new file mode 100644 index 0000000..8746936 --- /dev/null +++ b/heicode/controller/heicode_client_routes.go @@ -0,0 +1,59 @@ +package controller + +import ( + "github.com/gin-gonic/gin" + + "github.com/heicode/manager/common" + "github.com/heicode/manager/model" +) + +// HeicodeTaskWorkflow projects a deployment into the client-facing "workflow" +// shape consumed by the desktop right-hand task panel (unified spec §7.9 / +// §10.2). task_id is the deployment_id. Status is the Manager-judged +// display_status, so the client never has to interpret raw runtime state. +func HeicodeTaskWorkflow(c *gin.Context) { + record, ok := requireAuthenticatedUserAgentDeployment(c) + if !ok { + return + } + record = withDisplayStatus(record) + + artifacts, _ := model.ListAgentArtifacts(model.ListAgentArtifactsFilter{ + DeploymentID: record.DeploymentID, + Limit: 500, + }) + artItems := make([]gin.H, 0, len(artifacts)) + for _, a := range artifacts { + artItems = append(artItems, gin.H{ + "artifact_id": a.ArtifactID, + "title": a.Title, + "artifact_type": a.ArtifactType, + "summary": a.Summary, + }) + } + + agents := make([]gin.H, 0, len(record.AgentInstances)) + for _, inst := range record.AgentInstances { + agents = append(agents, gin.H{ + "agent_id": inst.InstanceID, + "name": inst.Role, + "role": inst.Role, + "status": firstNonEmpty(inst.RuntimeState, inst.Phase), + }) + } + + common.ApiSuccess(c, gin.H{ + "workflow_id": record.DeploymentID, + "task_id": record.DeploymentID, + "deployment_id": record.DeploymentID, + "mode": firstNonEmpty(record.SubMode, "agile"), + "title": firstNonEmpty(record.Plan.Objective, record.DeploymentID), + "summary": record.Plan.Objective, + "status": record.DisplayStatus, + "display_status": record.DisplayStatus, + "phase": record.Phase, + "agent_count": len(record.AgentInstances), + "agents": agents, + "artifacts": artItems, + }) +} diff --git a/heicode/controller/resource.go b/heicode/controller/resource.go index 8735674..a6163a0 100644 --- a/heicode/controller/resource.go +++ b/heicode/controller/resource.go @@ -94,7 +94,7 @@ type resourceGrantPayload struct { BindingScope string `json:"binding_scope"` ResourceId int `json:"resource_id"` Role string `json:"role"` - AgnetId string `json:"agnet_id"` + AgentId string `json:"agent_id"` PermissionScope map[string]any `json:"permission_scope"` Constraints map[string]any `json:"constraints"` Status string `json:"status"` @@ -108,7 +108,7 @@ type resourceGrantResponse struct { BindingScope string `json:"binding_scope"` ResourceId int `json:"resource_id"` Role string `json:"role"` - AgnetId string `json:"agnet_id"` + AgentId string `json:"agent_id"` PermissionScope map[string]any `json:"permission_scope"` Constraints map[string]any `json:"constraints"` Status string `json:"status"` @@ -190,7 +190,7 @@ func normalizeResourceGrantPayload(p resourceGrantPayload) (resourceGrantPayload p.ProjectId = strings.TrimSpace(p.ProjectId) p.BindingScope = strings.TrimSpace(p.BindingScope) p.Role = strings.TrimSpace(p.Role) - p.AgnetId = strings.TrimSpace(p.AgnetId) + p.AgentId = strings.TrimSpace(p.AgentId) p.Status = strings.ToLower(strings.TrimSpace(p.Status)) if p.ResourceId <= 0 { @@ -199,8 +199,8 @@ func normalizeResourceGrantPayload(p resourceGrantPayload) (resourceGrantPayload if p.Role == "" { return p, errors.New("role required") } - if p.AgnetId == "" { - return p, errors.New("agnet_id required") + if p.AgentId == "" { + return p, errors.New("agent_id required") } if p.Status == "" { p.Status = "active" @@ -375,7 +375,7 @@ func resourceGrantToResponse(grant model.ResourceGrant, resource *model.Resource BindingScope: grant.BindingScope, ResourceId: grant.ResourceId, Role: grant.Role, - AgnetId: grant.AgnetId, + AgentId: grant.AgentId, PermissionScope: unmarshalResourceJSON(grant.PermissionScope), Constraints: unmarshalResourceJSON(grant.Constraints), Status: grant.Status, @@ -719,8 +719,8 @@ func ListResourceGrants(c *gin.Context) { if bindingScope := strings.TrimSpace(c.Query("binding_scope")); bindingScope != "" { query = query.Where("binding_scope = ?", bindingScope) } - if agnetId := strings.TrimSpace(c.Query("agnet_id")); agnetId != "" { - query = query.Where("agnet_id = ?", agnetId) + if agentId := strings.TrimSpace(c.Query("agent_id")); agentId != "" { + query = query.Where("agent_id = ?", agentId) } if resourceId := strings.TrimSpace(c.Query("resource_id")); resourceId != "" { query = query.Where("resource_id = ?", resourceId) @@ -743,7 +743,7 @@ func ListResourceGrants(c *gin.Context) { func GenerateResourceGrantManifest(c *gin.Context) { userId := c.GetInt("id") bindingScope := strings.TrimSpace(c.Query("binding_scope")) - agentId := strings.TrimSpace(c.Query("agnet_id")) + agentId := strings.TrimSpace(c.Query("agent_id")) role := strings.TrimSpace(c.Query("role")) query := model.DB.Where("user_id = ? AND status = ?", userId, "active") @@ -751,7 +751,7 @@ func GenerateResourceGrantManifest(c *gin.Context) { query = query.Where("binding_scope = ?", bindingScope) } if agentId != "" { - query = query.Where("agnet_id = ?", agentId) + query = query.Where("agent_id = ?", agentId) } if role != "" { query = query.Where("role = ?", role) @@ -815,7 +815,7 @@ func CreateResourceGrant(c *gin.Context) { BindingScope: payload.BindingScope, ResourceId: payload.ResourceId, Role: payload.Role, - AgnetId: payload.AgnetId, + AgentId: payload.AgentId, PermissionScope: permissionScope, Constraints: constraints, Status: payload.Status, @@ -864,7 +864,7 @@ func UpdateResourceGrant(c *gin.Context) { grant.BindingScope = payload.BindingScope grant.ResourceId = payload.ResourceId grant.Role = payload.Role - grant.AgnetId = payload.AgnetId + grant.AgentId = payload.AgentId grant.PermissionScope = permissionScope grant.Constraints = constraints grant.Status = payload.Status diff --git a/heicode/controller/resource_test.go b/heicode/controller/resource_test.go index 597f087..c4c72e1 100644 --- a/heicode/controller/resource_test.go +++ b/heicode/controller/resource_test.go @@ -120,7 +120,7 @@ func TestDeleteResourceRevokesBindingAndActiveGrants(t *testing.T) { BindingScope: "project-alpha", ResourceId: resource.Id, Role: "backend", - AgnetId: "agent-backend-1", + AgentId: "agent-backend-1", PermissionScope: `{"actions":["repo:read"]}`, Status: "active", } @@ -159,7 +159,7 @@ func TestDeleteResourceRevokesBindingAndActiveGrants(t *testing.T) { require.Contains(t, listActive.Body.String(), `"items":[]`) } -func TestCreateResourceGrantAssignsBoundResourceToRoleAgnet(t *testing.T) { +func TestCreateResourceGrantAssignsBoundResourceToRoleAgent(t *testing.T) { db := setupResourceControllerTestDB(t) resource := model.ResourceBinding{ UserId: 7, @@ -177,7 +177,7 @@ func TestCreateResourceGrantAssignsBoundResourceToRoleAgnet(t *testing.T) { "binding_scope":"https://example.com/sk.git#main", "resource_id":%d, "role":"developer", - "agnet_id":"agnet-dev-1", + "agent_id":"agent-dev-1", "permission_scope":{"actions":["read"]}, "constraints":{"paths":["skills/**"]} }`, resource.Id) @@ -187,14 +187,14 @@ func TestCreateResourceGrantAssignsBoundResourceToRoleAgnet(t *testing.T) { require.Contains(t, w.Body.String(), `"success":true`) require.Contains(t, w.Body.String(), `"binding_scope":"https://example.com/sk.git#main"`) require.Contains(t, w.Body.String(), `"role":"developer"`) - require.Contains(t, w.Body.String(), `"agnet_id":"agnet-dev-1"`) + require.Contains(t, w.Body.String(), `"agent_id":"agent-dev-1"`) var grant model.ResourceGrant require.NoError(t, db.First(&grant).Error) require.Equal(t, resource.Id, grant.ResourceId) require.Equal(t, "https://example.com/sk.git#main", grant.BindingScope) require.Equal(t, "developer", grant.Role) - require.Equal(t, "agnet-dev-1", grant.AgnetId) + require.Equal(t, "agent-dev-1", grant.AgentId) } func TestCreateResourceGrantRejectsMismatchedBindingScope(t *testing.T) { @@ -212,7 +212,7 @@ func TestCreateResourceGrantRejectsMismatchedBindingScope(t *testing.T) { "binding_scope":"azure-vm-dev", "resource_id":%d, "role":"operator", - "agnet_id":"agnet-ops-1" + "agent_id":"agent-ops-1" }`, resource.Id) w := performResourceRequest(CreateResourceGrant, 7, http.MethodPost, "/", body) @@ -241,7 +241,7 @@ func TestGenerateResourceGrantManifestIncludesActiveGrantsOnly(t *testing.T) { BindingScope: "repo-main", ResourceId: resource.Id, Role: "backend", - AgnetId: "agnet-backend-1", + AgentId: "agent-backend-1", PermissionScope: `{"actions":["read"]}`, Constraints: `{"paths":["heicode/controller/**"]}`, Status: "active", @@ -251,7 +251,7 @@ func TestGenerateResourceGrantManifestIncludesActiveGrantsOnly(t *testing.T) { BindingScope: "repo-main", ResourceId: resource.Id, Role: "backend", - AgnetId: "agnet-backend-1", + AgentId: "agent-backend-1", PermissionScope: `{"actions":["write"]}`, Status: "revoked", }).Error) @@ -261,7 +261,7 @@ func TestGenerateResourceGrantManifestIncludesActiveGrantsOnly(t *testing.T) { 7, http.MethodGet, "/manifest", - "/manifest?binding_scope=repo-main&role=backend&agnet_id=agnet-backend-1", + "/manifest?binding_scope=repo-main&role=backend&agent_id=agent-backend-1", "", ) @@ -269,7 +269,7 @@ func TestGenerateResourceGrantManifestIncludesActiveGrantsOnly(t *testing.T) { require.Contains(t, w.Body.String(), `"success":true`) require.Contains(t, w.Body.String(), `"binding_scope":"repo-main"`) require.Contains(t, w.Body.String(), `"agent_role":"backend"`) - require.Contains(t, w.Body.String(), `"target_agent_ref":"agnet-backend-1"`) + require.Contains(t, w.Body.String(), `"target_agent_ref":"agent-backend-1"`) require.Contains(t, w.Body.String(), `"resource_type":"git"`) require.Contains(t, w.Body.String(), `"allowed_actions":["read"]`) require.Contains(t, w.Body.String(), `"secret_ref":"azkv://heicode-kv.vault.azure.net/secrets/repo-main"`) @@ -293,7 +293,7 @@ func TestDeleteResourceGrantRevokesInsteadOfDeleting(t *testing.T) { BindingScope: "project-alpha", ResourceId: resource.Id, Role: "backend", - AgnetId: "agent-backend-1", + AgentId: "agent-backend-1", PermissionScope: `{"actions":["repo:read","repo:write"]}`, Constraints: `{"paths":["src/**"]}`, Status: "active", diff --git a/heicode/docker-compose.azure-vm.yml b/heicode/docker-compose.azure-vm.yml index 00c396a..b87bc57 100644 --- a/heicode/docker-compose.azure-vm.yml +++ b/heicode/docker-compose.azure-vm.yml @@ -45,7 +45,7 @@ services: - NODE_NAME=heicode-node-1 # 默认与 docs/integration/Heicode-登录接口对接文档.md §2.1 一致;覆盖仅用于非标准网关。 - HEICODE_AUTH_BASE_URL=${HEICODE_AUTH_BASE_URL:-https://apimtaiji.azure-api.net/api/mcp} - # Agnet 登录后 JIT 同步:邮箱命中以下白名单则自动提权 + # Agent 登录后 JIT 同步:邮箱命中以下白名单则自动提权 - HEICODE_ROOT_EMAILS=${HEICODE_ROOT_EMAILS:-} - HEICODE_ADMIN_EMAILS=${HEICODE_ADMIN_EMAILS:-} # Long-lived resource credentials are written to Azure Key Vault via @@ -54,18 +54,18 @@ services: - AZURE_CLIENT_ID=${AZURE_CLIENT_ID:-} # Agent Manager Runtime: use the current production IP directly. # The public domain is not used until DNS/HTTPS is fixed. - - AGNET_RUNTIME_ENABLED=${AGNET_RUNTIME_ENABLED:-false} - - AGNET_RUNTIME_BASE_URL=${AGNET_RUNTIME_BASE_URL:-http://20.212.121.126} - - AGNET_RUNTIME_CREATE_PATH=${AGNET_RUNTIME_CREATE_PATH:-/api/agnet/deployments} - - AGNET_RUNTIME_HEALTH_PATH=${AGNET_RUNTIME_HEALTH_PATH:-/api/agnet/health} - - AGNET_RUNTIME_STOP_PATH=${AGNET_RUNTIME_STOP_PATH:-/api/agnet/deployments/{deployment_id}/stop} - - AGNET_RUNTIME_SERVICE_TOKEN=${AGNET_RUNTIME_SERVICE_TOKEN:-} - - AGNET_RUNTIME_CALLBACK_SIGNING_SECRET_REF=${AGNET_RUNTIME_CALLBACK_SIGNING_SECRET_REF:-} - # HeiCode-Swarm Runtime is separate from ordinary sub Agnet Runtime. + - AGENT_RUNTIME_ENABLED=${AGENT_RUNTIME_ENABLED:-false} + - AGENT_RUNTIME_BASE_URL=${AGENT_RUNTIME_BASE_URL:-http://20.212.121.126} + - AGENT_RUNTIME_CREATE_PATH=${AGENT_RUNTIME_CREATE_PATH:-/api/agent/deployments} + - AGENT_RUNTIME_HEALTH_PATH=${AGENT_RUNTIME_HEALTH_PATH:-/api/agent/health} + - AGENT_RUNTIME_STOP_PATH=${AGENT_RUNTIME_STOP_PATH:-/api/agent/deployments/{deployment_id}/stop} + - AGENT_RUNTIME_SERVICE_TOKEN=${AGENT_RUNTIME_SERVICE_TOKEN:-} + - AGENT_RUNTIME_CALLBACK_SIGNING_SECRET_REF=${AGENT_RUNTIME_CALLBACK_SIGNING_SECRET_REF:-} + # HeiCode-Swarm Runtime is separate from ordinary sub Agent Runtime. - SWARM_RUNTIME_ENABLED=${SWARM_RUNTIME_ENABLED:-false} - SWARM_RUNTIME_BASE_URL=${SWARM_RUNTIME_BASE_URL:-} - SWARM_RUNTIME_CREATE_PATH=${SWARM_RUNTIME_CREATE_PATH:-/api/swarms} - - SWARM_RUNTIME_HEALTH_PATH=${SWARM_RUNTIME_HEALTH_PATH:-/api/agnet/health} + - SWARM_RUNTIME_HEALTH_PATH=${SWARM_RUNTIME_HEALTH_PATH:-/api/agent/health} - SWARM_RUNTIME_STOP_PATH=${SWARM_RUNTIME_STOP_PATH:-/api/swarms/{swarm_id}/stop} - SWARM_RUNTIME_APPROVAL_DECISION_PATH=${SWARM_RUNTIME_APPROVAL_DECISION_PATH:-/api/swarms/{swarm_id}/approvals/{approval_id}} - SWARM_RUNTIME_SERVICE_TOKEN=${SWARM_RUNTIME_SERVICE_TOKEN:-} diff --git a/heicode/docs/deploy/new-api-rebuild-deploy-runbook.md b/heicode/docs/deploy/new-api-rebuild-deploy-runbook.md index 73aa3ee..a6a00e2 100644 --- a/heicode/docs/deploy/new-api-rebuild-deploy-runbook.md +++ b/heicode/docs/deploy/new-api-rebuild-deploy-runbook.md @@ -12,7 +12,7 @@ Security rule: never put real passwords, tokens, SSH keys, Redis keys, PostgreSQ | `heicode/docker-compose.azure-vm.yml` | Azure VM Manager service | Runs only `heicode`; PostgreSQL and Redis are expected to be managed Azure services. | | `heicode/docker-compose.override.yml` | Local-source image override | Builds `heicode-manager:local` from the checked-out repo. Keep it in the compose file list when deploying this repo state. | | `heicode/bin/azure_vm_deploy.sh` | SSH deployment helper | Uses env vars only; can fast-forward a remote branch, performs remote compose up, health gate, and rollback pointer capture. | -| `heicode/bin/acceptance_agnet_local.sh` | Local Agnet control-plane smoke/acceptance probe | Requires an admin session cookie supplied via env; does not store credentials. | +| `heicode/bin/acceptance_agent_local.sh` | Local Agent control-plane smoke/acceptance probe | Requires an admin session cookie supplied via env; does not store credentials. | | `heicode/.env.example` | Env-var reference | Placeholder-only reference; production `.env` must stay on the VM and out of Git. | ## 2. Required VM inputs @@ -114,9 +114,9 @@ The script will: 6. Poll `/api/status` through the VM-local health URL. 7. Update `.last_success_image` only after the health gate passes. -## 5.1 Agnet operator handoff +## 5.1 Agent operator handoff -When Agnet is the executor, Manager should create a high-risk `newapi-rebuild-deploy` deployment using `docs/integration/agnet-platform-request-contract.md` and pass only references: +When Agent is the executor, Manager should create a high-risk `newapi-rebuild-deploy` deployment using `docs/integration/agent-platform-request-contract.md` and pass only references: | Field | Required reference | |---|---| @@ -126,7 +126,7 @@ When Agnet is the executor, Manager should create a high-risk `newapi-rebuild-de | `orchestration_plan.constraints.healthcheck_url_ref` | `env://NEWAPI_HEALTHCHECK_URL`, expected to resolve to the VM-local `/api/status` probe. | | `orchestration_plan.metadata.commit` | Intended Git commit or branch to deploy, such as `origin/main` after push. | -Minimum evidence Agnet must return before Manager marks the operation deployed: +Minimum evidence Agent must return before Manager marks the operation deployed: 1. Remote commit after fetch/pull. 2. `docker compose ... ps` status for `heicode`. @@ -149,14 +149,14 @@ curl -fsS http://127.0.0.1:3000/api/status Expected health response includes `"success":true`. -Optional Manager / Agnet smoke probe after obtaining a safe admin session cookie without logging it: +Optional Manager / Agent smoke probe after obtaining a safe admin session cookie without logging it: ```bash AUTH_COOKIE='REDACTED_SESSION_COOKIE' \ BASE_URL='http://127.0.0.1:3000' \ TENANT_ID='tenant_smoke' \ PROJECT_ID='project_smoke' \ -./bin/acceptance_agnet_local.sh +./bin/acceptance_agent_local.sh ``` Do not commit or report the real cookie. diff --git a/heicode/middleware/auth_v2_user_test.go b/heicode/middleware/auth_v2_user_test.go index 8fa5733..5fcbe01 100644 --- a/heicode/middleware/auth_v2_user_test.go +++ b/heicode/middleware/auth_v2_user_test.go @@ -29,13 +29,13 @@ import ( func TestUserOrV2DeviceAuthRejectsMalformedEncryptedRequest(t *testing.T) { gin.SetMode(gin.TestMode) router := gin.New() - router.POST("/api/agnet/user/tasks/:task_id/deployment-draft", UserOrV2DeviceAuth(), func(c *gin.Context) { + router.POST("/api/agent/user/tasks/:task_id/deployment-draft", UserOrV2DeviceAuth(), func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"success": true}) }) req := httptest.NewRequest( http.MethodPost, - "/api/agnet/user/tasks/task-v2/deployment-draft", + "/api/agent/user/tasks/task-v2/deployment-draft", nil, ) req.Header.Set("Content-Encoding", V2ContentEncoding) @@ -100,7 +100,7 @@ func TestUserOrV2DeviceAuthDecryptsValidEncryptedRequest(t *testing.T) { } router := gin.New() - router.POST("/api/agnet/user/tasks/:task_id/deployment-draft", UserOrV2DeviceAuth(), func(c *gin.Context) { + router.POST("/api/agent/user/tasks/:task_id/deployment-draft", UserOrV2DeviceAuth(), func(c *gin.Context) { if got := c.GetInt("id"); got != 42 { t.Fatalf("id context = %d, want 42", got) } @@ -116,7 +116,7 @@ func TestUserOrV2DeviceAuthDecryptsValidEncryptedRequest(t *testing.T) { c.JSON(http.StatusOK, gin.H{"success": true}) }) - path := "/api/agnet/user/tasks/task-v2/deployment-draft" + path := "/api/agent/user/tasks/task-v2/deployment-draft" body := []byte(`{"objective":"encrypted sub task"}`) req := newEncryptedV2Request(t, http.MethodPost, path, body, deviceID, fingerprint, privateKey) rec := httptest.NewRecorder() diff --git a/heicode/model/agnet_approval.go b/heicode/model/agent_approval.go similarity index 88% rename from heicode/model/agnet_approval.go rename to heicode/model/agent_approval.go index 0fed09f..7b7cb56 100644 --- a/heicode/model/agnet_approval.go +++ b/heicode/model/agent_approval.go @@ -1,10 +1,10 @@ package model -// AgnetApprovalRequest records a user-visible approval gate for a -// high-risk Agnet operation. It intentionally stores only a Secret +// AgentApprovalRequest records a user-visible approval gate for a +// high-risk Agent operation. It intentionally stores only a Secret // Store reference for credential-backed operations; plaintext secrets // must never be written to this table. -type AgnetApprovalRequest struct { +type AgentApprovalRequest struct { Id int `json:"id" gorm:"primaryKey"` ApprovalID string `json:"approval_id" gorm:"type:varchar(64);uniqueIndex;not null"` UserId int `json:"user_id" gorm:"index;not null"` @@ -31,14 +31,14 @@ type AgnetApprovalRequest struct { UpdatedAt int64 `json:"updated_at" gorm:"autoUpdateTime;column:updated_at"` } -func (AgnetApprovalRequest) TableName() string { - return "agnet_approval_requests" +func (AgentApprovalRequest) TableName() string { + return "agent_approval_requests" } -// AgnetCredentialLease is the Manager-side short-lived credential +// AgentCredentialLease is the Manager-side short-lived credential // handle produced after an approval succeeds. CredentialRef is the // external handle; SecretRef is internal and points at Azure Key Vault. -type AgnetCredentialLease struct { +type AgentCredentialLease struct { Id int `json:"id" gorm:"primaryKey"` LeaseID string `json:"lease_id" gorm:"type:varchar(64);uniqueIndex;not null"` CredentialRef string `json:"credential_ref" gorm:"type:varchar(128);uniqueIndex;not null"` @@ -59,6 +59,6 @@ type AgnetCredentialLease struct { UpdatedAt int64 `json:"updated_at" gorm:"autoUpdateTime;column:updated_at"` } -func (AgnetCredentialLease) TableName() string { - return "agnet_credential_leases" +func (AgentCredentialLease) TableName() string { + return "agent_credential_leases" } diff --git a/heicode/model/agnet_artifact.go b/heicode/model/agent_artifact.go similarity index 82% rename from heicode/model/agnet_artifact.go rename to heicode/model/agent_artifact.go index 2a1fe45..e2b43d0 100644 --- a/heicode/model/agnet_artifact.go +++ b/heicode/model/agent_artifact.go @@ -7,7 +7,7 @@ import ( "gorm.io/gorm" ) -type AgnetArtifact struct { +type AgentArtifact struct { Id int `gorm:"primaryKey" json:"id"` ArtifactID string `gorm:"type:varchar(128);uniqueIndex" json:"artifact_id"` DeploymentID string `gorm:"type:varchar(64);index" json:"deployment_id"` @@ -24,22 +24,22 @@ type AgnetArtifact struct { CreatedAtMs int64 `gorm:"bigint;index" json:"created_at_ms"` } -func (AgnetArtifact) TableName() string { - return "agnet_artifacts" +func (AgentArtifact) TableName() string { + return "agent_artifacts" } -type ListAgnetArtifactsFilter struct { +type ListAgentArtifactsFilter struct { DeploymentID string TaskID string CorrelationID string Limit int } -func UpsertAgnetArtifact(row *AgnetArtifact) error { +func UpsertAgentArtifact(row *AgentArtifact) error { if DB == nil || row == nil { return nil } - var existing AgnetArtifact + var existing AgentArtifact if row.ArtifactID != "" { if err := DB.Where("artifact_id = ?", row.ArtifactID).First(&existing).Error; err == nil { row.Id = existing.Id @@ -49,11 +49,11 @@ func UpsertAgnetArtifact(row *AgnetArtifact) error { return DB.Create(row).Error } -func ListAgnetArtifacts(f ListAgnetArtifactsFilter) ([]AgnetArtifact, error) { +func ListAgentArtifacts(f ListAgentArtifactsFilter) ([]AgentArtifact, error) { if DB == nil { return nil, nil } - q := DB.Model(&AgnetArtifact{}) + q := DB.Model(&AgentArtifact{}) if f.DeploymentID != "" { q = q.Where("deployment_id = ?", f.DeploymentID) } @@ -67,13 +67,13 @@ func ListAgnetArtifacts(f ListAgnetArtifactsFilter) ([]AgnetArtifact, error) { if limit <= 0 || limit > 1000 { limit = 200 } - var items []AgnetArtifact + var items []AgentArtifact err := q.Order("created_at_ms asc, id asc").Limit(limit).Find(&items).Error return items, err } -func GetAgnetArtifactByDeployment(deploymentID string, artifactID string) (AgnetArtifact, bool, error) { - var row AgnetArtifact +func GetAgentArtifactByDeployment(deploymentID string, artifactID string) (AgentArtifact, bool, error) { + var row AgentArtifact if DB == nil { return row, false, nil } diff --git a/heicode/model/agnet_audit.go b/heicode/model/agent_audit.go similarity index 81% rename from heicode/model/agnet_audit.go rename to heicode/model/agent_audit.go index 1268d77..665d9f8 100644 --- a/heicode/model/agnet_audit.go +++ b/heicode/model/agent_audit.go @@ -6,15 +6,15 @@ import ( "github.com/heicode/manager/common" ) -// AgnetAuditEvent is the persistent audit-trail row for the Agnet +// AgentAuditEvent is the persistent audit-trail row for the Agent // control-plane. Each row records one observable transition in the -// Agnet lifecycle — deployment accepted, instance phase changed, SK +// Agent lifecycle — deployment accepted, instance phase changed, SK // snapshot refreshed, etc. — so admins can answer the "who / when / // for which task / against which resource / with what result" set of // questions even after the Manager container restarts. // // Before this table existed the control-plane stashed events in an -// in-process `map[string][]agnetEvent` (controller/agnet_control_plane +// in-process `map[string][]agentEvent` (controller/agent_control_plane // .go:219). Every restart wiped audit history — unacceptable for a // product where the audit page is part of the security story. // @@ -32,7 +32,7 @@ import ( // Cross-DB compatibility (CLAUDE.md Rule 2): GORM AutoMigrate maps the // tags to the correct types on SQLite / MySQL / PostgreSQL. No raw // SQL. No DB-specific column types. -type AgnetAuditEvent struct { +type AgentAuditEvent struct { Id int `gorm:"primaryKey" json:"id"` EventID string `gorm:"type:varchar(64);uniqueIndex" json:"event_id"` Event string `gorm:"type:varchar(64);index" json:"event"` @@ -52,17 +52,17 @@ type AgnetAuditEvent struct { // TableName pins the migration target so we can rename the Go struct // later without breaking the deployed schema. -func (AgnetAuditEvent) TableName() string { - return "agnet_audit_events" +func (AgentAuditEvent) TableName() string { + return "agent_audit_events" } -// InsertAgnetAuditEvent best-effort persists one audit row. Callers +// InsertAgentAuditEvent best-effort persists one audit row. Callers // invoke this in a hot path (right after mutating a deployment), so: // - errors are logged but never returned — the audit write must NOT // fail the user-facing API // - DB is nil-guarded so unit tests / partial-init binaries don't // panic on a missing connection -func InsertAgnetAuditEvent(evt *AgnetAuditEvent) { +func InsertAgentAuditEvent(evt *AgentAuditEvent) { if DB == nil || evt == nil { return } @@ -76,14 +76,14 @@ func InsertAgnetAuditEvent(evt *AgnetAuditEvent) { evt.Result = "ok" } if err := DB.Create(evt).Error; err != nil { - common.SysLog("InsertAgnetAuditEvent: " + err.Error()) + common.SysLog("InsertAgentAuditEvent: " + err.Error()) } } -// ListAgnetAuditEventsFilter narrows the audit query to a slice of +// ListAgentAuditEventsFilter narrows the audit query to a slice of // dashboard relevant rows. Zero-value fields are ignored — callers // pass only the filters they care about. -type ListAgnetAuditEventsFilter struct { +type ListAgentAuditEventsFilter struct { UserID string BindingScope string DeploymentID string @@ -94,14 +94,14 @@ type ListAgnetAuditEventsFilter struct { Offset int } -// ListAgnetAuditEvents pages over audit rows ordered newest-first. -// Used by the /api/agnet/audit-logs endpoint and the future task- +// ListAgentAuditEvents pages over audit rows ordered newest-first. +// Used by the /api/agent/audit-logs endpoint and the future task- // scoped audit drawer. -func ListAgnetAuditEvents(f ListAgnetAuditEventsFilter) ([]AgnetAuditEvent, int64, error) { +func ListAgentAuditEvents(f ListAgentAuditEventsFilter) ([]AgentAuditEvent, int64, error) { if DB == nil { return nil, 0, nil } - q := DB.Model(&AgnetAuditEvent{}) + q := DB.Model(&AgentAuditEvent{}) if f.UserID != "" { q = q.Where("user_id = ?", f.UserID) } @@ -128,19 +128,19 @@ func ListAgnetAuditEvents(f ListAgnetAuditEventsFilter) ([]AgnetAuditEvent, int6 if limit <= 0 || limit > 1000 { limit = 200 } - var items []AgnetAuditEvent + var items []AgentAuditEvent err := q.Order("occurred_at desc, id desc").Limit(limit).Offset(f.Offset).Find(&items).Error return items, total, err } -// ListAgnetAuditEventsByDeployment is the hot path for the deployment +// ListAgentAuditEventsByDeployment is the hot path for the deployment // detail drawer — returns all events for one deployment in chronological // order so the timeline reads top-to-bottom. -func ListAgnetAuditEventsByDeployment(deploymentID string) ([]AgnetAuditEvent, error) { +func ListAgentAuditEventsByDeployment(deploymentID string) ([]AgentAuditEvent, error) { if DB == nil || deploymentID == "" { return nil, nil } - var items []AgnetAuditEvent + var items []AgentAuditEvent err := DB.Where("deployment_id = ?", deploymentID). Order("occurred_at asc, id asc"). Find(&items).Error diff --git a/heicode/model/agnet_audit_test.go b/heicode/model/agent_audit_test.go similarity index 75% rename from heicode/model/agnet_audit_test.go rename to heicode/model/agent_audit_test.go index 7ae6a01..1a25300 100644 --- a/heicode/model/agnet_audit_test.go +++ b/heicode/model/agent_audit_test.go @@ -15,24 +15,24 @@ import ( func setupAuditTest(t *testing.T) { t.Helper() - if err := DB.AutoMigrate(&AgnetAuditEvent{}); err != nil { + if err := DB.AutoMigrate(&AgentAuditEvent{}); err != nil { t.Fatalf("migrate: %v", err) } - if err := DB.Exec("DELETE FROM agnet_audit_events").Error; err != nil { + if err := DB.Exec("DELETE FROM agent_audit_events").Error; err != nil { t.Fatalf("truncate: %v", err) } } -func TestInsertAgnetAuditEvent_PersistsRow(t *testing.T) { +func TestInsertAgentAuditEvent_PersistsRow(t *testing.T) { // Regression for H1 — the previous implementation appended to an // in-process map that was wiped on every container restart. This // test pins the new behaviour: rows survive in DB. setupAuditTest(t) - evt := &AgnetAuditEvent{ + evt := &AgentAuditEvent{ EventID: "evt_test_001", Event: "deployment.accepted", - Actor: "agnet_control_plane", + Actor: "agent_control_plane", Resource: "dep_abc", UserID: "user-42", ChannelID: "channel-1", @@ -41,9 +41,9 @@ func TestInsertAgnetAuditEvent_PersistsRow(t *testing.T) { CorrelationID: "corr-xyz", RequestID: "req-001", } - InsertAgnetAuditEvent(evt) + InsertAgentAuditEvent(evt) - var got AgnetAuditEvent + var got AgentAuditEvent if err := DB.Where("event_id = ?", "evt_test_001").First(&got).Error; err != nil { t.Fatalf("not persisted: %v", err) } @@ -58,24 +58,24 @@ func TestInsertAgnetAuditEvent_PersistsRow(t *testing.T) { } } -func TestInsertAgnetAuditEvent_NilGuards(t *testing.T) { +func TestInsertAgentAuditEvent_NilGuards(t *testing.T) { // Production safety: audit writes run inside hot paths (right // after a deployment mutation). A nil DB or nil event MUST NOT // panic — better to drop the audit row than to fail the user // API call. defer func() { if r := recover(); r != nil { - t.Fatalf("InsertAgnetAuditEvent panicked: %v", r) + t.Fatalf("InsertAgentAuditEvent panicked: %v", r) } }() - InsertAgnetAuditEvent(nil) // nil evt + InsertAgentAuditEvent(nil) // nil evt prev := DB DB = nil - InsertAgnetAuditEvent(&AgnetAuditEvent{EventID: "x"}) // nil DB + InsertAgentAuditEvent(&AgentAuditEvent{EventID: "x"}) // nil DB DB = prev } -func TestListAgnetAuditEvents_FilterAndPaginate(t *testing.T) { +func TestListAgentAuditEvents_FilterAndPaginate(t *testing.T) { setupAuditTest(t) now := time.Now().UnixMilli() @@ -84,7 +84,7 @@ func TestListAgnetAuditEvents_FilterAndPaginate(t *testing.T) { if i%2 == 0 { uid = "user-B" } - InsertAgnetAuditEvent(&AgnetAuditEvent{ + InsertAgentAuditEvent(&AgentAuditEvent{ EventID: fmt.Sprintf("evt_%d", i), Event: "deployment.accepted", UserID: uid, @@ -94,7 +94,7 @@ func TestListAgnetAuditEvents_FilterAndPaginate(t *testing.T) { } // All rows visible without filter. - rows, total, err := ListAgnetAuditEvents(ListAgnetAuditEventsFilter{}) + rows, total, err := ListAgentAuditEvents(ListAgentAuditEventsFilter{}) if err != nil { t.Fatalf("list: %v", err) } @@ -103,7 +103,7 @@ func TestListAgnetAuditEvents_FilterAndPaginate(t *testing.T) { } // user_id filter narrows to 3 (i=0,2,4 → user-B). - _, total, err = ListAgnetAuditEvents(ListAgnetAuditEventsFilter{UserID: "user-B"}) + _, total, err = ListAgentAuditEvents(ListAgentAuditEventsFilter{UserID: "user-B"}) if err != nil { t.Fatalf("list with userid: %v", err) } @@ -112,28 +112,28 @@ func TestListAgnetAuditEvents_FilterAndPaginate(t *testing.T) { } // Newest-first ordering. evt_4 inserted last → top. - rows, _, _ = ListAgnetAuditEvents(ListAgnetAuditEventsFilter{}) + rows, _, _ = ListAgentAuditEvents(ListAgentAuditEventsFilter{}) if rows[0].EventID != "evt_4" { t.Errorf("expected newest-first, got %q on top", rows[0].EventID) } } -func TestListAgnetAuditEventsByDeployment_Chronological(t *testing.T) { +func TestListAgentAuditEventsByDeployment_Chronological(t *testing.T) { // Detail-drawer reads need oldest-first for a top-to-bottom // timeline. Confirms ascending order independent of insertion // order. setupAuditTest(t) - InsertAgnetAuditEvent(&AgnetAuditEvent{ + InsertAgentAuditEvent(&AgentAuditEvent{ EventID: "evt_late", Event: "x", DeploymentID: "dep_T", OccurredAt: 9000, }) - InsertAgnetAuditEvent(&AgnetAuditEvent{ + InsertAgentAuditEvent(&AgentAuditEvent{ EventID: "evt_early", Event: "x", DeploymentID: "dep_T", OccurredAt: 1000, }) - rows, err := ListAgnetAuditEventsByDeployment("dep_T") + rows, err := ListAgentAuditEventsByDeployment("dep_T") if err != nil { t.Fatalf("query: %v", err) } diff --git a/heicode/model/agnet_callback.go b/heicode/model/agent_callback.go similarity index 84% rename from heicode/model/agnet_callback.go rename to heicode/model/agent_callback.go index 27ec8bf..3e3c279 100644 --- a/heicode/model/agnet_callback.go +++ b/heicode/model/agent_callback.go @@ -2,7 +2,7 @@ package model import "errors" -type AgnetCallbackEvent struct { +type AgentCallbackEvent struct { Id int `gorm:"primaryKey" json:"id"` EventID string `gorm:"type:varchar(128);uniqueIndex" json:"event_id"` IdempotencyKey string `gorm:"type:varchar(128);index" json:"idempotency_key"` @@ -22,25 +22,25 @@ type AgnetCallbackEvent struct { CreatedAtMs int64 `gorm:"bigint;index" json:"created_at_ms"` } -func (AgnetCallbackEvent) TableName() string { - return "agnet_callback_events" +func (AgentCallbackEvent) TableName() string { + return "agent_callback_events" } -type ListAgnetCallbackEventsFilter struct { +type ListAgentCallbackEventsFilter struct { DeploymentID string TaskID string CorrelationID string Limit int } -func InsertAgnetCallbackEvent(row *AgnetCallbackEvent) (bool, error) { +func InsertAgentCallbackEvent(row *AgentCallbackEvent) (bool, error) { if DB == nil || row == nil { return false, nil } if row.EventID == "" { return false, errors.New("event_id is required") } - var existing AgnetCallbackEvent + var existing AgentCallbackEvent if err := DB.Where("event_id = ?", row.EventID).First(&existing).Error; err == nil { return false, nil } @@ -55,11 +55,11 @@ func InsertAgnetCallbackEvent(row *AgnetCallbackEvent) (bool, error) { return true, nil } -func ListAgnetCallbackEvents(f ListAgnetCallbackEventsFilter) ([]AgnetCallbackEvent, error) { +func ListAgentCallbackEvents(f ListAgentCallbackEventsFilter) ([]AgentCallbackEvent, error) { if DB == nil { return nil, nil } - q := DB.Model(&AgnetCallbackEvent{}) + q := DB.Model(&AgentCallbackEvent{}) if f.DeploymentID != "" { q = q.Where("deployment_id = ?", f.DeploymentID) } @@ -73,7 +73,7 @@ func ListAgnetCallbackEvents(f ListAgnetCallbackEventsFilter) ([]AgnetCallbackEv if limit <= 0 || limit > 1000 { limit = 200 } - var items []AgnetCallbackEvent + var items []AgentCallbackEvent err := q.Order("created_at_ms asc, id asc").Limit(limit).Find(&items).Error return items, err } diff --git a/heicode/model/agnet_deployment.go b/heicode/model/agent_deployment.go similarity index 89% rename from heicode/model/agnet_deployment.go rename to heicode/model/agent_deployment.go index 1615b24..496a657 100644 --- a/heicode/model/agnet_deployment.go +++ b/heicode/model/agent_deployment.go @@ -1,10 +1,10 @@ package model -// AgnetDeployment stores the Manager-side deployment placeholder. -// It is intentionally a control-plane snapshot: the real Agnet platform +// AgentDeployment stores the Manager-side deployment placeholder. +// It is intentionally a control-plane snapshot: the real Agent platform // execution state can attach later, but Manager must not lose the accepted // plan, manifest, status, or audit context across container restarts. -type AgnetDeployment struct { +type AgentDeployment struct { Id int `gorm:"primaryKey" json:"id"` DeploymentID string `gorm:"type:varchar(64);uniqueIndex" json:"deployment_id"` UserID string `gorm:"type:varchar(64);index" json:"user_id"` @@ -29,6 +29,6 @@ type AgnetDeployment struct { PayloadJSON string `gorm:"type:text" json:"payload_json"` } -func (AgnetDeployment) TableName() string { - return "agnet_deployments" +func (AgentDeployment) TableName() string { + return "agent_deployments" } diff --git a/heicode/model/agnet_sk_snapshot.go b/heicode/model/agent_sk_snapshot.go similarity index 78% rename from heicode/model/agnet_sk_snapshot.go rename to heicode/model/agent_sk_snapshot.go index 7bfcdaa..cd74595 100644 --- a/heicode/model/agnet_sk_snapshot.go +++ b/heicode/model/agent_sk_snapshot.go @@ -1,6 +1,6 @@ package model -type AgnetSKSnapshot struct { +type AgentSKSnapshot struct { Id int `gorm:"primaryKey" json:"id"` SnapshotID string `gorm:"type:varchar(128);uniqueIndex" json:"snapshot_id"` DeploymentID string `gorm:"type:varchar(64);index" json:"deployment_id"` @@ -12,22 +12,22 @@ type AgnetSKSnapshot struct { ResolvedAtMs int64 `gorm:"bigint;index" json:"resolved_at_ms"` } -func (AgnetSKSnapshot) TableName() string { - return "agnet_sk_snapshots" +func (AgentSKSnapshot) TableName() string { + return "agent_sk_snapshots" } -func InsertAgnetSKSnapshots(items []AgnetSKSnapshot) error { +func InsertAgentSKSnapshots(items []AgentSKSnapshot) error { if DB == nil || len(items) == 0 { return nil } return DB.Create(&items).Error } -func ListAgnetSKSnapshots(deploymentID string) ([]AgnetSKSnapshot, error) { +func ListAgentSKSnapshots(deploymentID string) ([]AgentSKSnapshot, error) { if DB == nil || deploymentID == "" { return nil, nil } - var items []AgnetSKSnapshot + var items []AgentSKSnapshot err := DB.Where("deployment_id = ?", deploymentID). Order("resolved_at_ms asc, id asc"). Find(&items).Error diff --git a/heicode/model/main.go b/heicode/model/main.go index e382c77..37bd76d 100644 --- a/heicode/model/main.go +++ b/heicode/model/main.go @@ -247,6 +247,40 @@ func InitLogDB() (err error) { return err } +// renameAgnetTablesToAgent renames legacy agnet_* tables (and the +// resource_grants.agnet_id column) to their agent_* equivalents, preserving +// existing production data after the agnet->agent terminology unification. +// Idempotent and cross-DB (uses GORM Migrator). Runs before AutoMigrate so the +// renamed tables are reused instead of being recreated empty. +func renameAgnetTablesToAgent() { + m := DB.Migrator() + pairs := [][2]string{ + {"agnet_approval_requests", "agent_approval_requests"}, + {"agnet_credential_leases", "agent_credential_leases"}, + {"agnet_deployments", "agent_deployments"}, + {"agnet_callback_events", "agent_callback_events"}, + {"agnet_artifacts", "agent_artifacts"}, + {"agnet_sk_snapshots", "agent_sk_snapshots"}, + {"agnet_audit_events", "agent_audit_events"}, + } + for _, p := range pairs { + if m.HasTable(p[0]) && !m.HasTable(p[1]) { + if err := m.RenameTable(p[0], p[1]); err != nil { + common.SysLog("renameAgnetTablesToAgent: rename " + p[0] + " -> " + p[1] + ": " + err.Error()) + } else { + common.SysLog("renameAgnetTablesToAgent: renamed " + p[0] + " -> " + p[1]) + } + } + } + if m.HasTable("resource_grants") && m.HasColumn(&ResourceGrant{}, "agnet_id") && !m.HasColumn(&ResourceGrant{}, "agent_id") { + if err := m.RenameColumn(&ResourceGrant{}, "agnet_id", "agent_id"); err != nil { + common.SysLog("renameAgnetTablesToAgent: rename column agnet_id -> agent_id: " + err.Error()) + } else { + common.SysLog("renameAgnetTablesToAgent: renamed column resource_grants.agnet_id -> agent_id") + } + } +} + func migrateDB() error { // Migrate price_amount column from float/double to decimal for existing tables migrateSubscriptionPlanPriceAmount() @@ -254,6 +288,8 @@ func migrateDB() error { if err := migrateTokenModelLimitsToText(); err != nil { return err } + // Preserve data across the agnet->agent rename (idempotent, runs before AutoMigrate). + renameAgnetTablesToAgent() err := DB.AutoMigrate( &Channel{}, @@ -283,19 +319,19 @@ func migrateDB() error { &GitSource{}, &ResourceBinding{}, &ResourceGrant{}, - &AgnetApprovalRequest{}, - &AgnetCredentialLease{}, - &AgnetDeployment{}, - &AgnetCallbackEvent{}, - &AgnetArtifact{}, - &AgnetSKSnapshot{}, + &AgentApprovalRequest{}, + &AgentCredentialLease{}, + &AgentDeployment{}, + &AgentCallbackEvent{}, + &AgentArtifact{}, + &AgentSKSnapshot{}, // V2 device-binding: X25519 keypair the Manager uses for ECDH // body decryption. See model/server_key.go. &ServerKey{}, - // Agnet control-plane audit trail. Replaces the previous - // in-process `agnetEvents map` that was wiped on every container - // restart. See model/agnet_audit.go for the rationale. - &AgnetAuditEvent{}, + // Agent control-plane audit trail. Replaces the previous + // in-process `agentEvents map` that was wiped on every container + // restart. See model/agent_audit.go for the rationale. + &AgentAuditEvent{}, ) if err != nil { return err @@ -355,13 +391,13 @@ func migrateDBFast() error { {&GitSource{}, "GitSource"}, {&ResourceBinding{}, "ResourceBinding"}, {&ResourceGrant{}, "ResourceGrant"}, - {&AgnetApprovalRequest{}, "AgnetApprovalRequest"}, - {&AgnetCredentialLease{}, "AgnetCredentialLease"}, - {&AgnetDeployment{}, "AgnetDeployment"}, - {&AgnetCallbackEvent{}, "AgnetCallbackEvent"}, - {&AgnetArtifact{}, "AgnetArtifact"}, - {&AgnetSKSnapshot{}, "AgnetSKSnapshot"}, - {&AgnetAuditEvent{}, "AgnetAuditEvent"}, + {&AgentApprovalRequest{}, "AgentApprovalRequest"}, + {&AgentCredentialLease{}, "AgentCredentialLease"}, + {&AgentDeployment{}, "AgentDeployment"}, + {&AgentCallbackEvent{}, "AgentCallbackEvent"}, + {&AgentArtifact{}, "AgentArtifact"}, + {&AgentSKSnapshot{}, "AgentSKSnapshot"}, + {&AgentAuditEvent{}, "AgentAuditEvent"}, } // 动态计算migration数量,确保errChan缓冲区足够大 errChan := make(chan error, len(migrations)) diff --git a/heicode/model/resource.go b/heicode/model/resource.go index e8f1d76..160904b 100644 --- a/heicode/model/resource.go +++ b/heicode/model/resource.go @@ -22,7 +22,7 @@ type ResourceBinding struct { UpdatedAt int64 `json:"updated_at" gorm:"autoUpdateTime;column:updated_at"` } -// ResourceGrant assigns a ResourceBinding to a role and child Agnet. +// ResourceGrant assigns a ResourceBinding to a role and child Agent. // It is the auditable Manager expression of "user grants bound resource to role". type ResourceGrant struct { Id int `json:"id"` @@ -32,7 +32,7 @@ type ResourceGrant struct { BindingScope string `json:"binding_scope" gorm:"type:varchar(512);index"` ResourceId int `json:"resource_id" gorm:"index;not null"` Role string `json:"role" gorm:"type:varchar(128);index;not null"` - AgnetId string `json:"agnet_id" gorm:"type:varchar(128);index;not null"` + AgentId string `json:"agent_id" gorm:"type:varchar(128);index;not null"` PermissionScope string `json:"permission_scope" gorm:"type:text"` Constraints string `json:"constraints" gorm:"type:text"` Status string `json:"status" gorm:"type:varchar(32);default:'active';index"` diff --git a/heicode/router/agnet_runtime_http_smoke_test.go b/heicode/router/agent_runtime_http_smoke_test.go similarity index 83% rename from heicode/router/agnet_runtime_http_smoke_test.go rename to heicode/router/agent_runtime_http_smoke_test.go index d470a10..438d872 100644 --- a/heicode/router/agnet_runtime_http_smoke_test.go +++ b/heicode/router/agent_runtime_http_smoke_test.go @@ -21,7 +21,7 @@ import ( "gorm.io/gorm" ) -func setupAgnetRuntimeHTTPSmokeDB(t *testing.T) *gorm.DB { +func setupAgentRuntimeHTTPSmokeDB(t *testing.T) *gorm.DB { t.Helper() common.UsingSQLite = true common.UsingMySQL = false @@ -35,8 +35,8 @@ func setupAgnetRuntimeHTTPSmokeDB(t *testing.T) *gorm.DB { model.LOG_DB = db require.NoError(t, db.AutoMigrate( &model.User{}, - &model.AgnetDeployment{}, - &model.AgnetAuditEvent{}, + &model.AgentDeployment{}, + &model.AgentAuditEvent{}, )) adminToken := "runtime-smoke-admin-token" require.NoError(t, db.Create(&model.User{ @@ -64,7 +64,7 @@ func setupAgnetRuntimeHTTPSmokeDB(t *testing.T) *gorm.DB { return db } -func startAgnetRuntimeManagerSmokeServer(t *testing.T) string { +func startAgentRuntimeManagerSmokeServer(t *testing.T) string { t.Helper() gin.SetMode(gin.TestMode) engine := gin.New() @@ -82,7 +82,7 @@ func startAgnetRuntimeManagerSmokeServer(t *testing.T) string { return "http://" + listener.Addr().String() } -func agnetRuntimeAdminRequest(t *testing.T, method string, url string, body string) *http.Response { +func agentRuntimeAdminRequest(t *testing.T, method string, url string, body string) *http.Response { t.Helper() req, err := http.NewRequest(method, url, bytes.NewBufferString(body)) require.NoError(t, err) @@ -96,7 +96,7 @@ func agnetRuntimeAdminRequest(t *testing.T, method string, url string, body stri return resp } -func readAgnetRuntimeSmokeBody(t *testing.T, resp *http.Response) string { +func readAgentRuntimeSmokeBody(t *testing.T, resp *http.Response) string { t.Helper() defer resp.Body.Close() body, err := io.ReadAll(resp.Body) @@ -104,16 +104,16 @@ func readAgnetRuntimeSmokeBody(t *testing.T, resp *http.Response) string { return string(body) } -func TestAgnetRuntimeRealHTTPHealthAndShadowCreateSmoke(t *testing.T) { - db := setupAgnetRuntimeHTTPSmokeDB(t) +func TestAgentRuntimeRealHTTPHealthAndShadowCreateSmoke(t *testing.T) { + db := setupAgentRuntimeHTTPSmokeDB(t) runtimeCreateCalled := false runtime := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch { - case r.Method == http.MethodGet && r.URL.Path == "/api/agnet/health": + case r.Method == http.MethodGet && r.URL.Path == "/api/agent/health": require.Equal(t, "Bearer runtime-service-token", r.Header.Get("Authorization")) w.Header().Set("Content-Type", "application/json") _, _ = w.Write([]byte(`{"success":true,"data":{"status":"healthy"}}`)) - case r.Method == http.MethodPost && r.URL.Path == "/api/agnet/deployments": + case r.Method == http.MethodPost && r.URL.Path == "/api/agent/sub-agile/deployments": runtimeCreateCalled = true require.Equal(t, "Bearer runtime-service-token", r.Header.Get("Authorization")) require.Equal(t, "corr-http-smoke", r.Header.Get("X-Correlation-ID")) @@ -142,23 +142,23 @@ func TestAgnetRuntimeRealHTTPHealthAndShadowCreateSmoke(t *testing.T) { })) defer runtime.Close() - t.Setenv("AGNET_RUNTIME_ENABLED", "true") - t.Setenv("AGNET_RUNTIME_ASYNC", "false") - t.Setenv("AGNET_RUNTIME_BASE_URL", runtime.URL) - t.Setenv("AGNET_RUNTIME_SERVICE_TOKEN", "runtime-service-token") - t.Setenv("AGNET_RUNTIME_CALLBACK_SIGNING_SECRET_REF", "azkv://heicode-kv.vault.azure.net/secrets/callback-signing") + t.Setenv("AGENT_RUNTIME_ENABLED", "true") + t.Setenv("AGENT_RUNTIME_ASYNC", "false") + t.Setenv("AGENT_RUNTIME_BASE_URL", runtime.URL) + t.Setenv("AGENT_RUNTIME_SERVICE_TOKEN", "runtime-service-token") + t.Setenv("AGENT_RUNTIME_CALLBACK_SIGNING_SECRET_REF", "azkv://heicode-kv.vault.azure.net/secrets/callback-signing") - managerURL := startAgnetRuntimeManagerSmokeServer(t) + managerURL := startAgentRuntimeManagerSmokeServer(t) - healthResp := agnetRuntimeAdminRequest(t, http.MethodGet, managerURL+"/api/agnet/runtime/health", "") - healthBody := readAgnetRuntimeSmokeBody(t, healthResp) + healthResp := agentRuntimeAdminRequest(t, http.MethodGet, managerURL+"/api/agent/runtime/health", "") + healthBody := readAgentRuntimeSmokeBody(t, healthResp) require.Equal(t, http.StatusOK, healthResp.StatusCode) require.Contains(t, healthBody, `"success":true`) require.Contains(t, healthBody, `"status":"healthy"`) - schemaResp, err := http.Get(managerURL + "/api/agnet/callbacks/swarm-events/schema") + schemaResp, err := http.Get(managerURL + "/api/agent/callbacks/runtime-events/schema") require.NoError(t, err) - schemaBody := readAgnetRuntimeSmokeBody(t, schemaResp) + schemaBody := readAgentRuntimeSmokeBody(t, schemaResp) require.Equal(t, http.StatusOK, schemaResp.StatusCode) require.Contains(t, schemaBody, `"event_type":"task.claimed"`) require.Contains(t, schemaBody, `"event_type":"approval.requested"`) @@ -179,44 +179,44 @@ func TestAgnetRuntimeRealHTTPHealthAndShadowCreateSmoke(t *testing.T) { "user_context":{"user_id":"101","channel_id":"default"}, "billing_context":{"provider":"newapi","newapi_user_ref":"newapi-http-smoke"}, "agile_context":{"iteration":"2026-05-27~2026-05-28","stage":"development","checkpoint":"ready_for_test","acceptance_criteria":["接口返回成功"],"next_action":"submit_test_result","requires_user_approval":false}, - "agent_runtime":{"platform":"agnet","agents":[{"role":"builder","model_ref":"model-http-smoke","instance_count":1}]}, + "agent_runtime":{"platform":"agent","agents":[{"role":"builder","model_ref":"model-http-smoke","instance_count":1}]}, "agents":[{"role_template":"builder","goal":"smoke","default_model_id":"model-http-smoke","resource_grants":[{"grant_id":"grant-http-git","resource_id":"git-http","resource_type":"git","user_id":"101","binding_scope":"https://example.invalid/heicode/smoke.git#main","target_role":"builder","target_agent_ref":"agent-builder-1","permission_scope":["repo:read"],"metadata":{"repo_url":"https://example.invalid/heicode/smoke.git"},"secret_ref":"azkv://heicode-kv.vault.azure.net/secrets/http-smoke-git","status":"active"}]}], "constraints":{"allowed_model_ids":["model-http-smoke"]}, "metadata":{"correlation_id":"corr-http-smoke"} } }` - createResp := agnetRuntimeAdminRequest(t, http.MethodPost, managerURL+"/api/agnet/deployments", createBody) - responseBody := readAgnetRuntimeSmokeBody(t, createResp) + createResp := agentRuntimeAdminRequest(t, http.MethodPost, managerURL+"/api/agent/deployments", createBody) + responseBody := readAgentRuntimeSmokeBody(t, createResp) require.Equal(t, http.StatusOK, createResp.StatusCode) require.Contains(t, responseBody, `"success":true`) require.Contains(t, responseBody, `"runtime_deployment_id":"runtime-http-dep"`) require.Contains(t, responseBody, `"runtime_swarm_id":"runtime-http-swarm"`) require.True(t, runtimeCreateCalled) - var stored model.AgnetDeployment + var stored model.AgentDeployment require.NoError(t, db.Where("runtime_swarm_id = ?", "runtime-http-swarm").First(&stored).Error) require.Equal(t, "accepted", stored.RuntimeState) - logsResp := agnetRuntimeAdminRequest(t, http.MethodGet, managerURL+"/api/agnet/deployments/"+stored.DeploymentID+"/logs", "") - logsBody := readAgnetRuntimeSmokeBody(t, logsResp) + logsResp := agentRuntimeAdminRequest(t, http.MethodGet, managerURL+"/api/agent/deployments/"+stored.DeploymentID+"/logs", "") + logsBody := readAgentRuntimeSmokeBody(t, logsResp) require.Equal(t, http.StatusOK, logsResp.StatusCode) require.Contains(t, logsBody, `"data_source":"manager_control_plane"`) require.Contains(t, logsBody, `"runtime_source":"not_connected"`) - metricsResp := agnetRuntimeAdminRequest(t, http.MethodGet, managerURL+"/api/agnet/deployments/"+stored.DeploymentID+"/metrics", "") - metricsBody := readAgnetRuntimeSmokeBody(t, metricsResp) + metricsResp := agentRuntimeAdminRequest(t, http.MethodGet, managerURL+"/api/agent/deployments/"+stored.DeploymentID+"/metrics", "") + metricsBody := readAgentRuntimeSmokeBody(t, metricsResp) require.Equal(t, http.StatusOK, metricsResp.StatusCode) require.Contains(t, metricsBody, `"data_source":"manager_control_plane"`) require.Contains(t, metricsBody, `"platform_estimated":true`) } func TestSwarmRuntimeHTTPCreateUsesSwarmConfigAndPayload(t *testing.T) { - db := setupAgnetRuntimeHTTPSmokeDB(t) + db := setupAgentRuntimeHTTPSmokeDB(t) swarmCreateCalled := false swarmStopCalled := false swarm := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch { - case r.Method == http.MethodGet && r.URL.Path == "/api/agnet/health": + case r.Method == http.MethodGet && r.URL.Path == "/api/agent/health": require.Equal(t, "Bearer swarm-runtime-token", r.Header.Get("Authorization")) w.Header().Set("Content-Type", "application/json") _, _ = w.Write([]byte(`{"success":true,"data":{"status":"healthy","service":"heicode-swarm-runtime"}}`)) @@ -253,16 +253,16 @@ func TestSwarmRuntimeHTTPCreateUsesSwarmConfigAndPayload(t *testing.T) { })) defer swarm.Close() - t.Setenv("AGNET_RUNTIME_ENABLED", "false") + t.Setenv("AGENT_RUNTIME_ENABLED", "false") t.Setenv("SWARM_RUNTIME_ENABLED", "true") t.Setenv("SWARM_RUNTIME_ASYNC", "false") t.Setenv("SWARM_RUNTIME_BASE_URL", swarm.URL) t.Setenv("SWARM_RUNTIME_SERVICE_TOKEN", "swarm-runtime-token") - managerURL := startAgnetRuntimeManagerSmokeServer(t) + managerURL := startAgentRuntimeManagerSmokeServer(t) - healthResp := agnetRuntimeAdminRequest(t, http.MethodGet, managerURL+"/api/agnet/runtime/health?mode=swarm", "") - healthBody := readAgnetRuntimeSmokeBody(t, healthResp) + healthResp := agentRuntimeAdminRequest(t, http.MethodGet, managerURL+"/api/agent/runtime/health?mode=swarm", "") + healthBody := readAgentRuntimeSmokeBody(t, healthResp) require.Equal(t, http.StatusOK, healthResp.StatusCode) require.Contains(t, healthBody, `"mode":"swarm"`) require.Contains(t, healthBody, `"status":"healthy"`) @@ -279,7 +279,7 @@ func TestSwarmRuntimeHTTPCreateUsesSwarmConfigAndPayload(t *testing.T) { "user_context":{"user_id":"101","channel_id":"default"}, "billing_context":{"provider":"newapi","newapi_user_ref":"newapi-swarm-smoke"}, "agile_context":{"iteration":"2026-05-29","stage":"testing","checkpoint":"runtime_accepted","acceptance_criteria":["Runtime creates a swarm run"],"next_action":"submit_test_result","requires_user_approval":false}, - "agent_runtime":{"platform":"agnet","agents":[{"role":"planner","model_ref":"model-swarm","instance_count":1},{"role":"builder","model_ref":"model-swarm","instance_count":1},{"role":"reviewer","model_ref":"model-swarm","instance_count":1}]}, + "agent_runtime":{"platform":"agent","agents":[{"role":"planner","model_ref":"model-swarm","instance_count":1},{"role":"builder","model_ref":"model-swarm","instance_count":1},{"role":"reviewer","model_ref":"model-swarm","instance_count":1}]}, "agents":[ {"role_template":"planner","goal":"plan the swarm task","default_model_id":"model-swarm","resource_grants":[{"grant_id":"grant-swarm-plan","resource_id":"doc-swarm","resource_type":"project_doc","user_id":"101","binding_scope":"task-swarm-smoke","target_role":"planner","target_agent_ref":"agent-planner-1","permission_scope":["doc:read"],"metadata":{"resource_ref":"task-swarm-smoke"},"status":"active"}]}, {"role_template":"builder","goal":"build the swarm output","default_model_id":"model-swarm","resource_grants":[{"grant_id":"grant-swarm-build","resource_id":"git-swarm","resource_type":"git","user_id":"101","binding_scope":"task-swarm-smoke","target_role":"builder","target_agent_ref":"agent-builder-1","permission_scope":["repo:read"],"metadata":{"repo_url":"https://example.invalid/heicode/swarm.git"},"secret_ref":"azkv://heicode-kv.vault.azure.net/secrets/swarm-git","status":"active"}]}, @@ -289,21 +289,21 @@ func TestSwarmRuntimeHTTPCreateUsesSwarmConfigAndPayload(t *testing.T) { "metadata":{"correlation_id":"corr-swarm-smoke"} } }` - createResp := agnetRuntimeAdminRequest(t, http.MethodPost, managerURL+"/api/swarms", createBody) - responseBody := readAgnetRuntimeSmokeBody(t, createResp) + createResp := agentRuntimeAdminRequest(t, http.MethodPost, managerURL+"/api/swarms", createBody) + responseBody := readAgentRuntimeSmokeBody(t, createResp) require.Equal(t, http.StatusOK, createResp.StatusCode) require.Contains(t, responseBody, `"success":true`) require.Contains(t, responseBody, `"runtime_deployment_id":"swarm-runtime-dep"`) require.Contains(t, responseBody, `"runtime_swarm_id":"swarm-runtime-id"`) require.True(t, swarmCreateCalled) - var stored model.AgnetDeployment + var stored model.AgentDeployment require.NoError(t, db.Where("runtime_swarm_id = ?", "swarm-runtime-id").First(&stored).Error) require.Equal(t, "agile", stored.SubMode) require.Contains(t, stored.PlanJSON, `"runtime_mode":"swarm"`) - stopResp := agnetRuntimeAdminRequest(t, http.MethodPost, managerURL+"/api/agnet/user/deployments/"+stored.DeploymentID+"/stop", `{"reason":"smoke done"}`) - stopBody := readAgnetRuntimeSmokeBody(t, stopResp) + stopResp := agentRuntimeAdminRequest(t, http.MethodPost, managerURL+"/api/agent/user/deployments/"+stored.DeploymentID+"/stop", `{"reason":"smoke done"}`) + stopBody := readAgentRuntimeSmokeBody(t, stopResp) require.Equal(t, http.StatusOK, stopResp.StatusCode) require.Contains(t, stopBody, `"success":true`) require.Contains(t, stopBody, `"runtime_state":"stopped"`) diff --git a/heicode/router/api-router.go b/heicode/router/api-router.go index 2367355..b865f85 100644 --- a/heicode/router/api-router.go +++ b/heicode/router/api-router.go @@ -59,9 +59,11 @@ func SetApiRouter(router *gin.Engine) { apiRouter.POST("/stripe/webhook", controller.StripeWebhook) apiRouter.POST("/creem/webhook", controller.CreemWebhook) apiRouter.POST("/waffo/webhook", controller.WaffoWebhook) - apiRouter.GET("/agnet/callbacks/swarm-events/schema", controller.AgnetGetSwarmEventCallbackSchema) - apiRouter.POST("/agnet/callbacks/swarm-events", controller.AgnetReceiveSwarmEventCallback) - apiRouter.POST("/swarms", middleware.UserOrV2DeviceAuth(), controller.AgnetCreateUserSwarm) + apiRouter.GET("/agent/callbacks/runtime-events/schema", controller.AgentGetRuntimeEventCallbackSchema) + apiRouter.POST("/agent/callbacks/runtime-events", controller.AgentReceiveRuntimeEventCallback) + // Client-facing capability discovery (unified spec §6). Catalog data only. + apiRouter.GET("/heicode/capabilities", controller.HeicodeCapabilities) + apiRouter.POST("/swarms", middleware.UserOrV2DeviceAuth(), controller.AgentCreateUserSwarm) //apiRouter.POST("/waffo-pancake/webhook", controller.WaffoPancakeWebhook) // Universal secure verification routes @@ -81,7 +83,7 @@ func SetApiRouter(router *gin.Engine) { userRoute := apiRouter.Group("/user") { userRoute.POST("/register", middleware.CriticalRateLimit(), middleware.TurnstileCheck(), controller.Register) - userRoute.POST("/session/from-agnet", middleware.CriticalRateLimit(), controller.HeicodeAgnetSessionLogin) + userRoute.POST("/session/from-agent", middleware.CriticalRateLimit(), controller.HeicodeAgentSessionLogin) userRoute.POST("/login", middleware.CriticalRateLimit(), middleware.TurnstileCheck(), controller.Login) userRoute.POST("/login/2fa", middleware.CriticalRateLimit(), controller.Verify2FALogin) userRoute.POST("/passkey/login/begin", middleware.CriticalRateLimit(), controller.PasskeyLoginBegin) @@ -491,50 +493,80 @@ func SetApiRouter(router *gin.Engine) { deploymentsRoute.DELETE("/:id", controller.DeleteDeployment) } - // Agnet user approval gates and short-lived credential leases. - agnetApprovalRoute := apiRouter.Group("/agnet") - agnetApprovalRoute.Use(middleware.UserOrV2DeviceAuth()) + // Agent user approval gates and short-lived credential leases. + agentApprovalRoute := apiRouter.Group("/agent") + agentApprovalRoute.Use(middleware.UserOrV2DeviceAuth()) { - agnetApprovalRoute.GET("/approvals", controller.ListAgnetApprovalRequests) - agnetApprovalRoute.POST("/approvals", controller.CreateAgnetApprovalRequest) - agnetApprovalRoute.GET("/approvals/:approval_id", controller.GetAgnetApprovalRequest) - agnetApprovalRoute.POST("/approvals/:approval_id/approve", controller.ApproveAgnetApprovalRequest) - agnetApprovalRoute.POST("/approvals/:approval_id/reject", controller.RejectAgnetApprovalRequest) - agnetApprovalRoute.GET("/credential-leases", controller.ListAgnetCredentialLeases) - agnetApprovalRoute.POST("/credential-leases/:lease_id/revoke", controller.RevokeAgnetCredentialLease) - agnetApprovalRoute.GET("/role-templates", controller.AgnetListRoleTemplates) - agnetApprovalRoute.GET("/user/deployments", controller.AgnetListUserDeployments) - agnetApprovalRoute.POST("/user/deployments", controller.AgnetCreateUserDeployment) - agnetApprovalRoute.GET("/user/deployments/:deployment_id", controller.AgnetGetUserDeployment) - agnetApprovalRoute.POST("/user/deployments/:deployment_id/stop", controller.AgnetStopUserDeployment) - agnetApprovalRoute.GET("/user/deployments/:deployment_id/logs", controller.AgnetListUserDeploymentLogs) - agnetApprovalRoute.GET("/user/deployments/:deployment_id/metrics", controller.AgnetGetUserDeploymentMetrics) - agnetApprovalRoute.GET("/user/deployments/:deployment_id/runtime-diagnostics", controller.AgnetGetUserDeploymentRuntimeDiagnostics) - agnetApprovalRoute.GET("/user/deployments/:deployment_id/events", controller.AgnetListUserDeploymentEvents) - agnetApprovalRoute.POST("/user/deployments/:deployment_id/simulate-events", controller.AgnetSimulateUserDeploymentEvents) - agnetApprovalRoute.GET("/user/deployments/:deployment_id/artifacts", controller.AgnetListUserDeploymentArtifacts) - agnetApprovalRoute.GET("/user/deployments/:deployment_id/artifacts/:artifact_id/content", controller.AgnetGetUserDeploymentArtifactContent) - agnetApprovalRoute.GET("/user/deployments/:deployment_id/sk-snapshots", controller.AgnetListUserSKSnapshots) - agnetApprovalRoute.GET("/user/deployments/:deployment_id/timeline", controller.AgnetGetUserDeploymentTimeline) - agnetApprovalRoute.POST("/user/tasks/:task_id/deployment-draft", controller.AgnetCreateTaskDeploymentDraft) + agentApprovalRoute.GET("/approvals", controller.ListAgentApprovalRequests) + agentApprovalRoute.POST("/approvals", controller.CreateAgentApprovalRequest) + agentApprovalRoute.GET("/approvals/:approval_id", controller.GetAgentApprovalRequest) + agentApprovalRoute.POST("/approvals/:approval_id/approve", controller.ApproveAgentApprovalRequest) + agentApprovalRoute.POST("/approvals/:approval_id/reject", controller.RejectAgentApprovalRequest) + agentApprovalRoute.GET("/credential-leases", controller.ListAgentCredentialLeases) + agentApprovalRoute.POST("/credential-leases/:lease_id/revoke", controller.RevokeAgentCredentialLease) + agentApprovalRoute.GET("/role-templates", controller.AgentListRoleTemplates) + agentApprovalRoute.GET("/user/deployments", controller.AgentListUserDeployments) + agentApprovalRoute.POST("/user/deployments", controller.AgentCreateUserDeployment) + agentApprovalRoute.GET("/user/deployments/:deployment_id", controller.AgentGetUserDeployment) + agentApprovalRoute.POST("/user/deployments/:deployment_id/stop", controller.AgentStopUserDeployment) + agentApprovalRoute.GET("/user/deployments/:deployment_id/logs", controller.AgentListUserDeploymentLogs) + agentApprovalRoute.GET("/user/deployments/:deployment_id/metrics", controller.AgentGetUserDeploymentMetrics) + agentApprovalRoute.GET("/user/deployments/:deployment_id/runtime-diagnostics", controller.AgentGetUserDeploymentRuntimeDiagnostics) + agentApprovalRoute.GET("/user/deployments/:deployment_id/events", controller.AgentListUserDeploymentEvents) + agentApprovalRoute.POST("/user/deployments/:deployment_id/simulate-events", controller.AgentSimulateUserDeploymentEvents) + agentApprovalRoute.GET("/user/deployments/:deployment_id/artifacts", controller.AgentListUserDeploymentArtifacts) + agentApprovalRoute.GET("/user/deployments/:deployment_id/artifacts/:artifact_id/content", controller.AgentGetUserDeploymentArtifactContent) + agentApprovalRoute.GET("/user/deployments/:deployment_id/sk-snapshots", controller.AgentListUserSKSnapshots) + agentApprovalRoute.GET("/user/deployments/:deployment_id/timeline", controller.AgentGetUserDeploymentTimeline) + agentApprovalRoute.POST("/user/tasks/:task_id/deployment-draft", controller.AgentCreateTaskDeploymentDraft) } - // Agnet orchestration control plane (minimal integration endpoints) - agnetRoute := apiRouter.Group("/agnet") - agnetRoute.Use(middleware.AdminAuth()) + // Client-facing unified task routes (unified spec §5.1). task_id == the + // deployment_id, so these reuse the deployment control-plane handlers. + // Sub Agile -> agent_management; Swarm -> HeiCode-Swarm (mode is carried + // by the deployment record created at POST /tasks). + registerHeicodeTaskRoutes := func(group *gin.RouterGroup, createHandler gin.HandlerFunc) { + group.GET("/tasks", controller.AgentListUserDeployments) + group.POST("/tasks", createHandler) + group.GET("/tasks/:deployment_id", controller.AgentGetUserDeployment) + group.POST("/tasks/:deployment_id/stop", controller.AgentStopUserDeployment) + group.GET("/tasks/:deployment_id/timeline", controller.AgentGetUserDeploymentTimeline) + group.GET("/tasks/:deployment_id/workflow", controller.HeicodeTaskWorkflow) + group.GET("/tasks/:deployment_id/logs", controller.AgentListUserDeploymentLogs) + group.GET("/tasks/:deployment_id/events", controller.AgentListUserDeploymentEvents) + group.GET("/tasks/:deployment_id/metrics", controller.AgentGetUserDeploymentMetrics) + group.GET("/tasks/:deployment_id/diagnostics", controller.AgentGetUserDeploymentRuntimeDiagnostics) + group.GET("/tasks/:deployment_id/artifacts", controller.AgentListUserDeploymentArtifacts) + group.GET("/tasks/:deployment_id/artifacts/:artifact_id/content", controller.AgentGetUserDeploymentArtifactContent) + group.GET("/tasks/:deployment_id/sk-snapshots", controller.AgentListUserSKSnapshots) + group.POST("/tasks/:deployment_id/approvals/:approval_id/approve", controller.ApproveAgentApprovalRequest) + group.POST("/tasks/:deployment_id/approvals/:approval_id/reject", controller.RejectAgentApprovalRequest) + } + + heicodeSubAgileRoute := apiRouter.Group("/heicode/sub-agile") + heicodeSubAgileRoute.Use(middleware.UserOrV2DeviceAuth()) + registerHeicodeTaskRoutes(heicodeSubAgileRoute, controller.AgentCreateUserDeployment) + + heicodeSwarmRoute := apiRouter.Group("/heicode/swarm") + heicodeSwarmRoute.Use(middleware.UserOrV2DeviceAuth()) + registerHeicodeTaskRoutes(heicodeSwarmRoute, controller.AgentCreateUserSwarm) + + // Agent orchestration control plane (minimal integration endpoints) + agentRoute := apiRouter.Group("/agent") + agentRoute.Use(middleware.AdminAuth()) { - agnetRoute.GET("/deployments", controller.AgnetListDeployments) - agnetRoute.POST("/deployments", controller.AgnetCreateDeployment) - agnetRoute.GET("/deployments/:deployment_id", controller.AgnetGetDeployment) - agnetRoute.POST("/deployments/:deployment_id/stop", controller.AgnetStopDeployment) - agnetRoute.GET("/deployments/:deployment_id/logs", controller.AgnetListDeploymentLogs) - agnetRoute.GET("/deployments/:deployment_id/metrics", controller.AgnetGetDeploymentMetrics) - agnetRoute.GET("/deployments/:deployment_id/events", controller.AgnetListDeploymentEvents) - agnetRoute.GET("/deployments/:deployment_id/sk-snapshots", controller.AgnetListSKSnapshots) - agnetRoute.POST("/sk-snapshots/resolve", controller.AgnetResolveSKSnapshots) - agnetRoute.GET("/runtime/health", controller.AgnetRuntimeHealth) - agnetRoute.GET("/projects/:project_id/dashboard-snapshot", controller.AgnetProjectDashboardSnapshot) - agnetRoute.GET("/audit-logs", controller.AgnetListAuditLogs) + agentRoute.GET("/deployments", controller.AgentListDeployments) + agentRoute.POST("/deployments", controller.AgentCreateDeployment) + agentRoute.GET("/deployments/:deployment_id", controller.AgentGetDeployment) + agentRoute.POST("/deployments/:deployment_id/stop", controller.AgentStopDeployment) + agentRoute.GET("/deployments/:deployment_id/logs", controller.AgentListDeploymentLogs) + agentRoute.GET("/deployments/:deployment_id/metrics", controller.AgentGetDeploymentMetrics) + agentRoute.GET("/deployments/:deployment_id/events", controller.AgentListDeploymentEvents) + agentRoute.GET("/deployments/:deployment_id/sk-snapshots", controller.AgentListSKSnapshots) + agentRoute.POST("/sk-snapshots/resolve", controller.AgentResolveSKSnapshots) + agentRoute.GET("/runtime/health", controller.AgentRuntimeHealth) + agentRoute.GET("/projects/:project_id/dashboard-snapshot", controller.AgentProjectDashboardSnapshot) + agentRoute.GET("/audit-logs", controller.AgentListAuditLogs) } } } diff --git a/heicode/web/default/dist/index.html b/heicode/web/default/dist/index.html index b250dc7..4acc00f 100644 --- a/heicode/web/default/dist/index.html +++ b/heicode/web/default/dist/index.html @@ -13,14 +13,14 @@ - +
diff --git a/heicode/web/default/index.html b/heicode/web/default/index.html index cd95909..4973529 100644 --- a/heicode/web/default/index.html +++ b/heicode/web/default/index.html @@ -13,7 +13,7 @@ diff --git a/heicode/web/default/src/components/query-state.tsx b/heicode/web/default/src/components/query-state.tsx index f636dd8..1f4bcfc 100644 --- a/heicode/web/default/src/components/query-state.tsx +++ b/heicode/web/default/src/components/query-state.tsx @@ -105,7 +105,7 @@ export function QueryState(props: QueryStateProps) { {loadingFallback ?? ( // Sensible default — three skeleton rows mimicking a card // list. Callers pass `loadingFallback` to match their own - // grid (e.g. AgnetDeploymentsPage uses 2-col, audit uses + // grid (e.g. AgentDeploymentsPage uses 2-col, audit uses // a vertical timeline).
diff --git a/heicode/web/default/src/features/agnet-console/api.ts b/heicode/web/default/src/features/agent-console/api.ts similarity index 65% rename from heicode/web/default/src/features/agnet-console/api.ts rename to heicode/web/default/src/features/agent-console/api.ts index 3a12dda..5da1777 100644 --- a/heicode/web/default/src/features/agnet-console/api.ts +++ b/heicode/web/default/src/features/agent-console/api.ts @@ -1,23 +1,23 @@ import { api } from '@/lib/api' -export type AgnetSubMode = 'agile' | 'waterfall' +export type AgentSubMode = 'agile' | 'waterfall' -/** Sub-agent cloud/runtime binding (passed to Agnet on deploy). */ -export type AgnetRuntimeExecution = { +/** Sub-agent cloud/runtime binding (passed to Agent on deploy). */ +export type AgentRuntimeExecution = { profile_id?: string cloud_principal_refs?: string[] network_policy_ref?: string } /** SK allow/deny policy attached to the agent in the deployment plan. */ -export type AgnetSKAccessPolicy = { +export type AgentSKAccessPolicy = { policy_ref?: string deny_skill_ids?: string[] inherit_deployment_defaults?: boolean } /** Git-backed SK source (`type: git`). */ -export type AgnetRepoRef = { +export type AgentRepoRef = { connection_id?: string repo_url?: string ref: string @@ -25,20 +25,20 @@ export type AgnetRepoRef = { } /** Single SK source entry (git or upload). */ -export type AgnetSKSource = { +export type AgentSKSource = { type?: string artifact_id?: string mime?: string - repo_ref?: AgnetRepoRef + repo_ref?: AgentRepoRef } -export type AgnetAgentPlan = { +export type AgentAgentPlan = { role_template: string goal: string default_model_id?: string - sk_sources?: AgnetSKSource[] - runtime_execution?: AgnetRuntimeExecution - sk_access_policy?: AgnetSKAccessPolicy + sk_sources?: AgentSKSource[] + runtime_execution?: AgentRuntimeExecution + sk_access_policy?: AgentSKAccessPolicy resource_grants?: Array<{ grant_id?: string resource_id?: string @@ -56,13 +56,13 @@ export type AgnetAgentPlan = { }> } -export type AgnetBudget = { +export type AgentBudget = { max_tokens: number max_cost_usd: number max_duration_sec: number } -export type AgnetUserContext = { +export type AgentUserContext = { user_id: string email?: string role?: string @@ -70,15 +70,15 @@ export type AgnetUserContext = { subscription_tier?: string } -export type AgnetBillingContext = { +export type AgentBillingContext = { provider?: 'newapi' newapi_user_ref?: string newapi_group?: string quota_ref?: string } -export type AgnetAgentRuntime = { - platform?: 'agnet' +export type AgentAgentRuntime = { + platform?: 'agent' agents?: Array<{ role: string model_ref: string @@ -86,57 +86,57 @@ export type AgnetAgentRuntime = { }> } -export type AgnetConstraints = { - /** Runtime model allow-list for Agnet deployments; not a NewAPI billing map. */ +export type AgentConstraints = { + /** Runtime model allow-list for Agent deployments; not a NewAPI billing map. */ allowed_model_ids?: string[] } -export type AgnetOrchestrationMetadata = { +export type AgentOrchestrationMetadata = { /** - * Compatibility field for Agnet routing scope; UI treats this as user scope, + * Compatibility field for Agent routing scope; UI treats this as user scope, * not billing tenant. */ tenant_id: string /** - * Compatibility field for Agnet routing scope; UI treats this as resource + * Compatibility field for Agent routing scope; UI treats this as resource * scope, not project control. */ project_id: string correlation_id: string } -export type AgnetOrchestrationPlan = { +export type AgentOrchestrationPlan = { intent_id: string template_hint: string objective: string - sub_mode?: AgnetSubMode + sub_mode?: AgentSubMode risk_level: 'low' | 'medium' | 'high' - budget: AgnetBudget - user_context: AgnetUserContext - billing_context?: AgnetBillingContext - agent_runtime?: AgnetAgentRuntime - agents: AgnetAgentPlan[] - constraints: AgnetConstraints - metadata: AgnetOrchestrationMetadata + budget: AgentBudget + user_context: AgentUserContext + billing_context?: AgentBillingContext + agent_runtime?: AgentAgentRuntime + agents: AgentAgentPlan[] + constraints: AgentConstraints + metadata: AgentOrchestrationMetadata } -export type AgnetCreateDeploymentBody = { - orchestration_plan: AgnetOrchestrationPlan +export type AgentCreateDeploymentBody = { + orchestration_plan: AgentOrchestrationPlan } -export type AgnetCreateDeploymentResult = { +export type AgentCreateDeploymentResult = { deployment_id: string - sub_mode?: AgnetSubMode + sub_mode?: AgentSubMode status: string agent_instances?: Array<{ instance_id?: string role?: string phase?: string }> - permission_manifest?: AgnetPermissionManifest + permission_manifest?: AgentPermissionManifest } -export type AgnetPermissionManifest = { +export type AgentPermissionManifest = { user_id?: string binding_scope?: string agent_role?: string @@ -153,9 +153,9 @@ export type AgnetPermissionManifest = { }> } -export type AgnetDeployment = { +export type AgentDeployment = { deployment_id: string - sub_mode?: AgnetSubMode + sub_mode?: AgentSubMode status: string phase: string runtime_state?: string @@ -165,24 +165,24 @@ export type AgnetDeployment = { failure_reason?: string created_at: string updated_at: string - permission_manifest?: AgnetPermissionManifest + permission_manifest?: AgentPermissionManifest orchestration_plan: { intent_id?: string template_hint?: string objective?: string - sub_mode?: AgnetSubMode + sub_mode?: AgentSubMode risk_level?: string - budget?: AgnetBudget - agents?: AgnetAgentPlan[] - constraints?: AgnetConstraints - metadata?: AgnetOrchestrationMetadata + budget?: AgentBudget + agents?: AgentAgentPlan[] + constraints?: AgentConstraints + metadata?: AgentOrchestrationMetadata } } -export type AgnetRuntimeDiagnostics = { +export type AgentRuntimeDiagnostics = { deployment_id: string - runtime_mode?: 'agnet' | 'swarm' | string - sub_mode?: AgnetSubMode + runtime_mode?: 'agent' | 'swarm' | string + sub_mode?: AgentSubMode runtime_deployment_id?: string runtime_swarm_id?: string data_source?: string @@ -198,7 +198,7 @@ export type AgnetRuntimeDiagnostics = { checked_at?: string } -export type AgnetApprovalRequest = { +export type AgentApprovalRequest = { approval_id: string user_id: number deployment_id?: string @@ -221,10 +221,10 @@ export type AgnetApprovalRequest = { decided_at?: number created_at?: number updated_at?: number - credential_lease?: AgnetCredentialLease + credential_lease?: AgentCredentialLease } -export type AgnetCredentialLease = { +export type AgentCredentialLease = { lease_id: string credential_ref: string approval_id: string @@ -273,10 +273,10 @@ export type GitSourcePayload = { } // Platform-recommended role catalog shape. Mirrors backend -// `AgnetRoleTemplate` in controller/agnet_role_template.go. The +// `AgentRoleTemplate` in controller/agent_role_template.go. The // six canonical roles come from docs/product-package §13.3.3 — // keys are stable identifiers, display strings can be translated. -export type AgnetRoleTemplate = { +export type AgentRoleTemplate = { key: string display_name: string summary: string @@ -288,12 +288,12 @@ export type AgnetRoleTemplate = { // Cached at module level — the canonical six-role catalog doesn't // change between page loads, so we avoid an extra request every // time the create-deployment sheet opens. -let _roleTemplateCache: AgnetRoleTemplate[] | null = null +let _roleTemplateCache: AgentRoleTemplate[] | null = null -export async function listAgnetRoleTemplates(): Promise { +export async function listAgentRoleTemplates(): Promise { if (_roleTemplateCache) return _roleTemplateCache - const res = await api.get>( - '/api/agnet/role-templates' + const res = await api.get>( + '/api/agent/role-templates' ) const items = res.data?.data?.items ?? [] if (items.length > 0) { @@ -302,16 +302,16 @@ export async function listAgnetRoleTemplates(): Promise { return items } -export async function listAgnetDeployments(): Promise { - const res = await api.get>( - '/api/agnet/user/deployments' +export async function listAgentDeployments(): Promise { + const res = await api.get>( + '/api/agent/user/deployments' ) return res.data?.data?.items ?? [] } -export async function listAgnetDeploymentsQuiet(): Promise { - const res = await api.get>( - '/api/agnet/user/deployments', +export async function listAgentDeploymentsQuiet(): Promise { + const res = await api.get>( + '/api/agent/user/deployments', { skipBusinessError: true, skipErrorHandler: true, @@ -321,11 +321,11 @@ export async function listAgnetDeploymentsQuiet(): Promise { return res.data?.data?.items ?? [] } -export async function createAgnetDeployment( - body: AgnetCreateDeploymentBody -): Promise { - const res = await api.post>( - '/api/agnet/user/deployments', +export async function createAgentDeployment( + body: AgentCreateDeploymentBody +): Promise { + const res = await api.post>( + '/api/agent/user/deployments', body ) const env = res.data @@ -339,28 +339,28 @@ export async function createAgnetDeployment( return data } -export async function getAgnetDeploymentEvents(deploymentId: string) { +export async function getAgentDeploymentEvents(deploymentId: string) { const res = await api.get< ApiEnvelope<{ items?: Array> }> - >(`/api/agnet/user/deployments/${deploymentId}/events`) + >(`/api/agent/user/deployments/${deploymentId}/events`) return res.data?.data?.items ?? [] } -export async function getAgnetDeploymentArtifacts(deploymentId: string) { +export async function getAgentDeploymentArtifacts(deploymentId: string) { const res = await api.get< ApiEnvelope<{ items?: Array> }> - >(`/api/agnet/user/deployments/${deploymentId}/artifacts`) + >(`/api/agent/user/deployments/${deploymentId}/artifacts`) return res.data?.data?.items ?? [] } -export async function getAgnetDeploymentSKSnapshots(deploymentId: string) { +export async function getAgentDeploymentSKSnapshots(deploymentId: string) { const res = await api.get< ApiEnvelope<{ items?: Array> }> - >(`/api/agnet/user/deployments/${deploymentId}/sk-snapshots`) + >(`/api/agent/user/deployments/${deploymentId}/sk-snapshots`) return res.data?.data?.items ?? [] } -export async function getAgnetDeploymentTimeline(deploymentId: string) { +export async function getAgentDeploymentTimeline(deploymentId: string) { const res = await api.get< ApiEnvelope<{ callbacks?: Array> @@ -368,7 +368,7 @@ export async function getAgnetDeploymentTimeline(deploymentId: string) { sk_snapshots?: Array> timeline?: Array> }> - >(`/api/agnet/user/deployments/${deploymentId}/timeline`) + >(`/api/agent/user/deployments/${deploymentId}/timeline`) return ( res.data?.data ?? { callbacks: [], @@ -379,11 +379,11 @@ export async function getAgnetDeploymentTimeline(deploymentId: string) { ) } -export async function getAgnetRuntimeDiagnostics( +export async function getAgentRuntimeDiagnostics( deploymentId: string -): Promise { - const res = await api.get>( - `/api/agnet/user/deployments/${deploymentId}/runtime-diagnostics`, +): Promise { + const res = await api.get>( + `/api/agent/user/deployments/${deploymentId}/runtime-diagnostics`, { skipBusinessError: true, skipErrorHandler: true, @@ -393,46 +393,46 @@ export async function getAgnetRuntimeDiagnostics( return res.data?.data ?? null } -export async function simulateAgnetDeploymentEvents( +export async function simulateAgentDeploymentEvents( deploymentId: string, events?: string[] ): Promise<{ deployment_id: string; simulated: boolean; total: number }> { const res = await api.post< ApiEnvelope<{ deployment_id: string; simulated: boolean; total: number }> - >(`/api/agnet/user/deployments/${deploymentId}/simulate-events`, { + >(`/api/agent/user/deployments/${deploymentId}/simulate-events`, { events: events ?? [], }) const env = res.data if (!env?.success || !env.data) { - throw new Error(env?.message || 'simulateAgnetDeploymentEvents failed') + throw new Error(env?.message || 'simulateAgentDeploymentEvents failed') } return env.data } -export async function getAgnetAuditLogs() { +export async function getAgentAuditLogs() { const res = await api.get< ApiEnvelope<{ items?: Array> }> - >('/api/agnet/audit-logs') + >('/api/agent/audit-logs') return res.data?.data?.items ?? [] } -export async function listAgnetApprovals(params?: { +export async function listAgentApprovals(params?: { status?: string deployment_id?: string -}): Promise { - const res = await api.get>( - '/api/agnet/approvals', +}): Promise { + const res = await api.get>( + '/api/agent/approvals', { params } ) return res.data?.data?.items ?? [] } -export async function approveAgnetApproval( +export async function approveAgentApproval( approvalId: string, reason?: string -): Promise { - const res = await api.post>( - `/api/agnet/approvals/${approvalId}/approve`, +): Promise { + const res = await api.post>( + `/api/agent/approvals/${approvalId}/approve`, { reason } ) if (!res.data?.success || !res.data.data) { @@ -441,12 +441,12 @@ export async function approveAgnetApproval( return res.data.data } -export async function rejectAgnetApproval( +export async function rejectAgentApproval( approvalId: string, reason?: string -): Promise { - const res = await api.post>( - `/api/agnet/approvals/${approvalId}/reject`, +): Promise { + const res = await api.post>( + `/api/agent/approvals/${approvalId}/reject`, { reason } ) if (!res.data?.success || !res.data.data) { @@ -455,23 +455,23 @@ export async function rejectAgnetApproval( return res.data.data } -export async function listAgnetCredentialLeases(params?: { +export async function listAgentCredentialLeases(params?: { status?: string deployment_id?: string -}): Promise { - const res = await api.get>( - '/api/agnet/credential-leases', +}): Promise { + const res = await api.get>( + '/api/agent/credential-leases', { params } ) return res.data?.data?.items ?? [] } -export async function revokeAgnetCredentialLease( +export async function revokeAgentCredentialLease( leaseId: string, reason?: string -): Promise { - const res = await api.post>( - `/api/agnet/credential-leases/${leaseId}/revoke`, +): Promise { + const res = await api.post>( + `/api/agent/credential-leases/${leaseId}/revoke`, { reason } ) if (!res.data?.success || !res.data.data) { @@ -480,10 +480,10 @@ export async function revokeAgnetCredentialLease( return res.data.data } -export async function getAgnetSnapshots(deploymentId: string) { +export async function getAgentSnapshots(deploymentId: string) { const res = await api.get< ApiEnvelope<{ items?: Array> }> - >(`/api/agnet/deployments/${deploymentId}/sk-snapshots`, { + >(`/api/agent/deployments/${deploymentId}/sk-snapshots`, { skipBusinessError: true, skipErrorHandler: true, } as Record) diff --git a/heicode/web/default/src/features/agnet-console/azure-cloud-binding-sheet.tsx b/heicode/web/default/src/features/agent-console/azure-cloud-binding-sheet.tsx similarity index 100% rename from heicode/web/default/src/features/agnet-console/azure-cloud-binding-sheet.tsx rename to heicode/web/default/src/features/agent-console/azure-cloud-binding-sheet.tsx diff --git a/heicode/web/default/src/features/agnet-console/create-agnet-deployment-sheet.tsx b/heicode/web/default/src/features/agent-console/create-agent-deployment-sheet.tsx similarity index 97% rename from heicode/web/default/src/features/agnet-console/create-agnet-deployment-sheet.tsx rename to heicode/web/default/src/features/agent-console/create-agent-deployment-sheet.tsx index 70845d4..3caee27 100644 --- a/heicode/web/default/src/features/agnet-console/create-agnet-deployment-sheet.tsx +++ b/heicode/web/default/src/features/agent-console/create-agent-deployment-sheet.tsx @@ -40,13 +40,13 @@ import { import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' import { Textarea } from '@/components/ui/textarea' import { - createAgnetDeployment, - listAgnetRoleTemplates, - type AgnetAgentPlan, - type AgnetOrchestrationPlan, - type AgnetRoleTemplate, - type AgnetSKSource, - type AgnetSubMode, + createAgentDeployment, + listAgentRoleTemplates, + type AgentAgentPlan, + type AgentOrchestrationPlan, + type AgentRoleTemplate, + type AgentSKSource, + type AgentSubMode, } from './api' type ResourceType = @@ -240,7 +240,7 @@ function formatRiskLevel( return t('High') } -function formatSubMode(value: AgnetSubMode, t: (key: string) => string) { +function formatSubMode(value: AgentSubMode, t: (key: string) => string) { if (value === 'agile') return t('Agile') return t('Waterfall') } @@ -261,11 +261,11 @@ function formatRoleLabel(value: string, t: (key: string) => string) { function formatRoleDisplayName(value: string, t: (key: string) => string) { const name = value.trim().toLowerCase() const labels: Record = { - 'product agnet': t('Product Agnet'), - 'frontend agnet': t('Frontend Agnet'), - 'backend agnet': t('Backend Agnet'), - 'reviewer agnet': t('Reviewer Agnet'), - 'ops agnet': t('Ops Agnet'), + 'product agent': t('Product Agent'), + 'frontend agent': t('Frontend Agent'), + 'backend agent': t('Backend Agent'), + 'reviewer agent': t('Reviewer Agent'), + 'ops agent': t('Ops Agent'), } return labels[name] || value } @@ -293,15 +293,15 @@ function buildAgent( resourceScopeRef: string correlationId: string } -): AgnetAgentPlan { - let sk_sources: AgnetSKSource[] | undefined +): AgentAgentPlan { + let sk_sources: AgentSKSource[] | undefined const raw = row.sk_sources_json.trim() if (raw) { const parsed = JSON.parse(raw) as unknown if (!Array.isArray(parsed)) { throw new Error('sk_sources_must_be_array') } - sk_sources = parsed as AgnetSKSource[] + sk_sources = parsed as AgentSKSource[] } const role = row.role_template.trim() @@ -404,7 +404,7 @@ function SectionTitle({ ) } -export function CreateAgnetDeploymentSheet({ +export function CreateAgentDeploymentSheet({ open, onOpenChange, }: { @@ -419,7 +419,7 @@ export function CreateAgnetDeploymentSheet({ const [step, setStep] = useState('idea') const [templateHint, setTemplateHint] = useState(defaultTemplate.id) const [objective, setObjective] = useState(defaultTemplate.objective) - const [subMode, setSubMode] = useState('agile') + const [subMode, setSubMode] = useState('agile') const [riskLevel, setRiskLevel] = useState<'low' | 'medium' | 'high'>( defaultTemplate.risk ) @@ -442,11 +442,11 @@ export function CreateAgnetDeploymentSheet({ // subsequent sheet opens don't re-fetch. Empty array fallback // means the sheet stays usable if the catalog endpoint is down — // the role picker falls back to free-text input. - const [roleTemplates, setRoleTemplates] = useState([]) + const [roleTemplates, setRoleTemplates] = useState([]) useEffect(() => { if (!open) return let cancelled = false - listAgnetRoleTemplates() + listAgentRoleTemplates() .then((items) => { if (!cancelled) setRoleTemplates(items) }) @@ -556,7 +556,7 @@ export function CreateAgnetDeploymentSheet({ risk_level: riskLevel, resource_scope_ref: resourceScopeRef.trim(), agent_runtime: { - platform: 'agnet', + platform: 'agent', agents: previewAgents .filter((agent) => agent.role_template && agent.default_model_id) .map((agent) => ({ @@ -624,7 +624,7 @@ export function CreateAgnetDeploymentSheet({ mutationFn: async () => { const intentId = crypto.randomUUID() const allowed = splitComma(allowedModels) - let agentPlans: AgnetAgentPlan[] + let agentPlans: AgentAgentPlan[] try { agentPlans = agents.map((row, index) => buildAgent(row, index, { @@ -648,7 +648,7 @@ export function CreateAgnetDeploymentSheet({ instance_count: 1, })) - const plan: AgnetOrchestrationPlan = { + const plan: AgentOrchestrationPlan = { intent_id: intentId, template_hint: templateHint.trim(), objective: objective.trim(), @@ -674,7 +674,7 @@ export function CreateAgnetDeploymentSheet({ agent_runtime: runtimeAgents.length > 0 ? { - platform: 'agnet', + platform: 'agent', agents: runtimeAgents, } : undefined, @@ -689,15 +689,15 @@ export function CreateAgnetDeploymentSheet({ }, } - return createAgnetDeployment({ orchestration_plan: plan }) + return createAgentDeployment({ orchestration_plan: plan }) }, onSuccess: (data) => { toast.success( - t('Agnet deployment created', { + t('Agent deployment created', { deployment_id: data.deployment_id, }) as string ) - void queryClient.invalidateQueries({ queryKey: ['agnet', 'deployments'] }) + void queryClient.invalidateQueries({ queryKey: ['agent', 'deployments'] }) onOpenChange(false) resetForm() }, @@ -850,7 +850,7 @@ export function CreateAgnetDeploymentSheet({ setAllowedModels(e.target.value)} @@ -952,7 +952,7 @@ export function CreateAgnetDeploymentSheet({ icon={Bot} title={t('Role cards')} hint={t( - 'Each card maps one child Agnet role to a runtime model and one bounded resource grant.' + 'Each card maps one child Agent role to a runtime model and one bounded resource grant.' )} />
@@ -2156,7 +2156,7 @@ export function AgnetAuditPage() { >
{(approvalsQuery.data ?? []).map((approval) => ( -
{(leasesQuery.data ?? []).map((lease) => ( - 0 ? t('{{n}} doc source connected', { n: docSources.length }) : t( - 'Link product requirements, design docs or wiki repos so Agnet has project context.' + 'Link product requirements, design docs or wiki repos so Agent has project context.' ), done: docSources.length > 0, optional: true, @@ -2457,7 +2457,7 @@ export function AgnetSKSourcesPage() { @@ -2524,7 +2524,7 @@ export function AgnetSKSourcesPage() { disabled={!prereqsDone} onClick={() => setSummaryOpen(true)} > - {t('Confirm and launch Agnet')} + {t('Confirm and launch Agent')} ) : step.key === 'cloud' ? ( // Open the Azure-specific sheet. Manager stores the secret in @@ -2558,7 +2558,7 @@ export function AgnetSKSourcesPage() {

{t('How this works')}

{t( - 'Long-lived credentials are stored in the secret vault. Agnet only requests short-lived, scoped credentials at run time. Production deploys and other high-risk actions are approved from the desktop client.' + 'Long-lived credentials are stored in the secret vault. Agent only requests short-lived, scoped credentials at run time. Production deploys and other high-risk actions are approved from the desktop client.' )}

@@ -2812,8 +2812,8 @@ export function AgnetSKSourcesPage() { // ============================================================================= // 推荐确认卡 — docs/product-package/10 §"推荐确认卡": -// 本次会做 / 本次允许使用 / 本次不会做 / 高危规则 / 预计消耗 / 启动 Agnet -// 「启动 Agnet」旁边写「参数由 Heicode 自动生成」。 +// 本次会做 / 本次允许使用 / 本次不会做 / 高危规则 / 预计消耗 / 启动 Agent +// 「启动 Agent」旁边写「参数由 Heicode 自动生成」。 // 没有 JSON 编辑器、permission manifest、resource grant 表(§10 高级展开禁令)。 // ============================================================================= @@ -2859,12 +2859,12 @@ function RecommendationSummaryDialog({ const handleLaunch = () => { setLaunching(true) - // Real /api/agnet/deployments POST is wired separately when the task + // Real /api/agent/deployments POST is wired separately when the task // object backend lands. For now the summary card matches the docs spec // visually; clicking captures intent + hands off to the desktop client. setTimeout(() => { toast.success( - t('Agnet launch staged. Continue the task in the desktop client.') + t('Agent launch staged. Continue the task in the desktop client.') ) setLaunching(false) onClose() @@ -2880,7 +2880,7 @@ function RecommendationSummaryDialog({ {t('Recommendation summary')}

- {t('Confirm scope, risk and budget before launching Agnet')} + {t('Confirm scope, risk and budget before launching Agent')}

{t( @@ -2939,7 +2939,7 @@ function RecommendationSummaryDialog({ }} > - {launching ? t('Launching…') : t('Launch Agnet')} + {launching ? t('Launching…') : t('Launch Agent')}

@@ -2998,7 +2998,7 @@ function RecBlock({ // Templates / Agents (kept for backward compatibility — invoked by side routes) // ============================================================================= -export function AgnetTemplatesPage() { +export function AgentTemplatesPage() { const { t } = useTranslation() const templates = [ { @@ -3035,7 +3035,7 @@ export function AgnetTemplatesPage() { ) } -function runtimeSummary(rt: AgnetRuntimeExecution | undefined): boolean { +function runtimeSummary(rt: AgentRuntimeExecution | undefined): boolean { if (!rt) return false return Boolean( (rt.profile_id && rt.profile_id.trim() !== '') || @@ -3044,7 +3044,7 @@ function runtimeSummary(rt: AgnetRuntimeExecution | undefined): boolean { ) } -function policySummary(p: AgnetSKAccessPolicy | undefined): boolean { +function policySummary(p: AgentSKAccessPolicy | undefined): boolean { if (!p) return false return Boolean( (p.policy_ref && p.policy_ref.trim() !== '') || @@ -3053,15 +3053,15 @@ function policySummary(p: AgnetSKAccessPolicy | undefined): boolean { ) } -export function AgnetAgentsPage() { +export function AgentAgentsPage() { const { t } = useTranslation() const { data = [] } = useQuery({ - queryKey: ['agnet', 'deployments'], - queryFn: listAgnetDeployments, + queryKey: ['agent', 'deployments'], + queryFn: listAgentDeployments, }) const rows = useMemo( () => - data.flatMap((dep: AgnetDeployment) => + data.flatMap((dep: AgentDeployment) => (dep.orchestration_plan?.agents || []).map((agent, idx) => ({ dep: dep.deployment_id, id: `${dep.deployment_id}-${idx}`, @@ -3078,7 +3078,7 @@ export function AgnetAgentsPage() { return ( {rows.length === 0 ? (

{row.role}

- {row.dep} · {t('Agnet runtime model')}: {row.runtimeModel} + {row.dep} · {t('Agent runtime model')}: {row.runtimeModel}

{row.goal}

{runtimeSummary(row.runtime) && ( diff --git a/heicode/web/default/src/features/agnet-hub/index.tsx b/heicode/web/default/src/features/agent-hub/index.tsx similarity index 93% rename from heicode/web/default/src/features/agnet-hub/index.tsx rename to heicode/web/default/src/features/agent-hub/index.tsx index 9972062..063e8d5 100644 --- a/heicode/web/default/src/features/agnet-hub/index.tsx +++ b/heicode/web/default/src/features/agent-hub/index.tsx @@ -1,6 +1,6 @@ import { Link } from '@tanstack/react-router' -type AgnetHubProps = { +type AgentHubProps = { title: string description: string } @@ -11,7 +11,7 @@ const quickLinks = [ { title: 'Audit', to: '/audit' as const }, ] -export function AgnetHub(props: AgnetHubProps) { +export function AgentHub(props: AgentHubProps) { return (
diff --git a/heicode/web/default/src/features/auth/api.ts b/heicode/web/default/src/features/auth/api.ts index c2b49a0..907c924 100644 --- a/heicode/web/default/src/features/auth/api.ts +++ b/heicode/web/default/src/features/auth/api.ts @@ -51,10 +51,10 @@ export function clearHeicodeTokens() { } /** - * 外部 Agnet 登录成功后,用 token 向本站校验身份并写入 Manager 会话 Cookie。 + * 外部 Agent 登录成功后,用 token 向本站校验身份并写入 Manager 会话 Cookie。 * 不在本站再做密码校验;本地用户按需 JIT 创建。 */ -async function establishManagerSessionFromAgnet(): Promise<{ +async function establishManagerSessionFromAgent(): Promise<{ managerUserId?: number }> { const access_token = readToken(ACCESS_TOKEN_KEY) @@ -63,7 +63,7 @@ async function establishManagerSessionFromAgnet(): Promise<{ throw new Error('Missing Heicode access token') } const res = await api.post( - '/api/user/session/from-agnet', + '/api/user/session/from-agent', { access_token, refresh_token: refresh_token || undefined, @@ -101,13 +101,13 @@ async function establishManagerSessionFromAgnet(): Promise<{ return { managerUserId: body.data?.id } } -export async function loginWithAgnetTokens( +export async function loginWithAgentTokens( accessToken: string, refreshToken?: string ): Promise<{ managerUserId?: number }> { writeTokens(accessToken, refreshToken) try { - return await establishManagerSessionFromAgnet() + return await establishManagerSessionFromAgent() } catch (err) { clearHeicodeTokens() throw err @@ -185,7 +185,7 @@ export async function login(payload: LoginPayload) { if (res?.success) { writeTokens(res.data?.token, res.data?.refreshToken) try { - const sessionRes = await establishManagerSessionFromAgnet() + const sessionRes = await establishManagerSessionFromAgent() managerUserId = sessionRes.managerUserId } catch (syncErr) { if (isTwoFactorRequiredError(syncErr)) { @@ -330,7 +330,7 @@ export async function wechatLoginByCode(code: string): Promise { // Registration // ---------------------------------------------------------------------------- -// User registration via Agnet (太极 AI PAD) +// User registration via Agent (太极 AI PAD) export async function register(payload: RegisterPayload): Promise { - const list: AgnetDeployment[] = deploymentsQuery.data ?? [] + const list: AgentDeployment[] = deploymentsQuery.data ?? [] const counters: Record = { running: 0, success: 0, diff --git a/heicode/web/default/src/features/dashboard/components/home-hero.tsx b/heicode/web/default/src/features/dashboard/components/home-hero.tsx index b2927bd..92dcabe 100644 --- a/heicode/web/default/src/features/dashboard/components/home-hero.tsx +++ b/heicode/web/default/src/features/dashboard/components/home-hero.tsx @@ -189,7 +189,7 @@ function IdeaInput({ t }: { t: ReturnType['t'] }) {

{t( - 'Describe the product, feature, or fix. Heicode will draft the task, recommend resources, and dispatch the Agnet team.' + 'Describe the product, feature, or fix. Heicode will draft the task, recommend resources, and dispatch the Agent team.' )}

@@ -293,7 +293,7 @@ function ContinueTasks({ ) : recent.length === 0 ? (

{t( - 'No tasks yet. Capture an idea above, then confirm the recommendation in the desktop client to launch Agnet.' + 'No tasks yet. Capture an idea above, then confirm the recommendation in the desktop client to launch Agent.' )}

) : ( @@ -377,7 +377,7 @@ function TodayFocus({ tone: 'running', title: t('Running'), count: buckets.running.length, - hint: t('Active Agnet sub-loops'), + hint: t('Active Agent sub-loops'), }, ] @@ -477,7 +477,7 @@ function HelperEntries({ t }: { t: ReturnType['t'] }) { { Icon: Rocket, title: t('Task overview'), - desc: t('Status of every Agnet task you launched'), + desc: t('Status of every Agent task you launched'), to: '/deployments', }, ] diff --git a/heicode/web/default/src/features/home/components/hero-terminal-demo.tsx b/heicode/web/default/src/features/home/components/hero-terminal-demo.tsx index f1a862b..fa9ba98 100644 --- a/heicode/web/default/src/features/home/components/hero-terminal-demo.tsx +++ b/heicode/web/default/src/features/home/components/hero-terminal-demo.tsx @@ -15,7 +15,7 @@ const MODELS: ModelConfig[] = [ id: 'gpt-4o', name: 'gpt-4o', response: - 'Agnet planners propose orchestration runs; the platform arbitrates risk and budget before any agent executes a step.', + 'Agent planners propose orchestration runs; the platform arbitrates risk and budget before any agent executes a step.', tokens: 27, latency: 142, badgeClass: diff --git a/heicode/web/default/src/features/tasks/task-card-view.tsx b/heicode/web/default/src/features/tasks/task-card-view.tsx index 25bb091..b50bcaf 100644 --- a/heicode/web/default/src/features/tasks/task-card-view.tsx +++ b/heicode/web/default/src/features/tasks/task-card-view.tsx @@ -45,9 +45,9 @@ import { cn } from '@/lib/utils' import { Button } from '@/components/ui/button' import { Skeleton } from '@/components/ui/skeleton' import { - createAgnetDeployment, - type AgnetOrchestrationPlan, -} from '@/features/agnet-console/api' + createAgentDeployment, + type AgentOrchestrationPlan, +} from '@/features/agent-console/api' const route = getRouteApi('/_authenticated/tasks/$id') @@ -214,13 +214,13 @@ export function TaskCardView() { role_templates: ['backend'], default_model_id: 'claude-sonnet-4-6', }) - return createAgnetDeployment({ + return createAgentDeployment({ orchestration_plan: - draft.orchestration_plan as unknown as AgnetOrchestrationPlan, + draft.orchestration_plan as unknown as AgentOrchestrationPlan, }) }, onSuccess: (deployment) => { - void queryClient.invalidateQueries({ queryKey: ['agnet', 'deployments'] }) + void queryClient.invalidateQueries({ queryKey: ['agent', 'deployments'] }) toast.success( t('Manager deployment created', { deployment_id: deployment.deployment_id, diff --git a/heicode/web/default/src/features/wallet/index.tsx b/heicode/web/default/src/features/wallet/index.tsx index 034e990..c2ecd8f 100644 --- a/heicode/web/default/src/features/wallet/index.tsx +++ b/heicode/web/default/src/features/wallet/index.tsx @@ -91,7 +91,7 @@ export function Wallet(props: WalletProps) { // the canonical balance source is mcp-server /api/user/heicode/balance // (server-side wraps NewAPI admin token, returns the freshest figures). // Fall back to NewAPI /api/user/self when the user hasn't yet been - // mirrored into NewAPI via from-agnet (HEICODE_USER_NOT_FOUND) so a + // mirrored into NewAPI via from-agent (HEICODE_USER_NOT_FOUND) so a // brand-new account still sees something instead of empty stats. const fetchUser = useCallback(async () => { try { diff --git a/heicode/web/default/src/hooks/use-top-nav-links.ts b/heicode/web/default/src/hooks/use-top-nav-links.ts index 2917e75..0cedf46 100644 --- a/heicode/web/default/src/hooks/use-top-nav-links.ts +++ b/heicode/web/default/src/hooks/use-top-nav-links.ts @@ -10,9 +10,9 @@ export type TopNavLink = { external?: boolean } -// Default top-nav modules — agnet command axis only. +// Default top-nav modules — agent command axis only. // Pricing/Models/Channels are intentionally absent: Heicode Manager -// is a tenant + agnet control plane, not an API gateway storefront. +// is a tenant + agent control plane, not an API gateway storefront. const DEFAULT_HEADER_NAV_MODULES = { home: true, overview: true, @@ -26,7 +26,7 @@ const DEFAULT_HEADER_NAV_MODULES = { * Backend format example (stringified JSON): * { * home: true, - * agnet: true, + * agent: true, * deployments: true, * events: true, * audit: true, diff --git a/heicode/web/default/src/i18n/locales/_reports/fr.untranslated.json b/heicode/web/default/src/i18n/locales/_reports/fr.untranslated.json index 399e2f0..d2385a5 100644 --- a/heicode/web/default/src/i18n/locales/_reports/fr.untranslated.json +++ b/heicode/web/default/src/i18n/locales/_reports/fr.untranslated.json @@ -2,7 +2,7 @@ "Account provisioning is handled by your platform administrator. Reach out to the Heicode operator to be added to a tenant.": "Account provisioning is handled by your platform administrator. Reach out to the Heicode operator to be added to a tenant.", "Adjust filters or trigger a new orchestration plan.": "Adjust filters or trigger a new orchestration plan.", "Authenticate against the Heicode identity service. Tenant scope, role and SK access will be loaded automatically.": "Authenticate against the Heicode identity service. Tenant scope, role and SK access will be loaded automatically.", - "Card-based view of every Agnet orchestration run with risk, budget, executor and live status.": "Card-based view of every Agnet orchestration run with risk, budget, executor and live status.", + "Card-based view of every Agent orchestration run with risk, budget, executor and live status.": "Card-based view of every Agent orchestration run with risk, budget, executor and live status.", "Click to view image": "Click to view image", "Client login API contract and integration screenshots": "Client login API contract and integration screenshots", "Create a new code delivery run with checks.": "Create a new code delivery run with checks.", @@ -13,7 +13,7 @@ "Git sources subtitle": "Bind Git repositories for SK, then review immutable snapshot anchors tied to each deployment.", "Git-backed SK sources description": "Immutable snapshots from bound Git refs and uploads—wired into each run for audit.", "Immutable, hash-verified context bundles wired to every run.": "Immutable, hash-verified context bundles wired to every run.", - "Lifecycle and policy events emitted by Agnet deployments. Correlate with deployment cards.": "Lifecycle and policy events emitted by Agnet deployments. Correlate with deployment cards.", + "Lifecycle and policy events emitted by Agent deployments. Correlate with deployment cards.": "Lifecycle and policy events emitted by Agent deployments. Correlate with deployment cards.", "No audit entries match the filter.": "No audit entries match the filter.", "No resolved snapshots hint": "Resolve snapshots from the control plane after Git-backed sk_sources are configured.", "No deployments match the current filter": "No deployments match the current filter", @@ -24,7 +24,7 @@ "Reset filters to see all entries.": "Reset filters to see all entries.", "Resolve snapshots from the control plane to capture SK lineage.": "Resolve snapshots from the control plane to capture SK lineage.", "Set custom About HTML or URL in System Settings > General > About.": "Set custom About HTML or URL in System Settings > General > About.", - "Sign in to operate Agnet deployments, inspect events, and audit SK snapshots for every tenant under your account.": "Sign in to operate Agnet deployments, inspect events, and audit SK snapshots for every tenant under your account.", + "Sign in to operate Agent deployments, inspect events, and audit SK snapshots for every tenant under your account.": "Sign in to operate Agent deployments, inspect events, and audit SK snapshots for every tenant under your account.", "Sign in to operate deployments and review Git-bound snapshot anchors for tenants under your account.": "Sign in to operate deployments and review Git-bound snapshot anchors for tenants under your account.", "Sign in to your workspace": "Sign in to your workspace", "Source → snapshot → hash chain. Every snapshot is immutable and tied to the orchestration plan.": "Source → snapshot → hash chain. Every snapshot is immutable and tied to the orchestration plan.", diff --git a/heicode/web/default/src/i18n/locales/_reports/ja.untranslated.json b/heicode/web/default/src/i18n/locales/_reports/ja.untranslated.json index d63c623..d726d94 100644 --- a/heicode/web/default/src/i18n/locales/_reports/ja.untranslated.json +++ b/heicode/web/default/src/i18n/locales/_reports/ja.untranslated.json @@ -13,7 +13,7 @@ "Agentic development control plane": "Agentic development control plane", "Agents": "Agents", "Agile Minimal": "Agile Minimal", - "Agnet orchestration": "Agnet orchestration", + "Agent orchestration": "Agent orchestration", "AIGC2D": "AIGC2D", "All statuses": "All statuses", "All usage logs": "All usage logs", @@ -26,7 +26,7 @@ "Awaiting platform arbitration": "Awaiting platform arbitration", "AZURE_OPENAI_ENDPOINT *": "AZURE_OPENAI_ENDPOINT *", "budget": "budget", - "Card-based view of every Agnet orchestration run with risk, budget, executor and live status.": "Card-based view of every Agnet orchestration run with risk, budget, executor and live status.", + "Card-based view of every Agent orchestration run with risk, budget, executor and live status.": "Card-based view of every Agent orchestration run with risk, budget, executor and live status.", "channel": "channel", "checkout.session.completed": "checkout.session.completed", "checkout.session.expired": "checkout.session.expired", @@ -96,7 +96,7 @@ "Insufficient permission": "Insufficient permission", "Jimeng": "Jimeng", "JustSong": "JustSong", - "Lifecycle and policy events emitted by Agnet deployments. Correlate with deployment cards.": "Lifecycle and policy events emitted by Agnet deployments. Correlate with deployment cards.", + "Lifecycle and policy events emitted by Agent deployments. Correlate with deployment cards.": "Lifecycle and policy events emitted by Agent deployments. Correlate with deployment cards.", "LingYiWanWu": "LingYiWanWu", "LinuxDO": "LinuxDO", "Live code delivery runs": "Live code delivery runs", @@ -161,7 +161,7 @@ "Running deployments": "Running deployments", "Select deployment": "Select deployment", "Set custom About HTML or URL in System Settings > General > About.": "Set custom About HTML or URL in System Settings > General > About.", - "Sign in to operate Agnet deployments, inspect events, and audit SK snapshots for every tenant under your account.": "Sign in to operate Agnet deployments, inspect events, and audit SK snapshots for every tenant under your account.", + "Sign in to operate Agent deployments, inspect events, and audit SK snapshots for every tenant under your account.": "Sign in to operate Agent deployments, inspect events, and audit SK snapshots for every tenant under your account.", "Sign in to operate deployments and review Git-bound snapshot anchors for tenants under your account.": "Sign in to operate deployments and review Git-bound snapshot anchors for tenants under your account.", "Sign in to your workspace": "Sign in to your workspace", "SiliconFlow": "SiliconFlow", diff --git a/heicode/web/default/src/i18n/locales/_reports/ru.untranslated.json b/heicode/web/default/src/i18n/locales/_reports/ru.untranslated.json index 4c5a082..7ab3a09 100644 --- a/heicode/web/default/src/i18n/locales/_reports/ru.untranslated.json +++ b/heicode/web/default/src/i18n/locales/_reports/ru.untranslated.json @@ -16,7 +16,7 @@ "Agentic development control plane": "Agentic development control plane", "Agents": "Agents", "Agile Minimal": "Agile Minimal", - "Agnet orchestration": "Agnet orchestration", + "Agent orchestration": "Agent orchestration", "AI Proxy": "AI Proxy", "AIGC2D": "AIGC2D", "All statuses": "All statuses", @@ -30,7 +30,7 @@ "AZURE_OPENAI_ENDPOINT *": "AZURE_OPENAI_ENDPOINT *", "Baidu V2": "Baidu V2", "budget": "budget", - "Card-based view of every Agnet orchestration run with risk, budget, executor and live status.": "Card-based view of every Agnet orchestration run with risk, budget, executor and live status.", + "Card-based view of every Agent orchestration run with risk, budget, executor and live status.": "Card-based view of every Agent orchestration run with risk, budget, executor and live status.", "channel": "channel", "checkout.session.completed": "checkout.session.completed", "checkout.session.expired": "checkout.session.expired", @@ -100,7 +100,7 @@ "Insufficient permission": "Insufficient permission", "Jimeng": "Jimeng", "JustSong": "JustSong", - "Lifecycle and policy events emitted by Agnet deployments. Correlate with deployment cards.": "Lifecycle and policy events emitted by Agnet deployments. Correlate with deployment cards.", + "Lifecycle and policy events emitted by Agent deployments. Correlate with deployment cards.": "Lifecycle and policy events emitted by Agent deployments. Correlate with deployment cards.", "LingYiWanWu": "LingYiWanWu", "LinuxDO": "LinuxDO", "Live code delivery runs": "Live code delivery runs", @@ -164,7 +164,7 @@ "Running deployments": "Running deployments", "Select deployment": "Select deployment", "Set custom About HTML or URL in System Settings > General > About.": "Set custom About HTML or URL in System Settings > General > About.", - "Sign in to operate Agnet deployments, inspect events, and audit SK snapshots for every tenant under your account.": "Sign in to operate Agnet deployments, inspect events, and audit SK snapshots for every tenant under your account.", + "Sign in to operate Agent deployments, inspect events, and audit SK snapshots for every tenant under your account.": "Sign in to operate Agent deployments, inspect events, and audit SK snapshots for every tenant under your account.", "Sign in to operate deployments and review Git-bound snapshot anchors for tenants under your account.": "Sign in to operate deployments and review Git-bound snapshot anchors for tenants under your account.", "Sign in to your workspace": "Sign in to your workspace", "SiliconFlow": "SiliconFlow", diff --git a/heicode/web/default/src/i18n/locales/_reports/vi.untranslated.json b/heicode/web/default/src/i18n/locales/_reports/vi.untranslated.json index 399e2f0..d2385a5 100644 --- a/heicode/web/default/src/i18n/locales/_reports/vi.untranslated.json +++ b/heicode/web/default/src/i18n/locales/_reports/vi.untranslated.json @@ -2,7 +2,7 @@ "Account provisioning is handled by your platform administrator. Reach out to the Heicode operator to be added to a tenant.": "Account provisioning is handled by your platform administrator. Reach out to the Heicode operator to be added to a tenant.", "Adjust filters or trigger a new orchestration plan.": "Adjust filters or trigger a new orchestration plan.", "Authenticate against the Heicode identity service. Tenant scope, role and SK access will be loaded automatically.": "Authenticate against the Heicode identity service. Tenant scope, role and SK access will be loaded automatically.", - "Card-based view of every Agnet orchestration run with risk, budget, executor and live status.": "Card-based view of every Agnet orchestration run with risk, budget, executor and live status.", + "Card-based view of every Agent orchestration run with risk, budget, executor and live status.": "Card-based view of every Agent orchestration run with risk, budget, executor and live status.", "Click to view image": "Click to view image", "Client login API contract and integration screenshots": "Client login API contract and integration screenshots", "Create a new code delivery run with checks.": "Create a new code delivery run with checks.", @@ -13,7 +13,7 @@ "Git sources subtitle": "Bind Git repositories for SK, then review immutable snapshot anchors tied to each deployment.", "Git-backed SK sources description": "Immutable snapshots from bound Git refs and uploads—wired into each run for audit.", "Immutable, hash-verified context bundles wired to every run.": "Immutable, hash-verified context bundles wired to every run.", - "Lifecycle and policy events emitted by Agnet deployments. Correlate with deployment cards.": "Lifecycle and policy events emitted by Agnet deployments. Correlate with deployment cards.", + "Lifecycle and policy events emitted by Agent deployments. Correlate with deployment cards.": "Lifecycle and policy events emitted by Agent deployments. Correlate with deployment cards.", "No audit entries match the filter.": "No audit entries match the filter.", "No resolved snapshots hint": "Resolve snapshots from the control plane after Git-backed sk_sources are configured.", "No deployments match the current filter": "No deployments match the current filter", @@ -24,7 +24,7 @@ "Reset filters to see all entries.": "Reset filters to see all entries.", "Resolve snapshots from the control plane to capture SK lineage.": "Resolve snapshots from the control plane to capture SK lineage.", "Set custom About HTML or URL in System Settings > General > About.": "Set custom About HTML or URL in System Settings > General > About.", - "Sign in to operate Agnet deployments, inspect events, and audit SK snapshots for every tenant under your account.": "Sign in to operate Agnet deployments, inspect events, and audit SK snapshots for every tenant under your account.", + "Sign in to operate Agent deployments, inspect events, and audit SK snapshots for every tenant under your account.": "Sign in to operate Agent deployments, inspect events, and audit SK snapshots for every tenant under your account.", "Sign in to operate deployments and review Git-bound snapshot anchors for tenants under your account.": "Sign in to operate deployments and review Git-bound snapshot anchors for tenants under your account.", "Sign in to your workspace": "Sign in to your workspace", "Source → snapshot → hash chain. Every snapshot is immutable and tied to the orchestration plan.": "Source → snapshot → hash chain. Every snapshot is immutable and tied to the orchestration plan.", diff --git a/heicode/web/default/src/i18n/locales/en.json b/heicode/web/default/src/i18n/locales/en.json index 36d3990..a480298 100644 --- a/heicode/web/default/src/i18n/locales/en.json +++ b/heicode/web/default/src/i18n/locales/en.json @@ -78,7 +78,7 @@ "Actions": "Actions", "active": "active", "Active": "Active", - "Active Agnet sub-loops": "Active Agnet sub-loops", + "Active Agent sub-loops": "Active Agent sub-loops", "Active Cache Count": "Active Cache Count", "Active code delivery runs across tenants": "Active code delivery runs across tenants", "Active Files": "Active Files", @@ -176,7 +176,7 @@ "After invalidating, this subscription will be immediately deactivated. Historical records are not affected. Continue?": "After invalidating, this subscription will be immediately deactivated. Historical records are not affected. Continue?", "After scanning, the binding will complete automatically": "After scanning, the binding will complete automatically", "Agent": "Agent", - "Agent declarations parsed from each Agnet deployment plan.": "Agent declarations parsed from each Agnet deployment plan.", + "Agent declarations parsed from each Agent deployment plan.": "Agent declarations parsed from each Agent deployment plan.", "Agent declarations parsed from each deployment plan.": "Agent declarations parsed from each deployment plan.", "Agent ID *": "Agent ID *", "Agentic development control plane": "Agentic development control plane", @@ -184,12 +184,12 @@ "Aggregated usage metrics and trend charts.": "Aggregated usage metrics and trend charts.", "aggregates 50+ AI providers behind one unified API. Manage access, track costs, and scale effortlessly.": "aggregates 50+ AI providers behind one unified API. Manage access, track costs, and scale effortlessly.", "Agile Minimal": "Agile Minimal", - "Agnet allowed models comma": "Agnet runtime model IDs (comma-separated)", - "Agnet deployment created": "Agnet deployment created ({{deployment_id}})", - "Agnet launch staged. Continue the task in the desktop client.": "Agnet launch staged. Continue the task in the desktop client.", - "Agnet orchestration": "Agnet orchestration", - "Agnet runtime model": "Agnet runtime model", - "Agnet runtime model id": "Agnet runtime model ID", + "Agent allowed models comma": "Agent runtime model IDs (comma-separated)", + "Agent deployment created": "Agent deployment created ({{deployment_id}})", + "Agent launch staged. Continue the task in the desktop client.": "Agent launch staged. Continue the task in the desktop client.", + "Agent orchestration": "Agent orchestration", + "Agent runtime model": "Agent runtime model", + "Agent runtime model id": "Agent runtime model ID", "ago": "ago", "AGPL v3.0 License": "AGPL v3.0 License", "AI model testing environment": "AI model testing environment", @@ -504,7 +504,7 @@ "Browse and compare": "Browse and compare", "budget": "budget", "Budget & usage": "Budget & usage", - "Budget caps": "Agnet runtime caps", + "Budget caps": "Agent runtime caps", "Budget tokens = max tokens × ratio. Accepts a decimal between 0.002 and 1. Recommended to keep aligned with upstream billing.": "Budget tokens = max tokens × ratio. Accepts a decimal between 0.002 and 1. Recommended to keep aligned with upstream billing.", "Budget tokens = max tokens × ratio. Accepts a decimal between 0.1 and 1.": "Budget tokens = max tokens × ratio. Accepts a decimal between 0.1 and 1.", "Budget Tokens Ratio": "Budget Tokens Ratio", @@ -547,7 +547,7 @@ "Cancelled": "Cancelled", "Cancelled at": "Cancelled at", "Capture a reusable bundle of models, tags, or endpoints.": "Capture a reusable bundle of models, tags, or endpoints.", - "Card-based view of every Agnet orchestration run with risk, budget, executor and live status.": "Card-based view of every Agnet orchestration run with risk, runtime caps, resource scope and live status.", + "Card-based view of every Agent orchestration run with risk, budget, executor and live status.": "Card-based view of every Agent orchestration run with risk, runtime caps, resource scope and live status.", "Category Name": "Category Name", "Category name is required": "Category name is required", "Category name must be less than 50 characters": "Category name must be less than 50 characters", @@ -771,7 +771,7 @@ "Configure your account preferences and integrations": "Configure your account preferences and integrations", "Confirm": "Confirm", "Confirm Action": "Confirm Action", - "Confirm and launch Agnet": "Confirm and launch Agnet", + "Confirm and launch Agent": "Confirm and launch Agent", "Confirm Batch Update": "Confirm Batch Update", "Confirm Billing Conflicts": "Confirm Billing Conflicts", "Confirm Changes": "Confirm Changes", @@ -790,7 +790,7 @@ "Confirm password": "Confirm password", "Confirm Payment": "Confirm Payment", "Confirm recommendation summary": "Confirm recommendation summary", - "Confirm scope, risk and budget before launching Agnet": "Confirm scope, risk and budget before launching Agnet", + "Confirm scope, risk and budget before launching Agent": "Confirm scope, risk and budget before launching Agent", "Confirm Selection": "Confirm Selection", "Confirm settings and finish setup": "Confirm settings and finish setup", "Confirm Unbind": "Confirm Unbind", @@ -883,8 +883,8 @@ "Create a new code delivery run with checks.": "Create a new code delivery run with checks.", "Create a new user group to configure ratio overrides for.": "Create a new user group to configure ratio overrides for.", "Create account": "Create account", - "Create Agnet deployment": "Create Agnet deployment", - "Create Agnet deployment description": "Send the orchestration plan to Agnet. Model choices here are runtime policy, not billing setup.", + "Create Agent deployment": "Create Agent deployment", + "Create Agent deployment description": "Send the orchestration plan to Agent. Model choices here are runtime policy, not billing setup.", "Create an account": "Create an account", "Create and review invite or credit codes.": "Create and review invite or credit codes.", "Create API Key": "Create API Key", @@ -1040,7 +1040,7 @@ "Deployments": "Deployments", "Desc": "Desc", "Describe": "Describe", - "Describe the product, feature, or fix. Heicode will draft the task, recommend resources, and dispatch the Agnet team.": "Describe the product, feature, or fix. Heicode will draft the task, recommend resources, and dispatch the Agnet team.", + "Describe the product, feature, or fix. Heicode will draft the task, recommend resources, and dispatch the Agent team.": "Describe the product, feature, or fix. Heicode will draft the task, recommend resources, and dispatch the Agent team.", "Describe this model...": "Describe this model...", "Describe this vendor...": "Describe this vendor...", "Describe what this work should achieve.": "Describe what this work should achieve.", @@ -1181,7 +1181,7 @@ "e.g., us-central1 or JSON format for model-specific regions": "e.g., us-central1 or JSON format for model-specific regions", "e.g., v2.1": "e.g., v2.1", "Each backup code can only be used once.": "Each backup code can only be used once.", - "Each card maps one child Agnet role to a runtime model and one bounded resource grant.": "Each card maps one child Agnet role to a runtime model and one bounded resource grant.", + "Each card maps one child Agent role to a runtime model and one bounded resource grant.": "Each card maps one child Agent role to a runtime model and one bounded resource grant.", "Each item must be an object with a single key-value pair.": "Each item must be an object with a single key-value pair.", "Each item must have exactly one key-value pair.": "Each item must have exactly one key-value pair.", "Each line represents one keyword. Leave blank to disable the list but keep the switch states.": "Each line represents one keyword. Leave blank to disable the list but keep the switch states.", @@ -1677,8 +1677,8 @@ "Get Started": "Get Started", "Git binding": "Git binding", "Git sources": "Git sources", - "Git sources binding explainer": "Skill (SK) definitions live in Git. Manager does not edit Markdown here—register code repos, SK tool repos, and refs via the Heicode client or your deployment plan sk_sources. Pass runtime and SK-policy parameters into Agnet when it starts each deployment; effective permissions are stored and enforced on Agnet. Below lists immutable snapshot anchors (Git commit / upload artifact) Agnet resolved for auditing.", - "Git sources subtitle": "Configure bindings and deployment parameters for Agnet, then review snapshot anchors per deployment (enforcement lives on Agnet).", + "Git sources binding explainer": "Skill (SK) definitions live in Git. Manager does not edit Markdown here—register code repos, SK tool repos, and refs via the Heicode client or your deployment plan sk_sources. Pass runtime and SK-policy parameters into Agent when it starts each deployment; effective permissions are stored and enforced on Agent. Below lists immutable snapshot anchors (Git commit / upload artifact) Agent resolved for auditing.", + "Git sources subtitle": "Configure bindings and deployment parameters for Agent, then review snapshot anchors per deployment (enforcement lives on Agent).", "Git sources workflow step 1": "Bind your team’s Git repositories that hold application code and delivery context.", "Git sources workflow step 2": "Bind SK tool repositories (skills registry) that Resource Grants allow this run to draw from.", "Git sources workflow step 3": "Allocate cloud capacity and permissions for sub-agents—for example dedicated VMs, roles, and API scopes.", @@ -1958,7 +1958,7 @@ "Last updated:": "Last updated:", "Last Used": "Last Used", "Last used:": "Last used:", - "Launch Agnet": "Launch Agnet", + "Launch Agent": "Launch Agent", "Launching…": "Launching…", "Layout": "Layout", "Learn more": "Learn more", @@ -1978,7 +1978,7 @@ "Leave empty to use system temp directory": "Leave empty to use system temp directory", "Leave empty to use username": "Leave empty to use username", "Less": "Less", - "Lifecycle and policy events emitted by Agnet deployments. Correlate with deployment cards.": "Lifecycle and policy events emitted by Agnet deployments. Correlate with deployment cards.", + "Lifecycle and policy events emitted by Agent deployments. Correlate with deployment cards.": "Lifecycle and policy events emitted by Agent deployments. Correlate with deployment cards.", "Light": "Light", "Lightning Fast": "Lightning Fast", "Limit period": "Limit period", @@ -2023,7 +2023,7 @@ "Logo": "Logo", "Logo URL": "Logo URL", "Logs": "Logs", - "Long-lived credentials are stored in the secret vault. Agnet only requests short-lived, scoped credentials at run time. Production deploys and other high-risk actions are approved from the desktop client.": "Long-lived credentials are stored in the secret vault. Agnet only requests short-lived, scoped credentials at run time. Production deploys and other high-risk actions are approved from the desktop client.", + "Long-lived credentials are stored in the secret vault. Agent only requests short-lived, scoped credentials at run time. Production deploys and other high-risk actions are approved from the desktop client.": "Long-lived credentials are stored in the secret vault. Agent only requests short-lived, scoped credentials at run time. Production deploys and other high-risk actions are approved from the desktop client.", "m": "m", "Maintain a list of common questions for the dashboard help panel": "Maintain a list of common questions for the dashboard help panel", "Maintenance": "Maintenance", @@ -2381,7 +2381,7 @@ "No Sync": "No Sync", "No system announcements": "No system announcements", "No tasks yet": "No tasks yet", - "No tasks yet. Capture an idea above, then confirm the recommendation in the desktop client to launch Agnet.": "No tasks yet. Capture an idea above, then confirm the recommendation in the desktop client to launch Agnet.", + "No tasks yet. Capture an idea above, then confirm the recommendation in the desktop client to launch Agent.": "No tasks yet. Capture an idea above, then confirm the recommendation in the desktop client to launch Agent.", "No token found.": "No token found.", "No tools configured": "No tools configured", "No Upgrade": "No Upgrade", @@ -3039,7 +3039,7 @@ "Resources workflow step 1": "Bind project code, SK, document, or cloud-resource metadata without exposing raw credentials.", "Resources workflow step 2": "Keep secret material behind secret_ref; Manager surfaces references and status only.", "Resources workflow step 3": "Allocate scope, allowed paths/actions, runtime policy, and budget to the run manifest.", - "Resources workflow step 4": "Start a Work/Run; Agnet resolves immutable anchors and enforces the effective grants.", + "Resources workflow step 4": "Start a Work/Run; Agent resolves immutable anchors and enforces the effective grants.", "Resources workflow step 5": "Use snapshots, events, and audit together to replay which resource context actually ran.", "Resources workflow title": "Resource-to-run flow", "Response": "Response", @@ -3295,7 +3295,7 @@ "Sidebar Personal Settings": "Sidebar Personal Settings", "Sign in": "Sign in", "Sign In": "Sign In", - "Sign in to operate Agnet deployments, inspect events, and audit SK snapshots for every tenant under your account.": "Sign in to operate Agnet deployments, inspect events, and audit SK snapshots for every tenant under your account.", + "Sign in to operate Agent deployments, inspect events, and audit SK snapshots for every tenant under your account.": "Sign in to operate Agent deployments, inspect events, and audit SK snapshots for every tenant under your account.", "Sign in to operate deployments and review Git-bound snapshot anchors for tenants under your account.": "Sign in to bind repositories and cloud permissions, deploy sub-agents, and review Git-bound snapshot anchors for tenants under your account.", "Sign in to your workspace": "Sign in to your workspace", "Sign in with Passkey": "Sign in with Passkey", @@ -3364,10 +3364,10 @@ "status": "status", "Status & Sync": "Status & Sync", "Status Code Mapping": "Status Code Mapping", - "Status of every Agnet task you launched": "Status of every Agnet task you launched", + "Status of every Agent task you launched": "Status of every Agent task you launched", "Status Page Slug": "Status Page Slug", "Status, errors and budget burn in one auditable stream.": "Status, errors and budget burn in one auditable stream.", - "Status, latest activity and last update for every Agnet task you launched. Details live in the desktop client.": "Status, latest activity and last update for every Agnet task you launched. Details live in the desktop client.", + "Status, latest activity and last update for every Agent task you launched. Details live in the desktop client.": "Status, latest activity and last update for every Agent task you launched. Details live in the desktop client.", "Status:": "Status:", "Stay": "Stay", "Stay tuned though!": "Stay tuned though!", @@ -3480,7 +3480,7 @@ "Task Logs": "Task Logs", "Task not found. It may have been removed or was never created.": "Task not found. It may have been removed or was never created.", "Task overview": "Task overview", - "Tasks appear here after you confirm the recommendation in the desktop client and launch Agnet.": "Tasks appear here after you confirm the recommendation in the desktop client and launch Agnet.", + "Tasks appear here after you confirm the recommendation in the desktop client and launch Agent.": "Tasks appear here after you confirm the recommendation in the desktop client and launch Agent.", "Runtime diagnostics": "Runtime diagnostics", "Manager checks Runtime status separately from callback data, without mixing ordinary sub and swarm modes.": "Manager checks Runtime status separately from callback data, without mixing ordinary sub and swarm modes.", "runtime mode": "runtime mode", @@ -3685,7 +3685,7 @@ "Total:": "Total:", "TPM": "TPM", "Trace delivery context snapshots by hash.": "Trace delivery context snapshots by hash.", - "Track every Agnet work run by status, risk, budget, scope and secret_ref coverage.": "Track every Agnet work run by status, risk, budget, scope and secret_ref coverage.", + "Track every Agent work run by status, risk, budget, scope and secret_ref coverage.": "Track every Agent work run by status, risk, budget, scope and secret_ref coverage.", "Track per-request consumption to power usage analytics. Keeping this on increases database writes.": "Track per-request consumption to power usage analytics. Keeping this on increases database writes.", "Track usage, costs and performance with real-time analytics": "Track usage, costs and performance with real-time analytics", "Tracks current account base limits and additional metered usage on Codex upstream.": "Tracks current account base limits and additional metered usage on Codex upstream.", @@ -4112,7 +4112,7 @@ "This run will do": "This run will do", "No objective provided yet — go back to the Idea tab.": "No objective provided yet — go back to the Idea tab.", "Resources this run may use": "Resources this run may use", - "No resources bound yet — Agnet will run with no external data access.": "No resources bound yet — Agnet will run with no external data access.", + "No resources bound yet — Agent will run with no external data access.": "No resources bound yet — Agent will run with no external data access.", "no actions specified": "no actions specified", "This run will NOT do": "This run will NOT do", "Production deploys without client approval": "Production deploys without client approval", @@ -4143,7 +4143,7 @@ "No audit events yet for this deployment.": "No audit events yet for this deployment.", "Connect project docs": "Connect project docs", "{{n}} doc source connected": "{{n}} doc source connected", - "Link product requirements, design docs or wiki repos so Agnet has project context.": "Link product requirements, design docs or wiki repos so Agnet has project context.", + "Link product requirements, design docs or wiki repos so Agent has project context.": "Link product requirements, design docs or wiki repos so Agent has project context.", "Connect SK skill packs": "Connect SK skill packs", "{{n}} SK source connected": "{{n}} SK source connected", "Pick a reusable skill / agent toolset repository, or skip.": "Pick a reusable skill / agent toolset repository, or skip.", diff --git a/heicode/web/default/src/i18n/locales/fr.json b/heicode/web/default/src/i18n/locales/fr.json index 001631c..126d0a4 100644 --- a/heicode/web/default/src/i18n/locales/fr.json +++ b/heicode/web/default/src/i18n/locales/fr.json @@ -211,12 +211,12 @@ "Inherits deployment defaults": "Inherits deployment defaults", "Add agent": "Add agent", "Agent": "Agent", - "Agnet deployment created": "Agnet deployment created ({{deployment_id}})", + "Agent deployment created": "Agent deployment created ({{deployment_id}})", "Allowed models comma": "Allowed model IDs (comma-separated)", "Budget caps": "Budget caps", "Cloud principals comma": "Cloud principals (comma-separated)", - "Create Agnet deployment": "Create Agnet deployment", - "Create Agnet deployment description": "Send the full orchestration plan in one request. Manager stores it; execution runs on Agnet.", + "Create Agent deployment": "Create Agent deployment", + "Create Agent deployment description": "Send the full orchestration plan in one request. Manager stores it; execution runs on Agent.", "Default model id": "Default model ID", "Deployment plan": "Deployment plan", "Deployment request failed": "Deployment request failed", @@ -236,7 +236,7 @@ "Aggregated usage metrics and trend charts.": "Métriques d'utilisation agrégées et graphiques de tendances.", "aggregates 50+ AI providers behind one unified API. Manage access, track costs, and scale effortlessly.": "agrège plus de 50 fournisseurs IA derrière une API unifiée. Gérez l'accès, suivez les coûts et évoluez sans effort.", "Agile Minimal": "Agile Minimal", - "Agnet orchestration": "Agnet orchestration", + "Agent orchestration": "Agent orchestration", "AGPL v3.0 License": "Licence AGPL v3.0", "AI model testing environment": "Environnement de test de modèle IA", "AI models": "Modèles d'IA", @@ -566,7 +566,7 @@ "Cancelled": "Annulé", "Cancelled at": "Annulé le", "Capture a reusable bundle of models, tags, or endpoints.": "Capturez un ensemble réutilisable de modèles, d'étiquettes ou de points de terminaison.", - "Card-based view of every Agnet orchestration run with risk, budget, executor and live status.": "Card-based view of every Agnet orchestration run with risk, budget, executor and live status.", + "Card-based view of every Agent orchestration run with risk, budget, executor and live status.": "Card-based view of every Agent orchestration run with risk, budget, executor and live status.", "Category Name": "Nom de la catégorie", "Category name is required": "Le nom de la catégorie est requis", "Category name must be less than 50 characters": "Le nom de la catégorie doit contenir moins de 50 caractères", @@ -1640,8 +1640,8 @@ "Get Started": "Commencer", "Git binding": "Git binding", "Git sources": "Git sources", - "Git sources binding explainer": "Skill (SK) definitions live in Git. Manager does not edit Markdown here—register code repos, SK tool repos, and refs via the Heicode client or your deployment plan sk_sources. Pass runtime and SK-policy parameters into Agnet when it starts each deployment; effective permissions are stored and enforced on Agnet. Below lists immutable snapshot anchors (Git commit / upload artifact) Agnet resolved for auditing.", - "Git sources subtitle": "Configure bindings and deployment parameters for Agnet, then review snapshot anchors per deployment (enforcement lives on Agnet).", + "Git sources binding explainer": "Skill (SK) definitions live in Git. Manager does not edit Markdown here—register code repos, SK tool repos, and refs via the Heicode client or your deployment plan sk_sources. Pass runtime and SK-policy parameters into Agent when it starts each deployment; effective permissions are stored and enforced on Agent. Below lists immutable snapshot anchors (Git commit / upload artifact) Agent resolved for auditing.", + "Git sources subtitle": "Configure bindings and deployment parameters for Agent, then review snapshot anchors per deployment (enforcement lives on Agent).", "Git sources workflow title": "Typical setup flow", "Git sources workflow step 1": "Bind your team’s Git repositories that hold application code and delivery context.", "Git sources workflow step 2": "Bind SK tool repositories (skills registry) your tenant is allowed to draw from.", @@ -1918,7 +1918,7 @@ "Leave empty to use system temp directory": "Laisser vide pour utiliser le répertoire temporaire", "Leave empty to use username": "Laissez vide pour utiliser le nom d'utilisateur", "Less": "Moins", - "Lifecycle and policy events emitted by Agnet deployments. Correlate with deployment cards.": "Lifecycle and policy events emitted by Agnet deployments. Correlate with deployment cards.", + "Lifecycle and policy events emitted by Agent deployments. Correlate with deployment cards.": "Lifecycle and policy events emitted by Agent deployments. Correlate with deployment cards.", "Light": "Clair", "Lightning Fast": "Extrêmement rapide", "Limit period": "Période de limite", @@ -3146,7 +3146,7 @@ "Sidebar Personal Settings": "Paramètres personnels de la barre latérale", "Sign in": "Se connecter", "Sign In": "Se connecter", - "Sign in to operate Agnet deployments, inspect events, and audit SK snapshots for every tenant under your account.": "Sign in to operate Agnet deployments, inspect events, and audit SK snapshots for every tenant under your account.", + "Sign in to operate Agent deployments, inspect events, and audit SK snapshots for every tenant under your account.": "Sign in to operate Agent deployments, inspect events, and audit SK snapshots for every tenant under your account.", "Sign in to operate deployments and review Git-bound snapshot anchors for tenants under your account.": "Sign in to bind repositories and cloud permissions, deploy sub-agents, and review Git-bound snapshot anchors for tenants under your account.", "Sign in to your workspace": "Sign in to your workspace", "Sign in with Passkey": "Se connecter avec Passkey", diff --git a/heicode/web/default/src/i18n/locales/ja.json b/heicode/web/default/src/i18n/locales/ja.json index 4bb5f86..e87ddac 100644 --- a/heicode/web/default/src/i18n/locales/ja.json +++ b/heicode/web/default/src/i18n/locales/ja.json @@ -211,12 +211,12 @@ "Inherits deployment defaults": "Inherits deployment defaults", "Add agent": "Add agent", "Agent": "Agent", - "Agnet deployment created": "Agnet deployment created ({{deployment_id}})", + "Agent deployment created": "Agent deployment created ({{deployment_id}})", "Allowed models comma": "Allowed model IDs (comma-separated)", "Budget caps": "Budget caps", "Cloud principals comma": "Cloud principals (comma-separated)", - "Create Agnet deployment": "Create Agnet deployment", - "Create Agnet deployment description": "Send the full orchestration plan in one request. Manager stores it; execution runs on Agnet.", + "Create Agent deployment": "Create Agent deployment", + "Create Agent deployment description": "Send the full orchestration plan in one request. Manager stores it; execution runs on Agent.", "Default model id": "Default model ID", "Deployment plan": "Deployment plan", "Deployment request failed": "Deployment request failed", @@ -236,7 +236,7 @@ "Aggregated usage metrics and trend charts.": "集計された使用量メトリクスとトレンドチャート。", "aggregates 50+ AI providers behind one unified API. Manage access, track costs, and scale effortlessly.": "50以上のAIプロバイダーを統一APIで集約。アクセス管理、コスト追跡、スケーリングを簡単に。", "Agile Minimal": "Agile Minimal", - "Agnet orchestration": "Agnet orchestration", + "Agent orchestration": "Agent orchestration", "AGPL v3.0 License": "AGPL v3.0ライセンス", "AI model testing environment": "AIモデルテスト環境", "AI models": "AIモデル", @@ -566,7 +566,7 @@ "Cancelled": "キャンセル", "Cancelled at": "キャンセル日時", "Capture a reusable bundle of models, tags, or endpoints.": "モデル、タグ、またはエンドポイントの再利用可能なバンドルを保存。", - "Card-based view of every Agnet orchestration run with risk, budget, executor and live status.": "Card-based view of every Agnet orchestration run with risk, budget, executor and live status.", + "Card-based view of every Agent orchestration run with risk, budget, executor and live status.": "Card-based view of every Agent orchestration run with risk, budget, executor and live status.", "Category Name": "分類名称", "Category name is required": "カテゴリ名は必須です", "Category name must be less than 50 characters": "カテゴリ名は50文字以内にしてください", @@ -1640,8 +1640,8 @@ "Get Started": "開始する", "Git binding": "Git binding", "Git sources": "Git sources", - "Git sources binding explainer": "Skill (SK) definitions live in Git. Manager does not edit Markdown here—register code repos, SK tool repos, and refs via the Heicode client or your deployment plan sk_sources. Pass runtime and SK-policy parameters into Agnet when it starts each deployment; effective permissions are stored and enforced on Agnet. Below lists immutable snapshot anchors (Git commit / upload artifact) Agnet resolved for auditing.", - "Git sources subtitle": "Configure bindings and deployment parameters for Agnet, then review snapshot anchors per deployment (enforcement lives on Agnet).", + "Git sources binding explainer": "Skill (SK) definitions live in Git. Manager does not edit Markdown here—register code repos, SK tool repos, and refs via the Heicode client or your deployment plan sk_sources. Pass runtime and SK-policy parameters into Agent when it starts each deployment; effective permissions are stored and enforced on Agent. Below lists immutable snapshot anchors (Git commit / upload artifact) Agent resolved for auditing.", + "Git sources subtitle": "Configure bindings and deployment parameters for Agent, then review snapshot anchors per deployment (enforcement lives on Agent).", "Git sources workflow title": "Typical setup flow", "Git sources workflow step 1": "Bind your team’s Git repositories that hold application code and delivery context.", "Git sources workflow step 2": "Bind SK tool repositories (skills registry) your tenant is allowed to draw from.", @@ -1918,7 +1918,7 @@ "Leave empty to use system temp directory": "空欄でシステムの一時ディレクトリを使用", "Leave empty to use username": "ユーザー名を使用するには空のままにしてください", "Less": "少ない", - "Lifecycle and policy events emitted by Agnet deployments. Correlate with deployment cards.": "Lifecycle and policy events emitted by Agnet deployments. Correlate with deployment cards.", + "Lifecycle and policy events emitted by Agent deployments. Correlate with deployment cards.": "Lifecycle and policy events emitted by Agent deployments. Correlate with deployment cards.", "Light": "ライト", "Lightning Fast": "超高速", "Limit period": "制限期間", @@ -3146,7 +3146,7 @@ "Sidebar Personal Settings": "サイドバー個人設定", "Sign in": "ログイン", "Sign In": "ログイン", - "Sign in to operate Agnet deployments, inspect events, and audit SK snapshots for every tenant under your account.": "Sign in to operate Agnet deployments, inspect events, and audit SK snapshots for every tenant under your account.", + "Sign in to operate Agent deployments, inspect events, and audit SK snapshots for every tenant under your account.": "Sign in to operate Agent deployments, inspect events, and audit SK snapshots for every tenant under your account.", "Sign in to operate deployments and review Git-bound snapshot anchors for tenants under your account.": "Sign in to bind repositories and cloud permissions, deploy sub-agents, and review Git-bound snapshot anchors for tenants under your account.", "Sign in to your workspace": "Sign in to your workspace", "Sign in with Passkey": "Passkeyでログイン", diff --git a/heicode/web/default/src/i18n/locales/ru.json b/heicode/web/default/src/i18n/locales/ru.json index e7ad823..cf0cb39 100644 --- a/heicode/web/default/src/i18n/locales/ru.json +++ b/heicode/web/default/src/i18n/locales/ru.json @@ -211,12 +211,12 @@ "Inherits deployment defaults": "Inherits deployment defaults", "Add agent": "Add agent", "Agent": "Agent", - "Agnet deployment created": "Agnet deployment created ({{deployment_id}})", + "Agent deployment created": "Agent deployment created ({{deployment_id}})", "Allowed models comma": "Allowed model IDs (comma-separated)", "Budget caps": "Budget caps", "Cloud principals comma": "Cloud principals (comma-separated)", - "Create Agnet deployment": "Create Agnet deployment", - "Create Agnet deployment description": "Send the full orchestration plan in one request. Manager stores it; execution runs on Agnet.", + "Create Agent deployment": "Create Agent deployment", + "Create Agent deployment description": "Send the full orchestration plan in one request. Manager stores it; execution runs on Agent.", "Default model id": "Default model ID", "Deployment plan": "Deployment plan", "Deployment request failed": "Deployment request failed", @@ -236,7 +236,7 @@ "Aggregated usage metrics and trend charts.": "Агрегированные метрики использования и графики трендов.", "aggregates 50+ AI providers behind one unified API. Manage access, track costs, and scale effortlessly.": "объединяет 50+ ИИ-провайдеров за единым API. Управляйте доступом, отслеживайте затраты и масштабируйтесь без усилий.", "Agile Minimal": "Agile Minimal", - "Agnet orchestration": "Agnet orchestration", + "Agent orchestration": "Agent orchestration", "AGPL v3.0 License": "Лицензия AGPL v3.0", "AI model testing environment": "Среда тестирования ИИ моделей", "AI models": "Модели ИИ", @@ -566,7 +566,7 @@ "Cancelled": "Отменено", "Cancelled at": "Отменено", "Capture a reusable bundle of models, tags, or endpoints.": "Создайте повторно используемый набор моделей, тегов или конечных точек.", - "Card-based view of every Agnet orchestration run with risk, budget, executor and live status.": "Card-based view of every Agnet orchestration run with risk, budget, executor and live status.", + "Card-based view of every Agent orchestration run with risk, budget, executor and live status.": "Card-based view of every Agent orchestration run with risk, budget, executor and live status.", "Category Name": "Название категории", "Category name is required": "Название категории обязательно", "Category name must be less than 50 characters": "Название категории должно содержать менее 50 символов", @@ -1640,8 +1640,8 @@ "Get Started": "Начать", "Git binding": "Git binding", "Git sources": "Git sources", - "Git sources binding explainer": "Skill (SK) definitions live in Git. Manager does not edit Markdown here—register code repos, SK tool repos, and refs via the Heicode client or your deployment plan sk_sources. Pass runtime and SK-policy parameters into Agnet when it starts each deployment; effective permissions are stored and enforced on Agnet. Below lists immutable snapshot anchors (Git commit / upload artifact) Agnet resolved for auditing.", - "Git sources subtitle": "Configure bindings and deployment parameters for Agnet, then review snapshot anchors per deployment (enforcement lives on Agnet).", + "Git sources binding explainer": "Skill (SK) definitions live in Git. Manager does not edit Markdown here—register code repos, SK tool repos, and refs via the Heicode client or your deployment plan sk_sources. Pass runtime and SK-policy parameters into Agent when it starts each deployment; effective permissions are stored and enforced on Agent. Below lists immutable snapshot anchors (Git commit / upload artifact) Agent resolved for auditing.", + "Git sources subtitle": "Configure bindings and deployment parameters for Agent, then review snapshot anchors per deployment (enforcement lives on Agent).", "Git sources workflow title": "Typical setup flow", "Git sources workflow step 1": "Bind your team’s Git repositories that hold application code and delivery context.", "Git sources workflow step 2": "Bind SK tool repositories (skills registry) your tenant is allowed to draw from.", @@ -1918,7 +1918,7 @@ "Leave empty to use system temp directory": "Оставьте пустым для системного временного каталога", "Leave empty to use username": "Оставьте пустым, чтобы использовать имя пользователя", "Less": "Меньше", - "Lifecycle and policy events emitted by Agnet deployments. Correlate with deployment cards.": "Lifecycle and policy events emitted by Agnet deployments. Correlate with deployment cards.", + "Lifecycle and policy events emitted by Agent deployments. Correlate with deployment cards.": "Lifecycle and policy events emitted by Agent deployments. Correlate with deployment cards.", "Light": "Светлая", "Lightning Fast": "Молниеносно быстро", "Limit period": "Период ограничения", @@ -3146,7 +3146,7 @@ "Sidebar Personal Settings": "Личные настройки боковой панели", "Sign in": "Войти", "Sign In": "Войти", - "Sign in to operate Agnet deployments, inspect events, and audit SK snapshots for every tenant under your account.": "Sign in to operate Agnet deployments, inspect events, and audit SK snapshots for every tenant under your account.", + "Sign in to operate Agent deployments, inspect events, and audit SK snapshots for every tenant under your account.": "Sign in to operate Agent deployments, inspect events, and audit SK snapshots for every tenant under your account.", "Sign in to operate deployments and review Git-bound snapshot anchors for tenants under your account.": "Sign in to bind repositories and cloud permissions, deploy sub-agents, and review Git-bound snapshot anchors for tenants under your account.", "Sign in to your workspace": "Sign in to your workspace", "Sign in with Passkey": "Войти с Passkey", diff --git a/heicode/web/default/src/i18n/locales/vi.json b/heicode/web/default/src/i18n/locales/vi.json index dc1f2c8..b6bb2c8 100644 --- a/heicode/web/default/src/i18n/locales/vi.json +++ b/heicode/web/default/src/i18n/locales/vi.json @@ -211,12 +211,12 @@ "Inherits deployment defaults": "Inherits deployment defaults", "Add agent": "Add agent", "Agent": "Agent", - "Agnet deployment created": "Agnet deployment created ({{deployment_id}})", + "Agent deployment created": "Agent deployment created ({{deployment_id}})", "Allowed models comma": "Allowed model IDs (comma-separated)", "Budget caps": "Budget caps", "Cloud principals comma": "Cloud principals (comma-separated)", - "Create Agnet deployment": "Create Agnet deployment", - "Create Agnet deployment description": "Send the full orchestration plan in one request. Manager stores it; execution runs on Agnet.", + "Create Agent deployment": "Create Agent deployment", + "Create Agent deployment description": "Send the full orchestration plan in one request. Manager stores it; execution runs on Agent.", "Default model id": "Default model ID", "Deployment plan": "Deployment plan", "Deployment request failed": "Deployment request failed", @@ -236,7 +236,7 @@ "Aggregated usage metrics and trend charts.": "Chỉ số sử dụng tổng hợp và biểu đồ xu hướng.", "aggregates 50+ AI providers behind one unified API. Manage access, track costs, and scale effortlessly.": "tổng hợp hơn 50 nhà cung cấp AI sau một API thống nhất. Quản lý truy cập, theo dõi chi phí và mở rộng dễ dàng.", "Agile Minimal": "Agile Minimal", - "Agnet orchestration": "Agnet orchestration", + "Agent orchestration": "Agent orchestration", "AGPL v3.0 License": "Giấy phép AGPL v3.0", "AI model testing environment": "Môi trường thử nghiệm mô hình AI", "AI models": "mô hình AI", @@ -566,7 +566,7 @@ "Cancelled": "Đã hủy", "Cancelled at": "Đã hủy lúc", "Capture a reusable bundle of models, tags, or endpoints.": "Đóng gói một bộ có thể tái sử dụng gồm các mô hình, thẻ hoặc điểm cuối.", - "Card-based view of every Agnet orchestration run with risk, budget, executor and live status.": "Card-based view of every Agnet orchestration run with risk, budget, executor and live status.", + "Card-based view of every Agent orchestration run with risk, budget, executor and live status.": "Card-based view of every Agent orchestration run with risk, budget, executor and live status.", "Category Name": "Tên danh mục", "Category name is required": "Tên danh mục là bắt buộc", "Category name must be less than 50 characters": "Tên danh mục phải ít hơn 50 ký tự", @@ -1640,8 +1640,8 @@ "Get Started": "Bắt đầu", "Git binding": "Git binding", "Git sources": "Git sources", - "Git sources binding explainer": "Skill (SK) definitions live in Git. Manager does not edit Markdown here—register code repos, SK tool repos, and refs via the Heicode client or your deployment plan sk_sources. Pass runtime and SK-policy parameters into Agnet when it starts each deployment; effective permissions are stored and enforced on Agnet. Below lists immutable snapshot anchors (Git commit / upload artifact) Agnet resolved for auditing.", - "Git sources subtitle": "Configure bindings and deployment parameters for Agnet, then review snapshot anchors per deployment (enforcement lives on Agnet).", + "Git sources binding explainer": "Skill (SK) definitions live in Git. Manager does not edit Markdown here—register code repos, SK tool repos, and refs via the Heicode client or your deployment plan sk_sources. Pass runtime and SK-policy parameters into Agent when it starts each deployment; effective permissions are stored and enforced on Agent. Below lists immutable snapshot anchors (Git commit / upload artifact) Agent resolved for auditing.", + "Git sources subtitle": "Configure bindings and deployment parameters for Agent, then review snapshot anchors per deployment (enforcement lives on Agent).", "Git sources workflow title": "Typical setup flow", "Git sources workflow step 1": "Bind your team’s Git repositories that hold application code and delivery context.", "Git sources workflow step 2": "Bind SK tool repositories (skills registry) your tenant is allowed to draw from.", @@ -1918,7 +1918,7 @@ "Leave empty to use system temp directory": "Để trống để sử dụng thư mục tạm của hệ thống", "Leave empty to use username": "Để trống để sử dụng tên người dùng", "Less": "Ít hơn", - "Lifecycle and policy events emitted by Agnet deployments. Correlate with deployment cards.": "Lifecycle and policy events emitted by Agnet deployments. Correlate with deployment cards.", + "Lifecycle and policy events emitted by Agent deployments. Correlate with deployment cards.": "Lifecycle and policy events emitted by Agent deployments. Correlate with deployment cards.", "Light": "Ánh sáng", "Lightning Fast": "Nhanh như chớp", "Limit period": "Thời hiệu", @@ -3146,7 +3146,7 @@ "Sidebar Personal Settings": "Cài đặt cá nhân thanh bên", "Sign in": "Đăng nhập", "Sign In": "Đăng nhập", - "Sign in to operate Agnet deployments, inspect events, and audit SK snapshots for every tenant under your account.": "Sign in to operate Agnet deployments, inspect events, and audit SK snapshots for every tenant under your account.", + "Sign in to operate Agent deployments, inspect events, and audit SK snapshots for every tenant under your account.": "Sign in to operate Agent deployments, inspect events, and audit SK snapshots for every tenant under your account.", "Sign in to operate deployments and review Git-bound snapshot anchors for tenants under your account.": "Sign in to bind repositories and cloud permissions, deploy sub-agents, and review Git-bound snapshot anchors for tenants under your account.", "Sign in to your workspace": "Sign in to your workspace", "Sign in with Passkey": "Đăng nhập bằng Passkey", diff --git a/heicode/web/default/src/i18n/locales/zh.json b/heicode/web/default/src/i18n/locales/zh.json index 1e6ae9c..d881707 100644 --- a/heicode/web/default/src/i18n/locales/zh.json +++ b/heicode/web/default/src/i18n/locales/zh.json @@ -78,7 +78,7 @@ "Actions": "操作", "active": "活跃", "Active": "生效", - "Active Agnet sub-loops": "正在推进的 Agnet 子环节", + "Active Agent sub-loops": "正在推进的 Agent 子环节", "Active Cache Count": "活跃缓存数", "Active code delivery runs across tenants": "当前各租户正在进行的代码交付任务", "Active Files": "活跃文件", @@ -176,20 +176,20 @@ "After invalidating, this subscription will be immediately deactivated. Historical records are not affected. Continue?": "作废后该订阅将立即失效,历史记录不受影响。是否继续?", "After scanning, the binding will complete automatically": "扫描后,绑定将自动完成", "Agent": "子 Agent", - "Agent declarations parsed from each Agnet deployment plan.": "从各 Agnet 部署计划中解析的 Agent 声明。", - "Agent declarations parsed from each deployment plan.": "从各 Agnet 部署计划中解析的 Agent 声明。", + "Agent declarations parsed from each Agent deployment plan.": "从各 Agent 部署计划中解析的 Agent 声明。", + "Agent declarations parsed from each deployment plan.": "从各 Agent 部署计划中解析的 Agent 声明。", "Agent ID *": "代理 ID *", "Agentic development control plane": "智能体研发控制面", "Agents": "Agent", "Aggregated usage metrics and trend charts.": "聚合使用指标和趋势图表。", "aggregates 50+ AI providers behind one unified API. Manage access, track costs, and scale effortlessly.": "聚合 50+ AI 提供商于统一 API 之后。轻松管理访问、追踪成本、弹性扩展。", "Agile Minimal": "敏捷最小编队", - "Agnet allowed models comma": "Agnet 运行模型 ID(逗号分隔)", - "Agnet deployment created": "Agnet 部署已创建({{deployment_id}})", - "Agnet launch staged. Continue the task in the desktop client.": "已准备启动 Agnet,请回到客户端继续推进任务。", - "Agnet orchestration": "Agnet 编排", - "Agnet runtime model": "Agnet 运行模型", - "Agnet runtime model id": "Agnet 运行模型 ID", + "Agent allowed models comma": "Agent 运行模型 ID(逗号分隔)", + "Agent deployment created": "Agent 部署已创建({{deployment_id}})", + "Agent launch staged. Continue the task in the desktop client.": "已准备启动 Agent,请回到客户端继续推进任务。", + "Agent orchestration": "Agent 编排", + "Agent runtime model": "Agent 运行模型", + "Agent runtime model id": "Agent 运行模型 ID", "ago": "前", "AGPL v3.0 License": "AGPL v3.0 协议", "AI model testing environment": "AI模型测试环境", @@ -505,7 +505,7 @@ "Browse and compare": "浏览和比较", "budget": "预算", "Budget & usage": "预算与用量", - "Budget caps": "Agnet 运行上限", + "Budget caps": "Agent 运行上限", "Budget tokens = max tokens × ratio. Accepts a decimal between 0.002 and 1. Recommended to keep aligned with upstream billing.": "预算令牌 = 最大令牌数 × 比例。接受 0.002 到 1 之间的十进制数。建议与上游计费保持一致。", "Budget tokens = max tokens × ratio. Accepts a decimal between 0.1 and 1.": "预算令牌 = 最大令牌数 × 比例。接受 0.1 到 1 之间的十进制数。", "Budget Tokens Ratio": "预算令牌比例", @@ -548,7 +548,7 @@ "Cancelled": "已取消", "Cancelled at": "作废于", "Capture a reusable bundle of models, tags, or endpoints.": "捕获可重用的模型、标签或端点捆绑包。", - "Card-based view of every Agnet orchestration run with risk, budget, executor and live status.": "以卡片形式展示每次 Agnet 编排运行,包含风险、运行上限、资源作用域与实时状态。", + "Card-based view of every Agent orchestration run with risk, budget, executor and live status.": "以卡片形式展示每次 Agent 编排运行,包含风险、运行上限、资源作用域与实时状态。", "Category Name": "分类名称", "Category name is required": "分类名称不能为空", "Category name must be less than 50 characters": "分类名称不能超过 50 个字符", @@ -772,7 +772,7 @@ "Configure your account preferences and integrations": "配置您的账户偏好和集成", "Confirm": "确认", "Confirm Action": "确认操作", - "Confirm and launch Agnet": "确认并启动 Agnet", + "Confirm and launch Agent": "确认并启动 Agent", "Confirm Batch Update": "确认批量更新", "Confirm Billing Conflicts": "确认账单冲突", "Confirm Changes": "确认更改", @@ -791,7 +791,7 @@ "Confirm password": "确认密码", "Confirm Payment": "确认付款", "Confirm recommendation summary": "确认推荐摘要", - "Confirm scope, risk and budget before launching Agnet": "在启动 Agnet 之前,确认范围、风险和预算", + "Confirm scope, risk and budget before launching Agent": "在启动 Agent 之前,确认范围、风险和预算", "Confirm Selection": "确认选择", "Confirm settings and finish setup": "确认设置并完成安装", "Confirm Unbind": "确认解绑", @@ -885,8 +885,8 @@ "Create a new code delivery run with checks.": "创建新的带检查项的代码交付任务。", "Create a new user group to configure ratio overrides for.": "创建一个新的用户分组来配置比例覆盖。", "Create account": "创建账户", - "Create Agnet deployment": "创建 Agnet 部署", - "Create Agnet deployment description": "向 Agnet 提交编排计划。这里的模型选择是运行策略,不是计费配置。", + "Create Agent deployment": "创建 Agent 部署", + "Create Agent deployment description": "向 Agent 提交编排计划。这里的模型选择是运行策略,不是计费配置。", "Create an account": "创建一个账户", "Create and review invite or credit codes.": "创建和审查邀请或信用代码。", "Create API Key": "创建 API 密钥", @@ -1042,7 +1042,7 @@ "Deployments": "部署", "Desc": "描述", "Describe": "图生文", - "Describe the product, feature, or fix. Heicode will draft the task, recommend resources, and dispatch the Agnet team.": "用自然语言描述产品、功能或修复点。Heicode 会生成任务草案、推荐需要的资源,并调度 Agnet 团队执行。", + "Describe the product, feature, or fix. Heicode will draft the task, recommend resources, and dispatch the Agent team.": "用自然语言描述产品、功能或修复点。Heicode 会生成任务草案、推荐需要的资源,并调度 Agent 团队执行。", "Describe this model...": "描述此模型...", "Describe this vendor...": "描述此供应商...", "Describe what this work should achieve.": "描述这次工作要达成什么。", @@ -1183,7 +1183,7 @@ "e.g., us-central1 or JSON format for model-specific regions": "例如,us-central1 或模型特定区域的 JSON 格式", "e.g., v2.1": "例如,v2.1", "Each backup code can only be used once.": "每个备份代码只能使用一次。", - "Each card maps one child Agnet role to a runtime model and one bounded resource grant.": "每张卡把一个子 Agnet 角色映射到运行模型和一个受限资源授权。", + "Each card maps one child Agent role to a runtime model and one bounded resource grant.": "每张卡把一个子 Agent 角色映射到运行模型和一个受限资源授权。", "Each item must be an object with a single key-value pair.": "每个条目必须是包含单个键值对的对象。", "Each item must have exactly one key-value pair.": "每个条目必须恰好包含一个键值对。", "Each line represents one keyword. Leave blank to disable the list but keep the switch states.": "每行代表一个关键词。留空以禁用列表,但保留开关状态。", @@ -1679,8 +1679,8 @@ "Get Started": "开始使用", "Git binding": "Git 绑定", "Git sources": "Git 来源", - "Git sources binding explainer": "Skill(SK)定义以 Git 为唯一事实源。Manager 不在此编辑 Markdown:请在 Heicode 客户端或部署计划的 sk_sources 中登记仓库与引用;运行时与 SK 策略等参数在 Agnet 拉起编队/子 Agent 时传入。有效权限与策略落账在 Agnet 侧并由其执行;Manager 仅展示 Agnet 回传的不可变快照锚点(Git commit / 上传制品)供审计。", - "Git sources subtitle": "在部署参数中把绑定与策略交给 Agnet,在此按部署查看快照锚点(执行权在 Agnet)。", + "Git sources binding explainer": "Skill(SK)定义以 Git 为唯一事实源。Manager 不在此编辑 Markdown:请在 Heicode 客户端或部署计划的 sk_sources 中登记仓库与引用;运行时与 SK 策略等参数在 Agent 拉起编队/子 Agent 时传入。有效权限与策略落账在 Agent 侧并由其执行;Manager 仅展示 Agent 回传的不可变快照锚点(Git commit / 上传制品)供审计。", + "Git sources subtitle": "在部署参数中把绑定与策略交给 Agent,在此按部署查看快照锚点(执行权在 Agent)。", "Git sources workflow step 1": "绑定团队用于应用代码与交付上下文的 Git 仓库。", "Git sources workflow step 2": "绑定 SK 工具仓库(技能来源),声明本次运行可通过 Resource Grant 引用的工具集。", "Git sources workflow step 3": "为子 Agent 分配云上资源与权限(如独立虚拟机、角色与其他访问边界)。", @@ -1960,7 +1960,7 @@ "Last updated:": "上次更新时间:", "Last Used": "最后使用时间", "Last used:": "上次使用时间:", - "Launch Agnet": "启动 Agnet", + "Launch Agent": "启动 Agent", "Launching…": "正在准备启动…", "Layout": "布局", "Learn more": "了解更多", @@ -1980,7 +1980,7 @@ "Leave empty to use system temp directory": "留空使用系统临时目录", "Leave empty to use username": "留空以使用用户名", "Less": "更少", - "Lifecycle and policy events emitted by Agnet deployments. Correlate with deployment cards.": "Agnet 部署发出的生命周期与策略事件,可与部署卡片关联。", + "Lifecycle and policy events emitted by Agent deployments. Correlate with deployment cards.": "Agent 部署发出的生命周期与策略事件,可与部署卡片关联。", "Light": "浅色", "Lightning Fast": "极速", "Limit period": "限制周期", @@ -2025,7 +2025,7 @@ "Logo": "徽标", "Logo URL": "徽标 URL", "Logs": "日志", - "Long-lived credentials are stored in the secret vault. Agnet only requests short-lived, scoped credentials at run time. Production deploys and other high-risk actions are approved from the desktop client.": "长期凭证保存在密钥保管器。Agnet 执行时只申请短期、最小权限凭证。生产部署等高危操作需在客户端审批。", + "Long-lived credentials are stored in the secret vault. Agent only requests short-lived, scoped credentials at run time. Production deploys and other high-risk actions are approved from the desktop client.": "长期凭证保存在密钥保管器。Agent 执行时只申请短期、最小权限凭证。生产部署等高危操作需在客户端审批。", "m": "分钟", "Maintain a list of common questions for the dashboard help panel": "维护仪表板帮助面板的常见问题列表", "Maintenance": "维护", @@ -2385,7 +2385,7 @@ "No Sync": "不同步", "No system announcements": "暂无系统公告", "No tasks yet": "暂无任务", - "No tasks yet. Capture an idea above, then confirm the recommendation in the desktop client to launch Agnet.": "暂无任务。在上方暂存一个想法,然后在客户端确认推荐摘要,启动 Agnet。", + "No tasks yet. Capture an idea above, then confirm the recommendation in the desktop client to launch Agent.": "暂无任务。在上方暂存一个想法,然后在客户端确认推荐摘要,启动 Agent。", "No token found.": "未找到令牌。", "No tools configured": "未配置工具", "No Upgrade": "不升级", @@ -3043,7 +3043,7 @@ "Resources workflow step 1": "绑定项目代码、SK、文档或云资源 metadata,不暴露原始凭据。", "Resources workflow step 2": "密钥材料留在 secret_ref 后面;Manager 只展示引用和状态。", "Resources workflow step 3": "为运行 manifest 分配范围、允许路径/动作、运行策略和预算。", - "Resources workflow step 4": "启动 Work/Run;Agnet 解析不可变锚点并执行有效授权。", + "Resources workflow step 4": "启动 Work/Run;Agent 解析不可变锚点并执行有效授权。", "Resources workflow step 5": "结合快照、事件和审计回放实际运行的资源上下文。", "Resources workflow title": "资源到运行流程", "Response": "响应", @@ -3299,7 +3299,7 @@ "Sidebar Personal Settings": "左侧边栏个人设置", "Sign in": "登录", "Sign In": "登录", - "Sign in to operate Agnet deployments, inspect events, and audit SK snapshots for every tenant under your account.": "登录后即可操作 Agnet 部署、查看事件,并审计你账号下各租户的 SK 快照。", + "Sign in to operate Agent deployments, inspect events, and audit SK snapshots for every tenant under your account.": "登录后即可操作 Agent 部署、查看事件,并审计你账号下各租户的 SK 快照。", "Sign in to operate deployments and review Git-bound snapshot anchors for tenants under your account.": "登录后即可绑定仓库与云上权限、部署子 Agent,并审阅你账号下各租户与 Git 绑定的快照锚点。", "Sign in to your workspace": "登录到你的工作空间", "Sign in with Passkey": "使用 Passkey 登录", @@ -3368,10 +3368,10 @@ "status": "状态", "Status & Sync": "状态与同步", "Status Code Mapping": "状态码映射", - "Status of every Agnet task you launched": "查看你启动的每个 Agnet 任务的状态", + "Status of every Agent task you launched": "查看你启动的每个 Agent 任务的状态", "Status Page Slug": "状态页面 Slug", "Status, errors and budget burn in one auditable stream.": "状态、错误与预算消耗汇聚在一条可审计的流里。", - "Status, latest activity and last update for every Agnet task you launched. Details live in the desktop client.": "查看每个启动的 Agnet 任务的状态、最近动态和更新时间。详细对话与产物在客户端中查看。", + "Status, latest activity and last update for every Agent task you launched. Details live in the desktop client.": "查看每个启动的 Agent 任务的状态、最近动态和更新时间。详细对话与产物在客户端中查看。", "Status:": "状态:", "Stay": "留下来", "Stay tuned though!": "敬请期待!", @@ -3484,7 +3484,7 @@ "Task Logs": "任务日志", "Task not found. It may have been removed or was never created.": "任务不存在,可能已被删除或从未创建。", "Task overview": "任务总览", - "Tasks appear here after you confirm the recommendation in the desktop client and launch Agnet.": "在客户端确认推荐摘要并启动 Agnet 后,任务会出现在这里。", + "Tasks appear here after you confirm the recommendation in the desktop client and launch Agent.": "在客户端确认推荐摘要并启动 Agent 后,任务会出现在这里。", "Runtime diagnostics": "运行时诊断", "Manager checks Runtime status separately from callback data, without mixing ordinary sub and swarm modes.": "Manager 会独立检查运行时状态,并明确区分普通 sub 与蜂群模式,不混用回调数据。", "runtime mode": "运行模式", @@ -3685,7 +3685,7 @@ "Total:": "总计:", "TPM": "TPM", "Trace delivery context snapshots by hash.": "按哈希追溯交付上下文快照。", - "Track every Agnet work run by status, risk, budget, scope and secret_ref coverage.": "按状态、风险、预算、作用域和 secret_ref 覆盖情况跟踪每次 Agnet 工作运行。", + "Track every Agent work run by status, risk, budget, scope and secret_ref coverage.": "按状态、风险、预算、作用域和 secret_ref 覆盖情况跟踪每次 Agent 工作运行。", "Track per-request consumption to power usage analytics. Keeping this on increases database writes.": "跟踪每个请求的消耗,以支持使用情况分析。保持开启会增加数据库写入。", "Track usage, costs and performance with real-time analytics": "通过实时分析跟踪用量、成本和性能", "Tracks current account base limits and additional metered usage on Codex upstream.": "跟踪当前账号在 Codex 上游的基础限额与附加计费用量。", @@ -4139,11 +4139,11 @@ "Reviewer role": "评审", "Ops role": "运维", "Builder role": "构建", - "Product Agnet": "产品智能体", - "Frontend Agnet": "前端智能体", - "Backend Agnet": "后端智能体", - "Reviewer Agnet": "评审智能体", - "Ops Agnet": "运维智能体", + "Product Agent": "产品智能体", + "Frontend Agent": "前端智能体", + "Backend Agent": "后端智能体", + "Reviewer Agent": "评审智能体", + "Ops Agent": "运维智能体", "Git repository": "Git 仓库", "SK skill pack": "SK 技能包", "Cloud account": "云账号", @@ -4179,7 +4179,7 @@ "All levels": "全部级别", "Info": "信息", "Active credential leases": "有效凭证租约", - "Approve or reject high-risk Agnet operations before credentials are leased.": "在下发凭证租约前,审批或拒绝高危 Agnet 操作。", + "Approve or reject high-risk Agent operations before credentials are leased.": "在下发凭证租约前,审批或拒绝高危 Agent 操作。", "No pending approvals": "暂无待审批项", "High-risk operations will appear here.": "高危操作会显示在这里。", "No active credential leases": "暂无有效凭证租约", @@ -4195,14 +4195,14 @@ "Approved from Manager audit page": "从管理端审计页批准", "Rejected from Manager audit page": "从管理端审计页拒绝", "Revoked from Manager audit page": "从管理端审计页撤销", - "Connect code, docs and cloud resources for the current task, then confirm the recommendation before launching Agnet.": "为当前任务绑定代码、文档和云资源,然后确认推荐方案再启动 Agnet。", + "Connect code, docs and cloud resources for the current task, then confirm the recommendation before launching Agent.": "为当前任务绑定代码、文档和云资源,然后确认推荐方案再启动 Agent。", "Plaintext credentials are never shown. The secret_ref column is a vault pointer, not the secret itself.": "永远不展示明文密钥。表中的密钥引用只是密钥保管器里的指针,不是密钥本体。", "Confirm before launch": "确认后启动", "Heicode summarises what this run will do, what resources it can use, what stays off-limits, and the expected cost. Confirm to launch.": "Heicode 已汇总本次会做什么、能用哪些资源、哪些事情不会做、以及预计消耗。请确认后启动。", "This run will do": "本次会做", "No objective provided yet — go back to the Idea tab.": "还没填写目标 — 请回到「想法」标签页补充。", "Resources this run may use": "本次允许使用的资源", - "No resources bound yet — Agnet will run with no external data access.": "还没绑定资源 — Agnet 将在无外部数据访问的情况下运行。", + "No resources bound yet — Agent will run with no external data access.": "还没绑定资源 — Agent 将在无外部数据访问的情况下运行。", "no actions specified": "未指定动作", "This run will NOT do": "本次不会做", "Production deploys without client approval": "未经客户端审批的生产部署", @@ -4233,7 +4233,7 @@ "No audit events yet for this deployment.": "该部署暂无审计事件。", "Connect project docs": "绑定项目文档", "{{n}} doc source connected": "已绑定 {{n}} 份项目文档", - "Link product requirements, design docs or wiki repos so Agnet has project context.": "绑定需求文档、设计文档或 wiki 仓库,让 Agnet 拿到项目上下文。", + "Link product requirements, design docs or wiki repos so Agent has project context.": "绑定需求文档、设计文档或 wiki 仓库,让 Agent 拿到项目上下文。", "Connect SK skill packs": "绑定 SK 技能包", "{{n}} SK source connected": "已绑定 {{n}} 个 SK 来源", "Pick a reusable skill / agent toolset repository, or skip.": "选一个可复用的技能包 / Agent 工具集仓库,也可以跳过。", diff --git a/heicode/web/default/src/lib/api.ts b/heicode/web/default/src/lib/api.ts index 424270b..d2ed9ec 100644 --- a/heicode/web/default/src/lib/api.ts +++ b/heicode/web/default/src/lib/api.ts @@ -79,7 +79,7 @@ api.interceptors.response.use( const url = String(error?.config?.url || '') // Only treat 401 on identity/self endpoints as a real session expiry. - // Admin-only endpoints (/api/agnet, /api/channel, etc.) returning 401 for + // Admin-only endpoints (/api/agent, /api/channel, etc.) returning 401 for // non-admin users should NOT reset the session. const isIdentityEndpoint = url.includes('/api/user/self') || diff --git a/heicode/web/default/src/lib/heicode-mcp.ts b/heicode/web/default/src/lib/heicode-mcp.ts index abf0596..26f1c4f 100644 --- a/heicode/web/default/src/lib/heicode-mcp.ts +++ b/heicode/web/default/src/lib/heicode-mcp.ts @@ -15,12 +15,12 @@ import { api } from '@/lib/api' * §2 /api/resources/* ResourceBinding (5) * §3 /api/resource-grants/* ResourceGrant (4) * §4 /api/user/heicode/* NewAPI metadata passthrough (4) - * §5 /api/agnet/* Agnet platform stub (12 mock endpoints) + * §5 /api/agent/* Agent platform stub (12 mock endpoints) * §6 /api/user/tasks/* HeicodeTask orchestration (5) * * The shapes below are what mcp-server actually returns — they intentionally - * differ from the Heicode-local /api/agnet/* shapes used in earlier UI work. - * That earlier work treated AgnetDeployment as the user-facing task object; + * differ from the Heicode-local /api/agent/* shapes used in earlier UI work. + * That earlier work treated AgentDeployment as the user-facing task object; * the contract document is clear that HeicodeTask (§6) is the right object. */ @@ -227,7 +227,7 @@ export async function createDeploymentDraftFromHeicodeTask( options: HeicodeTaskDeploymentDraftOptions = {} ): Promise { const res = await api.post>( - `/api/agnet/user/tasks/${encodeURIComponent(task.id)}/deployment-draft`, + `/api/agent/user/tasks/${encodeURIComponent(task.id)}/deployment-draft`, { task, sub_mode: options.sub_mode ?? 'agile', @@ -339,10 +339,10 @@ export async function getHeicodeLogs( } // ============================================================================= -// §5 Agnet stub — only the endpoints UI needs. +// §5 Agent stub — only the endpoints UI needs. // ============================================================================= -export type McpAgnetDeployment = { +export type McpAgentDeployment = { deployment_id: string status: string phase?: string @@ -360,11 +360,11 @@ export type McpAgnetDeployment = { } } -export async function listMcpAgnetDeployments(params?: { +export async function listMcpAgentDeployments(params?: { status?: string limit?: number binding_scope?: string -}): Promise { +}): Promise { const qs = new URLSearchParams() if (params?.status) qs.set('status', params.status) if (params?.limit != null) qs.set('limit', String(params.limit)) @@ -372,8 +372,8 @@ export async function listMcpAgnetDeployments(params?: { const suffix = qs.toString() ? `?${qs.toString()}` : '' try { const env = await mcpFetch< - Envelope<{ items?: McpAgnetDeployment[]; total?: number }> - >(`/api/agnet/deployments${suffix}`) + Envelope<{ items?: McpAgentDeployment[]; total?: number }> + >(`/api/agent/deployments${suffix}`) return env.data?.items ?? [] } catch { return [] @@ -639,7 +639,7 @@ export async function listMcpAuditLogs(params?: { try { const env = await mcpFetch< Envelope<{ items?: McpAuditEntry[]; total?: number }> - >(`/api/agnet/audit-logs${suffix}`) + >(`/api/agent/audit-logs${suffix}`) return env.data?.items ?? [] } catch { return [] diff --git a/heicode/web/default/src/routes/_authenticated/agents/index.tsx b/heicode/web/default/src/routes/_authenticated/agents/index.tsx index c1d87dd..ec7d626 100644 --- a/heicode/web/default/src/routes/_authenticated/agents/index.tsx +++ b/heicode/web/default/src/routes/_authenticated/agents/index.tsx @@ -1,6 +1,6 @@ import { createFileRoute } from '@tanstack/react-router' -import { AgnetAgentsPage } from '@/features/agnet-console/pages' +import { AgentAgentsPage } from '@/features/agent-console/pages' export const Route = createFileRoute('/_authenticated/agents/')({ - component: AgnetAgentsPage, + component: AgentAgentsPage, }) diff --git a/heicode/web/default/src/routes/_authenticated/audit/index.tsx b/heicode/web/default/src/routes/_authenticated/audit/index.tsx index 29813be..bc85807 100644 --- a/heicode/web/default/src/routes/_authenticated/audit/index.tsx +++ b/heicode/web/default/src/routes/_authenticated/audit/index.tsx @@ -1,6 +1,6 @@ import { createFileRoute } from '@tanstack/react-router' -import { AgnetAuditPage } from '@/features/agnet-console/pages' +import { AgentAuditPage } from '@/features/agent-console/pages' export const Route = createFileRoute('/_authenticated/audit/')({ - component: AgnetAuditPage, + component: AgentAuditPage, }) diff --git a/heicode/web/default/src/routes/_authenticated/deployments/index.tsx b/heicode/web/default/src/routes/_authenticated/deployments/index.tsx index 855cf3a..8bd7f66 100644 --- a/heicode/web/default/src/routes/_authenticated/deployments/index.tsx +++ b/heicode/web/default/src/routes/_authenticated/deployments/index.tsx @@ -1,6 +1,6 @@ import { createFileRoute } from '@tanstack/react-router' -import { AgnetDeploymentsPage } from '@/features/agnet-console/pages' +import { AgentDeploymentsPage } from '@/features/agent-console/pages' export const Route = createFileRoute('/_authenticated/deployments/')({ - component: AgnetDeploymentsPage, + component: AgentDeploymentsPage, }) diff --git a/heicode/web/default/src/routes/_authenticated/events/index.tsx b/heicode/web/default/src/routes/_authenticated/events/index.tsx index 55bcdc8..286587d 100644 --- a/heicode/web/default/src/routes/_authenticated/events/index.tsx +++ b/heicode/web/default/src/routes/_authenticated/events/index.tsx @@ -1,6 +1,6 @@ import { createFileRoute } from '@tanstack/react-router' -import { AgnetEventsPage } from '@/features/agnet-console/pages' +import { AgentEventsPage } from '@/features/agent-console/pages' export const Route = createFileRoute('/_authenticated/events/')({ - component: AgnetEventsPage, + component: AgentEventsPage, }) diff --git a/heicode/web/default/src/routes/_authenticated/sk-sources/index.tsx b/heicode/web/default/src/routes/_authenticated/sk-sources/index.tsx index c5d1679..4e972eb 100644 --- a/heicode/web/default/src/routes/_authenticated/sk-sources/index.tsx +++ b/heicode/web/default/src/routes/_authenticated/sk-sources/index.tsx @@ -1,6 +1,6 @@ import { createFileRoute } from '@tanstack/react-router' -import { AgnetSKSourcesPage } from '@/features/agnet-console/pages' +import { AgentSKSourcesPage } from '@/features/agent-console/pages' export const Route = createFileRoute('/_authenticated/sk-sources/')({ - component: AgnetSKSourcesPage, + component: AgentSKSourcesPage, }) diff --git a/heicode/web/default/src/routes/_authenticated/templates/index.tsx b/heicode/web/default/src/routes/_authenticated/templates/index.tsx index 7930696..d7614a2 100644 --- a/heicode/web/default/src/routes/_authenticated/templates/index.tsx +++ b/heicode/web/default/src/routes/_authenticated/templates/index.tsx @@ -1,6 +1,6 @@ import { createFileRoute } from '@tanstack/react-router' -import { AgnetTemplatesPage } from '@/features/agnet-console/pages' +import { AgentTemplatesPage } from '@/features/agent-console/pages' export const Route = createFileRoute('/_authenticated/templates/')({ - component: AgnetTemplatesPage, + component: AgentTemplatesPage, }) diff --git a/heicode/web/default/src/styles/tokens.css b/heicode/web/default/src/styles/tokens.css index 5f60b5d..ef100bb 100644 --- a/heicode/web/default/src/styles/tokens.css +++ b/heicode/web/default/src/styles/tokens.css @@ -2,7 +2,7 @@ * Heicode Manager design tokens * * One source of truth for radii, spacing, elevations, semantic colors and - * surface treatments used by the Agnet command-center UI. Pages and components + * surface treatments used by the Agent command-center UI. Pages and components * should reference these tokens (via Tailwind or CSS variables) instead of * declaring ad-hoc values. * ==========================================================================*/ diff --git a/scripts/agnet_sub_mode_smoke.py b/scripts/agent_sub_mode_smoke.py similarity index 93% rename from scripts/agnet_sub_mode_smoke.py rename to scripts/agent_sub_mode_smoke.py index 6963520..532ad6e 100755 --- a/scripts/agnet_sub_mode_smoke.py +++ b/scripts/agent_sub_mode_smoke.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 """ -Smoke test for Heicode Manager ordinary sub-mode / Agnet integration. +Smoke test for Heicode Manager ordinary sub-mode / Agent integration. Default mode checks public health/status endpoints only. To validate an authenticated deployment flow, provide: @@ -15,7 +15,7 @@ Optional: HEICODE_CALLBACK_TOKEN= HEICODE_CALLBACK_SWARM_ID= HEICODE_MANAGER_BASE_URL=https://code.xinghanlab.com - AGENT_MANAGER_HEALTH_URL=http://20.212.121.126/api/agnet/health + AGENT_MANAGER_HEALTH_URL=http://20.212.121.126/api/agent/health """ from __future__ import annotations @@ -29,7 +29,7 @@ import urllib.request BASE = os.getenv("HEICODE_MANAGER_BASE_URL", "https://code.xinghanlab.com").rstrip("/") AGENT_HEALTH = os.getenv( - "AGENT_MANAGER_HEALTH_URL", "http://20.212.121.126/api/agnet/health" + "AGENT_MANAGER_HEALTH_URL", "http://20.212.121.126/api/agent/health" ) ACCESS_TOKEN = os.getenv("HEICODE_ACCESS_TOKEN", "").strip() USER_ID = os.getenv("HEICODE_USER_ID", "").strip() @@ -46,7 +46,7 @@ RUN_CALLBACK_SMOKE = os.getenv("HEICODE_RUN_CALLBACK_SMOKE", "").strip().lower() "true", "yes", } -UA = "heicode-agnet-sub-mode-smoke/1.0" +UA = "heicode-agent-sub-mode-smoke/1.0" FAILS: list[str] = [] @@ -103,7 +103,7 @@ check("Manager /api/status success", code == 200 and status.get("success") is Tr version = status.get("data", {}).get("version") if isinstance(status.get("data"), dict) else "" check("Manager status carries version", bool(version), f"version={version!r}") -code, _headers, body = manager_request("GET", "/api/agnet/callbacks/swarm-events/schema") +code, _headers, body = manager_request("GET", "/api/agent/callbacks/swarm-events/schema") schema = parse_json("callback schema JSON parseable", body) schema_data = schema.get("data", {}) if isinstance(schema.get("data"), dict) else {} schema_events = schema_data.get("events") or [] @@ -139,7 +139,7 @@ else: else: callback_headers = { "Content-Type": "application/json", - "X-Agnet-Service-Token": CALLBACK_TOKEN, + "X-Agent-Service-Token": CALLBACK_TOKEN, "X-Correlation-ID": f"smoke-corr-{DEPLOYMENT_ID}", } swarm_id = CALLBACK_SWARM_ID or f"smoke-swarm-{DEPLOYMENT_ID}" @@ -202,10 +202,10 @@ else: ] for event in callback_events: body_bytes = json.dumps(event).encode("utf-8") - callback_headers["X-Agnet-Event-Id"] = event["event_id"] + callback_headers["X-Agent-Event-Id"] = event["event_id"] code, _headers, body = request( "POST", - BASE + "/api/agnet/callbacks/swarm-events", + BASE + "/api/agent/callbacks/swarm-events", headers=callback_headers, body=body_bytes, ) @@ -221,7 +221,7 @@ else: if RUN_SIMULATE: code, _headers, body = manager_request( "POST", - f"/api/agnet/user/deployments/{DEPLOYMENT_ID}/simulate-events", + f"/api/agent/user/deployments/{DEPLOYMENT_ID}/simulate-events", auth=True, payload={}, ) @@ -233,7 +233,7 @@ else: ) code, _headers, body = manager_request( - "GET", f"/api/agnet/user/deployments/{DEPLOYMENT_ID}", auth=True + "GET", f"/api/agent/user/deployments/{DEPLOYMENT_ID}", auth=True ) detail = parse_json("deployment detail JSON parseable", body) detail_data = detail.get("data", {}) if isinstance(detail.get("data"), dict) else {} @@ -244,7 +244,7 @@ else: ) code, _headers, body = manager_request( - "GET", f"/api/agnet/user/deployments/{DEPLOYMENT_ID}/artifacts", auth=True + "GET", f"/api/agent/user/deployments/{DEPLOYMENT_ID}/artifacts", auth=True ) artifacts = parse_json("artifacts JSON parseable", body) artifacts_data = artifacts.get("data", {}) if isinstance(artifacts.get("data"), dict) else {} @@ -256,7 +256,7 @@ else: ) code, _headers, body = manager_request( - "GET", f"/api/agnet/user/deployments/{DEPLOYMENT_ID}/timeline", auth=True + "GET", f"/api/agent/user/deployments/{DEPLOYMENT_ID}/timeline", auth=True ) timeline = parse_json("timeline JSON parseable", body) timeline_data = timeline.get("data", {}) if isinstance(timeline.get("data"), dict) else {} @@ -309,4 +309,4 @@ if FAILS: print(f" - {item}") sys.exit(1) -print("\nALL AGNET SUB-MODE SMOKE CHECKS PASSED") +print("\nALL AGENT SUB-MODE SMOKE CHECKS PASSED")