Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
884386b
feat(webview): add view-local state base
easonliang28 Jul 21, 2026
d51085c
fix(webview): persist per-view selections through registered global s…
easonliang28 Jul 21, 2026
4510292
fix(webview): route mode switches through view-local persistence
easonliang28 Jul 21, 2026
7408bd0
chore: remove invisible chars
easonliang28 Jul 21, 2026
9554678
test(webview): restore ClineProvider parallel mode coverage
easonliang28 Jul 21, 2026
ae8fb5f
fix(webview): sync view local state after profile mutations
easonliang28 Jul 23, 2026
858dc83
fix(webview): persist view-local state safely
easonliang28 Jul 23, 2026
260a84d
fix(provider): sync view-local state when activating provider profile
easonliang28 Jul 21, 2026
264c45d
fix(api): sync setConfiguration view-local state
easonliang28 Jul 24, 2026
c4ba3f5
test(strengthen): assert stale openRouterModelId absent after profile…
easonliang28 Jul 24, 2026
c2c30b5
fix(webview): preserve isolated view state writes
easonliang28 Jul 27, 2026
78ddd24
chore(lint): update eslint suppression baseline
easonliang28 Jul 27, 2026
913c1c7
refactor(webview): consolidate view-local state persistence
easonliang28 Jul 29, 2026
43c283f
test(webview): mock workspace tracker launch init
easonliang28 Jul 29, 2026
4f49f31
test(vscode-e2e): cover cross-panel view state isolation
easonliang28 Jul 30, 2026
499611f
test(vscode-e2e): cover follow-up mode isolation
easonliang28 Jul 30, 2026
1a6b4c8
test(api): cover task controls and view-local values
easonliang28 Jul 30, 2026
1b7b1ce
fix(webview): address view-state review feedback
easonliang28 Jul 30, 2026
5f81e73
test: fix api configuration coverage failures
easonliang28 Aug 7, 2026
25b403f
test(vscode-e2e): poll for persisted view state entries before asserting
easonLiangWorldedtech Aug 19, 2026
14ea1fe
docs: add docstrings for per-view state and task-control APIs
easonLiangWorldedtech Aug 19, 2026
6d91575
Merge remote-tracking branch 'upstream/main' into pr-977-fix
easonliang28 Aug 23, 2026
045c26f
fix(webview): scope follow-up mode switch to target task and strip ne…
easonliang28 Aug 23, 2026
1e0a19a
chore: ignore .husky/_ shim directory and remove committed shims
easonliang28 Aug 28, 2026
3dd58e3
fix(webview): harden durable per-view state across review findings
easonliang28 Aug 28, 2026
cb99afc
fix(api): guard task registry identity and keep follow-up answers rel…
easonliang28 Aug 28, 2026
94a9aef
fix(webview): repair invalid view pins without clobbering the global …
easonliang28 Aug 28, 2026
8b8ebe6
test(e2e): derive view-state rounds from the fixture and document sha…
easonliang28 Aug 28, 2026
9c1102d
fix(webview): remove remaining as-any casts from the launch test suite
easonliang28 Aug 28, 2026
c7619f3
fix(extension): drop the remaining as-any casts in the set-configurat…
easonliang28 Aug 28, 2026
8c86718
fix(webview): re-key pre-launch view-state writes to the stable view id
easonliang28 Aug 28, 2026
f91e19c
test(webview): remove the no-explicit-any suppression from the parall…
easonliang28 Aug 28, 2026
7efbf33
merge: file-write-safety trial — include PR #977 (durable per-view st…
easonliang28 Aug 28, 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 @@ -55,3 +55,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