Skip to content

Commit 71110a9

Browse files
CloudAgent - Remove SessionModelNotice (#4417)
* refactor(ui): remove legacy gateway model fallback notice Removes the notice informing users to upgrade Kilo CLI when using Gateway model fallback in both mobile and web applications. Co-authored-by: kiloconnect[bot] <240665456+kiloconnect[bot]@users.noreply.github.com> * refactor(cloud-agent): remove SessionModelNotice --------- Co-authored-by: eshurakov <54751+eshurakov@users.noreply.github.com> Co-authored-by: kiloconnect[bot] <240665456+kiloconnect[bot]@users.noreply.github.com>
1 parent cd4a94a commit 71110a9

7 files changed

Lines changed: 5 additions & 255 deletions

File tree

‎apps/mobile/src/components/agents/model-selector.tsx‎

Lines changed: 2 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,7 @@
11
/* eslint-disable max-lines -- The selector and picker row share model disclosure behavior. */
22
import * as Haptics from 'expo-haptics';
33
import { type Href, useRouter } from 'expo-router';
4-
import {
5-
AlertTriangle,
6-
BookOpenCheck,
7-
Brain,
8-
Check,
9-
ChevronDown,
10-
Info,
11-
RefreshCw,
12-
Star,
13-
} from 'lucide-react-native';
4+
import { BookOpenCheck, Brain, Check, ChevronDown, Star } from 'lucide-react-native';
145
import { createContext, type ReactNode, useContext, useMemo } from 'react';
156
import { Pressable, ScrollView, View } from 'react-native';
167

@@ -25,10 +16,7 @@ import {
2516
mayTrainOnYourPrompts,
2617
} from '@/lib/free-model-data-disclosure';
2718
import { type ModelOption, thinkingEffortLabel } from '@/lib/hooks/use-available-models';
28-
import {
29-
type SessionModelNotice,
30-
type SessionModelOption,
31-
} from '@/lib/hooks/use-session-model-options';
19+
import { type SessionModelOption } from '@/lib/hooks/use-session-model-options';
3220
import { useThemeColors } from '@/lib/hooks/use-theme-colors';
3321
import {
3422
type ModelPickerSelection,
@@ -344,42 +332,3 @@ export function ModelPickerOptionRow({
344332
</View>
345333
);
346334
}
347-
348-
export function SessionModelNotices({
349-
notices,
350-
onRetry,
351-
}: Readonly<{ notices: SessionModelNotice[]; onRetry: () => void }>) {
352-
const colors = useThemeColors();
353-
if (notices.length === 0) {
354-
return null;
355-
}
356-
357-
return (
358-
<View className="gap-1 border-t border-warn-tile-border bg-warn-tile-bg px-3 py-2">
359-
{notices.map(notice => {
360-
const Icon =
361-
notice.id === 'legacy' || notice.id === 'local-provider' ? Info : AlertTriangle;
362-
return (
363-
<View key={notice.id} className="flex-row items-start gap-2">
364-
<Icon size={14} color={colors.warn} />
365-
<Text selectable className="flex-1 text-xs leading-4 text-foreground">
366-
{notice.message}
367-
</Text>
368-
{notice.retry ? (
369-
<Pressable
370-
onPress={onRetry}
371-
hitSlop={12}
372-
accessibilityRole="button"
373-
accessibilityLabel="Retry loading CLI models"
374-
className="flex-row items-center gap-1 rounded-full bg-secondary px-2 py-1 active:opacity-70"
375-
>
376-
<RefreshCw size={12} color={colors.foreground} />
377-
<Text className="text-xs font-medium text-foreground">Retry</Text>
378-
</Pressable>
379-
) : null}
380-
</View>
381-
);
382-
})}
383-
</View>
384-
);
385-
}

‎apps/mobile/src/components/agents/session-detail-content.tsx‎

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,7 @@ import { toast } from 'sonner-native';
99
import { ChatComposer } from '@/components/agents/chat-composer';
1010
import { ConnectivityBanner } from '@/components/agents/connectivity-banner';
1111
import { MessageBubble } from '@/components/agents/message-bubble';
12-
import {
13-
ModelPickerSelectionScopeProvider,
14-
SessionModelNotices,
15-
} from '@/components/agents/model-selector';
12+
import { ModelPickerSelectionScopeProvider } from '@/components/agents/model-selector';
1613
import { PermissionCard } from '@/components/agents/permission-card';
1714
import { QuestionCard } from '@/components/agents/question-card';
1815
import { getSessionKeyboardContainerKind } from '@/components/agents/session-keyboard-container-state';
@@ -375,12 +372,6 @@ export function SessionDetailContent({ sessionId }: Readonly<SessionDetailConten
375372
</View>
376373
) : (
377374
<>
378-
<SessionModelNotices
379-
notices={sessionModels.notices}
380-
onRetry={() => {
381-
manager.retryRemoteModels();
382-
}}
383-
/>
384375
<ModelPickerSelectionScopeProvider
385376
selectionScope={modelPickerSelectionScope}
386377
isSelectionCurrent={isModelPickerSelectionCurrent}

‎apps/mobile/src/lib/hooks/use-session-model-options.ts‎

Lines changed: 0 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,6 @@ type SessionModelSource =
1717
| 'remote-legacy-gateway'
1818
| 'remote-unavailable';
1919

20-
export type SessionModelNotice = {
21-
id: 'loading' | 'legacy' | 'error' | 'stale' | 'truncated' | 'unavailable' | 'local-provider';
22-
message: string;
23-
retry: boolean;
24-
};
25-
2620
export type SessionModelOption = {
2721
id: string;
2822
name: string;
@@ -56,7 +50,6 @@ type SessionModelOptions = {
5650
selectedVariant: string;
5751
pickerDisabled: boolean;
5852
isLoading: boolean;
59-
notices: SessionModelNotice[];
6053
};
6154

6255
export function useSessionModelOptions({
@@ -111,7 +104,6 @@ export function buildSessionModelOptions(
111104
selectedVariant: '',
112105
pickerDisabled: false,
113106
isLoading: input.gatewayModelsLoading,
114-
notices: [],
115107
};
116108
}
117109

@@ -129,26 +121,6 @@ function buildUnavailableRemoteOptions(input: BuildSessionModelOptionsInput): Se
129121
unavailable: true,
130122
} satisfies SessionModelOption);
131123
const loading = input.remoteModelState.refresh === 'loading';
132-
const notices: SessionModelNotice[] = [
133-
loading
134-
? {
135-
id: 'loading',
136-
message: 'Checking this CLI for available models.',
137-
retry: true,
138-
}
139-
: {
140-
id: 'error',
141-
message: "Models from this CLI couldn't be loaded. Sending still uses the session model.",
142-
retry: true,
143-
},
144-
];
145-
if (currentSelection) {
146-
notices.push({
147-
id: 'unavailable',
148-
message: `${currentSelection.model.modelID} is the session model. It can't be changed until this CLI's models load.`,
149-
retry: false,
150-
});
151-
}
152124

153125
return {
154126
source: 'remote-unavailable',
@@ -157,7 +129,6 @@ function buildUnavailableRemoteOptions(input: BuildSessionModelOptionsInput): Se
157129
selectedVariant: currentSelection?.variant ?? '',
158130
pickerDisabled: true,
159131
isLoading: loading,
160-
notices,
161132
};
162133
}
163134

@@ -199,19 +170,10 @@ function buildLegacyGatewayOptions(input: BuildSessionModelOptionsInput): Sessio
199170
option => option.modelRef && modelRefsEqual(option.modelRef, currentSelection.model)
200171
)
201172
: undefined;
202-
const notices: SessionModelNotice[] = [
203-
{
204-
id: 'legacy',
205-
message:
206-
'This CLI uses Gateway model fallback. Upgrade Kilo CLI to use its configured providers and models.',
207-
retry: false,
208-
},
209-
];
210173

211174
if (currentSelection && !selectedOption) {
212175
selectedOption = createUnavailableOption(currentSelection.model);
213176
options.unshift(selectedOption);
214-
notices.push(createUnavailableNotice(currentSelection.model));
215177
}
216178

217179
const selectedVariant =
@@ -226,7 +188,6 @@ function buildLegacyGatewayOptions(input: BuildSessionModelOptionsInput): Sessio
226188
selectedVariant,
227189
pickerDisabled: input.gatewayModelsLoading,
228190
isLoading: input.gatewayModelsLoading,
229-
notices,
230191
};
231192
}
232193

