Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
bf30669
fix(activate): target title-bar commands to their click-origin instance
easonliang28 Sep 4, 2026
854af59
fix(activate): serialize overlapping openClineInNewTab calls and hard…
easonliang28 Sep 7, 2026
cbf0edb
test(activate): pin openClineInNewTab creation branches and strengthe…
easonliang28 Sep 7, 2026
6cc68ef
fix(activate): harden tab creation serialization and centralize title…
easonliang28 Sep 7, 2026
d7453eb
test(activate): pin tracked tab identity against the created panel
easonliang28 Sep 7, 2026
199d072
test(activate): pin the tracked tab panel passed to getInstanceForView
easonliang28 Sep 7, 2026
c1bf947
feat(provider): persist per-view view-state identity and durable view…
easonliang28 Sep 5, 2026
1e952c5
fix(provider): track in-flight view-state mutations per field and exc…
easonliang28 Sep 7, 2026
b16a9e5
fix(provider): restore previous view-state id when registration persi…
easonliang28 Sep 7, 2026
a3c6064
fix(provider): reject invalid view-state modes and sync profile mutat…
easonliang28 Sep 8, 2026
3aec562
fix(package): move command palette entries to the commandPalette menu…
easonliang28 Sep 8, 2026
016c221
fix(webview): sync view-local state with mode and profile mutations a…
easonliang28 Sep 8, 2026
2573858
fix(provider): close mode-switch abort window, drop bracket access, c…
easonliang28 Sep 14, 2026
8d13c64
test(provider): cover abort and pinned profile deletion guards
easonliang28 Sep 14, 2026
2ba6240
test(provider): assert profile settings write identity
easonliang28 Sep 14, 2026
56d797c
fix(provider): make mode switch cancellation atomic and preserve view…
easonliang28 Sep 15, 2026
6c9856d
fix(core/webview): purge deleted provider profile from the settings s…
easonliang28 Sep 15, 2026
67dbd22
test: replace double assertions with single structural casts in view-…
easonliang28 Sep 27, 2026
1004cbd
fix(webview): contain the abort rollback failure in handleModeSwitch
easonliang28 Sep 27, 2026
dfb8264
fix(webview): restore only the mode field when rolling back an aborte…
easonliang28 Sep 27, 2026
ce8b276
fix(activate): drop the CodeIndexManager lookup left by the rebase
Oct 5, 2026
57dee56
fix(webview): copy the persisted viewStates map onto a null prototype
Oct 6, 2026
5f89348
fix(webview): re-key only the entry this instance wrote
Oct 6, 2026
7804876
fix(webview): do not bind a restored profile to the shared mode slot
Oct 6, 2026
66a540e
fix(lint): correct the recorded suppression count for the sticky-mode…
Oct 6, 2026
2804273
fix(webview): keep re-key authorship until the write succeeds
Oct 6, 2026
399e56e
fix(webview): do not load a temporary-id entry authored by another se…
Oct 6, 2026
e6add2f
Merge upstream/main (9c069e94ef) into vps2-f1a-rb
easonliang28 Oct 12, 2026
3643cea
fix(webview): claim unowned temp view-state slot and roll back shared…
easonliang28 Oct 12, 2026
29f4078
fix(webview): validate the webview-supplied view state id before adop…
easonliang28 Oct 13, 2026
df73920
fix(webview): refresh pinned views on non-activating saves
easonliang28 Oct 13, 2026
f78bc04
fix(webview): rebuild active task handlers when a pinned profile is s…
easonliang28 Oct 13, 2026
836e94a
test: fix describe nesting, strengthen prune assertion, make stale-entry
easonliang28 Oct 13, 2026
7188324
fix: rebuild re-pinned siblings' tasks on profile deletion
easonLiangWorldedtech Oct 13, 2026
8b77195
fix(webview): scope profile-save task rebuilds to the task's own profile
easonliang28 Oct 13, 2026
6b1322a
fix(webview): gate deleted-profile re-pin task writes on the task's o…
easonliang28 Oct 13, 2026
9c932a6
Merge upstream main 58341d882 into vps2/f1a-view-identity
easonliang28 Oct 14, 2026
06238d7
fix: clear newly-unused bindings surfaced by main unused-vars re-enable
easonliang28 Oct 14, 2026
8c7519e
chore: drop Stryker suppression comments per AGENTS.md policy
easonliang28 Oct 14, 2026
2f9ae13
test: await serialized view-state writes and global-state seeds deter…
easonliang28 Oct 14, 2026
2faae82
Merge upstream main into vps2/f1a-view-identity (row-9 refresh)
easonliang28 Oct 17, 2026
a305ee6
fix(webview): log synchronous postMessage throws in postMessageToWebview
easonliang28 Oct 17, 2026
e09014e
Merge remote-tracking branch 'upstream/main' into vps2/f1a-view-identity
easonLiangWorldedtech Oct 22, 2026
0e00fc7
chore(knip): mark the test-only getPanel seam @internal
easonLiangWorldedtech Oct 22, 2026
1b6c54b
fix(provider): bind durable view-states entries to a host-minted clai…
easonLiangWorldedtech Oct 22, 2026
57f8707
fix(webview): replace the buffered provider settings on profile activ…
easonLiangWorldedtech Oct 22, 2026
83f66a1
fix(webview): refuse a view-state claim bound by a concurrent registr…
easonLiangWorldedtech Oct 22, 2026
6cf4c3b
test(webview): use bracket access for the private write queue in the …
easonLiangWorldedtech Oct 22, 2026
e0c72c4
test(webview): cover the concurrent-bind refusal through setViewStateId
easonLiangWorldedtech Oct 22, 2026
d3003db
test(webview): use bracket access for the private write queue in the …
easonLiangWorldedtech Oct 22, 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
43 changes: 43 additions & 0 deletions packages/types/src/__tests__/global-settings.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,49 @@ describe("alwaysDenyUnapprovedCommands global setting", () => {
})
})

