Skip to content

feat: --session-preference CLI overrides and AI Allow-All Mode banner#17731

Open
ndoschek wants to merge 2 commits into
masterfrom
nd/session-preferences
Open

feat: --session-preference CLI overrides and AI Allow-All Mode banner#17731
ndoschek wants to merge 2 commits into
masterfrom
nd/session-preferences

Conversation

@ndoschek

Copy link
Copy Markdown
Member

What it does

Adds a new in-memory preference scope and a --session-preference CLI flag that lets adopters (and dev-container launch configs) pin preferences for the duration of a single process without touching the user's persisted settings. Builds on top of that a small UI surface so users always know which settings are being forced.

Preferences foundation:

  • New PreferenceScope.Session, a highest-precedence in-memory scope that never persists to disk, backed by SessionPreferenceProvider.
  • New --session-preference KEY=JSONVALUE CLI flag parallel to --set-preference. Repeatable, supports a base64: value prefix for shell-unfriendly values, and skips malformed entries with a warning instead of throwing.
  • Forwarded to remote backends via RemoteCliContribution.enhanceArgs, so values reach the remote process after a dev-container attach.
  • Writing the same key to a persistent scope drops the session override (evictSessionOverride) and emits a Session-scope change event so listeners stay in sync. The session scope is excluded from regular edit-target selection.
  • Session scope maps to monaco's MEMORY configuration target.
  • Session-overridden preferences show a subtle "Overridden by session" suffix in the Settings UI (styled like the existing "Modified in:" treatment) across all scope tabs. Editing the preference clears the session override.
  • A dedicated status bar item lists active session overrides in a markdown tooltip; each entry is a link that opens the Settings view filtered to that preference.
  • Both CLI buckets (--set-preference and --session-preference) are fetched in parallel after preferenceService.ready. Only preference keys are logged at startup; values are intentionally omitted so security-sensitive overrides (AI auto-approve, etc.) cannot leak into logs or screenshots.

AI surfacing:

  • New ChatBannerProvider contribution point and ChatBannerWidget host in @theia/ai-chat-ui for persistent banners above the chat content.
  • New AiAllowAllModeChatBanner in @theia/ai-ide: a dismissible strip that appears above the chat content when an AI tool confirmation preference (ai-features.chat.defaultToolConfirmation or ai-features.chat.toolConfirmation) is set in the session scope.
    • Red "Theia AI Allow-All Mode" only when the global default is forced to Allow-All.
    • Yellow "AI Tool Confirmation Overrides" for per-tool always_allow entries.
  • The banner intentionally only watches the two tool confirmation preferences. Other AI session overrides (enable AI, agent mode, default chat agent) are surfaced in the generic Session Preferences status bar item to avoid duplicating the same list in two places.
  • Announces changes via role='status' + aria-live='polite'.

Tests cover the session provider, override eviction (including the Session-scope change event and the no-op when writing to Session itself), CLI parsing including malformed entries, remote-arg forwarding, and the banner's bypass-detection rule.

How to test

Add a few --session-preference entries to the Launch Electron Backend (or Launch Browser Backend) configuration in .vscode/launch.json:

"args": [
    ".",
    // ...existing args...
    "--session-preference=ai-features.AiEnable.enableAI=true",
    "--session-preference=ai-features.agentMode.enabled=true",
    "--session-preference=ai-features.agentSettings={\"Coder\":{\"capabilityOverrides\":{\"shell-execution\":true}}}",
    "--session-preference=ai-features.chat.toolConfirmation={\"shellExecute\":\"always_allow\"}",
    "--session-preference=ai-features.chat.defaultChatAgent=\"Coder\"",
    "--session-preference=ai-features.chat.defaultToolConfirmation=\"always_allow\""
]