@@ -263,35 +224,10 @@ function buildCliCatalogOptions(input: BuildSessionModelOptionsInput): SessionMo
263224
option => option.modelRef && modelRefsEqual(option.modelRef, currentSelection.model)
264225
)
265226
: undefined;
266-
const notices: SessionModelNotice[] = [];
267227

268228
if (currentSelection && !selectedOption) {
269229
selectedOption = createUnavailableOption(currentSelection.model);
270230
options.unshift(selectedOption);
271-
notices.push(createUnavailableNotice(currentSelection.model));
272-
}
273-
if (input.remoteModelState.refresh === 'error') {
274-
notices.unshift({
275-
id: 'stale',
276-
message: 'Showing the last model catalog because refresh failed.',
277-
retry: true,
278-
});
279-
}
280-
if (catalog.truncated) {
281-
notices.push({
282-
id: 'truncated',
283-
message: 'This CLI returned a partial model catalog. Some models or variants may be missing.',
284-
retry: false,
285-
});
286-
}
287-
if (currentSelection && currentSelection.model.providerID !== 'kilo') {
288-
notices.push({
289-
id: 'local-provider',
290-
message: input.organizationId
291-
? "This model runs through your CLI provider, outside Kilo Gateway billing and this organization's model restrictions."
292-
: 'This model runs through your CLI provider, outside Kilo Gateway billing.',
293-
retry: false,
294-
});
295231
}
296232

