From 9141de3c9d41e73c831ee2f9b2127c6e1ddfb12d Mon Sep 17 00:00:00 2001 From: chen <54944284+chen893@users.noreply.github.com> Date: Mon, 13 Apr 2026 22:41:16 +0800 Subject: [PATCH 1/3] feat(usage): add sub-agent transcript parsing to usage statistics The session usage parser previously only scanned main session JSONL files (~/.claude/projects/*/*.jsonl), missing all API calls made by sub-agents (tool agents). Sub-agent transcripts are stored under /subagents/agent-*.jsonl with the same format as main session files. Changes: - Add collect_subagent_files() to scan //subagents/ including nested workflow subdirectories - Parameterize sync_single_file() with data_source and request_id_prefix to reuse parsing logic for both main and sub-agent records - Tag sub-agent records with data_source="session_subagent" and request_id prefix "subagent:" to avoid ID collisions - Update provider_name_coalesce() to display "Claude (Sub-Agent)" for sub-agent entries in usage stats queries - Add session_subagent icon (Bot) and i18n translations in en/zh/ja Co-Authored-By: Claude Opus 4.6 --- src-tauri/src/services/session_usage.rs | 139 ++++++++++++++++++++++-- src-tauri/src/services/usage_stats.rs | 11 +- src/components/usage/DataSourceBar.tsx | 3 +- src/i18n/locales/en.json | 1 + src/i18n/locales/ja.json | 1 + src/i18n/locales/zh.json | 1 + 6 files changed, 139 insertions(+), 17 deletions(-) diff --git a/src-tauri/src/services/session_usage.rs b/src-tauri/src/services/session_usage.rs index d821f35d21..7c30c229b0 100644 --- a/src-tauri/src/services/session_usage.rs +++ b/src-tauri/src/services/session_usage.rs @@ -1,11 +1,12 @@ //! Claude Code 会话日志使用追踪 //! //! 从 ~/.claude/projects/ 下的 JSONL 会话文件中提取 token 使用数据, -//! 实现无代理模式下的使用统计。 +//! 实现无代理模式下的使用统计。同时解析子 Agent (sub-agent) 的对话记录。 //! //! ## 数据流 //! ```text -//! ~/.claude/projects/*/*.jsonl → 增量解析 → 去重 → 费用计算 → proxy_request_logs 表 +//! ~/.claude/projects/*/*.jsonl → 增量解析 → 去重 → 费用计算 → proxy_request_logs 表 +//! ~/.claude/projects/*//subagents/ → 增量解析 → 去重 → 费用计算 → proxy_request_logs 表 //! ``` use crate::config::get_claude_config_dir; @@ -54,7 +55,7 @@ struct ParsedAssistantUsage { session_id: Option, } -/// 同步 Claude Code 会话日志到使用统计数据库 +/// 同步 Claude Code 会话日志到使用统计数据库(包括子 Agent 记录) pub fn sync_claude_session_logs(db: &Database) -> Result { let projects_dir = get_claude_config_dir().join("projects"); if !projects_dir.exists() { @@ -73,13 +74,13 @@ pub fn sync_claude_session_logs(db: &Database) -> Result { result.imported += imported; result.skipped += skipped; @@ -92,12 +93,32 @@ pub fn sync_claude_session_logs(db: &Database) -> Result { + result.imported += imported; + result.skipped += skipped; + } + Err(e) => { + let msg = format!("{}: {e}", file_path.display()); + log::warn!("[SESSION-SYNC] 子Agent文件解析失败: {msg}"); + result.errors.push(msg); + } + } + } + if result.imported > 0 { log::info!( - "[SESSION-SYNC] 同步完成: 导入 {} 条, 跳过 {} 条, 扫描 {} 个文件", + "[SESSION-SYNC] 同步完成: 导入 {} 条, 跳过 {} 条, 扫描 {} 个文件 (含 {} 个子Agent文件)", result.imported, result.skipped, - result.files_scanned + result.files_scanned, + subagent_files.len() ); } @@ -132,8 +153,99 @@ fn collect_jsonl_files(projects_dir: &Path) -> Vec { files } +/// 收集所有子 Agent 的会话文件 +/// +/// 扫描路径: ~/.claude/projects///subagents/agent-*.jsonl +fn collect_subagent_files(projects_dir: &Path) -> Vec { + let mut files = Vec::new(); + + let entries = match fs::read_dir(projects_dir) { + Ok(e) => e, + Err(_) => return files, + }; + + for entry in entries.flatten() { + let project_dir = entry.path(); + if !project_dir.is_dir() { + continue; + } + + // 遍历项目目录下的每个子目录 (可能是 session 目录) + let session_entries = match fs::read_dir(&project_dir) { + Ok(e) => e, + Err(_) => continue, + }; + + for session_entry in session_entries.flatten() { + let session_path = session_entry.path(); + if !session_path.is_dir() { + continue; + } + + let subagents_dir = session_path.join("subagents"); + if !subagents_dir.is_dir() { + continue; + } + + // 扫描 subagents/ 目录下的 agent-*.jsonl 文件 + if let Ok(agent_entries) = fs::read_dir(&subagents_dir) { + for agent_entry in agent_entries.flatten() { + let agent_path = agent_entry.path(); + let file_name = agent_path + .file_name() + .and_then(|n| n.to_str()) + .unwrap_or(""); + if file_name.starts_with("agent-") + && agent_path.extension().and_then(|e| e.to_str()) == Some("jsonl") + { + files.push(agent_path); + } + } + } + + // 同时扫描嵌套子目录 (如 workflows//agent-*.jsonl) + let subdirs = match fs::read_dir(&subagents_dir) { + Ok(e) => e, + Err(_) => continue, + }; + + for subdir_entry in subdirs.flatten() { + let subdir_path = subdir_entry.path(); + if !subdir_path.is_dir() { + continue; + } + + if let Ok(nested_entries) = fs::read_dir(&subdir_path) { + for nested_entry in nested_entries.flatten() { + let nested_path = nested_entry.path(); + let file_name = nested_path + .file_name() + .and_then(|n| n.to_str()) + .unwrap_or(""); + if file_name.starts_with("agent-") + && nested_path.extension().and_then(|e| e.to_str()) == Some("jsonl") + { + files.push(nested_path); + } + } + } + } + } + } + + files +} + /// 同步单个 JSONL 文件,返回 (imported, skipped) -fn sync_single_file(db: &Database, file_path: &Path) -> Result<(u32, u32), AppError> { +/// +/// `data_source` 用于区分数据来源 ("session_log" 或 "session_subagent") +/// `request_id_prefix` 用于生成唯一的 request_id 前缀 +fn sync_single_file( + db: &Database, + file_path: &Path, + data_source: &str, + request_id_prefix: &str, +) -> Result<(u32, u32), AppError> { let file_path_str = file_path.to_string_lossy().to_string(); // 获取文件元数据 @@ -278,14 +390,14 @@ fn sync_single_file(db: &Database, file_path: &Path) -> Result<(u32, u32), AppEr continue; } - let request_id = format!("session:{}", msg.message_id); + let request_id = format!("{}{}", request_id_prefix, msg.message_id); // 跳过 output_tokens 为 0 的无意义条目 if msg.output_tokens == 0 { continue; } - match insert_session_log_entry(db, &request_id, msg) { + match insert_session_log_entry(db, &request_id, msg, data_source) { Ok(true) => imported += 1, Ok(false) => skipped += 1, Err(e) => { @@ -335,10 +447,13 @@ fn update_sync_state( } /// 插入单条会话日志到 proxy_request_logs,返回是否成功插入 (true=新插入, false=已存在) +/// +/// `data_source` 区分主会话 ("session_log") 和子 Agent ("session_subagent") fn insert_session_log_entry( db: &Database, request_id: &str, msg: &ParsedAssistantUsage, + data_source: &str, ) -> Result { let conn = lock_conn!(db.conn); @@ -432,11 +547,11 @@ fn insert_session_log_entry( 200i64, // status_code: 有 stop_reason 说明请求成功 Option::::None, // error_message msg.session_id, - Some("session_log"), // provider_type + Some(data_source), // provider_type: 使用 data_source 值 1i64, // is_streaming: Claude Code 通常使用流式 "1.0", // cost_multiplier created_at, - "session_log", // data_source + data_source, // data_source: 区分主会话和子Agent ], ) .map_err(|e| AppError::Database(format!("插入会话日志失败: {e}")))?; diff --git a/src-tauri/src/services/usage_stats.rs b/src-tauri/src/services/usage_stats.rs index b43ea714ad..7422ff0d82 100644 --- a/src-tauri/src/services/usage_stats.rs +++ b/src-tauri/src/services/usage_stats.rs @@ -120,12 +120,15 @@ pub struct RequestLogDetail { /// SQL fragment: resolve provider_name with fallback for session-based entries. /// Session logs use placeholder provider_ids (_session, _codex_session, _gemini_session) /// that don't exist in the providers table — this COALESCE gives them readable names. +/// For _session entries, also checks data_source to distinguish sub-agent records. fn provider_name_coalesce(log_alias: &str, provider_alias: &str) -> String { format!( - "COALESCE({provider_alias}.name, CASE {log_alias}.provider_id \ - WHEN '_session' THEN 'Claude (Session)' \ - WHEN '_codex_session' THEN 'Codex (Session)' \ - WHEN '_gemini_session' THEN 'Gemini (Session)' \ + "COALESCE({provider_alias}.name, CASE \ + WHEN {log_alias}.provider_id = '_session' AND {log_alias}.data_source = 'session_subagent' \ + THEN 'Claude (Sub-Agent)' \ + WHEN {log_alias}.provider_id = '_session' THEN 'Claude (Session)' \ + WHEN {log_alias}.provider_id = '_codex_session' THEN 'Codex (Session)' \ + WHEN {log_alias}.provider_id = '_gemini_session' THEN 'Gemini (Session)' \ ELSE {log_alias}.provider_id END)" ) } diff --git a/src/components/usage/DataSourceBar.tsx b/src/components/usage/DataSourceBar.tsx index 7dabc51a8f..6e7b174d4e 100644 --- a/src/components/usage/DataSourceBar.tsx +++ b/src/components/usage/DataSourceBar.tsx @@ -2,7 +2,7 @@ import { useTranslation } from "react-i18next"; import { useQuery, useQueryClient } from "@tanstack/react-query"; import { usageApi } from "@/lib/api/usage"; import { usageKeys } from "@/lib/query/usage"; -import { Database, FileText, RefreshCw, Loader2 } from "lucide-react"; +import { Bot, Database, FileText, RefreshCw, Loader2 } from "lucide-react"; import { Button } from "@/components/ui/button"; import { useState } from "react"; import { toast } from "sonner"; @@ -14,6 +14,7 @@ interface DataSourceBarProps { const DATA_SOURCE_ICONS: Record = { proxy: , session_log: , + session_subagent: , codex_db: , codex_session: , gemini_session: , diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index b28e876c1e..822a0b0719 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -1091,6 +1091,7 @@ "dataSource": { "proxy": "Proxy", "session_log": "Session Log", + "session_subagent": "Sub-Agent", "codex_db": "Codex DB", "codex_session": "Codex Session", "gemini_session": "Gemini Session" diff --git a/src/i18n/locales/ja.json b/src/i18n/locales/ja.json index a8ba0cf517..f537c32f57 100644 --- a/src/i18n/locales/ja.json +++ b/src/i18n/locales/ja.json @@ -1091,6 +1091,7 @@ "dataSource": { "proxy": "プロキシ", "session_log": "セッションログ", + "session_subagent": "サブエージェント", "codex_db": "Codex DB", "codex_session": "Codex セッション", "gemini_session": "Gemini セッション" diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json index 421d7f859b..5875ee2240 100644 --- a/src/i18n/locales/zh.json +++ b/src/i18n/locales/zh.json @@ -1092,6 +1092,7 @@ "dataSource": { "proxy": "代理", "session_log": "会话日志", + "session_subagent": "子Agent", "codex_db": "Codex 数据库", "codex_session": "Codex 会话日志", "gemini_session": "Gemini 会话日志" From 7e968ebac3626744dcb10b262d31e5fce3bd0399 Mon Sep 17 00:00:00 2001 From: chen <54944284+chen893@users.noreply.github.com> Date: Mon, 13 Apr 2026 23:30:42 +0800 Subject: [PATCH 2/3] fix(usage): stabilize subagent session usage import --- src-tauri/src/services/session_usage.rs | 112 +++++++++++++++--------- src-tauri/src/services/usage_stats.rs | 14 +-- 2 files changed, 77 insertions(+), 49 deletions(-) diff --git a/src-tauri/src/services/session_usage.rs b/src-tauri/src/services/session_usage.rs index 7c30c229b0..99eacf3581 100644 --- a/src-tauri/src/services/session_usage.rs +++ b/src-tauri/src/services/session_usage.rs @@ -155,7 +155,7 @@ fn collect_jsonl_files(projects_dir: &Path) -> Vec { /// 收集所有子 Agent 的会话文件 /// -/// 扫描路径: ~/.claude/projects///subagents/agent-*.jsonl +/// 扫描路径: ~/.claude/projects///subagents/**/agent-*.jsonl fn collect_subagent_files(projects_dir: &Path) -> Vec { let mut files = Vec::new(); @@ -187,53 +187,41 @@ fn collect_subagent_files(projects_dir: &Path) -> Vec { continue; } - // 扫描 subagents/ 目录下的 agent-*.jsonl 文件 - if let Ok(agent_entries) = fs::read_dir(&subagents_dir) { - for agent_entry in agent_entries.flatten() { - let agent_path = agent_entry.path(); - let file_name = agent_path - .file_name() - .and_then(|n| n.to_str()) - .unwrap_or(""); - if file_name.starts_with("agent-") - && agent_path.extension().and_then(|e| e.to_str()) == Some("jsonl") - { - files.push(agent_path); - } - } - } + collect_subagent_files_recursive(&subagents_dir, &mut files); + } + } - // 同时扫描嵌套子目录 (如 workflows//agent-*.jsonl) - let subdirs = match fs::read_dir(&subagents_dir) { - Ok(e) => e, - Err(_) => continue, - }; + files +} - for subdir_entry in subdirs.flatten() { - let subdir_path = subdir_entry.path(); - if !subdir_path.is_dir() { - continue; - } +/// 递归扫描 subagents 目录下任意层级的 agent-*.jsonl。 +/// +/// Claude Code/工作流可能把子 Agent 日志放在 subagents/workflows// 等更深层目录, +/// 因此这里不限制递归深度,只在文件名层面过滤实际需要导入的 transcript。 +fn collect_subagent_files_recursive(dir: &Path, files: &mut Vec) { + let entries = match fs::read_dir(dir) { + Ok(e) => e, + Err(_) => return, + }; - if let Ok(nested_entries) = fs::read_dir(&subdir_path) { - for nested_entry in nested_entries.flatten() { - let nested_path = nested_entry.path(); - let file_name = nested_path - .file_name() - .and_then(|n| n.to_str()) - .unwrap_or(""); - if file_name.starts_with("agent-") - && nested_path.extension().and_then(|e| e.to_str()) == Some("jsonl") - { - files.push(nested_path); - } - } - } - } + for entry in entries.flatten() { + let file_type = match entry.file_type() { + Ok(ft) => ft, + Err(_) => continue, + }; + let path = entry.path(); + + if file_type.is_dir() { + collect_subagent_files_recursive(&path, files); + } else if file_type.is_file() && is_subagent_jsonl_file(&path) { + files.push(path); } } +} - files +fn is_subagent_jsonl_file(path: &Path) -> bool { + let file_name = path.file_name().and_then(|n| n.to_str()).unwrap_or(""); + file_name.starts_with("agent-") && path.extension().and_then(|e| e.to_str()) == Some("jsonl") } /// 同步单个 JSONL 文件,返回 (imported, skipped) @@ -668,6 +656,7 @@ pub fn get_data_source_breakdown(db: &Database) -> Result #[cfg(test)] mod tests { use super::*; + use std::fs; #[test] fn test_parse_usage_from_jsonl_line() { @@ -746,4 +735,43 @@ mod tests { messages.insert("msg_1".to_string(), final_entry); assert_eq!(messages.get("msg_1").unwrap().output_tokens, 1349); } + + #[test] + fn test_collect_subagent_files_recurses_nested_workflow_dirs() { + let temp = tempfile::tempdir().unwrap(); + let subagents_dir = temp + .path() + .join("project-a") + .join("session-1") + .join("subagents"); + let nested_dir = subagents_dir.join("workflows").join("run-1"); + + fs::create_dir_all(&nested_dir).unwrap(); + fs::write(subagents_dir.join("agent-root.jsonl"), "").unwrap(); + fs::write(nested_dir.join("agent-nested.jsonl"), "").unwrap(); + fs::write(nested_dir.join("not-agent.jsonl"), "").unwrap(); + fs::write(subagents_dir.join("agent-not-json.txt"), "").unwrap(); + + let mut files: Vec = collect_subagent_files(temp.path()) + .into_iter() + .map(|path| path.strip_prefix(temp.path()).unwrap().to_path_buf()) + .collect(); + files.sort(); + + let mut expected = vec![ + PathBuf::from("project-a") + .join("session-1") + .join("subagents") + .join("agent-root.jsonl"), + PathBuf::from("project-a") + .join("session-1") + .join("subagents") + .join("workflows") + .join("run-1") + .join("agent-nested.jsonl"), + ]; + expected.sort(); + + assert_eq!(files, expected); + } } diff --git a/src-tauri/src/services/usage_stats.rs b/src-tauri/src/services/usage_stats.rs index 7422ff0d82..2a347c0bda 100644 --- a/src-tauri/src/services/usage_stats.rs +++ b/src-tauri/src/services/usage_stats.rs @@ -120,15 +120,15 @@ pub struct RequestLogDetail { /// SQL fragment: resolve provider_name with fallback for session-based entries. /// Session logs use placeholder provider_ids (_session, _codex_session, _gemini_session) /// that don't exist in the providers table — this COALESCE gives them readable names. -/// For _session entries, also checks data_source to distinguish sub-agent records. +/// +/// This helper is shared by raw log and daily rollup queries. Keep it independent from +/// proxy_request_logs-only columns such as data_source; usage_daily_rollups does not store them. fn provider_name_coalesce(log_alias: &str, provider_alias: &str) -> String { format!( - "COALESCE({provider_alias}.name, CASE \ - WHEN {log_alias}.provider_id = '_session' AND {log_alias}.data_source = 'session_subagent' \ - THEN 'Claude (Sub-Agent)' \ - WHEN {log_alias}.provider_id = '_session' THEN 'Claude (Session)' \ - WHEN {log_alias}.provider_id = '_codex_session' THEN 'Codex (Session)' \ - WHEN {log_alias}.provider_id = '_gemini_session' THEN 'Gemini (Session)' \ + "COALESCE({provider_alias}.name, CASE {log_alias}.provider_id \ + WHEN '_session' THEN 'Claude (Session)' \ + WHEN '_codex_session' THEN 'Codex (Session)' \ + WHEN '_gemini_session' THEN 'Gemini (Session)' \ ELSE {log_alias}.provider_id END)" ) } From 01ffc6d0cd3e4954b1c7957ef7f3f081f79796dc Mon Sep 17 00:00:00 2001 From: chen <54944284+chen893@users.noreply.github.com> Date: Mon, 13 Apr 2026 23:37:48 +0800 Subject: [PATCH 3/3] fix(usage): merge subagent display into session logs --- src-tauri/src/services/session_usage.rs | 6 +++++- src-tauri/src/services/usage_stats.rs | 16 ++++++++++++++-- src/components/usage/DataSourceBar.tsx | 3 +-- src/i18n/locales/en.json | 1 - src/i18n/locales/ja.json | 1 - src/i18n/locales/zh.json | 1 - 6 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src-tauri/src/services/session_usage.rs b/src-tauri/src/services/session_usage.rs index 99eacf3581..550ce6b946 100644 --- a/src-tauri/src/services/session_usage.rs +++ b/src-tauri/src/services/session_usage.rs @@ -630,7 +630,11 @@ pub fn get_data_source_breakdown(db: &Database) -> Result let conn = lock_conn!(db.conn); let mut stmt = conn.prepare( - "SELECT COALESCE(data_source, 'proxy') as ds, COUNT(*) as cnt, + "SELECT CASE COALESCE(data_source, 'proxy') + WHEN 'session_subagent' THEN 'session_log' + ELSE COALESCE(data_source, 'proxy') + END as ds, + COUNT(*) as cnt, COALESCE(SUM(CAST(total_cost_usd AS REAL)), 0) as cost FROM proxy_request_logs GROUP BY ds diff --git a/src-tauri/src/services/usage_stats.rs b/src-tauri/src/services/usage_stats.rs index 2a347c0bda..5e0f36f2a1 100644 --- a/src-tauri/src/services/usage_stats.rs +++ b/src-tauri/src/services/usage_stats.rs @@ -133,6 +133,16 @@ fn provider_name_coalesce(log_alias: &str, provider_alias: &str) -> String { ) } +/// UI 展示层不单独拆分 Claude 主会话和子 Agent,会统一显示为 session_log。 +/// 数据库仍保留原始 data_source,便于后续排查或重新拆分。 +fn display_data_source_expr(log_alias: &str) -> String { + format!( + "CASE COALESCE({log_alias}.data_source, 'proxy') \ + WHEN 'session_subagent' THEN 'session_log' \ + ELSE COALESCE({log_alias}.data_source, 'proxy') END" + ) +} + impl Database { /// 获取使用量汇总 pub fn get_usage_summary( @@ -674,13 +684,14 @@ impl Database { params.push(Box::new(offset as i64)); let logs_pname = provider_name_coalesce("l", "p"); + let logs_data_source = display_data_source_expr("l"); let sql = format!( "SELECT l.request_id, l.provider_id, {logs_pname} as provider_name, l.app_type, l.model, l.request_model, l.cost_multiplier, l.input_tokens, l.output_tokens, l.cache_read_tokens, l.cache_creation_tokens, l.input_cost_usd, l.output_cost_usd, l.cache_read_cost_usd, l.cache_creation_cost_usd, l.total_cost_usd, l.is_streaming, l.latency_ms, l.first_token_ms, l.duration_ms, - l.status_code, l.error_message, l.created_at, l.data_source + l.status_code, l.error_message, l.created_at, {logs_data_source} as data_source FROM proxy_request_logs l LEFT JOIN providers p ON l.provider_id = p.id AND l.app_type = p.app_type {where_clause} @@ -752,13 +763,14 @@ impl Database { let conn = lock_conn!(self.conn); let detail_pname = provider_name_coalesce("l", "p"); + let detail_data_source = display_data_source_expr("l"); let detail_sql = format!( "SELECT l.request_id, l.provider_id, {detail_pname} as provider_name, l.app_type, l.model, l.request_model, l.cost_multiplier, input_tokens, output_tokens, cache_read_tokens, cache_creation_tokens, input_cost_usd, output_cost_usd, cache_read_cost_usd, cache_creation_cost_usd, total_cost_usd, is_streaming, latency_ms, first_token_ms, duration_ms, - status_code, error_message, created_at, l.data_source + status_code, error_message, created_at, {detail_data_source} as data_source FROM proxy_request_logs l LEFT JOIN providers p ON l.provider_id = p.id AND l.app_type = p.app_type WHERE l.request_id = ?" diff --git a/src/components/usage/DataSourceBar.tsx b/src/components/usage/DataSourceBar.tsx index 6e7b174d4e..7dabc51a8f 100644 --- a/src/components/usage/DataSourceBar.tsx +++ b/src/components/usage/DataSourceBar.tsx @@ -2,7 +2,7 @@ import { useTranslation } from "react-i18next"; import { useQuery, useQueryClient } from "@tanstack/react-query"; import { usageApi } from "@/lib/api/usage"; import { usageKeys } from "@/lib/query/usage"; -import { Bot, Database, FileText, RefreshCw, Loader2 } from "lucide-react"; +import { Database, FileText, RefreshCw, Loader2 } from "lucide-react"; import { Button } from "@/components/ui/button"; import { useState } from "react"; import { toast } from "sonner"; @@ -14,7 +14,6 @@ interface DataSourceBarProps { const DATA_SOURCE_ICONS: Record = { proxy: , session_log: , - session_subagent: , codex_db: , codex_session: , gemini_session: , diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 822a0b0719..b28e876c1e 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -1091,7 +1091,6 @@ "dataSource": { "proxy": "Proxy", "session_log": "Session Log", - "session_subagent": "Sub-Agent", "codex_db": "Codex DB", "codex_session": "Codex Session", "gemini_session": "Gemini Session" diff --git a/src/i18n/locales/ja.json b/src/i18n/locales/ja.json index f537c32f57..a8ba0cf517 100644 --- a/src/i18n/locales/ja.json +++ b/src/i18n/locales/ja.json @@ -1091,7 +1091,6 @@ "dataSource": { "proxy": "プロキシ", "session_log": "セッションログ", - "session_subagent": "サブエージェント", "codex_db": "Codex DB", "codex_session": "Codex セッション", "gemini_session": "Gemini セッション" diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json index 5875ee2240..421d7f859b 100644 --- a/src/i18n/locales/zh.json +++ b/src/i18n/locales/zh.json @@ -1092,7 +1092,6 @@ "dataSource": { "proxy": "代理", "session_log": "会话日志", - "session_subagent": "子Agent", "codex_db": "Codex 数据库", "codex_session": "Codex 会话日志", "gemini_session": "Gemini 会话日志"