Release/v6.8.5 - #129
Release/v6.8.5#129
Conversation
… dropping them
Some models leak internal argument-templating tags (e.g.
<longcat_arg_value>) into native tool-call JSON arguments, making the
JSON invalid and dropping the call at stream finalization.
- tryParseToolArguments strips value-position placeholder tags after
the strict parse fails: a tag prefixing a real value keeps the value
("limit": <tag>180 parses as 180), while a tag-only value removes
the key entirely so the tool's own default applies (read_file without
limit reads the complete file).
- Tags inside legitimate string values are never touched; well-formed
JSON still parses verbatim; incomplete streaming buffers keep
accumulating instead of completing early.
- The XML-path use_mcp_tool argument parse applies the same cleanup
before failing the call.
- Partial-parameter extraction strips tags so the streaming UI shows
the real value behind the tag.
- 39 regression tests: both tag shapes, nested files arrays, tag-only
keys followed by other members, composition with the bare-scalar
repair, tags inside valid strings untouched, incomplete-buffer
accumulation, and end-to-end processNativeToolCalls runs streamed at
chunk sizes 1/7/64/full.
|
✅ Reviewed the changes: Solid, well-tested placeholder-tag repair with good streaming coverage. One logging issue: the repair warning fires on every failed candidate (including every streaming delta) instead of only on successful repair. Reviewed src/core/assistant-message/tests/AssistantMessageParser.spec.ts: no issues found. Reviewed src/core/tools/useMcpToolTool.ts: no issues found. Reviewed src/package.json: version bump only, no issues. |
…ools Replace the narrow placeholder-tag regex cleanup with a best-effort repair layer (src/utils/jsonRepair.ts) used by every argument ingestion path: live streaming, stream finalization, and MCP argument validation. - strict JSON.parse first; valid arguments are never mutated (the only exception: stripping a leaked placeholder tag from a key, since keys are structural parameter names; tags inside string values survive) - repairs placeholder tags in structural positions (tag-prefixed value kept, tag-only value nulled so the tool default applies), unquoted keys/values, single quotes, Python literals, comments, trailing commas, missing commas, and lost key quotes - at finalization only: closes dangling strings/containers innermost- first, nulls a dangling colon value, wraps braceless bodies; during streaming incomplete buffers stay unparseable so deltas accumulate - unwraps a single-object array the model wrapped by mistake - ToolUse carries a repaired flag; repaired executions append the executed arguments to the tool result so the model does not repeat the malformed form - executor hardening: parsePositiveInteger for read_file/kilocode offset/limit (floor, clamp min 1, default on non-numeric), floored search_files max_results/context_lines
There was a problem hiding this comment.
🧪 PR Review is completed: Well-structured JSON-repair layer with strict-parse-first semantics and strong test coverage; the prior per-delta console.warn spam is fixed. One concern to verify: the new repair-note push in presentAssistantMessage may create a second tool_result block with a duplicate tool_use_id. Reviewed src/core/assistant-message/AssistantMessageParser.ts: no other issues found. Reviewed src/utils/jsonRepair.ts: no issues found. Reviewed src/utils/tests/jsonRepair.spec.ts: no issues found. Reviewed src/core/assistant-message/tests/AssistantMessageParser.spec.ts: no issues found. Reviewed src/core/tools/useMcpToolTool.ts: no issues found. Reviewed src/core/tools/kilocode.ts: no issues found. Reviewed src/core/tools/readFileTool.ts: no issues found. Reviewed src/services/search-files/types.ts: no issues found. Reviewed src/shared/tools.ts: no issues found.
Skipped files
CHANGELOG.md: Skipped file pattern
⬇️ Low Priority Suggestions (1)
src/core/assistant-message/presentAssistantMessage.ts (1 suggestion)
Location:
src/core/assistant-message/presentAssistantMessage.ts(Lines 962-975)🟠 API Contract / Tool-Result Pairing
Issue: This block runs only when
toolResultPushedis already true — i.e. the tool handler has already pushed atool_resultforblock.toolUseId. UnlesspushToolResult_withToolUseId_kilocode(defined ~line 320) merges content into the existing tool_result for that id, this pushes a secondtool_resultblock with the sametool_use_idinto the conversation history. Anthropic (and most OpenAI-compatible providers) enforce 1:1 tool_use/tool_result pairing — a duplicatetool_use_idin tool_result blocks can cause the next API request to be rejected with a 400, killing the task loop. Since this fires on every repaired tool call (the exact scenario this PR enables), please verify the helper's behavior.Fix: Append the repair note to the already-pushed tool_result for
block.toolUseIdinstead of pushing a separate tool_result block.Impact: Guarantees exactly one tool_result per tool_use, keeping the assistant/user message pairing valid across all providers.
- if (block.repaired && !block.partial && toolResultPushed && !cline.didRejectTool) { - try { - const executedArguments = - block.name === "use_mcp_tool" - ? String(block.params.arguments ?? "{}") - : JSON.stringify(block.params) - pushToolResult_withToolUseId_kilocode({ - type: "text", - text: formatArgumentRepairNote(executedArguments), - }) - } catch (error) { - console.error("[presentAssistantMessage] Failed to append argument repair note:", error) - } - } + if (block.repaired && !block.partial && toolResultPushed && !cline.didRejectTool) { + try { + const executedArguments = + block.name === "use_mcp_tool" + ? String(block.params.arguments ?? "{}") + : JSON.stringify(block.params) + const note = formatArgumentRepairNote(executedArguments) + const buffer = options.resultBuffer ?? cline.userMessageContent + const existing = [...buffer] + .reverse() + .find( + (m) => + (m as { type?: string }).type === "tool_result" && + (m as { tool_use_id?: string }).tool_use_id === block.toolUseId, + ) as { content: unknown } | undefined + if (existing && Array.isArray(existing.content)) { + ;(existing.content as Array<{ type: "text"; text: string }>).push({ type: "text", text: note }) + } + } catch (error) { + console.error("[presentAssistantMessage] Failed to append argument repair note:", error) + } + }
Release v6.8.5
Fixed
"limit": <longcat_arg_value>180or"limit": <longcat_arg_value>),AssistantMessageParsernow strips the tag and keeps the trailing value, or drops the dangling key entirely when no value follows so the tool's default applies. The repair runs only after strictJSON.parsefails, so valid JSON is never altered.useMcpToolToolapplies the same cleanup when validating MCP tool arguments, and streaming partial previews are tag-cleaned as well.Includes version bump to 6.8.5 and changelog entry.