agent-98 面板批次:
- 会话管理面板改接 history.ListSessions(主web+次级) + 新建次级会话按钮
- 记忆面板手动添加入口 POST /admin/memories(owner)
- 知识库真正实现 IndexDocument(分块+落projectindex) + POST /admin/rag 导入端点
- 三面板空态诚实表述(去除'后端M10+提供')
- RAG嵌入降级到keyword/line(无直属embedder,不伪造语义,符合红线)
agent-99 design/16 §4.6:
- model.Message.SenderName + 迁移0006 sender_name 列
- 第三方身份边界: session.ResolveActor判定(非LLM猜) + [第三方·昵称]前缀(不改源content)
- 群聊/陌生人落独立sess-xxx(persistIsolated),不写主会话,不广播
- 黑/白名单(allowGroups/denyGroups/allowUsers/denyUsers)最前拦截,不进LLM
- 多账号 CHANNELS_JSON 配置 + 每平台多实例
- 管理只读 GET /admin/channels/{platform}/messages(该平台非主人消息流)
实机验证(JWT): 会话管理返回主会话web / 记忆添加成功 / RAG导入documents非空+Cyrene命中; 后端build/vet/test全绿 + 前端build绿
28 KiB
Cyrene v2 客户端 API 文档(用户侧)
面向: Web 前端 / 移动端原生客户端 / IM 桥接等跨平台客户端开发者。 维护铁律: 每实现/变更一个 API(REST 端点、WS 事件、admin API)立即更新本文件;契约变更必须版本化,不破坏既有客户端。 状态: gateway 已在 M8 落地核心端点;M9 前端按此开发。
✅= 已实现,🕐= 规划。
0. 基础约定
- Base URL:
http://<host>:8080(网关唯一对外端口;生产一般经反代/HTTPS)。 - 版本: REST 全部挂在
/api/v1/{resource};未来破坏性变更升 v2。 - 认证:
Authorization: Bearer <JWT>(RBAC:owner/admin/viewer);管理端另支持X-API-Key。 - 响应统一:
{"data": ...}/{"error": {"code": <string>, "message": <string>}};状态码 200/201/204/400/401/403/404/429/500。 - 错误码:业务错误带
code字符串(如unauthorized/not_found),便于客户端分支。
1. REST 端点(公开)
| # | 端点 | 方法 | 认证 | 状态 | 说明 |
|---|---|---|---|---|---|
| 1 | /api/v1/auth/login |
POST | 否 | ✅ | {username,password} → {data:{token,jwt}}(24h,owner) |
| 2 | /api/v1/health |
GET | 否 | ✅ | 健康检查 {data:{status:"ok"}} |
| 3 | /api/v1/chat |
POST | JWT | ✅ | {text,image?,deviceId?,stream?} → {data:{reply}};stream:true 时经 WS 触发流式。image 为 base64 data: URI 或 http(s) URL,入视觉真推理(design/06);deviceId 为客户端生成的具体端标识(design/16 §4.5,如 web:chrome:a1b2c3,可选),落库到消息、便于主人知道从哪个端发来。会话编排(design/15):非流式且该会话「正忙」时不阻塞,返回 202 {data:{queued:true,queueDepth}}(稍后按上下文续接);待处理队列满时返回 409 {error:{code:"chat_busy"}} |
| 4 | /api/v1/sessions |
GET | JWT | ✅ | {data:[session...]}。会话持久化(design/16):注入 HistoryStore 后返回持久化会话元数据 `{data:[{id,type("main" |
| 5 | /api/v1/sessions |
POST | JWT(owner/admin) | ✅ | 新建次级会话 → 201 {data:{sessionId}}(sessionId="web:<uuid>");未注入 HistoryStore 回 501 not_implemented |
| 6 | /api/v1/sessions/{id}/messages |
GET | JWT(owner/admin) | ✅ | 热分页拉历史:?limit=&before=&platform= 默认 limit=50(上限 200)、before=上一页最旧 createdAt(毫秒),按时间倒序 {data:[{id,sessionId,role,content,mode?,mood?,image?,thinking?,platform?,deviceId?,senderName?,createdAt}]};platform 为可选来源平台过滤(design/16 §4.4)——Web 端不传(=全量共享主会话),IM 适配器传本平台(如 telegram)只拉本渠道消息;deviceId 为客户端具体端标识(design/16 §4.5,如 web:chrome:a1b2c3 / weixin-ilink:conf0:wx_uid);senderName 为第三方发送者昵称(design/16 §4.6,群聊/陌生人私聊消息带,主人私聊为空,omitempty);非法 limit/before 回 400;未注入 HistoryStore 回 501 |
| 7 | /api/v1/sessions/{id} |
DELETE | JWT(owner/admin) | ✅ | 删除次级会话及其消息 → {data:{deleted:<id>}};主会话不可删回 403 forbidden;未注入 HistoryStore 回 501 |
| 8 | /api/v1/personas |
GET | JWT | ✅ | {data:[persona...]} |
2. REST 端点(管理,/api/v1/admin/*)
仅供管理面板与授权客户端;RBAC:
owner/admin/viewer(viewer 只读、写 403);写操作全程审计。
| # | 端点 | 方法 | 状态 | 说明 |
|---|---|---|---|---|
| 1 | /api/v1/admin/personas |
GET | ✅ | 人格列表 |
| 2 | /api/v1/admin/sessions |
GET | ✅ | 持久化会话列表(主会话 + 次级会话,design/16 §2);未注入 HistoryStore 时回退渠道会话视图 |
| 3 | /api/v1/admin/memories |
GET | ✅ | 记忆列表(手动添加/检索已实现;编辑/删除/衰减随 M10 扩展) |
| 4 | /api/v1/admin/plugins |
GET | ✅ | 插件/能力列表(含 MCP/渠道) |
| 5 | /api/v1/admin/plugins/policy |
POST | ✅ | 写工具策略(owner-only,审计) |
| 6 | /api/v1/admin/channels |
GET | ✅ | 渠道列表/健康 |
| 7 | /api/v1/admin/iot |
GET | ✅ | IoT 设备列表 |
| 8 | /api/v1/admin/users |
GET/POST | ✅(GET) | 用户/角色/API key(GET 已实现;POST 创建用户/签发 key 随 M10 落地) |
| 9 | /api/v1/admin/llm |
GET/PUT | ✅(GET) | LLM 模型/路由/降级(GET 已实现,APIKey 脱敏;PUT 热重载 M10) |
| 10 | /api/v1/admin/automations |
GET/POST | ✅(GET) | 自动化与提醒(GET 已实现;POST 创建 M10) |
| 11 | /api/v1/admin/backthink |
GET | ✅ | 后台思考记录 |
| 12 | /api/v1/admin/rag |
GET | ✅ | 知识库文档列表(分块/嵌入状态);带 ?query= 时并入检索结果 |
| 13 | /api/v1/admin/rag/query |
POST | ✅ | 知识库检索(真实检索;配置向量增强走语义路径) |
| 14 | /api/v1/admin/rag/delete |
POST | 🕐(写) | 删除知识库文档;写操作 M10,当前返回 501 not_implemented |
| 15 | /api/v1/admin/observability/metrics |
GET | ✅ | 网关请求指标(总数/QPS/延迟/方法·路径·状态计数/LLM·子代理计数) |
| 16 | /api/v1/admin/observability/trace |
GET | ✅ | 最近 trace span(环形缓冲,默认 512 条) |
| 17 | /api/v1/admin/observability/audit |
GET | ✅ | 审计记录(who/what/when/result,SQLite 持久化) |
| 18 | /api/v1/admin/memories |
POST | ✅(写) | 手动添加记忆(owner-only,审计);body {personaId?,content,scope?},无 store 回 501 |
| 19 | /api/v1/admin/rag |
POST | ✅(写) | 导入知识文档(owner-only,审计);body {name,desc?,content},分块后落项目索引可检索;无 store 回 501 |
M10 前端契约补充(管理面板):
GET /api/v1/admin/users·GET /api/v1/admin/llm·GET /api/v1/admin/automations·GET /api/v1/admin/backthink·GET /api/v1/admin/rag·POST /api/v1/admin/rag/query·GET /api/v1/admin/observability/metrics·GET /api/v1/admin/observability/trace·GET /api/v1/admin/observability/audit已实现,返回真实数据;GET /api/v1/admin/logs·GET /api/v1/admin/system仍🕐未实现(前端按契约封装、后端 404 时显示"待实现"壳,不报错)。写操作已补齐(见下方「管理面板写操作契约」):人格/API key/设备控制/自动化/LLM 测试/渠道连接与识别测试/记忆检索/会话导出/插件卸载真实持久化或真实生效;仍为后置 M10:新增用户、IoT 场景与规则、插件安装(需源码清单)、主人档案保存、系统设置、知识库删除——后端如实返回501 not_implemented,不伪造成功。
管理面板写操作契约(POST/PUT/DELETE /api/v1/admin/*,均 owner-only,读为 owner/admin;写操作经 audit 落 audit_logs;成功 {data} / 失败 {error:{code,message}}):
POST /admin/personasbody{id,name,role,systemPrompt?,characterRef?,modes?/mode?,memoryNamespace?}→201 {data:{id,name,character,isDefault,mode,memoryNamespace}}(注册进 persona registry + 持久化到configs/personas/<id>.yaml);id 已存在回409 duplicate。PUT /admin/personas/{id}body 同上(不含 id)→200 {data:PersonaSummary};不存在回404。POST /admin/users→501 not_implemented(无持久用户表,当前为单 admin + APIKey 现状)。POST /admin/users/apikeybody{role:"owner|admin|viewer"}→201 {data:{id:<掩码>, key:<明文·仅此一次>, role}}。key仅此响应出现一次。DELETE /admin/users/apikey/{id}(id=列表掩码)→200 {data:{id,revoked:true}};未命中404。POST /admin/iot/{id}/controlbody{action,params?}→200 {data:{id,name,type,state,online}}(真实下发到 IoT provider);404设备不存在 /400动作不支持或参数错 /409设备离线。POST /admin/iot/scenarios、POST /admin/iot/rules→501 not_implemented(IoT 无场景/规则存储)。POST /admin/automationsbody{name,kind:"cron|event",expression}→201 {data:{id,name,kind}}(真实注册规则);无效 cron/规则回400 invalid_cron|invalid_rule。POST /admin/automations/{id}/run→200 {data:{id,output}}(手动触发);404。DELETE /admin/automations/{id}→200 {data:{id,deleted:true}};404。POST /admin/llmbody{name,model,baseUrl?,multimodal?,fallback?}→201 {data:{provider,temporary:true}}。临时,进程生命周期,不入 config、不绑定模型路由。POST /admin/llm/testbody{provider?,baseUrl,model,apiKey?,prompt?}→200 {data:{ok,provider,model,reply,tokens}}(真实模型调用);无效/失败回400 invalid_provider/500 llm_test_failed。API key 仅本次请求内使用,不落库、不进 wire。POST /admin/channels/open、POST /admin/channels/closebody{name}→200 {data:{name,connected}}(真实 Connect/Disconnect);404(未注册渠道)/500(凭据缺失等连接失败)。POST /admin/channels/testbody{platform?,accountId,isGroup}→200 {data:{role,owner,isGroup,env,verdict}}。结构性判定:channel 层 actor 元数据 ×configs/users/owner.yaml白名单,绝不靠提示词。GET /admin/channels/{platform}/messages?limit=&before=(owner/admin 读)→200 {data:[{id,sessionId,role,content,mode?,mood?,image?,platform?,deviceId?,senderName?,createdAt}]}。IM 适配器日志流(design/16 §4.6 §4):该平台所有非主人私聊/群聊消息(隔离会话sess-xxx,非主会话web)以消息流显示,senderName标发送者昵称;未注入HistoryStore回501。POST /admin/plugins/install→501 not_implemented(需插件源码清单)。POST /admin/plugins/uninstallbody{name}→200 {data:{name,uninstalled:true}}(真实从 registry 移除);404。POST /admin/memoriesbody{personaId?,content,scope?}→201 {data:{id,scope,text}}。手动添加记忆(owner 私聊直写);scope∈user|persona|project,缺省persona(与「记住…」同路径);无效非空scope回400;未注入memory.Store回501。POST /admin/memories/searchbody{query,personaId?,topK?}→200 {data:{query,personaId,semantic,hits:[{id,scope,text,score}]}}。真实检索(keyword×衰减);语义/向量路径需主会话注入 vector index(semantic当前恒false,诚实反映未接向量器)。GET /admin/sessions/{id}/export→200 {data:{session:{platform,channelId,sessionId,actorRole,owner,isGroup,memWritable}}};404。当前导出会话身份元数据(完整消息转录由 M10 提供)。
知识库 RAG 契约:
GET /admin/rag→{"data":{"documents":[{id,name,desc?,chunks,embedded}],"hits"?:[{docId,docName,chapter?,text,score,semantic}]}}。hits仅在携带?query=时出现;embedded反映部署是否配置向量增强(未配置为待嵌入)。POST /admin/ragbody{"name","desc?","content"}→201 {"data":{"name"}}。导入文档:后端分块(空行分段,超长段按固定长度切)并落项目索引(项目+章节),立即可被关键词/行上下文检索;未注入RagStore回501。语义/向量嵌入为优雅降级(知识库 Store 不直属向量索引,导入文档走 keyword/line 路径;语义检索仍对既有 project-linked 记忆生效)。POST /admin/rag/querybody{"query":"..."}→{"data":{"query":"...","hits":[{docId,docName,chapter?,text,score,semantic}]}}。检索走项目索引关键词+行上下文;配置 pgvector/向量增强时并入语义/向量命中(semantic:true)。POST /admin/rag/deletebody{"id":"..."}→ 删除为后置 M10,返回501{"error":{"code":"not_implemented",...}}。
可观测性契约(GET /api/v1/admin/observability/*,均需 owner/admin,?limit=N 缺省 50、上限 500):
GET /metrics→{"data":{"totalRequests","qps","avgLatencyMs","maxLatencyMs","perMethod{}","perPath{}","statusCounts{}","llmCalls","subagentEvents"}}。qps为近 1 秒窗口计数;llmCalls/subagentEvents由实际观测到的 trace span 按kind累计(Tracer 未注入时为 0)。Token/成本后端不统计、不返回(不伪造指标)。GET /trace?limit=100→{"data":[{id,parentId?,name,kind,status,startedAt,endedAt?,durationNs,props?,events?]}。kind∈llm_call|tool_call|orchestration|state_transition|subagent|memory|guardrail;durationNs为纳秒;events为[{time,type,props?}]。GET /audit?limit=100→{"data":[{id,who,what,when,result}]}。id为整数自增;when为 RFC3339;来源为 SQLiteaudit_logs表(未注入 AuditStore 时退化为进程内存)。
3. WebSocket /ws/chat(实时)
-
握手:
ws://<host>:8080/ws/chat?token=<JWT>或Authorization: Bearer;未认证在握手前回401JSON。 -
下行(服务端→客户端): 经 bus 广播
{type:"<event>", data:..., turnId?, seq?, sessionId?}(turnId/seq为流式关联字段,见 §3.1;sessionId为会话持久化(design/16)新增,能确定会话的事件带上,供客户端按激活会话过滤)——message.start/delta/end/thinking.start/delta/end/tool_call.start/delta/end/tool_execution.update(结构化流,design/04 §1.1),以及向后兼容的stream_chunk/stream_end/thinking(思维链) /mood/reminder/device_update/tool_used(工具回显) /subagent_event/graph_update/message_persisted(会话持久化增量,见 §3.3)。会话编排状态(design/15,均为广播,data.sessionId区分会话):session_busy/pending_ack/pending_drain/session_idle/session_interrupted/pending_full(见 §3.2)。语音回复走voice_output(定向回请求连接,非广播)。history回放事件(design/16,新增):WS 握手升级成功后、该连接下一帧即收到{type:"history", sessionId:"web", data:{messages:[{id,sessionId,role,content,mode?,mood?,image?,platform?,deviceId?,createdAt}]}}——回放主会话最新 50 条(thinking已剥离,不泄给非 owner;默认主会话,次级会话由客户端按激活会话走GET /sessions/{id}/messages拉取)。客户端据此在重连/新开连接后补全上下文。消息体含senderName?(第三方昵称,design/16 §4.6,主人私聊为空)。
3.1 结构化流事件(message.* / thinking.* / tool_call.* / tool_execution.*)
- 事件壳:
{type, turnId?, seq?, data},data为类型化结构(非any)。seq为 hub 级单调递增序号,供客户端将一次突发的流式事件排序关联。 message.start({index}) /message.delta({delta, index}) /message.end({index}):正文段。delta为正文 token 增量;客户端对message.delta累加到当前消息正文;message.start/end划定正文段边界,不结束整轮(结束由stream_end负责)。thinking.start({index}) /thinking.delta({delta, index}) /thinking.end({index}):思维链段(前端折叠块)。thinking.delta累加增量。tool_call.start({id, index}) /tool_call.delta({id, index, delta}) /tool_call.end({id, index}):工具调用中间态;tool_call.delta携带参数增量(如 Anthropicinput_json_delta)。tool_execution.update({id, index, delta}):工具执行进度/局部输出(保留,工具循环 §1.2 后置实现)。- 向后兼容:未映射/无法确定的事件仍走
stream_chunk+stream_end(非流式回退路径)。 - 上行(客户端→服务端):
mode_switch/chat(跑 loop 并广播流式) /voice_input/subagent_ask/task_output_query/interrupt。 interrupt(上行,客户端→服务端):{sessionId}→ 会话编排器中断(design/15 §3)——取消该会话当前在跑的任务(context cancel)、清空其待处理队列、回session_interrupted;用户说「停下/算了/换个话题」时发。命中空闲会话为 no-op。voice_input(上行,客户端→服务端):_{audio?, format?, text?, sessionId?}。audio为 base64(或 data: URI)音频,format为其编码(wav/mp3/ogg);text为已转写文本时跳过 STT。服务端走 STT→agent 聊天→TTS(design/06 §1),回复经voice_output定向回该连接。无注入语音服务时回error帧(voice unavailable)。voice_output(下行,服务端→客户端):{audio, format, text, reply}。audio为 base64 合成音频,format为其编码,text为识别文本,reply为助手回复文本。- 向后兼容:未映射/无法确定的事件仍走
stream_chunk+stream_end(非流式回退路径)。
3.2 会话编排状态事件(design/15)
- 触发:同一会话内,
chat到达时若「她忙」(正在跑 loop/工具循环/子代理/长回复)→ 新消息不阻塞、入该会话 pending;这一层的状态经以下事件推给前端,供「正在做 xx… 有新消息稍后回」轻状态展示。 session_busy:会话进入忙碌态({sessionId, mode},mode∈idle|thinking|executing|subagent)。pending_ack:忙时新消息已入队({sessionId, mode, queueDepth})——「收到,稍后回」。pending_drain:忙完开始消费下一待处理消息({sessionId, mode, queueDepth})。session_idle:会话回到空闲、队列清空({sessionId, mode})。session_interrupted:用户interrupt中断,当前任务取消、pending 清空({sessionId, mode, dropped})。pending_full:单会话 pending 达上限(默认 5)拒绝入队({sessionId, mode, max})——防无限积压(design/15 §4)。- 上下文续接:忙完重开时新回合带上一轮上下文(
{mode, queueDepth}之外的续接由服务端完成),非清空重来(design/15 §2.2)。
3.3 消息持久化增量事件(message_persisted,design/16)
- 触发:每一条 user / assistant 消息落库后由编排层广播——Web 主会话路径走
persistUserMessages/persistAssistantReply,IM 渠道主人私聊(design/16 §4.4)由 channelbridge 把消息落主会话web(带platform标记)后复用同一message_persisted广播(同一 hub 的BroadcastPersisted),保证 Web 端实时镜像 IM 消息。 - 载荷:
{type:"message_persisted", sessionId, data:{message:{id, sessionId, role, content, mode?, mood?, image?, platform?, deviceId?, senderName?, createdAt}}}——role∈user|assistant;id为后端生成的消息 id(客户端据此去重);platform为来源平台(web或渠道如telegram/weixin-ilink/qq-obv11);deviceId为具体端/客户端标识(design/16 §4.5,如web:chrome:a1b2c3/weixin-ilink:conf0:wx_uid);senderName为第三方发送者昵称(design/16 §4.6,群聊/陌生人消息带,主人私聊为空,omitempty);thinking已剥离(design/16 §6)。 - 注:群聊/陌生人(非主人)消息只在该 IM 适配器日志界面以消息流显示,不经
message_persisted进 Web 主会话(design/16 §4.6 §3-4);该日志流由GET /api/v1/admin/channels/{platform}/messages拉取。 - 客户端处理:仅当
data.message.sessionId === activeSessionId(激活会话)时处理;按idupsert——本端已预建的同 role 乐观气泡(本地uid无后端id)被归一(uid→ 后端id),异端无本地气泡则直接追加,两者最终收敛为同一条id相同的消息,「A 发消息、A 与 B 都无需刷新且各出现一次」。
4. 客户端接入指引
- 认证: 先
POST /api/v1/auth/login取 JWT → WS 握手(?token=)注入。 - 版本: 客户端请求头声明
X-API-Version: 1。 - 离线: PWA / 原生端建议缓存
sessions/personas列表。
5. 变更记录
v0.14(IM 多会话隔离 + 第三方身份 + 黑白名单,design/16 §4.6):chat_messages增sender_name列(迁移 0006,默认'');消息 DTO(REST 消息、WShistory回放、WSmessage_persisted)均带senderName(第三方发送者昵称,主人私聊为空omitempty)。第三方身份边界:IM 群聊/陌生人会话在上下文构建时对第三方消息注入[第三方 · <昵称>]前缀(不改源消息正文,落库保留原文)+ 前置系统提示词"每个 IM 的非管理员账户 = 第三方人员,和主人无关"——身份判定基于 channel 层 actor 元数据 + 白名单(MemWritable=false即群聊/陌生人),绝不靠 LLM 猜。会话隔离:群聊/陌生人消息落独立隔离会话(sess-xxx,带platform+senderName,不并入主会话web、不走message_persisted广播),供GET /api/v1/admin/channels/{platform}/messages(owner/admin,management 面板渠道日志区)以消息流显示;前端主会话只显示各平台管理员(owner)私聊;群聊/陌生人记忆只读不写主会话(memory_gating已按MemWritable门控)。多账号:CHANNELS_JSON支持同一平台多个接入配置(每个 account 独立 session/账号,见 §2 渠道契约)。黑白名单:每平台每账号可配allowGroups/denyGroups/allowUsers/denyUsers,channelbridge 入站即拦截(黑名单/不在白名单 → 丢弃,audit可查,不进 LLM);白名单按platform+account(群 id / 用户 uid)。v0.13(每条消息带 deviceID/client 标识,design/16 §4.5):chat_messages增device_id列(迁移 0005,默认'');POST /api/v1/chat请求体新增可选deviceId(客户端生成的端标识,如web:chrome:a1b2c3);消息 DTO(REST 消息、WShistory回放、WSmessage_persisted)均带deviceId。platform(平台)与deviceId(具体端)互补:同一平台多个端 → 相同platform、不同deviceId。Web 端由lib/client.ts用localStorage持久化每个浏览器一个的web:<browser>:<随机短id>,发送时自动带上,聊天气泡默认小字标注来源(仅当deviceId存在才显示);IM 端按<im平台名>:<本地配置唯一id>:<用户平台唯一uid>构造(当前UnifiedMessage尚无 deviceId 概念,channelbridge 以<platform>:<channelID>:<senderID>占位,<本地配置唯一id>待适配器提供后替换);Web 端聊天不回流 IM。v0.12(IM 渠道并入主会话 + 平台标记,design/16 §4.4):chat_messages增platform列(迁移 0004,默认'web');GET /api/v1/sessions/{id}/messages支持可选?platform=过滤(Web 端不传=全量共享主会话;IM 适配器传本平台只拉本渠道);消息 DTO(REST 与 WSmessage_persisted)均带platform。IM 渠道主人私聊(!IsGroup && Owner)经 channelbridge 落主会话web(platform=<渠道>)并广播message_persisted→ Web 端免刷新镜像;群聊/陌生人保持独立隔离上下文、不落主会话。Web 端聊天(platform=web)不回流 IM。v0.11(跨端实时同步,design/16 §4.2):WS 下行新增message_persisted事件——每条 user/assistant 消息落库后广播{type, sessionId, data:{message:{id,sessionId,role,content,mode?,mood?,image?,createdAt}}}(id为后端生成,thinking已剥离)。客户端按sessionId === activeSessionId过滤 + 按idupsert(含本端乐观气泡uid→后端id归一)去重,实现「A 发消息、A 与 B 都无需刷新且各出现一次」。后端history.Store新增AppendFull供编排层取含id的落库消息。v0.10(会话消息持久化 — WS 契约,design/16,后端-2):WS 下行帧新增可选sessionId字段(能确定会话的事件带上);新增history回放事件——WS 握手成功即回放主会话最新 50 条({type:"history", sessionId:"web", data:{messages:[...]}},thinking已剥离)。stream_end/stream_chunk/会话编排状态帧(session_busy等)在能确定 session 时带sessionId;message.delta等结构化流帧因model.StreamEvent无 session 上下文不带(即"若知道就带上"边界)。v0.9(会话消息持久化 + 主/次级会话,design/16,后端-1):新增POST /api/v1/sessions(新建次级会话)、GET /api/v1/sessions/{id}/messages?limit=&before=(游标热分页拉历史,时间倒序)、DELETE /api/v1/sessions/{id}(删次级会话及其消息);GET /api/v1/sessions在注入HistoryStore后改返回持久化会话元数据数组(主 + 次级,type区分),未注入时保持原渠道会话列表向后兼容。读消息/写会话为 owner/admin(用户侧私有历史,thinking 不达 viewer);会话元数据列表保持任意认证角色可读。v0.8(管理面板写操作 CRUD):补齐管理面板写端点(见 §2「管理面板写操作契约」,均 owner-only + 审计)——POST/PUT /admin/personas(真实注册 + 持久化configs/personas/<id>.yaml)、POST /admin/users/apikey+DELETE /admin/users/apikey/{id}(真实签发/撤销)、POST /admin/iot/{id}/control(真实设备控制)、POST /admin/automations+/run+DELETE(真实注册/触发/移除规则)、POST /admin/llm(临时记录)+/admin/llm/test(真实模型调用)、POST /admin/channels/{open,close,test}(真实连接/断开 + 结构性身份识别)、POST /admin/plugins/uninstall(真实 registry 移除)、POST /admin/memories/search(真实检索)、GET /admin/sessions/{id}/export。无存储的写(新增用户 / IoT 场景·规则 / 插件安装)如实返回501 not_implemented。v0.7(会话消息编排 design/15):POST /api/v1/chat非流式且会话「正忙」时改回202 {data:{queued:true,queueDepth}}(原同步{data:{reply}}仍为空闲时返回);pending 满回409 chat_busy。WS 上行新增interrupt({sessionId},中断当前任务+清 pending);WS 下行新增会话编排状态事件session_busy/pending_ack/pending_drain/session_idle/session_interrupted/pending_full(见 §3.2)。既有stream_chunk/stream_end与结构化流事件不变,契约向后兼容。v0.6(可观测性):新增GET /api/v1/admin/observability/{metrics,trace,audit}(owner/admin)——metrics 返回网关实时请求计数/QPS/延迟/方法·路径·状态分布 + LLM/子代理 span 计数;trace 返回 gateway 环形缓冲里的最近 span(经 TraceSink 对接 runtime tracer,映射为AdminTraceSpan);audit 返回持久化审计(SQLiteaudit_logs,未注入时退化为内存)。llmCalls/subagentEvents由实际观测 span 累计;Token/成本后端不统计、不返回(不伪造指标)。v0.5(批次B·视觉真推理 + 语音端):POST /api/v1/chat增image字段(base64 data: URI 或 URL),入 loop 视觉路由(vision/OCR 工具或主模型多模态,design/06 §0「先看图再对话」);WS 上行voice_input接 STT→聊天→TTS 管道,下行新增voice_output(定向回请求连接,含合成音频 + 识别文本 + 回复文本)。v0.4(结构化流):WS 下行新增message.*/thinking.*/tool_call.*/tool_execution.update结构化流事件(event shell 补turnId/seq,data类型化),对齐 OpenClaw AssistantMessageEvent;保留stream_chunk/stream_end向后兼容。v0.3(M10):管理面板/admin落地——按 §2 契约封装 admin 只读端点(personas/sessions/memories/plugins/channels/iot)+ owner-onlyplugins/policy写;其余 M10 端点以契约路径封装、后端未实现时前端显式"待实现"壳。v0.2(M8):gateway 落地——登录/health/chat/sessions/personas + admin 只读端点 +plugins/policy写 + WS/ws/chat(JWT + bus 广播)。v0.1(契约先行):骨架 + 端点登记表。