297233
const selectedVariant =
@@ -306,7 +242,6 @@ function buildCliCatalogOptions(input: BuildSessionModelOptionsInput): SessionMo
306242
selectedVariant,
307243
pickerDisabled: false,
308244
isLoading: false,
309-
notices,
310245
};
311246
}
312247

@@ -324,14 +259,6 @@ function createUnavailableOption(modelRef: ModelRef): SessionModelOption {
324259
};
325260
}
326261

327-
function createUnavailableNotice(modelRef: ModelRef): SessionModelNotice {
328-
return {
329-
id: 'unavailable',
330-
message: `${modelRef.modelID} is the session model but is not available in this catalog.`,
331-
retry: false,
332-
};
333-
}
334-
335262
function createGatewayOption(model: ModelOption): SessionModelOption {
336263
return {
337264
...model,

‎apps/mobile/src/lib/use-session-model-options.test.ts‎

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,6 @@ describe('buildSessionModelOptions', () => {
338338
});
339339
expect(result.selectedValue).toBe(unavailableOption?.id);
340340
expect(result.selectedVariant).toBe('');
341-
expect(result.notices.map(notice => notice.id)).toEqual(['legacy', 'unavailable']);
342341
});
343342

344343
it('does not project a removed legacy Gateway override as the selected model', () => {
@@ -393,10 +392,9 @@ describe('buildSessionModelOptions', () => {
393392
]);
394393
expect(result.options.some(option => option.id === gatewayModels[0]?.id)).toBe(false);
395394
expect(result.pickerDisabled).toBe(true);
396-
expect(result.notices).toEqual([expect.objectContaining({ id: 'error', retry: true })]);
397395
});
398396

