- Add 2026-06-02 changelog summarizing P0/P1 closure at the top. - New error codes: ARTIFACT_ARCHIVE_NOT_READY/_FAILED, FILE_PATH_REQUIRED, with retryable column and the unified error envelope note. - §9 auth bullet now reflects encrypted-body writes + no-body signed GET. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
21 KiB
Heicode 桌面客户端统一接口对接文档(v0.1)
更新时间:2026-06-02
适用范围:Heicode Desktop 对接 Heicode Manager 的统一接口层(Sub Agile / Swarm 两模式)。
Manager 生产地址:https://code.xinghanlab.com
基准:统一调用方案 v0.1 + agent_management Sub Mode Runtime 对接指南。
本文取代旧的
heicode-desktop-sub-agile-api.md(其/api/agnet/*路由已废弃)。新客户端一律使用本文的/api/heicode/*与/api/agent/*接口。
2026-06-02 更新(已对照客户端 gap-analysis 闭环 P0/P1 并在运行时实跑验证):
- GET 全程无 cookie 的「无 body 设备签名」(§1,P0-1);
- 审批列表
GET .../approvals?status=(§3.2,P0-2);- 产物列表归一化
display_artifact_type=project_folder+is_project(§5,P0-3);/archive下载契约(zip 头 +ARTIFACT_ARCHIVE_NOT_READY,§5,P0-4);- 单文件改
?path=编码(§5,P1-1);- revision
accepted/applied语义 +/messages·/execute自动用最新 accepted(§6,P1-2/P1-3);- Swarm 全量同形显式声明(§3,P1-4)。
0. 命名与总原则
- 命名统一:
agnet是历史拼写错误,已全量改为agent。所有新接口为/api/agent/*、/api/heicode/*;旧/api/agnet/*已下线(404)。 - 客户端只调 Manager:禁止直连 agent_management / HeiCode-Swarm / NewAPI / Runtime artifact 接口。
- Manager 是唯一状态裁判:客户端只消费
display_status,不自行用正则判断产物是否有效。 - task_id ≡ deployment_id:统一任务接口里
{task_id}即 Manager 的deployment_id。
0.1 推荐完整流程
1. GET /api/heicode/capabilities 取模式/模型,渲染模式与模型选择
2. POST /api/heicode/sub-agile/tasks (需求包) 创建任务 -> 响应里拿 deployment_id(=task_id)
3. (可选) POST .../tasks/{id}/execute 若需"先建后跑"或重试派发
4. 轮询 GET .../tasks/{id}/workflow (运行中 3-5s) 读 display_status + 三层状态 + agents
5. 出现 approval 时: GET .../approvals?status=pending -> approve/reject
6. display_status=completed 后:
GET .../tasks/{id}/artifacts 拿 artifact_id
GET .../artifacts/{artifact_id}/manifest 项目文件树
GET .../artifacts/{artifact_id}/files?path=<路径> 单文件正文
GET .../artifacts/{artifact_id}/archive 整包 zip
7. (可选) 用户本地改后: POST .../artifacts/{id}/local-edits
8. (可选) 部署: GET /api/heicode/deployment-targets -> POST .../tasks/{id}/deployments
成功判据:
display_status=completed(不是needs_codegen/completed_without_deliverable)且 artifacts 非空。
1. 认证与加密(重要)
桌面客户端调用本文所有 /api/heicode/* 接口,body 加密与签名和模型调用(/v1/*)完全一致——直接复用同一套 encryptedFetch / V2 设备签名实现,无需新增协议。
| 方式 | 适用 |
|---|---|
V2 加密 body + 设备签名(Content-Encoding: heicode-aead-v1 + X-Heicode-*,ChaCha20-Poly1305 + Ed25519) |
有 body 的请求(POST/PUT/DELETE) |
V2 无 body 设备签名(X-Heicode-* 签名头,不带 Content-Encoding) |
GET 等无 body 请求(fetch 规范禁止 GET 带 body) |
Manager session cookie + New-Api-User: <user_id> |
Web 控制台兼容路径(桌面端可不用) |
要点(与模型调用相同,未变):
aad/canonical里的path_with_query必须是 Manager 实际收到的 path,例如/api/heicode/sub-agile/tasks,不含 origin。- 每次新
X-Heicode-Nonce+ 新 X25519 临时 key;签名里sha256_hex(plaintext_body)与加密前 JSON 字节一致。 - 服务端在新路由上用同一中间件
UserOrV2DeviceAuth解密+验签,从设备绑定 token 解析用户身份;失败读响应头X-Heicode-Auth-Error。
GET 全程无 cookie 的设备签名(已支持):GET 没有 body,因此不发 Content-Encoding、不加密,但仍发同一套签名头并用相同 canonical 公式签名,sha256_hex(plaintext_body) 处填空 body 的哈希 sha256(""):
canonical = method + "\n" # "GET"
+ path_with_query + "\n" # 例如 /api/heicode/sub-agile/tasks/dep_x/workflow
+ timestamp_ms + "\n"
+ nonce_hex + "\n"
+ device_fingerprint + "\n"
+ ephemeral_pubkey_b64 + "\n" # GET 仍生成一对临时 X25519 key 并带 X-Heicode-Eph-Pubkey
+ sha256_hex("") # e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
signature = base64( ed25519_sign(device_priv, sha256(canonical)) )
即:GET 与写请求共用同一签名实现,区别仅是「不加密 body、body 哈希为空串哈希」。服务端凭 X-Heicode-Signature + X-Heicode-Device-Id + X-Heicode-Eph-Pubkey 三个头识别该模式(gap P0-1 已闭环)。客户端可用同一 encryptedFetch,对 GET 走「跳过加密、空 body」分支即可。
2. 能力发现
GET /api/heicode/capabilities
无需登录,返回模式与模型目录。
{ "success": true, "data": {
"modes": [
{"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":true},
{"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":false}
],
"models": [{"id":"gpt-5.4","name":"gpt-5.4","available":true}]
}}
enabled 反映该模式 Runtime 当前是否接通。
3. 统一任务接口
两套前缀,按模式选择:/api/heicode/sub-agile/*(→ agent_management)、/api/heicode/swarm/*(→ HeiCode-Swarm)。下表以 sub-agile 为例,swarm 路径把前缀 sub-agile 换成 swarm 后完全同形(含 artifacts/files/archive/revisions/local-edits/approvals/deployments 全部子接口,gap P1-4)——两组路由由同一注册函数生成,不存在「只实现了 sub-agile」的情况。
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /api/heicode/sub-agile/tasks |
创建任务(body = 需求包,见 §3.1) |
| GET | /api/heicode/sub-agile/tasks |
任务列表 |
| GET | /api/heicode/sub-agile/tasks/{task_id} |
任务详情(含 display_status,读取时自动从 Runtime 收敛) |
| POST | /api/heicode/sub-agile/tasks/{task_id}/messages |
持续对话:追加用户消息 |
| POST | /api/heicode/sub-agile/tasks/{task_id}/execute |
触发/确保执行(未派发则派发 Runtime) |
| DELETE | /api/heicode/sub-agile/tasks/{task_id} |
删除/停止任务 |
| GET | /api/heicode/sub-agile/tasks/{task_id}/workflow |
工作流投影(右侧面板:status/agents/artifacts/phase) |
| POST | /api/heicode/sub-agile/tasks/{task_id}/stop |
停止 |
| GET | /api/heicode/sub-agile/tasks/{task_id}/timeline |
时间线(events/callbacks/artifacts 聚合) |
| GET | /api/heicode/sub-agile/tasks/{task_id}/logs |
日志(含 user_logs 友好 + debug_logs 原始两层) |
| GET | /api/heicode/sub-agile/tasks/{task_id}/events |
事件 |
| GET | /api/heicode/sub-agile/tasks/{task_id}/metrics |
指标 |
| GET | /api/heicode/sub-agile/tasks/{task_id}/diagnostics |
诊断(直查 Runtime 状态 + warnings) |
| GET | /api/heicode/sub-agile/tasks/{task_id}/sk-snapshots |
SK 快照 |
| GET | /api/heicode/sub-agile/tasks/{task_id}/artifacts |
产物列表(主产物归一化为 project_folder,见 §5) |
| GET | /api/heicode/sub-agile/tasks/{task_id}/artifacts/{artifact_id}/manifest |
项目文件树 |
| GET | /api/heicode/sub-agile/tasks/{task_id}/artifacts/{artifact_id}/files?path= |
单文件正文 |
| GET | /api/heicode/sub-agile/tasks/{task_id}/artifacts/{artifact_id}/archive |
整包 zip |
| GET | /api/heicode/sub-agile/tasks/{task_id}/artifacts/{artifact_id}/revisions |
revision 列表 |
| POST | /api/heicode/sub-agile/tasks/{task_id}/artifacts/{artifact_id}/local-edits |
本地修改回传(批量 .../local-edits/batch) |
| GET | /api/heicode/sub-agile/tasks/{task_id}/approvals?status=pending |
审批列表(gap P0-2) |
| POST | /api/heicode/sub-agile/tasks/{task_id}/approvals/{approval_id}/approve |
同意审批 |
| POST | /api/heicode/sub-agile/tasks/{task_id}/approvals/{approval_id}/reject |
拒绝审批 |
| GET | /api/heicode/sub-agile/tasks/{task_id}/deployments · POST 同路径 |
云部署列表 / 发起(见 §7) |
3.1 创建 body(需求包)
客户端提交需求包,不提交 Runtime 原始参数;Manager 内部翻译成 orchestration_plan 并创建。
{
"mode": "sub_agile",
"client_role": "main_agent",
"conversation_id": "conv_xxx",
"requirement": {
"objective": "做一个 Oracle 云代理商网站,前后端分离",
"context": [], "attachments": [], "constraints": [], "acceptance_criteria": []
},
"model_selection": {
"type": "per_role",
"default_model": "gpt-5.4",
"roles": {"backend":"gpt-5.4","frontend":"gpt-5.4"}
},
"roles": ["backend", "frontend"]
}
model_selection.type:default(全局default_model)/per_role(roles映射)/primary(Swarm 用primary_model)。conversation_id作为任务关联键(落到correlation_id)。- 模型必须用生产 NewAPI 已存在模型(当前推荐
gpt-5.4)。 - 兼容:也接受直接传
{ "orchestration_plan": {...} }(高级用法)。
/messages body:{"message":"继续把测试补上","role":"user"}。
/execute body 可空;用于"创建后再触发"或重试派发。
创建响应(deployment_id 即后续所有 {task_id}):
{ "success": true, "message": "", "data": {
"deployment_id": "dep_b5fab27e9255",
"sub_mode": "agile",
"status": "accepted",
"display_status": "accepted",
"runtime_state": "runtime_syncing",
"runtime_swarm_id": "",
"agent_instances": [
{"instance_id":"agi_xxx","role":"backend","phase":"pending","runtime_state":"queued","failure_reason":""}
],
"permission_manifest": {"user_id":"22","binding_scope":"","resource_grants":[]}
}}
任务详情 GET .../tasks/{task_id} 返回同形 + 完整 orchestration_plan;任务列表 GET .../tasks 返回 {"items":[ ...同上... ],"total":N}。客户端按 display_status 渲染,详情/列表读取时 Manager 会自动从 Runtime 收敛终态。
3.2 审批列表(gap P0-2)
GET .../tasks/{task_id}/approvals?status=pending:
{ "success": true, "data": {
"task_id":"dep_xxx","total":1,
"items":[
{"approval_id":"appr_xxx","deployment_id":"dep_xxx","type":"deploy","risk_level":"high",
"title":"生产部署审批","status":"pending","created_at":"2026-06-02T00:00:00Z"}
]
}}
status 省略则返回全部;对每条 pending 项调 .../approvals/{approval_id}/approve|reject 闭环。
4. 状态模型(display_status)
客户端只展示 display_status(Manager 裁决结果):
| display_status | 含义 |
|---|---|
accepted / running / waiting_approval |
进行中 |
completed |
完成且有有效交付物 |
completed_without_deliverable |
Runtime 完成但无产物 |
needs_codegen |
只有方案/总结,需继续生成代码 |
failed / stopped |
终态 |
裁决规则:completed 且存在非兜底(metadata.synthesized!=true 且非纯总结)的真实 artifact → completed;否则降级为 needs_codegen(有总结)或 completed_without_deliverable(无产物)。
4.1 三层状态(GET .../workflow)
GET /api/heicode/sub-agile/tasks/{task_id}/workflow 返回客户端右侧面板需要的三层状态对象:
{ "success": true, "data": {
"task_id":"dep_xxx","conversation_id":"conv_xxx","mode":"agile",
"cloud_deployment_status":"completed", // Manager 控制面状态
"runtime_execution_status":"completed", // Runtime 上报状态
"display_status":"completed", // 唯一展示状态(客户端只看这个)
"last_synced_at":"2026-06-02T...",
"title":"...","summary":"...","phase":"development","agent_count":1,
"agents":[{"agent_id":"agi_x","name":"backend","role":"backend","status":"completed"}],
"artifacts":[{"artifact_id":"art_x","title":"...","artifact_type":"code_patch"}]
}}
client_task_status(本地交互态)由客户端自己维护,不由 Manager 返回。
4b. 响应 envelope 与实时刷新
成功:{ "success": true, "message": "", "data": {...} }
失败:{ "success": false, "message": "...", "error": { "code": "...", "message": "...", "retryable": false, "request_id": "..." } }
task_id / deployment_id / conversation_id / correlation_id 在各接口的 data 内返回(无独立 trace 字段)。
实时刷新:当前未提供 SSE;客户端按统一方案 §17.3 兜底用轮询刷新:
- 运行中:每 3–5 秒
GET .../workflow(或.../timeline); - 终态:降到 15–30 秒或停止。
不要订阅 .../events/stream(未提供,会 404)。
5. 产物:项目文件夹
Runtime 返回单个文本/markdown 多文件 artifact;Manager 解析成项目文件树后提供。
产物列表 GET .../tasks/{task_id}/artifacts:
{ "success": true, "data": {
"deployment_id":"dep_xxx","task_id":"dep_xxx","total":1,
"items":[
{"artifact_id":"art_swm_xxx_backend_1","artifact_type":"code_patch","title":"backend task delivery",
"summary":"...","uri":"azblob://...",
"is_project":true,"display_artifact_type":"project_folder",
"manifest_path":".../artifacts/art_swm_xxx_backend_1/manifest",
"files_path":".../artifacts/art_swm_xxx_backend_1/files",
"archive_path":".../artifacts/art_swm_xxx_backend_1/archive",
"revisions_path":".../artifacts/art_swm_xxx_backend_1/revisions"}
]
}}
主产物类型以 display_artifact_type 为准(gap P0-3):结构性代码任务的主交付物 Manager 一律归一化为 display_artifact_type:"project_folder" 且 is_project:true,并直接给出 manifest/files/archive/revisions 子路径——客户端不用再靠原始 artifact_type(可能是 code_patch)猜它是不是完整项目。非交付物(纯摘要)is_project:false,display_artifact_type 回落为原始类型。
判断是否真实交付:display_status=completed 且 is_project:true(Manager 已裁决,客户端无需自判)。
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | .../artifacts/{artifact_id}/content |
原始正文(兼容) |
| GET | .../artifacts/{artifact_id}/manifest |
项目文件树(entries[path,type,mime,size,hash,content_path] + archive) |
| GET | .../artifacts/{artifact_id}/files?path=<相对路径> |
单个文件正文(见下「路径编码」) |
| GET | .../artifacts/{artifact_id}/archive |
整个项目 zip 下载(见下「下载契约」) |
manifest 示例:
{ "success": true, "data": {
"artifact_id":"art_xxx","artifact_type":"project_folder","root_dir":"project","revision":1,
"content_hash":"sha256:...","file_count":2,
"entries":[
{"path":"app.py","type":"file","mime_type":"text/x-python","size_bytes":420,"content_hash":"sha256:...","content_path":".../files?path=app.py"},
{"path":"frontend/src/App.tsx","type":"file","mime_type":"text/typescript","size_bytes":300,"content_hash":"sha256:...","content_path":".../files?path=frontend%2Fsrc%2FApp.tsx"}
],
"archive":{"format":"zip","download_path":".../archive"}
}}
单文件「路径编码」(gap P1-1):用 ?path= query 传相对路径,整体做一次 URL encode(含 /、空格、中文、#、? 等保留字符)。例:GET .../files?path=frontend%2Fsrc%2FApp.tsx。直接用 manifest 里给的 content_path 即可,无需自己拼。找不到文件返回 {"error":{"code":"FILE_NOT_FOUND"}};缺 path 返回 FILE_PATH_REQUIRED。
整包「下载契约」(gap P0-4):GET .../archive 成功时返回真实 zip 二进制,响应头:
HTTP 200
Content-Type: application/zip
Content-Disposition: attachment; filename="art_xxx.zip"
Content-Length: <字节数>
客户端必须读 Content-Disposition 文件名、按 Content-Length 校验落盘,保存成功后才提示「已下载」。产物尚未就绪(无文件)时返回 HTTP 200 body 为:
{ "success": false, "error": { "code": "ARTIFACT_ARCHIVE_NOT_READY", "message": "项目压缩包尚未生成(产物还未就绪),请稍后重试。", "retryable": true } }
此时客户端不要提示已下载,按 retryable 稍后重试。
6. 本地修改回传(revision)
用户在本地改了产物后,必须显式上传;Manager 存为新 revision。
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | .../artifacts/{artifact_id}/revisions |
revision 列表 + current_project_revision |
| POST | .../artifacts/{artifact_id}/local-edits |
上传单文件修改 |
| POST | .../artifacts/{artifact_id}/local-edits/batch |
上传多文件修改(changes[],op=update/create/delete) |
单文件上传请求:
{ "base_artifact_id":"art_xxx","base_revision":1,"path":"frontend/src/App.tsx",
"content":"<html>...</html>","content_hash":"sha256:new","change_summary":"改了首页标题" }
批量请求:{ "base_revision":1, "changes":[ {"op":"update","path":"a.py","content":"..."},{"op":"create","path":"b.py","content":"..."},{"op":"delete","path":"c.py"} ], "change_summary":"..." }
成功响应:{ "success":true,"data":{ "artifact_id":"art_xxx","revision":2,"project_revision":2,"source":"client_local_edit","status":"accepted","content_hash":"sha256:new" } }
revision 状态语义(gap P1-2):
| status | 含义 |
|---|---|
accepted |
已被接受为当前可用基线(无冲突即直接接受,Manager 持有基线) |
applied |
已被 Runtime 在后续 /messages 或 /execute 消费 |
conflict |
与最新 revision 冲突(见下,不落库) |
后续执行使用最新 accepted revision(gap P1-3):调用 POST .../tasks/{id}/messages 或 .../execute 时,Manager 自动取该任务最新 accepted revision 作为基线并置为 applied,响应回带 active_project_revision(无本地修改时不返回该字段)。客户端无需手动指定 revision。
冲突:若 base_revision 不等于当前最新,返回 { "success":false,"error":{"code":"ARTIFACT_REVISION_CONFLICT","retryable":false},"data":{"current_project_revision":3} },客户端提示用户先同步最新再提交。
7. 云部署(控制面,执行待 Deploy Worker)
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/heicode/deployment-targets |
可部署云目标(azure 启用;aliyun/aws 预留) |
| POST | /api/heicode/sub-agile/tasks/{task_id}/deployments |
发起部署(body: artifact_id/target/environment/region/resource_binding_id/options) |
| GET | /api/heicode/sub-agile/tasks/{task_id}/deployments |
部署列表 |
发起部署请求:
{ "artifact_id":"art_xxx","artifact_revision":1,"target":"azure","environment":"preview",
"region":"eastasia","deployment_mode":"managed","resource_binding_id":123 }
响应:{ "success":true,"data":{ "deployment_run_id":"deploy_xxx","deployment_id":"dep_xxx","target":"azure","environment":"preview","status":"deployment_requested","executor":"pending_worker" } }
客户端禁止传云厂商 AccessKey/Secret/连接串/secret_ref,只传 resource_binding_id(Manager 内部解析为凭据)。
environment=production → status=waiting_approval(需审批);executor: "pending_worker" 表示真实云执行待 Deploy Worker 接入(当前部署不会真正起资源)。
8. Runtime 回调(仅 Runtime 用,客户端无需关心)
Runtime → Manager 统一回调:POST /api/agent/callbacks/runtime-events。
Schema 查询:GET /api/agent/callbacks/runtime-events/schema。
9. 客户端生产约束
- 审批 approve/reject 必须调 Manager,禁止本地伪造。
- 生产包禁用 mock 任务/审批/产物。
- 只消费 Manager 的
display_status/ manifest / diagnostics。 - 普通用户文案不出现
newapi、上游厂商、模型网关字眼。 - 任务接口认证:写请求用 V2 加密 body + 设备签名,GET 用 V2 无 body 设备签名(见 §1),均复用模型调用同一套实现;base_url 来自 preset。
10. 错误码(新增)
| code | 场景 | retryable |
|---|---|---|
ARTIFACT_REVISION_CONFLICT |
本地修改基于旧版本(data.current_project_revision 给出最新版本) |
false |
ARTIFACT_ARCHIVE_NOT_READY |
zip 产物尚未就绪(无文件),稍后重试 | true |
ARTIFACT_ARCHIVE_FAILED |
打包失败 | false |
RESOURCE_BINDING_INVALID |
resource_binding_id 不存在或非本人 | false |
DEPLOY_TARGET_DISABLED |
云目标未启用 | false |
FILE_NOT_FOUND |
项目文件不存在 | false |
FILE_PATH_REQUIRED |
取单文件未带 ?path= |
false |
其余错误码沿用旧文档 §13。所有错误统一形如 {"success":false,"error":{"code","message","retryable"}},可读 error.retryable 决定是否重试。