describe("viewStates global setting", () => {
it("parses a valid per-view record", () => {
const parsed = globalSettingsSchema.parse({
viewStates: {
"stable-view-1": { mode: "code", currentApiConfigName: "profile-a", updatedAt: 1730000000000 },
"stable-view-2": {},
},
})
expect(parsed.viewStates?.["stable-view-1"]).toEqual({
mode: "code",
currentApiConfigName: "profile-a",
updatedAt: 1730000000000,
})
expect(parsed.viewStates?.["stable-view-2"]).toEqual({})
expect(GLOBAL_SETTINGS_KEYS).toContain("viewStates")
})

it("parses an entry that carries the host-minted claim token", () => {
// The claim binds the entry to the webview that wrote it; a legacy entry without one
// still parses, because refusing it is setViewStateId's job, not the schema's.
const parsed = globalSettingsSchema.parse({
viewStates: {
"stable-view-1": { mode: "code", updatedAt: 1730000000000, claimToken: "7f9c-host-minted" },
"stable-view-2": { mode: "code" },
},
})
expect(parsed.viewStates?.["stable-view-1"]?.claimToken).toBe("7f9c-host-minted")
expect(parsed.viewStates?.["stable-view-2"]).toEqual({ mode: "code" })
})

it("rejects invalid field values inside a view entry", () => {
expect(() => globalSettingsSchema.parse({ viewStates: { v: { mode: 42 } } })).toThrow()
expect(() => globalSettingsSchema.parse({ viewStates: { v: { currentApiConfigName: 42 } } })).toThrow()
expect(() => globalSettingsSchema.parse({ viewStates: { v: { updatedAt: "soon" } } })).toThrow()
expect(() => globalSettingsSchema.parse({ viewStates: { v: { claimToken: 42 } } })).toThrow()
})

it("rejects non-object viewStates values", () => {
expect(() => globalSettingsSchema.parse({ viewStates: "not-a-record" })).toThrow()
expect(() => globalSettingsSchema.parse({ viewStates: { v: "not-an-entry" } })).toThrow()
})
})

