保留 CLI 协议解析错误正文
补齐 Codex CLI 与 Claude sidecar 失败终态和无效 JSON 细节 添加脱敏协议错误回归
This commit is contained in:
@@ -290,10 +290,15 @@ async fn run_sidecar_turn(
|
||||
if line.trim().is_empty() {
|
||||
continue;
|
||||
}
|
||||
let value: serde_json::Value = serde_json::from_str(&line).map_err(|_| {
|
||||
SidecarTurnFailure::message(
|
||||
"Claude Agent SDK sidecar 输出不是有效 JSON".to_string(),
|
||||
)
|
||||
let value: serde_json::Value = serde_json::from_str(&line).map_err(|error| {
|
||||
let detail = crate::agent::redact_agent_runtime_error(
|
||||
Path::new("__agc_no_project_root__"),
|
||||
&line,
|
||||
480,
|
||||
);
|
||||
SidecarTurnFailure::message(format!(
|
||||
"Claude Agent SDK sidecar 输出不是有效 JSON:{error};原文={detail}"
|
||||
))
|
||||
})?;
|
||||
match value.get("type").and_then(serde_json::Value::as_str) {
|
||||
Some("event") => {
|
||||
|
||||
@@ -543,9 +543,14 @@ fn parse_game_creator_codex_cli_response(
|
||||
if line.trim().is_empty() {
|
||||
continue;
|
||||
}
|
||||
let event = serde_json::from_str::<serde_json::Value>(line).map_err(|_| {
|
||||
let event = serde_json::from_str::<serde_json::Value>(line).map_err(|error| {
|
||||
let detail = crate::agent::redact_agent_runtime_error(
|
||||
Path::new("__agc_no_project_root__"),
|
||||
line,
|
||||
480,
|
||||
);
|
||||
platform_llm::LlmError::Deserialize(format!(
|
||||
"Codex CLI Agent JSONL 第 {} 行无效",
|
||||
"Codex CLI Agent JSONL 第 {} 行无效:{error};原文={detail}",
|
||||
index.saturating_add(1)
|
||||
))
|
||||
})?;
|
||||
@@ -587,9 +592,21 @@ fn parse_game_creator_codex_cli_response(
|
||||
}
|
||||
}
|
||||
Some("turn.failed") => {
|
||||
return Err(platform_llm::LlmError::Transport(
|
||||
"Codex CLI Agent 返回失败终态".to_string(),
|
||||
));
|
||||
let detail = event
|
||||
.get("error")
|
||||
.or_else(|| event.get("message"))
|
||||
.map(serde_json::Value::to_string)
|
||||
.unwrap_or_default();
|
||||
let detail = crate::agent::redact_agent_runtime_error(
|
||||
Path::new("__agc_no_project_root__"),
|
||||
detail.trim_matches('"'),
|
||||
480,
|
||||
);
|
||||
return Err(platform_llm::LlmError::Transport(if detail.is_empty() {
|
||||
"Codex CLI Agent 返回失败终态".to_string()
|
||||
} else {
|
||||
format!("Codex CLI Agent 返回失败终态:{detail}")
|
||||
}));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@@ -1219,6 +1236,29 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn codex_cli_mode_preserves_failed_terminal_and_invalid_json_details() {
|
||||
let request = LlmRunRequest::single_turn("系统", "任务");
|
||||
let failed = parse_game_creator_codex_cli_response(
|
||||
b"{\"type\":\"turn.failed\",\"error\":{\"message\":\"HTTP 502 upstream overloaded\",\"api_key\":\"fixture-secret\"}}\n",
|
||||
&request,
|
||||
)
|
||||
.expect_err("failed terminal must be returned as an error");
|
||||
let failed_text = failed.to_string();
|
||||
assert!(failed_text.contains("HTTP 502 upstream overloaded"));
|
||||
assert!(!failed_text.contains("fixture-secret"));
|
||||
|
||||
let invalid = parse_game_creator_codex_cli_response(
|
||||
b"{not-json Authorization: Bearer fixture-secret}\n",
|
||||
&request,
|
||||
)
|
||||
.expect_err("invalid JSONL must preserve a bounded safe line detail");
|
||||
let invalid_text = invalid.to_string();
|
||||
assert!(invalid_text.contains("JSONL 第 1 行无效"));
|
||||
assert!(invalid_text.contains("原文="));
|
||||
assert!(!invalid_text.contains("fixture-secret"));
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[tokio::test]
|
||||
async fn codex_cli_mode_builds_ephemeral_json_exec_with_prompt_on_stdin() {
|
||||
|
||||
@@ -107,6 +107,7 @@ DirectProject 已经解决过同一类问题([`【ADR】DirectProject命令接
|
||||
- 脱敏只替换敏感值、完整 URL、绝对路径和私钥内容;字段名(例如 `Authorization`、`token`、`api_key`)保留,便于判断错误类别。原始凭据值不进入用户文本、应用日志或项目诊断。
|
||||
- 上游 app-server 映射保留 `message` / `additionalDetails` 的脱敏摘要;因此 401/403/408/429/413/5xx 等状态不会因为分类为 transport 或 native 而丢失正文。
|
||||
- Claude Code(cc)侧车的非零退出、RPC `error`、stdout JSON/UTF-8 解析失败、stderr 和静默超时也走同一映射;侧车 stderr 只在有界收口窗口内读取并进入同一脱敏 detail,不再只写裸 `eprintln!`。
|
||||
- Codex CLI / Claude sidecar 的 JSONL/JSON 解析失败和失败终态保留解析错误、失败事件正文与有界安全片段,不再把无效 JSON、`turn.failed` 或缺失终态统一压成单一类别。
|
||||
- HTTP 409 只有明确包含泥点不足事实时才映射为 `paidCreditsInsufficient`;Claude Code 的普通 409 冲突保留为 `upstreamFailed`。
|
||||
- `hostDropped` 携带可选的脱敏 `detail`:panic hook 能取得的负载和位置随原回合占用进入失败事件;普通 Drop 仅记录“退出时未写终态、未观察到 panic”,不推断为网络中断。旧版本无 `detail` 的事件继续可读,界面明确标注旧事件未记录原因。
|
||||
- DirectProject 控制器的取消待发、终止、附件上传、项目历史读取和发送前置异常也复用同一份前端精确脱敏出口;这些非回合错误不得直接把 `Error.message` 原文写入状态栏、composer 或历史错误行。
|
||||
|
||||
@@ -17,6 +17,7 @@ Parent Milestone: `【里程碑】AGC错误具体文本展示-2026-10-04.md`
|
||||
8. 项目资源编辑/上传/预览/恢复队列/生成结果回读的 UI 错误统一使用 `normalizeDiagnosticText`,不再直出 `Error.message`。
|
||||
9. 应用壳、运行配置、认证状态、插件启动、运行预览和策划会话的非结构化错误统一使用 `visibleClientErrorMessage`。
|
||||
10. 资产导入、邀请码、策划工作区、发布封面/截图和素材命令的 UI 错误沿用同一共享出口。
|
||||
11. Codex CLI / Claude sidecar 的解析失败与失败终态保留有界脱敏正文,避免协议错误只剩类别码。
|
||||
|
||||
## 实现顺序
|
||||
|
||||
|
||||
Reference in New Issue
Block a user