fix(coding-agent): fixed read tool streaming routing and result expansion behavior

- Updated event-controller read-tool streaming handling to wait for a parseable target before routing tool calls, avoiding early component binding for unresolved arguments.
- Allowed internal-URL read calls to bypass the regular read grouping path and fall through to direct tool execution.
- Adjusted read tool rendering to honor the computed `expanded` flag for completed output instead of forcing expanded output.
This commit is contained in:
can1357
2026-05-13 04:42:43 +02:00
parent e70084976b
commit e7b4b4c20d
4 changed files with 28 additions and 30 deletions
@@ -277,21 +277,26 @@ export class EventController {
for (const content of this.ctx.streamingMessage.content) {
if (content.type !== "toolCall") continue;
if (
content.name === "read" &&
readArgsHaveTarget(content.arguments) &&
!readArgsTargetInternalUrl(content.arguments)
) {
this.#trackReadToolCall(content.id, content.arguments);
const component = this.ctx.pendingTools.get(content.id);
if (component) {
component.updateArgs(content.arguments, content.id);
} else {
const group = this.#getReadGroup();
group.updateArgs(content.arguments, content.id);
this.ctx.pendingTools.set(content.id, group);
if (content.name === "read") {
if (!readArgsHaveTarget(content.arguments)) {
// Args still streaming — defer until path is parseable so we can route to the
// read group (regular files) vs ToolExecutionComponent (internal URLs).
// Creating either component now would lock the read into the wrong shape.
continue;
}
continue;
if (!readArgsTargetInternalUrl(content.arguments)) {
this.#trackReadToolCall(content.id, content.arguments);
const component = this.ctx.pendingTools.get(content.id);
if (component) {
component.updateArgs(content.arguments, content.id);
} else {
const group = this.#getReadGroup();
group.updateArgs(content.arguments, content.id);
this.ctx.pendingTools.set(content.id, group);
}
continue;
}
// Internal URL read falls through to ToolExecutionComponent below.
}
// Preserve the raw partial JSON for renderers that need to surface fields before the JSON object closes.
+1 -4
View File
@@ -1378,10 +1378,7 @@ export function renderReadUrlResult(
const outputBlock = new CachedOutputBlock();
return {
render: (width: number) =>
outputBlock.render(
{ header, state: "error", sections: [{ lines: errorLines }], width },
uiTheme,
),
outputBlock.render({ header, state: "error", sections: [{ lines: errorLines }], width }, uiTheme),
invalidate: () => outputBlock.invalidate(),
};
}
+6 -7
View File
@@ -2674,7 +2674,6 @@ export function parsePrUnifiedDiff(text: string): PrDiffPayload {
return { unified: text, files };
}
interface ParsedDiffHeaderToken {
value: string;
nextIndex: number;
@@ -2719,7 +2718,7 @@ function parseDiffQuotedEscape(text: string, slashIndex: number): ParsedDiffHead
case "v":
return { value: "\v", nextIndex: slashIndex + 2 };
case "\\":
case "\"":
case '"':
return { value: next, nextIndex: slashIndex + 2 };
default:
return { value: next, nextIndex: slashIndex + 2 };
@@ -2727,11 +2726,11 @@ function parseDiffQuotedEscape(text: string, slashIndex: number): ParsedDiffHead
}
function parseDiffQuotedToken(text: string, startIndex: number): ParsedDiffHeaderToken | undefined {
if (text.charAt(startIndex) !== "\"") return undefined;
if (text.charAt(startIndex) !== '"') return undefined;
let value = "";
for (let i = startIndex + 1; i < text.length; i += 1) {
const ch = text.charAt(i);
if (ch === "\"") return { value, nextIndex: i + 1 };
if (ch === '"') return { value, nextIndex: i + 1 };
if (ch !== "\\") {
value += ch;
continue;
@@ -2759,7 +2758,7 @@ function stripPrDiffPathPrefix(value: string, prefix: "a/" | "b/"): string | und
function parsePrDiffHeaderPaths(header: string): { oldPath?: string; newPath?: string } {
const trail = header.slice("diff --git ".length);
if (trail.startsWith("\"")) {
if (trail.startsWith('"')) {
const oldToken = parseDiffQuotedToken(trail, 0);
if (!oldToken) return {};
const newToken = parseDiffHeaderToken(trail, oldToken.nextIndex);
@@ -2786,8 +2785,8 @@ function isPrDiffFileHeaderLine(line: string): boolean {
line === "+++ /dev/null" ||
line.startsWith("--- a/") ||
line.startsWith("+++ b/") ||
line.startsWith("--- \"a/") ||
line.startsWith("+++ \"b/")
line.startsWith('--- "a/') ||
line.startsWith('+++ "b/')
);
}
+2 -5
View File
@@ -1840,10 +1840,7 @@ export const readToolRenderer = {
const outputBlock = new CachedOutputBlock();
return {
render: (width: number) =>
outputBlock.render(
{ header, state: "error", sections: [{ lines: errorLines }], width },
uiTheme,
),
outputBlock.render({ header, state: "error", sections: [{ lines: errorLines }], width }, uiTheme),
invalidate: () => outputBlock.invalidate(),
};
}
@@ -1938,7 +1935,7 @@ export const readToolRenderer = {
title,
status: "complete",
output: warningLines.length > 0 ? warningLines.join("\n") : undefined,
expanded: true,
expanded,
width,
},
uiTheme,