mirror of
https://github.com/wu736139669/hapi.git
synced 2026-10-28 23:01:34 +00:00
* fix(cursor): pin Auto to CLI --model auto instead of account default Selecting Auto was treated as unset/ACP default[] after the Default→Auto rename. Persist and spawn `auto` so the picker matches `agent --list-models`. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(cursor): keep live ACP model until Auto actually applies Pin session.model to auto for the next `--model auto` spawn, but do not claim the live backend switched when ACP only advertises default[]. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(cursor): reject live Auto unless ACP auto or spawn confirmed it Do not persist Auto when the running catalog has no auto option. Clear the spawn-auto flag after a concrete model apply so Auto → concrete → Auto cannot fake a live switch. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(cursor): keep ACP wire ids spawn-safe and apply effort Cursor's parameterized picker (`clientCapabilities._meta.parameterizedModelPicker`) advertises bare model bases plus separate `fast` / `thought_level` config options. `buildCursorModelsSnapshotFromAcp` synthesized every base as `${base}[fast=${fast}]`, and Cursor rejects any bracket id whose parameter set is not that model's complete set, so 74 of the 76 published picker ids failed at ACP `session/new` with `Cannot use this model` (#1818). - Stop synthesizing wire ids: keep the advertised values verbatim (bare bases are accepted by `--model`, defaults applied). Parameter sets differ per model, so a synthesized id cannot be validated from the options alone. - Flag such catalogs as `parameterized` so variant CLI skus still attach to their base rows — the ACP apply path expresses them through config options. - Apply effort in `applyParameterizedCursorModel` (ids `effort`, `reasoning`, `reasoning_effort`), applied against the config options Cursor re-advertises for the newly selected model, and skipped on models without that axis. - Match CLI sku bases across Cursor's legacy `cursor-` family prefix (`cursor-grok-4.6-high` → ACP base `grok-4.6`) so those variant rows are not dropped. - Version the shared on-disk catalog cache (v2) so runners that already cached the rejected synthesized ids re-probe instead of serving them. Verified with live `cursor-agent 2026.09.10-fd3934a` ACP runs: the catalog no longer contains bracket ids, every advertised base and sampled variant sku spawns, and in-session `claude-opus-4-8-low` / `cursor-grok-4.6-low` set the matching `effort`/`reasoning` option. * test(cursor): cover aliased effort ids and thinking coexistence Covers the branches the HAPI bot review called out for #1818: the `xhigh` → `extra-high` alias on `gpt-5.5` (`reasoning`), the `reasoning_effort` id used by `gemini-3.8-flash`, and switching effort on a model that also advertises Claude's separate `thought_level` `thinking` toggle. * test(cursor): cover legacy sku base and post-switch effort option Adds the two apply-path cases the HAPI bot review called out for #1818: resolving `cursor-grok-4.6-high` onto an advertised `grok-4.6` base, and reading the effort option Cursor advertises for the newly selected model (`reasoning` for gpt-5.5 → `effort` for claude-opus-4-8) instead of the previous model's options. * test(cursor): create the cache dir in the migration test The version-envelope test wrote $HAPI_HOME/cache/cursor-models.json without creating the directory, so running it alone (or filtering with -t) failed with ENOENT before the assertion; it only passed because sibling tests in the same file had already created the path. Reported in the HAPI bot review of #1822. Verified: `vitest run ... -t 'ignores pre-versioned'` → 1 passed, whole file → 4 passed, and every other changed test file passes when run standalone. * Clarify Cursor Auto live-switch limitation in session pickers * Reconcile partial Cursor model changes before acknowledging Auto * Serialize Cursor model changes across clients with the existing async lock --------- Co-authored-by: Cursor <cursoragent@cursor.com>
65 lines
3.0 KiB
TypeScript
65 lines
3.0 KiB
TypeScript
import { expect, test } from '@playwright/test'
|
|
|
|
/**
|
|
* Regression spec for the Chromium select-all quirk: a page containing a
|
|
* contenteditable (the rich composer) makes Ctrl+A collapse to an empty
|
|
* caret when focus is outside the editable, so Ctrl+C copies nothing.
|
|
* SessionChat's applyGlobalSelectAll takeover must restore the expected
|
|
* "select the conversation" behavior while leaving composer/input
|
|
* select-all to the browser.
|
|
*/
|
|
test.describe('composer Ctrl+A + Ctrl+C copy', () => {
|
|
test.beforeEach(async ({ context, page }) => {
|
|
await context.grantPermissions(['clipboard-read', 'clipboard-write'])
|
|
await page.goto('/e2e-fixtures/composer-copy-fixture.html')
|
|
await page.locator('[data-testid="rich-composer-input"]').waitFor()
|
|
})
|
|
|
|
test('Ctrl+A outside the composer selects the message thread and Ctrl+C copies it', async ({ page }) => {
|
|
await page.keyboard.press('ControlOrMeta+a')
|
|
const selected = await page.evaluate(() => window.getSelection()?.toString() ?? '')
|
|
expect(selected).toContain('The quick brown fox jumps over the lazy dog')
|
|
expect(selected).toContain('my earlier user message')
|
|
await page.keyboard.press('ControlOrMeta+c')
|
|
const copied = await page.evaluate(() => navigator.clipboard.readText())
|
|
expect(copied).toBe(selected)
|
|
})
|
|
|
|
test('Ctrl+A with composer focused still selects only the composer draft', async ({ page }) => {
|
|
const editor = page.locator('[data-testid="rich-composer-input"]')
|
|
await editor.click()
|
|
await page.keyboard.type('my draft message')
|
|
await page.keyboard.press('ControlOrMeta+a')
|
|
await page.keyboard.press('ControlOrMeta+c')
|
|
const copied = await page.evaluate(() => navigator.clipboard.readText())
|
|
expect(copied).toBe('my draft message')
|
|
})
|
|
|
|
test('Ctrl+A in a textarea keeps native behavior', async ({ page }) => {
|
|
await page.evaluate(() => {
|
|
const textarea = document.createElement('textarea')
|
|
textarea.id = 'plain-textarea'
|
|
textarea.value = 'textarea draft'
|
|
document.body.appendChild(textarea)
|
|
})
|
|
const textarea = page.locator('#plain-textarea')
|
|
await textarea.click()
|
|
await page.keyboard.press('ControlOrMeta+a')
|
|
await page.keyboard.press('ControlOrMeta+c')
|
|
const copied = await page.evaluate(() => navigator.clipboard.readText())
|
|
expect(copied).toBe('textarea draft')
|
|
})
|
|
|
|
test('mouse-drag selection still copies via plain Ctrl+C', async ({ page }) => {
|
|
const msg = page.locator('[data-testid="assistant-message-2"]')
|
|
await msg.dragTo(page.locator('[data-testid="user-message-1"]'), {
|
|
sourcePosition: { x: 0, y: 2 },
|
|
targetPosition: { x: 300, y: 2 },
|
|
})
|
|
await page.keyboard.press('ControlOrMeta+c')
|
|
const copied = await page.evaluate(() => navigator.clipboard.readText())
|
|
expect(copied).toContain('Another assistant message')
|
|
expect(copied).toContain('my earlier user message')
|
|
})
|
|
})
|