微昕·砚文档

HTTP API 合同

画布实时会话

GET /api/canvas-projects/:id/realtime 使用登录 Cookie 升级为 WebSocket,握手校验当前用户对画布的归属和请求 Origin。首帧 welcome 提供服务端分配的 selfId 和窗口 Presence;客户端发送 presence,服务端推送 peer、leave、invalidate 和 heartbeat。Presence 只含有界世界坐标和拖拽矩形,不包含密钥、编辑租约 token、画布正文或实际保存指令。

invalidate 在事务提交后发送,客户端通过 GET /api/canvas-projects/:id/sync-status 校验版本与租约,再按需读取正文;长连接不代表云端保存成功。正常且无活动 Agent 时每 30 秒补偿查询,断线或活动 Agent 期间使用原有轮询。未登录返回 401,画布缺失或不属于当前用户返回 404,不允许的来源返回 403,实时服务不可用返回 503。完整协议和部署边界见画布操作同步。

视频工作台的生成模式

POST /api/workbench/tasks 的视频任务根据已校验的 references 推导操作类型,客户端不需要提交泛化的 video 操作:

输入操作
无媒体素材text_to_video
仅首帧或尾帧图片,可附带音频image_to_video
普通参考图、参考视频,或包含普通参考图的组合reference_to_video
仅音频audio_to_video

图片的 role=first_frame/last_frame 表示帧约束;reference 或未指定角色表示普通参考,不能静默改成首帧。模式、素材数量和参数共同参与模型能力校验及价格档选择;不支持的组合在创建任务和计费前拒绝。执行输入的 metadata.videoEditOperation 与任务操作保持一致,并保留帧素材标识。浏览器自定义渠道的工作台任务使用同一模式规则,其他入口沿用各自的意图解析。

图片响应格式

渠道模型的 capabilityConfig.image.responseFormat 包含 supported 和可选的 default。默认值为 auto,表示请求中不发送 response_format,由上游决定返回 URL 或 Base64;b64_json 和 url 会分别强制发送对应值。该配置按模型保存,不根据 openai-image 协议名称自动推断,避免兼容网关不接受 Base64 时生成失败。需要持久化 Base64 结果的模型(例如已验证的 wxin 图片模型)应将默认值设为 b64_json。

用户登录日志

菜单“用户与运营 → 用户登录日志”,页面 /admin/login-logs。查询在 service 校验管理员权限:匿名 401、普通用户 403。

  • GET /api/admin/login-logs:支持 keyword(用户名、昵称、脱敏账号或用户 ID)、event=login|logout、method=password|email|sms|linuxdo|verification|unknown、result=success|failed、精确 ip、RFC3339 from/to(含边界)、page/pageSize。开始时间不能晚于结束时间。默认每页 20 条、时间倒序;成功 data={logs,total,page,pageSize},空结果 logs=[]。
  • GET /api/admin/login-logs/:id:成功 data={log};不存在返回 404。包含用户快照、时间、事件、来源、方式、IP、UA、安全原因码/文案和非凭据的关联会话标识。

后端在密码、邮箱/短信验证码、Linux.do 登录、注册后自动登录及主动退出的真实认证路径记录,不提供客户端任意新增日志接口。verification 表示凭据/请求无效导致方式无法识别;unknown 表示上线前会话未保存方式。source=registration 标记注册自动登录。退出失败不清 Cookie,不再忽略撤销错误。刷新状态和会话自然过期不新增事件。浏览器/系统由页面根据 UA 展示,无法识别时明确标注;UA 是客户端自报信息。

个人信息

所有 /auth/profile 路径均要求当前活动用户的登录 Cookie,不能通过用户 ID 修改他人资料。

路径入参与结果
GET /auth/profile返回 user、hasPassword、emailEnabled、linuxdoEnabled 和可选 reauthenticatedUntil;密码散列、头像图片正文、内部缓存版本均不在用户 JSON 中
PUT /auth/profile/avatarmultipart file,只接受真实 PNG/JPEG、最大 5 MiB;返回 { user },包含带版本的头像地址
DELETE /auth/profile/avatar清除个人头像并明确选择系统默认头像;第三方重新登录不会覆盖该选择;返回 { user }
POST /auth/profile/avatar/default{ preset },只接受 person/director/camera/cat/rabbit/robot/rocket/sparkles;保存选择并清除个人图片,返回 { user }
GET /auth/profile/avatar/:userId只读取本账号的 PNG,Cache-Control: private, no-store;账号之间拒绝读取
POST /auth/profile/password{ currentPassword, newPassword };新密码至少 8 个 Unicode 字符、UTF-8 不超过 72 字节;返回 { changed: true } 并清除 Cookie、撤销全部会话
POST /auth/profile/email-code{ email, currentPassword };向未占用的新邮箱发送验证码,返回 { ticket, expiresIn, retryAfter }
POST /auth/profile/email{ ticket, emailCode, currentPassword };验证后更新邮箱及验证时间、撤销全部会话并清除 Cookie;返回 { changed: true, notificationStatus }
GET /auth/profile/linuxdo/start纯第三方账号重新核验身份的入口;绑定原用户和原会话,成功回到 /settings?section=profile&reauth=success,失败回到该页的 reauth=error 状态

已有本地密码的账号必须验证当前密码。纯 Linux.do 账号设置密码或更换邮箱时,必须先完成同一会话内的重新授权,核验必须匹配原第三方用户标识;有效期为 5 分钟。邮箱验证码用途固定为 profile_email,绑定当前会话与凭据状态,6 位数字、10 分钟有效、最多 5 次核验;收件地址跨场景共享 60 秒发送冷却、每小时 5 次和每天 20 次额度。发送失败明确报错;邮件服务未启用时禁止更换邮箱。

新邮箱验证之前保留原绑定。更新及验证码消费、登录会话撤销一起提交,唯一性检查在事务内重复执行。更新后向原邮箱发通知,notificationStatus 为 sent、failed 或 not_needed;failed 表示邮箱已更新、原邮箱通知失败,不能把已成功更新显示成未保存。接口使用现有业务错误信封和限流规则,密码、验证码和 ticket 不进入 URL。

上传头像先通过共享裁剪弹窗选择 1:1 区域,支持拖动、缩放裁剪框、键盘调整与重置;确认后导出最大 512×512 的 PNG 到待保存预览,点击“保存头像”才上传。取消裁剪保留原头像;待保存图片可重新调整裁剪。选择系统头像使用现有 AppModal、Avatar 与主题 token,支持 8 个预设,确认后才写入服务端。

用户反馈

所有反馈接口使用登录 Cookie 和标准业务信封。普通用户只能提交反馈、上传附件和读取本人列表;/api/admin/feedbacks 下的写入与全量列表由 service 强制校验管理员权限。

接口入参与结果
POST /api/feedbacks{ type, title, content, contact?, attachments?, environment? };类型为 bug/feature/improvement/other,标题最多 200 个 UTF-8 字节、正文最多 10000 个 UTF-8 字节。附件和环境字段是 JSON 字符串;返回 data 反馈记录
GET /api/feedbacks/my返回当前账号自己的反馈列表,按创建时间倒序,包含处理状态、管理员答复与归档标志
POST /api/feedbacks/attachmentsmultipart 字段 file;支持 PNG/JPG/JPEG/WebP/GIF/BMP 与 MP4/WebM/MOV/AVI/MKV,单文件最大 50 MiB,请求体最大 55 MiB;文件通过现有资源服务保存,返回附件元数据
GET /api/admin/feedbacks支持 type、status、keyword、archived、page、pageSize;默认每页 20 条,pageSize 超过 100 时使用默认值。返回 data={list,total,page,limit},按创建时间倒序
PATCH /api/admin/feedbacks/{id}可更新 status=pending/processing/resolved/closed、adminReply、archived;返回更新后的反馈记录
POST /api/admin/feedbacks/{id}/archive、POST /api/admin/feedbacks/{id}/unarchive归档或恢复历史反馈,返回更新后的反馈记录
DELETE /api/admin/feedbacks/{id}删除反馈记录,返回 data={ok:true}

Agent 画布权限与大事件

自由画布和项目关联画布均使用 canvas_projects。Agent 运行时按登录账号和画布 ID 查询归属,不能把所属项目 ID 当作画布 ID;画布不存在、属于其他账号或数据库读取失败时,不声明读写及导出权限。

canvas_updated 的画布增量超过单事件 128 KiB 上限时,复用 requiresRefresh: true 通知客户端重新读取完整画布,优先保留操作摘要;摘要仍超限则仅发送刷新通知。画布正文、历史和操作回执仍完整保存,普通大小事件继续携带 canvasPatch。不提高事件上限、不截断正文、不修改审批暂停或模型调度判断。

Agent 画布逐步撤销

聊天卡片使用 canvas_updated.undo 中的原始 runId 和 canvasId,不能改用当前最新轮次 ID;历史轮次中的卡片因此仍能对应真实操作。前端调用 POST /api/agent/runs/:id/undo,传 { stepId, expectedSnapshotHash, reason? };哈希为完整画布内容的 64 位十六进制摘要,不使用媒体局部哈希。成功返回 { accepted: true, snapshotHash } 并记录 canvas_undone。

服务端再次校验用户对运行及画布的归属,只允许撤销该运行最新一笔变更,并要求当前完整画布哈希仍等于变更后的哈希;有后续手工/Agent 修改则返回 409。快照缺失或操作已提交收费任务返回 400,不取消任务、不退款。成功响应丢失时可以重放同一个原始请求:只要画布仍是撤销后的快照,原 expectedSnapshotHash 仍可幂等确认;不能在其后又有新修改时继续重放覆盖。

Agent 模型输出限制

Pi 模型桥在保存待执行调用之前,复用单步输出限制校验:一次回复最多 8 个工具调用,正文最多 32000 字节。超限时整批未执行,释放本步模型任务并附带 invalid_output 及具体上限要求模型分批纠正;与既有异常重试共用首次失败后最多 3 次的上限,不截取部分调用执行。纠正请求仍是正常计费的文本模型任务,不创建媒体任务。

Agent 媒体规格与会话恢复

媒体准入发现解析规格与申请值不一致时,返回具体参数的 field、issue=invalid_value 及申请值与解析值,进入现有的有限次工具纠正流程;同一工具累计三次失败后停止。视频时长字段映射为 durationSeconds,视频清晰度映射为 quality。修正后仍须用户审批,未通过准入不创建媒体任务或媒体账单。图片 quality 档位已解析为同值 imageResolution 时按等价表达核对,不能因原质量字段为空误拒绝,也不能放过真正不同的分辨率。

Pi 审批或中断后恢复时,缺失的工具结果先按调用 ID 查找服务端已持久化的回执,保留其中的任务提交与失败事实;只有没有持久化回执的缺口才使用既有的审批暂停占位结果。运行时已经收到的结果不被旧占位覆盖。补齐只修复模型请求历史,不重新执行工具或自动重发收费任务;孤立结果、无效调用 ID 等其他历史错误仍由原校验拒绝。

当前系统行为合同为 v17,工具调用数量与单个 canvas_apply_ops 操作数量分别校验。既有活动运行受冻结合同版本保护;新会话采用新合同,不改写旧检查点。

Agent 分镜连线参数反馈

素材绑定使用“素材→分镜”和 toHandleId: row:<rowId>;镜头输出使用“分镜→图片/视频”和 fromHandleId: row:<rowId>。rowId 必须是读取工具返回的真实行 ID,不能用镜头编号代替。普通节点连线与镜头行连线按 handle 区分,已有整表连线不代表该行已经绑定。

handle 放错端点、格式错误或行 ID 不存在时,工具错误回执提供具体 field(如 ops[0].fromHandleId)、issue、方向说明及连线参数模板,不再使用无关的新增节点示例。模板不会被自动执行,不自动交换端点或修改原调用。权限、快照和重复连线校验保持不变;工具契约版本为 v4,重启后端后新会话使用更新后的说明。

KM 图片尺寸能力

图片能力 size.presets[].tier 在已有档位之外接受 1.5k;前端档位选择、请求归一化与服务端预设校验保持一致。km-kemei-seedream 默认 2048x2048、单次一张、最多十张参考图,提供二十四个精确尺寸。透明输出使用插件扩展字段,不开启宿主通用透明背景开关。模型能力仍保存在现有 JSON 字段,无数据库迁移或新端点。

注册邮箱等价检查

公开注册与邮箱绑定的唯一性检查将 gmail.com 和 googlemail.com 视为同一域,移除本地部分的点及 + 后缀。例如 a.b+x@googlemail.com 与 ab@gmail.com 不允许创建两个账号。其他域保留点和 +;现有域名白名单规则保持不变。

邮箱原值仍按原有小写、去首尾空格规则保存。登录、密码重置、验证码收件地址保持精确邮箱匹配,避免既有重复别名账号被合并或错误授权。已存在的重复账号不迁移、不删除、不重新发放注册奖励;它们会阻止相同规范信箱的新注册。写入前在事务内重新检查,以覆盖不同实例使用不同验证码的并发注册。无数据库结构迁移;唯一性由公开注册与绑定的事务入口保证,直接 SQL 写入不属于该约束入口。

分片上传配额

POST /resources/uploads 在接收分片前预留整个文件的日额度和账号容量。文件大小必须严格小于 resourceUploadMB;同一账号最多 32 个未到期会话,跨后端实例共同计数。未满足配额的请求不会创建分片目录。分片和完成请求按会话串行执行;会话过期或已完成返回 404,重新创建会话后可整传重试。

完成上传时以数据库事务提交容量并消费预留,使用既有资源幂等标识。失败释放预留;跨 UTC 日期完成需要通过当天日额度检查。请求与响应字段不变。

存储容量与只读

账号有效文件容量 = 后台全局 storedFileGB + 当前生效的存储扩容(支付购买或管理员赠送,多笔叠加、各自到期)。

GET /api/resources/storage-usage 返回 { usage }:

字段说明
usedBytes / totalBytes已用与有效总容量
baseBytes / purchasedBytes全局基础容量与生效扩容合计
nextExpiresAt最近一笔有期限扩容的到期时间,可为空
overLimitusedBytes > totalBytes
overLimitSince / readOnlyAt首次观察到超额的时间及其 7 天后进入只读的时间
readOnly宽限期已结束且仍超额

该接口每次实时计算,并刷新本实例的只读判定缓存(15 秒),所以用户删除素材或扩容后刷新用量即可解除限制。