Note: values must be valid JSON, so bare strings need escaped quotes (\"Coder\") and objects need their inner quotes escaped too. For complex values you can also use base64:<base64-encoded JSON> to avoid shell escaping.

Launch the application and check:

  1. A "Session Preferences (N)" entry appears on the left side of the status bar. Hovering it shows a tooltip listing each overridden preference as a clickable link that jumps to the Settings view, filtered to that preference.
  2. Opening the Settings UI: each overridden preference shows a subtle italic "Overridden by session" suffix in its row title. The input still works; changing the value clears the session override for the rest of the session.
  3. Above the chat view: a red "Theia AI Allow-All Mode" strip appears because defaultToolConfirmation is forced to Allow-All. Dismiss it with the close button; it stays dismissed for the rest of the process and comes back on the next launch.
  4. Remove the defaultToolConfirmation line and relaunch: the strip turns yellow ("AI Tool Confirmation Overrides") because toolConfirmation still has a per-tool Allow-All entry for shellExecute.
  5. Remove all --session-preference flags and relaunch: the status bar item, the suffix in the Settings UI, and the chat banner all disappear.
  6. Remote / dev-container attach: the same --session-preference flags are forwarded to the remote backend; the same UI signals should appear there as well.

Test the CLI guards by passing a malformed entry (e.g. --session-preference=foo with no =, or --session-preference=foo=notJson): the application starts normally and logs a warning in the dev tools console; the malformed entry is skipped, valid ones still apply.

Follow-ups

  • AI configuration view alignment. The reworked AI configuration view (planned for next week) should apply the same "Overridden by session" indication per control. The simple per-control check is preferenceService.inspect(key)?.sessionValue !== undefined. Leaving this out of this PR to avoid double work since the layout is changing.
  • Direct edits to settings.json do not evict session overrides. evictSessionOverride only fires from PreferenceServiceImpl.set(), so an edit in the JSON file is saved correctly but the session value still wins for the rest of the session. Status bar item remains the discoverable signal. A future improvement could either show a one-shot notification when the JSON is opened while a session override is active, or evict on file-based change events as well.
  • Workspace trust is not checked for CLI overrides. applyCliPreferences applies values unconditionally. Whether to gate security-sensitive overrides (such as AI tool auto-approval) at the CLI-application layer is a policy decision left for a follow-up.
  • Resource-scoped (language) overrides. SessionPreferenceProvider is global and does not honor resourceUri; per-language overrides (e.g. [typescript].editor.tabSize) are not supported through --session-preference. Sufficient for the current use cases (process-level AI configuration); revisit if a real need appears.

Breaking changes

  • This PR introduces breaking changes and requires careful review. If yes, the breaking changes section in the changelog has been updated.

Attribution

Review checklist

Reminder for reviewers

- Add `PreferenceScope.Session`, a highest-precedence in-memory preference scope that never persists to disk, backed by a new `SessionPreferenceProvider`
- Add `--session-preference KEY=JSONVALUE` CLI flag parallel to `--set-preference`; repeatable, supports a `base64:` value prefix, and skips malformed entries with a clear warning instead of throwing
- Forward session preferences to remote backends via `RemoteCliContribution.enhanceArgs` so values reach the remote process after dev-container attach
- Drop the session override when the same key is written to a persistent scope (`evictSessionOverride`) so an explicit user edit always wins for the rest of the session; the eviction emits its own Session-scope change event so listeners stay in sync. Session is excluded from regular edit-target selection
- Map the session scope to monaco's `MEMORY` configuration target
- Indicate session-overridden preferences in the Settings UI with a subtle "Overridden by session" suffix styled like the existing "Modified in:" treatment, visible across all scope tabs. Editing the preference clears the session override
- Add a status bar item that lists active session overrides in a markdown tooltip; each entry links to its row in the Settings view, and the footer notes how to clear or restore values
- Apply CLI session and persistent preferences after the preference service is ready, fetching both buckets in parallel; log only the keys (not values) on startup to avoid leaking sensitive overrides
- Cover the new behaviour with unit tests for the session provider, override eviction (including the Session-scope change event it emits and the no-op when writing to Session itself), CLI parsing (including malformed entries), and remote-arg forwarding
@github-project-automation github-project-automation Bot moved this to Waiting on reviewers in PR Backlog Jun 29, 2026
- Add `ChatBannerProvider` contribution point and `ChatBannerWidget` host in @theia/ai-chat-ui for persistent banners above the chat content
- Add `AiAllowAllModeChatBanner` in @theia/ai-ide: a dismissible strip surfaced above the chat content when an AI tool confirmation preference (`ai-features.chat.defaultToolConfirmation` or `ai-features.chat.toolConfirmation`) is set in the session scope via `--session-preference`
- Render in red ("Theia AI Allow-All Mode") only when the global default is forced to Allow-All; per-tool `always_allow` entries use warning styling ("AI Tool Confirmation Overrides")
- Watch only the two tool confirmation preferences here. Other AI session overrides (enable AI, agent mode, default chat agent) stay in the generic Session Preferences status bar item so the banner and the status bar do not duplicate the same list
- Announce changes via `role='status'` + `aria-live='polite'` so screen readers report updates without interrupting
- Cover the bypass-detection rule with a unit test so widening it later is an intentional change
@ndoschek ndoschek force-pushed the nd/session-preferences branch from 87d22eb to 28ecaa3 Compare June 30, 2026 06:48
@ndoschek ndoschek marked this pull request as ready for review July 2, 2026 10:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Waiting on reviewers

Development

Successfully merging this pull request may close these issues.

1 participant