@khalilgharbaoui/opencode-claude-code-plugin 0.58.0 → 0.60.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/progress-line.ts","../src/spawn-record-store.ts","../src/turn-history.ts","../src/turn-progress-store.ts","../src/turn-model.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 * with the band that prediction carries (`src/turn-model.ts` over\n * `src/turn-history.ts`, h #g247, h #g251), else nothing; the rest is\n * elapsed, calls and the running tool. `src/progress-line.ts` decides all of\n * 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 floorByChildren,\n layoutProgressLine,\n liveOutput,\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, promptChars, sessionTag, turnHistoryKey, type TurnHistory } from \"./turn-history.js\"\nimport { buildTurnModel, type TurnModel } from \"./turn-model.js\"\nimport { createTurnProgressReader, type TurnProgress } from \"./turn-progress-store.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 /** An account's level under the warning threshold, `ok` included: the success colour at full strength (h #g248). */\n ok: 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 /**\n * An estimate's cells covered at the estimate but not past the band's far\n * end: the success colour faded toward muted, like a finished row's ✓.\n */\n barLikely: Color\n /**\n * The track an estimate covers if the turn ends at the band's near end:\n * muted with a little of the success colour, brighter than the rest.\n */\n barBand: 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 ok: \"ok\",\n error: \"error\",\n agent: \"text\",\n description: \"text\",\n muted: \"muted\",\n effort: \"effort\",\n \"effort-faded\": \"effortFaded\",\n \"bar-done\": \"barDone\",\n \"bar-likely\": \"barLikely\",\n \"bar-band\": \"barBand\",\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 // The model over the whole store (src/turn-model.ts, h #g251), built once\n // per change to it because that is when its calibration is fitted, then\n // read once per line per tick.\n let turnModel: TurnModel | undefined = attempt(() => buildTurnModel(historyMap), undefined)\n // What each running turn has generated so far, which only the server half\n // sees: it writes the total as each API call ends (h #g251). Read on the\n // second hand like the others; the clock re-derives the lines.\n const liveProgress = createTurnProgressReader()\n let progressMap: Map<string, TurnProgress> = attempt(() => liveProgress.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 if (nextHistory !== historyMap) turnModel = attempt(() => buildTurnModel(nextHistory), turnModel)\n recordMap = next\n historyMap = nextHistory\n setRecordVersion((value) => value + 1)\n }\n progressMap = attempt(() => liveProgress.read(), progressMap)\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, h #g250, h #g251): the session's\n * recorded agent and model name the key, the turn's prompt length and the\n * effort weight its record, the session's own earlier turns are its\n * session level, its tool calls and the output the server half saw it\n * generate condition it beside the elapsed, its running foreground\n * subagents floor it, and the fill and the band are held so nothing on the\n * bar steps back within the turn. No spawn record, or too little record\n * for the key and its agent's other models, is no estimate, and the line\n * then draws no bar.\n */\n const estimateFor = (\n sessionID: string,\n measure: ProgressMeasure,\n view: ProgressView,\n agentFallback?: string,\n children: readonly ProgressView[] = [],\n ): ProgressView => {\n if (view.state !== \"running\" || !turnModel) return view\n const record = recordMap.get(sessionID)\n if (!record?.model) return view\n const tag = sessionTag(sessionID)\n const estimate = turnModel.estimate(turnHistoryKey(record.agent ?? agentFallback, record.model), {\n elapsedMs: view.elapsedMs,\n chars: measure.chars,\n effort: record.effort,\n calls: measure.calls,\n out: liveOutput(tag ? progressMap.get(tag) : undefined, measure.startedAt),\n session: tag,\n now: Date.now(),\n })\n if (!estimate) return view\n const turn = measure.startedAt ?? 0\n return { ...view, estimate: fills.holdEstimate(sessionID, turn, floorByChildren(estimate, view.elapsedMs, children)) }\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 // The running foreground children's lines: the session's turn waits in\n // their `task` calls, so it cannot end before they do.\n const waitedOn: 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 = estimateFor(row.sessionID, measure, rowProgress(measure, { started: row.started, now: clock }), row.agent)\n lines.set(row.sessionID, line)\n if (!row.background) waitedOn.push(line)\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, waitedOn) : 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 ok: theme.success ?? theme.accent,\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 barLikely: mix(theme.success ?? theme.accent, muted, 0.45),\n barBand: mix(muted, theme.success ?? theme.accent, 0.3),\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 ok: theme.text.feedback.success.base ?? accent,\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 barLikely: mix(theme.text.feedback.success.base ?? accent, muted, 0.45),\n barBand: mix(muted, theme.text.feedback.success.base ?? accent, 0.3),\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), `bar-likely` and `bar-band` the estimate's band on the bar\n * (h #g251); `tier`, `reset` and `ok` are the `Accounts` line's plan tier,\n * reset time and under-threshold level (`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-likely\"\n | \"bar-band\"\n | \"bar-over\"\n | \"bar-track\"\n | \"fraction\"\n | \"estimate\"\n | \"estimate-over\"\n | \"tool\"\n | \"cost\"\n | \"tier\"\n | \"reset\"\n | \"ok\"\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-model.ts`: a log-normal fitted to\n * the recorded turns, conditioned on this one having run this long, read\n * through the calibration of the model's own past errors), and the line\n * says both, `1m02s/~2m30s`. The bar draws the estimate's band as well:\n * the cells covered even if the turn runs to the band's far end in the\n * full colour, the rest of the cells covered at the estimate in a softer\n * one, and the track the turn has covered if it ends at the band's near\n * end a little brighter than the rest of the track:\n *\n * ▰▰▰▰▰▰▱▱▱▱ sure ▰▰▰▰ · likely ▰▰ · band ▱▱ · track ▱▱\n *\n * Nothing on the bar ever steps back within a turn (`createFillMemory`),\n * though the total it is drawn against can rise, which it does while a\n * foreground subagent has longer left than the session's own estimate\n * (`floorByChildren`). 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-model.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 const hold = (key: string, turn: number, fill: number): number => {\n const entry = held.get(key)\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(key, { turn, fill })\n return fill\n }\n return {\n hold,\n /**\n * The same hold for a whole estimate: its fill and both edges of its band,\n * each at its highest this turn, so neither the lit cells nor the band's\n * reach ever step back while the total rises. The total is not held: it\n * is the text, and the text says what the model says now. Each edge at\n * its highest stays on its side of the fill, because it is on that side\n * at every moment (elapsed over a longer total, and over a shorter one).\n */\n holdEstimate(sessionID: string, turn: number, estimate: TurnEstimate): TurnEstimate {\n const fill = hold(sessionID, turn, estimate.fill)\n if (!estimate.band) return { ...estimate, fill }\n const sure = hold(`${sessionID}\\u0000sure`, turn, estimate.band.sure)\n const maybe = hold(`${sessionID}\\u0000maybe`, turn, estimate.band.maybe)\n return {\n ...estimate,\n fill,\n band: { ...estimate.band, sure: Math.min(sure, fill), maybe: Math.max(maybe, fill) },\n }\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/** Where an estimate's band falls on the bar, as cell counts from the left. */\nexport interface EstimateCells {\n /** Lit in the full colour: covered even if the turn runs to the band's far end. */\n sure: number\n /** Lit, past `sure` in a softer colour: covered at the estimate itself (`estimateCells`). */\n likely: number\n /** Track past `likely` drawn brighter: covered if the turn ends at the band's near end. */\n band: number\n}\n\n/**\n * An estimate's cells, band included. The lit count is `estimateCells`\n * exactly, so the band only recolours cells and the bar still reads as it\n * did: never full before `over`, which is the warning colour's alone. Only\n * the band's track may reach the last cell, because a brighter `▱` is not a\n * full bar: it says the turn may end any moment now. An estimate without a\n * band (none to draw, or `over`) is its lit cells alone.\n */\nexport function estimateBandCells(estimate: TurnEstimate, width: number = BAR_WIDTH): EstimateCells {\n const likely = estimateCells(estimate, width)\n if (estimate.over || !estimate.band) return { sure: likely, likely, band: likely }\n const sure = Math.min(likely, Math.max(0, Math.round(estimate.band.sure * width)))\n const band = Math.max(likely, Math.min(width, Math.round(estimate.band.maybe * width)))\n return { sure, likely, band }\n}\n\n/** How far before the TUI's own start of a turn the server half may have marked it. */\nexport const TURN_PROGRESS_SLACK_MS = 1_000\n\n/**\n * The output a running turn has generated so far, from the live channel's\n * record for its session (`src/turn-progress-store.ts`, h #g251), or\n * undefined when the record is an earlier turn's. The server half marks a\n * turn when opencode asks it for the reply, which is after the message that\n * started the turn was stored, so this turn's record started no earlier\n * than the TUI's start of it, less a little slack. A message steered in\n * mid-turn restarts the TUI's turn and not the server's, so the record's\n * count, which runs from the server's start, is not read against it. A turn\n * the TUI dates from no message has nothing to check against and reads\n * nothing.\n */\nexport function liveOutput(record: { turn: number; out: number } | undefined, startedAt: number | undefined): number | undefined {\n if (!record || startedAt === undefined) return undefined\n return record.turn >= startedAt - TURN_PROGRESS_SLACK_MS ? record.out : undefined\n}\n\n/**\n * A turn parked in a foreground subagent cannot end before the subagent does\n * (h #g251), because its `task` call returns the child's answer. So each\n * running foreground child's remaining time, read off the child's own\n * estimate, floors the parent's: the total at least the parent's elapsed\n * plus the child's remaining median, each edge of the band at least the\n * elapsed plus the child's remaining edge. A `task_batch` waits for every\n * child it started, so the longest of each wins. The parent's record already\n * holds the time its turns usually spend in children, so the floor only\n * moves an estimate that a child is visibly outlasting.\n *\n * A child past its own record (`over`) has no remaining time to offer and\n * floors nothing, and an `over` parent stays over: it is past every recorded\n * turn of its own. The host applies this before the fill memory's hold, so a\n * rising floor lowers nothing on the bar.\n */\nexport function floorByChildren(estimate: TurnEstimate, elapsedMs: number, children: readonly ProgressView[]): TurnEstimate {\n if (estimate.over) return estimate\n let median = 0\n let low = 0\n let high = 0\n for (const child of children) {\n const own = child.estimate\n if (child.state !== \"running\" || !own || own.over) continue\n const left = (ms: number) => Math.max(0, ms - child.elapsedMs)\n median = Math.max(median, left(own.totalMs))\n low = Math.max(low, left(own.band?.lowMs ?? own.totalMs))\n high = Math.max(high, left(own.band?.highMs ?? own.totalMs))\n }\n const elapsed = Math.max(0, elapsedMs)\n const totalMs = Math.max(estimate.totalMs, elapsed + median)\n const band = estimate.band\n if (totalMs === estimate.totalMs && (!band || (band.lowMs >= elapsed + low && band.highMs >= elapsed + high))) {\n return estimate\n }\n const fill = totalMs > 0 ? elapsed / totalMs : 0\n if (!band) return { ...estimate, totalMs, fill }\n const lowMs = Math.min(totalMs, Math.max(band.lowMs, elapsed + low))\n const highMs = Math.max(totalMs, band.highMs, elapsed + high)\n return {\n ...estimate,\n totalMs,\n fill,\n band: {\n lowMs,\n highMs,\n sure: highMs > 0 ? elapsed / highMs : 0,\n maybe: lowMs > 0 ? Math.min(1, elapsed / lowMs) : 0,\n },\n }\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 bar: Segment[] = []\n const cellsOf = (segment: (cell: number) => Segment) => {\n for (let cell = 0; cell < BAR_WIDTH; cell += 1) bar.push(segment(cell))\n }\n if (ended) {\n cellsOf(() => ({ text: BAR_DONE, role: endedRole }))\n } else if (view.plan) {\n const filled = filledCells(view.plan)\n cellsOf((cell) => (cell < filled ? { text: BAR_DONE, role: \"bar-done\" } : { text: BAR_TODO, role: \"bar-track\" }))\n } else if (estimate) {\n const cells = estimateBandCells(estimate)\n cellsOf((cell) =>\n estimate.over\n ? { text: BAR_DONE, role: \"bar-over\" }\n : cell < cells.sure\n ? { text: BAR_DONE, role: \"bar-done\" }\n : cell < cells.likely\n ? { text: BAR_DONE, role: \"bar-likely\" }\n : cell < cells.band\n ? { text: BAR_TODO, role: \"bar-band\" }\n : { 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 took, for the sidebar's progress bar (h #g247): one sample\n * per finished turn, keyed by the agent and the model it ran as, written by\n * the server half at the CLI's `result` and read by the TUI half\n * (`src/tui.ts`). What the bar is drawn from that record is\n * `src/turn-model.ts`; this module is the record itself, and the counts both\n * halves must agree on (the prompt's length, the turn's tool calls, where a\n * turn starts).\n *\n * ## The store\n *\n * ```json\n * { \"version\": 1, \"turns\": { \"build/claude-opus-5-5\": [\n * { \"ms\": 61230, \"chars\": 420, \"effort\": \"high\", \"calls\": 3, \"out\": 5321, \"session\": \"5c1d0f3a\", \"at\": 1760000000000 },\n * { \"ms\": 6077, \"chars\": 279, \"effort\": \"low\", \"calls\": 1, \"failed\": true, \"at\": 1760000100000 } ] } }\n * ```\n *\n * A duration, a character count, an effort level, a tool-call count, the\n * output tokens its API calls generated, whether the turn ended on a failed\n * tool result, a short tag of the conversation it ran in, and a time: no\n * prompt text, no path, no session id, nothing secret. The output count is\n * the sum the live channel (`src/turn-progress-store.ts`) wrote as the turn\n * ran, so the record and the running turn count it the same way. The tag is\n * a 32-bit hash of the opencode session id (`sessionTag`),\n * enough for the estimate to tell this conversation's turns from another's\n * and not the id. Same discipline as the spawn record store (h #g234):\n * merge-on-write so two opencode processes do not erase each other, atomic\n * rename, `0600` in the `0700` directory, bounded at `MAX_TURN_SAMPLES`\n * newest samples per key, `MAX_TURN_KEYS` keys and `TURN_SAMPLE_MAX_AGE_MS`.\n * A missing, unreadable or half-written file is an empty store, which costs\n * 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\nimport { recordTurnProgress } from \"./turn-progress-store.js\"\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 /** Tool calls the turn made, counted as the TUI counts a turn's tool parts; absent on a sample from before 0.57.2. */\n calls?: number\n /**\n * Output tokens the turn's API calls generated, summed off each call's\n * `message_delta`; absent on a sample from before 0.58.0 and on a turn the\n * stream carried no `message_delta` for (the interactive transport's).\n */\n out?: number\n /**\n * The turn's last tool result was an error (the CLI's own refusal of a tool,\n * or a proxied tool opencode reported as failed), so the turn ended on a\n * failure opencode may have reported as the subagent failing. Its length\n * says nothing about a served turn's, and the estimate skips it (h #g251).\n * Written only when true.\n */\n failed?: boolean\n /**\n * The conversation the turn ran in, as `sessionTag` hashes its opencode\n * session id: the estimate's session level reads it (`src/turn-model.ts`).\n * Absent on a sample from before 0.58.0.\n */\n session?: 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\nconst SESSION_TAG = /^[0-9a-f]{8}$/\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 * A short tag for an opencode session id, the same on both halves: FNV-1a\n * over its characters, 32 bits as eight hex digits. Enough for the estimate\n * to tell one conversation's turns from another's (h #g251), and not the id:\n * a hash of it, which the store's \"no session id\" means to keep true.\n * Undefined for no id, and for `default`, the affinity of a turn opencode\n * did not tag with a session.\n */\nexport function sessionTag(sessionID: string | undefined): string | undefined {\n if (!sessionID || sessionID === \"default\") return undefined\n let hash = 0x811c9dc5\n for (let index = 0; index < sessionID.length; index += 1) {\n hash ^= sessionID.charCodeAt(index)\n hash = Math.imul(hash, 0x01000193) >>> 0\n }\n return hash.toString(16).padStart(8, \"0\")\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\nfunction toolResultParts(message: PromptMessageLike): Array<{ toolCallId?: unknown; output?: unknown }> {\n if (!Array.isArray(message.content)) return []\n return message.content.filter(\n (part): part is { toolCallId?: unknown; output?: unknown } =>\n !!part && typeof part === \"object\" && (part as { type?: unknown }).type === \"tool-result\",\n )\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 *\n * The exception is a tool result for no call this process is waiting on\n * (h #g251): after an interrupted turn opencode's next prompt ends in the\n * interrupted calls' results and then the operator's new message, which is a\n * fresh turn, not a continuation. Given the ids of the proxied calls still\n * parked, a trailing tool message that answers none of them starts the turn.\n */\nexport function startsTurn(prompt: readonly PromptMessageLike[], pendingToolCallIds?: ReadonlySet<string>): boolean {\n const last = prompt[prompt.length - 1]\n if (!last || last.role !== \"user\") return false\n const before = prompt[prompt.length - 2]\n if (before === undefined || before.role !== \"tool\") return true\n if (!pendingToolCallIds) return false\n return toolResultParts(before).every((part) => typeof part.toolCallId !== \"string\" || !pendingToolCallIds.has(part.toolCallId))\n}\n\n/**\n * How many tool calls the turn a prompt continues has made so far: the\n * `tool-call` parts of the assistant messages since the message that started\n * it. Every proxied and CLI-executed call the plugin emitted in an earlier\n * step is one of them, because opencode stores each as a tool part and hands\n * them back here; the step that sees the `result` adds its own emitted\n * calls, which are not in its prompt yet. That is the count the TUI reads\n * off the turn's tool parts (`measureProgress`), which is what lets the\n * estimate condition on it mid-turn (h #g250).\n *\n * Where the turn started is `from`, the prompt's length when `markTurnStart`\n * ran, so the count agrees with the start that was marked (h #g251); without\n * it, or with a prompt shorter than that (a compaction in between), the\n * boundary is read back off the prompt by `startsTurn`'s plain rule, the\n * last user message not following a tool result, so a message the host\n * steered in mid-turn does not restart the count.\n */\nexport function turnToolCalls(prompt: readonly PromptMessageLike[], from?: number): number {\n let start = 0\n if (from !== undefined && from >= 0 && from <= prompt.length) {\n start = from\n } else {\n for (let index = prompt.length - 1; index >= 0; index -= 1) {\n if (prompt[index].role !== \"user\") continue\n const before = prompt[index - 1]\n if (before === undefined || before.role !== \"tool\") {\n start = index\n break\n }\n }\n }\n let calls = 0\n for (let index = start; index < prompt.length; index += 1) {\n const message = prompt[index]\n if (message.role !== \"assistant\" || !Array.isArray(message.content)) continue\n for (const part of message.content) {\n if (part && typeof part === \"object\" && (part as { type?: unknown }).type === \"tool-call\") calls += 1\n }\n }\n return calls\n}\n\n/**\n * Whether the turn's newest tool result in the prompt, the one the final\n * step was answering, was an error: a proxied tool opencode ran and reported\n * failed (`error-text`, `error-json`, `execution-denied`). Trailing user\n * messages (a steered one) are looked past; a tool message from before the\n * turn started (`from`) is not this turn's. The final step's own CLI-executed\n * results are newer than anything here and are read off the stream instead.\n */\nexport function lastToolResultFailed(prompt: readonly PromptMessageLike[], from: number): boolean {\n for (let index = prompt.length - 1; index >= Math.max(0, from); index -= 1) {\n const message = prompt[index]\n if (message.role === \"user\") continue\n if (message.role !== \"tool\") return false\n const parts = toolResultParts(message)\n const newest = parts[parts.length - 1]\n const output = newest?.output as { type?: unknown } | undefined\n const type = output && typeof output === \"object\" ? output.type : undefined\n return type === \"error-text\" || type === \"error-json\" || type === \"execution-denied\"\n }\n return false\n}\n\n// ---------------------------------------------------------------------------\n// When a turn started, held from its first step to its result\n// ---------------------------------------------------------------------------\n\nconst MAX_TURN_STARTS = 256\n\nexport interface TurnStart {\n at: number\n chars?: number\n /** The prompt's length when the turn started: where `turnToolCalls` counts from. */\n promptLength?: number\n /** The session's tag (`sessionTag`): where the live channel writes this turn's progress. */\n session?: string\n /** Output tokens the turn's API calls have generated so far; absent until the first call ends. */\n out?: number\n}\n\nconst turnStarts = new Map<string, TurnStart>()\n\n/**\n * Note that a turn on `key` started. A turn is a run of steps (every proxied\n * tool call ends one), and only its last step sees the `result`, so the\n * start waits here, keyed like the process, until `takeTurnStart`; the\n * output its calls generate is added to it as they end (`noteTurnOutput`).\n * A 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, start: TurnStart): 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, {\n at: start.at,\n ...(start.chars !== undefined ? { chars: start.chars } : {}),\n ...(start.promptLength !== undefined ? { promptLength: start.promptLength } : {}),\n ...(start.session !== undefined ? { session: start.session } : {}),\n })\n}\n\n/**\n * One API call of the turn on `key` ended, having generated `tokens` output\n * tokens (its `message_delta`'s count, the real one): add them to the turn's\n * total and write the total to the live channel, where the TUI reads it\n * while the turn runs (h #g251). A key with no turn marked (a compaction, a\n * turn opencode did not tag with a session) is not one the sidebar shows,\n * and nothing is written.\n */\nexport function noteTurnOutput(key: string, tokens: number, now: number = Date.now()): void {\n const start = turnStarts.get(key)\n if (!start || !Number.isFinite(tokens) || tokens < 0) return\n start.out = (start.out ?? 0) + tokens\n if (start.session) recordTurnProgress(start.session, { turn: start.at, out: start.out }, now)\n}\n\n/** The start noted for `key`, once: a second `result` in the same turn finds nothing. */\nexport function takeTurnStart(key: string): TurnStart | 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 if (sample.calls !== undefined && (typeof sample.calls !== \"number\" || !Number.isFinite(sample.calls) || sample.calls < 0)) {\n return false\n }\n if (sample.out !== undefined && (typeof sample.out !== \"number\" || !Number.isFinite(sample.out) || sample.out < 0)) {\n return false\n }\n if (sample.failed !== undefined && typeof sample.failed !== \"boolean\") return false\n if (sample.session !== undefined && (typeof sample.session !== \"string\" || !SESSION_TAG.test(sample.session))) {\n return false\n }\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 ...(sample.calls !== undefined ? { calls: Math.round(sample.calls) } : {}),\n ...(sample.out !== undefined ? { out: Math.round(sample.out) } : {}),\n ...(sample.failed ? { failed: true } : {}),\n ...(sample.session ? { session: sample.session } : {}),\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\nexport function _setTurnHistoryStorePathForTests(path: string | null): void {\n pathOverride = path\n turnStarts.clear()\n}\n","/**\n * The live half of the progress estimate's record (h #g251): how much output\n * a running turn has generated so far, which is the one measure of a turn's\n * progress the TUI cannot read off opencode's store. opencode shows the\n * turn's tool calls as they stream, but every API call within a step lands\n * in one assistant message, and a step's usage is reported as zeros until\n * the turn's `result` on purpose (h #g169, h #g225). The server half reads\n * each call's real output at its `message_delta` (the `assistant` frames\n * carry a placeholder, measured on 2.1.293: 6 against the call's 41) and\n * writes the turn's running total here, once per API call; the TUI reads it\n * beside the turn history and the estimate conditions on it\n * (`src/turn-model.ts`).\n *\n * ## Shape, and what is never written\n *\n * ```json\n * { \"version\": 1, \"turns\": { \"5c1d0f3a\": { \"turn\": 1760000000000, \"out\": 5321, \"at\": 1760000012345 } } }\n * ```\n *\n * Keyed by the session's tag (`sessionTag` in `src/turn-history.ts`, a hash\n * of its opencode session id, which the TUI computes from the same id), so\n * the file holds no session id at all. `turn` is when the server half saw\n * the turn start, which is how the TUI tells this turn's record from the\n * previous one's; `out` the output tokens the turn's calls have generated;\n * `at` the last write. Nothing else: no prompt, no path, no text.\n *\n * ## Cost and failure\n *\n * One small write per API call, the same discipline as the spawn record\n * store (h #g234): merge-on-write so two opencode processes do not erase each\n * other, atomic rename, `0600` in the `0700` directory, bounded at\n * `MAX_TURN_PROGRESS` newest turns and `TURN_PROGRESS_MAX_AGE_MS` since their\n * last write. A missing, unreadable or half-written file is an empty store,\n * which costs the estimate one input 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 TurnProgress {\n /** When the server half saw the turn start. */\n turn: number\n /** Output tokens the turn's API calls have generated so far. */\n out: number\n /** The last write. */\n at: number\n}\n\n/** Turns kept at once: one per session that has run a turn lately. */\nexport const MAX_TURN_PROGRESS = 64\n/** A record not written this long is no running turn's. */\nexport const TURN_PROGRESS_MAX_AGE_MS = 24 * 60 * 60 * 1000\n\nconst TAG = /^[0-9a-f]{8}$/\n\nlet pathOverride: string | null = null\n\nexport function turnProgressStorePath(): 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-progress.json\")\n}\n\nfunction isProgress(value: unknown): value is TurnProgress {\n const record = value as TurnProgress | null\n if (!record || typeof record !== \"object\" || Array.isArray(record)) return false\n const count = (field: unknown) => typeof field === \"number\" && Number.isFinite(field) && field >= 0\n return count(record.turn) && count(record.out) && count(record.at)\n}\n\nfunction readEntries(file: string, now: number): Map<string, TurnProgress> {\n const out = new Map<string, TurnProgress>()\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_PROGRESS_MAX_AGE_MS\n const valid = Object.entries(entries as Record<string, unknown>)\n .filter((entry): entry is [string, TurnProgress] => TAG.test(entry[0]) && isProgress(entry[1]))\n .filter(([, record]) => record.at >= cutoff)\n .sort((a, b) => a[1].at - b[1].at)\n .slice(-MAX_TURN_PROGRESS)\n for (const [tag, record] of valid) out.set(tag, { turn: record.turn, out: Math.round(record.out), at: record.at })\n } catch {\n // ENOENT before the first call, 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 that the turn `tag`'s session started at `turn` has generated `out`\n * output tokens so far. Returns whether the disk was written, which is false\n * for an unusable record and a failed write alike: the caller has nothing to\n * do about either.\n */\nexport function recordTurnProgress(tag: string, progress: { turn: number; out: number }, now: number = Date.now()): boolean {\n const record = { turn: progress.turn, out: progress.out, at: now }\n if (!TAG.test(tag) || !isProgress(record)) return false\n const file = turnProgressStorePath()\n const merged = readEntries(file, now)\n merged.delete(tag)\n merged.set(tag, { ...record, out: Math.round(record.out) })\n const kept = [...merged.entries()].slice(-MAX_TURN_PROGRESS)\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 createTurnProgressReader(file: () => string = turnProgressStorePath) {\n let identity = \"\"\n let cached = new Map<string, TurnProgress>()\n return {\n read(now: number = Date.now()): Map<string, TurnProgress> {\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\nexport function _setTurnProgressStorePathForTests(path: string | null): void {\n pathOverride = path\n}\n","/**\n * The estimate behind the sidebar's progress bar (h #g247, h #g250, h #g251):\n * what a running turn is expected to take in all, and the band it is expected\n * to end in, read off this install's own record of finished turns\n * (`src/turn-history.ts`). Pure, and imported by the TUI bundle, so nothing\n * here logs or throws (h #g193).\n *\n * ## The model\n *\n * A turn's length is modelled as log-normal per agent and model: ln T is\n * normal with a mean μ and a spread σ. That is what a duration that runs from\n * thirty seconds to forty minutes under one key looks like, and it makes \"a\n * factor of two either way\" the natural unit of error. Both are the weighted\n * moments of the key's recorded turns in log space, each turn weighed by how\n * much it resembles this one:\n *\n * - a Gaussian kernel on the log of the prompt's length (`CHARS_BANDWIDTH`:\n * a prompt four times longer or shorter weighs e^-1/2, sixteen times e^-2);\n * - `OTHER_EFFORT_WEIGHT` for a turn at another effort, whose duration is\n * first scaled by the measured ratio between the two efforts' typical\n * durations once the key holds `MIN_EFFORT_RATIO_SAMPLES` turns at each,\n * because effort multiplies the thinking a turn does (h #g250);\n * - recency, halving every `RECENCY_HALF_LIFE_MS`, so a CLI update or a\n * machine change fades in days rather than the store's month.\n *\n * **Shrinkage, three levels with one rule.** A key with few turns borrows\n * from the same agent under other models: its mean and variance are pulled\n * toward that pool's with the weight of `SHRINKAGE_KAPPA` pseudo-turns, so a\n * new model under a known agent has a bar from its first turn, and a key with\n * a dozen turns of its own is nearly all its own. The pool's pull on the mean\n * is discounted by the pool's own size, κ·n/(n + κ), because a prior read off\n * one turn of another model is worth less than one read off forty: pulled\n * undiscounted, a single 200 s turn under another model put a five-turn key\n * of 40 s turns at 63 s, and the calibration carried that error on to every\n * other key's bar. With no pool the variance\n * alone is pulled toward `DEFAULT_LOG_SIGMA`, so three turns that happened to\n * agree do not claim a certainty they have not earned. Below the key, the\n * conversation: turns in one session resemble each other more than turns\n * across sessions (one session is a long refactor, another quick questions),\n * so the key's turns from THIS session are fitted on their own and shrunk\n * toward the key's fit by the same rule. One earlier turn of its own moves a\n * session's estimate a third of the way; a dozen make it nearly its own.\n * `MIN_TURN_SAMPLES` turns between the key and its pool are needed for a bar\n * at all, and a new agent waits for them: an agent is a kind of task and a\n * model a speed, and the record of a different kind of task says nothing a\n * spinner does not.\n *\n * **Conditioning.** At elapsed t the turn is known to be longer than t, so\n * every quantile is read off the distribution truncated there: with\n * F = C((ln t − μ)/σ) the share of such turns already over, the total's\n * quantile q is exp(μ + σ·C⁻¹(F + q·(1 − F))). Its median is the total\n * shown, its quarter and three-quarter points are the band. The total rises\n * as the turn outlasts more of the record instead of the bar sitting full;\n * past the key's longest recorded turn the estimate is `over`, the bar is\n * full in the warning colour and the total shown is the usual one. The\n * turn's progress so far narrows the record the same way: its tool calls\n * (h #g250), which the TUI counts off opencode's store, and the output\n * tokens its finished API calls have generated, which only the server half\n * can see and writes to a live channel as each call ends\n * (`src/turn-progress-store.ts`). The fit is redone over the recorded turns\n * that did at least as much of both, shrunk toward the unconditioned fit\n * with `CONDITIONED_KAPPA` pseudo-turns, fewer than the levels above use,\n * because that prior is known to run short for them: it holds the turns that\n * ended sooner. Output is compared only between turns at the same effort,\n * because effort is mostly how much a turn thinks, so a `low` turn's whole\n * output can be less than a `high` one's first call. A cut that would leave\n * no turn is not applied, so a turn on its fifth call on a key whose record\n * stops at four is still estimated from time rather than declared over at\n * ten seconds, which the h #g250 version did.\n *\n * **Calibration.** C is not the normal curve but the distribution of the\n * model's own standardized errors: every recorded turn estimated from the\n * rest of its key at its start (leave-one-out) gives a residual\n * (ln T − μ)/σ, and since residuals are dimensionless they pool across every\n * key, so a dozen turns anywhere in the store calibrate every bar. Each is\n * weighed by its turn's recency, as the fit weighs the turn: measured, the\n * misses on month-old turns, which the fit has moved away from, otherwise\n * pulled an estimate 15% under its own fit's median. The\n * measured distribution is smoothed with a Gaussian kernel (Silverman's\n * width), because a dozen raw residuals are a staircase with ties in it, and\n * it is blended with the normal curve, which counts as\n * `CALIBRATION_PRIOR_RESIDUALS` residuals: no threshold, the measured shape\n * takes over as the errors accrue. Whatever the log-normal gets wrong about\n * turn lengths, a bias, a spread or a skew, the band then shows what was\n * measured rather than what was assumed. The same leave-one-out estimates\n * are what `/claude-code-doctor` reports as the estimate's accuracy\n * (`assess`).\n *\n * What is NOT read: no model call, nothing from the prompt but its length,\n * and no token count from a finish, whose mid-turn usage stays zeros on\n * purpose (h #g169, h #g225): the output count comes through its own file.\n * A turn that ended on a failed tool result is skipped: its length says\n * nothing about a served turn's.\n */\nimport type { TurnHistory, TurnSample } from \"./turn-history.js\"\n\n/** Turns between a key and its agent's pool before there is an estimate at all. */\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/** Turns at each of two efforts before the ratio of their durations is measured and the other effort's scaled by it. */\nexport const MIN_EFFORT_RATIO_SAMPLES = 2\n/** A sample's weight halves every week. */\nexport const RECENCY_HALF_LIFE_MS = 7 * 24 * 60 * 60 * 1000\n/** Recorded turns a progress cut (the calls so far) must leave before it is applied. */\nexport const MIN_CONDITIONED_SAMPLES = 1\n/** The pseudo-turns a prior weighs against a key's own: with two of its own, a key is half its own. */\nexport const SHRINKAGE_KAPPA = 2\n/** The same for a progress cut's prior, the unconditioned fit, which runs short for the turns the cut keeps. */\nexport const CONDITIONED_KAPPA = 1\n/** The spread a key with no pool is pulled toward, in log space: a factor of about 2.2 either way. */\nexport const DEFAULT_LOG_SIGMA = 0.8\n/** The narrowest and widest spread a fit may claim, in log space: about ×1.2 and ×5 either way. */\nexport const MIN_LOG_SIGMA = 0.2\nexport const MAX_LOG_SIGMA = 1.6\n/** What the normal curve weighs in the calibration, in residuals: with twelve measured, the measured shape is half. */\nexport const CALIBRATION_PRIOR_RESIDUALS = 12\n/** Leave-one-out estimates per key, the newest: enough to calibrate on and to judge by. */\nexport const MAX_ASSESSED_PER_KEY = 16\n/** Residuals the calibration is fitted on at most, the newest keys' first. */\nconst MAX_CALIBRATION_RESIDUALS = 512\n/** The calibration is tabulated on this grid of standardized errors; beyond it, it is the edge's value. */\nconst GRID_LOW = -8\nconst GRID_HIGH = 8\nconst GRID_STEP = 0.04\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\nexport interface TurnEstimateBand {\n /** The total's quarter point: half the time the turn ends after this. */\n lowMs: number\n /** The total's three-quarter point: half the time the turn ends before this. */\n highMs: number\n /** Elapsed over `highMs`: the share of the bar the turn has surely covered. */\n sure: number\n /** Elapsed over `lowMs`, at most 1: the share it may have covered. */\n maybe: number\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 of its key: nothing is left to condition on. */\n over: boolean\n /** Where the turn is expected to end, around the total; absent once over. */\n band?: TurnEstimateBand\n}\n\nexport interface TurnEstimateInput {\n elapsedMs: number\n chars?: number\n effort?: string\n /** Tool calls the turn has made so far. */\n calls?: number\n /** Output tokens the turn's finished API calls have generated so far (the live channel). */\n out?: number\n /** The session's tag (`sessionTag`), so its own earlier turns weigh as the session level. */\n session?: string\n /** The clock the recency weights are read against; the model's build time by default. */\n now?: number\n}\n\n// ---------------------------------------------------------------------------\n// The normal curve\n// ---------------------------------------------------------------------------\n\n/** Φ(z), Abramowitz and Stegun 26.2.17: within 7.5e-8 everywhere. */\nexport function normalCdf(z: number): number {\n if (!Number.isFinite(z)) return z > 0 ? 1 : 0\n const x = Math.abs(z)\n const t = 1 / (1 + 0.2316419 * x)\n const poly = t * (0.31938153 + t * (-0.356563782 + t * (1.781477937 + t * (-1.821255978 + t * 1.330274429))))\n const tail = (Math.exp(-0.5 * x * x) / Math.sqrt(2 * Math.PI)) * poly\n return z >= 0 ? 1 - tail : tail\n}\n\n/** Φ⁻¹(p), Acklam's rational approximation: relative error under 1.2e-9. */\nexport function normalQuantile(p: number): number {\n const q = Math.min(1 - 1e-12, Math.max(1e-12, p))\n const a = [-3.969683028665376e1, 2.209460984245205e2, -2.759285104469687e2, 1.38357751867269e2, -3.066479806614716e1, 2.506628277459239]\n const b = [-5.447609879822406e1, 1.615858368580409e2, -1.556989798598866e2, 6.680131188771972e1, -1.328068155288572e1]\n const c = [-7.784894002430293e-3, -3.223964580411365e-1, -2.400758277161838, -2.549732539343734, 4.374664141464968, 2.938163982698783]\n const d = [7.784695709041462e-3, 3.224671290700398e-1, 2.445134137142996, 3.754408661907416]\n const low = 0.02425\n if (q < low || q > 1 - low) {\n const r = Math.sqrt(-2 * Math.log(q < low ? q : 1 - q))\n const x = (((((c[0] * r + c[1]) * r + c[2]) * r + c[3]) * r + c[4]) * r + c[5]) / ((((d[0] * r + d[1]) * r + d[2]) * r + d[3]) * r + 1)\n return q < low ? x : -x\n }\n const r = q - 0.5\n const s = r * r\n return ((((((a[0] * s + a[1]) * s + a[2]) * s + a[3]) * s + a[4]) * s + a[5]) * r) / (((((b[0] * s + b[1]) * s + b[2]) * s + b[3]) * s + b[4]) * s + 1)\n}\n\n// ---------------------------------------------------------------------------\n// Weights, and the samples as the fit sees them\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\nfunction recencyWeight(now: number, at: number): number {\n const age = Math.max(0, now - at)\n return Math.pow(0.5, age / RECENCY_HALF_LIFE_MS)\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\n/**\n * The factor each other effort's durations are scaled by before the fit: the\n * weighted median of this effort's turns over the weighted median of that\n * effort's, measured only once both groups hold `MIN_EFFORT_RATIO_SAMPLES`\n * (a ratio of one turn to one turn is noise), with the kernel and recency\n * weights on both medians so a group of long prompts is not read as a slow\n * effort. An effort with no measurable ratio is left as recorded, at\n * `OTHER_EFFORT_WEIGHT`; a sample with no effort is never scaled.\n */\nfunction effortScales(samples: readonly TurnSample[], weights: readonly number[], effort: string | undefined): Map<string, number> {\n const scales = new Map<string, number>()\n if (effort === undefined) return scales\n const groups = new Map<string, number[]>()\n samples.forEach((sample, index) => {\n if (sample.effort === undefined) return\n const group = groups.get(sample.effort) ?? []\n group.push(index)\n groups.set(sample.effort, group)\n })\n const own = groups.get(effort)\n if (!own || own.length < MIN_EFFORT_RATIO_SAMPLES) return scales\n const medianOf = (indices: readonly number[]) =>\n weightedMedian(\n indices.map((index) => samples[index].ms),\n indices.map((index) => weights[index]),\n )\n const ownMedian = medianOf(own)\n for (const [other, indices] of groups) {\n if (other === effort || indices.length < MIN_EFFORT_RATIO_SAMPLES) continue\n const otherMedian = medianOf(indices)\n if (ownMedian > 0 && otherMedian > 0) scales.set(other, ownMedian / otherMedian)\n }\n return scales\n}\n\ninterface Prepared {\n /** The duration, scaled to this turn's effort. */\n ms: number\n /** Its log, the model's unit. */\n y: number\n weight: number\n calls?: number\n out?: number\n /** Recorded at this turn's effort, or either effort unknown: its output count is comparable. */\n sameEffort: boolean\n session?: string\n}\n\n/** The samples the model reads: a usable duration, and not a turn that ended on a failure. */\nfunction usableSamples(samples: readonly TurnSample[]): TurnSample[] {\n return samples.filter((sample) => Number.isFinite(sample.ms) && sample.ms >= 0 && !sample.failed)\n}\n\nfunction prepare(samples: readonly TurnSample[], input: { chars?: number; effort?: string }, now: number): Prepared[] {\n const usable = usableSamples(samples)\n const base = usable.map((sample) => charsWeight(input.chars, sample.chars) * recencyWeight(now, sample.at))\n const scales = effortScales(usable, base, input.effort)\n return usable.map((sample, index) => {\n const ms = sample.ms * (sample.effort !== undefined ? (scales.get(sample.effort) ?? 1) : 1)\n return {\n ms,\n y: Math.log(Math.max(1, ms)),\n weight: base[index] * effortWeight(input.effort, sample.effort),\n calls: sample.calls,\n out: sample.out,\n sameEffort: input.effort === undefined || sample.effort === undefined || input.effort === sample.effort,\n session: sample.session,\n }\n })\n}\n\n// ---------------------------------------------------------------------------\n// The fit\n// ---------------------------------------------------------------------------\n\ninterface Moments {\n mean: number\n variance: number\n /** The effective count, (Σw)² / Σw²: six equal weights are six, one heavy and five light are nearer one. */\n n: number\n}\n\nfunction moments(set: readonly Prepared[]): Moments | undefined {\n let sumW = 0\n let sumW2 = 0\n let sumWY = 0\n for (const sample of set) {\n sumW += sample.weight\n sumW2 += sample.weight * sample.weight\n sumWY += sample.weight * sample.y\n }\n if (!(sumW > 0)) return undefined\n const mean = sumWY / sumW\n let sumWD = 0\n for (const sample of set) sumWD += sample.weight * (sample.y - mean) * (sample.y - mean)\n const n = (sumW * sumW) / sumW2\n const variance = n > 1 ? (sumWD / sumW) * (n / (n - 1)) : 0\n return { mean, variance, n }\n}\n\nexport interface LogNormalFit {\n mu: number\n sigma: number\n}\n\n/**\n * A key's moments pulled toward a prior: the mean toward the prior's mean\n * (its own, with no prior) with `meanKappa` pseudo-turns, the variance toward\n * the prior's variance (`DEFAULT_LOG_SIGMA` squared, with none) with `kappa`,\n * σ then clamped. No moments at all is the prior alone.\n */\nfunction shrink(\n own: Moments | undefined,\n prior: LogNormalFit | undefined,\n kappa = SHRINKAGE_KAPPA,\n meanKappa = kappa,\n): LogNormalFit | undefined {\n if (!own) return prior\n const priorMu = prior?.mu ?? own.mean\n const priorVariance = prior ? prior.sigma * prior.sigma : DEFAULT_LOG_SIGMA * DEFAULT_LOG_SIGMA\n const mu = (own.n * own.mean + meanKappa * priorMu) / (own.n + meanKappa)\n const variance = (own.n * own.variance + kappa * priorVariance) / (own.n + kappa)\n return { mu, sigma: Math.min(MAX_LOG_SIGMA, Math.max(MIN_LOG_SIGMA, Math.sqrt(Math.max(0, variance)))) }\n}\n\ninterface Pool {\n fit?: LogNormalFit\n count: number\n /** The pool's effective count, which discounts its pull on a key's mean. */\n n: number\n}\n\n/**\n * The same agent's turns under every other model, at this effort's scale and\n * weighed by recency, without the prompt-length kernel: the pool is a prior\n * worth `SHRINKAGE_KAPPA` turns, which the kernel's precision would not\n * change, and without it the pool depends on the effort alone and is built\n * once per key and effort (`buildTurnModel`) instead of once per estimate.\n */\nfunction agentPool(history: TurnHistory, key: string, effort: string | undefined, now: number): Pool {\n const slash = key.indexOf(\"/\")\n if (slash < 0) return { count: 0, n: 0 }\n const prefix = key.slice(0, slash + 1)\n const pool: Prepared[] = []\n for (const [other, samples] of history) {\n if (other === key || !other.startsWith(prefix)) continue\n pool.push(...prepare(samples, { effort }, now))\n }\n const measured = moments(pool)\n return { fit: shrink(measured, undefined), count: pool.length, n: measured?.n ?? 0 }\n}\n\ninterface KeyFit {\n /** The fit the estimate is read from, the turn's progress conditioned in. */\n fit: LogNormalFit\n /** The session's (else the key's) unconditioned fit: what an `over` turn shows as the usual. */\n usual: LogNormalFit\n /** The key's longest recorded duration at this effort; infinite with no record of its own. */\n maxMs: number\n}\n\nfunction fitKey(\n own: readonly Prepared[],\n pool: Pool,\n input: { calls?: number; out?: number; session?: string },\n): KeyFit | undefined {\n if (own.length + pool.count < MIN_TURN_SAMPLES) return undefined\n const poolPull = pool.fit ? (SHRINKAGE_KAPPA * pool.n) / (pool.n + SHRINKAGE_KAPPA) : SHRINKAGE_KAPPA\n const key = shrink(moments(own), pool.fit, SHRINKAGE_KAPPA, poolPull)\n if (!key) return undefined\n // The session level: this conversation's own turns, shrunk toward the key\n // by the same rule the key is shrunk toward its pool.\n const mine = input.session === undefined ? [] : own.filter((sample) => sample.session === input.session)\n const usual = mine.length > 0 ? (shrink(moments(mine), key) ?? key) : key\n // The turn's progress so far: a recorded turn that made fewer calls, or\n // generated less output at this effort, ended before this one could.\n const calls = input.calls !== undefined && input.calls > 0 ? input.calls : undefined\n const out = input.out !== undefined && input.out > 0 ? input.out : undefined\n let fit = usual\n if (calls !== undefined || out !== undefined) {\n const kept = own.filter(\n (sample) =>\n (calls === undefined || sample.calls === undefined || sample.calls >= calls) &&\n (out === undefined || sample.out === undefined || !sample.sameEffort || sample.out >= out),\n )\n if (kept.length < own.length && kept.length >= MIN_CONDITIONED_SAMPLES) {\n fit = shrink(moments(kept), usual, CONDITIONED_KAPPA) ?? usual\n }\n }\n const maxMs = own.length > 0 ? own.reduce((longest, sample) => Math.max(longest, sample.ms), 0) : Number.POSITIVE_INFINITY\n return { fit, usual, maxMs }\n}\n\n// ---------------------------------------------------------------------------\n// Calibration\n// ---------------------------------------------------------------------------\n\nexport interface Calibration {\n /** Residuals it was fitted on; 0 for the normal curve itself. */\n readonly count: number\n /** What the measured shape weighs against the normal curve, 0 to 1. */\n readonly weight: number\n cdf(z: number): number\n quantile(p: number): number\n}\n\nexport const NORMAL_CALIBRATION: Calibration = { count: 0, weight: 0, cdf: normalCdf, quantile: normalQuantile }\n\n/**\n * The q-quantile of weighted values sorted by value: each value sits at the\n * middle of its own weight on the cumulative scale, linear between them.\n */\nfunction weightedQuantile(sorted: ReadonlyArray<{ value: number; weight: number }>, total: number, q: number): number {\n let cumulative = 0\n let previous: { value: number; at: number } | undefined\n for (const entry of sorted) {\n const at = (cumulative + entry.weight / 2) / total\n cumulative += entry.weight\n if (at >= q) {\n if (!previous) return entry.value\n return previous.value + ((q - previous.at) / (at - previous.at)) * (entry.value - previous.value)\n }\n previous = { value: entry.value, at }\n }\n return sorted[sorted.length - 1].value\n}\n\n/**\n * The calibration from leave-one-out residuals: their distribution smoothed\n * with a Gaussian kernel of Silverman's width, 0.9 · min(sd, IQR/1.349) ·\n * n^(-1/5), blended with the normal curve weighing\n * `CALIBRATION_PRIOR_RESIDUALS`, and tabulated on a grid so a cdf or a\n * quantile is a lookup and the TUI's tick never sums the residuals. Each\n * residual may carry a weight, the recency of the turn it measured, because\n * the fit prefers recent turns and an old turn's miss says how the model did\n * on behaviour it has since moved away from; n is then the effective count.\n * Every term is an increasing curve and the normal one strictly so, which\n * makes the table strictly increasing and each lookup the other's inverse to\n * the grid's resolution. No residuals is the normal curve itself.\n */\nexport function calibrationFrom(residuals: readonly number[], weights?: readonly number[]): Calibration {\n const entries = residuals\n .map((value, index) => ({ value, weight: weights?.[index] ?? 1 }))\n .filter((entry) => Number.isFinite(entry.value) && Number.isFinite(entry.weight) && entry.weight > 0)\n .slice(-MAX_CALIBRATION_RESIDUALS)\n if (entries.length === 0) return NORMAL_CALIBRATION\n let total = 0\n let squares = 0\n let sum = 0\n for (const entry of entries) {\n total += entry.weight\n squares += entry.weight * entry.weight\n sum += entry.weight * entry.value\n }\n const n = (total * total) / squares\n const mean = sum / total\n let deviation = 0\n for (const entry of entries) deviation += entry.weight * (entry.value - mean) * (entry.value - mean)\n const sd = n > 1 ? Math.sqrt((deviation / total) * (n / (n - 1))) : 0\n const sorted = [...entries].sort((a, b) => a.value - b.value)\n const iqr = weightedQuantile(sorted, total, 0.75) - weightedQuantile(sorted, total, 0.25)\n const spreads = [sd, iqr / 1.349].filter((spread) => spread > 0)\n const spread = spreads.length > 0 ? Math.min(...spreads) : 1\n const width = Math.max(0.1, 0.9 * spread * Math.pow(n, -0.2))\n const weight = n / (n + CALIBRATION_PRIOR_RESIDUALS)\n const size = Math.round((GRID_HIGH - GRID_LOW) / GRID_STEP) + 1\n const table = new Float64Array(size)\n for (let index = 0; index < size; index += 1) {\n const z = GRID_LOW + index * GRID_STEP\n let kernel = 0\n for (const entry of entries) kernel += entry.weight * normalCdf((z - entry.value) / width)\n table[index] = weight * (kernel / total) + (1 - weight) * normalCdf(z)\n }\n return {\n count: entries.length,\n weight,\n cdf(z) {\n if (Number.isNaN(z)) return 0.5\n const position = (Math.min(GRID_HIGH, Math.max(GRID_LOW, z)) - GRID_LOW) / GRID_STEP\n const index = Math.min(size - 2, Math.floor(position))\n return table[index] + (position - index) * (table[index + 1] - table[index])\n },\n quantile(p) {\n if (!(p > table[0])) return GRID_LOW\n if (!(p < table[size - 1])) return GRID_HIGH\n // table[low] < p <= table[high], narrowed to one step.\n let low = 0\n let high = size - 1\n while (high - low > 1) {\n const mid = (low + high) >> 1\n if (table[mid] < p) low = mid\n else high = mid\n }\n const span = table[high] - table[low]\n return GRID_LOW + (low + (span > 0 ? (p - table[low]) / span : 1)) * GRID_STEP\n },\n }\n}\n\n/** The total's quantile q, given the turn has already run `truncatedAtMs`. */\nfunction quantileMs(fit: LogNormalFit, calibration: Calibration, truncatedAtMs: number, q: number): number {\n const passed = truncatedAtMs > 0 ? calibration.cdf((Math.log(truncatedAtMs) - fit.mu) / fit.sigma) : 0\n return Math.exp(fit.mu + fit.sigma * calibration.quantile(passed + q * (1 - passed)))\n}\n\n// ---------------------------------------------------------------------------\n// The model\n// ---------------------------------------------------------------------------\n\nexport interface TurnKeyAssessment {\n key: string\n /** Recorded turns the estimate reads, failed ones excluded. */\n turns: number\n failed: number\n /** The plain median of those turns. */\n typicalMs: number\n /** Turns estimated from the rest of their key at their start, the newest `MAX_ASSESSED_PER_KEY`. */\n assessed: number\n /** The median factor between the estimate and the actual, either way: ×1.6 means \"typically off by 60%\". */\n missFactor?: number\n /** The share of assessed turns that outran their estimate; one half is unbiased. */\n overShare?: number\n /** The share that ended inside the band; one half is calibrated. */\n inBandShare?: number\n}\n\nexport interface TurnEstimateAssessment {\n /** Every key in the store, most turns first. */\n keys: TurnKeyAssessment[]\n /** Leave-one-out residuals the calibration was fitted on, over every key. */\n residuals: number\n /** What their measured shape weighs against the normal curve, 0 to 1. */\n calibrationWeight: number\n}\n\nexport interface TurnModel {\n /** What a turn on `key` is expected to take, or undefined with too little record. */\n estimate(key: string, input: TurnEstimateInput): TurnEstimate | undefined\n readonly calibration: Calibration\n /** How the model has done on the turns it has, for `/claude-code-doctor`. */\n assess(): TurnEstimateAssessment\n}\n\nconst EMPTY_HISTORY: TurnHistory = new Map()\n\n/**\n * The model over a whole store, built once per change to it: the leave-one-out\n * estimates and the calibration are computed here, so an estimate is one\n * weighted fit and a few evaluations of the curve.\n */\nexport function buildTurnModel(history: TurnHistory = EMPTY_HISTORY, now: number = Date.now()): TurnModel {\n const pools = new Map<string, Pool>()\n const poolFor = (key: string, effort: string | undefined): Pool => {\n const id = `${key}\\u0000${effort ?? \"\"}`\n let pool = pools.get(id)\n if (!pool) {\n pool = agentPool(history, key, effort, now)\n pools.set(id, pool)\n }\n return pool\n }\n const assessed = new Map<string, Array<{ ms: number; fit: LogNormalFit }>>()\n const residuals: number[] = []\n const residualWeights: number[] = []\n for (const [key, samples] of history) {\n const usable = usableSamples(samples)\n if (usable.length < 2) continue\n const rows: Array<{ ms: number; fit: LogNormalFit }> = []\n for (const held of usable.slice(-MAX_ASSESSED_PER_KEY)) {\n const input = { chars: held.chars, effort: held.effort, session: held.session }\n const own = prepare(\n usable.filter((sample) => sample !== held),\n input,\n now,\n )\n const fitted = fitKey(own, poolFor(key, held.effort), input)\n if (!fitted) continue\n rows.push({ ms: held.ms, fit: fitted.fit })\n residuals.push((Math.log(Math.max(1, held.ms)) - fitted.fit.mu) / fitted.fit.sigma)\n residualWeights.push(recencyWeight(now, held.at))\n }\n if (rows.length > 0) assessed.set(key, rows)\n }\n const calibration = calibrationFrom(residuals, residualWeights)\n\n const estimate = (key: string, input: TurnEstimateInput): TurnEstimate | undefined => {\n const at = input.now ?? now\n const own = prepare(history.get(key) ?? [], input, at)\n const fitted = fitKey(own, poolFor(key, input.effort), input)\n if (!fitted) return undefined\n const elapsed = Math.max(0, Number.isFinite(input.elapsedMs) ? input.elapsedMs : 0)\n if (elapsed > fitted.maxMs) {\n return { totalMs: quantileMs(fitted.usual, calibration, 0, 0.5), fill: 1, over: true }\n }\n const totalMs = Math.max(elapsed, quantileMs(fitted.fit, calibration, elapsed, 0.5))\n const lowMs = Math.min(totalMs, Math.max(elapsed, quantileMs(fitted.fit, calibration, elapsed, 0.25)))\n const highMs = Math.max(totalMs, quantileMs(fitted.fit, calibration, elapsed, 0.75))\n return {\n totalMs,\n fill: totalMs > 0 ? elapsed / totalMs : 0,\n over: false,\n band: {\n lowMs,\n highMs,\n sure: highMs > 0 ? elapsed / highMs : 0,\n maybe: lowMs > 0 ? Math.min(1, elapsed / lowMs) : 0,\n },\n }\n }\n\n const assess = (): TurnEstimateAssessment => {\n const keys: TurnKeyAssessment[] = []\n for (const [key, samples] of history) {\n const usable = usableSamples(samples)\n const failed = samples.filter((sample) => sample.failed).length\n if (usable.length === 0 && failed === 0) continue\n const durations = usable.map((sample) => sample.ms).sort((a, b) => a - b)\n const typicalMs = durations.length > 0 ? durations[Math.floor((durations.length - 1) / 2)] : 0\n const rows = assessed.get(key) ?? []\n const misses: number[] = []\n let over = 0\n let inBand = 0\n for (const row of rows) {\n const expected = Math.exp(row.fit.mu + row.fit.sigma * calibration.quantile(0.5))\n const low = Math.exp(row.fit.mu + row.fit.sigma * calibration.quantile(0.25))\n const high = Math.exp(row.fit.mu + row.fit.sigma * calibration.quantile(0.75))\n misses.push(Math.abs(Math.log(Math.max(1, row.ms) / expected)))\n if (row.ms > expected) over += 1\n if (row.ms >= low && row.ms <= high) inBand += 1\n }\n misses.sort((a, b) => a - b)\n keys.push({\n key,\n turns: usable.length,\n failed,\n typicalMs,\n assessed: rows.length,\n ...(rows.length > 0\n ? {\n missFactor: Math.exp(misses[Math.floor((misses.length - 1) / 2)]),\n overShare: over / rows.length,\n inBandShare: inBand / rows.length,\n }\n : {}),\n })\n }\n keys.sort((a, b) => b.turns - a.turns || a.key.localeCompare(b.key))\n return { keys, residuals: residuals.length, calibrationWeight: calibration.weight }\n }\n\n return { estimate, calibration, assess }\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 alone: the model over one key, which\n * has no pool to borrow from and calibrates only on itself. The TUI builds\n * the model over the whole store instead (`buildTurnModel`).\n */\nexport function estimateTurn(samples: readonly TurnSample[], input: TurnEstimateInput): TurnEstimate | undefined {\n return buildTurnModel(new Map([[\"turn/model\", [...samples]]]), input.now ?? Date.now()).estimate(\"turn/model\", input)\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 the\n * success colour at full strength, `ok` included: the `ok` role, never `done`,\n * which is the finished row's faded success); the reset time in its own\n * (`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\" : \"ok\"\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":";AA2CA,SAAS,YAAY;AACrB,SAAS,eAAe,gBAAgB,QAAQ,QAAQ,YAAY,eAAe;AACnF,SAAS,YAAY,cAAc,iBAAiB;;;ACqD7C,IAAM,kBAAkB,KAAK;AAC7B,IAAM,gBAAgB;AAKtB,IAAM,oBAAoB;AAE1B,IAAM,oBAAoB;AAM1B,IAAM,aAAa;AACnB,IAAM,iBAAiB,CAAC,UAAK,UAAK,UAAK,UAAK,UAAK,UAAK,UAAK,UAAK,UAAK,QAAG;AAE/E,IAAM,eAAe;AAGd,SAAS,gBAAgB,OAAoE;AAClG,QAAMA,SAAQ,SAAS,IAAI,KAAK;AAChC,QAAM,QAAQ,aAAa,KAAKA,KAAI;AACpC,MAAI,CAAC,MAAO,QAAO,EAAE,aAAaA,MAAK;AACvC,SAAO,EAAE,aAAaA,MAAK,MAAM,GAAG,MAAM,KAAK,EAAE,KAAK,GAAG,OAAO,MAAM,CAAC,EAAE;AAC3E;AAEA,SAAS,KAAK,OAAoC;AAChD,SAAO,OAAO,UAAU,YAAY,MAAM,KAAK,IAAI,MAAM,KAAK,IAAI;AACpE;AAGO,SAAS,YAAY,MAAyC;AACnE,QAAM,WAAW,KAAK,YAAY,CAAC;AACnC,SAAO,KAAK,SAAS,SAAS,KAAK,KAAK,SAAS,SAAS;AAC5D;AAEA,SAAS,UAAU,MAAyC;AAC1D,QAAM,QAAQ,KAAK,SAAS,CAAC;AAC7B,SAAO,KAAK,MAAM,aAAa,KAAK,KAAK,MAAM,KAAK;AACtD;AAEA,SAAS,gBAAgB,MAAyC;AAChE,SAAO,KAAK,KAAK,OAAO,WAAW,KAAK,KAAK,KAAK,KAAK;AACzD;AAEA,SAAS,eAAe,MAA8B;AACpD,SAAO,KAAK,OAAO,eAAe,QAAQ,KAAK,UAAU,eAAe;AAC1E;AAEA,IAAM,sBACJ;AAQK,SAAS,cAAc,OAAe,IAAgC;AAC3E,QAAM,QAA2B,CAAC;AAClC,aAAW,SAAS,MAAM,SAAS,mBAAmB,GAAG;AACvD,QAAI,MAAM,CAAC,MAAM,UAAW;AAC5B,UAAM,KAAK,EAAE,WAAW,MAAM,CAAC,GAAG,OAAO,MAAM,CAAC,GAAG,GAAG,CAAC;AAAA,EACzD;AACA,SAAO;AACT;AAEA,SAAS,SAAS,OAA8B;AAC9C,SAAO,UAAU,cAAc,SAAS;AAC1C;AAQO,SAAS,eAAe,SAAiD;AAC9E,MAAI,KAAK,KAAK,OAAO;AACrB,MAAI,CAAC,GAAI,QAAO;AAChB,QAAM,KAAK,GAAG,QAAQ,GAAG;AACzB,MAAI,KAAK,EAAG,MAAK,GAAG,MAAM,GAAG,EAAE;AAC/B,MAAI,SAAS;AACb,QAAM,SAAS,kBAAkB,KAAK,EAAE;AACxC,MAAI,QAAQ;AACV,cAAU,IAAI,OAAO,CAAC,EAAE,YAAY,CAAC;AACrC,SAAK,GAAG,MAAM,GAAG,OAAO,KAAK;AAAA,EAC/B;AACA,MAAI,GAAG,SAAS,OAAO,GAAG;AACxB,aAAS,QAAQ,MAAM;AACvB,SAAK,GAAG,MAAM,GAAG,CAAC,QAAQ,MAAM;AAAA,EAClC;AACA,MAAI,CAAC,GAAG,WAAW,SAAS,EAAG,QAAO,GAAG,EAAE,GAAG,MAAM;AACpD,OAAK,GAAG,MAAM,UAAU,MAAM,EAAE,QAAQ,WAAW,EAAE;AACrD,QAAM,QAAQ,8BAA8B,KAAK,EAAE;AACnD,MAAI,CAAC,MAAO,QAAO,GAAG,EAAE,GAAG,MAAM;AACjC,QAAM,UAAU,MAAM,CAAC,MAAM,SAAY,MAAM,CAAC,IAAI,GAAG,MAAM,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC;AAC3E,SAAO,GAAG,MAAM,CAAC,CAAC,IAAI,OAAO,GAAG,MAAM;AACxC;AAOO,SAAS,oBAAoB,YAAoD;AACtF,QAAM,KAAK,KAAK,UAAU;AAC1B,MAAI,CAAC,IAAI,WAAW,cAAc,EAAG,QAAO;AAC5C,SAAO,GAAG,MAAM,eAAe,MAAM,KAAK;AAC5C;AAGO,SAAS,cACd,QACA,MACQ;AACR,QAAM,QAAQ,MAAM,UAAU;AAC9B,QAAM,aAAa,OAAO,OAAO,YAAY,WAAW,MAAM,UAAU;AACxE,QAAM,gBAAgB,OAAO,OAAO,eAAe,WAAW,MAAM,aAAa;AACjF,QAAM,QAAQ,eAAe,QAAQ,SAAS,UAAU;AACxD,QAAM,SAAS,KAAK,QAAQ,MAAM;AAClC,QAAM,UAAU,QAAQ,QAAQ,KAAK,OAAO,OAAO,IAAI,oBAAoB,aAAa;AACxF,SAAO,CAAC,OAAO,QAAQ,OAAO,EAAE,OAAO,OAAO,EAAE,KAAK,QAAK;AAC5D;AASO,SAAS,WAAW,OAA+C;AACxE,MAAI,OAAO,UAAU,YAAY,CAAC,OAAO,SAAS,KAAK,KAAK,SAAS,EAAG,QAAO;AAC/E,MAAI,QAAQ,KAAO,QAAO;AAC1B,QAAM,SAAS,CAAC,GAAG,GAAG,CAAC,EAAE,IAAI,CAAC,WAAW,MAAM,QAAQ,MAAM,CAAC,EAAE,KAAK,CAACA,UAASA,MAAK,UAAU,CAAC;AAC/F,SAAO,IAAI,UAAU,MAAM,QAAQ,CAAC,CAAC;AACvC;AAOO,SAAS,SAAS,OAAe,OAAuB;AAC7D,QAAM,QAAQ,MAAM,KAAK,MAAM,QAAQ,QAAQ,GAAG,EAAE,KAAK,CAAC;AAC1D,MAAI,SAAS,EAAG,QAAO;AACvB,MAAI,MAAM,UAAU,MAAO,QAAO,MAAM,KAAK,EAAE;AAC/C,MAAI,UAAU,EAAG,QAAO;AACxB,SAAO,GAAG,MAAM,MAAM,GAAG,QAAQ,CAAC,EAAE,KAAK,EAAE,EAAE,QAAQ,CAAC;AACxD;AAQO,SAAS,mBAAmB,OAAoC;AACrE,QAAM,cAAc,oBAAI,IAA6B;AACrD,aAAW,cAAc,MAAM,YAAa,aAAY,IAAI,WAAW,WAAW,UAAU;AAE5F,QAAM,UAAU,oBAAI,IAA2B;AAC/C,QAAM,YAA6B,CAAC;AACpC,aAAW,QAAQ,MAAM,OAAO;AAC9B,UAAM,QAAQ,YAAY,IAAI;AAC9B,QAAI,MAAO,SAAQ,IAAI,OAAO,IAAI;AAAA,QAC7B,WAAU,KAAK,IAAI;AAAA,EAC1B;AAEA,QAAM,OAAsB,CAAC;AAC7B,QAAM,OAAO,oBAAI,IAAY;AAC7B,aAAW,SAAS,MAAM,UAAU;AAClC,QAAI,KAAK,IAAI,MAAM,EAAE,EAAG;AACxB,SAAK,IAAI,MAAM,EAAE;AACjB,UAAM,SAAS,gBAAgB,MAAM,KAAK;AAC1C,QAAI,OAAO,QAAQ,IAAI,MAAM,EAAE;AAC/B,QAAI,CAAC,MAAM;AAGT,YAAM,QAAQ,UAAU;AAAA,QACtB,CAAC,cACC,gBAAgB,SAAS,MAAM,OAAO,gBACrC,OAAO,UAAU,UAAa,UAAU,SAAS,MAAM,OAAO;AAAA,MACnE;AACA,UAAI,SAAS,EAAG,QAAO,UAAU,OAAO,OAAO,CAAC,EAAE,CAAC;AAAA,IACrD;AACA,SAAK,KAAK,OAAO,OAAO,OAAO,QAAQ,MAAM,YAAY,IAAI,MAAM,EAAE,CAAC,CAAC;AAAA,EACzE;AAEA,aAAW,QAAQ,WAAW;AAC5B,QAAI,KAAK,WAAW,aAAa,KAAK,WAAW,UAAW;AAC5D,UAAM,cAAc,gBAAgB,IAAI;AACxC,UAAM,QAAQ,UAAU,IAAI;AAC5B,QAAI,CAAC,eAAe,CAAC,MAAO;AAC5B,SAAK,KAAK;AAAA,MACR,OAAO,SAAS;AAAA,MAChB,aAAa,eAAe;AAAA,MAC5B,YAAY,eAAe,IAAI;AAAA,MAC/B,OAAO;AAAA,MACP,OAAO,cAAc,QAAW,IAAI;AAAA,MACpC,SAAS,KAAK,SAAS,MAAM;AAAA,IAC/B,CAAC;AAAA,EACH;AAEA,QAAM,UAAU,KACb,OAAO,CAAC,QAAQ,IAAI,UAAU,SAAS,EACvC,KAAK,CAAC,GAAG,MAAM,EAAE,UAAU,EAAE,OAAO;AACvC,QAAM,WAAW,KACd,OAAO,CAAC,QAAQ,IAAI,UAAU,SAAS,EACvC,OAAO,CAAC,QAAQ,MAAM,OAAO,IAAI,YAAY,IAAI,YAAY,eAAe,EAC5E,KAAK,CAAC,GAAG,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,QAAQ,EACpE,MAAM,GAAG,aAAa;AACzB,SAAO,CAAC,GAAG,SAAS,GAAG,QAAQ;AACjC;AAEA,SAAS,OACP,OACA,OACA,QACA,MACA,YACa;AACb,QAAM,aAAa,OAAO,eAAe,IAAI,IAAI;AACjD,QAAM,UAAU,MAAM,SAAS,MAAM,WAAW,MAAM;AACtD,QAAM,SAAS,MAAM,OAAO,MAAM,EAAE;AACpC,MAAI;AACJ,MAAI;AACJ,MAAI,QAAQ,CAAC,cAAc,KAAK,WAAW,aAAa,KAAK,WAAW,WAAW;AACjF,YAAQ,KAAK,WAAW,cAAc,SAAS;AAC/C,eAAW,KAAK;AAAA,EAClB,WAAW,YAAY;AACrB,YAAQ,SAAS,WAAW,KAAK;AACjC,eAAW,WAAW;AAAA,EACxB,WAAW,WAAW,QAAQ;AAC5B,YAAQ;AAAA,EACV,WAAW,QAAQ,CAAC,YAAY;AAE9B,YAAQ;AAAA,EACV,WAAW,WAAW,UAAU,MAAM,MAAM,UAAU,mBAAmB;AACvE,YAAQ;AAAA,EACV,WAAW,WAAW,aAAa,YAAY;AAG7C,YAAQ;AAAA,EACV,OAAO;AACL,YAAQ;AAAA,EACV;AACA,MAAI,UAAU,aAAa,aAAa,QAAW;AACjD,eAAW,MAAM,UAAU,MAAM,EAAE,KAAK,MAAM,WAAW;AAAA,EAC3D;AACA,QAAM,OAAO,MAAM,OAAO,MAAM,EAAE;AAClC,SAAO;AAAA,IACL,WAAW,MAAM;AAAA,IACjB,OAAO,OAAO,UAAU,OAAO,UAAU,IAAI,IAAI,WAAc,KAAK,MAAM,KAAK,KAAK;AAAA,IACpF,cAAc,OAAO,gBAAgB,IAAI,IAAI,WAAc,OAAO;AAAA,IAClE;AAAA,IACA;AAAA,IACA,OAAO,cAAc,MAAM,OAAO,MAAM,EAAE,GAAG,IAAI;AAAA,IACjD,QAAQ,KAAK,MAAM,OAAO,MAAM,EAAE,GAAG,MAAM;AAAA,IAC3C,GAAI,SAAS,SAAY,EAAE,KAAK,IAAI,CAAC;AAAA,IACrC;AAAA,IACA;AAAA,EACF;AACF;AAgDO,SAAS,UACd,KACA,QAAgB,mBAChB,QAAQ,GACiC;AACzC,QAAM,WAAW,IAAI,UAAU;AAC/B,QAAM,QACJ,IAAI,UAAU,YACV,eAAe,QAAQ,eAAe,MAAM,IAC5C,IAAI,UAAU,SACZ,WACA;AACR,QAAM,SAAS,IAAI,aAAa,OAAO;AACvC,QAAM,WAAW,SAAS,OAAO,SAAS,IAAI;AAC9C,QAAM,aAAa,KAAK,IAAI,GAAG,KAAK,IAAI,MAAM,KAAK,IAAI,KAAK,EAAE,QAAQ,QAAQ,IAAI,WAAW,CAAC,CAAC;AAC/F,QAAM,QAAQ,SAAS,IAAI,OAAO,UAAU;AAC5C,QAAM,OAAO,QAAQ,IAAI,MAAM,KAAK,KAAK,EAAE,SAAS,IAAI;AACxD,QAAM,cAAc,OAAO,IAAI,SAAS,IAAI,aAAa,IAAI,IAAI;AACjE,QAAM,OAAO,IAAI,MAAM,KAAK,KAAK,EAAE,UAAU,cAAc,IAAI,MAAM,KAAK,WAAW,EAAE,SAAS;AAChG,QAAM,QAAmB;AAAA,IACvB,EAAE,MAAM,OAAO,MAAM,IAAI,UAAU,YAAY,YAAY,IAAI,UAAU,SAAS,SAAS,QAAQ;AAAA,IACnG,EAAE,MAAM,KAAK,MAAM,QAAQ;AAAA,IAC3B,EAAE,MAAM,OAAO,MAAM,WAAW,UAAU,QAAQ;AAAA,EACpD;AACA,MAAI,aAAa;AACf,UAAM,KAAK,EAAE,MAAM,KAAK,MAAM,QAAQ,CAAC;AACvC,UAAM,KAAK,EAAE,MAAM,aAAa,MAAM,WAAW,UAAU,cAAc,CAAC;AAAA,EAC5E;AACA,MAAI,QAAQ;AACV,UAAM,KAAK,EAAE,MAAM,IAAI,OAAO,KAAK,IAAI,GAAG,QAAQ,OAAO,OAAO,MAAM,CAAC,GAAG,MAAM,QAAQ,CAAC;AACzF,UAAM,KAAK,EAAE,MAAM,QAAQ,MAAM,QAAQ,CAAC;AAAA,EAC5C;AAGA,QAAM,SAAoB,CAAC;AAC3B,QAAM,UAAU,QAAQ,WAAW;AACnC,QAAM,SAAS,IAAI,QAAQ,SAAS,IAAI,OAAO,OAAO,EAAE,MAAM,QAAK,IAAI,CAAC;AACxE,QAAM,aAAa,OAAO,OAAO,CAAC,KAAK,UAAU,MAAM,MAAM,KAAK,KAAK,EAAE,QAAQ,CAAC,IAAI,IAAI,KAAK,IAAI,GAAG,OAAO,SAAS,CAAC;AACvH,QAAM,OAAO,WAAW,IAAI,IAAI;AAChC,QAAM,YAAY,OAAO,MAAM,KAAK,IAAI,EAAE,SAAS;AAInD,MAAI;AACJ,MAAI,MAAM;AACR,QAAI,OAAO,WAAW,EAAG,UAAS,aAAa,UAAU,KAAK;AAAA,aACrD,aAAa,IAAI,aAAa,QAAS,UAAS;AAAA,aAChD,aAAa,IAAI,aAAa,QAAS,UAAS;AAAA,EAC3D;AACA,MAAI,OAAO,SAAS,KAAK,WAAW,QAAW;AAC7C,UAAM,WAAW,IAAI,SAAS,OAAO,QAAQ,IAAI,MAAM,IAAI;AAC3D,WAAO,KAAK,EAAE,MAAM,YAAY,MAAM,QAAQ,CAAC;AAC/C,WAAO,QAAQ,CAAC,OAAO,UAAU;AAC/B,UAAI,QAAQ,EAAG,QAAO,KAAK,EAAE,MAAM,UAAO,MAAM,QAAQ,CAAC;AACzD,YAAM,OAAO,UAAU,WAAW,UAAU,WAAW,iBAAiB;AACxE,aAAO,KAAK,EAAE,MAAM,OAAO,KAAK,CAAC;AAAA,IACnC,CAAC;AACD,QAAI,QAAQ,WAAW,QAAW;AAChC,UAAI,OAAQ,QAAO,KAAK,EAAE,MAAM,QAAQ,MAAM,QAAQ,CAAC;AACvD,aAAO,KAAK,EAAE,MAAM,MAAM,MAAM,OAAO,CAAC;AAAA,IAC1C;AAAA,EACF;AACA,SAAO,EAAE,OAAO,OAAO;AACzB;AAGO,SAAS,iBAAiB,MAA6B;AAC5D,QAAM,UAAU,KAAK,OAAO,CAAC,QAAQ,IAAI,UAAU,SAAS,EAAE;AAC9D,SAAO,UAAU,IAAI,GAAG,OAAO,aAAa,GAAG,KAAK,MAAM;AAC5D;;;AClaO,IAAM,YAAY;AAElB,IAAM,qBAAqB;AAC3B,IAAM,WAAW;AACjB,IAAM,WAAW;AAgDxB,SAASC,MAAK,OAAoC;AAChD,SAAO,OAAO,UAAU,YAAY,MAAM,KAAK,IAAI,MAAM,KAAK,IAAI;AACpE;AAEA,SAAS,MAAM,OAAuB;AACpC,SAAO,MAAM,KAAK,KAAK,EAAE;AAC3B;AAQO,SAAS,aAAgB,UAAwB,QAA+C;AACrG,WAAS,QAAQ,SAAS,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG;AAC5D,QAAI,OAAO,SAAS,KAAK,CAAC,EAAG,QAAO,UAAU,IAAI,WAAW,SAAS,MAAM,KAAK;AAAA,EACnF;AACA,SAAO;AACT;AAQO,SAAS,gBAAgB,QAAyC;AACvE,QAAM,WAAW,OAAO;AACxB,MAAI,YAAY;AAChB,WAAS,QAAQ,SAAS,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG;AAC5D,QAAI,SAAS,KAAK,EAAE,SAAS,QAAQ;AACnC,kBAAY;AACZ;AAAA,IACF;AAAA,EACF;AACA,MAAI,QAAQ;AACZ,MAAI;AACJ,MAAI,SAAS;AACb,WAAS,QAAQ,YAAY,GAAG,QAAQ,SAAS,QAAQ,SAAS,GAAG;AACnE,UAAM,UAAU,SAAS,KAAK;AAC9B,QAAI,QAAQ,SAAS,YAAa;AAClC,aAAS,QAAQ,UAAU;AAC3B,eAAW,QAAQ,QAAQ,OAAO;AAChC,eAAS;AACT,YAAM,UAAU,KAAK,WAAW,aAAa,KAAK,WAAW;AAC7D,aAAO,UAAUA,MAAK,KAAK,IAAI,IAAI;AAAA,IACrC;AAAA,EACF;AACA,MAAI,QAAQ;AACZ,MAAI,OAAO;AACX,aAAW,QAAQ,OAAO,OAAO;AAC/B,QAAI,KAAK,WAAW,YAAa;AACjC,aAAS;AACT,QAAI,KAAK,WAAW,YAAa,SAAQ;AAAA,EAC3C;AACA,SAAO;AAAA,IACL,MAAM,QAAQ,IAAI,EAAE,MAAM,MAAM,IAAI;AAAA,IACpC,WAAW,aAAa,IAAI,SAAS,SAAS,EAAE,UAAU;AAAA,IAC1D,OAAO,aAAa,IAAI,SAAS,SAAS,EAAE,QAAQ;AAAA,IACpD;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAmBO,SAAS,gBACd,SACA,OAC0B;AAC1B,QAAM,YAAY,QAAQ,aAAa,MAAM;AAC7C,MAAI,MAAM,WAAW,QAAQ;AAC3B,WAAO;AAAA,MACL,OAAO;AAAA,MACP,MAAM,QAAQ;AAAA,MACd,WAAW,KAAK,IAAI,GAAG,MAAM,OAAO,aAAa,MAAM,IAAI;AAAA,MAC3D,OAAO,QAAQ;AAAA,MACf,MAAM,QAAQ;AAAA,IAChB;AAAA,EACF;AACA,MAAI,MAAM,cAAc,OAAW,QAAO;AAC1C,MAAI,cAAc,UAAa,MAAM,YAAY,UAAW,QAAO;AACnE,MAAI,MAAM,MAAM,MAAM,YAAY,mBAAoB,QAAO;AAC7D,SAAO;AAAA,IACL,OAAO,QAAQ,SAAS,UAAU;AAAA,IAClC,MAAM,QAAQ;AAAA,IACd,WAAW,KAAK,IAAI,GAAG,MAAM,aAAa,aAAa,MAAM,UAAU;AAAA,IACvE,OAAO,QAAQ;AAAA,IACf,MAAM;AAAA,EACR;AACF;AAMO,SAAS,YAAY,SAA0B,OAAuD;AAC3G,QAAM,YAAY,QAAQ,aAAa,MAAM;AAC7C,SAAO;AAAA,IACL,OAAO;AAAA,IACP,MAAM,QAAQ;AAAA,IACd,WAAW,KAAK,IAAI,GAAG,MAAM,MAAM,SAAS;AAAA,IAC5C,OAAO,QAAQ;AAAA,IACf,MAAM,QAAQ;AAAA,EAChB;AACF;AAWO,IAAM,mBAAmB;AAQzB,SAAS,oBAAoB;AAClC,QAAM,OAAO,oBAAI,IAAsD;AACvE,SAAO;AAAA,IACL,QAAQ,WAAmB,QAAuB,KAA2B;AAC3E,UAAI,QAAQ,KAAK,IAAI,SAAS;AAC9B,UAAI,CAAC,OAAO;AACV,gBAAQ,EAAE,QAAQ,UAAU;AAC5B,YAAI,KAAK,QAAQ,kBAAkB;AACjC,gBAAM,SAAS,KAAK,KAAK,EAAE,KAAK,EAAE;AAClC,cAAI,WAAW,OAAW,MAAK,OAAO,MAAM;AAAA,QAC9C;AACA,aAAK,IAAI,WAAW,KAAK;AAAA,MAC3B;AACA,UAAI,WAAW,UAAU,MAAM,WAAW,QAAQ;AAChD,cAAM,YAAY;AAClB,cAAM,YAAY;AAAA,MACpB,WAAW,WAAW,UAAU,MAAM,WAAW,QAAQ;AACvD,cAAM,YAAY;AAAA,MACpB;AACA,UAAI,WAAW,UAAW,OAAM,SAAS;AACzC,aAAO,EAAE,WAAW,MAAM,WAAW,WAAW,MAAM,UAAU;AAAA,IAClE;AAAA,EACF;AACF;AAQO,SAAS,mBAAmB;AACjC,QAAM,OAAO,oBAAI,IAA4C;AAC7D,QAAM,OAAO,CAAC,KAAa,MAAc,SAAyB;AAChE,UAAM,QAAQ,KAAK,IAAI,GAAG;AAC1B,QAAI,SAAS,MAAM,SAAS,MAAM;AAChC,YAAM,OAAO,KAAK,IAAI,MAAM,MAAM,IAAI;AACtC,aAAO,MAAM;AAAA,IACf;AACA,QAAI,CAAC,SAAS,KAAK,QAAQ,kBAAkB;AAC3C,YAAM,SAAS,KAAK,KAAK,EAAE,KAAK,EAAE;AAClC,UAAI,WAAW,OAAW,MAAK,OAAO,MAAM;AAAA,IAC9C;AACA,SAAK,IAAI,KAAK,EAAE,MAAM,KAAK,CAAC;AAC5B,WAAO;AAAA,EACT;AACA,SAAO;AAAA,IACL;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IASA,aAAa,WAAmB,MAAc,UAAsC;AAClF,YAAM,OAAO,KAAK,WAAW,MAAM,SAAS,IAAI;AAChD,UAAI,CAAC,SAAS,KAAM,QAAO,EAAE,GAAG,UAAU,KAAK;AAC/C,YAAM,OAAO,KAAK,GAAG,SAAS,UAAc,MAAM,SAAS,KAAK,IAAI;AACpE,YAAM,QAAQ,KAAK,GAAG,SAAS,WAAe,MAAM,SAAS,KAAK,KAAK;AACvE,aAAO;AAAA,QACL,GAAG;AAAA,QACH;AAAA,QACA,MAAM,EAAE,GAAG,SAAS,MAAM,MAAM,KAAK,IAAI,MAAM,IAAI,GAAG,OAAO,KAAK,IAAI,OAAO,IAAI,EAAE;AAAA,MACrF;AAAA,IACF;AAAA,EACF;AACF;AAOO,SAAS,YAAY,MAAoB,QAAgB,WAAmB;AACjF,MAAI,KAAK,SAAS,KAAK,KAAK,QAAQ,EAAG,QAAO;AAC9C,MAAI,KAAK,QAAQ,KAAK,MAAO,QAAO;AACpC,SAAO,KAAK,IAAI,QAAQ,GAAG,KAAK,IAAI,GAAG,KAAK,MAAO,KAAK,OAAO,KAAK,QAAS,KAAK,CAAC,CAAC;AACtF;AAOO,SAAS,cAAc,UAAwB,QAAgB,WAAmB;AACvF,MAAI,SAAS,KAAM,QAAO;AAC1B,SAAO,KAAK,IAAI,QAAQ,GAAG,KAAK,IAAI,GAAG,KAAK,MAAM,SAAS,OAAO,KAAK,CAAC,CAAC;AAC3E;AAoBO,SAAS,kBAAkB,UAAwB,QAAgB,WAA0B;AAClG,QAAM,SAAS,cAAc,UAAU,KAAK;AAC5C,MAAI,SAAS,QAAQ,CAAC,SAAS,KAAM,QAAO,EAAE,MAAM,QAAQ,QAAQ,MAAM,OAAO;AACjF,QAAM,OAAO,KAAK,IAAI,QAAQ,KAAK,IAAI,GAAG,KAAK,MAAM,SAAS,KAAK,OAAO,KAAK,CAAC,CAAC;AACjF,QAAM,OAAO,KAAK,IAAI,QAAQ,KAAK,IAAI,OAAO,KAAK,MAAM,SAAS,KAAK,QAAQ,KAAK,CAAC,CAAC;AACtF,SAAO,EAAE,MAAM,QAAQ,KAAK;AAC9B;AAGO,IAAM,yBAAyB;AAc/B,SAAS,WAAW,QAAmD,WAAmD;AAC/H,MAAI,CAAC,UAAU,cAAc,OAAW,QAAO;AAC/C,SAAO,OAAO,QAAQ,YAAY,yBAAyB,OAAO,MAAM;AAC1E;AAkBO,SAAS,gBAAgB,UAAwB,WAAmB,UAAiD;AAC1H,MAAI,SAAS,KAAM,QAAO;AAC1B,MAAI,SAAS;AACb,MAAI,MAAM;AACV,MAAI,OAAO;AACX,aAAW,SAAS,UAAU;AAC5B,UAAM,MAAM,MAAM;AAClB,QAAI,MAAM,UAAU,aAAa,CAAC,OAAO,IAAI,KAAM;AACnD,UAAM,OAAO,CAAC,OAAe,KAAK,IAAI,GAAG,KAAK,MAAM,SAAS;AAC7D,aAAS,KAAK,IAAI,QAAQ,KAAK,IAAI,OAAO,CAAC;AAC3C,UAAM,KAAK,IAAI,KAAK,KAAK,IAAI,MAAM,SAAS,IAAI,OAAO,CAAC;AACxD,WAAO,KAAK,IAAI,MAAM,KAAK,IAAI,MAAM,UAAU,IAAI,OAAO,CAAC;AAAA,EAC7D;AACA,QAAM,UAAU,KAAK,IAAI,GAAG,SAAS;AACrC,QAAM,UAAU,KAAK,IAAI,SAAS,SAAS,UAAU,MAAM;AAC3D,QAAM,OAAO,SAAS;AACtB,MAAI,YAAY,SAAS,YAAY,CAAC,QAAS,KAAK,SAAS,UAAU,OAAO,KAAK,UAAU,UAAU,OAAQ;AAC7G,WAAO;AAAA,EACT;AACA,QAAM,OAAO,UAAU,IAAI,UAAU,UAAU;AAC/C,MAAI,CAAC,KAAM,QAAO,EAAE,GAAG,UAAU,SAAS,KAAK;AAC/C,QAAM,QAAQ,KAAK,IAAI,SAAS,KAAK,IAAI,KAAK,OAAO,UAAU,GAAG,CAAC;AACnE,QAAM,SAAS,KAAK,IAAI,SAAS,KAAK,QAAQ,UAAU,IAAI;AAC5D,SAAO;AAAA,IACL,GAAG;AAAA,IACH;AAAA,IACA;AAAA,IACA,MAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA,MAAM,SAAS,IAAI,UAAU,SAAS;AAAA,MACtC,OAAO,QAAQ,IAAI,KAAK,IAAI,GAAG,UAAU,KAAK,IAAI;AAAA,IACpD;AAAA,EACF;AACF;AAGO,SAAS,cAAc,IAAoB;AAChD,QAAM,UAAU,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,GAAI,CAAC;AACjD,MAAI,UAAU,GAAI,QAAO,GAAG,OAAO;AACnC,QAAM,UAAU,KAAK,MAAM,UAAU,EAAE;AACvC,QAAMC,OAAM,CAAC,UAAkB,OAAO,KAAK,EAAE,SAAS,GAAG,GAAG;AAC5D,MAAI,UAAU,GAAI,QAAO,GAAG,OAAO,IAAIA,KAAI,UAAU,EAAE,CAAC;AACxD,SAAO,GAAG,KAAK,MAAM,UAAU,EAAE,CAAC,IAAIA,KAAI,UAAU,EAAE,CAAC;AACzD;AAwBO,SAAS,mBACd,MACA,MACA,QAAgB,mBAChB,QAAQ,GACQ;AAChB,QAAM,QAAQ,KAAK,UAAU;AAC7B,QAAM,YAA6B,KAAK,UAAU,UAAU,UAAU;AACtE,QAAM,eACJ,SAAS,WACL,CAAC,EAAE,MAAM,YAAY,MAAM,QAAQ,CAAC,IACpC;AAAA,IACE,KAAK,UAAU,YACX,EAAE,MAAM,eAAe,QAAQ,eAAe,MAAM,GAAG,MAAM,UAAU,IACvE,EAAE,MAAM,KAAK,UAAU,SAAS,WAAM,UAAK,MAAM,UAAU;AAAA,IAC/D,EAAE,MAAM,KAAK,MAAM,QAAQ;AAAA,EAC7B;AAEN,QAAM,WAAW,CAAC,SAAS,CAAC,KAAK,OAAO,KAAK,WAAW;AACxD,QAAM,MAAiB,CAAC;AACxB,QAAM,UAAU,CAAC,YAAuC;AACtD,aAAS,OAAO,GAAG,OAAO,WAAW,QAAQ,EAAG,KAAI,KAAK,QAAQ,IAAI,CAAC;AAAA,EACxE;AACA,MAAI,OAAO;AACT,YAAQ,OAAO,EAAE,MAAM,UAAU,MAAM,UAAU,EAAE;AAAA,EACrD,WAAW,KAAK,MAAM;AACpB,UAAM,SAAS,YAAY,KAAK,IAAI;AACpC,YAAQ,CAAC,SAAU,OAAO,SAAS,EAAE,MAAM,UAAU,MAAM,WAAW,IAAI,EAAE,MAAM,UAAU,MAAM,YAAY,CAAE;AAAA,EAClH,WAAW,UAAU;AACnB,UAAMC,SAAQ,kBAAkB,QAAQ;AACxC;AAAA,MAAQ,CAAC,SACP,SAAS,OACL,EAAE,MAAM,UAAU,MAAM,WAAW,IACnC,OAAOA,OAAM,OACX,EAAE,MAAM,UAAU,MAAM,WAAW,IACnC,OAAOA,OAAM,SACX,EAAE,MAAM,UAAU,MAAM,aAAa,IACrC,OAAOA,OAAM,OACX,EAAE,MAAM,UAAU,MAAM,WAAW,IACnC,EAAE,MAAM,UAAU,MAAM,YAAY;AAAA,IAChD;AAAA,EACF;AAEA,QAAM,WAAsB,CAAC;AAC7B,MAAI,KAAK,MAAM;AACb,aAAS,KAAK,EAAE,MAAM,GAAG,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,KAAK,IAAI,MAAM,QAAQ,UAAU,WAAW,CAAC;AAClG,aAAS,KAAK,EAAE,MAAM,cAAc,KAAK,SAAS,GAAG,MAAM,QAAQ,CAAC;AAAA,EACtE,WAAW,UAAU;AACnB,aAAS,KAAK;AAAA,MACZ,MAAM,GAAG,cAAc,KAAK,SAAS,CAAC,KAAK,cAAc,SAAS,OAAO,CAAC;AAAA,MAC1E,MAAM,SAAS,OAAO,kBAAkB;AAAA,IAC1C,CAAC;AAAA,EACH,OAAO;AACL,aAAS,KAAK,EAAE,MAAM,cAAc,KAAK,SAAS,GAAG,MAAM,QAAQ,CAAC;AAAA,EACtE;AACA,QAAM,QACJ,CAAC,KAAK,QAAQ,CAAC,YAAY,KAAK,QAAQ,IACpC,EAAE,MAAM,GAAG,KAAK,KAAK,QAAQ,KAAK,UAAU,IAAI,KAAK,GAAG,IAAI,MAAM,QAAQ,IAC1E;AACN,QAAM,OAA4B,CAAC,SAAS,KAAK,OAAO,EAAE,MAAM,KAAK,MAAM,MAAM,OAAO,IAAI;AAE5F,QAAM,SACJ,QAAQ,aAAa,OAAO,CAAC,KAAK,YAAY,MAAM,MAAM,QAAQ,IAAI,GAAG,CAAC,KAAK,IAAI,SAAS,IAAI,YAAY,IAAI;AAClH,QAAM,UAAU,CAACC,YACfA,QAAO,OAAO,CAAC,KAAK,UAAU,MAAM,MAAM,MAAM,IAAI,GAAG,CAAC,IAAI,IAAI,KAAK,IAAI,GAAGA,QAAO,SAAS,CAAC;AAC/F,MAAI,SAAS,CAAC,GAAG,UAAU,GAAI,QAAQ,CAAC,KAAK,IAAI,CAAC,GAAI,GAAI,OAAO,CAAC,IAAI,IAAI,CAAC,CAAE;AAC7E,MAAI,QAAQ,MAAM,IAAI,UAAU,MAAO,UAAS,CAAC,GAAG,UAAU,GAAI,OAAO,CAAC,IAAI,IAAI,CAAC,CAAE;AACrF,MAAI,QAAQ,MAAM,IAAI,UAAU,MAAM;AACpC,UAAM,OAAO,SAAS,QAAQ,QAAQ,IAAI;AAC1C,aAAS,QAAQ,IAAI,CAAC,GAAG,UAAU,EAAE,MAAM,SAAS,KAAK,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,IAAI;AAAA,EAC1F;AACA,QAAM,OAAkB,IAAI,SAAS,IAAI,CAAC,EAAE,MAAM,MAAM,MAAM,QAAQ,CAAC,IAAI,CAAC;AAC5E,SAAO,QAAQ,CAAC,OAAO,UAAU;AAC/B,QAAI,QAAQ,EAAG,MAAK,KAAK,EAAE,MAAM,UAAO,MAAM,QAAQ,CAAC;AACvD,SAAK,KAAK,KAAK;AAAA,EACjB,CAAC;AACD,SAAO,EAAE,MAAM,cAAc,KAAK,KAAK;AACzC;;;ACrfA,SAAS,WAAW,cAAc,YAAY,UAAU,qBAAqB;AAC7E,SAAS,eAAe;AACxB,SAAS,YAAY;AAed,IAAM,0BAA0B,IAAI,KAAK,KAAK,KAAK;AAE1D,IAAI,eAA8B;AAG3B,SAAS,uBAA+B;AAC7C,MAAI,aAAc,QAAO;AACzB,QAAM,YAAY,QAAQ,IAAI,kBAAkB,KAAK,QAAQ,GAAG,UAAU,OAAO;AACjF,SAAO,KAAK,WAAW,+BAA+B,qBAAqB;AAC7E;AAEA,SAAS,SAAS,OAAsC;AACtD,QAAM,SAAS;AACf,MAAI,CAAC,UAAU,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,EAAG,QAAO;AAC3E,MAAI,OAAO,OAAO,OAAO,YAAY,CAAC,OAAO,SAAS,OAAO,EAAE,EAAG,QAAO;AACzE,QAAM,KAAK,CAAC,UAAmB,UAAU,UAAa,OAAO,UAAU;AACvE,MAAI,CAAC,GAAG,OAAO,KAAK,KAAK,CAAC,GAAG,OAAO,MAAM,KAAK,CAAC,GAAG,OAAO,OAAO,KAAK,CAAC,GAAG,OAAO,KAAK,EAAG,QAAO;AAChG,SAAO,CAAC,CAAC,OAAO;AAClB;AAEA,SAAS,YAAY,MAAwC;AAC3D,QAAM,MAAM,oBAAI,IAAyB;AACzC,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,aAAa,MAAM,MAAM,CAAC;AACpD,UAAM,UAAU,UAAU,OAAO,WAAW,WAAW,OAAO,WAAW;AACzE,QAAI,CAAC,WAAW,OAAO,YAAY,SAAU,QAAO;AACpD,UAAM,SAAS,KAAK,IAAI,IAAI;AAC5B,UAAM,QAAQ,OAAO,QAAQ,OAAkC,EAC5D,OAAO,CAAC,UAA0C,SAAS,MAAM,CAAC,CAAC,CAAC,EACpE,OAAO,CAAC,CAAC,EAAE,MAAM,MAAM,OAAO,MAAM,MAAM,EAC1C,KAAK,CAAC,GAAG,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;AACnC,eAAW,CAAC,KAAK,MAAM,KAAK,OAAO;AACjC,UAAI,IAAI,KAAK;AAAA,QACX,OAAO,OAAO;AAAA,QACd,GAAI,OAAO,SAAS,EAAE,QAAQ,OAAO,OAAO,IAAI,CAAC;AAAA,QACjD,GAAI,OAAO,UAAU,EAAE,SAAS,OAAO,QAAQ,IAAI,CAAC;AAAA,QACpD,GAAI,OAAO,QAAQ,EAAE,OAAO,OAAO,MAAM,IAAI,CAAC;AAAA,QAC9C,IAAI,OAAO;AAAA,MACb,CAAC;AAAA,IACH;AAAA,EACF,QAAQ;AAAA,EAGR;AACA,SAAO;AACT;AAkDO,SAAS,wBAAwB,OAAqB,sBAAsB;AACjF,MAAI,WAAW;AACf,MAAI,SAAS,oBAAI,IAAyB;AAC1C,SAAO;AAAA,IACL,OAAiC;AAC/B,YAAM,OAAO,KAAK;AAClB,UAAI,OAAO;AACX,UAAI;AACF,cAAM,QAAQ,SAAS,IAAI;AAC3B,eAAO,GAAG,MAAM,OAAO,IAAI,MAAM,IAAI,IAAI,MAAM,GAAG;AAAA,MACpD,QAAQ;AACN,eAAO;AAAA,MACT;AACA,UAAI,SAAS,UAAU;AACrB,mBAAW;AACX,iBAAS,SAAS,YAAY,oBAAI,IAAI,IAAI,YAAY,IAAI;AAAA,MAC5D;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACF;;;ACpJA,SAAS,aAAAC,YAAW,gBAAAC,eAAc,cAAAC,aAAY,YAAAC,WAAU,iBAAAC,sBAAqB;AAC7E,SAAS,WAAAC,gBAAe;AACxB,SAAS,QAAAC,aAAY;;;ACCrB,SAAS,aAAAC,YAAW,gBAAAC,eAAc,cAAAC,aAAY,YAAAC,WAAU,iBAAAC,sBAAqB;AAC7E,SAAS,WAAAC,gBAAe;AACxB,SAAS,QAAAC,aAAY;AAYd,IAAM,oBAAoB;AAE1B,IAAM,2BAA2B,KAAK,KAAK,KAAK;AAEvD,IAAM,MAAM;AAEZ,IAAIC,gBAA8B;AAE3B,SAAS,wBAAgC;AAC9C,MAAIA,cAAc,QAAOA;AACzB,QAAM,YAAY,QAAQ,IAAI,kBAAkBD,MAAKD,SAAQ,GAAG,UAAU,OAAO;AACjF,SAAOC,MAAK,WAAW,+BAA+B,oBAAoB;AAC5E;AAEA,SAAS,WAAW,OAAuC;AACzD,QAAM,SAAS;AACf,MAAI,CAAC,UAAU,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,EAAG,QAAO;AAC3E,QAAM,QAAQ,CAAC,UAAmB,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,KAAK,SAAS;AAClG,SAAO,MAAM,OAAO,IAAI,KAAK,MAAM,OAAO,GAAG,KAAK,MAAM,OAAO,EAAE;AACnE;AAEA,SAASE,aAAY,MAAc,KAAwC;AACzE,QAAM,MAAM,oBAAI,IAA0B;AAC1C,MAAI;AACF,UAAM,SAAS,KAAK,MAAMP,cAAa,MAAM,MAAM,CAAC;AACpD,UAAM,UAAU,UAAU,OAAO,WAAW,WAAW,OAAO,QAAQ;AACtE,QAAI,CAAC,WAAW,OAAO,YAAY,SAAU,QAAO;AACpD,UAAM,SAAS,MAAM;AACrB,UAAM,QAAQ,OAAO,QAAQ,OAAkC,EAC5D,OAAO,CAAC,UAA2C,IAAI,KAAK,MAAM,CAAC,CAAC,KAAK,WAAW,MAAM,CAAC,CAAC,CAAC,EAC7F,OAAO,CAAC,CAAC,EAAE,MAAM,MAAM,OAAO,MAAM,MAAM,EAC1C,KAAK,CAAC,GAAG,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,EAChC,MAAM,CAAC,iBAAiB;AAC3B,eAAW,CAAC,KAAK,MAAM,KAAK,MAAO,KAAI,IAAI,KAAK,EAAE,MAAM,OAAO,MAAM,KAAK,KAAK,MAAM,OAAO,GAAG,GAAG,IAAI,OAAO,GAAG,CAAC;AAAA,EACnH,QAAQ;AAAA,EAGR;AACA,SAAO;AACT;AAgCO,SAAS,yBAAyB,OAAqB,uBAAuB;AACnF,MAAI,WAAW;AACf,MAAI,SAAS,oBAAI,IAA0B;AAC3C,SAAO;AAAA,IACL,KAAK,MAAc,KAAK,IAAI,GAA8B;AACxD,YAAM,OAAO,KAAK;AAClB,UAAI,OAAO;AACX,UAAI;AACF,cAAM,QAAQQ,UAAS,IAAI;AAC3B,eAAO,GAAG,MAAM,OAAO,IAAI,MAAM,IAAI,IAAI,MAAM,GAAG;AAAA,MACpD,QAAQ;AACN,eAAO;AAAA,MACT;AACA,UAAI,SAAS,UAAU;AACrB,mBAAW;AACX,iBAAS,SAAS,YAAY,oBAAI,IAAI,IAAIC,aAAY,MAAM,GAAG;AAAA,MACjE;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACF;;;ADnEO,IAAM,mBAAmB;AAIzB,IAAM,yBAAyB,KAAK,KAAK,KAAK,KAAK;AAE1D,IAAM,cAAc;AAEpB,IAAIC,gBAA8B;AAE3B,SAAS,uBAA+B;AAC7C,MAAIA,cAAc,QAAOA;AACzB,QAAM,YAAY,QAAQ,IAAI,kBAAkBC,MAAKC,SAAQ,GAAG,UAAU,OAAO;AACjF,SAAOD,MAAK,WAAW,+BAA+B,mBAAmB;AAC3E;AAGO,SAAS,eAAe,OAA2B,OAAuB;AAC/E,SAAO,GAAG,SAAS,MAAM,IAAI,KAAK;AACpC;AAUO,SAAS,WAAW,WAAmD;AAC5E,MAAI,CAAC,aAAa,cAAc,UAAW,QAAO;AAClD,MAAI,OAAO;AACX,WAAS,QAAQ,GAAG,QAAQ,UAAU,QAAQ,SAAS,GAAG;AACxD,YAAQ,UAAU,WAAW,KAAK;AAClC,WAAO,KAAK,KAAK,MAAM,QAAU,MAAM;AAAA,EACzC;AACA,SAAO,KAAK,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG;AAC1C;AAMA,IAAM,kBAAkB;AAAA,EACtB;AAAA,EACA;AAAA,EACA;AACF;AASO,SAAS,YAAY,OAA8C;AACxE,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,MAAI,QAAQ;AACZ,aAAWE,SAAQ,OAAO;AACxB,QAAI,WAAWA;AACf,eAAW,SAAS,gBAAiB,YAAW,SAAS,QAAQ,OAAO,EAAE;AAC1E,aAAS,SAAS,KAAK,EAAE;AAAA,EAC3B;AACA,SAAO;AACT;AA0LA,SAAS,SAAS,OAAqC;AACrD,QAAM,SAAS;AACf,MAAI,CAAC,UAAU,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,EAAG,QAAO;AAC3E,MAAI,OAAO,OAAO,OAAO,YAAY,CAAC,OAAO,SAAS,OAAO,EAAE,KAAK,OAAO,KAAK,EAAG,QAAO;AAC1F,MAAI,OAAO,OAAO,OAAO,YAAY,CAAC,OAAO,SAAS,OAAO,EAAE,EAAG,QAAO;AACzE,MAAI,OAAO,UAAU,WAAc,OAAO,OAAO,UAAU,YAAY,CAAC,OAAO,SAAS,OAAO,KAAK,KAAK,OAAO,QAAQ,IAAI;AAC1H,WAAO;AAAA,EACT;AACA,MAAI,OAAO,WAAW,UAAa,OAAO,OAAO,WAAW,SAAU,QAAO;AAC7E,MAAI,OAAO,UAAU,WAAc,OAAO,OAAO,UAAU,YAAY,CAAC,OAAO,SAAS,OAAO,KAAK,KAAK,OAAO,QAAQ,IAAI;AAC1H,WAAO;AAAA,EACT;AACA,MAAI,OAAO,QAAQ,WAAc,OAAO,OAAO,QAAQ,YAAY,CAAC,OAAO,SAAS,OAAO,GAAG,KAAK,OAAO,MAAM,IAAI;AAClH,WAAO;AAAA,EACT;AACA,MAAI,OAAO,WAAW,UAAa,OAAO,OAAO,WAAW,UAAW,QAAO;AAC9E,MAAI,OAAO,YAAY,WAAc,OAAO,OAAO,YAAY,YAAY,CAAC,YAAY,KAAK,OAAO,OAAO,IAAI;AAC7G,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEA,SAAS,YAAY,QAAgC;AACnD,SAAO;AAAA,IACL,IAAI,KAAK,MAAM,OAAO,EAAE;AAAA,IACxB,GAAI,OAAO,UAAU,SAAY,EAAE,OAAO,KAAK,MAAM,OAAO,KAAK,EAAE,IAAI,CAAC;AAAA,IACxE,GAAI,OAAO,SAAS,EAAE,QAAQ,OAAO,OAAO,IAAI,CAAC;AAAA,IACjD,GAAI,OAAO,UAAU,SAAY,EAAE,OAAO,KAAK,MAAM,OAAO,KAAK,EAAE,IAAI,CAAC;AAAA,IACxE,GAAI,OAAO,QAAQ,SAAY,EAAE,KAAK,KAAK,MAAM,OAAO,GAAG,EAAE,IAAI,CAAC;AAAA,IAClE,GAAI,OAAO,SAAS,EAAE,QAAQ,KAAK,IAAI,CAAC;AAAA,IACxC,GAAI,OAAO,UAAU,EAAE,SAAS,OAAO,QAAQ,IAAI,CAAC;AAAA,IACpD,IAAI,OAAO;AAAA,EACb;AACF;AAEA,SAASC,aAAY,MAAc,KAA0B;AAC3D,QAAM,MAAmB,oBAAI,IAAI;AACjC,MAAI;AACF,UAAM,SAAS,KAAK,MAAMC,cAAa,MAAM,MAAM,CAAC;AACpD,UAAM,UAAU,UAAU,OAAO,WAAW,WAAW,OAAO,QAAQ;AACtE,QAAI,CAAC,WAAW,OAAO,YAAY,SAAU,QAAO;AACpD,UAAM,SAAS,MAAM;AACrB,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,OAAkC,GAAG;AAC7E,UAAI,CAAC,OAAO,CAAC,MAAM,QAAQ,KAAK,EAAG;AACnC,YAAM,UAAU,MACb,OAAO,QAAQ,EACf,OAAO,CAAC,WAAW,OAAO,MAAM,MAAM,EACtC,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,EAAE,EAAE,EAC1B,MAAM,CAAC,gBAAgB,EACvB,IAAI,WAAW;AAClB,UAAI,QAAQ,SAAS,EAAG,KAAI,IAAI,KAAK,OAAO;AAAA,IAC9C;AAAA,EACF,QAAQ;AAAA,EAGR;AACA,SAAO;AACT;AAoCO,SAAS,wBAAwB,OAAqB,sBAAsB;AACjF,MAAI,WAAW;AACf,MAAI,SAAsB,oBAAI,IAAI;AAClC,SAAO;AAAA,IACL,KAAK,MAAc,KAAK,IAAI,GAAgB;AAC1C,YAAM,OAAO,KAAK;AAClB,UAAI,OAAO;AACX,UAAI;AACF,cAAM,QAAQC,UAAS,IAAI;AAC3B,eAAO,GAAG,MAAM,OAAO,IAAI,MAAM,IAAI,IAAI,MAAM,GAAG;AAAA,MACpD,QAAQ;AACN,eAAO;AAAA,MACT;AACA,UAAI,SAAS,UAAU;AACrB,mBAAW;AACX,iBAAS,SAAS,YAAY,oBAAI,IAAI,IAAIC,aAAY,MAAM,GAAG;AAAA,MACjE;AACA,aAAO;AAAA,IACT;AAAA,EACF;AACF;;;AExVO,IAAM,mBAAmB;AAEzB,IAAM,kBAAkB,KAAK,IAAI,CAAC;AAElC,IAAM,sBAAsB;AAE5B,IAAM,2BAA2B;AAEjC,IAAM,uBAAuB,IAAI,KAAK,KAAK,KAAK;AAEhD,IAAM,0BAA0B;AAEhC,IAAM,kBAAkB;AAExB,IAAM,oBAAoB;AAE1B,IAAM,oBAAoB;AAE1B,IAAM,gBAAgB;AACtB,IAAM,gBAAgB;AAEtB,IAAM,8BAA8B;AAEpC,IAAM,uBAAuB;AAEpC,IAAM,4BAA4B;AAElC,IAAM,WAAW;AACjB,IAAM,YAAY;AAClB,IAAM,YAAY;AAElB,IAAM,cAAc;AA2Cb,SAAS,UAAU,GAAmB;AAC3C,MAAI,CAAC,OAAO,SAAS,CAAC,EAAG,QAAO,IAAI,IAAI,IAAI;AAC5C,QAAM,IAAI,KAAK,IAAI,CAAC;AACpB,QAAM,IAAI,KAAK,IAAI,YAAY;AAC/B,QAAM,OAAO,KAAK,aAAa,KAAK,eAAe,KAAK,cAAc,KAAK,eAAe,IAAI;AAC9F,QAAM,OAAQ,KAAK,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,KAAK,IAAI,KAAK,EAAE,IAAK;AACjE,SAAO,KAAK,IAAI,IAAI,OAAO;AAC7B;AAGO,SAAS,eAAe,GAAmB;AAChD,QAAM,IAAI,KAAK,IAAI,IAAI,OAAO,KAAK,IAAI,OAAO,CAAC,CAAC;AAChD,QAAM,IAAI,CAAC,oBAAsB,mBAAqB,oBAAsB,kBAAoB,oBAAsB,iBAAiB;AACvI,QAAM,IAAI,CAAC,oBAAsB,mBAAqB,oBAAsB,mBAAqB,kBAAoB;AACrH,QAAM,IAAI,CAAC,uBAAuB,qBAAuB,oBAAoB,oBAAoB,mBAAmB,iBAAiB;AACrI,QAAM,IAAI,CAAC,sBAAsB,oBAAsB,mBAAmB,iBAAiB;AAC3F,QAAM,MAAM;AACZ,MAAI,IAAI,OAAO,IAAI,IAAI,KAAK;AAC1B,UAAMC,KAAI,KAAK,KAAK,KAAK,KAAK,IAAI,IAAI,MAAM,IAAI,IAAI,CAAC,CAAC;AACtD,UAAM,SAAS,EAAE,CAAC,IAAIA,KAAI,EAAE,CAAC,KAAKA,KAAI,EAAE,CAAC,KAAKA,KAAI,EAAE,CAAC,KAAKA,KAAI,EAAE,CAAC,KAAKA,KAAI,EAAE,CAAC,SAAS,EAAE,CAAC,IAAIA,KAAI,EAAE,CAAC,KAAKA,KAAI,EAAE,CAAC,KAAKA,KAAI,EAAE,CAAC,KAAKA,KAAI;AACrI,WAAO,IAAI,MAAM,IAAI,CAAC;AAAA,EACxB;AACA,QAAM,IAAI,IAAI;AACd,QAAM,IAAI,IAAI;AACd,cAAa,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,KAAK,SAAU,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,KAAK,IAAI;AACvJ;AAMA,SAAS,YAAY,GAAuB,GAA+B;AACzE,MAAI,MAAM,UAAa,MAAM,OAAW,QAAO;AAC/C,QAAM,WAAW,KAAK,KAAK,IAAI,gBAAgB,IAAI,YAAY,IAAI;AACnE,SAAO,KAAK,IAAI,OAAO,WAAW,QAAQ;AAC5C;AAEA,SAAS,aAAa,GAAuB,GAA+B;AAC1E,SAAO,MAAM,UAAa,MAAM,UAAa,MAAM,IAAI,IAAI;AAC7D;AAEA,SAAS,cAAc,KAAa,IAAoB;AACtD,QAAM,MAAM,KAAK,IAAI,GAAG,MAAM,EAAE;AAChC,SAAO,KAAK,IAAI,KAAK,MAAM,oBAAoB;AACjD;AAGA,SAAS,eAAe,QAA2B,SAAoC;AACrF,QAAM,QAAQ,OAAO,IAAI,CAAC,GAAG,UAAU,KAAK,EAAE,KAAK,CAAC,GAAG,MAAM,OAAO,CAAC,IAAI,OAAO,CAAC,CAAC;AAClF,QAAM,QAAQ,MAAM,OAAO,CAAC,KAAK,UAAU,MAAM,QAAQ,KAAK,GAAG,CAAC;AAClE,MAAI,aAAa;AACjB,aAAW,SAAS,OAAO;AACzB,kBAAc,QAAQ,KAAK;AAC3B,QAAI,cAAc,QAAQ,EAAG,QAAO,OAAO,KAAK;AAAA,EAClD;AACA,SAAO,OAAO,MAAM,MAAM,SAAS,CAAC,CAAC;AACvC;AAWA,SAAS,aAAa,SAAgC,SAA4B,QAAiD;AACjI,QAAM,SAAS,oBAAI,IAAoB;AACvC,MAAI,WAAW,OAAW,QAAO;AACjC,QAAM,SAAS,oBAAI,IAAsB;AACzC,UAAQ,QAAQ,CAAC,QAAQ,UAAU;AACjC,QAAI,OAAO,WAAW,OAAW;AACjC,UAAM,QAAQ,OAAO,IAAI,OAAO,MAAM,KAAK,CAAC;AAC5C,UAAM,KAAK,KAAK;AAChB,WAAO,IAAI,OAAO,QAAQ,KAAK;AAAA,EACjC,CAAC;AACD,QAAM,MAAM,OAAO,IAAI,MAAM;AAC7B,MAAI,CAAC,OAAO,IAAI,SAAS,yBAA0B,QAAO;AAC1D,QAAM,WAAW,CAAC,YAChB;AAAA,IACE,QAAQ,IAAI,CAAC,UAAU,QAAQ,KAAK,EAAE,EAAE;AAAA,IACxC,QAAQ,IAAI,CAAC,UAAU,QAAQ,KAAK,CAAC;AAAA,EACvC;AACF,QAAM,YAAY,SAAS,GAAG;AAC9B,aAAW,CAAC,OAAO,OAAO,KAAK,QAAQ;AACrC,QAAI,UAAU,UAAU,QAAQ,SAAS,yBAA0B;AACnE,UAAM,cAAc,SAAS,OAAO;AACpC,QAAI,YAAY,KAAK,cAAc,EAAG,QAAO,IAAI,OAAO,YAAY,WAAW;AAAA,EACjF;AACA,SAAO;AACT;AAgBA,SAAS,cAAc,SAA8C;AACnE,SAAO,QAAQ,OAAO,CAAC,WAAW,OAAO,SAAS,OAAO,EAAE,KAAK,OAAO,MAAM,KAAK,CAAC,OAAO,MAAM;AAClG;AAEA,SAAS,QAAQ,SAAgC,OAA4C,KAAyB;AACpH,QAAM,SAAS,cAAc,OAAO;AACpC,QAAM,OAAO,OAAO,IAAI,CAAC,WAAW,YAAY,MAAM,OAAO,OAAO,KAAK,IAAI,cAAc,KAAK,OAAO,EAAE,CAAC;AAC1G,QAAM,SAAS,aAAa,QAAQ,MAAM,MAAM,MAAM;AACtD,SAAO,OAAO,IAAI,CAAC,QAAQ,UAAU;AACnC,UAAM,KAAK,OAAO,MAAM,OAAO,WAAW,SAAa,OAAO,IAAI,OAAO,MAAM,KAAK,IAAK;AACzF,WAAO;AAAA,MACL;AAAA,MACA,GAAG,KAAK,IAAI,KAAK,IAAI,GAAG,EAAE,CAAC;AAAA,MAC3B,QAAQ,KAAK,KAAK,IAAI,aAAa,MAAM,QAAQ,OAAO,MAAM;AAAA,MAC9D,OAAO,OAAO;AAAA,MACd,KAAK,OAAO;AAAA,MACZ,YAAY,MAAM,WAAW,UAAa,OAAO,WAAW,UAAa,MAAM,WAAW,OAAO;AAAA,MACjG,SAAS,OAAO;AAAA,IAClB;AAAA,EACF,CAAC;AACH;AAaA,SAAS,QAAQ,KAA+C;AAC9D,MAAI,OAAO;AACX,MAAI,QAAQ;AACZ,MAAI,QAAQ;AACZ,aAAW,UAAU,KAAK;AACxB,YAAQ,OAAO;AACf,aAAS,OAAO,SAAS,OAAO;AAChC,aAAS,OAAO,SAAS,OAAO;AAAA,EAClC;AACA,MAAI,EAAE,OAAO,GAAI,QAAO;AACxB,QAAM,OAAO,QAAQ;AACrB,MAAI,QAAQ;AACZ,aAAW,UAAU,IAAK,UAAS,OAAO,UAAU,OAAO,IAAI,SAAS,OAAO,IAAI;AACnF,QAAM,IAAK,OAAO,OAAQ;AAC1B,QAAM,WAAW,IAAI,IAAK,QAAQ,QAAS,KAAK,IAAI,MAAM;AAC1D,SAAO,EAAE,MAAM,UAAU,EAAE;AAC7B;AAaA,SAAS,OACP,KACA,OACA,QAAQ,iBACR,YAAY,OACc;AAC1B,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,UAAU,OAAO,MAAM,IAAI;AACjC,QAAM,gBAAgB,QAAQ,MAAM,QAAQ,MAAM,QAAQ,oBAAoB;AAC9E,QAAM,MAAM,IAAI,IAAI,IAAI,OAAO,YAAY,YAAY,IAAI,IAAI;AAC/D,QAAM,YAAY,IAAI,IAAI,IAAI,WAAW,QAAQ,kBAAkB,IAAI,IAAI;AAC3E,SAAO,EAAE,IAAI,OAAO,KAAK,IAAI,eAAe,KAAK,IAAI,eAAe,KAAK,KAAK,KAAK,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,EAAE;AACzG;AAgBA,SAAS,UAAU,SAAsB,KAAa,QAA4B,KAAmB;AACnG,QAAM,QAAQ,IAAI,QAAQ,GAAG;AAC7B,MAAI,QAAQ,EAAG,QAAO,EAAE,OAAO,GAAG,GAAG,EAAE;AACvC,QAAM,SAAS,IAAI,MAAM,GAAG,QAAQ,CAAC;AACrC,QAAM,OAAmB,CAAC;AAC1B,aAAW,CAAC,OAAO,OAAO,KAAK,SAAS;AACtC,QAAI,UAAU,OAAO,CAAC,MAAM,WAAW,MAAM,EAAG;AAChD,SAAK,KAAK,GAAG,QAAQ,SAAS,EAAE,OAAO,GAAG,GAAG,CAAC;AAAA,EAChD;AACA,QAAM,WAAW,QAAQ,IAAI;AAC7B,SAAO,EAAE,KAAK,OAAO,UAAU,MAAS,GAAG,OAAO,KAAK,QAAQ,GAAG,UAAU,KAAK,EAAE;AACrF;AAWA,SAAS,OACP,KACA,MACA,OACoB;AACpB,MAAI,IAAI,SAAS,KAAK,QAAQ,iBAAkB,QAAO;AACvD,QAAM,WAAW,KAAK,MAAO,kBAAkB,KAAK,KAAM,KAAK,IAAI,mBAAmB;AACtF,QAAM,MAAM,OAAO,QAAQ,GAAG,GAAG,KAAK,KAAK,iBAAiB,QAAQ;AACpE,MAAI,CAAC,IAAK,QAAO;AAGjB,QAAM,OAAO,MAAM,YAAY,SAAY,CAAC,IAAI,IAAI,OAAO,CAAC,WAAW,OAAO,YAAY,MAAM,OAAO;AACvG,QAAM,QAAQ,KAAK,SAAS,IAAK,OAAO,QAAQ,IAAI,GAAG,GAAG,KAAK,MAAO;AAGtE,QAAM,QAAQ,MAAM,UAAU,UAAa,MAAM,QAAQ,IAAI,MAAM,QAAQ;AAC3E,QAAM,MAAM,MAAM,QAAQ,UAAa,MAAM,MAAM,IAAI,MAAM,MAAM;AACnE,MAAI,MAAM;AACV,MAAI,UAAU,UAAa,QAAQ,QAAW;AAC5C,UAAM,OAAO,IAAI;AAAA,MACf,CAAC,YACE,UAAU,UAAa,OAAO,UAAU,UAAa,OAAO,SAAS,WACrE,QAAQ,UAAa,OAAO,QAAQ,UAAa,CAAC,OAAO,cAAc,OAAO,OAAO;AAAA,IAC1F;AACA,QAAI,KAAK,SAAS,IAAI,UAAU,KAAK,UAAU,yBAAyB;AACtE,YAAM,OAAO,QAAQ,IAAI,GAAG,OAAO,iBAAiB,KAAK;AAAA,IAC3D;AAAA,EACF;AACA,QAAM,QAAQ,IAAI,SAAS,IAAI,IAAI,OAAO,CAAC,SAAS,WAAW,KAAK,IAAI,SAAS,OAAO,EAAE,GAAG,CAAC,IAAI,OAAO;AACzG,SAAO,EAAE,KAAK,OAAO,MAAM;AAC7B;AAeO,IAAM,qBAAkC,EAAE,OAAO,GAAG,QAAQ,GAAG,KAAK,WAAW,UAAU,eAAe;AAM/G,SAAS,iBAAiB,QAA0D,OAAe,GAAmB;AACpH,MAAI,aAAa;AACjB,MAAI;AACJ,aAAW,SAAS,QAAQ;AAC1B,UAAM,MAAM,aAAa,MAAM,SAAS,KAAK;AAC7C,kBAAc,MAAM;AACpB,QAAI,MAAM,GAAG;AACX,UAAI,CAAC,SAAU,QAAO,MAAM;AAC5B,aAAO,SAAS,SAAU,IAAI,SAAS,OAAO,KAAK,SAAS,OAAQ,MAAM,QAAQ,SAAS;AAAA,IAC7F;AACA,eAAW,EAAE,OAAO,MAAM,OAAO,GAAG;AAAA,EACtC;AACA,SAAO,OAAO,OAAO,SAAS,CAAC,EAAE;AACnC;AAeO,SAAS,gBAAgB,WAA8B,SAA0C;AACtG,QAAM,UAAU,UACb,IAAI,CAAC,OAAO,WAAW,EAAE,OAAO,QAAQ,UAAU,KAAK,KAAK,EAAE,EAAE,EAChE,OAAO,CAAC,UAAU,OAAO,SAAS,MAAM,KAAK,KAAK,OAAO,SAAS,MAAM,MAAM,KAAK,MAAM,SAAS,CAAC,EACnG,MAAM,CAAC,yBAAyB;AACnC,MAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,MAAI,QAAQ;AACZ,MAAI,UAAU;AACd,MAAI,MAAM;AACV,aAAW,SAAS,SAAS;AAC3B,aAAS,MAAM;AACf,eAAW,MAAM,SAAS,MAAM;AAChC,WAAO,MAAM,SAAS,MAAM;AAAA,EAC9B;AACA,QAAM,IAAK,QAAQ,QAAS;AAC5B,QAAM,OAAO,MAAM;AACnB,MAAI,YAAY;AAChB,aAAW,SAAS,QAAS,cAAa,MAAM,UAAU,MAAM,QAAQ,SAAS,MAAM,QAAQ;AAC/F,QAAM,KAAK,IAAI,IAAI,KAAK,KAAM,YAAY,SAAU,KAAK,IAAI,GAAG,IAAI;AACpE,QAAM,SAAS,CAAC,GAAG,OAAO,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;AAC5D,QAAM,MAAM,iBAAiB,QAAQ,OAAO,IAAI,IAAI,iBAAiB,QAAQ,OAAO,IAAI;AACxF,QAAM,UAAU,CAAC,IAAI,MAAM,KAAK,EAAE,OAAO,CAACC,YAAWA,UAAS,CAAC;AAC/D,QAAM,SAAS,QAAQ,SAAS,IAAI,KAAK,IAAI,GAAG,OAAO,IAAI;AAC3D,QAAM,QAAQ,KAAK,IAAI,KAAK,MAAM,SAAS,KAAK,IAAI,GAAG,IAAI,CAAC;AAC5D,QAAM,SAAS,KAAK,IAAI;AACxB,QAAM,OAAO,KAAK,OAAO,YAAY,YAAY,SAAS,IAAI;AAC9D,QAAM,QAAQ,IAAI,aAAa,IAAI;AACnC,WAAS,QAAQ,GAAG,QAAQ,MAAM,SAAS,GAAG;AAC5C,UAAM,IAAI,WAAW,QAAQ;AAC7B,QAAI,SAAS;AACb,eAAW,SAAS,QAAS,WAAU,MAAM,SAAS,WAAW,IAAI,MAAM,SAAS,KAAK;AACzF,UAAM,KAAK,IAAI,UAAU,SAAS,UAAU,IAAI,UAAU,UAAU,CAAC;AAAA,EACvE;AACA,SAAO;AAAA,IACL,OAAO,QAAQ;AAAA,IACf;AAAA,IACA,IAAI,GAAG;AACL,UAAI,OAAO,MAAM,CAAC,EAAG,QAAO;AAC5B,YAAM,YAAY,KAAK,IAAI,WAAW,KAAK,IAAI,UAAU,CAAC,CAAC,IAAI,YAAY;AAC3E,YAAM,QAAQ,KAAK,IAAI,OAAO,GAAG,KAAK,MAAM,QAAQ,CAAC;AACrD,aAAO,MAAM,KAAK,KAAK,WAAW,UAAU,MAAM,QAAQ,CAAC,IAAI,MAAM,KAAK;AAAA,IAC5E;AAAA,IACA,SAAS,GAAG;AACV,UAAI,EAAE,IAAI,MAAM,CAAC,GAAI,QAAO;AAC5B,UAAI,EAAE,IAAI,MAAM,OAAO,CAAC,GAAI,QAAO;AAEnC,UAAI,MAAM;AACV,UAAI,OAAO,OAAO;AAClB,aAAO,OAAO,MAAM,GAAG;AACrB,cAAM,MAAO,MAAM,QAAS;AAC5B,YAAI,MAAM,GAAG,IAAI,EAAG,OAAM;AAAA,YACrB,QAAO;AAAA,MACd;AACA,YAAM,OAAO,MAAM,IAAI,IAAI,MAAM,GAAG;AACpC,aAAO,YAAY,OAAO,OAAO,KAAK,IAAI,MAAM,GAAG,KAAK,OAAO,MAAM;AAAA,IACvE;AAAA,EACF;AACF;AAGA,SAAS,WAAW,KAAmB,aAA0B,eAAuB,GAAmB;AACzG,QAAM,SAAS,gBAAgB,IAAI,YAAY,KAAK,KAAK,IAAI,aAAa,IAAI,IAAI,MAAM,IAAI,KAAK,IAAI;AACrG,SAAO,KAAK,IAAI,IAAI,KAAK,IAAI,QAAQ,YAAY,SAAS,SAAS,KAAK,IAAI,OAAO,CAAC;AACtF;AAwCA,IAAM,gBAA6B,oBAAI,IAAI;AAOpC,SAAS,eAAe,UAAuB,eAAe,MAAc,KAAK,IAAI,GAAc;AACxG,QAAM,QAAQ,oBAAI,IAAkB;AACpC,QAAM,UAAU,CAAC,KAAa,WAAqC;AACjE,UAAM,KAAK,GAAG,GAAG,KAAS,UAAU,EAAE;AACtC,QAAI,OAAO,MAAM,IAAI,EAAE;AACvB,QAAI,CAAC,MAAM;AACT,aAAO,UAAU,SAAS,KAAK,QAAQ,GAAG;AAC1C,YAAM,IAAI,IAAI,IAAI;AAAA,IACpB;AACA,WAAO;AAAA,EACT;AACA,QAAM,WAAW,oBAAI,IAAsD;AAC3E,QAAM,YAAsB,CAAC;AAC7B,QAAM,kBAA4B,CAAC;AACnC,aAAW,CAAC,KAAK,OAAO,KAAK,SAAS;AACpC,UAAM,SAAS,cAAc,OAAO;AACpC,QAAI,OAAO,SAAS,EAAG;AACvB,UAAM,OAAiD,CAAC;AACxD,eAAW,QAAQ,OAAO,MAAM,CAAC,oBAAoB,GAAG;AACtD,YAAM,QAAQ,EAAE,OAAO,KAAK,OAAO,QAAQ,KAAK,QAAQ,SAAS,KAAK,QAAQ;AAC9E,YAAM,MAAM;AAAA,QACV,OAAO,OAAO,CAAC,WAAW,WAAW,IAAI;AAAA,QACzC;AAAA,QACA;AAAA,MACF;AACA,YAAM,SAAS,OAAO,KAAK,QAAQ,KAAK,KAAK,MAAM,GAAG,KAAK;AAC3D,UAAI,CAAC,OAAQ;AACb,WAAK,KAAK,EAAE,IAAI,KAAK,IAAI,KAAK,OAAO,IAAI,CAAC;AAC1C,gBAAU,MAAM,KAAK,IAAI,KAAK,IAAI,GAAG,KAAK,EAAE,CAAC,IAAI,OAAO,IAAI,MAAM,OAAO,IAAI,KAAK;AAClF,sBAAgB,KAAK,cAAc,KAAK,KAAK,EAAE,CAAC;AAAA,IAClD;AACA,QAAI,KAAK,SAAS,EAAG,UAAS,IAAI,KAAK,IAAI;AAAA,EAC7C;AACA,QAAM,cAAc,gBAAgB,WAAW,eAAe;AAE9D,QAAM,WAAW,CAAC,KAAa,UAAuD;AACpF,UAAM,KAAK,MAAM,OAAO;AACxB,UAAM,MAAM,QAAQ,QAAQ,IAAI,GAAG,KAAK,CAAC,GAAG,OAAO,EAAE;AACrD,UAAM,SAAS,OAAO,KAAK,QAAQ,KAAK,MAAM,MAAM,GAAG,KAAK;AAC5D,QAAI,CAAC,OAAQ,QAAO;AACpB,UAAM,UAAU,KAAK,IAAI,GAAG,OAAO,SAAS,MAAM,SAAS,IAAI,MAAM,YAAY,CAAC;AAClF,QAAI,UAAU,OAAO,OAAO;AAC1B,aAAO,EAAE,SAAS,WAAW,OAAO,OAAO,aAAa,GAAG,GAAG,GAAG,MAAM,GAAG,MAAM,KAAK;AAAA,IACvF;AACA,UAAM,UAAU,KAAK,IAAI,SAAS,WAAW,OAAO,KAAK,aAAa,SAAS,GAAG,CAAC;AACnF,UAAM,QAAQ,KAAK,IAAI,SAAS,KAAK,IAAI,SAAS,WAAW,OAAO,KAAK,aAAa,SAAS,IAAI,CAAC,CAAC;AACrG,UAAM,SAAS,KAAK,IAAI,SAAS,WAAW,OAAO,KAAK,aAAa,SAAS,IAAI,CAAC;AACnF,WAAO;AAAA,MACL;AAAA,MACA,MAAM,UAAU,IAAI,UAAU,UAAU;AAAA,MACxC,MAAM;AAAA,MACN,MAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACA,MAAM,SAAS,IAAI,UAAU,SAAS;AAAA,QACtC,OAAO,QAAQ,IAAI,KAAK,IAAI,GAAG,UAAU,KAAK,IAAI;AAAA,MACpD;AAAA,IACF;AAAA,EACF;AAEA,QAAM,SAAS,MAA8B;AAC3C,UAAM,OAA4B,CAAC;AACnC,eAAW,CAAC,KAAK,OAAO,KAAK,SAAS;AACpC,YAAM,SAAS,cAAc,OAAO;AACpC,YAAM,SAAS,QAAQ,OAAO,CAAC,WAAW,OAAO,MAAM,EAAE;AACzD,UAAI,OAAO,WAAW,KAAK,WAAW,EAAG;AACzC,YAAM,YAAY,OAAO,IAAI,CAAC,WAAW,OAAO,EAAE,EAAE,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AACxE,YAAM,YAAY,UAAU,SAAS,IAAI,UAAU,KAAK,OAAO,UAAU,SAAS,KAAK,CAAC,CAAC,IAAI;AAC7F,YAAM,OAAO,SAAS,IAAI,GAAG,KAAK,CAAC;AACnC,YAAM,SAAmB,CAAC;AAC1B,UAAI,OAAO;AACX,UAAI,SAAS;AACb,iBAAW,OAAO,MAAM;AACtB,cAAM,WAAW,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI,IAAI,QAAQ,YAAY,SAAS,GAAG,CAAC;AAChF,cAAM,MAAM,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI,IAAI,QAAQ,YAAY,SAAS,IAAI,CAAC;AAC5E,cAAM,OAAO,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI,IAAI,QAAQ,YAAY,SAAS,IAAI,CAAC;AAC7E,eAAO,KAAK,KAAK,IAAI,KAAK,IAAI,KAAK,IAAI,GAAG,IAAI,EAAE,IAAI,QAAQ,CAAC,CAAC;AAC9D,YAAI,IAAI,KAAK,SAAU,SAAQ;AAC/B,YAAI,IAAI,MAAM,OAAO,IAAI,MAAM,KAAM,WAAU;AAAA,MACjD;AACA,aAAO,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AAC3B,WAAK,KAAK;AAAA,QACR;AAAA,QACA,OAAO,OAAO;AAAA,QACd;AAAA,QACA;AAAA,QACA,UAAU,KAAK;AAAA,QACf,GAAI,KAAK,SAAS,IACd;AAAA,UACE,YAAY,KAAK,IAAI,OAAO,KAAK,OAAO,OAAO,SAAS,KAAK,CAAC,CAAC,CAAC;AAAA,UAChE,WAAW,OAAO,KAAK;AAAA,UACvB,aAAa,SAAS,KAAK;AAAA,QAC7B,IACA,CAAC;AAAA,MACP,CAAC;AAAA,IACH;AACA,SAAK,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,IAAI,cAAc,EAAE,GAAG,CAAC;AACnE,WAAO,EAAE,MAAM,WAAW,UAAU,QAAQ,mBAAmB,YAAY,OAAO;AAAA,EACpF;AAEA,SAAO,EAAE,UAAU,aAAa,OAAO;AACzC;;;ACxmBA,SAAS,aAAAC,YAAW,gBAAAC,eAAc,cAAAC,aAAY,YAAAC,WAAU,iBAAAC,sBAAqB;AAC7E,SAAS,WAAAC,gBAAe;AACxB,SAAS,QAAAC,aAAY;AA+Cd,IAAM,yBAAyB,IAAI,KAAK,KAAK;AAG7C,IAAM,2BAA2B,IAAI,KAAK,KAAK,KAAK;AAEpD,IAAM,qBAAqB;AAGlC,IAAM,OAAO;AACb,IAAM,SAAS;AAEf,IAAM,OAAO;AACb,IAAM,kBAAkB;AAExB,IAAIC,gBAA8B;AAG3B,SAAS,wBAAgC;AAC9C,MAAIC,cAAc,QAAOA;AACzB,QAAM,YAAY,QAAQ,IAAI,kBAAkBC,MAAKC,SAAQ,GAAG,UAAU,OAAO;AACjF,SAAOD,MAAK,WAAW,+BAA+B,oBAAoB;AAC5E;AAEA,SAAS,OAAO,OAAiC;AAC/C,SAAO,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK;AAC3D;AAEA,SAAS,WAAW,OAAoC;AACtD,MAAI,CAAC,SAAS,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,EAAG,QAAO;AACxE,QAAM,QAAQ;AACd,MAAI,CAAC,OAAO,MAAM,UAAU,EAAG,QAAO;AACtC,MAAI,OAAO,MAAM,WAAW,YAAY,CAAC,OAAO,KAAK,MAAM,MAAM,EAAG,QAAO;AAC3E,MAAI,MAAM,gBAAgB,QAAQ,CAAC,OAAO,MAAM,WAAW,EAAG,QAAO;AACrE,MAAI,MAAM,aAAa,UAAa,CAAC,OAAO,MAAM,QAAQ,EAAG,QAAO;AACpE,MACE,MAAM,eAAe,WACpB,OAAO,MAAM,eAAe,YAAY,MAAM,WAAW,SAAS,kBACnE;AACA,WAAO;AAAA,EACT;AACA,QAAM,QAAQ,MAAM,QAAQ,MAAM,KAAK,IACnC,CAAC,GAAG,IAAI,IAAI,MAAM,MAAM,OAAO,CAAC,MAAmB,OAAO,CAAC,KAAK,IAAI,KAAK,KAAK,GAAG,CAAC,CAAC,EAAE;AAAA,IACnF,CAAC,GAAG,MAAM,IAAI;AAAA,EAChB,IACA,CAAC;AACL,SAAO;AAAA,IACL,aAAa,MAAM;AAAA,IACnB,GAAI,MAAM,aAAa,SAAY,CAAC,IAAI,EAAE,UAAU,MAAM,SAAmB;AAAA,IAC7E,GAAI,MAAM,eAAe,SAAY,CAAC,IAAI,EAAE,YAAY,MAAM,WAAqB;AAAA,IACnF,QAAQ,MAAM;AAAA,IACd,YAAY,MAAM;AAAA,IAClB;AAAA,EACF;AACF;AAQA,IAAM,aAAa,OAAkB,EAAE,OAAO,oBAAI,IAAI,GAAG,OAAO,oBAAI,IAAI,EAAE;AAE1E,SAAS,UAAU,QAAgD;AACjE,QAAM,MAAqB,oBAAI,IAAI;AACnC,QAAM,QAAQ,OAAO;AACrB,MAAI,CAAC,SAAS,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,EAAG,QAAO;AACxE,aAAW,CAAC,SAAS,GAAG,KAAK,OAAO,QAAQ,KAAgC,GAAG;AAC7E,QAAI,IAAI,QAAQ,mBAAoB;AACpC,QAAI,CAAC,KAAK,KAAK,OAAO,KAAK,CAAC,OAAO,OAAO,QAAQ,YAAY,MAAM,QAAQ,GAAG,EAAG;AAClF,UAAM,SAAS;AACf,QAAI,CAAC,OAAO,OAAO,EAAE,EAAG;AACxB,UAAM,OAAO,OAAO,OAAO,SAAS,YAAY,KAAK,KAAK,OAAO,IAAI,IAAI,OAAO,OAAO;AACvF,QAAI,IAAI,SAAS,EAAE,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC,GAAI,IAAI,OAAO,GAAG,CAAC;AAAA,EAC/D;AACA,SAAO;AACT;AAEA,SAAS,WAAW,MAAc,KAAwB;AACxD,QAAM,MAAM,WAAW;AACvB,MAAI;AACF,UAAM,SAAS,KAAK,MAAME,cAAa,MAAM,MAAM,CAAC;AACpD,QAAI,CAAC,UAAU,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,EAAG,QAAO;AAC3E,QAAI,QAAQ,UAAU,MAAiC;AACvD,UAAM,WAAY,OAAmC;AACrD,QAAI,CAAC,YAAY,OAAO,aAAa,YAAY,MAAM,QAAQ,QAAQ,EAAG,QAAO;AACjF,UAAM,SAAS,MAAM;AACrB,eAAW,CAAC,SAAS,OAAO,KAAK,OAAO,QAAQ,QAAmC,GAAG;AACpF,UAAI,CAAC,KAAK,KAAK,OAAO,KAAK,CAAC,WAAW,OAAO,YAAY,SAAU;AACpE,YAAM,OAAO,oBAAI,IAAyB;AAC1C,iBAAW,CAAC,QAAQ,GAAG,KAAK,OAAO,QAAQ,OAAkC,GAAG;AAC9E,YAAI,CAAC,KAAK,KAAK,MAAM,EAAG;AACxB,cAAM,QAAQ,WAAW,GAAG;AAC5B,YAAI,SAAS,MAAM,cAAc,OAAQ,MAAK,IAAI,QAAQ,KAAK;AAAA,MACjE;AACA,UAAI,KAAK,OAAO,EAAG,KAAI,MAAM,IAAI,SAAS,IAAI;AAAA,IAChD;AAAA,EACF,QAAQ;AAAA,EAGR;AACA,SAAO;AACT;AA+DO,SAAS,iBAAiB,OAAoB,MAAc,KAAK,IAAI,GAAY;AACtF,MAAI,MAAM,aAAa,OAAW,QAAO,MAAM,WAAW;AAC1D,SAAO,MAAM,MAAM,aAAa;AAClC;AA0BO,SAAS,aAAa,aAA6B;AACxD,SAAO,KAAK,MAAM,cAAc,GAAI,IAAI;AAC1C;AA+LO,SAAS,yBAAyB,OAAqB,uBAAuB;AACnF,MAAI,WAAW;AACf,MAAI,QAAQ,WAAW;AACvB,QAAM,UAAU,MAAM;AACpB,UAAM,OAAO,KAAK;AAClB,QAAI,OAAO;AACX,QAAI;AACF,YAAM,QAAQC,UAAS,IAAI;AAC3B,aAAO,GAAG,MAAM,OAAO,IAAI,MAAM,IAAI,IAAI,MAAM,GAAG;AAAA,IACpD,QAAQ;AACN,aAAO;AAAA,IACT;AACA,QAAI,SAAS,UAAU;AACrB,iBAAW;AACX,cAAQ,SAAS,YAAY,WAAW,IAAI,WAAW,MAAM,KAAK,IAAI,CAAC;AAAA,IACzE;AAAA,EACF;AACA,SAAO;AAAA,IACL,OAAqB;AACnB,cAAQ;AACR,aAAO,MAAM;AAAA,IACf;AAAA;AAAA,IAEA,QAAuB;AACrB,cAAQ;AACR,aAAO,MAAM;AAAA,IACf;AAAA,EACF;AACF;;;AC5dO,IAAM,kBAAkB;AAGxB,IAAM,kBAAkB;AAG/B,IAAM,aAAa;AACnB,IAAM,YAAY;AAElB,IAAM,gBAAwC;AAAA,EAC5C,WAAW;AAAA,EACX,WAAW;AAAA,EACX,gBAAgB;AAAA,EAChB,kBAAkB;AAAA,EAClB,4BAA4B;AAAA,EAC5B,SAAS;AACX;AAGO,SAAS,gBAAgB,QAAwB;AACtD,MAAI,WAAW,UAAW,QAAO;AACjC,SAAO,cAAc,MAAM,KAAK,OAAO,QAAQ,MAAM,GAAG;AAC1D;AAEA,IAAM,MAAM,CAAC,UAAkB,OAAO,KAAK,EAAE,SAAS,GAAG,GAAG;AAE5D,SAAS,QAAQ,GAAS,GAAkB;AAC1C,SAAO,EAAE,YAAY,MAAM,EAAE,YAAY,KAAK,EAAE,SAAS,MAAM,EAAE,SAAS,KAAK,EAAE,QAAQ,MAAM,EAAE,QAAQ;AAC3G;AAQO,SAAS,iBAAiB,UAAkB,KAAa,OAAO,MAAc;AACnF,QAAM,OAAO,IAAI,KAAK,QAAQ;AAC9B,QAAM,QAAQ,GAAG,IAAI,KAAK,SAAS,CAAC,CAAC,IAAI,IAAI,KAAK,WAAW,CAAC,CAAC;AAC/D,MAAI,QAAQ,MAAM,IAAI,KAAK,GAAG,CAAC,EAAG,QAAO;AACzC,QAAM,OAAO,GAAG,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,IAAI,IAAI,KAAK,QAAQ,CAAC,CAAC,IAAI,KAAK;AACxE,SAAO,OAAO,GAAG,KAAK,YAAY,CAAC,IAAI,IAAI,KAAK;AAClD;AAQO,SAAS,eAAe,OAAoB,KAAuB;AACxE,MAAI,MAAM,aAAa,QAAW;AAChC,UAAM,OAAO,iBAAiB,MAAM,UAAU,GAAG;AACjD,QAAI,QAAQ,IAAI,KAAK,MAAM,QAAQ,GAAG,IAAI,KAAK,GAAG,CAAC,EAAG,QAAO,CAAC,GAAG,UAAU,GAAG,IAAI,EAAE;AACpF,WAAO,CAAC,GAAG,UAAU,GAAG,IAAI,IAAI,MAAM,iBAAiB,MAAM,UAAU,KAAK,KAAK,CAAC;AAAA,EACpF;AACA,MAAI,MAAM,WAAY,QAAO,CAAC,GAAG,UAAU,GAAG,MAAM,UAAU,IAAI,MAAM,UAAU;AAClF,SAAO,CAAC;AACV;AAEA,SAAS,KAAK,OAA4B;AACxC,MAAI,MAAM,WAAW,WAAY,QAAO;AACxC,MAAI,MAAM,gBAAgB,KAAM,QAAO;AACvC,SAAO,aAAa,MAAM,WAAW;AACvC;AAGO,SAAS,eACd,SACA,KACmC;AACnC,MAAI;AACJ,aAAW,CAAC,QAAQ,KAAK,KAAK,SAAS;AACrC,QAAI,CAAC,iBAAiB,OAAO,GAAG,EAAG;AACnC,QAAI,CAAC,QAAQ,KAAK,KAAK,IAAI,KAAK,KAAK,CAAC,CAAC,EAAG,QAAO,CAAC,QAAQ,KAAK;AAAA,EACjE;AACA,SAAO;AACT;AAcA,IAAMC,SAAQ,CAAC,UAAkB,MAAM,KAAK,KAAK,EAAE;AACnD,IAAM,gBAAgB,CAAC,aAAwB,SAAS,OAAO,CAAC,KAAK,YAAY,MAAMA,OAAM,QAAQ,IAAI,GAAG,CAAC;AAE7G,SAAS,UAAU,OAAmD;AACpE,SAAO,UAAU,YAAY,UAAU,UAAU,YAAY,WAAW;AAC1E;AAOA,SAAS,YAAY,UAAqB,QAA2B;AACnE,QAAM,MAAiB,CAAC;AACxB,MAAI,OAAO;AACX,aAAW,WAAW,UAAU;AAC9B,UAAM,SAASA,OAAM,QAAQ,IAAI;AACjC,QAAI,OAAO,UAAU,QAAQ;AAC3B,UAAI,KAAK,OAAO;AAChB,cAAQ;AACR;AAAA,IACF;AACA,QAAI,QAAQ,SAAS,WAAW;AAC9B,YAAM,MAAM,SAAS,QAAQ,MAAM,SAAS,IAAI;AAChD,UAAI,IAAK,KAAI,KAAK,EAAE,GAAG,SAAS,MAAM,IAAI,CAAC;AAAA,IAC7C;AACA;AAAA,EACF;AACA,SAAO;AACT;AAMA,SAAS,aAAa,OAAkB,UAAkB,UAA8B;AACtF,MAAI,MAAM,UAAU,QAAQ;AAC1B,UAAMC,OAAiB,CAAC;AACxB,QAAI,YAAY,MAAM,KAAM,CAAAA,KAAI,KAAK,EAAE,MAAM,WAAW,MAAM,QAAQ,GAAG,EAAE,MAAM,MAAM,MAAM,MAAM,OAAO,CAAC;AAC3G,IAAAA,KAAI,KAAK,EAAE,MAAM,WAAW,MAAM,QAAQ,GAAG,EAAE,MAAM,iBAAiB,MAAM,QAAQ,CAAC;AACrF,WAAOA;AAAA,EACT;AACA,QAAM,MAAiB;AAAA,IACrB,EAAE,MAAM,WAAW,MAAM,QAAQ;AAAA,IACjC,EAAE,MAAM,MAAM,OAAO,MAAM,UAAU,MAAM,KAAK,EAAE;AAAA,EACpD;AACA,QAAM,QAAQ,MAAM,UAAU,KAAK,IAAI,UAAU,MAAM,UAAU,SAAS,CAAC,CAAC;AAC5E,MAAI,OAAO;AACT,QAAI,KAAK,EAAE,MAAM,WAAW,MAAM,QAAQ,CAAC;AAC3C,QAAI,MAAM,WAAW,UAAU,GAAG;AAChC,UAAI,KAAK,EAAE,MAAM,YAAY,MAAM,QAAQ,GAAG,EAAE,MAAM,MAAM,MAAM,WAAW,MAAM,GAAG,MAAM,QAAQ,CAAC;AAAA,IACvG,OAAO;AACL,UAAI,KAAK,EAAE,MAAM,OAAO,MAAM,QAAQ,CAAC;AAAA,IACzC;AAAA,EACF;AACA,SAAO;AACT;AASO,SAAS,kBACd,OACA,KACA,QAAgB,mBAChB,QAAuB,oBAAI,IAAI,GACX;AACpB,QAAM,SAAsB,CAAC;AAC7B,QAAM,QAAQ,CAAC,GAAG,oBAAI,IAAI,CAAC,GAAG,MAAM,KAAK,GAAG,GAAG,MAAM,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK;AACpE,aAAW,WAAW,OAAO;AAC3B,UAAM,OAAO,SAAS,SAAS,KAAK,IAAI,GAAG,KAAK,MAAM,QAAQ,CAAC,CAAC,CAAC;AACjE,UAAM,UAAU,MAAM,IAAI,OAAO;AACjC,UAAM,WAAW,UAAU,eAAe,SAAS,GAAG,IAAI;AAC1D,QAAI,CAAC,UAAU;AACb,UAAI,CAAC,MAAM,IAAI,OAAO,EAAG;AACzB,aAAO,KAAK;AAAA,QACV;AAAA,QACA,QAAQ;AAAA,QACR,OAAO;AAAA,QACP;AAAA,QACA,OAAO;AAAA,QACP,MAAM,MAAM,IAAI,OAAO,GAAG;AAAA,QAC1B,WAAW,CAAC;AAAA,MACd,CAAC;AACD;AAAA,IACF;AACA,UAAM,CAAC,QAAQ,KAAK,IAAI;AACxB,UAAM,QACJ,MAAM,WAAW,aACb,YACA,MAAM,gBAAgB,QAAQ,aAAa,MAAM,WAAW,KAAK,kBAC/D,YACA;AACR,UAAM,QAAQ,gBAAgB,MAAM;AACpC,UAAM,QACJ,UAAU,YACN,YACA,MAAM,gBAAgB,OACpB,GAAG,QAAQ,GAAG,KAAK,MAAM,EAAE,OAC3B,GAAG,QAAQ,GAAG,KAAK,MAAM,EAAE,GAAG,KAAK,MAAM,aAAa,MAAM,WAAW,CAAC,CAAC;AACjF,WAAO,KAAK,EAAE,SAAS,QAAQ,OAAO,MAAM,OAAO,WAAW,eAAe,OAAO,GAAG,EAAE,CAAC;AAAA,EAC5F;AACA,QAAM,SAAS,CAAC,UAAqB,KAAK,IAAI,GAAG,QAAQD,OAAM,MAAM,IAAI,CAAC;AAG1E,QAAM,QAAQ,OAAO,OAAO,CAAC,UAAU,MAAM,UAAU,MAAM;AAC7D,QAAM,OAAO,KAAK,IAAI,GAAG,GAAG,MAAM,IAAI,CAAC,UAAU,MAAM,UAAU,SAAS,CAAC,CAAC;AAC5E,MAAI,WAAW;AACf,SACE,WAAW,QACX,MAAM,KAAK,CAAC,UAAU,cAAc,aAAa,OAAO,UAAU,IAAI,CAAC,IAAI,OAAO,KAAK,CAAC,GACxF;AACA,gBAAY;AAAA,EACd;AACA,SAAO,OAAO,IAAI,CAAC,UAAU;AAC3B,UAAM,OAAO,aAAa,OAAO,UAAU,IAAI;AAC/C,UAAM,OAAO,cAAc,IAAI,KAAK,OAAO,KAAK,IAAI,OAAO,aAAa,OAAO,UAAU,KAAK;AAC9F,WAAO;AAAA,MACL,SAAS,MAAM;AAAA,MACf,QAAQ,MAAM;AAAA,MACd,OAAO,MAAM;AAAA,MACb,UAAU,CAAC,EAAE,MAAM,MAAM,MAAM,MAAM,QAAQ,GAAG,GAAG,YAAY,MAAM,OAAO,KAAK,CAAC,CAAC;AAAA,IACrF;AAAA,EACF,CAAC;AACH;;;AR1LO,IAAM,0BAA0B;AAGvC,IAAM,gBAAgB;AACtB,IAAM,sBAAsB;AAE5B,IAAM,sBAAsB;AAE5B,IAAM,mBAAmB;AAEzB,IAAM,aAAa,eAAe,SAAS;AAG3C,IAAM,aAAa,oBAAI,IAAI,CAAC,QAAQ,UAAU,CAAC;AAmD/C,IAAM,aAA2D;AAAA,EAC/D,SAAS;AAAA,EACT,MAAM;AAAA,EACN,IAAI;AAAA,EACJ,OAAO;AAAA,EACP,OAAO;AAAA,EACP,aAAa;AAAA,EACb,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,gBAAgB;AAAA,EAChB,YAAY;AAAA,EACZ,cAAc;AAAA,EACd,YAAY;AAAA;AAAA,EAEZ,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,UAAU;AAAA,EACV,UAAU;AAAA,EACV,iBAAiB;AAAA,EACjB,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AACT;AAYA,SAAS,QAAW,IAAa,UAAgB;AAC/C,MAAI;AACF,WAAO,GAAG;AAAA,EACZ,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAOA,SAAS,IAAI,GAAU,GAAU,QAAuB;AACtD,SAAO;AAAA,IACL,MACE,KAAK;AAAA,MACH,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK;AAAA,MACpB,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK;AAAA,MACpB,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK;AAAA,MACpB,EAAE;AAAA,IACJ;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,UAAU,QAAuB,MAA8B;AACtE,SAAO,OAAO,WAAW,IAAI,KAAK,OAAO;AAC3C;AAEA,IAAM,eAA+B,EAAE,UAAU,CAAC,GAAG,OAAO,CAAC,EAAE;AAS/D,IAAM,aAA0B,EAAE,MAAM,CAAC,GAAG,OAAO,oBAAI,IAAI,EAAE;AAM7D,SAAS,cAAc,MAAmB;AACxC,QAAM,CAAC,OAAO,QAAQ,IAAI,aAAa,CAAC;AACxC,QAAM,CAAC,KAAK,MAAM,IAAI,aAAa,KAAK,IAAI,CAAC;AAC7C,QAAM,UAAU,wBAAwB;AACxC,MAAI,YAAsC,QAAQ,MAAM,QAAQ,KAAK,GAAG,oBAAI,IAAI,CAAC;AAIjF,QAAM,UAAU,wBAAwB;AACxC,MAAI,aAA0B,QAAQ,MAAM,QAAQ,KAAK,GAAG,oBAAI,IAAI,CAAC;AAIrE,MAAI,YAAmC,QAAQ,MAAM,eAAe,UAAU,GAAG,MAAS;AAI1F,QAAM,eAAe,yBAAyB;AAC9C,MAAI,cAAyC,QAAQ,MAAM,aAAa,KAAK,GAAG,oBAAI,IAAI,CAAC;AACzF,QAAM,CAAC,eAAe,gBAAgB,IAAI,aAAa,CAAC;AAIxD,QAAM,QAAQ,kBAAkB;AAGhC,QAAM,QAAQ,iBAAiB;AAM/B,QAAM,cAAc,yBAAyB;AAC7C,QAAM,CAAC,OAAO,QAAQ,IAAI,aAA2B,QAAQ,MAAM,YAAY,KAAK,GAAG,oBAAI,IAAI,CAAC,CAAC;AACjG,QAAM,CAAC,OAAO,QAAQ,IAAI,aAA4B,QAAQ,MAAM,YAAY,MAAM,GAAG,oBAAI,IAAI,CAAC,CAAC;AACnG,QAAM,CAAC,UAAU,WAAW,IAAI,aAAa,KAAK,IAAI,CAAC;AAKvD,QAAM,kBAAkB,oBAAI,IAAY;AACxC,QAAM,kBAAkB,oBAAI,IAAY;AAIxC,QAAM,kBAAkB,oBAAI,IAAY;AACxC,MAAI;AACJ,MAAI;AACJ,MAAI,gBAAgB;AACpB,QAAM,cAAc,MAAM;AACxB,QAAI,gBAAgB,OAAO,GAAG;AAC5B,YAAM,OAAO,QAAQ,MAAM,QAAQ,KAAK,GAAG,SAAS;AACpD,YAAM,cAAc,QAAQ,MAAM,QAAQ,KAAK,GAAG,UAAU;AAC5D,UAAI,SAAS,aAAa,gBAAgB,YAAY;AACpD,YAAI,gBAAgB,WAAY,aAAY,QAAQ,MAAM,eAAe,WAAW,GAAG,SAAS;AAChG,oBAAY;AACZ,qBAAa;AACb,yBAAiB,CAAC,UAAU,QAAQ,CAAC;AAAA,MACvC;AACA,oBAAc,QAAQ,MAAM,aAAa,KAAK,GAAG,WAAW;AAC5D,aAAO,KAAK,IAAI,CAAC;AAAA,IACnB;AAIA,aAAS,QAAQ,MAAM,YAAY,KAAK,GAAG,MAAM,CAAC,CAAC;AACnD,aAAS,QAAQ,MAAM,YAAY,MAAM,GAAG,MAAM,CAAC,CAAC;AACpD,UAAM,SAAS,KAAK,MAAM,KAAK,IAAI,IAAI,GAAM,IAAI;AACjD,QAAI,WAAW,SAAS,EAAG,aAAY,MAAM;AAAA,EAC/C;AACA,QAAM,aAAa,MAAM;AACvB,QAAI,gBAAgB,OAAO,KAAK,CAAC,SAAS;AACxC,gBAAU,YAAY,MAAM,SAAS,CAAC,WAAW,QAAQ,KAAK,UAAU,GAAG,mBAAmB;AAAA,IAChG,WAAW,gBAAgB,SAAS,KAAK,SAAS;AAChD,oBAAc,OAAO;AACrB,gBAAU;AAAA,IACZ;AACA,UAAM,SAAS,gBAAgB,OAAO,KAAK,gBAAgB,OAAO;AAGlE,UAAM,SAAS,gBAAgB,OAAO,IAAI,mBAAmB;AAC7D,QAAI,WAAW,CAAC,WAAW,kBAAkB,SAAS;AACpD,UAAI,QAAS,eAAc,OAAO;AAClC,sBAAgB;AAChB,gBAAU,YAAY,aAAa,MAAM;AAAA,IAC3C,WAAW,CAAC,UAAU,SAAS;AAC7B,oBAAc,OAAO;AACrB,gBAAU;AACV,sBAAgB;AAAA,IAClB;AAAA,EACF;AAEA,QAAM,aAAa,CAAC,cAClB,QAAQ,MAAM,KAAK,SAAS,SAAS,GAAG,YAAY;AAatD,QAAM,cAAc,CAClB,WACA,SACA,MACA,eACA,WAAoC,CAAC,MACpB;AACjB,QAAI,KAAK,UAAU,aAAa,CAAC,UAAW,QAAO;AACnD,UAAM,SAAS,UAAU,IAAI,SAAS;AACtC,QAAI,CAAC,QAAQ,MAAO,QAAO;AAC3B,UAAM,MAAM,WAAW,SAAS;AAChC,UAAM,WAAW,UAAU,SAAS,eAAe,OAAO,SAAS,eAAe,OAAO,KAAK,GAAG;AAAA,MAC/F,WAAW,KAAK;AAAA,MAChB,OAAO,QAAQ;AAAA,MACf,QAAQ,OAAO;AAAA,MACf,OAAO,QAAQ;AAAA,MACf,KAAK,WAAW,MAAM,YAAY,IAAI,GAAG,IAAI,QAAW,QAAQ,SAAS;AAAA,MACzE,SAAS;AAAA,MACT,KAAK,KAAK,IAAI;AAAA,IAChB,CAAC;AACD,QAAI,CAAC,SAAU,QAAO;AACtB,UAAM,OAAO,QAAQ,aAAa;AAClC,WAAO,EAAE,GAAG,MAAM,UAAU,MAAM,aAAa,WAAW,MAAM,gBAAgB,UAAU,KAAK,WAAW,QAAQ,CAAC,EAAE;AAAA,EACvH;AAEA,QAAM,aAAa,CAAC,cAAmC;AACrD,kBAAc;AAId,QAAI;AACJ,UAAM,QAAQ,KAAK,IAAI;AACvB,UAAM,WAAW,KAAK,OAAO,SAAS;AACtC,UAAM,OAAO,mBAAmB;AAAA,MAC9B,GAAG;AAAA,MACH,QAAQ,CAAC,OAAO,UAAU,IAAI,EAAE;AAAA,MAChC,KAAK;AAAA,IACP,CAAC;AACD,UAAM,QAAQ,oBAAI,IAA0B;AAG5C,UAAM,WAA2B,CAAC;AAClC,eAAW,OAAO,MAAM;AACtB,UAAI,IAAI,UAAU,aAAa,CAAC,IAAI,UAAW;AAC/C,YAAME,WAAU,gBAAgB,WAAW,IAAI,SAAS,CAAC;AACzD,YAAM,OAAO,YAAY,IAAI,WAAWA,UAAS,YAAYA,UAAS,EAAE,SAAS,IAAI,SAAS,KAAK,MAAM,CAAC,GAAG,IAAI,KAAK;AACtH,YAAM,IAAI,IAAI,WAAW,IAAI;AAC7B,UAAI,CAAC,IAAI,WAAY,UAAS,KAAK,IAAI;AAAA,IACzC;AACA,UAAM,SAAS,SAAS,OAAO,SAAS;AACxC,UAAM,SAAS,MAAM,QAAQ,WAAW,QAAQ,KAAK;AACrD,UAAM,UAAU,gBAAgB,WAAW,SAAS,CAAC;AACrD,UAAM,UAAU,gBAAgB,SAAS;AAAA,MACvC;AAAA,MACA,WAAW,OAAO;AAAA,MAClB,WAAW,SAAS,UAAU,SAAS,KAAK,OAAO;AAAA,MACnD,KAAK;AAAA,IACP,CAAC;AACD,UAAM,WAAW,UAAU,YAAY,WAAW,SAAS,SAAS,QAAW,QAAQ,IAAI;AAC3F,WAAO,EAAE,MAAM,OAAO,SAAS;AAAA,EACjC;AAEA,QAAM,SAAS,CAAC,YAAkC;AAEhD,UAAM,OAAO,cAAc,QAAQ,SAAS,aAAa,MAAM,MAAM;AACrE,eAAW,MAAM,eAAe,QAAQ,IAAI,CAAC;AAC7C,WAAO,MAAM,QAAQ,MAAM,SAAS,EAAE,IAAI,UAAU,KAAK,OAAO,GAAG,QAAQ,IAAI,EAAE,CAAC,CAAC;AACnF,WAAO;AAAA,EACT;AAGA,QAAM,oBAAoB,CAAC,MAAoB,SAA0C;AACvF,UAAM,OAAO,cAAc,MAAM;AACjC,YAAQ,MAAM,YAAY,MAAM;AAChC,UAAM,SAAS,mBAAmB,MAAM,MAAM,iBAAiB;AAC/D,WAAO,KAAK,QAAQ,CAAC,SAAS,UAAU;AACtC,UAAI,UAAU,KAAK,SAAS,WAAW,KAAK,UAAU,WAAW;AAC/D,cAAM,OAAO,cAAc,MAAM;AACjC,eAAO,MAAM,MAAM,eAAe,MAAM,IAAI,eAAe,MAAM,CAAC;AAClE,eAAO,MAAM,QAAQ,MAAM,SAAS,EAAE,IAAI,UAAU,KAAK,OAAO,GAAG,QAAQ,IAAI,EAAE,CAAC,CAAC;AACnF,mBAAW,MAAM,IAAI;AAAA,MACvB,OAAO;AACL,mBAAW,MAAM,OAAO,OAAO,CAAC;AAAA,MAClC;AAAA,IACF,CAAC;AACD,eAAW,WAAW,OAAO,IAAK,YAAW,MAAM,OAAO,OAAO,CAAC;AAClE,eAAW,WAAW,OAAO,KAAM,YAAW,MAAM,OAAO,OAAO,CAAC;AACnE,WAAO;AAAA,EACT;AAEA,QAAM,WAAW,CAAC,KAAkB,aAAoD;AACtF,UAAM,MAAM,cAAc,KAAK;AAC/B,UAAM,SAAS,UAAU,KAAK,iBAAiB;AAC/C,UAAM,QAAQ,cAAc,MAAM;AAClC,YAAQ,OAAO,YAAY,MAAM;AACjC,WAAO,MAAM,QAAQ,CAAC,SAAS,UAAU;AACvC,YAAM,OAAO,cAAc,MAAM;AACjC,UAAI,UAAU,KAAK,IAAI,UAAU,WAAW;AAC1C,eAAO,MAAM,MAAM,eAAe,MAAM,IAAI,eAAe,MAAM,CAAC;AAAA,MACpE,OAAO;AACL,mBAAW,MAAM,eAAe,QAAQ,IAAI,CAAC;AAAA,MAC/C;AACA,aAAO,MAAM,QAAQ,MAAM,SAAS,EAAE,IAAI,UAAU,KAAK,OAAO,GAAG,QAAQ,IAAI,EAAE,CAAC,CAAC;AACnF,iBAAW,OAAO,IAAI;AAAA,IACxB,CAAC;AACD,eAAW,KAAK,KAAK;AACrB,QAAI,OAAO,OAAO,SAAS,GAAG;AAG5B,YAAM,SAAS,cAAc,MAAM;AACnC,cAAQ,QAAQ,YAAY,MAAM;AAClC,iBAAW,WAAW,OAAO,OAAQ,YAAW,QAAQ,OAAO,OAAO,CAAC;AACvE,iBAAW,KAAK,MAAM;AAAA,IACxB;AACA,QAAI,SAAU,YAAW,KAAK,QAAQ,MAAM,kBAAkB,UAAU,QAAQ,GAAG,cAAc,KAAK,CAAC,CAAC;AACxG,QAAI,IAAI,WAAW;AACjB,YAAM,YAAY,IAAI;AACtB,cAAQ,KAAK,eAAe,MAAM,QAAQ,MAAM,KAAK,SAAS,SAAS,GAAG,MAAS,CAAC;AACpF;AAAA,QAAQ;AAAA,QAAK;AAAA,QAAe,MAC1B,QAAQ,MAAM,QAAQ,KAAK,mBAAmB,KAAK,OAAO,EAAE,KAAK,GAAG,MAAS;AAAA,MAC/E;AACA;AAAA,QAAQ;AAAA,QAAK;AAAA,QAAc,MACzB,QAAQ,MAAM,QAAQ,KAAK,mBAAmB,aAAa,GAAG,MAAS;AAAA,MACzE;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAEA,QAAM,eAAe,CAAC,UAA+B;AACnD,UAAM,QAAQ,cAAc,MAAM;AAClC,UAAM,OAAO,cAAc,GAAG;AAC9B,eAAW,MAAM,eAAe,KAAK,CAAC;AACtC,eAAW,OAAO,IAAI;AACtB,WAAO,MAAM,QAAQ,OAAO,MAAM,KAAK,OAAO,EAAE,IAAI,CAAC;AACrD,WAAO;AAAA,EACT;AAEA,QAAM,eAAe,CACnB,MACA,UACA,gBACgB;AAChB,UAAM,OAAO,KAAK;AAClB,UAAM,UAAU,cAAc,KAAK;AAInC,UAAM,SAAS,cAAc,KAAK;AAClC,YAAQ,QAAQ,iBAAiB,KAAK;AACtC,YAAQ,QAAQ,OAAO,CAAC;AACxB,YAAQ,QAAQ,eAAe,MAAM,KAAK,SAAS,KAAK,YAAY,CAAC,UAAU,CAAC,KAAK,CAAC;AACtF,QAAI,KAAK,SAAS,GAAG;AACnB,YAAM,QAAQ,cAAc,MAAM;AAClC,aAAO,OAAO,MAAO,SAAS,IAAI,WAAM,QAAI;AAC5C,aAAO,MAAM,QAAQ,OAAO,MAAM,KAAK,OAAO,EAAE,IAAI,CAAC;AACrD,iBAAW,QAAQ,KAAK;AAAA,IAC1B;AACA,UAAM,QAAQ,aAAa,WAAW;AACtC,UAAM,UAAU,cAAc,MAAM;AACpC,WAAO,SAAS,MAAO,SAAS,IAAI,KAAK,KAAK,iBAAiB,IAAI,CAAC,GAAI;AACxE,WAAO,MAAM,QAAQ,SAAS,SAAS,EAAE,IAAI,KAAK,OAAO,EAAE,MAAM,CAAC,CAAC;AACnE,eAAW,OAAO,OAAO;AACzB,eAAW,QAAQ,KAAK;AACxB,eAAW,SAAS,MAAM;AAE1B,UAAM,OAAO,cAAc,KAAK;AAChC;AAAA,MAAO;AAAA,MAAM,MACX,SAAS,KAAK,KAAK,UAAU,IACzB,KAAK,IAAI,CAAC,QAAQ,SAAS,KAAK,IAAI,YAAY,KAAK,MAAM,IAAI,IAAI,SAAS,IAAI,MAAS,CAAC,IAC1F;AAAA,IACN;AACA,eAAW,SAAS,IAAI;AACxB,WAAO;AAAA,EACT;AAOA,QAAM,gBAAgB,CAAC,aAA0D;AAC/E,UAAM,UAAU,cAAc,KAAK;AACnC,eAAW,SAAS,aAAa,eAAe,CAAC;AACjD,UAAM,OAAO,cAAc,KAAK;AAChC,WAAO,MAAM,MAAM;AACjB,YAAM,OAAO,SAAS;AACtB,aAAO,OAAO,QAAQ,MAAM,kBAAkB,MAAM,OAAO,GAAG,IAAI,IAAI;AAAA,IACxE,CAAC;AACD,eAAW,SAAS,IAAI;AACxB,WAAO;AAAA,EACT;AAEA,QAAM,gBAAgB,CAAC,UAA2C;AAChE,UAAM,UAAU,cAAc,KAAK;AACnC,eAAW,SAAS,aAAa,UAAU,CAAC;AAC5C,eAAW,QAAQ,OAAO;AACxB,YAAMC,QAAO,cAAc,MAAM;AACjC,cAAQA,OAAM,YAAY,MAAM;AAChC,iBAAW,WAAW,KAAK,SAAU,YAAWA,OAAM,OAAO,OAAO,CAAC;AACrE,iBAAW,SAASA,KAAI;AAAA,IAC1B;AACA,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,CAAC,cAAqD;AACnE,UAAM,OAAO,cAAc,KAAK;AAChC,UAAM,cAAc,cAAc,KAAK;AACvC,UAAM,eAAe,cAAc,KAAK;AACxC,UAAM,cAAc,cAAc,KAAK;AACvC,eAAW,MAAM,WAAW;AAC5B,eAAW,MAAM,YAAY;AAC7B,eAAW,MAAM,WAAW;AAC5B,UAAM,MAAM,uBAAO,mBAAmB;AACtC,oBAAgB,IAAI,GAAG;AACvB,UAAM,CAAC,UAAU,WAAW,IAAI,aAAa,IAAI;AACjD,UAAM,OAAO,WAAW,MAAM;AAC5B,YAAM,WAAW,UAAU;AAC3B,UAAI,CAAC,SAAU,QAAO;AACtB,aAAO,QAAQ,MAAM,WAAW,QAAQ,GAAG,UAAU;AAAA,IACvD,CAAC;AACD,WAAO,MAAM;AACX,YAAM,EAAE,MAAM,SAAS,IAAI,KAAK;AAChC,YAAM,UAAU,KAAK,KAAK,CAAC,QAAQ,IAAI,UAAU,SAAS,KAAK,UAAU,UAAU;AACnF,UAAI,QAAS,iBAAgB,IAAI,GAAG;AAAA,UAC/B,iBAAgB,OAAO,GAAG;AAC/B,UAAI,KAAK,SAAS,KAAK,SAAU,iBAAgB,IAAI,GAAG;AAAA,UACnD,iBAAgB,OAAO,GAAG;AAC/B,iBAAW;AAAA,IACb,CAAC;AACD,cAAU,MAAM;AACd,sBAAgB,OAAO,GAAG;AAC1B,sBAAgB,OAAO,GAAG;AAC1B,sBAAgB,OAAO,GAAG;AAC1B,iBAAW;AAAA,IACb,CAAC;AAED,UAAM,cAAc,WAAW,MAAM,KAAK,EAAE,aAAa,MAAS;AAClE;AAAA,MAAO;AAAA,MAAa,MAClB,YAAY,IAAI,QAAQ,MAAM,cAAc,MAAM,KAAK,EAAE,QAAQ,GAAG,IAAI,IAAI;AAAA,IAC9E;AACA,WAAO,cAAc,MAAM;AACzB,YAAM,UAAU,KAAK;AACrB,UAAI,QAAQ,KAAK,WAAW,EAAG,QAAO;AACtC,aAAO,QAAQ,MAAM,aAAa,SAAS,UAAU,WAAW,GAAG,IAAI;AAAA,IACzE,CAAC;AACD,UAAM,eAAe;AAAA,MAAW,MAC9B,QAAQ,MAAM,kBAAkB,MAAM,GAAG,SAAS,GAAG,mBAAmB,MAAM,CAAC,GAAG,CAAC,CAAuB;AAAA,IAC5G;AACA,WAAO,aAAa,MAAM;AACxB,YAAM,QAAQ,aAAa;AAC3B,UAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,aAAO,QAAQ,MAAM,cAAc,KAAK,GAAG,IAAI;AAAA,IACjD,CAAC;AACD,eAAW;AACX,WAAO;AAAA,EACT;AAEA,QAAM,UAAU,MAAM;AACpB,oBAAgB,MAAM;AACtB,oBAAgB,MAAM;AACtB,oBAAgB,MAAM;AACtB,eAAW;AAAA,EACb;AACA,SAAO,EAAE,QAAQ,QAAQ;AAC3B;AAqEO,SAAS,WAAW,KAAkB;AAK3C,QAAM,mBAAmB,oBAAI,IAA4C;AACzE,QAAM,UAAU,oBAAI,IAAY;AAChC,QAAM,YAAY,oBAAI,IAAoB;AAC1C,QAAM,aAAa,oBAAI,IAAoB;AAC3C,QAAM,CAAC,SAAS,UAAU,IAAI,aAAa,CAAC;AAC5C,QAAM,OAAO,MAAM,WAAW,CAAC,UAAU,QAAQ,CAAC;AAElD,QAAM,WAAW,CAAC,SAAoC;AACpD,QAAI,CAAC,MAAM,MAAM,CAAC,KAAK,SAAU,QAAO;AACxC,QAAI,WAAW,iBAAiB,IAAI,KAAK,QAAQ;AACjD,QAAI,CAAC,UAAU;AACb,iBAAW,oBAAI,IAAI;AACnB,uBAAiB,IAAI,KAAK,UAAU,QAAQ;AAAA,IAC9C;AACA,aAAS,IAAI,KAAK,IAAI;AAAA,MACpB,IAAI,KAAK;AAAA,MACT,OAAO,KAAK;AAAA,MACZ,OAAO,KAAK;AAAA,MACZ,SAAS,KAAK,MAAM;AAAA,MACpB,SAAS,KAAK,MAAM;AAAA,IACtB,CAAC;AACD,WAAO;AAAA,EACT;AAEA,QAAM,cAAiC,CAAC;AACxC,QAAM,SAAS,CAAC,MAAc,YAAuC;AACnE,UAAM,MAAM;AAAA,MACV,MAAM,IAAI,MAAM,GAAG,MAAM,CAAC,UAAU,QAAQ,MAAM,QAAQ,OAAO,cAAc,CAAC,CAAC,GAAG,MAAS,CAAC;AAAA,MAC9F;AAAA,IACF;AACA,QAAI,IAAK,aAAY,KAAK,GAAG;AAAA,EAC/B;AACA,SAAO,mBAAmB,CAAC,eAAe,SAAS,WAAW,IAAI,KAAK,KAAK,CAAC;AAC7E,SAAO,mBAAmB,CAAC,eAAe,SAAS,WAAW,IAAI,KAAK,KAAK,CAAC;AAC7E,SAAO,mBAAmB,CAAC,eAAe;AACxC,UAAM,OAAO,WAAW;AACxB,QAAI,MAAM,YAAY,iBAAiB,IAAI,KAAK,QAAQ,GAAG,OAAO,KAAK,EAAE,EAAG,MAAK;AAAA,EACnF,CAAC;AACD,SAAO,kBAAkB,CAAC,eAAe;AACvC,UAAM,KAAK,WAAW;AACtB,UAAM,OAAO,WAAW,QAAQ;AAChC,QAAI,CAAC,MAAM,CAAC,KAAM;AAClB,UAAM,WAAW,WAAW,IAAI,EAAE;AAClC,eAAW,IAAI,IAAI,IAAI;AACvB,QAAI,SAAS,UAAU,YAAY,aAAa,OAAQ,WAAU,IAAI,IAAI,KAAK,IAAI,CAAC;AACpF,QAAI,SAAS,OAAQ,WAAU,OAAO,EAAE;AAExC,SAAK;AAAA,EACP,CAAC;AAED,QAAM,gBAAgB,CAAC,aAAqB;AAC1C,QAAI,QAAQ,IAAI,QAAQ,EAAG;AAC3B,YAAQ,IAAI,QAAQ;AACpB,UAAM,UAAU,IAAI,QAAQ;AAC5B,QAAI,CAAC,SAAS,SAAU;AACxB,SAAK,QAAQ,QAAQ,EAClB,KAAK,MAAM,QAAQ,SAAU,KAAK,SAAS,EAAE,WAAW,SAAS,CAAC,CAAC,EACnE,KAAK,CAAC,aAAa;AAClB,UAAI,UAAU;AACd,iBAAW,QAAQ,UAAU,QAAQ,CAAC,EAAG,WAAU,SAAS,IAAI,KAAK;AACrE,UAAI,QAAS,MAAK;AAAA,IACpB,CAAC,EACA,MAAM,MAAM,MAAS;AAAA,EAC1B;AAEA,QAAM,OAAoB;AAAA,IACxB,SAAS;AACP,YAAM,QAAQ,IAAI,MAAM;AACxB,YAAM,QAAQ,MAAM;AACpB,aAAO;AAAA,QACL,MAAM,MAAM;AAAA,QACZ;AAAA,QACA,SAAS,MAAM;AAAA,QACf,MAAM,IAAI,MAAM,SAAS,OAAO,IAAI;AAAA,QACpC,IAAI,MAAM,WAAW,MAAM;AAAA,QAC3B,OAAO,IAAI,MAAM,OAAO,OAAO,IAAI;AAAA,QACnC,QAAQ,MAAM,WAAW;AAAA,QACzB,aAAa,IAAI,MAAM,WAAW,OAAO,OAAO,IAAI;AAAA,QACpD,OAAO,MAAM;AAAA,QACb,SAAS,MAAM,WAAW,MAAM;AAAA,QAChC,WAAW,IAAI,MAAM,WAAW,MAAM,QAAQ,OAAO,IAAI;AAAA,QACzD,SAAS,IAAI,OAAO,MAAM,WAAW,MAAM,QAAQ,GAAG;AAAA,QACtD,UAAU,IAAI,OAAO,MAAM,mBAAmB,MAAM,mBAAmB,GAAG;AAAA,QAC1E,UAAU,MAAM;AAAA,QAChB,MAAM,MAAM,QAAQ,MAAM;AAAA,QAC1B,MAAM,MAAM,aAAa,MAAM,WAAW;AAAA,QAC1C,MAAM,MAAM,aAAa,MAAM,WAAW;AAAA,QAC1C,OAAO,MAAM,QAAQ,MAAM;AAAA,MAC7B;AAAA,IACF;AAAA,IACA,OAAO,UAAU;AACf,cAAQ;AACR,oBAAc,QAAQ;AACtB,YAAM,QAAyB,CAAC;AAChC,YAAM,cAAiC,CAAC;AACxC,iBAAW,WAAW,IAAI,MAAM,QAAQ,SAAS,QAAQ,KAAK,CAAC,GAAG;AAChE,cAAM,eAAe,IAAI,MAAM,KAAK,QAAQ,EAAE,KAAK,CAAC;AACpD,YAAI,QAAQ,SAAS,aAAa;AAChC,qBAAW,QAAQ,cAAc;AAC/B,gBAAI,KAAK,SAAS,UAAU,CAAC,WAAW,IAAI,KAAK,QAAQ,EAAE,EAAG;AAC9D,kBAAM,QAAQ,KAAK,SAAS,CAAC;AAC7B,kBAAM,KAAK;AAAA,cACT,QAAQ,MAAM;AAAA,cACd,OAAO,MAAM;AAAA,cACb,UAAU,MAAM;AAAA,cAChB,OAAO,MAAM;AAAA,cACb,OAAO,MAAM,MAAM;AAAA,cACnB,KAAK,MAAM,MAAM;AAAA,YACnB,CAAC;AAAA,UACH;AAAA,QACF,WAAW,QAAQ,SAAS,QAAQ;AAElC,qBAAW,QAAQ,cAAc;AAC/B,gBAAI,KAAK,SAAS,UAAU,KAAK,cAAc,QAAQ,CAAC,KAAK,KAAM;AACnE,wBAAY,KAAK,GAAG,cAAc,KAAK,MAAM,QAAQ,MAAM,OAAO,CAAC;AAAA,UACrE;AAAA,QACF;AAAA,MACF;AACA,YAAM,QAAQ,IAAI,IAAI,iBAAiB,IAAI,QAAQ,KAAK,CAAC,CAAC;AAC1D,iBAAW,QAAQ,OAAO;AACxB,cAAM,KAAK,YAAY,IAAI;AAC3B,YAAI,CAAC,MAAM,MAAM,IAAI,EAAE,EAAG;AAC1B,cAAM,OAAO,QAAQ,MAAM,IAAI,MAAM,QAAQ,IAAI,EAAE,GAAG,MAAS;AAC/D,cAAM,IAAI,IAAI;AAAA,UACZ;AAAA,UACA,OAAO,MAAM;AAAA,UACb,OAAO,MAAM;AAAA,UACb,SAAS,MAAM,MAAM,WAAW,KAAK;AAAA,UACrC,SAAS,MAAM,MAAM;AAAA,QACvB,CAAC;AAAA,MACH;AACA,aAAO;AAAA,QACL,UAAU,CAAC,GAAG,MAAM,OAAO,CAAC;AAAA,QAC5B;AAAA,QACA;AAAA,QACA,QAAQ,CAAC,OAA8B;AACrC,gBAAM,OAAO,QAAQ,MAAM,IAAI,MAAM,QAAQ,OAAO,EAAE,GAAG,MAAM,MAAS;AACxE,cAAI,SAAS,UAAU,SAAS,QAAS,QAAO;AAChD,cAAI,SAAS,OAAQ,QAAO;AAC5B,iBAAO;AAAA,QACT;AAAA,QACA,WAAW,CAAC,OAAe,UAAU,IAAI,EAAE;AAAA,QAC3C,MAAM,CAAC,OAAe,QAAQ,MAAM,IAAI,MAAM,QAAQ,IAAI,EAAE,GAAG,MAAM,MAAS;AAAA,MAChF;AAAA,IACF;AAAA,IACA,SAAS,WAAW;AAClB,YAAM,WAA8B,CAAC;AAErC,YAAM,OAAO,aAAa,IAAI,MAAM,QAAQ,SAAS,SAAS,KAAK,CAAC,GAAG,CAAC,YAAY,QAAQ,SAAS,MAAM;AAC3G,iBAAW,WAAW,MAAM;AAC1B,cAAM,QAA4B,CAAC;AACnC,cAAM,QAAkB,CAAC;AACzB,mBAAW,QAAQ,IAAI,MAAM,KAAK,QAAQ,EAAE,KAAK,CAAC,GAAG;AACnD,cAAI,QAAQ,SAAS,eAAe,KAAK,SAAS,OAAQ,OAAM,KAAK,EAAE,MAAM,KAAK,MAAM,QAAQ,KAAK,OAAO,OAAO,CAAC;AAGpH,cAAI,QAAQ,SAAS,UAAU,KAAK,SAAS,UAAU,OAAO,KAAK,SAAS,SAAU,OAAM,KAAK,KAAK,IAAI;AAAA,QAC5G;AACA,iBAAS,KAAK;AAAA,UACZ,MAAM,QAAQ;AAAA,UACd,SAAS,QAAQ,MAAM;AAAA,UACvB,OAAO,QAAQ,UAAU,UAAa,QAAQ,UAAU;AAAA,UACxD;AAAA,UACA,OAAO,QAAQ,SAAS,SAAS,YAAY,KAAK,IAAI;AAAA,QACxD,CAAC;AAAA,MACH;AACA,YAAM,QAAQ,QAAQ,MAAM,IAAI,MAAM,QAAQ,OAAO,SAAS,GAAG,MAAS,KAAK,CAAC;AAChF,aAAO,EAAE,UAAU,OAAO,MAAM,IAAI,CAAC,UAAU,EAAE,QAAQ,KAAK,OAAO,EAAE,EAAE;AAAA,IAC3E;AAAA,IACA,SAAS,WAAW;AAClB,UAAI,MAAM,SAAS,WAAW,EAAE,UAAU,CAAC;AAAA,IAC7C;AAAA,EACF;AAEA,QAAM,UAAU,cAAc,IAAI;AAClC,MAAI,MAAM,SAAS;AAAA,IACjB,OAAO;AAAA,IACP,OAAO;AAAA,MACL,gBAAgB,MAAe,OAA+B;AAC5D,eAAO,QAAQ,OAAO,MAAM,MAAM,UAAU;AAAA,MAC9C;AAAA,IACF;AAAA,EACF,CAAC;AACD,MAAI,WAAW,UAAU,MAAM;AAC7B,eAAW,OAAO,YAAa,SAAQ,KAAK,MAAS;AACrD,YAAQ,QAAQ;AAAA,EAClB,CAAC;AACH;AA6DO,SAAS,QAAQ,SAAgC;AACtD,QAAM,OAAoB;AAAA,IACxB,SAAS;AACP,YAAM,QAAQ,QAAQ;AACtB,YAAM,QAAQ,MAAM,KAAK;AAEzB,YAAM,QAAQ,QAAQ,cAAc,UAAU,QAAQ;AACtD,YAAM,SAAS,MAAM,IAAI,OAAO,KAAK,KAAK,MAAM,KAAK;AACrD,aAAO;AAAA,QACL,MAAM,MAAM,KAAK;AAAA,QACjB;AAAA,QACA,SAAS;AAAA,QACT,MAAM,IAAI,MAAM,KAAK,SAAS,QAAQ,SAAS,MAAM,KAAK,SAAS,QAAQ,MAAM,OAAO,IAAI;AAAA,QAC5F,IAAI,MAAM,KAAK,SAAS,QAAQ,QAAQ;AAAA,QACxC,OAAO,IAAI,MAAM,KAAK,SAAS,MAAM,SAAS,MAAM,KAAK,SAAS,MAAM,MAAM,OAAO,IAAI;AAAA,QACzF,QAAQ,MAAM,KAAK,SAAS,SAAS,QAAQ;AAAA,QAC7C,aAAa,IAAI,MAAM,KAAK,SAAS,SAAS,QAAQ,OAAO,OAAO,IAAI;AAAA,QACxE,OAAO,MAAM,WAAW,OAAO;AAAA,QAC/B,SAAS,MAAM,KAAK,SAAS,QAAQ,QAAQ;AAAA,QAC7C,WAAW,IAAI,MAAM,KAAK,SAAS,QAAQ,QAAQ,QAAQ,OAAO,IAAI;AAAA,QACtE,SAAS,IAAI,OAAO,MAAM,KAAK,SAAS,QAAQ,QAAQ,QAAQ,GAAG;AAAA,QACnE,UAAU,IAAI,OAAO,MAAM,WAAW,QAAQ,MAAM,WAAW,OAAO,MAAM,GAAG;AAAA,QAC/E,UAAU,MAAM,KAAK;AAAA,QACrB,MAAM,MAAM,KAAK,SAAS,MAAM,QAAQ;AAAA,QACxC,MAAM,MAAM,IAAI,cAAc,KAAK,KAAK;AAAA,QACxC,MAAM,MAAM,IAAI,cAAc,KAAK,KAAK;AAAA,QACxC,OAAO,MAAM,KAAK,SAAS,MAAM,QAAQ;AAAA,MAC3C;AAAA,IACF;AAAA,IACA,OAAO,UAAU;AACf,YAAM,YAAiC,QAAQ,KAAK,QAAQ,KAAK,KAAK,CAAC,GACpE,OAAO,CAAC,YAAY,QAAQ,aAAa,QAAQ,EACjD,IAAI,CAAC,aAAa;AAAA,QACjB,IAAI,QAAQ;AAAA,QACZ,OAAO,QAAQ;AAAA,QACf,OAAO,QAAQ;AAAA,QACf,SAAS,QAAQ,MAAM;AAAA,QACvB,SAAS,QAAQ,MAAM;AAAA,MACzB,EAAE;AACJ,YAAM,QAAyB,CAAC;AAChC,YAAM,cAAiC,CAAC;AACxC,iBAAW,WAAW,QAAQ,KAAK,QAAQ,QAAQ,KAAK,QAAQ,KAAK,CAAC,GAAG;AACvE,YAAI,QAAQ,SAAS,aAAa;AAChC,qBAAW,QAAQ,QAAQ,WAAW,CAAC,GAAG;AACxC,gBAAI,KAAK,SAAS,UAAU,CAAC,WAAW,IAAI,KAAK,QAAQ,EAAE,EAAG;AAC9D,kBAAM,KAAK;AAAA,cACT,QAAQ,KAAK,OAAO;AAAA,cACpB,OAAO,KAAK,OAAO;AAAA,cACnB,UAAU,KAAK,OAAO;AAAA,cACtB,OAAO,KAAK,MAAM;AAAA,cAClB,KAAK,KAAK,MAAM;AAAA,YAClB,CAAC;AAAA,UACH;AAAA,QACF,WAAW,QAAQ,SAAS,eAAe,QAAQ,MAAM;AACvD,sBAAY,KAAK,GAAG,cAAc,QAAQ,MAAM,QAAQ,MAAM,OAAO,CAAC;AAAA,QACxE;AAAA,MACF;AACA,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA,QAAQ,CAAC,OAA8B;AACrC,gBAAM,SAAS,QAAQ,MAAM,QAAQ,KAAK,QAAQ,OAAO,EAAE,GAAG,MAAS;AACvE,cAAI,WAAW,UAAW,QAAO;AACjC,cAAI,WAAW,OAAQ,QAAO;AAC9B,iBAAO;AAAA,QACT;AAAA,QACA,WAAW,MAAM;AAAA,QACjB,MAAM,CAAC,OAAe,QAAQ,MAAM,QAAQ,KAAK,QAAQ,OAAO,EAAE,GAAG,MAAS;AAAA,MAChF;AAAA,IACF;AAAA,IACA,SAAS,WAAW;AAClB,YAAM,WAA8B,CAAC;AAErC,YAAM,OAAO;AAAA,QACX,QAAQ,KAAK,QAAQ,QAAQ,KAAK,SAAS,KAAK,CAAC;AAAA,QACjD,CAAC,YAAY,QAAQ,SAAS,UAAU,QAAQ,SAAS;AAAA,MAC3D;AACA,iBAAW,WAAW,MAAM;AAC1B,cAAM,QAA4B,CAAC;AACnC,cAAM,QAAkB,CAAC;AACzB,mBAAW,QAAQ,QAAQ,WAAW,CAAC,GAAG;AACxC,cAAI,QAAQ,SAAS,eAAe,KAAK,SAAS,OAAQ,OAAM,KAAK,EAAE,MAAM,KAAK,MAAM,QAAQ,KAAK,OAAO,OAAO,CAAC;AACpH,cAAI,QAAQ,SAAS,UAAU,KAAK,SAAS,UAAU,OAAO,KAAK,SAAS,SAAU,OAAM,KAAK,KAAK,IAAI;AAAA,QAC5G;AACA,YAAI,QAAQ,SAAS,eAAe,QAAQ,KAAM,OAAM,KAAK,QAAQ,IAAI;AACzE,cAAM,SAAS,QAAQ,SAAS,UAAU,QAAQ,SAAS;AAC3D,iBAAS,KAAK;AAAA;AAAA;AAAA,UAGZ,MAAM,QAAQ,SAAS,cAAc,SAAU,QAAQ,QAAQ;AAAA,UAC/D,SAAS,QAAQ,MAAM;AAAA,UACvB,OAAO,QAAQ,UAAU,UAAa,QAAQ,UAAU;AAAA,UACxD;AAAA,UACA,OAAO,SAAS,YAAY,KAAK,IAAI;AAAA,QACvC,CAAC;AAAA,MACH;AACA,aAAO,EAAE,UAAU,OAAO,CAAC,EAAE;AAAA,IAC/B;AAAA,IACA,SAAS,WAAW;AAClB,cAAQ,GAAG,OAAO,SAAS,EAAE,MAAM,WAAW,UAAU,CAAC;AAAA,IAC3D;AAAA,EACF;AAEA,QAAM,UAAU,cAAc,IAAI;AAClC,QAAM,UAAU;AAAA,IACd,MACE,QAAQ,GAAG,KAAK;AAAA,MACd,QAAQ;AAAA,MACR,QAAQ,CAAC,UAAU,QAAQ,OAAO,MAAM,MAAM,SAAS;AAAA,IACzD,CAAC;AAAA,IACH;AAAA,EACF;AACA,SAAO,MAAM;AACX,QAAI,QAAS,SAAQ,SAAS,MAAS;AACvC,YAAQ,QAAQ;AAAA,EAClB;AACF;AAEA,IAAM,MAAM,OAAO,QAA8B;AAC/C,UAAQ,MAAM,WAAW,GAAG,GAAG,MAAS;AAC1C;AAEA,IAAM,QAAQ,CAAC,YACb,QAAQ,MAAM,QAAQ,OAAO,GAAG,MAAS;AAE3C,IAAO,cAAQ,EAAE,IAAI,yBAAyB,KAAK,MAAM;","names":["text","text","pad","cells","pieces","mkdirSync","readFileSync","renameSync","statSync","writeFileSync","homedir","join","mkdirSync","readFileSync","renameSync","statSync","writeFileSync","homedir","join","pathOverride","readEntries","statSync","readEntries","pathOverride","join","homedir","text","readEntries","readFileSync","statSync","readEntries","r","spread","mkdirSync","readFileSync","renameSync","statSync","writeFileSync","homedir","join","pathOverride","pathOverride","join","homedir","readFileSync","statSync","cells","out","measure","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/turn-progress-store.ts","../src/turn-model.ts","../src/account-usage.ts","../src/account-sidebar.ts","../src/plugin-notice.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 * with the band that prediction carries (`src/turn-model.ts` over\n * `src/turn-history.ts`, h #g247, h #g251), else nothing; the rest is\n * elapsed, calls and the running tool. `src/progress-line.ts` decides all of\n * 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 floorByChildren,\n layoutProgressLine,\n liveOutput,\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, promptChars, sessionTag, turnHistoryKey, type TurnHistory } from \"./turn-history.js\"\nimport { buildTurnModel, type TurnModel } from \"./turn-model.js\"\nimport { createTurnProgressReader, type TurnProgress } from \"./turn-progress-store.js\"\nimport { createAccountUsageReader, type AccountUsage, type KnownAccounts } from \"./account-usage.js\"\nimport { accountUsageLines, type AccountUsageLine } from \"./account-sidebar.js\"\nimport { TOAST_DURATION_MS, TOAST_TITLE, createNoticeToaster } from \"./plugin-notice.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 /** An account's level under the warning threshold, `ok` included: the success colour at full strength (h #g248). */\n ok: 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 /**\n * An estimate's cells covered at the estimate but not past the band's far\n * end: the success colour faded toward muted, like a finished row's ✓.\n */\n barLikely: Color\n /**\n * The track an estimate covers if the turn ends at the band's near end:\n * muted with a little of the success colour, brighter than the rest.\n */\n barBand: 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 ok: \"ok\",\n error: \"error\",\n agent: \"text\",\n description: \"text\",\n muted: \"muted\",\n effort: \"effort\",\n \"effort-faded\": \"effortFaded\",\n \"bar-done\": \"barDone\",\n \"bar-likely\": \"barLikely\",\n \"bar-band\": \"barBand\",\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 // The model over the whole store (src/turn-model.ts, h #g251), built once\n // per change to it because that is when its calibration is fitted, then\n // read once per line per tick.\n let turnModel: TurnModel | undefined = attempt(() => buildTurnModel(historyMap), undefined)\n // What each running turn has generated so far, which only the server half\n // sees: it writes the total as each API call ends (h #g251). Read on the\n // second hand like the others; the clock re-derives the lines.\n const liveProgress = createTurnProgressReader()\n let progressMap: Map<string, TurnProgress> = attempt(() => liveProgress.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 if (nextHistory !== historyMap) turnModel = attempt(() => buildTurnModel(nextHistory), turnModel)\n recordMap = next\n historyMap = nextHistory\n setRecordVersion((value) => value + 1)\n }\n progressMap = attempt(() => liveProgress.read(), progressMap)\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, h #g250, h #g251): the session's\n * recorded agent and model name the key, the turn's prompt length and the\n * effort weight its record, the session's own earlier turns are its\n * session level, its tool calls and the output the server half saw it\n * generate condition it beside the elapsed, its running foreground\n * subagents floor it, and the fill and the band are held so nothing on the\n * bar steps back within the turn. No spawn record, or too little record\n * for the key and its agent's other models, is no estimate, and the line\n * then draws no bar.\n */\n const estimateFor = (\n sessionID: string,\n measure: ProgressMeasure,\n view: ProgressView,\n agentFallback?: string,\n children: readonly ProgressView[] = [],\n ): ProgressView => {\n if (view.state !== \"running\" || !turnModel) return view\n const record = recordMap.get(sessionID)\n if (!record?.model) return view\n const tag = sessionTag(sessionID)\n const estimate = turnModel.estimate(turnHistoryKey(record.agent ?? agentFallback, record.model), {\n elapsedMs: view.elapsedMs,\n chars: measure.chars,\n effort: record.effort,\n calls: measure.calls,\n out: liveOutput(tag ? progressMap.get(tag) : undefined, measure.startedAt),\n session: tag,\n now: Date.now(),\n })\n if (!estimate) return view\n const turn = measure.startedAt ?? 0\n return { ...view, estimate: fills.holdEstimate(sessionID, turn, floorByChildren(estimate, view.elapsedMs, children)) }\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 // The running foreground children's lines: the session's turn waits in\n // their `task` calls, so it cannot end before they do.\n const waitedOn: 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 = estimateFor(row.sessionID, measure, rowProgress(measure, { started: row.started, now: clock }), row.agent)\n lines.set(row.sessionID, line)\n if (!row.background) waitedOn.push(line)\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, waitedOn) : 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 ok: theme.success ?? theme.accent,\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 barLikely: mix(theme.success ?? theme.accent, muted, 0.45),\n barBand: mix(muted, theme.success ?? theme.accent, 0.3),\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 * Every bus event, the plugin's own RPC events included (2.0.31,\n * h #g253): how the server half's warnings arrive (`src/plugin-notice.ts`).\n */\n listen?(handler: (event: { details?: { type?: unknown; data?: unknown } }) => void): () => void\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 toast?: {\n show(options: { title?: string; message: string; variant?: \"info\" | \"success\" | \"warning\" | \"error\"; duration?: number }): void\n }\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 ok: theme.text.feedback.success.base ?? accent,\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 barLikely: mix(theme.text.feedback.success.base ?? accent, muted, 0.45),\n barBand: mix(muted, theme.text.feedback.success.base ?? accent, 0.3),\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 // The server half's warnings, which opencode 2 gives no other way to the\n // screen (`src/plugin-notice.ts`). Once each, however many of the plugin's\n // locations emitted them.\n const toastNotice = createNoticeToaster((notice) =>\n context.ui.toast?.show({\n title: TOAST_TITLE,\n message: notice.message,\n variant: notice.level === \"error\" ? \"error\" : \"warning\",\n duration: TOAST_DURATION_MS,\n }),\n )\n const stopNotices = attempt(\n () => context.data.listen?.((event) => void toastNotice(event?.details?.type, event?.details?.data)),\n undefined,\n )\n return () => {\n if (release) attempt(release, undefined)\n if (stopNotices) attempt(stopNotices, 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), `bar-likely` and `bar-band` the estimate's band on the bar\n * (h #g251); `tier`, `reset` and `ok` are the `Accounts` line's plan tier,\n * reset time and under-threshold level (`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-likely\"\n | \"bar-band\"\n | \"bar-over\"\n | \"bar-track\"\n | \"fraction\"\n | \"estimate\"\n | \"estimate-over\"\n | \"tool\"\n | \"cost\"\n | \"tier\"\n | \"reset\"\n | \"ok\"\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-model.ts`: a log-normal fitted to\n * the recorded turns, conditioned on this one having run this long, read\n * through the calibration of the model's own past errors), and the line\n * says both, `1m02s/~2m30s`. The bar draws the estimate's band as well:\n * the cells covered even if the turn runs to the band's far end in the\n * full colour, the rest of the cells covered at the estimate in a softer\n * one, and the track the turn has covered if it ends at the band's near\n * end a little brighter than the rest of the track:\n *\n * ▰▰▰▰▰▰▱▱▱▱ sure ▰▰▰▰ · likely ▰▰ · band ▱▱ · track ▱▱\n *\n * Nothing on the bar ever steps back within a turn (`createFillMemory`),\n * though the total it is drawn against can rise, which it does while a\n * foreground subagent has longer left than the session's own estimate\n * (`floorByChildren`). 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-model.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 const hold = (key: string, turn: number, fill: number): number => {\n const entry = held.get(key)\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(key, { turn, fill })\n return fill\n }\n return {\n hold,\n /**\n * The same hold for a whole estimate: its fill and both edges of its band,\n * each at its highest this turn, so neither the lit cells nor the band's\n * reach ever step back while the total rises. The total is not held: it\n * is the text, and the text says what the model says now. Each edge at\n * its highest stays on its side of the fill, because it is on that side\n * at every moment (elapsed over a longer total, and over a shorter one).\n */\n holdEstimate(sessionID: string, turn: number, estimate: TurnEstimate): TurnEstimate {\n const fill = hold(sessionID, turn, estimate.fill)\n if (!estimate.band) return { ...estimate, fill }\n const sure = hold(`${sessionID}\\u0000sure`, turn, estimate.band.sure)\n const maybe = hold(`${sessionID}\\u0000maybe`, turn, estimate.band.maybe)\n return {\n ...estimate,\n fill,\n band: { ...estimate.band, sure: Math.min(sure, fill), maybe: Math.max(maybe, fill) },\n }\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/** Where an estimate's band falls on the bar, as cell counts from the left. */\nexport interface EstimateCells {\n /** Lit in the full colour: covered even if the turn runs to the band's far end. */\n sure: number\n /** Lit, past `sure` in a softer colour: covered at the estimate itself (`estimateCells`). */\n likely: number\n /** Track past `likely` drawn brighter: covered if the turn ends at the band's near end. */\n band: number\n}\n\n/**\n * An estimate's cells, band included. The lit count is `estimateCells`\n * exactly, so the band only recolours cells and the bar still reads as it\n * did: never full before `over`, which is the warning colour's alone. Only\n * the band's track may reach the last cell, because a brighter `▱` is not a\n * full bar: it says the turn may end any moment now. An estimate without a\n * band (none to draw, or `over`) is its lit cells alone.\n */\nexport function estimateBandCells(estimate: TurnEstimate, width: number = BAR_WIDTH): EstimateCells {\n const likely = estimateCells(estimate, width)\n if (estimate.over || !estimate.band) return { sure: likely, likely, band: likely }\n const sure = Math.min(likely, Math.max(0, Math.round(estimate.band.sure * width)))\n const band = Math.max(likely, Math.min(width, Math.round(estimate.band.maybe * width)))\n return { sure, likely, band }\n}\n\n/** How far before the TUI's own start of a turn the server half may have marked it. */\nexport const TURN_PROGRESS_SLACK_MS = 1_000\n\n/**\n * The output a running turn has generated so far, from the live channel's\n * record for its session (`src/turn-progress-store.ts`, h #g251), or\n * undefined when the record is an earlier turn's. The server half marks a\n * turn when opencode asks it for the reply, which is after the message that\n * started the turn was stored, so this turn's record started no earlier\n * than the TUI's start of it, less a little slack. A message steered in\n * mid-turn restarts the TUI's turn and not the server's, so the record's\n * count, which runs from the server's start, is not read against it. A turn\n * the TUI dates from no message has nothing to check against and reads\n * nothing.\n */\nexport function liveOutput(record: { turn: number; out: number } | undefined, startedAt: number | undefined): number | undefined {\n if (!record || startedAt === undefined) return undefined\n return record.turn >= startedAt - TURN_PROGRESS_SLACK_MS ? record.out : undefined\n}\n\n/**\n * A turn parked in a foreground subagent cannot end before the subagent does\n * (h #g251), because its `task` call returns the child's answer. So each\n * running foreground child's remaining time, read off the child's own\n * estimate, floors the parent's: the total at least the parent's elapsed\n * plus the child's remaining median, each edge of the band at least the\n * elapsed plus the child's remaining edge. A `task_batch` waits for every\n * child it started, so the longest of each wins. The parent's record already\n * holds the time its turns usually spend in children, so the floor only\n * moves an estimate that a child is visibly outlasting.\n *\n * A child past its own record (`over`) has no remaining time to offer and\n * floors nothing, and an `over` parent stays over: it is past every recorded\n * turn of its own. The host applies this before the fill memory's hold, so a\n * rising floor lowers nothing on the bar.\n */\nexport function floorByChildren(estimate: TurnEstimate, elapsedMs: number, children: readonly ProgressView[]): TurnEstimate {\n if (estimate.over) return estimate\n let median = 0\n let low = 0\n let high = 0\n for (const child of children) {\n const own = child.estimate\n if (child.state !== \"running\" || !own || own.over) continue\n const left = (ms: number) => Math.max(0, ms - child.elapsedMs)\n median = Math.max(median, left(own.totalMs))\n low = Math.max(low, left(own.band?.lowMs ?? own.totalMs))\n high = Math.max(high, left(own.band?.highMs ?? own.totalMs))\n }\n const elapsed = Math.max(0, elapsedMs)\n const totalMs = Math.max(estimate.totalMs, elapsed + median)\n const band = estimate.band\n if (totalMs === estimate.totalMs && (!band || (band.lowMs >= elapsed + low && band.highMs >= elapsed + high))) {\n return estimate\n }\n const fill = totalMs > 0 ? elapsed / totalMs : 0\n if (!band) return { ...estimate, totalMs, fill }\n const lowMs = Math.min(totalMs, Math.max(band.lowMs, elapsed + low))\n const highMs = Math.max(totalMs, band.highMs, elapsed + high)\n return {\n ...estimate,\n totalMs,\n fill,\n band: {\n lowMs,\n highMs,\n sure: highMs > 0 ? elapsed / highMs : 0,\n maybe: lowMs > 0 ? Math.min(1, elapsed / lowMs) : 0,\n },\n }\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 bar: Segment[] = []\n const cellsOf = (segment: (cell: number) => Segment) => {\n for (let cell = 0; cell < BAR_WIDTH; cell += 1) bar.push(segment(cell))\n }\n if (ended) {\n cellsOf(() => ({ text: BAR_DONE, role: endedRole }))\n } else if (view.plan) {\n const filled = filledCells(view.plan)\n cellsOf((cell) => (cell < filled ? { text: BAR_DONE, role: \"bar-done\" } : { text: BAR_TODO, role: \"bar-track\" }))\n } else if (estimate) {\n const cells = estimateBandCells(estimate)\n cellsOf((cell) =>\n estimate.over\n ? { text: BAR_DONE, role: \"bar-over\" }\n : cell < cells.sure\n ? { text: BAR_DONE, role: \"bar-done\" }\n : cell < cells.likely\n ? { text: BAR_DONE, role: \"bar-likely\" }\n : cell < cells.band\n ? { text: BAR_TODO, role: \"bar-band\" }\n : { 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 took, for the sidebar's progress bar (h #g247): one sample\n * per finished turn, keyed by the agent and the model it ran as, written by\n * the server half at the CLI's `result` and read by the TUI half\n * (`src/tui.ts`). What the bar is drawn from that record is\n * `src/turn-model.ts`; this module is the record itself, and the counts both\n * halves must agree on (the prompt's length, the turn's tool calls, where a\n * turn starts).\n *\n * ## The store\n *\n * ```json\n * { \"version\": 1, \"turns\": { \"build/claude-opus-5-5\": [\n * { \"ms\": 61230, \"chars\": 420, \"effort\": \"high\", \"calls\": 3, \"out\": 5321, \"session\": \"5c1d0f3a\", \"at\": 1760000000000 },\n * { \"ms\": 6077, \"chars\": 279, \"effort\": \"low\", \"calls\": 1, \"failed\": true, \"at\": 1760000100000 } ] } }\n * ```\n *\n * A duration, a character count, an effort level, a tool-call count, the\n * output tokens its API calls generated, whether the turn ended on a failed\n * tool result, a short tag of the conversation it ran in, and a time: no\n * prompt text, no path, no session id, nothing secret. The output count is\n * the sum the live channel (`src/turn-progress-store.ts`) wrote as the turn\n * ran, so the record and the running turn count it the same way. The tag is\n * a 32-bit hash of the opencode session id (`sessionTag`),\n * enough for the estimate to tell this conversation's turns from another's\n * and not the id. Same discipline as the spawn record store (h #g234):\n * merge-on-write so two opencode processes do not erase each other, atomic\n * rename, `0600` in the `0700` directory, bounded at `MAX_TURN_SAMPLES`\n * newest samples per key, `MAX_TURN_KEYS` keys and `TURN_SAMPLE_MAX_AGE_MS`.\n * A missing, unreadable or half-written file is an empty store, which costs\n * 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\nimport { recordTurnProgress } from \"./turn-progress-store.js\"\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 /** Tool calls the turn made, counted as the TUI counts a turn's tool parts; absent on a sample from before 0.57.2. */\n calls?: number\n /**\n * Output tokens the turn's API calls generated, summed off each call's\n * `message_delta`; absent on a sample from before 0.58.0 and on a turn the\n * stream carried no `message_delta` for (the interactive transport's).\n */\n out?: number\n /**\n * The turn's last tool result was an error (the CLI's own refusal of a tool,\n * or a proxied tool opencode reported as failed), so the turn ended on a\n * failure opencode may have reported as the subagent failing. Its length\n * says nothing about a served turn's, and the estimate skips it (h #g251).\n * Written only when true.\n */\n failed?: boolean\n /**\n * The conversation the turn ran in, as `sessionTag` hashes its opencode\n * session id: the estimate's session level reads it (`src/turn-model.ts`).\n * Absent on a sample from before 0.58.0.\n */\n session?: 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\nconst SESSION_TAG = /^[0-9a-f]{8}$/\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 * A short tag for an opencode session id, the same on both halves: FNV-1a\n * over its characters, 32 bits as eight hex digits. Enough for the estimate\n * to tell one conversation's turns from another's (h #g251), and not the id:\n * a hash of it, which the store's \"no session id\" means to keep true.\n * Undefined for no id, and for `default`, the affinity of a turn opencode\n * did not tag with a session.\n */\nexport function sessionTag(sessionID: string | undefined): string | undefined {\n if (!sessionID || sessionID === \"default\") return undefined\n let hash = 0x811c9dc5\n for (let index = 0; index < sessionID.length; index += 1) {\n hash ^= sessionID.charCodeAt(index)\n hash = Math.imul(hash, 0x01000193) >>> 0\n }\n return hash.toString(16).padStart(8, \"0\")\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\nfunction toolResultParts(message: PromptMessageLike): Array<{ toolCallId?: unknown; output?: unknown }> {\n if (!Array.isArray(message.content)) return []\n return message.content.filter(\n (part): part is { toolCallId?: unknown; output?: unknown } =>\n !!part && typeof part === \"object\" && (part as { type?: unknown }).type === \"tool-result\",\n )\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 *\n * The exception is a tool result for no call this process is waiting on\n * (h #g251): after an interrupted turn opencode's next prompt ends in the\n * interrupted calls' results and then the operator's new message, which is a\n * fresh turn, not a continuation. Given the ids of the proxied calls still\n * parked, a trailing tool message that answers none of them starts the turn.\n */\nexport function startsTurn(prompt: readonly PromptMessageLike[], pendingToolCallIds?: ReadonlySet<string>): boolean {\n const last = prompt[prompt.length - 1]\n if (!last || last.role !== \"user\") return false\n const before = prompt[prompt.length - 2]\n if (before === undefined || before.role !== \"tool\") return true\n if (!pendingToolCallIds) return false\n return toolResultParts(before).every((part) => typeof part.toolCallId !== \"string\" || !pendingToolCallIds.has(part.toolCallId))\n}\n\n/**\n * How many tool calls the turn a prompt continues has made so far: the\n * `tool-call` parts of the assistant messages since the message that started\n * it. Every proxied and CLI-executed call the plugin emitted in an earlier\n * step is one of them, because opencode stores each as a tool part and hands\n * them back here; the step that sees the `result` adds its own emitted\n * calls, which are not in its prompt yet. That is the count the TUI reads\n * off the turn's tool parts (`measureProgress`), which is what lets the\n * estimate condition on it mid-turn (h #g250).\n *\n * Where the turn started is `from`, the prompt's length when `markTurnStart`\n * ran, so the count agrees with the start that was marked (h #g251); without\n * it, or with a prompt shorter than that (a compaction in between), the\n * boundary is read back off the prompt by `startsTurn`'s plain rule, the\n * last user message not following a tool result, so a message the host\n * steered in mid-turn does not restart the count.\n */\nexport function turnToolCalls(prompt: readonly PromptMessageLike[], from?: number): number {\n let start = 0\n if (from !== undefined && from >= 0 && from <= prompt.length) {\n start = from\n } else {\n for (let index = prompt.length - 1; index >= 0; index -= 1) {\n if (prompt[index].role !== \"user\") continue\n const before = prompt[index - 1]\n if (before === undefined || before.role !== \"tool\") {\n start = index\n break\n }\n }\n }\n let calls = 0\n for (let index = start; index < prompt.length; index += 1) {\n const message = prompt[index]\n if (message.role !== \"assistant\" || !Array.isArray(message.content)) continue\n for (const part of message.content) {\n if (part && typeof part === \"object\" && (part as { type?: unknown }).type === \"tool-call\") calls += 1\n }\n }\n return calls\n}\n\n/**\n * Whether the turn's newest tool result in the prompt, the one the final\n * step was answering, was an error: a proxied tool opencode ran and reported\n * failed (`error-text`, `error-json`, `execution-denied`). Trailing user\n * messages (a steered one) are looked past; a tool message from before the\n * turn started (`from`) is not this turn's. The final step's own CLI-executed\n * results are newer than anything here and are read off the stream instead.\n */\nexport function lastToolResultFailed(prompt: readonly PromptMessageLike[], from: number): boolean {\n for (let index = prompt.length - 1; index >= Math.max(0, from); index -= 1) {\n const message = prompt[index]\n if (message.role === \"user\") continue\n if (message.role !== \"tool\") return false\n const parts = toolResultParts(message)\n const newest = parts[parts.length - 1]\n const output = newest?.output as { type?: unknown } | undefined\n const type = output && typeof output === \"object\" ? output.type : undefined\n return type === \"error-text\" || type === \"error-json\" || type === \"execution-denied\"\n }\n return false\n}\n\n// ---------------------------------------------------------------------------\n// When a turn started, held from its first step to its result\n// ---------------------------------------------------------------------------\n\nconst MAX_TURN_STARTS = 256\n\nexport interface TurnStart {\n at: number\n chars?: number\n /** The prompt's length when the turn started: where `turnToolCalls` counts from. */\n promptLength?: number\n /** The session's tag (`sessionTag`): where the live channel writes this turn's progress. */\n session?: string\n /** Output tokens the turn's API calls have generated so far; absent until the first call ends. */\n out?: number\n}\n\nconst turnStarts = new Map<string, TurnStart>()\n\n/**\n * Note that a turn on `key` started. A turn is a run of steps (every proxied\n * tool call ends one), and only its last step sees the `result`, so the\n * start waits here, keyed like the process, until `takeTurnStart`; the\n * output its calls generate is added to it as they end (`noteTurnOutput`).\n * A 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, start: TurnStart): 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, {\n at: start.at,\n ...(start.chars !== undefined ? { chars: start.chars } : {}),\n ...(start.promptLength !== undefined ? { promptLength: start.promptLength } : {}),\n ...(start.session !== undefined ? { session: start.session } : {}),\n })\n}\n\n/**\n * One API call of the turn on `key` ended, having generated `tokens` output\n * tokens (its `message_delta`'s count, the real one): add them to the turn's\n * total and write the total to the live channel, where the TUI reads it\n * while the turn runs (h #g251). A key with no turn marked (a compaction, a\n * turn opencode did not tag with a session) is not one the sidebar shows,\n * and nothing is written.\n */\nexport function noteTurnOutput(key: string, tokens: number, now: number = Date.now()): void {\n const start = turnStarts.get(key)\n if (!start || !Number.isFinite(tokens) || tokens < 0) return\n start.out = (start.out ?? 0) + tokens\n if (start.session) recordTurnProgress(start.session, { turn: start.at, out: start.out }, now)\n}\n\n/** The start noted for `key`, once: a second `result` in the same turn finds nothing. */\nexport function takeTurnStart(key: string): TurnStart | 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 if (sample.calls !== undefined && (typeof sample.calls !== \"number\" || !Number.isFinite(sample.calls) || sample.calls < 0)) {\n return false\n }\n if (sample.out !== undefined && (typeof sample.out !== \"number\" || !Number.isFinite(sample.out) || sample.out < 0)) {\n return false\n }\n if (sample.failed !== undefined && typeof sample.failed !== \"boolean\") return false\n if (sample.session !== undefined && (typeof sample.session !== \"string\" || !SESSION_TAG.test(sample.session))) {\n return false\n }\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 ...(sample.calls !== undefined ? { calls: Math.round(sample.calls) } : {}),\n ...(sample.out !== undefined ? { out: Math.round(sample.out) } : {}),\n ...(sample.failed ? { failed: true } : {}),\n ...(sample.session ? { session: sample.session } : {}),\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\nexport function _setTurnHistoryStorePathForTests(path: string | null): void {\n pathOverride = path\n turnStarts.clear()\n}\n","/**\n * The live half of the progress estimate's record (h #g251): how much output\n * a running turn has generated so far, which is the one measure of a turn's\n * progress the TUI cannot read off opencode's store. opencode shows the\n * turn's tool calls as they stream, but every API call within a step lands\n * in one assistant message, and a step's usage is reported as zeros until\n * the turn's `result` on purpose (h #g169, h #g225). The server half reads\n * each call's real output at its `message_delta` (the `assistant` frames\n * carry a placeholder, measured on 2.1.293: 6 against the call's 41) and\n * writes the turn's running total here, once per API call; the TUI reads it\n * beside the turn history and the estimate conditions on it\n * (`src/turn-model.ts`).\n *\n * ## Shape, and what is never written\n *\n * ```json\n * { \"version\": 1, \"turns\": { \"5c1d0f3a\": { \"turn\": 1760000000000, \"out\": 5321, \"at\": 1760000012345 } } }\n * ```\n *\n * Keyed by the session's tag (`sessionTag` in `src/turn-history.ts`, a hash\n * of its opencode session id, which the TUI computes from the same id), so\n * the file holds no session id at all. `turn` is when the server half saw\n * the turn start, which is how the TUI tells this turn's record from the\n * previous one's; `out` the output tokens the turn's calls have generated;\n * `at` the last write. Nothing else: no prompt, no path, no text.\n *\n * ## Cost and failure\n *\n * One small write per API call, the same discipline as the spawn record\n * store (h #g234): merge-on-write so two opencode processes do not erase each\n * other, atomic rename, `0600` in the `0700` directory, bounded at\n * `MAX_TURN_PROGRESS` newest turns and `TURN_PROGRESS_MAX_AGE_MS` since their\n * last write. A missing, unreadable or half-written file is an empty store,\n * which costs the estimate one input 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 TurnProgress {\n /** When the server half saw the turn start. */\n turn: number\n /** Output tokens the turn's API calls have generated so far. */\n out: number\n /** The last write. */\n at: number\n}\n\n/** Turns kept at once: one per session that has run a turn lately. */\nexport const MAX_TURN_PROGRESS = 64\n/** A record not written this long is no running turn's. */\nexport const TURN_PROGRESS_MAX_AGE_MS = 24 * 60 * 60 * 1000\n\nconst TAG = /^[0-9a-f]{8}$/\n\nlet pathOverride: string | null = null\n\nexport function turnProgressStorePath(): 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-progress.json\")\n}\n\nfunction isProgress(value: unknown): value is TurnProgress {\n const record = value as TurnProgress | null\n if (!record || typeof record !== \"object\" || Array.isArray(record)) return false\n const count = (field: unknown) => typeof field === \"number\" && Number.isFinite(field) && field >= 0\n return count(record.turn) && count(record.out) && count(record.at)\n}\n\nfunction readEntries(file: string, now: number): Map<string, TurnProgress> {\n const out = new Map<string, TurnProgress>()\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_PROGRESS_MAX_AGE_MS\n const valid = Object.entries(entries as Record<string, unknown>)\n .filter((entry): entry is [string, TurnProgress] => TAG.test(entry[0]) && isProgress(entry[1]))\n .filter(([, record]) => record.at >= cutoff)\n .sort((a, b) => a[1].at - b[1].at)\n .slice(-MAX_TURN_PROGRESS)\n for (const [tag, record] of valid) out.set(tag, { turn: record.turn, out: Math.round(record.out), at: record.at })\n } catch {\n // ENOENT before the first call, 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 that the turn `tag`'s session started at `turn` has generated `out`\n * output tokens so far. Returns whether the disk was written, which is false\n * for an unusable record and a failed write alike: the caller has nothing to\n * do about either.\n */\nexport function recordTurnProgress(tag: string, progress: { turn: number; out: number }, now: number = Date.now()): boolean {\n const record = { turn: progress.turn, out: progress.out, at: now }\n if (!TAG.test(tag) || !isProgress(record)) return false\n const file = turnProgressStorePath()\n const merged = readEntries(file, now)\n merged.delete(tag)\n merged.set(tag, { ...record, out: Math.round(record.out) })\n const kept = [...merged.entries()].slice(-MAX_TURN_PROGRESS)\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 createTurnProgressReader(file: () => string = turnProgressStorePath) {\n let identity = \"\"\n let cached = new Map<string, TurnProgress>()\n return {\n read(now: number = Date.now()): Map<string, TurnProgress> {\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\nexport function _setTurnProgressStorePathForTests(path: string | null): void {\n pathOverride = path\n}\n","/**\n * The estimate behind the sidebar's progress bar (h #g247, h #g250, h #g251):\n * what a running turn is expected to take in all, and the band it is expected\n * to end in, read off this install's own record of finished turns\n * (`src/turn-history.ts`). Pure, and imported by the TUI bundle, so nothing\n * here logs or throws (h #g193).\n *\n * ## The model\n *\n * A turn's length is modelled as log-normal per agent and model: ln T is\n * normal with a mean μ and a spread σ. That is what a duration that runs from\n * thirty seconds to forty minutes under one key looks like, and it makes \"a\n * factor of two either way\" the natural unit of error. Both are the weighted\n * moments of the key's recorded turns in log space, each turn weighed by how\n * much it resembles this one:\n *\n * - a Gaussian kernel on the log of the prompt's length (`CHARS_BANDWIDTH`:\n * a prompt four times longer or shorter weighs e^-1/2, sixteen times e^-2);\n * - `OTHER_EFFORT_WEIGHT` for a turn at another effort, whose duration is\n * first scaled by the measured ratio between the two efforts' typical\n * durations once the key holds `MIN_EFFORT_RATIO_SAMPLES` turns at each,\n * because effort multiplies the thinking a turn does (h #g250);\n * - recency, halving every `RECENCY_HALF_LIFE_MS`, so a CLI update or a\n * machine change fades in days rather than the store's month.\n *\n * **Shrinkage, three levels with one rule.** A key with few turns borrows\n * from the same agent under other models: its mean and variance are pulled\n * toward that pool's with the weight of `SHRINKAGE_KAPPA` pseudo-turns, so a\n * new model under a known agent has a bar from its first turn, and a key with\n * a dozen turns of its own is nearly all its own. The pool's pull on the mean\n * is discounted by the pool's own size, κ·n/(n + κ), because a prior read off\n * one turn of another model is worth less than one read off forty: pulled\n * undiscounted, a single 200 s turn under another model put a five-turn key\n * of 40 s turns at 63 s, and the calibration carried that error on to every\n * other key's bar. With no pool the variance\n * alone is pulled toward `DEFAULT_LOG_SIGMA`, so three turns that happened to\n * agree do not claim a certainty they have not earned. Below the key, the\n * conversation: turns in one session resemble each other more than turns\n * across sessions (one session is a long refactor, another quick questions),\n * so the key's turns from THIS session are fitted on their own and shrunk\n * toward the key's fit by the same rule. One earlier turn of its own moves a\n * session's estimate a third of the way; a dozen make it nearly its own.\n * `MIN_TURN_SAMPLES` turns between the key and its pool are needed for a bar\n * at all, and a new agent waits for them: an agent is a kind of task and a\n * model a speed, and the record of a different kind of task says nothing a\n * spinner does not.\n *\n * **Conditioning.** At elapsed t the turn is known to be longer than t, so\n * every quantile is read off the distribution truncated there: with\n * F = C((ln t − μ)/σ) the share of such turns already over, the total's\n * quantile q is exp(μ + σ·C⁻¹(F + q·(1 − F))). Its median is the total\n * shown, its quarter and three-quarter points are the band. The total rises\n * as the turn outlasts more of the record instead of the bar sitting full;\n * past the key's longest recorded turn the estimate is `over`, the bar is\n * full in the warning colour and the total shown is the usual one. The\n * turn's progress so far narrows the record the same way: its tool calls\n * (h #g250), which the TUI counts off opencode's store, and the output\n * tokens its finished API calls have generated, which only the server half\n * can see and writes to a live channel as each call ends\n * (`src/turn-progress-store.ts`). The fit is redone over the recorded turns\n * that did at least as much of both, shrunk toward the unconditioned fit\n * with `CONDITIONED_KAPPA` pseudo-turns, fewer than the levels above use,\n * because that prior is known to run short for them: it holds the turns that\n * ended sooner. Output is compared only between turns at the same effort,\n * because effort is mostly how much a turn thinks, so a `low` turn's whole\n * output can be less than a `high` one's first call. A cut that would leave\n * no turn is not applied, so a turn on its fifth call on a key whose record\n * stops at four is still estimated from time rather than declared over at\n * ten seconds, which the h #g250 version did.\n *\n * **Calibration.** C is not the normal curve but the distribution of the\n * model's own standardized errors: every recorded turn estimated from the\n * rest of its key at its start (leave-one-out) gives a residual\n * (ln T − μ)/σ, and since residuals are dimensionless they pool across every\n * key, so a dozen turns anywhere in the store calibrate every bar. Each is\n * weighed by its turn's recency, as the fit weighs the turn: measured, the\n * misses on month-old turns, which the fit has moved away from, otherwise\n * pulled an estimate 15% under its own fit's median. The\n * measured distribution is smoothed with a Gaussian kernel (Silverman's\n * width), because a dozen raw residuals are a staircase with ties in it, and\n * it is blended with the normal curve, which counts as\n * `CALIBRATION_PRIOR_RESIDUALS` residuals: no threshold, the measured shape\n * takes over as the errors accrue. Whatever the log-normal gets wrong about\n * turn lengths, a bias, a spread or a skew, the band then shows what was\n * measured rather than what was assumed. The same leave-one-out estimates\n * are what `/claude-code-doctor` reports as the estimate's accuracy\n * (`assess`).\n *\n * What is NOT read: no model call, nothing from the prompt but its length,\n * and no token count from a finish, whose mid-turn usage stays zeros on\n * purpose (h #g169, h #g225): the output count comes through its own file.\n * A turn that ended on a failed tool result is skipped: its length says\n * nothing about a served turn's.\n */\nimport type { TurnHistory, TurnSample } from \"./turn-history.js\"\n\n/** Turns between a key and its agent's pool before there is an estimate at all. */\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/** Turns at each of two efforts before the ratio of their durations is measured and the other effort's scaled by it. */\nexport const MIN_EFFORT_RATIO_SAMPLES = 2\n/** A sample's weight halves every week. */\nexport const RECENCY_HALF_LIFE_MS = 7 * 24 * 60 * 60 * 1000\n/** Recorded turns a progress cut (the calls so far) must leave before it is applied. */\nexport const MIN_CONDITIONED_SAMPLES = 1\n/** The pseudo-turns a prior weighs against a key's own: with two of its own, a key is half its own. */\nexport const SHRINKAGE_KAPPA = 2\n/** The same for a progress cut's prior, the unconditioned fit, which runs short for the turns the cut keeps. */\nexport const CONDITIONED_KAPPA = 1\n/** The spread a key with no pool is pulled toward, in log space: a factor of about 2.2 either way. */\nexport const DEFAULT_LOG_SIGMA = 0.8\n/** The narrowest and widest spread a fit may claim, in log space: about ×1.2 and ×5 either way. */\nexport const MIN_LOG_SIGMA = 0.2\nexport const MAX_LOG_SIGMA = 1.6\n/** What the normal curve weighs in the calibration, in residuals: with twelve measured, the measured shape is half. */\nexport const CALIBRATION_PRIOR_RESIDUALS = 12\n/** Leave-one-out estimates per key, the newest: enough to calibrate on and to judge by. */\nexport const MAX_ASSESSED_PER_KEY = 16\n/** Residuals the calibration is fitted on at most, the newest keys' first. */\nconst MAX_CALIBRATION_RESIDUALS = 512\n/** The calibration is tabulated on this grid of standardized errors; beyond it, it is the edge's value. */\nconst GRID_LOW = -8\nconst GRID_HIGH = 8\nconst GRID_STEP = 0.04\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\nexport interface TurnEstimateBand {\n /** The total's quarter point: half the time the turn ends after this. */\n lowMs: number\n /** The total's three-quarter point: half the time the turn ends before this. */\n highMs: number\n /** Elapsed over `highMs`: the share of the bar the turn has surely covered. */\n sure: number\n /** Elapsed over `lowMs`, at most 1: the share it may have covered. */\n maybe: number\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 of its key: nothing is left to condition on. */\n over: boolean\n /** Where the turn is expected to end, around the total; absent once over. */\n band?: TurnEstimateBand\n}\n\nexport interface TurnEstimateInput {\n elapsedMs: number\n chars?: number\n effort?: string\n /** Tool calls the turn has made so far. */\n calls?: number\n /** Output tokens the turn's finished API calls have generated so far (the live channel). */\n out?: number\n /** The session's tag (`sessionTag`), so its own earlier turns weigh as the session level. */\n session?: string\n /** The clock the recency weights are read against; the model's build time by default. */\n now?: number\n}\n\n// ---------------------------------------------------------------------------\n// The normal curve\n// ---------------------------------------------------------------------------\n\n/** Φ(z), Abramowitz and Stegun 26.2.17: within 7.5e-8 everywhere. */\nexport function normalCdf(z: number): number {\n if (!Number.isFinite(z)) return z > 0 ? 1 : 0\n const x = Math.abs(z)\n const t = 1 / (1 + 0.2316419 * x)\n const poly = t * (0.31938153 + t * (-0.356563782 + t * (1.781477937 + t * (-1.821255978 + t * 1.330274429))))\n const tail = (Math.exp(-0.5 * x * x) / Math.sqrt(2 * Math.PI)) * poly\n return z >= 0 ? 1 - tail : tail\n}\n\n/** Φ⁻¹(p), Acklam's rational approximation: relative error under 1.2e-9. */\nexport function normalQuantile(p: number): number {\n const q = Math.min(1 - 1e-12, Math.max(1e-12, p))\n const a = [-3.969683028665376e1, 2.209460984245205e2, -2.759285104469687e2, 1.38357751867269e2, -3.066479806614716e1, 2.506628277459239]\n const b = [-5.447609879822406e1, 1.615858368580409e2, -1.556989798598866e2, 6.680131188771972e1, -1.328068155288572e1]\n const c = [-7.784894002430293e-3, -3.223964580411365e-1, -2.400758277161838, -2.549732539343734, 4.374664141464968, 2.938163982698783]\n const d = [7.784695709041462e-3, 3.224671290700398e-1, 2.445134137142996, 3.754408661907416]\n const low = 0.02425\n if (q < low || q > 1 - low) {\n const r = Math.sqrt(-2 * Math.log(q < low ? q : 1 - q))\n const x = (((((c[0] * r + c[1]) * r + c[2]) * r + c[3]) * r + c[4]) * r + c[5]) / ((((d[0] * r + d[1]) * r + d[2]) * r + d[3]) * r + 1)\n return q < low ? x : -x\n }\n const r = q - 0.5\n const s = r * r\n return ((((((a[0] * s + a[1]) * s + a[2]) * s + a[3]) * s + a[4]) * s + a[5]) * r) / (((((b[0] * s + b[1]) * s + b[2]) * s + b[3]) * s + b[4]) * s + 1)\n}\n\n// ---------------------------------------------------------------------------\n// Weights, and the samples as the fit sees them\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\nfunction recencyWeight(now: number, at: number): number {\n const age = Math.max(0, now - at)\n return Math.pow(0.5, age / RECENCY_HALF_LIFE_MS)\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\n/**\n * The factor each other effort's durations are scaled by before the fit: the\n * weighted median of this effort's turns over the weighted median of that\n * effort's, measured only once both groups hold `MIN_EFFORT_RATIO_SAMPLES`\n * (a ratio of one turn to one turn is noise), with the kernel and recency\n * weights on both medians so a group of long prompts is not read as a slow\n * effort. An effort with no measurable ratio is left as recorded, at\n * `OTHER_EFFORT_WEIGHT`; a sample with no effort is never scaled.\n */\nfunction effortScales(samples: readonly TurnSample[], weights: readonly number[], effort: string | undefined): Map<string, number> {\n const scales = new Map<string, number>()\n if (effort === undefined) return scales\n const groups = new Map<string, number[]>()\n samples.forEach((sample, index) => {\n if (sample.effort === undefined) return\n const group = groups.get(sample.effort) ?? []\n group.push(index)\n groups.set(sample.effort, group)\n })\n const own = groups.get(effort)\n if (!own || own.length < MIN_EFFORT_RATIO_SAMPLES) return scales\n const medianOf = (indices: readonly number[]) =>\n weightedMedian(\n indices.map((index) => samples[index].ms),\n indices.map((index) => weights[index]),\n )\n const ownMedian = medianOf(own)\n for (const [other, indices] of groups) {\n if (other === effort || indices.length < MIN_EFFORT_RATIO_SAMPLES) continue\n const otherMedian = medianOf(indices)\n if (ownMedian > 0 && otherMedian > 0) scales.set(other, ownMedian / otherMedian)\n }\n return scales\n}\n\ninterface Prepared {\n /** The duration, scaled to this turn's effort. */\n ms: number\n /** Its log, the model's unit. */\n y: number\n weight: number\n calls?: number\n out?: number\n /** Recorded at this turn's effort, or either effort unknown: its output count is comparable. */\n sameEffort: boolean\n session?: string\n}\n\n/** The samples the model reads: a usable duration, and not a turn that ended on a failure. */\nfunction usableSamples(samples: readonly TurnSample[]): TurnSample[] {\n return samples.filter((sample) => Number.isFinite(sample.ms) && sample.ms >= 0 && !sample.failed)\n}\n\nfunction prepare(samples: readonly TurnSample[], input: { chars?: number; effort?: string }, now: number): Prepared[] {\n const usable = usableSamples(samples)\n const base = usable.map((sample) => charsWeight(input.chars, sample.chars) * recencyWeight(now, sample.at))\n const scales = effortScales(usable, base, input.effort)\n return usable.map((sample, index) => {\n const ms = sample.ms * (sample.effort !== undefined ? (scales.get(sample.effort) ?? 1) : 1)\n return {\n ms,\n y: Math.log(Math.max(1, ms)),\n weight: base[index] * effortWeight(input.effort, sample.effort),\n calls: sample.calls,\n out: sample.out,\n sameEffort: input.effort === undefined || sample.effort === undefined || input.effort === sample.effort,\n session: sample.session,\n }\n })\n}\n\n// ---------------------------------------------------------------------------\n// The fit\n// ---------------------------------------------------------------------------\n\ninterface Moments {\n mean: number\n variance: number\n /** The effective count, (Σw)² / Σw²: six equal weights are six, one heavy and five light are nearer one. */\n n: number\n}\n\nfunction moments(set: readonly Prepared[]): Moments | undefined {\n let sumW = 0\n let sumW2 = 0\n let sumWY = 0\n for (const sample of set) {\n sumW += sample.weight\n sumW2 += sample.weight * sample.weight\n sumWY += sample.weight * sample.y\n }\n if (!(sumW > 0)) return undefined\n const mean = sumWY / sumW\n let sumWD = 0\n for (const sample of set) sumWD += sample.weight * (sample.y - mean) * (sample.y - mean)\n const n = (sumW * sumW) / sumW2\n const variance = n > 1 ? (sumWD / sumW) * (n / (n - 1)) : 0\n return { mean, variance, n }\n}\n\nexport interface LogNormalFit {\n mu: number\n sigma: number\n}\n\n/**\n * A key's moments pulled toward a prior: the mean toward the prior's mean\n * (its own, with no prior) with `meanKappa` pseudo-turns, the variance toward\n * the prior's variance (`DEFAULT_LOG_SIGMA` squared, with none) with `kappa`,\n * σ then clamped. No moments at all is the prior alone.\n */\nfunction shrink(\n own: Moments | undefined,\n prior: LogNormalFit | undefined,\n kappa = SHRINKAGE_KAPPA,\n meanKappa = kappa,\n): LogNormalFit | undefined {\n if (!own) return prior\n const priorMu = prior?.mu ?? own.mean\n const priorVariance = prior ? prior.sigma * prior.sigma : DEFAULT_LOG_SIGMA * DEFAULT_LOG_SIGMA\n const mu = (own.n * own.mean + meanKappa * priorMu) / (own.n + meanKappa)\n const variance = (own.n * own.variance + kappa * priorVariance) / (own.n + kappa)\n return { mu, sigma: Math.min(MAX_LOG_SIGMA, Math.max(MIN_LOG_SIGMA, Math.sqrt(Math.max(0, variance)))) }\n}\n\ninterface Pool {\n fit?: LogNormalFit\n count: number\n /** The pool's effective count, which discounts its pull on a key's mean. */\n n: number\n}\n\n/**\n * The same agent's turns under every other model, at this effort's scale and\n * weighed by recency, without the prompt-length kernel: the pool is a prior\n * worth `SHRINKAGE_KAPPA` turns, which the kernel's precision would not\n * change, and without it the pool depends on the effort alone and is built\n * once per key and effort (`buildTurnModel`) instead of once per estimate.\n */\nfunction agentPool(history: TurnHistory, key: string, effort: string | undefined, now: number): Pool {\n const slash = key.indexOf(\"/\")\n if (slash < 0) return { count: 0, n: 0 }\n const prefix = key.slice(0, slash + 1)\n const pool: Prepared[] = []\n for (const [other, samples] of history) {\n if (other === key || !other.startsWith(prefix)) continue\n pool.push(...prepare(samples, { effort }, now))\n }\n const measured = moments(pool)\n return { fit: shrink(measured, undefined), count: pool.length, n: measured?.n ?? 0 }\n}\n\ninterface KeyFit {\n /** The fit the estimate is read from, the turn's progress conditioned in. */\n fit: LogNormalFit\n /** The session's (else the key's) unconditioned fit: what an `over` turn shows as the usual. */\n usual: LogNormalFit\n /** The key's longest recorded duration at this effort; infinite with no record of its own. */\n maxMs: number\n}\n\nfunction fitKey(\n own: readonly Prepared[],\n pool: Pool,\n input: { calls?: number; out?: number; session?: string },\n): KeyFit | undefined {\n if (own.length + pool.count < MIN_TURN_SAMPLES) return undefined\n const poolPull = pool.fit ? (SHRINKAGE_KAPPA * pool.n) / (pool.n + SHRINKAGE_KAPPA) : SHRINKAGE_KAPPA\n const key = shrink(moments(own), pool.fit, SHRINKAGE_KAPPA, poolPull)\n if (!key) return undefined\n // The session level: this conversation's own turns, shrunk toward the key\n // by the same rule the key is shrunk toward its pool.\n const mine = input.session === undefined ? [] : own.filter((sample) => sample.session === input.session)\n const usual = mine.length > 0 ? (shrink(moments(mine), key) ?? key) : key\n // The turn's progress so far: a recorded turn that made fewer calls, or\n // generated less output at this effort, ended before this one could.\n const calls = input.calls !== undefined && input.calls > 0 ? input.calls : undefined\n const out = input.out !== undefined && input.out > 0 ? input.out : undefined\n let fit = usual\n if (calls !== undefined || out !== undefined) {\n const kept = own.filter(\n (sample) =>\n (calls === undefined || sample.calls === undefined || sample.calls >= calls) &&\n (out === undefined || sample.out === undefined || !sample.sameEffort || sample.out >= out),\n )\n if (kept.length < own.length && kept.length >= MIN_CONDITIONED_SAMPLES) {\n fit = shrink(moments(kept), usual, CONDITIONED_KAPPA) ?? usual\n }\n }\n const maxMs = own.length > 0 ? own.reduce((longest, sample) => Math.max(longest, sample.ms), 0) : Number.POSITIVE_INFINITY\n return { fit, usual, maxMs }\n}\n\n// ---------------------------------------------------------------------------\n// Calibration\n// ---------------------------------------------------------------------------\n\nexport interface Calibration {\n /** Residuals it was fitted on; 0 for the normal curve itself. */\n readonly count: number\n /** What the measured shape weighs against the normal curve, 0 to 1. */\n readonly weight: number\n cdf(z: number): number\n quantile(p: number): number\n}\n\nexport const NORMAL_CALIBRATION: Calibration = { count: 0, weight: 0, cdf: normalCdf, quantile: normalQuantile }\n\n/**\n * The q-quantile of weighted values sorted by value: each value sits at the\n * middle of its own weight on the cumulative scale, linear between them.\n */\nfunction weightedQuantile(sorted: ReadonlyArray<{ value: number; weight: number }>, total: number, q: number): number {\n let cumulative = 0\n let previous: { value: number; at: number } | undefined\n for (const entry of sorted) {\n const at = (cumulative + entry.weight / 2) / total\n cumulative += entry.weight\n if (at >= q) {\n if (!previous) return entry.value\n return previous.value + ((q - previous.at) / (at - previous.at)) * (entry.value - previous.value)\n }\n previous = { value: entry.value, at }\n }\n return sorted[sorted.length - 1].value\n}\n\n/**\n * The calibration from leave-one-out residuals: their distribution smoothed\n * with a Gaussian kernel of Silverman's width, 0.9 · min(sd, IQR/1.349) ·\n * n^(-1/5), blended with the normal curve weighing\n * `CALIBRATION_PRIOR_RESIDUALS`, and tabulated on a grid so a cdf or a\n * quantile is a lookup and the TUI's tick never sums the residuals. Each\n * residual may carry a weight, the recency of the turn it measured, because\n * the fit prefers recent turns and an old turn's miss says how the model did\n * on behaviour it has since moved away from; n is then the effective count.\n * Every term is an increasing curve and the normal one strictly so, which\n * makes the table strictly increasing and each lookup the other's inverse to\n * the grid's resolution. No residuals is the normal curve itself.\n */\nexport function calibrationFrom(residuals: readonly number[], weights?: readonly number[]): Calibration {\n const entries = residuals\n .map((value, index) => ({ value, weight: weights?.[index] ?? 1 }))\n .filter((entry) => Number.isFinite(entry.value) && Number.isFinite(entry.weight) && entry.weight > 0)\n .slice(-MAX_CALIBRATION_RESIDUALS)\n if (entries.length === 0) return NORMAL_CALIBRATION\n let total = 0\n let squares = 0\n let sum = 0\n for (const entry of entries) {\n total += entry.weight\n squares += entry.weight * entry.weight\n sum += entry.weight * entry.value\n }\n const n = (total * total) / squares\n const mean = sum / total\n let deviation = 0\n for (const entry of entries) deviation += entry.weight * (entry.value - mean) * (entry.value - mean)\n const sd = n > 1 ? Math.sqrt((deviation / total) * (n / (n - 1))) : 0\n const sorted = [...entries].sort((a, b) => a.value - b.value)\n const iqr = weightedQuantile(sorted, total, 0.75) - weightedQuantile(sorted, total, 0.25)\n const spreads = [sd, iqr / 1.349].filter((spread) => spread > 0)\n const spread = spreads.length > 0 ? Math.min(...spreads) : 1\n const width = Math.max(0.1, 0.9 * spread * Math.pow(n, -0.2))\n const weight = n / (n + CALIBRATION_PRIOR_RESIDUALS)\n const size = Math.round((GRID_HIGH - GRID_LOW) / GRID_STEP) + 1\n const table = new Float64Array(size)\n for (let index = 0; index < size; index += 1) {\n const z = GRID_LOW + index * GRID_STEP\n let kernel = 0\n for (const entry of entries) kernel += entry.weight * normalCdf((z - entry.value) / width)\n table[index] = weight * (kernel / total) + (1 - weight) * normalCdf(z)\n }\n return {\n count: entries.length,\n weight,\n cdf(z) {\n if (Number.isNaN(z)) return 0.5\n const position = (Math.min(GRID_HIGH, Math.max(GRID_LOW, z)) - GRID_LOW) / GRID_STEP\n const index = Math.min(size - 2, Math.floor(position))\n return table[index] + (position - index) * (table[index + 1] - table[index])\n },\n quantile(p) {\n if (!(p > table[0])) return GRID_LOW\n if (!(p < table[size - 1])) return GRID_HIGH\n // table[low] < p <= table[high], narrowed to one step.\n let low = 0\n let high = size - 1\n while (high - low > 1) {\n const mid = (low + high) >> 1\n if (table[mid] < p) low = mid\n else high = mid\n }\n const span = table[high] - table[low]\n return GRID_LOW + (low + (span > 0 ? (p - table[low]) / span : 1)) * GRID_STEP\n },\n }\n}\n\n/** The total's quantile q, given the turn has already run `truncatedAtMs`. */\nfunction quantileMs(fit: LogNormalFit, calibration: Calibration, truncatedAtMs: number, q: number): number {\n const passed = truncatedAtMs > 0 ? calibration.cdf((Math.log(truncatedAtMs) - fit.mu) / fit.sigma) : 0\n return Math.exp(fit.mu + fit.sigma * calibration.quantile(passed + q * (1 - passed)))\n}\n\n// ---------------------------------------------------------------------------\n// The model\n// ---------------------------------------------------------------------------\n\nexport interface TurnKeyAssessment {\n key: string\n /** Recorded turns the estimate reads, failed ones excluded. */\n turns: number\n failed: number\n /** The plain median of those turns. */\n typicalMs: number\n /** Turns estimated from the rest of their key at their start, the newest `MAX_ASSESSED_PER_KEY`. */\n assessed: number\n /** The median factor between the estimate and the actual, either way: ×1.6 means \"typically off by 60%\". */\n missFactor?: number\n /** The share of assessed turns that outran their estimate; one half is unbiased. */\n overShare?: number\n /** The share that ended inside the band; one half is calibrated. */\n inBandShare?: number\n}\n\nexport interface TurnEstimateAssessment {\n /** Every key in the store, most turns first. */\n keys: TurnKeyAssessment[]\n /** Leave-one-out residuals the calibration was fitted on, over every key. */\n residuals: number\n /** What their measured shape weighs against the normal curve, 0 to 1. */\n calibrationWeight: number\n}\n\nexport interface TurnModel {\n /** What a turn on `key` is expected to take, or undefined with too little record. */\n estimate(key: string, input: TurnEstimateInput): TurnEstimate | undefined\n readonly calibration: Calibration\n /** How the model has done on the turns it has, for `/claude-code-doctor`. */\n assess(): TurnEstimateAssessment\n}\n\nconst EMPTY_HISTORY: TurnHistory = new Map()\n\n/**\n * The model over a whole store, built once per change to it: the leave-one-out\n * estimates and the calibration are computed here, so an estimate is one\n * weighted fit and a few evaluations of the curve.\n */\nexport function buildTurnModel(history: TurnHistory = EMPTY_HISTORY, now: number = Date.now()): TurnModel {\n const pools = new Map<string, Pool>()\n const poolFor = (key: string, effort: string | undefined): Pool => {\n const id = `${key}\\u0000${effort ?? \"\"}`\n let pool = pools.get(id)\n if (!pool) {\n pool = agentPool(history, key, effort, now)\n pools.set(id, pool)\n }\n return pool\n }\n const assessed = new Map<string, Array<{ ms: number; fit: LogNormalFit }>>()\n const residuals: number[] = []\n const residualWeights: number[] = []\n for (const [key, samples] of history) {\n const usable = usableSamples(samples)\n if (usable.length < 2) continue\n const rows: Array<{ ms: number; fit: LogNormalFit }> = []\n for (const held of usable.slice(-MAX_ASSESSED_PER_KEY)) {\n const input = { chars: held.chars, effort: held.effort, session: held.session }\n const own = prepare(\n usable.filter((sample) => sample !== held),\n input,\n now,\n )\n const fitted = fitKey(own, poolFor(key, held.effort), input)\n if (!fitted) continue\n rows.push({ ms: held.ms, fit: fitted.fit })\n residuals.push((Math.log(Math.max(1, held.ms)) - fitted.fit.mu) / fitted.fit.sigma)\n residualWeights.push(recencyWeight(now, held.at))\n }\n if (rows.length > 0) assessed.set(key, rows)\n }\n const calibration = calibrationFrom(residuals, residualWeights)\n\n const estimate = (key: string, input: TurnEstimateInput): TurnEstimate | undefined => {\n const at = input.now ?? now\n const own = prepare(history.get(key) ?? [], input, at)\n const fitted = fitKey(own, poolFor(key, input.effort), input)\n if (!fitted) return undefined\n const elapsed = Math.max(0, Number.isFinite(input.elapsedMs) ? input.elapsedMs : 0)\n if (elapsed > fitted.maxMs) {\n return { totalMs: quantileMs(fitted.usual, calibration, 0, 0.5), fill: 1, over: true }\n }\n const totalMs = Math.max(elapsed, quantileMs(fitted.fit, calibration, elapsed, 0.5))\n const lowMs = Math.min(totalMs, Math.max(elapsed, quantileMs(fitted.fit, calibration, elapsed, 0.25)))\n const highMs = Math.max(totalMs, quantileMs(fitted.fit, calibration, elapsed, 0.75))\n return {\n totalMs,\n fill: totalMs > 0 ? elapsed / totalMs : 0,\n over: false,\n band: {\n lowMs,\n highMs,\n sure: highMs > 0 ? elapsed / highMs : 0,\n maybe: lowMs > 0 ? Math.min(1, elapsed / lowMs) : 0,\n },\n }\n }\n\n const assess = (): TurnEstimateAssessment => {\n const keys: TurnKeyAssessment[] = []\n for (const [key, samples] of history) {\n const usable = usableSamples(samples)\n const failed = samples.filter((sample) => sample.failed).length\n if (usable.length === 0 && failed === 0) continue\n const durations = usable.map((sample) => sample.ms).sort((a, b) => a - b)\n const typicalMs = durations.length > 0 ? durations[Math.floor((durations.length - 1) / 2)] : 0\n const rows = assessed.get(key) ?? []\n const misses: number[] = []\n let over = 0\n let inBand = 0\n for (const row of rows) {\n const expected = Math.exp(row.fit.mu + row.fit.sigma * calibration.quantile(0.5))\n const low = Math.exp(row.fit.mu + row.fit.sigma * calibration.quantile(0.25))\n const high = Math.exp(row.fit.mu + row.fit.sigma * calibration.quantile(0.75))\n misses.push(Math.abs(Math.log(Math.max(1, row.ms) / expected)))\n if (row.ms > expected) over += 1\n if (row.ms >= low && row.ms <= high) inBand += 1\n }\n misses.sort((a, b) => a - b)\n keys.push({\n key,\n turns: usable.length,\n failed,\n typicalMs,\n assessed: rows.length,\n ...(rows.length > 0\n ? {\n missFactor: Math.exp(misses[Math.floor((misses.length - 1) / 2)]),\n overShare: over / rows.length,\n inBandShare: inBand / rows.length,\n }\n : {}),\n })\n }\n keys.sort((a, b) => b.turns - a.turns || a.key.localeCompare(b.key))\n return { keys, residuals: residuals.length, calibrationWeight: calibration.weight }\n }\n\n return { estimate, calibration, assess }\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 alone: the model over one key, which\n * has no pool to borrow from and calibrates only on itself. The TUI builds\n * the model over the whole store instead (`buildTurnModel`).\n */\nexport function estimateTurn(samples: readonly TurnSample[], input: TurnEstimateInput): TurnEstimate | undefined {\n return buildTurnModel(new Map([[\"turn/model\", [...samples]]]), input.now ?? Date.now()).estimate(\"turn/model\", input)\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 the\n * success colour at full strength, `ok` included: the `ok` role, never `done`,\n * which is the finished row's faded success); the reset time in its own\n * (`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\" : \"ok\"\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","/**\n * The plugin's warnings, carried from its server half to its own sidebar half\n * on opencode 2, so they reach the screen there the way opencode 1's TUI shows\n * them as toasts (`src/tui-log-sink.ts`).\n *\n * opencode 2 runs a plugin's server half in a `serve --stdio` subprocess whose\n * stderr is a pipe nothing displays (measured on 2.0.22, h #g193), and its\n * plugin context has no toast route. What it does have is a channel between\n * the two halves of one plugin: the server half registers an RPC definition on\n * `ctx.rpc`, and every `events.emit` it makes travels on opencode's event bus\n * as `rpc.<id>.<event>`, where the sidebar half's `data.listen` sees it and\n * `ui.toast.show` can raise it. Measured on 2.0.31 (h #g253): every emit\n * reached the sidebar half and every toast rendered.\n *\n * Scoped to one opencode instance by construction, which a shared file under\n * `$XDG_STATE_HOME` would not be: every open opencode window would then toast\n * every other window's warnings.\n *\n * opencode 2 runs the server half's `setup` once per location, all in one\n * process (measured: the project directory and the home directory, both in\n * the TUI), so a warning is emitted on every live registration and the sidebar\n * half drops the copies by id.\n *\n * Imports nothing, and never throws or logs: the TUI bundle reads it.\n */\n\n/** The RPC definition's id; events arrive as `rpc.<id>.<event>`. */\nexport const NOTICE_RPC_ID = \"opencode-claude-code-plugin\"\n\n/** The one event the definition declares. */\nexport const NOTICE_EVENT = \"notice\"\n\n/** The bus event type the sidebar half listens for. */\nexport const NOTICE_EVENT_TYPE = `rpc.${NOTICE_RPC_ID}.${NOTICE_EVENT}`\n\n/**\n * Plain JSON Schema, which opencode's `Rpc.PortableDefinition` accepts beside\n * a Standard Schema: no schema library is needed at runtime.\n */\nexport const NOTICE_RPC_DEFINITION = {\n id: NOTICE_RPC_ID,\n methods: {},\n events: {\n [NOTICE_EVENT]: {\n schema: {\n type: \"object\",\n properties: {\n id: { type: \"string\" },\n level: { type: \"string\", enum: [\"warn\", \"error\"] },\n message: { type: \"string\" },\n },\n required: [\"id\", \"level\", \"message\"],\n additionalProperties: false,\n },\n },\n },\n} as const\n\n/** What one notice carries: a message only, never the log line's `data`. */\nexport interface PluginNotice {\n id: string\n level: \"warn\" | \"error\"\n message: string\n}\n\n/** What a toast is titled, so the operator knows who is talking. */\nexport const TOAST_TITLE = \"claude-code\"\n\n/** How long a warn/error toast stays up. Long enough to read a sentence. */\nexport const TOAST_DURATION_MS = 10_000\n\n/**\n * A toast is one line in a corner, not a log viewer. The full text (and its\n * `data`) is in the plugin log file; this is the part that has to fit on\n * screen.\n */\nexport const MAX_TOAST_CHARS = 240\n\nexport function toastMessage(message: string): string {\n const flat = message.replace(/\\s+/g, \" \").trim()\n if (flat.length <= MAX_TOAST_CHARS) return flat\n return `${flat.slice(0, MAX_TOAST_CHARS - 1)}…`\n}\n\n/** The notice for one warn/error line. `id` must be unique across processes. */\nexport function pluginNotice(id: string, level: \"warn\" | \"error\", message: string): PluginNotice {\n return { id, level, message: toastMessage(message) }\n}\n\n/** A bus event's notice, or undefined for anything that is not exactly one. */\nexport function readPluginNotice(type: unknown, data: unknown): PluginNotice | undefined {\n if (type !== NOTICE_EVENT_TYPE) return undefined\n if (!data || typeof data !== \"object\") return undefined\n const { id, level, message } = data as Record<string, unknown>\n if (typeof id !== \"string\" || id.length === 0) return undefined\n if (level !== \"warn\" && level !== \"error\") return undefined\n if (typeof message !== \"string\" || message.trim().length === 0) return undefined\n return { id, level, message: toastMessage(message) }\n}\n\n/** How many notice ids the sidebar half remembers to drop copies by. */\nexport const MAX_SEEN_NOTICES = 256\n\n/**\n * The sidebar half's side: hands each notice to `show` once, however many\n * registrations emitted it. Never throws, whatever `show` does.\n */\nexport function createNoticeToaster(show: (notice: PluginNotice) => void): (type: unknown, data: unknown) => boolean {\n const seen = new Set<string>()\n return (type, data) => {\n const notice = readPluginNotice(type, data)\n if (!notice || seen.has(notice.id)) return false\n seen.add(notice.id)\n if (seen.size > MAX_SEEN_NOTICES) {\n const oldest = seen.values().next().value\n if (oldest !== undefined) seen.delete(oldest)\n }\n try {\n show(notice)\n return true\n } catch {\n return false\n }\n }\n}\n"],"mappings":"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","names":["text","text","pad","cells","pieces","mkdirSync","readFileSync","renameSync","statSync","writeFileSync","homedir","join","mkdirSync","readFileSync","renameSync","statSync","writeFileSync","homedir","join","pathOverride","readEntries","statSync","readEntries","pathOverride","join","homedir","text","readEntries","readFileSync","statSync","readEntries","r","spread","mkdirSync","readFileSync","renameSync","statSync","writeFileSync","homedir","join","pathOverride","pathOverride","join","homedir","readFileSync","statSync","cells","out","measure","text"]}