Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
3396c9e
fix(activate): target title-bar commands to their click-origin instance
easonliang28 Sep 4, 2026
a143267
ci: re-trigger PR review-state labeler reconciliation (no-op commit)
easonliang28 Sep 7, 2026
723d871
fix(activate): serialize overlapping openClineInNewTab calls and hard…
easonliang28 Sep 7, 2026
c3027c8
test(activate): pin openClineInNewTab creation branches and strengthe…
easonliang28 Sep 7, 2026
00eb15f
fix(activate): harden tab creation serialization and centralize title…
easonliang28 Sep 7, 2026
ae038ab
test(activate): pin tracked tab identity against the created panel
easonliang28 Sep 7, 2026
99662bd
test(activate): pin the tracked tab panel passed to getInstanceForView
easonliang28 Sep 7, 2026
7be370a
feat(provider): persist per-view view-state identity and durable view…
easonliang28 Sep 5, 2026
8c13975
fix(provider): track in-flight view-state mutations per field and exc…
easonliang28 Sep 7, 2026
dc2e9cd
fix(provider): restore previous view-state id when registration persi…
easonliang28 Sep 7, 2026
d677e7b
fix(provider): reject invalid view-state modes and sync profile mutat…
easonliang28 Sep 8, 2026
20c3892
fix(package): move command palette entries to the commandPalette menu…
easonliang28 Sep 8, 2026
ccf0331
fix(core/webview): merge view-local state into getState for per-view …
easonliang28 Sep 6, 2026
9274d5a
fix(core/webview): purge deleted provider profile from the settings s…
easonliang28 Sep 7, 2026
6767ae1
fix(core/webview): treat already-gone profile secrets as prunable on …
easonliang28 Sep 7, 2026
6bada08
fix(webview): send stable view-state id on launch and re-pin per-view…
easonliang28 Sep 6, 2026
674be5f
fix(webview): keep view-state registration and storage writes failure…
easonliang28 Sep 7, 2026
6990e89
fix(webview): apply extension-side normalization to persisted view-st…
easonliang28 Sep 7, 2026
350ec7a
test(webview): add parallelMode spec with viewStates pruning edges an…
easonliang28 Sep 6, 2026
d64f49e
fix(webview): isolate parallel mode and provider profile writes
easonliang28 Sep 6, 2026
9a71116
fix(api): route setConfiguration through ClineProvider.setValues
easonliang28 Sep 7, 2026
1e66642
test(extension): add setValues to the api-configuration spec provider…
easonliang28 Sep 7, 2026
b80d59a
fix(webview): leave the surviving pin untouched in deleteProviderProfile
easonliang28 Sep 8, 2026
9004f5b
fix(webview): invalidate live sibling view state on reset and setting…
easonliang28 Sep 7, 2026
d1baf20
fix(api): wire task controls and sidebar-targeted configuration
easonliang28 Sep 6, 2026
0ac90ae
test(e2e): port view-state suite with reload and rehydration coverage
easonliang28 Sep 7, 2026
caa1ebc
fix(api): give startNewTask(newTab) a fresh tab panel instead of reus…
easonliang28 Sep 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions apps/vscode-e2e/fixtures/modes.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,20 @@
}
]
}
},
{
"match": {
"userMessage": "Use the `switch_mode` tool to switch to debug mode."
},
"response": {
"toolCalls": [
{
"name": "switch_mode",
"arguments": "{\"mode_slug\":\"debug\",\"reason\":\"User requested to switch to debug mode.\"}",
"id": "call_modes_switch_002"
}
]
}
}
]
}
95 changes: 95 additions & 0 deletions apps/vscode-e2e/src/fixtures/view-state.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import type { ChatCompletionRequest, ChatMessage, LLMock } from "@copilotkit/aimock"

const TASKS = ["A", "B", "C"] as const
const ROUNDS = 10

const MODE_SEQUENCES: Record<(typeof TASKS)[number], string[]> = {
A: ["ask", "debug", "architect", "orchestrator", "code", "ask", "debug", "architect", "orchestrator", "code"],
B: ["debug", "architect", "orchestrator", "code", "ask", "debug", "architect", "orchestrator", "code", "ask"],
C: ["architect", "orchestrator", "code", "ask", "debug", "architect", "orchestrator", "code", "ask", "debug"],
}

