Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
316 changes: 262 additions & 54 deletions src-tauri/src/acp/connection.rs

Large diffs are not rendered by default.

10 changes: 1 addition & 9 deletions src-tauri/src/parsers/claude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use regex::Regex;
use crate::models::*;
use crate::parsers::{
folder_name_from_path, is_safe_subagent_id, title_from_user_text, truncate_str, AgentEnv,
AgentParser, ParseError,
AgentParser, ParseError, TIMELINE_ENTRY_MAX_CHARS,
};

/// Regex that matches Claude Code system-injected XML tags and their content.
Expand Down Expand Up @@ -3465,14 +3465,6 @@ fn attribute_subagent_usage(session_path: &Path, turns: &mut [MessageTurn]) {
}
}

/// Per-entry cap for thinking/text blocks folded into a sub-agent timeline, so
/// one huge block cannot balloon the stats every detail load carries. Almost
/// every block is far below it; the ones that reach it are typically the
/// child's long final report (measured on local Claude Code transcripts: 27 of
/// 7,715 blocks, the longest ~40K chars), which the card also shows in full as
/// the Agent call's own result.
const TIMELINE_ENTRY_MAX_CHARS: usize = 16_384;

/// One parsed sub-agent transcript: the tool calls, the mixed timeline,
/// the deduped usage, the first record's timestamp, and the
/// first-to-last-timestamped-record span (`None` without a real interval).
Expand Down
315 changes: 292 additions & 23 deletions src-tauri/src/parsers/codebuddy.rs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src-tauri/src/parsers/kimi_code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -959,7 +959,7 @@ fn subagent_id_from_output(output: &str) -> Option<&str> {
/// `tool.call` with its `tool.result` by `toolCallId`. The outer
/// `tool_args_preview` / `tool_result_preview` helpers are reused so nested calls
/// render identically to top-level ones. Mirrors `codebuddy.rs`'s
/// `parse_codebuddy_subagent_tool_calls`.
/// `parse_codebuddy_subagent_transcript`.
///
/// Intentionally non-recursive: a nested `Agent` call inside the sub-agent shows
/// as a flat leaf tool here (no further descent), which bounds the work and
Expand Down
8 changes: 8 additions & 0 deletions src-tauri/src/parsers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,14 @@ pub fn truncate_str(s: &str, max_len: usize) -> String {
}
}

/// Per-entry cap for thinking/text blocks folded into a sub-agent timeline, so
/// one huge block cannot balloon the stats every detail load carries. Almost
/// every block is far below it; the ones that reach it are typically the
/// child's long final report (measured on local Claude Code transcripts: 27 of
/// 7,715 blocks, the longest ~40K chars), which the card also shows in full as
/// the Agent call's own result.
pub(crate) const TIMELINE_ENTRY_MAX_CHARS: usize = 16_384;

/// True when `id` is safe to embed as a single filename component beneath a
/// session's `subagents/` directory (Claude Code's and CodeBuddy's sub-agent
/// transcript layout). The id is read straight from transcript JSON
Expand Down
10 changes: 6 additions & 4 deletions src/contexts/acp-connections-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -227,10 +227,12 @@ export interface ClaudeApiRetryState {

export type LiveContentBlock =
/**
* `parentToolUseId`: subagent attribution (claude-agent-acp ≥0.63,
* `_meta.claudeCode.parentToolUseId`). Parented text/thinking belongs to
* the live Agent capsule of that tool call — the runtime store routes it
* out of the main thread. `undefined` = main-thread content.
* `parentToolUseId`: subagent attribution — Claude Code nests it at
* `_meta.claudeCode.parentToolUseId` (claude-agent-acp ≥0.63), CodeBuddy
* sends it flat at `_meta["codebuddy.ai/parentToolCallId"]`. Parented
* text/thinking belongs to the live Agent capsule of that tool call — the
* runtime store routes it out of the main thread. `undefined` = main-thread
* content.
*/
| { type: "text"; text: string; parentToolUseId?: string }
| { type: "thinking"; text: string; parentToolUseId?: string }
Expand Down
7 changes: 4 additions & 3 deletions src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,9 +263,10 @@ export type ContentBlock =
images?: ImageData[] | null
/**
* LIVE-stream data: the in-flight transcript of a native subagent —
* a marker per child tool call, plus (Claude Code only) the text/
* thinking chunks attributed to this Agent tool call via
* `_meta.claudeCode.parentToolUseId` (claude-agent-acp ≥0.63) —
* a marker per child tool call, plus the text/thinking chunks
* attributed to this Agent tool call — Claude Code via nested
* `_meta.claudeCode.parentToolUseId` (claude-agent-acp ≥0.63),
* CodeBuddy via flat `_meta["codebuddy.ai/parentToolCallId"]` —
* rendered inside the live Agent capsule. Still attached at settle so
* the finished card keeps the interleaved order it showed live; once
* history reloads from disk, the parsed `agent_stats.timeline` takes
Expand Down
10 changes: 6 additions & 4 deletions src/stores/conversation-runtime-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2359,10 +2359,12 @@ function reducer(
// Agent transcripts ride along on promotion: the settled card keeps the
// interleaved order it showed live, and the authoritative detail reload
// that later replaces `localTurns` supplies `agent_stats.timeline`
// parsed from the child's own transcript (Claude Code, Qoder). The only
// live prose comes from Claude Code; elsewhere the transcript is tool
// markers alone, which the card renders like stats, so a reload from
// an agent without a parsed timeline does not change the card's shape.
// parsed from the child's own transcript (Claude Code, Qoder, CodeBuddy).
// Live sub-agent prose arrives from Claude Code's forwarded chunks and
// from CodeBuddy's parented chunks (routed by the backend into the live
// Agent capsule); agents without a parsed timeline still render their
// transcript as tool markers alone, so a reload does not change the
// card's shape.
const streamingTurns = sourceLiveMessage
? buildStreamingTurnsFromLiveMessage(
current.conversationId,
Expand Down
Loading