describe("collapseCommandOutputByDefault global setting", () => {
it.each([true, false, undefined])("round-trips %s through the export/import schema", (value) => {
const settings = globalSettingsSchema.parse({ collapseCommandOutputByDefault: value })
Expand Down
5 changes: 5 additions & 0 deletions packages/types/src/__tests__/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
import { GLOBAL_STATE_KEYS } from "../index.js"

describe("GLOBAL_STATE_KEYS", () => {
it("should contain registered durable per-view state", () => {
expect(GLOBAL_STATE_KEYS).toContain("viewStates")
})

it("should contain provider settings keys", () => {
expect(GLOBAL_STATE_KEYS).toContain("autoApprovalEnabled")
})
Expand All @@ -13,6 +17,7 @@ describe("GLOBAL_STATE_KEYS", () => {

it("should not contain secret state keys", () => {
expect(GLOBAL_STATE_KEYS).not.toContain("openRouterApiKey")
expect(GLOBAL_STATE_KEYS).not.toContain("apiKey")
})

it("should contain OpenAI Compatible base URL setting", () => {
Expand Down
17 changes: 17 additions & 0 deletions packages/types/src/global-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,22 @@ export const MAX_CHECKPOINT_TIMEOUT_SECONDS = 60
*/
export const DEFAULT_CHECKPOINT_TIMEOUT_SECONDS = 15

/**
* Persisted non-secret selections for a stable webview instance.
*/
export const viewStateSchema = z.object({
mode: z.string().optional(),
currentApiConfigName: z.string().optional(),
updatedAt: z.number().optional(),
/**
* Host-minted capability token binding this entry to the webview that wrote it.
* Minted by the extension host (`crypto.randomUUID()`), delivered to that webview and
* never accepted from webview input. An entry is only re-adopted for a webview that
* presents this token, so a view cannot read another (e.g. closed) view's selections.
*/
claimToken: z.string().optional(),
})

/**
* GlobalSettings
*/
Expand All @@ -122,6 +138,7 @@ export const globalSettingsSchema = z.object({
currentApiConfigName: z.string().optional(),
listApiConfigMeta: z.array(providerSettingsEntrySchema).optional(),
pinnedApiConfigs: z.record(z.string(), z.boolean()).optional(),
viewStates: z.record(z.string(), viewStateSchema).optional(),

lastShownAnnouncementId: z.string().optional(),
customInstructions: z.string().optional(),
Expand Down
15 changes: 15 additions & 0 deletions packages/types/src/vscode-extension-host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ export interface ExtensionMessage {
| "originalContent"
| "rooHistoryImportProgress"
| "themeFixtureProbeRequest"
| "viewStateClaim"
text?: string
/** For fileContent: { path, content, error? } */
fileContent?: { path: string; content: string | null; error?: string }
Expand Down Expand Up @@ -169,6 +170,13 @@ export interface ExtensionMessage {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
values?: Record<string, any>
requestId?: string
/**
* For `viewStateClaim`: the durable `viewStates` key the delivered claim governs. The
* message is only ever sent to the webview that key belongs to.
*/
viewStateId?: string
/** For `viewStateClaim`: the host-minted token the webview stores and echoes on launch. */
viewStateClaim?: string
themeFixture?: WebviewThemeFixture
promptText?: string
results?:
Expand Down Expand Up @@ -669,6 +677,13 @@ export interface WebviewMessage {
| "openRulesDirectory"
| "themeFixtureProbeResponse"
text?: string
viewStateId?: string
/**
* For `webviewDidLaunch`: the host-minted claim previously delivered for `viewStateId`.
* The webview only echoes what it was given; the host rejects an id whose stored claim
* does not match, so a view cannot adopt another view's persisted selections.
*/
viewStateClaim?: string
taskId?: string
editedMessageContent?: string
tab?: "settings" | "history" | "mcp" | "modes" | "chat" | "marketplace" | "cloud"
Expand Down
8 changes: 8 additions & 0 deletions packages/types/src/vscode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ export const commandIds = [
"popoutButtonClicked",
"settingsButtonClicked",

// Editor-tab (popped-out) surface variants of the title-bar buttons. The
// shared ids above target the sidebar click origin, so the tab surface
// needs its own ids (see registerCommands.ts getTabProvider).
"plusButtonClickedInTab",
"settingsButtonClickedInTab",
"marketplaceButtonClickedInTab",
"historyButtonClickedInTab",

"openInNewTab",

"newTask",
Expand Down
Loading
Loading