From d2d9a22ae42b66348062202d445b1e2c72a70072 Mon Sep 17 00:00:00 2001 From: chenchen Date: Wed, 10 Jun 2026 00:45:29 +0800 Subject: [PATCH] docs(client-api): add available-models, config, telemetry, agent usage + hidden-token billing semantics (#35, #30) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bring docs/integration/heicode-desktop-client-api.md in lock-step with merged endpoints: - §3.1 /api/heicode/available-models: logged-in user model list is the single source of truth (no local presets / no CodeGW channel backend); exposes only model_id/display_name/default; forbidden fields enumerated. - §5.1 /api/heicode/agents/{id}/usage: token-name aggregation, time window, empty-data semantics, relation to billing logs. Includes the #30 hidden agent model token billing-collection semantics: UnlimitedQuota only waives the token's own remaining-quota cap, it does NOT bypass user.Quota — HM still checks user quota and settles to wallet/subscription via the billing expression; token revoked on stop/delete; hidden from user UI but auditable. - §7.1 /api/heicode/config: telemetry enabled/endpoint/max_batch/flush/retention_days. - §7.2 /api/heicode/telemetry/events: default-off 410 kill switch, V2 device signature, top-level array 1-20 / <=256KB, server redaction, context field whitelist (#32), 4xx-drop/5xx-retry, retention; production enable-gate note. - Updated §10 endpoint inventory. Docs-only; no code change. heicodeDocs org-level sync tracked in #34. Co-Authored-By: Claude Opus 4.8 --- .../integration/heicode-desktop-client-api.md | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/docs/integration/heicode-desktop-client-api.md b/docs/integration/heicode-desktop-client-api.md index 884b6d2f..b7a676bc 100644 --- a/docs/integration/heicode-desktop-client-api.md +++ b/docs/integration/heicode-desktop-client-api.md @@ -120,6 +120,29 @@ signature = base64( ed25519_sign( device_priv, sha256(canonical) ) ) > 客户端只需 `models`(当前可用 `gpt-5.4`);`modes` 是旧任务模式,新模型已无意义。 +> ⚠️ **能力发现 vs 登录用户模型列表**:`/api/heicode/capabilities` 是**免登录**的总目录(渲染模型选择用)。**登录后客户端展示的「我能用哪些模型」必须走 §3.1 `/api/heicode/available-models`**——它按当前用户的分组/订阅服务端收口,且**只有** HM 这一个来源:客户端不得使用本地 preset,也不得从 CodeGW 渠道后台读取模型。 + +--- + +## 3.1 登录用户可用模型 🟢(模型列表收口) + +| 方法 | 路径 | 鉴权 | 说明 | +|---|---|---|---| +| GET | `/api/heicode/available-models` | `UserOrV2DeviceAuth`(会话/JWT 或设备签名) | 当前登录用户**实际可用**的模型列表;服务端按用户可用分组 → 分组启用模型解析 | + +```json +{ "success": true, "data": { + "available_models": [ + { "model_id": "gpt-5.4", "display_name": "gpt-5.4", "default": true } + ] +}} +``` + +- 字段仅 `model_id` / `display_name` / `default`(默认模型)。 +- **禁止暴露字段**:`channel_id`、`base_url`、`api_key`、供应商类型、价格/倍率等一律不返回。 +- **唯一模型来源**:客户端登录后模型列表完全来自此接口,不使用本地 preset / 不读 CodeGW 渠道后台。 +- 鉴权要求登录用户上下文(`id>0`);未登录返回 `authentication required`。 + --- ## 4. Agent 模板列表 🟢 @@ -198,6 +221,31 @@ signature = base64( ed25519_sign( device_priv, sha256(canonical) ) ) --- +## 5.1 Agent 模型用量 🟢(按部署 Agent 聚合) + +| 方法 | 路径 | 鉴权 | 说明 | +|---|---|---|---| +| GET | `/api/heicode/agents/{deployment_id}/usage` | `UserOrV2DeviceAuth`,且只能查**自己**的 agent | 按该 agent 的隐藏模型 token(name=`agent:`)在计费 logs 中聚合用量 | + +查询参数(可选):`start` / `end` = unix 秒时间窗(缺省=全窗口)。 + +```json +{ "success": true, "data": { + "agent_id": "dep_4bb07dc1e376", + "quota": 12345, // 消耗的额度(配额单位) + "prompt_tokens": 8000, + "completion_tokens": 4000, + "call_count": 12, + "quota_per_unit": 500000 // 额度→货币换算分母(quota/quota_per_unit=美元额度) +}} +``` + +- **空数据语义**:无调用记录时各计数为 `0`(仍返回 `success:true`,不是 404)。 +- **与 billing logs 的关系**:用量来自统一计费 logs(`SumAgentUsage`),按 token name `agent:` 过滤聚合 —— 即 agent 走 HM `/v1/*` 的真实消耗,与用户钱包/订阅扣费同源。 +- **计费归集语义(#30)**:每个部署的 agent,HM 为其 mint 一个**隐藏、不展示在用户 token 列表**的模型 token(`UnlimitedQuota:true`)。`UnlimitedQuota` 的含义是「**不对该 token 自身设单独的剩余额度上限**」——它**不**绕过用户额度:agent 经此 token 调 `/v1/*` 时,HM 仍先校验 `user.Quota`,并在结算时从 `user.Quota`(钱包)或订阅项扣费、写计费 log,完全经过计费表达式。停止/删除 agent 后该 token 被撤销,旧 token 无法再调 `/v1/*`。该 token 对普通用户隐藏,但审计/管理员可追踪。 + +--- + ## 6. 直连 Agent(客户端 ↔ agent,A2A 协议) > 客户端**直接连 agent 子域名**(§5 的 `subdomain`),不经过 HM。agent 是 AM 的 `coding_a2a_agent`,走 **A2A 协议**。 @@ -242,6 +290,59 @@ signature = base64( ed25519_sign( device_priv, sha256(canonical) ) ) --- +## 7.1 客户端运行时配置 🟢 + +| 方法 | 路径 | 鉴权 | 说明 | +|---|---|---|---| +| GET | `/api/heicode/config` | 无(公开,非敏感全局配置) | 客户端 runtime config / telemetry 开关;客户端轮询以便会话内即时生效(无需重登) | + +```json +{ "success": true, "data": { + "telemetry": { + "enabled": false, // 默认关闭(kill switch) + "endpoint": "/api/heicode/telemetry/events", + "max_batch": 20, + "flush_interval_sec": 30, + "retention_days": 30 // 服务端保留期:超期遥测被清理(#32) + } +}} +``` + +- 客户端必须以 `telemetry.enabled` 为准:为 `false` 时**停止上送**(摄入端点也会回 410)。 + +--- + +## 7.2 客户端错误遥测上送 🟡(默认关闭) + +| 方法 | 路径 | 鉴权 | 说明 | +|---|---|---|---| +| POST | `/api/heicode/telemetry/events` | `UserOrV2DeviceAuth` + **V2 设备签名**(需配对设备) | 上送客户端错误遥测;**诊断流量,绝不计费、不进 consume log** | + +- **默认关闭**:`HEICODE_TELEMETRY_ENABLED=false` 时返回 **410**(kill switch),客户端应停止上送。 +- **鉴权**:需登录用户 + 已配对设备;请求头带 `X-Heicode-Device-Id`。会话-only(无设备)调用被拒(403)。 +- **Body = 顶层 JSON 数组**(不是包裹对象),**1–20 条/批**,**≤256KB**。超限 413,非数组 400。 +- **每条事件**字段(诊断用,无用户内容):`client_id`(须等于配对设备 id)、`schema_version`、`app_version`、`platform`、`os_version`、`arch`、`locale`、`error_category`、`error_code`、`error_message_hash`、`stack_hash`、`stack_top`(数组)、`context`(对象)、`timestamp`、`session_seq`。 +- **服务端脱敏**:`stack_top` / `context` 即使客户端已脱敏,服务端仍二次 redaction(剥离 `sk-`/`Bearer`/URL token/JSON 密钥字段)。 +- **context 字段白名单(#32)**:`context` 仅保留 `route` / `retryable` / `phase` / `exit_code` / `duration_ms` / `attempt`;其余键(含 email、完整文件路径、prompt、IP 原文等可识别信息)**一律丢弃**。`stack_top`/`context` 单字段脱敏后截断到 8KiB。 +- **重试语义**:4xx(校验失败/超限/kill switch 410)**丢弃不重试**;5xx(持久化失败)可重试。 +- **保留期(#32)**:服务端按 `HEICODE_TELEMETRY_RETENTION_DAYS`(默认 30)定期清理超期遥测。 + +> ⚠️ **上线前置门槛(#32)**:`device_id`/`client_id` 可关联账号,属隐私敏感。生产开启 `HEICODE_TELEMETRY_ENABLED=true` 前必须:隐私文档已如实披露「设备 ID 可关联账号的错误遥测」、产品/法务已确认、kill switch 已验证。隐私文档同步见 heicodeDocs(#34)。 + +```json +// 请求体(顶层数组,示意一条) +[ + { "client_id":"", "app_version":"0.5.0", "platform":"win32", + "error_category":"ui_crash", "error_code":"RENDERER_ERROR", + "error_message_hash":"9f2a7c1b4e8d", "stack_hash":"a1b2c3d4e5f6", + "stack_top":["at MessageList (MessageList.tsx:212:9)"], + "context":{"route":"chat"}, "timestamp":"2026-06-09T07:21:33.123Z", "session_seq":1 } +] +// 成功:{ "success": true, "accepted": 1 } +``` + +--- + ## 8. 响应 envelope 与错误码 **成功**:`{ "success": true, "data": {…} }` @@ -287,11 +388,15 @@ signature = base64( ed25519_sign( device_priv, sha256(canonical) ) ) | 接口 | 鉴权 | 状态 | |---|---|---| | `GET /api/heicode/capabilities` | 无(公开) | 🟢 | +| `GET /api/heicode/available-models` | 会话/设备 | 🟢(登录用户模型列表收口,唯一来源) | +| `GET /api/heicode/config` | 无(公开) | 🟢(runtime config / telemetry 开关) | | `GET /api/user/self`、`/self/models` | **UserAuth(会话/JWT)** | 🟢 | | `GET /api/heicode/agent-templates` | 会话/设备 | 🟢(生产已验证 19 中文模板) | | `GET /api/heicode/agents` `/{id}` `/{id}/status` | 会话/设备 | 🟢 | | `POST /api/heicode/agents` | 会话/设备 | 🟢(调 AM,AM 接口 🔴) | | `POST /api/heicode/agents/{id}/stop`、`DELETE /{id}` | 会话/设备 | 🟢(调 AM 🔴) | +| `GET /api/heicode/agents/{id}/usage` | 会话/设备(仅自己的 agent) | 🟢(按 `agent:` token 聚合计费 logs) | +| `POST /api/heicode/telemetry/events` | 会话/设备 + V2 设备签名 | 🟡 默认关闭(410 kill switch);不计费 | | 模型 `/v1/*` | 同模型调用 | 🟢 | | `POST /api/heicode/agent-access/verify` | 公开(令牌即凭据) | 🟢(可选兜底;接入用 ① 本地比对,默认不用此端点) | | 直连 agent SSE(subdomain + X-Agent-Access-Token) | agent 本地比对令牌 | 🟢 直连已通 / ⚠️ 令牌校验待 AM 开启 |