Compare commits

...

6 Commits

Author SHA1 Message Date
lhk229 47d7c55058 Merge branch 'master' into fix/401
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Successful in 6m35s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Successful in 5m32s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m49s
Project CI / Backend tests (pull_request) Has started running
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Successful in 5m16s
Project CI / Native shell tests (pull_request) Has started running
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Successful in 5m50s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 2m54s
Project CI / Repository checks (pull_request) Has started running
Project CI / Frontend tests (pull_request) Successful in 5m10s
Project CI / AI game creator shell web tests (pull_request) Has started running
2026-09-17 12:33:39 +08:00
lhk229 893c5cf832 修复AGC抠图远端项目身份
Project CI / AI game creator shell Rust smoke (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Has been cancelled
抠图请求改用绑定后的主站远端 projectId

工具回执同步返回远端 projectId

补充404根因决策记录与回归测试
2026-09-17 12:25:10 +08:00
lhk229 1ea5811788 同步AGC抠图Skill指纹
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Successful in 6m54s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Successful in 6m59s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Successful in 7m23s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Successful in 7m41s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 3m41s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 3m50s
Project CI / Repository checks (pull_request) Successful in 5m0s
Project CI / Frontend tests (pull_request) Successful in 6m3s
Project CI / Backend tests (pull_request) Successful in 11m4s
Project CI / Native shell tests (pull_request) Successful in 10m52s
Project CI / AI game creator shell web tests (pull_request) Successful in 3m44s
更新 agc-client-projection 内容哈希

递增 Skill Pack manifest 版本以通过启动校验
2026-09-17 10:46:59 +08:00
lhk229 75623207a6 Merge branch 'master' into fix/401
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Failing after 6m31s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m52s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Failing after 5m42s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Failing after 6m21s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Failing after 6m20s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 2m51s
Project CI / Native shell tests (pull_request) Failing after 2m56s
Project CI / AI game creator shell web tests (pull_request) Failing after 47s
Project CI / Frontend tests (pull_request) Successful in 4m27s
Project CI / Repository checks (pull_request) Successful in 3m32s
Project CI / Backend tests (pull_request) Has been cancelled
2026-09-17 10:17:26 +08:00
lhk229 b0c5c43956 Merge remote-tracking branch 'origin/master' into fix/401
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Failing after 6m20s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m55s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Failing after 5m11s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Failing after 5m49s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Failing after 4m56s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 3m23s
Project CI / Native shell tests (pull_request) Failing after 3m2s
Project CI / AI game creator shell web tests (pull_request) Failing after 1m8s
Project CI / Frontend tests (pull_request) Successful in 4m17s
Project CI / Repository checks (pull_request) Successful in 3m14s
Project CI / Backend tests (pull_request) Successful in 7m11s
2026-09-17 09:28:18 +08:00
lhk229 11afc65905 修复AGC客户端抠图认证路由
普通登录态抠图请求改走站内账号鉴权路由

保留 ExternalDeveloper 模式的 External v1 API Key 路由

透传站内 Idempotency-Key 并兼容两种队列响应结构

同步 AGC 抠图契约、技术方案与决策记录
2026-09-17 09:22:59 +08:00
7 changed files with 123 additions and 27 deletions
@@ -14,4 +14,4 @@ Read scopes remain separate: `asset.list` is the current project's local manifes
`agc_create_or_derive_resource` accepts only semantic intent. The client resolves `sourceLocalAssetId`, creates stable request identities, recovers matching pending operations, serializes paid submissions, writes supported media into the current canvas and same-name asset folder, validates downloaded bytes, commits the local manifest transaction, and returns redacted warnings. A tool error or timeout is not permission to generate again with a new identity.
`agc_remove_background` accepts a registered image `sourceLocalAssetId`, `assetName`, and optional `backgroundMode` and `screenColor`. `complex` uses semantic segmentation to identify the foreground; `flat` removes a solid-colour background. Prefer `flat` when the background is known to be solid; omitting the mode selects `complex`. Only `flat` accepts a colour: `auto`, `#RRGGBB`, or omitted for automatic service detection. Never infer a concrete colour for `auto`. Empty or invalid values and colour without `flat` are rejected. The client resolves the formal source resource, canvas/folder context, stable operation identity, idempotency key, and authenticated External v1 `/api/external/v1/editor/images/background-removals` call. Mode and colour are part of request identity. Its result is bounded queue state; Codex must not poll internal workers, construct source URLs, or retry with a new identity after an uncertain response.
`agc_remove_background` accepts a registered image `sourceLocalAssetId`, `assetName`, and optional `backgroundMode` and `screenColor`. `complex` uses semantic segmentation to identify the foreground; `flat` removes a solid-colour background. Prefer `flat` when the background is known to be solid; omitting the mode selects `complex`. Only `flat` accepts a colour: `auto`, `#RRGGBB`, or omitted for automatic service detection. Never infer a concrete colour for `auto`. Empty or invalid values and colour without `flat` are rejected. The client resolves the formal source resource, canvas/folder context, stable operation identity, idempotency key, and authenticated request. Ordinary account mode maps the External v1 shaped route to `/api/editor/images/background-removals`; ExternalDeveloper mode uses `/api/external/v1/editor/images/background-removals`. Mode and colour are part of request identity. Its result is bounded queue state; Codex must not poll internal workers, construct source URLs, or retry with a new identity after an uncertain response.
@@ -1,6 +1,6 @@
{
"schemaVersion": "agc-skill-pack.v1",
"version": "2026-08-26.17",
"version": "2026-08-26.18",
"skills": [
{
"name": "agc-game-production-workflow",
@@ -123,7 +123,7 @@
"agents/openai.yaml",
"references/projection-contract.md"
],
"sha256": "a929c27bc5b2b0bee0b7935e5c7b04ddbab1eb1804fe196f8c2537ad040ca5b1"
"sha256": "b6fa326e07ba48aa542fe3a1841e0fbc425656c6a41be4d575cf2e5c353a87b2"
}
]
}
@@ -1886,7 +1886,7 @@ async fn bridge_create_or_derive_resource(
}
async fn bridge_remove_background(state: &DirectToolBridgeState, arguments: &Value) -> Value {
let result = async {
let result = with_direct_editor_api_credentials(async {
super::direct_tools_mcp::validate_remove_background_arguments(arguments)?;
enforce_project_permission_policy(&state.root, "canvas.asset_generate")?;
enforce_project_permission_policy(&state.root, "asset.register")?;
@@ -1929,25 +1929,20 @@ async fn bridge_remove_background(state: &DirectToolBridgeState, arguments: &Val
screen_color,
);
let (_operation_id, idempotency_key) = state.resource_request_ids(&fingerprint)?;
let route = "/api/external/v1/editor/images/background-removals";
let mut request_body = json!({
"sourceImageSrc": source_resource_id,
"projectId": manifest.project_id,
"assetKind": source_asset.kind,
"assetFolderId": context.asset_folder_id,
"assetLabel": asset_name,
"sourceResourceId": source_resource_id,
});
if background_mode == Some("flat") {
request_body["backgroundMode"] = json!("flat");
}
if let Some(color) = screen_color {
request_body["screenColor"] = json!(color);
}
let route = access.api_route("/api/external/v1/editor/images/background-removals");
let request_body = background_removal_request_body(
&source_resource_id,
&context.project_id,
&source_asset.kind,
&context.asset_folder_id,
&asset_name,
background_mode,
screen_color,
);
let response = crate::http_client::with_agc_main_site_marker(
client
.post(format!("{}{}", api_base_url, route))
.bearer_auth(api_key)
.bearer_auth(access.bearer_token())
.header("Idempotency-Key", idempotency_key)
.json(&request_body),
)
@@ -1965,16 +1960,17 @@ async fn bridge_remove_background(state: &DirectToolBridgeState, arguments: &Val
}
return Err(format!("抠图服务提交失败:HTTP {}", status.as_u16()));
}
let queue_state = external_editor_response_data(&payload).clone();
let queue_state =
background_removal_queue_state(&payload, access.frozen_platform_session().is_some())?;
Ok::<_, String>(json!({
"status": "queued",
"sourceLocalAssetId": source_asset_id,
"assetName": asset_name,
"projectId": manifest.project_id,
"projectId": context.project_id,
"assetFolderId": context.asset_folder_id,
"queueState": bridge_safe_queue_state(queue_state),
}))
}
})
.await;
match result {
Ok(value) => bridge_tool_result(value.to_string(), Vec::new(), false),
@@ -2000,6 +1996,46 @@ fn background_removal_request_fingerprint(
}
}
fn background_removal_request_body(
source_resource_id: &str,
remote_project_id: &str,
asset_kind: &str,
asset_folder_id: &str,
asset_name: &str,
background_mode: Option<&str>,
screen_color: Option<&str>,
) -> Value {
let mut body = json!({
"sourceImageSrc": source_resource_id,
"projectId": remote_project_id,
"assetKind": asset_kind,
"assetFolderId": asset_folder_id,
"assetLabel": asset_name,
"sourceResourceId": source_resource_id,
});
if background_mode == Some("flat") {
body["backgroundMode"] = json!("flat");
}
if let Some(color) = screen_color {
body["screenColor"] = json!(color);
}
body
}
fn background_removal_queue_state(
payload: &Value,
platform_account_route: bool,
) -> Result<Value, String> {
let data = external_editor_response_data(payload);
if platform_account_route {
return data
.get("queueState")
.cloned()
.ok_or_else(|| "抠图服务响应缺少 queueState".to_string());
}
Ok(data.clone())
}
fn bridge_safe_queue_state(value: Value) -> Value {
let object = value.as_object();
json!({
@@ -3604,4 +3640,53 @@ mod tests {
assert!(projection.get("error").is_none());
assert!(projection.get("signedUrl").is_none());
}
#[test]
fn background_removal_queue_state_matches_authentication_route_envelope() {
let platform_payload = json!({
"data": {
"queueState": {
"operationId": "platform-operation-1",
"status": "queued"
}
}
});
assert_eq!(
background_removal_queue_state(&platform_payload, true).expect("platform queue state"),
json!({
"operationId": "platform-operation-1",
"status": "queued"
})
);
let external_payload = json!({
"data": {
"operationId": "external-operation-1",
"status": "queued"
}
});
assert_eq!(
background_removal_queue_state(&external_payload, false).expect("external queue state"),
json!({
"operationId": "external-operation-1",
"status": "queued"
})
);
}
#[test]
fn background_removal_request_uses_bound_remote_project_id() {
let body = background_removal_request_body(
"editor-resource-source",
"remote-project-1",
"character",
"remote-folder-1",
"雄狮-透明底",
Some("complex"),
None,
);
assert_eq!(body["projectId"], "remote-project-1");
assert_ne!(body["projectId"], "local-project-1");
assert_eq!(body["assetFolderId"], "remote-folder-1");
}
}
@@ -2,6 +2,13 @@
> 用途:记录已经确认、会影响后续开发的长期技术/产品/协作决策。短期讨论不要写在这里。
> 当前口径:历史条目的旧路径、旧版本和已退役对象只用于追溯,不构成现行实现依据;如与当前代码或 `docs/README.md` 冲突,以当前代码和最新专题文档为准。
## 2026-09-17 AGC 抠图提交使用远端画布项目身份
- 背景:AGC 已通过本地项目 ID 建立并持久化本地项目到主站远端画布项目的绑定,但 `agc_remove_background` 提交请求仍把本地 `manifest.project_id` 放入 `projectId``assetFolderId` 已使用远端素材目录 ID。主站因此按项目不存在或不属于当前账号返回 404,主站抠图和 BgFilter 本身均正常。
- 决策:抠图请求及工具回执统一使用 `prepare_external_canvas_generation_context` 返回的远端 `context.project_id`;本地 manifest 项目 ID 只用于绑定键和本地状态,不得作为主站业务请求的 `projectId`
- 验证:客户端定向 Rust 测试、格式、编码和 diff 检查通过;未修改主站路由或 BgFilter。
## 2026-09-16 抠图模式与背景色契约
- External v1 抠图和 AGC `agc_remove_background` 支持 `complex`(语义分割识别前景)与 `flat`(纯色背景抠图);明确纯色背景优先 flat,模式缺省仍为 complex,主站前端保持现有行为。
@@ -8176,8 +8183,8 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
## 2026-08-24 AGC Direct 抠图语义工具
- 决策:将 External v1 `/api/external/v1/editor/images/background-removals` 通过 `agc_remove_background` 加入受控 `agc_tools`。工具只接受当前 manifest 的图片 `sourceLocalAssetId` 与结果名称;客户端负责正式 resourceId、画布/素材目录、稳定 operation/idempotency 身份、权限和错误脱敏,不向 Codex 暴露内部 BgFilter worker、凭据或任意 API。
- 约束:异步结果只投影有界队列状态,不允许模型自行构造源 URL 或在不确定提交后更换请求身份;External v1 负责 API Key、幂等接收与统一 operation 查询,客户端不得绕过该契约。
- 决策:将抠图能力通过 `agc_remove_background` 加入受控 `agc_tools`。工具只接受当前 manifest 的图片 `sourceLocalAssetId` 与结果名称;普通登录态使用账号鉴权的 `/api/editor/images/background-removals`ExternalDeveloper 模式使用 External v1 `/api/external/v1/editor/images/background-removals`客户端负责正式 resourceId、画布/素材目录、稳定 operation/idempotency 身份、权限和错误脱敏,不向 Codex 暴露内部 BgFilter worker、凭据或任意 API。
- 约束:异步结果只投影有界队列状态,不允许模型自行构造源 URL 或在不确定提交后更换请求身份;两种路由都接收客户端稳定幂等身份,External v1 继续负责 API Key、幂等接收与统一 operation 查询,客户端不得绕过该契约。
## 2026-08-24 资源详情动作、空态滚动与最终图多步恢复
@@ -65,6 +65,8 @@ OpenAPI 与客户端工具的对外说明只描述模式用途、参数约束和
工具 schema、桥接参数校验和随包 `agc-client-projection` Skill/契约说明必须保持一致。模式与颜色属于请求意图,必须参与客户端幂等指纹;同一图片与名称的不同模式不能复用同一次请求。缺省 complex 且没有颜色时保留既有指纹。主站在默认值归一化之前计算 External 请求指纹,缺失的新字段不序列化,避免旧请求重放发生冲突。
客户端提交前建立的本地项目绑定会返回主站远端 `projectId``assetFolderId`,抠图请求必须使用这两个远端身份;本地 manifest `projectId` 仅用于绑定和本地状态,不能直接提交给主站。
## 实施任务
### 任务一:冻结 BgFilter 契约
@@ -136,7 +136,7 @@ npm 游戏的可预览产物固定为对应 package 目录下的 `dist/index.htm
- 项目路径、projectId、当前 revision、源文件路径与媒体类型、operationId、Idempotency-Key、登录态、项目锁、付费提交、轮询恢复、下载校验与 manifest 事务全部由客户端持有。模型不能提交或覆盖这些字段。同一 Direct `clientTurnId + 规范语义参数` 生成稳定 UUID v4 身份;单回合同参重试复用原 operation,不同请求串行且最多四项。跨回合存在完全匹配的 pending 账本时优先恢复原 operation,不能换键重发。
- 资源查询同时投影未完成 operation 的安全状态。媒体工具成功只返回 operation、本地相对路径、资源类型、Canvas/resource/asset/task 身份、正式序列帧以及脱敏后的 `warnings / sliceWarnings`;错误继续使用统一脱敏边界。客户端资源账本持久化 completed 结果的两类告警,committed replay 不能把历史告警伪装成空集合。
- 角色动画、视频、音效和背景音乐在构造新的远端请求前统一准备当前项目同名画布与素材目录上下文,并在端点支持时携带 `projectId / assetFolderId / canvasCompletion`。角色动画 placeholder 使用源图片真实宽高,避免非方形角色进入画布时失真;正式 resource/asset 与序列帧继续直接复用 External 返回身份,不从首帧伪造重复资源。已有冻结 request body 或已受理 operation 保持不变,不因本次升级重建请求或重复扣费。
- 抠图通过新增 `agc_remove_background` 语义工具开放:模型只提交当前 manifest 的图片 `sourceLocalAssetId` 与结果名称;客户端解析稳定 `resourceId`,准备同名画布/素材目录并生成稳定 operation/idempotency 身份,调用 External v1 `/api/external/v1/editor/images/background-removals`只返回有界队列状态。抠图服务仍由客户端和服务端负责源校验、BgFilter、素材登记与画布事务,Codex 不获得内部 worker、凭据或任意 API 调用权。
- 抠图通过新增 `agc_remove_background` 语义工具开放:模型只提交当前 manifest 的图片 `sourceLocalAssetId` 与结果名称;客户端解析稳定 `resourceId`,准备同名画布/素材目录并生成稳定 operation/idempotency 身份。普通登录态使用账号鉴权的 `/api/editor/images/background-removals`ExternalDeveloper 模式使用 External v1 `/api/external/v1/editor/images/background-removals`;两者都只返回有界队列状态。抠图服务仍由客户端和服务端负责源校验、BgFilter、素材登记与画布事务,Codex 不获得内部 worker、凭据或任意 API 调用权。
## 2026-08-23 AGC 资源生成补齐(视频 / 动画 / 音效 / 背景音乐)
@@ -6266,17 +6266,19 @@ pub(crate) async fn edit_editor_image_for_owner_with_source_snapshot(
pub async fn remove_editor_image_background(
State(state): State<AppState>,
headers: HeaderMap,
Extension(request_context): Extension<RequestContext>,
Extension(authenticated): Extension<AuthenticatedAccessToken>,
Json(payload): Json<EditorBackgroundRemovalRequest>,
) -> Result<Json<Value>, AppError> {
let caller = EditorGenerationCaller::from_authenticated(&authenticated);
let idempotency_key = optional_editor_idempotency_key(&headers)?;
let queue_job = enqueue_editor_background_removal_for_owner(
&state,
&request_context,
&caller,
payload,
None,
idempotency_key,
)
.await?;
Ok(json_success_body(