Skip to content

Commit b921c41

Browse files
authored
Merge branch 'main' into main
2 parents 1eed734 + 25545e9 commit b921c41

56 files changed

Lines changed: 997 additions & 302 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/vscode-e2e/fixtures/task-hello-world.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22
"fixtures": [
33
{
44
"match": {
5-
"userMessage": "Hello world, what is your name? Respond with 'My name is ...'"
5+
"userMessage": "TASK_HELLO_WORLD_SMOKE"
66
},
77
"response": {
88
"toolCalls": [
99
{
1010
"name": "attempt_completion",
11-
"arguments": "{\"result\":\"My name is Zoo! I'm your AI coding assistant, here to help you with development tasks.\"}",
11+
"arguments": "{\"result\":\"My name is Zoo.\"}",
1212
"id": "call_task_hello_world_001"
1313
}
1414
]

apps/vscode-e2e/src/fixtures/subtasks.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,9 +177,13 @@ export function addSubtaskFixtures(mock: InstanceType<typeof LLMock>) {
177177
},
178178
})
179179

180+
// Issue #561: parent prompt embeds SAME_CHILD_MARKER verbatim, so parent-resume turns
181+
// also match a bare substring check. Exclude the parent marker to let them fall through.
180182
mock.addFixture({
181183
match: {
182-
userMessage: new RegExp(SUBTASK_XPROFILE_SAME_CHILD_MARKER),
184+
predicate: (req) =>
185+
requestContains(req, [SUBTASK_XPROFILE_SAME_CHILD_MARKER]) &&
186+
!requestContains(req, [SUBTASK_XPROFILE_PARENT_MARKER]),
183187
},
184188
response: {
185189
toolCalls: [
@@ -212,6 +216,8 @@ export function addSubtaskFixtures(mock: InstanceType<typeof LLMock>) {
212216
},
213217
})
214218

219+
// Safe as bare regex: DIFFERENT_CHILD_MARKER is NOT embedded in SUBTASK_XPROFILE_PARENT_PROMPT,
220+
// so parent-resume turns never contain it. If that ever changes, add an exclusion predicate.
215221
mock.addFixture({
216222
match: {
217223
userMessage: new RegExp(SUBTASK_XPROFILE_DIFFERENT_CHILD_MARKER),

apps/vscode-e2e/src/suite/task.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ suite("Roo Code Task", function () {
2121

2222
const taskId = await api.startNewTask({
2323
configuration: { mode: "ask", alwaysAllowModeSwitch: true, autoApprovalEnabled: true },
24-
text: "Hello world, what is your name? Respond with 'My name is ...'",
24+
text: "TASK_HELLO_WORLD_SMOKE: what is your name?",
2525
})
2626

2727
await waitUntilCompleted({ api, taskId })

knip.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"@types/node-cache",
2222
"@types/vscode",
2323
"@vscode/codicons",
24+
"@vscode/ripgrep",
2425
"esbuild-wasm",
2526
"sambanova-ai-provider",
2627
"tree-sitter-wasms",

packages/types/src/providers/openai.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,32 @@ export const openAiNativeModels = {
2424
description:
2525
"GPT-5.1 Codex Max: Our most intelligent coding model optimized for long-horizon, agentic coding tasks",
2626
},
27+
"gpt-5.5": {
28+
maxTokens: 128000,
29+
contextWindow: 1_050_000,
30+
includedTools: ["apply_patch"],
31+
excludedTools: ["apply_diff", "write_to_file"],
32+
supportsImages: true,
33+
supportsPromptCache: true,
34+
supportsReasoningEffort: ["none", "low", "medium", "high", "xhigh"],
35+
reasoningEffort: "medium",
36+
inputPrice: 5.0,
37+
outputPrice: 30.0,
38+
cacheReadsPrice: 0.5,
39+
longContextPricing: {
40+
thresholdTokens: 272_000,
41+
inputPriceMultiplier: 2,
42+
outputPriceMultiplier: 1.5,
43+
appliesToServiceTiers: ["default", "flex"],
44+
},
45+
supportsVerbosity: true,
46+
supportsTemperature: false,
47+
tiers: [
48+
{ name: "flex", contextWindow: 1_050_000, inputPrice: 2.5, outputPrice: 15.0, cacheReadsPrice: 0.25 },
49+
{ name: "priority", contextWindow: 1_050_000, inputPrice: 12.5, outputPrice: 75.0, cacheReadsPrice: 1.25 },
50+
],
51+
description: "GPT-5.5: A new class of intelligence for coding and professional work",
52+
},
2753
"gpt-5.4": {
2854
maxTokens: 128000,
2955
contextWindow: 1_050_000,

packages/types/src/vscode.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ export const commandIds = [
4646
"acceptInput",
4747
"focusPanel",
4848
"toggleAutoApprove",
49+
50+
"showRipgrepDiagnostic",
4951
] as const
5052

5153
export type CommandId = (typeof commandIds)[number]

src/activate/__tests__/registerCommands.spec.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,10 @@ vi.mock("../../i18n", () => ({
8181
t: (key: string) => key,
8282
}))
8383

84+
vi.mock("../../services/ripgrep/diagnostic", () => ({
85+
registerRipgrepDiagnosticCommand: vi.fn().mockReturnValue({ dispose: vi.fn() }),
86+
}))
87+
8488
describe("getVisibleProviderOrLog", () => {
8589
let mockOutputChannel: vscode.OutputChannel
8690

@@ -172,6 +176,14 @@ describe("registerCommands handlers", () => {
172176
setPanel(undefined, "tab")
173177
})
174178

179+
it("registers the ripgrep diagnostic command and stores its disposable in context.subscriptions", async () => {
180+
const { registerRipgrepDiagnosticCommand } = await import("../../services/ripgrep/diagnostic")
181+
const mock = vi.mocked(registerRipgrepDiagnosticCommand)
182+
const disposable = mock.mock.results[0]?.value
183+
expect(mock).toHaveBeenCalled()
184+
expect(mockContext.subscriptions).toContain(disposable)
185+
})
186+
175187
it("settingsButtonClicked posts both settingsButtonClicked and didBecomeVisible actions", () => {
176188
handlers["zoo-code.settingsButtonClicked"]()
177189

src/activate/registerCommands.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { handleNewTask } from "./handleTask"
1313
import { CodeIndexManager } from "../services/code-index/manager"
1414
import { importSettingsWithFeedback } from "../core/config/importExport"
1515
import { MdmService } from "../services/mdm/MdmService"
16+
import { registerRipgrepDiagnosticCommand } from "../services/ripgrep/diagnostic"
1617
import { t } from "../i18n"
1718

1819
/**
@@ -68,9 +69,27 @@ export const registerCommands = (options: RegisterCommandOptions) => {
6869
const command = getCommand(id as CommandId)
6970
context.subscriptions.push(vscode.commands.registerCommand(command, callback))
7071
}
72+
73+
context.subscriptions.push(registerRipgrepDiagnosticCommand())
7174
}
7275

73-
const getCommandsMap = ({ context, outputChannel, provider }: RegisterCommandOptions): Record<CommandId, any> => ({
76+
// `showRipgrepDiagnostic` is registered separately by
77+
// `registerRipgrepDiagnosticCommand` (above), which owns the OutputChannel
78+
// lifecycle alongside the command registration, so it's intentionally
79+
// excluded from this map.
80+
//
81+
// Callback shape mirrors VS Code's own `commands.registerCommand` signature
82+
// (`(...args: any[]) => any`), with the return narrowed to `unknown` so
83+
// callers must inspect before using. `any[]` for args is unavoidable: the
84+
// callbacks here are heterogeneous (`importSettings` takes an optional
85+
// `filePath?: string`, others take none) and VS Code dispatches positional
86+
// args dynamically.
87+
type CommandCallback = (...args: any[]) => unknown
88+
const getCommandsMap = ({
89+
context,
90+
outputChannel,
91+
provider,
92+
}: RegisterCommandOptions): Record<Exclude<CommandId, "showRipgrepDiagnostic">, CommandCallback> => ({
7493
activationCompleted: () => {},
7594
plusButtonClicked: async () => {
7695
const visibleProvider = getVisibleProviderOrLog(outputChannel)

src/api/providers/__tests__/openai-native.spec.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,21 @@ describe("OpenAiNativeHandler", () => {
267267
expect(modelInfo.info.supportsReasoningEffort).toEqual(["low", "medium", "high", "xhigh"])
268268
})
269269

270+
it("should return GPT-5.5 model info when selected", () => {
271+
const gpt55Handler = new OpenAiNativeHandler({
272+
...mockOptions,
273+
apiModelId: "gpt-5.5",
274+
})
275+
276+
const modelInfo = gpt55Handler.getModel()
277+
expect(modelInfo.id).toBe("gpt-5.5")
278+
expect(modelInfo.info.maxTokens).toBe(128000)
279+
expect(modelInfo.info.contextWindow).toBe(1_050_000)
280+
expect(modelInfo.info.supportsVerbosity).toBe(true)
281+
expect(modelInfo.info.supportsReasoningEffort).toEqual(["none", "low", "medium", "high", "xhigh"])
282+
expect(modelInfo.info.reasoningEffort).toBe("medium")
283+
})
284+
270285
it("should return GPT-5.4 model info when selected", () => {
271286
const gpt54Handler = new OpenAiNativeHandler({
272287
...mockOptions,
@@ -430,6 +445,56 @@ describe("OpenAiNativeHandler", () => {
430445
expect(textChunks[1].text).toBe(" world")
431446
})
432447

448+
it("should handle GPT-5.5 model with Responses API", async () => {
449+
const mockFetch = vitest.fn().mockResolvedValue({
450+
ok: true,
451+
body: new ReadableStream({
452+
start(controller) {
453+
controller.enqueue(
454+
new TextEncoder().encode(
455+
'data: {"type":"response.output_item.added","item":{"type":"text","text":"GPT-5.5 reply"}}\n\n',
456+
),
457+
)
458+
controller.enqueue(new TextEncoder().encode("data: [DONE]\n\n"))
459+
controller.close()
460+
},
461+
}),
462+
})
463+
global.fetch = mockFetch as any
464+
465+
mockResponsesCreate.mockRejectedValue(new Error("SDK not available"))
466+
467+
handler = new OpenAiNativeHandler({
468+
...mockOptions,
469+
apiModelId: "gpt-5.5",
470+
})
471+
472+
const stream = handler.createMessage(systemPrompt, messages)
473+
const chunks: any[] = []
474+
for await (const chunk of stream) {
475+
chunks.push(chunk)
476+
}
477+
478+
expect(mockFetch).toHaveBeenCalledWith(
479+
"https://api.openai.com/v1/responses",
480+
expect.objectContaining({
481+
body: expect.any(String),
482+
}),
483+
)
484+
const body = (mockFetch.mock.calls[0][1] as any).body as string
485+
const parsedBody = JSON.parse(body)
486+
expect(parsedBody.model).toBe("gpt-5.5")
487+
expect(parsedBody.max_output_tokens).toBe(128000)
488+
expect(parsedBody.temperature).toBeUndefined()
489+
expect(parsedBody.include).toEqual(["reasoning.encrypted_content"])
490+
expect(parsedBody.reasoning?.effort).toBe("medium")
491+
expect(parsedBody.text?.verbosity).toBe("medium")
492+
493+
const textChunks = chunks.filter((chunk) => chunk.type === "text")
494+
expect(textChunks).toHaveLength(1)
495+
expect(textChunks[0].text).toBe("GPT-5.5 reply")
496+
})
497+
433498
it("should handle GPT-5.4 model with Responses API", async () => {
434499
const mockFetch = vitest.fn().mockResolvedValue({
435500
ok: true,

src/esbuild.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ async function main() {
126126
// global-agent must be external because it dynamically patches Node.js http/https modules
127127
// which breaks when bundled. It needs access to the actual Node.js module instances.
128128
// undici must be bundled because our VSIX is packaged with `--no-dependencies`.
129-
external: ["vscode", "esbuild", "global-agent"],
129+
external: ["vscode", "esbuild", "global-agent", "@vscode/ripgrep"],
130130
}
131131

132132
/**

0 commit comments

Comments
 (0)