399-
it('retains a stale v1 catalog with truncation and local-provider notices', () => {
397+
it('retains a stale v1 catalog with truncation and local-provider selection', () => {
400398
const result = buildSessionModelOptions({
401399
activeSessionType: 'remote',
402400
remoteModelState: {
@@ -438,12 +436,6 @@ describe('buildSessionModelOptions', () => {
438436

439437
const cliOption = result.options.find(option => option.overrideSource === 'cli-catalog');
440438

441-
expect(result.notices.map(notice => notice.id)).toEqual([
442-
'stale',
443-
'truncated',
444-
'local-provider',
445-
]);
446-
expect(result.notices[2]?.message).toContain("organization's model restrictions");
447439
expect(createRemoteModelOverride(cliOption, 'removed')).toEqual({
448440
source: 'cli-catalog',
449441
selection: { model: { providerID: 'local-provider', modelID: 'private-model' } },

‎apps/web/src/components/cloud-agent-next/CloudChatPage.tsx‎

Lines changed: 1 addition & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { useAtomValue, useSetAtom } from 'jotai';
55
import { useSearchParams } from 'next/navigation';
66
import { useMutation, useQueryClient } from '@tanstack/react-query';
77
import { useTRPC } from '@/lib/trpc/utils';
8-
import { ArrowDown, GitBranch, Info, RefreshCw } from 'lucide-react';
8+
import { ArrowDown, GitBranch } from 'lucide-react';
99
import { v4 as uuidv4 } from 'uuid';
1010

1111
import type { KiloSessionId } from '@/lib/cloud-agent-sdk';
@@ -41,7 +41,6 @@ import {
4141
createRemoteModelOverride,
4242
useSessionModels,
4343
validateRemoteModelOverride,
44-
type SessionModelNotice,
4544
} from './hooks/useSessionModels';
4645
import { ContextUsageIndicator } from './ContextUsageIndicator';
4746
import { resolveContextWindow } from './model-context-lengths';
@@ -147,41 +146,6 @@ type CloudChatPageProps = { organizationId?: string };
147146

148147
type TerminalStatusSummary = { status: TerminalStatus; statusText: string };
149148

150-
function SessionModelNotices({
151-
notices,
152-
onRetry,
153-
}: {
154-
notices: SessionModelNotice[];
155-
onRetry: () => void;
156-
}) {
157-
if (notices.length === 0) return null;
158-
159-
return (
160-
<div className="space-y-1.5 px-[max(1rem,calc(50%_-_27rem))] pt-2">
161-
{notices.map(notice => (
162-
<div
163-
key={notice.id}
164-
role="status"
165-
className="border-border bg-muted/40 text-muted-foreground flex items-center gap-2 rounded-md border px-2.5 py-1.5 text-xs"
166-
>
167-
<Info className="size-3.5 shrink-0" />
168-
<span className="min-w-0 flex-1">{notice.message}</span>
169-
{notice.retry && (
170-
<button
171-
type="button"
172-
onClick={onRetry}
173-
className="text-foreground hover:bg-accent focus-visible:ring-ring inline-flex h-6 shrink-0 items-center gap-1 rounded-md px-2 font-medium focus-visible:ring-2 focus-visible:outline-none"
174-
>
175-
<RefreshCw className="size-3" />
176-
Retry
177-
</button>
178-
)}
179-
</div>
180-
))}
181-
</div>
182-
);
183-
}
184-
185149
function TerminalPaneSlot({
186150
terminalId,
187151
active,
@@ -517,10 +481,6 @@ export default function CloudChatPage({ organizationId }: CloudChatPageProps) {
517481
void manager.interrupt();
518482
}, [manager]);
519483

520-
const handleRetryRemoteModels = useCallback(() => {
521-
manager.retryRemoteModels();
522-
}, [manager]);
523-
524484
const handleToggleSound = useCallback(() => {
525485
setSoundEnabled(prev => !prev);
526486
}, [setSoundEnabled]);
@@ -908,10 +868,6 @@ export default function CloudChatPage({ organizationId }: CloudChatPageProps) {
908868
</div>
909869
)}
910870
<div className={activeQuestion || activePermission ? 'hidden' : ''}>
911-
<SessionModelNotices
912-
notices={sessionModels.notices}
913-
onRetry={handleRetryRemoteModels}
914-
/>
915871
<ChatInput
916872
onSend={handleSendMessage}
917873
onSendCommand={handleSendSlashCommand}

‎apps/web/src/components/cloud-agent-next/hooks/useSessionModels.test.ts‎

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@ describe('buildSessionModels', () => {
7272
expect(result.selectedVariant).toBe('high');
7373
expect(result.availableVariants).toEqual(['none', 'high']);
7474
expect(result.modelPickerDisabled).toBe(false);
75-
expect(result.notices).toEqual([]);
7675
expect(result.gatewayOrganizationId).toBe('org-persisted');
7776
});
7877

@@ -355,7 +354,6 @@ describe('buildSessionModels', () => {
355354

356355
expect(result.source).toBe('remote-legacy-gateway');
357356
expect(result.gatewayOrganizationId).toBe('org-from-fetched-session');
358-
expect(result.notices.map(notice => notice.id)).toEqual(['legacy']);
359357
expect(gatewayOption).toMatchObject({
360358
id: 'anthropic/claude-sonnet-4',
361359
modelRef: { providerID: 'kilo', modelID: 'anthropic/claude-sonnet-4' },
@@ -404,7 +402,6 @@ describe('buildSessionModels', () => {
404402
expect(result.modelOptions.some(option => option.id === gatewayModels[0].id)).toBe(false);
405403
expect(result.selectedValue).toBe(result.modelOptions[0].id);
406404
expect(result.modelPickerDisabled).toBe(true);
407-
expect(result.notices).toEqual([expect.objectContaining({ id: 'error', retry: true })]);
408405
});
409406

410407
it('keeps a stale v1 catalog with truncation and local-provider disclosure', () => {
@@ -450,12 +447,6 @@ describe('buildSessionModels', () => {
450447
const cliOption = result.modelOptions.find(option => option.overrideSource === 'cli-catalog');
451448

452449
expect(result.source).toBe('remote-cli-catalog');
453-
expect(result.notices.map(notice => notice.id)).toEqual([
454-
'stale',
455-
'truncated',
456-
'local-provider',
457-
]);
458-
expect(result.notices[2].message).toContain("organization's model restrictions");
459450
expect(createRemoteModelOverride(cliOption, 'removed-variant')).toEqual({
460451
source: 'cli-catalog',
461452
selection: {

0 commit comments

Comments
 (0)