内置工具失败改成 typed 错误穿出派发边界,事件带原始 error,软失败清零
- dispatch 返回 Result<Value, ToolCallError>:不再在工具内部吞掉错误再补一个失败 Value,handle_direct_tool_bridge 在唯一一处 match 里同时组装发给模型的内容与诊断
- 新增 ToolCallError { message, redact_limit, error, images },error 直接存序列化后的 typed 错误;每个内置工具在自己的 agent/tool/<tool>/error.rs 定义 typed enum,共用 case(参数不是对象 / 未审核字段等)抽到 agent/tool/error.rs 的 ToolFailure
- AgentRuntimeErrorEvent 增加 error 字段携带原始 typed 错误,schema 升到 agent-runtime-error.v3;应用日志详情行扩成 message=… error=… metadata=…,各带独立长度预算
- 软失败清零:import_account_assets、run_validation、browser_playtest、environment_check、apply_patch、editor_execute、cocos_execute 原先返回 Ok(失败载荷) 的分支改为 typed 错误,并把 status / dispatched / retryAllowed 等载荷带进错误,模型仍能看到
- 失败证据截图走 ToolFailure::attached_images 与 ToolCallError.images,截图放 #[serde(skip)] 字段,不混进诊断里的 error
- bridge_tool_result 去掉 is_error 入参,isError 只在组装处按 Ok/Err 分支写一次;MCP 线上 isError 字段与校验不变
- 修正上一提交遗留的两处 rustfmt:direct_tools_mcp.rs、runtime_tools/context.rs(同一 workspace 整体格式门禁要求)
- 同步更新技术方案文档字段表与共享记忆决策记录
验证:cargo check --bin genarrative-ai-game-creator-shell --tests 通过;cargo test --bin genarrative-ai-game-creator-shell -- agent:: 951 passed / 0 failed;npm run check:encoding、git diff --check 通过;Windows 专属 cocos/unity/godot 分支在 Linux 下临时去 cfg 交叉编译通过(无真实 Windows 构建)
This commit is contained in:
@@ -2013,6 +2013,7 @@ pub(crate) fn record_direct_codex_failure(
|
||||
stage.id(),
|
||||
error_code,
|
||||
&detail,
|
||||
serde_json::to_value(failure).unwrap_or(serde_json::Value::Null),
|
||||
None,
|
||||
serde_json::json!({
|
||||
"legacyDiagnosticWritten": diagnostic_written,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -822,8 +822,7 @@ fn bounded_tool_string(arguments: &Value, field: &str, max_chars: usize) -> Resu
|
||||
fn validate_tool_object_fields(arguments: &Value, allowed: &[&str]) -> Result<(), String> {
|
||||
// 「入参不是对象 / 带了没声明的字段」是内置工具共用的两个 case:文案只写在
|
||||
// `ToolArgumentsRejection` 上,这里只负责渲染成 MCP 结果文本。
|
||||
ToolArgumentsRejection::check(arguments, allowed)
|
||||
.map_err(|rejection| rejection.to_user_msg())
|
||||
ToolArgumentsRejection::check(arguments, allowed).map_err(|rejection| rejection.to_user_msg())
|
||||
}
|
||||
|
||||
/// 与工具桥共用同一份 `agc_list_registered_assets` 入参规则。
|
||||
|
||||
@@ -9403,16 +9403,15 @@ mod canvas_generation_tests {
|
||||
])
|
||||
)
|
||||
).await.expect("both bridge requests must finish without serializing their POSTs");
|
||||
let duplicate = results.pop().expect("duplicate request result");
|
||||
assert_eq!(duplicate["isError"], true, "{duplicate}");
|
||||
assert!(duplicate["content"][0]["text"]
|
||||
.as_str()
|
||||
.unwrap()
|
||||
.contains("任何远端 POST 前拒绝并发请求"));
|
||||
let duplicate = results
|
||||
.pop()
|
||||
.expect("duplicate request result")
|
||||
.expect_err("duplicate request must be rejected before any remote POST");
|
||||
assert!(duplicate.message.contains("任何远端 POST 前拒绝并发请求"));
|
||||
let ids = results
|
||||
.into_iter()
|
||||
.map(|result| {
|
||||
assert_eq!(result["isError"], false, "{result}");
|
||||
let result = result.expect("bridge generation request must succeed");
|
||||
let text = result["content"][0]["text"]
|
||||
.as_str()
|
||||
.expect("bridge result text");
|
||||
|
||||
@@ -12,7 +12,7 @@ use std::path::Path;
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
pub(crate) const AGENT_RUNTIME_ERROR_SCHEMA_VERSION: &str = "agent-runtime-error.v2";
|
||||
pub(crate) const AGENT_RUNTIME_ERROR_SCHEMA_VERSION: &str = "agent-runtime-error.v3";
|
||||
|
||||
/// sidecar 里 `message` 的字符上限。
|
||||
pub(crate) const AGENT_RUNTIME_ERROR_MAX_MESSAGE_CHARS: usize = 8 * 1024;
|
||||
@@ -23,6 +23,7 @@ pub(crate) const AGENT_RUNTIME_ERROR_MAX_MESSAGE_CHARS: usize = 8 * 1024;
|
||||
/// 这里的预算留出身份字段的位置,保证被截掉的是正文尾部,而不是 `eventId`、`code`
|
||||
/// 或 `detailRef`。
|
||||
pub(crate) const AGENT_RUNTIME_ERROR_APP_LOG_MESSAGE_CHARS: usize = 1_200;
|
||||
pub(crate) const AGENT_RUNTIME_ERROR_APP_LOG_ERROR_CHARS: usize = 400;
|
||||
pub(crate) const AGENT_RUNTIME_ERROR_APP_LOG_METADATA_CHARS: usize = 200;
|
||||
|
||||
static ERROR_EVENT_SEQUENCE: AtomicU64 = AtomicU64::new(1);
|
||||
@@ -38,6 +39,9 @@ pub(crate) struct AgentRuntimeErrorEvent {
|
||||
pub occurred_at_unix_nanos: String,
|
||||
pub elapsed_ms: Option<u64>,
|
||||
pub message: String,
|
||||
/// 序列化后的 typed 错误 enum(一个 case 一个变体)。这是开发者侧的结构化事实;
|
||||
/// 没有 typed 错误的调用方传 `Value::Null`。
|
||||
pub error: Value,
|
||||
pub detail_ref: String,
|
||||
pub persistence_failed: bool,
|
||||
pub metadata: Value,
|
||||
@@ -50,6 +54,7 @@ pub(crate) fn persist_agent_runtime_error(
|
||||
stage: &str,
|
||||
code: &str,
|
||||
message: &str,
|
||||
error: Value,
|
||||
elapsed_ms: Option<u64>,
|
||||
metadata: Value,
|
||||
) -> Result<AgentRuntimeErrorEvent, String> {
|
||||
@@ -72,6 +77,7 @@ pub(crate) fn persist_agent_runtime_error(
|
||||
"occurredAtUnixNanos": occurred_at_unix_nanos.to_string(),
|
||||
"elapsedMs": elapsed_ms,
|
||||
"message": safe_message,
|
||||
"error": error,
|
||||
"metadata": metadata,
|
||||
});
|
||||
// 统一错误事件的项目内 sidecar 只在项目目录可见:用户提交错误报告时上传的是 AppData
|
||||
@@ -86,6 +92,7 @@ pub(crate) fn persist_agent_runtime_error(
|
||||
code,
|
||||
&detail_ref,
|
||||
message,
|
||||
&error,
|
||||
elapsed_ms,
|
||||
&metadata,
|
||||
);
|
||||
@@ -109,6 +116,7 @@ pub(crate) fn persist_agent_runtime_error(
|
||||
occurred_at_unix_nanos: occurred_at_unix_nanos.to_string(),
|
||||
elapsed_ms,
|
||||
message: message.to_string(),
|
||||
error,
|
||||
detail_ref,
|
||||
persistence_failed: false,
|
||||
metadata,
|
||||
@@ -138,6 +146,7 @@ pub(crate) fn agent_runtime_error_app_log_lines(
|
||||
code: &str,
|
||||
detail_ref: &str,
|
||||
message: &str,
|
||||
error: &Value,
|
||||
elapsed_ms: Option<u64>,
|
||||
metadata: &Value,
|
||||
) -> [String; 2] {
|
||||
@@ -158,13 +167,18 @@ pub(crate) fn agent_runtime_error_app_log_lines(
|
||||
message,
|
||||
AGENT_RUNTIME_ERROR_APP_LOG_MESSAGE_CHARS,
|
||||
));
|
||||
let error = single_line_log_field(&redact_agent_runtime_error(
|
||||
root,
|
||||
&error.to_string(),
|
||||
AGENT_RUNTIME_ERROR_APP_LOG_ERROR_CHARS,
|
||||
));
|
||||
let metadata = single_line_log_field(&redact_agent_runtime_error(
|
||||
root,
|
||||
&metadata.to_string(),
|
||||
AGENT_RUNTIME_ERROR_APP_LOG_METADATA_CHARS,
|
||||
));
|
||||
let detail_line = format!(
|
||||
"agent.runtime.error.detail eventId={event_id} message={message} metadata={metadata}"
|
||||
"agent.runtime.error.detail eventId={event_id} message={message} error={error} metadata={metadata}"
|
||||
);
|
||||
[identity, detail_line]
|
||||
}
|
||||
@@ -216,13 +230,18 @@ mod tests {
|
||||
"code-generation",
|
||||
"turn-idle-timeout",
|
||||
"本轮没有收到完成事件 C:\\Users\\private\\project https://provider.example/a?token=secret",
|
||||
serde_json::json!({ "type": "timedOut", "deadline": "idle" }),
|
||||
Some(1200),
|
||||
serde_json::json!({"lastEvent":"item/started"}),
|
||||
)
|
||||
.expect("persist event");
|
||||
assert_eq!(event.code, "turn-idle-timeout");
|
||||
assert_eq!(event.message, "本轮没有收到完成事件 C:\\Users\\private\\project https://provider.example/a?token=secret");
|
||||
let path = root.join(&event.detail_ref);
|
||||
let text = std::fs::read_to_string(path).expect("diagnostic");
|
||||
// typed 错误原样进 sidecar:开发侧按变体与字段定位,不解析那句中文。
|
||||
assert!(text.contains("timedOut"), "{text}");
|
||||
assert!(text.contains("idle"), "{text}");
|
||||
assert!(text.contains("<absolute-path>"));
|
||||
assert!(text.contains("<redacted-url>"));
|
||||
assert!(!text.contains("token=secret"));
|
||||
@@ -245,6 +264,7 @@ mod tests {
|
||||
"turn-idle-timeout",
|
||||
".agent/runtime/errors/error-1-1.json",
|
||||
"本轮没有收到完成事件\n附带换行 C:\\Users\\private\\project https://provider.example/a?token=secret\n第二行诊断",
|
||||
&serde_json::json!({ "type": "timedOut", "deadline": "idle" }),
|
||||
Some(1200),
|
||||
&serde_json::json!({"authorization": "Bearer secret"}),
|
||||
);
|
||||
@@ -276,6 +296,10 @@ mod tests {
|
||||
),
|
||||
"{detail}"
|
||||
);
|
||||
assert!(
|
||||
detail.contains("error={\"deadline\":\"idle\",\"type\":\"timedOut\"}"),
|
||||
"{detail}"
|
||||
);
|
||||
assert!(!detail.contains("Bearer secret"), "{detail}");
|
||||
|
||||
// 自由文本里出现裸标记词时,被整行替换的只能是详情行:身份行仍可定位事件。
|
||||
@@ -288,6 +312,7 @@ mod tests {
|
||||
"tool-error",
|
||||
".agent/runtime/errors/error-3-1.json",
|
||||
"credential rotation failed",
|
||||
&Value::Null,
|
||||
None,
|
||||
&serde_json::json!({"tool": "agc_tools"}),
|
||||
);
|
||||
|
||||
@@ -111,6 +111,7 @@ pub(crate) fn append_game_creator_agent_runtime_terminal_public_message_at(
|
||||
&state.phase,
|
||||
"agent-runtime-terminal",
|
||||
error,
|
||||
serde_json::Value::Null,
|
||||
None,
|
||||
serde_json::json!({
|
||||
"agentId": state.agent_id,
|
||||
|
||||
@@ -282,10 +282,9 @@ pub(in crate::agent) async fn observe_agent_runtime_account_asset_library(
|
||||
root: &Path,
|
||||
input: &serde_json::Value,
|
||||
) -> AgentRuntimeToolObservation {
|
||||
if let Err(rejection) = ToolArgumentsRejection::check(
|
||||
input,
|
||||
&["folderId", "query", "offset", "limit"],
|
||||
) {
|
||||
if let Err(rejection) =
|
||||
ToolArgumentsRejection::check(input, &["folderId", "query", "offset", "limit"])
|
||||
{
|
||||
return AgentRuntimeToolObservation {
|
||||
tool: "asset.library.list".to_string(),
|
||||
status: "rejected".to_string(),
|
||||
|
||||
@@ -3,25 +3,32 @@
|
||||
//! 这里只放错误本身;参数解析与工具执行留在 `direct_tool_bridge` 与 `direct_patch`。
|
||||
|
||||
use crate::agent::tool::error::ToolFailure;
|
||||
use serde_json::Value;
|
||||
|
||||
/// `agc_apply_patch` 的每一种失败各占一个变体;文案写在这里,捕获处只调 `to_user_msg`。
|
||||
///
|
||||
/// 补丁被拒绝(`status != completed`)不是错误:那是补丁工具自己的结果载荷,仍然原样回给
|
||||
/// 模型,好让它看到哪一段没打上。
|
||||
#[derive(Debug)]
|
||||
#[derive(serde::Serialize, Debug)]
|
||||
pub(crate) enum ApplyPatchError {
|
||||
/// 补丁没被受理:入参不合法、作用域越界或执行许可已关闭。
|
||||
PatchRejected { cause: String },
|
||||
/// 补丁受理了但没打上或只打了一部分(`status != completed`)。
|
||||
///
|
||||
/// `report` 是补丁工具自己的结果载荷(逐文件命中与冲突),原样带给模型,
|
||||
/// 好让它看到哪一段没打上。
|
||||
PatchNotApplied { report: Value },
|
||||
}
|
||||
|
||||
impl ToolFailure for ApplyPatchError {
|
||||
fn to_user_msg(&self) -> String {
|
||||
match self {
|
||||
Self::PatchRejected { cause } => cause.to_string(),
|
||||
Self::PatchNotApplied { report } => format!("补丁未完全应用:{report}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn redact_limit(&self) -> usize {
|
||||
1800
|
||||
match self {
|
||||
Self::PatchNotApplied { .. } => 32_000,
|
||||
_ => 1800,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,22 +3,43 @@
|
||||
//! 这里只放错误本身;参数解析与工具执行留在 `direct_tool_bridge` 与 `direct_validation`。
|
||||
|
||||
use crate::agent::tool::error::ToolFailure;
|
||||
use serde_json::Value;
|
||||
|
||||
/// `agc_browser_playtest` 的每一种失败各占一个变体;文案写在这里,捕获处只调 `to_user_msg`。
|
||||
#[derive(Debug)]
|
||||
#[derive(serde::Serialize, Debug)]
|
||||
pub(crate) enum BrowserPlaytestError {
|
||||
/// 浏览器试玩自身失败;原因来自 `direct_validation` 的稳定前缀码。
|
||||
PlaytestFailed { cause: String },
|
||||
/// 试玩跑完了但没通过:`report` 是试玩结果载荷(含逐视口结论与截图路径)。
|
||||
///
|
||||
/// 截图正文单独带在 `screenshots` 上:它要变成失败结果里的 MCP image block,
|
||||
/// 但不进错误正文(base64 会把诊断 sidecar 灌爆)。
|
||||
PlaytestNotPassed {
|
||||
report: Value,
|
||||
#[serde(skip)]
|
||||
screenshots: Vec<String>,
|
||||
},
|
||||
}
|
||||
|
||||
impl ToolFailure for BrowserPlaytestError {
|
||||
fn to_user_msg(&self) -> String {
|
||||
match self {
|
||||
Self::PlaytestFailed { cause } => cause.to_string(),
|
||||
Self::PlaytestNotPassed { report, .. } => format!("浏览器试玩未通过:{report}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn redact_limit(&self) -> usize {
|
||||
1800
|
||||
match self {
|
||||
Self::PlaytestNotPassed { .. } => 32_000,
|
||||
_ => 1800,
|
||||
}
|
||||
}
|
||||
|
||||
fn attached_images(&self) -> Vec<String> {
|
||||
match self {
|
||||
Self::PlaytestNotPassed { screenshots, .. } => screenshots.clone(),
|
||||
_ => Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,24 +2,39 @@
|
||||
//!
|
||||
//! 这里只放错误本身;参数解析与工具执行留在 `direct_tool_bridge`。
|
||||
|
||||
use crate::agent::tool::error::{
|
||||
ProjectPermissionRejection, ToolArgumentsRejection, ToolFailure,
|
||||
};
|
||||
use crate::agent::tool::error::{ProjectPermissionRejection, ToolArgumentsRejection, ToolFailure};
|
||||
use serde_json::Value;
|
||||
|
||||
/// Cocos 执行工具的每一种失败各占一个变体;文案写在这里,捕获处只调 `to_user_msg`。
|
||||
///
|
||||
/// 已有的"待核对""执行中"两种状态不是错误:它们各自带 `retryAllowed` 载荷,仍原样回给模型。
|
||||
#[derive(Debug)]
|
||||
#[derive(serde::Serialize, Debug)]
|
||||
pub(crate) enum CocosExecuteError {
|
||||
PluginDisabled,
|
||||
PluginUnavailable,
|
||||
PermissionDenied(ProjectPermissionRejection),
|
||||
Arguments(ToolArgumentsRejection),
|
||||
UnknownOperation { operation: String },
|
||||
SchemaInvalid { cause: String },
|
||||
OperationArgumentsInvalid { cause: String },
|
||||
UnknownOperation {
|
||||
operation: String,
|
||||
},
|
||||
SchemaInvalid {
|
||||
cause: String,
|
||||
},
|
||||
OperationArgumentsInvalid {
|
||||
cause: String,
|
||||
},
|
||||
CodeNotText,
|
||||
CodeInvalid { cause: String },
|
||||
CodeInvalid {
|
||||
cause: String,
|
||||
},
|
||||
/// 已有 Cocos 操作在执行中:本次不发命令,也不要重放。
|
||||
ExecutionBusy,
|
||||
/// 先前的 execute 结果待核对:当前不再发送执行命令,也不要自动重放。
|
||||
ReconciliationPending,
|
||||
/// execute 已发出但回执不是 `completed`:`report` 带 `status` / `retryAllowed` 等现场事实。
|
||||
ExecutionNotCompleted {
|
||||
report: Value,
|
||||
#[serde(skip)]
|
||||
screenshots: Vec<String>,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<ToolArgumentsRejection> for CocosExecuteError {
|
||||
@@ -52,6 +67,32 @@ impl ToolFailure for CocosExecuteError {
|
||||
}
|
||||
Self::CodeNotText => "code 必须是 JavaScript 函数体。".to_string(),
|
||||
Self::CodeInvalid { cause } => cause.to_string(),
|
||||
Self::ExecutionBusy => {
|
||||
"已有 Cocos 操作执行中,请等待回执;本次调用没有发送执行命令,也不要重放。"
|
||||
.to_string()
|
||||
}
|
||||
Self::ReconciliationPending => {
|
||||
"先前 Cocos execute 的结果待核对,当前不再发送执行命令,也不要自动重放;\
|
||||
请人工核对编辑器现场。"
|
||||
.to_string()
|
||||
}
|
||||
Self::ExecutionNotCompleted { report, .. } => {
|
||||
format!("Cocos 执行未完成:{report}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn redact_limit(&self) -> usize {
|
||||
match self {
|
||||
Self::ExecutionNotCompleted { .. } => 32_000,
|
||||
_ => 480,
|
||||
}
|
||||
}
|
||||
|
||||
fn attached_images(&self) -> Vec<String> {
|
||||
match self {
|
||||
Self::ExecutionNotCompleted { screenshots, .. } => screenshots.clone(),
|
||||
_ => Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+45
-14
@@ -14,25 +14,41 @@ const UNREGISTERED_SOURCE_HINT: &str = "先调用 agc_list_registered_assets 选
|
||||
|
||||
/// `agc_create_or_derive_resource` 的每一种失败各占一个变体;文案写在这里,捕获处只调
|
||||
/// `to_user_msg`。
|
||||
#[derive(Debug)]
|
||||
#[derive(serde::Serialize, Debug)]
|
||||
pub(crate) enum CreateOrDeriveResourceError {
|
||||
Arguments(ToolArgumentsRejection),
|
||||
PermissionDenied(ProjectPermissionRejection),
|
||||
Manifest(ProjectManifestRejection),
|
||||
Completion(ResourceCompletionRejection),
|
||||
KindEmpty,
|
||||
KindTooLong { got_chars: usize, max_chars: usize },
|
||||
KindTooLong {
|
||||
got_chars: usize,
|
||||
max_chars: usize,
|
||||
},
|
||||
KindHasControlCharacters,
|
||||
KindUnsupported { got: String },
|
||||
KindUnsupported {
|
||||
got: String,
|
||||
},
|
||||
ModeEmpty,
|
||||
ModeTooLong { got_chars: usize, max_chars: usize },
|
||||
ModeTooLong {
|
||||
got_chars: usize,
|
||||
max_chars: usize,
|
||||
},
|
||||
ModeHasControlCharacters,
|
||||
ModeUnsupported { got: String },
|
||||
ModeUnsupported {
|
||||
got: String,
|
||||
},
|
||||
SourceLocalAssetIdEmpty,
|
||||
SourceLocalAssetIdTooLong { got_chars: usize, max_chars: usize },
|
||||
SourceLocalAssetIdTooLong {
|
||||
got_chars: usize,
|
||||
max_chars: usize,
|
||||
},
|
||||
SourceLocalAssetIdHasControlCharacters,
|
||||
PromptEmpty,
|
||||
PromptTooLong { got_chars: usize, max_chars: usize },
|
||||
PromptTooLong {
|
||||
got_chars: usize,
|
||||
max_chars: usize,
|
||||
},
|
||||
PromptHasControlCharacters,
|
||||
PromptTooLongForKind {
|
||||
kind: DirectResourceGenerationKind,
|
||||
@@ -40,7 +56,10 @@ pub(crate) enum CreateOrDeriveResourceError {
|
||||
max_chars: usize,
|
||||
},
|
||||
AssetNameEmpty,
|
||||
AssetNameTooLong { got_chars: usize, max_chars: usize },
|
||||
AssetNameTooLong {
|
||||
got_chars: usize,
|
||||
max_chars: usize,
|
||||
},
|
||||
AssetNameHasControlCharacters,
|
||||
ImageCreationRequiresRegisteredSource,
|
||||
CharacterAnimationRequiresRegisteredSource,
|
||||
@@ -48,13 +67,25 @@ pub(crate) enum CreateOrDeriveResourceError {
|
||||
DeriveModeWithoutSource,
|
||||
SourceAssetNotRegistered,
|
||||
ProjectPathUnprojectable,
|
||||
GenerationQueueUnavailable { cause: String },
|
||||
PaidDispatchRefused { cause: String },
|
||||
PendingEditsUnreadable { cause: String },
|
||||
GenerationQueueUnavailable {
|
||||
cause: String,
|
||||
},
|
||||
PaidDispatchRefused {
|
||||
cause: String,
|
||||
},
|
||||
PendingEditsUnreadable {
|
||||
cause: String,
|
||||
},
|
||||
DuplicatePendingOperations,
|
||||
TurnIdUnavailable { cause: String },
|
||||
ProjectRevisionUnavailable { cause: String },
|
||||
ResourceGenerationFailed { cause: String },
|
||||
TurnIdUnavailable {
|
||||
cause: String,
|
||||
},
|
||||
ProjectRevisionUnavailable {
|
||||
cause: String,
|
||||
},
|
||||
ResourceGenerationFailed {
|
||||
cause: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<ToolArgumentsRejection> for CreateOrDeriveResourceError {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
use crate::agent::tool::error::{DirectExecutionGateRejection, ToolFailure};
|
||||
|
||||
/// `agc_delivery_status` 的每一种失败各占一个变体;文案写在这里,捕获处只调 `to_user_msg`。
|
||||
#[derive(Debug)]
|
||||
#[derive(serde::Serialize, Debug)]
|
||||
pub(crate) enum DeliveryStatusError {
|
||||
/// 只读状态工具不接受任何参数。
|
||||
ArgumentsNotAccepted,
|
||||
|
||||
@@ -6,14 +6,11 @@ use crate::agent::tool::create_or_derive_resource::error::CreateOrDeriveResource
|
||||
use crate::agent::tool::error::{ToolArgumentsRejection, ToolFailure};
|
||||
|
||||
/// `agc_edit_image` 的每一种失败各占一个变体;文案写在这里,捕获处只调 `to_user_msg`。
|
||||
#[derive(Debug)]
|
||||
#[derive(serde::Serialize, Debug)]
|
||||
pub(crate) enum EditImageError {
|
||||
Arguments(ToolArgumentsRejection),
|
||||
SourceLocalAssetIdEmpty,
|
||||
SourceLocalAssetIdTooLong {
|
||||
got_chars: usize,
|
||||
max_chars: usize,
|
||||
},
|
||||
SourceLocalAssetIdTooLong { got_chars: usize, max_chars: usize },
|
||||
SourceLocalAssetIdHasControlCharacters,
|
||||
PromptEmpty,
|
||||
PromptTooLong { got_chars: usize, max_chars: usize },
|
||||
@@ -40,7 +37,9 @@ impl ToolFailure for EditImageError {
|
||||
fn to_user_msg(&self) -> String {
|
||||
match self {
|
||||
Self::Arguments(rejection) => rejection.to_user_msg(),
|
||||
Self::SourceLocalAssetIdEmpty => "编辑图片失败:sourceLocalAssetId 不能为空。".to_string(),
|
||||
Self::SourceLocalAssetIdEmpty => {
|
||||
"编辑图片失败:sourceLocalAssetId 不能为空。".to_string()
|
||||
}
|
||||
Self::SourceLocalAssetIdTooLong {
|
||||
got_chars,
|
||||
max_chars,
|
||||
@@ -55,9 +54,7 @@ impl ToolFailure for EditImageError {
|
||||
got_chars,
|
||||
max_chars,
|
||||
} => format!("编辑图片失败:prompt 有 {got_chars} 个字符,超过上限 {max_chars}。"),
|
||||
Self::PromptHasControlCharacters => {
|
||||
"编辑图片失败:prompt 含有控制字符。".to_string()
|
||||
}
|
||||
Self::PromptHasControlCharacters => "编辑图片失败:prompt 含有控制字符。".to_string(),
|
||||
Self::AssetNameEmpty => "编辑图片失败:assetName 不能为空。".to_string(),
|
||||
Self::AssetNameTooLong {
|
||||
got_chars,
|
||||
|
||||
@@ -2,14 +2,13 @@
|
||||
//!
|
||||
//! 这里只放错误本身;参数解析与工具执行留在 `direct_tool_bridge`。
|
||||
|
||||
use crate::agent::tool::error::{
|
||||
ProjectPermissionRejection, ToolArgumentsRejection, ToolFailure,
|
||||
};
|
||||
use crate::agent::tool::error::{ProjectPermissionRejection, ToolArgumentsRejection, ToolFailure};
|
||||
use serde_json::Value;
|
||||
|
||||
/// 两个编辑器执行工具之间的差异:插件身份、给用户看的编辑器名、代码语言名。
|
||||
///
|
||||
/// 差异做成枚举,错误变体只带 `EditorKind`,不在文案里手写编辑器名。
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
#[derive(serde::Serialize, Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub(crate) enum EditorKind {
|
||||
Unity,
|
||||
Godot,
|
||||
@@ -39,15 +38,30 @@ impl EditorKind {
|
||||
}
|
||||
|
||||
/// 编辑器执行工具的每一种失败各占一个变体;文案写在这里,捕获处只调 `to_user_msg`。
|
||||
#[derive(Debug)]
|
||||
#[derive(serde::Serialize, Debug)]
|
||||
pub(crate) enum EditorExecuteError {
|
||||
PluginUnavailable { editor: EditorKind },
|
||||
PluginUnavailable {
|
||||
editor: EditorKind,
|
||||
},
|
||||
Arguments(ToolArgumentsRejection),
|
||||
PermissionDenied(ProjectPermissionRejection),
|
||||
CodeNotText { editor: EditorKind },
|
||||
CodeNotText {
|
||||
editor: EditorKind,
|
||||
},
|
||||
CodeInvalid,
|
||||
/// 编辑器适配器拒绝了这次执行,或回执不可用。
|
||||
EditorRejected { cause: String },
|
||||
EditorRejected {
|
||||
cause: String,
|
||||
},
|
||||
/// 适配器返回了「未完成」的回执:`report` 里带 `dispatched` / `retryAllowed` 等现场事实。
|
||||
ExecutionNotCompleted {
|
||||
editor: EditorKind,
|
||||
report: Value,
|
||||
},
|
||||
/// 执行任务异常退出,回执不可用:结果待核对,不要自动重放。
|
||||
ExecutionUnconfirmed {
|
||||
editor: EditorKind,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<ToolArgumentsRejection> for EditorExecuteError {
|
||||
@@ -75,6 +89,21 @@ impl ToolFailure for EditorExecuteError {
|
||||
}
|
||||
Self::CodeInvalid => "code 不能为空、包含 NUL 或超过 128 KiB。".to_string(),
|
||||
Self::EditorRejected { cause } => cause.to_string(),
|
||||
Self::ExecutionNotCompleted { editor, report } => {
|
||||
format!("{} 执行未完成:{report}", editor.label())
|
||||
}
|
||||
Self::ExecutionUnconfirmed { editor } => format!(
|
||||
"{} 执行任务异常,回执不可用;执行已发出,结果待核对,不要自动重放:\
|
||||
{{\"status\":\"needs-reconciliation\",\"dispatched\":true,\"retryAllowed\":false}}",
|
||||
editor.label()
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn redact_limit(&self) -> usize {
|
||||
match self {
|
||||
Self::ExecutionNotCompleted { .. } | Self::ExecutionUnconfirmed { .. } => 32_000,
|
||||
_ => 480,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,15 +2,18 @@
|
||||
//!
|
||||
//! 这里只放错误本身;参数解析与工具执行留在 `direct_tool_bridge`。
|
||||
|
||||
use crate::agent::tool::error::{
|
||||
ProjectPermissionRejection, ToolArgumentsRejection, ToolFailure,
|
||||
};
|
||||
use crate::agent::tool::error::{ProjectPermissionRejection, ToolArgumentsRejection, ToolFailure};
|
||||
use serde_json::Value;
|
||||
|
||||
/// `agc_environment_check` 不接受任何参数,所以它自己的失败只有权限门禁一类。
|
||||
#[derive(Debug)]
|
||||
/// `agc_environment_check` 不接受任何参数,所以它自己的失败只有权限门禁与环境未就绪两类。
|
||||
#[derive(serde::Serialize, Debug)]
|
||||
pub(crate) enum EnvironmentCheckError {
|
||||
Arguments(ToolArgumentsRejection),
|
||||
PermissionDenied(ProjectPermissionRejection),
|
||||
/// 环境检查跑完了但结论不是 `ready`:`report` 是检查载荷,原样带给模型。
|
||||
EnvironmentNotReady {
|
||||
report: Value,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<ToolArgumentsRejection> for EnvironmentCheckError {
|
||||
@@ -30,6 +33,14 @@ impl ToolFailure for EnvironmentCheckError {
|
||||
match self {
|
||||
Self::Arguments(rejection) => rejection.to_user_msg(),
|
||||
Self::PermissionDenied(rejection) => rejection.to_user_msg(),
|
||||
Self::EnvironmentNotReady { report } => format!("本地环境未就绪:{report}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn redact_limit(&self) -> usize {
|
||||
match self {
|
||||
Self::EnvironmentNotReady { .. } => 32_000,
|
||||
_ => 480,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
use serde_json::Value;
|
||||
|
||||
/// 工具失败对用户(以及转述给用户的模型)说的那一句话。
|
||||
pub(crate) trait ToolFailure {
|
||||
pub(crate) trait ToolFailure: serde::Serialize {
|
||||
fn to_user_msg(&self) -> String;
|
||||
|
||||
/// 这句话落进工具结果前允许保留的字符数上限。
|
||||
@@ -17,13 +17,21 @@ pub(crate) trait ToolFailure {
|
||||
fn redact_limit(&self) -> usize {
|
||||
480
|
||||
}
|
||||
|
||||
/// 失败时仍要一并回给模型的证据图(MCP image block 的 base64 正文)。
|
||||
///
|
||||
/// 只有真正的证据类失败才带:浏览器验证没通过时的双端截图、Cocos 预览截图。
|
||||
/// 证据图不进 `serde::Serialize` 的错误正文,避免 base64 正文灌爆诊断 sidecar。
|
||||
fn attached_images(&self) -> Vec<String> {
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
|
||||
/// 所有内置工具共用的入参形状校验:入参不是对象,或带了没声明的字段。
|
||||
///
|
||||
/// 只有这两个 case 是所有工具共享的;具体字段自己的语义(空值、超长、越界……)各工具
|
||||
/// 用自己 enum 的变体表达。
|
||||
#[derive(Debug)]
|
||||
#[derive(serde::Serialize, Debug)]
|
||||
pub(crate) enum ToolArgumentsRejection {
|
||||
NotObject,
|
||||
UnknownField { field: String },
|
||||
@@ -49,9 +57,9 @@ impl ToolFailure for ToolArgumentsRejection {
|
||||
fn to_user_msg(&self) -> String {
|
||||
match self {
|
||||
Self::NotObject => "工具参数必须是对象。".to_string(),
|
||||
Self::UnknownField { field } => format!(
|
||||
"不认识的工具参数「{field}」,请只传该工具声明支持过的字段。"
|
||||
),
|
||||
Self::UnknownField { field } => {
|
||||
format!("不认识的工具参数「{field}」,请只传该工具声明支持过的字段。")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -59,7 +67,7 @@ impl ToolFailure for ToolArgumentsRejection {
|
||||
/// 项目权限门禁拒绝该动作,或策略本身读不出来。
|
||||
///
|
||||
/// 两种失败各占一个变体;产生方是 `project::verification` 的两个门禁函数。
|
||||
#[derive(Debug)]
|
||||
#[derive(serde::Serialize, Debug)]
|
||||
pub(crate) enum ProjectPermissionRejection {
|
||||
Denied { command_id: String },
|
||||
PolicyUnavailable { cause: String },
|
||||
@@ -74,9 +82,9 @@ impl ToolFailure for ProjectPermissionRejection {
|
||||
Self::Denied { command_id } => {
|
||||
format!("项目权限策略拒绝执行:{command_id}")
|
||||
}
|
||||
Self::PolicyUnavailable { cause } => format!(
|
||||
"读取项目权限策略失败,无法确认该动作是否被允许,本次操作未执行:{cause}"
|
||||
),
|
||||
Self::PolicyUnavailable { cause } => {
|
||||
format!("读取项目权限策略失败,无法确认该动作是否被允许,本次操作未执行:{cause}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -85,7 +93,7 @@ impl ToolFailure for ProjectPermissionRejection {
|
||||
///
|
||||
/// 三个列表工具的 `offset`/`limit` 语义完全一致,只是默认值与上限不同;上限随变体带上,
|
||||
/// 所以文案只需要一份。
|
||||
#[derive(Debug)]
|
||||
#[derive(serde::Serialize, Debug)]
|
||||
pub(crate) enum ToolPageRejection {
|
||||
OffsetNotInteger { got: String },
|
||||
OffsetTooLarge { got: usize, max: usize },
|
||||
@@ -99,9 +107,9 @@ impl ToolFailure for ToolPageRejection {
|
||||
Self::OffsetNotInteger { got } => {
|
||||
format!("工具参数 offset 必须是非负整数,当前是「{got}」。")
|
||||
}
|
||||
Self::OffsetTooLarge { got, max } => format!(
|
||||
"工具参数 offset={got} 超出上限 {max},请从更小的偏移重新分页。"
|
||||
),
|
||||
Self::OffsetTooLarge { got, max } => {
|
||||
format!("工具参数 offset={got} 超出上限 {max},请从更小的偏移重新分页。")
|
||||
}
|
||||
Self::LimitNotInteger { got } => {
|
||||
format!("工具参数 limit 必须是整数,当前是「{got}」。")
|
||||
}
|
||||
@@ -115,7 +123,7 @@ impl ToolFailure for ToolPageRejection {
|
||||
/// 本地项目清单(manifest)读不出来:清单文件损坏、权限不足或项目尚未初始化。
|
||||
///
|
||||
/// 读清单是多个工具的公共第一步,失败语义相同,因此文案只在这里写一份。
|
||||
#[derive(Debug)]
|
||||
#[derive(serde::Serialize, Debug)]
|
||||
pub(crate) struct ProjectManifestRejection {
|
||||
pub(crate) cause: String,
|
||||
}
|
||||
@@ -130,7 +138,7 @@ impl ToolFailure for ProjectManifestRejection {
|
||||
///
|
||||
/// `agc_create_or_derive_resource` 与 `agc_remove_background` 共用同一个完成投影,
|
||||
/// 失败语义相同,因此文案只在这里写一份。
|
||||
#[derive(Debug)]
|
||||
#[derive(serde::Serialize, Debug)]
|
||||
pub(crate) enum ResourceCompletionRejection {
|
||||
MissingAsset,
|
||||
WarningsUnreadable { cause: String },
|
||||
@@ -151,7 +159,7 @@ impl ToolFailure for ResourceCompletionRejection {
|
||||
///
|
||||
/// 独立客户端 MCP 没有 Direct 回合的执行账本,不能借用其它回合的预算,所以这里直接拒绝,
|
||||
/// 而不是让工具在半途发现账本不存在。
|
||||
#[derive(Debug)]
|
||||
#[derive(serde::Serialize, Debug)]
|
||||
pub(crate) enum DirectTurnGateRejection {
|
||||
/// 该工具整体属于客户端 Direct 回合。
|
||||
ToolRequiresDirectTurn { tool: String },
|
||||
@@ -176,7 +184,7 @@ impl ToolFailure for DirectTurnGateRejection {
|
||||
/// 客户端工具桥收到没有登记过的工具名。
|
||||
///
|
||||
/// 带上收到的名字:模型拼错工具名时要能从错误里看出自己写了什么,而不是只知道"不认识"。
|
||||
#[derive(Debug)]
|
||||
#[derive(serde::Serialize, Debug)]
|
||||
pub(crate) struct UnknownClientToolRejection {
|
||||
pub(crate) tool: String,
|
||||
}
|
||||
@@ -191,7 +199,7 @@ impl ToolFailure for UnknownClientToolRejection {
|
||||
///
|
||||
/// 这些都不是某个工具的入参问题,而是宿主这一侧的事实;任何工具都可能碰到,所以在这里
|
||||
/// 定义一次。
|
||||
#[derive(Debug)]
|
||||
#[derive(serde::Serialize, Debug)]
|
||||
pub(crate) enum DirectExecutionGateRejection {
|
||||
/// 执行许可(付费/写入/执行租约)取不到。
|
||||
PermitUnavailable { cause: String },
|
||||
|
||||
@@ -3,13 +3,11 @@
|
||||
//! 这里只放错误本身;参数解析与工具执行留在 `direct_tool_bridge`。
|
||||
|
||||
use crate::agent::generation::PLATFORM_ART_ASSET_GENERATION_KINDS;
|
||||
use crate::agent::tool::error::{
|
||||
ProjectPermissionRejection, ToolArgumentsRejection, ToolFailure,
|
||||
};
|
||||
use crate::agent::tool::error::{ProjectPermissionRejection, ToolArgumentsRejection, ToolFailure};
|
||||
use shared_contracts::game_creation_app::GameCreationAppAssetKind;
|
||||
|
||||
/// `agc_generate_image` 的每一种失败各占一个变体;文案写在这里,捕获处只调 `to_user_msg`。
|
||||
#[derive(Debug)]
|
||||
#[derive(serde::Serialize, Debug)]
|
||||
pub(crate) enum GenerateImageError {
|
||||
Arguments(ToolArgumentsRejection),
|
||||
PermissionDenied(ProjectPermissionRejection),
|
||||
|
||||
+53
-21
@@ -2,29 +2,54 @@
|
||||
//!
|
||||
//! 这里只放错误本身;参数解析与工具执行留在 `direct_tool_bridge`。
|
||||
|
||||
use crate::agent::tool::error::{
|
||||
ProjectPermissionRejection, ToolArgumentsRejection, ToolFailure,
|
||||
};
|
||||
use crate::agent::tool::error::{ProjectPermissionRejection, ToolArgumentsRejection, ToolFailure};
|
||||
use serde_json::Value;
|
||||
|
||||
/// `agc_import_account_assets` 的每一种失败各占一个变体;文案写在这里,捕获处只调
|
||||
/// `to_user_msg`。
|
||||
#[derive(Debug)]
|
||||
#[derive(serde::Serialize, Debug)]
|
||||
pub(crate) enum ImportAccountAssetsError {
|
||||
Arguments(ToolArgumentsRejection),
|
||||
PermissionDenied(ProjectPermissionRejection),
|
||||
AssetIdsNotStringArray { got: String },
|
||||
AssetIdsTooMany { max: usize },
|
||||
AssetIdsNotStringArray {
|
||||
got: String,
|
||||
},
|
||||
AssetIdsTooMany {
|
||||
max: usize,
|
||||
},
|
||||
AssetIdsEntryEmpty,
|
||||
AssetIdsEntryTooLong { got_chars: usize, max_chars: usize },
|
||||
AssetIdsEntryTooLong {
|
||||
got_chars: usize,
|
||||
max_chars: usize,
|
||||
},
|
||||
AssetIdsEntryHasControlCharacters,
|
||||
LocalPathsNotStringArray { got: String },
|
||||
LocalPathsTooMany { max: usize },
|
||||
LocalPathsNotStringArray {
|
||||
got: String,
|
||||
},
|
||||
LocalPathsTooMany {
|
||||
max: usize,
|
||||
},
|
||||
LocalPathsEntryEmpty,
|
||||
LocalPathsEntryTooLong { got_chars: usize, max_chars: usize },
|
||||
LocalPathsEntryTooLong {
|
||||
got_chars: usize,
|
||||
max_chars: usize,
|
||||
},
|
||||
LocalPathsEntryHasControlCharacters,
|
||||
LocalPathsNotProjectRelative { got: String },
|
||||
LocalPathsProtectedControlPath { got: String },
|
||||
LocalPathsNotProjectRelative {
|
||||
got: String,
|
||||
},
|
||||
LocalPathsProtectedControlPath {
|
||||
got: String,
|
||||
},
|
||||
NoImportSource,
|
||||
/// 一项都没登记上:`report` 是逐项失败载荷,原样带给模型。
|
||||
ImportFailed {
|
||||
report: Value,
|
||||
},
|
||||
/// 有一部分登记上了:`report` 里带成功的项与逐项失败原因,原样带给模型。
|
||||
ImportPartial {
|
||||
report: Value,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<ToolArgumentsRejection> for ImportAccountAssetsError {
|
||||
@@ -50,9 +75,7 @@ impl ToolFailure for ImportAccountAssetsError {
|
||||
Self::AssetIdsTooMany { max } => {
|
||||
format!("登记素材失败:assetIds 一次最多包含 {max} 项。")
|
||||
}
|
||||
Self::AssetIdsEntryEmpty => {
|
||||
"登记素材失败:assetIds 只能包含非空字符串。".to_string()
|
||||
}
|
||||
Self::AssetIdsEntryEmpty => "登记素材失败:assetIds 只能包含非空字符串。".to_string(),
|
||||
Self::AssetIdsEntryTooLong {
|
||||
got_chars,
|
||||
max_chars,
|
||||
@@ -80,15 +103,24 @@ impl ToolFailure for ImportAccountAssetsError {
|
||||
Self::LocalPathsEntryHasControlCharacters => {
|
||||
"登记素材失败:localPaths 中存在含有控制字符的条目。".to_string()
|
||||
}
|
||||
Self::LocalPathsNotProjectRelative { got } => format!(
|
||||
"登记素材失败:localPaths 的「{got}」不是受控项目根内的项目相对路径。"
|
||||
),
|
||||
Self::LocalPathsProtectedControlPath { got } => format!(
|
||||
"登记素材失败:localPaths 的「{got}」命中隐藏、构建或敏感控制路径。"
|
||||
),
|
||||
Self::LocalPathsNotProjectRelative { got } => {
|
||||
format!("登记素材失败:localPaths 的「{got}」不是受控项目根内的项目相对路径。")
|
||||
}
|
||||
Self::LocalPathsProtectedControlPath { got } => {
|
||||
format!("登记素材失败:localPaths 的「{got}」命中隐藏、构建或敏感控制路径。")
|
||||
}
|
||||
Self::NoImportSource => {
|
||||
"登记素材失败:至少提供一个非空的 assetIds 或 localPaths 数组。".to_string()
|
||||
}
|
||||
Self::ImportFailed { report } => format!("登记素材失败:{report}"),
|
||||
Self::ImportPartial { report } => format!("登记素材部分成功:{report}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn redact_limit(&self) -> usize {
|
||||
match self {
|
||||
Self::ImportFailed { .. } | Self::ImportPartial { .. } => 32_000,
|
||||
_ => 480,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
//! 这里只放错误本身;参数解析与工具执行留在 `direct_tool_bridge`。
|
||||
|
||||
use crate::agent::tool::error::{
|
||||
ProjectPermissionRejection, ToolArgumentsRejection, ToolPageRejection, ToolFailure,
|
||||
ProjectPermissionRejection, ToolArgumentsRejection, ToolFailure, ToolPageRejection,
|
||||
};
|
||||
|
||||
/// `agc_list_account_assets` 的每一种失败各占一个变体;文案写在这里,捕获处只调
|
||||
/// `to_user_msg`。
|
||||
#[derive(Debug)]
|
||||
#[derive(serde::Serialize, Debug)]
|
||||
pub(crate) enum ListAccountAssetsError {
|
||||
Arguments(ToolArgumentsRejection),
|
||||
PermissionDenied(ProjectPermissionRejection),
|
||||
@@ -50,9 +50,9 @@ impl ToolFailure for ListAccountAssetsError {
|
||||
Self::FolderIdTooLong {
|
||||
got_chars,
|
||||
max_chars,
|
||||
} => format!(
|
||||
"列出账号素材失败:folderId 有 {got_chars} 个字符,超过上限 {max_chars}。"
|
||||
),
|
||||
} => {
|
||||
format!("列出账号素材失败:folderId 有 {got_chars} 个字符,超过上限 {max_chars}。")
|
||||
}
|
||||
Self::FolderIdHasControlCharacters => {
|
||||
"列出账号素材失败:folderId 含有控制字符。".to_string()
|
||||
}
|
||||
@@ -61,9 +61,7 @@ impl ToolFailure for ListAccountAssetsError {
|
||||
got_chars,
|
||||
max_chars,
|
||||
} => format!("列出账号素材失败:query 有 {got_chars} 个字符,超过上限 {max_chars}。"),
|
||||
Self::QueryHasControlCharacters => {
|
||||
"列出账号素材失败:query 含有控制字符。".to_string()
|
||||
}
|
||||
Self::QueryHasControlCharacters => "列出账号素材失败:query 含有控制字符。".to_string(),
|
||||
Self::AccountAssetsUnreadable { cause } => {
|
||||
format!("列出账号素材失败:读取账号素材列表失败:{cause}")
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
//! 这里只放错误本身;参数解析与工具执行留在 `direct_tool_bridge`。
|
||||
|
||||
use crate::agent::tool::error::{
|
||||
ProjectManifestRejection, ProjectPermissionRejection, ToolArgumentsRejection,
|
||||
ToolPageRejection, ToolFailure,
|
||||
ProjectManifestRejection, ProjectPermissionRejection, ToolArgumentsRejection, ToolFailure,
|
||||
ToolPageRejection,
|
||||
};
|
||||
|
||||
/// `agc_list_project_files` 允许的 `kind` 过滤值。
|
||||
@@ -13,7 +13,7 @@ pub(crate) const LIST_PROJECT_FILES_KINDS: &[&str] = &[
|
||||
];
|
||||
|
||||
/// `agc_list_project_files` 的每一种失败各占一个变体;文案写在这里,捕获处只调 `to_user_msg`。
|
||||
#[derive(Debug)]
|
||||
#[derive(serde::Serialize, Debug)]
|
||||
pub(crate) enum ListProjectFilesError {
|
||||
Arguments(ToolArgumentsRejection),
|
||||
PermissionDenied(ProjectPermissionRejection),
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
//! 这里只放错误本身;参数解析与工具执行留在 `direct_tool_bridge`。
|
||||
|
||||
use crate::agent::tool::error::{
|
||||
ProjectManifestRejection, ProjectPermissionRejection, ToolArgumentsRejection,
|
||||
ToolPageRejection, ToolFailure,
|
||||
ProjectManifestRejection, ProjectPermissionRejection, ToolArgumentsRejection, ToolFailure,
|
||||
ToolPageRejection,
|
||||
};
|
||||
|
||||
/// `agc_list_registered_assets` 的每一种失败各占一个变体;文案写在这里,捕获处只调
|
||||
/// `to_user_msg`。
|
||||
#[derive(Debug)]
|
||||
#[derive(serde::Serialize, Debug)]
|
||||
pub(crate) enum ListRegisteredAssetsError {
|
||||
Arguments(ToolArgumentsRejection),
|
||||
PermissionDenied(ProjectPermissionRejection),
|
||||
|
||||
@@ -2,17 +2,14 @@
|
||||
//!
|
||||
//! 这里只放错误本身;参数解析与工具执行留在 `direct_tool_bridge`。
|
||||
|
||||
use crate::agent::tool::error::{
|
||||
ProjectPermissionRejection, ToolArgumentsRejection, ToolFailure,
|
||||
};
|
||||
use crate::agent::tool::error::{ProjectPermissionRejection, ToolArgumentsRejection, ToolFailure};
|
||||
|
||||
/// 美术重生成只接受客户端当前回合签发的授权;这里列出授权链自己的失败。
|
||||
#[derive(Debug)]
|
||||
#[derive(serde::Serialize, Debug)]
|
||||
pub(crate) enum ArtRegenerationAuthorizationRejection {
|
||||
StateUnavailable { cause: String },
|
||||
MissingTurnAuthorization,
|
||||
BoundToAnotherBrief,
|
||||
CompletedResultNotSuccessful,
|
||||
CompletedResultTurnExpired,
|
||||
CompletedResultMismatch,
|
||||
}
|
||||
@@ -30,9 +27,6 @@ impl ToolFailure for ArtRegenerationAuthorizationRejection {
|
||||
Self::BoundToAnotherBrief => {
|
||||
"美术重生成失败:当前客户端回合已绑定另一项稳定美术重生成请求。".to_string()
|
||||
}
|
||||
Self::CompletedResultNotSuccessful => {
|
||||
"美术重生成失败:拒绝缓存未成功的美术重生成结果。".to_string()
|
||||
}
|
||||
Self::CompletedResultTurnExpired => {
|
||||
"美术重生成失败:美术重生成完成时客户端回合授权已失效。".to_string()
|
||||
}
|
||||
@@ -45,7 +39,7 @@ impl ToolFailure for ArtRegenerationAuthorizationRejection {
|
||||
|
||||
/// `taonier_prepare_game_art` 的每一种失败各占一个变体;文案写在这里,捕获处只调
|
||||
/// `to_user_msg`。
|
||||
#[derive(Debug)]
|
||||
#[derive(serde::Serialize, Debug)]
|
||||
pub(crate) enum PrepareGameArtError {
|
||||
Arguments(ToolArgumentsRejection),
|
||||
PermissionDenied(ProjectPermissionRejection),
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::agent::tool::error::ToolFailure;
|
||||
|
||||
/// `agc_read_project_context` 的每一种失败各占一个变体;文案写在这里,捕获处只调
|
||||
/// `to_user_msg`。
|
||||
#[derive(Debug)]
|
||||
#[derive(serde::Serialize, Debug)]
|
||||
pub(crate) enum ReadProjectContextError {
|
||||
/// 上下文读取失败:入参不合法、路径不安全、快照复核中断或响应超预算。
|
||||
/// 具体分类等 `direct_project_context` 给出 typed 出口后再拆变体。
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ use crate::agent::tool::error::{DirectExecutionGateRejection, ToolFailure};
|
||||
|
||||
/// `agc_register_delivery_contract` 的每一种失败各占一个变体;文案写在这里,捕获处只调
|
||||
/// `to_user_msg`。
|
||||
#[derive(Debug)]
|
||||
#[derive(serde::Serialize, Debug)]
|
||||
pub(crate) enum RegisterDeliveryContractError {
|
||||
/// 宿主交付会话取不到。
|
||||
Gate(DirectExecutionGateRejection),
|
||||
|
||||
@@ -9,7 +9,7 @@ use crate::agent::tool::error::{
|
||||
|
||||
/// `agc_remove_background` 的每一种失败各占一个变体;文案写在这里,捕获处只调
|
||||
/// `to_user_msg`。
|
||||
#[derive(Debug)]
|
||||
#[derive(serde::Serialize, Debug)]
|
||||
pub(crate) enum RemoveBackgroundError {
|
||||
Arguments(ToolArgumentsRejection),
|
||||
PermissionDenied(ProjectPermissionRejection),
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user