修复 AGC Claude 执行认证与错误诊断
官方 Claude 模型复用当前平台会话令牌 保留 Claude 失败终态摘要并增加回归测试 补充 AGC 排障记忆
This commit is contained in:
@@ -385,12 +385,44 @@ fn configure_claude_code_environment(
|
||||
{
|
||||
command.env("ANTHROPIC_AUTH_TOKEN", llm.api_key.trim());
|
||||
}
|
||||
// Official AGC model entries use the account session rather than a
|
||||
// user-supplied API key. The Claude SDK only reads Anthropic auth
|
||||
// from its child-process environment, so bridge the already-held
|
||||
// platform token without persisting or exposing it in argv/logs.
|
||||
if llm.api_key.trim().is_empty()
|
||||
&& !llm.custom_enabled
|
||||
&& std::env::var_os("ANTHROPIC_API_KEY").is_none()
|
||||
&& std::env::var_os("ANTHROPIC_AUTH_TOKEN").is_none()
|
||||
{
|
||||
if let Some(session) = crate::platform_session::current_platform_session() {
|
||||
command.env("ANTHROPIC_AUTH_TOKEN", session.access_token);
|
||||
}
|
||||
}
|
||||
if !llm.model.trim().is_empty() {
|
||||
command.env("ANTHROPIC_MODEL", llm.model.trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn claude_result_error_detail(value: &serde_json::Value) -> Option<String> {
|
||||
let candidates = [
|
||||
value.get("result").and_then(serde_json::Value::as_str),
|
||||
value.get("error").and_then(serde_json::Value::as_str),
|
||||
value.get("message").and_then(serde_json::Value::as_str),
|
||||
value
|
||||
.get("errors")
|
||||
.and_then(serde_json::Value::as_array)
|
||||
.and_then(|errors| errors.first())
|
||||
.and_then(serde_json::Value::as_str),
|
||||
];
|
||||
candidates
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.map(str::trim)
|
||||
.find(|detail| !detail.is_empty())
|
||||
.map(|detail| detail.chars().take(512).collect())
|
||||
}
|
||||
|
||||
fn configure_claude_code_process(command: &mut tokio::process::Command) {
|
||||
super::codex_cli::configure_game_creator_codex_cli_process(command);
|
||||
}
|
||||
@@ -495,9 +527,12 @@ fn parse_claude_code_result(
|
||||
platform_llm::LlmError::Deserialize("Claude Code JSON 输出无效".to_string())
|
||||
})?;
|
||||
if value.get("is_error").and_then(serde_json::Value::as_bool) == Some(true) {
|
||||
return Err(platform_llm::LlmError::Transport(
|
||||
"Claude Code 返回失败终态".to_string(),
|
||||
));
|
||||
let detail = claude_result_error_detail(&value)
|
||||
.map(|detail| format!(":{detail}"))
|
||||
.unwrap_or_default();
|
||||
return Err(platform_llm::LlmError::Transport(format!(
|
||||
"Claude Code 返回失败终态{detail}"
|
||||
)));
|
||||
}
|
||||
let response_id = value
|
||||
.get("session_id")
|
||||
@@ -829,7 +864,10 @@ fn parse_direct_stream_result(
|
||||
}
|
||||
} else if event_type == Some("result") {
|
||||
if event.get("is_error").and_then(serde_json::Value::as_bool) == Some(true) {
|
||||
return Err("Claude Code 返回失败终态".to_string());
|
||||
let detail = claude_result_error_detail(&event)
|
||||
.map(|detail| format!(":{detail}"))
|
||||
.unwrap_or_default();
|
||||
return Err(format!("Claude Code 返回失败终态{detail}"));
|
||||
}
|
||||
result = event
|
||||
.get("result")
|
||||
@@ -884,6 +922,16 @@ mod tests {
|
||||
assert_eq!(response.tool_calls[0].name, "runtime_tool_file_read");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn preserves_claude_terminal_error_detail() {
|
||||
let error = parse_claude_code_result(
|
||||
br#"{"is_error":true,"result":"Authentication failed"}"#,
|
||||
&request(false),
|
||||
)
|
||||
.expect_err("failed terminal must be rejected");
|
||||
assert!(error.to_string().contains("Authentication failed"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_direct_stream_result_and_final_text() {
|
||||
let mut observed = Vec::new();
|
||||
@@ -903,4 +951,16 @@ mod tests {
|
||||
[DirectCodexTurnObservation::AgentMessageSegment { .. }]
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn preserves_direct_stream_terminal_error_detail() {
|
||||
let error = parse_direct_stream_result(
|
||||
br#"{"type":"result","is_error":true,"result":"Authentication failed"}
|
||||
"#,
|
||||
"turn-1",
|
||||
None,
|
||||
)
|
||||
.expect_err("failed terminal must be rejected");
|
||||
assert!(error.contains("Authentication failed"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6158,3 +6158,9 @@ Cocos Creator 根目录由 `package.json.creator.version` 与普通 `assets/`
|
||||
- **同时**:主站顶栏(`platform-desktop-topbar`)在窄屏必须保持单行——加 `flex-wrap` 后新增一个动作入口就会让品牌折到第二行;窄屏降级靠 `< 640px` 图标化下载入口、`< 480px` 只留品牌 IP 标识。
|
||||
- **验证**:真机尺寸下用 `document.elementFromPoint(按钮中心)` 断言命中的是按钮自身而不是底部菜单(`overlay.parentElement === document.body`);顶栏断点矩阵(320–768)断言单行且无横向溢出。
|
||||
- **关联**:`src/components/common/SquareImageCropModal.tsx`、`src/components/platform-entry/PlatformProfileModalShell.tsx`、`src/components/platform-entry/PlatformEntryActiveFlowShell.tsx`、`src/index.css`。
|
||||
|
||||
## 2026-10-01 AGC 官方 Claude 模型缺少账号令牌会被误报为通道断开
|
||||
|
||||
- **现象**:模型目录把回合路由到 `cc`,本地 `game-creator.config.json` 的 `llm.apiKey` 为空时,Claude Agent SDK 返回失败终态;界面只显示“执行通道未能建立或已断开”。
|
||||
- **根因**:Claude sidecar 只从 `ANTHROPIC_API_KEY` / `ANTHROPIC_AUTH_TOKEN` 或本地 `llm.apiKey` 读取认证,没有复用已登录的 AGC 平台会话;同时失败终态解析丢弃了上游错误摘要。
|
||||
- **处理**:官方模型且未启用自定义目录时,将当前平台会话令牌仅注入 sidecar 子进程环境;保留最多 512 字符的 Claude 终态错误摘要,继续由统一诊断层脱敏,避免凭据落盘。
|
||||
|
||||
Reference in New Issue
Block a user