dsh-model-think-level 1.0.4 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/client/EffortEditor.d.ts +3 -1
- package/lib/client/auto-effort.d.ts +23 -0
- package/lib/client/injection/auto-effort-seat.d.ts +20 -5
- package/lib/client/injection/models-page-editor.d.ts +7 -1
- package/lib/client/locales.d.ts +10 -1
- package/lib/client/styles.d.ts +1 -1
- package/lib/client.js +108 -11
- package/lib/client.js.map +3 -3
- package/package.json +2 -2
package/lib/client.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/client/index.ts", "../src/constants.ts", "../src/client/LocaleRefresh.tsx", "../src/client/locales.ts", "../src/client/slider-pref.ts", "../src/client/provider-order.ts", "../src/client/provider-enabled.ts", "../src/client/model-order.ts", "../src/client/styles.ts", "../src/client/injection/composer-menu.ts", "../src/client/ComposerSlider.tsx", "../src/knowledge.ts", "../src/client/effort-memory.ts", "../src/client/ModelSearch.tsx", "../src/client/injection/mount.tsx", "../src/shared.ts", "../src/detection.ts", "../src/client/ops.ts", "../src/client/injection/configured-efforts.ts", "../src/autofill.ts", "../src/client/injection/autofill-run.ts", "../src/client/injection/models-page.ts", "../src/client/EffortEditor.tsx", "../src/client/effort.ts", "../src/client/wire-preview.ts", "../src/client/auto-effort.ts", "../src/client/injection/auto-effort-seat.ts", "../src/client/injection/models-page-editor.ts", "../src/client/injection/dom.ts", "../src/client/injection/key-manager.ts", "../src/client/KeyManager.tsx", "../src/key-refs.ts", "../src/client/key-manager-client.ts", "../src/client/injection/icon.ts", "../src/client/injection/key-reveal.ts", "../src/client/injection/editor-tabs.ts", "../src/client/injection/row-drag.ts", "../src/client/injection/provider-order-drag.ts", "../src/client/injection/provider-toggle.ts", "../src/client/injection/provider-delete-seat.ts", "../src/client/injection/model-order-drag.ts", "../src/client/injection/session-directory.ts", "../src/client/injection/model-menu.ts", "../src/client/injection/provider-card-slot.ts", "../src/client/HeadersEditor.tsx", "../src/client/injection/editor-tab-bar.ts", "../src/client/SliderToggle.tsx", "../src/client/injection/slider-toggle-slot.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Browser half of dsh-model-think-level.\n *\n * This is the ASSEMBLY LAYER: it registers the copy dictionaries and the\n * stylesheet, builds the injection modules with their dependencies, and wires\n * them to the plugin fiber's lifecycle. The behaviour itself lives in\n * `src/client/injection/`, one module per seam:\n *\n * models-page.ts the Models-page DOM injection (observer + editors)\n * models-page-editor.ts the editor injector behind it (anchors, staging, flush)\n * composer-menu.ts the composer slider AND the model search box\n * session-directory.ts the per-session directory + effort-memory wiretaps\n * configured-efforts.ts the settings document's per-model `defaultEffort` cache\n * slider-toggle-slot.ts the Models-page footer toggle\n * provider-order-drag.ts the Models-page provider-row drag handles (order)\n * provider-order.ts the stored provider order (settings page + composer)\n * provider-toggle.ts the Models-page per-provider enable switch\n * provider-enabled.ts the stored disabled providers (settings page + composer)\n * autofill-run.ts the running auto-fill complement (issue #7)\n * mount.tsx foreign React roots and the render boundary\n * model-menu.ts locating the official composer model menu\n *\n * Contributions, all of which dispose with the plugin fiber:\n * 1. The DOM bypass injector: a MutationObserver over the whole document\n * keeps the official Models page's model rows equipped with the\n * thinking-effort editor, wherever the page lives in the settings\n * surface (a panel, a dialog, a portal). This is the SINGLE path:\n * the official per-model disclosure keeps the same anchors (the\n * `modelAdvanced` dictionary value \u2014 Capacities / \u5BB9\u91CF through\n * 0.1.6-alpha.1, Model options / \u6A21\u578B\u9009\u9879 from 0.1.6-alpha.2), so the\n * editor lives under each model row rather than on the provider card.\n * 2. The composer reasoning-effort slider AND the model search box, mounted\n * inside the OFFICIAL model menu opened from the bottom-right seat. The\n * seat's trigger is never touched \u2014 the official \"model \u00B7 effort\" display\n * stays. The search box is unconditional: it does not follow the slider\n * preference.\n * 3. The Models-page slider toggle, taking the official\n * 'settings.models.footer' slot unconditionally at apply.\n * 4. The stylesheet and copy dictionaries.\n *\n * @module dsh-model-think-level/client\n */\n\nimport type {\n ClientContext, ConfigFormsLike, RemoteApi, SettingsScopeBinderLike, SettingsScopeReadLike,\n} from './types.js'\n// Type-only: pulls the shell's locale/remote context merges into this program.\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client'\nimport type {} from '@deepseek-ai/dsh-client-locale/client'\nimport type {} from '@deepseek-ai/dsh-api-remotes/client'\nimport type { Translate } from '@deepseek-ai/dsh-client-ui-slots'\nimport { createElement } from 'react'\nimport type { ReactNode } from 'react'\nimport { PI_AI_NS, PLUGIN_ID, STORE_NS } from '../constants.js'\nimport { LocaleRefresh, type LocaleFace } from './LocaleRefresh.tsx'\nimport { en, zh, type BreKey } from './locales.ts'\nimport { SLIDER_PREF_KEY, sliderEnabled, subscribeSliderEnabled, syncSliderEnabled } from './slider-pref.js'\nimport {\n PROVIDER_DISABLED_KEY, parseDisabledProviders, subscribeDisabledProviders, syncDisabledProviders,\n} from './provider-enabled.js'\nimport {\n PROVIDER_ORDER_KEY, parseProviderOrder, subscribeProviderOrder, syncProviderOrder,\n} from './provider-order.js'\nimport {\n MODEL_ORDER_KEY, parseModelOrders, subscribeModelOrders, syncModelOrders,\n} from './model-order.js'\nimport { STYLES } from './styles.ts'\nimport { createComposerMenu } from './injection/composer-menu.js'\nimport { createConfiguredEfforts } from './injection/configured-efforts.js'\nimport { createIdleAutofill } from './injection/autofill-run.js'\nimport { createModelsPage } from './injection/models-page.js'\nimport { createSessionDirectoryTracker } from './injection/session-directory.js'\nimport { modelMenuOf, modelTriggerOf } from './injection/model-menu.js'\nimport { registerProviderCardSlot } from './injection/provider-card-slot.js'\nimport { registerSliderToggleSlot } from './injection/slider-toggle-slot.js'\n\n/** Stable plugin id, matching the cordis.patch.yml row and the bundle id. */\nexport const name = PLUGIN_ID\n\n/** Cordis fiber dependencies of the browser half. */\nexport const inject = ['slots', 'locale', 'connection', 'remote', 'remote.settings']\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n /** This plugin's copy dictionaries. */\n [STORE_NS]: BreKey\n }\n}\n\n/**\n * Apply the browser half.\n * @param ctx - client root context.\n */\nexport function apply(ctx: ClientContext): void {\n ctx.effect(() => ctx.locale.register(STORE_NS, { zh, en }), 'dsh-model-think-level: dictionaries')\n\n const style = document.createElement('style')\n // `data-plugin` is the host's own ownership marker, not decoration. The\n // client module system claims EVERY untagged `<style>` for whichever plugin\n // happens to materialize next (claimStyles: querySelectorAll of\n // 'style:not([data-plugin])' -> setAttribute('data-plugin', id)) and later\n // deletes its claimed tags by that id (removeOwnedStyles). A stylesheet left\n // untagged is therefore stolen by an unrelated plugin and removed when THAT\n // plugin reloads, while our injected DOM stays mounted: the rows keep\n // rendering and every rule stops applying (default buttons, no flex, no\n // card). Tagging the sheet here keeps it ours, so only our own reload cycles\n // touch it.\n style.dataset['plugin'] = PLUGIN_ID\n style.dataset['pluginStyles'] = PLUGIN_ID\n style.textContent = STYLES\n document.head.appendChild(style)\n ctx.effect(() => () => style.remove(), 'dsh-model-think-level: stylesheet')\n\n // The kernel mounts the settings Remote as an injectable\n // 'remote.settings' service, declared in the plugin's own inject above \u2014 so\n // the face is available before apply runs. No runtime seat probing remains.\n //\n // The official settings form is picked up on top of it when this shell\n // provides one: reads then ride its shared describe mirror (no wire round\n // trip, and the revision the settings surface itself fences writes with).\n // Prefer 0.1.7's `configForms.get(entryId)` and retain the 0.1.5/0.1.6\n // `settingsScope.bind({ namespace })` face for backward compatibility.\n // Deliberately NOT part of the plugin's `inject`: an older kernel without\n // either service must still activate on the wire-describe path.\n const settingsScope = ((): SettingsScopeReadLike | undefined => {\n try {\n const forms = ctx.get?.('configForms') as ConfigFormsLike | undefined\n try {\n const current = forms?.get?.(PI_AI_NS)\n if (current !== undefined) return current\n } catch {\n // A broken new service must not disable the legacy one.\n }\n // `ctx.get('settingsScope')` yields the kernel's BINDER, not a scope:\n // only `bind({ namespace })` mints the read face (getSnapshot). Using\n // the binder directly made every describe throw a TypeError.\n const binder = ctx.get?.('settingsScope') as SettingsScopeBinderLike | undefined\n return binder?.bind?.({ namespace: PI_AI_NS })\n } catch {\n return undefined\n }\n })()\n const settingsApi: RemoteApi = settingsScope === undefined\n ? { settings: ctx.remote.settings }\n : { settings: ctx.remote.settings, scope: settingsScope }\n // The shell's Translate is `(key: string, params?: Record<string, unknown>)`;\n // our components take a string-keyed face, so the bound translator narrows.\n const t = ctx.locale.bind(STORE_NS) as Translate\n\n // The locale service is also its own LocaleFace (getSnapshot/subscribe).\n // Without those two members a language switch cannot reach copy\n // that is already on screen \u2014 the pre-existing behaviour, not a failure.\n const localeFace = (): LocaleFace | undefined => {\n const locale = ctx.locale\n return typeof locale.subscribe === 'function' && typeof locale.getSnapshot === 'function'\n ? locale as LocaleFace\n : undefined\n }\n\n /**\n * Wrap a subtree so it re-translates on a language switch. The thunk is\n * rebuilt per render by {@link LocaleRefresh}; without the seat the subtree\n * renders exactly as it did before.\n */\n const refreshed = (children: () => ReactNode): ReactNode => {\n const face = localeFace()\n return face === undefined ? children() : createElement(LocaleRefresh, { locale: face, children })\n }\n\n // ---- The five injection seams ----\n // Each owns its own state now; see the module list in this file's header.\n const configuredEfforts = createConfiguredEfforts({ api: settingsApi })\n const sessionDirectory = createSessionDirectoryTracker({\n ctx,\n configuredEffort: configuredEfforts.of,\n })\n const composerMenu = createComposerMenu({\n menuOf: modelMenuOf,\n triggerOf: modelTriggerOf,\n t,\n refreshed,\n sliderEnabled,\n directory: sessionDirectory.ensure,\n })\n // The running auto-fill complement (issue #7): the host fills once at boot,\n // everything a session adds afterwards is filled on the idle pass.\n const autofill = createIdleAutofill({ api: settingsApi })\n const modelsPage = createModelsPage({\n ctx, api: settingsApi, t, onIdle: autofill.run, refreshed,\n })\n\n /**\n * The composer path, run on every mutation burst and on every debounced\n * scan. It LEADS both for two reasons: the slider/search body must land\n * before the first paint, and a session switch must have its directory\n * watched before the new session's projection can read as Default.\n */\n const onComposerMutation = (): void => {\n sessionDirectory.ensure()\n composerMenu.reconcile()\n }\n\n ctx.effect(() => {\n modelsPage.start(onComposerMutation)\n return () => {\n // Land what the session held back, unmount the editors, stop observing.\n modelsPage.teardown()\n // Orphaned foreign roots must not outlive the fiber: on plugin disable\n // or HMR they would keep rendering with a stale face.\n composerMenu.dispose()\n // Restore every wrapped directory's original select (the instances\n // outlive the fiber on disable/HMR and must submit officially again).\n sessionDirectory.dispose()\n }\n }, 'dsh-model-think-level: DOM injector')\n\n // A page unload is the last moment the committed ledgers can be landed, and\n // the moment the rest must be DISCARDED: a reload drops the official card's\n // own draft, so the plugin's uncommitted edits go with it. (The dispose\n // effect above keeps the ledger for a same-document fiber cycle, where the\n // card survives.)\n ctx.effect(() => {\n const onPageHide = (): void => { modelsPage.flushOnUnload() }\n window.addEventListener('pagehide', onPageHide)\n return () => { window.removeEventListener('pagehide', onPageHide) }\n }, 'dsh-model-think-level: ledger flush on unload')\n\n // Refresh the injection when the settings document changes (an apply from\n // either the official page or this plugin re-renders the rows). The folded\n // describe snapshot is invalidated first: without that, every later scan \u2014\n // and every editor mounted from it \u2014 would keep reading the revision and\n // providers as of the very first scan, making Apply conflict forever until\n // a full page reload.\n ctx.effect(() => {\n const refresh = (): void => {\n modelsPage.state.describePromise = undefined\n // The configured-pick cache rides the same invalidations: a document\n // update (or a fresh connection) must not leave stale picks behind.\n configuredEfforts.invalidate()\n modelsPage.schedule()\n }\n const disposers = [\n ctx.remote.$on('settings/document-updated', (ns: unknown) => {\n if (ns === PI_AI_NS) refresh()\n }),\n ctx.on('connection/reset', refresh),\n ]\n return () => { for (const dispose of disposers) dispose() }\n }, 'dsh-model-think-level: pushed invalidations')\n\n // Refresh the composer the moment a preference flips (the toggle and the\n // menu can be open at the same time), and follow other tabs' changes. The\n // provider order joins in: its rows live on the settings page, so a change\n // has to reach a scan \u2014 and a composer that is already open \u2014 without a\n // reload. So does the enable switch, for the same reason in reverse: the\n // switch is on the settings page, and it decides what the switcher lists.\n ctx.effect(() => {\n const onStorage = (event: StorageEvent): void => {\n if (event.key === SLIDER_PREF_KEY) syncSliderEnabled(event.newValue !== 'false')\n else if (event.key === PROVIDER_ORDER_KEY) syncProviderOrder(parseProviderOrder(event.newValue))\n else if (event.key === PROVIDER_DISABLED_KEY) {\n syncDisabledProviders(parseDisabledProviders(event.newValue))\n }\n else if (event.key === MODEL_ORDER_KEY) syncModelOrders(parseModelOrders(event.newValue))\n }\n window.addEventListener('storage', onStorage)\n const dispose = subscribeSliderEnabled(() => { modelsPage.schedule() })\n const disposeOrder = subscribeProviderOrder(() => { modelsPage.schedule() })\n const disposeEnabled = subscribeDisabledProviders(() => { modelsPage.schedule() })\n // The per-provider model order rides the same scan: a drag inside an open\n // editor has to leave the grips where they are, which takes a rescan.\n const disposeModelOrder = subscribeModelOrders(() => { modelsPage.schedule() })\n return () => {\n window.removeEventListener('storage', onStorage)\n void dispose()\n void disposeOrder()\n void disposeEnabled()\n void disposeModelOrder()\n }\n }, 'dsh-model-think-level: client preferences')\n\n ctx.effect(() => registerSliderToggleSlot(ctx, t), 'dsh-model-think-level: footer slot activation')\n\n // The request-header editor (issue #12) takes the OFFICIAL provider-card seat:\n // keyed by the adapter family's settings namespace, it receives every\n // llm-pi-ai card \u2014 shipped, added, and hand-declared alike \u2014 with no DOM\n // anchor to maintain.\n ctx.effect(\n () => registerProviderCardSlot(ctx, settingsApi, t),\n 'dsh-model-think-level: provider-card slot activation',\n )\n}\n\nexport type { BreKey }\nexport { EffortEditor } from './EffortEditor.tsx'\nexport type { EffortEditorProps, EffortModel } from './EffortEditor.tsx'\nexport { findModelMenu, findModelTrigger } from './injection/model-menu.js'\nexport type { InjectorDeps, EditorMountProps, ScanState } from './injection/models-page-editor.js'\nexport * from './ops.ts'\nexport * from './types.ts'\n", "/**\n * Plugin-wide constants shared by the host and browser halves.\n *\n * @module dsh-model-think-level/constants\n */\n\n/** Stable plugin id, matching the cordis.patch.yml row and the bundle id. */\nexport const PLUGIN_ID = 'dsh-model-think-level'\n\n/** Same-origin host route that proxies a provider's RAW /models listing. */\nexport const PROBE_PATH = '/dsh-model-think-level/raw-models'\n/** Read-only diagnostics for the request-header takeover. */\nexport const DIAG_PATH = '/dsh-model-think-level/header-bridge'\n\n/**\n * Same-origin host route that resolves one provider profile's stored\n * credential, so the API-key field's eye can show a SAVED key.\n *\n * The harness keeps provider keys write-only on purpose: the settings document\n * carries only the profile's `apiKeyEnv` reference, and the credential service\n * reports `{ configured, source, writable }` through `describe()` \u2014 never the\n * value. Only the host can read it (`credentials.resolve()`), and it does so\n * for the request adapters; the browser normally never sees a stored key at\n * all. This route is the plugin's deliberate exception: an explicit,\n * user-initiated, same-fence read that answers with the value and is never\n * cached, so the eye can put it back into the official field.\n */\nexport const PROVIDER_KEY_PATH = '/dsh-model-think-level/provider-key'\n\n/**\n * Same-origin host route that owns a provider's list of API keys: their\n * references, their labels, and which one is in use.\n *\n * The harness has no notion of \"several keys for one provider\": a profile names\n * exactly one reference (`apiKeyEnv`) and a credential store holds exactly one\n * value per reference, with no enumeration and no label. Both facts are\n * deliberate \u2014 a configuration surface learns which references exist from its\n * settings schema \u2014 so a list of keys can only be plugin-owned state. It lives\n * in the harness home (`<DSH_HOME>/dsh-model-think-level/key-index.json`) rather\n * than in the pi-ai profile, so the official add-provider card, which writes the\n * WHOLE profile at `providers.<route>`, cannot drop it.\n *\n * The route both reads and writes: the values themselves still go to the\n * credential store through the host's own `credentials` service, so the browser\n * never needs a new Remote face.\n */\nexport const KEY_INDEX_PATH = '/dsh-model-think-level/key-index'\n\n/**\n * Same-origin host route that reports this plugin's autofill switches. The\n * browser half runs the running auto-fill complement, and a `dsh.client`\n * declaration carries no plugin config, so the two switches have to be read\n * from the host that owns the cordis row; without it a deployment that turned\n * `autofill: false` would still be written to from the page.\n */\nexport const AUTOFILL_CONFIG_PATH = '/dsh-model-think-level/autofill-config'\n\n/**\n * Same-origin host route reporting the request-header overlay (issue #12): the\n * routes whose `user-agent` the plugin is overriding, and what it found of the\n * plugins that rewrite the same surface. The browser half renders the\n * coexistence warning from here \u2014 the takeover lives host-side, so the page\n * cannot observe it on its own.\n */\nexport const HEADERS_CONFIG_PATH = '/dsh-model-think-level/headers-config'\n\n/** The settings namespace this plugin edits: pi-ai custom provider routes. */\nexport const PI_AI_NS = 'llm-pi-ai'\n\n/**\n * Model-level marker written when the user deliberately unsets the\n * declaration (\"back to inheritance\"). Schemastery passes unknown model keys\n * through, so the marker survives official-page saves and restarts \u2014 which is\n * the point: auto-fill must respect the absence as a decision, not as a gap\n * to fill.\n */\nexport const UNSET_MARKER = 'reasoningEffortsUnset'\n\n/**\n * Model-level marker for a deliberately unset input-modality declaration,\n * mirroring {@link UNSET_MARKER}: auto-fill must respect the absence of\n * `input` as a decision (the route default applies), not as a gap to fill.\n */\nexport const INPUT_UNSET_MARKER = 'inputUnset'\n\n/**\n * Model-level provenance marker written beside every ladder the HOST autofill\n * fills from the knowledge base, carrying the settings revision the fill read.\n *\n * It exists because the plugin's auto-fill and the user's staged declaration\n * race on the SAME row: whichever half runs the fill writes the knowledge base\n * suggestion, while the staged-declaration flush compares the document against\n * its own suggestion. By the time the flush looks, the suggestion may already\n * be stored -- and byte equality alone cannot tell \"the plugin wrote this\" from\n * \"the user declared this\", so a staged ladder differing from the knowledge\n * base in even one spelling (the editor's `off: null` against the knowledge\n * base's `off: 'none'`) was dropped as a document takeover: the reported\n * \"configured it on the add-provider card, saved, and it is gone\".\n *\n * With the marker the flush knows the stored ladder is a suggestion, so a\n * staged user intent overrides it. Either way the user's bytes win; the marker\n * only decides whether the flush is allowed to write at all. Schemastery passes\n * unknown model keys through, so the marker survives official-page saves and\n * restarts like the other two markers. Any ladder written through the plugin\n * clears it, which is what makes a later hand edit outrank a fresh staging.\n */\nexport const AUTOFILL_MARKER = 'reasoningEffortsAutofilled'\n\n/**\n * Model-level default-effort pick (issue #4): the level a brand-new session\n * starts this model at, outranking the remembered levels. The value is one of\n * the model's own declared ladder keys, or the field is absent when the user\n * has not picked one (memory chain applies). Deliberately NOT a harness\n * concept: pi-ai's runtime never sees it (unknown model keys ride the settings\n * document the way the other markers do), and the browser half is its only\n * consumer. The name matches the catalog's `reasoning.defaultEffort` spelling\n * so a future first-class field can adopt the stored values as-is.\n */\nexport const DEFAULT_EFFORT_FIELD = 'defaultEffort'\n\n/** Locale dictionary namespace for the browser half's copy (not a settings namespace). */\nexport const STORE_NS = PLUGIN_ID\n", "/**\n * Re-translates a mounted subtree when the shell's active language changes.\n *\n * `ctx.locale.bind()` hands out ONE stable translate function per namespace\n * (memo-safety by design, so the reference can ride inject surfaces), which\n * leaves a mounted subtree with no signal that the copy it rendered is stale:\n * the function is the same object, and nothing re-renders to call it again.\n * The shell's own outlets do not have this problem \u2014 the slot renderer\n * re-derives their `t` from (namespace, revision), so a language switch hands\n * them a NEW function. Our editors mount through `createRoot` and sit outside\n * that machinery entirely, so they need the revision subscription spelled out.\n *\n * `children` is a thunk on purpose: returning a cached element would let React\n * bail out of the subtree (an unchanged element reference short-circuits the\n * re-render), so it has to be rebuilt on every render.\n *\n * @module dsh-model-think-level/client/LocaleRefresh\n */\nimport { useCallback, useSyncExternalStore } from 'react'\nimport type { ReactNode } from 'react'\n\n/** The slice of the shell's locale service this component consumes. */\nexport interface LocaleFace {\n /** Notified on every snapshot change (language switch or registration). */\n subscribe(fn: () => void): () => void\n /** Current snapshot; `revision` moves on every change. */\n getSnapshot(): { revision?: number }\n}\n\nexport interface LocaleRefreshProps {\n /** The shell's locale service, acting as its own LocaleFace. */\n locale: LocaleFace\n /** Rebuilt on every render \u2014 see the module docblock. */\n children: () => ReactNode\n}\n\n/** Re-render {@link LocaleRefreshProps.children} on every locale revision bump. */\nexport function LocaleRefresh(props: LocaleRefreshProps): ReactNode {\n const { locale } = props\n // Stable identities: the shell's locale object never changes identity, so\n // these two stay put across renders and uSES never churns subscriptions.\n const subscribe = useCallback((notify: () => void) => locale.subscribe(notify), [locale])\n const getSnapshot = useCallback(() => locale.getSnapshot().revision ?? 0, [locale])\n useSyncExternalStore(subscribe, getSnapshot)\n return props.children()\n}\n", "/** Copy dictionaries for the thinking-effort + input-modality editor. */\n\n/** English strings (the key-set source of truth). */\nexport const en = {\n reasoningEffort: 'Reasoning effort',\n thinkingMode: 'Thinking',\n thinkingModeOff: 'Off \u2014 this model is declared as not reasoning.',\n autoAdapt: 'Auto-adapt',\n autoAdaptAll: 'Auto-detect thinking levels',\n autoAdaptAllHint: 'Adapt every model of this provider at once',\n apply: 'Apply',\n reset: 'Reset',\n pendingSave: 'Modified \u2014 it will be written when you save this card.',\n pendingHint: 'Cancel or reload discards it, exactly like the card\\'s own fields.',\n appliedHint: 'Applied suggestion from {source} \u00B7 confidence: {confidence}',\n inputHintEndpoint: 'Modality advice comes from the endpoint listing.',\n inputHintKnowledge: 'Modality advice comes from the knowledge base.',\n inputHintHeuristic: 'Modality guessed from the name alone (low confidence) \u2014 verify.',\n referenceTitle: 'Reference capacities (hints only, never auto-filled)',\n contextWindowLabel: 'Context window',\n maxTokensLabel: 'Max output',\n inputModality: 'Input modalities',\n modalityImage: 'Image input',\n modalityInherit: 'Not declared \u2014 the provider default applies (usually text-only).',\n clearDeclaration: 'Clear declaration',\n confidence_high: 'high',\n confidence_medium: 'medium',\n confidence_low: 'low \u2014 verify before relying on it',\n noSuggestion: 'No suggestion is available for this model.',\n conflict: 'The settings document kept changing under this edit. Wait a moment, then apply again.',\n writeError: 'Failed: {message}',\n renderFailed: 'Reasoning-effort editor failed to render',\n wirePlaceholder: 'Wire value',\n wireValue: 'value',\n reasoningDisabled: 'No reasoning-effort control declared for this model (false) \u2014 do not arm thinking levels.',\n bareHint: 'Clearing every level removes the declaration \u2014 requests go out bare (provider default), which some relays require.',\n defaultRiskDisabled: 'No off level: Default and provider tests send thinking: disabled, which this model rejects (e.g. 1210).',\n defaultRiskQwen: 'No off level: Default sends enable_thinking: false \u2014 only fails where the upstream requires thinking on.',\n stagedHint: 'Not saved yet. Staged settings are applied automatically once the model is saved.',\n level_off: 'off',\n level_minimal: 'minimal',\n level_low: 'low',\n level_medium: 'medium',\n level_high: 'high',\n level_xhigh: 'xhigh',\n level_max: 'max',\n sliderToggleTitle: 'Model and reasoning-effort rows',\n sliderToggleDescription: 'Show the model row and the reasoning-level chips in the model menu; the levels follow the current model.',\n sliderToggleAria: 'Enable the model and reasoning-effort rows',\n settingOn: 'On',\n settingOff: 'Off',\n sliderEffortAria: 'Reasoning effort',\n sliderNoLevels: 'The current model exposes no reasoning-effort levels.',\n triggerFallback: 'Select model',\n effortDefault: 'Default',\n modelRowLabel: 'Model',\n effortRowLabel: 'Reasoning level',\n tagVision: 'Image input',\n tagThinking: 'Sends thinking levels',\n sliderEffortTitle: 'Reasoning effort \u00B7 {name}',\n sliderEffortTitleError: 'Reasoning effort failed: {error}',\n sliderNoCurrent: 'No model selected.',\n compatTitle: 'Endpoint compatibility',\n budgetFieldLabel: 'Thinking budget field',\n budgetFieldHint: 'Which parameter carries the thinking-token budget: some vLLM and self-hosted endpoints read thinking_token_budget, others thinking_budget or thinking_budget_tokens. Unset sends none of them.',\n budgetUnset: 'Not set',\n defaultEffortLabel: 'Default effort',\n defaultEffortUnset: 'Follow memory',\n defaultEffortHint: 'The level every new session starts this model at \u2014 it outranks the remembered levels. Within a session, a level you pick by hand always wins. Empty follows the memory chain.',\n clearDefaultEffort: 'Clear pick',\n priorityLabel: 'vLLM priority',\n priorityPlaceholder: 'e.g. 0',\n priorityHint: 'Scheduler priority for endpoints started with --priority, where lower runs first. Leave empty to send nothing.',\n priorityInvalid: 'Priority must be an integer \u2014 it is not saved until fixed.',\n maxOutputLabel: 'max_output_tokens in requests',\n maxOutputUnset: 'Unset \u2014 follow the protocol default',\n maxOutputOn: 'Send it',\n maxOutputOff: 'Omit it',\n maxOutputHint: 'For Responses-protocol gateways only, and only when a request carries an output cap: some of them reject the parameter, so \"Omit it\" keeps the cap out of the request entirely. Unset is the safe choice.',\n aliasMigrated: 'Legacy alias detected \u2014 saving writes the explicit budget field.',\n modelSearchPlaceholder: 'Search models\u2026',\n modelSearchNoMatches: 'No matching models',\n modelSearchClear: 'Clear search',\n modelSearchFailed: 'Model search failed to render',\n editorTabsLabel: 'Editor sections',\n editorTabProvider: 'Provider',\n editorTabModels: 'Models',\n editorTabAdvanced: 'Advanced',\n keyRevealShow: 'Show key',\n keyRevealHide: 'Hide key',\n keyRevealFailed: 'Stored key unavailable',\n keysLoading: 'Loading keys\u2026',\n keysAdd: 'Add another key',\n keysAddConfirm: 'Add',\n keysEnable: 'Use {name}',\n keysEnabled: 'Enabled',\n keysMissing: 'Not stored',\n keysEdit: 'Edit',\n keysRemove: 'Delete',\n keysConfirmRemove: 'Confirm delete',\n keysCancel: 'Cancel',\n keysSave: 'Save',\n keysAlias: 'Label',\n keysAliasPlaceholder: 'Name (optional), e.g. Team',\n keysValue: 'Key',\n keysValuePlaceholder: 'Paste the API key',\n keysNewValuePlaceholder: 'Paste the new API key',\n keysAddIllegal: 'A key is printable ASCII without spaces.',\n keysAdded: 'Key stored.',\n keysRenamed: 'Label saved.',\n keysReplaced: 'Key replaced.',\n keysRemoved: 'Key removed.',\n keysEnabledNote: '{name} is now in use.',\n keysUnreachable: 'The plugin\u2019s key route did not answer.',\n keysPanelFailed: 'Key manager failed to render',\n headersTitle: 'Request headers',\n headersHint: 'Sent on every request of this route, the model-listing probe included \u2014 add any gateway, routing, metering, or authentication header here.',\n headersEmpty: 'No extra request headers are configured.',\n headersAdd: 'Add header',\n headersName: 'Name',\n headersNamePlaceholder: 'e.g. x-company',\n headersValue: 'Value',\n headersEdit: 'Edit',\n headersSave: 'Save',\n headersCancel: 'Cancel',\n headersClear: 'Clear all',\n headersSaved: 'Saved.',\n headersCleared: 'Cleared \u2014 this route sends no extra request headers.',\n headersReadOnly: 'The settings document is not writable.',\n headersUserAgentTitle: 'User-Agent (taken over at the fetch layer)',\n headersUserAgentHint: 'The official adapter discards a configured user-agent \u2014 the name is reserved for client attribution \u2014 so this plugin adds it at the request layer. Filling it in really does replace the outgoing User-Agent.',\n headersUserAgentClear: 'Do not override',\n headersUserAgentPlaceholder: 'e.g. claude-cli/2.1.161 (external, cli)',\n headersConflictOrigin: 'Routes sharing one endpoint declare different request-header values: when the model in a request body does not identify exactly one of them, that request falls back to the first route.',\n headersConflictPatched: 'The installed dsh-llm-pi-ai already carries a request-header patch: enter values in the headers above and this plugin need not take the request layer over.',\n headersConflictSiblings: 'A plugin rewriting the same layer is installed: {names}. Only one user-agent rewrite should be active at a time, or the last writer on the wire wins.',\n headersError: 'Save failed: {error}',\n headersNoRoute: 'This route is no longer in the settings document \u2014 save the official card once first.',\n providerReorderAria: 'Reorder {provider}',\n providerReorderHint: 'Drag to set the provider order \u2014 the model switcher follows it.',\n providerToggleAria: 'Enable {provider}',\n providerToggleHint: 'A disabled provider is not listed by the model switcher.',\n providerRemoveDisabled: 'Delete',\n providerRemoveLocked: 'A built-in provider cannot be deleted.',\n modelReorderAria: 'Reorder {model}',\n modelReorderHint: 'Drag to set the order of this provider\u2019s models \u2014 the model switcher follows it.',\n}\n\n/** The plugin's locale key union. */\nexport type BreKey = keyof typeof en\n\n/** Chinese strings (same keys as {@link en}). */\nexport const zh: { [Key in keyof typeof en]: string } = {\n reasoningEffort: '\u601D\u8003\u5F3A\u5EA6',\n thinkingMode: '\u601D\u8003',\n thinkingModeOff: '\u5DF2\u5173\u95ED\u2014\u2014\u8BE5\u6A21\u578B\u58F0\u660E\u4E3A\u4E0D\u601D\u8003\u3002',\n autoAdapt: '\u81EA\u52A8\u9002\u914D',\n autoAdaptAll: '\u81EA\u52A8\u83B7\u53D6\u601D\u8003\u7B49\u7EA7',\n autoAdaptAllHint: '\u5C06\u8BE5\u4F9B\u5E94\u5546\u4E0B\u6240\u6709\u6A21\u578B\u81EA\u52A8\u9002\u914D\u601D\u8003\u7B49\u7EA7',\n apply: '\u5E94\u7528',\n reset: '\u653E\u5F03\u4FEE\u6539',\n pendingSave: '\u5DF2\u4FEE\u6539\u2014\u2014\u70B9\u5361\u7247\u300C\u4FDD\u5B58\u300D\u65F6\u4E00\u8D77\u5199\u5165\u3002',\n pendingHint: '\u70B9\u300C\u53D6\u6D88\u300D\u6216\u5237\u65B0\u9875\u9762\u4F1A\u4E00\u5E76\u4E22\u5F03\uFF0C\u4E0E\u5361\u7247\u81EA\u8EAB\u7684\u5B57\u6BB5\u4E00\u81F4\u3002',\n appliedHint: '\u5DF2\u6309 {source} \u5E94\u7528\u5EFA\u8BAE \u00B7 \u7F6E\u4FE1\u5EA6\uFF1A{confidence}',\n inputHintEndpoint: '\u6A21\u6001\u5EFA\u8BAE\u6765\u81EA\u7AEF\u70B9\u5217\u8868\u3002',\n inputHintKnowledge: '\u6A21\u6001\u5EFA\u8BAE\u6765\u81EA\u77E5\u8BC6\u5E93\u3002',\n inputHintHeuristic: '\u6A21\u6001\u5EFA\u8BAE\u4EC5\u6309\u547D\u540D\u63A8\u65AD\uFF08\u4F4E\u7F6E\u4FE1\u5EA6\uFF09\uFF0C\u8BF7\u6838\u5BF9\u3002',\n referenceTitle: '\u53C2\u8003\u5BB9\u91CF\uFF08\u4EC5\u63D0\u793A\uFF0C\u4E0D\u81EA\u52A8\u586B\u5145\uFF09',\n contextWindowLabel: '\u4E0A\u4E0B\u6587\u7A97\u53E3',\n maxTokensLabel: '\u6700\u5927\u8F93\u51FA',\n inputModality: '\u8F93\u5165\u6A21\u6001',\n modalityImage: '\u56FE\u7247\u8F93\u5165',\n modalityInherit: '\u672A\u58F0\u660E\u2014\u2014\u7EE7\u627F\u63D0\u4F9B\u65B9\u9ED8\u8BA4\uFF08\u901A\u5E38\u4EC5\u6587\u672C\uFF09\u3002',\n clearDeclaration: '\u6E05\u9664\u58F0\u660E',\n confidence_high: '\u9AD8',\n confidence_medium: '\u4E2D',\n confidence_low: '\u4F4E\uFF08\u5EFA\u8BAE\u6838\u5BF9\uFF09',\n noSuggestion: '\u6CA1\u6709\u9002\u7528\u4E8E\u8BE5\u6A21\u578B\u7684\u5EFA\u8BAE\u3002',\n conflict: '\u8BBE\u7F6E\u6587\u6863\u5728\u4FDD\u5B58\u671F\u95F4\u6301\u7EED\u53D8\u5316\uFF0C\u8BF7\u7A0D\u5019\u518D\u6B21\u5E94\u7528\u3002',\n writeError: '\u5199\u5165\u5931\u8D25\uFF1A{message}',\n renderFailed: '\u601D\u8003\u5F3A\u5EA6\u7F16\u8F91\u5668\u6E32\u67D3\u5931\u8D25',\n wirePlaceholder: '\u7EBF\u4E0A\u53D6\u503C',\n wireValue: '\u53D6\u503C',\n reasoningDisabled: '\u8BE5\u6A21\u578B\u58F0\u660E\u4E3A\u65E0 reasoning-effort \u63A7\u5236\uFF08false\uFF09\u2014\u2014\u8BF7\u52FF\u52FE\u9009\u601D\u8003\u6863\u3002',\n bareHint: '\u5168\u90E8\u53D6\u6D88\u5E76\u4FDD\u5B58\u5C06\u6E05\u9664\u58F0\u660E\u2014\u2014\u8BF7\u6C42\u4EE5\u88F8\u683C\u5F0F\u53D1\u51FA\uFF08\u8D70\u63D0\u4F9B\u65B9\u9ED8\u8BA4\uFF09\uFF0C\u90E8\u5206\u4E2D\u8F6C\u7AD9\u9700\u8981\u8FD9\u6837\u3002',\n defaultRiskDisabled: '\u65E0 off \u6863\uFF1ADefault \u4E0E\u63D0\u4F9B\u5546\u6D4B\u8BD5\u4F1A\u53D1\u9001 thinking: disabled\uFF0C\u8BE5\u6A21\u578B\u4F1A\u62A5\u9519\uFF08\u5982 1210\uFF09\u3002',\n defaultRiskQwen: '\u65E0 off \u6863\uFF1ADefault \u4F1A\u53D1\u9001 enable_thinking: false\u2014\u2014\u4EC5\u5728\u4E0A\u6E38\u8981\u6C42\u5F00\u542F\u601D\u8003\u65F6\u624D\u4F1A\u5931\u8D25\u3002',\n stagedHint: '\u5C1A\u672A\u4FDD\u5B58\u3002\u6682\u5B58\u7684\u914D\u7F6E\u5C06\u5728\u8BE5\u6A21\u578B\u4FDD\u5B58\u540E\u81EA\u52A8\u5E94\u7528\u3002',\n level_off: 'off',\n level_minimal: 'minimal',\n level_low: 'low',\n level_medium: 'medium',\n level_high: 'high',\n level_xhigh: 'xhigh',\n level_max: 'max',\n sliderToggleTitle: '\u6A21\u578B\u4E0E\u601D\u8003\u7B49\u7EA7\u884C',\n sliderToggleDescription: '\u5728\u6A21\u578B\u83DC\u5355\u4E2D\u663E\u793A\u6A21\u578B\u884C\u4E0E\u601D\u8003\u7B49\u7EA7\u6807\u7B7E\uFF0C\u6863\u4F4D\u968F\u5F53\u524D\u6A21\u578B\u81EA\u52A8\u9002\u914D',\n sliderToggleAria: '\u542F\u7528\u6A21\u578B\u4E0E\u601D\u8003\u7B49\u7EA7\u884C',\n settingOn: '\u542F\u7528',\n settingOff: '\u505C\u7528',\n sliderEffortAria: '\u601D\u8003\u5F3A\u5EA6',\n sliderNoLevels: '\u5F53\u524D\u6A21\u578B\u672A\u63D0\u4F9B\u601D\u8003\u5F3A\u5EA6\u6863\u4F4D',\n triggerFallback: '\u9009\u62E9\u6A21\u578B',\n effortDefault: '\u9ED8\u8BA4',\n modelRowLabel: '\u6A21\u578B',\n effortRowLabel: '\u601D\u8003\u7B49\u7EA7',\n tagVision: '\u652F\u6301\u56FE\u7247\u8F93\u5165',\n tagThinking: '\u652F\u6301\u601D\u8003\u7B49\u7EA7',\n sliderEffortTitle: '\u601D\u8003\u5F3A\u5EA6 \u00B7 {name}',\n sliderEffortTitleError: '\u601D\u8003\u5F3A\u5EA6\u8BBE\u7F6E\u5931\u8D25\uFF1A{error}',\n sliderNoCurrent: '\u5F53\u524D\u65E0\u9009\u62E9\u4E2D\u7684\u6A21\u578B\u3002',\n compatTitle: '\u7AEF\u70B9\u517C\u5BB9',\n budgetFieldLabel: '\u601D\u8003\u9884\u7B97\u5B57\u6BB5',\n budgetFieldHint: '\u601D\u8003 token \u9884\u7B97\u7528\u54EA\u4E2A\u53C2\u6570\u53D1\u9001\uFF1A\u90E8\u5206 vLLM\uFF0F\u81EA\u5EFA\u7AEF\u70B9\u8BFB thinking_token_budget\uFF0C\u53E6\u4E00\u4E9B\u8BFB thinking_budget \u6216 thinking_budget_tokens\u3002\u672A\u8BBE\u7F6E\u8868\u793A\u4E09\u4E2A\u90FD\u4E0D\u53D1\u9001\u3002',\n budgetUnset: '\u672A\u8BBE\u7F6E',\n defaultEffortLabel: '\u9ED8\u8BA4\u601D\u8003\u5F3A\u5EA6',\n defaultEffortUnset: '\u8DDF\u968F\u8BB0\u5FC6',\n defaultEffortHint: '\u6BCF\u4E2A\u65B0\u4F1A\u8BDD\u6253\u5F00\u8BE5\u6A21\u578B\u65F6\u4F7F\u7528\u7684\u6863\u4F4D\u2014\u2014\u4F18\u5148\u4E8E\u8BB0\u4F4F\u7684\u4E0A\u6B21\u6863\u4F4D\u3002\u4F1A\u8BDD\u5185\u624B\u52A8\u9009\u62E9\u7684\u6863\u4F4D\u59CB\u7EC8\u6700\u9AD8\u3002\u7559\u7A7A\u5219\u8DDF\u968F\u8BB0\u5FC6\u94FE\u3002',\n clearDefaultEffort: '\u6E05\u9664\u9009\u62E9',\n priorityLabel: 'vLLM \u4F18\u5148\u7EA7',\n priorityPlaceholder: '\u4F8B\u5982 0',\n priorityHint: '\u4EE5 --priority \u542F\u52A8\u7684\u7AEF\u70B9\u7684\u8C03\u5EA6\u4F18\u5148\u7EA7\uFF0C\u6570\u503C\u8D8A\u5C0F\u8D8A\u5148\u6267\u884C\u3002\u7559\u7A7A\u8868\u793A\u4E0D\u53D1\u9001\u3002',\n priorityInvalid: '\u4F18\u5148\u7EA7\u5FC5\u987B\u662F\u6574\u6570\u2014\u2014\u4FEE\u6B63\u524D\u4E0D\u4F1A\u4FDD\u5B58\u3002',\n maxOutputLabel: '\u8BF7\u6C42\u4E2D\u7684 max_output_tokens',\n maxOutputUnset: '\u672A\u8BBE\u7F6E\u2014\u2014\u8DDF\u968F\u534F\u8BAE\u9ED8\u8BA4',\n maxOutputOn: '\u53D1\u9001',\n maxOutputOff: '\u4E0D\u53D1\u9001',\n maxOutputHint: '\u4EC5\u5BF9 Responses \u534F\u8BAE\u7684\u7F51\u5173\u751F\u6548\uFF0C\u4E14\u53EA\u5728\u8BF7\u6C42\u5E26\u8F93\u51FA\u4E0A\u9650\u65F6\u624D\u8D77\u4F5C\u7528\uFF1A\u90E8\u5206\u7F51\u5173\u4F1A\u62D2\u6536\u8BE5\u53C2\u6570\uFF0C\u9009\"\u4E0D\u53D1\u9001\"\u53EF\u8BA9\u8BF7\u6C42\u5B8C\u5168\u4E0D\u643A\u5E26\u8F93\u51FA\u4E0A\u9650\u3002\u672A\u8BBE\u7F6E\u662F\u6700\u5B89\u5168\u7684\u9009\u62E9\u3002',\n aliasMigrated: '\u68C0\u6D4B\u5230\u65E7\u522B\u540D\u2014\u2014\u4FDD\u5B58\u65F6\u4F1A\u5199\u5165\u663E\u5F0F\u9884\u7B97\u5B57\u6BB5\u3002',\n modelSearchPlaceholder: '\u641C\u7D22\u6A21\u578B\u2026',\n modelSearchNoMatches: '\u672A\u627E\u5230\u5339\u914D\u6A21\u578B',\n modelSearchClear: '\u6E05\u7A7A\u641C\u7D22',\n modelSearchFailed: '\u6A21\u578B\u641C\u7D22\u6E32\u67D3\u5931\u8D25',\n editorTabsLabel: '\u7F16\u8F91\u5668\u5206\u533A',\n editorTabProvider: '\u4F9B\u5E94\u5546\u914D\u7F6E',\n editorTabModels: '\u6A21\u578B\u914D\u7F6E',\n editorTabAdvanced: '\u9AD8\u7EA7\u914D\u7F6E',\n keyRevealShow: '\u663E\u793A\u5BC6\u94A5',\n keyRevealHide: '\u9690\u85CF\u5BC6\u94A5',\n keyRevealFailed: '\u65E0\u6CD5\u8BFB\u53D6\u5DF2\u4FDD\u5B58\u7684\u5BC6\u94A5',\n keysLoading: '\u6B63\u5728\u8BFB\u53D6\u5BC6\u94A5\u2026',\n keysAdd: '\u6DFB\u52A0\u53E6\u4E00\u4E2A\u5BC6\u94A5',\n keysAddConfirm: '\u6DFB\u52A0',\n keysEnable: '\u542F\u7528 {name}',\n keysEnabled: '\u542F\u7528',\n keysMissing: '\u672A\u5B58\u50A8',\n keysEdit: '\u4FEE\u6539',\n keysRemove: '\u5220\u9664',\n keysConfirmRemove: '\u786E\u8BA4\u5220\u9664',\n keysCancel: '\u53D6\u6D88',\n keysSave: '\u4FDD\u5B58',\n keysAlias: '\u522B\u540D',\n keysAliasPlaceholder: '\u540D\u79F0\uFF08\u53EF\u9009\uFF09\uFF0C\u4F8B\u5982 \u56E2\u961F',\n keysValue: '\u5BC6\u94A5',\n keysValuePlaceholder: '\u7C98\u8D34 API \u5BC6\u94A5',\n keysNewValuePlaceholder: '\u7C98\u8D34\u65B0\u7684 API \u5BC6\u94A5',\n keysAddIllegal: '\u5BC6\u94A5\u53EA\u80FD\u662F\u53EF\u6253\u5370 ASCII \u5B57\u7B26\uFF0C\u4E14\u4E0D\u80FD\u5305\u542B\u7A7A\u683C\u3002',\n keysAdded: '\u5DF2\u4FDD\u5B58\u5BC6\u94A5\u3002',\n keysRenamed: '\u5DF2\u4FDD\u5B58\u522B\u540D\u3002',\n keysReplaced: '\u5DF2\u66FF\u6362\u5BC6\u94A5\u3002',\n keysRemoved: '\u5DF2\u5220\u9664\u5BC6\u94A5\u3002',\n keysEnabledNote: '\u5DF2\u542F\u7528 {name}\u3002',\n keysUnreachable: '\u63D2\u4EF6\u7684\u5BC6\u94A5\u63A5\u53E3\u6CA1\u6709\u54CD\u5E94\u3002',\n keysPanelFailed: '\u5BC6\u94A5\u7BA1\u7406\u9762\u677F\u6E32\u67D3\u5931\u8D25',\n headersTitle: '\u8BF7\u6C42\u5934',\n headersHint: '\u8BE5\u8DEF\u7531\u7684\u6BCF\u4E2A\u8BF7\u6C42\u90FD\u4F1A\u643A\u5E26\uFF08\u8BC6\u522B\u6A21\u578B\u5217\u8868\u7684\u63A2\u6D4B\u8BF7\u6C42\u4E5F\u5305\u62EC\uFF09\uFF1A\u4E2D\u8F6C\u7AD9\u7684\u8DEF\u7531\u3001\u8BA1\u91CF\u3001\u8BA4\u8BC1\u7B49\u4EFB\u610F\u8BF7\u6C42\u5934\u90FD\u53EF\u4EE5\u5199\u5728\u8FD9\u91CC\u3002',\n headersEmpty: '\u5C1A\u672A\u914D\u7F6E\u989D\u5916\u8BF7\u6C42\u5934\u3002',\n headersAdd: '\u6DFB\u52A0\u8BF7\u6C42\u5934',\n headersName: '\u540D\u79F0',\n headersNamePlaceholder: '\u4F8B\u5982 x-company',\n headersValue: '\u503C',\n headersEdit: '\u7F16\u8F91',\n headersSave: '\u4FDD\u5B58',\n headersCancel: '\u53D6\u6D88',\n headersClear: '\u5168\u90E8\u6E05\u7A7A',\n headersSaved: '\u5DF2\u4FDD\u5B58\u3002',\n headersCleared: '\u5DF2\u6E05\u7A7A\u2014\u2014\u8BE5\u8DEF\u7531\u4E0D\u518D\u643A\u5E26\u989D\u5916\u8BF7\u6C42\u5934\u3002',\n headersReadOnly: '\u5F53\u524D\u8BBE\u7F6E\u6587\u6863\u4E0D\u53EF\u5199\u5165\u3002',\n headersUserAgentTitle: 'User-Agent\uFF08\u7531\u63D2\u4EF6\u5728\u8BF7\u6C42\u5C42\u63A5\u7BA1\uFF09',\n headersUserAgentHint: '\u5B98\u65B9\u9002\u914D\u5668\u4F1A\u4E22\u5F03\u914D\u7F6E\u7684 user-agent\uFF08\u8BE5\u540D\u79F0\u4FDD\u7559\u7ED9\u5BA2\u6237\u7AEF\u5F52\u5C5E\u6807\u8BC6\uFF09\uFF0C\u6240\u4EE5\u7531\u63D2\u4EF6\u5728\u8BF7\u6C42\u5C42\u8865\u4E0A\u3002\u586B\u4E86\u5C31\u4F1A\u771F\u6B63\u66FF\u6362\u4E0A\u884C\u8BF7\u6C42\u5934\u91CC\u7684 User-Agent\u3002',\n headersUserAgentClear: '\u4E0D\u8986\u76D6',\n headersUserAgentPlaceholder: '\u4F8B\u5982 claude-cli/2.1.161 (external, cli)',\n headersConflictOrigin: '\u540C\u4E00\u4E2A endpoint \u4E0A\u6709\u591A\u4E2A\u8DEF\u7531\u914D\u7F6E\u4E86\u4E0D\u540C\u7684\u8BF7\u6C42\u5934\u503C\uFF1A\u5F53\u8BF7\u6C42\u4F53\u91CC\u7684\u6A21\u578B\u65E0\u6CD5\u552F\u4E00\u5B9A\u4F4D\u5176\u4E2D\u4E00\u4E2A\u8DEF\u7531\u65F6\uFF0C\u8BE5\u8BF7\u6C42\u56DE\u9000\u5230\u7B2C\u4E00\u4E2A\u8DEF\u7531\u7684\u914D\u7F6E\u3002',\n headersConflictPatched: '\u68C0\u6D4B\u5230\u5B98\u65B9 dsh-llm-pi-ai \u5DF2\u5728\u8BF7\u6C42\u5C42\u5904\u7406\u8BF7\u6C42\u5934\uFF1A\u628A\u503C\u586B\u5728\u4E0A\u65B9\u8BF7\u6C42\u5934\u914D\u7F6E\u4E2D\u5373\u53EF\uFF0C\u65E0\u9700\u91CD\u590D\u63A5\u7BA1\u3002',\n headersConflictSiblings: '\u68C0\u6D4B\u5230\u4F1A\u6539\u5199\u540C\u4E00\u5C42\u7684\u540C\u7C7B\u63D2\u4EF6\uFF1A{names}\u3002\u540C\u4E00\u65F6\u95F4\u53EA\u5E94\u542F\u7528\u4E00\u79CD\u8BF7\u6C42\u5934\u6539\u5199\uFF0C\u5426\u5219\u7EBF\u4E0A\u751F\u6548\u7684\u662F\u6700\u540E\u5199\u5165\u8005\u3002',\n headersError: '\u4FDD\u5B58\u5931\u8D25\uFF1A{error}',\n headersNoRoute: '\u8BE5\u8DEF\u7531\u5DF2\u4E0D\u5728\u8BBE\u7F6E\u6587\u6863\u4E2D\u2014\u2014\u8BF7\u5148\u4FDD\u5B58\u4E00\u6B21\u5B98\u65B9\u5361\u7247\u3002',\n providerReorderAria: '\u62D6\u52A8\u6392\u5E8F\uFF1A{provider}',\n providerReorderHint: '\u62D6\u52A8\u8C03\u6574\u4F9B\u5E94\u5546\u987A\u5E8F\u2014\u2014\u6A21\u578B\u5207\u6362\u6309\u6B64\u987A\u5E8F\u663E\u793A\u3002',\n providerToggleAria: '\u542F\u7528 {provider}',\n providerToggleHint: '\u7981\u7528\u540E\u8BE5\u4F9B\u5E94\u5546\u4E0D\u5728\u6A21\u578B\u5207\u6362\u5217\u8868\u4E2D\u663E\u793A\u3002',\n providerRemoveDisabled: '\u5220\u9664',\n providerRemoveLocked: '\u5185\u7F6E\u4F9B\u5E94\u5546\u4E0D\u53EF\u5220\u9664\u3002',\n modelReorderAria: '\u62D6\u52A8\u6392\u5E8F\uFF1A{model}',\n modelReorderHint: '\u62D6\u52A8\u8C03\u6574\u8BE5\u4F9B\u5E94\u5546\u5185\u6A21\u578B\u987A\u5E8F\u2014\u2014\u6A21\u578B\u5207\u6362\u6309\u6B64\u987A\u5E8F\u663E\u793A\u3002',\n}\n", "/**\n * The reasoning-effort slider preference: a session-scoped localStorage-backed\n * enable flag, deliberately NOT settings.yaml (it is a UI preference, like the\n * upstream plugin's toggle it replaces). Defaults to enabled, but the initial\n * read migrates the UPSTREAM plugin's keys (dsh-reasoning-effort.enabled / the\n * @dsh-external legacy one) so a user who had switched the upstream slider off\n * does not suddenly get it back.\n *\n * @module dsh-model-think-level/client/slider-pref\n */\n\n/** localStorage key (own namespace; the upstream plugin's keys stay untouched). */\nexport const SLIDER_PREF_KEY = 'dsh-model-think-level.slider.enabled'\n\n/** Upstream plugin keys, consulted as a one-time migration fallback. */\nconst UPSTREAM_KEYS = ['dsh-reasoning-effort.enabled', '@dsh-external/dsh-reasoning-effort.enabled']\n\nlet enabled = read()\n\nfunction read(): boolean {\n try {\n const own = window.localStorage.getItem(SLIDER_PREF_KEY)\n const migrated = own === null\n ? UPSTREAM_KEYS.map(key => window.localStorage.getItem(key)).find(value => value !== null)\n : null\n return (own ?? migrated ?? null) !== 'false'\n } catch {\n return true\n }\n}\n\nconst listeners = new Set<() => void>()\n\nfunction notify(): void {\n for (const listener of [...listeners]) listener()\n}\n\n/** Snapshot the current preference. */\nexport function sliderEnabled(): boolean {\n return enabled\n}\n\n/** Save the preference (persisted immediately; a failed write keeps the session value). */\nexport function setSliderEnabled(next: boolean): void {\n if (enabled === next) return\n enabled = next\n try {\n window.localStorage.setItem(SLIDER_PREF_KEY, String(next))\n } catch {\n // The current page still follows the choice when storage is unavailable.\n }\n notify()\n}\n\n/** Apply a value incoming from another tab (no persist: the writer already stored it). */\nexport function syncSliderEnabled(next: boolean): void {\n if (enabled === next) return\n enabled = next\n notify()\n}\n\n/** Subscribe to preference changes; returns the disposer. */\nexport function subscribeSliderEnabled(listener: () => void): () => void {\n listeners.add(listener)\n return () => { listeners.delete(listener) }\n}\n", "/**\n * The provider-order preference: the sequence the user set by dragging the\n * provider rows on the official Models settings page.\n *\n * Deliberately localStorage, like the slider preference and for the same\n * reason: this is a UI ordering, not a wire setting. It is also the ONLY place\n * the order can live. The Models page's row order is not the `providers` key\n * order \u2014 `dsh-llm-pi-ai`'s `directoryEntries()` builds the configurable-\n * provider directory as \"every installed catalog route, then the routes the\n * profiles declare\", and the page keeps that declaration order (pinning\n * `deepseek-account`/`deepseek-official` first). Rewriting the dict would not\n * move a single row, and the settings write vocabulary\n * (`{ op: 'set' | 'unset' }`) has no reorder op to rewrite it with. So the\n * order lives beside the page: the rows are placed with CSS `order`, and the\n * composer model switcher reads the very same sequence.\n *\n * @module dsh-model-think-level/client/provider-order\n */\n\n/** localStorage key (own namespace; the upstream plugin's keys stay untouched). */\nexport const PROVIDER_ORDER_KEY = 'dsh-model-think-level.provider-order'\n\n/**\n * The two routes the official Models page always pins to the top, in this\n * order (its own `joinProviderDirectory` sort, matched here so the composer\n * switcher mirrors the page before the user has dragged anything).\n */\nconst PINNED: readonly string[] = ['deepseek-account', 'deepseek-official']\n\n/** Parse a stored/announced payload into a usable id sequence. */\nexport function parseProviderOrder(raw: string | null): readonly string[] {\n if (raw === null) return []\n try {\n const parsed: unknown = JSON.parse(raw)\n if (!Array.isArray(parsed)) return []\n return dedupe(parsed.filter((value): value is string => typeof value === 'string'))\n } catch {\n return []\n }\n}\n\n/** Drop empty and repeated ids, keeping the first occurrence's position. */\nfunction dedupe(ids: readonly string[]): string[] {\n const seen = new Set<string>()\n const kept: string[] = []\n for (const id of ids) {\n if (id.length === 0 || seen.has(id)) continue\n seen.add(id)\n kept.push(id)\n }\n return kept\n}\n\nlet order: readonly string[] = (() => {\n try {\n return parseProviderOrder(window.localStorage.getItem(PROVIDER_ORDER_KEY))\n } catch {\n // No storage (a private window, a non-DOM host): the ordering still works\n // for this page, it just does not survive a reload.\n return []\n }\n})()\n\nconst listeners = new Set<() => void>()\n\nfunction notify(): void {\n for (const listener of [...listeners]) listener()\n}\n\nfunction sameOrder(left: readonly string[], right: readonly string[]): boolean {\n return left.length === right.length && left.every((id, index) => id === right[index])\n}\n\n/**\n * Snapshot the current sequence. The returned array identity is stable until\n * the order changes, which is what makes it a legal `useSyncExternalStore`\n * snapshot.\n */\nexport function providerOrder(): readonly string[] {\n return order\n}\n\n/** Save the sequence (persisted immediately; a failed write keeps the session value). */\nexport function setProviderOrder(next: readonly string[]): void {\n const deduped = dedupe(next)\n if (sameOrder(order, deduped)) return\n order = deduped\n try {\n window.localStorage.setItem(PROVIDER_ORDER_KEY, JSON.stringify(order))\n } catch {\n // The current page still follows the new order when storage is unavailable.\n }\n notify()\n}\n\n/** Apply a sequence incoming from another tab (no persist: the writer already stored it). */\nexport function syncProviderOrder(next: readonly string[]): void {\n const deduped = dedupe(next)\n if (sameOrder(order, deduped)) return\n order = deduped\n notify()\n}\n\n/** Subscribe to order changes; returns the disposer. */\nexport function subscribeProviderOrder(listener: () => void): () => void {\n listeners.add(listener)\n return () => { listeners.delete(listener) }\n}\n\n/**\n * The order the official Models page itself renders `ids` in: the two pinned\n * routes first, everything else in the order it arrived (a stable sort, exactly\n * like the page's `toSorted`).\n */\nexport function baseProviderOrder(ids: readonly string[]): readonly string[] {\n const rank = (id: string): number => {\n const pinned = PINNED.indexOf(id)\n return pinned < 0 ? PINNED.length : pinned\n }\n return ids\n .map((id, index) => ({ id, index, rank: rank(id) }))\n .sort((left, right) => left.rank - right.rank || left.index - right.index)\n .map(entry => entry.id)\n}\n\n/**\n * Fold the user's sequence into a base order: the ids the preference names are\n * permuted among the slots they occupy, and every other id keeps the slot it\n * already had. A provider the user has never seen therefore keeps its natural\n * position instead of being exiled to the end.\n */\nexport function mergeProviderOrder(\n base: readonly string[],\n preferred: readonly string[],\n): readonly string[] {\n const rank = new Map<string, number>()\n for (const id of preferred) if (!rank.has(id)) rank.set(id, rank.size)\n const slots: number[] = []\n const named: string[] = []\n base.forEach((id, index) => {\n if (!rank.has(id)) return\n slots.push(index)\n named.push(id)\n })\n named.sort((left, right) => (rank.get(left) as number) - (rank.get(right) as number))\n const merged = [...base]\n slots.forEach((slot, position) => { merged[slot] = named[position] as string })\n return merged\n}\n\n/**\n * The effective order of `ids`: the dragged sequence where it applies, the\n * official page's own order everywhere else.\n */\nexport function resolveProviderOrder(ids: readonly string[]): readonly string[] {\n return mergeProviderOrder(baseProviderOrder(ids), providerOrder())\n}\n\n/**\n * Move `item` so it ends up at index `toIndex` of the result. The index is\n * interpreted against the RESULT, not the input, so a caller that derived it\n * from the row the pointer is over lands where the user pointed.\n */\nexport function moveToIndex<T>(items: readonly T[], item: T, toIndex: number): T[] {\n const from = items.indexOf(item)\n if (from < 0) return [...items]\n const next = [...items]\n next.splice(from, 1)\n const target = Math.max(0, Math.min(next.length, toIndex))\n next.splice(target, 0, item)\n return next\n}\n", "/**\n * The providers the user switched OFF on the Models settings page.\n *\n * The stored value is the DISABLED set, not the enabled one, so an empty \u2014 or\n * absent \u2014 preference means \"everything is on\". That direction matters for more\n * than brevity: a route that appears AFTER this preference was written is\n * enabled by default, instead of being hidden by an omission nobody made.\n *\n * The composer's Model switcher reads it to leave those providers out of its\n * provider column; the settings page reads it to draw each row's switch.\n *\n * @module dsh-model-think-level/client/provider-enabled\n */\n\nimport { parseProviderOrder } from './provider-order.js'\n\n/** Where the disabled provider ids live. */\nexport const PROVIDER_DISABLED_KEY = 'dsh-model-think-level.providers.disabled'\n\nconst listeners = new Set<() => void>()\n\n/** The disabled ids, as a snapshot whose identity is stable until they change. */\nlet disabled: readonly string[] = read()\n\nfunction read(): readonly string[] {\n try {\n return parseProviderOrder(window.localStorage.getItem(PROVIDER_DISABLED_KEY))\n } catch {\n return []\n }\n}\n\n/**\n * Parse a stored value. The shape is the provider order's \u2014 a deduped list of\n * non-empty ids \u2014 so the one parser serves both.\n */\nexport function parseDisabledProviders(raw: string | null): readonly string[] {\n return parseProviderOrder(raw)\n}\n\n/** The disabled provider ids. */\nexport function disabledProviders(): readonly string[] {\n return disabled\n}\n\n/** Whether one provider is switched on \u2014 the default for anything unnamed. */\nexport function isProviderEnabled(id: string): boolean {\n return !disabled.includes(id)\n}\n\n/** Switch one provider on or off, and persist the new set. */\nexport function setProviderEnabled(id: string, enabled: boolean): void {\n const already = !disabled.includes(id)\n if (enabled === already) return\n apply(enabled ? disabled.filter(candidate => candidate !== id) : [...disabled, id], true)\n}\n\n/** Apply an external (cross-tab) value without writing it back. */\nexport function syncDisabledProviders(next: readonly string[]): void {\n apply(next, false)\n}\n\n/** Listen for a switch, from this tab or another; returns the unsubscribe. */\nexport function subscribeDisabledProviders(listener: () => void): () => void {\n listeners.add(listener)\n return () => { listeners.delete(listener) }\n}\n\n/** Order is meaningless to a set: a reshuffle is not a change, and not a write. */\nfunction sameSet(left: readonly string[], right: readonly string[]): boolean {\n if (left.length !== right.length) return false\n return left.every(id => right.includes(id))\n}\n\nfunction apply(next: readonly string[], persist: boolean): void {\n if (sameSet(disabled, next)) return\n disabled = [...new Set(next)]\n if (persist) {\n try {\n window.localStorage.setItem(PROVIDER_DISABLED_KEY, JSON.stringify(disabled))\n } catch {\n // A storage that refuses the write (private mode, quota) must not lose\n // the in-memory switch: the user sees the row go off either way.\n }\n }\n // Notify a copy: a listener may unsubscribe while being notified.\n for (const listener of [...listeners]) listener()\n}\n", "/**\n * The per-provider model-order preference: the sequence the user set by dragging\n * the model rows inside one provider's editor on the official Models page.\n *\n * One storage key holds every provider's sequence, keyed by the provider id the\n * provider rows already use (`data-bre-provider`, or the Edit button's route) \u2014\n * the same id space as `provider-order.ts`, so a provider renamed in the\n * switcher keeps its models' order.\n *\n * localStorage, for the same reason the provider order is: this is a UI\n * ordering, not a wire setting. The official editor renders\n * `models.map((model, index) => <ModelRow position={index + 1} />)`, so the row\n * an index names is not something we can rewrite through the settings API (and\n * a reorder there would renumber every `aria-label` on the page). The rows are\n * placed with CSS `order` instead, and the composer switcher reads the same\n * sequences.\n *\n * @module dsh-model-think-level/client/model-order\n */\n\nimport { mergeProviderOrder } from './provider-order.js'\n\n/** localStorage key (own namespace; the upstream plugin's keys stay untouched). */\nexport const MODEL_ORDER_KEY = 'dsh-model-think-level.model-order'\n\n/** Every provider's stored sequence, by provider id. */\nexport type ModelOrderMap = Readonly<Record<string, readonly string[]>>\n\n/** Parse a stored/announced payload into a usable map. */\nexport function parseModelOrders(raw: string | null): ModelOrderMap {\n if (raw === null) return {}\n try {\n const parsed: unknown = JSON.parse(raw)\n if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) return {}\n const map: Record<string, readonly string[]> = {}\n for (const [provider, value] of Object.entries(parsed as Record<string, unknown>)) {\n if (provider.length === 0 || !Array.isArray(value)) continue\n const ids = dedupe(value.filter((entry): entry is string => typeof entry === 'string'))\n // A provider with no nameable model left is not an order: keeping it would\n // pin the provider's rows to slots they can no longer hold.\n if (ids.length > 0) map[provider] = ids\n }\n return map\n } catch {\n return {}\n }\n}\n\n/** Drop empty and repeated ids, keeping the first occurrence's position. */\nfunction dedupe(ids: readonly string[]): string[] {\n const seen = new Set<string>()\n const kept: string[] = []\n for (const id of ids) {\n if (id.length === 0 || seen.has(id)) continue\n seen.add(id)\n kept.push(id)\n }\n return kept\n}\n\nlet orders: ModelOrderMap = (() => {\n try {\n return parseModelOrders(window.localStorage.getItem(MODEL_ORDER_KEY))\n } catch {\n // No storage (a private window, a non-DOM host): the ordering still works\n // for this page, it just does not survive a reload.\n return {}\n }\n})()\n\nconst listeners = new Set<() => void>()\n\nfunction notify(): void {\n for (const listener of [...listeners]) listener()\n}\n\nfunction sameIds(left: readonly string[], right: readonly string[]): boolean {\n return left.length === right.length && left.every((id, index) => id === right[index])\n}\n\nfunction persist(): void {\n try {\n window.localStorage.setItem(MODEL_ORDER_KEY, JSON.stringify(orders))\n } catch {\n // The current page still follows the new order when storage is unavailable.\n }\n}\n\n/**\n * Snapshot every stored sequence. The returned object identity is stable until\n * some provider's order changes, which is what makes it a legal\n * `useSyncExternalStore` snapshot for a component that shows one provider.\n */\nexport function modelOrders(): ModelOrderMap {\n return orders\n}\n\n/** One provider's stored sequence (empty when the user has never dragged its models). */\nexport function modelOrderOf(provider: string): readonly string[] {\n return orders[provider] ?? EMPTY\n}\n\n/** A shared empty sequence, so a never-dragged provider returns a stable value. */\nconst EMPTY: readonly string[] = []\n\n/** Save one provider's sequence (persisted immediately; a failed write keeps the session value). */\nexport function setModelOrder(provider: string, next: readonly string[]): void {\n if (provider.length === 0) return\n const ids = dedupe(next)\n const current = modelOrderOf(provider)\n // An exact no-op keeps the snapshot identity, and with it every subscriber's\n // render, untouched \u2014 a drag that lands a row where it already was.\n if (sameIds(current, ids) && (ids.length > 0 || !(provider in orders))) return\n const map: Record<string, readonly string[]> = { ...orders }\n if (ids.length === 0) delete map[provider]\n else map[provider] = ids\n orders = map\n persist()\n notify()\n}\n\n/** Apply a map incoming from another tab (no persist: the writer already stored it). */\nexport function syncModelOrders(next: ModelOrderMap): void {\n orders = next\n notify()\n}\n\n/** Subscribe to order changes; returns the disposer. */\nexport function subscribeModelOrders(listener: () => void): () => void {\n listeners.add(listener)\n return () => { listeners.delete(listener) }\n}\n\n/**\n * Fold one provider's sequence into the ids the editor currently renders: the\n * ids the preference names are permuted among the slots they occupy, and a model\n * the user has never dragged (a newly added one) keeps the position it holds.\n */\nexport function orderModels(ids: readonly string[], preferred: readonly string[]): readonly string[] {\n return mergeProviderOrder(ids, preferred)\n}\n\n/** The effective order of a provider's `ids`: the dragged sequence where it applies. */\nexport function resolveModelOrder(provider: string, ids: readonly string[]): readonly string[] {\n return mergeProviderOrder(ids, modelOrderOf(provider))\n}\n\n/**\n * The same fold for a caller holding whole model entries rather than ids \u2014 the\n * composer switcher renders the directory's own objects, and must not rebuild\n * them. An entry whose id the preference does not name keeps its position, so a\n * model the user has never dragged (a newly added one) does not jump.\n */\nexport function orderModelEntries<T extends { readonly id: string }>(\n entries: readonly T[],\n preferred: readonly string[],\n): readonly T[] {\n if (entries.length === 0 || preferred.length === 0) return entries\n const byId = new Map(entries.map(entry => [entry.id, entry]))\n return orderModels(entries.map(entry => entry.id), preferred).map(id => byId.get(id) as T)\n}\n\n/** Drop everything one provider stored (used when its last model is removed). */\nexport function clearModelOrder(provider: string): void {\n setModelOrder(provider, [])\n}\n", "/**\n * Stylesheet for the injected editor. Deliberately tiny\n * and self-contained: it is injected into the settings panel and must not\n * collide with the official Models page classes, so every selector is prefixed\n * `bre-`. Uses CSS variables for theme awareness where the host provides them.\n */\n\n/** The stylesheet text, inserted once into <head> by the client apply(). */\nexport const STYLES = `\n/* The injector's mount wrapper. It \u2014 not the editor inside it \u2014 is the item\n placed into the official row disclosure's repeat(auto-fit, minmax(160px,1fr))\n grid (context window and max tokens take two cells), so the span belongs\n here: on the editor itself it would target the wrapper's block box and be\n ignored, squeezing the block into one cell. */\n.bre-effort-slot {\n grid-column: 1 / -1;\n}\n/* The slot wrapper is a plain column: the master thinking switch sits in it,\n directly above the card it gates. */\n.bre-effort-editor {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin: 8px 0 4px;\n}\n/* The \u601D\u8003\u5F3A\u5EA6 card proper \u2014 rendered only while the master switch is on. */\n.bre-effort-card {\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 10px 12px;\n border: 0.5px solid var(--dsw-alias-border-l2);\n border-radius: 8px;\n background: var(--dsw-alias-bg-layer-2);\n box-sizing: border-box;\n}\n.bre-effort-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n}\n.bre-effort-title {\n font-size: 12px;\n font-weight: 600;\n color: var(--dsw-alias-label-secondary);\n}\n.bre-link-button {\n background: none;\n border: none;\n padding: 2px 6px;\n font-size: 12px;\n color: var(--dsw-alias-link);\n cursor: pointer;\n border-radius: 4px;\n}\n.bre-link-button:hover { text-decoration: underline; }\n.bre-link-button:disabled { opacity: 0.5; cursor: default; text-decoration: none; }\n.bre-auto-effort {\n /* Seated in the OFFICIAL catalogue head, beside the host's own fetch link:\n it borrows the plugin's link metrics and only adds the gap the head's own\n controls are spaced by, so it reads as one more control of that row. */\n margin-left: 4px;\n flex: none;\n}\n.bre-effort-grid {\n /* Exactly two equal columns mirroring the official capacity pair the editor\n sits under; an odd row count leaves the last cell in the left column,\n so the left side carries the extra level. */\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 4px 16px;\n}\n.bre-effort-row {\n display: grid;\n grid-template-columns: 20px 76px minmax(0, 1fr);\n align-items: center;\n gap: 6px;\n font-size: 12px;\n}\n.bre-effort-row input[type='checkbox'] {\n /* Bigger than the browser default (~13px): a tap/point target that does\n not require precision, in the theme accent when checked. */\n width: 18px;\n height: 18px;\n margin: 0;\n accent-color: var(--dsw-alias-brand-primary);\n cursor: pointer;\n}\n.bre-effort-level { color: var(--dsw-alias-label-tertiary); }\n.bre-effort-wire {\n box-sizing: border-box;\n min-width: 0;\n height: 24px;\n padding: 0 6px;\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: 4px;\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 12px;\n}\n.bre-effort-wire:focus {\n outline: none;\n border-color: var(--dsw-alias-brand-primary);\n}\n.bre-effort-empty { min-height: 24px; }\n.bre-effort-actions {\n display: flex;\n gap: 8px;\n justify-content: flex-end;\n}\n.bre-secondary-button {\n height: 26px;\n padding: 0 12px;\n border-radius: 6px;\n font-size: 12px;\n cursor: pointer;\n border: 0.5px solid var(--dsw-alias-border-l3);\n}\n.bre-secondary-button:disabled { opacity: 0.5; cursor: default; }\n.bre-secondary-button { background: transparent; color: inherit; }\n.bre-effort-message { font-size: 12px; margin: 0; }\n.bre-effort-message.bre-error { color: #c62828; }\n.bre-effort-message.bre-info { color: var(--dsw-alias-link); }\n.bre-effort-note { font-size: 11px; margin: 0; color: var(--dsw-alias-label-tertiary); }\n/* ---- Input-modality section ---- */\n.bre-modality {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n.bre-modality-row {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n}\n.bre-modality-row input[type='checkbox'] {\n width: 18px;\n height: 18px;\n margin: 0;\n accent-color: var(--dsw-alias-brand-primary);\n cursor: pointer;\n}\n.bre-modality-clear { margin-left: auto; }\n.bre-modality-note { font-size: 11px; margin: 0; color: var(--dsw-alias-label-tertiary); }\n/* The master thinking switch, which lives OUTSIDE the card it gates: with it\n off that card is not rendered at all, so nothing here has to be hidden. */\n.bre-thinking-switch { margin: 0; }\n/* ---- Endpoint-compatibility controls ----\n Same shape as the official capacity fields the editor sits under: a caption\n above the control, the control capped at the official enum width (a field\n width dropdown reads as a text field the user is expected to fill), and a\n hint line beneath. Tokens are the official ones \u2014 the plugin's own --dsh-*\n names are defined nowhere in this app, so their light-mode literals used to\n render in both themes. */\n.bre-compat {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n.bre-compat-row {\n display: flex;\n flex-direction: column;\n gap: 4px;\n min-width: 0;\n}\n.bre-compat-field { display: flex; flex-direction: column; gap: 4px; min-width: 0; }\n.bre-compat-label {\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n}\n.bre-compat-hint {\n font-size: 11px;\n line-height: 16px;\n color: var(--dsw-alias-label-tertiary);\n}\n.bre-select, .bre-text-input {\n box-sizing: border-box;\n width: 100%;\n max-width: 240px;\n height: 32px;\n padding: 0 10px;\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: 8px;\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 14px;\n line-height: 22px;\n}\n.bre-text-input::placeholder { color: var(--dsw-alias-label-dimmed); }\n.bre-select:focus, .bre-text-input:focus {\n outline: none;\n border-color: var(--dsw-alias-brand-primary);\n}\n.bre-select:disabled, .bre-text-input:disabled { opacity: 0.6; cursor: default; }\n.bre-select {\n cursor: pointer;\n /* The OS arrow sits flush against the right edge; the official select swaps\n it for the shared 12px chevron inset on the same right pad. Data-URI SVGs\n cannot resolve CSS variables, so the stroke is the caption gray both\n themes share. */\n appearance: none;\n padding-right: 32px;\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none'%3E%3Cpath d='M3 4.5L6 7.5L9 4.5' stroke='%2381858C' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E\");\n background-repeat: no-repeat;\n background-position: right 12px center;\n background-size: 12px 12px;\n}\n.bre-error { color: var(--dsw-alias-state-error-primary); }\n/* ---- Zoned suggestion display ---- */\n.bre-suggestion {\n display: flex;\n flex-direction: column;\n gap: 3px;\n}\n.bre-reference {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n gap: 4px 14px;\n padding: 6px 8px;\n border: 1px dashed var(--dsw-alias-border-l3);\n border-radius: 6px;\n font-size: 11px;\n color: var(--dsw-alias-label-tertiary);\n}\n.bre-reference-title { font-weight: 600; }\n.bre-reference-values { display: inline-flex; gap: 14px; }\n/* ---- Composer model-menu slider (mounted inside the OFFICIAL menu) ----\n Visuals ported VERBATIM from HanaAyane's dsh-reasoning-effort (MIT) \u2014 the\n only deliberate difference is the chibi-runner \"big fish\" knob, which is\n dropped so the knob is always the white circle. Class names are re-\n prefixed bre- (the upstream's re- prefix would clash while both plugins\n are installed); every color/size/animation value stays upstream's. */\n.bre-slider-body {\n /* The replicated popover body. The official menu shell carries padding: 4px\n (ModelSelect.module.css), so pull the replica flush to the box. overflow\n stays VISIBLE: an open list is laid out inside the body, so nothing ever\n has to escape it, and the rows' own hover radius needs no clipping. */\n overflow: visible;\n margin: -4px;\n padding: 8px;\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n/* The official menu is content-sized; while the replicated popover body is\n live, its box takes the upstream .re-model-menu width. The class is added\n by the mount and removed when the slider is switched off. */\n.bre-model-menu-host {\n width: min(340px, calc(100vw - 32px));\n min-width: 0;\n}\n/* The official trigger keeps ownership of its model/effort children. The\n provider prefix is a pseudo-element so host React updates, focus handling and\n the click target remain untouched.\n\n Scoped to the composer, which owns the only button that carries this stamp.\n The settings sidebar's account button is ALSO an [aria-haspopup=\"menu\"]\n button, so an unscoped rule paints the prefix onto the account row the\n moment a stale stamp lands there \u2014 and a stamp that escaped the composer\n must render nothing rather than relabel a host control. The seat anchor is\n listed too, so a shell that renders the trigger outside the card body still\n gets the prefix. */\n[data-composer-card] button[data-bre-provider]::before,\n[data-slot=\"conversation.input.model\"] button[data-bre-provider]::before {\n content: attr(data-bre-provider) \" \u00B7 \";\n flex: none;\n color: var(--dsw-alias-label-secondary);\n font-weight: 400;\n white-space: nowrap;\n}\n\n/* ---- Model / reasoning-level rows ----\n Presentation follows dsh-tauri-model-config's settings rows\n (src/client/models/styles.ts: .zGbnIq_rowCard / .zGbnIq_rowHead /\n .zGbnIq_iconButton): hairline separators between rows and a list opening\n under the row it belongs to. Re-prefixed bre- and routed through the tokens\n this file already uses. */\n/* Layout only \u2014 it deliberately paints NO background, border, radius or\n overflow clip. The replica is mounted inside the OFFICIAL menu, and that\n shell already draws its own surface (MenuSurface: a [data-menu-material]\n panel whose .material child paints --dsw-menu-surface-fill, i.e.\n --dsw-specific-menu, under backdrop-filter: var(--dsw-menu-backdrop-filter),\n rounded by --dsw-radius-lg and outlined by --dsw-elevation-prominent).\n A second opaque card in here shows that official surface as a grey frame\n around it \u2014 two stacked surfaces where every other menu in the app has one. */\n.bre-card {\n display: flex;\n flex-direction: column;\n}\n.bre-row-control {\n appearance: none;\n box-sizing: border-box;\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n min-height: 38px;\n padding: 0 10px;\n border: 0;\n /* Matches the official menu cells (.u91W7W_cell / .u91W7W_option use\n --dsw-radius-md, 8px here) and the .bre-option rows below, now that the\n surface behind the rows is the menu's own and hover is no longer clipped\n by a card. */\n border-radius: 8px;\n background: transparent;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n transition: background 120ms ease;\n}\n.bre-row-control:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover, rgba(120, 125, 140, .09));\n}\n.bre-row-control.is-open { background: var(--dsw-alias-fill-tertiary, rgba(120, 125, 140, .09)); }\n.bre-row-control:disabled { cursor: default; opacity: .6; }\n.bre-row-control:focus-visible {\n outline: none;\n box-shadow: inset 0 0 0 2px var(--dsw-alias-border-l3);\n}\n.bre-row-label {\n flex: none;\n font-size: 13px;\n line-height: 20px;\n color: var(--dsw-alias-label-secondary);\n}\n.bre-row-value {\n flex: 1 1 auto;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n text-align: right;\n font-size: 13px;\n line-height: 20px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n.bre-row-value.is-muted { font-weight: 400; color: var(--dsw-alias-label-tertiary); }\n.bre-divider { height: 1px; margin: 0 10px; background: var(--dsw-alias-border-l4); }\n.bre-panel {\n display: flex;\n flex-direction: column;\n gap: 2px;\n max-height: 220px;\n overflow-y: auto;\n padding: 2px 6px 8px;\n}\n/* Provider column + model column of the model list, side by side. The height is\n FIXED: switching provider must not resize the card, or every click would need\n another same-frame re-place and any missed one shows as a jump. */\n.bre-panel-models {\n flex-direction: row;\n align-items: stretch;\n gap: 0;\n height: 240px;\n max-height: none;\n overflow: hidden;\n padding: 4px 6px 8px;\n}\n.bre-providers {\n flex: none;\n display: flex;\n flex-direction: column;\n gap: 2px;\n width: 112px;\n overflow-y: auto;\n padding-right: 2px;\n}\n.bre-provider-models {\n flex: 1 1 auto;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 2px;\n overflow-y: auto;\n margin-left: 6px;\n padding-left: 6px;\n border-left: 1px solid var(--dsw-alias-border-l4);\n}\n.bre-option,\n.bre-provider {\n appearance: none;\n box-sizing: border-box;\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n min-height: 30px;\n padding: 0 8px;\n border: 0;\n border-radius: 8px;\n background: transparent;\n color: inherit;\n font: inherit;\n font-size: 13px;\n line-height: 20px;\n text-align: left;\n cursor: pointer;\n transition: background 120ms ease, color 120ms ease;\n}\n/* The provider cell carries its chevron hard against the right edge. */\n.bre-provider { gap: 4px; padding: 0 4px 0 8px; }\n.bre-option:hover:not(:disabled),\n.bre-provider:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover, rgba(120, 125, 140, .09));\n}\n.bre-option:disabled,\n.bre-provider:disabled { cursor: default; opacity: .6; }\n.bre-option:focus-visible,\n.bre-provider:focus-visible {\n outline: none;\n box-shadow: inset 0 0 0 2px var(--dsw-alias-border-l3);\n}\n.bre-option-name {\n flex: 1 1 auto;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.bre-option.is-active {\n color: var(--dsw-static-deepseek-500, #4d70ff);\n font-weight: 500;\n background: var(--dsw-alias-fill-tertiary, rgba(120, 125, 140, .09));\n}\n/* The open provider is the highlighted one; the provider of the CURRENT model\n keeps the accent so the column says where the selection lives. */\n.bre-provider.is-open { background: var(--dsw-alias-fill-tertiary, rgba(120, 125, 140, .09)); }\n.bre-provider.is-current .bre-option-name {\n color: var(--dsw-static-deepseek-500, #4d70ff);\n font-weight: 500;\n}\n.bre-provider .bre-row-chevron { width: 14px; height: 14px; font-size: 14px; }\n/* Capability badges on a model row (image input / thinking). Muted so the name\n stays the brightest thing in the row; the badge's own title carries the copy. */\n.bre-option-tags {\n flex: none;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n color: var(--dsw-alias-label-tertiary);\n}\n.bre-option.is-active .bre-option-tags { color: var(--dsw-static-deepseek-500, #4d70ff); }\n.bre-tag { display: inline-flex; align-items: center; }\n.bre-tag-icon { display: block; width: 13px; height: 13px; }\n.bre-option-check { flex: none; width: 14px; height: 14px; }\n.bre-empty { padding: 6px 8px; font-size: 12px; color: var(--dsw-alias-label-tertiary); }\n.bre-slider-advanced {\n /* upstream .re-advanced: the padded area that hosts the slider */\n padding: 14px;\n}\n.bre-effort {\n display: flex;\n align-items: center;\n width: 100%;\n min-width: 0;\n height: 32px;\n color: var(--dsw-alias-label-secondary);\n user-select: none;\n box-sizing: border-box;\n}\n.bre-effort-slider {\n --bre-progress: 50%;\n position: relative;\n width: 100%;\n height: 30px;\n flex: 1 1 auto;\n border-radius: 999px;\n isolation: isolate;\n transition: filter 180ms ease;\n}\n.bre-effort-track {\n position: absolute;\n inset: 0;\n overflow: hidden;\n border-radius: inherit;\n background: linear-gradient(100deg, #03040a 0%, #071126 22%, #101d4c 45%, #302262 70%, #5d35a0 100%);\n box-shadow:\n inset 0 1px 0 rgba(189, 199, 255, .15),\n inset 0 -1px 0 rgba(0, 0, 0, .55),\n 0 3px 10px rgba(12, 17, 55, .34);\n}\n.bre-effort-track::after {\n content: \"\";\n position: absolute;\n inset: 0;\n background:\n radial-gradient(circle at 18% 45%, rgba(82, 130, 255, .12), transparent 24%),\n linear-gradient(90deg, rgba(0, 0, 0, .28), transparent 42%, rgba(168, 113, 255, .12));\n pointer-events: none;\n}\n.bre-effort-fx {\n position: absolute;\n z-index: 1;\n inset: 0;\n overflow: hidden;\n border-radius: inherit;\n pointer-events: none;\n}\n.bre-effort-canvas {\n position: absolute;\n z-index: 2;\n inset: 0;\n width: 100%;\n height: 100%;\n opacity: 1;\n image-rendering: pixelated;\n mix-blend-mode: screen;\n transition: filter 140ms ease;\n}\n.bre-effort-flare {\n position: absolute;\n z-index: 3;\n top: 50%;\n left: var(--bre-progress);\n width: 78px;\n height: 46px;\n border-radius: 50%;\n background: radial-gradient(ellipse at 100% 50%, rgba(255,255,255,.96) 0 4%, rgba(188,189,255,.8) 11%, rgba(106,87,255,.5) 28%, rgba(105,31,255,.2) 49%, transparent 74%);\n filter: blur(2px) saturate(1.25);\n mix-blend-mode: screen;\n transform: translate(-100%, -50%);\n transition: left 70ms linear, filter 140ms ease;\n pointer-events: none;\n}\n.bre-effort-flare::before,\n.bre-effort-flare::after {\n content: \"\";\n position: absolute;\n inset: 50% auto auto 100%;\n border-radius: 999px;\n transform: translate(-50%, -50%);\n}\n.bre-effort-flare::before {\n width: 52px;\n height: 1px;\n background: linear-gradient(90deg, transparent, rgba(100,160,255,.42), #f1ecff, rgba(193,82,255,.65), transparent);\n box-shadow: 0 0 7px #9b7cff, 0 0 13px rgba(72,132,255,.64);\n}\n.bre-effort-flare::after {\n width: 1px;\n height: 20px;\n background: linear-gradient(180deg, transparent, rgba(196,190,255,.84), transparent);\n box-shadow: 0 0 7px #9c7cff;\n}\n.bre-effort-knob {\n position: absolute;\n z-index: 4;\n top: 50%;\n left: clamp(14px, var(--bre-progress), calc(100% - 14px));\n width: 28px;\n height: 28px;\n border: 1px solid rgba(255,255,255,.94);\n border-radius: 50%;\n background: #fff;\n box-shadow:\n 0 0 0 2px rgba(92,105,255,.12),\n 0 0 14px rgba(121,82,255,.48),\n 0 2px 7px rgba(0,0,0,.3);\n transform: translate(-50%, -50%);\n transition: left 190ms cubic-bezier(.22,1,.36,1), transform 160ms ease, box-shadow 180ms ease;\n pointer-events: none;\n}\n.bre-effort-input {\n position: absolute;\n z-index: 5;\n inset: -5px 0;\n width: 100%;\n height: calc(100% + 10px);\n margin: 0;\n opacity: 0;\n cursor: grab;\n touch-action: none;\n}\n.bre-effort-input:active { cursor: grabbing; }\n.bre-effort-input:focus-visible + .bre-effort-knob {\n outline: 2px solid var(--dsw-static-blue-400);\n outline-offset: 2px;\n}\n.bre-effort.is-dragging .bre-effort-canvas {\n filter: saturate(1.45) brightness(1.28) contrast(1.06);\n}\n.bre-effort.is-dragging .bre-effort-flare {\n filter: blur(1.5px) saturate(1.6) brightness(1.42);\n transition: none;\n}\n.bre-effort.is-dragging .bre-effort-knob {\n transform: translate(-50%, -50%) scale(1.07);\n transition: none;\n box-shadow:\n 0 0 0 3px rgba(113,115,255,.25),\n 0 0 20px rgba(74,145,255,.86),\n 0 0 31px rgba(171,53,255,.66),\n 0 3px 8px rgba(0,0,0,.32);\n}\n.bre-effort-slider[data-top] .bre-effort-track {\n animation: bre-effort-dark-breathe 1.9s ease-in-out infinite;\n}\n.bre-effort-slider[data-top] .bre-effort-knob {\n box-shadow:\n 0 0 0 3px rgba(119,99,255,.18),\n 0 0 22px rgba(135,78,255,.76),\n 0 0 34px rgba(53,121,255,.34),\n 0 3px 8px rgba(0,0,0,.3);\n}\n.bre-effort.is-error .bre-effort-slider {\n outline: 1px solid var(--dsw-alias-state-error-secondary);\n outline-offset: 2px;\n}\n.bre-effort.is-busy { opacity: .72; }\n.bre-effort-sr {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n}\nbody:not([data-ds-dark-theme]) .bre-effort-slider {\n filter: none;\n}\nbody:not([data-ds-dark-theme]) .bre-effort-track {\n background: var(--dsw-static-blue-75, #e5f0ff);\n box-shadow:\n inset 0 1px 0 rgba(255,255,255,.9),\n inset 0 0 0 1px rgba(80,133,194,.14),\n 0 3px 10px rgba(48,101,165,.13);\n}\nbody:not([data-ds-dark-theme]) .bre-effort-track::before {\n content: \"\";\n position: absolute;\n z-index: 0;\n inset: 0 auto 0 0;\n width: var(--bre-progress);\n border-radius: inherit;\n background: linear-gradient(90deg, #fff 0%, #e2f0ff 20%, #a8d0fb 57%, #438fdf 100%);\n transition: width 190ms cubic-bezier(.22,1,.36,1);\n}\nbody:not([data-ds-dark-theme]) .bre-effort-slider[data-top] .bre-effort-track::before {\n background: linear-gradient(90deg, #fff 0%, #d7eaff 18%, #75afea 54%, #0751ad 100%);\n}\nbody:not([data-ds-dark-theme]) .bre-effort.is-dragging .bre-effort-track::before {\n transition: none;\n}\nbody:not([data-ds-dark-theme]) .bre-effort-track::after {\n z-index: 1;\n background: linear-gradient(90deg, rgba(255,255,255,.48), transparent 34%, rgba(23,101,201,.07));\n}\nbody:not([data-ds-dark-theme]) .bre-effort-canvas {\n opacity: .78;\n mix-blend-mode: multiply;\n}\nbody:not([data-ds-dark-theme]) .bre-effort-flare {\n background: radial-gradient(ellipse at 100% 50%, rgba(255,255,255,.98) 0 5%, rgba(204,231,255,.88) 13%, rgba(91,162,241,.48) 31%, rgba(37,111,207,.16) 53%, transparent 75%);\n filter: blur(2px) saturate(1.12);\n}\nbody:not([data-ds-dark-theme]) .bre-effort-flare::before {\n background: linear-gradient(90deg, transparent, rgba(116,177,244,.34), #fff, rgba(66,139,225,.58), transparent);\n box-shadow: 0 0 7px rgba(58,133,222,.5), 0 0 13px rgba(104,176,255,.38);\n}\nbody:not([data-ds-dark-theme]) .bre-effort-flare::after {\n background: linear-gradient(180deg, transparent, rgba(255,255,255,.94), transparent);\n box-shadow: 0 0 7px rgba(64,137,224,.44);\n}\nbody:not([data-ds-dark-theme]) .bre-effort-knob {\n border-color: rgba(126,160,197,.32);\n box-shadow:\n 0 0 0 2px rgba(58,124,207,.09),\n 0 0 13px rgba(48,118,207,.3),\n 0 3px 8px rgba(39,77,119,.18);\n}\nbody:not([data-ds-dark-theme]) .bre-effort-slider[data-top] .bre-effort-track {\n animation-name: bre-effort-light-breathe;\n}\nbody:not([data-ds-dark-theme]) .bre-effort-slider[data-top] .bre-effort-knob,\nbody:not([data-ds-dark-theme]) .bre-effort.is-dragging .bre-effort-knob {\n box-shadow:\n 0 0 0 3px rgba(36,105,192,.15),\n 0 0 20px rgba(25,100,201,.45),\n 0 3px 8px rgba(39,77,119,.18);\n}\n@keyframes bre-effort-dark-breathe {\n 0%, 100% { box-shadow: inset 0 1px 0 rgba(196,204,255,.16), 0 3px 10px rgba(18,25,72,.4); }\n 50% { box-shadow: inset 0 1px 0 rgba(220,214,255,.24), 0 0 21px rgba(111,66,255,.5); }\n}\n@keyframes bre-effort-light-breathe {\n 0%, 100% { box-shadow: inset 0 1px 0 rgba(255,255,255,.9), inset 0 0 0 1px rgba(67,124,193,.16), 0 3px 10px rgba(48,101,165,.13); }\n 50% { box-shadow: inset 0 1px 0 rgba(255,255,255,.96), inset 0 0 0 1px rgba(31,102,190,.22), 0 0 19px rgba(31,105,201,.24); }\n}\n.bre-slider-hint {\n /* upstream re-model-status */\n display: block;\n padding: 14px;\n color: var(--dsw-alias-label-tertiary, #9296a0);\n font-size: 12px;\n text-align: center;\n}\n/* upstream re-model-error: the directory/store error line under the row */\n.bre-model-error {\n margin: 8px;\n padding: 8px 10px;\n border-radius: 8px;\n color: var(--dsw-alias-state-error-primary, #c83e4d);\n background: var(--dsw-alias-state-error-tertiary, rgba(220,55,70,.08));\n font-size: 11px;\n}\n/* upstream .re-menu-separator */\n.bre-menu-separator {\n height: 1px;\n background: var(--dsw-alias-stroke-secondary, rgba(121,126,145,.16));\n}\n/* upstream .re-model-row: name \u00B7 current effort \u203A (click \u2192 official model list) */\n.bre-model-row {\n display: grid;\n grid-template-columns: minmax(0, 1fr) auto auto;\n align-items: center;\n gap: 8px;\n min-height: 45px;\n padding: 0 14px;\n width: 100%;\n border: 0;\n color: inherit;\n background: transparent;\n font: inherit;\n text-align: left;\n cursor: pointer;\n}\n.bre-model-row:hover { background: var(--dsw-alias-fill-tertiary, rgba(120,125,140,.09)); }\n.bre-model-row-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; }\n.bre-model-row-effort { color: var(--dsw-static-deepseek-500, #4d70ff); font-size: 12px; }\n.bre-row-chevron {\n flex: none;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 16px;\n height: 16px;\n font-size: 18px;\n line-height: 1;\n color: var(--dsw-alias-label-tertiary);\n transform: rotate(0deg);\n transition: transform 120ms ease;\n}\n.bre-row-chevron.is-open { transform: rotate(90deg); }\n@media (prefers-reduced-motion: reduce) {\n .bre-effort-slider[data-top] .bre-effort-track { animation: none; }\n .bre-effort-knob,\n .bre-effort-flare,\n body:not([data-ds-dark-theme]) .bre-effort-track::before { transition: none; }\n}\n/* ---- Models-page slider toggle (boxed setting item) ----\n Item form ported VERBATIM from upstream .re-setting-row; the surrounding\n box is the requested container (border only, transparent background). */\n.bre-slider-setting {\n margin-top: 12px;\n padding: 0 14px;\n border: 1px solid var(--dsw-alias-stroke-secondary, rgba(121,126,145,.2));\n border-radius: 12px;\n background: transparent;\n}\n.bre-slider-setting-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 24px;\n padding: 16px 0;\n /* Upstream .re-setting-row carries a list bottom border because the general\n settings list held TWO rows (the slider + the big-fish toggle). This box\n holds exactly one, so no divider: the box itself is the container. */\n}\n.bre-slider-setting-copy { min-width: 0; }\n.bre-slider-setting-title {\n color: var(--dsw-alias-label-primary, #15171b);\n font-size: 14px;\n font-weight: 400;\n line-height: 22px;\n}\n.bre-slider-setting-description {\n margin-top: 3px;\n color: var(--dsw-alias-label-tertiary, #9296a0);\n font-size: 12px;\n line-height: 18px;\n}\n.bre-slider-setting-control { display: inline-flex; align-items: center; gap: 10px; flex: none; }\n.bre-slider-setting-state { color: var(--dsw-alias-label-secondary, #686c75); font-size: 13px; }\n.bre-slider-setting-switch {\n position: relative;\n width: 38px;\n height: 22px;\n padding: 0;\n border: 0;\n border-radius: 999px;\n background: var(--dsw-alias-fill-quaternary, #c7cbd3);\n cursor: pointer;\n transition: background 150ms ease;\n}\n.bre-slider-setting-switch:hover { filter: brightness(.97); }\n.bre-slider-setting-switch:disabled { cursor: not-allowed; opacity: .45; }\n.bre-slider-setting-switch:focus-visible {\n outline: 2px solid var(--dsw-static-blue-400, #5d83ff);\n outline-offset: 2px;\n}\n.bre-slider-setting-switch.is-on { background: var(--dsw-alias-state-business-primary, #4f73ff); }\n.bre-slider-setting-switch-knob {\n position: absolute;\n top: 2px;\n left: 2px;\n width: 18px;\n height: 18px;\n border-radius: 50%;\n background: #fff;\n box-shadow: 0 1px 4px rgba(0,0,0,.2);\n transition: transform 170ms cubic-bezier(.22,1,.36,1);\n}\n.bre-slider-setting-switch.is-on .bre-slider-setting-switch-knob { transform: translateX(16px); }\n\n/* Model selection search box */\n.bre-model-search-box {\n display: flex;\n flex-direction: column;\n padding: 6px 8px 4px;\n position: sticky;\n top: 0;\n z-index: 2;\n background: var(--dsw-specific-menu, var(--dsw-alias-bg-layer-1, #1e1f22));\n}\n.bre-model-search-box:has(.bre-search-empty),\n.bre-model-search-box.has-empty {\n flex: 1 1 auto;\n}\n.bre-search-input-wrapper {\n display: flex;\n align-items: center;\n position: relative;\n width: 100%;\n height: 32px;\n padding: 0 8px;\n box-sizing: border-box;\n border-radius: 8px;\n background: var(--dsw-alias-bg-layer-2, rgba(255, 255, 255, 0.06));\n border: 0.5px solid var(--dsw-alias-border-l4, rgba(255, 255, 255, 0.12));\n color: var(--dsw-alias-label-primary);\n transition: border-color 150ms ease, box-shadow 150ms ease;\n}\n.bre-search-input-wrapper:focus-within {\n border-color: var(--dsw-static-deepseek-500, #4d70ff);\n box-shadow: 0 0 0 2px rgba(77, 112, 255, 0.2);\n}\n.bre-search-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n flex: 0 0 16px;\n width: 16px;\n height: 16px;\n margin-right: 6px;\n color: var(--dsw-alias-label-tertiary, #81858c);\n pointer-events: none;\n}\n.bre-search-icon svg {\n width: 14px;\n height: 14px;\n}\n.bre-search-input {\n flex: 1 1 auto;\n min-width: 0;\n height: 100%;\n padding: 0;\n border: none;\n outline: none;\n background: transparent;\n color: inherit;\n font: inherit;\n font-size: 13px;\n line-height: 20px;\n}\n.bre-search-input::placeholder {\n color: var(--dsw-alias-label-tertiary, #81858c);\n opacity: 0.85;\n}\n.bre-search-clear {\n display: flex;\n align-items: center;\n justify-content: center;\n flex: 0 0 18px;\n width: 18px;\n height: 18px;\n margin-left: 4px;\n padding: 0;\n border: none;\n border-radius: 4px;\n background: transparent;\n color: var(--dsw-alias-label-tertiary, #81858c);\n cursor: pointer;\n transition: background 120ms ease, color 120ms ease;\n}\n.bre-search-clear:hover {\n background: var(--dsw-alias-interactive-bg-hover, rgba(255, 255, 255, 0.1));\n color: var(--dsw-alias-label-primary);\n}\n.bre-search-clear svg {\n width: 12px;\n height: 12px;\n}\n.bre-search-empty {\n display: flex;\n align-items: center;\n justify-content: center;\n flex: 1 1 auto;\n min-height: 180px;\n padding: 24px 16px;\n text-align: center;\n color: var(--dsw-alias-label-tertiary, #81858c);\n font-size: 13px;\n line-height: 20px;\n user-select: none;\n}\n\n/* ---- Request-header section (issue #12). Mounted through the official\n settings.models.provider-card seat, so it sits in the card's own layout\n rather than a disclosure grid.\n\n Every value below was MEASURED off the official Models page's own controls\n in DSH 0.1.7-rc.2 rather than guessed, and each is expressed through the same\n --dsw-alias-* token the host paints with, so a theme switch (light/dark)\n repaints this section along with the page:\n\n provider card radius 20px, 1px var(--dsw-alias-settings-card-stroke)\n editor action save = filled var(--dsw-alias-button-primary-fill), h36,\n radius 12px, 0 14px, 14px; cancel = 1px border, same box\n row action h28, radius 8px, 0 10px, 12px, 1px border\n text link h21, radius 4px, 2px 6px, 12px, link-blue label\n text input radius 12px, 0 10px, 1px var(--dsw-alias-border-l3) ---- */\n/* The provider-card slot's wrapper.\n The official slot mounts us inside a display:contents container, so our own\n root IS a flex item of the card row \u2014 and the row lays its children out with\n a 12px gap. A wrapper that stays in the flow while empty therefore adds one\n phantom gap to EVERY card in the list, which is exactly the height regression\n this rule exists to prevent. Collapsed, the wrapper leaves the flow entirely:\n the card renders as it did before the plugin.\n (Deliberately NOT display:contents here \u2014 that keeps it a gap-participating\n item, which is the bug this rule fixes.)\n Open, it becomes an ordinary block so the section below can lay itself out.\n data-edit lives HERE \u2014 the occurrence component publishes the card's state\n onto its own root, so keying the rule on .bre-headers[data-edit] instead\n would silently never match. */\n.bre-headers-host {\n display: none;\n}\n.bre-headers-host[data-edit=\"1\"] {\n display: block;\n}\n.bre-headers {\n display: flex;\n flex-direction: column;\n gap: 8px;\n /* The card lays its own children out with a 12px gap and no dividers; this\n section is an addition to that column, so it separates itself the same way\n the host separates card sections \u2014 a hairline on the card's own stroke. */\n border-top: 1px solid var(--dsw-alias-border-l2, #0000001a);\n padding-top: 12px;\n\n /* Present ONLY while the provider card is being edited.\n The provider list is a list of providers: a request-header row parked in\n every card, collapsed or not, is noise on a surface the user did not ask\n to configure. The card reveals its editor when the user presses its own\n Edit action, and that is the moment this section belongs on screen \u2014 the\n same gesture that opens the model list opens this.\n\n The visibility is decided by the component (which watches the card and\n sets data-edit on the wrapper), not by a selector: the official editor is\n NOT a sibling of this element \u2014 its container sits in the card row's own\n children, after the row head and this section's own wrapper \u2014 so no\n relative selector can reach it. The wrapper's data-edit is the one fact\n CSS can act on.\n\n Degradation is safe by construction: with no observer (no card ancestor to\n watch) data-edit stays \"0\", the section is never revealed, and the\n official page stays clean rather than leaking a row into every card. */\n display: none;\n}\n.bre-headers-host[data-edit=\"1\"] .bre-headers {\n display: flex;\n}\n/* The collapsed heading is the section's identity while the card is being\n edited: title, configured count, and the \u203A that opens the details. */\n.bre-headers-disclosure {\n display: flex;\n align-items: center;\n gap: 6px;\n width: 100%;\n padding: 0;\n border: none;\n background: transparent;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n}\n.bre-headers-disclosure:hover .bre-effort-title {\n color: var(--dsw-alias-label-secondary, #61666b);\n}\n.bre-headers-chevron {\n margin-left: auto;\n padding-right: 2px;\n color: var(--dsw-alias-label-tertiary, #81858c);\n font-size: 14px;\n line-height: 1;\n transition: transform 150ms ease;\n}\n.bre-headers[data-open=\"1\"] .bre-headers-chevron { transform: rotate(90deg); }\n/* How many entries are configured \u2014 the one fact worth showing while closed. */\n.bre-headers-count {\n min-width: 16px;\n padding: 0 5px;\n border-radius: 8px;\n background: var(--dsw-alias-interactive-bg-hover, #2631480f);\n color: var(--dsw-alias-label-secondary, #61666b);\n font-size: 11px;\n line-height: 16px;\n text-align: center;\n}\n.bre-headers-head {\n display: flex;\n align-items: center;\n min-height: 32px;\n padding: 0 8px;\n border-radius: var(--dsw-radius-sm, 8px);\n background: var(--dsw-alias-interactive-bg-hover, #2631480f);\n}\n.bre-headers-head .bre-effort-title {\n color: var(--dsw-alias-label-primary, #0f1115);\n font-weight: 600;\n}\n.bre-headers-edit, .bre-headers-rows {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n.bre-headers-columns,\n.bre-headers-row {\n display: grid;\n grid-template-columns: minmax(120px, 0.9fr) minmax(180px, 1.5fr) 32px;\n align-items: center;\n gap: 8px;\n}\n.bre-headers-columns {\n padding: 0 10px;\n color: var(--dsw-alias-label-tertiary, #81858c);\n font-size: 11px;\n line-height: 16px;\n}\n.bre-headers-row {\n min-height: 40px;\n padding: 4px 6px;\n border: 1px solid var(--dsw-alias-border-l2, #0000001a);\n border-radius: var(--dsw-radius-sm, 8px);\n background: var(--dsw-alias-bg-layer-1, #fff);\n}\n.bre-headers-add {\n align-self: flex-start;\n min-height: 30px;\n padding: 0 10px;\n border: 1px solid var(--dsw-alias-border-l2, #0000001a);\n border-radius: var(--dsw-radius-sm, 8px);\n color: var(--dsw-alias-label-primary, #0f1115);\n}\n.bre-headers-add:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover, #2631480f);\n}\n.bre-headers-remove {\n width: 32px;\n padding: 0;\n justify-self: center;\n}\n.bre-headers-rows .bre-headers-name {\n color: var(--dsw-alias-label-secondary, #61666b);\n font-size: 12px;\n word-break: break-all;\n}\n.bre-headers-masked {\n color: var(--dsw-alias-label-tertiary, #81858c);\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n letter-spacing: 0;\n}\n\n/* The section's commit/dismiss pair, matching the official editor's own\n actions: a filled commit and a bordered dismiss, 36px tall at 14px. */\n.bre-headers-edit .bre-primary-button {\n height: 36px;\n padding: 0 14px;\n border: none;\n border-radius: var(--dsw-radius-md, 12px);\n background: var(--dsw-alias-button-primary-fill, #0f1115);\n color: var(--dsw-alias-label-primary-foreground, #fff);\n font-size: 14px;\n font-weight: 400;\n line-height: 1;\n cursor: pointer;\n}\n.bre-headers-edit .bre-primary-button:hover:not(:disabled) {\n background: var(--dsw-alias-button-primary-hover, #43454a);\n}\n.bre-headers-edit .bre-primary-button:disabled { opacity: 0.5; cursor: default; }\n.bre-headers-edit .bre-secondary-button {\n height: 36px;\n padding: 0 14px;\n border: 1px solid var(--dsw-alias-border-l3, #0000001f);\n border-radius: var(--dsw-radius-md, 12px);\n background: transparent;\n color: var(--dsw-alias-label-primary, #0f1115);\n font-size: 14px;\n}\n.bre-headers-edit .bre-secondary-button:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover, #2631480f);\n}\n\n/* The row actions and the section's Edit affordance: the official 28px\n bordered small button, and the 21px blue text link the model rows use. */\n.bre-headers-row .bre-link-button {\n height: 28px;\n padding: 2px 8px;\n border: none;\n border-radius: var(--dsw-radius-sm, 8px);\n background: transparent;\n color: var(--dsw-alias-label-secondary, #61666b);\n font-size: 12px;\n line-height: 1;\n}\n.bre-headers-row .bre-link-button:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover, #2631480f);\n}\n.bre-headers .bre-effort-head > .bre-link-button {\n height: 28px;\n padding: 0 10px;\n border: 1px solid var(--dsw-alias-border-l2, #0000001a);\n border-radius: var(--dsw-radius-sm, 8px);\n color: var(--dsw-alias-label-primary, #0f1115);\n font-size: 12px;\n text-decoration: none;\n}\n.bre-headers .bre-effort-head > .bre-link-button:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover, #2631480f);\n text-decoration: none;\n}\n.bre-headers-edit .bre-link-button {\n color: var(--dsw-alias-label-secondary, #61666b);\n font-size: 12px;\n}\n\n/* The section's own fields, on the official input box. */\n.bre-headers-edit input.bre-text-input {\n height: 32px;\n padding: 0 10px;\n border: 1px solid var(--dsw-alias-border-l3, #0000001f);\n border-radius: var(--dsw-radius-md, 12px);\n background: var(--dsw-alias-bg-layer-1, #fff);\n color: var(--dsw-alias-label-primary, #0f1115);\n font-size: 13px;\n box-sizing: border-box;\n}\n.bre-headers-edit input.bre-text-input:focus {\n border-color: var(--dsw-alias-label-tertiary, #81858c);\n outline: none;\n}\n.bre-headers-edit input.bre-text-input:disabled { opacity: 0.6; cursor: default; }\n\n/* Status copy and the coexistence warnings, on the host's own state colours. */\n.bre-headers .bre-effort-note.bre-warn { color: var(--dsw-alias-state-warn-label, #dd8629); }\n.bre-headers .bre-effort-message.bre-success { color: var(--dsw-alias-state-success-primary, #22c55e); }\n.bre-headers .bre-effort-message.bre-error { color: var(--dsw-alias-state-error-primary, #ec1313); }\n\n@media (max-width: 520px) {\n .bre-headers-columns { display: none; }\n .bre-headers-row {\n grid-template-columns: minmax(0, 1fr) minmax(0, 1.25fr) 32px;\n gap: 6px;\n padding: 4px;\n }\n .bre-headers-edit input.bre-text-input {\n padding: 0 8px;\n font-size: 12px;\n }\n}\n\n/* ---- Provider row reordering (Models page) ---- */\n\n/* The whole row header is the drag handle: an 18px grip is a poor thing to\n have to hit, so a drag may start anywhere on the header that is not one of\n the card's own controls (those keep their own cursor, declared below). */\nli[data-bre-row-head='1'] { cursor: grab; }\nli[data-bre-row-head='1']:active { cursor: grabbing; }\nli[data-bre-row-head='1'] button,\nli[data-bre-row-head='1'] a,\nli[data-bre-row-head='1'] input { cursor: pointer; }\n\n/* The grip is mounted as the first child of that header, so it reads as part\n of the row rather than as a control bolted beside it. It is also the button\n that reorders by keyboard. */\n.bre-drag-grip {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: 0 0 auto;\n width: 14px;\n height: 20px;\n margin-right: 2px;\n border-radius: 4px;\n color: var(--dsw-alias-label-tertiary, #81858c);\n cursor: grab;\n touch-action: none;\n}\n\n.bre-drag-grip:hover {\n color: var(--dsw-alias-label-secondary, #61666b);\n background: var(--dsw-alias-interactive-bg-hover, #2631480f);\n}\n\n.bre-drag-grip:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n.bre-drag-grip:active { cursor: grabbing; }\n\n/* What the pointer carries: the browser's own drag image would be a screenshot\n of a whole card, so we hand it this chip with the provider name instead. */\n.bre-drag-ghost {\n position: fixed;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n max-width: 260px;\n padding: 5px 10px 5px 7px;\n border: 1px solid var(--dsw-alias-border-secondary, #e6e8eb);\n border-radius: 8px;\n background: var(--dsw-alias-bg-elevated, #ffffff);\n box-shadow: 0 6px 18px #0000002e;\n color: var(--dsw-alias-label-primary, #17181a);\n font-size: 13px;\n line-height: 18px;\n pointer-events: none;\n transform: translate(-50%, -50%);\n z-index: 60;\n}\n\n.bre-drag-ghost-name {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.bre-drag-ghost svg { color: var(--dsw-alias-label-tertiary, #81858c); }\n\n/* The row the drag is carrying: still in place, visibly the one in hand. */\nli.bre-row-dragging { opacity: .5; }\n\n/* The row the pointer would drop it on \u2014 a secondary cue; the insertion line\n below is the one that says which side. */\nli.bre-row-drop-target { border-color: var(--dsw-alias-brand-primary); }\n\n/* The insertion line. A fixed overlay on the body, never a child of the list\n React owns, so writing it cannot trigger anything in the official section.\n The layer stack matters more than it looks: the provider list lives inside the\n settings dialog, whose overlay is fixed and opaque with z-index 1000, so a\n line in the shell's own range would be drawn behind the dialog and never\n seen. It clears the dialog, and stays under the header's controls. */\n.bre-drop-line {\n position: fixed;\n display: none;\n height: 2px;\n border-radius: 2px;\n background: var(--dsw-alias-brand-primary, #4d6bfe);\n box-shadow: 0 0 0 1px #ffffffb3, 0 1px 4px var(--dsw-alias-brand-primary, #4d6bfe);\n pointer-events: none;\n z-index: 1200;\n}\n\n/* The row slide (a FLIP pass driven from the script) is motion; drop it for a\n reader who asked for less. */\n@media (prefers-reduced-motion: reduce) {\n li[data-bre-row-provider] { transition: none !important; }\n}\n\n@media (max-width: 520px) {\n .bre-drag-grip { width: 18px; height: 24px; }\n .bre-drag-ghost { max-width: 180px; font-size: 12px; }\n}\n\n/* ---- Model rows inside one provider's editor (Models page) ---- */\n\n/* The official model row is a four-column grid (id, display name, two icon\n buttons). Our grip is a fifth child, so the grid needs a leading track to\n hold it \u2014 declared by our own class, because the official rule is a single\n class selector and ours has to win on specificity, not on load order.\n The name must NOT be \"bre-model-row\": that one is this plugin's own composer\n row (see the \".bre-model-row\" block above), and lending it to the host's grid\n would hand that grid our padding, gap, min-height, pointer cursor and hover\n fill along with it. */\n.bre-model-grid[class*=\"modelRow\"] {\n grid-template-columns: auto minmax(0, 1.4fr) minmax(0, 1fr) auto auto;\n}\n\n/* A model row is a grid of inputs, so only the grip drags: the row keeps its\n text cursor and its own click behaviour. The grip takes the height of the\n row's controls, so the pointer has a real target inside a 6px-gap grid. */\n.bre-model-grid > .bre-drag-grip {\n width: 18px;\n height: 24px;\n margin-right: 0;\n}\n\n/* The engine paints the carried row and the row under the pointer with the same\n classes it uses on a provider card; the official model entry carries the\n border, so the drop cue has to land there rather than on the list item. */\ndiv.bre-row-dragging { opacity: .5; }\n[class*=\"modelEntry\"].bre-row-drop-target { border-color: var(--dsw-alias-brand-primary); }\n\n/* The row slide (a FLIP pass driven from the script) is motion; drop it for a\n reader who asked for less. */\n@media (prefers-reduced-motion: reduce) {\n [data-bre-row-model] { transition: none !important; }\n}\n\n/* ---- Provider enable switch (Models page) ---- */\n\n/* Leads the row's own action group (which is pushed right by margin-left:auto),\n in front of the card's buttons, so the destructive one stays last. The knob is\n a pseudo element: no extra node for the official section's tree to trip over. */\n.bre-provider-switch {\n position: relative;\n flex: 0 0 auto;\n width: 30px;\n height: 18px;\n padding: 0;\n border: none;\n border-radius: 9px;\n background: var(--dsw-alias-bg-tertiary, #d7dae0);\n cursor: pointer;\n transition: background 140ms ease;\n}\n\n.bre-provider-switch::after {\n content: '';\n position: absolute;\n top: 2px;\n left: 2px;\n width: 14px;\n height: 14px;\n border-radius: 50%;\n background: var(--dsw-alias-bg-elevated, #ffffff);\n box-shadow: 0 1px 2px #00000026;\n transition: transform 140ms ease;\n}\n\n.bre-provider-switch:not([aria-checked='true']):hover { background: var(--dsw-alias-bg-quaternary, #c9ccd2); }\n\n.bre-provider-switch[aria-checked='true'] { background: var(--dsw-alias-brand-primary, #4d6bfe); }\n\n.bre-provider-switch[aria-checked='true']:hover { background: var(--dsw-alias-brand-primary-hover, #3f5ce8); }\n\n.bre-provider-switch[aria-checked='true']::after { transform: translateX(12px); }\n\n.bre-provider-switch:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary, #4d6bfe);\n outline-offset: 2px;\n}\n\n/* A provider that is switched off stays on the page \u2014 this is the only place it\n can be switched back on \u2014 but it is visibly out of play. The switch itself is\n left at full strength: it is the way back. */\nli.bre-row-disabled [class*=\"rowIdentity\"],\nli.bre-row-disabled [class*=\"rowName\"] { opacity: .5; }\n\n@media (prefers-reduced-motion: reduce) {\n .bre-provider-switch,\n .bre-provider-switch::after { transition: none; }\n}\n\n/* ---- The reserved delete seat (Models page) ---- */\n\n/* A provider the host will not let the user remove renders no delete button, so\n that row's action group \u2014 pushed right by margin-left:auto \u2014 starts further\n right than its neighbours' and the column of buttons lines up raggedly. This\n seat holds the place: it borrows the metrics the official button takes inside\n an action group (.rowActions button is 28px tall / 12px / 0 10px) and the\n dimmed treatment the host gives its own disabled buttons (opacity .4 with a\n default cursor). It is always disabled \u2014 it is the alignment, not an action. */\n.bre-provider-delete {\n box-sizing: border-box;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n height: 28px;\n padding: 0 10px;\n border: none;\n border-radius: var(--dsw-radius-sm, 6px);\n background: 0 0;\n color: var(--dsw-alias-state-error-primary, #e5484d);\n font: inherit;\n font-size: 12px;\n line-height: 18px;\n opacity: .4;\n cursor: default;\n}\n\n/* -----------------------------------------------------------------------\n The provider editor's three tabs.\n\n The official editor lays a provider out as one long column \u2014 identity\n fields, then the model list, then this plugin's request-header section\n one slot up the card. The tab pass (injection/editor-tabs.ts) tags each\n region and stamps the card with the active tab; EVERY visibility decision\n lives here, so the official React tree never moves and a pass failure\n degrades to the untouched one-column editor.\n ----------------------------------------------------------------------- */\n\n/* The segmented control. The plugin's tab-bar component renders it inside the\n request-header mount, which already holds the card's 12px gap slot \u2014 so the\n bar reaches the panes in the card's own rhythm WITHOUT this plugin ever\n inserting a node into the official child list. It stays hidden until the pass\n marks the card: a card the pass could not take over (the DeepSeek account\n editor) keeps that mount visible for its request-header section, and must not\n grow a bar that switches nothing. */\n.bre-editor-tabs {\n display: none;\n gap: 2px;\n padding: 2px;\n border-radius: 10px;\n background: var(--dsw-alias-fill-tertiary, rgba(120, 125, 140, .09));\n}\n\n/* Only a taken-over card shows the bar. */\n.bre-tabbed .bre-editor-tabs { display: flex; }\n\n/* ...and while the takeover is on, the mount holding that bar MUST be visible.\n This is the one place the takeover does not delegate its reveal to the\n official-edit state: the bar lives in here, and gating it on the _editor\n class probe would make the tabs depend on a probe an official redesign can\n break \u2014 with data-edit stuck at 0 the bar would be inside a display:none\n parent and no rule of ours could reach it. The taken-over class is written by\n the pass itself, in the same frame, so it is the trustworthy half of the\n pair. The section's own visibility stays a pane decision (below). */\n.bre-tabbed .bre-headers-host { display: block; }\n\n.bre-editor-tab {\n flex: 1 1 0;\n min-width: 0;\n height: 28px;\n padding: 0 10px;\n border: none;\n border-radius: 8px;\n background: transparent;\n color: var(--dsw-alias-label-secondary, #61666b);\n font: inherit;\n font-size: 12px;\n font-weight: 500;\n line-height: 1;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n cursor: pointer;\n transition: background 140ms ease, color 140ms ease, box-shadow 140ms ease;\n}\n\n.bre-editor-tab:hover { color: var(--dsw-alias-label-primary, #0f1115); }\n\n/* The selected segment lifts out of the track: the page's own surface colour\n plus the card's own hairline, the same elevation the host uses for raised\n controls. Hover on a selected tab stays put \u2014 it is already where you are. */\n.bre-editor-tab[aria-selected='true'],\n.bre-editor-tab[aria-selected='true']:hover {\n background: var(--dsw-alias-bg-layer-1, #fff);\n color: var(--dsw-alias-label-primary, #0f1115);\n box-shadow: 0 1px 2px #0000001a, 0 0 0 1px var(--dsw-alias-border-l2, #0000001a);\n}\n\n.bre-editor-tab:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary, #4d6bfe);\n outline-offset: 1px;\n}\n\n/* An advanced section with no headers mount (pre-first-render, feature off)\n offers an empty pane \u2014 the button steps aside until the mount exists. */\n.bre-editor-tab[hidden] { display: none; }\n\n/* Pane visibility, keyed on the card's active tab. Only the HIDING has to win\n a specificity fight (against the official sheet and the headers host's own\n reveal rule); the active pane keeps whatever display the official layout\n gave it, so showing must not declare a display at all. */\n.bre-tabbed:not([data-bre-tab='provider']) [data-bre-region='provider'],\n.bre-tabbed:not([data-bre-tab='models']) [data-bre-region='models'],\n.bre-tabbed:not([data-bre-tab='advanced']) [data-bre-region='advanced'] {\n display: none !important;\n}\n\n/* The advanced tab: the official body \u2014 fields, model list, action row and the\n editor's own header \u2014 steps aside for the request-header section, which is a\n complete form with its own save/cancel and becomes the pane under the very\n same bar. */\n.bre-tabbed[data-bre-tab='advanced'] [data-bre-editor-body] {\n display: none !important;\n}\n\n/* The advanced pane shows the section for the same reason the mount is shown:\n a pane that is already the active one has no business depending on the\n official-edit probe. The rule above still wins for every other tab, and the\n base bre-headers rule keeps a collapsed card's section hidden \u2014 a card only\n carries the taken-over class while its editor is open. */\n.bre-tabbed[data-bre-tab='advanced'] .bre-headers { display: flex; }\n\n/* The pane is a card, and the section draws it. On the advanced tab the section\n is not an appendage to the fields above it any more \u2014 it is the whole surface\n \u2014 so it takes the inset fill the official module cards use (the add and setup\n cards): the one fill in this system that stays visible on the content card's\n own white in the light theme and reads as raised in the dark one, where every\n theme layer token collapses onto the same value. The hairline goes with it: it\n divided this section from those fields, and here the bar above ends the pane.\n The 12px top margin sets the card off the tab bar above it \u2014 the same gap the\n provider and models panes put between the bar and their own cards \u2014 and the\n 12px padding is the card's inner inset, not the separator. */\n.bre-tabbed[data-bre-tab='advanced'] .bre-headers {\n margin-top: 12px;\n padding: 12px;\n border-top: none;\n border-radius: var(--dsw-radius-lg, 16px);\n background: var(--dsw-alias-bg-module-platform, #f5f6f7);\n}\n\n/* The live editor folds its identity fields and model list into one\n \"Customized\" disclosure. Under the tabs the grouping is the panes' job: the\n group stays expanded (the pass forces it open) and its summary row \u2014 now a\n toggle with nothing left to toggle \u2014 steps aside. */\n.bre-tabbed [data-bre-editor-body] details > summary {\n display: none !important;\n}\n\n/* A region that is NOT a pane. The editor's own header repeats the card row's\n provider name and route tag, and that row is already on screen directly above\n it, so the header is hidden on every tab. No pane rule above can select this\n id, which is what keeps it hidden whichever tab is active. */\n.bre-tabbed [data-bre-region='none'] {\n display: none !important;\n}\n\n/* The Models pane shows the custom model list and the catalogue, and two\n hairlines used to divide it: the \"Customized\" group's separator from the key\n field (which this pane hides) and the catalogue's own, drawn directly above\n its \"Model catalogue\" heading. Neither has anything to divide here \u2014 the tab\n bar already ends the pane above, and the divider between the two lists above\n the catalogue is the group's boundary, not a rule. Both go, with every top\n padding that held content off them (the group's 10px, its body's 12px, the\n catalogue's 12px), so the pane runs from the tab bar into the catalogue with\n only the official editor's own 14px gap between blocks. */\n.bre-tabbed[data-bre-tab='models'] [data-bre-editor-body] details[class*='customized'] {\n border-top: none;\n padding-top: 0;\n}\n\n.bre-tabbed[data-bre-tab='models'] [data-bre-editor-body] details[class*='customized'] > [class*='customizedBody'] {\n padding-top: 0;\n}\n\n/* The catalogue is a section (its heading, title and meta are the divs inside\n it), so this probe hits the ruled box and nothing else. */\n.bre-tabbed[data-bre-tab='models'] [data-bre-editor-body] section[class*='modelCatalog'] {\n border-top: none;\n padding-top: 0;\n}\n\n/* The catalogue heading names a box this pane has already named for the user:\n the Models tab IS the catalogue, so \"\u6A21\u578B\u76EE\u5F55\" says it twice, and\n \"\u5DF2\u81EA\u5B9A\u4E49\u6A21\u578B\u76EE\u5F55\" (the meta line beside it) says nothing at all \u2014 the rows\n below ARE the customized catalogue. Both strings are the host's own, so they\n go by class stem, and the heading takes its title and meta with it. The\n head's own controls (\u6062\u590D\u9ED8\u8BA4\u6A21\u578B / \u83B7\u53D6\u53EF\u7528\u6A21\u578B) are siblings of the heading,\n not children, so they stay \u2014 the auto-adapt seat is anchored to them. */\n.bre-tabbed[data-bre-tab='models'] [data-bre-editor-body] [class*='modelCatalogHeading'] {\n display: none;\n}\n\n/* With the heading gone the head holds nothing but controls, so they group at\n the start of the row (the official rule spreads them to opposite edges) and\n centre against each other \u2014 the host's links are 28px tall, the seat is not.\n The official gap is left alone: the seat's own 4px margin tops it up. */\n.bre-tabbed[data-bre-tab='models'] [data-bre-editor-body] [class*='modelListHead'] {\n justify-content: flex-start;\n align-items: center;\n}\n\n/* The API-key eye. The official key field is a plain flex column whose input is\n its last in-flow child, so the button hangs on the field's own box and is\n measured against the input's 32px row \u2014 the official markup offers no\n wrapper, and introducing one would mean moving DOM the host owns. The input\n keeps the room the button needs. */\n.bre-key-field { position: relative; }\n\n.bre-key-field > input { padding-right: 34px; }\n\n/* A field the plugin's key list manages hands its input over: the list below\n says the same thing once per key and keeps the pick, the alias and the value\n in one place, so an official box that writes the provider's single key would\n be a second, conflicting way to do it. The input stays in the DOM \u2014 the\n host's form still owns it and the reveal pass still finds it \u2014 so unmanaging\n the field brings the official box back. The marker is what the reveal pass\n stamps on that input; the direct-child form covers the pass that has not run\n yet, and the eye is the button the same pass hangs on the field. */\n.bre-key-managed [data-bre-key-input],\n.bre-key-managed > input,\n.bre-key-managed .bre-key-eye { display: none; }\n\n/* A field that failed validation grows a message line under the input; the eye\n rides above it so it stays on the input it belongs to (18px line + 6px gap). */\n.bre-key-field:has(> p) .bre-key-eye { bottom: 24px; }\n\n.bre-key-eye {\n position: absolute;\n right: 4px;\n bottom: 0;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 32px;\n padding: 0;\n border: 0;\n border-radius: 8px;\n background: transparent;\n color: var(--dsw-alias-label-secondary, #6b7280);\n cursor: pointer;\n -webkit-appearance: none;\n appearance: none;\n transition: background 140ms ease, color 140ms ease;\n}\n\n.bre-key-eye:hover {\n color: var(--dsw-alias-label-primary, #0f1115);\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.bre-key-eye:focus-visible {\n outline: none;\n box-shadow: inset 0 0 0 2px var(--dsw-alias-border-l3);\n}\n\n/* Resolving a stored key is a same-origin round trip, so the button can be\n busy: it stays in place and dims rather than changing size or disappearing. */\n.bre-key-eye[aria-busy='true'] {\n opacity: 0.55;\n cursor: progress;\n}\n\n.bre-key-eye[hidden] { display: none; }\n\n/* One glyph at a time. The state lives on the button's own attribute, so a\n click swaps the icon without the host re-rendering anything. */\n.bre-key-eye svg[data-bre-icon] { display: none; }\n\n.bre-key-eye[aria-pressed='false'] svg[data-bre-icon='eye'],\n.bre-key-eye[aria-pressed='true'] svg[data-bre-icon='eye-off'] { display: block; }\n\n/* The key list. The plugin mounts it into its own host, a sibling placed right\n after the official key field, so the list sits under the input without\n changing the field's own box: the reveal eye is measured against that box\n (bottom: 0), and a list inside the field would drag the eye down onto the\n list's last row. The margin only covers the case where the card spaces its\n fields with margins instead of a gap. */\n.bre-keys-host {\n display: block;\n min-width: 0;\n margin-top: 4px;\n}\n\n.bre-keys { display: flex; flex-direction: column; gap: 8px; }\n\n/* While the key list is still reading, the panel renders NOTHING (the component\n returns an empty .bre-keys). An empty ruled box \u2014 or a placeholder line that\n then swaps to the list \u2014 would paint one frame of a panel that then visibly\n changes: the \"the key section flashes when you press Edit\" report. :empty\n takes the panel out of the layout until the first answer lands, so the list\n is the first thing the key area ever paints, and nothing under it moves. */\n.bre-keys:empty { display: none; }\n\n/* One ruled container holding the keys and the add entry, the way a settings\n list reads: a single hairline box, and hairlines between rows only. */\n.bre-keys-list {\n display: flex;\n flex-direction: column;\n margin: 0;\n padding: 0;\n border: 0.5px solid var(--dsw-alias-border-l2, #d5d7dd);\n border-radius: 8px;\n list-style: none;\n overflow: hidden;\n}\n\n.bre-keys-list > li + li { border-top: 0.5px solid var(--dsw-alias-border-l2, #d5d7dd); }\n\n.bre-keys-row {\n display: flex;\n align-items: center;\n gap: 8px;\n min-width: 0;\n padding: 9px 10px;\n}\n\n/* The enable choice closes the row. The radio stays the real input \u2014 so the\n keys of one provider are one group, choosing one clears the rest, and the\n pick is keyboard-reachable \u2014 and the circle is its face; the word beside it\n names the action instead of a state to hunt for. */\n.bre-keys-enable {\n display: inline-flex;\n flex: 0 0 auto;\n align-items: center;\n gap: 6px;\n cursor: pointer;\n}\n\n.bre-keys-enable-text {\n font-size: 11px;\n line-height: 1.5;\n color: var(--dsw-alias-label-secondary, #6b7280);\n}\n\n.bre-keys-enable:has(.bre-keys-enabled:checked) .bre-keys-enable-text {\n color: var(--dsw-alias-label-primary, #0f1115);\n}\n\n.bre-keys-enabled {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n border: 0;\n overflow: hidden;\n white-space: nowrap;\n clip-path: inset(50%);\n}\n\n.bre-keys-mark {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 16px;\n height: 16px;\n box-sizing: border-box;\n border: 1px solid var(--dsw-alias-border-l3, #9aa0aa);\n border-radius: 50%;\n color: var(--dsw-alias-bg-layer-1, #ffffff);\n transition: background 120ms ease, border-color 120ms ease;\n}\n\n.bre-keys-mark > svg { width: 12px; height: 12px; }\n\n.bre-keys-enabled:checked + .bre-keys-mark {\n border-color: var(--dsw-alias-label-primary, #0f1115);\n background: var(--dsw-alias-label-primary, #0f1115);\n}\n\n.bre-keys-enabled:focus-visible + .bre-keys-mark {\n outline: none;\n box-shadow: 0 0 0 2px var(--dsw-alias-brand-primary, #3b82f6);\n}\n\n/* With an alias the alias is the row's name and the masked key follows it as a\n quiet code pill; without one the masked key is the name and takes the row's\n measure, so a key is always readable as itself. */\n.bre-keys-alias {\n flex: 0 1 auto;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 12px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary, #0f1115);\n}\n\n.bre-keys-mask {\n flex: 0 1 auto;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n padding: 1px 6px;\n border-radius: 4px;\n background: var(--dsw-alias-interactive-bg-hover, rgba(127, 127, 127, 0.08));\n color: var(--dsw-alias-label-secondary, #6b7280);\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 11px;\n}\n\n.bre-keys-mask-plain {\n flex: 1 1 auto;\n padding: 0;\n background: transparent;\n color: var(--dsw-alias-label-primary, #0f1115);\n font-family: inherit;\n font-size: 12px;\n}\n\n/* The row's three actions, revealed on hover or keyboard focus rather than\n removed, so the row keeps its width and nothing shifts under the pointer.\n Revealing is opacity alone; a pointer that cannot hover simply always sees\n them, because a hidden action there would be an unreachable one. */\n.bre-keys-actions {\n display: inline-flex;\n flex: 0 0 auto;\n align-items: center;\n gap: 10px;\n margin-left: auto;\n opacity: 0;\n pointer-events: none;\n transition: opacity 120ms ease;\n}\n\n.bre-keys-row:hover .bre-keys-actions,\n.bre-keys-row:focus-within .bre-keys-actions {\n opacity: 1;\n pointer-events: auto;\n}\n\n@media (hover: none) {\n .bre-keys-actions { opacity: 1; pointer-events: auto; }\n}\n\n.bre-keys-danger { color: var(--dsw-alias-state-error-primary, #c62828); }\n\n/* The one inline form. It opens under the row it edits, or \u2014 for a new key \u2014 as\n the list's own last item, which is what keeps adding a key in place instead\n of in a dialog that has to be found and dismissed. */\n.bre-keys-form-row { padding: 10px; }\n\n.bre-keys-form { display: flex; flex-direction: column; gap: 8px; }\n\n.bre-keys-form-fields {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 8px;\n}\n\n/* One field means the whole row: the value form holds only the secret, and it\n must not sit in half a row beside an empty half. */\n.bre-keys-form-fields > :only-child { grid-column: 1 / -1; }\n\n/* The shared input caps itself at 240px for the panels it was written for; here\n the field is the measure. */\n.bre-keys-form-fields .bre-text-input { max-width: none; }\n\n.bre-keys-form-actions {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 8px;\n}\n\n/* The shared primary button is styled inside the headers editor, which is not\n where this form lives, so the key list carries its own compact copy. */\n.bre-keys-form-actions .bre-primary-button {\n height: 28px;\n padding: 0 12px;\n border: 0;\n border-radius: var(--dsw-radius-md, 8px);\n background: var(--dsw-alias-button-primary-fill, #0f1115);\n color: var(--dsw-alias-label-primary-foreground, #ffffff);\n font: inherit;\n font-size: 12px;\n cursor: pointer;\n -webkit-appearance: none;\n appearance: none;\n}\n\n.bre-keys-form-actions .bre-primary-button:hover {\n background: var(--dsw-alias-button-primary-hover, #43454a);\n}\n\n.bre-keys-form-actions .bre-primary-button:disabled { opacity: 0.5; cursor: default; }\n\n.bre-keys-form-actions .bre-primary-button:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--dsw-alias-brand-primary, #3b82f6);\n}\n\n/* The add entry is the list's own last row \u2014 full width, the same shape as the\n keys it adds to \u2014 so the way in and the thing it makes read as one list. */\n.bre-keys-add-row { padding: 0; }\n\n.bre-keys-add {\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n padding: 9px 10px;\n box-sizing: border-box;\n border: 0;\n background: transparent;\n color: var(--dsw-alias-label-secondary, #6b7280);\n font: inherit;\n font-size: 12px;\n text-align: left;\n cursor: pointer;\n -webkit-appearance: none;\n appearance: none;\n transition: background 120ms ease, color 120ms ease;\n}\n\n.bre-keys-add:hover {\n background: var(--dsw-alias-interactive-bg-hover, rgba(127, 127, 127, 0.08));\n color: var(--dsw-alias-label-primary, #0f1115);\n}\n\n.bre-keys-add:focus-visible {\n outline: none;\n box-shadow: inset 0 0 0 2px var(--dsw-alias-brand-primary, #3b82f6);\n}\n\n.bre-keys-add:disabled { opacity: 0.55; cursor: default; }\n\n.bre-keys-add-mark {\n display: inline-flex;\n flex: 0 0 auto;\n align-items: center;\n justify-content: center;\n width: 16px;\n height: 16px;\n}\n\n.bre-keys .bre-effort-message { font-size: 11px; }\n\n@media (prefers-reduced-motion: reduce) {\n .bre-editor-tab { transition: none; }\n .bre-key-eye { transition: none; }\n .bre-keys-mark, .bre-keys-actions, .bre-keys-add { transition: none; }\n}\n`\n", "/**\n * The composer model menu injection: the reasoning-effort slider AND the model\n * search box, both mounted inside the OFFICIAL model menu opened from the\n * bottom-right seat. The official menu body remains host-owned apart from the\n * replicated rows; its trigger keeps its own children and receives only a\n * provider prefix through a plugin-owned data attribute.\n *\n * This module is the single place that decides which pane the menu is showing,\n * where each foreign body sits, and when each one retires. The two pane probes\n * look alike but answer different questions, and must NOT be merged:\n *\n * - the slider's probe \u2014 `[role=\"menuitemradio\"]` \u2014 asks \"did the official\n * drill-in replace my replica body?\". The effort pane matches too, which\n * is fine: our body belongs to the root pane either way.\n * - the search's probe \u2014 `section[role=\"group\"]` \u2014 asks \"is there a\n * filterable model list here?\". The effort pane has no groups, so a\n * role-based probe would mount a search box over a list it cannot filter.\n *\n * @module dsh-model-think-level/client/injection/composer-menu\n */\n\nimport { createElement } from 'react'\nimport { flushSync } from 'react-dom'\nimport type { ReactNode } from 'react'\nimport type { Translate } from '@deepseek-ai/dsh-client-ui-slots'\nimport { PLUGIN_ID } from '../../constants.js'\nimport { ComposerSlider, providerLabelOf } from '../ComposerSlider.js'\nimport { ModelSearch, isModelPane } from '../ModelSearch.js'\nimport type { ModelDirectoryLike } from '../types.js'\nimport { EffortBoundary, mountReact, unmountReact, type ForeignMount } from './mount.js'\n\n/** Everything the composer injection needs from its host. */\nexport interface ComposerMenuDeps {\n /** The official menu element, or undefined while it is closed. */\n menuOf: () => HTMLElement | undefined\n /** The official model trigger, present even while the menu is closed. */\n triggerOf: () => HTMLElement | undefined\n /** The plugin's locale-bound translator. */\n t: Translate\n /** Wrap a subtree so it re-translates on a language switch. */\n refreshed: (children: () => ReactNode) => ReactNode\n /** Whether the composer slider preference is on. */\n sliderEnabled: () => boolean\n /** The current session's model directory, or undefined in the boot window. */\n directory: () => ModelDirectoryLike | undefined\n}\n\n/** The composer injection's face: one reconcile per scan, one dispose per fiber. */\nexport interface ComposerMenuInjection {\n /** Mount/reposition/retire both foreign roots (idempotent, mutation-cheap). */\n reconcile: () => void\n /** Unmount both roots; the fiber is going away. */\n dispose: () => void\n}\n\n/**\n * Re-run the official placement inside the CURRENT frame.\n *\n * The card anchors itself in a layout effect (ModelSelect.tsx:163-188) that\n * runs BEFORE this module's MutationObserver injection, so its `top` is\n * computed from the PRE-injection height: the slider replica is still shown\n * and the search box not yet mounted, which floated the card ~56px above the\n * trigger. Both edits are done synchronously by the callers (the injected\n * roots commit inside `flushSync`, the replica is hidden inline), so this\n * flushSync-wrapped `resize` lets the official `place()` re-measure and commit\n * BEFORE the browser paints \u2014 no intermediate frame is ever shown. Waiting for\n * a later frame (the previous implementation) is exactly what made the menu\n * visibly jump.\n *\n * Only the official effect ever writes the card's position: this module still\n * adds no class and no inline style of its own.\n *\n * COUPLING (deliberate, and the reason there is no geometry code here): this\n * works only because the official card re-runs its `place()` on a window\n * `resize` and commits the result through React state. A kernel that switches\n * to `requestAnimationFrame` placement or writes the position outside React\n * would leave this a no-op \u2014 the menu would keep the pre-injection `top`\n * instead of jumping, i.e. it would degrade back to the mis-anchored (not\n * broken) state the earlier fixes describe, never to a crash.\n */\nconst rePlaceInFrame = (): void => {\n flushSync(() => { window.dispatchEvent(new Event('resize')) })\n}\n\n/**\n * Build the composer menu injection.\n * @param deps - menu lookup, copy, locale refresh, preference and directory.\n * @returns the injection's {@link ComposerMenuInjection} face.\n */\nexport function createComposerMenu(deps: ComposerMenuDeps): ComposerMenuInjection {\n const { menuOf, triggerOf, t, refreshed, sliderEnabled, directory } = deps\n let sliderMount: ForeignMount | undefined\n let searchMount: ForeignMount | undefined\n let lastModelPane: boolean | undefined\n let triggerDirectory: ModelDirectoryLike | undefined\n let triggerUnsubscribe: (() => void) | undefined\n let decoratedTrigger: HTMLElement | undefined\n\n const clearTriggerDecoration = (): void => {\n decoratedTrigger?.removeAttribute('data-bre-provider')\n decoratedTrigger = undefined\n }\n\n /** Update the provider prefix without changing the host-owned trigger children. */\n const updateTriggerDecoration = (): void => {\n const trigger = triggerOf()\n if (decoratedTrigger !== undefined && decoratedTrigger !== trigger) {\n clearTriggerDecoration()\n }\n const state = triggerDirectory?.store.getSnapshot()\n const current = state?.current\n if (trigger === undefined || current === null || current === undefined) {\n if (trigger === decoratedTrigger) clearTriggerDecoration()\n return\n }\n const group = state?.groups.find(candidate => candidate.id === current.provider)\n const provider = group === undefined ? current.provider : providerLabelOf(group)\n if (provider.trim() === '') {\n if (trigger === decoratedTrigger) clearTriggerDecoration()\n return\n }\n trigger.dataset['breProvider'] = provider\n decoratedTrigger = trigger\n }\n\n /** Follow whichever session directory the official composer currently owns. */\n const syncTriggerDirectory = (): void => {\n const next = directory()\n if (next !== triggerDirectory) {\n triggerUnsubscribe?.()\n triggerUnsubscribe = undefined\n triggerDirectory = next\n if (next !== undefined) triggerUnsubscribe = next.store.subscribe(updateTriggerDecoration)\n }\n updateTriggerDecoration()\n }\n\n const unmountSearch = (): void => {\n unmountReact(searchMount)\n searchMount = undefined\n }\n\n /**\n * Reconcile the search box into the official model menu (idempotent).\n *\n * Deliberately NO class on the menu and NO width/height of our own: the\n * official card measures itself in a layout effect whose deps\n * ([open, pane, state], ModelSelect.tsx:163-188) a later injection cannot\n * invalidate, so any resize from here would leave the card anchored to its\n * pre-injection height (and no longer right-aligned with its trigger). The\n * official box already caps itself (max-height) and scrolls its groups\n * container, so it needs no help.\n */\n const reconcileSearch = (menu: HTMLElement): void => {\n // Positioned from STRUCTURE, not class names: the group sections' parent\n // IS the official scrolling container, while the `groups` class on it is a\n // CSS-module hash (`Uc5hea_groups`) that a literal `.groups` selector never\n // matches. Inserting before that container keeps the official load notices\n // on top and leaves the slider wrapper as the menu's first child.\n const group = menu.querySelector<HTMLElement>('section[role=\"group\"]')\n const container = group?.parentElement\n const referenceNode = (container !== null && container !== undefined && container.parentElement === menu)\n ? container\n : group\n\n if (searchMount === undefined) {\n const wrapper = document.createElement('div')\n wrapper.dataset['plugin'] = PLUGIN_ID\n wrapper.dataset['breSearch'] = '1'\n menu.insertBefore(wrapper, referenceNode)\n searchMount = mountReact(\n wrapper,\n createElement(EffortBoundary, {\n fallbackText: t('modelSearchFailed'),\n children: refreshed(() => createElement(ModelSearch, { menu, t })),\n }),\n // Sync: the card is measured right after this, so the input must\n // already contribute its height (see rePlaceInFrame).\n { sync: true },\n )\n return\n }\n if (searchMount.wrapper.parentElement !== menu) {\n menu.insertBefore(searchMount.wrapper, referenceNode)\n return\n }\n if (referenceNode !== null && searchMount.wrapper.nextSibling !== referenceNode) {\n menu.insertBefore(searchMount.wrapper, referenceNode)\n }\n }\n\n /** Reconcile the slider into the official model menu (idempotent). */\n const reconcileSliderInto = (menu: HTMLElement): void => {\n if (!sliderEnabled()) {\n unmountReact(sliderMount)\n sliderMount = undefined\n // The preference flipped off while the menu stayed open: the host menu\n // must go back to the official content-sized box AND its root cells\n // must become visible again (the active branch hides them inline).\n menu.classList.remove('bre-model-menu-host')\n for (const el of Array.from(menu.children)) {\n // Attribute-only probe on purpose: `instanceof HTMLButtonElement` is\n // realm-bound and silently fails when the official card is rendered\n // from another realm (it then never matched, so the root cells stayed\n // hidden after the preference was switched off).\n if (el.getAttribute('role') === 'menuitem') {\n // `menu.children` is an Element collection; the cells are buttons.\n ;(el as HTMLElement).style.display = ''\n }\n }\n return\n }\n const resolved = directory()\n if (resolved === undefined) {\n // Transient boot window only: the seat itself cannot mount before the\n // directory service resolves, so a menu open here is momentary. The\n // next DOM mutation re-enters (the mounted slider keeps its face).\n return\n }\n // React re-renders the menu around a foreign node; keep our wrapper as the\n // menu's FIRST child by re-inserting it whenever a re-render displaced it.\n // The React root survives DOM moves (the fiber tree is position-independent),\n // so a displaced slider is re-attached, never re-created.\n if (sliderMount === undefined) {\n const wrapper = document.createElement('div')\n wrapper.dataset['plugin'] = PLUGIN_ID\n wrapper.dataset['breSlider'] = '1'\n menu.insertBefore(wrapper, menu.firstChild)\n sliderMount = mountReact(wrapper,\n createElement(EffortBoundary, {\n fallbackText: t('renderFailed'),\n children: refreshed(() => createElement(ComposerSlider, {\n directory: resolved,\n t,\n // The replicated body renders its OWN model and level lists, so it\n // never has to hand control back to the official drill-in pane \u2014\n // that is what lets picking a model collapse only the list while\n // the popover (and the still-unpicked level row) stays open.\n // It only needs the card re-measured whenever its height changes:\n // the official card measures from its own layout effect and only\n // re-runs on a window resize, which rePlaceInFrame dispatches.\n onLayoutChange: rePlaceInFrame,\n })),\n }),\n // Sync: the replica changes the menu's height, so it must be committed\n // before rePlaceInFrame re-measures (same-frame, no visible jump).\n { sync: true },\n )\n } else if (sliderMount.wrapper.parentElement !== menu || menu.firstChild !== sliderMount.wrapper) {\n menu.insertBefore(sliderMount.wrapper, menu.firstChild)\n }\n // The replicated popover body takes the upstream .re-model-menu box.\n menu.classList.add('bre-model-menu-host')\n // Popover replication rules, mirroring upstream's pane behaviour:\n // - ROOT pane: our body (slider + separator + model row) IS the content;\n // the official menu's root cells (role=menuitem direct children \u2014\n // \"Model\" and \"Effort\") are duplicated and hidden. A pane switch\n // re-creates them and the next scan re-applies.\n // - Model-pane drill-in (menuitemradio rows): upstream shows ONLY the\n // pane, so our body retires while the official list is open.\n // Known tradeoff (a11y): the official shell's Arrow-key roving focus walks\n // the hidden cells \u2014 focus() on a display:none node is a no-op \u2014 so\n // keyboard users reach the replica via Tab; the replica row's Enter opens\n // the official model list, keeping model switching keyboard-reachable.\n const cells = Array.from(menu.children)\n .filter((el): el is HTMLElement => el.getAttribute('role') === 'menuitem')\n // Only a NON-descendant radio counts as the official drill-in. A plain\n // descendant test would be tripped by any menuitemradio the replica itself\n // renders, which would make the replica hide itself. The wrapper is hoisted\n // into a const first: `sliderMount`'s narrowing does not survive into the\n // predicate's closure.\n const wrapper = sliderMount.wrapper\n const officialPane = Array.from(menu.querySelectorAll('[role=\"menuitemradio\"]'))\n .some(el => !wrapper.contains(el))\n if (officialPane) {\n // Focus handoff BEFORE hiding: the replica row (possibly focused) is\n // about to become display:none. Without moving focus first the browser\n // drops it to <body>, the official shell's onBlur reads that as \"focus\n // left the seat\" and closes the menu instantly \u2014 the flash-out that\n // made the model list unusable. The menu itself sits inside the seat\n // root, so focusing it keeps the blur inside (tabindex -1: no Tab\n // stop, no focus ring for programmatic focus).\n if (sliderMount.wrapper.contains(document.activeElement)) {\n menu.tabIndex = -1\n menu.focus({ preventScroll: true })\n }\n sliderMount.wrapper.style.display = 'none'\n } else {\n sliderMount.wrapper.style.display = ''\n for (const cell of cells) cell.style.display = 'none'\n }\n }\n\n const reconcile = (): void => {\n syncTriggerDirectory()\n const menu = menuOf()\n if (menu === undefined) {\n unmountReact(sliderMount)\n sliderMount = undefined\n unmountSearch()\n lastModelPane = undefined\n return\n }\n // The search box is unconditional: it does not follow the slider\n // preference (see the spec's \"\u641C\u7D22\u6052\u5F00\" decision).\n const modelPane = isModelPane(menu)\n if (modelPane) reconcileSearch(menu)\n else unmountSearch()\n\n reconcileSliderInto(menu)\n\n // A pane switch is exactly when this module rewrites the menu's contents\n // (the slider replica hides, the search box mounts or unmounts), so the\n // card's already-computed `top` is stale. Both edits above are synchronous\n // (the roots commit inside flushSync, the replica toggles inline), so the\n // official placement can be re-run in THIS frame \u2014 no jump is ever painted.\n // Filtering and scrolling only toggle row visibility and never reach here.\n if (modelPane !== lastModelPane) {\n lastModelPane = modelPane\n if (modelPane || sliderEnabled()) rePlaceInFrame()\n }\n }\n\n const dispose = (): void => {\n triggerUnsubscribe?.()\n triggerUnsubscribe = undefined\n triggerDirectory = undefined\n clearTriggerDecoration()\n unmountReact(sliderMount)\n sliderMount = undefined\n unmountSearch()\n lastModelPane = undefined\n }\n\n return { reconcile, dispose }\n}\n", "/**\n * The replicated composer popover body: a \"model\" row and a \"reasoning level\"\n * row, both driven from the official model directory.\n *\n * Presentation follows `dsh-tauri-model-config`: one bordered card, a chevron\n * that rotates as its panel opens, and a selectable list rendered under the row\n * it belongs to. The model list is a provider column with the models of the\n * selected provider beside it. The radiation slider this module shipped with\n * upstream is gone \u2014 it was reported as ugly and the reference plugin has none.\n *\n * Four invariants are load-bearing and must survive any edit:\n * - the body is mounted INSIDE the official model menu, and the shell closes\n * that menu on any blur that leaves the card, so every commit hands focus to\n * the menu BEFORE the busy state disables the control that was clicked\n * (issue #13's snap-shut);\n * - the levels always come from the model the directory currently reports,\n * never from a cached label, so a model switch re-reads the ladder;\n * - picking a model must NOT close the popover: the reasoning level below is\n * still unpicked, so the list collapses and the rows stay;\n * - a panel and the card's re-measure must land in the SAME frame (see\n * {@link ComposerSlider}'s `togglePanel`), or the panel is painted once at\n * the stale position and the menu visibly jumps.\n *\n * @module dsh-model-think-level/client/ComposerSlider\n */\nimport {\n createElement,\n useCallback,\n useEffect,\n useRef,\n useState,\n useSyncExternalStore,\n} from 'react'\nimport { flushSync } from 'react-dom'\nimport type { ReactNode } from 'react'\nimport { inferModalitiesFromName, matchKnowledgeBase } from '../knowledge.js'\nimport { selectRefusalMessage } from './effort-memory.js'\nimport {\n disabledProviders,\n subscribeDisabledProviders,\n} from './provider-enabled.js'\nimport {\n baseProviderOrder,\n mergeProviderOrder,\n providerOrder,\n subscribeProviderOrder,\n} from './provider-order.js'\nimport {\n modelOrders,\n orderModelEntries,\n subscribeModelOrders,\n} from './model-order.js'\nimport type {\n DirectoryCurrentLike,\n DirectoryGroupLike,\n EffortLevelLike,\n ModelDirectoryLike,\n ModelDirectoryStateLike,\n} from './types.js'\n\n/** The model's declared effort levels, when there are at least two to choose from. */\nexport function sliderLevels(state: ModelDirectoryStateLike): readonly EffortLevelLike[] {\n if (state.current === null) return []\n const group = state.groups.find(candidate => candidate.id === state.current?.provider)\n const model = group?.models.find(candidate => candidate.id === state.current?.model)\n const efforts = model?.reasoning?.efforts\n return efforts !== undefined && efforts.length >= 2 ? efforts : []\n}\n\n/** The current model of a directory snapshot. */\nexport function currentModelOf(state: ModelDirectoryStateLike): DirectoryCurrentLike | null {\n return state.current\n}\n\n/**\n * Level index the body should report as current: the session's current effort\n * when the model still offers it, else the adapter default, else the middle.\n */\nexport function effectiveEffortIndex(\n levels: readonly EffortLevelLike[],\n state: ModelDirectoryStateLike,\n): number {\n const model = currentModelOf(state)\n const current = levels.findIndex(level => level.id === model?.reasoningEffort)\n if (current >= 0) return current\n const group = state.groups.find(candidate => candidate.id === model?.provider)\n const fallback = group?.models.find(candidate => candidate.id === model?.model)\n ?.reasoning?.defaultEffort\n const at = fallback === undefined ? -1 : levels.findIndex(level => level.id === fallback)\n if (at >= 0) return at\n return Math.floor((levels.length - 1) / 2)\n}\n\n/** The plugin's bound translator (the props face, named for reuse below). */\ntype Translator = (key: string, params?: Record<string, string | number>) => string\n\n/** Props of {@link ComposerSlider}. */\nexport interface ComposerSliderProps {\n /** The session's shared model directory (load + select ride the official seam). */\n directory: ModelDirectoryLike\n /** Localized copy (the plugin's bound translator). */\n t: Translator\n /**\n * Re-measure hook. The official card measures itself from its own layout\n * effect (`[open, pane, state]`) and only re-runs on a window `resize`, so a\n * body that grows here must ask the injector to re-place it \u2014 in the same\n * frame, never from a deferred task.\n */\n onLayoutChange?: () => void\n}\n\n/** Which row has its list open. At most one at a time. */\ntype Panel = 'model' | 'effort' | null\n\n/** A model row of one provider group, as the directory exposes it. */\ninterface ModelChoiceLike {\n id: string\n name?: string\n /** The declared effort ladder; present when the model reasons at all. */\n reasoning?: { efforts?: readonly unknown[]; defaultEffort?: string }\n /** Declared request modalities in core vocabulary (`image` = vision). */\n input?: readonly string[]\n}\n\n/** The models of one provider group (the SDK type leaves the element open). */\nfunction modelsOf(group: DirectoryGroupLike): readonly ModelChoiceLike[] {\n return (group.models ?? []) as unknown as readonly ModelChoiceLike[]\n}\n\n/** Provider label: the SDK face names it differently across kernel lines. */\nexport function providerLabelOf(group: DirectoryGroupLike): string {\n const named = group as unknown as { name?: string; label?: string; title?: string }\n return named.name ?? named.label ?? named.title ?? group.id\n}\n\n/** The chevron marking a row as drilling into a list; rotates while open. */\nfunction chevron(open: boolean): ReactNode {\n return createElement(\n 'span',\n { className: 'bre-row-chevron' + (open ? ' is-open' : ''), 'aria-hidden': true },\n '\u203A',\n )\n}\n\n/** The check marking the current entry of an open list. */\nfunction checkIcon(): ReactNode {\n return createElement(\n 'svg',\n {\n className: 'bre-option-check',\n width: 14,\n height: 14,\n viewBox: '0 0 16 16',\n fill: 'none',\n 'aria-hidden': true,\n },\n createElement('path', {\n d: 'M3.5 8.5l3 3 6-7',\n stroke: 'currentColor',\n strokeWidth: 1.6,\n strokeLinecap: 'round',\n strokeLinejoin: 'round',\n }),\n )\n}\n\n/**\n * Capability badge caches. The knowledge lookup is a linear scan with boundary\n * regexes; the model list re-renders on every panel toggle, so the answer is\n * memoized per model id.\n */\nconst VISION_CACHE = new Map<string, boolean>()\n\n/**\n * Whether the model accepts image input. The declaration the directory carries\n * wins; a model that declares nothing falls back to the plugin's own knowledge\n * base and then to its name heuristic \u2014 the same tiers the suggestion engine\n * uses, so the badge can never be more confident than the suggestion it would\n * write.\n */\nfunction supportsVision(model: ModelChoiceLike): boolean {\n if (Array.isArray(model.input)) return model.input.includes('image')\n const cached = VISION_CACHE.get(model.id)\n if (cached !== undefined) return cached\n const known = matchKnowledgeBase(model.id, model.name)?.input\n const verdict = known !== undefined\n ? known.includes('image')\n : (inferModalitiesFromName(model.id) ?? []).includes('image')\n VISION_CACHE.set(model.id, verdict)\n return verdict\n}\n\n/** Whether the model reasons at all (any declared effort level). */\nfunction supportsThinking(model: ModelChoiceLike): boolean {\n const efforts = model.reasoning?.efforts\n return Array.isArray(efforts) && efforts.length > 0\n}\n\n/** The image-input badge: an eye, shown only when the model accepts images. */\nfunction visionIcon(label: string): ReactNode {\n return createElement(\n 'svg',\n {\n className: 'bre-tag-icon',\n width: 13,\n height: 13,\n viewBox: '0 0 16 16',\n fill: 'none',\n role: 'img',\n 'aria-label': label,\n },\n createElement('path', {\n d: 'M1.8 8s2.4-4.2 6.2-4.2S14.2 8 14.2 8s-2.4 4.2-6.2 4.2S1.8 8 1.8 8z',\n stroke: 'currentColor',\n strokeWidth: 1.3,\n strokeLinejoin: 'round',\n }),\n createElement('circle', { cx: 8, cy: 8, r: 1.8, fill: 'currentColor' }),\n )\n}\n\n/** The thinking badge: a sparkle, shown only when the model reasons. */\nfunction thinkingIcon(label: string): ReactNode {\n return createElement(\n 'svg',\n {\n className: 'bre-tag-icon',\n width: 13,\n height: 13,\n viewBox: '0 0 16 16',\n fill: 'none',\n role: 'img',\n 'aria-label': label,\n },\n createElement('path', {\n d: 'M8 1.9l1.35 3.75L13.1 7l-3.75 1.35L8 12.1 6.65 8.35 2.9 7l3.75-1.35z',\n fill: 'currentColor',\n }),\n )\n}\n\n/** The capability badges of one model, or null when it declares neither. */\nfunction modelTags(model: ModelChoiceLike, t: Translator): ReactNode {\n const badges: ReactNode[] = []\n if (supportsVision(model)) {\n badges.push(createElement(\n 'span',\n { key: 'vision', className: 'bre-tag', title: t('tagVision') },\n visionIcon(t('tagVision')),\n ))\n }\n if (supportsThinking(model)) {\n badges.push(createElement(\n 'span',\n { key: 'thinking', className: 'bre-tag', title: t('tagThinking') },\n thinkingIcon(t('tagThinking')),\n ))\n }\n if (badges.length === 0) return null\n return createElement('span', { className: 'bre-option-tags' }, ...badges)\n}\n\n/** The official directory selection input this body speaks. */\ninterface SelectInput {\n provider: string\n model: string\n reasoningEffort?: string\n}\n\n/**\n * Render the model row and the reasoning-level row. Both values come from the\n * store; the commit travels through {@link ModelDirectoryLike.select}\n * (optimistic, rolled back on refusal).\n */\nexport function ComposerSlider(props: ComposerSliderProps): ReactNode {\n const { directory, t } = props\n const layoutChange = props.onLayoutChange\n const state = useSyncExternalStore(\n (notify: () => void) => directory.store.subscribe(notify),\n () => directory.store.getSnapshot(),\n )\n const levels = sliderLevels(state)\n const [effort, setEffort] = useState('')\n const [panel, setPanel] = useState<Panel>(null)\n const [openProvider, setOpenProvider] = useState<string | null>(null)\n const [committing, setCommitting] = useState(false)\n const [localError, setLocalError] = useState<string | null>(null)\n const bodyRef = useRef<HTMLDivElement>(null)\n const committedRef = useRef('')\n const committingRef = useRef(false)\n const available = state.current !== null && levels.length >= 2\n const busy = committing || state.status === 'selecting'\n const error = localError ?? state.error\n const select = directory.select as unknown as (\n input: SelectInput,\n ) => Promise<Parameters<typeof selectRefusalMessage>[0]>\n\n // The store is the single source of truth: every settled snapshot pulls the\n // selection back onto the level the session actually reports. Skipped while a\n // commit is in flight so the optimistic pick is not yanked back mid-round-trip.\n useEffect(() => {\n if (committingRef.current) return\n const index = effectiveEffortIndex(levels, state)\n const next = levels[index]?.id ?? ''\n committedRef.current = next\n setEffort(next)\n setLocalError(null)\n }, [available, levels, state])\n\n useEffect(() => {\n directory.load().catch(() => undefined)\n }, [directory])\n\n const current = state.current\n const group = state.groups.find(candidate => candidate.id === current?.provider)\n const model = group?.models.find(candidate => candidate.id === current?.model)\n const modelName = model?.name ?? (current === null ? t('triggerFallback') : current.model)\n const modelLabel = current === null\n ? modelName\n : providerLabelOf(group ?? ({ id: current.provider } as DirectoryGroupLike)) + ' \u00B7 ' + modelName\n const effortLabel = levels.find(level => level.id === effort)?.name ?? t('effortDefault')\n\n // The provider order set on the Models settings page, where the rows are\n // draggable: the switcher reports the same sequence so the two views never\n // disagree about where a provider lives. Subscribed rather than read once,\n // because a drag on the settings page has to reach an already-open composer.\n const preferredOrder = useSyncExternalStore(subscribeProviderOrder, providerOrder)\n\n // The providers switched off on the Models settings page. Hiding one here is\n // the whole point of the switch, and the trigger decoration still keeps the\n // current provider visible so the selected model remains unambiguous.\n const disabled = useSyncExternalStore(subscribeDisabledProviders, disabledProviders)\n\n // The model order set inside each provider's editor on the settings page: the\n // right column reports the same sequence, per provider. Subscribed for the\n // same reason as the provider order \u2014 a drag has to reach an open composer.\n const modelOrderByProvider = useSyncExternalStore(subscribeModelOrders, modelOrders)\n\n // Providers that actually hold models; the one whose models the right column\n // shows follows the current model until the user picks another column entry.\n const providerCatalog = state.groups\n .filter(candidate => modelsOf(candidate).length > 0 && !disabled.includes(candidate.id))\n .map(candidate => candidate.id)\n const providerIds = mergeProviderOrder(baseProviderOrder(providerCatalog), preferredOrder)\n const shownProvider = openProvider !== null && providerIds.includes(openProvider)\n ? openProvider\n : (current !== null && providerIds.includes(current.provider)\n ? current.provider\n : providerIds[0] ?? null)\n const shownModels = shownProvider === null\n ? []\n : orderModelEntries(\n modelsOf(state.groups.find(candidate => candidate.id === shownProvider) as DirectoryGroupLike),\n modelOrderByProvider[shownProvider] ?? [],\n )\n const activeKey = current === null ? '' : current.provider + '/' + current.model\n\n /**\n * Toggle a list. A panel changes the body's height, and the official card only\n * re-measures on a window `resize`, so BOTH steps must land in one frame:\n * commit the DOM synchronously, then let the injector re-place the card before\n * the browser paints. Deferring the re-measure (the previous `setTimeout`)\n * painted the panel once at the stale position \u2014 the flash that read as a\n * different page.\n */\n const togglePanel = useCallback((next: Panel): void => {\n if (panel === next) return\n flushSync(() => { setPanel(next) })\n layoutChange?.()\n }, [layoutChange, panel])\n\n /** Collapse whichever list is open, re-placing the card in the same frame. */\n const collapsePanel = useCallback((): void => {\n if (panel === null) return\n flushSync(() => { setPanel(null) })\n layoutChange?.()\n }, [layoutChange, panel])\n\n /**\n * Hand focus to the menu before a control this body owns is disabled or\n * unmounted. A focused disabled node drops focus onto <body>, and the shell\n * reads that as \"focus left the seat\" and closes the menu instantly \u2014 the\n * flash-out every commit shipped with (issue #13).\n */\n const focusMenu = useCallback((): void => {\n const menu = bodyRef.current?.closest<HTMLElement>('[role=\"menu\"]')\n if (menu === null || menu === undefined) return\n menu.tabIndex = -1\n menu.focus({ preventScroll: true })\n }, [])\n\n /** Pick a model. Collapses the list; deliberately leaves the popover open. */\n const chooseModel = useCallback(async (provider: string, id: string): Promise<void> => {\n if (committingRef.current) return\n const now = directory.store.getSnapshot().current\n collapsePanel()\n if (now !== null && now.provider === provider && now.model === id) return\n committingRef.current = true\n focusMenu()\n setCommitting(true)\n setLocalError(null)\n try {\n // No reasoningEffort on purpose: a model switch resets the level, and the\n // effort-memory wiretap then restores the one remembered for that model.\n const outcome = await select({ provider, model: id })\n const refusal = selectRefusalMessage(outcome)\n if (refusal !== undefined) throw new Error(refusal)\n } catch (cause) {\n setLocalError(cause instanceof Error ? cause.message : String(cause))\n } finally {\n committingRef.current = false\n setCommitting(false)\n }\n }, [collapsePanel, directory, focusMenu, select])\n\n /** Pick a reasoning level. Collapses the list; leaves the popover open. */\n const chooseEffort = useCallback(async (next: string): Promise<void> => {\n if (committingRef.current) return\n collapsePanel()\n // The row's displayed level can be a PREDICTION rather than a commitment:\n // `effectiveEffortIndex` falls back to the adapter default (or the middle of\n // the ladder) when the session carries no effort, so the FIRST use of a\n // model shows \"Medium\" while the seat trigger still reads Default. Guarding\n // the repick on that displayed level swallowed the very click that would\n // commit it, leaving the level unpickable until another one was chosen.\n // Only the SESSION'S OWN effort is a settled level.\n if (next === state.current?.reasoningEffort && state.status !== 'error') return\n committingRef.current = true\n const previous = committedRef.current\n focusMenu()\n setCommitting(true)\n setLocalError(null)\n // Optimistic pick keeps the row responsive.\n setEffort(next)\n\n try {\n const now = state.current\n if (now === null) throw new Error(t('sliderNoCurrent'))\n const outcome = await select({\n provider: now.provider,\n model: now.model,\n reasoningEffort: next,\n })\n // Kernels through 0.1.6-alpha.1 reject by throwing; 0.1.6-alpha.2\n // resolves the refusal result instead. Normalizing it into a throw keeps\n // the optimistic rollback and the in-menu error line identical.\n const refusal = selectRefusalMessage(outcome)\n if (refusal !== undefined) throw new Error(refusal)\n\n const snapshot = directory.store.getSnapshot()\n const accepted = levels.find(level => level.id === snapshot.current?.reasoningEffort)?.id\n const settled = accepted ?? next\n committedRef.current = settled\n setEffort(settled)\n } catch (cause) {\n committedRef.current = previous\n setEffort(previous)\n setLocalError(cause instanceof Error ? cause.message : String(cause))\n } finally {\n committingRef.current = false\n setCommitting(false)\n }\n }, [collapsePanel, directory, focusMenu, levels, select, state.current, state.status, t])\n\n /** The two rows; each opens its own list underneath. */\n const rows: ReactNode[] = [\n createElement(\n 'button',\n {\n key: 'row:model',\n type: 'button',\n role: 'menuitem',\n className: 'bre-row-control' + (panel === 'model' ? ' is-open' : ''),\n disabled: busy,\n 'aria-expanded': panel === 'model',\n onClick: () => { togglePanel(panel === 'model' ? null : 'model') },\n },\n createElement('span', { className: 'bre-row-label' }, t('modelRowLabel')),\n createElement('span', { className: 'bre-row-value' }, modelLabel),\n chevron(panel === 'model'),\n ),\n // Provider column + the models of the selected provider, side by side. The\n // panel's height is FIXED (see .bre-panel-models): switching provider must\n // not resize the card, or every provider click would need another re-place.\n panel === 'model'\n ? createElement(\n 'div',\n { key: 'panel:model', className: 'bre-panel bre-panel-models' },\n createElement(\n 'div',\n { className: 'bre-providers', role: 'group', 'aria-label': t('modelRowLabel') },\n ...providerIds.map(provider => createElement(\n 'button',\n {\n key: provider,\n type: 'button',\n className: 'bre-provider'\n + (provider === shownProvider ? ' is-open' : '')\n + (provider === current?.provider ? ' is-current' : ''),\n disabled: busy,\n 'aria-expanded': provider === shownProvider,\n onClick: () => { setOpenProvider(provider) },\n },\n createElement(\n 'span',\n { className: 'bre-option-name' },\n providerLabelOf(state.groups.find(candidate => candidate.id === provider) as DirectoryGroupLike),\n ),\n chevron(provider === shownProvider),\n )),\n ...(providerIds.length === 0\n ? [createElement('div', { key: 'no-providers', className: 'bre-empty' }, t('sliderNoCurrent'))]\n : []),\n ),\n createElement(\n 'div',\n { className: 'bre-provider-models', role: 'group', 'aria-label': t('modelRowLabel') },\n ...(shownModels.length === 0\n ? [createElement('div', { key: 'no-models', className: 'bre-empty' }, t('sliderNoCurrent'))]\n : shownModels.map(choice => {\n const key = shownProvider + '/' + choice.id\n const active = key === activeKey\n return createElement(\n 'button',\n {\n key,\n type: 'button',\n className: 'bre-option' + (active ? ' is-active' : ''),\n disabled: busy,\n 'aria-pressed': active,\n onClick: () => { void chooseModel(shownProvider as string, choice.id) },\n },\n createElement('span', { className: 'bre-option-name' }, choice.name ?? choice.id),\n // No tick on a model row: it would sit right beside the\n // capability badges and read as one of them. The accent colour\n // (plus the row tint in CSS) is the whole selection signal.\n modelTags(choice, t),\n )\n })),\n ),\n )\n : null,\n createElement('div', { key: 'divider', className: 'bre-divider', 'aria-hidden': true }),\n createElement(\n 'button',\n {\n key: 'row:effort',\n type: 'button',\n role: 'menuitem',\n className: 'bre-row-control' + (panel === 'effort' ? ' is-open' : ''),\n disabled: busy || !available,\n 'aria-expanded': available ? panel === 'effort' : undefined,\n onClick: () => { togglePanel(panel === 'effort' ? null : 'effort') },\n },\n createElement('span', { className: 'bre-row-label' }, t('effortRowLabel')),\n createElement(\n 'span',\n { className: 'bre-row-value' + (available ? '' : ' is-muted') },\n available ? effortLabel : t('sliderNoLevels'),\n ),\n available ? chevron(panel === 'effort') : null,\n ),\n panel === 'effort' && available\n ? createElement(\n 'div',\n {\n key: 'panel:effort',\n className: 'bre-panel',\n role: 'group',\n 'aria-label': t('effortRowLabel'),\n },\n ...levels.map(level => createElement(\n 'button',\n {\n key: level.id,\n type: 'button',\n className: 'bre-option' + (level.id === effort ? ' is-active' : ''),\n disabled: busy,\n 'aria-pressed': level.id === effort,\n onClick: () => { void chooseEffort(level.id) },\n },\n createElement('span', { className: 'bre-option-name' }, level.name),\n level.id === effort ? checkIcon() : null,\n )),\n )\n : null,\n ]\n\n return createElement(\n 'div',\n { className: 'bre-slider-body', ref: bodyRef },\n createElement('div', { className: 'bre-card' }, ...rows),\n error === null\n ? null\n : createElement('div', { className: 'bre-model-error', role: 'status' }, error),\n )\n}\n", "/**\n * The reasoning-effort + input-modality knowledge base + wire-protocol\n * inference engine.\n *\n * Every model a third-party provider route serves must declare its\n * 'reasoningEfforts' (which DSH thinking levels it accepts, and the exact\n * string to send on the wire for each) before the composer's model picker\n * offers any reasoning control for it. This module answers \"what should this\n * model's declaration be?\" from three sources, in order:\n *\n * 1. An endpoint listing signal (when the route was probed): an explicit\n * \"does not reason\", a modality disclosure, or a context length.\n * 2. A curated knowledge base keyed by model id/name patterns, carrying the\n * exact level set, wire spellings, request modalities, and reference\n * capacities known to work against that family of endpoints.\n * 3. Wire-protocol inference from the route's 'api' / 'baseURL', plus a\n * conservative name heuristic for modalities, for families nothing else\n * names.\n *\n * All outputs stay on the adapter's own vocabulary (THINKING_LEVELS,\n * INPUT_MODALITIES, the reasoningEfforts/input dict shapes) so a suggested\n * declaration can be written to llm-pi-ai settings with no translation step.\n * A suggestion is a *preset*: the user can accept it, tune it, or ignore it.\n * The plugin never silently overwrites a declaration the user already made.\n *\n * @module dsh-model-think-level/knowledge\n */\n\n/** The canonical pi-ai escalation order accepted by llm-pi-ai. */\nexport const THINKING_LEVELS = ['off', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'] as const\n\nexport type ThinkingLevel = (typeof THINKING_LEVELS)[number]\n\n/**\n * The request-modality vocabulary llm-pi-ai accepts on a model entry -- a\n * local mirror of pi-ai's MODALITY_GATE (text/image today). The core schema\n * refuses anything else at write time, and its drift gate forces the\n * vocabulary to grow exactly when pi-ai's Model['input'] does; the vocabulary\n * spec pins this mirror against upstream so the UI follows.\n */\nexport const INPUT_MODALITIES = ['text', 'image'] as const\n\nexport type InputModality = (typeof INPUT_MODALITIES)[number]\n\n/** One level's wire spelling; null means \"send nothing for this level\". */\nexport type WireSpelling = string | null\n\n/** A full reasoning-effort declaration value: level -> wire spelling. */\nexport type ReasoningEfforts = Partial<Record<ThinkingLevel, WireSpelling>>\n\n/** A full input-modality declaration value: the modalities a model accepts. */\nexport type InputModalities = readonly InputModality[]\n\n/** Reasoning-budget request-field spellings pi-ai accepts (older kernels reject them). */\nexport const THINKING_TOKEN_BUDGET_FIELDS = ['thinking_token_budget', 'thinking_budget', 'thinking_budget_tokens'] as const\nexport type ThinkingTokenBudgetField = (typeof THINKING_TOKEN_BUDGET_FIELDS)[number]\n\n/** The compat fields this plugin may suggest (a subset of the profile schema). */\nexport interface CompatSuggestion {\n /** Wire format the endpoint speaks; omitted when unknown. */\n thinkingFormat?: string\n /** Whether the endpoint accepts a reasoning-effort-style parameter. */\n supportsReasoningEffort?: boolean\n /**\n * Whether pi-ai may rewrite the system prompt to the developer role for\n * reasoning models. Self-hosted relays get an explicit false (their\n * upstreams reject role:developer); official endpoints keep pi-ai's own\n * detection default by omitting the field.\n */\n supportsDeveloperRole?: boolean\n /**\n * Anthropic-messages protocol only: force adaptive thinking so the\n * declared efforts dispatch as `output_config.effort` instead of falling\n * into the older budget-based thinking path. pi-ai offers this field on\n * exactly the anthropic-messages compat gate, and the oracle models that\n * take an effort ladder all have adaptive thinking.\n */\n forceAdaptiveThinking?: boolean\n thinkingTokenBudgetField?: ThinkingTokenBudgetField\n supportsThinkingTokenBudget?: boolean\n vllmPriority?: number\n supportsMaxOutputTokens?: boolean\n}\n\nexport const NEW_COMPAT_KEYS = ['thinkingTokenBudgetField', 'vllmPriority', 'supportsMaxOutputTokens'] as const\n\n/** One knowledge-base entry: which model families it matches and what to declare. */\nexport interface KnowledgeEntry {\n /** Stable id (also used to de-duplicate user overrides). */\n id: string\n /** Model id/name patterns (lowercase substring match). */\n patterns: readonly string[]\n /**\n * Level set + wire spellings to declare. `false` marks a family the\n * official catalog serves WITHOUT any reasoning control (GPT-4o / GPT-4\n * generations): suggesting a ladder there would have the composer send\n * reasoning_effort and get a 400 from the real endpoint.\n */\n efforts: ReasoningEfforts | false\n /** Optional compat block to declare alongside the efforts. */\n compat?: CompatSuggestion\n /**\n * The model family takes an effort ladder on the anthropic-messages\n * protocol too: pi-ai only routes declared efforts to `output_config.effort`\n * when `forceAdaptiveThinking` is set, so this pin adds that compat block\n * on anthropic-messages routes (and nothing on any other protocol).\n */\n anthropicAdaptive?: boolean\n /**\n * Request modalities to declare, in core vocabulary. Absent = no modality\n * suggestion for this family. Wider support a gateway may serve (pdf,\n * audio, video) is recorded in 'note' until the core vocabulary grows.\n */\n input?: InputModalities\n /**\n * Reference context window in tokens (display-only advice; the official\n * capacity inputs are never auto-filled).\n */\n contextWindow?: number\n /** Reference max output tokens in tokens (display-only advice). */\n maxTokens?: number\n /**\n * The vendor's own default level for the family, when its docs name one\n * (the same research pass that recorded the ladders). Consumed by the\n * client's effort memory as the model-switch fallback: a switch to a model\n * with no remembered level re-applies the vendor default instead of leaving\n * the session on the host's \"Default\" (no-effort) state. Never guessed --\n * entries whose docs publish no default simply carry none.\n */\n defaultEffort?: ThinkingLevel\n /** Human note shown next to the suggestion (localized by the caller). */\n note?: string\n}\n\n/** What a provider route names about itself that inference can use. */\nexport interface RouteFacts {\n /** Wire protocol as configured ('api'), if any. */\n api?: string\n /** Endpoint URL, if any. */\n baseURL?: string\n /** Display name, if any. */\n displayName?: string\n}\n\n/** The endpoint facts a suggestion fuses, as the probe route reports them. */\nexport interface EndpointFacts {\n /** Tri-state reasoning signal: true / false / 'unknown' (listing silent). */\n reasoning: boolean | 'unknown'\n /** The listing field that produced the reasoning signal, null when none did. */\n source: string | null\n /**\n * The listing's input-modality disclosure, normalized to lowercase strings,\n * when the listing named such a field at all. Present-but-empty means the\n * field existed with no usable members; absent means the listing never\n * spoke about modalities (or the probe never ran) -- the tri-state\n * discipline: silence is not refusal.\n */\n input?: readonly string[]\n /** Context length the listing disclosed, when it carries one. */\n contextLength?: number\n}\n\n/** How a suggestion's input-modality part was derived. */\nexport type InputSource = 'knowledge' | 'endpoint' | 'heuristic'\n\n/** A resolved suggestion for one model on one route. */\nexport interface EffortSuggestion {\n /**\n * The reasoning-effort declaration to write, if one is derivable. False\n * arrives when the endpoint explicitly reports the model does not reason.\n */\n efforts?: ReasoningEfforts | false\n /**\n * The vendor's own default level for the model, when the knowledge base\n * names one and the suggested ladder carries it. The effort memory uses it\n * as the model-switch fallback (see {@link KnowledgeEntry.defaultEffort}).\n */\n defaultEffort?: ThinkingLevel\n /** The compat block to write alongside, if any. */\n compat?: CompatSuggestion\n /** Request modalities to declare, when derivable. */\n input?: InputModalities\n /** Where the modality part came from -- its confidence rides this. */\n inputSource?: InputSource\n /** Reference context window (display-only; never auto-filled anywhere). */\n contextWindow?: number\n /** Reference max output tokens (display-only). */\n maxTokens?: number\n /** Whether a knowledge-base entry matched (vs protocol inference). */\n matched: boolean\n /** The knowledge entry id when one matched. */\n entryId?: string\n /** Human-readable source of the suggestion. */\n source: string\n /**\n * Overall confidence for the UI: high = knowledge base (wire values) and/or\n * an explicit endpoint signal; medium = endpoint confirmed reasoning but the\n * level ladder is inferred; low = protocol inference alone.\n */\n confidence: 'high' | 'medium' | 'low'\n /** The endpoint signal behind this suggestion, when one was supplied. */\n endpoint?: { reasoning: boolean | 'unknown'; source: string | null }\n}\n\n/**\n * The curated knowledge base. Patterns are lowercase substring matches against\n * the model id (and display name). First match wins, longest pattern wins.\n *\n * Sources of truth (per family; DeepSeek / OpenAI / Anthropic re-checked\n * 2026-09 against their current official docs, the rest 2026-08): official API docs --\n * api-docs.deepseek.com (news/pricing/thinking/vision/API reference),\n * developers.openai.com model pages + guides (platform.openai.com blocked;\n * Azure learn mirror cross-checked), platform.claude.com (effort page,\n * models overview, OpenAI-SDK compatibility page), ai.google.dev (thinking +\n * OpenAI-compat pages), docs.x.ai (model pages + reasoning capability),\n * docs.bigmodel.cn (thinking + model pages), platform.kimi.com (models /\n * use-thinking-models / use-reasoning-effort), platform.stepfun.com (model\n * pages + reasoning), docs.byteplus.com / volcengine ModelArk (deep\n * reasoning), cloud.baidu.com (Qianfan pricing/thinking model list),\n * plus first-party GitHub cards (openai/gpt-oss, mistralai/mistral-common,\n * Tencent-Hunyuan/Hy3, MiniMax-AI/MiniMax-M3) -- cross-checked against the\n * public OpenRouter catalog (input_modalities, supported_parameters) where\n * noted. Capacities are REFERENCE values: aggregators disagree, so the note\n * says so where the spread is wide, and families whose numbers stay contested\n * simply carry none.\n */\nexport const KNOWLEDGE_BASE: readonly KnowledgeEntry[] = [\n {\n id: 'deepseek-v4-vision',\n // Must precede deepseek-v4 semantically -- guaranteed anyway by the\n // longest-boundary-hit rule, since these patterns are strictly longer.\n patterns: ['deepseek-v4-flash-vision', 'deepseek-v4-vision'],\n efforts: { off: 'none', low: 'low', high: 'high', max: 'max' },\n defaultEffort: 'high',\n compat: { thinkingFormat: 'deepseek', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 1_048_576,\n maxTokens: 384_000,\n note: 'DeepSeek \u89C6\u89C9\u5B9E\u9A8C\u7248 id\uFF08deepseek-v4-flash-vision-exp\uFF09\u3002\u5B98\u65B9\u76EE\u5F55\u5DF2\u6807\u6CE8\u8BE5\u6A21\u578B\u9000\u5F79\uFF0C\u540D\u5B57\u4ECD\u88AB\u63A5\u53D7\u3001\u7531\u73B0\u884C deepseek-flash \u63D0\u4F9B\u670D\u52A1\u2014\u2014\u56FE\u7247\u8F93\u5165\u73B0\u5DF2\u7531 deepseek-flash \u539F\u751F\u63D0\u4F9B\u3002',\n },\n {\n id: 'deepseek-v4-1-flash',\n // V4.1-Flash is DeepSeek's CURRENT flagship and it takes image input, which\n // the shared `deepseek-v4` stem cannot express: that entry carries the text\n // floor its pro/legacy siblings share, and one modal answer cannot be both.\n // This entry keys every V4.1 spelling, and it must stay ahead of the base\n // stem -- the longest-hit rule guarantees that, these patterns are strictly\n // longer -- so an id, or a display name, that says \"V4.1 Flash\" declares\n // images even when the relay invented the id itself.\n // 'deepseek-flash' is the CURRENT official id (2026-09-10) and belongs\n // here, not to the stem: the pricing page serves it as V4.1-Flash, and a\n // third-party reseller exposing DeepSeek's own model under that name means\n // the same model.\n patterns: ['deepseek-v4.1-flash', 'deepseek-flash'],\n efforts: { off: 'none', low: 'low', high: 'high', max: 'max' },\n defaultEffort: 'high',\n compat: { thinkingFormat: 'deepseek', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 1_048_576,\n maxTokens: 384_000,\n note: 'DeepSeek-V4.1-Flash\uFF082026-09-10 \u53D1\u5E03\u7684\u73B0\u884C\u5B98\u65B9\u6A21\u578B\uFF0C\u5B98\u65B9 id \u4E3A deepseek-flash\uFF1B25 \u4E07\u5E76\u53D1\u3001**\u539F\u751F\u56FE\u7247\u8F93\u5165**\uFF09\u3002V4.1 \u7684\u5404\u5199\u6CD5\uFF08deepseek-flash\u3001deepseek-v4.1-flash\u3001deepseek-v4-1-flash\uFF0C\u4EE5\u53CA\u5E26\u65E5\u671F\u540E\u7F00\u7684\u7B2C\u4E09\u65B9\u53D8\u4F53\uFF09\u90FD\u5F52\u6B64\u6761\u2014\u2014\u7EAF\u6587\u672C\u7684 deepseek-v4 \u4E3B\u5E72\u4E0D\u518D\u541E\u6389\u5B83\u4EEC\u3002\u5B98\u65B9\u679A\u4E3E\u540C V4 \u7CFB\uFF1AOff\uFF08thinking:\"disabled\"\uFF09/ Low / High / Max\uFF0C\u9ED8\u8BA4 High\uFF1B\u5BB9\u91CF 1,048,576 \u4E0A\u4E0B\u6587 / \u6700\u5927\u8F93\u51FA 384K\u3002',\n },\n {\n id: 'deepseek-v4',\n // One pattern covers the family's TEXT-floor members: every serving\n // suffixes the base id (deepseek-v4-flash, deepseek-v4-pro,\n // deepseek-v4-flash-free ...), so the shared stem keys them all --\n // including free/aggregator spellings the official catalog never lists.\n // Closing thinking is the 'thinking: disabled' object, so the off spelling\n // is a non-null placeholder that arms the deepseek format's disabled\n // branch. The placeholder must stay 'none': pi-ai's deepseek completions\n // branch only checks non-null (any string arms thinking:disabled), while\n // the same map rides the Responses API as reasoning.effort, whose official\n // DeepSeek values are none/low/high/max -- 'off' would be a 400 there.\n // The stem deliberately stops at 'deepseek-v4': the image-capable V4.1\n // generation keys its own strictly longer entry above, while this entry\n // keeps the retired deepseek-v4-flash spelling and the text-only\n // deepseek-v4-pro, whose declarations agree with its text floor.\n patterns: ['deepseek-v4'],\n efforts: { off: 'none', low: 'low', high: 'high', max: 'max' },\n defaultEffort: 'high',\n compat: { thinkingFormat: 'deepseek', supportsReasoningEffort: true },\n input: ['text'],\n contextWindow: 1_048_576,\n maxTokens: 384_000,\n note: 'DeepSeek \u5B98\u65B9\u679A\u4E3E Low / High / Max\uFF08\u9ED8\u8BA4 High\uFF1Bminimal\u3001medium\u3001xhigh \u517C\u5BB9\u6620\u5C04\uFF0Cultra\u2192max\uFF09\uFF0COff \u5373 thinking:\"disabled\"\uFF08Responses API \u4E0B off \u4EE5 reasoning.effort:\"none\" \u8868\u793A\uFF09\u3002\u672C\u6761\u76EE\u662F V4 \u5BB6\u65CF\u7684**\u7EAF\u6587\u672C**\u6210\u5458\uFF1Adeepseek-v4-pro\uFF08= DeepSeek-V4-Pro-0813\uFF0C\u5B98\u65B9\u4E0D\u652F\u6301\u56FE\u7247\uFF09\u4E0E\u5DF2\u9000\u5F79\u6A21\u578B\u7684\u517C\u5BB9\u522B\u540D deepseek-v4-flash\uFF08deepseek-v4-flash-vision-exp \u53E6\u6709\u89C6\u89C9\u6761\u76EE\uFF09\u3002**\u6536\u56FE\u7684 V4.1-Flash\uFF08\u5B98\u65B9 id deepseek-flash\uFF09\u53E6\u6709\u4E13\u95E8\u6761\u76EE**\uFF0C\u5176 input \u542B image\u3002\u5BB9\u91CF\uFF1A1,048,576 \u4E0A\u4E0B\u6587 / \u6700\u5927\u8F93\u51FA 384K\uFF08393,216\uFF1B\u9ED8\u8BA4\u975E\u601D\u8003 8K\u3001\u601D\u8003 64K\u3001effort=max \u65F6 128K\uFF09\u3002',\n },\n {\n id: 'deepseek-v3',\n // deepseek-reasoner belongs to the R1 entry: it is the reasoning model's\n // API name and must not match here. The old alias pair\n // (deepseek-chat / deepseek-reasoner) was retired upstream in 2026-07;\n // the id patterns stay for third-party gateways still serving V3.x.\n patterns: ['deepseek-v3', 'deepseek-chat'],\n efforts: { off: 'none', high: 'high', max: 'max' },\n compat: { thinkingFormat: 'deepseek', supportsReasoningEffort: true },\n input: ['text'],\n contextWindow: 163_840,\n maxTokens: 65_536,\n note: 'DeepSeek V3 \u6863\u4F4D\uFF1AOff / High / Max\u3002\u5B98\u65B9\u5DF2\u505C\u552E V3 \u4EE3\uFF08\u5B9A\u4EF7\u9875\u4EC5\u5269 V4 \u4E09\u578B\uFF09\uFF0C\u5BB9\u91CF\u53D6\u76EE\u5F55\u73B0\u5F79\u503C\u3002',\n },\n {\n id: 'deepseek-r1',\n patterns: ['deepseek-r1', 'deepseek-reasoner'],\n // R1's reasoning cannot be switched off at the API level; the harness\n // treats it as a thinking model whose only declared level is the default.\n efforts: { high: 'high' },\n defaultEffort: 'high',\n compat: { thinkingFormat: 'deepseek', supportsReasoningEffort: true },\n input: ['text'],\n contextWindow: 163_840,\n maxTokens: 32_768,\n note: 'DeepSeek-R1 \u4E3A\u63A8\u7406\u6A21\u578B\uFF0C\u4EC5\u63D0\u4F9B High\u3002\u5B98\u65B9\u5DF2\u4E0B\u67B6 R1 \u4EE3\uFF0C\u5BB9\u91CF\u53D6\u76EE\u5F55 r1-0528 \u503C\u3002',\n },\n {\n id: 'openai-gpt-5-2',\n patterns: ['gpt-5.2'],\n // gpt-5.2 (2025-12) took 'none' as the explicit no-thinking value\n // (replacing gpt-5's 'minimal') and added xhigh; its default is none.\n efforts: { off: 'none', low: 'low', medium: 'medium', high: 'high', xhigh: 'xhigh' },\n defaultEffort: 'off',\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 400_000,\n maxTokens: 128_000,\n note: 'GPT-5.2 \u6863\u4F4D\uFF1ANone / Low / Medium / High / XHigh\uFF08\u9ED8\u8BA4 None\uFF09\u3002\u5B98\u65B9\u53E6\u652F\u6301 PDF \u8F93\u5165\uFF08\u6838\u5FC3\u8BCD\u8868\u6682\u4E0D\u542B\uFF09\u3002gpt-5.2-pro \u5B98\u65B9\u9875\u672A\u5355\u5217\u6863\u4F4D\u884C\uFF0C\u6309\u672C\u6761\u76EE\u540C\u6863\u5904\u7406\uFF1Bgpt-5.2-codex \u89C1\u5355\u72EC\u6761\u76EE\u3002',\n },\n {\n id: 'openai-gpt-5-2-codex',\n // The codex variant's official model page names low/medium/high/xhigh\n // only -- NO none -- so the shared gpt-5.2 entry must not claim an off\n // for it (a 'none' request would 400). Longer pattern wins over 'gpt-5.2'.\n patterns: ['gpt-5.2-codex'],\n efforts: { low: 'low', medium: 'medium', high: 'high', xhigh: 'xhigh' },\n // OpenAI's API default for the codex line is medium (Inspect AI's\n // per-model default table, aligned with developers.openai.com).\n defaultEffort: 'medium',\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 400_000,\n maxTokens: 128_000,\n note: 'GPT-5.2-Codex \u6863\u4F4D\uFF1ALow / Medium / High / XHigh\uFF08\u65E0 None \u6863\uFF0C\u52FF\u52FE Off\uFF09\uFF0C\u5E26\u56FE\u8F93\u5165\u3001400K\u3002',\n },\n {\n id: 'openai-gpt-5-6',\n patterns: ['gpt-5.6'],\n // gpt-5.6 spans the full modern ladder including max; the sol/luna/terra\n // variants share it (official reasoning guide: values \"can include none\n // minimal low medium high xhigh, and max\").\n efforts: { off: 'none', low: 'low', medium: 'medium', high: 'high', xhigh: 'xhigh', max: 'max' },\n defaultEffort: 'medium',\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 1_050_000,\n maxTokens: 128_000,\n note: 'GPT-5.6\uFF08\u522B\u540D\u5373 sol\uFF09\u6863\u4F4D\uFF1ANone / Low / Medium(\u9ED8\u8BA4) / High / XHigh / Max\uFF1Bsol/luna/terra \u540C\u6863\uFF0C\u5E26\u56FE\u8F93\u5165\u3002',\n },\n {\n id: 'openai-gpt-5-5',\n patterns: ['gpt-5.5'],\n // gpt-5.5 defaults to medium (official reasoning guide) and tops out at\n // xhigh; the -pro variant drops none/low (medium/high/xhigh only).\n efforts: { off: 'none', low: 'low', medium: 'medium', high: 'high', xhigh: 'xhigh' },\n defaultEffort: 'medium',\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 1_050_000,\n maxTokens: 128_000,\n note: 'GPT-5.5 \u6863\u4F4D\uFF1ANone / Low / Medium(\u9ED8\u8BA4) / High / XHigh\uFF0C\u5E26\u56FE\u8F93\u5165\u3002pro \u53D8\u4F53\u4EC5 Medium / High(\u9ED8\u8BA4) / XHigh \u4E14\u4EC5 Responses API\u3002',\n },\n {\n id: 'openai-gpt-5-4',\n patterns: ['gpt-5.4'],\n // Official model page: none (default) / low / medium / high / xhigh;\n // pro drops none+low and is Responses-only; mini/nano sit on 400K.\n efforts: { off: 'none', low: 'low', medium: 'medium', high: 'high', xhigh: 'xhigh' },\n defaultEffort: 'off',\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 1_050_000,\n maxTokens: 128_000,\n note: 'GPT-5.4 \u6863\u4F4D\uFF1ANone(\u9ED8\u8BA4) / Low / Medium / High / XHigh\uFF0C\u5E26\u56FE\u8F93\u5165\u3002pro \u53D8\u4F53\u4EC5 Medium/High/XHigh\uFF1Bmini/nano \u4E3A 400K \u4E0A\u4E0B\u6587\u3002',\n },\n {\n id: 'openai-gpt-5-3',\n patterns: ['gpt-5.3'],\n // Official model page: low / medium / high / xhigh only -- no none.\n efforts: { low: 'low', medium: 'medium', high: 'high', xhigh: 'xhigh' },\n defaultEffort: 'medium',\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 400_000,\n maxTokens: 128_000,\n note: 'GPT-5.3-Codex \u6863\u4F4D\uFF1ALow / Medium / High / XHigh\uFF08\u65E0 None \u6863\uFF09\uFF0C\u5E26\u56FE\u8F93\u5165\u3001400K\uFF1Bgpt-5.3-chat \u4E3A\u975E\u63A8\u7406\u804A\u5929\u6A21\u578B\uFF08\u89C1 chat \u6761\u76EE\uFF09\u3002',\n },\n {\n id: 'openai-gpt-5-1',\n patterns: ['gpt-5.1'],\n // gpt-5.1 (2025-11): none replaces minimal; no xhigh outside codex-max.\n // The codex variants split into their own entry: their official pages\n // publish no effort line, and the codex line's 5.2/5.3 successors take\n // no none -- so the conservative declaration there drops both none and\n // xhigh (see openai-gpt-5-1-codex).\n efforts: { off: 'none', low: 'low', medium: 'medium', high: 'high' },\n defaultEffort: 'off',\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 400_000,\n maxTokens: 128_000,\n note: 'GPT-5.1 \u6863\u4F4D\uFF1ANone / Low / Medium / High\uFF08\u9ED8\u8BA4 None\uFF09\u3002',\n },\n {\n id: 'openai-gpt-5-1-codex',\n // Official model pages for 5.1-codex / -codex-max / -codex-mini publish no\n // reasoning-effort line (unlike gpt-5.1 itself); the codex successors\n // 5.2/5.3 are low/medium/high/xhigh without none, so none is NOT declared\n // here either. xhigh is likewise unproven for 5.1-codex (the 5.1 page's\n // own ladder stops at high) and is withheld.\n patterns: ['gpt-5.1-codex'],\n efforts: { low: 'low', medium: 'medium', high: 'high' },\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 400_000,\n maxTokens: 128_000,\n note: 'GPT-5.1-Codex \u7CFB\u5217\uFF1A\u5B98\u65B9\u6A21\u578B\u9875\u672A\u5355\u5217 effort \u503C\u57DF\uFF0C\u6309\u540C\u4EE3\u4FDD\u5B88\u6863 Low / Medium / High\uFF08\u65E0 None\uFF1Bxhigh \u672A\u8BC1\u5B9E\uFF09\u3002\u5982\u7AEF\u70B9\u652F\u6301 xhigh/none \u53EF\u624B\u8C03\u3002',\n },\n {\n id: 'openai-gpt-5',\n patterns: ['gpt-5'],\n // The 2025-08 first generation: minimal is the floor, there is no none,\n // and the default (medium) thinks -- so no off level is offered.\n efforts: { minimal: 'minimal', low: 'low', medium: 'medium', high: 'high' },\n defaultEffort: 'medium',\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 400_000,\n maxTokens: 128_000,\n note: 'GPT-5 \u521D\u4EE3\u6863\u4F4D\uFF1AMinimal / Low / Medium / High\uFF0C\u65E0\u5173\u95ED\u6863\u3002',\n },\n {\n id: 'openai-chat',\n // Non-reasoning chat lines across generations: they take no\n // reasoning_effort at all (official model pages list no \"Reasoning\").\n // Official ids are the *-chat-latest snapshots; the bare -chat spellings\n // stay for gateways serving them. gpt-5-chat-latest was removed upstream\n // on 2026-07-23 and gpt-chat-latest has no model page anymore -- the\n // patterns persist for compatibility only.\n patterns: [\n 'gpt-5.1-chat-latest', 'gpt-5.2-chat-latest', 'gpt-5.3-chat-latest', 'gpt-5-chat-latest',\n 'gpt-chat-latest', 'gpt-5-chat', 'gpt-5.1-chat', 'gpt-5.2-chat', 'gpt-5.3-chat',\n ],\n efforts: false,\n input: ['text', 'image'],\n contextWindow: 128_000,\n maxTokens: 16_384,\n note: '-chat \u7CFB\u5217\uFF08\u5B98\u65B9 id \u4E3A gpt-5.x-chat-latest\uFF09\u4E3A\u975E\u63A8\u7406\u804A\u5929\u6A21\u578B\uFF0C\u4E0D\u652F\u6301 effort \u53C2\u6570\uFF08\u52FF\u52FE\u601D\u8003\u6863\uFF09\uFF1B\u5B98\u65B9\u652F\u6301\u56FE\u7247\u8F93\u5165\uFF0C128K / 16,384 \u8F93\u51FA\u3002gpt-5-chat-latest \u5DF2\u4E8E 2026-07-23 \u4E0B\u67B6\u3001gpt-chat-latest \u5DF2\u65E0\u5B98\u65B9\u9875\u9762\uFF08\u4FDD\u7559\u6A21\u5F0F\u4F9B\u7F51\u5173\uFF09\u3002',\n },\n {\n id: 'openai-o',\n // The o-series scheduled-removal notice (2026-06-11): o1/o1-pro/o3-mini/\n // o4-mini are removed 2026-10-23 and o3/o3-pro on 2026-12-11, with\n // gpt-5.6-sol / gpt-5.6-terra replacing them; the tokens stay for\n // gateways and local deployments still serving them, and match only on\n // word boundaries (see matchKnowledgeBase), so 'o1' hits 'o1-mini' but\n // not 'ko1'.\n patterns: ['o1', 'o3', 'o4'],\n // o-series had no none: not sending the parameter meant medium (think\n // on), so no off level is offered.\n efforts: { low: 'low', medium: 'medium', high: 'high' },\n defaultEffort: 'medium',\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 200_000,\n maxTokens: 100_000,\n note: 'OpenAI o \u7CFB\u6863\u4F4D\uFF1ALow / Medium / High\u3002\u5B98\u65B9\u5DF2\u4E8E 2026-06 \u516C\u544A\u9000\u5F79\uFF08o1/o1-pro/o3-mini/o4-mini 2026-10-23 \u79FB\u9664\u3001o3/o3-pro 2026-12-11\uFF09\uFF0C\u7F51\u5173\u6B8B\u7559\u4ECD\u53EF\u547D\u4E2D\uFF1B\u591A\u6570 o \u7CFB\u7AEF\u70B9\u6536\u56FE\uFF08o3-mini \u4F8B\u5916\uFF09\u3002',\n },\n {\n id: 'openai-gpt-oss',\n // Official README: configurable reasoning effort low / medium / high\n // (CLI default: low); official model page: 131,072 context and exactly\n // the same 131,072 max output tokens, text input only.\n patterns: ['gpt-oss'],\n efforts: { low: 'low', medium: 'medium', high: 'high' },\n defaultEffort: 'low',\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text'],\n contextWindow: 131_072,\n maxTokens: 131_072,\n note: 'GPT-OSS \u5F00\u6E90\u6743\u91CD\uFF08Ollama/vLLM \u5E38\u89C1\uFF09\uFF1Areasoning effort Low / Medium / High\uFF0C\u7EAF\u6587\u672C\uFF0C131K \u4E0A\u4E0B\u6587 / 131K \u8F93\u51FA\u30022026-09 \u590D\u6838\uFF1A\u5B98\u65B9\u6A21\u578B\u9875\u73B0\u5728\u5199 Chat Completions = Not supported\u3001\u65E0\u56FE\u7247\u8F93\u5165\uFF08\u9ED8\u8BA4 Low \u6765\u81EA\u5B98\u65B9 CLI\uFF0C\u4E0D\u662F API \u9875\uFF09\uFF0C\u56E0\u6B64\u8FD9\u91CC\u4E0D\u518D\u58F0\u660E Off\u3002',\n },\n {\n id: 'openai-gpt-6-astra',\n // Longest-pattern-wins keeps this ahead of the 5.x entries; 'astra' is the\n // only stable token an aggregator is likely to expose.\n patterns: ['gpt-6-astra', 'gpt-6'],\n // Official reasoning guide: none returns HTTP 400 on this model, and the\n // model page lists low/medium/high/xhigh/max. Official docs publish no\n // default effort for it, so none is recorded rather than guessed.\n efforts: { low: 'low', medium: 'medium', high: 'high', xhigh: 'xhigh', max: 'max' },\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 1_050_000,\n maxTokens: 128_000,\n note: 'GPT-6 Astra \u6863\u4F4D\uFF1ALow / Medium / High / XHigh / Max\u2014\u2014**\u6CA1\u6709 None \u6863**\uFF08\u5B98\u65B9\u539F\u6587\uFF1A\u8BBE\u4E3A none \u8FD4\u56DE HTTP 400\uFF09\uFF0C\u56E0\u6B64\u672C\u6761\u76EE\u4E0D\u58F0\u660E Off\u3002\u5E26\u56FE\u8F93\u5165\uFF0C1,050,000 \u4E0A\u4E0B\u6587 / 128,000 \u8F93\u51FA\uFF1BAI \u5B98\u65B9\u9ED8\u8BA4\u6863\u4F4D\u672A\u516C\u5E03\uFF0C\u6545\u4E0D\u586B\u3002\u5DE5\u5177\u8C03\u7528\u9700\u8D70 Responses API\u3002',\n },\n {\n id: 'openai-gpt-5-6-cyber',\n // Before the family entry: cyber's model page publishes NO effort line and\n // limits Chat Completions, so the family ladder (which includes none/max)\n // must not be offered for it.\n patterns: ['gpt-5.6-cyber'],\n // Conservative: the codex-style successors publish no none, and cyber's\n // page publishes nothing at all -- declare the intersection the other 5.6\n // variants agree on, plus xhigh (every published 5.6 ladder carries it).\n efforts: { low: 'low', medium: 'medium', high: 'high', xhigh: 'xhigh' },\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 400_000,\n maxTokens: 128_000,\n note: 'GPT-5.6-Cyber\uFF1A\u5B98\u65B9\u6A21\u578B\u9875\u672A\u5355\u5217 effort \u503C\u57DF\uFF08\u4E0D\u7B49\u4E8E\u4E0D\u652F\u6301\uFF09\uFF0C\u6545\u6309\u540C\u4EE3\u4FDD\u5B88\u6863 Low / Medium / High / XHigh\uFF1B\u8BE5\u578B\u53F7\u4EC5 Responses API\uFF0C\u5E26\u56FE\u8F93\u5165\u3001400K \u4E0A\u4E0B\u6587\u3002\u5B98\u65B9\u652F\u6301\u7684\u5176\u4ED6\u6863\u4F4D\u53EF\u624B\u8C03\u540E\u5E94\u7528\u3002',\n },\n {\n id: 'openai-gpt-4o',\n // Boundary matching keeps 'gpt-4' from hitting 'gpt-4o', so the vision\n // generation needs its own entry. gpt-4.1 keeps its own entry below\n // (1M context, larger output cap).\n patterns: ['gpt-4o'],\n // GPT-4o is not a reasoning model (official catalog lists no reasoning,\n // the API refuses reasoning_effort): suggest `false` so the editor\n // marks the row \"does not reason\" instead of wiring a rejected ladder.\n efforts: false,\n input: ['text', 'image'],\n contextWindow: 128_000,\n maxTokens: 16_384,\n note: 'GPT-4o \u4EE3\u9645\uFF1A\u975E\u63A8\u7406\u6A21\u578B\uFF0C\u4E0D\u652F\u6301 effort \u53C2\u6570\uFF08\u52FF\u52FE\u601D\u8003\u6863\uFF09\uFF1B\u56FE\u7247\u8F93\u5165\u5168\u7CFB\u6807\u914D\uFF0C128K / 16,384 \u8F93\u51FA\u3002',\n },\n {\n id: 'openai-gpt-4-1',\n // Official model page: 1,047,576 context / 32,768 max output, text+image\n // on every variant (4.1 / mini / nano alike -- the old \"nano is\n // text-only\" claim was wrong). No reasoning step at all.\n patterns: ['gpt-4.1'],\n efforts: false,\n input: ['text', 'image'],\n contextWindow: 1_047_576,\n maxTokens: 32_768,\n note: 'GPT-4.1 \u5168\u7CFB\uFF08\u542B mini/nano\uFF09\uFF1A\u975E\u63A8\u7406\u6A21\u578B\uFF0C\u4E0D\u652F\u6301 effort \u53C2\u6570\uFF1B\u5B98\u65B9 1,047,576 \u4E0A\u4E0B\u6587 / 32,768 \u8F93\u51FA\uFF0C\u5168\u7CFB\u5E26\u56FE\u8F93\u5165\u3002',\n },\n {\n id: 'openai-gpt-4-turbo-preview',\n // The preview snapshot is text-only (later turbo snapshots take images;\n // the shared preview spelling must not claim them). Longest-boundary-hit\n // keeps it ahead of 'gpt-4-turbo'.\n patterns: ['gpt-4-turbo-preview'],\n efforts: false,\n input: ['text'],\n contextWindow: 128_000,\n maxTokens: 4_096,\n note: 'GPT-4 Turbo Preview\uFF1A\u975E\u63A8\u7406\u6A21\u578B\uFF0C\u7EAF\u6587\u672C\u8F93\u5165\uFF08\u6B63\u5F0F turbo \u5FEB\u7167\u652F\u6301\u56FE\u7247\uFF0C\u89C1\u4E0B\u6761\uFF09\uFF0C128K\u3002',\n },\n {\n id: 'openai-gpt-4-turbo',\n patterns: ['gpt-4-turbo'],\n efforts: false,\n input: ['text', 'image'],\n contextWindow: 128_000,\n maxTokens: 4_096,\n note: 'GPT-4 Turbo\uFF1A\u975E\u63A8\u7406\u6A21\u578B\uFF0C\u5B98\u65B9\u652F\u6301\u56FE\u7247\u8F93\u5165\uFF0C128K / 4,096 \u8F93\u51FA\u3002',\n },\n {\n id: 'openai-gpt',\n patterns: ['gpt-4', 'gpt-3.5'],\n // Same reasoning as gpt-4o above: the GPT-4/3.5 generations take no\n // reasoning_effort -- suggest `false` rather than a rejected ladder.\n // gpt-4 remains text-only (8K window); the vision-bearing 4o/4.1/turbo\n // lines have their own entries.\n efforts: false,\n input: ['text'],\n note: 'GPT-4/3.5 \u4EE3\u9645\uFF1A\u975E\u63A8\u7406\u6A21\u578B\uFF0C\u4E0D\u652F\u6301 effort \u53C2\u6570\uFF08\u52FF\u52FE\u601D\u8003\u6863\uFF09\u3002gpt-4-turbo/4.1 \u8D77\u652F\u6301\u56FE\u7247\uFF0C\u89C1\u5355\u72EC\u6761\u76EE\uFF1B\u65B0\u4EE3\u8BF7\u7528 GPT-5 \u7CFB\uFF08\u89C1 openai-chat/gpt-5.x \u6761\u76EE\uFF09\u3002',\n },\n {\n id: 'anthropic-claude-5',\n // Claude 5th generation (fable/mythos/opus-5/sonnet-5, 2026): adaptive\n // thinking with an effort ladder that adds xhigh and max; the fable and\n // mythos entries think always-on, so no off level. Mythos Preview is\n // split into its own entry: it takes max but NOT xhigh (official effort\n // matrix), so the shared ladder's xhigh would be a 400.\n patterns: ['claude-fable-5', 'claude-mythos-5', 'claude-opus-5', 'claude-sonnet-5'],\n // Ladder per the official effort matrix (platform.claude.com\n // docs/build-with-claude/effort). NOTE: Anthropic's own OpenAI-SDK\n // compatibility layer IGNORES reasoning_effort (thinking is driven by\n // its own param, on by default for Claude 5) -- the declaration bites\n // on third-party gateways that map it; on the native\n // anthropic-messages protocol, anthropicAdaptive pins the compat that\n // makes pi-ai dispatch these as output_config.effort.\n efforts: { low: 'low', medium: 'medium', high: 'high', xhigh: 'xhigh', max: 'max' },\n defaultEffort: 'high',\n // No thinkingFormat: pi-ai has no 'anthropic' member, and the\n // openai-completions compat gate that takes thinkingFormat is not the\n // protocol an Anthropic endpoint speaks.\n compat: { supportsReasoningEffort: true },\n anthropicAdaptive: true,\n input: ['text', 'image'],\n contextWindow: 1_000_000,\n maxTokens: 128_000,\n note: 'Claude 5 \u4EE3\uFF08Fable 5 / Mythos 5 / Opus 5 / Sonnet 5\uFF09\u6863\u4F4D\uFF1ALow / Medium / High(\u9ED8\u8BA4) / XHigh / Max\uFF1BFable 5.1 \u4E0E Mythos 5.1 \u89C1\u5355\u72EC\u6761\u76EE\uFF0CMythos Preview \u4EC5\u81F3 Max\u30021M \u4E0A\u4E0B\u6587 / 128K \u8F93\u51FA\uFF08Batch 300K\uFF09\u3002\u5B98\u65B9\u652F\u6301 PDF \u8F93\u5165\u3002\u6CE8\u610F\uFF1AAnthropic \u5B98\u65B9 OpenAI \u517C\u5BB9\u5C42\u4F1A\u5FFD\u7565 effort \u53C2\u6570\uFF0C\u58F0\u660E\u5728\u7B2C\u4E09\u65B9\u7F51\u5173\u6620\u5C04\u65F6\u751F\u6548\u3002',\n },\n {\n id: 'anthropic-claude-5-1',\n // Ahead of the claude-5 entry, whose 'claude-fable-5' / 'claude-mythos-5'\n // patterns are prefixes of these ids and would win the equal-length tie.\n // Same ladder and default as the 5.0 generation per the official effort\n // page; the meaningful difference is behavioural (these two are the\n // models allowed to change effort mid-conversation), which this plugin\n // does not declare per model.\n patterns: ['claude-fable-5-1', 'claude-mythos-5-1'],\n efforts: { low: 'low', medium: 'medium', high: 'high', xhigh: 'xhigh', max: 'max' },\n defaultEffort: 'high',\n compat: { supportsReasoningEffort: true },\n anthropicAdaptive: true,\n input: ['text', 'image'],\n contextWindow: 1_000_000,\n maxTokens: 128_000,\n note: 'Claude Fable 5.1 / Mythos 5.1\uFF1A\u5B98\u65B9\u6863\u4F4D Low / Medium / High(\u9ED8\u8BA4) / XHigh / Max\uFF0C\u81EA\u9002\u5E94\u601D\u8003\u5E38\u5F00\uFF08\u4E0D\u63A5\u53D7 enabled/disabled\uFF09\u30021M \u4E0A\u4E0B\u6587 / 128K \u8F93\u51FA\uFF1B\u8FD9\u4E24\u578B\u4E5F\u662F\u5B98\u65B9\u552F\u4E00\u5141\u8BB8\u4F1A\u8BDD\u4E2D\u9014\u6539\u6863\u7684\uFF08\u9700 beta header\uFF0C\u672C\u63D2\u4EF6\u4E0D\u6D89\u53CA\uFF09\u3002',\n },\n {\n id: 'anthropic-claude-mythos-preview',\n // Official effort matrix: max is available, xhigh is NOT (the matrix's\n // xhigh list covers Fable 5 / Mythos 5 / Opus 5 / Opus 4.8 / Opus 4.7 /\n // Sonnet 5 only).\n patterns: ['claude-mythos-preview'],\n efforts: { low: 'low', medium: 'medium', high: 'high', max: 'max' },\n // Anthropic's effort page: the API default is HIGH across the whole\n // supported list (omitting effort behaves exactly as high).\n defaultEffort: 'high',\n compat: { supportsReasoningEffort: true },\n anthropicAdaptive: true,\n input: ['text', 'image'],\n contextWindow: 1_000_000,\n maxTokens: 128_000,\n note: 'Claude Mythos Preview\uFF1A\u5B98\u65B9\u6863\u4F4D Low / Medium / High / Max\uFF08\u65E0 XHigh\uFF09\uFF0C1M \u4E0A\u4E0B\u6587\u3002',\n },\n {\n id: 'anthropic-claude-opus-4-high',\n patterns: ['claude-opus-4-8', 'claude-opus-4-7'],\n // Official effort page: Opus 4.7/4.8 take the full ladder including\n // xhigh and max (xhigh is the recommended coding start on 4.7).\n efforts: { low: 'low', medium: 'medium', high: 'high', xhigh: 'xhigh', max: 'max' },\n defaultEffort: 'high',\n compat: { supportsReasoningEffort: true },\n anthropicAdaptive: true,\n input: ['text', 'image'],\n contextWindow: 1_000_000,\n maxTokens: 128_000,\n note: 'Claude Opus 4.7/4.8 \u6863\u4F4D\uFF1ALow / Medium / High(\u9ED8\u8BA4) / XHigh / Max\uFF1Bxhigh \u4E3A\u5B98\u65B9\u63A8\u8350\u7684\u7F16\u7801\u8D77\u6B65\u6863\u30021M \u4E0A\u4E0B\u6587\u3002',\n },\n {\n id: 'anthropic-claude-4-6',\n patterns: ['claude-opus-4-6', 'claude-sonnet-4-6'],\n // Official effort page: the 4.6 generation supports max but NOT xhigh\n // (\"some models that support max don't support xhigh\").\n efforts: { low: 'low', medium: 'medium', high: 'high', max: 'max' },\n defaultEffort: 'high',\n compat: { supportsReasoningEffort: true },\n anthropicAdaptive: true,\n input: ['text', 'image'],\n contextWindow: 1_000_000,\n maxTokens: 128_000,\n note: 'Claude 4.6 \u4EE3\u6863\u4F4D\uFF1ALow / Medium / High / Max\uFF08\u65E0 XHigh\u2014\u2014\u5B98\u65B9\u660E\u8A00\u300C\u652F\u6301 max \u7684\u90E8\u5206\u578B\u53F7\u4E0D\u652F\u6301 xhigh\u300D\uFF09\u30021M \u4E0A\u4E0B\u6587\u3002',\n },\n {\n id: 'anthropic-claude-opus-4-5',\n // The only extended-thinking-only model in Anthropic's official effort\n // support list (claude-opus-4-5-20251101): effort works alongside\n // budget_tokens, and the max/xhigh columns of the official matrix do not\n // name it -- so the ladder tops out at high.\n patterns: ['claude-opus-4-5'],\n efforts: { low: 'low', medium: 'medium', high: 'high' },\n defaultEffort: 'high',\n compat: { supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 200_000,\n maxTokens: 64_000,\n note: 'Claude Opus 4.5\uFF1A\u5B98\u65B9 effort \u652F\u6301\u5217\u8868\uFF0820251101 \u5FEB\u7167\uFF09\uFF0C\u6863\u4F4D Low / Medium / High\uFF08\u65E0 XHigh/Max\uFF09\uFF0C\u53EF\u4E0E budget_tokens \u5E76\u7528\uFF1B200K / 64K\u3002',\n },\n {\n id: 'anthropic-claude',\n // Everything else in the Claude line: 3.x and the 4.5-generation\n // Sonnet/Haiku. Anthropic's official effort support list names exactly\n // fable-5 / mythos-5 / mythos-preview / opus-5 / opus-4-8 / opus-4-7 /\n // opus-4-6 / sonnet-5 / sonnet-4-6 / opus-4-5-20251101 -- none of the\n // models matching this entry take the effort parameter (their thinking\n // rides thinking.type), so a ladder here would be refused. `false` is\n // the honest \"no effort control\" declaration, matching the openai-chat\n // convention.\n patterns: ['claude'],\n efforts: false,\n input: ['text', 'image'],\n contextWindow: 200_000,\n maxTokens: 64_000,\n note: 'Claude 3.x \u4E0E Sonnet 4.5 / Haiku 4.5\uFF1A\u5B98\u65B9 effort \u53C2\u6570\u4E0D\u652F\u6301\uFF08\u4EC5 Fable/Mythos 5\u3001Opus 5/4.6-4.8\u3001Sonnet 5/4.6\u3001Opus 4.5 \u652F\u6301\uFF09\uFF0C\u601D\u8003\u7531 thinking.type \u63A7\u5236\u2014\u2014\u52FF\u52FE\u601D\u8003\u6863\u3002\u53C2\u8003\u5BB9\u91CF 200K / 64K\uFF08Haiku 3.5 \u4E3A\u7EAF\u6587\u672C\uFF0C\u6309\u9700\u53D6\u6D88\u56FE\u7247\uFF09\u3002',\n },\n {\n id: 'google-gemini',\n patterns: ['gemini'],\n // Gemini over the OpenAI-compat layer maps reasoning_effort per family\n // (3.1+/3.x -> thinking_level, 2.5 -> thinking_budget). The official\n // compatibility sheet is a full grid (minimal / low / medium / high x\n // family), but the declared ladder stays low/medium/high: the safest\n // universal set, since minimal lands on a non-native value on Gemini\n // 3.1 Pro and none is reserved for 2.5 non-Pro. Users wanting the finer\n // grid tune by hand.\n efforts: { low: 'low', medium: 'medium', high: 'high' },\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 1_048_576,\n maxTokens: 65_536,\n note: 'Gemini \u901A\u7528\u5B89\u5168\u6863\uFF1ALow / Medium / High\uFF08\u5B98\u65B9 OpenAI \u517C\u5BB9\u6620\u5C04\u8868\u53E6\u6536 minimal\uFF1A2.5 \u7CFB\u6620\u5C04\u4E3A 1,024 \u9884\u7B97\u30013.1 Flash-Lite/3 Flash \u539F\u751F minimal\u30013.1 Pro \u843D low\uFF09\u3002none \u4EC5\u80FD\u5173 2.5 \u975E Pro\uFF1B2.5 Pro \u4E0E 3 \u4EE3\u4E0D\u53EF\u5173\uFF1B\u5404\u578B\u9ED8\u8BA4\u4E0D\u4E00\uFF08flash-lite \u9ED8\u8BA4\u5173\uFF09\u3002\u5B98\u65B9\u53E6\u6536\u97F3\u9891/\u89C6\u9891/PDF\u3002',\n },\n {\n id: 'xai-grok-4-7',\n // grok-4.7 shipped after the 2026-08 pass that read it as 404 upstream;\n // that reading is retired here. xAI's own docs were unreachable for this\n // pass, so every field below is a public-catalog reading rather than a\n // first-party one: OpenRouter publishes the slug x-ai/grok-4.7 with\n // reasoning_effort in its supported_parameters, and models.dev records\n // the ladder as low/medium/high/xhigh -- the same four steps 4.6 takes,\n // which is why the same declaration shape is reused. The default level\n // and whether thinking can be closed are NOT established, so neither an\n // `off` nor a defaultEffort is declared. Capacity follows the sources\n // that agree (500K context); max output disagrees across catalogs\n // (450K vs 500K), so it stays out.\n patterns: ['grok-4.7'],\n efforts: { low: 'low', medium: 'medium', high: 'high', xhigh: 'xhigh' },\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 500_000,\n note: 'Grok 4.7\uFF08\u4E0A\u6E38\u5FEB\u7167 grok-4.7-20260916\uFF09\uFF1A\u6863\u4F4D Low / Medium / High / XHigh\u2014\u2014**\u53D6\u81EA\u516C\u5F00\u76EE\u5F55**\uFF08OpenRouter \u7684 supported_parameters\u3001models.dev \u5BF9 docs.x.ai \u7684\u8F6C\u8FF0\uFF09\uFF0C\u672C\u6B21 xAI \u5B98\u65B9\u6587\u6863\u7AD9\u4E0D\u53EF\u8FBE\u3001\u672A\u80FD\u4E00\u624B\u8BC1\u5B9E\uFF1B\u9ED8\u8BA4\u6863\u4F4D\u4E0E\"\u80FD\u5426\u5173\u95ED\u601D\u8003\"\u540C\u6837\u672A\u8BC1\u5B9E\uFF0C\u6545\u4E0D\u58F0\u660E Off\uFF0C\u4E5F\u4E0D\u5199\u9ED8\u8BA4\u6863\u3002\u5E26\u56FE\u8F93\u5165\uFF08\u76EE\u5F55\u8BB0 text+image+file\uFF0CPDF \u5B58\u7591\uFF09\uFF0C500K \u4E0A\u4E0B\u6587\uFF1B\u6700\u5927\u8F93\u51FA\u5404\u76EE\u5F55\u7ED9\u51FA 450K \u4E0E 500K \u4E24\u79CD\u503C\uFF0C\u6309\"\u5206\u6B67\u5373\u4E0D\u5199\"\u5904\u7406\u30022026-08 \u90A3\u6B21\"grok-4.7 \u4E0D\u5B58\u5728\uFF08\u5B98\u65B9 404\uFF09\"\u7684\u7ED3\u8BBA\u5DF2\u968F\u53D1\u5E03\u4F5C\u5E9F\u3002',\n },\n {\n id: 'xai-grok-high',\n patterns: ['grok-4.6'],\n // grok-4.6 adds xhigh on top of low/medium/high (default) and cannot\n // close thinking (docs.x.ai model page + reasoning capability page).\n // (An earlier pass read grok-4.7 as 404 upstream; it shipped later --\n // see the entry above.)\n efforts: { low: 'low', medium: 'medium', high: 'high', xhigh: 'xhigh' },\n defaultEffort: 'high',\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 500_000,\n note: 'Grok 4.6 \u6863\u4F4D\uFF1ALow / Medium / High(\u9ED8\u8BA4) / XHigh\uFF0C\u601D\u8003\u4E0D\u53EF\u5173\u95ED\uFF1B\u5E26\u56FE\u8F93\u5165\u3001500K \u4E0A\u4E0B\u6587\u3002Grok 4.7 \u89C1\u4E0A\u4E00\u6761\uFF08\u6B64\u524D\u8BB0\u4E3A\"\u4E0D\u5B58\u5728\"\uFF0C\u5DF2\u968F\u53D1\u5E03\u4F5C\u5E9F\uFF09\u3002',\n },\n {\n id: 'xai-grok-4-5',\n // grok-4.5 keeps the three-step ladder (default high) with image input\n // and a 500K context window; an xhigh request is silently treated as\n // high (official reasoning page), so it is not declared here.\n patterns: ['grok-4.5'],\n efforts: { low: 'low', medium: 'medium', high: 'high' },\n defaultEffort: 'high',\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 500_000,\n note: 'Grok 4.5 \u6863\u4F4D\uFF1ALow / Medium / High(\u9ED8\u8BA4)\uFF0C\u601D\u8003\u4E0D\u53EF\u5173\u95ED\uFF1B\u5E26\u56FE\u8F93\u5165\u3002\u4F20\u5165 xhigh \u4F1A\u88AB\u5B98\u65B9\u9759\u9ED8\u6309 High \u5904\u7406\u3002',\n },\n {\n id: 'xai-grok-4-3',\n // Official reasoning-capability pages: grok-4.3 documents FOUR effort\n // levels (none / low / medium / high) with a 1M context window; the\n // deprecated grok-4-fast/grok-4-1-fast/grok-4-0709 slugs redirect to\n // grok-4.3 at low/none. The old \"no none for 4.3\" reading of the docs\n // was wrong and is fixed here.\n patterns: ['grok-4.3'],\n // Official models page (docs.x.ai model JSON): supportedEfforts now\n // spans none/low/medium/high/XHIGH with defaultEffort \"low\" -- xhigh\n // joined the ladder after the 2026-08 pass, and low is the default.\n efforts: { off: 'none', low: 'low', medium: 'medium', high: 'high', xhigh: 'xhigh' },\n defaultEffort: 'low',\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 1_000_000,\n note: 'Grok 4.3\uFF1A\u5B98\u65B9\u56DB\u7EA7 reasoning effort\uFF08None / Low / Medium / High\uFF09\uFF0C1M \u4E0A\u4E0B\u6587\u3001\u5E26\u56FE\u8F93\u5165\uFF1Bgrok-4-fast \u7B49\u65E7 slug 2026-05-15 \u8D77\u81EA\u52A8\u91CD\u5B9A\u5411\u5230\u672C\u578B\u53F7\u3002',\n },\n {\n id: 'xai-grok',\n // grok-4.5 (and 4.1-fast etc.) take the standard three-step ladder;\n // grok-4 and older reject reasoning_effort outright -- gateways serving\n // them will refuse the value and the user can clear it by hand. Grok\n // 4/4.1 and grok-3 retired 2026-05-15 (redirected to grok-4.3), and\n // grok-build-0.1 (early-access coding model) publishes no effort\n // contract.\n patterns: ['grok'],\n efforts: { low: 'low', medium: 'medium', high: 'high' },\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text'],\n note: 'Grok \u901A\u7528\u6863\u4F4D\uFF1ALow / Medium / High\u3002\u521D\u4EE3 grok-4\u3001grok-4.20 \u4E0E grok-build-0.1 \u4E0D\u63A5\u53D7\u8BE5\u53C2\u6570\uFF08grok-4 \u7CFB\u4E0E grok-3 \u5DF2\u4E8E 2026-05-15 \u9000\u5F79\u5E76\u91CD\u5B9A\u5411\u81F3 4.3\uFF09\uFF1Bgrok-4.20-multi-agent \u7684\u56DB\u6863\u63A7\u5236\u7684\u662F agent \u6570\u91CF\u800C\u975E\u601D\u8003\u6DF1\u5EA6\u3002\u65B0\u65E7\u4EE3\u9645\u8BF7\u4F18\u5148\u9009 4.5/4.6/4.3\uFF1B\u591A\u6A21\u6001\u53D8\u4F53\u6309\u9700\u52FE\u9009\u56FE\u7247\u3002',\n },\n {\n id: 'mistral-magistral',\n // The native magistral line is a run-together thinking family with no\n // reasoning_effort control of its own (its thinking is driven by\n // prompt_mode / always-on), and Mistral marked the whole line\n // deprecated on 2026-07-28 (phased out through mid-2026; reasoning now\n // rides the generalist models). `false` is the honest \"no effort\n // control\" declaration; gateways that happen to map an effort\n // parameter onto magistral ids can be tuned by hand.\n patterns: ['magistral'],\n efforts: false,\n input: ['text'],\n contextWindow: 32_768,\n maxTokens: 32_768,\n note: 'Magistral \u539F\u751F\u601D\u8003\u7EBF\uFF08\u65E0 effort \u53C2\u6570\uFF0C\u4E3A prompt_mode \u8BED\u4E49\uFF09\uFF0C\u5B98\u65B9\u5DF2\u4E8E 2026-07-28 \u58F0\u660E\u5F03\u7528\u3001\u9010\u6B65\u64A4\u51FA\uFF1B\u73B0\u5F79\u63A8\u7406\u8D70 mistral-small-2603 / mistral-medium-3-5 \u7684 reasoning_effort\uFF08\u89C1\u4E0B\u6761\uFF09\u3002',\n },\n {\n id: 'mistral-medium-3',\n // Official reasoning page + mistral-common request.py (the first-party\n // protocol enum) name the reasoning_effort models: values are exactly\n // none/high. The API ids today are mistral-medium-3-5 (Medium 3.5's\n // model page names the dotted spelling too) and mistral-small-2603\n // (Mistral Small 4); mistral-small-latest is its alias. The dated\n // \"small-2506\" builds are retiring and are NOT matched; the bare\n // 'mistral-medium-3' stem was dropped so the 3.1 generation (no\n // reasoning_effort) does not get the ladder.\n // Normalization folds '.' and '-' to the same space, so the one dotted\n // spelling also keys the API id (mistral-medium-3-5).\n patterns: ['mistral-medium-3.5', 'mistral-small-latest', 'mistral-small-2603'],\n efforts: { off: 'none', high: 'high' },\n // mistral-common's ModelSettings defaults reasoning_effort to None --\n // the endpoint's no-parameter behaviour IS the none/off level.\n defaultEffort: 'off',\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n note: 'Mistral \u73B0\u5F79\u63A8\u7406\u6863\uFF1ANone / High\u2014\u2014\u5B98\u65B9 reasoning_effort \u679A\u4E3E\uFF08mistral-common \u534F\u8BAE\u5E93\uFF09\u5373 none/high\uFF1Bmistral-small-2603\uFF08Small 4\uFF09\u4E0E mistral-medium-3-5 \u7ECF\u5B83\u63A7\u5236\uFF0CMedium 3.5 \u5E26\u89C6\u89C9\u3002\u5BB9\u91CF\u672A\u5728\u5B98\u65B9\u76EE\u5F55\u9875\u5355\u5217\uFF0C\u4E0D\u63D0\u4F9B\u3002',\n },\n {\n id: 'qwen-vision',\n // The DashScope vision lines: -vl suffixes and QvQ. Normalization folds\n // 'qwen2.5-vl' to 'qwen2 5 vl', hence the spelled-out middle pattern.\n // Note the modern generations (3.8-max / 3.7-plus / 3.7-flash and the\n // 3.5-omni line) are multimodal too -- see the qwen-3-8 entry and the\n // plain qwen entry note.\n patterns: ['qwen-vl', 'qwen2-vl', 'qwen2-5-vl', 'qwen3-vl', 'qvq'],\n // DashScope takes NO reasoning_effort: thinking is the enable_thinking\n // boolean -- pi-ai's 'qwen' format dispatches exactly that switch.\n // Commercial Model Studio deployments default enable_thinking to TRUE\n // (help.aliyun.com) -- the on state, which is High here.\n efforts: { off: null, high: 'high' },\n defaultEffort: 'high',\n compat: { thinkingFormat: 'qwen' },\n input: ['text', 'image'],\n contextWindow: 131_072,\n maxTokens: 8_192,\n note: '\u901A\u4E49\u89C6\u89C9\u7EBF\uFF08Qwen-VL/QvQ\uFF09\uFF1Aenable_thinking \u5F00\u5173\uFF08\u5F00=High\uFF09\uFF0C\u6536\u56FE\u3002qwen3-vl \u5BB9\u91CF\u66F4\u5927\uFF0C\u6309\u9700\u4E0A\u8C03\u3002',\n },\n {\n id: 'qwen-3-8',\n // The 3.8 generation moved from the bare enable_thinking switch to an\n // official reasoning_effort ladder: the Qwen3.8 README (\"Flexible\n // Thinking Control\"), the Qwen3.8-27B model card, the Qwen3.8-Flash-Next\n // card and the qwen.ai Flash-Next blog all document reasoning_effort\n // with xhigh as the DEFAULT (xhigh/medium/low), alongside the\n // per-request enable_thinking toggle (chat_template_kwargs on\n // open-weight serving). qwen3.8-max stays the current flagship (1M\n // context; Model Studio lists it under image/video understanding), so\n // the plain qwen entry's text-only declaration must not claim it.\n patterns: ['qwen3.8'],\n efforts: { off: null, low: 'low', medium: 'medium', xhigh: 'xhigh' },\n defaultEffort: 'xhigh',\n compat: { thinkingFormat: 'qwen', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 1_000_000,\n note: 'Qwen 3.8 \u7CFB\uFF1A\u5B98\u65B9 reasoning_effort \u6863\u4F4D XHigh(\u9ED8\u8BA4) / Medium / Low\uFF0Cthinking \u9ED8\u8BA4\u5F00\u3001\u53EF\u6309\u8BF7\u6C42\u5173\u95ED\uFF1B\u539F\u751F\u591A\u6A21\u6001\uFF08qwen3.8-max \u56FE\u50CF/\u89C6\u9891\u7406\u89E3\uFF0C\u89C6\u9891\u672A\u5165\u6838\u5FC3\u8BCD\u8868\uFF09\u30021M \u6863\u4E0A\u4E0B\u6587\u300227B \u4E0E Flash-Next \u5F00\u6E90\u6B3E\u89C1\u5355\u72EC\u6761\u76EE\u3002',\n },\n {\n id: 'qwen-3-8-27b',\n // Open-weight dense release (2026-08-14, HF Qwen/Qwen3.8-27B model\n // card): \"native vision-language model\", 262,144 context natively\n // \"extensible up to 1,000,000 tokens\", reasoning_effort xhigh (default)\n // / medium / low with per-request thinking disable. Longer than the\n // bare 'qwen3.8' pattern, so it wins for -27B ids.\n patterns: ['qwen3.8-27b'],\n efforts: { off: null, low: 'low', medium: 'medium', xhigh: 'xhigh' },\n defaultEffort: 'xhigh',\n compat: { thinkingFormat: 'qwen', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 262_144,\n note: 'Qwen3.8-27B\uFF08dense\uFF0C2026-08-14 \u5F00\u6E90\uFF09\uFF1A\u5B98\u65B9 reasoning_effort \u6863\u4F4D XHigh(\u9ED8\u8BA4) / Medium / Low\uFF0Cthinking \u9ED8\u8BA4\u5F00\u3001\u53EF\u6309\u8BF7\u6C42\u5173\uFF1B\u539F\u751F\u56FE\u50CF+\u89C6\u9891\u7406\u89E3\uFF08\u89C6\u9891\u672A\u5165\u6838\u5FC3\u8BCD\u8868\uFF09\u3002262,144 \u539F\u751F\u4E0A\u4E0B\u6587\u3001\u5B98\u65B9\u58F0\u660E\u53EF\u6269\u81F3 1M\u3002\u6765\u6E90\uFF1AHF Qwen/Qwen3.8-27B \u6A21\u578B\u5361\u3002',\n },\n {\n id: 'qwen-3-8-flash-next',\n // Open-weight experimental architecture preview (HF\n // Qwen/Qwen3.8-Flash-Next card + the qwen.ai blog): the\n // Qwen4-underpinning hybrid (QSA, n-gram embedding; 125B with 6B\n // activated), native vision encoder, 262,144 context natively\n // extensible to 1M. Thinking control identical to the 3.8 generation:\n // enable_thinking toggle plus reasoning_effort xhigh (default) /\n // medium / low. Longest pattern, so it wins for -flash-next ids.\n patterns: ['qwen3.8-flash-next'],\n efforts: { off: null, low: 'low', medium: 'medium', xhigh: 'xhigh' },\n defaultEffort: 'xhigh',\n compat: { thinkingFormat: 'qwen', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 262_144,\n note: 'Qwen3.8-Flash-Next\uFF08125B-A6B \u5B9E\u9A8C\u67B6\u6784\uFF0CQwen4 \u524D\u8EAB\uFF09\uFF1A\u5B98\u65B9 reasoning_effort \u6863\u4F4D XHigh(\u9ED8\u8BA4) / Medium / Low + enable_thinking \u5F00\u5173\uFF1B\u539F\u751F\u89C6\u89C9\uFF08Vision Encoder\uFF09\u3002262,144 \u539F\u751F\u4E0A\u4E0B\u6587\u3001\u53EF\u6269\u81F3 1M\u3002\u6765\u6E90\uFF1AHF \u6A21\u578B\u5361\u4E0E qwen.ai \u5B98\u65B9\u535A\u5BA2\u3002',\n },\n {\n id: 'qwen',\n patterns: ['qwen', 'qwq'],\n // DashScope takes NO reasoning_effort: thinking is the enable_thinking\n // boolean (plus thinking_budget) -- pi-ai's 'qwen' format dispatches\n // exactly that switch, so the ladder is honestly open/close with one\n // nominal thinking level. QwQ is think-always; clearing the off level\n // there is one click. Commercial DashScope defaults enable_thinking to\n // TRUE (open-weight serving defaults to false) -- the on state is High.\n efforts: { off: null, high: 'high' },\n defaultEffort: 'high',\n compat: { thinkingFormat: 'qwen' },\n input: ['text'],\n contextWindow: 1_000_000,\n maxTokens: 65_536,\n note: '\u901A\u4E49\u5343\u95EE\uFF1Aenable_thinking \u5F00\u5173\uFF08\u65E0 effort \u6863\uFF09\uFF0C\u5F00=High\uFF1B\u73B0\u5F79\u65D7\u8230\u4E3A qwen3.8 \u7CFB\uFF081M \u6863\u4E0A\u4E0B\u6587\uFF0C\u89C1 qwen-3-8 \u6761\u76EE\uFF09\u30023.6/3.7 \u4EE3\u7684 Plus/Flash \u4EA6\u9ED8\u8BA4\u591A\u6A21\u6001\uFF08\u56FE\u7247/\u89C6\u9891\uFF09\uFF0C\u5982\u63A5\u5165\u8BF7\u6309\u9700\u52FE\u9009\u56FE\u7247\uFF1B\u89C6\u89C9\u7EBF\u89C1 qwen-vision \u6761\u76EE\u3002',\n },\n {\n id: 'glm-vision',\n // Zhipu's vision lines: glm-4v / glm-4.6v / glm-4.5v / glm-5v. Longer\n // than the plain 'glm' patterns, so they win for those ids.\n // 'glm-4-6v' (not 'glm-46v'): normalization folds 'glm-4.6v' to\n // 'glm 4 6v', so the pattern must carry the same inner separator; the\n // same fold gives 'glm-4.5v' -> 'glm 4 5v'.\n patterns: ['glm-4v', 'glm-4-6v', 'glm-4-5v', 'glm-5v'],\n efforts: { off: null, high: 'high' },\n // GLM-4.5V is forced-thinking and the 4.x line defaults to enabled\n // (docs.bigmodel.cn thinking page) -- the on state is High.\n defaultEffort: 'high',\n compat: { thinkingFormat: 'zai' },\n input: ['text', 'image'],\n contextWindow: 131_072,\n maxTokens: 32_768,\n note: '\u667A\u8C31\u89C6\u89C9\u7EBF\uFF08GLM-4V/4.5V/4.6V/5V\uFF09\uFF1Athinking \u5F00\u5173\uFF08\u5F00=High\uFF09\uFF0C\u6536\u56FE\u3002GLM-4.5V \u4E3A\u5F3A\u5236\u601D\u8003\uFF08\u4F20 disabled \u62A5\u9519\uFF0C\u8BF7\u624B\u52A8\u5220 Off \u6863\uFF09\u3002',\n },\n {\n id: 'glm-5-3-flash',\n // Official model page (docs.bigmodel.cn/cn/guide/models/vlm/glm-5.3-flash,\n // page dated 2026-08-28): input modalities \"\u89C6\u9891\u3001\u56FE\u50CF\u3001\u6587\u672C\u3001\u6587\u4EF6\", 1M\n // context / 128K max output, and \"thinking.type \u4EC5\u652F\u6301 enabled\" \u2014 forced\n // thinking, no off switch. \"\u6587\u672C\u53C2\u6570\u4E0E GLM-5.3 \u4FDD\u6301\u4E00\u81F4\" gives the flash\n // variant the 5.3 ladder (low/high/max). Native multimodal (320B), which\n // the text-only glm-5.3 entry must NOT claim \u2014 hence this separate entry;\n // the longer pattern wins over plain 'glm-5.3'.\n patterns: ['glm-5.3-flash'],\n efforts: { low: 'low', high: 'high', max: 'max' },\n defaultEffort: 'max',\n compat: { thinkingFormat: 'zai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 1_048_576,\n maxTokens: 131_072,\n note: 'GLM-5.3-Flash\uFF08320B\uFF0C\u5B98\u65B9\u6587\u6863 vlm \u5206\u7C7B\uFF09\uFF1A\u5F3A\u5236\u601D\u8003\uFF08thinking.type \u4EC5 enabled\uFF0C\u4E0D\u652F\u6301\u5173\u95ED\uFF09\uFF0C\u6587\u672C\u53C2\u6570\u4E0E GLM-5.3 \u4E00\u81F4\u2014\u2014\u6863\u4F4D Low / High / Max\u3002\u8F93\u5165\u6A21\u6001\uFF1A\u89C6\u9891\u3001\u56FE\u50CF\u3001\u6587\u672C\u3001\u6587\u4EF6\uFF08\u89C6\u9891/\u6587\u4EF6\u672A\u5165\u6838\u5FC3\u8BCD\u8868\uFF0C\u56FE\u7247\u53EF\u52FE\uFF09\u3002\u5B98\u65B9 1M \u4E0A\u4E0B\u6587 / 128K \u6700\u5927\u8F93\u51FA\u3002\u6765\u6E90\uFF1Adocs.bigmodel.cn \u6A21\u578B\u9875\u3002',\n },\n {\n id: 'glm-5-3',\n patterns: ['glm-5.3'],\n // glm-5.3 accepts exactly max/high/low; anything else (including the\n // 5.2 ladder's minimal/none) errors, and thinking cannot be disabled.\n // The API reference names reasoning_effort's default as max.\n efforts: { low: 'low', high: 'high', max: 'max' },\n defaultEffort: 'max',\n compat: { thinkingFormat: 'zai', supportsReasoningEffort: true },\n input: ['text'],\n contextWindow: 1_048_576,\n maxTokens: 131_072,\n note: 'GLM-5.3 \u6863\u4F4D\uFF1ALow / High / Max\uFF08\u5F3A\u5236\u601D\u8003\uFF0C\u5176\u4F59\u503C\u62A5\u9519\uFF09\u3002\u5B98\u65B9\u5BB9\u91CF 1M / 128K\u3002',\n },\n {\n id: 'glm-5-2',\n patterns: ['glm-5.2'],\n // glm-5.2 takes the widest ladder in the wild: none/minimal/low/medium/\n // high/xhigh/max (default max). The zai format arms thinking:disabled\n // for off and sends the effort for the rest.\n efforts: { off: 'none', minimal: 'minimal', low: 'low', medium: 'medium', high: 'high', xhigh: 'xhigh', max: 'max' },\n defaultEffort: 'max',\n compat: { thinkingFormat: 'zai', supportsReasoningEffort: true },\n input: ['text'],\n contextWindow: 1_048_576,\n maxTokens: 131_072,\n // Official capability page: none/minimal stop thinking, low/medium map\n // to high, xhigh maps to max -- all seven spellings are accepted.\n note: 'GLM-5.2 \u6863\u4F4D\uFF1ANone / Minimal / Low / Medium / High / XHigh / Max\uFF08\u5B98\u65B9\u6620\u5C04\uFF1ALow\u00B7Medium\u2192High\u3001XHigh\u2192Max\u3001None\u00B7Minimal=\u505C\u6B62\u601D\u8003\uFF09\u3002\u89C6\u89C9\u7EBF\u89C1 glm-vision \u6761\u76EE\u3002',\n },\n {\n id: 'glm',\n patterns: ['glm', 'zhipu', 'chatglm'],\n // GLM 4.x: thinking.type enabled/disabled only, no effort ladder --\n // the zai format sends exactly that switch (and no effort value,\n // because supportsReasoningEffort is not declared). GLM-4.5/4.6\n // default to enabled (hybrid reasoning: the model decides per\n // request) -- the on state is High.\n efforts: { off: null, high: 'high' },\n defaultEffort: 'high',\n compat: { thinkingFormat: 'zai' },\n input: ['text'],\n note: 'GLM \u901A\u7528\uFF1Athinking \u5F00\u5173\uFF08\u65E0 effort \u6863\uFF09\uFF0C\u5F00=High\uFF1Beffort \u9636\u68AF\u4EC5 GLM-5.2+ \u652F\u6301\uFF08\u89C1\u4E0A\u4E24\u6761\u76EE\uFF09\u3002\u6CE8\u610F GLM-4.7/GLM-4.5V \u4E3A\u5F3A\u5236\u601D\u8003\uFF0C\u4F20 disabled \u4F1A\u62A5\u9519\uFF08\u8BF7\u624B\u52A8\u5220 Off \u6863\uFF09\u3002\u89C6\u89C9\u7EBF\u89C1 glm-vision \u6761\u76EE\u3002',\n },\n {\n id: 'kimi-k3',\n patterns: ['kimi-k3'],\n // kimi-k3: top-level reasoning_effort low/high/max (default max),\n // always thinking -- the thinking object must not be sent at all.\n efforts: { low: 'low', high: 'high', max: 'max' },\n defaultEffort: 'max',\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 1_048_576,\n note: 'Kimi K3 \u6863\u4F4D\uFF1ALow / High / Max(\u9ED8\u8BA4 Max)\uFF0C\u8D70\u9876\u5C42 reasoning_effort\uFF1B\u59CB\u7EC8\u63A8\u7406\u3001\u52FF\u4F20 thinking \u5BF9\u8C61\u3002\u539F\u751F\u89C6\u89C9\u7406\u89E3\uFF0C1M \u4E0A\u4E0B\u6587\uFF08\u6700\u5927\u8F93\u51FA\u5B98\u65B9\u672A\u5355\u72EC\u5217\uFF0C\u4E0D\u63D0\u4F9B\uFF09\u3002',\n },\n {\n id: 'kimi-k2-vision',\n // K2.5/K2.6 are multimodal thinking models with a toggleable\n // thinking.type switch (official use-thinking-models table); K2.5 was\n // re-verified as MULTIMODAL on 2026-08-24 (the old \"text-only\" note was\n // wrong). Longer patterns beat the plain 'kimi' stem below.\n patterns: ['kimi-k2.6', 'kimi-k2.5'],\n efforts: { off: null, high: 'high' },\n defaultEffort: 'high',\n compat: { thinkingFormat: 'deepseek' },\n input: ['text', 'image'],\n contextWindow: 262_144,\n note: 'Kimi K2.5/K2.6 \u89C6\u89C9\u4EE3\uFF1Athinking.type \u5F00\u5173\uFF08\u9ED8\u8BA4\u5F00\u3001\u53EF\u5173\uFF09\uFF0C\u5F00=High\uFF1B\u5E26\u89C6\u89C9\uFF0C256K\u3002K2.7 Code \u59CB\u7EC8\u601D\u8003\uFF0C\u89C1\u5355\u72EC\u6761\u76EE\u3002',\n },\n {\n id: 'kimi-k27-code',\n // K2.7 Code (and its highspeed twin) ALWAYS think: thinking.type only\n // accepts \"enabled\", sending \"disabled\" errors -- so no Off level.\n patterns: ['kimi-k2.7'],\n efforts: { high: 'high' },\n defaultEffort: 'high',\n compat: { thinkingFormat: 'deepseek' },\n input: ['text', 'image'],\n contextWindow: 262_144,\n note: 'Kimi K2.7 Code\uFF08\u542B\u9AD8\u901F\u7248\uFF09\uFF1A\u59CB\u7EC8\u601D\u8003\u2014\u2014thinking.type \u4EC5\u63A5\u53D7 enabled\uFF08\u4F20 disabled \u62A5\u9519\uFF09\uFF0C\u65E0 Off \u6863\uFF1B\u5E26\u89C6\u89C9\uFF0C256K\u3002',\n },\n {\n id: 'kimi-moonshot-v1-vision',\n // Legacy Moonshot V1 vision variants (official model list): plain\n // generation models with no thinking control, but they DO accept images\n // -- the generic 'moonshot' pattern must not declare text-only for them.\n patterns: ['moonshot-v1-8k-vision', 'moonshot-v1-32k-vision', 'moonshot-v1-128k-vision'],\n efforts: false,\n input: ['text', 'image'],\n note: 'Moonshot V1 \u89C6\u89C9\u65E7\u4EE3\uFF088k/32k/128k \u5404\u4E00\uFF09\uFF1A\u65E0\u601D\u8003\u63A7\u4EF6\uFF08\u751F\u6210\u6A21\u578B\uFF09\u3001\u652F\u6301\u56FE\u7247\u8F93\u5165\uFF1B\u8BE5\u7EBF\u5DF2\u505C\u6B62\u5BF9\u65B0\u7528\u6237\u5F00\u653E\uFF082026-08-31 \u5168\u91CF\u4E0B\u7EBF\uFF0C\u7F51\u5173\u6B8B\u7559\u4ECD\u53EF\u547D\u4E2D\uFF09\u3002',\n },\n {\n id: 'kimi',\n // k2.5/2.6 use the DeepSeek-shaped thinking.type enabled/disabled\n // switch, which the 'deepseek' format dispatches; the old k2 preview\n // ids and moonshot-v1 names are retired upstream (2026-05/08) but live\n // on gateways. Effort ladders are not part of their contract.\n patterns: ['kimi', 'moonshot'],\n efforts: { off: null, high: 'high' },\n compat: { thinkingFormat: 'deepseek' },\n input: ['text'],\n contextWindow: 262_144,\n note: 'Kimi \u901A\u7528\uFF1Athinking \u5F00\u5173\uFF08\u65E0 effort \u6863\uFF09\uFF0C\u5F00=High\u3002k2 \u7CFB\u5217 2026-05-25 \u4E0B\u7EBF\u3001kimi-latest 2026-01-28 \u4E0B\u7EBF\u3001moonshot-v1 \u7CFB 2026-08-31 \u5168\u91CF\u4E0B\u7EBF\uFF08\u7F51\u5173\u6B8B\u7559\u4ECD\u53EF\u547D\u4E2D\uFF1Bmoonshot-v1 \u672C\u8EAB\u975E\u601D\u8003\u6A21\u578B\uFF09\u3002\u89C6\u89C9\u4EE3\u4E0E K2.7 Code \u89C1\u5355\u72EC\u6761\u76EE\u3002',\n },\n {\n id: 'hunyuan-hy3',\n // Open-weight contract (Tencent-Hunyuan/Hy3 README): reasoning_effort\n // travels inside chat_template_kwargs as \"no_think\" (default) / \"low\" /\n // \"high\"; the same README's spec table states a 256K context length.\n patterns: ['hy3'],\n efforts: { low: 'low', high: 'high' },\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text'],\n contextWindow: 262_144,\n note: '\u6DF7\u5143 hy3\uFF1A\u5F00\u6E90\u5951\u7EA6\u7ECF chat_template_kwargs.reasoning_effort = no_think(\u9ED8\u8BA4)/low/high\uFF1B\u5B98\u65B9\u6A21\u578B\u5361 256K \u4E0A\u4E0B\u6587\uFF08295B MoE\uFF0C2026 \u5F00\u6E90\uFF09\u3002',\n },\n {\n id: 'hunyuan-hy4',\n // Open-weight contract (Tencent-Hunyuan/Hy4-preview README): reasoning\n // defaults to \"high\" (deep chain-of-thought) and is disabled with\n // extra_body={\"chat_template_kwargs\": {\"reasoning_effort\": \"no_think\"}}\n // \u2014 the same chat-template channel as hy3, with the default flipped to\n // high. The spec table states a 1M context length; the model is a\n // 770B-A49B MoE with Gated DSA attention. The README documents no levels\n // beyond high/no_think, so a low rung is NOT declared without evidence.\n patterns: ['hy4-preview', 'hy-4-preview'],\n efforts: { off: 'no_think', high: 'high' },\n defaultEffort: 'high',\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n contextWindow: 1_000_000,\n note: '\u6DF7\u5143 Hy4 preview\uFF1A\u5B98\u65B9 README\u2014\u2014reasoning \u9ED8\u8BA4 high\uFF08\u6DF1\u5EA6\u601D\u8003\uFF09\uFF0C\u5173\u95ED\u7ECF chat_template_kwargs.reasoning_effort=no_think\uFF1B\u5B98\u65B9\u89C4\u683C\u8868 1M \u4E0A\u4E0B\u6587\uFF08770B-A49B MoE\uFF0CGated DSA\uFF09\u3002low \u6863\u5B98\u65B9\u672A\u5217\uFF0C\u5982\u6709\u8BF7\u624B\u8C03\uFF1B\u89C6\u89C9\u672A\u58F0\u660E\uFF0C\u6309\u9700\u624B\u52FE\u3002\u6765\u6E90\uFF1ATencent-Hunyuan/Hy4-preview \u5B98\u65B9 README\u3002',\n },\n {\n id: 'step-5-preview',\n // StepFun's official model is 'step-5-preview' (there is no bare\n // 'step-5'), carrying the same three-step reasoning_effort ladder as the\n // 3.x line with no way to close thinking -- hence no `off`. The official\n // page calls medium the \"default recommendation\". 1M context, 64K max\n // output, native image + video input.\n patterns: ['step-5'],\n efforts: { low: 'low', medium: 'medium', high: 'high' },\n defaultEffort: 'medium',\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 1_000_000,\n maxTokens: 65_536,\n note: '\u9636\u8DC3 Step-5-Preview\uFF08\u5B98\u65B9 id \u5373 step-5-preview\uFF0C\u65E0\u88F8 step-5\uFF1Bpattern \u53D6\u5BB6\u65CF\u4E3B\u5E72\u4EE5\u517C\u5BB9\u7F51\u5173\u5199\u6CD5\uFF09\uFF1A\u6863\u4F4D Low / Medium(\u5B98\u65B9\u6807\"\u9ED8\u8BA4\u63A8\u8350\") / High\uFF0C\u601D\u8003\u4E0D\u53EF\u5173\u95ED\uFF1B\u539F\u751F\u56FE\u7247 + \u89C6\u9891\u8F93\u5165\uFF1B1M \u4E0A\u4E0B\u6587\u300164K \u6700\u5927\u8F93\u51FA\uFF08\u5B98\u65B9\u53E6\u6CE8 max_tokens \u9ED8\u8BA4 INF\u3001\u4E0E 64K \u4E0A\u9650\u5E76\u5B58\uFF0C\u4EE5\u7AEF\u70B9\u5B9E\u6D4B\u4E3A\u51C6\uFF09\u3002',\n },\n {\n id: 'step-3-7',\n // StepFun step-3.7 is the vision-capable generation; 3.5 stayed\n // text-only on the official serving plan.\n patterns: ['step-3.7', 'step-3.6'],\n efforts: { low: 'low', medium: 'medium', high: 'high' },\n defaultEffort: 'medium',\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 262_144,\n note: '\u9636\u8DC3 Step-3.6/3.7 \u6863\u4F4D\uFF1ALow / Medium(\u9ED8\u8BA4\u63A8\u8350) / High\uFF0C\u539F\u751F\u56FE\u7247+\u89C6\u9891\u7406\u89E3\u3002\u4E0A\u4E0B\u6587\u4E3A\u8F93\u5165+\u8F93\u51FA\u603B\u548C\u4E0A\u9650\u3002',\n },\n {\n id: 'step-3-5',\n // step-3.5-flash (official listing): only low/high, text-only.\n patterns: ['step-3.5'],\n efforts: { low: 'low', high: 'high' },\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text'],\n contextWindow: 262_144,\n note: '\u9636\u8DC3 Step-3.5 Flash \u6863\u4F4D\uFF1ALow / High\uFF08\u7EAF\u6587\u672C\u63A8\u7406\u65D7\u8230\uFF09\u3002\u4E0A\u4E0B\u6587\u4E3A\u8F93\u5165+\u8F93\u51FA\u603B\u548C\u4E0A\u9650\u3002',\n },\n {\n id: 'step',\n // StepFun step-3.x: reasoning_effort low/medium/high (default medium);\n // thinking cannot be turned off, only tuned.\n patterns: ['step-3', 'step-2'],\n efforts: { low: 'low', medium: 'medium', high: 'high' },\n defaultEffort: 'medium',\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text'],\n note: '\u9636\u8DC3 Step \u6863\u4F4D\uFF1ALow / Medium / High\uFF08\u9ED8\u8BA4 Medium\uFF0C\u4E0D\u53EF\u5173\u95ED\uFF09\u30023.6/3.7 \u89C6\u89C9\u4EE3\u89C1\u5355\u72EC\u6761\u76EE\u3002',\n },\n {\n id: 'doubao',\n // Volcengine Ark: officially documented is the thinking.type\n // enabled/disabled/auto switch; the reasoning_effort ladder on\n // seed-2.x has community evidence (chatbox/compshare) but no first-\n // party doc yet -- kept as the conventional ladder, verify per gateway.\n // 'doubao' already covers every doubao-* spelling; the bare 'seed' token\n // (boundary-checked) catches seed-first ids some gateways serve. The\n // seed generations are vision-capable; doubao-1.5-pro stays text-only.\n patterns: ['doubao', 'seed'],\n efforts: { off: null, low: 'low', medium: 'medium', high: 'high' },\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n note: '\u8C46\u5305\u6863\u4F4D\uFF1AOff / Low / Medium / High\u2014\u2014effort \u9636\u68AF\u4ECD\u662F\u793E\u533A\u8BC1\u636E\u3001\u5B98\u65B9 Ark \u6587\u6863\u660E\u5217\u7684\u4E3A thinking.type \u5F00\u5173\uFF082026-08-24 \u590D\u6838\u4ECD\u672A\u89C1\u5230 effort \u5B98\u65B9\u660E\u6587\uFF09\u3002seed \u4EE3\u6536\u56FE\uFF081.5-pro \u4F8B\u5916\uFF09\u3002',\n },\n {\n id: 'minimax-m3',\n // Official M3 README: a thinking parameter with enabled / adaptive /\n // disabled -- NO reasoning_effort ladder. That is exactly the\n // deepseek-shaped toggle, so the kimi-style declaration fits; \"adaptive\"\n // is not expressible in the level vocabulary and defaults apply.\n patterns: ['minimax-m3'],\n efforts: { off: null, high: 'high' },\n compat: { thinkingFormat: 'deepseek' },\n input: ['text', 'image'],\n contextWindow: 1_048_576,\n note: 'MiniMax-M3\uFF1A\u5B98\u65B9 thinking \u53C2\u6570 enabled/adaptive/disabled\uFF08\u65E0 effort \u6863\uFF09\uFF0C\u5F00=High\u3001\u5173=disabled\uFF1B\u539F\u751F\u591A\u6A21\u6001\uFF08\u56FE/\u89C6\u9891\uFF0C\u6838\u5FC3\u8BCD\u8868\u4EC5\u542B\u56FE\uFF09\uFF0C1M \u4E0A\u4E0B\u6587\u3002',\n },\n {\n id: 'mimo-v2-6',\n // Xiaomi MiMo v2.6 (and the v2.5 generation it supersedes) takes a required\n // thinking.type toggle (enabled/disabled, default enabled) plus a required\n // reasoning.effort. The official page lists the accepted effort values as\n // none/minimal/low/medium/high/xhigh/max/ultra and normalizes minimal to\n // low and xhigh/max/ultra to high -- but says outright that reasoning\n // strength is not differentiated yet, so the ladder below is what the\n // endpoint ACCEPTS, not a set of levels that behave differently today.\n // xhigh/max are deliberately absent: the endpoint folds them into high.\n // One entry covers both generations: the same models are documented under\n // one contract, and the family's only text-only member (v2.5-pro) keys its\n // own entry with a strictly longer pattern.\n patterns: ['mimo-v2.6', 'mimo-v2.5'],\n efforts: { off: 'none', low: 'low', medium: 'medium', high: 'high' },\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 1_048_576,\n maxTokens: 131_072,\n note: '\u5C0F\u7C73 MiMo v2.6 \u7CFB\u5217\uFF08mimo-v2.6-pro / -flash / -pro-ultraspeed\uFF1B1M \u4E0A\u4E0B\u6587 / 128K \u6700\u5927\u8F93\u51FA\u3001\u539F\u751F\u5168\u6A21\u6001\uFF09\u4E0E\u5176\u524D\u4EE3 mimo-v2.5\u3002\u5B98\u65B9\u6587\u6863\uFF1Athinking.type\uFF08enabled/disabled\uFF0C\u9ED8\u8BA4 enabled\uFF09+ reasoning.effort\uFF08none \u5173\u95ED\uFF1B\u5176\u4F59\u5408\u6CD5\u503C\u5747\u5F00\u542F\uFF0C\u4E14**\u73B0\u9636\u6BB5\u6682\u672A\u533A\u5206\u63A8\u7406\u5F3A\u5EA6**\u2014\u2014minimal \u5F52\u4E00\u4E3A low\u3001xhigh/max/ultra \u5F52\u4E00\u4E3A high\uFF09\uFF0C\u56E0\u6B64\u8FD9\u91CC\u58F0\u660E\u7684\u662F\u7AEF\u70B9**\u63A5\u53D7**\u7684\u6863\u4F4D Off / Low / Medium / High\uFF08xhigh\u3001max \u4E0D\u58F0\u660E\uFF0C\u7AEF\u70B9\u53EA\u4F1A\u6298\u53E0\u4E3A high\uFF09\u3002\u601D\u8003\u6A21\u5F0F\u4E0B temperature / top_p \u4E0D\u53EF\u81EA\u5B9A\u4E49\uFF08\u5F3A\u5236 1.0 / 0.95\uFF09\uFF1B\u5E26\u5DE5\u5177\u8C03\u7528\u7684\u5386\u53F2\u8F6E\u5FC5\u987B\u5B8C\u6574\u56DE\u4F20 reasoning_content\uFF0C\u5426\u5219 400\u3002mimo-v2.5 \u4E0E v2.5-pro \u5B98\u65B9\u5B9A\u4E8E 2026-10-21 \u4E0B\u7EBF\u3002',\n },\n {\n id: 'mimo-v2-5-pro',\n // The v2.5 flagship is the family's only text-only member -- the official\n // capability table lists no multimodal understanding for it (models.dev\n // agrees, attachment=false), while every v2.6 model and mimo-v2.5 do.\n // Strictly longer pattern than the family entry's 'mimo-v2.5', so the\n // longest-hit rule routes it here.\n patterns: ['mimo-v2.5-pro'],\n efforts: { off: 'none', low: 'low', medium: 'medium', high: 'high' },\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text'],\n contextWindow: 1_048_576,\n maxTokens: 131_072,\n note: '\u5C0F\u7C73 MiMo v2.5-Pro\uFF1A\u8BE5\u5BB6\u65CF\u552F\u4E00\u7684**\u7EAF\u6587\u672C**\u6210\u5458\uFF08\u5B98\u65B9\u80FD\u529B\u8868\u672A\u5217\u5168\u6A21\u6001\u7406\u89E3\uFF09\uFF0C1M \u4E0A\u4E0B\u6587 / 128K \u6700\u5927\u8F93\u51FA\uFF0C\u63A8\u7406\u5951\u7EA6\u4E0E v2.6 \u7CFB\u5217\u76F8\u540C\uFF08thinking.type + reasoning.effort\uFF0Cnone \u5173\u95ED\u3001\u5176\u4F59\u6863\u4F4D\u6682\u4E0D\u533A\u5206\u5F3A\u5EA6\uFF09\u3002\u5B98\u65B9\u5B9A\u4E8E 2026-10-21 \u4E0B\u7EBF\u3002',\n },\n {\n id: 'baidu-ernie',\n // Baidu Qianfan's OpenAI-compatible surface accepts no reasoning_effort\n // parameter (the v2 API parameter tables carry none); thinking is a\n // per-variant property: the -Thinking / ERNIE 5.0-Thinking-* family is\n // always-on thinking, the plain ERNIE 4.5/5.0/5.1 generations are not\n // thinking models at all. The official pricing page (2026-08) lists\n // ERNIE 5.1 / 5.0 / 5.0-Thinking-Preview|Latest|Exp / 4.5-Turbo(-VL).\n patterns: ['ernie'],\n efforts: false,\n input: ['text'],\n contextWindow: 131_072,\n note: '\u767E\u5EA6\u5343\u5E06 ERNIE \u7CFB\uFF1A\u5B98\u65B9 OpenAI \u517C\u5BB9\u63A5\u53E3\u65E0 reasoning_effort \u53C2\u6570\u2014\u2014\u601D\u8003\u7531\u6A21\u578B\u53D8\u4F53\u51B3\u5B9A\uFF08-Thinking \u7CFB\u5217\u59CB\u7EC8\u601D\u8003\u3001\u666E\u901A\u7CFB\u5217\u4E0D\u601D\u8003\uFF09\uFF0C\u52FF\u52FE\u601D\u8003\u6863\u3002ERNIE 4.5 Turbo VL \u652F\u6301\u56FE\u7247\uFF0C\u6309\u9700\u624B\u52FE\uFF1B\u53C2\u8003\u5BB9\u91CF\u53D6 Turbo 128K \u6863\u3002',\n },\n]\n\n/**\n * The generic OpenAI-compatible ladder -- the single source for every\n * OpenAI-shaped protocol row below and the fallback when nothing matches.\n */\nexport const GENERIC_OPENAI_EFFORTS: ReasoningEfforts = {\n off: null,\n low: 'low',\n medium: 'medium',\n high: 'high',\n}\n\n/**\n * Level sets keyed by pi-ai's REAL wire protocols -- the only values a\n * route's 'api' may take are 'openai-completions' | 'openai-responses' |\n * 'anthropic-messages' (llm-pi-ai provider.ts PROTOCOLS) -- plus the\n * 'deepseek' URL dialect for routes that name no api but point at DeepSeek's\n * own endpoint, which takes its native off/high/max ladder.\n *\n * 'off: null' is only offered where a no-thinking mode is plausible; the\n * conservative choice elsewhere is to omit it so the picker never promises an\n * Off the endpoint would reject. Exported for the vocabulary grid tests that\n * pin these ladders against pi-ai's resolution rules.\n */\nexport const PROTOCOL_INFERENCE: Readonly<Record<string, ReasoningEfforts>> = {\n 'openai-completions': GENERIC_OPENAI_EFFORTS,\n 'openai-responses': GENERIC_OPENAI_EFFORTS,\n 'anthropic-messages': GENERIC_OPENAI_EFFORTS,\n // The live V4 contract: low/high/max (+ thinking toggle for off, spelled\n // 'none' so an off selection also maps to the official Responses-API value\n // reasoning.effort: none).\n deepseek: { off: 'none', low: 'low', high: 'high', max: 'max' },\n}\n\n/** A best-effort generic declaration when even the protocol is unknown. */\nconst GENERIC_FALLBACK: ReasoningEfforts = { low: 'low', medium: 'medium', high: 'high' }\n\n/** Normalize one route fact for matching/inference. */\nfunction normalize(value: string | undefined): string {\n return (value ?? '').toLowerCase().trim()\n}\n\n/** Whether a character is a lowercase letter or digit (word-boundary test). */\nfunction isAlnum(ch: string | undefined): boolean {\n return ch !== undefined && /[a-z0-9]/.test(ch)\n}\n\n/**\n * Lowercase and collapse every non-alphanumeric run to one space, so id /\n * display-name / pattern separators ('-', '_', '.', spaces) compare equal:\n * pattern 'deepseek-v3' matches display name \"DeepSeek V3\".\n */\nfunction normalizeLoose(value: string): string {\n return value.toLowerCase().trim().replace(/[^a-z0-9]+/g, ' ')\n}\n\n/**\n * Whether the occurrence of a pattern at index 'at' sits on a word boundary\n * in the haystack: neither neighbor may be a letter/digit. Keeps short family\n * tokens from false-hitting ('o1' must hit 'o1 mini' but not 'ko1 pro';\n * 'seed' hits 'doubao seed 1 6' but not 'seedling').\n */\nfunction onBoundary(haystack: string, at: number, length: number): boolean {\n const before = at === 0 ? undefined : haystack[at - 1]\n const after = haystack[at + length]\n return !isAlnum(before) && !isAlnum(after)\n}\n\n/** Match the knowledge base: first match wins, longest boundary hit wins. */\nexport function matchKnowledgeBase(modelId: string, displayName?: string): KnowledgeEntry | undefined {\n const haystack = normalizeLoose(modelId) + ' ' + normalizeLoose(displayName ?? '')\n let best: { entry: KnowledgeEntry; length: number } | undefined\n for (const entry of KNOWLEDGE_BASE) {\n for (const pattern of entry.patterns) {\n const needle = normalizeLoose(pattern)\n let at = haystack.indexOf(needle)\n while (at >= 0) {\n if (onBoundary(haystack, at, needle.length)) {\n if (best === undefined || needle.length > best.length) {\n best = { entry, length: needle.length }\n }\n break\n }\n at = haystack.indexOf(needle, at + 1)\n }\n }\n }\n return best?.entry\n}\n\n/**\n * Infer which PROTOCOL_INFERENCE key applies to a route. A configured 'api'\n * is already a pi-ai protocol name and wins as-is; otherwise the endpoint's\n * HOST names the dialect -- matched on the registrable domain so an aggregator\n * path like https://gw.example.com/deepseek/v1 stays generic -- and anything\n * unrecognized resolves to the OpenAI-compatible ladder (the overwhelming\n * majority of gateways).\n */\nconst NATIVE_DIALECT_DOMAINS: Readonly<Record<string, string>> = {\n // api.deepseek.com is the official endpoint (api-docs.deepseek.com). The\n // 'deepseek.ai' form was dropped: it resolves to a Vercel-hosted site\n // (76.76.21.21), not DeepSeek infrastructure, so nothing warrants assuming\n // the native dialect for it.\n 'deepseek.com': 'deepseek',\n 'anthropic.com': 'anthropic-messages',\n}\n\nexport function inferProtocol(route: RouteFacts): string {\n const api = normalize(route.api)\n if (api.length > 0) return api\n const baseURL = route.baseURL\n if (baseURL !== undefined && baseURL.length > 0) {\n try {\n const host = new URL(baseURL).hostname.toLowerCase()\n const parts = host.split('.')\n if (parts.length >= 2) {\n const dialect = NATIVE_DIALECT_DOMAINS[parts.slice(-2).join('.')]\n if (dialect !== undefined) return dialect\n }\n } catch {\n // Malformed URL: fall through to the generic ladder.\n }\n }\n // Former 'gemini' guessing removed: pi-ai has no gemini protocol, and\n // Gemini gateways speak the OpenAI-compatible dialect.\n return 'openai-completions'\n}\n\n/**\n * The one wire protocol whose compat gate offers thinkingFormat /\n * supportsReasoningEffort (llm-pi-ai COMPLETIONS_COMPAT_GATE). pi-ai refuses\n * a model-level compat field its resolved protocol does not take\n * (assertServiceable throws at the write), so compat suggestions are gated to\n * this protocol only -- with one exception: the anthropic-messages gate\n * offers forceAdaptiveThinking, which is what makes declared efforts reach\n * the wire as output_config.effort (see {@link KnowledgeEntry.anthropicAdaptive}).\n */\nconst COMPAT_CAPABLE_PROTOCOL = 'openai-completions'\nconst ANTHROPIC_PROTOCOL = 'anthropic-messages'\n\n/** Whether a baseURL names a self-hosted endpoint (loopback, private LAN, .local, custom port). */\nexport function isSelfHostedEndpoint(baseURL: string | undefined): boolean {\n if (baseURL === undefined || baseURL.length === 0) return false\n let host = ''\n try {\n host = new URL(baseURL).hostname.toLowerCase()\n } catch {\n return false\n }\n if (host === 'localhost' || host === '::1' || host.endsWith('.local') || host.endsWith('.internal')) return true\n if (/^(127\\.|10\\.|192\\.168\\.)/.test(host)) return true\n if (/^172\\.(1[6-9]|2[0-9]|3[01])\\./.test(host)) return true\n return host.includes(':')\n}\n\n/** Drop catalog-withheld keys pi-ai never accepts from a profile (defensive). */\nexport function sanitizeCompatForProtocol(compat: CompatSuggestion, api: string): CompatSuggestion | undefined {\n const norm = normalize(api)\n const copy: Record<string, unknown> = { ...compat }\n delete copy['supportsMidConvoEffort']\n delete copy['allowedFallbackModels']\n if (norm === ANTHROPIC_PROTOCOL) {\n for (const key of ['thinkingFormat', 'supportsReasoningEffort', 'thinkingTokenBudgetField', 'supportsThinkingTokenBudget', 'vllmPriority', 'supportsMaxOutputTokens'] as const) delete copy[key]\n if (copy['forceAdaptiveThinking'] === undefined) return undefined\n return { forceAdaptiveThinking: copy['forceAdaptiveThinking'] as boolean }\n }\n if (norm === 'openai-responses') {\n for (const key of ['thinkingFormat', 'supportsReasoningEffort', 'thinkingTokenBudgetField', 'supportsThinkingTokenBudget', 'vllmPriority'] as const) delete copy[key]\n if (copy['supportsMaxOutputTokens'] === undefined) return undefined\n return { supportsMaxOutputTokens: copy['supportsMaxOutputTokens'] as boolean }\n }\n if (norm !== COMPAT_CAPABLE_PROTOCOL) return undefined\n delete copy['forceAdaptiveThinking']\n delete copy['supportsMaxOutputTokens']\n return Object.keys(copy).length === 0 ? undefined : (copy as CompatSuggestion)\n}\n\n/** Gate a compat block against the route's real protocol. */\nfunction compatForRoute(entry: KnowledgeEntry, route: RouteFacts): CompatSuggestion | undefined {\n const api = normalize(route.api)\n if (api === COMPAT_CAPABLE_PROTOCOL) {\n let compat = entry.compat\n if (compat !== undefined) compat = sanitizeCompatForProtocol(compat, api) ?? undefined\n if (compat === undefined && !isSelfHostedEndpoint(route.baseURL)) return undefined\n if (compat === undefined) return { thinkingTokenBudgetField: 'thinking_token_budget' }\n if (compat.thinkingTokenBudgetField === undefined && compat.supportsThinkingTokenBudget !== true && isSelfHostedEndpoint(route.baseURL)) {\n return { ...compat, thinkingTokenBudgetField: 'thinking_token_budget' }\n }\n return compat\n }\n if (api === ANTHROPIC_PROTOCOL && entry.anthropicAdaptive === true) {\n return { forceAdaptiveThinking: true }\n }\n return undefined\n}\n\n/**\n * Official endpoint roots where pi-ai may send role:developer, so no pin is\n * needed. Matching is a DNS-suffix match, so multi-level roots (e.g.\n * openai.azure.com) work while a relay merely hosted on the same cloud\n * (e.g. *.cloudapp.azure.com) still counts as self-hosted.\n *\n * Deliberately fail-safe: hosts outside this set get the pin. `system` is\n * accepted everywhere, `developer` is not, so an unknown host keeps the\n * universally accepted role -- the pin is harmless on official endpoints\n * too. Only add a root after verifying its endpoints accept\n * role:developer (vendor docs or a wire capture); when in doubt, leave it\n * out. The pi-ai-parity entries change no bytes (pi-ai already detects\n * those hosts as nonstandard, i.e. supportsDeveloperRole false); they are\n * listed so the two detectors cannot drift apart silently.\n */\nconst OFFICIAL_RELAY_DOMAINS: readonly string[] = [\n // First-party OpenAI-compatible endpoints (developer accepted).\n 'openai.com',\n 'openai.azure.com',\n 'deepseek.com',\n 'openrouter.ai',\n 'z.ai',\n 'bigmodel.cn',\n 'moonshot.ai',\n 'moonshot.cn',\n 'aliyuncs.com',\n 'anthropic.com',\n 'x.ai',\n 'together.ai',\n 'together.xyz',\n 'nvidia.com',\n 'cerebras.ai',\n 'cloudflare.com',\n // pi-ai-parity: detected nonstandard upstream (pin would be a no-op).\n 'chutes.ai',\n 'opencode.ai',\n 'ant-ling.com',\n]\n\n/**\n * Whether a route is a self-hosted relay whose upstreams cannot be assumed\n * to accept role:developer: openai-completions protocol on a baseURL whose\n * host no official root claims. Absent or malformed URLs answer false --\n * with nothing to judge the host by, pi-ai's detection default stands.\n */\nexport function isSelfHostedRelay(route: RouteFacts): boolean {\n if (normalize(route.api) !== COMPAT_CAPABLE_PROTOCOL) return false\n const baseURL = route.baseURL\n if (baseURL === undefined || baseURL.length === 0) return false\n try {\n const host = new URL(baseURL).hostname.toLowerCase()\n if (!host.includes('.')) return false\n return !OFFICIAL_RELAY_DOMAINS.some((root) => host === root || host.endsWith('.' + root))\n } catch {\n return false\n }\n}\n\n/**\n * Pin the system role on self-hosted relays: merge\n * `supportsDeveloperRole: false` into a compat block that would otherwise\n * let pi-ai rewrite the system prompt to role:developer (issue #2 -- the\n * upstream answers \u89D2\u8272\u4FE1\u606F\u4E0D\u6B63\u786E). An explicitly declared value always\n * wins; non-reasoning suggestions (efforts === false) never carry compat.\n */\nfunction withRolePin(\n compat: CompatSuggestion | undefined,\n route: RouteFacts,\n reasons: boolean,\n): CompatSuggestion | undefined {\n if (compat === undefined || !reasons) return compat\n if (compat.supportsDeveloperRole !== undefined) return compat\n if (!isSelfHostedRelay(route)) return compat\n return { ...compat, supportsDeveloperRole: false }\n}\n\n/** Conservative ladder offered when the endpoint confirms reasoning but nothing names the levels. */\nconst ENDPOINT_CONFIRMED_LADDER: ReasoningEfforts = { off: null, low: 'low', medium: 'medium', high: 'high' }\n\n/**\n * Name-heuristic modality tokens -- the last-resort tier (the practice\n * one-api/new-api bake into code): vision-flavored tokens that appear in\n * vendor ids often enough to suggest image input at LOW confidence. Boundary\n * matching keeps 'vl' from hitting 'eval' or 'svelte'.\n */\nconst VISION_NAME_TOKENS: readonly string[] = [\n 'vl',\n 'vision',\n 'omni',\n '4o',\n 'pixtral',\n 'internvl',\n]\n\n/** Lowest-confidence modality guess from the model id alone, or undefined. */\nexport function inferModalitiesFromName(modelId: string): InputModalities | undefined {\n const haystack = normalizeLoose(modelId)\n for (const token of VISION_NAME_TOKENS) {\n const needle = normalizeLoose(token)\n let at = haystack.indexOf(needle)\n while (at >= 0) {\n if (onBoundary(haystack, at, needle.length)) return ['text', 'image']\n at = haystack.indexOf(needle, at + 1)\n }\n }\n return undefined\n}\n\n/**\n * Intersect a raw listing disclosure with the core vocabulary. Returns\n * undefined when the listing never named a modality field OR named one whose\n * members all fall outside the core vocabulary (an audio-only declaration\n * must NOT read as \"text only\"). A present disclosure always yields at least\n * text-only when image is absent -- so an explicit text-only listing can\n * strip an image claim.\n */\nfunction declaredCoreInput(raw: readonly string[] | undefined): InputModalities | undefined {\n if (raw === undefined) return undefined\n const lowered = raw.map(value => value.toLowerCase())\n if (!lowered.some(value => (INPUT_MODALITIES as readonly string[]).includes(value))) return undefined\n return lowered.includes('image') ? ['text', 'image'] : ['text']\n}\n\n/**\n * Resolve the suggestion for one model on one route, fusing the available\n * signals by confidence:\n *\n * L1 endpoint listing signal (when supplied) -- answers \"does it reason at\n * all\" and, independently, \"what does it accept\": an explicit false\n * wins the effort ladder outright (suggest false, high); a modality\n * disclosure or a context length feeds those parts regardless;\n * L2 knowledge base -- the ONLY source of wire spellings and compat, plus\n * modality/capacity references when the endpoint stays silent;\n * L3 name heuristic -- a low-confidence image guess for vision-flavored\n * ids when both sources above are silent;\n * L4 protocol inference -- level-ladder skeleton when nothing better exists.\n *\n * @param modelId - the model id (or display name) to match.\n * @param route - route facts used when the knowledge base misses; its\n * displayName participates in knowledge-base matching.\n * @param endpoint - optional raw-listing facts for this model (from the\n * host's same-origin probe route); omit when the endpoint was not asked.\n * @returns a suggestion (always derivable -- protocol inference has a fallback).\n */\nexport function suggestEfforts(\n modelId: string,\n route: RouteFacts,\n endpoint?: EndpointFacts,\n): EffortSuggestion {\n const entry = matchKnowledgeBase(modelId, route.displayName)\n\n // ---- Modality + capacity parts (orthogonal to the effort ladder) ----\n let input: InputModalities | undefined\n let inputSource: InputSource | undefined\n let contextWindow: number | undefined\n let maxTokens: number | undefined\n\n if (entry?.input !== undefined) {\n input = entry.input\n inputSource = 'knowledge'\n }\n if (entry?.contextWindow !== undefined) contextWindow = entry.contextWindow\n if (entry?.maxTokens !== undefined) maxTokens = entry.maxTokens\n\n // An endpoint disclosure outranks the knowledge base; a disclosed context\n // length likewise replaces the reference value. Silence changes nothing.\n const listedInput = declaredCoreInput(endpoint?.input)\n if (listedInput !== undefined) {\n input = listedInput\n inputSource = 'endpoint'\n }\n if (endpoint?.contextLength !== undefined && Number.isFinite(endpoint.contextLength) && endpoint.contextLength > 0) {\n contextWindow = endpoint.contextLength\n }\n\n // L3: nothing named the modalities -- fall back to the name heuristic.\n if (input === undefined) {\n const guessed = inferModalitiesFromName(modelId)\n if (guessed !== undefined) {\n input = guessed\n inputSource = 'heuristic'\n }\n }\n\n const modalityParts = {\n ...(input === undefined ? {} : { input }),\n ...(inputSource === undefined ? {} : { inputSource }),\n ...(contextWindow === undefined ? {} : { contextWindow }),\n ...(maxTokens === undefined ? {} : { maxTokens }),\n }\n\n const endpointPart = endpoint === undefined\n ? {}\n : { endpoint: { reasoning: endpoint.reasoning, source: endpoint.source } }\n\n // L1: an explicit \"does not reason\" from the endpoint outranks everything.\n if (endpoint?.reasoning === false) {\n return {\n efforts: false,\n matched: false,\n source: 'endpoint:' + (endpoint.source ?? 'listing'),\n confidence: 'high',\n ...endpointPart,\n ...modalityParts,\n }\n }\n\n if (entry !== undefined) {\n const compat = withRolePin(compatForRoute(entry, route), route, entry.efforts !== false)\n return {\n efforts: entry.efforts,\n // The vendor default rides along only when the suggested ladder\n // actually carries it (a declaration may deliberately drop a level).\n ...(entry.efforts !== false\n && entry.defaultEffort !== undefined\n && entry.efforts[entry.defaultEffort] !== undefined\n ? { defaultEffort: entry.defaultEffort }\n : {}),\n ...(compat === undefined ? {} : { compat }),\n matched: true,\n entryId: entry.id,\n source: entry.id,\n // Knowledge base carries the wire values; an agreeing endpoint signal\n // only reinforces it. A DISAGREEING signal (endpoint says no reasoning)\n // was handled above; unknown changes nothing.\n confidence: 'high',\n ...endpointPart,\n ...modalityParts,\n }\n }\n\n // L2 missed: the ladder comes from protocol inference. An explicit endpoint\n // \"reasons\" upgrades confidence to medium -- support is confirmed, but the\n // level set and spellings are inferred, so the user should double-check.\n const protocol = inferProtocol(route)\n const efforts = PROTOCOL_INFERENCE[protocol] ?? GENERIC_FALLBACK\n const compatBase: CompatSuggestion | undefined = normalize(route.api) === COMPAT_CAPABLE_PROTOCOL\n ? { thinkingFormat: 'openai', supportsReasoningEffort: true }\n : undefined\n const compatWithBudget = compatBase !== undefined && isSelfHostedEndpoint(route.baseURL)\n ? { ...compatBase, thinkingTokenBudgetField: 'thinking_token_budget' as const }\n : compatBase\n const compat = withRolePin(compatWithBudget, route, true)\n if (endpoint?.reasoning === true) {\n return {\n efforts: { ...ENDPOINT_CONFIRMED_LADDER },\n ...(ENDPOINT_CONFIRMED_LADDER['medium'] !== undefined ? { defaultEffort: 'medium' as const } : {}),\n // compat was already gated to the protocol above; no second gate.\n ...(compat === undefined ? {} : { compat }),\n matched: false,\n source: 'endpoint:' + (endpoint.source ?? 'listing'),\n confidence: 'medium',\n ...endpointPart,\n ...modalityParts,\n }\n }\n return {\n efforts,\n // Inferred ladders default to medium (the convention codex/OpenAI set for\n // their own ladders) whenever the inferred set carries it.\n ...(efforts['medium'] !== undefined ? { defaultEffort: 'medium' as const } : {}),\n ...(compat === undefined ? {} : { compat }),\n matched: false,\n source: 'protocol:' + protocol,\n confidence: 'low',\n ...endpointPart,\n ...modalityParts,\n }\n}\n", "/**\n * Effort memory: per-model reasoning-effort levels, re-applied automatically\n * when a model switch lands without one.\n *\n * The official model list submits a model switch as a selection WITHOUT an\n * effort unless the model advertises a default (`ModelSelect` builds its\n * choices from `reasoning.defaultEffort`, which hand-declared models never\n * carry) \u2014 and the host replaces the session's whole selection, so the\n * previous level is dropped and the trigger reads \"Default\". Wrapping the\n * shared per-session directory's `select` (the SAME instance the official\n * seat, the /model popup, and this plugin's slider all submit through) lets\n * the remembered level ride the switch in ONE atomic commit: no \"Default\"\n * flash, and every surface shows the re-applied level.\n *\n * The memory is keyed by \"provider/model-id\" \u2014 the exact fields a selection\n * carries \u2014 because the same model id under two providers names two models,\n * and display names are editable and never unique. Two delivery paths share\n * one fallback chain (the SESSION'S own sighting of a level the user picked\n * in it, else the per-model configured pick from the settings document, else\n * the last cross-session memory from localStorage, else the VENDOR'S documented\n * default from the knowledge base, else the official no-effort behaviour):\n * - the wrapped `select`: a model switch submitted without a level rides\n * the remembered level in ONE atomic commit \u2014 no \"Default\" flash, and\n * every surface shows the re-applied level;\n * - the projection watcher: a RESTORED session's durable selection arrives\n * from session history without going through `select` at all, so the\n * watcher re-applies the chain to a level-less projection (one successful\n * attempt per model per directory lifetime; a REFUSED attempt is refunded\n * and retried on the next store update, up to a small ceiling \u2014 which\n * also keeps an explicit provider-default pick from being fought).\n * Discipline both paths keep:\n * - an EXPLICIT level (slider drag, any effort row) always wins: it is\n * remembered for that exact model and submitted untouched \u2014 and it is\n * recorded as a SESSION sighting, which outranks the configured pick for\n * the rest of the session (switching away and back must not lose it);\n * - an effort-less selection for the CURRENT model is an explicit \"follow\n * the provider default\" and must pass through untouched \u2014 the session\n * sighting dies with it;\n * - nothing is injected while the slider preference (the seat the memory\n * serves) is switched off.\n *\n * @module dsh-model-think-level/client/effort-memory\n */\nimport { sliderEnabled } from './slider-pref.js'\nimport { suggestEfforts } from '../knowledge.js'\nimport type { DirectoryCurrentLike, ModelDirectoryLike, ModelDirectoryStateLike } from './types.js'\n\n/**\n * The wiring-provided read of the per-model configured pick (the settings\n * document's `defaultEffort` field). Async because it lives in the pi-ai\n * document, not in localStorage; the wiring owns the caching describe.\n * Absent (tests, or a wiring that does not supply it) skips the layer.\n */\nexport interface EffortMemoryDeps {\n configuredEffort?: (provider: string, model: string) => Promise<string | undefined>\n}\n\n/** localStorage key (same namespace discipline as the slider preference). */\nconst EFFORT_MEMORY_KEY = 'dsh-model-think-level.slider.efforts'\n\n/** Marker guarding against double-wrapping one directory instance (HMR re-apply). */\nconst WIRED_MARKER = 'breSelectWired'\n\n/**\n * Whether a `select` outcome is a refusal.\n *\n * Kernels through 0.1.6-alpha.1 reject by THROWING; 0.1.6-alpha.2 resolves the\n * wire's `RemoteResult` (`{ok:false}`) instead. Normalizing both shapes here\n * keeps the memory discipline identical on either kernel.\n * @param outcome - the value `select` resolved with (or undefined on success).\n * @returns whether the host refused the selection.\n */\nexport function isSelectRefusal(outcome: unknown): boolean {\n return typeof outcome === 'object' && outcome !== null && (outcome as { ok?: unknown }).ok === false\n}\n\n/**\n * The user-facing message of a refusal result, or undefined when the outcome\n * was accepted. Only 0.1.6-alpha.2's resolved shape carries one; a kernel that\n * rejects by throwing never reaches here.\n * @param outcome - the value `select` resolved with.\n * @returns `code: message`, or undefined when accepted.\n */\nexport function selectRefusalMessage(outcome: unknown): string | undefined {\n if (!isSelectRefusal(outcome)) return undefined\n const error = (outcome as { error?: { code?: unknown; message?: unknown } }).error\n const code = typeof error?.code === 'string' ? error.code : 'settings/rejected'\n const message = typeof error?.message === 'string' ? error.message : ''\n return message.length > 0 ? `${code}: ${message}` : code\n}\n\n/** Per-model memory: \"provider/model-id\" \u2192 the last level explicitly picked for it. */\ntype EffortMemory = Record<string, string>\n\n/** The storage key of one model: the exact fields a selection carries. */\nfunction memoryKey(provider: string, model: string): string {\n return `${provider}/${model}`\n}\n\n/**\n * Read the per-model memory, defensively: the value is plain JSON in\n * localStorage, so a malformed or hostile document degrades to \"no memory\"\n * instead of poisoning the wrapper.\n */\nfunction readMemory(): EffortMemory {\n try {\n const raw = window.localStorage.getItem(EFFORT_MEMORY_KEY)\n if (raw === null) return {}\n const parsed: unknown = JSON.parse(raw)\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) return {}\n const memory: EffortMemory = {}\n for (const [key, value] of Object.entries(parsed as Record<string, unknown>)) {\n if (key.length > 0 && typeof value === 'string' && value.trim().length > 0) memory[key] = value\n }\n return memory\n } catch {\n return {}\n }\n}\n\nfunction writeMemory(memory: EffortMemory): void {\n try {\n window.localStorage.setItem(EFFORT_MEMORY_KEY, JSON.stringify(memory))\n } catch {\n // Unavailable storage (quota, privacy mode): this page keeps working with\n // the levels it picked; the switch fallback below does not depend on us.\n }\n}\n\n/** Record the level the user explicitly picked for one model. */\nexport function rememberEffort(provider: string, model: string, id: string): void {\n const memory = readMemory()\n memory[memoryKey(provider, model)] = id\n writeMemory(memory)\n}\n\n/** The level explicitly picked for one model, or undefined when none was. */\nexport function rememberedEffort(provider: string, model: string): string | undefined {\n return readMemory()[memoryKey(provider, model)]\n}\n\n/** The effort level ids one model's entry advertises in a directory snapshot. */\nfunction effortIdsOf(\n state: ModelDirectoryStateLike,\n provider: string,\n model: string,\n): readonly string[] {\n const group = state.groups.find(candidate => candidate.id === provider)\n const efforts = group?.models.find(candidate => candidate.id === model)?.reasoning?.efforts\n return efforts === undefined ? [] : efforts.map(level => level.id)\n}\n\n/** The model-advertised default level, when the catalog carries one. */\nfunction advertisedDefaultOf(\n state: ModelDirectoryStateLike,\n provider: string,\n model: string,\n): string | undefined {\n const group = state.groups.find(candidate => candidate.id === provider)\n return group?.models.find(candidate => candidate.id === model)?.reasoning?.defaultEffort\n}\n\n/**\n * The level the composer body displays when a ladder has no current or declared\n * default. Keep the wire fallback identical to that visual prediction; otherwise\n * the body can say \"Medium\" while the native trigger still says \"Default\".\n * A one-level ladder remains provider-owned because there is no real choice to\n * restore and the existing no-fallback behavior is useful for capability probes.\n */\nfunction displayedMiddleOf(supported: readonly string[]): string | undefined {\n if (supported.length < 2) return undefined\n return supported[Math.floor((supported.length - 1) / 2)]\n}\n\n/** Whether the selection moves to a DIFFERENT model (or picks the first one). */\nfunction isModelSwitch(\n current: DirectoryCurrentLike | null,\n selection: { provider: string; model: string },\n): boolean {\n return current === null\n || current.provider !== selection.provider\n || current.model !== selection.model\n}\n\n/**\n * Resolve the level the memory chain lands on for one model of a snapshot:\n * the SESSION'S own sighting (a level the user picked in this session --\n * switching away and back must not lose it), else the per-model configured\n * pick from the settings document, else the cross-session memory, else the\n * vendor's documented default, else the catalog's advertised default, else the\n * same middle-level preview used by the composer body \u2014 always validated\n * against the ADVERTISED ladder, and undefined when the ladder has no usable\n * fallback.\n * Async only because the configured pick reads the settings document; every\n * other layer is synchronous.\n */\nasync function resolveFallback(\n snapshot: ModelDirectoryStateLike,\n provider: string,\n model: string,\n sessionSightings: ReadonlyMap<string, string>,\n deps: EffortMemoryDeps | undefined,\n): Promise<string | undefined> {\n const supported = effortIdsOf(snapshot, provider, model)\n if (supported.length === 0) return undefined\n const key = memoryKey(provider, model)\n const sighted = sessionSightings.get(key)\n if (sighted !== undefined && supported.includes(sighted)) return sighted\n const configured = await deps?.configuredEffort?.(provider, model)\n if (configured !== undefined && supported.includes(configured)) return configured\n const memory = rememberedEffort(provider, model)\n if (memory !== undefined && supported.includes(memory)) return memory\n const fallback = suggestEfforts(model, {}).defaultEffort\n if (fallback !== undefined && supported.includes(fallback)) return fallback\n const advertised = advertisedDefaultOf(snapshot, provider, model)\n if (advertised !== undefined && supported.includes(advertised)) return advertised\n return displayedMiddleOf(supported)\n}\n\n/**\n * Wire one directory with the effort-memory discipline: wrap its `select`\n * (model-switch path) and watch its store (projection-restore path).\n * Idempotent: an already-wired directory is left alone and the returned\n * disposer is a no-op. The disposer unsubscribes the watcher and restores\n * the original `select` (the directory instance outlives the plugin fiber\n * on disable/HMR).\n * @param directory - the shared per-session directory to wire.\n * @param deps - optional wiring face (the configured-pick read).\n * @returns the disposer restoring the original `select` and the watcher.\n */\nexport function wireEffortMemory(directory: ModelDirectoryLike, deps?: EffortMemoryDeps): () => void {\n const target = directory as unknown as Record<string, unknown>\n if (target[WIRED_MARKER] === true) return () => {}\n // Whether `select` was an OWN property at wire time decides the restore:\n // real directories carry it on the prototype, so the disposer removes the\n // instance shadow entirely instead of freezing a copy of today's method\n // onto the instance (which would keep shadowing a hot-swapped prototype).\n const hadOwnSelect = Object.prototype.hasOwnProperty.call(target, 'select')\n const original = directory.select\n // Levels the user picked IN THIS SESSION, per model. The directory (and\n // this closure) is a per-session instance, so the map is session-scoped by\n // construction: it outranks the configured pick for the rest of the\n // session \u2014 switching away and back must not lose the user's pick \u2014 and\n // dies with the session instead of leaking into the next one.\n const sessionSightings = new Map<string, string>()\n // One successful restore per model per directory lifetime: spent attempts\n // are never retried, which bounds the watcher (no loop on a refused write)\n // and keeps an explicit provider-default pick from being fought. A REFUSED\n // submission refunds its attempt -- a brand-new session's host may refuse\n // the first selectModel while the session is still initializing -- up to a\n // small ceiling, beyond which the model is given up on for this directory.\n // A chain MISS refunds too: the configured layer reads live settings, so a\n // later document change can answer differently.\n const attemptedRestores = new Set<string>()\n const restoreFailures = new Map<string, number>()\n\n /** Stop retrying one model after this many refused restores (per directory). */\n const RESTORE_RETRY_LIMIT = 3\n\n // Every wrapped select call is a user utterance, counted in order. Both\n // async submission paths (the wrapped switch and the watcher) verify just\n // before their post-await submission that no LATER utterance landed \u2014 a\n // newer user action always wins, whatever it said, and the superseded\n // submission is dropped entirely instead of overwriting it.\n let utterances = 0\n\n const wrapped = async (\n selection: Parameters<ModelDirectoryLike['select']>[0],\n ): Promise<unknown> => {\n const myUtterance = ++utterances\n // An explicit level is the memory's source of truth \u2014 but only an\n // ACCEPTED one: a refused selection (host validation failed) must not\n // poison the memory and re-fail every later switch.\n if (selection.reasoningEffort !== undefined) {\n const result = await original.call(directory, selection)\n // Recording is part of the plugin's presence: with the slider off the\n // plugin is absent, so an explicit pick must not be recorded either \u2014\n // the wrapped submit then stays a pure pass-through, exactly as the\n // unwired directory behaved. A REFUSED pick is never recorded, whether\n // the kernel rejected by throwing (<= 0.1.6-alpha.1) or by resolving a\n // refusal result (0.1.6-alpha.2).\n if (!isSelectRefusal(result) && sliderEnabled()) {\n sessionSightings.set(memoryKey(selection.provider, selection.model), selection.reasoningEffort)\n rememberEffort(selection.provider, selection.model, selection.reasoningEffort)\n }\n return result\n }\n const snapshot = directory.store.getSnapshot()\n // Same model, no level: an explicit \"follow the provider default\" \u2014\n // the session sighting dies with it, so this session keeps honouring\n // the user's pick for the rest of its life. The model's restore attempt\n // is spent with it: the confirmed selection re-notifies the watcher with\n // a level-less projection, and the chain must not answer it by fighting\n // the pick the user just made. (Both bookkeeping moves are gated like\n // the recording above \u2014 with the slider off this is a plain pass-through.)\n if (!isModelSwitch(snapshot.current, selection)) {\n if (sliderEnabled()) {\n const key = memoryKey(selection.provider, selection.model)\n sessionSightings.delete(key)\n attemptedRestores.add(key)\n }\n return original.call(directory, selection)\n }\n // A switch without a level: re-apply the session's sighting, the\n // configured pick, the model's own memory, the vendor/catalog default, or\n // the same middle-level preview the composer displays \u2014 so the native\n // trigger and the replicated body settle on one level.\n if (!sliderEnabled()) return original.call(directory, selection)\n const fallback = await resolveFallback(snapshot, selection.provider, selection.model, sessionSightings, deps)\n // The chain awaited (the configured layer may have made a wire round\n // trip): re-verify before speaking. A newer user action \u2014 an explicit\n // pick, a follow-the-default, or a different selection \u2014 that landed\n // during the window is final: the stale submission is dropped entirely\n // instead of overwriting it. \"Newer\" means the store's selection moved\n // off the snapshot this call started from, or another wrapped call ran.\n const fresh = directory.store.getSnapshot()\n const moved = (a: DirectoryCurrentLike | null, b: DirectoryCurrentLike | null): boolean =>\n a === null || b === null\n ? a !== b\n : a.provider !== b.provider || a.model !== b.model || a.reasoningEffort !== b.reasoningEffort\n if (utterances !== myUtterance || moved(fresh.current, snapshot.current)) return undefined\n // The chain awaited: the gate is re-checked BEFORE speaking, or a slider\n // toggle landing inside the await window would make the plugin inject a\n // level while it is already absent (the switch itself must still land --\n // as the plain pass-through the unwired directory would have performed).\n if (!sliderEnabled()) return original.call(directory, selection)\n if (fallback === undefined) return original.call(directory, selection)\n // The catalog can refresh during the same window: the level must still\n // sit on the CURRENT advertised ladder.\n if (!effortIdsOf(fresh, selection.provider, selection.model).includes(fallback)) {\n return original.call(directory, selection)\n }\n return original.call(directory, { ...selection, reasoningEffort: fallback })\n }\n\n /** Re-apply the chain to a level-less durable projection (session restore). */\n const restoreProjection = (): void => {\n const snapshot = directory.store.getSnapshot()\n const current = snapshot.current\n if (current === null || current.reasoningEffort !== undefined) return\n // The gate comes BEFORE the attempt is spent: with the slider off the\n // plugin is absent, and absence must not burn the model's one attempt.\n if (!sliderEnabled()) return\n const key = memoryKey(current.provider, current.model)\n if (attemptedRestores.has(key)) return\n // A model with no advertised ladder cannot be spoken to at all: a\n // deterministic miss \u2014 spend the attempt so the watcher does not\n // re-resolve the same dead end on every store update.\n if (effortIdsOf(snapshot, current.provider, current.model).length === 0) {\n attemptedRestores.add(key)\n return\n }\n attemptedRestores.add(key)\n const utteranceAtAttempt = utterances\n // Directly through the ORIGINAL select: the watcher's own re-apply is a\n // memory READ, not a user pick, and must not be remembered as one.\n void resolveFallback(snapshot, current.provider, current.model, sessionSightings, deps)\n .then(async (fallback) => {\n if (fallback === undefined) {\n // The chain lands nowhere for now \u2014 refund: the configured layer\n // reads live settings, so a later document change may answer.\n attemptedRestores.delete(key)\n return\n }\n // The chain awaited: the gate is re-checked before speaking. A toggle\n // landing inside the window must leave the attempt REFUNDED, not\n // spent -- the watcher is silent while the slider is off, so a spent\n // attempt here would leave the model un-restored forever after the\n // slider comes back (nothing re-kicks a spent restore).\n if (!sliderEnabled()) {\n attemptedRestores.delete(key)\n return\n }\n // The chain awaited: re-verify against the CURRENT store before\n // speaking. A newer user utterance (an explicit pick, or a\n // follow-the-default that landed mid-window) is final \u2014 this attempt\n // stays spent instead of overriding it.\n const fresh = directory.store.getSnapshot()\n const freshCurrent = fresh.current\n if (utterances !== utteranceAtAttempt\n || freshCurrent === null || freshCurrent.reasoningEffort !== undefined\n || memoryKey(freshCurrent.provider, freshCurrent.model) !== key\n || !effortIdsOf(fresh, freshCurrent.provider, freshCurrent.model).includes(fallback)) {\n return\n }\n const outcome = await original\n .call(directory, { provider: freshCurrent.provider, model: freshCurrent.model, reasoningEffort: fallback })\n // 0.1.6-alpha.2 resolves the refusal instead of throwing: route it to\n // the refund path below, or a transient refusal would spend the\n // model's only attempt.\n if (isSelectRefusal(outcome)) throw outcome\n })\n .catch(() => {\n // A refusal is transient (a session mid-initialization), not a\n // decision: refund the attempt so the next store update retries --\n // but stop at the ceiling so a persistently rejected submission\n // cannot turn the watcher into a loop.\n const failures = (restoreFailures.get(key) ?? 0) + 1\n restoreFailures.set(key, failures)\n if (failures < RESTORE_RETRY_LIMIT) {\n attemptedRestores.delete(key)\n // The refusal's own error update reached this watcher BEFORE this\n // refund (the store notifies synchronously, the refund is a\n // microtask), so \"wait for the next store update\" may wait forever\n // on a quiet session: re-kick the watcher here, ceiling still\n // bounding the loop.\n restoreProjection()\n }\n })\n }\n\n const unsubscribe = directory.store.subscribe(restoreProjection)\n // The projection may already be resident when the directory gets wired.\n restoreProjection()\n\n target[WIRED_MARKER] = true\n directory.select = wrapped as typeof directory.select\n return () => {\n delete target[WIRED_MARKER]\n unsubscribe()\n if (hadOwnSelect) directory.select = original\n else delete target['select']\n }\n}\n", "/**\n * Real-time search filter for the composer model selection panel.\n *\n * Injected above the official provider-group list while the drilled-in model\n * pane is open (`section[role=\"group\"]` present). Filters the official option\n * buttons and provider groups via zero-dependency DOM visibility toggles\n * without mutating the underlying ModelDirectory store, and never touches the\n * official card's own sizing or scrolling.\n *\n * @module dsh-model-think-level/client/ModelSearch\n */\n\nimport { createElement, useCallback, useEffect, useRef, useState } from 'react'\nimport type { KeyboardEvent as ReactKeyboardEvent, ReactNode } from 'react'\n\n/** Props of {@link ModelSearch}. */\nexport interface ModelSearchProps {\n /** The official menu element containing group sections and model options. */\n menu: HTMLElement\n /** Localized copy translator. */\n t: (key: string, params?: Record<string, string | number>) => string\n}\n\n/** The official provider-group sections: also the model pane's own marker. */\nconst GROUP_SELECTOR = 'section[role=\"group\"]'\n\n/** The official option buttons (model rows AND effort rows share this role). */\nconst OPTION_SELECTOR = 'button[role=\"menuitemradio\"]'\n\n/**\n * Whether the menu currently shows the drilled-in model list.\n *\n * Deliberately structural: the official effort pane renders\n * `button[role=\"menuitemradio\"]` too (ModelSelect.tsx:464) and would match a\n * role-based probe, while only the model pane renders provider groups. The\n * probe also avoids class names \u2014 the groups container's `groups` class is a\n * CSS-module hash (`Uc5hea_groups` in the official build), so a literal\n * `.groups` selector never matches.\n */\nexport function isModelPane(menu: HTMLElement): boolean {\n return menu.querySelector(GROUP_SELECTOR) !== null\n}\n\n/** The visible, enabled option buttons, in document order. */\nexport function visibleOptions(menu: HTMLElement): HTMLButtonElement[] {\n return Array.from(menu.querySelectorAll<HTMLButtonElement>(OPTION_SELECTOR))\n .filter(btn => !btn.disabled && btn.style.display !== 'none')\n}\n\n/**\n * A provider group's heading text, read through the official aria contract\n * (`section[role=\"group\"][aria-labelledby]`, ModelSelect.tsx:415-416).\n *\n * No structural fallback: \"the first descendant carrying an id\" would be the\n * first MODEL NAME whenever the heading is missing, which would then match\n * that group on any query containing it.\n */\nfunction groupNameOf(group: HTMLElement): string {\n const labelledBy = group.getAttribute('aria-labelledby')\n const heading = labelledBy === null ? null : group.ownerDocument.getElementById(labelledBy)\n return heading?.textContent?.toLowerCase() ?? ''\n}\n\n/**\n * Filter official model option buttons and provider groups by search query.\n *\n * `touched` records the inline `display` each node held BEFORE this filter\n * first changed it, so the restore pass puts back the node's own value instead\n * of blindly clearing an inline style the official menu may have set itself.\n * @param menu - the official menu element.\n * @param query - the raw search text; empty restores everything.\n * @param touched - carry-over map of the nodes this filter currently hides.\n * @returns number of visible model buttons across all groups.\n */\nexport function filterMenuModels(\n menu: HTMLElement,\n query: string,\n touched: Map<HTMLElement, string> = new Map(),\n): number {\n const hide = (element: HTMLElement): void => {\n if (!touched.has(element)) touched.set(element, element.style.display)\n element.style.display = 'none'\n }\n const show = (element: HTMLElement): void => {\n const original = touched.get(element)\n if (original === undefined) return\n element.style.display = original\n touched.delete(element)\n }\n // The official list replaces its rows on a re-render; detached nodes must not\n // accumulate in the map for the lifetime of an open menu.\n for (const element of [...touched.keys()]) {\n if (!element.isConnected) touched.delete(element)\n }\n\n const tokens = query.trim().toLowerCase().split(/\\s+/).filter(Boolean)\n const groups = Array.from(menu.querySelectorAll<HTMLElement>(GROUP_SELECTOR))\n let totalVisible = 0\n\n // An empty query restores EVERYTHING unconditionally \u2014 including a group\n // that carries a heading but no options at all, which the matching branch\n // below would otherwise hide.\n if (tokens.length === 0) {\n for (const element of [...touched.keys()]) show(element)\n for (const group of groups) {\n totalVisible += group.querySelectorAll(OPTION_SELECTOR).length\n }\n return totalVisible\n }\n\n for (const group of groups) {\n const groupName = groupNameOf(group)\n const buttons = Array.from(group.querySelectorAll<HTMLElement>(OPTION_SELECTOR))\n let visibleInGroup = 0\n\n for (const btn of buttons) {\n const titleText = (btn.getAttribute('title') ?? '').toLowerCase()\n const contentText = (btn.textContent ?? '').toLowerCase()\n const combined = `${groupName} ${titleText} ${contentText}`\n\n if (tokens.every(token => combined.includes(token))) {\n show(btn)\n visibleInGroup += 1\n } else {\n hide(btn)\n }\n }\n\n if (visibleInGroup > 0) {\n show(group)\n totalVisible += visibleInGroup\n } else {\n hide(group)\n }\n }\n\n return totalVisible\n}\n\n/**\n * Search input and empty-state controller for the model menu.\n */\nexport function ModelSearch({ menu, t }: ModelSearchProps): ReactNode {\n const [query, setQuery] = useState('')\n // No sentinel needed: an empty query can never show the empty state, so the\n // first frame is correct before any filter has run.\n const [totalVisible, setTotalVisible] = useState(0)\n const inputRef = useRef<HTMLInputElement>(null)\n const queryRef = useRef('')\n // The nodes this filter currently hides, with the inline display each held\n // before this plugin first touched it (see filterMenuModels).\n const touchedRef = useRef(new Map<HTMLElement, string>())\n\n const runFilter = useCallback((nextQuery: string): void => {\n queryRef.current = nextQuery\n const count = filterMenuModels(menu, nextQuery, touchedRef.current)\n setTotalVisible(count)\n }, [menu])\n\n // Restore every row this filter hid when the box goes away (pane switch,\n // menu close, plugin dispose). There is deliberately NO mount-time pass: the\n // initial query is empty, so the list is already unfiltered \u2014 and a mount\n // pass would run from a PASSIVE effect, i.e. it can land AFTER a keystroke\n // the user already made and silently wipe the filter with it.\n useEffect(() => () => {\n filterMenuModels(menu, '', touchedRef.current)\n }, [menu])\n\n // Keep the filter applied across the official list's OWN re-renders: the\n // directory store can push a fresh catalog (an edit elsewhere, a connection\n // reset) while the menu is open, and the rows React rebuilds come back\n // visible with the query still in the box. Only childList is observed, so\n // this plugin's own display writes can never re-trigger the observer.\n useEffect(() => {\n const observer = new MutationObserver(() => {\n if (queryRef.current.trim().length === 0) return\n runFilter(queryRef.current)\n })\n observer.observe(menu, { childList: true, subtree: true })\n return () => { observer.disconnect() }\n }, [menu, runFilter])\n\n // While a filter is active the plugin owns the list-navigation keys for rows\n // INSIDE the list. Reason: the official roving focus (ModelSelect.tsx:215-226)\n // walks every ref'd row, including the ones this filter hid \u2014 focus() on a\n // display:none node is a no-op, so the official handler would stick on the\n // same item forever (and Home/End/PageUp/PageDown would do nothing at all).\n // A CAPTURE-phase native listener stops the event before the official\n // bubble-phase handler (delegated to the portaled container, an ancestor of\n // this plugin's own root container) ever sees it.\n //\n // It must NOT touch keys aimed at the input: the input lives inside this\n // plugin's own React root, whose container is a DESCENDANT of the menu, so\n // capturing here and stopping would swallow the key before the input's own\n // React handler could run.\n useEffect(() => {\n const onKeyDown = (event: KeyboardEvent): void => {\n if (queryRef.current.trim().length === 0) return\n const key = event.key\n const arrow = key === 'ArrowDown' || key === 'ArrowUp'\n const jump = key === 'Home' || key === 'End' || key === 'PageUp' || key === 'PageDown'\n if (!arrow && !jump) return\n if (event.target === inputRef.current) return\n if (event.target instanceof Node && !menu.contains(event.target)) return\n const options = visibleOptions(menu)\n if (options.length === 0) return\n const current = options.findIndex(option => option === document.activeElement)\n let next: number\n if (key === 'Home' || key === 'PageUp') next = 0\n else if (key === 'End' || key === 'PageDown') next = options.length - 1\n else if (key === 'ArrowDown') next = current === -1 ? 0 : (current + 1) % options.length\n else next = current === -1 ? options.length - 1 : (current - 1 + options.length) % options.length\n event.preventDefault()\n event.stopPropagation()\n options[next]?.focus()\n }\n menu.addEventListener('keydown', onKeyDown, true)\n return () => { menu.removeEventListener('keydown', onKeyDown, true) }\n }, [menu])\n\n const handleKeyDown = (event: ReactKeyboardEvent<HTMLInputElement>): void => {\n const key = event.key\n if (key === 'ArrowDown') {\n event.preventDefault()\n // The official handler runs later in this same dispatch (from an\n // ancestor container) and would step one row PAST the one focused here,\n // so the keystroke is consumed instead.\n event.stopPropagation()\n visibleOptions(menu)[0]?.focus()\n return\n }\n if (query.trim().length > 0 && (key === 'ArrowUp' || key === 'Home' || key === 'PageUp' || key === 'End' || key === 'PageDown')) {\n const options = visibleOptions(menu)\n if (options.length > 0) {\n event.preventDefault()\n event.stopPropagation()\n const last = key === 'ArrowUp' || key === 'End' || key === 'PageDown'\n options[last ? options.length - 1 : 0]?.focus()\n }\n return\n }\n if (key === 'Escape' && query.length > 0) {\n event.preventDefault()\n event.stopPropagation()\n setQuery('')\n runFilter('')\n }\n }\n\n const handleClear = (): void => {\n setQuery('')\n runFilter('')\n inputRef.current?.focus({ preventScroll: true })\n }\n\n const hasEmptyState = totalVisible === 0 && query.trim().length > 0\n\n return createElement(\n 'div',\n { className: 'bre-model-search-box' + (hasEmptyState ? ' has-empty' : ''), 'data-bre-search': '1' },\n createElement(\n 'div',\n { className: 'bre-search-input-wrapper' },\n createElement(\n 'span',\n { className: 'bre-search-icon', 'aria-hidden': 'true' },\n createElement(\n 'svg',\n { viewBox: '0 0 16 16', fill: 'none', stroke: 'currentColor', strokeWidth: '1.5' },\n createElement('circle', { cx: '7', cy: '7', r: '4.5' }),\n createElement('path', { d: 'M10.5 10.5L14 14', strokeLinecap: 'round' }),\n ),\n ),\n createElement('input', {\n ref: inputRef,\n type: 'text',\n className: 'bre-search-input',\n // ARIA has no legal textbox inside `role=\"menu\"`; the box is labelled\n // and fully operable, so `searchbox` is the least-wrong role for it \u2014\n // the same documented-tradeoff shape the slider's hidden cells carry.\n role: 'searchbox',\n placeholder: t('modelSearchPlaceholder'),\n 'aria-label': t('modelSearchPlaceholder'),\n value: query,\n spellCheck: false,\n autoComplete: 'off',\n onChange: (e: { currentTarget: HTMLInputElement }) => {\n const val = e.currentTarget.value\n setQuery(val)\n runFilter(val)\n },\n onKeyDown: handleKeyDown,\n }),\n query.length > 0\n ? createElement(\n 'button',\n {\n type: 'button',\n className: 'bre-search-clear',\n title: t('modelSearchClear'),\n 'aria-label': t('modelSearchClear'),\n onClick: handleClear,\n },\n createElement(\n 'svg',\n { viewBox: '0 0 16 16', fill: 'none', stroke: 'currentColor', strokeWidth: '1.5' },\n createElement('path', { d: 'M4 4L12 12M12 4L4 12', strokeLinecap: 'round' }),\n ),\n )\n : null,\n ),\n hasEmptyState\n ? createElement(\n 'div',\n { className: 'bre-search-empty', role: 'status' },\n t('modelSearchNoMatches'),\n )\n : null,\n )\n}\n", "/**\n * Foreign React roots mounted into the official shell's DOM.\n *\n * This plugin never becomes part of the host's React tree: it renders its own\n * roots into elements it inserts itself. These helpers are the whole of that\n * mechanism, plus the render-failure boundary that keeps a throwing subtree\n * from leaving an empty root behind.\n *\n * @module dsh-model-think-level/client/injection/mount\n */\n\nimport { Component, createElement } from 'react'\nimport type { ErrorInfo, ReactNode } from 'react'\nimport { flushSync } from 'react-dom'\nimport { createRoot, type Root } from 'react-dom/client'\n\n/**\n * The root this plugin scans. The official models page can live anywhere in\n * the settings surface (a hash-classed panel, a dialog, a portal), so guessing\n * a container is fragile \u2014 and the injector is idempotent and cheap, so\n * scanning the whole document is both safe and correct.\n */\nexport function panelRoot(): HTMLElement {\n return document.body\n}\n\n/** One foreign React root this plugin created, with the element holding it. */\nexport interface ForeignMount {\n wrapper: HTMLElement\n root: Root\n}\n\n/** Options of {@link mountReact}. */\nexport interface MountOptions {\n /**\n * Commit the subtree before returning (React's `flushSync`).\n *\n * REQUIRED for roots whose height the official model card measures: a root\n * that commits one frame later makes the card grow across frames, so its\n * anchored position is visible at the wrong height for at least one paint.\n */\n sync?: boolean\n}\n\n/** Create a React root inside `container` and render `children` into it. */\nexport function mountReact(container: HTMLElement, children: ReactNode, options?: MountOptions): ForeignMount {\n const root = createRoot(container)\n if (options?.sync === true) flushSync(() => { root.render(children) })\n else root.render(children)\n return { wrapper: container, root }\n}\n\n/**\n * Unmount a foreign root and detach its wrapper.\n *\n * `root.unmount()` runs the subtree's passive cleanups synchronously, so a\n * component that restored DOM on cleanup (the search filter) has done so by\n * the time the wrapper leaves the document.\n */\nexport function unmountReact(mount: ForeignMount | undefined): void {\n if (mount === undefined) return\n mount.root.unmount()\n mount.wrapper.remove()\n}\n\n/** Render-failure boundary: surfaces the cause instead of an empty root. */\nexport class EffortBoundary extends Component<{ children?: ReactNode; fallbackText: string }, { error: string | null }> {\n state: { error: string | null } = { error: null }\n static getDerivedStateFromError(error: unknown): { error: string } {\n return { error: error instanceof Error ? error.message : String(error) }\n }\n override componentDidCatch(error: unknown, info: ErrorInfo): void {\n console.error('[bre] editor render failed:', error, info.componentStack)\n }\n override render(): ReactNode {\n if (this.state.error !== null) {\n return createElement(\n 'div',\n { style: { color: '#c00', fontSize: '11px', whiteSpace: 'pre-wrap', padding: '6px' } },\n `${this.props.fallbackText}: ${this.state.error}`,\n )\n }\n return this.props.children\n }\n}\n", "/**\n * Shape guards and settings-dict readers shared by the host and browser\n * halves. Pure data logic only: esbuild inlines this module into both\n * bundles, so it must never import a harness package or touch DOM/node APIs.\n *\n * @module dsh-model-think-level/shared\n */\n\nimport type { RouteFacts } from './knowledge.js'\n\n/** Whether a value is a plain-ish object (not null, not an array). */\nexport const isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value)\n\n/**\n * Whether a settings refusal reads like \"this kernel does not know the newer\n * compat keys\". The host autofill and the browser write path each downgrade\n * once on this signal, so they must answer identically.\n */\nexport function looksLikeCompatRefusal(message: string): boolean {\n const msg = message.toLowerCase()\n return msg.includes('compat')\n || msg.includes('thinkingtokenbudget')\n || msg.includes('vllmpriority')\n || msg.includes('supportsmaxoutput')\n}\n\n/** The models array of one route in a providers dict, as records. */\nexport function modelsOf(providers: unknown, route: string): Record<string, unknown>[] {\n if (!isRecord(providers)) return []\n const profile = providers[route]\n if (!isRecord(profile) || !Array.isArray(profile['models'])) return []\n return profile['models'].filter(isRecord)\n}\n\n/** One provider profile's route facts, as suggestion inference reads them. */\nexport function routeFactsOf(providers: unknown, route: string): RouteFacts {\n if (!isRecord(providers)) return {}\n const profile = providers[route]\n if (!isRecord(profile)) return {}\n const api = typeof profile['api'] === 'string' ? profile['api'] : undefined\n const baseURL = typeof profile['baseURL'] === 'string' ? profile['baseURL'] : undefined\n const displayName = typeof profile['displayName'] === 'string' ? profile['displayName'] : undefined\n return { api, baseURL, displayName }\n}\n", "/**\n * Endpoint reasoning/modality-signal detection over a provider's RAW /models\n * listing.\n *\n * The sanctioned `llm.discoverModels` wire call strips capability signals\n * host-side, so the host half proxies the raw listing through a same-origin\n * route; this module is the pure half both ends share. Detection approach\n * follows the format survey proven by dsh-reasoning-efforts (MIT): listings\n * disclose capabilities under several field conventions, and \"no signal\"\n * must stay distinct from \"explicitly unsupported\" -- for modalities just as\n * for reasoning.\n *\n * @module dsh-model-think-level/detection\n */\n\n/** One raw /models listing entry: pass through unknown fields untouched. */\nexport type RawModelEntry = Record<string, unknown>\n\n/** Tri-state endpoint signal: true reasons, false explicitly does not, 'unknown' = the listing never said. */\nexport type EndpointReasoning = boolean | 'unknown'\n\n/**\n * What the endpoint disclosed about one model. Every part is independent and\n * may be absent: an absent part means the listing never spoke about it (or\n * the probe never ran) -- silence is not refusal.\n */\nexport interface EndpointSignal {\n reasoning: EndpointReasoning\n /** The listing field that produced the reasoning signal, null when none did. */\n source: string | null\n /**\n * The listing's input-modality disclosure, lowercased, when it named a\n * field we recognize. Members outside the harness's image/text world\n * (audio, video, pdf/file) are passed through verbatim -- the knowledge\n * layer owns the intersection with the core vocabulary.\n */\n input?: readonly string[]\n /** Context length the listing disclosed, when it carries one. */\n contextLength?: number\n}\n\nimport { isRecord } from './shared.js'\n\nconst hasBoolean = (entry: Record<string, unknown>, key: string): boolean =>\n typeof entry[key] === 'boolean'\n\n/** Find the raw listing entry for one model id. */\nexport function findListingEntry(\n listing: unknown,\n modelId: string,\n): RawModelEntry | undefined {\n if (!Array.isArray(listing)) return undefined\n for (const entry of listing) {\n if (isRecord(entry) && entry['id'] === modelId) return entry\n }\n return undefined\n}\n\n/**\n * Read one listing entry's REASONING signal. Field conventions covered:\n * - `supported_features: [..., \"reasoning\"]` (rich feature arrays)\n * - `supported_parameters: [..., \"reasoning\" | \"include_reasoning\" |\n * \"reasoning_effort\"]` (OpenRouter-style)\n * - `supports_reasoning` / `supportsReasoning` (boolean flags)\n * - `can_reason` / `reasoning` (boolean flags)\n * - top-level `reasoning_effort` / `supports_reasoning_effort`\n *\n * A bare `{id, object, owned_by}` listing yields 'unknown' -- that is NOT\n * \"unsupported\": the endpoint simply never said.\n */\nexport function analyzeListingEntry(entry: unknown): EndpointSignal {\n if (!isRecord(entry)) return { reasoning: 'unknown', source: null }\n const features = entry['supported_features']\n if (Array.isArray(features) && features.includes('reasoning')) {\n return { ...signalParts(entry), reasoning: true, source: 'supported_features' }\n }\n const parameters = entry['supported_parameters']\n if (\n Array.isArray(parameters) &&\n (parameters.includes('reasoning') ||\n parameters.includes('include_reasoning') ||\n parameters.includes('reasoning_effort'))\n ) {\n return { ...signalParts(entry), reasoning: true, source: 'supported_parameters' }\n }\n if (hasBoolean(entry, 'supports_reasoning')) {\n return { ...signalParts(entry), reasoning: entry['supports_reasoning'] as boolean, source: 'supports_reasoning' }\n }\n if (hasBoolean(entry, 'supportsReasoning')) {\n return { ...signalParts(entry), reasoning: entry['supportsReasoning'] as boolean, source: 'supportsReasoning' }\n }\n if (hasBoolean(entry, 'can_reason')) {\n return { ...signalParts(entry), reasoning: entry['can_reason'] as boolean, source: 'can_reason' }\n }\n if (hasBoolean(entry, 'reasoning')) {\n return { ...signalParts(entry), reasoning: entry['reasoning'] as boolean, source: 'reasoning' }\n }\n\n // A present, meaningful effort field signals support; a null placeholder \u2014\n // or an empty string, which some gateways emit as a filler \u2014 says nothing\n // and must stay 'unknown' (tri-state discipline).\n const effortField = entry['reasoning_effort']\n if (\n (effortField !== undefined && effortField !== null && !(typeof effortField === 'string' && effortField.trim().length === 0)) ||\n entry['supports_reasoning_effort'] === true\n ) {\n return { ...signalParts(entry), reasoning: true, source: 'reasoning_effort' }\n }\n // The nested capabilities object is the Anthropic Messages listing shape \u2014\n // the shape the kernel's own discovery reports (this probe\n // has read it since the 0.3.5 line): thinking.supported and effort.supported\n // are the two disclosures that say a model reasons. An explicit false is a\n // refusal, not silence.\n const capabilities = isRecord(entry['capabilities']) ? entry['capabilities'] : undefined\n if (capabilities !== undefined) {\n const thinking = capabilities['thinking']\n if (isRecord(thinking) && typeof thinking['supported'] === 'boolean') {\n return {\n ...signalParts(entry),\n reasoning: thinking['supported'] as boolean,\n source: 'capabilities.thinking.supported',\n }\n }\n const effort = capabilities['effort']\n if (isRecord(effort) && typeof effort['supported'] === 'boolean') {\n return {\n ...signalParts(entry),\n reasoning: effort['supported'] as boolean,\n source: 'capabilities.effort.supported',\n }\n }\n }\n return { ...signalParts(entry), reasoning: 'unknown', source: null }\n}\n\n/**\n * Read one listing entry's MODALITY + CAPACITY disclosures. Field conventions\n * covered:\n * - `architecture.input_modalities: [\"text\",\"image\",...]` (OpenRouter and\n * every aggregator that mirrors it)\n * - top-level `input_modalities: [...]` (flattened copies)\n * - `modalities: { input: [...] }` (models.dev style)\n * - `supported_features: [..., \"vision\"]` /\n * `capabilities: [..., \"vision\"]` (feature arrays)\n * - `supports_vision` / `supportsVision` /\n * `supports_images` / `supportsImages` (boolean flags)\n * - `context_length` / `top_provider.context_length` (OpenRouter capacity)\n *\n * Absence stays absent: a listing that names no recognized field yields no\n * `input` part at all -- never an implicit \"text only\".\n */\nfunction signalParts(entry: Record<string, unknown>): Pick<EndpointSignal, 'input' | 'contextLength'> {\n const parts: { input?: readonly string[]; contextLength?: number } = {}\n\n const rawList = (value: unknown): readonly string[] | undefined => {\n if (!Array.isArray(value)) return undefined\n const members = value.filter((item): item is string => typeof item === 'string')\n return members.length > 0 ? members.map(member => member.toLowerCase()) : []\n }\n\n const architecture = isRecord(entry['architecture']) ? entry['architecture'] : undefined\n let input = rawList(architecture?.['input_modalities'])\n if (input === undefined) input = rawList(entry['input_modalities'])\n if (input === undefined) {\n const modalities = isRecord(entry['modalities']) ? entry['modalities'] : undefined\n input = rawList(modalities?.['input'])\n }\n if (input === undefined) {\n const capabilityArray = entry['capabilities']\n const visionIn = (list: unknown): boolean =>\n Array.isArray(list) && list.some(item => typeof item === 'string' && item.toLowerCase() === 'vision')\n if (visionIn(entry['supported_features']) || visionIn(capabilityArray)) {\n input = ['image']\n } else if (isRecord(capabilityArray)) {\n // The Anthropic Messages listing nests modality booleans as\n // capabilities.<modality>.supported \u2014 the shape the kernel discovery\n // added to the interrogations. An explicit false answers\n // with the text floor (refusal, not silence).\n const members: string[] = []\n for (const key of ['image_input', 'video_input', 'pdf_input', 'file_input']) {\n const slot = isRecord(capabilityArray[key]) ? capabilityArray[key] : undefined\n if (slot === undefined || typeof slot['supported'] !== 'boolean') continue\n const member = key.replace(/_input$/, '')\n if (slot['supported'] === true) members.push(member)\n else if (members.length === 0) members.push('text')\n }\n if (members.length > 0) input = members\n }\n }\n if (input === undefined) {\n if (hasBoolean(entry, 'supports_vision')) input = entry['supports_vision'] === true ? ['image'] : []\n else if (hasBoolean(entry, 'supportsVision')) input = entry['supportsVision'] === true ? ['image'] : []\n // `supports_images` is the flag CodeBuddy-derived gateways publish (their\n // model listings carry `supports_images` and none of the spellings above),\n // so without it such an endpoint reads as silent and the knowledge layer\n // wins by default -- which under-declares modalities whenever the catalog\n // entry it matched happens to be text-only. The two spellings are read as\n // ONE field, never as two answers: they are the same disclosure, and a\n // gateway carrying the pair would otherwise let whichever arm this chain\n // reaches first veto the other -- the snake_case arm always does. Only\n // `true` declares image input; a pair refusing in both spellings still\n // answers with the text floor below, exactly like the siblings above.\n else if (hasBoolean(entry, 'supports_images') || hasBoolean(entry, 'supportsImages')) {\n input = entry['supports_images'] === true || entry['supportsImages'] === true ? ['image'] : []\n }\n }\n // An empty disclosure still ANSWERED: an explicit refusal (supports_vision:\n // false, supports_images: false, or a gateway declaring no members at all)\n // maps to the text floor every supported protocol carries -- so the fusion\n // layer can strip an image claim instead of mistaking refusal for silence.\n if (input !== undefined) parts.input = input.length === 0 ? ['text'] : input\n\n // Capacity conventions: top-level, OpenRouter's top_provider, the Anthropic\n // native max_input_tokens (a 0 placeholder means 'not disclosed' \u2014 never a\n // real zero), and the models-dev limit.context.\n const length = entry['context_length']\n if (typeof length === 'number' && Number.isFinite(length) && length > 0) {\n parts.contextLength = length\n } else {\n const topProvider = isRecord(entry['top_provider']) ? entry['top_provider'] : undefined\n const nested = topProvider?.['context_length']\n if (typeof nested === 'number' && Number.isFinite(nested) && nested > 0) {\n parts.contextLength = nested\n } else {\n const maxInput = entry['max_input_tokens']\n if (typeof maxInput === 'number' && Number.isFinite(maxInput) && maxInput > 0) {\n parts.contextLength = maxInput\n } else {\n const limit = isRecord(entry['limit']) ? entry['limit'] : undefined\n const limitContext = limit?.['context']\n if (typeof limitContext === 'number' && Number.isFinite(limitContext) && limitContext > 0) {\n parts.contextLength = limitContext\n }\n }\n }\n }\n\n return parts\n}\n\n/**\n * Detect one model's endpoint signal from a raw listing.\n * @returns the signal, plus whether the model appeared in the listing at all.\n */\nexport function detectModelSignal(\n listing: unknown,\n modelId: string,\n): { found: boolean; signal: EndpointSignal } {\n const entry = findListingEntry(listing, modelId)\n if (entry === undefined) return { found: false, signal: { reasoning: 'unknown', source: null } }\n return { found: true, signal: analyzeListingEntry(entry) }\n}\n", "/**\n * Client-side declaration write seam over 'settings.mutate', plus the\n * knowledge-base / protocol suggestions. Pure logic --\n * no React, no DOM -- so it stays unit-testable in isolation.\n *\n * @module dsh-model-think-level/client/ops\n */\n\nimport {\n suggestEfforts,\n type CompatSuggestion,\n type InputModalities,\n type ReasoningEfforts,\n} from '../knowledge.js'\nimport { AUTOFILL_MARKER, DEFAULT_EFFORT_FIELD, INPUT_UNSET_MARKER, PI_AI_NS, PROBE_PATH, UNSET_MARKER } from '../constants.js'\nimport { detectModelSignal, type EndpointSignal } from '../detection.js'\nimport { isRecord, looksLikeCompatRefusal, routeFactsOf } from '../shared.js'\nimport type { SettingsPathOpView } from '@deepseek-ai/dsh-api-remotes/client'\nimport type {\n DefaultEffortIntent,\n EffortEditorApi,\n EffortWriteIntent,\n HeldWrite,\n InputIntent,\n RemoteApi,\n SettingsJoin,\n SettingsNamespaceView,\n} from './types.js'\n\n/** The user-layer providers dict of the pi-ai namespace, as records. */\nexport function providersOf(namespace: SettingsNamespaceView | undefined): Record<string, Record<string, unknown>> {\n const value = namespace?.value as { providers?: unknown } | undefined\n const providers = value?.providers\n if (typeof providers !== 'object' || providers === null || Array.isArray(providers)) return {}\n return Object.fromEntries(\n Object.entries(providers as Record<string, unknown>).filter(([, profile]) =>\n typeof profile === 'object' && profile !== null && !Array.isArray(profile)),\n ) as Record<string, Record<string, unknown>>\n}\n\n/** The providers dict of one settings layer (user or base), as a record. */\nfunction providersOfLayer(layer: unknown): Record<string, unknown> | undefined {\n if (!isRecord(layer)) return undefined\n const providers = layer['providers']\n return isRecord(providers) ? providers : undefined\n}\n\n/**\n * The `models` value a write must be based on, read from the RAW layers: the\n * user section when it declares one, else the composition base. That is the\n * official card's own inheritance rule (`ProviderEditor` seeds its draft from\n * `namespace.user` and its fallback from `namespace.base`).\n *\n * Never the resolved `value`: rebuilding from it materializes every schema\n * default -- `input: []`, an empty `compat`, an absent `models` -- into the\n * stored document, so the user's file grows keys they never chose and a later\n * schema change can no longer reach the row.\n * @param namespace - the namespace view carrying the layers.\n * @param route - the provider route key.\n * @returns the raw `models` value, or undefined when neither layer declares one.\n */\nexport function baselineModelsOf(namespace: SettingsNamespaceView | undefined, route: string): unknown {\n for (const layer of [namespace?.user, namespace?.base]) {\n const profile = providersOfLayer(layer)?.[route]\n if (isRecord(profile) && profile['models'] !== undefined) return profile['models']\n }\n return undefined\n}\n\n/**\n * The RAW USER layer's providers dict, or undefined when the section declares\n * none. The browser half's running auto-fill builds its patch from this --\n * never from the resolved `value`, which would materialize schema defaults\n * into the stored document (the same rule the host's boot pass follows).\n * @param namespace - the namespace view.\n * @returns the user section's providers dict, when it has one.\n */\nexport function userProvidersOf(namespace: SettingsNamespaceView | undefined): Record<string, unknown> | undefined {\n return providersOfLayer(namespace?.user)\n}\n\n/** The reasoningEfforts of one model in a route's models. */\nexport function effortsOf(models: Record<string, unknown>[], modelId: string): false | ReasoningEfforts | undefined {\n const entry = models.find(model => model['id'] === modelId)\n if (entry === undefined) return undefined\n const efforts = entry['reasoningEfforts']\n if (efforts === false) return false\n if (typeof efforts === 'object' && efforts !== null && !Array.isArray(efforts)) {\n return efforts as ReasoningEfforts\n }\n return undefined\n}\n\n/** The input-modality declaration of one model in a route's models. Empty\n * mirrors undefined -- the resolved settings layer materializes absent arrays\n * as [] (schemastery), and llm-pi-ai's own declaredInput reads that as \"no\n * answer here\": an empty list must stay inheritable, never a phantom\n * text-only declaration. */\nexport function inputOf(models: Record<string, unknown>[], modelId: string): InputModalities | undefined {\n const entry = models.find(model => model['id'] === modelId)\n const input = entry?.['input']\n if (!Array.isArray(input)) return undefined\n const members = input.filter((member): member is string => typeof member === 'string')\n // Members originate from the core schema's vocabulary; the cast only\n // recovers that guarantee the index signature erased.\n return (members.length > 0 ? members : undefined) as InputModalities | undefined\n}\n\n/** The stored compat block of one model in a route's models (unvalidated passthrough). */\nexport function compatOf(models: Record<string, unknown>[], modelId: string): CompatSuggestion | undefined {\n const entry = models.find(model => model['id'] === modelId)\n const compat = entry?.['compat']\n if (!isRecord(compat)) return undefined\n return { ...(compat as CompatSuggestion) }\n}\n\n/** The display name of one model in a route's models. */\nexport function nameOf(models: Record<string, unknown>[], modelId: string): string | undefined {\n const entry = models.find(model => model['id'] === modelId)\n const name = entry?.['name']\n return typeof name === 'string' && name.length > 0 ? name : undefined\n}\n\n/**\n * The stored per-model default-effort pick of one model in a route's models,\n * or undefined when the user has not picked one. A non-string value degrades\n * to undefined: the field is plugin-owned, and a hostile or stale document\n * must degrade to \"no default\", never poison the memory chain.\n */\nexport function defaultEffortOf(models: Record<string, unknown>[], modelId: string): string | undefined {\n const entry = models.find(model => model['id'] === modelId)\n const effort = entry?.[DEFAULT_EFFORT_FIELD]\n return typeof effort === 'string' && effort.length > 0 ? effort : undefined\n}\n\n/**\n * Ask the host's same-origin probe route for this model's raw-listing facts\n * (reasoning signal, modality disclosure, context length). Any failure --\n * route absent, endpoint unreachable, listing shape unexpected -- degrades to\n * an unanswered signal (\"asked, no answer\"), never to a thrown error:\n * suggestions must not break because the endpoint would not talk.\n */\nasync function probeEndpoint(route: string, modelId: string): Promise<EndpointSignal> {\n try {\n const response = await fetch(`${PROBE_PATH}?route=${encodeURIComponent(route)}`, { method: 'GET' })\n if (!response.ok) return { reasoning: 'unknown', source: null }\n const body = (await response.json()) as { ok?: boolean; data?: unknown }\n if (!body?.ok) return { reasoning: 'unknown', source: null }\n return detectModelSignal(body.data, modelId).signal\n } catch {\n return { reasoning: 'unknown', source: null }\n }\n}\n\n/**\n * Build the write seam over a settings Remote face.\n * @param api - the settings Remote methods.\n * @param describe - how to obtain the pi-ai namespace join (injectable for tests).\n * @param stage - sink for declarations staged against a route that is not\n * saved yet; the injector owns the store and flushes it once the route exists.\n */\nexport function createEditorApi(\n api: RemoteApi,\n describe: () => Promise<SettingsJoin> = () => describeNamespace(api),\n stage?: (\n route: string,\n modelId: string,\n efforts: EffortWriteIntent,\n compat?: CompatSuggestion,\n input?: InputModalities | null,\n defaultEffort?: string | null,\n ) => void,\n /**\n * Present only on a seam an ON-SCREEN editor drives: while the official card\n * is open its own frozen revision baseline makes any write here break the\n * user's next save in that card, so the seam queues the full intent instead\n * of committing it. The injector replays it once the card is gone.\n */\n hold?: (\n route: string,\n modelId: string,\n write: HeldWrite,\n ) => void,\n /**\n * Present only on the editor's own seam: forget everything this row reported.\n * The editor's Reset calls it, because the ledger it wrote to outlives the\n * React state \u2014 without this a Reset followed by the official Save would\n * still write the discarded edits.\n */\n withdraw?: (route: string, modelId: string) => void,\n /**\n * Whether this row's route is still unsaved (the create card's draft route,\n * or a typed-but-unsaved row). The injector knows this per row, so it is told\n * rather than re-derived here: deriving it would cost a wire describe on\n * every keystroke the editor reports.\n */\n stagedOf?: () => boolean,\n): EffortEditorApi {\n return {\n async suggest(route, modelId, name, stagedFacts) {\n const providers = providersOf((await describe()).namespace)\n const stored = routeFactsOf(providers, route)\n // A saved route's stored profile is authoritative; the staged facts only\n // fill what the settings document does not hold yet (the create card).\n const facts = {\n api: stored.api ?? stagedFacts?.api,\n baseURL: stored.baseURL ?? stagedFacts?.baseURL,\n displayName: name ?? stored.displayName,\n }\n // L1 first: the endpoint's own word about this model. The fusion in\n // suggestEfforts keeps wire values knowledge-base-only. A route the\n // settings document does not hold (a create card) cannot be probed --\n // the host resolves routes from settings -- so skip the doomed round\n // trip and leave the signal absent until the route is saved.\n const endpoint = providers[route] === undefined ? undefined : await probeEndpoint(route, modelId)\n const suggestion = suggestEfforts(modelId, facts, endpoint)\n if (suggestion.efforts === undefined) return { ok: false, error: 'no-suggestion' }\n return {\n ok: true,\n suggestion: {\n efforts: suggestion.efforts,\n // The host autofill writes the suggestion's compat block beside the\n // declaration; the browser seam must write the SAME bytes so one\n // suggestion never produces two different documents. (thinkingFormat\n // is what makes off/thinking dispatch work on deepseek/qwen/zai\n // endpoints.)\n ...(suggestion.compat === undefined ? {} : { compat: suggestion.compat }),\n ...(suggestion.defaultEffort === undefined ? {} : { defaultEffort: suggestion.defaultEffort }),\n ...(suggestion.defaultEffort === undefined ? {} : { defaultEffort: suggestion.defaultEffort }),\n ...(suggestion.input === undefined ? {} : { input: suggestion.input }),\n ...(suggestion.inputSource === undefined ? {} : { inputSource: suggestion.inputSource }),\n ...(suggestion.contextWindow === undefined ? {} : { contextWindow: suggestion.contextWindow }),\n ...(suggestion.maxTokens === undefined ? {} : { maxTokens: suggestion.maxTokens }),\n matched: suggestion.matched,\n source: suggestion.source,\n confidence: suggestion.confidence,\n ...(suggestion.endpoint === undefined ? {} : { endpoint: suggestion.endpoint }),\n },\n }\n },\n commit(route, modelId, write) {\n // The editor reports the row's whole pending edit, and whether the route\n // exists in the document yet decides the ledger: an unsaved route (the\n // create card, or a typed-but-unsaved row) stages, a saved one queues\n // until the official card's Save. Both are synchronous and memory-only:\n // the change must register before the user can reach that Save button.\n if (stagedOf?.() === true) {\n stage?.(route, modelId, write.efforts, write.compat, write.input, write.defaultEffort)\n return\n }\n hold?.(route, modelId, {\n efforts: write.efforts,\n ...(write.compat === undefined ? {} : { compat: write.compat }),\n ...(write.input === undefined ? {} : { input: write.input }),\n ...(write.clearCompatKeys === undefined ? {} : { clearCompatKeys: write.clearCompatKeys }),\n ...(write.defaultEffort === undefined ? {} : { defaultEffort: write.defaultEffort }),\n })\n },\n withdraw(route, modelId) {\n withdraw?.(route, modelId)\n },\n stageEfforts(route, modelId, efforts, compat, input, defaultEffort) {\n stage?.(route, modelId, efforts, compat, input, defaultEffort)\n },\n async writeEfforts(route, modelId, rawEfforts, compat, input, clearCompatKeys, defaultEffort) {\n // While an editor owns the document (the official editing card is open)\n // commit nothing: that card froze its own revision baseline, so any\n // write from here makes the user's NEXT save in it fail with\n // `settings/conflict` -- which reads exactly like \"I configured it,\n // saved it, and it is gone\". Queue the full intent verbatim instead; the\n // injector replays it once the card is gone. A replay seam passes no\n // holder, so the same call commits then.\n if (hold !== undefined) {\n hold(route, modelId, {\n efforts: rawEfforts,\n ...(compat === undefined ? {} : { compat }),\n ...(input === undefined ? {} : { input }),\n ...(clearCompatKeys === undefined ? {} : { clearCompatKeys }),\n ...(defaultEffort === undefined ? {} : { defaultEffort }),\n })\n return { ok: true, staged: true }\n }\n // 'keep' means the ladder part of the edit is a no-op: a modality-only\n // edit must never fall through to the unset branch (which would stamp\n // the durable marker onto a never-declared ladder and silence host\n // auto-fill for it forever).\n const results = await writeModelRows(api, route, [{\n modelId,\n efforts: rawEfforts,\n ...(compat === undefined ? {} : { compat }),\n ...(input === undefined ? {} : { input }),\n ...(clearCompatKeys === undefined ? {} : { clearCompatKeys }),\n ...(defaultEffort === undefined ? {} : { defaultEffort }),\n }], describe)\n const result = results[0] ?? { ok: false, error: 'no-result' }\n if (result.ok) return { ok: true }\n // A vanished row is reported by code so the callers can drop it; the\n // wire itself says 'model-not-found', which is what this error means.\n if (result.modelNotFound === true) return { ok: false, error: 'model-not-found' }\n return { ok: false, error: result.error ?? 'conflict' }\n },\n }\n}\n\n/**\n * One row's intent inside a batch write. `touch` distinguishes \"this edit never\n * carried the ladder\" (leave it alone) from \"unset it durably\"; a modality\n * `undefined` likewise leaves the stored declaration untouched.\n */\nexport interface RowIntent {\n modelId: string\n /** The ladder part; 'keep' means this edit never carried it. */\n efforts: EffortWriteIntent\n compat?: CompatSuggestion\n input?: InputIntent\n clearCompatKeys?: readonly string[]\n defaultEffort?: DefaultEffortIntent\n}\n\n/** The outcome of one batch write, as the caller's ledgers need it. */\nexport interface RowWriteResult {\n ok: boolean\n /**\n * True when the failure is \"this row is not (or no longer) in the document\":\n * the caller drops the intent instead of retrying it forever.\n */\n modelNotFound?: boolean\n /**\n * True when the write was abandoned before reaching the wire because an\n * official card opened during the read. Not a failure: the caller keeps the\n * intent and does not back off.\n */\n aborted?: boolean\n /** The refusal message, when `ok` is false and it is a real failure. */\n error?: string\n}\n\n/**\n * Apply every row of ONE route in a single rebuild + single mutate.\n *\n * This is the write path's core: `writeEfforts` is the one-row case of it, and\n * the injector's ledgers call it with every row they hold for that route. One\n * describe and one `mutate` per route rather than per row -- the official\n * document is a whole-array set, so batching costs no extra semantics.\n *\n * Failing rows are the caller's business: this returns one result per row, all\n * derived from the same response, so a route that refuses keeps every one of\n * its intents for the next pass (never a partial landing).\n * @param api - the settings Remote.\n * @param route - the provider route whose models array to rebuild.\n * @param rows - the intents to apply, in order.\n * @param describe - the read seam; a conflict retry forces the wire read.\n * @param abort - checked after the read and before the mutate: an official\n * card may have opened while we were reading, and writing then would land\n * behind that card's frozen revision baseline (issue #7). An aborted write\n * returns `{ ok: false, aborted: true }` for every row and consumes nothing.\n * @returns one result per row, in the same order.\n */\nexport async function writeModelRows(\n api: RemoteApi,\n route: string,\n rows: readonly RowIntent[],\n describe: () => Promise<SettingsJoin> = () => describeNamespace(api),\n abort?: () => boolean,\n): Promise<RowWriteResult[]> {\n if (rows.length === 0) return []\n // Retry once on a revision conflict: a concurrent writer (this plugin's own\n // autofill, or the official page) moved the namespace between our describe\n // and mutate. Re-reading and retrying with the fresh revision is the same\n // recovery the official settings form uses.\n // Downgrade once for older kernels: a `settings/rejected` refusal naming an\n // unknown compat key means the kernel predates the newer compat schema.\n // Stripping the newer-only keys and retrying keeps one artifact writable\n // across kernel lines with no version sniffing.\n let downgraded = false\n for (let attempt = 0; attempt < 3; attempt++) {\n try {\n // Attempt 0 reads through the injected seam (the injector seeds it with\n // the scan's join). A conflict retry MUST go to the wire: the scope\n // snapshot behind that seam folds a fresh view in asynchronously, so it\n // would hand back the very revision the write just failed against.\n const join = attempt === 0 ? await describe() : await describeNamespace(api, { fresh: true })\n if (join.namespace === undefined) return rows.map(() => ({ ok: false, error: 'no-namespace' }))\n // The write rebuilds the models array verbatim, from the RAW layers (the\n // user section, else the composition base) -- never the resolved value.\n // A row this code cannot represent must refuse the write rather than\n // silently drop the row.\n const rawModels = baselineModelsOf(join.namespace, route)\n if (!Array.isArray(rawModels)) return rows.map(() => ({ ok: false, modelNotFound: true }))\n if (!rawModels.every(isRecord)) return rows.map(() => ({ ok: false, error: 'invalid-models' }))\n const models = rawModels as Record<string, unknown>[]\n // Every row this write intends must still exist: a vanished row is\n // dropped by the caller, and mixing it into a rebuild would rewrite the\n // array around a gap.\n const nextModels = models.map(model => model)\n const missing: string[] = []\n for (const row of rows) {\n const index = nextModels.findIndex(model => model['id'] === row.modelId)\n if (index < 0) {\n missing.push(row.modelId)\n continue\n }\n nextModels[index] = mergeRowIntent(nextModels[index]!, row, downgraded ? undefined : row.compat)\n }\n if (missing.length > 0) {\n return rows.map(row => (missing.includes(row.modelId)\n ? { ok: false, modelNotFound: true }\n : { ok: false, error: 'model-not-found' }))\n }\n // The card fence, re-checked at the last possible moment: a card that\n // opened during the read must not have this write land behind its frozen\n // baseline. Aborting consumes nothing -- every row stays for a retry.\n if (abort?.() === true) return rows.map(() => ({ ok: false, aborted: true }))\n const response = await api.settings.mutate(\n PI_AI_NS,\n // The rebuilt models array is JSON-shaped by construction (a settings\n // document is JSON); the value is JsonValue on this baseline, so the\n // set op asserts once instead of rebuilding the row's type.\n [{ op: 'set', path: ['providers', route, 'models'], value: nextModels } as unknown as SettingsPathOpView],\n join.namespace.revision,\n )\n if (!response.ok) {\n // The stable wire code, not the message prose: 'settings/conflict'\n // (the Typert refusal code) means a concurrent writer moved the\n // namespace between our describe and mutate.\n if (attempt === 0 && response.error.code === 'settings/conflict') continue\n const looksCompat = response.error.code === 'settings/rejected' && looksLikeCompatRefusal(response.error.message)\n if (!downgraded && looksCompat && rows.some(row => row.compat !== undefined)) {\n downgraded = true\n continue\n }\n return rows.map(() => ({ ok: false, error: response.error.message }))\n }\n return rows.map(() => ({ ok: true }))\n } catch (error) {\n return rows.map(() => ({ ok: false, error: error instanceof Error ? error.message : String(error) }))\n }\n }\n return rows.map(() => ({ ok: false, error: 'conflict' }))\n}\n\n/**\n * Rewrite ONE model row in place: the ladder, its compat block, the modality\n * declaration, and the default-effort pick.\n *\n * Split out of the write loop so a batch shares exactly this row surgery --\n * including the provenance rules below, which a batch must not be able to\n * drift away from.\n * @param model - the row as the document holds it.\n * @param row - the intent to apply.\n * @param compat - the compat block to write, or undefined to leave it.\n * @returns the rebuilt row.\n */\nfunction mergeRowIntent(\n model: Record<string, unknown>,\n row: RowIntent,\n compat: CompatSuggestion | undefined,\n): Record<string, unknown> {\n const copy = { ...model }\n // 'keep' means the ladder part of the edit is a no-op: a modality-only edit\n // must never fall through to the unset branch (which would stamp the durable\n // marker onto a never-declared ladder and silence host auto-fill forever).\n const touch = row.efforts !== 'keep'\n const efforts = row.efforts === 'keep' ? undefined : row.efforts\n if (!touch) {\n // Ladder untouched by this edit: no delete, no marker.\n } else if (efforts === undefined) {\n // Unset the declaration durably: the marker records the absence as a\n // decision, so the host's auto-fill never reads it back as a gap to fill --\n // not now, and not after the next restart.\n delete copy['reasoningEfforts']\n copy[UNSET_MARKER] = true\n } else {\n // A real declaration supersedes any earlier unset marker -- and retires the\n // autofill provenance marker: these bytes are now a user decision, so a\n // later staging must not be allowed to override them as if they were still\n // the knowledge base's suggestion (they may be byte-identical on purpose).\n delete copy[UNSET_MARKER]\n delete copy[AUTOFILL_MARKER]\n if (efforts === false) {\n copy['reasoningEfforts'] = false\n } else {\n copy['reasoningEfforts'] = { ...efforts }\n // The compat belongs to the declaration: merge it over the stored block so\n // hand-tuned keys (incl. newer-only fields the suggestion never names)\n // survive a declaration edit. Only the keys THIS edit owns and left empty\n // are deleted -- clearing the UI's own picker has to mean unset, while a\n // field the editor never showed stays untouched.\n const stored = isRecord(model['compat']) ? (model['compat'] as Record<string, unknown>) : {}\n const merged: Record<string, unknown> = { ...stored, ...(compat ?? {}) }\n for (const key of row.clearCompatKeys ?? []) delete merged[key]\n if (Object.keys(stored).length > 0 || Object.keys(merged).length > 0) {\n if (Object.keys(merged).length === 0) delete copy['compat']\n else copy['compat'] = merged\n }\n }\n }\n // The modality part rides the same mutate. An omitted intent touches nothing\n // (a staged flush must not strip declarations it never carried); null unsets\n // durably through the marker.\n if (row.input !== undefined) {\n if (row.input === null) {\n delete copy['input']\n copy[INPUT_UNSET_MARKER] = true\n } else {\n delete copy[INPUT_UNSET_MARKER]\n copy['input'] = [...row.input]\n }\n }\n // The per-model default-effort pick rides the same mutate: omitted =\n // untouched (a ladder-only edit never clears the pick), null = cleared (back\n // to the memory chain), a string = the pick.\n if (row.defaultEffort !== undefined) {\n if (row.defaultEffort === null) delete copy[DEFAULT_EFFORT_FIELD]\n else copy[DEFAULT_EFFORT_FIELD] = row.defaultEffort\n }\n return copy\n}\n\n/** One result of writing a provider's request headers. */\nexport type ProviderHeadersReply = { ok: true } | { ok: false; error: string }\n\n/**\n * The RAW layer's `headers` dict for one route, as the editor seeds its draft.\n *\n * Reads through {@link baselineModelsOf}'s rule (the user section when it\n * declares one, else the composition base) for the same reason: the resolved\n * `value` materializes schema defaults, and a draft seeded from it would write\n * those defaults back on the first save. The run of the layers is \"what the\n * user sees is what the user edits\", which is the inheritance the official\n * ProviderEditor applies to its own fields.\n * @param namespace - the namespace view carrying the layers.\n * @param route - the provider route key.\n * @returns the stored header pairs, or undefined when no layer declares any.\n */\nexport function providerHeadersOf(\n namespace: SettingsNamespaceView | undefined,\n route: string,\n): Record<string, string> | undefined {\n for (const layer of [namespace?.user, namespace?.base]) {\n const profile = providersOfLayer(layer)?.[route]\n if (!isRecord(profile)) continue\n const headers = profile['headers']\n if (headers === undefined) continue\n if (!isRecord(headers)) return undefined\n const pairs: Record<string, string> = {}\n for (const [name, value] of Object.entries(headers)) {\n if (typeof value === 'string') pairs[name] = value\n }\n // An explicitly empty dict is the user's \"send no extra headers\": reported\n // as an empty dict rather than absent, so the editor shows a cleared state\n // instead of a field the next save would silently refill from below.\n return pairs\n }\n return undefined\n}\n\n/**\n * Write ONE route's request headers through the live settings seam.\n *\n * A whole-dict `set` on `providers.<route>.headers`, so a brother field of the\n * profile (`baseURL`, `apiKeyEnv`, `models`, `compat`\u2026) is never restated and\n * cannot be lost to this write. The settings layer's path write merges into the\n * existing profile, which is why the write does not have to read the profile\n * back and rebuild it the way the models-array write must.\n *\n * The conflict discipline mirrors {@link writeModelRows}: one retry, re-read\n * fresh. A concurrent writer (the official card's own Save, another tab) moving\n * the namespace between the describe and the mutate is a race the user's edit\n * must survive, so the second attempt fences on the NEW revision instead of\n * failing.\n * @param api - the settings Remote (plus the optional scope).\n * @param route - the provider route key.\n * @param headers - the complete header dict to store (`{}` = send none).\n * @returns whether the write landed, and the refusal text when it did not.\n */\nexport async function writeProviderHeaders(\n api: RemoteApi,\n route: string,\n headers: Record<string, string>,\n): Promise<ProviderHeadersReply> {\n if (typeof route !== 'string' || route.length === 0) return { ok: false, error: 'no-route' }\n for (let attempt = 0; attempt < 2; attempt += 1) {\n try {\n // A conflict retry must NOT read the mirror: it folds a fresh view in\n // asynchronously and would hand back the very revision the write failed\n // against (the same trap writeModelRows documents).\n const join = attempt === 0 ? await describeNamespace(api) : await describeNamespace(api, { fresh: true })\n if (join.namespace === undefined) return { ok: false, error: 'no-namespace' }\n if (join.writable !== true) return { ok: false, error: 'read-only' }\n // The route must exist in the RAW layers: a `set` with an intermediate\n // path that resolves nowhere would create a stray profile carrying only\n // headers -- a route with no endpoint and no protocol, which the adapter\n // would then refuse to build. The slot this editor renders in only\n // dispatches for saved rows, so an absent route is a genuine race with a\n // deletion rather than a normal state.\n if (!routeIsDeclared(join.namespace, route)) return { ok: false, error: 'no-route' }\n const response = await api.settings.mutate(\n PI_AI_NS,\n [{ op: 'set', path: ['providers', route, 'headers'], value: { ...headers } } as unknown as SettingsPathOpView],\n join.namespace.revision,\n )\n if (!response.ok) {\n if (attempt === 0 && response.error.code === 'settings/conflict') continue\n return { ok: false, error: response.error.message }\n }\n return { ok: true }\n } catch (error) {\n return { ok: false, error: error instanceof Error ? error.message : String(error) }\n }\n }\n return { ok: false, error: 'conflict' }\n}\n\n/** Whether either raw layer declares this route (own key, never a prototype name). */\nfunction routeIsDeclared(namespace: SettingsNamespaceView | undefined, route: string): boolean {\n for (const layer of [namespace?.user, namespace?.base]) {\n const providers = providersOfLayer(layer)\n if (providers !== undefined && Object.prototype.hasOwnProperty.call(providers, route)) return true\n }\n return false\n}\n\n/**\n * Describe the pi-ai namespace plus writability.\n *\n * Prefers the official settings scope's snapshot when the shell provides one:\n * the shared describe mirror already carries this namespace's resolved section\n * together with its RAW layers (user / base) and the revision the settings\n * surface itself fences writes with, so a scan costs no wire round trip.\n * `fresh: true` skips the snapshot and reads the wire -- what a conflict retry\n * needs, because the mirror folds a fresh view in asynchronously and would\n * otherwise hand back the very revision the write just failed against.\n * @param api - the settings Remote (plus the optional scope).\n * @param options - `fresh` forces the wire read.\n */\nexport async function describeNamespace(api: RemoteApi, options: { fresh?: boolean } = {}): Promise<SettingsJoin> {\n const viaScope = options.fresh === true ? undefined : joinFromScope(api)\n if (viaScope !== undefined) return viaScope\n const response = await api.settings.describe()\n if (!response.ok) return { namespace: undefined, writable: false }\n const namespace = response.value.namespaces.find(ns => ns.ns === PI_AI_NS)\n return { namespace, writable: response.value.writable }\n}\n\n/**\n * The namespace join as the official scope's snapshot holds it, or undefined\n * when there is no scope / it has no accepted section yet.\n *\n * The snapshot carries only what a browser surface consumes, so the wire-only\n * fields (`schema`, `applies`, `secrets`) are filled with the shapes the plugin\n * never reads; every field this half touches (`value`, `user`, `base`,\n * `revision`) comes from the mirror verbatim.\n */\nfunction joinFromScope(api: RemoteApi): SettingsJoin | undefined {\n const snapshot = api.scope?.getSnapshot()\n if (snapshot === undefined || snapshot.status !== 'ready') return undefined\n if (typeof snapshot.revision !== 'number') return undefined\n return {\n namespace: {\n ns: PI_AI_NS,\n schema: {},\n value: snapshot.value,\n user: snapshot.user,\n base: snapshot.base,\n revision: snapshot.revision,\n applies: 'live',\n secrets: [],\n } as unknown as SettingsNamespaceView,\n writable: snapshot.writable === true,\n }\n}\n", "/**\n * The browser-side cache of the settings document's per-model `defaultEffort`\n * field (issue #4).\n *\n * The composer effort chain reads it through {@link ConfiguredEfforts.of}; a\n * pushed document update or a connection reset invalidates it and the next read\n * re-describes. A refused or empty read is deliberately NOT cached, so a\n * gateway hiccup cannot silently disable the configured layer until the next\n * invalidation.\n *\n * @module dsh-model-think-level/client/injection/configured-efforts\n */\n\nimport { DEFAULT_EFFORT_FIELD } from '../../constants.js'\nimport { describeNamespace, providersOf } from '../ops.js'\nimport type { RemoteApi } from '../types.js'\n\n/** What the cache reads through. */\nexport interface ConfiguredEffortsDeps {\n /** The settings remote. */\n api: RemoteApi\n}\n\n/** The cache's face. */\nexport interface ConfiguredEfforts {\n /** The configured pick for one model, after the cache has settled. */\n of: (provider: string, model: string) => Promise<string | undefined>\n /** Drop the cache; the next read re-describes. */\n invalidate: () => void\n}\n\n/**\n * Build the configured-pick cache.\n * @param deps - the settings remote.\n * @returns the cache's {@link ConfiguredEfforts} face.\n */\nexport function createConfiguredEfforts(deps: ConfiguredEffortsDeps): ConfiguredEfforts {\n let configuredEfforts: Map<string, string> | undefined\n let configuredEffortsInflight: Promise<void> | undefined\n let configuredEffortsGeneration = 0\n\n const ensureConfiguredEfforts = (): Promise<void> => {\n if (configuredEfforts !== undefined) return Promise.resolve()\n const generation = configuredEffortsGeneration\n configuredEffortsInflight ??= describeNamespace(deps.api)\n .then((join) => {\n if (join.namespace === undefined) {\n // The namespace is unregistered, or the read was refused (a\n // gateway hiccup): do NOT cache the empty answer \u2014 the next chain\n // read re-describes, instead of silently disabling the configured\n // layer until the next invalidation.\n configuredEffortsInflight = undefined\n return\n }\n const map = new Map<string, string>()\n for (const [route, profile] of Object.entries(providersOf(join.namespace))) {\n const rawModels = Array.isArray(profile['models']) ? profile['models'] : []\n for (const model of rawModels) {\n if (typeof model !== 'object' || model === null || Array.isArray(model)) continue\n const id = (model as Record<string, unknown>)['id']\n const effort = (model as Record<string, unknown>)[DEFAULT_EFFORT_FIELD]\n if (typeof id === 'string' && typeof effort === 'string' && effort.length > 0) {\n map.set(`${route}/${id}`, effort)\n }\n }\n }\n // An invalidation during the flight must not land a stale snapshot:\n // the caller re-describes on the next read instead.\n if (generation === configuredEffortsGeneration) configuredEfforts = map\n else configuredEffortsInflight = undefined\n })\n .catch(() => {\n // A failed read leaves the cache empty: the next read re-describes.\n if (generation === configuredEffortsGeneration) configuredEffortsInflight = undefined\n })\n return configuredEffortsInflight\n }\n\n const of = async (provider: string, model: string): Promise<string | undefined> => {\n await ensureConfiguredEfforts()\n return configuredEfforts?.get(`${provider}/${model}`)\n }\n\n const invalidate = (): void => {\n configuredEffortsGeneration += 1\n configuredEfforts = undefined\n configuredEffortsInflight = undefined\n }\n\n return { of, invalidate }\n}\n", "/**\n * The knowledge-base auto-fill: build the settings patch that declares\n * `reasoningEfforts` -- and, unless declined, the input-modality declaration --\n * on every undeclared model of a providers dict.\n *\n * Shared by BOTH halves on purpose. The host builds its boot fill with it, and\n * the browser half builds the very same patch from its own read, so one\n * suggestion can never produce two different documents. Pure data logic: no\n * harness package, no node or DOM API.\n *\n * @module dsh-model-think-level/autofill\n */\n\nimport { AUTOFILL_MARKER, INPUT_UNSET_MARKER, UNSET_MARKER } from './constants.js'\nimport { isSelfHostedRelay, NEW_COMPAT_KEYS, suggestEfforts } from './knowledge.js'\nimport { isRecord, routeFactsOf } from './shared.js'\n\n/** A JSON object patch. */\ntype JsonObject = Record<string, unknown>\n\n/**\n * Whether a model row carries an input-modality declaration the document\n * already answers with. An absent key and an empty array both read as \"no\n * answer here\" -- mirroring how llm-pi-ai's own resolution treats them.\n */\nfunction declaresInput(model: JsonObject): boolean {\n const input = model['input']\n return Array.isArray(input) && input.length > 0\n}\n\n/**\n * Build a patch that adds reasoningEfforts -- and, unless declined, the\n * input-modality declaration -- to every undeclared model of the given\n * routes, from the knowledge base / protocol inference. Returns the partial\n * providers patch, or undefined when nothing needs filling.\n * @param providers - the resolved providers dict.\n * @param routeFilter - optional route filter (defaults to all routes).\n * @param options - fill switches; both default on.\n * @param revision - settings revision this read is based on; recorded as the\n * provenance marker of every ladder filled here (see {@link AUTOFILL_MARKER}).\n */\nexport function buildAutofillPatch(\n providers: unknown,\n routeFilter: (route: string) => boolean = () => true,\n options: { efforts?: boolean; modalities?: boolean } = {},\n revision = 0,\n): JsonObject | undefined {\n const fillEfforts = options.efforts !== false\n const fillModalities = options.modalities !== false\n if (!isRecord(providers)) return undefined\n const patchRoutes: JsonObject = {}\n for (const route of Object.keys(providers)) {\n if (!routeFilter(route)) continue\n // Read the RAW models array: this builder rebuilds the array verbatim,\n // so a route carrying a row it cannot represent (non-record) is left\n // alone \u2014 a filtered rebuild would silently DELETE that row.\n const profile = providers[route]\n const rawModels = isRecord(profile) && Array.isArray(profile['models']) ? profile['models'] : undefined\n if (rawModels === undefined || rawModels.length === 0) continue\n if (!rawModels.every(isRecord)) continue\n const models = rawModels as Record<string, unknown>[]\n const nextModels: JsonObject[] = []\n let changed = false\n for (const model of models) {\n // Declared parts are untouched \u2014 and so are parts the user\n // deliberately unset: the durable markers record those absences as\n // decisions, so auto-fill never reads them back as gaps (the bug this\n // guards: a refill one event after every user \"unset\").\n const effortsDeclared = model['reasoningEfforts'] !== undefined || model[UNSET_MARKER] === true\n const inputDeclared = declaresInput(model) || model[INPUT_UNSET_MARKER] === true\n if ((effortsDeclared || !fillEfforts) && (inputDeclared || !fillModalities)) {\n // Versioned exception to never-touch-declared (issue #2): a declared\n // ladder whose compat lacks the role pin on a self-hosted relay gets\n // exactly that field merged in -- the declaration itself is untouched.\n const backfilled = backfillRolePin(model, providers, route)\n if (backfilled !== undefined) {\n changed = true\n nextModels.push(backfilled)\n } else {\n nextModels.push(model)\n }\n continue\n }\n const id = typeof model['id'] === 'string' ? model['id'] : ''\n if (id.length === 0) {\n nextModels.push(model)\n continue\n }\n const routeInfo = routeFactsOf(providers, route)\n const name = typeof model['name'] === 'string' ? model['name'] : undefined\n const suggestion = suggestEfforts(id, { ...routeInfo, displayName: name ?? routeInfo.displayName })\n if (suggestion.efforts === undefined) {\n nextModels.push(model)\n continue\n }\n const fill: JsonObject = { ...model }\n let touched = false\n // Capacities are never filled here: contextWindow / maxTokens are\n // display-only reference values in the browser half, and writing them\n // host-side would silently override the route defaults.\n // Reaching this branch means neither part was declared and no durable\n // unset marker stands on the row -- so there is no stale marker inside\n // `fill` to scrub; the guards above already excluded it.\n if (!effortsDeclared && fillEfforts) {\n touched = true\n fill['reasoningEfforts'] = suggestion.efforts as JsonObject\n // Provenance: the browser flush has to be able to tell this knowledge\n // base suggestion apart from a declaration the user made, or a staged\n // intent that differs in one spelling is dropped as a takeover (see\n // AUTOFILL_MARKER). The value is the revision this fill read.\n fill[AUTOFILL_MARKER] = revision\n if (suggestion.compat !== undefined) {\n const stored = isRecord(model['compat']) ? (model['compat'] as JsonObject) : {}\n fill['compat'] = { ...stored, ...(suggestion.compat as JsonObject) }\n }\n }\n if (!inputDeclared && fillModalities && suggestion.input !== undefined) {\n touched = true\n fill['input'] = [...suggestion.input]\n }\n if (!touched) {\n nextModels.push(model)\n continue\n }\n changed = true\n nextModels.push(fill)\n }\n if (changed) patchRoutes[route] = { models: nextModels }\n }\n return Object.keys(patchRoutes).length === 0 ? undefined : { providers: patchRoutes }\n}\n\n/** Strip newer-only compat keys from an autofill patch (older-kernel downgrade). */\nexport function stripNewCompatKeysDeep(patch: JsonObject): JsonObject | undefined {\n const providers = isRecord(patch['providers']) ? (patch['providers'] as JsonObject) : undefined\n if (providers === undefined) return undefined\n let strippedAny = false\n const nextProviders: JsonObject = {}\n for (const [route, profile] of Object.entries(providers)) {\n if (!isRecord(profile) || !Array.isArray(profile['models'])) {\n nextProviders[route] = profile as JsonObject\n continue\n }\n nextProviders[route] = {\n ...(profile as JsonObject),\n models: (profile['models'] as JsonObject[]).map(model => {\n if (!isRecord(model) || !isRecord(model['compat'])) return model\n const compat = { ...(model['compat'] as JsonObject) }\n for (const key of NEW_COMPAT_KEYS) {\n if (key in compat) {\n delete compat[key]\n strippedAny = true\n }\n }\n return { ...model, compat }\n }),\n }\n }\n if (!strippedAny) return undefined\n return { providers: nextProviders }\n}\n\n/**\n * Merge the role pin into one declared model row, or refuse it.\n *\n * Refusals (all silent, all deliberate): the row carries no ladder dict\n * (`false`, absent, or a deliberate-unset marker -- bare rows keep the bare\n * wire behavior they were chosen for); its compat already names an explicit\n * role value; or the route is not a self-hosted relay. A refusal changes\n * nothing; an acceptance touches exactly one compat field.\n */\nfunction backfillRolePin(\n model: Record<string, unknown>,\n providers: unknown,\n route: string,\n): JsonObject | undefined {\n if (!isRecord(model['reasoningEfforts'])) return undefined\n if (model[UNSET_MARKER] === true) return undefined\n const compat = model['compat']\n const base: JsonObject = isRecord(compat) ? { ...compat } : {}\n if (base['supportsDeveloperRole'] !== undefined) return undefined\n if (!isSelfHostedRelay(routeFactsOf(providers, route))) return undefined\n return { ...model, compat: { ...base, supportsDeveloperRole: false } }\n}\n", "/**\n * The running auto-fill complement (issue #7).\n *\n * The host fills once at boot. Everything a session adds afterwards is filled\n * HERE, on the idle pass: that is the only moment no official card is holding a\n * revision baseline a write would invalidate.\n *\n * @module dsh-model-think-level/client/injection/autofill-run\n */\n\nimport { AUTOFILL_CONFIG_PATH, PI_AI_NS } from '../../constants.js'\nimport { buildAutofillPatch } from '../../autofill.js'\nimport { describeNamespace, userProvidersOf } from '../ops.js'\nimport type { RemoteApi } from '../types.js'\n\n/** What the complement needs from its host. */\nexport interface IdleAutofillDeps {\n /** The settings remote. */\n api: RemoteApi\n}\n\n/** The complement's face. */\nexport interface IdleAutofill {\n /** Run the complement; fire-and-forget on the injector's idle pass. */\n run: () => void\n}\n\n/**\n * Build the auto-fill complement.\n * @param deps - the settings remote.\n * @returns the complement's {@link IdleAutofill} face.\n */\nexport function createIdleAutofill(deps: IdleAutofillDeps): IdleAutofill {\n let autofillSwitches: Promise<{ autofill: boolean; modalityAutofill: boolean }> | undefined\n\n /**\n * The host's autofill switches, read once. A `dsh.client` declaration carries\n * no plugin config, so a deployment configured `autofill: false` would\n * otherwise still be written to from the page. An unreachable or older host\n * keeps the documented defaults rather than silently disabling the feature.\n */\n const autofillSwitchesOf = (): Promise<{ autofill: boolean; modalityAutofill: boolean }> => {\n autofillSwitches ??= (async () => {\n const fallback = { autofill: true, modalityAutofill: true }\n try {\n const response = await fetch(AUTOFILL_CONFIG_PATH, { method: 'GET' })\n if (!response.ok) return fallback\n const body = (await response.json()) as { ok?: boolean; data?: { autofill?: unknown; modalityAutofill?: unknown } }\n if (body?.ok !== true) return fallback\n return {\n autofill: body.data?.autofill !== false,\n modalityAutofill: body.data?.modalityAutofill !== false,\n }\n } catch {\n return fallback\n }\n })()\n return autofillSwitches\n }\n\n /**\n * Fill the models this session added, through the very patch builder the\n * host's boot pass uses (so one suggestion can never produce two different\n * documents). Runs on the idle pass only.\n */\n const runIdleAutofill = async (): Promise<void> => {\n try {\n const switches = await autofillSwitchesOf()\n if (!switches.autofill) return\n // Fresh: the scope mirror folds a just-settled write in asynchronously,\n // so reading it here would fence this fill on a superseded revision and\n // manufacture a `settings/conflict` (the same reason a conflict retry\n // re-reads the wire).\n const join = await describeNamespace(deps.api, { fresh: true })\n const namespace = join.namespace\n if (namespace === undefined || join.writable !== true) return\n const userProviders = userProvidersOf(namespace)\n if (userProviders === undefined) return\n const patch = buildAutofillPatch(\n userProviders, () => true, { modalities: switches.modalityAutofill }, namespace.revision,\n )\n if (patch === undefined) return\n const routes = patch['providers'] as Record<string, { models: unknown }>\n const ops = Object.entries(routes).map(([route, profile]) => ({\n op: 'set' as const,\n path: ['providers', route, 'models'],\n value: profile.models,\n })) as unknown as Parameters<typeof deps.api.settings.mutate>[1]\n const response = await deps.api.settings.mutate(PI_AI_NS, ops, namespace.revision)\n // A refusal arrives as a VALUE, not a throw: without this check the whole\n // complement failed silently (the catch below never ran). Whatever is\n // still undeclared is picked up by the next idle pass.\n if (!response.ok) {\n console.error(`[bre] idle autofill refused: ${response.error.message}`)\n }\n } catch (error) {\n console.error(`[bre] idle autofill failed: ${error instanceof Error ? error.message : String(error)}`)\n }\n }\n\n return { run: () => { void runIdleAutofill() } }\n}\n", "/**\n * The Models-page DOM injection.\n *\n * Owns the document-wide MutationObserver, the debounced scan, the retry\n * backoff, the editor mounts, the host-label anchors and the ledger teardown.\n * The scan itself is `reconcile()` from the editor injector; this module is the\n * wiring and the lifecycle around it.\n *\n * The composer path is NOT this module's business: the caller hands `start()`\n * a synchronous hook that runs on every mutation burst BEFORE the debounced\n * scan, because the composer body must land before the first paint while the\n * settings-page scan does heavy wire reads.\n *\n * @module dsh-model-think-level/client/injection/models-page\n */\n\nimport { createElement } from 'react'\nimport type { ReactNode } from 'react'\nimport { createRoot } from 'react-dom/client'\nimport type { Translate } from '@deepseek-ai/dsh-client-ui-slots'\nimport { PLUGIN_ID } from '../../constants.js'\nimport { EffortEditor } from '../EffortEditor.tsx'\nimport {\n createScanState, flushOnTeardown, flushOnUnload, reconcile,\n type HostLabels, type InjectorDeps, type ScanState,\n} from './models-page-editor.js'\nimport { describeNamespace } from '../ops.ts'\nimport type { ClientContext, RemoteApi } from '../types.js'\nimport { EffortBoundary, panelRoot } from './mount.js'\nimport {\n createEditorTabsState,\n reconcileEditorTabs,\n teardownEditorTabs,\n type EditorTabsDeps,\n} from './editor-tabs.js'\nimport {\n createProviderOrderState,\n reconcileProviderOrder,\n teardownProviderOrder,\n type ProviderOrderDeps,\n} from './provider-order-drag.js'\nimport {\n createProviderToggleState,\n reconcileProviderEnabled,\n teardownProviderEnabled,\n type ProviderToggleDeps,\n} from './provider-toggle.js'\nimport {\n reconcileProviderDeleteSeat,\n teardownProviderDeleteSeats,\n type ProviderDeleteDeps,\n} from './provider-delete-seat.js'\nimport { teardownAutoEffortSeats } from './auto-effort-seat.js'\nimport {\n createModelOrderState,\n reconcileModelOrder,\n teardownModelOrder,\n type ModelOrderDeps,\n} from './model-order-drag.js'\n\n/** Dictionary namespace owning the Models page's copy (ui-settings-models). */\nconst HOST_MODELS_NS = 'settings.models'\n\n/**\n * The official Models-page controls this plugin anchors to, as\n * (dictionary key, English copy) pairs \u2014 the English copy is both the anchor\n * for a host that ships no such namespace and the language the host itself\n * falls back to, so it stays valid in every configuration. Resolved through\n * the host's own dictionary so a third language (or a late language pack)\n * relabels the page and the anchors together.\n */\nconst HOST_LABEL_KEYS = {\n // The disclosure copy was renamed `Capacities`/`\u5BB9\u91CF` \u2192 `Model options`/\n // `\u6A21\u578B\u9009\u9879` in 0.1.6-alpha.2, so the no-dictionary fallback carries BOTH\n // English labels (newest first). The dictionary key resolves first on every\n // host that ships one.\n capacity: ['modelAdvanced', 'Model options', 'Capacities'],\n modelId: ['modelId', 'Model ID'],\n modelName: ['modelName', 'Display name'],\n routeId: ['customRoute', 'Provider ID'],\n baseUrl: ['baseUrl', 'Base URL'],\n apiProtocol: ['customApi', 'API protocol'],\n // The editing card's action row. `apply` is the commit (en 'Apply' / zh\n // '\u4FDD\u5B58'), `cancel` the dismiss; the busy copy is 'applying' and needs no\n // anchor of its own: the button keeps its position in the row.\n apply: ['apply', 'Apply'],\n cancel: ['cancel', 'Cancel'],\n // A provider row's edit button. The row order pass reads the provider id out\n // of this label's `{provider}` slot for every row this plugin gets no seat in\n // (the DeepSeek rows), so the template must be resolved in the page's own\n // language rather than assumed.\n editProvider: ['editProvider', 'Edit {provider}'],\n} as const satisfies Record<keyof HostLabels, readonly [string, string, ...string[]]>\n\n/** What the Models-page injection needs from its host. */\nexport interface ModelsPageDeps {\n /** The client root context. */\n ctx: ClientContext\n /** The settings remote the editors read and write through. */\n api: RemoteApi\n /** The plugin's locale-bound translator. */\n t: Translate\n /** Run once per idle pass, after everything the session held back landed. */\n onIdle: () => void\n /** Wrap a subtree so it re-translates on a language switch. */\n refreshed: (children: () => ReactNode) => ReactNode\n}\n\n/** The Models-page injection's face. */\nexport interface ModelsPageInjection {\n /** The live scan state (the ledger teardown drains it). */\n readonly state: ScanState\n /** The host label anchors in the host's ACTIVE language. */\n labels: () => HostLabels\n /**\n * Begin observing. `onComposerMutation` runs synchronously on every mutation\n * burst, before the debounced scan.\n */\n start: (onComposerMutation: () => void) => void\n /** Disconnect the observer, cancel pending timers, stop the scan chain. */\n stopObserver: () => void\n /** Land the held writes and unmount every editor; the fiber is going away. */\n teardown: () => void\n /** Land only the committed writes and discard the rest (page unload). */\n flushOnUnload: () => void\n /** Force a rescan (document update, connection reset, preference flip). */\n schedule: () => void\n}\n\n/**\n * Build the Models-page injection.\n * @param deps - context, settings remote, copy, idle hook and locale refresh.\n * @returns the injection's {@link ModelsPageInjection} face.\n */\nexport function createModelsPage(deps: ModelsPageDeps): ModelsPageInjection {\n const { ctx, api, t, onIdle, refreshed } = deps\n\n /**\n * The official controls' aria-labels in the host's ACTIVE language.\n *\n * A thunk, never a cached value: the host renders those labels from its own\n * dictionary through its own fallback chain, so re-reading per scan is what\n * keeps a language switch (or a late language pack) from stranding the\n * anchors on words the page no longer prints.\n */\n const labels = (): HostLabels => {\n const translate = ctx.locale.bind(HOST_MODELS_NS) as (key: string) => string\n const resolve = ([key, ...fallbacks]: readonly [string, string, ...string[]]): readonly string[] => {\n const value = translate(key)\n // A host with no such namespace makes translate() echo the key back.\n return value === key || value.trim() === '' ? fallbacks : [value, ...fallbacks]\n }\n return {\n capacity: resolve(HOST_LABEL_KEYS.capacity),\n modelId: resolve(HOST_LABEL_KEYS.modelId),\n modelName: resolve(HOST_LABEL_KEYS.modelName),\n routeId: resolve(HOST_LABEL_KEYS.routeId),\n baseUrl: resolve(HOST_LABEL_KEYS.baseUrl),\n apiProtocol: resolve(HOST_LABEL_KEYS.apiProtocol),\n apply: resolve(HOST_LABEL_KEYS.apply),\n cancel: resolve(HOST_LABEL_KEYS.cancel),\n editProvider: resolve(HOST_LABEL_KEYS.editProvider),\n }\n }\n\n /** Debounce window for DOM-mutation scans (one scan per render burst). */\n const SCAN_DEBOUNCE_MS = 120\n const scanState = createScanState()\n const providerOrderState = createProviderOrderState()\n const providerOrderDeps: ProviderOrderDeps = { t, labels }\n const providerToggleState = createProviderToggleState()\n const providerToggleDeps: ProviderToggleDeps = { t, labels }\n // No per-scan state: the seat IS the DOM node, and the pass is idempotent by\n // its presence, so nothing has to be remembered between scans.\n const providerDeleteDeps: ProviderDeleteDeps = { t, labels }\n const modelOrderState = createModelOrderState()\n const modelOrderDeps: ModelOrderDeps = { t, labels }\n const editorTabsState = createEditorTabsState()\n const editorTabsDeps: EditorTabsDeps = { t }\n let scanTimer: number | undefined\n let retryTimer: number | undefined\n let observer: MutationObserver | undefined\n let composerMutation: (() => void) | undefined\n // Set on dispose. An idle pass already in flight can still call onBackoff\n // after stopObserver cleared the timer; without this gate that would re-arm\n // a retry timer on a dead fiber, restarting the scan chain (and re-mounting\n // editors nothing will ever unmount).\n let stopped = false\n\n /**\n * The injector's dependencies, built ONCE outside the debounced scan: the\n * teardown path (plugin dispose, page unload) drains the ledgers through the\n * very same seam a live scan uses, so a landing write can never take a\n * different path than an in-session one.\n */\n const injectorDeps: InjectorDeps = {\n api,\n describeNamespace: () => describeNamespace(api),\n t,\n labels,\n // The idle pass landed everything the session held back; the caller's\n // complement rides the same moment.\n onIdle,\n // A refused held write arms a backoff; nothing on a settled page would\n // schedule the retry scan, so the injector asks for a timer.\n onBackoff: (delayMs) => { scheduleRetry(delayMs) },\n mount(container, props) {\n const rootEl = document.createElement('div')\n // The slot class carries the grid-column span: this wrapper \u2014 not\n // the React editor inside it \u2014 is the item the official disclosure\n // grid places (see STYLES).\n rootEl.className = 'bre-effort-slot'\n // Mark the container synchronously \u2014 before React renders \u2014 so the\n // idempotency guard (hasEditor) holds from the very first scan.\n // Without this, the appendChild-triggered MutationObserver scan can\n // run while React's async render has not produced the editor div\n // yet, misjudge the row as unmounted, and mount again \u2014 an infinite\n // loop that grows the container without bound.\n rootEl.dataset['plugin'] = PLUGIN_ID\n container.appendChild(rootEl)\n const reactRoot = createRoot(rootEl)\n const renderEditor = (p: typeof props): void => {\n reactRoot.render(createElement(\n EffortBoundary,\n {\n fallbackText: t('renderFailed'),\n // Same seat as the composer bodies: without the locale\n // subscription a language switch re-renders the official page\n // but not this editor \u2014 sameProps compares only document data,\n // so the copy would stay in the language it rendered in.\n children: refreshed(() => createElement(EffortEditor, p)),\n },\n ))\n }\n renderEditor(props)\n return {\n unmount: () => { reactRoot.unmount() },\n render: renderEditor,\n }\n },\n }\n\n /**\n * Wake the injector when a failed idle pass's backoff expires: a settled\n * settings page emits no DOM mutation, so without this the held intent would\n * wait for a scan that never comes. One timer at a time; the fired timer\n * re-runs the scan, which re-arms the next backoff if the write is refused.\n */\n function scheduleRetry(delayMs: number): void {\n if (stopped || retryTimer !== undefined) return\n retryTimer = window.setTimeout(() => {\n retryTimer = undefined\n schedule()\n }, delayMs)\n }\n\n function schedule(): void {\n if (stopped || scanTimer !== undefined) return\n // Debounce: the official page re-renders in bursts (typing, expanding,\n // applying); one scan per frame keeps the editor stable mid-keystroke.\n scanTimer = window.setTimeout(() => {\n scanTimer = undefined\n // The composer path rides along: the debounced scan is the only thing\n // that covers the paths no mutation carries \u2014 apply boot and a slider\n // preference flip.\n composerMutation?.()\n reconcile(panelRoot(), injectorDeps, scanState)\n // The tab pass rides the settled scan as well, after the editor injector\n // has stocked the open card. Its reactive path is the observer above; this\n // one covers the paths no mutation carries \u2014 a boot over an editor that\n // is already open, and a retry whose scan re-arms without a re-render.\n // Its own writes are guarded, so no scan ever chases its own tail.\n reconcileEditorTabs(panelRoot(), editorTabsDeps, editorTabsState)\n // The row order pass rides the same scan but NOT the editor reconcile:\n // `reconcile` bails out whenever no editing card is open, and the\n // provider rows are on the page precisely when nothing is being edited.\n reconcileProviderOrder(panelRoot(), providerOrderDeps, providerOrderState)\n // The switch pass follows the order pass: it reads the same row ids, and\n // it must see the rows where that pass left them.\n reconcileProviderEnabled(panelRoot(), providerToggleDeps, providerToggleState)\n // The delete seat follows the switch pass: it reads the same rows, and it\n // fills the hole the host leaves in an unremovable row's action group.\n reconcileProviderDeleteSeat(panelRoot(), providerDeleteDeps)\n // The model pass comes last and covers a different list entirely: the\n // model rows inside whichever provider editor is open. Its own lists are\n // absent unless a card is expanded, so it no-ops on a collapsed page.\n reconcileModelOrder(panelRoot(), modelOrderDeps, modelOrderState)\n }, SCAN_DEBOUNCE_MS)\n }\n\n const start = (onComposerMutation: () => void): void => {\n if (stopped || observer !== undefined) return\n composerMutation = onComposerMutation\n // A session may already be resident when the fiber starts (page reload,\n // HMR): wire it before the first mutation has a chance to land.\n onComposerMutation()\n observer = new MutationObserver(() => {\n // The composer path runs SYNCHRONOUSLY on the mutation microtask: the\n // React commit that opens the menu and this callback are delivered\n // before the browser's next paint, so the FIRST painted frame already\n // carries the replicated popover \u2014 the official menu never flashes and\n // is never \"covered\". The settings-page editor/toggle reconciles stay\n // debounced below (they do heavy wire reads).\n // The effort-memory wiring leads the callback for the same reason: a\n // session switch lands as a DOM mutation, and the new session's\n // directory must be watched before its projection can read as Default.\n onComposerMutation()\n // The tab pass runs HERE too, synchronously, for the same reason: it is\n // pure DOM work (attribute writes, no wire reads), and it must land in\n // the microtask that carries the official commit. Deferred to the\n // debounced scan below, the opened editor would paint as the official\n // one long column and then visibly re-sort itself a frame or more later\n // \u2014 the \"click Edit and the tabbed editor flickers in\" report.\n reconcileEditorTabs(panelRoot(), editorTabsDeps, editorTabsState)\n schedule()\n })\n observer.observe(document.body, { childList: true, subtree: true })\n schedule()\n }\n\n const stopObserver = (): void => {\n stopped = true\n if (scanTimer !== undefined) {\n window.clearTimeout(scanTimer)\n scanTimer = undefined\n }\n if (retryTimer !== undefined) {\n window.clearTimeout(retryTimer)\n retryTimer = undefined\n }\n observer?.disconnect()\n observer = undefined\n }\n\n const teardown = (): void => {\n stopObserver()\n // Land what the session held back before the fiber goes away: a plugin\n // disable or HMR must not strand the user's intent in memory alone. The\n // ledgers stay in sessionStorage WITH their commit evidence, so the next\n // fiber in this document retries a flush the runtime cut short.\n flushOnTeardown(scanState, injectorDeps)\n // Orphaned editors must not outlive the fiber: on plugin disable or HMR\n // they would keep rendering with a stale api face, failing every write\n // visibly. Unmount every React root this plugin created.\n for (const [, entry] of scanState.mounted) entry.editor.unmount()\n scanState.mounted.clear()\n // The grips are raw DOM this plugin owns inside the official page: leaving\n // them behind would keep a dead fiber's listeners live on a live page.\n teardownProviderOrder(providerOrderState)\n // Same for the enable switches: a live listener on a dead fiber would keep\n // toggling providers the user can no longer see the state of.\n teardownProviderEnabled()\n // The delete seats are raw DOM as well, and they stand for a control the\n // page owns: a dead fiber must not leave a row reading as unavailable.\n teardownProviderDeleteSeats()\n // And for the auto-adapt seats: raw DOM this plugin put in the official\n // catalogue head, which a dead fiber must not leave clickable-looking.\n teardownAutoEffortSeats()\n // And for the model grips inside an open editor, whose own lists live\n // outside the provider rows' seat.\n teardownModelOrder(modelOrderState)\n // And for the editor tabs: the bar and the region tags are raw DOM and\n // attributes this plugin owns inside the official card \u2014 a dead fiber must\n // not leave the page keyed to tabs nothing maintains anymore.\n teardownEditorTabs()\n }\n\n const flushOnUnloadNow = (): void => { flushOnUnload(scanState, injectorDeps) }\n\n return {\n state: scanState,\n labels,\n start,\n stopObserver,\n teardown,\n flushOnUnload: flushOnUnloadNow,\n schedule,\n }\n}\n", "/**\n * The thinking-effort + input-modality editor injected into the official\n * Models page's model rows. Rendered with ReactDOM.createRoot into a DOM\n * container the injector creates, it needs no harness services of its own:\n * everything arrives through a plain props face, so it stays testable in\n * isolation. The draft/intent rules live in the client/effort module.\n *\n * Suggestion feedback is ZONED: provenance + confidence form their own line;\n * capacity references render as a separate read-only block explicitly marked\n * \"never auto-filled\" -- the official capacity inputs above stay untouched.\n * The apply/reset actions sit at the very bottom and own every section.\n *\n * @module dsh-model-think-level/EffortEditor\n */\n\nimport { useEffect, useRef, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport { PLUGIN_ID } from '../constants.js'\nimport type {\n CompatSuggestion,\n InputModalities,\n InputSource,\n ReasoningEfforts,\n} from '../knowledge.js'\nimport {\n buildIntent,\n draftFrom,\n LEVEL_ORDER,\n sameEfforts,\n type DraftLevels,\n type EffortIntent,\n} from './effort.js'\nimport { defaultWireRisk } from './wire-preview.js'\nimport { suggestEfforts } from '../knowledge.js'\nimport { listenAutoEffort, queueAutoEffort } from './auto-effort.js'\nimport type { EffortEditorApi, EffortWriteIntent, PendingWrite, PendingWriteParts } from './types.js'\n\n/** Thousands-grouped token counts, matching the official capacity inputs. */\nconst COUNT = new Intl.NumberFormat('en-US')\n\n/** One model row's data the editor needs. */\nexport interface EffortModel {\n /** Model id (the settings key). */\n id: string\n /** Display name, when one is set. */\n name?: string\n /** The model's current reasoningEfforts declaration. */\n efforts?: false | ReasoningEfforts\n}\n\n/** Props of {@link EffortEditor}. */\nexport interface EffortEditorProps {\n /** Route key. */\n route: string\n /**\n * Route display name. Part of the mount contract but never rendered here:\n * this editor lives inside ONE model row, whose provider card already\n * carries the name, so the component does not even destructure it. The\n * injector keeps it in the prop set and its sameProps diff compares it, which\n * is what catches a provider rename under an already-mounted editor.\n */\n routeDisplayName: string\n /** Route wire protocol, when configured. */\n routeApi?: string\n /** Route endpoint, when configured. */\n routeBaseURL?: string\n /** Model id. */\n modelId: string\n /** Model display name, when one is set. */\n modelName?: string\n /** The model's current reasoningEfforts declaration. */\n efforts?: false | ReasoningEfforts\n /** The model's current input-modality declaration. */\n input?: InputModalities\n /** The model's stored compat block (passthrough; suggestions merge over it). */\n compat?: CompatSuggestion\n /** The model's stored per-model default-effort pick, when one is set. */\n defaultEffort?: string\n /** Model row index (for aria labels). */\n index: number\n /**\n * The model row is not saved yet (a create card's draft route, or a new\n * model row on a saved route). Apply then stages the declaration instead\n * of writing settings; the injector applies staged declarations\n * automatically once the row is saved.\n */\n staged?: boolean\n /**\n * Whether the official Models page owns input types for this row (the\n * 0.1.6-alpha.2 `ModelInputTypes` control was sniffed in the disclosure).\n * True hides the modality section: the official editor is the single\n * surface, so showing a second one would be duplicate UI. Older kernels\n * have no such control and keep the section.\n */\n officialInputTypes?: boolean\n /** The write seam (settings.mutate plus the suggestion engine). */\n api: EffortEditorApi\n /** Read-only (settings document not writable). */\n readOnly: boolean\n /** Localized copy. */\n t: (key: string, params?: Record<string, string | number>) => string\n}\n\n/** Draft state of the modality part: declared or inheriting, image or not. */\nexport interface DraftModality {\n declared: boolean\n image: boolean\n}\n\n/** Read the stored declaration into a draft. Empty mirrors undefined: the\n * resolved settings layer materializes absent arrays as [], and core reads\n * that as \"no answer here\" -- never as a phantom text-only declaration. */\nexport function modalityFrom(input: InputModalities | undefined): DraftModality {\n if (input === undefined || input.length === 0) return { declared: false, image: false }\n return { declared: true, image: input.includes('image') }\n}\n\n/** Resolve a modality draft to the write intent (null = durably unset). */\nexport function buildModalityIntent(draft: DraftModality): InputModalities | null {\n if (!draft.declared) return null\n return draft.image ? ['text', 'image'] : ['text']\n}\n\n/**\n * The compat fields this editor can SET, per protocol \u2014 which is also the set\n * it may clear. A key from neither list survives an apply untouched: a\n * hand-tuned field the UI never showed is not this editor's to drop. Clearing\n * a picker means unset, not \"keep whatever was chosen last\".\n */\nexport const OWNED_COMPAT_KEYS: Readonly<Record<string, readonly string[]>> = {\n 'openai-completions': ['thinkingTokenBudgetField', 'supportsThinkingTokenBudget', 'vllmPriority'],\n 'openai-responses': ['supportsMaxOutputTokens'],\n}\n\n/** The compat fields clearable on one protocol (empty = this edit owns none). */\nexport function ownedCompatKeys(routeApi: string | undefined): readonly string[] {\n return OWNED_COMPAT_KEYS[(routeApi ?? '').toLowerCase()] ?? []\n}\n\n/** The owned compat fields the draft actually left EMPTY. */\nexport function clearedCompatKeys(routeApi: string | undefined, draft: CompatSuggestion | undefined): readonly string[] {\n const owned = ownedCompatKeys(routeApi)\n if (owned.length === 0) return []\n const set = new Set<string>(draft === undefined ? [] : Object.keys(draft))\n return owned.filter(key => !set.has(key))\n}\n\n/**\n * The `clearCompatKeys` part of a commit, derived from the draft.\n *\n * A DEFINED draft always reports (an empty array is meaningful: every owned\n * field it shows is set). An EMPTY draft must still clear the fields the\n * protocol OWNS -- dropping the last owned pick is a user decision to unset --\n * but only when there is something to clear: an unrelated route keeps the\n * shape it always had (no `clearCompatKeys` at all).\n * @param routeApi - the route's wire protocol.\n * @param writeCompat - the compat bytes this commit writes, if any.\n * @returns the clear part to spread into the commit payload.\n */\nexport function compatClearIntent(\n routeApi: string | undefined,\n writeCompat: CompatSuggestion | undefined,\n): { clearCompatKeys?: readonly string[] } {\n if (writeCompat !== undefined) return { clearCompatKeys: clearedCompatKeys(routeApi, writeCompat) }\n const owned = clearedCompatKeys(routeApi, undefined)\n return owned.length === 0 ? {} : { clearCompatKeys: owned }\n}\n\n/** Semantic equality between a modality draft and a stored declaration. */\nexport function sameModality(draft: DraftModality, stored: InputModalities | undefined): boolean {\n if (!draft.declared) return stored === undefined\n if (stored === undefined) return false\n return draft.image ? stored.includes('image') : !stored.includes('image')\n}\n\n/**\n * The ladder part of a write, from a switch state and the draft it resolves to.\n *\n * `'keep'` when nothing is armed and nothing is stored: that is a no-op, NOT an\n * unset \u2014 an undefined there would stamp the durable unset marker onto a\n * never-declared model and silence host auto-fill.\n * @param nextThinking - the (pre-update) switch state the edit resolved to.\n * @param built - `buildIntent` of the draft the edit resolved to, which is the\n * explicit `false` when the only armed level is 'off'.\n * @param initialEfforts - the row's stored declaration.\n * @returns the ladder intent to write.\n */\nexport function ladderIntent(\n nextThinking: boolean,\n built: EffortIntent,\n initialEfforts: EffortWriteIntent | undefined,\n): EffortWriteIntent {\n const next = nextThinking ? built : false\n return nextThinking && next === undefined && initialEfforts === undefined ? 'keep' : next\n}\n\n/**\n * The compat draft a FRESH editor on this row would hold.\n *\n * The editor's three compat inputs are seeded from the stored block, so an\n * untouched editor's draft is exactly the stored block restricted to the three\n * fields it shows. The injector's provider-wide adapt has no editor to ask for\n * a draft, so it derives the same value here: a hand-tuned field the UI does\n * not show stays untouchable, and one it does show survives the write.\n * @param compat - the row's stored compat block.\n * @returns the draft, or undefined when it holds no field at all.\n */\nexport function compatDraftOf(compat: CompatSuggestion | undefined): CompatSuggestion | undefined {\n const out: CompatSuggestion = {}\n const budgetField = compat?.thinkingTokenBudgetField ?? ''\n // The cast is the same one the editor's own compatDraft applies to its state\n // string: the stored block is already typed, and a hand-tuned spelling the\n // plugin's union does not name must survive the round trip unchanged.\n if (budgetField !== '') out.thinkingTokenBudgetField = budgetField as CompatSuggestion['thinkingTokenBudgetField']\n const priorityText = compat?.vllmPriority === undefined ? '' : String(compat.vllmPriority)\n const pruned = priorityText.trim()\n if (pruned !== '' && /^-?\\d+$/.test(pruned)) out.vllmPriority = Number.parseInt(pruned, 10)\n const maxOutput = compat?.supportsMaxOutputTokens === undefined ? '' : String(compat.supportsMaxOutputTokens)\n if (maxOutput === 'true') out.supportsMaxOutputTokens = true\n else if (maxOutput === 'false') out.supportsMaxOutputTokens = false\n return Object.keys(out).length === 0 ? undefined : out\n}\n\n/**\n * Assemble one complete pending write from the parts a caller resolved: the\n * compat merge (the applied suggestion's block with the caller's own fields on\n * top), the thinking-budget default an openai-completions endpoint needs, and\n * the clear list for the fields this write OWNS and left empty.\n *\n * The single implementation of that assembly: the row's own editor reports the\n * user's edits through it, and the injector's provider-wide adapt reports\n * collapsed rows through it, so the same suggestion always writes the same\n * bytes whichever path applied it.\n * @param parts - the resolved parts (see {@link PendingWriteParts}).\n * @returns the write to hand the injector's ledger.\n */\nexport function pendingWriteOf(parts: PendingWriteParts): PendingWrite {\n const baseCompat = parts.appliedCompat\n const manualCompat = parts.manualCompat\n let writeCompat = baseCompat !== undefined ? { ...baseCompat, ...(manualCompat ?? {}) } : manualCompat\n if (\n parts.initialCompat?.supportsThinkingTokenBudget === true\n && parts.initialCompat?.thinkingTokenBudgetField === undefined\n && writeCompat?.thinkingTokenBudgetField === undefined\n && (parts.routeApi ?? '').toLowerCase() === 'openai-completions'\n ) {\n writeCompat = { ...(writeCompat ?? {}), thinkingTokenBudgetField: 'thinking_token_budget' }\n }\n return {\n efforts: parts.efforts,\n ...(writeCompat === undefined ? {} : { compat: writeCompat }),\n ...(parts.input === undefined ? {} : { input: parts.input }),\n ...compatClearIntent(parts.routeApi, writeCompat),\n ...(parts.defaultEffort === undefined ? {} : { defaultEffort: parts.defaultEffort }),\n }\n}\n\n/**\n * Render one model's thinking-effort + input-modality editor: the level\n * checkboxes, the modality toggle, the auto-adapt action, and the\n * apply/reset actions that own both sections.\n */\nexport function EffortEditor({ route, routeApi, routeBaseURL, modelId, modelName, efforts: initialEfforts, input: initialInput, compat: initialCompat, defaultEffort: initialDefaultEffort, index, staged = false, officialInputTypes = false, api, readOnly, t }: EffortEditorProps): ReactNode {\n const [draft, setDraft] = useState<DraftLevels>(() => draftFrom(initialEfforts))\n const [modality, setModality] = useState<DraftModality>(() => modalityFrom(initialInput))\n // The master thinking switch. OFF reports the ladder as `false` \u2014 the row's\n // own \"declares no reasoning-effort control\" \u2014 which is what makes the level\n // grid below unnecessary; ON keeps every edit byte-for-byte as before. It\n // starts ON unless the stored row already says `false`, so a row nobody\n // touches never gains a declaration it did not have.\n const [thinking, setThinking] = useState<boolean>(() => initialEfforts !== false)\n // The per-model default-effort pick (issue #4), as the level id or '' for\n // \"follow the memory chain\". The pick lives in the same Apply as the ladder\n // so one mutate carries both.\n const [defaultEffort, setDefaultEffort] = useState<string>(() => initialDefaultEffort ?? '')\n const [busy, setBusy] = useState(false)\n const [message, setMessage] = useState<{ kind: 'success' | 'error' | 'info'; text: string } | undefined>(undefined)\n const [suggested, setSuggested] = useState<ReasoningEfforts | false | undefined>(undefined)\n const [suggestedSource, setSuggestedSource] = useState('')\n const [suggestedConfidence, setSuggestedConfidence] = useState<'high' | 'medium' | 'low'>('low')\n // The compat block the applied suggestion carries, kept in a REF on purpose:\n // it describes the wire format, not the level set, so level/image tweaks must\n // NOT clear it -- markDirty() resets the user-facing draft context, and a\n // state here would wipe the very bytes Apply/stage needs to reproduce what\n // the host autofill writes. Only a fresh Auto-adapt replaces it; Reset\n // discards it along with everything else.\n const appliedCompatRef = useRef<CompatSuggestion | undefined>(undefined)\n // Modality + capacity parts of the applied suggestion. Capacities are\n // DISPLAY-ONLY: they render in the reference block and never touch the\n // official capacity inputs.\n const [budgetField, setBudgetField] = useState<string>(() => initialCompat?.thinkingTokenBudgetField ?? '')\n const [priorityText, setPriorityText] = useState<string>(() => initialCompat?.vllmPriority === undefined ? '' : String(initialCompat.vllmPriority))\n const [maxOutput, setMaxOutput] = useState<string>(() => initialCompat?.supportsMaxOutputTokens === undefined ? '' : String(initialCompat.supportsMaxOutputTokens))\n const previousCompat = useRef<CompatSuggestion | undefined>(initialCompat)\n const [suggestedInputSource, setSuggestedInputSource] = useState<InputSource | undefined>(undefined)\n const [referenceContext, setReferenceContext] = useState<number | undefined>(undefined)\n const [referenceMaxTokens, setReferenceMaxTokens] = useState<number | undefined>(undefined)\n // Once the user edits, the drafts own the row: a re-render that changes\n // the initial props (the official page re-renders on its own edits and on\n // apply) must not overwrite in-flight input. Before any edit the drafts\n // simply follow the props.\n const dirtyRef = useRef(false)\n const previousEfforts = useRef<false | ReasoningEfforts | undefined>(initialEfforts)\n const previousInput = useRef<InputModalities | undefined>(initialInput)\n const previousDefaultEffort = useRef<string | undefined>(initialDefaultEffort)\n // The auto-adapt closure of the LATEST render, for the listener below: that\n // closure reads this render's props and state, but the subscription has to\n // outlive them, so the listener reaches it through a ref instead of\n // re-subscribing on every render.\n const autoAdaptRef = useRef<(() => Promise<void>) | undefined>(undefined)\n // The SAVED ladder, not the in-flight draft. Bulk adapt must not overwrite a\n // model whose thinking levels are already configured; the row's own \u81EA\u52A8\u9002\u914D\n // link still may, because that click is about this one row.\n const savedEffortsRef = useRef(initialEfforts)\n savedEffortsRef.current = initialEfforts\n\n // The catalogue head's \"adapt every model\" seat (user request \u2464) publishes\n // one request per provider. An unconfigured row answers it with the same\n // autoAdapt() its own link runs. A row that already has a stored ladder\n // (including an explicit \"does not reason\") does not: bulk adapt fills the\n // gaps and leaves configured levels alone. The row's own link is unaffected.\n useEffect(() => listenAutoEffort((target) => {\n if (target !== route) return\n if (savedEffortsRef.current !== undefined) return\n const run = autoAdaptRef.current\n if (run !== undefined) queueAutoEffort(run)\n }), [route])\n\n useEffect(() => {\n if (!sameEfforts(previousEfforts.current, initialEfforts)) {\n previousEfforts.current = initialEfforts\n if (!dirtyRef.current) {\n setDraft(draftFrom(initialEfforts))\n setThinking(initialEfforts !== false)\n }\n }\n if (previousInput.current !== initialInput) {\n previousInput.current = initialInput\n // A push that lands while the user is mid-edit must not clobber the\n // draft -- same contract as the level grid above.\n if (!dirtyRef.current) setModality(modalityFrom(initialInput))\n }\n if (previousDefaultEffort.current !== initialDefaultEffort) {\n previousDefaultEffort.current = initialDefaultEffort\n if (!dirtyRef.current) setDefaultEffort(initialDefaultEffort ?? '')\n }\n if (JSON.stringify(previousCompat.current ?? null) !== JSON.stringify(initialCompat ?? null)) {\n previousCompat.current = initialCompat\n if (!dirtyRef.current) {\n setBudgetField(initialCompat?.thinkingTokenBudgetField ?? '')\n setPriorityText(initialCompat?.vllmPriority === undefined ? '' : String(initialCompat.vllmPriority))\n setMaxOutput(initialCompat?.supportsMaxOutputTokens === undefined ? '' : String(initialCompat.supportsMaxOutputTokens))\n }\n }\n // The pick participates in the same sync discipline as the other three\n // parts \u2014 its token MUST ride the dependency array, or a pick-only push\n // (another tab, a hand-edited document) never reaches this draft, and a\n // later unrelated Apply writes the stale pick back over the document.\n }, [initialEfforts, initialInput, initialCompat, initialDefaultEffort])\n\n const compatDraft = (): CompatSuggestion | undefined => {\n const out: CompatSuggestion = {}\n if (budgetField !== '') out.thinkingTokenBudgetField = budgetField as CompatSuggestion['thinkingTokenBudgetField']\n const pruned = priorityText.trim()\n if (pruned !== '' && /^-?\\d+$/.test(pruned)) out.vllmPriority = Number.parseInt(pruned, 10)\n if (maxOutput === 'true') out.supportsMaxOutputTokens = true\n else if (maxOutput === 'false') out.supportsMaxOutputTokens = false\n return Object.keys(out).length === 0 ? undefined : out\n }\n const compatChanged = (): boolean => {\n const draftOut = compatDraft()\n return JSON.stringify(draftOut ?? null) !== JSON.stringify({\n ...(initialCompat?.thinkingTokenBudgetField === undefined ? {} : { thinkingTokenBudgetField: initialCompat.thinkingTokenBudgetField }),\n ...(initialCompat?.vllmPriority === undefined ? {} : { vllmPriority: initialCompat.vllmPriority }),\n ...(initialCompat?.supportsMaxOutputTokens === undefined ? {} : { supportsMaxOutputTokens: initialCompat.supportsMaxOutputTokens }),\n })\n }\n // The declared levels of this draft: the pick's value domain.\n const armedLevels = LEVEL_ORDER.filter(level => draft[level].on)\n const defaultEffortIntent = defaultEffort === (initialDefaultEffort ?? '')\n ? undefined\n : defaultEffort === '' ? null : defaultEffort\n\n // What the row would report right now: the switch OFF *is* the `false`\n // declaration, so it participates in the dirty comparison like any other part.\n const effectiveEfforts: false | ReasoningEfforts | undefined = thinking ? buildIntent(draft) : false\n const changed = !sameEfforts(effectiveEfforts, initialEfforts)\n || !sameModality(modality, initialInput)\n || compatChanged()\n || defaultEffortIntent !== undefined\n\n /**\n * What this editor last handed the injector, serialized for idempotency.\n *\n * The editor has no commit action of its own since C2: every change reports\n * itself, and the injector decides where the intent lands. React's state\n * updates are asynchronous, so each change point computes its OWN next value\n * and passes it down \u2014 nothing here reads a state value the current event has\n * not yet committed to. The ref then collapses repeat reports: a controlled\n * text input fires on every keystroke, and the wire value only moves on some\n * of them.\n */\n const lastCommitRef = useRef<string | undefined>(undefined)\n\n const markDirty = (): void => {\n dirtyRef.current = true\n setSuggested(undefined)\n setSuggestedSource('')\n setSuggestedConfidence('low')\n setSuggestedInputSource(undefined)\n // appliedCompatRef deliberately SURVIVES this (see its declaration) --\n // the commit still has to write the applied suggestion's compat bytes.\n // Reference capacities SURVIVE tweaks too: they are read-only context about\n // the model, not part of the applied suggestion -- only Reset or a fresh\n // Auto-adapt replaces them.\n }\n\n /**\n * Report the row's complete pending edit to the injector.\n *\n * The whole intent is recomputed from the values handed in, never from the\n * stored state: the caller has just computed them, and reading `draft` here\n * would see the pre-update value on the reporting turn.\n *\n * The ladder part travels as `'keep'` when nothing is armed and nothing is\n * stored: that is a no-op, NOT an unset -- sending `undefined` would stamp\n * the durable unset marker onto a never-declared model and silence host\n * auto-fill.\n */\n const commitPending = (\n nextDraft: DraftLevels,\n nextModality: DraftModality,\n nextDefaultEffort: string,\n // The switch's pre-update value: a toggle computes its own next value and\n // hands it in, exactly like the three parts above.\n nextThinking: boolean = thinking,\n ): void => {\n // OFF is the explicit `false` declaration, whatever the draft still holds\n // (the draft is deliberately left alone, so flipping back loses no edits).\n const effortsIntent = ladderIntent(nextThinking, buildIntent(nextDraft), initialEfforts)\n // The modality part travels ONLY when this edit actually changed it: an\n // untouched row must omit the intent entirely -- a null would stamp the\n // durable inputUnset marker onto modalities the user never decided about.\n const nextInput = sameModality(nextModality, initialInput) ? undefined : buildModalityIntent(nextModality)\n const defaultEffortOut = nextDefaultEffort === (initialDefaultEffort ?? '')\n ? undefined\n : nextDefaultEffort === '' ? null : nextDefaultEffort\n // The compat merge, the owned-key clear list and the thinking-budget\n // default live in ONE place ({@link pendingWriteOf}), shared with the\n // injector's provider-wide adapt: a suggestion must write the same bytes\n // whichever path applied it.\n const write = pendingWriteOf({\n efforts: effortsIntent,\n input: nextInput,\n defaultEffort: defaultEffortOut,\n manualCompat: compatDraft(),\n appliedCompat: appliedCompatRef.current,\n routeApi,\n initialCompat,\n })\n // The payload IS the identity: two edits that resolve to the same write are\n // the same report, whatever draft states produced them.\n const signature = JSON.stringify(write)\n if (lastCommitRef.current === signature) return\n lastCommitRef.current = signature\n api.commit(route, modelId, write)\n // \"Modified\" is DERIVED, never messaged: every handler below clears the\n // message in the same batch, so a pending-save message set here would be\n // overwritten on the very turn it became true (React folds both updates).\n // It renders from `changed` instead \u2014 see the message block.\n }\n\n const patchLevel = (level: (typeof LEVEL_ORDER)[number], on: boolean): void => {\n markDirty()\n // Disarming the level the pick names clears the pick with it: the pick is\n // a level of the declared ladder, and a stale one must not survive.\n const nextDefaultEffort = !on && defaultEffort === level ? '' : defaultEffort\n if (nextDefaultEffort !== defaultEffort) setDefaultEffort(nextDefaultEffort)\n setDraft(current => {\n const next = { ...current, [level]: { ...current[level], on } }\n // Enabling a thinking level pre-fills the conventional spelling.\n if (on && level !== 'off' && next[level].wire.trim().length === 0) {\n next[level] = { ...next[level], wire: level }\n }\n commitPending(next, modality, nextDefaultEffort)\n return next\n })\n setMessage(undefined)\n }\n\n /**\n * The master switch. ON restores the ordinary ladder editing; OFF reports the\n * explicit `false` declaration and takes the default pick with it (a pick\n * names a level of a ladder that no longer exists). The DRAFT is deliberately\n * left untouched: flipping back and forth loses no wire spellings.\n */\n const patchThinking = (on: boolean): void => {\n markDirty()\n setThinking(on)\n const nextDefaultEffort = on ? defaultEffort : ''\n if (nextDefaultEffort !== defaultEffort) setDefaultEffort(nextDefaultEffort)\n commitPending(draft, modality, nextDefaultEffort, on)\n setMessage(undefined)\n }\n\n const patchWire = (level: (typeof LEVEL_ORDER)[number], wire: string): void => {\n markDirty()\n setDraft(current => {\n const next = { ...current, [level]: { ...current[level], wire } }\n commitPending(next, modality, defaultEffort)\n return next\n })\n setMessage(undefined)\n }\n\n const patchImage = (on: boolean): void => {\n markDirty()\n const nextModality = { declared: true, image: on }\n setModality(nextModality)\n commitPending(draft, nextModality, defaultEffort)\n setMessage(undefined)\n }\n\n const clearModality = (): void => {\n markDirty()\n const nextModality = { declared: false, image: false }\n setModality(nextModality)\n commitPending(draft, nextModality, defaultEffort)\n setMessage(undefined)\n }\n\n // Each text field reports on its own change: a controlled input fires per\n // keystroke, and commitPending collapses the ones that do not move the value.\n const patchBudgetField = (value: string): void => {\n markDirty()\n setBudgetField(value)\n commitPending(draft, modality, defaultEffort)\n setMessage(undefined)\n }\n\n const patchPriority = (value: string): void => {\n markDirty()\n setPriorityText(value)\n commitPending(draft, modality, defaultEffort)\n setMessage(undefined)\n }\n\n const patchMaxOutput = (value: string): void => {\n markDirty()\n setMaxOutput(value)\n commitPending(draft, modality, defaultEffort)\n setMessage(undefined)\n }\n\n const patchDefaultEffort = (value: string): void => {\n markDirty()\n setDefaultEffort(value)\n commitPending(draft, modality, value)\n setMessage(undefined)\n }\n\n const applySuggestion = (\n parts: {\n efforts: ReasoningEfforts | false\n source: string\n confidence: 'high' | 'medium' | 'low'\n compat?: CompatSuggestion\n input?: InputModalities\n inputSource?: InputSource\n contextWindow?: number\n maxTokens?: number\n defaultEffort?: string\n },\n ): void => {\n markDirty()\n // A suggestion carries a verdict about reasoning: arm the switch to match\n // it, or applying a ladder while the switch is OFF would report `false`\n // and write the ladder nowhere.\n const nextThinking = parts.efforts !== false\n setThinking(nextThinking)\n const nextDraft = draftFrom(parts.efforts)\n setDraft(nextDraft)\n // The suggestion's own modality, when it carries one; else the draft's.\n const nextModality = parts.input === undefined ? modality : modalityFrom(parts.input)\n if (parts.input !== undefined) setModality(nextModality)\n // A blank pick takes the vendor default. A pick the user already chose\n // stays: auto-adapt must not overwrite a level they set by hand.\n const nextDefault = defaultEffort === '' && parts.defaultEffort !== undefined\n ? parts.defaultEffort\n : defaultEffort\n if (nextDefault !== defaultEffort) setDefaultEffort(nextDefault)\n setSuggested(parts.efforts)\n setSuggestedSource(parts.source)\n setSuggestedConfidence(parts.confidence)\n appliedCompatRef.current = parts.compat\n setSuggestedInputSource(parts.inputSource)\n setReferenceContext(parts.contextWindow)\n setReferenceMaxTokens(parts.maxTokens)\n setMessage({\n kind: 'info',\n text: t('appliedHint', { source: parts.source, confidence: t('confidence_' + parts.confidence) }),\n })\n // One commit for the whole suggestion, so the ledger never holds the\n // ladder without the compat block that makes its wire spellings work.\n commitPending(nextDraft, nextModality, nextDefault, nextThinking)\n }\n\n const autoAdapt = async (): Promise<void> => {\n // The link that normally calls this is `disabled` while the row is\n // read-only or already working, so the gate had to be the button's. The\n // provider-wide seat request (user request \u2464) calls THIS directly and\n // bypasses the button entirely, so the same gate has to live here or a\n // read-only row would be written.\n if (readOnly || busy) return\n setBusy(true)\n setMessage(undefined)\n try {\n // A staged route has no stored profile yet; the card's own typed\n // protocol/endpoint are the facts inference has to work with.\n const reply = staged\n ? await api.suggest(route, modelId, modelName, { ...(routeApi === undefined ? {} : { api: routeApi }), ...(routeBaseURL === undefined ? {} : { baseURL: routeBaseURL }) })\n : await api.suggest(route, modelId, modelName)\n if (!reply.ok) {\n setMessage({ kind: 'error', text: reply.error === 'no-suggestion' ? t('noSuggestion') : reply.error })\n return\n }\n applySuggestion(reply.suggestion)\n } catch (error) {\n setMessage({ kind: 'error', text: t('writeError', { message: String(error) }) })\n } finally {\n setBusy(false)\n }\n }\n\n // Hand the listener above the closure of this render. An effect rather than a\n // write during render: React may discard a render it never commits.\n useEffect(() => { autoAdaptRef.current = autoAdapt })\n\n const reset = (): void => {\n // Back to the SAVED declarations, not to \"everything off\": Reset means\n // \"discard my edits\". Clearing a declaration is still one flow away --\n // uncheck everything (the unset intent).\n dirtyRef.current = false\n // The ledger outlives this component's state: without withdrawing, the\n // discard is cosmetic and the official Save writes the edits anyway.\n api.withdraw(route, modelId)\n setDraft(draftFrom(initialEfforts))\n setThinking(initialEfforts !== false)\n setModality(modalityFrom(initialInput))\n setDefaultEffort(initialDefaultEffort ?? '')\n setBudgetField(initialCompat?.thinkingTokenBudgetField ?? '')\n setPriorityText(initialCompat?.vllmPriority === undefined ? '' : String(initialCompat.vllmPriority))\n setMaxOutput(initialCompat?.supportsMaxOutputTokens === undefined ? '' : String(initialCompat.supportsMaxOutputTokens))\n setSuggested(undefined)\n setSuggestedSource('')\n setSuggestedConfidence('low')\n appliedCompatRef.current = undefined\n setSuggestedInputSource(undefined)\n setReferenceContext(undefined)\n setReferenceMaxTokens(undefined)\n setMessage(undefined)\n // Let the next edit report itself again: the collapsed signature belongs\n // to the intent that was just withdrawn.\n lastCommitRef.current = undefined\n }\n\n // Default-wire warning (issue #2): a stored forced-thinking ladder sends\n // an off-equivalent on Default/test calls. The format prefers the just\n // applied suggestion, else the knowledge base for this route.\n const wireFormat = appliedCompatRef.current?.thinkingFormat\n ?? suggestEfforts(modelId, {\n ...(routeApi === undefined ? {} : { api: routeApi }),\n ...(routeBaseURL === undefined ? {} : { baseURL: routeBaseURL }),\n }).compat?.thinkingFormat\n const wireRisk = defaultWireRisk(initialEfforts, wireFormat)\n\n const disabled = readOnly || busy\n\n return (\n <div className=\"bre-effort-editor\" data-plugin={PLUGIN_ID}>\n {/* The master switch lives OUTSIDE the effort card on purpose: with it\n off there is no ladder to configure, so the card below is not rendered\n at all \u2014 one row is everything an un-thinking model shows. */}\n <label className=\"bre-modality-row bre-thinking-switch\">\n <input\n type=\"checkbox\"\n checked={thinking}\n disabled={disabled}\n aria-label={t('thinkingMode') + ' ' + String(index + 1)}\n onChange={(event) => { patchThinking(event.target.checked) }}\n />\n <span className=\"bre-effort-level\">{t('thinkingMode')}</span>\n {thinking ? null : <span className=\"bre-effort-note\">{t('thinkingModeOff')}</span>}\n </label>\n {thinking ? (\n <div className=\"bre-effort-card\">\n <div className=\"bre-effort-head\">\n <span className=\"bre-effort-title\">{t('reasoningEffort')}</span>\n <button\n type=\"button\"\n className=\"bre-link-button\"\n disabled={disabled}\n onClick={() => { void autoAdapt() }}\n >\n {t('autoAdapt')}\n </button>\n </div>\n <div className=\"bre-effort-grid\">\n {LEVEL_ORDER.map(level => {\n const cell = draft[level]\n return (\n <label key={level} className=\"bre-effort-row\">\n <input\n type=\"checkbox\"\n checked={cell.on}\n disabled={disabled}\n aria-label={t('level_' + level) + ' ' + String(index + 1)}\n onChange={(event) => { patchLevel(level, event.target.checked) }}\n />\n <span className=\"bre-effort-level\">{t('level_' + level)}</span>\n {/* Every armed level takes a wire input, off included: a\n non-null off spelling is what some formats send to close\n thinking (OpenAI's none spelling, or the deepseek/zai\n explicit disabled object); an empty one sends nothing. */}\n {cell.on\n ? (\n <input\n type=\"text\"\n className=\"bre-effort-wire\"\n value={cell.wire}\n disabled={disabled}\n placeholder={t('wirePlaceholder')}\n aria-label={t('level_' + level) + ' ' + t('wireValue')}\n onChange={(event) => { patchWire(level, event.target.value) }}\n />\n )\n : <span className=\"bre-effort-empty\" />}\n </label>\n )\n })}\n </div>\n {armedLevels.length > 0 ? (\n <div className=\"bre-modality\">\n <span className=\"bre-effort-title\">{t('defaultEffortLabel')}</span>\n <label className=\"bre-modality-row\">\n <select\n className=\"bre-select\"\n disabled={disabled}\n aria-label={t('defaultEffortLabel') + ' ' + String(index + 1)}\n value={defaultEffort}\n onChange={(event) => { patchDefaultEffort(event.target.value) }}\n >\n <option value=\"\">{t('defaultEffortUnset')}</option>\n {/* A pick pushed from outside the draft (a hand-edited\n document) can name a level this draft no longer declares:\n render it anyway, so the select shows the real state\n instead of silently blanking to the first option. */}\n {(defaultEffort !== '' && !armedLevels.includes(defaultEffort as (typeof LEVEL_ORDER)[number])\n ? [...armedLevels, defaultEffort as (typeof LEVEL_ORDER)[number]]\n : armedLevels\n ).map(level => (\n <option key={level} value={level}>{t('level_' + level)}</option>\n ))}\n </select>\n {defaultEffort !== '' ? (\n <button\n type=\"button\"\n className=\"bre-link-button bre-modality-clear\"\n disabled={disabled}\n onClick={() => { patchDefaultEffort('') }}\n >\n {t('clearDefaultEffort')}\n </button>\n ) : null}\n </label>\n <p className=\"bre-modality-note\">{t('defaultEffortHint')}</p>\n </div>\n ) : null}\n {/* The official page owns input types from 0.1.6-alpha.2 on (its\n ModelInputTypes control was sniffed in this row's disclosure): the\n official editor is the single surface then, so this section stands\n down. Older kernels keep it. */}\n {officialInputTypes ? null : (\n <div className=\"bre-modality\">\n <span className=\"bre-effort-title\">{t('inputModality')}</span>\n <label className=\"bre-modality-row\">\n <input\n type=\"checkbox\"\n checked={modality.declared && modality.image}\n disabled={disabled}\n aria-label={t('modalityImage') + ' ' + String(index + 1)}\n onChange={(event) => { patchImage(event.target.checked) }}\n />\n <span className=\"bre-effort-level\">{t('modalityImage')}</span>\n {modality.declared\n ? (\n <button\n type=\"button\"\n className=\"bre-link-button bre-modality-clear\"\n disabled={disabled}\n onClick={clearModality}\n >\n {t('clearDeclaration')}\n </button>\n )\n : null}\n </label>\n {!modality.declared ? <p className=\"bre-modality-note\">{t('modalityInherit')}</p> : null}\n </div>\n )}\n {(routeApi ?? '').toLowerCase() === 'openai-completions' ? (\n <div className=\"bre-compat\">\n <span className=\"bre-effort-title\">{t('compatTitle')}</span>\n <label className=\"bre-compat-row\">\n <span className=\"bre-compat-label\">{t('budgetFieldLabel')}</span>\n <select\n className=\"bre-select\"\n disabled={disabled}\n aria-label={t('budgetFieldLabel') + ' ' + String(index + 1)}\n value={budgetField}\n onChange={(event) => { patchBudgetField(event.target.value) }}\n >\n <option value=\"\">{t('budgetUnset')}</option>\n <option value=\"thinking_token_budget\">thinking_token_budget</option>\n <option value=\"thinking_budget\">thinking_budget</option>\n <option value=\"thinking_budget_tokens\">thinking_budget_tokens</option>\n </select>\n <span className=\"bre-compat-hint\">{t('budgetFieldHint')}</span>\n </label>\n <div className=\"bre-compat-row\">\n <label className=\"bre-compat-field\">\n <span className=\"bre-compat-label\">{t('priorityLabel')}</span>\n <input\n type=\"text\"\n inputMode=\"numeric\"\n className=\"bre-text-input\"\n value={priorityText}\n disabled={disabled}\n placeholder={t('priorityPlaceholder')}\n aria-label={t('priorityLabel') + ' ' + String(index + 1)}\n onChange={(event) => { patchPriority(event.target.value) }}\n />\n </label>\n <span className=\"bre-compat-hint\">{t('priorityHint')}</span>\n {priorityText.trim() !== '' && !/^-?\\d+$/.test(priorityText.trim()) ? <p className=\"bre-effort-note bre-error\">{t('priorityInvalid')}</p> : null}\n </div>\n {initialCompat?.supportsThinkingTokenBudget === true && initialCompat?.thinkingTokenBudgetField === undefined ? <p className=\"bre-effort-note\">{t('aliasMigrated')}</p> : null}\n </div>\n ) : null}\n {(routeApi ?? '').toLowerCase() === 'openai-responses' ? (\n <div className=\"bre-compat\">\n <span className=\"bre-effort-title\">{t('compatTitle')}</span>\n <label className=\"bre-compat-row\">\n <span className=\"bre-compat-label\">{t('maxOutputLabel')}</span>\n <select\n className=\"bre-select\"\n disabled={disabled}\n aria-label={t('maxOutputLabel') + ' ' + String(index + 1)}\n value={maxOutput}\n onChange={(event) => { patchMaxOutput(event.target.value) }}\n >\n <option value=\"\">{t('maxOutputUnset')}</option>\n <option value=\"true\">{t('maxOutputOn')}</option>\n <option value=\"false\">{t('maxOutputOff')}</option>\n </select>\n <span className=\"bre-compat-hint\">{t('maxOutputHint')}</span>\n </label>\n </div>\n ) : null}\n {staged ? <p className=\"bre-effort-note\">{t('stagedHint')}</p> : null}\n {initialEfforts === false ? <p className=\"bre-effort-note\">{t('reasoningDisabled')}</p> : null}\n {wireRisk === undefined\n ? null\n : <p className=\"bre-effort-note\">{t(wireRisk === 'thinking-disabled' ? 'defaultRiskDisabled' : 'defaultRiskQwen')}</p>}\n {/* Unsetting says what it does: the save path below turns an\n all-off draft over a stored declaration into the durable unset\n marker, i.e. bare provider-default requests (the relay-compat\n mode of issue #2). Never-declared rows map to keep, so the hint\n stays hidden there exactly as the write does. */}\n {buildIntent(draft) === undefined && initialEfforts !== undefined\n ? <p className=\"bre-effort-note\">{t('bareHint')}</p> : null}\n {suggested !== undefined ? (\n <div className=\"bre-suggestion\">\n <p className=\"bre-effort-note\">\n {t('appliedHint', { source: suggestedSource, confidence: t('confidence_' + suggestedConfidence) })}\n </p>\n {suggestedInputSource === undefined\n ? null\n : (\n <p className=\"bre-effort-note\">\n {suggestedInputSource === 'endpoint'\n ? t('inputHintEndpoint')\n : suggestedInputSource === 'knowledge'\n ? t('inputHintKnowledge')\n : t('inputHintHeuristic')}\n </p>\n )}\n {referenceContext === undefined && referenceMaxTokens === undefined\n ? null\n : (\n <div className=\"bre-reference\">\n <span className=\"bre-reference-title\">{t('referenceTitle')}</span>\n <span className=\"bre-reference-values\">\n {referenceContext === undefined ? null : (\n <span>{t('contextWindowLabel')} <b>{COUNT.format(referenceContext)}</b></span>\n )}\n {referenceMaxTokens === undefined ? null : (\n <span>{t('maxTokensLabel')} <b>{COUNT.format(referenceMaxTokens)}</b></span>\n )}\n </span>\n </div>\n )}\n </div>\n ) : null}\n {message === undefined ? null : (\n <p className={'bre-effort-message bre-' + message.kind} role={message.kind === 'error' ? 'alert' : 'status'}>\n {message.text}\n </p>\n )}\n {!changed\n ? null\n : (\n <p className=\"bre-effort-message bre-info\" role=\"status\">\n {t('pendingSave')}\n </p>\n )}\n {!changed ? null : <p className=\"bre-effort-note\">{t('pendingHint')}</p>}\n {/* No commit button since C2: the row's edits land with the official\n card's own Save (issue #7), so the only action left is discarding\n them. A card-level commit would ride the frozen revision baseline\n that made that Save fail in the first place. */}\n <div className=\"bre-effort-actions\">\n <button\n type=\"button\"\n className=\"bre-secondary-button\"\n disabled={disabled || !changed}\n onClick={reset}\n >\n {t('reset')}\n </button>\n </div>\n </div>\n ) : null}\n </div>\n )\n}\n\nexport type { DraftLevels }\n", "/**\n * Pure draft logic of the thinking-effort editor: mapping a stored declaration\n * to the editor's checkbox draft and back, plus semantic comparison. Kept out\n * of the component so the intent rules are unit-testable without React.\n *\n * @module dsh-model-think-level/client/effort\n */\n\nimport { THINKING_LEVELS, type ReasoningEfforts, type ThinkingLevel } from '../knowledge.js'\n\n/** The escalation order, matching the schema's ladder (same tuple, local alias). */\nexport const LEVEL_ORDER: readonly ThinkingLevel[] = THINKING_LEVELS\n\n/** Draft state of one level's wire value. */\nexport type DraftLevels = Record<ThinkingLevel, { on: boolean; wire: string }>\n\n/** What the editor can write: unset (no declaration), disabled (false), or a dict. */\nexport type EffortIntent = ReasoningEfforts | false | undefined\n\n/**\n * The draft of one declaration. A `false` declaration (reasoning disabled)\n * shows as the off level armed \u2014 saving it writes `false`; an unset\n * declaration shows as every level off \u2014 saving it unsets the field.\n */\nexport function draftFrom(efforts: EffortIntent): DraftLevels {\n const draft = {} as DraftLevels\n for (const level of LEVEL_ORDER) {\n if (efforts === undefined) {\n draft[level] = { on: false, wire: '' }\n continue\n }\n if (efforts === false) {\n draft[level] = level === 'off' ? { on: true, wire: '' } : { on: false, wire: '' }\n continue\n }\n const wire = efforts[level]\n draft[level] = {\n on: wire !== undefined,\n wire: wire === null ? '' : typeof wire === 'string' ? wire : '',\n }\n }\n return draft\n}\n\n/**\n * Resolve a draft to the value to write:\n * - no level armed \u2192 `undefined` (unset the declaration);\n * - only `off` armed \u2192 `false` (reasoning disabled);\n * - at least one thinking level \u2192 the dict (with `off` when armed).\n *\n * The off level's wire value is meaningful: `null` means \"send nothing\"\n * (pi-ai lets the endpoint default apply), while a string is what\n * dispatch sends to close thinking \u2014 `none` on OpenAI-style endpoints,\n * or any non-null spelling to arm the deepseek/zai formats' explicit\n * `thinking: disabled`.\n */\nexport function buildIntent(draft: DraftLevels): EffortIntent {\n let off = false\n let thinking = false\n const out: ReasoningEfforts = {}\n for (const level of LEVEL_ORDER) {\n const cell = draft[level]\n if (!cell.on) continue\n if (level === 'off') {\n off = true\n const wire = cell.wire.trim()\n out.off = wire.length === 0 ? null : wire\n continue\n }\n thinking = true\n // A level switched on without a spelling defaults to its own name \u2014 the\n // convention every OpenAI-compatible endpoint follows.\n const wire = cell.wire.trim()\n out[level] = wire.length === 0 ? level : wire\n }\n if (thinking) return out\n return off ? false : undefined\n}\n\n/** Semantic equality of two intents, independent of object key order. */\nexport function sameEfforts(a: EffortIntent, b: EffortIntent): boolean {\n if (a === b) return true\n if (a === false || a === undefined || b === false || b === undefined) return false\n const aKeys = LEVEL_ORDER.filter(level => a[level] !== undefined)\n const bKeys = LEVEL_ORDER.filter(level => b[level] !== undefined)\n if (aKeys.length !== bKeys.length) return false\n return aKeys.every(level => a[level] === b[level])\n}\n", "/**\n * Default-wire risk: what pi-ai sends when a call names no effort.\n *\n * Three openai-completions thinking formats have no omit path: with no\n * effort selected they emit an off-equivalent even when the ladder offers no\n * `off` level (read from pi-ai 0.85 `dist/api/openai-completions.js`, kept in\n * sync by `scripts/wire-repro.mjs`). The generic `openai` format instead\n * sends nothing, so it is safe. This predicate names the risk for one\n * declared ladder so the editor can warn where the failure would otherwise\n * look inexplicable (issue #2: \"off \u5DF2\u5220\uFF0C\u6D4B\u8BD5\u9875\u7167\u6837 1210\").\n *\n * Pure data logic, no DOM: esbuild inlines it into the browser bundle.\n *\n * @module dsh-model-think-level/client/wire-preview\n */\n\nimport type { ReasoningEfforts } from '../knowledge.js'\n\n/** How a Default call fails on a forced-thinking ladder, by wire format. */\nexport type DefaultWireRisk =\n /** `thinking: {type: disabled}` -- rejected with 1210-class errors. */\n | 'thinking-disabled'\n /** `enable_thinking: false` -- rejected only where the upstream requires thinking on. */\n | 'enable-thinking-false'\n\n/**\n * The Default-wire risk of one declared ladder under one thinking format.\n * `false` (non-reasoning), absent declarations, ladders carrying `off`, and\n * formats with an omit path (generic `openai`, unknown) all answer\n * undefined: Default is safe there.\n */\nexport function defaultWireRisk(\n declared: ReasoningEfforts | false | undefined,\n thinkingFormat: string | undefined,\n): DefaultWireRisk | undefined {\n if (declared === undefined || declared === false) return undefined\n if ('off' in declared) return undefined\n if (thinkingFormat === 'zai' || thinkingFormat === 'deepseek') return 'thinking-disabled'\n if (thinkingFormat === 'qwen') return 'enable-thinking-false'\n return undefined\n}\n", "/**\n * The \"adapt every model of this provider\" request (user request \u2464).\n *\n * The seat beside the official fetch-models link and the per-row reasoning\n * editor are two DIFFERENT React roots: the seat is raw DOM this plugin writes\n * into the official catalogue head, while the editor is a root the injector\n * mounts into each model row's disclosure. They share no props, so the request\n * travels as one document event, and every editor whose own route matches\n * answers it by running the very same `autoAdapt()` its own \u81EA\u52A8\u9002\u914D link runs.\n *\n * That indirection is the point: adapting N models this way is byte-identical\n * to clicking that link N times, so the seat cannot drift from the control the\n * user already trusts \u2014 and it still reaches the models whose thinking switch\n * is OFF, which the link itself cannot (with the switch off the whole effort\n * card, link included, is not rendered at all).\n *\n * @module dsh-model-think-level/client/auto-effort\n */\n\n/** The event a seat dispatches and every mounted editor listens for. */\nexport const AUTO_EFFORT_EVENT = 'bre:auto-effort'\n\n/** The event's payload: the provider route whose models are to be adapted. */\nexport interface AutoEffortDetail {\n /** The provider route the request belongs to. */\n readonly route: string\n}\n\n/**\n * The tail of the one-at-a-time queue. A catalogue can carry dozens of models\n * and every adapt reads the provider's own model list, so the requests must not\n * all be in flight at once. The chain never rejects: a failed adapt reports\n * itself inside its own editor (the editor owns that message), and one bad row\n * must not strand the rows queued behind it.\n */\nlet tail: Promise<void> = Promise.resolve()\n\n/** Run one auto-adapt after every earlier one settled. */\nexport function queueAutoEffort(run: () => Promise<void> | void): void {\n tail = tail.then(run, run).then(() => undefined, () => undefined)\n}\n\n/** Ask every editor of `route` on this document to adapt its own model. */\nexport function requestAutoEffort(route: string, target: Document = document): void {\n target.dispatchEvent(new CustomEvent<AutoEffortDetail>(AUTO_EFFORT_EVENT, { detail: { route } }))\n}\n\n/**\n * Answer this document's auto-adapt requests until the returned disposer runs.\n * A request naming another route is ignored \u2014 one card is open at a time, but a\n * re-render can leave a previous card's rows mounted for a scan.\n */\nexport function listenAutoEffort(handler: (route: string) => void, target: Document = document): () => void {\n const listener = (event: Event): void => {\n const detail = (event as CustomEvent<AutoEffortDetail>).detail\n if (typeof detail?.route !== 'string' || detail.route.length === 0) return\n handler(detail.route)\n }\n target.addEventListener(AUTO_EFFORT_EVENT, listener)\n return () => { target.removeEventListener(AUTO_EFFORT_EVENT, listener) }\n}\n", "/**\n * The \"adapt every model of this provider\" seat, beside the official\n * fetch-models link (user request \u2464).\n *\n * The official catalogue head carries exactly one control \u2014 the link that\n * fetches the provider's model list \u2014 so a provider with twenty models makes\n * the user open twenty rows and click \u81EA\u52A8\u9002\u914D in each. The seat stands next to\n * that link and asks every model of the provider to adapt at once.\n *\n * It is raw DOM inside the host's own head for the same reason the provider\n * delete seat is: the plugin owns no slot there. It never moves a React-owned\n * node \u2014 it only inserts itself as the anchor's next sibling and re-inserts\n * itself when a re-render drops it \u2014 and every write is compare-before-write,\n * so a settled pass emits no mutation of its own.\n *\n * The click does not write anything itself: it publishes a request on the\n * document (see `../auto-effort.js`), which the per-model editors answer for\n * their own rows, and hands the route to `deps.onRequest`, which answers for\n * the models that have no editor on screen -- a collapsed row renders no\n * disclosure container, so nothing else on the page can serve it. The seat\n * therefore holds no write path of its own, and neither answer stages a\n * document the open card is already holding.\n *\n * @module dsh-model-think-level/client/injection/auto-effort-seat\n */\n\nimport { requestAutoEffort } from '../auto-effort.js'\n\n/** Marks the seat, and carries the route whose models it adapts. */\nconst SEAT_ATTR = 'data-bre-auto-effort'\n\n/** The card's catalogue, probed by class-name fragment (a CSS-module hash). */\nconst CATALOG_PROBE = '[class*=\"modelCatalog\"]'\n\n/** The catalogue's head row: the strip that carries the fetch link. */\nconst HEAD_PROBE = '[class*=\"modelListHead\"]'\n\n/** The host's own link-shaped control, probed the same way. */\nconst LINK_PROBE = 'button[class*=\"linkButton\"]'\n\n/** The seat's class: `bre-link-button` gives it the shared link metrics. */\nconst SEAT_CLASS = 'bre-link-button bre-auto-effort'\n\n/** One card's catalogue, paired with the route that card edits. */\nexport interface AutoEffortSeatTarget {\n /** The card's catalogue section. */\n readonly catalogue: HTMLElement\n /** The provider route the card edits. */\n readonly route: string\n}\n\nexport interface AutoEffortSeatDeps {\n /** The plugin's own translator, for the label and the tooltip. */\n t: (key: string, params?: Record<string, string | number>) => string\n /**\n * Adapt every model of the route that has no on-screen editor: the injector\n * owns the settings writes, so the seat only reports the click. Absent in\n * tests that only care where the seat is placed.\n */\n onRequest?: (route: string) => void\n}\n\n/**\n * The catalogue CONTAINERS inside a scope: the outermost matches only.\n *\n * {@link CATALOG_PROBE} is a class-name SUBSTRING, and the official catalogue\n * renders three more elements whose class names carry it \u2014 the heading, the\n * title and the meta nested inside the section. Only the outermost match IS a\n * catalogue, so the container is defined structurally here instead of by a\n * second list of class names to keep in step: seating a control in one of the\n * other three drops a button inside the heading, and the official\n * column-flex text then collapses to a character per line (the\n * empty-catalogue layout break).\n *\n * The sweep in {@link reconcileAutoEffortSeats} deliberately asks the NEAREST\n * match instead: a seat a previous pass left inside the heading belongs to no\n * catalogue this pass names, so it comes out.\n */\nexport function cataloguesOf(scope: HTMLElement): HTMLElement[] {\n return Array.from(scope.querySelectorAll<HTMLElement>(CATALOG_PROBE))\n .filter(candidate => (candidate.parentElement?.closest(CATALOG_PROBE) ?? null) === null)\n}\n\n/**\n * Give every target's catalogue head an auto-adapt seat. Idempotent: a head\n * that already has one is only relabelled and re-seated, and a seat whose\n * catalogue this pass no longer names is taken out.\n */\nexport function reconcileAutoEffortSeats(\n root: HTMLElement,\n targets: readonly AutoEffortSeatTarget[],\n deps: AutoEffortSeatDeps,\n): void {\n const wanted = new Map<HTMLElement, string>()\n for (const target of targets) {\n if (target.route.length === 0) continue\n if (!target.catalogue.isConnected) continue\n wanted.set(target.catalogue, target.route)\n }\n const label = deps.t('autoAdaptAll')\n const hint = deps.t('autoAdaptAllHint')\n for (const [catalogue, route] of wanted) {\n const head = catalogue.querySelector<HTMLElement>(HEAD_PROBE) ?? catalogue\n const seat = head.querySelector<HTMLButtonElement>(`[${SEAT_ATTR}]`) ?? createSeat(deps)\n // Beside the host's own link when the head renders one (the control the\n // user already reaches for), else at the head's end.\n const anchor = lastLink(head)\n if (anchor !== null) {\n if (anchor.nextElementSibling !== seat) anchor.after(seat)\n } else if (head.lastElementChild !== seat) {\n head.append(seat)\n }\n syncSeat(seat, route, label, hint)\n }\n // The target set is the only state the seat set may hold: a seat whose\n // catalogue is gone \u2014 or that a React re-render moved out of one \u2014 comes out.\n // The NEAREST match answers, so a seat a previous pass left inside the\n // heading (whose own class name carries the stem, but which is not a\n // catalogue) belongs to nothing this pass names and goes. One head holds one\n // seat: the loop above re-homes a nested seat to head level, which would\n // otherwise leave it beside the seat that was already there.\n const seated = new Set<HTMLElement>()\n for (const seat of Array.from(root.querySelectorAll<HTMLElement>(`[${SEAT_ATTR}]`))) {\n const catalogue = seat.closest<HTMLElement>(CATALOG_PROBE)\n if (catalogue === null || !wanted.has(catalogue) || seated.has(catalogue)) {\n seat.remove()\n continue\n }\n seated.add(catalogue)\n }\n}\n\n/** Take every seat out again (plugin dispose, HMR teardown). */\nexport function teardownAutoEffortSeats(): void {\n for (const seat of Array.from(document.querySelectorAll<HTMLElement>(`[${SEAT_ATTR}]`))) {\n seat.remove()\n }\n}\n\n/** The head's own fetch link: the LAST link-shaped button it renders. */\nfunction lastLink(head: HTMLElement): HTMLElement | null {\n const links = Array.from(head.querySelectorAll<HTMLElement>(LINK_PROBE))\n return links.length === 0 ? null : links[links.length - 1]\n}\n\nfunction createSeat(deps: AutoEffortSeatDeps): HTMLButtonElement {\n const seat = document.createElement('button')\n seat.className = SEAT_CLASS\n seat.setAttribute('type', 'button')\n seat.setAttribute(SEAT_ATTR, '')\n // The route is read at CLICK time, not captured: a create card's Provider ID\n // can be retyped after the seat was first placed, and the sync below rewrites\n // the attribute to whatever the card names now.\n seat.addEventListener('click', () => {\n const route = seat.getAttribute(SEAT_ATTR) ?? ''\n if (route.length === 0) return\n // Two answers, one click: the event reaches every mounted row's editor\n // (the expanded ones), and `onRequest` covers the rows the pass could not\n // mount -- a collapsed row has no disclosure container to mount into.\n requestAutoEffort(route)\n deps.onRequest?.(route)\n })\n return seat\n}\n\n/** Make the seat stand for this route, compare-before-write on every field. */\nfunction syncSeat(seat: HTMLElement, route: string, label: string, hint: string): void {\n if (seat.getAttribute(SEAT_ATTR) !== route) seat.setAttribute(SEAT_ATTR, route)\n // Guarded: the page's observer watches childList, and replacing the seat's\n // text node on every scan would make each pass feed the next one.\n if (seat.textContent !== label) seat.textContent = label\n // The label IS the accessible name (a text button names itself), so the\n // scope goes in the tooltip only: an aria-label that replaced the visible\n // word would break \"click \u81EA\u52A8\u83B7\u53D6\u601D\u8003\u7B49\u7EA7\" for voice control (WCAG 2.5.3).\n if (seat.getAttribute('title') !== hint) seat.setAttribute('title', hint)\n}\n", "/**\n * The DOM bypass injector: the piece that makes reasoning effort editable\n * *inside* the official Models page's model rows.\n *\n * The official Models-page slot contract's two sanctioned seats are both\n * coarser than a model row: the keyed `settings.models.provider-card`\n * renders per provider card and `settings.models.footer`\n * renders after the rows \u2014 neither reaches a single model row's editor\n * internals, so this plugin mounts its editor as a DOM contribution next to\n * the official per-model capacity disclosure. The anchor is the official\n * disclosure chevron button, found by aria-label in both locales (the\n * `modelAdvanced` dictionary value: \"Capacities\"/\u5BB9\u91CF through 0.1.6-alpha.1,\n * \"Model options\"/\u6A21\u578B\u9009\u9879 from 0.1.6-alpha.2); the editor is inserted into the\n * same disclosure container that holds the official context-window / max\n * tokens fields.\n *\n * Because this walks the official page's rendered DOM (class names and\n * structure that the harness can change), the injector is defensive by\n * construction:\n * - it re-scans on every DOM mutation (a routed page, an open editor, an\n * applied form all re-render), and every scan reconciles idempotently;\n * - a model row that carries no disclosure yet is left alone and picked up\n * on the next mutation;\n * - if the official structure it depends on ever stops appearing, it simply\n * stops injecting \u2014 the settings page remains untouched.\n *\n * @module dsh-model-think-level/client/injection/models-page-editor\n */\n\nimport { AUTOFILL_MARKER, INPUT_UNSET_MARKER, PLUGIN_ID, UNSET_MARKER } from '../../constants.js'\nimport { suggestEfforts, type CompatSuggestion, type InputModalities, type ReasoningEfforts } from '../../knowledge.js'\nimport { modelsOf, routeFactsOf, isRecord } from '../../shared.js'\nimport { buildIntent, draftFrom, sameEfforts } from '../effort.js'\nimport {\n buildModalityIntent,\n compatDraftOf,\n ladderIntent,\n modalityFrom,\n pendingWriteOf,\n sameModality,\n} from '../EffortEditor.js'\nimport { compatOf, createEditorApi, defaultEffortOf, effortsOf, inputOf, nameOf, providersOf, writeModelRows, type RowIntent } from '../ops.js'\nimport type { EffortEditorApi, EffortWriteIntent, HeldWrite, RemoteApi, SettingsJoin } from '../types.js'\nimport { cataloguesOf, reconcileAutoEffortSeats, type AutoEffortSeatTarget } from './auto-effort-seat.js'\nimport { panelRoot } from './mount.js'\n\nexport type { SettingsJoin }\n\n/**\n * Own-property membership over the providers dict. Plain `in` would answer\n * true for inherited names ('constructor', 'toString', '__proto__'\u2026), so a\n * route typed as one of those in the create card would resolve against\n * prototype members instead of being treated as unsaved.\n */\nfunction hasOwn(object: Record<string, unknown>, key: string): boolean {\n return Object.prototype.hasOwnProperty.call(object, key)\n}\n\n/**\n * Localized aria-labels of the official settings controls this injector\n * anchors to, in the language the host is currently rendering.\n *\n * The host renders these labels from its own `settings.models` dictionary\n * (`ui-settings-models`), so the plugin must read them through the same\n * channel instead of pinning the two languages it knows about: a host\n * language pack that adds, say, Japanese would otherwise relabel every\n * control and leave the injector unable to find a single model row. Each\n * field carries the active language first and English last \u2014 English is the\n * host's own fallback floor, so it doubles as ours.\n *\n * Every field is a LIST because the official labels are numbered per row\n * (\"Model ID 1\", \"\u30E2\u30C7\u30EB ID 2\", \u2026) and are matched by prefix.\n */\nexport interface HostLabels {\n /** The per-row disclosure button (\"Model options\", \"\u6A21\u578B\u9009\u9879\", \"Capacities\", \"\u5BB9\u91CF\", \u2026). */\n capacity: readonly string[]\n /** The model-id input. */\n modelId: readonly string[]\n /** The per-row display-name input. */\n modelName: readonly string[]\n /** The create card's route-id input. */\n routeId: readonly string[]\n /** The create card's endpoint input. */\n baseUrl: readonly string[]\n /** The create card's protocol select. */\n apiProtocol: readonly string[]\n /**\n * The editing card's commit button. C2 drives the plugin's landing write off\n * the official Save, so the button is resolved when the action row's own\n * structure cannot be read (the buttons ARE the last child pair of\n * `[class*=\"editorActions\"]`; the copy is the fallback).\n */\n apply: readonly string[]\n /** The editing card's dismiss button (the commit's left-hand sibling). */\n cancel: readonly string[]\n /**\n * A provider row's edit button, as the official `providerCopy` template\n * (\"Edit {provider}\" / \"\u7F16\u8F91 {provider}\"). Its `{provider}` slot carries\n * `Name (route)`, which is how a row names its provider even when the row\n * offers this plugin no seat to stamp that name on.\n */\n editProvider: readonly string[]\n}\n\n/** A row's identity as found on the page, resolved from the settings join. */\ninterface FoundModel {\n /**\n * The official disclosure container that holds the capacity fields, when the\n * row is EXPANDED. A collapsed row renders no such container at all (the\n * official editor returns null for it), so a row the user has not unfolded\n * still counts as found: it is a model of the provider and everything that\n * works per PROVIDER (the auto-adapt seat, the provider-wide adapt) must see\n * it. Only the per-row editor mount needs the container.\n */\n container?: HTMLElement\n /** The trigger's OWN model row element (row-scoped input reads). */\n row: HTMLElement\n /** The model id read from the row's \"Model ID\" input. */\n modelId: string\n /** The nearest card element (for route resolution). */\n card: HTMLElement\n}\n\n/** The join the injector renders from. */\nexport interface InjectorDeps {\n /**\n * The settings Remote face for THIS scan/write. The kernel mounts\n * `remote.settings` as an injectable service, so the plugin's own top-level\n * `inject` declaration guarantees it before apply runs \u2014 no runtime seat\n * probing remains.\n */\n api: RemoteApi\n /** Read the pi-ai namespace plus writability from the settings join. */\n describeNamespace(): Promise<SettingsJoin>\n /** Localized copy. */\n t: (key: string, params?: Record<string, string | number>) => string\n /**\n * The official controls' aria-labels in the ACTIVE language. A thunk, not a\n * value: the host resolves labels through the same fallback chain it renders\n * from, so re-reading per scan is what keeps a language switch (and a late\n * language pack) in step with the page it has to find controls on.\n */\n labels(): HostLabels\n /** Mount one editor into a container (React); render() updates its props in place. */\n mount(container: HTMLElement, props: EditorMountProps): MountedEditor\n /**\n * Runs after an idle pass landed everything the session held back. The\n * browser half hangs its autofill complement here: \"the user stopped\n * editing\" is the only moment this side may safely write the document.\n */\n onIdle?(): void\n /**\n * Arm a timed retry after a failed idle pass. The backoff clock only helps\n * if something wakes the injector when it expires; a settled page has no DOM\n * mutation to schedule the next scan. Optional so tests drive passes by hand.\n */\n onBackoff?(delayMs: number): void\n}\n\n/** One mounted editor: unmount disposes the React root; render swaps props in place. */\nexport interface MountedEditor {\n unmount(): void\n render(props: EditorMountProps): void\n}\n\n/** Props handed to the editor mount for one model row. */\nexport interface EditorMountProps {\n /** The route being edited. */\n route: string\n /** Route display name (shown in the editor header). */\n routeDisplayName: string\n /** Route wire protocol, when configured. */\n routeApi?: string\n /** Route endpoint, when configured. */\n routeBaseURL?: string\n /** Model id. */\n modelId: string\n /** Model display name, when one is set. */\n modelName?: string\n /** The model's current reasoningEfforts declaration. */\n efforts?: false | ReasoningEfforts\n /** The model's current input-modality declaration. */\n input?: InputModalities\n /** The model's stored compat block (passthrough; the editor merges suggestions over it). */\n compat?: CompatSuggestion\n /** The model's stored per-model default-effort pick, when one is set. */\n defaultEffort?: string\n /** Row ordinal among the models found in this scan (for aria labels). */\n index: number\n /** True while the row is unsaved (a create card's draft route, or a new\n * model row on a saved route): Apply stages the declaration instead of\n * writing, and the injector lands it once the row is saved. */\n staged?: boolean\n /**\n * Whether the official Models page owns input types for this row: 0.1.6-alpha.2\n * renders its own ModelInputTypes control inside the disclosure. When true,\n * the editor hides its modality section \u2014 a CAPABILITY sniffed from the row\n * DOM, never a kernel version.\n */\n officialInputTypes?: boolean\n api: EffortEditorApi\n readOnly: boolean\n t: (key: string, params?: Record<string, string | number>) => string\n}\n\n/** Mutable scan state kept across reconcile invocations. */\nexport interface ScanState {\n /** Currently mounted editors, keyed by their container element. */\n mounted: Map<HTMLElement, { editor: MountedEditor; props: EditorMountProps }>\n /** The last describe promise, folded so scans never stack reads. */\n describePromise: Promise<SettingsJoin> | undefined\n /**\n * Declarations staged against routes that do not exist in the settings\n * document yet (the create card's typed route id), keyed route \u2192 model id.\n * Flushed automatically once a route appears. Mirrored into `sessionStorage`\n * so a same-document fiber cycle (HMR, disable-then-enable) keeps the user's\n * mid-edit staging; a real reload discards it (see {@link restoreLedger}).\n */\n pending: Map<string, Map<string, StagedDeclaration>>\n /**\n * Staged models that looked ghosted on the last scan (their route is saved,\n * but no matching row exists on the page anymore). Withdrawn once they miss\n * TWO consecutive scans, so one transient re-render gap cannot drop real\n * mid-edit staging. Mirrors {@link pending}'s key shape.\n */\n missedScans: Map<string, Set<string>>\n /**\n * Writes an ON-SCREEN editor asked for while the official card held the\n * document, keyed route \u2192 model id. Kept verbatim (this is the user's own\n * declaration, so no suggestion arbitration applies) and replayed the\n * moment the card is gone. Persisted beside its commit evidence, so a\n * same-document fiber cycle still lands a write the official Save\n * authorized, while a reload or a dismissed card drops it (see\n * {@link restoreLedger} / {@link forgetRoute}).\n */\n queued: Map<string, Map<string, HeldWrite>>\n /**\n * Whether an official editing card was on the page at the last reconcile.\n * `undefined` until the first scan. The `!== false` test is what makes the\n * idle pass run exactly once per editing session -- and once for a page\n * that loads straight into the models section without ever editing.\n */\n editing: boolean | undefined\n /**\n * Whether an idle pass is in flight. One pass at a time: two overlapping\n * passes would describe the same revision and then fight over it, turning a\n * clean replay into a self-inflicted `settings/conflict`.\n */\n flushing: boolean\n /** Consecutive failed idle passes; the backoff exponent. Reset on success. */\n flushFailures: number\n /** Earliest `Date.now()` at which the next retry may run (0 = any time). */\n nextFlushAt: number\n /**\n * Routes whose OFFICIAL card committed in this session (the user pressed its\n * commit button). The held ledgers land per route: only the routes the user\n * actually saved are written, because the official card's Save is now what\n * commits this plugin's edits too (issue #7 / C2). An intent the user\n * dismissed without saving is dropped, exactly like the card's own fields.\n */\n committing: Set<string>\n /**\n * Routes with a provider-wide adapt in flight (the auto-adapt seat's click).\n * Nothing debounces that click: a second one arriving while the first pass is\n * still describing and suggesting would re-ask every model and re-commit the\n * same bytes, so the route is simply not re-entered until its pass ends.\n */\n adapting: Set<string>\n /**\n * Whether the official action row has failed to yield usable buttons for\n * every card seen so far. When it has, the landing decision degrades to\n * \"the card went away, so write it\" -- writing too much is recoverable,\n * silently losing the user's declaration is not.\n */\n signalsUnavailable: boolean\n /** Commit buttons already wired, so a re-scan never double-registers. */\n submitWired: WeakSet<Element>\n /** Cancel buttons already wired. */\n cancelWired: WeakSet<Element>\n}\n\n/**\n * One staged declaration: the level set plus the suggestion's compat block and\n * modalities. A 'keep' effort travels untouched to the flush write. The\n * per-model default-effort pick (issue #4) rides along: a string to write,\n * null to clear, absent to touch nothing.\n *\n * Design note: staging cannot express a deliberate UNSET. An all-clear draft\n * stages `keep`/no-input, whose flush resolves to null and withdraws the\n * whole staging -- host autofill then fills its suggestion back in. To record\n * \"declare nothing\" durably, save the row first and apply an all-clear write\n * through the saved-row seam (the durable unset marker).\n */\nexport interface StagedDeclaration {\n efforts: Exclude<EffortWriteIntent, undefined>\n compat?: CompatSuggestion\n input?: InputModalities\n defaultEffort?: string | null\n}\n\n/**\n * The held-write ledgers as `sessionStorage` keeps them: the two maps, plus the\n * document identity and the commit evidence a same-document restore needs.\n * Every entry is the user's own declaration, already JSON-shaped, so the file\n * IS the intent (no derivation on the way back in).\n */\ninterface LedgerFile {\n /** Identity of the document that wrote the file; a different one is a reload. */\n document: string\n /**\n * Routes whose held write had COMMIT EVIDENCE when persisted (the user\n * pressed the official Save). A restored queued entry without its route here\n * is an abandoned edit and is dropped, matching the official draft.\n */\n committed: string[]\n pending: [string, [string, StagedDeclaration][]][]\n queued: [string, [string, HeldWrite][]][]\n}\n\n/**\n * The `sessionStorage` ledger key, and the module-local sentinel identifying\n * the CURRENT document across a same-page fiber cycle. The sentinel lives on\n * the window, so a real reload (a fresh window) reads a different id and the\n * old file is discarded; HMR / disable-enable keeps it.\n */\nconst LEDGER_DOCUMENT_KEY = '__breLedgerDocument'\n\n/** The current document's ledger identity, minting one on first use. */\nfunction documentId(): string {\n const holder = globalThis as { [LEDGER_DOCUMENT_KEY]?: string }\n return (holder[LEDGER_DOCUMENT_KEY] ??= `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`)\n}\n\n/**\n * The `sessionStorage` slot holding both ledgers. Session-scoped on purpose:\n * the intents belong to the card session the user was working in, and a NEW\n * tab must not inherit another tab's half-finished edit.\n */\nconst LEDGER_KEY = 'bre:held-writes:v1'\n\n/** `sessionStorage`, or undefined when the environment has none (node, privacy mode). */\nfunction ledgerStorage(): Storage | undefined {\n try {\n return typeof sessionStorage === 'undefined' ? undefined : sessionStorage\n } catch {\n return undefined\n }\n}\n\n/**\n * Mirror both ledgers into `sessionStorage`. Called after every change and\n * best-effort by nature: an unavailable or full storage keeps the ledgers in\n * memory alone (the behaviour before they were persisted), never an error the\n * user has to see.\n */\nfunction persistLedger(state: ScanState): void {\n const store = ledgerStorage()\n if (store === undefined) return\n try {\n if (state.pending.size === 0 && state.queued.size === 0) {\n store.removeItem(LEDGER_KEY)\n return\n }\n const file: LedgerFile = {\n document: documentId(),\n // Only routes that still hold a write matter: a spent marker is never\n // restored (and flushQueued prunes it).\n committed: [...state.committing].filter(route => state.queued.has(route)),\n pending: [...state.pending].map(([route, models]) => [route, [...models]]),\n queued: [...state.queued].map(([route, models]) => [route, [...models]]),\n }\n store.setItem(LEDGER_KEY, JSON.stringify(file))\n } catch {\n // Quota / disabled storage: the in-memory ledgers remain authoritative.\n }\n}\n\n/** One ledger entry as the file carries it, or undefined when malformed. */\nfunction ledgerEntry<T>(value: unknown, validate?: (write: unknown) => boolean): [string, [string, T][]][] | undefined {\n if (!Array.isArray(value)) return undefined\n const routes: [string, [string, T][]][] = []\n for (const row of value) {\n if (!Array.isArray(row) || row.length !== 2) return undefined\n const [route, models] = row as [unknown, unknown]\n if (typeof route !== 'string' || !Array.isArray(models)) return undefined\n const entries: [string, T][] = []\n for (const entry of models) {\n if (!Array.isArray(entry) || entry.length !== 2) return undefined\n const [modelId, write] = entry as [unknown, unknown]\n if (typeof modelId !== 'string' || !isRecord(write)) return undefined\n if (validate !== undefined && !validate(write)) return undefined\n entries.push([modelId, write as T])\n }\n routes.push([route, entries])\n }\n return routes\n}\n\n/**\n * Restore the ledgers from `sessionStorage`.\n *\n * A file written by a PREVIOUS document is a reload: the official card's own\n * draft would have died with it, so both ledgers are discarded outright. A\n * same-document file (HMR / disable-enable) comes back, but a held write is\n * restored ONLY with commit evidence -- an edit the user never saved must not\n * be resurrected behind a later Save. A malformed or foreign file is ignored\n * wholesale rather than partially applied: a half-read intent is worse than a\n * dropped one on a document the user can edit by hand.\n * @param state - the fresh scan state to fill.\n */\nfunction restoreLedger(state: ScanState): void {\n const store = ledgerStorage()\n if (store === undefined) return\n try {\n const raw = store.getItem(LEDGER_KEY)\n if (raw === null) return\n const parsed = JSON.parse(raw) as Partial<LedgerFile>\n if (parsed.document !== documentId()) return\n const pending = ledgerEntry<StagedDeclaration>(parsed.pending)\n const queued = ledgerEntry<HeldWrite>(parsed.queued, isHeldWrite)\n if (pending === undefined || queued === undefined) return\n const committed = Array.isArray(parsed.committed)\n ? parsed.committed.filter((route): route is string => typeof route === 'string')\n : []\n for (const [route, models] of pending) state.pending.set(route, new Map(models))\n for (const [route, models] of queued) {\n // No evidence = the user never saved this route: drop it.\n if (!committed.includes(route)) continue\n state.queued.set(route, new Map(models))\n state.committing.add(route)\n }\n } catch {\n // Unreadable file: start clean.\n }\n}\n\n/**\n * Land whatever the session held back, best effort, when the fiber is about to\n * go away (plugin disable / HMR). The ledgers STAY in `sessionStorage` (with\n * their commit evidence), so the next fiber in the same document picks the work\n * up; a flush the runtime cuts short is simply retried by that fiber.\n * @param state - the scan state whose ledgers to drain.\n * @param deps - the injection dependencies.\n */\nexport function flushOnTeardown(state: ScanState, deps: InjectorDeps): void {\n void (async () => {\n try {\n await flushQueued(deps, state)\n await flushPending(deps, state)\n } catch (error) {\n console.error(`[bre] teardown flush failed: ${error instanceof Error ? error.message : String(error)}`)\n }\n })()\n}\n\n/**\n * The page is going away: land the committed intents best effort, then CLEAR\n * the ledger. Unlike a fiber cycle, a reload discards the official card's own\n * draft, so the plugin's must go too -- anything the cut-short flush could not\n * land must not be resurrected by the next document. (The window sentinel\n * covers the case pagehide never fires: crash / plugin already disabled.)\n * @param state - the scan state whose ledgers to drain.\n * @param deps - the injection dependencies.\n */\nexport function flushOnUnload(state: ScanState, deps: InjectorDeps): void {\n // The page is going away: an open card's frozen baseline no longer matters,\n // so the in-flight fence must not block this last best-effort landing.\n void (async () => {\n try {\n await flushQueued(deps, state, true)\n await flushPending(deps, state, true)\n } catch (error) {\n console.error(`[bre] unload flush failed: ${error instanceof Error ? error.message : String(error)}`)\n } finally {\n try {\n ledgerStorage()?.removeItem(LEDGER_KEY)\n } catch {\n // Disabled storage: nothing to clear.\n }\n }\n })()\n}\n\nexport function createScanState(): ScanState {\n const state: ScanState = {\n mounted: new Map(),\n describePromise: undefined,\n pending: new Map(),\n missedScans: new Map(),\n queued: new Map(),\n editing: undefined,\n flushing: false,\n flushFailures: 0,\n nextFlushAt: 0,\n committing: new Set(),\n adapting: new Set(),\n signalsUnavailable: false,\n submitWired: new WeakSet(),\n cancelWired: new WeakSet(),\n }\n restoreLedger(state)\n return state\n}\n\n/**\n * Land everything the session held back, one pass at a time.\n *\n * A refusal keeps its intent in the ledger and pushes the next attempt out on\n * a backoff, so a settled document is not hammered while a genuinely stuck\n * write (a refused compat key, a vanished route) is retried indefinitely\n * instead of being lost on the first failure.\n * @param deps - the injection dependencies.\n * @param state - mutable scan state.\n */\nasync function runIdlePass(deps: InjectorDeps, state: ScanState): Promise<void> {\n if (state.flushing) return\n state.flushing = true\n try {\n const queuedFailed = await flushQueued(deps, state)\n const pendingFailed = await flushPending(deps, state)\n if (queuedFailed || pendingFailed) {\n // A refusal arrives as a VALUE, not a throw: folding it into the same\n // backoff keeps a doomed route from being re-mutated on every scan, and\n // puts both failure shapes (refused, unreachable) on one clock.\n armBackoff(deps, state)\n } else {\n state.flushFailures = 0\n state.nextFlushAt = 0\n }\n } catch (error) {\n armBackoff(deps, state)\n console.error(`[bre] idle flush failed: ${error instanceof Error ? error.message : String(error)}`)\n } finally {\n state.flushing = false\n }\n // The hook is a courtesy seat for the browser half's autofill: a fault in\n // it must not reject the pass (the caller void-s the promise) and become an\n // unhandled rejection.\n try {\n deps.onIdle?.()\n } catch (error) {\n console.error(`[bre] idle hook failed: ${error instanceof Error ? error.message : String(error)}`)\n }\n}\n\n/**\n * Push the next attempt out on the exponential backoff and tell the host to\n * wake the injector when it expires (a settled page has no DOM mutation).\n */\nfunction armBackoff(deps: InjectorDeps, state: ScanState): void {\n state.flushFailures += 1\n const delay = Math.min(2 ** state.flushFailures * 500, 30_000)\n state.nextFlushAt = Date.now() + delay\n deps.onBackoff?.(delay)\n}\n\n/** Whether either ledger still holds work the idle pass has to land. */\nfunction hasOutstanding(state: ScanState): boolean {\n return state.queued.size > 0 || state.pending.size > 0\n}\n\n/**\n * Record (or, for `undefined`, withdraw) one staged declaration. A route with\n * no staged models left leaves the store entirely.\n */\nexport function stageEffortsInto(\n state: ScanState,\n route: string,\n modelId: string,\n efforts: EffortWriteIntent,\n compat?: CompatSuggestion,\n /**\n * The modality part. `null` is a deliberate unset (the editor's \"clear\n * declaration\" on an unsaved route) and stores as such: the staged flush\n * reads it back verbatim, exactly like the saved-row seam does.\n */\n input?: InputModalities | null,\n defaultEffort?: string | null,\n): void {\n try {\n const models = state.pending.get(route)\n if (efforts === undefined) {\n if (models === undefined) return\n models.delete(modelId)\n if (models.size === 0) state.pending.delete(route)\n return\n }\n // 'keep' is storable: a modality-only staging must survive the flush as a\n // declaration that touches everything EXCEPT the ladder.\n state.pending.set(route, (models ?? new Map()).set(modelId, {\n efforts,\n ...(compat === undefined ? {} : { compat }),\n ...(input === undefined ? {} : { input }),\n ...(defaultEffort === undefined ? {} : { defaultEffort }),\n }))\n } finally {\n // Both arms changed the ledger (or provably did not): persisting here\n // keeps the early returns from having to remember it.\n persistLedger(state)\n }\n}\n\n/**\n * Record (or replace) the write an on-screen editor asked for while the\n * official card held the document. The latest intent for a row wins: the user\n * may apply twice before closing the card.\n * @param state - mutable scan state.\n * @param route - the route being edited.\n * @param modelId - the model id being edited.\n * @param write - the full intent, kept verbatim for the replay.\n */\nexport function queueWriteInto(state: ScanState, route: string, modelId: string, write: HeldWrite): void {\n state.queued.set(route, (state.queued.get(route) ?? new Map()).set(modelId, write))\n persistLedger(state)\n}\n\n/**\n * Withdraw one row's staged intent (the create card's erase-that-row flow, and\n * the editor's Reset on a route the document does not hold yet). Idempotent,\n * and it clears the route entry once its last model is gone.\n * @param state - mutable scan state.\n * @param route - the route the staging was made against.\n * @param modelId - the model whose intent to drop.\n */\nexport function withdrawStaged(state: ScanState, route: string, modelId: string): void {\n const models = state.pending.get(route)\n if (models === undefined) return\n models.delete(modelId)\n if (models.size === 0) state.pending.delete(route)\n persistLedger(state)\n}\n\n/**\n * Withdraw one row's held intent (the editor's own Reset, or any later\n * \"discard this edit\"). Idempotent, and it clears the route entry once its\n * last model is gone.\n * @param state - mutable scan state.\n * @param route - the route the editor was editing.\n * @param modelId - the model whose intent to drop.\n */\nexport function withdrawHeldWrite(state: ScanState, route: string, modelId: string): void {\n const models = state.queued.get(route)\n if (models === undefined) return\n models.delete(modelId)\n if (models.size === 0) state.queued.delete(route)\n persistLedger(state)\n}\n\n/**\n * Withdraw a row's intent wherever it landed.\n *\n * The editor's Reset is the caller and it does not know which ledger its own\n * `commit` fed \u2014 the create card stages, a saved row queues, and the same row\n * can move between the two as the user edits the route id. Discarding only one\n * of them would leave the Reset cosmetic on the other: the official Save would\n * still write edits the user explicitly threw away.\n * @param state - mutable scan state.\n * @param route - the route the editor was editing.\n * @param modelId - the model whose intent to drop.\n */\nexport function withdrawIntent(state: ScanState, route: string, modelId: string): void {\n withdrawStaged(state, route, modelId)\n withdrawHeldWrite(state, route, modelId)\n}\n\n/**\n * Drop every intent one route holds, right now: the official card's cancel.\n *\n * The card's own fields die with the dismissal, so the plugin's must too -- and\n * they must die at the CLICK, not on some later scan. A deferred \"discarded\"\n * marker would outlive the card that set it (a dismiss carrying no plugin edit\n * leaves nothing for a later pass to drain), and the next card for that same\n * route would then be read as already-dismissed: its Save would never be\n * wired, and the user's fresh edit would be dropped silently. Clearing the\n * ledgers here leaves no state at all to misinterpret -- including the staged\n * declaration a dismissed CREATE card was holding for a route that does not\n * exist yet, which would otherwise land the moment that route appeared.\n *\n * Safe by construction: the official page keeps ONE editing card at a time\n * (create and edit mutually exclusive), so the route this resolves is the only\n * card that could own these entries.\n * @param state - mutable scan state.\n * @param route - the route whose official card was dismissed.\n */\nexport function forgetRoute(state: ScanState, route: string): void {\n state.queued.delete(route)\n state.pending.delete(route)\n state.missedScans.delete(route)\n state.committing.delete(route)\n persistLedger(state)\n}\n\n/**\n * The write intents one staged declaration survives against the SAVED row,\n * decided PER PART: staging never overwrites what the document already says,\n * but a part the document took over must not silence its sibling. Returns\n * null when nothing is left to write (the model vanished from the card, or\n * every part was taken over / is a keep) -- the caller withdraws the staging.\n */\nexport interface EffectiveStagedIntents {\n efforts: EffortWriteIntent\n compat?: CompatSuggestion\n input?: InputModalities\n /** The staged default-effort pick, when the staging carried one. */\n defaultEffort?: string | null\n /**\n * Compat fields the edit OWNS and left empty, so \"unset\" can mean unset\n * instead of keeping the last choice forever. Computed by the editor's own\n * `clearedCompatKeys` against the route's protocol, never against the whole\n * stored block: a hand-tuned field the editor never showed survives.\n *\n * A STAGED flush never sets this. Staging carries a ladder and a modality,\n * not a compat decision, and the host autofill writes the knowledge base's\n * compat block into the very row it fills -- so clearing here would delete\n * the suggestion the row is supposed to start from (the same \"the plugin's\n * own proposal is not a user decision\" rule the ladder follows).\n */\n clearCompatKeys?: readonly string[]\n}\n\nexport function effectiveStagedIntents(\n declaration: StagedDeclaration,\n current: Record<string, unknown> | undefined,\n autofill?: AutofillFootprint,\n): EffectiveStagedIntents | null {\n if (current === undefined) return null\n // Ladder part: a declaration or a deliberate-unset marker on the row owns\n // it \u2014 EXCEPT when the stored declaration is this plugin's own host autofill\n // (the knowledge base proposal it lays down in the route-creation window).\n // That is a suggestion, not a user decision, so a staged intent outranks it;\n // anything else (a hand-tuned ladder, a marker) is a real takeover. 'keep'\n // means the staging never carried the ladder anyway.\n //\n // Provenance first: a ladder carrying the host autofill's marker IS the\n // knowledge base's proposal, whatever its bytes are. The host fills\n // in-process the instant a provider is committed, so by the time this flush\n // runs its own suggestion is already in the document \u2014 byte equality alone\n // then reads the user's staged intent as a document takeover and silently\n // drops it (the reported \"configured it on the card, saved, and it is gone\").\n const ladderMarked =\n current[UNSET_MARKER] !== true\n && current['reasoningEfforts'] !== undefined\n && typeof current[AUTOFILL_MARKER] === 'number'\n const ladderAutofilled =\n ladderMarked\n || (current[UNSET_MARKER] !== true\n && autofill?.efforts !== undefined\n && sameEffortsValue(current['reasoningEfforts'], autofill.efforts))\n const ladderTaken = ladderAutofilled\n ? false\n : current['reasoningEfforts'] !== undefined\n || current[UNSET_MARKER] === true\n || declaration.efforts === 'keep'\n // Modality part: the same provenance rule, decided per part. The knowledge\n // base disclosure the autofill wrote does not answer over a staged choice,\n // while a disclosure that differs from the footprint is a hand-made one and\n // stands. A ladder-only 'keep' staging leaves the stored modality alone, just\n // as before \u2014 the staging never carried a modality in that case.\n const inputAutofilled =\n current[INPUT_UNSET_MARKER] !== true\n && autofill?.input !== undefined\n && sameInputList(current['input'], autofill.input)\n const inputTaken = inputAutofilled\n ? false\n : (Array.isArray(current['input']) && current['input'].length > 0)\n || current[INPUT_UNSET_MARKER] === true\n const efforts: EffortWriteIntent = ladderTaken ? 'keep' : declaration.efforts\n const input = inputTaken ? undefined : declaration.input\n // The default-effort pick has no takeover question: host autofill never\n // writes this field, so whatever the document holds is a user decision and\n // the staged intent (write or clear) passes through untouched.\n const defaultEffort = declaration.defaultEffort\n if (efforts === 'keep' && input === undefined && defaultEffort === undefined) return null\n return {\n efforts,\n ...(declaration.compat === undefined ? {} : { compat: declaration.compat }),\n ...(input === undefined ? {} : { input }),\n ...(defaultEffort === undefined ? {} : { defaultEffort }),\n }\n}\n\n/** What this plugin's host autofill would write for one model, as the flush compares it. */\nexport interface AutofillFootprint {\n efforts?: ReasoningEfforts | false\n input?: InputModalities\n}\n\n/** Semantic equality of two reasoningEfforts dict values (key set + wire strings). */\nfunction sameEffortsValue(a: unknown, b: ReasoningEfforts | false): boolean {\n if (a === b) return true\n if (!isRecord(a) || !isRecord(b)) return false\n const keys = Object.keys(b)\n if (Object.keys(a).length !== keys.length) return false\n const other = b as Record<string, unknown>\n return keys.every(key => a[key] === other[key])\n}\n\n/** Semantic equality of a raw input value with a modality list (order-insensitive). */\nfunction sameInputList(a: unknown, b: InputModalities): boolean {\n if (!Array.isArray(a) || a.length !== b.length) return false\n const set = new Set<string>(b)\n return a.every(member => typeof member === 'string' && set.has(member))\n}\n\n/**\n * Shape guard for a restored held write. The ledger is `sessionStorage`, so a\n * foreign or half-written file must be rejected before its entries reach\n * `writeModelRows` (a schema-level refusal there is noisy and late).\n */\nfunction isHeldWrite(value: unknown): value is HeldWrite {\n if (!isRecord(value)) return false\n const efforts = value['efforts']\n if (efforts !== false && efforts !== 'keep' && !isRecord(efforts)) return false\n const clear = value['clearCompatKeys']\n return clear === undefined || (Array.isArray(clear) && clear.every(key => typeof key === 'string'))\n}\n\n/**\n * Flush staged declarations for routes that now exist, writing each model's\n * declaration through the live write seam. A model the saved profile does not\n * carry yet keeps its staging (the row may still be mid-creation -- the user\n * edits the card after staging -- and the declaration lands whenever the row\n * first appears); each PART is decided against the saved row via\n * {@link effectiveStagedIntents}, with the host autofill's knowledge-base\n * footprint passed in so the plugin's own proposal never outranks a staged\n * user intent. A write that still fails (conflict retry exhausted) stays\n * staged; the write's own document-updated invalidation re-scans and\n * re-flushes it.\n * @returns whether a write of this route was refused and left unlanded.\n */\nasync function flushRoute(\n deps: InjectorDeps,\n state: ScanState,\n route: string,\n models: ReadonlyMap<string, StagedDeclaration>,\n ignoreFence = false,\n): Promise<boolean> {\n // ONE read for the whole route: the rows all live in the same array, so a\n // per-model read was pure repetition. Every arbitration below still runs\n // per model against this same snapshot -- the write is one whole-array set.\n const join = await deps.describeNamespace()\n const providers = providersOf(join.namespace)\n // Snapshot: the loop below withdraws entries whose edit proved empty, and\n // the batch then reports back per model as its results land.\n const intents: RowIntent[] = []\n for (const [modelId, declaration] of [...models]) {\n const current = modelsOf(providers, route).find(model => model['id'] === modelId)\n // The row is not saved yet (mid-edit card, or the user renamed it):\n // keep the staging instead of dropping it -- it lands when a row with\n // this id first appears; the scan's ghost pass withdraws staging whose\n // row is provably gone.\n if (current === undefined) continue\n // A concurrent scan may have withdrawn this staging while our read was\n // in flight (the two-scan ghost pass): writing it anyway would resurrect\n // a declaration nothing on the page owns anymore.\n if (state.pending.get(route)?.get(modelId) !== declaration) continue\n // Mirror the host autofill's suggestion for this exact row (same facts,\n // same knowledge base) so {@link effectiveStagedIntents} can tell the\n // plugin's own proposal apart from a user decision.\n const routeInfo = routeFactsOf(providers, route)\n const name = typeof current['name'] === 'string' ? current['name'] : undefined\n const suggestion = suggestEfforts(modelId, name === undefined ? routeInfo : { ...routeInfo, displayName: name })\n const effective = effectiveStagedIntents(declaration, current, {\n ...(suggestion.efforts === undefined ? {} : { efforts: suggestion.efforts }),\n ...(suggestion.input === undefined ? {} : { input: suggestion.input }),\n })\n if (effective === null) {\n stageEffortsInto(state, route, modelId, undefined)\n continue\n }\n intents.push({\n modelId,\n efforts: effective.efforts,\n ...(effective.compat === undefined ? {} : { compat: effective.compat }),\n ...(effective.input === undefined ? {} : { input: effective.input }),\n ...(effective.defaultEffort === undefined ? {} : { defaultEffort: effective.defaultEffort }),\n })\n }\n if (intents.length === 0) return false\n // Seed the write's FIRST read with the snapshot this pass already took: one\n // read per route instead of two. A conflict retry re-describes fresh through\n // the live seam, so the seed never costs the write its recovery path.\n let seeded = false\n const results = await writeModelRows(deps.api, route, intents, () => {\n if (!seeded) {\n seeded = true\n return Promise.resolve(join)\n }\n return deps.describeNamespace()\n }, ignoreFence ? undefined : officialCardOpen)\n let failed = false\n intents.forEach((intent, at) => {\n const result = results[at]\n // A card opened during the read: keep the staging, do not back off.\n if (result?.aborted === true) return\n if (result?.ok === true || result?.modelNotFound === true) {\n stageEffortsInto(state, route, intent.modelId, undefined)\n return\n }\n // Anything else keeps the staging (the next scan retries it), but a\n // silent keep is unobservable -- surface the failure for diagnostics, and\n // report it so the pass can back off instead of hammering.\n failed = true\n console.error(`[bre] staged flush write failed for \"${route}\"/\"${intent.modelId}\": ${result?.error ?? 'unknown'}`)\n })\n return failed\n}\n\n/**\n * Replay every editor-held write through a HOLDER-LESS seam, so the very call\n * that would have fought the official card now commits. A row that vanished\n * meanwhile drops its entry instead of retrying forever; any other failure\n * stays queued for the next idle pass.\n *\n * The LANDING DECISION lives here (issue #7 / C2), per route, because this\n * ledger is the one the official card's Save governs:\n * - the user committed -> write it, now that the card is gone;\n * - the official action row was never readable in this session -> the signal\n * cannot be trusted, so degrade to \"the card went away, write it\": writing\n * too much is recoverable, losing the declaration is not;\n * - otherwise the card closed with neither signal (the user just walked\n * away) -> the intent waits rather than landing behind a frozen baseline.\n *\n * A DISMISSED card has no branch here on purpose: its cancel clears the ledger\n * the moment it is pressed ({@link forgetRoute}), so \"dismissed\" can never\n * outlive the card it belonged to and swallow a later save of the same route.\n * @param deps - the injection dependencies.\n * @param state - mutable scan state.\n * @returns whether this pass left a write unlanded (the caller backs off).\n */\nasync function flushQueued(deps: InjectorDeps, state: ScanState, ignoreFence = false): Promise<boolean> {\n // A commit marker with nothing queued is SPENT: the official Save carried no\n // plugin edit (or the route was already landed). Dropping it here keeps it\n // from authorizing a later edit the user never saved.\n for (const route of [...state.committing]) {\n if (!state.queued.has(route)) state.committing.delete(route)\n }\n // Writability is the document's call, not the commit signal's: a memory /\n // non-loopback page refuses writes, and the held ledger obeys the same gate\n // the staged ledger already does. Wait for a landable route before paying\n // for the read, so a page whose intents are all uncommitted costs nothing.\n const landable = [...state.queued.keys()]\n .some(route => state.committing.has(route) || state.signalsUnavailable)\n if (!landable) {\n persistLedger(state)\n return false\n }\n const join = await deps.describeNamespace()\n if (join.writable !== true) {\n persistLedger(state)\n return false\n }\n let failed = false\n for (const [route, models] of [...state.queued]) {\n // The staged ledger has no official row whose Save could commit it; this\n // one does, so an uncommitted route waits instead of landing behind that\n // card's frozen revision baseline.\n const committed = state.committing.has(route)\n if (!committed && !state.signalsUnavailable) continue\n // One read, one mutate for the whole route: the held intents are per model\n // but the document is a single models array, so a per-model write was\n // rebuilding and rewriting that same array N times.\n const intents: RowIntent[] = [...models].map(([modelId, write]) => ({\n modelId,\n efforts: write.efforts,\n ...(write.compat === undefined ? {} : { compat: write.compat }),\n ...(write.input === undefined ? {} : { input: write.input }),\n ...(write.clearCompatKeys === undefined ? {} : { clearCompatKeys: write.clearCompatKeys }),\n ...(write.defaultEffort === undefined ? {} : { defaultEffort: write.defaultEffort }),\n }))\n const results = await writeModelRows(deps.api, route, intents, undefined, ignoreFence ? undefined : officialCardOpen)\n let aborted = false\n intents.forEach((intent, at) => {\n const result = results[at]\n // A card opened during the read: keep every intent, do not back off.\n if (result?.aborted === true) { aborted = true; return }\n if (result?.ok === true || result?.modelNotFound === true) {\n models.delete(intent.modelId)\n return\n }\n console.error(`[bre] held write failed for \"${route}\"/\"${intent.modelId}\": ${result?.error ?? 'unknown'}`)\n })\n if (models.size === 0) {\n state.queued.delete(route)\n // The marker is what authorizes the landing, so it is dropped only once\n // every intent of the route made it -- a refusal keeps both the intent\n // and the authority to retry it on the next pass.\n state.committing.delete(route)\n } else if (!aborted) {\n failed = true\n }\n }\n // The queue just shrank (landed intents) or stayed as it was (a route still\n // waiting for its card): either way the stored file must match what is left.\n persistLedger(state)\n return failed\n}\n\n/**\n * Land the create-card declarations whose route has appeared. Each route goes\n * through its own live describe, so one refused route cannot poison a sibling\n * that would have landed.\n * @param deps - the injection dependencies.\n * @param state - mutable scan state.\n * @returns whether a write was refused and left unlanded.\n */\nasync function flushPending(deps: InjectorDeps, state: ScanState, ignoreFence = false): Promise<boolean> {\n if (state.pending.size === 0) return false\n const join = await deps.describeNamespace()\n if (join.writable !== true) return false\n const providers = providersOf(join.namespace)\n let failed = false\n for (const [route, models] of [...state.pending]) {\n if (models.size === 0) {\n state.pending.delete(route)\n continue\n }\n // A create card still owns a route the document has not taken yet.\n if (!hasOwn(providers, route)) continue\n if (await flushRoute(deps, state, route, models, ignoreFence)) failed = true\n }\n persistLedger(state)\n return failed\n}\n\n/**\n * Find the first input/select whose aria-label starts with one of the labels.\n * Exported because the model-order pass reads a row's id and display name the\n * same way the editor reads them: by the official label stem, never by\n * position (the label's trailing number IS the position).\n */\nexport function inputValueByLabel(card: HTMLElement, labels: readonly string[]): string {\n for (const label of labels) {\n const input = Array.from(card.querySelectorAll<HTMLInputElement | HTMLSelectElement>('input[aria-label], select[aria-label]'))\n .find(candidate => (candidate.getAttribute('aria-label') ?? '').startsWith(label))\n if (input !== undefined && input.value.trim().length > 0) return input.value.trim()\n }\n return ''\n}\n\n/** The nearest editor card element of a trigger. */\nfunction cardOf(trigger: Element): HTMLElement | undefined {\n return trigger.closest<HTMLElement>('[class*=\"editor\"], [class*=\"rowCard\"], [class*=\"addCard\"]') ?? undefined\n}\n\n/**\n * The official per-row disclosure container (holds the capacity fields), or\n * undefined while that row is COLLAPSED: the official editor renders the\n * container only for an expanded row, so an untouched provider shows nothing\n * for its models to mount into.\n */\nfunction disclosureOf(trigger: Element): HTMLElement | undefined {\n // The disclosure lives inside the trigger's own model row, not elsewhere in\n // the card: scoping to the row keeps each trigger's container distinct even\n // when several rows are expanded at once.\n const row = trigger.closest<HTMLElement>('[class*=\"modelEntry\"]')\n if (row === null) {\n // Fall back to the card scope (stale or unusual structure).\n const card = cardOf(trigger)\n if (card === undefined) return undefined\n const candidates = Array.from(card.querySelectorAll<HTMLElement>('[class*=\"modelAdvanced\"]'))\n return candidates.find(candidate => candidate.offsetParent !== null) ?? candidates[0]\n }\n const candidates = Array.from(row.querySelectorAll<HTMLElement>('[class*=\"modelAdvanced\"]'))\n return candidates.find(candidate => candidate.offsetParent !== null) ?? candidates[0]\n}\n\n/**\n * Whether the official page ships its own input-types editor inside this\n * row's disclosure (0.1.6-alpha.2's `ModelInputTypes` fieldset). This is the\n * CAPABILITY signal the plugin sniffs to stand its own modality section down:\n * a DOM fact, matched by the official class-name stem the same way the\n * `modelAdvanced` anchor is \u2014 never a kernel version number.\n */\nfunction officialInputTypesOf(container: HTMLElement): boolean {\n return container.querySelector('[class*=\"modelInputTypes\"]') !== null\n}\n\n/**\n * One auto-adapt seat per card that has models on screen: the card's own\n * catalogue, paired with the route that card edits. Deduped by card, because\n * every model row of a card shares the one catalogue head.\n */\n/**\n * Seat the auto-adapt control on an open card whose model list is empty.\n *\n * The host keeps the catalogue head (\u83B7\u53D6\u53EF\u7528\u6A21\u578B) after the last row is\n * deleted. The seat has to stay beside that link, or the link jumps. A card\n * whose route cannot be read keeps whatever seat the previous pass left: a\n * settings read that has not landed yet must not flash the control away.\n */\nfunction seatEmptyCatalog(\n root: HTMLElement,\n deps: InjectorDeps,\n state: ScanState,\n join: SettingsJoin,\n labels: HostLabels,\n): void {\n if (!root.isConnected) return\n if (officialCardOf(root) === undefined) return\n const stillEmpty = !Array.from(root.querySelectorAll<HTMLButtonElement>('button[aria-label]'))\n .some(button => labels.capacity.some(label => (button.getAttribute('aria-label') ?? '').startsWith(label)))\n if (!stillEmpty) return\n reconcileAutoEffortSeats(root, emptyCatalogTargets(root, providersOf(join.namespace), labels), {\n t: deps.t,\n onRequest: route => { void adaptEveryModel(state, deps, route) },\n })\n}\n\n/** One seat per catalogue whose card still has a head and a readable route. */\nfunction emptyCatalogTargets(\n root: HTMLElement,\n providers: Record<string, Record<string, unknown>>,\n labels: HostLabels,\n): AutoEffortSeatTarget[] {\n const targets: AutoEffortSeatTarget[] = []\n // The CONTAINERS, not every element whose class name carries the stem: the\n // heading, the title and the meta are matches too, and seating inside them\n // collapses the official heading to one character per line. See\n // {@link cataloguesOf}.\n for (const catalogue of cataloguesOf(root)) {\n const card = cardOf(catalogue)\n if (card === undefined) continue\n const resolved = routeOfCard(card, providers, labels)\n if (resolved === undefined || resolved.staged) continue\n targets.push({ catalogue, route: resolved.route })\n }\n return targets\n}\n\nfunction autoEffortTargets(\n found: readonly FoundModel[],\n providers: Record<string, Record<string, unknown>>,\n labels: HostLabels,\n): AutoEffortSeatTarget[] {\n const targets: AutoEffortSeatTarget[] = []\n const seen = new Set<HTMLElement>()\n for (const model of found) {\n if (seen.has(model.card)) continue\n seen.add(model.card)\n const resolved = routeOfCard(model.card, providers, labels)\n if (resolved === undefined) continue\n const [catalogue] = cataloguesOf(model.card)\n if (catalogue === undefined) continue\n targets.push({ catalogue, route: resolved.route })\n }\n return targets\n}\n\n/**\n * Adapt every model of one provider, the rows the user never unfolded\n * included.\n *\n * The seat's click reaches the per-row editors through a document event, but\n * an editor only exists for a row with a disclosure container, and a collapsed\n * row renders none \u2014 so the visible answer is structurally incomplete. This\n * routine answers from the settings DOCUMENT instead of the DOM: every model\n * the provider declares goes through the same suggestion the row's own\n * auto-adapt would have asked for, is mapped by the same pure functions\n * ({@link ladderIntent}, {@link compatDraftOf}, {@link pendingWriteOf}) so both\n * paths write identical bytes, and lands by the same rule \u2014 nothing is written\n * while an official card holds the document; the write is held on the route\n * and the card's own Save commits it (issue #7 / C2).\n *\n * An expanded row answers for itself when its editor is mounted. A row the\n * user already configured (a stored ladder, an explicit \"does not reason\",\n * or a deliberate unset) is left alone \u2014 bulk adapt must not overwrite that.\n * A row that is expanded but still has no thinking-level declaration is not\n * configured: this walk adapts it too. Two writers never share one row,\n * because a configured expanded row is skipped here and an unconfigured one\n * has no in-flight ladder draft to protect.\n * @param state - mutable scan state: the adapting guard and the held ledger.\n * @param deps - the injection dependencies.\n * @param route - the provider route whose models are adapted.\n */\nexport async function adaptEveryModel(state: ScanState, deps: InjectorDeps, route: string): Promise<void> {\n if (route.length === 0 || state.adapting.has(route)) return\n state.adapting.add(route)\n try {\n // One read for the whole run: `suggest` only ever READS the namespace here\n // (its endpoint probe is a read-only GET, it never resolves a credential),\n // the holds below change nothing the read depends on, and every model of\n // the route needs the same provider facts anyway.\n let join: Promise<SettingsJoin> | undefined\n const describe = (): Promise<SettingsJoin> => (join ??= deps.describeNamespace())\n const seen = await describe()\n const providers = providersOf(seen.namespace)\n // A read-only page and a route that is not in the document yet (a create\n // card's typed id) have nothing to adapt: the card's own rows stage their\n // work and it lands with the route.\n if (seen.writable !== true || !hasOwn(providers, route)) return\n const models = modelsOf(providers, route)\n if (models.length === 0) return\n const profile = providers[route] ?? {}\n const routeApi = typeof profile['api'] === 'string' ? profile['api'] as string : undefined\n // Expanded rows whose stored ladder is already a decision. An expanded row\n // with no declaration is NOT covered: the user asked bulk adapt to fill\n // exactly those, the same as a collapsed row.\n const covered = new Set<string>()\n for (const entry of state.mounted.values()) {\n if (entry.props.route !== route) continue\n if (entry.props.efforts !== undefined) covered.add(entry.props.modelId)\n }\n const api = createEditorApi(\n deps.api,\n describe,\n // No stage arm: this routine only runs while the page is up, and a write\n // made then is always a HOLD that the official Save lands.\n undefined,\n (heldRoute, modelId, write) => { queueWriteInto(state, heldRoute, modelId, write) },\n )\n for (const model of models) {\n const modelId = typeof model['id'] === 'string' ? model['id'] : ''\n if (modelId.length === 0 || covered.has(modelId)) continue\n const initialEfforts = effortsOf(models, modelId)\n // A collapsed row has no editor, so the document is the only record.\n // Same rule as the expanded skip above: a stored ladder, `false`, or the\n // durable unset marker is a decision and must survive this click.\n if (initialEfforts !== undefined || model[UNSET_MARKER] === true) continue\n const initialInput = inputOf(models, modelId)\n const initialCompat = compatOf(models, modelId)\n const initialDefault = defaultEffortOf(models, modelId) ?? ''\n const reply = await api.suggest(route, modelId, nameOf(models, modelId))\n // 'no-suggestion' is the normal outcome for a model with no knowledge\n // match (the seat is a bulk convenience, not a promise): leave that row\n // exactly as it is.\n if (!reply.ok) continue\n const parts = reply.suggestion\n const nextThinking = parts.efforts !== false\n // The draft a fresh editor would hold, mapped exactly as its own commit\n // maps it: an untouched ladder and an untouched modality stay out of the\n // write rather than stamping an unset marker onto the row. The vendor\n // default travels the same way the row's own auto-adapt does: a fresh\n // editor starts from the stored pick and only reports a change.\n const nextModality = parts.input === undefined ? modalityFrom(initialInput) : modalityFrom(parts.input)\n const nextDefault = parts.defaultEffort ?? initialDefault\n const defaultEffort = nextDefault === initialDefault\n ? undefined\n : nextDefault === '' ? null : nextDefault\n api.commit(route, modelId, pendingWriteOf({\n efforts: ladderIntent(nextThinking, buildIntent(draftFrom(parts.efforts)), initialEfforts),\n input: sameModality(nextModality, initialInput) ? undefined : buildModalityIntent(nextModality),\n defaultEffort,\n manualCompat: compatDraftOf(initialCompat),\n appliedCompat: parts.compat,\n routeApi,\n initialCompat,\n }))\n }\n } catch (error) {\n // The seat has no message area of its own, and one row's refusal must not\n // strand the rows behind it: report, keep whatever holds already landed.\n console.warn('[bre] provider-wide adapt failed:', error)\n } finally {\n state.adapting.delete(route)\n }\n}\n\n/** Whether an editor is already mounted in a container (idempotency guard). */\nfunction hasEditor(container: HTMLElement): boolean {\n return container.querySelector(`[data-plugin=\"${PLUGIN_ID}\"]`) !== null\n}\n\n/**\n * Semantic equality of two mount-prop sets, so a scan only re-renders an\n * existing editor when the settings document actually moved under it. The\n * guard is what makes refresh safe under the MutationObserver: render mutates\n * DOM, DOM mutations schedule scans, and a no-op comparison ends the cycle.\n */\nfunction sameProps(a: EditorMountProps, b: EditorMountProps): boolean {\n return a.route === b.route\n && a.routeDisplayName === b.routeDisplayName\n && a.routeApi === b.routeApi\n && a.routeBaseURL === b.routeBaseURL\n && a.modelId === b.modelId\n && a.modelName === b.modelName\n && a.index === b.index\n && a.readOnly === b.readOnly\n && a.defaultEffort === b.defaultEffort\n // A create card's container surviving its own save must flip the editor\n // to write mode: staged changes the Apply button's whole contract, so it\n // participates in the diff like any other prop.\n && a.staged === b.staged\n // The official capability can appear/disappear across an HMR or host\n // update; the modality section must follow it.\n && a.officialInputTypes === b.officialInputTypes\n && sameEfforts(a.efforts, b.efforts)\n && sameInput(a.input, b.input)\n && sameCompat(a.compat, b.compat)\n}\n\n/** Semantic equality of two compat blocks (key-order-insensitive). */\nfunction sameCompat(a: CompatSuggestion | undefined, b: CompatSuggestion | undefined): boolean {\n if (a === b) return true\n if (a === undefined || b === undefined) return false\n return JSON.stringify({ ...a }, Object.keys({ ...a, ...b }).sort()) === JSON.stringify({ ...b }, Object.keys({ ...a, ...b }).sort())\n}\n\n/** Semantic equality of two modality declarations (order-insensitive). */\nfunction sameInput(a: InputModalities | undefined, b: InputModalities | undefined): boolean {\n if (a === b) return true\n if (a === undefined || b === undefined) return false\n const setA = new Set(a)\n return a.length === b.length && b.every(item => setA.has(item))\n}\n\n/** Whether the card carries any input/select labeled with one of the labels. */\nfunction hasLabeledInput(card: HTMLElement, labels: readonly string[]): boolean {\n return Array.from(card.querySelectorAll<HTMLInputElement | HTMLSelectElement>('input[aria-label], select[aria-label]'))\n .some(candidate => labels.some(label => (candidate.getAttribute('aria-label') ?? '').startsWith(label)))\n}\n\n/**\n * The route token of a card, resolved against the joined providers. The\n * official edit card prints the route key as the `.editorRoute` tag next to\n * the display-name title; the create card prints a fixed heading with no key,\n * but its \"Provider ID\" input carries the route id being chosen \u2014 a route not\n * in the settings document yet. Match the key first (exact, unambiguous),\n * then the create card's typed id, then the display name (a create card never\n * reaches the name arm: its Provider ID input marks it, and its fixed heading\n * could otherwise collide with a provider's display name), and finally the\n * title itself as a route key: a provider that never set a display name\n * renders the route id as its title and hides the `.editorRoute` tag (the\n * two are then equal), so only this last arm can resolve it. A create card\n * never reaches the last arm either \u2014 its Provider ID input already returned.\n */\nfunction routeOfCard(\n card: HTMLElement,\n providers: Record<string, Record<string, unknown>>,\n labels: HostLabels,\n): { route: string; staged: boolean } | undefined {\n const key = card.querySelector<HTMLElement>('[class*=\"editorRoute\"]')?.textContent?.trim()\n if (key !== undefined && key.length > 0 && hasOwn(providers, key)) return { route: key, staged: false }\n if (hasLabeledInput(card, labels.routeId)) {\n const typed = inputValueByLabel(card, labels.routeId)\n return typed.length > 0 && !hasOwn(providers, typed) ? { route: typed, staged: true } : undefined\n }\n const title = card.querySelector<HTMLElement>('[class*=\"editorTitle\"], [class*=\"rowName\"]')?.textContent?.trim()\n if (title === undefined || title.length === 0) return undefined\n const byName = Object.entries(providers).find(([, profile]) => profile['displayName'] === title)\n if (byName !== undefined) return { route: byName[0], staged: false }\n // A provider that never set a display name resolves to its route key, and\n // the host hides the .editorRoute tag while that key equals the title\n // (ProviderEditor renders it only when the name differs) \u2014 so neither arm\n // above can find the route. The title is then the route key itself, which\n // is exact: resolve it directly. A create card never reaches this arm (its\n // Provider ID input already returned), so an editor-title/key collision\n // with the fixed create heading cannot occur.\n if (hasOwn(providers, title)) return { route: title, staged: false }\n return undefined\n}\n\n/**\n * The commit/cancel pair of one card's official action row, or undefined when\n * the row cannot be read.\n *\n * Three tiers, most structural first, because this is the signal the plugin's\n * landing write hangs on and the official page is free to restyle it:\n * 1. the last two buttons of `[class*=\"editorActions\"]` -- the commit is the\n * rightmost, its dismiss the one before it (`EditorFooter.tsx`: the row is\n * a plain two-button div, cancel first, commit last);\n * 2. the declared CSS Modules classes `primaryButton` / `secondaryButton`\n * (they carry a hash suffix, so only the prefix can match);\n * 3. the host's own copy for that row, resolved through the dictionary it\n * renders from (`apply` is 'Apply' in English and '\u4FDD\u5B58' in Chinese, so\n * pinning either language would strand one of them).\n *\n * A row whose buttons no tier can identify returns undefined, which is what\n * makes {@link ScanState.signalsUnavailable} trip and the landing decision\n * degrade to \"the card went away, write it\" rather than dropping the edit.\n */\nfunction actionsOf(card: HTMLElement, labels: HostLabels): { submit: HTMLElement; cancel: HTMLElement } | undefined {\n const row = card.querySelector<HTMLElement>('[class*=\"editorActions\"]')\n if (row === null) return undefined\n const buttons = Array.from(row.querySelectorAll<HTMLElement>('button'))\n if (buttons.length < 2) return undefined\n const matches = (node: HTMLElement, label: string): boolean =>\n (node.textContent ?? '').trim() === label\n const primary = row.querySelector<HTMLElement>('[class*=\"primaryButton\"]')\n const secondary = row.querySelector<HTMLElement>('[class*=\"secondaryButton\"]')\n if (primary !== null && secondary !== null) return { submit: primary, cancel: secondary }\n const submit = [...buttons].reverse().find(node => labels.apply.some(label => matches(node, label)))\n const cancel = [...buttons].reverse().find(node => labels.cancel.some(label => matches(node, label)))\n if (submit !== undefined && cancel !== undefined) return { submit, cancel }\n // Structural last resort: the rightmost button commits, its left sibling\n // dismisses. Used only when neither the classes nor the copy resolve, so a\n // one-button row (a future layout) still yields a commit.\n const last = buttons[buttons.length - 1]\n const previous = buttons[buttons.length - 2]\n if (last === undefined || previous === undefined) return undefined\n return { submit: last, cancel: previous }\n}\n\n/**\n * Wire one button's click ONCE, in the capture phase.\n *\n * Capture, not bubble: the marker has to be recorded before the official\n * React handler runs and tears the card down, and React's own listeners sit\n * on the root container. Capture also makes this independent of whether the\n * official handler stops propagation.\n */\nfunction wireOnce(\n node: HTMLElement,\n wired: WeakSet<Element>,\n onHit: () => void,\n): void {\n if (wired.has(node)) return\n wired.add(node)\n node.addEventListener('click', onHit, { capture: true })\n}\n\n/**\n * Whether an official editing card is open on the page, told from the card's\n * own action row (its Cancel/commit pair) with the model-row containers as a\n * fallback signal.\n *\n * The distinction matters: an ON-SCREEN card holds a frozen revision baseline,\n * so a write landing while it is open is refused on the user's next save --\n * issue #7. Judging \"is a card open\" from the capacity buttons alone answers\n * \"is a model row expanded\", which is not the same question: an open card with\n * an empty model list would read as idle and let exactly that write through.\n */\nfunction officialCardOf(root: HTMLElement): HTMLElement | undefined {\n return root.querySelector<HTMLElement>('[class*=\"editorActions\"]')\n ?? root.querySelector<HTMLElement>('[class*=\"modelEntry\"]')\n ?? undefined\n}\n\n/**\n * The LIVE form of the write fence: whether an official card is open in the\n * CURRENT DOM.\n *\n * The scan's own boolean lags by `SCAN_DEBOUNCE_MS`, so a write that started\n * during that window would still see \"no card\" and land behind the card's\n * frozen revision baseline \u2014 issue #7 again. `writeModelRows` already probes\n * this at the last possible moment (after its read, before its mutate); this\n * helper makes that probe read the DOM instead of the last scan's snapshot.\n */\nfunction officialCardOpen(): boolean {\n return officialCardOf(panelRoot()) !== undefined\n}\n\n/**\n * Scan the settings DOM for official model rows and reconcile the injected\n * editors. Idempotent: existing editors are left alone, new disclosures get\n * one, and removed ones are unmounted.\n * @param root - the settings panel root to scan.\n * @param deps - the injection dependencies.\n * @param state - mutable scan state shared across invocations.\n */\nexport function reconcile(root: HTMLElement, deps: InjectorDeps, state: ScanState): void {\n // Cheap DOM gate BEFORE the wire read: most mutations in a running app\n // (chat streaming, typing anywhere) fire while no model row exists at\n // all. One prefix query per locale answers \"is the Models page here?\" \u2014\n // when it is not and nothing is mounted, the scan costs nothing more.\n if (!root.isConnected) return\n // One label read per scan, taken before the cheap DOM gate below: the gate\n // itself matches on them, and reading lazily is what keeps a language switch\n // in step with the page (the labels the host renders change with it).\n const labels = deps.labels()\n // \"Is an official editing card open?\" is the gate that decides whether this\n // plugin may write at all. The card's own action row answers it structurally,\n // with the model-row containers as a second signal: an open card whose model\n // list happens to be empty carries no capacity button, and judging idleness\n // from those buttons alone would let a write slip into that card's frozen\n // revision baseline -- issue #7 again, under a rarer trigger.\n const cardOpen = officialCardOf(root) !== undefined\n // Match on the attribute VALUE, never through a selector built out of host\n // copy: a language pack whose label carries a quote or a bracket would make\n // `querySelector` throw, and this scan has no try/catch around it, so the\n // settings injection would stall for that language.\n const hasCapacityRows = Array.from(root.querySelectorAll<HTMLButtonElement>('button[aria-label]'))\n .some(button => labels.capacity.some(label => (button.getAttribute('aria-label') ?? '').startsWith(label)))\n if (!cardOpen) {\n // No card is open, so no catalogue is either \u2014 but the seat is this\n // plugin's OWN DOM inside the host's head, and only a scan can take it\n // out, so every path that skips the row scan also has to clear the seats.\n reconcileAutoEffortSeats(root, [], { t: deps.t })\n if (state.mounted.size > 0) {\n for (const [, entry] of state.mounted) entry.editor.unmount()\n state.mounted.clear()\n }\n // No editing card is on the page, so no on-screen revision baseline can be\n // invalidated by a write: THIS is the moment to land everything the\n // session held back. `!== false` also covers the first scan (undefined) --\n // and a page that reloaded onto restored ledgers -- so outstanding work\n // always gets its pass; the second arm only fires while intents wait, so a\n // settled page costs nothing beyond the check.\n const wasEditing = state.editing\n state.editing = false\n if (wasEditing !== false || (hasOutstanding(state) && Date.now() >= state.nextFlushAt)) {\n void runIdlePass(deps, state).catch(error => {\n console.error(`[bre] idle pass failed: ${error instanceof Error ? error.message : String(error)}`)\n })\n }\n return\n }\n state.editing = true\n // An open card showing no model row has nothing to equip: the idle pass\n // stays fenced until the card goes away (or a row appears). The catalogue\n // head stays, though \u2014 the host still renders \u83B7\u53D6\u53EF\u7528\u6A21\u578B there \u2014 so the\n // auto-adapt seat stays beside it. Taking the seat out is what made that\n // link jump the moment the last model was deleted.\n if (!hasCapacityRows) {\n for (const [, entry] of state.mounted) entry.editor.unmount()\n state.mounted.clear()\n state.describePromise ??= deps.describeNamespace()\n const seatLabels = labels\n void state.describePromise.then(join => {\n seatEmptyCatalog(root, deps, state, join, seatLabels)\n }).catch(error => {\n console.error(`[bre] empty-catalog seat failed: ${error instanceof Error ? error.message : String(error)}`)\n })\n return\n }\n // Fold the describe request across scans (one wire read per wave). A\n // promise's .then ALWAYS runs asynchronously (microtask), even when already\n // resolved \u2014 the fold just keeps concurrent scans from stacking wire reads.\n // The holder clears this field on rejection (retry the read next scan) and\n // on pushed invalidations (settings/document-updated, connection/reset \u2014\n // see apply()), so a stale snapshot never outlives the change that made it\n // stale.\n state.describePromise ??= deps.describeNamespace()\n const run = (join: SettingsJoin): void => {\n if (!root.isConnected) return\n const namespace = join.namespace\n const providers = providersOf(namespace)\n\n // Staged declarations land on the IDLE pass, never from an open card:\n // writing while the official editor holds the document is exactly what\n // made the user's own save in that card fail with `settings/conflict`.\n\n const found: FoundModel[] = []\n for (const aria of labels.capacity) {\n // The official disclosure buttons carry a numbered aria-label\n // (\"Capacities 1\", \"\u5BB9\u91CF 2\", \u2026), so match by prefix.\n const triggers = Array.from(root.querySelectorAll<HTMLButtonElement>('button[aria-label]'))\n .filter(button => (button.getAttribute('aria-label') ?? '').startsWith(aria))\n for (const trigger of triggers) {\n const card = cardOf(trigger)\n if (card === undefined) continue\n // The model id lives on the trigger's OWN row, not elsewhere in the\n // card: reading the card would return the first row's id for every\n // trigger once more than one model is present.\n const row = trigger.closest<HTMLElement>('[class*=\"modelEntry\"]') ?? card\n const modelId = inputValueByLabel(row, labels.modelId)\n if (modelId.length === 0) continue\n // The container is OPTIONAL: a collapsed row renders no disclosure at\n // all, and skipping it here was what kept the provider-wide seat (and\n // every provider-wide decision) blind until the user unfolded a model.\n // A row with no container is still a model of this provider -- it\n // contributes to what the seat adapts, and only its own editor mount\n // waits for the container the chevron will render.\n found.push({ container: disclosureOf(trigger), row, modelId, card })\n }\n }\n\n // The \"adapt every model of this provider\" seat (user request \u2464) rides the\n // same scan that found the rows: one seat per card, seated in that card's\n // catalogue head beside the official fetch link. The rows' own editors\n // answer the click through the document event, but only for a row that is\n // EXPANDED (only it has a container to mount into), so the seat also hands\n // the route to the provider-wide adapt, which reads the settings document\n // and covers exactly the rows nothing on screen can answer for.\n reconcileAutoEffortSeats(root, autoEffortTargets(found, providers, labels), {\n t: deps.t,\n onRequest: route => { void adaptEveryModel(state, deps, route) },\n })\n\n // Unmount editors whose rows are gone (the page re-rendered).\n for (const [key, entry] of state.mounted) {\n if (!found.some(candidate => candidate.container === key)) {\n entry.editor.unmount()\n state.mounted.delete(key)\n }\n }\n\n // Ghosted staging: the route IS saved but its staged model row is no\n // longer anywhere on the page (renamed away, or the row/card deleted).\n // One missing scan can be a transient re-render gap, so staging is\n // withdrawn only after missing TWO consecutive scans; routes that do not\n // exist yet are never touched here -- an open create card still owns those.\n if (state.pending.size > 0) {\n const onPage = new Map<string, Set<string>>()\n for (const target of found) {\n const resolved = routeOfCard(target.card, providers, labels)\n if (resolved === undefined) continue\n let ids = onPage.get(resolved.route)\n if (ids === undefined) onPage.set(resolved.route, ids = new Set())\n ids.add(target.modelId)\n }\n const missing = new Map<string, Set<string>>()\n for (const [route, models] of state.pending) {\n if (!hasOwn(providers, route)) continue\n const present = onPage.get(route)\n const stale = [...models.keys()].filter(id => !present?.has(id))\n if (stale.length > 0) missing.set(route, new Set(stale))\n }\n for (const [route, ids] of missing) {\n const prior = state.missedScans.get(route)\n if (prior === undefined) continue\n for (const id of ids) {\n if (!prior.has(id)) continue\n stageEffortsInto(state, route, id, undefined)\n prior.delete(id)\n }\n }\n state.missedScans = missing\n }\n\n found.forEach((target, index) => {\n // A collapsed row has nowhere to mount: its editor appears the moment\n // the official chevron unfolds it and the next scan sees the container.\n // Everything below assumes one, so the row bows out here -- after it has\n // already served the provider-wide side of the scan above.\n const container = target.container\n if (container === undefined) return\n const resolved = routeOfCard(target.card, providers, labels)\n if (resolved === undefined) return\n const { route, staged: routeStaged } = resolved\n const profile = providers[route] ?? {}\n const models = modelsOf(providers, route)\n // Staged covers TWO unsaved shapes: the create card's draft route, and\n // a typed-but-unsaved model row on a SAVED route. Writing the latter\n // would bounce model-not-found (the row is not in the document yet);\n // staging rides the same flush-on-save path as the create card.\n const savedModelIds = new Set(models.map(model => model['id']))\n const staged = routeStaged || !savedModelIds.has(target.modelId)\n // A staged row's baseline is the pending store (the settings document\n // holds nothing for the model yet); the create card's typed protocol\n // and endpoint stand in for the stored profile facts, both for the\n // editor's display and for suggestion inference.\n const stagedEfforts = staged ? state.pending.get(route)?.get(target.modelId)?.efforts : undefined\n const efforts = staged\n ? (stagedEfforts === 'keep' ? undefined : stagedEfforts)\n : effortsOf(models, target.modelId)\n const input = staged\n ? state.pending.get(route)?.get(target.modelId)?.input\n : inputOf(models, target.modelId)\n const compat = staged\n ? state.pending.get(route)?.get(target.modelId)?.compat\n : compatOf(models, target.modelId)\n const defaultEffort = staged\n ? (state.pending.get(route)?.get(target.modelId)?.defaultEffort ?? undefined)\n : defaultEffortOf(models, target.modelId)\n // An unsaved row has no stored declaration to name it, but the user may\n // have typed a Display name on the row already -- suggestion inference\n // and knowledge-base matching lose that signal without it. The read is\n // ROW-scoped (the same multi-row trap the model id above avoids).\n const typedName = staged ? inputValueByLabel(target.row, labels.modelName) : ''\n const modelName = staged ? (typedName.length > 0 ? typedName : undefined) : nameOf(models, target.modelId)\n const typedApi = routeStaged ? inputValueByLabel(target.card, labels.apiProtocol) : ''\n const typedBaseURL = routeStaged ? inputValueByLabel(target.card, labels.baseUrl) : ''\n // The official action row is the signal this row's landing write hangs\n // on (C2): wire its two buttons. A row whose buttons no tier can name\n // flips the global degrade flag, so the landing decision falls back to\n // \"the card went away, write it\" instead of silently dropping the edit.\n // No guard on the route's current markers: a REOPENED card is a new\n // element whose buttons must be wired again, and `wireOnce`'s WeakSet\n // already collapses every repeat within one element.\n const actions = actionsOf(target.card, labels)\n if (actions === undefined) {\n state.signalsUnavailable = true\n } else {\n // A readable row is positive evidence the signal works: clear the\n // degrade flag so one transient unreadable card does not disable the\n // \"commits with the official Save\" gate for the rest of the session.\n state.signalsUnavailable = false\n // Resolve the route AT CLICK TIME: a create card's Provider ID can be\n // (re)typed after the buttons were first wired, and React reuses the\n // button element -- a captured route would mark / clear the wrong one.\n wireOnce(actions.submit, state.submitWired, () => {\n const live = routeOfCard(target.card, providers, labels)?.route ?? route\n state.committing.add(live)\n persistLedger(state)\n })\n wireOnce(actions.cancel, state.cancelWired, () => {\n const live = routeOfCard(target.card, providers, labels)?.route ?? route\n forgetRoute(state, live)\n })\n }\n const routeApi = routeStaged && typedApi.length > 0\n ? typedApi\n : typeof profile['api'] === 'string' ? profile['api'] as string : undefined\n const routeBaseURL = routeStaged && typedBaseURL.length > 0\n ? typedBaseURL\n : typeof profile['baseURL'] === 'string' ? profile['baseURL'] as string : undefined\n // The editor's write seam reads the namespace LIVE through its own\n // describe (not this scan's snapshot): a conflict retry must re-read a\n // fresh revision to have any chance of succeeding. Staged rows stage\n // into this scan state's pending store instead of writing settings.\n const next: EditorMountProps = {\n route,\n routeDisplayName: routeStaged ? route : typeof profile['displayName'] === 'string' ? profile['displayName'] as string : route,\n ...routeApi === undefined ? {} : { routeApi },\n ...routeBaseURL === undefined ? {} : { routeBaseURL },\n modelId: target.modelId,\n ...modelName === undefined ? {} : { modelName },\n ...efforts === undefined ? {} : { efforts },\n ...input === undefined ? {} : { input },\n ...compat === undefined ? {} : { compat },\n ...defaultEffort === undefined ? {} : { defaultEffort },\n index,\n staged,\n officialInputTypes: officialInputTypesOf(container),\n api: createEditorApi(\n deps.api,\n undefined,\n (r, m, e, c, i, de) => { stageEffortsInto(state, r, m, e, c, i, de) },\n // This row is on screen: it ALWAYS holds the document. The card it\n // lives in froze its revision baseline, so the intent is queued and\n // the official card's own Save is what commits it (issue #7 / C2).\n (r, m, w) => { queueWriteInto(state, r, m, w) },\n // The editor's Reset drops wherever this row's intent landed.\n r => { withdrawIntent(state, r, target.modelId) },\n // Re-resolve the card's staged-ness at CALL time, not at mount time:\n // the same DOM row moves between unsaved and saved as the user types\n // a route id (or adds a model row), and the scan snapshot behind this\n // closure lags that by up to one debounce window. The saved-model arm\n // still reads the snapshot: the settings document only changes when a\n // describe does.\n () => (routeOfCard(target.card, providers, labels)?.staged ?? routeStaged) || !savedModelIds.has(target.modelId),\n ),\n readOnly: join.writable !== true,\n t: deps.t,\n }\n const existing = state.mounted.get(container)\n if (existing !== undefined) {\n // The official page kept the container but moved the document under\n // it (an apply from this editor or elsewhere): swap the fresh props\n // in place so the editor never shows a stale saved declaration. The\n // sameProps guard keeps unchanged rows from re-rendering.\n if (!sameProps(existing.props, next)) {\n existing.props = next\n existing.editor.render(next)\n }\n return\n }\n if (hasEditor(container)) return\n const editor = deps.mount(container, next)\n state.mounted.set(container, { editor, props: next })\n })\n }\n // A rejected describe must not permanently disable the injector: clear the\n // folded promise so the next scan retries the read.\n void state.describePromise.then(run, () => {\n state.describePromise = undefined\n })\n}\n", "/**\n * Compare-before-write DOM helpers.\n *\n * Every write in the injection layer goes through one of these. An unchanged\n * `setAttribute` still fires a mutation record, and the passes listen for\n * mutations on the whole panel, so a blind write would have the scan chasing\n * its own tail: mark \u2192 record \u2192 rescan \u2192 mark. Comparing first is what makes\n * a settled page produce no mutations at all.\n *\n * @module dsh-model-think-level/client/injection/dom\n */\n\n/** setAttribute only when the value would change. */\nexport function setAttr(el: Element, name: string, value: string): void {\n if (el.getAttribute(name) !== value) el.setAttribute(name, value)\n}\n\n/** `hidden` is a property, not an attribute these passes can compare textually. */\nexport function setHidden(el: HTMLElement | undefined, hidden: boolean): void {\n if (el !== undefined && el.hidden !== hidden) el.hidden = hidden\n}\n\n/** Class writes compare first, like {@link setAttr}. `classList.add` is a\n * no-op for a token it already holds in a spec-conforming engine, but it is\n * not one in jsdom \u2014 and this helper's promise (\"a settled page writes\n * nothing\") has to hold in both, since a write here feeds the same scan. */\nexport function setClass(el: HTMLElement, name: string, on: boolean): void {\n if (el.classList.contains(name) === on) return\n if (on) el.classList.add(name)\n else el.classList.remove(name)\n}\n\n/** Write text only when it differs, for the same reason as {@link setAttr}. */\nexport function setText(el: HTMLElement | undefined, text: string): void {\n if (el !== undefined && el.textContent !== text) el.textContent = text\n}\n", "/**\n * The key list's seat: the official key field grows a plugin-owned host, and the\n * list is rendered inline into it, directly under the input.\n *\n * The field is not reachable from the settings page's React tree \u2014 the plugin\n * only gets keyed slots, and none of them sits beside a form field \u2014 so the seat\n * is a DOM leaf inserted directly AFTER the field. The list then falls under the\n * input, which is where a provider's keys belong: the label above already says\n * \"API key\", and the list reads as part of the field rather than as a surface\n * that has to be opened, positioned and dismissed.\n *\n * A sibling rather than a last child: the reveal eye hangs on the field's own\n * bottom edge (`bottom: 0` against the field, which its validation-message rule\n * also measures from), so a list inside the field would carry the eye down with\n * it and leave it sitting on the list's last row. React tolerates the foreign\n * sibling \u2014 it resolves a node's next sibling by walking past nodes it does not\n * own \u2014 and the list mirrors the field's region, so a pane hides it along with\n * the field it belongs to.\n *\n * The host carries the route its list was built for \u2014 that attribute is how the\n * pass knows whether the mounted list still belongs to the field it follows, and\n * how a test asks what is mounted there.\n *\n * The whole seat is skipped when the field is not a key field, or when its card\n * announces no route: the add-provider form has no route yet, and keys belong to\n * a route that exists.\n *\n * @module dsh-model-think-level/client/injection/key-manager\n */\n\nimport { createElement } from 'react'\nimport type { ReactNode } from 'react'\nimport { KeyManager } from '../KeyManager.js'\nimport type { KeyManagerClient } from '../key-manager-client.js'\nimport { setAttr, setClass } from './dom.js'\nimport { EffortBoundary, mountReact, unmountReact, type ForeignMount } from './mount.js'\n\n/** Set on a field whose keys the plugin lists. */\nexport const KEY_MANAGED_CLASS = 'bre-key-managed'\n\n/** The plugin's own element right after the field: the container of the list. */\nconst KEY_HOST_CLASS = 'bre-keys-host'\n\n/** The route the host's list was built for; read by the pass and by tests. */\nconst KEY_HOST_ATTR = 'data-bre-key-host'\n\n/** The official key input, marked by the reveal pass that dresses it. */\nconst KEY_INPUT_ATTR = 'data-bre-key-input'\n\n/** The pane the field belongs to; the list mirrors it so a pane hides both. */\nconst KEY_REGION_ATTR = 'data-bre-region'\n\n/** The repo's inline translator shape. */\nexport type Translator = (key: string, params?: Record<string, string | number>) => string\n\n/** What a reconcile tells the field's key list. */\nexport interface KeyManagerFieldDeps {\n /** Copy for the list and its form. */\n readonly t: Translator\n /** The provider route this card edits; absent on the add-provider form. */\n readonly route?: string | undefined\n /** List transport; injectable for tests. */\n readonly client?: KeyManagerClient | undefined\n}\n\n/** One mounted list, as the field holding it remembers it. */\ninterface FieldState {\n /** The route the mounted list manages. */\n route: string\n /** The plugin's element after the field, holding the React root. */\n readonly host: HTMLElement\n /** The React root mounted into {@link host}. */\n readonly mount: ForeignMount\n /** The copy the list was last rendered with. */\n t: Translator\n /** The transport the list was last rendered with. */\n client?: KeyManagerClient | undefined\n}\n\n/**\n * The lists this module mounted, keyed by the field holding them.\n *\n * Keyed by the field, not by the route: a card re-render replaces the field's\n * React children (and with them the old host), and looking the field up tells\n * the next pass whether the list it mounted is still there.\n */\nconst FIELDS = new WeakMap<HTMLElement, FieldState>()\n\n/** The official key input of a field, or undefined when this is another field. */\nfunction keyInputOf(field: HTMLElement): HTMLInputElement | undefined {\n const marked = field.querySelector(`input[${KEY_INPUT_ATTR}]`)\n if (marked instanceof HTMLInputElement) return marked\n // Before the reveal pass has marked it \u2014 or with that pass off \u2014 the key input\n // is still the field's only password input, and no other field of the card has\n // one. Everything else the card holds (name, base URL, protocol) is text.\n const secret = field.querySelector('input[type=\"password\"]')\n return secret instanceof HTMLInputElement ? secret : undefined\n}\n\n/** The plugin's host after a field, or undefined when the field has none. */\nfunction hostOf(field: HTMLElement): HTMLElement | undefined {\n const known = FIELDS.get(field)?.host\n if (known !== undefined && known.isConnected) return known\n const next = field.nextElementSibling\n return next instanceof HTMLElement && next.hasAttribute(KEY_HOST_ATTR) ? next : undefined\n}\n\n/**\n * Mount the key list into a field, replacing whatever this module mounted there\n * before. Idempotent: a field whose list is already up and current is left\n * exactly as it is, so a settled page sees no write at all.\n */\nexport function enhanceKeyManagerField(field: HTMLElement, deps: KeyManagerFieldDeps): void {\n const route = deps.route\n if (route === undefined || route.length === 0) return\n if (keyInputOf(field) === undefined) return\n // A field the page has already dropped cannot take a sibling, and the sweep\n // that removed it has already taken the list down.\n if (field.parentElement === null) return\n setClass(field, KEY_MANAGED_CLASS, true)\n\n const known = FIELDS.get(field)\n let host = hostOf(field)\n if (host === undefined) {\n // The field has no host: either this is the first pass over it, or the page\n // re-rendered and took the old host with it. Either way the state's own host\n // is gone, so its root is taken down (the element is already detached)\n // before a new host takes its place after the field.\n if (known !== undefined) drop(field, known)\n host = field.ownerDocument.createElement('div')\n host.className = KEY_HOST_CLASS\n field.after(host)\n }\n setAttr(host, KEY_HOST_ATTR, route)\n // The list belongs to the pane the field belongs to and the pane rules key on\n // the region, so the list mirrors the field's own \u2014 otherwise it would stay on\n // screen in the Models and Advanced panes, under a field those panes hide.\n const region = field.getAttribute(KEY_REGION_ATTR)\n if (region === null) {\n if (host.hasAttribute(KEY_REGION_ATTR)) host.removeAttribute(KEY_REGION_ATTR)\n } else setAttr(host, KEY_REGION_ATTR, region)\n\n const subtree = (): ReactNode =>\n createElement(\n EffortBoundary,\n { fallbackText: deps.t('keysPanelFailed') },\n createElement(KeyManager, { route, t: deps.t, client: deps.client }),\n )\n\n const state = FIELDS.get(field)\n if (state !== undefined && state.route === route) {\n // The same list for the same route: nothing the official page did can change\n // what it shows, so only a new language or transport is worth a render \u2014 and\n // React writes no DOM for props that did not change.\n if (state.t !== deps.t || state.client !== deps.client) {\n state.t = deps.t\n state.client = deps.client\n state.mount.root.render(subtree())\n }\n return\n }\n if (state !== undefined) {\n // The field outlived its list: the card switched provider, or the page\n // reused the field. The old root goes, the host stays where it is.\n FIELDS.delete(field)\n state.mount.root.unmount()\n }\n FIELDS.set(field, { route, host, mount: mountReact(host, subtree(), { sync: true }), t: deps.t, client: deps.client })\n}\n\n/** Take one mounted list down and forget it; the state's host goes with it. */\nfunction drop(field: HTMLElement, state: FieldState): void {\n FIELDS.delete(field)\n unmountReact(state.mount)\n}\n\n/**\n * Drop the list of one field, leaving the official field exactly as the page\n * rendered it. Called when the takeover ends, and for the stray sweep of a page\n * that is being torn down.\n */\nexport function resetKeyManagerField(field: HTMLElement): void {\n setClass(field, KEY_MANAGED_CLASS, false)\n const state = FIELDS.get(field)\n if (state !== undefined) drop(field, state)\n // Whatever the map knew, nothing of this module may stay in the field: both\n // the state's own host and any host the field picked up along the way go.\n hostOf(field)?.remove()\n}\n\n/** Drop every list this module mounted. */\nexport function teardownKeyManager(): void {\n for (const host of Array.from(document.querySelectorAll<HTMLElement>(`[${KEY_HOST_ATTR}]`))) {\n // A host sits directly after the field it was built for, so the field is the\n // host's previous sibling; one whose field is already gone is removed.\n const field = host.previousElementSibling\n if (field instanceof HTMLElement && FIELDS.has(field)) resetKeyManagerField(field)\n else host.remove()\n }\n}\n\n/**\n * The route a field's list was built for, or undefined when it has none.\n *\n * A test hook: the route lives on the host, so what the pass decided can be read\n * back from the DOM alone.\n */\nexport function keyManagerRoute(field: HTMLElement): string | undefined {\n return hostOf(field)?.getAttribute(KEY_HOST_ATTR) ?? undefined\n}\n", "/**\n * The API-key list: one provider's keys, which one is in use, and the one\n * inline form that adds, relabels or replaces a key.\n *\n * Rendered inline under the official key field (see `injection/key-manager`),\n * not as a floating panel: the field's label already says \"API key\", so its list\n * belongs directly beneath it, inside the card that owns the profile \u2014 the list\n * reads as part of the field rather than as a surface that has to be opened,\n * positioned and dismissed.\n *\n * The list is a view over host state and holds no truth of its own \u2014 every\n * mutation answers with the whole list, and the list is adopted as it arrives\n * (`adopt`), so the GUI can never drift from what the settings document and the\n * credential store actually say. Three facts it deliberately reflects:\n *\n * - **the key in use is the profile's reference.** Choosing a row writes\n * `providers.<route>.apiKeyEnv`, so it holds for every request the route\n * makes, not only the ones this GUI starts. Exactly one key is in use per\n * provider, which is why the mark at the head of a row is a radio.\n * - **a value is shown masked, or not at all.** The host reads the stored\n * secret and answers a head-and-tail preview; the middle never leaves it,\n * and the eye beside the field stays the one gesture that reveals a value.\n * - **one form serves all three edits.** Adding, relabelling and replacing a\n * value differ only in which fields it shows, so they share a form rendered\n * as the row below the row they act on \u2014 for an add, the list's last row.\n *\n * @module dsh-model-think-level/client/KeyManager\n */\n\nimport { Fragment, useCallback, useEffect, useId, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport { isLegalKeyValue, normalizeKeyAlias } from '../key-refs.js'\nimport { httpKeyManagerClient, type KeyAnswer, type KeyManagerClient, type KeyRow } from './key-manager-client.js'\n\n/** The repo's inline translator shape. */\ntype Translate = (key: string, params?: Record<string, string | number>) => string\n\n/** Props of {@link KeyManager}. */\nexport interface KeyManagerProps {\n /** The provider route whose keys this list manages. */\n readonly route: string\n /** Copy for the list's labels. */\n readonly t: Translate\n /** Transport; injectable for tests. */\n readonly client?: KeyManagerClient | undefined\n}\n\n/** A transient line under the list: the host's refusal, or a confirmation. */\ninterface Notice {\n readonly kind: 'error' | 'status'\n readonly text: string\n}\n\n/** Which form is open, and on which row. */\ninterface Editing {\n readonly kind: 'add' | 'alias' | 'value'\n readonly ref?: string | undefined\n}\n\n/** The check drawn inside the mark of the key in use. */\nconst CHECK_PATH = 'M3.9 8.35 6.6 11.05 12.1 5.5'\n\n/** The circle-plus drawn on the row that opens the add form. */\nconst PLUS_PATHS: readonly string[] = ['M8 3.9v8.2', 'M3.9 8h8.2']\n\n/** One 16px stroke glyph, on the same grid the injected controls draw on. */\nfunction Glyph({ paths }: { paths: readonly string[] }): ReactNode {\n return (\n <svg\n viewBox=\"0 0 16 16\"\n width={16}\n height={16}\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={1.5}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n focusable=\"false\"\n >\n {paths.map(d => (\n <path key={d} d={d} />\n ))}\n </svg>\n )\n}\n\n/**\n * What a row shows where its stored value would be.\n *\n * The host masks the value it read; when it could not read one at all, a row\n * that the store says holds nothing says so instead of pretending to hide a\n * secret, and a row the store would not answer for shows the mask alone \u2014 the\n * absence of a preview is the store's answer, not a claim about the key.\n */\nfunction previewOf(row: KeyRow, t: Translate): string {\n if (row.masked !== undefined) return row.masked\n return row.configured === false ? t('keysMissing') : '\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022'\n}\n\n/** How a row is named in its controls: its label, its preview, or its ref. */\nfunction nameOf(row: KeyRow): string {\n return row.alias ?? row.masked ?? row.ref\n}\n\n/**\n * Render the key list for one provider route.\n * @param props - see {@link KeyManagerProps}.\n * @returns the list's root element.\n */\nexport function KeyManager(props: KeyManagerProps): ReactNode {\n const { route, t } = props\n const client = props.client ?? httpKeyManagerClient\n const group = useId()\n\n const [rows, setRows] = useState<readonly KeyRow[] | undefined>(undefined)\n const [enabledRef, setEnabledRef] = useState<string | null>(null)\n const [notice, setNotice] = useState<Notice | undefined>(undefined)\n const [busy, setBusy] = useState(false)\n const [editing, setEditing] = useState<Editing | undefined>(undefined)\n const [nameDraft, setNameDraft] = useState('')\n const [valueDraft, setValueDraft] = useState('')\n const [confirming, setConfirming] = useState<string | undefined>(undefined)\n\n /**\n * Adopt whatever the host just answered. A refusal keeps the list that is\n * already on screen \u2014 a failed write changed nothing, so showing an empty\n * list would be a lie about the state of the document.\n */\n const adopt = useCallback(\n (answer: KeyAnswer, okText: string | undefined): void => {\n if (!answer.ok) {\n setNotice({ kind: 'error', text: answer.error ?? t('keysUnreachable') })\n return\n }\n setRows(answer.entries)\n setEnabledRef(answer.enabledRef)\n setNotice(okText === undefined ? undefined : { kind: 'status', text: okText })\n },\n [t],\n )\n\n useEffect(() => {\n let live = true\n void (async () => {\n const answer = await client.list(route)\n if (live) adopt(answer, undefined)\n })()\n return () => {\n live = false\n }\n }, [adopt, client, route])\n\n /** Run one mutation, then take the host's word for the new list. */\n const run = useCallback(\n async (action: () => Promise<KeyAnswer>, okText: string): Promise<void> => {\n setBusy(true)\n setConfirming(undefined)\n setEditing(undefined)\n try {\n adopt(await action(), okText)\n } finally {\n setBusy(false)\n }\n },\n [adopt],\n )\n\n const closeForm = (): void => {\n setEditing(undefined)\n setNameDraft('')\n setValueDraft('')\n }\n\n const openForm = (kind: 'alias' | 'value', row: KeyRow): void => {\n setEditing({ kind, ref: row.ref })\n setNameDraft(kind === 'alias' ? row.alias ?? '' : '')\n setValueDraft('')\n setConfirming(undefined)\n }\n\n /**\n * Write what the open form holds. The three kinds differ only in which fields\n * they showed, so they share one submit: an add stores a new reference, a\n * relabel writes the label, and a replace swaps the secret of the reference\n * already on the row \u2014 never its name, its label or its seat.\n */\n const submit = (): void => {\n if (busy || editing === undefined) return\n const kind = editing.kind\n const ref = editing.ref\n // An alias-only edit accepts an empty field: that is how a label is cleared.\n if (kind !== 'alias' && !isLegalKeyValue(valueDraft)) return\n const alias = normalizeKeyAlias(nameDraft)\n setBusy(true)\n void (async () => {\n try {\n if (kind === 'add') {\n const answer = await client.add(route, valueDraft, alias)\n adopt(answer, t('keysAdded'))\n if (answer.ok) closeForm()\n return\n }\n if (ref === undefined) return\n const answer =\n kind === 'alias' ? await client.rename(route, ref, alias) : await client.replace(route, ref, valueDraft)\n adopt(answer, kind === 'alias' ? t('keysRenamed') : t('keysReplaced'))\n if (answer.ok) closeForm()\n } finally {\n setBusy(false)\n }\n })()\n }\n\n /** The one inline form, shaped by the edit it is open for. */\n const form = (): ReactNode => {\n if (editing === undefined) return null\n const kind = editing.kind\n const wantsValue = kind !== 'alias'\n const illegal = wantsValue && valueDraft.length > 0 && !isLegalKeyValue(valueDraft)\n return (\n <div className=\"bre-keys-form\">\n <div className=\"bre-keys-form-fields\">\n {kind === 'value' ? null : (\n <input\n className=\"bre-text-input bre-keys-name\"\n type=\"text\"\n value={nameDraft}\n placeholder={t('keysAliasPlaceholder')}\n aria-label={t('keysAlias')}\n disabled={busy}\n autoFocus={kind === 'alias'}\n onChange={event => setNameDraft(event.target.value)}\n />\n )}\n {wantsValue ? (\n <input\n className=\"bre-text-input bre-keys-secret\"\n type=\"password\"\n autoComplete=\"new-password\"\n spellCheck={false}\n value={valueDraft}\n placeholder={kind === 'add' ? t('keysValuePlaceholder') : t('keysNewValuePlaceholder')}\n aria-label={t('keysValue')}\n aria-invalid={illegal}\n disabled={busy}\n autoFocus\n onChange={event => setValueDraft(event.target.value)}\n />\n ) : null}\n </div>\n {illegal ? (\n <p className=\"bre-effort-message bre-error\" role=\"alert\">\n {t('keysAddIllegal')}\n </p>\n ) : null}\n <div className=\"bre-keys-form-actions\">\n <button type=\"button\" className=\"bre-link-button\" disabled={busy} onClick={closeForm}>\n {t('keysCancel')}\n </button>\n <button\n type=\"button\"\n className=\"bre-primary-button\"\n disabled={busy || (wantsValue && !isLegalKeyValue(valueDraft))}\n onClick={submit}\n >\n {kind === 'add' ? t('keysAddConfirm') : t('keysSave')}\n </button>\n </div>\n </div>\n )\n }\n\n /**\n * The entry that adds a key: the list's own last item, or the form once it is\n * open. It is offered even when the list could not be read \u2014 adding a key\n * needs no list \u2014 so a failed read must not leave the user with no way out.\n */\n const addEntry = (): ReactNode =>\n editing?.kind === 'add' ? (\n <li className=\"bre-keys-form-row\">{form()}</li>\n ) : (\n <li className=\"bre-keys-add-row\">\n <button\n type=\"button\"\n className=\"bre-keys-add\"\n disabled={busy}\n onClick={() => {\n setEditing({ kind: 'add' })\n setNameDraft('')\n setValueDraft('')\n setConfirming(undefined)\n }}\n >\n <span className=\"bre-keys-add-mark\" aria-hidden=\"true\">\n <Glyph paths={PLUS_PATHS} />\n </span>\n {t('keysAdd')}\n </button>\n </li>\n )\n\n if (rows === undefined) {\n // Still reading: NOTHING, until the answer lands. The mount happens in the\n // same frame the card opens (flushSync), and a placeholder here would paint\n // one frame of a one-line panel that then visibly swaps to the list \u2014 the\n // \"the key section flashes when you press Edit\" report. An empty panel in\n // that frame reads as the card still opening; the list is the first thing\n // the pane ever shows. A read that FAILED says so instead of appearing to\n // load forever, and still offers the add entry.\n if (notice === undefined) return <div className=\"bre-keys\" />\n return (\n <div className=\"bre-keys\">\n <p className={'bre-effort-message bre-' + notice.kind} role={notice.kind === 'error' ? 'alert' : 'status'}>\n {notice.text}\n </p>\n <ul className=\"bre-keys-list\">{addEntry()}</ul>\n </div>\n )\n }\n\n return (\n <div className=\"bre-keys\">\n <ul className=\"bre-keys-list\">\n {rows.map(row => {\n const name = nameOf(row)\n return (\n <Fragment key={row.ref}>\n <li className=\"bre-keys-row\" data-state={row.configured === false ? 'missing' : 'ready'}>\n {row.alias === undefined ? null : <span className=\"bre-keys-alias\">{row.alias}</span>}\n <span className={row.alias === undefined ? 'bre-keys-mask bre-keys-mask-plain' : 'bre-keys-mask'}>\n {previewOf(row, t)}\n </span>\n <span className=\"bre-keys-actions\">\n {confirming === row.ref ? (\n <>\n <button\n type=\"button\"\n className=\"bre-link-button bre-keys-danger\"\n disabled={busy}\n onClick={() => void run(() => client.remove(route, row.ref), t('keysRemoved'))}\n >\n {t('keysConfirmRemove')}\n </button>\n <button\n type=\"button\"\n className=\"bre-link-button\"\n disabled={busy}\n onClick={() => setConfirming(undefined)}\n >\n {t('keysCancel')}\n </button>\n </>\n ) : (\n <>\n <button\n type=\"button\"\n className=\"bre-link-button\"\n disabled={busy}\n onClick={() => {\n setConfirming(row.ref)\n setEditing(undefined)\n }}\n >\n {t('keysRemove')}\n </button>\n <button\n type=\"button\"\n className=\"bre-link-button\"\n disabled={busy}\n onClick={() => openForm('alias', row)}\n >\n {t('keysAlias')}\n </button>\n <button\n type=\"button\"\n className=\"bre-link-button\"\n disabled={busy}\n onClick={() => openForm('value', row)}\n >\n {t('keysEdit')}\n </button>\n </>\n )}\n </span>\n {/* The enable choice sits at the END of the row: one radio per\n key, one enabled key per provider, and the control is named\n for what it does instead of for a state to hunt for. */}\n <label className=\"bre-keys-enable\" title={t('keysEnable', { name })}>\n <input\n type=\"radio\"\n name={group}\n className=\"bre-keys-enabled\"\n checked={row.enabled}\n disabled={busy}\n aria-label={t('keysEnable', { name })}\n onChange={() => void run(() => client.enable(route, row.ref), t('keysEnabledNote', { name }))}\n />\n <span className=\"bre-keys-mark\" aria-hidden=\"true\">\n {row.enabled ? <Glyph paths={[CHECK_PATH]} /> : null}\n </span>\n <span className=\"bre-keys-enable-text\">{t('keysEnabled')}</span>\n </label>\n </li>\n {editing !== undefined && editing.ref === row.ref ? (\n <li className=\"bre-keys-form-row\">{form()}</li>\n ) : null}\n </Fragment>\n )\n })}\n {addEntry()}\n </ul>\n\n {notice === undefined ? null : (\n <p className={'bre-effort-message bre-' + notice.kind} role={notice.kind === 'error' ? 'alert' : 'status'}>\n {notice.text}\n </p>\n )}\n </div>\n )\n}\n", "/**\n * The credential-reference vocabulary the API-key manager shares between both\n * halves.\n *\n * A provider's key list is a list of NAMES: every value lives in the harness\n * credential store under its own reference, and the profile records exactly one\n * of those names (`providers.<route>.apiKeyEnv`) as the reference the adapter\n * resolves when it calls the provider. This module owns the naming rules the\n * two halves must agree on \u2014 the browser half picks the next free name for a\n * new key, the host half re-validates whatever arrives \u2014 so one name can never\n * mean two different things on the two sides of the wire.\n *\n * Deliberately free of `node:` imports: the client bundle imports this module\n * too (the same way `constants.ts` is shared), and a single `node:` import\n * would drag the host into the browser build.\n *\n * @module dsh-model-think-level/key-refs\n */\n\n/** One key the manager lists for a provider route. */\nexport interface KeyEntry {\n /** Credential reference holding the value. */\n readonly ref: string\n /** User-facing label; absent means \"show the reference itself\". */\n readonly alias?: string\n}\n\n/** Longest reference the manager will mint or accept. */\nexport const KEY_REF_MAX_LENGTH = 80\n\n/** Longest alias the manager stores. A label, never a secret. */\nexport const KEY_ALIAS_MAX_LENGTH = 40\n\n/**\n * The shape of a reference the manager accepts. Looser than the credential\n * store's own POSIX-ish rule on purpose: a route may legitimately start with a\n * digit (`9router` \u2192 `9ROUTER_API_KEY`), and the store stays the authority on\n * what it will hold. This is the plugin's sanity bound, not the validation.\n */\nexport const KEY_REF_PATTERN = /^[A-Za-z0-9_]{1,80}$/\n\n/**\n * The reference the official Models page derives for a provider route: the\n * route upper-cased with every non-alphanumeric run folded to `_`, plus\n * `_API_KEY`. Mirrors the official page exactly so the first key the manager\n * adds for a provider is the very reference that page would have written.\n * @param route - the provider route key (`ofox`, `aliyun-cn`).\n * @returns the derived reference name.\n */\nexport function deriveKeyRef(route: string): string {\n return `${route.toUpperCase().replace(/[^A-Z0-9]+/g, '_')}_API_KEY`\n}\n\n/** Whether a value is a reference name this manager will pass to the store. */\nexport function isKeyRefName(value: unknown): value is string {\n return typeof value === 'string' && KEY_REF_PATTERN.test(value)\n}\n\n/**\n * The first free name in the sequence `<DERIVED>`, `<DERIVED>_2`, `<DERIVED>_3`\u2026\n * @param route - the provider route key.\n * @param taken - every reference already listed for that route.\n * @returns a reference not in `taken`.\n */\nexport function nextKeyRef(route: string, taken: readonly string[]): string {\n const used = new Set(taken)\n const base = deriveKeyRef(route)\n if (!used.has(base)) return base\n for (let suffix = 2; suffix <= 999; suffix += 1) {\n const candidate = `${base}_${suffix}`\n if (!used.has(candidate)) return candidate\n }\n // A thousand keys on one route is not a real deployment; stay unique anyway.\n return `${base}_${Date.now()}`\n}\n\n/**\n * A stored alias, or undefined when the label carries nothing.\n *\n * Control characters are dropped rather than escaped: an alias is rendered as\n * text on the official page, and a stray newline there is a rendering bug, not\n * data worth keeping.\n * @param value - whatever arrived (user input, or a hand-edited index file).\n * @returns the trimmed label, capped at {@link KEY_ALIAS_MAX_LENGTH}.\n */\nexport function normalizeKeyAlias(value: unknown): string | undefined {\n if (typeof value !== 'string') return undefined\n let cleaned = ''\n for (const char of value) {\n const code = char.codePointAt(0) ?? 0\n if (code < 32 || code === 127) continue\n cleaned += char\n }\n cleaned = cleaned.trim()\n if (cleaned.length === 0) return undefined\n return cleaned.slice(0, KEY_ALIAS_MAX_LENGTH)\n}\n\n/**\n * Whether a value is a key the official page would itself accept: printable\n * ASCII with no spaces (its own `LEGAL_API_KEY`). The manager applies the same\n * rule so a key it stores can never be one the official card would refuse.\n */\nexport function isLegalKeyValue(value: unknown): value is string {\n return typeof value === 'string' && /^[\\x21-\\x7E]+$/.test(value)\n}\n\n/** Shortest value that still gets a head and a tail; below it, a flat mask. */\nconst KEY_MASK_MIN_LENGTH = 12\n\n/**\n * A stored key's display form: a head, an ellipsis and a tail \u2014 enough to tell\n * two look-alike keys apart, never the whole secret.\n *\n * The manager shows every row this way, so a list can say which key a row holds\n * without putting anything usable on the page; the eye beside the field stays\n * the one gesture that reveals a value. A value too short to survive trimming\n * gets a fixed-width mask instead: at that length a head and a tail would be\n * most of the key.\n */\nexport function maskKeyValue(value: string): string {\n if (value.length === 0) return ''\n if (value.length < KEY_MASK_MIN_LENGTH) return '\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022'\n return `${value.slice(0, 4)}...${value.slice(-4)}`\n}\n", "/**\n * The browser half's client for the host's API-key manager.\n *\n * Every key fact \u2014 which references a provider lists, which label each carries,\n * which one is in use, and whether the credential store actually holds a value\n * \u2014 is host state: the list is the plugin's own index file, the value lives in\n * the credential store, and the \"in use\" seat is the profile's `apiKeyEnv`\n * field. All six operations therefore ride ONE same-origin route\n * ({@link KEY_INDEX_PATH}), and the browser half only ever handles references,\n * labels, booleans and masked previews. It never sees a secret it did not\n * explicitly ask for: a key value is fetched one reference at a time through the\n * existing reveal route, on a click, and is never cached.\n *\n * Fails closed. A refused fence, a route that is not in the settings document,\n * a store that cannot be written, a network error \u2014 each answers `{ ok: false }`\n * (with the host's own wording when it sent one) and the panel says so; nothing\n * here invents success, and nothing here invents an error message either.\n *\n * @module dsh-model-think-level/client/key-manager-client\n */\n\nimport { KEY_INDEX_PATH, PROVIDER_KEY_PATH } from '../constants.js'\nimport { isRecord } from '../shared.js'\n\n/** One key row as the host reports it. */\nexport interface KeyRow {\n /** Credential reference holding the value. */\n readonly ref: string\n /** Stored label; absent means \"show the reference itself\". */\n readonly alias?: string\n /** Whether this is the reference the profile currently names. */\n readonly enabled: boolean\n /** Whether the credential store holds a value; null when it could not say. */\n readonly configured: boolean | null\n /**\n * The stored value in the host's display form \u2014 a head, an ellipsis and a\n * tail. Absent when the store would not let the value be read, so a row shows\n * nothing rather than a made-up preview.\n */\n readonly masked?: string\n}\n\n/** A successful answer: the whole list, in the host's order. */\nexport interface KeyState {\n readonly ok: true\n readonly route: string\n /** The reference in use, or null when the provider names none. */\n readonly enabledRef: string | null\n readonly entries: readonly KeyRow[]\n}\n\n/** A refused answer, carrying the host's own words when it sent any. */\nexport interface KeyFailure {\n readonly ok: false\n readonly error?: string\n}\n\n/** What one call to the key route answers. */\nexport type KeyAnswer = KeyState | KeyFailure\n\n/** The manager's transport, injectable so the panel can be tested without one. */\nexport interface KeyManagerClient {\n /** Read the provider's whole key list. */\n list(route: string): Promise<KeyAnswer>\n /** Store a new key and list it, returning the list it produced. */\n add(route: string, value: string, alias: string | undefined): Promise<KeyAnswer>\n /** Make one listed reference the key in use. */\n enable(route: string, ref: string): Promise<KeyAnswer>\n /** Relabel one listed reference (undefined clears the label). */\n rename(route: string, ref: string, alias: string | undefined): Promise<KeyAnswer>\n /** Replace the stored secret of one listed reference, keeping its name. */\n replace(route: string, ref: string, value: string): Promise<KeyAnswer>\n /** Forget one listed reference and its value. */\n remove(route: string, ref: string): Promise<KeyAnswer>\n /** The stored value of one listed reference, or undefined. */\n reveal(route: string, ref: string): Promise<string | undefined>\n}\n\n/** The host's wording for a refusal, if it sent one. */\nfunction messageOf(body: Record<string, unknown>): string | undefined {\n for (const field of ['error', 'message'] as const) {\n const value = body[field]\n if (typeof value === 'string' && value.length > 0) return value\n }\n return undefined\n}\n\n/** One row, or undefined when the payload is not one. */\nfunction rowOf(value: unknown): KeyRow | undefined {\n if (!isRecord(value)) return undefined\n const ref = value['ref']\n if (typeof ref !== 'string' || ref.length === 0) return undefined\n const alias = value['alias']\n const configured = value['configured']\n const masked = value['masked']\n return {\n ref,\n ...(typeof alias === 'string' && alias.length > 0 ? { alias } : {}),\n enabled: value['enabled'] === true,\n configured: typeof configured === 'boolean' ? configured : null,\n ...(typeof masked === 'string' && masked.length > 0 ? { masked } : {}),\n }\n}\n\n/** A list answer, or undefined when the payload is not one. */\nfunction stateOf(body: Record<string, unknown>): KeyState | undefined {\n const entries = body['entries']\n if (!Array.isArray(entries)) return undefined\n const rows: KeyRow[] = []\n for (const entry of entries) {\n const row = rowOf(entry)\n if (row !== undefined) rows.push(row)\n }\n const enabledRef = body['enabledRef']\n const route = body['route']\n return {\n ok: true,\n route: typeof route === 'string' ? route : '',\n enabledRef: typeof enabledRef === 'string' && enabledRef.length > 0 ? enabledRef : null,\n entries: rows,\n }\n}\n\n/**\n * Read one answer, whatever the status was.\n *\n * The host answers its refusals with a 4xx and a body, so the body is read\n * either way: the status alone would throw away the only copy of the reason.\n */\nasync function answerOf(response: Response): Promise<KeyAnswer> {\n let body: unknown\n try {\n body = await response.json()\n } catch {\n body = undefined\n }\n if (!isRecord(body)) return { ok: false }\n if (body['ok'] === true) {\n const state = stateOf(body)\n if (state !== undefined) return state\n }\n const error = messageOf(body)\n return error === undefined ? { ok: false } : { ok: false, error }\n}\n\n/** One call to the key route: a GET with no payload, a POST with one. */\nasync function request(route: string, payload?: Record<string, unknown>): Promise<KeyAnswer> {\n const url = `${KEY_INDEX_PATH}?route=${encodeURIComponent(route)}`\n try {\n const response = await fetch(\n url,\n payload === undefined\n ? { method: 'GET', cache: 'no-store', credentials: 'same-origin' }\n : {\n method: 'POST',\n cache: 'no-store',\n credentials: 'same-origin',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n },\n )\n return await answerOf(response)\n } catch {\n return { ok: false }\n }\n}\n\n/** The live client: same-origin fetch against the host's route. */\nexport const httpKeyManagerClient: KeyManagerClient = {\n list: route => request(route),\n add: (route, value, alias) => request(route, { op: 'add', value, ...(alias === undefined ? {} : { alias }) }),\n enable: (route, ref) => request(route, { op: 'enable', ref }),\n rename: (route, ref, alias) => request(route, { op: 'rename', ref, ...(alias === undefined ? {} : { alias }) }),\n remove: (route, ref) => request(route, { op: 'remove', ref }),\n replace: (route, ref, value) => request(route, { op: 'value', ref, value }),\n async reveal(route, ref) {\n try {\n const response = await fetch(\n `${PROVIDER_KEY_PATH}?route=${encodeURIComponent(route)}&ref=${encodeURIComponent(ref)}`,\n {\n method: 'GET',\n // A secret must not sit in the HTTP cache.\n cache: 'no-store',\n credentials: 'same-origin',\n },\n )\n if (!response.ok) return undefined\n const body = (await response.json()) as { ok?: unknown; key?: unknown }\n if (!isRecord(body) || body['ok'] !== true) return undefined\n const key = body['key']\n return typeof key === 'string' && key.length > 0 ? key : undefined\n } catch {\n return undefined\n }\n },\n}\n", "/**\n * The plugin's own outline glyphs.\n *\n * Shared because two injected controls draw one: the API-key eye, which swaps\n * between two glyphs by attribute, and the key field's alias chip, which is\n * icon-only so that it does not repeat the official \"API key\" label with the\n * word for the same thing. Every glyph is a stroke-only outline on the same\n * 16px grid, inherits `currentColor`, and is hidden from assistive tech \u2014 the\n * button holding it carries the meaning in its own `aria-label`.\n *\n * @module dsh-model-think-level/client/injection/icon\n */\n\n/** The namespace every SVG node here is created in. */\nexport const SVG_NS = 'http://www.w3.org/2000/svg'\n\n/** Marks a glyph, so the stylesheet can show one of several, and a sweep find it. */\nexport const ICON_ATTR = 'data-bre-icon'\n\n/** One outline path of a glyph, in the 16px viewBox. */\nexport function svgPath(d: string): SVGPathElement {\n const path = document.createElementNS(SVG_NS, 'path')\n path.setAttribute('d', d)\n return path\n}\n\n/** A stroke-only 16px glyph built from outline paths. */\nexport function svgGlyph(kind: string, paths: readonly string[]): SVGElement {\n const svg = document.createElementNS(SVG_NS, 'svg')\n svg.setAttribute('viewBox', '0 0 16 16')\n svg.setAttribute('width', '16')\n svg.setAttribute('height', '16')\n svg.setAttribute('fill', 'none')\n svg.setAttribute('stroke', 'currentColor')\n svg.setAttribute('stroke-width', '1.5')\n svg.setAttribute('stroke-linecap', 'round')\n svg.setAttribute('stroke-linejoin', 'round')\n svg.setAttribute('aria-hidden', 'true')\n svg.setAttribute('focusable', 'false')\n svg.setAttribute(ICON_ATTR, kind)\n svg.append(...paths.map(svgPath))\n return svg\n}\n", "/**\n * The eye on a provider's API-key field.\n *\n * Two jobs, one control. The official input is `<input type=\"password\">` whose\n * value is React's own draft state (`keyDraft`), and a SAVED key is never part\n * of it: a configured provider shows a placeholder instead, because the harness\n * keeps provider keys write-only (the settings document carries only the\n * profile's `apiKeyEnv` reference, and the credential service reports\n * `{ configured }` \u2014 never the value). So on a settled card the eye has nothing\n * to unmask, and the plugin asks its own host for the stored value\n * ({@link loadProviderKey}, the `PROVIDER_KEY_PATH` route) when \u2014 and only\n * when \u2014 the user clicks it with an empty field. A field holding a draft the\n * user just typed is never touched: the fetch is skipped and the click only\n * swaps the mask.\n *\n * The read is deliberately constrained:\n *\n * - it happens on an explicit click, never on render, and answers `undefined`\n * for every failure (no route, refused fence, reference-free profile,\n * unconfigured credential), which leaves the field masked;\n * - it is never cached (the request says `no-store` and the route answers\n * with the same header) and never logged;\n * - hiding the field again takes the borrowed value back out \u2014 the draft\n * returns to empty, which the official form reads as \"keep the stored key\",\n * so revealing can never overwrite the credential by accident. A value the\n * user edits after revealing is theirs, and is left alone.\n *\n * The button is the one node this plugin inserts into official DOM, and it is\n * inserted deliberately: the affordance only reads as an eye at the END of the\n * input if it sits inside the input's own box, and the official field has no\n * wrapper to hang it on. The trade is contained \u2014\n *\n * - the field itself is never restructured, and nothing official is moved or\n * removed: the button is an absolutely positioned LEAF appended last;\n * - React ignores trailing foreign children when it reconciles the field's\n * known children, so the node survives every re-render (each keystroke\n * re-renders this field);\n * - React never writes the input's `type` back (its props do not change), so\n * the revealed state survives too;\n * - the pass removes the button, the marker and the class on cleanup, and\n * restores the mask, so a withdrawn takeover leaves nothing behind.\n *\n * @module dsh-model-think-level/client/injection/key-reveal\n */\n\nimport { PROVIDER_KEY_PATH } from '../../constants.js'\nimport { setAttr, setClass, setHidden } from './dom.js'\nimport { svgGlyph } from './icon.js'\n\n/** Set on the field that carries the key input. */\nexport const KEY_FIELD_CLASS = 'bre-key-field'\n\n/** The eye button inside that field. */\nexport const KEY_EYE_CLASS = 'bre-key-eye'\n\n/** Set on the key input, so it is still findable once it is unmasked. */\nconst KEY_INPUT_ATTR = 'data-bre-key-input'\n\n/** The button's label while the key is masked, and while it is revealed. The\n * pass writes both on every reconcile, so a click only has to pick one and the\n * copy stays current across a language switch without a re-render. */\nconst SHOW_LABEL_ATTR = 'data-bre-eye-show'\nconst HIDE_LABEL_ATTR = 'data-bre-eye-hide'\n\n/** The two glyphs the eye swaps between, in the shared 16px grid. */\nconst EYE_PATHS = [\n 'M1.5 8S4 3.75 8 3.75 14.5 8 14.5 8 12 12.25 8 12.25 1.5 8 1.5 8Z',\n 'M6.2 8a1.8 1.8 0 1 0 3.6 0 1.8 1.8 0 0 0-3.6 0Z',\n]\n\n/** The repo's inline translator shape (see `HeadersEditor`, `ComposerSlider`). */\nexport type Translator = (key: string, params?: Record<string, string | number>) => string\n\n/** What a reconcile tells the field's eye. */\nexport interface KeyRevealDeps {\n /** Copy for the button's labels and its failure tooltip. */\n readonly t: Translator\n /**\n * The provider route this card edits, when the card announces one. Absent on\n * the add-provider form (no route exists yet), which is what keeps the eye\n * there a plain mask toggle.\n */\n readonly route?: string | undefined\n /** How to resolve the stored credential; injectable for tests. */\n readonly loadKey?: ((route: string) => Promise<string | undefined>) | undefined\n}\n\n/**\n * The latest deps of each dressed field.\n *\n * The eye's click handler is created once and then lives as long as the field,\n * while the pass re-visits it with fresh deps (a language switch, a route that\n * changed). Keeping them here means the handler always reads the CURRENT copy\n * and route instead of the ones captured when the button appeared.\n */\nconst DEPS = new WeakMap<HTMLElement, KeyRevealDeps>()\n\n/** The key input of a field, when the field is the key field: the masked input\n * on a first pass, and \u2014 once revealed, where a `[type=password]` probe would\n * no longer find it \u2014 the input this pass marked. */\nfunction keyInputOf(field: HTMLElement): HTMLInputElement | undefined {\n const marked = field.querySelector(`input[${KEY_INPUT_ATTR}]`)\n if (marked instanceof HTMLInputElement) return marked\n const masked = field.querySelector('input[type=\"password\"]')\n return masked instanceof HTMLInputElement ? masked : undefined\n}\n\n/** The eye this pass put in a field, matched by class and by being a direct\n * child, so a nested button of the official page can never be mistaken for it. */\nfunction eyeOf(field: HTMLElement): HTMLButtonElement | undefined {\n for (const child of Array.from(field.children)) {\n if (child instanceof HTMLButtonElement && child.classList.contains(KEY_EYE_CLASS)) return child\n }\n return undefined\n}\n\n/**\n * Give an editor field an eye at the end of its input, if it holds the API key.\n *\n * Runs on every reconcile of the card, so it only ever writes what differs; a\n * field with no key input is handed to {@link resetKeyField} instead, which is\n * how a takeover that has moved on (a provider whose key field is gone) drops\n * the affordance it no longer needs.\n */\nexport function revealKeyField(field: HTMLElement, deps: KeyRevealDeps): void {\n const input = keyInputOf(field)\n if (input === undefined) {\n resetKeyField(field)\n return\n }\n DEPS.set(field, deps)\n setAttr(input, KEY_INPUT_ATTR, '')\n setClass(field, KEY_FIELD_CLASS, true)\n const eye = eyeOf(field) ?? createEye(field)\n setAttr(eye, SHOW_LABEL_ATTR, deps.t('keyRevealShow'))\n setAttr(eye, HIDE_LABEL_ATTR, deps.t('keyRevealHide'))\n syncEye(eye, input)\n}\n\n/** Undo {@link revealKeyField}: drop the button, the class, the marker, and\n * re-mask the input, leaving the official field exactly as it was rendered. */\nexport function resetKeyField(field: HTMLElement): void {\n const eye = eyeOf(field)\n if (eye !== undefined) eye.remove()\n DEPS.delete(field)\n setClass(field, KEY_FIELD_CLASS, false)\n const input = field.querySelector(`input[${KEY_INPUT_ATTR}]`)\n if (input instanceof HTMLInputElement) {\n input.removeAttribute(KEY_INPUT_ATTR)\n input.type = 'password'\n }\n}\n\n/**\n * Ask the host for the credential a provider profile names.\n *\n * Fails closed by design: every refusal \u2014 an unknown route, a profile with no\n * reference, a credential that is not configured, a refused trust fence, a\n * network error \u2014 is `undefined`, and the caller leaves the field masked rather\n * than uncovering an empty box.\n */\nexport async function loadProviderKey(route: string): Promise<string | undefined> {\n try {\n const response = await fetch(`${PROVIDER_KEY_PATH}?route=${encodeURIComponent(route)}`, {\n method: 'GET',\n // A secret must not sit in the HTTP cache.\n cache: 'no-store',\n // The route is same-origin; sending credentials would add nothing and\n // keeps the request a plain GET.\n credentials: 'same-origin',\n })\n if (!response.ok) return undefined\n const body = (await response.json()) as { ok?: unknown; key?: unknown }\n if (body === null || typeof body !== 'object' || body.ok !== true) return undefined\n return typeof body.key === 'string' && body.key.length > 0 ? body.key : undefined\n } catch {\n return undefined\n }\n}\n\n/** State only, never structure: the button exists by then. Reads the labels\n * off the button rather than taking a translator, so the click handler does\n * not hold a stale copy of the copy. */\nfunction syncEye(eye: HTMLButtonElement, input: HTMLInputElement): void {\n const revealed = input.type !== 'password'\n setAttr(eye, 'aria-pressed', revealed ? 'true' : 'false')\n const label = eye.getAttribute(revealed ? HIDE_LABEL_ATTR : SHOW_LABEL_ATTR)\n if (label !== null) {\n setAttr(eye, 'aria-label', label)\n // Same idiom as the drag grip and the provider switch: a tooltip for a\n // control whose state only an icon shows.\n setAttr(eye, 'title', label)\n }\n // A disabled input cannot be edited, so the eye has nothing to reveal.\n setHidden(eye, input.disabled)\n}\n\n/**\n * Write into a React-controlled input the way the user's own typing would: the\n * prototype setter bypasses React's value tracker (an instance-level override),\n * and the bubbling `input` event then lets the official component take the\n * value into its draft state \u2014 so a re-render keeps it instead of reverting to\n * the empty draft.\n */\nfunction writeInputValue(input: HTMLInputElement, value: string): void {\n const descriptor = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')\n if (descriptor?.set === undefined) input.value = value\n else descriptor.set.call(input, value)\n input.dispatchEvent(new Event('input', { bubbles: true }))\n}\n\nfunction createEye(field: HTMLElement): HTMLButtonElement {\n const eye = document.createElement('button')\n eye.type = 'button'\n eye.className = KEY_EYE_CLASS\n eye.append(svgIcon('eye'), svgIcon('eye-off'))\n /** The stored value this eye wrote into the field, so hiding can take it\n * back out. Held in the closure, not in an attribute: the value is already in\n * the input, and a second copy in the DOM would outlive the reveal. */\n let borrowed: string | undefined\n\n const toggle = async (): Promise<void> => {\n const input = keyInputOf(field)\n const deps = DEPS.get(field)\n if (input === undefined || deps === undefined) return\n if (input.type !== 'password') {\n // Hiding: give back a value this eye borrowed. A value the user edited\n // after revealing is theirs, and stays.\n if (borrowed !== undefined && input.value === borrowed) writeInputValue(input, '')\n borrowed = undefined\n input.type = 'password'\n syncEye(eye, input)\n return\n }\n const route = deps.route\n if (route !== undefined && borrowed === undefined && input.value.length === 0) {\n setAttr(eye, 'aria-busy', 'true')\n let key: string | undefined\n try {\n key = await (deps.loadKey ?? loadProviderKey)(route)\n } catch {\n key = undefined\n }\n if (eye.hasAttribute('aria-busy')) eye.removeAttribute('aria-busy')\n if (key === undefined) {\n // Nothing to show: stay masked and say so in the tooltip, rather than\n // flipping an empty box to plain text and pretending it worked.\n const failed = deps.t('keyRevealFailed')\n setAttr(eye, 'aria-label', failed)\n setAttr(eye, 'title', failed)\n return\n }\n // A draft typed while the host was answering wins over the stored key.\n if (keyInputOf(field) === input && input.value.length === 0) {\n borrowed = key\n writeInputValue(input, key)\n }\n }\n input.type = 'text'\n syncEye(eye, input)\n }\n\n eye.addEventListener('click', (event: MouseEvent): void => {\n // The eye is this plugin's own control inside a form the official page\n // owns: keep the gesture from reaching their handlers and from submitting.\n event.preventDefault()\n event.stopPropagation()\n void toggle()\n })\n field.append(eye)\n return eye\n}\n\n/** A stroke-only 16px glyph that inherits the button's colour. */\nfunction svgIcon(kind: 'eye' | 'eye-off'): SVGElement {\n const paths = kind === 'eye-off' ? [...EYE_PATHS, 'M2.5 13.5 13.5 2.5'] : EYE_PATHS\n return svgGlyph(kind, paths)\n}\n", "/**\n * The provider editor's three-tab takeover for the official Models settings\n * page.\n *\n * The official editor presents one provider as a single long column \u2014 identity\n * fields, then the model list, then (one section further up the card) this\n * plugin's request-header editor \u2014 and the column keeps growing with every\n * feature. This pass re-presents the same content as three tabs:\n *\n * `provider` \u2014 the official identity fields (display name, key, base URL,\n * protocol); the editor's own header and action row stay put.\n * `models` \u2014 the official model list, the rows the effort editor seats\n * into.\n * `advanced` \u2014 the plugin's request-header section, which the provider-card\n * slot already mounts and which owns its own save/cancel.\n *\n * The takeover is pure CSS, by the same iron rule every pass here follows:\n * React owns the official children, so none of them move. The card carries the\n * active tab (`data-bre-tab`), each region carries its tab membership\n * (`data-bre-region`), and the stylesheet \u2014 not this module \u2014 decides what is\n * visible.\n *\n * The bar is NOT part of that DOM, and this module never inserts it. It is\n * React-rendered by {@link EditorTabBar} inside the plugin's own request-header\n * mount, which the provider-card slot already guarantees is a child of the\n * card; the stylesheet reveals it the moment the card carries the takeover\n * class. An earlier revision inserted the bar straight into the official child\n * list from here, which made it a foreign node in a React-reconciled list: it\n * was at the mercy of the next render, and the insertion itself needed the\n * headers mount to be a DIRECT child of the card \u2014 an anchor this module cannot\n * verify and which threw (aborting the whole scan) when it did not hold. Owning\n * the bar inside our own subtree removes both hazards: the pass below only ever\n * writes attributes, and the slot component publishes the card's open state the\n * same way.\n *\n * Everything degrades to the official page: a card whose structure this pass\n * cannot recognise (host redesign, partial render) is left exactly as the\n * official page built it, a card whose takeover throws is rolled back the same\n * way, and every attribute write is compared before it lands so the scan never\n * feeds the MutationObserver its own tail.\n *\n * The same visit also dresses the provider's API-key field with its reveal eye\n * ({@link revealKeyField}) and its key controls ({@link enhanceKeyManagerField}),\n * because that field's shape is decided by exactly the same classification and\n * its affordances have to come and go with the takeover.\n *\n * @module dsh-model-think-level/client/injection/editor-tabs\n */\n\nimport { setAttr, setClass, setHidden, setText } from './dom.js'\nimport { KEY_MANAGED_CLASS, enhanceKeyManagerField, resetKeyManagerField, teardownKeyManager } from './key-manager.js'\nimport { KEY_FIELD_CLASS, resetKeyField, revealKeyField } from './key-reveal.js'\n\n/** Marks the tab bar the plugin's own tab-bar component renders (one per card). */\nconst TABS_CLASS = 'bre-editor-tabs'\n\n/** One button inside the bar; carries its tab id. */\nconst TAB_BUTTON_CLASS = 'bre-editor-tab'\n\n/** The attribute holding a button's tab id. */\nconst TAB_ID_ATTR = 'data-bre-tab-id'\n\n/** Set on a card whose editor has been re-presented as tabs. */\nconst TABBED_CLASS = 'bre-tabbed'\n\n/** The card's active tab id; the region-visibility CSS keys on it. */\nconst TAB_ATTR = 'data-bre-tab'\n\n/**\n * The provider route a card edits, as published by the provider-card slot on\n * its own mount (`src/client/injection/provider-card-slot.ts` writes\n * `data-bre-provider={route}` on `.bre-headers-host`). Read here rather than\n * imported: an import of the slot would close a cycle back through the tab bar\n * this module backs. Absent on the add-provider form, which edits no route yet.\n */\nconst PROVIDER_ROUTE_ATTR = 'data-bre-provider'\n\n/** Tags an element as the pane content of one tab. */\nconst REGION_ATTR = 'data-bre-region'\n\n/** Tags the official editor body, hidden wholesale on the advanced tab. */\nconst BODY_ATTR = 'data-bre-editor-body'\n\n/** Remembers a `<details>` this pass forced open (the official \"Customized\"\n * group starts collapsed; with the tab bar in charge it must stay expanded).\n * The value records the state the pass found, so cleanup can restore it. */\nconst DETAILS_MEMO_ATTR = 'data-bre-details'\n\n/** The plugin's request-header mount inside a provider card. It stays visible\n * on every tab \u2014 it carries the tab bar; only the section inside it is a pane. */\nconst HEADERS_HOST_SELECTOR = '.bre-headers-host'\n\n/** The request-header section itself: the `advanced` pane. */\nconst HEADERS_SECTION_SELECTOR = '.bre-headers'\n\n/** Tab ids, in display order. */\nconst TAB_PROVIDER = 'provider'\nconst TAB_MODELS = 'models'\nconst TAB_ADVANCED = 'advanced'\nconst TAB_IDS = [TAB_PROVIDER, TAB_MODELS, TAB_ADVANCED] as const\n\n/**\n * A region id that is NEVER a tab. The pane rules only ever reveal the three\n * ids above, so a region tagged with this one stays hidden on every tab \u2014 which\n * is exactly what a part of the editor that the card already shows elsewhere\n * needs. Today that is the editor's own header (its title repeats the card\n * row's provider name, and its route tag repeats the row's route tag).\n */\nconst REGION_NONE = 'none'\n\n/** A tab id, as both the bar and the card attribute spell it. */\nexport type EditorTabId = (typeof TAB_IDS)[number]\n\n/** The tab ids, in display order, for the tab-bar component. */\nexport const EDITOR_TAB_IDS: readonly EditorTabId[] = TAB_IDS\n\n/** The bar's class, a button's class and a button's tab-id attribute, exported\n * so the component that renders the bar and the pass that labels it spell the\n * contract once. */\nexport const EDITOR_TABS_CLASS = TABS_CLASS\nexport const EDITOR_TAB_BUTTON_CLASS = TAB_BUTTON_CLASS\nexport const EDITOR_TAB_ID_ATTR = TAB_ID_ATTR\n\n/** The structural probes of an OPEN official editor \u2014 the same signals the\n * editor reconcile and the write fence already trust. */\nconst EDITOR_PROBE =\n '[class*=\"editorActions\"], [class*=\"modelList\"], [class*=\"modelCatalog\"], [class*=\"modelEntry\"]'\n\n/** Elements that ARE the model list itself \u2014 classified as the models pane on\n * sight. The live editor nests the catalog inside the \"Customized\" disclosure\n * together with the identity fields; anything up to this boundary is a group\n * to step inside, and the boundary itself is where the sorting stops (model\n * rows carry inputs of their own, which must never sort as provider fields). */\nconst MODELS_TERMINAL = '[class*=\"modelCatalog\"], [class*=\"modelList\"], [class*=\"modelEntry\"]'\n\n/** A provider card: a row with an open editor, or the create-provider card. */\nconst CARD_PROBE = '[class*=\"rowCard\"], [class*=\"addCard\"], li'\n\n/** Per-scan bookkeeping (one instance lives in `models-page.ts`). */\nexport interface EditorTabsState {\n /** Cards whose takeover threw and had to be rolled back. A card that cannot\n * be taken over must not repeat its complaint on every scan. */\n readonly warned: WeakSet<Element>\n}\n\nexport interface EditorTabsDeps {\n /** The plugin's own translator, for the tab labels. */\n t: (key: string, params?: Record<string, string | number>) => string\n}\n\nexport function createEditorTabsState(): EditorTabsState {\n return { warned: new WeakSet() }\n}\n\n/**\n * The card a plugin mount sits in. Both the slot component (publishing the\n * card's open state, switching tabs) and this pass resolve the card through\n * this one rule, so a card can never be taken over by one and addressed by the\n * other.\n */\nexport function editorCardOf(element: Element | null): HTMLElement | null {\n return element === null ? null : element.closest<HTMLElement>(CARD_PROBE)\n}\n\n/**\n * Switch one card to a tab. The card attribute is the single source of truth:\n * the stylesheet swaps the pane on the attribute alone, and the bar's buttons\n * follow it here.\n */\nexport function selectEditorTab(card: HTMLElement, id: EditorTabId): void {\n setAttr(card, TAB_ATTR, id)\n syncSelected(card)\n}\n\n/**\n * Visit every open provider editor and make sure it presents as tabs.\n * Idempotent: a card that already carries the takeover is only relabelled,\n * which is also how a language switch reaches the page. Cards the pass no\n * longer recognises as open fall back to the official one-column layout.\n */\nexport function reconcileEditorTabs(root: HTMLElement, deps: EditorTabsDeps, state: EditorTabsState): void {\n if (!root.isConnected) return\n const open = new Set<HTMLElement>()\n for (const probe of Array.from(root.querySelectorAll<HTMLElement>(EDITOR_PROBE))) {\n const card = probe.closest<HTMLElement>(CARD_PROBE)\n if (card !== null) open.add(card)\n }\n for (const card of Array.from(root.querySelectorAll<HTMLElement>(`.${TABBED_CLASS}`))) {\n if (!open.has(card)) cleanupCard(card)\n }\n for (const card of open) {\n try {\n applyTabs(card, deps)\n } catch (error) {\n // One unprocessable card must not cost the page its other cards. Roll the\n // takeover back \u2014 the official editor then renders exactly as the\n // official page built it \u2014 and say so once per card rather than on every\n // scan. `cleanupCard` only removes attributes and classes, so the\n // rollback cannot throw a second time.\n if (!state.warned.has(card)) {\n state.warned.add(card)\n console.warn('[bre] editor tabs: rolled a provider card back to the official layout:', error)\n }\n cleanupCard(card)\n }\n }\n}\n\n/** Remove every takeover mark. The bar is the tab-bar component's own DOM and\n * is hidden, not removed, by dropping the class it is revealed under. */\nexport function teardownEditorTabs(): void {\n for (const card of Array.from(document.querySelectorAll<HTMLElement>(`.${TABBED_CLASS}`))) {\n card.classList.remove(TABBED_CLASS)\n card.removeAttribute(TAB_ATTR)\n card.removeAttribute(BODY_ATTR)\n for (const el of Array.from(card.querySelectorAll<HTMLElement>(`[${REGION_ATTR}]`))) {\n el.removeAttribute(REGION_ATTR)\n }\n }\n // Stray marks on cards the class already left (mid-cleanup shapes) \u2014 cheap\n // to sweep document-wide once, on teardown only.\n for (const el of Array.from(document.querySelectorAll<HTMLElement>(`[${TAB_ATTR}]`))) el.removeAttribute(TAB_ATTR)\n for (const el of Array.from(document.querySelectorAll<HTMLElement>(`[${REGION_ATTR}]`))) {\n el.removeAttribute(REGION_ATTR)\n }\n for (const el of Array.from(document.querySelectorAll<HTMLElement>(`[${BODY_ATTR}]`))) {\n el.removeAttribute(BODY_ATTR)\n }\n for (const group of Array.from(document.querySelectorAll<HTMLDetailsElement>(`details[${DETAILS_MEMO_ATTR}]`))) {\n group.removeAttribute('open')\n group.removeAttribute(DETAILS_MEMO_ATTR)\n }\n // The reveal eye is the one node this pass inserts into official DOM, so it\n // needs the same stray sweep: whatever card it was left on, it goes \u2014 with\n // the mask restored to the input it was unmasking.\n for (const field of Array.from(document.querySelectorAll<HTMLElement>(`.${KEY_FIELD_CLASS}`))) {\n resetKeyField(field)\n }\n // The alias chip and the add button are the other two nodes this pass\n // inserts into official DOM, and the panel they open is a body-level layer:\n // both go the same way, and the layer is closed outright.\n for (const field of Array.from(document.querySelectorAll<HTMLElement>(`.${KEY_MANAGED_CLASS}`))) {\n resetKeyManagerField(field)\n }\n teardownKeyManager()\n}\n\n/** The body of the card's open editor, or undefined when unrecognisable. */\nfunction editorBodyOf(card: HTMLElement): HTMLElement | undefined {\n for (const child of Array.from(card.children)) {\n if (!(child instanceof HTMLElement)) continue\n // The card's header and action rows are \"editor\"-classed too; the body is\n // the remaining editor-classed child that actually holds the form.\n if (child.matches('[class*=\"editorHeader\"], [class*=\"editorTitle\"], [class*=\"editorActions\"], [class*=\"editorRoute\"]')) {\n continue\n }\n if (!child.matches('[class*=\"editor\"]')) continue\n if (child.querySelector(EDITOR_PROBE) !== null || child.querySelector('input, select') !== null) return child\n }\n // Climb from the probes instead: the body is the probe's ancestor hanging\n // directly off the card.\n let node: HTMLElement | null = card.querySelector<HTMLElement>(EDITOR_PROBE)\n while (node !== null && node.parentElement !== card) node = node.parentElement\n return node !== null && node !== card ? node : undefined\n}\n\n/** One editor body sorted into tab panes. */\ninterface BodyRegions {\n /** The editor's own header (title + route tag). The card row directly above\n * already shows both, so the pass tags it {@link REGION_NONE}: hidden on\n * every tab. */\n header?: HTMLElement\n /** The model list's container; absent when the provider has no catalogue. */\n models?: HTMLElement\n /** The commit/cancel row; always visible. */\n actions: HTMLElement\n /** Every remaining child \u2014 the provider identity fields. */\n fields: HTMLElement[]\n}\n\n/**\n * Sort the body into panes. Undefined when the shape is not the editor this\n * module knows: the identity fields and the action row must be present, or the\n * card is left alone.\n *\n * The model list is deliberately NOT required. A provider with no catalogue\n * yet still has an editor worth tabbing \u2014 the fields are exactly the ones a\n * user is looking for \u2014 so a missing catalogue only costs the Models tab\n * (which the bar hides) rather than the whole takeover.\n *\n * The live editor does not keep its panes as flat siblings: identity fields\n * and the model catalog share one `<details>` group (\"Customized\"), so a\n * child that carries the model probes *and* form controls is treated as a\n * grouping wrapper \u2014 its children are sorted instead of the group itself\n * becoming one pane. The sort never steps past `MODELS_TERMINAL`, where the\n * catalog's own model rows (inputs of their own) live.\n */\nfunction classifyBody(body: HTMLElement): BodyRegions | undefined {\n const regions = {\n header: undefined as HTMLElement | undefined,\n models: undefined as HTMLElement | undefined,\n actions: undefined as HTMLElement | undefined,\n fields: [] as HTMLElement[],\n }\n classifyChildren(body, regions, 0)\n const { actions, fields, header, models } = regions\n if (actions === undefined || fields.length === 0) return undefined\n if (!fields.some((field) => field.querySelector('input, select, textarea') !== null)) return undefined\n return { header, models, actions, fields }\n}\n\n/** Recursion cap: the live nesting is body \u2192 details \u2192 body \u2192 catalog. */\nconst CLASSIFY_MAX_DEPTH = 4\n\ntype BodyRegionsDraft = {\n header: HTMLElement | undefined\n models: HTMLElement | undefined\n actions: HTMLElement | undefined\n fields: HTMLElement[]\n}\n\nfunction classifyChildren(container: HTMLElement, regions: BodyRegionsDraft, depth: number): void {\n if (depth > CLASSIFY_MAX_DEPTH) return\n for (const child of Array.from(container.children)) {\n if (!(child instanceof HTMLElement)) continue\n // Plugin-authored subtrees are nobody's pane. NOTE: the region tags are\n // NOT ownership \u2014 they mark this pass's own classification of official\n // children, and filtering on them would make the next scan un-classify\n // everything it classified the pass before.\n if (child.matches('[class*=\"bre-\"], [data-plugin]')) continue\n if (regions.header === undefined && child.matches('[class*=\"editorHeader\"], [class*=\"editorTitle\"]')) {\n regions.header = child\n continue\n }\n if (child.matches('[class*=\"editorActions\"]')) {\n regions.actions ??= child\n continue\n }\n if (child.matches(MODELS_TERMINAL)) {\n regions.models ??= child\n continue\n }\n if (child.querySelector(EDITOR_PROBE) !== null) {\n // A grouping wrapper \u2014 fields and the model list together. Step inside.\n classifyChildren(child, regions, depth + 1)\n continue\n }\n if (child.querySelector('[class*=\"editorActions\"]') !== null) {\n regions.actions ??= child\n continue\n }\n if (child.querySelector('input, select, textarea') !== null) {\n regions.fields.push(child)\n }\n // Summaries, error/hint paragraphs, bare wrappers \u2014 nobody's pane.\n }\n}\n\n/** The provider route this card edits, when the slot has published one. */\nfunction providerRouteOf(card: HTMLElement): string | undefined {\n const host = card.querySelector<HTMLElement>(`[${PROVIDER_ROUTE_ATTR}]`)\n const route = host?.getAttribute(PROVIDER_ROUTE_ATTR)\n return route === null || route === undefined || route === '' ? undefined : route\n}\n\n/** Tag one open editor card for the takeover. */\nfunction applyTabs(card: HTMLElement, deps: EditorTabsDeps): void {\n const body = editorBodyOf(card)\n if (body === undefined) {\n cleanupCard(card)\n return\n }\n const regions = classifyBody(body)\n if (regions === undefined) {\n cleanupCard(card)\n return\n }\n const headersSection = card.querySelector<HTMLElement>(HEADERS_SECTION_SELECTOR)\n\n // One route for the whole pane: every field here belongs to the same\n // provider, and the eye and the key panel both use it to name the provider\n // whose keys they act on.\n const route = providerRouteOf(card)\n for (const field of regions.fields) {\n setAttr(field, REGION_ATTR, TAB_PROVIDER)\n // Dresses the API-key field with its reveal eye; a no-op on every other\n // field of the pane.\n revealKeyField(field, { t: deps.t, route })\n // \u2026and with the alias chip and the add button that open the key panel,\n // likewise only on the field that holds the key input.\n enhanceKeyManagerField(field, { t: deps.t, route })\n }\n // The editor's own header repeats the card row's provider name and route tag,\n // and that row sits right above it: rather than give it a pane of its own it\n // is tagged with a region id no tab can select, so it stays hidden. It is\n // tagged rather than removed so cleanup restores the official markup by\n // dropping the one attribute.\n if (regions.header !== undefined) setAttr(regions.header, REGION_ATTR, REGION_NONE)\n if (regions.models !== undefined) setAttr(regions.models, REGION_ATTR, TAB_MODELS)\n setAttr(body, BODY_ATTR, '')\n // The MOUNT is not the pane: it carries the bar and stays visible on every\n // tab, so only the section inside it is tagged (and hidden off `advanced`).\n if (headersSection !== null) setAttr(headersSection, REGION_ATTR, TAB_ADVANCED)\n forceGroupsOpen(body)\n\n // A tab whose pane is not on this card steps aside: the model list on a\n // provider with no catalogue, the section while the slot has not rendered it\n // (pre-first-render, or the headers feature off). A card sitting on a tab\n // that just became unavailable falls back to the first one.\n const available = new Set<string>([TAB_PROVIDER])\n if (regions.models !== undefined) available.add(TAB_MODELS)\n if (headersSection !== null) available.add(TAB_ADVANCED)\n const active = card.getAttribute(TAB_ATTR) ?? TAB_PROVIDER\n setAttr(card, TAB_ATTR, available.has(active) ? active : TAB_PROVIDER)\n setClass(card, TABBED_CLASS, true)\n\n syncTabBar(card, deps, available)\n}\n\n/**\n * Keep the bar in step with the card.\n *\n * The bar's STRUCTURE belongs to the tab-bar component; this pass owns the copy\n * (a language switch relabels the official page, and the bar must be relabelled\n * with it), the availability of each pane, and the selected state. All three\n * are written only when they differ, so a settled page produces no mutation of\n * its own.\n */\nfunction syncTabBar(card: HTMLElement, deps: EditorTabsDeps, available: ReadonlySet<string>): void {\n const bar = card.querySelector<HTMLElement>(`.${TABS_CLASS}`)\n if (bar === null) return\n setAttr(bar, 'aria-label', deps.t('editorTabsLabel'))\n setText(tabOf(bar, TAB_PROVIDER), deps.t('editorTabProvider'))\n setText(tabOf(bar, TAB_MODELS), deps.t('editorTabModels'))\n setText(tabOf(bar, TAB_ADVANCED), deps.t('editorTabAdvanced'))\n setHidden(tabOf(bar, TAB_MODELS), !available.has(TAB_MODELS))\n setHidden(tabOf(bar, TAB_ADVANCED), !available.has(TAB_ADVANCED))\n syncSelected(card, bar)\n}\n\n/** Mark the active button. The panes follow the card's attribute in CSS, so\n * this is the bar's own state (and assistive tech's), not the pane switch. */\nfunction syncSelected(card: HTMLElement, bar?: HTMLElement): void {\n const target = bar ?? card.querySelector<HTMLElement>(`.${TABS_CLASS}`)\n if (target === null) return\n const active = card.getAttribute(TAB_ATTR) ?? TAB_PROVIDER\n for (const tab of Array.from(target.querySelectorAll<HTMLElement>(`[${TAB_ID_ATTR}]`))) {\n setAttr(tab, 'aria-selected', tab.getAttribute(TAB_ID_ATTR) === active ? 'true' : 'false')\n }\n}\n\nfunction tabOf(bar: HTMLElement, id: string): HTMLElement | undefined {\n for (const tab of Array.from(bar.children)) {\n if (tab instanceof HTMLElement && tab.getAttribute(TAB_ID_ATTR) === id) return tab\n }\n return undefined\n}\n\n/** Drop the takeover from one card, back to the official one-column layout. */\nfunction cleanupCard(card: HTMLElement): void {\n // The bar is NOT removed here: it is the tab-bar component's own DOM, and\n // React unmounts it with the card. Leaving the takeover class off is all it\n // takes to hide it, because the stylesheet reveals it only under that class.\n card.classList.remove(TABBED_CLASS)\n card.removeAttribute(TAB_ATTR)\n card.removeAttribute(BODY_ATTR)\n for (const el of Array.from(card.querySelectorAll<HTMLElement>(`[${REGION_ATTR}]`))) {\n if (el.getAttribute(REGION_ATTR) === TAB_PROVIDER) {\n resetKeyField(el)\n resetKeyManagerField(el)\n }\n el.removeAttribute(REGION_ATTR)\n }\n restoreGroups(card)\n}\n\n/**\n * The official \"Customized\" group renders collapsed by default; with the tab\n * bar in charge there is no summary row left to expand it, so any group inside\n * a taken-over body is forced open. A memo attribute records the groups this\n * pass opened (vs. ones the page rendered open) so cleanup restores exactly\n * those.\n */\nfunction forceGroupsOpen(body: HTMLElement): void {\n for (const group of Array.from(body.querySelectorAll('details'))) {\n if (group.hasAttribute('open')) continue\n setAttr(group, DETAILS_MEMO_ATTR, 'closed')\n setAttr(group, 'open', '')\n }\n}\n\n/** Undo {@link forceGroupsOpen}: re-close the groups the pass opened. */\nfunction restoreGroups(card: HTMLElement): void {\n for (const group of Array.from(card.querySelectorAll(`details[${DETAILS_MEMO_ATTR}]`))) {\n group.removeAttribute('open')\n group.removeAttribute(DETAILS_MEMO_ATTR)\n }\n}\n\n", "/**\n * The drag engine behind both reorderable lists of the Models settings page:\n * the provider rows, and the model rows inside one provider's editor.\n *\n * Neither list is reordered in the DOM \u2014 React owns those children \u2014 so a row\n * is moved by writing a CSS `order` on it, which reorders the screen (both\n * lists are flex columns) without touching the child list React holds. The id\n * sequence that results is stored by whoever owns the list, through the\n * adapter's `commit`.\n *\n * What the pointer sees while dragging:\n * - a chip carrying the row's name, instead of the browser's screenshot of\n * whatever element was grabbed;\n * - an insertion line between two rows \u2014 drawn as a fixed overlay on the\n * body, never as a child of the list React owns;\n * - rows that slide (a FLIP pass) instead of jumping;\n * - the list scrolling on its own while the pointer rests near an edge.\n *\n * The two lists differ in exactly three ways, and all three live in the\n * adapter: where a row's grip is mounted, whether the whole seat drags or only\n * the grip (a model row is a grid of inputs \u2014 grabbing it must stay a text\n * selection), and how a row is named and the order stored.\n *\n * @module dsh-model-think-level/client/injection/row-drag\n */\n\nimport { moveToIndex } from '../provider-order.js'\n\n/** One reorderable list, as the engine needs to see it. */\nexport interface DragAdapter {\n /** How an event target resolves to one of this list's rows, or nothing. */\n rowOf: (node: Element, list: HTMLElement) => HTMLElement | undefined\n /** The row's stable id. A row that cannot be named is never moved or grabbed. */\n idOf: (row: HTMLElement) => string | undefined\n /** The text the carried chip shows for a row. */\n labelOf: (row: HTMLElement) => string | undefined\n /** The stored order applied to the ids now on screen. */\n order: (ids: readonly string[]) => readonly string[]\n /** Persist the order the rows screen in. */\n commit: (list: HTMLElement) => void\n /** The element a row's grip is mounted into. */\n seatOf: (row: HTMLElement) => HTMLElement | undefined\n /** Whether the whole seat drags (a provider header) or only the grip does. */\n readonly seatDrags: boolean\n /** The grip's accessible name. */\n gripLabel: (row: HTMLElement, id: string) => string\n /** The grip's tooltip. */\n gripHint: () => string\n /** Extra per-row DOM work, once the row is known to be nameable. */\n decorate?: (row: HTMLElement, seat: HTMLElement) => void\n}\n\n/** The row a drag is currently carrying, plus everything an abandoned drag needs. */\nexport interface DragSession {\n readonly row: HTMLElement\n readonly list: HTMLElement\n /** Every row in screen order when the drag began \u2014 an abandoned drag's undo. */\n readonly before: HTMLElement[]\n /** The nearest scroller, for the edge auto-scroll. */\n readonly scroller: HTMLElement | undefined\n /** The adapter that owned the list when the drag began. */\n readonly adapter: DragAdapter\n /** Pixels per frame the auto-scroll moves while the pointer rests near an edge. */\n scrollStep: number\n /** The scheduled auto-scroll frame, while one is pending. */\n frame: number | undefined\n dropped: boolean\n}\n\n/** Per-scan bookkeeping; one instance per module that owns a list. */\nexport interface DragState {\n /** The drag sources already carrying listeners. */\n readonly sources: WeakSet<Element>\n /** The lists already carrying the delegated dragover/drop listeners. */\n readonly lists: WeakSet<Element>\n /** The insertion line; created on the first drag and reused until teardown. */\n line: HTMLElement | undefined\n drag: DragSession | undefined\n}\n\n/** Marks the grip we mount, so a rescan recognizes its own work. */\nconst HANDLE_ATTR = 'data-bre-drag-handle'\n/** Marks a seat we made draggable, so teardown hands back an untouched one. */\nconst HEAD_ATTR = 'data-bre-row-head'\n/** Which side of the hovered row the line is on (before/after). */\nconst DROP_SIDE_ATTR = 'data-bre-drop-side'\nconst DRAGGING_CLASS = 'bre-row-dragging'\nconst DROP_CLASS = 'bre-row-drop-target'\nconst GHOST_CLASS = 'bre-drag-ghost'\nconst LINE_CLASS = 'bre-drop-line'\nconst SVG_NS = 'http://www.w3.org/2000/svg'\n/** The official lists' row gap, halved to aim the insertion line at its middle. */\nconst ROW_GAP = 8\n/** How close to a scroller's edge the pointer has to be to start scrolling. */\nconst EDGE_PX = 28\n/** Pixels per frame the edge auto-scroll moves. */\nconst SCROLL_STEP = 12\n/** The slide a row does when the pointer carries another one past it. */\nconst MOVE_TRANSITION = 'transform 180ms cubic-bezier(0.2, 0.7, 0.3, 1)'\n/**\n * Controls that own the gesture that starts on them \u2014 including the official\n * card's own buttons, and anything the plugin renders inside a row.\n */\nconst INTERACTIVE =\n 'button, a, input, textarea, select, [contenteditable], [role=\"button\"], ' +\n '[role=\"switch\"], [role=\"checkbox\"], [role=\"textbox\"], [role=\"combobox\"]'\n\nexport function createDragState(): DragState {\n return { sources: new WeakSet(), lists: new WeakSet(), line: undefined, drag: undefined }\n}\n\n/** A list's child elements, in DOM order. */\nexport function rowElements(parent: HTMLElement): HTMLElement[] {\n return Array.from(parent.children).filter((child): child is HTMLElement => child instanceof HTMLElement)\n}\n\n/** A row's own `order` when it has one, else the position it holds in the DOM. */\nfunction orderOf(row: HTMLElement, index: number): number {\n const raw = row.style.order\n if (raw === '') return index\n const parsed = Number.parseInt(raw, 10)\n return Number.isNaN(parsed) ? index : parsed\n}\n\n/** The rows in the sequence they currently screen. */\nexport function visualRows(list: HTMLElement): HTMLElement[] {\n return rowElements(list)\n .map((row, index) => ({ row, key: orderOf(row, index), index }))\n .sort((left, right) => left.key - right.key || left.index - right.index)\n .map(entry => entry.row)\n}\n\n/**\n * Put `sequence` on screen: each row it names takes the slot a named row\n * already held, and a row it does not name keeps the slot it has. Every child\n * gets an explicit `order` once anything moves, so no two rows share a slot.\n */\nexport function placeRows(list: HTMLElement, sequence: readonly HTMLElement[]): void {\n const current = visualRows(list)\n const named = current.filter(row => sequence.includes(row))\n const slots = named.map(row => current.indexOf(row))\n const ordered = sequence.filter(row => named.includes(row))\n const target = new Map<HTMLElement, number>()\n current.forEach((row, index) => target.set(row, index))\n slots.forEach((slot, position) => {\n const row = ordered[position]\n if (row !== undefined) target.set(row, slot)\n })\n for (const [row, slot] of target) {\n const value = String(slot)\n if (row.style.order !== value) row.style.order = value\n }\n}\n\n/**\n * Run `mutate`, then let every row that the mutation moved slide to where it\n * went. A row is pinned to its old offset with a transform, that position is\n * laid out (or the browser animates nothing), and the transform is released\n * under a transition \u2014 the standard FLIP pass, measured in layout pixels so a\n * container that cannot be measured (a hidden page) simply does not animate.\n */\nexport function slideIntoPlace(list: HTMLElement, mutate: () => void): void {\n const rows = rowElements(list)\n const before = rows.map(row => row.offsetTop)\n mutate()\n rows.forEach((row, index) => {\n const delta = (before[index] as number) - row.offsetTop\n if (delta === 0) return\n row.style.transition = 'none'\n row.style.transform = `translateY(${delta}px)`\n void row.offsetHeight\n row.style.transition = MOVE_TRANSITION\n row.style.transform = ''\n })\n}\n\n/** The ids of the rows that have one, in the order they screen. */\nexport function orderedIds(\n list: HTMLElement,\n idOf: (row: HTMLElement) => string | undefined,\n): string[] {\n return visualRows(list)\n .map(row => idOf(row))\n .filter((id): id is string => id !== undefined)\n}\n\n/**\n * Place the rows in `ids` order. Rows without a known id are left exactly where\n * they are: a row we cannot name is a row we must not move.\n */\nexport function applyIdOrder(\n list: HTMLElement,\n ids: readonly string[],\n idOf: (row: HTMLElement) => string | undefined,\n): void {\n const rank = new Map<string, number>()\n for (const [index, id] of ids.entries()) if (!rank.has(id)) rank.set(id, index)\n const named = visualRows(list).filter(row => idOf(row) !== undefined)\n const ordered = [...named].sort((left, right) => {\n const leftRank = rank.get(idOf(left) as string) ?? Number.MAX_SAFE_INTEGER\n const rightRank = rank.get(idOf(right) as string) ?? Number.MAX_SAFE_INTEGER\n return leftRank - rightRank\n })\n placeRows(list, ordered)\n}\n\n/** Hand every row back the inline geometry a drag wrote on it. */\nexport function clearRowOrder(rows: Iterable<HTMLElement>): void {\n for (const row of rows) {\n row.style.order = ''\n row.style.transition = ''\n row.style.transform = ''\n }\n}\n\n/**\n * Where the carried row has to land to sit before/after `hovered`.\n *\n * The index is read against the sequence WITHOUT the carried row, which is the\n * sequence `moveToIndex` splices into. Reading it against the sequence that\n * still holds the row is what makes a downward drag land one slot too far: the\n * row's own slot is still counted.\n */\nexport function dropIndex(\n rows: readonly HTMLElement[],\n carried: HTMLElement,\n hovered: HTMLElement,\n after: boolean,\n): number {\n const rest = rows.filter(row => row !== carried)\n const at = rest.indexOf(hovered)\n if (at < 0) return Math.max(0, rows.indexOf(carried))\n return after ? at + 1 : at\n}\n\n/** The row nearest a viewport Y \u2014 the fallback while the pointer is between rows. */\nexport function nearestRow(rows: readonly HTMLElement[], clientY: number): HTMLElement | undefined {\n let best: HTMLElement | undefined\n let bestDistance = Number.POSITIVE_INFINITY\n for (const row of rows) {\n const rect = row.getBoundingClientRect()\n const distance = clientY < rect.top ? rect.top - clientY : clientY > rect.bottom ? clientY - rect.bottom : 0\n if (distance < bestDistance) {\n bestDistance = distance\n best = row\n }\n }\n return best\n}\n\n/** Whether the pointer sits in the lower half of `row` (a zero-height row reads as \"after\"). */\nfunction isAfter(row: HTMLElement, clientY: number): boolean {\n const rect = row.getBoundingClientRect()\n if (rect.height <= 0) return true\n return clientY > rect.top + rect.height / 2\n}\n\n/** The viewport Y the insertion line belongs at for `row`. */\nexport function dropLineTop(row: HTMLElement, after: boolean): number {\n const rect = row.getBoundingClientRect()\n return after ? rect.bottom + ROW_GAP / 2 - 1 : rect.top - ROW_GAP / 2 - 1\n}\n\n/** Six dots, drawn rather than fetched, so the grip needs no asset URL. */\nfunction gripIcon(): SVGElement {\n const svg = document.createElementNS(SVG_NS, 'svg')\n svg.setAttribute('width', '10')\n svg.setAttribute('height', '14')\n svg.setAttribute('viewBox', '0 0 10 14')\n svg.setAttribute('fill', 'none')\n svg.setAttribute('aria-hidden', 'true')\n const dots: ReadonlyArray<readonly [number, number]> = [[2.5, 3], [7.5, 3], [2.5, 7], [7.5, 7], [2.5, 11], [7.5, 11]]\n for (const [cx, cy] of dots) {\n const dot = document.createElementNS(SVG_NS, 'circle')\n dot.setAttribute('cx', String(cx))\n dot.setAttribute('cy', String(cy))\n dot.setAttribute('r', '1.2')\n dot.setAttribute('fill', 'currentColor')\n svg.appendChild(dot)\n }\n return svg\n}\n\n/**\n * The chip the pointer carries. The browser's own drag image is a screenshot of\n * whatever element was grabbed, which for a grip is nothing and for a row card\n * is an unreadable slab \u2014 so we hand it a chip with the row's name.\n */\nexport function makeGhost(row: HTMLElement, label?: string): HTMLElement {\n const ghost = document.createElement('div')\n ghost.className = GHOST_CLASS\n ghost.setAttribute('aria-hidden', 'true')\n ghost.appendChild(gripIcon())\n const name = document.createElement('span')\n name.className = 'bre-drag-ghost-name'\n const printed = label ?? row.querySelector<HTMLElement>('[class*=\"rowName\"]')?.textContent?.trim()\n name.textContent = printed === undefined ? '' : printed\n ghost.appendChild(name)\n return ghost\n}\n\n/** Whether a drag that began on `target` belongs to a control rather than a row. */\nfunction interactiveTarget(target: EventTarget | null): boolean {\n if (!(target instanceof Element)) return false\n if (target.closest(`[${HANDLE_ATTR}]`) !== null) return false\n return target.closest(INTERACTIVE) !== null\n}\n\n/** The list a row belongs to \u2014 both lists hold their rows as direct children. */\nfunction listOf(row: HTMLElement): HTMLElement | undefined {\n return row.parentElement ?? undefined\n}\n\nfunction clearDropMarks(list: HTMLElement): void {\n for (const child of rowElements(list)) {\n child.classList.remove(DROP_CLASS)\n child.removeAttribute(DROP_SIDE_ATTR)\n }\n}\n\n/** The nearest ancestor that actually scrolls, for the edge auto-scroll. */\nexport function scrollHostOf(node: HTMLElement): HTMLElement | undefined {\n let current: HTMLElement | null = node.parentElement\n while (current !== null) {\n if (scrolls(current)) return current\n current = current.parentElement\n }\n const root = document.scrollingElement\n return root instanceof HTMLElement && root.scrollHeight > root.clientHeight ? root : undefined\n}\n\n/** Whether an element is a scroll container with something left to scroll. */\nfunction scrolls(node: HTMLElement): boolean {\n if (node.scrollHeight <= node.clientHeight) return false\n const declared = node.style.overflowY\n const overflow = declared.length > 0 ? declared : window.getComputedStyle(node).overflowY\n return overflow === 'auto' || overflow === 'scroll' || overflow === 'overlay'\n}\n\n/** The pixels per frame `host` should scroll while the pointer is at `clientY`. */\nexport function edgeScrollStep(host: HTMLElement, clientY: number): number {\n const rect = host.getBoundingClientRect()\n if (rect.height <= 0) return 0\n if (clientY < rect.top + EDGE_PX) return -SCROLL_STEP\n if (clientY > rect.bottom - EDGE_PX) return SCROLL_STEP\n return 0\n}\n\n/** Start, retarget or stop the auto-scroll pump of one drag. */\nfunction setScrollStep(state: DragState, session: DragSession, step: number): void {\n if (session.scrollStep === step) return\n session.scrollStep = step\n if (step === 0) {\n if (session.frame !== undefined) {\n window.cancelAnimationFrame(session.frame)\n session.frame = undefined\n }\n return\n }\n const host = session.scroller\n if (host === undefined || session.frame !== undefined) return\n const tick = (): void => {\n session.frame = undefined\n if (state.drag !== session || session.scrollStep === 0) return\n host.scrollTop += session.scrollStep\n session.frame = window.requestAnimationFrame(tick)\n }\n session.frame = window.requestAnimationFrame(tick)\n}\n\nfunction dropLine(state: DragState): HTMLElement {\n const existing = state.line\n if (existing !== undefined && existing.isConnected) return existing\n const line = document.createElement('div')\n line.className = LINE_CLASS\n line.setAttribute('aria-hidden', 'true')\n document.body.appendChild(line)\n state.line = line\n return line\n}\n\nfunction showDropLine(state: DragState, row: HTMLElement, after: boolean): void {\n const line = dropLine(state)\n const rect = row.getBoundingClientRect()\n line.style.top = `${dropLineTop(row, after)}px`\n line.style.left = `${rect.left}px`\n line.style.width = `${rect.width}px`\n line.style.display = 'block'\n line.setAttribute(DROP_SIDE_ATTR, after ? 'after' : 'before')\n}\n\nfunction hideDropLine(state: DragState): void {\n if (state.line === undefined) return\n state.line.style.display = 'none'\n}\n\n/** End whatever drag is in flight, and undo it unless it landed. */\nfunction finishDrag(state: DragState): void {\n const session = state.drag\n state.drag = undefined\n if (session === undefined) return\n setScrollStep(state, session, 0)\n hideDropLine(state)\n session.row.classList.remove(DRAGGING_CLASS)\n clearDropMarks(session.list)\n // A drag that never landed (Esc, or a drop outside the list) is abandoned:\n // the rows slide back, and nothing is stored.\n if (!session.dropped) slideIntoPlace(session.list, () => { placeRows(session.list, session.before) })\n}\n\nfunction startDrag(row: HTMLElement, event: DragEvent, adapter: DragAdapter, state: DragState): void {\n // A drag that began on a row's own controls is not a reorder: cancel ours and\n // leave the control to the browser.\n if (interactiveTarget(event.target)) {\n event.preventDefault()\n return\n }\n const list = listOf(row)\n const id = adapter.idOf(row)\n if (list === undefined || id === undefined) {\n event.preventDefault()\n return\n }\n const session: DragSession = {\n row,\n list,\n before: visualRows(list),\n scroller: scrollHostOf(list),\n adapter,\n scrollStep: 0,\n frame: undefined,\n dropped: false,\n }\n state.drag = session\n row.classList.add(DRAGGING_CLASS)\n const ghost = makeGhost(row, adapter.labelOf(row))\n if (Number.isFinite(event.clientX) && Number.isFinite(event.clientY)) {\n ghost.style.left = `${event.clientX}px`\n ghost.style.top = `${event.clientY}px`\n }\n document.body.appendChild(ghost)\n // A synthesized drag (a test, or a script) may carry no transfer at all;\n // there is then nothing to hand the chip to, but the session \u2014 the part that\n // actually moves rows \u2014 is already in place.\n const dataTransfer: DataTransfer | null | undefined = event.dataTransfer\n if (dataTransfer != null) {\n try {\n dataTransfer.effectAllowed = 'move'\n dataTransfer.setData('text/plain', id)\n dataTransfer.setDragImage(ghost, Math.round(ghost.offsetWidth / 2), Math.round(ghost.offsetHeight / 2))\n } catch {\n // Not every engine lets a synthesized drag choose its own image.\n }\n }\n // The snapshot is taken while the drag starts, so the chip only has to exist\n // for this task \u2014 leaving it in the DOM would outlive its usefulness.\n window.setTimeout(() => { ghost.remove() }, 0)\n}\n\n/** Move the carried row under the pointer, and say where it would land. */\nfunction dragOver(list: HTMLElement, event: DragEvent, state: DragState): void {\n const session = state.drag\n if (session === undefined || session.list !== list) return\n const target = event.target\n const over = target instanceof Element ? session.adapter.rowOf(target, list) : undefined\n const rows = visualRows(list)\n const hovered = over !== undefined && rows.includes(over)\n ? over\n : nearestRow(rows, event.clientY)\n if (hovered === undefined) return\n event.preventDefault()\n const dataTransfer: DataTransfer | null | undefined = event.dataTransfer\n if (dataTransfer != null) dataTransfer.dropEffect = 'move'\n const after = isAfter(hovered, event.clientY)\n const slot = dropIndex(rows, session.row, hovered, after)\n // A dragover repeats for as long as the pointer rests, and re-placing a row\n // that would not move is what makes the list flicker under the cursor.\n if (slot !== rows.indexOf(session.row)) {\n slideIntoPlace(list, () => { placeRows(list, moveToIndex(rows, session.row, slot)) })\n }\n clearDropMarks(list)\n hovered.classList.add(DROP_CLASS)\n hovered.setAttribute(DROP_SIDE_ATTR, after ? 'after' : 'before')\n showDropLine(state, hovered, after)\n setScrollStep(state, session, session.scroller === undefined ? 0 : edgeScrollStep(session.scroller, event.clientY))\n}\n\n/** The delegated half of the drag: one pair of listeners per list. */\nfunction wireList(list: HTMLElement, state: DragState): void {\n if (state.lists.has(list)) return\n state.lists.add(list)\n list.addEventListener('dragover', (event: DragEvent) => { dragOver(list, event, state) })\n list.addEventListener('drop', (event: DragEvent) => {\n const session = state.drag\n if (session === undefined || session.list !== list) return\n event.preventDefault()\n session.dropped = true\n // Land the order on the ids the rows actually carry: a row with no id has\n // no name to store, and is left out of the stored sequence.\n session.adapter.commit(list)\n })\n}\n\n/** Make one drag source start (and end) a drag, once. */\nfunction wireSource(source: HTMLElement, row: HTMLElement, adapter: DragAdapter, state: DragState): void {\n if (state.sources.has(source)) return\n state.sources.add(source)\n if (adapter.seatDrags) {\n source.setAttribute(HEAD_ATTR, '1')\n source.setAttribute('draggable', 'true')\n }\n source.addEventListener('dragstart', (event: DragEvent) => { startDrag(row, event, adapter, state) })\n source.addEventListener('dragend', () => { finishDrag(state) })\n}\n\n/** The grip's own two jobs: it is not a row action, and it can reorder by keyboard. */\nfunction wireGrip(grip: HTMLElement, row: HTMLElement, adapter: DragAdapter, state: DragState): void {\n grip.addEventListener('click', (event: MouseEvent) => {\n event.preventDefault()\n event.stopPropagation()\n })\n grip.addEventListener('keydown', (event: KeyboardEvent) => {\n if (event.key !== 'ArrowUp' && event.key !== 'ArrowDown') return\n const list = listOf(row)\n if (list === undefined) return\n const current = visualRows(list)\n const index = current.indexOf(row)\n if (index < 0) return\n const target = index + (event.key === 'ArrowUp' ? -1 : 1)\n if (target < 0 || target >= current.length) return\n event.preventDefault()\n slideIntoPlace(list, () => { placeRows(list, moveToIndex(current, row, target)) })\n adapter.commit(list)\n // A keyboard move that lost focus would make the next press a no-op the\n // user cannot explain.\n grip.focus()\n })\n void state\n}\n\n/** Keep one grip's copy in step with the row it belongs to. */\nfunction syncGrip(grip: HTMLElement, row: HTMLElement, id: string, adapter: DragAdapter): void {\n const label = adapter.gripLabel(row, id)\n const hint = adapter.gripHint()\n if (grip.getAttribute(HANDLE_ATTR) !== id) grip.setAttribute(HANDLE_ATTR, id)\n if (grip.getAttribute('aria-label') !== label) grip.setAttribute('aria-label', label)\n if (grip.getAttribute('title') !== hint) grip.setAttribute('title', hint)\n}\n\n/** Mount (or refresh) one row's grip, and wire whatever drags for that row. */\nfunction ensureGrip(\n row: HTMLElement,\n seat: HTMLElement,\n id: string,\n adapter: DragAdapter,\n state: DragState,\n): void {\n const existing = seat.querySelector<HTMLElement>(`[${HANDLE_ATTR}]`)\n if (existing !== null) {\n // Already mounted (a rescan, or a language switch): refresh the copy.\n syncGrip(existing, row, id, adapter)\n return\n }\n adapter.decorate?.(row, seat)\n const grip = document.createElement('span')\n grip.className = 'bre-drag-grip'\n grip.setAttribute('role', 'button')\n grip.setAttribute('tabindex', '0')\n grip.setAttribute('draggable', 'true')\n grip.appendChild(gripIcon())\n syncGrip(grip, row, id, adapter)\n wireGrip(grip, row, adapter, state)\n wireSource(adapter.seatDrags ? seat : grip, row, adapter, state)\n seat.insertBefore(grip, seat.firstChild)\n}\n\n/**\n * One pass over one list: mount a grip in every nameable row, wire the list,\n * and place the rows in the order the adapter's preference asks for.\n *\n * Idempotent by DOM presence \u2014 the same guard the editor injector uses, which\n * keeps the insertion from re-triggering the scan that found it.\n */\nexport function reconcileRows(\n root: HTMLElement,\n list: HTMLElement,\n adapter: DragAdapter,\n state: DragState,\n): void {\n if (!root.isConnected) return\n wireList(list, state)\n for (const row of rowElements(list)) {\n const id = adapter.idOf(row)\n if (id === undefined) continue\n const seat = adapter.seatOf(row)\n if (seat === undefined) continue\n ensureGrip(row, seat, id, adapter, state)\n }\n // Mid-drag the screen order is the user's, not the preference's: writing it\n // now would snap the row back under the pointer.\n if (state.drag !== undefined) return\n applyIdOrder(list, adapter.order(orderedIds(list, adapter.idOf)), adapter.idOf)\n}\n\n/**\n * Undo everything the engine put on the page: the grips, the draggable seats,\n * the insertion line, the chip, and the classes a drag left behind. The inline\n * `order` of a row is the list owner's to clear (it knows which rows are its\n * own), because the engine's grip removal is global by attribute.\n *\n * A plugin disable or an HMR replace unmounts React trees on its own, but the\n * grips and the draggable seats are raw DOM with listeners attached to\n * themselves \u2014 left behind they would keep reordering rows (and persisting that\n * order) on behalf of a fiber that no longer exists.\n */\nexport function teardownDrags(state: DragState): void {\n state.drag = undefined\n for (const grip of Array.from(document.querySelectorAll<HTMLElement>(`[${HANDLE_ATTR}]`))) grip.remove()\n for (const head of Array.from(document.querySelectorAll<HTMLElement>(`[${HEAD_ATTR}]`))) {\n head.removeAttribute(HEAD_ATTR)\n head.removeAttribute('draggable')\n }\n for (const row of Array.from(document.querySelectorAll<HTMLElement>(`.${DRAGGING_CLASS}, .${DROP_CLASS}`))) {\n row.classList.remove(DRAGGING_CLASS, DROP_CLASS)\n }\n for (const line of Array.from(document.querySelectorAll<HTMLElement>(`.${LINE_CLASS}`))) line.remove()\n for (const ghost of Array.from(document.querySelectorAll<HTMLElement>(`.${GHOST_CLASS}`))) ghost.remove()\n state.line = undefined\n}\n", "/**\n * Provider-row drag reordering for the official Models settings page.\n *\n * The official section renders one `<li>` per provider inside a `<ul>` that is\n * already a flex column, and React owns those children \u2014 so rows are never\n * moved in the DOM. Each row is given a CSS `order` instead, which reorders the\n * screen without touching React's child list, and the resulting id sequence is\n * stored by `../provider-order.js` for the composer model switcher to read.\n *\n * The interaction itself \u2014 the grip, the drag session, the insertion line, the\n * FLIP slide, the edge auto-scroll \u2014 lives in `./row-drag.js`, which the model\n * rows inside a provider use too. This module is the provider list's adapter\n * onto it: it names each row (the plugin's `data-bre-provider` stamp, or the\n * official Edit button's label parsed back through `editProvider`), mounts the\n * grip in the row's own header (`[class*=\"rowHead\"]`), and lets the WHOLE header\n * drag \u2014 a 18px grip is a poor thing to have to hit. The card's own buttons keep\n * their behaviour, because the engine cancels a drag that begins on one, and\n * everything the page renders below the header is untouched.\n *\n * @module dsh-model-think-level/client/injection/provider-order-drag\n */\n\nimport type { HostLabels } from './models-page-editor.js'\nimport { resolveProviderOrder, setProviderOrder } from '../provider-order.js'\nimport {\n applyIdOrder,\n clearRowOrder,\n createDragState,\n orderedIds,\n reconcileRows,\n rowElements,\n teardownDrags,\n} from './row-drag.js'\nimport type { DragAdapter, DragState } from './row-drag.js'\n\n// The engine's helpers are part of this module's surface: the provider tests\n// exercise them here, and callers that only know this list should not have to\n// reach into the shared engine to reach them.\nexport {\n dropIndex,\n dropLineTop,\n edgeScrollStep,\n makeGhost,\n nearestRow,\n placeRows,\n rowElements,\n scrollHostOf,\n visualRows,\n} from './row-drag.js'\n\n/** Per-scan bookkeeping (one instance lives in `models-page.ts`). */\nexport type ProviderOrderState = DragState\n\nexport interface ProviderOrderDeps {\n /** The plugin's own translator, for the grip's label and tooltip. */\n t: (key: string, params?: Record<string, string | number>) => string\n /** The official `settings.models` label sets, resolved fresh on every scan. */\n labels: () => HostLabels\n}\n\n/** Marks the row with the provider id it belongs to (ours to add and to read). */\nconst ROW_ID_ATTR = 'data-bre-row-provider'\n/** The `{provider}` slot of the official `providerCopy` templates. */\nconst PROVIDER_TOKEN = '{provider}'\n/** Class-name fragments of the official provider rows, probed by substring. */\nconst ROW_CLASS_PROBES = ['rowCard', 'setupCard'] as const\n\nexport function createProviderOrderState(): ProviderOrderState {\n return createDragState()\n}\n\n/**\n * The official provider-row list: the `<ul>` that holds the row cards. Probed by\n * class-name fragment because the page's class names are CSS-module hashes\n * (`_3nPmjq_rows` today, something else tomorrow).\n */\nexport function rowsListOf(root: HTMLElement): HTMLElement | undefined {\n for (const probe of ROW_CLASS_PROBES) {\n for (const hit of Array.from(root.querySelectorAll<HTMLElement>(`[class*=\"${probe}\"]`))) {\n const parent = hit.parentElement\n if (parent === null || parent.tagName !== 'UL') continue\n if (rowElements(parent).length === 0) continue\n return parent\n }\n }\n return undefined\n}\n\n/**\n * The row's own header \u2014 the seat the grip is mounted into, and the drag source.\n * Exported because the enable switch mounts into the same seat and must agree\n * with this module about where that seat is.\n */\nexport function rowHeadOf(row: HTMLElement): HTMLElement | undefined {\n return row.querySelector<HTMLElement>('[class*=\"rowHead\"]') ?? undefined\n}\n\n/**\n * The row's displayed provider name, for the grip's label, the drag chip and\n * the switch's label.\n */\nexport function rowNameOf(row: HTMLElement): string | undefined {\n const name = row.querySelector<HTMLElement>('[class*=\"rowName\"]')?.textContent?.trim()\n return name !== undefined && name.length > 0 ? name : undefined\n}\n\n/** The `{provider}` slot split out of one official template. */\nfunction templateParts(template: string): { prefix: string; suffix: string } | undefined {\n const at = template.indexOf(PROVIDER_TOKEN)\n if (at < 0) return undefined\n return { prefix: template.slice(0, at), suffix: template.slice(at + PROVIDER_TOKEN.length) }\n}\n\n/**\n * Recover the provider id from an official `providerTargetLabel`: `Name (route)`\n * \u2014 the form used whenever the display name differs from the route \u2014 or the\n * bare route when the two are equal. The id is the LAST parenthesized group, so\n * a display name that itself carries parentheses still splits correctly.\n */\nexport function idFromTargetLabel(label: string): string | undefined {\n const trimmed = label.trim()\n if (trimmed.length === 0) return undefined\n const parenthesized = /^(.*) \\(([^()]*)\\)$/.exec(trimmed)\n if (parenthesized !== null) {\n const route = parenthesized[2] as string\n return route.length === 0 ? undefined : route\n }\n // A route whose display name IS the route is the whole label \u2014 and a route\n // never carries whitespace.\n return /\\s/.test(trimmed) ? undefined : trimmed\n}\n\n/** The official card roots: a provider row, a row awaiting its first key, and the add card. */\nconst CARD_ROOT_PROBE = '[class*=\"rowCard\"], [class*=\"setupCard\"], [class*=\"addCard\"]'\n\n/**\n * Whether a control belongs to this plugin (or any other `bre-`-prefixed seat)\n * rather than to the official page: any mark of ours between the control and its\n * own card means the control is ours.\n *\n * The walk deliberately STOPS at the card. The tab takeover paints `bre-tabbed`\n * on the OFFICIAL row while that card's editor is open (`./editor-tabs.ts`), so\n * an ancestor-wide `[class*=\"bre-\"]` test reads the row's own Edit button as\n * ours; `providerIdOfRow` then finds no id, and every pass keyed on the row id\n * skips exactly the row the user has open. The built-in rows carry no\n * `data-bre-provider` stamp to fall back on, so the seat, the switch and the\n * drag handle all disappear with the id.\n */\nfunction isForeign(node: Element): boolean {\n const card = node.closest(CARD_ROOT_PROBE)\n for (let el: Element | null = node; el !== null && el !== card; el = el.parentElement) {\n if (el.matches('[data-plugin], [class*=\"bre-\"]')) return true\n }\n return false\n}\n\n/**\n * The provider id of one row: the stamp our own slot carries when the row has\n * one (every llm-pi-ai row does), else the official Edit button's aria-label\n * parsed back through the localized `editProvider` template.\n */\nexport function providerIdOfRow(row: HTMLElement, labels: HostLabels): string | undefined {\n const stamped = row.querySelector<HTMLElement>('[data-bre-provider]')?.getAttribute('data-bre-provider')\n if (typeof stamped === 'string' && stamped.length > 0) return stamped\n for (const template of labels.editProvider) {\n const parts = templateParts(template)\n if (parts === undefined) continue\n for (const button of Array.from(row.querySelectorAll<HTMLElement>('button[aria-label]'))) {\n if (isForeign(button)) continue\n const label = button.getAttribute('aria-label') ?? ''\n if (label.length <= parts.prefix.length + parts.suffix.length) continue\n if (!label.startsWith(parts.prefix) || !label.endsWith(parts.suffix)) continue\n const id = idFromTargetLabel(label.slice(parts.prefix.length, label.length - parts.suffix.length))\n if (id !== undefined) return id\n }\n }\n return undefined\n}\n\n/** The id this module stamped on a row, if any. */\nfunction rowIdOf(row: HTMLElement): string | undefined {\n const value = row.getAttribute(ROW_ID_ATTR)\n return value === null || value.length === 0 ? undefined : value\n}\n\n/** The stamped ids of the rows that have one, in the order they screen. */\nexport function orderedRowIds(list: HTMLElement): string[] {\n return orderedIds(list, rowIdOf)\n}\n\n/** Place the rows in the stored order; rows we could not name are left alone. */\nexport function applyRowOrder(list: HTMLElement, ids: readonly string[]): void {\n applyIdOrder(list, ids, rowIdOf)\n}\n\n/**\n * One pass over the document: name every provider row, mount a grip in each\n * row's header, make that header a drag source, and place the rows in the order\n * the preference asks for.\n *\n * Runs on every scan (the official editor's own pass bails out when no card is\n * open, so this cannot ride along with it) and is idempotent by DOM presence \u2014\n * the same guard the editor injector uses, which keeps the insertion from\n * re-triggering the scan that found it.\n */\nexport function reconcileProviderOrder(\n root: HTMLElement,\n deps: ProviderOrderDeps,\n state: ProviderOrderState,\n): void {\n if (!root.isConnected) return\n const list = rowsListOf(root)\n if (list === undefined) return\n const labels = deps.labels()\n // Name the rows before the engine looks at them: a row without a stamp has no\n // id, and a row the engine cannot name gets no grip and is never moved.\n for (const row of rowElements(list)) {\n const id = providerIdOfRow(row, labels)\n if (id === undefined) {\n row.removeAttribute(ROW_ID_ATTR)\n continue\n }\n if (rowIdOf(row) !== id) row.setAttribute(ROW_ID_ATTR, id)\n }\n reconcileRows(root, list, providerAdapter(deps), state)\n}\n\n/** The provider list as the engine needs to see it. */\nfunction providerAdapter(deps: ProviderOrderDeps): DragAdapter {\n return {\n // A provider row is the `<li>` a descendant belongs to \u2014 the engine checks\n // that it really is one of the list's own rows.\n rowOf: (node) => {\n const row = node.closest('li')\n return row instanceof HTMLElement ? row : undefined\n },\n idOf: rowIdOf,\n labelOf: rowNameOf,\n order: (ids) => resolveProviderOrder(ids),\n commit: (list) => { setProviderOrder(orderedRowIds(list)) },\n seatOf: rowHeadOf,\n // The header drags, not just the grip: the header is the row's own seat, and\n // a provider card is a large target that no text selection competes for.\n seatDrags: true,\n gripLabel: (row, id) => deps.t('providerReorderAria', { provider: rowNameOf(row) ?? id }),\n gripHint: () => deps.t('providerReorderHint'),\n }\n}\n\n/**\n * Undo everything this module put on the official page. A plugin disable or an\n * HMR replace unmounts React trees on its own, but the grips, the draggable\n * headers and the insertion line are raw DOM with listeners attached to\n * themselves \u2014 left behind they would keep reordering the rows (and persisting\n * that order) on behalf of a fiber that no longer exists.\n */\nexport function teardownProviderOrder(state: ProviderOrderState): void {\n teardownDrags(state)\n const stamped = Array.from(document.querySelectorAll<HTMLElement>(`[${ROW_ID_ATTR}]`))\n clearRowOrder(stamped)\n for (const row of stamped) row.removeAttribute(ROW_ID_ATTR)\n}\n", "/**\n * Per-provider enable switch for the official Models settings page.\n *\n * A provider that is switched off stays listed \u2014 that is the only place it can\n * be switched back on \u2014 but leaves the composer's provider column, which reads\n * the same store (`../provider-enabled.js`). The stored value is the set of\n * DISABLED ids, so an unknown or newly added route is enabled by default.\n *\n * The switch is a `role=\"switch\"` button appended to the row's action group (or\n * to the header when the official row has no action group), which leaves the\n * header's own flex layout \u2014 identity on one side, actions on the other \u2014 with\n * the same children it had. `role=\"switch\"` is already in the drag module's list\n * of controls that own their gesture, so a drag started on the switch is refused\n * and the click can only mean \"toggle\".\n *\n * Nothing here waits for the next scan to look right: the debounced scan is\n * 120ms away, and a switch whose knob only moves a beat later reads as a missed\n * click \u2014 so the click paints the new state itself.\n *\n * @module dsh-model-think-level/client/injection/provider-toggle\n */\n\nimport { isProviderEnabled, setProviderEnabled } from '../provider-enabled.js'\nimport type { HostLabels } from './models-page-editor.js'\nimport { providerIdOfRow, rowHeadOf, rowNameOf, rowsListOf } from './provider-order-drag.js'\n\n/** Marks the switch, and carries the provider id it governs. */\nconst SWITCH_ATTR = 'data-bre-provider-switch'\n\n/** Set on a row whose provider is switched off. */\nconst OFF_CLASS = 'bre-row-disabled'\n\n/** Per-scan bookkeeping (one instance lives in `models-page.ts`). */\nexport interface ProviderToggleState {\n /** Switches that already carry their click listener. */\n readonly switches: WeakSet<Element>\n}\n\nexport interface ProviderToggleDeps {\n /** The plugin's own translator, for the switch's label and tooltip. */\n t: (key: string, params?: Record<string, string | number>) => string\n /** The official `settings.models` label sets, resolved fresh on every scan. */\n labels: () => HostLabels\n}\n\nexport function createProviderToggleState(): ProviderToggleState {\n return { switches: new WeakSet() }\n}\n\n/**\n * Visit every provider row and make sure it shows a switch carrying its own\n * state. Idempotent: a row that already has one is only relabelled, which is\n * also how a language switch and an external toggle reach the page.\n */\nexport function reconcileProviderEnabled(\n root: HTMLElement,\n deps: ProviderToggleDeps,\n state: ProviderToggleState,\n): void {\n if (!root.isConnected) return\n const list = rowsListOf(root)\n if (list === undefined) return\n const labels = deps.labels()\n for (const child of Array.from(list.children)) {\n if (!(child instanceof HTMLElement)) continue\n // A row we cannot name (an unexpected card shape) is left alone: switching\n // off an unknown id would hide a provider the user never chose.\n const id = providerIdOfRow(child, labels)\n if (id === undefined) continue\n const head = rowHeadOf(child)\n if (head === undefined) continue\n ensureSwitch(child, head, id, deps, state)\n child.classList.toggle(OFF_CLASS, !isProviderEnabled(id))\n }\n}\n\n/** Remove every switch and every dimmed row. */\nexport function teardownProviderEnabled(): void {\n for (const switchButton of Array.from(document.querySelectorAll<HTMLElement>(`[${SWITCH_ATTR}]`))) {\n switchButton.remove()\n }\n for (const row of Array.from(document.querySelectorAll<HTMLElement>(`.${OFF_CLASS}`))) {\n row.classList.remove(OFF_CLASS)\n }\n}\n\nfunction ensureSwitch(\n row: HTMLElement,\n head: HTMLElement,\n id: string,\n deps: ProviderToggleDeps,\n state: ProviderToggleState,\n): void {\n const existing = head.querySelector<HTMLElement>(`[${SWITCH_ATTR}]`)\n if (existing !== null) {\n syncSwitch(existing, row, id, deps)\n return\n }\n const control = document.createElement('button')\n control.className = 'bre-provider-switch'\n control.setAttribute('type', 'button')\n control.setAttribute('role', 'switch')\n control.setAttribute(SWITCH_ATTR, id)\n syncSwitch(control, row, id, deps)\n wireSwitch(control, row, state)\n const actions = head.querySelector<HTMLElement>('[class*=\"rowActions\"]')\n // The action group is `margin-left: auto` and holds the card's own buttons:\n // the switch goes in FRONT of them, so the destructive control stays last and\n // the header keeps the children it had. Without a group the header itself\n // takes it, which is the same seat the grip uses.\n if (actions === null) head.appendChild(control)\n else actions.insertBefore(control, actions.firstChild)\n}\n\n/** Make the button agree with the store: its name, its tooltip, its state. */\nfunction syncSwitch(\n control: HTMLElement,\n row: HTMLElement,\n id: string,\n deps: ProviderToggleDeps,\n): void {\n const label = deps.t('providerToggleAria', { provider: rowNameOf(row) ?? id })\n const hint = deps.t('providerToggleHint')\n const checked = isProviderEnabled(id) ? 'true' : 'false'\n if (control.getAttribute(SWITCH_ATTR) !== id) control.setAttribute(SWITCH_ATTR, id)\n if (control.getAttribute('aria-checked') !== checked) control.setAttribute('aria-checked', checked)\n if (control.getAttribute('aria-label') !== label) control.setAttribute('aria-label', label)\n if (control.getAttribute('title') !== hint) control.setAttribute('title', hint)\n}\n\nfunction wireSwitch(control: HTMLElement, row: HTMLElement, state: ProviderToggleState): void {\n if (state.switches.has(control)) return\n state.switches.add(control)\n control.addEventListener('click', (event: MouseEvent) => {\n // The row is itself a button on the official page (it opens the editor) and\n // the header drags: neither may read this click as its own.\n event.preventDefault()\n event.stopPropagation()\n const id = control.getAttribute(SWITCH_ATTR)\n if (id === null) return\n const next = !isProviderEnabled(id)\n setProviderEnabled(id, next)\n // Paint it now \u2014 see the module note: the scan that would have done this is\n // debounced, and the switch must feel like a switch.\n control.setAttribute('aria-checked', next ? 'true' : 'false')\n row.classList.toggle(OFF_CLASS, !next)\n })\n}\n", "/**\n * The reserved delete seat on a provider row of the official Models page.\n *\n * The official row renders its destructive button only for a provider the host\n * still considers removable (`row.removable`); a built-in provider such as\n * deepseek renders nothing there at all. The action group is `margin-left: auto`,\n * so the rows that keep the button are wider than the rows that do not and the\n * whole column of buttons lines up raggedly against the card's right edge.\n *\n * The seat is therefore kept warm: a row the host left without a delete button\n * gets a disabled one of the same metrics, which is enough for every row's\n * actions to start at the same place. It is `disabled`, so it takes no click,\n * no focus and no keyboard activation \u2014 the button is the alignment, not an\n * action \u2014 and it still owns its gesture as far as the drag engine is concerned,\n * because that engine refuses a drag beginning on any `button`.\n *\n * Its word is the host's own: the label is read off a delete button the page\n * still renders, so the odd row out is not the one row written in another\n * language. A page that renders none at all (every provider built in) falls back\n * to this plugin's dictionary.\n *\n * @module dsh-model-think-level/client/injection/provider-delete-seat\n */\n\nimport type { HostLabels } from './models-page-editor.js'\nimport { providerIdOfRow, rowHeadOf, rowNameOf, rowsListOf } from './provider-order-drag.js'\n\n/** Marks the seat, and carries the provider id it stands for. */\nconst SEAT_ATTR = 'data-bre-provider-delete'\n\n/** The row's action group, probed by class-name fragment (a CSS-module hash). */\nconst ACTIONS_PROBE = '[class*=\"rowActions\"]'\n\n/** The host's own destructive control, probed the same way. */\nconst OFFICIAL_DELETE_PROBE = '[class*=\"dangerButton\"]'\n\n/** The seat's class: the metrics it borrows from the official row button. */\nconst SEAT_CLASS = 'bre-provider-delete'\n\nexport interface ProviderDeleteDeps {\n /** The plugin's own translator, for the tooltip and the fallback label. */\n t: (key: string, params?: Record<string, string | number>) => string\n /** The official `settings.models` label sets, resolved fresh on every scan. */\n labels: () => HostLabels\n}\n\n/**\n * Give every provider row the delete seat the host left out. Idempotent: a row\n * that already has one is only relabelled, and a row whose provider became\n * removable hands the seat back to the host's own button.\n */\nexport function reconcileProviderDeleteSeat(root: HTMLElement, deps: ProviderDeleteDeps): void {\n if (!root.isConnected) return\n const list = rowsListOf(root)\n if (list === undefined) return\n const labels = deps.labels()\n const label = officialDeleteLabel(list) ?? deps.t('providerRemoveDisabled')\n for (const child of Array.from(list.children)) {\n if (!(child instanceof HTMLElement)) continue\n // Same rule as the switch pass: a row we cannot name is a row we leave\n // exactly as the host rendered it.\n const id = providerIdOfRow(child, labels)\n if (id === undefined) continue\n const head = rowHeadOf(child)\n if (head === undefined) continue\n const actions = head.querySelector<HTMLElement>(ACTIONS_PROBE)\n // No action group means nothing to line up with: the row already renders its\n // controls somewhere this module does not claim.\n if (actions === null) continue\n const existing = actions.querySelector<HTMLElement>(`[${SEAT_ATTR}]`)\n // A removable provider carries the host's own button: the seat stands down\n // rather than doubling it, so a settings reload that flips `removable` \u2014 or\n // an added provider \u2014 is followed on the next scan.\n if (actions.querySelector(OFFICIAL_DELETE_PROBE) !== null) {\n if (existing !== null) existing.remove()\n continue\n }\n const seat = existing ?? createSeat()\n // The destructive control is the row's LAST action. A seat that is not there\n // yet \u2014 or one the host re-rendered its own children in front of \u2014 is put\n // back at the end.\n if (actions.lastElementChild !== seat) actions.appendChild(seat)\n syncSeat(seat, id, label, deps.t('providerRemoveLocked', { provider: rowNameOf(child) ?? id }))\n }\n}\n\n/** Take every seat out again (plugin dispose, HMR teardown). */\nexport function teardownProviderDeleteSeats(): void {\n for (const seat of Array.from(document.querySelectorAll<HTMLElement>(`[${SEAT_ATTR}]`))) {\n seat.remove()\n }\n}\n\n/**\n * The word the host uses for this action, off any row that still renders one.\n * The page's own copy beats a translation of ours: it is the same word in the\n * same language, at the width the neighbouring buttons are drawn at.\n */\nfunction officialDeleteLabel(list: HTMLElement): string | undefined {\n const button = list.querySelector<HTMLElement>(`${ACTIONS_PROBE} ${OFFICIAL_DELETE_PROBE}`)\n const text = button?.textContent?.trim() ?? ''\n return text.length > 0 ? text : undefined\n}\n\nfunction createSeat(): HTMLElement {\n const seat = document.createElement('button')\n seat.className = SEAT_CLASS\n seat.setAttribute('type', 'button')\n // `disabled` is the STATE, not the styling: the seat must not take a click, a\n // focus or a keyboard activation. Its dimmed look is the host's own disabled\n // treatment (see STYLES).\n seat.setAttribute('disabled', '')\n return seat\n}\n\n/** Make the seat stand for this row, compare-before-write on every field. */\nfunction syncSeat(seat: HTMLElement, id: string, label: string, hint: string): void {\n if (seat.getAttribute(SEAT_ATTR) !== id) seat.setAttribute(SEAT_ATTR, id)\n // Guarded: the page's observer watches childList, and replacing the seat's\n // text node on every scan would make each pass feed the next one.\n if (seat.textContent !== label) seat.textContent = label\n if (seat.getAttribute('aria-label') !== hint) seat.setAttribute('aria-label', hint)\n if (seat.getAttribute('title') !== hint) seat.setAttribute('title', hint)\n}\n", "/**\n * Model-row drag reordering inside one provider's editor on the official Models\n * settings page.\n *\n * The official editor renders a provider's models as\n * `div.modelList > div.modelEntry > div.modelRow` (a four-column grid: id,\n * display name, and two icon buttons), straight from\n * `models.map((model, index) => \u2026)`. React owns those children, so \u2014 exactly as\n * for the provider rows \u2014 a model is moved with a CSS `order`, and the id\n * sequence that results is stored per provider by `../model-order.js`.\n *\n * Two differences from the provider list, and both are in the adapter:\n * - the row is a grid of INPUTS, so only the grip drags. A drag that began on\n * the row body has to stay a text selection, which is why `seatDrags` is\n * false here and true for a provider header;\n * - the model list lives inside the provider's own `li.rowCard`, so the\n * provider id \u2014 and with it which stored sequence to read and to write \u2014 is\n * recovered from the enclosing row on every call rather than captured, and\n * a list whose provider cannot be named is left completely alone.\n *\n * A model's id is the value of its own \"Model ID\" input, not its position: the\n * official `aria-label`s carry `position` (`Model ID 1`), and a reorder that\n * renumbered them would rename every field on the page. A row whose id input is\n * still blank (a model just added, not yet named) gets no grip and is never\n * moved.\n *\n * @module dsh-model-think-level/client/injection/model-order-drag\n */\n\nimport { orderedIds, reconcileRows, rowElements, teardownDrags, createDragState, clearRowOrder } from './row-drag.js'\nimport type { DragAdapter, DragState } from './row-drag.js'\nimport { setModelOrder, resolveModelOrder, modelOrderOf } from '../model-order.js'\nimport { inputValueByLabel, type HostLabels } from './models-page-editor.js'\nimport { providerIdOfRow } from './provider-order-drag.js'\n\n/** Marks the entry with the model id it holds (ours to add and to read). */\nconst ROW_ID_ATTR = 'data-bre-row-model'\n/**\n * Marks the grid we widened to make room for the grip.\n *\n * Deliberately NOT `bre-model-row`: that name is already the plugin's own\n * composer row (`.bre-model-row` in `../styles.ts`), and a class borrowed onto\n * the host's grid would drag that row's `padding`, `gap`, `min-height` and\n * hover fill along with it.\n */\nconst MODEL_ROW_CLASS = 'bre-model-grid'\n\n/** Per-scan bookkeeping (one instance lives in `models-page.ts`). */\nexport type ModelOrderState = DragState\n\nexport interface ModelOrderDeps {\n /** The plugin's own translator, for the grip's label and tooltip. */\n t: (key: string, params?: Record<string, string | number>) => string\n /** The official `settings.models` label sets, resolved fresh on every scan. */\n labels: () => HostLabels\n}\n\nexport function createModelOrderState(): ModelOrderState {\n return createDragState()\n}\n\n/**\n * Every model list under `root`.\n *\n * Probed by class-name fragment, like the official anchors elsewhere: the\n * bundle's names are CSS-module hashes. The fragment alone is too loose \u2014\n * `modelListHead` and any future sibling share it \u2014 so a hit only counts when\n * its DIRECT children are the model entries, which is what the official\n * `.modelList { display: flex; flex-direction: column; gap: 8px }` lays out.\n */\nexport function modelListsOf(root: HTMLElement): HTMLElement[] {\n const lists: HTMLElement[] = []\n for (const hit of Array.from(root.querySelectorAll<HTMLElement>('[class*=\"modelList\"]'))) {\n const entries = rowElements(hit).filter(child => child.matches('[class*=\"modelEntry\"]'))\n if (entries.length === 0) continue\n lists.push(hit)\n }\n return lists\n}\n\n/** The model id this module stamped on an entry, if any. */\nfunction modelIdOf(entry: HTMLElement): string | undefined {\n const value = entry.getAttribute(ROW_ID_ATTR)\n return value === null || value.length === 0 ? undefined : value\n}\n\n/** The provider whose editor holds `list` \u2014 the key its models' order is stored under. */\nfunction providerOfList(list: HTMLElement, deps: ModelOrderDeps): string | undefined {\n const row = list.closest('li')\n if (row === null) return undefined\n return providerIdOfRow(row, deps.labels())\n}\n\n/** What the user sees for one model: its display name, else its id. */\nfunction modelLabelOf(entry: HTMLElement, deps: ModelOrderDeps): string | undefined {\n const name = inputValueByLabel(entry, deps.labels().modelName)\n if (name.length > 0) return name\n return modelIdOf(entry)\n}\n\n/** One model list, as the engine needs to see it. */\nfunction modelAdapter(list: HTMLElement, deps: ModelOrderDeps): DragAdapter {\n return {\n // A model row is the `modelEntry` a descendant belongs to; the engine checks\n // that it really is one of this list's own rows.\n rowOf: (node) => {\n const entry = node.closest('[class*=\"modelEntry\"]')\n return entry instanceof HTMLElement ? entry : undefined\n },\n idOf: modelIdOf,\n labelOf: (entry) => modelLabelOf(entry, deps),\n // Read and write the provider at CALL time: the row this list sits in is the\n // only authority on which sequence applies, and a rescan can move the list\n // to another provider's card.\n order: (ids) => {\n const provider = providerOfList(list, deps)\n return provider === undefined ? ids : resolveModelOrder(provider, ids)\n },\n commit: (target) => {\n const provider = providerOfList(target, deps)\n if (provider === undefined) return\n setModelOrder(provider, orderedIds(target, modelIdOf))\n },\n seatOf: (entry) => entry.querySelector<HTMLElement>('[class*=\"modelRow\"]') ?? undefined,\n // Only the grip drags: the row is a grid of inputs.\n seatDrags: false,\n gripLabel: (entry, id) => deps.t('modelReorderAria', { model: modelLabelOf(entry, deps) ?? id }),\n gripHint: () => deps.t('modelReorderHint'),\n // The official grid is `minmax(0,1.4fr) minmax(0,1fr) auto auto`. The grip\n // becomes a fifth child, so the grid needs a leading track to hold it \u2014\n // added by our own class, because the official rule is a single class\n // selector and ours has to win on specificity, not on load order.\n decorate: (_entry, seat) => { seat.classList.add(MODEL_ROW_CLASS) },\n }\n}\n\n/**\n * One pass over the page: name every model row in every provider editor, mount a\n * grip in its grid, and place the rows in the order the user stored for that\n * provider.\n *\n * A provider whose id cannot be recovered is skipped whole \u2014 reading a sequence\n * that might belong to another provider, or writing one there, is worse than\n * leaving that editor without grips for a scan.\n */\nexport function reconcileModelOrder(\n root: HTMLElement,\n deps: ModelOrderDeps,\n state: ModelOrderState,\n): void {\n if (!root.isConnected) return\n const labels = deps.labels()\n for (const list of modelListsOf(root)) {\n if (providerOfList(list, deps) === undefined) continue\n // Name the rows before the engine looks at them: a row with a blank id input\n // has no name, gets no grip, and is never moved.\n for (const entry of rowElements(list)) {\n const id = inputValueByLabel(entry, labels.modelId)\n if (id.length === 0) {\n entry.removeAttribute(ROW_ID_ATTR)\n continue\n }\n if (modelIdOf(entry) !== id) entry.setAttribute(ROW_ID_ATTR, id)\n }\n reconcileRows(root, list, modelAdapter(list, deps), state)\n }\n}\n\n/**\n * Undo everything this module put on the page. A plugin disable or an HMR\n * replace unmounts React trees on its own, but the grips, the widened grids and\n * the stamps are raw DOM: left behind, a grip would keep reordering rows \u2014 and\n * persisting that order \u2014 on behalf of a fiber that no longer exists.\n */\nexport function teardownModelOrder(state: ModelOrderState): void {\n teardownDrags(state)\n for (const seat of Array.from(document.querySelectorAll<HTMLElement>(`.${MODEL_ROW_CLASS}`))) {\n seat.classList.remove(MODEL_ROW_CLASS)\n }\n const stamped = Array.from(document.querySelectorAll<HTMLElement>(`[${ROW_ID_ATTR}]`))\n clearRowOrder(stamped)\n for (const entry of stamped) entry.removeAttribute(ROW_ID_ATTR)\n}\n\n/** The stored sequence for one provider, for callers that only need to read it. */\nexport function storedModelOrder(provider: string): readonly string[] {\n return modelOrderOf(provider)\n}\n", "/**\n * Per-session model directory resolution and the effort-memory wiretap ledger.\n *\n * Deliberately menu-INDEPENDENT: the directory is a per-session instance, and a\n * brand-new session's durable projection lands without going through `select`,\n * so the watcher is the only thing that can re-apply the remembered level to it\n * \u2014 it must be in place from the session's birth, not from the first time the\n * user opens the model menu (issue #4: every new session otherwise read as\n * Default until a manual pick). The caller therefore drives `ensure()` on every\n * mutation and scan; the per-session cache makes the hot path one snapshot read.\n *\n * @module dsh-model-think-level/client/injection/session-directory\n */\n\nimport { wireEffortMemory } from '../effort-memory.js'\nimport type { ModelDirectoryLike } from '../types.js'\n\n// ---- Host faces (structural; both kernels expose the same shape, see\n// ---- client/types.ts). ----\n\n/**\n * The `sessions` service face this needs: the current session id.\n *\n * Through 0.1.6-alpha.1 the catalog snapshot carried the selection as\n * `current`; 0.1.6-alpha.2 dropped it (navigation moved to the view owner) and\n * reports rows as `byId`, each with its `retainedBy` source counts.\n */\ninterface SessionsLike {\n list?: {\n getSnapshot(): {\n current?: string\n byId?: Record<string, { id?: string; retainedBy?: Record<string, number> } | undefined>\n }\n }\n}\n\n/**\n * The `ui-session` service face (0.1.6-alpha.2+): its scope adapter exposes the\n * main-view binding, whose `key` is the current session id.\n */\ninterface UiSessionLike {\n adapter?: { current?: { getSnapshot(): { key?: string } } }\n}\n\n/** The `modelDirectories` service face this needs. */\ninterface ModelDirectoriesLike {\n directoryFor(sessionId: string): ModelDirectoryLike\n}\n\n/**\n * Resolve the current session id across kernel lines:\n * 1. the list snapshot's `current` (0.1.6-alpha.1 and earlier);\n * 2. the ui-session main binding (0.1.6-alpha.2's authoritative source);\n * 3. the first main-view-retained catalog row \u2014 the same preference\n * ui-session itself applies, used when that service is absent.\n * A non-empty string is required at every step: an empty id would resolve no\n * session scope and only throw inside the resolver.\n * @param sessions - the sessions service face, when mounted.\n * @param uiSession - the ui-session service face, when mounted.\n * @returns the current session id, or undefined in the boot window.\n */\nfunction currentSessionId(\n sessions: SessionsLike | undefined,\n uiSession: UiSessionLike | undefined,\n): string | undefined {\n const snapshot = sessions?.list?.getSnapshot()\n const direct = snapshot?.current\n if (typeof direct === 'string' && direct.length > 0) return direct\n const bound = uiSession?.adapter?.current?.getSnapshot?.()?.key\n if (typeof bound === 'string' && bound.length > 0) return bound\n for (const [key, row] of Object.entries(snapshot?.byId ?? {})) {\n if (row === undefined || (row.retainedBy?.['mainView'] ?? 0) <= 0) continue\n return typeof row.id === 'string' && row.id.length > 0 ? row.id : key\n }\n return undefined\n}\n\n/** The context seats this probes, lazily (services mount after boot). */\ninterface SliderContext {\n get?(name: string): unknown\n}\n\n/** What the tracker needs from its host. */\nexport interface SessionDirectoryDeps {\n /** The client root context, probed lazily for its service seats. */\n ctx: unknown\n /** The configured-pick reader layering the effort chain (issue #4). */\n configuredEffort: (provider: string, model: string) => Promise<string | undefined>\n}\n\n/** The tracker's face. */\nexport interface SessionDirectoryTracker {\n /** Resolve (and wire) the current session's directory; undefined in the boot window. */\n ensure: () => ModelDirectoryLike | undefined\n /** Restore every wrapped directory's original select; the fiber is going away. */\n dispose: () => void\n}\n\n/**\n * Build the session-directory tracker.\n * @param deps - the client context and the configured-pick reader.\n * @returns the tracker's {@link SessionDirectoryTracker} face.\n */\nexport function createSessionDirectoryTracker(deps: SessionDirectoryDeps): SessionDirectoryTracker {\n let current: { sessionId: string; directory: ModelDirectoryLike } | undefined\n // One effort-memory wiretap per directory instance, with each original\n // `select` kept for the fiber disposer to restore. Entries record their\n // session id so the sweep below can release the wiretaps of directories whose\n // session scope is gone (a deleted session's directory must not stay\n // referenced and wrapped until fiber dispose).\n interface WiredDirectory {\n sessionId: string\n restore: () => void\n }\n const wiredDirectories = new Map<ModelDirectoryLike, WiredDirectory>()\n\n /**\n * Release the wiretaps whose session scope is gone. The host resolver\n * deletes a directory's entry when the session scope tears down, and its\n * `directoryFor` then refuses that id (scope and binding are both gone --\n * not the transient unmounted window, which happens before a wiretap can\n * exist): re-resolving that id and comparing identities tells a dead\n * wiretap from one whose session is merely not current. Runs on EVERY\n * directory resolution, cache hits included -- the sweep must not depend\n * on the current session changing.\n */\n const sweepDeadWiretaps = (directories: ModelDirectoriesLike, liveDirectory: ModelDirectoryLike): void => {\n for (const [wired, entry] of wiredDirectories) {\n if (wired === liveDirectory) continue\n let alive: boolean\n try {\n alive = directories.directoryFor(entry.sessionId) === wired\n } catch {\n alive = false\n }\n if (!alive) {\n entry.restore()\n wiredDirectories.delete(wired)\n }\n }\n }\n\n const ensure = (): ModelDirectoryLike | undefined => {\n const host = deps.ctx as SliderContext\n const sessions = host.get?.('sessions') as SessionsLike | undefined\n const uiSession = host.get?.('uiSession') as UiSessionLike | undefined\n const directories = host.get?.('modelDirectories') as ModelDirectoriesLike | undefined\n const sessionId = currentSessionId(sessions, uiSession)\n if (sessionId === undefined || directories === undefined) return undefined\n try {\n // Resolve FIRST, then compare identities: a session scope can be torn\n // down and re-resolved under the same id, and the cached instance would\n // be a disposed directory serving a frozen snapshot forever.\n const directory = directories.directoryFor(sessionId)\n sweepDeadWiretaps(directories, directory)\n if (current !== undefined && current.sessionId === sessionId\n && current.directory === directory) {\n return directory\n }\n // The effort-memory wiretap rides the shared directory (wrapping its\n // select AND watching for level-less restored projections), so a model\n // switch and a session restore both carry the remembered level.\n // Idempotent per instance. The configured-pick read rides along so the\n // chain's second layer comes from the settings document.\n if (!wiredDirectories.has(directory)) {\n wiredDirectories.set(directory, {\n sessionId,\n restore: wireEffortMemory(directory, { configuredEffort: deps.configuredEffort }),\n })\n }\n current = { sessionId, directory }\n return directory\n } catch {\n // Unknown session (a scope not mounted yet): transient \u2014 retry next scan.\n current = undefined\n return undefined\n }\n }\n\n const dispose = (): void => {\n for (const [, entry] of wiredDirectories) entry.restore()\n wiredDirectories.clear()\n }\n\n return { ensure, dispose }\n}\n", "/**\n * Locating the official composer model seat \u2014 the ONE surface this plugin may\n * decorate with a provider prefix.\n *\n * Everything in this module is scoped to the composer card\n * (`[data-composer-card]`, owned by InputBar) and, inside it, to the model\n * seat's own slot anchor. That scope is not a convenience: the shell renders\n * several `button[aria-haspopup=\"menu\"]` elements (the settings sidebar's\n * account button, the composer's permission selector, \u2026), so a document-wide\n * search hands back a button this plugin does not own and paints the provider\n * prefix on a host surface's label. When the seat cannot be identified the\n * finders return undefined \u2014 the prefix is simply not shown, never stamped on\n * someone else's control.\n *\n * @module dsh-model-think-level/client/injection/model-menu\n */\n\n/**\n * The declared slot whose single entry is the model seat (ModelSelect).\n *\n * `renderSlot` wraps every entry in an addressable anchor div (`data-slot` =\n * the slot key, `display: contents`), which the shell documents as \"purely\n * addressable surface\". The anchor therefore names the seat independently of\n * its translated copy, and it is the identity to trust whenever the shell\n * renders it.\n */\nconst MODEL_SEAT_SLOT = 'conversation.input.model'\n\n/**\n * The composer card: the shell's single owner of the model seat.\n *\n * The card is both the search scope and the proof that a candidate button\n * belongs to this plugin's seat at all. The shell renders ONE card (the\n * composer input surface, `data-composer-card` on InputBar's card div).\n *\n * @param doc - the document to search.\n * @returns the card element, or undefined when no composer is mounted.\n */\nfunction composerCardOf(doc: Document): HTMLElement | undefined {\n return doc.querySelector<HTMLElement>('[data-composer-card]') ?? undefined\n}\n\n/**\n * The model seat's own subtree inside the card.\n *\n * @param card - the composer card.\n * @returns the seat's slot anchor, or undefined on a shell that renders none.\n */\nfunction seatAnchorOf(card: HTMLElement): HTMLElement | undefined {\n return card.querySelector<HTMLElement>(`[data-slot=\"${MODEL_SEAT_SLOT}\"]`) ?? undefined\n}\n\n/**\n * The menu-trigger buttons that may belong to the model seat.\n *\n * Scoped to the seat's anchor when it exists, so the composer's permission\n * selector (rendered earlier in the card, also `aria-haspopup=\"menu\"`) is never\n * a candidate. Without an anchor the whole card is the scope, and the ordering\n * rules in the finders below pick the seat out of it.\n *\n * @param card - the composer card.\n * @returns the candidate triggers, in document order.\n */\nfunction seatTriggerCandidates(card: HTMLElement): HTMLButtonElement[] {\n const scope = seatAnchorOf(card) ?? card\n return Array.from(scope.querySelectorAll<HTMLButtonElement>('button[aria-haspopup=\"menu\"]'))\n}\n\n/**\n * The menu a trigger points at through `aria-controls`, when it is one.\n *\n * This is the primary route because a portaled popover leaves the card while\n * the control keeps naming it, so the link is what survives the move. A\n * controls id that names nothing, or names a non-menu element, is not a menu.\n *\n * @param trigger - the candidate button.\n * @param doc - the document the linked element lives in.\n * @returns the linked menu, or undefined.\n */\nfunction linkedMenuOf(trigger: HTMLButtonElement, doc: Document): HTMLElement | undefined {\n const id = trigger.getAttribute('aria-controls')\n if (id === null || id.length === 0) return undefined\n const menu = doc.getElementById(id)\n if (menu === null) return undefined\n return menu.getAttribute('role') === 'menu' ? menu : undefined\n}\n\n/**\n * The official composer model menu: the seat root's open popover.\n *\n * Shape-tolerant across kernels: newer kernels portal the menu to\n * document.body and link it from the seat trigger via aria-controls, while\n * older kernels render it inline right after the trigger. Both shapes keep\n * aria-haspopup=\"menu\" on the trigger and role=\"menu\" on the menu, so the\n * controls link is the primary route and the sibling check stays the fallback.\n *\n * The search NEVER leaves the composer card. When no card is mounted there is\n * no model seat, and answering with some other surface's menu is exactly the\n * cross-surface confusion this scoping exists to prevent.\n *\n * @param doc - the document to search.\n * @returns the open model menu, or undefined while it is closed.\n */\nexport function findModelMenu(doc: Document = document): HTMLElement | undefined {\n const card = composerCardOf(doc)\n if (card === undefined) return undefined\n\n for (const trigger of seatTriggerCandidates(card)) {\n const menu = linkedMenuOf(trigger, doc)\n if (menu !== undefined) return menu\n }\n\n // Pre-portal inline shape: the menu sits right after its trigger button.\n // Scoped like the candidates, so another seat's inline menu is not mistaken\n // for the model seat's.\n const scope = seatAnchorOf(card) ?? card\n for (const menu of Array.from(scope.querySelectorAll<HTMLElement>('[role=\"menu\"]'))) {\n if (menu.previousElementSibling?.matches('button[aria-haspopup=\"menu\"]')) return menu\n }\n return undefined\n}\n\n/**\n * The official composer model trigger.\n *\n * The trigger stays in the card while its menu is closed, so this finder keeps\n * the menu lookup's linked and inline checks and then falls back to the seat\n * the closed card still renders:\n *\n * - inside the seat's anchor, every candidate IS the seat;\n * - without an anchor, the seat is the LAST menu trigger of the card, since\n * the shell renders it at the end of the composer's trailing control row\n * (after the tools row that hosts the permission selector).\n *\n * @param doc - the document to search.\n * @returns the composer's model trigger, or undefined while no composer exists.\n */\nexport function findModelTrigger(doc: Document = document): HTMLButtonElement | undefined {\n const card = composerCardOf(doc)\n if (card === undefined) return undefined\n\n const anchor = seatAnchorOf(card)\n const candidates = seatTriggerCandidates(card)\n const seat = candidates[0]\n if (seat === undefined) return undefined\n\n // Prefer the trigger whose controls link resolves to the currently open\n // model menu. This disambiguates a card that holds another menu trigger.\n for (const trigger of candidates) {\n if (linkedMenuOf(trigger, doc) !== undefined) return trigger\n }\n\n // Pre-portal inline shape: the menu follows its trigger in the card.\n for (const trigger of candidates) {\n if (trigger.nextElementSibling?.matches('[role=\"menu\"]')) return trigger\n }\n\n return anchor === undefined ? candidates.at(-1) : seat\n}\n\n/** The official composer model menu: the seat root's open popover. */\nexport function modelMenuOf(): HTMLElement | undefined {\n return findModelMenu()\n}\n\n/** The official composer model trigger, or undefined while the seat is absent. */\nexport function modelTriggerOf(): HTMLButtonElement | undefined {\n return findModelTrigger()\n}\n", "/**\n * The provider-card slot registration: the seat the request-header editor\n * (issue #12) renders in.\n *\n * `settings.models.provider-card` is a KEYED slot declared by the official\n * Models section, dispatched on each card's owning settings namespace\n * (`ProviderDirectoryEntry.settingsNs`). Registering under `llm-pi-ai` is\n * therefore all it takes to receive every card of this adapter family \u2014 the\n * shipped routes, the ones the user adds, and the hand-declared ones alike \u2014\n * without a DOM anchor, a mutation observer, or a label to match on.\n *\n * The ROUTE is not a registration-time fact: the section passes each occurrence\n * its directory row as owner props, so the route arrives on the component at\n * render time and one registration serves every card.\n *\n * The mount also hosts the editor's tab bar ({@link EditorTabBar}) and is the\n * element the editor-tabs pass resolves its card from, so the plugin's own\n * subtree is what carries both the takeover's control and the state the\n * stylesheet reveals the section on.\n *\n * @module dsh-model-think-level/client/injection/provider-card-slot\n */\n\nimport { createElement, useEffect, useRef } from 'react'\nimport type { ReactNode } from 'react'\nimport type { Translate } from '@deepseek-ai/dsh-client-ui-slots'\nimport { PI_AI_NS, PLUGIN_ID } from '../../constants.js'\nimport { HeadersEditor } from '../HeadersEditor.js'\nimport { EditorTabBar } from './editor-tab-bar.js'\nimport { editorCardOf } from './editor-tabs.js'\nimport type { ClientContext, RemoteApi, SlotRegistrarFace } from '../types.js'\n\n/** The keyed-slot key this plugin occupies: the adapter family it extends. */\nconst SLOT = 'settings.models.provider-card'\n\n/**\n * The occurrence props the official Models section hands one provider-card\n * extension (its own `ProviderCardExtrasOwnerProps`). Declared structurally for\n * the same reason as {@link SlotRegistrarFace}: the plugin pins only what it\n * reads, so a field the section adds cannot break activation.\n */\ninterface ProviderCardOccurrence {\n /** The card's directory row; `provider` is the settings route key. */\n provider?: { provider?: string }\n}\n\n/** Props the occurrence component receives: the section's share plus ours. */\nexport type ProviderCardSlotProps = ProviderCardOccurrence & { api: RemoteApi; t: Translate }\n\n/**\n * The occurrence component: resolve this card's route, render the editor with\n * its tab bar, and publish the card's open/closed state so the section reveals\n * itself with the official editor. Exported for its own tests (the registration\n * below is the production path).\n */\nexport function ProviderCardSlot({ provider, api, t }: ProviderCardSlotProps): ReactNode {\n const host = useRef<HTMLDivElement | null>(null)\n\n useEffect(() => {\n const wrapper = host.current\n // No card row around us (a bare render) means no card state to observe:\n // leaving `data-edit` off keeps the section hidden, which is the only safe\n // default.\n const card = editorCardOf(wrapper)\n if (wrapper === null || card === null) return\n const observed = card as unknown as Node\n // The one edit-state signal the official row exposes. The section holds\n // its editor target in component state (ui-settings-models\n // ModelsSection's `editing`) and renders the editor with NO data\n // attribute and NO aria-expanded on the row's Edit button \u2014 the CSS-module\n // class of the mounted editor (word root `editor`, compiled to a\n // `_editor*` hash) is the only thing that exists iff the card is open.\n // This is a COUPLING, not a contract: if a future official build renames\n // that class root, `data-edit` degrades to a constant 0 \u2014 the section\n // stays hidden and the official page stays untouched, so the failure mode\n // is \"the feature is gone\", never \"the page is broken\". The upgrade path\n // is an official one: a data attribute on the editor or an `editing` field\n // on the slot occurrence replaces this probe one-for-one.\n const openEditor = (): boolean => card.querySelector('[class*=\"_editor\"]') !== null\n // Publish the card's state onto our own wrapper instead of into React\n // state. The reveal has to land in the batch the official mutation arrived\n // in: a setState from here is committed by React's scheduler, i.e. after\n // the frame that already painted the opened editor, and the section \u2014 the\n // tab bar's own container \u2014 would blink in one frame late. Writing the\n // attribute directly is the same choice the composer's popover path makes.\n // The compare keeps a settled card from emitting mutations of its own, and\n // `data-edit` is deliberately not a prop below, so React never patches it\n // back.\n const sync = (): void => {\n const edit = openEditor() ? '1' : '0'\n if (wrapper.dataset['edit'] !== edit) wrapper.dataset['edit'] = edit\n }\n sync()\n const observer = new MutationObserver(sync)\n // Child list only, and subtree: the editor element itself arrives as a\n // child of the row, and its own internals are irrelevant here.\n observer.observe(observed, { childList: true, subtree: true })\n return () => {\n observer.disconnect()\n }\n }, [])\n\n const route = provider?.provider\n // A card with no resolvable route is not this section's business: render\n // nothing rather than an editor that could not address a settings key.\n if (typeof route !== 'string' || route.length === 0) return null\n return createElement(\n 'div',\n {\n ref: host,\n className: 'bre-headers-host',\n // Names the provider row this slot sits in, for the row-order pass. The\n // slot is dispatched per card, so it is the one place every llm-pi-ai row\n // \u2014 saved, first-run or draft \u2014 hands back its route.\n 'data-bre-provider': route,\n },\n // The tab bar goes FIRST and lives in this, our own, subtree \u2014 never in the\n // official card's child list, which React reconciles. The stylesheet\n // reveals it while the card carries the editor-tabs takeover, so it appears\n // with the tabbed editor and stays hidden on a card the pass left alone\n // (the DeepSeek account editor).\n createElement(EditorTabBar, { host, t }),\n createElement(HeadersEditor, { route, api, t }),\n )\n}\n\n/**\n * Take the provider-card seat.\n * @param ctx - client root context (its `slots` service registers the seat).\n * @param api - the settings Remote the editor reads and writes through.\n * @param t - the plugin's locale-bound translator.\n * @returns the seat's disposer, or a no-op when the seat is unavailable.\n */\nexport function registerProviderCardSlot(ctx: ClientContext, api: RemoteApi, t: Translate): () => void {\n const host = ctx as unknown as { slots?: SlotRegistrarFace }\n // `inject` is the seam the official registrar binds to the calling scope;\n // wire whatever comes back so a plugin disable / HMR leaves no seat behind.\n const injected = host.slots?.inject(SLOT, () => {\n host.slots?.register({\n name: SLOT,\n // The keyed dispatch key. `llm-pi-ai` is the ONLY family this plugin\n // extends, so one registration covers every card; other adapter families\n // dispatch other keys and never reach this component.\n key: PI_AI_NS,\n inject: () => ({ api, t }),\n }, ProviderCardSlot)\n })\n return typeof injected === 'function' ? injected as () => void : () => {}\n}\n", "/**\n * The provider-card request-header editor (issue #12, MVP-1) \u2014 the fetch-layer\n * `user-agent` takeover is its MVP-2 half, rendered in the same section because\n * to the user they are one thing: \"what this route sends\".\n *\n * Mounted through the OFFICIAL seat, `settings.models.provider-card`, which the\n * Models section dispatches per provider card. That is a sanctioned extension\n * point, so unlike the per-model effort editor this component needs no DOM\n * injection, no mutation observer and no anchor labels.\n *\n * Write discipline: the draft is a whole dict and only the explicit Save writes\n * it, so an edit can never race a stale snapshot of the same card the way\n * per-keystroke writes would \u2014 and Cancel/Reload behaves like the official\n * card's own fields. The headers land through `llm-pi-ai.providers.<route>.headers`,\n * the one channel the adapter actually sends.\n *\n * @module dsh-model-think-level/HeadersEditor\n */\n\nimport { useEffect, useId, useRef, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport { HEADERS_CONFIG_PATH, PLUGIN_ID } from '../constants.js'\nimport { describeNamespace, providerHeadersOf, writeProviderHeaders, type ProviderHeadersReply } from './ops.js'\nimport type { RemoteApi } from './types.js'\n\n/**\n * Offered client identities, most useful first. These are the shapes relays\n * that police the `user-agent` look for; every one is a preset, never a filter\n * \u2014 the field stays free text.\n */\nconst USER_AGENT_PRESETS: readonly string[] = [\n 'claude-cli/2.1.161 (external, cli)',\n 'claude-code/1.0.0',\n 'Kilo-Code/1.0',\n]\n\n/** The fixed fetch-layer report the host route serves. */\nexport interface HeaderEnvironment {\n /** Whether the fetch-layer takeover is enabled in this deployment. */\n enabled: boolean\n /** Whether the plugin's wrapper currently owns the global fetch. */\n installed: boolean\n /** Routes whose configured request headers are sent. */\n overrides: { origin: string; route: string; headers: Record<string, string>; userAgent?: string }[]\n /** Origins two routes claim with different User-Agent values. */\n conflicts: { origin: string; routes: string[]; values: string[] }[]\n /** Same-origin differences for any configured header. */\n headerConflicts?: { origin: string; header: string; routes: string[]; values: string[] }[]\n /** What was found of the plugins rewriting the same surface. */\n environment: {\n adapter: 'patched' | 'stock' | 'unknown'\n siblings: { name: string; path: string }[]\n preferOfficialLayer: boolean\n }\n}\n\n/** Props of {@link HeadersEditor}. */\nexport interface HeadersEditorProps {\n /** The provider route this card edits. */\n route: string\n /** The write seam. */\n api: RemoteApi\n /** Localized copy. */\n t: (key: string, params?: Record<string, string | number>) => string\n}\n\n/** One editable header row; a stable `id` keeps React keys off the name. */\ninterface DraftRow {\n id: number\n name: string\n value: string\n}\n\n/** A draft as the editor holds it: every configured header is one row. */\ninterface Draft {\n rows: DraftRow[]\n /** Legacy input accepted by headersFromDraft callers; the UI no longer uses it. */\n userAgent?: string\n}\n\n/** Build a draft from every stored header entry, including User-Agent. */\nfunction draftFrom(headers: Record<string, string> | undefined): Draft {\n let id = 0\n const rows = Object.entries(headers ?? {}).map(([name, value]) => ({ id: id += 1, name, value }))\n // One blank pair by default: the pane is the editor now, and an empty pane\n // would offer nothing to type into \u2014 the other two panes always show fields,\n // and this one starts with one pair the user can fill (or replace) directly.\n if (rows.length === 0) rows.push({ id: id += 1, name: '', value: '' })\n return { rows }\n}\n\n/** The headers dict a draft stores: blank names are dropped. */\nexport function headersFromDraft(draft: Draft): Record<string, string> {\n const out: Record<string, string> = {}\n let hasUserAgent = false\n for (const row of draft.rows) {\n const name = row.name.trim()\n if (name.length === 0) continue\n out[name] = row.value\n if (name.toLowerCase() === 'user-agent') hasUserAgent = true\n }\n // Keep old programmatic callers working while new UI drafts use rows only.\n const legacyUserAgent = draft.userAgent?.trim()\n if (!hasUserAgent && legacyUserAgent !== undefined && legacyUserAgent.length > 0) out['user-agent'] = legacyUserAgent\n return out\n}\n\n/**\n * Render the provider card's request-header section.\n * @param props - route identity, write seam and copy.\n * @returns the section.\n */\nexport function HeadersEditor({ route, api, t }: HeadersEditorProps): ReactNode {\n // Kept for the optional User-Agent preset list when a row is named that way.\n const uaListId = useId()\n const [stored, setStored] = useState<Record<string, string> | undefined>(undefined)\n const [draft, setDraft] = useState<Draft>({ rows: [] })\n /** Whether the section's details are disclosed. Collapsed on mount. */\n const [open, setOpen] = useState(false)\n const [editing, setEditing] = useState(false)\n const [loading, setLoading] = useState(true)\n const [busy, setBusy] = useState(false)\n const [readOnly, setReadOnly] = useState(false)\n const [message, setMessage] = useState<{ kind: 'success' | 'error' | 'info'; text: string } | undefined>(undefined)\n const [environment, setEnvironment] = useState<HeaderEnvironment | undefined>(undefined)\n /**\n * Whether the tab takeover made this section a whole pane (`\u9AD8\u7EA7\u914D\u7F6E`).\n *\n * There is no disclosure control to click there: the pane holds this section\n * and nothing else, so it renders expanded and drops the pill.\n */\n const [tabbed, setTabbed] = useState(false)\n const nextRowId = useRef(1)\n const rootRef = useRef<HTMLDivElement | null>(null)\n\n // Seed the draft from the stored document. The slot hands the editor a route,\n // not a profile: the section reads the pi-ai namespace itself, so the card\n // re-renders that the section pushes cannot hand it a stale draft.\n useEffect(() => {\n let live = true\n void (async () => {\n try {\n const join = await describeNamespace(api)\n if (!live) return\n const headers = providerHeadersOf(join.namespace, route)\n setStored(headers)\n setDraft(draftFrom(headers))\n setReadOnly(join.writable !== true)\n } catch {\n // A read failure leaves the section inert rather than blocking the card.\n if (live) setReadOnly(true)\n } finally {\n if (live) setLoading(false)\n }\n })()\n return () => { live = false }\n }, [api, route])\n\n // The fetch-layer report is a host fact the page cannot observe on its own:\n // read once per mount, and never let its absence break the editor.\n useEffect(() => {\n let live = true\n void (async () => {\n try {\n const response = await fetch(HEADERS_CONFIG_PATH, { method: 'GET' })\n const body = await response.json() as { ok?: boolean; data?: HeaderEnvironment }\n if (live && body.ok === true && body.data !== undefined) setEnvironment(body.data)\n } catch {\n // Older host, or a page that cannot reach its own origin: stay silent.\n }\n })()\n return () => { live = false }\n }, [])\n\n // The takeover marks this component's own root with `data-bre-region=\"advanced\"`\n // (the editor-tabs pass), which is the only signal that says \"you are the\n // whole \u9AD8\u7EA7\u914D\u7F6E pane now\". The mark lands in the microtask carrying the\n // official commit, so it can arrive either side of this effect: read once on\n // mount AND observe, because neither alone is sufficient.\n useEffect(() => {\n const root = rootRef.current\n if (root === null) return\n const sync = (): void => { setTabbed(root.dataset['breRegion'] === 'advanced') }\n sync()\n const observer = new MutationObserver(sync)\n observer.observe(root, { attributes: true, attributeFilter: ['data-bre-region'] })\n return () => { observer.disconnect() }\n }, [])\n\n const addRow = (): void => {\n setDraft(current => ({ ...current, rows: [...current.rows, { id: nextRowId.current += 1, name: '', value: '' }] }))\n setMessage(undefined)\n }\n\n const patchRow = (id: number, patch: Partial<DraftRow>): void => {\n setDraft(current => ({ ...current, rows: current.rows.map(row => (row.id === id ? { ...row, ...patch } : row)) }))\n setMessage(undefined)\n }\n\n const removeRow = (id: number): void => {\n setDraft(current => ({ ...current, rows: current.rows.filter(row => row.id !== id) }))\n setMessage(undefined)\n }\n\n const cancel = (): void => {\n setDraft(draftFrom(stored))\n setEditing(false)\n setMessage(undefined)\n }\n\n const save = async (next: Record<string, string>): Promise<void> => {\n setBusy(true)\n setMessage(undefined)\n let reply: ProviderHeadersReply\n try {\n reply = await writeProviderHeaders(api, route, next)\n } catch (error) {\n reply = { ok: false, error: error instanceof Error ? error.message : String(error) }\n }\n setBusy(false)\n if (!reply.ok) {\n setMessage({\n kind: 'error',\n text: reply.error === 'no-route' ? t('headersNoRoute')\n : reply.error === 'read-only' ? t('headersReadOnly')\n : t('headersError', { error: reply.error }),\n })\n return\n }\n setStored(next)\n setEditing(false)\n // Saving opened the section (edit mode is always disclosed); landing the\n // write keeps it open so the user sees the new value rather than watching\n // the card snap shut under them.\n setMessage({\n kind: 'success',\n text: Object.keys(next).length === 0 ? t('headersCleared') : t('headersSaved'),\n })\n }\n\n const rows = Object.entries(stored ?? {})\n const disabled = readOnly || busy\n // Collapsed by default in the single-column card: the card is a list of\n // providers, and a section that pushed its hint, its rows and its warnings\n // into every card would bury the list. Collapsed shows the section's identity\n // only \u2014 what it is called and whether anything is configured \u2014 and the\n // disclosure arrow opens the rest. The tab takeover removes that trade-off:\n // there the section is the whole \u9AD8\u7EA7\u914D\u7F6E pane, so it stays expanded and the\n // disclosure disappears instead of hiding the only thing the pane holds.\n const count = rows.length\n // Under the tabs the pane IS the editor: rows on screen, typed in place, and\n // the pane's own Save commits them through the same seam the fold's editor\n // used. No \"press Edit first\" step \u2014 the other two panes take their edits\n // directly, and this one does now too.\n const inline = tabbed\n\n return (\n <div className=\"bre-headers\" ref={rootRef} data-plugin={PLUGIN_ID} data-open={open ? '1' : '0'}>\n {/* The title strip only exists while the section is a fold inside the\n provider card: the grey head is the identity of a collapsed disclosure.\n Under the tabs the section is the whole \u9AD8\u7EA7\u914D\u7F6E pane, and the\n provider and models panes carry no heading strip of their own \u2014 the\n tab itself names the pane \u2014 so the head steps aside there the same\n way the disclosure button does, keeping the three panes identical in\n weight. */}\n {!tabbed && (\n <div className=\"bre-headers-head\">\n {editing\n ? <span className=\"bre-effort-title\">{t('headersTitle')}</span>\n : (\n <button\n type=\"button\"\n className=\"bre-headers-disclosure\"\n aria-expanded={open}\n aria-label={`${t('headersTitle')} ${route}`}\n onClick={() => { setOpen(!open); setMessage(undefined) }}\n >\n <span className=\"bre-effort-title\">{t('headersTitle')}</span>\n {loading || count === 0\n ? null\n : <span className=\"bre-headers-count\">{count}</span>}\n <span className=\"bre-headers-chevron\" aria-hidden=\"true\">\u203A</span>\n </button>\n )}\n </div>\n )}\n\n {!open && !editing && !tabbed\n ? null\n : (\n <>\n <p className=\"bre-effort-note\">{t('headersHint')}</p>\n {environment === undefined\n ? null\n : (\n <>\n {environment.environment.preferOfficialLayer\n ? <p className=\"bre-effort-note bre-warn\">{t('headersConflictPatched')}</p>\n : null}\n {environment.conflicts.length === 0 && (environment.headerConflicts?.length ?? 0) === 0\n ? null\n : <p className=\"bre-effort-note bre-warn\">{t('headersConflictOrigin')}</p>}\n {environment.environment.siblings.length === 0\n ? null\n : (\n <p className=\"bre-effort-note bre-warn\">\n {t('headersConflictSiblings', { names: environment.environment.siblings.map(item => item.name).join(', ') })}\n </p>\n )}\n </>\n )}\n\n {loading\n ? null\n : editing || inline\n ? (\n <div className=\"bre-headers-edit\">\n <div className=\"bre-headers-columns\" aria-hidden=\"true\">\n <span>{t('headersName')}</span>\n <span>{t('headersValue')}</span>\n <span />\n </div>\n {draft.rows.map(row => (\n <div className=\"bre-headers-row\" key={row.id}>\n <input\n type=\"text\"\n className=\"bre-text-input bre-headers-name\"\n value={row.name}\n disabled={disabled}\n placeholder={t('headersNamePlaceholder')}\n aria-label={t('headersName')}\n onChange={(event) => { patchRow(row.id, { name: event.target.value }) }}\n />\n <input\n type=\"text\"\n className=\"bre-text-input bre-headers-value\"\n value={row.value}\n disabled={disabled}\n list={row.name.trim().toLowerCase() === 'user-agent' ? uaListId : undefined}\n placeholder={t('headersValue')}\n aria-label={t('headersValue')}\n onChange={(event) => { patchRow(row.id, { value: event.target.value }) }}\n />\n <button\n type=\"button\"\n className=\"bre-link-button bre-headers-remove\"\n disabled={disabled}\n aria-label={`${t('headersClear')} ${row.name}`}\n onClick={() => { removeRow(row.id) }}\n >\n \u00D7\n </button>\n </div>\n ))}\n <button\n type=\"button\"\n className=\"bre-link-button bre-headers-add\"\n disabled={disabled}\n onClick={addRow}\n >\n {t('headersAdd')}\n </button>\n {false && (<div className=\"bre-headers-legacy\">\n <span className=\"bre-effort-title\">{t('headersUserAgentTitle')}</span>\n <input\n type=\"text\"\n className=\"bre-text-input bre-headers-value\"\n value={draft.userAgent ?? ''}\n disabled={disabled}\n list={uaListId}\n placeholder={t('headersUserAgentPlaceholder')}\n aria-label={t('headersUserAgentTitle')}\n onChange={(event) => {\n setDraft(current => ({ ...current, userAgent: event.target.value }))\n setMessage(undefined)\n }}\n />\n {(draft.userAgent ?? '').trim().length === 0\n ? null\n : (\n <button\n type=\"button\"\n className=\"bre-link-button bre-headers-remove\"\n disabled={disabled}\n onClick={() => { setDraft(current => ({ ...current, userAgent: '' })); setMessage(undefined) }}\n >\n {t('headersUserAgentClear')}\n </button>\n )}\n </div>\n )}\n <datalist id={uaListId}>\n {USER_AGENT_PRESETS.map(preset => <option key={preset} value={preset} />)}\n </datalist>\n <p className=\"bre-effort-note bre-headers-legacy\" hidden>{t('headersUserAgentHint')}</p>\n <div className=\"bre-effort-actions\">\n <button\n type=\"button\"\n className=\"bre-primary-button\"\n disabled={disabled}\n onClick={() => { void save(headersFromDraft(draft)) }}\n >\n {t('headersSave')}\n </button>\n <button type=\"button\" className=\"bre-secondary-button\" disabled={disabled} onClick={cancel}>\n {t('headersCancel')}\n </button>\n </div>\n </div>\n )\n : (\n <div className=\"bre-headers-rows\">\n {rows.length === 0\n ? <p className=\"bre-effort-note\">{t('headersEmpty')}</p>\n : rows.map(([name, value]) => (\n <div className=\"bre-headers-row\" key={name}>\n <span className=\"bre-headers-name\">{name}</span>\n {/* Masked: the official README warns that a credential in\n `headers` is not reached by the harness redactor, so the\n value must not sit in plain sight on a shared screen. */}\n <span className=\"bre-headers-value bre-headers-masked\">{'\u2022'.repeat(Math.min(value.length, 24))}</span>\n </div>\n ))}\n {readOnly\n ? null\n : (\n <div className=\"bre-effort-actions\">\n <button\n type=\"button\"\n className=\"bre-secondary-button\"\n disabled={disabled}\n onClick={() => { setEditing(true); setMessage(undefined) }}\n >\n {t('headersEdit')}\n </button>\n </div>\n )}\n </div>\n )}\n </>\n )}\n\n {message === undefined\n ? null\n : (\n <p className={'bre-effort-message bre-' + message.kind} role={message.kind === 'error' ? 'alert' : 'status'}>\n {message.text}\n </p>\n )}\n </div>\n )\n}\n", "/**\n * The provider editor's segmented control (\u4F9B\u5E94\u5546\u914D\u7F6E / \u6A21\u578B\u914D\u7F6E / \u9AD8\u7EA7\u914D\u7F6E).\n *\n * This is the bar the editor-tabs pass drives. It lives HERE, in the plugin's\n * own React subtree, rather than being built by the pass and inserted into the\n * official card: the card's children belong to React, and a foreign node in\n * that list survives only until the next render. The component is rendered\n * inside the provider-card slot's own mount \u2014 a child of the card the slot\n * already resolves \u2014 and the stylesheet reveals it as soon as the pass marks\n * the card (`bre-tabbed`), which happens in the same mutation batch as the\n * official render.\n *\n * The component renders STRUCTURE only: it holds no state, because the card's\n * `data-bre-tab` attribute is the source of truth for both the pane switch\n * (pure CSS) and the selected button. The pass owns the copy, the per-pane\n * availability and `aria-selected`, so a language switch relabels the bar\n * without a re-render and a pane that is not on the card hides its own button.\n *\n * @module dsh-model-think-level/client/injection/editor-tab-bar\n */\n\nimport { createElement } from 'react'\nimport type { ReactNode } from 'react'\nimport type { Translate } from '@deepseek-ai/dsh-client-ui-slots'\nimport {\n EDITOR_TABS_CLASS,\n EDITOR_TAB_BUTTON_CLASS,\n EDITOR_TAB_ID_ATTR,\n EDITOR_TAB_IDS,\n editorCardOf,\n selectEditorTab,\n type EditorTabId,\n} from './editor-tabs.js'\n\n/** The locale key of each tab's label. */\nconst LABEL_KEYS: Record<EditorTabId, string> = {\n provider: 'editorTabProvider',\n models: 'editorTabModels',\n advanced: 'editorTabAdvanced',\n}\n\nexport interface EditorTabBarProps {\n /** The plugin's own mount inside the card this bar switches. It hangs\n * directly off the card, which is how the card is resolved on click. */\n host: { readonly current: HTMLDivElement | null }\n /** The plugin's translator. */\n t: Translate\n}\n\n/** One provider editor's tab bar. */\nexport function EditorTabBar({ host, t }: EditorTabBarProps): ReactNode {\n return createElement(\n 'div',\n { className: EDITOR_TABS_CLASS, role: 'tablist', 'aria-label': t('editorTabsLabel') },\n ...EDITOR_TAB_IDS.map((id) => createElement(\n 'button',\n {\n key: id,\n type: 'button',\n role: 'tab',\n className: EDITOR_TAB_BUTTON_CLASS,\n [EDITOR_TAB_ID_ATTR]: id,\n onClick: (event: MouseEvent): void => {\n // The tab is this plugin's own control: the official page has no\n // click business here, and a stray handler of theirs must not see the\n // gesture that rearranges their editor.\n event.preventDefault()\n event.stopPropagation()\n const card = editorCardOf(host.current)\n if (card !== null) selectEditorTab(card, id)\n },\n },\n t(LABEL_KEYS[id]),\n )),\n )\n}\n", "/**\n * The reasoning-effort slider enable toggle. The item form is the upstream\n * plugin's setting row ported VERBATIM (title, description, state, switch \u2014\n * colors, sizes and spacing included), wrapped in the requested boxed\n * container on the Models settings page below the add-provider and\n * add-custom-provider actions. Mounted through the official\n * 'settings.models.footer' slot.\n *\n * The big-fish slider setting is deliberately gone: that feature was not\n * carried into this integration.\n *\n * @module dsh-model-think-level/client/SliderToggle\n */\nimport { createElement, useSyncExternalStore } from 'react'\nimport type { ReactNode } from 'react'\nimport { setSliderEnabled, sliderEnabled, subscribeSliderEnabled } from './slider-pref.js'\n\nexport interface SliderToggleProps {\n /** Localized copy (the plugin's bound translator). */\n t: (key: string, params?: Record<string, string | number>) => string\n}\n\n/** One boxed setting row: label copy (upstream .re-setting-row form), live state, and the switch. */\nexport function SliderToggle(props: SliderToggleProps): ReactNode {\n const { t } = props\n const enabled = useSyncExternalStore(subscribeSliderEnabled, sliderEnabled)\n const toggle = (): void => { setSliderEnabled(!enabled) }\n return createElement(\n 'div',\n { className: 'bre-slider-setting' },\n createElement('div', { className: 'bre-slider-setting-row' },\n createElement('div', { className: 'bre-slider-setting-copy' },\n createElement('div', { className: 'bre-slider-setting-title' }, t('sliderToggleTitle')),\n createElement('div', { className: 'bre-slider-setting-description' }, t('sliderToggleDescription')),\n ),\n createElement('div', { className: 'bre-slider-setting-control' },\n createElement('span', { className: 'bre-slider-setting-state' }, enabled ? t('settingOn') : t('settingOff')),\n createElement('button', {\n type: 'button',\n role: 'switch',\n 'aria-label': t('sliderToggleAria'),\n 'aria-checked': enabled,\n className: 'bre-slider-setting-switch' + (enabled ? ' is-on' : ''),\n onClick: toggle,\n }, createElement('span', { className: 'bre-slider-setting-switch-knob', 'aria-hidden': true })),\n ),\n ),\n )\n}\n", "/**\n * The Models-page footer slot: the boxed composer-slider toggle.\n *\n * The Models page ships the sanctioned 'settings.models.footer' extension slot\n * after the provider rows and the add controls, and the plugin's top-level\n * inject already declares 'remote.settings' \u2014 its wired contract on this\n * kernel \u2014 so the toggle takes that seat unconditionally; no DOM fallback\n * exists.\n *\n * @module dsh-model-think-level/client/injection/slider-toggle-slot\n */\n\nimport type { Translate } from '@deepseek-ai/dsh-client-ui-slots'\nimport { PLUGIN_ID } from '../../constants.js'\nimport { SliderToggle } from '../SliderToggle.js'\nimport type { ClientContext, SlotRegistrarFace } from '../types.js'\n\n/**\n * Take the official footer seat.\n * @param ctx - client root context (its `slots` service registers the seat).\n * @param t - the plugin's locale-bound translator.\n */\nexport function registerSliderToggleSlot(ctx: ClientContext, t: Translate): () => void {\n const host = ctx as unknown as { slots?: SlotRegistrarFace }\n // `register` hands back the remover for its seat; the `inject` seam is where\n // the official registrar binds it to the calling scope. Wire whatever comes\n // back so a plugin disable / HMR leaves no seat (and no stale component)\n // behind instead of dropping the disposer on the floor.\n const injected = host.slots?.inject('settings.models.footer', () => {\n host.slots?.register({\n name: 'settings.models.footer',\n id: PLUGIN_ID + '-slider-toggle',\n order: 15,\n inject: () => ({ t }),\n }, SliderToggle)\n })\n return typeof injected === 'function' ? injected as () => void : () => {}\n}\n"],
|
|
5
|
-
"mappings": "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AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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",
|
|
6
|
-
"names": ["index_exports", "__export", "EffortEditor", "apply", "baselineModelsOf", "compatOf", "createEditorApi", "defaultEffortOf", "describeNamespace", "effortsOf", "findModelMenu", "findModelTrigger", "inject", "inputOf", "name", "nameOf", "providerHeadersOf", "providersOf", "userProvidersOf", "writeModelRows", "writeProviderHeaders", "__toCommonJS", "import_react", "PLUGIN_ID", "PROBE_PATH", "PROVIDER_KEY_PATH", "KEY_INDEX_PATH", "AUTOFILL_CONFIG_PATH", "HEADERS_CONFIG_PATH", "PI_AI_NS", "UNSET_MARKER", "INPUT_UNSET_MARKER", "AUTOFILL_MARKER", "DEFAULT_EFFORT_FIELD", "STORE_NS", "PLUGIN_ID", "import_react", "LocaleRefresh", "props", "locale", "subscribe", "notify", "getSnapshot", "en", "zh", "SLIDER_PREF_KEY", "UPSTREAM_KEYS", "enabled", "read", "own", "migrated", "key", "value", "listeners", "notify", "listener", "sliderEnabled", "setSliderEnabled", "next", "syncSliderEnabled", "subscribeSliderEnabled", "PROVIDER_ORDER_KEY", "PINNED", "parseProviderOrder", "raw", "parsed", "dedupe", "value", "ids", "seen", "kept", "id", "order", "listeners", "notify", "listener", "sameOrder", "left", "right", "index", "providerOrder", "setProviderOrder", "next", "deduped", "syncProviderOrder", "subscribeProviderOrder", "baseProviderOrder", "rank", "pinned", "entry", "mergeProviderOrder", "base", "preferred", "slots", "named", "merged", "slot", "position", "resolveProviderOrder", "moveToIndex", "items", "item", "toIndex", "from", "target", "PROVIDER_DISABLED_KEY", "listeners", "disabled", "read", "parseProviderOrder", "parseDisabledProviders", "raw", "disabledProviders", "isProviderEnabled", "id", "setProviderEnabled", "enabled", "already", "apply", "candidate", "syncDisabledProviders", "next", "subscribeDisabledProviders", "listener", "sameSet", "left", "right", "persist", "MODEL_ORDER_KEY", "parseModelOrders", "raw", "parsed", "map", "provider", "value", "ids", "dedupe", "entry", "seen", "kept", "id", "orders", "listeners", "notify", "listener", "sameIds", "left", "right", "index", "persist", "modelOrders", "modelOrderOf", "EMPTY", "setModelOrder", "next", "current", "syncModelOrders", "subscribeModelOrders", "orderModels", "preferred", "mergeProviderOrder", "resolveModelOrder", "orderModelEntries", "entries", "byId", "STYLES", "import_react", "import_react_dom", "import_react", "import_react_dom", "THINKING_LEVELS", "INPUT_MODALITIES", "KNOWLEDGE_BASE", "GENERIC_OPENAI_EFFORTS", "PROTOCOL_INFERENCE", "GENERIC_FALLBACK", "normalize", "value", "isAlnum", "ch", "normalizeLoose", "onBoundary", "haystack", "at", "length", "before", "after", "matchKnowledgeBase", "modelId", "displayName", "best", "entry", "pattern", "needle", "NATIVE_DIALECT_DOMAINS", "inferProtocol", "route", "api", "baseURL", "parts", "dialect", "COMPAT_CAPABLE_PROTOCOL", "ANTHROPIC_PROTOCOL", "isSelfHostedEndpoint", "host", "sanitizeCompatForProtocol", "compat", "norm", "copy", "key", "compatForRoute", "OFFICIAL_RELAY_DOMAINS", "isSelfHostedRelay", "root", "withRolePin", "reasons", "ENDPOINT_CONFIRMED_LADDER", "VISION_NAME_TOKENS", "inferModalitiesFromName", "token", "declaredCoreInput", "raw", "lowered", "INPUT_MODALITIES", "suggestEfforts", "endpoint", "input", "inputSource", "contextWindow", "maxTokens", "listedInput", "guessed", "modalityParts", "endpointPart", "protocol", "efforts", "compatBase", "compatWithBudget", "EFFORT_MEMORY_KEY", "WIRED_MARKER", "isSelectRefusal", "outcome", "selectRefusalMessage", "error", "code", "message", "memoryKey", "provider", "model", "readMemory", "raw", "parsed", "memory", "key", "value", "writeMemory", "rememberEffort", "id", "rememberedEffort", "effortIdsOf", "state", "efforts", "candidate", "level", "advertisedDefaultOf", "displayedMiddleOf", "supported", "isModelSwitch", "current", "selection", "resolveFallback", "snapshot", "sessionSightings", "deps", "sighted", "configured", "fallback", "suggestEfforts", "advertised", "wireEffortMemory", "directory", "target", "hadOwnSelect", "original", "attemptedRestores", "restoreFailures", "RESTORE_RETRY_LIMIT", "utterances", "wrapped", "myUtterance", "result", "sliderEnabled", "fresh", "a", "b", "restoreProjection", "utteranceAtAttempt", "freshCurrent", "failures", "unsubscribe", "sliderLevels", "state", "efforts", "candidate", "currentModelOf", "effectiveEffortIndex", "levels", "model", "current", "level", "fallback", "at", "modelsOf", "group", "providerLabelOf", "named", "chevron", "open", "checkIcon", "VISION_CACHE", "supportsVision", "cached", "known", "matchKnowledgeBase", "verdict", "inferModalitiesFromName", "supportsThinking", "visionIcon", "label", "thinkingIcon", "modelTags", "badges", "ComposerSlider", "props", "directory", "t", "layoutChange", "notify", "effort", "setEffort", "panel", "setPanel", "openProvider", "setOpenProvider", "committing", "setCommitting", "localError", "setLocalError", "bodyRef", "committedRef", "committingRef", "available", "busy", "error", "select", "index", "next", "modelName", "modelLabel", "effortLabel", "preferredOrder", "subscribeProviderOrder", "providerOrder", "disabled", "subscribeDisabledProviders", "disabledProviders", "modelOrderByProvider", "subscribeModelOrders", "modelOrders", "providerCatalog", "providerIds", "mergeProviderOrder", "baseProviderOrder", "shownProvider", "shownModels", "orderModelEntries", "activeKey", "togglePanel", "collapsePanel", "focusMenu", "menu", "chooseModel", "provider", "id", "now", "outcome", "refusal", "selectRefusalMessage", "cause", "chooseEffort", "previous", "snapshot", "settled", "rows", "choice", "key", "active", "import_react", "GROUP_SELECTOR", "OPTION_SELECTOR", "isModelPane", "menu", "visibleOptions", "btn", "groupNameOf", "group", "labelledBy", "filterMenuModels", "query", "touched", "hide", "element", "show", "original", "tokens", "groups", "totalVisible", "groupName", "buttons", "visibleInGroup", "titleText", "contentText", "combined", "token", "ModelSearch", "setQuery", "setTotalVisible", "inputRef", "queryRef", "touchedRef", "runFilter", "nextQuery", "count", "observer", "onKeyDown", "event", "key", "options", "current", "option", "next", "handleKeyDown", "handleClear", "hasEmptyState", "e", "val", "import_react", "import_react_dom", "import_client", "panelRoot", "mountReact", "container", "children", "options", "root", "unmountReact", "mount", "EffortBoundary", "__publicField", "error", "info", "rePlaceInFrame", "createComposerMenu", "deps", "menuOf", "triggerOf", "t", "refreshed", "sliderEnabled", "directory", "sliderMount", "searchMount", "lastModelPane", "triggerDirectory", "triggerUnsubscribe", "decoratedTrigger", "clearTriggerDecoration", "updateTriggerDecoration", "trigger", "state", "current", "group", "candidate", "provider", "providerLabelOf", "syncTriggerDirectory", "next", "unmountSearch", "unmountReact", "reconcileSearch", "menu", "container", "referenceNode", "wrapper", "PLUGIN_ID", "mountReact", "EffortBoundary", "ModelSearch", "reconcileSliderInto", "el", "resolved", "ComposerSlider", "cells", "cell", "modelPane", "isModelPane", "isRecord", "value", "looksLikeCompatRefusal", "message", "msg", "modelsOf", "providers", "route", "profile", "routeFactsOf", "api", "baseURL", "displayName", "hasBoolean", "entry", "key", "findListingEntry", "listing", "modelId", "isRecord", "analyzeListingEntry", "features", "signalParts", "parameters", "effortField", "capabilities", "thinking", "effort", "parts", "rawList", "value", "members", "item", "member", "architecture", "input", "modalities", "capabilityArray", "visionIn", "list", "slot", "length", "nested", "maxInput", "limitContext", "detectModelSignal", "providersOf", "namespace", "providers", "profile", "providersOfLayer", "layer", "isRecord", "baselineModelsOf", "route", "userProvidersOf", "effortsOf", "models", "modelId", "entry", "model", "efforts", "inputOf", "input", "members", "member", "compatOf", "compat", "nameOf", "name", "defaultEffortOf", "effort", "DEFAULT_EFFORT_FIELD", "probeEndpoint", "response", "PROBE_PATH", "body", "detectModelSignal", "createEditorApi", "api", "describe", "describeNamespace", "stage", "hold", "withdraw", "stagedOf", "stagedFacts", "stored", "routeFactsOf", "facts", "endpoint", "suggestion", "suggestEfforts", "write", "defaultEffort", "rawEfforts", "clearCompatKeys", "result", "writeModelRows", "rows", "abort", "downgraded", "attempt", "join", "rawModels", "nextModels", "missing", "row", "index", "mergeRowIntent", "PI_AI_NS", "looksCompat", "looksLikeCompatRefusal", "error", "copy", "touch", "UNSET_MARKER", "AUTOFILL_MARKER", "merged", "key", "INPUT_UNSET_MARKER", "providerHeadersOf", "headers", "pairs", "value", "writeProviderHeaders", "routeIsDeclared", "options", "viaScope", "joinFromScope", "ns", "snapshot", "createConfiguredEfforts", "deps", "configuredEfforts", "configuredEffortsInflight", "configuredEffortsGeneration", "ensureConfiguredEfforts", "generation", "describeNamespace", "join", "map", "route", "profile", "providersOf", "rawModels", "model", "id", "effort", "DEFAULT_EFFORT_FIELD", "provider", "declaresInput", "model", "input", "buildAutofillPatch", "providers", "routeFilter", "options", "revision", "fillEfforts", "fillModalities", "isRecord", "patchRoutes", "route", "profile", "rawModels", "models", "nextModels", "changed", "effortsDeclared", "UNSET_MARKER", "inputDeclared", "INPUT_UNSET_MARKER", "backfilled", "backfillRolePin", "id", "routeInfo", "routeFactsOf", "name", "suggestion", "suggestEfforts", "fill", "touched", "AUTOFILL_MARKER", "stored", "backfillRolePin", "model", "providers", "route", "isRecord", "UNSET_MARKER", "compat", "base", "isSelfHostedRelay", "routeFactsOf", "createIdleAutofill", "deps", "autofillSwitches", "autofillSwitchesOf", "fallback", "response", "AUTOFILL_CONFIG_PATH", "body", "runIdleAutofill", "switches", "join", "describeNamespace", "namespace", "userProviders", "userProvidersOf", "patch", "buildAutofillPatch", "routes", "ops", "route", "profile", "PI_AI_NS", "error", "import_react", "import_client", "import_react", "LEVEL_ORDER", "THINKING_LEVELS", "draftFrom", "efforts", "draft", "level", "wire", "buildIntent", "off", "thinking", "out", "cell", "sameEfforts", "a", "b", "aKeys", "bKeys", "defaultWireRisk", "declared", "thinkingFormat", "AUTO_EFFORT_EVENT", "tail", "queueAutoEffort", "run", "requestAutoEffort", "route", "target", "listenAutoEffort", "handler", "listener", "event", "detail", "import_jsx_runtime", "COUNT", "modalityFrom", "input", "buildModalityIntent", "draft", "OWNED_COMPAT_KEYS", "ownedCompatKeys", "routeApi", "clearedCompatKeys", "owned", "set", "key", "compatClearIntent", "writeCompat", "sameModality", "stored", "ladderIntent", "nextThinking", "built", "initialEfforts", "next", "compatDraftOf", "compat", "out", "budgetField", "pruned", "maxOutput", "pendingWriteOf", "parts", "baseCompat", "manualCompat", "EffortEditor", "route", "routeBaseURL", "modelId", "modelName", "initialInput", "initialCompat", "initialDefaultEffort", "index", "staged", "officialInputTypes", "api", "readOnly", "t", "setDraft", "draftFrom", "modality", "setModality", "thinking", "setThinking", "defaultEffort", "setDefaultEffort", "busy", "setBusy", "message", "setMessage", "suggested", "setSuggested", "suggestedSource", "setSuggestedSource", "suggestedConfidence", "setSuggestedConfidence", "appliedCompatRef", "setBudgetField", "priorityText", "setPriorityText", "setMaxOutput", "previousCompat", "suggestedInputSource", "setSuggestedInputSource", "referenceContext", "setReferenceContext", "referenceMaxTokens", "setReferenceMaxTokens", "dirtyRef", "previousEfforts", "previousInput", "previousDefaultEffort", "autoAdaptRef", "savedEffortsRef", "listenAutoEffort", "target", "run", "queueAutoEffort", "sameEfforts", "compatDraft", "compatChanged", "draftOut", "armedLevels", "LEVEL_ORDER", "level", "defaultEffortIntent", "effectiveEfforts", "buildIntent", "changed", "lastCommitRef", "markDirty", "commitPending", "nextDraft", "nextModality", "nextDefaultEffort", "effortsIntent", "nextInput", "write", "signature", "patchLevel", "on", "current", "patchThinking", "patchWire", "wire", "patchImage", "clearModality", "patchBudgetField", "value", "patchPriority", "patchMaxOutput", "patchDefaultEffort", "applySuggestion", "nextDefault", "autoAdapt", "reply", "error", "reset", "wireFormat", "suggestEfforts", "wireRisk", "defaultWireRisk", "disabled", "PLUGIN_ID", "event", "cell", "SEAT_ATTR", "CATALOG_PROBE", "HEAD_PROBE", "LINK_PROBE", "SEAT_CLASS", "cataloguesOf", "scope", "candidate", "reconcileAutoEffortSeats", "root", "targets", "deps", "wanted", "target", "label", "hint", "catalogue", "route", "head", "seat", "createSeat", "anchor", "lastLink", "syncSeat", "seated", "teardownAutoEffortSeats", "links", "requestAutoEffort", "hasOwn", "object", "key", "LEDGER_DOCUMENT_KEY", "documentId", "holder", "LEDGER_KEY", "ledgerStorage", "persistLedger", "state", "store", "file", "route", "models", "ledgerEntry", "value", "validate", "routes", "row", "entries", "entry", "modelId", "write", "isRecord", "restoreLedger", "raw", "parsed", "pending", "queued", "isHeldWrite", "committed", "flushOnTeardown", "deps", "flushQueued", "flushPending", "error", "flushOnUnload", "createScanState", "runIdlePass", "queuedFailed", "pendingFailed", "armBackoff", "delay", "hasOutstanding", "stageEffortsInto", "efforts", "compat", "input", "defaultEffort", "queueWriteInto", "withdrawStaged", "withdrawHeldWrite", "withdrawIntent", "forgetRoute", "effectiveStagedIntents", "declaration", "current", "autofill", "ladderTaken", "UNSET_MARKER", "AUTOFILL_MARKER", "sameEffortsValue", "inputTaken", "INPUT_UNSET_MARKER", "sameInputList", "a", "b", "keys", "other", "set", "member", "clear", "flushRoute", "ignoreFence", "join", "providers", "providersOf", "intents", "modelsOf", "model", "routeInfo", "routeFactsOf", "name", "suggestion", "suggestEfforts", "effective", "seeded", "results", "writeModelRows", "officialCardOpen", "failed", "intent", "at", "result", "aborted", "inputValueByLabel", "card", "labels", "label", "candidate", "cardOf", "trigger", "disclosureOf", "candidates", "officialInputTypesOf", "container", "seatEmptyCatalog", "root", "officialCardOf", "button", "reconcileAutoEffortSeats", "emptyCatalogTargets", "adaptEveryModel", "targets", "catalogue", "cataloguesOf", "resolved", "routeOfCard", "autoEffortTargets", "found", "seen", "describe", "profile", "routeApi", "covered", "api", "createEditorApi", "heldRoute", "initialEfforts", "effortsOf", "initialInput", "inputOf", "initialCompat", "compatOf", "initialDefault", "defaultEffortOf", "reply", "nameOf", "parts", "nextThinking", "nextModality", "modalityFrom", "nextDefault", "pendingWriteOf", "ladderIntent", "buildIntent", "draftFrom", "sameModality", "buildModalityIntent", "compatDraftOf", "hasEditor", "PLUGIN_ID", "sameProps", "sameEfforts", "sameInput", "sameCompat", "setA", "item", "hasLabeledInput", "typed", "title", "byName", "actionsOf", "buttons", "matches", "node", "primary", "secondary", "submit", "cancel", "last", "previous", "wireOnce", "wired", "onHit", "panelRoot", "reconcile", "cardOpen", "hasCapacityRows", "wasEditing", "seatLabels", "run", "namespace", "aria", "triggers", "onPage", "target", "ids", "missing", "present", "stale", "id", "prior", "index", "routeStaged", "savedModelIds", "staged", "stagedEfforts", "typedName", "modelName", "typedApi", "typedBaseURL", "actions", "live", "routeBaseURL", "next", "r", "m", "e", "c", "i", "de", "w", "existing", "editor", "setAttr", "el", "name", "value", "setHidden", "hidden", "setClass", "on", "setText", "text", "import_react", "import_react", "normalizeKeyAlias", "value", "cleaned", "char", "code", "isLegalKeyValue", "messageOf", "body", "field", "value", "rowOf", "isRecord", "ref", "alias", "configured", "masked", "stateOf", "entries", "rows", "entry", "row", "enabledRef", "route", "answerOf", "response", "state", "error", "request", "payload", "url", "KEY_INDEX_PATH", "httpKeyManagerClient", "PROVIDER_KEY_PATH", "key", "import_jsx_runtime", "CHECK_PATH", "PLUS_PATHS", "Glyph", "paths", "d", "previewOf", "row", "nameOf", "KeyManager", "props", "route", "t", "client", "httpKeyManagerClient", "group", "rows", "setRows", "enabledRef", "setEnabledRef", "notice", "setNotice", "busy", "setBusy", "editing", "setEditing", "nameDraft", "setNameDraft", "valueDraft", "setValueDraft", "confirming", "setConfirming", "adopt", "answer", "okText", "live", "run", "action", "closeForm", "openForm", "kind", "submit", "ref", "isLegalKeyValue", "alias", "normalizeKeyAlias", "form", "wantsValue", "illegal", "event", "addEntry", "name", "KEY_MANAGED_CLASS", "KEY_HOST_CLASS", "KEY_HOST_ATTR", "KEY_INPUT_ATTR", "KEY_REGION_ATTR", "FIELDS", "keyInputOf", "field", "marked", "secret", "hostOf", "known", "next", "enhanceKeyManagerField", "deps", "route", "setClass", "host", "drop", "setAttr", "region", "subtree", "EffortBoundary", "KeyManager", "state", "mountReact", "unmountReact", "resetKeyManagerField", "teardownKeyManager", "SVG_NS", "ICON_ATTR", "svgPath", "d", "path", "svgGlyph", "kind", "paths", "svg", "KEY_FIELD_CLASS", "KEY_EYE_CLASS", "KEY_INPUT_ATTR", "SHOW_LABEL_ATTR", "HIDE_LABEL_ATTR", "EYE_PATHS", "DEPS", "keyInputOf", "field", "marked", "masked", "eyeOf", "child", "revealKeyField", "deps", "input", "resetKeyField", "setAttr", "setClass", "eye", "createEye", "syncEye", "loadProviderKey", "route", "response", "PROVIDER_KEY_PATH", "body", "revealed", "label", "setHidden", "writeInputValue", "value", "descriptor", "svgIcon", "borrowed", "toggle", "key", "failed", "event", "kind", "paths", "svgGlyph", "TABS_CLASS", "TAB_BUTTON_CLASS", "TAB_ID_ATTR", "TABBED_CLASS", "TAB_ATTR", "PROVIDER_ROUTE_ATTR", "REGION_ATTR", "BODY_ATTR", "DETAILS_MEMO_ATTR", "HEADERS_SECTION_SELECTOR", "TAB_PROVIDER", "TAB_MODELS", "TAB_ADVANCED", "TAB_IDS", "REGION_NONE", "EDITOR_TAB_IDS", "EDITOR_TABS_CLASS", "TABS_CLASS", "EDITOR_TAB_BUTTON_CLASS", "TAB_BUTTON_CLASS", "EDITOR_TAB_ID_ATTR", "TAB_ID_ATTR", "EDITOR_PROBE", "MODELS_TERMINAL", "CARD_PROBE", "createEditorTabsState", "editorCardOf", "element", "selectEditorTab", "card", "id", "setAttr", "TAB_ATTR", "syncSelected", "reconcileEditorTabs", "root", "deps", "state", "open", "probe", "TABBED_CLASS", "cleanupCard", "applyTabs", "error", "teardownEditorTabs", "BODY_ATTR", "el", "REGION_ATTR", "group", "DETAILS_MEMO_ATTR", "field", "KEY_FIELD_CLASS", "resetKeyField", "KEY_MANAGED_CLASS", "resetKeyManagerField", "teardownKeyManager", "editorBodyOf", "child", "node", "classifyBody", "body", "regions", "classifyChildren", "actions", "fields", "header", "models", "CLASSIFY_MAX_DEPTH", "container", "depth", "providerRouteOf", "route", "PROVIDER_ROUTE_ATTR", "headersSection", "revealKeyField", "enhanceKeyManagerField", "forceGroupsOpen", "available", "active", "setClass", "syncTabBar", "bar", "setText", "tabOf", "setHidden", "target", "tab", "restoreGroups", "HANDLE_ATTR", "HEAD_ATTR", "DROP_SIDE_ATTR", "DRAGGING_CLASS", "DROP_CLASS", "GHOST_CLASS", "LINE_CLASS", "SVG_NS", "ROW_GAP", "EDGE_PX", "SCROLL_STEP", "MOVE_TRANSITION", "INTERACTIVE", "createDragState", "rowElements", "parent", "child", "orderOf", "row", "index", "raw", "parsed", "visualRows", "list", "left", "right", "entry", "placeRows", "sequence", "current", "named", "slots", "ordered", "target", "slot", "position", "value", "slideIntoPlace", "mutate", "rows", "before", "delta", "orderedIds", "idOf", "id", "applyIdOrder", "ids", "rank", "leftRank", "rightRank", "clearRowOrder", "dropIndex", "carried", "hovered", "after", "at", "nearestRow", "clientY", "best", "bestDistance", "rect", "distance", "isAfter", "dropLineTop", "gripIcon", "svg", "dots", "cx", "cy", "dot", "makeGhost", "label", "ghost", "name", "printed", "interactiveTarget", "listOf", "clearDropMarks", "scrollHostOf", "node", "scrolls", "root", "declared", "overflow", "edgeScrollStep", "host", "setScrollStep", "state", "session", "step", "tick", "dropLine", "existing", "line", "showDropLine", "hideDropLine", "finishDrag", "startDrag", "event", "adapter", "dataTransfer", "dragOver", "over", "moveToIndex", "wireList", "wireSource", "source", "wireGrip", "grip", "syncGrip", "hint", "ensureGrip", "seat", "reconcileRows", "teardownDrags", "head", "ROW_ID_ATTR", "PROVIDER_TOKEN", "ROW_CLASS_PROBES", "createProviderOrderState", "createDragState", "rowsListOf", "root", "probe", "hit", "parent", "rowElements", "rowHeadOf", "row", "rowNameOf", "name", "templateParts", "template", "at", "idFromTargetLabel", "label", "trimmed", "parenthesized", "route", "CARD_ROOT_PROBE", "isForeign", "node", "card", "el", "providerIdOfRow", "labels", "stamped", "parts", "button", "id", "rowIdOf", "value", "orderedRowIds", "list", "orderedIds", "reconcileProviderOrder", "root", "deps", "state", "list", "rowsListOf", "labels", "row", "rowElements", "id", "providerIdOfRow", "ROW_ID_ATTR", "rowIdOf", "reconcileRows", "providerAdapter", "node", "rowNameOf", "ids", "resolveProviderOrder", "setProviderOrder", "orderedRowIds", "rowHeadOf", "teardownProviderOrder", "teardownDrags", "stamped", "clearRowOrder", "SWITCH_ATTR", "OFF_CLASS", "createProviderToggleState", "reconcileProviderEnabled", "root", "deps", "state", "list", "rowsListOf", "labels", "child", "id", "providerIdOfRow", "head", "rowHeadOf", "ensureSwitch", "isProviderEnabled", "teardownProviderEnabled", "switchButton", "row", "existing", "syncSwitch", "control", "wireSwitch", "actions", "label", "rowNameOf", "hint", "checked", "event", "next", "setProviderEnabled", "SEAT_ATTR", "ACTIONS_PROBE", "OFFICIAL_DELETE_PROBE", "SEAT_CLASS", "reconcileProviderDeleteSeat", "root", "deps", "list", "rowsListOf", "labels", "label", "officialDeleteLabel", "child", "id", "providerIdOfRow", "head", "rowHeadOf", "actions", "existing", "seat", "createSeat", "syncSeat", "rowNameOf", "teardownProviderDeleteSeats", "text", "hint", "ROW_ID_ATTR", "MODEL_ROW_CLASS", "createModelOrderState", "createDragState", "modelListsOf", "root", "lists", "hit", "rowElements", "child", "modelIdOf", "entry", "value", "providerOfList", "list", "deps", "row", "providerIdOfRow", "modelLabelOf", "name", "inputValueByLabel", "modelAdapter", "node", "ids", "provider", "resolveModelOrder", "target", "setModelOrder", "orderedIds", "id", "_entry", "seat", "reconcileModelOrder", "state", "labels", "reconcileRows", "teardownModelOrder", "teardownDrags", "stamped", "clearRowOrder", "HOST_MODELS_NS", "HOST_LABEL_KEYS", "createModelsPage", "deps", "ctx", "api", "t", "onIdle", "refreshed", "labels", "translate", "resolve", "key", "fallbacks", "value", "SCAN_DEBOUNCE_MS", "scanState", "createScanState", "providerOrderState", "createProviderOrderState", "providerOrderDeps", "providerToggleState", "createProviderToggleState", "providerToggleDeps", "providerDeleteDeps", "modelOrderState", "createModelOrderState", "modelOrderDeps", "editorTabsState", "createEditorTabsState", "editorTabsDeps", "scanTimer", "retryTimer", "observer", "composerMutation", "stopped", "injectorDeps", "describeNamespace", "delayMs", "scheduleRetry", "container", "props", "rootEl", "PLUGIN_ID", "reactRoot", "renderEditor", "p", "EffortBoundary", "EffortEditor", "schedule", "reconcile", "panelRoot", "reconcileEditorTabs", "reconcileProviderOrder", "reconcileProviderEnabled", "reconcileProviderDeleteSeat", "reconcileModelOrder", "start", "onComposerMutation", "stopObserver", "flushOnTeardown", "entry", "teardownProviderOrder", "teardownProviderEnabled", "teardownProviderDeleteSeats", "teardownAutoEffortSeats", "teardownModelOrder", "teardownEditorTabs", "flushOnUnload", "currentSessionId", "sessions", "uiSession", "snapshot", "direct", "bound", "key", "row", "createSessionDirectoryTracker", "deps", "current", "wiredDirectories", "sweepDeadWiretaps", "directories", "liveDirectory", "wired", "entry", "alive", "host", "sessionId", "directory", "wireEffortMemory", "MODEL_SEAT_SLOT", "composerCardOf", "doc", "seatAnchorOf", "card", "seatTriggerCandidates", "scope", "linkedMenuOf", "trigger", "id", "menu", "findModelMenu", "findModelTrigger", "anchor", "candidates", "seat", "modelMenuOf", "modelTriggerOf", "import_react", "import_react", "import_jsx_runtime", "USER_AGENT_PRESETS", "draftFrom", "headers", "id", "rows", "name", "value", "headersFromDraft", "draft", "out", "hasUserAgent", "row", "legacyUserAgent", "HeadersEditor", "route", "api", "t", "uaListId", "stored", "setStored", "setDraft", "open", "setOpen", "editing", "setEditing", "loading", "setLoading", "busy", "setBusy", "readOnly", "setReadOnly", "message", "setMessage", "environment", "setEnvironment", "tabbed", "setTabbed", "nextRowId", "rootRef", "live", "join", "describeNamespace", "providerHeadersOf", "body", "HEADERS_CONFIG_PATH", "root", "sync", "observer", "addRow", "current", "patchRow", "patch", "removeRow", "cancel", "save", "next", "reply", "writeProviderHeaders", "error", "disabled", "count", "inline", "PLUGIN_ID", "item", "event", "preset", "import_react", "LABEL_KEYS", "EditorTabBar", "host", "EDITOR_TABS_CLASS", "EDITOR_TAB_IDS", "id", "EDITOR_TAB_BUTTON_CLASS", "EDITOR_TAB_ID_ATTR", "event", "card", "editorCardOf", "selectEditorTab", "SLOT", "ProviderCardSlot", "provider", "api", "t", "host", "wrapper", "card", "editorCardOf", "observed", "openEditor", "sync", "edit", "observer", "route", "EditorTabBar", "HeadersEditor", "registerProviderCardSlot", "ctx", "injected", "PI_AI_NS", "import_react", "SliderToggle", "props", "enabled", "subscribeSliderEnabled", "sliderEnabled", "toggle", "setSliderEnabled", "registerSliderToggleSlot", "ctx", "host", "injected", "PLUGIN_ID", "SliderToggle", "name", "PLUGIN_ID", "inject", "apply", "ctx", "STORE_NS", "zh", "en", "style", "STYLES", "settingsScope", "forms", "current", "PI_AI_NS", "settingsApi", "t", "localeFace", "locale", "refreshed", "children", "face", "LocaleRefresh", "configuredEfforts", "createConfiguredEfforts", "sessionDirectory", "createSessionDirectoryTracker", "composerMenu", "createComposerMenu", "modelMenuOf", "modelTriggerOf", "sliderEnabled", "autofill", "createIdleAutofill", "modelsPage", "createModelsPage", "onComposerMutation", "onPageHide", "refresh", "disposers", "ns", "dispose", "onStorage", "event", "SLIDER_PREF_KEY", "syncSliderEnabled", "PROVIDER_ORDER_KEY", "syncProviderOrder", "parseProviderOrder", "PROVIDER_DISABLED_KEY", "syncDisabledProviders", "parseDisabledProviders", "MODEL_ORDER_KEY", "syncModelOrders", "parseModelOrders", "subscribeSliderEnabled", "disposeOrder", "subscribeProviderOrder", "disposeEnabled", "subscribeDisabledProviders", "disposeModelOrder", "subscribeModelOrders", "registerSliderToggleSlot", "registerProviderCardSlot"]
|
|
4
|
+
"sourcesContent": ["/**\n * Browser half of dsh-model-think-level.\n *\n * This is the ASSEMBLY LAYER: it registers the copy dictionaries and the\n * stylesheet, builds the injection modules with their dependencies, and wires\n * them to the plugin fiber's lifecycle. The behaviour itself lives in\n * `src/client/injection/`, one module per seam:\n *\n * models-page.ts the Models-page DOM injection (observer + editors)\n * models-page-editor.ts the editor injector behind it (anchors, staging, flush)\n * composer-menu.ts the composer slider AND the model search box\n * session-directory.ts the per-session directory + effort-memory wiretaps\n * configured-efforts.ts the settings document's per-model `defaultEffort` cache\n * slider-toggle-slot.ts the Models-page footer toggle\n * provider-order-drag.ts the Models-page provider-row drag handles (order)\n * provider-order.ts the stored provider order (settings page + composer)\n * provider-toggle.ts the Models-page per-provider enable switch\n * provider-enabled.ts the stored disabled providers (settings page + composer)\n * autofill-run.ts the running auto-fill complement (issue #7)\n * mount.tsx foreign React roots and the render boundary\n * model-menu.ts locating the official composer model menu\n *\n * Contributions, all of which dispose with the plugin fiber:\n * 1. The DOM bypass injector: a MutationObserver over the whole document\n * keeps the official Models page's model rows equipped with the\n * thinking-effort editor, wherever the page lives in the settings\n * surface (a panel, a dialog, a portal). This is the SINGLE path:\n * the official per-model disclosure keeps the same anchors (the\n * `modelAdvanced` dictionary value \u2014 Capacities / \u5BB9\u91CF through\n * 0.1.6-alpha.1, Model options / \u6A21\u578B\u9009\u9879 from 0.1.6-alpha.2), so the\n * editor lives under each model row rather than on the provider card.\n * 2. The composer reasoning-effort slider AND the model search box, mounted\n * inside the OFFICIAL model menu opened from the bottom-right seat. The\n * seat's trigger is never touched \u2014 the official \"model \u00B7 effort\" display\n * stays. The search box is unconditional: it does not follow the slider\n * preference.\n * 3. The Models-page slider toggle, taking the official\n * 'settings.models.footer' slot unconditionally at apply.\n * 4. The stylesheet and copy dictionaries.\n *\n * @module dsh-model-think-level/client\n */\n\nimport type {\n ClientContext, ConfigFormsLike, RemoteApi, SettingsScopeBinderLike, SettingsScopeReadLike,\n} from './types.js'\n// Type-only: pulls the shell's locale/remote context merges into this program.\nimport type {} from '@deepseek-ai/dsh-client-ui-settings/client'\nimport type {} from '@deepseek-ai/dsh-client-locale/client'\nimport type {} from '@deepseek-ai/dsh-api-remotes/client'\nimport type { Translate } from '@deepseek-ai/dsh-client-ui-slots'\nimport { createElement } from 'react'\nimport type { ReactNode } from 'react'\nimport { PI_AI_NS, PLUGIN_ID, STORE_NS } from '../constants.js'\nimport { LocaleRefresh, type LocaleFace } from './LocaleRefresh.tsx'\nimport { en, zh, type BreKey } from './locales.ts'\nimport { SLIDER_PREF_KEY, sliderEnabled, subscribeSliderEnabled, syncSliderEnabled } from './slider-pref.js'\nimport {\n PROVIDER_DISABLED_KEY, parseDisabledProviders, subscribeDisabledProviders, syncDisabledProviders,\n} from './provider-enabled.js'\nimport {\n PROVIDER_ORDER_KEY, parseProviderOrder, subscribeProviderOrder, syncProviderOrder,\n} from './provider-order.js'\nimport {\n MODEL_ORDER_KEY, parseModelOrders, subscribeModelOrders, syncModelOrders,\n} from './model-order.js'\nimport { STYLES } from './styles.ts'\nimport { createComposerMenu } from './injection/composer-menu.js'\nimport { createConfiguredEfforts } from './injection/configured-efforts.js'\nimport { createIdleAutofill } from './injection/autofill-run.js'\nimport { createModelsPage } from './injection/models-page.js'\nimport { createSessionDirectoryTracker } from './injection/session-directory.js'\nimport { modelMenuOf, modelTriggerOf } from './injection/model-menu.js'\nimport { registerProviderCardSlot } from './injection/provider-card-slot.js'\nimport { registerSliderToggleSlot } from './injection/slider-toggle-slot.js'\n\n/** Stable plugin id, matching the cordis.patch.yml row and the bundle id. */\nexport const name = PLUGIN_ID\n\n/** Cordis fiber dependencies of the browser half. */\nexport const inject = ['slots', 'locale', 'connection', 'remote', 'remote.settings']\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n /** This plugin's copy dictionaries. */\n [STORE_NS]: BreKey\n }\n}\n\n/**\n * Apply the browser half.\n * @param ctx - client root context.\n */\nexport function apply(ctx: ClientContext): void {\n ctx.effect(() => ctx.locale.register(STORE_NS, { zh, en }), 'dsh-model-think-level: dictionaries')\n\n const style = document.createElement('style')\n // `data-plugin` is the host's own ownership marker, not decoration. The\n // client module system claims EVERY untagged `<style>` for whichever plugin\n // happens to materialize next (claimStyles: querySelectorAll of\n // 'style:not([data-plugin])' -> setAttribute('data-plugin', id)) and later\n // deletes its claimed tags by that id (removeOwnedStyles). A stylesheet left\n // untagged is therefore stolen by an unrelated plugin and removed when THAT\n // plugin reloads, while our injected DOM stays mounted: the rows keep\n // rendering and every rule stops applying (default buttons, no flex, no\n // card). Tagging the sheet here keeps it ours, so only our own reload cycles\n // touch it.\n style.dataset['plugin'] = PLUGIN_ID\n style.dataset['pluginStyles'] = PLUGIN_ID\n style.textContent = STYLES\n document.head.appendChild(style)\n ctx.effect(() => () => style.remove(), 'dsh-model-think-level: stylesheet')\n\n // The kernel mounts the settings Remote as an injectable\n // 'remote.settings' service, declared in the plugin's own inject above \u2014 so\n // the face is available before apply runs. No runtime seat probing remains.\n //\n // The official settings form is picked up on top of it when this shell\n // provides one: reads then ride its shared describe mirror (no wire round\n // trip, and the revision the settings surface itself fences writes with).\n // Prefer 0.1.7's `configForms.get(entryId)` and retain the 0.1.5/0.1.6\n // `settingsScope.bind({ namespace })` face for backward compatibility.\n // Deliberately NOT part of the plugin's `inject`: an older kernel without\n // either service must still activate on the wire-describe path.\n const settingsScope = ((): SettingsScopeReadLike | undefined => {\n try {\n const forms = ctx.get?.('configForms') as ConfigFormsLike | undefined\n try {\n const current = forms?.get?.(PI_AI_NS)\n if (current !== undefined) return current\n } catch {\n // A broken new service must not disable the legacy one.\n }\n // `ctx.get('settingsScope')` yields the kernel's BINDER, not a scope:\n // only `bind({ namespace })` mints the read face (getSnapshot). Using\n // the binder directly made every describe throw a TypeError.\n const binder = ctx.get?.('settingsScope') as SettingsScopeBinderLike | undefined\n return binder?.bind?.({ namespace: PI_AI_NS })\n } catch {\n return undefined\n }\n })()\n const settingsApi: RemoteApi = settingsScope === undefined\n ? { settings: ctx.remote.settings }\n : { settings: ctx.remote.settings, scope: settingsScope }\n // The shell's Translate is `(key: string, params?: Record<string, unknown>)`;\n // our components take a string-keyed face, so the bound translator narrows.\n const t = ctx.locale.bind(STORE_NS) as Translate\n\n // The locale service is also its own LocaleFace (getSnapshot/subscribe).\n // Without those two members a language switch cannot reach copy\n // that is already on screen \u2014 the pre-existing behaviour, not a failure.\n const localeFace = (): LocaleFace | undefined => {\n const locale = ctx.locale\n return typeof locale.subscribe === 'function' && typeof locale.getSnapshot === 'function'\n ? locale as LocaleFace\n : undefined\n }\n\n /**\n * Wrap a subtree so it re-translates on a language switch. The thunk is\n * rebuilt per render by {@link LocaleRefresh}; without the seat the subtree\n * renders exactly as it did before.\n */\n const refreshed = (children: () => ReactNode): ReactNode => {\n const face = localeFace()\n return face === undefined ? children() : createElement(LocaleRefresh, { locale: face, children })\n }\n\n // ---- The five injection seams ----\n // Each owns its own state now; see the module list in this file's header.\n const configuredEfforts = createConfiguredEfforts({ api: settingsApi })\n const sessionDirectory = createSessionDirectoryTracker({\n ctx,\n configuredEffort: configuredEfforts.of,\n })\n const composerMenu = createComposerMenu({\n menuOf: modelMenuOf,\n triggerOf: modelTriggerOf,\n t,\n refreshed,\n sliderEnabled,\n directory: sessionDirectory.ensure,\n })\n // The running auto-fill complement (issue #7): the host fills once at boot,\n // everything a session adds afterwards is filled on the idle pass.\n const autofill = createIdleAutofill({ api: settingsApi })\n const modelsPage = createModelsPage({\n ctx, api: settingsApi, t, onIdle: autofill.run, refreshed,\n })\n\n /**\n * The composer path, run on every mutation burst and on every debounced\n * scan. It LEADS both for two reasons: the slider/search body must land\n * before the first paint, and a session switch must have its directory\n * watched before the new session's projection can read as Default.\n */\n const onComposerMutation = (): void => {\n sessionDirectory.ensure()\n composerMenu.reconcile()\n }\n\n ctx.effect(() => {\n modelsPage.start(onComposerMutation)\n return () => {\n // Land what the session held back, unmount the editors, stop observing.\n modelsPage.teardown()\n // Orphaned foreign roots must not outlive the fiber: on plugin disable\n // or HMR they would keep rendering with a stale face.\n composerMenu.dispose()\n // Restore every wrapped directory's original select (the instances\n // outlive the fiber on disable/HMR and must submit officially again).\n sessionDirectory.dispose()\n }\n }, 'dsh-model-think-level: DOM injector')\n\n // A page unload is the last moment the committed ledgers can be landed, and\n // the moment the rest must be DISCARDED: a reload drops the official card's\n // own draft, so the plugin's uncommitted edits go with it. (The dispose\n // effect above keeps the ledger for a same-document fiber cycle, where the\n // card survives.)\n ctx.effect(() => {\n const onPageHide = (): void => { modelsPage.flushOnUnload() }\n window.addEventListener('pagehide', onPageHide)\n return () => { window.removeEventListener('pagehide', onPageHide) }\n }, 'dsh-model-think-level: ledger flush on unload')\n\n // Refresh the injection when the settings document changes (an apply from\n // either the official page or this plugin re-renders the rows). The folded\n // describe snapshot is invalidated first: without that, every later scan \u2014\n // and every editor mounted from it \u2014 would keep reading the revision and\n // providers as of the very first scan, making Apply conflict forever until\n // a full page reload.\n ctx.effect(() => {\n const refresh = (): void => {\n modelsPage.state.describePromise = undefined\n // The configured-pick cache rides the same invalidations: a document\n // update (or a fresh connection) must not leave stale picks behind.\n configuredEfforts.invalidate()\n modelsPage.schedule()\n }\n const disposers = [\n ctx.remote.$on('settings/document-updated', (ns: unknown) => {\n if (ns === PI_AI_NS) refresh()\n }),\n ctx.on('connection/reset', refresh),\n ]\n return () => { for (const dispose of disposers) dispose() }\n }, 'dsh-model-think-level: pushed invalidations')\n\n // Refresh the composer the moment a preference flips (the toggle and the\n // menu can be open at the same time), and follow other tabs' changes. The\n // provider order joins in: its rows live on the settings page, so a change\n // has to reach a scan \u2014 and a composer that is already open \u2014 without a\n // reload. So does the enable switch, for the same reason in reverse: the\n // switch is on the settings page, and it decides what the switcher lists.\n ctx.effect(() => {\n const onStorage = (event: StorageEvent): void => {\n if (event.key === SLIDER_PREF_KEY) syncSliderEnabled(event.newValue !== 'false')\n else if (event.key === PROVIDER_ORDER_KEY) syncProviderOrder(parseProviderOrder(event.newValue))\n else if (event.key === PROVIDER_DISABLED_KEY) {\n syncDisabledProviders(parseDisabledProviders(event.newValue))\n }\n else if (event.key === MODEL_ORDER_KEY) syncModelOrders(parseModelOrders(event.newValue))\n }\n window.addEventListener('storage', onStorage)\n const dispose = subscribeSliderEnabled(() => { modelsPage.schedule() })\n const disposeOrder = subscribeProviderOrder(() => { modelsPage.schedule() })\n const disposeEnabled = subscribeDisabledProviders(() => { modelsPage.schedule() })\n // The per-provider model order rides the same scan: a drag inside an open\n // editor has to leave the grips where they are, which takes a rescan.\n const disposeModelOrder = subscribeModelOrders(() => { modelsPage.schedule() })\n return () => {\n window.removeEventListener('storage', onStorage)\n void dispose()\n void disposeOrder()\n void disposeEnabled()\n void disposeModelOrder()\n }\n }, 'dsh-model-think-level: client preferences')\n\n ctx.effect(() => registerSliderToggleSlot(ctx, t), 'dsh-model-think-level: footer slot activation')\n\n // The request-header editor (issue #12) takes the OFFICIAL provider-card seat:\n // keyed by the adapter family's settings namespace, it receives every\n // llm-pi-ai card \u2014 shipped, added, and hand-declared alike \u2014 with no DOM\n // anchor to maintain.\n ctx.effect(\n () => registerProviderCardSlot(ctx, settingsApi, t),\n 'dsh-model-think-level: provider-card slot activation',\n )\n}\n\nexport type { BreKey }\nexport { EffortEditor } from './EffortEditor.tsx'\nexport type { EffortEditorProps, EffortModel } from './EffortEditor.tsx'\nexport { findModelMenu, findModelTrigger } from './injection/model-menu.js'\nexport type { InjectorDeps, EditorMountProps, ScanState } from './injection/models-page-editor.js'\nexport * from './ops.ts'\nexport * from './types.ts'\n", "/**\n * Plugin-wide constants shared by the host and browser halves.\n *\n * @module dsh-model-think-level/constants\n */\n\n/** Stable plugin id, matching the cordis.patch.yml row and the bundle id. */\nexport const PLUGIN_ID = 'dsh-model-think-level'\n\n/** Same-origin host route that proxies a provider's RAW /models listing. */\nexport const PROBE_PATH = '/dsh-model-think-level/raw-models'\n/** Read-only diagnostics for the request-header takeover. */\nexport const DIAG_PATH = '/dsh-model-think-level/header-bridge'\n\n/**\n * Same-origin host route that resolves one provider profile's stored\n * credential, so the API-key field's eye can show a SAVED key.\n *\n * The harness keeps provider keys write-only on purpose: the settings document\n * carries only the profile's `apiKeyEnv` reference, and the credential service\n * reports `{ configured, source, writable }` through `describe()` \u2014 never the\n * value. Only the host can read it (`credentials.resolve()`), and it does so\n * for the request adapters; the browser normally never sees a stored key at\n * all. This route is the plugin's deliberate exception: an explicit,\n * user-initiated, same-fence read that answers with the value and is never\n * cached, so the eye can put it back into the official field.\n */\nexport const PROVIDER_KEY_PATH = '/dsh-model-think-level/provider-key'\n\n/**\n * Same-origin host route that owns a provider's list of API keys: their\n * references, their labels, and which one is in use.\n *\n * The harness has no notion of \"several keys for one provider\": a profile names\n * exactly one reference (`apiKeyEnv`) and a credential store holds exactly one\n * value per reference, with no enumeration and no label. Both facts are\n * deliberate \u2014 a configuration surface learns which references exist from its\n * settings schema \u2014 so a list of keys can only be plugin-owned state. It lives\n * in the harness home (`<DSH_HOME>/dsh-model-think-level/key-index.json`) rather\n * than in the pi-ai profile, so the official add-provider card, which writes the\n * WHOLE profile at `providers.<route>`, cannot drop it.\n *\n * The route both reads and writes: the values themselves still go to the\n * credential store through the host's own `credentials` service, so the browser\n * never needs a new Remote face.\n */\nexport const KEY_INDEX_PATH = '/dsh-model-think-level/key-index'\n\n/**\n * Same-origin host route that reports this plugin's autofill switches. The\n * browser half runs the running auto-fill complement, and a `dsh.client`\n * declaration carries no plugin config, so the two switches have to be read\n * from the host that owns the cordis row; without it a deployment that turned\n * `autofill: false` would still be written to from the page.\n */\nexport const AUTOFILL_CONFIG_PATH = '/dsh-model-think-level/autofill-config'\n\n/**\n * Same-origin host route reporting the request-header overlay (issue #12): the\n * routes whose `user-agent` the plugin is overriding, and what it found of the\n * plugins that rewrite the same surface. The browser half renders the\n * coexistence warning from here \u2014 the takeover lives host-side, so the page\n * cannot observe it on its own.\n */\nexport const HEADERS_CONFIG_PATH = '/dsh-model-think-level/headers-config'\n\n/** The settings namespace this plugin edits: pi-ai custom provider routes. */\nexport const PI_AI_NS = 'llm-pi-ai'\n\n/**\n * Model-level marker written when the user deliberately unsets the\n * declaration (\"back to inheritance\"). Schemastery passes unknown model keys\n * through, so the marker survives official-page saves and restarts \u2014 which is\n * the point: auto-fill must respect the absence as a decision, not as a gap\n * to fill.\n */\nexport const UNSET_MARKER = 'reasoningEffortsUnset'\n\n/**\n * Model-level marker for a deliberately unset input-modality declaration,\n * mirroring {@link UNSET_MARKER}: auto-fill must respect the absence of\n * `input` as a decision (the route default applies), not as a gap to fill.\n */\nexport const INPUT_UNSET_MARKER = 'inputUnset'\n\n/**\n * Model-level provenance marker written beside every ladder the HOST autofill\n * fills from the knowledge base, carrying the settings revision the fill read.\n *\n * It exists because the plugin's auto-fill and the user's staged declaration\n * race on the SAME row: whichever half runs the fill writes the knowledge base\n * suggestion, while the staged-declaration flush compares the document against\n * its own suggestion. By the time the flush looks, the suggestion may already\n * be stored -- and byte equality alone cannot tell \"the plugin wrote this\" from\n * \"the user declared this\", so a staged ladder differing from the knowledge\n * base in even one spelling (the editor's `off: null` against the knowledge\n * base's `off: 'none'`) was dropped as a document takeover: the reported\n * \"configured it on the add-provider card, saved, and it is gone\".\n *\n * With the marker the flush knows the stored ladder is a suggestion, so a\n * staged user intent overrides it. Either way the user's bytes win; the marker\n * only decides whether the flush is allowed to write at all. Schemastery passes\n * unknown model keys through, so the marker survives official-page saves and\n * restarts like the other two markers. Any ladder written through the plugin\n * clears it, which is what makes a later hand edit outrank a fresh staging.\n */\nexport const AUTOFILL_MARKER = 'reasoningEffortsAutofilled'\n\n/**\n * Model-level default-effort pick (issue #4): the level a brand-new session\n * starts this model at, outranking the remembered levels. The value is one of\n * the model's own declared ladder keys, or the field is absent when the user\n * has not picked one (memory chain applies). Deliberately NOT a harness\n * concept: pi-ai's runtime never sees it (unknown model keys ride the settings\n * document the way the other markers do), and the browser half is its only\n * consumer. The name matches the catalog's `reasoning.defaultEffort` spelling\n * so a future first-class field can adopt the stored values as-is.\n */\nexport const DEFAULT_EFFORT_FIELD = 'defaultEffort'\n\n/** Locale dictionary namespace for the browser half's copy (not a settings namespace). */\nexport const STORE_NS = PLUGIN_ID\n", "/**\n * Re-translates a mounted subtree when the shell's active language changes.\n *\n * `ctx.locale.bind()` hands out ONE stable translate function per namespace\n * (memo-safety by design, so the reference can ride inject surfaces), which\n * leaves a mounted subtree with no signal that the copy it rendered is stale:\n * the function is the same object, and nothing re-renders to call it again.\n * The shell's own outlets do not have this problem \u2014 the slot renderer\n * re-derives their `t` from (namespace, revision), so a language switch hands\n * them a NEW function. Our editors mount through `createRoot` and sit outside\n * that machinery entirely, so they need the revision subscription spelled out.\n *\n * `children` is a thunk on purpose: returning a cached element would let React\n * bail out of the subtree (an unchanged element reference short-circuits the\n * re-render), so it has to be rebuilt on every render.\n *\n * @module dsh-model-think-level/client/LocaleRefresh\n */\nimport { useCallback, useSyncExternalStore } from 'react'\nimport type { ReactNode } from 'react'\n\n/** The slice of the shell's locale service this component consumes. */\nexport interface LocaleFace {\n /** Notified on every snapshot change (language switch or registration). */\n subscribe(fn: () => void): () => void\n /** Current snapshot; `revision` moves on every change. */\n getSnapshot(): { revision?: number }\n}\n\nexport interface LocaleRefreshProps {\n /** The shell's locale service, acting as its own LocaleFace. */\n locale: LocaleFace\n /** Rebuilt on every render \u2014 see the module docblock. */\n children: () => ReactNode\n}\n\n/** Re-render {@link LocaleRefreshProps.children} on every locale revision bump. */\nexport function LocaleRefresh(props: LocaleRefreshProps): ReactNode {\n const { locale } = props\n // Stable identities: the shell's locale object never changes identity, so\n // these two stay put across renders and uSES never churns subscriptions.\n const subscribe = useCallback((notify: () => void) => locale.subscribe(notify), [locale])\n const getSnapshot = useCallback(() => locale.getSnapshot().revision ?? 0, [locale])\n useSyncExternalStore(subscribe, getSnapshot)\n return props.children()\n}\n", "/** Copy dictionaries for the thinking-effort + input-modality editor. */\n\n/** English strings (the key-set source of truth). */\nexport const en = {\n reasoningEffort: 'Reasoning effort',\n thinkingMode: 'Thinking',\n thinkingModeOff: 'Off \u2014 this model is declared as not reasoning.',\n autoAdapt: 'Auto-adapt',\n autoAdaptAll: 'Auto-detect thinking levels',\n autoAdaptWorking: 'Detecting\u2026',\n autoAdaptDone: 'Adapted {count} \u00B7 save to write',\n autoAdaptEmpty: 'All already configured',\n autoAdaptUnsuggested: 'No suggestion available',\n autoAdaptBlocked: 'No models to adapt yet',\n autoAdaptFailed: 'Detection failed: {message}',\n autoAdaptDoneHint: 'Held for {count} model(s): they land when this card is saved.',\n autoAdaptEmptyHint: 'Every model of this provider already declares its thinking levels.',\n autoAdaptUnsuggestedHint: 'The models that still need a declaration have no knowledge-base match \u2014 adapt them one by one.',\n autoAdaptBlockedHint: 'Nothing to adapt yet: the card has no models, is not saved, or the settings document is read-only.',\n apply: 'Apply',\n reset: 'Reset',\n pendingSave: 'Modified \u2014 it will be written when you save this card.',\n pendingHint: 'Cancel or reload discards it, exactly like the card\\'s own fields.',\n appliedHint: 'Applied suggestion from {source} \u00B7 confidence: {confidence}',\n inputHintEndpoint: 'Modality advice comes from the endpoint listing.',\n inputHintKnowledge: 'Modality advice comes from the knowledge base.',\n inputHintHeuristic: 'Modality guessed from the name alone (low confidence) \u2014 verify.',\n referenceTitle: 'Reference capacities (hints only, never auto-filled)',\n contextWindowLabel: 'Context window',\n maxTokensLabel: 'Max output',\n inputModality: 'Input modalities',\n modalityImage: 'Image input',\n modalityInherit: 'Not declared \u2014 the provider default applies (usually text-only).',\n clearDeclaration: 'Clear declaration',\n confidence_high: 'high',\n confidence_medium: 'medium',\n confidence_low: 'low \u2014 verify before relying on it',\n noSuggestion: 'No suggestion is available for this model.',\n conflict: 'The settings document kept changing under this edit. Wait a moment, then apply again.',\n writeError: 'Failed: {message}',\n renderFailed: 'Reasoning-effort editor failed to render',\n wirePlaceholder: 'Wire value',\n wireValue: 'value',\n reasoningDisabled: 'No reasoning-effort control declared for this model (false) \u2014 do not arm thinking levels.',\n bareHint: 'Clearing every level removes the declaration \u2014 requests go out bare (provider default), which some relays require.',\n defaultRiskDisabled: 'No off level: Default and provider tests send thinking: disabled, which this model rejects (e.g. 1210).',\n defaultRiskQwen: 'No off level: Default sends enable_thinking: false \u2014 only fails where the upstream requires thinking on.',\n stagedHint: 'Not saved yet. Staged settings are applied automatically once the model is saved.',\n level_off: 'off',\n level_minimal: 'minimal',\n level_low: 'low',\n level_medium: 'medium',\n level_high: 'high',\n level_xhigh: 'xhigh',\n level_max: 'max',\n sliderToggleTitle: 'Model and reasoning-effort rows',\n sliderToggleDescription: 'Show the model row and the reasoning-level chips in the model menu; the levels follow the current model.',\n sliderToggleAria: 'Enable the model and reasoning-effort rows',\n settingOn: 'On',\n settingOff: 'Off',\n sliderEffortAria: 'Reasoning effort',\n sliderNoLevels: 'The current model exposes no reasoning-effort levels.',\n triggerFallback: 'Select model',\n effortDefault: 'Default',\n modelRowLabel: 'Model',\n effortRowLabel: 'Reasoning level',\n tagVision: 'Image input',\n tagThinking: 'Sends thinking levels',\n sliderEffortTitle: 'Reasoning effort \u00B7 {name}',\n sliderEffortTitleError: 'Reasoning effort failed: {error}',\n sliderNoCurrent: 'No model selected.',\n compatTitle: 'Endpoint compatibility',\n budgetFieldLabel: 'Thinking budget field',\n budgetFieldHint: 'Which parameter carries the thinking-token budget: some vLLM and self-hosted endpoints read thinking_token_budget, others thinking_budget or thinking_budget_tokens. Unset sends none of them.',\n budgetUnset: 'Not set',\n defaultEffortLabel: 'Default effort',\n defaultEffortUnset: 'Follow memory',\n defaultEffortHint: 'The level every new session starts this model at \u2014 it outranks the remembered levels. Within a session, a level you pick by hand always wins. Empty follows the memory chain.',\n clearDefaultEffort: 'Clear pick',\n priorityLabel: 'vLLM priority',\n priorityPlaceholder: 'e.g. 0',\n priorityHint: 'Scheduler priority for endpoints started with --priority, where lower runs first. Leave empty to send nothing.',\n priorityInvalid: 'Priority must be an integer \u2014 it is not saved until fixed.',\n maxOutputLabel: 'max_output_tokens in requests',\n maxOutputUnset: 'Unset \u2014 follow the protocol default',\n maxOutputOn: 'Send it',\n maxOutputOff: 'Omit it',\n maxOutputHint: 'For Responses-protocol gateways only, and only when a request carries an output cap: some of them reject the parameter, so \"Omit it\" keeps the cap out of the request entirely. Unset is the safe choice.',\n aliasMigrated: 'Legacy alias detected \u2014 saving writes the explicit budget field.',\n modelSearchPlaceholder: 'Search models\u2026',\n modelSearchNoMatches: 'No matching models',\n modelSearchClear: 'Clear search',\n modelSearchFailed: 'Model search failed to render',\n editorTabsLabel: 'Editor sections',\n editorTabProvider: 'Provider',\n editorTabModels: 'Models',\n editorTabAdvanced: 'Advanced',\n keyRevealShow: 'Show key',\n keyRevealHide: 'Hide key',\n keyRevealFailed: 'Stored key unavailable',\n keysLoading: 'Loading keys\u2026',\n keysAdd: 'Add another key',\n keysAddConfirm: 'Add',\n keysEnable: 'Use {name}',\n keysEnabled: 'Enabled',\n keysMissing: 'Not stored',\n keysEdit: 'Edit',\n keysRemove: 'Delete',\n keysConfirmRemove: 'Confirm delete',\n keysCancel: 'Cancel',\n keysSave: 'Save',\n keysAlias: 'Label',\n keysAliasPlaceholder: 'Name (optional), e.g. Team',\n keysValue: 'Key',\n keysValuePlaceholder: 'Paste the API key',\n keysNewValuePlaceholder: 'Paste the new API key',\n keysAddIllegal: 'A key is printable ASCII without spaces.',\n keysAdded: 'Key stored.',\n keysRenamed: 'Label saved.',\n keysReplaced: 'Key replaced.',\n keysRemoved: 'Key removed.',\n keysEnabledNote: '{name} is now in use.',\n keysUnreachable: 'The plugin\u2019s key route did not answer.',\n keysPanelFailed: 'Key manager failed to render',\n headersTitle: 'Request headers',\n headersHint: 'Sent on every request of this route, the model-listing probe included \u2014 add any gateway, routing, metering, or authentication header here.',\n headersEmpty: 'No extra request headers are configured.',\n headersAdd: 'Add header',\n headersName: 'Name',\n headersNamePlaceholder: 'e.g. x-company',\n headersValue: 'Value',\n headersEdit: 'Edit',\n headersSave: 'Save',\n headersCancel: 'Cancel',\n headersClear: 'Clear all',\n headersSaved: 'Saved.',\n headersCleared: 'Cleared \u2014 this route sends no extra request headers.',\n headersReadOnly: 'The settings document is not writable.',\n headersUserAgentTitle: 'User-Agent (taken over at the fetch layer)',\n headersUserAgentHint: 'The official adapter discards a configured user-agent \u2014 the name is reserved for client attribution \u2014 so this plugin adds it at the request layer. Filling it in really does replace the outgoing User-Agent.',\n headersUserAgentClear: 'Do not override',\n headersUserAgentPlaceholder: 'e.g. claude-cli/2.1.161 (external, cli)',\n headersConflictOrigin: 'Routes sharing one endpoint declare different request-header values: when the model in a request body does not identify exactly one of them, that request falls back to the first route.',\n headersConflictPatched: 'The installed dsh-llm-pi-ai already carries a request-header patch: enter values in the headers above and this plugin need not take the request layer over.',\n headersConflictSiblings: 'A plugin rewriting the same layer is installed: {names}. Only one user-agent rewrite should be active at a time, or the last writer on the wire wins.',\n headersError: 'Save failed: {error}',\n headersNoRoute: 'This route is no longer in the settings document \u2014 save the official card once first.',\n providerReorderAria: 'Reorder {provider}',\n providerReorderHint: 'Drag to set the provider order \u2014 the model switcher follows it.',\n providerToggleAria: 'Enable {provider}',\n providerToggleHint: 'A disabled provider is not listed by the model switcher.',\n providerRemoveDisabled: 'Delete',\n providerRemoveLocked: 'A built-in provider cannot be deleted.',\n modelReorderAria: 'Reorder {model}',\n modelReorderHint: 'Drag to set the order of this provider\u2019s models \u2014 the model switcher follows it.',\n}\n\n/** The plugin's locale key union. */\nexport type BreKey = keyof typeof en\n\n/** Chinese strings (same keys as {@link en}). */\nexport const zh: { [Key in keyof typeof en]: string } = {\n reasoningEffort: '\u601D\u8003\u5F3A\u5EA6',\n thinkingMode: '\u601D\u8003',\n thinkingModeOff: '\u5DF2\u5173\u95ED\u2014\u2014\u8BE5\u6A21\u578B\u58F0\u660E\u4E3A\u4E0D\u601D\u8003\u3002',\n autoAdapt: '\u81EA\u52A8\u9002\u914D',\n autoAdaptAll: '\u81EA\u52A8\u83B7\u53D6\u601D\u8003\u7B49\u7EA7',\n autoAdaptWorking: '\u83B7\u53D6\u4E2D\u2026',\n autoAdaptDone: '\u5DF2\u9002\u914D {count} \u4E2A\u00B7\u4FDD\u5B58\u540E\u5199\u5165',\n autoAdaptEmpty: '\u5DF2\u5168\u90E8\u914D\u7F6E',\n autoAdaptUnsuggested: '\u6CA1\u6709\u53EF\u7528\u5EFA\u8BAE',\n autoAdaptBlocked: '\u6682\u65E0\u53EF\u9002\u914D\u6A21\u578B',\n autoAdaptFailed: '\u83B7\u53D6\u5931\u8D25\uFF1A{message}',\n autoAdaptDoneHint: '\u5DF2\u6682\u5B58 {count} \u4E2A\u6A21\u578B\u7684\u9002\u914D\u7ED3\u679C\uFF1A\u70B9\u8BE5\u5361\u7247\u7684\u300C\u4FDD\u5B58\u300D\u540E\u5199\u5165\u3002',\n autoAdaptEmptyHint: '\u8BE5\u4F9B\u5E94\u5546\u4E0B\u6240\u6709\u6A21\u578B\u90FD\u5DF2\u58F0\u660E\u601D\u8003\u7B49\u7EA7\u3002',\n autoAdaptUnsuggestedHint: '\u4ECD\u9700\u58F0\u660E\u7684\u6A21\u578B\u6CA1\u6709\u77E5\u8BC6\u5E93\u5339\u914D\u2014\u2014\u8BF7\u9010\u4E2A\u6253\u5F00\u6A21\u578B\u884C\u624B\u52A8\u9002\u914D\u3002',\n autoAdaptBlockedHint: '\u6682\u65E0\u53EF\u9002\u914D\u5185\u5BB9\uFF1A\u8BE5\u5361\u7247\u8FD8\u6CA1\u6709\u6A21\u578B\u3001\u5C1A\u672A\u4FDD\u5B58\uFF0C\u6216\u5F53\u524D\u8BBE\u7F6E\u6587\u6863\u4E0D\u53EF\u5199\u5165\u3002',\n apply: '\u5E94\u7528',\n reset: '\u653E\u5F03\u4FEE\u6539',\n pendingSave: '\u5DF2\u4FEE\u6539\u2014\u2014\u70B9\u5361\u7247\u300C\u4FDD\u5B58\u300D\u65F6\u4E00\u8D77\u5199\u5165\u3002',\n pendingHint: '\u70B9\u300C\u53D6\u6D88\u300D\u6216\u5237\u65B0\u9875\u9762\u4F1A\u4E00\u5E76\u4E22\u5F03\uFF0C\u4E0E\u5361\u7247\u81EA\u8EAB\u7684\u5B57\u6BB5\u4E00\u81F4\u3002',\n appliedHint: '\u5DF2\u6309 {source} \u5E94\u7528\u5EFA\u8BAE \u00B7 \u7F6E\u4FE1\u5EA6\uFF1A{confidence}',\n inputHintEndpoint: '\u6A21\u6001\u5EFA\u8BAE\u6765\u81EA\u7AEF\u70B9\u5217\u8868\u3002',\n inputHintKnowledge: '\u6A21\u6001\u5EFA\u8BAE\u6765\u81EA\u77E5\u8BC6\u5E93\u3002',\n inputHintHeuristic: '\u6A21\u6001\u5EFA\u8BAE\u4EC5\u6309\u547D\u540D\u63A8\u65AD\uFF08\u4F4E\u7F6E\u4FE1\u5EA6\uFF09\uFF0C\u8BF7\u6838\u5BF9\u3002',\n referenceTitle: '\u53C2\u8003\u5BB9\u91CF\uFF08\u4EC5\u63D0\u793A\uFF0C\u4E0D\u81EA\u52A8\u586B\u5145\uFF09',\n contextWindowLabel: '\u4E0A\u4E0B\u6587\u7A97\u53E3',\n maxTokensLabel: '\u6700\u5927\u8F93\u51FA',\n inputModality: '\u8F93\u5165\u6A21\u6001',\n modalityImage: '\u56FE\u7247\u8F93\u5165',\n modalityInherit: '\u672A\u58F0\u660E\u2014\u2014\u7EE7\u627F\u63D0\u4F9B\u65B9\u9ED8\u8BA4\uFF08\u901A\u5E38\u4EC5\u6587\u672C\uFF09\u3002',\n clearDeclaration: '\u6E05\u9664\u58F0\u660E',\n confidence_high: '\u9AD8',\n confidence_medium: '\u4E2D',\n confidence_low: '\u4F4E\uFF08\u5EFA\u8BAE\u6838\u5BF9\uFF09',\n noSuggestion: '\u6CA1\u6709\u9002\u7528\u4E8E\u8BE5\u6A21\u578B\u7684\u5EFA\u8BAE\u3002',\n conflict: '\u8BBE\u7F6E\u6587\u6863\u5728\u4FDD\u5B58\u671F\u95F4\u6301\u7EED\u53D8\u5316\uFF0C\u8BF7\u7A0D\u5019\u518D\u6B21\u5E94\u7528\u3002',\n writeError: '\u5199\u5165\u5931\u8D25\uFF1A{message}',\n renderFailed: '\u601D\u8003\u5F3A\u5EA6\u7F16\u8F91\u5668\u6E32\u67D3\u5931\u8D25',\n wirePlaceholder: '\u7EBF\u4E0A\u53D6\u503C',\n wireValue: '\u53D6\u503C',\n reasoningDisabled: '\u8BE5\u6A21\u578B\u58F0\u660E\u4E3A\u65E0 reasoning-effort \u63A7\u5236\uFF08false\uFF09\u2014\u2014\u8BF7\u52FF\u52FE\u9009\u601D\u8003\u6863\u3002',\n bareHint: '\u5168\u90E8\u53D6\u6D88\u5E76\u4FDD\u5B58\u5C06\u6E05\u9664\u58F0\u660E\u2014\u2014\u8BF7\u6C42\u4EE5\u88F8\u683C\u5F0F\u53D1\u51FA\uFF08\u8D70\u63D0\u4F9B\u65B9\u9ED8\u8BA4\uFF09\uFF0C\u90E8\u5206\u4E2D\u8F6C\u7AD9\u9700\u8981\u8FD9\u6837\u3002',\n defaultRiskDisabled: '\u65E0 off \u6863\uFF1ADefault \u4E0E\u63D0\u4F9B\u5546\u6D4B\u8BD5\u4F1A\u53D1\u9001 thinking: disabled\uFF0C\u8BE5\u6A21\u578B\u4F1A\u62A5\u9519\uFF08\u5982 1210\uFF09\u3002',\n defaultRiskQwen: '\u65E0 off \u6863\uFF1ADefault \u4F1A\u53D1\u9001 enable_thinking: false\u2014\u2014\u4EC5\u5728\u4E0A\u6E38\u8981\u6C42\u5F00\u542F\u601D\u8003\u65F6\u624D\u4F1A\u5931\u8D25\u3002',\n stagedHint: '\u5C1A\u672A\u4FDD\u5B58\u3002\u6682\u5B58\u7684\u914D\u7F6E\u5C06\u5728\u8BE5\u6A21\u578B\u4FDD\u5B58\u540E\u81EA\u52A8\u5E94\u7528\u3002',\n level_off: 'off',\n level_minimal: 'minimal',\n level_low: 'low',\n level_medium: 'medium',\n level_high: 'high',\n level_xhigh: 'xhigh',\n level_max: 'max',\n sliderToggleTitle: '\u6A21\u578B\u4E0E\u601D\u8003\u7B49\u7EA7\u884C',\n sliderToggleDescription: '\u5728\u6A21\u578B\u83DC\u5355\u4E2D\u663E\u793A\u6A21\u578B\u884C\u4E0E\u601D\u8003\u7B49\u7EA7\u6807\u7B7E\uFF0C\u6863\u4F4D\u968F\u5F53\u524D\u6A21\u578B\u81EA\u52A8\u9002\u914D',\n sliderToggleAria: '\u542F\u7528\u6A21\u578B\u4E0E\u601D\u8003\u7B49\u7EA7\u884C',\n settingOn: '\u542F\u7528',\n settingOff: '\u505C\u7528',\n sliderEffortAria: '\u601D\u8003\u5F3A\u5EA6',\n sliderNoLevels: '\u5F53\u524D\u6A21\u578B\u672A\u63D0\u4F9B\u601D\u8003\u5F3A\u5EA6\u6863\u4F4D',\n triggerFallback: '\u9009\u62E9\u6A21\u578B',\n effortDefault: '\u9ED8\u8BA4',\n modelRowLabel: '\u6A21\u578B',\n effortRowLabel: '\u601D\u8003\u7B49\u7EA7',\n tagVision: '\u652F\u6301\u56FE\u7247\u8F93\u5165',\n tagThinking: '\u652F\u6301\u601D\u8003\u7B49\u7EA7',\n sliderEffortTitle: '\u601D\u8003\u5F3A\u5EA6 \u00B7 {name}',\n sliderEffortTitleError: '\u601D\u8003\u5F3A\u5EA6\u8BBE\u7F6E\u5931\u8D25\uFF1A{error}',\n sliderNoCurrent: '\u5F53\u524D\u65E0\u9009\u62E9\u4E2D\u7684\u6A21\u578B\u3002',\n compatTitle: '\u7AEF\u70B9\u517C\u5BB9',\n budgetFieldLabel: '\u601D\u8003\u9884\u7B97\u5B57\u6BB5',\n budgetFieldHint: '\u601D\u8003 token \u9884\u7B97\u7528\u54EA\u4E2A\u53C2\u6570\u53D1\u9001\uFF1A\u90E8\u5206 vLLM\uFF0F\u81EA\u5EFA\u7AEF\u70B9\u8BFB thinking_token_budget\uFF0C\u53E6\u4E00\u4E9B\u8BFB thinking_budget \u6216 thinking_budget_tokens\u3002\u672A\u8BBE\u7F6E\u8868\u793A\u4E09\u4E2A\u90FD\u4E0D\u53D1\u9001\u3002',\n budgetUnset: '\u672A\u8BBE\u7F6E',\n defaultEffortLabel: '\u9ED8\u8BA4\u601D\u8003\u5F3A\u5EA6',\n defaultEffortUnset: '\u8DDF\u968F\u8BB0\u5FC6',\n defaultEffortHint: '\u6BCF\u4E2A\u65B0\u4F1A\u8BDD\u6253\u5F00\u8BE5\u6A21\u578B\u65F6\u4F7F\u7528\u7684\u6863\u4F4D\u2014\u2014\u4F18\u5148\u4E8E\u8BB0\u4F4F\u7684\u4E0A\u6B21\u6863\u4F4D\u3002\u4F1A\u8BDD\u5185\u624B\u52A8\u9009\u62E9\u7684\u6863\u4F4D\u59CB\u7EC8\u6700\u9AD8\u3002\u7559\u7A7A\u5219\u8DDF\u968F\u8BB0\u5FC6\u94FE\u3002',\n clearDefaultEffort: '\u6E05\u9664\u9009\u62E9',\n priorityLabel: 'vLLM \u4F18\u5148\u7EA7',\n priorityPlaceholder: '\u4F8B\u5982 0',\n priorityHint: '\u4EE5 --priority \u542F\u52A8\u7684\u7AEF\u70B9\u7684\u8C03\u5EA6\u4F18\u5148\u7EA7\uFF0C\u6570\u503C\u8D8A\u5C0F\u8D8A\u5148\u6267\u884C\u3002\u7559\u7A7A\u8868\u793A\u4E0D\u53D1\u9001\u3002',\n priorityInvalid: '\u4F18\u5148\u7EA7\u5FC5\u987B\u662F\u6574\u6570\u2014\u2014\u4FEE\u6B63\u524D\u4E0D\u4F1A\u4FDD\u5B58\u3002',\n maxOutputLabel: '\u8BF7\u6C42\u4E2D\u7684 max_output_tokens',\n maxOutputUnset: '\u672A\u8BBE\u7F6E\u2014\u2014\u8DDF\u968F\u534F\u8BAE\u9ED8\u8BA4',\n maxOutputOn: '\u53D1\u9001',\n maxOutputOff: '\u4E0D\u53D1\u9001',\n maxOutputHint: '\u4EC5\u5BF9 Responses \u534F\u8BAE\u7684\u7F51\u5173\u751F\u6548\uFF0C\u4E14\u53EA\u5728\u8BF7\u6C42\u5E26\u8F93\u51FA\u4E0A\u9650\u65F6\u624D\u8D77\u4F5C\u7528\uFF1A\u90E8\u5206\u7F51\u5173\u4F1A\u62D2\u6536\u8BE5\u53C2\u6570\uFF0C\u9009\"\u4E0D\u53D1\u9001\"\u53EF\u8BA9\u8BF7\u6C42\u5B8C\u5168\u4E0D\u643A\u5E26\u8F93\u51FA\u4E0A\u9650\u3002\u672A\u8BBE\u7F6E\u662F\u6700\u5B89\u5168\u7684\u9009\u62E9\u3002',\n aliasMigrated: '\u68C0\u6D4B\u5230\u65E7\u522B\u540D\u2014\u2014\u4FDD\u5B58\u65F6\u4F1A\u5199\u5165\u663E\u5F0F\u9884\u7B97\u5B57\u6BB5\u3002',\n modelSearchPlaceholder: '\u641C\u7D22\u6A21\u578B\u2026',\n modelSearchNoMatches: '\u672A\u627E\u5230\u5339\u914D\u6A21\u578B',\n modelSearchClear: '\u6E05\u7A7A\u641C\u7D22',\n modelSearchFailed: '\u6A21\u578B\u641C\u7D22\u6E32\u67D3\u5931\u8D25',\n editorTabsLabel: '\u7F16\u8F91\u5668\u5206\u533A',\n editorTabProvider: '\u4F9B\u5E94\u5546\u914D\u7F6E',\n editorTabModels: '\u6A21\u578B\u914D\u7F6E',\n editorTabAdvanced: '\u9AD8\u7EA7\u914D\u7F6E',\n keyRevealShow: '\u663E\u793A\u5BC6\u94A5',\n keyRevealHide: '\u9690\u85CF\u5BC6\u94A5',\n keyRevealFailed: '\u65E0\u6CD5\u8BFB\u53D6\u5DF2\u4FDD\u5B58\u7684\u5BC6\u94A5',\n keysLoading: '\u6B63\u5728\u8BFB\u53D6\u5BC6\u94A5\u2026',\n keysAdd: '\u6DFB\u52A0\u53E6\u4E00\u4E2A\u5BC6\u94A5',\n keysAddConfirm: '\u6DFB\u52A0',\n keysEnable: '\u542F\u7528 {name}',\n keysEnabled: '\u542F\u7528',\n keysMissing: '\u672A\u5B58\u50A8',\n keysEdit: '\u4FEE\u6539',\n keysRemove: '\u5220\u9664',\n keysConfirmRemove: '\u786E\u8BA4\u5220\u9664',\n keysCancel: '\u53D6\u6D88',\n keysSave: '\u4FDD\u5B58',\n keysAlias: '\u522B\u540D',\n keysAliasPlaceholder: '\u540D\u79F0\uFF08\u53EF\u9009\uFF09\uFF0C\u4F8B\u5982 \u56E2\u961F',\n keysValue: '\u5BC6\u94A5',\n keysValuePlaceholder: '\u7C98\u8D34 API \u5BC6\u94A5',\n keysNewValuePlaceholder: '\u7C98\u8D34\u65B0\u7684 API \u5BC6\u94A5',\n keysAddIllegal: '\u5BC6\u94A5\u53EA\u80FD\u662F\u53EF\u6253\u5370 ASCII \u5B57\u7B26\uFF0C\u4E14\u4E0D\u80FD\u5305\u542B\u7A7A\u683C\u3002',\n keysAdded: '\u5DF2\u4FDD\u5B58\u5BC6\u94A5\u3002',\n keysRenamed: '\u5DF2\u4FDD\u5B58\u522B\u540D\u3002',\n keysReplaced: '\u5DF2\u66FF\u6362\u5BC6\u94A5\u3002',\n keysRemoved: '\u5DF2\u5220\u9664\u5BC6\u94A5\u3002',\n keysEnabledNote: '\u5DF2\u542F\u7528 {name}\u3002',\n keysUnreachable: '\u63D2\u4EF6\u7684\u5BC6\u94A5\u63A5\u53E3\u6CA1\u6709\u54CD\u5E94\u3002',\n keysPanelFailed: '\u5BC6\u94A5\u7BA1\u7406\u9762\u677F\u6E32\u67D3\u5931\u8D25',\n headersTitle: '\u8BF7\u6C42\u5934',\n headersHint: '\u8BE5\u8DEF\u7531\u7684\u6BCF\u4E2A\u8BF7\u6C42\u90FD\u4F1A\u643A\u5E26\uFF08\u8BC6\u522B\u6A21\u578B\u5217\u8868\u7684\u63A2\u6D4B\u8BF7\u6C42\u4E5F\u5305\u62EC\uFF09\uFF1A\u4E2D\u8F6C\u7AD9\u7684\u8DEF\u7531\u3001\u8BA1\u91CF\u3001\u8BA4\u8BC1\u7B49\u4EFB\u610F\u8BF7\u6C42\u5934\u90FD\u53EF\u4EE5\u5199\u5728\u8FD9\u91CC\u3002',\n headersEmpty: '\u5C1A\u672A\u914D\u7F6E\u989D\u5916\u8BF7\u6C42\u5934\u3002',\n headersAdd: '\u6DFB\u52A0\u8BF7\u6C42\u5934',\n headersName: '\u540D\u79F0',\n headersNamePlaceholder: '\u4F8B\u5982 x-company',\n headersValue: '\u503C',\n headersEdit: '\u7F16\u8F91',\n headersSave: '\u4FDD\u5B58',\n headersCancel: '\u53D6\u6D88',\n headersClear: '\u5168\u90E8\u6E05\u7A7A',\n headersSaved: '\u5DF2\u4FDD\u5B58\u3002',\n headersCleared: '\u5DF2\u6E05\u7A7A\u2014\u2014\u8BE5\u8DEF\u7531\u4E0D\u518D\u643A\u5E26\u989D\u5916\u8BF7\u6C42\u5934\u3002',\n headersReadOnly: '\u5F53\u524D\u8BBE\u7F6E\u6587\u6863\u4E0D\u53EF\u5199\u5165\u3002',\n headersUserAgentTitle: 'User-Agent\uFF08\u7531\u63D2\u4EF6\u5728\u8BF7\u6C42\u5C42\u63A5\u7BA1\uFF09',\n headersUserAgentHint: '\u5B98\u65B9\u9002\u914D\u5668\u4F1A\u4E22\u5F03\u914D\u7F6E\u7684 user-agent\uFF08\u8BE5\u540D\u79F0\u4FDD\u7559\u7ED9\u5BA2\u6237\u7AEF\u5F52\u5C5E\u6807\u8BC6\uFF09\uFF0C\u6240\u4EE5\u7531\u63D2\u4EF6\u5728\u8BF7\u6C42\u5C42\u8865\u4E0A\u3002\u586B\u4E86\u5C31\u4F1A\u771F\u6B63\u66FF\u6362\u4E0A\u884C\u8BF7\u6C42\u5934\u91CC\u7684 User-Agent\u3002',\n headersUserAgentClear: '\u4E0D\u8986\u76D6',\n headersUserAgentPlaceholder: '\u4F8B\u5982 claude-cli/2.1.161 (external, cli)',\n headersConflictOrigin: '\u540C\u4E00\u4E2A endpoint \u4E0A\u6709\u591A\u4E2A\u8DEF\u7531\u914D\u7F6E\u4E86\u4E0D\u540C\u7684\u8BF7\u6C42\u5934\u503C\uFF1A\u5F53\u8BF7\u6C42\u4F53\u91CC\u7684\u6A21\u578B\u65E0\u6CD5\u552F\u4E00\u5B9A\u4F4D\u5176\u4E2D\u4E00\u4E2A\u8DEF\u7531\u65F6\uFF0C\u8BE5\u8BF7\u6C42\u56DE\u9000\u5230\u7B2C\u4E00\u4E2A\u8DEF\u7531\u7684\u914D\u7F6E\u3002',\n headersConflictPatched: '\u68C0\u6D4B\u5230\u5B98\u65B9 dsh-llm-pi-ai \u5DF2\u5728\u8BF7\u6C42\u5C42\u5904\u7406\u8BF7\u6C42\u5934\uFF1A\u628A\u503C\u586B\u5728\u4E0A\u65B9\u8BF7\u6C42\u5934\u914D\u7F6E\u4E2D\u5373\u53EF\uFF0C\u65E0\u9700\u91CD\u590D\u63A5\u7BA1\u3002',\n headersConflictSiblings: '\u68C0\u6D4B\u5230\u4F1A\u6539\u5199\u540C\u4E00\u5C42\u7684\u540C\u7C7B\u63D2\u4EF6\uFF1A{names}\u3002\u540C\u4E00\u65F6\u95F4\u53EA\u5E94\u542F\u7528\u4E00\u79CD\u8BF7\u6C42\u5934\u6539\u5199\uFF0C\u5426\u5219\u7EBF\u4E0A\u751F\u6548\u7684\u662F\u6700\u540E\u5199\u5165\u8005\u3002',\n headersError: '\u4FDD\u5B58\u5931\u8D25\uFF1A{error}',\n headersNoRoute: '\u8BE5\u8DEF\u7531\u5DF2\u4E0D\u5728\u8BBE\u7F6E\u6587\u6863\u4E2D\u2014\u2014\u8BF7\u5148\u4FDD\u5B58\u4E00\u6B21\u5B98\u65B9\u5361\u7247\u3002',\n providerReorderAria: '\u62D6\u52A8\u6392\u5E8F\uFF1A{provider}',\n providerReorderHint: '\u62D6\u52A8\u8C03\u6574\u4F9B\u5E94\u5546\u987A\u5E8F\u2014\u2014\u6A21\u578B\u5207\u6362\u6309\u6B64\u987A\u5E8F\u663E\u793A\u3002',\n providerToggleAria: '\u542F\u7528 {provider}',\n providerToggleHint: '\u7981\u7528\u540E\u8BE5\u4F9B\u5E94\u5546\u4E0D\u5728\u6A21\u578B\u5207\u6362\u5217\u8868\u4E2D\u663E\u793A\u3002',\n providerRemoveDisabled: '\u5220\u9664',\n providerRemoveLocked: '\u5185\u7F6E\u4F9B\u5E94\u5546\u4E0D\u53EF\u5220\u9664\u3002',\n modelReorderAria: '\u62D6\u52A8\u6392\u5E8F\uFF1A{model}',\n modelReorderHint: '\u62D6\u52A8\u8C03\u6574\u8BE5\u4F9B\u5E94\u5546\u5185\u6A21\u578B\u987A\u5E8F\u2014\u2014\u6A21\u578B\u5207\u6362\u6309\u6B64\u987A\u5E8F\u663E\u793A\u3002',\n}\n", "/**\n * The reasoning-effort slider preference: a session-scoped localStorage-backed\n * enable flag, deliberately NOT settings.yaml (it is a UI preference, like the\n * upstream plugin's toggle it replaces). Defaults to enabled, but the initial\n * read migrates the UPSTREAM plugin's keys (dsh-reasoning-effort.enabled / the\n * @dsh-external legacy one) so a user who had switched the upstream slider off\n * does not suddenly get it back.\n *\n * @module dsh-model-think-level/client/slider-pref\n */\n\n/** localStorage key (own namespace; the upstream plugin's keys stay untouched). */\nexport const SLIDER_PREF_KEY = 'dsh-model-think-level.slider.enabled'\n\n/** Upstream plugin keys, consulted as a one-time migration fallback. */\nconst UPSTREAM_KEYS = ['dsh-reasoning-effort.enabled', '@dsh-external/dsh-reasoning-effort.enabled']\n\nlet enabled = read()\n\nfunction read(): boolean {\n try {\n const own = window.localStorage.getItem(SLIDER_PREF_KEY)\n const migrated = own === null\n ? UPSTREAM_KEYS.map(key => window.localStorage.getItem(key)).find(value => value !== null)\n : null\n return (own ?? migrated ?? null) !== 'false'\n } catch {\n return true\n }\n}\n\nconst listeners = new Set<() => void>()\n\nfunction notify(): void {\n for (const listener of [...listeners]) listener()\n}\n\n/** Snapshot the current preference. */\nexport function sliderEnabled(): boolean {\n return enabled\n}\n\n/** Save the preference (persisted immediately; a failed write keeps the session value). */\nexport function setSliderEnabled(next: boolean): void {\n if (enabled === next) return\n enabled = next\n try {\n window.localStorage.setItem(SLIDER_PREF_KEY, String(next))\n } catch {\n // The current page still follows the choice when storage is unavailable.\n }\n notify()\n}\n\n/** Apply a value incoming from another tab (no persist: the writer already stored it). */\nexport function syncSliderEnabled(next: boolean): void {\n if (enabled === next) return\n enabled = next\n notify()\n}\n\n/** Subscribe to preference changes; returns the disposer. */\nexport function subscribeSliderEnabled(listener: () => void): () => void {\n listeners.add(listener)\n return () => { listeners.delete(listener) }\n}\n", "/**\n * The provider-order preference: the sequence the user set by dragging the\n * provider rows on the official Models settings page.\n *\n * Deliberately localStorage, like the slider preference and for the same\n * reason: this is a UI ordering, not a wire setting. It is also the ONLY place\n * the order can live. The Models page's row order is not the `providers` key\n * order \u2014 `dsh-llm-pi-ai`'s `directoryEntries()` builds the configurable-\n * provider directory as \"every installed catalog route, then the routes the\n * profiles declare\", and the page keeps that declaration order (pinning\n * `deepseek-account`/`deepseek-official` first). Rewriting the dict would not\n * move a single row, and the settings write vocabulary\n * (`{ op: 'set' | 'unset' }`) has no reorder op to rewrite it with. So the\n * order lives beside the page: the rows are placed with CSS `order`, and the\n * composer model switcher reads the very same sequence.\n *\n * @module dsh-model-think-level/client/provider-order\n */\n\n/** localStorage key (own namespace; the upstream plugin's keys stay untouched). */\nexport const PROVIDER_ORDER_KEY = 'dsh-model-think-level.provider-order'\n\n/**\n * The two routes the official Models page always pins to the top, in this\n * order (its own `joinProviderDirectory` sort, matched here so the composer\n * switcher mirrors the page before the user has dragged anything).\n */\nconst PINNED: readonly string[] = ['deepseek-account', 'deepseek-official']\n\n/** Parse a stored/announced payload into a usable id sequence. */\nexport function parseProviderOrder(raw: string | null): readonly string[] {\n if (raw === null) return []\n try {\n const parsed: unknown = JSON.parse(raw)\n if (!Array.isArray(parsed)) return []\n return dedupe(parsed.filter((value): value is string => typeof value === 'string'))\n } catch {\n return []\n }\n}\n\n/** Drop empty and repeated ids, keeping the first occurrence's position. */\nfunction dedupe(ids: readonly string[]): string[] {\n const seen = new Set<string>()\n const kept: string[] = []\n for (const id of ids) {\n if (id.length === 0 || seen.has(id)) continue\n seen.add(id)\n kept.push(id)\n }\n return kept\n}\n\nlet order: readonly string[] = (() => {\n try {\n return parseProviderOrder(window.localStorage.getItem(PROVIDER_ORDER_KEY))\n } catch {\n // No storage (a private window, a non-DOM host): the ordering still works\n // for this page, it just does not survive a reload.\n return []\n }\n})()\n\nconst listeners = new Set<() => void>()\n\nfunction notify(): void {\n for (const listener of [...listeners]) listener()\n}\n\nfunction sameOrder(left: readonly string[], right: readonly string[]): boolean {\n return left.length === right.length && left.every((id, index) => id === right[index])\n}\n\n/**\n * Snapshot the current sequence. The returned array identity is stable until\n * the order changes, which is what makes it a legal `useSyncExternalStore`\n * snapshot.\n */\nexport function providerOrder(): readonly string[] {\n return order\n}\n\n/** Save the sequence (persisted immediately; a failed write keeps the session value). */\nexport function setProviderOrder(next: readonly string[]): void {\n const deduped = dedupe(next)\n if (sameOrder(order, deduped)) return\n order = deduped\n try {\n window.localStorage.setItem(PROVIDER_ORDER_KEY, JSON.stringify(order))\n } catch {\n // The current page still follows the new order when storage is unavailable.\n }\n notify()\n}\n\n/** Apply a sequence incoming from another tab (no persist: the writer already stored it). */\nexport function syncProviderOrder(next: readonly string[]): void {\n const deduped = dedupe(next)\n if (sameOrder(order, deduped)) return\n order = deduped\n notify()\n}\n\n/** Subscribe to order changes; returns the disposer. */\nexport function subscribeProviderOrder(listener: () => void): () => void {\n listeners.add(listener)\n return () => { listeners.delete(listener) }\n}\n\n/**\n * The order the official Models page itself renders `ids` in: the two pinned\n * routes first, everything else in the order it arrived (a stable sort, exactly\n * like the page's `toSorted`).\n */\nexport function baseProviderOrder(ids: readonly string[]): readonly string[] {\n const rank = (id: string): number => {\n const pinned = PINNED.indexOf(id)\n return pinned < 0 ? PINNED.length : pinned\n }\n return ids\n .map((id, index) => ({ id, index, rank: rank(id) }))\n .sort((left, right) => left.rank - right.rank || left.index - right.index)\n .map(entry => entry.id)\n}\n\n/**\n * Fold the user's sequence into a base order: the ids the preference names are\n * permuted among the slots they occupy, and every other id keeps the slot it\n * already had. A provider the user has never seen therefore keeps its natural\n * position instead of being exiled to the end.\n */\nexport function mergeProviderOrder(\n base: readonly string[],\n preferred: readonly string[],\n): readonly string[] {\n const rank = new Map<string, number>()\n for (const id of preferred) if (!rank.has(id)) rank.set(id, rank.size)\n const slots: number[] = []\n const named: string[] = []\n base.forEach((id, index) => {\n if (!rank.has(id)) return\n slots.push(index)\n named.push(id)\n })\n named.sort((left, right) => (rank.get(left) as number) - (rank.get(right) as number))\n const merged = [...base]\n slots.forEach((slot, position) => { merged[slot] = named[position] as string })\n return merged\n}\n\n/**\n * The effective order of `ids`: the dragged sequence where it applies, the\n * official page's own order everywhere else.\n */\nexport function resolveProviderOrder(ids: readonly string[]): readonly string[] {\n return mergeProviderOrder(baseProviderOrder(ids), providerOrder())\n}\n\n/**\n * Move `item` so it ends up at index `toIndex` of the result. The index is\n * interpreted against the RESULT, not the input, so a caller that derived it\n * from the row the pointer is over lands where the user pointed.\n */\nexport function moveToIndex<T>(items: readonly T[], item: T, toIndex: number): T[] {\n const from = items.indexOf(item)\n if (from < 0) return [...items]\n const next = [...items]\n next.splice(from, 1)\n const target = Math.max(0, Math.min(next.length, toIndex))\n next.splice(target, 0, item)\n return next\n}\n", "/**\n * The providers the user switched OFF on the Models settings page.\n *\n * The stored value is the DISABLED set, not the enabled one, so an empty \u2014 or\n * absent \u2014 preference means \"everything is on\". That direction matters for more\n * than brevity: a route that appears AFTER this preference was written is\n * enabled by default, instead of being hidden by an omission nobody made.\n *\n * The composer's Model switcher reads it to leave those providers out of its\n * provider column; the settings page reads it to draw each row's switch.\n *\n * @module dsh-model-think-level/client/provider-enabled\n */\n\nimport { parseProviderOrder } from './provider-order.js'\n\n/** Where the disabled provider ids live. */\nexport const PROVIDER_DISABLED_KEY = 'dsh-model-think-level.providers.disabled'\n\nconst listeners = new Set<() => void>()\n\n/** The disabled ids, as a snapshot whose identity is stable until they change. */\nlet disabled: readonly string[] = read()\n\nfunction read(): readonly string[] {\n try {\n return parseProviderOrder(window.localStorage.getItem(PROVIDER_DISABLED_KEY))\n } catch {\n return []\n }\n}\n\n/**\n * Parse a stored value. The shape is the provider order's \u2014 a deduped list of\n * non-empty ids \u2014 so the one parser serves both.\n */\nexport function parseDisabledProviders(raw: string | null): readonly string[] {\n return parseProviderOrder(raw)\n}\n\n/** The disabled provider ids. */\nexport function disabledProviders(): readonly string[] {\n return disabled\n}\n\n/** Whether one provider is switched on \u2014 the default for anything unnamed. */\nexport function isProviderEnabled(id: string): boolean {\n return !disabled.includes(id)\n}\n\n/** Switch one provider on or off, and persist the new set. */\nexport function setProviderEnabled(id: string, enabled: boolean): void {\n const already = !disabled.includes(id)\n if (enabled === already) return\n apply(enabled ? disabled.filter(candidate => candidate !== id) : [...disabled, id], true)\n}\n\n/** Apply an external (cross-tab) value without writing it back. */\nexport function syncDisabledProviders(next: readonly string[]): void {\n apply(next, false)\n}\n\n/** Listen for a switch, from this tab or another; returns the unsubscribe. */\nexport function subscribeDisabledProviders(listener: () => void): () => void {\n listeners.add(listener)\n return () => { listeners.delete(listener) }\n}\n\n/** Order is meaningless to a set: a reshuffle is not a change, and not a write. */\nfunction sameSet(left: readonly string[], right: readonly string[]): boolean {\n if (left.length !== right.length) return false\n return left.every(id => right.includes(id))\n}\n\nfunction apply(next: readonly string[], persist: boolean): void {\n if (sameSet(disabled, next)) return\n disabled = [...new Set(next)]\n if (persist) {\n try {\n window.localStorage.setItem(PROVIDER_DISABLED_KEY, JSON.stringify(disabled))\n } catch {\n // A storage that refuses the write (private mode, quota) must not lose\n // the in-memory switch: the user sees the row go off either way.\n }\n }\n // Notify a copy: a listener may unsubscribe while being notified.\n for (const listener of [...listeners]) listener()\n}\n", "/**\n * The per-provider model-order preference: the sequence the user set by dragging\n * the model rows inside one provider's editor on the official Models page.\n *\n * One storage key holds every provider's sequence, keyed by the provider id the\n * provider rows already use (`data-bre-provider`, or the Edit button's route) \u2014\n * the same id space as `provider-order.ts`, so a provider renamed in the\n * switcher keeps its models' order.\n *\n * localStorage, for the same reason the provider order is: this is a UI\n * ordering, not a wire setting. The official editor renders\n * `models.map((model, index) => <ModelRow position={index + 1} />)`, so the row\n * an index names is not something we can rewrite through the settings API (and\n * a reorder there would renumber every `aria-label` on the page). The rows are\n * placed with CSS `order` instead, and the composer switcher reads the same\n * sequences.\n *\n * @module dsh-model-think-level/client/model-order\n */\n\nimport { mergeProviderOrder } from './provider-order.js'\n\n/** localStorage key (own namespace; the upstream plugin's keys stay untouched). */\nexport const MODEL_ORDER_KEY = 'dsh-model-think-level.model-order'\n\n/** Every provider's stored sequence, by provider id. */\nexport type ModelOrderMap = Readonly<Record<string, readonly string[]>>\n\n/** Parse a stored/announced payload into a usable map. */\nexport function parseModelOrders(raw: string | null): ModelOrderMap {\n if (raw === null) return {}\n try {\n const parsed: unknown = JSON.parse(raw)\n if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) return {}\n const map: Record<string, readonly string[]> = {}\n for (const [provider, value] of Object.entries(parsed as Record<string, unknown>)) {\n if (provider.length === 0 || !Array.isArray(value)) continue\n const ids = dedupe(value.filter((entry): entry is string => typeof entry === 'string'))\n // A provider with no nameable model left is not an order: keeping it would\n // pin the provider's rows to slots they can no longer hold.\n if (ids.length > 0) map[provider] = ids\n }\n return map\n } catch {\n return {}\n }\n}\n\n/** Drop empty and repeated ids, keeping the first occurrence's position. */\nfunction dedupe(ids: readonly string[]): string[] {\n const seen = new Set<string>()\n const kept: string[] = []\n for (const id of ids) {\n if (id.length === 0 || seen.has(id)) continue\n seen.add(id)\n kept.push(id)\n }\n return kept\n}\n\nlet orders: ModelOrderMap = (() => {\n try {\n return parseModelOrders(window.localStorage.getItem(MODEL_ORDER_KEY))\n } catch {\n // No storage (a private window, a non-DOM host): the ordering still works\n // for this page, it just does not survive a reload.\n return {}\n }\n})()\n\nconst listeners = new Set<() => void>()\n\nfunction notify(): void {\n for (const listener of [...listeners]) listener()\n}\n\nfunction sameIds(left: readonly string[], right: readonly string[]): boolean {\n return left.length === right.length && left.every((id, index) => id === right[index])\n}\n\nfunction persist(): void {\n try {\n window.localStorage.setItem(MODEL_ORDER_KEY, JSON.stringify(orders))\n } catch {\n // The current page still follows the new order when storage is unavailable.\n }\n}\n\n/**\n * Snapshot every stored sequence. The returned object identity is stable until\n * some provider's order changes, which is what makes it a legal\n * `useSyncExternalStore` snapshot for a component that shows one provider.\n */\nexport function modelOrders(): ModelOrderMap {\n return orders\n}\n\n/** One provider's stored sequence (empty when the user has never dragged its models). */\nexport function modelOrderOf(provider: string): readonly string[] {\n return orders[provider] ?? EMPTY\n}\n\n/** A shared empty sequence, so a never-dragged provider returns a stable value. */\nconst EMPTY: readonly string[] = []\n\n/** Save one provider's sequence (persisted immediately; a failed write keeps the session value). */\nexport function setModelOrder(provider: string, next: readonly string[]): void {\n if (provider.length === 0) return\n const ids = dedupe(next)\n const current = modelOrderOf(provider)\n // An exact no-op keeps the snapshot identity, and with it every subscriber's\n // render, untouched \u2014 a drag that lands a row where it already was.\n if (sameIds(current, ids) && (ids.length > 0 || !(provider in orders))) return\n const map: Record<string, readonly string[]> = { ...orders }\n if (ids.length === 0) delete map[provider]\n else map[provider] = ids\n orders = map\n persist()\n notify()\n}\n\n/** Apply a map incoming from another tab (no persist: the writer already stored it). */\nexport function syncModelOrders(next: ModelOrderMap): void {\n orders = next\n notify()\n}\n\n/** Subscribe to order changes; returns the disposer. */\nexport function subscribeModelOrders(listener: () => void): () => void {\n listeners.add(listener)\n return () => { listeners.delete(listener) }\n}\n\n/**\n * Fold one provider's sequence into the ids the editor currently renders: the\n * ids the preference names are permuted among the slots they occupy, and a model\n * the user has never dragged (a newly added one) keeps the position it holds.\n */\nexport function orderModels(ids: readonly string[], preferred: readonly string[]): readonly string[] {\n return mergeProviderOrder(ids, preferred)\n}\n\n/** The effective order of a provider's `ids`: the dragged sequence where it applies. */\nexport function resolveModelOrder(provider: string, ids: readonly string[]): readonly string[] {\n return mergeProviderOrder(ids, modelOrderOf(provider))\n}\n\n/**\n * The same fold for a caller holding whole model entries rather than ids \u2014 the\n * composer switcher renders the directory's own objects, and must not rebuild\n * them. An entry whose id the preference does not name keeps its position, so a\n * model the user has never dragged (a newly added one) does not jump.\n */\nexport function orderModelEntries<T extends { readonly id: string }>(\n entries: readonly T[],\n preferred: readonly string[],\n): readonly T[] {\n if (entries.length === 0 || preferred.length === 0) return entries\n const byId = new Map(entries.map(entry => [entry.id, entry]))\n return orderModels(entries.map(entry => entry.id), preferred).map(id => byId.get(id) as T)\n}\n\n/** Drop everything one provider stored (used when its last model is removed). */\nexport function clearModelOrder(provider: string): void {\n setModelOrder(provider, [])\n}\n", "/**\n * Stylesheet for the injected editor. Deliberately tiny\n * and self-contained: it is injected into the settings panel and must not\n * collide with the official Models page classes, so every selector is prefixed\n * `bre-`. Uses CSS variables for theme awareness where the host provides them.\n */\n\n/** The stylesheet text, inserted once into <head> by the client apply(). */\nexport const STYLES = `\n/* The injector's mount wrapper. It \u2014 not the editor inside it \u2014 is the item\n placed into the official row disclosure's repeat(auto-fit, minmax(160px,1fr))\n grid (context window and max tokens take two cells), so the span belongs\n here: on the editor itself it would target the wrapper's block box and be\n ignored, squeezing the block into one cell. */\n.bre-effort-slot {\n grid-column: 1 / -1;\n}\n/* The slot wrapper is a plain column: the master thinking switch sits in it,\n directly above the card it gates. */\n.bre-effort-editor {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin: 8px 0 4px;\n}\n/* The \u601D\u8003\u5F3A\u5EA6 card proper \u2014 rendered only while the master switch is on. */\n.bre-effort-card {\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 10px 12px;\n border: 0.5px solid var(--dsw-alias-border-l2);\n border-radius: 8px;\n background: var(--dsw-alias-bg-layer-2);\n box-sizing: border-box;\n}\n.bre-effort-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n}\n.bre-effort-title {\n font-size: 12px;\n font-weight: 600;\n color: var(--dsw-alias-label-secondary);\n}\n.bre-link-button {\n background: none;\n border: none;\n padding: 2px 6px;\n font-size: 12px;\n color: var(--dsw-alias-link);\n cursor: pointer;\n border-radius: 4px;\n}\n.bre-link-button:hover { text-decoration: underline; }\n.bre-link-button:disabled { opacity: 0.5; cursor: default; text-decoration: none; }\n.bre-auto-effort {\n /* Seated in the OFFICIAL catalogue head, among the host's own controls, so it\n wears THEIR metrics instead of the plugin's blue underlined link look\n (user request: \"\u4E0D\u8981\u8D85\u94FE\u63A5\u6837\u5F0F\uFF0C\u8DDF\u53E6\u5916\u4E24\u4E2A\u4FDD\u6301\u4E00\u81F4\"). These values mirror\n the host's own linkButton rule; the seat cannot simply take that class,\n because it would then answer the probe that finds the anchor it sits after.\n No extra margin: the head's own 12px gap already spaces it exactly like the\n controls beside it. */\n box-sizing: border-box;\n display: inline-flex;\n align-items: center;\n height: 28px;\n padding: 0 10px;\n border: none;\n border-radius: var(--dsw-radius-sm, 8px);\n background: none;\n color: var(--dsw-alias-label-tertiary, #6b7280);\n font: inherit;\n font-size: 12px;\n line-height: 18px;\n cursor: pointer;\n flex: none;\n}\n.bre-auto-effort:hover {\n background: var(--dsw-alias-interactive-bg-hover, #2631480f);\n color: var(--dsw-alias-label-secondary, #374151);\n}\n.bre-effort-grid {\n /* Exactly two equal columns mirroring the official capacity pair the editor\n sits under; an odd row count leaves the last cell in the left column,\n so the left side carries the extra level. */\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 4px 16px;\n}\n.bre-effort-row {\n display: grid;\n grid-template-columns: 20px 76px minmax(0, 1fr);\n align-items: center;\n gap: 6px;\n font-size: 12px;\n}\n.bre-effort-row input[type='checkbox'] {\n /* Bigger than the browser default (~13px): a tap/point target that does\n not require precision, in the theme accent when checked. */\n width: 18px;\n height: 18px;\n margin: 0;\n accent-color: var(--dsw-alias-brand-primary);\n cursor: pointer;\n}\n.bre-effort-level { color: var(--dsw-alias-label-tertiary); }\n.bre-effort-wire {\n box-sizing: border-box;\n min-width: 0;\n height: 24px;\n padding: 0 6px;\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: 4px;\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 12px;\n}\n.bre-effort-wire:focus {\n outline: none;\n border-color: var(--dsw-alias-brand-primary);\n}\n.bre-effort-empty { min-height: 24px; }\n.bre-effort-actions {\n display: flex;\n gap: 8px;\n justify-content: flex-end;\n}\n.bre-secondary-button {\n height: 26px;\n padding: 0 12px;\n border-radius: 6px;\n font-size: 12px;\n cursor: pointer;\n border: 0.5px solid var(--dsw-alias-border-l3);\n}\n.bre-secondary-button:disabled { opacity: 0.5; cursor: default; }\n.bre-secondary-button { background: transparent; color: inherit; }\n.bre-effort-message { font-size: 12px; margin: 0; }\n.bre-effort-message.bre-error { color: #c62828; }\n.bre-effort-message.bre-info { color: var(--dsw-alias-link); }\n.bre-effort-note { font-size: 11px; margin: 0; color: var(--dsw-alias-label-tertiary); }\n/* ---- Input-modality section ---- */\n.bre-modality {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n.bre-modality-row {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 12px;\n}\n.bre-modality-row input[type='checkbox'] {\n width: 18px;\n height: 18px;\n margin: 0;\n accent-color: var(--dsw-alias-brand-primary);\n cursor: pointer;\n}\n.bre-modality-clear { margin-left: auto; }\n.bre-modality-note { font-size: 11px; margin: 0; color: var(--dsw-alias-label-tertiary); }\n/* The master thinking switch, which lives OUTSIDE the card it gates: with it\n off that card is not rendered at all, so nothing here has to be hidden. */\n.bre-thinking-switch { margin: 0; }\n/* ---- Endpoint-compatibility controls ----\n Same shape as the official capacity fields the editor sits under: a caption\n above the control, the control capped at the official enum width (a field\n width dropdown reads as a text field the user is expected to fill), and a\n hint line beneath. Tokens are the official ones \u2014 the plugin's own --dsh-*\n names are defined nowhere in this app, so their light-mode literals used to\n render in both themes. */\n.bre-compat {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n.bre-compat-row {\n display: flex;\n flex-direction: column;\n gap: 4px;\n min-width: 0;\n}\n.bre-compat-field { display: flex; flex-direction: column; gap: 4px; min-width: 0; }\n.bre-compat-label {\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n}\n.bre-compat-hint {\n font-size: 11px;\n line-height: 16px;\n color: var(--dsw-alias-label-tertiary);\n}\n.bre-select, .bre-text-input {\n box-sizing: border-box;\n width: 100%;\n max-width: 240px;\n height: 32px;\n padding: 0 10px;\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: 8px;\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-primary);\n font: inherit;\n font-size: 14px;\n line-height: 22px;\n}\n.bre-text-input::placeholder { color: var(--dsw-alias-label-dimmed); }\n.bre-select:focus, .bre-text-input:focus {\n outline: none;\n border-color: var(--dsw-alias-brand-primary);\n}\n.bre-select:disabled, .bre-text-input:disabled { opacity: 0.6; cursor: default; }\n.bre-select {\n cursor: pointer;\n /* The OS arrow sits flush against the right edge; the official select swaps\n it for the shared 12px chevron inset on the same right pad. Data-URI SVGs\n cannot resolve CSS variables, so the stroke is the caption gray both\n themes share. */\n appearance: none;\n padding-right: 32px;\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none'%3E%3Cpath d='M3 4.5L6 7.5L9 4.5' stroke='%2381858C' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E\");\n background-repeat: no-repeat;\n background-position: right 12px center;\n background-size: 12px 12px;\n}\n.bre-error { color: var(--dsw-alias-state-error-primary); }\n/* ---- Zoned suggestion display ---- */\n.bre-suggestion {\n display: flex;\n flex-direction: column;\n gap: 3px;\n}\n.bre-reference {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n gap: 4px 14px;\n padding: 6px 8px;\n border: 1px dashed var(--dsw-alias-border-l3);\n border-radius: 6px;\n font-size: 11px;\n color: var(--dsw-alias-label-tertiary);\n}\n.bre-reference-title { font-weight: 600; }\n.bre-reference-values { display: inline-flex; gap: 14px; }\n/* ---- Composer model-menu slider (mounted inside the OFFICIAL menu) ----\n Visuals ported VERBATIM from HanaAyane's dsh-reasoning-effort (MIT) \u2014 the\n only deliberate difference is the chibi-runner \"big fish\" knob, which is\n dropped so the knob is always the white circle. Class names are re-\n prefixed bre- (the upstream's re- prefix would clash while both plugins\n are installed); every color/size/animation value stays upstream's. */\n.bre-slider-body {\n /* The replicated popover body. The official menu shell carries padding: 4px\n (ModelSelect.module.css), so pull the replica flush to the box. overflow\n stays VISIBLE: an open list is laid out inside the body, so nothing ever\n has to escape it, and the rows' own hover radius needs no clipping. */\n overflow: visible;\n margin: -4px;\n padding: 8px;\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n/* The official menu is content-sized; while the replicated popover body is\n live, its box takes the upstream .re-model-menu width. The class is added\n by the mount and removed when the slider is switched off. */\n.bre-model-menu-host {\n width: min(340px, calc(100vw - 32px));\n min-width: 0;\n}\n/* The official trigger keeps ownership of its model/effort children. The\n provider prefix is a pseudo-element so host React updates, focus handling and\n the click target remain untouched.\n\n Scoped to the composer, which owns the only button that carries this stamp.\n The settings sidebar's account button is ALSO an [aria-haspopup=\"menu\"]\n button, so an unscoped rule paints the prefix onto the account row the\n moment a stale stamp lands there \u2014 and a stamp that escaped the composer\n must render nothing rather than relabel a host control. The seat anchor is\n listed too, so a shell that renders the trigger outside the card body still\n gets the prefix. */\n[data-composer-card] button[data-bre-provider]::before,\n[data-slot=\"conversation.input.model\"] button[data-bre-provider]::before {\n content: attr(data-bre-provider) \" \u00B7 \";\n flex: none;\n color: var(--dsw-alias-label-secondary);\n font-weight: 400;\n white-space: nowrap;\n}\n\n/* ---- Model / reasoning-level rows ----\n Presentation follows dsh-tauri-model-config's settings rows\n (src/client/models/styles.ts: .zGbnIq_rowCard / .zGbnIq_rowHead /\n .zGbnIq_iconButton): hairline separators between rows and a list opening\n under the row it belongs to. Re-prefixed bre- and routed through the tokens\n this file already uses. */\n/* Layout only \u2014 it deliberately paints NO background, border, radius or\n overflow clip. The replica is mounted inside the OFFICIAL menu, and that\n shell already draws its own surface (MenuSurface: a [data-menu-material]\n panel whose .material child paints --dsw-menu-surface-fill, i.e.\n --dsw-specific-menu, under backdrop-filter: var(--dsw-menu-backdrop-filter),\n rounded by --dsw-radius-lg and outlined by --dsw-elevation-prominent).\n A second opaque card in here shows that official surface as a grey frame\n around it \u2014 two stacked surfaces where every other menu in the app has one. */\n.bre-card {\n display: flex;\n flex-direction: column;\n}\n.bre-row-control {\n appearance: none;\n box-sizing: border-box;\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n min-height: 38px;\n padding: 0 10px;\n border: 0;\n /* Matches the official menu cells (.u91W7W_cell / .u91W7W_option use\n --dsw-radius-md, 8px here) and the .bre-option rows below, now that the\n surface behind the rows is the menu's own and hover is no longer clipped\n by a card. */\n border-radius: 8px;\n background: transparent;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n transition: background 120ms ease;\n}\n.bre-row-control:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover, rgba(120, 125, 140, .09));\n}\n.bre-row-control.is-open { background: var(--dsw-alias-fill-tertiary, rgba(120, 125, 140, .09)); }\n.bre-row-control:disabled { cursor: default; opacity: .6; }\n.bre-row-control:focus-visible {\n outline: none;\n box-shadow: inset 0 0 0 2px var(--dsw-alias-border-l3);\n}\n.bre-row-label {\n flex: none;\n font-size: 13px;\n line-height: 20px;\n color: var(--dsw-alias-label-secondary);\n}\n.bre-row-value {\n flex: 1 1 auto;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n text-align: right;\n font-size: 13px;\n line-height: 20px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n.bre-row-value.is-muted { font-weight: 400; color: var(--dsw-alias-label-tertiary); }\n.bre-divider { height: 1px; margin: 0 10px; background: var(--dsw-alias-border-l4); }\n.bre-panel {\n display: flex;\n flex-direction: column;\n gap: 2px;\n max-height: 220px;\n overflow-y: auto;\n padding: 2px 6px 8px;\n}\n/* Provider column + model column of the model list, side by side. The height is\n FIXED: switching provider must not resize the card, or every click would need\n another same-frame re-place and any missed one shows as a jump. */\n.bre-panel-models {\n flex-direction: row;\n align-items: stretch;\n gap: 0;\n height: 240px;\n max-height: none;\n overflow: hidden;\n padding: 4px 6px 8px;\n}\n.bre-providers {\n flex: none;\n display: flex;\n flex-direction: column;\n gap: 2px;\n width: 112px;\n overflow-y: auto;\n padding-right: 2px;\n}\n.bre-provider-models {\n flex: 1 1 auto;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 2px;\n overflow-y: auto;\n margin-left: 6px;\n padding-left: 6px;\n border-left: 1px solid var(--dsw-alias-border-l4);\n}\n.bre-option,\n.bre-provider {\n appearance: none;\n box-sizing: border-box;\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n min-height: 30px;\n padding: 0 8px;\n border: 0;\n border-radius: 8px;\n background: transparent;\n color: inherit;\n font: inherit;\n font-size: 13px;\n line-height: 20px;\n text-align: left;\n cursor: pointer;\n transition: background 120ms ease, color 120ms ease;\n}\n/* The provider cell carries its chevron hard against the right edge. */\n.bre-provider { gap: 4px; padding: 0 4px 0 8px; }\n.bre-option:hover:not(:disabled),\n.bre-provider:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover, rgba(120, 125, 140, .09));\n}\n.bre-option:disabled,\n.bre-provider:disabled { cursor: default; opacity: .6; }\n.bre-option:focus-visible,\n.bre-provider:focus-visible {\n outline: none;\n box-shadow: inset 0 0 0 2px var(--dsw-alias-border-l3);\n}\n.bre-option-name {\n flex: 1 1 auto;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.bre-option.is-active {\n color: var(--dsw-static-deepseek-500, #4d70ff);\n font-weight: 500;\n background: var(--dsw-alias-fill-tertiary, rgba(120, 125, 140, .09));\n}\n/* The open provider is the highlighted one; the provider of the CURRENT model\n keeps the accent so the column says where the selection lives. */\n.bre-provider.is-open { background: var(--dsw-alias-fill-tertiary, rgba(120, 125, 140, .09)); }\n.bre-provider.is-current .bre-option-name {\n color: var(--dsw-static-deepseek-500, #4d70ff);\n font-weight: 500;\n}\n.bre-provider .bre-row-chevron { width: 14px; height: 14px; font-size: 14px; }\n/* Capability badges on a model row (image input / thinking). Muted so the name\n stays the brightest thing in the row; the badge's own title carries the copy. */\n.bre-option-tags {\n flex: none;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n color: var(--dsw-alias-label-tertiary);\n}\n.bre-option.is-active .bre-option-tags { color: var(--dsw-static-deepseek-500, #4d70ff); }\n.bre-tag { display: inline-flex; align-items: center; }\n.bre-tag-icon { display: block; width: 13px; height: 13px; }\n.bre-option-check { flex: none; width: 14px; height: 14px; }\n.bre-empty { padding: 6px 8px; font-size: 12px; color: var(--dsw-alias-label-tertiary); }\n.bre-slider-advanced {\n /* upstream .re-advanced: the padded area that hosts the slider */\n padding: 14px;\n}\n.bre-effort {\n display: flex;\n align-items: center;\n width: 100%;\n min-width: 0;\n height: 32px;\n color: var(--dsw-alias-label-secondary);\n user-select: none;\n box-sizing: border-box;\n}\n.bre-effort-slider {\n --bre-progress: 50%;\n position: relative;\n width: 100%;\n height: 30px;\n flex: 1 1 auto;\n border-radius: 999px;\n isolation: isolate;\n transition: filter 180ms ease;\n}\n.bre-effort-track {\n position: absolute;\n inset: 0;\n overflow: hidden;\n border-radius: inherit;\n background: linear-gradient(100deg, #03040a 0%, #071126 22%, #101d4c 45%, #302262 70%, #5d35a0 100%);\n box-shadow:\n inset 0 1px 0 rgba(189, 199, 255, .15),\n inset 0 -1px 0 rgba(0, 0, 0, .55),\n 0 3px 10px rgba(12, 17, 55, .34);\n}\n.bre-effort-track::after {\n content: \"\";\n position: absolute;\n inset: 0;\n background:\n radial-gradient(circle at 18% 45%, rgba(82, 130, 255, .12), transparent 24%),\n linear-gradient(90deg, rgba(0, 0, 0, .28), transparent 42%, rgba(168, 113, 255, .12));\n pointer-events: none;\n}\n.bre-effort-fx {\n position: absolute;\n z-index: 1;\n inset: 0;\n overflow: hidden;\n border-radius: inherit;\n pointer-events: none;\n}\n.bre-effort-canvas {\n position: absolute;\n z-index: 2;\n inset: 0;\n width: 100%;\n height: 100%;\n opacity: 1;\n image-rendering: pixelated;\n mix-blend-mode: screen;\n transition: filter 140ms ease;\n}\n.bre-effort-flare {\n position: absolute;\n z-index: 3;\n top: 50%;\n left: var(--bre-progress);\n width: 78px;\n height: 46px;\n border-radius: 50%;\n background: radial-gradient(ellipse at 100% 50%, rgba(255,255,255,.96) 0 4%, rgba(188,189,255,.8) 11%, rgba(106,87,255,.5) 28%, rgba(105,31,255,.2) 49%, transparent 74%);\n filter: blur(2px) saturate(1.25);\n mix-blend-mode: screen;\n transform: translate(-100%, -50%);\n transition: left 70ms linear, filter 140ms ease;\n pointer-events: none;\n}\n.bre-effort-flare::before,\n.bre-effort-flare::after {\n content: \"\";\n position: absolute;\n inset: 50% auto auto 100%;\n border-radius: 999px;\n transform: translate(-50%, -50%);\n}\n.bre-effort-flare::before {\n width: 52px;\n height: 1px;\n background: linear-gradient(90deg, transparent, rgba(100,160,255,.42), #f1ecff, rgba(193,82,255,.65), transparent);\n box-shadow: 0 0 7px #9b7cff, 0 0 13px rgba(72,132,255,.64);\n}\n.bre-effort-flare::after {\n width: 1px;\n height: 20px;\n background: linear-gradient(180deg, transparent, rgba(196,190,255,.84), transparent);\n box-shadow: 0 0 7px #9c7cff;\n}\n.bre-effort-knob {\n position: absolute;\n z-index: 4;\n top: 50%;\n left: clamp(14px, var(--bre-progress), calc(100% - 14px));\n width: 28px;\n height: 28px;\n border: 1px solid rgba(255,255,255,.94);\n border-radius: 50%;\n background: #fff;\n box-shadow:\n 0 0 0 2px rgba(92,105,255,.12),\n 0 0 14px rgba(121,82,255,.48),\n 0 2px 7px rgba(0,0,0,.3);\n transform: translate(-50%, -50%);\n transition: left 190ms cubic-bezier(.22,1,.36,1), transform 160ms ease, box-shadow 180ms ease;\n pointer-events: none;\n}\n.bre-effort-input {\n position: absolute;\n z-index: 5;\n inset: -5px 0;\n width: 100%;\n height: calc(100% + 10px);\n margin: 0;\n opacity: 0;\n cursor: grab;\n touch-action: none;\n}\n.bre-effort-input:active { cursor: grabbing; }\n.bre-effort-input:focus-visible + .bre-effort-knob {\n outline: 2px solid var(--dsw-static-blue-400);\n outline-offset: 2px;\n}\n.bre-effort.is-dragging .bre-effort-canvas {\n filter: saturate(1.45) brightness(1.28) contrast(1.06);\n}\n.bre-effort.is-dragging .bre-effort-flare {\n filter: blur(1.5px) saturate(1.6) brightness(1.42);\n transition: none;\n}\n.bre-effort.is-dragging .bre-effort-knob {\n transform: translate(-50%, -50%) scale(1.07);\n transition: none;\n box-shadow:\n 0 0 0 3px rgba(113,115,255,.25),\n 0 0 20px rgba(74,145,255,.86),\n 0 0 31px rgba(171,53,255,.66),\n 0 3px 8px rgba(0,0,0,.32);\n}\n.bre-effort-slider[data-top] .bre-effort-track {\n animation: bre-effort-dark-breathe 1.9s ease-in-out infinite;\n}\n.bre-effort-slider[data-top] .bre-effort-knob {\n box-shadow:\n 0 0 0 3px rgba(119,99,255,.18),\n 0 0 22px rgba(135,78,255,.76),\n 0 0 34px rgba(53,121,255,.34),\n 0 3px 8px rgba(0,0,0,.3);\n}\n.bre-effort.is-error .bre-effort-slider {\n outline: 1px solid var(--dsw-alias-state-error-secondary);\n outline-offset: 2px;\n}\n.bre-effort.is-busy { opacity: .72; }\n.bre-effort-sr {\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n}\nbody:not([data-ds-dark-theme]) .bre-effort-slider {\n filter: none;\n}\nbody:not([data-ds-dark-theme]) .bre-effort-track {\n background: var(--dsw-static-blue-75, #e5f0ff);\n box-shadow:\n inset 0 1px 0 rgba(255,255,255,.9),\n inset 0 0 0 1px rgba(80,133,194,.14),\n 0 3px 10px rgba(48,101,165,.13);\n}\nbody:not([data-ds-dark-theme]) .bre-effort-track::before {\n content: \"\";\n position: absolute;\n z-index: 0;\n inset: 0 auto 0 0;\n width: var(--bre-progress);\n border-radius: inherit;\n background: linear-gradient(90deg, #fff 0%, #e2f0ff 20%, #a8d0fb 57%, #438fdf 100%);\n transition: width 190ms cubic-bezier(.22,1,.36,1);\n}\nbody:not([data-ds-dark-theme]) .bre-effort-slider[data-top] .bre-effort-track::before {\n background: linear-gradient(90deg, #fff 0%, #d7eaff 18%, #75afea 54%, #0751ad 100%);\n}\nbody:not([data-ds-dark-theme]) .bre-effort.is-dragging .bre-effort-track::before {\n transition: none;\n}\nbody:not([data-ds-dark-theme]) .bre-effort-track::after {\n z-index: 1;\n background: linear-gradient(90deg, rgba(255,255,255,.48), transparent 34%, rgba(23,101,201,.07));\n}\nbody:not([data-ds-dark-theme]) .bre-effort-canvas {\n opacity: .78;\n mix-blend-mode: multiply;\n}\nbody:not([data-ds-dark-theme]) .bre-effort-flare {\n background: radial-gradient(ellipse at 100% 50%, rgba(255,255,255,.98) 0 5%, rgba(204,231,255,.88) 13%, rgba(91,162,241,.48) 31%, rgba(37,111,207,.16) 53%, transparent 75%);\n filter: blur(2px) saturate(1.12);\n}\nbody:not([data-ds-dark-theme]) .bre-effort-flare::before {\n background: linear-gradient(90deg, transparent, rgba(116,177,244,.34), #fff, rgba(66,139,225,.58), transparent);\n box-shadow: 0 0 7px rgba(58,133,222,.5), 0 0 13px rgba(104,176,255,.38);\n}\nbody:not([data-ds-dark-theme]) .bre-effort-flare::after {\n background: linear-gradient(180deg, transparent, rgba(255,255,255,.94), transparent);\n box-shadow: 0 0 7px rgba(64,137,224,.44);\n}\nbody:not([data-ds-dark-theme]) .bre-effort-knob {\n border-color: rgba(126,160,197,.32);\n box-shadow:\n 0 0 0 2px rgba(58,124,207,.09),\n 0 0 13px rgba(48,118,207,.3),\n 0 3px 8px rgba(39,77,119,.18);\n}\nbody:not([data-ds-dark-theme]) .bre-effort-slider[data-top] .bre-effort-track {\n animation-name: bre-effort-light-breathe;\n}\nbody:not([data-ds-dark-theme]) .bre-effort-slider[data-top] .bre-effort-knob,\nbody:not([data-ds-dark-theme]) .bre-effort.is-dragging .bre-effort-knob {\n box-shadow:\n 0 0 0 3px rgba(36,105,192,.15),\n 0 0 20px rgba(25,100,201,.45),\n 0 3px 8px rgba(39,77,119,.18);\n}\n@keyframes bre-effort-dark-breathe {\n 0%, 100% { box-shadow: inset 0 1px 0 rgba(196,204,255,.16), 0 3px 10px rgba(18,25,72,.4); }\n 50% { box-shadow: inset 0 1px 0 rgba(220,214,255,.24), 0 0 21px rgba(111,66,255,.5); }\n}\n@keyframes bre-effort-light-breathe {\n 0%, 100% { box-shadow: inset 0 1px 0 rgba(255,255,255,.9), inset 0 0 0 1px rgba(67,124,193,.16), 0 3px 10px rgba(48,101,165,.13); }\n 50% { box-shadow: inset 0 1px 0 rgba(255,255,255,.96), inset 0 0 0 1px rgba(31,102,190,.22), 0 0 19px rgba(31,105,201,.24); }\n}\n.bre-slider-hint {\n /* upstream re-model-status */\n display: block;\n padding: 14px;\n color: var(--dsw-alias-label-tertiary, #9296a0);\n font-size: 12px;\n text-align: center;\n}\n/* upstream re-model-error: the directory/store error line under the row */\n.bre-model-error {\n margin: 8px;\n padding: 8px 10px;\n border-radius: 8px;\n color: var(--dsw-alias-state-error-primary, #c83e4d);\n background: var(--dsw-alias-state-error-tertiary, rgba(220,55,70,.08));\n font-size: 11px;\n}\n/* upstream .re-menu-separator */\n.bre-menu-separator {\n height: 1px;\n background: var(--dsw-alias-stroke-secondary, rgba(121,126,145,.16));\n}\n/* upstream .re-model-row: name \u00B7 current effort \u203A (click \u2192 official model list) */\n.bre-model-row {\n display: grid;\n grid-template-columns: minmax(0, 1fr) auto auto;\n align-items: center;\n gap: 8px;\n min-height: 45px;\n padding: 0 14px;\n width: 100%;\n border: 0;\n color: inherit;\n background: transparent;\n font: inherit;\n text-align: left;\n cursor: pointer;\n}\n.bre-model-row:hover { background: var(--dsw-alias-fill-tertiary, rgba(120,125,140,.09)); }\n.bre-model-row-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; }\n.bre-model-row-effort { color: var(--dsw-static-deepseek-500, #4d70ff); font-size: 12px; }\n.bre-row-chevron {\n flex: none;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 16px;\n height: 16px;\n font-size: 18px;\n line-height: 1;\n color: var(--dsw-alias-label-tertiary);\n transform: rotate(0deg);\n transition: transform 120ms ease;\n}\n.bre-row-chevron.is-open { transform: rotate(90deg); }\n@media (prefers-reduced-motion: reduce) {\n .bre-effort-slider[data-top] .bre-effort-track { animation: none; }\n .bre-effort-knob,\n .bre-effort-flare,\n body:not([data-ds-dark-theme]) .bre-effort-track::before { transition: none; }\n}\n/* ---- Models-page slider toggle (boxed setting item) ----\n Item form ported VERBATIM from upstream .re-setting-row; the surrounding\n box is the requested container (border only, transparent background). */\n.bre-slider-setting {\n margin-top: 12px;\n padding: 0 14px;\n border: 1px solid var(--dsw-alias-stroke-secondary, rgba(121,126,145,.2));\n border-radius: 12px;\n background: transparent;\n}\n.bre-slider-setting-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 24px;\n padding: 16px 0;\n /* Upstream .re-setting-row carries a list bottom border because the general\n settings list held TWO rows (the slider + the big-fish toggle). This box\n holds exactly one, so no divider: the box itself is the container. */\n}\n.bre-slider-setting-copy { min-width: 0; }\n.bre-slider-setting-title {\n color: var(--dsw-alias-label-primary, #15171b);\n font-size: 14px;\n font-weight: 400;\n line-height: 22px;\n}\n.bre-slider-setting-description {\n margin-top: 3px;\n color: var(--dsw-alias-label-tertiary, #9296a0);\n font-size: 12px;\n line-height: 18px;\n}\n.bre-slider-setting-control { display: inline-flex; align-items: center; gap: 10px; flex: none; }\n.bre-slider-setting-state { color: var(--dsw-alias-label-secondary, #686c75); font-size: 13px; }\n.bre-slider-setting-switch {\n position: relative;\n width: 38px;\n height: 22px;\n padding: 0;\n border: 0;\n border-radius: 999px;\n background: var(--dsw-alias-fill-quaternary, #c7cbd3);\n cursor: pointer;\n transition: background 150ms ease;\n}\n.bre-slider-setting-switch:hover { filter: brightness(.97); }\n.bre-slider-setting-switch:disabled { cursor: not-allowed; opacity: .45; }\n.bre-slider-setting-switch:focus-visible {\n outline: 2px solid var(--dsw-static-blue-400, #5d83ff);\n outline-offset: 2px;\n}\n.bre-slider-setting-switch.is-on { background: var(--dsw-alias-state-business-primary, #4f73ff); }\n.bre-slider-setting-switch-knob {\n position: absolute;\n top: 2px;\n left: 2px;\n width: 18px;\n height: 18px;\n border-radius: 50%;\n background: #fff;\n box-shadow: 0 1px 4px rgba(0,0,0,.2);\n transition: transform 170ms cubic-bezier(.22,1,.36,1);\n}\n.bre-slider-setting-switch.is-on .bre-slider-setting-switch-knob { transform: translateX(16px); }\n\n/* Model selection search box */\n.bre-model-search-box {\n display: flex;\n flex-direction: column;\n padding: 6px 8px 4px;\n position: sticky;\n top: 0;\n z-index: 2;\n background: var(--dsw-specific-menu, var(--dsw-alias-bg-layer-1, #1e1f22));\n}\n.bre-model-search-box:has(.bre-search-empty),\n.bre-model-search-box.has-empty {\n flex: 1 1 auto;\n}\n.bre-search-input-wrapper {\n display: flex;\n align-items: center;\n position: relative;\n width: 100%;\n height: 32px;\n padding: 0 8px;\n box-sizing: border-box;\n border-radius: 8px;\n background: var(--dsw-alias-bg-layer-2, rgba(255, 255, 255, 0.06));\n border: 0.5px solid var(--dsw-alias-border-l4, rgba(255, 255, 255, 0.12));\n color: var(--dsw-alias-label-primary);\n transition: border-color 150ms ease, box-shadow 150ms ease;\n}\n.bre-search-input-wrapper:focus-within {\n border-color: var(--dsw-static-deepseek-500, #4d70ff);\n box-shadow: 0 0 0 2px rgba(77, 112, 255, 0.2);\n}\n.bre-search-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n flex: 0 0 16px;\n width: 16px;\n height: 16px;\n margin-right: 6px;\n color: var(--dsw-alias-label-tertiary, #81858c);\n pointer-events: none;\n}\n.bre-search-icon svg {\n width: 14px;\n height: 14px;\n}\n.bre-search-input {\n flex: 1 1 auto;\n min-width: 0;\n height: 100%;\n padding: 0;\n border: none;\n outline: none;\n background: transparent;\n color: inherit;\n font: inherit;\n font-size: 13px;\n line-height: 20px;\n}\n.bre-search-input::placeholder {\n color: var(--dsw-alias-label-tertiary, #81858c);\n opacity: 0.85;\n}\n.bre-search-clear {\n display: flex;\n align-items: center;\n justify-content: center;\n flex: 0 0 18px;\n width: 18px;\n height: 18px;\n margin-left: 4px;\n padding: 0;\n border: none;\n border-radius: 4px;\n background: transparent;\n color: var(--dsw-alias-label-tertiary, #81858c);\n cursor: pointer;\n transition: background 120ms ease, color 120ms ease;\n}\n.bre-search-clear:hover {\n background: var(--dsw-alias-interactive-bg-hover, rgba(255, 255, 255, 0.1));\n color: var(--dsw-alias-label-primary);\n}\n.bre-search-clear svg {\n width: 12px;\n height: 12px;\n}\n.bre-search-empty {\n display: flex;\n align-items: center;\n justify-content: center;\n flex: 1 1 auto;\n min-height: 180px;\n padding: 24px 16px;\n text-align: center;\n color: var(--dsw-alias-label-tertiary, #81858c);\n font-size: 13px;\n line-height: 20px;\n user-select: none;\n}\n\n/* ---- Request-header section (issue #12). Mounted through the official\n settings.models.provider-card seat, so it sits in the card's own layout\n rather than a disclosure grid.\n\n Every value below was MEASURED off the official Models page's own controls\n in DSH 0.1.7-rc.2 rather than guessed, and each is expressed through the same\n --dsw-alias-* token the host paints with, so a theme switch (light/dark)\n repaints this section along with the page:\n\n provider card radius 20px, 1px var(--dsw-alias-settings-card-stroke)\n editor action save = filled var(--dsw-alias-button-primary-fill), h36,\n radius 12px, 0 14px, 14px; cancel = 1px border, same box\n row action h28, radius 8px, 0 10px, 12px, 1px border\n text link h21, radius 4px, 2px 6px, 12px, link-blue label\n text input radius 12px, 0 10px, 1px var(--dsw-alias-border-l3) ---- */\n/* The provider-card slot's wrapper.\n The official slot mounts us inside a display:contents container, so our own\n root IS a flex item of the card row \u2014 and the row lays its children out with\n a 12px gap. A wrapper that stays in the flow while empty therefore adds one\n phantom gap to EVERY card in the list, which is exactly the height regression\n this rule exists to prevent. Collapsed, the wrapper leaves the flow entirely:\n the card renders as it did before the plugin.\n (Deliberately NOT display:contents here \u2014 that keeps it a gap-participating\n item, which is the bug this rule fixes.)\n Open, it becomes an ordinary block so the section below can lay itself out.\n data-edit lives HERE \u2014 the occurrence component publishes the card's state\n onto its own root, so keying the rule on .bre-headers[data-edit] instead\n would silently never match. */\n.bre-headers-host {\n display: none;\n}\n.bre-headers-host[data-edit=\"1\"] {\n display: block;\n}\n.bre-headers {\n display: flex;\n flex-direction: column;\n gap: 8px;\n /* The card lays its own children out with a 12px gap and no dividers; this\n section is an addition to that column, so it separates itself the same way\n the host separates card sections \u2014 a hairline on the card's own stroke. */\n border-top: 1px solid var(--dsw-alias-border-l2, #0000001a);\n padding-top: 12px;\n\n /* Present ONLY while the provider card is being edited.\n The provider list is a list of providers: a request-header row parked in\n every card, collapsed or not, is noise on a surface the user did not ask\n to configure. The card reveals its editor when the user presses its own\n Edit action, and that is the moment this section belongs on screen \u2014 the\n same gesture that opens the model list opens this.\n\n The visibility is decided by the component (which watches the card and\n sets data-edit on the wrapper), not by a selector: the official editor is\n NOT a sibling of this element \u2014 its container sits in the card row's own\n children, after the row head and this section's own wrapper \u2014 so no\n relative selector can reach it. The wrapper's data-edit is the one fact\n CSS can act on.\n\n Degradation is safe by construction: with no observer (no card ancestor to\n watch) data-edit stays \"0\", the section is never revealed, and the\n official page stays clean rather than leaking a row into every card. */\n display: none;\n}\n.bre-headers-host[data-edit=\"1\"] .bre-headers {\n display: flex;\n}\n/* The collapsed heading is the section's identity while the card is being\n edited: title, configured count, and the \u203A that opens the details. */\n.bre-headers-disclosure {\n display: flex;\n align-items: center;\n gap: 6px;\n width: 100%;\n padding: 0;\n border: none;\n background: transparent;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n}\n.bre-headers-disclosure:hover .bre-effort-title {\n color: var(--dsw-alias-label-secondary, #61666b);\n}\n.bre-headers-chevron {\n margin-left: auto;\n padding-right: 2px;\n color: var(--dsw-alias-label-tertiary, #81858c);\n font-size: 14px;\n line-height: 1;\n transition: transform 150ms ease;\n}\n.bre-headers[data-open=\"1\"] .bre-headers-chevron { transform: rotate(90deg); }\n/* How many entries are configured \u2014 the one fact worth showing while closed. */\n.bre-headers-count {\n min-width: 16px;\n padding: 0 5px;\n border-radius: 8px;\n background: var(--dsw-alias-interactive-bg-hover, #2631480f);\n color: var(--dsw-alias-label-secondary, #61666b);\n font-size: 11px;\n line-height: 16px;\n text-align: center;\n}\n.bre-headers-head {\n display: flex;\n align-items: center;\n min-height: 32px;\n padding: 0 8px;\n border-radius: var(--dsw-radius-sm, 8px);\n background: var(--dsw-alias-interactive-bg-hover, #2631480f);\n}\n.bre-headers-head .bre-effort-title {\n color: var(--dsw-alias-label-primary, #0f1115);\n font-weight: 600;\n}\n.bre-headers-edit, .bre-headers-rows {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n.bre-headers-columns,\n.bre-headers-row {\n display: grid;\n grid-template-columns: minmax(120px, 0.9fr) minmax(180px, 1.5fr) 32px;\n align-items: center;\n gap: 8px;\n}\n.bre-headers-columns {\n padding: 0 10px;\n color: var(--dsw-alias-label-tertiary, #81858c);\n font-size: 11px;\n line-height: 16px;\n}\n.bre-headers-row {\n min-height: 40px;\n padding: 4px 6px;\n border: 1px solid var(--dsw-alias-border-l2, #0000001a);\n border-radius: var(--dsw-radius-sm, 8px);\n background: var(--dsw-alias-bg-layer-1, #fff);\n}\n.bre-headers-add {\n align-self: flex-start;\n min-height: 30px;\n padding: 0 10px;\n border: 1px solid var(--dsw-alias-border-l2, #0000001a);\n border-radius: var(--dsw-radius-sm, 8px);\n color: var(--dsw-alias-label-primary, #0f1115);\n}\n.bre-headers-add:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover, #2631480f);\n}\n.bre-headers-remove {\n width: 32px;\n padding: 0;\n justify-self: center;\n}\n.bre-headers-rows .bre-headers-name {\n color: var(--dsw-alias-label-secondary, #61666b);\n font-size: 12px;\n word-break: break-all;\n}\n.bre-headers-masked {\n color: var(--dsw-alias-label-tertiary, #81858c);\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n letter-spacing: 0;\n}\n\n/* The section's commit/dismiss pair, matching the official editor's own\n actions: a filled commit and a bordered dismiss, 36px tall at 14px. */\n.bre-headers-edit .bre-primary-button {\n height: 36px;\n padding: 0 14px;\n border: none;\n border-radius: var(--dsw-radius-md, 12px);\n background: var(--dsw-alias-button-primary-fill, #0f1115);\n color: var(--dsw-alias-label-primary-foreground, #fff);\n font-size: 14px;\n font-weight: 400;\n line-height: 1;\n cursor: pointer;\n}\n.bre-headers-edit .bre-primary-button:hover:not(:disabled) {\n background: var(--dsw-alias-button-primary-hover, #43454a);\n}\n.bre-headers-edit .bre-primary-button:disabled { opacity: 0.5; cursor: default; }\n.bre-headers-edit .bre-secondary-button {\n height: 36px;\n padding: 0 14px;\n border: 1px solid var(--dsw-alias-border-l3, #0000001f);\n border-radius: var(--dsw-radius-md, 12px);\n background: transparent;\n color: var(--dsw-alias-label-primary, #0f1115);\n font-size: 14px;\n}\n.bre-headers-edit .bre-secondary-button:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover, #2631480f);\n}\n\n/* The row actions and the section's Edit affordance: the official 28px\n bordered small button, and the 21px blue text link the model rows use. */\n.bre-headers-row .bre-link-button {\n height: 28px;\n padding: 2px 8px;\n border: none;\n border-radius: var(--dsw-radius-sm, 8px);\n background: transparent;\n color: var(--dsw-alias-label-secondary, #61666b);\n font-size: 12px;\n line-height: 1;\n}\n.bre-headers-row .bre-link-button:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover, #2631480f);\n}\n.bre-headers .bre-effort-head > .bre-link-button {\n height: 28px;\n padding: 0 10px;\n border: 1px solid var(--dsw-alias-border-l2, #0000001a);\n border-radius: var(--dsw-radius-sm, 8px);\n color: var(--dsw-alias-label-primary, #0f1115);\n font-size: 12px;\n text-decoration: none;\n}\n.bre-headers .bre-effort-head > .bre-link-button:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover, #2631480f);\n text-decoration: none;\n}\n.bre-headers-edit .bre-link-button {\n color: var(--dsw-alias-label-secondary, #61666b);\n font-size: 12px;\n}\n\n/* The section's own fields, on the official input box. */\n.bre-headers-edit input.bre-text-input {\n height: 32px;\n padding: 0 10px;\n border: 1px solid var(--dsw-alias-border-l3, #0000001f);\n border-radius: var(--dsw-radius-md, 12px);\n background: var(--dsw-alias-bg-layer-1, #fff);\n color: var(--dsw-alias-label-primary, #0f1115);\n font-size: 13px;\n box-sizing: border-box;\n}\n.bre-headers-edit input.bre-text-input:focus {\n border-color: var(--dsw-alias-label-tertiary, #81858c);\n outline: none;\n}\n.bre-headers-edit input.bre-text-input:disabled { opacity: 0.6; cursor: default; }\n\n/* Status copy and the coexistence warnings, on the host's own state colours. */\n.bre-headers .bre-effort-note.bre-warn { color: var(--dsw-alias-state-warn-label, #dd8629); }\n.bre-headers .bre-effort-message.bre-success { color: var(--dsw-alias-state-success-primary, #22c55e); }\n.bre-headers .bre-effort-message.bre-error { color: var(--dsw-alias-state-error-primary, #ec1313); }\n\n@media (max-width: 520px) {\n .bre-headers-columns { display: none; }\n .bre-headers-row {\n grid-template-columns: minmax(0, 1fr) minmax(0, 1.25fr) 32px;\n gap: 6px;\n padding: 4px;\n }\n .bre-headers-edit input.bre-text-input {\n padding: 0 8px;\n font-size: 12px;\n }\n}\n\n/* ---- Provider row reordering (Models page) ---- */\n\n/* The whole row header is the drag handle: an 18px grip is a poor thing to\n have to hit, so a drag may start anywhere on the header that is not one of\n the card's own controls (those keep their own cursor, declared below). */\nli[data-bre-row-head='1'] { cursor: grab; }\nli[data-bre-row-head='1']:active { cursor: grabbing; }\nli[data-bre-row-head='1'] button,\nli[data-bre-row-head='1'] a,\nli[data-bre-row-head='1'] input { cursor: pointer; }\n\n/* The grip is mounted as the first child of that header, so it reads as part\n of the row rather than as a control bolted beside it. It is also the button\n that reorders by keyboard. */\n.bre-drag-grip {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: 0 0 auto;\n width: 14px;\n height: 20px;\n margin-right: 2px;\n border-radius: 4px;\n color: var(--dsw-alias-label-tertiary, #81858c);\n cursor: grab;\n touch-action: none;\n}\n\n.bre-drag-grip:hover {\n color: var(--dsw-alias-label-secondary, #61666b);\n background: var(--dsw-alias-interactive-bg-hover, #2631480f);\n}\n\n.bre-drag-grip:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n.bre-drag-grip:active { cursor: grabbing; }\n\n/* What the pointer carries: the browser's own drag image would be a screenshot\n of a whole card, so we hand it this chip with the provider name instead. */\n.bre-drag-ghost {\n position: fixed;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n max-width: 260px;\n padding: 5px 10px 5px 7px;\n border: 1px solid var(--dsw-alias-border-secondary, #e6e8eb);\n border-radius: 8px;\n background: var(--dsw-alias-bg-elevated, #ffffff);\n box-shadow: 0 6px 18px #0000002e;\n color: var(--dsw-alias-label-primary, #17181a);\n font-size: 13px;\n line-height: 18px;\n pointer-events: none;\n transform: translate(-50%, -50%);\n z-index: 60;\n}\n\n.bre-drag-ghost-name {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.bre-drag-ghost svg { color: var(--dsw-alias-label-tertiary, #81858c); }\n\n/* The row the drag is carrying: still in place, visibly the one in hand. */\nli.bre-row-dragging { opacity: .5; }\n\n/* The row the pointer would drop it on \u2014 a secondary cue; the insertion line\n below is the one that says which side. */\nli.bre-row-drop-target { border-color: var(--dsw-alias-brand-primary); }\n\n/* The insertion line. A fixed overlay on the body, never a child of the list\n React owns, so writing it cannot trigger anything in the official section.\n The layer stack matters more than it looks: the provider list lives inside the\n settings dialog, whose overlay is fixed and opaque with z-index 1000, so a\n line in the shell's own range would be drawn behind the dialog and never\n seen. It clears the dialog, and stays under the header's controls. */\n.bre-drop-line {\n position: fixed;\n display: none;\n height: 2px;\n border-radius: 2px;\n background: var(--dsw-alias-brand-primary, #4d6bfe);\n box-shadow: 0 0 0 1px #ffffffb3, 0 1px 4px var(--dsw-alias-brand-primary, #4d6bfe);\n pointer-events: none;\n z-index: 1200;\n}\n\n/* The row slide (a FLIP pass driven from the script) is motion; drop it for a\n reader who asked for less. */\n@media (prefers-reduced-motion: reduce) {\n li[data-bre-row-provider] { transition: none !important; }\n}\n\n@media (max-width: 520px) {\n .bre-drag-grip { width: 18px; height: 24px; }\n .bre-drag-ghost { max-width: 180px; font-size: 12px; }\n}\n\n/* ---- Model rows inside one provider's editor (Models page) ---- */\n\n/* The official model row is a four-column grid (id, display name, two icon\n buttons). Our grip is a fifth child, so the grid needs a leading track to\n hold it \u2014 declared by our own class, because the official rule is a single\n class selector and ours has to win on specificity, not on load order.\n The name must NOT be \"bre-model-row\": that one is this plugin's own composer\n row (see the \".bre-model-row\" block above), and lending it to the host's grid\n would hand that grid our padding, gap, min-height, pointer cursor and hover\n fill along with it. */\n.bre-model-grid[class*=\"modelRow\"] {\n grid-template-columns: auto minmax(0, 1.4fr) minmax(0, 1fr) auto auto;\n}\n\n/* A model row is a grid of inputs, so only the grip drags: the row keeps its\n text cursor and its own click behaviour. The grip takes the height of the\n row's controls, so the pointer has a real target inside a 6px-gap grid. */\n.bre-model-grid > .bre-drag-grip {\n width: 18px;\n height: 24px;\n margin-right: 0;\n}\n\n/* The engine paints the carried row and the row under the pointer with the same\n classes it uses on a provider card; the official model entry carries the\n border, so the drop cue has to land there rather than on the list item. */\ndiv.bre-row-dragging { opacity: .5; }\n[class*=\"modelEntry\"].bre-row-drop-target { border-color: var(--dsw-alias-brand-primary); }\n\n/* The row slide (a FLIP pass driven from the script) is motion; drop it for a\n reader who asked for less. */\n@media (prefers-reduced-motion: reduce) {\n [data-bre-row-model] { transition: none !important; }\n}\n\n/* ---- Provider enable switch (Models page) ---- */\n\n/* Leads the row's own action group (which is pushed right by margin-left:auto),\n in front of the card's buttons, so the destructive one stays last. The knob is\n a pseudo element: no extra node for the official section's tree to trip over. */\n.bre-provider-switch {\n position: relative;\n flex: 0 0 auto;\n width: 30px;\n height: 18px;\n padding: 0;\n border: none;\n border-radius: 9px;\n background: var(--dsw-alias-bg-tertiary, #d7dae0);\n cursor: pointer;\n transition: background 140ms ease;\n}\n\n.bre-provider-switch::after {\n content: '';\n position: absolute;\n top: 2px;\n left: 2px;\n width: 14px;\n height: 14px;\n border-radius: 50%;\n background: var(--dsw-alias-bg-elevated, #ffffff);\n box-shadow: 0 1px 2px #00000026;\n transition: transform 140ms ease;\n}\n\n.bre-provider-switch:not([aria-checked='true']):hover { background: var(--dsw-alias-bg-quaternary, #c9ccd2); }\n\n.bre-provider-switch[aria-checked='true'] { background: var(--dsw-alias-brand-primary, #4d6bfe); }\n\n.bre-provider-switch[aria-checked='true']:hover { background: var(--dsw-alias-brand-primary-hover, #3f5ce8); }\n\n.bre-provider-switch[aria-checked='true']::after { transform: translateX(12px); }\n\n.bre-provider-switch:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary, #4d6bfe);\n outline-offset: 2px;\n}\n\n/* A provider that is switched off stays on the page \u2014 this is the only place it\n can be switched back on \u2014 but it is visibly out of play. The switch itself is\n left at full strength: it is the way back. */\nli.bre-row-disabled [class*=\"rowIdentity\"],\nli.bre-row-disabled [class*=\"rowName\"] { opacity: .5; }\n\n@media (prefers-reduced-motion: reduce) {\n .bre-provider-switch,\n .bre-provider-switch::after { transition: none; }\n}\n\n/* ---- The reserved delete seat (Models page) ---- */\n\n/* A provider the host will not let the user remove renders no delete button, so\n that row's action group \u2014 pushed right by margin-left:auto \u2014 starts further\n right than its neighbours' and the column of buttons lines up raggedly. This\n seat holds the place: it borrows the metrics the official button takes inside\n an action group (.rowActions button is 28px tall / 12px / 0 10px) and the\n dimmed treatment the host gives its own disabled buttons (opacity .4 with a\n default cursor). It is always disabled \u2014 it is the alignment, not an action. */\n.bre-provider-delete {\n box-sizing: border-box;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n height: 28px;\n padding: 0 10px;\n border: none;\n border-radius: var(--dsw-radius-sm, 6px);\n background: 0 0;\n color: var(--dsw-alias-state-error-primary, #e5484d);\n font: inherit;\n font-size: 12px;\n line-height: 18px;\n opacity: .4;\n cursor: default;\n}\n\n/* -----------------------------------------------------------------------\n The provider editor's three tabs.\n\n The official editor lays a provider out as one long column \u2014 identity\n fields, then the model list, then this plugin's request-header section\n one slot up the card. The tab pass (injection/editor-tabs.ts) tags each\n region and stamps the card with the active tab; EVERY visibility decision\n lives here, so the official React tree never moves and a pass failure\n degrades to the untouched one-column editor.\n ----------------------------------------------------------------------- */\n\n/* The segmented control. The plugin's tab-bar component renders it inside the\n request-header mount, which already holds the card's 12px gap slot \u2014 so the\n bar reaches the panes in the card's own rhythm WITHOUT this plugin ever\n inserting a node into the official child list. It stays hidden until the pass\n marks the card: a card the pass could not take over (the DeepSeek account\n editor) keeps that mount visible for its request-header section, and must not\n grow a bar that switches nothing. */\n.bre-editor-tabs {\n display: none;\n gap: 2px;\n padding: 2px;\n border-radius: 10px;\n background: var(--dsw-alias-fill-tertiary, rgba(120, 125, 140, .09));\n}\n\n/* Only a taken-over card shows the bar. */\n.bre-tabbed .bre-editor-tabs { display: flex; }\n\n/* ...and while the takeover is on, the mount holding that bar MUST be visible.\n This is the one place the takeover does not delegate its reveal to the\n official-edit state: the bar lives in here, and gating it on the _editor\n class probe would make the tabs depend on a probe an official redesign can\n break \u2014 with data-edit stuck at 0 the bar would be inside a display:none\n parent and no rule of ours could reach it. The taken-over class is written by\n the pass itself, in the same frame, so it is the trustworthy half of the\n pair. The section's own visibility stays a pane decision (below). */\n.bre-tabbed .bre-headers-host { display: block; }\n\n.bre-editor-tab {\n flex: 1 1 0;\n min-width: 0;\n height: 28px;\n padding: 0 10px;\n border: none;\n border-radius: 8px;\n background: transparent;\n color: var(--dsw-alias-label-secondary, #61666b);\n font: inherit;\n font-size: 12px;\n font-weight: 500;\n line-height: 1;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n cursor: pointer;\n transition: background 140ms ease, color 140ms ease, box-shadow 140ms ease;\n}\n\n.bre-editor-tab:hover { color: var(--dsw-alias-label-primary, #0f1115); }\n\n/* The selected segment lifts out of the track: the page's own surface colour\n plus the card's own hairline, the same elevation the host uses for raised\n controls. Hover on a selected tab stays put \u2014 it is already where you are. */\n.bre-editor-tab[aria-selected='true'],\n.bre-editor-tab[aria-selected='true']:hover {\n background: var(--dsw-alias-bg-layer-1, #fff);\n color: var(--dsw-alias-label-primary, #0f1115);\n box-shadow: 0 1px 2px #0000001a, 0 0 0 1px var(--dsw-alias-border-l2, #0000001a);\n}\n\n.bre-editor-tab:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary, #4d6bfe);\n outline-offset: 1px;\n}\n\n/* An advanced section with no headers mount (pre-first-render, feature off)\n offers an empty pane \u2014 the button steps aside until the mount exists. */\n.bre-editor-tab[hidden] { display: none; }\n\n/* Pane visibility, keyed on the card's active tab. Only the HIDING has to win\n a specificity fight (against the official sheet and the headers host's own\n reveal rule); the active pane keeps whatever display the official layout\n gave it, so showing must not declare a display at all. */\n.bre-tabbed:not([data-bre-tab='provider']) [data-bre-region='provider'],\n.bre-tabbed:not([data-bre-tab='models']) [data-bre-region='models'],\n.bre-tabbed:not([data-bre-tab='advanced']) [data-bre-region='advanced'] {\n display: none !important;\n}\n\n/* The advanced tab: the official body \u2014 fields, model list, action row and the\n editor's own header \u2014 steps aside for the request-header section, which is a\n complete form with its own save/cancel and becomes the pane under the very\n same bar. */\n.bre-tabbed[data-bre-tab='advanced'] [data-bre-editor-body] {\n display: none !important;\n}\n\n/* The advanced pane shows the section for the same reason the mount is shown:\n a pane that is already the active one has no business depending on the\n official-edit probe. The rule above still wins for every other tab, and the\n base bre-headers rule keeps a collapsed card's section hidden \u2014 a card only\n carries the taken-over class while its editor is open. */\n.bre-tabbed[data-bre-tab='advanced'] .bre-headers { display: flex; }\n\n/* The pane is a card, and the section draws it. On the advanced tab the section\n is not an appendage to the fields above it any more \u2014 it is the whole surface\n \u2014 so it takes the inset fill the official module cards use (the add and setup\n cards): the one fill in this system that stays visible on the content card's\n own white in the light theme and reads as raised in the dark one, where every\n theme layer token collapses onto the same value. The hairline goes with it: it\n divided this section from those fields, and here the bar above ends the pane.\n The 12px top margin sets the card off the tab bar above it \u2014 the same gap the\n provider and models panes put between the bar and their own cards \u2014 and the\n 12px padding is the card's inner inset, not the separator. */\n.bre-tabbed[data-bre-tab='advanced'] .bre-headers {\n margin-top: 12px;\n padding: 12px;\n border-top: none;\n border-radius: var(--dsw-radius-lg, 16px);\n background: var(--dsw-alias-bg-module-platform, #f5f6f7);\n}\n\n/* The live editor folds its identity fields and model list into one\n \"Customized\" disclosure. Under the tabs the grouping is the panes' job: the\n group stays expanded (the pass forces it open) and its summary row \u2014 now a\n toggle with nothing left to toggle \u2014 steps aside. */\n.bre-tabbed [data-bre-editor-body] details > summary {\n display: none !important;\n}\n\n/* A region that is NOT a pane. The editor's own header repeats the card row's\n provider name and route tag, and that row is already on screen directly above\n it, so the header is hidden on every tab. No pane rule above can select this\n id, which is what keeps it hidden whichever tab is active. */\n.bre-tabbed [data-bre-region='none'] {\n display: none !important;\n}\n\n/* The Models pane shows the custom model list and the catalogue, and two\n hairlines used to divide it: the \"Customized\" group's separator from the key\n field (which this pane hides) and the catalogue's own, drawn directly above\n its \"Model catalogue\" heading. Neither has anything to divide here \u2014 the tab\n bar already ends the pane above, and the divider between the two lists above\n the catalogue is the group's boundary, not a rule. Both go, with every top\n padding that held content off them (the group's 10px, its body's 12px, the\n catalogue's 12px), so the pane runs from the tab bar into the catalogue with\n only the official editor's own 14px gap between blocks. */\n.bre-tabbed[data-bre-tab='models'] [data-bre-editor-body] details[class*='customized'] {\n border-top: none;\n padding-top: 0;\n}\n\n.bre-tabbed[data-bre-tab='models'] [data-bre-editor-body] details[class*='customized'] > [class*='customizedBody'] {\n padding-top: 0;\n}\n\n/* The catalogue is a section (its heading, title and meta are the divs inside\n it), so this probe hits the ruled box and nothing else. */\n.bre-tabbed[data-bre-tab='models'] [data-bre-editor-body] section[class*='modelCatalog'] {\n border-top: none;\n padding-top: 0;\n}\n\n/* The catalogue heading names a box this pane has already named for the user:\n the Models tab IS the catalogue, so \"\u6A21\u578B\u76EE\u5F55\" says it twice, and\n \"\u5DF2\u81EA\u5B9A\u4E49\u6A21\u578B\u76EE\u5F55\" (the meta line beside it) says nothing at all \u2014 the rows\n below ARE the customized catalogue. Both strings are the host's own, so they\n go by class stem, and the heading takes its title and meta with it. The\n head's own controls (\u6062\u590D\u9ED8\u8BA4\u6A21\u578B / \u83B7\u53D6\u53EF\u7528\u6A21\u578B) are siblings of the heading,\n not children, so they stay \u2014 the auto-adapt seat is anchored to them. */\n.bre-tabbed[data-bre-tab='models'] [data-bre-editor-body] [class*='modelCatalogHeading'] {\n display: none;\n}\n\n/* With the heading gone the head holds only controls, and they read as TWO\n ENDS: \u6062\u590D\u9ED8\u8BA4\u6A21\u578B stays at the left edge while \u83B7\u53D6\u53EF\u7528\u6A21\u578B keeps the\n auto-adapt seat beside it at the far right \u2014 the seat belongs to that link's\n group, it is never a column of its own. The official space-between cannot\n express that once a third child exists: it spreads the three evenly, which\n strands the fetch link in the middle of the card. So the free space is handed\n to the fetch link's own leading auto margin instead \u2014 an auto margin absorbs\n the space BEFORE justify-content is consulted, whatever the official rule\n says. The fetch link is the button the seat sits after, which is also the\n only control present when the provider is not overridden: the pair then\n simply holds the right edge. align-items centres the taller host links\n against the seat. */\n.bre-tabbed[data-bre-tab='models'] [data-bre-editor-body] [class*='modelListHead'] {\n align-items: center;\n}\n.bre-tabbed[data-bre-tab='models'] [data-bre-editor-body] [class*='modelListHead'] > button:has(+ .bre-auto-effort) {\n margin-left: auto;\n}\n\n/* The click's answer, as a bubble at the top of the window that takes itself\n away again. The control is left alone on purpose: its label is what the user\n aimed at, and a button that rewrites itself is a button that moved. The\n bubble is a fixed overlay on the body \u2014 not a child of the head, which is the\n host's flex row and would take a fourth item \u2014 and it clears the settings\n dialog's opaque overlay for the same reason the drop line does. It follows\n the host's own toast in placement and tone, but not in z-index: the settings\n dialog's overlay outranks the toast layer, and this bubble is raised from\n inside that dialog. Pointer-events none keeps it out of the way, the phase\n colours the headline so the outcome reads before the words do, and the\n hold/fade pair is timed by --bre-note-hold, which the script sets from the\n same constant it uses for the dismissal. The fade is counted INSIDE that\n hold, so the bubble is gone when the clock says it is, rather than fading\n after it. */\n.bre-auto-effort-note {\n position: fixed;\n top: 40px;\n left: 50%;\n display: flex;\n flex-direction: column;\n gap: 2px;\n width: max-content;\n max-width: min(640px, calc(100vw - 48px));\n padding: 10px 14px;\n border-radius: 10px;\n background: var(--dsw-alias-toast-bg, #2b2f36);\n color: var(--dsw-alias-toast-label, #f9fafb);\n box-shadow: var(--dsw-shadow-lv3, 0 6px 24px #00000029);\n font-size: 13px;\n line-height: 20px;\n pointer-events: none;\n transform: translateX(-50%);\n animation: bre-note-in 160ms ease-out, bre-note-out 400ms ease calc(var(--bre-note-hold, 3000ms) - 400ms) forwards;\n z-index: 1200;\n}\n/* A pass in flight has no lifetime of its own: it is replaced by its verdict,\n which restarts both animations because the phase changes the rule. */\n.bre-auto-effort-note[data-bre-auto-effort-phase='working'] {\n animation: bre-note-in 160ms ease-out;\n}\n@keyframes bre-note-in {\n from { opacity: 0; transform: translate(-50%, -6px); }\n to { opacity: 1; transform: translate(-50%, 0); }\n}\n@keyframes bre-note-out {\n to { opacity: 0; visibility: hidden; }\n}\n@media (prefers-reduced-motion: reduce) {\n .bre-auto-effort-note {\n animation: bre-note-out 400ms ease calc(var(--bre-note-hold, 3000ms) - 400ms) forwards;\n }\n .bre-auto-effort-note[data-bre-auto-effort-phase='working'] { animation: none; }\n}\n.bre-auto-effort-note-text { font-weight: 500; }\n.bre-auto-effort-note-detail { opacity: .72; }\n/* The attribute is the visibility switch, so it must beat the flex column above. */\n.bre-auto-effort-note-detail[hidden] { display: none; }\n.bre-auto-effort-note[data-bre-auto-effort-phase='working'] .bre-auto-effort-note-text {\n color: var(--dsw-alias-state-business-primary, #6ea8fe);\n}\n.bre-auto-effort-note[data-bre-auto-effort-phase='done'] .bre-auto-effort-note-text {\n color: var(--dsw-alias-state-success-primary, #22c55e);\n}\n.bre-auto-effort-note[data-bre-auto-effort-phase='failed'] .bre-auto-effort-note-text {\n color: var(--dsw-alias-state-error-secondary, #ff8a80);\n}\n\n/* The API-key eye. The official key field is a plain flex column whose input is\n its last in-flow child, so the button hangs on the field's own box and is\n measured against the input's 32px row \u2014 the official markup offers no\n wrapper, and introducing one would mean moving DOM the host owns. The input\n keeps the room the button needs. */\n.bre-key-field { position: relative; }\n\n.bre-key-field > input { padding-right: 34px; }\n\n/* A field the plugin's key list manages hands its input over: the list below\n says the same thing once per key and keeps the pick, the alias and the value\n in one place, so an official box that writes the provider's single key would\n be a second, conflicting way to do it. The input stays in the DOM \u2014 the\n host's form still owns it and the reveal pass still finds it \u2014 so unmanaging\n the field brings the official box back. The marker is what the reveal pass\n stamps on that input; the direct-child form covers the pass that has not run\n yet, and the eye is the button the same pass hangs on the field. */\n.bre-key-managed [data-bre-key-input],\n.bre-key-managed > input,\n.bre-key-managed .bre-key-eye { display: none; }\n\n/* A field that failed validation grows a message line under the input; the eye\n rides above it so it stays on the input it belongs to (18px line + 6px gap). */\n.bre-key-field:has(> p) .bre-key-eye { bottom: 24px; }\n\n.bre-key-eye {\n position: absolute;\n right: 4px;\n bottom: 0;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 32px;\n padding: 0;\n border: 0;\n border-radius: 8px;\n background: transparent;\n color: var(--dsw-alias-label-secondary, #6b7280);\n cursor: pointer;\n -webkit-appearance: none;\n appearance: none;\n transition: background 140ms ease, color 140ms ease;\n}\n\n.bre-key-eye:hover {\n color: var(--dsw-alias-label-primary, #0f1115);\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n.bre-key-eye:focus-visible {\n outline: none;\n box-shadow: inset 0 0 0 2px var(--dsw-alias-border-l3);\n}\n\n/* Resolving a stored key is a same-origin round trip, so the button can be\n busy: it stays in place and dims rather than changing size or disappearing. */\n.bre-key-eye[aria-busy='true'] {\n opacity: 0.55;\n cursor: progress;\n}\n\n.bre-key-eye[hidden] { display: none; }\n\n/* One glyph at a time. The state lives on the button's own attribute, so a\n click swaps the icon without the host re-rendering anything. */\n.bre-key-eye svg[data-bre-icon] { display: none; }\n\n.bre-key-eye[aria-pressed='false'] svg[data-bre-icon='eye'],\n.bre-key-eye[aria-pressed='true'] svg[data-bre-icon='eye-off'] { display: block; }\n\n/* The key list. The plugin mounts it into its own host, a sibling placed right\n after the official key field, so the list sits under the input without\n changing the field's own box: the reveal eye is measured against that box\n (bottom: 0), and a list inside the field would drag the eye down onto the\n list's last row. The margin only covers the case where the card spaces its\n fields with margins instead of a gap. */\n.bre-keys-host {\n display: block;\n min-width: 0;\n margin-top: 4px;\n}\n\n.bre-keys { display: flex; flex-direction: column; gap: 8px; }\n\n/* While the key list is still reading, the panel renders NOTHING (the component\n returns an empty .bre-keys). An empty ruled box \u2014 or a placeholder line that\n then swaps to the list \u2014 would paint one frame of a panel that then visibly\n changes: the \"the key section flashes when you press Edit\" report. :empty\n takes the panel out of the layout until the first answer lands, so the list\n is the first thing the key area ever paints, and nothing under it moves. */\n.bre-keys:empty { display: none; }\n\n/* One ruled container holding the keys and the add entry, the way a settings\n list reads: a single hairline box, and hairlines between rows only. */\n.bre-keys-list {\n display: flex;\n flex-direction: column;\n margin: 0;\n padding: 0;\n border: 0.5px solid var(--dsw-alias-border-l2, #d5d7dd);\n border-radius: 8px;\n list-style: none;\n overflow: hidden;\n}\n\n.bre-keys-list > li + li { border-top: 0.5px solid var(--dsw-alias-border-l2, #d5d7dd); }\n\n.bre-keys-row {\n display: flex;\n align-items: center;\n gap: 8px;\n min-width: 0;\n padding: 9px 10px;\n}\n\n/* The enable choice closes the row. The radio stays the real input \u2014 so the\n keys of one provider are one group, choosing one clears the rest, and the\n pick is keyboard-reachable \u2014 and the circle is its face; the word beside it\n names the action instead of a state to hunt for. */\n.bre-keys-enable {\n display: inline-flex;\n flex: 0 0 auto;\n align-items: center;\n gap: 6px;\n cursor: pointer;\n}\n\n.bre-keys-enable-text {\n font-size: 11px;\n line-height: 1.5;\n color: var(--dsw-alias-label-secondary, #6b7280);\n}\n\n.bre-keys-enable:has(.bre-keys-enabled:checked) .bre-keys-enable-text {\n color: var(--dsw-alias-label-primary, #0f1115);\n}\n\n.bre-keys-enabled {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n border: 0;\n overflow: hidden;\n white-space: nowrap;\n clip-path: inset(50%);\n}\n\n.bre-keys-mark {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 16px;\n height: 16px;\n box-sizing: border-box;\n border: 1px solid var(--dsw-alias-border-l3, #9aa0aa);\n border-radius: 50%;\n color: var(--dsw-alias-bg-layer-1, #ffffff);\n transition: background 120ms ease, border-color 120ms ease;\n}\n\n.bre-keys-mark > svg { width: 12px; height: 12px; }\n\n.bre-keys-enabled:checked + .bre-keys-mark {\n border-color: var(--dsw-alias-label-primary, #0f1115);\n background: var(--dsw-alias-label-primary, #0f1115);\n}\n\n.bre-keys-enabled:focus-visible + .bre-keys-mark {\n outline: none;\n box-shadow: 0 0 0 2px var(--dsw-alias-brand-primary, #3b82f6);\n}\n\n/* With an alias the alias is the row's name and the masked key follows it as a\n quiet code pill; without one the masked key is the name and takes the row's\n measure, so a key is always readable as itself. */\n.bre-keys-alias {\n flex: 0 1 auto;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 12px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary, #0f1115);\n}\n\n.bre-keys-mask {\n flex: 0 1 auto;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n padding: 1px 6px;\n border-radius: 4px;\n background: var(--dsw-alias-interactive-bg-hover, rgba(127, 127, 127, 0.08));\n color: var(--dsw-alias-label-secondary, #6b7280);\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 11px;\n}\n\n.bre-keys-mask-plain {\n flex: 1 1 auto;\n padding: 0;\n background: transparent;\n color: var(--dsw-alias-label-primary, #0f1115);\n font-family: inherit;\n font-size: 12px;\n}\n\n/* The row's three actions, revealed on hover or keyboard focus rather than\n removed, so the row keeps its width and nothing shifts under the pointer.\n Revealing is opacity alone; a pointer that cannot hover simply always sees\n them, because a hidden action there would be an unreachable one. */\n.bre-keys-actions {\n display: inline-flex;\n flex: 0 0 auto;\n align-items: center;\n gap: 10px;\n margin-left: auto;\n opacity: 0;\n pointer-events: none;\n transition: opacity 120ms ease;\n}\n\n.bre-keys-row:hover .bre-keys-actions,\n.bre-keys-row:focus-within .bre-keys-actions {\n opacity: 1;\n pointer-events: auto;\n}\n\n@media (hover: none) {\n .bre-keys-actions { opacity: 1; pointer-events: auto; }\n}\n\n.bre-keys-danger { color: var(--dsw-alias-state-error-primary, #c62828); }\n\n/* The one inline form. It opens under the row it edits, or \u2014 for a new key \u2014 as\n the list's own last item, which is what keeps adding a key in place instead\n of in a dialog that has to be found and dismissed. */\n.bre-keys-form-row { padding: 10px; }\n\n.bre-keys-form { display: flex; flex-direction: column; gap: 8px; }\n\n.bre-keys-form-fields {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 8px;\n}\n\n/* One field means the whole row: the value form holds only the secret, and it\n must not sit in half a row beside an empty half. */\n.bre-keys-form-fields > :only-child { grid-column: 1 / -1; }\n\n/* The shared input caps itself at 240px for the panels it was written for; here\n the field is the measure. */\n.bre-keys-form-fields .bre-text-input { max-width: none; }\n\n.bre-keys-form-actions {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 8px;\n}\n\n/* The shared primary button is styled inside the headers editor, which is not\n where this form lives, so the key list carries its own compact copy. */\n.bre-keys-form-actions .bre-primary-button {\n height: 28px;\n padding: 0 12px;\n border: 0;\n border-radius: var(--dsw-radius-md, 8px);\n background: var(--dsw-alias-button-primary-fill, #0f1115);\n color: var(--dsw-alias-label-primary-foreground, #ffffff);\n font: inherit;\n font-size: 12px;\n cursor: pointer;\n -webkit-appearance: none;\n appearance: none;\n}\n\n.bre-keys-form-actions .bre-primary-button:hover {\n background: var(--dsw-alias-button-primary-hover, #43454a);\n}\n\n.bre-keys-form-actions .bre-primary-button:disabled { opacity: 0.5; cursor: default; }\n\n.bre-keys-form-actions .bre-primary-button:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--dsw-alias-brand-primary, #3b82f6);\n}\n\n/* The add entry is the list's own last row \u2014 full width, the same shape as the\n keys it adds to \u2014 so the way in and the thing it makes read as one list. */\n.bre-keys-add-row { padding: 0; }\n\n.bre-keys-add {\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n padding: 9px 10px;\n box-sizing: border-box;\n border: 0;\n background: transparent;\n color: var(--dsw-alias-label-secondary, #6b7280);\n font: inherit;\n font-size: 12px;\n text-align: left;\n cursor: pointer;\n -webkit-appearance: none;\n appearance: none;\n transition: background 120ms ease, color 120ms ease;\n}\n\n.bre-keys-add:hover {\n background: var(--dsw-alias-interactive-bg-hover, rgba(127, 127, 127, 0.08));\n color: var(--dsw-alias-label-primary, #0f1115);\n}\n\n.bre-keys-add:focus-visible {\n outline: none;\n box-shadow: inset 0 0 0 2px var(--dsw-alias-brand-primary, #3b82f6);\n}\n\n.bre-keys-add:disabled { opacity: 0.55; cursor: default; }\n\n.bre-keys-add-mark {\n display: inline-flex;\n flex: 0 0 auto;\n align-items: center;\n justify-content: center;\n width: 16px;\n height: 16px;\n}\n\n.bre-keys .bre-effort-message { font-size: 11px; }\n\n@media (prefers-reduced-motion: reduce) {\n .bre-editor-tab { transition: none; }\n .bre-key-eye { transition: none; }\n .bre-keys-mark, .bre-keys-actions, .bre-keys-add { transition: none; }\n}\n`\n", "/**\n * The composer model menu injection: the reasoning-effort slider AND the model\n * search box, both mounted inside the OFFICIAL model menu opened from the\n * bottom-right seat. The official menu body remains host-owned apart from the\n * replicated rows; its trigger keeps its own children and receives only a\n * provider prefix through a plugin-owned data attribute.\n *\n * This module is the single place that decides which pane the menu is showing,\n * where each foreign body sits, and when each one retires. The two pane probes\n * look alike but answer different questions, and must NOT be merged:\n *\n * - the slider's probe \u2014 `[role=\"menuitemradio\"]` \u2014 asks \"did the official\n * drill-in replace my replica body?\". The effort pane matches too, which\n * is fine: our body belongs to the root pane either way.\n * - the search's probe \u2014 `section[role=\"group\"]` \u2014 asks \"is there a\n * filterable model list here?\". The effort pane has no groups, so a\n * role-based probe would mount a search box over a list it cannot filter.\n *\n * @module dsh-model-think-level/client/injection/composer-menu\n */\n\nimport { createElement } from 'react'\nimport { flushSync } from 'react-dom'\nimport type { ReactNode } from 'react'\nimport type { Translate } from '@deepseek-ai/dsh-client-ui-slots'\nimport { PLUGIN_ID } from '../../constants.js'\nimport { ComposerSlider, providerLabelOf } from '../ComposerSlider.js'\nimport { ModelSearch, isModelPane } from '../ModelSearch.js'\nimport type { ModelDirectoryLike } from '../types.js'\nimport { EffortBoundary, mountReact, unmountReact, type ForeignMount } from './mount.js'\n\n/** Everything the composer injection needs from its host. */\nexport interface ComposerMenuDeps {\n /** The official menu element, or undefined while it is closed. */\n menuOf: () => HTMLElement | undefined\n /** The official model trigger, present even while the menu is closed. */\n triggerOf: () => HTMLElement | undefined\n /** The plugin's locale-bound translator. */\n t: Translate\n /** Wrap a subtree so it re-translates on a language switch. */\n refreshed: (children: () => ReactNode) => ReactNode\n /** Whether the composer slider preference is on. */\n sliderEnabled: () => boolean\n /** The current session's model directory, or undefined in the boot window. */\n directory: () => ModelDirectoryLike | undefined\n}\n\n/** The composer injection's face: one reconcile per scan, one dispose per fiber. */\nexport interface ComposerMenuInjection {\n /** Mount/reposition/retire both foreign roots (idempotent, mutation-cheap). */\n reconcile: () => void\n /** Unmount both roots; the fiber is going away. */\n dispose: () => void\n}\n\n/**\n * Re-run the official placement inside the CURRENT frame.\n *\n * The card anchors itself in a layout effect (ModelSelect.tsx:163-188) that\n * runs BEFORE this module's MutationObserver injection, so its `top` is\n * computed from the PRE-injection height: the slider replica is still shown\n * and the search box not yet mounted, which floated the card ~56px above the\n * trigger. Both edits are done synchronously by the callers (the injected\n * roots commit inside `flushSync`, the replica is hidden inline), so this\n * flushSync-wrapped `resize` lets the official `place()` re-measure and commit\n * BEFORE the browser paints \u2014 no intermediate frame is ever shown. Waiting for\n * a later frame (the previous implementation) is exactly what made the menu\n * visibly jump.\n *\n * Only the official effect ever writes the card's position: this module still\n * adds no class and no inline style of its own.\n *\n * COUPLING (deliberate, and the reason there is no geometry code here): this\n * works only because the official card re-runs its `place()` on a window\n * `resize` and commits the result through React state. A kernel that switches\n * to `requestAnimationFrame` placement or writes the position outside React\n * would leave this a no-op \u2014 the menu would keep the pre-injection `top`\n * instead of jumping, i.e. it would degrade back to the mis-anchored (not\n * broken) state the earlier fixes describe, never to a crash.\n */\nconst rePlaceInFrame = (): void => {\n flushSync(() => { window.dispatchEvent(new Event('resize')) })\n}\n\n/**\n * Build the composer menu injection.\n * @param deps - menu lookup, copy, locale refresh, preference and directory.\n * @returns the injection's {@link ComposerMenuInjection} face.\n */\nexport function createComposerMenu(deps: ComposerMenuDeps): ComposerMenuInjection {\n const { menuOf, triggerOf, t, refreshed, sliderEnabled, directory } = deps\n let sliderMount: ForeignMount | undefined\n let searchMount: ForeignMount | undefined\n let lastModelPane: boolean | undefined\n let triggerDirectory: ModelDirectoryLike | undefined\n let triggerUnsubscribe: (() => void) | undefined\n let decoratedTrigger: HTMLElement | undefined\n\n const clearTriggerDecoration = (): void => {\n decoratedTrigger?.removeAttribute('data-bre-provider')\n decoratedTrigger = undefined\n }\n\n /** Update the provider prefix without changing the host-owned trigger children. */\n const updateTriggerDecoration = (): void => {\n const trigger = triggerOf()\n if (decoratedTrigger !== undefined && decoratedTrigger !== trigger) {\n clearTriggerDecoration()\n }\n const state = triggerDirectory?.store.getSnapshot()\n const current = state?.current\n if (trigger === undefined || current === null || current === undefined) {\n if (trigger === decoratedTrigger) clearTriggerDecoration()\n return\n }\n const group = state?.groups.find(candidate => candidate.id === current.provider)\n const provider = group === undefined ? current.provider : providerLabelOf(group)\n if (provider.trim() === '') {\n if (trigger === decoratedTrigger) clearTriggerDecoration()\n return\n }\n trigger.dataset['breProvider'] = provider\n decoratedTrigger = trigger\n }\n\n /** Follow whichever session directory the official composer currently owns. */\n const syncTriggerDirectory = (): void => {\n const next = directory()\n if (next !== triggerDirectory) {\n triggerUnsubscribe?.()\n triggerUnsubscribe = undefined\n triggerDirectory = next\n if (next !== undefined) triggerUnsubscribe = next.store.subscribe(updateTriggerDecoration)\n }\n updateTriggerDecoration()\n }\n\n const unmountSearch = (): void => {\n unmountReact(searchMount)\n searchMount = undefined\n }\n\n /**\n * Reconcile the search box into the official model menu (idempotent).\n *\n * Deliberately NO class on the menu and NO width/height of our own: the\n * official card measures itself in a layout effect whose deps\n * ([open, pane, state], ModelSelect.tsx:163-188) a later injection cannot\n * invalidate, so any resize from here would leave the card anchored to its\n * pre-injection height (and no longer right-aligned with its trigger). The\n * official box already caps itself (max-height) and scrolls its groups\n * container, so it needs no help.\n */\n const reconcileSearch = (menu: HTMLElement): void => {\n // Positioned from STRUCTURE, not class names: the group sections' parent\n // IS the official scrolling container, while the `groups` class on it is a\n // CSS-module hash (`Uc5hea_groups`) that a literal `.groups` selector never\n // matches. Inserting before that container keeps the official load notices\n // on top and leaves the slider wrapper as the menu's first child.\n const group = menu.querySelector<HTMLElement>('section[role=\"group\"]')\n const container = group?.parentElement\n const referenceNode = (container !== null && container !== undefined && container.parentElement === menu)\n ? container\n : group\n\n if (searchMount === undefined) {\n const wrapper = document.createElement('div')\n wrapper.dataset['plugin'] = PLUGIN_ID\n wrapper.dataset['breSearch'] = '1'\n menu.insertBefore(wrapper, referenceNode)\n searchMount = mountReact(\n wrapper,\n createElement(EffortBoundary, {\n fallbackText: t('modelSearchFailed'),\n children: refreshed(() => createElement(ModelSearch, { menu, t })),\n }),\n // Sync: the card is measured right after this, so the input must\n // already contribute its height (see rePlaceInFrame).\n { sync: true },\n )\n return\n }\n if (searchMount.wrapper.parentElement !== menu) {\n menu.insertBefore(searchMount.wrapper, referenceNode)\n return\n }\n if (referenceNode !== null && searchMount.wrapper.nextSibling !== referenceNode) {\n menu.insertBefore(searchMount.wrapper, referenceNode)\n }\n }\n\n /** Reconcile the slider into the official model menu (idempotent). */\n const reconcileSliderInto = (menu: HTMLElement): void => {\n if (!sliderEnabled()) {\n unmountReact(sliderMount)\n sliderMount = undefined\n // The preference flipped off while the menu stayed open: the host menu\n // must go back to the official content-sized box AND its root cells\n // must become visible again (the active branch hides them inline).\n menu.classList.remove('bre-model-menu-host')\n for (const el of Array.from(menu.children)) {\n // Attribute-only probe on purpose: `instanceof HTMLButtonElement` is\n // realm-bound and silently fails when the official card is rendered\n // from another realm (it then never matched, so the root cells stayed\n // hidden after the preference was switched off).\n if (el.getAttribute('role') === 'menuitem') {\n // `menu.children` is an Element collection; the cells are buttons.\n ;(el as HTMLElement).style.display = ''\n }\n }\n return\n }\n const resolved = directory()\n if (resolved === undefined) {\n // Transient boot window only: the seat itself cannot mount before the\n // directory service resolves, so a menu open here is momentary. The\n // next DOM mutation re-enters (the mounted slider keeps its face).\n return\n }\n // React re-renders the menu around a foreign node; keep our wrapper as the\n // menu's FIRST child by re-inserting it whenever a re-render displaced it.\n // The React root survives DOM moves (the fiber tree is position-independent),\n // so a displaced slider is re-attached, never re-created.\n if (sliderMount === undefined) {\n const wrapper = document.createElement('div')\n wrapper.dataset['plugin'] = PLUGIN_ID\n wrapper.dataset['breSlider'] = '1'\n menu.insertBefore(wrapper, menu.firstChild)\n sliderMount = mountReact(wrapper,\n createElement(EffortBoundary, {\n fallbackText: t('renderFailed'),\n children: refreshed(() => createElement(ComposerSlider, {\n directory: resolved,\n t,\n // The replicated body renders its OWN model and level lists, so it\n // never has to hand control back to the official drill-in pane \u2014\n // that is what lets picking a model collapse only the list while\n // the popover (and the still-unpicked level row) stays open.\n // It only needs the card re-measured whenever its height changes:\n // the official card measures from its own layout effect and only\n // re-runs on a window resize, which rePlaceInFrame dispatches.\n onLayoutChange: rePlaceInFrame,\n })),\n }),\n // Sync: the replica changes the menu's height, so it must be committed\n // before rePlaceInFrame re-measures (same-frame, no visible jump).\n { sync: true },\n )\n } else if (sliderMount.wrapper.parentElement !== menu || menu.firstChild !== sliderMount.wrapper) {\n menu.insertBefore(sliderMount.wrapper, menu.firstChild)\n }\n // The replicated popover body takes the upstream .re-model-menu box.\n menu.classList.add('bre-model-menu-host')\n // Popover replication rules, mirroring upstream's pane behaviour:\n // - ROOT pane: our body (slider + separator + model row) IS the content;\n // the official menu's root cells (role=menuitem direct children \u2014\n // \"Model\" and \"Effort\") are duplicated and hidden. A pane switch\n // re-creates them and the next scan re-applies.\n // - Model-pane drill-in (menuitemradio rows): upstream shows ONLY the\n // pane, so our body retires while the official list is open.\n // Known tradeoff (a11y): the official shell's Arrow-key roving focus walks\n // the hidden cells \u2014 focus() on a display:none node is a no-op \u2014 so\n // keyboard users reach the replica via Tab; the replica row's Enter opens\n // the official model list, keeping model switching keyboard-reachable.\n const cells = Array.from(menu.children)\n .filter((el): el is HTMLElement => el.getAttribute('role') === 'menuitem')\n // Only a NON-descendant radio counts as the official drill-in. A plain\n // descendant test would be tripped by any menuitemradio the replica itself\n // renders, which would make the replica hide itself. The wrapper is hoisted\n // into a const first: `sliderMount`'s narrowing does not survive into the\n // predicate's closure.\n const wrapper = sliderMount.wrapper\n const officialPane = Array.from(menu.querySelectorAll('[role=\"menuitemradio\"]'))\n .some(el => !wrapper.contains(el))\n if (officialPane) {\n // Focus handoff BEFORE hiding: the replica row (possibly focused) is\n // about to become display:none. Without moving focus first the browser\n // drops it to <body>, the official shell's onBlur reads that as \"focus\n // left the seat\" and closes the menu instantly \u2014 the flash-out that\n // made the model list unusable. The menu itself sits inside the seat\n // root, so focusing it keeps the blur inside (tabindex -1: no Tab\n // stop, no focus ring for programmatic focus).\n if (sliderMount.wrapper.contains(document.activeElement)) {\n menu.tabIndex = -1\n menu.focus({ preventScroll: true })\n }\n sliderMount.wrapper.style.display = 'none'\n } else {\n sliderMount.wrapper.style.display = ''\n for (const cell of cells) cell.style.display = 'none'\n }\n }\n\n const reconcile = (): void => {\n syncTriggerDirectory()\n const menu = menuOf()\n if (menu === undefined) {\n unmountReact(sliderMount)\n sliderMount = undefined\n unmountSearch()\n lastModelPane = undefined\n return\n }\n // The search box is unconditional: it does not follow the slider\n // preference (see the spec's \"\u641C\u7D22\u6052\u5F00\" decision).\n const modelPane = isModelPane(menu)\n if (modelPane) reconcileSearch(menu)\n else unmountSearch()\n\n reconcileSliderInto(menu)\n\n // A pane switch is exactly when this module rewrites the menu's contents\n // (the slider replica hides, the search box mounts or unmounts), so the\n // card's already-computed `top` is stale. Both edits above are synchronous\n // (the roots commit inside flushSync, the replica toggles inline), so the\n // official placement can be re-run in THIS frame \u2014 no jump is ever painted.\n // Filtering and scrolling only toggle row visibility and never reach here.\n if (modelPane !== lastModelPane) {\n lastModelPane = modelPane\n if (modelPane || sliderEnabled()) rePlaceInFrame()\n }\n }\n\n const dispose = (): void => {\n triggerUnsubscribe?.()\n triggerUnsubscribe = undefined\n triggerDirectory = undefined\n clearTriggerDecoration()\n unmountReact(sliderMount)\n sliderMount = undefined\n unmountSearch()\n lastModelPane = undefined\n }\n\n return { reconcile, dispose }\n}\n", "/**\n * The replicated composer popover body: a \"model\" row and a \"reasoning level\"\n * row, both driven from the official model directory.\n *\n * Presentation follows `dsh-tauri-model-config`: one bordered card, a chevron\n * that rotates as its panel opens, and a selectable list rendered under the row\n * it belongs to. The model list is a provider column with the models of the\n * selected provider beside it. The radiation slider this module shipped with\n * upstream is gone \u2014 it was reported as ugly and the reference plugin has none.\n *\n * Four invariants are load-bearing and must survive any edit:\n * - the body is mounted INSIDE the official model menu, and the shell closes\n * that menu on any blur that leaves the card, so every commit hands focus to\n * the menu BEFORE the busy state disables the control that was clicked\n * (issue #13's snap-shut);\n * - the levels always come from the model the directory currently reports,\n * never from a cached label, so a model switch re-reads the ladder;\n * - picking a model must NOT close the popover: the reasoning level below is\n * still unpicked, so the list collapses and the rows stay;\n * - a panel and the card's re-measure must land in the SAME frame (see\n * {@link ComposerSlider}'s `togglePanel`), or the panel is painted once at\n * the stale position and the menu visibly jumps.\n *\n * @module dsh-model-think-level/client/ComposerSlider\n */\nimport {\n createElement,\n useCallback,\n useEffect,\n useRef,\n useState,\n useSyncExternalStore,\n} from 'react'\nimport { flushSync } from 'react-dom'\nimport type { ReactNode } from 'react'\nimport { inferModalitiesFromName, matchKnowledgeBase } from '../knowledge.js'\nimport { selectRefusalMessage } from './effort-memory.js'\nimport {\n disabledProviders,\n subscribeDisabledProviders,\n} from './provider-enabled.js'\nimport {\n baseProviderOrder,\n mergeProviderOrder,\n providerOrder,\n subscribeProviderOrder,\n} from './provider-order.js'\nimport {\n modelOrders,\n orderModelEntries,\n subscribeModelOrders,\n} from './model-order.js'\nimport type {\n DirectoryCurrentLike,\n DirectoryGroupLike,\n EffortLevelLike,\n ModelDirectoryLike,\n ModelDirectoryStateLike,\n} from './types.js'\n\n/** The model's declared effort levels, when there are at least two to choose from. */\nexport function sliderLevels(state: ModelDirectoryStateLike): readonly EffortLevelLike[] {\n if (state.current === null) return []\n const group = state.groups.find(candidate => candidate.id === state.current?.provider)\n const model = group?.models.find(candidate => candidate.id === state.current?.model)\n const efforts = model?.reasoning?.efforts\n return efforts !== undefined && efforts.length >= 2 ? efforts : []\n}\n\n/** The current model of a directory snapshot. */\nexport function currentModelOf(state: ModelDirectoryStateLike): DirectoryCurrentLike | null {\n return state.current\n}\n\n/**\n * Level index the body should report as current: the session's current effort\n * when the model still offers it, else the adapter default, else the middle.\n */\nexport function effectiveEffortIndex(\n levels: readonly EffortLevelLike[],\n state: ModelDirectoryStateLike,\n): number {\n const model = currentModelOf(state)\n const current = levels.findIndex(level => level.id === model?.reasoningEffort)\n if (current >= 0) return current\n const group = state.groups.find(candidate => candidate.id === model?.provider)\n const fallback = group?.models.find(candidate => candidate.id === model?.model)\n ?.reasoning?.defaultEffort\n const at = fallback === undefined ? -1 : levels.findIndex(level => level.id === fallback)\n if (at >= 0) return at\n return Math.floor((levels.length - 1) / 2)\n}\n\n/** The plugin's bound translator (the props face, named for reuse below). */\ntype Translator = (key: string, params?: Record<string, string | number>) => string\n\n/** Props of {@link ComposerSlider}. */\nexport interface ComposerSliderProps {\n /** The session's shared model directory (load + select ride the official seam). */\n directory: ModelDirectoryLike\n /** Localized copy (the plugin's bound translator). */\n t: Translator\n /**\n * Re-measure hook. The official card measures itself from its own layout\n * effect (`[open, pane, state]`) and only re-runs on a window `resize`, so a\n * body that grows here must ask the injector to re-place it \u2014 in the same\n * frame, never from a deferred task.\n */\n onLayoutChange?: () => void\n}\n\n/** Which row has its list open. At most one at a time. */\ntype Panel = 'model' | 'effort' | null\n\n/** A model row of one provider group, as the directory exposes it. */\ninterface ModelChoiceLike {\n id: string\n name?: string\n /** The declared effort ladder; present when the model reasons at all. */\n reasoning?: { efforts?: readonly unknown[]; defaultEffort?: string }\n /** Declared request modalities in core vocabulary (`image` = vision). */\n input?: readonly string[]\n}\n\n/** The models of one provider group (the SDK type leaves the element open). */\nfunction modelsOf(group: DirectoryGroupLike): readonly ModelChoiceLike[] {\n return (group.models ?? []) as unknown as readonly ModelChoiceLike[]\n}\n\n/** Provider label: the SDK face names it differently across kernel lines. */\nexport function providerLabelOf(group: DirectoryGroupLike): string {\n const named = group as unknown as { name?: string; label?: string; title?: string }\n return named.name ?? named.label ?? named.title ?? group.id\n}\n\n/** The chevron marking a row as drilling into a list; rotates while open. */\nfunction chevron(open: boolean): ReactNode {\n return createElement(\n 'span',\n { className: 'bre-row-chevron' + (open ? ' is-open' : ''), 'aria-hidden': true },\n '\u203A',\n )\n}\n\n/** The check marking the current entry of an open list. */\nfunction checkIcon(): ReactNode {\n return createElement(\n 'svg',\n {\n className: 'bre-option-check',\n width: 14,\n height: 14,\n viewBox: '0 0 16 16',\n fill: 'none',\n 'aria-hidden': true,\n },\n createElement('path', {\n d: 'M3.5 8.5l3 3 6-7',\n stroke: 'currentColor',\n strokeWidth: 1.6,\n strokeLinecap: 'round',\n strokeLinejoin: 'round',\n }),\n )\n}\n\n/**\n * Capability badge caches. The knowledge lookup is a linear scan with boundary\n * regexes; the model list re-renders on every panel toggle, so the answer is\n * memoized per model id.\n */\nconst VISION_CACHE = new Map<string, boolean>()\n\n/**\n * Whether the model accepts image input. The declaration the directory carries\n * wins; a model that declares nothing falls back to the plugin's own knowledge\n * base and then to its name heuristic \u2014 the same tiers the suggestion engine\n * uses, so the badge can never be more confident than the suggestion it would\n * write.\n */\nfunction supportsVision(model: ModelChoiceLike): boolean {\n if (Array.isArray(model.input)) return model.input.includes('image')\n const cached = VISION_CACHE.get(model.id)\n if (cached !== undefined) return cached\n const known = matchKnowledgeBase(model.id, model.name)?.input\n const verdict = known !== undefined\n ? known.includes('image')\n : (inferModalitiesFromName(model.id) ?? []).includes('image')\n VISION_CACHE.set(model.id, verdict)\n return verdict\n}\n\n/** Whether the model reasons at all (any declared effort level). */\nfunction supportsThinking(model: ModelChoiceLike): boolean {\n const efforts = model.reasoning?.efforts\n return Array.isArray(efforts) && efforts.length > 0\n}\n\n/** The image-input badge: an eye, shown only when the model accepts images. */\nfunction visionIcon(label: string): ReactNode {\n return createElement(\n 'svg',\n {\n className: 'bre-tag-icon',\n width: 13,\n height: 13,\n viewBox: '0 0 16 16',\n fill: 'none',\n role: 'img',\n 'aria-label': label,\n },\n createElement('path', {\n d: 'M1.8 8s2.4-4.2 6.2-4.2S14.2 8 14.2 8s-2.4 4.2-6.2 4.2S1.8 8 1.8 8z',\n stroke: 'currentColor',\n strokeWidth: 1.3,\n strokeLinejoin: 'round',\n }),\n createElement('circle', { cx: 8, cy: 8, r: 1.8, fill: 'currentColor' }),\n )\n}\n\n/** The thinking badge: a sparkle, shown only when the model reasons. */\nfunction thinkingIcon(label: string): ReactNode {\n return createElement(\n 'svg',\n {\n className: 'bre-tag-icon',\n width: 13,\n height: 13,\n viewBox: '0 0 16 16',\n fill: 'none',\n role: 'img',\n 'aria-label': label,\n },\n createElement('path', {\n d: 'M8 1.9l1.35 3.75L13.1 7l-3.75 1.35L8 12.1 6.65 8.35 2.9 7l3.75-1.35z',\n fill: 'currentColor',\n }),\n )\n}\n\n/** The capability badges of one model, or null when it declares neither. */\nfunction modelTags(model: ModelChoiceLike, t: Translator): ReactNode {\n const badges: ReactNode[] = []\n if (supportsVision(model)) {\n badges.push(createElement(\n 'span',\n { key: 'vision', className: 'bre-tag', title: t('tagVision') },\n visionIcon(t('tagVision')),\n ))\n }\n if (supportsThinking(model)) {\n badges.push(createElement(\n 'span',\n { key: 'thinking', className: 'bre-tag', title: t('tagThinking') },\n thinkingIcon(t('tagThinking')),\n ))\n }\n if (badges.length === 0) return null\n return createElement('span', { className: 'bre-option-tags' }, ...badges)\n}\n\n/** The official directory selection input this body speaks. */\ninterface SelectInput {\n provider: string\n model: string\n reasoningEffort?: string\n}\n\n/**\n * Render the model row and the reasoning-level row. Both values come from the\n * store; the commit travels through {@link ModelDirectoryLike.select}\n * (optimistic, rolled back on refusal).\n */\nexport function ComposerSlider(props: ComposerSliderProps): ReactNode {\n const { directory, t } = props\n const layoutChange = props.onLayoutChange\n const state = useSyncExternalStore(\n (notify: () => void) => directory.store.subscribe(notify),\n () => directory.store.getSnapshot(),\n )\n const levels = sliderLevels(state)\n const [effort, setEffort] = useState('')\n const [panel, setPanel] = useState<Panel>(null)\n const [openProvider, setOpenProvider] = useState<string | null>(null)\n const [committing, setCommitting] = useState(false)\n const [localError, setLocalError] = useState<string | null>(null)\n const bodyRef = useRef<HTMLDivElement>(null)\n const committedRef = useRef('')\n const committingRef = useRef(false)\n const available = state.current !== null && levels.length >= 2\n const busy = committing || state.status === 'selecting'\n const error = localError ?? state.error\n const select = directory.select as unknown as (\n input: SelectInput,\n ) => Promise<Parameters<typeof selectRefusalMessage>[0]>\n\n // The store is the single source of truth: every settled snapshot pulls the\n // selection back onto the level the session actually reports. Skipped while a\n // commit is in flight so the optimistic pick is not yanked back mid-round-trip.\n useEffect(() => {\n if (committingRef.current) return\n const index = effectiveEffortIndex(levels, state)\n const next = levels[index]?.id ?? ''\n committedRef.current = next\n setEffort(next)\n setLocalError(null)\n }, [available, levels, state])\n\n useEffect(() => {\n directory.load().catch(() => undefined)\n }, [directory])\n\n const current = state.current\n const group = state.groups.find(candidate => candidate.id === current?.provider)\n const model = group?.models.find(candidate => candidate.id === current?.model)\n const modelName = model?.name ?? (current === null ? t('triggerFallback') : current.model)\n const modelLabel = current === null\n ? modelName\n : providerLabelOf(group ?? ({ id: current.provider } as DirectoryGroupLike)) + ' \u00B7 ' + modelName\n const effortLabel = levels.find(level => level.id === effort)?.name ?? t('effortDefault')\n\n // The provider order set on the Models settings page, where the rows are\n // draggable: the switcher reports the same sequence so the two views never\n // disagree about where a provider lives. Subscribed rather than read once,\n // because a drag on the settings page has to reach an already-open composer.\n const preferredOrder = useSyncExternalStore(subscribeProviderOrder, providerOrder)\n\n // The providers switched off on the Models settings page. Hiding one here is\n // the whole point of the switch, and the trigger decoration still keeps the\n // current provider visible so the selected model remains unambiguous.\n const disabled = useSyncExternalStore(subscribeDisabledProviders, disabledProviders)\n\n // The model order set inside each provider's editor on the settings page: the\n // right column reports the same sequence, per provider. Subscribed for the\n // same reason as the provider order \u2014 a drag has to reach an open composer.\n const modelOrderByProvider = useSyncExternalStore(subscribeModelOrders, modelOrders)\n\n // Providers that actually hold models; the one whose models the right column\n // shows follows the current model until the user picks another column entry.\n const providerCatalog = state.groups\n .filter(candidate => modelsOf(candidate).length > 0 && !disabled.includes(candidate.id))\n .map(candidate => candidate.id)\n const providerIds = mergeProviderOrder(baseProviderOrder(providerCatalog), preferredOrder)\n const shownProvider = openProvider !== null && providerIds.includes(openProvider)\n ? openProvider\n : (current !== null && providerIds.includes(current.provider)\n ? current.provider\n : providerIds[0] ?? null)\n const shownModels = shownProvider === null\n ? []\n : orderModelEntries(\n modelsOf(state.groups.find(candidate => candidate.id === shownProvider) as DirectoryGroupLike),\n modelOrderByProvider[shownProvider] ?? [],\n )\n const activeKey = current === null ? '' : current.provider + '/' + current.model\n\n /**\n * Toggle a list. A panel changes the body's height, and the official card only\n * re-measures on a window `resize`, so BOTH steps must land in one frame:\n * commit the DOM synchronously, then let the injector re-place the card before\n * the browser paints. Deferring the re-measure (the previous `setTimeout`)\n * painted the panel once at the stale position \u2014 the flash that read as a\n * different page.\n */\n const togglePanel = useCallback((next: Panel): void => {\n if (panel === next) return\n flushSync(() => { setPanel(next) })\n layoutChange?.()\n }, [layoutChange, panel])\n\n /** Collapse whichever list is open, re-placing the card in the same frame. */\n const collapsePanel = useCallback((): void => {\n if (panel === null) return\n flushSync(() => { setPanel(null) })\n layoutChange?.()\n }, [layoutChange, panel])\n\n /**\n * Hand focus to the menu before a control this body owns is disabled or\n * unmounted. A focused disabled node drops focus onto <body>, and the shell\n * reads that as \"focus left the seat\" and closes the menu instantly \u2014 the\n * flash-out every commit shipped with (issue #13).\n */\n const focusMenu = useCallback((): void => {\n const menu = bodyRef.current?.closest<HTMLElement>('[role=\"menu\"]')\n if (menu === null || menu === undefined) return\n menu.tabIndex = -1\n menu.focus({ preventScroll: true })\n }, [])\n\n /** Pick a model. Collapses the list; deliberately leaves the popover open. */\n const chooseModel = useCallback(async (provider: string, id: string): Promise<void> => {\n if (committingRef.current) return\n const now = directory.store.getSnapshot().current\n collapsePanel()\n if (now !== null && now.provider === provider && now.model === id) return\n committingRef.current = true\n focusMenu()\n setCommitting(true)\n setLocalError(null)\n try {\n // No reasoningEffort on purpose: a model switch resets the level, and the\n // effort-memory wiretap then restores the one remembered for that model.\n const outcome = await select({ provider, model: id })\n const refusal = selectRefusalMessage(outcome)\n if (refusal !== undefined) throw new Error(refusal)\n } catch (cause) {\n setLocalError(cause instanceof Error ? cause.message : String(cause))\n } finally {\n committingRef.current = false\n setCommitting(false)\n }\n }, [collapsePanel, directory, focusMenu, select])\n\n /** Pick a reasoning level. Collapses the list; leaves the popover open. */\n const chooseEffort = useCallback(async (next: string): Promise<void> => {\n if (committingRef.current) return\n collapsePanel()\n // The row's displayed level can be a PREDICTION rather than a commitment:\n // `effectiveEffortIndex` falls back to the adapter default (or the middle of\n // the ladder) when the session carries no effort, so the FIRST use of a\n // model shows \"Medium\" while the seat trigger still reads Default. Guarding\n // the repick on that displayed level swallowed the very click that would\n // commit it, leaving the level unpickable until another one was chosen.\n // Only the SESSION'S OWN effort is a settled level.\n if (next === state.current?.reasoningEffort && state.status !== 'error') return\n committingRef.current = true\n const previous = committedRef.current\n focusMenu()\n setCommitting(true)\n setLocalError(null)\n // Optimistic pick keeps the row responsive.\n setEffort(next)\n\n try {\n const now = state.current\n if (now === null) throw new Error(t('sliderNoCurrent'))\n const outcome = await select({\n provider: now.provider,\n model: now.model,\n reasoningEffort: next,\n })\n // Kernels through 0.1.6-alpha.1 reject by throwing; 0.1.6-alpha.2\n // resolves the refusal result instead. Normalizing it into a throw keeps\n // the optimistic rollback and the in-menu error line identical.\n const refusal = selectRefusalMessage(outcome)\n if (refusal !== undefined) throw new Error(refusal)\n\n const snapshot = directory.store.getSnapshot()\n const accepted = levels.find(level => level.id === snapshot.current?.reasoningEffort)?.id\n const settled = accepted ?? next\n committedRef.current = settled\n setEffort(settled)\n } catch (cause) {\n committedRef.current = previous\n setEffort(previous)\n setLocalError(cause instanceof Error ? cause.message : String(cause))\n } finally {\n committingRef.current = false\n setCommitting(false)\n }\n }, [collapsePanel, directory, focusMenu, levels, select, state.current, state.status, t])\n\n /** The two rows; each opens its own list underneath. */\n const rows: ReactNode[] = [\n createElement(\n 'button',\n {\n key: 'row:model',\n type: 'button',\n role: 'menuitem',\n className: 'bre-row-control' + (panel === 'model' ? ' is-open' : ''),\n disabled: busy,\n 'aria-expanded': panel === 'model',\n onClick: () => { togglePanel(panel === 'model' ? null : 'model') },\n },\n createElement('span', { className: 'bre-row-label' }, t('modelRowLabel')),\n createElement('span', { className: 'bre-row-value' }, modelLabel),\n chevron(panel === 'model'),\n ),\n // Provider column + the models of the selected provider, side by side. The\n // panel's height is FIXED (see .bre-panel-models): switching provider must\n // not resize the card, or every provider click would need another re-place.\n panel === 'model'\n ? createElement(\n 'div',\n { key: 'panel:model', className: 'bre-panel bre-panel-models' },\n createElement(\n 'div',\n { className: 'bre-providers', role: 'group', 'aria-label': t('modelRowLabel') },\n ...providerIds.map(provider => createElement(\n 'button',\n {\n key: provider,\n type: 'button',\n className: 'bre-provider'\n + (provider === shownProvider ? ' is-open' : '')\n + (provider === current?.provider ? ' is-current' : ''),\n disabled: busy,\n 'aria-expanded': provider === shownProvider,\n onClick: () => { setOpenProvider(provider) },\n },\n createElement(\n 'span',\n { className: 'bre-option-name' },\n providerLabelOf(state.groups.find(candidate => candidate.id === provider) as DirectoryGroupLike),\n ),\n chevron(provider === shownProvider),\n )),\n ...(providerIds.length === 0\n ? [createElement('div', { key: 'no-providers', className: 'bre-empty' }, t('sliderNoCurrent'))]\n : []),\n ),\n createElement(\n 'div',\n { className: 'bre-provider-models', role: 'group', 'aria-label': t('modelRowLabel') },\n ...(shownModels.length === 0\n ? [createElement('div', { key: 'no-models', className: 'bre-empty' }, t('sliderNoCurrent'))]\n : shownModels.map(choice => {\n const key = shownProvider + '/' + choice.id\n const active = key === activeKey\n return createElement(\n 'button',\n {\n key,\n type: 'button',\n className: 'bre-option' + (active ? ' is-active' : ''),\n disabled: busy,\n 'aria-pressed': active,\n onClick: () => { void chooseModel(shownProvider as string, choice.id) },\n },\n createElement('span', { className: 'bre-option-name' }, choice.name ?? choice.id),\n // No tick on a model row: it would sit right beside the\n // capability badges and read as one of them. The accent colour\n // (plus the row tint in CSS) is the whole selection signal.\n modelTags(choice, t),\n )\n })),\n ),\n )\n : null,\n createElement('div', { key: 'divider', className: 'bre-divider', 'aria-hidden': true }),\n createElement(\n 'button',\n {\n key: 'row:effort',\n type: 'button',\n role: 'menuitem',\n className: 'bre-row-control' + (panel === 'effort' ? ' is-open' : ''),\n disabled: busy || !available,\n 'aria-expanded': available ? panel === 'effort' : undefined,\n onClick: () => { togglePanel(panel === 'effort' ? null : 'effort') },\n },\n createElement('span', { className: 'bre-row-label' }, t('effortRowLabel')),\n createElement(\n 'span',\n { className: 'bre-row-value' + (available ? '' : ' is-muted') },\n available ? effortLabel : t('sliderNoLevels'),\n ),\n available ? chevron(panel === 'effort') : null,\n ),\n panel === 'effort' && available\n ? createElement(\n 'div',\n {\n key: 'panel:effort',\n className: 'bre-panel',\n role: 'group',\n 'aria-label': t('effortRowLabel'),\n },\n ...levels.map(level => createElement(\n 'button',\n {\n key: level.id,\n type: 'button',\n className: 'bre-option' + (level.id === effort ? ' is-active' : ''),\n disabled: busy,\n 'aria-pressed': level.id === effort,\n onClick: () => { void chooseEffort(level.id) },\n },\n createElement('span', { className: 'bre-option-name' }, level.name),\n level.id === effort ? checkIcon() : null,\n )),\n )\n : null,\n ]\n\n return createElement(\n 'div',\n { className: 'bre-slider-body', ref: bodyRef },\n createElement('div', { className: 'bre-card' }, ...rows),\n error === null\n ? null\n : createElement('div', { className: 'bre-model-error', role: 'status' }, error),\n )\n}\n", "/**\n * The reasoning-effort + input-modality knowledge base + wire-protocol\n * inference engine.\n *\n * Every model a third-party provider route serves must declare its\n * 'reasoningEfforts' (which DSH thinking levels it accepts, and the exact\n * string to send on the wire for each) before the composer's model picker\n * offers any reasoning control for it. This module answers \"what should this\n * model's declaration be?\" from three sources, in order:\n *\n * 1. An endpoint listing signal (when the route was probed): an explicit\n * \"does not reason\", a modality disclosure, or a context length.\n * 2. A curated knowledge base keyed by model id/name patterns, carrying the\n * exact level set, wire spellings, request modalities, and reference\n * capacities known to work against that family of endpoints.\n * 3. Wire-protocol inference from the route's 'api' / 'baseURL', plus a\n * conservative name heuristic for modalities, for families nothing else\n * names.\n *\n * All outputs stay on the adapter's own vocabulary (THINKING_LEVELS,\n * INPUT_MODALITIES, the reasoningEfforts/input dict shapes) so a suggested\n * declaration can be written to llm-pi-ai settings with no translation step.\n * A suggestion is a *preset*: the user can accept it, tune it, or ignore it.\n * The plugin never silently overwrites a declaration the user already made.\n *\n * @module dsh-model-think-level/knowledge\n */\n\n/** The canonical pi-ai escalation order accepted by llm-pi-ai. */\nexport const THINKING_LEVELS = ['off', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'] as const\n\nexport type ThinkingLevel = (typeof THINKING_LEVELS)[number]\n\n/**\n * The request-modality vocabulary llm-pi-ai accepts on a model entry -- a\n * local mirror of pi-ai's MODALITY_GATE (text/image today). The core schema\n * refuses anything else at write time, and its drift gate forces the\n * vocabulary to grow exactly when pi-ai's Model['input'] does; the vocabulary\n * spec pins this mirror against upstream so the UI follows.\n */\nexport const INPUT_MODALITIES = ['text', 'image'] as const\n\nexport type InputModality = (typeof INPUT_MODALITIES)[number]\n\n/** One level's wire spelling; null means \"send nothing for this level\". */\nexport type WireSpelling = string | null\n\n/** A full reasoning-effort declaration value: level -> wire spelling. */\nexport type ReasoningEfforts = Partial<Record<ThinkingLevel, WireSpelling>>\n\n/** A full input-modality declaration value: the modalities a model accepts. */\nexport type InputModalities = readonly InputModality[]\n\n/** Reasoning-budget request-field spellings pi-ai accepts (older kernels reject them). */\nexport const THINKING_TOKEN_BUDGET_FIELDS = ['thinking_token_budget', 'thinking_budget', 'thinking_budget_tokens'] as const\nexport type ThinkingTokenBudgetField = (typeof THINKING_TOKEN_BUDGET_FIELDS)[number]\n\n/** The compat fields this plugin may suggest (a subset of the profile schema). */\nexport interface CompatSuggestion {\n /** Wire format the endpoint speaks; omitted when unknown. */\n thinkingFormat?: string\n /** Whether the endpoint accepts a reasoning-effort-style parameter. */\n supportsReasoningEffort?: boolean\n /**\n * Whether pi-ai may rewrite the system prompt to the developer role for\n * reasoning models. Self-hosted relays get an explicit false (their\n * upstreams reject role:developer); official endpoints keep pi-ai's own\n * detection default by omitting the field.\n */\n supportsDeveloperRole?: boolean\n /**\n * Anthropic-messages protocol only: force adaptive thinking so the\n * declared efforts dispatch as `output_config.effort` instead of falling\n * into the older budget-based thinking path. pi-ai offers this field on\n * exactly the anthropic-messages compat gate, and the oracle models that\n * take an effort ladder all have adaptive thinking.\n */\n forceAdaptiveThinking?: boolean\n thinkingTokenBudgetField?: ThinkingTokenBudgetField\n supportsThinkingTokenBudget?: boolean\n vllmPriority?: number\n supportsMaxOutputTokens?: boolean\n}\n\nexport const NEW_COMPAT_KEYS = ['thinkingTokenBudgetField', 'vllmPriority', 'supportsMaxOutputTokens'] as const\n\n/** One knowledge-base entry: which model families it matches and what to declare. */\nexport interface KnowledgeEntry {\n /** Stable id (also used to de-duplicate user overrides). */\n id: string\n /** Model id/name patterns (lowercase substring match). */\n patterns: readonly string[]\n /**\n * Level set + wire spellings to declare. `false` marks a family the\n * official catalog serves WITHOUT any reasoning control (GPT-4o / GPT-4\n * generations): suggesting a ladder there would have the composer send\n * reasoning_effort and get a 400 from the real endpoint.\n */\n efforts: ReasoningEfforts | false\n /** Optional compat block to declare alongside the efforts. */\n compat?: CompatSuggestion\n /**\n * The model family takes an effort ladder on the anthropic-messages\n * protocol too: pi-ai only routes declared efforts to `output_config.effort`\n * when `forceAdaptiveThinking` is set, so this pin adds that compat block\n * on anthropic-messages routes (and nothing on any other protocol).\n */\n anthropicAdaptive?: boolean\n /**\n * Request modalities to declare, in core vocabulary. Absent = no modality\n * suggestion for this family. Wider support a gateway may serve (pdf,\n * audio, video) is recorded in 'note' until the core vocabulary grows.\n */\n input?: InputModalities\n /**\n * Reference context window in tokens (display-only advice; the official\n * capacity inputs are never auto-filled).\n */\n contextWindow?: number\n /** Reference max output tokens in tokens (display-only advice). */\n maxTokens?: number\n /**\n * The vendor's own default level for the family, when its docs name one\n * (the same research pass that recorded the ladders). Consumed by the\n * client's effort memory as the model-switch fallback: a switch to a model\n * with no remembered level re-applies the vendor default instead of leaving\n * the session on the host's \"Default\" (no-effort) state. Never guessed --\n * entries whose docs publish no default simply carry none.\n */\n defaultEffort?: ThinkingLevel\n /** Human note shown next to the suggestion (localized by the caller). */\n note?: string\n}\n\n/** What a provider route names about itself that inference can use. */\nexport interface RouteFacts {\n /** Wire protocol as configured ('api'), if any. */\n api?: string\n /** Endpoint URL, if any. */\n baseURL?: string\n /** Display name, if any. */\n displayName?: string\n}\n\n/** The endpoint facts a suggestion fuses, as the probe route reports them. */\nexport interface EndpointFacts {\n /** Tri-state reasoning signal: true / false / 'unknown' (listing silent). */\n reasoning: boolean | 'unknown'\n /** The listing field that produced the reasoning signal, null when none did. */\n source: string | null\n /**\n * The listing's input-modality disclosure, normalized to lowercase strings,\n * when the listing named such a field at all. Present-but-empty means the\n * field existed with no usable members; absent means the listing never\n * spoke about modalities (or the probe never ran) -- the tri-state\n * discipline: silence is not refusal.\n */\n input?: readonly string[]\n /** Context length the listing disclosed, when it carries one. */\n contextLength?: number\n}\n\n/** How a suggestion's input-modality part was derived. */\nexport type InputSource = 'knowledge' | 'endpoint' | 'heuristic'\n\n/** A resolved suggestion for one model on one route. */\nexport interface EffortSuggestion {\n /**\n * The reasoning-effort declaration to write, if one is derivable. False\n * arrives when the endpoint explicitly reports the model does not reason.\n */\n efforts?: ReasoningEfforts | false\n /**\n * The vendor's own default level for the model, when the knowledge base\n * names one and the suggested ladder carries it. The effort memory uses it\n * as the model-switch fallback (see {@link KnowledgeEntry.defaultEffort}).\n */\n defaultEffort?: ThinkingLevel\n /** The compat block to write alongside, if any. */\n compat?: CompatSuggestion\n /** Request modalities to declare, when derivable. */\n input?: InputModalities\n /** Where the modality part came from -- its confidence rides this. */\n inputSource?: InputSource\n /** Reference context window (display-only; never auto-filled anywhere). */\n contextWindow?: number\n /** Reference max output tokens (display-only). */\n maxTokens?: number\n /** Whether a knowledge-base entry matched (vs protocol inference). */\n matched: boolean\n /** The knowledge entry id when one matched. */\n entryId?: string\n /** Human-readable source of the suggestion. */\n source: string\n /**\n * Overall confidence for the UI: high = knowledge base (wire values) and/or\n * an explicit endpoint signal; medium = endpoint confirmed reasoning but the\n * level ladder is inferred; low = protocol inference alone.\n */\n confidence: 'high' | 'medium' | 'low'\n /** The endpoint signal behind this suggestion, when one was supplied. */\n endpoint?: { reasoning: boolean | 'unknown'; source: string | null }\n}\n\n/**\n * The curated knowledge base. Patterns are lowercase substring matches against\n * the model id (and display name). First match wins, longest pattern wins.\n *\n * Sources of truth (per family; DeepSeek / OpenAI / Anthropic re-checked\n * 2026-09 against their current official docs, the rest 2026-08): official API docs --\n * api-docs.deepseek.com (news/pricing/thinking/vision/API reference),\n * developers.openai.com model pages + guides (platform.openai.com blocked;\n * Azure learn mirror cross-checked), platform.claude.com (effort page,\n * models overview, OpenAI-SDK compatibility page), ai.google.dev (thinking +\n * OpenAI-compat pages), docs.x.ai (model pages + reasoning capability),\n * docs.bigmodel.cn (thinking + model pages), platform.kimi.com (models /\n * use-thinking-models / use-reasoning-effort), platform.stepfun.com (model\n * pages + reasoning), docs.byteplus.com / volcengine ModelArk (deep\n * reasoning), cloud.baidu.com (Qianfan pricing/thinking model list),\n * plus first-party GitHub cards (openai/gpt-oss, mistralai/mistral-common,\n * Tencent-Hunyuan/Hy3, MiniMax-AI/MiniMax-M3) -- cross-checked against the\n * public OpenRouter catalog (input_modalities, supported_parameters) where\n * noted. Capacities are REFERENCE values: aggregators disagree, so the note\n * says so where the spread is wide, and families whose numbers stay contested\n * simply carry none.\n */\nexport const KNOWLEDGE_BASE: readonly KnowledgeEntry[] = [\n {\n id: 'deepseek-v4-vision',\n // Must precede deepseek-v4 semantically -- guaranteed anyway by the\n // longest-boundary-hit rule, since these patterns are strictly longer.\n patterns: ['deepseek-v4-flash-vision', 'deepseek-v4-vision'],\n efforts: { off: 'none', low: 'low', high: 'high', max: 'max' },\n defaultEffort: 'high',\n compat: { thinkingFormat: 'deepseek', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 1_048_576,\n maxTokens: 384_000,\n note: 'DeepSeek \u89C6\u89C9\u5B9E\u9A8C\u7248 id\uFF08deepseek-v4-flash-vision-exp\uFF09\u3002\u5B98\u65B9\u76EE\u5F55\u5DF2\u6807\u6CE8\u8BE5\u6A21\u578B\u9000\u5F79\uFF0C\u540D\u5B57\u4ECD\u88AB\u63A5\u53D7\u3001\u7531\u73B0\u884C deepseek-flash \u63D0\u4F9B\u670D\u52A1\u2014\u2014\u56FE\u7247\u8F93\u5165\u73B0\u5DF2\u7531 deepseek-flash \u539F\u751F\u63D0\u4F9B\u3002',\n },\n {\n id: 'deepseek-v4-1-flash',\n // V4.1-Flash is DeepSeek's CURRENT flagship and it takes image input, which\n // the shared `deepseek-v4` stem cannot express: that entry carries the text\n // floor its pro/legacy siblings share, and one modal answer cannot be both.\n // This entry keys every V4.1 spelling, and it must stay ahead of the base\n // stem -- the longest-hit rule guarantees that, these patterns are strictly\n // longer -- so an id, or a display name, that says \"V4.1 Flash\" declares\n // images even when the relay invented the id itself.\n // 'deepseek-flash' is the CURRENT official id (2026-09-10) and belongs\n // here, not to the stem: the pricing page serves it as V4.1-Flash, and a\n // third-party reseller exposing DeepSeek's own model under that name means\n // the same model.\n patterns: ['deepseek-v4.1-flash', 'deepseek-flash'],\n efforts: { off: 'none', low: 'low', high: 'high', max: 'max' },\n defaultEffort: 'high',\n compat: { thinkingFormat: 'deepseek', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 1_048_576,\n maxTokens: 384_000,\n note: 'DeepSeek-V4.1-Flash\uFF082026-09-10 \u53D1\u5E03\u7684\u73B0\u884C\u5B98\u65B9\u6A21\u578B\uFF0C\u5B98\u65B9 id \u4E3A deepseek-flash\uFF1B25 \u4E07\u5E76\u53D1\u3001**\u539F\u751F\u56FE\u7247\u8F93\u5165**\uFF09\u3002V4.1 \u7684\u5404\u5199\u6CD5\uFF08deepseek-flash\u3001deepseek-v4.1-flash\u3001deepseek-v4-1-flash\uFF0C\u4EE5\u53CA\u5E26\u65E5\u671F\u540E\u7F00\u7684\u7B2C\u4E09\u65B9\u53D8\u4F53\uFF09\u90FD\u5F52\u6B64\u6761\u2014\u2014\u7EAF\u6587\u672C\u7684 deepseek-v4 \u4E3B\u5E72\u4E0D\u518D\u541E\u6389\u5B83\u4EEC\u3002\u5B98\u65B9\u679A\u4E3E\u540C V4 \u7CFB\uFF1AOff\uFF08thinking:\"disabled\"\uFF09/ Low / High / Max\uFF0C\u9ED8\u8BA4 High\uFF1B\u5BB9\u91CF 1,048,576 \u4E0A\u4E0B\u6587 / \u6700\u5927\u8F93\u51FA 384K\u3002',\n },\n {\n id: 'deepseek-v4',\n // One pattern covers the family's TEXT-floor members: every serving\n // suffixes the base id (deepseek-v4-flash, deepseek-v4-pro,\n // deepseek-v4-flash-free ...), so the shared stem keys them all --\n // including free/aggregator spellings the official catalog never lists.\n // Closing thinking is the 'thinking: disabled' object, so the off spelling\n // is a non-null placeholder that arms the deepseek format's disabled\n // branch. The placeholder must stay 'none': pi-ai's deepseek completions\n // branch only checks non-null (any string arms thinking:disabled), while\n // the same map rides the Responses API as reasoning.effort, whose official\n // DeepSeek values are none/low/high/max -- 'off' would be a 400 there.\n // The stem deliberately stops at 'deepseek-v4': the image-capable V4.1\n // generation keys its own strictly longer entry above, while this entry\n // keeps the retired deepseek-v4-flash spelling and the text-only\n // deepseek-v4-pro, whose declarations agree with its text floor.\n patterns: ['deepseek-v4'],\n efforts: { off: 'none', low: 'low', high: 'high', max: 'max' },\n defaultEffort: 'high',\n compat: { thinkingFormat: 'deepseek', supportsReasoningEffort: true },\n input: ['text'],\n contextWindow: 1_048_576,\n maxTokens: 384_000,\n note: 'DeepSeek \u5B98\u65B9\u679A\u4E3E Low / High / Max\uFF08\u9ED8\u8BA4 High\uFF1Bminimal\u3001medium\u3001xhigh \u517C\u5BB9\u6620\u5C04\uFF0Cultra\u2192max\uFF09\uFF0COff \u5373 thinking:\"disabled\"\uFF08Responses API \u4E0B off \u4EE5 reasoning.effort:\"none\" \u8868\u793A\uFF09\u3002\u672C\u6761\u76EE\u662F V4 \u5BB6\u65CF\u7684**\u7EAF\u6587\u672C**\u6210\u5458\uFF1Adeepseek-v4-pro\uFF08= DeepSeek-V4-Pro-0813\uFF0C\u5B98\u65B9\u4E0D\u652F\u6301\u56FE\u7247\uFF09\u4E0E\u5DF2\u9000\u5F79\u6A21\u578B\u7684\u517C\u5BB9\u522B\u540D deepseek-v4-flash\uFF08deepseek-v4-flash-vision-exp \u53E6\u6709\u89C6\u89C9\u6761\u76EE\uFF09\u3002**\u6536\u56FE\u7684 V4.1-Flash\uFF08\u5B98\u65B9 id deepseek-flash\uFF09\u53E6\u6709\u4E13\u95E8\u6761\u76EE**\uFF0C\u5176 input \u542B image\u3002\u5BB9\u91CF\uFF1A1,048,576 \u4E0A\u4E0B\u6587 / \u6700\u5927\u8F93\u51FA 384K\uFF08393,216\uFF1B\u9ED8\u8BA4\u975E\u601D\u8003 8K\u3001\u601D\u8003 64K\u3001effort=max \u65F6 128K\uFF09\u3002',\n },\n {\n id: 'deepseek-v3',\n // deepseek-reasoner belongs to the R1 entry: it is the reasoning model's\n // API name and must not match here. The old alias pair\n // (deepseek-chat / deepseek-reasoner) was retired upstream in 2026-07;\n // the id patterns stay for third-party gateways still serving V3.x.\n patterns: ['deepseek-v3', 'deepseek-chat'],\n efforts: { off: 'none', high: 'high', max: 'max' },\n compat: { thinkingFormat: 'deepseek', supportsReasoningEffort: true },\n input: ['text'],\n contextWindow: 163_840,\n maxTokens: 65_536,\n note: 'DeepSeek V3 \u6863\u4F4D\uFF1AOff / High / Max\u3002\u5B98\u65B9\u5DF2\u505C\u552E V3 \u4EE3\uFF08\u5B9A\u4EF7\u9875\u4EC5\u5269 V4 \u4E09\u578B\uFF09\uFF0C\u5BB9\u91CF\u53D6\u76EE\u5F55\u73B0\u5F79\u503C\u3002',\n },\n {\n id: 'deepseek-r1',\n patterns: ['deepseek-r1', 'deepseek-reasoner'],\n // R1's reasoning cannot be switched off at the API level; the harness\n // treats it as a thinking model whose only declared level is the default.\n efforts: { high: 'high' },\n defaultEffort: 'high',\n compat: { thinkingFormat: 'deepseek', supportsReasoningEffort: true },\n input: ['text'],\n contextWindow: 163_840,\n maxTokens: 32_768,\n note: 'DeepSeek-R1 \u4E3A\u63A8\u7406\u6A21\u578B\uFF0C\u4EC5\u63D0\u4F9B High\u3002\u5B98\u65B9\u5DF2\u4E0B\u67B6 R1 \u4EE3\uFF0C\u5BB9\u91CF\u53D6\u76EE\u5F55 r1-0528 \u503C\u3002',\n },\n {\n id: 'openai-gpt-5-2',\n patterns: ['gpt-5.2'],\n // gpt-5.2 (2025-12) took 'none' as the explicit no-thinking value\n // (replacing gpt-5's 'minimal') and added xhigh; its default is none.\n efforts: { off: 'none', low: 'low', medium: 'medium', high: 'high', xhigh: 'xhigh' },\n defaultEffort: 'off',\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 400_000,\n maxTokens: 128_000,\n note: 'GPT-5.2 \u6863\u4F4D\uFF1ANone / Low / Medium / High / XHigh\uFF08\u9ED8\u8BA4 None\uFF09\u3002\u5B98\u65B9\u53E6\u652F\u6301 PDF \u8F93\u5165\uFF08\u6838\u5FC3\u8BCD\u8868\u6682\u4E0D\u542B\uFF09\u3002gpt-5.2-pro \u5B98\u65B9\u9875\u672A\u5355\u5217\u6863\u4F4D\u884C\uFF0C\u6309\u672C\u6761\u76EE\u540C\u6863\u5904\u7406\uFF1Bgpt-5.2-codex \u89C1\u5355\u72EC\u6761\u76EE\u3002',\n },\n {\n id: 'openai-gpt-5-2-codex',\n // The codex variant's official model page names low/medium/high/xhigh\n // only -- NO none -- so the shared gpt-5.2 entry must not claim an off\n // for it (a 'none' request would 400). Longer pattern wins over 'gpt-5.2'.\n patterns: ['gpt-5.2-codex'],\n efforts: { low: 'low', medium: 'medium', high: 'high', xhigh: 'xhigh' },\n // OpenAI's API default for the codex line is medium (Inspect AI's\n // per-model default table, aligned with developers.openai.com).\n defaultEffort: 'medium',\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 400_000,\n maxTokens: 128_000,\n note: 'GPT-5.2-Codex \u6863\u4F4D\uFF1ALow / Medium / High / XHigh\uFF08\u65E0 None \u6863\uFF0C\u52FF\u52FE Off\uFF09\uFF0C\u5E26\u56FE\u8F93\u5165\u3001400K\u3002',\n },\n {\n id: 'openai-gpt-5-6',\n patterns: ['gpt-5.6'],\n // gpt-5.6 spans the full modern ladder including max; the sol/luna/terra\n // variants share it (official reasoning guide: values \"can include none\n // minimal low medium high xhigh, and max\").\n efforts: { off: 'none', low: 'low', medium: 'medium', high: 'high', xhigh: 'xhigh', max: 'max' },\n defaultEffort: 'medium',\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 1_050_000,\n maxTokens: 128_000,\n note: 'GPT-5.6\uFF08\u522B\u540D\u5373 sol\uFF09\u6863\u4F4D\uFF1ANone / Low / Medium(\u9ED8\u8BA4) / High / XHigh / Max\uFF1Bsol/luna/terra \u540C\u6863\uFF0C\u5E26\u56FE\u8F93\u5165\u3002',\n },\n {\n id: 'openai-gpt-5-5',\n patterns: ['gpt-5.5'],\n // gpt-5.5 defaults to medium (official reasoning guide) and tops out at\n // xhigh; the -pro variant drops none/low (medium/high/xhigh only).\n efforts: { off: 'none', low: 'low', medium: 'medium', high: 'high', xhigh: 'xhigh' },\n defaultEffort: 'medium',\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 1_050_000,\n maxTokens: 128_000,\n note: 'GPT-5.5 \u6863\u4F4D\uFF1ANone / Low / Medium(\u9ED8\u8BA4) / High / XHigh\uFF0C\u5E26\u56FE\u8F93\u5165\u3002pro \u53D8\u4F53\u4EC5 Medium / High(\u9ED8\u8BA4) / XHigh \u4E14\u4EC5 Responses API\u3002',\n },\n {\n id: 'openai-gpt-5-4',\n patterns: ['gpt-5.4'],\n // Official model page: none (default) / low / medium / high / xhigh;\n // pro drops none+low and is Responses-only; mini/nano sit on 400K.\n efforts: { off: 'none', low: 'low', medium: 'medium', high: 'high', xhigh: 'xhigh' },\n defaultEffort: 'off',\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 1_050_000,\n maxTokens: 128_000,\n note: 'GPT-5.4 \u6863\u4F4D\uFF1ANone(\u9ED8\u8BA4) / Low / Medium / High / XHigh\uFF0C\u5E26\u56FE\u8F93\u5165\u3002pro \u53D8\u4F53\u4EC5 Medium/High/XHigh\uFF1Bmini/nano \u4E3A 400K \u4E0A\u4E0B\u6587\u3002',\n },\n {\n id: 'openai-gpt-5-3',\n patterns: ['gpt-5.3'],\n // Official model page: low / medium / high / xhigh only -- no none.\n efforts: { low: 'low', medium: 'medium', high: 'high', xhigh: 'xhigh' },\n defaultEffort: 'medium',\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 400_000,\n maxTokens: 128_000,\n note: 'GPT-5.3-Codex \u6863\u4F4D\uFF1ALow / Medium / High / XHigh\uFF08\u65E0 None \u6863\uFF09\uFF0C\u5E26\u56FE\u8F93\u5165\u3001400K\uFF1Bgpt-5.3-chat \u4E3A\u975E\u63A8\u7406\u804A\u5929\u6A21\u578B\uFF08\u89C1 chat \u6761\u76EE\uFF09\u3002',\n },\n {\n id: 'openai-gpt-5-1',\n patterns: ['gpt-5.1'],\n // gpt-5.1 (2025-11): none replaces minimal; no xhigh outside codex-max.\n // The codex variants split into their own entry: their official pages\n // publish no effort line, and the codex line's 5.2/5.3 successors take\n // no none -- so the conservative declaration there drops both none and\n // xhigh (see openai-gpt-5-1-codex).\n efforts: { off: 'none', low: 'low', medium: 'medium', high: 'high' },\n defaultEffort: 'off',\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 400_000,\n maxTokens: 128_000,\n note: 'GPT-5.1 \u6863\u4F4D\uFF1ANone / Low / Medium / High\uFF08\u9ED8\u8BA4 None\uFF09\u3002',\n },\n {\n id: 'openai-gpt-5-1-codex',\n // Official model pages for 5.1-codex / -codex-max / -codex-mini publish no\n // reasoning-effort line (unlike gpt-5.1 itself); the codex successors\n // 5.2/5.3 are low/medium/high/xhigh without none, so none is NOT declared\n // here either. xhigh is likewise unproven for 5.1-codex (the 5.1 page's\n // own ladder stops at high) and is withheld.\n patterns: ['gpt-5.1-codex'],\n efforts: { low: 'low', medium: 'medium', high: 'high' },\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 400_000,\n maxTokens: 128_000,\n note: 'GPT-5.1-Codex \u7CFB\u5217\uFF1A\u5B98\u65B9\u6A21\u578B\u9875\u672A\u5355\u5217 effort \u503C\u57DF\uFF0C\u6309\u540C\u4EE3\u4FDD\u5B88\u6863 Low / Medium / High\uFF08\u65E0 None\uFF1Bxhigh \u672A\u8BC1\u5B9E\uFF09\u3002\u5982\u7AEF\u70B9\u652F\u6301 xhigh/none \u53EF\u624B\u8C03\u3002',\n },\n {\n id: 'openai-gpt-5',\n patterns: ['gpt-5'],\n // The 2025-08 first generation: minimal is the floor, there is no none,\n // and the default (medium) thinks -- so no off level is offered.\n efforts: { minimal: 'minimal', low: 'low', medium: 'medium', high: 'high' },\n defaultEffort: 'medium',\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 400_000,\n maxTokens: 128_000,\n note: 'GPT-5 \u521D\u4EE3\u6863\u4F4D\uFF1AMinimal / Low / Medium / High\uFF0C\u65E0\u5173\u95ED\u6863\u3002',\n },\n {\n id: 'openai-chat',\n // Non-reasoning chat lines across generations: they take no\n // reasoning_effort at all (official model pages list no \"Reasoning\").\n // Official ids are the *-chat-latest snapshots; the bare -chat spellings\n // stay for gateways serving them. gpt-5-chat-latest was removed upstream\n // on 2026-07-23 and gpt-chat-latest has no model page anymore -- the\n // patterns persist for compatibility only.\n patterns: [\n 'gpt-5.1-chat-latest', 'gpt-5.2-chat-latest', 'gpt-5.3-chat-latest', 'gpt-5-chat-latest',\n 'gpt-chat-latest', 'gpt-5-chat', 'gpt-5.1-chat', 'gpt-5.2-chat', 'gpt-5.3-chat',\n ],\n efforts: false,\n input: ['text', 'image'],\n contextWindow: 128_000,\n maxTokens: 16_384,\n note: '-chat \u7CFB\u5217\uFF08\u5B98\u65B9 id \u4E3A gpt-5.x-chat-latest\uFF09\u4E3A\u975E\u63A8\u7406\u804A\u5929\u6A21\u578B\uFF0C\u4E0D\u652F\u6301 effort \u53C2\u6570\uFF08\u52FF\u52FE\u601D\u8003\u6863\uFF09\uFF1B\u5B98\u65B9\u652F\u6301\u56FE\u7247\u8F93\u5165\uFF0C128K / 16,384 \u8F93\u51FA\u3002gpt-5-chat-latest \u5DF2\u4E8E 2026-07-23 \u4E0B\u67B6\u3001gpt-chat-latest \u5DF2\u65E0\u5B98\u65B9\u9875\u9762\uFF08\u4FDD\u7559\u6A21\u5F0F\u4F9B\u7F51\u5173\uFF09\u3002',\n },\n {\n id: 'openai-o',\n // The o-series scheduled-removal notice (2026-06-11): o1/o1-pro/o3-mini/\n // o4-mini are removed 2026-10-23 and o3/o3-pro on 2026-12-11, with\n // gpt-5.6-sol / gpt-5.6-terra replacing them; the tokens stay for\n // gateways and local deployments still serving them, and match only on\n // word boundaries (see matchKnowledgeBase), so 'o1' hits 'o1-mini' but\n // not 'ko1'.\n patterns: ['o1', 'o3', 'o4'],\n // o-series had no none: not sending the parameter meant medium (think\n // on), so no off level is offered.\n efforts: { low: 'low', medium: 'medium', high: 'high' },\n defaultEffort: 'medium',\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 200_000,\n maxTokens: 100_000,\n note: 'OpenAI o \u7CFB\u6863\u4F4D\uFF1ALow / Medium / High\u3002\u5B98\u65B9\u5DF2\u4E8E 2026-06 \u516C\u544A\u9000\u5F79\uFF08o1/o1-pro/o3-mini/o4-mini 2026-10-23 \u79FB\u9664\u3001o3/o3-pro 2026-12-11\uFF09\uFF0C\u7F51\u5173\u6B8B\u7559\u4ECD\u53EF\u547D\u4E2D\uFF1B\u591A\u6570 o \u7CFB\u7AEF\u70B9\u6536\u56FE\uFF08o3-mini \u4F8B\u5916\uFF09\u3002',\n },\n {\n id: 'openai-gpt-oss',\n // Official README: configurable reasoning effort low / medium / high\n // (CLI default: low); official model page: 131,072 context and exactly\n // the same 131,072 max output tokens, text input only.\n patterns: ['gpt-oss'],\n efforts: { low: 'low', medium: 'medium', high: 'high' },\n defaultEffort: 'low',\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text'],\n contextWindow: 131_072,\n maxTokens: 131_072,\n note: 'GPT-OSS \u5F00\u6E90\u6743\u91CD\uFF08Ollama/vLLM \u5E38\u89C1\uFF09\uFF1Areasoning effort Low / Medium / High\uFF0C\u7EAF\u6587\u672C\uFF0C131K \u4E0A\u4E0B\u6587 / 131K \u8F93\u51FA\u30022026-09 \u590D\u6838\uFF1A\u5B98\u65B9\u6A21\u578B\u9875\u73B0\u5728\u5199 Chat Completions = Not supported\u3001\u65E0\u56FE\u7247\u8F93\u5165\uFF08\u9ED8\u8BA4 Low \u6765\u81EA\u5B98\u65B9 CLI\uFF0C\u4E0D\u662F API \u9875\uFF09\uFF0C\u56E0\u6B64\u8FD9\u91CC\u4E0D\u518D\u58F0\u660E Off\u3002',\n },\n {\n id: 'openai-gpt-6-astra',\n // Longest-pattern-wins keeps this ahead of the 5.x entries; 'astra' is the\n // only stable token an aggregator is likely to expose.\n patterns: ['gpt-6-astra', 'gpt-6'],\n // Official reasoning guide: none returns HTTP 400 on this model, and the\n // model page lists low/medium/high/xhigh/max. Official docs publish no\n // default effort for it, so none is recorded rather than guessed.\n efforts: { low: 'low', medium: 'medium', high: 'high', xhigh: 'xhigh', max: 'max' },\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 1_050_000,\n maxTokens: 128_000,\n note: 'GPT-6 Astra \u6863\u4F4D\uFF1ALow / Medium / High / XHigh / Max\u2014\u2014**\u6CA1\u6709 None \u6863**\uFF08\u5B98\u65B9\u539F\u6587\uFF1A\u8BBE\u4E3A none \u8FD4\u56DE HTTP 400\uFF09\uFF0C\u56E0\u6B64\u672C\u6761\u76EE\u4E0D\u58F0\u660E Off\u3002\u5E26\u56FE\u8F93\u5165\uFF0C1,050,000 \u4E0A\u4E0B\u6587 / 128,000 \u8F93\u51FA\uFF1BAI \u5B98\u65B9\u9ED8\u8BA4\u6863\u4F4D\u672A\u516C\u5E03\uFF0C\u6545\u4E0D\u586B\u3002\u5DE5\u5177\u8C03\u7528\u9700\u8D70 Responses API\u3002',\n },\n {\n id: 'openai-gpt-5-6-cyber',\n // Before the family entry: cyber's model page publishes NO effort line and\n // limits Chat Completions, so the family ladder (which includes none/max)\n // must not be offered for it.\n patterns: ['gpt-5.6-cyber'],\n // Conservative: the codex-style successors publish no none, and cyber's\n // page publishes nothing at all -- declare the intersection the other 5.6\n // variants agree on, plus xhigh (every published 5.6 ladder carries it).\n efforts: { low: 'low', medium: 'medium', high: 'high', xhigh: 'xhigh' },\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 400_000,\n maxTokens: 128_000,\n note: 'GPT-5.6-Cyber\uFF1A\u5B98\u65B9\u6A21\u578B\u9875\u672A\u5355\u5217 effort \u503C\u57DF\uFF08\u4E0D\u7B49\u4E8E\u4E0D\u652F\u6301\uFF09\uFF0C\u6545\u6309\u540C\u4EE3\u4FDD\u5B88\u6863 Low / Medium / High / XHigh\uFF1B\u8BE5\u578B\u53F7\u4EC5 Responses API\uFF0C\u5E26\u56FE\u8F93\u5165\u3001400K \u4E0A\u4E0B\u6587\u3002\u5B98\u65B9\u652F\u6301\u7684\u5176\u4ED6\u6863\u4F4D\u53EF\u624B\u8C03\u540E\u5E94\u7528\u3002',\n },\n {\n id: 'openai-gpt-4o',\n // Boundary matching keeps 'gpt-4' from hitting 'gpt-4o', so the vision\n // generation needs its own entry. gpt-4.1 keeps its own entry below\n // (1M context, larger output cap).\n patterns: ['gpt-4o'],\n // GPT-4o is not a reasoning model (official catalog lists no reasoning,\n // the API refuses reasoning_effort): suggest `false` so the editor\n // marks the row \"does not reason\" instead of wiring a rejected ladder.\n efforts: false,\n input: ['text', 'image'],\n contextWindow: 128_000,\n maxTokens: 16_384,\n note: 'GPT-4o \u4EE3\u9645\uFF1A\u975E\u63A8\u7406\u6A21\u578B\uFF0C\u4E0D\u652F\u6301 effort \u53C2\u6570\uFF08\u52FF\u52FE\u601D\u8003\u6863\uFF09\uFF1B\u56FE\u7247\u8F93\u5165\u5168\u7CFB\u6807\u914D\uFF0C128K / 16,384 \u8F93\u51FA\u3002',\n },\n {\n id: 'openai-gpt-4-1',\n // Official model page: 1,047,576 context / 32,768 max output, text+image\n // on every variant (4.1 / mini / nano alike -- the old \"nano is\n // text-only\" claim was wrong). No reasoning step at all.\n patterns: ['gpt-4.1'],\n efforts: false,\n input: ['text', 'image'],\n contextWindow: 1_047_576,\n maxTokens: 32_768,\n note: 'GPT-4.1 \u5168\u7CFB\uFF08\u542B mini/nano\uFF09\uFF1A\u975E\u63A8\u7406\u6A21\u578B\uFF0C\u4E0D\u652F\u6301 effort \u53C2\u6570\uFF1B\u5B98\u65B9 1,047,576 \u4E0A\u4E0B\u6587 / 32,768 \u8F93\u51FA\uFF0C\u5168\u7CFB\u5E26\u56FE\u8F93\u5165\u3002',\n },\n {\n id: 'openai-gpt-4-turbo-preview',\n // The preview snapshot is text-only (later turbo snapshots take images;\n // the shared preview spelling must not claim them). Longest-boundary-hit\n // keeps it ahead of 'gpt-4-turbo'.\n patterns: ['gpt-4-turbo-preview'],\n efforts: false,\n input: ['text'],\n contextWindow: 128_000,\n maxTokens: 4_096,\n note: 'GPT-4 Turbo Preview\uFF1A\u975E\u63A8\u7406\u6A21\u578B\uFF0C\u7EAF\u6587\u672C\u8F93\u5165\uFF08\u6B63\u5F0F turbo \u5FEB\u7167\u652F\u6301\u56FE\u7247\uFF0C\u89C1\u4E0B\u6761\uFF09\uFF0C128K\u3002',\n },\n {\n id: 'openai-gpt-4-turbo',\n patterns: ['gpt-4-turbo'],\n efforts: false,\n input: ['text', 'image'],\n contextWindow: 128_000,\n maxTokens: 4_096,\n note: 'GPT-4 Turbo\uFF1A\u975E\u63A8\u7406\u6A21\u578B\uFF0C\u5B98\u65B9\u652F\u6301\u56FE\u7247\u8F93\u5165\uFF0C128K / 4,096 \u8F93\u51FA\u3002',\n },\n {\n id: 'openai-gpt',\n patterns: ['gpt-4', 'gpt-3.5'],\n // Same reasoning as gpt-4o above: the GPT-4/3.5 generations take no\n // reasoning_effort -- suggest `false` rather than a rejected ladder.\n // gpt-4 remains text-only (8K window); the vision-bearing 4o/4.1/turbo\n // lines have their own entries.\n efforts: false,\n input: ['text'],\n note: 'GPT-4/3.5 \u4EE3\u9645\uFF1A\u975E\u63A8\u7406\u6A21\u578B\uFF0C\u4E0D\u652F\u6301 effort \u53C2\u6570\uFF08\u52FF\u52FE\u601D\u8003\u6863\uFF09\u3002gpt-4-turbo/4.1 \u8D77\u652F\u6301\u56FE\u7247\uFF0C\u89C1\u5355\u72EC\u6761\u76EE\uFF1B\u65B0\u4EE3\u8BF7\u7528 GPT-5 \u7CFB\uFF08\u89C1 openai-chat/gpt-5.x \u6761\u76EE\uFF09\u3002',\n },\n {\n id: 'anthropic-claude-5',\n // Claude 5th generation (fable/mythos/opus-5/sonnet-5, 2026): adaptive\n // thinking with an effort ladder that adds xhigh and max; the fable and\n // mythos entries think always-on, so no off level. Mythos Preview is\n // split into its own entry: it takes max but NOT xhigh (official effort\n // matrix), so the shared ladder's xhigh would be a 400.\n patterns: ['claude-fable-5', 'claude-mythos-5', 'claude-opus-5', 'claude-sonnet-5'],\n // Ladder per the official effort matrix (platform.claude.com\n // docs/build-with-claude/effort). NOTE: Anthropic's own OpenAI-SDK\n // compatibility layer IGNORES reasoning_effort (thinking is driven by\n // its own param, on by default for Claude 5) -- the declaration bites\n // on third-party gateways that map it; on the native\n // anthropic-messages protocol, anthropicAdaptive pins the compat that\n // makes pi-ai dispatch these as output_config.effort.\n efforts: { low: 'low', medium: 'medium', high: 'high', xhigh: 'xhigh', max: 'max' },\n defaultEffort: 'high',\n // No thinkingFormat: pi-ai has no 'anthropic' member, and the\n // openai-completions compat gate that takes thinkingFormat is not the\n // protocol an Anthropic endpoint speaks.\n compat: { supportsReasoningEffort: true },\n anthropicAdaptive: true,\n input: ['text', 'image'],\n contextWindow: 1_000_000,\n maxTokens: 128_000,\n note: 'Claude 5 \u4EE3\uFF08Fable 5 / Mythos 5 / Opus 5 / Sonnet 5\uFF09\u6863\u4F4D\uFF1ALow / Medium / High(\u9ED8\u8BA4) / XHigh / Max\uFF1BFable 5.1 \u4E0E Mythos 5.1 \u89C1\u5355\u72EC\u6761\u76EE\uFF0CMythos Preview \u4EC5\u81F3 Max\u30021M \u4E0A\u4E0B\u6587 / 128K \u8F93\u51FA\uFF08Batch 300K\uFF09\u3002\u5B98\u65B9\u652F\u6301 PDF \u8F93\u5165\u3002\u6CE8\u610F\uFF1AAnthropic \u5B98\u65B9 OpenAI \u517C\u5BB9\u5C42\u4F1A\u5FFD\u7565 effort \u53C2\u6570\uFF0C\u58F0\u660E\u5728\u7B2C\u4E09\u65B9\u7F51\u5173\u6620\u5C04\u65F6\u751F\u6548\u3002',\n },\n {\n id: 'anthropic-claude-5-1',\n // Ahead of the claude-5 entry, whose 'claude-fable-5' / 'claude-mythos-5'\n // patterns are prefixes of these ids and would win the equal-length tie.\n // Same ladder and default as the 5.0 generation per the official effort\n // page; the meaningful difference is behavioural (these two are the\n // models allowed to change effort mid-conversation), which this plugin\n // does not declare per model.\n patterns: ['claude-fable-5-1', 'claude-mythos-5-1'],\n efforts: { low: 'low', medium: 'medium', high: 'high', xhigh: 'xhigh', max: 'max' },\n defaultEffort: 'high',\n compat: { supportsReasoningEffort: true },\n anthropicAdaptive: true,\n input: ['text', 'image'],\n contextWindow: 1_000_000,\n maxTokens: 128_000,\n note: 'Claude Fable 5.1 / Mythos 5.1\uFF1A\u5B98\u65B9\u6863\u4F4D Low / Medium / High(\u9ED8\u8BA4) / XHigh / Max\uFF0C\u81EA\u9002\u5E94\u601D\u8003\u5E38\u5F00\uFF08\u4E0D\u63A5\u53D7 enabled/disabled\uFF09\u30021M \u4E0A\u4E0B\u6587 / 128K \u8F93\u51FA\uFF1B\u8FD9\u4E24\u578B\u4E5F\u662F\u5B98\u65B9\u552F\u4E00\u5141\u8BB8\u4F1A\u8BDD\u4E2D\u9014\u6539\u6863\u7684\uFF08\u9700 beta header\uFF0C\u672C\u63D2\u4EF6\u4E0D\u6D89\u53CA\uFF09\u3002',\n },\n {\n id: 'anthropic-claude-mythos-preview',\n // Official effort matrix: max is available, xhigh is NOT (the matrix's\n // xhigh list covers Fable 5 / Mythos 5 / Opus 5 / Opus 4.8 / Opus 4.7 /\n // Sonnet 5 only).\n patterns: ['claude-mythos-preview'],\n efforts: { low: 'low', medium: 'medium', high: 'high', max: 'max' },\n // Anthropic's effort page: the API default is HIGH across the whole\n // supported list (omitting effort behaves exactly as high).\n defaultEffort: 'high',\n compat: { supportsReasoningEffort: true },\n anthropicAdaptive: true,\n input: ['text', 'image'],\n contextWindow: 1_000_000,\n maxTokens: 128_000,\n note: 'Claude Mythos Preview\uFF1A\u5B98\u65B9\u6863\u4F4D Low / Medium / High / Max\uFF08\u65E0 XHigh\uFF09\uFF0C1M \u4E0A\u4E0B\u6587\u3002',\n },\n {\n id: 'anthropic-claude-opus-4-high',\n patterns: ['claude-opus-4-8', 'claude-opus-4-7'],\n // Official effort page: Opus 4.7/4.8 take the full ladder including\n // xhigh and max (xhigh is the recommended coding start on 4.7).\n efforts: { low: 'low', medium: 'medium', high: 'high', xhigh: 'xhigh', max: 'max' },\n defaultEffort: 'high',\n compat: { supportsReasoningEffort: true },\n anthropicAdaptive: true,\n input: ['text', 'image'],\n contextWindow: 1_000_000,\n maxTokens: 128_000,\n note: 'Claude Opus 4.7/4.8 \u6863\u4F4D\uFF1ALow / Medium / High(\u9ED8\u8BA4) / XHigh / Max\uFF1Bxhigh \u4E3A\u5B98\u65B9\u63A8\u8350\u7684\u7F16\u7801\u8D77\u6B65\u6863\u30021M \u4E0A\u4E0B\u6587\u3002',\n },\n {\n id: 'anthropic-claude-4-6',\n patterns: ['claude-opus-4-6', 'claude-sonnet-4-6'],\n // Official effort page: the 4.6 generation supports max but NOT xhigh\n // (\"some models that support max don't support xhigh\").\n efforts: { low: 'low', medium: 'medium', high: 'high', max: 'max' },\n defaultEffort: 'high',\n compat: { supportsReasoningEffort: true },\n anthropicAdaptive: true,\n input: ['text', 'image'],\n contextWindow: 1_000_000,\n maxTokens: 128_000,\n note: 'Claude 4.6 \u4EE3\u6863\u4F4D\uFF1ALow / Medium / High / Max\uFF08\u65E0 XHigh\u2014\u2014\u5B98\u65B9\u660E\u8A00\u300C\u652F\u6301 max \u7684\u90E8\u5206\u578B\u53F7\u4E0D\u652F\u6301 xhigh\u300D\uFF09\u30021M \u4E0A\u4E0B\u6587\u3002',\n },\n {\n id: 'anthropic-claude-opus-4-5',\n // The only extended-thinking-only model in Anthropic's official effort\n // support list (claude-opus-4-5-20251101): effort works alongside\n // budget_tokens, and the max/xhigh columns of the official matrix do not\n // name it -- so the ladder tops out at high.\n patterns: ['claude-opus-4-5'],\n efforts: { low: 'low', medium: 'medium', high: 'high' },\n defaultEffort: 'high',\n compat: { supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 200_000,\n maxTokens: 64_000,\n note: 'Claude Opus 4.5\uFF1A\u5B98\u65B9 effort \u652F\u6301\u5217\u8868\uFF0820251101 \u5FEB\u7167\uFF09\uFF0C\u6863\u4F4D Low / Medium / High\uFF08\u65E0 XHigh/Max\uFF09\uFF0C\u53EF\u4E0E budget_tokens \u5E76\u7528\uFF1B200K / 64K\u3002',\n },\n {\n id: 'anthropic-claude',\n // Everything else in the Claude line: 3.x and the 4.5-generation\n // Sonnet/Haiku. Anthropic's official effort support list names exactly\n // fable-5 / mythos-5 / mythos-preview / opus-5 / opus-4-8 / opus-4-7 /\n // opus-4-6 / sonnet-5 / sonnet-4-6 / opus-4-5-20251101 -- none of the\n // models matching this entry take the effort parameter (their thinking\n // rides thinking.type), so a ladder here would be refused. `false` is\n // the honest \"no effort control\" declaration, matching the openai-chat\n // convention.\n patterns: ['claude'],\n efforts: false,\n input: ['text', 'image'],\n contextWindow: 200_000,\n maxTokens: 64_000,\n note: 'Claude 3.x \u4E0E Sonnet 4.5 / Haiku 4.5\uFF1A\u5B98\u65B9 effort \u53C2\u6570\u4E0D\u652F\u6301\uFF08\u4EC5 Fable/Mythos 5\u3001Opus 5/4.6-4.8\u3001Sonnet 5/4.6\u3001Opus 4.5 \u652F\u6301\uFF09\uFF0C\u601D\u8003\u7531 thinking.type \u63A7\u5236\u2014\u2014\u52FF\u52FE\u601D\u8003\u6863\u3002\u53C2\u8003\u5BB9\u91CF 200K / 64K\uFF08Haiku 3.5 \u4E3A\u7EAF\u6587\u672C\uFF0C\u6309\u9700\u53D6\u6D88\u56FE\u7247\uFF09\u3002',\n },\n {\n id: 'google-gemini',\n patterns: ['gemini'],\n // Gemini over the OpenAI-compat layer maps reasoning_effort per family\n // (3.1+/3.x -> thinking_level, 2.5 -> thinking_budget). The official\n // compatibility sheet is a full grid (minimal / low / medium / high x\n // family), but the declared ladder stays low/medium/high: the safest\n // universal set, since minimal lands on a non-native value on Gemini\n // 3.1 Pro and none is reserved for 2.5 non-Pro. Users wanting the finer\n // grid tune by hand.\n efforts: { low: 'low', medium: 'medium', high: 'high' },\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 1_048_576,\n maxTokens: 65_536,\n note: 'Gemini \u901A\u7528\u5B89\u5168\u6863\uFF1ALow / Medium / High\uFF08\u5B98\u65B9 OpenAI \u517C\u5BB9\u6620\u5C04\u8868\u53E6\u6536 minimal\uFF1A2.5 \u7CFB\u6620\u5C04\u4E3A 1,024 \u9884\u7B97\u30013.1 Flash-Lite/3 Flash \u539F\u751F minimal\u30013.1 Pro \u843D low\uFF09\u3002none \u4EC5\u80FD\u5173 2.5 \u975E Pro\uFF1B2.5 Pro \u4E0E 3 \u4EE3\u4E0D\u53EF\u5173\uFF1B\u5404\u578B\u9ED8\u8BA4\u4E0D\u4E00\uFF08flash-lite \u9ED8\u8BA4\u5173\uFF09\u3002\u5B98\u65B9\u53E6\u6536\u97F3\u9891/\u89C6\u9891/PDF\u3002',\n },\n {\n id: 'xai-grok-4-7',\n // grok-4.7 shipped after the 2026-08 pass that read it as 404 upstream;\n // that reading is retired here. xAI's own docs were unreachable for this\n // pass, so every field below is a public-catalog reading rather than a\n // first-party one: OpenRouter publishes the slug x-ai/grok-4.7 with\n // reasoning_effort in its supported_parameters, and models.dev records\n // the ladder as low/medium/high/xhigh -- the same four steps 4.6 takes,\n // which is why the same declaration shape is reused. The default level\n // and whether thinking can be closed are NOT established, so neither an\n // `off` nor a defaultEffort is declared. Capacity follows the sources\n // that agree (500K context); max output disagrees across catalogs\n // (450K vs 500K), so it stays out.\n patterns: ['grok-4.7'],\n efforts: { low: 'low', medium: 'medium', high: 'high', xhigh: 'xhigh' },\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 500_000,\n note: 'Grok 4.7\uFF08\u4E0A\u6E38\u5FEB\u7167 grok-4.7-20260916\uFF09\uFF1A\u6863\u4F4D Low / Medium / High / XHigh\u2014\u2014**\u53D6\u81EA\u516C\u5F00\u76EE\u5F55**\uFF08OpenRouter \u7684 supported_parameters\u3001models.dev \u5BF9 docs.x.ai \u7684\u8F6C\u8FF0\uFF09\uFF0C\u672C\u6B21 xAI \u5B98\u65B9\u6587\u6863\u7AD9\u4E0D\u53EF\u8FBE\u3001\u672A\u80FD\u4E00\u624B\u8BC1\u5B9E\uFF1B\u9ED8\u8BA4\u6863\u4F4D\u4E0E\"\u80FD\u5426\u5173\u95ED\u601D\u8003\"\u540C\u6837\u672A\u8BC1\u5B9E\uFF0C\u6545\u4E0D\u58F0\u660E Off\uFF0C\u4E5F\u4E0D\u5199\u9ED8\u8BA4\u6863\u3002\u5E26\u56FE\u8F93\u5165\uFF08\u76EE\u5F55\u8BB0 text+image+file\uFF0CPDF \u5B58\u7591\uFF09\uFF0C500K \u4E0A\u4E0B\u6587\uFF1B\u6700\u5927\u8F93\u51FA\u5404\u76EE\u5F55\u7ED9\u51FA 450K \u4E0E 500K \u4E24\u79CD\u503C\uFF0C\u6309\"\u5206\u6B67\u5373\u4E0D\u5199\"\u5904\u7406\u30022026-08 \u90A3\u6B21\"grok-4.7 \u4E0D\u5B58\u5728\uFF08\u5B98\u65B9 404\uFF09\"\u7684\u7ED3\u8BBA\u5DF2\u968F\u53D1\u5E03\u4F5C\u5E9F\u3002',\n },\n {\n id: 'xai-grok-high',\n patterns: ['grok-4.6'],\n // grok-4.6 adds xhigh on top of low/medium/high (default) and cannot\n // close thinking (docs.x.ai model page + reasoning capability page).\n // (An earlier pass read grok-4.7 as 404 upstream; it shipped later --\n // see the entry above.)\n efforts: { low: 'low', medium: 'medium', high: 'high', xhigh: 'xhigh' },\n defaultEffort: 'high',\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 500_000,\n note: 'Grok 4.6 \u6863\u4F4D\uFF1ALow / Medium / High(\u9ED8\u8BA4) / XHigh\uFF0C\u601D\u8003\u4E0D\u53EF\u5173\u95ED\uFF1B\u5E26\u56FE\u8F93\u5165\u3001500K \u4E0A\u4E0B\u6587\u3002Grok 4.7 \u89C1\u4E0A\u4E00\u6761\uFF08\u6B64\u524D\u8BB0\u4E3A\"\u4E0D\u5B58\u5728\"\uFF0C\u5DF2\u968F\u53D1\u5E03\u4F5C\u5E9F\uFF09\u3002',\n },\n {\n id: 'xai-grok-4-5',\n // grok-4.5 keeps the three-step ladder (default high) with image input\n // and a 500K context window; an xhigh request is silently treated as\n // high (official reasoning page), so it is not declared here.\n patterns: ['grok-4.5'],\n efforts: { low: 'low', medium: 'medium', high: 'high' },\n defaultEffort: 'high',\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 500_000,\n note: 'Grok 4.5 \u6863\u4F4D\uFF1ALow / Medium / High(\u9ED8\u8BA4)\uFF0C\u601D\u8003\u4E0D\u53EF\u5173\u95ED\uFF1B\u5E26\u56FE\u8F93\u5165\u3002\u4F20\u5165 xhigh \u4F1A\u88AB\u5B98\u65B9\u9759\u9ED8\u6309 High \u5904\u7406\u3002',\n },\n {\n id: 'xai-grok-4-3',\n // Official reasoning-capability pages: grok-4.3 documents FOUR effort\n // levels (none / low / medium / high) with a 1M context window; the\n // deprecated grok-4-fast/grok-4-1-fast/grok-4-0709 slugs redirect to\n // grok-4.3 at low/none. The old \"no none for 4.3\" reading of the docs\n // was wrong and is fixed here.\n patterns: ['grok-4.3'],\n // Official models page (docs.x.ai model JSON): supportedEfforts now\n // spans none/low/medium/high/XHIGH with defaultEffort \"low\" -- xhigh\n // joined the ladder after the 2026-08 pass, and low is the default.\n efforts: { off: 'none', low: 'low', medium: 'medium', high: 'high', xhigh: 'xhigh' },\n defaultEffort: 'low',\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 1_000_000,\n note: 'Grok 4.3\uFF1A\u5B98\u65B9\u56DB\u7EA7 reasoning effort\uFF08None / Low / Medium / High\uFF09\uFF0C1M \u4E0A\u4E0B\u6587\u3001\u5E26\u56FE\u8F93\u5165\uFF1Bgrok-4-fast \u7B49\u65E7 slug 2026-05-15 \u8D77\u81EA\u52A8\u91CD\u5B9A\u5411\u5230\u672C\u578B\u53F7\u3002',\n },\n {\n id: 'xai-grok',\n // grok-4.5 (and 4.1-fast etc.) take the standard three-step ladder;\n // grok-4 and older reject reasoning_effort outright -- gateways serving\n // them will refuse the value and the user can clear it by hand. Grok\n // 4/4.1 and grok-3 retired 2026-05-15 (redirected to grok-4.3), and\n // grok-build-0.1 (early-access coding model) publishes no effort\n // contract.\n patterns: ['grok'],\n efforts: { low: 'low', medium: 'medium', high: 'high' },\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text'],\n note: 'Grok \u901A\u7528\u6863\u4F4D\uFF1ALow / Medium / High\u3002\u521D\u4EE3 grok-4\u3001grok-4.20 \u4E0E grok-build-0.1 \u4E0D\u63A5\u53D7\u8BE5\u53C2\u6570\uFF08grok-4 \u7CFB\u4E0E grok-3 \u5DF2\u4E8E 2026-05-15 \u9000\u5F79\u5E76\u91CD\u5B9A\u5411\u81F3 4.3\uFF09\uFF1Bgrok-4.20-multi-agent \u7684\u56DB\u6863\u63A7\u5236\u7684\u662F agent \u6570\u91CF\u800C\u975E\u601D\u8003\u6DF1\u5EA6\u3002\u65B0\u65E7\u4EE3\u9645\u8BF7\u4F18\u5148\u9009 4.5/4.6/4.3\uFF1B\u591A\u6A21\u6001\u53D8\u4F53\u6309\u9700\u52FE\u9009\u56FE\u7247\u3002',\n },\n {\n id: 'mistral-magistral',\n // The native magistral line is a run-together thinking family with no\n // reasoning_effort control of its own (its thinking is driven by\n // prompt_mode / always-on), and Mistral marked the whole line\n // deprecated on 2026-07-28 (phased out through mid-2026; reasoning now\n // rides the generalist models). `false` is the honest \"no effort\n // control\" declaration; gateways that happen to map an effort\n // parameter onto magistral ids can be tuned by hand.\n patterns: ['magistral'],\n efforts: false,\n input: ['text'],\n contextWindow: 32_768,\n maxTokens: 32_768,\n note: 'Magistral \u539F\u751F\u601D\u8003\u7EBF\uFF08\u65E0 effort \u53C2\u6570\uFF0C\u4E3A prompt_mode \u8BED\u4E49\uFF09\uFF0C\u5B98\u65B9\u5DF2\u4E8E 2026-07-28 \u58F0\u660E\u5F03\u7528\u3001\u9010\u6B65\u64A4\u51FA\uFF1B\u73B0\u5F79\u63A8\u7406\u8D70 mistral-small-2603 / mistral-medium-3-5 \u7684 reasoning_effort\uFF08\u89C1\u4E0B\u6761\uFF09\u3002',\n },\n {\n id: 'mistral-medium-3',\n // Official reasoning page + mistral-common request.py (the first-party\n // protocol enum) name the reasoning_effort models: values are exactly\n // none/high. The API ids today are mistral-medium-3-5 (Medium 3.5's\n // model page names the dotted spelling too) and mistral-small-2603\n // (Mistral Small 4); mistral-small-latest is its alias. The dated\n // \"small-2506\" builds are retiring and are NOT matched; the bare\n // 'mistral-medium-3' stem was dropped so the 3.1 generation (no\n // reasoning_effort) does not get the ladder.\n // Normalization folds '.' and '-' to the same space, so the one dotted\n // spelling also keys the API id (mistral-medium-3-5).\n patterns: ['mistral-medium-3.5', 'mistral-small-latest', 'mistral-small-2603'],\n efforts: { off: 'none', high: 'high' },\n // mistral-common's ModelSettings defaults reasoning_effort to None --\n // the endpoint's no-parameter behaviour IS the none/off level.\n defaultEffort: 'off',\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n note: 'Mistral \u73B0\u5F79\u63A8\u7406\u6863\uFF1ANone / High\u2014\u2014\u5B98\u65B9 reasoning_effort \u679A\u4E3E\uFF08mistral-common \u534F\u8BAE\u5E93\uFF09\u5373 none/high\uFF1Bmistral-small-2603\uFF08Small 4\uFF09\u4E0E mistral-medium-3-5 \u7ECF\u5B83\u63A7\u5236\uFF0CMedium 3.5 \u5E26\u89C6\u89C9\u3002\u5BB9\u91CF\u672A\u5728\u5B98\u65B9\u76EE\u5F55\u9875\u5355\u5217\uFF0C\u4E0D\u63D0\u4F9B\u3002',\n },\n {\n id: 'qwen-vision',\n // The DashScope vision lines: -vl suffixes and QvQ. Normalization folds\n // 'qwen2.5-vl' to 'qwen2 5 vl', hence the spelled-out middle pattern.\n // Note the modern generations (3.8-max / 3.7-plus / 3.7-flash and the\n // 3.5-omni line) are multimodal too -- see the qwen-3-8 entry and the\n // plain qwen entry note.\n patterns: ['qwen-vl', 'qwen2-vl', 'qwen2-5-vl', 'qwen3-vl', 'qvq'],\n // DashScope takes NO reasoning_effort: thinking is the enable_thinking\n // boolean -- pi-ai's 'qwen' format dispatches exactly that switch.\n // Commercial Model Studio deployments default enable_thinking to TRUE\n // (help.aliyun.com) -- the on state, which is High here.\n efforts: { off: null, high: 'high' },\n defaultEffort: 'high',\n compat: { thinkingFormat: 'qwen' },\n input: ['text', 'image'],\n contextWindow: 131_072,\n maxTokens: 8_192,\n note: '\u901A\u4E49\u89C6\u89C9\u7EBF\uFF08Qwen-VL/QvQ\uFF09\uFF1Aenable_thinking \u5F00\u5173\uFF08\u5F00=High\uFF09\uFF0C\u6536\u56FE\u3002qwen3-vl \u5BB9\u91CF\u66F4\u5927\uFF0C\u6309\u9700\u4E0A\u8C03\u3002',\n },\n {\n id: 'qwen-3-8',\n // The 3.8 generation moved from the bare enable_thinking switch to an\n // official reasoning_effort ladder: the Qwen3.8 README (\"Flexible\n // Thinking Control\"), the Qwen3.8-27B model card, the Qwen3.8-Flash-Next\n // card and the qwen.ai Flash-Next blog all document reasoning_effort\n // with xhigh as the DEFAULT (xhigh/medium/low), alongside the\n // per-request enable_thinking toggle (chat_template_kwargs on\n // open-weight serving). qwen3.8-max stays the current flagship (1M\n // context; Model Studio lists it under image/video understanding), so\n // the plain qwen entry's text-only declaration must not claim it.\n patterns: ['qwen3.8'],\n efforts: { off: null, low: 'low', medium: 'medium', xhigh: 'xhigh' },\n defaultEffort: 'xhigh',\n compat: { thinkingFormat: 'qwen', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 1_000_000,\n note: 'Qwen 3.8 \u7CFB\uFF1A\u5B98\u65B9 reasoning_effort \u6863\u4F4D XHigh(\u9ED8\u8BA4) / Medium / Low\uFF0Cthinking \u9ED8\u8BA4\u5F00\u3001\u53EF\u6309\u8BF7\u6C42\u5173\u95ED\uFF1B\u539F\u751F\u591A\u6A21\u6001\uFF08qwen3.8-max \u56FE\u50CF/\u89C6\u9891\u7406\u89E3\uFF0C\u89C6\u9891\u672A\u5165\u6838\u5FC3\u8BCD\u8868\uFF09\u30021M \u6863\u4E0A\u4E0B\u6587\u300227B \u4E0E Flash-Next \u5F00\u6E90\u6B3E\u89C1\u5355\u72EC\u6761\u76EE\u3002',\n },\n {\n id: 'qwen-3-8-27b',\n // Open-weight dense release (2026-08-14, HF Qwen/Qwen3.8-27B model\n // card): \"native vision-language model\", 262,144 context natively\n // \"extensible up to 1,000,000 tokens\", reasoning_effort xhigh (default)\n // / medium / low with per-request thinking disable. Longer than the\n // bare 'qwen3.8' pattern, so it wins for -27B ids.\n patterns: ['qwen3.8-27b'],\n efforts: { off: null, low: 'low', medium: 'medium', xhigh: 'xhigh' },\n defaultEffort: 'xhigh',\n compat: { thinkingFormat: 'qwen', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 262_144,\n note: 'Qwen3.8-27B\uFF08dense\uFF0C2026-08-14 \u5F00\u6E90\uFF09\uFF1A\u5B98\u65B9 reasoning_effort \u6863\u4F4D XHigh(\u9ED8\u8BA4) / Medium / Low\uFF0Cthinking \u9ED8\u8BA4\u5F00\u3001\u53EF\u6309\u8BF7\u6C42\u5173\uFF1B\u539F\u751F\u56FE\u50CF+\u89C6\u9891\u7406\u89E3\uFF08\u89C6\u9891\u672A\u5165\u6838\u5FC3\u8BCD\u8868\uFF09\u3002262,144 \u539F\u751F\u4E0A\u4E0B\u6587\u3001\u5B98\u65B9\u58F0\u660E\u53EF\u6269\u81F3 1M\u3002\u6765\u6E90\uFF1AHF Qwen/Qwen3.8-27B \u6A21\u578B\u5361\u3002',\n },\n {\n id: 'qwen-3-8-flash-next',\n // Open-weight experimental architecture preview (HF\n // Qwen/Qwen3.8-Flash-Next card + the qwen.ai blog): the\n // Qwen4-underpinning hybrid (QSA, n-gram embedding; 125B with 6B\n // activated), native vision encoder, 262,144 context natively\n // extensible to 1M. Thinking control identical to the 3.8 generation:\n // enable_thinking toggle plus reasoning_effort xhigh (default) /\n // medium / low. Longest pattern, so it wins for -flash-next ids.\n patterns: ['qwen3.8-flash-next'],\n efforts: { off: null, low: 'low', medium: 'medium', xhigh: 'xhigh' },\n defaultEffort: 'xhigh',\n compat: { thinkingFormat: 'qwen', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 262_144,\n note: 'Qwen3.8-Flash-Next\uFF08125B-A6B \u5B9E\u9A8C\u67B6\u6784\uFF0CQwen4 \u524D\u8EAB\uFF09\uFF1A\u5B98\u65B9 reasoning_effort \u6863\u4F4D XHigh(\u9ED8\u8BA4) / Medium / Low + enable_thinking \u5F00\u5173\uFF1B\u539F\u751F\u89C6\u89C9\uFF08Vision Encoder\uFF09\u3002262,144 \u539F\u751F\u4E0A\u4E0B\u6587\u3001\u53EF\u6269\u81F3 1M\u3002\u6765\u6E90\uFF1AHF \u6A21\u578B\u5361\u4E0E qwen.ai \u5B98\u65B9\u535A\u5BA2\u3002',\n },\n {\n id: 'qwen',\n patterns: ['qwen', 'qwq'],\n // DashScope takes NO reasoning_effort: thinking is the enable_thinking\n // boolean (plus thinking_budget) -- pi-ai's 'qwen' format dispatches\n // exactly that switch, so the ladder is honestly open/close with one\n // nominal thinking level. QwQ is think-always; clearing the off level\n // there is one click. Commercial DashScope defaults enable_thinking to\n // TRUE (open-weight serving defaults to false) -- the on state is High.\n efforts: { off: null, high: 'high' },\n defaultEffort: 'high',\n compat: { thinkingFormat: 'qwen' },\n input: ['text'],\n contextWindow: 1_000_000,\n maxTokens: 65_536,\n note: '\u901A\u4E49\u5343\u95EE\uFF1Aenable_thinking \u5F00\u5173\uFF08\u65E0 effort \u6863\uFF09\uFF0C\u5F00=High\uFF1B\u73B0\u5F79\u65D7\u8230\u4E3A qwen3.8 \u7CFB\uFF081M \u6863\u4E0A\u4E0B\u6587\uFF0C\u89C1 qwen-3-8 \u6761\u76EE\uFF09\u30023.6/3.7 \u4EE3\u7684 Plus/Flash \u4EA6\u9ED8\u8BA4\u591A\u6A21\u6001\uFF08\u56FE\u7247/\u89C6\u9891\uFF09\uFF0C\u5982\u63A5\u5165\u8BF7\u6309\u9700\u52FE\u9009\u56FE\u7247\uFF1B\u89C6\u89C9\u7EBF\u89C1 qwen-vision \u6761\u76EE\u3002',\n },\n {\n id: 'glm-vision',\n // Zhipu's vision lines: glm-4v / glm-4.6v / glm-4.5v / glm-5v. Longer\n // than the plain 'glm' patterns, so they win for those ids.\n // 'glm-4-6v' (not 'glm-46v'): normalization folds 'glm-4.6v' to\n // 'glm 4 6v', so the pattern must carry the same inner separator; the\n // same fold gives 'glm-4.5v' -> 'glm 4 5v'.\n patterns: ['glm-4v', 'glm-4-6v', 'glm-4-5v', 'glm-5v'],\n efforts: { off: null, high: 'high' },\n // GLM-4.5V is forced-thinking and the 4.x line defaults to enabled\n // (docs.bigmodel.cn thinking page) -- the on state is High.\n defaultEffort: 'high',\n compat: { thinkingFormat: 'zai' },\n input: ['text', 'image'],\n contextWindow: 131_072,\n maxTokens: 32_768,\n note: '\u667A\u8C31\u89C6\u89C9\u7EBF\uFF08GLM-4V/4.5V/4.6V/5V\uFF09\uFF1Athinking \u5F00\u5173\uFF08\u5F00=High\uFF09\uFF0C\u6536\u56FE\u3002GLM-4.5V \u4E3A\u5F3A\u5236\u601D\u8003\uFF08\u4F20 disabled \u62A5\u9519\uFF0C\u8BF7\u624B\u52A8\u5220 Off \u6863\uFF09\u3002',\n },\n {\n id: 'glm-5-3-flash',\n // Official model page (docs.bigmodel.cn/cn/guide/models/vlm/glm-5.3-flash,\n // page dated 2026-08-28): input modalities \"\u89C6\u9891\u3001\u56FE\u50CF\u3001\u6587\u672C\u3001\u6587\u4EF6\", 1M\n // context / 128K max output, and \"thinking.type \u4EC5\u652F\u6301 enabled\" \u2014 forced\n // thinking, no off switch. \"\u6587\u672C\u53C2\u6570\u4E0E GLM-5.3 \u4FDD\u6301\u4E00\u81F4\" gives the flash\n // variant the 5.3 ladder (low/high/max). Native multimodal (320B), which\n // the text-only glm-5.3 entry must NOT claim \u2014 hence this separate entry;\n // the longer pattern wins over plain 'glm-5.3'.\n patterns: ['glm-5.3-flash'],\n efforts: { low: 'low', high: 'high', max: 'max' },\n defaultEffort: 'max',\n compat: { thinkingFormat: 'zai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 1_048_576,\n maxTokens: 131_072,\n note: 'GLM-5.3-Flash\uFF08320B\uFF0C\u5B98\u65B9\u6587\u6863 vlm \u5206\u7C7B\uFF09\uFF1A\u5F3A\u5236\u601D\u8003\uFF08thinking.type \u4EC5 enabled\uFF0C\u4E0D\u652F\u6301\u5173\u95ED\uFF09\uFF0C\u6587\u672C\u53C2\u6570\u4E0E GLM-5.3 \u4E00\u81F4\u2014\u2014\u6863\u4F4D Low / High / Max\u3002\u8F93\u5165\u6A21\u6001\uFF1A\u89C6\u9891\u3001\u56FE\u50CF\u3001\u6587\u672C\u3001\u6587\u4EF6\uFF08\u89C6\u9891/\u6587\u4EF6\u672A\u5165\u6838\u5FC3\u8BCD\u8868\uFF0C\u56FE\u7247\u53EF\u52FE\uFF09\u3002\u5B98\u65B9 1M \u4E0A\u4E0B\u6587 / 128K \u6700\u5927\u8F93\u51FA\u3002\u6765\u6E90\uFF1Adocs.bigmodel.cn \u6A21\u578B\u9875\u3002',\n },\n {\n id: 'glm-5-3',\n patterns: ['glm-5.3'],\n // glm-5.3 accepts exactly max/high/low; anything else (including the\n // 5.2 ladder's minimal/none) errors, and thinking cannot be disabled.\n // The API reference names reasoning_effort's default as max.\n efforts: { low: 'low', high: 'high', max: 'max' },\n defaultEffort: 'max',\n compat: { thinkingFormat: 'zai', supportsReasoningEffort: true },\n input: ['text'],\n contextWindow: 1_048_576,\n maxTokens: 131_072,\n note: 'GLM-5.3 \u6863\u4F4D\uFF1ALow / High / Max\uFF08\u5F3A\u5236\u601D\u8003\uFF0C\u5176\u4F59\u503C\u62A5\u9519\uFF09\u3002\u5B98\u65B9\u5BB9\u91CF 1M / 128K\u3002',\n },\n {\n id: 'glm-5-2',\n patterns: ['glm-5.2'],\n // glm-5.2 takes the widest ladder in the wild: none/minimal/low/medium/\n // high/xhigh/max (default max). The zai format arms thinking:disabled\n // for off and sends the effort for the rest.\n efforts: { off: 'none', minimal: 'minimal', low: 'low', medium: 'medium', high: 'high', xhigh: 'xhigh', max: 'max' },\n defaultEffort: 'max',\n compat: { thinkingFormat: 'zai', supportsReasoningEffort: true },\n input: ['text'],\n contextWindow: 1_048_576,\n maxTokens: 131_072,\n // Official capability page: none/minimal stop thinking, low/medium map\n // to high, xhigh maps to max -- all seven spellings are accepted.\n note: 'GLM-5.2 \u6863\u4F4D\uFF1ANone / Minimal / Low / Medium / High / XHigh / Max\uFF08\u5B98\u65B9\u6620\u5C04\uFF1ALow\u00B7Medium\u2192High\u3001XHigh\u2192Max\u3001None\u00B7Minimal=\u505C\u6B62\u601D\u8003\uFF09\u3002\u89C6\u89C9\u7EBF\u89C1 glm-vision \u6761\u76EE\u3002',\n },\n {\n id: 'glm',\n patterns: ['glm', 'zhipu', 'chatglm'],\n // GLM 4.x: thinking.type enabled/disabled only, no effort ladder --\n // the zai format sends exactly that switch (and no effort value,\n // because supportsReasoningEffort is not declared). GLM-4.5/4.6\n // default to enabled (hybrid reasoning: the model decides per\n // request) -- the on state is High.\n efforts: { off: null, high: 'high' },\n defaultEffort: 'high',\n compat: { thinkingFormat: 'zai' },\n input: ['text'],\n note: 'GLM \u901A\u7528\uFF1Athinking \u5F00\u5173\uFF08\u65E0 effort \u6863\uFF09\uFF0C\u5F00=High\uFF1Beffort \u9636\u68AF\u4EC5 GLM-5.2+ \u652F\u6301\uFF08\u89C1\u4E0A\u4E24\u6761\u76EE\uFF09\u3002\u6CE8\u610F GLM-4.7/GLM-4.5V \u4E3A\u5F3A\u5236\u601D\u8003\uFF0C\u4F20 disabled \u4F1A\u62A5\u9519\uFF08\u8BF7\u624B\u52A8\u5220 Off \u6863\uFF09\u3002\u89C6\u89C9\u7EBF\u89C1 glm-vision \u6761\u76EE\u3002',\n },\n {\n id: 'kimi-k3',\n patterns: ['kimi-k3'],\n // kimi-k3: top-level reasoning_effort low/high/max (default max),\n // always thinking -- the thinking object must not be sent at all.\n efforts: { low: 'low', high: 'high', max: 'max' },\n defaultEffort: 'max',\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 1_048_576,\n note: 'Kimi K3 \u6863\u4F4D\uFF1ALow / High / Max(\u9ED8\u8BA4 Max)\uFF0C\u8D70\u9876\u5C42 reasoning_effort\uFF1B\u59CB\u7EC8\u63A8\u7406\u3001\u52FF\u4F20 thinking \u5BF9\u8C61\u3002\u539F\u751F\u89C6\u89C9\u7406\u89E3\uFF0C1M \u4E0A\u4E0B\u6587\uFF08\u6700\u5927\u8F93\u51FA\u5B98\u65B9\u672A\u5355\u72EC\u5217\uFF0C\u4E0D\u63D0\u4F9B\uFF09\u3002',\n },\n {\n id: 'kimi-k2-vision',\n // K2.5/K2.6 are multimodal thinking models with a toggleable\n // thinking.type switch (official use-thinking-models table); K2.5 was\n // re-verified as MULTIMODAL on 2026-08-24 (the old \"text-only\" note was\n // wrong). Longer patterns beat the plain 'kimi' stem below.\n patterns: ['kimi-k2.6', 'kimi-k2.5'],\n efforts: { off: null, high: 'high' },\n defaultEffort: 'high',\n compat: { thinkingFormat: 'deepseek' },\n input: ['text', 'image'],\n contextWindow: 262_144,\n note: 'Kimi K2.5/K2.6 \u89C6\u89C9\u4EE3\uFF1Athinking.type \u5F00\u5173\uFF08\u9ED8\u8BA4\u5F00\u3001\u53EF\u5173\uFF09\uFF0C\u5F00=High\uFF1B\u5E26\u89C6\u89C9\uFF0C256K\u3002K2.7 Code \u59CB\u7EC8\u601D\u8003\uFF0C\u89C1\u5355\u72EC\u6761\u76EE\u3002',\n },\n {\n id: 'kimi-k27-code',\n // K2.7 Code (and its highspeed twin) ALWAYS think: thinking.type only\n // accepts \"enabled\", sending \"disabled\" errors -- so no Off level.\n patterns: ['kimi-k2.7'],\n efforts: { high: 'high' },\n defaultEffort: 'high',\n compat: { thinkingFormat: 'deepseek' },\n input: ['text', 'image'],\n contextWindow: 262_144,\n note: 'Kimi K2.7 Code\uFF08\u542B\u9AD8\u901F\u7248\uFF09\uFF1A\u59CB\u7EC8\u601D\u8003\u2014\u2014thinking.type \u4EC5\u63A5\u53D7 enabled\uFF08\u4F20 disabled \u62A5\u9519\uFF09\uFF0C\u65E0 Off \u6863\uFF1B\u5E26\u89C6\u89C9\uFF0C256K\u3002',\n },\n {\n id: 'kimi-moonshot-v1-vision',\n // Legacy Moonshot V1 vision variants (official model list): plain\n // generation models with no thinking control, but they DO accept images\n // -- the generic 'moonshot' pattern must not declare text-only for them.\n patterns: ['moonshot-v1-8k-vision', 'moonshot-v1-32k-vision', 'moonshot-v1-128k-vision'],\n efforts: false,\n input: ['text', 'image'],\n note: 'Moonshot V1 \u89C6\u89C9\u65E7\u4EE3\uFF088k/32k/128k \u5404\u4E00\uFF09\uFF1A\u65E0\u601D\u8003\u63A7\u4EF6\uFF08\u751F\u6210\u6A21\u578B\uFF09\u3001\u652F\u6301\u56FE\u7247\u8F93\u5165\uFF1B\u8BE5\u7EBF\u5DF2\u505C\u6B62\u5BF9\u65B0\u7528\u6237\u5F00\u653E\uFF082026-08-31 \u5168\u91CF\u4E0B\u7EBF\uFF0C\u7F51\u5173\u6B8B\u7559\u4ECD\u53EF\u547D\u4E2D\uFF09\u3002',\n },\n {\n id: 'kimi',\n // k2.5/2.6 use the DeepSeek-shaped thinking.type enabled/disabled\n // switch, which the 'deepseek' format dispatches; the old k2 preview\n // ids and moonshot-v1 names are retired upstream (2026-05/08) but live\n // on gateways. Effort ladders are not part of their contract.\n patterns: ['kimi', 'moonshot'],\n efforts: { off: null, high: 'high' },\n compat: { thinkingFormat: 'deepseek' },\n input: ['text'],\n contextWindow: 262_144,\n note: 'Kimi \u901A\u7528\uFF1Athinking \u5F00\u5173\uFF08\u65E0 effort \u6863\uFF09\uFF0C\u5F00=High\u3002k2 \u7CFB\u5217 2026-05-25 \u4E0B\u7EBF\u3001kimi-latest 2026-01-28 \u4E0B\u7EBF\u3001moonshot-v1 \u7CFB 2026-08-31 \u5168\u91CF\u4E0B\u7EBF\uFF08\u7F51\u5173\u6B8B\u7559\u4ECD\u53EF\u547D\u4E2D\uFF1Bmoonshot-v1 \u672C\u8EAB\u975E\u601D\u8003\u6A21\u578B\uFF09\u3002\u89C6\u89C9\u4EE3\u4E0E K2.7 Code \u89C1\u5355\u72EC\u6761\u76EE\u3002',\n },\n {\n id: 'hunyuan-hy3',\n // Open-weight contract (Tencent-Hunyuan/Hy3 README): reasoning_effort\n // travels inside chat_template_kwargs as \"no_think\" (default) / \"low\" /\n // \"high\"; the same README's spec table states a 256K context length.\n patterns: ['hy3'],\n efforts: { low: 'low', high: 'high' },\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text'],\n contextWindow: 262_144,\n note: '\u6DF7\u5143 hy3\uFF1A\u5F00\u6E90\u5951\u7EA6\u7ECF chat_template_kwargs.reasoning_effort = no_think(\u9ED8\u8BA4)/low/high\uFF1B\u5B98\u65B9\u6A21\u578B\u5361 256K \u4E0A\u4E0B\u6587\uFF08295B MoE\uFF0C2026 \u5F00\u6E90\uFF09\u3002',\n },\n {\n id: 'hunyuan-hy4',\n // Open-weight contract (Tencent-Hunyuan/Hy4-preview README): reasoning\n // defaults to \"high\" (deep chain-of-thought) and is disabled with\n // extra_body={\"chat_template_kwargs\": {\"reasoning_effort\": \"no_think\"}}\n // \u2014 the same chat-template channel as hy3, with the default flipped to\n // high. The spec table states a 1M context length; the model is a\n // 770B-A49B MoE with Gated DSA attention. The README documents no levels\n // beyond high/no_think, so a low rung is NOT declared without evidence.\n patterns: ['hy4-preview', 'hy-4-preview'],\n efforts: { off: 'no_think', high: 'high' },\n defaultEffort: 'high',\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n contextWindow: 1_000_000,\n note: '\u6DF7\u5143 Hy4 preview\uFF1A\u5B98\u65B9 README\u2014\u2014reasoning \u9ED8\u8BA4 high\uFF08\u6DF1\u5EA6\u601D\u8003\uFF09\uFF0C\u5173\u95ED\u7ECF chat_template_kwargs.reasoning_effort=no_think\uFF1B\u5B98\u65B9\u89C4\u683C\u8868 1M \u4E0A\u4E0B\u6587\uFF08770B-A49B MoE\uFF0CGated DSA\uFF09\u3002low \u6863\u5B98\u65B9\u672A\u5217\uFF0C\u5982\u6709\u8BF7\u624B\u8C03\uFF1B\u89C6\u89C9\u672A\u58F0\u660E\uFF0C\u6309\u9700\u624B\u52FE\u3002\u6765\u6E90\uFF1ATencent-Hunyuan/Hy4-preview \u5B98\u65B9 README\u3002',\n },\n {\n id: 'step-5-preview',\n // StepFun's official model is 'step-5-preview' (there is no bare\n // 'step-5'), carrying the same three-step reasoning_effort ladder as the\n // 3.x line with no way to close thinking -- hence no `off`. The official\n // page calls medium the \"default recommendation\". 1M context, 64K max\n // output, native image + video input.\n patterns: ['step-5'],\n efforts: { low: 'low', medium: 'medium', high: 'high' },\n defaultEffort: 'medium',\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 1_000_000,\n maxTokens: 65_536,\n note: '\u9636\u8DC3 Step-5-Preview\uFF08\u5B98\u65B9 id \u5373 step-5-preview\uFF0C\u65E0\u88F8 step-5\uFF1Bpattern \u53D6\u5BB6\u65CF\u4E3B\u5E72\u4EE5\u517C\u5BB9\u7F51\u5173\u5199\u6CD5\uFF09\uFF1A\u6863\u4F4D Low / Medium(\u5B98\u65B9\u6807\"\u9ED8\u8BA4\u63A8\u8350\") / High\uFF0C\u601D\u8003\u4E0D\u53EF\u5173\u95ED\uFF1B\u539F\u751F\u56FE\u7247 + \u89C6\u9891\u8F93\u5165\uFF1B1M \u4E0A\u4E0B\u6587\u300164K \u6700\u5927\u8F93\u51FA\uFF08\u5B98\u65B9\u53E6\u6CE8 max_tokens \u9ED8\u8BA4 INF\u3001\u4E0E 64K \u4E0A\u9650\u5E76\u5B58\uFF0C\u4EE5\u7AEF\u70B9\u5B9E\u6D4B\u4E3A\u51C6\uFF09\u3002',\n },\n {\n id: 'step-3-7',\n // StepFun step-3.7 is the vision-capable generation; 3.5 stayed\n // text-only on the official serving plan.\n patterns: ['step-3.7', 'step-3.6'],\n efforts: { low: 'low', medium: 'medium', high: 'high' },\n defaultEffort: 'medium',\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 262_144,\n note: '\u9636\u8DC3 Step-3.6/3.7 \u6863\u4F4D\uFF1ALow / Medium(\u9ED8\u8BA4\u63A8\u8350) / High\uFF0C\u539F\u751F\u56FE\u7247+\u89C6\u9891\u7406\u89E3\u3002\u4E0A\u4E0B\u6587\u4E3A\u8F93\u5165+\u8F93\u51FA\u603B\u548C\u4E0A\u9650\u3002',\n },\n {\n id: 'step-3-5',\n // step-3.5-flash (official listing): only low/high, text-only.\n patterns: ['step-3.5'],\n efforts: { low: 'low', high: 'high' },\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text'],\n contextWindow: 262_144,\n note: '\u9636\u8DC3 Step-3.5 Flash \u6863\u4F4D\uFF1ALow / High\uFF08\u7EAF\u6587\u672C\u63A8\u7406\u65D7\u8230\uFF09\u3002\u4E0A\u4E0B\u6587\u4E3A\u8F93\u5165+\u8F93\u51FA\u603B\u548C\u4E0A\u9650\u3002',\n },\n {\n id: 'step',\n // StepFun step-3.x: reasoning_effort low/medium/high (default medium);\n // thinking cannot be turned off, only tuned.\n patterns: ['step-3', 'step-2'],\n efforts: { low: 'low', medium: 'medium', high: 'high' },\n defaultEffort: 'medium',\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text'],\n note: '\u9636\u8DC3 Step \u6863\u4F4D\uFF1ALow / Medium / High\uFF08\u9ED8\u8BA4 Medium\uFF0C\u4E0D\u53EF\u5173\u95ED\uFF09\u30023.6/3.7 \u89C6\u89C9\u4EE3\u89C1\u5355\u72EC\u6761\u76EE\u3002',\n },\n {\n id: 'doubao',\n // Volcengine Ark: officially documented is the thinking.type\n // enabled/disabled/auto switch; the reasoning_effort ladder on\n // seed-2.x has community evidence (chatbox/compshare) but no first-\n // party doc yet -- kept as the conventional ladder, verify per gateway.\n // 'doubao' already covers every doubao-* spelling; the bare 'seed' token\n // (boundary-checked) catches seed-first ids some gateways serve. The\n // seed generations are vision-capable; doubao-1.5-pro stays text-only.\n patterns: ['doubao', 'seed'],\n efforts: { off: null, low: 'low', medium: 'medium', high: 'high' },\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n note: '\u8C46\u5305\u6863\u4F4D\uFF1AOff / Low / Medium / High\u2014\u2014effort \u9636\u68AF\u4ECD\u662F\u793E\u533A\u8BC1\u636E\u3001\u5B98\u65B9 Ark \u6587\u6863\u660E\u5217\u7684\u4E3A thinking.type \u5F00\u5173\uFF082026-08-24 \u590D\u6838\u4ECD\u672A\u89C1\u5230 effort \u5B98\u65B9\u660E\u6587\uFF09\u3002seed \u4EE3\u6536\u56FE\uFF081.5-pro \u4F8B\u5916\uFF09\u3002',\n },\n {\n id: 'minimax-m3',\n // Official M3 README: a thinking parameter with enabled / adaptive /\n // disabled -- NO reasoning_effort ladder. That is exactly the\n // deepseek-shaped toggle, so the kimi-style declaration fits; \"adaptive\"\n // is not expressible in the level vocabulary and defaults apply.\n patterns: ['minimax-m3'],\n efforts: { off: null, high: 'high' },\n compat: { thinkingFormat: 'deepseek' },\n input: ['text', 'image'],\n contextWindow: 1_048_576,\n note: 'MiniMax-M3\uFF1A\u5B98\u65B9 thinking \u53C2\u6570 enabled/adaptive/disabled\uFF08\u65E0 effort \u6863\uFF09\uFF0C\u5F00=High\u3001\u5173=disabled\uFF1B\u539F\u751F\u591A\u6A21\u6001\uFF08\u56FE/\u89C6\u9891\uFF0C\u6838\u5FC3\u8BCD\u8868\u4EC5\u542B\u56FE\uFF09\uFF0C1M \u4E0A\u4E0B\u6587\u3002',\n },\n {\n id: 'mimo-v2-6',\n // Xiaomi MiMo v2.6 (and the v2.5 generation it supersedes) takes a required\n // thinking.type toggle (enabled/disabled, default enabled) plus a required\n // reasoning.effort. The official page lists the accepted effort values as\n // none/minimal/low/medium/high/xhigh/max/ultra and normalizes minimal to\n // low and xhigh/max/ultra to high -- but says outright that reasoning\n // strength is not differentiated yet, so the ladder below is what the\n // endpoint ACCEPTS, not a set of levels that behave differently today.\n // xhigh/max are deliberately absent: the endpoint folds them into high.\n // One entry covers both generations: the same models are documented under\n // one contract, and the family's only text-only member (v2.5-pro) keys its\n // own entry with a strictly longer pattern.\n patterns: ['mimo-v2.6', 'mimo-v2.5'],\n efforts: { off: 'none', low: 'low', medium: 'medium', high: 'high' },\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text', 'image'],\n contextWindow: 1_048_576,\n maxTokens: 131_072,\n note: '\u5C0F\u7C73 MiMo v2.6 \u7CFB\u5217\uFF08mimo-v2.6-pro / -flash / -pro-ultraspeed\uFF1B1M \u4E0A\u4E0B\u6587 / 128K \u6700\u5927\u8F93\u51FA\u3001\u539F\u751F\u5168\u6A21\u6001\uFF09\u4E0E\u5176\u524D\u4EE3 mimo-v2.5\u3002\u5B98\u65B9\u6587\u6863\uFF1Athinking.type\uFF08enabled/disabled\uFF0C\u9ED8\u8BA4 enabled\uFF09+ reasoning.effort\uFF08none \u5173\u95ED\uFF1B\u5176\u4F59\u5408\u6CD5\u503C\u5747\u5F00\u542F\uFF0C\u4E14**\u73B0\u9636\u6BB5\u6682\u672A\u533A\u5206\u63A8\u7406\u5F3A\u5EA6**\u2014\u2014minimal \u5F52\u4E00\u4E3A low\u3001xhigh/max/ultra \u5F52\u4E00\u4E3A high\uFF09\uFF0C\u56E0\u6B64\u8FD9\u91CC\u58F0\u660E\u7684\u662F\u7AEF\u70B9**\u63A5\u53D7**\u7684\u6863\u4F4D Off / Low / Medium / High\uFF08xhigh\u3001max \u4E0D\u58F0\u660E\uFF0C\u7AEF\u70B9\u53EA\u4F1A\u6298\u53E0\u4E3A high\uFF09\u3002\u601D\u8003\u6A21\u5F0F\u4E0B temperature / top_p \u4E0D\u53EF\u81EA\u5B9A\u4E49\uFF08\u5F3A\u5236 1.0 / 0.95\uFF09\uFF1B\u5E26\u5DE5\u5177\u8C03\u7528\u7684\u5386\u53F2\u8F6E\u5FC5\u987B\u5B8C\u6574\u56DE\u4F20 reasoning_content\uFF0C\u5426\u5219 400\u3002mimo-v2.5 \u4E0E v2.5-pro \u5B98\u65B9\u5B9A\u4E8E 2026-10-21 \u4E0B\u7EBF\u3002',\n },\n {\n id: 'mimo-v2-5-pro',\n // The v2.5 flagship is the family's only text-only member -- the official\n // capability table lists no multimodal understanding for it (models.dev\n // agrees, attachment=false), while every v2.6 model and mimo-v2.5 do.\n // Strictly longer pattern than the family entry's 'mimo-v2.5', so the\n // longest-hit rule routes it here.\n patterns: ['mimo-v2.5-pro'],\n efforts: { off: 'none', low: 'low', medium: 'medium', high: 'high' },\n compat: { thinkingFormat: 'openai', supportsReasoningEffort: true },\n input: ['text'],\n contextWindow: 1_048_576,\n maxTokens: 131_072,\n note: '\u5C0F\u7C73 MiMo v2.5-Pro\uFF1A\u8BE5\u5BB6\u65CF\u552F\u4E00\u7684**\u7EAF\u6587\u672C**\u6210\u5458\uFF08\u5B98\u65B9\u80FD\u529B\u8868\u672A\u5217\u5168\u6A21\u6001\u7406\u89E3\uFF09\uFF0C1M \u4E0A\u4E0B\u6587 / 128K \u6700\u5927\u8F93\u51FA\uFF0C\u63A8\u7406\u5951\u7EA6\u4E0E v2.6 \u7CFB\u5217\u76F8\u540C\uFF08thinking.type + reasoning.effort\uFF0Cnone \u5173\u95ED\u3001\u5176\u4F59\u6863\u4F4D\u6682\u4E0D\u533A\u5206\u5F3A\u5EA6\uFF09\u3002\u5B98\u65B9\u5B9A\u4E8E 2026-10-21 \u4E0B\u7EBF\u3002',\n },\n {\n id: 'baidu-ernie',\n // Baidu Qianfan's OpenAI-compatible surface accepts no reasoning_effort\n // parameter (the v2 API parameter tables carry none); thinking is a\n // per-variant property: the -Thinking / ERNIE 5.0-Thinking-* family is\n // always-on thinking, the plain ERNIE 4.5/5.0/5.1 generations are not\n // thinking models at all. The official pricing page (2026-08) lists\n // ERNIE 5.1 / 5.0 / 5.0-Thinking-Preview|Latest|Exp / 4.5-Turbo(-VL).\n patterns: ['ernie'],\n efforts: false,\n input: ['text'],\n contextWindow: 131_072,\n note: '\u767E\u5EA6\u5343\u5E06 ERNIE \u7CFB\uFF1A\u5B98\u65B9 OpenAI \u517C\u5BB9\u63A5\u53E3\u65E0 reasoning_effort \u53C2\u6570\u2014\u2014\u601D\u8003\u7531\u6A21\u578B\u53D8\u4F53\u51B3\u5B9A\uFF08-Thinking \u7CFB\u5217\u59CB\u7EC8\u601D\u8003\u3001\u666E\u901A\u7CFB\u5217\u4E0D\u601D\u8003\uFF09\uFF0C\u52FF\u52FE\u601D\u8003\u6863\u3002ERNIE 4.5 Turbo VL \u652F\u6301\u56FE\u7247\uFF0C\u6309\u9700\u624B\u52FE\uFF1B\u53C2\u8003\u5BB9\u91CF\u53D6 Turbo 128K \u6863\u3002',\n },\n]\n\n/**\n * The generic OpenAI-compatible ladder -- the single source for every\n * OpenAI-shaped protocol row below and the fallback when nothing matches.\n */\nexport const GENERIC_OPENAI_EFFORTS: ReasoningEfforts = {\n off: null,\n low: 'low',\n medium: 'medium',\n high: 'high',\n}\n\n/**\n * Level sets keyed by pi-ai's REAL wire protocols -- the only values a\n * route's 'api' may take are 'openai-completions' | 'openai-responses' |\n * 'anthropic-messages' (llm-pi-ai provider.ts PROTOCOLS) -- plus the\n * 'deepseek' URL dialect for routes that name no api but point at DeepSeek's\n * own endpoint, which takes its native off/high/max ladder.\n *\n * 'off: null' is only offered where a no-thinking mode is plausible; the\n * conservative choice elsewhere is to omit it so the picker never promises an\n * Off the endpoint would reject. Exported for the vocabulary grid tests that\n * pin these ladders against pi-ai's resolution rules.\n */\nexport const PROTOCOL_INFERENCE: Readonly<Record<string, ReasoningEfforts>> = {\n 'openai-completions': GENERIC_OPENAI_EFFORTS,\n 'openai-responses': GENERIC_OPENAI_EFFORTS,\n 'anthropic-messages': GENERIC_OPENAI_EFFORTS,\n // The live V4 contract: low/high/max (+ thinking toggle for off, spelled\n // 'none' so an off selection also maps to the official Responses-API value\n // reasoning.effort: none).\n deepseek: { off: 'none', low: 'low', high: 'high', max: 'max' },\n}\n\n/** A best-effort generic declaration when even the protocol is unknown. */\nconst GENERIC_FALLBACK: ReasoningEfforts = { low: 'low', medium: 'medium', high: 'high' }\n\n/** Normalize one route fact for matching/inference. */\nfunction normalize(value: string | undefined): string {\n return (value ?? '').toLowerCase().trim()\n}\n\n/** Whether a character is a lowercase letter or digit (word-boundary test). */\nfunction isAlnum(ch: string | undefined): boolean {\n return ch !== undefined && /[a-z0-9]/.test(ch)\n}\n\n/**\n * Lowercase and collapse every non-alphanumeric run to one space, so id /\n * display-name / pattern separators ('-', '_', '.', spaces) compare equal:\n * pattern 'deepseek-v3' matches display name \"DeepSeek V3\".\n */\nfunction normalizeLoose(value: string): string {\n return value.toLowerCase().trim().replace(/[^a-z0-9]+/g, ' ')\n}\n\n/**\n * Whether the occurrence of a pattern at index 'at' sits on a word boundary\n * in the haystack: neither neighbor may be a letter/digit. Keeps short family\n * tokens from false-hitting ('o1' must hit 'o1 mini' but not 'ko1 pro';\n * 'seed' hits 'doubao seed 1 6' but not 'seedling').\n */\nfunction onBoundary(haystack: string, at: number, length: number): boolean {\n const before = at === 0 ? undefined : haystack[at - 1]\n const after = haystack[at + length]\n return !isAlnum(before) && !isAlnum(after)\n}\n\n/** Match the knowledge base: first match wins, longest boundary hit wins. */\nexport function matchKnowledgeBase(modelId: string, displayName?: string): KnowledgeEntry | undefined {\n const haystack = normalizeLoose(modelId) + ' ' + normalizeLoose(displayName ?? '')\n let best: { entry: KnowledgeEntry; length: number } | undefined\n for (const entry of KNOWLEDGE_BASE) {\n for (const pattern of entry.patterns) {\n const needle = normalizeLoose(pattern)\n let at = haystack.indexOf(needle)\n while (at >= 0) {\n if (onBoundary(haystack, at, needle.length)) {\n if (best === undefined || needle.length > best.length) {\n best = { entry, length: needle.length }\n }\n break\n }\n at = haystack.indexOf(needle, at + 1)\n }\n }\n }\n return best?.entry\n}\n\n/**\n * Infer which PROTOCOL_INFERENCE key applies to a route. A configured 'api'\n * is already a pi-ai protocol name and wins as-is; otherwise the endpoint's\n * HOST names the dialect -- matched on the registrable domain so an aggregator\n * path like https://gw.example.com/deepseek/v1 stays generic -- and anything\n * unrecognized resolves to the OpenAI-compatible ladder (the overwhelming\n * majority of gateways).\n */\nconst NATIVE_DIALECT_DOMAINS: Readonly<Record<string, string>> = {\n // api.deepseek.com is the official endpoint (api-docs.deepseek.com). The\n // 'deepseek.ai' form was dropped: it resolves to a Vercel-hosted site\n // (76.76.21.21), not DeepSeek infrastructure, so nothing warrants assuming\n // the native dialect for it.\n 'deepseek.com': 'deepseek',\n 'anthropic.com': 'anthropic-messages',\n}\n\nexport function inferProtocol(route: RouteFacts): string {\n const api = normalize(route.api)\n if (api.length > 0) return api\n const baseURL = route.baseURL\n if (baseURL !== undefined && baseURL.length > 0) {\n try {\n const host = new URL(baseURL).hostname.toLowerCase()\n const parts = host.split('.')\n if (parts.length >= 2) {\n const dialect = NATIVE_DIALECT_DOMAINS[parts.slice(-2).join('.')]\n if (dialect !== undefined) return dialect\n }\n } catch {\n // Malformed URL: fall through to the generic ladder.\n }\n }\n // Former 'gemini' guessing removed: pi-ai has no gemini protocol, and\n // Gemini gateways speak the OpenAI-compatible dialect.\n return 'openai-completions'\n}\n\n/**\n * The one wire protocol whose compat gate offers thinkingFormat /\n * supportsReasoningEffort (llm-pi-ai COMPLETIONS_COMPAT_GATE). pi-ai refuses\n * a model-level compat field its resolved protocol does not take\n * (assertServiceable throws at the write), so compat suggestions are gated to\n * this protocol only -- with one exception: the anthropic-messages gate\n * offers forceAdaptiveThinking, which is what makes declared efforts reach\n * the wire as output_config.effort (see {@link KnowledgeEntry.anthropicAdaptive}).\n */\nconst COMPAT_CAPABLE_PROTOCOL = 'openai-completions'\nconst ANTHROPIC_PROTOCOL = 'anthropic-messages'\n\n/** Whether a baseURL names a self-hosted endpoint (loopback, private LAN, .local, custom port). */\nexport function isSelfHostedEndpoint(baseURL: string | undefined): boolean {\n if (baseURL === undefined || baseURL.length === 0) return false\n let host = ''\n try {\n host = new URL(baseURL).hostname.toLowerCase()\n } catch {\n return false\n }\n if (host === 'localhost' || host === '::1' || host.endsWith('.local') || host.endsWith('.internal')) return true\n if (/^(127\\.|10\\.|192\\.168\\.)/.test(host)) return true\n if (/^172\\.(1[6-9]|2[0-9]|3[01])\\./.test(host)) return true\n return host.includes(':')\n}\n\n/** Drop catalog-withheld keys pi-ai never accepts from a profile (defensive). */\nexport function sanitizeCompatForProtocol(compat: CompatSuggestion, api: string): CompatSuggestion | undefined {\n const norm = normalize(api)\n const copy: Record<string, unknown> = { ...compat }\n delete copy['supportsMidConvoEffort']\n delete copy['allowedFallbackModels']\n if (norm === ANTHROPIC_PROTOCOL) {\n for (const key of ['thinkingFormat', 'supportsReasoningEffort', 'thinkingTokenBudgetField', 'supportsThinkingTokenBudget', 'vllmPriority', 'supportsMaxOutputTokens'] as const) delete copy[key]\n if (copy['forceAdaptiveThinking'] === undefined) return undefined\n return { forceAdaptiveThinking: copy['forceAdaptiveThinking'] as boolean }\n }\n if (norm === 'openai-responses') {\n for (const key of ['thinkingFormat', 'supportsReasoningEffort', 'thinkingTokenBudgetField', 'supportsThinkingTokenBudget', 'vllmPriority'] as const) delete copy[key]\n if (copy['supportsMaxOutputTokens'] === undefined) return undefined\n return { supportsMaxOutputTokens: copy['supportsMaxOutputTokens'] as boolean }\n }\n if (norm !== COMPAT_CAPABLE_PROTOCOL) return undefined\n delete copy['forceAdaptiveThinking']\n delete copy['supportsMaxOutputTokens']\n return Object.keys(copy).length === 0 ? undefined : (copy as CompatSuggestion)\n}\n\n/** Gate a compat block against the route's real protocol. */\nfunction compatForRoute(entry: KnowledgeEntry, route: RouteFacts): CompatSuggestion | undefined {\n const api = normalize(route.api)\n if (api === COMPAT_CAPABLE_PROTOCOL) {\n let compat = entry.compat\n if (compat !== undefined) compat = sanitizeCompatForProtocol(compat, api) ?? undefined\n if (compat === undefined && !isSelfHostedEndpoint(route.baseURL)) return undefined\n if (compat === undefined) return { thinkingTokenBudgetField: 'thinking_token_budget' }\n if (compat.thinkingTokenBudgetField === undefined && compat.supportsThinkingTokenBudget !== true && isSelfHostedEndpoint(route.baseURL)) {\n return { ...compat, thinkingTokenBudgetField: 'thinking_token_budget' }\n }\n return compat\n }\n if (api === ANTHROPIC_PROTOCOL && entry.anthropicAdaptive === true) {\n return { forceAdaptiveThinking: true }\n }\n return undefined\n}\n\n/**\n * Official endpoint roots where pi-ai may send role:developer, so no pin is\n * needed. Matching is a DNS-suffix match, so multi-level roots (e.g.\n * openai.azure.com) work while a relay merely hosted on the same cloud\n * (e.g. *.cloudapp.azure.com) still counts as self-hosted.\n *\n * Deliberately fail-safe: hosts outside this set get the pin. `system` is\n * accepted everywhere, `developer` is not, so an unknown host keeps the\n * universally accepted role -- the pin is harmless on official endpoints\n * too. Only add a root after verifying its endpoints accept\n * role:developer (vendor docs or a wire capture); when in doubt, leave it\n * out. The pi-ai-parity entries change no bytes (pi-ai already detects\n * those hosts as nonstandard, i.e. supportsDeveloperRole false); they are\n * listed so the two detectors cannot drift apart silently.\n */\nconst OFFICIAL_RELAY_DOMAINS: readonly string[] = [\n // First-party OpenAI-compatible endpoints (developer accepted).\n 'openai.com',\n 'openai.azure.com',\n 'deepseek.com',\n 'openrouter.ai',\n 'z.ai',\n 'bigmodel.cn',\n 'moonshot.ai',\n 'moonshot.cn',\n 'aliyuncs.com',\n 'anthropic.com',\n 'x.ai',\n 'together.ai',\n 'together.xyz',\n 'nvidia.com',\n 'cerebras.ai',\n 'cloudflare.com',\n // pi-ai-parity: detected nonstandard upstream (pin would be a no-op).\n 'chutes.ai',\n 'opencode.ai',\n 'ant-ling.com',\n]\n\n/**\n * Whether a route is a self-hosted relay whose upstreams cannot be assumed\n * to accept role:developer: openai-completions protocol on a baseURL whose\n * host no official root claims. Absent or malformed URLs answer false --\n * with nothing to judge the host by, pi-ai's detection default stands.\n */\nexport function isSelfHostedRelay(route: RouteFacts): boolean {\n if (normalize(route.api) !== COMPAT_CAPABLE_PROTOCOL) return false\n const baseURL = route.baseURL\n if (baseURL === undefined || baseURL.length === 0) return false\n try {\n const host = new URL(baseURL).hostname.toLowerCase()\n if (!host.includes('.')) return false\n return !OFFICIAL_RELAY_DOMAINS.some((root) => host === root || host.endsWith('.' + root))\n } catch {\n return false\n }\n}\n\n/**\n * Pin the system role on self-hosted relays: merge\n * `supportsDeveloperRole: false` into a compat block that would otherwise\n * let pi-ai rewrite the system prompt to role:developer (issue #2 -- the\n * upstream answers \u89D2\u8272\u4FE1\u606F\u4E0D\u6B63\u786E). An explicitly declared value always\n * wins; non-reasoning suggestions (efforts === false) never carry compat.\n */\nfunction withRolePin(\n compat: CompatSuggestion | undefined,\n route: RouteFacts,\n reasons: boolean,\n): CompatSuggestion | undefined {\n if (compat === undefined || !reasons) return compat\n if (compat.supportsDeveloperRole !== undefined) return compat\n if (!isSelfHostedRelay(route)) return compat\n return { ...compat, supportsDeveloperRole: false }\n}\n\n/** Conservative ladder offered when the endpoint confirms reasoning but nothing names the levels. */\nconst ENDPOINT_CONFIRMED_LADDER: ReasoningEfforts = { off: null, low: 'low', medium: 'medium', high: 'high' }\n\n/**\n * Name-heuristic modality tokens -- the last-resort tier (the practice\n * one-api/new-api bake into code): vision-flavored tokens that appear in\n * vendor ids often enough to suggest image input at LOW confidence. Boundary\n * matching keeps 'vl' from hitting 'eval' or 'svelte'.\n */\nconst VISION_NAME_TOKENS: readonly string[] = [\n 'vl',\n 'vision',\n 'omni',\n '4o',\n 'pixtral',\n 'internvl',\n]\n\n/** Lowest-confidence modality guess from the model id alone, or undefined. */\nexport function inferModalitiesFromName(modelId: string): InputModalities | undefined {\n const haystack = normalizeLoose(modelId)\n for (const token of VISION_NAME_TOKENS) {\n const needle = normalizeLoose(token)\n let at = haystack.indexOf(needle)\n while (at >= 0) {\n if (onBoundary(haystack, at, needle.length)) return ['text', 'image']\n at = haystack.indexOf(needle, at + 1)\n }\n }\n return undefined\n}\n\n/**\n * Intersect a raw listing disclosure with the core vocabulary. Returns\n * undefined when the listing never named a modality field OR named one whose\n * members all fall outside the core vocabulary (an audio-only declaration\n * must NOT read as \"text only\"). A present disclosure always yields at least\n * text-only when image is absent -- so an explicit text-only listing can\n * strip an image claim.\n */\nfunction declaredCoreInput(raw: readonly string[] | undefined): InputModalities | undefined {\n if (raw === undefined) return undefined\n const lowered = raw.map(value => value.toLowerCase())\n if (!lowered.some(value => (INPUT_MODALITIES as readonly string[]).includes(value))) return undefined\n return lowered.includes('image') ? ['text', 'image'] : ['text']\n}\n\n/**\n * Resolve the suggestion for one model on one route, fusing the available\n * signals by confidence:\n *\n * L1 endpoint listing signal (when supplied) -- answers \"does it reason at\n * all\" and, independently, \"what does it accept\": an explicit false\n * wins the effort ladder outright (suggest false, high); a modality\n * disclosure or a context length feeds those parts regardless;\n * L2 knowledge base -- the ONLY source of wire spellings and compat, plus\n * modality/capacity references when the endpoint stays silent;\n * L3 name heuristic -- a low-confidence image guess for vision-flavored\n * ids when both sources above are silent;\n * L4 protocol inference -- level-ladder skeleton when nothing better exists.\n *\n * @param modelId - the model id (or display name) to match.\n * @param route - route facts used when the knowledge base misses; its\n * displayName participates in knowledge-base matching.\n * @param endpoint - optional raw-listing facts for this model (from the\n * host's same-origin probe route); omit when the endpoint was not asked.\n * @returns a suggestion (always derivable -- protocol inference has a fallback).\n */\nexport function suggestEfforts(\n modelId: string,\n route: RouteFacts,\n endpoint?: EndpointFacts,\n): EffortSuggestion {\n const entry = matchKnowledgeBase(modelId, route.displayName)\n\n // ---- Modality + capacity parts (orthogonal to the effort ladder) ----\n let input: InputModalities | undefined\n let inputSource: InputSource | undefined\n let contextWindow: number | undefined\n let maxTokens: number | undefined\n\n if (entry?.input !== undefined) {\n input = entry.input\n inputSource = 'knowledge'\n }\n if (entry?.contextWindow !== undefined) contextWindow = entry.contextWindow\n if (entry?.maxTokens !== undefined) maxTokens = entry.maxTokens\n\n // An endpoint disclosure outranks the knowledge base; a disclosed context\n // length likewise replaces the reference value. Silence changes nothing.\n const listedInput = declaredCoreInput(endpoint?.input)\n if (listedInput !== undefined) {\n input = listedInput\n inputSource = 'endpoint'\n }\n if (endpoint?.contextLength !== undefined && Number.isFinite(endpoint.contextLength) && endpoint.contextLength > 0) {\n contextWindow = endpoint.contextLength\n }\n\n // L3: nothing named the modalities -- fall back to the name heuristic.\n if (input === undefined) {\n const guessed = inferModalitiesFromName(modelId)\n if (guessed !== undefined) {\n input = guessed\n inputSource = 'heuristic'\n }\n }\n\n const modalityParts = {\n ...(input === undefined ? {} : { input }),\n ...(inputSource === undefined ? {} : { inputSource }),\n ...(contextWindow === undefined ? {} : { contextWindow }),\n ...(maxTokens === undefined ? {} : { maxTokens }),\n }\n\n const endpointPart = endpoint === undefined\n ? {}\n : { endpoint: { reasoning: endpoint.reasoning, source: endpoint.source } }\n\n // L1: an explicit \"does not reason\" from the endpoint outranks everything.\n if (endpoint?.reasoning === false) {\n return {\n efforts: false,\n matched: false,\n source: 'endpoint:' + (endpoint.source ?? 'listing'),\n confidence: 'high',\n ...endpointPart,\n ...modalityParts,\n }\n }\n\n if (entry !== undefined) {\n const compat = withRolePin(compatForRoute(entry, route), route, entry.efforts !== false)\n return {\n efforts: entry.efforts,\n // The vendor default rides along only when the suggested ladder\n // actually carries it (a declaration may deliberately drop a level).\n ...(entry.efforts !== false\n && entry.defaultEffort !== undefined\n && entry.efforts[entry.defaultEffort] !== undefined\n ? { defaultEffort: entry.defaultEffort }\n : {}),\n ...(compat === undefined ? {} : { compat }),\n matched: true,\n entryId: entry.id,\n source: entry.id,\n // Knowledge base carries the wire values; an agreeing endpoint signal\n // only reinforces it. A DISAGREEING signal (endpoint says no reasoning)\n // was handled above; unknown changes nothing.\n confidence: 'high',\n ...endpointPart,\n ...modalityParts,\n }\n }\n\n // L2 missed: the ladder comes from protocol inference. An explicit endpoint\n // \"reasons\" upgrades confidence to medium -- support is confirmed, but the\n // level set and spellings are inferred, so the user should double-check.\n const protocol = inferProtocol(route)\n const efforts = PROTOCOL_INFERENCE[protocol] ?? GENERIC_FALLBACK\n const compatBase: CompatSuggestion | undefined = normalize(route.api) === COMPAT_CAPABLE_PROTOCOL\n ? { thinkingFormat: 'openai', supportsReasoningEffort: true }\n : undefined\n const compatWithBudget = compatBase !== undefined && isSelfHostedEndpoint(route.baseURL)\n ? { ...compatBase, thinkingTokenBudgetField: 'thinking_token_budget' as const }\n : compatBase\n const compat = withRolePin(compatWithBudget, route, true)\n if (endpoint?.reasoning === true) {\n return {\n efforts: { ...ENDPOINT_CONFIRMED_LADDER },\n ...(ENDPOINT_CONFIRMED_LADDER['medium'] !== undefined ? { defaultEffort: 'medium' as const } : {}),\n // compat was already gated to the protocol above; no second gate.\n ...(compat === undefined ? {} : { compat }),\n matched: false,\n source: 'endpoint:' + (endpoint.source ?? 'listing'),\n confidence: 'medium',\n ...endpointPart,\n ...modalityParts,\n }\n }\n return {\n efforts,\n // Inferred ladders default to medium (the convention codex/OpenAI set for\n // their own ladders) whenever the inferred set carries it.\n ...(efforts['medium'] !== undefined ? { defaultEffort: 'medium' as const } : {}),\n ...(compat === undefined ? {} : { compat }),\n matched: false,\n source: 'protocol:' + protocol,\n confidence: 'low',\n ...endpointPart,\n ...modalityParts,\n }\n}\n", "/**\n * Effort memory: per-model reasoning-effort levels, re-applied automatically\n * when a model switch lands without one.\n *\n * The official model list submits a model switch as a selection WITHOUT an\n * effort unless the model advertises a default (`ModelSelect` builds its\n * choices from `reasoning.defaultEffort`, which hand-declared models never\n * carry) \u2014 and the host replaces the session's whole selection, so the\n * previous level is dropped and the trigger reads \"Default\". Wrapping the\n * shared per-session directory's `select` (the SAME instance the official\n * seat, the /model popup, and this plugin's slider all submit through) lets\n * the remembered level ride the switch in ONE atomic commit: no \"Default\"\n * flash, and every surface shows the re-applied level.\n *\n * The memory is keyed by \"provider/model-id\" \u2014 the exact fields a selection\n * carries \u2014 because the same model id under two providers names two models,\n * and display names are editable and never unique. Two delivery paths share\n * one fallback chain (the SESSION'S own sighting of a level the user picked\n * in it, else the per-model configured pick from the settings document, else\n * the last cross-session memory from localStorage, else the VENDOR'S documented\n * default from the knowledge base, else the official no-effort behaviour):\n * - the wrapped `select`: a model switch submitted without a level rides\n * the remembered level in ONE atomic commit \u2014 no \"Default\" flash, and\n * every surface shows the re-applied level;\n * - the projection watcher: a RESTORED session's durable selection arrives\n * from session history without going through `select` at all, so the\n * watcher re-applies the chain to a level-less projection (one successful\n * attempt per model per directory lifetime; a REFUSED attempt is refunded\n * and retried on the next store update, up to a small ceiling \u2014 which\n * also keeps an explicit provider-default pick from being fought).\n * Discipline both paths keep:\n * - an EXPLICIT level (slider drag, any effort row) always wins: it is\n * remembered for that exact model and submitted untouched \u2014 and it is\n * recorded as a SESSION sighting, which outranks the configured pick for\n * the rest of the session (switching away and back must not lose it);\n * - an effort-less selection for the CURRENT model is an explicit \"follow\n * the provider default\" and must pass through untouched \u2014 the session\n * sighting dies with it;\n * - nothing is injected while the slider preference (the seat the memory\n * serves) is switched off.\n *\n * @module dsh-model-think-level/client/effort-memory\n */\nimport { sliderEnabled } from './slider-pref.js'\nimport { suggestEfforts } from '../knowledge.js'\nimport type { DirectoryCurrentLike, ModelDirectoryLike, ModelDirectoryStateLike } from './types.js'\n\n/**\n * The wiring-provided read of the per-model configured pick (the settings\n * document's `defaultEffort` field). Async because it lives in the pi-ai\n * document, not in localStorage; the wiring owns the caching describe.\n * Absent (tests, or a wiring that does not supply it) skips the layer.\n */\nexport interface EffortMemoryDeps {\n configuredEffort?: (provider: string, model: string) => Promise<string | undefined>\n}\n\n/** localStorage key (same namespace discipline as the slider preference). */\nconst EFFORT_MEMORY_KEY = 'dsh-model-think-level.slider.efforts'\n\n/** Marker guarding against double-wrapping one directory instance (HMR re-apply). */\nconst WIRED_MARKER = 'breSelectWired'\n\n/**\n * Whether a `select` outcome is a refusal.\n *\n * Kernels through 0.1.6-alpha.1 reject by THROWING; 0.1.6-alpha.2 resolves the\n * wire's `RemoteResult` (`{ok:false}`) instead. Normalizing both shapes here\n * keeps the memory discipline identical on either kernel.\n * @param outcome - the value `select` resolved with (or undefined on success).\n * @returns whether the host refused the selection.\n */\nexport function isSelectRefusal(outcome: unknown): boolean {\n return typeof outcome === 'object' && outcome !== null && (outcome as { ok?: unknown }).ok === false\n}\n\n/**\n * The user-facing message of a refusal result, or undefined when the outcome\n * was accepted. Only 0.1.6-alpha.2's resolved shape carries one; a kernel that\n * rejects by throwing never reaches here.\n * @param outcome - the value `select` resolved with.\n * @returns `code: message`, or undefined when accepted.\n */\nexport function selectRefusalMessage(outcome: unknown): string | undefined {\n if (!isSelectRefusal(outcome)) return undefined\n const error = (outcome as { error?: { code?: unknown; message?: unknown } }).error\n const code = typeof error?.code === 'string' ? error.code : 'settings/rejected'\n const message = typeof error?.message === 'string' ? error.message : ''\n return message.length > 0 ? `${code}: ${message}` : code\n}\n\n/** Per-model memory: \"provider/model-id\" \u2192 the last level explicitly picked for it. */\ntype EffortMemory = Record<string, string>\n\n/** The storage key of one model: the exact fields a selection carries. */\nfunction memoryKey(provider: string, model: string): string {\n return `${provider}/${model}`\n}\n\n/**\n * Read the per-model memory, defensively: the value is plain JSON in\n * localStorage, so a malformed or hostile document degrades to \"no memory\"\n * instead of poisoning the wrapper.\n */\nfunction readMemory(): EffortMemory {\n try {\n const raw = window.localStorage.getItem(EFFORT_MEMORY_KEY)\n if (raw === null) return {}\n const parsed: unknown = JSON.parse(raw)\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) return {}\n const memory: EffortMemory = {}\n for (const [key, value] of Object.entries(parsed as Record<string, unknown>)) {\n if (key.length > 0 && typeof value === 'string' && value.trim().length > 0) memory[key] = value\n }\n return memory\n } catch {\n return {}\n }\n}\n\nfunction writeMemory(memory: EffortMemory): void {\n try {\n window.localStorage.setItem(EFFORT_MEMORY_KEY, JSON.stringify(memory))\n } catch {\n // Unavailable storage (quota, privacy mode): this page keeps working with\n // the levels it picked; the switch fallback below does not depend on us.\n }\n}\n\n/** Record the level the user explicitly picked for one model. */\nexport function rememberEffort(provider: string, model: string, id: string): void {\n const memory = readMemory()\n memory[memoryKey(provider, model)] = id\n writeMemory(memory)\n}\n\n/** The level explicitly picked for one model, or undefined when none was. */\nexport function rememberedEffort(provider: string, model: string): string | undefined {\n return readMemory()[memoryKey(provider, model)]\n}\n\n/** The effort level ids one model's entry advertises in a directory snapshot. */\nfunction effortIdsOf(\n state: ModelDirectoryStateLike,\n provider: string,\n model: string,\n): readonly string[] {\n const group = state.groups.find(candidate => candidate.id === provider)\n const efforts = group?.models.find(candidate => candidate.id === model)?.reasoning?.efforts\n return efforts === undefined ? [] : efforts.map(level => level.id)\n}\n\n/** The model-advertised default level, when the catalog carries one. */\nfunction advertisedDefaultOf(\n state: ModelDirectoryStateLike,\n provider: string,\n model: string,\n): string | undefined {\n const group = state.groups.find(candidate => candidate.id === provider)\n return group?.models.find(candidate => candidate.id === model)?.reasoning?.defaultEffort\n}\n\n/**\n * The level the composer body displays when a ladder has no current or declared\n * default. Keep the wire fallback identical to that visual prediction; otherwise\n * the body can say \"Medium\" while the native trigger still says \"Default\".\n * A one-level ladder remains provider-owned because there is no real choice to\n * restore and the existing no-fallback behavior is useful for capability probes.\n */\nfunction displayedMiddleOf(supported: readonly string[]): string | undefined {\n if (supported.length < 2) return undefined\n return supported[Math.floor((supported.length - 1) / 2)]\n}\n\n/** Whether the selection moves to a DIFFERENT model (or picks the first one). */\nfunction isModelSwitch(\n current: DirectoryCurrentLike | null,\n selection: { provider: string; model: string },\n): boolean {\n return current === null\n || current.provider !== selection.provider\n || current.model !== selection.model\n}\n\n/**\n * Resolve the level the memory chain lands on for one model of a snapshot:\n * the SESSION'S own sighting (a level the user picked in this session --\n * switching away and back must not lose it), else the per-model configured\n * pick from the settings document, else the cross-session memory, else the\n * vendor's documented default, else the catalog's advertised default, else the\n * same middle-level preview used by the composer body \u2014 always validated\n * against the ADVERTISED ladder, and undefined when the ladder has no usable\n * fallback.\n * Async only because the configured pick reads the settings document; every\n * other layer is synchronous.\n */\nasync function resolveFallback(\n snapshot: ModelDirectoryStateLike,\n provider: string,\n model: string,\n sessionSightings: ReadonlyMap<string, string>,\n deps: EffortMemoryDeps | undefined,\n): Promise<string | undefined> {\n const supported = effortIdsOf(snapshot, provider, model)\n if (supported.length === 0) return undefined\n const key = memoryKey(provider, model)\n const sighted = sessionSightings.get(key)\n if (sighted !== undefined && supported.includes(sighted)) return sighted\n const configured = await deps?.configuredEffort?.(provider, model)\n if (configured !== undefined && supported.includes(configured)) return configured\n const memory = rememberedEffort(provider, model)\n if (memory !== undefined && supported.includes(memory)) return memory\n const fallback = suggestEfforts(model, {}).defaultEffort\n if (fallback !== undefined && supported.includes(fallback)) return fallback\n const advertised = advertisedDefaultOf(snapshot, provider, model)\n if (advertised !== undefined && supported.includes(advertised)) return advertised\n return displayedMiddleOf(supported)\n}\n\n/**\n * Wire one directory with the effort-memory discipline: wrap its `select`\n * (model-switch path) and watch its store (projection-restore path).\n * Idempotent: an already-wired directory is left alone and the returned\n * disposer is a no-op. The disposer unsubscribes the watcher and restores\n * the original `select` (the directory instance outlives the plugin fiber\n * on disable/HMR).\n * @param directory - the shared per-session directory to wire.\n * @param deps - optional wiring face (the configured-pick read).\n * @returns the disposer restoring the original `select` and the watcher.\n */\nexport function wireEffortMemory(directory: ModelDirectoryLike, deps?: EffortMemoryDeps): () => void {\n const target = directory as unknown as Record<string, unknown>\n if (target[WIRED_MARKER] === true) return () => {}\n // Whether `select` was an OWN property at wire time decides the restore:\n // real directories carry it on the prototype, so the disposer removes the\n // instance shadow entirely instead of freezing a copy of today's method\n // onto the instance (which would keep shadowing a hot-swapped prototype).\n const hadOwnSelect = Object.prototype.hasOwnProperty.call(target, 'select')\n const original = directory.select\n // Levels the user picked IN THIS SESSION, per model. The directory (and\n // this closure) is a per-session instance, so the map is session-scoped by\n // construction: it outranks the configured pick for the rest of the\n // session \u2014 switching away and back must not lose the user's pick \u2014 and\n // dies with the session instead of leaking into the next one.\n const sessionSightings = new Map<string, string>()\n // One successful restore per model per directory lifetime: spent attempts\n // are never retried, which bounds the watcher (no loop on a refused write)\n // and keeps an explicit provider-default pick from being fought. A REFUSED\n // submission refunds its attempt -- a brand-new session's host may refuse\n // the first selectModel while the session is still initializing -- up to a\n // small ceiling, beyond which the model is given up on for this directory.\n // A chain MISS refunds too: the configured layer reads live settings, so a\n // later document change can answer differently.\n const attemptedRestores = new Set<string>()\n const restoreFailures = new Map<string, number>()\n\n /** Stop retrying one model after this many refused restores (per directory). */\n const RESTORE_RETRY_LIMIT = 3\n\n // Every wrapped select call is a user utterance, counted in order. Both\n // async submission paths (the wrapped switch and the watcher) verify just\n // before their post-await submission that no LATER utterance landed \u2014 a\n // newer user action always wins, whatever it said, and the superseded\n // submission is dropped entirely instead of overwriting it.\n let utterances = 0\n\n const wrapped = async (\n selection: Parameters<ModelDirectoryLike['select']>[0],\n ): Promise<unknown> => {\n const myUtterance = ++utterances\n // An explicit level is the memory's source of truth \u2014 but only an\n // ACCEPTED one: a refused selection (host validation failed) must not\n // poison the memory and re-fail every later switch.\n if (selection.reasoningEffort !== undefined) {\n const result = await original.call(directory, selection)\n // Recording is part of the plugin's presence: with the slider off the\n // plugin is absent, so an explicit pick must not be recorded either \u2014\n // the wrapped submit then stays a pure pass-through, exactly as the\n // unwired directory behaved. A REFUSED pick is never recorded, whether\n // the kernel rejected by throwing (<= 0.1.6-alpha.1) or by resolving a\n // refusal result (0.1.6-alpha.2).\n if (!isSelectRefusal(result) && sliderEnabled()) {\n sessionSightings.set(memoryKey(selection.provider, selection.model), selection.reasoningEffort)\n rememberEffort(selection.provider, selection.model, selection.reasoningEffort)\n }\n return result\n }\n const snapshot = directory.store.getSnapshot()\n // Same model, no level: an explicit \"follow the provider default\" \u2014\n // the session sighting dies with it, so this session keeps honouring\n // the user's pick for the rest of its life. The model's restore attempt\n // is spent with it: the confirmed selection re-notifies the watcher with\n // a level-less projection, and the chain must not answer it by fighting\n // the pick the user just made. (Both bookkeeping moves are gated like\n // the recording above \u2014 with the slider off this is a plain pass-through.)\n if (!isModelSwitch(snapshot.current, selection)) {\n if (sliderEnabled()) {\n const key = memoryKey(selection.provider, selection.model)\n sessionSightings.delete(key)\n attemptedRestores.add(key)\n }\n return original.call(directory, selection)\n }\n // A switch without a level: re-apply the session's sighting, the\n // configured pick, the model's own memory, the vendor/catalog default, or\n // the same middle-level preview the composer displays \u2014 so the native\n // trigger and the replicated body settle on one level.\n if (!sliderEnabled()) return original.call(directory, selection)\n const fallback = await resolveFallback(snapshot, selection.provider, selection.model, sessionSightings, deps)\n // The chain awaited (the configured layer may have made a wire round\n // trip): re-verify before speaking. A newer user action \u2014 an explicit\n // pick, a follow-the-default, or a different selection \u2014 that landed\n // during the window is final: the stale submission is dropped entirely\n // instead of overwriting it. \"Newer\" means the store's selection moved\n // off the snapshot this call started from, or another wrapped call ran.\n const fresh = directory.store.getSnapshot()\n const moved = (a: DirectoryCurrentLike | null, b: DirectoryCurrentLike | null): boolean =>\n a === null || b === null\n ? a !== b\n : a.provider !== b.provider || a.model !== b.model || a.reasoningEffort !== b.reasoningEffort\n if (utterances !== myUtterance || moved(fresh.current, snapshot.current)) return undefined\n // The chain awaited: the gate is re-checked BEFORE speaking, or a slider\n // toggle landing inside the await window would make the plugin inject a\n // level while it is already absent (the switch itself must still land --\n // as the plain pass-through the unwired directory would have performed).\n if (!sliderEnabled()) return original.call(directory, selection)\n if (fallback === undefined) return original.call(directory, selection)\n // The catalog can refresh during the same window: the level must still\n // sit on the CURRENT advertised ladder.\n if (!effortIdsOf(fresh, selection.provider, selection.model).includes(fallback)) {\n return original.call(directory, selection)\n }\n return original.call(directory, { ...selection, reasoningEffort: fallback })\n }\n\n /** Re-apply the chain to a level-less durable projection (session restore). */\n const restoreProjection = (): void => {\n const snapshot = directory.store.getSnapshot()\n const current = snapshot.current\n if (current === null || current.reasoningEffort !== undefined) return\n // The gate comes BEFORE the attempt is spent: with the slider off the\n // plugin is absent, and absence must not burn the model's one attempt.\n if (!sliderEnabled()) return\n const key = memoryKey(current.provider, current.model)\n if (attemptedRestores.has(key)) return\n // A model with no advertised ladder cannot be spoken to at all: a\n // deterministic miss \u2014 spend the attempt so the watcher does not\n // re-resolve the same dead end on every store update.\n if (effortIdsOf(snapshot, current.provider, current.model).length === 0) {\n attemptedRestores.add(key)\n return\n }\n attemptedRestores.add(key)\n const utteranceAtAttempt = utterances\n // Directly through the ORIGINAL select: the watcher's own re-apply is a\n // memory READ, not a user pick, and must not be remembered as one.\n void resolveFallback(snapshot, current.provider, current.model, sessionSightings, deps)\n .then(async (fallback) => {\n if (fallback === undefined) {\n // The chain lands nowhere for now \u2014 refund: the configured layer\n // reads live settings, so a later document change may answer.\n attemptedRestores.delete(key)\n return\n }\n // The chain awaited: the gate is re-checked before speaking. A toggle\n // landing inside the window must leave the attempt REFUNDED, not\n // spent -- the watcher is silent while the slider is off, so a spent\n // attempt here would leave the model un-restored forever after the\n // slider comes back (nothing re-kicks a spent restore).\n if (!sliderEnabled()) {\n attemptedRestores.delete(key)\n return\n }\n // The chain awaited: re-verify against the CURRENT store before\n // speaking. A newer user utterance (an explicit pick, or a\n // follow-the-default that landed mid-window) is final \u2014 this attempt\n // stays spent instead of overriding it.\n const fresh = directory.store.getSnapshot()\n const freshCurrent = fresh.current\n if (utterances !== utteranceAtAttempt\n || freshCurrent === null || freshCurrent.reasoningEffort !== undefined\n || memoryKey(freshCurrent.provider, freshCurrent.model) !== key\n || !effortIdsOf(fresh, freshCurrent.provider, freshCurrent.model).includes(fallback)) {\n return\n }\n const outcome = await original\n .call(directory, { provider: freshCurrent.provider, model: freshCurrent.model, reasoningEffort: fallback })\n // 0.1.6-alpha.2 resolves the refusal instead of throwing: route it to\n // the refund path below, or a transient refusal would spend the\n // model's only attempt.\n if (isSelectRefusal(outcome)) throw outcome\n })\n .catch(() => {\n // A refusal is transient (a session mid-initialization), not a\n // decision: refund the attempt so the next store update retries --\n // but stop at the ceiling so a persistently rejected submission\n // cannot turn the watcher into a loop.\n const failures = (restoreFailures.get(key) ?? 0) + 1\n restoreFailures.set(key, failures)\n if (failures < RESTORE_RETRY_LIMIT) {\n attemptedRestores.delete(key)\n // The refusal's own error update reached this watcher BEFORE this\n // refund (the store notifies synchronously, the refund is a\n // microtask), so \"wait for the next store update\" may wait forever\n // on a quiet session: re-kick the watcher here, ceiling still\n // bounding the loop.\n restoreProjection()\n }\n })\n }\n\n const unsubscribe = directory.store.subscribe(restoreProjection)\n // The projection may already be resident when the directory gets wired.\n restoreProjection()\n\n target[WIRED_MARKER] = true\n directory.select = wrapped as typeof directory.select\n return () => {\n delete target[WIRED_MARKER]\n unsubscribe()\n if (hadOwnSelect) directory.select = original\n else delete target['select']\n }\n}\n", "/**\n * Real-time search filter for the composer model selection panel.\n *\n * Injected above the official provider-group list while the drilled-in model\n * pane is open (`section[role=\"group\"]` present). Filters the official option\n * buttons and provider groups via zero-dependency DOM visibility toggles\n * without mutating the underlying ModelDirectory store, and never touches the\n * official card's own sizing or scrolling.\n *\n * @module dsh-model-think-level/client/ModelSearch\n */\n\nimport { createElement, useCallback, useEffect, useRef, useState } from 'react'\nimport type { KeyboardEvent as ReactKeyboardEvent, ReactNode } from 'react'\n\n/** Props of {@link ModelSearch}. */\nexport interface ModelSearchProps {\n /** The official menu element containing group sections and model options. */\n menu: HTMLElement\n /** Localized copy translator. */\n t: (key: string, params?: Record<string, string | number>) => string\n}\n\n/** The official provider-group sections: also the model pane's own marker. */\nconst GROUP_SELECTOR = 'section[role=\"group\"]'\n\n/** The official option buttons (model rows AND effort rows share this role). */\nconst OPTION_SELECTOR = 'button[role=\"menuitemradio\"]'\n\n/**\n * Whether the menu currently shows the drilled-in model list.\n *\n * Deliberately structural: the official effort pane renders\n * `button[role=\"menuitemradio\"]` too (ModelSelect.tsx:464) and would match a\n * role-based probe, while only the model pane renders provider groups. The\n * probe also avoids class names \u2014 the groups container's `groups` class is a\n * CSS-module hash (`Uc5hea_groups` in the official build), so a literal\n * `.groups` selector never matches.\n */\nexport function isModelPane(menu: HTMLElement): boolean {\n return menu.querySelector(GROUP_SELECTOR) !== null\n}\n\n/** The visible, enabled option buttons, in document order. */\nexport function visibleOptions(menu: HTMLElement): HTMLButtonElement[] {\n return Array.from(menu.querySelectorAll<HTMLButtonElement>(OPTION_SELECTOR))\n .filter(btn => !btn.disabled && btn.style.display !== 'none')\n}\n\n/**\n * A provider group's heading text, read through the official aria contract\n * (`section[role=\"group\"][aria-labelledby]`, ModelSelect.tsx:415-416).\n *\n * No structural fallback: \"the first descendant carrying an id\" would be the\n * first MODEL NAME whenever the heading is missing, which would then match\n * that group on any query containing it.\n */\nfunction groupNameOf(group: HTMLElement): string {\n const labelledBy = group.getAttribute('aria-labelledby')\n const heading = labelledBy === null ? null : group.ownerDocument.getElementById(labelledBy)\n return heading?.textContent?.toLowerCase() ?? ''\n}\n\n/**\n * Filter official model option buttons and provider groups by search query.\n *\n * `touched` records the inline `display` each node held BEFORE this filter\n * first changed it, so the restore pass puts back the node's own value instead\n * of blindly clearing an inline style the official menu may have set itself.\n * @param menu - the official menu element.\n * @param query - the raw search text; empty restores everything.\n * @param touched - carry-over map of the nodes this filter currently hides.\n * @returns number of visible model buttons across all groups.\n */\nexport function filterMenuModels(\n menu: HTMLElement,\n query: string,\n touched: Map<HTMLElement, string> = new Map(),\n): number {\n const hide = (element: HTMLElement): void => {\n if (!touched.has(element)) touched.set(element, element.style.display)\n element.style.display = 'none'\n }\n const show = (element: HTMLElement): void => {\n const original = touched.get(element)\n if (original === undefined) return\n element.style.display = original\n touched.delete(element)\n }\n // The official list replaces its rows on a re-render; detached nodes must not\n // accumulate in the map for the lifetime of an open menu.\n for (const element of [...touched.keys()]) {\n if (!element.isConnected) touched.delete(element)\n }\n\n const tokens = query.trim().toLowerCase().split(/\\s+/).filter(Boolean)\n const groups = Array.from(menu.querySelectorAll<HTMLElement>(GROUP_SELECTOR))\n let totalVisible = 0\n\n // An empty query restores EVERYTHING unconditionally \u2014 including a group\n // that carries a heading but no options at all, which the matching branch\n // below would otherwise hide.\n if (tokens.length === 0) {\n for (const element of [...touched.keys()]) show(element)\n for (const group of groups) {\n totalVisible += group.querySelectorAll(OPTION_SELECTOR).length\n }\n return totalVisible\n }\n\n for (const group of groups) {\n const groupName = groupNameOf(group)\n const buttons = Array.from(group.querySelectorAll<HTMLElement>(OPTION_SELECTOR))\n let visibleInGroup = 0\n\n for (const btn of buttons) {\n const titleText = (btn.getAttribute('title') ?? '').toLowerCase()\n const contentText = (btn.textContent ?? '').toLowerCase()\n const combined = `${groupName} ${titleText} ${contentText}`\n\n if (tokens.every(token => combined.includes(token))) {\n show(btn)\n visibleInGroup += 1\n } else {\n hide(btn)\n }\n }\n\n if (visibleInGroup > 0) {\n show(group)\n totalVisible += visibleInGroup\n } else {\n hide(group)\n }\n }\n\n return totalVisible\n}\n\n/**\n * Search input and empty-state controller for the model menu.\n */\nexport function ModelSearch({ menu, t }: ModelSearchProps): ReactNode {\n const [query, setQuery] = useState('')\n // No sentinel needed: an empty query can never show the empty state, so the\n // first frame is correct before any filter has run.\n const [totalVisible, setTotalVisible] = useState(0)\n const inputRef = useRef<HTMLInputElement>(null)\n const queryRef = useRef('')\n // The nodes this filter currently hides, with the inline display each held\n // before this plugin first touched it (see filterMenuModels).\n const touchedRef = useRef(new Map<HTMLElement, string>())\n\n const runFilter = useCallback((nextQuery: string): void => {\n queryRef.current = nextQuery\n const count = filterMenuModels(menu, nextQuery, touchedRef.current)\n setTotalVisible(count)\n }, [menu])\n\n // Restore every row this filter hid when the box goes away (pane switch,\n // menu close, plugin dispose). There is deliberately NO mount-time pass: the\n // initial query is empty, so the list is already unfiltered \u2014 and a mount\n // pass would run from a PASSIVE effect, i.e. it can land AFTER a keystroke\n // the user already made and silently wipe the filter with it.\n useEffect(() => () => {\n filterMenuModels(menu, '', touchedRef.current)\n }, [menu])\n\n // Keep the filter applied across the official list's OWN re-renders: the\n // directory store can push a fresh catalog (an edit elsewhere, a connection\n // reset) while the menu is open, and the rows React rebuilds come back\n // visible with the query still in the box. Only childList is observed, so\n // this plugin's own display writes can never re-trigger the observer.\n useEffect(() => {\n const observer = new MutationObserver(() => {\n if (queryRef.current.trim().length === 0) return\n runFilter(queryRef.current)\n })\n observer.observe(menu, { childList: true, subtree: true })\n return () => { observer.disconnect() }\n }, [menu, runFilter])\n\n // While a filter is active the plugin owns the list-navigation keys for rows\n // INSIDE the list. Reason: the official roving focus (ModelSelect.tsx:215-226)\n // walks every ref'd row, including the ones this filter hid \u2014 focus() on a\n // display:none node is a no-op, so the official handler would stick on the\n // same item forever (and Home/End/PageUp/PageDown would do nothing at all).\n // A CAPTURE-phase native listener stops the event before the official\n // bubble-phase handler (delegated to the portaled container, an ancestor of\n // this plugin's own root container) ever sees it.\n //\n // It must NOT touch keys aimed at the input: the input lives inside this\n // plugin's own React root, whose container is a DESCENDANT of the menu, so\n // capturing here and stopping would swallow the key before the input's own\n // React handler could run.\n useEffect(() => {\n const onKeyDown = (event: KeyboardEvent): void => {\n if (queryRef.current.trim().length === 0) return\n const key = event.key\n const arrow = key === 'ArrowDown' || key === 'ArrowUp'\n const jump = key === 'Home' || key === 'End' || key === 'PageUp' || key === 'PageDown'\n if (!arrow && !jump) return\n if (event.target === inputRef.current) return\n if (event.target instanceof Node && !menu.contains(event.target)) return\n const options = visibleOptions(menu)\n if (options.length === 0) return\n const current = options.findIndex(option => option === document.activeElement)\n let next: number\n if (key === 'Home' || key === 'PageUp') next = 0\n else if (key === 'End' || key === 'PageDown') next = options.length - 1\n else if (key === 'ArrowDown') next = current === -1 ? 0 : (current + 1) % options.length\n else next = current === -1 ? options.length - 1 : (current - 1 + options.length) % options.length\n event.preventDefault()\n event.stopPropagation()\n options[next]?.focus()\n }\n menu.addEventListener('keydown', onKeyDown, true)\n return () => { menu.removeEventListener('keydown', onKeyDown, true) }\n }, [menu])\n\n const handleKeyDown = (event: ReactKeyboardEvent<HTMLInputElement>): void => {\n const key = event.key\n if (key === 'ArrowDown') {\n event.preventDefault()\n // The official handler runs later in this same dispatch (from an\n // ancestor container) and would step one row PAST the one focused here,\n // so the keystroke is consumed instead.\n event.stopPropagation()\n visibleOptions(menu)[0]?.focus()\n return\n }\n if (query.trim().length > 0 && (key === 'ArrowUp' || key === 'Home' || key === 'PageUp' || key === 'End' || key === 'PageDown')) {\n const options = visibleOptions(menu)\n if (options.length > 0) {\n event.preventDefault()\n event.stopPropagation()\n const last = key === 'ArrowUp' || key === 'End' || key === 'PageDown'\n options[last ? options.length - 1 : 0]?.focus()\n }\n return\n }\n if (key === 'Escape' && query.length > 0) {\n event.preventDefault()\n event.stopPropagation()\n setQuery('')\n runFilter('')\n }\n }\n\n const handleClear = (): void => {\n setQuery('')\n runFilter('')\n inputRef.current?.focus({ preventScroll: true })\n }\n\n const hasEmptyState = totalVisible === 0 && query.trim().length > 0\n\n return createElement(\n 'div',\n { className: 'bre-model-search-box' + (hasEmptyState ? ' has-empty' : ''), 'data-bre-search': '1' },\n createElement(\n 'div',\n { className: 'bre-search-input-wrapper' },\n createElement(\n 'span',\n { className: 'bre-search-icon', 'aria-hidden': 'true' },\n createElement(\n 'svg',\n { viewBox: '0 0 16 16', fill: 'none', stroke: 'currentColor', strokeWidth: '1.5' },\n createElement('circle', { cx: '7', cy: '7', r: '4.5' }),\n createElement('path', { d: 'M10.5 10.5L14 14', strokeLinecap: 'round' }),\n ),\n ),\n createElement('input', {\n ref: inputRef,\n type: 'text',\n className: 'bre-search-input',\n // ARIA has no legal textbox inside `role=\"menu\"`; the box is labelled\n // and fully operable, so `searchbox` is the least-wrong role for it \u2014\n // the same documented-tradeoff shape the slider's hidden cells carry.\n role: 'searchbox',\n placeholder: t('modelSearchPlaceholder'),\n 'aria-label': t('modelSearchPlaceholder'),\n value: query,\n spellCheck: false,\n autoComplete: 'off',\n onChange: (e: { currentTarget: HTMLInputElement }) => {\n const val = e.currentTarget.value\n setQuery(val)\n runFilter(val)\n },\n onKeyDown: handleKeyDown,\n }),\n query.length > 0\n ? createElement(\n 'button',\n {\n type: 'button',\n className: 'bre-search-clear',\n title: t('modelSearchClear'),\n 'aria-label': t('modelSearchClear'),\n onClick: handleClear,\n },\n createElement(\n 'svg',\n { viewBox: '0 0 16 16', fill: 'none', stroke: 'currentColor', strokeWidth: '1.5' },\n createElement('path', { d: 'M4 4L12 12M12 4L4 12', strokeLinecap: 'round' }),\n ),\n )\n : null,\n ),\n hasEmptyState\n ? createElement(\n 'div',\n { className: 'bre-search-empty', role: 'status' },\n t('modelSearchNoMatches'),\n )\n : null,\n )\n}\n", "/**\n * Foreign React roots mounted into the official shell's DOM.\n *\n * This plugin never becomes part of the host's React tree: it renders its own\n * roots into elements it inserts itself. These helpers are the whole of that\n * mechanism, plus the render-failure boundary that keeps a throwing subtree\n * from leaving an empty root behind.\n *\n * @module dsh-model-think-level/client/injection/mount\n */\n\nimport { Component, createElement } from 'react'\nimport type { ErrorInfo, ReactNode } from 'react'\nimport { flushSync } from 'react-dom'\nimport { createRoot, type Root } from 'react-dom/client'\n\n/**\n * The root this plugin scans. The official models page can live anywhere in\n * the settings surface (a hash-classed panel, a dialog, a portal), so guessing\n * a container is fragile \u2014 and the injector is idempotent and cheap, so\n * scanning the whole document is both safe and correct.\n */\nexport function panelRoot(): HTMLElement {\n return document.body\n}\n\n/** One foreign React root this plugin created, with the element holding it. */\nexport interface ForeignMount {\n wrapper: HTMLElement\n root: Root\n}\n\n/** Options of {@link mountReact}. */\nexport interface MountOptions {\n /**\n * Commit the subtree before returning (React's `flushSync`).\n *\n * REQUIRED for roots whose height the official model card measures: a root\n * that commits one frame later makes the card grow across frames, so its\n * anchored position is visible at the wrong height for at least one paint.\n */\n sync?: boolean\n}\n\n/** Create a React root inside `container` and render `children` into it. */\nexport function mountReact(container: HTMLElement, children: ReactNode, options?: MountOptions): ForeignMount {\n const root = createRoot(container)\n if (options?.sync === true) flushSync(() => { root.render(children) })\n else root.render(children)\n return { wrapper: container, root }\n}\n\n/**\n * Unmount a foreign root and detach its wrapper.\n *\n * `root.unmount()` runs the subtree's passive cleanups synchronously, so a\n * component that restored DOM on cleanup (the search filter) has done so by\n * the time the wrapper leaves the document.\n */\nexport function unmountReact(mount: ForeignMount | undefined): void {\n if (mount === undefined) return\n mount.root.unmount()\n mount.wrapper.remove()\n}\n\n/** Render-failure boundary: surfaces the cause instead of an empty root. */\nexport class EffortBoundary extends Component<{ children?: ReactNode; fallbackText: string }, { error: string | null }> {\n state: { error: string | null } = { error: null }\n static getDerivedStateFromError(error: unknown): { error: string } {\n return { error: error instanceof Error ? error.message : String(error) }\n }\n override componentDidCatch(error: unknown, info: ErrorInfo): void {\n console.error('[bre] editor render failed:', error, info.componentStack)\n }\n override render(): ReactNode {\n if (this.state.error !== null) {\n return createElement(\n 'div',\n { style: { color: '#c00', fontSize: '11px', whiteSpace: 'pre-wrap', padding: '6px' } },\n `${this.props.fallbackText}: ${this.state.error}`,\n )\n }\n return this.props.children\n }\n}\n", "/**\n * Shape guards and settings-dict readers shared by the host and browser\n * halves. Pure data logic only: esbuild inlines this module into both\n * bundles, so it must never import a harness package or touch DOM/node APIs.\n *\n * @module dsh-model-think-level/shared\n */\n\nimport type { RouteFacts } from './knowledge.js'\n\n/** Whether a value is a plain-ish object (not null, not an array). */\nexport const isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value)\n\n/**\n * Whether a settings refusal reads like \"this kernel does not know the newer\n * compat keys\". The host autofill and the browser write path each downgrade\n * once on this signal, so they must answer identically.\n */\nexport function looksLikeCompatRefusal(message: string): boolean {\n const msg = message.toLowerCase()\n return msg.includes('compat')\n || msg.includes('thinkingtokenbudget')\n || msg.includes('vllmpriority')\n || msg.includes('supportsmaxoutput')\n}\n\n/** The models array of one route in a providers dict, as records. */\nexport function modelsOf(providers: unknown, route: string): Record<string, unknown>[] {\n if (!isRecord(providers)) return []\n const profile = providers[route]\n if (!isRecord(profile) || !Array.isArray(profile['models'])) return []\n return profile['models'].filter(isRecord)\n}\n\n/** One provider profile's route facts, as suggestion inference reads them. */\nexport function routeFactsOf(providers: unknown, route: string): RouteFacts {\n if (!isRecord(providers)) return {}\n const profile = providers[route]\n if (!isRecord(profile)) return {}\n const api = typeof profile['api'] === 'string' ? profile['api'] : undefined\n const baseURL = typeof profile['baseURL'] === 'string' ? profile['baseURL'] : undefined\n const displayName = typeof profile['displayName'] === 'string' ? profile['displayName'] : undefined\n return { api, baseURL, displayName }\n}\n", "/**\n * Endpoint reasoning/modality-signal detection over a provider's RAW /models\n * listing.\n *\n * The sanctioned `llm.discoverModels` wire call strips capability signals\n * host-side, so the host half proxies the raw listing through a same-origin\n * route; this module is the pure half both ends share. Detection approach\n * follows the format survey proven by dsh-reasoning-efforts (MIT): listings\n * disclose capabilities under several field conventions, and \"no signal\"\n * must stay distinct from \"explicitly unsupported\" -- for modalities just as\n * for reasoning.\n *\n * @module dsh-model-think-level/detection\n */\n\n/** One raw /models listing entry: pass through unknown fields untouched. */\nexport type RawModelEntry = Record<string, unknown>\n\n/** Tri-state endpoint signal: true reasons, false explicitly does not, 'unknown' = the listing never said. */\nexport type EndpointReasoning = boolean | 'unknown'\n\n/**\n * What the endpoint disclosed about one model. Every part is independent and\n * may be absent: an absent part means the listing never spoke about it (or\n * the probe never ran) -- silence is not refusal.\n */\nexport interface EndpointSignal {\n reasoning: EndpointReasoning\n /** The listing field that produced the reasoning signal, null when none did. */\n source: string | null\n /**\n * The listing's input-modality disclosure, lowercased, when it named a\n * field we recognize. Members outside the harness's image/text world\n * (audio, video, pdf/file) are passed through verbatim -- the knowledge\n * layer owns the intersection with the core vocabulary.\n */\n input?: readonly string[]\n /** Context length the listing disclosed, when it carries one. */\n contextLength?: number\n}\n\nimport { isRecord } from './shared.js'\n\nconst hasBoolean = (entry: Record<string, unknown>, key: string): boolean =>\n typeof entry[key] === 'boolean'\n\n/** Find the raw listing entry for one model id. */\nexport function findListingEntry(\n listing: unknown,\n modelId: string,\n): RawModelEntry | undefined {\n if (!Array.isArray(listing)) return undefined\n for (const entry of listing) {\n if (isRecord(entry) && entry['id'] === modelId) return entry\n }\n return undefined\n}\n\n/**\n * Read one listing entry's REASONING signal. Field conventions covered:\n * - `supported_features: [..., \"reasoning\"]` (rich feature arrays)\n * - `supported_parameters: [..., \"reasoning\" | \"include_reasoning\" |\n * \"reasoning_effort\"]` (OpenRouter-style)\n * - `supports_reasoning` / `supportsReasoning` (boolean flags)\n * - `can_reason` / `reasoning` (boolean flags)\n * - top-level `reasoning_effort` / `supports_reasoning_effort`\n *\n * A bare `{id, object, owned_by}` listing yields 'unknown' -- that is NOT\n * \"unsupported\": the endpoint simply never said.\n */\nexport function analyzeListingEntry(entry: unknown): EndpointSignal {\n if (!isRecord(entry)) return { reasoning: 'unknown', source: null }\n const features = entry['supported_features']\n if (Array.isArray(features) && features.includes('reasoning')) {\n return { ...signalParts(entry), reasoning: true, source: 'supported_features' }\n }\n const parameters = entry['supported_parameters']\n if (\n Array.isArray(parameters) &&\n (parameters.includes('reasoning') ||\n parameters.includes('include_reasoning') ||\n parameters.includes('reasoning_effort'))\n ) {\n return { ...signalParts(entry), reasoning: true, source: 'supported_parameters' }\n }\n if (hasBoolean(entry, 'supports_reasoning')) {\n return { ...signalParts(entry), reasoning: entry['supports_reasoning'] as boolean, source: 'supports_reasoning' }\n }\n if (hasBoolean(entry, 'supportsReasoning')) {\n return { ...signalParts(entry), reasoning: entry['supportsReasoning'] as boolean, source: 'supportsReasoning' }\n }\n if (hasBoolean(entry, 'can_reason')) {\n return { ...signalParts(entry), reasoning: entry['can_reason'] as boolean, source: 'can_reason' }\n }\n if (hasBoolean(entry, 'reasoning')) {\n return { ...signalParts(entry), reasoning: entry['reasoning'] as boolean, source: 'reasoning' }\n }\n\n // A present, meaningful effort field signals support; a null placeholder \u2014\n // or an empty string, which some gateways emit as a filler \u2014 says nothing\n // and must stay 'unknown' (tri-state discipline).\n const effortField = entry['reasoning_effort']\n if (\n (effortField !== undefined && effortField !== null && !(typeof effortField === 'string' && effortField.trim().length === 0)) ||\n entry['supports_reasoning_effort'] === true\n ) {\n return { ...signalParts(entry), reasoning: true, source: 'reasoning_effort' }\n }\n // The nested capabilities object is the Anthropic Messages listing shape \u2014\n // the shape the kernel's own discovery reports (this probe\n // has read it since the 0.3.5 line): thinking.supported and effort.supported\n // are the two disclosures that say a model reasons. An explicit false is a\n // refusal, not silence.\n const capabilities = isRecord(entry['capabilities']) ? entry['capabilities'] : undefined\n if (capabilities !== undefined) {\n const thinking = capabilities['thinking']\n if (isRecord(thinking) && typeof thinking['supported'] === 'boolean') {\n return {\n ...signalParts(entry),\n reasoning: thinking['supported'] as boolean,\n source: 'capabilities.thinking.supported',\n }\n }\n const effort = capabilities['effort']\n if (isRecord(effort) && typeof effort['supported'] === 'boolean') {\n return {\n ...signalParts(entry),\n reasoning: effort['supported'] as boolean,\n source: 'capabilities.effort.supported',\n }\n }\n }\n return { ...signalParts(entry), reasoning: 'unknown', source: null }\n}\n\n/**\n * Read one listing entry's MODALITY + CAPACITY disclosures. Field conventions\n * covered:\n * - `architecture.input_modalities: [\"text\",\"image\",...]` (OpenRouter and\n * every aggregator that mirrors it)\n * - top-level `input_modalities: [...]` (flattened copies)\n * - `modalities: { input: [...] }` (models.dev style)\n * - `supported_features: [..., \"vision\"]` /\n * `capabilities: [..., \"vision\"]` (feature arrays)\n * - `supports_vision` / `supportsVision` /\n * `supports_images` / `supportsImages` (boolean flags)\n * - `context_length` / `top_provider.context_length` (OpenRouter capacity)\n *\n * Absence stays absent: a listing that names no recognized field yields no\n * `input` part at all -- never an implicit \"text only\".\n */\nfunction signalParts(entry: Record<string, unknown>): Pick<EndpointSignal, 'input' | 'contextLength'> {\n const parts: { input?: readonly string[]; contextLength?: number } = {}\n\n const rawList = (value: unknown): readonly string[] | undefined => {\n if (!Array.isArray(value)) return undefined\n const members = value.filter((item): item is string => typeof item === 'string')\n return members.length > 0 ? members.map(member => member.toLowerCase()) : []\n }\n\n const architecture = isRecord(entry['architecture']) ? entry['architecture'] : undefined\n let input = rawList(architecture?.['input_modalities'])\n if (input === undefined) input = rawList(entry['input_modalities'])\n if (input === undefined) {\n const modalities = isRecord(entry['modalities']) ? entry['modalities'] : undefined\n input = rawList(modalities?.['input'])\n }\n if (input === undefined) {\n const capabilityArray = entry['capabilities']\n const visionIn = (list: unknown): boolean =>\n Array.isArray(list) && list.some(item => typeof item === 'string' && item.toLowerCase() === 'vision')\n if (visionIn(entry['supported_features']) || visionIn(capabilityArray)) {\n input = ['image']\n } else if (isRecord(capabilityArray)) {\n // The Anthropic Messages listing nests modality booleans as\n // capabilities.<modality>.supported \u2014 the shape the kernel discovery\n // added to the interrogations. An explicit false answers\n // with the text floor (refusal, not silence).\n const members: string[] = []\n for (const key of ['image_input', 'video_input', 'pdf_input', 'file_input']) {\n const slot = isRecord(capabilityArray[key]) ? capabilityArray[key] : undefined\n if (slot === undefined || typeof slot['supported'] !== 'boolean') continue\n const member = key.replace(/_input$/, '')\n if (slot['supported'] === true) members.push(member)\n else if (members.length === 0) members.push('text')\n }\n if (members.length > 0) input = members\n }\n }\n if (input === undefined) {\n if (hasBoolean(entry, 'supports_vision')) input = entry['supports_vision'] === true ? ['image'] : []\n else if (hasBoolean(entry, 'supportsVision')) input = entry['supportsVision'] === true ? ['image'] : []\n // `supports_images` is the flag CodeBuddy-derived gateways publish (their\n // model listings carry `supports_images` and none of the spellings above),\n // so without it such an endpoint reads as silent and the knowledge layer\n // wins by default -- which under-declares modalities whenever the catalog\n // entry it matched happens to be text-only. The two spellings are read as\n // ONE field, never as two answers: they are the same disclosure, and a\n // gateway carrying the pair would otherwise let whichever arm this chain\n // reaches first veto the other -- the snake_case arm always does. Only\n // `true` declares image input; a pair refusing in both spellings still\n // answers with the text floor below, exactly like the siblings above.\n else if (hasBoolean(entry, 'supports_images') || hasBoolean(entry, 'supportsImages')) {\n input = entry['supports_images'] === true || entry['supportsImages'] === true ? ['image'] : []\n }\n }\n // An empty disclosure still ANSWERED: an explicit refusal (supports_vision:\n // false, supports_images: false, or a gateway declaring no members at all)\n // maps to the text floor every supported protocol carries -- so the fusion\n // layer can strip an image claim instead of mistaking refusal for silence.\n if (input !== undefined) parts.input = input.length === 0 ? ['text'] : input\n\n // Capacity conventions: top-level, OpenRouter's top_provider, the Anthropic\n // native max_input_tokens (a 0 placeholder means 'not disclosed' \u2014 never a\n // real zero), and the models-dev limit.context.\n const length = entry['context_length']\n if (typeof length === 'number' && Number.isFinite(length) && length > 0) {\n parts.contextLength = length\n } else {\n const topProvider = isRecord(entry['top_provider']) ? entry['top_provider'] : undefined\n const nested = topProvider?.['context_length']\n if (typeof nested === 'number' && Number.isFinite(nested) && nested > 0) {\n parts.contextLength = nested\n } else {\n const maxInput = entry['max_input_tokens']\n if (typeof maxInput === 'number' && Number.isFinite(maxInput) && maxInput > 0) {\n parts.contextLength = maxInput\n } else {\n const limit = isRecord(entry['limit']) ? entry['limit'] : undefined\n const limitContext = limit?.['context']\n if (typeof limitContext === 'number' && Number.isFinite(limitContext) && limitContext > 0) {\n parts.contextLength = limitContext\n }\n }\n }\n }\n\n return parts\n}\n\n/**\n * Detect one model's endpoint signal from a raw listing.\n * @returns the signal, plus whether the model appeared in the listing at all.\n */\nexport function detectModelSignal(\n listing: unknown,\n modelId: string,\n): { found: boolean; signal: EndpointSignal } {\n const entry = findListingEntry(listing, modelId)\n if (entry === undefined) return { found: false, signal: { reasoning: 'unknown', source: null } }\n return { found: true, signal: analyzeListingEntry(entry) }\n}\n", "/**\n * Client-side declaration write seam over 'settings.mutate', plus the\n * knowledge-base / protocol suggestions. Pure logic --\n * no React, no DOM -- so it stays unit-testable in isolation.\n *\n * @module dsh-model-think-level/client/ops\n */\n\nimport {\n suggestEfforts,\n type CompatSuggestion,\n type InputModalities,\n type ReasoningEfforts,\n} from '../knowledge.js'\nimport { AUTOFILL_MARKER, DEFAULT_EFFORT_FIELD, INPUT_UNSET_MARKER, PI_AI_NS, PROBE_PATH, UNSET_MARKER } from '../constants.js'\nimport { detectModelSignal, type EndpointSignal } from '../detection.js'\nimport { isRecord, looksLikeCompatRefusal, routeFactsOf } from '../shared.js'\nimport type { SettingsPathOpView } from '@deepseek-ai/dsh-api-remotes/client'\nimport type {\n DefaultEffortIntent,\n EffortEditorApi,\n EffortWriteIntent,\n HeldWrite,\n InputIntent,\n RemoteApi,\n SettingsJoin,\n SettingsNamespaceView,\n} from './types.js'\n\n/** The user-layer providers dict of the pi-ai namespace, as records. */\nexport function providersOf(namespace: SettingsNamespaceView | undefined): Record<string, Record<string, unknown>> {\n const value = namespace?.value as { providers?: unknown } | undefined\n const providers = value?.providers\n if (typeof providers !== 'object' || providers === null || Array.isArray(providers)) return {}\n return Object.fromEntries(\n Object.entries(providers as Record<string, unknown>).filter(([, profile]) =>\n typeof profile === 'object' && profile !== null && !Array.isArray(profile)),\n ) as Record<string, Record<string, unknown>>\n}\n\n/** The providers dict of one settings layer (user or base), as a record. */\nfunction providersOfLayer(layer: unknown): Record<string, unknown> | undefined {\n if (!isRecord(layer)) return undefined\n const providers = layer['providers']\n return isRecord(providers) ? providers : undefined\n}\n\n/**\n * The `models` value a write must be based on, read from the RAW layers: the\n * user section when it declares one, else the composition base. That is the\n * official card's own inheritance rule (`ProviderEditor` seeds its draft from\n * `namespace.user` and its fallback from `namespace.base`).\n *\n * Never the resolved `value`: rebuilding from it materializes every schema\n * default -- `input: []`, an empty `compat`, an absent `models` -- into the\n * stored document, so the user's file grows keys they never chose and a later\n * schema change can no longer reach the row.\n * @param namespace - the namespace view carrying the layers.\n * @param route - the provider route key.\n * @returns the raw `models` value, or undefined when neither layer declares one.\n */\nexport function baselineModelsOf(namespace: SettingsNamespaceView | undefined, route: string): unknown {\n for (const layer of [namespace?.user, namespace?.base]) {\n const profile = providersOfLayer(layer)?.[route]\n if (isRecord(profile) && profile['models'] !== undefined) return profile['models']\n }\n return undefined\n}\n\n/**\n * The RAW USER layer's providers dict, or undefined when the section declares\n * none. The browser half's running auto-fill builds its patch from this --\n * never from the resolved `value`, which would materialize schema defaults\n * into the stored document (the same rule the host's boot pass follows).\n * @param namespace - the namespace view.\n * @returns the user section's providers dict, when it has one.\n */\nexport function userProvidersOf(namespace: SettingsNamespaceView | undefined): Record<string, unknown> | undefined {\n return providersOfLayer(namespace?.user)\n}\n\n/** The reasoningEfforts of one model in a route's models. */\nexport function effortsOf(models: Record<string, unknown>[], modelId: string): false | ReasoningEfforts | undefined {\n const entry = models.find(model => model['id'] === modelId)\n if (entry === undefined) return undefined\n const efforts = entry['reasoningEfforts']\n if (efforts === false) return false\n if (typeof efforts === 'object' && efforts !== null && !Array.isArray(efforts)) {\n return efforts as ReasoningEfforts\n }\n return undefined\n}\n\n/** The input-modality declaration of one model in a route's models. Empty\n * mirrors undefined -- the resolved settings layer materializes absent arrays\n * as [] (schemastery), and llm-pi-ai's own declaredInput reads that as \"no\n * answer here\": an empty list must stay inheritable, never a phantom\n * text-only declaration. */\nexport function inputOf(models: Record<string, unknown>[], modelId: string): InputModalities | undefined {\n const entry = models.find(model => model['id'] === modelId)\n const input = entry?.['input']\n if (!Array.isArray(input)) return undefined\n const members = input.filter((member): member is string => typeof member === 'string')\n // Members originate from the core schema's vocabulary; the cast only\n // recovers that guarantee the index signature erased.\n return (members.length > 0 ? members : undefined) as InputModalities | undefined\n}\n\n/** The stored compat block of one model in a route's models (unvalidated passthrough). */\nexport function compatOf(models: Record<string, unknown>[], modelId: string): CompatSuggestion | undefined {\n const entry = models.find(model => model['id'] === modelId)\n const compat = entry?.['compat']\n if (!isRecord(compat)) return undefined\n return { ...(compat as CompatSuggestion) }\n}\n\n/** The display name of one model in a route's models. */\nexport function nameOf(models: Record<string, unknown>[], modelId: string): string | undefined {\n const entry = models.find(model => model['id'] === modelId)\n const name = entry?.['name']\n return typeof name === 'string' && name.length > 0 ? name : undefined\n}\n\n/**\n * The stored per-model default-effort pick of one model in a route's models,\n * or undefined when the user has not picked one. A non-string value degrades\n * to undefined: the field is plugin-owned, and a hostile or stale document\n * must degrade to \"no default\", never poison the memory chain.\n */\nexport function defaultEffortOf(models: Record<string, unknown>[], modelId: string): string | undefined {\n const entry = models.find(model => model['id'] === modelId)\n const effort = entry?.[DEFAULT_EFFORT_FIELD]\n return typeof effort === 'string' && effort.length > 0 ? effort : undefined\n}\n\n/**\n * Ask the host's same-origin probe route for this model's raw-listing facts\n * (reasoning signal, modality disclosure, context length). Any failure --\n * route absent, endpoint unreachable, listing shape unexpected -- degrades to\n * an unanswered signal (\"asked, no answer\"), never to a thrown error:\n * suggestions must not break because the endpoint would not talk.\n */\nasync function probeEndpoint(route: string, modelId: string): Promise<EndpointSignal> {\n try {\n const response = await fetch(`${PROBE_PATH}?route=${encodeURIComponent(route)}`, { method: 'GET' })\n if (!response.ok) return { reasoning: 'unknown', source: null }\n const body = (await response.json()) as { ok?: boolean; data?: unknown }\n if (!body?.ok) return { reasoning: 'unknown', source: null }\n return detectModelSignal(body.data, modelId).signal\n } catch {\n return { reasoning: 'unknown', source: null }\n }\n}\n\n/**\n * Build the write seam over a settings Remote face.\n * @param api - the settings Remote methods.\n * @param describe - how to obtain the pi-ai namespace join (injectable for tests).\n * @param stage - sink for declarations staged against a route that is not\n * saved yet; the injector owns the store and flushes it once the route exists.\n */\nexport function createEditorApi(\n api: RemoteApi,\n describe: () => Promise<SettingsJoin> = () => describeNamespace(api),\n stage?: (\n route: string,\n modelId: string,\n efforts: EffortWriteIntent,\n compat?: CompatSuggestion,\n input?: InputModalities | null,\n defaultEffort?: string | null,\n ) => void,\n /**\n * Present only on a seam an ON-SCREEN editor drives: while the official card\n * is open its own frozen revision baseline makes any write here break the\n * user's next save in that card, so the seam queues the full intent instead\n * of committing it. The injector replays it once the card is gone.\n */\n hold?: (\n route: string,\n modelId: string,\n write: HeldWrite,\n ) => void,\n /**\n * Present only on the editor's own seam: forget everything this row reported.\n * The editor's Reset calls it, because the ledger it wrote to outlives the\n * React state \u2014 without this a Reset followed by the official Save would\n * still write the discarded edits.\n */\n withdraw?: (route: string, modelId: string) => void,\n /**\n * Whether this row's route is still unsaved (the create card's draft route,\n * or a typed-but-unsaved row). The injector knows this per row, so it is told\n * rather than re-derived here: deriving it would cost a wire describe on\n * every keystroke the editor reports.\n */\n stagedOf?: () => boolean,\n): EffortEditorApi {\n return {\n async suggest(route, modelId, name, stagedFacts) {\n const providers = providersOf((await describe()).namespace)\n const stored = routeFactsOf(providers, route)\n // A saved route's stored profile is authoritative; the staged facts only\n // fill what the settings document does not hold yet (the create card).\n const facts = {\n api: stored.api ?? stagedFacts?.api,\n baseURL: stored.baseURL ?? stagedFacts?.baseURL,\n displayName: name ?? stored.displayName,\n }\n // L1 first: the endpoint's own word about this model. The fusion in\n // suggestEfforts keeps wire values knowledge-base-only. A route the\n // settings document does not hold (a create card) cannot be probed --\n // the host resolves routes from settings -- so skip the doomed round\n // trip and leave the signal absent until the route is saved.\n const endpoint = providers[route] === undefined ? undefined : await probeEndpoint(route, modelId)\n const suggestion = suggestEfforts(modelId, facts, endpoint)\n if (suggestion.efforts === undefined) return { ok: false, error: 'no-suggestion' }\n return {\n ok: true,\n suggestion: {\n efforts: suggestion.efforts,\n // The host autofill writes the suggestion's compat block beside the\n // declaration; the browser seam must write the SAME bytes so one\n // suggestion never produces two different documents. (thinkingFormat\n // is what makes off/thinking dispatch work on deepseek/qwen/zai\n // endpoints.)\n ...(suggestion.compat === undefined ? {} : { compat: suggestion.compat }),\n ...(suggestion.defaultEffort === undefined ? {} : { defaultEffort: suggestion.defaultEffort }),\n ...(suggestion.defaultEffort === undefined ? {} : { defaultEffort: suggestion.defaultEffort }),\n ...(suggestion.input === undefined ? {} : { input: suggestion.input }),\n ...(suggestion.inputSource === undefined ? {} : { inputSource: suggestion.inputSource }),\n ...(suggestion.contextWindow === undefined ? {} : { contextWindow: suggestion.contextWindow }),\n ...(suggestion.maxTokens === undefined ? {} : { maxTokens: suggestion.maxTokens }),\n matched: suggestion.matched,\n source: suggestion.source,\n confidence: suggestion.confidence,\n ...(suggestion.endpoint === undefined ? {} : { endpoint: suggestion.endpoint }),\n },\n }\n },\n commit(route, modelId, write) {\n // The editor reports the row's whole pending edit, and whether the route\n // exists in the document yet decides the ledger: an unsaved route (the\n // create card, or a typed-but-unsaved row) stages, a saved one queues\n // until the official card's Save. Both are synchronous and memory-only:\n // the change must register before the user can reach that Save button.\n if (stagedOf?.() === true) {\n stage?.(route, modelId, write.efforts, write.compat, write.input, write.defaultEffort)\n return\n }\n hold?.(route, modelId, {\n efforts: write.efforts,\n ...(write.compat === undefined ? {} : { compat: write.compat }),\n ...(write.input === undefined ? {} : { input: write.input }),\n ...(write.clearCompatKeys === undefined ? {} : { clearCompatKeys: write.clearCompatKeys }),\n ...(write.defaultEffort === undefined ? {} : { defaultEffort: write.defaultEffort }),\n })\n },\n withdraw(route, modelId) {\n withdraw?.(route, modelId)\n },\n stageEfforts(route, modelId, efforts, compat, input, defaultEffort) {\n stage?.(route, modelId, efforts, compat, input, defaultEffort)\n },\n async writeEfforts(route, modelId, rawEfforts, compat, input, clearCompatKeys, defaultEffort) {\n // While an editor owns the document (the official editing card is open)\n // commit nothing: that card froze its own revision baseline, so any\n // write from here makes the user's NEXT save in it fail with\n // `settings/conflict` -- which reads exactly like \"I configured it,\n // saved it, and it is gone\". Queue the full intent verbatim instead; the\n // injector replays it once the card is gone. A replay seam passes no\n // holder, so the same call commits then.\n if (hold !== undefined) {\n hold(route, modelId, {\n efforts: rawEfforts,\n ...(compat === undefined ? {} : { compat }),\n ...(input === undefined ? {} : { input }),\n ...(clearCompatKeys === undefined ? {} : { clearCompatKeys }),\n ...(defaultEffort === undefined ? {} : { defaultEffort }),\n })\n return { ok: true, staged: true }\n }\n // 'keep' means the ladder part of the edit is a no-op: a modality-only\n // edit must never fall through to the unset branch (which would stamp\n // the durable marker onto a never-declared ladder and silence host\n // auto-fill for it forever).\n const results = await writeModelRows(api, route, [{\n modelId,\n efforts: rawEfforts,\n ...(compat === undefined ? {} : { compat }),\n ...(input === undefined ? {} : { input }),\n ...(clearCompatKeys === undefined ? {} : { clearCompatKeys }),\n ...(defaultEffort === undefined ? {} : { defaultEffort }),\n }], describe)\n const result = results[0] ?? { ok: false, error: 'no-result' }\n if (result.ok) return { ok: true }\n // A vanished row is reported by code so the callers can drop it; the\n // wire itself says 'model-not-found', which is what this error means.\n if (result.modelNotFound === true) return { ok: false, error: 'model-not-found' }\n return { ok: false, error: result.error ?? 'conflict' }\n },\n }\n}\n\n/**\n * One row's intent inside a batch write. `touch` distinguishes \"this edit never\n * carried the ladder\" (leave it alone) from \"unset it durably\"; a modality\n * `undefined` likewise leaves the stored declaration untouched.\n */\nexport interface RowIntent {\n modelId: string\n /** The ladder part; 'keep' means this edit never carried it. */\n efforts: EffortWriteIntent\n compat?: CompatSuggestion\n input?: InputIntent\n clearCompatKeys?: readonly string[]\n defaultEffort?: DefaultEffortIntent\n}\n\n/** The outcome of one batch write, as the caller's ledgers need it. */\nexport interface RowWriteResult {\n ok: boolean\n /**\n * True when the failure is \"this row is not (or no longer) in the document\":\n * the caller drops the intent instead of retrying it forever.\n */\n modelNotFound?: boolean\n /**\n * True when the write was abandoned before reaching the wire because an\n * official card opened during the read. Not a failure: the caller keeps the\n * intent and does not back off.\n */\n aborted?: boolean\n /** The refusal message, when `ok` is false and it is a real failure. */\n error?: string\n}\n\n/**\n * Apply every row of ONE route in a single rebuild + single mutate.\n *\n * This is the write path's core: `writeEfforts` is the one-row case of it, and\n * the injector's ledgers call it with every row they hold for that route. One\n * describe and one `mutate` per route rather than per row -- the official\n * document is a whole-array set, so batching costs no extra semantics.\n *\n * Failing rows are the caller's business: this returns one result per row, all\n * derived from the same response, so a route that refuses keeps every one of\n * its intents for the next pass (never a partial landing).\n * @param api - the settings Remote.\n * @param route - the provider route whose models array to rebuild.\n * @param rows - the intents to apply, in order.\n * @param describe - the read seam; a conflict retry forces the wire read.\n * @param abort - checked after the read and before the mutate: an official\n * card may have opened while we were reading, and writing then would land\n * behind that card's frozen revision baseline (issue #7). An aborted write\n * returns `{ ok: false, aborted: true }` for every row and consumes nothing.\n * @returns one result per row, in the same order.\n */\nexport async function writeModelRows(\n api: RemoteApi,\n route: string,\n rows: readonly RowIntent[],\n describe: () => Promise<SettingsJoin> = () => describeNamespace(api),\n abort?: () => boolean,\n): Promise<RowWriteResult[]> {\n if (rows.length === 0) return []\n // Retry once on a revision conflict: a concurrent writer (this plugin's own\n // autofill, or the official page) moved the namespace between our describe\n // and mutate. Re-reading and retrying with the fresh revision is the same\n // recovery the official settings form uses.\n // Downgrade once for older kernels: a `settings/rejected` refusal naming an\n // unknown compat key means the kernel predates the newer compat schema.\n // Stripping the newer-only keys and retrying keeps one artifact writable\n // across kernel lines with no version sniffing.\n let downgraded = false\n for (let attempt = 0; attempt < 3; attempt++) {\n try {\n // Attempt 0 reads through the injected seam (the injector seeds it with\n // the scan's join). A conflict retry MUST go to the wire: the scope\n // snapshot behind that seam folds a fresh view in asynchronously, so it\n // would hand back the very revision the write just failed against.\n const join = attempt === 0 ? await describe() : await describeNamespace(api, { fresh: true })\n if (join.namespace === undefined) return rows.map(() => ({ ok: false, error: 'no-namespace' }))\n // The write rebuilds the models array verbatim, from the RAW layers (the\n // user section, else the composition base) -- never the resolved value.\n // A row this code cannot represent must refuse the write rather than\n // silently drop the row.\n const rawModels = baselineModelsOf(join.namespace, route)\n if (!Array.isArray(rawModels)) return rows.map(() => ({ ok: false, modelNotFound: true }))\n if (!rawModels.every(isRecord)) return rows.map(() => ({ ok: false, error: 'invalid-models' }))\n const models = rawModels as Record<string, unknown>[]\n // Every row this write intends must still exist: a vanished row is\n // dropped by the caller, and mixing it into a rebuild would rewrite the\n // array around a gap.\n const nextModels = models.map(model => model)\n const missing: string[] = []\n for (const row of rows) {\n const index = nextModels.findIndex(model => model['id'] === row.modelId)\n if (index < 0) {\n missing.push(row.modelId)\n continue\n }\n nextModels[index] = mergeRowIntent(nextModels[index]!, row, downgraded ? undefined : row.compat)\n }\n if (missing.length > 0) {\n return rows.map(row => (missing.includes(row.modelId)\n ? { ok: false, modelNotFound: true }\n : { ok: false, error: 'model-not-found' }))\n }\n // The card fence, re-checked at the last possible moment: a card that\n // opened during the read must not have this write land behind its frozen\n // baseline. Aborting consumes nothing -- every row stays for a retry.\n if (abort?.() === true) return rows.map(() => ({ ok: false, aborted: true }))\n const response = await api.settings.mutate(\n PI_AI_NS,\n // The rebuilt models array is JSON-shaped by construction (a settings\n // document is JSON); the value is JsonValue on this baseline, so the\n // set op asserts once instead of rebuilding the row's type.\n [{ op: 'set', path: ['providers', route, 'models'], value: nextModels } as unknown as SettingsPathOpView],\n join.namespace.revision,\n )\n if (!response.ok) {\n // The stable wire code, not the message prose: 'settings/conflict'\n // (the Typert refusal code) means a concurrent writer moved the\n // namespace between our describe and mutate.\n if (attempt === 0 && response.error.code === 'settings/conflict') continue\n const looksCompat = response.error.code === 'settings/rejected' && looksLikeCompatRefusal(response.error.message)\n if (!downgraded && looksCompat && rows.some(row => row.compat !== undefined)) {\n downgraded = true\n continue\n }\n return rows.map(() => ({ ok: false, error: response.error.message }))\n }\n return rows.map(() => ({ ok: true }))\n } catch (error) {\n return rows.map(() => ({ ok: false, error: error instanceof Error ? error.message : String(error) }))\n }\n }\n return rows.map(() => ({ ok: false, error: 'conflict' }))\n}\n\n/**\n * Rewrite ONE model row in place: the ladder, its compat block, the modality\n * declaration, and the default-effort pick.\n *\n * Split out of the write loop so a batch shares exactly this row surgery --\n * including the provenance rules below, which a batch must not be able to\n * drift away from.\n * @param model - the row as the document holds it.\n * @param row - the intent to apply.\n * @param compat - the compat block to write, or undefined to leave it.\n * @returns the rebuilt row.\n */\nfunction mergeRowIntent(\n model: Record<string, unknown>,\n row: RowIntent,\n compat: CompatSuggestion | undefined,\n): Record<string, unknown> {\n const copy = { ...model }\n // 'keep' means the ladder part of the edit is a no-op: a modality-only edit\n // must never fall through to the unset branch (which would stamp the durable\n // marker onto a never-declared ladder and silence host auto-fill forever).\n const touch = row.efforts !== 'keep'\n const efforts = row.efforts === 'keep' ? undefined : row.efforts\n if (!touch) {\n // Ladder untouched by this edit: no delete, no marker.\n } else if (efforts === undefined) {\n // Unset the declaration durably: the marker records the absence as a\n // decision, so the host's auto-fill never reads it back as a gap to fill --\n // not now, and not after the next restart.\n delete copy['reasoningEfforts']\n copy[UNSET_MARKER] = true\n } else {\n // A real declaration supersedes any earlier unset marker -- and retires the\n // autofill provenance marker: these bytes are now a user decision, so a\n // later staging must not be allowed to override them as if they were still\n // the knowledge base's suggestion (they may be byte-identical on purpose).\n delete copy[UNSET_MARKER]\n delete copy[AUTOFILL_MARKER]\n if (efforts === false) {\n copy['reasoningEfforts'] = false\n } else {\n copy['reasoningEfforts'] = { ...efforts }\n // The compat belongs to the declaration: merge it over the stored block so\n // hand-tuned keys (incl. newer-only fields the suggestion never names)\n // survive a declaration edit. Only the keys THIS edit owns and left empty\n // are deleted -- clearing the UI's own picker has to mean unset, while a\n // field the editor never showed stays untouched.\n const stored = isRecord(model['compat']) ? (model['compat'] as Record<string, unknown>) : {}\n const merged: Record<string, unknown> = { ...stored, ...(compat ?? {}) }\n for (const key of row.clearCompatKeys ?? []) delete merged[key]\n if (Object.keys(stored).length > 0 || Object.keys(merged).length > 0) {\n if (Object.keys(merged).length === 0) delete copy['compat']\n else copy['compat'] = merged\n }\n }\n }\n // The modality part rides the same mutate. An omitted intent touches nothing\n // (a staged flush must not strip declarations it never carried); null unsets\n // durably through the marker.\n if (row.input !== undefined) {\n if (row.input === null) {\n delete copy['input']\n copy[INPUT_UNSET_MARKER] = true\n } else {\n delete copy[INPUT_UNSET_MARKER]\n copy['input'] = [...row.input]\n }\n }\n // The per-model default-effort pick rides the same mutate: omitted =\n // untouched (a ladder-only edit never clears the pick), null = cleared (back\n // to the memory chain), a string = the pick.\n if (row.defaultEffort !== undefined) {\n if (row.defaultEffort === null) delete copy[DEFAULT_EFFORT_FIELD]\n else copy[DEFAULT_EFFORT_FIELD] = row.defaultEffort\n }\n return copy\n}\n\n/** One result of writing a provider's request headers. */\nexport type ProviderHeadersReply = { ok: true } | { ok: false; error: string }\n\n/**\n * The RAW layer's `headers` dict for one route, as the editor seeds its draft.\n *\n * Reads through {@link baselineModelsOf}'s rule (the user section when it\n * declares one, else the composition base) for the same reason: the resolved\n * `value` materializes schema defaults, and a draft seeded from it would write\n * those defaults back on the first save. The run of the layers is \"what the\n * user sees is what the user edits\", which is the inheritance the official\n * ProviderEditor applies to its own fields.\n * @param namespace - the namespace view carrying the layers.\n * @param route - the provider route key.\n * @returns the stored header pairs, or undefined when no layer declares any.\n */\nexport function providerHeadersOf(\n namespace: SettingsNamespaceView | undefined,\n route: string,\n): Record<string, string> | undefined {\n for (const layer of [namespace?.user, namespace?.base]) {\n const profile = providersOfLayer(layer)?.[route]\n if (!isRecord(profile)) continue\n const headers = profile['headers']\n if (headers === undefined) continue\n if (!isRecord(headers)) return undefined\n const pairs: Record<string, string> = {}\n for (const [name, value] of Object.entries(headers)) {\n if (typeof value === 'string') pairs[name] = value\n }\n // An explicitly empty dict is the user's \"send no extra headers\": reported\n // as an empty dict rather than absent, so the editor shows a cleared state\n // instead of a field the next save would silently refill from below.\n return pairs\n }\n return undefined\n}\n\n/**\n * Write ONE route's request headers through the live settings seam.\n *\n * A whole-dict `set` on `providers.<route>.headers`, so a brother field of the\n * profile (`baseURL`, `apiKeyEnv`, `models`, `compat`\u2026) is never restated and\n * cannot be lost to this write. The settings layer's path write merges into the\n * existing profile, which is why the write does not have to read the profile\n * back and rebuild it the way the models-array write must.\n *\n * The conflict discipline mirrors {@link writeModelRows}: one retry, re-read\n * fresh. A concurrent writer (the official card's own Save, another tab) moving\n * the namespace between the describe and the mutate is a race the user's edit\n * must survive, so the second attempt fences on the NEW revision instead of\n * failing.\n * @param api - the settings Remote (plus the optional scope).\n * @param route - the provider route key.\n * @param headers - the complete header dict to store (`{}` = send none).\n * @returns whether the write landed, and the refusal text when it did not.\n */\nexport async function writeProviderHeaders(\n api: RemoteApi,\n route: string,\n headers: Record<string, string>,\n): Promise<ProviderHeadersReply> {\n if (typeof route !== 'string' || route.length === 0) return { ok: false, error: 'no-route' }\n for (let attempt = 0; attempt < 2; attempt += 1) {\n try {\n // A conflict retry must NOT read the mirror: it folds a fresh view in\n // asynchronously and would hand back the very revision the write failed\n // against (the same trap writeModelRows documents).\n const join = attempt === 0 ? await describeNamespace(api) : await describeNamespace(api, { fresh: true })\n if (join.namespace === undefined) return { ok: false, error: 'no-namespace' }\n if (join.writable !== true) return { ok: false, error: 'read-only' }\n // The route must exist in the RAW layers: a `set` with an intermediate\n // path that resolves nowhere would create a stray profile carrying only\n // headers -- a route with no endpoint and no protocol, which the adapter\n // would then refuse to build. The slot this editor renders in only\n // dispatches for saved rows, so an absent route is a genuine race with a\n // deletion rather than a normal state.\n if (!routeIsDeclared(join.namespace, route)) return { ok: false, error: 'no-route' }\n const response = await api.settings.mutate(\n PI_AI_NS,\n [{ op: 'set', path: ['providers', route, 'headers'], value: { ...headers } } as unknown as SettingsPathOpView],\n join.namespace.revision,\n )\n if (!response.ok) {\n if (attempt === 0 && response.error.code === 'settings/conflict') continue\n return { ok: false, error: response.error.message }\n }\n return { ok: true }\n } catch (error) {\n return { ok: false, error: error instanceof Error ? error.message : String(error) }\n }\n }\n return { ok: false, error: 'conflict' }\n}\n\n/** Whether either raw layer declares this route (own key, never a prototype name). */\nfunction routeIsDeclared(namespace: SettingsNamespaceView | undefined, route: string): boolean {\n for (const layer of [namespace?.user, namespace?.base]) {\n const providers = providersOfLayer(layer)\n if (providers !== undefined && Object.prototype.hasOwnProperty.call(providers, route)) return true\n }\n return false\n}\n\n/**\n * Describe the pi-ai namespace plus writability.\n *\n * Prefers the official settings scope's snapshot when the shell provides one:\n * the shared describe mirror already carries this namespace's resolved section\n * together with its RAW layers (user / base) and the revision the settings\n * surface itself fences writes with, so a scan costs no wire round trip.\n * `fresh: true` skips the snapshot and reads the wire -- what a conflict retry\n * needs, because the mirror folds a fresh view in asynchronously and would\n * otherwise hand back the very revision the write just failed against.\n * @param api - the settings Remote (plus the optional scope).\n * @param options - `fresh` forces the wire read.\n */\nexport async function describeNamespace(api: RemoteApi, options: { fresh?: boolean } = {}): Promise<SettingsJoin> {\n const viaScope = options.fresh === true ? undefined : joinFromScope(api)\n if (viaScope !== undefined) return viaScope\n const response = await api.settings.describe()\n if (!response.ok) return { namespace: undefined, writable: false }\n const namespace = response.value.namespaces.find(ns => ns.ns === PI_AI_NS)\n return { namespace, writable: response.value.writable }\n}\n\n/**\n * The namespace join as the official scope's snapshot holds it, or undefined\n * when there is no scope / it has no accepted section yet.\n *\n * The snapshot carries only what a browser surface consumes, so the wire-only\n * fields (`schema`, `applies`, `secrets`) are filled with the shapes the plugin\n * never reads; every field this half touches (`value`, `user`, `base`,\n * `revision`) comes from the mirror verbatim.\n */\nfunction joinFromScope(api: RemoteApi): SettingsJoin | undefined {\n const snapshot = api.scope?.getSnapshot()\n if (snapshot === undefined || snapshot.status !== 'ready') return undefined\n if (typeof snapshot.revision !== 'number') return undefined\n return {\n namespace: {\n ns: PI_AI_NS,\n schema: {},\n value: snapshot.value,\n user: snapshot.user,\n base: snapshot.base,\n revision: snapshot.revision,\n applies: 'live',\n secrets: [],\n } as unknown as SettingsNamespaceView,\n writable: snapshot.writable === true,\n }\n}\n", "/**\n * The browser-side cache of the settings document's per-model `defaultEffort`\n * field (issue #4).\n *\n * The composer effort chain reads it through {@link ConfiguredEfforts.of}; a\n * pushed document update or a connection reset invalidates it and the next read\n * re-describes. A refused or empty read is deliberately NOT cached, so a\n * gateway hiccup cannot silently disable the configured layer until the next\n * invalidation.\n *\n * @module dsh-model-think-level/client/injection/configured-efforts\n */\n\nimport { DEFAULT_EFFORT_FIELD } from '../../constants.js'\nimport { describeNamespace, providersOf } from '../ops.js'\nimport type { RemoteApi } from '../types.js'\n\n/** What the cache reads through. */\nexport interface ConfiguredEffortsDeps {\n /** The settings remote. */\n api: RemoteApi\n}\n\n/** The cache's face. */\nexport interface ConfiguredEfforts {\n /** The configured pick for one model, after the cache has settled. */\n of: (provider: string, model: string) => Promise<string | undefined>\n /** Drop the cache; the next read re-describes. */\n invalidate: () => void\n}\n\n/**\n * Build the configured-pick cache.\n * @param deps - the settings remote.\n * @returns the cache's {@link ConfiguredEfforts} face.\n */\nexport function createConfiguredEfforts(deps: ConfiguredEffortsDeps): ConfiguredEfforts {\n let configuredEfforts: Map<string, string> | undefined\n let configuredEffortsInflight: Promise<void> | undefined\n let configuredEffortsGeneration = 0\n\n const ensureConfiguredEfforts = (): Promise<void> => {\n if (configuredEfforts !== undefined) return Promise.resolve()\n const generation = configuredEffortsGeneration\n configuredEffortsInflight ??= describeNamespace(deps.api)\n .then((join) => {\n if (join.namespace === undefined) {\n // The namespace is unregistered, or the read was refused (a\n // gateway hiccup): do NOT cache the empty answer \u2014 the next chain\n // read re-describes, instead of silently disabling the configured\n // layer until the next invalidation.\n configuredEffortsInflight = undefined\n return\n }\n const map = new Map<string, string>()\n for (const [route, profile] of Object.entries(providersOf(join.namespace))) {\n const rawModels = Array.isArray(profile['models']) ? profile['models'] : []\n for (const model of rawModels) {\n if (typeof model !== 'object' || model === null || Array.isArray(model)) continue\n const id = (model as Record<string, unknown>)['id']\n const effort = (model as Record<string, unknown>)[DEFAULT_EFFORT_FIELD]\n if (typeof id === 'string' && typeof effort === 'string' && effort.length > 0) {\n map.set(`${route}/${id}`, effort)\n }\n }\n }\n // An invalidation during the flight must not land a stale snapshot:\n // the caller re-describes on the next read instead.\n if (generation === configuredEffortsGeneration) configuredEfforts = map\n else configuredEffortsInflight = undefined\n })\n .catch(() => {\n // A failed read leaves the cache empty: the next read re-describes.\n if (generation === configuredEffortsGeneration) configuredEffortsInflight = undefined\n })\n return configuredEffortsInflight\n }\n\n const of = async (provider: string, model: string): Promise<string | undefined> => {\n await ensureConfiguredEfforts()\n return configuredEfforts?.get(`${provider}/${model}`)\n }\n\n const invalidate = (): void => {\n configuredEffortsGeneration += 1\n configuredEfforts = undefined\n configuredEffortsInflight = undefined\n }\n\n return { of, invalidate }\n}\n", "/**\n * The knowledge-base auto-fill: build the settings patch that declares\n * `reasoningEfforts` -- and, unless declined, the input-modality declaration --\n * on every undeclared model of a providers dict.\n *\n * Shared by BOTH halves on purpose. The host builds its boot fill with it, and\n * the browser half builds the very same patch from its own read, so one\n * suggestion can never produce two different documents. Pure data logic: no\n * harness package, no node or DOM API.\n *\n * @module dsh-model-think-level/autofill\n */\n\nimport { AUTOFILL_MARKER, INPUT_UNSET_MARKER, UNSET_MARKER } from './constants.js'\nimport { isSelfHostedRelay, NEW_COMPAT_KEYS, suggestEfforts } from './knowledge.js'\nimport { isRecord, routeFactsOf } from './shared.js'\n\n/** A JSON object patch. */\ntype JsonObject = Record<string, unknown>\n\n/**\n * Whether a model row carries an input-modality declaration the document\n * already answers with. An absent key and an empty array both read as \"no\n * answer here\" -- mirroring how llm-pi-ai's own resolution treats them.\n */\nfunction declaresInput(model: JsonObject): boolean {\n const input = model['input']\n return Array.isArray(input) && input.length > 0\n}\n\n/**\n * Build a patch that adds reasoningEfforts -- and, unless declined, the\n * input-modality declaration -- to every undeclared model of the given\n * routes, from the knowledge base / protocol inference. Returns the partial\n * providers patch, or undefined when nothing needs filling.\n * @param providers - the resolved providers dict.\n * @param routeFilter - optional route filter (defaults to all routes).\n * @param options - fill switches; both default on.\n * @param revision - settings revision this read is based on; recorded as the\n * provenance marker of every ladder filled here (see {@link AUTOFILL_MARKER}).\n */\nexport function buildAutofillPatch(\n providers: unknown,\n routeFilter: (route: string) => boolean = () => true,\n options: { efforts?: boolean; modalities?: boolean } = {},\n revision = 0,\n): JsonObject | undefined {\n const fillEfforts = options.efforts !== false\n const fillModalities = options.modalities !== false\n if (!isRecord(providers)) return undefined\n const patchRoutes: JsonObject = {}\n for (const route of Object.keys(providers)) {\n if (!routeFilter(route)) continue\n // Read the RAW models array: this builder rebuilds the array verbatim,\n // so a route carrying a row it cannot represent (non-record) is left\n // alone \u2014 a filtered rebuild would silently DELETE that row.\n const profile = providers[route]\n const rawModels = isRecord(profile) && Array.isArray(profile['models']) ? profile['models'] : undefined\n if (rawModels === undefined || rawModels.length === 0) continue\n if (!rawModels.every(isRecord)) continue\n const models = rawModels as Record<string, unknown>[]\n const nextModels: JsonObject[] = []\n let changed = false\n for (const model of models) {\n // Declared parts are untouched \u2014 and so are parts the user\n // deliberately unset: the durable markers record those absences as\n // decisions, so auto-fill never reads them back as gaps (the bug this\n // guards: a refill one event after every user \"unset\").\n const effortsDeclared = model['reasoningEfforts'] !== undefined || model[UNSET_MARKER] === true\n const inputDeclared = declaresInput(model) || model[INPUT_UNSET_MARKER] === true\n if ((effortsDeclared || !fillEfforts) && (inputDeclared || !fillModalities)) {\n // Versioned exception to never-touch-declared (issue #2): a declared\n // ladder whose compat lacks the role pin on a self-hosted relay gets\n // exactly that field merged in -- the declaration itself is untouched.\n const backfilled = backfillRolePin(model, providers, route)\n if (backfilled !== undefined) {\n changed = true\n nextModels.push(backfilled)\n } else {\n nextModels.push(model)\n }\n continue\n }\n const id = typeof model['id'] === 'string' ? model['id'] : ''\n if (id.length === 0) {\n nextModels.push(model)\n continue\n }\n const routeInfo = routeFactsOf(providers, route)\n const name = typeof model['name'] === 'string' ? model['name'] : undefined\n const suggestion = suggestEfforts(id, { ...routeInfo, displayName: name ?? routeInfo.displayName })\n if (suggestion.efforts === undefined) {\n nextModels.push(model)\n continue\n }\n const fill: JsonObject = { ...model }\n let touched = false\n // Capacities are never filled here: contextWindow / maxTokens are\n // display-only reference values in the browser half, and writing them\n // host-side would silently override the route defaults.\n // Reaching this branch means neither part was declared and no durable\n // unset marker stands on the row -- so there is no stale marker inside\n // `fill` to scrub; the guards above already excluded it.\n if (!effortsDeclared && fillEfforts) {\n touched = true\n fill['reasoningEfforts'] = suggestion.efforts as JsonObject\n // Provenance: the browser flush has to be able to tell this knowledge\n // base suggestion apart from a declaration the user made, or a staged\n // intent that differs in one spelling is dropped as a takeover (see\n // AUTOFILL_MARKER). The value is the revision this fill read.\n fill[AUTOFILL_MARKER] = revision\n if (suggestion.compat !== undefined) {\n const stored = isRecord(model['compat']) ? (model['compat'] as JsonObject) : {}\n fill['compat'] = { ...stored, ...(suggestion.compat as JsonObject) }\n }\n }\n if (!inputDeclared && fillModalities && suggestion.input !== undefined) {\n touched = true\n fill['input'] = [...suggestion.input]\n }\n if (!touched) {\n nextModels.push(model)\n continue\n }\n changed = true\n nextModels.push(fill)\n }\n if (changed) patchRoutes[route] = { models: nextModels }\n }\n return Object.keys(patchRoutes).length === 0 ? undefined : { providers: patchRoutes }\n}\n\n/** Strip newer-only compat keys from an autofill patch (older-kernel downgrade). */\nexport function stripNewCompatKeysDeep(patch: JsonObject): JsonObject | undefined {\n const providers = isRecord(patch['providers']) ? (patch['providers'] as JsonObject) : undefined\n if (providers === undefined) return undefined\n let strippedAny = false\n const nextProviders: JsonObject = {}\n for (const [route, profile] of Object.entries(providers)) {\n if (!isRecord(profile) || !Array.isArray(profile['models'])) {\n nextProviders[route] = profile as JsonObject\n continue\n }\n nextProviders[route] = {\n ...(profile as JsonObject),\n models: (profile['models'] as JsonObject[]).map(model => {\n if (!isRecord(model) || !isRecord(model['compat'])) return model\n const compat = { ...(model['compat'] as JsonObject) }\n for (const key of NEW_COMPAT_KEYS) {\n if (key in compat) {\n delete compat[key]\n strippedAny = true\n }\n }\n return { ...model, compat }\n }),\n }\n }\n if (!strippedAny) return undefined\n return { providers: nextProviders }\n}\n\n/**\n * Merge the role pin into one declared model row, or refuse it.\n *\n * Refusals (all silent, all deliberate): the row carries no ladder dict\n * (`false`, absent, or a deliberate-unset marker -- bare rows keep the bare\n * wire behavior they were chosen for); its compat already names an explicit\n * role value; or the route is not a self-hosted relay. A refusal changes\n * nothing; an acceptance touches exactly one compat field.\n */\nfunction backfillRolePin(\n model: Record<string, unknown>,\n providers: unknown,\n route: string,\n): JsonObject | undefined {\n if (!isRecord(model['reasoningEfforts'])) return undefined\n if (model[UNSET_MARKER] === true) return undefined\n const compat = model['compat']\n const base: JsonObject = isRecord(compat) ? { ...compat } : {}\n if (base['supportsDeveloperRole'] !== undefined) return undefined\n if (!isSelfHostedRelay(routeFactsOf(providers, route))) return undefined\n return { ...model, compat: { ...base, supportsDeveloperRole: false } }\n}\n", "/**\n * The running auto-fill complement (issue #7).\n *\n * The host fills once at boot. Everything a session adds afterwards is filled\n * HERE, on the idle pass: that is the only moment no official card is holding a\n * revision baseline a write would invalidate.\n *\n * @module dsh-model-think-level/client/injection/autofill-run\n */\n\nimport { AUTOFILL_CONFIG_PATH, PI_AI_NS } from '../../constants.js'\nimport { buildAutofillPatch } from '../../autofill.js'\nimport { describeNamespace, userProvidersOf } from '../ops.js'\nimport type { RemoteApi } from '../types.js'\n\n/** What the complement needs from its host. */\nexport interface IdleAutofillDeps {\n /** The settings remote. */\n api: RemoteApi\n}\n\n/** The complement's face. */\nexport interface IdleAutofill {\n /** Run the complement; fire-and-forget on the injector's idle pass. */\n run: () => void\n}\n\n/**\n * Build the auto-fill complement.\n * @param deps - the settings remote.\n * @returns the complement's {@link IdleAutofill} face.\n */\nexport function createIdleAutofill(deps: IdleAutofillDeps): IdleAutofill {\n let autofillSwitches: Promise<{ autofill: boolean; modalityAutofill: boolean }> | undefined\n\n /**\n * The host's autofill switches, read once. A `dsh.client` declaration carries\n * no plugin config, so a deployment configured `autofill: false` would\n * otherwise still be written to from the page. An unreachable or older host\n * keeps the documented defaults rather than silently disabling the feature.\n */\n const autofillSwitchesOf = (): Promise<{ autofill: boolean; modalityAutofill: boolean }> => {\n autofillSwitches ??= (async () => {\n const fallback = { autofill: true, modalityAutofill: true }\n try {\n const response = await fetch(AUTOFILL_CONFIG_PATH, { method: 'GET' })\n if (!response.ok) return fallback\n const body = (await response.json()) as { ok?: boolean; data?: { autofill?: unknown; modalityAutofill?: unknown } }\n if (body?.ok !== true) return fallback\n return {\n autofill: body.data?.autofill !== false,\n modalityAutofill: body.data?.modalityAutofill !== false,\n }\n } catch {\n return fallback\n }\n })()\n return autofillSwitches\n }\n\n /**\n * Fill the models this session added, through the very patch builder the\n * host's boot pass uses (so one suggestion can never produce two different\n * documents). Runs on the idle pass only.\n */\n const runIdleAutofill = async (): Promise<void> => {\n try {\n const switches = await autofillSwitchesOf()\n if (!switches.autofill) return\n // Fresh: the scope mirror folds a just-settled write in asynchronously,\n // so reading it here would fence this fill on a superseded revision and\n // manufacture a `settings/conflict` (the same reason a conflict retry\n // re-reads the wire).\n const join = await describeNamespace(deps.api, { fresh: true })\n const namespace = join.namespace\n if (namespace === undefined || join.writable !== true) return\n const userProviders = userProvidersOf(namespace)\n if (userProviders === undefined) return\n const patch = buildAutofillPatch(\n userProviders, () => true, { modalities: switches.modalityAutofill }, namespace.revision,\n )\n if (patch === undefined) return\n const routes = patch['providers'] as Record<string, { models: unknown }>\n const ops = Object.entries(routes).map(([route, profile]) => ({\n op: 'set' as const,\n path: ['providers', route, 'models'],\n value: profile.models,\n })) as unknown as Parameters<typeof deps.api.settings.mutate>[1]\n const response = await deps.api.settings.mutate(PI_AI_NS, ops, namespace.revision)\n // A refusal arrives as a VALUE, not a throw: without this check the whole\n // complement failed silently (the catch below never ran). Whatever is\n // still undeclared is picked up by the next idle pass.\n if (!response.ok) {\n console.error(`[bre] idle autofill refused: ${response.error.message}`)\n }\n } catch (error) {\n console.error(`[bre] idle autofill failed: ${error instanceof Error ? error.message : String(error)}`)\n }\n }\n\n return { run: () => { void runIdleAutofill() } }\n}\n", "/**\n * The Models-page DOM injection.\n *\n * Owns the document-wide MutationObserver, the debounced scan, the retry\n * backoff, the editor mounts, the host-label anchors and the ledger teardown.\n * The scan itself is `reconcile()` from the editor injector; this module is the\n * wiring and the lifecycle around it.\n *\n * The composer path is NOT this module's business: the caller hands `start()`\n * a synchronous hook that runs on every mutation burst BEFORE the debounced\n * scan, because the composer body must land before the first paint while the\n * settings-page scan does heavy wire reads.\n *\n * @module dsh-model-think-level/client/injection/models-page\n */\n\nimport { createElement } from 'react'\nimport type { ReactNode } from 'react'\nimport { createRoot } from 'react-dom/client'\nimport type { Translate } from '@deepseek-ai/dsh-client-ui-slots'\nimport { PLUGIN_ID } from '../../constants.js'\nimport { EffortEditor } from '../EffortEditor.tsx'\nimport {\n createScanState, flushOnTeardown, flushOnUnload, reconcile,\n type HostLabels, type InjectorDeps, type ScanState,\n} from './models-page-editor.js'\nimport { describeNamespace } from '../ops.ts'\nimport type { ClientContext, RemoteApi } from '../types.js'\nimport { EffortBoundary, panelRoot } from './mount.js'\nimport {\n createEditorTabsState,\n reconcileEditorTabs,\n teardownEditorTabs,\n type EditorTabsDeps,\n} from './editor-tabs.js'\nimport {\n createProviderOrderState,\n reconcileProviderOrder,\n teardownProviderOrder,\n type ProviderOrderDeps,\n} from './provider-order-drag.js'\nimport {\n createProviderToggleState,\n reconcileProviderEnabled,\n teardownProviderEnabled,\n type ProviderToggleDeps,\n} from './provider-toggle.js'\nimport {\n reconcileProviderDeleteSeat,\n teardownProviderDeleteSeats,\n type ProviderDeleteDeps,\n} from './provider-delete-seat.js'\nimport { teardownAutoEffortSeats } from './auto-effort-seat.js'\nimport {\n createModelOrderState,\n reconcileModelOrder,\n teardownModelOrder,\n type ModelOrderDeps,\n} from './model-order-drag.js'\n\n/** Dictionary namespace owning the Models page's copy (ui-settings-models). */\nconst HOST_MODELS_NS = 'settings.models'\n\n/**\n * The official Models-page controls this plugin anchors to, as\n * (dictionary key, English copy) pairs \u2014 the English copy is both the anchor\n * for a host that ships no such namespace and the language the host itself\n * falls back to, so it stays valid in every configuration. Resolved through\n * the host's own dictionary so a third language (or a late language pack)\n * relabels the page and the anchors together.\n */\nconst HOST_LABEL_KEYS = {\n // The disclosure copy was renamed `Capacities`/`\u5BB9\u91CF` \u2192 `Model options`/\n // `\u6A21\u578B\u9009\u9879` in 0.1.6-alpha.2, so the no-dictionary fallback carries BOTH\n // English labels (newest first). The dictionary key resolves first on every\n // host that ships one.\n capacity: ['modelAdvanced', 'Model options', 'Capacities'],\n modelId: ['modelId', 'Model ID'],\n modelName: ['modelName', 'Display name'],\n routeId: ['customRoute', 'Provider ID'],\n baseUrl: ['baseUrl', 'Base URL'],\n apiProtocol: ['customApi', 'API protocol'],\n // The editing card's action row. `apply` is the commit (en 'Apply' / zh\n // '\u4FDD\u5B58'), `cancel` the dismiss; the busy copy is 'applying' and needs no\n // anchor of its own: the button keeps its position in the row.\n apply: ['apply', 'Apply'],\n cancel: ['cancel', 'Cancel'],\n // A provider row's edit button. The row order pass reads the provider id out\n // of this label's `{provider}` slot for every row this plugin gets no seat in\n // (the DeepSeek rows), so the template must be resolved in the page's own\n // language rather than assumed.\n editProvider: ['editProvider', 'Edit {provider}'],\n} as const satisfies Record<keyof HostLabels, readonly [string, string, ...string[]]>\n\n/** What the Models-page injection needs from its host. */\nexport interface ModelsPageDeps {\n /** The client root context. */\n ctx: ClientContext\n /** The settings remote the editors read and write through. */\n api: RemoteApi\n /** The plugin's locale-bound translator. */\n t: Translate\n /** Run once per idle pass, after everything the session held back landed. */\n onIdle: () => void\n /** Wrap a subtree so it re-translates on a language switch. */\n refreshed: (children: () => ReactNode) => ReactNode\n}\n\n/** The Models-page injection's face. */\nexport interface ModelsPageInjection {\n /** The live scan state (the ledger teardown drains it). */\n readonly state: ScanState\n /** The host label anchors in the host's ACTIVE language. */\n labels: () => HostLabels\n /**\n * Begin observing. `onComposerMutation` runs synchronously on every mutation\n * burst, before the debounced scan.\n */\n start: (onComposerMutation: () => void) => void\n /** Disconnect the observer, cancel pending timers, stop the scan chain. */\n stopObserver: () => void\n /** Land the held writes and unmount every editor; the fiber is going away. */\n teardown: () => void\n /** Land only the committed writes and discard the rest (page unload). */\n flushOnUnload: () => void\n /** Force a rescan (document update, connection reset, preference flip). */\n schedule: () => void\n}\n\n/**\n * Build the Models-page injection.\n * @param deps - context, settings remote, copy, idle hook and locale refresh.\n * @returns the injection's {@link ModelsPageInjection} face.\n */\nexport function createModelsPage(deps: ModelsPageDeps): ModelsPageInjection {\n const { ctx, api, t, onIdle, refreshed } = deps\n\n /**\n * The official controls' aria-labels in the host's ACTIVE language.\n *\n * A thunk, never a cached value: the host renders those labels from its own\n * dictionary through its own fallback chain, so re-reading per scan is what\n * keeps a language switch (or a late language pack) from stranding the\n * anchors on words the page no longer prints.\n */\n const labels = (): HostLabels => {\n const translate = ctx.locale.bind(HOST_MODELS_NS) as (key: string) => string\n const resolve = ([key, ...fallbacks]: readonly [string, string, ...string[]]): readonly string[] => {\n const value = translate(key)\n // A host with no such namespace makes translate() echo the key back.\n return value === key || value.trim() === '' ? fallbacks : [value, ...fallbacks]\n }\n return {\n capacity: resolve(HOST_LABEL_KEYS.capacity),\n modelId: resolve(HOST_LABEL_KEYS.modelId),\n modelName: resolve(HOST_LABEL_KEYS.modelName),\n routeId: resolve(HOST_LABEL_KEYS.routeId),\n baseUrl: resolve(HOST_LABEL_KEYS.baseUrl),\n apiProtocol: resolve(HOST_LABEL_KEYS.apiProtocol),\n apply: resolve(HOST_LABEL_KEYS.apply),\n cancel: resolve(HOST_LABEL_KEYS.cancel),\n editProvider: resolve(HOST_LABEL_KEYS.editProvider),\n }\n }\n\n /** Debounce window for DOM-mutation scans (one scan per render burst). */\n const SCAN_DEBOUNCE_MS = 120\n const scanState = createScanState()\n const providerOrderState = createProviderOrderState()\n const providerOrderDeps: ProviderOrderDeps = { t, labels }\n const providerToggleState = createProviderToggleState()\n const providerToggleDeps: ProviderToggleDeps = { t, labels }\n // No per-scan state: the seat IS the DOM node, and the pass is idempotent by\n // its presence, so nothing has to be remembered between scans.\n const providerDeleteDeps: ProviderDeleteDeps = { t, labels }\n const modelOrderState = createModelOrderState()\n const modelOrderDeps: ModelOrderDeps = { t, labels }\n const editorTabsState = createEditorTabsState()\n const editorTabsDeps: EditorTabsDeps = { t }\n let scanTimer: number | undefined\n let retryTimer: number | undefined\n let observer: MutationObserver | undefined\n let composerMutation: (() => void) | undefined\n // Set on dispose. An idle pass already in flight can still call onBackoff\n // after stopObserver cleared the timer; without this gate that would re-arm\n // a retry timer on a dead fiber, restarting the scan chain (and re-mounting\n // editors nothing will ever unmount).\n let stopped = false\n\n /**\n * The injector's dependencies, built ONCE outside the debounced scan: the\n * teardown path (plugin dispose, page unload) drains the ledgers through the\n * very same seam a live scan uses, so a landing write can never take a\n * different path than an in-session one.\n */\n const injectorDeps: InjectorDeps = {\n api,\n describeNamespace: () => describeNamespace(api),\n t,\n labels,\n // The idle pass landed everything the session held back; the caller's\n // complement rides the same moment.\n onIdle,\n // A refused held write arms a backoff; nothing on a settled page would\n // schedule the retry scan, so the injector asks for a timer.\n onBackoff: (delayMs) => { scheduleRetry(delayMs) },\n mount(container, props) {\n const rootEl = document.createElement('div')\n // The slot class carries the grid-column span: this wrapper \u2014 not\n // the React editor inside it \u2014 is the item the official disclosure\n // grid places (see STYLES).\n rootEl.className = 'bre-effort-slot'\n // Mark the container synchronously \u2014 before React renders \u2014 so the\n // idempotency guard (hasEditor) holds from the very first scan.\n // Without this, the appendChild-triggered MutationObserver scan can\n // run while React's async render has not produced the editor div\n // yet, misjudge the row as unmounted, and mount again \u2014 an infinite\n // loop that grows the container without bound.\n rootEl.dataset['plugin'] = PLUGIN_ID\n container.appendChild(rootEl)\n const reactRoot = createRoot(rootEl)\n const renderEditor = (p: typeof props): void => {\n reactRoot.render(createElement(\n EffortBoundary,\n {\n fallbackText: t('renderFailed'),\n // Same seat as the composer bodies: without the locale\n // subscription a language switch re-renders the official page\n // but not this editor \u2014 sameProps compares only document data,\n // so the copy would stay in the language it rendered in.\n children: refreshed(() => createElement(EffortEditor, p)),\n },\n ))\n }\n renderEditor(props)\n return {\n unmount: () => { reactRoot.unmount() },\n render: renderEditor,\n }\n },\n }\n\n /**\n * Wake the injector when a failed idle pass's backoff expires: a settled\n * settings page emits no DOM mutation, so without this the held intent would\n * wait for a scan that never comes. One timer at a time; the fired timer\n * re-runs the scan, which re-arms the next backoff if the write is refused.\n */\n function scheduleRetry(delayMs: number): void {\n if (stopped || retryTimer !== undefined) return\n retryTimer = window.setTimeout(() => {\n retryTimer = undefined\n schedule()\n }, delayMs)\n }\n\n function schedule(): void {\n if (stopped || scanTimer !== undefined) return\n // Debounce: the official page re-renders in bursts (typing, expanding,\n // applying); one scan per frame keeps the editor stable mid-keystroke.\n scanTimer = window.setTimeout(() => {\n scanTimer = undefined\n // The composer path rides along: the debounced scan is the only thing\n // that covers the paths no mutation carries \u2014 apply boot and a slider\n // preference flip.\n composerMutation?.()\n reconcile(panelRoot(), injectorDeps, scanState)\n // The tab pass rides the settled scan as well, after the editor injector\n // has stocked the open card. Its reactive path is the observer above; this\n // one covers the paths no mutation carries \u2014 a boot over an editor that\n // is already open, and a retry whose scan re-arms without a re-render.\n // Its own writes are guarded, so no scan ever chases its own tail.\n reconcileEditorTabs(panelRoot(), editorTabsDeps, editorTabsState)\n // The row order pass rides the same scan but NOT the editor reconcile:\n // `reconcile` bails out whenever no editing card is open, and the\n // provider rows are on the page precisely when nothing is being edited.\n reconcileProviderOrder(panelRoot(), providerOrderDeps, providerOrderState)\n // The switch pass follows the order pass: it reads the same row ids, and\n // it must see the rows where that pass left them.\n reconcileProviderEnabled(panelRoot(), providerToggleDeps, providerToggleState)\n // The delete seat follows the switch pass: it reads the same rows, and it\n // fills the hole the host leaves in an unremovable row's action group.\n reconcileProviderDeleteSeat(panelRoot(), providerDeleteDeps)\n // The model pass comes last and covers a different list entirely: the\n // model rows inside whichever provider editor is open. Its own lists are\n // absent unless a card is expanded, so it no-ops on a collapsed page.\n reconcileModelOrder(panelRoot(), modelOrderDeps, modelOrderState)\n }, SCAN_DEBOUNCE_MS)\n }\n\n const start = (onComposerMutation: () => void): void => {\n if (stopped || observer !== undefined) return\n composerMutation = onComposerMutation\n // A session may already be resident when the fiber starts (page reload,\n // HMR): wire it before the first mutation has a chance to land.\n onComposerMutation()\n observer = new MutationObserver(() => {\n // The composer path runs SYNCHRONOUSLY on the mutation microtask: the\n // React commit that opens the menu and this callback are delivered\n // before the browser's next paint, so the FIRST painted frame already\n // carries the replicated popover \u2014 the official menu never flashes and\n // is never \"covered\". The settings-page editor/toggle reconciles stay\n // debounced below (they do heavy wire reads).\n // The effort-memory wiring leads the callback for the same reason: a\n // session switch lands as a DOM mutation, and the new session's\n // directory must be watched before its projection can read as Default.\n onComposerMutation()\n // The tab pass runs HERE too, synchronously, for the same reason: it is\n // pure DOM work (attribute writes, no wire reads), and it must land in\n // the microtask that carries the official commit. Deferred to the\n // debounced scan below, the opened editor would paint as the official\n // one long column and then visibly re-sort itself a frame or more later\n // \u2014 the \"click Edit and the tabbed editor flickers in\" report.\n reconcileEditorTabs(panelRoot(), editorTabsDeps, editorTabsState)\n schedule()\n })\n observer.observe(document.body, { childList: true, subtree: true })\n schedule()\n }\n\n const stopObserver = (): void => {\n stopped = true\n if (scanTimer !== undefined) {\n window.clearTimeout(scanTimer)\n scanTimer = undefined\n }\n if (retryTimer !== undefined) {\n window.clearTimeout(retryTimer)\n retryTimer = undefined\n }\n observer?.disconnect()\n observer = undefined\n }\n\n const teardown = (): void => {\n stopObserver()\n // Land what the session held back before the fiber goes away: a plugin\n // disable or HMR must not strand the user's intent in memory alone. The\n // ledgers stay in sessionStorage WITH their commit evidence, so the next\n // fiber in this document retries a flush the runtime cut short.\n flushOnTeardown(scanState, injectorDeps)\n // Orphaned editors must not outlive the fiber: on plugin disable or HMR\n // they would keep rendering with a stale api face, failing every write\n // visibly. Unmount every React root this plugin created.\n for (const [, entry] of scanState.mounted) entry.editor.unmount()\n scanState.mounted.clear()\n // The grips are raw DOM this plugin owns inside the official page: leaving\n // them behind would keep a dead fiber's listeners live on a live page.\n teardownProviderOrder(providerOrderState)\n // Same for the enable switches: a live listener on a dead fiber would keep\n // toggling providers the user can no longer see the state of.\n teardownProviderEnabled()\n // The delete seats are raw DOM as well, and they stand for a control the\n // page owns: a dead fiber must not leave a row reading as unavailable.\n teardownProviderDeleteSeats()\n // And for the auto-adapt seats: raw DOM this plugin put in the official\n // catalogue head, which a dead fiber must not leave clickable-looking.\n teardownAutoEffortSeats()\n // And for the model grips inside an open editor, whose own lists live\n // outside the provider rows' seat.\n teardownModelOrder(modelOrderState)\n // And for the editor tabs: the bar and the region tags are raw DOM and\n // attributes this plugin owns inside the official card \u2014 a dead fiber must\n // not leave the page keyed to tabs nothing maintains anymore.\n teardownEditorTabs()\n }\n\n const flushOnUnloadNow = (): void => { flushOnUnload(scanState, injectorDeps) }\n\n return {\n state: scanState,\n labels,\n start,\n stopObserver,\n teardown,\n flushOnUnload: flushOnUnloadNow,\n schedule,\n }\n}\n", "/**\n * The thinking-effort + input-modality editor injected into the official\n * Models page's model rows. Rendered with ReactDOM.createRoot into a DOM\n * container the injector creates, it needs no harness services of its own:\n * everything arrives through a plain props face, so it stays testable in\n * isolation. The draft/intent rules live in the client/effort module.\n *\n * Suggestion feedback is ZONED: provenance + confidence form their own line;\n * capacity references render as a separate read-only block explicitly marked\n * \"never auto-filled\" -- the official capacity inputs above stay untouched.\n * The apply/reset actions sit at the very bottom and own every section.\n *\n * @module dsh-model-think-level/EffortEditor\n */\n\nimport { useEffect, useRef, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport { PLUGIN_ID } from '../constants.js'\nimport type {\n CompatSuggestion,\n InputModalities,\n InputSource,\n ReasoningEfforts,\n} from '../knowledge.js'\nimport {\n buildIntent,\n draftFrom,\n LEVEL_ORDER,\n sameEfforts,\n type DraftLevels,\n type EffortIntent,\n} from './effort.js'\nimport { defaultWireRisk } from './wire-preview.js'\nimport { suggestEfforts } from '../knowledge.js'\nimport { listenAutoEffort, queueAutoEffort } from './auto-effort.js'\nimport type { EffortEditorApi, EffortWriteIntent, PendingWrite, PendingWriteParts } from './types.js'\n\n/** Thousands-grouped token counts, matching the official capacity inputs. */\nconst COUNT = new Intl.NumberFormat('en-US')\n\n/** One model row's data the editor needs. */\nexport interface EffortModel {\n /** Model id (the settings key). */\n id: string\n /** Display name, when one is set. */\n name?: string\n /** The model's current reasoningEfforts declaration. */\n efforts?: false | ReasoningEfforts\n}\n\n/** Props of {@link EffortEditor}. */\nexport interface EffortEditorProps {\n /** Route key. */\n route: string\n /**\n * Route display name. Part of the mount contract but never rendered here:\n * this editor lives inside ONE model row, whose provider card already\n * carries the name, so the component does not even destructure it. The\n * injector keeps it in the prop set and its sameProps diff compares it, which\n * is what catches a provider rename under an already-mounted editor.\n */\n routeDisplayName: string\n /** Route wire protocol, when configured. */\n routeApi?: string\n /** Route endpoint, when configured. */\n routeBaseURL?: string\n /** Model id. */\n modelId: string\n /** Model display name, when one is set. */\n modelName?: string\n /** The model's current reasoningEfforts declaration. */\n efforts?: false | ReasoningEfforts\n /** A deliberate unset is also a configured decision bulk adapt must preserve. */\n effortsUnset?: boolean\n /** The model's current input-modality declaration. */\n input?: InputModalities\n /** The model's stored compat block (passthrough; suggestions merge over it). */\n compat?: CompatSuggestion\n /** The model's stored per-model default-effort pick, when one is set. */\n defaultEffort?: string\n /** Model row index (for aria labels). */\n index: number\n /**\n * The model row is not saved yet (a create card's draft route, or a new\n * model row on a saved route). Apply then stages the declaration instead\n * of writing settings; the injector applies staged declarations\n * automatically once the row is saved.\n */\n staged?: boolean\n /**\n * Whether the official Models page owns input types for this row (the\n * 0.1.6-alpha.2 `ModelInputTypes` control was sniffed in the disclosure).\n * True hides the modality section: the official editor is the single\n * surface, so showing a second one would be duplicate UI. Older kernels\n * have no such control and keep the section.\n */\n officialInputTypes?: boolean\n /** The write seam (settings.mutate plus the suggestion engine). */\n api: EffortEditorApi\n /** Read-only (settings document not writable). */\n readOnly: boolean\n /** Localized copy. */\n t: (key: string, params?: Record<string, string | number>) => string\n}\n\n/** Draft state of the modality part: declared or inheriting, image or not. */\nexport interface DraftModality {\n declared: boolean\n image: boolean\n}\n\n/** Read the stored declaration into a draft. Empty mirrors undefined: the\n * resolved settings layer materializes absent arrays as [], and core reads\n * that as \"no answer here\" -- never as a phantom text-only declaration. */\nexport function modalityFrom(input: InputModalities | undefined): DraftModality {\n if (input === undefined || input.length === 0) return { declared: false, image: false }\n return { declared: true, image: input.includes('image') }\n}\n\n/** Resolve a modality draft to the write intent (null = durably unset). */\nexport function buildModalityIntent(draft: DraftModality): InputModalities | null {\n if (!draft.declared) return null\n return draft.image ? ['text', 'image'] : ['text']\n}\n\n/**\n * The compat fields this editor can SET, per protocol \u2014 which is also the set\n * it may clear. A key from neither list survives an apply untouched: a\n * hand-tuned field the UI never showed is not this editor's to drop. Clearing\n * a picker means unset, not \"keep whatever was chosen last\".\n */\nexport const OWNED_COMPAT_KEYS: Readonly<Record<string, readonly string[]>> = {\n 'openai-completions': ['thinkingTokenBudgetField', 'supportsThinkingTokenBudget', 'vllmPriority'],\n 'openai-responses': ['supportsMaxOutputTokens'],\n}\n\n/** The compat fields clearable on one protocol (empty = this edit owns none). */\nexport function ownedCompatKeys(routeApi: string | undefined): readonly string[] {\n return OWNED_COMPAT_KEYS[(routeApi ?? '').toLowerCase()] ?? []\n}\n\n/** The owned compat fields the draft actually left EMPTY. */\nexport function clearedCompatKeys(routeApi: string | undefined, draft: CompatSuggestion | undefined): readonly string[] {\n const owned = ownedCompatKeys(routeApi)\n if (owned.length === 0) return []\n const set = new Set<string>(draft === undefined ? [] : Object.keys(draft))\n return owned.filter(key => !set.has(key))\n}\n\n/**\n * The `clearCompatKeys` part of a commit, derived from the draft.\n *\n * A DEFINED draft always reports (an empty array is meaningful: every owned\n * field it shows is set). An EMPTY draft must still clear the fields the\n * protocol OWNS -- dropping the last owned pick is a user decision to unset --\n * but only when there is something to clear: an unrelated route keeps the\n * shape it always had (no `clearCompatKeys` at all).\n * @param routeApi - the route's wire protocol.\n * @param writeCompat - the compat bytes this commit writes, if any.\n * @returns the clear part to spread into the commit payload.\n */\nexport function compatClearIntent(\n routeApi: string | undefined,\n writeCompat: CompatSuggestion | undefined,\n): { clearCompatKeys?: readonly string[] } {\n if (writeCompat !== undefined) return { clearCompatKeys: clearedCompatKeys(routeApi, writeCompat) }\n const owned = clearedCompatKeys(routeApi, undefined)\n return owned.length === 0 ? {} : { clearCompatKeys: owned }\n}\n\n/** Semantic equality between a modality draft and a stored declaration. */\nexport function sameModality(draft: DraftModality, stored: InputModalities | undefined): boolean {\n if (!draft.declared) return stored === undefined\n if (stored === undefined) return false\n return draft.image ? stored.includes('image') : !stored.includes('image')\n}\n\n/**\n * The ladder part of a write, from a switch state and the draft it resolves to.\n *\n * `'keep'` when nothing is armed and nothing is stored: that is a no-op, NOT an\n * unset \u2014 an undefined there would stamp the durable unset marker onto a\n * never-declared model and silence host auto-fill.\n * @param nextThinking - the (pre-update) switch state the edit resolved to.\n * @param built - `buildIntent` of the draft the edit resolved to, which is the\n * explicit `false` when the only armed level is 'off'.\n * @param initialEfforts - the row's stored declaration.\n * @returns the ladder intent to write.\n */\nexport function ladderIntent(\n nextThinking: boolean,\n built: EffortIntent,\n initialEfforts: EffortWriteIntent | undefined,\n): EffortWriteIntent {\n const next = nextThinking ? built : false\n return nextThinking && next === undefined && initialEfforts === undefined ? 'keep' : next\n}\n\n/**\n * The compat draft a FRESH editor on this row would hold.\n *\n * The editor's three compat inputs are seeded from the stored block, so an\n * untouched editor's draft is exactly the stored block restricted to the three\n * fields it shows. The injector's provider-wide adapt has no editor to ask for\n * a draft, so it derives the same value here: a hand-tuned field the UI does\n * not show stays untouchable, and one it does show survives the write.\n * @param compat - the row's stored compat block.\n * @returns the draft, or undefined when it holds no field at all.\n */\nexport function compatDraftOf(compat: CompatSuggestion | undefined): CompatSuggestion | undefined {\n const out: CompatSuggestion = {}\n const budgetField = compat?.thinkingTokenBudgetField ?? ''\n // The cast is the same one the editor's own compatDraft applies to its state\n // string: the stored block is already typed, and a hand-tuned spelling the\n // plugin's union does not name must survive the round trip unchanged.\n if (budgetField !== '') out.thinkingTokenBudgetField = budgetField as CompatSuggestion['thinkingTokenBudgetField']\n const priorityText = compat?.vllmPriority === undefined ? '' : String(compat.vllmPriority)\n const pruned = priorityText.trim()\n if (pruned !== '' && /^-?\\d+$/.test(pruned)) out.vllmPriority = Number.parseInt(pruned, 10)\n const maxOutput = compat?.supportsMaxOutputTokens === undefined ? '' : String(compat.supportsMaxOutputTokens)\n if (maxOutput === 'true') out.supportsMaxOutputTokens = true\n else if (maxOutput === 'false') out.supportsMaxOutputTokens = false\n return Object.keys(out).length === 0 ? undefined : out\n}\n\n/**\n * Assemble one complete pending write from the parts a caller resolved: the\n * compat merge (the applied suggestion's block with the caller's own fields on\n * top), the thinking-budget default an openai-completions endpoint needs, and\n * the clear list for the fields this write OWNS and left empty.\n *\n * The single implementation of that assembly: the row's own editor reports the\n * user's edits through it, and the injector's provider-wide adapt reports\n * collapsed rows through it, so the same suggestion always writes the same\n * bytes whichever path applied it.\n * @param parts - the resolved parts (see {@link PendingWriteParts}).\n * @returns the write to hand the injector's ledger.\n */\nexport function pendingWriteOf(parts: PendingWriteParts): PendingWrite {\n const baseCompat = parts.appliedCompat\n const manualCompat = parts.manualCompat\n let writeCompat = baseCompat !== undefined ? { ...baseCompat, ...(manualCompat ?? {}) } : manualCompat\n if (\n parts.initialCompat?.supportsThinkingTokenBudget === true\n && parts.initialCompat?.thinkingTokenBudgetField === undefined\n && writeCompat?.thinkingTokenBudgetField === undefined\n && (parts.routeApi ?? '').toLowerCase() === 'openai-completions'\n ) {\n writeCompat = { ...(writeCompat ?? {}), thinkingTokenBudgetField: 'thinking_token_budget' }\n }\n return {\n efforts: parts.efforts,\n ...(writeCompat === undefined ? {} : { compat: writeCompat }),\n ...(parts.input === undefined ? {} : { input: parts.input }),\n ...compatClearIntent(parts.routeApi, writeCompat),\n ...(parts.defaultEffort === undefined ? {} : { defaultEffort: parts.defaultEffort }),\n }\n}\n\n/**\n * Render one model's thinking-effort + input-modality editor: the level\n * checkboxes, the modality toggle, the auto-adapt action, and the\n * apply/reset actions that own both sections.\n */\nexport function EffortEditor({ route, routeApi, routeBaseURL, modelId, modelName, efforts: initialEfforts, effortsUnset = false, input: initialInput, compat: initialCompat, defaultEffort: initialDefaultEffort, index, staged = false, officialInputTypes = false, api, readOnly, t }: EffortEditorProps): ReactNode {\n const [draft, setDraft] = useState<DraftLevels>(() => draftFrom(initialEfforts))\n const [modality, setModality] = useState<DraftModality>(() => modalityFrom(initialInput))\n // The master thinking switch. OFF reports the ladder as `false` \u2014 the row's\n // own \"declares no reasoning-effort control\" \u2014 which is what makes the level\n // grid below unnecessary; ON keeps every edit byte-for-byte as before. It\n // starts ON unless the stored row already says `false`, so a row nobody\n // touches never gains a declaration it did not have.\n const [thinking, setThinking] = useState<boolean>(() => initialEfforts !== false)\n // The per-model default-effort pick (issue #4), as the level id or '' for\n // \"follow the memory chain\". The pick lives in the same Apply as the ladder\n // so one mutate carries both.\n const [defaultEffort, setDefaultEffort] = useState<string>(() => initialDefaultEffort ?? '')\n const [busy, setBusy] = useState(false)\n const [message, setMessage] = useState<{ kind: 'success' | 'error' | 'info'; text: string } | undefined>(undefined)\n const [suggested, setSuggested] = useState<ReasoningEfforts | false | undefined>(undefined)\n const [suggestedSource, setSuggestedSource] = useState('')\n const [suggestedConfidence, setSuggestedConfidence] = useState<'high' | 'medium' | 'low'>('low')\n // The compat block the applied suggestion carries, kept in a REF on purpose:\n // it describes the wire format, not the level set, so level/image tweaks must\n // NOT clear it -- markDirty() resets the user-facing draft context, and a\n // state here would wipe the very bytes Apply/stage needs to reproduce what\n // the host autofill writes. Only a fresh Auto-adapt replaces it; Reset\n // discards it along with everything else.\n const appliedCompatRef = useRef<CompatSuggestion | undefined>(undefined)\n // Modality + capacity parts of the applied suggestion. Capacities are\n // DISPLAY-ONLY: they render in the reference block and never touch the\n // official capacity inputs.\n const [budgetField, setBudgetField] = useState<string>(() => initialCompat?.thinkingTokenBudgetField ?? '')\n const [priorityText, setPriorityText] = useState<string>(() => initialCompat?.vllmPriority === undefined ? '' : String(initialCompat.vllmPriority))\n const [maxOutput, setMaxOutput] = useState<string>(() => initialCompat?.supportsMaxOutputTokens === undefined ? '' : String(initialCompat.supportsMaxOutputTokens))\n const previousCompat = useRef<CompatSuggestion | undefined>(initialCompat)\n const [suggestedInputSource, setSuggestedInputSource] = useState<InputSource | undefined>(undefined)\n const [referenceContext, setReferenceContext] = useState<number | undefined>(undefined)\n const [referenceMaxTokens, setReferenceMaxTokens] = useState<number | undefined>(undefined)\n // Once the user edits, the drafts own the row: a re-render that changes\n // the initial props (the official page re-renders on its own edits and on\n // apply) must not overwrite in-flight input. Before any edit the drafts\n // simply follow the props.\n const dirtyRef = useRef(false)\n const previousEfforts = useRef<false | ReasoningEfforts | undefined>(initialEfforts)\n const previousInput = useRef<InputModalities | undefined>(initialInput)\n const previousDefaultEffort = useRef<string | undefined>(initialDefaultEffort)\n // The auto-adapt closure of the LATEST render, for the listener below: that\n // closure reads this render's props and state, but the subscription has to\n // outlive them, so the listener reaches it through a ref instead of\n // re-subscribing on every render.\n const autoAdaptRef = useRef<(() => Promise<void>) | undefined>(undefined)\n // The SAVED ladder, not the in-flight draft. Bulk adapt must not overwrite a\n // model whose thinking levels are already configured; the row's own \u81EA\u52A8\u9002\u914D\n // link still may, because that click is about this one row.\n const savedEffortsRef = useRef(initialEfforts)\n savedEffortsRef.current = initialEfforts\n const effortsUnsetRef = useRef(effortsUnset)\n effortsUnsetRef.current = effortsUnset\n\n // The catalogue head's \"adapt every model\" seat (user request \u2464) publishes\n // one request per provider. An unconfigured row answers it with the same\n // autoAdapt() its own link runs. A row that already has a stored ladder\n // (including an explicit \"does not reason\" or a durable unset) does not:\n // bulk adapt fills gaps and leaves configured levels alone. The row's own\n // link is unaffected.\n useEffect(() => listenAutoEffort((target) => {\n if (target !== route) return\n if (savedEffortsRef.current !== undefined || effortsUnsetRef.current) return\n const run = autoAdaptRef.current\n if (run !== undefined) queueAutoEffort(run)\n }), [route])\n\n useEffect(() => {\n if (!sameEfforts(previousEfforts.current, initialEfforts)) {\n previousEfforts.current = initialEfforts\n if (!dirtyRef.current) {\n setDraft(draftFrom(initialEfforts))\n setThinking(initialEfforts !== false)\n }\n }\n if (previousInput.current !== initialInput) {\n previousInput.current = initialInput\n // A push that lands while the user is mid-edit must not clobber the\n // draft -- same contract as the level grid above.\n if (!dirtyRef.current) setModality(modalityFrom(initialInput))\n }\n if (previousDefaultEffort.current !== initialDefaultEffort) {\n previousDefaultEffort.current = initialDefaultEffort\n if (!dirtyRef.current) setDefaultEffort(initialDefaultEffort ?? '')\n }\n if (JSON.stringify(previousCompat.current ?? null) !== JSON.stringify(initialCompat ?? null)) {\n previousCompat.current = initialCompat\n if (!dirtyRef.current) {\n setBudgetField(initialCompat?.thinkingTokenBudgetField ?? '')\n setPriorityText(initialCompat?.vllmPriority === undefined ? '' : String(initialCompat.vllmPriority))\n setMaxOutput(initialCompat?.supportsMaxOutputTokens === undefined ? '' : String(initialCompat.supportsMaxOutputTokens))\n }\n }\n // The pick participates in the same sync discipline as the other three\n // parts \u2014 its token MUST ride the dependency array, or a pick-only push\n // (another tab, a hand-edited document) never reaches this draft, and a\n // later unrelated Apply writes the stale pick back over the document.\n }, [initialEfforts, initialInput, initialCompat, initialDefaultEffort])\n\n const compatDraft = (): CompatSuggestion | undefined => {\n const out: CompatSuggestion = {}\n if (budgetField !== '') out.thinkingTokenBudgetField = budgetField as CompatSuggestion['thinkingTokenBudgetField']\n const pruned = priorityText.trim()\n if (pruned !== '' && /^-?\\d+$/.test(pruned)) out.vllmPriority = Number.parseInt(pruned, 10)\n if (maxOutput === 'true') out.supportsMaxOutputTokens = true\n else if (maxOutput === 'false') out.supportsMaxOutputTokens = false\n return Object.keys(out).length === 0 ? undefined : out\n }\n const compatChanged = (): boolean => {\n const draftOut = compatDraft()\n return JSON.stringify(draftOut ?? null) !== JSON.stringify({\n ...(initialCompat?.thinkingTokenBudgetField === undefined ? {} : { thinkingTokenBudgetField: initialCompat.thinkingTokenBudgetField }),\n ...(initialCompat?.vllmPriority === undefined ? {} : { vllmPriority: initialCompat.vllmPriority }),\n ...(initialCompat?.supportsMaxOutputTokens === undefined ? {} : { supportsMaxOutputTokens: initialCompat.supportsMaxOutputTokens }),\n })\n }\n // The declared levels of this draft: the pick's value domain.\n const armedLevels = LEVEL_ORDER.filter(level => draft[level].on)\n const defaultEffortIntent = defaultEffort === (initialDefaultEffort ?? '')\n ? undefined\n : defaultEffort === '' ? null : defaultEffort\n\n // What the row would report right now: the switch OFF *is* the `false`\n // declaration, so it participates in the dirty comparison like any other part.\n const effectiveEfforts: false | ReasoningEfforts | undefined = thinking ? buildIntent(draft) : false\n const changed = !sameEfforts(effectiveEfforts, initialEfforts)\n || !sameModality(modality, initialInput)\n || compatChanged()\n || defaultEffortIntent !== undefined\n\n /**\n * What this editor last handed the injector, serialized for idempotency.\n *\n * The editor has no commit action of its own since C2: every change reports\n * itself, and the injector decides where the intent lands. React's state\n * updates are asynchronous, so each change point computes its OWN next value\n * and passes it down \u2014 nothing here reads a state value the current event has\n * not yet committed to. The ref then collapses repeat reports: a controlled\n * text input fires on every keystroke, and the wire value only moves on some\n * of them.\n */\n const lastCommitRef = useRef<string | undefined>(undefined)\n\n const markDirty = (): void => {\n dirtyRef.current = true\n setSuggested(undefined)\n setSuggestedSource('')\n setSuggestedConfidence('low')\n setSuggestedInputSource(undefined)\n // appliedCompatRef deliberately SURVIVES this (see its declaration) --\n // the commit still has to write the applied suggestion's compat bytes.\n // Reference capacities SURVIVE tweaks too: they are read-only context about\n // the model, not part of the applied suggestion -- only Reset or a fresh\n // Auto-adapt replaces them.\n }\n\n /**\n * Report the row's complete pending edit to the injector.\n *\n * The whole intent is recomputed from the values handed in, never from the\n * stored state: the caller has just computed them, and reading `draft` here\n * would see the pre-update value on the reporting turn.\n *\n * The ladder part travels as `'keep'` when nothing is armed and nothing is\n * stored: that is a no-op, NOT an unset -- sending `undefined` would stamp\n * the durable unset marker onto a never-declared model and silence host\n * auto-fill.\n */\n const commitPending = (\n nextDraft: DraftLevels,\n nextModality: DraftModality,\n nextDefaultEffort: string,\n // The switch's pre-update value: a toggle computes its own next value and\n // hands it in, exactly like the three parts above.\n nextThinking: boolean = thinking,\n ): void => {\n // OFF is the explicit `false` declaration, whatever the draft still holds\n // (the draft is deliberately left alone, so flipping back loses no edits).\n const effortsIntent = ladderIntent(nextThinking, buildIntent(nextDraft), initialEfforts)\n // The modality part travels ONLY when this edit actually changed it: an\n // untouched row must omit the intent entirely -- a null would stamp the\n // durable inputUnset marker onto modalities the user never decided about.\n const nextInput = sameModality(nextModality, initialInput) ? undefined : buildModalityIntent(nextModality)\n const defaultEffortOut = nextDefaultEffort === (initialDefaultEffort ?? '')\n ? undefined\n : nextDefaultEffort === '' ? null : nextDefaultEffort\n // The compat merge, the owned-key clear list and the thinking-budget\n // default live in ONE place ({@link pendingWriteOf}), shared with the\n // injector's provider-wide adapt: a suggestion must write the same bytes\n // whichever path applied it.\n const write = pendingWriteOf({\n efforts: effortsIntent,\n input: nextInput,\n defaultEffort: defaultEffortOut,\n manualCompat: compatDraft(),\n appliedCompat: appliedCompatRef.current,\n routeApi,\n initialCompat,\n })\n // The payload IS the identity: two edits that resolve to the same write are\n // the same report, whatever draft states produced them.\n const signature = JSON.stringify(write)\n if (lastCommitRef.current === signature) return\n lastCommitRef.current = signature\n api.commit(route, modelId, write)\n // \"Modified\" is DERIVED, never messaged: every handler below clears the\n // message in the same batch, so a pending-save message set here would be\n // overwritten on the very turn it became true (React folds both updates).\n // It renders from `changed` instead \u2014 see the message block.\n }\n\n const patchLevel = (level: (typeof LEVEL_ORDER)[number], on: boolean): void => {\n markDirty()\n // Disarming the level the pick names clears the pick with it: the pick is\n // a level of the declared ladder, and a stale one must not survive.\n const nextDefaultEffort = !on && defaultEffort === level ? '' : defaultEffort\n if (nextDefaultEffort !== defaultEffort) setDefaultEffort(nextDefaultEffort)\n setDraft(current => {\n const next = { ...current, [level]: { ...current[level], on } }\n // Enabling a thinking level pre-fills the conventional spelling.\n if (on && level !== 'off' && next[level].wire.trim().length === 0) {\n next[level] = { ...next[level], wire: level }\n }\n commitPending(next, modality, nextDefaultEffort)\n return next\n })\n setMessage(undefined)\n }\n\n /**\n * The master switch. ON restores the ordinary ladder editing; OFF reports the\n * explicit `false` declaration and takes the default pick with it (a pick\n * names a level of a ladder that no longer exists). The DRAFT is deliberately\n * left untouched: flipping back and forth loses no wire spellings.\n */\n const patchThinking = (on: boolean): void => {\n markDirty()\n setThinking(on)\n const nextDefaultEffort = on ? defaultEffort : ''\n if (nextDefaultEffort !== defaultEffort) setDefaultEffort(nextDefaultEffort)\n commitPending(draft, modality, nextDefaultEffort, on)\n setMessage(undefined)\n }\n\n const patchWire = (level: (typeof LEVEL_ORDER)[number], wire: string): void => {\n markDirty()\n setDraft(current => {\n const next = { ...current, [level]: { ...current[level], wire } }\n commitPending(next, modality, defaultEffort)\n return next\n })\n setMessage(undefined)\n }\n\n const patchImage = (on: boolean): void => {\n markDirty()\n const nextModality = { declared: true, image: on }\n setModality(nextModality)\n commitPending(draft, nextModality, defaultEffort)\n setMessage(undefined)\n }\n\n const clearModality = (): void => {\n markDirty()\n const nextModality = { declared: false, image: false }\n setModality(nextModality)\n commitPending(draft, nextModality, defaultEffort)\n setMessage(undefined)\n }\n\n // Each text field reports on its own change: a controlled input fires per\n // keystroke, and commitPending collapses the ones that do not move the value.\n const patchBudgetField = (value: string): void => {\n markDirty()\n setBudgetField(value)\n commitPending(draft, modality, defaultEffort)\n setMessage(undefined)\n }\n\n const patchPriority = (value: string): void => {\n markDirty()\n setPriorityText(value)\n commitPending(draft, modality, defaultEffort)\n setMessage(undefined)\n }\n\n const patchMaxOutput = (value: string): void => {\n markDirty()\n setMaxOutput(value)\n commitPending(draft, modality, defaultEffort)\n setMessage(undefined)\n }\n\n const patchDefaultEffort = (value: string): void => {\n markDirty()\n setDefaultEffort(value)\n commitPending(draft, modality, value)\n setMessage(undefined)\n }\n\n const applySuggestion = (\n parts: {\n efforts: ReasoningEfforts | false\n source: string\n confidence: 'high' | 'medium' | 'low'\n compat?: CompatSuggestion\n input?: InputModalities\n inputSource?: InputSource\n contextWindow?: number\n maxTokens?: number\n defaultEffort?: string\n },\n ): void => {\n markDirty()\n // A suggestion carries a verdict about reasoning: arm the switch to match\n // it, or applying a ladder while the switch is OFF would report `false`\n // and write the ladder nowhere.\n const nextThinking = parts.efforts !== false\n setThinking(nextThinking)\n const nextDraft = draftFrom(parts.efforts)\n setDraft(nextDraft)\n // The suggestion's own modality, when it carries one; else the draft's.\n const nextModality = parts.input === undefined ? modality : modalityFrom(parts.input)\n if (parts.input !== undefined) setModality(nextModality)\n // A blank pick takes the vendor default. A pick the user already chose\n // stays: auto-adapt must not overwrite a level they set by hand.\n const nextDefault = defaultEffort === '' && parts.defaultEffort !== undefined\n ? parts.defaultEffort\n : defaultEffort\n if (nextDefault !== defaultEffort) setDefaultEffort(nextDefault)\n setSuggested(parts.efforts)\n setSuggestedSource(parts.source)\n setSuggestedConfidence(parts.confidence)\n appliedCompatRef.current = parts.compat\n setSuggestedInputSource(parts.inputSource)\n setReferenceContext(parts.contextWindow)\n setReferenceMaxTokens(parts.maxTokens)\n setMessage({\n kind: 'info',\n text: t('appliedHint', { source: parts.source, confidence: t('confidence_' + parts.confidence) }),\n })\n // One commit for the whole suggestion, so the ledger never holds the\n // ladder without the compat block that makes its wire spellings work.\n commitPending(nextDraft, nextModality, nextDefault, nextThinking)\n }\n\n const autoAdapt = async (): Promise<void> => {\n // The link that normally calls this is `disabled` while the row is\n // read-only or already working, so the gate had to be the button's. The\n // provider-wide seat request (user request \u2464) calls THIS directly and\n // bypasses the button entirely, so the same gate has to live here or a\n // read-only row would be written.\n if (readOnly || busy) return\n setBusy(true)\n setMessage(undefined)\n try {\n // A staged route has no stored profile yet; the card's own typed\n // protocol/endpoint are the facts inference has to work with.\n const reply = staged\n ? await api.suggest(route, modelId, modelName, { ...(routeApi === undefined ? {} : { api: routeApi }), ...(routeBaseURL === undefined ? {} : { baseURL: routeBaseURL }) })\n : await api.suggest(route, modelId, modelName)\n if (!reply.ok) {\n setMessage({ kind: 'error', text: reply.error === 'no-suggestion' ? t('noSuggestion') : reply.error })\n return\n }\n applySuggestion(reply.suggestion)\n } catch (error) {\n setMessage({ kind: 'error', text: t('writeError', { message: String(error) }) })\n } finally {\n setBusy(false)\n }\n }\n\n // Hand the listener above the closure of this render. An effect rather than a\n // write during render: React may discard a render it never commits.\n useEffect(() => { autoAdaptRef.current = autoAdapt })\n\n const reset = (): void => {\n // Back to the SAVED declarations, not to \"everything off\": Reset means\n // \"discard my edits\". Clearing a declaration is still one flow away --\n // uncheck everything (the unset intent).\n dirtyRef.current = false\n // The ledger outlives this component's state: without withdrawing, the\n // discard is cosmetic and the official Save writes the edits anyway.\n api.withdraw(route, modelId)\n setDraft(draftFrom(initialEfforts))\n setThinking(initialEfforts !== false)\n setModality(modalityFrom(initialInput))\n setDefaultEffort(initialDefaultEffort ?? '')\n setBudgetField(initialCompat?.thinkingTokenBudgetField ?? '')\n setPriorityText(initialCompat?.vllmPriority === undefined ? '' : String(initialCompat.vllmPriority))\n setMaxOutput(initialCompat?.supportsMaxOutputTokens === undefined ? '' : String(initialCompat.supportsMaxOutputTokens))\n setSuggested(undefined)\n setSuggestedSource('')\n setSuggestedConfidence('low')\n appliedCompatRef.current = undefined\n setSuggestedInputSource(undefined)\n setReferenceContext(undefined)\n setReferenceMaxTokens(undefined)\n setMessage(undefined)\n // Let the next edit report itself again: the collapsed signature belongs\n // to the intent that was just withdrawn.\n lastCommitRef.current = undefined\n }\n\n // Default-wire warning (issue #2): a stored forced-thinking ladder sends\n // an off-equivalent on Default/test calls. The format prefers the just\n // applied suggestion, else the knowledge base for this route.\n const wireFormat = appliedCompatRef.current?.thinkingFormat\n ?? suggestEfforts(modelId, {\n ...(routeApi === undefined ? {} : { api: routeApi }),\n ...(routeBaseURL === undefined ? {} : { baseURL: routeBaseURL }),\n }).compat?.thinkingFormat\n const wireRisk = defaultWireRisk(initialEfforts, wireFormat)\n\n const disabled = readOnly || busy\n\n return (\n <div className=\"bre-effort-editor\" data-plugin={PLUGIN_ID}>\n {/* The master switch lives OUTSIDE the effort card on purpose: with it\n off there is no ladder to configure, so the card below is not rendered\n at all \u2014 one row is everything an un-thinking model shows. */}\n <label className=\"bre-modality-row bre-thinking-switch\">\n <input\n type=\"checkbox\"\n checked={thinking}\n disabled={disabled}\n aria-label={t('thinkingMode') + ' ' + String(index + 1)}\n onChange={(event) => { patchThinking(event.target.checked) }}\n />\n <span className=\"bre-effort-level\">{t('thinkingMode')}</span>\n {thinking ? null : <span className=\"bre-effort-note\">{t('thinkingModeOff')}</span>}\n </label>\n {thinking ? (\n <div className=\"bre-effort-card\">\n <div className=\"bre-effort-head\">\n <span className=\"bre-effort-title\">{t('reasoningEffort')}</span>\n <button\n type=\"button\"\n className=\"bre-link-button\"\n disabled={disabled}\n onClick={() => { void autoAdapt() }}\n >\n {t('autoAdapt')}\n </button>\n </div>\n <div className=\"bre-effort-grid\">\n {LEVEL_ORDER.map(level => {\n const cell = draft[level]\n return (\n <label key={level} className=\"bre-effort-row\">\n <input\n type=\"checkbox\"\n checked={cell.on}\n disabled={disabled}\n aria-label={t('level_' + level) + ' ' + String(index + 1)}\n onChange={(event) => { patchLevel(level, event.target.checked) }}\n />\n <span className=\"bre-effort-level\">{t('level_' + level)}</span>\n {/* Every armed level takes a wire input, off included: a\n non-null off spelling is what some formats send to close\n thinking (OpenAI's none spelling, or the deepseek/zai\n explicit disabled object); an empty one sends nothing. */}\n {cell.on\n ? (\n <input\n type=\"text\"\n className=\"bre-effort-wire\"\n value={cell.wire}\n disabled={disabled}\n placeholder={t('wirePlaceholder')}\n aria-label={t('level_' + level) + ' ' + t('wireValue')}\n onChange={(event) => { patchWire(level, event.target.value) }}\n />\n )\n : <span className=\"bre-effort-empty\" />}\n </label>\n )\n })}\n </div>\n {armedLevels.length > 0 ? (\n <div className=\"bre-modality\">\n <span className=\"bre-effort-title\">{t('defaultEffortLabel')}</span>\n <label className=\"bre-modality-row\">\n <select\n className=\"bre-select\"\n disabled={disabled}\n aria-label={t('defaultEffortLabel') + ' ' + String(index + 1)}\n value={defaultEffort}\n onChange={(event) => { patchDefaultEffort(event.target.value) }}\n >\n <option value=\"\">{t('defaultEffortUnset')}</option>\n {/* A pick pushed from outside the draft (a hand-edited\n document) can name a level this draft no longer declares:\n render it anyway, so the select shows the real state\n instead of silently blanking to the first option. */}\n {(defaultEffort !== '' && !armedLevels.includes(defaultEffort as (typeof LEVEL_ORDER)[number])\n ? [...armedLevels, defaultEffort as (typeof LEVEL_ORDER)[number]]\n : armedLevels\n ).map(level => (\n <option key={level} value={level}>{t('level_' + level)}</option>\n ))}\n </select>\n {defaultEffort !== '' ? (\n <button\n type=\"button\"\n className=\"bre-link-button bre-modality-clear\"\n disabled={disabled}\n onClick={() => { patchDefaultEffort('') }}\n >\n {t('clearDefaultEffort')}\n </button>\n ) : null}\n </label>\n <p className=\"bre-modality-note\">{t('defaultEffortHint')}</p>\n </div>\n ) : null}\n {/* The official page owns input types from 0.1.6-alpha.2 on (its\n ModelInputTypes control was sniffed in this row's disclosure): the\n official editor is the single surface then, so this section stands\n down. Older kernels keep it. */}\n {officialInputTypes ? null : (\n <div className=\"bre-modality\">\n <span className=\"bre-effort-title\">{t('inputModality')}</span>\n <label className=\"bre-modality-row\">\n <input\n type=\"checkbox\"\n checked={modality.declared && modality.image}\n disabled={disabled}\n aria-label={t('modalityImage') + ' ' + String(index + 1)}\n onChange={(event) => { patchImage(event.target.checked) }}\n />\n <span className=\"bre-effort-level\">{t('modalityImage')}</span>\n {modality.declared\n ? (\n <button\n type=\"button\"\n className=\"bre-link-button bre-modality-clear\"\n disabled={disabled}\n onClick={clearModality}\n >\n {t('clearDeclaration')}\n </button>\n )\n : null}\n </label>\n {!modality.declared ? <p className=\"bre-modality-note\">{t('modalityInherit')}</p> : null}\n </div>\n )}\n {(routeApi ?? '').toLowerCase() === 'openai-completions' ? (\n <div className=\"bre-compat\">\n <span className=\"bre-effort-title\">{t('compatTitle')}</span>\n <label className=\"bre-compat-row\">\n <span className=\"bre-compat-label\">{t('budgetFieldLabel')}</span>\n <select\n className=\"bre-select\"\n disabled={disabled}\n aria-label={t('budgetFieldLabel') + ' ' + String(index + 1)}\n value={budgetField}\n onChange={(event) => { patchBudgetField(event.target.value) }}\n >\n <option value=\"\">{t('budgetUnset')}</option>\n <option value=\"thinking_token_budget\">thinking_token_budget</option>\n <option value=\"thinking_budget\">thinking_budget</option>\n <option value=\"thinking_budget_tokens\">thinking_budget_tokens</option>\n </select>\n <span className=\"bre-compat-hint\">{t('budgetFieldHint')}</span>\n </label>\n <div className=\"bre-compat-row\">\n <label className=\"bre-compat-field\">\n <span className=\"bre-compat-label\">{t('priorityLabel')}</span>\n <input\n type=\"text\"\n inputMode=\"numeric\"\n className=\"bre-text-input\"\n value={priorityText}\n disabled={disabled}\n placeholder={t('priorityPlaceholder')}\n aria-label={t('priorityLabel') + ' ' + String(index + 1)}\n onChange={(event) => { patchPriority(event.target.value) }}\n />\n </label>\n <span className=\"bre-compat-hint\">{t('priorityHint')}</span>\n {priorityText.trim() !== '' && !/^-?\\d+$/.test(priorityText.trim()) ? <p className=\"bre-effort-note bre-error\">{t('priorityInvalid')}</p> : null}\n </div>\n {initialCompat?.supportsThinkingTokenBudget === true && initialCompat?.thinkingTokenBudgetField === undefined ? <p className=\"bre-effort-note\">{t('aliasMigrated')}</p> : null}\n </div>\n ) : null}\n {(routeApi ?? '').toLowerCase() === 'openai-responses' ? (\n <div className=\"bre-compat\">\n <span className=\"bre-effort-title\">{t('compatTitle')}</span>\n <label className=\"bre-compat-row\">\n <span className=\"bre-compat-label\">{t('maxOutputLabel')}</span>\n <select\n className=\"bre-select\"\n disabled={disabled}\n aria-label={t('maxOutputLabel') + ' ' + String(index + 1)}\n value={maxOutput}\n onChange={(event) => { patchMaxOutput(event.target.value) }}\n >\n <option value=\"\">{t('maxOutputUnset')}</option>\n <option value=\"true\">{t('maxOutputOn')}</option>\n <option value=\"false\">{t('maxOutputOff')}</option>\n </select>\n <span className=\"bre-compat-hint\">{t('maxOutputHint')}</span>\n </label>\n </div>\n ) : null}\n {staged ? <p className=\"bre-effort-note\">{t('stagedHint')}</p> : null}\n {initialEfforts === false ? <p className=\"bre-effort-note\">{t('reasoningDisabled')}</p> : null}\n {wireRisk === undefined\n ? null\n : <p className=\"bre-effort-note\">{t(wireRisk === 'thinking-disabled' ? 'defaultRiskDisabled' : 'defaultRiskQwen')}</p>}\n {/* Unsetting says what it does: the save path below turns an\n all-off draft over a stored declaration into the durable unset\n marker, i.e. bare provider-default requests (the relay-compat\n mode of issue #2). Never-declared rows map to keep, so the hint\n stays hidden there exactly as the write does. */}\n {buildIntent(draft) === undefined && initialEfforts !== undefined\n ? <p className=\"bre-effort-note\">{t('bareHint')}</p> : null}\n {suggested !== undefined ? (\n <div className=\"bre-suggestion\">\n <p className=\"bre-effort-note\">\n {t('appliedHint', { source: suggestedSource, confidence: t('confidence_' + suggestedConfidence) })}\n </p>\n {suggestedInputSource === undefined\n ? null\n : (\n <p className=\"bre-effort-note\">\n {suggestedInputSource === 'endpoint'\n ? t('inputHintEndpoint')\n : suggestedInputSource === 'knowledge'\n ? t('inputHintKnowledge')\n : t('inputHintHeuristic')}\n </p>\n )}\n {referenceContext === undefined && referenceMaxTokens === undefined\n ? null\n : (\n <div className=\"bre-reference\">\n <span className=\"bre-reference-title\">{t('referenceTitle')}</span>\n <span className=\"bre-reference-values\">\n {referenceContext === undefined ? null : (\n <span>{t('contextWindowLabel')} <b>{COUNT.format(referenceContext)}</b></span>\n )}\n {referenceMaxTokens === undefined ? null : (\n <span>{t('maxTokensLabel')} <b>{COUNT.format(referenceMaxTokens)}</b></span>\n )}\n </span>\n </div>\n )}\n </div>\n ) : null}\n {message === undefined ? null : (\n <p className={'bre-effort-message bre-' + message.kind} role={message.kind === 'error' ? 'alert' : 'status'}>\n {message.text}\n </p>\n )}\n {!changed\n ? null\n : (\n <p className=\"bre-effort-message bre-info\" role=\"status\">\n {t('pendingSave')}\n </p>\n )}\n {!changed ? null : <p className=\"bre-effort-note\">{t('pendingHint')}</p>}\n {/* No commit button since C2: the row's edits land with the official\n card's own Save (issue #7), so the only action left is discarding\n them. A card-level commit would ride the frozen revision baseline\n that made that Save fail in the first place. */}\n <div className=\"bre-effort-actions\">\n <button\n type=\"button\"\n className=\"bre-secondary-button\"\n disabled={disabled || !changed}\n onClick={reset}\n >\n {t('reset')}\n </button>\n </div>\n </div>\n ) : null}\n </div>\n )\n}\n\nexport type { DraftLevels }\n", "/**\n * Pure draft logic of the thinking-effort editor: mapping a stored declaration\n * to the editor's checkbox draft and back, plus semantic comparison. Kept out\n * of the component so the intent rules are unit-testable without React.\n *\n * @module dsh-model-think-level/client/effort\n */\n\nimport { THINKING_LEVELS, type ReasoningEfforts, type ThinkingLevel } from '../knowledge.js'\n\n/** The escalation order, matching the schema's ladder (same tuple, local alias). */\nexport const LEVEL_ORDER: readonly ThinkingLevel[] = THINKING_LEVELS\n\n/** Draft state of one level's wire value. */\nexport type DraftLevels = Record<ThinkingLevel, { on: boolean; wire: string }>\n\n/** What the editor can write: unset (no declaration), disabled (false), or a dict. */\nexport type EffortIntent = ReasoningEfforts | false | undefined\n\n/**\n * The draft of one declaration. A `false` declaration (reasoning disabled)\n * shows as the off level armed \u2014 saving it writes `false`; an unset\n * declaration shows as every level off \u2014 saving it unsets the field.\n */\nexport function draftFrom(efforts: EffortIntent): DraftLevels {\n const draft = {} as DraftLevels\n for (const level of LEVEL_ORDER) {\n if (efforts === undefined) {\n draft[level] = { on: false, wire: '' }\n continue\n }\n if (efforts === false) {\n draft[level] = level === 'off' ? { on: true, wire: '' } : { on: false, wire: '' }\n continue\n }\n const wire = efforts[level]\n draft[level] = {\n on: wire !== undefined,\n wire: wire === null ? '' : typeof wire === 'string' ? wire : '',\n }\n }\n return draft\n}\n\n/**\n * Resolve a draft to the value to write:\n * - no level armed \u2192 `undefined` (unset the declaration);\n * - only `off` armed \u2192 `false` (reasoning disabled);\n * - at least one thinking level \u2192 the dict (with `off` when armed).\n *\n * The off level's wire value is meaningful: `null` means \"send nothing\"\n * (pi-ai lets the endpoint default apply), while a string is what\n * dispatch sends to close thinking \u2014 `none` on OpenAI-style endpoints,\n * or any non-null spelling to arm the deepseek/zai formats' explicit\n * `thinking: disabled`.\n */\nexport function buildIntent(draft: DraftLevels): EffortIntent {\n let off = false\n let thinking = false\n const out: ReasoningEfforts = {}\n for (const level of LEVEL_ORDER) {\n const cell = draft[level]\n if (!cell.on) continue\n if (level === 'off') {\n off = true\n const wire = cell.wire.trim()\n out.off = wire.length === 0 ? null : wire\n continue\n }\n thinking = true\n // A level switched on without a spelling defaults to its own name \u2014 the\n // convention every OpenAI-compatible endpoint follows.\n const wire = cell.wire.trim()\n out[level] = wire.length === 0 ? level : wire\n }\n if (thinking) return out\n return off ? false : undefined\n}\n\n/** Semantic equality of two intents, independent of object key order. */\nexport function sameEfforts(a: EffortIntent, b: EffortIntent): boolean {\n if (a === b) return true\n if (a === false || a === undefined || b === false || b === undefined) return false\n const aKeys = LEVEL_ORDER.filter(level => a[level] !== undefined)\n const bKeys = LEVEL_ORDER.filter(level => b[level] !== undefined)\n if (aKeys.length !== bKeys.length) return false\n return aKeys.every(level => a[level] === b[level])\n}\n", "/**\n * Default-wire risk: what pi-ai sends when a call names no effort.\n *\n * Three openai-completions thinking formats have no omit path: with no\n * effort selected they emit an off-equivalent even when the ladder offers no\n * `off` level (read from pi-ai 0.85 `dist/api/openai-completions.js`, kept in\n * sync by `scripts/wire-repro.mjs`). The generic `openai` format instead\n * sends nothing, so it is safe. This predicate names the risk for one\n * declared ladder so the editor can warn where the failure would otherwise\n * look inexplicable (issue #2: \"off \u5DF2\u5220\uFF0C\u6D4B\u8BD5\u9875\u7167\u6837 1210\").\n *\n * Pure data logic, no DOM: esbuild inlines it into the browser bundle.\n *\n * @module dsh-model-think-level/client/wire-preview\n */\n\nimport type { ReasoningEfforts } from '../knowledge.js'\n\n/** How a Default call fails on a forced-thinking ladder, by wire format. */\nexport type DefaultWireRisk =\n /** `thinking: {type: disabled}` -- rejected with 1210-class errors. */\n | 'thinking-disabled'\n /** `enable_thinking: false` -- rejected only where the upstream requires thinking on. */\n | 'enable-thinking-false'\n\n/**\n * The Default-wire risk of one declared ladder under one thinking format.\n * `false` (non-reasoning), absent declarations, ladders carrying `off`, and\n * formats with an omit path (generic `openai`, unknown) all answer\n * undefined: Default is safe there.\n */\nexport function defaultWireRisk(\n declared: ReasoningEfforts | false | undefined,\n thinkingFormat: string | undefined,\n): DefaultWireRisk | undefined {\n if (declared === undefined || declared === false) return undefined\n if ('off' in declared) return undefined\n if (thinkingFormat === 'zai' || thinkingFormat === 'deepseek') return 'thinking-disabled'\n if (thinkingFormat === 'qwen') return 'enable-thinking-false'\n return undefined\n}\n", "/**\n * The \"adapt every model of this provider\" request (user request \u2464).\n *\n * The seat beside the official fetch-models link and the per-row reasoning\n * editor are two DIFFERENT React roots: the seat is raw DOM this plugin writes\n * into the official catalogue head, while the editor is a root the injector\n * mounts into each model row's disclosure. They share no props, so the request\n * travels as one document event, and every editor whose own route matches\n * answers it by running the very same `autoAdapt()` its own \u81EA\u52A8\u9002\u914D link runs.\n *\n * That indirection is the point: adapting N models this way is byte-identical\n * to clicking that link N times, so the seat cannot drift from the control the\n * user already trusts \u2014 and it still reaches the models whose thinking switch\n * is OFF, which the link itself cannot (with the switch off the whole effort\n * card, link included, is not rendered at all).\n *\n * @module dsh-model-think-level/client/auto-effort\n */\n\n/** The event a seat dispatches and every mounted editor listens for. */\nexport const AUTO_EFFORT_EVENT = 'bre:auto-effort'\n\n/** The event's payload: the provider route whose models are to be adapted. */\nexport interface AutoEffortDetail {\n /** The provider route the request belongs to. */\n readonly route: string\n}\n\n/**\n * What one provider-wide adapt did, so the seat that asked can SAY so.\n *\n * The seat used to be a fire-and-forget control: the click held writes in a\n * ledger the user never saw, so \"did that work?\" had no answer on screen. The\n * counts below are the settings-document walk's own report \u2014 the arm that can\n * see every model of the route, which is exactly the set the click is about.\n */\nexport interface AutoAdaptReport {\n /** Unconfigured models this pass held an adaptation for. */\n readonly held: number\n /** Unconfigured models that had no suggestion to offer. */\n readonly unsuggested: number\n /**\n * Why the pass could not run at all, when it could not: another pass is in\n * flight, the document is not writable, the route is not saved yet, or the\n * route declares no models. Every one of those is \"nothing was held\", which\n * must not read as \"already configured\".\n */\n readonly blocked?: 'busy' | 'unwritable' | 'unknown-route' | 'no-models'\n /** The failure text when the pass threw before it could report counts. */\n readonly failed?: string\n}\n\n/**\n * The tail of the one-at-a-time queue. A catalogue can carry dozens of models\n * and every adapt reads the provider's own model list, so the requests must not\n * all be in flight at once. The chain never rejects: a failed adapt reports\n * itself inside its own editor (the editor owns that message), and one bad row\n * must not strand the rows queued behind it.\n */\nlet tail: Promise<void> = Promise.resolve()\n\n/** Run one auto-adapt after every earlier one settled. */\nexport function queueAutoEffort(run: () => Promise<void> | void): void {\n tail = tail.then(run, run).then(() => undefined, () => undefined)\n}\n\n/** Ask every editor of `route` on this document to adapt its own model. */\nexport function requestAutoEffort(route: string, target: Document = document): void {\n target.dispatchEvent(new CustomEvent<AutoEffortDetail>(AUTO_EFFORT_EVENT, { detail: { route } }))\n}\n\n/**\n * Answer this document's auto-adapt requests until the returned disposer runs.\n * A request naming another route is ignored \u2014 one card is open at a time, but a\n * re-render can leave a previous card's rows mounted for a scan.\n */\nexport function listenAutoEffort(handler: (route: string) => void, target: Document = document): () => void {\n const listener = (event: Event): void => {\n const detail = (event as CustomEvent<AutoEffortDetail>).detail\n if (typeof detail?.route !== 'string' || detail.route.length === 0) return\n handler(detail.route)\n }\n target.addEventListener(AUTO_EFFORT_EVENT, listener)\n return () => { target.removeEventListener(AUTO_EFFORT_EVENT, listener) }\n}\n", "/**\n * The \"adapt every model of this provider\" seat, beside the official\n * fetch-models link (user request \u2464).\n *\n * The official catalogue head carries exactly one control \u2014 the link that\n * fetches the provider's model list \u2014 so a provider with twenty models makes\n * the user open twenty rows and click \u81EA\u52A8\u9002\u914D in each. The seat stands next to\n * that link and asks every model of the provider to adapt at once.\n *\n * It is raw DOM inside the host's own head for the same reason the provider\n * delete seat is: the plugin owns no slot there. It never moves a React-owned\n * node \u2014 it only inserts itself as the anchor's next sibling and re-inserts\n * itself when a re-render drops it \u2014 and every write is compare-before-write,\n * so a settled pass emits no mutation of its own.\n *\n * The click does not write anything itself: it publishes a request on the\n * document (see `../auto-effort.js`), which the per-model editors answer for\n * their own rows, and hands the route to `deps.onRequest`, which answers for\n * the models that have no editor on screen -- a collapsed row renders no\n * disclosure container, so nothing else on the page can serve it. The seat\n * therefore holds no write path of its own, and neither answer stages a\n * document the open card is already holding.\n *\n * What the click DID is reported in a bubble at the top of the window\n * ({@link showNote}), never by rewriting the control: the head's button is the\n * host's layout, and a button whose label and enabledness change under the\n * pointer is a different control every time the user looks at it. The bubble\n * takes itself away again, because a verdict is feedback and not state.\n *\n * @module dsh-model-think-level/client/injection/auto-effort-seat\n */\n\nimport { requestAutoEffort, type AutoAdaptReport } from '../auto-effort.js'\n\n/** Marks the seat, and carries the route whose models it adapts. */\nconst SEAT_ATTR = 'data-bre-auto-effort'\n\n/** The card's catalogue, probed by class-name fragment (a CSS-module hash). */\nconst CATALOG_PROBE = '[class*=\"modelCatalog\"]'\n\n/** The catalogue's head row: the strip that carries the fetch link. */\nconst HEAD_PROBE = '[class*=\"modelListHead\"]'\n\n/** The host's own link-shaped control, probed the same way. */\nconst LINK_PROBE = 'button[class*=\"linkButton\"]'\n\n/**\n * The seat's class.\n *\n * It carries no plugin link class: the seat stands among the host's own head\n * controls and wears THEIR metrics, not the plugin's underlined link look\n * (user request: \"\u4E0D\u8981\u8D85\u94FE\u63A5\u6837\u5F0F\uFF0C\u8DDF\u53E6\u5916\u4E24\u4E2A\u4FDD\u6301\u4E00\u81F4\").\n */\nconst SEAT_CLASS = 'bre-auto-effort'\n\n/** The bubble's class, and the phase it carries for the sheet. */\nconst NOTE_CLASS = 'bre-auto-effort-note'\nconst NOTE_PHASE_ATTR = 'data-bre-auto-effort-phase'\nconst NOTE_TEXT_CLASS = 'bre-auto-effort-note-text'\nconst NOTE_DETAIL_CLASS = 'bre-auto-effort-note-detail'\n\n/**\n * How long a verdict stays on screen before it goes away.\n *\n * A result is feedback, not state: it answers the click the user just made, and\n * the setting it describes is visible in the rows themselves. A bubble that\n * outlives the moment would sit over the card the user is now working in \u2014 so\n * it lives as long as the host's own toast does, and no longer.\n */\nconst OUTCOME_TTL_MS = 3000\n\n/** One card's catalogue, paired with the route that card edits. */\nexport interface AutoEffortSeatTarget {\n /** The card's catalogue section. */\n readonly catalogue: HTMLElement\n /** The provider route the card edits. */\n readonly route: string\n}\n\n/**\n * What a reporter may answer with: a report, a promise of one, or nothing at\n * all (a host that cannot say what it did). Both shapes are accepted because\n * the injector's pass is async while a test double is usually not.\n */\nexport type AutoAdaptAnswer = AutoAdaptReport | void | Promise<AutoAdaptReport | void>\n\nexport interface AutoEffortSeatDeps {\n /** The plugin's own translator, for the label the seat carries. */\n t: (key: string, params?: Record<string, string | number>) => string\n /**\n * Adapt every model of the route that has no on-screen editor, and REPORT\n * what the pass did: the injector owns the settings writes, so the seat can\n * only ask and then say the answer. Absent in tests that only care where the\n * seat is placed \u2014 without it the control keeps its idle label, because\n * there is no truthful verdict to show.\n */\n onRequest?: (route: string) => AutoAdaptAnswer\n}\n\n/**\n * What the seat says about the last click on one route.\n *\n * A verdict is FEEDBACK, not state, and it is not painted onto the control: the\n * head's button keeps its label and its enabledness, because a\n * button that rewrites itself into \"\u5DF2\u9002\u914D 3 \u4E2A\" is a different control every\n * time the user looks at it \u2014 and the next click has to be readable as the same\n * action as the last one. The verdict appears in a bubble at the top of the\n * window instead (see {@link showNote}) and goes away on its own.\n */\ntype SeatPhase = 'working' | 'done' | 'empty' | 'unsuggested' | 'blocked' | 'failed'\n\n/** One route's last verdict, in the words the bubble renders. */\ninterface SeatOutcome {\n readonly phase: SeatPhase\n /** Unconfigured models the pass held an adaptation for. */\n readonly count: number\n /** Models of the same pass that had no suggestion (told in the detail line). */\n readonly missed: number\n /** The failure text, for the `failed` phase. */\n readonly detail: string\n}\n\n/**\n * The seat's word for one pass, or null when the pass has nothing to say.\n *\n * `busy` is the null case on purpose: a click that arrived while another pass\n * of the same route was in flight did not do anything, and reporting it would\n * erase the verdict the running pass is about to publish when it lands.\n * @param report - what the settings-document walk did.\n * @returns the verdict to paint, or null to leave the label as it is.\n */\nfunction outcomeOf(report: AutoAdaptReport): SeatOutcome | null {\n if (report.blocked === 'busy') return null\n if (report.failed !== undefined) return { phase: 'failed', count: 0, missed: 0, detail: report.failed }\n // Nothing to write is not one condition but three, and they read very\n // differently to a user: \"already configured\" is the pass working correctly,\n // \"blocked\" is the pass refusing, and conflating them is exactly the silence\n // this report exists to end.\n if (report.blocked !== undefined) return { phase: 'blocked', count: 0, missed: 0, detail: '' }\n if (report.held > 0) return { phase: 'done', count: report.held, missed: report.unsuggested, detail: '' }\n if (report.unsuggested > 0) return { phase: 'unsuggested', count: 0, missed: report.unsuggested, detail: '' }\n return { phase: 'empty', count: 0, missed: 0, detail: '' }\n}\n\n/**\n * The catalogue CONTAINERS inside a scope: the outermost matches only.\n *\n * {@link CATALOG_PROBE} is a class-name SUBSTRING, and the official catalogue\n * renders three more elements whose class names carry it \u2014 the heading, the\n * title and the meta nested inside the section. Only the outermost match IS a\n * catalogue, so the container is defined structurally here instead of by a\n * second list of class names to keep in step: seating a control in one of the\n * other three drops a button inside the heading, and the official\n * column-flex text then collapses to a character per line (the\n * empty-catalogue layout break).\n *\n * The sweep in {@link reconcileAutoEffortSeats} deliberately asks the NEAREST\n * match instead: a seat a previous pass left inside the heading belongs to no\n * catalogue this pass names, so it comes out.\n */\nexport function cataloguesOf(scope: HTMLElement): HTMLElement[] {\n return Array.from(scope.querySelectorAll<HTMLElement>(CATALOG_PROBE))\n .filter(candidate => (candidate.parentElement?.closest(CATALOG_PROBE) ?? null) === null)\n}\n\n/**\n * Give every target's catalogue head an auto-adapt seat. Idempotent: a head\n * that already has one is only relabelled and re-seated, and a seat whose\n * catalogue this pass no longer names is taken out.\n */\nexport function reconcileAutoEffortSeats(\n root: HTMLElement,\n targets: readonly AutoEffortSeatTarget[],\n deps: AutoEffortSeatDeps,\n): void {\n const wanted = new Map<HTMLElement, string>()\n for (const target of targets) {\n if (target.route.length === 0) continue\n if (!target.catalogue.isConnected) continue\n wanted.set(target.catalogue, target.route)\n }\n const label = deps.t('autoAdaptAll')\n for (const [catalogue, route] of wanted) {\n const head = catalogue.querySelector<HTMLElement>(HEAD_PROBE) ?? catalogue\n const seat = head.querySelector<HTMLButtonElement>(`[${SEAT_ATTR}]`) ?? createSeat(deps)\n // Beside the host's own link when the head renders one (the control the\n // user already reaches for), else at the head's end.\n const anchor = lastLink(head)\n if (anchor !== null) {\n if (anchor.nextElementSibling !== seat) anchor.after(seat)\n } else if (head.lastElementChild !== seat) {\n head.append(seat)\n }\n syncSeat(seat, route, label)\n }\n // The target set is the only state the seat set may hold: a seat whose\n // catalogue is gone \u2014 or that a React re-render moved out of one \u2014 comes out.\n // The NEAREST match answers, so a seat a previous pass left inside the\n // heading (whose own class name carries the stem, but which is not a\n // catalogue) belongs to nothing this pass names and goes. One head holds one\n // seat: the loop above re-homes a nested seat to head level, which would\n // otherwise leave it beside the seat that was already there.\n const seated = new Set<HTMLElement>()\n const routes = new Set(wanted.values())\n for (const seat of Array.from(root.querySelectorAll<HTMLElement>(`[${SEAT_ATTR}]`))) {\n const catalogue = seat.closest<HTMLElement>(CATALOG_PROBE)\n const route = seat.getAttribute(SEAT_ATTR) ?? ''\n if (catalogue === null || !wanted.has(catalogue) || seated.has(catalogue)) {\n seat.remove()\n // A route this pass no longer names is a card that CLOSED, so its bubble\n // goes with it; a route that is merely re-rendered is still in `routes`,\n // which is what keeps a bubble up across the host's own renders.\n if (route.length > 0 && !routes.has(route)) hideNote(route)\n continue\n }\n seated.add(catalogue)\n }\n}\n\n/** Take down one route's bubble and its pending dismissal (the card is gone). */\nfunction hideNote(route: string): void {\n const entry = notes.get(route)\n if (entry === undefined) return\n if (entry.timer !== null) clearTimeout(entry.timer)\n entry.note.remove()\n notes.delete(route)\n}\n\n/** Take every seat out again (plugin dispose, HMR teardown). */\nexport function teardownAutoEffortSeats(): void {\n for (const seat of Array.from(document.querySelectorAll<HTMLElement>(`[${SEAT_ATTR}]`))) {\n seat.remove()\n }\n // Bubbles belong to the page that was up, and the bubble elements are this\n // plugin's OWN DOM outside the seat \u2014 nothing else would ever unmount them.\n for (const entry of notes.values()) {\n if (entry.timer !== null) clearTimeout(entry.timer)\n entry.note.remove()\n }\n notes.clear()\n}\n\n/** The head's own fetch link: the LAST link-shaped button it renders. */\nfunction lastLink(head: HTMLElement): HTMLElement | null {\n const links = Array.from(head.querySelectorAll<HTMLElement>(LINK_PROBE))\n return links.length === 0 ? null : links[links.length - 1]\n}\n\nfunction createSeat(deps: AutoEffortSeatDeps): HTMLButtonElement {\n const seat = document.createElement('button')\n seat.className = SEAT_CLASS\n seat.setAttribute('type', 'button')\n seat.setAttribute(SEAT_ATTR, '')\n // The route is read at CLICK time, not captured: a create card's Provider ID\n // can be retyped after the seat was first placed, and the sync below rewrites\n // the attribute to whatever the card names now.\n seat.addEventListener('click', () => {\n const route = seat.getAttribute(SEAT_ATTR) ?? ''\n if (route.length === 0) return\n // Two answers, one click: the event reaches every mounted row's editor\n // (the expanded ones), and `onRequest` covers the rows the pass could not\n // mount -- a collapsed row has no disclosure container to mount into.\n requestAutoEffort(route)\n void runRequest(route, deps)\n })\n return seat\n}\n\n/**\n * Ask the injector to adapt the route, and report what came back in a bubble.\n *\n * Without `onRequest` there is no reporter (placement-only tests, and any host\n * that mounted the seat alone): the control then says nothing instead of\n * inventing a verdict.\n */\nasync function runRequest(route: string, deps: AutoEffortSeatDeps): Promise<void> {\n if (deps.onRequest === undefined) return\n // Shown before the await: the pass reads the settings document and probes\n // each model, which is long enough for a silent click to read as a dead one.\n showNote(route, { phase: 'working', count: 0, missed: 0, detail: '' }, deps, 0)\n let answer: AutoAdaptReport | void\n try {\n answer = await deps.onRequest(route)\n } catch (error) {\n answer = { held: 0, unsuggested: 0, failed: String(error) }\n }\n // A reporter that returned nothing said nothing, and one that answered\n // `busy` only refused a double click: both retire the bubble, because the pass\n // that IS running will put up its own verdict when it lands.\n if (answer === undefined) {\n hideNote(route)\n return\n }\n const outcome = outcomeOf(answer)\n if (outcome === null) return\n showNote(route, outcome, deps, OUTCOME_TTL_MS)\n}\n\n/**\n * Raise (or replace) a route's bubble at the top of the window, and \u2014 for a\n * verdict \u2014 arrange for it to retire.\n *\n * The bubble is this plugin's own DOM: it is never a child of the head, because\n * the head is the host's flex row and a fourth child would be a fourth item in\n * its layout. It hangs off the body as a fixed top-centre overlay instead,\n * exactly like the host's own toast and our own drag ghost.\n */\nfunction showNote(\n route: string,\n outcome: SeatOutcome,\n deps: AutoEffortSeatDeps,\n ttlMs: number,\n): void {\n let entry = notes.get(route)\n // A bubble already up for this route is REPLACED, not stacked: two passes of\n // the same card would otherwise leave two bubbles over the same page. One\n // that is no longer in the document was taken out from under us (a host that\n // clears the body, a teardown that missed it) and is rebuilt instead of\n // being written to in vain.\n if (entry !== undefined && !entry.note.isConnected) {\n if (entry.timer !== null) clearTimeout(entry.timer)\n notes.delete(route)\n entry = undefined\n }\n if (entry !== undefined && entry.timer !== null) clearTimeout(entry.timer)\n const note = entry?.note ?? buildNote()\n note.setAttribute(NOTE_PHASE_ATTR, outcome.phase)\n const text = note.querySelector<HTMLElement>(`.${NOTE_TEXT_CLASS}`)!\n const detail = note.querySelector<HTMLElement>(`.${NOTE_DETAIL_CLASS}`)!\n const wording = wordsOf(outcome, deps)\n if (text.textContent !== wording.text) text.textContent = wording.text\n if (detail.textContent !== wording.detail) detail.textContent = wording.detail\n detail.hidden = wording.detail.length === 0\n if (entry === undefined) document.body.append(note)\n // A working bubble has no lifetime of its own: the pass answers and replaces\n // it with the verdict. The clock starts when the verdict lands. The fade is\n // declared in the sheet and timed from here, so the two can never disagree.\n const timer = ttlMs > 0 ? setTimeout(() => { hideNote(route) }, ttlMs) : null\n note.style.setProperty('--bre-note-hold', `${ttlMs}ms`)\n notes.set(route, { note, timer })\n}\n\n/** The bubble's own element, empty until it is filled. */\nfunction buildNote(): HTMLElement {\n const note = document.createElement('div')\n note.className = NOTE_CLASS\n // Announced, because the control the user pressed does NOT change: for a\n // reader that cannot see the bubble, this is the only answer to the click.\n note.setAttribute('role', 'status')\n note.setAttribute('aria-live', 'polite')\n note.innerHTML = `<span class=\"${NOTE_TEXT_CLASS}\"></span><span class=\"${NOTE_DETAIL_CLASS}\"></span>`\n return note\n}\n\n/**\n * The bubbles currently up, one per route.\n *\n * A route is the key rather than the element: the seat that raised a bubble is\n * re-created by the host's own renders, and the bubble must outlive it \u2014 it is\n * fixed at the top of the window and no longer anchored to anything.\n */\nconst notes = new Map<string, { note: HTMLElement; timer: ReturnType<typeof setTimeout> | null }>()\n\n/** The note's two lines: what happened, and the part the headline omits. */\nfunction wordsOf(outcome: SeatOutcome, deps: AutoEffortSeatDeps): { text: string; detail: string } {\n switch (outcome.phase) {\n case 'working': return { text: deps.t('autoAdaptWorking'), detail: '' }\n case 'done':\n // The detail is the part the headline cannot hold: these writes are HELD\n // for the card's own Save, and some models may have had no suggestion.\n return {\n text: deps.t('autoAdaptDone', { count: outcome.count }),\n detail: outcome.missed > 0\n ? `${deps.t('autoAdaptDoneHint', { count: outcome.count })} ${deps.t('autoAdaptUnsuggestedHint')}`\n : deps.t('autoAdaptDoneHint', { count: outcome.count }),\n }\n case 'empty': return { text: deps.t('autoAdaptEmpty'), detail: deps.t('autoAdaptEmptyHint') }\n case 'unsuggested': return { text: deps.t('autoAdaptUnsuggested'), detail: deps.t('autoAdaptUnsuggestedHint') }\n case 'blocked': return { text: deps.t('autoAdaptBlocked'), detail: deps.t('autoAdaptBlockedHint') }\n case 'failed': return { text: deps.t('autoAdaptFailed', { message: outcome.detail }), detail: outcome.detail }\n }\n}\n\n/** Make the seat stand for this route, compare-before-write on every field. */\nfunction syncSeat(seat: HTMLElement, route: string, label: string): void {\n if (seat.getAttribute(SEAT_ATTR) !== route) seat.setAttribute(SEAT_ATTR, route)\n // Guarded: the page's observer watches childList, and replacing the seat's\n // text node on every scan would make each pass feed the next one. The label\n // is the IDLE label and never the verdict: see the module comment.\n if (seat.textContent !== label) seat.textContent = label\n // No tooltip: the seat reads as one more control of the host's own head, and\n // the host gives ITS controls none unless they can be blocked (user request:\n // \"\u4E5F\u4E0D\u8981\u9F20\u6807\u60AC\u6D6E\u63D0\u793A\"). The label alone names the action, as it does there.\n if (seat.getAttribute('title') !== null) seat.removeAttribute('title')\n}\n", "/**\n * The DOM bypass injector: the piece that makes reasoning effort editable\n * *inside* the official Models page's model rows.\n *\n * The official Models-page slot contract's two sanctioned seats are both\n * coarser than a model row: the keyed `settings.models.provider-card`\n * renders per provider card and `settings.models.footer`\n * renders after the rows \u2014 neither reaches a single model row's editor\n * internals, so this plugin mounts its editor as a DOM contribution next to\n * the official per-model capacity disclosure. The anchor is the official\n * disclosure chevron button, found by aria-label in both locales (the\n * `modelAdvanced` dictionary value: \"Capacities\"/\u5BB9\u91CF through 0.1.6-alpha.1,\n * \"Model options\"/\u6A21\u578B\u9009\u9879 from 0.1.6-alpha.2); the editor is inserted into the\n * same disclosure container that holds the official context-window / max\n * tokens fields.\n *\n * Because this walks the official page's rendered DOM (class names and\n * structure that the harness can change), the injector is defensive by\n * construction:\n * - it re-scans on every DOM mutation (a routed page, an open editor, an\n * applied form all re-render), and every scan reconciles idempotently;\n * - a model row that carries no disclosure yet is left alone and picked up\n * on the next mutation;\n * - if the official structure it depends on ever stops appearing, it simply\n * stops injecting \u2014 the settings page remains untouched.\n *\n * @module dsh-model-think-level/client/injection/models-page-editor\n */\n\nimport { AUTOFILL_MARKER, INPUT_UNSET_MARKER, PLUGIN_ID, UNSET_MARKER } from '../../constants.js'\nimport { suggestEfforts, type CompatSuggestion, type InputModalities, type ReasoningEfforts } from '../../knowledge.js'\nimport { modelsOf, routeFactsOf, isRecord } from '../../shared.js'\nimport { buildIntent, draftFrom, sameEfforts } from '../effort.js'\nimport {\n buildModalityIntent,\n compatDraftOf,\n ladderIntent,\n modalityFrom,\n pendingWriteOf,\n sameModality,\n} from '../EffortEditor.js'\nimport { compatOf, createEditorApi, defaultEffortOf, effortsOf, inputOf, nameOf, providersOf, writeModelRows, type RowIntent } from '../ops.js'\nimport type { EffortEditorApi, EffortWriteIntent, HeldWrite, RemoteApi, SettingsJoin } from '../types.js'\nimport { cataloguesOf, reconcileAutoEffortSeats, type AutoEffortSeatTarget } from './auto-effort-seat.js'\nimport type { AutoAdaptReport } from '../auto-effort.js'\nimport { panelRoot } from './mount.js'\n\nexport type { SettingsJoin }\n\n/**\n * Own-property membership over the providers dict. Plain `in` would answer\n * true for inherited names ('constructor', 'toString', '__proto__'\u2026), so a\n * route typed as one of those in the create card would resolve against\n * prototype members instead of being treated as unsaved.\n */\nfunction hasOwn(object: Record<string, unknown>, key: string): boolean {\n return Object.prototype.hasOwnProperty.call(object, key)\n}\n\n/**\n * Localized aria-labels of the official settings controls this injector\n * anchors to, in the language the host is currently rendering.\n *\n * The host renders these labels from its own `settings.models` dictionary\n * (`ui-settings-models`), so the plugin must read them through the same\n * channel instead of pinning the two languages it knows about: a host\n * language pack that adds, say, Japanese would otherwise relabel every\n * control and leave the injector unable to find a single model row. Each\n * field carries the active language first and English last \u2014 English is the\n * host's own fallback floor, so it doubles as ours.\n *\n * Every field is a LIST because the official labels are numbered per row\n * (\"Model ID 1\", \"\u30E2\u30C7\u30EB ID 2\", \u2026) and are matched by prefix.\n */\nexport interface HostLabels {\n /** The per-row disclosure button (\"Model options\", \"\u6A21\u578B\u9009\u9879\", \"Capacities\", \"\u5BB9\u91CF\", \u2026). */\n capacity: readonly string[]\n /** The model-id input. */\n modelId: readonly string[]\n /** The per-row display-name input. */\n modelName: readonly string[]\n /** The create card's route-id input. */\n routeId: readonly string[]\n /** The create card's endpoint input. */\n baseUrl: readonly string[]\n /** The create card's protocol select. */\n apiProtocol: readonly string[]\n /**\n * The editing card's commit button. C2 drives the plugin's landing write off\n * the official Save, so the button is resolved when the action row's own\n * structure cannot be read (the buttons ARE the last child pair of\n * `[class*=\"editorActions\"]`; the copy is the fallback).\n */\n apply: readonly string[]\n /** The editing card's dismiss button (the commit's left-hand sibling). */\n cancel: readonly string[]\n /**\n * A provider row's edit button, as the official `providerCopy` template\n * (\"Edit {provider}\" / \"\u7F16\u8F91 {provider}\"). Its `{provider}` slot carries\n * `Name (route)`, which is how a row names its provider even when the row\n * offers this plugin no seat to stamp that name on.\n */\n editProvider: readonly string[]\n}\n\n/** A row's identity as found on the page, resolved from the settings join. */\ninterface FoundModel {\n /**\n * The official disclosure container that holds the capacity fields, when the\n * row is EXPANDED. A collapsed row renders no such container at all (the\n * official editor returns null for it), so a row the user has not unfolded\n * still counts as found: it is a model of the provider and everything that\n * works per PROVIDER (the auto-adapt seat, the provider-wide adapt) must see\n * it. Only the per-row editor mount needs the container.\n */\n container?: HTMLElement\n /** The trigger's OWN model row element (row-scoped input reads). */\n row: HTMLElement\n /** The model id read from the row's \"Model ID\" input. */\n modelId: string\n /** The nearest card element (for route resolution). */\n card: HTMLElement\n}\n\n/** The join the injector renders from. */\nexport interface InjectorDeps {\n /**\n * The settings Remote face for THIS scan/write. The kernel mounts\n * `remote.settings` as an injectable service, so the plugin's own top-level\n * `inject` declaration guarantees it before apply runs \u2014 no runtime seat\n * probing remains.\n */\n api: RemoteApi\n /** Read the pi-ai namespace plus writability from the settings join. */\n describeNamespace(): Promise<SettingsJoin>\n /** Localized copy. */\n t: (key: string, params?: Record<string, string | number>) => string\n /**\n * The official controls' aria-labels in the ACTIVE language. A thunk, not a\n * value: the host resolves labels through the same fallback chain it renders\n * from, so re-reading per scan is what keeps a language switch (and a late\n * language pack) in step with the page it has to find controls on.\n */\n labels(): HostLabels\n /** Mount one editor into a container (React); render() updates its props in place. */\n mount(container: HTMLElement, props: EditorMountProps): MountedEditor\n /**\n * Runs after an idle pass landed everything the session held back. The\n * browser half hangs its autofill complement here: \"the user stopped\n * editing\" is the only moment this side may safely write the document.\n */\n onIdle?(): void\n /**\n * Arm a timed retry after a failed idle pass. The backoff clock only helps\n * if something wakes the injector when it expires; a settled page has no DOM\n * mutation to schedule the next scan. Optional so tests drive passes by hand.\n */\n onBackoff?(delayMs: number): void\n}\n\n/** One mounted editor: unmount disposes the React root; render swaps props in place. */\nexport interface MountedEditor {\n unmount(): void\n render(props: EditorMountProps): void\n}\n\n/** Props handed to the editor mount for one model row. */\nexport interface EditorMountProps {\n /** The route being edited. */\n route: string\n /** Route display name (shown in the editor header). */\n routeDisplayName: string\n /** Route wire protocol, when configured. */\n routeApi?: string\n /** Route endpoint, when configured. */\n routeBaseURL?: string\n /** Model id. */\n modelId: string\n /** Model display name, when one is set. */\n modelName?: string\n /** The model's current reasoningEfforts declaration. */\n efforts?: false | ReasoningEfforts\n /** Whether the saved model explicitly opted out of an effort declaration. */\n effortsUnset?: boolean\n /** The model's current input-modality declaration. */\n input?: InputModalities\n /** The model's stored compat block (passthrough; the editor merges suggestions over it). */\n compat?: CompatSuggestion\n /** The model's stored per-model default-effort pick, when one is set. */\n defaultEffort?: string\n /** Row ordinal among the models found in this scan (for aria labels). */\n index: number\n /** True while the row is unsaved (a create card's draft route, or a new\n * model row on a saved route): Apply stages the declaration instead of\n * writing, and the injector lands it once the row is saved. */\n staged?: boolean\n /**\n * Whether the official Models page owns input types for this row: 0.1.6-alpha.2\n * renders its own ModelInputTypes control inside the disclosure. When true,\n * the editor hides its modality section \u2014 a CAPABILITY sniffed from the row\n * DOM, never a kernel version.\n */\n officialInputTypes?: boolean\n api: EffortEditorApi\n readOnly: boolean\n t: (key: string, params?: Record<string, string | number>) => string\n}\n\n/** Mutable scan state kept across reconcile invocations. */\nexport interface ScanState {\n /** Currently mounted editors, keyed by their container element. */\n mounted: Map<HTMLElement, { editor: MountedEditor; props: EditorMountProps }>\n /** The last describe promise, folded so scans never stack reads. */\n describePromise: Promise<SettingsJoin> | undefined\n /**\n * Declarations staged against routes that do not exist in the settings\n * document yet (the create card's typed route id), keyed route \u2192 model id.\n * Flushed automatically once a route appears. Mirrored into `sessionStorage`\n * so a same-document fiber cycle (HMR, disable-then-enable) keeps the user's\n * mid-edit staging; a real reload discards it (see {@link restoreLedger}).\n */\n pending: Map<string, Map<string, StagedDeclaration>>\n /**\n * Staged models that looked ghosted on the last scan (their route is saved,\n * but no matching row exists on the page anymore). Withdrawn once they miss\n * TWO consecutive scans, so one transient re-render gap cannot drop real\n * mid-edit staging. Mirrors {@link pending}'s key shape.\n */\n missedScans: Map<string, Set<string>>\n /**\n * Writes an ON-SCREEN editor asked for while the official card held the\n * document, keyed route \u2192 model id. Kept verbatim (this is the user's own\n * declaration, so no suggestion arbitration applies) and replayed the\n * moment the card is gone. Persisted beside its commit evidence, so a\n * same-document fiber cycle still lands a write the official Save\n * authorized, while a reload or a dismissed card drops it (see\n * {@link restoreLedger} / {@link forgetRoute}).\n */\n queued: Map<string, Map<string, HeldWrite>>\n /**\n * Whether an official editing card was on the page at the last reconcile.\n * `undefined` until the first scan. The `!== false` test is what makes the\n * idle pass run exactly once per editing session -- and once for a page\n * that loads straight into the models section without ever editing.\n */\n editing: boolean | undefined\n /**\n * Whether an idle pass is in flight. One pass at a time: two overlapping\n * passes would describe the same revision and then fight over it, turning a\n * clean replay into a self-inflicted `settings/conflict`.\n */\n flushing: boolean\n /** Consecutive failed idle passes; the backoff exponent. Reset on success. */\n flushFailures: number\n /** Earliest `Date.now()` at which the next retry may run (0 = any time). */\n nextFlushAt: number\n /**\n * Routes whose OFFICIAL card committed in this session (the user pressed its\n * commit button). The held ledgers land per route: only the routes the user\n * actually saved are written, because the official card's Save is now what\n * commits this plugin's edits too (issue #7 / C2). An intent the user\n * dismissed without saving is dropped, exactly like the card's own fields.\n */\n committing: Set<string>\n /**\n * Routes with a provider-wide adapt in flight (the auto-adapt seat's click).\n * Nothing debounces that click: a second one arriving while the first pass is\n * still describing and suggesting would re-ask every model and re-commit the\n * same bytes, so the route is simply not re-entered until its pass ends.\n */\n adapting: Set<string>\n /**\n * Whether the official action row has failed to yield usable buttons for\n * every card seen so far. When it has, the landing decision degrades to\n * \"the card went away, so write it\" -- writing too much is recoverable,\n * silently losing the user's declaration is not.\n */\n signalsUnavailable: boolean\n /** Commit buttons already wired, so a re-scan never double-registers. */\n submitWired: WeakSet<Element>\n /** Cancel buttons already wired. */\n cancelWired: WeakSet<Element>\n}\n\n/**\n * One staged declaration: the level set plus the suggestion's compat block and\n * modalities. A 'keep' effort travels untouched to the flush write. The\n * per-model default-effort pick (issue #4) rides along: a string to write,\n * null to clear, absent to touch nothing.\n *\n * Design note: staging cannot express a deliberate UNSET. An all-clear draft\n * stages `keep`/no-input, whose flush resolves to null and withdraws the\n * whole staging -- host autofill then fills its suggestion back in. To record\n * \"declare nothing\" durably, save the row first and apply an all-clear write\n * through the saved-row seam (the durable unset marker).\n */\nexport interface StagedDeclaration {\n efforts: Exclude<EffortWriteIntent, undefined>\n compat?: CompatSuggestion\n input?: InputModalities\n defaultEffort?: string | null\n}\n\n/**\n * The held-write ledgers as `sessionStorage` keeps them: the two maps, plus the\n * document identity and the commit evidence a same-document restore needs.\n * Every entry is the user's own declaration, already JSON-shaped, so the file\n * IS the intent (no derivation on the way back in).\n */\ninterface LedgerFile {\n /** Identity of the document that wrote the file; a different one is a reload. */\n document: string\n /**\n * Routes whose held write had COMMIT EVIDENCE when persisted (the user\n * pressed the official Save). A restored queued entry without its route here\n * is an abandoned edit and is dropped, matching the official draft.\n */\n committed: string[]\n pending: [string, [string, StagedDeclaration][]][]\n queued: [string, [string, HeldWrite][]][]\n}\n\n/**\n * The `sessionStorage` ledger key, and the module-local sentinel identifying\n * the CURRENT document across a same-page fiber cycle. The sentinel lives on\n * the window, so a real reload (a fresh window) reads a different id and the\n * old file is discarded; HMR / disable-enable keeps it.\n */\nconst LEDGER_DOCUMENT_KEY = '__breLedgerDocument'\n\n/** The current document's ledger identity, minting one on first use. */\nfunction documentId(): string {\n const holder = globalThis as { [LEDGER_DOCUMENT_KEY]?: string }\n return (holder[LEDGER_DOCUMENT_KEY] ??= `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`)\n}\n\n/**\n * The `sessionStorage` slot holding both ledgers. Session-scoped on purpose:\n * the intents belong to the card session the user was working in, and a NEW\n * tab must not inherit another tab's half-finished edit.\n */\nconst LEDGER_KEY = 'bre:held-writes:v1'\n\n/** `sessionStorage`, or undefined when the environment has none (node, privacy mode). */\nfunction ledgerStorage(): Storage | undefined {\n try {\n return typeof sessionStorage === 'undefined' ? undefined : sessionStorage\n } catch {\n return undefined\n }\n}\n\n/**\n * Mirror both ledgers into `sessionStorage`. Called after every change and\n * best-effort by nature: an unavailable or full storage keeps the ledgers in\n * memory alone (the behaviour before they were persisted), never an error the\n * user has to see.\n */\nfunction persistLedger(state: ScanState): void {\n const store = ledgerStorage()\n if (store === undefined) return\n try {\n if (state.pending.size === 0 && state.queued.size === 0) {\n store.removeItem(LEDGER_KEY)\n return\n }\n const file: LedgerFile = {\n document: documentId(),\n // Only routes that still hold a write matter: a spent marker is never\n // restored (and flushQueued prunes it).\n committed: [...state.committing].filter(route => state.queued.has(route)),\n pending: [...state.pending].map(([route, models]) => [route, [...models]]),\n queued: [...state.queued].map(([route, models]) => [route, [...models]]),\n }\n store.setItem(LEDGER_KEY, JSON.stringify(file))\n } catch {\n // Quota / disabled storage: the in-memory ledgers remain authoritative.\n }\n}\n\n/** One ledger entry as the file carries it, or undefined when malformed. */\nfunction ledgerEntry<T>(value: unknown, validate?: (write: unknown) => boolean): [string, [string, T][]][] | undefined {\n if (!Array.isArray(value)) return undefined\n const routes: [string, [string, T][]][] = []\n for (const row of value) {\n if (!Array.isArray(row) || row.length !== 2) return undefined\n const [route, models] = row as [unknown, unknown]\n if (typeof route !== 'string' || !Array.isArray(models)) return undefined\n const entries: [string, T][] = []\n for (const entry of models) {\n if (!Array.isArray(entry) || entry.length !== 2) return undefined\n const [modelId, write] = entry as [unknown, unknown]\n if (typeof modelId !== 'string' || !isRecord(write)) return undefined\n if (validate !== undefined && !validate(write)) return undefined\n entries.push([modelId, write as T])\n }\n routes.push([route, entries])\n }\n return routes\n}\n\n/**\n * Restore the ledgers from `sessionStorage`.\n *\n * A file written by a PREVIOUS document is a reload: the official card's own\n * draft would have died with it, so both ledgers are discarded outright. A\n * same-document file (HMR / disable-enable) comes back, but a held write is\n * restored ONLY with commit evidence -- an edit the user never saved must not\n * be resurrected behind a later Save. A malformed or foreign file is ignored\n * wholesale rather than partially applied: a half-read intent is worse than a\n * dropped one on a document the user can edit by hand.\n * @param state - the fresh scan state to fill.\n */\nfunction restoreLedger(state: ScanState): void {\n const store = ledgerStorage()\n if (store === undefined) return\n try {\n const raw = store.getItem(LEDGER_KEY)\n if (raw === null) return\n const parsed = JSON.parse(raw) as Partial<LedgerFile>\n if (parsed.document !== documentId()) return\n const pending = ledgerEntry<StagedDeclaration>(parsed.pending)\n const queued = ledgerEntry<HeldWrite>(parsed.queued, isHeldWrite)\n if (pending === undefined || queued === undefined) return\n const committed = Array.isArray(parsed.committed)\n ? parsed.committed.filter((route): route is string => typeof route === 'string')\n : []\n for (const [route, models] of pending) state.pending.set(route, new Map(models))\n for (const [route, models] of queued) {\n // No evidence = the user never saved this route: drop it.\n if (!committed.includes(route)) continue\n state.queued.set(route, new Map(models))\n state.committing.add(route)\n }\n } catch {\n // Unreadable file: start clean.\n }\n}\n\n/**\n * Land whatever the session held back, best effort, when the fiber is about to\n * go away (plugin disable / HMR). The ledgers STAY in `sessionStorage` (with\n * their commit evidence), so the next fiber in the same document picks the work\n * up; a flush the runtime cuts short is simply retried by that fiber.\n * @param state - the scan state whose ledgers to drain.\n * @param deps - the injection dependencies.\n */\nexport function flushOnTeardown(state: ScanState, deps: InjectorDeps): void {\n void (async () => {\n try {\n await flushQueued(deps, state)\n await flushPending(deps, state)\n } catch (error) {\n console.error(`[bre] teardown flush failed: ${error instanceof Error ? error.message : String(error)}`)\n }\n })()\n}\n\n/**\n * The page is going away: land the committed intents best effort, then CLEAR\n * the ledger. Unlike a fiber cycle, a reload discards the official card's own\n * draft, so the plugin's must go too -- anything the cut-short flush could not\n * land must not be resurrected by the next document. (The window sentinel\n * covers the case pagehide never fires: crash / plugin already disabled.)\n * @param state - the scan state whose ledgers to drain.\n * @param deps - the injection dependencies.\n */\nexport function flushOnUnload(state: ScanState, deps: InjectorDeps): void {\n // The page is going away: an open card's frozen baseline no longer matters,\n // so the in-flight fence must not block this last best-effort landing.\n void (async () => {\n try {\n await flushQueued(deps, state, true)\n await flushPending(deps, state, true)\n } catch (error) {\n console.error(`[bre] unload flush failed: ${error instanceof Error ? error.message : String(error)}`)\n } finally {\n try {\n ledgerStorage()?.removeItem(LEDGER_KEY)\n } catch {\n // Disabled storage: nothing to clear.\n }\n }\n })()\n}\n\nexport function createScanState(): ScanState {\n const state: ScanState = {\n mounted: new Map(),\n describePromise: undefined,\n pending: new Map(),\n missedScans: new Map(),\n queued: new Map(),\n editing: undefined,\n flushing: false,\n flushFailures: 0,\n nextFlushAt: 0,\n committing: new Set(),\n adapting: new Set(),\n signalsUnavailable: false,\n submitWired: new WeakSet(),\n cancelWired: new WeakSet(),\n }\n restoreLedger(state)\n return state\n}\n\n/**\n * Land everything the session held back, one pass at a time.\n *\n * A refusal keeps its intent in the ledger and pushes the next attempt out on\n * a backoff, so a settled document is not hammered while a genuinely stuck\n * write (a refused compat key, a vanished route) is retried indefinitely\n * instead of being lost on the first failure.\n * @param deps - the injection dependencies.\n * @param state - mutable scan state.\n */\nasync function runIdlePass(deps: InjectorDeps, state: ScanState): Promise<void> {\n if (state.flushing) return\n state.flushing = true\n try {\n const queuedFailed = await flushQueued(deps, state)\n const pendingFailed = await flushPending(deps, state)\n if (queuedFailed || pendingFailed) {\n // A refusal arrives as a VALUE, not a throw: folding it into the same\n // backoff keeps a doomed route from being re-mutated on every scan, and\n // puts both failure shapes (refused, unreachable) on one clock.\n armBackoff(deps, state)\n } else {\n state.flushFailures = 0\n state.nextFlushAt = 0\n }\n } catch (error) {\n armBackoff(deps, state)\n console.error(`[bre] idle flush failed: ${error instanceof Error ? error.message : String(error)}`)\n } finally {\n state.flushing = false\n }\n // The hook is a courtesy seat for the browser half's autofill: a fault in\n // it must not reject the pass (the caller void-s the promise) and become an\n // unhandled rejection.\n try {\n deps.onIdle?.()\n } catch (error) {\n console.error(`[bre] idle hook failed: ${error instanceof Error ? error.message : String(error)}`)\n }\n}\n\n/**\n * Push the next attempt out on the exponential backoff and tell the host to\n * wake the injector when it expires (a settled page has no DOM mutation).\n */\nfunction armBackoff(deps: InjectorDeps, state: ScanState): void {\n state.flushFailures += 1\n const delay = Math.min(2 ** state.flushFailures * 500, 30_000)\n state.nextFlushAt = Date.now() + delay\n deps.onBackoff?.(delay)\n}\n\n/** Whether either ledger still holds work the idle pass has to land. */\nfunction hasOutstanding(state: ScanState): boolean {\n return state.queued.size > 0 || state.pending.size > 0\n}\n\n/**\n * Record (or, for `undefined`, withdraw) one staged declaration. A route with\n * no staged models left leaves the store entirely.\n */\nexport function stageEffortsInto(\n state: ScanState,\n route: string,\n modelId: string,\n efforts: EffortWriteIntent,\n compat?: CompatSuggestion,\n /**\n * The modality part. `null` is a deliberate unset (the editor's \"clear\n * declaration\" on an unsaved route) and stores as such: the staged flush\n * reads it back verbatim, exactly like the saved-row seam does.\n */\n input?: InputModalities | null,\n defaultEffort?: string | null,\n): void {\n try {\n const models = state.pending.get(route)\n if (efforts === undefined) {\n if (models === undefined) return\n models.delete(modelId)\n if (models.size === 0) state.pending.delete(route)\n return\n }\n // 'keep' is storable: a modality-only staging must survive the flush as a\n // declaration that touches everything EXCEPT the ladder.\n state.pending.set(route, (models ?? new Map()).set(modelId, {\n efforts,\n ...(compat === undefined ? {} : { compat }),\n ...(input === undefined ? {} : { input }),\n ...(defaultEffort === undefined ? {} : { defaultEffort }),\n }))\n } finally {\n // Both arms changed the ledger (or provably did not): persisting here\n // keeps the early returns from having to remember it.\n persistLedger(state)\n }\n}\n\n/**\n * Record (or replace) the write an on-screen editor asked for while the\n * official card held the document. The latest intent for a row wins: the user\n * may apply twice before closing the card.\n * @param state - mutable scan state.\n * @param route - the route being edited.\n * @param modelId - the model id being edited.\n * @param write - the full intent, kept verbatim for the replay.\n */\nexport function queueWriteInto(state: ScanState, route: string, modelId: string, write: HeldWrite): void {\n state.queued.set(route, (state.queued.get(route) ?? new Map()).set(modelId, write))\n persistLedger(state)\n}\n\n/**\n * Withdraw one row's staged intent (the create card's erase-that-row flow, and\n * the editor's Reset on a route the document does not hold yet). Idempotent,\n * and it clears the route entry once its last model is gone.\n * @param state - mutable scan state.\n * @param route - the route the staging was made against.\n * @param modelId - the model whose intent to drop.\n */\nexport function withdrawStaged(state: ScanState, route: string, modelId: string): void {\n const models = state.pending.get(route)\n if (models === undefined) return\n models.delete(modelId)\n if (models.size === 0) state.pending.delete(route)\n persistLedger(state)\n}\n\n/**\n * Withdraw one row's held intent (the editor's own Reset, or any later\n * \"discard this edit\"). Idempotent, and it clears the route entry once its\n * last model is gone.\n * @param state - mutable scan state.\n * @param route - the route the editor was editing.\n * @param modelId - the model whose intent to drop.\n */\nexport function withdrawHeldWrite(state: ScanState, route: string, modelId: string): void {\n const models = state.queued.get(route)\n if (models === undefined) return\n models.delete(modelId)\n if (models.size === 0) state.queued.delete(route)\n persistLedger(state)\n}\n\n/**\n * Withdraw a row's intent wherever it landed.\n *\n * The editor's Reset is the caller and it does not know which ledger its own\n * `commit` fed \u2014 the create card stages, a saved row queues, and the same row\n * can move between the two as the user edits the route id. Discarding only one\n * of them would leave the Reset cosmetic on the other: the official Save would\n * still write edits the user explicitly threw away.\n * @param state - mutable scan state.\n * @param route - the route the editor was editing.\n * @param modelId - the model whose intent to drop.\n */\nexport function withdrawIntent(state: ScanState, route: string, modelId: string): void {\n withdrawStaged(state, route, modelId)\n withdrawHeldWrite(state, route, modelId)\n}\n\n/**\n * Drop every intent one route holds, right now: the official card's cancel.\n *\n * The card's own fields die with the dismissal, so the plugin's must too -- and\n * they must die at the CLICK, not on some later scan. A deferred \"discarded\"\n * marker would outlive the card that set it (a dismiss carrying no plugin edit\n * leaves nothing for a later pass to drain), and the next card for that same\n * route would then be read as already-dismissed: its Save would never be\n * wired, and the user's fresh edit would be dropped silently. Clearing the\n * ledgers here leaves no state at all to misinterpret -- including the staged\n * declaration a dismissed CREATE card was holding for a route that does not\n * exist yet, which would otherwise land the moment that route appeared.\n *\n * Safe by construction: the official page keeps ONE editing card at a time\n * (create and edit mutually exclusive), so the route this resolves is the only\n * card that could own these entries.\n * @param state - mutable scan state.\n * @param route - the route whose official card was dismissed.\n */\nexport function forgetRoute(state: ScanState, route: string): void {\n state.queued.delete(route)\n state.pending.delete(route)\n state.missedScans.delete(route)\n state.committing.delete(route)\n persistLedger(state)\n}\n\n/**\n * The write intents one staged declaration survives against the SAVED row,\n * decided PER PART: staging never overwrites what the document already says,\n * but a part the document took over must not silence its sibling. Returns\n * null when nothing is left to write (the model vanished from the card, or\n * every part was taken over / is a keep) -- the caller withdraws the staging.\n */\nexport interface EffectiveStagedIntents {\n efforts: EffortWriteIntent\n compat?: CompatSuggestion\n input?: InputModalities\n /** The staged default-effort pick, when the staging carried one. */\n defaultEffort?: string | null\n /**\n * Compat fields the edit OWNS and left empty, so \"unset\" can mean unset\n * instead of keeping the last choice forever. Computed by the editor's own\n * `clearedCompatKeys` against the route's protocol, never against the whole\n * stored block: a hand-tuned field the editor never showed survives.\n *\n * A STAGED flush never sets this. Staging carries a ladder and a modality,\n * not a compat decision, and the host autofill writes the knowledge base's\n * compat block into the very row it fills -- so clearing here would delete\n * the suggestion the row is supposed to start from (the same \"the plugin's\n * own proposal is not a user decision\" rule the ladder follows).\n */\n clearCompatKeys?: readonly string[]\n}\n\nexport function effectiveStagedIntents(\n declaration: StagedDeclaration,\n current: Record<string, unknown> | undefined,\n autofill?: AutofillFootprint,\n): EffectiveStagedIntents | null {\n if (current === undefined) return null\n // Ladder part: a declaration or a deliberate-unset marker on the row owns\n // it \u2014 EXCEPT when the stored declaration is this plugin's own host autofill\n // (the knowledge base proposal it lays down in the route-creation window).\n // That is a suggestion, not a user decision, so a staged intent outranks it;\n // anything else (a hand-tuned ladder, a marker) is a real takeover. 'keep'\n // means the staging never carried the ladder anyway.\n //\n // Provenance first: a ladder carrying the host autofill's marker IS the\n // knowledge base's proposal, whatever its bytes are. The host fills\n // in-process the instant a provider is committed, so by the time this flush\n // runs its own suggestion is already in the document \u2014 byte equality alone\n // then reads the user's staged intent as a document takeover and silently\n // drops it (the reported \"configured it on the card, saved, and it is gone\").\n const ladderMarked =\n current[UNSET_MARKER] !== true\n && current['reasoningEfforts'] !== undefined\n && typeof current[AUTOFILL_MARKER] === 'number'\n const ladderAutofilled =\n ladderMarked\n || (current[UNSET_MARKER] !== true\n && autofill?.efforts !== undefined\n && sameEffortsValue(current['reasoningEfforts'], autofill.efforts))\n const ladderTaken = ladderAutofilled\n ? false\n : current['reasoningEfforts'] !== undefined\n || current[UNSET_MARKER] === true\n || declaration.efforts === 'keep'\n // Modality part: the same provenance rule, decided per part. The knowledge\n // base disclosure the autofill wrote does not answer over a staged choice,\n // while a disclosure that differs from the footprint is a hand-made one and\n // stands. A ladder-only 'keep' staging leaves the stored modality alone, just\n // as before \u2014 the staging never carried a modality in that case.\n const inputAutofilled =\n current[INPUT_UNSET_MARKER] !== true\n && autofill?.input !== undefined\n && sameInputList(current['input'], autofill.input)\n const inputTaken = inputAutofilled\n ? false\n : (Array.isArray(current['input']) && current['input'].length > 0)\n || current[INPUT_UNSET_MARKER] === true\n const efforts: EffortWriteIntent = ladderTaken ? 'keep' : declaration.efforts\n const input = inputTaken ? undefined : declaration.input\n // The default-effort pick has no takeover question: host autofill never\n // writes this field, so whatever the document holds is a user decision and\n // the staged intent (write or clear) passes through untouched.\n const defaultEffort = declaration.defaultEffort\n if (efforts === 'keep' && input === undefined && defaultEffort === undefined) return null\n return {\n efforts,\n ...(declaration.compat === undefined ? {} : { compat: declaration.compat }),\n ...(input === undefined ? {} : { input }),\n ...(defaultEffort === undefined ? {} : { defaultEffort }),\n }\n}\n\n/** What this plugin's host autofill would write for one model, as the flush compares it. */\nexport interface AutofillFootprint {\n efforts?: ReasoningEfforts | false\n input?: InputModalities\n}\n\n/** Semantic equality of two reasoningEfforts dict values (key set + wire strings). */\nfunction sameEffortsValue(a: unknown, b: ReasoningEfforts | false): boolean {\n if (a === b) return true\n if (!isRecord(a) || !isRecord(b)) return false\n const keys = Object.keys(b)\n if (Object.keys(a).length !== keys.length) return false\n const other = b as Record<string, unknown>\n return keys.every(key => a[key] === other[key])\n}\n\n/** Semantic equality of a raw input value with a modality list (order-insensitive). */\nfunction sameInputList(a: unknown, b: InputModalities): boolean {\n if (!Array.isArray(a) || a.length !== b.length) return false\n const set = new Set<string>(b)\n return a.every(member => typeof member === 'string' && set.has(member))\n}\n\n/**\n * Shape guard for a restored held write. The ledger is `sessionStorage`, so a\n * foreign or half-written file must be rejected before its entries reach\n * `writeModelRows` (a schema-level refusal there is noisy and late).\n */\nfunction isHeldWrite(value: unknown): value is HeldWrite {\n if (!isRecord(value)) return false\n const efforts = value['efforts']\n if (efforts !== false && efforts !== 'keep' && !isRecord(efforts)) return false\n const clear = value['clearCompatKeys']\n return clear === undefined || (Array.isArray(clear) && clear.every(key => typeof key === 'string'))\n}\n\n/**\n * Flush staged declarations for routes that now exist, writing each model's\n * declaration through the live write seam. A model the saved profile does not\n * carry yet keeps its staging (the row may still be mid-creation -- the user\n * edits the card after staging -- and the declaration lands whenever the row\n * first appears); each PART is decided against the saved row via\n * {@link effectiveStagedIntents}, with the host autofill's knowledge-base\n * footprint passed in so the plugin's own proposal never outranks a staged\n * user intent. A write that still fails (conflict retry exhausted) stays\n * staged; the write's own document-updated invalidation re-scans and\n * re-flushes it.\n * @returns whether a write of this route was refused and left unlanded.\n */\nasync function flushRoute(\n deps: InjectorDeps,\n state: ScanState,\n route: string,\n models: ReadonlyMap<string, StagedDeclaration>,\n ignoreFence = false,\n): Promise<boolean> {\n // ONE read for the whole route: the rows all live in the same array, so a\n // per-model read was pure repetition. Every arbitration below still runs\n // per model against this same snapshot -- the write is one whole-array set.\n const join = await deps.describeNamespace()\n const providers = providersOf(join.namespace)\n // Snapshot: the loop below withdraws entries whose edit proved empty, and\n // the batch then reports back per model as its results land.\n const intents: RowIntent[] = []\n for (const [modelId, declaration] of [...models]) {\n const current = modelsOf(providers, route).find(model => model['id'] === modelId)\n // The row is not saved yet (mid-edit card, or the user renamed it):\n // keep the staging instead of dropping it -- it lands when a row with\n // this id first appears; the scan's ghost pass withdraws staging whose\n // row is provably gone.\n if (current === undefined) continue\n // A concurrent scan may have withdrawn this staging while our read was\n // in flight (the two-scan ghost pass): writing it anyway would resurrect\n // a declaration nothing on the page owns anymore.\n if (state.pending.get(route)?.get(modelId) !== declaration) continue\n // Mirror the host autofill's suggestion for this exact row (same facts,\n // same knowledge base) so {@link effectiveStagedIntents} can tell the\n // plugin's own proposal apart from a user decision.\n const routeInfo = routeFactsOf(providers, route)\n const name = typeof current['name'] === 'string' ? current['name'] : undefined\n const suggestion = suggestEfforts(modelId, name === undefined ? routeInfo : { ...routeInfo, displayName: name })\n const effective = effectiveStagedIntents(declaration, current, {\n ...(suggestion.efforts === undefined ? {} : { efforts: suggestion.efforts }),\n ...(suggestion.input === undefined ? {} : { input: suggestion.input }),\n })\n if (effective === null) {\n stageEffortsInto(state, route, modelId, undefined)\n continue\n }\n intents.push({\n modelId,\n efforts: effective.efforts,\n ...(effective.compat === undefined ? {} : { compat: effective.compat }),\n ...(effective.input === undefined ? {} : { input: effective.input }),\n ...(effective.defaultEffort === undefined ? {} : { defaultEffort: effective.defaultEffort }),\n })\n }\n if (intents.length === 0) return false\n // Seed the write's FIRST read with the snapshot this pass already took: one\n // read per route instead of two. A conflict retry re-describes fresh through\n // the live seam, so the seed never costs the write its recovery path.\n let seeded = false\n const results = await writeModelRows(deps.api, route, intents, () => {\n if (!seeded) {\n seeded = true\n return Promise.resolve(join)\n }\n return deps.describeNamespace()\n }, ignoreFence ? undefined : officialCardOpen)\n let failed = false\n intents.forEach((intent, at) => {\n const result = results[at]\n // A card opened during the read: keep the staging, do not back off.\n if (result?.aborted === true) return\n if (result?.ok === true || result?.modelNotFound === true) {\n stageEffortsInto(state, route, intent.modelId, undefined)\n return\n }\n // Anything else keeps the staging (the next scan retries it), but a\n // silent keep is unobservable -- surface the failure for diagnostics, and\n // report it so the pass can back off instead of hammering.\n failed = true\n console.error(`[bre] staged flush write failed for \"${route}\"/\"${intent.modelId}\": ${result?.error ?? 'unknown'}`)\n })\n return failed\n}\n\n/**\n * Replay every editor-held write through a HOLDER-LESS seam, so the very call\n * that would have fought the official card now commits. A row that vanished\n * meanwhile drops its entry instead of retrying forever; any other failure\n * stays queued for the next idle pass.\n *\n * The LANDING DECISION lives here (issue #7 / C2), per route, because this\n * ledger is the one the official card's Save governs:\n * - the user committed -> write it, now that the card is gone;\n * - the official action row was never readable in this session -> the signal\n * cannot be trusted, so degrade to \"the card went away, write it\": writing\n * too much is recoverable, losing the declaration is not;\n * - otherwise the card closed with neither signal (the user just walked\n * away) -> the intent waits rather than landing behind a frozen baseline.\n *\n * A DISMISSED card has no branch here on purpose: its cancel clears the ledger\n * the moment it is pressed ({@link forgetRoute}), so \"dismissed\" can never\n * outlive the card it belonged to and swallow a later save of the same route.\n * @param deps - the injection dependencies.\n * @param state - mutable scan state.\n * @returns whether this pass left a write unlanded (the caller backs off).\n */\nasync function flushQueued(deps: InjectorDeps, state: ScanState, ignoreFence = false): Promise<boolean> {\n // A commit marker with nothing queued is SPENT: the official Save carried no\n // plugin edit (or the route was already landed). Dropping it here keeps it\n // from authorizing a later edit the user never saved.\n for (const route of [...state.committing]) {\n if (!state.queued.has(route)) state.committing.delete(route)\n }\n // Writability is the document's call, not the commit signal's: a memory /\n // non-loopback page refuses writes, and the held ledger obeys the same gate\n // the staged ledger already does. Wait for a landable route before paying\n // for the read, so a page whose intents are all uncommitted costs nothing.\n const landable = [...state.queued.keys()]\n .some(route => state.committing.has(route) || state.signalsUnavailable)\n if (!landable) {\n persistLedger(state)\n return false\n }\n const join = await deps.describeNamespace()\n if (join.writable !== true) {\n persistLedger(state)\n return false\n }\n let failed = false\n for (const [route, models] of [...state.queued]) {\n // The staged ledger has no official row whose Save could commit it; this\n // one does, so an uncommitted route waits instead of landing behind that\n // card's frozen revision baseline.\n const committed = state.committing.has(route)\n if (!committed && !state.signalsUnavailable) continue\n // One read, one mutate for the whole route: the held intents are per model\n // but the document is a single models array, so a per-model write was\n // rebuilding and rewriting that same array N times.\n const intents: RowIntent[] = [...models].map(([modelId, write]) => ({\n modelId,\n efforts: write.efforts,\n ...(write.compat === undefined ? {} : { compat: write.compat }),\n ...(write.input === undefined ? {} : { input: write.input }),\n ...(write.clearCompatKeys === undefined ? {} : { clearCompatKeys: write.clearCompatKeys }),\n ...(write.defaultEffort === undefined ? {} : { defaultEffort: write.defaultEffort }),\n }))\n const results = await writeModelRows(deps.api, route, intents, undefined, ignoreFence ? undefined : officialCardOpen)\n let aborted = false\n intents.forEach((intent, at) => {\n const result = results[at]\n // A card opened during the read: keep every intent, do not back off.\n if (result?.aborted === true) { aborted = true; return }\n if (result?.ok === true || result?.modelNotFound === true) {\n models.delete(intent.modelId)\n return\n }\n console.error(`[bre] held write failed for \"${route}\"/\"${intent.modelId}\": ${result?.error ?? 'unknown'}`)\n })\n if (models.size === 0) {\n state.queued.delete(route)\n // The marker is what authorizes the landing, so it is dropped only once\n // every intent of the route made it -- a refusal keeps both the intent\n // and the authority to retry it on the next pass.\n state.committing.delete(route)\n } else if (!aborted) {\n failed = true\n }\n }\n // The queue just shrank (landed intents) or stayed as it was (a route still\n // waiting for its card): either way the stored file must match what is left.\n persistLedger(state)\n return failed\n}\n\n/**\n * Land the create-card declarations whose route has appeared. Each route goes\n * through its own live describe, so one refused route cannot poison a sibling\n * that would have landed.\n * @param deps - the injection dependencies.\n * @param state - mutable scan state.\n * @returns whether a write was refused and left unlanded.\n */\nasync function flushPending(deps: InjectorDeps, state: ScanState, ignoreFence = false): Promise<boolean> {\n if (state.pending.size === 0) return false\n const join = await deps.describeNamespace()\n if (join.writable !== true) return false\n const providers = providersOf(join.namespace)\n let failed = false\n for (const [route, models] of [...state.pending]) {\n if (models.size === 0) {\n state.pending.delete(route)\n continue\n }\n // A create card still owns a route the document has not taken yet.\n if (!hasOwn(providers, route)) continue\n if (await flushRoute(deps, state, route, models, ignoreFence)) failed = true\n }\n persistLedger(state)\n return failed\n}\n\n/**\n * Find the first input/select whose aria-label starts with one of the labels.\n * Exported because the model-order pass reads a row's id and display name the\n * same way the editor reads them: by the official label stem, never by\n * position (the label's trailing number IS the position).\n */\nexport function inputValueByLabel(card: HTMLElement, labels: readonly string[]): string {\n for (const label of labels) {\n const input = Array.from(card.querySelectorAll<HTMLInputElement | HTMLSelectElement>('input[aria-label], select[aria-label]'))\n .find(candidate => (candidate.getAttribute('aria-label') ?? '').startsWith(label))\n if (input !== undefined && input.value.trim().length > 0) return input.value.trim()\n }\n return ''\n}\n\n/** The nearest editor card element of a trigger. */\nfunction cardOf(trigger: Element): HTMLElement | undefined {\n return trigger.closest<HTMLElement>('[class*=\"editor\"], [class*=\"rowCard\"], [class*=\"addCard\"]') ?? undefined\n}\n\n/**\n * The official per-row disclosure container (holds the capacity fields), or\n * undefined while that row is COLLAPSED: the official editor renders the\n * container only for an expanded row, so an untouched provider shows nothing\n * for its models to mount into.\n */\nfunction disclosureOf(trigger: Element): HTMLElement | undefined {\n // The disclosure lives inside the trigger's own model row, not elsewhere in\n // the card: scoping to the row keeps each trigger's container distinct even\n // when several rows are expanded at once.\n const row = trigger.closest<HTMLElement>('[class*=\"modelEntry\"]')\n if (row === null) {\n // Fall back to the card scope (stale or unusual structure).\n const card = cardOf(trigger)\n if (card === undefined) return undefined\n const candidates = Array.from(card.querySelectorAll<HTMLElement>('[class*=\"modelAdvanced\"]'))\n return candidates.find(candidate => candidate.offsetParent !== null) ?? candidates[0]\n }\n const candidates = Array.from(row.querySelectorAll<HTMLElement>('[class*=\"modelAdvanced\"]'))\n return candidates.find(candidate => candidate.offsetParent !== null) ?? candidates[0]\n}\n\n/**\n * Whether the official page ships its own input-types editor inside this\n * row's disclosure (0.1.6-alpha.2's `ModelInputTypes` fieldset). This is the\n * CAPABILITY signal the plugin sniffs to stand its own modality section down:\n * a DOM fact, matched by the official class-name stem the same way the\n * `modelAdvanced` anchor is \u2014 never a kernel version number.\n */\nfunction officialInputTypesOf(container: HTMLElement): boolean {\n return container.querySelector('[class*=\"modelInputTypes\"]') !== null\n}\n\n/**\n * One auto-adapt seat per card that has models on screen: the card's own\n * catalogue, paired with the route that card edits. Deduped by card, because\n * every model row of a card shares the one catalogue head.\n */\n/**\n * Seat the auto-adapt control on an open card whose model list is empty.\n *\n * The host keeps the catalogue head (\u83B7\u53D6\u53EF\u7528\u6A21\u578B) after the last row is\n * deleted. The seat has to stay beside that link, or the link jumps. A card\n * whose route cannot be read keeps whatever seat the previous pass left: a\n * settings read that has not landed yet must not flash the control away.\n */\nfunction seatEmptyCatalog(\n root: HTMLElement,\n deps: InjectorDeps,\n state: ScanState,\n join: SettingsJoin,\n labels: HostLabels,\n): void {\n if (!root.isConnected) return\n if (officialCardOf(root) === undefined) return\n const stillEmpty = !Array.from(root.querySelectorAll<HTMLButtonElement>('button[aria-label]'))\n .some(button => labels.capacity.some(label => (button.getAttribute('aria-label') ?? '').startsWith(label)))\n if (!stillEmpty) return\n reconcileAutoEffortSeats(root, emptyCatalogTargets(root, providersOf(join.namespace), labels), {\n t: deps.t,\n onRequest: route => adaptEveryModel(state, deps, route),\n })\n}\n\n/** One seat per catalogue whose card still has a head and a readable route. */\nfunction emptyCatalogTargets(\n root: HTMLElement,\n providers: Record<string, Record<string, unknown>>,\n labels: HostLabels,\n): AutoEffortSeatTarget[] {\n const targets: AutoEffortSeatTarget[] = []\n // The CONTAINERS, not every element whose class name carries the stem: the\n // heading, the title and the meta are matches too, and seating inside them\n // collapses the official heading to one character per line. See\n // {@link cataloguesOf}.\n for (const catalogue of cataloguesOf(root)) {\n const card = cardOf(catalogue)\n if (card === undefined) continue\n const resolved = routeOfCard(card, providers, labels)\n if (resolved === undefined || resolved.staged) continue\n targets.push({ catalogue, route: resolved.route })\n }\n return targets\n}\n\nfunction autoEffortTargets(\n found: readonly FoundModel[],\n providers: Record<string, Record<string, unknown>>,\n labels: HostLabels,\n): AutoEffortSeatTarget[] {\n const targets: AutoEffortSeatTarget[] = []\n const seen = new Set<HTMLElement>()\n for (const model of found) {\n if (seen.has(model.card)) continue\n seen.add(model.card)\n const resolved = routeOfCard(model.card, providers, labels)\n if (resolved === undefined) continue\n const [catalogue] = cataloguesOf(model.card)\n if (catalogue === undefined) continue\n targets.push({ catalogue, route: resolved.route })\n }\n return targets\n}\n\n/**\n * Adapt every model of one provider, the rows the user never unfolded\n * included.\n *\n * The seat's click reaches the per-row editors through a document event, but\n * an editor only exists for a row with a disclosure container, and a collapsed\n * row renders none \u2014 so the visible answer is structurally incomplete. This\n * routine answers from the settings DOCUMENT instead of the DOM: every model\n * the provider declares goes through the same suggestion the row's own\n * auto-adapt would have asked for, is mapped by the same pure functions\n * ({@link ladderIntent}, {@link compatDraftOf}, {@link pendingWriteOf}) so both\n * paths write identical bytes, and lands by the same rule \u2014 nothing is written\n * while an official card holds the document; the write is held on the route\n * and the card's own Save commits it (issue #7 / C2).\n *\n * An expanded row answers for itself when its editor is mounted. A row the\n * user already configured (a stored ladder, an explicit \"does not reason\",\n * or a deliberate unset) is left alone \u2014 bulk adapt must not overwrite that.\n * A row that is expanded but still has no thinking-level declaration is not\n * configured: this walk adapts it too. Two writers never share one row,\n * because a configured expanded row is skipped here and an unconfigured one\n * has no in-flight ladder draft to protect.\n * @param state - mutable scan state: the adapting guard and the held ledger.\n * @param deps - the injection dependencies.\n * @param route - the provider route whose models are adapted.\n * @returns what the pass did, so the seat can say something true about the\n * click: how many models it held an adaptation for, how many had no\n * suggestion, and -- when nothing could be adapted at all -- why.\n */\nexport async function adaptEveryModel(state: ScanState, deps: InjectorDeps, route: string): Promise<AutoAdaptReport> {\n // A second click while a pass is in flight is not a no-op the user should\n // read as success: 'busy' travels back so the seat keeps the verdict the\n // running pass is about to publish.\n if (route.length === 0) return { held: 0, unsuggested: 0, blocked: 'unknown-route' }\n if (state.adapting.has(route)) return { held: 0, unsuggested: 0, blocked: 'busy' }\n state.adapting.add(route)\n let held = 0\n let unsuggested = 0\n try {\n // One read for the whole run: `suggest` only ever READS the namespace here\n // (its endpoint probe is a read-only GET, it never resolves a credential),\n // the holds below change nothing the read depends on, and every model of\n // the route needs the same provider facts anyway.\n let join: Promise<SettingsJoin> | undefined\n const describe = (): Promise<SettingsJoin> => (join ??= deps.describeNamespace())\n const seen = await describe()\n const providers = providersOf(seen.namespace)\n // A read-only page and a route that is not in the document yet (a create\n // card's typed id) have nothing to adapt: the card's own rows stage their\n // work and it lands with the route. Both are reported as blocked, never as\n // \"already configured\" -- the user would read that as a completed pass.\n if (seen.writable !== true) return { held: 0, unsuggested: 0, blocked: 'unwritable' }\n if (!hasOwn(providers, route)) return { held: 0, unsuggested: 0, blocked: 'unknown-route' }\n const models = modelsOf(providers, route)\n if (models.length === 0) return { held: 0, unsuggested: 0, blocked: 'no-models' }\n const profile = providers[route] ?? {}\n const routeApi = typeof profile['api'] === 'string' ? profile['api'] as string : undefined\n // Expanded rows whose stored ladder is already a decision. An expanded row\n // with no declaration is NOT covered: the user asked bulk adapt to fill\n // exactly those, the same as a collapsed row.\n const covered = new Set<string>()\n for (const entry of state.mounted.values()) {\n if (entry.props.route !== route) continue\n if (entry.props.efforts !== undefined) covered.add(entry.props.modelId)\n }\n const api = createEditorApi(\n deps.api,\n describe,\n // No stage arm: this routine only runs while the page is up, and a write\n // made then is always a HOLD that the official Save lands.\n undefined,\n (heldRoute, modelId, write) => { queueWriteInto(state, heldRoute, modelId, write) },\n )\n for (const model of models) {\n const modelId = typeof model['id'] === 'string' ? model['id'] : ''\n if (modelId.length === 0 || covered.has(modelId)) continue\n const initialEfforts = effortsOf(models, modelId)\n // A collapsed row has no editor, so the document is the only record.\n // Same rule as the expanded skip above: a stored ladder, `false`, or the\n // durable unset marker is a decision and must survive this click.\n if (initialEfforts !== undefined || model[UNSET_MARKER] === true) continue\n const initialInput = inputOf(models, modelId)\n const initialCompat = compatOf(models, modelId)\n const initialDefault = defaultEffortOf(models, modelId) ?? ''\n const reply = await api.suggest(route, modelId, nameOf(models, modelId))\n // 'no-suggestion' is the normal outcome for a model with no knowledge\n // match (the seat is a bulk convenience, not a promise): leave that row\n // exactly as it is, but COUNT it -- a pass that matched nothing at all\n // must not be indistinguishable from a provider that needs nothing.\n if (!reply.ok) {\n unsuggested += 1\n continue\n }\n const parts = reply.suggestion\n const nextThinking = parts.efforts !== false\n // The draft a fresh editor would hold, mapped exactly as its own commit\n // maps it: an untouched ladder and an untouched modality stay out of the\n // write rather than stamping an unset marker onto the row. The vendor\n // default travels the same way the row's own auto-adapt does: a fresh\n // editor starts from the stored pick and only reports a change.\n const nextModality = parts.input === undefined ? modalityFrom(initialInput) : modalityFrom(parts.input)\n const nextDefault = parts.defaultEffort ?? initialDefault\n const defaultEffort = nextDefault === initialDefault\n ? undefined\n : nextDefault === '' ? null : nextDefault\n api.commit(route, modelId, pendingWriteOf({\n efforts: ladderIntent(nextThinking, buildIntent(draftFrom(parts.efforts)), initialEfforts),\n input: sameModality(nextModality, initialInput) ? undefined : buildModalityIntent(nextModality),\n defaultEffort,\n manualCompat: compatDraftOf(initialCompat),\n appliedCompat: parts.compat,\n routeApi,\n initialCompat,\n }))\n held += 1\n }\n return { held, unsuggested }\n } catch (error) {\n // The seat has no message area of its own, and one row's refusal must not\n // strand the rows behind it: report, keep whatever holds already landed.\n console.warn('[bre] provider-wide adapt failed:', error)\n return { held, unsuggested, failed: error instanceof Error ? error.message : String(error) }\n } finally {\n state.adapting.delete(route)\n }\n}\n\n/** Whether an editor is already mounted in a container (idempotency guard). */\nfunction hasEditor(container: HTMLElement): boolean {\n return container.querySelector(`[data-plugin=\"${PLUGIN_ID}\"]`) !== null\n}\n\n/**\n * Semantic equality of two mount-prop sets, so a scan only re-renders an\n * existing editor when the settings document actually moved under it. The\n * guard is what makes refresh safe under the MutationObserver: render mutates\n * DOM, DOM mutations schedule scans, and a no-op comparison ends the cycle.\n */\nfunction sameProps(a: EditorMountProps, b: EditorMountProps): boolean {\n return a.route === b.route\n && a.routeDisplayName === b.routeDisplayName\n && a.routeApi === b.routeApi\n && a.routeBaseURL === b.routeBaseURL\n && a.modelId === b.modelId\n && a.modelName === b.modelName\n && a.index === b.index\n && a.readOnly === b.readOnly\n && a.defaultEffort === b.defaultEffort\n // A create card's container surviving its own save must flip the editor\n // to write mode: staged changes the Apply button's whole contract, so it\n // participates in the diff like any other prop.\n && a.staged === b.staged\n // The official capability can appear/disappear across an HMR or host\n // update; the modality section must follow it.\n && a.officialInputTypes === b.officialInputTypes\n && a.effortsUnset === b.effortsUnset\n && sameEfforts(a.efforts, b.efforts)\n && sameInput(a.input, b.input)\n && sameCompat(a.compat, b.compat)\n}\n\n/** Semantic equality of two compat blocks (key-order-insensitive). */\nfunction sameCompat(a: CompatSuggestion | undefined, b: CompatSuggestion | undefined): boolean {\n if (a === b) return true\n if (a === undefined || b === undefined) return false\n return JSON.stringify({ ...a }, Object.keys({ ...a, ...b }).sort()) === JSON.stringify({ ...b }, Object.keys({ ...a, ...b }).sort())\n}\n\n/** Semantic equality of two modality declarations (order-insensitive). */\nfunction sameInput(a: InputModalities | undefined, b: InputModalities | undefined): boolean {\n if (a === b) return true\n if (a === undefined || b === undefined) return false\n const setA = new Set(a)\n return a.length === b.length && b.every(item => setA.has(item))\n}\n\n/** Whether the card carries any input/select labeled with one of the labels. */\nfunction hasLabeledInput(card: HTMLElement, labels: readonly string[]): boolean {\n return Array.from(card.querySelectorAll<HTMLInputElement | HTMLSelectElement>('input[aria-label], select[aria-label]'))\n .some(candidate => labels.some(label => (candidate.getAttribute('aria-label') ?? '').startsWith(label)))\n}\n\n/**\n * The route token of a card, resolved against the joined providers. The\n * official edit card prints the route key as the `.editorRoute` tag next to\n * the display-name title; the create card prints a fixed heading with no key,\n * but its \"Provider ID\" input carries the route id being chosen \u2014 a route not\n * in the settings document yet. Match the key first (exact, unambiguous),\n * then the create card's typed id, then the display name (a create card never\n * reaches the name arm: its Provider ID input marks it, and its fixed heading\n * could otherwise collide with a provider's display name), and finally the\n * title itself as a route key: a provider that never set a display name\n * renders the route id as its title and hides the `.editorRoute` tag (the\n * two are then equal), so only this last arm can resolve it. A create card\n * never reaches the last arm either \u2014 its Provider ID input already returned.\n */\nfunction routeOfCard(\n card: HTMLElement,\n providers: Record<string, Record<string, unknown>>,\n labels: HostLabels,\n): { route: string; staged: boolean } | undefined {\n const key = card.querySelector<HTMLElement>('[class*=\"editorRoute\"]')?.textContent?.trim()\n if (key !== undefined && key.length > 0 && hasOwn(providers, key)) return { route: key, staged: false }\n if (hasLabeledInput(card, labels.routeId)) {\n const typed = inputValueByLabel(card, labels.routeId)\n return typed.length > 0 && !hasOwn(providers, typed) ? { route: typed, staged: true } : undefined\n }\n const title = card.querySelector<HTMLElement>('[class*=\"editorTitle\"], [class*=\"rowName\"]')?.textContent?.trim()\n if (title === undefined || title.length === 0) return undefined\n const byName = Object.entries(providers).find(([, profile]) => profile['displayName'] === title)\n if (byName !== undefined) return { route: byName[0], staged: false }\n // A provider that never set a display name resolves to its route key, and\n // the host hides the .editorRoute tag while that key equals the title\n // (ProviderEditor renders it only when the name differs) \u2014 so neither arm\n // above can find the route. The title is then the route key itself, which\n // is exact: resolve it directly. A create card never reaches this arm (its\n // Provider ID input already returned), so an editor-title/key collision\n // with the fixed create heading cannot occur.\n if (hasOwn(providers, title)) return { route: title, staged: false }\n return undefined\n}\n\n/**\n * The commit/cancel pair of one card's official action row, or undefined when\n * the row cannot be read.\n *\n * Three tiers, most structural first, because this is the signal the plugin's\n * landing write hangs on and the official page is free to restyle it:\n * 1. the last two buttons of `[class*=\"editorActions\"]` -- the commit is the\n * rightmost, its dismiss the one before it (`EditorFooter.tsx`: the row is\n * a plain two-button div, cancel first, commit last);\n * 2. the declared CSS Modules classes `primaryButton` / `secondaryButton`\n * (they carry a hash suffix, so only the prefix can match);\n * 3. the host's own copy for that row, resolved through the dictionary it\n * renders from (`apply` is 'Apply' in English and '\u4FDD\u5B58' in Chinese, so\n * pinning either language would strand one of them).\n *\n * A row whose buttons no tier can identify returns undefined, which is what\n * makes {@link ScanState.signalsUnavailable} trip and the landing decision\n * degrade to \"the card went away, write it\" rather than dropping the edit.\n */\nfunction actionsOf(card: HTMLElement, labels: HostLabels): { submit: HTMLElement; cancel: HTMLElement } | undefined {\n const row = card.querySelector<HTMLElement>('[class*=\"editorActions\"]')\n if (row === null) return undefined\n const buttons = Array.from(row.querySelectorAll<HTMLElement>('button'))\n if (buttons.length < 2) return undefined\n const matches = (node: HTMLElement, label: string): boolean =>\n (node.textContent ?? '').trim() === label\n const primary = row.querySelector<HTMLElement>('[class*=\"primaryButton\"]')\n const secondary = row.querySelector<HTMLElement>('[class*=\"secondaryButton\"]')\n if (primary !== null && secondary !== null) return { submit: primary, cancel: secondary }\n const submit = [...buttons].reverse().find(node => labels.apply.some(label => matches(node, label)))\n const cancel = [...buttons].reverse().find(node => labels.cancel.some(label => matches(node, label)))\n if (submit !== undefined && cancel !== undefined) return { submit, cancel }\n // Structural last resort: the rightmost button commits, its left sibling\n // dismisses. Used only when neither the classes nor the copy resolve, so a\n // one-button row (a future layout) still yields a commit.\n const last = buttons[buttons.length - 1]\n const previous = buttons[buttons.length - 2]\n if (last === undefined || previous === undefined) return undefined\n return { submit: last, cancel: previous }\n}\n\n/**\n * Wire one button's click ONCE, in the capture phase.\n *\n * Capture, not bubble: the marker has to be recorded before the official\n * React handler runs and tears the card down, and React's own listeners sit\n * on the root container. Capture also makes this independent of whether the\n * official handler stops propagation.\n */\nfunction wireOnce(\n node: HTMLElement,\n wired: WeakSet<Element>,\n onHit: () => void,\n): void {\n if (wired.has(node)) return\n wired.add(node)\n node.addEventListener('click', onHit, { capture: true })\n}\n\n/**\n * Whether an official editing card is open on the page, told from the card's\n * own action row (its Cancel/commit pair) with the model-row containers as a\n * fallback signal.\n *\n * The distinction matters: an ON-SCREEN card holds a frozen revision baseline,\n * so a write landing while it is open is refused on the user's next save --\n * issue #7. Judging \"is a card open\" from the capacity buttons alone answers\n * \"is a model row expanded\", which is not the same question: an open card with\n * an empty model list would read as idle and let exactly that write through.\n */\nfunction officialCardOf(root: HTMLElement): HTMLElement | undefined {\n return root.querySelector<HTMLElement>('[class*=\"editorActions\"]')\n ?? root.querySelector<HTMLElement>('[class*=\"modelEntry\"]')\n ?? undefined\n}\n\n/**\n * The LIVE form of the write fence: whether an official card is open in the\n * CURRENT DOM.\n *\n * The scan's own boolean lags by `SCAN_DEBOUNCE_MS`, so a write that started\n * during that window would still see \"no card\" and land behind the card's\n * frozen revision baseline \u2014 issue #7 again. `writeModelRows` already probes\n * this at the last possible moment (after its read, before its mutate); this\n * helper makes that probe read the DOM instead of the last scan's snapshot.\n */\nfunction officialCardOpen(): boolean {\n return officialCardOf(panelRoot()) !== undefined\n}\n\n/**\n * Scan the settings DOM for official model rows and reconcile the injected\n * editors. Idempotent: existing editors are left alone, new disclosures get\n * one, and removed ones are unmounted.\n * @param root - the settings panel root to scan.\n * @param deps - the injection dependencies.\n * @param state - mutable scan state shared across invocations.\n */\nexport function reconcile(root: HTMLElement, deps: InjectorDeps, state: ScanState): void {\n // Cheap DOM gate BEFORE the wire read: most mutations in a running app\n // (chat streaming, typing anywhere) fire while no model row exists at\n // all. One prefix query per locale answers \"is the Models page here?\" \u2014\n // when it is not and nothing is mounted, the scan costs nothing more.\n if (!root.isConnected) return\n // One label read per scan, taken before the cheap DOM gate below: the gate\n // itself matches on them, and reading lazily is what keeps a language switch\n // in step with the page (the labels the host renders change with it).\n const labels = deps.labels()\n // \"Is an official editing card open?\" is the gate that decides whether this\n // plugin may write at all. The card's own action row answers it structurally,\n // with the model-row containers as a second signal: an open card whose model\n // list happens to be empty carries no capacity button, and judging idleness\n // from those buttons alone would let a write slip into that card's frozen\n // revision baseline -- issue #7 again, under a rarer trigger.\n const cardOpen = officialCardOf(root) !== undefined\n // Match on the attribute VALUE, never through a selector built out of host\n // copy: a language pack whose label carries a quote or a bracket would make\n // `querySelector` throw, and this scan has no try/catch around it, so the\n // settings injection would stall for that language.\n const hasCapacityRows = Array.from(root.querySelectorAll<HTMLButtonElement>('button[aria-label]'))\n .some(button => labels.capacity.some(label => (button.getAttribute('aria-label') ?? '').startsWith(label)))\n if (!cardOpen) {\n // No card is open, so no catalogue is either \u2014 but the seat is this\n // plugin's OWN DOM inside the host's head, and only a scan can take it\n // out, so every path that skips the row scan also has to clear the seats.\n reconcileAutoEffortSeats(root, [], { t: deps.t })\n if (state.mounted.size > 0) {\n for (const [, entry] of state.mounted) entry.editor.unmount()\n state.mounted.clear()\n }\n // No editing card is on the page, so no on-screen revision baseline can be\n // invalidated by a write: THIS is the moment to land everything the\n // session held back. `!== false` also covers the first scan (undefined) --\n // and a page that reloaded onto restored ledgers -- so outstanding work\n // always gets its pass; the second arm only fires while intents wait, so a\n // settled page costs nothing beyond the check.\n const wasEditing = state.editing\n state.editing = false\n if (wasEditing !== false || (hasOutstanding(state) && Date.now() >= state.nextFlushAt)) {\n void runIdlePass(deps, state).catch(error => {\n console.error(`[bre] idle pass failed: ${error instanceof Error ? error.message : String(error)}`)\n })\n }\n return\n }\n state.editing = true\n // An open card showing no model row has nothing to equip: the idle pass\n // stays fenced until the card goes away (or a row appears). The catalogue\n // head stays, though \u2014 the host still renders \u83B7\u53D6\u53EF\u7528\u6A21\u578B there \u2014 so the\n // auto-adapt seat stays beside it. Taking the seat out is what made that\n // link jump the moment the last model was deleted.\n if (!hasCapacityRows) {\n for (const [, entry] of state.mounted) entry.editor.unmount()\n state.mounted.clear()\n state.describePromise ??= deps.describeNamespace()\n const seatLabels = labels\n void state.describePromise.then(join => {\n seatEmptyCatalog(root, deps, state, join, seatLabels)\n }).catch(error => {\n console.error(`[bre] empty-catalog seat failed: ${error instanceof Error ? error.message : String(error)}`)\n })\n return\n }\n // Fold the describe request across scans (one wire read per wave). A\n // promise's .then ALWAYS runs asynchronously (microtask), even when already\n // resolved \u2014 the fold just keeps concurrent scans from stacking wire reads.\n // The holder clears this field on rejection (retry the read next scan) and\n // on pushed invalidations (settings/document-updated, connection/reset \u2014\n // see apply()), so a stale snapshot never outlives the change that made it\n // stale.\n state.describePromise ??= deps.describeNamespace()\n const run = (join: SettingsJoin): void => {\n if (!root.isConnected) return\n const namespace = join.namespace\n const providers = providersOf(namespace)\n\n // Staged declarations land on the IDLE pass, never from an open card:\n // writing while the official editor holds the document is exactly what\n // made the user's own save in that card fail with `settings/conflict`.\n\n const found: FoundModel[] = []\n for (const aria of labels.capacity) {\n // The official disclosure buttons carry a numbered aria-label\n // (\"Capacities 1\", \"\u5BB9\u91CF 2\", \u2026), so match by prefix.\n const triggers = Array.from(root.querySelectorAll<HTMLButtonElement>('button[aria-label]'))\n .filter(button => (button.getAttribute('aria-label') ?? '').startsWith(aria))\n for (const trigger of triggers) {\n const card = cardOf(trigger)\n if (card === undefined) continue\n // The model id lives on the trigger's OWN row, not elsewhere in the\n // card: reading the card would return the first row's id for every\n // trigger once more than one model is present.\n const row = trigger.closest<HTMLElement>('[class*=\"modelEntry\"]') ?? card\n const modelId = inputValueByLabel(row, labels.modelId)\n if (modelId.length === 0) continue\n // The container is OPTIONAL: a collapsed row renders no disclosure at\n // all, and skipping it here was what kept the provider-wide seat (and\n // every provider-wide decision) blind until the user unfolded a model.\n // A row with no container is still a model of this provider -- it\n // contributes to what the seat adapts, and only its own editor mount\n // waits for the container the chevron will render.\n found.push({ container: disclosureOf(trigger), row, modelId, card })\n }\n }\n\n // The \"adapt every model of this provider\" seat (user request \u2464) rides the\n // same scan that found the rows: one seat per card, seated in that card's\n // catalogue head beside the official fetch link. The rows' own editors\n // answer the click through the document event, but only for a row that is\n // EXPANDED (only it has a container to mount into), so the seat also hands\n // the route to the provider-wide adapt, which reads the settings document\n // and covers exactly the rows nothing on screen can answer for.\n reconcileAutoEffortSeats(root, autoEffortTargets(found, providers, labels), {\n t: deps.t,\n onRequest: route => adaptEveryModel(state, deps, route),\n })\n\n // Unmount editors whose rows are gone (the page re-rendered).\n for (const [key, entry] of state.mounted) {\n if (!found.some(candidate => candidate.container === key)) {\n entry.editor.unmount()\n state.mounted.delete(key)\n }\n }\n\n // Ghosted staging: the route IS saved but its staged model row is no\n // longer anywhere on the page (renamed away, or the row/card deleted).\n // One missing scan can be a transient re-render gap, so staging is\n // withdrawn only after missing TWO consecutive scans; routes that do not\n // exist yet are never touched here -- an open create card still owns those.\n if (state.pending.size > 0) {\n const onPage = new Map<string, Set<string>>()\n for (const target of found) {\n const resolved = routeOfCard(target.card, providers, labels)\n if (resolved === undefined) continue\n let ids = onPage.get(resolved.route)\n if (ids === undefined) onPage.set(resolved.route, ids = new Set())\n ids.add(target.modelId)\n }\n const missing = new Map<string, Set<string>>()\n for (const [route, models] of state.pending) {\n if (!hasOwn(providers, route)) continue\n const present = onPage.get(route)\n const stale = [...models.keys()].filter(id => !present?.has(id))\n if (stale.length > 0) missing.set(route, new Set(stale))\n }\n for (const [route, ids] of missing) {\n const prior = state.missedScans.get(route)\n if (prior === undefined) continue\n for (const id of ids) {\n if (!prior.has(id)) continue\n stageEffortsInto(state, route, id, undefined)\n prior.delete(id)\n }\n }\n state.missedScans = missing\n }\n\n // The action row belongs to the CARD, not to an expanded model disclosure.\n // Wire it even when every model is collapsed: the provider-wide button can\n // hold declarations for those models, and only the official Save may land\n // them once the card releases its frozen settings revision.\n const cards = new Set(found.map(target => target.card))\n for (const card of cards) {\n const resolved = routeOfCard(card, providers, labels)\n if (resolved === undefined) continue\n const route = resolved.route\n const actions = actionsOf(card, labels)\n if (actions === undefined) {\n state.signalsUnavailable = true\n } else {\n // A readable row is positive evidence the signal works: clear the\n // degrade flag so one transient unreadable card does not disable the\n // \"commits with the official Save\" gate for the rest of the session.\n state.signalsUnavailable = false\n // Resolve the route AT CLICK TIME: a create card's Provider ID can be\n // (re)typed after the buttons were first wired, and React reuses the\n // button element -- a captured route would mark / clear the wrong one.\n wireOnce(actions.submit, state.submitWired, () => {\n const live = routeOfCard(card, providers, labels)?.route ?? route\n state.committing.add(live)\n persistLedger(state)\n })\n wireOnce(actions.cancel, state.cancelWired, () => {\n const live = routeOfCard(card, providers, labels)?.route ?? route\n forgetRoute(state, live)\n })\n }\n }\n\n found.forEach((target, index) => {\n // A collapsed row has nowhere to mount: its editor appears the moment\n // the official chevron unfolds it and the next scan sees the container.\n // Everything below assumes one, so the row bows out here -- after it has\n // already served the provider-wide side of the scan above.\n const container = target.container\n if (container === undefined) return\n const resolved = routeOfCard(target.card, providers, labels)\n if (resolved === undefined) return\n const { route, staged: routeStaged } = resolved\n const profile = providers[route] ?? {}\n const models = modelsOf(providers, route)\n // Staged covers TWO unsaved shapes: the create card's draft route, and\n // a typed-but-unsaved model row on a SAVED route. Writing the latter\n // would bounce model-not-found (the row is not in the document yet);\n // staging rides the same flush-on-save path as the create card.\n const savedModelIds = new Set(models.map(model => model['id']))\n const staged = routeStaged || !savedModelIds.has(target.modelId)\n // A staged row's baseline is the pending store (the settings document\n // holds nothing for the model yet); the create card's typed protocol\n // and endpoint stand in for the stored profile facts, both for the\n // editor's display and for suggestion inference.\n const stagedEfforts = staged ? state.pending.get(route)?.get(target.modelId)?.efforts : undefined\n const efforts = staged\n ? (stagedEfforts === 'keep' ? undefined : stagedEfforts)\n : effortsOf(models, target.modelId)\n const effortsUnset = !staged && models.some(model => model['id'] === target.modelId && model[UNSET_MARKER] === true)\n const input = staged\n ? state.pending.get(route)?.get(target.modelId)?.input\n : inputOf(models, target.modelId)\n const compat = staged\n ? state.pending.get(route)?.get(target.modelId)?.compat\n : compatOf(models, target.modelId)\n const defaultEffort = staged\n ? (state.pending.get(route)?.get(target.modelId)?.defaultEffort ?? undefined)\n : defaultEffortOf(models, target.modelId)\n // An unsaved row has no stored declaration to name it, but the user may\n // have typed a Display name on the row already -- suggestion inference\n // and knowledge-base matching lose that signal without it. The read is\n // ROW-scoped (the same multi-row trap the model id above avoids).\n const typedName = staged ? inputValueByLabel(target.row, labels.modelName) : ''\n const modelName = staged ? (typedName.length > 0 ? typedName : undefined) : nameOf(models, target.modelId)\n const typedApi = routeStaged ? inputValueByLabel(target.card, labels.apiProtocol) : ''\n const typedBaseURL = routeStaged ? inputValueByLabel(target.card, labels.baseUrl) : ''\n const routeApi = routeStaged && typedApi.length > 0\n ? typedApi\n : typeof profile['api'] === 'string' ? profile['api'] as string : undefined\n const routeBaseURL = routeStaged && typedBaseURL.length > 0\n ? typedBaseURL\n : typeof profile['baseURL'] === 'string' ? profile['baseURL'] as string : undefined\n // The editor's write seam reads the namespace LIVE through its own\n // describe (not this scan's snapshot): a conflict retry must re-read a\n // fresh revision to have any chance of succeeding. Staged rows stage\n // into this scan state's pending store instead of writing settings.\n const next: EditorMountProps = {\n route,\n routeDisplayName: routeStaged ? route : typeof profile['displayName'] === 'string' ? profile['displayName'] as string : route,\n ...routeApi === undefined ? {} : { routeApi },\n ...routeBaseURL === undefined ? {} : { routeBaseURL },\n modelId: target.modelId,\n ...modelName === undefined ? {} : { modelName },\n ...efforts === undefined ? {} : { efforts },\n ...effortsUnset ? { effortsUnset } : {},\n ...input === undefined ? {} : { input },\n ...compat === undefined ? {} : { compat },\n ...defaultEffort === undefined ? {} : { defaultEffort },\n index,\n staged,\n officialInputTypes: officialInputTypesOf(container),\n api: createEditorApi(\n deps.api,\n undefined,\n (r, m, e, c, i, de) => { stageEffortsInto(state, r, m, e, c, i, de) },\n // This row is on screen: it ALWAYS holds the document. The card it\n // lives in froze its revision baseline, so the intent is queued and\n // the official card's own Save is what commits it (issue #7 / C2).\n (r, m, w) => { queueWriteInto(state, r, m, w) },\n // The editor's Reset drops wherever this row's intent landed.\n r => { withdrawIntent(state, r, target.modelId) },\n // Re-resolve the card's staged-ness at CALL time, not at mount time:\n // the same DOM row moves between unsaved and saved as the user types\n // a route id (or adds a model row), and the scan snapshot behind this\n // closure lags that by up to one debounce window. The saved-model arm\n // still reads the snapshot: the settings document only changes when a\n // describe does.\n () => (routeOfCard(target.card, providers, labels)?.staged ?? routeStaged) || !savedModelIds.has(target.modelId),\n ),\n readOnly: join.writable !== true,\n t: deps.t,\n }\n const existing = state.mounted.get(container)\n if (existing !== undefined) {\n // The official page kept the container but moved the document under\n // it (an apply from this editor or elsewhere): swap the fresh props\n // in place so the editor never shows a stale saved declaration. The\n // sameProps guard keeps unchanged rows from re-rendering.\n if (!sameProps(existing.props, next)) {\n existing.props = next\n existing.editor.render(next)\n }\n return\n }\n if (hasEditor(container)) return\n const editor = deps.mount(container, next)\n state.mounted.set(container, { editor, props: next })\n })\n }\n // A rejected describe must not permanently disable the injector: clear the\n // folded promise so the next scan retries the read.\n void state.describePromise.then(run, () => {\n state.describePromise = undefined\n })\n}\n", "/**\n * Compare-before-write DOM helpers.\n *\n * Every write in the injection layer goes through one of these. An unchanged\n * `setAttribute` still fires a mutation record, and the passes listen for\n * mutations on the whole panel, so a blind write would have the scan chasing\n * its own tail: mark \u2192 record \u2192 rescan \u2192 mark. Comparing first is what makes\n * a settled page produce no mutations at all.\n *\n * @module dsh-model-think-level/client/injection/dom\n */\n\n/** setAttribute only when the value would change. */\nexport function setAttr(el: Element, name: string, value: string): void {\n if (el.getAttribute(name) !== value) el.setAttribute(name, value)\n}\n\n/** `hidden` is a property, not an attribute these passes can compare textually. */\nexport function setHidden(el: HTMLElement | undefined, hidden: boolean): void {\n if (el !== undefined && el.hidden !== hidden) el.hidden = hidden\n}\n\n/** Class writes compare first, like {@link setAttr}. `classList.add` is a\n * no-op for a token it already holds in a spec-conforming engine, but it is\n * not one in jsdom \u2014 and this helper's promise (\"a settled page writes\n * nothing\") has to hold in both, since a write here feeds the same scan. */\nexport function setClass(el: HTMLElement, name: string, on: boolean): void {\n if (el.classList.contains(name) === on) return\n if (on) el.classList.add(name)\n else el.classList.remove(name)\n}\n\n/** Write text only when it differs, for the same reason as {@link setAttr}. */\nexport function setText(el: HTMLElement | undefined, text: string): void {\n if (el !== undefined && el.textContent !== text) el.textContent = text\n}\n", "/**\n * The key list's seat: the official key field grows a plugin-owned host, and the\n * list is rendered inline into it, directly under the input.\n *\n * The field is not reachable from the settings page's React tree \u2014 the plugin\n * only gets keyed slots, and none of them sits beside a form field \u2014 so the seat\n * is a DOM leaf inserted directly AFTER the field. The list then falls under the\n * input, which is where a provider's keys belong: the label above already says\n * \"API key\", and the list reads as part of the field rather than as a surface\n * that has to be opened, positioned and dismissed.\n *\n * A sibling rather than a last child: the reveal eye hangs on the field's own\n * bottom edge (`bottom: 0` against the field, which its validation-message rule\n * also measures from), so a list inside the field would carry the eye down with\n * it and leave it sitting on the list's last row. React tolerates the foreign\n * sibling \u2014 it resolves a node's next sibling by walking past nodes it does not\n * own \u2014 and the list mirrors the field's region, so a pane hides it along with\n * the field it belongs to.\n *\n * The host carries the route its list was built for \u2014 that attribute is how the\n * pass knows whether the mounted list still belongs to the field it follows, and\n * how a test asks what is mounted there.\n *\n * The whole seat is skipped when the field is not a key field, or when its card\n * announces no route: the add-provider form has no route yet, and keys belong to\n * a route that exists.\n *\n * @module dsh-model-think-level/client/injection/key-manager\n */\n\nimport { createElement } from 'react'\nimport type { ReactNode } from 'react'\nimport { KeyManager } from '../KeyManager.js'\nimport type { KeyManagerClient } from '../key-manager-client.js'\nimport { setAttr, setClass } from './dom.js'\nimport { EffortBoundary, mountReact, unmountReact, type ForeignMount } from './mount.js'\n\n/** Set on a field whose keys the plugin lists. */\nexport const KEY_MANAGED_CLASS = 'bre-key-managed'\n\n/** The plugin's own element right after the field: the container of the list. */\nconst KEY_HOST_CLASS = 'bre-keys-host'\n\n/** The route the host's list was built for; read by the pass and by tests. */\nconst KEY_HOST_ATTR = 'data-bre-key-host'\n\n/** The official key input, marked by the reveal pass that dresses it. */\nconst KEY_INPUT_ATTR = 'data-bre-key-input'\n\n/** The pane the field belongs to; the list mirrors it so a pane hides both. */\nconst KEY_REGION_ATTR = 'data-bre-region'\n\n/** The repo's inline translator shape. */\nexport type Translator = (key: string, params?: Record<string, string | number>) => string\n\n/** What a reconcile tells the field's key list. */\nexport interface KeyManagerFieldDeps {\n /** Copy for the list and its form. */\n readonly t: Translator\n /** The provider route this card edits; absent on the add-provider form. */\n readonly route?: string | undefined\n /** List transport; injectable for tests. */\n readonly client?: KeyManagerClient | undefined\n}\n\n/** One mounted list, as the field holding it remembers it. */\ninterface FieldState {\n /** The route the mounted list manages. */\n route: string\n /** The plugin's element after the field, holding the React root. */\n readonly host: HTMLElement\n /** The React root mounted into {@link host}. */\n readonly mount: ForeignMount\n /** The copy the list was last rendered with. */\n t: Translator\n /** The transport the list was last rendered with. */\n client?: KeyManagerClient | undefined\n}\n\n/**\n * The lists this module mounted, keyed by the field holding them.\n *\n * Keyed by the field, not by the route: a card re-render replaces the field's\n * React children (and with them the old host), and looking the field up tells\n * the next pass whether the list it mounted is still there.\n */\nconst FIELDS = new WeakMap<HTMLElement, FieldState>()\n\n/** The official key input of a field, or undefined when this is another field. */\nfunction keyInputOf(field: HTMLElement): HTMLInputElement | undefined {\n const marked = field.querySelector(`input[${KEY_INPUT_ATTR}]`)\n if (marked instanceof HTMLInputElement) return marked\n // Before the reveal pass has marked it \u2014 or with that pass off \u2014 the key input\n // is still the field's only password input, and no other field of the card has\n // one. Everything else the card holds (name, base URL, protocol) is text.\n const secret = field.querySelector('input[type=\"password\"]')\n return secret instanceof HTMLInputElement ? secret : undefined\n}\n\n/** The plugin's host after a field, or undefined when the field has none. */\nfunction hostOf(field: HTMLElement): HTMLElement | undefined {\n const known = FIELDS.get(field)?.host\n if (known !== undefined && known.isConnected) return known\n const next = field.nextElementSibling\n return next instanceof HTMLElement && next.hasAttribute(KEY_HOST_ATTR) ? next : undefined\n}\n\n/**\n * Mount the key list into a field, replacing whatever this module mounted there\n * before. Idempotent: a field whose list is already up and current is left\n * exactly as it is, so a settled page sees no write at all.\n */\nexport function enhanceKeyManagerField(field: HTMLElement, deps: KeyManagerFieldDeps): void {\n const route = deps.route\n if (route === undefined || route.length === 0) return\n if (keyInputOf(field) === undefined) return\n // A field the page has already dropped cannot take a sibling, and the sweep\n // that removed it has already taken the list down.\n if (field.parentElement === null) return\n setClass(field, KEY_MANAGED_CLASS, true)\n\n const known = FIELDS.get(field)\n let host = hostOf(field)\n if (host === undefined) {\n // The field has no host: either this is the first pass over it, or the page\n // re-rendered and took the old host with it. Either way the state's own host\n // is gone, so its root is taken down (the element is already detached)\n // before a new host takes its place after the field.\n if (known !== undefined) drop(field, known)\n host = field.ownerDocument.createElement('div')\n host.className = KEY_HOST_CLASS\n field.after(host)\n }\n setAttr(host, KEY_HOST_ATTR, route)\n // The list belongs to the pane the field belongs to and the pane rules key on\n // the region, so the list mirrors the field's own \u2014 otherwise it would stay on\n // screen in the Models and Advanced panes, under a field those panes hide.\n const region = field.getAttribute(KEY_REGION_ATTR)\n if (region === null) {\n if (host.hasAttribute(KEY_REGION_ATTR)) host.removeAttribute(KEY_REGION_ATTR)\n } else setAttr(host, KEY_REGION_ATTR, region)\n\n const subtree = (): ReactNode =>\n createElement(\n EffortBoundary,\n { fallbackText: deps.t('keysPanelFailed') },\n createElement(KeyManager, { route, t: deps.t, client: deps.client }),\n )\n\n const state = FIELDS.get(field)\n if (state !== undefined && state.route === route) {\n // The same list for the same route: nothing the official page did can change\n // what it shows, so only a new language or transport is worth a render \u2014 and\n // React writes no DOM for props that did not change.\n if (state.t !== deps.t || state.client !== deps.client) {\n state.t = deps.t\n state.client = deps.client\n state.mount.root.render(subtree())\n }\n return\n }\n if (state !== undefined) {\n // The field outlived its list: the card switched provider, or the page\n // reused the field. The old root goes, the host stays where it is.\n FIELDS.delete(field)\n state.mount.root.unmount()\n }\n FIELDS.set(field, { route, host, mount: mountReact(host, subtree(), { sync: true }), t: deps.t, client: deps.client })\n}\n\n/** Take one mounted list down and forget it; the state's host goes with it. */\nfunction drop(field: HTMLElement, state: FieldState): void {\n FIELDS.delete(field)\n unmountReact(state.mount)\n}\n\n/**\n * Drop the list of one field, leaving the official field exactly as the page\n * rendered it. Called when the takeover ends, and for the stray sweep of a page\n * that is being torn down.\n */\nexport function resetKeyManagerField(field: HTMLElement): void {\n setClass(field, KEY_MANAGED_CLASS, false)\n const state = FIELDS.get(field)\n if (state !== undefined) drop(field, state)\n // Whatever the map knew, nothing of this module may stay in the field: both\n // the state's own host and any host the field picked up along the way go.\n hostOf(field)?.remove()\n}\n\n/** Drop every list this module mounted. */\nexport function teardownKeyManager(): void {\n for (const host of Array.from(document.querySelectorAll<HTMLElement>(`[${KEY_HOST_ATTR}]`))) {\n // A host sits directly after the field it was built for, so the field is the\n // host's previous sibling; one whose field is already gone is removed.\n const field = host.previousElementSibling\n if (field instanceof HTMLElement && FIELDS.has(field)) resetKeyManagerField(field)\n else host.remove()\n }\n}\n\n/**\n * The route a field's list was built for, or undefined when it has none.\n *\n * A test hook: the route lives on the host, so what the pass decided can be read\n * back from the DOM alone.\n */\nexport function keyManagerRoute(field: HTMLElement): string | undefined {\n return hostOf(field)?.getAttribute(KEY_HOST_ATTR) ?? undefined\n}\n", "/**\n * The API-key list: one provider's keys, which one is in use, and the one\n * inline form that adds, relabels or replaces a key.\n *\n * Rendered inline under the official key field (see `injection/key-manager`),\n * not as a floating panel: the field's label already says \"API key\", so its list\n * belongs directly beneath it, inside the card that owns the profile \u2014 the list\n * reads as part of the field rather than as a surface that has to be opened,\n * positioned and dismissed.\n *\n * The list is a view over host state and holds no truth of its own \u2014 every\n * mutation answers with the whole list, and the list is adopted as it arrives\n * (`adopt`), so the GUI can never drift from what the settings document and the\n * credential store actually say. Three facts it deliberately reflects:\n *\n * - **the key in use is the profile's reference.** Choosing a row writes\n * `providers.<route>.apiKeyEnv`, so it holds for every request the route\n * makes, not only the ones this GUI starts. Exactly one key is in use per\n * provider, which is why the mark at the head of a row is a radio.\n * - **a value is shown masked, or not at all.** The host reads the stored\n * secret and answers a head-and-tail preview; the middle never leaves it,\n * and the eye beside the field stays the one gesture that reveals a value.\n * - **one form serves all three edits.** Adding, relabelling and replacing a\n * value differ only in which fields it shows, so they share a form rendered\n * as the row below the row they act on \u2014 for an add, the list's last row.\n *\n * @module dsh-model-think-level/client/KeyManager\n */\n\nimport { Fragment, useCallback, useEffect, useId, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport { isLegalKeyValue, normalizeKeyAlias } from '../key-refs.js'\nimport { httpKeyManagerClient, type KeyAnswer, type KeyManagerClient, type KeyRow } from './key-manager-client.js'\n\n/** The repo's inline translator shape. */\ntype Translate = (key: string, params?: Record<string, string | number>) => string\n\n/** Props of {@link KeyManager}. */\nexport interface KeyManagerProps {\n /** The provider route whose keys this list manages. */\n readonly route: string\n /** Copy for the list's labels. */\n readonly t: Translate\n /** Transport; injectable for tests. */\n readonly client?: KeyManagerClient | undefined\n}\n\n/** A transient line under the list: the host's refusal, or a confirmation. */\ninterface Notice {\n readonly kind: 'error' | 'status'\n readonly text: string\n}\n\n/** Which form is open, and on which row. */\ninterface Editing {\n readonly kind: 'add' | 'alias' | 'value'\n readonly ref?: string | undefined\n}\n\n/** The check drawn inside the mark of the key in use. */\nconst CHECK_PATH = 'M3.9 8.35 6.6 11.05 12.1 5.5'\n\n/** The circle-plus drawn on the row that opens the add form. */\nconst PLUS_PATHS: readonly string[] = ['M8 3.9v8.2', 'M3.9 8h8.2']\n\n/** One 16px stroke glyph, on the same grid the injected controls draw on. */\nfunction Glyph({ paths }: { paths: readonly string[] }): ReactNode {\n return (\n <svg\n viewBox=\"0 0 16 16\"\n width={16}\n height={16}\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={1.5}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n focusable=\"false\"\n >\n {paths.map(d => (\n <path key={d} d={d} />\n ))}\n </svg>\n )\n}\n\n/**\n * What a row shows where its stored value would be.\n *\n * The host masks the value it read; when it could not read one at all, a row\n * that the store says holds nothing says so instead of pretending to hide a\n * secret, and a row the store would not answer for shows the mask alone \u2014 the\n * absence of a preview is the store's answer, not a claim about the key.\n */\nfunction previewOf(row: KeyRow, t: Translate): string {\n if (row.masked !== undefined) return row.masked\n return row.configured === false ? t('keysMissing') : '\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022'\n}\n\n/** How a row is named in its controls: its label, its preview, or its ref. */\nfunction nameOf(row: KeyRow): string {\n return row.alias ?? row.masked ?? row.ref\n}\n\n/**\n * Render the key list for one provider route.\n * @param props - see {@link KeyManagerProps}.\n * @returns the list's root element.\n */\nexport function KeyManager(props: KeyManagerProps): ReactNode {\n const { route, t } = props\n const client = props.client ?? httpKeyManagerClient\n const group = useId()\n\n const [rows, setRows] = useState<readonly KeyRow[] | undefined>(undefined)\n const [enabledRef, setEnabledRef] = useState<string | null>(null)\n const [notice, setNotice] = useState<Notice | undefined>(undefined)\n const [busy, setBusy] = useState(false)\n const [editing, setEditing] = useState<Editing | undefined>(undefined)\n const [nameDraft, setNameDraft] = useState('')\n const [valueDraft, setValueDraft] = useState('')\n const [confirming, setConfirming] = useState<string | undefined>(undefined)\n\n /**\n * Adopt whatever the host just answered. A refusal keeps the list that is\n * already on screen \u2014 a failed write changed nothing, so showing an empty\n * list would be a lie about the state of the document.\n */\n const adopt = useCallback(\n (answer: KeyAnswer, okText: string | undefined): void => {\n if (!answer.ok) {\n setNotice({ kind: 'error', text: answer.error ?? t('keysUnreachable') })\n return\n }\n setRows(answer.entries)\n setEnabledRef(answer.enabledRef)\n setNotice(okText === undefined ? undefined : { kind: 'status', text: okText })\n },\n [t],\n )\n\n useEffect(() => {\n let live = true\n void (async () => {\n const answer = await client.list(route)\n if (live) adopt(answer, undefined)\n })()\n return () => {\n live = false\n }\n }, [adopt, client, route])\n\n /** Run one mutation, then take the host's word for the new list. */\n const run = useCallback(\n async (action: () => Promise<KeyAnswer>, okText: string): Promise<void> => {\n setBusy(true)\n setConfirming(undefined)\n setEditing(undefined)\n try {\n adopt(await action(), okText)\n } finally {\n setBusy(false)\n }\n },\n [adopt],\n )\n\n const closeForm = (): void => {\n setEditing(undefined)\n setNameDraft('')\n setValueDraft('')\n }\n\n const openForm = (kind: 'alias' | 'value', row: KeyRow): void => {\n setEditing({ kind, ref: row.ref })\n setNameDraft(kind === 'alias' ? row.alias ?? '' : '')\n setValueDraft('')\n setConfirming(undefined)\n }\n\n /**\n * Write what the open form holds. The three kinds differ only in which fields\n * they showed, so they share one submit: an add stores a new reference, a\n * relabel writes the label, and a replace swaps the secret of the reference\n * already on the row \u2014 never its name, its label or its seat.\n */\n const submit = (): void => {\n if (busy || editing === undefined) return\n const kind = editing.kind\n const ref = editing.ref\n // An alias-only edit accepts an empty field: that is how a label is cleared.\n if (kind !== 'alias' && !isLegalKeyValue(valueDraft)) return\n const alias = normalizeKeyAlias(nameDraft)\n setBusy(true)\n void (async () => {\n try {\n if (kind === 'add') {\n const answer = await client.add(route, valueDraft, alias)\n adopt(answer, t('keysAdded'))\n if (answer.ok) closeForm()\n return\n }\n if (ref === undefined) return\n const answer =\n kind === 'alias' ? await client.rename(route, ref, alias) : await client.replace(route, ref, valueDraft)\n adopt(answer, kind === 'alias' ? t('keysRenamed') : t('keysReplaced'))\n if (answer.ok) closeForm()\n } finally {\n setBusy(false)\n }\n })()\n }\n\n /** The one inline form, shaped by the edit it is open for. */\n const form = (): ReactNode => {\n if (editing === undefined) return null\n const kind = editing.kind\n const wantsValue = kind !== 'alias'\n const illegal = wantsValue && valueDraft.length > 0 && !isLegalKeyValue(valueDraft)\n return (\n <div className=\"bre-keys-form\">\n <div className=\"bre-keys-form-fields\">\n {kind === 'value' ? null : (\n <input\n className=\"bre-text-input bre-keys-name\"\n type=\"text\"\n value={nameDraft}\n placeholder={t('keysAliasPlaceholder')}\n aria-label={t('keysAlias')}\n disabled={busy}\n autoFocus={kind === 'alias'}\n onChange={event => setNameDraft(event.target.value)}\n />\n )}\n {wantsValue ? (\n <input\n className=\"bre-text-input bre-keys-secret\"\n type=\"password\"\n autoComplete=\"new-password\"\n spellCheck={false}\n value={valueDraft}\n placeholder={kind === 'add' ? t('keysValuePlaceholder') : t('keysNewValuePlaceholder')}\n aria-label={t('keysValue')}\n aria-invalid={illegal}\n disabled={busy}\n autoFocus\n onChange={event => setValueDraft(event.target.value)}\n />\n ) : null}\n </div>\n {illegal ? (\n <p className=\"bre-effort-message bre-error\" role=\"alert\">\n {t('keysAddIllegal')}\n </p>\n ) : null}\n <div className=\"bre-keys-form-actions\">\n <button type=\"button\" className=\"bre-link-button\" disabled={busy} onClick={closeForm}>\n {t('keysCancel')}\n </button>\n <button\n type=\"button\"\n className=\"bre-primary-button\"\n disabled={busy || (wantsValue && !isLegalKeyValue(valueDraft))}\n onClick={submit}\n >\n {kind === 'add' ? t('keysAddConfirm') : t('keysSave')}\n </button>\n </div>\n </div>\n )\n }\n\n /**\n * The entry that adds a key: the list's own last item, or the form once it is\n * open. It is offered even when the list could not be read \u2014 adding a key\n * needs no list \u2014 so a failed read must not leave the user with no way out.\n */\n const addEntry = (): ReactNode =>\n editing?.kind === 'add' ? (\n <li className=\"bre-keys-form-row\">{form()}</li>\n ) : (\n <li className=\"bre-keys-add-row\">\n <button\n type=\"button\"\n className=\"bre-keys-add\"\n disabled={busy}\n onClick={() => {\n setEditing({ kind: 'add' })\n setNameDraft('')\n setValueDraft('')\n setConfirming(undefined)\n }}\n >\n <span className=\"bre-keys-add-mark\" aria-hidden=\"true\">\n <Glyph paths={PLUS_PATHS} />\n </span>\n {t('keysAdd')}\n </button>\n </li>\n )\n\n if (rows === undefined) {\n // Still reading: NOTHING, until the answer lands. The mount happens in the\n // same frame the card opens (flushSync), and a placeholder here would paint\n // one frame of a one-line panel that then visibly swaps to the list \u2014 the\n // \"the key section flashes when you press Edit\" report. An empty panel in\n // that frame reads as the card still opening; the list is the first thing\n // the pane ever shows. A read that FAILED says so instead of appearing to\n // load forever, and still offers the add entry.\n if (notice === undefined) return <div className=\"bre-keys\" />\n return (\n <div className=\"bre-keys\">\n <p className={'bre-effort-message bre-' + notice.kind} role={notice.kind === 'error' ? 'alert' : 'status'}>\n {notice.text}\n </p>\n <ul className=\"bre-keys-list\">{addEntry()}</ul>\n </div>\n )\n }\n\n return (\n <div className=\"bre-keys\">\n <ul className=\"bre-keys-list\">\n {rows.map(row => {\n const name = nameOf(row)\n return (\n <Fragment key={row.ref}>\n <li className=\"bre-keys-row\" data-state={row.configured === false ? 'missing' : 'ready'}>\n {row.alias === undefined ? null : <span className=\"bre-keys-alias\">{row.alias}</span>}\n <span className={row.alias === undefined ? 'bre-keys-mask bre-keys-mask-plain' : 'bre-keys-mask'}>\n {previewOf(row, t)}\n </span>\n <span className=\"bre-keys-actions\">\n {confirming === row.ref ? (\n <>\n <button\n type=\"button\"\n className=\"bre-link-button bre-keys-danger\"\n disabled={busy}\n onClick={() => void run(() => client.remove(route, row.ref), t('keysRemoved'))}\n >\n {t('keysConfirmRemove')}\n </button>\n <button\n type=\"button\"\n className=\"bre-link-button\"\n disabled={busy}\n onClick={() => setConfirming(undefined)}\n >\n {t('keysCancel')}\n </button>\n </>\n ) : (\n <>\n <button\n type=\"button\"\n className=\"bre-link-button\"\n disabled={busy}\n onClick={() => {\n setConfirming(row.ref)\n setEditing(undefined)\n }}\n >\n {t('keysRemove')}\n </button>\n <button\n type=\"button\"\n className=\"bre-link-button\"\n disabled={busy}\n onClick={() => openForm('alias', row)}\n >\n {t('keysAlias')}\n </button>\n <button\n type=\"button\"\n className=\"bre-link-button\"\n disabled={busy}\n onClick={() => openForm('value', row)}\n >\n {t('keysEdit')}\n </button>\n </>\n )}\n </span>\n {/* The enable choice sits at the END of the row: one radio per\n key, one enabled key per provider, and the control is named\n for what it does instead of for a state to hunt for. */}\n <label className=\"bre-keys-enable\" title={t('keysEnable', { name })}>\n <input\n type=\"radio\"\n name={group}\n className=\"bre-keys-enabled\"\n checked={row.enabled}\n disabled={busy}\n aria-label={t('keysEnable', { name })}\n onChange={() => void run(() => client.enable(route, row.ref), t('keysEnabledNote', { name }))}\n />\n <span className=\"bre-keys-mark\" aria-hidden=\"true\">\n {row.enabled ? <Glyph paths={[CHECK_PATH]} /> : null}\n </span>\n <span className=\"bre-keys-enable-text\">{t('keysEnabled')}</span>\n </label>\n </li>\n {editing !== undefined && editing.ref === row.ref ? (\n <li className=\"bre-keys-form-row\">{form()}</li>\n ) : null}\n </Fragment>\n )\n })}\n {addEntry()}\n </ul>\n\n {notice === undefined ? null : (\n <p className={'bre-effort-message bre-' + notice.kind} role={notice.kind === 'error' ? 'alert' : 'status'}>\n {notice.text}\n </p>\n )}\n </div>\n )\n}\n", "/**\n * The credential-reference vocabulary the API-key manager shares between both\n * halves.\n *\n * A provider's key list is a list of NAMES: every value lives in the harness\n * credential store under its own reference, and the profile records exactly one\n * of those names (`providers.<route>.apiKeyEnv`) as the reference the adapter\n * resolves when it calls the provider. This module owns the naming rules the\n * two halves must agree on \u2014 the browser half picks the next free name for a\n * new key, the host half re-validates whatever arrives \u2014 so one name can never\n * mean two different things on the two sides of the wire.\n *\n * Deliberately free of `node:` imports: the client bundle imports this module\n * too (the same way `constants.ts` is shared), and a single `node:` import\n * would drag the host into the browser build.\n *\n * @module dsh-model-think-level/key-refs\n */\n\n/** One key the manager lists for a provider route. */\nexport interface KeyEntry {\n /** Credential reference holding the value. */\n readonly ref: string\n /** User-facing label; absent means \"show the reference itself\". */\n readonly alias?: string\n}\n\n/** Longest reference the manager will mint or accept. */\nexport const KEY_REF_MAX_LENGTH = 80\n\n/** Longest alias the manager stores. A label, never a secret. */\nexport const KEY_ALIAS_MAX_LENGTH = 40\n\n/**\n * The shape of a reference the manager accepts. Looser than the credential\n * store's own POSIX-ish rule on purpose: a route may legitimately start with a\n * digit (`9router` \u2192 `9ROUTER_API_KEY`), and the store stays the authority on\n * what it will hold. This is the plugin's sanity bound, not the validation.\n */\nexport const KEY_REF_PATTERN = /^[A-Za-z0-9_]{1,80}$/\n\n/**\n * The reference the official Models page derives for a provider route: the\n * route upper-cased with every non-alphanumeric run folded to `_`, plus\n * `_API_KEY`. Mirrors the official page exactly so the first key the manager\n * adds for a provider is the very reference that page would have written.\n * @param route - the provider route key (`ofox`, `aliyun-cn`).\n * @returns the derived reference name.\n */\nexport function deriveKeyRef(route: string): string {\n return `${route.toUpperCase().replace(/[^A-Z0-9]+/g, '_')}_API_KEY`\n}\n\n/** Whether a value is a reference name this manager will pass to the store. */\nexport function isKeyRefName(value: unknown): value is string {\n return typeof value === 'string' && KEY_REF_PATTERN.test(value)\n}\n\n/**\n * The first free name in the sequence `<DERIVED>`, `<DERIVED>_2`, `<DERIVED>_3`\u2026\n * @param route - the provider route key.\n * @param taken - every reference already listed for that route.\n * @returns a reference not in `taken`.\n */\nexport function nextKeyRef(route: string, taken: readonly string[]): string {\n const used = new Set(taken)\n const base = deriveKeyRef(route)\n if (!used.has(base)) return base\n for (let suffix = 2; suffix <= 999; suffix += 1) {\n const candidate = `${base}_${suffix}`\n if (!used.has(candidate)) return candidate\n }\n // A thousand keys on one route is not a real deployment; stay unique anyway.\n return `${base}_${Date.now()}`\n}\n\n/**\n * A stored alias, or undefined when the label carries nothing.\n *\n * Control characters are dropped rather than escaped: an alias is rendered as\n * text on the official page, and a stray newline there is a rendering bug, not\n * data worth keeping.\n * @param value - whatever arrived (user input, or a hand-edited index file).\n * @returns the trimmed label, capped at {@link KEY_ALIAS_MAX_LENGTH}.\n */\nexport function normalizeKeyAlias(value: unknown): string | undefined {\n if (typeof value !== 'string') return undefined\n let cleaned = ''\n for (const char of value) {\n const code = char.codePointAt(0) ?? 0\n if (code < 32 || code === 127) continue\n cleaned += char\n }\n cleaned = cleaned.trim()\n if (cleaned.length === 0) return undefined\n return cleaned.slice(0, KEY_ALIAS_MAX_LENGTH)\n}\n\n/**\n * Whether a value is a key the official page would itself accept: printable\n * ASCII with no spaces (its own `LEGAL_API_KEY`). The manager applies the same\n * rule so a key it stores can never be one the official card would refuse.\n */\nexport function isLegalKeyValue(value: unknown): value is string {\n return typeof value === 'string' && /^[\\x21-\\x7E]+$/.test(value)\n}\n\n/** Shortest value that still gets a head and a tail; below it, a flat mask. */\nconst KEY_MASK_MIN_LENGTH = 12\n\n/**\n * A stored key's display form: a head, an ellipsis and a tail \u2014 enough to tell\n * two look-alike keys apart, never the whole secret.\n *\n * The manager shows every row this way, so a list can say which key a row holds\n * without putting anything usable on the page; the eye beside the field stays\n * the one gesture that reveals a value. A value too short to survive trimming\n * gets a fixed-width mask instead: at that length a head and a tail would be\n * most of the key.\n */\nexport function maskKeyValue(value: string): string {\n if (value.length === 0) return ''\n if (value.length < KEY_MASK_MIN_LENGTH) return '\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022'\n return `${value.slice(0, 4)}...${value.slice(-4)}`\n}\n", "/**\n * The browser half's client for the host's API-key manager.\n *\n * Every key fact \u2014 which references a provider lists, which label each carries,\n * which one is in use, and whether the credential store actually holds a value\n * \u2014 is host state: the list is the plugin's own index file, the value lives in\n * the credential store, and the \"in use\" seat is the profile's `apiKeyEnv`\n * field. All six operations therefore ride ONE same-origin route\n * ({@link KEY_INDEX_PATH}), and the browser half only ever handles references,\n * labels, booleans and masked previews. It never sees a secret it did not\n * explicitly ask for: a key value is fetched one reference at a time through the\n * existing reveal route, on a click, and is never cached.\n *\n * Fails closed. A refused fence, a route that is not in the settings document,\n * a store that cannot be written, a network error \u2014 each answers `{ ok: false }`\n * (with the host's own wording when it sent one) and the panel says so; nothing\n * here invents success, and nothing here invents an error message either.\n *\n * @module dsh-model-think-level/client/key-manager-client\n */\n\nimport { KEY_INDEX_PATH, PROVIDER_KEY_PATH } from '../constants.js'\nimport { isRecord } from '../shared.js'\n\n/** One key row as the host reports it. */\nexport interface KeyRow {\n /** Credential reference holding the value. */\n readonly ref: string\n /** Stored label; absent means \"show the reference itself\". */\n readonly alias?: string\n /** Whether this is the reference the profile currently names. */\n readonly enabled: boolean\n /** Whether the credential store holds a value; null when it could not say. */\n readonly configured: boolean | null\n /**\n * The stored value in the host's display form \u2014 a head, an ellipsis and a\n * tail. Absent when the store would not let the value be read, so a row shows\n * nothing rather than a made-up preview.\n */\n readonly masked?: string\n}\n\n/** A successful answer: the whole list, in the host's order. */\nexport interface KeyState {\n readonly ok: true\n readonly route: string\n /** The reference in use, or null when the provider names none. */\n readonly enabledRef: string | null\n readonly entries: readonly KeyRow[]\n}\n\n/** A refused answer, carrying the host's own words when it sent any. */\nexport interface KeyFailure {\n readonly ok: false\n readonly error?: string\n}\n\n/** What one call to the key route answers. */\nexport type KeyAnswer = KeyState | KeyFailure\n\n/** The manager's transport, injectable so the panel can be tested without one. */\nexport interface KeyManagerClient {\n /** Read the provider's whole key list. */\n list(route: string): Promise<KeyAnswer>\n /** Store a new key and list it, returning the list it produced. */\n add(route: string, value: string, alias: string | undefined): Promise<KeyAnswer>\n /** Make one listed reference the key in use. */\n enable(route: string, ref: string): Promise<KeyAnswer>\n /** Relabel one listed reference (undefined clears the label). */\n rename(route: string, ref: string, alias: string | undefined): Promise<KeyAnswer>\n /** Replace the stored secret of one listed reference, keeping its name. */\n replace(route: string, ref: string, value: string): Promise<KeyAnswer>\n /** Forget one listed reference and its value. */\n remove(route: string, ref: string): Promise<KeyAnswer>\n /** The stored value of one listed reference, or undefined. */\n reveal(route: string, ref: string): Promise<string | undefined>\n}\n\n/** The host's wording for a refusal, if it sent one. */\nfunction messageOf(body: Record<string, unknown>): string | undefined {\n for (const field of ['error', 'message'] as const) {\n const value = body[field]\n if (typeof value === 'string' && value.length > 0) return value\n }\n return undefined\n}\n\n/** One row, or undefined when the payload is not one. */\nfunction rowOf(value: unknown): KeyRow | undefined {\n if (!isRecord(value)) return undefined\n const ref = value['ref']\n if (typeof ref !== 'string' || ref.length === 0) return undefined\n const alias = value['alias']\n const configured = value['configured']\n const masked = value['masked']\n return {\n ref,\n ...(typeof alias === 'string' && alias.length > 0 ? { alias } : {}),\n enabled: value['enabled'] === true,\n configured: typeof configured === 'boolean' ? configured : null,\n ...(typeof masked === 'string' && masked.length > 0 ? { masked } : {}),\n }\n}\n\n/** A list answer, or undefined when the payload is not one. */\nfunction stateOf(body: Record<string, unknown>): KeyState | undefined {\n const entries = body['entries']\n if (!Array.isArray(entries)) return undefined\n const rows: KeyRow[] = []\n for (const entry of entries) {\n const row = rowOf(entry)\n if (row !== undefined) rows.push(row)\n }\n const enabledRef = body['enabledRef']\n const route = body['route']\n return {\n ok: true,\n route: typeof route === 'string' ? route : '',\n enabledRef: typeof enabledRef === 'string' && enabledRef.length > 0 ? enabledRef : null,\n entries: rows,\n }\n}\n\n/**\n * Read one answer, whatever the status was.\n *\n * The host answers its refusals with a 4xx and a body, so the body is read\n * either way: the status alone would throw away the only copy of the reason.\n */\nasync function answerOf(response: Response): Promise<KeyAnswer> {\n let body: unknown\n try {\n body = await response.json()\n } catch {\n body = undefined\n }\n if (!isRecord(body)) return { ok: false }\n if (body['ok'] === true) {\n const state = stateOf(body)\n if (state !== undefined) return state\n }\n const error = messageOf(body)\n return error === undefined ? { ok: false } : { ok: false, error }\n}\n\n/** One call to the key route: a GET with no payload, a POST with one. */\nasync function request(route: string, payload?: Record<string, unknown>): Promise<KeyAnswer> {\n const url = `${KEY_INDEX_PATH}?route=${encodeURIComponent(route)}`\n try {\n const response = await fetch(\n url,\n payload === undefined\n ? { method: 'GET', cache: 'no-store', credentials: 'same-origin' }\n : {\n method: 'POST',\n cache: 'no-store',\n credentials: 'same-origin',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n },\n )\n return await answerOf(response)\n } catch {\n return { ok: false }\n }\n}\n\n/** The live client: same-origin fetch against the host's route. */\nexport const httpKeyManagerClient: KeyManagerClient = {\n list: route => request(route),\n add: (route, value, alias) => request(route, { op: 'add', value, ...(alias === undefined ? {} : { alias }) }),\n enable: (route, ref) => request(route, { op: 'enable', ref }),\n rename: (route, ref, alias) => request(route, { op: 'rename', ref, ...(alias === undefined ? {} : { alias }) }),\n remove: (route, ref) => request(route, { op: 'remove', ref }),\n replace: (route, ref, value) => request(route, { op: 'value', ref, value }),\n async reveal(route, ref) {\n try {\n const response = await fetch(\n `${PROVIDER_KEY_PATH}?route=${encodeURIComponent(route)}&ref=${encodeURIComponent(ref)}`,\n {\n method: 'GET',\n // A secret must not sit in the HTTP cache.\n cache: 'no-store',\n credentials: 'same-origin',\n },\n )\n if (!response.ok) return undefined\n const body = (await response.json()) as { ok?: unknown; key?: unknown }\n if (!isRecord(body) || body['ok'] !== true) return undefined\n const key = body['key']\n return typeof key === 'string' && key.length > 0 ? key : undefined\n } catch {\n return undefined\n }\n },\n}\n", "/**\n * The plugin's own outline glyphs.\n *\n * Shared because two injected controls draw one: the API-key eye, which swaps\n * between two glyphs by attribute, and the key field's alias chip, which is\n * icon-only so that it does not repeat the official \"API key\" label with the\n * word for the same thing. Every glyph is a stroke-only outline on the same\n * 16px grid, inherits `currentColor`, and is hidden from assistive tech \u2014 the\n * button holding it carries the meaning in its own `aria-label`.\n *\n * @module dsh-model-think-level/client/injection/icon\n */\n\n/** The namespace every SVG node here is created in. */\nexport const SVG_NS = 'http://www.w3.org/2000/svg'\n\n/** Marks a glyph, so the stylesheet can show one of several, and a sweep find it. */\nexport const ICON_ATTR = 'data-bre-icon'\n\n/** One outline path of a glyph, in the 16px viewBox. */\nexport function svgPath(d: string): SVGPathElement {\n const path = document.createElementNS(SVG_NS, 'path')\n path.setAttribute('d', d)\n return path\n}\n\n/** A stroke-only 16px glyph built from outline paths. */\nexport function svgGlyph(kind: string, paths: readonly string[]): SVGElement {\n const svg = document.createElementNS(SVG_NS, 'svg')\n svg.setAttribute('viewBox', '0 0 16 16')\n svg.setAttribute('width', '16')\n svg.setAttribute('height', '16')\n svg.setAttribute('fill', 'none')\n svg.setAttribute('stroke', 'currentColor')\n svg.setAttribute('stroke-width', '1.5')\n svg.setAttribute('stroke-linecap', 'round')\n svg.setAttribute('stroke-linejoin', 'round')\n svg.setAttribute('aria-hidden', 'true')\n svg.setAttribute('focusable', 'false')\n svg.setAttribute(ICON_ATTR, kind)\n svg.append(...paths.map(svgPath))\n return svg\n}\n", "/**\n * The eye on a provider's API-key field.\n *\n * Two jobs, one control. The official input is `<input type=\"password\">` whose\n * value is React's own draft state (`keyDraft`), and a SAVED key is never part\n * of it: a configured provider shows a placeholder instead, because the harness\n * keeps provider keys write-only (the settings document carries only the\n * profile's `apiKeyEnv` reference, and the credential service reports\n * `{ configured }` \u2014 never the value). So on a settled card the eye has nothing\n * to unmask, and the plugin asks its own host for the stored value\n * ({@link loadProviderKey}, the `PROVIDER_KEY_PATH` route) when \u2014 and only\n * when \u2014 the user clicks it with an empty field. A field holding a draft the\n * user just typed is never touched: the fetch is skipped and the click only\n * swaps the mask.\n *\n * The read is deliberately constrained:\n *\n * - it happens on an explicit click, never on render, and answers `undefined`\n * for every failure (no route, refused fence, reference-free profile,\n * unconfigured credential), which leaves the field masked;\n * - it is never cached (the request says `no-store` and the route answers\n * with the same header) and never logged;\n * - hiding the field again takes the borrowed value back out \u2014 the draft\n * returns to empty, which the official form reads as \"keep the stored key\",\n * so revealing can never overwrite the credential by accident. A value the\n * user edits after revealing is theirs, and is left alone.\n *\n * The button is the one node this plugin inserts into official DOM, and it is\n * inserted deliberately: the affordance only reads as an eye at the END of the\n * input if it sits inside the input's own box, and the official field has no\n * wrapper to hang it on. The trade is contained \u2014\n *\n * - the field itself is never restructured, and nothing official is moved or\n * removed: the button is an absolutely positioned LEAF appended last;\n * - React ignores trailing foreign children when it reconciles the field's\n * known children, so the node survives every re-render (each keystroke\n * re-renders this field);\n * - React never writes the input's `type` back (its props do not change), so\n * the revealed state survives too;\n * - the pass removes the button, the marker and the class on cleanup, and\n * restores the mask, so a withdrawn takeover leaves nothing behind.\n *\n * @module dsh-model-think-level/client/injection/key-reveal\n */\n\nimport { PROVIDER_KEY_PATH } from '../../constants.js'\nimport { setAttr, setClass, setHidden } from './dom.js'\nimport { svgGlyph } from './icon.js'\n\n/** Set on the field that carries the key input. */\nexport const KEY_FIELD_CLASS = 'bre-key-field'\n\n/** The eye button inside that field. */\nexport const KEY_EYE_CLASS = 'bre-key-eye'\n\n/** Set on the key input, so it is still findable once it is unmasked. */\nconst KEY_INPUT_ATTR = 'data-bre-key-input'\n\n/** The button's label while the key is masked, and while it is revealed. The\n * pass writes both on every reconcile, so a click only has to pick one and the\n * copy stays current across a language switch without a re-render. */\nconst SHOW_LABEL_ATTR = 'data-bre-eye-show'\nconst HIDE_LABEL_ATTR = 'data-bre-eye-hide'\n\n/** The two glyphs the eye swaps between, in the shared 16px grid. */\nconst EYE_PATHS = [\n 'M1.5 8S4 3.75 8 3.75 14.5 8 14.5 8 12 12.25 8 12.25 1.5 8 1.5 8Z',\n 'M6.2 8a1.8 1.8 0 1 0 3.6 0 1.8 1.8 0 0 0-3.6 0Z',\n]\n\n/** The repo's inline translator shape (see `HeadersEditor`, `ComposerSlider`). */\nexport type Translator = (key: string, params?: Record<string, string | number>) => string\n\n/** What a reconcile tells the field's eye. */\nexport interface KeyRevealDeps {\n /** Copy for the button's labels and its failure tooltip. */\n readonly t: Translator\n /**\n * The provider route this card edits, when the card announces one. Absent on\n * the add-provider form (no route exists yet), which is what keeps the eye\n * there a plain mask toggle.\n */\n readonly route?: string | undefined\n /** How to resolve the stored credential; injectable for tests. */\n readonly loadKey?: ((route: string) => Promise<string | undefined>) | undefined\n}\n\n/**\n * The latest deps of each dressed field.\n *\n * The eye's click handler is created once and then lives as long as the field,\n * while the pass re-visits it with fresh deps (a language switch, a route that\n * changed). Keeping them here means the handler always reads the CURRENT copy\n * and route instead of the ones captured when the button appeared.\n */\nconst DEPS = new WeakMap<HTMLElement, KeyRevealDeps>()\n\n/** The key input of a field, when the field is the key field: the masked input\n * on a first pass, and \u2014 once revealed, where a `[type=password]` probe would\n * no longer find it \u2014 the input this pass marked. */\nfunction keyInputOf(field: HTMLElement): HTMLInputElement | undefined {\n const marked = field.querySelector(`input[${KEY_INPUT_ATTR}]`)\n if (marked instanceof HTMLInputElement) return marked\n const masked = field.querySelector('input[type=\"password\"]')\n return masked instanceof HTMLInputElement ? masked : undefined\n}\n\n/** The eye this pass put in a field, matched by class and by being a direct\n * child, so a nested button of the official page can never be mistaken for it. */\nfunction eyeOf(field: HTMLElement): HTMLButtonElement | undefined {\n for (const child of Array.from(field.children)) {\n if (child instanceof HTMLButtonElement && child.classList.contains(KEY_EYE_CLASS)) return child\n }\n return undefined\n}\n\n/**\n * Give an editor field an eye at the end of its input, if it holds the API key.\n *\n * Runs on every reconcile of the card, so it only ever writes what differs; a\n * field with no key input is handed to {@link resetKeyField} instead, which is\n * how a takeover that has moved on (a provider whose key field is gone) drops\n * the affordance it no longer needs.\n */\nexport function revealKeyField(field: HTMLElement, deps: KeyRevealDeps): void {\n const input = keyInputOf(field)\n if (input === undefined) {\n resetKeyField(field)\n return\n }\n DEPS.set(field, deps)\n setAttr(input, KEY_INPUT_ATTR, '')\n setClass(field, KEY_FIELD_CLASS, true)\n const eye = eyeOf(field) ?? createEye(field)\n setAttr(eye, SHOW_LABEL_ATTR, deps.t('keyRevealShow'))\n setAttr(eye, HIDE_LABEL_ATTR, deps.t('keyRevealHide'))\n syncEye(eye, input)\n}\n\n/** Undo {@link revealKeyField}: drop the button, the class, the marker, and\n * re-mask the input, leaving the official field exactly as it was rendered. */\nexport function resetKeyField(field: HTMLElement): void {\n const eye = eyeOf(field)\n if (eye !== undefined) eye.remove()\n DEPS.delete(field)\n setClass(field, KEY_FIELD_CLASS, false)\n const input = field.querySelector(`input[${KEY_INPUT_ATTR}]`)\n if (input instanceof HTMLInputElement) {\n input.removeAttribute(KEY_INPUT_ATTR)\n input.type = 'password'\n }\n}\n\n/**\n * Ask the host for the credential a provider profile names.\n *\n * Fails closed by design: every refusal \u2014 an unknown route, a profile with no\n * reference, a credential that is not configured, a refused trust fence, a\n * network error \u2014 is `undefined`, and the caller leaves the field masked rather\n * than uncovering an empty box.\n */\nexport async function loadProviderKey(route: string): Promise<string | undefined> {\n try {\n const response = await fetch(`${PROVIDER_KEY_PATH}?route=${encodeURIComponent(route)}`, {\n method: 'GET',\n // A secret must not sit in the HTTP cache.\n cache: 'no-store',\n // The route is same-origin; sending credentials would add nothing and\n // keeps the request a plain GET.\n credentials: 'same-origin',\n })\n if (!response.ok) return undefined\n const body = (await response.json()) as { ok?: unknown; key?: unknown }\n if (body === null || typeof body !== 'object' || body.ok !== true) return undefined\n return typeof body.key === 'string' && body.key.length > 0 ? body.key : undefined\n } catch {\n return undefined\n }\n}\n\n/** State only, never structure: the button exists by then. Reads the labels\n * off the button rather than taking a translator, so the click handler does\n * not hold a stale copy of the copy. */\nfunction syncEye(eye: HTMLButtonElement, input: HTMLInputElement): void {\n const revealed = input.type !== 'password'\n setAttr(eye, 'aria-pressed', revealed ? 'true' : 'false')\n const label = eye.getAttribute(revealed ? HIDE_LABEL_ATTR : SHOW_LABEL_ATTR)\n if (label !== null) {\n setAttr(eye, 'aria-label', label)\n // Same idiom as the drag grip and the provider switch: a tooltip for a\n // control whose state only an icon shows.\n setAttr(eye, 'title', label)\n }\n // A disabled input cannot be edited, so the eye has nothing to reveal.\n setHidden(eye, input.disabled)\n}\n\n/**\n * Write into a React-controlled input the way the user's own typing would: the\n * prototype setter bypasses React's value tracker (an instance-level override),\n * and the bubbling `input` event then lets the official component take the\n * value into its draft state \u2014 so a re-render keeps it instead of reverting to\n * the empty draft.\n */\nfunction writeInputValue(input: HTMLInputElement, value: string): void {\n const descriptor = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')\n if (descriptor?.set === undefined) input.value = value\n else descriptor.set.call(input, value)\n input.dispatchEvent(new Event('input', { bubbles: true }))\n}\n\nfunction createEye(field: HTMLElement): HTMLButtonElement {\n const eye = document.createElement('button')\n eye.type = 'button'\n eye.className = KEY_EYE_CLASS\n eye.append(svgIcon('eye'), svgIcon('eye-off'))\n /** The stored value this eye wrote into the field, so hiding can take it\n * back out. Held in the closure, not in an attribute: the value is already in\n * the input, and a second copy in the DOM would outlive the reveal. */\n let borrowed: string | undefined\n\n const toggle = async (): Promise<void> => {\n const input = keyInputOf(field)\n const deps = DEPS.get(field)\n if (input === undefined || deps === undefined) return\n if (input.type !== 'password') {\n // Hiding: give back a value this eye borrowed. A value the user edited\n // after revealing is theirs, and stays.\n if (borrowed !== undefined && input.value === borrowed) writeInputValue(input, '')\n borrowed = undefined\n input.type = 'password'\n syncEye(eye, input)\n return\n }\n const route = deps.route\n if (route !== undefined && borrowed === undefined && input.value.length === 0) {\n setAttr(eye, 'aria-busy', 'true')\n let key: string | undefined\n try {\n key = await (deps.loadKey ?? loadProviderKey)(route)\n } catch {\n key = undefined\n }\n if (eye.hasAttribute('aria-busy')) eye.removeAttribute('aria-busy')\n if (key === undefined) {\n // Nothing to show: stay masked and say so in the tooltip, rather than\n // flipping an empty box to plain text and pretending it worked.\n const failed = deps.t('keyRevealFailed')\n setAttr(eye, 'aria-label', failed)\n setAttr(eye, 'title', failed)\n return\n }\n // A draft typed while the host was answering wins over the stored key.\n if (keyInputOf(field) === input && input.value.length === 0) {\n borrowed = key\n writeInputValue(input, key)\n }\n }\n input.type = 'text'\n syncEye(eye, input)\n }\n\n eye.addEventListener('click', (event: MouseEvent): void => {\n // The eye is this plugin's own control inside a form the official page\n // owns: keep the gesture from reaching their handlers and from submitting.\n event.preventDefault()\n event.stopPropagation()\n void toggle()\n })\n field.append(eye)\n return eye\n}\n\n/** A stroke-only 16px glyph that inherits the button's colour. */\nfunction svgIcon(kind: 'eye' | 'eye-off'): SVGElement {\n const paths = kind === 'eye-off' ? [...EYE_PATHS, 'M2.5 13.5 13.5 2.5'] : EYE_PATHS\n return svgGlyph(kind, paths)\n}\n", "/**\n * The provider editor's three-tab takeover for the official Models settings\n * page.\n *\n * The official editor presents one provider as a single long column \u2014 identity\n * fields, then the model list, then (one section further up the card) this\n * plugin's request-header editor \u2014 and the column keeps growing with every\n * feature. This pass re-presents the same content as three tabs:\n *\n * `provider` \u2014 the official identity fields (display name, key, base URL,\n * protocol); the editor's own header and action row stay put.\n * `models` \u2014 the official model list, the rows the effort editor seats\n * into.\n * `advanced` \u2014 the plugin's request-header section, which the provider-card\n * slot already mounts and which owns its own save/cancel.\n *\n * The takeover is pure CSS, by the same iron rule every pass here follows:\n * React owns the official children, so none of them move. The card carries the\n * active tab (`data-bre-tab`), each region carries its tab membership\n * (`data-bre-region`), and the stylesheet \u2014 not this module \u2014 decides what is\n * visible.\n *\n * The bar is NOT part of that DOM, and this module never inserts it. It is\n * React-rendered by {@link EditorTabBar} inside the plugin's own request-header\n * mount, which the provider-card slot already guarantees is a child of the\n * card; the stylesheet reveals it the moment the card carries the takeover\n * class. An earlier revision inserted the bar straight into the official child\n * list from here, which made it a foreign node in a React-reconciled list: it\n * was at the mercy of the next render, and the insertion itself needed the\n * headers mount to be a DIRECT child of the card \u2014 an anchor this module cannot\n * verify and which threw (aborting the whole scan) when it did not hold. Owning\n * the bar inside our own subtree removes both hazards: the pass below only ever\n * writes attributes, and the slot component publishes the card's open state the\n * same way.\n *\n * Everything degrades to the official page: a card whose structure this pass\n * cannot recognise (host redesign, partial render) is left exactly as the\n * official page built it, a card whose takeover throws is rolled back the same\n * way, and every attribute write is compared before it lands so the scan never\n * feeds the MutationObserver its own tail.\n *\n * The same visit also dresses the provider's API-key field with its reveal eye\n * ({@link revealKeyField}) and its key controls ({@link enhanceKeyManagerField}),\n * because that field's shape is decided by exactly the same classification and\n * its affordances have to come and go with the takeover.\n *\n * @module dsh-model-think-level/client/injection/editor-tabs\n */\n\nimport { setAttr, setClass, setHidden, setText } from './dom.js'\nimport { KEY_MANAGED_CLASS, enhanceKeyManagerField, resetKeyManagerField, teardownKeyManager } from './key-manager.js'\nimport { KEY_FIELD_CLASS, resetKeyField, revealKeyField } from './key-reveal.js'\n\n/** Marks the tab bar the plugin's own tab-bar component renders (one per card). */\nconst TABS_CLASS = 'bre-editor-tabs'\n\n/** One button inside the bar; carries its tab id. */\nconst TAB_BUTTON_CLASS = 'bre-editor-tab'\n\n/** The attribute holding a button's tab id. */\nconst TAB_ID_ATTR = 'data-bre-tab-id'\n\n/** Set on a card whose editor has been re-presented as tabs. */\nconst TABBED_CLASS = 'bre-tabbed'\n\n/** The card's active tab id; the region-visibility CSS keys on it. */\nconst TAB_ATTR = 'data-bre-tab'\n\n/**\n * The provider route a card edits, as published by the provider-card slot on\n * its own mount (`src/client/injection/provider-card-slot.ts` writes\n * `data-bre-provider={route}` on `.bre-headers-host`). Read here rather than\n * imported: an import of the slot would close a cycle back through the tab bar\n * this module backs. Absent on the add-provider form, which edits no route yet.\n */\nconst PROVIDER_ROUTE_ATTR = 'data-bre-provider'\n\n/** Tags an element as the pane content of one tab. */\nconst REGION_ATTR = 'data-bre-region'\n\n/** Tags the official editor body, hidden wholesale on the advanced tab. */\nconst BODY_ATTR = 'data-bre-editor-body'\n\n/** Remembers a `<details>` this pass forced open (the official \"Customized\"\n * group starts collapsed; with the tab bar in charge it must stay expanded).\n * The value records the state the pass found, so cleanup can restore it. */\nconst DETAILS_MEMO_ATTR = 'data-bre-details'\n\n/** The plugin's request-header mount inside a provider card. It stays visible\n * on every tab \u2014 it carries the tab bar; only the section inside it is a pane. */\nconst HEADERS_HOST_SELECTOR = '.bre-headers-host'\n\n/** The request-header section itself: the `advanced` pane. */\nconst HEADERS_SECTION_SELECTOR = '.bre-headers'\n\n/** Tab ids, in display order. */\nconst TAB_PROVIDER = 'provider'\nconst TAB_MODELS = 'models'\nconst TAB_ADVANCED = 'advanced'\nconst TAB_IDS = [TAB_PROVIDER, TAB_MODELS, TAB_ADVANCED] as const\n\n/**\n * A region id that is NEVER a tab. The pane rules only ever reveal the three\n * ids above, so a region tagged with this one stays hidden on every tab \u2014 which\n * is exactly what a part of the editor that the card already shows elsewhere\n * needs. Today that is the editor's own header (its title repeats the card\n * row's provider name, and its route tag repeats the row's route tag).\n */\nconst REGION_NONE = 'none'\n\n/** A tab id, as both the bar and the card attribute spell it. */\nexport type EditorTabId = (typeof TAB_IDS)[number]\n\n/** The tab ids, in display order, for the tab-bar component. */\nexport const EDITOR_TAB_IDS: readonly EditorTabId[] = TAB_IDS\n\n/** The bar's class, a button's class and a button's tab-id attribute, exported\n * so the component that renders the bar and the pass that labels it spell the\n * contract once. */\nexport const EDITOR_TABS_CLASS = TABS_CLASS\nexport const EDITOR_TAB_BUTTON_CLASS = TAB_BUTTON_CLASS\nexport const EDITOR_TAB_ID_ATTR = TAB_ID_ATTR\n\n/** The structural probes of an OPEN official editor \u2014 the same signals the\n * editor reconcile and the write fence already trust. */\nconst EDITOR_PROBE =\n '[class*=\"editorActions\"], [class*=\"modelList\"], [class*=\"modelCatalog\"], [class*=\"modelEntry\"]'\n\n/** Elements that ARE the model list itself \u2014 classified as the models pane on\n * sight. The live editor nests the catalog inside the \"Customized\" disclosure\n * together with the identity fields; anything up to this boundary is a group\n * to step inside, and the boundary itself is where the sorting stops (model\n * rows carry inputs of their own, which must never sort as provider fields). */\nconst MODELS_TERMINAL = '[class*=\"modelCatalog\"], [class*=\"modelList\"], [class*=\"modelEntry\"]'\n\n/** A provider card: a row with an open editor, or the create-provider card. */\nconst CARD_PROBE = '[class*=\"rowCard\"], [class*=\"addCard\"], li'\n\n/** Per-scan bookkeeping (one instance lives in `models-page.ts`). */\nexport interface EditorTabsState {\n /** Cards whose takeover threw and had to be rolled back. A card that cannot\n * be taken over must not repeat its complaint on every scan. */\n readonly warned: WeakSet<Element>\n}\n\nexport interface EditorTabsDeps {\n /** The plugin's own translator, for the tab labels. */\n t: (key: string, params?: Record<string, string | number>) => string\n}\n\nexport function createEditorTabsState(): EditorTabsState {\n return { warned: new WeakSet() }\n}\n\n/**\n * The card a plugin mount sits in. Both the slot component (publishing the\n * card's open state, switching tabs) and this pass resolve the card through\n * this one rule, so a card can never be taken over by one and addressed by the\n * other.\n */\nexport function editorCardOf(element: Element | null): HTMLElement | null {\n return element === null ? null : element.closest<HTMLElement>(CARD_PROBE)\n}\n\n/**\n * Switch one card to a tab. The card attribute is the single source of truth:\n * the stylesheet swaps the pane on the attribute alone, and the bar's buttons\n * follow it here.\n */\nexport function selectEditorTab(card: HTMLElement, id: EditorTabId): void {\n setAttr(card, TAB_ATTR, id)\n syncSelected(card)\n}\n\n/**\n * Visit every open provider editor and make sure it presents as tabs.\n * Idempotent: a card that already carries the takeover is only relabelled,\n * which is also how a language switch reaches the page. Cards the pass no\n * longer recognises as open fall back to the official one-column layout.\n */\nexport function reconcileEditorTabs(root: HTMLElement, deps: EditorTabsDeps, state: EditorTabsState): void {\n if (!root.isConnected) return\n const open = new Set<HTMLElement>()\n for (const probe of Array.from(root.querySelectorAll<HTMLElement>(EDITOR_PROBE))) {\n const card = probe.closest<HTMLElement>(CARD_PROBE)\n if (card !== null) open.add(card)\n }\n for (const card of Array.from(root.querySelectorAll<HTMLElement>(`.${TABBED_CLASS}`))) {\n if (!open.has(card)) cleanupCard(card)\n }\n for (const card of open) {\n try {\n applyTabs(card, deps)\n } catch (error) {\n // One unprocessable card must not cost the page its other cards. Roll the\n // takeover back \u2014 the official editor then renders exactly as the\n // official page built it \u2014 and say so once per card rather than on every\n // scan. `cleanupCard` only removes attributes and classes, so the\n // rollback cannot throw a second time.\n if (!state.warned.has(card)) {\n state.warned.add(card)\n console.warn('[bre] editor tabs: rolled a provider card back to the official layout:', error)\n }\n cleanupCard(card)\n }\n }\n}\n\n/** Remove every takeover mark. The bar is the tab-bar component's own DOM and\n * is hidden, not removed, by dropping the class it is revealed under. */\nexport function teardownEditorTabs(): void {\n for (const card of Array.from(document.querySelectorAll<HTMLElement>(`.${TABBED_CLASS}`))) {\n card.classList.remove(TABBED_CLASS)\n card.removeAttribute(TAB_ATTR)\n card.removeAttribute(BODY_ATTR)\n for (const el of Array.from(card.querySelectorAll<HTMLElement>(`[${REGION_ATTR}]`))) {\n el.removeAttribute(REGION_ATTR)\n }\n }\n // Stray marks on cards the class already left (mid-cleanup shapes) \u2014 cheap\n // to sweep document-wide once, on teardown only.\n for (const el of Array.from(document.querySelectorAll<HTMLElement>(`[${TAB_ATTR}]`))) el.removeAttribute(TAB_ATTR)\n for (const el of Array.from(document.querySelectorAll<HTMLElement>(`[${REGION_ATTR}]`))) {\n el.removeAttribute(REGION_ATTR)\n }\n for (const el of Array.from(document.querySelectorAll<HTMLElement>(`[${BODY_ATTR}]`))) {\n el.removeAttribute(BODY_ATTR)\n }\n for (const group of Array.from(document.querySelectorAll<HTMLDetailsElement>(`details[${DETAILS_MEMO_ATTR}]`))) {\n group.removeAttribute('open')\n group.removeAttribute(DETAILS_MEMO_ATTR)\n }\n // The reveal eye is the one node this pass inserts into official DOM, so it\n // needs the same stray sweep: whatever card it was left on, it goes \u2014 with\n // the mask restored to the input it was unmasking.\n for (const field of Array.from(document.querySelectorAll<HTMLElement>(`.${KEY_FIELD_CLASS}`))) {\n resetKeyField(field)\n }\n // The alias chip and the add button are the other two nodes this pass\n // inserts into official DOM, and the panel they open is a body-level layer:\n // both go the same way, and the layer is closed outright.\n for (const field of Array.from(document.querySelectorAll<HTMLElement>(`.${KEY_MANAGED_CLASS}`))) {\n resetKeyManagerField(field)\n }\n teardownKeyManager()\n}\n\n/** The body of the card's open editor, or undefined when unrecognisable. */\nfunction editorBodyOf(card: HTMLElement): HTMLElement | undefined {\n for (const child of Array.from(card.children)) {\n if (!(child instanceof HTMLElement)) continue\n // The card's header and action rows are \"editor\"-classed too; the body is\n // the remaining editor-classed child that actually holds the form.\n if (child.matches('[class*=\"editorHeader\"], [class*=\"editorTitle\"], [class*=\"editorActions\"], [class*=\"editorRoute\"]')) {\n continue\n }\n if (!child.matches('[class*=\"editor\"]')) continue\n if (child.querySelector(EDITOR_PROBE) !== null || child.querySelector('input, select') !== null) return child\n }\n // Climb from the probes instead: the body is the probe's ancestor hanging\n // directly off the card.\n let node: HTMLElement | null = card.querySelector<HTMLElement>(EDITOR_PROBE)\n while (node !== null && node.parentElement !== card) node = node.parentElement\n return node !== null && node !== card ? node : undefined\n}\n\n/** One editor body sorted into tab panes. */\ninterface BodyRegions {\n /** The editor's own header (title + route tag). The card row directly above\n * already shows both, so the pass tags it {@link REGION_NONE}: hidden on\n * every tab. */\n header?: HTMLElement\n /** The model list's container; absent when the provider has no catalogue. */\n models?: HTMLElement\n /** The commit/cancel row; always visible. */\n actions: HTMLElement\n /** Every remaining child \u2014 the provider identity fields. */\n fields: HTMLElement[]\n}\n\n/**\n * Sort the body into panes. Undefined when the shape is not the editor this\n * module knows: the identity fields and the action row must be present, or the\n * card is left alone.\n *\n * The model list is deliberately NOT required. A provider with no catalogue\n * yet still has an editor worth tabbing \u2014 the fields are exactly the ones a\n * user is looking for \u2014 so a missing catalogue only costs the Models tab\n * (which the bar hides) rather than the whole takeover.\n *\n * The live editor does not keep its panes as flat siblings: identity fields\n * and the model catalog share one `<details>` group (\"Customized\"), so a\n * child that carries the model probes *and* form controls is treated as a\n * grouping wrapper \u2014 its children are sorted instead of the group itself\n * becoming one pane. The sort never steps past `MODELS_TERMINAL`, where the\n * catalog's own model rows (inputs of their own) live.\n */\nfunction classifyBody(body: HTMLElement): BodyRegions | undefined {\n const regions = {\n header: undefined as HTMLElement | undefined,\n models: undefined as HTMLElement | undefined,\n actions: undefined as HTMLElement | undefined,\n fields: [] as HTMLElement[],\n }\n classifyChildren(body, regions, 0)\n const { actions, fields, header, models } = regions\n if (actions === undefined || fields.length === 0) return undefined\n if (!fields.some((field) => field.querySelector('input, select, textarea') !== null)) return undefined\n return { header, models, actions, fields }\n}\n\n/** Recursion cap: the live nesting is body \u2192 details \u2192 body \u2192 catalog. */\nconst CLASSIFY_MAX_DEPTH = 4\n\ntype BodyRegionsDraft = {\n header: HTMLElement | undefined\n models: HTMLElement | undefined\n actions: HTMLElement | undefined\n fields: HTMLElement[]\n}\n\nfunction classifyChildren(container: HTMLElement, regions: BodyRegionsDraft, depth: number): void {\n if (depth > CLASSIFY_MAX_DEPTH) return\n for (const child of Array.from(container.children)) {\n if (!(child instanceof HTMLElement)) continue\n // Plugin-authored subtrees are nobody's pane. NOTE: the region tags are\n // NOT ownership \u2014 they mark this pass's own classification of official\n // children, and filtering on them would make the next scan un-classify\n // everything it classified the pass before.\n if (child.matches('[class*=\"bre-\"], [data-plugin]')) continue\n if (regions.header === undefined && child.matches('[class*=\"editorHeader\"], [class*=\"editorTitle\"]')) {\n regions.header = child\n continue\n }\n if (child.matches('[class*=\"editorActions\"]')) {\n regions.actions ??= child\n continue\n }\n if (child.matches(MODELS_TERMINAL)) {\n regions.models ??= child\n continue\n }\n if (child.querySelector(EDITOR_PROBE) !== null) {\n // A grouping wrapper \u2014 fields and the model list together. Step inside.\n classifyChildren(child, regions, depth + 1)\n continue\n }\n if (child.querySelector('[class*=\"editorActions\"]') !== null) {\n regions.actions ??= child\n continue\n }\n if (child.querySelector('input, select, textarea') !== null) {\n regions.fields.push(child)\n }\n // Summaries, error/hint paragraphs, bare wrappers \u2014 nobody's pane.\n }\n}\n\n/** The provider route this card edits, when the slot has published one. */\nfunction providerRouteOf(card: HTMLElement): string | undefined {\n const host = card.querySelector<HTMLElement>(`[${PROVIDER_ROUTE_ATTR}]`)\n const route = host?.getAttribute(PROVIDER_ROUTE_ATTR)\n return route === null || route === undefined || route === '' ? undefined : route\n}\n\n/** Tag one open editor card for the takeover. */\nfunction applyTabs(card: HTMLElement, deps: EditorTabsDeps): void {\n const body = editorBodyOf(card)\n if (body === undefined) {\n cleanupCard(card)\n return\n }\n const regions = classifyBody(body)\n if (regions === undefined) {\n cleanupCard(card)\n return\n }\n const headersSection = card.querySelector<HTMLElement>(HEADERS_SECTION_SELECTOR)\n\n // One route for the whole pane: every field here belongs to the same\n // provider, and the eye and the key panel both use it to name the provider\n // whose keys they act on.\n const route = providerRouteOf(card)\n for (const field of regions.fields) {\n setAttr(field, REGION_ATTR, TAB_PROVIDER)\n // Dresses the API-key field with its reveal eye; a no-op on every other\n // field of the pane.\n revealKeyField(field, { t: deps.t, route })\n // \u2026and with the alias chip and the add button that open the key panel,\n // likewise only on the field that holds the key input.\n enhanceKeyManagerField(field, { t: deps.t, route })\n }\n // The editor's own header repeats the card row's provider name and route tag,\n // and that row sits right above it: rather than give it a pane of its own it\n // is tagged with a region id no tab can select, so it stays hidden. It is\n // tagged rather than removed so cleanup restores the official markup by\n // dropping the one attribute.\n if (regions.header !== undefined) setAttr(regions.header, REGION_ATTR, REGION_NONE)\n if (regions.models !== undefined) setAttr(regions.models, REGION_ATTR, TAB_MODELS)\n setAttr(body, BODY_ATTR, '')\n // The MOUNT is not the pane: it carries the bar and stays visible on every\n // tab, so only the section inside it is tagged (and hidden off `advanced`).\n if (headersSection !== null) setAttr(headersSection, REGION_ATTR, TAB_ADVANCED)\n forceGroupsOpen(body)\n\n // A tab whose pane is not on this card steps aside: the model list on a\n // provider with no catalogue, the section while the slot has not rendered it\n // (pre-first-render, or the headers feature off). A card sitting on a tab\n // that just became unavailable falls back to the first one.\n const available = new Set<string>([TAB_PROVIDER])\n if (regions.models !== undefined) available.add(TAB_MODELS)\n if (headersSection !== null) available.add(TAB_ADVANCED)\n const active = card.getAttribute(TAB_ATTR) ?? TAB_PROVIDER\n setAttr(card, TAB_ATTR, available.has(active) ? active : TAB_PROVIDER)\n setClass(card, TABBED_CLASS, true)\n\n syncTabBar(card, deps, available)\n}\n\n/**\n * Keep the bar in step with the card.\n *\n * The bar's STRUCTURE belongs to the tab-bar component; this pass owns the copy\n * (a language switch relabels the official page, and the bar must be relabelled\n * with it), the availability of each pane, and the selected state. All three\n * are written only when they differ, so a settled page produces no mutation of\n * its own.\n */\nfunction syncTabBar(card: HTMLElement, deps: EditorTabsDeps, available: ReadonlySet<string>): void {\n const bar = card.querySelector<HTMLElement>(`.${TABS_CLASS}`)\n if (bar === null) return\n setAttr(bar, 'aria-label', deps.t('editorTabsLabel'))\n setText(tabOf(bar, TAB_PROVIDER), deps.t('editorTabProvider'))\n setText(tabOf(bar, TAB_MODELS), deps.t('editorTabModels'))\n setText(tabOf(bar, TAB_ADVANCED), deps.t('editorTabAdvanced'))\n setHidden(tabOf(bar, TAB_MODELS), !available.has(TAB_MODELS))\n setHidden(tabOf(bar, TAB_ADVANCED), !available.has(TAB_ADVANCED))\n syncSelected(card, bar)\n}\n\n/** Mark the active button. The panes follow the card's attribute in CSS, so\n * this is the bar's own state (and assistive tech's), not the pane switch. */\nfunction syncSelected(card: HTMLElement, bar?: HTMLElement): void {\n const target = bar ?? card.querySelector<HTMLElement>(`.${TABS_CLASS}`)\n if (target === null) return\n const active = card.getAttribute(TAB_ATTR) ?? TAB_PROVIDER\n for (const tab of Array.from(target.querySelectorAll<HTMLElement>(`[${TAB_ID_ATTR}]`))) {\n setAttr(tab, 'aria-selected', tab.getAttribute(TAB_ID_ATTR) === active ? 'true' : 'false')\n }\n}\n\nfunction tabOf(bar: HTMLElement, id: string): HTMLElement | undefined {\n for (const tab of Array.from(bar.children)) {\n if (tab instanceof HTMLElement && tab.getAttribute(TAB_ID_ATTR) === id) return tab\n }\n return undefined\n}\n\n/** Drop the takeover from one card, back to the official one-column layout. */\nfunction cleanupCard(card: HTMLElement): void {\n // The bar is NOT removed here: it is the tab-bar component's own DOM, and\n // React unmounts it with the card. Leaving the takeover class off is all it\n // takes to hide it, because the stylesheet reveals it only under that class.\n card.classList.remove(TABBED_CLASS)\n card.removeAttribute(TAB_ATTR)\n card.removeAttribute(BODY_ATTR)\n for (const el of Array.from(card.querySelectorAll<HTMLElement>(`[${REGION_ATTR}]`))) {\n if (el.getAttribute(REGION_ATTR) === TAB_PROVIDER) {\n resetKeyField(el)\n resetKeyManagerField(el)\n }\n el.removeAttribute(REGION_ATTR)\n }\n restoreGroups(card)\n}\n\n/**\n * The official \"Customized\" group renders collapsed by default; with the tab\n * bar in charge there is no summary row left to expand it, so any group inside\n * a taken-over body is forced open. A memo attribute records the groups this\n * pass opened (vs. ones the page rendered open) so cleanup restores exactly\n * those.\n */\nfunction forceGroupsOpen(body: HTMLElement): void {\n for (const group of Array.from(body.querySelectorAll('details'))) {\n if (group.hasAttribute('open')) continue\n setAttr(group, DETAILS_MEMO_ATTR, 'closed')\n setAttr(group, 'open', '')\n }\n}\n\n/** Undo {@link forceGroupsOpen}: re-close the groups the pass opened. */\nfunction restoreGroups(card: HTMLElement): void {\n for (const group of Array.from(card.querySelectorAll(`details[${DETAILS_MEMO_ATTR}]`))) {\n group.removeAttribute('open')\n group.removeAttribute(DETAILS_MEMO_ATTR)\n }\n}\n\n", "/**\n * The drag engine behind both reorderable lists of the Models settings page:\n * the provider rows, and the model rows inside one provider's editor.\n *\n * Neither list is reordered in the DOM \u2014 React owns those children \u2014 so a row\n * is moved by writing a CSS `order` on it, which reorders the screen (both\n * lists are flex columns) without touching the child list React holds. The id\n * sequence that results is stored by whoever owns the list, through the\n * adapter's `commit`.\n *\n * What the pointer sees while dragging:\n * - a chip carrying the row's name, instead of the browser's screenshot of\n * whatever element was grabbed;\n * - an insertion line between two rows \u2014 drawn as a fixed overlay on the\n * body, never as a child of the list React owns;\n * - rows that slide (a FLIP pass) instead of jumping;\n * - the list scrolling on its own while the pointer rests near an edge.\n *\n * The two lists differ in exactly three ways, and all three live in the\n * adapter: where a row's grip is mounted, whether the whole seat drags or only\n * the grip (a model row is a grid of inputs \u2014 grabbing it must stay a text\n * selection), and how a row is named and the order stored.\n *\n * @module dsh-model-think-level/client/injection/row-drag\n */\n\nimport { moveToIndex } from '../provider-order.js'\n\n/** One reorderable list, as the engine needs to see it. */\nexport interface DragAdapter {\n /** How an event target resolves to one of this list's rows, or nothing. */\n rowOf: (node: Element, list: HTMLElement) => HTMLElement | undefined\n /** The row's stable id. A row that cannot be named is never moved or grabbed. */\n idOf: (row: HTMLElement) => string | undefined\n /** The text the carried chip shows for a row. */\n labelOf: (row: HTMLElement) => string | undefined\n /** The stored order applied to the ids now on screen. */\n order: (ids: readonly string[]) => readonly string[]\n /** Persist the order the rows screen in. */\n commit: (list: HTMLElement) => void\n /** The element a row's grip is mounted into. */\n seatOf: (row: HTMLElement) => HTMLElement | undefined\n /** Whether the whole seat drags (a provider header) or only the grip does. */\n readonly seatDrags: boolean\n /** The grip's accessible name. */\n gripLabel: (row: HTMLElement, id: string) => string\n /** The grip's tooltip. */\n gripHint: () => string\n /** Extra per-row DOM work, once the row is known to be nameable. */\n decorate?: (row: HTMLElement, seat: HTMLElement) => void\n}\n\n/** The row a drag is currently carrying, plus everything an abandoned drag needs. */\nexport interface DragSession {\n readonly row: HTMLElement\n readonly list: HTMLElement\n /** Every row in screen order when the drag began \u2014 an abandoned drag's undo. */\n readonly before: HTMLElement[]\n /** The nearest scroller, for the edge auto-scroll. */\n readonly scroller: HTMLElement | undefined\n /** The adapter that owned the list when the drag began. */\n readonly adapter: DragAdapter\n /** Pixels per frame the auto-scroll moves while the pointer rests near an edge. */\n scrollStep: number\n /** The scheduled auto-scroll frame, while one is pending. */\n frame: number | undefined\n dropped: boolean\n}\n\n/** Per-scan bookkeeping; one instance per module that owns a list. */\nexport interface DragState {\n /** The drag sources already carrying listeners. */\n readonly sources: WeakSet<Element>\n /** The lists already carrying the delegated dragover/drop listeners. */\n readonly lists: WeakSet<Element>\n /** The insertion line; created on the first drag and reused until teardown. */\n line: HTMLElement | undefined\n drag: DragSession | undefined\n}\n\n/** Marks the grip we mount, so a rescan recognizes its own work. */\nconst HANDLE_ATTR = 'data-bre-drag-handle'\n/** Marks a seat we made draggable, so teardown hands back an untouched one. */\nconst HEAD_ATTR = 'data-bre-row-head'\n/** Which side of the hovered row the line is on (before/after). */\nconst DROP_SIDE_ATTR = 'data-bre-drop-side'\nconst DRAGGING_CLASS = 'bre-row-dragging'\nconst DROP_CLASS = 'bre-row-drop-target'\nconst GHOST_CLASS = 'bre-drag-ghost'\nconst LINE_CLASS = 'bre-drop-line'\nconst SVG_NS = 'http://www.w3.org/2000/svg'\n/** The official lists' row gap, halved to aim the insertion line at its middle. */\nconst ROW_GAP = 8\n/** How close to a scroller's edge the pointer has to be to start scrolling. */\nconst EDGE_PX = 28\n/** Pixels per frame the edge auto-scroll moves. */\nconst SCROLL_STEP = 12\n/** The slide a row does when the pointer carries another one past it. */\nconst MOVE_TRANSITION = 'transform 180ms cubic-bezier(0.2, 0.7, 0.3, 1)'\n/**\n * Controls that own the gesture that starts on them \u2014 including the official\n * card's own buttons, and anything the plugin renders inside a row.\n */\nconst INTERACTIVE =\n 'button, a, input, textarea, select, [contenteditable], [role=\"button\"], ' +\n '[role=\"switch\"], [role=\"checkbox\"], [role=\"textbox\"], [role=\"combobox\"]'\n\nexport function createDragState(): DragState {\n return { sources: new WeakSet(), lists: new WeakSet(), line: undefined, drag: undefined }\n}\n\n/** A list's child elements, in DOM order. */\nexport function rowElements(parent: HTMLElement): HTMLElement[] {\n return Array.from(parent.children).filter((child): child is HTMLElement => child instanceof HTMLElement)\n}\n\n/** A row's own `order` when it has one, else the position it holds in the DOM. */\nfunction orderOf(row: HTMLElement, index: number): number {\n const raw = row.style.order\n if (raw === '') return index\n const parsed = Number.parseInt(raw, 10)\n return Number.isNaN(parsed) ? index : parsed\n}\n\n/** The rows in the sequence they currently screen. */\nexport function visualRows(list: HTMLElement): HTMLElement[] {\n return rowElements(list)\n .map((row, index) => ({ row, key: orderOf(row, index), index }))\n .sort((left, right) => left.key - right.key || left.index - right.index)\n .map(entry => entry.row)\n}\n\n/**\n * Put `sequence` on screen: each row it names takes the slot a named row\n * already held, and a row it does not name keeps the slot it has. Every child\n * gets an explicit `order` once anything moves, so no two rows share a slot.\n */\nexport function placeRows(list: HTMLElement, sequence: readonly HTMLElement[]): void {\n const current = visualRows(list)\n const named = current.filter(row => sequence.includes(row))\n const slots = named.map(row => current.indexOf(row))\n const ordered = sequence.filter(row => named.includes(row))\n const target = new Map<HTMLElement, number>()\n current.forEach((row, index) => target.set(row, index))\n slots.forEach((slot, position) => {\n const row = ordered[position]\n if (row !== undefined) target.set(row, slot)\n })\n for (const [row, slot] of target) {\n const value = String(slot)\n if (row.style.order !== value) row.style.order = value\n }\n}\n\n/**\n * Run `mutate`, then let every row that the mutation moved slide to where it\n * went. A row is pinned to its old offset with a transform, that position is\n * laid out (or the browser animates nothing), and the transform is released\n * under a transition \u2014 the standard FLIP pass, measured in layout pixels so a\n * container that cannot be measured (a hidden page) simply does not animate.\n */\nexport function slideIntoPlace(list: HTMLElement, mutate: () => void): void {\n const rows = rowElements(list)\n const before = rows.map(row => row.offsetTop)\n mutate()\n rows.forEach((row, index) => {\n const delta = (before[index] as number) - row.offsetTop\n if (delta === 0) return\n row.style.transition = 'none'\n row.style.transform = `translateY(${delta}px)`\n void row.offsetHeight\n row.style.transition = MOVE_TRANSITION\n row.style.transform = ''\n })\n}\n\n/** The ids of the rows that have one, in the order they screen. */\nexport function orderedIds(\n list: HTMLElement,\n idOf: (row: HTMLElement) => string | undefined,\n): string[] {\n return visualRows(list)\n .map(row => idOf(row))\n .filter((id): id is string => id !== undefined)\n}\n\n/**\n * Place the rows in `ids` order. Rows without a known id are left exactly where\n * they are: a row we cannot name is a row we must not move.\n */\nexport function applyIdOrder(\n list: HTMLElement,\n ids: readonly string[],\n idOf: (row: HTMLElement) => string | undefined,\n): void {\n const rank = new Map<string, number>()\n for (const [index, id] of ids.entries()) if (!rank.has(id)) rank.set(id, index)\n const named = visualRows(list).filter(row => idOf(row) !== undefined)\n const ordered = [...named].sort((left, right) => {\n const leftRank = rank.get(idOf(left) as string) ?? Number.MAX_SAFE_INTEGER\n const rightRank = rank.get(idOf(right) as string) ?? Number.MAX_SAFE_INTEGER\n return leftRank - rightRank\n })\n placeRows(list, ordered)\n}\n\n/** Hand every row back the inline geometry a drag wrote on it. */\nexport function clearRowOrder(rows: Iterable<HTMLElement>): void {\n for (const row of rows) {\n row.style.order = ''\n row.style.transition = ''\n row.style.transform = ''\n }\n}\n\n/**\n * Where the carried row has to land to sit before/after `hovered`.\n *\n * The index is read against the sequence WITHOUT the carried row, which is the\n * sequence `moveToIndex` splices into. Reading it against the sequence that\n * still holds the row is what makes a downward drag land one slot too far: the\n * row's own slot is still counted.\n */\nexport function dropIndex(\n rows: readonly HTMLElement[],\n carried: HTMLElement,\n hovered: HTMLElement,\n after: boolean,\n): number {\n const rest = rows.filter(row => row !== carried)\n const at = rest.indexOf(hovered)\n if (at < 0) return Math.max(0, rows.indexOf(carried))\n return after ? at + 1 : at\n}\n\n/** The row nearest a viewport Y \u2014 the fallback while the pointer is between rows. */\nexport function nearestRow(rows: readonly HTMLElement[], clientY: number): HTMLElement | undefined {\n let best: HTMLElement | undefined\n let bestDistance = Number.POSITIVE_INFINITY\n for (const row of rows) {\n const rect = row.getBoundingClientRect()\n const distance = clientY < rect.top ? rect.top - clientY : clientY > rect.bottom ? clientY - rect.bottom : 0\n if (distance < bestDistance) {\n bestDistance = distance\n best = row\n }\n }\n return best\n}\n\n/** Whether the pointer sits in the lower half of `row` (a zero-height row reads as \"after\"). */\nfunction isAfter(row: HTMLElement, clientY: number): boolean {\n const rect = row.getBoundingClientRect()\n if (rect.height <= 0) return true\n return clientY > rect.top + rect.height / 2\n}\n\n/** The viewport Y the insertion line belongs at for `row`. */\nexport function dropLineTop(row: HTMLElement, after: boolean): number {\n const rect = row.getBoundingClientRect()\n return after ? rect.bottom + ROW_GAP / 2 - 1 : rect.top - ROW_GAP / 2 - 1\n}\n\n/** Six dots, drawn rather than fetched, so the grip needs no asset URL. */\nfunction gripIcon(): SVGElement {\n const svg = document.createElementNS(SVG_NS, 'svg')\n svg.setAttribute('width', '10')\n svg.setAttribute('height', '14')\n svg.setAttribute('viewBox', '0 0 10 14')\n svg.setAttribute('fill', 'none')\n svg.setAttribute('aria-hidden', 'true')\n const dots: ReadonlyArray<readonly [number, number]> = [[2.5, 3], [7.5, 3], [2.5, 7], [7.5, 7], [2.5, 11], [7.5, 11]]\n for (const [cx, cy] of dots) {\n const dot = document.createElementNS(SVG_NS, 'circle')\n dot.setAttribute('cx', String(cx))\n dot.setAttribute('cy', String(cy))\n dot.setAttribute('r', '1.2')\n dot.setAttribute('fill', 'currentColor')\n svg.appendChild(dot)\n }\n return svg\n}\n\n/**\n * The chip the pointer carries. The browser's own drag image is a screenshot of\n * whatever element was grabbed, which for a grip is nothing and for a row card\n * is an unreadable slab \u2014 so we hand it a chip with the row's name.\n */\nexport function makeGhost(row: HTMLElement, label?: string): HTMLElement {\n const ghost = document.createElement('div')\n ghost.className = GHOST_CLASS\n ghost.setAttribute('aria-hidden', 'true')\n ghost.appendChild(gripIcon())\n const name = document.createElement('span')\n name.className = 'bre-drag-ghost-name'\n const printed = label ?? row.querySelector<HTMLElement>('[class*=\"rowName\"]')?.textContent?.trim()\n name.textContent = printed === undefined ? '' : printed\n ghost.appendChild(name)\n return ghost\n}\n\n/** Whether a drag that began on `target` belongs to a control rather than a row. */\nfunction interactiveTarget(target: EventTarget | null): boolean {\n if (!(target instanceof Element)) return false\n if (target.closest(`[${HANDLE_ATTR}]`) !== null) return false\n return target.closest(INTERACTIVE) !== null\n}\n\n/** The list a row belongs to \u2014 both lists hold their rows as direct children. */\nfunction listOf(row: HTMLElement): HTMLElement | undefined {\n return row.parentElement ?? undefined\n}\n\nfunction clearDropMarks(list: HTMLElement): void {\n for (const child of rowElements(list)) {\n child.classList.remove(DROP_CLASS)\n child.removeAttribute(DROP_SIDE_ATTR)\n }\n}\n\n/** The nearest ancestor that actually scrolls, for the edge auto-scroll. */\nexport function scrollHostOf(node: HTMLElement): HTMLElement | undefined {\n let current: HTMLElement | null = node.parentElement\n while (current !== null) {\n if (scrolls(current)) return current\n current = current.parentElement\n }\n const root = document.scrollingElement\n return root instanceof HTMLElement && root.scrollHeight > root.clientHeight ? root : undefined\n}\n\n/** Whether an element is a scroll container with something left to scroll. */\nfunction scrolls(node: HTMLElement): boolean {\n if (node.scrollHeight <= node.clientHeight) return false\n const declared = node.style.overflowY\n const overflow = declared.length > 0 ? declared : window.getComputedStyle(node).overflowY\n return overflow === 'auto' || overflow === 'scroll' || overflow === 'overlay'\n}\n\n/** The pixels per frame `host` should scroll while the pointer is at `clientY`. */\nexport function edgeScrollStep(host: HTMLElement, clientY: number): number {\n const rect = host.getBoundingClientRect()\n if (rect.height <= 0) return 0\n if (clientY < rect.top + EDGE_PX) return -SCROLL_STEP\n if (clientY > rect.bottom - EDGE_PX) return SCROLL_STEP\n return 0\n}\n\n/** Start, retarget or stop the auto-scroll pump of one drag. */\nfunction setScrollStep(state: DragState, session: DragSession, step: number): void {\n if (session.scrollStep === step) return\n session.scrollStep = step\n if (step === 0) {\n if (session.frame !== undefined) {\n window.cancelAnimationFrame(session.frame)\n session.frame = undefined\n }\n return\n }\n const host = session.scroller\n if (host === undefined || session.frame !== undefined) return\n const tick = (): void => {\n session.frame = undefined\n if (state.drag !== session || session.scrollStep === 0) return\n host.scrollTop += session.scrollStep\n session.frame = window.requestAnimationFrame(tick)\n }\n session.frame = window.requestAnimationFrame(tick)\n}\n\nfunction dropLine(state: DragState): HTMLElement {\n const existing = state.line\n if (existing !== undefined && existing.isConnected) return existing\n const line = document.createElement('div')\n line.className = LINE_CLASS\n line.setAttribute('aria-hidden', 'true')\n document.body.appendChild(line)\n state.line = line\n return line\n}\n\nfunction showDropLine(state: DragState, row: HTMLElement, after: boolean): void {\n const line = dropLine(state)\n const rect = row.getBoundingClientRect()\n line.style.top = `${dropLineTop(row, after)}px`\n line.style.left = `${rect.left}px`\n line.style.width = `${rect.width}px`\n line.style.display = 'block'\n line.setAttribute(DROP_SIDE_ATTR, after ? 'after' : 'before')\n}\n\nfunction hideDropLine(state: DragState): void {\n if (state.line === undefined) return\n state.line.style.display = 'none'\n}\n\n/** End whatever drag is in flight, and undo it unless it landed. */\nfunction finishDrag(state: DragState): void {\n const session = state.drag\n state.drag = undefined\n if (session === undefined) return\n setScrollStep(state, session, 0)\n hideDropLine(state)\n session.row.classList.remove(DRAGGING_CLASS)\n clearDropMarks(session.list)\n // A drag that never landed (Esc, or a drop outside the list) is abandoned:\n // the rows slide back, and nothing is stored.\n if (!session.dropped) slideIntoPlace(session.list, () => { placeRows(session.list, session.before) })\n}\n\nfunction startDrag(row: HTMLElement, event: DragEvent, adapter: DragAdapter, state: DragState): void {\n // A drag that began on a row's own controls is not a reorder: cancel ours and\n // leave the control to the browser.\n if (interactiveTarget(event.target)) {\n event.preventDefault()\n return\n }\n const list = listOf(row)\n const id = adapter.idOf(row)\n if (list === undefined || id === undefined) {\n event.preventDefault()\n return\n }\n const session: DragSession = {\n row,\n list,\n before: visualRows(list),\n scroller: scrollHostOf(list),\n adapter,\n scrollStep: 0,\n frame: undefined,\n dropped: false,\n }\n state.drag = session\n row.classList.add(DRAGGING_CLASS)\n const ghost = makeGhost(row, adapter.labelOf(row))\n if (Number.isFinite(event.clientX) && Number.isFinite(event.clientY)) {\n ghost.style.left = `${event.clientX}px`\n ghost.style.top = `${event.clientY}px`\n }\n document.body.appendChild(ghost)\n // A synthesized drag (a test, or a script) may carry no transfer at all;\n // there is then nothing to hand the chip to, but the session \u2014 the part that\n // actually moves rows \u2014 is already in place.\n const dataTransfer: DataTransfer | null | undefined = event.dataTransfer\n if (dataTransfer != null) {\n try {\n dataTransfer.effectAllowed = 'move'\n dataTransfer.setData('text/plain', id)\n dataTransfer.setDragImage(ghost, Math.round(ghost.offsetWidth / 2), Math.round(ghost.offsetHeight / 2))\n } catch {\n // Not every engine lets a synthesized drag choose its own image.\n }\n }\n // The snapshot is taken while the drag starts, so the chip only has to exist\n // for this task \u2014 leaving it in the DOM would outlive its usefulness.\n window.setTimeout(() => { ghost.remove() }, 0)\n}\n\n/** Move the carried row under the pointer, and say where it would land. */\nfunction dragOver(list: HTMLElement, event: DragEvent, state: DragState): void {\n const session = state.drag\n if (session === undefined || session.list !== list) return\n const target = event.target\n const over = target instanceof Element ? session.adapter.rowOf(target, list) : undefined\n const rows = visualRows(list)\n const hovered = over !== undefined && rows.includes(over)\n ? over\n : nearestRow(rows, event.clientY)\n if (hovered === undefined) return\n event.preventDefault()\n const dataTransfer: DataTransfer | null | undefined = event.dataTransfer\n if (dataTransfer != null) dataTransfer.dropEffect = 'move'\n const after = isAfter(hovered, event.clientY)\n const slot = dropIndex(rows, session.row, hovered, after)\n // A dragover repeats for as long as the pointer rests, and re-placing a row\n // that would not move is what makes the list flicker under the cursor.\n if (slot !== rows.indexOf(session.row)) {\n slideIntoPlace(list, () => { placeRows(list, moveToIndex(rows, session.row, slot)) })\n }\n clearDropMarks(list)\n hovered.classList.add(DROP_CLASS)\n hovered.setAttribute(DROP_SIDE_ATTR, after ? 'after' : 'before')\n showDropLine(state, hovered, after)\n setScrollStep(state, session, session.scroller === undefined ? 0 : edgeScrollStep(session.scroller, event.clientY))\n}\n\n/** The delegated half of the drag: one pair of listeners per list. */\nfunction wireList(list: HTMLElement, state: DragState): void {\n if (state.lists.has(list)) return\n state.lists.add(list)\n list.addEventListener('dragover', (event: DragEvent) => { dragOver(list, event, state) })\n list.addEventListener('drop', (event: DragEvent) => {\n const session = state.drag\n if (session === undefined || session.list !== list) return\n event.preventDefault()\n session.dropped = true\n // Land the order on the ids the rows actually carry: a row with no id has\n // no name to store, and is left out of the stored sequence.\n session.adapter.commit(list)\n })\n}\n\n/** Make one drag source start (and end) a drag, once. */\nfunction wireSource(source: HTMLElement, row: HTMLElement, adapter: DragAdapter, state: DragState): void {\n if (state.sources.has(source)) return\n state.sources.add(source)\n if (adapter.seatDrags) {\n source.setAttribute(HEAD_ATTR, '1')\n source.setAttribute('draggable', 'true')\n }\n source.addEventListener('dragstart', (event: DragEvent) => { startDrag(row, event, adapter, state) })\n source.addEventListener('dragend', () => { finishDrag(state) })\n}\n\n/** The grip's own two jobs: it is not a row action, and it can reorder by keyboard. */\nfunction wireGrip(grip: HTMLElement, row: HTMLElement, adapter: DragAdapter, state: DragState): void {\n grip.addEventListener('click', (event: MouseEvent) => {\n event.preventDefault()\n event.stopPropagation()\n })\n grip.addEventListener('keydown', (event: KeyboardEvent) => {\n if (event.key !== 'ArrowUp' && event.key !== 'ArrowDown') return\n const list = listOf(row)\n if (list === undefined) return\n const current = visualRows(list)\n const index = current.indexOf(row)\n if (index < 0) return\n const target = index + (event.key === 'ArrowUp' ? -1 : 1)\n if (target < 0 || target >= current.length) return\n event.preventDefault()\n slideIntoPlace(list, () => { placeRows(list, moveToIndex(current, row, target)) })\n adapter.commit(list)\n // A keyboard move that lost focus would make the next press a no-op the\n // user cannot explain.\n grip.focus()\n })\n void state\n}\n\n/** Keep one grip's copy in step with the row it belongs to. */\nfunction syncGrip(grip: HTMLElement, row: HTMLElement, id: string, adapter: DragAdapter): void {\n const label = adapter.gripLabel(row, id)\n const hint = adapter.gripHint()\n if (grip.getAttribute(HANDLE_ATTR) !== id) grip.setAttribute(HANDLE_ATTR, id)\n if (grip.getAttribute('aria-label') !== label) grip.setAttribute('aria-label', label)\n if (grip.getAttribute('title') !== hint) grip.setAttribute('title', hint)\n}\n\n/** Mount (or refresh) one row's grip, and wire whatever drags for that row. */\nfunction ensureGrip(\n row: HTMLElement,\n seat: HTMLElement,\n id: string,\n adapter: DragAdapter,\n state: DragState,\n): void {\n const existing = seat.querySelector<HTMLElement>(`[${HANDLE_ATTR}]`)\n if (existing !== null) {\n // Already mounted (a rescan, or a language switch): refresh the copy.\n syncGrip(existing, row, id, adapter)\n return\n }\n adapter.decorate?.(row, seat)\n const grip = document.createElement('span')\n grip.className = 'bre-drag-grip'\n grip.setAttribute('role', 'button')\n grip.setAttribute('tabindex', '0')\n grip.setAttribute('draggable', 'true')\n grip.appendChild(gripIcon())\n syncGrip(grip, row, id, adapter)\n wireGrip(grip, row, adapter, state)\n wireSource(adapter.seatDrags ? seat : grip, row, adapter, state)\n seat.insertBefore(grip, seat.firstChild)\n}\n\n/**\n * One pass over one list: mount a grip in every nameable row, wire the list,\n * and place the rows in the order the adapter's preference asks for.\n *\n * Idempotent by DOM presence \u2014 the same guard the editor injector uses, which\n * keeps the insertion from re-triggering the scan that found it.\n */\nexport function reconcileRows(\n root: HTMLElement,\n list: HTMLElement,\n adapter: DragAdapter,\n state: DragState,\n): void {\n if (!root.isConnected) return\n wireList(list, state)\n for (const row of rowElements(list)) {\n const id = adapter.idOf(row)\n if (id === undefined) continue\n const seat = adapter.seatOf(row)\n if (seat === undefined) continue\n ensureGrip(row, seat, id, adapter, state)\n }\n // Mid-drag the screen order is the user's, not the preference's: writing it\n // now would snap the row back under the pointer.\n if (state.drag !== undefined) return\n applyIdOrder(list, adapter.order(orderedIds(list, adapter.idOf)), adapter.idOf)\n}\n\n/**\n * Undo everything the engine put on the page: the grips, the draggable seats,\n * the insertion line, the chip, and the classes a drag left behind. The inline\n * `order` of a row is the list owner's to clear (it knows which rows are its\n * own), because the engine's grip removal is global by attribute.\n *\n * A plugin disable or an HMR replace unmounts React trees on its own, but the\n * grips and the draggable seats are raw DOM with listeners attached to\n * themselves \u2014 left behind they would keep reordering rows (and persisting that\n * order) on behalf of a fiber that no longer exists.\n */\nexport function teardownDrags(state: DragState): void {\n state.drag = undefined\n for (const grip of Array.from(document.querySelectorAll<HTMLElement>(`[${HANDLE_ATTR}]`))) grip.remove()\n for (const head of Array.from(document.querySelectorAll<HTMLElement>(`[${HEAD_ATTR}]`))) {\n head.removeAttribute(HEAD_ATTR)\n head.removeAttribute('draggable')\n }\n for (const row of Array.from(document.querySelectorAll<HTMLElement>(`.${DRAGGING_CLASS}, .${DROP_CLASS}`))) {\n row.classList.remove(DRAGGING_CLASS, DROP_CLASS)\n }\n for (const line of Array.from(document.querySelectorAll<HTMLElement>(`.${LINE_CLASS}`))) line.remove()\n for (const ghost of Array.from(document.querySelectorAll<HTMLElement>(`.${GHOST_CLASS}`))) ghost.remove()\n state.line = undefined\n}\n", "/**\n * Provider-row drag reordering for the official Models settings page.\n *\n * The official section renders one `<li>` per provider inside a `<ul>` that is\n * already a flex column, and React owns those children \u2014 so rows are never\n * moved in the DOM. Each row is given a CSS `order` instead, which reorders the\n * screen without touching React's child list, and the resulting id sequence is\n * stored by `../provider-order.js` for the composer model switcher to read.\n *\n * The interaction itself \u2014 the grip, the drag session, the insertion line, the\n * FLIP slide, the edge auto-scroll \u2014 lives in `./row-drag.js`, which the model\n * rows inside a provider use too. This module is the provider list's adapter\n * onto it: it names each row (the plugin's `data-bre-provider` stamp, or the\n * official Edit button's label parsed back through `editProvider`), mounts the\n * grip in the row's own header (`[class*=\"rowHead\"]`), and lets the WHOLE header\n * drag \u2014 a 18px grip is a poor thing to have to hit. The card's own buttons keep\n * their behaviour, because the engine cancels a drag that begins on one, and\n * everything the page renders below the header is untouched.\n *\n * @module dsh-model-think-level/client/injection/provider-order-drag\n */\n\nimport type { HostLabels } from './models-page-editor.js'\nimport { resolveProviderOrder, setProviderOrder } from '../provider-order.js'\nimport {\n applyIdOrder,\n clearRowOrder,\n createDragState,\n orderedIds,\n reconcileRows,\n rowElements,\n teardownDrags,\n} from './row-drag.js'\nimport type { DragAdapter, DragState } from './row-drag.js'\n\n// The engine's helpers are part of this module's surface: the provider tests\n// exercise them here, and callers that only know this list should not have to\n// reach into the shared engine to reach them.\nexport {\n dropIndex,\n dropLineTop,\n edgeScrollStep,\n makeGhost,\n nearestRow,\n placeRows,\n rowElements,\n scrollHostOf,\n visualRows,\n} from './row-drag.js'\n\n/** Per-scan bookkeeping (one instance lives in `models-page.ts`). */\nexport type ProviderOrderState = DragState\n\nexport interface ProviderOrderDeps {\n /** The plugin's own translator, for the grip's label and tooltip. */\n t: (key: string, params?: Record<string, string | number>) => string\n /** The official `settings.models` label sets, resolved fresh on every scan. */\n labels: () => HostLabels\n}\n\n/** Marks the row with the provider id it belongs to (ours to add and to read). */\nconst ROW_ID_ATTR = 'data-bre-row-provider'\n/** The `{provider}` slot of the official `providerCopy` templates. */\nconst PROVIDER_TOKEN = '{provider}'\n/** Class-name fragments of the official provider rows, probed by substring. */\nconst ROW_CLASS_PROBES = ['rowCard', 'setupCard'] as const\n\nexport function createProviderOrderState(): ProviderOrderState {\n return createDragState()\n}\n\n/**\n * The official provider-row list: the `<ul>` that holds the row cards. Probed by\n * class-name fragment because the page's class names are CSS-module hashes\n * (`_3nPmjq_rows` today, something else tomorrow).\n */\nexport function rowsListOf(root: HTMLElement): HTMLElement | undefined {\n for (const probe of ROW_CLASS_PROBES) {\n for (const hit of Array.from(root.querySelectorAll<HTMLElement>(`[class*=\"${probe}\"]`))) {\n const parent = hit.parentElement\n if (parent === null || parent.tagName !== 'UL') continue\n if (rowElements(parent).length === 0) continue\n return parent\n }\n }\n return undefined\n}\n\n/**\n * The row's own header \u2014 the seat the grip is mounted into, and the drag source.\n * Exported because the enable switch mounts into the same seat and must agree\n * with this module about where that seat is.\n */\nexport function rowHeadOf(row: HTMLElement): HTMLElement | undefined {\n return row.querySelector<HTMLElement>('[class*=\"rowHead\"]') ?? undefined\n}\n\n/**\n * The row's displayed provider name, for the grip's label, the drag chip and\n * the switch's label.\n */\nexport function rowNameOf(row: HTMLElement): string | undefined {\n const name = row.querySelector<HTMLElement>('[class*=\"rowName\"]')?.textContent?.trim()\n return name !== undefined && name.length > 0 ? name : undefined\n}\n\n/** The `{provider}` slot split out of one official template. */\nfunction templateParts(template: string): { prefix: string; suffix: string } | undefined {\n const at = template.indexOf(PROVIDER_TOKEN)\n if (at < 0) return undefined\n return { prefix: template.slice(0, at), suffix: template.slice(at + PROVIDER_TOKEN.length) }\n}\n\n/**\n * Recover the provider id from an official `providerTargetLabel`: `Name (route)`\n * \u2014 the form used whenever the display name differs from the route \u2014 or the\n * bare route when the two are equal. The id is the LAST parenthesized group, so\n * a display name that itself carries parentheses still splits correctly.\n */\nexport function idFromTargetLabel(label: string): string | undefined {\n const trimmed = label.trim()\n if (trimmed.length === 0) return undefined\n const parenthesized = /^(.*) \\(([^()]*)\\)$/.exec(trimmed)\n if (parenthesized !== null) {\n const route = parenthesized[2] as string\n return route.length === 0 ? undefined : route\n }\n // A route whose display name IS the route is the whole label \u2014 and a route\n // never carries whitespace.\n return /\\s/.test(trimmed) ? undefined : trimmed\n}\n\n/** The official card roots: a provider row, a row awaiting its first key, and the add card. */\nconst CARD_ROOT_PROBE = '[class*=\"rowCard\"], [class*=\"setupCard\"], [class*=\"addCard\"]'\n\n/**\n * Whether a control belongs to this plugin (or any other `bre-`-prefixed seat)\n * rather than to the official page: any mark of ours between the control and its\n * own card means the control is ours.\n *\n * The walk deliberately STOPS at the card. The tab takeover paints `bre-tabbed`\n * on the OFFICIAL row while that card's editor is open (`./editor-tabs.ts`), so\n * an ancestor-wide `[class*=\"bre-\"]` test reads the row's own Edit button as\n * ours; `providerIdOfRow` then finds no id, and every pass keyed on the row id\n * skips exactly the row the user has open. The built-in rows carry no\n * `data-bre-provider` stamp to fall back on, so the seat, the switch and the\n * drag handle all disappear with the id.\n */\nfunction isForeign(node: Element): boolean {\n const card = node.closest(CARD_ROOT_PROBE)\n for (let el: Element | null = node; el !== null && el !== card; el = el.parentElement) {\n if (el.matches('[data-plugin], [class*=\"bre-\"]')) return true\n }\n return false\n}\n\n/**\n * The provider id of one row: the stamp our own slot carries when the row has\n * one (every llm-pi-ai row does), else the official Edit button's aria-label\n * parsed back through the localized `editProvider` template.\n */\nexport function providerIdOfRow(row: HTMLElement, labels: HostLabels): string | undefined {\n const stamped = row.querySelector<HTMLElement>('[data-bre-provider]')?.getAttribute('data-bre-provider')\n if (typeof stamped === 'string' && stamped.length > 0) return stamped\n for (const template of labels.editProvider) {\n const parts = templateParts(template)\n if (parts === undefined) continue\n for (const button of Array.from(row.querySelectorAll<HTMLElement>('button[aria-label]'))) {\n if (isForeign(button)) continue\n const label = button.getAttribute('aria-label') ?? ''\n if (label.length <= parts.prefix.length + parts.suffix.length) continue\n if (!label.startsWith(parts.prefix) || !label.endsWith(parts.suffix)) continue\n const id = idFromTargetLabel(label.slice(parts.prefix.length, label.length - parts.suffix.length))\n if (id !== undefined) return id\n }\n }\n return undefined\n}\n\n/** The id this module stamped on a row, if any. */\nfunction rowIdOf(row: HTMLElement): string | undefined {\n const value = row.getAttribute(ROW_ID_ATTR)\n return value === null || value.length === 0 ? undefined : value\n}\n\n/** The stamped ids of the rows that have one, in the order they screen. */\nexport function orderedRowIds(list: HTMLElement): string[] {\n return orderedIds(list, rowIdOf)\n}\n\n/** Place the rows in the stored order; rows we could not name are left alone. */\nexport function applyRowOrder(list: HTMLElement, ids: readonly string[]): void {\n applyIdOrder(list, ids, rowIdOf)\n}\n\n/**\n * One pass over the document: name every provider row, mount a grip in each\n * row's header, make that header a drag source, and place the rows in the order\n * the preference asks for.\n *\n * Runs on every scan (the official editor's own pass bails out when no card is\n * open, so this cannot ride along with it) and is idempotent by DOM presence \u2014\n * the same guard the editor injector uses, which keeps the insertion from\n * re-triggering the scan that found it.\n */\nexport function reconcileProviderOrder(\n root: HTMLElement,\n deps: ProviderOrderDeps,\n state: ProviderOrderState,\n): void {\n if (!root.isConnected) return\n const list = rowsListOf(root)\n if (list === undefined) return\n const labels = deps.labels()\n // Name the rows before the engine looks at them: a row without a stamp has no\n // id, and a row the engine cannot name gets no grip and is never moved.\n for (const row of rowElements(list)) {\n const id = providerIdOfRow(row, labels)\n if (id === undefined) {\n row.removeAttribute(ROW_ID_ATTR)\n continue\n }\n if (rowIdOf(row) !== id) row.setAttribute(ROW_ID_ATTR, id)\n }\n reconcileRows(root, list, providerAdapter(deps), state)\n}\n\n/** The provider list as the engine needs to see it. */\nfunction providerAdapter(deps: ProviderOrderDeps): DragAdapter {\n return {\n // A provider row is the `<li>` a descendant belongs to \u2014 the engine checks\n // that it really is one of the list's own rows.\n rowOf: (node) => {\n const row = node.closest('li')\n return row instanceof HTMLElement ? row : undefined\n },\n idOf: rowIdOf,\n labelOf: rowNameOf,\n order: (ids) => resolveProviderOrder(ids),\n commit: (list) => { setProviderOrder(orderedRowIds(list)) },\n seatOf: rowHeadOf,\n // The header drags, not just the grip: the header is the row's own seat, and\n // a provider card is a large target that no text selection competes for.\n seatDrags: true,\n gripLabel: (row, id) => deps.t('providerReorderAria', { provider: rowNameOf(row) ?? id }),\n gripHint: () => deps.t('providerReorderHint'),\n }\n}\n\n/**\n * Undo everything this module put on the official page. A plugin disable or an\n * HMR replace unmounts React trees on its own, but the grips, the draggable\n * headers and the insertion line are raw DOM with listeners attached to\n * themselves \u2014 left behind they would keep reordering the rows (and persisting\n * that order) on behalf of a fiber that no longer exists.\n */\nexport function teardownProviderOrder(state: ProviderOrderState): void {\n teardownDrags(state)\n const stamped = Array.from(document.querySelectorAll<HTMLElement>(`[${ROW_ID_ATTR}]`))\n clearRowOrder(stamped)\n for (const row of stamped) row.removeAttribute(ROW_ID_ATTR)\n}\n", "/**\n * Per-provider enable switch for the official Models settings page.\n *\n * A provider that is switched off stays listed \u2014 that is the only place it can\n * be switched back on \u2014 but leaves the composer's provider column, which reads\n * the same store (`../provider-enabled.js`). The stored value is the set of\n * DISABLED ids, so an unknown or newly added route is enabled by default.\n *\n * The switch is a `role=\"switch\"` button appended to the row's action group (or\n * to the header when the official row has no action group), which leaves the\n * header's own flex layout \u2014 identity on one side, actions on the other \u2014 with\n * the same children it had. `role=\"switch\"` is already in the drag module's list\n * of controls that own their gesture, so a drag started on the switch is refused\n * and the click can only mean \"toggle\".\n *\n * Nothing here waits for the next scan to look right: the debounced scan is\n * 120ms away, and a switch whose knob only moves a beat later reads as a missed\n * click \u2014 so the click paints the new state itself.\n *\n * @module dsh-model-think-level/client/injection/provider-toggle\n */\n\nimport { isProviderEnabled, setProviderEnabled } from '../provider-enabled.js'\nimport type { HostLabels } from './models-page-editor.js'\nimport { providerIdOfRow, rowHeadOf, rowNameOf, rowsListOf } from './provider-order-drag.js'\n\n/** Marks the switch, and carries the provider id it governs. */\nconst SWITCH_ATTR = 'data-bre-provider-switch'\n\n/** Set on a row whose provider is switched off. */\nconst OFF_CLASS = 'bre-row-disabled'\n\n/** Per-scan bookkeeping (one instance lives in `models-page.ts`). */\nexport interface ProviderToggleState {\n /** Switches that already carry their click listener. */\n readonly switches: WeakSet<Element>\n}\n\nexport interface ProviderToggleDeps {\n /** The plugin's own translator, for the switch's label and tooltip. */\n t: (key: string, params?: Record<string, string | number>) => string\n /** The official `settings.models` label sets, resolved fresh on every scan. */\n labels: () => HostLabels\n}\n\nexport function createProviderToggleState(): ProviderToggleState {\n return { switches: new WeakSet() }\n}\n\n/**\n * Visit every provider row and make sure it shows a switch carrying its own\n * state. Idempotent: a row that already has one is only relabelled, which is\n * also how a language switch and an external toggle reach the page.\n */\nexport function reconcileProviderEnabled(\n root: HTMLElement,\n deps: ProviderToggleDeps,\n state: ProviderToggleState,\n): void {\n if (!root.isConnected) return\n const list = rowsListOf(root)\n if (list === undefined) return\n const labels = deps.labels()\n for (const child of Array.from(list.children)) {\n if (!(child instanceof HTMLElement)) continue\n // A row we cannot name (an unexpected card shape) is left alone: switching\n // off an unknown id would hide a provider the user never chose.\n const id = providerIdOfRow(child, labels)\n if (id === undefined) continue\n const head = rowHeadOf(child)\n if (head === undefined) continue\n ensureSwitch(child, head, id, deps, state)\n child.classList.toggle(OFF_CLASS, !isProviderEnabled(id))\n }\n}\n\n/** Remove every switch and every dimmed row. */\nexport function teardownProviderEnabled(): void {\n for (const switchButton of Array.from(document.querySelectorAll<HTMLElement>(`[${SWITCH_ATTR}]`))) {\n switchButton.remove()\n }\n for (const row of Array.from(document.querySelectorAll<HTMLElement>(`.${OFF_CLASS}`))) {\n row.classList.remove(OFF_CLASS)\n }\n}\n\nfunction ensureSwitch(\n row: HTMLElement,\n head: HTMLElement,\n id: string,\n deps: ProviderToggleDeps,\n state: ProviderToggleState,\n): void {\n const existing = head.querySelector<HTMLElement>(`[${SWITCH_ATTR}]`)\n if (existing !== null) {\n syncSwitch(existing, row, id, deps)\n return\n }\n const control = document.createElement('button')\n control.className = 'bre-provider-switch'\n control.setAttribute('type', 'button')\n control.setAttribute('role', 'switch')\n control.setAttribute(SWITCH_ATTR, id)\n syncSwitch(control, row, id, deps)\n wireSwitch(control, row, state)\n const actions = head.querySelector<HTMLElement>('[class*=\"rowActions\"]')\n // The action group is `margin-left: auto` and holds the card's own buttons:\n // the switch goes in FRONT of them, so the destructive control stays last and\n // the header keeps the children it had. Without a group the header itself\n // takes it, which is the same seat the grip uses.\n if (actions === null) head.appendChild(control)\n else actions.insertBefore(control, actions.firstChild)\n}\n\n/** Make the button agree with the store: its name, its tooltip, its state. */\nfunction syncSwitch(\n control: HTMLElement,\n row: HTMLElement,\n id: string,\n deps: ProviderToggleDeps,\n): void {\n const label = deps.t('providerToggleAria', { provider: rowNameOf(row) ?? id })\n const hint = deps.t('providerToggleHint')\n const checked = isProviderEnabled(id) ? 'true' : 'false'\n if (control.getAttribute(SWITCH_ATTR) !== id) control.setAttribute(SWITCH_ATTR, id)\n if (control.getAttribute('aria-checked') !== checked) control.setAttribute('aria-checked', checked)\n if (control.getAttribute('aria-label') !== label) control.setAttribute('aria-label', label)\n if (control.getAttribute('title') !== hint) control.setAttribute('title', hint)\n}\n\nfunction wireSwitch(control: HTMLElement, row: HTMLElement, state: ProviderToggleState): void {\n if (state.switches.has(control)) return\n state.switches.add(control)\n control.addEventListener('click', (event: MouseEvent) => {\n // The row is itself a button on the official page (it opens the editor) and\n // the header drags: neither may read this click as its own.\n event.preventDefault()\n event.stopPropagation()\n const id = control.getAttribute(SWITCH_ATTR)\n if (id === null) return\n const next = !isProviderEnabled(id)\n setProviderEnabled(id, next)\n // Paint it now \u2014 see the module note: the scan that would have done this is\n // debounced, and the switch must feel like a switch.\n control.setAttribute('aria-checked', next ? 'true' : 'false')\n row.classList.toggle(OFF_CLASS, !next)\n })\n}\n", "/**\n * The reserved delete seat on a provider row of the official Models page.\n *\n * The official row renders its destructive button only for a provider the host\n * still considers removable (`row.removable`); a built-in provider such as\n * deepseek renders nothing there at all. The action group is `margin-left: auto`,\n * so the rows that keep the button are wider than the rows that do not and the\n * whole column of buttons lines up raggedly against the card's right edge.\n *\n * The seat is therefore kept warm: a row the host left without a delete button\n * gets a disabled one of the same metrics, which is enough for every row's\n * actions to start at the same place. It is `disabled`, so it takes no click,\n * no focus and no keyboard activation \u2014 the button is the alignment, not an\n * action \u2014 and it still owns its gesture as far as the drag engine is concerned,\n * because that engine refuses a drag beginning on any `button`.\n *\n * Its word is the host's own: the label is read off a delete button the page\n * still renders, so the odd row out is not the one row written in another\n * language. A page that renders none at all (every provider built in) falls back\n * to this plugin's dictionary.\n *\n * @module dsh-model-think-level/client/injection/provider-delete-seat\n */\n\nimport type { HostLabels } from './models-page-editor.js'\nimport { providerIdOfRow, rowHeadOf, rowNameOf, rowsListOf } from './provider-order-drag.js'\n\n/** Marks the seat, and carries the provider id it stands for. */\nconst SEAT_ATTR = 'data-bre-provider-delete'\n\n/** The row's action group, probed by class-name fragment (a CSS-module hash). */\nconst ACTIONS_PROBE = '[class*=\"rowActions\"]'\n\n/** The host's own destructive control, probed the same way. */\nconst OFFICIAL_DELETE_PROBE = '[class*=\"dangerButton\"]'\n\n/** The seat's class: the metrics it borrows from the official row button. */\nconst SEAT_CLASS = 'bre-provider-delete'\n\nexport interface ProviderDeleteDeps {\n /** The plugin's own translator, for the tooltip and the fallback label. */\n t: (key: string, params?: Record<string, string | number>) => string\n /** The official `settings.models` label sets, resolved fresh on every scan. */\n labels: () => HostLabels\n}\n\n/**\n * Give every provider row the delete seat the host left out. Idempotent: a row\n * that already has one is only relabelled, and a row whose provider became\n * removable hands the seat back to the host's own button.\n */\nexport function reconcileProviderDeleteSeat(root: HTMLElement, deps: ProviderDeleteDeps): void {\n if (!root.isConnected) return\n const list = rowsListOf(root)\n if (list === undefined) return\n const labels = deps.labels()\n const label = officialDeleteLabel(list) ?? deps.t('providerRemoveDisabled')\n for (const child of Array.from(list.children)) {\n if (!(child instanceof HTMLElement)) continue\n // Same rule as the switch pass: a row we cannot name is a row we leave\n // exactly as the host rendered it.\n const id = providerIdOfRow(child, labels)\n if (id === undefined) continue\n const head = rowHeadOf(child)\n if (head === undefined) continue\n const actions = head.querySelector<HTMLElement>(ACTIONS_PROBE)\n // No action group means nothing to line up with: the row already renders its\n // controls somewhere this module does not claim.\n if (actions === null) continue\n const existing = actions.querySelector<HTMLElement>(`[${SEAT_ATTR}]`)\n // A removable provider carries the host's own button: the seat stands down\n // rather than doubling it, so a settings reload that flips `removable` \u2014 or\n // an added provider \u2014 is followed on the next scan.\n if (actions.querySelector(OFFICIAL_DELETE_PROBE) !== null) {\n if (existing !== null) existing.remove()\n continue\n }\n const seat = existing ?? createSeat()\n // The destructive control is the row's LAST action. A seat that is not there\n // yet \u2014 or one the host re-rendered its own children in front of \u2014 is put\n // back at the end.\n if (actions.lastElementChild !== seat) actions.appendChild(seat)\n syncSeat(seat, id, label, deps.t('providerRemoveLocked', { provider: rowNameOf(child) ?? id }))\n }\n}\n\n/** Take every seat out again (plugin dispose, HMR teardown). */\nexport function teardownProviderDeleteSeats(): void {\n for (const seat of Array.from(document.querySelectorAll<HTMLElement>(`[${SEAT_ATTR}]`))) {\n seat.remove()\n }\n}\n\n/**\n * The word the host uses for this action, off any row that still renders one.\n * The page's own copy beats a translation of ours: it is the same word in the\n * same language, at the width the neighbouring buttons are drawn at.\n */\nfunction officialDeleteLabel(list: HTMLElement): string | undefined {\n const button = list.querySelector<HTMLElement>(`${ACTIONS_PROBE} ${OFFICIAL_DELETE_PROBE}`)\n const text = button?.textContent?.trim() ?? ''\n return text.length > 0 ? text : undefined\n}\n\nfunction createSeat(): HTMLElement {\n const seat = document.createElement('button')\n seat.className = SEAT_CLASS\n seat.setAttribute('type', 'button')\n // `disabled` is the STATE, not the styling: the seat must not take a click, a\n // focus or a keyboard activation. Its dimmed look is the host's own disabled\n // treatment (see STYLES).\n seat.setAttribute('disabled', '')\n return seat\n}\n\n/** Make the seat stand for this row, compare-before-write on every field. */\nfunction syncSeat(seat: HTMLElement, id: string, label: string, hint: string): void {\n if (seat.getAttribute(SEAT_ATTR) !== id) seat.setAttribute(SEAT_ATTR, id)\n // Guarded: the page's observer watches childList, and replacing the seat's\n // text node on every scan would make each pass feed the next one.\n if (seat.textContent !== label) seat.textContent = label\n if (seat.getAttribute('aria-label') !== hint) seat.setAttribute('aria-label', hint)\n if (seat.getAttribute('title') !== hint) seat.setAttribute('title', hint)\n}\n", "/**\n * Model-row drag reordering inside one provider's editor on the official Models\n * settings page.\n *\n * The official editor renders a provider's models as\n * `div.modelList > div.modelEntry > div.modelRow` (a four-column grid: id,\n * display name, and two icon buttons), straight from\n * `models.map((model, index) => \u2026)`. React owns those children, so \u2014 exactly as\n * for the provider rows \u2014 a model is moved with a CSS `order`, and the id\n * sequence that results is stored per provider by `../model-order.js`.\n *\n * Two differences from the provider list, and both are in the adapter:\n * - the row is a grid of INPUTS, so only the grip drags. A drag that began on\n * the row body has to stay a text selection, which is why `seatDrags` is\n * false here and true for a provider header;\n * - the model list lives inside the provider's own `li.rowCard`, so the\n * provider id \u2014 and with it which stored sequence to read and to write \u2014 is\n * recovered from the enclosing row on every call rather than captured, and\n * a list whose provider cannot be named is left completely alone.\n *\n * A model's id is the value of its own \"Model ID\" input, not its position: the\n * official `aria-label`s carry `position` (`Model ID 1`), and a reorder that\n * renumbered them would rename every field on the page. A row whose id input is\n * still blank (a model just added, not yet named) gets no grip and is never\n * moved.\n *\n * @module dsh-model-think-level/client/injection/model-order-drag\n */\n\nimport { orderedIds, reconcileRows, rowElements, teardownDrags, createDragState, clearRowOrder } from './row-drag.js'\nimport type { DragAdapter, DragState } from './row-drag.js'\nimport { setModelOrder, resolveModelOrder, modelOrderOf } from '../model-order.js'\nimport { inputValueByLabel, type HostLabels } from './models-page-editor.js'\nimport { providerIdOfRow } from './provider-order-drag.js'\n\n/** Marks the entry with the model id it holds (ours to add and to read). */\nconst ROW_ID_ATTR = 'data-bre-row-model'\n/**\n * Marks the grid we widened to make room for the grip.\n *\n * Deliberately NOT `bre-model-row`: that name is already the plugin's own\n * composer row (`.bre-model-row` in `../styles.ts`), and a class borrowed onto\n * the host's grid would drag that row's `padding`, `gap`, `min-height` and\n * hover fill along with it.\n */\nconst MODEL_ROW_CLASS = 'bre-model-grid'\n\n/** Per-scan bookkeeping (one instance lives in `models-page.ts`). */\nexport type ModelOrderState = DragState\n\nexport interface ModelOrderDeps {\n /** The plugin's own translator, for the grip's label and tooltip. */\n t: (key: string, params?: Record<string, string | number>) => string\n /** The official `settings.models` label sets, resolved fresh on every scan. */\n labels: () => HostLabels\n}\n\nexport function createModelOrderState(): ModelOrderState {\n return createDragState()\n}\n\n/**\n * Every model list under `root`.\n *\n * Probed by class-name fragment, like the official anchors elsewhere: the\n * bundle's names are CSS-module hashes. The fragment alone is too loose \u2014\n * `modelListHead` and any future sibling share it \u2014 so a hit only counts when\n * its DIRECT children are the model entries, which is what the official\n * `.modelList { display: flex; flex-direction: column; gap: 8px }` lays out.\n */\nexport function modelListsOf(root: HTMLElement): HTMLElement[] {\n const lists: HTMLElement[] = []\n for (const hit of Array.from(root.querySelectorAll<HTMLElement>('[class*=\"modelList\"]'))) {\n const entries = rowElements(hit).filter(child => child.matches('[class*=\"modelEntry\"]'))\n if (entries.length === 0) continue\n lists.push(hit)\n }\n return lists\n}\n\n/** The model id this module stamped on an entry, if any. */\nfunction modelIdOf(entry: HTMLElement): string | undefined {\n const value = entry.getAttribute(ROW_ID_ATTR)\n return value === null || value.length === 0 ? undefined : value\n}\n\n/** The provider whose editor holds `list` \u2014 the key its models' order is stored under. */\nfunction providerOfList(list: HTMLElement, deps: ModelOrderDeps): string | undefined {\n const row = list.closest('li')\n if (row === null) return undefined\n return providerIdOfRow(row, deps.labels())\n}\n\n/** What the user sees for one model: its display name, else its id. */\nfunction modelLabelOf(entry: HTMLElement, deps: ModelOrderDeps): string | undefined {\n const name = inputValueByLabel(entry, deps.labels().modelName)\n if (name.length > 0) return name\n return modelIdOf(entry)\n}\n\n/** One model list, as the engine needs to see it. */\nfunction modelAdapter(list: HTMLElement, deps: ModelOrderDeps): DragAdapter {\n return {\n // A model row is the `modelEntry` a descendant belongs to; the engine checks\n // that it really is one of this list's own rows.\n rowOf: (node) => {\n const entry = node.closest('[class*=\"modelEntry\"]')\n return entry instanceof HTMLElement ? entry : undefined\n },\n idOf: modelIdOf,\n labelOf: (entry) => modelLabelOf(entry, deps),\n // Read and write the provider at CALL time: the row this list sits in is the\n // only authority on which sequence applies, and a rescan can move the list\n // to another provider's card.\n order: (ids) => {\n const provider = providerOfList(list, deps)\n return provider === undefined ? ids : resolveModelOrder(provider, ids)\n },\n commit: (target) => {\n const provider = providerOfList(target, deps)\n if (provider === undefined) return\n setModelOrder(provider, orderedIds(target, modelIdOf))\n },\n seatOf: (entry) => entry.querySelector<HTMLElement>('[class*=\"modelRow\"]') ?? undefined,\n // Only the grip drags: the row is a grid of inputs.\n seatDrags: false,\n gripLabel: (entry, id) => deps.t('modelReorderAria', { model: modelLabelOf(entry, deps) ?? id }),\n gripHint: () => deps.t('modelReorderHint'),\n // The official grid is `minmax(0,1.4fr) minmax(0,1fr) auto auto`. The grip\n // becomes a fifth child, so the grid needs a leading track to hold it \u2014\n // added by our own class, because the official rule is a single class\n // selector and ours has to win on specificity, not on load order.\n decorate: (_entry, seat) => { seat.classList.add(MODEL_ROW_CLASS) },\n }\n}\n\n/**\n * One pass over the page: name every model row in every provider editor, mount a\n * grip in its grid, and place the rows in the order the user stored for that\n * provider.\n *\n * A provider whose id cannot be recovered is skipped whole \u2014 reading a sequence\n * that might belong to another provider, or writing one there, is worse than\n * leaving that editor without grips for a scan.\n */\nexport function reconcileModelOrder(\n root: HTMLElement,\n deps: ModelOrderDeps,\n state: ModelOrderState,\n): void {\n if (!root.isConnected) return\n const labels = deps.labels()\n for (const list of modelListsOf(root)) {\n if (providerOfList(list, deps) === undefined) continue\n // Name the rows before the engine looks at them: a row with a blank id input\n // has no name, gets no grip, and is never moved.\n for (const entry of rowElements(list)) {\n const id = inputValueByLabel(entry, labels.modelId)\n if (id.length === 0) {\n entry.removeAttribute(ROW_ID_ATTR)\n continue\n }\n if (modelIdOf(entry) !== id) entry.setAttribute(ROW_ID_ATTR, id)\n }\n reconcileRows(root, list, modelAdapter(list, deps), state)\n }\n}\n\n/**\n * Undo everything this module put on the page. A plugin disable or an HMR\n * replace unmounts React trees on its own, but the grips, the widened grids and\n * the stamps are raw DOM: left behind, a grip would keep reordering rows \u2014 and\n * persisting that order \u2014 on behalf of a fiber that no longer exists.\n */\nexport function teardownModelOrder(state: ModelOrderState): void {\n teardownDrags(state)\n for (const seat of Array.from(document.querySelectorAll<HTMLElement>(`.${MODEL_ROW_CLASS}`))) {\n seat.classList.remove(MODEL_ROW_CLASS)\n }\n const stamped = Array.from(document.querySelectorAll<HTMLElement>(`[${ROW_ID_ATTR}]`))\n clearRowOrder(stamped)\n for (const entry of stamped) entry.removeAttribute(ROW_ID_ATTR)\n}\n\n/** The stored sequence for one provider, for callers that only need to read it. */\nexport function storedModelOrder(provider: string): readonly string[] {\n return modelOrderOf(provider)\n}\n", "/**\n * Per-session model directory resolution and the effort-memory wiretap ledger.\n *\n * Deliberately menu-INDEPENDENT: the directory is a per-session instance, and a\n * brand-new session's durable projection lands without going through `select`,\n * so the watcher is the only thing that can re-apply the remembered level to it\n * \u2014 it must be in place from the session's birth, not from the first time the\n * user opens the model menu (issue #4: every new session otherwise read as\n * Default until a manual pick). The caller therefore drives `ensure()` on every\n * mutation and scan; the per-session cache makes the hot path one snapshot read.\n *\n * @module dsh-model-think-level/client/injection/session-directory\n */\n\nimport { wireEffortMemory } from '../effort-memory.js'\nimport type { ModelDirectoryLike } from '../types.js'\n\n// ---- Host faces (structural; both kernels expose the same shape, see\n// ---- client/types.ts). ----\n\n/**\n * The `sessions` service face this needs: the current session id.\n *\n * Through 0.1.6-alpha.1 the catalog snapshot carried the selection as\n * `current`; 0.1.6-alpha.2 dropped it (navigation moved to the view owner) and\n * reports rows as `byId`, each with its `retainedBy` source counts.\n */\ninterface SessionsLike {\n list?: {\n getSnapshot(): {\n current?: string\n byId?: Record<string, { id?: string; retainedBy?: Record<string, number> } | undefined>\n }\n }\n}\n\n/**\n * The `ui-session` service face (0.1.6-alpha.2+): its scope adapter exposes the\n * main-view binding, whose `key` is the current session id.\n */\ninterface UiSessionLike {\n adapter?: { current?: { getSnapshot(): { key?: string } } }\n}\n\n/** The `modelDirectories` service face this needs. */\ninterface ModelDirectoriesLike {\n directoryFor(sessionId: string): ModelDirectoryLike\n}\n\n/**\n * Resolve the current session id across kernel lines:\n * 1. the list snapshot's `current` (0.1.6-alpha.1 and earlier);\n * 2. the ui-session main binding (0.1.6-alpha.2's authoritative source);\n * 3. the first main-view-retained catalog row \u2014 the same preference\n * ui-session itself applies, used when that service is absent.\n * A non-empty string is required at every step: an empty id would resolve no\n * session scope and only throw inside the resolver.\n * @param sessions - the sessions service face, when mounted.\n * @param uiSession - the ui-session service face, when mounted.\n * @returns the current session id, or undefined in the boot window.\n */\nfunction currentSessionId(\n sessions: SessionsLike | undefined,\n uiSession: UiSessionLike | undefined,\n): string | undefined {\n const snapshot = sessions?.list?.getSnapshot()\n const direct = snapshot?.current\n if (typeof direct === 'string' && direct.length > 0) return direct\n const bound = uiSession?.adapter?.current?.getSnapshot?.()?.key\n if (typeof bound === 'string' && bound.length > 0) return bound\n for (const [key, row] of Object.entries(snapshot?.byId ?? {})) {\n if (row === undefined || (row.retainedBy?.['mainView'] ?? 0) <= 0) continue\n return typeof row.id === 'string' && row.id.length > 0 ? row.id : key\n }\n return undefined\n}\n\n/** The context seats this probes, lazily (services mount after boot). */\ninterface SliderContext {\n get?(name: string): unknown\n}\n\n/** What the tracker needs from its host. */\nexport interface SessionDirectoryDeps {\n /** The client root context, probed lazily for its service seats. */\n ctx: unknown\n /** The configured-pick reader layering the effort chain (issue #4). */\n configuredEffort: (provider: string, model: string) => Promise<string | undefined>\n}\n\n/** The tracker's face. */\nexport interface SessionDirectoryTracker {\n /** Resolve (and wire) the current session's directory; undefined in the boot window. */\n ensure: () => ModelDirectoryLike | undefined\n /** Restore every wrapped directory's original select; the fiber is going away. */\n dispose: () => void\n}\n\n/**\n * Build the session-directory tracker.\n * @param deps - the client context and the configured-pick reader.\n * @returns the tracker's {@link SessionDirectoryTracker} face.\n */\nexport function createSessionDirectoryTracker(deps: SessionDirectoryDeps): SessionDirectoryTracker {\n let current: { sessionId: string; directory: ModelDirectoryLike } | undefined\n // One effort-memory wiretap per directory instance, with each original\n // `select` kept for the fiber disposer to restore. Entries record their\n // session id so the sweep below can release the wiretaps of directories whose\n // session scope is gone (a deleted session's directory must not stay\n // referenced and wrapped until fiber dispose).\n interface WiredDirectory {\n sessionId: string\n restore: () => void\n }\n const wiredDirectories = new Map<ModelDirectoryLike, WiredDirectory>()\n\n /**\n * Release the wiretaps whose session scope is gone. The host resolver\n * deletes a directory's entry when the session scope tears down, and its\n * `directoryFor` then refuses that id (scope and binding are both gone --\n * not the transient unmounted window, which happens before a wiretap can\n * exist): re-resolving that id and comparing identities tells a dead\n * wiretap from one whose session is merely not current. Runs on EVERY\n * directory resolution, cache hits included -- the sweep must not depend\n * on the current session changing.\n */\n const sweepDeadWiretaps = (directories: ModelDirectoriesLike, liveDirectory: ModelDirectoryLike): void => {\n for (const [wired, entry] of wiredDirectories) {\n if (wired === liveDirectory) continue\n let alive: boolean\n try {\n alive = directories.directoryFor(entry.sessionId) === wired\n } catch {\n alive = false\n }\n if (!alive) {\n entry.restore()\n wiredDirectories.delete(wired)\n }\n }\n }\n\n const ensure = (): ModelDirectoryLike | undefined => {\n const host = deps.ctx as SliderContext\n const sessions = host.get?.('sessions') as SessionsLike | undefined\n const uiSession = host.get?.('uiSession') as UiSessionLike | undefined\n const directories = host.get?.('modelDirectories') as ModelDirectoriesLike | undefined\n const sessionId = currentSessionId(sessions, uiSession)\n if (sessionId === undefined || directories === undefined) return undefined\n try {\n // Resolve FIRST, then compare identities: a session scope can be torn\n // down and re-resolved under the same id, and the cached instance would\n // be a disposed directory serving a frozen snapshot forever.\n const directory = directories.directoryFor(sessionId)\n sweepDeadWiretaps(directories, directory)\n if (current !== undefined && current.sessionId === sessionId\n && current.directory === directory) {\n return directory\n }\n // The effort-memory wiretap rides the shared directory (wrapping its\n // select AND watching for level-less restored projections), so a model\n // switch and a session restore both carry the remembered level.\n // Idempotent per instance. The configured-pick read rides along so the\n // chain's second layer comes from the settings document.\n if (!wiredDirectories.has(directory)) {\n wiredDirectories.set(directory, {\n sessionId,\n restore: wireEffortMemory(directory, { configuredEffort: deps.configuredEffort }),\n })\n }\n current = { sessionId, directory }\n return directory\n } catch {\n // Unknown session (a scope not mounted yet): transient \u2014 retry next scan.\n current = undefined\n return undefined\n }\n }\n\n const dispose = (): void => {\n for (const [, entry] of wiredDirectories) entry.restore()\n wiredDirectories.clear()\n }\n\n return { ensure, dispose }\n}\n", "/**\n * Locating the official composer model seat \u2014 the ONE surface this plugin may\n * decorate with a provider prefix.\n *\n * Everything in this module is scoped to the composer card\n * (`[data-composer-card]`, owned by InputBar) and, inside it, to the model\n * seat's own slot anchor. That scope is not a convenience: the shell renders\n * several `button[aria-haspopup=\"menu\"]` elements (the settings sidebar's\n * account button, the composer's permission selector, \u2026), so a document-wide\n * search hands back a button this plugin does not own and paints the provider\n * prefix on a host surface's label. When the seat cannot be identified the\n * finders return undefined \u2014 the prefix is simply not shown, never stamped on\n * someone else's control.\n *\n * @module dsh-model-think-level/client/injection/model-menu\n */\n\n/**\n * The declared slot whose single entry is the model seat (ModelSelect).\n *\n * `renderSlot` wraps every entry in an addressable anchor div (`data-slot` =\n * the slot key, `display: contents`), which the shell documents as \"purely\n * addressable surface\". The anchor therefore names the seat independently of\n * its translated copy, and it is the identity to trust whenever the shell\n * renders it.\n */\nconst MODEL_SEAT_SLOT = 'conversation.input.model'\n\n/**\n * The composer card: the shell's single owner of the model seat.\n *\n * The card is both the search scope and the proof that a candidate button\n * belongs to this plugin's seat at all. The shell renders ONE card (the\n * composer input surface, `data-composer-card` on InputBar's card div).\n *\n * @param doc - the document to search.\n * @returns the card element, or undefined when no composer is mounted.\n */\nfunction composerCardOf(doc: Document): HTMLElement | undefined {\n return doc.querySelector<HTMLElement>('[data-composer-card]') ?? undefined\n}\n\n/**\n * The model seat's own subtree inside the card.\n *\n * @param card - the composer card.\n * @returns the seat's slot anchor, or undefined on a shell that renders none.\n */\nfunction seatAnchorOf(card: HTMLElement): HTMLElement | undefined {\n return card.querySelector<HTMLElement>(`[data-slot=\"${MODEL_SEAT_SLOT}\"]`) ?? undefined\n}\n\n/**\n * The menu-trigger buttons that may belong to the model seat.\n *\n * Scoped to the seat's anchor when it exists, so the composer's permission\n * selector (rendered earlier in the card, also `aria-haspopup=\"menu\"`) is never\n * a candidate. Without an anchor the whole card is the scope, and the ordering\n * rules in the finders below pick the seat out of it.\n *\n * @param card - the composer card.\n * @returns the candidate triggers, in document order.\n */\nfunction seatTriggerCandidates(card: HTMLElement): HTMLButtonElement[] {\n const scope = seatAnchorOf(card) ?? card\n return Array.from(scope.querySelectorAll<HTMLButtonElement>('button[aria-haspopup=\"menu\"]'))\n}\n\n/**\n * The menu a trigger points at through `aria-controls`, when it is one.\n *\n * This is the primary route because a portaled popover leaves the card while\n * the control keeps naming it, so the link is what survives the move. A\n * controls id that names nothing, or names a non-menu element, is not a menu.\n *\n * @param trigger - the candidate button.\n * @param doc - the document the linked element lives in.\n * @returns the linked menu, or undefined.\n */\nfunction linkedMenuOf(trigger: HTMLButtonElement, doc: Document): HTMLElement | undefined {\n const id = trigger.getAttribute('aria-controls')\n if (id === null || id.length === 0) return undefined\n const menu = doc.getElementById(id)\n if (menu === null) return undefined\n return menu.getAttribute('role') === 'menu' ? menu : undefined\n}\n\n/**\n * The official composer model menu: the seat root's open popover.\n *\n * Shape-tolerant across kernels: newer kernels portal the menu to\n * document.body and link it from the seat trigger via aria-controls, while\n * older kernels render it inline right after the trigger. Both shapes keep\n * aria-haspopup=\"menu\" on the trigger and role=\"menu\" on the menu, so the\n * controls link is the primary route and the sibling check stays the fallback.\n *\n * The search NEVER leaves the composer card. When no card is mounted there is\n * no model seat, and answering with some other surface's menu is exactly the\n * cross-surface confusion this scoping exists to prevent.\n *\n * @param doc - the document to search.\n * @returns the open model menu, or undefined while it is closed.\n */\nexport function findModelMenu(doc: Document = document): HTMLElement | undefined {\n const card = composerCardOf(doc)\n if (card === undefined) return undefined\n\n for (const trigger of seatTriggerCandidates(card)) {\n const menu = linkedMenuOf(trigger, doc)\n if (menu !== undefined) return menu\n }\n\n // Pre-portal inline shape: the menu sits right after its trigger button.\n // Scoped like the candidates, so another seat's inline menu is not mistaken\n // for the model seat's.\n const scope = seatAnchorOf(card) ?? card\n for (const menu of Array.from(scope.querySelectorAll<HTMLElement>('[role=\"menu\"]'))) {\n if (menu.previousElementSibling?.matches('button[aria-haspopup=\"menu\"]')) return menu\n }\n return undefined\n}\n\n/**\n * The official composer model trigger.\n *\n * The trigger stays in the card while its menu is closed, so this finder keeps\n * the menu lookup's linked and inline checks and then falls back to the seat\n * the closed card still renders:\n *\n * - inside the seat's anchor, every candidate IS the seat;\n * - without an anchor, the seat is the LAST menu trigger of the card, since\n * the shell renders it at the end of the composer's trailing control row\n * (after the tools row that hosts the permission selector).\n *\n * @param doc - the document to search.\n * @returns the composer's model trigger, or undefined while no composer exists.\n */\nexport function findModelTrigger(doc: Document = document): HTMLButtonElement | undefined {\n const card = composerCardOf(doc)\n if (card === undefined) return undefined\n\n const anchor = seatAnchorOf(card)\n const candidates = seatTriggerCandidates(card)\n const seat = candidates[0]\n if (seat === undefined) return undefined\n\n // Prefer the trigger whose controls link resolves to the currently open\n // model menu. This disambiguates a card that holds another menu trigger.\n for (const trigger of candidates) {\n if (linkedMenuOf(trigger, doc) !== undefined) return trigger\n }\n\n // Pre-portal inline shape: the menu follows its trigger in the card.\n for (const trigger of candidates) {\n if (trigger.nextElementSibling?.matches('[role=\"menu\"]')) return trigger\n }\n\n return anchor === undefined ? candidates.at(-1) : seat\n}\n\n/** The official composer model menu: the seat root's open popover. */\nexport function modelMenuOf(): HTMLElement | undefined {\n return findModelMenu()\n}\n\n/** The official composer model trigger, or undefined while the seat is absent. */\nexport function modelTriggerOf(): HTMLButtonElement | undefined {\n return findModelTrigger()\n}\n", "/**\n * The provider-card slot registration: the seat the request-header editor\n * (issue #12) renders in.\n *\n * `settings.models.provider-card` is a KEYED slot declared by the official\n * Models section, dispatched on each card's owning settings namespace\n * (`ProviderDirectoryEntry.settingsNs`). Registering under `llm-pi-ai` is\n * therefore all it takes to receive every card of this adapter family \u2014 the\n * shipped routes, the ones the user adds, and the hand-declared ones alike \u2014\n * without a DOM anchor, a mutation observer, or a label to match on.\n *\n * The ROUTE is not a registration-time fact: the section passes each occurrence\n * its directory row as owner props, so the route arrives on the component at\n * render time and one registration serves every card.\n *\n * The mount also hosts the editor's tab bar ({@link EditorTabBar}) and is the\n * element the editor-tabs pass resolves its card from, so the plugin's own\n * subtree is what carries both the takeover's control and the state the\n * stylesheet reveals the section on.\n *\n * @module dsh-model-think-level/client/injection/provider-card-slot\n */\n\nimport { createElement, useEffect, useRef } from 'react'\nimport type { ReactNode } from 'react'\nimport type { Translate } from '@deepseek-ai/dsh-client-ui-slots'\nimport { PI_AI_NS, PLUGIN_ID } from '../../constants.js'\nimport { HeadersEditor } from '../HeadersEditor.js'\nimport { EditorTabBar } from './editor-tab-bar.js'\nimport { editorCardOf } from './editor-tabs.js'\nimport type { ClientContext, RemoteApi, SlotRegistrarFace } from '../types.js'\n\n/** The keyed-slot key this plugin occupies: the adapter family it extends. */\nconst SLOT = 'settings.models.provider-card'\n\n/**\n * The occurrence props the official Models section hands one provider-card\n * extension (its own `ProviderCardExtrasOwnerProps`). Declared structurally for\n * the same reason as {@link SlotRegistrarFace}: the plugin pins only what it\n * reads, so a field the section adds cannot break activation.\n */\ninterface ProviderCardOccurrence {\n /** The card's directory row; `provider` is the settings route key. */\n provider?: { provider?: string }\n}\n\n/** Props the occurrence component receives: the section's share plus ours. */\nexport type ProviderCardSlotProps = ProviderCardOccurrence & { api: RemoteApi; t: Translate }\n\n/**\n * The occurrence component: resolve this card's route, render the editor with\n * its tab bar, and publish the card's open/closed state so the section reveals\n * itself with the official editor. Exported for its own tests (the registration\n * below is the production path).\n */\nexport function ProviderCardSlot({ provider, api, t }: ProviderCardSlotProps): ReactNode {\n const host = useRef<HTMLDivElement | null>(null)\n\n useEffect(() => {\n const wrapper = host.current\n // No card row around us (a bare render) means no card state to observe:\n // leaving `data-edit` off keeps the section hidden, which is the only safe\n // default.\n const card = editorCardOf(wrapper)\n if (wrapper === null || card === null) return\n const observed = card as unknown as Node\n // The one edit-state signal the official row exposes. The section holds\n // its editor target in component state (ui-settings-models\n // ModelsSection's `editing`) and renders the editor with NO data\n // attribute and NO aria-expanded on the row's Edit button \u2014 the CSS-module\n // class of the mounted editor (word root `editor`, compiled to a\n // `_editor*` hash) is the only thing that exists iff the card is open.\n // This is a COUPLING, not a contract: if a future official build renames\n // that class root, `data-edit` degrades to a constant 0 \u2014 the section\n // stays hidden and the official page stays untouched, so the failure mode\n // is \"the feature is gone\", never \"the page is broken\". The upgrade path\n // is an official one: a data attribute on the editor or an `editing` field\n // on the slot occurrence replaces this probe one-for-one.\n const openEditor = (): boolean => card.querySelector('[class*=\"_editor\"]') !== null\n // Publish the card's state onto our own wrapper instead of into React\n // state. The reveal has to land in the batch the official mutation arrived\n // in: a setState from here is committed by React's scheduler, i.e. after\n // the frame that already painted the opened editor, and the section \u2014 the\n // tab bar's own container \u2014 would blink in one frame late. Writing the\n // attribute directly is the same choice the composer's popover path makes.\n // The compare keeps a settled card from emitting mutations of its own, and\n // `data-edit` is deliberately not a prop below, so React never patches it\n // back.\n const sync = (): void => {\n const edit = openEditor() ? '1' : '0'\n if (wrapper.dataset['edit'] !== edit) wrapper.dataset['edit'] = edit\n }\n sync()\n const observer = new MutationObserver(sync)\n // Child list only, and subtree: the editor element itself arrives as a\n // child of the row, and its own internals are irrelevant here.\n observer.observe(observed, { childList: true, subtree: true })\n return () => {\n observer.disconnect()\n }\n }, [])\n\n const route = provider?.provider\n // A card with no resolvable route is not this section's business: render\n // nothing rather than an editor that could not address a settings key.\n if (typeof route !== 'string' || route.length === 0) return null\n return createElement(\n 'div',\n {\n ref: host,\n className: 'bre-headers-host',\n // Names the provider row this slot sits in, for the row-order pass. The\n // slot is dispatched per card, so it is the one place every llm-pi-ai row\n // \u2014 saved, first-run or draft \u2014 hands back its route.\n 'data-bre-provider': route,\n },\n // The tab bar goes FIRST and lives in this, our own, subtree \u2014 never in the\n // official card's child list, which React reconciles. The stylesheet\n // reveals it while the card carries the editor-tabs takeover, so it appears\n // with the tabbed editor and stays hidden on a card the pass left alone\n // (the DeepSeek account editor).\n createElement(EditorTabBar, { host, t }),\n createElement(HeadersEditor, { route, api, t }),\n )\n}\n\n/**\n * Take the provider-card seat.\n * @param ctx - client root context (its `slots` service registers the seat).\n * @param api - the settings Remote the editor reads and writes through.\n * @param t - the plugin's locale-bound translator.\n * @returns the seat's disposer, or a no-op when the seat is unavailable.\n */\nexport function registerProviderCardSlot(ctx: ClientContext, api: RemoteApi, t: Translate): () => void {\n const host = ctx as unknown as { slots?: SlotRegistrarFace }\n // `inject` is the seam the official registrar binds to the calling scope;\n // wire whatever comes back so a plugin disable / HMR leaves no seat behind.\n const injected = host.slots?.inject(SLOT, () => {\n host.slots?.register({\n name: SLOT,\n // The keyed dispatch key. `llm-pi-ai` is the ONLY family this plugin\n // extends, so one registration covers every card; other adapter families\n // dispatch other keys and never reach this component.\n key: PI_AI_NS,\n inject: () => ({ api, t }),\n }, ProviderCardSlot)\n })\n return typeof injected === 'function' ? injected as () => void : () => {}\n}\n", "/**\n * The provider-card request-header editor (issue #12, MVP-1) \u2014 the fetch-layer\n * `user-agent` takeover is its MVP-2 half, rendered in the same section because\n * to the user they are one thing: \"what this route sends\".\n *\n * Mounted through the OFFICIAL seat, `settings.models.provider-card`, which the\n * Models section dispatches per provider card. That is a sanctioned extension\n * point, so unlike the per-model effort editor this component needs no DOM\n * injection, no mutation observer and no anchor labels.\n *\n * Write discipline: the draft is a whole dict and only the explicit Save writes\n * it, so an edit can never race a stale snapshot of the same card the way\n * per-keystroke writes would \u2014 and Cancel/Reload behaves like the official\n * card's own fields. The headers land through `llm-pi-ai.providers.<route>.headers`,\n * the one channel the adapter actually sends.\n *\n * @module dsh-model-think-level/HeadersEditor\n */\n\nimport { useEffect, useId, useRef, useState } from 'react'\nimport type { ReactNode } from 'react'\nimport { HEADERS_CONFIG_PATH, PLUGIN_ID } from '../constants.js'\nimport { describeNamespace, providerHeadersOf, writeProviderHeaders, type ProviderHeadersReply } from './ops.js'\nimport type { RemoteApi } from './types.js'\n\n/**\n * Offered client identities, most useful first. These are the shapes relays\n * that police the `user-agent` look for; every one is a preset, never a filter\n * \u2014 the field stays free text.\n */\nconst USER_AGENT_PRESETS: readonly string[] = [\n 'claude-cli/2.1.161 (external, cli)',\n 'claude-code/1.0.0',\n 'Kilo-Code/1.0',\n]\n\n/** The fixed fetch-layer report the host route serves. */\nexport interface HeaderEnvironment {\n /** Whether the fetch-layer takeover is enabled in this deployment. */\n enabled: boolean\n /** Whether the plugin's wrapper currently owns the global fetch. */\n installed: boolean\n /** Routes whose configured request headers are sent. */\n overrides: { origin: string; route: string; headers: Record<string, string>; userAgent?: string }[]\n /** Origins two routes claim with different User-Agent values. */\n conflicts: { origin: string; routes: string[]; values: string[] }[]\n /** Same-origin differences for any configured header. */\n headerConflicts?: { origin: string; header: string; routes: string[]; values: string[] }[]\n /** What was found of the plugins rewriting the same surface. */\n environment: {\n adapter: 'patched' | 'stock' | 'unknown'\n siblings: { name: string; path: string }[]\n preferOfficialLayer: boolean\n }\n}\n\n/** Props of {@link HeadersEditor}. */\nexport interface HeadersEditorProps {\n /** The provider route this card edits. */\n route: string\n /** The write seam. */\n api: RemoteApi\n /** Localized copy. */\n t: (key: string, params?: Record<string, string | number>) => string\n}\n\n/** One editable header row; a stable `id` keeps React keys off the name. */\ninterface DraftRow {\n id: number\n name: string\n value: string\n}\n\n/** A draft as the editor holds it: every configured header is one row. */\ninterface Draft {\n rows: DraftRow[]\n /** Legacy input accepted by headersFromDraft callers; the UI no longer uses it. */\n userAgent?: string\n}\n\n/** Build a draft from every stored header entry, including User-Agent. */\nfunction draftFrom(headers: Record<string, string> | undefined): Draft {\n let id = 0\n const rows = Object.entries(headers ?? {}).map(([name, value]) => ({ id: id += 1, name, value }))\n // One blank pair by default: the pane is the editor now, and an empty pane\n // would offer nothing to type into \u2014 the other two panes always show fields,\n // and this one starts with one pair the user can fill (or replace) directly.\n if (rows.length === 0) rows.push({ id: id += 1, name: '', value: '' })\n return { rows }\n}\n\n/** The headers dict a draft stores: blank names are dropped. */\nexport function headersFromDraft(draft: Draft): Record<string, string> {\n const out: Record<string, string> = {}\n let hasUserAgent = false\n for (const row of draft.rows) {\n const name = row.name.trim()\n if (name.length === 0) continue\n out[name] = row.value\n if (name.toLowerCase() === 'user-agent') hasUserAgent = true\n }\n // Keep old programmatic callers working while new UI drafts use rows only.\n const legacyUserAgent = draft.userAgent?.trim()\n if (!hasUserAgent && legacyUserAgent !== undefined && legacyUserAgent.length > 0) out['user-agent'] = legacyUserAgent\n return out\n}\n\n/**\n * Render the provider card's request-header section.\n * @param props - route identity, write seam and copy.\n * @returns the section.\n */\nexport function HeadersEditor({ route, api, t }: HeadersEditorProps): ReactNode {\n // Kept for the optional User-Agent preset list when a row is named that way.\n const uaListId = useId()\n const [stored, setStored] = useState<Record<string, string> | undefined>(undefined)\n const [draft, setDraft] = useState<Draft>({ rows: [] })\n /** Whether the section's details are disclosed. Collapsed on mount. */\n const [open, setOpen] = useState(false)\n const [editing, setEditing] = useState(false)\n const [loading, setLoading] = useState(true)\n const [busy, setBusy] = useState(false)\n const [readOnly, setReadOnly] = useState(false)\n const [message, setMessage] = useState<{ kind: 'success' | 'error' | 'info'; text: string } | undefined>(undefined)\n const [environment, setEnvironment] = useState<HeaderEnvironment | undefined>(undefined)\n /**\n * Whether the tab takeover made this section a whole pane (`\u9AD8\u7EA7\u914D\u7F6E`).\n *\n * There is no disclosure control to click there: the pane holds this section\n * and nothing else, so it renders expanded and drops the pill.\n */\n const [tabbed, setTabbed] = useState(false)\n const nextRowId = useRef(1)\n const rootRef = useRef<HTMLDivElement | null>(null)\n\n // Seed the draft from the stored document. The slot hands the editor a route,\n // not a profile: the section reads the pi-ai namespace itself, so the card\n // re-renders that the section pushes cannot hand it a stale draft.\n useEffect(() => {\n let live = true\n void (async () => {\n try {\n const join = await describeNamespace(api)\n if (!live) return\n const headers = providerHeadersOf(join.namespace, route)\n setStored(headers)\n setDraft(draftFrom(headers))\n setReadOnly(join.writable !== true)\n } catch {\n // A read failure leaves the section inert rather than blocking the card.\n if (live) setReadOnly(true)\n } finally {\n if (live) setLoading(false)\n }\n })()\n return () => { live = false }\n }, [api, route])\n\n // The fetch-layer report is a host fact the page cannot observe on its own:\n // read once per mount, and never let its absence break the editor.\n useEffect(() => {\n let live = true\n void (async () => {\n try {\n const response = await fetch(HEADERS_CONFIG_PATH, { method: 'GET' })\n const body = await response.json() as { ok?: boolean; data?: HeaderEnvironment }\n if (live && body.ok === true && body.data !== undefined) setEnvironment(body.data)\n } catch {\n // Older host, or a page that cannot reach its own origin: stay silent.\n }\n })()\n return () => { live = false }\n }, [])\n\n // The takeover marks this component's own root with `data-bre-region=\"advanced\"`\n // (the editor-tabs pass), which is the only signal that says \"you are the\n // whole \u9AD8\u7EA7\u914D\u7F6E pane now\". The mark lands in the microtask carrying the\n // official commit, so it can arrive either side of this effect: read once on\n // mount AND observe, because neither alone is sufficient.\n useEffect(() => {\n const root = rootRef.current\n if (root === null) return\n const sync = (): void => { setTabbed(root.dataset['breRegion'] === 'advanced') }\n sync()\n const observer = new MutationObserver(sync)\n observer.observe(root, { attributes: true, attributeFilter: ['data-bre-region'] })\n return () => { observer.disconnect() }\n }, [])\n\n const addRow = (): void => {\n setDraft(current => ({ ...current, rows: [...current.rows, { id: nextRowId.current += 1, name: '', value: '' }] }))\n setMessage(undefined)\n }\n\n const patchRow = (id: number, patch: Partial<DraftRow>): void => {\n setDraft(current => ({ ...current, rows: current.rows.map(row => (row.id === id ? { ...row, ...patch } : row)) }))\n setMessage(undefined)\n }\n\n const removeRow = (id: number): void => {\n setDraft(current => ({ ...current, rows: current.rows.filter(row => row.id !== id) }))\n setMessage(undefined)\n }\n\n const cancel = (): void => {\n setDraft(draftFrom(stored))\n setEditing(false)\n setMessage(undefined)\n }\n\n const save = async (next: Record<string, string>): Promise<void> => {\n setBusy(true)\n setMessage(undefined)\n let reply: ProviderHeadersReply\n try {\n reply = await writeProviderHeaders(api, route, next)\n } catch (error) {\n reply = { ok: false, error: error instanceof Error ? error.message : String(error) }\n }\n setBusy(false)\n if (!reply.ok) {\n setMessage({\n kind: 'error',\n text: reply.error === 'no-route' ? t('headersNoRoute')\n : reply.error === 'read-only' ? t('headersReadOnly')\n : t('headersError', { error: reply.error }),\n })\n return\n }\n setStored(next)\n setEditing(false)\n // Saving opened the section (edit mode is always disclosed); landing the\n // write keeps it open so the user sees the new value rather than watching\n // the card snap shut under them.\n setMessage({\n kind: 'success',\n text: Object.keys(next).length === 0 ? t('headersCleared') : t('headersSaved'),\n })\n }\n\n const rows = Object.entries(stored ?? {})\n const disabled = readOnly || busy\n // Collapsed by default in the single-column card: the card is a list of\n // providers, and a section that pushed its hint, its rows and its warnings\n // into every card would bury the list. Collapsed shows the section's identity\n // only \u2014 what it is called and whether anything is configured \u2014 and the\n // disclosure arrow opens the rest. The tab takeover removes that trade-off:\n // there the section is the whole \u9AD8\u7EA7\u914D\u7F6E pane, so it stays expanded and the\n // disclosure disappears instead of hiding the only thing the pane holds.\n const count = rows.length\n // Under the tabs the pane IS the editor: rows on screen, typed in place, and\n // the pane's own Save commits them through the same seam the fold's editor\n // used. No \"press Edit first\" step \u2014 the other two panes take their edits\n // directly, and this one does now too.\n const inline = tabbed\n\n return (\n <div className=\"bre-headers\" ref={rootRef} data-plugin={PLUGIN_ID} data-open={open ? '1' : '0'}>\n {/* The title strip only exists while the section is a fold inside the\n provider card: the grey head is the identity of a collapsed disclosure.\n Under the tabs the section is the whole \u9AD8\u7EA7\u914D\u7F6E pane, and the\n provider and models panes carry no heading strip of their own \u2014 the\n tab itself names the pane \u2014 so the head steps aside there the same\n way the disclosure button does, keeping the three panes identical in\n weight. */}\n {!tabbed && (\n <div className=\"bre-headers-head\">\n {editing\n ? <span className=\"bre-effort-title\">{t('headersTitle')}</span>\n : (\n <button\n type=\"button\"\n className=\"bre-headers-disclosure\"\n aria-expanded={open}\n aria-label={`${t('headersTitle')} ${route}`}\n onClick={() => { setOpen(!open); setMessage(undefined) }}\n >\n <span className=\"bre-effort-title\">{t('headersTitle')}</span>\n {loading || count === 0\n ? null\n : <span className=\"bre-headers-count\">{count}</span>}\n <span className=\"bre-headers-chevron\" aria-hidden=\"true\">\u203A</span>\n </button>\n )}\n </div>\n )}\n\n {!open && !editing && !tabbed\n ? null\n : (\n <>\n <p className=\"bre-effort-note\">{t('headersHint')}</p>\n {environment === undefined\n ? null\n : (\n <>\n {environment.environment.preferOfficialLayer\n ? <p className=\"bre-effort-note bre-warn\">{t('headersConflictPatched')}</p>\n : null}\n {environment.conflicts.length === 0 && (environment.headerConflicts?.length ?? 0) === 0\n ? null\n : <p className=\"bre-effort-note bre-warn\">{t('headersConflictOrigin')}</p>}\n {environment.environment.siblings.length === 0\n ? null\n : (\n <p className=\"bre-effort-note bre-warn\">\n {t('headersConflictSiblings', { names: environment.environment.siblings.map(item => item.name).join(', ') })}\n </p>\n )}\n </>\n )}\n\n {loading\n ? null\n : editing || inline\n ? (\n <div className=\"bre-headers-edit\">\n <div className=\"bre-headers-columns\" aria-hidden=\"true\">\n <span>{t('headersName')}</span>\n <span>{t('headersValue')}</span>\n <span />\n </div>\n {draft.rows.map(row => (\n <div className=\"bre-headers-row\" key={row.id}>\n <input\n type=\"text\"\n className=\"bre-text-input bre-headers-name\"\n value={row.name}\n disabled={disabled}\n placeholder={t('headersNamePlaceholder')}\n aria-label={t('headersName')}\n onChange={(event) => { patchRow(row.id, { name: event.target.value }) }}\n />\n <input\n type=\"text\"\n className=\"bre-text-input bre-headers-value\"\n value={row.value}\n disabled={disabled}\n list={row.name.trim().toLowerCase() === 'user-agent' ? uaListId : undefined}\n placeholder={t('headersValue')}\n aria-label={t('headersValue')}\n onChange={(event) => { patchRow(row.id, { value: event.target.value }) }}\n />\n <button\n type=\"button\"\n className=\"bre-link-button bre-headers-remove\"\n disabled={disabled}\n aria-label={`${t('headersClear')} ${row.name}`}\n onClick={() => { removeRow(row.id) }}\n >\n \u00D7\n </button>\n </div>\n ))}\n <button\n type=\"button\"\n className=\"bre-link-button bre-headers-add\"\n disabled={disabled}\n onClick={addRow}\n >\n {t('headersAdd')}\n </button>\n {false && (<div className=\"bre-headers-legacy\">\n <span className=\"bre-effort-title\">{t('headersUserAgentTitle')}</span>\n <input\n type=\"text\"\n className=\"bre-text-input bre-headers-value\"\n value={draft.userAgent ?? ''}\n disabled={disabled}\n list={uaListId}\n placeholder={t('headersUserAgentPlaceholder')}\n aria-label={t('headersUserAgentTitle')}\n onChange={(event) => {\n setDraft(current => ({ ...current, userAgent: event.target.value }))\n setMessage(undefined)\n }}\n />\n {(draft.userAgent ?? '').trim().length === 0\n ? null\n : (\n <button\n type=\"button\"\n className=\"bre-link-button bre-headers-remove\"\n disabled={disabled}\n onClick={() => { setDraft(current => ({ ...current, userAgent: '' })); setMessage(undefined) }}\n >\n {t('headersUserAgentClear')}\n </button>\n )}\n </div>\n )}\n <datalist id={uaListId}>\n {USER_AGENT_PRESETS.map(preset => <option key={preset} value={preset} />)}\n </datalist>\n <p className=\"bre-effort-note bre-headers-legacy\" hidden>{t('headersUserAgentHint')}</p>\n <div className=\"bre-effort-actions\">\n <button\n type=\"button\"\n className=\"bre-primary-button\"\n disabled={disabled}\n onClick={() => { void save(headersFromDraft(draft)) }}\n >\n {t('headersSave')}\n </button>\n <button type=\"button\" className=\"bre-secondary-button\" disabled={disabled} onClick={cancel}>\n {t('headersCancel')}\n </button>\n </div>\n </div>\n )\n : (\n <div className=\"bre-headers-rows\">\n {rows.length === 0\n ? <p className=\"bre-effort-note\">{t('headersEmpty')}</p>\n : rows.map(([name, value]) => (\n <div className=\"bre-headers-row\" key={name}>\n <span className=\"bre-headers-name\">{name}</span>\n {/* Masked: the official README warns that a credential in\n `headers` is not reached by the harness redactor, so the\n value must not sit in plain sight on a shared screen. */}\n <span className=\"bre-headers-value bre-headers-masked\">{'\u2022'.repeat(Math.min(value.length, 24))}</span>\n </div>\n ))}\n {readOnly\n ? null\n : (\n <div className=\"bre-effort-actions\">\n <button\n type=\"button\"\n className=\"bre-secondary-button\"\n disabled={disabled}\n onClick={() => { setEditing(true); setMessage(undefined) }}\n >\n {t('headersEdit')}\n </button>\n </div>\n )}\n </div>\n )}\n </>\n )}\n\n {message === undefined\n ? null\n : (\n <p className={'bre-effort-message bre-' + message.kind} role={message.kind === 'error' ? 'alert' : 'status'}>\n {message.text}\n </p>\n )}\n </div>\n )\n}\n", "/**\n * The provider editor's segmented control (\u4F9B\u5E94\u5546\u914D\u7F6E / \u6A21\u578B\u914D\u7F6E / \u9AD8\u7EA7\u914D\u7F6E).\n *\n * This is the bar the editor-tabs pass drives. It lives HERE, in the plugin's\n * own React subtree, rather than being built by the pass and inserted into the\n * official card: the card's children belong to React, and a foreign node in\n * that list survives only until the next render. The component is rendered\n * inside the provider-card slot's own mount \u2014 a child of the card the slot\n * already resolves \u2014 and the stylesheet reveals it as soon as the pass marks\n * the card (`bre-tabbed`), which happens in the same mutation batch as the\n * official render.\n *\n * The component renders STRUCTURE only: it holds no state, because the card's\n * `data-bre-tab` attribute is the source of truth for both the pane switch\n * (pure CSS) and the selected button. The pass owns the copy, the per-pane\n * availability and `aria-selected`, so a language switch relabels the bar\n * without a re-render and a pane that is not on the card hides its own button.\n *\n * @module dsh-model-think-level/client/injection/editor-tab-bar\n */\n\nimport { createElement } from 'react'\nimport type { ReactNode } from 'react'\nimport type { Translate } from '@deepseek-ai/dsh-client-ui-slots'\nimport {\n EDITOR_TABS_CLASS,\n EDITOR_TAB_BUTTON_CLASS,\n EDITOR_TAB_ID_ATTR,\n EDITOR_TAB_IDS,\n editorCardOf,\n selectEditorTab,\n type EditorTabId,\n} from './editor-tabs.js'\n\n/** The locale key of each tab's label. */\nconst LABEL_KEYS: Record<EditorTabId, string> = {\n provider: 'editorTabProvider',\n models: 'editorTabModels',\n advanced: 'editorTabAdvanced',\n}\n\nexport interface EditorTabBarProps {\n /** The plugin's own mount inside the card this bar switches. It hangs\n * directly off the card, which is how the card is resolved on click. */\n host: { readonly current: HTMLDivElement | null }\n /** The plugin's translator. */\n t: Translate\n}\n\n/** One provider editor's tab bar. */\nexport function EditorTabBar({ host, t }: EditorTabBarProps): ReactNode {\n return createElement(\n 'div',\n { className: EDITOR_TABS_CLASS, role: 'tablist', 'aria-label': t('editorTabsLabel') },\n ...EDITOR_TAB_IDS.map((id) => createElement(\n 'button',\n {\n key: id,\n type: 'button',\n role: 'tab',\n className: EDITOR_TAB_BUTTON_CLASS,\n [EDITOR_TAB_ID_ATTR]: id,\n onClick: (event: MouseEvent): void => {\n // The tab is this plugin's own control: the official page has no\n // click business here, and a stray handler of theirs must not see the\n // gesture that rearranges their editor.\n event.preventDefault()\n event.stopPropagation()\n const card = editorCardOf(host.current)\n if (card !== null) selectEditorTab(card, id)\n },\n },\n t(LABEL_KEYS[id]),\n )),\n )\n}\n", "/**\n * The reasoning-effort slider enable toggle. The item form is the upstream\n * plugin's setting row ported VERBATIM (title, description, state, switch \u2014\n * colors, sizes and spacing included), wrapped in the requested boxed\n * container on the Models settings page below the add-provider and\n * add-custom-provider actions. Mounted through the official\n * 'settings.models.footer' slot.\n *\n * The big-fish slider setting is deliberately gone: that feature was not\n * carried into this integration.\n *\n * @module dsh-model-think-level/client/SliderToggle\n */\nimport { createElement, useSyncExternalStore } from 'react'\nimport type { ReactNode } from 'react'\nimport { setSliderEnabled, sliderEnabled, subscribeSliderEnabled } from './slider-pref.js'\n\nexport interface SliderToggleProps {\n /** Localized copy (the plugin's bound translator). */\n t: (key: string, params?: Record<string, string | number>) => string\n}\n\n/** One boxed setting row: label copy (upstream .re-setting-row form), live state, and the switch. */\nexport function SliderToggle(props: SliderToggleProps): ReactNode {\n const { t } = props\n const enabled = useSyncExternalStore(subscribeSliderEnabled, sliderEnabled)\n const toggle = (): void => { setSliderEnabled(!enabled) }\n return createElement(\n 'div',\n { className: 'bre-slider-setting' },\n createElement('div', { className: 'bre-slider-setting-row' },\n createElement('div', { className: 'bre-slider-setting-copy' },\n createElement('div', { className: 'bre-slider-setting-title' }, t('sliderToggleTitle')),\n createElement('div', { className: 'bre-slider-setting-description' }, t('sliderToggleDescription')),\n ),\n createElement('div', { className: 'bre-slider-setting-control' },\n createElement('span', { className: 'bre-slider-setting-state' }, enabled ? t('settingOn') : t('settingOff')),\n createElement('button', {\n type: 'button',\n role: 'switch',\n 'aria-label': t('sliderToggleAria'),\n 'aria-checked': enabled,\n className: 'bre-slider-setting-switch' + (enabled ? ' is-on' : ''),\n onClick: toggle,\n }, createElement('span', { className: 'bre-slider-setting-switch-knob', 'aria-hidden': true })),\n ),\n ),\n )\n}\n", "/**\n * The Models-page footer slot: the boxed composer-slider toggle.\n *\n * The Models page ships the sanctioned 'settings.models.footer' extension slot\n * after the provider rows and the add controls, and the plugin's top-level\n * inject already declares 'remote.settings' \u2014 its wired contract on this\n * kernel \u2014 so the toggle takes that seat unconditionally; no DOM fallback\n * exists.\n *\n * @module dsh-model-think-level/client/injection/slider-toggle-slot\n */\n\nimport type { Translate } from '@deepseek-ai/dsh-client-ui-slots'\nimport { PLUGIN_ID } from '../../constants.js'\nimport { SliderToggle } from '../SliderToggle.js'\nimport type { ClientContext, SlotRegistrarFace } from '../types.js'\n\n/**\n * Take the official footer seat.\n * @param ctx - client root context (its `slots` service registers the seat).\n * @param t - the plugin's locale-bound translator.\n */\nexport function registerSliderToggleSlot(ctx: ClientContext, t: Translate): () => void {\n const host = ctx as unknown as { slots?: SlotRegistrarFace }\n // `register` hands back the remover for its seat; the `inject` seam is where\n // the official registrar binds it to the calling scope. Wire whatever comes\n // back so a plugin disable / HMR leaves no seat (and no stale component)\n // behind instead of dropping the disposer on the floor.\n const injected = host.slots?.inject('settings.models.footer', () => {\n host.slots?.register({\n name: 'settings.models.footer',\n id: PLUGIN_ID + '-slider-toggle',\n order: 15,\n inject: () => ({ t }),\n }, SliderToggle)\n })\n return typeof injected === 'function' ? injected as () => void : () => {}\n}\n"],
|
|
5
|
+
"mappings": "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AAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;A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",
|
|
6
|
+
"names": ["index_exports", "__export", "EffortEditor", "apply", "baselineModelsOf", "compatOf", "createEditorApi", "defaultEffortOf", "describeNamespace", "effortsOf", "findModelMenu", "findModelTrigger", "inject", "inputOf", "name", "nameOf", "providerHeadersOf", "providersOf", "userProvidersOf", "writeModelRows", "writeProviderHeaders", "__toCommonJS", "import_react", "PLUGIN_ID", "PROBE_PATH", "PROVIDER_KEY_PATH", "KEY_INDEX_PATH", "AUTOFILL_CONFIG_PATH", "HEADERS_CONFIG_PATH", "PI_AI_NS", "UNSET_MARKER", "INPUT_UNSET_MARKER", "AUTOFILL_MARKER", "DEFAULT_EFFORT_FIELD", "STORE_NS", "PLUGIN_ID", "import_react", "LocaleRefresh", "props", "locale", "subscribe", "notify", "getSnapshot", "en", "zh", "SLIDER_PREF_KEY", "UPSTREAM_KEYS", "enabled", "read", "own", "migrated", "key", "value", "listeners", "notify", "listener", "sliderEnabled", "setSliderEnabled", "next", "syncSliderEnabled", "subscribeSliderEnabled", "PROVIDER_ORDER_KEY", "PINNED", "parseProviderOrder", "raw", "parsed", "dedupe", "value", "ids", "seen", "kept", "id", "order", "listeners", "notify", "listener", "sameOrder", "left", "right", "index", "providerOrder", "setProviderOrder", "next", "deduped", "syncProviderOrder", "subscribeProviderOrder", "baseProviderOrder", "rank", "pinned", "entry", "mergeProviderOrder", "base", "preferred", "slots", "named", "merged", "slot", "position", "resolveProviderOrder", "moveToIndex", "items", "item", "toIndex", "from", "target", "PROVIDER_DISABLED_KEY", "listeners", "disabled", "read", "parseProviderOrder", "parseDisabledProviders", "raw", "disabledProviders", "isProviderEnabled", "id", "setProviderEnabled", "enabled", "already", "apply", "candidate", "syncDisabledProviders", "next", "subscribeDisabledProviders", "listener", "sameSet", "left", "right", "persist", "MODEL_ORDER_KEY", "parseModelOrders", "raw", "parsed", "map", "provider", "value", "ids", "dedupe", "entry", "seen", "kept", "id", "orders", "listeners", "notify", "listener", "sameIds", "left", "right", "index", "persist", "modelOrders", "modelOrderOf", "EMPTY", "setModelOrder", "next", "current", "syncModelOrders", "subscribeModelOrders", "orderModels", "preferred", "mergeProviderOrder", "resolveModelOrder", "orderModelEntries", "entries", "byId", "STYLES", "import_react", "import_react_dom", "import_react", "import_react_dom", "THINKING_LEVELS", "INPUT_MODALITIES", "KNOWLEDGE_BASE", "GENERIC_OPENAI_EFFORTS", "PROTOCOL_INFERENCE", "GENERIC_FALLBACK", "normalize", "value", "isAlnum", "ch", "normalizeLoose", "onBoundary", "haystack", "at", "length", "before", "after", "matchKnowledgeBase", "modelId", "displayName", "best", "entry", "pattern", "needle", "NATIVE_DIALECT_DOMAINS", "inferProtocol", "route", "api", "baseURL", "parts", "dialect", "COMPAT_CAPABLE_PROTOCOL", "ANTHROPIC_PROTOCOL", "isSelfHostedEndpoint", "host", "sanitizeCompatForProtocol", "compat", "norm", "copy", "key", "compatForRoute", "OFFICIAL_RELAY_DOMAINS", "isSelfHostedRelay", "root", "withRolePin", "reasons", "ENDPOINT_CONFIRMED_LADDER", "VISION_NAME_TOKENS", "inferModalitiesFromName", "token", "declaredCoreInput", "raw", "lowered", "INPUT_MODALITIES", "suggestEfforts", "endpoint", "input", "inputSource", "contextWindow", "maxTokens", "listedInput", "guessed", "modalityParts", "endpointPart", "protocol", "efforts", "compatBase", "compatWithBudget", "EFFORT_MEMORY_KEY", "WIRED_MARKER", "isSelectRefusal", "outcome", "selectRefusalMessage", "error", "code", "message", "memoryKey", "provider", "model", "readMemory", "raw", "parsed", "memory", "key", "value", "writeMemory", "rememberEffort", "id", "rememberedEffort", "effortIdsOf", "state", "efforts", "candidate", "level", "advertisedDefaultOf", "displayedMiddleOf", "supported", "isModelSwitch", "current", "selection", "resolveFallback", "snapshot", "sessionSightings", "deps", "sighted", "configured", "fallback", "suggestEfforts", "advertised", "wireEffortMemory", "directory", "target", "hadOwnSelect", "original", "attemptedRestores", "restoreFailures", "RESTORE_RETRY_LIMIT", "utterances", "wrapped", "myUtterance", "result", "sliderEnabled", "fresh", "a", "b", "restoreProjection", "utteranceAtAttempt", "freshCurrent", "failures", "unsubscribe", "sliderLevels", "state", "efforts", "candidate", "currentModelOf", "effectiveEffortIndex", "levels", "model", "current", "level", "fallback", "at", "modelsOf", "group", "providerLabelOf", "named", "chevron", "open", "checkIcon", "VISION_CACHE", "supportsVision", "cached", "known", "matchKnowledgeBase", "verdict", "inferModalitiesFromName", "supportsThinking", "visionIcon", "label", "thinkingIcon", "modelTags", "badges", "ComposerSlider", "props", "directory", "t", "layoutChange", "notify", "effort", "setEffort", "panel", "setPanel", "openProvider", "setOpenProvider", "committing", "setCommitting", "localError", "setLocalError", "bodyRef", "committedRef", "committingRef", "available", "busy", "error", "select", "index", "next", "modelName", "modelLabel", "effortLabel", "preferredOrder", "subscribeProviderOrder", "providerOrder", "disabled", "subscribeDisabledProviders", "disabledProviders", "modelOrderByProvider", "subscribeModelOrders", "modelOrders", "providerCatalog", "providerIds", "mergeProviderOrder", "baseProviderOrder", "shownProvider", "shownModels", "orderModelEntries", "activeKey", "togglePanel", "collapsePanel", "focusMenu", "menu", "chooseModel", "provider", "id", "now", "outcome", "refusal", "selectRefusalMessage", "cause", "chooseEffort", "previous", "snapshot", "settled", "rows", "choice", "key", "active", "import_react", "GROUP_SELECTOR", "OPTION_SELECTOR", "isModelPane", "menu", "visibleOptions", "btn", "groupNameOf", "group", "labelledBy", "filterMenuModels", "query", "touched", "hide", "element", "show", "original", "tokens", "groups", "totalVisible", "groupName", "buttons", "visibleInGroup", "titleText", "contentText", "combined", "token", "ModelSearch", "setQuery", "setTotalVisible", "inputRef", "queryRef", "touchedRef", "runFilter", "nextQuery", "count", "observer", "onKeyDown", "event", "key", "options", "current", "option", "next", "handleKeyDown", "handleClear", "hasEmptyState", "e", "val", "import_react", "import_react_dom", "import_client", "panelRoot", "mountReact", "container", "children", "options", "root", "unmountReact", "mount", "EffortBoundary", "__publicField", "error", "info", "rePlaceInFrame", "createComposerMenu", "deps", "menuOf", "triggerOf", "t", "refreshed", "sliderEnabled", "directory", "sliderMount", "searchMount", "lastModelPane", "triggerDirectory", "triggerUnsubscribe", "decoratedTrigger", "clearTriggerDecoration", "updateTriggerDecoration", "trigger", "state", "current", "group", "candidate", "provider", "providerLabelOf", "syncTriggerDirectory", "next", "unmountSearch", "unmountReact", "reconcileSearch", "menu", "container", "referenceNode", "wrapper", "PLUGIN_ID", "mountReact", "EffortBoundary", "ModelSearch", "reconcileSliderInto", "el", "resolved", "ComposerSlider", "cells", "cell", "modelPane", "isModelPane", "isRecord", "value", "looksLikeCompatRefusal", "message", "msg", "modelsOf", "providers", "route", "profile", "routeFactsOf", "api", "baseURL", "displayName", "hasBoolean", "entry", "key", "findListingEntry", "listing", "modelId", "isRecord", "analyzeListingEntry", "features", "signalParts", "parameters", "effortField", "capabilities", "thinking", "effort", "parts", "rawList", "value", "members", "item", "member", "architecture", "input", "modalities", "capabilityArray", "visionIn", "list", "slot", "length", "nested", "maxInput", "limitContext", "detectModelSignal", "providersOf", "namespace", "providers", "profile", "providersOfLayer", "layer", "isRecord", "baselineModelsOf", "route", "userProvidersOf", "effortsOf", "models", "modelId", "entry", "model", "efforts", "inputOf", "input", "members", "member", "compatOf", "compat", "nameOf", "name", "defaultEffortOf", "effort", "DEFAULT_EFFORT_FIELD", "probeEndpoint", "response", "PROBE_PATH", "body", "detectModelSignal", "createEditorApi", "api", "describe", "describeNamespace", "stage", "hold", "withdraw", "stagedOf", "stagedFacts", "stored", "routeFactsOf", "facts", "endpoint", "suggestion", "suggestEfforts", "write", "defaultEffort", "rawEfforts", "clearCompatKeys", "result", "writeModelRows", "rows", "abort", "downgraded", "attempt", "join", "rawModels", "nextModels", "missing", "row", "index", "mergeRowIntent", "PI_AI_NS", "looksCompat", "looksLikeCompatRefusal", "error", "copy", "touch", "UNSET_MARKER", "AUTOFILL_MARKER", "merged", "key", "INPUT_UNSET_MARKER", "providerHeadersOf", "headers", "pairs", "value", "writeProviderHeaders", "routeIsDeclared", "options", "viaScope", "joinFromScope", "ns", "snapshot", "createConfiguredEfforts", "deps", "configuredEfforts", "configuredEffortsInflight", "configuredEffortsGeneration", "ensureConfiguredEfforts", "generation", "describeNamespace", "join", "map", "route", "profile", "providersOf", "rawModels", "model", "id", "effort", "DEFAULT_EFFORT_FIELD", "provider", "declaresInput", "model", "input", "buildAutofillPatch", "providers", "routeFilter", "options", "revision", "fillEfforts", "fillModalities", "isRecord", "patchRoutes", "route", "profile", "rawModels", "models", "nextModels", "changed", "effortsDeclared", "UNSET_MARKER", "inputDeclared", "INPUT_UNSET_MARKER", "backfilled", "backfillRolePin", "id", "routeInfo", "routeFactsOf", "name", "suggestion", "suggestEfforts", "fill", "touched", "AUTOFILL_MARKER", "stored", "backfillRolePin", "model", "providers", "route", "isRecord", "UNSET_MARKER", "compat", "base", "isSelfHostedRelay", "routeFactsOf", "createIdleAutofill", "deps", "autofillSwitches", "autofillSwitchesOf", "fallback", "response", "AUTOFILL_CONFIG_PATH", "body", "runIdleAutofill", "switches", "join", "describeNamespace", "namespace", "userProviders", "userProvidersOf", "patch", "buildAutofillPatch", "routes", "ops", "route", "profile", "PI_AI_NS", "error", "import_react", "import_client", "import_react", "LEVEL_ORDER", "THINKING_LEVELS", "draftFrom", "efforts", "draft", "level", "wire", "buildIntent", "off", "thinking", "out", "cell", "sameEfforts", "a", "b", "aKeys", "bKeys", "defaultWireRisk", "declared", "thinkingFormat", "AUTO_EFFORT_EVENT", "tail", "queueAutoEffort", "run", "requestAutoEffort", "route", "target", "listenAutoEffort", "handler", "listener", "event", "detail", "import_jsx_runtime", "COUNT", "modalityFrom", "input", "buildModalityIntent", "draft", "OWNED_COMPAT_KEYS", "ownedCompatKeys", "routeApi", "clearedCompatKeys", "owned", "set", "key", "compatClearIntent", "writeCompat", "sameModality", "stored", "ladderIntent", "nextThinking", "built", "initialEfforts", "next", "compatDraftOf", "compat", "out", "budgetField", "pruned", "maxOutput", "pendingWriteOf", "parts", "baseCompat", "manualCompat", "EffortEditor", "route", "routeBaseURL", "modelId", "modelName", "effortsUnset", "initialInput", "initialCompat", "initialDefaultEffort", "index", "staged", "officialInputTypes", "api", "readOnly", "t", "setDraft", "draftFrom", "modality", "setModality", "thinking", "setThinking", "defaultEffort", "setDefaultEffort", "busy", "setBusy", "message", "setMessage", "suggested", "setSuggested", "suggestedSource", "setSuggestedSource", "suggestedConfidence", "setSuggestedConfidence", "appliedCompatRef", "setBudgetField", "priorityText", "setPriorityText", "setMaxOutput", "previousCompat", "suggestedInputSource", "setSuggestedInputSource", "referenceContext", "setReferenceContext", "referenceMaxTokens", "setReferenceMaxTokens", "dirtyRef", "previousEfforts", "previousInput", "previousDefaultEffort", "autoAdaptRef", "savedEffortsRef", "effortsUnsetRef", "listenAutoEffort", "target", "run", "queueAutoEffort", "sameEfforts", "compatDraft", "compatChanged", "draftOut", "armedLevels", "LEVEL_ORDER", "level", "defaultEffortIntent", "effectiveEfforts", "buildIntent", "changed", "lastCommitRef", "markDirty", "commitPending", "nextDraft", "nextModality", "nextDefaultEffort", "effortsIntent", "nextInput", "write", "signature", "patchLevel", "on", "current", "patchThinking", "patchWire", "wire", "patchImage", "clearModality", "patchBudgetField", "value", "patchPriority", "patchMaxOutput", "patchDefaultEffort", "applySuggestion", "nextDefault", "autoAdapt", "reply", "error", "reset", "wireFormat", "suggestEfforts", "wireRisk", "defaultWireRisk", "disabled", "PLUGIN_ID", "event", "cell", "SEAT_ATTR", "CATALOG_PROBE", "HEAD_PROBE", "LINK_PROBE", "SEAT_CLASS", "NOTE_CLASS", "NOTE_PHASE_ATTR", "NOTE_TEXT_CLASS", "NOTE_DETAIL_CLASS", "OUTCOME_TTL_MS", "outcomeOf", "report", "cataloguesOf", "scope", "candidate", "reconcileAutoEffortSeats", "root", "targets", "deps", "wanted", "target", "label", "catalogue", "route", "head", "seat", "createSeat", "anchor", "lastLink", "syncSeat", "seated", "routes", "hideNote", "entry", "notes", "teardownAutoEffortSeats", "links", "requestAutoEffort", "runRequest", "showNote", "answer", "error", "outcome", "ttlMs", "note", "buildNote", "text", "detail", "wording", "wordsOf", "timer", "hasOwn", "object", "key", "LEDGER_DOCUMENT_KEY", "documentId", "holder", "LEDGER_KEY", "ledgerStorage", "persistLedger", "state", "store", "file", "route", "models", "ledgerEntry", "value", "validate", "routes", "row", "entries", "entry", "modelId", "write", "isRecord", "restoreLedger", "raw", "parsed", "pending", "queued", "isHeldWrite", "committed", "flushOnTeardown", "deps", "flushQueued", "flushPending", "error", "flushOnUnload", "createScanState", "runIdlePass", "queuedFailed", "pendingFailed", "armBackoff", "delay", "hasOutstanding", "stageEffortsInto", "efforts", "compat", "input", "defaultEffort", "queueWriteInto", "withdrawStaged", "withdrawHeldWrite", "withdrawIntent", "forgetRoute", "effectiveStagedIntents", "declaration", "current", "autofill", "ladderTaken", "UNSET_MARKER", "AUTOFILL_MARKER", "sameEffortsValue", "inputTaken", "INPUT_UNSET_MARKER", "sameInputList", "a", "b", "keys", "other", "set", "member", "clear", "flushRoute", "ignoreFence", "join", "providers", "providersOf", "intents", "modelsOf", "model", "routeInfo", "routeFactsOf", "name", "suggestion", "suggestEfforts", "effective", "seeded", "results", "writeModelRows", "officialCardOpen", "failed", "intent", "at", "result", "aborted", "inputValueByLabel", "card", "labels", "label", "candidate", "cardOf", "trigger", "disclosureOf", "candidates", "officialInputTypesOf", "container", "seatEmptyCatalog", "root", "officialCardOf", "button", "reconcileAutoEffortSeats", "emptyCatalogTargets", "adaptEveryModel", "targets", "catalogue", "cataloguesOf", "resolved", "routeOfCard", "autoEffortTargets", "found", "seen", "held", "unsuggested", "describe", "profile", "routeApi", "covered", "api", "createEditorApi", "heldRoute", "initialEfforts", "effortsOf", "initialInput", "inputOf", "initialCompat", "compatOf", "initialDefault", "defaultEffortOf", "reply", "nameOf", "parts", "nextThinking", "nextModality", "modalityFrom", "nextDefault", "pendingWriteOf", "ladderIntent", "buildIntent", "draftFrom", "sameModality", "buildModalityIntent", "compatDraftOf", "hasEditor", "PLUGIN_ID", "sameProps", "sameEfforts", "sameInput", "sameCompat", "setA", "item", "hasLabeledInput", "typed", "title", "byName", "actionsOf", "buttons", "matches", "node", "primary", "secondary", "submit", "cancel", "last", "previous", "wireOnce", "wired", "onHit", "panelRoot", "reconcile", "cardOpen", "hasCapacityRows", "wasEditing", "seatLabels", "run", "namespace", "aria", "triggers", "onPage", "target", "ids", "missing", "present", "stale", "id", "prior", "cards", "actions", "live", "index", "routeStaged", "savedModelIds", "staged", "stagedEfforts", "effortsUnset", "typedName", "modelName", "typedApi", "typedBaseURL", "routeBaseURL", "next", "r", "m", "e", "c", "i", "de", "w", "existing", "editor", "setAttr", "el", "name", "value", "setHidden", "hidden", "setClass", "on", "setText", "text", "import_react", "import_react", "normalizeKeyAlias", "value", "cleaned", "char", "code", "isLegalKeyValue", "messageOf", "body", "field", "value", "rowOf", "isRecord", "ref", "alias", "configured", "masked", "stateOf", "entries", "rows", "entry", "row", "enabledRef", "route", "answerOf", "response", "state", "error", "request", "payload", "url", "KEY_INDEX_PATH", "httpKeyManagerClient", "PROVIDER_KEY_PATH", "key", "import_jsx_runtime", "CHECK_PATH", "PLUS_PATHS", "Glyph", "paths", "d", "previewOf", "row", "nameOf", "KeyManager", "props", "route", "t", "client", "httpKeyManagerClient", "group", "rows", "setRows", "enabledRef", "setEnabledRef", "notice", "setNotice", "busy", "setBusy", "editing", "setEditing", "nameDraft", "setNameDraft", "valueDraft", "setValueDraft", "confirming", "setConfirming", "adopt", "answer", "okText", "live", "run", "action", "closeForm", "openForm", "kind", "submit", "ref", "isLegalKeyValue", "alias", "normalizeKeyAlias", "form", "wantsValue", "illegal", "event", "addEntry", "name", "KEY_MANAGED_CLASS", "KEY_HOST_CLASS", "KEY_HOST_ATTR", "KEY_INPUT_ATTR", "KEY_REGION_ATTR", "FIELDS", "keyInputOf", "field", "marked", "secret", "hostOf", "known", "next", "enhanceKeyManagerField", "deps", "route", "setClass", "host", "drop", "setAttr", "region", "subtree", "EffortBoundary", "KeyManager", "state", "mountReact", "unmountReact", "resetKeyManagerField", "teardownKeyManager", "SVG_NS", "ICON_ATTR", "svgPath", "d", "path", "svgGlyph", "kind", "paths", "svg", "KEY_FIELD_CLASS", "KEY_EYE_CLASS", "KEY_INPUT_ATTR", "SHOW_LABEL_ATTR", "HIDE_LABEL_ATTR", "EYE_PATHS", "DEPS", "keyInputOf", "field", "marked", "masked", "eyeOf", "child", "revealKeyField", "deps", "input", "resetKeyField", "setAttr", "setClass", "eye", "createEye", "syncEye", "loadProviderKey", "route", "response", "PROVIDER_KEY_PATH", "body", "revealed", "label", "setHidden", "writeInputValue", "value", "descriptor", "svgIcon", "borrowed", "toggle", "key", "failed", "event", "kind", "paths", "svgGlyph", "TABS_CLASS", "TAB_BUTTON_CLASS", "TAB_ID_ATTR", "TABBED_CLASS", "TAB_ATTR", "PROVIDER_ROUTE_ATTR", "REGION_ATTR", "BODY_ATTR", "DETAILS_MEMO_ATTR", "HEADERS_SECTION_SELECTOR", "TAB_PROVIDER", "TAB_MODELS", "TAB_ADVANCED", "TAB_IDS", "REGION_NONE", "EDITOR_TAB_IDS", "EDITOR_TABS_CLASS", "TABS_CLASS", "EDITOR_TAB_BUTTON_CLASS", "TAB_BUTTON_CLASS", "EDITOR_TAB_ID_ATTR", "TAB_ID_ATTR", "EDITOR_PROBE", "MODELS_TERMINAL", "CARD_PROBE", "createEditorTabsState", "editorCardOf", "element", "selectEditorTab", "card", "id", "setAttr", "TAB_ATTR", "syncSelected", "reconcileEditorTabs", "root", "deps", "state", "open", "probe", "TABBED_CLASS", "cleanupCard", "applyTabs", "error", "teardownEditorTabs", "BODY_ATTR", "el", "REGION_ATTR", "group", "DETAILS_MEMO_ATTR", "field", "KEY_FIELD_CLASS", "resetKeyField", "KEY_MANAGED_CLASS", "resetKeyManagerField", "teardownKeyManager", "editorBodyOf", "child", "node", "classifyBody", "body", "regions", "classifyChildren", "actions", "fields", "header", "models", "CLASSIFY_MAX_DEPTH", "container", "depth", "providerRouteOf", "route", "PROVIDER_ROUTE_ATTR", "headersSection", "revealKeyField", "enhanceKeyManagerField", "forceGroupsOpen", "available", "active", "setClass", "syncTabBar", "bar", "setText", "tabOf", "setHidden", "target", "tab", "restoreGroups", "HANDLE_ATTR", "HEAD_ATTR", "DROP_SIDE_ATTR", "DRAGGING_CLASS", "DROP_CLASS", "GHOST_CLASS", "LINE_CLASS", "SVG_NS", "ROW_GAP", "EDGE_PX", "SCROLL_STEP", "MOVE_TRANSITION", "INTERACTIVE", "createDragState", "rowElements", "parent", "child", "orderOf", "row", "index", "raw", "parsed", "visualRows", "list", "left", "right", "entry", "placeRows", "sequence", "current", "named", "slots", "ordered", "target", "slot", "position", "value", "slideIntoPlace", "mutate", "rows", "before", "delta", "orderedIds", "idOf", "id", "applyIdOrder", "ids", "rank", "leftRank", "rightRank", "clearRowOrder", "dropIndex", "carried", "hovered", "after", "at", "nearestRow", "clientY", "best", "bestDistance", "rect", "distance", "isAfter", "dropLineTop", "gripIcon", "svg", "dots", "cx", "cy", "dot", "makeGhost", "label", "ghost", "name", "printed", "interactiveTarget", "listOf", "clearDropMarks", "scrollHostOf", "node", "scrolls", "root", "declared", "overflow", "edgeScrollStep", "host", "setScrollStep", "state", "session", "step", "tick", "dropLine", "existing", "line", "showDropLine", "hideDropLine", "finishDrag", "startDrag", "event", "adapter", "dataTransfer", "dragOver", "over", "moveToIndex", "wireList", "wireSource", "source", "wireGrip", "grip", "syncGrip", "hint", "ensureGrip", "seat", "reconcileRows", "teardownDrags", "head", "ROW_ID_ATTR", "PROVIDER_TOKEN", "ROW_CLASS_PROBES", "createProviderOrderState", "createDragState", "rowsListOf", "root", "probe", "hit", "parent", "rowElements", "rowHeadOf", "row", "rowNameOf", "name", "templateParts", "template", "at", "idFromTargetLabel", "label", "trimmed", "parenthesized", "route", "CARD_ROOT_PROBE", "isForeign", "node", "card", "el", "providerIdOfRow", "labels", "stamped", "parts", "button", "id", "rowIdOf", "value", "orderedRowIds", "list", "orderedIds", "reconcileProviderOrder", "root", "deps", "state", "list", "rowsListOf", "labels", "row", "rowElements", "id", "providerIdOfRow", "ROW_ID_ATTR", "rowIdOf", "reconcileRows", "providerAdapter", "node", "rowNameOf", "ids", "resolveProviderOrder", "setProviderOrder", "orderedRowIds", "rowHeadOf", "teardownProviderOrder", "teardownDrags", "stamped", "clearRowOrder", "SWITCH_ATTR", "OFF_CLASS", "createProviderToggleState", "reconcileProviderEnabled", "root", "deps", "state", "list", "rowsListOf", "labels", "child", "id", "providerIdOfRow", "head", "rowHeadOf", "ensureSwitch", "isProviderEnabled", "teardownProviderEnabled", "switchButton", "row", "existing", "syncSwitch", "control", "wireSwitch", "actions", "label", "rowNameOf", "hint", "checked", "event", "next", "setProviderEnabled", "SEAT_ATTR", "ACTIONS_PROBE", "OFFICIAL_DELETE_PROBE", "SEAT_CLASS", "reconcileProviderDeleteSeat", "root", "deps", "list", "rowsListOf", "labels", "label", "officialDeleteLabel", "child", "id", "providerIdOfRow", "head", "rowHeadOf", "actions", "existing", "seat", "createSeat", "syncSeat", "rowNameOf", "teardownProviderDeleteSeats", "text", "hint", "ROW_ID_ATTR", "MODEL_ROW_CLASS", "createModelOrderState", "createDragState", "modelListsOf", "root", "lists", "hit", "rowElements", "child", "modelIdOf", "entry", "value", "providerOfList", "list", "deps", "row", "providerIdOfRow", "modelLabelOf", "name", "inputValueByLabel", "modelAdapter", "node", "ids", "provider", "resolveModelOrder", "target", "setModelOrder", "orderedIds", "id", "_entry", "seat", "reconcileModelOrder", "state", "labels", "reconcileRows", "teardownModelOrder", "teardownDrags", "stamped", "clearRowOrder", "HOST_MODELS_NS", "HOST_LABEL_KEYS", "createModelsPage", "deps", "ctx", "api", "t", "onIdle", "refreshed", "labels", "translate", "resolve", "key", "fallbacks", "value", "SCAN_DEBOUNCE_MS", "scanState", "createScanState", "providerOrderState", "createProviderOrderState", "providerOrderDeps", "providerToggleState", "createProviderToggleState", "providerToggleDeps", "providerDeleteDeps", "modelOrderState", "createModelOrderState", "modelOrderDeps", "editorTabsState", "createEditorTabsState", "editorTabsDeps", "scanTimer", "retryTimer", "observer", "composerMutation", "stopped", "injectorDeps", "describeNamespace", "delayMs", "scheduleRetry", "container", "props", "rootEl", "PLUGIN_ID", "reactRoot", "renderEditor", "p", "EffortBoundary", "EffortEditor", "schedule", "reconcile", "panelRoot", "reconcileEditorTabs", "reconcileProviderOrder", "reconcileProviderEnabled", "reconcileProviderDeleteSeat", "reconcileModelOrder", "start", "onComposerMutation", "stopObserver", "flushOnTeardown", "entry", "teardownProviderOrder", "teardownProviderEnabled", "teardownProviderDeleteSeats", "teardownAutoEffortSeats", "teardownModelOrder", "teardownEditorTabs", "flushOnUnload", "currentSessionId", "sessions", "uiSession", "snapshot", "direct", "bound", "key", "row", "createSessionDirectoryTracker", "deps", "current", "wiredDirectories", "sweepDeadWiretaps", "directories", "liveDirectory", "wired", "entry", "alive", "host", "sessionId", "directory", "wireEffortMemory", "MODEL_SEAT_SLOT", "composerCardOf", "doc", "seatAnchorOf", "card", "seatTriggerCandidates", "scope", "linkedMenuOf", "trigger", "id", "menu", "findModelMenu", "findModelTrigger", "anchor", "candidates", "seat", "modelMenuOf", "modelTriggerOf", "import_react", "import_react", "import_jsx_runtime", "USER_AGENT_PRESETS", "draftFrom", "headers", "id", "rows", "name", "value", "headersFromDraft", "draft", "out", "hasUserAgent", "row", "legacyUserAgent", "HeadersEditor", "route", "api", "t", "uaListId", "stored", "setStored", "setDraft", "open", "setOpen", "editing", "setEditing", "loading", "setLoading", "busy", "setBusy", "readOnly", "setReadOnly", "message", "setMessage", "environment", "setEnvironment", "tabbed", "setTabbed", "nextRowId", "rootRef", "live", "join", "describeNamespace", "providerHeadersOf", "body", "HEADERS_CONFIG_PATH", "root", "sync", "observer", "addRow", "current", "patchRow", "patch", "removeRow", "cancel", "save", "next", "reply", "writeProviderHeaders", "error", "disabled", "count", "inline", "PLUGIN_ID", "item", "event", "preset", "import_react", "LABEL_KEYS", "EditorTabBar", "host", "EDITOR_TABS_CLASS", "EDITOR_TAB_IDS", "id", "EDITOR_TAB_BUTTON_CLASS", "EDITOR_TAB_ID_ATTR", "event", "card", "editorCardOf", "selectEditorTab", "SLOT", "ProviderCardSlot", "provider", "api", "t", "host", "wrapper", "card", "editorCardOf", "observed", "openEditor", "sync", "edit", "observer", "route", "EditorTabBar", "HeadersEditor", "registerProviderCardSlot", "ctx", "injected", "PI_AI_NS", "import_react", "SliderToggle", "props", "enabled", "subscribeSliderEnabled", "sliderEnabled", "toggle", "setSliderEnabled", "registerSliderToggleSlot", "ctx", "host", "injected", "PLUGIN_ID", "SliderToggle", "name", "PLUGIN_ID", "inject", "apply", "ctx", "STORE_NS", "zh", "en", "style", "STYLES", "settingsScope", "forms", "current", "PI_AI_NS", "settingsApi", "t", "localeFace", "locale", "refreshed", "children", "face", "LocaleRefresh", "configuredEfforts", "createConfiguredEfforts", "sessionDirectory", "createSessionDirectoryTracker", "composerMenu", "createComposerMenu", "modelMenuOf", "modelTriggerOf", "sliderEnabled", "autofill", "createIdleAutofill", "modelsPage", "createModelsPage", "onComposerMutation", "onPageHide", "refresh", "disposers", "ns", "dispose", "onStorage", "event", "SLIDER_PREF_KEY", "syncSliderEnabled", "PROVIDER_ORDER_KEY", "syncProviderOrder", "parseProviderOrder", "PROVIDER_DISABLED_KEY", "syncDisabledProviders", "parseDisabledProviders", "MODEL_ORDER_KEY", "syncModelOrders", "parseModelOrders", "subscribeSliderEnabled", "disposeOrder", "subscribeProviderOrder", "disposeEnabled", "subscribeDisabledProviders", "disposeModelOrder", "subscribeModelOrders", "registerSliderToggleSlot", "registerProviderCardSlot"]
|
|
7
7
|
}
|