@khalilgharbaoui/opencode-claude-code-plugin 0.56.0 → 0.57.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/dist/tui.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/tui.ts","../src/subagent-sidebar.ts","../src/spawn-record-store.ts","../src/account-usage.ts","../src/account-sidebar.ts"],"sourcesContent":["/**\n * The TUI half of the package: a `Subagents` section at the bottom of\n * opencode's session sidebar listing the current session's subagents, running\n * first, each with a spinner (or ✓ / ✗), its agent type, its task description,\n * a `bg` marker for background ones, and a muted line saying what it really\n * runs as (`sonnet-5.5 · high · alpha`). Clicking a row opens that subagent's\n * session; opencode's own `up` key goes back to the parent.\n *\n * Below it, in the same slot, an `Accounts` section: one line per account\n * with a current usage reading (`appical · 5h 72% · resets 18:00`), from the\n * store the server half writes on every `rate_limit_event`\n * (`src/account-usage.ts`, laid out by `src/account-sidebar.ts`, h #g244). It\n * shares the slot rather than claiming a second one, so neither major has a\n * second registration to accept, and draws nothing until a reading exists.\n *\n * Always on: there is no option. One module serves both majors (h #g234):\n *\n * - opencode 1.x loads TUI plugins from `tui.json`'s `plugin` list, never\n * from `opencode.json`'s, resolves a package to `exports[\"./tui\"]`, and\n * wants a default export `{ id, tui }` with no `server`.\n * - opencode 2.x resolves the SAME `plugins` entry it already has\n * (`<repo>/dist`) to `<dir>/tui`, and wants `{ id, setup }`. 1.x ignores a\n * `setup` key and 2.x a `tui` key, so the default export carries both.\n *\n * Everything that decides WHAT to show lives in `src/subagent-sidebar.ts`\n * (pure, tested). Each major gets a thin adapter that gathers that module's\n * inputs from its own TUI state and supplies its own theme colours; the\n * section itself is drawn by one renderer. Nothing in here may throw into the\n * host or log: on the TUI's own thread stderr is the screen (h #g193), so every\n * failure degrades to drawing less.\n */\nimport { RGBA } from \"@opentui/core\"\nimport { createElement, createTextNode, effect, insert, insertNode, setProp } from \"@opentui/solid\"\nimport { createMemo, createSignal, onCleanup } from \"solid-js\"\nimport {\n SIDEBAR_ROW_WIDTH,\n SPINNER_FRAMES,\n collapsedSummary,\n completionsIn,\n deriveSubagentRows,\n layoutRow,\n partChildID,\n type ChildRunState,\n type ChildSessionInput,\n type CompletionInput,\n type Segment,\n type SidebarInput,\n type SubagentRow,\n type TaskPartInput,\n} from \"./subagent-sidebar.js\"\nimport { createSpawnRecordReader, type SpawnRecord } from \"./spawn-record-store.js\"\nimport { createAccountUsageReader, type AccountUsage } from \"./account-usage.js\"\nimport { accountUsageLines, type AccountUsageLine } from \"./account-sidebar.js\"\n\nexport const SUBAGENTS_TUI_PLUGIN_ID = \"opencode-claude-code-plugin.subagents\"\n\n/** After opencode 1.x's own sidebar sections (files is 500). */\nconst SECTION_ORDER = 900\nconst SPINNER_INTERVAL_MS = 100\n/** How often the spawn records and the finished rows' fade are rechecked. */\nconst REFRESH_INTERVAL_MS = 1500\n\n/** The tool names a subagent dispatch has: `task` on 1.x, `subagent` on 2.x. */\nconst TASK_TOOLS = new Set([\"task\", \"subagent\"])\n\ntype Color = RGBA\n\n/** The colours the section paints with, whatever theme shape the host has. */\nexport interface SidebarColors {\n text: Color\n muted: Color\n spinner: Color\n done: Color\n error: Color\n /** The effort on a row's second line: the theme's warning colour. */\n effort: Color\n effortFaded: Color\n hover: Color\n}\n\n/** What one major has to supply; the renderer below does everything else. */\ninterface SidebarHost {\n colors(): SidebarColors\n /** The inputs for one parent session, minus the clock and the records. */\n gather(parentID: string): Pick<SidebarInput, \"children\" | \"parts\" | \"completions\" | \"status\" | \"stoppedAt\">\n navigate(sessionID: string): void\n}\n\nfunction attempt<T>(fn: () => T, fallback: T): T {\n try {\n return fn()\n } catch {\n return fallback\n }\n}\n\n/**\n * `a` moved `weight` of the way to `b`, so a finished glyph reads as muted.\n * Works in whatever scale the host's RGBA uses, because it only ever hands\n * the class back values it read from the same class.\n */\nfunction mix(a: Color, b: Color, weight: number): Color {\n return attempt(\n () =>\n RGBA.fromValues(\n a.r + (b.r - a.r) * weight,\n a.g + (b.g - a.g) * weight,\n a.b + (b.b - a.b) * weight,\n a.a,\n ),\n a,\n )\n}\n\nfunction roleColor(colors: SidebarColors, role: Segment[\"role\"]): Color {\n switch (role) {\n case \"spinner\":\n return colors.spinner\n case \"done\":\n return colors.done\n case \"error\":\n return colors.error\n case \"effort\":\n return colors.effort\n case \"effort-faded\":\n return colors.effortFaded\n case \"agent\":\n case \"description\":\n return colors.text\n default:\n return colors.muted\n }\n}\n\n/**\n * The section, its clocks and the spawn-record reader, for one host. Returns\n * the slot renderer and a disposer.\n */\nfunction createSidebar(host: SidebarHost) {\n const [frame, setFrame] = createSignal(0)\n const [now, setNow] = createSignal(Date.now())\n const records = createSpawnRecordReader()\n let recordMap: Map<string, SpawnRecord> = attempt(() => records.read(), new Map())\n const [recordVersion, setRecordVersion] = createSignal(0)\n // The `Accounts` lines (h #g244): every account's last usage reading, from\n // the store the server half writes on each `rate_limit_event`. Its own clock\n // value, so a tick that only rechecks the readings does not re-derive the\n // subagent rows, which walk the session's messages.\n const usageReader = createAccountUsageReader()\n const [usage, setUsage] = createSignal<AccountUsage>(attempt(() => usageReader.read(), new Map()))\n const [usageNow, setUsageNow] = createSignal(Date.now())\n\n // One spinner clock and one refresh clock for every mounted section, each\n // running only while something needs it: nothing ticks while no subagent is\n // listed, and the spinner stops the moment the last one finishes.\n const sectionsRunning = new Set<symbol>()\n const sectionsShowing = new Set<symbol>()\n // Every mounted sidebar, whatever it shows: a usage reading arrives with no\n // event the TUI can see, so the one `stat` that finds it runs while a\n // sidebar is on screen, and nothing runs once none is.\n const sectionsMounted = new Set<symbol>()\n let spinner: ReturnType<typeof setInterval> | undefined\n let refresh: ReturnType<typeof setInterval> | undefined\n const syncClocks = () => {\n if (sectionsRunning.size > 0 && !spinner) {\n spinner = setInterval(() => setFrame((value) => (value + 1) % SPINNER_FRAMES.length), SPINNER_INTERVAL_MS)\n } else if (sectionsRunning.size === 0 && spinner) {\n clearInterval(spinner)\n spinner = undefined\n }\n const wanted = sectionsShowing.size > 0 || sectionsMounted.size > 0\n if (wanted && !refresh) {\n refresh = setInterval(() => {\n if (sectionsShowing.size > 0) {\n const next = attempt(() => records.read(), recordMap)\n if (next !== recordMap) {\n recordMap = next\n setRecordVersion((value) => value + 1)\n }\n setNow(Date.now())\n }\n // The reader hands back the same map while the file is unchanged, and\n // the clock moves once a minute (a reset is shown to the minute), so\n // an idle tick re-lays nothing out.\n setUsage(attempt(() => usageReader.read(), usage()))\n const minute = Math.floor(Date.now() / 60_000) * 60_000\n if (minute !== usageNow()) setUsageNow(minute)\n }, REFRESH_INTERVAL_MS)\n } else if (!wanted && refresh) {\n clearInterval(refresh)\n refresh = undefined\n }\n }\n\n const deriveRows = (parentID: string): SubagentRow[] => {\n recordVersion()\n return deriveSubagentRows({\n ...host.gather(parentID),\n record: (id) => recordMap.get(id),\n now: now(),\n })\n }\n\n const buildRow = (row: SubagentRow): OpentuiNode => {\n const box = createElement(\"box\")\n const layout = layoutRow(row, SIDEBAR_ROW_WIDTH)\n const first = createElement(\"text\")\n setProp(first, \"wrapMode\", \"none\")\n layout.first.forEach((segment, index) => {\n const span = createElement(\"span\")\n if (index === 0 && row.state === \"running\") {\n insert(span, () => SPINNER_FRAMES[frame() % SPINNER_FRAMES.length])\n } else {\n insertNode(span, createTextNode(segment.text))\n }\n effect(() => setProp(span, \"style\", { fg: roleColor(host.colors(), segment.role) }))\n insertNode(first, span)\n })\n insertNode(box, first)\n if (layout.second.length > 0) {\n // One line of spans, like the first, so the effort can take its own colour.\n const second = createElement(\"text\")\n setProp(second, \"wrapMode\", \"none\")\n for (const segment of layout.second) {\n const span = createElement(\"span\")\n insertNode(span, createTextNode(segment.text))\n effect(() => setProp(span, \"style\", { fg: roleColor(host.colors(), segment.role) }))\n insertNode(second, span)\n }\n insertNode(box, second)\n }\n if (row.sessionID) {\n const sessionID = row.sessionID\n setProp(box, \"onMouseDown\", () => attempt(() => host.navigate(sessionID), undefined))\n setProp(box, \"onMouseOver\", () =>\n attempt(() => setProp(box, \"backgroundColor\", host.colors().hover), undefined),\n )\n setProp(box, \"onMouseOut\", () =>\n attempt(() => setProp(box, \"backgroundColor\", \"transparent\"), undefined),\n )\n }\n return box\n }\n\n const buildSection = (\n list: SubagentRow[],\n expanded: () => boolean,\n setExpanded: (next: (value: boolean) => boolean) => boolean,\n ): OpentuiNode => {\n const section = createElement(\"box\")\n // Heading, styled like opencode 1.x's own MCP and Todo sections: a bold\n // title, and with more than two rows a caret that folds the list away\n // behind a one-line summary.\n const header = createElement(\"box\")\n setProp(header, \"flexDirection\", \"row\")\n setProp(header, \"gap\", 1)\n setProp(header, \"onMouseDown\", () => list.length > 2 && setExpanded((value) => !value))\n if (list.length > 2) {\n const caret = createElement(\"text\")\n insert(caret, () => (expanded() ? \"▼\" : \"▶\"))\n effect(() => setProp(caret, \"fg\", host.colors().text))\n insertNode(header, caret)\n }\n const title = createElement(\"text\")\n const bold = createElement(\"b\")\n insertNode(bold, createTextNode(\"Subagents\"))\n insertNode(title, bold)\n const summary = createElement(\"span\")\n insert(summary, () => (expanded() ? \"\" : ` (${collapsedSummary(list)})`))\n effect(() => setProp(summary, \"style\", { fg: host.colors().muted }))\n insertNode(title, summary)\n effect(() => setProp(title, \"fg\", host.colors().text))\n insertNode(header, title)\n insertNode(section, header)\n\n const body = createElement(\"box\")\n insert(body, () => (expanded() || list.length <= 2 ? list.map(buildRow) : null))\n insertNode(section, body)\n return section\n }\n\n const buildAccounts = (lines: AccountUsageLine[]): OpentuiNode => {\n const section = createElement(\"box\")\n const title = createElement(\"text\")\n const bold = createElement(\"b\")\n insertNode(bold, createTextNode(\"Accounts\"))\n insertNode(title, bold)\n effect(() => setProp(title, \"fg\", host.colors().text))\n insertNode(section, title)\n for (const line of lines) {\n const text = createElement(\"text\")\n setProp(text, \"wrapMode\", \"none\")\n for (const segment of line.segments) {\n const span = createElement(\"span\")\n insertNode(span, createTextNode(segment.text))\n effect(() => setProp(span, \"style\", { fg: roleColor(host.colors(), segment.role) }))\n insertNode(text, span)\n }\n insertNode(section, text)\n }\n return section\n }\n\n const render = (sessionID: () => string | undefined): OpentuiNode => {\n const root = createElement(\"box\")\n const subagentsBox = createElement(\"box\")\n const accountsBox = createElement(\"box\")\n insertNode(root, subagentsBox)\n insertNode(root, accountsBox)\n const key = Symbol(\"subagents-section\")\n sectionsMounted.add(key)\n const [expanded, setExpanded] = createSignal(true)\n const rows = createMemo(() => {\n const parentID = sessionID()\n if (!parentID) return [] as SubagentRow[]\n return attempt(() => deriveRows(parentID), [] as SubagentRow[])\n })\n effect(() => {\n const list = rows()\n if (list.some((row) => row.state === \"running\")) sectionsRunning.add(key)\n else sectionsRunning.delete(key)\n if (list.length > 0) sectionsShowing.add(key)\n else sectionsShowing.delete(key)\n syncClocks()\n })\n onCleanup(() => {\n sectionsRunning.delete(key)\n sectionsShowing.delete(key)\n sectionsMounted.delete(key)\n syncClocks()\n })\n insert(subagentsBox, () => {\n const list = rows()\n if (list.length === 0) return null\n return attempt(() => buildSection(list, expanded, setExpanded), null)\n })\n const accountLines = createMemo(() =>\n attempt(() => accountUsageLines(usage(), usageNow()), [] as AccountUsageLine[]),\n )\n insert(accountsBox, () => {\n const lines = accountLines()\n if (lines.length === 0) return null\n return attempt(() => buildAccounts(lines), null)\n })\n syncClocks()\n return root\n }\n\n const dispose = () => {\n sectionsRunning.clear()\n sectionsShowing.clear()\n sectionsMounted.clear()\n syncClocks()\n }\n return { render, dispose }\n}\n\n// ---------------------------------------------------------------------------\n// opencode 1.x\n// ---------------------------------------------------------------------------\n\n// The structural slice of opencode 1.x's `TuiPluginApi` this module reads.\n// Hand-written, like `src/opencode-types.ts`, so nothing is imported from\n// `@opencode-ai/plugin`.\ninterface V1SessionInfo {\n id: string\n parentID?: string\n title?: string\n agent?: string\n time?: { created?: number; updated?: number }\n}\ninterface V1Part {\n type: string\n tool?: string\n synthetic?: boolean\n text?: string\n state?: {\n status?: string\n input?: Record<string, unknown>\n metadata?: Record<string, unknown>\n title?: string\n time?: { start?: number; end?: number }\n }\n}\ninterface V1Api {\n state: {\n session: {\n get(sessionID: string): V1SessionInfo | undefined\n messages(sessionID: string): ReadonlyArray<{ id: string; role: string; time?: { created?: number } }> | undefined\n status(sessionID: string): { type?: string } | undefined\n }\n part(messageID: string): ReadonlyArray<V1Part> | undefined\n }\n theme: {\n current: {\n text: Color\n textMuted: Color\n accent: Color\n success: Color\n warning: Color\n error: Color\n backgroundElement: Color\n }\n }\n route: { navigate(name: string, params?: Record<string, unknown>): void }\n event: { on(type: string, handler: (event: { properties?: any }) => void): () => void }\n client?: { session?: { children?: (input: { sessionID: string }) => Promise<{ data?: V1SessionInfo[] }> } }\n slots: { register(plugin: { order?: number; slots: Record<string, (ctx: unknown, props: any) => OpentuiNode> }): string }\n lifecycle?: { onDispose(fn: () => void): () => void }\n}\n\nexport function registerV1(api: V1Api): void {\n // Child sessions seen per parent. 1.x has no session list in its TUI state,\n // so they come from its events, from a one-off `children` fetch the first\n // time a parent is shown (a TUI started after the dispatch still lists\n // them), and from finished parts' metadata.\n const childrenByParent = new Map<string, Map<string, ChildSessionInput>>()\n const fetched = new Set<string>()\n const stoppedAt = new Map<string, number>()\n const lastStatus = new Map<string, string>()\n const [version, setVersion] = createSignal(0)\n const bump = () => setVersion((value) => value + 1)\n\n const remember = (info: V1SessionInfo | undefined) => {\n if (!info?.id || !info.parentID) return false\n let children = childrenByParent.get(info.parentID)\n if (!children) {\n children = new Map()\n childrenByParent.set(info.parentID, children)\n }\n children.set(info.id, {\n id: info.id,\n title: info.title,\n agent: info.agent,\n created: info.time?.created,\n updated: info.time?.updated,\n })\n return true\n }\n\n const unsubscribe: Array<() => void> = []\n const listen = (type: string, handler: (properties: any) => void) => {\n const off = attempt(\n () => api.event.on(type, (event) => attempt(() => handler(event?.properties ?? {}), undefined)),\n undefined,\n )\n if (off) unsubscribe.push(off)\n }\n listen(\"session.created\", (properties) => remember(properties.info) && bump())\n listen(\"session.updated\", (properties) => remember(properties.info) && bump())\n listen(\"session.deleted\", (properties) => {\n const info = properties.info as V1SessionInfo | undefined\n if (info?.parentID && childrenByParent.get(info.parentID)?.delete(info.id)) bump()\n })\n listen(\"session.status\", (properties) => {\n const id = properties.sessionID as string | undefined\n const type = properties.status?.type as string | undefined\n if (!id || !type) return\n const previous = lastStatus.get(id)\n lastStatus.set(id, type)\n if (type === \"idle\" && previous && previous !== \"idle\") stoppedAt.set(id, Date.now())\n if (type !== \"idle\") stoppedAt.delete(id)\n })\n\n const fetchChildren = (parentID: string) => {\n if (fetched.has(parentID)) return\n fetched.add(parentID)\n const session = api.client?.session\n if (!session?.children) return\n void Promise.resolve()\n .then(() => session.children!.call(session, { sessionID: parentID }))\n .then((response) => {\n let changed = false\n for (const info of response?.data ?? []) changed = remember(info) || changed\n if (changed) bump()\n })\n .catch(() => undefined)\n }\n\n const host: SidebarHost = {\n colors() {\n const theme = api.theme.current\n return {\n text: theme.text,\n muted: theme.textMuted,\n spinner: theme.accent,\n done: mix(theme.success, theme.textMuted, 0.45),\n error: mix(theme.error, theme.textMuted, 0.35),\n effort: theme.warning ?? theme.textMuted,\n effortFaded: mix(theme.warning ?? theme.textMuted, theme.textMuted, 0.45),\n hover: theme.backgroundElement,\n }\n },\n gather(parentID) {\n version()\n fetchChildren(parentID)\n const parts: TaskPartInput[] = []\n const completions: CompletionInput[] = []\n for (const message of api.state.session.messages(parentID) ?? []) {\n const messageParts = api.state.part(message.id) ?? []\n if (message.role === \"assistant\") {\n for (const part of messageParts) {\n if (part.type !== \"tool\" || !TASK_TOOLS.has(part.tool ?? \"\")) continue\n const state = part.state ?? {}\n parts.push({\n status: state.status,\n input: state.input,\n metadata: state.metadata,\n title: state.title,\n start: state.time?.start,\n end: state.time?.end,\n })\n }\n } else if (message.role === \"user\") {\n // A background child's end is a synthetic user part in the parent.\n for (const part of messageParts) {\n if (part.type !== \"text\" || part.synthetic !== true || !part.text) continue\n completions.push(...completionsIn(part.text, message.time?.created))\n }\n }\n }\n const known = new Map(childrenByParent.get(parentID) ?? [])\n for (const part of parts) {\n const id = partChildID(part)\n if (!id || known.has(id)) continue\n const info = attempt(() => api.state.session.get(id), undefined)\n known.set(id, {\n id,\n title: info?.title,\n agent: info?.agent,\n created: info?.time?.created ?? part.start,\n updated: info?.time?.updated,\n })\n }\n return {\n children: [...known.values()],\n parts,\n completions,\n status: (id: string): ChildRunState => {\n const type = attempt(() => api.state.session.status(id)?.type, undefined)\n if (type === \"busy\" || type === \"retry\") return \"busy\"\n if (type === \"idle\") return \"idle\"\n return \"unknown\"\n },\n stoppedAt: (id: string) => stoppedAt.get(id),\n }\n },\n navigate(sessionID) {\n api.route.navigate(\"session\", { sessionID })\n },\n }\n\n const sidebar = createSidebar(host)\n api.slots.register({\n order: SECTION_ORDER,\n slots: {\n sidebar_content(_ctx: unknown, props: { session_id: string }) {\n return sidebar.render(() => props.session_id)\n },\n },\n })\n api.lifecycle?.onDispose(() => {\n for (const off of unsubscribe) attempt(off, undefined)\n sidebar.dispose()\n })\n}\n\n// ---------------------------------------------------------------------------\n// opencode 2.x\n// ---------------------------------------------------------------------------\n\n// The structural slice of opencode 2.x's TUI `Context` this module reads,\n// measured on 2.0.22 (h #g234). Its sessions carry `parentID`, its status is\n// `\"running\" | \"idle\"`, an assistant message holds its tool calls inline as\n// `content[]` items named `subagent` (with `metadata.sessionID` set while the\n// call still runs, unlike 1.x), and a background child's end is a message of\n// type `synthetic` whose text is `<subagent sessionID=\"...\" state=\"...\">`.\ninterface V2Session {\n id: string\n parentID?: string\n title?: string\n agent?: string\n time?: { created?: number; updated?: number }\n}\ninterface V2Message {\n type?: string\n text?: string\n time?: { created?: number }\n content?: Array<{\n type?: string\n name?: string\n time?: { created?: number; completed?: number }\n state?: { status?: string; input?: Record<string, unknown>; metadata?: Record<string, unknown> }\n }>\n}\ntype V2Shade = Record<string, Color>\ninterface V2Context {\n data: {\n session: {\n list(): V2Session[] | undefined\n status(sessionID: string): string\n message: { list(sessionID: string): V2Message[] | undefined }\n }\n }\n theme: {\n text: { base: Color; muted: Color; feedback: { success: V2Shade; warning?: V2Shade; error: V2Shade } }\n background: { raised: { base: Color } }\n hue: { accent: V2Shade }\n }\n themeMode?: \"dark\" | \"light\"\n ui: {\n router: { navigate(destination: { type: \"session\"; sessionID: string }): void }\n slot(claim: { append: string; render: (input: { sessionID: string }) => OpentuiNode }): () => void\n }\n}\n\nexport function setupV2(context: V2Context): () => void {\n const host: SidebarHost = {\n colors() {\n const theme = context.theme\n const muted = theme.text.muted\n // The accent hue at the shade that reads on this mode's background.\n const accent = theme.hue.accent[context.themeMode === \"light\" ? \"500\" : \"200\"] ?? theme.text.base\n return {\n text: theme.text.base,\n muted,\n spinner: accent,\n done: mix(theme.text.feedback.success.muted ?? theme.text.feedback.success.base, muted, 0.45),\n error: mix(theme.text.feedback.error.muted ?? theme.text.feedback.error.base, muted, 0.35),\n effort: theme.text.feedback.warning?.base ?? muted,\n effortFaded: mix(theme.text.feedback.warning?.base ?? muted, muted, 0.45),\n hover: theme.background.raised.base,\n }\n },\n gather(parentID) {\n const children: ChildSessionInput[] = (context.data.session.list() ?? [])\n .filter((session) => session.parentID === parentID)\n .map((session) => ({\n id: session.id,\n title: session.title,\n agent: session.agent,\n created: session.time?.created,\n updated: session.time?.updated,\n }))\n const parts: TaskPartInput[] = []\n const completions: CompletionInput[] = []\n for (const message of context.data.session.message.list(parentID) ?? []) {\n if (message.type === \"assistant\") {\n for (const item of message.content ?? []) {\n if (item.type !== \"tool\" || !TASK_TOOLS.has(item.name ?? \"\")) continue\n parts.push({\n status: item.state?.status,\n input: item.state?.input,\n metadata: item.state?.metadata,\n start: item.time?.created,\n end: item.time?.completed,\n })\n }\n } else if (message.type === \"synthetic\" && message.text) {\n completions.push(...completionsIn(message.text, message.time?.created))\n }\n }\n return {\n children,\n parts,\n completions,\n status: (id: string): ChildRunState => {\n const status = attempt(() => context.data.session.status(id), undefined)\n if (status === \"running\") return \"busy\"\n if (status === \"idle\") return \"idle\"\n return \"unknown\"\n },\n stoppedAt: () => undefined,\n }\n },\n navigate(sessionID) {\n context.ui.router.navigate({ type: \"session\", sessionID })\n },\n }\n\n const sidebar = createSidebar(host)\n const release = attempt(\n () =>\n context.ui.slot({\n append: \"sidebar.content\",\n render: (input) => sidebar.render(() => input.sessionID),\n }),\n undefined,\n )\n return () => {\n if (release) attempt(release, undefined)\n sidebar.dispose()\n }\n}\n\nconst tui = async (api: V1Api): Promise<void> => {\n attempt(() => registerV1(api), undefined)\n}\n\nconst setup = (context: V2Context): (() => void) | void =>\n attempt(() => setupV2(context), undefined)\n\nexport default { id: SUBAGENTS_TUI_PLUGIN_ID, tui, setup }\n","/**\n * The model behind the sidebar's `Subagents` section (`src/tui.ts`): which\n * subagents of one opencode session to show, in what state, in what order, and\n * the exact text of each row.\n *\n * Pure on purpose. It imports nothing, so the TUI bundle carries no logger (a\n * logger on the TUI's main thread writes to the terminal it is drawing on,\n * h #g193) and every rule here is testable without a terminal. The TUI half\n * only gathers the inputs and paints the segments this module returns.\n *\n * Measured on opencode 1.18.35 (h #g234), and each one shapes a rule below:\n *\n * - A RUNNING `task` part carries no `state.metadata` in the TUI's store, so\n * the child session id is not on the part until it completes. Rows are\n * built from the child SESSIONS (`parentID` is the current session) and a\n * part is matched to its child by the title opencode gives the child,\n * `<description> (@<agent> subagent)`.\n * - A background dispatch's part completes at once with a `<task id\n * state=\"running\">` envelope; the child's end arrives later as a synthetic\n * user part `<task id=\"ses_...\" state=\"completed\">` in the parent (opencode\n * 2 writes `<subagent sessionID=\"...\" state=\"...\">`). That envelope is the\n * authoritative end of a background child.\n * - `metadata.model` on a finished part is what opencode ASKED for, not what\n * ran: an agent's `forceModel`, its `reasoningEffort`, the dispatch form and\n * the fallback chain all change the spawn without telling opencode. So the\n * label prefers the spawn record the server half writes\n * (`src/spawn-record-store.ts`) and falls back to the part.\n */\n\nexport type SubagentState = \"running\" | \"done\" | \"error\"\n\n/** One child session as either major reports it. */\nexport interface ChildSessionInput {\n id: string\n title?: string\n agent?: string\n created?: number\n updated?: number\n}\n\n/** A `task` / `subagent` tool part in the parent, as either major reports it. */\nexport interface TaskPartInput {\n status?: string\n input?: Record<string, unknown>\n metadata?: Record<string, unknown>\n title?: string\n start?: number\n end?: number\n}\n\n/** A background child's end, read out of the parent's synthetic user parts. */\nexport interface CompletionInput {\n sessionID: string\n state: string\n at?: number\n}\n\n/** What the server half recorded about a session's real spawn. */\nexport interface SpawnRecordInput {\n model?: string\n effort?: string\n account?: string\n at?: number\n}\n\nexport type ChildRunState = \"busy\" | \"idle\" | \"unknown\"\n\nexport interface SidebarInput {\n children: ChildSessionInput[]\n parts: TaskPartInput[]\n completions: CompletionInput[]\n status: (sessionID: string) => ChildRunState\n record: (sessionID: string) => SpawnRecordInput | undefined\n /** When the TUI first saw a child stop, by session id. */\n stoppedAt: (sessionID: string) => number | undefined\n now: number\n}\n\nexport interface SubagentRow {\n /** The child session, absent while a dispatch has not created one yet. */\n sessionID?: string\n agent: string\n description: string\n background: boolean\n state: SubagentState\n /** `sonnet-5.5 · high · alpha`, empty when nothing is known. */\n label: string\n /** The effort piece of `label`, drawn in the theme's warning colour. */\n effort?: string\n started: number\n finished?: number\n}\n\n/** How long a finished subagent stays in the list, and how many of them. */\nexport const FINISHED_TTL_MS = 10 * 60_000\nexport const FINISHED_KEEP = 3\n/**\n * A background child that reads idle with no envelope yet is still starting\n * for this long: opencode creates the session before it marks it busy.\n */\nexport const STARTING_GRACE_MS = 10_000\n/** The usable width of a row in opencode 1.18.35's 42-column sidebar. */\nexport const SIDEBAR_ROW_WIDTH = 36\nexport const SPINNER_FRAMES = [\"⠋\", \"⠙\", \"⠹\", \"⠸\", \"⠼\", \"⠴\", \"⠦\", \"⠧\", \"⠇\", \"⠏\"]\n\nconst TITLE_SUFFIX = /\\s*\\(@([^)\\s]+) subagent\\)\\s*$/\n\n/** `Fix the form (@implementor subagent)` -> description and agent. */\nexport function parseChildTitle(title: string | undefined): { description: string; agent?: string } {\n const text = (title ?? \"\").trim()\n const match = TITLE_SUFFIX.exec(text)\n if (!match) return { description: text }\n return { description: text.slice(0, match.index).trim(), agent: match[1] }\n}\n\nfunction text(value: unknown): string | undefined {\n return typeof value === \"string\" && value.trim() ? value.trim() : undefined\n}\n\n/** The child session a part names, on either major's metadata. */\nexport function partChildID(part: TaskPartInput): string | undefined {\n const metadata = part.metadata ?? {}\n return text(metadata.sessionId) ?? text(metadata.sessionID)\n}\n\nfunction partAgent(part: TaskPartInput): string | undefined {\n const input = part.input ?? {}\n return text(input.subagent_type) ?? text(input.agent)\n}\n\nfunction partDescription(part: TaskPartInput): string | undefined {\n return text(part.input?.description) ?? text(part.title)\n}\n\nfunction partBackground(part: TaskPartInput): boolean {\n return part.input?.background === true || part.metadata?.background === true\n}\n\nconst COMPLETION_ENVELOPE =\n /<(?:task\\s+id|subagent\\s+sessionID)=\"(ses_[A-Za-z0-9]+)\"\\s+state=\"([a-z_]+)\"/g\n\n/**\n * Every background end a text names, in order. Both majors' envelopes:\n * `<task id=\"ses_...\" state=\"completed\">` (1.x) and\n * `<subagent sessionID=\"ses_...\" state=\"completed\">` (2.x). `running` is the\n * dispatch's own answer, not an end, so it is skipped.\n */\nexport function completionsIn(value: string, at?: number): CompletionInput[] {\n const found: CompletionInput[] = []\n for (const match of value.matchAll(COMPLETION_ENVELOPE)) {\n if (match[2] === \"running\") continue\n found.push({ sessionID: match[1], state: match[2], at })\n }\n return found\n}\n\nfunction endState(state: string): SubagentState {\n return state === \"completed\" ? \"done\" : \"error\"\n}\n\n/**\n * `claude-opus-5-5` -> `opus-5.5`, `claude-sonnet-4-5-20250929@alpha` ->\n * `sonnet-4.5`, `claude-opus-5-5-fast` -> `opus-5.5 fast`,\n * `claude-opus-5[1m]` -> `opus-5 1m`. Anything that is not a Claude id is\n * returned without its account marker and otherwise untouched.\n */\nexport function shortModelName(modelID: string | undefined): string | undefined {\n let id = text(modelID)\n if (!id) return undefined\n const at = id.indexOf(\"@\")\n if (at > 0) id = id.slice(0, at)\n let suffix = \"\"\n const window = /\\[(\\d+[km])\\]$/i.exec(id)\n if (window) {\n suffix += ` ${window[1].toLowerCase()}`\n id = id.slice(0, window.index)\n }\n if (id.endsWith(\"-fast\")) {\n suffix = ` fast${suffix}`\n id = id.slice(0, -\"-fast\".length)\n }\n if (!id.startsWith(\"claude-\")) return `${id}${suffix}`\n id = id.slice(\"claude-\".length).replace(/-\\d{8}$/, \"\")\n const match = /^([a-z]+)-(\\d+)(?:-(\\d+))?$/.exec(id)\n if (!match) return `${id}${suffix}`\n const version = match[3] === undefined ? match[2] : `${match[2]}.${match[3]}`\n return `${match[1]}-${version}${suffix}`\n}\n\n/**\n * The account a provider id names on a multi-account install\n * (`claude-code-alpha` -> `alpha`). A bare `claude-code` (one account) and a\n * provider that is not this plugin's name none: there is nothing to tell apart.\n */\nexport function accountFromProvider(providerID: string | undefined): string | undefined {\n const id = text(providerID)\n if (!id?.startsWith(\"claude-code-\")) return undefined\n return id.slice(\"claude-code-\".length) || undefined\n}\n\n/** `sonnet-5.5 · high · alpha`; pieces that are not known are left out. */\nexport function subagentLabel(\n record: SpawnRecordInput | undefined,\n part: TaskPartInput | undefined,\n): string {\n const asked = part?.metadata?.model as { modelID?: unknown; providerID?: unknown } | undefined\n const askedModel = typeof asked?.modelID === \"string\" ? asked.modelID : undefined\n const askedProvider = typeof asked?.providerID === \"string\" ? asked.providerID : undefined\n const model = shortModelName(record?.model ?? askedModel)\n const effort = text(record?.effort)\n const account = record?.model ? text(record.account) : accountFromProvider(askedProvider)\n return [model, effort, account].filter(Boolean).join(\" · \")\n}\n\n/**\n * Cut `value` to `width` cells with a trailing ellipsis. Counts code points,\n * which is a cell each for the text a task description holds; a wide glyph\n * can overrun by one cell and is left to the renderer's own clipping.\n */\nexport function truncate(value: string, width: number): string {\n const chars = Array.from(value.replace(/\\s+/g, \" \").trim())\n if (width <= 0) return \"\"\n if (chars.length <= width) return chars.join(\"\")\n if (width === 1) return \"…\"\n return `${chars.slice(0, width - 1).join(\"\").trimEnd()}…`\n}\n\n/**\n * The rows for one session: running first (oldest first), then finished\n * (newest first), at most `FINISHED_KEEP` of those and none older than\n * `FINISHED_TTL_MS`. Empty when nothing is worth showing, which is what makes\n * the section disappear.\n */\nexport function deriveSubagentRows(input: SidebarInput): SubagentRow[] {\n const completions = new Map<string, CompletionInput>()\n for (const completion of input.completions) completions.set(completion.sessionID, completion)\n\n const byChild = new Map<string, TaskPartInput>()\n const unmatched: TaskPartInput[] = []\n for (const part of input.parts) {\n const child = partChildID(part)\n if (child) byChild.set(child, part)\n else unmatched.push(part)\n }\n\n const rows: SubagentRow[] = []\n const seen = new Set<string>()\n for (const child of input.children) {\n if (seen.has(child.id)) continue\n seen.add(child.id)\n const parsed = parseChildTitle(child.title)\n let part = byChild.get(child.id)\n if (!part) {\n // A running part has no child id yet: match it on the title opencode\n // gave the child, agent and description both.\n const index = unmatched.findIndex(\n (candidate) =>\n partDescription(candidate) === parsed.description &&\n (parsed.agent === undefined || partAgent(candidate) === parsed.agent),\n )\n if (index >= 0) part = unmatched.splice(index, 1)[0]\n }\n rows.push(rowFor(input, child, parsed, part, completions.get(child.id)))\n }\n // A dispatch opencode has not created a session for yet: shown, not clickable.\n for (const part of unmatched) {\n if (part.status !== \"pending\" && part.status !== \"running\") continue\n const description = partDescription(part)\n const agent = partAgent(part)\n if (!description && !agent) continue\n rows.push({\n agent: agent ?? \"subagent\",\n description: description ?? \"\",\n background: partBackground(part),\n state: \"running\",\n label: subagentLabel(undefined, part),\n started: part.start ?? input.now,\n })\n }\n\n const running = rows\n .filter((row) => row.state === \"running\")\n .sort((a, b) => a.started - b.started)\n const finished = rows\n .filter((row) => row.state !== \"running\")\n .filter((row) => input.now - (row.finished ?? row.started) <= FINISHED_TTL_MS)\n .sort((a, b) => (b.finished ?? b.started) - (a.finished ?? a.started))\n .slice(0, FINISHED_KEEP)\n return [...running, ...finished]\n}\n\nfunction rowFor(\n input: SidebarInput,\n child: ChildSessionInput,\n parsed: { description: string; agent?: string },\n part: TaskPartInput | undefined,\n completion: CompletionInput | undefined,\n): SubagentRow {\n const background = part ? partBackground(part) : false\n const started = part?.start ?? child.created ?? input.now\n const status = input.status(child.id)\n let state: SubagentState\n let finished: number | undefined\n if (part && !background && part.status !== \"pending\" && part.status !== \"running\") {\n state = part.status === \"completed\" ? \"done\" : \"error\"\n finished = part.end\n } else if (completion) {\n state = endState(completion.state)\n finished = completion.at\n } else if (status === \"busy\") {\n state = \"running\"\n } else if (part && !background) {\n // pending or running: the parent is still waiting on this child.\n state = \"running\"\n } else if (status === \"idle\" && input.now - started < STARTING_GRACE_MS) {\n state = \"running\"\n } else if (status === \"unknown\" && background) {\n // Nothing says it stopped: a background child keeps its spinner until\n // its envelope arrives or opencode reports it idle.\n state = \"running\"\n } else {\n state = \"done\"\n }\n if (state !== \"running\" && finished === undefined) {\n finished = input.stoppedAt(child.id) ?? child.updated ?? started\n }\n return {\n sessionID: child.id,\n agent: parsed.agent ?? (part ? partAgent(part) : undefined) ?? text(child.agent) ?? \"subagent\",\n description: (part ? partDescription(part) : undefined) ?? parsed.description,\n background,\n state,\n label: subagentLabel(input.record(child.id), part),\n effort: text(input.record(child.id)?.effort),\n started,\n finished,\n }\n}\n\n/** A run of text with the role the TUI maps onto a theme colour. */\nexport interface Segment {\n text: string\n role: \"spinner\" | \"done\" | \"error\" | \"agent\" | \"description\" | \"muted\" | \"effort\" | \"effort-faded\"\n}\n\n/**\n * The two lines of one row, laid out for `width` cells:\n *\n * ⠋ implementor Fix the dispatch f… bg\n * sonnet-5.5 · high · alpha\n *\n * The `bg` marker is right-aligned, the description takes what is left, and a\n * finished row is drawn in the muted role throughout apart from its glyph.\n */\nexport function layoutRow(\n row: SubagentRow,\n width: number = SIDEBAR_ROW_WIDTH,\n frame = 0,\n): { first: Segment[]; second: Segment[] } {\n const finished = row.state !== \"running\"\n const glyph =\n row.state === \"running\"\n ? SPINNER_FRAMES[frame % SPINNER_FRAMES.length]\n : row.state === \"done\"\n ? \"✓\"\n : \"✗\"\n const marker = row.background ? \"bg\" : \"\"\n const reserved = marker ? marker.length + 1 : 0\n const agentWidth = Math.max(1, Math.min(Array.from(row.agent).length, width - 2 - reserved - 6))\n const agent = truncate(row.agent, agentWidth)\n const room = width - 2 - Array.from(agent).length - 1 - reserved\n const description = room > 0 ? truncate(row.description, room) : \"\"\n const used = 2 + Array.from(agent).length + (description ? 1 + Array.from(description).length : 0)\n const first: Segment[] = [\n { text: glyph, role: row.state === \"running\" ? \"spinner\" : row.state === \"done\" ? \"done\" : \"error\" },\n { text: \" \", role: \"muted\" },\n { text: agent, role: finished ? \"muted\" : \"agent\" },\n ]\n if (description) {\n first.push({ text: \" \", role: \"muted\" })\n first.push({ text: description, role: finished ? \"muted\" : \"description\" })\n }\n if (marker) {\n first.push({ text: \" \".repeat(Math.max(1, width - used - marker.length)), role: \"muted\" })\n first.push({ text: marker, role: \"muted\" })\n }\n // The effort is the one piece in colour: it is what changes from one\n // subagent to the next and what costs the most, so it is the one to spot.\n const second: Segment[] = []\n if (row.label) {\n const pieces = truncate(row.label, width - 2).split(\" · \")\n const effortAt = row.effort ? pieces.indexOf(row.effort) : -1\n second.push({ text: \" \", role: \"muted\" })\n pieces.forEach((piece, index) => {\n if (index > 0) second.push({ text: \" · \", role: \"muted\" })\n const role = index !== effortAt ? \"muted\" : finished ? \"effort-faded\" : \"effort\"\n second.push({ text: piece, role })\n })\n }\n return { first, second }\n}\n\n/** The collapsed heading's count: `2 running` or `3 done`. */\nexport function collapsedSummary(rows: SubagentRow[]): string {\n const running = rows.filter((row) => row.state === \"running\").length\n return running > 0 ? `${running} running` : `${rows.length} done`\n}\n","/**\n * What each opencode session's `claude` process REALLY spawned as: model,\n * effort and account. Written by the server half on every turn that reaches\n * the CLI, read by the TUI half's `Subagents` sidebar section (`src/tui.ts`).\n *\n * ## Why a file, and why the TUI cannot read this anywhere else\n *\n * Measured on opencode 1.18.35 (h #g234): an `implementor` subagent whose\n * agent file says `forceModel: claude-sonnet-5-5` and `reasoningEffort: high`\n * spawned as exactly that, while the finished `task` part's `metadata.model`\n * said `claude-code-default/claude-opus-5-5` and no message carried an effort\n * at all. Everything that changes a spawn on the plugin side (an agent's\n * `forceModel` and `reasoningEffort`, `defaultSubagentModel`, the dispatch form\n * of h #g227, the fallback chain of h #g162, an account failover) is invisible\n * to opencode by construction, so showing opencode's own view would be showing\n * the wrong model exactly when the operator configured it not to be. The two\n * halves do not share memory on either major (1.x runs the server half in a\n * worker thread, 2.x in a subprocess), so the record goes through the state\n * directory the resume and dispatch stores already use.\n *\n * ## Shape, and what is never written\n *\n * ```json\n * { \"version\": 1, \"sessions\": {\n * \"ses_abc\": { \"model\": \"claude-sonnet-5-5\", \"effort\": \"high\", \"account\": \"alpha\", \"at\": 1760000000000 } } }\n * ```\n *\n * A model id, an effort level and an account NAME: the same three strings the\n * dispatch store keeps (h #g228), none secret. No prompt, no path, no session\n * key. `account` is written only on a multi-account install, where it tells\n * two configured accounts apart; on a single-account install there is nothing\n * to tell apart and it stays out.\n *\n * ## Cost and failure\n *\n * One write per session per process, and another only when what it spawns as\n * changes: `recordSessionSpawn` compares against what this process last wrote\n * and returns before touching the disk otherwise. Same discipline as the\n * resume store (h #g197): merge-on-write so two opencode processes do not erase\n * each other, atomic rename, `0600` in the `0700` directory, bounded at\n * `MAX_SPAWN_RECORDS` newest sessions and `SPAWN_RECORD_MAX_AGE_MS`. A missing,\n * unreadable or half-written file is an empty store, which costs the sidebar\n * its effort and falls back to the model opencode asked for. Two writers racing\n * can drop one entry; the next turn of that session writes it again, and the\n * only thing it ever costs is a label.\n *\n * Imports nothing that logs, because the TUI bundle reads it on the terminal's\n * own thread (h #g193): every function here is silent and never throws.\n */\nimport { mkdirSync, readFileSync, renameSync, statSync, writeFileSync } from \"node:fs\"\nimport { homedir } from \"node:os\"\nimport { join } from \"node:path\"\n\nexport interface SpawnRecord {\n model?: string\n effort?: string\n account?: string\n at: number\n}\n\n/** Sessions worth remembering at once. A subagent fan-out names a handful. */\nexport const MAX_SPAWN_RECORDS = 256\n\n/** A session not spawned this long ago is not one the sidebar is showing. */\nexport const SPAWN_RECORD_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000\n\nlet pathOverride: string | null = null\nconst lastWritten = new Map<string, string>()\n\nexport function spawnRecordStorePath(): string {\n if (pathOverride) return pathOverride\n const stateRoot = process.env.XDG_STATE_HOME || join(homedir(), \".local\", \"state\")\n return join(stateRoot, \"opencode-claude-code-plugin\", \"session-spawns.json\")\n}\n\nfunction isRecord(value: unknown): value is SpawnRecord {\n const record = value as SpawnRecord | null\n if (!record || typeof record !== \"object\" || Array.isArray(record)) return false\n if (typeof record.at !== \"number\" || !Number.isFinite(record.at)) return false\n const ok = (field: unknown) => field === undefined || typeof field === \"string\"\n if (!ok(record.model) || !ok(record.effort) || !ok(record.account)) return false\n return !!record.model\n}\n\nfunction readEntries(file: string): Map<string, SpawnRecord> {\n const out = new Map<string, SpawnRecord>()\n try {\n const parsed = JSON.parse(readFileSync(file, \"utf8\"))\n const entries = parsed && typeof parsed === \"object\" ? parsed.sessions : null\n if (!entries || typeof entries !== \"object\") return out\n const cutoff = Date.now() - SPAWN_RECORD_MAX_AGE_MS\n const valid = Object.entries(entries as Record<string, unknown>)\n .filter((entry): entry is [string, SpawnRecord] => isRecord(entry[1]))\n .filter(([, record]) => record.at >= cutoff)\n .sort((a, b) => a[1].at - b[1].at)\n for (const [key, record] of valid) {\n out.set(key, {\n model: record.model,\n ...(record.effort ? { effort: record.effort } : {}),\n ...(record.account ? { account: record.account } : {}),\n at: record.at,\n })\n }\n } catch {\n // ENOENT before the first spawn, or a file another process is renaming\n // into place: the empty store is the answer.\n }\n return out\n}\n\n/**\n * Record what `sessionID` spawned as. Returns whether the disk was written,\n * which is false for an unchanged record, an unusable id and a failed write\n * alike: the caller has nothing to do about any of them.\n */\nexport function recordSessionSpawn(\n sessionID: string,\n spawn: { model: string; effort?: string; account?: string },\n now: number = Date.now(),\n): boolean {\n if (!sessionID || sessionID === \"default\" || !spawn.model) return false\n const record: SpawnRecord = {\n model: spawn.model,\n ...(spawn.effort ? { effort: spawn.effort } : {}),\n ...(spawn.account ? { account: spawn.account } : {}),\n at: now,\n }\n const fingerprint = `${record.model}\\n${record.effort ?? \"\"}\\n${record.account ?? \"\"}`\n if (lastWritten.get(sessionID) === fingerprint) return false\n const file = spawnRecordStorePath()\n const merged = readEntries(file)\n merged.delete(sessionID)\n merged.set(sessionID, record)\n const cutoff = now - SPAWN_RECORD_MAX_AGE_MS\n const kept = [...merged.entries()]\n .filter(([, entry]) => entry.at >= cutoff)\n .sort((a, b) => a[1].at - b[1].at)\n .slice(-MAX_SPAWN_RECORDS)\n try {\n mkdirSync(join(file, \"..\"), { recursive: true, mode: 0o700 })\n const tmp = `${file}.${process.pid}.tmp`\n writeFileSync(tmp, JSON.stringify({ version: 1, sessions: Object.fromEntries(kept) }), {\n mode: 0o600,\n })\n renameSync(tmp, file)\n } catch {\n return false\n }\n if (lastWritten.size >= MAX_SPAWN_RECORDS) lastWritten.clear()\n lastWritten.set(sessionID, fingerprint)\n return true\n}\n\n/**\n * A reader that re-parses the file only when it changed. The TUI calls `read()`\n * on every refresh; an unchanged file costs one `stat`.\n */\nexport function createSpawnRecordReader(file: () => string = spawnRecordStorePath) {\n let identity = \"\"\n let cached = new Map<string, SpawnRecord>()\n return {\n read(): Map<string, SpawnRecord> {\n const path = file()\n let next = \"\"\n try {\n const stats = statSync(path)\n next = `${stats.mtimeMs}:${stats.size}:${stats.ino}`\n } catch {\n next = \"missing\"\n }\n if (next !== identity) {\n identity = next\n cached = next === \"missing\" ? new Map() : readEntries(path)\n }\n return cached\n },\n }\n}\n\nexport function _setSpawnRecordStorePathForTests(path: string | null): void {\n pathOverride = path\n lastWritten.clear()\n}\n","/**\n * What each account's usage windows looked like the last time the CLI said,\n * so the operator hears about a window filling up BEFORE it runs out\n * (`usageWarnThresholds`, `usageSwitchAt`, h #g244).\n *\n * ## Where the numbers come from\n *\n * The headless CLI emits `{type:\"rate_limit_event\", rate_limit_info:{status,\n * rateLimitType, resetsAt, utilization, ...}}` whenever its view of the\n * account's limits CHANGES (h #g194). Before this module the plugin kept only\n * a REJECTED one (`rememberAccountLimit`), so the first thing an operator saw\n * was the limit itself. Every event is a reading now, keyed account -> window.\n *\n * Two measured facts shape the record:\n *\n * - `utilization` is a FRACTION, not a percent. Read off the live plugin log\n * on 2026-10-16 and 2026-10-17 (CLI 2.1.293): `allowed_warning` events\n * carrying `0.75` and `0.9`, which the existing NOTICE already rendered as\n * \"75% used\" and \"90% used\". It is stored exactly as the CLI sent it.\n * - An `allowed` event on an account with headroom carries NO utilization at\n * all (`utilization: null` in the CLI's own frame, absent after parsing).\n * That is \"unknown\", never zero: a null reading crosses no threshold and\n * the sidebar shows `ok`, not `0%`.\n *\n * The interactive transport has no event, so the only thing it records is a\n * limit reply: status `rejected`, utilization unknown, and whatever reset the\n * reply's own text names (`limitFactsFromText`, h #g208).\n *\n * ## A window's crossings, and when they clear\n *\n * Each window keeps `noted`, the thresholds (in percent) a `▌ **usage:**` note\n * already announced, so a reused process's next turn, another session on the\n * same account and a restarted opencode all stay quiet about a crossing that\n * was already said. `noted` carries over to a new reading for as long as the\n * PREVIOUS reading is still current, and is cleared once its reset has passed:\n * the reset is what ends a window, and comparing two `resetsAt` values for\n * equality would re-announce a crossing every time the CLI rounded the reset\n * differently. A reading with no reset time (the interactive transport's) is\n * current for `UNTIMED_READING_TTL_MS`.\n *\n * ## File and failure\n *\n * `$XDG_STATE_HOME/opencode-claude-code-plugin/account-usage.json`, the fifth\n * file the plugin writes there, with the same discipline as the resume and\n * spawn stores (h #g197, h #g234): merge-on-write so two opencode processes do\n * not erase each other, atomic rename, `0600` in a `0700` directory, bounded,\n * and an unreadable or half-written file is an empty store. It holds account\n * NAMES, window names, numbers and times, and nothing else.\n *\n * Imports nothing that logs, because the TUI bundle reads it on the terminal's\n * own thread (h #g193): every function here is silent and never throws.\n */\nimport { mkdirSync, readFileSync, renameSync, statSync, writeFileSync } from \"node:fs\"\nimport { homedir } from \"node:os\"\nimport { join } from \"node:path\"\n\n/** One reading of one window, as the CLI reported it. */\nexport interface UsageReading {\n /** The CLI's fraction (0.75 is 75%), or null when the event carried none. */\n utilization: number | null\n /** Epoch ms the window resets at, when the CLI said. */\n resetsAt?: number\n /**\n * The TUI's own reset phrase (`3pm (Europe/Amsterdam)`), for a limit that\n * reached us only as text. Shown only when `resetsAt` is unknown.\n */\n resetsText?: string\n /** `allowed`, `allowed_warning` or `rejected`, as the CLI sent it. */\n status: string\n}\n\nexport interface WindowUsage extends UsageReading {\n /** Epoch ms this reading was recorded. */\n observedAt: number\n /** Thresholds, in percent, a note already announced in this window. */\n noted: number[]\n}\n\n/** account -> window (the CLI's `rateLimitType`) -> reading. */\nexport type AccountUsage = Map<string, Map<string, WindowUsage>>\n\nexport const DEFAULT_USAGE_WARN_THRESHOLDS: readonly number[] = [50, 75, 90, 95]\nexport const DEFAULT_USAGE_SWITCH_AT = 90\n\n/**\n * How long a reading with no reset time stays current. The only such reading\n * is the interactive transport's limit reply, and the window it names is the\n * five-hour one (`limitFactsFromText`), so five hours is the longest that\n * reading could still be true.\n */\nexport const UNTIMED_READING_TTL_MS = 5 * 60 * 60 * 1000\n\n/** A reading not refreshed in this long is dropped: past every window's reset. */\nexport const USAGE_READING_MAX_AGE_MS = 8 * 24 * 60 * 60 * 1000\n\nexport const MAX_USAGE_ACCOUNTS = 32\nexport const MAX_USAGE_WINDOWS = 8\n\nconst NAME = /^[a-z0-9_-]{1,64}$/\nconst STATUS = /^[a-z_]{1,32}$/\nconst MAX_RESETS_TEXT = 80\n\nlet pathOverride: string | null = null\nlet cache: { path: string; usage: AccountUsage } | null = null\n\nexport function accountUsageStorePath(): string {\n if (pathOverride) return pathOverride\n const stateRoot = process.env.XDG_STATE_HOME || join(homedir(), \".local\", \"state\")\n return join(stateRoot, \"opencode-claude-code-plugin\", \"account-usage.json\")\n}\n\nfunction finite(value: unknown): value is number {\n return typeof value === \"number\" && Number.isFinite(value)\n}\n\nfunction validEntry(value: unknown): WindowUsage | null {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return null\n const entry = value as Record<string, unknown>\n if (!finite(entry.observedAt)) return null\n if (typeof entry.status !== \"string\" || !STATUS.test(entry.status)) return null\n if (entry.utilization !== null && !finite(entry.utilization)) return null\n if (entry.resetsAt !== undefined && !finite(entry.resetsAt)) return null\n if (\n entry.resetsText !== undefined &&\n (typeof entry.resetsText !== \"string\" || entry.resetsText.length > MAX_RESETS_TEXT)\n ) {\n return null\n }\n const noted = Array.isArray(entry.noted)\n ? [...new Set(entry.noted.filter((t): t is number => finite(t) && t > 0 && t <= 100))].sort(\n (a, b) => a - b,\n )\n : []\n return {\n utilization: entry.utilization as number | null,\n ...(entry.resetsAt === undefined ? {} : { resetsAt: entry.resetsAt as number }),\n ...(entry.resetsText === undefined ? {} : { resetsText: entry.resetsText as string }),\n status: entry.status,\n observedAt: entry.observedAt,\n noted,\n }\n}\n\nfunction readStore(file: string, now: number): AccountUsage {\n const out: AccountUsage = new Map()\n try {\n const parsed = JSON.parse(readFileSync(file, \"utf8\"))\n const accounts = parsed && typeof parsed === \"object\" ? parsed.accounts : null\n if (!accounts || typeof accounts !== \"object\" || Array.isArray(accounts)) return out\n const cutoff = now - USAGE_READING_MAX_AGE_MS\n for (const [account, windows] of Object.entries(accounts as Record<string, unknown>)) {\n if (!NAME.test(account) || !windows || typeof windows !== \"object\") continue\n const kept = new Map<string, WindowUsage>()\n for (const [window, raw] of Object.entries(windows as Record<string, unknown>)) {\n if (!NAME.test(window)) continue\n const entry = validEntry(raw)\n if (entry && entry.observedAt >= cutoff) kept.set(window, entry)\n }\n if (kept.size > 0) out.set(account, kept)\n }\n } catch {\n // ENOENT before the first event, or a file another process is renaming\n // into place: the empty store is the answer.\n }\n return out\n}\n\nfunction newestObserved(windows: Map<string, WindowUsage>): number {\n let newest = 0\n for (const entry of windows.values()) newest = Math.max(newest, entry.observedAt)\n return newest\n}\n\nfunction bounded(usage: AccountUsage, now: number): AccountUsage {\n const cutoff = now - USAGE_READING_MAX_AGE_MS\n const accounts = [...usage.entries()]\n .map(([account, windows]) => {\n const kept = [...windows.entries()]\n .filter(([, entry]) => entry.observedAt >= cutoff)\n .sort((a, b) => a[1].observedAt - b[1].observedAt)\n .slice(-MAX_USAGE_WINDOWS)\n return [account, new Map(kept)] as const\n })\n .filter(([, windows]) => windows.size > 0)\n .sort((a, b) => newestObserved(a[1]) - newestObserved(b[1]))\n .slice(-MAX_USAGE_ACCOUNTS)\n return new Map(accounts)\n}\n\nfunction writeStore(file: string, usage: AccountUsage): boolean {\n const accounts: Record<string, Record<string, WindowUsage>> = {}\n for (const [account, windows] of usage) accounts[account] = Object.fromEntries(windows)\n try {\n mkdirSync(join(file, \"..\"), { recursive: true, mode: 0o700 })\n const tmp = `${file}.${process.pid}.tmp`\n writeFileSync(tmp, JSON.stringify({ version: 1, accounts }), { mode: 0o600 })\n renameSync(tmp, file)\n return true\n } catch {\n return false\n }\n}\n\n/** This process's view, loaded from disk once and kept current by its own writes. */\nfunction cached(now: number): AccountUsage {\n const file = accountUsageStorePath()\n if (!cache || cache.path !== file) cache = { path: file, usage: readStore(file, now) }\n return cache.usage\n}\n\n/** Re-read the file into the cache, for a decision another process may have made. */\nfunction refreshed(now: number): AccountUsage {\n const file = accountUsageStorePath()\n cache = { path: file, usage: readStore(file, now) }\n return cache.usage\n}\n\n/**\n * Whether a reading still describes the window it was taken in: its reset is\n * ahead, or, with no reset time, it is younger than `UNTIMED_READING_TTL_MS`.\n */\nexport function isReadingCurrent(entry: WindowUsage, now: number = Date.now()): boolean {\n if (entry.resetsAt !== undefined) return entry.resetsAt > now\n return now - entry.observedAt < UNTIMED_READING_TTL_MS\n}\n\n/**\n * The record a new reading leaves behind. The thresholds already announced\n * carry over only while the previous reading is still current; once its reset\n * has passed, the window has turned over and every crossing may be said again.\n */\nexport function applyReading(\n previous: WindowUsage | undefined,\n reading: UsageReading,\n now: number = Date.now(),\n): WindowUsage {\n const carried = previous && isReadingCurrent(previous, now) ? previous.noted : []\n return {\n utilization: finite(reading.utilization) ? reading.utilization : null,\n ...(finite(reading.resetsAt) ? { resetsAt: reading.resetsAt } : {}),\n ...(reading.resetsText && !finite(reading.resetsAt)\n ? { resetsText: reading.resetsText.slice(0, MAX_RESETS_TEXT) }\n : {}),\n status: STATUS.test(reading.status) ? reading.status : \"unknown\",\n observedAt: now,\n noted: [...carried],\n }\n}\n\n/** The CLI's fraction as a percent, to one decimal so `0.9` is exactly 90. */\nexport function usagePercent(utilization: number): number {\n return Math.round(utilization * 1000) / 10\n}\n\n/**\n * The thresholds this reading crosses that no note announced yet. None for a\n * reading that is no longer current, one whose utilization is unknown, and a\n * rejection: a window that ran out gets the `▌ **usage limit:**` note instead.\n */\nexport function pendingCrossings(\n entry: WindowUsage,\n thresholds: readonly number[],\n now: number = Date.now(),\n): number[] {\n if (!isReadingCurrent(entry, now)) return []\n if (entry.utilization === null || entry.status === \"rejected\") return []\n const percent = usagePercent(entry.utilization)\n return [...new Set(thresholds)]\n .filter((threshold) => percent >= threshold && !entry.noted.includes(threshold))\n .sort((a, b) => a - b)\n}\n\n/**\n * `usageWarnThresholds`: percents above 0 and up to 100, deduplicated and\n * sorted. Unset is the default list; an explicit empty list turns the notes off.\n */\nexport function resolveUsageWarnThresholds(value: unknown): number[] {\n if (!Array.isArray(value)) return [...DEFAULT_USAGE_WARN_THRESHOLDS]\n return [...new Set(value.filter((t): t is number => finite(t) && t > 0 && t <= 100))].sort(\n (a, b) => a - b,\n )\n}\n\n/**\n * `usageSwitchAt`: a percent above 0 and up to 100, or null for \"never\". `false`\n * and `0` turn it off; anything unusable is the default.\n */\nexport function resolveUsageSwitchAt(value: unknown): number | null {\n if (value === false || value === 0) return null\n if (finite(value) && value > 0 && value <= 100) return value\n return DEFAULT_USAGE_SWITCH_AT\n}\n\n/**\n * Record one reading. Returns the record left behind, or null when the account\n * or window name is unusable or the write failed (the in-process view still\n * takes the reading, so this turn's note is not lost to a read-only disk).\n */\nexport function recordUsageReading(\n account: string,\n window: string,\n reading: UsageReading,\n now: number = Date.now(),\n): WindowUsage | null {\n if (!NAME.test(account) || !NAME.test(window)) return null\n const file = accountUsageStorePath()\n const merged = readStore(file, now)\n const previous = merged.get(account)?.get(window) ?? cached(now).get(account)?.get(window)\n const entry = applyReading(previous, reading, now)\n const windows = merged.get(account) ?? new Map<string, WindowUsage>()\n windows.set(window, entry)\n merged.set(account, windows)\n const kept = bounded(merged, now)\n cache = { path: file, usage: kept }\n return writeStore(file, kept) ? entry : null\n}\n\n/** Mark thresholds announced, on the window's newest reading. */\nexport function markUsageNoted(\n account: string,\n window: string,\n thresholds: readonly number[],\n now: number = Date.now(),\n): boolean {\n if (!NAME.test(account) || !NAME.test(window) || thresholds.length === 0) return false\n const file = accountUsageStorePath()\n const merged = readStore(file, now)\n const entry = merged.get(account)?.get(window) ?? cached(now).get(account)?.get(window)\n if (!entry) return false\n const updated = {\n ...entry,\n noted: [...new Set([...entry.noted, ...thresholds])].sort((a, b) => a - b),\n }\n const windows = merged.get(account) ?? new Map<string, WindowUsage>()\n windows.set(window, updated)\n merged.set(account, windows)\n const kept = bounded(merged, now)\n cache = { path: file, usage: kept }\n return writeStore(file, kept)\n}\n\nexport interface UsageCrossing {\n window: string\n entry: WindowUsage\n /** Newly crossed thresholds, in percent, ascending. */\n crossed: number[]\n}\n\n/**\n * Every window of `account` with a threshold to announce. Answered from this\n * process's view, which costs no I/O on an ordinary turn; only when something\n * looks pending is the file re-read, so a crossing another opencode process\n * already announced is not announced twice.\n */\nexport function usageCrossings(\n account: string,\n thresholds: readonly number[],\n now: number = Date.now(),\n): UsageCrossing[] {\n if (thresholds.length === 0) return []\n const collect = (usage: AccountUsage): UsageCrossing[] => {\n const out: UsageCrossing[] = []\n for (const [window, entry] of usage.get(account) ?? []) {\n const crossed = pendingCrossings(entry, thresholds, now)\n if (crossed.length > 0) out.push({ window, entry, crossed })\n }\n return out\n }\n if (collect(cached(now)).length === 0) return []\n return collect(refreshed(now))\n}\n\n/**\n * Whether any current window of `account` is at or over `percent`, or was\n * refused outright. What a switch at a threshold reads, so it never moves new\n * turns onto an account that is no better off.\n */\nexport function accountUsageAtOrOver(\n account: string,\n percent: number,\n now: number = Date.now(),\n): boolean {\n for (const entry of cached(now).get(account)?.values() ?? []) {\n if (!isReadingCurrent(entry, now)) continue\n if (entry.status === \"rejected\") return true\n if (entry.utilization !== null && usagePercent(entry.utilization) >= percent) return true\n }\n return false\n}\n\n/** Every recorded reading, read fresh off disk. For `/claude-code-doctor`. */\nexport function snapshotAccountUsage(now: number = Date.now()): AccountUsage {\n return refreshed(now)\n}\n\n/**\n * A reader that re-parses the file only when it changed, for the TUI's\n * refresh clock: an unchanged file costs one `stat`.\n */\nexport function createAccountUsageReader(file: () => string = accountUsageStorePath) {\n let identity = \"\"\n let usage: AccountUsage = new Map()\n return {\n read(): AccountUsage {\n const path = file()\n let next = \"\"\n try {\n const stats = statSync(path)\n next = `${stats.mtimeMs}:${stats.size}:${stats.ino}`\n } catch {\n next = \"missing\"\n }\n if (next !== identity) {\n identity = next\n usage = next === \"missing\" ? new Map() : readStore(path, Date.now())\n }\n return usage\n },\n }\n}\n\n/** Test-only. Points the store at another file and forgets the cache. */\nexport function _setAccountUsageStorePathForTests(path: string | null): void {\n pathOverride = path\n cache = null\n}\n","/**\n * The model behind the sidebar's `Accounts` section (`src/tui.ts`): one line\n * per account with a current usage reading, `appical · 5h 72% · resets 18:00`\n * (h #g244).\n *\n * Pure, like `src/subagent-sidebar.ts`, and for the same reason: the TUI bundle\n * runs on the terminal's own thread, where a logger writes over the interface\n * (h #g193). It imports only the store's reader types and that module's pure\n * helpers.\n *\n * Which window an account's line shows: a refused one first (the account is\n * out now, whatever its other windows say), else the fullest known one, else\n * any current reading (`ok`, a window the CLI reported without a utilization,\n * which is how it reports one with headroom). A reading whose reset has passed\n * describes a window that is over and is not shown at all.\n */\nimport { isReadingCurrent, usagePercent, type AccountUsage, type WindowUsage } from \"./account-usage.js\"\nimport { SIDEBAR_ROW_WIDTH, truncate, type Segment } from \"./subagent-sidebar.js\"\n\nexport interface AccountUsageLine {\n account: string\n window: string\n /** `limited` (refused), `warning` (at or past `WARNING_PERCENT`), or `ok`. */\n state: \"limited\" | \"warning\" | \"ok\"\n segments: Segment[]\n}\n\n/** Where a line takes the theme's warning colour: the CLI's own first warning level. */\nexport const WARNING_PERCENT = 75\n\nconst SHORT_WINDOWS: Record<string, string> = {\n five_hour: \"5h\",\n seven_day: \"7d\",\n seven_day_opus: \"7d opus\",\n seven_day_sonnet: \"7d sonnet\",\n seven_day_overage_included: \"7d extra\",\n overage: \"extra\",\n}\n\n/** `five_hour` -> `5h`; an unknown window name is shown as itself, `unknown` as nothing. */\nexport function shortWindowName(window: string): string {\n if (window === \"unknown\") return \"\"\n return SHORT_WINDOWS[window] ?? window.replace(/_/g, \" \")\n}\n\nconst MONTHS = [\"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\", \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"]\n\n/**\n * A reset time in local time, as short as it can be unambiguous: `18:00` today,\n * `Oct 21 12:00` on another day.\n */\nexport function formatResetClock(resetsAt: number, now: number): string {\n const when = new Date(resetsAt)\n const today = new Date(now)\n const pad = (value: number) => String(value).padStart(2, \"0\")\n const clock = `${pad(when.getHours())}:${pad(when.getMinutes())}`\n const sameDay =\n when.getFullYear() === today.getFullYear() &&\n when.getMonth() === today.getMonth() &&\n when.getDate() === today.getDate()\n return sameDay ? clock : `${MONTHS[when.getMonth()]} ${when.getDate()} ${clock}`\n}\n\nfunction rank(entry: WindowUsage): number {\n if (entry.status === \"rejected\") return 1000\n if (entry.utilization === null) return -1\n return usagePercent(entry.utilization)\n}\n\n/** The window an account's line is about, or undefined when none is current. */\nexport function pressingWindow(\n windows: ReadonlyMap<string, WindowUsage>,\n now: number,\n): [string, WindowUsage] | undefined {\n let best: [string, WindowUsage] | undefined\n for (const [window, entry] of windows) {\n if (!isReadingCurrent(entry, now)) continue\n if (!best || rank(entry) > rank(best[1])) best = [window, entry]\n }\n return best\n}\n\n/** Every account with a current reading, alphabetically, each laid out for `width`. */\nexport function accountUsageLines(\n usage: AccountUsage,\n now: number,\n width: number = SIDEBAR_ROW_WIDTH,\n): AccountUsageLine[] {\n const lines: AccountUsageLine[] = []\n for (const account of [...usage.keys()].sort()) {\n const pressing = pressingWindow(usage.get(account)!, now)\n if (!pressing) continue\n const [window, entry] = pressing\n const state: AccountUsageLine[\"state\"] =\n entry.status === \"rejected\"\n ? \"limited\"\n : entry.utilization !== null && usagePercent(entry.utilization) >= WARNING_PERCENT\n ? \"warning\"\n : \"ok\"\n const short = shortWindowName(window)\n const level =\n state === \"limited\"\n ? \"limited\"\n : entry.utilization === null\n ? `${short ? `${short} ` : \"\"}ok`\n : `${short ? `${short} ` : \"\"}${Math.round(usagePercent(entry.utilization))}%`\n const resets =\n entry.resetsAt !== undefined\n ? ` · resets ${formatResetClock(entry.resetsAt, now)}`\n : entry.resetsText\n ? ` · resets ${entry.resetsText}`\n : \"\"\n const name = truncate(account, Math.max(1, Math.floor(width / 2)))\n // `truncate` trims, so the space after the name is added outside it.\n const rest = ` ${truncate(`· ${level}${resets}`, Math.max(0, width - Array.from(name).length - 1))}`\n lines.push({\n account,\n window,\n state,\n segments: [\n { text: name, role: \"agent\" },\n { text: rest, role: state === \"limited\" ? \"error\" : state === \"warning\" ? \"effort\" : \"muted\" },\n ],\n })\n }\n return lines\n}\n\n/** The plain text of a line, for tests and the collapsed summary. */\nexport function lineText(line: AccountUsageLine): string {\n return line.segments.map((segment) => segment.text).join(\"\")\n}\n"],"mappings":"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","names":["text","mkdirSync","readFileSync","renameSync","statSync","writeFileSync","homedir","join","pathOverride","pathOverride","join","homedir","readFileSync","statSync","text"]}
1
+ {"version":3,"sources":["../src/tui.ts","../src/subagent-sidebar.ts","../src/progress-line.ts","../src/spawn-record-store.ts","../src/turn-history.ts","../src/account-usage.ts","../src/account-sidebar.ts"],"sourcesContent":["/**\n * The TUI half of the package: a `Subagents` section at the bottom of\n * opencode's session sidebar listing the current session's subagents, running\n * first, each with a spinner (or ✓ / ✗), its agent type, its task description,\n * a `bg` marker for background ones, a muted line saying what it really runs\n * as with its spend so far (`sonnet-5.5 · high · alpha · $0.42`), and\n * while it runs a progress line (`▰▰▰▱▱▱▱▱▱▱ 45s/~2m10s · read`).\n * Clicking a row opens that subagent's session; opencode's own `up` key goes\n * back to the parent.\n *\n * Above it, under a `Main Progress` heading, the same line for the session on\n * screen while its turn runs and for a moment after it ends (h #g246). The\n * bar is a todo list's fraction when the session has one, else the turn's\n * elapsed over the total its agent and model's own recorded turns predict\n * (`src/turn-history.ts`, h #g247), else nothing; the rest is elapsed, calls\n * and the running tool. `src/progress-line.ts` decides all of it.\n *\n * Below it, in the same slot, an `Accounts` section: one line per account\n * this install runs, from the first draw (h #g248): its current usage reading\n * (`appical · 5h 72% · resets 18:00`) from the store the server half writes\n * on every `rate_limit_event`, or its plan tier and `no reading yet` until\n * one arrives (`src/account-usage.ts`, laid out by `src/account-sidebar.ts`,\n * h #g244). It shares the slot rather than claiming a second one, so neither\n * major has a second registration to accept.\n *\n * Always on: there is no option. One module serves both majors (h #g234):\n *\n * - opencode 1.x loads TUI plugins from `tui.json`'s `plugin` list, never\n * from `opencode.json`'s, resolves a package to `exports[\"./tui\"]`, and\n * wants a default export `{ id, tui }` with no `server`.\n * - opencode 2.x resolves the SAME `plugins` entry it already has\n * (`<repo>/dist`) to `<dir>/tui`, and wants `{ id, setup }`. 1.x ignores a\n * `setup` key and 2.x a `tui` key, so the default export carries both.\n *\n * Everything that decides WHAT to show lives in `src/subagent-sidebar.ts`\n * and `src/progress-line.ts` (pure, tested). Each major gets a thin adapter\n * that gathers those modules' inputs from its own TUI state and supplies its\n * own theme colours; the sections themselves are drawn by one renderer.\n * Nothing in here may throw into the host or log: on the TUI's own thread\n * stderr is the screen (h #g193), so every failure degrades to drawing less.\n */\nimport { RGBA } from \"@opentui/core\"\nimport { createElement, createTextNode, effect, insert, insertNode, setProp } from \"@opentui/solid\"\nimport { createMemo, createSignal, onCleanup } from \"solid-js\"\nimport {\n SIDEBAR_ROW_WIDTH,\n SPINNER_FRAMES,\n collapsedSummary,\n completionsIn,\n deriveSubagentRows,\n layoutRow,\n partChildID,\n type ChildRunState,\n type ChildSessionInput,\n type CompletionInput,\n type Segment,\n type SidebarInput,\n type SubagentRow,\n type TaskPartInput,\n} from \"./subagent-sidebar.js\"\nimport {\n createFillMemory,\n createStatusWatch,\n layoutProgressLine,\n measureProgress,\n rowProgress,\n sessionProgress,\n turnMessages,\n type ProgressMeasure,\n type ProgressMessage,\n type ProgressSource,\n type ProgressToolCall,\n type ProgressView,\n} from \"./progress-line.js\"\nimport { createSpawnRecordReader, type SpawnRecord } from \"./spawn-record-store.js\"\nimport { createTurnHistoryReader, estimateTurn, promptChars, turnHistoryKey, type TurnHistory } from \"./turn-history.js\"\nimport { createAccountUsageReader, type AccountUsage, type KnownAccounts } from \"./account-usage.js\"\nimport { accountUsageLines, type AccountUsageLine } from \"./account-sidebar.js\"\n\nexport const SUBAGENTS_TUI_PLUGIN_ID = \"opencode-claude-code-plugin.subagents\"\n\n/** After opencode 1.x's own sidebar sections (files is 500). */\nconst SECTION_ORDER = 900\nconst SPINNER_INTERVAL_MS = 100\n/** How often the spawn records and the finished rows' fade are rechecked. */\nconst REFRESH_INTERVAL_MS = 1500\n/** The refresh while something runs: the progress lines' elapsed moves by the second. */\nconst PROGRESS_TICK_MS = 1000\n/** The spinner counter wraps here, a multiple of the spinner's frame count, so it never skips at the wrap. */\nconst FRAME_WRAP = SPINNER_FRAMES.length * 600\n\n/** The tool names a subagent dispatch has: `task` on 1.x, `subagent` on 2.x. */\nconst TASK_TOOLS = new Set([\"task\", \"subagent\"])\n\ntype Color = RGBA\n\n/** The colours the section paints with, whatever theme shape the host has. */\nexport interface SidebarColors {\n text: Color\n muted: Color\n spinner: Color\n done: Color\n error: Color\n /** The effort on a row's second line: the theme's warning colour. */\n effort: Color\n effortFaded: Color\n hover: Color\n /** A bar's filled cells: the theme's success colour. */\n barDone: Color\n /** The track still to go: the muted colour pulled toward the background. */\n barTrack: Color\n /** `2/4`: the text colour, drawn bold; an estimate's `1m02s/~2m30s` takes it unbolded. */\n fraction: Color\n /** The tool running now: the theme's info colour. */\n tool: Color\n /** A subagent's spend so far: the theme's secondary colour. */\n cost: Color\n /** An account's plan tier, on a line with no reading yet: the secondary colour, like the spend. */\n tier: Color\n /**\n * When an account's window resets: the info colour, like the running tool,\n * so the time reads apart from the level beside it (h #g248).\n */\n reset: Color\n}\n\n/**\n * Which colour each segment role takes. A `Record` over the role union, so a\n * role added to `Segment` without a colour here fails the typecheck rather\n * than drawing muted by accident.\n */\nconst ROLE_COLOR: Record<Segment[\"role\"], keyof SidebarColors> = {\n spinner: \"spinner\",\n done: \"done\",\n error: \"error\",\n agent: \"text\",\n description: \"text\",\n muted: \"muted\",\n effort: \"effort\",\n \"effort-faded\": \"effortFaded\",\n \"bar-done\": \"barDone\",\n // Past every recorded turn: the warning colour, which the effort already takes.\n \"bar-over\": \"effort\",\n \"bar-track\": \"barTrack\",\n fraction: \"fraction\",\n estimate: \"fraction\",\n \"estimate-over\": \"effort\",\n tool: \"tool\",\n cost: \"cost\",\n tier: \"tier\",\n reset: \"reset\",\n}\n\n/** What one major has to supply; the renderer below does everything else. */\ninterface SidebarHost {\n colors(): SidebarColors\n /** The inputs for one parent session, minus the clock and the records. */\n gather(parentID: string): Pick<SidebarInput, \"children\" | \"parts\" | \"completions\" | \"status\" | \"stoppedAt\" | \"cost\">\n /** A session's own messages and todo list, what its progress line is measured from. */\n progress(sessionID: string): ProgressSource\n navigate(sessionID: string): void\n}\n\nfunction attempt<T>(fn: () => T, fallback: T): T {\n try {\n return fn()\n } catch {\n return fallback\n }\n}\n\n/**\n * `a` moved `weight` of the way to `b`, so a finished glyph reads as muted.\n * Works in whatever scale the host's RGBA uses, because it only ever hands\n * the class back values it read from the same class.\n */\nfunction mix(a: Color, b: Color, weight: number): Color {\n return attempt(\n () =>\n RGBA.fromValues(\n a.r + (b.r - a.r) * weight,\n a.g + (b.g - a.g) * weight,\n a.b + (b.b - a.b) * weight,\n a.a,\n ),\n a,\n )\n}\n\nfunction roleColor(colors: SidebarColors, role: Segment[\"role\"]): Color {\n return colors[ROLE_COLOR[role] ?? \"muted\"]\n}\n\nconst EMPTY_SOURCE: ProgressSource = { messages: [], todos: [] }\n\n/** What one sidebar shows for its session: the rows, each running row's line, and the session's own. */\ninterface SidebarView {\n rows: SubagentRow[]\n lines: Map<string, ProgressView>\n progress?: ProgressView\n}\n\nconst EMPTY_VIEW: SidebarView = { rows: [], lines: new Map() }\n\n/**\n * The sections, their clocks and the store readers, for one host. Returns\n * the slot renderer and a disposer.\n */\nfunction createSidebar(host: SidebarHost) {\n const [frame, setFrame] = createSignal(0)\n const [now, setNow] = createSignal(Date.now())\n const records = createSpawnRecordReader()\n let recordMap: Map<string, SpawnRecord> = attempt(() => records.read(), new Map())\n // How long turns of each agent and model took before (h #g247), what the\n // bar is drawn from when the session has no todo list. The server half\n // writes it at every served `result`.\n const history = createTurnHistoryReader()\n let historyMap: TurnHistory = attempt(() => history.read(), new Map())\n const [recordVersion, setRecordVersion] = createSignal(0)\n // What the TUI itself remembers about each session's status (h #g246): when\n // it first saw a run and when it saw the stop, for the one line whose turn\n // no message dates and for the linger after a turn.\n const watch = createStatusWatch()\n // And the fill each bar has reached this turn, so an estimate's bar never\n // steps back while its total rises.\n const fills = createFillMemory()\n // The `Accounts` lines (h #g244): every account's last usage reading, from\n // the store the server half writes on each `rate_limit_event`, and the\n // accounts it registered there at startup (h #g248). Their own clock value,\n // so a tick that only rechecks the readings does not re-derive the subagent\n // rows, which walk the session's messages.\n const usageReader = createAccountUsageReader()\n const [usage, setUsage] = createSignal<AccountUsage>(attempt(() => usageReader.read(), new Map()))\n const [known, setKnown] = createSignal<KnownAccounts>(attempt(() => usageReader.known(), new Map()))\n const [usageNow, setUsageNow] = createSignal(Date.now())\n\n // One spinner clock and one refresh clock for every mounted section, each\n // running only while something needs it: nothing ticks while no subagent is\n // listed, and the spinner stops the moment the last one finishes.\n const sectionsRunning = new Set<symbol>()\n const sectionsShowing = new Set<symbol>()\n // Every mounted sidebar, whatever it shows: a usage reading arrives with no\n // event the TUI can see, so the one `stat` that finds it runs while a\n // sidebar is on screen, and nothing runs once none is.\n const sectionsMounted = new Set<symbol>()\n let spinner: ReturnType<typeof setInterval> | undefined\n let refresh: ReturnType<typeof setInterval> | undefined\n let refreshPeriod = 0\n const refreshTick = () => {\n if (sectionsShowing.size > 0) {\n const next = attempt(() => records.read(), recordMap)\n const nextHistory = attempt(() => history.read(), historyMap)\n if (next !== recordMap || nextHistory !== historyMap) {\n recordMap = next\n historyMap = nextHistory\n setRecordVersion((value) => value + 1)\n }\n setNow(Date.now())\n }\n // The reader hands back the same map while the file is unchanged, and\n // the clock moves once a minute (a reset is shown to the minute), so\n // an idle tick re-lays nothing out.\n setUsage(attempt(() => usageReader.read(), usage()))\n setKnown(attempt(() => usageReader.known(), known()))\n const minute = Math.floor(Date.now() / 60_000) * 60_000\n if (minute !== usageNow()) setUsageNow(minute)\n }\n const syncClocks = () => {\n if (sectionsRunning.size > 0 && !spinner) {\n spinner = setInterval(() => setFrame((value) => (value + 1) % FRAME_WRAP), SPINNER_INTERVAL_MS)\n } else if (sectionsRunning.size === 0 && spinner) {\n clearInterval(spinner)\n spinner = undefined\n }\n const wanted = sectionsShowing.size > 0 || sectionsMounted.size > 0\n // While something runs the refresh is also the progress lines' second\n // hand; otherwise it is the slower recheck of the records and the fades.\n const period = sectionsRunning.size > 0 ? PROGRESS_TICK_MS : REFRESH_INTERVAL_MS\n if (wanted && (!refresh || refreshPeriod !== period)) {\n if (refresh) clearInterval(refresh)\n refreshPeriod = period\n refresh = setInterval(refreshTick, period)\n } else if (!wanted && refresh) {\n clearInterval(refresh)\n refresh = undefined\n refreshPeriod = 0\n }\n }\n\n const progressOf = (sessionID: string): ProgressSource =>\n attempt(() => host.progress(sessionID), EMPTY_SOURCE)\n\n /**\n * A running line's estimate (h #g247): the session's recorded agent and\n * model name the history, the turn's prompt length and the effort weight\n * it, and the fill is held so the bar only advances within the turn. No\n * spawn record, no history or too little of it is no estimate, and the\n * line then draws no bar.\n */\n const estimateFor = (\n sessionID: string,\n measure: ProgressMeasure,\n view: ProgressView,\n agentFallback?: string,\n ): ProgressView => {\n if (view.state !== \"running\") return view\n const record = recordMap.get(sessionID)\n if (!record?.model) return view\n const samples = historyMap.get(turnHistoryKey(record.agent ?? agentFallback, record.model))\n if (!samples) return view\n const estimate = estimateTurn(samples, { elapsedMs: view.elapsedMs, chars: measure.chars, effort: record.effort })\n if (!estimate) return view\n const turn = measure.startedAt ?? 0\n return { ...view, estimate: { ...estimate, fill: fills.hold(sessionID, turn, estimate.fill) } }\n }\n\n const deriveView = (sessionID: string): SidebarView => {\n recordVersion()\n // The clock signal is what re-runs this every second; the time itself is\n // read fresh, because a change in the host's store re-runs it between\n // ticks too, when the signal can be a tick stale.\n now()\n const clock = Date.now()\n const gathered = host.gather(sessionID)\n const rows = deriveSubagentRows({\n ...gathered,\n record: (id) => recordMap.get(id),\n now: clock,\n })\n const lines = new Map<string, ProgressView>()\n for (const row of rows) {\n if (row.state !== \"running\" || !row.sessionID) continue\n const measure = measureProgress(progressOf(row.sessionID))\n const line = rowProgress(measure, { started: row.started, now: clock })\n lines.set(row.sessionID, estimateFor(row.sessionID, measure, line, row.agent))\n }\n const status = gathered.status(sessionID)\n const memory = watch.observe(sessionID, status, clock)\n const measure = measureProgress(progressOf(sessionID))\n const session = sessionProgress(measure, {\n status,\n busySince: memory.busySince,\n stoppedAt: gathered.stoppedAt(sessionID) ?? memory.stoppedAt,\n now: clock,\n })\n const progress = session ? estimateFor(sessionID, measure, session) : undefined\n return { rows, lines, progress }\n }\n\n const styled = (segment: Segment): OpentuiNode => {\n // The fraction is the one bold piece; `b` is a text chunk like `span`.\n const span = createElement(segment.role === \"fraction\" ? \"b\" : \"span\")\n insertNode(span, createTextNode(segment.text))\n effect(() => setProp(span, \"style\", { fg: roleColor(host.colors(), segment.role) }))\n return span\n }\n\n /** One progress line; only the spinner glyph follows the spinner clock, the bar moves with the second hand. */\n const buildProgressLine = (view: ProgressView, lead: \"glyph\" | \"indent\"): OpentuiNode => {\n const line = createElement(\"text\")\n setProp(line, \"wrapMode\", \"none\")\n const layout = layoutProgressLine(view, lead, SIDEBAR_ROW_WIDTH)\n layout.lead.forEach((segment, index) => {\n if (index === 0 && lead === \"glyph\" && view.state === \"running\") {\n const span = createElement(\"span\")\n insert(span, () => SPINNER_FRAMES[frame() % SPINNER_FRAMES.length])\n effect(() => setProp(span, \"style\", { fg: roleColor(host.colors(), segment.role) }))\n insertNode(line, span)\n } else {\n insertNode(line, styled(segment))\n }\n })\n for (const segment of layout.bar) insertNode(line, styled(segment))\n for (const segment of layout.tail) insertNode(line, styled(segment))\n return line\n }\n\n const buildRow = (row: SubagentRow, progress: ProgressView | undefined): OpentuiNode => {\n const box = createElement(\"box\")\n const layout = layoutRow(row, SIDEBAR_ROW_WIDTH)\n const first = createElement(\"text\")\n setProp(first, \"wrapMode\", \"none\")\n layout.first.forEach((segment, index) => {\n const span = createElement(\"span\")\n if (index === 0 && row.state === \"running\") {\n insert(span, () => SPINNER_FRAMES[frame() % SPINNER_FRAMES.length])\n } else {\n insertNode(span, createTextNode(segment.text))\n }\n effect(() => setProp(span, \"style\", { fg: roleColor(host.colors(), segment.role) }))\n insertNode(first, span)\n })\n insertNode(box, first)\n if (layout.second.length > 0) {\n // One line of spans, like the first, so the effort and the spend can\n // take their own colours.\n const second = createElement(\"text\")\n setProp(second, \"wrapMode\", \"none\")\n for (const segment of layout.second) insertNode(second, styled(segment))\n insertNode(box, second)\n }\n if (progress) insertNode(box, attempt(() => buildProgressLine(progress, \"indent\"), createElement(\"box\")))\n if (row.sessionID) {\n const sessionID = row.sessionID\n setProp(box, \"onMouseDown\", () => attempt(() => host.navigate(sessionID), undefined))\n setProp(box, \"onMouseOver\", () =>\n attempt(() => setProp(box, \"backgroundColor\", host.colors().hover), undefined),\n )\n setProp(box, \"onMouseOut\", () =>\n attempt(() => setProp(box, \"backgroundColor\", \"transparent\"), undefined),\n )\n }\n return box\n }\n\n const buildHeading = (label: string): OpentuiNode => {\n const title = createElement(\"text\")\n const bold = createElement(\"b\")\n insertNode(bold, createTextNode(label))\n insertNode(title, bold)\n effect(() => setProp(title, \"fg\", host.colors().text))\n return title\n }\n\n const buildSection = (\n view: SidebarView,\n expanded: () => boolean,\n setExpanded: (next: (value: boolean) => boolean) => boolean,\n ): OpentuiNode => {\n const list = view.rows\n const section = createElement(\"box\")\n // Heading, styled like opencode 1.x's own MCP and Todo sections: a bold\n // title, and with more than two rows a caret that folds the list away\n // behind a one-line summary.\n const header = createElement(\"box\")\n setProp(header, \"flexDirection\", \"row\")\n setProp(header, \"gap\", 1)\n setProp(header, \"onMouseDown\", () => list.length > 2 && setExpanded((value) => !value))\n if (list.length > 2) {\n const caret = createElement(\"text\")\n insert(caret, () => (expanded() ? \"▼\" : \"▶\"))\n effect(() => setProp(caret, \"fg\", host.colors().text))\n insertNode(header, caret)\n }\n const title = buildHeading(\"Subagents\")\n const summary = createElement(\"span\")\n insert(summary, () => (expanded() ? \"\" : ` (${collapsedSummary(list)})`))\n effect(() => setProp(summary, \"style\", { fg: host.colors().muted }))\n insertNode(title, summary)\n insertNode(header, title)\n insertNode(section, header)\n\n const body = createElement(\"box\")\n insert(body, () =>\n expanded() || list.length <= 2\n ? list.map((row) => buildRow(row, row.sessionID ? view.lines.get(row.sessionID) : undefined))\n : null,\n )\n insertNode(section, body)\n return section\n }\n\n /**\n * The `Main Progress` heading and the session's own line, rebuilt as the\n * line changes. `Main`, because right above `Subagents` a bare `Progress`\n * read as theirs (the maintainer, in their own pane, h #g246).\n */\n const buildProgress = (progress: () => ProgressView | undefined): OpentuiNode => {\n const section = createElement(\"box\")\n insertNode(section, buildHeading(\"Main Progress\"))\n const body = createElement(\"box\")\n insert(body, () => {\n const view = progress()\n return view ? attempt(() => buildProgressLine(view, \"glyph\"), null) : null\n })\n insertNode(section, body)\n return section\n }\n\n const buildAccounts = (lines: AccountUsageLine[]): OpentuiNode => {\n const section = createElement(\"box\")\n insertNode(section, buildHeading(\"Accounts\"))\n for (const line of lines) {\n const text = createElement(\"text\")\n setProp(text, \"wrapMode\", \"none\")\n for (const segment of line.segments) insertNode(text, styled(segment))\n insertNode(section, text)\n }\n return section\n }\n\n const render = (sessionID: () => string | undefined): OpentuiNode => {\n const root = createElement(\"box\")\n const progressBox = createElement(\"box\")\n const subagentsBox = createElement(\"box\")\n const accountsBox = createElement(\"box\")\n insertNode(root, progressBox)\n insertNode(root, subagentsBox)\n insertNode(root, accountsBox)\n const key = Symbol(\"subagents-section\")\n sectionsMounted.add(key)\n const [expanded, setExpanded] = createSignal(true)\n const view = createMemo(() => {\n const parentID = sessionID()\n if (!parentID) return EMPTY_VIEW\n return attempt(() => deriveView(parentID), EMPTY_VIEW)\n })\n effect(() => {\n const { rows, progress } = view()\n const running = rows.some((row) => row.state === \"running\") || progress?.state === \"running\"\n if (running) sectionsRunning.add(key)\n else sectionsRunning.delete(key)\n if (rows.length > 0 || progress) sectionsShowing.add(key)\n else sectionsShowing.delete(key)\n syncClocks()\n })\n onCleanup(() => {\n sectionsRunning.delete(key)\n sectionsShowing.delete(key)\n sectionsMounted.delete(key)\n syncClocks()\n })\n // The heading is built once per run; only the line under it follows the clock.\n const hasProgress = createMemo(() => view().progress !== undefined)\n insert(progressBox, () =>\n hasProgress() ? attempt(() => buildProgress(() => view().progress), null) : null,\n )\n insert(subagentsBox, () => {\n const current = view()\n if (current.rows.length === 0) return null\n return attempt(() => buildSection(current, expanded, setExpanded), null)\n })\n const accountLines = createMemo(() =>\n attempt(() => accountUsageLines(usage(), usageNow(), SIDEBAR_ROW_WIDTH, known()), [] as AccountUsageLine[]),\n )\n insert(accountsBox, () => {\n const lines = accountLines()\n if (lines.length === 0) return null\n return attempt(() => buildAccounts(lines), null)\n })\n syncClocks()\n return root\n }\n\n const dispose = () => {\n sectionsRunning.clear()\n sectionsShowing.clear()\n sectionsMounted.clear()\n syncClocks()\n }\n return { render, dispose }\n}\n\n// ---------------------------------------------------------------------------\n// opencode 1.x\n// ---------------------------------------------------------------------------\n\n// The structural slice of opencode 1.x's `TuiPluginApi` this module reads.\n// Hand-written, like `src/opencode-types.ts`, so nothing is imported from\n// `@opencode-ai/plugin`.\ninterface V1SessionInfo {\n id: string\n parentID?: string\n title?: string\n agent?: string\n time?: { created?: number; updated?: number }\n cost?: number\n}\ninterface V1Message {\n id: string\n role: string\n time?: { created?: number }\n error?: unknown\n}\ninterface V1Part {\n type: string\n tool?: string\n synthetic?: boolean\n text?: string\n state?: {\n status?: string\n input?: Record<string, unknown>\n metadata?: Record<string, unknown>\n title?: string\n time?: { start?: number; end?: number }\n }\n}\ninterface V1Api {\n state: {\n session: {\n get(sessionID: string): V1SessionInfo | undefined\n messages(sessionID: string): ReadonlyArray<V1Message> | undefined\n status(sessionID: string): { type?: string } | undefined\n /** The session's todo list, as its own Todo section reads it. */\n todo?(sessionID: string): ReadonlyArray<{ status?: string }> | undefined\n }\n part(messageID: string): ReadonlyArray<V1Part> | undefined\n }\n theme: {\n current: {\n text: Color\n textMuted: Color\n accent: Color\n success: Color\n warning: Color\n error: Color\n backgroundElement: Color\n backgroundPanel?: Color\n info?: Color\n primary?: Color\n secondary?: Color\n }\n }\n route: { navigate(name: string, params?: Record<string, unknown>): void }\n event: { on(type: string, handler: (event: { properties?: any }) => void): () => void }\n client?: { session?: { children?: (input: { sessionID: string }) => Promise<{ data?: V1SessionInfo[] }> } }\n slots: { register(plugin: { order?: number; slots: Record<string, (ctx: unknown, props: any) => OpentuiNode> }): string }\n lifecycle?: { onDispose(fn: () => void): () => void }\n}\n\nexport function registerV1(api: V1Api): void {\n // Child sessions seen per parent. 1.x has no session list in its TUI state,\n // so they come from its events, from a one-off `children` fetch the first\n // time a parent is shown (a TUI started after the dispatch still lists\n // them), and from finished parts' metadata.\n const childrenByParent = new Map<string, Map<string, ChildSessionInput>>()\n const fetched = new Set<string>()\n const stoppedAt = new Map<string, number>()\n const lastStatus = new Map<string, string>()\n const [version, setVersion] = createSignal(0)\n const bump = () => setVersion((value) => value + 1)\n\n const remember = (info: V1SessionInfo | undefined) => {\n if (!info?.id || !info.parentID) return false\n let children = childrenByParent.get(info.parentID)\n if (!children) {\n children = new Map()\n childrenByParent.set(info.parentID, children)\n }\n children.set(info.id, {\n id: info.id,\n title: info.title,\n agent: info.agent,\n created: info.time?.created,\n updated: info.time?.updated,\n })\n return true\n }\n\n const unsubscribe: Array<() => void> = []\n const listen = (type: string, handler: (properties: any) => void) => {\n const off = attempt(\n () => api.event.on(type, (event) => attempt(() => handler(event?.properties ?? {}), undefined)),\n undefined,\n )\n if (off) unsubscribe.push(off)\n }\n listen(\"session.created\", (properties) => remember(properties.info) && bump())\n listen(\"session.updated\", (properties) => remember(properties.info) && bump())\n listen(\"session.deleted\", (properties) => {\n const info = properties.info as V1SessionInfo | undefined\n if (info?.parentID && childrenByParent.get(info.parentID)?.delete(info.id)) bump()\n })\n listen(\"session.status\", (properties) => {\n const id = properties.sessionID as string | undefined\n const type = properties.status?.type as string | undefined\n if (!id || !type) return\n const previous = lastStatus.get(id)\n lastStatus.set(id, type)\n if (type === \"idle\" && previous && previous !== \"idle\") stoppedAt.set(id, Date.now())\n if (type !== \"idle\") stoppedAt.delete(id)\n // A session going busy with nothing listed has no clock to notice it.\n bump()\n })\n\n const fetchChildren = (parentID: string) => {\n if (fetched.has(parentID)) return\n fetched.add(parentID)\n const session = api.client?.session\n if (!session?.children) return\n void Promise.resolve()\n .then(() => session.children!.call(session, { sessionID: parentID }))\n .then((response) => {\n let changed = false\n for (const info of response?.data ?? []) changed = remember(info) || changed\n if (changed) bump()\n })\n .catch(() => undefined)\n }\n\n const host: SidebarHost = {\n colors() {\n const theme = api.theme.current\n const muted = theme.textMuted\n return {\n text: theme.text,\n muted,\n spinner: theme.accent,\n done: mix(theme.success, muted, 0.45),\n error: mix(theme.error, muted, 0.35),\n effort: theme.warning ?? muted,\n effortFaded: mix(theme.warning ?? muted, muted, 0.45),\n hover: theme.backgroundElement,\n barDone: theme.success ?? theme.accent,\n barTrack: mix(muted, theme.backgroundPanel ?? theme.backgroundElement, 0.5),\n fraction: theme.text,\n tool: theme.info ?? theme.accent,\n cost: theme.secondary ?? theme.primary ?? muted,\n tier: theme.secondary ?? theme.primary ?? muted,\n reset: theme.info ?? theme.accent,\n }\n },\n gather(parentID) {\n version()\n fetchChildren(parentID)\n const parts: TaskPartInput[] = []\n const completions: CompletionInput[] = []\n for (const message of api.state.session.messages(parentID) ?? []) {\n const messageParts = api.state.part(message.id) ?? []\n if (message.role === \"assistant\") {\n for (const part of messageParts) {\n if (part.type !== \"tool\" || !TASK_TOOLS.has(part.tool ?? \"\")) continue\n const state = part.state ?? {}\n parts.push({\n status: state.status,\n input: state.input,\n metadata: state.metadata,\n title: state.title,\n start: state.time?.start,\n end: state.time?.end,\n })\n }\n } else if (message.role === \"user\") {\n // A background child's end is a synthetic user part in the parent.\n for (const part of messageParts) {\n if (part.type !== \"text\" || part.synthetic !== true || !part.text) continue\n completions.push(...completionsIn(part.text, message.time?.created))\n }\n }\n }\n const known = new Map(childrenByParent.get(parentID) ?? [])\n for (const part of parts) {\n const id = partChildID(part)\n if (!id || known.has(id)) continue\n const info = attempt(() => api.state.session.get(id), undefined)\n known.set(id, {\n id,\n title: info?.title,\n agent: info?.agent,\n created: info?.time?.created ?? part.start,\n updated: info?.time?.updated,\n })\n }\n return {\n children: [...known.values()],\n parts,\n completions,\n status: (id: string): ChildRunState => {\n const type = attempt(() => api.state.session.status(id)?.type, undefined)\n if (type === \"busy\" || type === \"retry\") return \"busy\"\n if (type === \"idle\") return \"idle\"\n return \"unknown\"\n },\n stoppedAt: (id: string) => stoppedAt.get(id),\n cost: (id: string) => attempt(() => api.state.session.get(id)?.cost, undefined),\n }\n },\n progress(sessionID) {\n const messages: ProgressMessage[] = []\n // The turn only: the parts of every earlier message are never read.\n const turn = turnMessages(api.state.session.messages(sessionID) ?? [], (message) => message.role === \"user\")\n for (const message of turn) {\n const tools: ProgressToolCall[] = []\n const texts: string[] = []\n for (const part of api.state.part(message.id) ?? []) {\n if (message.role === \"assistant\" && part.type === \"tool\") tools.push({ name: part.tool, status: part.state?.status })\n // Every text part, synthetic ones included: a background child's\n // end starts a turn too, and the server counts its envelope.\n if (message.role === \"user\" && part.type === \"text\" && typeof part.text === \"string\") texts.push(part.text)\n }\n messages.push({\n role: message.role,\n created: message.time?.created,\n error: message.error !== undefined && message.error !== null,\n tools,\n chars: message.role === \"user\" ? promptChars(texts) : undefined,\n })\n }\n const todos = attempt(() => api.state.session.todo?.(sessionID), undefined) ?? []\n return { messages, todos: todos.map((todo) => ({ status: todo.status })) }\n },\n navigate(sessionID) {\n api.route.navigate(\"session\", { sessionID })\n },\n }\n\n const sidebar = createSidebar(host)\n api.slots.register({\n order: SECTION_ORDER,\n slots: {\n sidebar_content(_ctx: unknown, props: { session_id: string }) {\n return sidebar.render(() => props.session_id)\n },\n },\n })\n api.lifecycle?.onDispose(() => {\n for (const off of unsubscribe) attempt(off, undefined)\n sidebar.dispose()\n })\n}\n\n// ---------------------------------------------------------------------------\n// opencode 2.x\n// ---------------------------------------------------------------------------\n\n// The structural slice of opencode 2.x's TUI `Context` this module reads,\n// measured on 2.0.22 (h #g234). Its sessions carry `parentID`, its status is\n// `\"running\" | \"idle\"`, an assistant message holds its tool calls inline as\n// `content[]` items named `subagent` (with `metadata.sessionID` set while the\n// call still runs, unlike 1.x), and a background child's end is a message of\n// type `synthetic` whose text is `<subagent sessionID=\"...\" state=\"...\">`.\n// It hands a TUI plugin no todo list (h #g246), so a 2.x bar is the history's\n// estimate or nothing (h #g247).\ninterface V2Session {\n id: string\n parentID?: string\n title?: string\n agent?: string\n time?: { created?: number; updated?: number }\n}\ninterface V2Message {\n type?: string\n text?: string\n time?: { created?: number }\n error?: unknown\n content?: Array<{\n type?: string\n name?: string\n text?: string\n time?: { created?: number; completed?: number }\n state?: { status?: string; input?: Record<string, unknown>; metadata?: Record<string, unknown> }\n }>\n}\ntype V2Shade = Record<string, Color>\ninterface V2Context {\n data: {\n session: {\n list(): V2Session[] | undefined\n status(sessionID: string): string\n /** A child's own spend; a root's is its family's sum, which is not read. */\n cost?(sessionID: string): number\n message: { list(sessionID: string): V2Message[] | undefined }\n }\n }\n theme: {\n text: {\n base: Color\n muted: Color\n feedback: { success: V2Shade; warning?: V2Shade; error: V2Shade; info?: V2Shade }\n }\n background: { base?: Color; raised: { base: Color } }\n hue: { accent: V2Shade; interactive?: V2Shade }\n }\n themeMode?: \"dark\" | \"light\"\n ui: {\n router: { navigate(destination: { type: \"session\"; sessionID: string }): void }\n slot(claim: { append: string; render: (input: { sessionID: string }) => OpentuiNode }): () => void\n }\n}\n\nexport function setupV2(context: V2Context): () => void {\n const host: SidebarHost = {\n colors() {\n const theme = context.theme\n const muted = theme.text.muted\n // The accent and interactive hues at the shade that reads on this mode's background.\n const shade = context.themeMode === \"light\" ? \"500\" : \"200\"\n const accent = theme.hue.accent[shade] ?? theme.text.base\n return {\n text: theme.text.base,\n muted,\n spinner: accent,\n done: mix(theme.text.feedback.success.muted ?? theme.text.feedback.success.base, muted, 0.45),\n error: mix(theme.text.feedback.error.muted ?? theme.text.feedback.error.base, muted, 0.35),\n effort: theme.text.feedback.warning?.base ?? muted,\n effortFaded: mix(theme.text.feedback.warning?.base ?? muted, muted, 0.45),\n hover: theme.background.raised.base,\n barDone: theme.text.feedback.success.base ?? accent,\n barTrack: mix(muted, theme.background.base ?? theme.background.raised.base, 0.5),\n fraction: theme.text.base,\n tool: theme.text.feedback.info?.base ?? accent,\n cost: theme.hue.interactive?.[shade] ?? accent,\n tier: theme.hue.interactive?.[shade] ?? accent,\n reset: theme.text.feedback.info?.base ?? accent,\n }\n },\n gather(parentID) {\n const children: ChildSessionInput[] = (context.data.session.list() ?? [])\n .filter((session) => session.parentID === parentID)\n .map((session) => ({\n id: session.id,\n title: session.title,\n agent: session.agent,\n created: session.time?.created,\n updated: session.time?.updated,\n }))\n const parts: TaskPartInput[] = []\n const completions: CompletionInput[] = []\n for (const message of context.data.session.message.list(parentID) ?? []) {\n if (message.type === \"assistant\") {\n for (const item of message.content ?? []) {\n if (item.type !== \"tool\" || !TASK_TOOLS.has(item.name ?? \"\")) continue\n parts.push({\n status: item.state?.status,\n input: item.state?.input,\n metadata: item.state?.metadata,\n start: item.time?.created,\n end: item.time?.completed,\n })\n }\n } else if (message.type === \"synthetic\" && message.text) {\n completions.push(...completionsIn(message.text, message.time?.created))\n }\n }\n return {\n children,\n parts,\n completions,\n status: (id: string): ChildRunState => {\n const status = attempt(() => context.data.session.status(id), undefined)\n if (status === \"running\") return \"busy\"\n if (status === \"idle\") return \"idle\"\n return \"unknown\"\n },\n stoppedAt: () => undefined,\n cost: (id: string) => attempt(() => context.data.session.cost?.(id), undefined),\n }\n },\n progress(sessionID) {\n const messages: ProgressMessage[] = []\n // The turn only, as on 1.x; a synthetic message starts one like a user's.\n const turn = turnMessages(\n context.data.session.message.list(sessionID) ?? [],\n (message) => message.type === \"user\" || message.type === \"synthetic\",\n )\n for (const message of turn) {\n const tools: ProgressToolCall[] = []\n const texts: string[] = []\n for (const item of message.content ?? []) {\n if (message.type === \"assistant\" && item.type === \"tool\") tools.push({ name: item.name, status: item.state?.status })\n if (message.type === \"user\" && item.type === \"text\" && typeof item.text === \"string\") texts.push(item.text)\n }\n if (message.type === \"synthetic\" && message.text) texts.push(message.text)\n const starts = message.type === \"user\" || message.type === \"synthetic\"\n messages.push({\n // A synthetic message (a background child's end) prompts the parent\n // like a user message does: it starts a turn.\n role: message.type === \"synthetic\" ? \"user\" : (message.type ?? \"\"),\n created: message.time?.created,\n error: message.error !== undefined && message.error !== null,\n tools,\n chars: starts ? promptChars(texts) : undefined,\n })\n }\n return { messages, todos: [] }\n },\n navigate(sessionID) {\n context.ui.router.navigate({ type: \"session\", sessionID })\n },\n }\n\n const sidebar = createSidebar(host)\n const release = attempt(\n () =>\n context.ui.slot({\n append: \"sidebar.content\",\n render: (input) => sidebar.render(() => input.sessionID),\n }),\n undefined,\n )\n return () => {\n if (release) attempt(release, undefined)\n sidebar.dispose()\n }\n}\n\nconst tui = async (api: V1Api): Promise<void> => {\n attempt(() => registerV1(api), undefined)\n}\n\nconst setup = (context: V2Context): (() => void) | void =>\n attempt(() => setupV2(context), undefined)\n\nexport default { id: SUBAGENTS_TUI_PLUGIN_ID, tui, setup }\n","/**\n * The model behind the sidebar's `Subagents` section (`src/tui.ts`): which\n * subagents of one opencode session to show, in what state, in what order, and\n * the exact text of each row.\n *\n * Pure on purpose. It imports nothing, so the TUI bundle carries no logger (a\n * logger on the TUI's main thread writes to the terminal it is drawing on,\n * h #g193) and every rule here is testable without a terminal. The TUI half\n * only gathers the inputs and paints the segments this module returns.\n *\n * Measured on opencode 1.18.35 (h #g234), and each one shapes a rule below:\n *\n * - A RUNNING `task` part carries no `state.metadata` in the TUI's store, so\n * the child session id is not on the part until it completes. Rows are\n * built from the child SESSIONS (`parentID` is the current session) and a\n * part is matched to its child by the title opencode gives the child,\n * `<description> (@<agent> subagent)`.\n * - A background dispatch's part completes at once with a `<task id\n * state=\"running\">` envelope; the child's end arrives later as a synthetic\n * user part `<task id=\"ses_...\" state=\"completed\">` in the parent (opencode\n * 2 writes `<subagent sessionID=\"...\" state=\"...\">`). That envelope is the\n * authoritative end of a background child.\n * - `metadata.model` on a finished part is what opencode ASKED for, not what\n * ran: an agent's `forceModel`, its `reasoningEffort`, the dispatch form and\n * the fallback chain all change the spawn without telling opencode. So the\n * label prefers the spawn record the server half writes\n * (`src/spawn-record-store.ts`) and falls back to the part.\n */\n\nexport type SubagentState = \"running\" | \"done\" | \"error\"\n\n/** One child session as either major reports it. */\nexport interface ChildSessionInput {\n id: string\n title?: string\n agent?: string\n created?: number\n updated?: number\n}\n\n/** A `task` / `subagent` tool part in the parent, as either major reports it. */\nexport interface TaskPartInput {\n status?: string\n input?: Record<string, unknown>\n metadata?: Record<string, unknown>\n title?: string\n start?: number\n end?: number\n}\n\n/** A background child's end, read out of the parent's synthetic user parts. */\nexport interface CompletionInput {\n sessionID: string\n state: string\n at?: number\n}\n\n/** What the server half recorded about a session's real spawn. */\nexport interface SpawnRecordInput {\n model?: string\n effort?: string\n account?: string\n at?: number\n}\n\nexport type ChildRunState = \"busy\" | \"idle\" | \"unknown\"\n\nexport interface SidebarInput {\n children: ChildSessionInput[]\n parts: TaskPartInput[]\n completions: CompletionInput[]\n status: (sessionID: string) => ChildRunState\n record: (sessionID: string) => SpawnRecordInput | undefined\n /** When the TUI first saw a child stop, by session id. */\n stoppedAt: (sessionID: string) => number | undefined\n /** A child's spend so far in dollars, as the host's session record carries it (h #g246). */\n cost?: (sessionID: string) => number | undefined\n now: number\n}\n\nexport interface SubagentRow {\n /** The child session, absent while a dispatch has not created one yet. */\n sessionID?: string\n agent: string\n description: string\n background: boolean\n state: SubagentState\n /** `sonnet-5.5 · high · alpha`, empty when nothing is known. */\n label: string\n /** The effort piece of `label`, drawn in the theme's warning colour. */\n effort?: string\n /** Spend so far in dollars, right-aligned on the label's line while it fits; kept once finished. */\n cost?: number\n started: number\n finished?: number\n}\n\n/** How long a finished subagent stays in the list, and how many of them. */\nexport const FINISHED_TTL_MS = 10 * 60_000\nexport const FINISHED_KEEP = 3\n/**\n * A background child that reads idle with no envelope yet is still starting\n * for this long: opencode creates the session before it marks it busy.\n */\nexport const STARTING_GRACE_MS = 10_000\n/** The usable width of a row in opencode 1.18.35's 42-column sidebar. */\nexport const SIDEBAR_ROW_WIDTH = 36\n/**\n * What a row's second and third lines sit in by, under the glyph's cell. One\n * cell, not the two that would align them under the agent name, because the\n * cell is what lets `sonnet-5.5 · high · appical · $0.42` fit a row (h #g246).\n */\nexport const ROW_INDENT = \" \"\nexport const SPINNER_FRAMES = [\"⠋\", \"⠙\", \"⠹\", \"⠸\", \"⠼\", \"⠴\", \"⠦\", \"⠧\", \"⠇\", \"⠏\"]\n\nconst TITLE_SUFFIX = /\\s*\\(@([^)\\s]+) subagent\\)\\s*$/\n\n/** `Fix the form (@implementor subagent)` -> description and agent. */\nexport function parseChildTitle(title: string | undefined): { description: string; agent?: string } {\n const text = (title ?? \"\").trim()\n const match = TITLE_SUFFIX.exec(text)\n if (!match) return { description: text }\n return { description: text.slice(0, match.index).trim(), agent: match[1] }\n}\n\nfunction text(value: unknown): string | undefined {\n return typeof value === \"string\" && value.trim() ? value.trim() : undefined\n}\n\n/** The child session a part names, on either major's metadata. */\nexport function partChildID(part: TaskPartInput): string | undefined {\n const metadata = part.metadata ?? {}\n return text(metadata.sessionId) ?? text(metadata.sessionID)\n}\n\nfunction partAgent(part: TaskPartInput): string | undefined {\n const input = part.input ?? {}\n return text(input.subagent_type) ?? text(input.agent)\n}\n\nfunction partDescription(part: TaskPartInput): string | undefined {\n return text(part.input?.description) ?? text(part.title)\n}\n\nfunction partBackground(part: TaskPartInput): boolean {\n return part.input?.background === true || part.metadata?.background === true\n}\n\nconst COMPLETION_ENVELOPE =\n /<(?:task\\s+id|subagent\\s+sessionID)=\"(ses_[A-Za-z0-9]+)\"\\s+state=\"([a-z_]+)\"/g\n\n/**\n * Every background end a text names, in order. Both majors' envelopes:\n * `<task id=\"ses_...\" state=\"completed\">` (1.x) and\n * `<subagent sessionID=\"ses_...\" state=\"completed\">` (2.x). `running` is the\n * dispatch's own answer, not an end, so it is skipped.\n */\nexport function completionsIn(value: string, at?: number): CompletionInput[] {\n const found: CompletionInput[] = []\n for (const match of value.matchAll(COMPLETION_ENVELOPE)) {\n if (match[2] === \"running\") continue\n found.push({ sessionID: match[1], state: match[2], at })\n }\n return found\n}\n\nfunction endState(state: string): SubagentState {\n return state === \"completed\" ? \"done\" : \"error\"\n}\n\n/**\n * `claude-opus-5-5` -> `opus-5.5`, `claude-sonnet-4-5-20250929@alpha` ->\n * `sonnet-4.5`, `claude-opus-5-5-fast` -> `opus-5.5 fast`,\n * `claude-opus-5[1m]` -> `opus-5 1m`. Anything that is not a Claude id is\n * returned without its account marker and otherwise untouched.\n */\nexport function shortModelName(modelID: string | undefined): string | undefined {\n let id = text(modelID)\n if (!id) return undefined\n const at = id.indexOf(\"@\")\n if (at > 0) id = id.slice(0, at)\n let suffix = \"\"\n const window = /\\[(\\d+[km])\\]$/i.exec(id)\n if (window) {\n suffix += ` ${window[1].toLowerCase()}`\n id = id.slice(0, window.index)\n }\n if (id.endsWith(\"-fast\")) {\n suffix = ` fast${suffix}`\n id = id.slice(0, -\"-fast\".length)\n }\n if (!id.startsWith(\"claude-\")) return `${id}${suffix}`\n id = id.slice(\"claude-\".length).replace(/-\\d{8}$/, \"\")\n const match = /^([a-z]+)-(\\d+)(?:-(\\d+))?$/.exec(id)\n if (!match) return `${id}${suffix}`\n const version = match[3] === undefined ? match[2] : `${match[2]}.${match[3]}`\n return `${match[1]}-${version}${suffix}`\n}\n\n/**\n * The account a provider id names on a multi-account install\n * (`claude-code-alpha` -> `alpha`). A bare `claude-code` (one account) and a\n * provider that is not this plugin's name none: there is nothing to tell apart.\n */\nexport function accountFromProvider(providerID: string | undefined): string | undefined {\n const id = text(providerID)\n if (!id?.startsWith(\"claude-code-\")) return undefined\n return id.slice(\"claude-code-\".length) || undefined\n}\n\n/** `sonnet-5.5 · high · alpha`; pieces that are not known are left out. */\nexport function subagentLabel(\n record: SpawnRecordInput | undefined,\n part: TaskPartInput | undefined,\n): string {\n const asked = part?.metadata?.model as { modelID?: unknown; providerID?: unknown } | undefined\n const askedModel = typeof asked?.modelID === \"string\" ? asked.modelID : undefined\n const askedProvider = typeof asked?.providerID === \"string\" ? asked.providerID : undefined\n const model = shortModelName(record?.model ?? askedModel)\n const effort = text(record?.effort)\n const account = record?.model ? text(record.account) : accountFromProvider(askedProvider)\n return [model, effort, account].filter(Boolean).join(\" · \")\n}\n\n/**\n * `$0.42`, and never more than five cells under ten thousand dollars (`$12.4`,\n * `$123`), because the spend shares one 36-cell line with the model, the effort\n * and the account, and a cent is noise past ten dollars. A spend under a cent\n * reads `<$0.01` rather than a false zero, and nothing at all is nothing: no\n * record, nothing spent yet, or not a number.\n */\nexport function formatCost(value: number | undefined): string | undefined {\n if (typeof value !== \"number\" || !Number.isFinite(value) || value <= 0) return undefined\n if (value < 0.005) return \"<$0.01\"\n const digits = [2, 1, 0].map((places) => value.toFixed(places)).find((text) => text.length <= 4)\n return `$${digits ?? value.toFixed(0)}`\n}\n\n/**\n * Cut `value` to `width` cells with a trailing ellipsis. Counts code points,\n * which is a cell each for the text a task description holds; a wide glyph\n * can overrun by one cell and is left to the renderer's own clipping.\n */\nexport function truncate(value: string, width: number): string {\n const chars = Array.from(value.replace(/\\s+/g, \" \").trim())\n if (width <= 0) return \"\"\n if (chars.length <= width) return chars.join(\"\")\n if (width === 1) return \"…\"\n return `${chars.slice(0, width - 1).join(\"\").trimEnd()}…`\n}\n\n/**\n * The rows for one session: running first (oldest first), then finished\n * (newest first), at most `FINISHED_KEEP` of those and none older than\n * `FINISHED_TTL_MS`. Empty when nothing is worth showing, which is what makes\n * the section disappear.\n */\nexport function deriveSubagentRows(input: SidebarInput): SubagentRow[] {\n const completions = new Map<string, CompletionInput>()\n for (const completion of input.completions) completions.set(completion.sessionID, completion)\n\n const byChild = new Map<string, TaskPartInput>()\n const unmatched: TaskPartInput[] = []\n for (const part of input.parts) {\n const child = partChildID(part)\n if (child) byChild.set(child, part)\n else unmatched.push(part)\n }\n\n const rows: SubagentRow[] = []\n const seen = new Set<string>()\n for (const child of input.children) {\n if (seen.has(child.id)) continue\n seen.add(child.id)\n const parsed = parseChildTitle(child.title)\n let part = byChild.get(child.id)\n if (!part) {\n // A running part has no child id yet: match it on the title opencode\n // gave the child, agent and description both.\n const index = unmatched.findIndex(\n (candidate) =>\n partDescription(candidate) === parsed.description &&\n (parsed.agent === undefined || partAgent(candidate) === parsed.agent),\n )\n if (index >= 0) part = unmatched.splice(index, 1)[0]\n }\n rows.push(rowFor(input, child, parsed, part, completions.get(child.id)))\n }\n // A dispatch opencode has not created a session for yet: shown, not clickable.\n for (const part of unmatched) {\n if (part.status !== \"pending\" && part.status !== \"running\") continue\n const description = partDescription(part)\n const agent = partAgent(part)\n if (!description && !agent) continue\n rows.push({\n agent: agent ?? \"subagent\",\n description: description ?? \"\",\n background: partBackground(part),\n state: \"running\",\n label: subagentLabel(undefined, part),\n started: part.start ?? input.now,\n })\n }\n\n const running = rows\n .filter((row) => row.state === \"running\")\n .sort((a, b) => a.started - b.started)\n const finished = rows\n .filter((row) => row.state !== \"running\")\n .filter((row) => input.now - (row.finished ?? row.started) <= FINISHED_TTL_MS)\n .sort((a, b) => (b.finished ?? b.started) - (a.finished ?? a.started))\n .slice(0, FINISHED_KEEP)\n return [...running, ...finished]\n}\n\nfunction rowFor(\n input: SidebarInput,\n child: ChildSessionInput,\n parsed: { description: string; agent?: string },\n part: TaskPartInput | undefined,\n completion: CompletionInput | undefined,\n): SubagentRow {\n const background = part ? partBackground(part) : false\n const started = part?.start ?? child.created ?? input.now\n const status = input.status(child.id)\n let state: SubagentState\n let finished: number | undefined\n if (part && !background && part.status !== \"pending\" && part.status !== \"running\") {\n state = part.status === \"completed\" ? \"done\" : \"error\"\n finished = part.end\n } else if (completion) {\n state = endState(completion.state)\n finished = completion.at\n } else if (status === \"busy\") {\n state = \"running\"\n } else if (part && !background) {\n // pending or running: the parent is still waiting on this child.\n state = \"running\"\n } else if (status === \"idle\" && input.now - started < STARTING_GRACE_MS) {\n state = \"running\"\n } else if (status === \"unknown\" && background) {\n // Nothing says it stopped: a background child keeps its spinner until\n // its envelope arrives or opencode reports it idle.\n state = \"running\"\n } else {\n state = \"done\"\n }\n if (state !== \"running\" && finished === undefined) {\n finished = input.stoppedAt(child.id) ?? child.updated ?? started\n }\n const cost = input.cost?.(child.id)\n return {\n sessionID: child.id,\n agent: parsed.agent ?? (part ? partAgent(part) : undefined) ?? text(child.agent) ?? \"subagent\",\n description: (part ? partDescription(part) : undefined) ?? parsed.description,\n background,\n state,\n label: subagentLabel(input.record(child.id), part),\n effort: text(input.record(child.id)?.effort),\n ...(cost !== undefined ? { cost } : {}),\n started,\n finished,\n }\n}\n\n/**\n * A run of text with the role the TUI maps onto a theme colour. `bar-done`\n * through `cost` are the progress line's and the spend (`src/progress-line.ts`,\n * h #g246); `tier` and `reset` are the `Accounts` line's plan tier and reset\n * time (`src/account-sidebar.ts`, h #g248).\n */\nexport interface Segment {\n text: string\n role:\n | \"spinner\"\n | \"done\"\n | \"error\"\n | \"agent\"\n | \"description\"\n | \"muted\"\n | \"effort\"\n | \"effort-faded\"\n | \"bar-done\"\n | \"bar-over\"\n | \"bar-track\"\n | \"fraction\"\n | \"estimate\"\n | \"estimate-over\"\n | \"tool\"\n | \"cost\"\n | \"tier\"\n | \"reset\"\n}\n\n/**\n * The two lines of one row, laid out for `width` cells:\n *\n * ⠋ implementor Fix the dispatch f… bg\n * sonnet-5.5 · high · alpha · $0.42\n *\n * The `bg` marker is right-aligned, the description takes what is left, and a\n * finished row is drawn in the muted role throughout apart from its glyph and\n * its spend. The second line sits `ROW_INDENT` in. The spend follows the label\n * after ` · ` while that fits; when it does not, the dot goes first (a space\n * is left), then the spend itself: the model is never cut to make room for a\n * number, and a label that was cut already has none.\n */\nexport function layoutRow(\n row: SubagentRow,\n width: number = SIDEBAR_ROW_WIDTH,\n frame = 0,\n): { first: Segment[]; second: Segment[] } {\n const finished = row.state !== \"running\"\n const glyph =\n row.state === \"running\"\n ? SPINNER_FRAMES[frame % SPINNER_FRAMES.length]\n : row.state === \"done\"\n ? \"✓\"\n : \"✗\"\n const marker = row.background ? \"bg\" : \"\"\n const reserved = marker ? marker.length + 1 : 0\n const agentWidth = Math.max(1, Math.min(Array.from(row.agent).length, width - 2 - reserved - 6))\n const agent = truncate(row.agent, agentWidth)\n const room = width - 2 - Array.from(agent).length - 1 - reserved\n const description = room > 0 ? truncate(row.description, room) : \"\"\n const used = 2 + Array.from(agent).length + (description ? 1 + Array.from(description).length : 0)\n const first: Segment[] = [\n { text: glyph, role: row.state === \"running\" ? \"spinner\" : row.state === \"done\" ? \"done\" : \"error\" },\n { text: \" \", role: \"muted\" },\n { text: agent, role: finished ? \"muted\" : \"agent\" },\n ]\n if (description) {\n first.push({ text: \" \", role: \"muted\" })\n first.push({ text: description, role: finished ? \"muted\" : \"description\" })\n }\n if (marker) {\n first.push({ text: \" \".repeat(Math.max(1, width - used - marker.length)), role: \"muted\" })\n first.push({ text: marker, role: \"muted\" })\n }\n // The effort is the one piece in colour: it is what changes from one\n // subagent to the next and what costs the most, so it is the one to spot.\n const second: Segment[] = []\n const content = width - ROW_INDENT.length\n const pieces = row.label ? truncate(row.label, content).split(\" · \") : []\n const labelWidth = pieces.reduce((sum, piece) => sum + Array.from(piece).length, 0) + 3 * Math.max(0, pieces.length - 1)\n const cost = formatCost(row.cost)\n const costWidth = cost ? Array.from(cost).length : 0\n // What stands between the label and the spend: ` · ` while that fits, a\n // space when only that does, nothing after no label, and no spend at all\n // when even a space will not fit it.\n let joiner: string | undefined\n if (cost) {\n if (pieces.length === 0) joiner = costWidth <= content ? \"\" : undefined\n else if (labelWidth + 3 + costWidth <= content) joiner = \" · \"\n else if (labelWidth + 1 + costWidth <= content) joiner = \" \"\n }\n if (pieces.length > 0 || joiner !== undefined) {\n const effortAt = row.effort ? pieces.indexOf(row.effort) : -1\n second.push({ text: ROW_INDENT, role: \"muted\" })\n pieces.forEach((piece, index) => {\n if (index > 0) second.push({ text: \" · \", role: \"muted\" })\n const role = index !== effortAt ? \"muted\" : finished ? \"effort-faded\" : \"effort\"\n second.push({ text: piece, role })\n })\n if (cost && joiner !== undefined) {\n if (joiner) second.push({ text: joiner, role: \"muted\" })\n second.push({ text: cost, role: \"cost\" })\n }\n }\n return { first, second }\n}\n\n/** The collapsed heading's count: `2 running` or `3 done`. */\nexport function collapsedSummary(rows: SubagentRow[]): string {\n const running = rows.filter((row) => row.state === \"running\").length\n return running > 0 ? `${running} running` : `${rows.length} done`\n}\n","/**\n * The model behind the sidebar's progress lines (`src/tui.ts`, h #g246,\n * h #g247): one for the session on screen, under a `Main Progress` heading\n * above `Subagents`, and one under each RUNNING subagent row:\n *\n * Main Progress\n * ⠋ ▰▰▰▰▱▱▱▱▱▱ 1m02s/~2m30s · bash\n * Subagents\n * ⠋ implementor Fix the dispatch form…\n * sonnet-5.5 · high · alpha · $0.42\n * ▰▰▰▱▱▱▱▱▱▱ 45s/~2m10s · read\n *\n * What the bar means, in order of what the session has:\n *\n * - With a todo list it is the fraction of items completed and the line\n * says which, `2/4`, beside the elapsed. opencode 1.x's TUI store holds\n * every session's list (the CLI's TaskCreate family reaches it as\n * `todowrite`, h #g85); opencode 2.x exposes none to a TUI plugin. A bar\n * is never full before the last item is done and never empty once the\n * first is.\n * - Else, with history for this agent and model, it is the turn's elapsed\n * over its estimated total (`src/turn-history.ts`: the weighted median\n * residual life of the recorded turns that have outlasted this one so\n * far, weighted toward turns with a prompt of about this length and at\n * this effort), and the line says both, `1m02s/~2m30s`. The bar only\n * ever advances within a turn (`createFillMemory`), though the total it\n * is drawn against can rise. Past every recorded turn it is full in the\n * warning colour and the total shown is the usual one.\n * - Else there is no bar: the spinner says the session works, and a bar\n * that could not fill would say nothing the spinner does not.\n * - The rest is the turn's own record: elapsed since the last user message\n * (else since the TUI first saw the session busy), the tool calls made\n * since it, and the tool running now.\n * - A turn that ended lingers `PROGRESS_LINGER_MS` with its final state, the\n * bar filled and ✓ or ✗ off the last reply's error, then the line leaves.\n * A subagent row's line leaves with the row's spinner.\n *\n * Pure, like `src/subagent-sidebar.ts` and for the same reason: the TUI bundle\n * runs on the terminal's own thread, where a logger writes over the interface\n * (h #g193). It imports only that module's text helpers and the estimate's\n * type.\n */\nimport {\n ROW_INDENT,\n SIDEBAR_ROW_WIDTH,\n SPINNER_FRAMES,\n truncate,\n type ChildRunState,\n type Segment,\n} from \"./subagent-sidebar.js\"\nimport type { TurnEstimate } from \"./turn-history.js\"\n\n/** The bar's width in cells. */\nexport const BAR_WIDTH = 10\n/** How long a finished turn's line stays, showing its final state. */\nexport const PROGRESS_LINGER_MS = 15_000\nexport const BAR_DONE = \"▰\"\nexport const BAR_TODO = \"▱\"\n\nexport interface ProgressToolCall {\n name?: string\n status?: string\n}\n\nexport interface ProgressMessage {\n role: string\n created?: number\n /** The message ended in an error (an assistant message's `error`). */\n error?: boolean\n /** The message's tool calls, in order. */\n tools: ProgressToolCall[]\n /** A user message's length in characters (`promptChars`); absent when unknown. */\n chars?: number\n}\n\nexport interface ProgressTodo {\n status?: string\n}\n\n/** What a session's own store holds that its progress line is measured from. */\nexport interface ProgressSource {\n messages: ProgressMessage[]\n todos: ProgressTodo[]\n}\n\nexport interface ProgressPlan {\n done: number\n total: number\n}\n\nexport interface ProgressMeasure {\n /** The todo list's completed count over its total; absent without a list. */\n plan?: ProgressPlan\n /** The last user message's time, which is when the turn started. */\n startedAt?: number\n /** The length of the message that started the turn, the estimate's kernel input. */\n chars?: number\n /** Tool calls since that message. */\n calls: number\n /** The tool running now, if one is. */\n tool?: string\n /** The newest reply since that message ended in an error. */\n failed: boolean\n}\n\nfunction text(value: unknown): string | undefined {\n return typeof value === \"string\" && value.trim() ? value.trim() : undefined\n}\n\nfunction cells(value: string): number {\n return Array.from(value).length\n}\n\n/**\n * The messages a line is measured from: the last user message and everything\n * after it, all of them when there is none. A host hands `measureProgress`\n * only these, so it reads the tool parts of the turn on screen and a long\n * session's earlier messages cost nothing per tick.\n */\nexport function turnMessages<T>(messages: readonly T[], isUser: (message: T) => boolean): readonly T[] {\n for (let index = messages.length - 1; index >= 0; index -= 1) {\n if (isUser(messages[index])) return index === 0 ? messages : messages.slice(index)\n }\n return messages\n}\n\n/**\n * The facts a line is built from. The turn is everything after the last user\n * message (all of it when there is none); the tool running now is the newest\n * call only while it is still pending or running; a todo marked `cancelled`\n * is not part of the plan.\n */\nexport function measureProgress(source: ProgressSource): ProgressMeasure {\n const messages = source.messages\n let turnStart = -1\n for (let index = messages.length - 1; index >= 0; index -= 1) {\n if (messages[index].role === \"user\") {\n turnStart = index\n break\n }\n }\n let calls = 0\n let tool: string | undefined\n let failed = false\n for (let index = turnStart + 1; index < messages.length; index += 1) {\n const message = messages[index]\n if (message.role !== \"assistant\") continue\n failed = message.error === true\n for (const call of message.tools) {\n calls += 1\n const running = call.status === \"running\" || call.status === \"pending\"\n tool = running ? text(call.name) : undefined\n }\n }\n let total = 0\n let done = 0\n for (const todo of source.todos) {\n if (todo.status === \"cancelled\") continue\n total += 1\n if (todo.status === \"completed\") done += 1\n }\n return {\n plan: total > 0 ? { done, total } : undefined,\n startedAt: turnStart >= 0 ? messages[turnStart].created : undefined,\n chars: turnStart >= 0 ? messages[turnStart].chars : undefined,\n calls,\n tool,\n failed,\n }\n}\n\n/** One line, ready to lay out. */\nexport interface ProgressView {\n state: \"running\" | \"done\" | \"error\"\n plan?: ProgressPlan\n /** The turn's estimated total from its agent and model's history; the host attaches it while the turn runs. */\n estimate?: TurnEstimate\n elapsedMs: number\n calls: number\n tool?: string\n}\n\n/**\n * The line for the session on screen. Running while opencode reports the\n * session busy; after it stopped, its final state for `PROGRESS_LINGER_MS`\n * from the stop; otherwise nothing. A stop older than the turn's own start is\n * an earlier turn's and shows nothing either.\n */\nexport function sessionProgress(\n measure: ProgressMeasure,\n input: { status: ChildRunState; busySince?: number; stoppedAt?: number; now: number },\n): ProgressView | undefined {\n const startedAt = measure.startedAt ?? input.busySince\n if (input.status === \"busy\") {\n return {\n state: \"running\",\n plan: measure.plan,\n elapsedMs: Math.max(0, input.now - (startedAt ?? input.now)),\n calls: measure.calls,\n tool: measure.tool,\n }\n }\n if (input.stoppedAt === undefined) return undefined\n if (startedAt !== undefined && input.stoppedAt < startedAt) return undefined\n if (input.now - input.stoppedAt > PROGRESS_LINGER_MS) return undefined\n return {\n state: measure.failed ? \"error\" : \"done\",\n plan: measure.plan,\n elapsedMs: Math.max(0, input.stoppedAt - (startedAt ?? input.stoppedAt)),\n calls: measure.calls,\n tool: undefined,\n }\n}\n\n/**\n * The line under a running subagent row. The row's own state says it runs,\n * and the turn's start falls back to the row's, which is the dispatch.\n */\nexport function rowProgress(measure: ProgressMeasure, input: { started: number; now: number }): ProgressView {\n const startedAt = measure.startedAt ?? input.started\n return {\n state: \"running\",\n plan: measure.plan,\n elapsedMs: Math.max(0, input.now - startedAt),\n calls: measure.calls,\n tool: measure.tool,\n }\n}\n\n/** What the TUI remembers about a session's status, for the two inputs no store carries. */\nexport interface StatusMemory {\n /** When the TUI first saw the session busy, this run. */\n busySince?: number\n /** When the TUI saw it go idle after that. */\n stoppedAt?: number\n}\n\n/** How many sessions the watch and the fill memory remember; the oldest observed is forgotten first. */\nexport const STATUS_WATCH_CAP = 256\n\n/**\n * Status transitions as the TUI observes them. Busy after anything else dates\n * the run and forgets the earlier stop; idle after busy dates the stop; an\n * unchanged status, and `unknown`, change nothing. Both dates are kept until\n * the next run starts, which is what lets a finished line linger.\n */\nexport function createStatusWatch() {\n const seen = new Map<string, { status: ChildRunState } & StatusMemory>()\n return {\n observe(sessionID: string, status: ChildRunState, now: number): StatusMemory {\n let entry = seen.get(sessionID)\n if (!entry) {\n entry = { status: \"unknown\" }\n if (seen.size >= STATUS_WATCH_CAP) {\n const oldest = seen.keys().next().value\n if (oldest !== undefined) seen.delete(oldest)\n }\n seen.set(sessionID, entry)\n }\n if (status === \"busy\" && entry.status !== \"busy\") {\n entry.busySince = now\n entry.stoppedAt = undefined\n } else if (status === \"idle\" && entry.status === \"busy\") {\n entry.stoppedAt = now\n }\n if (status !== \"unknown\") entry.status = status\n return { busySince: entry.busySince, stoppedAt: entry.stoppedAt }\n },\n }\n}\n\n/**\n * The fill a session's bar has reached this turn, so the bar only ever\n * advances within a turn: the estimate's total rises as the turn outlasts\n * recorded ones, and a cell that went dark again would read as work undone.\n * Keyed by the turn's own start, so a new turn starts the bar over.\n */\nexport function createFillMemory() {\n const held = new Map<string, { turn: number; fill: number }>()\n return {\n hold(sessionID: string, turn: number, fill: number): number {\n const entry = held.get(sessionID)\n if (entry && entry.turn === turn) {\n entry.fill = Math.max(entry.fill, fill)\n return entry.fill\n }\n if (!entry && held.size >= STATUS_WATCH_CAP) {\n const oldest = held.keys().next().value\n if (oldest !== undefined) held.delete(oldest)\n }\n held.set(sessionID, { turn, fill })\n return fill\n },\n }\n}\n\n/**\n * How many cells a plan fills: proportional and rounded, but never the whole\n * bar before the last item is done and never none once the first is, so the\n * bar can only say \"finished\" when the list does.\n */\nexport function filledCells(plan: ProgressPlan, width: number = BAR_WIDTH): number {\n if (plan.total <= 0 || plan.done <= 0) return 0\n if (plan.done >= plan.total) return width\n return Math.min(width - 1, Math.max(1, Math.round((plan.done / plan.total) * width)))\n}\n\n/**\n * How many cells an estimate fills: proportional and rounded, never the\n * whole bar while recorded turns remain to outlast, because the full bar is\n * the `over` state's and must always mean the same thing.\n */\nexport function estimateCells(estimate: TurnEstimate, width: number = BAR_WIDTH): number {\n if (estimate.over) return width\n return Math.min(width - 1, Math.max(0, Math.round(estimate.fill * width)))\n}\n\n/** `12s`, `1m03s`, `1h02m`. */\nexport function formatElapsed(ms: number): string {\n const seconds = Math.max(0, Math.floor(ms / 1000))\n if (seconds < 60) return `${seconds}s`\n const minutes = Math.floor(seconds / 60)\n const pad = (value: number) => String(value).padStart(2, \"0\")\n if (minutes < 60) return `${minutes}m${pad(seconds % 60)}s`\n return `${Math.floor(minutes / 60)}h${pad(minutes % 60)}m`\n}\n\n/** The three runs of one line: what leads it, the bar's cells (one segment each, none without a bar), the rest. */\nexport interface ProgressLayout {\n lead: Segment[]\n bar: Segment[]\n tail: Segment[]\n}\n\n/**\n * One line laid out for `width` cells:\n *\n * ⠋ ▰▰▰▰▰▱▱▱▱▱ 2/4 · 1m03s · bash (a plan; lead \"glyph\": the session's own)\n * ▰▰▰▱▱▱▱▱▱▱ 45s/~2m10s · read (an estimate; lead \"indent\": under a row)\n * ⠋ 12s · 3 calls · bash (neither: no bar)\n *\n * The bar is one segment per cell, and two spaces follow it, not one: the\n * maintainer read one as the glyphs running into the time (h #g246). A plan's\n * fraction and an estimate's `elapsed/~total` each take the call count's\n * place. When the line is too wide the call count goes first, then the tool's\n * name is cut, then the tool goes. An ended line keeps no tool (nothing runs),\n * and its bar is full, ✓ or ✗ leading it. Under a row the line sits\n * `ROW_INDENT` in, beside the row's model line (`layoutRow`).\n */\nexport function layoutProgressLine(\n view: ProgressView,\n lead: \"glyph\" | \"indent\",\n width: number = SIDEBAR_ROW_WIDTH,\n frame = 0,\n): ProgressLayout {\n const ended = view.state !== \"running\"\n const endedRole: Segment[\"role\"] = view.state === \"error\" ? \"error\" : \"done\"\n const leadSegments: Segment[] =\n lead === \"indent\"\n ? [{ text: ROW_INDENT, role: \"muted\" }]\n : [\n view.state === \"running\"\n ? { text: SPINNER_FRAMES[frame % SPINNER_FRAMES.length], role: \"spinner\" }\n : { text: view.state === \"done\" ? \"✓\" : \"✗\", role: endedRole },\n { text: \" \", role: \"muted\" },\n ]\n // A plan outranks an estimate, and an ended line is its final state.\n const estimate = !ended && !view.plan ? view.estimate : undefined\n const filled = ended ? BAR_WIDTH : view.plan ? filledCells(view.plan) : estimate ? estimateCells(estimate) : undefined\n const litRole: Segment[\"role\"] = ended ? endedRole : estimate?.over ? \"bar-over\" : \"bar-done\"\n const bar: Segment[] = []\n if (filled !== undefined) {\n for (let cell = 0; cell < BAR_WIDTH; cell += 1) {\n bar.push(cell < filled ? { text: BAR_DONE, role: litRole } : { text: BAR_TODO, role: \"bar-track\" })\n }\n }\n\n const required: Segment[] = []\n if (view.plan) {\n required.push({ text: `${view.plan.done}/${view.plan.total}`, role: ended ? \"muted\" : \"fraction\" })\n required.push({ text: formatElapsed(view.elapsedMs), role: \"muted\" })\n } else if (estimate) {\n required.push({\n text: `${formatElapsed(view.elapsedMs)}/~${formatElapsed(estimate.totalMs)}`,\n role: estimate.over ? \"estimate-over\" : \"estimate\",\n })\n } else {\n required.push({ text: formatElapsed(view.elapsedMs), role: \"muted\" })\n }\n const calls: Segment | undefined =\n !view.plan && !estimate && view.calls > 0\n ? { text: `${view.calls} call${view.calls === 1 ? \"\" : \"s\"}`, role: \"muted\" }\n : undefined\n const tool: Segment | undefined = !ended && view.tool ? { text: view.tool, role: \"tool\" } : undefined\n // The two spaces after the bar are the 2.\n const budget =\n width - leadSegments.reduce((sum, segment) => sum + cells(segment.text), 0) - (bar.length > 0 ? BAR_WIDTH + 2 : 0)\n const widthOf = (pieces: Segment[]) =>\n pieces.reduce((sum, piece) => sum + cells(piece.text), 0) + 3 * Math.max(0, pieces.length - 1)\n let pieces = [...required, ...(calls ? [calls] : []), ...(tool ? [tool] : [])]\n if (widthOf(pieces) > budget && calls) pieces = [...required, ...(tool ? [tool] : [])]\n if (widthOf(pieces) > budget && tool) {\n const room = budget - widthOf(required) - 3\n pieces = room >= 2 ? [...required, { text: truncate(tool.text, room), role: \"tool\" }] : required\n }\n const tail: Segment[] = bar.length > 0 ? [{ text: \" \", role: \"muted\" }] : []\n pieces.forEach((piece, index) => {\n if (index > 0) tail.push({ text: \" · \", role: \"muted\" })\n tail.push(piece)\n })\n return { lead: leadSegments, bar, tail }\n}\n\n/** The plain text of a laid-out line, for tests. */\nexport function progressLineText(layout: ProgressLayout): string {\n return [...layout.lead, ...layout.bar, ...layout.tail].map((segment) => segment.text).join(\"\")\n}\n","/**\n * What each opencode session's `claude` process REALLY spawned as: model,\n * effort and account. Written by the server half on every turn that reaches\n * the CLI, read by the TUI half's `Subagents` sidebar section (`src/tui.ts`).\n *\n * ## Why a file, and why the TUI cannot read this anywhere else\n *\n * Measured on opencode 1.18.35 (h #g234): an `implementor` subagent whose\n * agent file says `forceModel: claude-sonnet-5-5` and `reasoningEffort: high`\n * spawned as exactly that, while the finished `task` part's `metadata.model`\n * said `claude-code-default/claude-opus-5-5` and no message carried an effort\n * at all. Everything that changes a spawn on the plugin side (an agent's\n * `forceModel` and `reasoningEffort`, `defaultSubagentModel`, the dispatch form\n * of h #g227, the fallback chain of h #g162, an account failover) is invisible\n * to opencode by construction, so showing opencode's own view would be showing\n * the wrong model exactly when the operator configured it not to be. The two\n * halves do not share memory on either major (1.x runs the server half in a\n * worker thread, 2.x in a subprocess), so the record goes through the state\n * directory the resume and dispatch stores already use.\n *\n * ## Shape, and what is never written\n *\n * ```json\n * { \"version\": 1, \"sessions\": {\n * \"ses_abc\": { \"model\": \"claude-sonnet-5-5\", \"effort\": \"high\", \"account\": \"alpha\", \"agent\": \"implementor\", \"at\": 1760000000000 } } }\n * ```\n *\n * A model id, an effort level, an account NAME and an agent name: the three\n * strings the dispatch store keeps (h #g228) plus the agent the session runs\n * as, none secret. No prompt, no path, no session key. `account` is written\n * only on a multi-account install, where it tells two configured accounts\n * apart; on a single-account install there is nothing to tell apart and it\n * stays out. `agent` is what the turn-history key needs beside the model\n * (h #g247): the TUI knows a child's agent from its title but the session on\n * screen's from nothing else.\n *\n * ## Cost and failure\n *\n * One write per session per process, and another only when what it spawns as\n * changes: `recordSessionSpawn` compares against what this process last wrote\n * and returns before touching the disk otherwise. Same discipline as the\n * resume store (h #g197): merge-on-write so two opencode processes do not erase\n * each other, atomic rename, `0600` in the `0700` directory, bounded at\n * `MAX_SPAWN_RECORDS` newest sessions and `SPAWN_RECORD_MAX_AGE_MS`. A missing,\n * unreadable or half-written file is an empty store, which costs the sidebar\n * its effort and falls back to the model opencode asked for. Two writers racing\n * can drop one entry; the next turn of that session writes it again, and the\n * only thing it ever costs is a label.\n *\n * Imports nothing that logs, because the TUI bundle reads it on the terminal's\n * own thread (h #g193): every function here is silent and never throws.\n */\nimport { mkdirSync, readFileSync, renameSync, statSync, writeFileSync } from \"node:fs\"\nimport { homedir } from \"node:os\"\nimport { join } from \"node:path\"\n\nexport interface SpawnRecord {\n model?: string\n effort?: string\n account?: string\n /** The opencode agent the session ran as: `build`, `explore`, a subagent's type. */\n agent?: string\n at: number\n}\n\n/** Sessions worth remembering at once. A subagent fan-out names a handful. */\nexport const MAX_SPAWN_RECORDS = 256\n\n/** A session not spawned this long ago is not one the sidebar is showing. */\nexport const SPAWN_RECORD_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000\n\nlet pathOverride: string | null = null\nconst lastWritten = new Map<string, string>()\n\nexport function spawnRecordStorePath(): string {\n if (pathOverride) return pathOverride\n const stateRoot = process.env.XDG_STATE_HOME || join(homedir(), \".local\", \"state\")\n return join(stateRoot, \"opencode-claude-code-plugin\", \"session-spawns.json\")\n}\n\nfunction isRecord(value: unknown): value is SpawnRecord {\n const record = value as SpawnRecord | null\n if (!record || typeof record !== \"object\" || Array.isArray(record)) return false\n if (typeof record.at !== \"number\" || !Number.isFinite(record.at)) return false\n const ok = (field: unknown) => field === undefined || typeof field === \"string\"\n if (!ok(record.model) || !ok(record.effort) || !ok(record.account) || !ok(record.agent)) return false\n return !!record.model\n}\n\nfunction readEntries(file: string): Map<string, SpawnRecord> {\n const out = new Map<string, SpawnRecord>()\n try {\n const parsed = JSON.parse(readFileSync(file, \"utf8\"))\n const entries = parsed && typeof parsed === \"object\" ? parsed.sessions : null\n if (!entries || typeof entries !== \"object\") return out\n const cutoff = Date.now() - SPAWN_RECORD_MAX_AGE_MS\n const valid = Object.entries(entries as Record<string, unknown>)\n .filter((entry): entry is [string, SpawnRecord] => isRecord(entry[1]))\n .filter(([, record]) => record.at >= cutoff)\n .sort((a, b) => a[1].at - b[1].at)\n for (const [key, record] of valid) {\n out.set(key, {\n model: record.model,\n ...(record.effort ? { effort: record.effort } : {}),\n ...(record.account ? { account: record.account } : {}),\n ...(record.agent ? { agent: record.agent } : {}),\n at: record.at,\n })\n }\n } catch {\n // ENOENT before the first spawn, or a file another process is renaming\n // into place: the empty store is the answer.\n }\n return out\n}\n\n/**\n * Record what `sessionID` spawned as. Returns whether the disk was written,\n * which is false for an unchanged record, an unusable id and a failed write\n * alike: the caller has nothing to do about any of them.\n */\nexport function recordSessionSpawn(\n sessionID: string,\n spawn: { model: string; effort?: string; account?: string; agent?: string },\n now: number = Date.now(),\n): boolean {\n if (!sessionID || sessionID === \"default\" || !spawn.model) return false\n const record: SpawnRecord = {\n model: spawn.model,\n ...(spawn.effort ? { effort: spawn.effort } : {}),\n ...(spawn.account ? { account: spawn.account } : {}),\n ...(spawn.agent ? { agent: spawn.agent } : {}),\n at: now,\n }\n const fingerprint = `${record.model}\\n${record.effort ?? \"\"}\\n${record.account ?? \"\"}\\n${record.agent ?? \"\"}`\n if (lastWritten.get(sessionID) === fingerprint) return false\n const file = spawnRecordStorePath()\n const merged = readEntries(file)\n merged.delete(sessionID)\n merged.set(sessionID, record)\n const cutoff = now - SPAWN_RECORD_MAX_AGE_MS\n const kept = [...merged.entries()]\n .filter(([, entry]) => entry.at >= cutoff)\n .sort((a, b) => a[1].at - b[1].at)\n .slice(-MAX_SPAWN_RECORDS)\n try {\n mkdirSync(join(file, \"..\"), { recursive: true, mode: 0o700 })\n const tmp = `${file}.${process.pid}.tmp`\n writeFileSync(tmp, JSON.stringify({ version: 1, sessions: Object.fromEntries(kept) }), {\n mode: 0o600,\n })\n renameSync(tmp, file)\n } catch {\n return false\n }\n if (lastWritten.size >= MAX_SPAWN_RECORDS) lastWritten.clear()\n lastWritten.set(sessionID, fingerprint)\n return true\n}\n\n/**\n * A reader that re-parses the file only when it changed. The TUI calls `read()`\n * on every refresh; an unchanged file costs one `stat`.\n */\nexport function createSpawnRecordReader(file: () => string = spawnRecordStorePath) {\n let identity = \"\"\n let cached = new Map<string, SpawnRecord>()\n return {\n read(): Map<string, SpawnRecord> {\n const path = file()\n let next = \"\"\n try {\n const stats = statSync(path)\n next = `${stats.mtimeMs}:${stats.size}:${stats.ino}`\n } catch {\n next = \"missing\"\n }\n if (next !== identity) {\n identity = next\n cached = next === \"missing\" ? new Map() : readEntries(path)\n }\n return cached\n },\n }\n}\n\nexport function _setSpawnRecordStorePathForTests(path: string | null): void {\n pathOverride = path\n lastWritten.clear()\n}\n","/**\n * How long a turn usually takes, for the sidebar's progress bar (h #g247):\n * one sample per finished turn, keyed by the agent and the model it ran as,\n * written by the server half at the CLI's `result` and read by the TUI half\n * (`src/tui.ts`), plus the estimate the bar is drawn from.\n *\n * ## What is estimated, and how\n *\n * A turn's length is not knowable up front: it depends on what the tools\n * return, which has not happened yet. What IS known is how long turns like\n * it took before, so the bar is drawn from this install's own record. The\n * key is the agent and the model, the two inputs that move a duration most;\n * the prompt's length and the effort ride on the sample and weight it.\n *\n * At elapsed `t` the estimate is the weighted median residual life: over the\n * recorded turns that lasted LONGER than `t` (the ones this turn can still\n * be like), the weighted median of what remained of them at `t`. The total\n * is `t` plus that, and the bar is `t` over the total. Conditioning on\n * survival is what keeps the bar honest once a turn runs long: the total\n * rises with `t` instead of the bar sitting full. The weights prefer turns\n * that started like this one: a Gaussian kernel on the log of the prompt's\n * length (`CHARS_BANDWIDTH`, so a prompt four times longer or shorter still\n * weighs e^-1/2, sixteen times e^-2), and `OTHER_EFFORT_WEIGHT` for a turn\n * at another effort. Past the longest recorded turn there is nothing left to\n * condition on, and the estimate says so (`over`): the bar is full, drawn in\n * the warning colour, and the total shown is the usual one, the weighted\n * median of every sample.\n *\n * Fewer than `MIN_TURN_SAMPLES` samples is no estimate at all, and the line\n * then draws no bar rather than a guess. The first turns of a new agent or\n * model show the spinner, the elapsed, the calls and the tool.\n *\n * ## The store\n *\n * ```json\n * { \"version\": 1, \"turns\": { \"build/claude-opus-5-5\": [\n * { \"ms\": 61230, \"chars\": 420, \"effort\": \"high\", \"at\": 1760000000000 } ] } }\n * ```\n *\n * A duration, a character count, an effort level and a time: no prompt text,\n * no path, no session id, nothing secret. Same discipline as the spawn\n * record store (h #g234): merge-on-write so two opencode processes do not\n * erase each other, atomic rename, `0600` in the `0700` directory, bounded\n * at `MAX_TURN_SAMPLES` newest samples per key, `MAX_TURN_KEYS` keys and\n * `TURN_SAMPLE_MAX_AGE_MS`. A missing, unreadable or half-written file is\n * an empty store, which costs a bar and nothing else.\n *\n * Imports nothing that logs, because the TUI bundle reads it on the\n * terminal's own thread (h #g193): every function here is silent and never\n * throws.\n */\nimport { mkdirSync, readFileSync, renameSync, statSync, writeFileSync } from \"node:fs\"\nimport { homedir } from \"node:os\"\nimport { join } from \"node:path\"\n\nexport interface TurnSample {\n /** How long the turn took, from the prompt reaching the provider to the CLI's `result`. */\n ms: number\n /** The prompt's length in characters, the host's injected blocks taken off; absent when unknown. */\n chars?: number\n effort?: string\n at: number\n}\n\n/** Samples per key, oldest first. */\nexport type TurnHistory = Map<string, TurnSample[]>\n\n/** Samples kept per agent and model: enough for a median, few enough to read every tick. */\nexport const MAX_TURN_SAMPLES = 48\n/** Agent and model pairs kept at once. */\nexport const MAX_TURN_KEYS = 64\n/** A turn older than this says little about how the agent runs now. */\nexport const TURN_SAMPLE_MAX_AGE_MS = 30 * 24 * 60 * 60 * 1000\n/** Fewer samples than this is no estimate. */\nexport const MIN_TURN_SAMPLES = 3\n/** The prompt-length kernel's bandwidth, in log-length: a factor of four is one standard deviation. */\nexport const CHARS_BANDWIDTH = Math.log(4)\n/** What a sample at another effort weighs against one at the same. */\nexport const OTHER_EFFORT_WEIGHT = 0.5\n/** Added to both lengths before the log, so a one-word prompt is not infinitely far from an empty one. */\nconst CHARS_FLOOR = 50\n\nlet pathOverride: string | null = null\n\nexport function turnHistoryStorePath(): string {\n if (pathOverride) return pathOverride\n const stateRoot = process.env.XDG_STATE_HOME || join(homedir(), \".local\", \"state\")\n return join(stateRoot, \"opencode-claude-code-plugin\", \"turn-history.json\")\n}\n\n/** The key a turn's samples live under: the agent it ran as and the model, `build/claude-opus-5-5`. */\nexport function turnHistoryKey(agent: string | undefined, model: string): string {\n return `${agent || \"main\"}/${model}`\n}\n\n// ---------------------------------------------------------------------------\n// The prompt's length, counted the same on both halves\n// ---------------------------------------------------------------------------\n\nconst INJECTED_BLOCKS = [\n /<system-reminder>[\\s\\S]*?<\\/system-reminder>/g,\n /<dcp-system-reminder>[\\s\\S]*?<\\/dcp-system-reminder>/g,\n /<dcp-message-id>[\\s\\S]*?<\\/dcp-message-id>/g,\n]\n\n/**\n * How long a prompt is, in characters, with the blocks a host injects at\n * provider time taken off (opencode's `<system-reminder>`, opencode-dcp's\n * reminder and its message id): the TUI counts the stored message and the\n * server the forwarded one, and the kernel needs both to count the same\n * text. `undefined` for no text at all, which is \"unknown\", never zero.\n */\nexport function promptChars(texts: readonly string[]): number | undefined {\n if (texts.length === 0) return undefined\n let total = 0\n for (const text of texts) {\n let stripped = text\n for (const block of INJECTED_BLOCKS) stripped = stripped.replace(block, \"\")\n total += stripped.trim().length\n }\n return total\n}\n\ninterface PromptMessageLike {\n role: string\n content: unknown\n}\n\nfunction textsOf(content: unknown): string[] {\n if (typeof content === \"string\") return [content]\n if (!Array.isArray(content)) return []\n const texts: string[] = []\n for (const part of content) {\n if (!part || typeof part !== \"object\" || (part as { type?: unknown }).type !== \"text\") continue\n const text = (part as { text?: unknown }).text\n if (typeof text === \"string\") texts.push(text)\n }\n return texts\n}\n\n/** The newest user message's length: the one this turn answers. */\nexport function newestPromptChars(prompt: readonly PromptMessageLike[]): number | undefined {\n for (let index = prompt.length - 1; index >= 0; index -= 1) {\n if (prompt[index].role === \"user\") return promptChars(textsOf(prompt[index].content))\n }\n return undefined\n}\n\n/**\n * Whether a prompt STARTS a turn: it ends in a user message that does not\n * follow a tool result. A tool-result prompt continues the turn that made\n * the call, and so does a user message the host promoted beside one (h #g204).\n */\nexport function startsTurn(prompt: readonly PromptMessageLike[]): boolean {\n const last = prompt[prompt.length - 1]\n if (!last || last.role !== \"user\") return false\n const before = prompt[prompt.length - 2]\n return before === undefined || before.role !== \"tool\"\n}\n\n// ---------------------------------------------------------------------------\n// When a turn started, held from its first step to its result\n// ---------------------------------------------------------------------------\n\nconst MAX_TURN_STARTS = 256\nconst turnStarts = new Map<string, { at: number; chars?: number }>()\n\n/**\n * Note that a turn on `key` started at `at`. A turn is a run of steps (every\n * proxied tool call ends one), and only its last step sees the `result`, so\n * the start waits here, keyed like the process, until `takeTurnStart`. A\n * turn that never reaches a result leaves its start to be overwritten by\n * the key's next one.\n */\nexport function markTurnStart(key: string, at: number, chars?: number): void {\n turnStarts.delete(key)\n if (turnStarts.size >= MAX_TURN_STARTS) {\n const oldest = turnStarts.keys().next().value\n if (oldest !== undefined) turnStarts.delete(oldest)\n }\n turnStarts.set(key, { at, ...(chars !== undefined ? { chars } : {}) })\n}\n\n/** The start noted for `key`, once: a second `result` in the same turn finds nothing. */\nexport function takeTurnStart(key: string): { at: number; chars?: number } | undefined {\n const start = turnStarts.get(key)\n turnStarts.delete(key)\n return start\n}\n\n// ---------------------------------------------------------------------------\n// The store\n// ---------------------------------------------------------------------------\n\nfunction isSample(value: unknown): value is TurnSample {\n const sample = value as TurnSample | null\n if (!sample || typeof sample !== \"object\" || Array.isArray(sample)) return false\n if (typeof sample.ms !== \"number\" || !Number.isFinite(sample.ms) || sample.ms < 0) return false\n if (typeof sample.at !== \"number\" || !Number.isFinite(sample.at)) return false\n if (sample.chars !== undefined && (typeof sample.chars !== \"number\" || !Number.isFinite(sample.chars) || sample.chars < 0)) {\n return false\n }\n if (sample.effort !== undefined && typeof sample.effort !== \"string\") return false\n return true\n}\n\nfunction cleanSample(sample: TurnSample): TurnSample {\n return {\n ms: Math.round(sample.ms),\n ...(sample.chars !== undefined ? { chars: Math.round(sample.chars) } : {}),\n ...(sample.effort ? { effort: sample.effort } : {}),\n at: sample.at,\n }\n}\n\nfunction readEntries(file: string, now: number): TurnHistory {\n const out: TurnHistory = new Map()\n try {\n const parsed = JSON.parse(readFileSync(file, \"utf8\"))\n const entries = parsed && typeof parsed === \"object\" ? parsed.turns : null\n if (!entries || typeof entries !== \"object\") return out\n const cutoff = now - TURN_SAMPLE_MAX_AGE_MS\n for (const [key, value] of Object.entries(entries as Record<string, unknown>)) {\n if (!key || !Array.isArray(value)) continue\n const samples = value\n .filter(isSample)\n .filter((sample) => sample.at >= cutoff)\n .sort((a, b) => a.at - b.at)\n .slice(-MAX_TURN_SAMPLES)\n .map(cleanSample)\n if (samples.length > 0) out.set(key, samples)\n }\n } catch {\n // ENOENT before the first turn, or a file another process is renaming\n // into place: the empty store is the answer.\n }\n return out\n}\n\n/** The keys with the newest samples, `MAX_TURN_KEYS` of them. */\nfunction bounded(history: TurnHistory): TurnHistory {\n const newest = (samples: TurnSample[]) => samples[samples.length - 1]?.at ?? 0\n return new Map([...history.entries()].sort((a, b) => newest(a[1]) - newest(b[1])).slice(-MAX_TURN_KEYS))\n}\n\n/**\n * Record one finished turn under `key`. Returns whether the disk was\n * written, which is false for an unusable sample and a failed write alike:\n * the caller has nothing to do about either.\n */\nexport function recordTurnSample(key: string, sample: TurnSample): boolean {\n if (!key || !isSample(sample)) return false\n const file = turnHistoryStorePath()\n const merged = readEntries(file, sample.at)\n const samples = [...(merged.get(key) ?? []), cleanSample(sample)].sort((a, b) => a.at - b.at).slice(-MAX_TURN_SAMPLES)\n merged.set(key, samples)\n const kept = bounded(merged)\n try {\n mkdirSync(join(file, \"..\"), { recursive: true, mode: 0o700 })\n const tmp = `${file}.${process.pid}.tmp`\n writeFileSync(tmp, JSON.stringify({ version: 1, turns: Object.fromEntries(kept) }), { mode: 0o600 })\n renameSync(tmp, file)\n } catch {\n return false\n }\n return true\n}\n\n/**\n * A reader that re-parses the file only when it changed. The TUI calls\n * `read()` on every refresh while something runs; an unchanged file costs\n * one `stat`.\n */\nexport function createTurnHistoryReader(file: () => string = turnHistoryStorePath) {\n let identity = \"\"\n let cached: TurnHistory = new Map()\n return {\n read(now: number = Date.now()): TurnHistory {\n const path = file()\n let next = \"\"\n try {\n const stats = statSync(path)\n next = `${stats.mtimeMs}:${stats.size}:${stats.ino}`\n } catch {\n next = \"missing\"\n }\n if (next !== identity) {\n identity = next\n cached = next === \"missing\" ? new Map() : readEntries(path, now)\n }\n return cached\n },\n }\n}\n\n// ---------------------------------------------------------------------------\n// The estimate\n// ---------------------------------------------------------------------------\n\nexport interface TurnEstimate {\n /** What the whole turn is expected to take, elapsed included; once `over`, the usual duration. */\n totalMs: number\n /** Elapsed over that total, 0 to 1; 1 once past every recorded turn. */\n fill: number\n /** The turn has outlasted every recorded one: nothing is left to condition on. */\n over: boolean\n}\n\n/** The value at which the cumulative weight first reaches half the total. */\nfunction weightedMedian(values: readonly number[], weights: readonly number[]): number {\n const order = values.map((_, index) => index).sort((a, b) => values[a] - values[b])\n const total = order.reduce((sum, index) => sum + weights[index], 0)\n let cumulative = 0\n for (const index of order) {\n cumulative += weights[index]\n if (cumulative >= total / 2) return values[index]\n }\n return values[order[order.length - 1]]\n}\n\nfunction charsWeight(a: number | undefined, b: number | undefined): number {\n if (a === undefined || b === undefined) return 1\n const distance = Math.log((a + CHARS_FLOOR) / (b + CHARS_FLOOR)) / CHARS_BANDWIDTH\n return Math.exp(-0.5 * distance * distance)\n}\n\nfunction effortWeight(a: string | undefined, b: string | undefined): number {\n return a === undefined || b === undefined || a === b ? 1 : OTHER_EFFORT_WEIGHT\n}\n\n/**\n * What a turn that has run `elapsedMs` is expected to take in all, given the\n * recorded turns of its agent and model: the weighted median residual life\n * described at the top. `undefined` with fewer than `MIN_TURN_SAMPLES`\n * samples.\n */\nexport function estimateTurn(\n samples: readonly TurnSample[],\n input: { elapsedMs: number; chars?: number; effort?: string },\n): TurnEstimate | undefined {\n const usable = samples.filter((sample) => Number.isFinite(sample.ms) && sample.ms >= 0)\n if (usable.length < MIN_TURN_SAMPLES) return undefined\n const elapsed = Math.max(0, input.elapsedMs)\n const weights = usable.map(\n (sample) => charsWeight(input.chars, sample.chars) * effortWeight(input.effort, sample.effort),\n )\n const survivors = usable\n .map((sample, index) => ({ ms: sample.ms, weight: weights[index] }))\n .filter((sample) => sample.ms > elapsed)\n if (survivors.length === 0) {\n const usual = weightedMedian(usable.map((sample) => sample.ms), weights)\n return { totalMs: usual, fill: 1, over: true }\n }\n const residual = weightedMedian(\n survivors.map((sample) => sample.ms - elapsed),\n survivors.map((sample) => sample.weight),\n )\n const totalMs = elapsed + residual\n return { totalMs, fill: totalMs > 0 ? elapsed / totalMs : 0, over: false }\n}\n\nexport function _setTurnHistoryStorePathForTests(path: string | null): void {\n pathOverride = path\n turnStarts.clear()\n}\n","/**\n * What each account's usage windows looked like the last time the CLI said,\n * so the operator hears about a window filling up BEFORE it runs out\n * (`usageWarnThresholds`, `usageSwitchAt`, h #g244).\n *\n * ## Where the numbers come from\n *\n * The headless CLI emits `{type:\"rate_limit_event\", rate_limit_info:{status,\n * rateLimitType, resetsAt, utilization, ...}}` whenever its view of the\n * account's limits CHANGES (h #g194). Before this module the plugin kept only\n * a REJECTED one (`rememberAccountLimit`), so the first thing an operator saw\n * was the limit itself. Every event is a reading now, keyed account -> window.\n *\n * Two measured facts shape the record:\n *\n * - `utilization` is a FRACTION, not a percent. Read off the live plugin log\n * on 2026-10-16 and 2026-10-17 (CLI 2.1.293): `allowed_warning` events\n * carrying `0.75` and `0.9`, which the existing NOTICE already rendered as\n * \"75% used\" and \"90% used\". It is stored exactly as the CLI sent it.\n * - An `allowed` event on an account with headroom carries NO utilization at\n * all (`utilization: null` in the CLI's own frame, absent after parsing).\n * That is \"unknown\", never zero: a null reading crosses no threshold and\n * the sidebar shows `ok`, not `0%`.\n *\n * The interactive transport has no event, so the only thing it records is a\n * limit reply: status `rejected`, utilization unknown, and whatever reset the\n * reply's own text names (`limitFactsFromText`, h #g208).\n *\n * ## A window's crossings, and when they clear\n *\n * Each window keeps `noted`, the thresholds (in percent) a `▌ **usage:**` note\n * already announced, so a reused process's next turn, another session on the\n * same account and a restarted opencode all stay quiet about a crossing that\n * was already said. `noted` carries over to a new reading for as long as the\n * PREVIOUS reading is still current, and is cleared once its reset has passed:\n * the reset is what ends a window, and comparing two `resetsAt` values for\n * equality would re-announce a crossing every time the CLI rounded the reset\n * differently. A reading with no reset time (the interactive transport's) is\n * current for `UNTIMED_READING_TTL_MS`.\n *\n * ## Which accounts exist\n *\n * A reading arrives on an account's first turn, so until then the sidebar had\n * nothing to list for it, and an account whose last window had reset vanished\n * from it (h #g248). The server half now registers every configured account\n * at startup (`registerKnownAccounts`, called by `registerSidebarAccounts` in\n * `src/account-tier.ts`): the name and the plan tier read off its own\n * `.claude.json`, under `known` beside the readings. That list is REPLACED on\n * each startup, because it is the configured set and not an observation, and\n * the readings are never touched by it.\n *\n * ## File and failure\n *\n * `$XDG_STATE_HOME/opencode-claude-code-plugin/account-usage.json`, the fifth\n * file the plugin writes there, with the same discipline as the resume and\n * spawn stores (h #g197, h #g234): merge-on-write so two opencode processes do\n * not erase each other, atomic rename, `0600` in a `0700` directory, bounded,\n * and an unreadable or half-written file is an empty store. It holds account\n * NAMES, window names, plan tier labels (`Max 20x`), numbers and times, and\n * nothing else.\n *\n * Imports nothing that logs, because the TUI bundle reads it on the terminal's\n * own thread (h #g193): every function here is silent and never throws.\n */\nimport { mkdirSync, readFileSync, renameSync, statSync, writeFileSync } from \"node:fs\"\nimport { homedir } from \"node:os\"\nimport { join } from \"node:path\"\n\n/** One reading of one window, as the CLI reported it. */\nexport interface UsageReading {\n /** The CLI's fraction (0.75 is 75%), or null when the event carried none. */\n utilization: number | null\n /** Epoch ms the window resets at, when the CLI said. */\n resetsAt?: number\n /**\n * The TUI's own reset phrase (`3pm (Europe/Amsterdam)`), for a limit that\n * reached us only as text. Shown only when `resetsAt` is unknown.\n */\n resetsText?: string\n /** `allowed`, `allowed_warning` or `rejected`, as the CLI sent it. */\n status: string\n}\n\nexport interface WindowUsage extends UsageReading {\n /** Epoch ms this reading was recorded. */\n observedAt: number\n /** Thresholds, in percent, a note already announced in this window. */\n noted: number[]\n}\n\n/** account -> window (the CLI's `rateLimitType`) -> reading. */\nexport type AccountUsage = Map<string, Map<string, WindowUsage>>\n\n/** What the startup registration knows about an account before any reading. */\nexport interface KnownAccount {\n /** `Max 20x`, `Pro`, ...: the plan tier off the account's own `.claude.json`, when readable. */\n tier?: string\n /** Epoch ms the registration that wrote this entry ran. */\n at: number\n}\n\n/** account -> what the server half registered about it at startup. */\nexport type KnownAccounts = Map<string, KnownAccount>\n\nexport const DEFAULT_USAGE_WARN_THRESHOLDS: readonly number[] = [50, 75, 90, 95]\nexport const DEFAULT_USAGE_SWITCH_AT = 90\n\n/**\n * How long a reading with no reset time stays current. The only such reading\n * is the interactive transport's limit reply, and the window it names is the\n * five-hour one (`limitFactsFromText`), so five hours is the longest that\n * reading could still be true.\n */\nexport const UNTIMED_READING_TTL_MS = 5 * 60 * 60 * 1000\n\n/** A reading not refreshed in this long is dropped: past every window's reset. */\nexport const USAGE_READING_MAX_AGE_MS = 8 * 24 * 60 * 60 * 1000\n\nexport const MAX_USAGE_ACCOUNTS = 32\nexport const MAX_USAGE_WINDOWS = 8\n\nconst NAME = /^[a-z0-9_-]{1,64}$/\nconst STATUS = /^[a-z_]{1,32}$/\n/** A plan tier label as `src/account-tier.ts` renders one: `Max 20x`, `Pro`. */\nconst TIER = /^[A-Za-z0-9][A-Za-z0-9 ]{0,23}$/\nconst MAX_RESETS_TEXT = 80\n\nlet pathOverride: string | null = null\nlet cache: { path: string; usage: AccountUsage } | null = null\n\nexport function accountUsageStorePath(): string {\n if (pathOverride) return pathOverride\n const stateRoot = process.env.XDG_STATE_HOME || join(homedir(), \".local\", \"state\")\n return join(stateRoot, \"opencode-claude-code-plugin\", \"account-usage.json\")\n}\n\nfunction finite(value: unknown): value is number {\n return typeof value === \"number\" && Number.isFinite(value)\n}\n\nfunction validEntry(value: unknown): WindowUsage | null {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return null\n const entry = value as Record<string, unknown>\n if (!finite(entry.observedAt)) return null\n if (typeof entry.status !== \"string\" || !STATUS.test(entry.status)) return null\n if (entry.utilization !== null && !finite(entry.utilization)) return null\n if (entry.resetsAt !== undefined && !finite(entry.resetsAt)) return null\n if (\n entry.resetsText !== undefined &&\n (typeof entry.resetsText !== \"string\" || entry.resetsText.length > MAX_RESETS_TEXT)\n ) {\n return null\n }\n const noted = Array.isArray(entry.noted)\n ? [...new Set(entry.noted.filter((t): t is number => finite(t) && t > 0 && t <= 100))].sort(\n (a, b) => a - b,\n )\n : []\n return {\n utilization: entry.utilization as number | null,\n ...(entry.resetsAt === undefined ? {} : { resetsAt: entry.resetsAt as number }),\n ...(entry.resetsText === undefined ? {} : { resetsText: entry.resetsText as string }),\n status: entry.status,\n observedAt: entry.observedAt,\n noted,\n }\n}\n\n/** The file's two maps: the readings, and the accounts registered at startup. */\ninterface StoreFile {\n usage: AccountUsage\n known: KnownAccounts\n}\n\nconst emptyStore = (): StoreFile => ({ usage: new Map(), known: new Map() })\n\nfunction readKnown(parsed: Record<string, unknown>): KnownAccounts {\n const out: KnownAccounts = new Map()\n const known = parsed.known\n if (!known || typeof known !== \"object\" || Array.isArray(known)) return out\n for (const [account, raw] of Object.entries(known as Record<string, unknown>)) {\n if (out.size >= MAX_USAGE_ACCOUNTS) break\n if (!NAME.test(account) || !raw || typeof raw !== \"object\" || Array.isArray(raw)) continue\n const record = raw as Record<string, unknown>\n if (!finite(record.at)) continue\n const tier = typeof record.tier === \"string\" && TIER.test(record.tier) ? record.tier : undefined\n out.set(account, { ...(tier ? { tier } : {}), at: record.at })\n }\n return out\n}\n\nfunction parseStore(file: string, now: number): StoreFile {\n const out = emptyStore()\n try {\n const parsed = JSON.parse(readFileSync(file, \"utf8\"))\n if (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) return out\n out.known = readKnown(parsed as Record<string, unknown>)\n const accounts = (parsed as Record<string, unknown>).accounts\n if (!accounts || typeof accounts !== \"object\" || Array.isArray(accounts)) return out\n const cutoff = now - USAGE_READING_MAX_AGE_MS\n for (const [account, windows] of Object.entries(accounts as Record<string, unknown>)) {\n if (!NAME.test(account) || !windows || typeof windows !== \"object\") continue\n const kept = new Map<string, WindowUsage>()\n for (const [window, raw] of Object.entries(windows as Record<string, unknown>)) {\n if (!NAME.test(window)) continue\n const entry = validEntry(raw)\n if (entry && entry.observedAt >= cutoff) kept.set(window, entry)\n }\n if (kept.size > 0) out.usage.set(account, kept)\n }\n } catch {\n // ENOENT before the first event, or a file another process is renaming\n // into place: the empty store is the answer.\n }\n return out\n}\n\nfunction readStore(file: string, now: number): AccountUsage {\n return parseStore(file, now).usage\n}\n\nfunction newestObserved(windows: Map<string, WindowUsage>): number {\n let newest = 0\n for (const entry of windows.values()) newest = Math.max(newest, entry.observedAt)\n return newest\n}\n\nfunction bounded(usage: AccountUsage, now: number): AccountUsage {\n const cutoff = now - USAGE_READING_MAX_AGE_MS\n const accounts = [...usage.entries()]\n .map(([account, windows]) => {\n const kept = [...windows.entries()]\n .filter(([, entry]) => entry.observedAt >= cutoff)\n .sort((a, b) => a[1].observedAt - b[1].observedAt)\n .slice(-MAX_USAGE_WINDOWS)\n return [account, new Map(kept)] as const\n })\n .filter(([, windows]) => windows.size > 0)\n .sort((a, b) => newestObserved(a[1]) - newestObserved(b[1]))\n .slice(-MAX_USAGE_ACCOUNTS)\n return new Map(accounts)\n}\n\nfunction writeStore(file: string, usage: AccountUsage, known: KnownAccounts): boolean {\n const accounts: Record<string, Record<string, WindowUsage>> = {}\n for (const [account, windows] of usage) accounts[account] = Object.fromEntries(windows)\n // `known` is written once something registered; a file no startup touched\n // keeps its old shape.\n const registered = known.size > 0 ? { known: Object.fromEntries(known) } : {}\n try {\n mkdirSync(join(file, \"..\"), { recursive: true, mode: 0o700 })\n const tmp = `${file}.${process.pid}.tmp`\n writeFileSync(tmp, JSON.stringify({ version: 1, accounts, ...registered }), { mode: 0o600 })\n renameSync(tmp, file)\n return true\n } catch {\n return false\n }\n}\n\n/** This process's view, loaded from disk once and kept current by its own writes. */\nfunction cached(now: number): AccountUsage {\n const file = accountUsageStorePath()\n if (!cache || cache.path !== file) cache = { path: file, usage: readStore(file, now) }\n return cache.usage\n}\n\n/** Re-read the file into the cache, for a decision another process may have made. */\nfunction refreshed(now: number): AccountUsage {\n const file = accountUsageStorePath()\n cache = { path: file, usage: readStore(file, now) }\n return cache.usage\n}\n\n/**\n * Whether a reading still describes the window it was taken in: its reset is\n * ahead, or, with no reset time, it is younger than `UNTIMED_READING_TTL_MS`.\n */\nexport function isReadingCurrent(entry: WindowUsage, now: number = Date.now()): boolean {\n if (entry.resetsAt !== undefined) return entry.resetsAt > now\n return now - entry.observedAt < UNTIMED_READING_TTL_MS\n}\n\n/**\n * The record a new reading leaves behind. The thresholds already announced\n * carry over only while the previous reading is still current; once its reset\n * has passed, the window has turned over and every crossing may be said again.\n */\nexport function applyReading(\n previous: WindowUsage | undefined,\n reading: UsageReading,\n now: number = Date.now(),\n): WindowUsage {\n const carried = previous && isReadingCurrent(previous, now) ? previous.noted : []\n return {\n utilization: finite(reading.utilization) ? reading.utilization : null,\n ...(finite(reading.resetsAt) ? { resetsAt: reading.resetsAt } : {}),\n ...(reading.resetsText && !finite(reading.resetsAt)\n ? { resetsText: reading.resetsText.slice(0, MAX_RESETS_TEXT) }\n : {}),\n status: STATUS.test(reading.status) ? reading.status : \"unknown\",\n observedAt: now,\n noted: [...carried],\n }\n}\n\n/** The CLI's fraction as a percent, to one decimal so `0.9` is exactly 90. */\nexport function usagePercent(utilization: number): number {\n return Math.round(utilization * 1000) / 10\n}\n\n/**\n * The thresholds this reading crosses that no note announced yet. None for a\n * reading that is no longer current, one whose utilization is unknown, and a\n * rejection: a window that ran out gets the `▌ **usage limit:**` note instead.\n */\nexport function pendingCrossings(\n entry: WindowUsage,\n thresholds: readonly number[],\n now: number = Date.now(),\n): number[] {\n if (!isReadingCurrent(entry, now)) return []\n if (entry.utilization === null || entry.status === \"rejected\") return []\n const percent = usagePercent(entry.utilization)\n return [...new Set(thresholds)]\n .filter((threshold) => percent >= threshold && !entry.noted.includes(threshold))\n .sort((a, b) => a - b)\n}\n\n/**\n * `usageWarnThresholds`: percents above 0 and up to 100, deduplicated and\n * sorted. Unset is the default list; an explicit empty list turns the notes off.\n */\nexport function resolveUsageWarnThresholds(value: unknown): number[] {\n if (!Array.isArray(value)) return [...DEFAULT_USAGE_WARN_THRESHOLDS]\n return [...new Set(value.filter((t): t is number => finite(t) && t > 0 && t <= 100))].sort(\n (a, b) => a - b,\n )\n}\n\n/**\n * `usageSwitchAt`: a percent above 0 and up to 100, or null for \"never\". `false`\n * and `0` turn it off; anything unusable is the default.\n */\nexport function resolveUsageSwitchAt(value: unknown): number | null {\n if (value === false || value === 0) return null\n if (finite(value) && value > 0 && value <= 100) return value\n return DEFAULT_USAGE_SWITCH_AT\n}\n\n/**\n * Record one reading. Returns the record left behind, or null when the account\n * or window name is unusable or the write failed (the in-process view still\n * takes the reading, so this turn's note is not lost to a read-only disk).\n */\nexport function recordUsageReading(\n account: string,\n window: string,\n reading: UsageReading,\n now: number = Date.now(),\n): WindowUsage | null {\n if (!NAME.test(account) || !NAME.test(window)) return null\n const file = accountUsageStorePath()\n const store = parseStore(file, now)\n const merged = store.usage\n const previous = merged.get(account)?.get(window) ?? cached(now).get(account)?.get(window)\n const entry = applyReading(previous, reading, now)\n const windows = merged.get(account) ?? new Map<string, WindowUsage>()\n windows.set(window, entry)\n merged.set(account, windows)\n const kept = bounded(merged, now)\n cache = { path: file, usage: kept }\n return writeStore(file, kept, store.known) ? entry : null\n}\n\n/** Mark thresholds announced, on the window's newest reading. */\nexport function markUsageNoted(\n account: string,\n window: string,\n thresholds: readonly number[],\n now: number = Date.now(),\n): boolean {\n if (!NAME.test(account) || !NAME.test(window) || thresholds.length === 0) return false\n const file = accountUsageStorePath()\n const store = parseStore(file, now)\n const merged = store.usage\n const entry = merged.get(account)?.get(window) ?? cached(now).get(account)?.get(window)\n if (!entry) return false\n const updated = {\n ...entry,\n noted: [...new Set([...entry.noted, ...thresholds])].sort((a, b) => a - b),\n }\n const windows = merged.get(account) ?? new Map<string, WindowUsage>()\n windows.set(window, updated)\n merged.set(account, windows)\n const kept = bounded(merged, now)\n cache = { path: file, usage: kept }\n return writeStore(file, kept, store.known)\n}\n\n/** Whether two registrations name the same accounts with the same tiers. */\nfunction sameKnown(a: KnownAccounts, b: KnownAccounts): boolean {\n if (a.size !== b.size) return false\n for (const [account, record] of a) {\n const other = b.get(account)\n if (!other || other.tier !== record.tier) return false\n }\n return true\n}\n\n/**\n * Every account this install is configured with, from the process starting\n * up, so the sidebar lists them all before any reading arrives (h #g248). The\n * list REPLACES the one on disk rather than merging into it: it is the\n * configured set, not an accumulated observation, and merging would keep an\n * account the operator removed on screen until it aged out. The readings are\n * untouched. Nothing is written while the file already says the same, so a\n * second pass (opencode 2 runs its provider transform more than once) costs\n * one read. Returns false only when a write was needed and failed.\n */\nexport function registerKnownAccounts(\n accounts: ReadonlyArray<{ account: string; tier?: string }>,\n now: number = Date.now(),\n): boolean {\n const file = accountUsageStorePath()\n const store = parseStore(file, now)\n const known: KnownAccounts = new Map()\n for (const entry of accounts) {\n if (known.size >= MAX_USAGE_ACCOUNTS) break\n if (!NAME.test(entry.account)) continue\n const tier = entry.tier && TIER.test(entry.tier) ? entry.tier : undefined\n known.set(entry.account, { ...(tier ? { tier } : {}), at: now })\n }\n if (sameKnown(store.known, known)) return true\n return writeStore(file, bounded(store.usage, now), known)\n}\n\n/** The accounts registered at startup, read fresh off disk. For `/claude-code-doctor`. */\nexport function snapshotKnownAccounts(now: number = Date.now()): KnownAccounts {\n return parseStore(accountUsageStorePath(), now).known\n}\n\nexport interface UsageCrossing {\n window: string\n entry: WindowUsage\n /** Newly crossed thresholds, in percent, ascending. */\n crossed: number[]\n}\n\n/**\n * Every window of `account` with a threshold to announce. Answered from this\n * process's view, which costs no I/O on an ordinary turn; only when something\n * looks pending is the file re-read, so a crossing another opencode process\n * already announced is not announced twice.\n */\nexport function usageCrossings(\n account: string,\n thresholds: readonly number[],\n now: number = Date.now(),\n): UsageCrossing[] {\n if (thresholds.length === 0) return []\n const collect = (usage: AccountUsage): UsageCrossing[] => {\n const out: UsageCrossing[] = []\n for (const [window, entry] of usage.get(account) ?? []) {\n const crossed = pendingCrossings(entry, thresholds, now)\n if (crossed.length > 0) out.push({ window, entry, crossed })\n }\n return out\n }\n if (collect(cached(now)).length === 0) return []\n return collect(refreshed(now))\n}\n\n/**\n * Whether any current window of `account` is at or over `percent`, or was\n * refused outright. What a switch at a threshold reads, so it never moves new\n * turns onto an account that is no better off.\n */\nexport function accountUsageAtOrOver(\n account: string,\n percent: number,\n now: number = Date.now(),\n): boolean {\n for (const entry of cached(now).get(account)?.values() ?? []) {\n if (!isReadingCurrent(entry, now)) continue\n if (entry.status === \"rejected\") return true\n if (entry.utilization !== null && usagePercent(entry.utilization) >= percent) return true\n }\n return false\n}\n\n/** Every recorded reading, read fresh off disk. For `/claude-code-doctor`. */\nexport function snapshotAccountUsage(now: number = Date.now()): AccountUsage {\n return refreshed(now)\n}\n\n/**\n * A reader that re-parses the file only when it changed, for the TUI's\n * refresh clock: an unchanged file costs one `stat`.\n */\nexport function createAccountUsageReader(file: () => string = accountUsageStorePath) {\n let identity = \"\"\n let store = emptyStore()\n const refresh = () => {\n const path = file()\n let next = \"\"\n try {\n const stats = statSync(path)\n next = `${stats.mtimeMs}:${stats.size}:${stats.ino}`\n } catch {\n next = \"missing\"\n }\n if (next !== identity) {\n identity = next\n store = next === \"missing\" ? emptyStore() : parseStore(path, Date.now())\n }\n }\n return {\n read(): AccountUsage {\n refresh()\n return store.usage\n },\n /** The accounts registered at startup, with their tiers: the same map while the file is unchanged. */\n known(): KnownAccounts {\n refresh()\n return store.known\n },\n }\n}\n\n/** Test-only. Points the store at another file and forgets the cache. */\nexport function _setAccountUsageStorePathForTests(path: string | null): void {\n pathOverride = path\n cache = null\n}\n","/**\n * The model behind the sidebar's `Accounts` section (`src/tui.ts`): one line\n * per account this install runs, from the sidebar's first draw (h #g248).\n * With a current usage reading the line is `appical · 5h 72% · resets 18:00`\n * (`appical · 5h 72% · 2026-10-18 01:20` for a reset on another day, h #g244,\n * h #g246); before one has arrived it is `appical · Max 20x · no reading yet`.\n *\n * Pure, like `src/subagent-sidebar.ts`, and for the same reason: the TUI bundle\n * runs on the terminal's own thread, where a logger writes over the interface\n * (h #g193). It imports only the store's reader types and that module's pure\n * helpers.\n *\n * Which accounts: every account the server half registered at startup (the\n * configured list, or the one `default` account) plus any with a reading,\n * alphabetically. Which window an account's line shows: a refused one first\n * (the account is out now, whatever its other windows say), else the fullest\n * known one, else any current reading (`ok`, a window the CLI reported without\n * a utilization, which is how it reports one with headroom). A reading whose\n * reset has passed describes a window that is over and is not shown; the\n * account then reads `no reading yet` again, with the plan tier read off its\n * own `.claude.json` (`src/account-tier.ts`) when the store has one.\n *\n * Colours, by segment: the name in the text colour; the level in its state's\n * (`limited` in the error colour, 75% and up in the warning colour, else\n * muted); the reset time in its own (`reset`, the theme's info colour), so it\n * reads apart from the level beside it; the word `resets` and every separator\n * muted; the tier in its own (`tier`, the secondary colour).\n */\nimport {\n isReadingCurrent,\n usagePercent,\n type AccountUsage,\n type KnownAccounts,\n type WindowUsage,\n} from \"./account-usage.js\"\nimport { SIDEBAR_ROW_WIDTH, truncate, type Segment } from \"./subagent-sidebar.js\"\n\nexport interface AccountUsageLine {\n account: string\n /** The window the line is about; empty on a line with no current reading. */\n window: string\n /**\n * `limited` (refused), `warning` (at or past `WARNING_PERCENT`), `ok`, or\n * `none` for an account no current reading describes.\n */\n state: \"limited\" | \"warning\" | \"ok\" | \"none\"\n segments: Segment[]\n}\n\n/** Where a line takes the theme's warning colour: the CLI's own first warning level. */\nexport const WARNING_PERCENT = 75\n\n/** What a line says for an account no current reading describes. */\nexport const NO_READING_TEXT = \"no reading yet\"\n\n/** The word ahead of a reset time, muted while the time takes its own colour. */\nconst RESET_WORD = \"resets \"\nconst SEPARATOR = \" · \"\n\nconst SHORT_WINDOWS: Record<string, string> = {\n five_hour: \"5h\",\n seven_day: \"7d\",\n seven_day_opus: \"7d opus\",\n seven_day_sonnet: \"7d sonnet\",\n seven_day_overage_included: \"7d extra\",\n overage: \"extra\",\n}\n\n/** `five_hour` -> `5h`; an unknown window name is shown as itself, `unknown` as nothing. */\nexport function shortWindowName(window: string): string {\n if (window === \"unknown\") return \"\"\n return SHORT_WINDOWS[window] ?? window.replace(/_/g, \" \")\n}\n\nconst pad = (value: number) => String(value).padStart(2, \"0\")\n\nfunction sameDay(a: Date, b: Date): boolean {\n return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate()\n}\n\n/**\n * A reset time in local time, in the usage-limit note's own notation\n * (`formatLocalMinute` in `src/cli-events.ts`, which this module cannot import\n * because that one logs): `18:00` today, `2026-10-21 12:00` on another day,\n * and `10-21 12:00` with `year` off, which is what a line falls back on.\n */\nexport function formatResetClock(resetsAt: number, now: number, year = true): string {\n const when = new Date(resetsAt)\n const clock = `${pad(when.getHours())}:${pad(when.getMinutes())}`\n if (sameDay(when, new Date(now))) return clock\n const date = `${pad(when.getMonth() + 1)}-${pad(when.getDate())} ${clock}`\n return year ? `${when.getFullYear()}-${date}` : date\n}\n\n/**\n * How a line may say when its window resets, widest first: `resets 2026-10-18\n * 01:20`, then without the word, then without the year. A reset today is\n * `resets 01:20` only, since that always fits, and the interactive transport's\n * phrase has no year to drop. Empty when nothing is known.\n */\nexport function resetSpellings(entry: WindowUsage, now: number): string[] {\n if (entry.resetsAt !== undefined) {\n const full = formatResetClock(entry.resetsAt, now)\n if (sameDay(new Date(entry.resetsAt), new Date(now))) return [`${RESET_WORD}${full}`]\n return [`${RESET_WORD}${full}`, full, formatResetClock(entry.resetsAt, now, false)]\n }\n if (entry.resetsText) return [`${RESET_WORD}${entry.resetsText}`, entry.resetsText]\n return []\n}\n\nfunction rank(entry: WindowUsage): number {\n if (entry.status === \"rejected\") return 1000\n if (entry.utilization === null) return -1\n return usagePercent(entry.utilization)\n}\n\n/** The window an account's line is about, or undefined when none is current. */\nexport function pressingWindow(\n windows: ReadonlyMap<string, WindowUsage>,\n now: number,\n): [string, WindowUsage] | undefined {\n let best: [string, WindowUsage] | undefined\n for (const [window, entry] of windows) {\n if (!isReadingCurrent(entry, now)) continue\n if (!best || rank(entry) > rank(best[1])) best = [window, entry]\n }\n return best\n}\n\ninterface LineDraft {\n account: string\n window: string\n state: AccountUsageLine[\"state\"]\n name: string\n /** `5h 72%`, `limited`, `7d ok`; empty on a line with no reading. */\n level: string\n /** The plan tier, drawn only on a line with no reading. */\n tier?: string\n spellings: string[]\n}\n\nconst cells = (value: string) => Array.from(value).length\nconst segmentsCells = (segments: Segment[]) => segments.reduce((sum, segment) => sum + cells(segment.text), 0)\n\nfunction levelRole(state: AccountUsageLine[\"state\"]): Segment[\"role\"] {\n return state === \"limited\" ? \"error\" : state === \"warning\" ? \"effort\" : \"muted\"\n}\n\n/**\n * Segments cut to `budget` cells: a run that does not fit is cut with `…`, and\n * a separator that does not fit ends the line, since nothing it would\n * introduce fits either.\n */\nfunction fitSegments(segments: Segment[], budget: number): Segment[] {\n const out: Segment[] = []\n let used = 0\n for (const segment of segments) {\n const length = cells(segment.text)\n if (used + length <= budget) {\n out.push(segment)\n used += length\n continue\n }\n if (segment.text !== SEPARATOR) {\n const cut = truncate(segment.text, budget - used)\n if (cut) out.push({ ...segment, text: cut })\n }\n break\n }\n return out\n}\n\n/**\n * What follows the name: the level and the reset for a reading, each in its\n * own colour; the tier (while `withTier`) and `no reading yet` without one.\n */\nfunction tailSegments(draft: LineDraft, spelling: number, withTier: boolean): Segment[] {\n if (draft.state === \"none\") {\n const out: Segment[] = []\n if (withTier && draft.tier) out.push({ text: SEPARATOR, role: \"muted\" }, { text: draft.tier, role: \"tier\" })\n out.push({ text: SEPARATOR, role: \"muted\" }, { text: NO_READING_TEXT, role: \"muted\" })\n return out\n }\n const out: Segment[] = [\n { text: SEPARATOR, role: \"muted\" },\n { text: draft.level, role: levelRole(draft.state) },\n ]\n const reset = draft.spellings[Math.min(spelling, draft.spellings.length - 1)]\n if (reset) {\n out.push({ text: SEPARATOR, role: \"muted\" })\n if (reset.startsWith(RESET_WORD)) {\n out.push({ text: RESET_WORD, role: \"muted\" }, { text: reset.slice(RESET_WORD.length), role: \"reset\" })\n } else {\n out.push({ text: reset, role: \"reset\" })\n }\n }\n return out\n}\n\n/**\n * Every account with a current reading or a registration, alphabetically, each\n * laid out for `width`. The reset is spelt the same way on every line, the\n * widest spelling in `resetSpellings` that fits all of them, so the column\n * reads alike; a line with no reading gives up its tier before it is cut, and\n * a line that fits nothing else is cut.\n */\nexport function accountUsageLines(\n usage: AccountUsage,\n now: number,\n width: number = SIDEBAR_ROW_WIDTH,\n known: KnownAccounts = new Map(),\n): AccountUsageLine[] {\n const drafts: LineDraft[] = []\n const names = [...new Set([...usage.keys(), ...known.keys()])].sort()\n for (const account of names) {\n const name = truncate(account, Math.max(1, Math.floor(width / 2)))\n const windows = usage.get(account)\n const pressing = windows ? pressingWindow(windows, now) : undefined\n if (!pressing) {\n if (!known.has(account)) continue\n drafts.push({\n account,\n window: \"\",\n state: \"none\",\n name,\n level: \"\",\n tier: known.get(account)?.tier,\n spellings: [],\n })\n continue\n }\n const [window, entry] = pressing\n const state: AccountUsageLine[\"state\"] =\n entry.status === \"rejected\"\n ? \"limited\"\n : entry.utilization !== null && usagePercent(entry.utilization) >= WARNING_PERCENT\n ? \"warning\"\n : \"ok\"\n const short = shortWindowName(window)\n const level =\n state === \"limited\"\n ? \"limited\"\n : entry.utilization === null\n ? `${short ? `${short} ` : \"\"}ok`\n : `${short ? `${short} ` : \"\"}${Math.round(usagePercent(entry.utilization))}%`\n drafts.push({ account, window, state, name, level, spellings: resetSpellings(entry, now) })\n }\n const budget = (draft: LineDraft) => Math.max(0, width - cells(draft.name))\n // The reset's spelling is decided over the lines that have one; a line with\n // no reading has nothing to spell and does not move the column.\n const spelt = drafts.filter((draft) => draft.state !== \"none\")\n const last = Math.max(0, ...spelt.map((draft) => draft.spellings.length - 1))\n let spelling = 0\n while (\n spelling < last &&\n spelt.some((draft) => segmentsCells(tailSegments(draft, spelling, true)) > budget(draft))\n ) {\n spelling += 1\n }\n return drafts.map((draft) => {\n const full = tailSegments(draft, spelling, true)\n const tail = segmentsCells(full) <= budget(draft) ? full : tailSegments(draft, spelling, false)\n return {\n account: draft.account,\n window: draft.window,\n state: draft.state,\n segments: [{ text: draft.name, role: \"agent\" }, ...fitSegments(tail, budget(draft))],\n }\n })\n}\n\n/** The plain text of a line, for tests and the collapsed summary. */\nexport function lineText(line: AccountUsageLine): string {\n return line.segments.map((segment) => segment.text).join(\"\")\n}\n"],"mappings":"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","names":["text","text","pad","pieces","mkdirSync","readFileSync","renameSync","statSync","writeFileSync","homedir","join","pathOverride","join","homedir","text","readEntries","readFileSync","statSync","readEntries","mkdirSync","readFileSync","renameSync","statSync","writeFileSync","homedir","join","pathOverride","pathOverride","join","homedir","readFileSync","statSync","cells","out","measure","text"]}