超额后的行为:

  • 宽限期内:上传、分片上传、生成资源落库和媒体生成任务准入返回 storage_capacity_exceeded,其它写入照常。
  • 宽限期结束:/api 下的非 GET 请求先经过只读守卫,返回 HTTP 403、code=40302、reason=storage_read_only。放行的请求:所有 GET/HEAD/OPTIONS/DELETE;路径前缀 /auth/、/payments/、/wallet/、/admin/、/settings/、/diagnostics/、/model-catalog/;以及 POST /assets/batch-delete、/assets/batch、/resources/access、/announcements/read、/models/available、/models/:id/quote、/ai/models、/runninghub/*-info、/canvas-projects/:id/edit-lease、/feedbacks、/feedbacks/attachments、/tasks/:id/{cancel,query-provider,recover-media,text-deltas,text-replay-complete}、/agent/runs/:id/cancel、/dramas/:id/pause、/creation-runs/:id/{release,heartbeat}。新增的写接口默认受只读约束,确需放行时在 backend/internal/handler/storage_guard.go 登记。

扩容相关接口:

方法路径行为
GET/api/resources/storage-grants当前用户的扩容记录(含已到期、已撤销),最多 100 条
GET/api/admin/users/:id/storage管理员查看用户有效容量(不写入超额状态)和扩容记录
POST/api/admin/users/:id/storage-grants管理员赠送,请求 { "bytes": 10737418240, "durationDays": 30, "note": "" };bytes 必须是整 GB,durationDays=0 为永久;写管理员审计
POST/api/admin/storage-grants/:id/revoke管理员撤销,请求 { "reason": "线下退款" },原因必填;写管理员审计

存储空间商品与积分商品共用 /api/payments/* 和 /api/admin/payments/products。商品和订单增加 kind/productKind(credits|storage)、storageBytes、storageDurationDays。/payments/providers 与下单不再要求积分功能;积分功能关闭时,/payments/products 只返回存储空间商品,积分商品下单返回功能未开启。商品类型创建后不能修改。

AutoDL 视频画面规格配置

渠道模型的 capabilityConfig.video.resolutions 与 defaultResolution 直接表示所选完整分辨率与默认值,始终生效;不再使用或返回 customSizeEnabled、customScreenSpec。AutoDL v2.3.0 未声明独立比例参数,因此 ratios 为空、defaultRatio 为空字符串,横竖与方形由完整分辨率标签表达。

服务端返回 fixedScreenSpec 供前端展示插件预设;写入时重新读取官方插件的当前工作流规格,不信任客户端传入的固定值。所选列表必须非空且属于插件预设,默认值必须属于所选列表;大小写归一化为插件原始标签。任务创建在价格选择前清空无效的 size、归一化 vquality;自动选择使用配置的默认值,不支持的显式选择被拒绝。价格档可保留全部插件预设,但任务和模型目录只采用所选列表。数据库继续使用现有能力 JSON 字段,无新增表或迁移。

路径前缀是 /api。前后端一起发布,不按 /api/v1、/api/v2 做路径版本。不兼容变更随版本一起改接口和调用方。

机器可读规范:运行中的 GET /api/openapi.yaml,源文件 backend/internal/handler/openapi.yaml。前端 TypeScript 仍走 web/src/services/api 的 http,不从 OpenAPI 生成客户端。

出站模型服务域名解析失败返回 HTTP / code: 502、reason: upstream_dns_failed,不归类为用户参数错误。公开消息不包含上游域名、DNS 服务器或请求 URL;原始解析错误仅保留在内部错误链。此分类不会自动重试、切换渠道或放宽 SSRF 校验;上下文取消仍按取消处理。

模型 HTTP 失败及经过 Provider 错误转换的业务失败,在通用提示后追加经过过滤的 ;上游:具体原因,供任务错误和画布卡片展示内容安全、提示词、尺寸等可操作信息。只读取上游 error / message / msg / detail 消息字段,不回传整个 JSON;消息中的 HTTP(S) URL 隐藏,包含余额、账务、凭据、请求诊断或 HTML 的消息不追加原文,保留通用分类提示。后台响应拦截规则仍对最终用户文案生效;不会修改历史任务错误或改变重试、退款策略。

资源访问与 OSS/CDN 分发

普通上传、分片合并及 URL 导入按内容签名识别 MIME,不以客户端声明或文件扩展名决定媒体类型;无法识别的内容仍可作为附件上传。上传标识对应的 ready/failed 重试均先读取内容,并核对检测后的 MIME、资源类型与大小;不一致返回 409,不覆盖原对象。这是内容判型与元数据一致性校验,不是完整格式解码或内容摘要去重。

非媒体文件(包括可执行脚本的 SVG)通过平台本地读取或代理交付,强制 Content-Disposition: attachment 与 Content-Security-Policy: sandbox; default-src 'none',不向浏览器或访问描述返回其 OSS/CDN 直链。普通图片、音频及视频保持原有媒体分发规则。历史资源不在本次变更中批量重新判型。

资源展示、播放、下载、复制地址和模型输入统一消费资源访问合同,不在客户端通过 direct=1、proxy=1 或存储厂商规则选择出口。

  • POST /api/resources/access 要求登录,请求体为 1–100 项 { resourceId, purpose?, variant? }。purpose 支持 display、copy、download、browser-process、provider-input;variant 支持 original、playback、thumbnail(缩略图未生成前回落原件,fallbackReason 为 thumbnail_not_ready)。每项独立返回 access 或结构化 error。
  • access.delivery 为 cdn、origin、platform-local 或 platform-proxy。媒体配置了可用 CDN 时返回 CDN 地址;OSS 源站签名是源站直连,不等于 CDN。媒体的 requireCDN 开启且 CDN 鉴权不完整时失败,不静默回源。浏览器媒体的展示、复制、下载和本地处理不使用 platform-proxy,正文由 OSS/CDN 直接发送;allowPrivateProxy 允许服务端在 provider-input 场景读取私有媒体源站。非媒体附件始终通过平台控制响应头,不受上述媒体直连规则影响。
  • expiresAt / refreshAt 只描述当前授权有效期。有效期来自后台「资源与请求策略」(provider-input 与其余签名访问分别配置,默认均为 4 小时);公开 CDN 可没有 expiresAt。签名地址不写入画布或服务端持久化内容。
  • display、copy、browser-process 的源站签名把签名起始时间对齐到有效期 1/4 的时间窗(按 Unix 纪元对齐),过期时间为窗口起点加有效期:同一窗口内重复签发得到逐字节相同的 URL,刷新画布或多实例签发都能命中浏览器 HTTP 缓存;签名同时携带 response-cache-control=private, max-age=<有效期>, immutable,由对象存储回写缓存头。download 与 provider-input 仍按当前时间签发,不带缓存覆盖。
  • GET /api/resources/:id/file 仍是稳定平台入口,负责本地/平台代理字节流,可直连的云媒体返回 307 到 CDN 或对象存储,非媒体附件走平台流;?access=1 只返回访问描述,不打开正文。Range 只影响字节流路径。
  • 公共资源、画布分享、外观、公告、管理员资源和日志媒体复用同一个 HTTP 执行器。公共签名链接受自身到期时间和变体绑定限制,但已签发的外部 URL 不承诺立即撤销。

当前自动签名能力:公开 CDN、七牛私有下载签名和对象存储源站签名。阿里云/腾讯云私有 CDN 签名尚未自动支持,不能仅填写 CDN 域名就宣称已命中 CDN。真实 CORS、Range、CDN 命中率及账单收益必须在实际云环境通过浏览器网络记录和服务端流量观测验收。

AI 拆图视觉规划输入

文本生成任务的 metadata.edit = "layer-planning" 使用图片参考时,服务端从当前用户归属的就绪资源读取实际像素并转换为内嵌图片,清除附带的旧 URL。图片数量与字节数沿用文本模型能力及资源读取上限;不允许其他用户资源或客户端内嵌内容替换资源文件。普通生成的签名 URL 策略保持不变,任务端点、账务与数据库结构沿用已有合同。

画布资源完整性与 operations 写入

PATCH /canvas-projects/:id/operations 每次接受 1–500 条操作。前端超限变更按原顺序分批提交,每批发送前保存准确的请求 ID、请求正文和未提交操作;只有全部确认并读回后,界面才标记整份画布已保存。响应丢失时重放同一批 ID 和正文,依靠服务端事务回执去重;冲突停止在当前批,不放宽字段基线。大变更由多笔事务组成,中途失败可能已有部分批次落库,本地仍保留完整待提交快照与后续操作,不能将部分成功显示成全部完成。

画布 operations 应用前校验当前文档中的资源引用;引用须属于当前用户且处于 ready。引用收集器递归遍历受支持的资源字段,因此 nodes[节点 ID].metadata.videoPreview.storageKey 等嵌套派生封面也会参与检查。维护写入会保留必要的损坏文档审计信息;若资源在事务期间缺失或状态变化,事务回滚,画布 revision 不递增。

HTTP 409 不能一概解释为并发冲突:对象或字段基线冲突使用 reason: conflict;资源完整性失败使用 reason: canvas_history_resources_missing,并在 details 中返回 resourceIds 和 missingResources(含资源 ID、JSON 路径、节点 ID、引用类型及 source)。客户端应按 reason 分流,不解析 msg 猜测原因。普通 operations 不会绕过资源校验或 revision 语义,维护修复也不能把缺失主媒体写回为有效画布。

运维扫描/修复仅处理当前画布中可安全清除的失效 videoPreview;不删除主媒体、不扫描并改写所有维护记录,也不替代备份。使用目标数据库对应的新版本二进制,先在维护窗口备份并执行只读扫描:

CANVAS_BACKEND_DATA_DIR=/明确的数据目录 ./migrate-schema repair-canvas-resources

复核 JSON 报告后,只有 repairable 项可由明确授权的操作员再次运行 repair-canvas-resources --apply。命令要求显式配置 CANVAS_BACKEND_DATA_DIR 或 DATABASE_URL,逐画布按当前 revision CAS 保存并保留维护审计记录;有未解决主媒体/素材绑定问题时以非零退出,不会自动重传或重建节点。发生并发编辑时停止并重新扫描,不能用整批快照替换或绕过 operations 校验。

生成成功后的重试保存

POST /tasks/:id/recover-media 无需请求体。当前用户只能恢复自己有保存恢复记录的失败任务;重入队仍返回原任务 ID,不请求重新生成、不新建计费订单。已排队、运行或完成时幂等返回当前任务。取消任务、已退款订单、任务/账单归属不一致、项目已删除或归档时拒绝恢复。

管理员接口 POST /admin/api-logs/:id/recover-media 按请求日志关联的任务重新排队保存作品(图片、视频、音频),不重新生成、不新建订单,不受用户手动次数限制,并重新给满 20 分钟自动重试窗口;写管理员审计 api_log.recover_task_media。请求体可选 {"refundedBilling": "charge" | "waive"}:原订单已退款时必须提供,charge 在作品保存成功后从可用余额补扣(余额不足会变为负数),waive 保持已退款;订单未退款时忽略该字段,成功后按原订单结算。响应 data.task 为任务摘要。

生成作品直传平台对象存储失败或该存储处于熔断时,作品先保存到服务器并继续登记、结算,任务状态为成功;请求日志依次出现失败的 upload(错误文案附存储错误码,如“上传 OSS 失败(存储返回 SlowDownWrite/503)”)和成功的 local_fallback,后台补传写 offload 阶段日志。GET /admin/storage/stats 的 data.stats.offload 返回待补传数量、字节数、最早时间、最近错误码和本地文件丢失数。

冻结积分自动退回不处理“上游已确认生成作品”的订单:任务带作品恢复记录,或诊断为结果保存失败时跳过,等待作品交付后结算或由管理员处理。

任务详情和摘要新增 mediaStage、canRecoverMedia。后者表示失败任务存在保存恢复记录,实际操作仍校验权限、账单、项目及重试额度。每任务最多手动恢复三次,两次之间至少一分钟,每次手动操作只执行一次保存尝试;接口另受用户维度任务请求限流约束。普通 /tasks/:id/retry 不接受带保存恢复记录的任务,避免误走重新生成。

保存网络故障在原 worker 中最多自动重试四次(15 秒、1 分钟、3 分钟、10 分钟并加抖动;超过 20 分钟不再安排自动重试),使用 next_poll_at 释放 worker,而不是在请求内睡眠。不可重试错误直接保留为保存失败。画布和任务中心显示“作品已生成,正在保存”或“作品保存未完成”,失败按钮为“重试保存”。完成后客户端消费原任务的结果。

请求明细保留上游生成与结果下载的独立状态,新增 upload、local_save、register 内部非计费明细;列表和详情通过 mediaStage 展示交付摘要。recordType=request 排除这些保存明细和下载,all 查看全部阶段,download 仍仅查看下载。视频下载失败不会再覆盖生成请求的成功记录。

此恢复能力接入声明式媒体协议返回 URL/data URL 的任务路径;旧协议自行下载、协议二进制下载端点以及无恢复记录的历史失败任务不自动补录。历史任务需先核实上游结果可用性、资源归属及原账单,再单独制定修复动作。

素材读取合同

素材详情、批量读取、分页列表和用户数据快照统一返回完整素材记录。id、kind、title、分类、状态和主版本以素材表列为准;媒体内容保留在 data 中。项目上传资源转换为素材时即写入该合同,媒体大小使用 data.bytes;只有存储键的媒体仍返回字符串类型的 url(图片为 dataUrl),可以为空,由 storageKey 定位资源。

已有项目上传素材的资源元数据在读取时组装为同一合同,真实大小从已记录的 size 映射为 bytes。该过程不创建新素材 ID,也不为缺少资源定位或真实文件大小的素材伪造有效数据。

素材彻底删除

DELETE /assets/:id 表示用户确认后的彻底删除,不依赖服务端当前记录是否已经同步为 archived。接口仍校验登录用户和素材归属,但不会因画布、任务、项目或历史快照仍有引用而拒绝;事务会删除素材及对应历史资源索引,并通过删除 Outbox 清理未被其他素材复用的物理对象。其他素材共享的同一物理对象继续保留。移入回收站仍通过素材写入接口把状态更新为 archived,不能用 DELETE 代替可恢复归档。

素材页及素材选择器的批量彻底删除、清空回收站统一调用 POST /assets/batch-delete,请求体直接为 JSON 数组(不是 { ids: [...] }):

["asset-1", "asset-2"]
  • 每次 1–1000 项,单个请求体最多 256 KiB;ID 去除首尾空白并去重,空 ID / 非数组返回 400,任一 ID 不存在或不属于当前用户返回 404,整批不删除。
  • 成功信封 data.ids 为请求传入的数组。前端等待服务端成功后一次更新本地集合并冲刷持久化;失败不回退为逐条 DELETE,不先移除本地记录。
  • 整批素材、版本、表现、业务关联、待删资源及对应历史资源索引,与物理删除 Outbox 在同一数据库事务中提交,数据库失败整批回滚。共享检查排除整批被删素材,避免两条互相共享的选中素材残留文件。
  • 物理空间由后台删除队列异步释放,失败保留任务重试;接口成功不代表对象存储已经清理完成。未选中素材使用的资源,以及其他 Resource 行(包括其他用户)复用的物理对象继续保留。
  • 旧画布 / 任务 / 历史快照记录本身保留,但被删媒体链接将失效。此操作不可恢复;没有数据库结构迁移。

密码登录限流

POST /auth/login 保留 IP 总请求限制与十分钟账号请求限制。同一 IP 下,用户名和邮箱解析为同一用户时共享账号计数;不存在的账号仍按去除首尾空白、转小写后的输入计次。错误密码与不存在的账号使用相同的 401 响应,达到限额后均返回 429,不返回限流键或用户身份。计数仍涵盖所有登录请求,成功不清零。

注册服务协议

POST /auth/register 的 JSON 请求必须携带布尔值 acceptedTerms: true,首次创建管理员也适用。缺省、false 或 null 均按未同意处理,在创建用户、消费验证码和创建会话前返回 HTTP 400,提示 请先同意<协议名称>。原有注册开放、邮箱白名单、验证码与限流校验继续生效。

GET /auth/linuxdo/start 可携带 acceptedTerms=true,只有该精确值表示同意。服务端将标志写入一次性的 OAuth state;回调仅在新建 Linux.do 用户时要求该 state 已同意,不从回调参数重新取值。已有 Linux.do 用户可从登录页继续登录,无需传此参数;同意协议不绕过注册关闭策略。

协议名称与正文由管理员在 PATCH /admin/settings/registration 配置,公开读取面是 GET /auth/settings 的 agreementTitle、agreementContent。agreementTitle 未配置时按当前外观品牌名生成 <品牌名>服务协议,因此外观改名后注册页文案同步;接口返回的标题不含书名号,由注册页补《》。agreementContent 支持多段文本,按空行分段渲染,留空时注册页提示“服务协议内容待补充”。

读取注册配置失败(如 JSON 损坏)时,RegistrationAgreement 返回空标题而不是品牌名兜底标题,注册页据此隐藏协议入口并提示可重新读取,避免展示后台并不存在的协议名称;此时错误提示统一回落为“请先同意服务协议”。

PATCH /admin/settings/registration 的两个协议字段为可选指针语义:不提交表示保留已保存内容,提交空串表示明确清空。注册开关与协议正文可以分开保存,单独切换 enabled 不会覆盖协议。当前只校验本次注册的同意标志,不保存长期协议版本或签署凭证。前后端需一起更新,旧客户端缺少同意标志时无法注册。

站点皮肤主按钮

画布配置、更新公告、状态动画包与 Live2D(外观合同版本 11)

外观设置新增 updates: { enabled, currentVersion, releases },由管理员通过 PATCH /admin/settings/appearance 保存;关闭开关保留草稿,但 GET /public/appearance 返回关闭状态与空公告列表,前端恢复内置更新日志。开启时公开版本与内容块,当前版本须对应公告版本。媒体上传、管理员预览、公开引用读取及限制详见 自定义更新公告。JSON 请求上限为 512KB;无数据表迁移。

GET /public/appearance 和管理员 GET /admin/settings/appearance 返回 canvas;管理员通过 PATCH /admin/settings/appearance 随外观配置统一保存。字段为 agentName(1–24 字)、launcherLabel(0–24 字)、panelTitle(1–40 字)、welcomeTitle(1–100 字)、welcomeDescription(0–200 字)、inputPlaceholder(1–160 字)、avatarType(orb|bundle|live2d)、avatarHeight(120–360 整数)、live2dResourceId、live2dEntry、avatarBundleResourceId 和状态到 clip 名的 avatarClipStates。状态键为 idle|queued|running|waiting_approval|completed|failed|cancelled|rejected;状态动画包为空资源 ID 时使用内置 /doll/avatar.json。启用上传包时服务端核验资源归属、ZIP 与每个状态引用的 clip。名称以数据形式投影到每轮 Agent 执行上下文,不提供自由系统提示词配置,也不改变工具权限。

  • POST /admin/settings/appearance/live2d:管理员 multipart file 上传,受上传频率和配额限制。成功信封的 data.model 为 { resourceId, entry },上传不会发布。ZIP 限 128 MiB、256 条目、解压合计 384 MiB、单文件 128 MiB;只接收 model3/MOC3/PNG/JSON,拒绝路径穿越、脚本和外部资源。
  • GET /admin/settings/appearance/live2d/:id/*file:管理员预览,校验资源归属;其他管理员仅能查看当前外观已引用模型。
  • GET /public/appearance/live2d/:id/*file:仅提供当前启用模型的数据文件。停用或更换后旧模型拒绝公开访问。成功为文件内容,失败为错误信封;响应带 nosniff 和私有、需重新验证的缓存策略。
  • POST /admin/settings/appearance/avatar-bundles:管理员 multipart file 导入状态动画 ZIP,受上传频率和配额限制。ZIP 最大 128 MiB、解压总量最大 384 MiB、最多 128 个条目;包含根目录 avatar.json 和其逐一引用的 MP4。成功信封 data.bundle 返回 { resourceId, manifest },不会自动启用。
  • GET /admin/settings/appearance/avatar/:id/*file:上传者可预览未启用包,其他管理员只能预览当前配置引用包中的 manifest 或 MP4。
  • GET /public/appearance/avatar/:id/*file:只提供当前启用动画包中经 manifest 引用的文件;MP4 支持 Range,停用或更换后旧包拒绝公开读取。

管理员资源列表增加 kind=live2d 筛选;引用中的模型禁止删除,清除外观引用后按既有资源删除流程清理。

部署要求与更详细的文件限制见 画布 Agent 外观与 Live2D。

GET /public/appearance 返回当前 activeSkin;管理员通过 GET/PUT /admin/settings/appearance 读取、保存完整主题库。外观合同版本 8 新增 tokens.buttons.light/dark,字段为 mode(solid|gradient)、整数 angle(0–360)、HEX 色值 start/end、hoverStart/hoverEnd、activeStart/activeEnd、foreground。纯色模式使用既有主操作颜色,渐变仅用于主按钮。非法模式、角度、色值或修改锁定经典主题均拒绝保存;整块缺失的旧配置按主题身份补齐。此变更不增加数据表迁移。

模型规格报价

管理员批量调价

POST /admin/channels/:id/models/batch-reprice 接收最终售价及原始价格版本,返回 { code: 0, data: { updated: number }, msg }。仅管理员可调用;单批 1–100 个模型,空 ID、重复 ID 或不属于当前系统渠道的记录整批拒绝。请求结构如下(价格为整数微积分):

{
  "models": [{
    "modelId": "MODEL001",
    "priceVersion": 4,
    "priceTiers": [{
      "id": "TIER001",
      "priceVersion": 8,
      "prices": { "unitPriceMicrocredits": 1370000 }
    }]
  }]
}

弹窗一行一个模型,列为模型展示名、渠道展示名(上游模型 ID)、规格、成本价和销售价输入框;多个规格在模型行内纵向对齐。利润率沿用财务口径:(销售价 − 成本价) / 销售价 × 100%,并非成本加价率。目标利润率为 0–99.99%,最多两位小数;售价按 成本价 / (1 − 目标利润率) 计算,按所选 0–6 位积分小数四舍五入(默认 2 位)。成本 1 积分、目标 20%、保留 2 位时售价为 1.25 积分。修改比例或精度实时计算,也可点击「重新计算」按当前参数覆盖全部手动售价;随后允许逐规格手动修改,保存以最终输入值为准。舍入和手动修改可能使实际利润率偏离目标;售价为零时利润率显示 —。

每个模型必须提交全部已启用规格(模型本身可停用),且每个规格只提交当前计费方式需要的完整 prices 字段:按次/秒为 unitPriceMicrocredits;文本 Token 为 inputTokenPriceMicrocredits、outputTokenPriceMicrocredits、cachedTokenPriceMicrocredits;视频 Token 仅为 outputTokenPriceMicrocredits。缺失、多余、重复、停用规格、null、负数或非整数价格均拒绝。成本和售价配置必须已保存;成本及 Token 售价上限为 1,000,000 积分,按次/秒售价不得超出 JavaScript 安全整数微积分范围。

服务端校验最终售价和模型/规格版本,不重新计算覆盖手工价格。价格快照过期必须刷新后重试。所有模型与价格档在单事务中更新,增加价格版本、失效目录缓存;无效配置、归属错误、超限或并发版本冲突均不部分提交。不改变成本、规格、计费方式和启停状态;未启用规格保持原值。此接口不新增数据表或迁移,不追溯修改已下单的计费快照。

用户模型目录

GET /model-catalog 固定返回 source=system,直接查询所有启用系统渠道下的渠道模型,目录数据在 channels[].models[],models 固定为空数组。不受逻辑模型开关影响,不反查逻辑模型或供应线路。每项的 modelKey 是调用标识,displayName 是一级分组展示名(未设置时归入 未分类),icon 是模型图标键,channelLabel 是二级渠道展示名。后台渠道模型创建与更新请求使用相同的 channelLabel 字段;服务端去除首尾空白后限制为 80 字,空值或与上游模型 ID 相同的值表示不固化该名称,读取时回退到上游模型 ID(默认与上游 ID 保持一致,上游改名时自动跟随)。

渠道模型创建、更新、管理列表、公开目录和会话模型价格信息均使用 description 表达模型描述。服务端去除首尾空白后限制为 500 字,允许空字符串清除描述;无管理员权限或超长内容拒绝保存。创作端二级选项常驻展示该字段,不依赖悬浮 Tooltip;未填写的系统模型不按名称生成宣传文案。

以上模型接口同时使用 tags: [{ text, color }] 传递有序展示标签,例如 [{"text":"限时特价","color":"purple"},{"text":"官方1折","color":"gold"}]。最多 5 个,文字去除首尾空白后为 1–12 字且不可重复;颜色仅接受 purple、blue、green、gold、orange、pink。创建/更新由管理员操作,非法标签拒绝整个保存;提交 tags: [](或省略字段)清空标签。用户模型选择器在对应二级渠道项中常驻展示,标签不参与路由、价格计算或权限判断。

创作端跨渠道按模型 displayName 分组为一级菜单(新导入模型默认 未分类),二级菜单使用每条模型的 channelLabel;未手动设置时与上游模型 ID 保持一致,例如 MiniMax H3 → minimax-h3。选中值继续使用 channelId::modelKey,报价和生成请求保留该渠道身份。展示字段不参与路由、权限及计费。系统渠道的“前台名称/前台显示别名”已移除,管理 API 不再接受或返回 publicAlias。

同一产品在不同渠道中使用相同的模型展示名即可归组,modelKey 可以不同;相同 modelKey 但展示名不同会分为不同组。一级顺序与图标按后台渠道和模型排序首次出现的位置确定,二级保持后台渠道与模型顺序。即使二级展示名相同,也保留独立选项及各自能力、价格,不自动改写历史配置或交换两个展示字段。

GET /admin/references 的 channels[] 同时返回 modelDisplayNames,包含系统渠道已创建模型的一级分组名称(含停用模型)。已设置过展示名的分组保留原值,即使与模型 ID 相同也算作已分组;未设置的模型不进入该列表,避免把模型 ID 当成一级分组。编辑器“模型展示名(一级目录)”汇总去重后提供可搜索下拉,点击即可展开全部已有分组,同时允许输入新分组;保存后刷新引用数据。

以下接口需要登录 Cookie,返回 data.quote,仅校验和报价,不创建任务、账单或冻结余额,不调用供应商生成接口。

接口请求
POST /model-catalog/quote{ channelId, modelKey, intent },仅允许启用的系统渠道与可用模型,复用任务准入、默认参数和价格档选择
POST /models/:id/quote直接提交 intent,按前台逻辑模型当前可用线路和价格策略报价

intent 包含 capability(text/image/video/audio)、operation、inputs(素材类型到数量的映射)和 options(请求规格)。素材数量必须为 0–128 的整数,再按模型能力限制校验。视频规格包括 vquality、size、videoSeconds、布尔值 videoGenerateAudio,显式 false 必须保留。

报价包含 logicalModelId(系统渠道直连为空)、billingMode、quantity、amountMicrocredits、estimated。视频 Token 报价另有 videoTokenEstimate:formulaTokens、reservedTokens、outputWidth、outputHeight、framesPerSecond、outputSeconds、referenceSeconds、referenceDurationEstimated、dimensionsEstimated、reservationMarginPercent。

Token 报价的 estimated 为 true,quantity 是预留 Token 数。参考素材数量不包含时长信息,带参考视频时统一按 15 秒估算并返回 referenceDurationEstimated: true。所有视频协议均支持 Token 定价;10% 预留是平台策略,不是费用上限。成功后优先有效上游用量,无有效用量时按提交的公式快照结算,余量不计入公式费用。账单新增 videoFormulaTokens 及 usageSource(provider / video_formula),公式结算的 usageAvailable 为 false,outputTokens 表示公式数量。算法、配置与异常用量处理见视频 Token 计费。

管理员积分成本

GET/POST /admin/channels/:id/models 与 PATCH /admin/channels/:id/models/:modelId 的 priceTiers[] 增加 costPricing:configured、unitPriceMicrocredits、inputTokenPriceMicrocredits、outputTokenPriceMicrocredits、cachedTokenPriceMicrocredits。成本沿用该价格档的计费方式;按次/秒使用单位价,文本 Token 按输入/输出/缓存分别定价,视频 Token 只使用输出价。价格为整数微积分,范围为 0 到 1000000000000;configured=false 表示未配置,true 且价格为 0 表示显式零成本。

成本保存到下单时的内部账单快照,不受售价倍率或扣费上限影响,后续改价不重算旧订单。历史逻辑模型任务切换备用线路时,成本更新为实际执行线路的快照,独立于用户售价策略。管理员请求列表、详情与 CSV 同时展示销售金额及成本金额;JSON 增加 creditCostConfigured 和可选 creditCostMicrocredits。成功结算后按实际计费数量、Token 用量或视频公式快照核算;未结算、已退回、历史无快照或缺少有效用量时不编造成本金额。

成本字段仅通过经过管理员权限校验的接口显式返回;普通用户的模型目录、会话模型信息、报价、任务与账单 JSON 均不包含成本配置、成本快照或成本金额。底层价格档和账单成本字段禁止 JSON 序列化,不以页面隐藏代替接口隔离。

数据概览不再提供独立的“模型价格配置”入口。GET /admin/analytics/overview 的 kpi、models[] 使用 finance 返回 settledOrders、costedOrders、revenueMicrocredits、costMicrocredits、可空的 profitMicrocredits、profitMargin,取代旧的费用/币种字段。按当前请求日志时间及筛选范围关联已结算订单,以订单 ID 去重并核对用户与实际结算渠道;轮询、下载、未结算、退款和无关联账单请求不计入财务汇总。收入为实际结算积分,成本复用系统模型价格档的订单快照与计费用量,利润为收入减成本,利润率为利润占收入百分比。已知成本可显示部分合计;只要有已结算订单缺失成本,利润和利润率返回 null,不把未知成本视为零。无订单时利润为 null;零收入时利润率为 null。历史无成本快照的订单不按当前价回填。此处为已结算生成订单的积分毛利,不含充值收入或失败请求的上游损耗。

GET /admin/analytics/export.csv 沿用相同口径,CSV 输出收入、成本、利润(整数微积分);重复请求只在该筛选范围内选定的一行记录金额,未知值留空。

上传、工作流结构查询、取消查询和取消请求也不参与财务归属,即使日志被标记为可计费;它们不能代替生成请求承载订单金额。订单是否已结算以账单状态为准,保留人工核账后结算订单的统计能力。

画布保存与 operations 同步

画布写入统一使用 PATCH /canvas-projects/:id/operations。请求只携带对象级或字段级变更,不提交整批画布快照;mutationId 是一次写入的幂等身份,客户端在响应丢失、断网、刷新或后台重试时必须复用同一个 ID 和原始 operations。baseRevision 记录生成请求时的已确认基线版本,用于诊断和响应;字段/对象的 before、expected、beforeValue 是实际并发校验,允许不同字段合并,拒绝同字段覆盖和缺失对象更新。可选顶层字段缺失时必须显式编码为 JSON null;服务端仅对早期客户端省略这些可选字段的 value 或 beforeValue 按 null 兼容处理,并继续执行基线校验。

接口行为
GET /canvas-projects/:id返回当前完整画布及权威 revision;响应包含基于数据库版本的弱 ETag 和 Cache-Control: private, no-cache。请求可用 If-None-Match 做条件读取,版本未变化时返回 304。
PATCH /canvas-projects/:id/operations接收 { mutationId, baseRevision?, operations[] },返回当前项目摘要和 mutationId。支持 create_canvas、create_node、patch_node、remove_node、create_connection、update_connection、remove_connection、set_field。
DELETE /canvas-projects/:id显式删除画布。删除后迟到的更新不会重新创建画布;只有新的 create_canvas 操作可以显式创建同 ID 画布。

create_node、create_connection 和 create_canvas 是唯一的创建语义;更新、删除和连接操作都要求目标存在并通过对象基线校验。节点删除会同步移除关联连接,迟到媒体回写或编辑更新返回结构化冲突,不会复活已删除节点。请求摘要与确定性结果写入 canvas_mutation_receipts,画布内容、版本递增和 receipt 在同一数据库事务完成;同一 mutationId 和相同摘要返回原结果,摘要不同返回 canvas_mutation_id_reused。

前端先把未确认请求和请求基线写入账号隔离的本地 journal,再发 HTTP 请求。journal 保存已确认基线、原始请求、请求生成时的本地投影和创建时间;刷新后只重放原请求,随后把请求之后的新编辑按新的基线生成下一批 operations。服务端返回完整画布时,客户端只合并仍未确认的本地 mutations,视口仍属于本地视图状态,不进入远端 operations。

打开画布时,旧浏览器缓存与云端更新按会话原基线合并,再同时更新编辑器与缓存。不能先把最新云端版本设为基线、再直接提交旧缓存,否则其他设备或 Agent 新增而缓存中没有的节点会被误判为删除。已落盘的明确删除请求仍按原 journal 重放。operations 明确返回字段冲突后,客户端读取最新基线,保留当前本地编辑的字段,合入云端无关变化,再以新 mutationId 提交;响应未知时仍重放原 ID/body。已删除对象不复活,旧任务观察值不能覆盖云端的新任务绑定及较新结果。对于没有可验证内容哈希、又与云端不同的旧缓存,合并前在当前账号的 localforage 中保存最多三份恢复副本(yan:canvas-unverified-recovery:<canvasId>);副本落盘失败时不替换本地内容、不发送推导删除。

云端 Agent、媒体生成终态回写、预演台、创作提交以及 LibTV/TapNow 导入都复用同一 operations 服务。Agent 事件中的 canvasPatch 只是一种有界通知;过大、删除或无法完整表达的变更使用 requiresRefresh: true,客户端重新读取后按未确认请求继续重放。用户界面不提供版本记录、加载云端版、覆盖云端或手动重试入口,不弹出同步报错;失败请求由后台自动重放,未确认时不显示已同步。同账号同输入框并发输入按共同基线的文字增删合并;租约只约束服务端写入,客户端保持可编辑,每批保存确认后释放凭据供另一端接手。存在未确认请求时,先重放 receipt,再合入后续输入,避免响应丢失后重复插入本机文字。

平移和缩放只持久化浏览器视口,不生成远端 operations。保存日志仅记录请求/链路 ID、画布/用户 ID、版本、节点数量变化及 HTTP 状态,不记录正文、提示词或媒体 URL。

operations 的结构化配额在提交事务内实时统计账号的素材/画布数量和 JSON 字节数,不为画布保存扫描任务正文及 API 请求/响应日志。接口请求、响应、容量上限及错误语义均不变;性能排查应分别记录成功写入、同请求重放和冲突请求的耗时,冲突请求提前退出不能代表完整提交耗时。

欢迎页开放配置

GET /public/welcome 无需登录,信封 data 仅包含布尔值 welcomeEnabled,响应禁用缓存。读取失败按标准错误信封返回。

管理员通过 GET/PATCH /admin/settings/features 读取和保存 welcomeEnabled,默认 true,与其他功能开关一起存入 system_settings 的 feature_availability JSON,并记录管理员审计。关闭后新访问 /welcome 的浏览器跳转到 /。

云端 Agent

所有 /agent 路径都需要登录 Cookie。GET /agent/capabilities 返回当前版本(2)、权限模式、上下文范围、技能和工具清单;跨轮历史仍按可恢复事实和发送预算整理,不把某个固定字节数当作模型窗口。Agent 运行由服务端任务 Worker 驱动,浏览器断开不会停止运行。

文本模型能力合同包含 contextWindowTokens(输入加输出的总上下文窗口)和 maxOutputTokens(输出/推理上限)。Agent 每轮根据实际执行的渠道模型计算输入预算,并预留工具 schema、协议包装和恢复空间;逻辑模型按候选文本线路取安全交集,旧配置或能力解析失败回退 128K / 16K。1M 模型可通过后台能力配置使用更大输入预算。前后端的 Token 估算用于提前压缩和拒绝病态请求,最终仍以上游 tokenizer 和响应为准。运行 checkpoint 的 512KB 持久化上限只约束恢复状态,不限制模型上下文窗口。

  • POST /agent/runs 创建一轮;POST /agent/runs/:id/messages 以上一轮为父节点创建新的不可变轮次,两者返回 { run },追加后必须使用新的 run.id 订阅。
  • GET /agent/conversations?canvasId=&limit=100&before= 只读查询当前用户在该画布中参与过的对话,返回 { items, nextCursor? }。每项包含 id(现有 conversationId)、canvasId、title、latestRunId、status、revision、cleanupPending 和时间;按最新轮 createdAt、id 倒序。对话换绑画布后,目录摘要仍取整个对话的最新轮次,不能使用旧画布内的最后一轮。画布必须属于当前用户。
  • GET /agent/conversations/:id/runs?limit=100&after= 返回 { items, nextCursor? },按 createdAt、id 升序恢复同一用户、同一对话的全部轮次,包括跨页面的轮次。两处 limit 为 1–100,分页游标直接回传服务端的 nextCursor。运行视图增加 conversationId、userPrompt、idempotencyKey,供另一设备恢复用户消息并去重;每轮仍只返回一页事件。首次恢复用 GET /agent/runs/:id?fromStart=true&sinceSeq=0&eventLimit=500 从第一条事件开始,再以 latestSeq 作为后续 sinceSeq,直到追平 eventCount;默认不带 fromStart 的尾部窗口行为不变。客户端已恢复过某轮时,应把本机从 seq=1 连续的事件前缀与列表尾部窗口合并:合并后追平 eventCount 就不再请求,否则从前缀末尾按 sinceSeq 增量读取,不能因为尾部窗口不从 1 开始而回到 sinceSeq=0。读取接口不创建模型或媒体任务。
  • GET /agent/conversations/:id 返回单个对话的目录项(字段同上),只读控制列、不载入事件,latestRunId/status/revision/cleanupPending 取该对话全局最新一轮;会话不存在或不属于当前用户返回 404。前端轮询时,已选中但不在当前画布目录里的对话用它判断是否变化,未变化就复用本机快照,不再每轮重读全部轮次。
  • 同一对话只允许从最新终态轮次接续。另一设备已经提交后续轮次时,旧父轮新请求返回 409,reason: agent_conversation_stale_parent,客户端先同步历史,不能自动重发。该检查与任务创建、积分预留同事务串行,覆盖新子轮尚未保存执行检查点的间隙;原请求的幂等回放仍先执行,画布独占和防重复生成规则保持。
  • POST /agent/runs/:id/interjections 向正在运行的轮次插入一句话,不新建轮次、不产生新的扣费身份。请求 { text, messageId },text 最多 4000 字符;同一 messageId 幂等。插话在下一次模型调用前注入;运行已结束返回 409,前端应回落为新一轮发送。步数耗尽或运行失败时发出 user_interjection_dropped,不会静默吞掉,也不会自动替用户开新一轮。
  • 请求字段:canvasId(空字符串为未绑定画布的对话)、prompt、受管 logicalModelId 或 channelId + channelModelKey、permissionMode: "read_only" | "request_approval" | "auto"、reasoningMode、profileRevision、skillIds、contextScope: [] | ["canvas"]、focusNodeIds(最多 8 个)、focusNodeOmittedCount、budget: { maxCredits, maxGenerationTasks, maxVideoSeconds, maxSteps } 和 idempotencyKey。前端多选超过 8 个时保留前 8 个并报告省略数量,不清空全部焦点;计数只提供事实,不授予访问权限。未绑定画布时上下文与焦点必须为空。不接受密钥、自定义 URL、客户端历史或未知字段。
  • 同用户同幂等键及相同请求返回同一运行;不同请求复用键返回 409。失败后重试同一提交应保留键。上一轮未完成时禁止追加;已完成、失败、取消或已拒绝的终态轮次作为不可变历史读取,即使其冻结的策略或节点能力版本早于当前服务,也可用于创建采用当前执行合同的新一轮,父运行不会被改写。旧版合同下仍活动或待审批的运行不得恢复执行。历史只保留最近 10 轮用户目标;运行历史与跨轮摘要仍受独立的持久化 JSON 保护,超限先丢掉更早的可重读正文并保留结构化事实,单轮仍超出当前模型能力预算时才提示缩小范围或新建对话;历史结构损坏返回明确 409,不投影为通用 500。
  • 续轮交接是一条有界结构化事实帧({ source: "server", kind: "run_handoff", parentRunId, status, failureReason?, submittedTaskIds?, canvasChanges?, omittedCanvasChanges?, rejectedAction?, authority })。上一轮状态为 rejected 时,rejectedAction 带被拒绝的工具名、审批说明(截 200 字符)、用户拒绝理由(截 300 字符)和"不要原样再次申请"的说明,让模型换方向而不是重复申请;画布改动取自上一轮真实的 canvas_updated 事件,最多 12 组(operation、去重后的 nodeId/title、actionCount),超出的组数只记 omittedCanvasChanges,不静默丢弃;帧还带 authority 明写"执行事实交接,不是重放写入、重复提交任务或扩大权限的授权"。帧按字节封顶(单个 nodeId 截 64 字符、整帧 8 KiB,超限按最近改动优先裁剪),失败原因沿用 cloudAgentSafeUserMessage 的脱敏口径,不合格退回固定说法。身份由服务端字段 agentContextSource=continuation 承载并要求"来源元数据 + 【运行状态】 前缀 + JSON source=server/kind=run_handoff"三者同时成立,因此用户逐字复制帧正文也拿不到该身份;升级前落库、没有来源字段的旧散文交接只在历史裁剪与轮次计数上按"紧跟上一轮 assistant 回复 + 旧版文案前缀"识别,该判据不影响 canonical 身份。
  • 新一轮只沿用状态为 completed 的上一轮 Pi 会话快照,且该快照必须能修复成合法的工具调用历史。上一轮失败、取消或被拒绝时,新一轮从文本历史和 run_handoff 帧开始,不继承停在半批工具调用或"等待审批"占位上的会话;帧里的状态、失败原因和 submittedTaskIds 用来避免重复提交。
  • 每次模型请求前,服务端修复工具调用与结果的配对,不因历史形状让整轮失败。具体规则:缺结果的调用优先补服务端已保存的真实回执(只认该调用声明之后、同工具名的结果),否则补 errorClass: approval_deferred 的"未执行"结果;"等待审批"或"未执行"占位在服务端已有真实回执时换成回执;同一调用的重复结果只保留一份,真实结果优先于占位;找不到调用的结果改为 orphan_tool_results 运行状态数据消息;调用 ID 缺失或别名冲突的调用被移除。运行时历史仍无法使用时退回服务端历史。
  • POST /agent/runs/:id/approvals/:approvalId/decision 的 { decision, reason? } 可在批准图片生成时附带 mediaSettings: { logicalModelId? | channelId? + channelModelKey?, size, quality }。仅替换本次模型选择、画幅和质量,不允许修改目标、提示词、引用、模式或快照。服务端先无收费预检,失败保留待审批状态;成功原子更新审批调用、哈希及预览,提交任务时再次校验内容、资源归属、模型能力、价格与累计预算。相同参数重复批准幂等,不同参数复用已决定审批返回 409。
  • 审批拒绝是独立终态 rejected,表示本次操作未执行;不会生成 tool_failed、继续调用模型、写入画布、提交生成任务或扣费。拒绝后的草稿按既有草稿策略保留,用户可在下一轮说明修改方向。
  • 创建和追加消息可选择 reasoningMode,默认 off;auto/deep 为服务端行为策略与模型推理配置的选择,不等于允许暴露内部思维链。供应商能力由后端校验;声明式 Agent 渠道不支持时明确报错。推理可能增加耗时和费用。
  • Cloud Agent 与首页创造的规划请求使用自动工具选择。OpenAI Chat Completion 兼容协议会把显式 auto 归一化为省略 tool_choice,沿用协议默认的自动选择语义;显式开启思考模式时还会在首个上游请求前移除 required 或指定函数对象。非思考模式仍保留艺术点评、提示词优化等结构化任务的强制函数选择。若上游隐式开启思考而本地未声明,仍按 required → auto → 省略字段做兼容重试。
  • GET /agent/profile?scope=user|project|canvas&projectId=...&canvasId=... 按作用域读取生效的用户 → 项目 → 画布偏好;PATCH /agent/profile 用 { scope, projectId?, canvasId?, content, revision } 更新单层(首次 revision 为 0,冲突返回 409)。读取返回各层独立数字 revision,以及合并文档的 revision/hash;新轮次携带合并 profileRevision,发生变化拒绝创建,已运行轮次仍用提交时固定的快照。system prompt 只包含层级、revision、hash 和长度清单,正文保存在运行快照中;本轮快照有层时才提供 agent_profile_read,只读清单中存在的层。没有偏好层时工具清单不含该工具。偏好仅是非授权数据,不能扩展工具、节点或预算,不能绕过审批;最多 12000 字符/层。
  • 行为规则唯一来源是编译进后端的 agent-system-policy.md v17;不再 embed 旧操作合同或独立媒体策略。媒体 ID/版本/哈希从同一系统合同派生,仅服务于冻结校验。画布能力和工具 Policy 仍由代码强制执行;新生成模式还须接入共享任务适配器。用户目标、画布内容和经验以动态消息提供,不写入稳定 system 前缀。
  • 已安装且属于当前用户的技能在创建时只保存版本、哈希和文本文件路径,不预存全部正文,也不按所有选中技能正文合计 128KB 拒绝创建。skill_read_file 按需读取 SKILL.md 或必要引用,支持字符偏移 offset(默认 0),每页最多 12000 字符,返回 hasMore / nextOffset。读取时重新验证用户权限、启用状态和版本;技能更新后拒绝混读,需新开一轮。技能是不可信数据,不能创造工具、改变权限或覆盖系统规则。
  • Agent 按实际文本模型的 contextWindowTokens 与 maxOutputTokens 计算输入预算,并在达到该模型预算的压缩阈值时移除较早工具结果中可重新读取的正文,保留工具调用/结果配对、用户原始要求、生成回执及最近完整工具轮次,并允许重新读取。此机制是确定性正文卸载,不是 LLM 语义摘要;运行 checkpoint 的持久化上限与模型上下文窗口独立。技能读取事件只保存回执,不复制正文,正文页只进入模型对话。
  • 达到同一条压缩线时整轮不再直接判死:Agent 先暂停步进循环,用一次独立模型调用(任务操作名 cloud_agent_context_compaction,按单步墙钟计时但不占本轮的模型调用步数)把历史压成结构化检查点,再以 Resume 用压缩后的上下文继续本轮;同一轮最多压缩 64 次,压完仍超过输入预算才回到原有的预算失败路径。检查点是固定字段的 JSON(version 为 1,字段为 historySummary、scriptDesign、operationHistory、pendingTasks、currentWork、nextStep、decisions、constraints、userPreferences、compactedTurnCount),未知字段一律拒绝,超过 64KiB 判为不合格。压缩后的上下文是「检查点帧 + 确认回执 + 最近 2 对完整对话」:不以 tool 回执或带工具调用的 assistant 开头,检查点帧不计为用户轮次,续轮交接不会再重复追加本轮的用户要求与回复。渠道没有配置模型上下文窗口时,判据退回既有的跨轮历史闸门(cloudAgentHistoryKeepRounds 轮与 cloudAgentHistoryMaxBytes 字节)。
  • 压缩过程对外只有两个事件:context_compaction_requested 带触发读数(basis 为 tokens/bytes、projectedTokens、usableInputTokens、compactAtTokens、pressureRatio、budgetSource、sourceBytes、turnCount),context_compacted 带 mode(model 为压缩模型输出,fallback 为服务端保底检查点)、resume、historyMessages、compactedTurnCount、droppedTurns,降级时附 reason。压缩模型失败、输出不合规、或本轮在压缩期间被取消/失败,服务端都落保底检查点并写 mode=fallback;终态轮次的收尾只落检查点与事件,不改写已经失败或已取消的状态。
  • 每次普通 Agent 步进模型调用发出之前都会落一条 context_pressure 事件(schemaVersion: 2、phase: "before_request"),把"这次请求装了多少、离装不下还有多远"作为可观测读数下发,requestId 是本次请求的任务 ID。主读数是 projectedNextInputTokens:有可采信的上游实测锚点时等于"锚点实测 + 本地估算的有符号增量"(tokenSource / measurementSource 为 provider,tokenScale 给出实测÷估算的换算比例);没有实测时退回纯本地估算(tokenSource 为 estimate)。两把尺子各有自己的名字、谁也不覆盖谁:estimatedInputTokens(readingScope: "next_request"、estimateMethod: "local_v1")永远是本地下一次请求的估算,pressureTokens / providerUsage(providerMeasurementScope: "previous_request")永远是上游对上一次请求的实测,消费方不得把两者画成一条曲线。本读数只增加可观测性:请求准入与正文裁剪的既有判据不变。
  • 上游实测锚点的采信与治理:上游没有回 usage 的调用(usage_available=false)不定锚、不猜用量,读数继续用估算;只有"模型调用"这一步能配锚(媒体任务发的是另一份信封,operation 不匹配即跳过)。上游实测与本地估算差出一个量级时不采信(比值低于 0.5× 或高于 2×,通常是换了模型或计量口径),事件给 anchorRejected 说明原因并继续用估算。锚点可以跨步沿用,但换模型、换线路、换系统提示或工具 schema、换模型窗口,以及超过 3 步未刷新(anchor.ageSteps)都会作废:真机上出现过锚点被冻结、估算已经涨了 50% 而读数还停在老值的情况。
  • 上下文用量按 system、tools、messages 分别解释:策略与稳定能力合同属于 system,画布目录、运行观察、相关经验及工具结果属于 messages。相关经验不再登记为 system 末尾的个人记忆段,也不应与上一请求的供应商实测混为同一读数。旧版本事件可以保留其原始分段,客户端不得据此把动态经验重新拼进系统提示。
  • 窗口口径由 contextWindowTokens / reservedOutputTokens / overheadTokens / inputBudgetTokens / usableInputTokens / budgetSource 给出:输入预算 = 上下文窗口 − 输出预留 − overhead,其中 overhead 是工具 schema、协议包装与兜底轮的比例预留(窗口的 4%,夹在 4096–32768),compactAtTokens 是既有的"可重读正文就地裁剪"触发线(输入预算的 85%),budgetSource 说明这份窗口来自渠道模型还是逻辑模型的线路交集。没有解析到模型自己声明的窗口时不下发这些字段、也不给占用率(modelLimitConfigured: false):窗口未知就说未知,不拿兜底默认窗口冒充真实能力编一个百分比。
  • context_transition 事件说明"读数为什么变":window_resolved(窗口从未确认变为已确认,读数改用窗口口径,不是上下文变少)、model_changed / route_changed / window_changed(锚点因换模型、换线路或换窗口而作废;窗口变化另带 before / after 的窗口读数)。它只解释口径与锚点状态,不携带任何授权语义,也不改变执行边界。
  • 画布 Agent 的看图能力:当且仅当本轮渠道模型的能力合同声明了图片输入(text.references.maxImages > 0)时,额外暴露 canvas_inspect_image(nodeId, refresh?)。工具校验图片节点、资源归属、就绪状态、媒体类型和模型文件大小限制;检查点与任务输入只保存 resource:ID 占位及授权参考清单。worker 复用资源读取链路,在内存中将占位替换为真实图片数据后展开模型协议,不持久化 base64 或签名 URL,不依赖 CANVAS_PUBLIC_BASE_URL,不接受任意外部地址。多图在本批所有 tool 回执后统一附送;图片保留最近 3 个工具轮次,发给模型时还按数量上限保留最新图片,本批超限则明确要求分批查看。同一张图一轮内附送两次后只回执文字,确需重看时传 refresh=true。工具成功不等于识别成功,普通模型正文不再自动写入 visualNote,旧 inspected 标记及观察不跨轮继承;visualIdentity=unknown 表示没有已确认缓存,不表示模型不能查看随附图片。画面内文字仍按不可信数据处理;资源读取失败则真实失败,不凭标题推断画面。其他需要公网 URL 的生成协议仍遵循各自存储配置合同。
  • 平台共有 29 个注册工具,GET /agent/capabilities 返回当前全集;每轮按偏好快照、技能、权限、画布上下文和视觉能力裁剪。定义、Policy 与执行绑定在统一 Registry 汇合,未知或漏绑定条目拒绝执行。8 个读取工具的参数结构同时生成 schema 并执行严格解码:agent_profile_read、canvas_get_state、canvas_read_text、canvas_read_batch_table、canvas_read_storyboard、skill_read_file、task_get、image_text_detect。写工具仍需 contextScope: ["canvas"],read_only 不授予画布写入或 remember_lesson;不开放 shell、任意网络和外部 MCP。
  • plan_update 维护本轮结构化待办清单(整表替换,最多 20 项),跨轮继承未完成项,并在界面以 PlanBar 展示。权限为 request_approval 时,首次建立至少 2 项的清单会先进入审批;auto 直接写入清单。新一轮的第一次模型请求就会带上继承的清单。模型在没有工具调用且清单仍有未完成项时,服务端会催办并要求继续调用工具;这是软约束,不验证工作是否真的完成,也不会在没有清单时强行催建。
  • ask_user 给出一个问题与 2–6 个候选项后结束本轮,界面弹出选项面板;用户点选或输入后作为新一轮继续。运行中也可以用插话直接回答。
  • recall_lessons / remember_lesson 只操作当前用户的个人经验。每步自动选择少量相关已批准经验作为动态数据,remember_lesson 先暂存运行候选,整轮成功后才进入待审;来源证据不等于经验质量验证。用户可在记忆设置中批准/编辑/导入导出,压缩则必须另走提案审批与恢复版本合同,详见本页“用户 Agent 记忆”。
  • canvas_list_node_types 从服务端节点能力 registry 返回当前可用类型;可更新节点的 updateFields 同时包含字段类型、最大字符数和字段语义。模型不能从前端渲染器、技能或偏好文档推断额外能力。
  • canvas_apply_ops 必须带 snapshotHash,每批最多 20 项;add_node 可省略坐标:服务端按画布内容落位(优先贴合本批连线的对端,否则追加到同泳道之后并避让已有节点),不再落到原点重叠;update_node 的 patch 额外支持 x/y(坐标绝对值上限 1e6)用于微调单个节点。支持新增文本、Markdown、图片、视频、音频、分组与分镜表节点,以及 connect_nodes 引用连线。媒体占位节点的 content 是提示词,不是媒体 URL;只建节点不提交收费生成。update_node 的字段映射完全由能力 registry 决定:文本 / Markdown 可改标题和正文,图片 / 视频 / 音频可改标题和下一次生成使用的提示词草稿;媒体更新参数中的 content 写入 metadata.composerContent,不会覆盖媒体结果 metadata.content、已提交提示词 metadata.prompt、任务状态或引用状态。锁定节点、删除、任意元数据写入、自连接、重复边与不存在的端点仍被服务端拒绝。
  • canvas_arrange_nodes 只改节点坐标:snapshotHash 必需,mode 取 auto(有引用连线按依赖分层、否则按媒体类型分区)/flow/byType/row/column/grid,groups 把节点按给定顺序排成横向位置带(仅位置分带,不创建容器、不改层级),align 做对齐或等距分布,gap 调整组间距,dryRun 只预演。nodeIds 省略时整理全部可整理节点,锁定节点、背板等容器节点、隐藏批次子节点与已归属背板的子节点保持不动并在回执中说明;一次最多 50 个节点。整理与 add_node 的自动落位共用服务端布局实现(backend/internal/canvas/layout),与前端“自动整理节点”使用同一套常量与算法。
  • model_list 复用生成面板的当前模型目录。生成前传 mode 和实际 referenceNodeIds(无参考传空数组),服务端读取当前用户画布内的就绪资源,按真实素材类型、数量及生成操作构建意图并调用统一能力匹配,不让多图生视频落入仅文生视频候选;返回 intent 供核对。空参数仍可浏览完整目录;空候选不降级到不匹配模型。每项的 selection 是 logicalModelId 或 channelId + channelModelKey,工具调用必须原样选择一种,不猜 ID 或混用;时长、画幅、音频、价格仍需核对返回配置,任务准入再次强校验。
  • generate_media 支持图片 / 视频 / 音频,必填 mode、完整 prompt、有序 referenceNodeIds(无参考传空数组)。续用草稿传真实 nodeId,新建可省略,执行器按运行和工具调用身份生成稳定 ID;title 省略时保留草稿原名或使用默认名称。执行器创建草稿和连线,无需先调用通用画布写入工具。snapshotHash 可省略,由服务端取当前内容快照。size、imageResolution、quality、视频 durationSeconds / videoGenerateAudio 默认由已有草稿或所选模型合同解析,图片 / 音频时长为 0。sourceNodeId 仅接文本 / 镜头提示词节点,媒体放 referenceNodeIds;引用从当前账号资源解析并校验归属、类型、就绪状态和模型能力,不接受 URL。图片仅接图片参考;视频按能力接图片、视频、音频;音频当前仅支持文本输入。
  • configurationChange 可选,形如 { "fields": ["size"], "userInstruction": "改成9:16" },对应值仍填顶层字段。仅允许 model、size、quality、imageResolution、durationSeconds、videoGenerateAudio,字段须有值且符合模式;未列出的已有配置继续继承。引用必须逐字来自最新真实用户消息,不能引用历史、工具结果或生成提示词。该结构只表示变更提案来源,不能代替收费授权;实际配置和引用依据展示在原有生成审批中,批准后才提交。用户直接编辑审批配置仍严格校验,不经过 LLM 提示规范化。
  • 参数选择与收费授权分开:用户明确说“随便 / 你决定 / 按默认”时,Agent 可从当前目录选择能力及预算匹配的模型,优先用户最近明确使用的可用模型,其次价格清楚的低成本选项;支持时沿用参考图原始比例,否则选目录默认,时长用目录默认。未要求音频且模型不支持时用默片。用户接受已推荐方案后不再要求特定确认口令;仅真正缺失或冲突的阻塞项一次性询问。本轮已有目录结果应复用。以上是 Agent 的参数选择策略,服务端仍强校验具体规格,不提供绕过预算 / 能力校验的默认兜底,也不绕过独立收费审批。
  • 媒体参数先进行无任务、无预留账单的准入校验,再在同一事务创建草稿节点、来源 / 资产连线及待审批检查点;approval_requested 和运行中的 approval 携带目录真实 modelName。用户批准后才在事务中绑定任务、创建预留账单并更新检查点;拒绝或取消保留未生成草稿,不提交媒体任务。nodeId 可续用同类型、未锁定、idle、无任务且无媒体结果的空白占位节点或当前运行草稿;跨轮 Agent 草稿还要求原运行属于同一用户和画布、已结束且清理完成。续用必须重新读取快照并重新审批,不能复用旧审批、覆盖已有任务或成品,不能通过循环新建节点绕过限制。画布读取的 generationDraft 提示未提交、原运行状态、是否仍在清理及仍需审批;写入仍重新强校验。已明确审批的规格不得被模型默认值静默替换。媒体终态回写原节点;已删除或已换任务的节点不自动复活,结果仍可从任务中心查看。
  • generate_media.prompt 使用 @图片1、@视频1、@音频1 引用素材,各类型按 referenceNodeIds 的出现顺序独立编号,文本来源不占媒体编号。角色主图属于图片序列,与普通图片共享编号;编辑器显示时保留 @角色N 前缀,但 N 仍对应实际图片位置,例如角色、场景图、角色依次显示为 @角色1、@图片2、@角色3,任务使用 @图片1、@图片2、@图片3。已有标签保留;遗漏的画布素材以“素材名称:@标签”补入 【资产参考】,不推断素材用途。节点入边同步排列,草稿、审批和实际任务的标签对应同一组参考资源,编辑器与任务仅转换显示前缀。未绑定的媒体编号和补齐后超过16000字符的提示词会在提交前被拒绝,模型自身的提示词长度限制仍由任务准入校验。临时标注图没有持久画布节点,不自动添加无法解析的编辑器标签;历史节点不自动改写。
  • Agent 的 snapshotHash 忽略顶层 viewport、updatedAt。canvas_get_state 另返回 mediaSnapshotHash,供 generate_media.snapshotHash 使用:仅比较节点与连线,节点忽略 position、width、height、createdAt、updatedAt,保留全部业务字段及未知 metadata。生成草稿审批也固定该哈希,移动、调整显示尺寸、自动补时间戳和保存聊天记录不会使生成审批失效,执行保留最新展示信息;提示词、素材、锁定状态、任务绑定和连线变化仍需重新读取并审批。画布修改与撤销继续使用完整内容哈希,数据库写入保持完整快照 CAS。发布前已按旧算法生成的待审批内容可能要求重新读取和审批,不自动替换旧快照重提任务。
  • canvas_get_state.nodes[].outputReference 的 ready / issue 仅表示该节点产物能否作为其他生成的参考,不诊断本节点的生成输入。generation 返回当前画布、当前用户所属任务的 taskId、taskStatus 和安全的失败 / 取消 error;无任务绑定为 not_submitted,无法验证归属或读取失败为 unavailable。节点无法作为草稿提交时返回 submitBlockedReason / submitBlockedIssue,与写路径共用节点准入规则;未被阻止不等于已获授权,草稿所属运行和审批仍在提交时校验。task_get 同样返回安全错误详情。generate_media 准入失败可返回 snapshot_conflict、task_bound、target_locked 等结构化 reason 和 nodeId,不自动选择新节点或重新生成。
  • 模型目录解析后的规格与申请值不同时,返回 reason: invalid_tool_arguments 及对应 field(videoSeconds 映射为 durationSeconds,视频清晰度映射为 quality),消息给出申请值和解析值,由 Agent 修正后重新申请审批;同一工具每轮最多 3 次失败尝试,失败阶段不创建任务和订单。比例按预设换算成同一画幅的像素(例如 16:9 → 1824x1024,宽高比误差不超过 2%)不算规格变化;像素换回比例仍按不一致处理。旧的 quality 档位被解析为同值 imageResolution 时不报不一致。
  • 用户批准后、提交任务前,若报价过期、生成依赖或角色卡版本变化、解析规格或价格与批准时不同,返回 reason: approval_stale、errorClass: state_conflict、taskSubmitted: false。此时没有任务和订单,整轮继续,Agent 需重新调用并再次等待审批;这类失败计入同一工具的 3 次上限。恢复提示会说明提交前未通过,不再提示"已执行一次"。批准后解析结果的哈希与批准时一致时,不再逐字段比较批准参数。
  • task_get / generation 的执行状态与回写状态分开:succeeded 不返回执行 error;回写失败仍通过 writebackOutcome / writebackError 暴露,不能因生成成功而隐藏。失败 / 取消任务保留脱敏错误,缺少详情不能伪装成成功。Agent 文本步骤的持久诊断为 upstream_dns_failed 时,run_failed.reason 同步该分类并明确提示检查渠道域名及后端 DNS,不自动重跑。
  • 模型选择将 model_list.selection 展开到顶层:非空 logicalModelId,或完整非空 channelId + channelModelKey,不得混用;未使用字段可省略或传空字符串,不能传 null 或纯空白。generate_media 完全省略选择字段时,仅允许继承草稿或本轮最新同模式、同参考节点查询的唯一候选;最新查询失败、无候选、多候选或存在未查询的临时参考时不补模型。image_layer_split 仍要求显式选择。显式选择先检查结构,省略时在解析草稿和目录后检查,均早于创建草稿、审批和计费。错误返回 reason: invalid_tool_arguments、field、issue 和本轮工具 parameters。补齐后仍校验目录、能力、权限、价格和预算,不猜模型、不切换渠道、不吞掉未知字段。
  • canvas_apply_ops 创建节点、编辑提示词或连线不会提交生成任务,也不会改变已提交任务的输入;收费生成独立使用 generate_media。操作参数错误按数组项返回 field(例如 ops[0].snapshotHash)和 issue(例如 unexpected_field、type_mismatch、required),并附本轮工具 schema;不把未知字段误报成缺少其他必填字段,不回显任意参数键和值。
  • read_only 只允许读取;request_approval 对写画布和媒体生成暂停为 waiting_approval;auto 可在预算内执行已授权画布操作,但图片 / 视频生成始终独立审批,不能绕过。maxGenerationTasks / maxVideoSeconds 为非负整数,0 表示该项不设业务上限,正数按本轮累计执行;不再固定 8 次模型调用、8 个生成任务或 120 秒视频。积分上限仍必填且大于 0,实际报价在任务创建与预留积分前校验,时长必须符合所选模型的实际能力。失败事件保留具体错误和已有任务的 taskId、nodeId、status;媒体结果的 phase 区分 admission 与 completion,taskSubmitted 表达是否已提交。已提交的收费生成失败作为工具错误交回模型,整轮保持可继续,不得自动重试收费生成;只有生成结果无法回写画布时才把整轮标为 failed。模型应报告错误,不能自行删减提示词。 budget.maxSteps=0 表示不设固定模型步数上限,正数按本轮累计且有效上限为 9999;Pi 与 Go 共用此值,权限投影不能仅凭画布归属开启写入。
  • 使用画布工具时必须绑定当前用户已保存的画布;平台级对话不注入画布目录。首轮目录是独立动态消息,最多 120 个节点、约 24 KiB;全局项只含 ID、类型、截断标题及能力标记。明确选中的最多 8 个焦点先列入,附带至多 240 个 Unicode 字符的正文/提示词摘要、已记录的媒体/任务状态与一跳邻居;另选中但未附带的数量以 omittedFocusCount 说明。摘要不等于资源就绪证明,正文和资源事实仍需语义工具读取。Pi 不再额外注入 CanvasIntelligence 的聚类、情感、建议或任意 metadata。canvas_get_state 支持分页、最多 8 个节点精读及关联子图,保留真实 ID、结构化行和截断标记;名称不等于视觉识别。
  • canvas_get_state 接受 offset、connectionOffset、storyboardOffset、nodeIds、focusNodeIds、depth、includeRelated;分页偏移非负,两个 ID 数组互斥且最多 8 项。depth(0–3,默认 1)和 includeRelated 仅随焦点使用且互斥。读取目录可传 {},精读用返回的真实 ID;画布由运行绑定,不接受额外 canvasId、limit 或 page。JSON 非对象、多对象、未知字段和类型错误按现有参数纠错回执拒绝,不静默丢字段。
  • GET /agent/runs/:id 返回运行信息;POST /agent/runs/:id/cancel 取消当前运行及活动子任务;POST /agent/runs/:id/approvals/:approvalId/decision 接受 {"decision":"approve"|"reject","reason":"..."},审批决定幂等且服务端校验归属。
  • GET /agent/skills/usage 需要登录,只聚合当前用户最近最多 200 次 Agent 运行的日志;返回技能启用次数、检索命中、成功/失败读取、总纲/卡片读取和最多 5 条最常读卡片路径,不返回技能正文。损坏的日志会返回错误而非静默给出不完整统计。
  • 运行详情的事件分页:事件全量在 cloud_agent_event_records(append-only,主键 run_id + sequence),运行详情默认只返回尾部一窗(最新 40 条),并同时给出四个位置字段:eventSeqBase(本次返回的首条事件之前已入库的条数,不变量是 events[i].seq == eventSeqBase + i + 1)、eventCount(该轮次累计事件条数)、latestSeq(本次返回的最后一条序号,可直接作为下次 sinceSeq)、eventsTruncated(还有更早的记录没随本次返回)。不返回事件时 eventsTruncated 为 false,eventSeqBase 退回内存窗口在整条日志里的偏移。
  • 运行详情查询参数:?sinceSeq=<seq> 只返回该序号之后的事件(增量 / 断线重连,单次最多 500 条,按 seq 升序);?eventLimit=<n> 覆盖默认页大小,取值 1–500,大于窗口时由服务端从事件表往前补页。两个参数都必须是整数,越界或非法返回 400;游标追平或越界时返回空 events 而不是报错。读取一律走 (run_id, sequence) 的 keyset 分页(sequence > ? ORDER BY sequence LIMIT ?),不用 offset,也不会为这些字段载入整条日志。升级前把事件放在 state_json 里的旧轮次在事件表中没有行,读取回退到内存窗口,行为与升级前一致。
  • 首次通过取消接口取消运行时记录一次 run_cancelled 事件,source: user_request 表示来自用户取消 API,并携带当时的 activeTaskId / mediaTaskId;此事件表示取消请求已接收,关联任务最终状态以任务查询及收尾状态为准。运行记录损坏或超限时优先保证取消和后台收尾,事件无法保存会记录服务端告警;旧导出日志不能据此追溯取消发起来源。
  • GET /agent/runs/:id/events 返回 agent_event 和首次 / 状态版本变化时的紧凑 run_snapshot SSE;无变化时只发送 heartbeat。事件 Seq 在运行内递增;支持 ?after= / Last-Event-ID,游标不是运行 ID,服务端把它作为运行详情的 sinceSeq,因此断线重连只读该游标之后的增量。cleanupPending: true 时即使运行已进入终态也会继续推送,直到后台收尾完成。前端应以快照恢复状态,代理只对这一精确路径关闭缓冲。
  • generation_task_created 携带 canvasId、nodeId、taskId、title、mode、referenceNodeIds。草稿、提交、画布工具写入,以及媒体成功 / 失败 / 取消的真实节点变更,均在画布写入与运行检查点的同一事务持久化 canvas_updated。节点已删除或换任务时不伪造更新;run_failed 本身不代表节点媒体任务失败。
  • 未注册 Agent 能力的节点不阻断整张画布读取:摘要和 canvas_get_state 仅返回基础信息,并标记 agentSupported: false 与原因;不暴露任意 metadata,不授予该类型的创建、修改、连线或媒体引用能力。
  • approval.preview 和 canvas_updated.actions 是服务端根据同一解析、快照与 Capability 规划流程生成的解释信息,不是授权凭证。审批卡片明确列出新增/修改节点、节点类型、修改字段、名称变化和连线两端;执行事件保存同样的可读信息,供历史消息显示和点击定位。实际写入仍重新校验用户归属、snapshotHash、Capability、节点锁、预算和审批 callHash。
  • 可纠正且尚未产生副作用的工具参数错误会在 tool_failed 的顶层及 result.retry 返回 { groupId, attempt, maxAttempts, status }。status=retrying 表示 Agent 可按字段、schema 和示例自动纠正;同一工具累计三次仍失败时返回 status=exhausted,随后才产生 run_failed(reason=tool_retry_exhausted)。纠正成功的 tool_completed 返回同一 groupId 和 status=recovered。前端应按 groupId 折叠中间失败,不能将其显示为独立红色错误。权限、安全、存储错误以及已提交的收费生成任务不进入该重试流程。
  • canvas_updated 包含 canvasId、operation、callId、actions、可选的 preview/text 和 canvasPatch。canvasPatch 为 { canvasId, updatedAt, nodes: [{ before, after }], connections: [{ before, after }] };新增对象的 before 为 null,仅包含有变化的节点和连线。当前工具不开放删除。节点 action 保存操作、节点 ID、执行前标题、类型、修改字段和可选的修改后标题;引用 action 同时保存来源与目标节点 ID、标题和类型,不要求前端从内部 ID 猜测对象。callId 用于合并同一画布操作的工具完成消息。 可撤销且保留完整快照的普通写入附带 undo: { runId, canvasId, stepId, expectedSnapshotHash };媒体提交、无完整快照、超出快照容量或无实际变化时不提供可执行撤销凭据。
  • 前端对增量按 40ms 窗口合批,三方合并远端基线、当前 Store 和编辑器,正常完成 / 失败无需全量 GET;保留无关节点编辑、拖动与视口,同字段冲突、删除或新任务绑定不强行覆盖。只有事件缺口、断流、增量冲突或没有增量的事件源需要全量校准,同一画布协调器最多每秒发起一次,单请求在途,期间重复通知合并为一次尾随校准。已识别增量协议后,相邻 tool_failed / run_failed 不重复触发整图刷新。
  • Agent 创建 / 续聊请求在客户端先持久化完整正文、幂等键、父运行和消息 ID,再提交;网络超时或响应丢失只允许用同一正文和 key 有界重放,不能换 key 生成新收费轮次。客户端区分“服务端已明确返回错误”和“未收到服务端确认”,不再把 HTTP 409/500 一律描述成请求未确认。待确认记录损坏时对话暂停发送,不把它当成“从未提交”。
  • 生成按钮与提交入口共用节点任务状态:排队 / 运行中不可重复生成,终态解除任务锁,历史 taskId 不永久禁用。聊天留痕保留节点名、引用与失败任务 ID,点击使用现有画布聚焦;已删除节点只提示,不自动重建。聊天缓存不重复保存增量中的完整节点正文。

Agent 的聊天列表缓存只负责 UI 恢复;运行、技能快照、审批、工具调用、子任务和事件均以服务端持久化状态为准。提交响应丢失时,客户端应使用同一幂等键重试,不生成新的提交。

媒体完成事件分别报告生成与回写结果:tool_failed.result.status 始终是媒体任务真实状态,generationError 是脱敏后的生成错误;writebackError / writebackReason 记录回写失败,不能覆盖生成错误。targetNodeId 保留原调用目标,nodeId 只在匹配到真实任务绑定时返回。回写原因包含 canvas_unavailable、target_node_missing、task_binding_changed、result_resource_unavailable,缺少可验证目标时为 target_node_unknown。终态 run_failed 同步提供目标节点、generationStatus、两层错误及回写 reason,并持久化 failureMessage。回写必须同时匹配原目标节点和任务 ID,不能仅凭另一个节点复制的 taskId 改写目标;恢复收尾同样使用任务持久化的目标信息。失败任务只承诺保留任务记录,不宣称已经生成可用媒体。

Skills 的 tool_completed / tool_failed 事件附带 skillId、skillName(已启用技能)和 path;空路径表示列目录,不表示已读取文件。目录结果中的 entryPath 固定为 SKILL.md,files 为本轮固定快照的可读路径;Agent 先读取入口,再按入口引用读取必要文件。同一运行内同技能同路径只允许请求一次(包括不存在的路径),重复请求返回工具失败和继续执行指引,状态随运行持久化。模型初始上下文只包含技能版本、哈希、入口和可读文件清单,不包含技能正文。

模型任务失败的 run_failed 事件保留 taskId,并提供安全分类 reason:model_connection_reset、model_request_timeout、model_connection_refused 或 model_task_failed。text 不透传原始上游错误,避免泄露地址和凭证;前端用事件类型而非摘要关键词判断工具成功 / 失败。失败不会触发自动付费重试。

管理员支付订单

GET /api/admin/payments/orders 需要管理员权限,支持 status、keyword、providerId、timeField、from、to、page、pageSize。关键词匹配订单号、渠道交易号、用户名称、用户名或邮箱,也支持完整用户 ID 精确匹配。用户名和邮箱搜索忽略大小写;所有筛选同时生效,返回匹配总数和当前页订单。timeField 为 created(默认创建时间)、paid(渠道支付时间)、credited(入账时间)。from、to 为 YYYY-MM-DD,包含起止日期,按北京时间转换为左闭右开的时间区间;未填写的边界不限制,倒置日期或无效状态/时间口径返回 400。

每条管理员列表订单在原有字段之外包含 user: { id, username, displayName, email },由当前页用户 ID 批量读取;用户不存在时返回 user: null,保留原 userId 与历史订单。此身份摘要仅用于管理员订单列表,不添加到普通用户支付接口或支付状态同步响应。

后台支付订单列表展示用户身份,点击名称打开用户详情;订单详情提供完整订单号、渠道交易号、用户 ID 和各阶段时间。同步支付状态与关闭订单位于更多菜单,关闭订单仍需确认。

官方易支付插件 official-payment-epay 通过既有支付接口接入,不新增业务路由。管理员必须显式填写可信支付网关;V1 支持 MD5 下单、查单与通知验签,未付款订单的关单仅关闭本地记录。V2 目前只支持 RSA 下单和通知验签,不支持查单、刷新收银台及自动关单;相应操作返回明确失败,不能按“订单已关闭”处理。启用前需在实际网关验收回调和到账,不能用插件安装成功代替支付链路验收。

支付对账筛选及 CSV 导出

GET /api/admin/payments/reconciliations 支持 providerId、status(running / completed / failed)、from、to(账单日期,包含起止日)、page、pageSize。GET /api/admin/payments/reconciliations/:id/items 支持 result、page、pageSize;result=abnormal 同时选出所有非 matched、非 recovered 明细,未指定或 all 表示全部。具体结果还支持 matched、recovered、local_order_not_found、provider_record_missing、amount_mismatch、trade_no_mismatch、credit_failed。

三个只读导出接口均在 service 校验管理员权限,返回 text/csv; charset=utf-8 附件,设置 Cache-Control: no-store。导出复用列表筛选条件,不受 page、pageSize 影响:

接口参数与内容
GET /api/admin/payments/orders/export.csv与订单列表相同的筛选;完整订单号、渠道交易号、用户身份、商品、渠道、金额、币种、积分、订单/渠道状态及各阶段时间
GET /api/admin/payments/reconciliations/export.csv与对账列表相同的筛选;每日执行状态、总笔数、一致/补发/异常笔数、失败原因、发起人 ID 或系统、开始/完成时间
GET /api/admin/payments/reconciliations/:id/items/export.csvresult;单次对账的完整交易明细及对账开始/完成时间,便于区分同日重跑结果

最多导出 10000 行数据(不含表头);超限返回 400 业务错误,提示缩小筛选范围,不截断文件。无匹配数据时接口返回仅含表头的 CSV;前端无数据时禁用导出。导出不查单、不重跑对账、不补发积分。

对账完成不代表无异常,汇总保留异常笔数;失败记录仍可导出汇总及失败原因。明细仅允许已完成的对账,执行中或失败返回 409。对账重跑复用记录并替换明细,明细导出使用可重复读快照,保证汇总与明细来自同一次数据库快照。订单及关联用户身份也在同一快照内读取。

明细的“记录金额”附带“金额来源”:通常为渠道账单,provider_record_missing 为本地订单。该字段不声称同时保存渠道和本地金额,不根据订单当前值推算历史差额。订单金额和积分为订单数据,未支付订单的积分不代表已经发放。

CSV 使用中文表头、UTF-8 BOM、CRLF;金额按整数分精确转换为两位小数,积分从微积分转换并保留最多六位小数,时间表头注明北京时间。空时间留空。文本转义逗号/引号/换行,并在公式触发字符前加单引号;可被解析为数字或科学计数法的 ID/订单号也加单引号防止表格软件丢失精度,不生成 ="..." 公式。通用 CSV 消费者可能看见该保护前缀,按文本导入并按需移除;仅 CSV 引号不能声明列类型。

前端使用已经应用到列表的筛选条件导出;更改条件后,先查询/筛选再启用导出,分页和刷新沿用已应用条件。明细抽屉支持“仅异常”和单独刷新,切换记录/筛选时取消旧请求,避免旧响应覆盖新记录。下载接口的 JSON 错误 Blob 会解析为业务提示,超量或重跑冲突不会被保存为 CSV。

用户 Agent 记忆

以下接口都要求当前用户登录 Cookie,按 author_user_id 或提案/版本的 user_id 校验归属;客户端不能提交另一个用户来扩大 scope。手动新增记忆直接批准,Agent 产生的经验必须先满足真实写入/生成成功资格,暂存在运行检查点,整轮成功收尾后才提交待审;失败、取消、等待确认不提交。每轮最多 3 条、每用户最多 50 条待审和 300 条已批准;待审 30 天到期为 expired,不是用户拒绝。

路径请求与结果
GET/POST /api/agent/memories按状态读取 / 手工添加;状态含 pending/approved/rejected/expired,新增正文按受控字段校验
PATCH/DELETE /api/agent/memories/:id编辑 / 删除自己的条目;POST .../:id/decide 的 { decision: "approve" | "reject" } 决定待审经验是否以后可用
GET /api/agent/memories/export、POST /api/agent/memories/importkind=agent-memories 的个人记忆 JSON;一次导入最多 200 条,仍校验内容与归属
GET/PATCH /api/agent/memories/settings读取压缩设置或更新 compactInterval: off/daily/weekly/monthly 与受管文本模型
POST /api/agent/memories/compact按文本模型计费生成压缩提案,返回设置视图的 taskId/proposalId/lastStatus/summary;模型完成不直接改删记忆
GET /api/agent/memories/proposals?limit=30返回 { proposals };每项含 id/status/version/taskId?/summary/changes/createdAt/decidedAt?,change 含 kind、全部 before 和拟保留的 after
POST /api/agent/memories/proposals/:proposalId/decide必填 { decision: "approve" | "reject", version };批准原子应用整份提案,拒绝不改原记忆;返回更新后的提案
GET /api/agent/memories/revisions?limit=30返回 { revisions };每项含 id/proposalId/version/before/after/createdAt/restoredAt?
POST /api/agent/memories/revisions/:revisionId/restore必填 { version };原子恢复该批次压缩前的内容,返回更新后的版本视图

提案和历史列表默认最近 30 项、最多 100 项。version 是读到的对象版本,不能用条目数量或更新时间替代。未登录为 401;缺少/无效 version、错误枚举或未知请求字段为 400;资源不存在或属于他人为 404;提案版本、源记忆内容或恢复后置状态不匹配为 409。批准会在同一事务锁定全部源记忆并比较内容版本,再改写/合并、保存完整 before/after 历史;任一项冲突整批回滚,没有部分批准。拒绝同样校验提案版本,但不要求已失效的源内容仍匹配。

恢复只允许一次成功恢复;它要求当前所有保留条目仍等于应用后的内容/状态版本,且被合并删除的 ID 仍未被任何用户的新记录复用。后续编辑、删除、ID 复用都使恢复返回冲突,不覆盖新内容。压缩和恢复都保留原始来源、来源运行和工具证据,不把改写或去重当作新的验证成功。

记忆返回可选 originRunId、evidence: [{ sequence, callId?, tool }],设置页可展开查看来源。lastVerifiedAt 只保留原始证据字段,当前流程不因新建、重复命中、压缩或用户批准而刷新。hits 是主动取回次数,injected 是相关经验随模型任务实际入队的次数,均不证明经验带来了成功。

自动召回对每个普通 Go/Pi 模型步骤重算,只从本人已批准记忆中选择达到阈值的条目,最多 5 条且 JSON 正文不超过 6 KiB;无相关项不注入。记忆消息在当前真实用户消息/插话之前,以有来源的动态 user 数据承载,不进入 system。仅构造或预算拒绝不递增 injected;参数/预算、工具权限和独立收费审批不会被经验改变。

压缩任务仍为 canvas_text、operation=agent_memory_compact,每批最多 80 条并受 48 KiB 输入预算限制。按稳定 ID 公平轮转,游标推进到实际入批条目的末尾,低 Hits 记忆会进入后续批次。调度每 5 分钟扫描、每拍最多启动 3 个用户;周期触发生成的是待审批提案。状态区分 queued/running/awaiting_review/applied/rejected/restored/succeeded/failed,其中 succeeded 表示本批无可应用修改。memoryProposalId、任务 ID 与生成中提案版本绑定,重复或迟到回调不覆盖已决定提案或新任务。

提案结构和来源字段由 Schema 58 持久化,详见 后端数据库。协议/事务及界面组件测试不代表真实收费模型压缩质量、浏览器端操作链路或 PostgreSQL 部署已验收。

管理员查看 Agent 记忆

GET /api/admin/agent-lessons、DELETE /api/admin/agent-lessons/:id 需要管理员权限。列表支持 status、userId、keyword(主题/内容/用户名)与 limit;条目含解析后的 steps、authorUserId、authorUsername、authorDisplayName。管理员不能批准或拒绝,批准入口在用户设置页。

管理员系统渠道

POST /api/admin/channels/:id/duplicate 需要管理员权限,会复制指定系统渠道的连接配置、加密凭证、请求头、模型和价格档,生成新的渠道与模型 ID;复制渠道名称自动追加“ - 副本”。原渠道及其前台供应线路不受影响。

信封

成功:

{ "code": 0, "data": {}, "msg": "ok" }

失败:HTTP status 与 code 都不是成功;reason 是稳定机器可读原因。

{ "code": 40301, "data": null, "msg": "账号素材数量已达到 100 个上限", "reason": "quota_exceeded" }

code === 0 才是业务成功。HTTP 200 但 code !== 0 必须当失败。前端判断类型用 ApiError.reason / ApiError.code,不要解析 msg。

错误码

code 默认等于 HTTP 状态。需要区分同一状态的不同原因时,使用 status * 100 + n。没有单独的 1001/2001 号段,以免和 HTTP 状态、现有 42901 以及重试判断打架。

codeHTTPreason含义
0200—成功
400400invalid_argument请求不合法
401401unauthorized未登录
403403forbidden无权限
404404not_found资源不存在
409409conflict冲突
409409project_name_conflictPOST /projects 同一用户下已有同名项目;客户端可有限更换名称重试
409409skill_version_conflictPUT /skills/:id/files 的 baseVersionId 已不是当前版本;details.currentVersionId 为最新版本,客户端需重新加载后再编辑
429429rate_limited限流(未细分)
500500internal内部错误
502502bad_gateway上游失败
503503unavailable暂时不可用
504504timeout超时
40301403quota_exceeded账号配额用尽(不自动重试)
40301403storage_capacity_exceeded账号文件容量不足,上传或媒体生成被拒;前端据此展示“购买空间”入口
40302403storage_read_only账号超出文件容量且 7 天宽限期已结束,内容写入被拒;details 含 usedBytes、totalBytes、readOnlyAt
40901409conflict幂等冲突
42901429rate_limited请求限流(可带 Retry-After)

模型错误把已有字符串码放进 reason(如 model_capability_not_supported),code 仍为 400。

常量:后端标准原因定义在 backend/internal/kernel/error_codes.go,由 app 和 service 兼容导出;前端业务模块按 ApiError.reason 分支。

技能库分类 API

以下接口需要登录。技能库分类与技能广场共享分类分离:skills.tag 仍表示共享市场分类;用户技能库分类记录在用户自己的技能状态中,只影响该用户技能库和画布 Agent。

方法路径行为
GET/api/skills/library-categories?scope=mine|created返回当前用户可见的个人分类和平台分类,以及所选视图下每类数量、全部技能数和未分类数量。mine 包含自己创建及已加入的公开技能;created 只统计自己创建的技能。
POST/api/skills/library-categories请求 { "name": "分镜", "scope": "personal" }。scope 可省略并默认为 personal;管理员可指定 platform,该分类对所有用户可见、可分配和筛选。同一用户可见范围内分类名唯一。
DELETE/api/skills/library-categories/:id删除个人分类(仅分类所有者)或平台分类(仅管理员),只解除该分类与技能的关联,不删除技能。
PATCH/api/skills/:id/library-category请求 { "categoryId": "分类 ID" } 设置分类;空字符串清除分类。仅允许对自己创建或已加入的技能操作,并且只能使用当前用户的个人分类或平台分类。

GET /api/skills 可带 libraryCategoryId 或 libraryUncategorized=true,仅用于 scope=mine / scope=created,且两者不能同时使用。它们可与共享分类筛选 tag 同时提供,结果需同时满足两个筛选条件。分类列表的数量不受搜索词影响,表示对应技能库范围内的完整数量。

技能列表响应中的 categories 是技能广场共享分类,包含 value、label 和 count;count 统计已启用且公开的技能,不受当前搜索词、分页和视图筛选影响。它与技能库个人/平台分类是两套独立分类。

技能包文件 API

以下接口需要登录。读取接口沿用技能可见性(私有技能仅作者可读);写入只允许作者。

方法路径行为
GET/api/skills/:id/files当前版本的文件清单:path、kind、mimeType、size、sha256
GET/api/skills/:id/file?path=单个文件;文本返回 content,二进制只返回 binary=true,文本超过 512KB 返回 400
GET/api/skills/:id/file/raw?path=原始字节,Content-Security-Policy: sandbox、nosniff、no-store
PUT/api/skills/:id/files作者批量修改文本文件并生成新版本,见下文

PUT /api/skills/:id/files 请求:

{
  "baseVersionId": "编辑开始时的 versionId",
  "operations": [
    { "op": "delete", "path": "old.md" },
    { "op": "rename", "path": "draft.md", "to": "references/camera.md" },
    { "op": "write", "path": "SKILL.md", "content": "# 技能\n..." }
  ]
}
  • 服务端按顺序把操作应用到当前版本的完整文件集,再整体重建 ZIP、skill_versions 和 skill_files,并在同一事务里以 current_version_id = baseVersionId 为条件更新技能行;并发写入(另一标签页、GitHub 同步、技能信息抽屉)会让后到请求得到 409 skill_version_conflict,details.currentVersionId 为最新版本。
  • 路径必须已经规范化:相对路径、不含 ..、.、空层级、反斜杠、控制字符和首尾空格,最多 8 层、单层不超过 255 字节,不能使用 .git、__MACOSX 或 .DS_Store;文件与文件夹不能重名。
  • write 只接受 UTF-8 文本,单文件不超过 512KB,且目标类型必须可按文本预览;已有二进制文件不能被覆盖。SKILL.md 不能删除或重命名。整包仍受 512 个文件、20MB 限制,单次最多 256 个操作,请求体上限与安装上传一致。
  • SKILL.md 变化时同步 skills.instruction;frontmatter 的 metadata.version 作为版本号,缺省时使用时间戳。单文件 Markdown 技能编辑后包含多个文件时 sourceType 变为 zip,避免技能信息抽屉以 instruction 重建整包时丢失附属文件。
  • GitHub 技能返回 409 failed_precondition,请在仓库修改后同步。内容与当前版本完全相同时返回 changed=false,不生成新版本。
  • 成功响应 data={ skill, files, changed };单个操作校验失败返回 400,details.operationIndex 指向出错的操作。

画布工具 API

以下接口均要求登录,使用标准业务信封:

方法路径行为
GET/api/tools分页列表;scope=public/favorites/custom,默认 public;可按 type=style/motion/nine_grid/effect、tag、search 筛选。page 从 1 开始,pageSize 默认 20、最大 80;返回 tools、totalCount、page、pageSize、hasMore
GET/api/tools/:id可见且启用的工具详情,包含 prompt 和 extraInfo;列表不含这两个大字段
POST/api/tools新建自己的工具;type、label、prompt 必填,visibility 默认 private;预览仅支持本人已上传完成的资源,公开工具暂不接受私人预览
POST / DELETE/api/tools/:id/favorite幂等收藏 / 取消收藏,仅允许操作可见且启用工具
DELETE/api/tools/:id仅删除自己的用户工具,不能删除内置或他人工具

生成提示词工具引用格式为 @[tool:type:ID:label:icon],label 使用 URI 编码。名称和图标只用于展示,不作为授权依据。服务端在任务入队及执行前检查用户可见性、启用状态、工具类型和生成模式;无效、越权、嵌套工具或数据库错误必须失败,不能把未解析 token 发送给模型。风格和九宫格仅用于图片,运镜和特效仅用于视频。入队校验不冻结工具快照:排队期间删除工具将导致执行失败。

场景与道具图片

PUT /api/projects/:id/assets/:assetId/image 接收 { resourceId, expectedVersionId },返回标准信封 data={ asset }。只允许当前用户在自己项目内,为场景或道具绑定本人已就绪的图片;资源必须有有效尺寸和图片 MIME。两个字段均必填,过期版本拒绝,主版本必须属于目标资产。

项目资产摘要可包含 visual={ definition, status }:definition 保留描述与提示词,status 为 missing|ready。未出图的设定使用 entity;真实图片绑定后,同一资产使用 image 和真实资源定位,旧版本及图片引用保留。继续使用现有资产、版本、表现表,无数据库迁移。

受管图片任务使用 metadata.operation=project_asset_image、projectAssetId 和 projectAssetVersionId,由任务成功事务绑定资源,媒体保存恢复同样适用。结果的 projectAssetImageStatus=bound|stale 表示已绑定或资产版本已变化;stale 仍保留生成文件,不能自动重发收费请求。历史画布图片匹配只信任服务端版本表现记录,不信任客户端自报的历史。

平台技能策展

新增登录读取 /api/skills/curation 与管理员 GET/PUT /api/admin/skill-curation;原技能列表仅在策展开启时应用新增分类筛选,响应结构不变。见 接口与行为。

一级分类扩展:管理写入增加 root 命令,assignment.rootId 与子分类完整集合一起保存;列表增加 platformRootId 筛选。关闭模式继续忽略新增筛选。

服务商目录 API

均需管理员权限,返回统一信封。目录来自 https://ai.yan.tv/v1/catalog.json(静态 JSON),CANVAS_PROVIDER_CATALOG_DISABLED=1 时关闭。

方法路径说明
GET/admin/provider-catalog返回推广期内(startsAt ≤ now < endsAt)的服务商,按 rank 排序;source 为 live/cache/unavailable/disabled;成本已按 1 元 = 1 积分换算为微积分;不支持的协议标记 supported=false
POST/admin/provider-catalog/refresh强制重新拉取,失败时沿用上次快照
POST/admin/provider-catalog/:id/verify{ apiKey },请求 GET {baseUrl}/models,返回 upstreamModelCount、availableModels、missingModels,不落库
POST/admin/provider-catalog/:id/install{ name, apiKey, markupPercent, models: [{ modelKey, markupPercent? }], enabled },一次事务创建渠道、模型和规格档,返回 { channel }

install 的 Base URL、协议、能力和成本全部从服务端快照读取,不信任请求体。售价 = 成本 × (1 + 加价率),向上取整到微积分;加价率 0–1000%,最多两位小数。推广期已过返回 404。API Key 只加密存入渠道,不写日志。

短剧自动制作

所有 /api/dramas 路径都需要登录 Cookie 并受 shortDrama 功能开关保护;对象归属在 service 中按当前用户校验,返回标准业务信封。

方法与路径用途
GET /dramas?page&pageSize&q&status作品列表:只含自动制作短剧;q 按作品名搜索,status 为 draft/running/paused/completed。每项含状态、集数、完成数、最多 4 张角色参考图拼封面、风格、画幅、已用积分与更新时间;statusCounts 给出各状态总数(all 为全部)
GET /dramas/asset-schemes参考图方案目录:定妆照、三视图设定板、四格资产板的默认提示词、画幅与版式说明,场景默认提示词,以及角色/场景可用变量;道具参考图使用固定模板
GET /dramas/modelstext / image / video 三类平台模型:options 为可由服务端独立执行的模型及默认选择,视频模型附 maxVideoSeconds(单次最长秒数,也是剧情段时长上限);unavailable 为平台目录里有、但短剧用不了的模型及 reason(例如视频模型未声明原声对白或不支持图生视频),前端置灰展示。参考图走文生图,不再要求图片模型支持参考图输入
POST /dramas/estimate按章节数、字数与设置估算集数、镜头数、剧情段视频数、参考图数与费用区间(基于真实报价,含重试余量)
POST /dramas一次性创建作品、写入小说章节与制作设置;start=true 立即进入自动制作。请求体上限 32 MiB,单章 ≤ 6 万字,全书 ≤ 600 万字
GET /dramas/:id制作台摘要:控制面状态、每集按剧情段聚合的进度 stats(segments、promptReady、videoReady、failed)、队列计数、待处理收件箱。cast_failed 的 castKind 为 character/prop/scene;segment_failed 带 episodeNumber、segmentIndex 与失败环节 media(prompt/video)
PATCH /dramas/:id/settings修改单集时长(30–1200 秒)、分镜规则 storyboard(segmentMode auto/fixed/range、segmentSeconds、segmentMinSeconds、segmentMaxSeconds、pacing、dialogue、transition、transitionMs)、视频提示词技能 promptSkill({ skillId, skillName, path },不可读时返回 400;传空 skillId 清除)、审阅方式、生成节奏、参考图方案、模型、并发与预算;开工后不能修改画幅;提示词包含未知变量时返回 400
POST /dramas/:id/media/release手动模式下放行生成:target=assets 只生成参考图,episodes(配合 episodeIds,最多 500)生成指定集的参考图与剧情段视频,all 全部开始
POST /dramas/:id/start · /pause · /retry-failed继续 / 暂停 / 把失败的参考图、段提示词与段视频重新排队,返回 { segments, casts }

显式要求产出的操作(retry-failed、media/release、approve、rescript、segments/prompts、segments/generate、cast/:castId/regenerate)在作品暂停或已完成时会按 start 的同一套校验(模型可用、预算未超)恢复制作;校验不通过返回 400,已写入的请求保留,处理后点继续即可。只改内容的操作(改镜头脚本、改设定文字)不恢复暂停中的制作。 | GET /dramas/:id/jobs?limit | 最近的调度工作(类型、状态、错误、实际花费、重试次数、入队/开始/结束时间) | | GET /dramas/:id/jobs/:jobId | 单项工作详情:历次提交(最新在前),每次含任务状态、模型、请求参数白名单(尺寸、时长、是否同步生成声音等,不含渠道配置)、提示词、错误、花费与产出资源;只限作品所有者 | | GET /dramas/:id/episodes/:episodeId | 单集详情:镜头脚本、剧情段、改编来源章节、进行中的工作与 scriptJob。每个剧情段含 videoPrompt(生效内容,手动改写优先)、generated、custom、promptStatus、videoStatus、videoResourceId、tailFrameResourceId、videoModel、promptJob / videoJob,以及 request:生成本段视频时实际发送的生成方式 operation、秒数、模型上限、首帧(firstFrameResourceId,上一段未生成时 firstFramePending=true)、参考图列表(编号、类型、名称、资源、是否随请求发送)、追加在提示词开头的 header 与限制说明 warning;与提交时同一函数计算 | | PATCH /dramas/:id/episodes/:episodeId | 修改集标题 | | POST /dramas/:id/episodes/:episodeId/approve · /rescript | 确认本集提示词(样片模式下确认第 1 集样片)/ 重新改编本集 | | POST /dramas/:id/episodes/:episodeId/segments/prompts | { segments: number[] } 让文本模型(重新)撰写这些剧情段的完整视频提示词,正在撰写的段跳过;返回 { queued } | | PATCH /dramas/:id/episodes/:episodeId/segments/:index | 保存剧情段视频提示词改写 { videoPromptOverride },最多 12000 字,空串恢复为模型撰写的版本;本段与后续已生成的段视频标记过期 | | POST /dramas/:id/episodes/:episodeId/segments/generate | { segments, videoModel? } 按剧情段生成视频,可同时换视频模型(只接受目录中的模型)。提示词缺失或过期、视频正在生成时返回 400;样片模式下第 1 集样片确认前,其他集返回 400。段按顺序提交,第 2 段起等上一段最后一帧 | | POST /dramas/:id/episodes/:episodeId/segments/:index/reprocess | 重新处理已生成、但结果回写(如抽尾帧)失败的段视频:只重放最近一次已成功的生成任务结果,不重新调用模型、不重复扣费,返回 { reprocessing: true }。段视频不是 failed、没有已入库视频或最近一次作业不可重放时返回 400。段视图的 videoReprocessable 为 true 时可调用 | | PATCH /dramas/:id/shots/:shotId | 修改镜头脚本:说话人、台词、动作、景别、运镜、起幅画面、运动描述、转场、时长(1–60 秒,且本段合计不超过视频模型单次上限);所在段的提示词改写被清空、视频标记过期;已写好的提示词标记过期,尚未写完的退回待撰写并自动按新镜头重写 | | GET /dramas/:id/cast · PATCH /dramas/:id/cast/:castId | 读取 / 修改角色、道具与场景设定(名称、别名、外观、声线、锁定) | | GET /dramas/:id/cast/:castId/impact · POST …/regenerate | 预览引用该设定的集数与剧情段数 { episodes, segments };重新生成参考图,restyleSegments=true 时引用它、且已生成或生成中的剧情段视频标记过期(段提示词只引用参考图编号,保持不变),返回实际受影响的 { affectedSegments } | | POST /dramas/:id/cast/regenerate-all | 按当前参考图方案重做某一类(kind 为 character/prop/scene)全部未锁定的参考图,可选 restyleSegments | | GET /dramas/:id/chapters/:chapterId | 读取单章原文 | | DELETE /dramas/:id | 删除作品与全部制作记录 |

编排由后端调度循环推进,浏览器只提交意图;制作台目前按 3 秒轮询摘要和当前集详情,没有新增 SSE,也不需要调整代理缓冲配置。模型选择只接受逻辑模型或系统渠道模型;浏览器自定义渠道密钥无法驱动无人值守流水线。

On this page