Files
zeekay 37ff5ac382 gates: the composer's model menu, its suggestions and its agent shelf as they are
The model word under the composer opens @hanzo/build's own menu since 0.2.73
(Search models, Enso's row, model rows carrying data-model and data-paused),
not a panel with @hanzo/ui ModelPicker inside it, and effort is a word of its
own. chat.ts reads that menu; picker, limits, upgrade and named measure it.
ModelPicker's research-preview row has no counterpart in build's menu, which
lists what /v1/models serves, so picker.spec stops asserting it and LLM.md
says where Zen 7 is asked for instead. The usage bars are UsageMeter rows
keyed by class (usage-meter-row), as the settings gates already read them.

An empty chat shows @hanzo/build Suggestions since @hanzo/rooms 0.1.83, and a
starter does its act on the press; starters and local-cloud hold that, with
the kept rows answered so no model is asked. Agents are seated from
+ › Agents, which lists the org's own agents; together.spec gives the org the
core team's rows and holds the same room, briefs and names. The thread on
/models is found by its accessible name: rooms' Ask no longer draws a
placeholder, so TryChat stops passing one.

Claude-Session: https://claude.ai/code/session_01BLLMyBFSvAfCt7ZGqYuiEz
2026-10-22 13:05:18 -07:00

45 lines
2.0 KiB
TypeScript

import { test, expect } from '@playwright/test'
import { serveExport } from './export'
import { signIn, answer } from '../session'
/**
* THE ROW NAMES WHAT IS ABOUT TO ANSWER.
*
* `served()` sends a model the gateway carries when the preference is not one,
* and its own reasoning turns on the reader being able to see the difference —
* "a chat that works on a model the reader can see beats a chat that refuses on
* a name nobody serves". Measured on production, neither `enso` nor `free` is in
* the catalogue, so EVERY turn is a substitution.
*
* The catalogue here deliberately excludes both names, which is the production
* case: what must appear is the model that will answer, never the one asked for.
* The override lists Hanzo models alone, so the catalogue carries one of them.
*/
const CATALOGUE = { data: [{ id: 'zen-6' }, { id: 'deepseek-chat' }] }
test('the composer names the model that will answer, not the one asked for', async ({ page }) => {
await signIn(page)
await answer(page, '/v1/models', CATALOGUE)
const site = await serveExport()
try {
await page.setViewportSize({ width: 1440, height: 900 })
await page.goto(`${site.url}/chat`)
const room = page.locator('main')
const named = room.locator('[data-slot="mind"]').first()
await expect(named, 'nothing names Enso on the composer').toBeVisible({ timeout: 20_000 })
await expect(named).toContainText('Enso')
// The override is one press in: the model menu (@hanzo/build Tune's own
// since 0.2.73) leads with Enso's row, the sentence that states the default,
// and lists what the catalogue serves beside it.
await named.click()
const menu = page.locator('[data-slot="model-menu"]')
await expect(menu).toBeVisible({ timeout: 10_000 })
await expect(menu.getByRole('menuitem', { name: 'Enso Picks the model for each message and step' })).toBeVisible()
await expect(menu.locator('[data-slot="model-row"][data-model="zen-6"]')).toBeVisible()
} finally {
await site.close()
}
})