diff --git a/docs/project-memory/shared-memory/decision-log.md b/docs/project-memory/shared-memory/decision-log.md index 8f2177b2d..1dabf13e1 100644 --- a/docs/project-memory/shared-memory/decision-log.md +++ b/docs/project-memory/shared-memory/decision-log.md @@ -6603,6 +6603,8 @@ - 2026-08-06 修订:画布 Agent 的 `edit_image` 只接受图片输入,新任务以 `assetKind=null` 表示普通静态图片,不再使用 synthetic `editor_agent_edit_image`。worker 仅按服务端生成的 `editor-agent:` dedupe namespace 识别并归一历史排队 payload;普通调用伪造旧值继续被拒绝。已持久化资源中的旧值只有在后端从真实目标图层 / 项目资源解析后才兼容为空类型,避免历史 Agent 结果失去快速编辑能力,同时不扩大请求白名单。 - 2026-08-07 修订:站内与 External v1 图片编辑请求统一只接受必填 `sourceReferenceId`,且该值必须是当前账号已登记的项目资源 ID 或素材 ID;objectKey、URL、Data URL、Blob URL 以及旧 `sourceImageSrc/sourceResourceId/assetKind` 字段全部返回 400,不提供兼容别名。后端用共享窄查询分别按两张表主键定点解析,双表同 ID、未命中、跨账号、对象缺失或越权均失败关闭;权威类型完全来自业务记录,只允许普通静态图片、规范图、角色图、完整图标图集、图标规范、宣发图和 UI 设计图。请求带 `targetLayerId` 时必须同时带 `projectId`,来源与目标优先比较 `assetObjectId`,任一方缺失才比较 canonical `(bucket, objectKey)`,且来源默认类型必须与目标资源默认类型一致;最终类型取目标覆盖值或目标资源类型。HTTP 入队写入版本化服务端解析快照,worker 执行前按同一业务 ID 再次定点解析,身份或类型漂移即失败关闭。旧任务只把已有资源 ID 或旧来源字符串本身当业务 ID 迁移,绝不按 objectKey 反查。Canvas Agent 必须从 `ImageMetadata.reference_id` 取主来源;红框标注上传图只作为辅助 `referenceImageSrcs`,不能冒充被编辑资源。仅以素材 ID 编辑时,队列审计与 `generationInputs.references` 保留素材 ID,不伪造项目资源关系。 - 2026-08-08 修订:`scene` 是单张静态图片素材,加入前端快速编辑正向白名单和 api-server 权威来源白名单;编辑结果继续保留 `scene`。用户标签覆盖侧的前端菜单与 SpacetimeDB 结构化布局白名单也必须显式覆盖 `scene`。通用图片生成接口仍拒绝 `scene`,避免绕过结构化场景生成契约。 +- 2026-08-08 修订:普通静态图片的正式语义值固定为 `assetKind=null/None`;`"image"` 只属于前端派生 `CanvasMediaType`、MIME / content type 和生成参考媒体类型,不是 `CanvasAssetKind`,不得由上传、生成、资源 / 素材写入或 read model 生产。API 与 SpacetimeDB storage 的读写边界把短期误写的历史 `assetKind="image"` 原位归一为空;快速编辑白名单继续直接允许空类型,不新增 `"image"` 分支。 +- 2026-08-08 修订:画布 Agent 的 `edit_image` 工具内部可先形成待确认的 `EditorImageEditRequest`,但确认接口必须在通用入队前复用站内图片编辑的来源解析与目标预检,写入 `{ version, request, source }` 服务端快照。worker 只解析正式 versioned payload 与既有历史 payload,不接受当前 direct request 作为 fallback;这样未上线的 Agent 路径在生产端原位修正,不扩大消费端协议。 - 验证:模型测试覆盖允许与拒绝类型,工具栏和两类右键菜单测试覆盖单个拆分图标、角色动作及音频不展示,提交工作流测试覆盖单个拆分图标和角色动作绕过入口时仍拒绝;后端表驱动测试覆盖全部现役素材 / 媒体类型与未知类型,锁定图片编辑端点失败关闭。 - 关联:`src/components/image-editor/ImageCanvasGenerationModel.ts`、`ImageCanvasSelectedLayerToolbarView.tsx`、`ImageCanvasContextMenusView.tsx`、`useImageCanvasGenerationWorkflow.ts`、`useImageCanvasGenerationSubmissionWorkflow.ts`。 diff --git a/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md b/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md index 67565171e..c9e04b8f0 100644 --- a/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md +++ b/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md @@ -84,6 +84,7 @@ npm run check:server-rs-ddd - 画布 Agent 的 `gpt-5.4-mini` Chat Completions 规划使用 1024 生成 token 预算;VectorEngine 专用 client 显式发送当前字段 `max_completion_tokens`,其预算包含可见输出和隐藏 reasoning token。通用 OpenAI-compatible client 默认保留旧 `max_tokens`,只有确认 endpoint 能力后才 opt-in,禁止按模型名猜测或在 `400` 后自动重放。前端在 POST pending 120 秒后显示不入库的耐心等待提示;provider request future 明确返回 connect/timeout/HTTP/transport 错误时立即进入正式失败,尚未返回则继续等待。专用 provider 单 attempt hard timeout 为 8 分钟;请求发起阶段的 timeout、连接失败、`408`、`429` 与 `5xx` 读取 `GENARRATIVE_LLM_MAX_RETRIES`,但画布 Agent 最多重试 1 次,显式配置 0 仍可关闭,专用重试退避最多 60 秒。消息规划生命周期从 handler 入口开始计入 18 分钟总 deadline,进入 `agent.prompt(...)` 时只使用剩余预算;该 deadline 覆盖会话锁/上下文准备与最多 3 轮规划,并为错误持久化/HTTP 返回预留约 2 分钟,不允许多轮规划绕过前端 20 分钟 timeout。已收到成功响应头后的响应体读取或解析失败直接按明确失败收口,并使用该成功响应所属的真实 attempt 记录错误。重试只包围 LLM 规划请求并发生在任何待确认工具执行之前,因此不会重复提交生成任务或扣费。 - 对话附件只允许引用当前工程 `editor_project_resource` 或当前账号 `editor_asset` 的图片;前端可提交展示用 `imageSrc` / `thumbnailSrc`,后端必须按 `resourceId` / `assetId` 重新归一、校验 owner / project 和 `objectKey`,再给 LLM 或生成工具使用。 - `edit-image` 只接受当前图片上下文中的 `object_image_id`;`source_image_id` 不是现役 schema 字段,prompt、tool args、确认执行和测试中都不得生成或兼容该字段。 +- `edit-image` 在工具准备阶段生成的 direct `EditorImageEditRequest` 只用于待确认消息内部状态,不是 worker queue contract。确认接口必须先按当前 owner 对 `sourceReferenceId` 做权威来源解析、目标预检与参考图上限校验,再写入 `{ version: 1, request, source }`;站内图片编辑与画布 Agent 共用同一准备 helper。worker 不接受当前 direct request fallback,只保留正式 versioned payload 和已经存在的历史任务迁移解析。 - 画布 Agent 工具复用既有编辑器图片生成 / 修改 / 图标 spritesheet BFF,并继续使用后端模型定价和 `execute_billable_asset_operation_with_cost`;前端不提交 `priceMudPoints`。 - api-server 对 `PromptRunError` 的持久化顺序固定为:先按 `partial_outputs` 原顺序映射已成功工具,将其保存为 `status=not_completed` 且无 `externalJobId` 的待确认消息;再在同一会话增量末尾追加 `ERROR ` terminal system 消息并整体写入 OSS。后续规划失败不得吞掉失败前已执行的成功工具结果;结构化 `ToolFailed` 可用于调用方诊断与流程决策,但画布确认面不得把它伪装成成功待确认卡。 - `/messages/{messageId}/confirm` 与 `/messages/{messageId}/cancel` 只返回成功确认;前端成功后立即重新读取整个会话,以会话详情中的权威消息状态和 `externalJobId` 驱动气泡展示与任务轮询。 @@ -437,7 +438,7 @@ Responses 的终态载荷既是工具调用的恢复源,也是正文的恢复 ## 编辑器角色动作素材持久化契约(2026-07-28) -- `asset_kind` 是资源 / 素材唯一权威媒体类别,不新增或返回 `media_type` / `mediaType`。角色动作预览 MP4 使用 `asset_kind = video`,最终透明帧集使用 `asset_kind = character-animation`;前端据此派生具体渲染器。 +- `asset_kind` 是资源 / 素材可选的权威语义类别,不新增或返回并列的 `media_type` / `mediaType`。普通静态图片固定使用 `asset_kind = NULL`,`image` 只可作为前端派生媒体类型、MIME / content type 或生成参考的媒体类型,不能作为语义 `asset_kind`;API 与 SpacetimeDB storage 的写入和 snapshot 边界把历史误值 `image` 归一为 `None`。角色动作预览 MP4 使用 `asset_kind = video`,最终透明帧集使用 `asset_kind = character-animation`;前端据此派生具体渲染器。 - `editor_project_resource` 与 `editor_asset` 表尾只追加 `image_sequence_frames_json: Option` 和 `image_sequence_duration_ms: Option`;`editor_showcase_asset` 作为提交时冻结的审核与公开快照,也在表尾追加并从账号素材复制相同两字段。前者保存完整有效帧数组,数组位置是唯一播放顺序,正式帧对象不保存或返回 `frameIndex`;后者只表示该图片序列完整播放一次的毫秒时长。帧数始终取数组长度,FPS 在播放或导出时即时推导,不持久化 `frame_count` 或 `fps`。 - 图片序列时长不能复用音频 / 视频生成请求的 `durationSeconds`,资源和素材也不保存通用 `duration_seconds`。角色动作与视频生成响应保留各自既有的请求 / 结果级秒数;音频 / 视频的用户可见时长只作为字符串展示项写入 `generation_inputs_json.fields[]`,上传媒体使用本次上传探测值,带时长选项的生成任务使用用户提交值,不再复制到 `EditorAsset`、`CanvasLayer` 或画布 layout,也不在素材放置 / 工程恢复时探测或从 layout、resource 做双来源回退。素材详情和画布 ZIP 用户可见元数据只透传实际存在的 `fields[]` 时长项;缺少该项时省略时长,不生成 `--:--` 等占位值。音频播放控件只信任 `