const markerFor = (taskName: (typeof TASKS)[number]) => `FOLLOWUP_MODE_ISOLATION_${taskName}`
const answerFor = (taskName: (typeof TASKS)[number], round: number) => `${taskName} follow-up round ${round}`
const callIdFor = (taskName: (typeof TASKS)[number], round: number) =>
`call_followup_mode_${taskName.toLowerCase()}_${String(round).padStart(2, "0")}`

const lastToolResultContains = (req: ChatCompletionRequest, toolCallId: string, expected: string[]) => {
const messages = Array.isArray(req?.messages) ? req.messages : []
const toolMessage = messages.filter((message: ChatMessage) => message?.role === "tool").at(-1)
const content = toolMessage?.content

return (
toolMessage?.tool_call_id === toolCallId &&
typeof content === "string" &&
expected.every((text) => content.includes(text))
)
}

const followupToolCall = (taskName: (typeof TASKS)[number], round: number) => ({
name: "ask_followup_question",
arguments: JSON.stringify({
question: `Task ${taskName}: choose mode for round ${round}`,
follow_up: [
{
text: answerFor(taskName, round),
mode: MODE_SEQUENCES[taskName][round - 1],
},
],
}),
id: callIdFor(taskName, round),
})

export const getFollowupModeIsolationPlan = () =>
TASKS.map((taskName) => ({
taskName,
marker: markerFor(taskName),
rounds: MODE_SEQUENCES[taskName].map((mode, index) => ({
round: index + 1,
answer: answerFor(taskName, index + 1),
mode,
})),
}))

export function addViewStateFixtures(mock: InstanceType<typeof LLMock>) {
for (const taskName of TASKS) {
mock.addFixture({
match: {
userMessage: markerFor(taskName),
},
response: {
toolCalls: [followupToolCall(taskName, 1)],
},
})

for (let round = 1; round < ROUNDS; round++) {
mock.addFixture({
match: {
predicate: (req) =>
lastToolResultContains(req, callIdFor(taskName, round), [answerFor(taskName, round)]),
},
response: {
toolCalls: [followupToolCall(taskName, round + 1)],
},
})
}

mock.addFixture({
match: {
predicate: (req) =>
lastToolResultContains(req, callIdFor(taskName, ROUNDS), [answerFor(taskName, ROUNDS)]),
},
response: {
toolCalls: [
{
name: "attempt_completion",
arguments: JSON.stringify({
result: `Task ${taskName} completed ${ROUNDS} follow-up mode switches.`,
}),
id: `call_followup_mode_${taskName.toLowerCase()}_complete`,
},
],
},
})
}
}
37 changes: 37 additions & 0 deletions apps/vscode-e2e/src/runTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import { addUseMcpToolResultFixtures } from "./fixtures/use-mcp-tool"
import { addWriteToFileResultFixtures } from "./fixtures/write-to-file"
import { createScenarioWorkspace, removeScenarioWorkspace } from "./restart/scenarioWorkspace"
import { runRestartScenario } from "./restart/vscodeCoordinator"
import { toolResultContains } from "./fixtures/tool-result"
import { addViewStateFixtures } from "./fixtures/view-state"

function getCliFlagValue(flag: string) {
return process.argv.find((arg, index) => process.argv[index - 1] === flag)
Expand Down Expand Up @@ -143,6 +145,41 @@ async function main() {
addUseMcpToolResultFixtures(mock)
addWriteToFileResultFixtures(mock)
addDeepSeekV4Fixtures(mock)
addViewStateFixtures(mock)

// Model-agnostic predicate fixtures for the view-state suite's post-switch
// turns. They coexist with the legacy model-scoped regex fixture below
// (shared response id call_modes_post_switch_001) so the modes suite keeps
// its OpenRouter-scoped match while view-state runs under any default model.
mock.addFixture({
match: {
predicate: (req) => toolResultContains(req, "call_modes_switch_001", []),
},
response: {
toolCalls: [
{
name: "attempt_completion",
arguments: JSON.stringify({ result: "Switched to ❓ Ask mode as requested." }),
id: "call_modes_post_switch_001",
},
],
},
})

mock.addFixture({
match: {
predicate: (req) => toolResultContains(req, "call_modes_switch_002", []),
},
response: {
toolCalls: [
{
name: "attempt_completion",
arguments: JSON.stringify({ result: "Switched to 🪲 Debug mode as requested." }),
id: "call_modes_post_switch_002",
},
],
},
})

// The modes test (switch_mode → ask) triggers a second API call whose last
// user message starts with <environment_details> directly — no <user_message>
Expand Down
Loading
Loading