@coool/opencode-adaptive 1.2.3 → 1.4.0
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/README.md +1 -0
- package/dist/adapters/opencode/command.d.ts +30 -0
- package/dist/adapters/opencode/command.js +85 -0
- package/dist/adapters/opencode/command.js.map +1 -0
- package/dist/adapters/opencode/index.d.ts +1 -0
- package/dist/adapters/opencode/index.js +1 -0
- package/dist/adapters/opencode/index.js.map +1 -1
- package/dist/gui-experimental.cjs +3 -1
- package/dist/gui.d.ts +2 -1
- package/dist/gui.js +7 -5
- package/dist/gui.js.map +1 -1
- package/dist/index.js +14 -2
- package/dist/index.js.map +1 -1
- package/dist/rpc/handlers.d.ts +6 -0
- package/dist/rpc/handlers.js +24 -18
- package/dist/rpc/handlers.js.map +1 -1
- package/dist/tui.js +5 -3
- package/dist/tui.js.map +1 -1
- package/dist/ui/{tui → flow}/controller.d.ts +5 -8
- package/dist/ui/{tui → flow}/controller.js +4 -9
- package/dist/ui/flow/controller.js.map +1 -0
- package/dist/ui/{tui → flow}/menu.d.ts +2 -0
- package/dist/ui/{tui → flow}/menu.js +1 -1
- package/dist/ui/flow/menu.js.map +1 -0
- package/dist/ui/flow/options.d.ts +12 -0
- package/dist/ui/flow/options.js.map +1 -0
- package/dist/ui/{tui → flow}/receipts.d.ts +1 -1
- package/dist/ui/{tui → flow}/receipts.js +4 -7
- package/dist/ui/flow/receipts.js.map +1 -0
- package/dist/ui/{tui → flow}/status.d.ts +0 -2
- package/dist/ui/{tui → flow}/status.js +0 -6
- package/dist/ui/flow/status.js.map +1 -0
- package/dist/ui/{tui → flow}/tiers.d.ts +2 -9
- package/dist/ui/{tui → flow}/tiers.js +5 -25
- package/dist/ui/flow/tiers.js.map +1 -0
- package/dist/ui/gui/api.d.ts +6 -2
- package/dist/ui/gui/api.js +4 -4
- package/dist/ui/gui/api.js.map +1 -1
- package/dist/ui/gui/flow.d.ts +23 -0
- package/dist/ui/gui/flow.js +123 -0
- package/dist/ui/gui/flow.js.map +1 -0
- package/dist/ui/gui/view.js +2 -2
- package/dist/ui/gui/view.js.map +1 -1
- package/dist/ui/tui/search.d.ts +9 -0
- package/dist/ui/tui/search.js +10 -0
- package/dist/ui/tui/search.js.map +1 -0
- package/package.json +2 -1
- package/dist/ui/tui/controller.js.map +0 -1
- package/dist/ui/tui/menu.js.map +0 -1
- package/dist/ui/tui/options.d.ts +0 -19
- package/dist/ui/tui/options.js.map +0 -1
- package/dist/ui/tui/receipts.js.map +0 -1
- package/dist/ui/tui/status.js.map +0 -1
- package/dist/ui/tui/tiers.js.map +0 -1
- /package/dist/ui/{tui → flow}/options.js +0 -0
package/dist/tui.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tui.js","sourceRoot":"","sources":["../src/tui.tsx"],"names":[],"mappings":"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","sourcesContent":["/** @jsxImportSource @opentui/solid */\n// The pragma pins the JSX runtime for this file. The host compiles plugin\n// sources in a cache dir where its loader's tsconfig lookup finds nothing and\n// its default (react/jsx-runtime) fails to resolve there (empirical, sibling\n// @coool plugins); our own tsc build reads the same pragma, so dist/tui.js\n// imports @opentui/solid/jsx-runtime — the renderer the slot render runs on.\n/**\n * TUI entrypoint (ADP-033): registers the `/adaptive` slash command and\n * drives the dialog-based flows through typed RPC, plus the footer status\n * chip rendered through host UI slots. The TUI is a thin view — no local\n * settings state beyond the chip's reactive mode mirror; the server stays\n * the single source of truth. This file is the repo's one JSX surface.\n */\nimport { define } from \"@opencode/plugin/tui/plugin\"\nimport type { Context } from \"@opencode/plugin/tui/plugin\"\nimport { TextAttributes } from \"@opentui/core\"\nimport { createStore } from \"solid-js/store\"\nimport { PLUGIN_ID } from \"./plugin-id.js\"\nimport { adaptiveRpc } from \"./rpc/definition.js\"\nimport type { OperatingMode } from \"./core/scheduler/types.js\"\nimport { parseMode } from \"./state/settings/index.js\"\nimport { runAdaptive } from \"./ui/tui/controller.js\"\nimport type { AdaptiveApi, AdaptiveView } from \"./ui/tui/controller.js\"\nimport type { SelectOption } from \"./ui/tui/options.js\"\nimport { errorText } from \"./ui/tui/options.js\"\nimport type { ModelSummary } from \"./ui/tui/tiers.js\"\n\n/** Footer chip appearance: label plus foreground color (null = theme default). */\nexport interface Chip {\n readonly text: string\n readonly fg: string | null\n}\n\n/**\n * Label/color mapping for the footer chip. Pure so the mapping is testable\n * without a JSX runtime: active is green (supervision engaged), observe is\n * the neutral default (visible but calm), off is a soft red — noticeable,\n * but calmer than full-brightness ANSI red, which reads as an error.\n */\nexport function chipFor(mode: OperatingMode): Chip {\n switch (mode) {\n case \"active\":\n return { text: \"Adaptive · active\", fg: \"green\" }\n case \"observe\":\n return { text: \"Adaptive · observe\", fg: null }\n case \"off\":\n return { text: \"Adaptive · off\", fg: \"#e57373\" }\n }\n}\n\nconst BOOT_FETCH_RETRIES = 5\nconst BOOT_FETCH_RETRY_MS = 2000\n\nexport default define({\n id: PLUGIN_ID,\n setup(context: Context) {\n const client = context.client.rpc(adaptiveRpc)\n // RPC handlers resolve per location: a location-less call never reaches a\n // project-local plugin's handlers (\"RPC is unavailable\", verified live in\n // the sibling @coool plugins), so every call carries the TUI's location.\n const at = () => ({ location: context.location })\n const api: AdaptiveApi = {\n settings: () => client[\"settings.get\"](undefined, at()),\n setSettings: (patch) => client[\"settings.set\"](patch, at()),\n status: () => client[\"status.get\"](undefined, at()),\n receipts: () => client[\"receipts.list\"](undefined, at()),\n sessionMode: (sessionID) =>\n client[\"session-mode.get\"]({ sessionID }, at()).then((out) => out.mode),\n setSessionMode: (sessionID, mode) => client[\"session-mode.set\"]({ sessionID, mode }, at()),\n tierRequest: (sessionID) =>\n client[\"tier-request.get\"]({ sessionID }, at()).then((out) => out.tier),\n setTierRequest: (sessionID, tier) => client[\"tier-request.set\"]({ sessionID, tier }, at()),\n }\n const view: AdaptiveView = {\n select: (options) => context.ui.dialog.select(options),\n selectPaged: <Value,>(spec: {\n title: string\n options: readonly SelectOption<Value>[]\n actions: readonly { title: string; value: string; bind: string }[]\n }) =>\n // Footer actions flip pages: triggering one closes the dialog and\n // resolves with the action instead of a pick; the controller loop\n // then reopens the browser at the new offset. The host resolves its\n // own select promise undefined on the clear() — resolving our\n // promise first-come means the action result wins.\n new Promise<{ picked: Value | undefined; action: string | undefined }>(\n (resolve, reject) => {\n void context.ui.dialog\n .select({\n title: spec.title,\n options: spec.options,\n actions: spec.actions.map((action) => ({\n bind: action.bind,\n title: action.title,\n selection: \"none\" as const,\n onTrigger: () => {\n context.ui.dialog.clear()\n resolve({ picked: undefined, action: action.value })\n },\n })),\n })\n .then((picked) => {\n resolve({ picked, action: undefined })\n }, reject)\n },\n ),\n confirm: (options) => context.ui.dialog.confirm(options),\n alert: (options) => context.ui.dialog.alert(options),\n table: (spec) =>\n // The host has no table dialog; a custom dialog render gives the\n // column grid its own layout. The default dialog size is too narrow\n // for the column grid (rows wrap mid-token), so the dialog is\n // widened to xlarge right after it opens. Escape closes the dialog\n // and fires onClose, which resolves the view promise.\n new Promise<void>((resolve) => {\n context.ui.dialog.show(\n () => (\n // Styled to match the host's own dialogs (bold title, dim esc\n // hint). The host dialog panel already insets the content, so\n // the box adds no top padding of its own; a blank spacer row\n // separates the title row from the table.\n <box style={{ flexDirection: \"column\", paddingBottom: 1, paddingX: 2 }}>\n <box style={{ flexDirection: \"row\", justifyContent: \"space-between\" }}>\n <text style={{ attributes: TextAttributes.BOLD }}>{spec.title}</text>\n <text style={{ attributes: TextAttributes.DIM }}>esc</text>\n </box>\n <text> </text>\n {spec.lines.map((line) => (\n <text>{line}</text>\n ))}\n </box>\n ),\n () => {\n resolve()\n },\n )\n context.ui.dialog.set({ size: \"xlarge\" })\n }),\n toast: (message, variant) => {\n context.ui.toast.show({ message, variant })\n },\n }\n // Real OpenCode models for the tier editor search (ADP-033); structural\n // summaries keep the view models free of client types.\n const models = (): readonly ModelSummary[] => {\n const list = context.data.location.model.list(context.location)\n return (list ?? []).map((info) => ({\n providerID: info.providerID,\n modelID: info.modelID,\n name: info.name,\n variants: info.variants.map((variant) => variant.id),\n }))\n }\n context.keymap.layer(() => ({\n // \"global\" opts out of input-mode scoping: the default (\"base\") binds\n // the commands to one OpenCode input mode, which kept the palette from\n // matching them (the sibling @coool plugins register the same way).\n mode: \"global\",\n commands: [\n {\n id: \"coool.adaptive.menu\",\n title: \"Adaptive\",\n group: \"Adaptive\",\n palette: true,\n // Surface the command in the palette's suggested row: discovery was\n // the reason users never found the menu at all.\n suggested: true,\n slash: { name: \"adaptive\" },\n run: () => {\n // Inside a session the flows steer that session's mode override\n // (ADP-054); on home they patch the global mode.\n const current = context.ui.router.current()\n const sessionID = current.type === \"session\" ? current.sessionID : undefined\n void runAdaptive({\n api,\n view,\n models,\n ...(sessionID !== undefined ? { sessionID } : {}),\n }).catch((error: unknown) => {\n view.toast(errorText(error), \"error\")\n })\n },\n },\n ],\n }))\n\n // Footer chip state: a reactive mirror of the server's operating mode,\n // fetched once at boot and kept live via the settings-changed event, plus\n // the per-session overrides seen so far (ADP-054).\n const [chip, setChip] = createStore<{\n mode: OperatingMode | null\n sessions: Record<string, OperatingMode | null>\n }>({ mode: null, sessions: {} })\n\n const fetchMode = async (): Promise<boolean> => {\n try {\n const settings = await api.settings()\n const mode = parseMode(settings.mode)\n if (mode !== undefined) setChip(\"mode\", mode)\n return true\n } catch {\n return false\n }\n }\n\n /**\n * One session's override, cached so the chip never re-fetches per render.\n * A fetch failure caches `null` (follow global): the chip stays correct\n * for override-less sessions, and a later `session-mode-changed` event\n * refreshes sessions that do carry an override.\n */\n const fetchSessionMode = async (sessionID: string): Promise<void> => {\n try {\n setChip(\"sessions\", sessionID, await api.sessionMode(sessionID))\n } catch {\n setChip(\"sessions\", sessionID, null)\n }\n }\n\n const bootFetch = async (retriesLeft: number): Promise<void> => {\n if (await fetchMode()) return\n // The server plugin may not be registered yet at TUI boot; retry\n // quietly. Bounded (unlike the sibling plugins' unbounded retry) so the\n // timer loop provably terminates: after the cap the chip stays hidden\n // until the next settings-changed event refreshes it.\n if (retriesLeft > 0) {\n setTimeout(() => void bootFetch(retriesLeft - 1), BOOT_FETCH_RETRY_MS)\n }\n }\n\n const renderChip = (mode: OperatingMode | null) => {\n if (mode === null) return <text></text>\n const entry = chipFor(mode)\n // The accent-colored span cannot execute in-repo: the terminal renderer\n // cannot start headless and jsdom's HTML parser mangles the intrinsic\n // <text><span> template (null insert parent). The chipFor tests pin the\n // color mapping; the chip render test pins the plain observe shape.\n /* v8 ignore start */\n if (entry.fg === null) return <text>{entry.text}</text>\n return (\n <text>\n <span style={{ fg: entry.fg }}>{entry.text}</span>\n </text>\n )\n /* v8 ignore stop */\n }\n const chipNode = (sessionID: string | undefined) => {\n // On home (no session id) the chip shows the global mode; in a session\n // it shows the session's EFFECTIVE mode (override ?? global).\n if (sessionID === undefined) return renderChip(chip.mode)\n const override = chip.sessions[sessionID]\n if (override === undefined) {\n // First render for this session: fetch the override; the store write\n // re-renders this node with the fetched value. Until then the global\n // mode is shown.\n void fetchSessionMode(sessionID)\n return renderChip(chip.mode)\n }\n return renderChip(override ?? chip.mode)\n }\n // The home footer slot only exists on the home screen; sessions render\n // the prompt footer — its slot input carries the open session's id, so\n // the chip there shows that session's effective mode (ADP-054).\n const disposeSlots = [\n context.ui.slot({ append: \"home.footer.status\", render: () => chipNode(undefined) }),\n context.ui.slot({\n append: \"prompt.footer.status\",\n render: (input) => chipNode(input.sessionID),\n }),\n ]\n\n const disposeWatch = client.events.on(\"settings-changed\", () => {\n void fetchMode()\n // A global mode change re-renders override-less sessions reactively;\n // cached overrides are re-fetched so a stale mirror cannot stick.\n for (const sessionID of Object.keys(chip.sessions)) {\n void fetchSessionMode(sessionID)\n }\n })\n const disposeSessionWatch = client.events.on(\"session-mode-changed\", (event) => {\n // The event data crosses the wire as an untyped record: a malformed\n // payload is ignored (fail-open) instead of steering a bogus session.\n const sessionID = event.data.sessionID\n if (typeof sessionID === \"string\") void fetchSessionMode(sessionID)\n })\n\n void bootFetch(BOOT_FETCH_RETRIES)\n\n return () => {\n for (const dispose of disposeSlots) dispose()\n disposeWatch()\n disposeSessionWatch()\n }\n },\n})\n"]}
|
|
1
|
+
{"version":3,"file":"tui.js","sourceRoot":"","sources":["../src/tui.tsx"],"names":[],"mappings":"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","sourcesContent":["/** @jsxImportSource @opentui/solid */\n// The pragma pins the JSX runtime for this file. The host compiles plugin\n// sources in a cache dir where its loader's tsconfig lookup finds nothing and\n// its default (react/jsx-runtime) fails to resolve there (empirical, sibling\n// @coool plugins); our own tsc build reads the same pragma, so dist/tui.js\n// imports @opentui/solid/jsx-runtime — the renderer the slot render runs on.\n/**\n * TUI entrypoint (ADP-033): registers the `/adaptive` slash command and\n * drives the dialog-based flows through typed RPC, plus the footer status\n * chip rendered through host UI slots. The TUI is a thin view — no local\n * settings state beyond the chip's reactive mode mirror; the server stays\n * the single source of truth. This file is the repo's one JSX surface.\n */\nimport { define } from \"@opencode/plugin/tui/plugin\"\nimport type { Context } from \"@opencode/plugin/tui/plugin\"\nimport { TextAttributes } from \"@opentui/core\"\nimport { createStore } from \"solid-js/store\"\nimport { PLUGIN_ID } from \"./plugin-id.js\"\nimport { adaptiveRpc } from \"./rpc/definition.js\"\nimport type { OperatingMode } from \"./core/scheduler/types.js\"\nimport { parseMode } from \"./state/settings/index.js\"\nimport { runAdaptive } from \"./ui/flow/controller.js\"\nimport type { AdaptiveApi, AdaptiveView } from \"./ui/flow/controller.js\"\nimport { errorText } from \"./ui/flow/options.js\"\nimport type { ModelSummary } from \"./ui/flow/tiers.js\"\nimport { matchOptions } from \"./ui/tui/search.js\"\n\n/** Footer chip appearance: label plus foreground color (null = theme default). */\nexport interface Chip {\n readonly text: string\n readonly fg: string | null\n}\n\n/**\n * Label/color mapping for the footer chip. Pure so the mapping is testable\n * without a JSX runtime: active is green (supervision engaged), observe is\n * the neutral default (visible but calm), off is a soft red — noticeable,\n * but calmer than full-brightness ANSI red, which reads as an error.\n */\nexport function chipFor(mode: OperatingMode): Chip {\n switch (mode) {\n case \"active\":\n return { text: \"Adaptive · active\", fg: \"green\" }\n case \"observe\":\n return { text: \"Adaptive · observe\", fg: null }\n case \"off\":\n return { text: \"Adaptive · off\", fg: \"#e57373\" }\n }\n}\n\nconst BOOT_FETCH_RETRIES = 5\nconst BOOT_FETCH_RETRY_MS = 2000\n\nexport default define({\n id: PLUGIN_ID,\n setup(context: Context) {\n const client = context.client.rpc(adaptiveRpc)\n // RPC handlers resolve per location: a location-less call never reaches a\n // project-local plugin's handlers (\"RPC is unavailable\", verified live in\n // the sibling @coool plugins), so every call carries the TUI's location.\n const at = () => ({ location: context.location })\n const api: AdaptiveApi = {\n settings: () => client[\"settings.get\"](undefined, at()),\n setSettings: (patch) => client[\"settings.set\"](patch, at()),\n status: () => client[\"status.get\"](undefined, at()),\n receipts: () => client[\"receipts.list\"](undefined, at()),\n sessionMode: (sessionID) =>\n client[\"session-mode.get\"]({ sessionID }, at()).then((out) => out.mode),\n setSessionMode: (sessionID, mode) => client[\"session-mode.set\"]({ sessionID, mode }, at()),\n tierRequest: (sessionID) =>\n client[\"tier-request.get\"]({ sessionID }, at()).then((out) => out.tier),\n setTierRequest: (sessionID, tier) => client[\"tier-request.set\"]({ sessionID, tier }, at()),\n }\n const view: AdaptiveView = {\n // A placeholder marks a searchable list (the model picker).\n select: (options) =>\n context.ui.dialog.select(\n options.placeholder === undefined ? options : { ...options, search: matchOptions },\n ),\n selectPaged: <Value,>(spec: {\n title: string\n options: readonly { title: string; value: Value }[]\n actions: readonly { title: string; value: string; bind: string }[]\n }) =>\n // Footer actions flip pages: triggering one closes the dialog and\n // resolves with the action instead of a pick; the controller loop\n // then reopens the browser at the new offset. The host resolves its\n // own select promise undefined on the clear() — resolving our\n // promise first-come means the action result wins.\n new Promise<{ picked: Value | undefined; action: string | undefined }>(\n (resolve, reject) => {\n void context.ui.dialog\n .select({\n title: spec.title,\n options: spec.options,\n actions: spec.actions.map((action) => ({\n bind: action.bind,\n title: action.title,\n selection: \"none\" as const,\n onTrigger: () => {\n context.ui.dialog.clear()\n resolve({ picked: undefined, action: action.value })\n },\n })),\n })\n .then((picked) => {\n resolve({ picked, action: undefined })\n }, reject)\n },\n ),\n confirm: (options) => context.ui.dialog.confirm(options),\n alert: (options) => context.ui.dialog.alert(options),\n table: (spec) =>\n // The host has no table dialog; a custom dialog render gives the\n // column grid its own layout. The default dialog size is too narrow\n // for the column grid (rows wrap mid-token), so the dialog is\n // widened to xlarge right after it opens. Escape closes the dialog\n // and fires onClose, which resolves the view promise.\n new Promise<void>((resolve) => {\n context.ui.dialog.show(\n () => (\n // Styled to match the host's own dialogs (bold title, dim esc\n // hint). The host dialog panel already insets the content, so\n // the box adds no top padding of its own; a blank spacer row\n // separates the title row from the table.\n <box style={{ flexDirection: \"column\", paddingBottom: 1, paddingX: 2 }}>\n <box style={{ flexDirection: \"row\", justifyContent: \"space-between\" }}>\n <text style={{ attributes: TextAttributes.BOLD }}>{spec.title}</text>\n <text style={{ attributes: TextAttributes.DIM }}>esc</text>\n </box>\n <text> </text>\n {spec.lines.map((line) => (\n <text>{line}</text>\n ))}\n </box>\n ),\n () => {\n resolve()\n },\n )\n context.ui.dialog.set({ size: \"xlarge\" })\n }),\n toast: (message, variant) => {\n context.ui.toast.show({ message, variant })\n },\n }\n // Real OpenCode models for the tier editor search (ADP-033); structural\n // summaries keep the view models free of client types.\n const models = (): readonly ModelSummary[] => {\n const list = context.data.location.model.list(context.location)\n return (list ?? []).map((info) => ({\n providerID: info.providerID,\n modelID: info.modelID,\n name: info.name,\n variants: info.variants.map((variant) => variant.id),\n }))\n }\n context.keymap.layer(() => ({\n // \"global\" opts out of input-mode scoping: the default (\"base\") binds\n // the commands to one OpenCode input mode, which kept the palette from\n // matching them (the sibling @coool plugins register the same way).\n mode: \"global\",\n commands: [\n {\n id: \"coool.adaptive.menu\",\n title: \"Adaptive\",\n group: \"Adaptive\",\n palette: true,\n // Surface the command in the palette's suggested row: discovery was\n // the reason users never found the menu at all.\n suggested: true,\n slash: { name: \"adaptive\" },\n run: () => {\n // Inside a session the flows steer that session's mode override\n // (ADP-054); on home they patch the global mode.\n const current = context.ui.router.current()\n const sessionID = current.type === \"session\" ? current.sessionID : undefined\n void runAdaptive({\n api,\n view,\n models,\n ...(sessionID !== undefined ? { sessionID } : {}),\n }).catch((error: unknown) => {\n view.toast(errorText(error), \"error\")\n })\n },\n },\n ],\n }))\n\n // Footer chip state: a reactive mirror of the server's operating mode,\n // fetched once at boot and kept live via the settings-changed event, plus\n // the per-session overrides seen so far (ADP-054).\n const [chip, setChip] = createStore<{\n mode: OperatingMode | null\n sessions: Record<string, OperatingMode | null>\n }>({ mode: null, sessions: {} })\n\n const fetchMode = async (): Promise<boolean> => {\n try {\n const settings = await api.settings()\n const mode = parseMode(settings.mode)\n if (mode !== undefined) setChip(\"mode\", mode)\n return true\n } catch {\n return false\n }\n }\n\n /**\n * One session's override, cached so the chip never re-fetches per render.\n * A fetch failure caches `null` (follow global): the chip stays correct\n * for override-less sessions, and a later `session-mode-changed` event\n * refreshes sessions that do carry an override.\n */\n const fetchSessionMode = async (sessionID: string): Promise<void> => {\n try {\n setChip(\"sessions\", sessionID, await api.sessionMode(sessionID))\n } catch {\n setChip(\"sessions\", sessionID, null)\n }\n }\n\n const bootFetch = async (retriesLeft: number): Promise<void> => {\n if (await fetchMode()) return\n // The server plugin may not be registered yet at TUI boot; retry\n // quietly. Bounded (unlike the sibling plugins' unbounded retry) so the\n // timer loop provably terminates: after the cap the chip stays hidden\n // until the next settings-changed event refreshes it.\n if (retriesLeft > 0) {\n setTimeout(() => void bootFetch(retriesLeft - 1), BOOT_FETCH_RETRY_MS)\n }\n }\n\n const renderChip = (mode: OperatingMode | null) => {\n if (mode === null) return <text></text>\n const entry = chipFor(mode)\n // The accent-colored span cannot execute in-repo: the terminal renderer\n // cannot start headless and jsdom's HTML parser mangles the intrinsic\n // <text><span> template (null insert parent). The chipFor tests pin the\n // color mapping; the chip render test pins the plain observe shape.\n /* v8 ignore start */\n if (entry.fg === null) return <text>{entry.text}</text>\n return (\n <text>\n <span style={{ fg: entry.fg }}>{entry.text}</span>\n </text>\n )\n /* v8 ignore stop */\n }\n const chipNode = (sessionID: string | undefined) => {\n // On home (no session id) the chip shows the global mode; in a session\n // it shows the session's EFFECTIVE mode (override ?? global).\n if (sessionID === undefined) return renderChip(chip.mode)\n const override = chip.sessions[sessionID]\n if (override === undefined) {\n // First render for this session: fetch the override; the store write\n // re-renders this node with the fetched value. Until then the global\n // mode is shown.\n void fetchSessionMode(sessionID)\n return renderChip(chip.mode)\n }\n return renderChip(override ?? chip.mode)\n }\n // The home footer slot only exists on the home screen; sessions render\n // the prompt footer — its slot input carries the open session's id, so\n // the chip there shows that session's effective mode (ADP-054).\n const disposeSlots = [\n context.ui.slot({ append: \"home.footer.status\", render: () => chipNode(undefined) }),\n context.ui.slot({\n append: \"prompt.footer.status\",\n render: (input) => chipNode(input.sessionID),\n }),\n ]\n\n const disposeWatch = client.events.on(\"settings-changed\", () => {\n void fetchMode()\n // A global mode change re-renders override-less sessions reactively;\n // cached overrides are re-fetched so a stale mirror cannot stick.\n for (const sessionID of Object.keys(chip.sessions)) {\n void fetchSessionMode(sessionID)\n }\n })\n const disposeSessionWatch = client.events.on(\"session-mode-changed\", (event) => {\n // The event data crosses the wire as an untyped record: a malformed\n // payload is ignored (fail-open) instead of steering a bogus session.\n const sessionID = event.data.sessionID\n if (typeof sessionID === \"string\") void fetchSessionMode(sessionID)\n })\n\n void bootFetch(BOOT_FETCH_RETRIES)\n\n return () => {\n for (const dispose of disposeSlots) dispose()\n disposeWatch()\n disposeSessionWatch()\n }\n },\n})\n"]}
|
|
@@ -25,14 +25,15 @@ export interface AdaptiveView {
|
|
|
25
25
|
title: string;
|
|
26
26
|
placeholder?: string;
|
|
27
27
|
options: readonly SelectOption<Value>[];
|
|
28
|
-
current?: Value;
|
|
29
|
-
search?: (query: string, options: readonly SelectOption<Value>[]) => readonly SelectOption<Value>[];
|
|
30
28
|
}): Promise<Value | undefined>;
|
|
31
29
|
/** A select with footer actions (page flips): the action closes the
|
|
32
30
|
* dialog and is reported separately from an option pick. */
|
|
33
31
|
selectPaged<Value>(spec: {
|
|
34
32
|
title: string;
|
|
35
|
-
options: readonly
|
|
33
|
+
options: readonly {
|
|
34
|
+
title: string;
|
|
35
|
+
value: Value;
|
|
36
|
+
}[];
|
|
36
37
|
actions: readonly {
|
|
37
38
|
title: string;
|
|
38
39
|
value: string;
|
|
@@ -45,10 +46,6 @@ export interface AdaptiveView {
|
|
|
45
46
|
confirm(options: {
|
|
46
47
|
title: string;
|
|
47
48
|
message: string;
|
|
48
|
-
label?: {
|
|
49
|
-
confirm?: string;
|
|
50
|
-
cancel?: string;
|
|
51
|
-
};
|
|
52
49
|
}): Promise<boolean | undefined>;
|
|
53
50
|
alert(options: {
|
|
54
51
|
title: string;
|
|
@@ -59,7 +56,7 @@ export interface AdaptiveView {
|
|
|
59
56
|
title: string;
|
|
60
57
|
lines: string[];
|
|
61
58
|
}): Promise<void>;
|
|
62
|
-
toast(message: string, variant: "
|
|
59
|
+
toast(message: string, variant: "success" | "error"): void;
|
|
63
60
|
}
|
|
64
61
|
export interface AdaptiveApi {
|
|
65
62
|
settings: () => Promise<SettingsV1>;
|
|
@@ -291,7 +291,7 @@ async function runTierEditor(env, feedback) {
|
|
|
291
291
|
}
|
|
292
292
|
}
|
|
293
293
|
else {
|
|
294
|
-
const next = await runResourceMenu(env, pageSettings, tier, choice
|
|
294
|
+
const next = await runResourceMenu(env, pageSettings, tier, choice, feedback);
|
|
295
295
|
if (next !== undefined) {
|
|
296
296
|
saved = next;
|
|
297
297
|
pageSettings = next;
|
|
@@ -319,13 +319,11 @@ async function runAddModel(env, settings, tier, feedback) {
|
|
|
319
319
|
const pick = await env.view.select({
|
|
320
320
|
title: "Add model",
|
|
321
321
|
placeholder: "Search provider / model / name",
|
|
322
|
-
options: modelPickerOptions(models
|
|
323
|
-
search: (query) => modelPickerOptions(models, query),
|
|
322
|
+
options: modelPickerOptions(models),
|
|
324
323
|
});
|
|
325
324
|
if (pick === undefined)
|
|
326
325
|
return undefined;
|
|
327
|
-
const
|
|
328
|
-
const variants = info?.variants ?? [];
|
|
326
|
+
const variants = pick.variants;
|
|
329
327
|
let variant;
|
|
330
328
|
if (variants.length > 0) {
|
|
331
329
|
const choice = await env.view.select({
|
|
@@ -350,10 +348,7 @@ function resourceMenuOptions() {
|
|
|
350
348
|
{ title: "Remove", value: "remove" },
|
|
351
349
|
];
|
|
352
350
|
}
|
|
353
|
-
async function runResourceMenu(env, settings, tier, index, feedback) {
|
|
354
|
-
const ref = settings.tiers[tier][index];
|
|
355
|
-
if (ref === undefined)
|
|
356
|
-
return undefined;
|
|
351
|
+
async function runResourceMenu(env, settings, tier, { index, ref }, feedback) {
|
|
357
352
|
const pick = await env.view.select({
|
|
358
353
|
title: `Resource: ${resourceLabel(ref)}`,
|
|
359
354
|
options: resourceMenuOptions(),
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"controller.js","sourceRoot":"","sources":["../../../src/ui/flow/controller.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The `/adaptive` keyboard-flow controller (ADP-033): a pure-ish state machine\n * over two seams — the typed RPC API and the host dialogs/toasts. It holds no\n * local settings state: every mutation goes through `settings.set` and the\n * returned snapshot becomes the new local view (the server is the single\n * source of truth, ADP-036).\n *\n * Flow contract: `undefined` selections always mean \"cancel this step\" — the\n * menu loop continues or exits; API failures toast and keep the loop alive.\n */\nimport type { OperatingMode } from \"../../core/scheduler/types.js\"\nimport type { TierName } from \"../../core/tier-policy/types.js\"\nimport { TIERS } from \"../../core/tier-policy/types.js\"\nimport type { ModelRef } from \"../../core/work-unit/types.js\"\nimport type { SettingsPatch, SettingsV1 } from \"../../state/settings/index.js\"\nimport type { PatchResult } from \"../../state/settings/types.js\"\nimport type { SessionModeSetResult } from \"../../state/session-modes/index.js\"\nimport type { TierRequestSetResult } from \"../../rpc/handlers.js\"\nimport type { Receipt } from \"../../state/receipts/types.js\"\nimport type { StatusSnapshot } from \"../../rpc/status.js\"\nimport { STATUS_EMPTY_MESSAGE, statusTable } from \"./status.js\"\nimport {\n mainMenuOptions,\n modeOptions,\n modeTargetOptions,\n sessionModeOptions,\n sessionTierOptions,\n type MainMenuChoice,\n} from \"./menu.js\"\nimport {\n modelPickerOptions,\n moveResource,\n moveResourceToTop,\n removeResource,\n resourceLabel,\n tierEditorOptions,\n tierMenuOptions,\n variantOptions,\n type ModelSummary,\n} from \"./tiers.js\"\nimport { errorText, type SelectOption } from \"./options.js\"\nimport { receiptDetail, receiptLine, receiptPage, RECEIPTS_PAGE_SIZE } from \"./receipts.js\"\n\nexport interface AdaptiveView {\n select<Value>(options: {\n title: string\n placeholder?: string\n options: readonly SelectOption<Value>[]\n }): Promise<Value | undefined>\n /** A select with footer actions (page flips): the action closes the\n * dialog and is reported separately from an option pick. */\n selectPaged<Value>(spec: {\n title: string\n options: readonly { title: string; value: Value }[]\n actions: readonly { title: string; value: string; bind: string }[]\n }): Promise<{ picked: Value | undefined; action: string | undefined }>\n confirm(options: { title: string; message: string }): Promise<boolean | undefined>\n alert(options: { title: string; message: string }): Promise<void>\n /** A column-aligned text table (header row first); Escape closes it. */\n table(spec: { title: string; lines: string[] }): Promise<void>\n toast(message: string, variant: \"success\" | \"error\"): void\n}\n\nexport interface AdaptiveApi {\n settings: () => Promise<SettingsV1>\n setSettings: (patch: SettingsPatch) => Promise<PatchResult>\n status: () => Promise<StatusSnapshot>\n receipts: () => Promise<Receipt[]>\n /** The session's mode override (`null` = follows the global mode). */\n sessionMode: (sessionID: string) => Promise<OperatingMode | null>\n setSessionMode: (sessionID: string, mode: OperatingMode | null) => Promise<SessionModeSetResult>\n /** The session's pinned tier request (`null` = follows the routing ladder). */\n tierRequest: (sessionID: string) => Promise<TierName | null>\n setTierRequest: (sessionID: string, tier: string | null) => Promise<TierRequestSetResult>\n}\n\nexport type ModelLister = () => readonly ModelSummary[]\n\nexport interface AdaptiveEnv {\n api: AdaptiveApi\n view: AdaptiveView\n models: ModelLister\n /**\n * The session the command was opened from (ADP-054). Present only when the\n * palette command ran inside a session screen — the mode menu then steers\n * that session's override instead of the global mode.\n */\n sessionID?: string | undefined\n}\n\nexport const INVITE_TITLE = \"OpenCode Adaptive\"\nexport const INVITE_MESSAGE =\n \"Adaptive starts in Observe mode: routing decisions are recorded as receipts, \" +\n \"but no model is switched. Open /adaptive to configure tiers or switch to Active.\"\n\n/** Tracks whether this dialog session already showed the save toast. */\nexport interface SaveFeedback {\n once: boolean\n}\n\n/** Run one API call; on failure toast and return undefined. */\nasync function tryApi<T>(env: AdaptiveEnv, run: () => Promise<T>): Promise<T | undefined> {\n try {\n return await run()\n } catch (error) {\n env.view.toast(errorText(error), \"error\")\n return undefined\n }\n}\n\n/**\n * Apply one patch; alert on validation errors, toast on success. The host\n * never auto-dismisses toasts, so the success toast is shown only on the\n * first successful save of a dialog session — later saves stay silent\n * (the updated menu already reflects them).\n */\nasync function applySettings(\n env: AdaptiveEnv,\n patch: SettingsPatch,\n feedback: SaveFeedback,\n): Promise<SettingsV1 | undefined> {\n const result = await tryApi(env, () => env.api.setSettings(patch))\n if (result === undefined) return undefined\n if (!result.ok) {\n await env.view.alert({ title: \"Invalid settings\", message: result.errors.join(\"; \") })\n return undefined\n }\n if (!feedback.once) {\n env.view.toast(\"Settings saved\", \"success\")\n feedback.once = true\n }\n return result.settings\n}\n\n/**\n * Persist one session-mode override; alert on errors, toast on success. Same\n * once-toast semantics as `applySettings` — the success toast is shown only\n * on the first successful save of a dialog session.\n */\nasync function applySessionMode(\n env: AdaptiveEnv,\n sessionID: string,\n mode: OperatingMode | null,\n feedback: SaveFeedback,\n): Promise<SessionModeSetResult | undefined> {\n const result = await tryApi(env, () => env.api.setSessionMode(sessionID, mode))\n if (result === undefined) return undefined\n if (!result.ok) {\n await env.view.alert({ title: \"Invalid settings\", message: result.errors.join(\"; \") })\n return undefined\n }\n if (!feedback.once) {\n env.view.toast(\"Settings saved\", \"success\")\n feedback.once = true\n }\n return result\n}\n\n/**\n * Persist one session tier request; alert on errors, toast on success. Same\n * once-toast semantics as `applySettings` — the success toast is shown only\n * on the first successful save of a dialog session.\n */\nasync function applyTierRequest(\n env: AdaptiveEnv,\n sessionID: string,\n tier: string | null,\n feedback: SaveFeedback,\n): Promise<TierRequestSetResult | undefined> {\n const result = await tryApi(env, () => env.api.setTierRequest(sessionID, tier))\n if (result === undefined) return undefined\n if (!result.ok) {\n await env.view.alert({ title: \"Invalid tier request\", message: result.errors.join(\"; \") })\n return undefined\n }\n if (!feedback.once) {\n env.view.toast(\"Settings saved\", \"success\")\n feedback.once = true\n }\n return result\n}\n\nexport async function runAdaptive(env: AdaptiveEnv): Promise<void> {\n const loaded = await tryApi(env, () => env.api.settings())\n if (loaded === undefined) return\n let settings = loaded\n const feedback: SaveFeedback = { once: false }\n const sessionID = env.sessionID\n // The session's current override: fetched once when the flow starts inside\n // a session; a failure degrades to \"follow global\" (the menu still works).\n let override: OperatingMode | null =\n sessionID === undefined\n ? null\n : ((await tryApi(env, () => env.api.sessionMode(sessionID))) ?? null)\n if (!settings.inviteDismissed) {\n await env.view.confirm({ title: INVITE_TITLE, message: INVITE_MESSAGE })\n const pick = await env.view.select({\n title: INVITE_TITLE,\n options: [\n { title: \"Open the menu now\", value: true },\n { title: \"Later\", value: false },\n ],\n })\n const dismissed = await applySettings(env, { inviteDismissed: true }, feedback)\n if (dismissed !== undefined) settings = dismissed\n if (pick !== true) return\n }\n for (;;) {\n // Fresh status and settings on every pass: the menu rows pair the two,\n // and the server is the single source of truth (ADP-036). A failed read\n // degrades silently to the last known values — the menu descriptions\n // must not spam a toast on every loop iteration (the flows that need\n // status toast through tryApi themselves).\n const [freshSettings, status] = await Promise.all([\n env.api.settings().catch(() => undefined),\n env.api.status().catch(() => undefined),\n ])\n if (freshSettings !== undefined) settings = freshSettings\n // The session's pinned tier request: fetched per loop so the entry\n // description tracks the latest request; a failure degrades to the\n // generic description (the menu still works).\n const tierPinned =\n sessionID === undefined\n ? undefined\n : await env.api.tierRequest(sessionID).catch(() => undefined)\n const choice = await env.view.select({\n title: `Adaptive (${override ?? settings.mode})`,\n options: mainMenuOptions(settings, status, tierPinned, override ?? settings.mode),\n })\n if (choice === undefined) return\n if (choice === \"status\") {\n await runStatusView(env)\n continue\n }\n if (choice === \"models\") {\n settings = (await runTierEditor(env, feedback)) ?? settings\n continue\n }\n if (choice === \"mode\") {\n const next = await runModeMenu(env, settings, feedback, sessionID, override)\n if (next !== undefined) {\n settings = next.settings\n override = next.override\n }\n continue\n }\n if (choice === \"tier\") {\n if (sessionID === undefined) {\n // The tier request only exists inside a session; on home the pick is\n // explained (not silently dropped) and the menu re-opens.\n await env.view.alert({\n title: \"This session\",\n message:\n \"This session's tier request can only be changed inside a session. Open a session and run /adaptive from there.\",\n })\n continue\n }\n await runTierRequestMenu(env, sessionID, feedback)\n continue\n }\n if (choice === \"pin\") {\n settings = (await runPinMenu(env, settings, feedback)) ?? settings\n continue\n }\n await runReceiptsView(env)\n }\n}\n\n/** What one mode-menu pass changed (settings and/or the session override). */\ninterface ModeMenuOutcome {\n settings: SettingsV1\n override: OperatingMode | null\n}\n\nasync function runModeMenu(\n env: AdaptiveEnv,\n settings: SettingsV1,\n feedback: SaveFeedback,\n sessionID: string | undefined,\n override: OperatingMode | null,\n): Promise<ModeMenuOutcome | undefined> {\n // Every screen gets the same first question — which mode to steer — so a\n // user who opens the menu on home learns the two-level structure before\n // they ever run the command inside a session.\n for (;;) {\n const target = await env.view.select({\n title: \"Operating mode\",\n options: modeTargetOptions(settings.mode, override ?? settings.mode),\n })\n if (target === undefined) return undefined\n if (target === \"global\") {\n const pick = await env.view.select({\n title: \"Global mode\",\n options: modeOptions(settings.mode),\n })\n if (pick === undefined || pick === settings.mode) return undefined\n const applied = await applySettings(env, { mode: pick }, feedback)\n // The session override is deliberately untouched: changing the global\n // default must not rewrite what a session was pinned to.\n return applied === undefined ? undefined : { settings: applied, override }\n }\n if (sessionID === undefined) {\n // The session override only exists inside a session; on home the pick\n // is explained (not silently dropped) and the target question repeats.\n await env.view.alert({\n title: \"This session\",\n message:\n \"This session's mode can only be changed inside a session. Open a session and run /adaptive from there.\",\n })\n continue\n }\n const pick = await env.view.select({\n title: \"This session's mode\",\n options: sessionModeOptions({\n global: settings.mode,\n override,\n effective: override ?? settings.mode,\n }),\n })\n if (pick === undefined) return undefined\n if (pick.kind === \"global\") {\n if (override === null) return undefined\n const saved = await applySessionMode(env, sessionID, null, feedback)\n return saved === undefined ? undefined : { settings, override: null }\n }\n if (pick.mode === (override ?? settings.mode)) return undefined\n const saved = await applySessionMode(env, sessionID, pick.mode, feedback)\n return saved === undefined ? undefined : { settings, override: pick.mode }\n }\n}\n\n/** The per-session tier-request menu (ADP-055): pin a tier or clear it. */\nasync function runTierRequestMenu(\n env: AdaptiveEnv,\n sessionID: string,\n feedback: SaveFeedback,\n): Promise<void> {\n const pinned = await tryApi(env, () => env.api.tierRequest(sessionID))\n if (pinned === undefined) return\n const pick = await env.view.select({\n title: \"This session's tier\",\n options: sessionTierOptions({ pinned }),\n })\n if (pick === undefined) return\n const next = pick.kind === \"ladder\" ? null : pick.tier\n if (next === pinned) return\n await applyTierRequest(env, sessionID, next, feedback)\n}\n\n/**\n * The status view: every configured model with its state as a column-aligned\n * table (header row, tier order, priority order within tiers). No\n * per-resource selection step — the table IS the view; Escape returns to the\n * menu.\n */\nasync function runStatusView(env: AdaptiveEnv): Promise<void> {\n const snapshot = await tryApi(env, () => env.api.status())\n if (snapshot === undefined) return\n const configured = TIERS.some((tier) => snapshot.tiers[tier].length > 0)\n if (!configured) {\n await env.view.alert({ title: \"Status\", message: STATUS_EMPTY_MESSAGE })\n return\n }\n await env.view.table({ title: \"Status\", lines: statusTable(snapshot) })\n}\n\ntype TierEditorOutcome = SettingsV1 | undefined\n\nasync function runTierEditor(env: AdaptiveEnv, feedback: SaveFeedback): Promise<TierEditorOutcome> {\n // The last confirmed server snapshot from this editor session, returned\n // when the editor closes so the menu view stays in sync with the server.\n let saved: SettingsV1 | undefined\n for (;;) {\n // The server is the single source of truth (ADP-036): settings and\n // status are re-fetched on every tier-list pass — entering the editor\n // and after each Escape out of a tier page — so an edit can never\n // clobber a change made outside this dialog. A failed read toasts and\n // closes the editor: the tier list cannot render meaningfully without\n // them.\n const settings = await tryApi(env, () => env.api.settings())\n if (settings === undefined) return saved\n const status = await tryApi(env, () => env.api.status())\n if (status === undefined) return saved\n const tier = await env.view.select({ title: \"Tier\", options: tierMenuOptions(status) })\n if (tier === undefined) return saved\n let pageStatus = status\n let pageSettings = settings\n // The tier page owns the loop until Escape: an add, move, or remove\n // re-opens this page instead of bouncing back to the tier list, so\n // consecutive edits on one tier stay in context. The page's settings\n // and status carry across iterations: each action's save response\n // updates them for the next render.\n for (;;) {\n const choice = await env.view.select({\n title: `Tier: ${tier}`,\n options: tierEditorOptions(pageSettings, tier, pageStatus),\n })\n if (choice === undefined) break\n if (choice.kind === \"add\") {\n const next = await runAddModel(env, pageSettings, tier, feedback)\n if (next !== undefined) {\n saved = next\n pageSettings = next\n }\n } else {\n const next = await runResourceMenu(env, pageSettings, tier, choice, feedback)\n if (next !== undefined) {\n saved = next\n pageSettings = next\n }\n }\n // The rows re-pair with the server after every action; a failed read\n // keeps the last known values so the page still renders (the save\n // already reported itself — a failed cosmetic refresh must neither\n // spam a toast nor bounce the page).\n const [freshSettings, freshStatus] = await Promise.all([\n env.api.settings().catch(() => undefined),\n env.api.status().catch(() => undefined),\n ])\n if (freshSettings !== undefined) {\n saved = freshSettings\n pageSettings = freshSettings\n }\n if (freshStatus !== undefined) pageStatus = freshStatus\n }\n }\n}\n\nasync function runAddModel(\n env: AdaptiveEnv,\n settings: SettingsV1,\n tier: TierName,\n feedback: SaveFeedback,\n): Promise<SettingsV1 | undefined> {\n const models = env.models()\n const pick = await env.view.select({\n title: \"Add model\",\n placeholder: \"Search provider / model / name\",\n options: modelPickerOptions(models),\n })\n if (pick === undefined) return undefined\n const variants = pick.variants\n let variant: string | undefined\n if (variants.length > 0) {\n const choice = await env.view.select({\n title: `Variant: ${pick.providerID}/${pick.modelID}`,\n options: variantOptions(variants),\n })\n if (choice === undefined) return undefined\n variant = choice.variant\n }\n const ref: ModelRef =\n variant === undefined\n ? { providerID: pick.providerID, id: pick.modelID }\n : { providerID: pick.providerID, id: pick.modelID, variant }\n const list = [...settings.tiers[tier], ref]\n return applySettings(env, { tiers: { ...settings.tiers, [tier]: list } }, feedback)\n}\n\ntype ResourceMenuChoice = \"top\" | \"up\" | \"down\" | \"remove\"\n\nfunction resourceMenuOptions(): SelectOption<ResourceMenuChoice>[] {\n return [\n { title: \"Move to top\", value: \"top\", description: \"Make this the primary resource\" },\n { title: \"Move up\", value: \"up\", description: \"Higher priority within the tier\" },\n { title: \"Move down\", value: \"down\", description: \"Lower priority within the tier\" },\n { title: \"Remove\", value: \"remove\" },\n ]\n}\n\nasync function runResourceMenu(\n env: AdaptiveEnv,\n settings: SettingsV1,\n tier: TierName,\n { index, ref }: { index: number; ref: ModelRef },\n feedback: SaveFeedback,\n): Promise<SettingsV1 | undefined> {\n const pick = await env.view.select({\n title: `Resource: ${resourceLabel(ref)}`,\n options: resourceMenuOptions(),\n })\n if (pick === undefined) return undefined\n if (pick === \"remove\") {\n const confirmed = await env.view.confirm({\n title: \"Remove resource\",\n message: `Remove ${resourceLabel(ref)} from ${tier}?`,\n })\n if (confirmed !== true) return undefined\n return applySettings(\n env,\n { tiers: { ...settings.tiers, [tier]: removeResource(settings.tiers[tier], index) } },\n feedback,\n )\n }\n const list =\n pick === \"top\"\n ? moveResourceToTop(settings.tiers[tier], index)\n : moveResource(settings.tiers[tier], index, pick === \"up\" ? -1 : 1)\n return applySettings(env, { tiers: { ...settings.tiers, [tier]: list } }, feedback)\n}\n\ntype PinChoice = { kind: \"clear\" } | { kind: \"pin\"; ref: ModelRef }\n\nfunction pinOptions(settings: SettingsV1): SelectOption<PinChoice>[] {\n const options: SelectOption<PinChoice>[] = [\n { title: \"No pin\", value: { kind: \"clear\" }, description: \"clear the pin\" },\n ]\n for (const tier of TIERS) {\n for (const ref of settings.tiers[tier]) {\n options.push({\n title: `${resourceLabel(ref)} (${tier})`,\n description: \"route every turn to this resource, even in active mode\",\n value: { kind: \"pin\", ref },\n })\n }\n }\n return options\n}\n\nasync function runPinMenu(\n env: AdaptiveEnv,\n settings: SettingsV1,\n feedback: SaveFeedback,\n): Promise<SettingsV1 | undefined> {\n const configured = TIERS.some((tier) => settings.tiers[tier].length > 0)\n if (!configured) {\n await env.view.alert({ title: \"Pin\", message: \"(no configured resources to pin)\" })\n return undefined\n }\n const pick = await env.view.select({ title: \"Pin\", options: pinOptions(settings) })\n if (pick === undefined) return undefined\n return applySettings(env, { pin: pick.kind === \"clear\" ? null : pick.ref }, feedback)\n}\n\nasync function runReceiptsView(env: AdaptiveEnv): Promise<void> {\n const all = await tryApi(env, () => env.api.receipts())\n if (all === undefined) return\n if (all.length === 0) {\n await env.view.alert({\n title: \"Receipts\",\n message: \"No decisions recorded yet.\",\n })\n return\n }\n // Paginated browse: every page is a bounded view, and the page flips are\n // footer actions (not list items) — they close the dialog and the loop\n // reopens it at the new offset. Viewing a detail returns to the same\n // page; Escape (no pick, no action) leaves the browser.\n for (let offset = 0; ;) {\n const page = receiptPage(all.slice(offset))\n const hasNext = offset + RECEIPTS_PAGE_SIZE < all.length\n const result = await env.view.selectPaged({\n title: `Receipts (${String(all.length)}, newest first)`,\n options: page.map((receipt) => ({ title: receiptLine(receipt), value: receipt })),\n actions: [\n ...(offset > 0 ? [{ title: \"Previous page\", value: \"prev\", bind: \"ctrl+b\" }] : []),\n ...(hasNext ? [{ title: \"Next page\", value: \"next\", bind: \"ctrl+f\" }] : []),\n ],\n })\n if (result.action === \"next\") {\n offset += RECEIPTS_PAGE_SIZE\n continue\n }\n if (result.action === \"prev\") {\n offset = Math.max(0, offset - RECEIPTS_PAGE_SIZE)\n continue\n }\n const pick = result.picked\n if (pick === undefined) return\n await env.view.alert({ title: `Receipt #${String(pick.id)}`, message: receiptDetail(pick) })\n }\n}\n\n/** Exported for tests: the receipts page size used by the receipts browser. */\nexport { RECEIPTS_PAGE_SIZE }\n\nexport type {\n MainMenuChoice,\n OperatingMode,\n StatusSnapshot,\n SettingsV1,\n SettingsPatch,\n PatchResult,\n Receipt,\n}\n"]}
|
|
@@ -11,6 +11,8 @@ export declare function statusSummary(status: StatusSnapshot | undefined): strin
|
|
|
11
11
|
export declare function modelsSummary(settings: SettingsV1): string;
|
|
12
12
|
export declare function mainMenuOptions(settings: SettingsV1, status: StatusSnapshot | undefined, tierRequest?: TierName | null, effectiveMode?: OperatingMode): SelectOption<MainMenuChoice>[];
|
|
13
13
|
export declare function modeOptions(current: OperatingMode): SelectOption<OperatingMode>[];
|
|
14
|
+
/** What one mode does — shared by the global and the per-session menus. */
|
|
15
|
+
export declare function modeDescription(mode: OperatingMode): string;
|
|
14
16
|
/** One per-session mode-menu pick: follow the global mode, or set an override. */
|
|
15
17
|
export type SessionModeChoice = {
|
|
16
18
|
kind: "global";
|
|
@@ -86,7 +86,7 @@ export function modeOptions(current) {
|
|
|
86
86
|
}));
|
|
87
87
|
}
|
|
88
88
|
/** What one mode does — shared by the global and the per-session menus. */
|
|
89
|
-
function modeDescription(mode) {
|
|
89
|
+
export function modeDescription(mode) {
|
|
90
90
|
switch (mode) {
|
|
91
91
|
case "off":
|
|
92
92
|
return "Adaptive does nothing";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"menu.js","sourceRoot":"","sources":["../../../src/ui/flow/menu.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Main-menu and mode-menu view models for `/adaptive` (ADP-033): pure data\n * that the controller feeds into `ui.dialog.select`. Descriptions are dynamic\n * where state makes them more informative (health counters, tier contents).\n */\nimport { OPERATING_MODES } from \"../../core/scheduler/types.js\"\nimport type { OperatingMode } from \"../../core/scheduler/types.js\"\nimport type { TierName } from \"../../core/tier-policy/types.js\"\nimport { TIERS } from \"../../core/tier-policy/types.js\"\nimport type { StatusSnapshot } from \"../../rpc/status.js\"\nimport type { SettingsV1 } from \"../../state/settings/index.js\"\nimport type { SelectOption } from \"./options.js\"\nimport { resourceLabel } from \"./tiers.js\"\n\nexport const MAIN_MENU_ITEMS = [\"status\", \"models\", \"mode\", \"tier\", \"pin\", \"receipts\"] as const\nexport type MainMenuChoice = (typeof MAIN_MENU_ITEMS)[number]\n\n/** \"3 resources · 1 windowed\" — the status line under the menu entry. */\nexport function statusSummary(status: StatusSnapshot | undefined): string {\n if (status === undefined) return \"how every configured model is doing\"\n const total = TIERS.reduce((sum, tier) => sum + status.tiers[tier].length, 0)\n if (total === 0) return \"no models configured yet\"\n const windowed = TIERS.reduce(\n (sum, tier) => sum + status.tiers[tier].filter((entry) => entry.windowed).length,\n 0,\n )\n const windowedNote = windowed === 0 ? \"\" : ` · ${String(windowed)} windowed`\n return `${String(total)} resource${total === 1 ? \"\" : \"s\"}${windowedNote}`\n}\n\n/** \"quick: 1 · standard: 0 · advanced: 2\" — what is configured where. */\nexport function modelsSummary(settings: SettingsV1): string {\n const parts = TIERS.map((tier: TierName) => `${tier}: ${String(settings.tiers[tier].length)}`)\n return parts.join(\" · \")\n}\n\nfunction tierRequestDescription(tierRequest: TierName | null | undefined): string {\n if (tierRequest === undefined) return \"pin a tier for the current session\"\n if (tierRequest === null) return \"not pinned — the routing ladder decides\"\n return `pinned to ${tierRequest} — steers this session`\n}\n\nfunction modeMarker(\n mode: OperatingMode,\n input: { override: OperatingMode | null; effective: OperatingMode },\n): \"override\" | \"current\" | undefined {\n if (mode === input.override) return \"override\"\n if (mode === input.effective) return \"current\"\n return undefined\n}\n\nexport function mainMenuOptions(\n settings: SettingsV1,\n status: StatusSnapshot | undefined,\n tierRequest?: TierName | null,\n effectiveMode: OperatingMode = settings.mode,\n): SelectOption<MainMenuChoice>[] {\n const optionFor = (item: MainMenuChoice): SelectOption<MainMenuChoice> => {\n switch (item) {\n case \"status\":\n return {\n title: \"Status\",\n value: item,\n description: statusSummary(status),\n }\n case \"models\":\n return {\n title: \"Models\",\n value: item,\n description: modelsSummary(settings),\n }\n case \"mode\":\n return {\n title: \"Mode\",\n value: item,\n description: `currently ${effectiveMode}`,\n }\n case \"tier\":\n return {\n title: \"Tier\",\n value: item,\n description: tierRequestDescription(tierRequest),\n }\n case \"pin\":\n return {\n title: \"Pin\",\n value: item,\n description:\n settings.pin === null\n ? \"force one resource\"\n : `pinned to ${resourceLabel(settings.pin)}`,\n }\n case \"receipts\":\n return { title: \"Receipts\", value: item, description: \"why each turn ran the model it ran\" }\n }\n }\n return MAIN_MENU_ITEMS.map(optionFor)\n}\n\nexport function modeOptions(current: OperatingMode): SelectOption<OperatingMode>[] {\n return OPERATING_MODES.map((mode) => ({\n title: mode === current ? `${mode} (current)` : mode,\n value: mode,\n description: modeDescription(mode),\n }))\n}\n\n/** What one mode does — shared by the global and the per-session menus. */\nexport function modeDescription(mode: OperatingMode): string {\n switch (mode) {\n case \"off\":\n return \"Adaptive does nothing\"\n case \"observe\":\n return \"Record decisions without applying them\"\n case \"active\":\n return \"Apply routing and recovery decisions\"\n }\n}\n\n/** One per-session mode-menu pick: follow the global mode, or set an override. */\nexport type SessionModeChoice = { kind: \"global\" } | { kind: \"mode\"; mode: OperatingMode }\n\n/** Which mode the user steers inside a session: the global default or the session override. */\nexport type ModeTarget = \"global\" | \"session\"\n\n/**\n * The first select of the in-session mode flow (ADP-054): ask which mode to\n * steer before showing any mode list — the global default or this session's\n * override. Each row names the current value so the user can tell at a\n * glance which side needs changing.\n */\nexport function modeTargetOptions(\n global: OperatingMode,\n effective: OperatingMode,\n): SelectOption<ModeTarget>[] {\n return [\n {\n title: \"Global mode…\",\n description: `applies to every session without an override (now ${global})`,\n value: \"global\",\n },\n {\n title: \"This session…\",\n description: `override for this session only (now ${effective})`,\n value: \"session\",\n },\n ]\n}\n\n/**\n * The per-session mode menu (ADP-054): first \"Follow global\" (clearing the\n * override), then every mode. An existing override is marked \"(override)\" —\n * distinct from the effective mode's \"(current)\" — so the user can tell a\n * set override apart from a session merely inheriting the global mode.\n */\nexport function sessionModeOptions(input: {\n global: OperatingMode\n override: OperatingMode | null\n effective: OperatingMode\n}): SelectOption<SessionModeChoice>[] {\n const options: SelectOption<SessionModeChoice>[] = [\n {\n title: `Follow global (${input.global})`,\n description: \"clear this session's override\",\n value: { kind: \"global\" },\n },\n ]\n for (const mode of OPERATING_MODES) {\n const marker = modeMarker(mode, input)\n options.push({\n title: marker === undefined ? mode : `${mode} (${marker})`,\n value: { kind: \"mode\", mode },\n description: modeDescription(mode),\n })\n }\n return options\n}\n\n/** What one tier is for — shared by the tier editor and the per-session menu. */\nfunction tierDescription(tier: TierName): string {\n switch (tier) {\n case \"quick\":\n return \"routine edits, mechanical steps\"\n case \"standard\":\n return \"the default\"\n case \"advanced\":\n return \"multi-step design, broad refactors\"\n }\n}\n\n/** One per-session tier-menu pick: follow the routing ladder, or pin a tier. */\nexport type SessionTierChoice = { kind: \"ladder\" } | { kind: \"tier\"; tier: TierName }\n\n/**\n * The per-session tier menu (ADP-055): first \"Follow routing ladder\" (clearing\n * the request), then every tier. A pinned request is marked \"(pinned)\" so the\n * user can tell the sticky request apart from the ordinary ladder.\n */\nexport function sessionTierOptions(input: {\n pinned: TierName | null\n}): SelectOption<SessionTierChoice>[] {\n const options: SelectOption<SessionTierChoice>[] = [\n {\n title: \"Follow routing ladder\",\n description: \"clear this session's tier request\",\n value: { kind: \"ladder\" },\n },\n ]\n for (const tier of TIERS) {\n options.push({\n title: tier === input.pinned ? `${tier} (pinned)` : tier,\n value: { kind: \"tier\", tier },\n description: tierDescription(tier),\n })\n }\n return options\n}\n"]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared option shapes for the dialog-driven /adaptive flow (TUI and GUI). These mirror the subset of
|
|
3
|
+
* the host `ui.dialog.select` contract the Adaptive flows use; `tui.tsx` and `gui.tsx` adapt
|
|
4
|
+
* them to the real plugin types (structural superset).
|
|
5
|
+
*/
|
|
6
|
+
export interface SelectOption<Value> {
|
|
7
|
+
readonly title: string;
|
|
8
|
+
readonly value: Value;
|
|
9
|
+
readonly description?: string;
|
|
10
|
+
}
|
|
11
|
+
/** Friendly one-line rendering of an API failure for toasts. */
|
|
12
|
+
export declare function errorText(error: unknown): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"options.js","sourceRoot":"","sources":["../../../src/ui/flow/options.ts"],"names":[],"mappings":"AAWA,gEAAgE;AAChE,MAAM,UAAU,SAAS,CAAC,KAAc;IACtC,IAAI,KAAK,YAAY,KAAK;QAAE,OAAO,KAAK,CAAC,OAAO,CAAA;IAChD,0EAA0E;IAC1E,6EAA6E;IAC7E,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAChD,MAAM,OAAO,GAAI,KAA+B,CAAC,OAAO,CAAA;QACxD,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,OAAO,CAAA;QACrE,yEAAyE;QACzE,oEAAoE;QACpE,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,KAAc,EAAE,EAAE,CACjD,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAClD,CAAA;IACH,CAAC;IACD,OAAO,MAAM,CAAC,KAAK,CAAC,CAAA;AACtB,CAAC","sourcesContent":["/**\n * Shared option shapes for the dialog-driven /adaptive flow (TUI and GUI). These mirror the subset of\n * the host `ui.dialog.select` contract the Adaptive flows use; `tui.tsx` and `gui.tsx` adapt\n * them to the real plugin types (structural superset).\n */\nexport interface SelectOption<Value> {\n readonly title: string\n readonly value: Value\n readonly description?: string\n}\n\n/** Friendly one-line rendering of an API failure for toasts. */\nexport function errorText(error: unknown): string {\n if (error instanceof Error) return error.message\n // RPC failures surface as plain objects ({type, message}); String() would\n // render \"[object Object]\" and throw away the only diagnostic the user gets.\n if (typeof error === \"object\" && error !== null) {\n const message = (error as { message?: unknown }).message\n if (typeof message === \"string\" && message.length > 0) return message\n // JSON.stringify throws on BigInt values, which host payloads can carry;\n // stringify them as strings instead of losing the whole diagnostic.\n return JSON.stringify(error, (_, value: unknown) =>\n typeof value === \"bigint\" ? String(value) : value,\n )\n }\n return String(error)\n}\n"]}
|
|
@@ -13,7 +13,7 @@ export declare function receiptDetail(receipt: Receipt, now?: number): string;
|
|
|
13
13
|
export declare function relativeTime(at: number, now: number): string;
|
|
14
14
|
/**
|
|
15
15
|
* Plain-language explanation for a receipt, keyed on the finite reason
|
|
16
|
-
* vocabulary
|
|
16
|
+
* vocabulary (the maps are total over it).
|
|
17
17
|
*/
|
|
18
18
|
export declare function whatHappened(receipt: Receipt): string;
|
|
19
19
|
/** Receipts shown per dialog page (bounded view). */
|
|
@@ -61,19 +61,17 @@ export function relativeTime(at, now) {
|
|
|
61
61
|
}
|
|
62
62
|
/**
|
|
63
63
|
* Plain-language explanation for a receipt, keyed on the finite reason
|
|
64
|
-
* vocabulary
|
|
64
|
+
* vocabulary (the maps are total over it).
|
|
65
65
|
*/
|
|
66
66
|
export function whatHappened(receipt) {
|
|
67
67
|
const decision = receipt.decision;
|
|
68
68
|
switch (decision.kind) {
|
|
69
69
|
case "apply":
|
|
70
|
-
return
|
|
71
|
-
`Adaptive routed this turn to ${resourceLabel(decision.model)}.`);
|
|
70
|
+
return applyExplanation[decision.reason];
|
|
72
71
|
case "defer":
|
|
73
|
-
return deferExplanation[decision.reason]
|
|
72
|
+
return deferExplanation[decision.reason];
|
|
74
73
|
case "park":
|
|
75
|
-
return
|
|
76
|
-
"Adaptive could not route this turn and left it to OpenCode.");
|
|
74
|
+
return parkExplanation[decision.reason];
|
|
77
75
|
case "escalate":
|
|
78
76
|
return `This turn needed stronger capability, so Adaptive moved it to ${resourceLabel(decision.target)}.`;
|
|
79
77
|
case "observe-only":
|
|
@@ -89,7 +87,6 @@ const applyExplanation = {
|
|
|
89
87
|
};
|
|
90
88
|
const deferExplanation = {
|
|
91
89
|
"routing-defer": "No routing rule applied to this turn, so Adaptive left the choice to OpenCode.",
|
|
92
|
-
"tier-not-configured": "The tier that would handle this turn has no models configured, so Adaptive left the choice to OpenCode.",
|
|
93
90
|
"adaptive-off": "Adaptive is switched off, so every turn runs on OpenCode's own choice.",
|
|
94
91
|
"pin-unverifiable": "The pinned resource could not be verified as available, so Adaptive left the choice to OpenCode.",
|
|
95
92
|
"pin-incompatible": "The pinned resource does not fit this turn's requirements, so Adaptive left the choice to OpenCode.",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"receipts.js","sourceRoot":"","sources":["../../../src/ui/flow/receipts.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Receipt view models (ADP-030/033): one-line summaries and bounded detail\n * text for the `/adaptive` receipts browser. No secrets — receipt content is\n * already bounded at write time; this layer only formats.\n */\nimport type {\n ApplyReason,\n Decision,\n ParkReason,\n SchedulerDeferReason,\n} from \"../../core/scheduler/types.js\"\nimport { resourceLabel } from \"./tiers.js\"\nimport type { Receipt, ReceiptOutcome } from \"../../state/receipts/types.js\"\n\nexport function summarizeDecision(decision: Decision): string {\n switch (decision.kind) {\n case \"apply\": {\n const tier = decision.tier === undefined ? \"\" : ` [${decision.tier}]`\n return `apply ${resourceLabel(decision.model)}${tier} (${decision.reason})`\n }\n case \"defer\": {\n const detail = decision.detail === undefined ? \"\" : ` — ${decision.detail}`\n return `defer: ${decision.reason}${detail}`\n }\n case \"park\": {\n const detail = decision.detail === undefined ? \"\" : ` — ${decision.detail}`\n return `park: ${decision.reason}${detail}`\n }\n case \"escalate\": {\n return `escalate ${decision.step} → ${resourceLabel(decision.target)} [${decision.tier}]`\n }\n case \"observe-only\": {\n return `observe: ${summarizeDecision(decision.would)}`\n }\n }\n}\n\nexport function outcomeLine(outcome: ReceiptOutcome | undefined): string {\n if (outcome === undefined) return \"pending\"\n if (outcome.status === \"succeeded\") return \"succeeded\"\n if (outcome.status === \"failed\") return `failed (${outcome.category})`\n return `interrupted (${outcome.reason})`\n}\n\nexport function receiptLine(receipt: Receipt): string {\n return `#${String(receipt.id)} ${summarizeDecision(receipt.decision)} — ${outcomeLine(receipt.outcome)}`\n}\n\nexport function receiptDetail(receipt: Receipt, now: number = Date.now()): string {\n return [\n `id: ${String(receipt.id)}`,\n `when: ${relativeTime(receipt.at, now)} (${new Date(receipt.at).toISOString()})`,\n `session: ${receipt.sessionID}`,\n `unit: ${receipt.unitID ?? \"-\"}`,\n `tier: ${receipt.tier ?? \"-\"}`,\n `decision: ${summarizeDecision(receipt.decision)}`,\n `outcome: ${outcomeLine(receipt.outcome)}`,\n \"\",\n `What happened: ${whatHappened(receipt)}`,\n ].join(\"\\n\")\n}\n\n/** \"just now\" / \"2m ago\" / \"3h ago\" / \"5d ago\" for a receipt timestamp. */\nexport function relativeTime(at: number, now: number): string {\n const seconds = Math.max(0, Math.floor((now - at) / 1000))\n if (seconds < 60) return \"just now\"\n const minutes = Math.floor(seconds / 60)\n if (minutes < 60) return `${String(minutes)}m ago`\n const hours = Math.floor(minutes / 60)\n if (hours < 24) return `${String(hours)}h ago`\n return `${String(Math.floor(hours / 24))}d ago`\n}\n\n/**\n * Plain-language explanation for a receipt, keyed on the finite reason\n * vocabulary (the maps are total over it).\n */\nexport function whatHappened(receipt: Receipt): string {\n const decision = receipt.decision\n switch (decision.kind) {\n case \"apply\":\n return applyExplanation[decision.reason]\n case \"defer\":\n return deferExplanation[decision.reason]\n case \"park\":\n return parkExplanation[decision.reason]\n case \"escalate\":\n return `This turn needed stronger capability, so Adaptive moved it to ${resourceLabel(decision.target)}.`\n case \"observe-only\":\n return \"Adaptive is observing: this is what it would have done, without applying it.\"\n }\n}\n\nconst applyExplanation: Record<ApplyReason, string> = {\n pin: \"Adaptive routed to the resource you pinned.\",\n preferred:\n \"Adaptive chose this resource: it is the preferred resource for the tier and was available.\",\n fallback:\n \"The preferred resource was unavailable, so Adaptive moved the turn to the next resource in the tier.\",\n probe:\n \"Adaptive sent this turn as a recovery probe after an outage, to check whether the resource works again.\",\n draining: \"Adaptive routed to this resource while its provider was draining capacity.\",\n}\n\nconst deferExplanation: Record<SchedulerDeferReason, string> = {\n \"routing-defer\": \"No routing rule applied to this turn, so Adaptive left the choice to OpenCode.\",\n \"adaptive-off\": \"Adaptive is switched off, so every turn runs on OpenCode's own choice.\",\n \"pin-unverifiable\":\n \"The pinned resource could not be verified as available, so Adaptive left the choice to OpenCode.\",\n \"pin-incompatible\":\n \"The pinned resource does not fit this turn's requirements, so Adaptive left the choice to OpenCode.\",\n \"escalation-not-justified\":\n \"No structured capability evidence justified escalation, so Adaptive kept the current capability.\",\n \"escalation-budget-exhausted\":\n \"This unit has used its escalation budget; no further escalation is allowed.\",\n \"escalation-ceiling\":\n \"Adaptive is already at the highest capability tier; no stronger tier is configured.\",\n \"escalation-no-candidate\":\n \"No stronger resource was eligible to escalate to (unavailable, blocked, or not configured).\",\n}\n\nconst parkExplanation: Record<ParkReason, string> = {\n \"all-blocked\":\n \"Every resource in the tier is currently unavailable, so the turn ran without Adaptive routing.\",\n \"no-candidates\":\n \"There are no models configured for this tier, so the turn ran without Adaptive routing.\",\n \"no-compatible-resource\":\n \"No configured resource fits this turn's requirements, so the turn ran without Adaptive routing.\",\n \"pin-blocked\": \"The pinned resource is unavailable and no fallback is allowed while pinned.\",\n}\n\n/** Receipts shown per dialog page (bounded view). */\nexport const RECEIPTS_PAGE_SIZE = 20\n\nexport function receiptPage(receipts: readonly Receipt[]): Receipt[] {\n return receipts.slice(0, RECEIPTS_PAGE_SIZE)\n}\n"]}
|
|
@@ -7,8 +7,6 @@ export declare function healthEntries(snapshot: StatusSnapshot): {
|
|
|
7
7
|
}[];
|
|
8
8
|
/** Local date and time: quota windows may reset days or weeks from now. */
|
|
9
9
|
export declare function clockTime(until: number): string;
|
|
10
|
-
/** One plain status line for a resource: health, window, breaker, pin. */
|
|
11
|
-
export declare function statusLine(status: TierResourceStatus, pin: StatusSnapshot["pin"]): string;
|
|
12
10
|
/** The STATE column of the status table: window, breaker, pin markers. */
|
|
13
11
|
export declare function stateCell(status: TierResourceStatus, pin: StatusSnapshot["pin"]): string;
|
|
14
12
|
/**
|
|
@@ -29,10 +29,6 @@ function pinnedTo(ref, pin) {
|
|
|
29
29
|
return false;
|
|
30
30
|
return pin.providerID === ref.providerID && pin.id === ref.id && pin.variant === ref.variant;
|
|
31
31
|
}
|
|
32
|
-
/** One plain status line for a resource: health, window, breaker, pin. */
|
|
33
|
-
export function statusLine(status, pin) {
|
|
34
|
-
return `${resourceLabel(status.ref)} — ${status.health}${stateSuffix(status, pin)}`;
|
|
35
|
-
}
|
|
36
32
|
/** Everything after the health state: capacity usage, window, breaker, pin. */
|
|
37
33
|
function stateSuffix(status, pin) {
|
|
38
34
|
let suffix = "";
|
|
@@ -73,8 +69,6 @@ export function statusTable(snapshot) {
|
|
|
73
69
|
status.health,
|
|
74
70
|
stateCell(status, snapshot.pin),
|
|
75
71
|
]);
|
|
76
|
-
if (rows.length === 0)
|
|
77
|
-
return [];
|
|
78
72
|
// Header and rows are built together, always four cells wide — the column
|
|
79
73
|
// index cannot be out of range.
|
|
80
74
|
const widths = header.map((cell, column) =>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../../src/ui/flow/status.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Status-view helpers (ADP-033): flatten the status snapshot into one page\n * showing every configured model with its state, grouped by tier in priority\n * order. Pure and deterministic; local wall-clock rendering is derived from\n * the snapshot's own timestamps.\n */\nimport { TIERS } from \"../../core/tier-policy/types.js\"\nimport type { TierName } from \"../../core/tier-policy/types.js\"\nimport type { StatusSnapshot, TierResourceStatus } from \"../../rpc/status.js\"\nimport { resourceLabel } from \"./tiers.js\"\n\n/** Configured resources in tier order (quick → standard → advanced). */\nexport function healthEntries(\n snapshot: StatusSnapshot,\n): { tier: TierName; status: TierResourceStatus }[] {\n const entries: { tier: TierName; status: TierResourceStatus }[] = []\n for (const tier of TIERS) {\n for (const status of snapshot.tiers[tier]) entries.push({ tier, status })\n }\n return entries\n}\n\n/** Local date and time: quota windows may reset days or weeks from now. */\nexport function clockTime(until: number): string {\n const at = new Date(until)\n const hours = String(at.getHours()).padStart(2, \"0\")\n const minutes = String(at.getMinutes()).padStart(2, \"0\")\n const month = String(at.getMonth() + 1).padStart(2, \"0\")\n const day = String(at.getDate()).padStart(2, \"0\")\n return `${String(at.getFullYear())}-${month}-${day} ${hours}:${minutes}`\n}\n\nfunction pinnedTo(ref: TierResourceStatus[\"ref\"], pin: StatusSnapshot[\"pin\"]): boolean {\n if (pin === null) return false\n return pin.providerID === ref.providerID && pin.id === ref.id && pin.variant === ref.variant\n}\n\n/** Everything after the health state: capacity usage, window, breaker, pin. */\nfunction stateSuffix(status: TierResourceStatus, pin: StatusSnapshot[\"pin\"]): string {\n let suffix = \"\"\n if (status.windowed) {\n suffix +=\n status.windowUntil === null\n ? \", windowed (deadline unknown)\"\n : `, windowed until ${clockTime(status.windowUntil)}`\n }\n if (status.usedPercent !== null) {\n suffix += `, used ${String(status.usedPercent)}%`\n if (status.resetAt !== null) suffix += `, resets ${clockTime(status.resetAt)}`\n }\n if (status.breaker === \"open\") suffix += \", breaker open\"\n else if (status.breaker === \"half_open\") suffix += \", probing\"\n if (pinnedTo(status.ref, pin)) suffix += \" (pinned)\"\n return suffix\n}\n\n/** The STATE column of the status table: window, breaker, pin markers. */\nexport function stateCell(status: TierResourceStatus, pin: StatusSnapshot[\"pin\"]): string {\n return stateSuffix(status, pin).replace(/^[, ]+/, \"\") || \"-\"\n}\n\n/**\n * The whole inventory as a column-aligned table (header row first): models\n * grouped in tier order (quick → standard → advanced), one row per model,\n * columns padded to the widest cell so the grid reads at a glance. There is\n * no per-resource selection step: the table IS the view.\n */\nexport function statusTable(snapshot: StatusSnapshot): string[] {\n const header = [\"MODEL\", \"TIER\", \"HEALTH\", \"STATE\"]\n const rows = healthEntries(snapshot).map(({ tier, status }) => [\n resourceLabel(status.ref),\n tier,\n status.health,\n stateCell(status, snapshot.pin),\n ])\n // Header and rows are built together, always four cells wide — the column\n // index cannot be out of range.\n const widths = header.map((cell, column) =>\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n Math.max(cell.length, ...rows.map((row) => row[column]!.length)),\n )\n const render = (cells: string[]): string =>\n cells\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n .map((cell, column) => cell.padEnd(widths[column]!))\n .join(\" \")\n .trimEnd()\n return [render(header), ...rows.map(render)]\n}\n\n/** Empty-state copy for the status view when nothing is configured. */\nexport const STATUS_EMPTY_MESSAGE = \"No models configured yet — open Models to add some.\"\n"]}
|
|
@@ -7,7 +7,6 @@ import type { ModelRef } from "../../core/work-unit/types.js";
|
|
|
7
7
|
import type { TierName } from "../../core/tier-policy/types.js";
|
|
8
8
|
import type { StatusSnapshot, TierResourceStatus } from "../../rpc/status.js";
|
|
9
9
|
import type { SettingsV1 } from "../../state/settings/index.js";
|
|
10
|
-
import type { PatchResult } from "../../state/settings/types.js";
|
|
11
10
|
import type { SelectOption } from "./options.js";
|
|
12
11
|
/** `provider/model` with an `@variant` suffix when pinned to one. */
|
|
13
12
|
export declare function resourceLabel(ref: ModelRef): string;
|
|
@@ -21,6 +20,7 @@ export type TierEditorChoice = {
|
|
|
21
20
|
} | {
|
|
22
21
|
kind: "resource";
|
|
23
22
|
index: number;
|
|
23
|
+
ref: ModelRef;
|
|
24
24
|
};
|
|
25
25
|
export declare function tierEditorOptions(settings: SettingsV1, tier: TierName, status: StatusSnapshot | undefined): SelectOption<TierEditorChoice>[];
|
|
26
26
|
export declare function tierMenuOptions(status: StatusSnapshot): SelectOption<TierName>[];
|
|
@@ -31,12 +31,7 @@ export interface ModelSummary {
|
|
|
31
31
|
readonly name: string;
|
|
32
32
|
readonly variants: readonly string[];
|
|
33
33
|
}
|
|
34
|
-
export
|
|
35
|
-
providerID: string;
|
|
36
|
-
modelID: string;
|
|
37
|
-
}
|
|
38
|
-
export declare function modelMatches(info: ModelSummary, query: string): boolean;
|
|
39
|
-
export declare function modelPickerOptions(models: readonly ModelSummary[], query: string): SelectOption<ModelPick>[];
|
|
34
|
+
export declare function modelPickerOptions(models: readonly ModelSummary[]): SelectOption<ModelSummary>[];
|
|
40
35
|
export interface VariantChoice {
|
|
41
36
|
variant: string | undefined;
|
|
42
37
|
}
|
|
@@ -47,5 +42,3 @@ export declare function moveResourceToTop(resources: readonly ModelRef[], index:
|
|
|
47
42
|
export declare function moveResource(resources: readonly ModelRef[], index: number, delta: -1 | 1): ModelRef[];
|
|
48
43
|
/** Drop the resource at `index`; out-of-bounds keeps the list unchanged. */
|
|
49
44
|
export declare function removeResource(resources: readonly ModelRef[], index: number): ModelRef[];
|
|
50
|
-
/** First validation error line for an alert; undefined when the patch applied. */
|
|
51
|
-
export declare function validationMessage(result: PatchResult): string | undefined;
|
|
@@ -30,7 +30,7 @@ export function tierEditorOptions(settings, tier, status) {
|
|
|
30
30
|
settings.tiers[tier].forEach((ref, index) => {
|
|
31
31
|
options.push({
|
|
32
32
|
title: `${resourceLabel(ref)}${healthTag(tierStatus?.[index])}`,
|
|
33
|
-
value: { kind: "resource", index },
|
|
33
|
+
value: { kind: "resource", index, ref },
|
|
34
34
|
description: "Move or remove",
|
|
35
35
|
});
|
|
36
36
|
});
|
|
@@ -49,21 +49,11 @@ export function tierMenuOptions(status) {
|
|
|
49
49
|
return { title: `${tier} (${String(resources.length)})`, value: tier, description };
|
|
50
50
|
});
|
|
51
51
|
}
|
|
52
|
-
export function
|
|
53
|
-
|
|
54
|
-
if (q === "")
|
|
55
|
-
return true;
|
|
56
|
-
return (info.providerID.toLowerCase().includes(q) ||
|
|
57
|
-
info.modelID.toLowerCase().includes(q) ||
|
|
58
|
-
info.name.toLowerCase().includes(q));
|
|
59
|
-
}
|
|
60
|
-
export function modelPickerOptions(models, query) {
|
|
61
|
-
return models
|
|
62
|
-
.filter((info) => modelMatches(info, query))
|
|
63
|
-
.map((info) => ({
|
|
52
|
+
export function modelPickerOptions(models) {
|
|
53
|
+
return models.map((info) => ({
|
|
64
54
|
title: `${info.providerID}/${info.modelID}`,
|
|
65
55
|
description: info.name,
|
|
66
|
-
value:
|
|
56
|
+
value: info,
|
|
67
57
|
}));
|
|
68
58
|
}
|
|
69
59
|
export function variantOptions(variants) {
|
|
@@ -77,10 +67,7 @@ export function variantOptions(variants) {
|
|
|
77
67
|
}
|
|
78
68
|
/** Move the resource at `index` to the front (make it primary); out-of-bounds is a no-op. */
|
|
79
69
|
export function moveResourceToTop(resources, index) {
|
|
80
|
-
|
|
81
|
-
if (item === undefined)
|
|
82
|
-
return [...resources];
|
|
83
|
-
return [item, ...resources.filter((_, i) => i !== index)];
|
|
70
|
+
return [...resources.filter((_, i) => i === index), ...resources.filter((_, i) => i !== index)];
|
|
84
71
|
}
|
|
85
72
|
/** Swap the resource at `index` with its `delta` neighbour; bounds are no-ops. */
|
|
86
73
|
export function moveResource(resources, index, delta) {
|
|
@@ -96,13 +83,6 @@ export function moveResource(resources, index, delta) {
|
|
|
96
83
|
}
|
|
97
84
|
/** Drop the resource at `index`; out-of-bounds keeps the list unchanged. */
|
|
98
85
|
export function removeResource(resources, index) {
|
|
99
|
-
const item = resources[index];
|
|
100
|
-
if (item === undefined)
|
|
101
|
-
return [...resources];
|
|
102
86
|
return resources.filter((_, i) => i !== index);
|
|
103
87
|
}
|
|
104
|
-
/** First validation error line for an alert; undefined when the patch applied. */
|
|
105
|
-
export function validationMessage(result) {
|
|
106
|
-
return result.ok ? undefined : result.errors.join("; ");
|
|
107
|
-
}
|
|
108
88
|
//# sourceMappingURL=tiers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tiers.js","sourceRoot":"","sources":["../../../src/ui/flow/tiers.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Tier-editor view models (ADP-033): resource labels with clear health tags,\n * model search over the real OpenCode model list, variant picking, and pure\n * reorder/remove helpers. All functions are total and deterministic.\n */\nimport type { ModelRef } from \"../../core/work-unit/types.js\"\nimport { TIERS } from \"../../core/tier-policy/types.js\"\nimport type { TierName } from \"../../core/tier-policy/types.js\"\nimport type { StatusSnapshot, TierResourceStatus } from \"../../rpc/status.js\"\nimport type { SettingsV1 } from \"../../state/settings/index.js\"\nimport type { SelectOption } from \"./options.js\"\n\n/** `provider/model` with an `@variant` suffix when pinned to one. */\nexport function resourceLabel(ref: ModelRef): string {\n const base = `${ref.providerID}/${ref.id}`\n return ref.variant === undefined ? base : `${base}@${ref.variant}`\n}\n\n/**\n * Unavailable-resource display: empty string when healthy, otherwise a\n * bracketed, comma-separated tag list (health, breaker, window).\n */\nexport function healthTag(status: TierResourceStatus | undefined): string {\n if (status === undefined) return \"\"\n const parts: string[] = []\n if (status.health !== \"healthy\") parts.push(status.health)\n if (status.breaker === \"open\") parts.push(\"breaker-open\")\n else if (status.breaker === \"half_open\") parts.push(\"half-open\")\n if (status.windowed) parts.push(\"windowed\")\n return parts.length === 0 ? \"\" : ` [${parts.join(\", \")}]`\n}\n\nexport type TierEditorChoice = { kind: \"add\" } | { kind: \"resource\"; index: number; ref: ModelRef }\nexport function tierEditorOptions(\n settings: SettingsV1,\n tier: TierName,\n status: StatusSnapshot | undefined,\n): SelectOption<TierEditorChoice>[] {\n const options: SelectOption<TierEditorChoice>[] = [\n { title: \"Add model…\", value: { kind: \"add\" }, description: \"Search OpenCode models\" },\n ]\n const tierStatus = status?.tiers[tier]\n settings.tiers[tier].forEach((ref, index) => {\n options.push({\n title: `${resourceLabel(ref)}${healthTag(tierStatus?.[index])}`,\n value: { kind: \"resource\", index, ref },\n description: \"Move or remove\",\n })\n })\n // No \"Back\" row: Escape steps back out of the dialog (undefined pick →\n // the editor loop re-shows the tier list).\n return options\n}\n\nexport function tierMenuOptions(status: StatusSnapshot): SelectOption<TierName>[] {\n return TIERS.map((tier) => {\n const resources = status.tiers[tier]\n // The lists are short: showing them inline answers \"what is configured\n // where\" without opening the editor.\n const description =\n resources.length === 0\n ? \"(empty — add models to enable routing)\"\n : resources.map((entry) => resourceLabel(entry.ref)).join(\" · \")\n return { title: `${tier} (${String(resources.length)})`, value: tier, description }\n })\n}\n\n/** Structural summary of one real OpenCode model (built by the entry). */\nexport interface ModelSummary {\n readonly providerID: string\n readonly modelID: string\n readonly name: string\n readonly variants: readonly string[]\n}\n\nexport function modelPickerOptions(models: readonly ModelSummary[]): SelectOption<ModelSummary>[] {\n return models.map((info) => ({\n title: `${info.providerID}/${info.modelID}`,\n description: info.name,\n value: info,\n }))\n}\n\nexport interface VariantChoice {\n variant: string | undefined\n}\n\nexport function variantOptions(variants: readonly string[]): SelectOption<VariantChoice>[] {\n const options: SelectOption<VariantChoice>[] = [\n { title: \"(model default)\", value: { variant: undefined } },\n ]\n for (const variant of variants) {\n options.push({ title: `@${variant}`, value: { variant } })\n }\n return options\n}\n\n/** Move the resource at `index` to the front (make it primary); out-of-bounds is a no-op. */\nexport function moveResourceToTop(resources: readonly ModelRef[], index: number): ModelRef[] {\n return [...resources.filter((_, i) => i === index), ...resources.filter((_, i) => i !== index)]\n}\n\n/** Swap the resource at `index` with its `delta` neighbour; bounds are no-ops. */\nexport function moveResource(\n resources: readonly ModelRef[],\n index: number,\n delta: -1 | 1,\n): ModelRef[] {\n const next = [...resources]\n const target = index + delta\n const item = next[index]\n const other = next[target]\n if (item === undefined || other === undefined) return next\n next[index] = other\n next[target] = item\n return next\n}\n\n/** Drop the resource at `index`; out-of-bounds keeps the list unchanged. */\nexport function removeResource(resources: readonly ModelRef[], index: number): ModelRef[] {\n return resources.filter((_, i) => i !== index)\n}\n"]}
|