Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
7b163b1
feat(webview): add view-local state base
easonliang28 Jul 21, 2026
0e63667
fix(webview): persist per-view selections through registered global s…
easonliang28 Jul 21, 2026
a21c8a9
fix(webview): route mode switches through view-local persistence
easonliang28 Jul 21, 2026
19fd13e
chore: remove invisible chars
easonliang28 Jul 21, 2026
41d575e
test(webview): restore ClineProvider parallel mode coverage
easonliang28 Jul 21, 2026
287ed8d
fix(webview): sync view local state after profile mutations
easonliang28 Jul 23, 2026
c3199d3
fix(webview): persist view-local state safely
easonliang28 Jul 23, 2026
ae21d23
fix(provider): sync view-local state when activating provider profile
easonliang28 Jul 21, 2026
7268a67
fix(api): sync setConfiguration view-local state
easonliang28 Jul 24, 2026
bee18af
test(strengthen): assert stale openRouterModelId absent after profile…
easonliang28 Jul 24, 2026
495e3e9
fix(webview): preserve isolated view state writes
easonliang28 Jul 27, 2026
d8fd51e
chore(lint): update eslint suppression baseline
easonliang28 Jul 27, 2026
1024c03
refactor(webview): consolidate view-local state persistence
easonliang28 Jul 29, 2026
96d4d70
test(webview): mock workspace tracker launch init
easonliang28 Jul 29, 2026
b255a25
test(vscode-e2e): cover cross-panel view state isolation
easonliang28 Jul 30, 2026
bc6f4b4
test(vscode-e2e): cover follow-up mode isolation
easonliang28 Jul 30, 2026
b479a78
test(api): cover task controls and view-local values
easonliang28 Jul 30, 2026
aa4236d
fix(webview): address view-state review feedback
easonliang28 Jul 30, 2026
ae112c3
test: fix api configuration coverage failures
easonliang28 Aug 7, 2026
62682df
test(vscode-e2e): poll for persisted view state entries before asserting
easonLiangWorldedtech Aug 19, 2026
7c468f4
docs: add docstrings for per-view state and task-control APIs
easonLiangWorldedtech Aug 19, 2026
c74222a
fix(webview): scope follow-up mode switch to target task and strip ne…
easonliang28 Aug 23, 2026
0fc8126
chore: ignore .husky/_ shim directory and remove committed shims
easonliang28 Aug 28, 2026
a6b5413
fix(webview): harden durable per-view state across review findings
easonliang28 Aug 28, 2026
327c356
fix(api): guard task registry identity and keep follow-up answers rel…
easonliang28 Aug 28, 2026
0cf997e
fix(webview): repair invalid view pins without clobbering the global …
easonliang28 Aug 28, 2026
c518d72
test(e2e): derive view-state rounds from the fixture and document sha…
easonliang28 Aug 28, 2026
1654cda
fix(webview): remove remaining as-any casts from the launch test suite
easonliang28 Aug 28, 2026
8d656ef
fix(extension): drop the remaining as-any casts in the set-configurat…
easonliang28 Aug 28, 2026
4b2c9ec
fix(webview): re-key pre-launch view-state writes to the stable view id
easonliang28 Aug 28, 2026
a7d3460
test(webview): remove the no-explicit-any suppression from the parall…
easonliang28 Aug 28, 2026
f44349a
fix(webview): validate mode slugs and unify the mode-change paths
easonliang28 Aug 29, 2026
fab50e0
chore: temporary diagnostics for the flaky e2e viewStates read
easonliang28 Aug 29, 2026
8ad5cdc
test(e2e): drop viewStates diagnostics, give the poll a 30s budget
easonliang28 Aug 29, 2026
f5b080a
fix(extension): restore specs damaged during rebase
easonliang28 Sep 2, 2026
7222122
fix(extension): dispatch webview postMessage without awaiting the ren…
easonliang28 Sep 2, 2026
68b0356
fix: address CodeRabbit review findings on view-local state
easonliang28 Sep 2, 2026
3cea125
fix(tests): adapt mode-switch specs to provider-owned task mode write
easonliang28 Sep 2, 2026
beb70ac
ci: re-trigger CI after flaky Windows runner failure
easonliang28 Sep 2, 2026
8736a7f
ci: re-trigger CI after flaky extension-host-visual settle
easonliang28 Sep 2, 2026
f6d8b5b
Merge branch 'main' into feat/view-local-state-base
edelauna Sep 3, 2026
1609ba8
fix(webview): settle color transitions during visual theme swaps
easonliang28 Sep 4, 2026
98615c2
test(webview): kill surviving viewStateId mutants; document equivalen…
easonliang28 Sep 4, 2026
e9a44b2
fix(webview): address review findings and harden viewStateId mutation…
easonliang28 Sep 4, 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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,6 @@ qdrant_storage/
plans/

roo-cli-*.tar.gz*

# Husky-generated hook shims are machine-local (created by husky/git-lfs install)
.husky/_/
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