Skip to content

feat(webview): add viewStateId generation and persistence infrastructure - #1183

Draft
easonLiangWorldedtech wants to merge 1 commit into
Zoo-Code-Org:mainfrom
easonLiangWorldedtech:feat/view-local-state-base-1
Draft

feat(webview): add viewStateId generation and persistence infrastructure#1183
easonLiangWorldedtech wants to merge 1 commit into
Zoo-Code-Org:mainfrom
easonLiangWorldedtech:feat/view-local-state-base-1

Conversation

@easonLiangWorldedtech

@easonLiangWorldedtech easonLiangWorldedtech commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Generate and persist a unique viewStateId for each webview instance, establishing the foundation infrastructure for per-view state. This is the first step of the entire per-view isolation architecture.

Changes

  • Export VSCodeAPIWrapper class — Expose getViewStateId() method for external use
  • Generate unique viewStateId — Use crypto.randomUUID() with fallback mechanism
  • Persist in localStorage — Persist viewStateId in dev server environment (dev server compatibility)
  • Add viewStateSchema — Add type-safe schema in global-settings.ts
  • webviewDidLaunch handshake — Automatically send viewStateId during webview launch

Files Changed (7 files, +559 / -124)

File Change
src/core/webview/ClineProvider.ts +511/-124 — Core logic: ID generation, persistence, handshake
webview-ui/src/utils/vscode.ts +58/-7 — VSCodeAPIWrapper export + getViewStateId()
webview-ui/src/context/ExtensionStateContext.tsx +5/-1 — Integrate viewStateId context
packages/types/src/global-settings.ts +10 — viewStateSchema
webview-ui/src/utils/__tests__/vscode.spec.ts +89 — VSCode utility tests
packages/types/src/__tests__/index.test.ts +5 — Type validation
src/eslint-suppressions.json -5 — Remove obsolete suppressions

Test Notes

  • base-1 alone: Tests will fail because base-2 provides the setValues() method that base-1's persistence logic depends on. The infrastructure is in place but not yet wired to actual state updates.
  • base-1 + base-2: All tests pass — persistence is fully functional with setValues integration.

Related

- Export VSCodeAPIWrapper class and add getViewStateId() method
- Generate unique viewStateId using crypto.randomUUID() with fallback
- Persist viewStateId in localStorage for dev server compatibility
- Add viewStateSchema to global-settings.ts for type safety
- Send viewStateId during webviewDidLaunch handshake
@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds persistent identifiers for webview instances and stores per-view mode and provider configuration in validated global settings. ClineProvider hydrates, synchronizes, prunes, and clears this state. Selected provider paths now use stronger TypeScript types.

Changes

Per-view state persistence

Layer / File(s) Summary
View-state storage contract
packages/types/src/global-settings.ts, packages/types/src/__tests__/index.test.ts
Adds viewStateSchema and optional globalSettingsSchema.viewStates. Tests cover durable viewStates storage and secret-key exclusion.
Webview view-state identification
webview-ui/src/utils/vscode.ts, webview-ui/src/context/ExtensionStateContext.tsx, webview-ui/src/utils/__tests__/vscode.spec.ts
VSCodeAPIWrapper retrieves or generates persistent view-state IDs and falls back to memory when browser storage fails. Webview launch messages include the ID. Tests cover reuse, generation, and fallback behavior.
Provider state hydration and persistence
src/core/webview/ClineProvider.ts
ClineProvider loads and saves bounded per-view state, overlays it on shared state, synchronizes mode and provider selections, and clears it during reset.
Typed provider and delegation paths
src/core/webview/ClineProvider.ts, src/eslint-suppressions.json
Replaces selected any usages with specific types, changes array parameters to unknown[], and removes the provider’s ESLint suppression.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related issues

Possibly related PRs

  • Zoo-Code-Org/Zoo-Code#909 — Directly extends the same per-view state work across ClineProvider, webview launch messaging, and vscode.ts.
  • Zoo-Code-Org/Zoo-Code#928 — Overlaps in per-view persistence and initialization across the provider, launch flow, and API wrapper.
  • Zoo-Code-Org/Zoo-Code#966 — Shares the view-state isolation changes in ClineProvider, webview messaging, and VSCodeAPIWrapper.

Suggested labels: awaiting-review

