Files
orca/src/main/runtime/rpc/methods/client-ui.test.ts
T
weekbinandNeil e48d83a5e1 Fix MiniMax China usage routing and credential handling (#14929)
* feat(minimax): endpoint selector, API key auth, weekly usage window (#14264)

The MiniMax (MiniMax) Coding Plan usage fetch was hardcoded to the
overseas platform (platform.minimax.io) and a single 5h session
window, so users on the CN endpoint (www.minimaxi.com) got nothing.

Three changes:

- Add `minimaxEndpoint` (`overseas`|`cn`) and
  `minimaxApiKeyConfigured` settings fields with sensible defaults
  that preserve current behavior. The CN endpoint also accepts an
  API key (safeStorage-encrypted via a new
  `minimax-api-key-store.ts` + IPC pair) for users without a
  browser session cookie. Status-bar visibility now OR's both
  credential flags.
- Cookie-jar origin now tracks the active endpoint. Previously
  cookies were stored under the overseas origin and silently
  dropped when the user picked CN — fixed by threading
  `endpointMode` through the request context, the manual cookie
  header path, and the cookie-jar clear.
- Parse the weekly window in addition to the 5h session and
  surface both as per-window chips (`5h [bar] 10%   wk [bar] 20%`).
  The status bar's compact section prefers the session window; the
  popover keeps the existing `Session` / `Weekly` labels. The
  MiniMax fetcher is split into three files (data / parse / main)
  to stay under the 300-line cap.

i18n is scoped to the Settings-page text (en + zh only); the 5H/7D
duration shorthands stay English across locales by project convention.

Tests: 9 new/updated files; cookies + API key exercised end-to-end
via the rate-limit service with the upstream-refactored test files
(`service-minimax-usage.test.ts`,
`web-preload-api-settings.test.ts`,
`web-preload-api-agent-providers.test.ts`,
`service-test-harness.ts`, and the runtime-home / reset-credit
fixtures).

Refs #14264

* Keep merge formatting scoped to MiniMax

* Keep MiniMax credential status in rate-limit test fixtures

* Use the China console origin for MiniMax request referer

---------

Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
2026-09-06 19:20:50 -07:00

868 lines
31 KiB
TypeScript

import { describe, expect, it, vi } from 'vitest'
import { getDefaultUIState } from '../../../../shared/constants'
import { omitPairingLocalUiFields } from '../../../../shared/pairing-local-ui-fields'
import {
MAX_QUICK_COMMAND_AGENT_PROMPT_LENGTH,
MAX_QUICK_COMMAND_ID_LENGTH,
MAX_QUICK_COMMAND_LABEL_LENGTH,
MAX_QUICK_COMMAND_REPO_ID_LENGTH,
MAX_QUICK_COMMAND_TERMINAL_TEXT_LENGTH
} from '../../../../shared/terminal-quick-commands'
import { DEFAULT_WORKTREE_CARD_PROPERTIES } from '../../../../shared/worktree/card-properties'
import type { PersistedUIState } from '../../../../shared/persisted-ui-state-types'
import type { OrcaRuntimeService } from '../../orca-runtime'
import type { RpcRequest } from '../core'
import { RpcDispatcher } from '../dispatcher'
import { CLIENT_UI_METHODS } from './client-ui'
function makeRequest(method: string, params?: unknown): RpcRequest {
return { id: 'req-1', authToken: 'tok', method, params }
}
describe('client UI RPC methods', () => {
it('returns the runtime host agent settings needed by mobile create flows', async () => {
const settings = {
worktreeVisibilityDefaults: { external: 'show' as const },
defaultTuiAgent: 'codex',
disabledTuiAgents: ['claude'],
agentCmdOverrides: { codex: 'codex --profile work' },
defaultTaskSource: 'gitlab',
defaultTaskViewPreset: 'my-prs',
visibleTaskProviders: ['github', 'gitlab'],
defaultRepoSelection: ['repo-1'],
defaultLinearTeamSelection: ['team-1'],
experimentalStructuredNativeChat: true,
compactWorktreeCards: true,
minimaxGroupId: 'group-42',
minimaxUsageModels: 'general,abab6.5',
minimaxEndpoint: 'cn',
githubProjects: {
pinned: [
{
owner: 'stablyai',
ownerType: 'organization' as const,
number: 1,
host: 'ghe.example:8443'
}
],
recent: [],
lastViewByProject: {},
activeProject: null
}
}
const runtime = {
getRuntimeId: () => 'test-runtime',
getClientSettings: vi.fn(() => settings)
} as unknown as OrcaRuntimeService
const dispatcher = new RpcDispatcher({ runtime, methods: CLIENT_UI_METHODS })
const response = await dispatcher.dispatch(makeRequest('settings.get'))
expect(runtime.getClientSettings).toHaveBeenCalledTimes(1)
expect(response).toMatchObject({ ok: true, result: { settings } })
})
it('rejects paired attempts to mutate the host-owned structured chat setting', async () => {
const runtime = {
getRuntimeId: () => 'test-runtime',
updateClientSettings: vi.fn()
} as unknown as OrcaRuntimeService
const dispatcher = new RpcDispatcher({ runtime, methods: CLIENT_UI_METHODS })
const response = await dispatcher.dispatch(
makeRequest('settings.update', { experimentalStructuredNativeChat: true })
)
expect(response).toMatchObject({
ok: false,
error: { code: 'invalid_argument' }
})
expect(runtime.updateClientSettings).not.toHaveBeenCalled()
})
it('persists the runtime host task source settings for mobile Tasks', async () => {
const settings = {
defaultTuiAgent: null,
disabledTuiAgents: ['claude'],
agentCmdOverrides: {},
defaultTaskSource: 'linear',
defaultTaskViewPreset: 'issues',
visibleTaskProviders: ['github', 'linear'],
defaultRepoSelection: ['repo-1', 'repo-2'],
defaultLinearTeamSelection: ['team-1', 'team-2'],
experimentalNewWorktreeCardStyle: true,
compactWorktreeCards: true,
githubProjects: {
pinned: [],
recent: [],
lastViewByProject: {
'organization:stablyai:1': { viewId: 'view-1' }
},
activeProject: {
owner: 'stablyai',
ownerType: 'organization' as const,
number: 1,
host: 'ghe.example:8443'
}
}
}
const runtime = {
getRuntimeId: () => 'test-runtime',
updateClientSettings: vi.fn(() => settings)
} as unknown as OrcaRuntimeService
const dispatcher = new RpcDispatcher({ runtime, methods: CLIENT_UI_METHODS })
const response = await dispatcher.dispatch(
makeRequest('settings.update', {
worktreeVisibilityDefaults: {
external: 'show',
customSources: [
{ id: 'team', rootPath: ' /srv/team ' },
{ id: 'invalid', rootPath: '../relative' }
],
sourcePreferences: {
builtIn: { claude: 'show', unknown: 'show' },
custom: { team: 'hide', 'bad id': 'show' }
}
},
defaultTuiAgent: 'codex',
disabledTuiAgents: ['claude', 'not-real', 'claude'],
defaultTaskSource: 'linear',
visibleTaskProviders: ['github', 'linear'],
defaultTaskViewPreset: 'my-prs',
experimentalNewWorktreeCardStyle: true,
compactWorktreeCards: true,
minimaxGroupId: 'group-42',
minimaxUsageModels: 'general,abab6.5',
minimaxEndpoint: 'cn',
defaultRepoSelection: settings.defaultRepoSelection,
defaultLinearTeamSelection: ['team-1', 'team-2'],
githubProjects: settings.githubProjects
})
)
expect(runtime.updateClientSettings).toHaveBeenCalledWith({
worktreeVisibilityDefaults: {
external: 'show',
customSources: [{ id: 'team', rootPath: '/srv/team' }],
sourcePreferences: {
builtIn: { claude: 'show' },
custom: { team: 'hide' }
}
},
defaultTuiAgent: 'codex',
disabledTuiAgents: ['claude'],
defaultTaskSource: 'linear',
visibleTaskProviders: ['github', 'linear'],
defaultTaskViewPreset: 'my-prs',
experimentalNewWorktreeCardStyle: true,
compactWorktreeCards: true,
minimaxGroupId: 'group-42',
minimaxUsageModels: 'general,abab6.5',
minimaxEndpoint: 'cn',
defaultRepoSelection: settings.defaultRepoSelection,
defaultLinearTeamSelection: ['team-1', 'team-2'],
githubProjects: settings.githubProjects
})
expect(response).toMatchObject({ ok: true, result: { settings } })
vi.mocked(runtime.updateClientSettings).mockClear()
await dispatcher.dispatch(
makeRequest('settings.update', {
defaultTaskSource: 'jira',
visibleTaskProviders: ['github', 'jira']
})
)
expect(runtime.updateClientSettings).toHaveBeenCalledWith({
defaultTaskSource: 'jira',
visibleTaskProviders: ['github', 'jira']
})
})
it('normalizes manual bot-author overrides before persisting', async () => {
const runtime = {
getRuntimeId: () => 'test-runtime',
updateClientSettings: vi.fn(() => ({}))
} as unknown as OrcaRuntimeService
const dispatcher = new RpcDispatcher({ runtime, methods: CLIENT_UI_METHODS })
await dispatcher.dispatch(
makeRequest('settings.update', {
prBotAuthorOverrides: [' GretelFlux ', 'gretelflux', 42, '', 'another-bot']
})
)
expect(runtime.updateClientSettings).toHaveBeenCalledWith({
prBotAuthorOverrides: ['another-bot', 'gretelflux']
})
})
it('loads and normalizes quick commands through the targeted payload', async () => {
const commands = [
{
id: 'review',
label: 'Review',
action: 'agent-prompt' as const,
agent: 'codex' as const,
prompt: 'Review this diff',
scope: { type: 'global' as const }
}
]
const runtime = {
getRuntimeId: () => 'test-runtime',
getClientTerminalQuickCommands: vi.fn(() => commands),
updateClientTerminalQuickCommands: vi.fn(() => commands)
} as unknown as OrcaRuntimeService
const dispatcher = new RpcDispatcher({ runtime, methods: CLIENT_UI_METHODS })
const getResponse = await dispatcher.dispatch(makeRequest('settings.getTerminalQuickCommands'))
const updateResponse = await dispatcher.dispatch(
makeRequest('settings.updateTerminalQuickCommands', {
mutation: {
type: 'upsert',
command: {
id: ' review ',
label: ' Review ',
action: 'agent-prompt',
agent: 'codex',
prompt: 'Review this diff\n',
scope: { type: 'global' }
}
}
})
)
expect(getResponse).toMatchObject({ ok: true, result: { terminalQuickCommands: commands } })
expect(runtime.updateClientTerminalQuickCommands).toHaveBeenCalledWith({
type: 'upsert',
command: commands[0]
})
expect(updateResponse).toMatchObject({
ok: true,
result: { terminalQuickCommands: commands }
})
await dispatcher.dispatch(
makeRequest('settings.updateTerminalQuickCommands', {
mutation: { type: 'delete', id: 'review' }
})
)
expect(runtime.updateClientTerminalQuickCommands).toHaveBeenLastCalledWith({
type: 'delete',
id: 'review'
})
})
it('rejects malformed quick-command mutations instead of changing persisted commands', async () => {
const runtime = {
getRuntimeId: () => 'test-runtime',
updateClientTerminalQuickCommands: vi.fn()
} as unknown as OrcaRuntimeService
const dispatcher = new RpcDispatcher({ runtime, methods: CLIENT_UI_METHODS })
for (const mutation of [
null,
'not-a-mutation',
{ type: 'delete', id: '' },
{ type: 'upsert', command: null },
{ type: 'upsert', command: { id: 'incomplete' } },
{
type: 'upsert',
command: {
id: 'unsupported-agent',
label: 'Unsupported agent',
action: 'agent-prompt',
agent: 'aider',
prompt: 'Review this diff'
}
},
{
type: 'upsert',
command: {
id: 'oversized-command',
label: 'Oversized command',
action: 'terminal-command',
command: 'x'.repeat(MAX_QUICK_COMMAND_TERMINAL_TEXT_LENGTH + 1),
appendEnter: true
}
},
{
type: 'upsert',
command: {
id: 'x'.repeat(MAX_QUICK_COMMAND_ID_LENGTH + 1),
label: 'Oversized id',
action: 'terminal-command',
command: 'true',
appendEnter: true
}
},
{
type: 'upsert',
command: {
id: 'oversized-label',
label: 'x'.repeat(MAX_QUICK_COMMAND_LABEL_LENGTH + 1),
action: 'terminal-command',
command: 'true',
appendEnter: true
}
},
{
type: 'upsert',
command: {
id: 'oversized-repo',
label: 'Oversized repo',
action: 'terminal-command',
command: 'true',
appendEnter: true,
scope: { type: 'repo', repoId: 'x'.repeat(MAX_QUICK_COMMAND_REPO_ID_LENGTH + 1) }
}
},
{
type: 'upsert',
command: {
id: 'oversized-prompt',
label: 'Oversized prompt',
action: 'agent-prompt',
agent: 'codex',
prompt: 'x'.repeat(MAX_QUICK_COMMAND_AGENT_PROMPT_LENGTH + 1)
}
},
{ type: 'upsert', command: { id: 'default-pwd', label: 'Removed', command: 'pwd' } }
]) {
const response = await dispatcher.dispatch(
makeRequest('settings.updateTerminalQuickCommands', { mutation })
)
expect(response).toMatchObject({
ok: false,
error: { code: 'invalid_argument' }
})
}
expect(runtime.updateClientTerminalQuickCommands).not.toHaveBeenCalled()
})
it('caps oversized bot-author override payloads', async () => {
const runtime = {
getRuntimeId: () => 'test-runtime',
updateClientSettings: vi.fn(() => ({}))
} as unknown as OrcaRuntimeService
const dispatcher = new RpcDispatcher({ runtime, methods: CLIENT_UI_METHODS })
await dispatcher.dispatch(
makeRequest('settings.update', {
prBotAuthorOverrides: Array.from(
{ length: 600 },
(_, i) => `bot-${String(i).padStart(4, '0')}`
)
})
)
const [update] = vi.mocked(runtime.updateClientSettings).mock.calls[0]!
expect((update as { prBotAuthorOverrides: string[] }).prBotAuthorOverrides).toHaveLength(500)
})
it('routes bot-author deltas to the runtime-owned atomic update', async () => {
const settings = { prBotAuthorOverrides: ['alice', 'bob'] }
const runtime = {
getRuntimeId: () => 'test-runtime',
updateClientPRBotAuthorOverride: vi.fn(() => settings)
} as unknown as OrcaRuntimeService
const dispatcher = new RpcDispatcher({ runtime, methods: CLIENT_UI_METHODS })
const response = await dispatcher.dispatch(
makeRequest('settings.updatePRBotAuthorOverride', { author: ' Bob ', isBot: true })
)
expect(runtime.updateClientPRBotAuthorOverride).toHaveBeenCalledWith({
author: ' Bob ',
isBot: true
})
expect(response).toMatchObject({ ok: true, result: { settings } })
})
it('returns the runtime host persisted UI state', async () => {
const ui: PersistedUIState = {
...getDefaultUIState(),
groupBy: 'none',
sortBy: 'smart',
showActiveOnly: true,
filterRepoIds: ['repo-1']
}
const runtime = {
getRuntimeId: () => 'test-runtime',
getUIState: vi.fn(() => ui)
} as unknown as OrcaRuntimeService
const dispatcher = new RpcDispatcher({ runtime, methods: CLIENT_UI_METHODS })
const response = await dispatcher.dispatch(makeRequest('ui.get'))
expect(runtime.getUIState).toHaveBeenCalledTimes(1)
expect(response).toMatchObject({ ok: true, result: { ui: omitPairingLocalUiFields(ui) } })
})
it('persists UI updates on the runtime host and returns the updated state', async () => {
const updated: PersistedUIState = {
...getDefaultUIState(),
rightSidebarOpen: false,
rightSidebarTab: 'checks',
rightSidebarExplorerView: 'search',
showActiveOnly: true,
hideAutomationGeneratedWorkspaces: true,
filterRepoIds: ['repo-1']
}
const runtime = {
getRuntimeId: () => 'test-runtime',
updateUIState: vi.fn(() => updated)
} as unknown as OrcaRuntimeService
const dispatcher = new RpcDispatcher({ runtime, methods: CLIENT_UI_METHODS })
const response = await dispatcher.dispatch(
makeRequest('ui.set', {
rightSidebarOpen: false,
rightSidebarTab: 'checks',
rightSidebarExplorerView: 'search',
showActiveOnly: true,
hideSleepingWorkspaces: true,
hideAutomationGeneratedWorkspaces: true,
filterRepoIds: ['repo-1']
})
)
expect(runtime.updateUIState).toHaveBeenCalledWith({
rightSidebarOpen: false,
rightSidebarTab: 'checks',
rightSidebarExplorerView: 'search',
showActiveOnly: true,
hideSleepingWorkspaces: true,
hideAutomationGeneratedWorkspaces: true,
filterRepoIds: ['repo-1']
})
expect(response).toMatchObject({ ok: true, result: { ui: omitPairingLocalUiFields(updated) } })
})
it('lets a paired client clear the OSC 52 default-on notice', async () => {
// Why pin this key: the update schema is strict, so an omitted field does not get
// stripped — it rejects the whole call. The renderer only logs that failure, so the
// one-shot notice would re-toast on every launch of every web/SSH/relay client.
const updated: PersistedUIState = {
...getDefaultUIState(),
osc52ClipboardDefaultOnNoticePending: false
}
const runtime = {
getRuntimeId: () => 'test-runtime',
updateUIState: vi.fn(() => updated)
} as unknown as OrcaRuntimeService
const dispatcher = new RpcDispatcher({ runtime, methods: CLIENT_UI_METHODS })
const response = await dispatcher.dispatch(
makeRequest('ui.set', { osc52ClipboardDefaultOnNoticePending: false })
)
expect(response).toMatchObject({ ok: true })
expect(runtime.updateUIState).toHaveBeenCalledWith({
osc52ClipboardDefaultOnNoticePending: false
})
})
it('does not let a paired client replace the host workspace origin filter', async () => {
const runtime = {
getRuntimeId: () => 'test-runtime',
updateUIState: vi.fn(() => getDefaultUIState())
} as unknown as OrcaRuntimeService
const dispatcher = new RpcDispatcher({ runtime, methods: CLIENT_UI_METHODS })
const response = await dispatcher.dispatch(
makeRequest('ui.set', {
hideWorkspacesFromOtherDevices: true,
sidebarWidth: 280
})
)
expect(response).toMatchObject({ ok: true })
expect(runtime.updateUIState).toHaveBeenCalledWith({ sidebarWidth: 280 })
})
it('accepts persisted literal UI arrays and nested UI state', async () => {
const updated: PersistedUIState = {
...getDefaultUIState(),
worktreeCardProperties: ['status', 'branch', 'automation', 'inline-agents'],
_worktreeCardModeDefaulted: true,
statusBarItems: ['codex', 'kimi', 'minimax', 'grok', 'antigravity', 'ports'],
_portsStatusBarDefaultAdded: true,
_kimiStatusBarDefaultAdded: true,
_minimaxStatusBarDefaultAdded: true,
_grokStatusBarDefaultAdded: true,
_antigravityStatusBarDefaultAdded: true,
taskResumeState: {
githubMode: 'items',
githubItemsQuery: 'is:open',
githubProjectHiddenFieldIdsByView: {
'project-1:view-1': ['field-1']
},
linearMode: 'projects',
linearContext: {
kind: 'project',
id: 'project-9',
workspaceId: 'workspace-1',
model: 'project'
},
jiraPreset: 'assigned',
jiraQuery: 'ENG'
},
workspaceCleanup: {
dismissals: {
'repo::/worktree': {
worktreeId: 'repo::/worktree',
dismissedAt: 123,
fingerprint: 'abc',
classifierVersion: 2
}
}
},
featureTipsSeenIds: ['voice-dictation'],
featureInteractions: {
tasks: { firstInteractedAt: 100, interactionCount: 2 }
},
contextualToursSeenIds: ['tasks'],
contextualToursAutoEligible: true,
usageEmptyStateDismissed: true,
browserDefaultZoomLevel: 1.5,
manualRepoOrder: [{ hostId: 'runtime:node-b', repoId: 'repo-b' }]
}
const runtime = {
getRuntimeId: () => 'test-runtime',
updateUIState: vi.fn(() => updated)
} as unknown as OrcaRuntimeService
const dispatcher = new RpcDispatcher({ runtime, methods: CLIENT_UI_METHODS })
const payload = {
worktreeCardProperties: ['status', 'branch', 'automation', 'inline-agents'],
_worktreeCardModeDefaulted: true,
statusBarItems: ['codex', 'kimi', 'minimax', 'grok', 'antigravity', 'ports'],
_portsStatusBarDefaultAdded: true,
_kimiStatusBarDefaultAdded: true,
_minimaxStatusBarDefaultAdded: true,
_grokStatusBarDefaultAdded: true,
_antigravityStatusBarDefaultAdded: true,
taskResumeState: {
githubMode: 'items',
githubItemsQuery: 'is:open',
githubProjectHiddenFieldIdsByView: {
'project-1:view-1': ['field-1']
},
linearMode: 'projects',
linearContext: {
kind: 'project',
id: 'project-9',
workspaceId: 'workspace-1',
model: 'project'
},
jiraPreset: 'assigned',
jiraQuery: 'ENG'
},
workspaceCleanup: {
dismissals: {
'repo::/worktree': {
worktreeId: 'repo::/worktree',
dismissedAt: 123,
fingerprint: 'abc',
classifierVersion: 2
}
}
},
featureTipsSeenIds: ['voice-dictation'],
featureInteractions: {
tasks: { firstInteractedAt: 100, interactionCount: 2 }
},
contextualToursSeenIds: ['tasks'],
contextualToursAutoEligible: true,
usageEmptyStateDismissed: true,
browserDefaultZoomLevel: 1.5,
manualRepoOrder: [{ hostId: 'runtime:node-b', repoId: 'repo-b' }]
}
const response = await dispatcher.dispatch(makeRequest('ui.set', payload))
const { manualRepoOrder: _desktopOwnedOrder, ...forwarded } = payload
expect(runtime.updateUIState).toHaveBeenCalledWith({
...forwarded,
worktreeCardProperties: ['status', 'unread', 'branch', 'automation', 'inline-agents']
})
expect(response).toMatchObject({ ok: true, result: { ui: omitPairingLocalUiFields(updated) } })
})
// Why one case per field: the schema is strict, so a single unlisted key makes
// the dispatcher reject the ENTIRE ui.set payload with invalid_argument instead
// of stripping it. A combined payload would pass as soon as any one field were
// restored, hiding the rest of the drift.
it.each([
['taskResumeState.linearMode', { taskResumeState: { linearMode: 'projects' } }],
[
'taskResumeState.linearContext',
{
taskResumeState: {
linearContext: { kind: 'project', id: 'project-9', workspaceId: 'workspace-1' }
}
}
],
['taskResumeState.jiraPreset', { taskResumeState: { jiraPreset: 'assigned' } }],
['taskResumeState.jiraQuery', { taskResumeState: { jiraQuery: 'ENG' } }],
['activeView', { activeView: 'tasks' }],
['showDotfilesByWorktree', { showDotfilesByWorktree: { 'repo::/worktree': true } }],
['setupGuideSidebarDismissed', { setupGuideSidebarDismissed: true }],
['setupGuideBrowserMilestoneMigrated', { setupGuideBrowserMilestoneMigrated: true }],
[
'setupGuideBrowserMilestoneLegacyComplete',
{ setupGuideBrowserMilestoneLegacyComplete: true }
],
['browserImportHintHidden', { browserImportHintHidden: true }],
['mobileEmulatorTabIntroDismissed', { mobileEmulatorTabIntroDismissed: true }],
['mobileEmulatorAgentSetupDismissed', { mobileEmulatorAgentSetupDismissed: true }],
['alwaysShowDefaultBranchWorkspace', { alwaysShowDefaultBranchWorkspace: false }]
])('accepts %s, which the renderer persists through ui.set', async (_label, payload) => {
const runtime = {
getRuntimeId: () => 'test-runtime',
updateUIState: vi.fn(() => getDefaultUIState())
} as unknown as OrcaRuntimeService
const dispatcher = new RpcDispatcher({ runtime, methods: CLIENT_UI_METHODS })
const response = await dispatcher.dispatch(makeRequest('ui.set', payload))
expect(response).toMatchObject({ ok: true })
expect(runtime.updateUIState).toHaveBeenCalledWith(payload)
})
it('accepts the whole debounced App writer payload', async () => {
const runtime = {
getRuntimeId: () => 'test-runtime',
updateUIState: vi.fn(() => getDefaultUIState())
} as unknown as OrcaRuntimeService
const dispatcher = new RpcDispatcher({ runtime, methods: CLIENT_UI_METHODS })
// Mirrors App.tsx's 150ms writer: one unlisted key here dropped every other
// preference in the same call for paired web/SSH/relay clients.
const payload = {
sidebarWidth: 280,
rightSidebarOpen: true,
rightSidebarTab: 'explorer',
rightSidebarExplorerView: 'files',
rightSidebarWidth: 320,
markdownTocPanelWidth: 200,
groupBy: 'repo',
sortBy: 'smart',
projectOrderBy: 'manual',
showActiveOnly: false,
hideSleepingWorkspaces: false,
showSleepingWorkspaces: true,
hideDefaultBranchWorkspace: false,
hideAutomationGeneratedWorkspaces: false,
alwaysShowDefaultBranchWorkspace: true,
showDotfilesByWorktree: { 'repo::/worktree': true },
filterRepoIds: ['repo-1'],
acknowledgedAgentsByPaneKey: { 'pane-1': 123 }
}
const response = await dispatcher.dispatch(makeRequest('ui.set', payload))
expect(response).toMatchObject({ ok: true })
expect(runtime.updateUIState).toHaveBeenCalledWith(payload)
})
it('records a feature interaction through the runtime host', async () => {
const updated: PersistedUIState = {
...getDefaultUIState(),
featureInteractions: {
tasks: { firstInteractedAt: 100, interactionCount: 1 }
}
}
const runtime = {
getRuntimeId: () => 'test-runtime',
recordFeatureInteraction: vi.fn(() => updated)
} as unknown as OrcaRuntimeService
const dispatcher = new RpcDispatcher({ runtime, methods: CLIENT_UI_METHODS })
const response = await dispatcher.dispatch(makeRequest('ui.recordFeatureInteraction', 'tasks'))
expect(runtime.recordFeatureInteraction).toHaveBeenCalledWith('tasks')
expect(response).toMatchObject({ ok: true, result: { ui: omitPairingLocalUiFields(updated) } })
})
it('rejects unknown and malformed UI update fields', async () => {
const runtime = {
getRuntimeId: () => 'test-runtime',
updateUIState: vi.fn()
} as unknown as OrcaRuntimeService
const dispatcher = new RpcDispatcher({ runtime, methods: CLIENT_UI_METHODS })
const response = await dispatcher.dispatch(
makeRequest('ui.set', { showActiveOnly: 'yes', unknownField: true })
)
expect(response).toMatchObject({ ok: false, error: { code: 'invalid_argument' } })
expect(runtime.updateUIState).not.toHaveBeenCalled()
})
// Why the contract flipped: an unknown VALUE used to fail the whole batch, so
// one drifted enum member took sidebar widths, filters and agent acks down
// with it. Unknown KEYS still reject — the parity assertions catch those.
it.each([
['worktree card property', { worktreeCardProperties: ['status', 'pr-status'] }],
['feature interaction id', { featureInteractions: { unknown: { firstInteractedAt: 100 } } }],
['feature tip id', { featureTipsSeenIds: ['voice-dictation', 'unknown-tip'] }],
['right sidebar tab', { rightSidebarTab: 'not-a-tab' }]
])('drops an unknown %s instead of rejecting the batch around it', async (_label, drifted) => {
const runtime = {
getRuntimeId: () => 'test-runtime',
updateUIState: vi.fn(() => getDefaultUIState())
} as unknown as OrcaRuntimeService
const dispatcher = new RpcDispatcher({ runtime, methods: CLIENT_UI_METHODS })
const response = await dispatcher.dispatch(
makeRequest('ui.set', { ...drifted, sidebarWidth: 280, filterRepoIds: ['repo-1'] })
)
expect(response).toMatchObject({ ok: true })
expect(runtime.updateUIState).toHaveBeenCalledWith({
sidebarWidth: 280,
filterRepoIds: ['repo-1']
})
})
it('accepts the Jira issue card property across the runtime UI boundary', async () => {
const updated: PersistedUIState = {
...getDefaultUIState(),
worktreeCardProperties: ['status', 'unread', 'jira-issue']
}
const runtime = {
getRuntimeId: () => 'test-runtime',
updateUIState: vi.fn(() => updated)
} as unknown as OrcaRuntimeService
const dispatcher = new RpcDispatcher({ runtime, methods: CLIENT_UI_METHODS })
const response = await dispatcher.dispatch(
makeRequest('ui.set', { worktreeCardProperties: ['status', 'jira-issue'] })
)
expect(runtime.updateUIState).toHaveBeenCalledWith({
worktreeCardProperties: ['status', 'unread', 'jira-issue']
})
expect(response).toMatchObject({ ok: true, result: { ui: omitPairingLocalUiFields(updated) } })
})
it('accepts every worktree card property the shared union defines', async () => {
const runtime = {
getRuntimeId: () => 'test-runtime',
updateUIState: vi.fn(() => getDefaultUIState())
} as unknown as OrcaRuntimeService
const dispatcher = new RpcDispatcher({ runtime, methods: CLIENT_UI_METHODS })
// 'cli' was missing from the schema, so Settings → Default card mode sent a
// payload the host rejected outright.
const response = await dispatcher.dispatch(
makeRequest('ui.set', { worktreeCardProperties: [...DEFAULT_WORKTREE_CARD_PROPERTIES] })
)
expect(response).toMatchObject({ ok: true })
expect(runtime.updateUIState).toHaveBeenCalledWith({
worktreeCardProperties: [...DEFAULT_WORKTREE_CARD_PROPERTIES]
})
})
it.each(['workspaces', 'pr-checks', 'plugin:acme.tools/inspector'])(
'accepts the %s right sidebar tab a paired client can be sitting on',
async (rightSidebarTab) => {
const runtime = {
getRuntimeId: () => 'test-runtime',
updateUIState: vi.fn(() => getDefaultUIState())
} as unknown as OrcaRuntimeService
const dispatcher = new RpcDispatcher({ runtime, methods: CLIENT_UI_METHODS })
const response = await dispatcher.dispatch(
makeRequest('ui.set', { rightSidebarTab, sidebarWidth: 280 })
)
expect(response).toMatchObject({ ok: true })
expect(runtime.updateUIState).toHaveBeenCalledWith({ rightSidebarTab, sidebarWidth: 280 })
}
)
it('rejects star-nag persisted state mutations from remote clients', async () => {
const runtime = {
getRuntimeId: () => 'test-runtime',
updateUIState: vi.fn()
} as unknown as OrcaRuntimeService
const dispatcher = new RpcDispatcher({ runtime, methods: CLIENT_UI_METHODS })
const response = await dispatcher.dispatch(
makeRequest('ui.set', {
starNagBaselineAgents: 10,
starNagAppVersion: '1.2.3',
starNagAgentValueMomentAppVersion: '1.2.3',
starNagNextThreshold: 70,
starNagCompleted: true,
starNagDeferredUntil: null
})
)
expect(response).toMatchObject({ ok: false, error: { code: 'invalid_argument' } })
expect(runtime.updateUIState).not.toHaveBeenCalled()
})
it('strips retired worktree card properties from legacy clients', async () => {
const updated: PersistedUIState = {
...getDefaultUIState(),
worktreeCardProperties: ['status', 'issue']
}
const runtime = {
getRuntimeId: () => 'test-runtime',
updateUIState: vi.fn(() => updated)
} as unknown as OrcaRuntimeService
const dispatcher = new RpcDispatcher({ runtime, methods: CLIENT_UI_METHODS })
const response = await dispatcher.dispatch(
makeRequest('ui.set', { worktreeCardProperties: ['status', 'unread', 'ci', 'pr', 'issue'] })
)
expect(runtime.updateUIState).toHaveBeenCalledWith({
worktreeCardProperties: ['status', 'unread', 'ci', 'issue', 'pr']
})
expect(response).toMatchObject({ ok: true, result: { ui: omitPairingLocalUiFields(updated) } })
})
it('rejects each star-nag persisted state mutation field from remote clients', async () => {
const runtime = {
getRuntimeId: () => 'test-runtime',
updateUIState: vi.fn()
} as unknown as OrcaRuntimeService
const dispatcher = new RpcDispatcher({ runtime, methods: CLIENT_UI_METHODS })
const forbiddenPayloads = [
{ starNagBaselineAgents: 10 },
{ starNagAppVersion: '1.2.3' },
{ starNagAgentValueMomentAppVersion: '1.2.3' },
{ starNagNextThreshold: 70 },
{ starNagCompleted: true },
{ starNagDeferredUntil: null }
]
for (const payload of forbiddenPayloads) {
const response = await dispatcher.dispatch(makeRequest('ui.set', payload))
expect(response).toMatchObject({ ok: false, error: { code: 'invalid_argument' } })
}
expect(runtime.updateUIState).not.toHaveBeenCalled()
})
it('rejects unknown feature interaction ids for increment RPC', async () => {
const runtime = {
getRuntimeId: () => 'test-runtime',
recordFeatureInteraction: vi.fn()
} as unknown as OrcaRuntimeService
const dispatcher = new RpcDispatcher({ runtime, methods: CLIENT_UI_METHODS })
const response = await dispatcher.dispatch(
makeRequest('ui.recordFeatureInteraction', 'unknown-feature')
)
expect(response).toMatchObject({ ok: false, error: { code: 'invalid_argument' } })
expect(runtime.recordFeatureInteraction).not.toHaveBeenCalled()
})
})