Compare commits

...

10 Commits

Author SHA1 Message Date
lhk229 b07483fc86 Merge branch 'master' into fix/401
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Waiting to run
Project CI / Backend tests (pull_request) Waiting to run
Project CI / Native shell tests (pull_request) Waiting to run
Project CI / Frontend tests (pull_request) Waiting to run
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Waiting to run
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Waiting to run
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Waiting to run
Project CI / AI game creator shell Rust smoke (pull_request) Waiting to run
Project CI / AI game creator shell Rust crates (pull_request) Waiting to run
Project CI / Repository checks (pull_request) Waiting to run
Project CI / AI game creator shell web tests (pull_request) Waiting to run
2026-09-17 20:25:18 +08:00
lhk229 3eea34fbe0 Merge branch 'master' into fix/401
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Successful in 7m9s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Successful in 7m15s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Failing after 7m35s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Failing after 8m3s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 2m3s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 2m20s
Project CI / Native shell tests (pull_request) Failing after 2m37s
Project CI / Frontend tests (pull_request) Failing after 4m28s
Project CI / AI game creator shell web tests (pull_request) Failing after 3m14s
Project CI / Repository checks (pull_request) Successful in 5m19s
Project CI / Backend tests (pull_request) Successful in 9m1s
2026-09-17 19:10:04 +08:00
lhk229 469a62b4be 合并最新远程master到抠图修复分支
Project CI / AI game creator shell Rust shard 1/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 3/4 (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 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
同步远程master的模板库、项目快照及媒体参数校验更新

合并技能说明与决策记录,保留抠图恢复合同并重新同步技能包指纹
2026-09-17 18:49:53 +08:00
lhk229 8f33dc9b78 修复AGC抠图结果下载登记与任务恢复
将抠图接入资源编辑账本,受理后轮询并下载透明PNG登记本地资源
保留任务身份及模式颜色参数,中断后恢复原任务并修正账号和开发者路由
补充完整链路、失败处理和恢复幂等测试,同步技能包指纹及技术文档
2026-09-17 18:40:55 +08:00
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 / AI game creator shell Rust shard 4/4 (pull_request) Successful in 5m16s
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 / Frontend tests (pull_request) Successful in 5m10s
Project CI / Repository checks (pull_request) Successful in 4m25s
Project CI / Backend tests (pull_request) Successful in 8m25s
Project CI / Native shell tests (pull_request) Successful in 8m3s
Project CI / AI game creator shell web tests (pull_request) Successful in 5m39s
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
11 changed files with 1094 additions and 165 deletions
@@ -14,7 +14,7 @@ Let the client derive projections from real disk changes and trusted tool result
3. Keep read scopes separate: `asset.list` is the current project manifest, `asset.library.list` is the signed-in account library, and the web project's canvas resource read model is the authoritative canvas list. The account library is not the complete canvas list. 3. Keep read scopes separate: `asset.list` is the current project manifest, `asset.library.list` is the signed-in account library, and the web project's canvas resource read model is the authoritative canvas list. The account library is not the complete canvas list.
4. Use `canvas.asset_import` for safe account/canvas asset IDs or project-relative local paths. The client rechecks ownership and validates bytes; host absolute paths require native UI file-picker authorization. 4. Use `canvas.asset_import` for safe account/canvas asset IDs or project-relative local paths. The client rechecks ownership and validates bytes; host absolute paths require native UI file-picker authorization.
5. When the user explicitly asks to create or derive video, character animation, sound effect, or background music, call `agc_create_or_derive_resource`. Use `create` only for video/audio without a source and `derive` with a registered `sourceLocalAssetId`; character animation is always derived from an image. Keep `prompt` inside the per-kind limit that the client really enforces: background music at most 140 characters, sound effect at most 1900, video and character animation at most 4000. A longer prompt is rejected before submission, so write the short version first instead of retrying the same text. 5. When the user explicitly asks to create or derive video, character animation, sound effect, or background music, call `agc_create_or_derive_resource`. Use `create` only for video/audio without a source and `derive` with a registered `sourceLocalAssetId`; character animation is always derived from an image. Keep `prompt` inside the per-kind limit that the client really enforces: background music at most 140 characters, sound effect at most 1900, video and character animation at most 4000. A longer prompt is rejected before submission, so write the short version first instead of retrying the same text.
6. When the user explicitly asks to remove an image background, call `agc_remove_background` with a registered image `sourceLocalAssetId` and `assetName`. Optional `backgroundMode` is `complex` (semantic foreground segmentation; default) or `flat` (solid-colour background removal). Prefer `flat` when the background is known to be solid. Only `flat` accepts optional `screenColor`: `auto`, `#RRGGBB`, or omitted for automatic detection by the service. Do not select a colour on behalf of `auto`. The client requires the signed-in account, owns canvas/folder context and task identity, and returns only bounded queue state. 6. When the user explicitly asks to remove an image background, call `agc_remove_background` with a registered image `sourceLocalAssetId` and `assetName`. Optional `backgroundMode` is `complex` (semantic foreground segmentation; default) or `flat` (solid-colour background removal). Prefer `flat` when the background is known to be solid. Only `flat` accepts optional `screenColor`: `auto`, `#RRGGBB`, or omitted for automatic detection by the service. Do not select a colour on behalf of `auto`. The client requires the signed-in account, owns canvas/folder context and task identity, waits for the accepted operation, downloads and registers the completed local asset, and preserves the operation for recovery when the remote result is not yet known.
7. Preserve existing relative paths when a small edit is sufficient so client resource identities remain stable. 7. Preserve existing relative paths when a small edit is sufficient so client resource identities remain stable.
8. Do not edit `.agent/manifest.json`, revision counters, version records, resource IDs, canvas identities, source provenance, generation ledgers, or browser receipts by hand. 8. Do not edit `.agent/manifest.json`, revision counters, version records, resource IDs, canvas identities, source provenance, generation ledgers, or browser receipts by hand.
9. Do not create a version when no game file changed. The client compares content fingerprints and advances revision only after an actual source change. 9. Do not create a version when no game file changed. The client compares content fingerprints and advances revision only after an actual source change.
@@ -16,4 +16,6 @@ Read scopes remain separate: `asset.list` is the current project's local manifes
`prompt` limits are per kind and are enforced before any paid submission: background music accepts 1-140 characters, sound effect 1-1900, video and character animation 1-4000, and image editing (`agc_edit_image`) 1-32000. The client composes the submitted request from a fixed prefix plus your prompt, so an over-limit prompt fails locally with the exact limit; shorten the text rather than resubmitting the same value. `agc_edit_image` remains the image path; this tool never generates or edits still images. `prompt` limits are per kind and are enforced before any paid submission: background music accepts 1-140 characters, sound effect 1-1900, video and character animation 1-4000, and image editing (`agc_edit_image`) 1-32000. The client composes the submitted request from a fixed prefix plus your prompt, so an over-limit prompt fails locally with the exact limit; shorten the text rather than resubmitting the same value. `agc_edit_image` remains the image path; this tool never generates or edits still images.
`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. After acceptance, the client polls the authenticated generation status route, downloads the completed media, and commits it to the local manifest. If completion is unknown, it retains the same local operation for recovery; it never retries with a new identity or exposes internal worker details.
After an interrupted call, inspect `agc_list_registered_assets.pendingOperations`. Calling `agc_remove_background` again with the same source, name, mode, and colour resumes the matching pending operation. A submission marked `reconciliation-required` needs client-side reconciliation and cannot be automatically resumed. Do not change parameters to bypass a pending task. A queued receipt, fixed progress value, or absent local file does not establish that the background-removal provider is waiting in a queue; report only the observed state.
@@ -1,6 +1,6 @@
{ {
"schemaVersion": "agc-skill-pack.v1", "schemaVersion": "agc-skill-pack.v1",
"version": "2026-08-26.19", "version": "2026-08-26.22",
"skills": [ "skills": [
{ {
"name": "agc-game-production-workflow", "name": "agc-game-production-workflow",
@@ -123,7 +123,7 @@
"agents/openai.yaml", "agents/openai.yaml",
"references/projection-contract.md" "references/projection-contract.md"
], ],
"sha256": "0700d4a7a18ee6151811f38786211ad416863f2e425fdc2ded67555a0a1923a1" "sha256": "72fc5209cd7b30f544a5071714b3796d3757cad670c8cba3b36b5593cf19c160"
} }
] ]
} }
@@ -1294,7 +1294,10 @@ fn bridge_list_registered_assets(root: &Path, arguments: &Value) -> Value {
.map(|asset| bridge_registered_resource(asset, include_sequence_frames)) .map(|asset| bridge_registered_resource(asset, include_sequence_frames))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let next_offset = (offset + resources.len() < total).then_some(offset + resources.len()); let next_offset = (offset + resources.len() < total).then_some(offset + resources.len());
let pending = list_pending_local_project_resource_edits_at( let platform_session = (editor_api_mode() == EditorApiMode::PlatformAccount)
.then(current_platform_session)
.flatten();
let pending = list_pending_local_project_resource_edits_for_session_at(
ListPendingLocalProjectResourceEditsInput { ListPendingLocalProjectResourceEditsInput {
project_path: root project_path: root
.to_str() .to_str()
@@ -1302,6 +1305,7 @@ fn bridge_list_registered_assets(root: &Path, arguments: &Value) -> Value {
.to_string(), .to_string(),
expected_project_id: manifest.project_id, expected_project_id: manifest.project_id,
}, },
platform_session.as_ref(),
)? )?
.into_iter() .into_iter()
.map(|edit| { .map(|edit| {
@@ -1311,6 +1315,8 @@ fn bridge_list_registered_assets(root: &Path, arguments: &Value) -> Value {
"mode": edit.generation_mode, "mode": edit.generation_mode,
"sourceResourceId": edit.source_resource_id, "sourceResourceId": edit.source_resource_id,
"assetName": edit.asset_name, "assetName": edit.asset_name,
"backgroundMode": edit.background_mode,
"screenColor": edit.screen_color,
"phase": edit.phase, "phase": edit.phase,
"createdAt": edit.created_at, "createdAt": edit.created_at,
}) })
@@ -1772,8 +1778,8 @@ async fn bridge_import_account_assets(state: &DirectToolBridgeState, arguments:
fn bridge_completed_resource_result( fn bridge_completed_resource_result(
root: &Path, root: &Path,
kind: DirectResourceGenerationKind, kind: &str,
mode: DirectResourceGenerationMode, mode: &str,
result: DeriveLocalProjectResourceResult, result: DeriveLocalProjectResourceResult,
) -> Result<Value, String> { ) -> Result<Value, String> {
let asset = result let asset = result
@@ -1785,8 +1791,8 @@ fn bridge_completed_resource_result(
Ok(json!({ Ok(json!({
"status": "completed", "status": "completed",
"operationId": result.operation_id, "operationId": result.operation_id,
"kind": kind.as_str(), "kind": kind,
"mode": mode.as_str(), "mode": mode,
"sourceResourceId": result.source_resource_id, "sourceResourceId": result.source_resource_id,
"committedProjectRevision": result.committed_project_revision, "committedProjectRevision": result.committed_project_revision,
"resource": bridge_registered_resource(asset, true), "resource": bridge_registered_resource(asset, true),
@@ -1881,10 +1887,17 @@ async fn bridge_create_or_derive_resource(
source_version_id: None, source_version_id: None,
prompt: input.prompt.clone(), prompt: input.prompt.clone(),
asset_name: input.asset_name.clone(), asset_name: input.asset_name.clone(),
background_mode: None,
screen_color: None,
}; };
with_direct_editor_api_credentials(derive_local_project_resource_at(request)).await? with_direct_editor_api_credentials(derive_local_project_resource_at(request)).await?
}; };
bridge_completed_resource_result(&state.root, input.kind, input.mode, completed) bridge_completed_resource_result(
&state.root,
input.kind.as_str(),
input.mode.as_str(),
completed,
)
} }
.await; .await;
match result { match result {
@@ -1898,7 +1911,8 @@ async fn bridge_create_or_derive_resource(
} }
async fn bridge_remove_background(state: &DirectToolBridgeState, arguments: &Value) -> Value { async fn bridge_remove_background(state: &DirectToolBridgeState, arguments: &Value) -> Value {
let result = async { let _generation_guard = state.resource_generation_gate.lock().await;
let result = with_direct_editor_api_credentials(async {
super::direct_tools_mcp::validate_remove_background_arguments(arguments)?; super::direct_tools_mcp::validate_remove_background_arguments(arguments)?;
enforce_project_permission_policy(&state.root, "canvas.asset_generate")?; enforce_project_permission_policy(&state.root, "canvas.asset_generate")?;
enforce_project_permission_policy(&state.root, "asset.register")?; enforce_project_permission_policy(&state.root, "asset.register")?;
@@ -1919,74 +1933,72 @@ async fn bridge_remove_background(state: &DirectToolBridgeState, arguments: &Val
if !source_asset.media_type.starts_with("image/") { if !source_asset.media_type.starts_with("image/") {
return Err("抠图工具只接受当前项目已登记的图片资源".to_string()); return Err("抠图工具只接受当前项目已登记的图片资源".to_string());
} }
let source_resource_id = source_asset let background_mode = background_mode.unwrap_or("complex").to_string();
.source let source_resource_id = bridge_asset_canonical_resource_id(source_asset);
.resource_id
.as_deref()
.map(str::trim)
.filter(|value| !value.is_empty() && !value.starts_with("local-asset:"))
.ok_or_else(|| "图片资源缺少可供抠图服务使用的正式 resourceId".to_string())?
.to_string();
let (api_base_url, api_key, session) = resolve_canvas_sync_api_credentials(None, None)?;
let access = ExternalEditorBindingAccess::new(&api_base_url, &api_key, session.as_ref())?;
let client = crate::http_client::agc_main_site_client_builder()
.build()
.map_err(|_| "创建抠图服务连接失败".to_string())?;
let context =
prepare_external_canvas_generation_context(&state.root, &client, &access).await?;
let fingerprint = background_removal_request_fingerprint( let fingerprint = background_removal_request_fingerprint(
&source_asset_id, &source_asset_id,
&asset_name, &asset_name,
background_mode, Some(background_mode.as_str()),
screen_color, screen_color,
); );
let (_operation_id, idempotency_key) = state.resource_request_ids(&fingerprint)?; let (_, _, platform_session) = resolve_canvas_sync_api_credentials(None, None)?;
let route = "/api/external/v1/editor/images/background-removals"; let pending = list_pending_local_project_resource_edits_for_session_at(
let mut request_body = json!({ ListPendingLocalProjectResourceEditsInput {
"sourceImageSrc": source_resource_id, project_path: state.root.to_string_lossy().into_owned(),
"projectId": manifest.project_id, expected_project_id: manifest.project_id.clone(),
"assetKind": source_asset.kind, },
"assetFolderId": context.asset_folder_id, platform_session.as_ref(),
"assetLabel": asset_name, )?;
"sourceResourceId": source_resource_id, let matching_pending = pending
}); .into_iter()
if background_mode == Some("flat") { .filter(|pending| {
request_body["backgroundMode"] = json!("flat"); pending.edit_kind == LocalProjectResourceEditKind::BackgroundRemoval
&& (pending.source_asset_id.as_deref() == Some(source_asset_id.as_str())
|| pending.source_resource_id == format!("local-asset:{source_asset_id}"))
&& pending.asset_name == asset_name
&& pending.background_mode.as_deref().unwrap_or("complex")
== background_mode.as_str()
&& pending.screen_color.as_deref() == screen_color
})
.collect::<Vec<_>>();
if matching_pending.len() > 1 {
return Err("存在多个相同抠图 operation,必须先在客户端完成对账".to_string());
} }
if let Some(color) = screen_color { let completed = if let Some(pending) = matching_pending.into_iter().next() {
request_body["screenColor"] = json!(color); resume_local_project_resource_edit_at(ResumeLocalProjectResourceEditInput {
} project_path: state.root.to_string_lossy().into_owned(),
let response = crate::http_client::with_agc_main_site_marker( expected_project_id: manifest.project_id.clone(),
client operation_id: pending.operation_id,
.post(format!("{}{}", api_base_url, route)) })
.bearer_auth(api_key) .await?
.header("Idempotency-Key", idempotency_key) } else {
.json(&request_body), let (operation_id, idempotency_key) = state.resource_request_ids(&fingerprint)?;
) let revision = read_game_creator_agent_runtime_project_revision(&state.root)?.revision;
.send() let request = DeriveLocalProjectResourceInput {
.await project_path: state.root.to_string_lossy().into_owned(),
.map_err(|error| format!("抠图服务提交失败:{error}"))?; expected_project_id: manifest.project_id.clone(),
let status = response.status(); expected_project_revision: revision,
let payload = response operation_id,
.json::<Value>() idempotency_key,
.await edit_kind: LocalProjectResourceEditKind::BackgroundRemoval,
.map_err(|error| format!("抠图服务响应无法解析:{error}"))?; generation_mode: LocalProjectResourceGenerationMode::Derive,
if !status.is_success() { source_resource_id,
if status == reqwest::StatusCode::UNAUTHORIZED { source_asset_id: Some(source_asset_id.clone()),
return Err("authentication-required: 抠图服务提交失败:HTTP 401".to_string()); source_path: Some(source_asset.local_path.clone()),
} source_media_type: Some(source_asset.media_type.clone()),
return Err(format!("抠图服务提交失败:HTTP {}", status.as_u16())); source_subtype: Some(source_asset.kind.clone()),
} producer_task_id: source_asset.source.task_id.clone(),
let queue_state = external_editor_response_data(&payload).clone(); source_version_id: None,
Ok::<_, String>(json!({ prompt: "去除背景".to_string(),
"status": "queued", asset_name: asset_name.clone(),
"sourceLocalAssetId": source_asset_id, background_mode: Some(background_mode),
"assetName": asset_name, screen_color: screen_color.map(str::to_string),
"projectId": manifest.project_id, };
"assetFolderId": context.asset_folder_id, derive_local_project_resource_at(request).await?
"queueState": bridge_safe_queue_state(queue_state), };
})) emit_game_creator_manifest_invalidated(&state.root, "direct-background-removal");
} bridge_completed_resource_result(&state.root, "background-removal", "derive", completed)
})
.await; .await;
match result { match result {
Ok(value) => bridge_tool_result(value.to_string(), Vec::new(), false), Ok(value) => bridge_tool_result(value.to_string(), Vec::new(), false),
@@ -2012,17 +2024,6 @@ fn background_removal_request_fingerprint(
} }
} }
fn bridge_safe_queue_state(value: Value) -> Value {
let object = value.as_object();
json!({
"operationId": object.and_then(|value| value.get("operationId")).and_then(Value::as_str),
"status": object.and_then(|value| value.get("status")).and_then(Value::as_str),
"phaseLabel": object.and_then(|value| value.get("phaseLabel")).and_then(Value::as_str),
"progress": object.and_then(|value| value.get("progress")).and_then(Value::as_u64),
"updatedAtMicros": object.and_then(|value| value.get("updatedAtMicros")).and_then(Value::as_u64),
})
}
fn bridge_art_resources( fn bridge_art_resources(
root: &Path, root: &Path,
asset_paths: &[String], asset_paths: &[String],
@@ -3755,20 +3756,4 @@ mod tests {
); );
} }
} }
#[test]
fn bridge_background_removal_queue_projection_is_bounded() {
let projection = bridge_safe_queue_state(json!({
"operationId": "background-removal-1",
"status": "queued",
"phaseLabel": "排队中",
"progress": 0,
"updatedAtMicros": 1,
"error": "private provider detail",
"signedUrl": "https://private.invalid/result"
}));
assert_eq!(projection["operationId"], "background-removal-1");
assert!(projection.get("error").is_none());
assert!(projection.get("signedUrl").is_none());
}
} }
File diff suppressed because it is too large Load Diff
@@ -421,6 +421,8 @@ type PendingLocalProjectResourceEdit = {
editKind: string; editKind: string;
sourceResourceId: string; sourceResourceId: string;
assetName: string; assetName: string;
backgroundMode?: string | null;
screenColor?: string | null;
phase: string; phase: string;
createdAt: number; createdAt: number;
}; };
@@ -463,6 +465,7 @@ type ResourceEditServiceIdentityConfirmation = {
function pendingResourceEditKindLabel(editKind: string) { function pendingResourceEditKindLabel(editKind: string) {
if (editKind === 'image') return '图片编辑'; if (editKind === 'image') return '图片编辑';
if (editKind === 'background-removal') return '图片抠图';
if (editKind === 'text') return '文本编辑'; if (editKind === 'text') return '文本编辑';
if (editKind === 'agent-result') return '智能体结果编辑'; if (editKind === 'agent-result') return '智能体结果编辑';
return '资源编辑'; return '资源编辑';
@@ -2,6 +2,13 @@
> 用途:记录已经确认、会影响后续开发的长期技术/产品/协作决策。短期讨论不要写在这里。 > 用途:记录已经确认、会影响后续开发的长期技术/产品/协作决策。短期讨论不要写在这里。
> 当前口径:历史条目的旧路径、旧版本和已退役对象只用于追溯,不构成现行实现依据;如与当前代码或 `docs/README.md` 冲突,以当前代码和最新专题文档为准。 > 当前口径:历史条目的旧路径、旧版本和已退役对象只用于追溯,不构成现行实现依据;如与当前代码或 `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-17 图集切分模式改为显式声明 ## 2026-09-17 图集切分模式改为显式声明
- 决策:`sliceMode` 在图标图集生成入口成为必填字段且不保留任何默认值。省略、`null` 或空字符串必须在引用解析、定价、入队和 provider / OSS 副作用之前返回 `400``field=sliceMode`);`grid` 必须同时提供 `gridX`/`gridY``connected-components` 不得携带网格尺寸,二者矛盾同样在副作用前失败关闭。 - 决策:`sliceMode` 在图标图集生成入口成为必填字段且不保留任何默认值。省略、`null` 或空字符串必须在引用解析、定价、入队和 provider / OSS 副作用之前返回 `400``field=sliceMode`);`grid` 必须同时提供 `gridX`/`gridY``connected-components` 不得携带网格尺寸,二者矛盾同样在副作用前失败关闭。
@@ -22,6 +29,7 @@
- 影响范围:`apps/ai-game-creator-shell/src-tauri/src/project/resource_editor.rs`(上限与文案的唯一口径)、`agent/direct_tool_bridge.rs`(按 kind 判定与未登记源资源提示)、`agent/direct_tools_mcp.rs`schema 与校验)、`resources/agc-skills/agc-client-projection/**` 与清单指纹(version `2026-08-26.18`)。**未改** `/api/external/v1` 契约与 OpenAPI、SpacetimeDB schema、前端 TS 侧 `resourceEditPromptMaxLength` 数字、客户端 UI 行为。 - 影响范围:`apps/ai-game-creator-shell/src-tauri/src/project/resource_editor.rs`(上限与文案的唯一口径)、`agent/direct_tool_bridge.rs`(按 kind 判定与未登记源资源提示)、`agent/direct_tools_mcp.rs`schema 与校验)、`resources/agc-skills/agc-client-projection/**` 与清单指纹(version `2026-08-26.18`)。**未改** `/api/external/v1` 契约与 OpenAPI、SpacetimeDB schema、前端 TS 侧 `resourceEditPromptMaxLength` 数字、客户端 UI 行为。
- 验证方式:新增 `tool_prompt_limits_agree_with_the_client_authority`(四个 kind 的 schema 上限、MCP 校验与客户端权威口径同数字,超限文案带真实上限)、`bridge_resource_prompt_limits_follow_the_client_authority`(工具桥侧同类门禁,含图片编辑的 32000 边界)、`edit_image_tool_reaches_the_platform_image_edit_route``background_music_tool_reaches_the_platform_audio_route`(MCP 工具层 → 真实工具桥 → 假平台,断言 `/api/editor/images/edits``/api/editor/audios/background-music/generations` 的路径、Bearer、Idempotency-Key、正文与派生资源落盘,图片编辑正文不得回填 assetKind)、`background_music_prompt_over_the_limit_is_rejected_before_any_bridge_call`(超限在桥请求之前失败)、`unregistered_source_reports_the_registration_follow_up_tools``agent::direct_tools_mcp` 22 passed、`agent::skill_pack` 4 passed、`agent::direct_tool_bridge` 17 passed(7 条本机既有失败见下)、`npm run agc:skill-pack:check``skill-pack:test` 通过。本机 `tempfile::tempdir()` 归属校验失败导致的既有用例(`project::resource_editor` 45 条、`agent::direct_tool_bridge` 7 条)在本轮改动前后**同为失败**(stash 基线复跑确认),与本次无关。 - 验证方式:新增 `tool_prompt_limits_agree_with_the_client_authority`(四个 kind 的 schema 上限、MCP 校验与客户端权威口径同数字,超限文案带真实上限)、`bridge_resource_prompt_limits_follow_the_client_authority`(工具桥侧同类门禁,含图片编辑的 32000 边界)、`edit_image_tool_reaches_the_platform_image_edit_route``background_music_tool_reaches_the_platform_audio_route`(MCP 工具层 → 真实工具桥 → 假平台,断言 `/api/editor/images/edits``/api/editor/audios/background-music/generations` 的路径、Bearer、Idempotency-Key、正文与派生资源落盘,图片编辑正文不得回填 assetKind)、`background_music_prompt_over_the_limit_is_rejected_before_any_bridge_call`(超限在桥请求之前失败)、`unregistered_source_reports_the_registration_follow_up_tools``agent::direct_tools_mcp` 22 passed、`agent::skill_pack` 4 passed、`agent::direct_tool_bridge` 17 passed(7 条本机既有失败见下)、`npm run agc:skill-pack:check``skill-pack:test` 通过。本机 `tempfile::tempdir()` 归属校验失败导致的既有用例(`project::resource_editor` 45 条、`agent::direct_tool_bridge` 7 条)在本轮改动前后**同为失败**(stash 基线复跑确认),与本次无关。
- 关联文档:[AI游戏创作智能体App实施计划](../../technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md)、[踩坑记录](pitfalls.md)。 - 关联文档:[AI游戏创作智能体App实施计划](../../technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md)、[踩坑记录](pitfalls.md)。
## 2026-09-16 抠图模式与背景色契约 ## 2026-09-16 抠图模式与背景色契约
- External v1 抠图和 AGC `agc_remove_background` 支持 `complex`(语义分割识别前景)与 `flat`(纯色背景抠图);明确纯色背景优先 flat,模式缺省仍为 complex,主站前端保持现有行为。 - External v1 抠图和 AGC `agc_remove_background` 支持 `complex`(语义分割识别前景)与 `flat`(纯色背景抠图);明确纯色背景优先 flat,模式缺省仍为 complex,主站前端保持现有行为。
@@ -8196,8 +8204,8 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
## 2026-08-24 AGC Direct 抠图语义工具 ## 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。 - 决策:将抠图能力通过 `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 查询,客户端不得绕过该契约。 - 约束:异步结果只投影有界队列状态,不允许模型自行构造源 URL 或在不确定提交后更换请求身份;两种路由都接收客户端稳定幂等身份,External v1 继续负责 API Key、幂等接收与统一 operation 查询,客户端不得绕过该契约。
## 2026-08-24 资源详情动作、空态滚动与最终图多步恢复 ## 2026-08-24 资源详情动作、空态滚动与最终图多步恢复
@@ -8849,3 +8857,10 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
- 边界:Deploy 阶段在远端 dev / release agent 执行,不受该上限约束。调整只动这两处:`systemctl set-property / revert jenkins.service``docker update --cpus=<n> gitea-runner` 加同步 compose(备份 `/opt/gitea-stack/compose.yml.bak-<时间戳>`)。 - 边界:Deploy 阶段在远端 dev / release agent 执行,不受该上限约束。调整只动这两处:`systemctl set-property / revert jenkins.service``docker update --cpus=<n> gitea-runner` 加同步 compose(备份 `/opt/gitea-stack/compose.yml.bak-<时间戳>`)。
- 验证:限速后 `Genarrative-Full-Build-And-Deploy` #289 / #290 SUCCESS;采样期 Jenkins 峰值 10.2~10.5 核、限流不足 2s(可忽略),runner 峰值 12.07 核且持续出现 throttling,整机回落到 2.6%~19.8%。 - 验证:限速后 `Genarrative-Full-Build-And-Deploy` #289 / #290 SUCCESS;采样期 Jenkins 峰值 10.2~10.5 核、限流不足 2s(可忽略),runner 峰值 12.07 核且持续出现 throttling,整机回落到 2.6%~19.8%。
- 关联文档:[开发运维](../../【开发运维】本地开发验证与生产运维-2026-05-15.md)。 - 关联文档:[开发运维](../../【开发运维】本地开发验证与生产运维-2026-05-15.md)。
## 2026-09-17 AGC 抠图接入本地资源编辑恢复闭环
- 背景:`agc_remove_background` 原先只提交 `/api/editor/images/background-removals` 并返回 `queued`,没有轮询远端任务、下载完成媒体或写入本地 manifestBgFilter 已成功处理但 Agent 因此永远只能看到受理回执。
- 决策:抠图作为 `LocalProjectResourceEditKind::BackgroundRemoval` 接入现有资源编辑账本,模式和背景色写入 operation 身份;提交后复用同一套轮询、结果下载、staging、manifest 提交和恢复逻辑。已有账本优先恢复,禁止在未知结果时换 operation/idempotency 重发。
- 边界:主站异步队列、BgFilter 和 SpacetimeDB schema 不变;Agent 只获得本地完成资源和安全身份投影,不接触内部 worker 或凭据。
- 恢复:已受理任务中断后按原 operation 续查;提交结果不确定时保留账本并人工对账。升级前无账本的 queued 回执不自动迁移或重发,已有远端成果通过正式素材导入恢复。
@@ -61,10 +61,25 @@ OpenAPI 与客户端工具的对外说明只描述模式用途、参数约束和
} }
``` ```
客户端保留旧参数调用;新字段不填时不改变旧调用语义。客户端不读取图片、不自动选色、不把 `auto` 改写为具体颜色,使用原有 Bearer 认证、幂等键和队列返回模型 客户端保留旧参数调用;新字段不填时不改变模式语义。客户端不自动选色、不把 `auto` 改写为具体颜色;源资源校验、认证、异步任务查询、结果下载和本地登记由客户端负责
工具 schema、桥接参数校验和随包 `agc-client-projection` Skill/契约说明必须保持一致。模式与颜色属于请求意图,必须参与客户端幂等指纹;同一图片与名称的不同模式不能复用同一次请求。缺省 complex 且没有颜色时保留既有指纹。主站在默认值归一化之前计算 External 请求指纹,缺失的新字段不序列化,避免旧请求重放发生冲突。 工具 schema、桥接参数校验和随包 `agc-client-projection` Skill/契约说明必须保持一致。模式与颜色属于请求意图,必须参与客户端幂等指纹;同一图片与名称的不同模式不能复用同一次请求。缺省 complex 且没有颜色时保留既有指纹。主站在默认值归一化之前计算 External 请求指纹,缺失的新字段不序列化,避免旧请求重放发生冲突。
客户端提交前建立的本地项目绑定会返回主站远端 `projectId``assetFolderId`,抠图请求必须使用这两个远端身份;本地 manifest `projectId` 仅用于绑定和本地状态,不能直接提交给主站。
### 本地结果与恢复合同
`agc_remove_background` 复用本地资源编辑账本,类型为 `background-removal`。源图片保持不变,抠图结果作为新资源写入项目。模式和背景色随账本持久化并参与请求指纹,其它编辑类型的历史指纹保持不变。
1. 客户端建立源图片的正式资源绑定,在提交前保存 operation、幂等键和请求意图。普通登录态提交 `/api/editor/images/background-removals`,从 `data.queueState.operationId` 读取受理身份;开发者模式提交 External v1 对应路由,从 `data.operationId` 读取身份。
2. 受理后持续查询账号路由 `/api/runtime/external-generation/jobs/{operationId}` 或 External v1 对应状态路由。`queued``running` 只描述远端返回状态;固定进度值、本地文件缺失或 pending 清单为空均不能证明 BgFilter 排队。
3. 远端 completed 后按稳定资源身份换取有效下载 URL,校验结果为带 alpha 通道的有效 PNG,随后复用 staging、manifest 和 revision 提交。只在本地登记完成后向 Agent 返回 `completed``operationId``resource.localAssetId`、相对路径和安全告警,不暴露临时 URL 或凭据。
4. 轮询中断、超时或下载失败保留已受理 operation;`agc_list_registered_assets.pendingOperations` 与客户端恢复面板可见。相同源资源、结果名称、模式和颜色的后续调用优先恢复同一任务,不再次提交。不同账号不能恢复原账号任务;切回原账号后按既有恢复规则续接。
5. 远端 failed 明确失败;提交响应不确定且无法确认 operation 时进入人工对账状态,不自动换键重发。失败/未知均不得伪造透明图或自动切换本地抠图方式。
6. 升级前仅返回 queued、没有本地账本的任务不自动迁移;已有远端结果须通过正式资源查询和导入恢复,不据旧回执重新发起付费请求。
本修复只扩展 AGC 客户端现有工作流,不修改主站队列、BgFilter 或 SpacetimeDB schema。验收覆盖账号与开发者两种响应封装、queued/running/completed、已受理中断恢复不重复 POST、远端失败不登记结果,以及既有资源编辑回归。
## 实施任务 ## 实施任务
### 任务一:冻结 BgFilter 契约 ### 任务一:冻结 BgFilter 契约
@@ -93,6 +108,13 @@ OpenAPI 与客户端工具的对外说明只描述模式用途、参数约束和
## 验收证据 ## 验收证据
2026-09-17 客户端闭环验证:
- `cargo test --locked --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml --bin genarrative-ai-game-creator-shell project::resource_editor:: -- --test-threads=1` 通过。新增 HTTP fixture 覆盖开发者 queued/running/completed、账号 HTTP 200 queueState 与 `data.job`、换签下载、PNG alpha 校验、原图保留和新资源提交。
- 已受理任务的首次轮询失败后,pending 保留模式与颜色;恢复只查询原 operation,整个流程只 POST 一次,成功后清除 pending。远端 failed 不新增结果资源。既有账号隔离、提交原子性和崩溃恢复用例通过。
- `cargo test --locked --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml --bin genarrative-ai-game-creator-shell agent::direct_tool_bridge::tests -- --test-threads=1`、AGC 类型检查、技能包校验、Rust 格式、编码、文档索引与 diff 检查通过。
- 本轮未运行真实登录客户端 → 本地主站 → BgFilter 的端到端 smoke;当前本地后端已停止,自动化证据使用模拟 HTTP 服务。此前 BgFilter 成功日志只证明上游处理完成,不证明主站结果持久化或客户端导入成功。
2026-09-16 实测: 2026-09-16 实测:
- 主站 `cargo test -p api-server background_removal`:36 项通过,覆盖非法请求入队前拒绝、缺省 complex、队列参数保留、旧请求指纹、父侧内部 RPC 和 provider multipart。 - 主站 `cargo test -p api-server background_removal`:36 项通过,覆盖非法请求入队前拒绝、缺省 complex、队列参数保留、旧请求指纹、父侧内部 RPC 和 provider multipart。
@@ -164,7 +164,7 @@ npm 游戏的可预览产物固定为对应 package 目录下的 `dist/index.htm
- 项目路径、projectId、当前 revision、源文件路径与媒体类型、operationId、Idempotency-Key、登录态、项目锁、付费提交、轮询恢复、下载校验与 manifest 事务全部由客户端持有。模型不能提交或覆盖这些字段。同一 Direct `clientTurnId + 规范语义参数` 生成稳定 UUID v4 身份;单回合同参重试复用原 operation,不同请求串行且最多四项。跨回合存在完全匹配的 pending 账本时优先恢复原 operation,不能换键重发。 - 项目路径、projectId、当前 revision、源文件路径与媒体类型、operationId、Idempotency-Key、登录态、项目锁、付费提交、轮询恢复、下载校验与 manifest 事务全部由客户端持有。模型不能提交或覆盖这些字段。同一 Direct `clientTurnId + 规范语义参数` 生成稳定 UUID v4 身份;单回合同参重试复用原 operation,不同请求串行且最多四项。跨回合存在完全匹配的 pending 账本时优先恢复原 operation,不能换键重发。
- 资源查询同时投影未完成 operation 的安全状态。媒体工具成功只返回 operation、本地相对路径、资源类型、Canvas/resource/asset/task 身份、正式序列帧以及脱敏后的 `warnings / sliceWarnings`;错误继续使用统一脱敏边界。客户端资源账本持久化 completed 结果的两类告警,committed replay 不能把历史告警伪装成空集合。 - 资源查询同时投影未完成 operation 的安全状态。媒体工具成功只返回 operation、本地相对路径、资源类型、Canvas/resource/asset/task 身份、正式序列帧以及脱敏后的 `warnings / sliceWarnings`;错误继续使用统一脱敏边界。客户端资源账本持久化 completed 结果的两类告警,committed replay 不能把历史告警伪装成空集合。
- 角色动画、视频、音效和背景音乐在构造新的远端请求前统一准备当前项目同名画布与素材目录上下文,并在端点支持时携带 `projectId / assetFolderId / canvasCompletion`。角色动画 placeholder 使用源图片真实宽高,避免非方形角色进入画布时失真;正式 resource/asset 与序列帧继续直接复用 External 返回身份,不从首帧伪造重复资源。已有冻结 request body 或已受理 operation 保持不变,不因本次升级重建请求或重复扣费。 - 角色动画、视频、音效和背景音乐在构造新的远端请求前统一准备当前项目同名画布与素材目录上下文,并在端点支持时携带 `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`;客户端接收异步受理后轮询任务状态,下载完成媒体并登记到本地 manifest,未知结果保留同一 operation 供恢复。抠图服务仍由客户端和服务端负责源校验、BgFilter、素材登记与画布事务,Codex 不获得内部 worker、凭据或任意 API 调用权。
## 2026-08-23 AGC 资源生成补齐(视频 / 动画 / 音效 / 背景音乐) ## 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( pub async fn remove_editor_image_background(
State(state): State<AppState>, State(state): State<AppState>,
headers: HeaderMap,
Extension(request_context): Extension<RequestContext>, Extension(request_context): Extension<RequestContext>,
Extension(authenticated): Extension<AuthenticatedAccessToken>, Extension(authenticated): Extension<AuthenticatedAccessToken>,
Json(payload): Json<EditorBackgroundRemovalRequest>, Json(payload): Json<EditorBackgroundRemovalRequest>,
) -> Result<Json<Value>, AppError> { ) -> Result<Json<Value>, AppError> {
let caller = EditorGenerationCaller::from_authenticated(&authenticated); let caller = EditorGenerationCaller::from_authenticated(&authenticated);
let idempotency_key = optional_editor_idempotency_key(&headers)?;
let queue_job = enqueue_editor_background_removal_for_owner( let queue_job = enqueue_editor_background_removal_for_owner(
&state, &state,
&request_context, &request_context,
&caller, &caller,
payload, payload,
None, idempotency_key,
) )
.await?; .await?;
Ok(json_success_body( Ok(json_success_body(