Skip to content
Merged
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
10 changes: 5 additions & 5 deletions frontend/src/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ export const WS_RECONNECT_POLL_MS = 5_000;
export const SCROLL_NEAR_BOTTOM_PX = 150;
export const SCROLL_RESTORE_DELAY_MS = 100;
export const LAST_SESSION_KEY = 'mitzo-last-session';
// Default model for new sessions. Opus 4.6 is used rather than 4.7 because
// 4.7 is not yet available on every supported provider (e.g. Vertex AI at
// time of writing) — picking a widely-available model keeps the out-of-box
// experience functional. Users with access can still select Opus 4.7 from
// the dropdown; the selection persists per-browser.
// Default model for new sessions. Opus 4.6 is used rather than 4.8 because
// 4.8 is not yet available on every supported provider — picking a widely-
// available model keeps the out-of-box experience functional. Users with
// access can still select Opus 4.8 from the dropdown; the selection persists
// per-browser.
export const DEFAULT_MODEL = 'claude-opus-4-6';

// --- Tool grouping ---
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/pages/ChatView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -223,10 +223,10 @@ export function ChatView() {
onChange={(e) => setModel(e.target.value)}
disabled={messages.running}
>
<option value="claude-opus-4-7">Opus 4.7</option>
<option value="claude-opus-4-7:max">Opus 4.7 Max</option>
<option value="claude-opus-4-8">Opus 4.8</option>
<option value="claude-opus-4-8:max">Opus 4.8 Max</option>
<option value="claude-opus-4-6">Opus 4.6</option>
<option value="claude-opus-4-5">Opus 4.5</option>
<option value="claude-sonnet-5">Sonnet 5</option>
<option value="claude-sonnet-4-6">Sonnet 4.6</option>
<option value="claude-sonnet-4-5">Sonnet 4.5</option>
<option value="claude-haiku-4-5">Haiku 4.5</option>
Expand Down
6 changes: 4 additions & 2 deletions frontend/src/pages/DesktopChatView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -198,10 +198,12 @@ export function DesktopChatView() {
onChange={(e) => setModel(e.target.value)}
disabled={messages.running}
>
<option value="claude-opus-4-7">Opus 4.7</option>
<option value="claude-opus-4-7:max">Opus 4.7 Max</option>
<option value="claude-opus-4-8">Opus 4.8</option>
<option value="claude-opus-4-8:max">Opus 4.8 Max</option>
<option value="claude-opus-4-6">Opus 4.6</option>
<option value="claude-sonnet-5">Sonnet 5</option>
<option value="claude-sonnet-4-6">Sonnet 4.6</option>
<option value="claude-sonnet-4-5">Sonnet 4.5</option>
<option value="claude-haiku-4-5">Haiku 4.5</option>
</select>
<div className="mode-pills">
Expand Down
4 changes: 2 additions & 2 deletions packages/client/__tests__/store.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -792,11 +792,11 @@ describe('interruptMessage', () => {
store.getState().sendMessage('first');
lastWs.simulateMessage({ type: 'session_id', sessionId: 'sess-int3' });

store.getState().interruptMessage('urgent', { model: 'claude-opus-4-7' });
store.getState().interruptMessage('urgent', { model: 'claude-opus-4-8' });

const interrupts = lastWs.parsedSent().filter((m) => m.type === 'interrupt');
expect(interrupts).toHaveLength(1);
expect(interrupts[0].model).toBe('claude-opus-4-7');
expect(interrupts[0].model).toBe('claude-opus-4-8');
});

it('does not include model field when both opts and config are null', () => {
Expand Down
27 changes: 17 additions & 10 deletions server/__tests__/model-spec.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,26 @@ describe('parseModelSpec', () => {
});

it('parses a plain model ID', () => {
expect(parseModelSpec('claude-opus-4-7')).toEqual({
model: 'claude-opus-4-7',
expect(parseModelSpec('claude-opus-4-8')).toEqual({
model: 'claude-opus-4-8',
effort: undefined,
});
});

it('splits model and effort on colon', () => {
expect(parseModelSpec('claude-opus-4-7:max')).toEqual({
model: 'claude-opus-4-7',
expect(parseModelSpec('claude-opus-4-8:max')).toEqual({
model: 'claude-opus-4-8',
effort: 'max',
});
});

it('handles trailing colon as empty effort', () => {
expect(parseModelSpec('claude-opus-4-7:')).toEqual({ model: 'claude-opus-4-7', effort: '' });
expect(parseModelSpec('claude-opus-4-8:')).toEqual({ model: 'claude-opus-4-8', effort: '' });
});

it('handles multiple colons — only splits on first', () => {
expect(parseModelSpec('claude-opus-4-7:max:extra')).toEqual({
model: 'claude-opus-4-7',
expect(parseModelSpec('claude-opus-4-8:max:extra')).toEqual({
model: 'claude-opus-4-8',
effort: 'max:extra',
});
});
Expand All @@ -38,27 +38,34 @@ describe('resolveThinking', () => {
});

it('returns adaptive for plain opus model', () => {
expect(resolveThinking('claude-opus-4-7')).toEqual({ type: 'adaptive' });
expect(resolveThinking('claude-opus-4-8')).toEqual({ type: 'adaptive' });
});

it('returns adaptive for opus 4-6', () => {
expect(resolveThinking('claude-opus-4-6')).toEqual({ type: 'adaptive' });
});

it('returns 128k budget for opus :max', () => {
expect(resolveThinking('claude-opus-4-7:max')).toEqual({
expect(resolveThinking('claude-opus-4-8:max')).toEqual({
type: 'enabled',
budgetTokens: 128_000,
});
});

it('returns 10k budget for sonnet', () => {
it('returns 10k budget for sonnet 4-6', () => {
expect(resolveThinking('claude-sonnet-4-6')).toEqual({
type: 'enabled',
budgetTokens: 10_000,
});
});

it('returns 10k budget for sonnet 5', () => {
expect(resolveThinking('claude-sonnet-5')).toEqual({
type: 'enabled',
budgetTokens: 10_000,
});
});

it('returns undefined for haiku', () => {
expect(resolveThinking('claude-haiku-4-5')).toBeUndefined();
});
Expand Down
8 changes: 4 additions & 4 deletions server/__tests__/ws-handler-v2.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2364,7 +2364,7 @@ describe('handleInterruptV2 forwarding', () => {
sessionId: 'sess-resume',
prompt: 'urgent',
clientMsgId: 'i-resume',
model: 'claude-opus-4-7',
model: 'claude-opus-4-8',
},
ctx,
);
Expand All @@ -2373,7 +2373,7 @@ describe('handleInterruptV2 forwarding', () => {
transport,
'c2:sess-resume',
'urgent',
expect.objectContaining({ resume: 'sess-resume', model: 'claude-opus-4-7' }),
expect.objectContaining({ resume: 'sess-resume', model: 'claude-opus-4-8' }),
);
});

Expand Down Expand Up @@ -2435,7 +2435,7 @@ describe('handleInterruptV2 forwarding', () => {
sessionId: 'sess-precedence',
prompt: 'override',
clientMsgId: 'i-precedence',
model: 'claude-opus-4-7',
model: 'claude-opus-4-8',
},
ctx,
);
Expand All @@ -2444,7 +2444,7 @@ describe('handleInterruptV2 forwarding', () => {
transport,
'c4:sess-precedence',
'override',
expect.objectContaining({ resume: 'sess-precedence', model: 'claude-opus-4-7' }),
expect.objectContaining({ resume: 'sess-precedence', model: 'claude-opus-4-8' }),
);
});

Expand Down
8 changes: 4 additions & 4 deletions server/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -366,16 +366,16 @@ export function getRepoConfig() {
}

export const AVAILABLE_MODELS = [
{ id: 'claude-opus-4-7', label: 'Opus 4.7', desc: 'Adaptive thinking' },
{ id: 'claude-opus-4-7:max', label: 'Opus 4.7 Max', desc: 'Max thinking (128k)' },
{ id: 'claude-opus-4-8', label: 'Opus 4.8', desc: 'Latest Opus' },
{ id: 'claude-opus-4-8:max', label: 'Opus 4.8 Max', desc: 'Max thinking (128k)' },
{ id: 'claude-opus-4-6', label: 'Opus 4.6', desc: 'Previous Opus' },
{ id: 'claude-opus-4-5', label: 'Opus 4.5', desc: 'Legacy Opus' },
{ id: 'claude-sonnet-5', label: 'Sonnet 5', desc: 'Latest Sonnet' },
{ id: 'claude-sonnet-4-6', label: 'Sonnet 4.6', desc: 'Balanced' },
{ id: 'claude-sonnet-4-5', label: 'Sonnet 4.5', desc: 'Previous Sonnet' },
{ id: 'claude-haiku-4-5', label: 'Haiku 4.5', desc: 'Fastest' },
];

/** Split "claude-opus-4-7:max" → { model: "claude-opus-4-7", effort: "max" } */
/** Split "claude-opus-4-8:max" → { model: "claude-opus-4-8", effort: "max" } */
export function parseModelSpec(spec?: string): { model: string; effort: string | undefined } {
if (!spec) return { model: '', effort: undefined };
const idx = spec.indexOf(':');
Expand Down
Loading