Skip to content

Commit 62773b8

Browse files
Merge pull request #3 from continuedev/fix/missing-upstream-deps
Fix: add missing upstream dependencies breaking main
2 parents 20e0f1b + fe0661e commit 62773b8

6 files changed

Lines changed: 202 additions & 8 deletions

File tree

core/config/load.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,7 @@ function llmToSerializedModelDescription(llm: ILLM): ModelDescription {
651651
envSecretLocations: llm.envSecretLocations,
652652
sourceFile: llm.sourceFile,
653653
isFromAutoDetect: llm.isFromAutoDetect,
654+
toolOverrides: llm.toolOverrides,
654655
};
655656
}
656657

core/index.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1255,6 +1255,9 @@ export interface ModelDescription {
12551255

12561256
sourceFile?: string;
12571257
isFromAutoDetect?: boolean;
1258+
1259+
/** Tool overrides for this model */
1260+
toolOverrides?: ToolOverride[];
12581261
}
12591262

12601263
export interface JSONEmbedOptions {

core/llm/utils/retry.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { isAbortError } from "../../util/isAbortError.js";
2+
13
/**
24
* Configuration options for the retry decorator
35
*/
@@ -103,7 +105,7 @@ function defaultShouldRetry(error: any, attempt: number): boolean {
103105
}
104106

105107
// Abort signal errors should not be retried
106-
if (error.name === "AbortError" || error.code === "ABORT_ERR") {
108+
if (isAbortError(error)) {
107109
return false;
108110
}
109111

core/tools/systemMessageTools/toolCodeblocks/index.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,20 @@ export class SystemMessageToolCodeblocksFramework
6565
return toolDefinition.trim();
6666
}
6767

68-
systemMessagePrefix = `You have access to several "tools" that you can use at any time to retrieve information and/or perform tasks for the User.
69-
To use a tool, respond with a tool code block (\`\`\`tool) using the syntax shown in the examples below:`;
70-
71-
systemMessageSuffix = `If it seems like the User's request could be solved with one of the tools, choose the BEST one for the job based on the user's request and the tool descriptions
72-
Then send the \`\`\`tool codeblock (YOU call the tool, not the user). Always start the codeblock on a new line.
73-
Do not perform actions with/for hypothetical files. Ask the user or use tools to deduce which files are relevant.
74-
You can only call ONE tool at at time. The tool codeblock should be the last thing you say; stop your response after the tool codeblock.`;
68+
systemMessagePrefix = `You have access to tools. To call a tool, you MUST respond with EXACTLY this format — a tool code block (\`\`\`tool) using the syntax shown below.
69+
70+
CRITICAL: Follow the exact syntax. Do not use XML tags, JSON objects, or any other format for tool calls.`;
71+
72+
systemMessageSuffix = `RULES FOR TOOL USE:
73+
1. To call a tool, output a \`\`\`tool code block using EXACTLY the format shown above.
74+
2. Always start the code block on a new line.
75+
3. You can only call ONE tool at a time.
76+
4. The \`\`\`tool code block MUST be the last thing in your response. Stop immediately after the closing \`\`\`.
77+
5. Do NOT wrap tool calls in XML tags like <tool_call> or <function=...>.
78+
6. Do NOT use JSON format for tool calls.
79+
7. Do NOT invent tools that are not listed above.
80+
8. If the user's request can be addressed with a listed tool, use it rather than guessing.
81+
9. Do not perform actions with hypothetical files. Use tools to find relevant files.`;
7582

7683
exampleDynamicToolDefinition = `
7784
\`\`\`tool_definition

core/util/isAbortError.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
* Unified abort error detection.
3+
* Covers all known abort patterns in the Continue codebase:
4+
* - String literal "cancel" (streaming cancellation)
5+
* - Error with name "AbortError" (node-fetch, DOM)
6+
* - Error with code "ABORT_ERR" (Node.js AbortSignal)
7+
* - DOMException with name "AbortError" (browser/Node.js 18+)
8+
* - Plain objects with name "AbortError" (serialized errors)
9+
*/
10+
export function isAbortError(error: unknown): boolean {
11+
if (error === null || error === undefined) return false;
12+
13+
// String-based "cancel" (used in Continue streaming path)
14+
if (error === "cancel") return true;
15+
16+
// Standard Error objects
17+
if (error instanceof Error) {
18+
if (error.name === "AbortError") return true;
19+
if ("code" in error && (error as any).code === "ABORT_ERR") return true;
20+
}
21+
22+
// DOMException (browser/Node.js 18+)
23+
// In some runtimes DOMException does not extend Error, so check separately.
24+
if (
25+
typeof DOMException !== "undefined" &&
26+
error instanceof DOMException &&
27+
error.name === "AbortError"
28+
) {
29+
return true;
30+
}
31+
32+
// Plain objects with name property (e.g. serialized errors across boundaries)
33+
if (typeof error === "object" && "name" in error) {
34+
const name = (error as any).name;
35+
if (typeof name === "string" && name === "AbortError") return true;
36+
}
37+
38+
return false;
39+
}

core/util/isAbortError.vitest.ts

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
import { describe, expect, it } from "vitest";
2+
import { isAbortError } from "./isAbortError";
3+
4+
describe("isAbortError", () => {
5+
// -- Positive cases --
6+
7+
it('returns true for "cancel" string', () => {
8+
expect(isAbortError("cancel")).toBe(true);
9+
});
10+
11+
it("returns true for Error with name AbortError", () => {
12+
const error = Object.assign(new Error("aborted"), { name: "AbortError" });
13+
expect(isAbortError(error)).toBe(true);
14+
});
15+
16+
it("returns true for Error with ABORT_ERR code", () => {
17+
const error = Object.assign(new Error("aborted"), { code: "ABORT_ERR" });
18+
expect(isAbortError(error)).toBe(true);
19+
});
20+
21+
it("returns true for Error with both AbortError name and ABORT_ERR code", () => {
22+
const error = Object.assign(new Error("aborted"), {
23+
name: "AbortError",
24+
code: "ABORT_ERR",
25+
});
26+
expect(isAbortError(error)).toBe(true);
27+
});
28+
29+
it("returns true for custom Error subclass with AbortError name", () => {
30+
class CustomAbortError extends Error {
31+
constructor() {
32+
super("aborted");
33+
this.name = "AbortError";
34+
}
35+
}
36+
expect(isAbortError(new CustomAbortError())).toBe(true);
37+
});
38+
39+
const hasDOMException = typeof DOMException !== "undefined";
40+
41+
it.skipIf(!hasDOMException)(
42+
"returns true for DOMException with AbortError name",
43+
() => {
44+
const error = new DOMException("Operation aborted", "AbortError");
45+
expect(isAbortError(error)).toBe(true);
46+
},
47+
);
48+
49+
it("returns true for plain object with name AbortError", () => {
50+
expect(isAbortError({ name: "AbortError" })).toBe(true);
51+
});
52+
53+
it("returns true for frozen object with name AbortError", () => {
54+
expect(isAbortError(Object.freeze({ name: "AbortError" }))).toBe(true);
55+
});
56+
57+
// -- Negative cases: exact match, not substring --
58+
59+
it("returns false for Error with name containing AbortError as substring", () => {
60+
const error = Object.assign(new Error("x"), { name: "NetworkAbortError" });
61+
expect(isAbortError(error)).toBe(false);
62+
});
63+
64+
it("returns false for Error with partial name Abort", () => {
65+
const error = Object.assign(new Error("x"), { name: "Abort" });
66+
expect(isAbortError(error)).toBe(false);
67+
});
68+
69+
it("returns false for plain object with AbortError substring in name", () => {
70+
expect(isAbortError({ name: "AbortErrorWrapper" })).toBe(false);
71+
});
72+
73+
// -- Negative cases: cancel is case-sensitive --
74+
75+
it('returns false for "Cancel" (capital C)', () => {
76+
expect(isAbortError("Cancel")).toBe(false);
77+
});
78+
79+
it('returns false for "CANCEL" (all caps)', () => {
80+
expect(isAbortError("CANCEL")).toBe(false);
81+
});
82+
83+
// -- Negative cases: plain object edge cases --
84+
85+
it("returns false for plain object with ABORT_ERR code but no AbortError name", () => {
86+
// code check only applies to instanceof Error, not plain objects
87+
expect(isAbortError({ code: "ABORT_ERR" })).toBe(false);
88+
});
89+
90+
it("returns false for plain object with numeric name", () => {
91+
expect(isAbortError({ name: 123 })).toBe(false);
92+
});
93+
94+
it("returns false for plain object with null name", () => {
95+
expect(isAbortError({ name: null })).toBe(false);
96+
});
97+
98+
it.skipIf(!hasDOMException)(
99+
"returns false for DOMException with non-AbortError name",
100+
() => {
101+
const error = new DOMException("fail", "NetworkError");
102+
expect(isAbortError(error)).toBe(false);
103+
},
104+
);
105+
106+
// -- Negative cases: primitives and nullish --
107+
108+
it("returns false for null", () => {
109+
expect(isAbortError(null)).toBe(false);
110+
});
111+
112+
it("returns false for undefined", () => {
113+
expect(isAbortError(undefined)).toBe(false);
114+
});
115+
116+
it("returns false for empty string", () => {
117+
expect(isAbortError("")).toBe(false);
118+
});
119+
120+
it("returns false for number", () => {
121+
expect(isAbortError(42)).toBe(false);
122+
});
123+
124+
it("returns false for boolean", () => {
125+
expect(isAbortError(false)).toBe(false);
126+
expect(isAbortError(true)).toBe(false);
127+
});
128+
129+
// -- Negative cases: regular errors --
130+
131+
it("returns false for regular Error", () => {
132+
expect(isAbortError(new Error("network error"))).toBe(false);
133+
});
134+
135+
it("returns false for TypeError", () => {
136+
expect(isAbortError(new TypeError("bad type"))).toBe(false);
137+
});
138+
139+
it("returns false for plain object without name", () => {
140+
expect(isAbortError({ message: "error" })).toBe(false);
141+
});
142+
});

0 commit comments

Comments
 (0)