Suggested reviewers: edelauna

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the implementation and test dependency, but it omits the required issue link, test procedure, checklist, and documentation sections. Add the required template sections, link an approved GitHub Issue, document reproducible test steps, complete the checklist, and state the documentation impact.
✅ Passed checks (4 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly identifies the primary change: generating and persisting viewStateId infrastructure for webviews.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

🧹 Nitpick comments (4)
src/core/webview/ClineProvider.ts (3)

556-565: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

clearPersistedViewState writes back without pruning.

savePersistedViewState applies prunePersistedViewStates before writing. clearPersistedViewState writes states directly. The two paths therefore apply different invariants to the same key. A clear operation can restore an over-cap map that a prior save had trimmed, because the fresh read returns whatever is currently stored.

Apply the same pruning in both paths.

♻️ Proposed fix
 			const states = this.getPersistedViewStates({ fresh: true })
 			delete states[viewStateId]
-			await this.contextProxy.setValue("viewStates", states)
+			await this.contextProxy.setValue("viewStates", this.prunePersistedViewStates(states))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/webview/ClineProvider.ts` around lines 556 - 565, Update
clearPersistedViewState to pass the states through prunePersistedViewStates
before setValue, matching the existing savePersistedViewState write path and
preserving the persisted view-state cap.

1927-1942: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Redundant view-state writes for one logical mutation.

saveViewState already calls _saveViewLocalStateFromMutation. This block calls saveViewState twice inside the Promise.all, then calls _saveViewLocalStateFromMutation again with the same currentApiConfigName and apiConfiguration. The result is that currentApiConfigName is persisted twice and the local cache is written three times, and two separate entries are queued on persistedViewStateWriteQueue.

Keep the single explicit call and drop the two saveViewState calls from the Promise.all.

♻️ Proposed fix
 					await Promise.all([
 						this.updateGlobalState("listApiConfigMeta", listApiConfigMeta),
 						this.updateGlobalState("currentApiConfigName", name),
 						this.providerSettingsManager.setModeConfig(mode, id),
 						this.contextProxy.setProviderSettings(providerSettings),
-						this.saveViewState("currentApiConfigName", name),
-						this.saveViewState("apiConfiguration", providerSettings),
 					])
 
 					await this._saveViewLocalStateFromMutation({
 						listApiConfigMeta,
 						currentApiConfigName: name,
 						apiConfiguration: providerSettings,
 					})
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/webview/ClineProvider.ts` around lines 1927 - 1942, Remove the
saveViewState calls for “currentApiConfigName” and “apiConfiguration” from the
Promise.all in the provider settings mutation, leaving the existing explicit
_saveViewLocalStateFromMutation call to persist both values once. Keep the other
state updates and providerSettingsManager operations unchanged.

507-509: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Add provider-level tests for the view-state lifecycle.

This PR adds view-state persistence tests for the webview wrapper and the types package, but no test covers the provider side. The untested behavior includes: merge precedence in getState, pruning at the 50-entry cap, serialization through persistedViewStateWriteQueue, and re-loading after setViewStateId. src/core/webview/__tests__/ClineProvider.spec.ts already exists and is the right layer for these.

As per coding guidelines: "Place tests in the narrowest layer that proves the behavior: package-local unit tests for pure logic and similar concerns; integration tests for cooperating internal modules."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/webview/ClineProvider.ts` around lines 507 - 509, The provider-level
view-state lifecycle is missing coverage. Extend ClineProvider.spec.ts with
tests for getState merge precedence, 50-entry pruning, persistence through
persistedViewStateWriteQueue, and reloading after setViewStateId, using the
existing provider test setup and preserving current behavior.

Source: Coding guidelines

webview-ui/src/utils/__tests__/vscode.spec.ts (1)

47-61: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for the crypto-absent fallback branch.

The three tests all stub or rely on crypto.randomUUID. The Date.now()/Math.random() fallback in createViewStateId has no test. That branch is the safety net for restricted or insecure contexts, which is the same environment class this PR targets.

💚 Proposed additional test
it("generates a viewStateId without crypto.randomUUID", () => {
	Object.defineProperty(globalThis, "crypto", { configurable: true, value: {} })
	const storage = createMockStorage()
	Object.defineProperty(globalThis, "localStorage", { configurable: true, value: storage })
	const wrapper = new VSCodeAPIWrapper()

	const viewStateId = wrapper.getViewStateId()

	expect(viewStateId).toMatch(/^[a-z0-9]+-[a-z0-9]+$/)
	expect(wrapper.getViewStateId()).toBe(viewStateId)
})
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@webview-ui/src/utils/__tests__/vscode.spec.ts` around lines 47 - 61, Add a
test covering the crypto-absent fallback in createViewStateId by defining
globalThis.crypto without randomUUID, then instantiate VSCodeAPIWrapper with
mocked localStorage and verify getViewStateId returns the fallback-shaped
identifier and persists the same value across repeated calls.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/core/webview/ClineProvider.ts`:
- Around line 642-648: Update the on and corresponding off overrides in
ClineProvider to resolve the listener variance mismatch without casting super.on
or super.off to any. Remove both inline ESLint suppressions and use a type-safe
invocation that preserves the TaskProviderEvents listener signatures.
- Around line 590-621: Update src/core/webview/ClineProvider.ts lines 590-621 in
loadViewState to merge loaded values into the existing viewLocalState buffer,
and assign currentApiConfigName only after getProfile succeeds; update lines
354-356 to retain the constructor-started loadViewState promise and await it in
getState() before returning state.
- Around line 3012-3013: Update the destructiveCommandGuardEnabled field in the
return object to read from mergedStateValues instead of stateValues, while
preserving the existing nullish fallback to
DEFAULT_DESTRUCTIVE_COMMAND_GUARD_ENABLED.
- Around line 1987-1990: In deleteProviderProfile, replace the
_updateViewLocalStateFromMutation call with _saveViewLocalStateFromMutation so
the new currentApiConfigName and listApiConfigMeta are persisted in viewStates
as well as updated in memory.
- Around line 575-584: Update setViewStateId to compare the value that will
actually be stored in this.viewStateId, preventing repeated loadViewState calls
for equivalent handshakes. Avoid the lossy character replacement for viewStates
keys; either retain the trimmed id unchanged or validate and reject ids
containing unexpected characters before updating state.
- Line 1764: Replace the double assertion in the task mode assignment with
bracket notation to access the private _taskMode member directly, preserving the
existing newMode value and its string | undefined typing.
- Around line 3269-3285: Update the provider-settings branch around
PROVIDER_SETTINGS_KEYS and providerSettingsUpdate so single-key mutations,
including apiProvider, merge into the existing
this.viewLocalState.apiConfiguration instead of replacing it; preserve any
intentional stale-key clearing only if explicitly documented and safe. Replace
the reduce-based object construction with a single-pass mutation or equivalent
accumulator that avoids repeated object spreads and O(n²) allocations.
- Around line 2999-3002: Update the apiConfiguration construction in
ClineProvider to replace the shared configuration entirely when a view-local
profile is selected, rather than spreading providerSettings with
mergedStateValues.apiConfiguration. Strip only the profile name as currently
required, and compose secrets from the selected profile so model IDs, URLs,
provider fields, and secret state cannot be inherited from the shared profile.

In `@webview-ui/src/context/ExtensionStateContext.tsx`:
- Around line 514-517: Update the webviewDidLaunch message type to declare
viewStateId, then in the corresponding handler call
provider.setViewStateId(message.viewStateId) before or while processing the
launch event. Ensure the webview-generated ID is propagated so per-view state no
longer falls back to the default viewId.

In `@webview-ui/src/utils/vscode.ts`:
- Around line 26-32: Update createViewStateId to verify crypto.randomUUID is
callable before invoking it, and guard the invocation against insecure-context
failures so getViewStateId falls back to the existing timestamp/random value
when UUID generation is unavailable.

---

Nitpick comments:
In `@src/core/webview/ClineProvider.ts`:
- Around line 556-565: Update clearPersistedViewState to pass the states through
prunePersistedViewStates before setValue, matching the existing
savePersistedViewState write path and preserving the persisted view-state cap.
- Around line 1927-1942: Remove the saveViewState calls for
“currentApiConfigName” and “apiConfiguration” from the Promise.all in the
provider settings mutation, leaving the existing explicit
_saveViewLocalStateFromMutation call to persist both values once. Keep the other
state updates and providerSettingsManager operations unchanged.
- Around line 507-509: The provider-level view-state lifecycle is missing
coverage. Extend ClineProvider.spec.ts with tests for getState merge precedence,
50-entry pruning, persistence through persistedViewStateWriteQueue, and
reloading after setViewStateId, using the existing provider test setup and
preserving current behavior.

In `@webview-ui/src/utils/__tests__/vscode.spec.ts`:
- Around line 47-61: Add a test covering the crypto-absent fallback in
createViewStateId by defining globalThis.crypto without randomUUID, then
instantiate VSCodeAPIWrapper with mocked localStorage and verify getViewStateId
returns the fallback-shaped identifier and persists the same value across
repeated calls.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 5d4dd981-b4b5-48c1-ac3b-4d1010fef6b9

📥 Commits

Reviewing files that changed from the base of the PR and between 276e425 and 4c3e1db.

📒 Files selected for processing (7)
  • packages/types/src/__tests__/index.test.ts
  • packages/types/src/global-settings.ts
  • src/core/webview/ClineProvider.ts
  • src/eslint-suppressions.json
  • webview-ui/src/context/ExtensionStateContext.tsx
  • webview-ui/src/utils/__tests__/vscode.spec.ts
  • webview-ui/src/utils/vscode.ts
💤 Files with no reviewable changes (1)
  • src/eslint-suppressions.json

Comment on lines +575 to +584
public async setViewStateId(viewStateId: string | undefined): Promise<void> {
const normalizedViewStateId = viewStateId?.trim()

if (!normalizedViewStateId || normalizedViewStateId === this.viewStateId) {
return
}

this.viewStateId = normalizedViewStateId.replace(/[^A-Za-z0-9_-]/g, "_")
await this.loadViewState()
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Compare the sanitized id, and reconsider sanitizing at all.

Two problems here:

  1. The equality check uses normalizedViewStateId, but the field stores the sanitized value. If the incoming id contains any character outside [A-Za-z0-9_-], the check never matches and every handshake re-runs loadViewState().
  2. replace(/[^A-Za-z0-9_-]/g, "_") is many-to-one. Two distinct webviews whose ids differ only in a replaced character map to the same persisted key and overwrite each other's mode and profile. The value is used only as an object key in the viewStates record, so no escaping is required.

Sanitize first, then compare. If you keep the sanitizer, reject unexpected ids instead of collapsing them.

🐛 Proposed fix
 	public async setViewStateId(viewStateId: string | undefined): Promise<void> {
-		const normalizedViewStateId = viewStateId?.trim()
-
-		if (!normalizedViewStateId || normalizedViewStateId === this.viewStateId) {
-			return
-		}
-
-		this.viewStateId = normalizedViewStateId.replace(/[^A-Za-z0-9_-]/g, "_")
-		await this.loadViewState()
+		const normalizedViewStateId = viewStateId?.trim().replace(/[^A-Za-z0-9_-]/g, "_")
+
+		if (!normalizedViewStateId || normalizedViewStateId === this.viewStateId) {
+			return
+		}
+
+		this.viewStateId = normalizedViewStateId
+		await this.loadViewState()
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/webview/ClineProvider.ts` around lines 575 - 584, Update
setViewStateId to compare the value that will actually be stored in
this.viewStateId, preventing repeated loadViewState calls for equivalent
handshakes. Avoid the lossy character replacement for viewStates keys; either
retain the trimmed id unchanged or validate and reject ids containing unexpected
characters before updating state.

Comment on lines +590 to +621
private async loadViewState(): Promise<void> {
try {
const persisted = this.getPersistedViewStates()[this.viewStateId]
const loadedState: Partial<ExtensionState> = {}

if (persisted?.mode) {
loadedState.mode = persisted.mode
}

if (persisted?.currentApiConfigName) {
loadedState.currentApiConfigName = persisted.currentApiConfigName

try {
const { name: _name, ...apiConfiguration } = await this.providerSettingsManager.getProfile({
name: persisted.currentApiConfigName,
})
loadedState.apiConfiguration = apiConfiguration as ProviderSettings
} catch (error) {
this.log(
`[loadViewState] Unable to resolve API profile '${persisted.currentApiConfigName}' for viewId ${this.viewId}: ${error instanceof Error ? error.message : String(error)}`,
)
}
}

this.viewLocalState = loadedState
this.log(`[loadViewState] Loaded state for viewId ${this.viewId}`)
} catch (error) {
this.log(
`[loadViewState] Error loading state for viewId ${this.viewId}: ${error instanceof Error ? error.message : String(error)}`,
)
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Async loadViewState both races the first read and discards concurrent writes. Both sites stem from one root cause: loadViewState is asynchronous, it awaits providerSettingsManager.getProfile(), and it finishes by replacing the whole viewLocalState object. The constructor starts it without awaiting, so a window exists in which getState() reads an unloaded buffer and other code writes into a buffer that is about to be thrown away.

  • src/core/webview/ClineProvider.ts#L590-L621: merge the loaded values into the existing buffer instead of assigning over it, and set currentApiConfigName only when getProfile() succeeds.
  • src/core/webview/ClineProvider.ts#L354-L356: store the returned promise in a field and await it in getState(), so the first read after construction observes the loaded per-view values.
📍 Affects 1 file
  • src/core/webview/ClineProvider.ts#L590-L621 (this comment)
  • src/core/webview/ClineProvider.ts#L354-L356
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/webview/ClineProvider.ts` around lines 590 - 621, Update
src/core/webview/ClineProvider.ts lines 590-621 in loadViewState to merge loaded
values into the existing viewLocalState buffer, and assign currentApiConfigName
only after getProfile succeeds; update lines 354-356 to retain the
constructor-started loadViewState promise and await it in getState() before
returning state.

Comment on lines 642 to 648
override on<K extends keyof TaskProviderEvents>(
event: K,
listener: (...args: TaskProviderEvents[K]) => void | Promise<void>,
): this {
return super.on(event, listener as any)
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return (super.on as any)(event, listener)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Replace the added as any casts and inline ESLint suppressions.

Both overrides now carry // eslint-disable-next-line @typescript-eslint/no-explicit-any`` with (super.on as any) and `(super.off as any)`. The coding guidelines require fixing lint violations in new TypeScript code rather than suppressing them, and require avoiding `as any`. This PR also removes suppressions elsewhere, so adding two inline ones here works against that goal.

The variance mismatch can be resolved without any.

♻️ Proposed fix
 	override on<K extends keyof TaskProviderEvents>(
 		event: K,
 		listener: (...args: TaskProviderEvents[K]) => void | Promise<void>,
 	): this {
-		// eslint-disable-next-line `@typescript-eslint/no-explicit-any`
-		return (super.on as any)(event, listener)
+		return super.on(event as never, listener as never)
 	}
 	override off<K extends keyof TaskProviderEvents>(
 		event: K,
 		listener: (...args: TaskProviderEvents[K]) => void | Promise<void>,
 	): this {
-		// eslint-disable-next-line `@typescript-eslint/no-explicit-any`
-		return (super.off as any)(event, listener)
+		return super.off(event as never, listener as never)
 	}

As per coding guidelines: "Fix lint violations in new TypeScript code rather than suppressing them" and "Avoid as any; use typed APIs, bracket notation for private members, or precise test doubles and type guards."

Also applies to: 653-659

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/webview/ClineProvider.ts` around lines 642 - 648, Update the on and
corresponding off overrides in ClineProvider to resolve the listener variance
mismatch without casting super.on or super.off to any. Remove both inline ESLint
suppressions and use a type-safe invocation that preserves the
TaskProviderEvents listener signatures.

Source: Coding guidelines


// Only update the task's mode after successful persistence.
;(task as any)._taskMode = newMode
;(task as unknown as Record<string, string>)._taskMode = newMode

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Use bracket notation instead of an undocumented double assertion.

(task as unknown as Record<string, string>) is a double assertion, and it is inaccurate: Task._taskMode is typed string | undefined, and Task is not a string-valued record. The guidelines name bracket notation as the preferred way to reach a private member, and permit double assertions only as a documented last resort.

♻️ Proposed fix
-				;(task as unknown as Record<string, string>)._taskMode = newMode
+				task["_taskMode"] = newMode

As per coding guidelines: "Avoid as any; use typed APIs, bracket notation for private members, or precise test doubles and type guards. Use double assertions only as a last resort and document them."

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
;(task as unknown as Record<string, string>)._taskMode = newMode
task["_taskMode"] = newMode
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/webview/ClineProvider.ts` at line 1764, Replace the double assertion
in the task mode assignment with bracket notation to access the private
_taskMode member directly, preserving the existing newMode value and its string
| undefined typing.

Source: Coding guidelines

Comment on lines +1987 to +1990
this._updateViewLocalStateFromMutation({
currentApiConfigName: profileToActivate,
listApiConfigMeta: entries,
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Profile deletion updates the cache but not the persisted view state.

deleteProviderProfile switches currentApiConfigName to profileToActivate and calls _updateViewLocalStateFromMutation, which only touches the in-memory cache. The persisted viewStates entry keeps the deleted profile name. After a reload, loadViewState calls getProfile with that deleted name, the call fails, and the per-view profile selection is lost with an error in the log.

Use _saveViewLocalStateFromMutation so the persisted entry is updated too.

🐛 Proposed fix
-		this._updateViewLocalStateFromMutation({
-			currentApiConfigName: profileToActivate,
-			listApiConfigMeta: entries,
-		})
+		await this._saveViewLocalStateFromMutation({
+			currentApiConfigName: profileToActivate,
+			listApiConfigMeta: entries,
+		})
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
this._updateViewLocalStateFromMutation({
currentApiConfigName: profileToActivate,
listApiConfigMeta: entries,
})
await this._saveViewLocalStateFromMutation({
currentApiConfigName: profileToActivate,
listApiConfigMeta: entries,
})
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/webview/ClineProvider.ts` around lines 1987 - 1990, In
deleteProviderProfile, replace the _updateViewLocalStateFromMutation call with
_saveViewLocalStateFromMutation so the new currentApiConfigName and
listApiConfigMeta are persisted in viewStates as well as updated in memory.

Comment on lines +2999 to +3002
apiConfiguration: {
...providerSettings,
...mergedStateValues.apiConfiguration,
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Inspect getProviderSettings composition to judge the merge precedence.
set -euo pipefail

fd -t f 'ContextProxy.ts' --exec rg -n -C10 'getProviderSettings|setProviderSettings' {}

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 1923


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate relevant files and symbols, then inspect the focused implementation slices.
printf 'Files:\n'
fd -t f 'ClineProvider.ts|ContextProxy.ts|.*schemas.*|.*settings.*' src | sed -n '1,120p'

printf '\nContextProxy get/set and state shape:\n'
wc -l src/i18n/ContextProxy.ts
sed -n '430,560p' src/i18n/ContextProxy.ts

printf '\nClineProvider focused slice:\n'
wc -l src/core/webview/ClineProvider.ts
sed -n '2950,3035p' src/core/webview/ClineProvider.ts

printf '\nSearch loadViewState and providerSettingsSchema definitions:\n'
rg -n -C8 'function loadViewState|const loadViewState|providerSettingsSchema|loadViewState\(' src

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 327


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'ContextProxy get/set and state shape:\n'
wc -l src/core/config/ContextProxy.ts
sed -n '430,590p' src/core/config/ContextProxy.ts

printf '\nClineProvider focused slice:\n'
wc -l src/core/webview/ClineProvider.ts
sed -n '2950,3035p' src/core/webview/ClineProvider.ts

printf '\nSearch loadViewState and providerSettingsSchema definitions:\n'
rg -n -C8 'function loadViewState|const loadViewState|providerSettingsSchema|loadViewState\(' src

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 14980


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'loadViewState implementation:\n'
sed -n '585,820p' src/core/webview/ClineProvider.ts

printf '\nState type definitions around ExtensionState / viewLocalState:\n'
rg -n -C5 'interface ExtensionState|type ExtensionState|viewLocalState|PERSISTED_VIEW_STATES|persistedViewStates|apiConfiguration' src/core/webview/ClineProvider.ts

printf '\nType definitions from packages (if present):\n'
fd -t f '.*types.*|.*schema.*|.*state.*' . --exclude node_modules --exclude dist --exclude build | sed -n '1,160p'
rg -n -C5 'interface ProviderSettings|type ProviderSettings|providerSettingsSchema|apiConfiguration|APIConfiguration|SecretStateKey|isSecretStateKey' src packages 2>/dev/null | sed -n '1,220p' || true

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 45687


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'ProviderSettingsManager get/set profile slices:\n'
sed -n '1,220p' src/core/config/ProviderSettingsManager.ts
sed -n '350,660p' src/core/config/ProviderSettingsManager.ts

printf '\nSaved mutation helpers slices:\n'
sed -n '1480,1560p' src/core/webview/ClineProvider.ts
sed -n '3300,3325p' src/core/webview/ClineProvider.ts

printf '\nBehavioral probe for state merge shape from static sources:\n'
python3 - <<'PY'
from pathlib import Path
src = Path('src/core/webview/ClineProvider.ts').read_text()
checks = {
    'viewLocalState initialized empty': 'private viewLocalState: Partial<ExtensionState> = {}' in src,
    'loadViewState unsets missing persisted entries': 'Missing entries are intentionally left unset' in src,
    'loadViewState strips only profile name': '.{ name: _name, ...apiConfiguration }' in src or "name: _name, ...apiConfiguration" in src,
    'loadViewState uses ProviderSettingsManager.getProfile': 'providerSettingsManager.getProfile' in src,
    'getState merges stateValues then viewLocalState': '{ ...stateValues, ...this.viewLocalState }' in src,
    'getState spreads providerSettings then merged apiConfiguration': '...providerSettings,\n\t\t\t...mergedStateValues.apiConfiguration,' in src,
}
for name, ok in checks.items():
    print(f'{name}: {ok}')
print('all true:', all(checks.values()))
PY

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 20544


Replace the whole apiConfiguration when the view-local profile is present.

getProfile() returns the selected profile as ProviderSettings, but only name is stripped before assigning viewLocalState.apiConfiguration. The current spread merges provider keys, so a view-local provider can inherit model IDs, base URLs, or other fields from the shared profile. Also ensure secrets are composed correctly for the selected profile instead of leaking the shared profile’s secret state.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/webview/ClineProvider.ts` around lines 2999 - 3002, Update the
apiConfiguration construction in ClineProvider to replace the shared
configuration entirely when a view-local profile is selected, rather than
spreading providerSettings with mergedStateValues.apiConfiguration. Strip only
the profile name as currently required, and compose secrets from the selected
profile so model IDs, URLs, provider fields, and secret state cannot be
inherited from the shared profile.

Comment on lines 3012 to 3013
destructiveCommandGuardEnabled:
stateValues.destructiveCommandGuardEnabled ?? DEFAULT_DESTRUCTIVE_COMMAND_GUARD_ENABLED,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

destructiveCommandGuardEnabled still reads stateValues.

Every neighbouring field in this return object reads mergedStateValues. This one reads stateValues. The two are equivalent today, because viewLocalState only ever holds mode, currentApiConfigName, and apiConfiguration. The asymmetry becomes a bug if the view-local buffer later gains this key.

🐛 Proposed fix
 			destructiveCommandGuardEnabled:
-				stateValues.destructiveCommandGuardEnabled ?? DEFAULT_DESTRUCTIVE_COMMAND_GUARD_ENABLED,
+				mergedStateValues.destructiveCommandGuardEnabled ?? DEFAULT_DESTRUCTIVE_COMMAND_GUARD_ENABLED,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
destructiveCommandGuardEnabled:
stateValues.destructiveCommandGuardEnabled ?? DEFAULT_DESTRUCTIVE_COMMAND_GUARD_ENABLED,
destructiveCommandGuardEnabled:
mergedStateValues.destructiveCommandGuardEnabled ?? DEFAULT_DESTRUCTIVE_COMMAND_GUARD_ENABLED,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/webview/ClineProvider.ts` around lines 3012 - 3013, Update the
destructiveCommandGuardEnabled field in the return object to read from
mergedStateValues instead of stateValues, while preserving the existing nullish
fallback to DEFAULT_DESTRUCTIVE_COMMAND_GUARD_ENABLED.

Comment on lines +3269 to +3285
} else if (PROVIDER_SETTINGS_KEYS.some((key) => key in values)) {
const providerSettingsUpdate = PROVIDER_SETTINGS_KEYS.reduce((acc, key) => {
if (key in values) {
return { ...acc, [key]: values[key as keyof RooCodeSettings] }
}

return acc
}, {} as ProviderSettings)

this.viewLocalState.apiConfiguration =
"apiProvider" in providerSettingsUpdate
? providerSettingsUpdate
: {
...(this.viewLocalState.apiConfiguration ?? {}),
...providerSettingsUpdate,
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

A single provider-key mutation can wipe the view-local apiConfiguration.

Two problems in this branch.

  1. When the mutation contains apiProvider, Line 3280 assigns providerSettingsUpdate directly and discards the existing this.viewLocalState.apiConfiguration. providerSettingsUpdate holds only the keys present in this one mutation. A call such as setValue("apiProvider", "anthropic") therefore reduces the whole view-local configuration to { apiProvider: "anthropic" }, and getState() then falls back to the shared profile for every other field. If the intent is to clear stale keys from the previous provider, state that in a comment and confirm it is safe for single-key callers.
  2. The reduce allocates a new object on every iteration of PROVIDER_SETTINGS_KEYS. That list is large, so this is O(n²) allocation on a path reached by every setValue and setValues that touches a provider key.
♻️ Proposed fix for the allocation, plus a guard for single-key mutations
 		} else if (PROVIDER_SETTINGS_KEYS.some((key) => key in values)) {
-			const providerSettingsUpdate = PROVIDER_SETTINGS_KEYS.reduce((acc, key) => {
-				if (key in values) {
-					return { ...acc, [key]: values[key as keyof RooCodeSettings] }
-				}
-
-				return acc
-			}, {} as ProviderSettings)
+			const providerSettingsUpdate: ProviderSettings = {}
+			for (const key of PROVIDER_SETTINGS_KEYS) {
+				if (key in values) {
+					;(providerSettingsUpdate as Record<string, unknown>)[key] = values[key as keyof RooCodeSettings]
+				}
+			}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/webview/ClineProvider.ts` around lines 3269 - 3285, Update the
provider-settings branch around PROVIDER_SETTINGS_KEYS and
providerSettingsUpdate so single-key mutations, including apiProvider, merge
into the existing this.viewLocalState.apiConfiguration instead of replacing it;
preserve any intentional stale-key clearing only if explicitly documented and
safe. Replace the reduce-based object construction with a single-pass mutation
or equivalent accumulator that avoids repeated object spreads and O(n²)
allocations.

Comment on lines +514 to +517
vscode.postMessage({
type: "webviewDidLaunch",
viewStateId: typeof vscode.getViewStateId === "function" ? vscode.getViewStateId() : undefined,
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Trace the viewStateId handshake from webview message type to provider consumption.
set -euo pipefail

# 1. Does the WebviewMessage type declare viewStateId?
rg -n -C3 'viewStateId' --type=ts --type=tsx -g '!**/node_modules/**' src packages webview-ui

# 2. Locate the webviewDidLaunch handler and check whether it reads viewStateId.
rg -n -C10 '"webviewDidLaunch"|case "webviewDidLaunch"' --type=ts -g '!**/node_modules/**' src

# 3. Find call sites of setViewStateId.
ast-grep run --pattern '$_.setViewStateId($$$)' --lang typescript src

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 191


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## files matching relevant names"
git ls-files | rg '(^|/)(ExtensionStateContext|webview|ClineProvider|Provider|Message|types)\.(ts|tsx)$|webview-ui/src' | head -200

echo
echo "## viewStateId occurrences"
rg -n -C3 'viewStateId' --glob '*.ts' --glob '*.tsx' .

echo
echo "## webviewDidLaunch occurrences"
rg -n -C10 '"webviewDidLaunch"|case "webviewDidLaunch"|webviewDidLaunch' --glob '*.ts' --glob '*.tsx' .

echo
echo "## setViewStateId occurrences"
rg -n -C4 'setViewStateId' --glob '*.ts' --glob '*.tsx' .

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 11579


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## locate key symbols"
rg -n -C5 'export .*setViewStateId|setViewStateId|viewStateId|webviewDidLaunch|ViewStateId' --glob '*.ts' --glob '*.tsx' .

echo
echo "## ClineProvider outline"
ast-grep outline src/core/webview/ClineProvider.ts --view compact | sed -n '1,220p'

echo
echo "## ClineProvider relevant lines"
rg -n -C8 'viewStateId|setViewStateId|webviewDidLaunch|webviewStateId' src/core/webview/ClineProvider.ts

echo
echo "## App outline"
ast-grep outline webview-ui/src/App.tsx --view compact | sed -n '1,220p'

echo
echo "## webview-ui App state/message relevant lines"
rg -n -C8 'setViewStateId|viewStateId|viewId|webviewDidLaunch|onMessage|postMessage' webview-ui/src/App.tsx

echo
echo "## ExtensionStateContext relevant lines"
fd -a 'ExtensionStateContext.tsx' . | while read -r f; do
  echo "--- $f"
  wc -l "$f"
  rg -n -C8 'viewStateId|setViewStateId|webviewDidLaunch' "$f"
done

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 24268


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the exact contract/handler body and tests around webviewDidLaunch.
sed -n '430,480p' packages/types/src/vscode-extension-host.ts
sed -n '810,835p' src/core/webview/__tests__/ClineProvider.spec.ts
sed -n '560,590p' src/core/webview/webviewMessageHandler.ts
sed -n '570,592p' src/core/webview/ClineProvider.ts

echo
echo "## all postMessage webviewDidLaunch occurrences in tracked TS/TSX"
python3 - <<'PY'
import subprocess
files = subprocess.check_output(['git', 'ls-files'], text=True).splitlines()
for f in files:
    if f.endswith('.ts') or f.endswith('.tsx'):
        text = open(f, encoding='utf-8', errors='ignore').read()
        if 'webviewDidLaunch' in text:
            for i, line in enumerate(text.splitlines(), 1):
                if 'webviewDidLaunch' in line:
                    print(f"{f}:{i}: {line.strip()}")
PY

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 5674


Forward viewStateId in the webviewDidLaunch handler.

WebviewMessage only declares type for webviewDidLaunch, and the handler ignores any extra payload without calling ClineProvider.setViewStateId. Add viewStateId to the supported payload shape and call provider.setViewStateId(message.viewStateId) in the webviewDidLaunch case so persisted per-view state uses the webview-generated id instead of the default viewId.

🧰 Tools
🪛 GitHub Actions: E2E Tests (Mocked) / 0_e2e-mock.txt

[error] 516-516: TypeScript build failed in '@roo-code/vscode-webview' while running 'tsc -b && vite build': object literal specifies unknown property 'viewStateId', which does not exist in type 'WebviewMessage' (TS2353).

🪛 GitHub Actions: E2E Tests (Mocked) / e2e-mock

[error] 516-516: TypeScript build failed in '@roo-code/vscode-webview' during 'tsc -b && vite build': Object literal may only specify known properties, and 'viewStateId' does not exist in type 'WebviewMessage' (TS2353).

🪛 GitHub Actions: Release Validation / 0_validate-release.txt

[error] 516-516: TypeScript build failed: Object literal specifies unknown property 'viewStateId', which does not exist in type 'WebviewMessage' (TS2353). Failed command: pnpm --filter @roo-code/vscode-webview build.

🪛 GitHub Actions: Release Validation / validate-release

[error] 516-516: TypeScript build failed during 'pnpm --filter @roo-code/vscode-webview build': object literal includes unknown property 'viewStateId', which does not exist in type 'WebviewMessage' (TS2353).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@webview-ui/src/context/ExtensionStateContext.tsx` around lines 514 - 517,
Update the webviewDidLaunch message type to declare viewStateId, then in the
corresponding handler call provider.setViewStateId(message.viewStateId) before
or while processing the launch event. Ensure the webview-generated ID is
propagated so per-view state no longer falls back to the default viewId.

Comment on lines +26 to +32
private createViewStateId(): string {
if (typeof crypto !== "undefined" && "randomUUID" in crypto) {
return crypto.randomUUID()
}

return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

crypto.randomUUID secure context requirement browser availability

💡 Result:

The crypto.randomUUID method is strictly restricted to secure contexts [1][2]. This means it is only available in environments delivered over HTTPS or on localhost (including 127.0.0.1 and file://) [3][2]. If you attempt to call crypto.randomUUID in a non-secure context (such as an insecure HTTP connection over a local network or internal hostname), the crypto object may not contain the method, or the call will fail, typically resulting in an error such as crypto.randomUUID is not a function [3][2]. There is no intention to drop this secure context requirement, as browser vendors and the W3C have standardized this to ensure cryptographic security [3]. Developers encountering this issue in development environments on internal networks are advised to serve their applications over HTTPS, for example by using tools like mkcert to generate locally trusted certificates [3][2]. In terms of availability, crypto.randomUUID is widely supported in all modern browsers [4]. Support began appearing in major browsers around 2021 (e.g., Chrome 92, Firefox 95) [4][5]. It is available for use in both standard browser windows and Web Workers [1]. For environments outside of browsers, such as Node.js, the secure context requirement does not apply; in Node.js, crypto.randomUUID is available as of version 14.17 [2].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Files matching vscode/SettingsView/ExtensionStateContext:\n'
git ls-files | rg '(^|/)(vscode\.(ts|tsx)|SettingsView\.(ts|tsx)|ExtensionStateContext\.(ts|tsx))$' || true

printf '\n--- webview-ui/src/utils/vscode.ts outline ---\n'
ast-grep outline webview-ui/src/utils/vscode.ts --view expanded || true

printf '\n--- webview-ui/src/utils/vscode.ts relevant lines ---\n'
cat -n webview-ui/src/utils/vscode.ts | sed -n '1,90p'

printf '\n--- Search for getViewStateId/createViewStateId usages ---\n'
rg -n "createViewStateId|getViewStateId|webviewDidLaunch|ExtensionStateContext" webview-ui/src -S

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 37160


🌐 Web query:

MDN Crypto randomUUID secure context TypeError outside secure context

💡 Result:

The TypeError "crypto.randomUUID is not a function" occurs because the crypto.randomUUID method is strictly restricted to secure contexts [1][2][3]. What constitutes a secure context: - HTTPS: Any page served over a secure HTTPS connection is a secure context [1][4]. - localhost: Browsers treat http://localhost as a secure context, allowing crypto.randomUUID to function even without HTTPS [2][3][4]. - Other origins: Arbitrary HTTP origins (e.g., internal network IP addresses like 192.168.x.x or custom non-HTTPS hostnames) are not considered secure contexts [5][4][6]. If your application is accessed via these addresses, crypto.randomUUID will be undefined, and calling it will result in a TypeError [3][4]. Why this restriction exists: The Web Crypto API is designed to provide cryptographic primitives [7]. To prevent passive eavesdroppers or man-in-the-middle attackers from observing or tampering with cryptographic operations, browser vendors and the W3C restricted access to these methods to secure contexts [3][4][6]. Recommended solutions: 1. Use HTTPS: The most robust solution is to serve your application over HTTPS, even for internal network deployments [3][4][6]. Tools like mkcert can help generate locally trusted certificates for development [4]. 2. Check Availability: If you must support insecure environments, implement a feature check before calling the method [3]: if (typeof crypto?.randomUUID === 'function') { const id = crypto.randomUUID; } else { // Fallback to a secure library or an alternative ID generation method } 3. Use a Polyfill/Fallback: If crypto.randomUUID is unavailable, you can use crypto.getRandomValues to generate a UUID yourself, or use a well-vetted library to ensure collision resistance [3][5][6]. Do not use insecure random number generators (like Math.random) for creating unique IDs if they are required to be cryptographically secure [8][3].

Citations:


Guard crypto.randomUUID() against insecure-context failures.

"randomUUID" in crypto tests for the property only; outside a secure context it is unavailable, and calling crypto.randomUUID() can fail. Wrap the call or check typeof crypto.randomUUID === "function" before calling it so getViewStateId() falls back to the deterministic fallback instead of failing during webview initialization.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@webview-ui/src/utils/vscode.ts` around lines 26 - 32, Update
createViewStateId to verify crypto.randomUUID is callable before invoking it,
and guard the invocation against insecure-context failures so getViewStateId
falls back to the existing timestamp/random value when UUID generation is
unavailable.

@codecov

codecov Bot commented Aug 7, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants