Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions packages/cli/src/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2104,6 +2104,7 @@ export async function loadCliConfig(
: (settings.memory?.autoSkillConfirm ?? true),
fastModel: settings.fastModel || undefined,
visionModel: settings.visionModel || undefined,
compactionModel: settings.compactionModel || undefined,
// Use separated hooks if provided, otherwise fall back to merged hooks
userHooks:
bareMode || safeMode
Expand Down
11 changes: 11 additions & 0 deletions packages/cli/src/config/settingsSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1285,6 +1285,17 @@ const SETTINGS_SCHEMA = {
showInDialog: false,
},

compactionModel: {
type: 'string',
label: 'Compaction Model',
category: 'Model',
requiresRestart: false,
default: '',
description:
'Model used for chat compression (auto-compaction). Set with /model --compaction. Leave empty to fall back to fastModel or the main model. A smaller/faster model reduces compression latency and cost.',
showInDialog: false,
},

model: {
type: 'object',
label: 'Model',
Expand Down
3 changes: 3 additions & 0 deletions packages/cli/src/ui/AppContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1104,6 +1104,7 @@ export const AppContainer = (props: AppContainerProps) => {
isFastModelMode,
isVoiceModelMode,
isVisionModelMode,
isCompactionModelMode,
openModelDialog,
closeModelDialog,
} = useModelCommand();
Expand Down Expand Up @@ -3676,6 +3677,7 @@ export const AppContainer = (props: AppContainerProps) => {
isFastModelMode,
isVoiceModelMode,
isVisionModelMode,
isCompactionModelMode,
isTrustDialogOpen,
activeArenaDialog,
isPermissionsDialogOpen,
Expand Down Expand Up @@ -3816,6 +3818,7 @@ export const AppContainer = (props: AppContainerProps) => {
isFastModelMode,
isVoiceModelMode,
isVisionModelMode,
isCompactionModelMode,
isTrustDialogOpen,
activeArenaDialog,
isPermissionsDialogOpen,
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/ui/commands/modelCommand.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ describe('modelCommand', () => {
it('should have the correct name and description', () => {
expect(modelCommand.name).toBe('model');
expect(modelCommand.description).toBe(
'Switch the model for this session (--fast for suggestion model, --voice for voice transcription model, --vision for the vision bridge model, [model-id] to switch immediately, or [model-id] [prompt] to run a one-off prompt on another model; the inline prompt is sent verbatim without @file expansion).',
'Switch the model for this session (--fast for suggestion model, --voice for voice transcription model, --vision for the vision bridge model, --compaction for chat compression model, [model-id] to switch immediately, or [model-id] [prompt] to run a one-off prompt on another model; the inline prompt is sent verbatim without @file expansion).',
);
});

Expand Down
123 changes: 114 additions & 9 deletions packages/cli/src/ui/commands/modelCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ const MAIN_MODEL_CONFIGURATION_HINT =
const FAST_MODEL_CONFIGURATION_HINT =
'Configure models in settings.modelProviders and ensure the required environment variables are set. In interactive mode, run /auth to configure or switch providers, or run /model --fast without a model to choose from configured models.';

const COMPACTION_MODEL_CONFIGURATION_HINT =
'Configure models in settings.modelProviders and ensure the required environment variables are set. In interactive mode, run /auth to configure or switch providers, or run /model --compaction without a model to choose from configured models.';

const VISION_MODEL_CONFIGURATION_HINT =
'Configure an image-capable model in settings.modelProviders and ensure the required environment variables are set. Run /model --vision <model-id> to set it, or leave it unset to auto-pick a same-provider vision model.';

Expand Down Expand Up @@ -92,7 +95,7 @@ async function switchMainModel(
}

function formatUnavailableModelMessage(
kind: 'Model' | 'Fast model' | 'Vision model',
kind: 'Model' | 'Fast model' | 'Vision model' | 'Compaction model',
modelName: string,
authType: AuthType,
availableModels: AvailableModel[],
Expand All @@ -110,7 +113,9 @@ function formatUnavailableModelMessage(
? FAST_MODEL_CONFIGURATION_HINT
: kind === 'Vision model'
? VISION_MODEL_CONFIGURATION_HINT
: MAIN_MODEL_CONFIGURATION_HINT;
: kind === 'Compaction model'
? COMPACTION_MODEL_CONFIGURATION_HINT
: MAIN_MODEL_CONFIGURATION_HINT;

return (
`${kind} '${modelName}' is not available for auth type '${authType}'.\n` +
Expand All @@ -119,10 +124,10 @@ function formatUnavailableModelMessage(
);
}

// Fast and vision share the same "not configured for any auth type" message
// Fast, vision, and compaction share the same "not configured for any auth type" message
// shape, differing only in the label and the configuration hint.
function formatUnavailableAuxModelMessage(
label: 'Fast model' | 'Vision model',
label: 'Fast model' | 'Vision model' | 'Compaction model',
modelName: string,
availableModels: AvailableModel[],
hint: string,
Expand Down Expand Up @@ -166,6 +171,18 @@ function formatUnavailableVisionModelMessage(
);
}

function formatUnavailableCompactionModelMessage(
modelName: string,
availableModels: AvailableModel[],
): string {
return formatUnavailableAuxModelMessage(
'Compaction model',
modelName,
availableModels,
COMPACTION_MODEL_CONFIGURATION_HINT,
);
}

function formatAmbiguousVisionModelMessage(
modelName: string,
matchingModels: AvailableModel[],
Expand Down Expand Up @@ -243,15 +260,15 @@ function formatUnavailableVoiceModelMessage(
// Get an array of the available model IDs as strings, filtered by mode
function getAvailableModelIds(
context: CommandContext,
mode: 'main' | 'fast' | 'voice' | 'vision' = 'main',
mode: 'main' | 'fast' | 'voice' | 'vision' | 'compaction' = 'main',
) {
const { services } = context;
const { config } = services;
if (!config) {
return [];
}
const availableModels = config.getAvailableModels().filter((m) => {
if (mode === 'fast') return !m.voiceOnly;
if (mode === 'fast' || mode === 'compaction') return !m.voiceOnly;
if (mode === 'voice') return !m.fastOnly;
// 'vision' and 'main' both exclude fast/voice-only models.
return !m.fastOnly && !m.voiceOnly;
Expand All @@ -264,10 +281,10 @@ export const modelCommand: SlashCommand = {
completionPriority: 100,
get description() {
return t(
'Switch the model for this session (--fast for suggestion model, --voice for voice transcription model, --vision for the vision bridge model, [model-id] to switch immediately, or [model-id] [prompt] to run a one-off prompt on another model; the inline prompt is sent verbatim without @file expansion).',
'Switch the model for this session (--fast for suggestion model, --voice for voice transcription model, --vision for the vision bridge model, --compaction for chat compression model, [model-id] to switch immediately, or [model-id] [prompt] to run a one-off prompt on another model; the inline prompt is sent verbatim without @file expansion).',
);
},
argumentHint: '[--fast|--voice|--vision] [<model-id>] | <model-id> <prompt>',
argumentHint: '[--fast|--voice|--vision|--compaction] [<model-id>] | <model-id> <prompt>',
kind: CommandKind.BUILT_IN,
supportedModes: ['interactive', 'non_interactive', 'acp'] as const,
completion: async (context, partialArg) => {
Expand All @@ -289,13 +306,19 @@ export const modelCommand: SlashCommand = {
'Set the image-capable model used to transcribe images for a text-only main model',
),
},
{
value: '--compaction',
description: t(
'Set the model used for chat compression (auto-compaction)',
),
},
].filter((item) => item.value.startsWith(partialArg));
if (flagCompletions.length > 0) {
return flagCompletions;
}
const trimmed = partialArg.trim();
if (trimmed) {
let mode: 'main' | 'fast' | 'voice' | 'vision' = 'main';
let mode: 'main' | 'fast' | 'voice' | 'vision' | 'compaction' = 'main';
let modelPrefix = trimmed;
if (trimmed.startsWith('--fast ')) {
mode = 'fast';
Expand All @@ -306,6 +329,9 @@ export const modelCommand: SlashCommand = {
} else if (trimmed.startsWith('--vision ')) {
mode = 'vision';
modelPrefix = trimmed.slice('--vision '.length);
} else if (trimmed.startsWith('--compaction ')) {
mode = 'compaction';
modelPrefix = trimmed.slice('--compaction '.length);
}
return getAvailableModelIds(context, mode).filter((id) =>
id.startsWith(modelPrefix),
Expand Down Expand Up @@ -608,6 +634,85 @@ export const modelCommand: SlashCommand = {
};
}

const isCompactionModelCommand =

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Critical] Missing auth-type validation gate

The --fast handler (line 463) explicitly checks config.getContentGeneratorConfig()?.authType and returns an error when unavailable. This --compaction handler skips that gate entirely, going straight to resolveModelId(modelName).

When no auth type is configured, /model --compaction <id> produces a confusing error path instead of the clear "Authentication type not available." message that --fast shows.

Suggested change
const isCompactionModelCommand =
const isCompactionModelCommand =
args === '--compaction' || args.startsWith('--compaction ');
if (isCompactionModelCommand) {
const contentGeneratorConfig = config.getContentGeneratorConfig();
const authType = contentGeneratorConfig?.authType;
if (!authType) {
return {
type: 'message',
messageType: 'error',
content: t('Authentication type not available.'),
};
}
const modelName = args.replace('--compaction', '').trim();

— qwen3.7-max via Qwen Code /review

args === '--compaction' || args.startsWith('--compaction ');
if (isCompactionModelCommand) {
const modelName = args.replace('--compaction', '').trim();
if (!modelName) {
if (context.executionMode !== 'interactive') {
const compactionModel =
context.services.settings?.merged?.compactionModel?.trim() ||
t('not set (falls back to fast model, then main model)');
return {
type: 'message',
messageType: 'info',
content: t(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] Misleading "clear" instruction — no clear mechanism exists

This message tells users to use "/model --compaction " to clear the override, but when an empty/whitespace model name is passed, the code only shows the info message or opens the dialog — it never calls setCompactionModel(undefined) or persistSetting(settings, 'compactionModel', '').

Either implement the clear path (mirroring how --fast could work), or remove the false instruction from this message.

— qwen3.7-max via Qwen Code /review

'Current compaction model: {{compactionModel}}\nUse "/model --compaction <model-id>" to set compaction model, or "/model --compaction " to clear the override.',
{ compactionModel },
),
};
}
return {
type: 'dialog',
dialog: 'compaction-model',
};
}
if (!settings) {
return {
type: 'message',
messageType: 'error',
content: t('Settings service not available.'),
};
}

const selector = (() => {
try {
return resolveModelId(modelName);
} catch {
return undefined;
}
})();
if (!selector) {
return {
type: 'message',
messageType: 'error',
content: formatUnavailableCompactionModelMessage(modelName, []),
};
}

const availableModels = (
selector.authType
? config.getAvailableModelsForAuthType(selector.authType)
: config.getAllConfiguredModels()
).filter((m) => !m.voiceOnly);
if (!availableModels.some((model) => model.id === selector.modelId)) {
return {
type: 'message',
messageType: 'error',
content: selector.authType
? formatUnavailableModelMessage(
'Compaction model',
selector.modelId,
selector.authType,
availableModels,
)
: formatUnavailableCompactionModelMessage(
modelName,
availableModels,
),
};
}

persistSetting(settings, 'compactionModel', modelName);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] Zero test coverage for /model --compaction command handler

85+ lines of new command handling code with no tests. The existing --fast and --vision flags have extensive test coverage that should be mirrored here. Missing tests for:

  • Setting a valid compaction model: /model --compaction <model-id>
  • Rejecting unavailable compaction models (both bare and auth-qualified)
  • Non-interactive mode showing current compaction model status
  • Dialog action when /model --compaction is run with no args in interactive mode
  • Config.getCompactionModel() / setCompactionModel() / resolveCompactionModelSelector() methods
  • isCompactionModelMode state management in useModelCommand

— qwen3.7-max via Qwen Code /review

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Critical] Cross-provider model ambiguity — raw modelName persisted without authType qualifier

The --vision handler (line 614-617) builds qualifiedModelName = \${authType}:${modelId}`before persisting. This handler persists the baremodelName. When the same model ID (e.g., gpt-4o`) is configured under multiple providers, compression traffic could silently route to a different provider than the user intended.

Suggested change
persistSetting(settings, 'compactionModel', modelName);
const matched = availableModels.find((m) => m.id === selector.modelId);
const qualifiedModelName = selector.authType
? `${selector.authType}:${selector.modelId}`
: matched?.authType
? `${matched.authType}:${selector.modelId}`
: modelName;
persistSetting(settings, 'compactionModel', qualifiedModelName);
config.setCompactionModel(qualifiedModelName);

— qwen3.7-max via Qwen Code /review

// Sync runtime Config so the compression service picks it up immediately.
config.setCompactionModel(modelName);
return {
type: 'message',
messageType: 'info',
content: t('Compaction Model') + ': ' + modelName,
};
}

const contentGeneratorConfig = config.getContentGeneratorConfig();
if (!contentGeneratorConfig) {
return {
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/ui/commands/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ export interface OpenDialogActionReturn {
| 'fast-model'
| 'voice-model'
| 'vision-model'
| 'compaction-model'
| 'subagent_create'
| 'subagent_list'
| 'skills_manage'
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/ui/components/DialogManager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@ export const DialogManager = ({
isFastModelMode={uiState.isFastModelMode}
isVoiceModelMode={uiState.isVoiceModelMode}
isVisionModelMode={uiState.isVisionModelMode}
isCompactionModelMode={uiState.isCompactionModelMode}
/>
);
}
Expand Down
22 changes: 18 additions & 4 deletions packages/cli/src/ui/components/ModelDialog.tsx

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Critical] The newly added filter condition (isCompactionModelMode || !m.visionOnly) silently prevents vision-only models from appearing in the /model --vision dialog.

availableModelEntries filters registry models with two back-to-back conditions:

(isVisionModelMode || !m.visionOnly) &&   // ← existing line: shows all models in vision mode
(isCompactionModelMode || !m.visionOnly), // ← new line added by this PR

Both conditions are ANDed. When isVisionModelMode = true and isCompactionModelMode = false (i.e., the user runs /model --vision):

  • Condition 1 evaluates to true || !m.visionOnly = true (correct — include vision-only models)
  • Condition 2 evaluates to false || !m.visionOnly = !m.visionOnly (wrong — excludes vision-only models)

The combined AND result is !m.visionOnly, so vision-only models can never appear in the selection list, defeating the purpose of condition 1. The fix is either to remove the new line (since condition 1 already handles the vision filter) or to add isVisionModelMode to the new guard:

(isCompactionModelMode || isVisionModelMode || !m.visionOnly),

— claude-sonnet-4-6 via Qwen Code /review


Generated by Claude Code

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Critical] handleSelect has no isCompactionModelMode branch. Selecting a model while in compaction mode falls through to config.switchModel(), which switches the primary chat model instead of persisting a compaction model.

The fast/voice/vision handlers each early-return with mode-specific persistence (settings.setValue(scope, 'fastModel', ...), 'visionModel', etc.). The compaction mode is missing an equivalent:

if (isCompactionModelMode) {
  const compactionModel = encodeAuxModelSelector(selected);
  const scope = getPersistScopeForModelSelection(settings);
  settings.setValue(scope, 'compactionModel', compactionModel);
  config?.setCompactionModel?.(compactionModel);
  uiState?.historyManager.addItem({ type: 'success', text: `Compaction Model: ${compactionModel}` }, Date.now());
  onClose();
  return;
}

Without this, /model --compaction + Enter overwrites the user's primary model setting, which is a destructive side effect of what should be a narrowly-scoped compaction model selection.

— claude-sonnet-4-6 via Qwen Code /review


Generated by Claude Code

Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ interface ModelDialogProps {
isFastModelMode?: boolean;
isVoiceModelMode?: boolean;
isVisionModelMode?: boolean;
isCompactionModelMode?: boolean;
}

function maskApiKey(apiKey: string | undefined): string {
Expand Down Expand Up @@ -239,6 +240,7 @@ export function ModelDialog({
isFastModelMode,
isVoiceModelMode,
isVisionModelMode,
isCompactionModelMode,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] Three isCompactionModelMode integration gaps in this component

isCompactionModelMode is destructured here but not used in several places where the other mode flags are checked:

  1. Dialog title (~line 790): No isCompactionModelMode branch in the title ternary — falls through to generic "Select Model" instead of "Select Compaction Model".

  2. Left-arrow escape (~line 491): useKeypress handler checks isFastModelMode || isVoiceModelMode || isVisionModelMode but omits isCompactionModelMode — left arrow won't dismiss the compaction dialog.

  3. Preferred entry (~line 430-483): No preferredCompactionModelEntry computed — dialog opens with cursor at the wrong position instead of highlighting the currently configured compaction model.

Each should mirror the pattern used by isFastModelMode/isVisionModelMode.

— qwen3.7-max via Qwen Code /review

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] Left-arrow dismiss and preferred-entry gaps for isCompactionModelMode

Two integration points missed beyond the existing gaps already noted on this component:

  1. Left-arrow handler (~line 506): (isFastModelMode || isVoiceModelMode || isVisionModelMode) omits isCompactionModelMode. In all other specialized pickers, left-arrow dismisses the dialog.

  2. Preferred entry (~line 440): The preferredKey resolution chain has no preferredCompactionModelEntry, so the current compaction model is never highlighted in the picker.

— qwen3.7-max via Qwen Code /review

}: ModelDialogProps): React.JSX.Element {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Critical] handleSelect is missing an isCompactionModelMode handler block

The callback has handlers for isVoiceModelMode, isFastModelMode, and isVisionModelMode but no if (isCompactionModelMode) branch. When a user opens the compaction model picker via /model --compaction (no model-id) and selects a model from the dialog, the code falls through to the main model selection path (config.switchModel(...)), silently changing the user's primary model instead of setting the compaction model.

Add an if (isCompactionModelMode) block mirroring the isFastModelMode pattern:

  1. Encode the selector via encodeAuxModelSelector(selected)
  2. Persist via settings.setValue(scope, 'compactionModel', fastModel)
  3. Call config?.setCompactionModel(fastModel)
  4. Add a history item and return

Also add isCompactionModelMode to the handleSelect dependency array (currently missing — the callback won't re-create when compaction mode activates, causing a stale closure).

— qwen3.7-max via Qwen Code /review

const config = useContext(ConfigContext);
const uiState = useContext(UIStateContext);
Expand All @@ -261,7 +263,9 @@ export function ModelDialog({
(m.authType !== AuthType.QWEN_OAUTH ||
authType === AuthType.QWEN_OAUTH) &&
(isFastModelMode || !m.fastOnly) &&
(isVoiceModelMode || !m.voiceOnly),
(isVoiceModelMode || !m.voiceOnly) &&
(isVisionModelMode || !m.visionOnly) &&
(isCompactionModelMode || !m.visionOnly),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Critical] Copy-paste bug: (isCompactionModelMode || !m.visionOnly) should be (isCompactionModelMode || !m.voiceOnly)

The compaction filter reuses !m.visionOnly from the vision line above. But getAvailableModelIds (line 219) and the CLI validation (line 613) both use !m.voiceOnly for compaction mode (same as fast mode). The dialog and CLI will show different model sets: the dialog excludes vision-only models (irrelevant to text compression) but includes voice-only models (unsuitable for compression).

Suggested change
(isCompactionModelMode || !m.visionOnly),
(isCompactionModelMode || !m.voiceOnly),

— qwen3.7-max via Qwen Code /review

);

// Group registry models by authType
Expand Down Expand Up @@ -315,7 +319,14 @@ export function ModelDialog({
}

return result;
}, [authType, config, isFastModelMode, isVoiceModelMode]);
}, [
authType,
config,
isFastModelMode,
isVoiceModelMode,
isVisionModelMode,
isCompactionModelMode,
]);

const MODEL_OPTIONS = useMemo(
() =>
Expand Down Expand Up @@ -411,8 +422,11 @@ export function ModelDialog({
// Check if current model is a runtime model
// Runtime snapshot ID is already in $runtime|${authType}|${modelId} format
const activeRuntimeSnapshot =
isFastModelMode || isVoiceModelMode || isVisionModelMode
? undefined // fast/voice/vision models are never runtime model selections
isFastModelMode ||
isVoiceModelMode ||
isVisionModelMode ||
isCompactionModelMode
? undefined // fast/voice/vision/compaction models are never runtime model selections
: config?.getActiveRuntimeModelSnapshot?.();
const currentBaseUrl = config
?.getModelsConfig()
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/ui/contexts/UIActionsContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export interface UIActions {
fastModelMode?: boolean;
voiceModelMode?: boolean;
visionModelMode?: boolean;
compactionModelMode?: boolean;
}) => void;
openArenaDialog: (type: Exclude<ArenaDialogType, null>) => void;
closeArenaDialog: () => void;
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/ui/contexts/UIStateContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export interface UIState {
isFastModelMode: boolean;
isVoiceModelMode: boolean;
isVisionModelMode: boolean;
isCompactionModelMode: boolean;
isTrustDialogOpen: boolean;
activeArenaDialog: ArenaDialogType;
isPermissionsDialogOpen: boolean;
Expand Down
4 changes: 4 additions & 0 deletions packages/cli/src/ui/hooks/slashCommandProcessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ export interface SlashCommandProcessorActions {
fastModelMode?: boolean;
voiceModelMode?: boolean;
visionModelMode?: boolean;
compactionModelMode?: boolean;
}) => void;
openTrustDialog: () => void;
openPermissionsDialog: () => void;
Expand Down Expand Up @@ -833,6 +834,9 @@ export const useSlashCommandProcessor = (
case 'vision-model':
actions.openModelDialog({ visionModelMode: true });
return { type: 'handled' };
case 'compaction-model':
actions.openModelDialog({ compactionModelMode: true });
return { type: 'handled' };
case 'trust':
actions.openTrustDialog();
return { type: 'handled' };
Expand Down
Loading
Loading