Compare commits

...

1 Commits

Author SHA1 Message Date
abeatrix eb4edda1e1 fix: try repairing interrupted native tools stream
Add jsonrepair package (v3.13.2) to handle malformed JSON responses and
bump package version from 2.2.0 to 2.2.1. Also clean up peer dependency
flags in package-lock.json for various platform-specific packages.

This supports the fix for interrupted response handling with native
tools by providing robust JSON parsing capabilities.

Refactor the interrupted response message construction to:
- Extract interruption note into a separate variable for clarity
- Build abortedAssistantContent as an array to properly include any
  finalized tool uses that were streamed before interruption
- Separate error message into its own text block instead of concatenating

This ensures tool calls are preserved in the conversation history when
a response is interrupted, maintaining context for task resumption.
2026-02-12 14:33:47 -08:00
4 changed files with 98 additions and 104 deletions
+55 -79
View File
@@ -75,6 +75,7 @@
"isbinaryfile": "^5.0.2",
"jschardet": "^3.1.4",
"json5": "^2.2.3",
"jsonrepair": "^3.13.2",
"mammoth": "^1.11.0",
"nanoid": "^5.1.6",
"nice-grpc": "^2.1.12",
@@ -161,7 +162,7 @@
},
"cli": {
"name": "cline",
"version": "2.2.0",
"version": "2.2.1",
"cpu": [
"x64",
"arm64"
@@ -1575,6 +1576,7 @@
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@babel/code-frame": "^7.29.0",
"@babel/generator": "^7.29.0",
@@ -3128,6 +3130,7 @@
"resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.3.tgz",
"integrity": "sha512-Iq8QQQ/7X3Sac15oB6p0FmUg/klxQvXLeileoqrTRGJYLV+/9tubbr9ipz0GKHjmXVsgFPo/+W+2cA8eNcR+XA==",
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"@grpc/proto-loader": "^0.8.0",
"@js-sdsl/ordered-map": "^4.4.2"
@@ -4030,6 +4033,7 @@
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.26.0.tgz",
"integrity": "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@hono/node-server": "^1.19.9",
"ajv": "^8.17.1",
@@ -4105,6 +4109,7 @@
"resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz",
"integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==",
"license": "Apache-2.0",
"peer": true,
"engines": {
"node": ">=8.0.0"
}
@@ -5795,8 +5800,7 @@
"optional": true,
"os": [
"android"
],
"peer": true
]
},
"node_modules/@rollup/rollup-android-arm64": {
"version": "4.57.1",
@@ -5809,8 +5813,7 @@
"optional": true,
"os": [
"android"
],
"peer": true
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.57.1",
@@ -5823,8 +5826,7 @@
"optional": true,
"os": [
"darwin"
],
"peer": true
]
},
"node_modules/@rollup/rollup-darwin-x64": {
"version": "4.57.1",
@@ -5837,8 +5839,7 @@
"optional": true,
"os": [
"darwin"
],
"peer": true
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
"version": "4.57.1",
@@ -5851,8 +5852,7 @@
"optional": true,
"os": [
"freebsd"
],
"peer": true
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
"version": "4.57.1",
@@ -5865,8 +5865,7 @@
"optional": true,
"os": [
"freebsd"
],
"peer": true
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.57.1",
@@ -5879,8 +5878,7 @@
"optional": true,
"os": [
"linux"
],
"peer": true
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.57.1",
@@ -5893,8 +5891,7 @@
"optional": true,
"os": [
"linux"
],
"peer": true
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.57.1",
@@ -5907,8 +5904,7 @@
"optional": true,
"os": [
"linux"
],
"peer": true
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.57.1",
@@ -5921,8 +5917,7 @@
"optional": true,
"os": [
"linux"
],
"peer": true
]
},
"node_modules/@rollup/rollup-linux-loong64-gnu": {
"version": "4.57.1",
@@ -5935,8 +5930,7 @@
"optional": true,
"os": [
"linux"
],
"peer": true
]
},
"node_modules/@rollup/rollup-linux-loong64-musl": {
"version": "4.57.1",
@@ -5949,8 +5943,7 @@
"optional": true,
"os": [
"linux"
],
"peer": true
]
},
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
"version": "4.57.1",
@@ -5963,8 +5956,7 @@
"optional": true,
"os": [
"linux"
],
"peer": true
]
},
"node_modules/@rollup/rollup-linux-ppc64-musl": {
"version": "4.57.1",
@@ -5977,8 +5969,7 @@
"optional": true,
"os": [
"linux"
],
"peer": true
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.57.1",
@@ -5991,8 +5982,7 @@
"optional": true,
"os": [
"linux"
],
"peer": true
]
},
"node_modules/@rollup/rollup-linux-riscv64-musl": {
"version": "4.57.1",
@@ -6005,8 +5995,7 @@
"optional": true,
"os": [
"linux"
],
"peer": true
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.57.1",
@@ -6019,8 +6008,7 @@
"optional": true,
"os": [
"linux"
],
"peer": true
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.57.1",
@@ -6033,8 +6021,7 @@
"optional": true,
"os": [
"linux"
],
"peer": true
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.57.1",
@@ -6047,8 +6034,7 @@
"optional": true,
"os": [
"linux"
],
"peer": true
]
},
"node_modules/@rollup/rollup-openbsd-x64": {
"version": "4.57.1",
@@ -6061,8 +6047,7 @@
"optional": true,
"os": [
"openbsd"
],
"peer": true
]
},
"node_modules/@rollup/rollup-openharmony-arm64": {
"version": "4.57.1",
@@ -6075,8 +6060,7 @@
"optional": true,
"os": [
"openharmony"
],
"peer": true
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.57.1",
@@ -6089,8 +6073,7 @@
"optional": true,
"os": [
"win32"
],
"peer": true
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.57.1",
@@ -6103,8 +6086,7 @@
"optional": true,
"os": [
"win32"
],
"peer": true
]
},
"node_modules/@rollup/rollup-win32-x64-gnu": {
"version": "4.57.1",
@@ -6117,8 +6099,7 @@
"optional": true,
"os": [
"win32"
],
"peer": true
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.57.1",
@@ -6131,8 +6112,7 @@
"optional": true,
"os": [
"win32"
],
"peer": true
]
},
"node_modules/@sap-ai-sdk/ai-api": {
"version": "2.6.0",
@@ -7804,6 +7784,7 @@
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.32.tgz",
"integrity": "sha512-Ez8QE4DMfhjjTsES9K2dwfV258qBui7qxUsoaixZDiTzbde4U12e1pXGNu/ECsUIOi5/zoCxAQxIhQnaUQ2VvA==",
"license": "MIT",
"peer": true,
"dependencies": {
"undici-types": "~6.21.0"
}
@@ -7866,6 +7847,7 @@
"integrity": "sha512-KkiJeU6VbYbUOp5ITMIc7kBfqlYkKA5KhEHVrGMmUUMt7NeaZg65ojdPk+FtNrBAOXNVM5QM72jnADjM+XVRAQ==",
"devOptional": true,
"license": "MIT",
"peer": true,
"dependencies": {
"csstype": "^3.2.2"
}
@@ -8800,6 +8782,7 @@
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"license": "MIT",
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -9764,6 +9747,7 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
@@ -11162,7 +11146,8 @@
"version": "0.0.1367902",
"resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1367902.tgz",
"integrity": "sha512-XxtPuC3PGakY6PD7dG66/o8KwJ/LkH2/EKe19Dcw58w53dv4/vSQEkn/SzuyhHE2q4zPgCkxQBxus3VV4ql+Pg==",
"license": "BSD-3-Clause"
"license": "BSD-3-Clause",
"peer": true
},
"node_modules/diff": {
"version": "5.2.2",
@@ -12179,6 +12164,7 @@
"resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz",
"integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==",
"license": "MIT",
"peer": true,
"dependencies": {
"accepts": "^2.0.0",
"body-parser": "^2.2.1",
@@ -13486,6 +13472,7 @@
"resolved": "https://registry.npmjs.org/hono/-/hono-4.11.7.tgz",
"integrity": "sha512-l7qMiNee7t82bH3SeyUCt9UF15EVmaBvsppY2zQtrbIhl/yzBTny+YUxsVjSjQ6gaqaeVtZmGocom8TzBlA4Yw==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=16.9.0"
}
@@ -13764,6 +13751,7 @@
"resolved": "https://registry.npmjs.org/@jrichman/ink/-/ink-6.4.7.tgz",
"integrity": "sha512-QHyxhNF5VonF5cRmdAJD/UPucB9nRx3FozWMjQrDGfBxfAL9lpyu72/MlFPgloS1TMTGsOt7YN6dTPPA6mh0Aw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@alcalzone/ansi-tokenize": "^0.2.1",
"ansi-escapes": "^7.0.0",
@@ -15017,6 +15005,7 @@
"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz",
"integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==",
"license": "MIT",
"peer": true,
"bin": {
"jiti": "lib/jiti-cli.mjs"
}
@@ -15140,6 +15129,15 @@
"graceful-fs": "^4.1.6"
}
},
"node_modules/jsonrepair": {
"version": "3.13.2",
"resolved": "https://registry.npmjs.org/jsonrepair/-/jsonrepair-3.13.2.tgz",
"integrity": "sha512-Leuly0nbM4R+S5SVJk3VHfw1oxnlEK9KygdZvfUtEtTawNDyzB4qa1xWTmFt1aeoA7sXZkVTRuIixJ8bAvqVUg==",
"license": "ISC",
"bin": {
"jsonrepair": "bin/cli.js"
}
},
"node_modules/jsonwebtoken": {
"version": "9.0.3",
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz",
@@ -15366,6 +15364,7 @@
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz",
"integrity": "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==",
"license": "MPL-2.0",
"peer": true,
"dependencies": {
"detect-libc": "^2.0.3"
},
@@ -19102,6 +19101,7 @@
"resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz",
"integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -21809,6 +21809,7 @@
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -22130,6 +22131,7 @@
"resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz",
"integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==",
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "^0.27.0",
"fdir": "^6.5.0",
@@ -22211,7 +22213,6 @@
"os": [
"aix"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -22228,7 +22229,6 @@
"os": [
"android"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -22245,7 +22245,6 @@
"os": [
"android"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -22262,7 +22261,6 @@
"os": [
"android"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -22279,7 +22277,6 @@
"os": [
"darwin"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -22296,7 +22293,6 @@
"os": [
"darwin"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -22313,7 +22309,6 @@
"os": [
"freebsd"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -22330,7 +22325,6 @@
"os": [
"freebsd"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -22347,7 +22341,6 @@
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -22364,7 +22357,6 @@
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -22381,7 +22373,6 @@
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -22398,7 +22389,6 @@
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -22415,7 +22405,6 @@
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -22432,7 +22421,6 @@
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -22449,7 +22437,6 @@
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -22466,7 +22453,6 @@
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -22483,7 +22469,6 @@
"os": [
"linux"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -22500,7 +22485,6 @@
"os": [
"netbsd"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -22517,7 +22501,6 @@
"os": [
"netbsd"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -22534,7 +22517,6 @@
"os": [
"openbsd"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -22551,7 +22533,6 @@
"os": [
"openbsd"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -22568,7 +22549,6 @@
"os": [
"openharmony"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -22585,7 +22565,6 @@
"os": [
"sunos"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -22602,7 +22581,6 @@
"os": [
"win32"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -22619,7 +22597,6 @@
"os": [
"win32"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -22636,7 +22613,6 @@
"os": [
"win32"
],
"peer": true,
"engines": {
"node": ">=18"
}
@@ -22692,7 +22668,6 @@
"os": [
"darwin"
],
"peer": true,
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
@@ -23627,6 +23602,7 @@
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
"license": "MIT",
"peer": true,
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
+1
View File
@@ -564,6 +564,7 @@
"isbinaryfile": "^5.0.2",
"jschardet": "^3.1.4",
"json5": "^2.2.3",
"jsonrepair": "^3.13.2",
"mammoth": "^1.11.0",
"nanoid": "^5.1.6",
"nice-grpc": "^2.1.12",
+18 -5
View File
@@ -1,5 +1,6 @@
import type { ToolUse } from "@core/assistant-message"
import { JSONParser } from "@streamparser/json"
import { jsonrepair } from "jsonrepair"
import { nanoid } from "nanoid"
import { McpHub } from "@/services/mcp/McpHub"
import { CLINE_MCP_TOOL_IDENTIFIER } from "@/shared/mcp"
@@ -130,11 +131,7 @@ class ToolUseHandler {
if (pending.parsedInput != null) {
input = pending.parsedInput
} else if (pending.input) {
try {
input = JSON.parse(pending.input)
} catch {
input = this.extractPartialJsonFields(pending.input)
}
parseJsonSafe(pending.input)
}
return {
@@ -349,3 +346,19 @@ class ReasoningHandler {
this.pendingReasoning = null
}
}
function parseJsonSafe(input: unknown): string | unknown {
if (typeof input === "string" && (input.startsWith("{") || input.startsWith("["))) {
try {
return JSON.parse(input)
} catch {
try {
const repaired = jsonrepair(input)
return JSON.parse(repaired)
} catch {
return input
}
}
}
return input
}
+24 -20
View File
@@ -2491,7 +2491,7 @@ export class Task {
await this.say(
"api_req_started",
JSON.stringify({
request: userContent.map((block) => formatContentBlockToMarkdown(block)).join("\n\n") + "\n\nLoading...",
request: `${userContent.map((block) => formatContentBlockToMarkdown(block)).join("\n\n")}\n\nLoading...`,
}),
)
@@ -2540,7 +2540,7 @@ export class Task {
// if last message is a partial we need to update and save it
const lastMessage = this.messageStateHandler.getClineMessages().at(-1)
if (lastMessage && lastMessage.partial) {
if (lastMessage?.partial) {
// lastMessage.ts = Date.now() DO NOT update ts since it is used as a key for virtuoso list
lastMessage.partial = false
// instead of streaming partialMessage events, we do a save and post like normal to persist to disk
@@ -2562,21 +2562,24 @@ export class Task {
})
await this.messageStateHandler.saveClineMessagesAndUpdateHistory()
// Let assistant know their response was interrupted for when task is resumed
const abortedAssistantContent: Array<ClineAssistantContent> = [
{
type: "text",
text: assistantMessage,
},
// Let assistant know their response was interrupted for when task is resumed
{
type: "text",
text: `[ERROR] ${cancelReason === "streaming_failed" ? "Response interrupted by API Error" : "Response interrupted by user"}`,
},
]
// Include any native tool call blocks that were streamed before the interruption
abortedAssistantContent.push(...toolUseHandler.getAllFinalizedToolUses())
await this.messageStateHandler.addToApiConversationHistory({
role: "assistant",
content: [
{
type: "text",
text:
assistantMessage +
`\n\n[${
cancelReason === "streaming_failed"
? "Response interrupted by API Error"
: "Response interrupted by user"
}]`,
},
],
content: abortedAssistantContent,
modelInfo,
metrics: {
tokens: {
@@ -2724,7 +2727,9 @@ export class Task {
// present content to user - we don't want the stream to break if present fails, so we catch errors here
await this.presentAssistantMessage().catch((error) =>
Logger.debug("[Task] Failed to present message: " + error),
Logger.debug(
`[Task] Failed to present message: ${error instanceof Error ? error.message : String(error)}`,
),
)
if (this.taskState.abort) {
@@ -3249,8 +3254,7 @@ export class Task {
*/
private formatWorkspaceRootsSection(): string {
const multiRootEnabled = isMultiRootEnabled(this.stateManager)
const hasWorkspaceManager = !!this.workspaceManager
const roots = hasWorkspaceManager ? this.workspaceManager!.getRoots() : []
const roots = this.workspaceManager?.getRoots() || []
// Only show workspace roots if multi-root is enabled and there are multiple roots
if (!multiRootEnabled || roots.length <= 1) {
@@ -3267,7 +3271,7 @@ export class Task {
}
// Add primary workspace information
const primary = this.workspaceManager!.getPrimaryRoot()
const primary = this.workspaceManager?.getPrimaryRoot()
const primaryName = this.getPrimaryWorkspaceName(primary)
section += `\n\nPrimary workspace: ${primaryName}`
@@ -3507,7 +3511,7 @@ export class Task {
details += "\n\n# Current Mode"
const mode = this.stateManager.getGlobalSettingsKey("mode")
if (mode === "plan") {
details += "\nPLAN MODE\n" + formatResponse.planModeInstructions()
details += `\nPLAN MODE\n${formatResponse.planModeInstructions()}`
} else {
details += "\nACT MODE"
}