@khalilgharbaoui/opencode-claude-code-plugin 0.54.0 → 0.54.2

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 CHANGED
@@ -164,6 +164,7 @@ function rowFor(input, child, parsed, part, completion) {
164
164
  background,
165
165
  state,
166
166
  label: subagentLabel(input.record(child.id), part),
167
+ effort: text(input.record(child.id)?.effort),
167
168
  started,
168
169
  finished
169
170
  };
@@ -191,7 +192,17 @@ function layoutRow(row, width = SIDEBAR_ROW_WIDTH, frame = 0) {
191
192
  first.push({ text: " ".repeat(Math.max(1, width - used - marker.length)), role: "muted" });
192
193
  first.push({ text: marker, role: "muted" });
193
194
  }
194
- const second = row.label ? [{ text: ` ${truncate(row.label, width - 2)}`, role: "muted" }] : [];
195
+ const second = [];
196
+ if (row.label) {
197
+ const pieces = truncate(row.label, width - 2).split(" \xB7 ");
198
+ const effortAt = row.effort ? pieces.indexOf(row.effort) : -1;
199
+ second.push({ text: " ", role: "muted" });
200
+ pieces.forEach((piece, index) => {
201
+ if (index > 0) second.push({ text: " \xB7 ", role: "muted" });
202
+ const role = index !== effortAt ? "muted" : finished ? "effort-faded" : "effort";
203
+ second.push({ text: piece, role });
204
+ });
205
+ }
195
206
  return { first, second };
196
207
  }
197
208
  function collapsedSummary(rows) {
@@ -292,6 +303,10 @@ function roleColor(colors, role) {
292
303
  return colors.done;
293
304
  case "error":
294
305
  return colors.error;
306
+ case "effort":
307
+ return colors.effort;
308
+ case "effort-faded":
309
+ return colors.effortFaded;
295
310
  case "agent":
296
311
  case "description":
297
312
  return colors.text;
@@ -354,12 +369,16 @@ function createSidebar(host) {
354
369
  insertNode(first, span);
355
370
  });
356
371
  insertNode(box, first);
357
- for (const segment of layout.second) {
358
- const line = createElement("text");
359
- insertNode(line, createTextNode(segment.text));
360
- setProp(line, "wrapMode", "none");
361
- effect(() => setProp(line, "fg", roleColor(host.colors(), segment.role)));
362
- insertNode(box, line);
372
+ if (layout.second.length > 0) {
373
+ const second = createElement("text");
374
+ setProp(second, "wrapMode", "none");
375
+ for (const segment of layout.second) {
376
+ const span = createElement("span");
377
+ insertNode(span, createTextNode(segment.text));
378
+ effect(() => setProp(span, "style", { fg: roleColor(host.colors(), segment.role) }));
379
+ insertNode(second, span);
380
+ }
381
+ insertNode(box, second);
363
382
  }
364
383
  if (row.sessionID) {
365
384
  const sessionID = row.sessionID;
@@ -507,6 +526,8 @@ function registerV1(api) {
507
526
  spinner: theme.accent,
508
527
  done: mix(theme.success, theme.textMuted, 0.45),
509
528
  error: mix(theme.error, theme.textMuted, 0.35),
529
+ effort: theme.warning ?? theme.textMuted,
530
+ effortFaded: mix(theme.warning ?? theme.textMuted, theme.textMuted, 0.45),
510
531
  hover: theme.backgroundElement
511
532
  };
512
533
  },
@@ -593,6 +614,8 @@ function setupV2(context) {
593
614
  spinner: accent,
594
615
  done: mix(theme.text.feedback.success.muted ?? theme.text.feedback.success.base, muted, 0.45),
595
616
  error: mix(theme.text.feedback.error.muted ?? theme.text.feedback.error.base, muted, 0.35),
617
+ effort: theme.text.feedback.warning?.base ?? muted,
618
+ effortFaded: mix(theme.text.feedback.warning?.base ?? muted, muted, 0.45),
596
619
  hover: theme.background.raised.base
597
620
  };
598
621
  },
package/dist/tui.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/tui.ts","../src/subagent-sidebar.ts","../src/spawn-record-store.ts"],"sourcesContent":["/**\n * The TUI half of the package: a `Subagents` section at the bottom of\n * opencode's session sidebar listing the current session's subagents, running\n * first, each with a spinner (or ✓ / ✗), its agent type, its task description,\n * a `bg` marker for background ones, and a muted line saying what it really\n * runs as (`sonnet-5.5 · high · alpha`). Clicking a row opens that subagent's\n * session; opencode's own `up` key goes back to the parent.\n *\n * Always on: there is no option. One module serves both majors (h #g234):\n *\n * - opencode 1.x loads TUI plugins from `tui.json`'s `plugin` list, never\n * from `opencode.json`'s, resolves a package to `exports[\"./tui\"]`, and\n * wants a default export `{ id, tui }` with no `server`.\n * - opencode 2.x resolves the SAME `plugins` entry it already has\n * (`<repo>/dist`) to `<dir>/tui`, and wants `{ id, setup }`. 1.x ignores a\n * `setup` key and 2.x a `tui` key, so the default export carries both.\n *\n * Everything that decides WHAT to show lives in `src/subagent-sidebar.ts`\n * (pure, tested). Each major gets a thin adapter that gathers that module's\n * inputs from its own TUI state and supplies its own theme colours; the\n * section itself is drawn by one renderer. Nothing in here may throw into the\n * host or log: on the TUI's own thread stderr is the screen (h #g193), so every\n * failure degrades to drawing less.\n */\nimport { RGBA } from \"@opentui/core\"\nimport { createElement, createTextNode, effect, insert, insertNode, setProp } from \"@opentui/solid\"\nimport { createMemo, createSignal, onCleanup } from \"solid-js\"\nimport {\n SIDEBAR_ROW_WIDTH,\n SPINNER_FRAMES,\n collapsedSummary,\n completionsIn,\n deriveSubagentRows,\n layoutRow,\n partChildID,\n type ChildRunState,\n type ChildSessionInput,\n type CompletionInput,\n type Segment,\n type SidebarInput,\n type SubagentRow,\n type TaskPartInput,\n} from \"./subagent-sidebar.js\"\nimport { createSpawnRecordReader, type SpawnRecord } from \"./spawn-record-store.js\"\n\nexport const SUBAGENTS_TUI_PLUGIN_ID = \"opencode-claude-code-plugin.subagents\"\n\n/** After opencode 1.x's own sidebar sections (files is 500). */\nconst SECTION_ORDER = 900\nconst SPINNER_INTERVAL_MS = 100\n/** How often the spawn records and the finished rows' fade are rechecked. */\nconst REFRESH_INTERVAL_MS = 1500\n\n/** The tool names a subagent dispatch has: `task` on 1.x, `subagent` on 2.x. */\nconst TASK_TOOLS = new Set([\"task\", \"subagent\"])\n\ntype Color = RGBA\n\n/** The colours the section paints with, whatever theme shape the host has. */\nexport interface SidebarColors {\n text: Color\n muted: Color\n spinner: Color\n done: Color\n error: Color\n hover: Color\n}\n\n/** What one major has to supply; the renderer below does everything else. */\ninterface SidebarHost {\n colors(): SidebarColors\n /** The inputs for one parent session, minus the clock and the records. */\n gather(parentID: string): Pick<SidebarInput, \"children\" | \"parts\" | \"completions\" | \"status\" | \"stoppedAt\">\n navigate(sessionID: string): void\n}\n\nfunction attempt<T>(fn: () => T, fallback: T): T {\n try {\n return fn()\n } catch {\n return fallback\n }\n}\n\n/**\n * `a` moved `weight` of the way to `b`, so a finished glyph reads as muted.\n * Works in whatever scale the host's RGBA uses, because it only ever hands\n * the class back values it read from the same class.\n */\nfunction mix(a: Color, b: Color, weight: number): Color {\n return attempt(\n () =>\n RGBA.fromValues(\n a.r + (b.r - a.r) * weight,\n a.g + (b.g - a.g) * weight,\n a.b + (b.b - a.b) * weight,\n a.a,\n ),\n a,\n )\n}\n\nfunction roleColor(colors: SidebarColors, role: Segment[\"role\"]): Color {\n switch (role) {\n case \"spinner\":\n return colors.spinner\n case \"done\":\n return colors.done\n case \"error\":\n return colors.error\n case \"agent\":\n case \"description\":\n return colors.text\n default:\n return colors.muted\n }\n}\n\n/**\n * The section, its clocks and the spawn-record reader, for one host. Returns\n * the slot renderer and a disposer.\n */\nfunction createSidebar(host: SidebarHost) {\n const [frame, setFrame] = createSignal(0)\n const [now, setNow] = createSignal(Date.now())\n const records = createSpawnRecordReader()\n let recordMap: Map<string, SpawnRecord> = attempt(() => records.read(), new Map())\n const [recordVersion, setRecordVersion] = createSignal(0)\n\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 let spinner: ReturnType<typeof setInterval> | undefined\n let refresh: ReturnType<typeof setInterval> | undefined\n const syncClocks = () => {\n if (sectionsRunning.size > 0 && !spinner) {\n spinner = setInterval(() => setFrame((value) => (value + 1) % SPINNER_FRAMES.length), SPINNER_INTERVAL_MS)\n } else if (sectionsRunning.size === 0 && spinner) {\n clearInterval(spinner)\n spinner = undefined\n }\n if (sectionsShowing.size > 0 && !refresh) {\n refresh = setInterval(() => {\n const next = attempt(() => records.read(), recordMap)\n if (next !== recordMap) {\n recordMap = next\n setRecordVersion((value) => value + 1)\n }\n setNow(Date.now())\n }, REFRESH_INTERVAL_MS)\n } else if (sectionsShowing.size === 0 && refresh) {\n clearInterval(refresh)\n refresh = undefined\n }\n }\n\n const deriveRows = (parentID: string): SubagentRow[] => {\n recordVersion()\n return deriveSubagentRows({\n ...host.gather(parentID),\n record: (id) => recordMap.get(id),\n now: now(),\n })\n }\n\n const buildRow = (row: SubagentRow): OpentuiNode => {\n const box = createElement(\"box\")\n const layout = layoutRow(row, SIDEBAR_ROW_WIDTH)\n const first = createElement(\"text\")\n setProp(first, \"wrapMode\", \"none\")\n layout.first.forEach((segment, index) => {\n const span = createElement(\"span\")\n if (index === 0 && row.state === \"running\") {\n insert(span, () => SPINNER_FRAMES[frame() % SPINNER_FRAMES.length])\n } else {\n insertNode(span, createTextNode(segment.text))\n }\n effect(() => setProp(span, \"style\", { fg: roleColor(host.colors(), segment.role) }))\n insertNode(first, span)\n })\n insertNode(box, first)\n for (const segment of layout.second) {\n const line = createElement(\"text\")\n insertNode(line, createTextNode(segment.text))\n setProp(line, \"wrapMode\", \"none\")\n effect(() => setProp(line, \"fg\", roleColor(host.colors(), segment.role)))\n insertNode(box, line)\n }\n if (row.sessionID) {\n const sessionID = row.sessionID\n setProp(box, \"onMouseDown\", () => attempt(() => host.navigate(sessionID), undefined))\n setProp(box, \"onMouseOver\", () =>\n attempt(() => setProp(box, \"backgroundColor\", host.colors().hover), undefined),\n )\n setProp(box, \"onMouseOut\", () =>\n attempt(() => setProp(box, \"backgroundColor\", \"transparent\"), undefined),\n )\n }\n return box\n }\n\n const buildSection = (\n list: SubagentRow[],\n expanded: () => boolean,\n setExpanded: (next: (value: boolean) => boolean) => boolean,\n ): OpentuiNode => {\n const section = createElement(\"box\")\n // Heading, styled like opencode 1.x's own MCP and Todo sections: a bold\n // title, and with more than two rows a caret that folds the list away\n // behind a one-line summary.\n const header = createElement(\"box\")\n setProp(header, \"flexDirection\", \"row\")\n setProp(header, \"gap\", 1)\n setProp(header, \"onMouseDown\", () => list.length > 2 && setExpanded((value) => !value))\n if (list.length > 2) {\n const caret = createElement(\"text\")\n insert(caret, () => (expanded() ? \"▼\" : \"▶\"))\n effect(() => setProp(caret, \"fg\", host.colors().text))\n insertNode(header, caret)\n }\n const title = createElement(\"text\")\n const bold = createElement(\"b\")\n insertNode(bold, createTextNode(\"Subagents\"))\n insertNode(title, bold)\n const summary = createElement(\"span\")\n insert(summary, () => (expanded() ? \"\" : ` (${collapsedSummary(list)})`))\n effect(() => setProp(summary, \"style\", { fg: host.colors().muted }))\n insertNode(title, summary)\n effect(() => setProp(title, \"fg\", host.colors().text))\n insertNode(header, title)\n insertNode(section, header)\n\n const body = createElement(\"box\")\n insert(body, () => (expanded() || list.length <= 2 ? list.map(buildRow) : null))\n insertNode(section, body)\n return section\n }\n\n const render = (sessionID: () => string | undefined): OpentuiNode => {\n const root = createElement(\"box\")\n const key = Symbol(\"subagents-section\")\n const [expanded, setExpanded] = createSignal(true)\n const rows = createMemo(() => {\n const parentID = sessionID()\n if (!parentID) return [] as SubagentRow[]\n return attempt(() => deriveRows(parentID), [] as SubagentRow[])\n })\n effect(() => {\n const list = rows()\n if (list.some((row) => row.state === \"running\")) sectionsRunning.add(key)\n else sectionsRunning.delete(key)\n if (list.length > 0) sectionsShowing.add(key)\n else sectionsShowing.delete(key)\n syncClocks()\n })\n onCleanup(() => {\n sectionsRunning.delete(key)\n sectionsShowing.delete(key)\n syncClocks()\n })\n insert(root, () => {\n const list = rows()\n if (list.length === 0) return null\n return attempt(() => buildSection(list, expanded, setExpanded), null)\n })\n return root\n }\n\n const dispose = () => {\n sectionsRunning.clear()\n sectionsShowing.clear()\n syncClocks()\n }\n return { render, dispose }\n}\n\n// ---------------------------------------------------------------------------\n// opencode 1.x\n// ---------------------------------------------------------------------------\n\n// The structural slice of opencode 1.x's `TuiPluginApi` this module reads.\n// Hand-written, like `src/opencode-types.ts`, so nothing is imported from\n// `@opencode-ai/plugin`.\ninterface V1SessionInfo {\n id: string\n parentID?: string\n title?: string\n agent?: string\n time?: { created?: number; updated?: number }\n}\ninterface V1Part {\n type: string\n tool?: string\n synthetic?: boolean\n text?: string\n state?: {\n status?: string\n input?: Record<string, unknown>\n metadata?: Record<string, unknown>\n title?: string\n time?: { start?: number; end?: number }\n }\n}\ninterface V1Api {\n state: {\n session: {\n get(sessionID: string): V1SessionInfo | undefined\n messages(sessionID: string): ReadonlyArray<{ id: string; role: string; time?: { created?: number } }> | undefined\n status(sessionID: string): { type?: string } | undefined\n }\n part(messageID: string): ReadonlyArray<V1Part> | undefined\n }\n theme: {\n current: { text: Color; textMuted: Color; accent: Color; success: Color; error: Color; backgroundElement: Color }\n }\n route: { navigate(name: string, params?: Record<string, unknown>): void }\n event: { on(type: string, handler: (event: { properties?: any }) => void): () => void }\n client?: { session?: { children?: (input: { sessionID: string }) => Promise<{ data?: V1SessionInfo[] }> } }\n slots: { register(plugin: { order?: number; slots: Record<string, (ctx: unknown, props: any) => OpentuiNode> }): string }\n lifecycle?: { onDispose(fn: () => void): () => void }\n}\n\nexport function registerV1(api: V1Api): void {\n // Child sessions seen per parent. 1.x has no session list in its TUI state,\n // so they come from its events, from a one-off `children` fetch the first\n // time a parent is shown (a TUI started after the dispatch still lists\n // them), and from finished parts' metadata.\n const childrenByParent = new Map<string, Map<string, ChildSessionInput>>()\n const fetched = new Set<string>()\n const stoppedAt = new Map<string, number>()\n const lastStatus = new Map<string, string>()\n const [version, setVersion] = createSignal(0)\n const bump = () => setVersion((value) => value + 1)\n\n const remember = (info: V1SessionInfo | undefined) => {\n if (!info?.id || !info.parentID) return false\n let children = childrenByParent.get(info.parentID)\n if (!children) {\n children = new Map()\n childrenByParent.set(info.parentID, children)\n }\n children.set(info.id, {\n id: info.id,\n title: info.title,\n agent: info.agent,\n created: info.time?.created,\n updated: info.time?.updated,\n })\n return true\n }\n\n const unsubscribe: Array<() => void> = []\n const listen = (type: string, handler: (properties: any) => void) => {\n const off = attempt(\n () => api.event.on(type, (event) => attempt(() => handler(event?.properties ?? {}), undefined)),\n undefined,\n )\n if (off) unsubscribe.push(off)\n }\n listen(\"session.created\", (properties) => remember(properties.info) && bump())\n listen(\"session.updated\", (properties) => remember(properties.info) && bump())\n listen(\"session.deleted\", (properties) => {\n const info = properties.info as V1SessionInfo | undefined\n if (info?.parentID && childrenByParent.get(info.parentID)?.delete(info.id)) bump()\n })\n listen(\"session.status\", (properties) => {\n const id = properties.sessionID as string | undefined\n const type = properties.status?.type as string | undefined\n if (!id || !type) return\n const previous = lastStatus.get(id)\n lastStatus.set(id, type)\n if (type === \"idle\" && previous && previous !== \"idle\") stoppedAt.set(id, Date.now())\n if (type !== \"idle\") stoppedAt.delete(id)\n })\n\n const fetchChildren = (parentID: string) => {\n if (fetched.has(parentID)) return\n fetched.add(parentID)\n const session = api.client?.session\n if (!session?.children) return\n void Promise.resolve()\n .then(() => session.children!.call(session, { sessionID: parentID }))\n .then((response) => {\n let changed = false\n for (const info of response?.data ?? []) changed = remember(info) || changed\n if (changed) bump()\n })\n .catch(() => undefined)\n }\n\n const host: SidebarHost = {\n colors() {\n const theme = api.theme.current\n return {\n text: theme.text,\n muted: theme.textMuted,\n spinner: theme.accent,\n done: mix(theme.success, theme.textMuted, 0.45),\n error: mix(theme.error, theme.textMuted, 0.35),\n hover: theme.backgroundElement,\n }\n },\n gather(parentID) {\n version()\n fetchChildren(parentID)\n const parts: TaskPartInput[] = []\n const completions: CompletionInput[] = []\n for (const message of api.state.session.messages(parentID) ?? []) {\n const messageParts = api.state.part(message.id) ?? []\n if (message.role === \"assistant\") {\n for (const part of messageParts) {\n if (part.type !== \"tool\" || !TASK_TOOLS.has(part.tool ?? \"\")) continue\n const state = part.state ?? {}\n parts.push({\n status: state.status,\n input: state.input,\n metadata: state.metadata,\n title: state.title,\n start: state.time?.start,\n end: state.time?.end,\n })\n }\n } else if (message.role === \"user\") {\n // A background child's end is a synthetic user part in the parent.\n for (const part of messageParts) {\n if (part.type !== \"text\" || part.synthetic !== true || !part.text) continue\n completions.push(...completionsIn(part.text, message.time?.created))\n }\n }\n }\n const known = new Map(childrenByParent.get(parentID) ?? [])\n for (const part of parts) {\n const id = partChildID(part)\n if (!id || known.has(id)) continue\n const info = attempt(() => api.state.session.get(id), undefined)\n known.set(id, {\n id,\n title: info?.title,\n agent: info?.agent,\n created: info?.time?.created ?? part.start,\n updated: info?.time?.updated,\n })\n }\n return {\n children: [...known.values()],\n parts,\n completions,\n status: (id: string): ChildRunState => {\n const type = attempt(() => api.state.session.status(id)?.type, undefined)\n if (type === \"busy\" || type === \"retry\") return \"busy\"\n if (type === \"idle\") return \"idle\"\n return \"unknown\"\n },\n stoppedAt: (id: string) => stoppedAt.get(id),\n }\n },\n navigate(sessionID) {\n api.route.navigate(\"session\", { sessionID })\n },\n }\n\n const sidebar = createSidebar(host)\n api.slots.register({\n order: SECTION_ORDER,\n slots: {\n sidebar_content(_ctx: unknown, props: { session_id: string }) {\n return sidebar.render(() => props.session_id)\n },\n },\n })\n api.lifecycle?.onDispose(() => {\n for (const off of unsubscribe) attempt(off, undefined)\n sidebar.dispose()\n })\n}\n\n// ---------------------------------------------------------------------------\n// opencode 2.x\n// ---------------------------------------------------------------------------\n\n// The structural slice of opencode 2.x's TUI `Context` this module reads,\n// measured on 2.0.22 (h #g234). Its sessions carry `parentID`, its status is\n// `\"running\" | \"idle\"`, an assistant message holds its tool calls inline as\n// `content[]` items named `subagent` (with `metadata.sessionID` set while the\n// call still runs, unlike 1.x), and a background child's end is a message of\n// type `synthetic` whose text is `<subagent sessionID=\"...\" state=\"...\">`.\ninterface V2Session {\n id: string\n parentID?: string\n title?: string\n agent?: string\n time?: { created?: number; updated?: number }\n}\ninterface V2Message {\n type?: string\n text?: string\n time?: { created?: number }\n content?: Array<{\n type?: string\n name?: string\n time?: { created?: number; completed?: number }\n state?: { status?: string; input?: Record<string, unknown>; metadata?: Record<string, unknown> }\n }>\n}\ntype V2Shade = Record<string, Color>\ninterface V2Context {\n data: {\n session: {\n list(): V2Session[] | undefined\n status(sessionID: string): string\n message: { list(sessionID: string): V2Message[] | undefined }\n }\n }\n theme: {\n text: { base: Color; muted: Color; feedback: { success: V2Shade; error: V2Shade } }\n background: { raised: { base: Color } }\n hue: { accent: V2Shade }\n }\n themeMode?: \"dark\" | \"light\"\n ui: {\n router: { navigate(destination: { type: \"session\"; sessionID: string }): void }\n slot(claim: { append: string; render: (input: { sessionID: string }) => OpentuiNode }): () => void\n }\n}\n\nexport function setupV2(context: V2Context): () => void {\n const host: SidebarHost = {\n colors() {\n const theme = context.theme\n const muted = theme.text.muted\n // The accent hue at the shade that reads on this mode's background.\n const accent = theme.hue.accent[context.themeMode === \"light\" ? \"500\" : \"200\"] ?? theme.text.base\n return {\n text: theme.text.base,\n muted,\n spinner: accent,\n done: mix(theme.text.feedback.success.muted ?? theme.text.feedback.success.base, muted, 0.45),\n error: mix(theme.text.feedback.error.muted ?? theme.text.feedback.error.base, muted, 0.35),\n hover: theme.background.raised.base,\n }\n },\n gather(parentID) {\n const children: ChildSessionInput[] = (context.data.session.list() ?? [])\n .filter((session) => session.parentID === parentID)\n .map((session) => ({\n id: session.id,\n title: session.title,\n agent: session.agent,\n created: session.time?.created,\n updated: session.time?.updated,\n }))\n const parts: TaskPartInput[] = []\n const completions: CompletionInput[] = []\n for (const message of context.data.session.message.list(parentID) ?? []) {\n if (message.type === \"assistant\") {\n for (const item of message.content ?? []) {\n if (item.type !== \"tool\" || !TASK_TOOLS.has(item.name ?? \"\")) continue\n parts.push({\n status: item.state?.status,\n input: item.state?.input,\n metadata: item.state?.metadata,\n start: item.time?.created,\n end: item.time?.completed,\n })\n }\n } else if (message.type === \"synthetic\" && message.text) {\n completions.push(...completionsIn(message.text, message.time?.created))\n }\n }\n return {\n children,\n parts,\n completions,\n status: (id: string): ChildRunState => {\n const status = attempt(() => context.data.session.status(id), undefined)\n if (status === \"running\") return \"busy\"\n if (status === \"idle\") return \"idle\"\n return \"unknown\"\n },\n stoppedAt: () => undefined,\n }\n },\n navigate(sessionID) {\n context.ui.router.navigate({ type: \"session\", sessionID })\n },\n }\n\n const sidebar = createSidebar(host)\n const release = attempt(\n () =>\n context.ui.slot({\n append: \"sidebar.content\",\n render: (input) => sidebar.render(() => input.sessionID),\n }),\n undefined,\n )\n return () => {\n if (release) attempt(release, undefined)\n sidebar.dispose()\n }\n}\n\nconst tui = async (api: V1Api): Promise<void> => {\n attempt(() => registerV1(api), undefined)\n}\n\nconst setup = (context: V2Context): (() => void) | void =>\n attempt(() => setupV2(context), undefined)\n\nexport default { id: SUBAGENTS_TUI_PLUGIN_ID, tui, setup }\n","/**\n * The model behind the sidebar's `Subagents` section (`src/tui.ts`): which\n * subagents of one opencode session to show, in what state, in what order, and\n * the exact text of each row.\n *\n * Pure on purpose. It imports nothing, so the TUI bundle carries no logger (a\n * logger on the TUI's main thread writes to the terminal it is drawing on,\n * h #g193) and every rule here is testable without a terminal. The TUI half\n * only gathers the inputs and paints the segments this module returns.\n *\n * Measured on opencode 1.18.35 (h #g234), and each one shapes a rule below:\n *\n * - A RUNNING `task` part carries no `state.metadata` in the TUI's store, so\n * the child session id is not on the part until it completes. Rows are\n * built from the child SESSIONS (`parentID` is the current session) and a\n * part is matched to its child by the title opencode gives the child,\n * `<description> (@<agent> subagent)`.\n * - A background dispatch's part completes at once with a `<task id\n * state=\"running\">` envelope; the child's end arrives later as a synthetic\n * user part `<task id=\"ses_...\" state=\"completed\">` in the parent (opencode\n * 2 writes `<subagent sessionID=\"...\" state=\"...\">`). That envelope is the\n * authoritative end of a background child.\n * - `metadata.model` on a finished part is what opencode ASKED for, not what\n * ran: an agent's `forceModel`, its `reasoningEffort`, the dispatch form and\n * the fallback chain all change the spawn without telling opencode. So the\n * label prefers the spawn record the server half writes\n * (`src/spawn-record-store.ts`) and falls back to the part.\n */\n\nexport type SubagentState = \"running\" | \"done\" | \"error\"\n\n/** One child session as either major reports it. */\nexport interface ChildSessionInput {\n id: string\n title?: string\n agent?: string\n created?: number\n updated?: number\n}\n\n/** A `task` / `subagent` tool part in the parent, as either major reports it. */\nexport interface TaskPartInput {\n status?: string\n input?: Record<string, unknown>\n metadata?: Record<string, unknown>\n title?: string\n start?: number\n end?: number\n}\n\n/** A background child's end, read out of the parent's synthetic user parts. */\nexport interface CompletionInput {\n sessionID: string\n state: string\n at?: number\n}\n\n/** What the server half recorded about a session's real spawn. */\nexport interface SpawnRecordInput {\n model?: string\n effort?: string\n account?: string\n at?: number\n}\n\nexport type ChildRunState = \"busy\" | \"idle\" | \"unknown\"\n\nexport interface SidebarInput {\n children: ChildSessionInput[]\n parts: TaskPartInput[]\n completions: CompletionInput[]\n status: (sessionID: string) => ChildRunState\n record: (sessionID: string) => SpawnRecordInput | undefined\n /** When the TUI first saw a child stop, by session id. */\n stoppedAt: (sessionID: string) => number | undefined\n now: number\n}\n\nexport interface SubagentRow {\n /** The child session, absent while a dispatch has not created one yet. */\n sessionID?: string\n agent: string\n description: string\n background: boolean\n state: SubagentState\n /** `sonnet-5.5 · high · alpha`, empty when nothing is known. */\n label: string\n started: number\n finished?: number\n}\n\n/** How long a finished subagent stays in the list, and how many of them. */\nexport const FINISHED_TTL_MS = 10 * 60_000\nexport const FINISHED_KEEP = 3\n/**\n * A background child that reads idle with no envelope yet is still starting\n * for this long: opencode creates the session before it marks it busy.\n */\nexport const STARTING_GRACE_MS = 10_000\n/** The usable width of a row in opencode 1.18.35's 42-column sidebar. */\nexport const SIDEBAR_ROW_WIDTH = 36\nexport const SPINNER_FRAMES = [\"⠋\", \"⠙\", \"⠹\", \"⠸\", \"⠼\", \"⠴\", \"⠦\", \"⠧\", \"⠇\", \"⠏\"]\n\nconst TITLE_SUFFIX = /\\s*\\(@([^)\\s]+) subagent\\)\\s*$/\n\n/** `Fix the form (@implementor subagent)` -> description and agent. */\nexport function parseChildTitle(title: string | undefined): { description: string; agent?: string } {\n const text = (title ?? \"\").trim()\n const match = TITLE_SUFFIX.exec(text)\n if (!match) return { description: text }\n return { description: text.slice(0, match.index).trim(), agent: match[1] }\n}\n\nfunction text(value: unknown): string | undefined {\n return typeof value === \"string\" && value.trim() ? value.trim() : undefined\n}\n\n/** The child session a part names, on either major's metadata. */\nexport function partChildID(part: TaskPartInput): string | undefined {\n const metadata = part.metadata ?? {}\n return text(metadata.sessionId) ?? text(metadata.sessionID)\n}\n\nfunction partAgent(part: TaskPartInput): string | undefined {\n const input = part.input ?? {}\n return text(input.subagent_type) ?? text(input.agent)\n}\n\nfunction partDescription(part: TaskPartInput): string | undefined {\n return text(part.input?.description) ?? text(part.title)\n}\n\nfunction partBackground(part: TaskPartInput): boolean {\n return part.input?.background === true || part.metadata?.background === true\n}\n\nconst COMPLETION_ENVELOPE =\n /<(?:task\\s+id|subagent\\s+sessionID)=\"(ses_[A-Za-z0-9]+)\"\\s+state=\"([a-z_]+)\"/g\n\n/**\n * Every background end a text names, in order. Both majors' envelopes:\n * `<task id=\"ses_...\" state=\"completed\">` (1.x) and\n * `<subagent sessionID=\"ses_...\" state=\"completed\">` (2.x). `running` is the\n * dispatch's own answer, not an end, so it is skipped.\n */\nexport function completionsIn(value: string, at?: number): CompletionInput[] {\n const found: CompletionInput[] = []\n for (const match of value.matchAll(COMPLETION_ENVELOPE)) {\n if (match[2] === \"running\") continue\n found.push({ sessionID: match[1], state: match[2], at })\n }\n return found\n}\n\nfunction endState(state: string): SubagentState {\n return state === \"completed\" ? \"done\" : \"error\"\n}\n\n/**\n * `claude-opus-5-5` -> `opus-5.5`, `claude-sonnet-4-5-20250929@alpha` ->\n * `sonnet-4.5`, `claude-opus-5-5-fast` -> `opus-5.5 fast`,\n * `claude-opus-5[1m]` -> `opus-5 1m`. Anything that is not a Claude id is\n * returned without its account marker and otherwise untouched.\n */\nexport function shortModelName(modelID: string | undefined): string | undefined {\n let id = text(modelID)\n if (!id) return undefined\n const at = id.indexOf(\"@\")\n if (at > 0) id = id.slice(0, at)\n let suffix = \"\"\n const window = /\\[(\\d+[km])\\]$/i.exec(id)\n if (window) {\n suffix += ` ${window[1].toLowerCase()}`\n id = id.slice(0, window.index)\n }\n if (id.endsWith(\"-fast\")) {\n suffix = ` fast${suffix}`\n id = id.slice(0, -\"-fast\".length)\n }\n if (!id.startsWith(\"claude-\")) return `${id}${suffix}`\n id = id.slice(\"claude-\".length).replace(/-\\d{8}$/, \"\")\n const match = /^([a-z]+)-(\\d+)(?:-(\\d+))?$/.exec(id)\n if (!match) return `${id}${suffix}`\n const version = match[3] === undefined ? match[2] : `${match[2]}.${match[3]}`\n return `${match[1]}-${version}${suffix}`\n}\n\n/**\n * The account a provider id names on a multi-account install\n * (`claude-code-alpha` -> `alpha`). A bare `claude-code` (one account) and a\n * provider that is not this plugin's name none: there is nothing to tell apart.\n */\nexport function accountFromProvider(providerID: string | undefined): string | undefined {\n const id = text(providerID)\n if (!id?.startsWith(\"claude-code-\")) return undefined\n return id.slice(\"claude-code-\".length) || undefined\n}\n\n/** `sonnet-5.5 · high · alpha`; pieces that are not known are left out. */\nexport function subagentLabel(\n record: SpawnRecordInput | undefined,\n part: TaskPartInput | undefined,\n): string {\n const asked = part?.metadata?.model as { modelID?: unknown; providerID?: unknown } | undefined\n const askedModel = typeof asked?.modelID === \"string\" ? asked.modelID : undefined\n const askedProvider = typeof asked?.providerID === \"string\" ? asked.providerID : undefined\n const model = shortModelName(record?.model ?? askedModel)\n const effort = text(record?.effort)\n const account = record?.model ? text(record.account) : accountFromProvider(askedProvider)\n return [model, effort, account].filter(Boolean).join(\" · \")\n}\n\n/**\n * Cut `value` to `width` cells with a trailing ellipsis. Counts code points,\n * which is a cell each for the text a task description holds; a wide glyph\n * can overrun by one cell and is left to the renderer's own clipping.\n */\nexport function truncate(value: string, width: number): string {\n const chars = Array.from(value.replace(/\\s+/g, \" \").trim())\n if (width <= 0) return \"\"\n if (chars.length <= width) return chars.join(\"\")\n if (width === 1) return \"…\"\n return `${chars.slice(0, width - 1).join(\"\").trimEnd()}…`\n}\n\n/**\n * The rows for one session: running first (oldest first), then finished\n * (newest first), at most `FINISHED_KEEP` of those and none older than\n * `FINISHED_TTL_MS`. Empty when nothing is worth showing, which is what makes\n * the section disappear.\n */\nexport function deriveSubagentRows(input: SidebarInput): SubagentRow[] {\n const completions = new Map<string, CompletionInput>()\n for (const completion of input.completions) completions.set(completion.sessionID, completion)\n\n const byChild = new Map<string, TaskPartInput>()\n const unmatched: TaskPartInput[] = []\n for (const part of input.parts) {\n const child = partChildID(part)\n if (child) byChild.set(child, part)\n else unmatched.push(part)\n }\n\n const rows: SubagentRow[] = []\n const seen = new Set<string>()\n for (const child of input.children) {\n if (seen.has(child.id)) continue\n seen.add(child.id)\n const parsed = parseChildTitle(child.title)\n let part = byChild.get(child.id)\n if (!part) {\n // A running part has no child id yet: match it on the title opencode\n // gave the child, agent and description both.\n const index = unmatched.findIndex(\n (candidate) =>\n partDescription(candidate) === parsed.description &&\n (parsed.agent === undefined || partAgent(candidate) === parsed.agent),\n )\n if (index >= 0) part = unmatched.splice(index, 1)[0]\n }\n rows.push(rowFor(input, child, parsed, part, completions.get(child.id)))\n }\n // A dispatch opencode has not created a session for yet: shown, not clickable.\n for (const part of unmatched) {\n if (part.status !== \"pending\" && part.status !== \"running\") continue\n const description = partDescription(part)\n const agent = partAgent(part)\n if (!description && !agent) continue\n rows.push({\n agent: agent ?? \"subagent\",\n description: description ?? \"\",\n background: partBackground(part),\n state: \"running\",\n label: subagentLabel(undefined, part),\n started: part.start ?? input.now,\n })\n }\n\n const running = rows\n .filter((row) => row.state === \"running\")\n .sort((a, b) => a.started - b.started)\n const finished = rows\n .filter((row) => row.state !== \"running\")\n .filter((row) => input.now - (row.finished ?? row.started) <= FINISHED_TTL_MS)\n .sort((a, b) => (b.finished ?? b.started) - (a.finished ?? a.started))\n .slice(0, FINISHED_KEEP)\n return [...running, ...finished]\n}\n\nfunction rowFor(\n input: SidebarInput,\n child: ChildSessionInput,\n parsed: { description: string; agent?: string },\n part: TaskPartInput | undefined,\n completion: CompletionInput | undefined,\n): SubagentRow {\n const background = part ? partBackground(part) : false\n const started = part?.start ?? child.created ?? input.now\n const status = input.status(child.id)\n let state: SubagentState\n let finished: number | undefined\n if (part && !background && part.status !== \"pending\" && part.status !== \"running\") {\n state = part.status === \"completed\" ? \"done\" : \"error\"\n finished = part.end\n } else if (completion) {\n state = endState(completion.state)\n finished = completion.at\n } else if (status === \"busy\") {\n state = \"running\"\n } else if (part && !background) {\n // pending or running: the parent is still waiting on this child.\n state = \"running\"\n } else if (status === \"idle\" && input.now - started < STARTING_GRACE_MS) {\n state = \"running\"\n } else if (status === \"unknown\" && background) {\n // Nothing says it stopped: a background child keeps its spinner until\n // its envelope arrives or opencode reports it idle.\n state = \"running\"\n } else {\n state = \"done\"\n }\n if (state !== \"running\" && finished === undefined) {\n finished = input.stoppedAt(child.id) ?? child.updated ?? started\n }\n return {\n sessionID: child.id,\n agent: parsed.agent ?? (part ? partAgent(part) : undefined) ?? text(child.agent) ?? \"subagent\",\n description: (part ? partDescription(part) : undefined) ?? parsed.description,\n background,\n state,\n label: subagentLabel(input.record(child.id), part),\n started,\n finished,\n }\n}\n\n/** A run of text with the role the TUI maps onto a theme colour. */\nexport interface Segment {\n text: string\n role: \"spinner\" | \"done\" | \"error\" | \"agent\" | \"description\" | \"muted\"\n}\n\n/**\n * The two lines of one row, laid out for `width` cells:\n *\n * ⠋ implementor Fix the dispatch f… bg\n * sonnet-5.5 · high · alpha\n *\n * The `bg` marker is right-aligned, the description takes what is left, and a\n * finished row is drawn in the muted role throughout apart from its glyph.\n */\nexport function layoutRow(\n row: SubagentRow,\n width: number = SIDEBAR_ROW_WIDTH,\n frame = 0,\n): { first: Segment[]; second: Segment[] } {\n const finished = row.state !== \"running\"\n const glyph =\n row.state === \"running\"\n ? SPINNER_FRAMES[frame % SPINNER_FRAMES.length]\n : row.state === \"done\"\n ? \"✓\"\n : \"✗\"\n const marker = row.background ? \"bg\" : \"\"\n const reserved = marker ? marker.length + 1 : 0\n const agentWidth = Math.max(1, Math.min(Array.from(row.agent).length, width - 2 - reserved - 6))\n const agent = truncate(row.agent, agentWidth)\n const room = width - 2 - Array.from(agent).length - 1 - reserved\n const description = room > 0 ? truncate(row.description, room) : \"\"\n const used = 2 + Array.from(agent).length + (description ? 1 + Array.from(description).length : 0)\n const first: Segment[] = [\n { text: glyph, role: row.state === \"running\" ? \"spinner\" : row.state === \"done\" ? \"done\" : \"error\" },\n { text: \" \", role: \"muted\" },\n { text: agent, role: finished ? \"muted\" : \"agent\" },\n ]\n if (description) {\n first.push({ text: \" \", role: \"muted\" })\n first.push({ text: description, role: finished ? \"muted\" : \"description\" })\n }\n if (marker) {\n first.push({ text: \" \".repeat(Math.max(1, width - used - marker.length)), role: \"muted\" })\n first.push({ text: marker, role: \"muted\" })\n }\n const second: Segment[] = row.label\n ? [{ text: ` ${truncate(row.label, width - 2)}`, role: \"muted\" }]\n : []\n return { first, second }\n}\n\n/** The collapsed heading's count: `2 running` or `3 done`. */\nexport function collapsedSummary(rows: SubagentRow[]): string {\n const running = rows.filter((row) => row.state === \"running\").length\n return running > 0 ? `${running} running` : `${rows.length} done`\n}\n","/**\n * What each opencode session's `claude` process REALLY spawned as: model,\n * effort and account. Written by the server half on every turn that reaches\n * the CLI, read by the TUI half's `Subagents` sidebar section (`src/tui.ts`).\n *\n * ## Why a file, and why the TUI cannot read this anywhere else\n *\n * Measured on opencode 1.18.35 (h #g234): an `implementor` subagent whose\n * agent file says `forceModel: claude-sonnet-5-5` and `reasoningEffort: high`\n * spawned as exactly that, while the finished `task` part's `metadata.model`\n * said `claude-code-default/claude-opus-5-5` and no message carried an effort\n * at all. Everything that changes a spawn on the plugin side (an agent's\n * `forceModel` and `reasoningEffort`, `defaultSubagentModel`, the dispatch form\n * of h #g227, the fallback chain of h #g162, an account failover) is invisible\n * to opencode by construction, so showing opencode's own view would be showing\n * the wrong model exactly when the operator configured it not to be. The two\n * halves do not share memory on either major (1.x runs the server half in a\n * worker thread, 2.x in a subprocess), so the record goes through the state\n * directory the resume and dispatch stores already use.\n *\n * ## Shape, and what is never written\n *\n * ```json\n * { \"version\": 1, \"sessions\": {\n * \"ses_abc\": { \"model\": \"claude-sonnet-5-5\", \"effort\": \"high\", \"account\": \"alpha\", \"at\": 1760000000000 } } }\n * ```\n *\n * A model id, an effort level and an account NAME: the same three strings the\n * dispatch store keeps (h #g228), none secret. No prompt, no path, no session\n * key. `account` is written only on a multi-account install, where it tells\n * two configured accounts apart; on a single-account install there is nothing\n * to tell apart and it stays out.\n *\n * ## Cost and failure\n *\n * One write per session per process, and another only when what it spawns as\n * changes: `recordSessionSpawn` compares against what this process last wrote\n * and returns before touching the disk otherwise. Same discipline as the\n * resume store (h #g197): merge-on-write so two opencode processes do not erase\n * each other, atomic rename, `0600` in the `0700` directory, bounded at\n * `MAX_SPAWN_RECORDS` newest sessions and `SPAWN_RECORD_MAX_AGE_MS`. A missing,\n * unreadable or half-written file is an empty store, which costs the sidebar\n * its effort and falls back to the model opencode asked for. Two writers racing\n * can drop one entry; the next turn of that session writes it again, and the\n * only thing it ever costs is a label.\n *\n * Imports nothing that logs, because the TUI bundle reads it on the terminal's\n * own thread (h #g193): every function here is silent and never throws.\n */\nimport { mkdirSync, readFileSync, renameSync, statSync, writeFileSync } from \"node:fs\"\nimport { homedir } from \"node:os\"\nimport { join } from \"node:path\"\n\nexport interface SpawnRecord {\n model?: string\n effort?: string\n account?: string\n at: number\n}\n\n/** Sessions worth remembering at once. A subagent fan-out names a handful. */\nexport const MAX_SPAWN_RECORDS = 256\n\n/** A session not spawned this long ago is not one the sidebar is showing. */\nexport const SPAWN_RECORD_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000\n\nlet pathOverride: string | null = null\nconst lastWritten = new Map<string, string>()\n\nexport function spawnRecordStorePath(): string {\n if (pathOverride) return pathOverride\n const stateRoot = process.env.XDG_STATE_HOME || join(homedir(), \".local\", \"state\")\n return join(stateRoot, \"opencode-claude-code-plugin\", \"session-spawns.json\")\n}\n\nfunction isRecord(value: unknown): value is SpawnRecord {\n const record = value as SpawnRecord | null\n if (!record || typeof record !== \"object\" || Array.isArray(record)) return false\n if (typeof record.at !== \"number\" || !Number.isFinite(record.at)) return false\n const ok = (field: unknown) => field === undefined || typeof field === \"string\"\n if (!ok(record.model) || !ok(record.effort) || !ok(record.account)) return false\n return !!record.model\n}\n\nfunction readEntries(file: string): Map<string, SpawnRecord> {\n const out = new Map<string, SpawnRecord>()\n try {\n const parsed = JSON.parse(readFileSync(file, \"utf8\"))\n const entries = parsed && typeof parsed === \"object\" ? parsed.sessions : null\n if (!entries || typeof entries !== \"object\") return out\n const cutoff = Date.now() - SPAWN_RECORD_MAX_AGE_MS\n const valid = Object.entries(entries as Record<string, unknown>)\n .filter((entry): entry is [string, SpawnRecord] => isRecord(entry[1]))\n .filter(([, record]) => record.at >= cutoff)\n .sort((a, b) => a[1].at - b[1].at)\n for (const [key, record] of valid) {\n out.set(key, {\n model: record.model,\n ...(record.effort ? { effort: record.effort } : {}),\n ...(record.account ? { account: record.account } : {}),\n at: record.at,\n })\n }\n } catch {\n // ENOENT before the first spawn, or a file another process is renaming\n // into place: the empty store is the answer.\n }\n return out\n}\n\n/**\n * Record what `sessionID` spawned as. Returns whether the disk was written,\n * which is false for an unchanged record, an unusable id and a failed write\n * alike: the caller has nothing to do about any of them.\n */\nexport function recordSessionSpawn(\n sessionID: string,\n spawn: { model: string; effort?: string; account?: string },\n now: number = Date.now(),\n): boolean {\n if (!sessionID || sessionID === \"default\" || !spawn.model) return false\n const record: SpawnRecord = {\n model: spawn.model,\n ...(spawn.effort ? { effort: spawn.effort } : {}),\n ...(spawn.account ? { account: spawn.account } : {}),\n at: now,\n }\n const fingerprint = `${record.model}\\n${record.effort ?? \"\"}\\n${record.account ?? \"\"}`\n if (lastWritten.get(sessionID) === fingerprint) return false\n const file = spawnRecordStorePath()\n const merged = readEntries(file)\n merged.delete(sessionID)\n merged.set(sessionID, record)\n const cutoff = now - SPAWN_RECORD_MAX_AGE_MS\n const kept = [...merged.entries()]\n .filter(([, entry]) => entry.at >= cutoff)\n .sort((a, b) => a[1].at - b[1].at)\n .slice(-MAX_SPAWN_RECORDS)\n try {\n mkdirSync(join(file, \"..\"), { recursive: true, mode: 0o700 })\n const tmp = `${file}.${process.pid}.tmp`\n writeFileSync(tmp, JSON.stringify({ version: 1, sessions: Object.fromEntries(kept) }), {\n mode: 0o600,\n })\n renameSync(tmp, file)\n } catch {\n return false\n }\n if (lastWritten.size >= MAX_SPAWN_RECORDS) lastWritten.clear()\n lastWritten.set(sessionID, fingerprint)\n return true\n}\n\n/**\n * A reader that re-parses the file only when it changed. The TUI calls `read()`\n * on every refresh; an unchanged file costs one `stat`.\n */\nexport function createSpawnRecordReader(file: () => string = spawnRecordStorePath) {\n let identity = \"\"\n let cached = new Map<string, SpawnRecord>()\n return {\n read(): Map<string, SpawnRecord> {\n const path = file()\n let next = \"\"\n try {\n const stats = statSync(path)\n next = `${stats.mtimeMs}:${stats.size}:${stats.ino}`\n } catch {\n next = \"missing\"\n }\n if (next !== identity) {\n identity = next\n cached = next === \"missing\" ? new Map() : readEntries(path)\n }\n return cached\n },\n }\n}\n\nexport function _setSpawnRecordStorePathForTests(path: string | null): void {\n pathOverride = path\n lastWritten.clear()\n}\n"],"mappings":"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","names":["text"]}
1
+ {"version":3,"sources":["../src/tui.ts","../src/subagent-sidebar.ts","../src/spawn-record-store.ts"],"sourcesContent":["/**\n * The TUI half of the package: a `Subagents` section at the bottom of\n * opencode's session sidebar listing the current session's subagents, running\n * first, each with a spinner (or ✓ / ✗), its agent type, its task description,\n * a `bg` marker for background ones, and a muted line saying what it really\n * runs as (`sonnet-5.5 · high · alpha`). Clicking a row opens that subagent's\n * session; opencode's own `up` key goes back to the parent.\n *\n * Always on: there is no option. One module serves both majors (h #g234):\n *\n * - opencode 1.x loads TUI plugins from `tui.json`'s `plugin` list, never\n * from `opencode.json`'s, resolves a package to `exports[\"./tui\"]`, and\n * wants a default export `{ id, tui }` with no `server`.\n * - opencode 2.x resolves the SAME `plugins` entry it already has\n * (`<repo>/dist`) to `<dir>/tui`, and wants `{ id, setup }`. 1.x ignores a\n * `setup` key and 2.x a `tui` key, so the default export carries both.\n *\n * Everything that decides WHAT to show lives in `src/subagent-sidebar.ts`\n * (pure, tested). Each major gets a thin adapter that gathers that module's\n * inputs from its own TUI state and supplies its own theme colours; the\n * section itself is drawn by one renderer. Nothing in here may throw into the\n * host or log: on the TUI's own thread stderr is the screen (h #g193), so every\n * failure degrades to drawing less.\n */\nimport { RGBA } from \"@opentui/core\"\nimport { createElement, createTextNode, effect, insert, insertNode, setProp } from \"@opentui/solid\"\nimport { createMemo, createSignal, onCleanup } from \"solid-js\"\nimport {\n SIDEBAR_ROW_WIDTH,\n SPINNER_FRAMES,\n collapsedSummary,\n completionsIn,\n deriveSubagentRows,\n layoutRow,\n partChildID,\n type ChildRunState,\n type ChildSessionInput,\n type CompletionInput,\n type Segment,\n type SidebarInput,\n type SubagentRow,\n type TaskPartInput,\n} from \"./subagent-sidebar.js\"\nimport { createSpawnRecordReader, type SpawnRecord } from \"./spawn-record-store.js\"\n\nexport const SUBAGENTS_TUI_PLUGIN_ID = \"opencode-claude-code-plugin.subagents\"\n\n/** After opencode 1.x's own sidebar sections (files is 500). */\nconst SECTION_ORDER = 900\nconst SPINNER_INTERVAL_MS = 100\n/** How often the spawn records and the finished rows' fade are rechecked. */\nconst REFRESH_INTERVAL_MS = 1500\n\n/** The tool names a subagent dispatch has: `task` on 1.x, `subagent` on 2.x. */\nconst TASK_TOOLS = new Set([\"task\", \"subagent\"])\n\ntype Color = RGBA\n\n/** The colours the section paints with, whatever theme shape the host has. */\nexport interface SidebarColors {\n text: Color\n muted: Color\n spinner: Color\n done: Color\n error: Color\n /** The effort on a row's second line: the theme's warning colour. */\n effort: Color\n effortFaded: Color\n hover: Color\n}\n\n/** What one major has to supply; the renderer below does everything else. */\ninterface SidebarHost {\n colors(): SidebarColors\n /** The inputs for one parent session, minus the clock and the records. */\n gather(parentID: string): Pick<SidebarInput, \"children\" | \"parts\" | \"completions\" | \"status\" | \"stoppedAt\">\n navigate(sessionID: string): void\n}\n\nfunction attempt<T>(fn: () => T, fallback: T): T {\n try {\n return fn()\n } catch {\n return fallback\n }\n}\n\n/**\n * `a` moved `weight` of the way to `b`, so a finished glyph reads as muted.\n * Works in whatever scale the host's RGBA uses, because it only ever hands\n * the class back values it read from the same class.\n */\nfunction mix(a: Color, b: Color, weight: number): Color {\n return attempt(\n () =>\n RGBA.fromValues(\n a.r + (b.r - a.r) * weight,\n a.g + (b.g - a.g) * weight,\n a.b + (b.b - a.b) * weight,\n a.a,\n ),\n a,\n )\n}\n\nfunction roleColor(colors: SidebarColors, role: Segment[\"role\"]): Color {\n switch (role) {\n case \"spinner\":\n return colors.spinner\n case \"done\":\n return colors.done\n case \"error\":\n return colors.error\n case \"effort\":\n return colors.effort\n case \"effort-faded\":\n return colors.effortFaded\n case \"agent\":\n case \"description\":\n return colors.text\n default:\n return colors.muted\n }\n}\n\n/**\n * The section, its clocks and the spawn-record reader, for one host. Returns\n * the slot renderer and a disposer.\n */\nfunction createSidebar(host: SidebarHost) {\n const [frame, setFrame] = createSignal(0)\n const [now, setNow] = createSignal(Date.now())\n const records = createSpawnRecordReader()\n let recordMap: Map<string, SpawnRecord> = attempt(() => records.read(), new Map())\n const [recordVersion, setRecordVersion] = createSignal(0)\n\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 let spinner: ReturnType<typeof setInterval> | undefined\n let refresh: ReturnType<typeof setInterval> | undefined\n const syncClocks = () => {\n if (sectionsRunning.size > 0 && !spinner) {\n spinner = setInterval(() => setFrame((value) => (value + 1) % SPINNER_FRAMES.length), SPINNER_INTERVAL_MS)\n } else if (sectionsRunning.size === 0 && spinner) {\n clearInterval(spinner)\n spinner = undefined\n }\n if (sectionsShowing.size > 0 && !refresh) {\n refresh = setInterval(() => {\n const next = attempt(() => records.read(), recordMap)\n if (next !== recordMap) {\n recordMap = next\n setRecordVersion((value) => value + 1)\n }\n setNow(Date.now())\n }, REFRESH_INTERVAL_MS)\n } else if (sectionsShowing.size === 0 && refresh) {\n clearInterval(refresh)\n refresh = undefined\n }\n }\n\n const deriveRows = (parentID: string): SubagentRow[] => {\n recordVersion()\n return deriveSubagentRows({\n ...host.gather(parentID),\n record: (id) => recordMap.get(id),\n now: now(),\n })\n }\n\n const buildRow = (row: SubagentRow): OpentuiNode => {\n const box = createElement(\"box\")\n const layout = layoutRow(row, SIDEBAR_ROW_WIDTH)\n const first = createElement(\"text\")\n setProp(first, \"wrapMode\", \"none\")\n layout.first.forEach((segment, index) => {\n const span = createElement(\"span\")\n if (index === 0 && row.state === \"running\") {\n insert(span, () => SPINNER_FRAMES[frame() % SPINNER_FRAMES.length])\n } else {\n insertNode(span, createTextNode(segment.text))\n }\n effect(() => setProp(span, \"style\", { fg: roleColor(host.colors(), segment.role) }))\n insertNode(first, span)\n })\n insertNode(box, first)\n if (layout.second.length > 0) {\n // One line of spans, like the first, so the effort can take its own colour.\n const second = createElement(\"text\")\n setProp(second, \"wrapMode\", \"none\")\n for (const segment of layout.second) {\n const span = createElement(\"span\")\n insertNode(span, createTextNode(segment.text))\n effect(() => setProp(span, \"style\", { fg: roleColor(host.colors(), segment.role) }))\n insertNode(second, span)\n }\n insertNode(box, second)\n }\n if (row.sessionID) {\n const sessionID = row.sessionID\n setProp(box, \"onMouseDown\", () => attempt(() => host.navigate(sessionID), undefined))\n setProp(box, \"onMouseOver\", () =>\n attempt(() => setProp(box, \"backgroundColor\", host.colors().hover), undefined),\n )\n setProp(box, \"onMouseOut\", () =>\n attempt(() => setProp(box, \"backgroundColor\", \"transparent\"), undefined),\n )\n }\n return box\n }\n\n const buildSection = (\n list: SubagentRow[],\n expanded: () => boolean,\n setExpanded: (next: (value: boolean) => boolean) => boolean,\n ): OpentuiNode => {\n const section = createElement(\"box\")\n // Heading, styled like opencode 1.x's own MCP and Todo sections: a bold\n // title, and with more than two rows a caret that folds the list away\n // behind a one-line summary.\n const header = createElement(\"box\")\n setProp(header, \"flexDirection\", \"row\")\n setProp(header, \"gap\", 1)\n setProp(header, \"onMouseDown\", () => list.length > 2 && setExpanded((value) => !value))\n if (list.length > 2) {\n const caret = createElement(\"text\")\n insert(caret, () => (expanded() ? \"▼\" : \"▶\"))\n effect(() => setProp(caret, \"fg\", host.colors().text))\n insertNode(header, caret)\n }\n const title = createElement(\"text\")\n const bold = createElement(\"b\")\n insertNode(bold, createTextNode(\"Subagents\"))\n insertNode(title, bold)\n const summary = createElement(\"span\")\n insert(summary, () => (expanded() ? \"\" : ` (${collapsedSummary(list)})`))\n effect(() => setProp(summary, \"style\", { fg: host.colors().muted }))\n insertNode(title, summary)\n effect(() => setProp(title, \"fg\", host.colors().text))\n insertNode(header, title)\n insertNode(section, header)\n\n const body = createElement(\"box\")\n insert(body, () => (expanded() || list.length <= 2 ? list.map(buildRow) : null))\n insertNode(section, body)\n return section\n }\n\n const render = (sessionID: () => string | undefined): OpentuiNode => {\n const root = createElement(\"box\")\n const key = Symbol(\"subagents-section\")\n const [expanded, setExpanded] = createSignal(true)\n const rows = createMemo(() => {\n const parentID = sessionID()\n if (!parentID) return [] as SubagentRow[]\n return attempt(() => deriveRows(parentID), [] as SubagentRow[])\n })\n effect(() => {\n const list = rows()\n if (list.some((row) => row.state === \"running\")) sectionsRunning.add(key)\n else sectionsRunning.delete(key)\n if (list.length > 0) sectionsShowing.add(key)\n else sectionsShowing.delete(key)\n syncClocks()\n })\n onCleanup(() => {\n sectionsRunning.delete(key)\n sectionsShowing.delete(key)\n syncClocks()\n })\n insert(root, () => {\n const list = rows()\n if (list.length === 0) return null\n return attempt(() => buildSection(list, expanded, setExpanded), null)\n })\n return root\n }\n\n const dispose = () => {\n sectionsRunning.clear()\n sectionsShowing.clear()\n syncClocks()\n }\n return { render, dispose }\n}\n\n// ---------------------------------------------------------------------------\n// opencode 1.x\n// ---------------------------------------------------------------------------\n\n// The structural slice of opencode 1.x's `TuiPluginApi` this module reads.\n// Hand-written, like `src/opencode-types.ts`, so nothing is imported from\n// `@opencode-ai/plugin`.\ninterface V1SessionInfo {\n id: string\n parentID?: string\n title?: string\n agent?: string\n time?: { created?: number; updated?: number }\n}\ninterface V1Part {\n type: string\n tool?: string\n synthetic?: boolean\n text?: string\n state?: {\n status?: string\n input?: Record<string, unknown>\n metadata?: Record<string, unknown>\n title?: string\n time?: { start?: number; end?: number }\n }\n}\ninterface V1Api {\n state: {\n session: {\n get(sessionID: string): V1SessionInfo | undefined\n messages(sessionID: string): ReadonlyArray<{ id: string; role: string; time?: { created?: number } }> | undefined\n status(sessionID: string): { type?: string } | undefined\n }\n part(messageID: string): ReadonlyArray<V1Part> | undefined\n }\n theme: {\n current: {\n text: Color\n textMuted: Color\n accent: Color\n success: Color\n warning: Color\n error: Color\n backgroundElement: Color\n }\n }\n route: { navigate(name: string, params?: Record<string, unknown>): void }\n event: { on(type: string, handler: (event: { properties?: any }) => void): () => void }\n client?: { session?: { children?: (input: { sessionID: string }) => Promise<{ data?: V1SessionInfo[] }> } }\n slots: { register(plugin: { order?: number; slots: Record<string, (ctx: unknown, props: any) => OpentuiNode> }): string }\n lifecycle?: { onDispose(fn: () => void): () => void }\n}\n\nexport function registerV1(api: V1Api): void {\n // Child sessions seen per parent. 1.x has no session list in its TUI state,\n // so they come from its events, from a one-off `children` fetch the first\n // time a parent is shown (a TUI started after the dispatch still lists\n // them), and from finished parts' metadata.\n const childrenByParent = new Map<string, Map<string, ChildSessionInput>>()\n const fetched = new Set<string>()\n const stoppedAt = new Map<string, number>()\n const lastStatus = new Map<string, string>()\n const [version, setVersion] = createSignal(0)\n const bump = () => setVersion((value) => value + 1)\n\n const remember = (info: V1SessionInfo | undefined) => {\n if (!info?.id || !info.parentID) return false\n let children = childrenByParent.get(info.parentID)\n if (!children) {\n children = new Map()\n childrenByParent.set(info.parentID, children)\n }\n children.set(info.id, {\n id: info.id,\n title: info.title,\n agent: info.agent,\n created: info.time?.created,\n updated: info.time?.updated,\n })\n return true\n }\n\n const unsubscribe: Array<() => void> = []\n const listen = (type: string, handler: (properties: any) => void) => {\n const off = attempt(\n () => api.event.on(type, (event) => attempt(() => handler(event?.properties ?? {}), undefined)),\n undefined,\n )\n if (off) unsubscribe.push(off)\n }\n listen(\"session.created\", (properties) => remember(properties.info) && bump())\n listen(\"session.updated\", (properties) => remember(properties.info) && bump())\n listen(\"session.deleted\", (properties) => {\n const info = properties.info as V1SessionInfo | undefined\n if (info?.parentID && childrenByParent.get(info.parentID)?.delete(info.id)) bump()\n })\n listen(\"session.status\", (properties) => {\n const id = properties.sessionID as string | undefined\n const type = properties.status?.type as string | undefined\n if (!id || !type) return\n const previous = lastStatus.get(id)\n lastStatus.set(id, type)\n if (type === \"idle\" && previous && previous !== \"idle\") stoppedAt.set(id, Date.now())\n if (type !== \"idle\") stoppedAt.delete(id)\n })\n\n const fetchChildren = (parentID: string) => {\n if (fetched.has(parentID)) return\n fetched.add(parentID)\n const session = api.client?.session\n if (!session?.children) return\n void Promise.resolve()\n .then(() => session.children!.call(session, { sessionID: parentID }))\n .then((response) => {\n let changed = false\n for (const info of response?.data ?? []) changed = remember(info) || changed\n if (changed) bump()\n })\n .catch(() => undefined)\n }\n\n const host: SidebarHost = {\n colors() {\n const theme = api.theme.current\n return {\n text: theme.text,\n muted: theme.textMuted,\n spinner: theme.accent,\n done: mix(theme.success, theme.textMuted, 0.45),\n error: mix(theme.error, theme.textMuted, 0.35),\n effort: theme.warning ?? theme.textMuted,\n effortFaded: mix(theme.warning ?? theme.textMuted, theme.textMuted, 0.45),\n hover: theme.backgroundElement,\n }\n },\n gather(parentID) {\n version()\n fetchChildren(parentID)\n const parts: TaskPartInput[] = []\n const completions: CompletionInput[] = []\n for (const message of api.state.session.messages(parentID) ?? []) {\n const messageParts = api.state.part(message.id) ?? []\n if (message.role === \"assistant\") {\n for (const part of messageParts) {\n if (part.type !== \"tool\" || !TASK_TOOLS.has(part.tool ?? \"\")) continue\n const state = part.state ?? {}\n parts.push({\n status: state.status,\n input: state.input,\n metadata: state.metadata,\n title: state.title,\n start: state.time?.start,\n end: state.time?.end,\n })\n }\n } else if (message.role === \"user\") {\n // A background child's end is a synthetic user part in the parent.\n for (const part of messageParts) {\n if (part.type !== \"text\" || part.synthetic !== true || !part.text) continue\n completions.push(...completionsIn(part.text, message.time?.created))\n }\n }\n }\n const known = new Map(childrenByParent.get(parentID) ?? [])\n for (const part of parts) {\n const id = partChildID(part)\n if (!id || known.has(id)) continue\n const info = attempt(() => api.state.session.get(id), undefined)\n known.set(id, {\n id,\n title: info?.title,\n agent: info?.agent,\n created: info?.time?.created ?? part.start,\n updated: info?.time?.updated,\n })\n }\n return {\n children: [...known.values()],\n parts,\n completions,\n status: (id: string): ChildRunState => {\n const type = attempt(() => api.state.session.status(id)?.type, undefined)\n if (type === \"busy\" || type === \"retry\") return \"busy\"\n if (type === \"idle\") return \"idle\"\n return \"unknown\"\n },\n stoppedAt: (id: string) => stoppedAt.get(id),\n }\n },\n navigate(sessionID) {\n api.route.navigate(\"session\", { sessionID })\n },\n }\n\n const sidebar = createSidebar(host)\n api.slots.register({\n order: SECTION_ORDER,\n slots: {\n sidebar_content(_ctx: unknown, props: { session_id: string }) {\n return sidebar.render(() => props.session_id)\n },\n },\n })\n api.lifecycle?.onDispose(() => {\n for (const off of unsubscribe) attempt(off, undefined)\n sidebar.dispose()\n })\n}\n\n// ---------------------------------------------------------------------------\n// opencode 2.x\n// ---------------------------------------------------------------------------\n\n// The structural slice of opencode 2.x's TUI `Context` this module reads,\n// measured on 2.0.22 (h #g234). Its sessions carry `parentID`, its status is\n// `\"running\" | \"idle\"`, an assistant message holds its tool calls inline as\n// `content[]` items named `subagent` (with `metadata.sessionID` set while the\n// call still runs, unlike 1.x), and a background child's end is a message of\n// type `synthetic` whose text is `<subagent sessionID=\"...\" state=\"...\">`.\ninterface V2Session {\n id: string\n parentID?: string\n title?: string\n agent?: string\n time?: { created?: number; updated?: number }\n}\ninterface V2Message {\n type?: string\n text?: string\n time?: { created?: number }\n content?: Array<{\n type?: string\n name?: string\n time?: { created?: number; completed?: number }\n state?: { status?: string; input?: Record<string, unknown>; metadata?: Record<string, unknown> }\n }>\n}\ntype V2Shade = Record<string, Color>\ninterface V2Context {\n data: {\n session: {\n list(): V2Session[] | undefined\n status(sessionID: string): string\n message: { list(sessionID: string): V2Message[] | undefined }\n }\n }\n theme: {\n text: { base: Color; muted: Color; feedback: { success: V2Shade; warning?: V2Shade; error: V2Shade } }\n background: { raised: { base: Color } }\n hue: { accent: V2Shade }\n }\n themeMode?: \"dark\" | \"light\"\n ui: {\n router: { navigate(destination: { type: \"session\"; sessionID: string }): void }\n slot(claim: { append: string; render: (input: { sessionID: string }) => OpentuiNode }): () => void\n }\n}\n\nexport function setupV2(context: V2Context): () => void {\n const host: SidebarHost = {\n colors() {\n const theme = context.theme\n const muted = theme.text.muted\n // The accent hue at the shade that reads on this mode's background.\n const accent = theme.hue.accent[context.themeMode === \"light\" ? \"500\" : \"200\"] ?? theme.text.base\n return {\n text: theme.text.base,\n muted,\n spinner: accent,\n done: mix(theme.text.feedback.success.muted ?? theme.text.feedback.success.base, muted, 0.45),\n error: mix(theme.text.feedback.error.muted ?? theme.text.feedback.error.base, muted, 0.35),\n effort: theme.text.feedback.warning?.base ?? muted,\n effortFaded: mix(theme.text.feedback.warning?.base ?? muted, muted, 0.45),\n hover: theme.background.raised.base,\n }\n },\n gather(parentID) {\n const children: ChildSessionInput[] = (context.data.session.list() ?? [])\n .filter((session) => session.parentID === parentID)\n .map((session) => ({\n id: session.id,\n title: session.title,\n agent: session.agent,\n created: session.time?.created,\n updated: session.time?.updated,\n }))\n const parts: TaskPartInput[] = []\n const completions: CompletionInput[] = []\n for (const message of context.data.session.message.list(parentID) ?? []) {\n if (message.type === \"assistant\") {\n for (const item of message.content ?? []) {\n if (item.type !== \"tool\" || !TASK_TOOLS.has(item.name ?? \"\")) continue\n parts.push({\n status: item.state?.status,\n input: item.state?.input,\n metadata: item.state?.metadata,\n start: item.time?.created,\n end: item.time?.completed,\n })\n }\n } else if (message.type === \"synthetic\" && message.text) {\n completions.push(...completionsIn(message.text, message.time?.created))\n }\n }\n return {\n children,\n parts,\n completions,\n status: (id: string): ChildRunState => {\n const status = attempt(() => context.data.session.status(id), undefined)\n if (status === \"running\") return \"busy\"\n if (status === \"idle\") return \"idle\"\n return \"unknown\"\n },\n stoppedAt: () => undefined,\n }\n },\n navigate(sessionID) {\n context.ui.router.navigate({ type: \"session\", sessionID })\n },\n }\n\n const sidebar = createSidebar(host)\n const release = attempt(\n () =>\n context.ui.slot({\n append: \"sidebar.content\",\n render: (input) => sidebar.render(() => input.sessionID),\n }),\n undefined,\n )\n return () => {\n if (release) attempt(release, undefined)\n sidebar.dispose()\n }\n}\n\nconst tui = async (api: V1Api): Promise<void> => {\n attempt(() => registerV1(api), undefined)\n}\n\nconst setup = (context: V2Context): (() => void) | void =>\n attempt(() => setupV2(context), undefined)\n\nexport default { id: SUBAGENTS_TUI_PLUGIN_ID, tui, setup }\n","/**\n * The model behind the sidebar's `Subagents` section (`src/tui.ts`): which\n * subagents of one opencode session to show, in what state, in what order, and\n * the exact text of each row.\n *\n * Pure on purpose. It imports nothing, so the TUI bundle carries no logger (a\n * logger on the TUI's main thread writes to the terminal it is drawing on,\n * h #g193) and every rule here is testable without a terminal. The TUI half\n * only gathers the inputs and paints the segments this module returns.\n *\n * Measured on opencode 1.18.35 (h #g234), and each one shapes a rule below:\n *\n * - A RUNNING `task` part carries no `state.metadata` in the TUI's store, so\n * the child session id is not on the part until it completes. Rows are\n * built from the child SESSIONS (`parentID` is the current session) and a\n * part is matched to its child by the title opencode gives the child,\n * `<description> (@<agent> subagent)`.\n * - A background dispatch's part completes at once with a `<task id\n * state=\"running\">` envelope; the child's end arrives later as a synthetic\n * user part `<task id=\"ses_...\" state=\"completed\">` in the parent (opencode\n * 2 writes `<subagent sessionID=\"...\" state=\"...\">`). That envelope is the\n * authoritative end of a background child.\n * - `metadata.model` on a finished part is what opencode ASKED for, not what\n * ran: an agent's `forceModel`, its `reasoningEffort`, the dispatch form and\n * the fallback chain all change the spawn without telling opencode. So the\n * label prefers the spawn record the server half writes\n * (`src/spawn-record-store.ts`) and falls back to the part.\n */\n\nexport type SubagentState = \"running\" | \"done\" | \"error\"\n\n/** One child session as either major reports it. */\nexport interface ChildSessionInput {\n id: string\n title?: string\n agent?: string\n created?: number\n updated?: number\n}\n\n/** A `task` / `subagent` tool part in the parent, as either major reports it. */\nexport interface TaskPartInput {\n status?: string\n input?: Record<string, unknown>\n metadata?: Record<string, unknown>\n title?: string\n start?: number\n end?: number\n}\n\n/** A background child's end, read out of the parent's synthetic user parts. */\nexport interface CompletionInput {\n sessionID: string\n state: string\n at?: number\n}\n\n/** What the server half recorded about a session's real spawn. */\nexport interface SpawnRecordInput {\n model?: string\n effort?: string\n account?: string\n at?: number\n}\n\nexport type ChildRunState = \"busy\" | \"idle\" | \"unknown\"\n\nexport interface SidebarInput {\n children: ChildSessionInput[]\n parts: TaskPartInput[]\n completions: CompletionInput[]\n status: (sessionID: string) => ChildRunState\n record: (sessionID: string) => SpawnRecordInput | undefined\n /** When the TUI first saw a child stop, by session id. */\n stoppedAt: (sessionID: string) => number | undefined\n now: number\n}\n\nexport interface SubagentRow {\n /** The child session, absent while a dispatch has not created one yet. */\n sessionID?: string\n agent: string\n description: string\n background: boolean\n state: SubagentState\n /** `sonnet-5.5 · high · alpha`, empty when nothing is known. */\n label: string\n /** The effort piece of `label`, drawn in the theme's warning colour. */\n effort?: string\n started: number\n finished?: number\n}\n\n/** How long a finished subagent stays in the list, and how many of them. */\nexport const FINISHED_TTL_MS = 10 * 60_000\nexport const FINISHED_KEEP = 3\n/**\n * A background child that reads idle with no envelope yet is still starting\n * for this long: opencode creates the session before it marks it busy.\n */\nexport const STARTING_GRACE_MS = 10_000\n/** The usable width of a row in opencode 1.18.35's 42-column sidebar. */\nexport const SIDEBAR_ROW_WIDTH = 36\nexport const SPINNER_FRAMES = [\"⠋\", \"⠙\", \"⠹\", \"⠸\", \"⠼\", \"⠴\", \"⠦\", \"⠧\", \"⠇\", \"⠏\"]\n\nconst TITLE_SUFFIX = /\\s*\\(@([^)\\s]+) subagent\\)\\s*$/\n\n/** `Fix the form (@implementor subagent)` -> description and agent. */\nexport function parseChildTitle(title: string | undefined): { description: string; agent?: string } {\n const text = (title ?? \"\").trim()\n const match = TITLE_SUFFIX.exec(text)\n if (!match) return { description: text }\n return { description: text.slice(0, match.index).trim(), agent: match[1] }\n}\n\nfunction text(value: unknown): string | undefined {\n return typeof value === \"string\" && value.trim() ? value.trim() : undefined\n}\n\n/** The child session a part names, on either major's metadata. */\nexport function partChildID(part: TaskPartInput): string | undefined {\n const metadata = part.metadata ?? {}\n return text(metadata.sessionId) ?? text(metadata.sessionID)\n}\n\nfunction partAgent(part: TaskPartInput): string | undefined {\n const input = part.input ?? {}\n return text(input.subagent_type) ?? text(input.agent)\n}\n\nfunction partDescription(part: TaskPartInput): string | undefined {\n return text(part.input?.description) ?? text(part.title)\n}\n\nfunction partBackground(part: TaskPartInput): boolean {\n return part.input?.background === true || part.metadata?.background === true\n}\n\nconst COMPLETION_ENVELOPE =\n /<(?:task\\s+id|subagent\\s+sessionID)=\"(ses_[A-Za-z0-9]+)\"\\s+state=\"([a-z_]+)\"/g\n\n/**\n * Every background end a text names, in order. Both majors' envelopes:\n * `<task id=\"ses_...\" state=\"completed\">` (1.x) and\n * `<subagent sessionID=\"ses_...\" state=\"completed\">` (2.x). `running` is the\n * dispatch's own answer, not an end, so it is skipped.\n */\nexport function completionsIn(value: string, at?: number): CompletionInput[] {\n const found: CompletionInput[] = []\n for (const match of value.matchAll(COMPLETION_ENVELOPE)) {\n if (match[2] === \"running\") continue\n found.push({ sessionID: match[1], state: match[2], at })\n }\n return found\n}\n\nfunction endState(state: string): SubagentState {\n return state === \"completed\" ? \"done\" : \"error\"\n}\n\n/**\n * `claude-opus-5-5` -> `opus-5.5`, `claude-sonnet-4-5-20250929@alpha` ->\n * `sonnet-4.5`, `claude-opus-5-5-fast` -> `opus-5.5 fast`,\n * `claude-opus-5[1m]` -> `opus-5 1m`. Anything that is not a Claude id is\n * returned without its account marker and otherwise untouched.\n */\nexport function shortModelName(modelID: string | undefined): string | undefined {\n let id = text(modelID)\n if (!id) return undefined\n const at = id.indexOf(\"@\")\n if (at > 0) id = id.slice(0, at)\n let suffix = \"\"\n const window = /\\[(\\d+[km])\\]$/i.exec(id)\n if (window) {\n suffix += ` ${window[1].toLowerCase()}`\n id = id.slice(0, window.index)\n }\n if (id.endsWith(\"-fast\")) {\n suffix = ` fast${suffix}`\n id = id.slice(0, -\"-fast\".length)\n }\n if (!id.startsWith(\"claude-\")) return `${id}${suffix}`\n id = id.slice(\"claude-\".length).replace(/-\\d{8}$/, \"\")\n const match = /^([a-z]+)-(\\d+)(?:-(\\d+))?$/.exec(id)\n if (!match) return `${id}${suffix}`\n const version = match[3] === undefined ? match[2] : `${match[2]}.${match[3]}`\n return `${match[1]}-${version}${suffix}`\n}\n\n/**\n * The account a provider id names on a multi-account install\n * (`claude-code-alpha` -> `alpha`). A bare `claude-code` (one account) and a\n * provider that is not this plugin's name none: there is nothing to tell apart.\n */\nexport function accountFromProvider(providerID: string | undefined): string | undefined {\n const id = text(providerID)\n if (!id?.startsWith(\"claude-code-\")) return undefined\n return id.slice(\"claude-code-\".length) || undefined\n}\n\n/** `sonnet-5.5 · high · alpha`; pieces that are not known are left out. */\nexport function subagentLabel(\n record: SpawnRecordInput | undefined,\n part: TaskPartInput | undefined,\n): string {\n const asked = part?.metadata?.model as { modelID?: unknown; providerID?: unknown } | undefined\n const askedModel = typeof asked?.modelID === \"string\" ? asked.modelID : undefined\n const askedProvider = typeof asked?.providerID === \"string\" ? asked.providerID : undefined\n const model = shortModelName(record?.model ?? askedModel)\n const effort = text(record?.effort)\n const account = record?.model ? text(record.account) : accountFromProvider(askedProvider)\n return [model, effort, account].filter(Boolean).join(\" · \")\n}\n\n/**\n * Cut `value` to `width` cells with a trailing ellipsis. Counts code points,\n * which is a cell each for the text a task description holds; a wide glyph\n * can overrun by one cell and is left to the renderer's own clipping.\n */\nexport function truncate(value: string, width: number): string {\n const chars = Array.from(value.replace(/\\s+/g, \" \").trim())\n if (width <= 0) return \"\"\n if (chars.length <= width) return chars.join(\"\")\n if (width === 1) return \"…\"\n return `${chars.slice(0, width - 1).join(\"\").trimEnd()}…`\n}\n\n/**\n * The rows for one session: running first (oldest first), then finished\n * (newest first), at most `FINISHED_KEEP` of those and none older than\n * `FINISHED_TTL_MS`. Empty when nothing is worth showing, which is what makes\n * the section disappear.\n */\nexport function deriveSubagentRows(input: SidebarInput): SubagentRow[] {\n const completions = new Map<string, CompletionInput>()\n for (const completion of input.completions) completions.set(completion.sessionID, completion)\n\n const byChild = new Map<string, TaskPartInput>()\n const unmatched: TaskPartInput[] = []\n for (const part of input.parts) {\n const child = partChildID(part)\n if (child) byChild.set(child, part)\n else unmatched.push(part)\n }\n\n const rows: SubagentRow[] = []\n const seen = new Set<string>()\n for (const child of input.children) {\n if (seen.has(child.id)) continue\n seen.add(child.id)\n const parsed = parseChildTitle(child.title)\n let part = byChild.get(child.id)\n if (!part) {\n // A running part has no child id yet: match it on the title opencode\n // gave the child, agent and description both.\n const index = unmatched.findIndex(\n (candidate) =>\n partDescription(candidate) === parsed.description &&\n (parsed.agent === undefined || partAgent(candidate) === parsed.agent),\n )\n if (index >= 0) part = unmatched.splice(index, 1)[0]\n }\n rows.push(rowFor(input, child, parsed, part, completions.get(child.id)))\n }\n // A dispatch opencode has not created a session for yet: shown, not clickable.\n for (const part of unmatched) {\n if (part.status !== \"pending\" && part.status !== \"running\") continue\n const description = partDescription(part)\n const agent = partAgent(part)\n if (!description && !agent) continue\n rows.push({\n agent: agent ?? \"subagent\",\n description: description ?? \"\",\n background: partBackground(part),\n state: \"running\",\n label: subagentLabel(undefined, part),\n started: part.start ?? input.now,\n })\n }\n\n const running = rows\n .filter((row) => row.state === \"running\")\n .sort((a, b) => a.started - b.started)\n const finished = rows\n .filter((row) => row.state !== \"running\")\n .filter((row) => input.now - (row.finished ?? row.started) <= FINISHED_TTL_MS)\n .sort((a, b) => (b.finished ?? b.started) - (a.finished ?? a.started))\n .slice(0, FINISHED_KEEP)\n return [...running, ...finished]\n}\n\nfunction rowFor(\n input: SidebarInput,\n child: ChildSessionInput,\n parsed: { description: string; agent?: string },\n part: TaskPartInput | undefined,\n completion: CompletionInput | undefined,\n): SubagentRow {\n const background = part ? partBackground(part) : false\n const started = part?.start ?? child.created ?? input.now\n const status = input.status(child.id)\n let state: SubagentState\n let finished: number | undefined\n if (part && !background && part.status !== \"pending\" && part.status !== \"running\") {\n state = part.status === \"completed\" ? \"done\" : \"error\"\n finished = part.end\n } else if (completion) {\n state = endState(completion.state)\n finished = completion.at\n } else if (status === \"busy\") {\n state = \"running\"\n } else if (part && !background) {\n // pending or running: the parent is still waiting on this child.\n state = \"running\"\n } else if (status === \"idle\" && input.now - started < STARTING_GRACE_MS) {\n state = \"running\"\n } else if (status === \"unknown\" && background) {\n // Nothing says it stopped: a background child keeps its spinner until\n // its envelope arrives or opencode reports it idle.\n state = \"running\"\n } else {\n state = \"done\"\n }\n if (state !== \"running\" && finished === undefined) {\n finished = input.stoppedAt(child.id) ?? child.updated ?? started\n }\n return {\n sessionID: child.id,\n agent: parsed.agent ?? (part ? partAgent(part) : undefined) ?? text(child.agent) ?? \"subagent\",\n description: (part ? partDescription(part) : undefined) ?? parsed.description,\n background,\n state,\n label: subagentLabel(input.record(child.id), part),\n effort: text(input.record(child.id)?.effort),\n started,\n finished,\n }\n}\n\n/** A run of text with the role the TUI maps onto a theme colour. */\nexport interface Segment {\n text: string\n role: \"spinner\" | \"done\" | \"error\" | \"agent\" | \"description\" | \"muted\" | \"effort\" | \"effort-faded\"\n}\n\n/**\n * The two lines of one row, laid out for `width` cells:\n *\n * ⠋ implementor Fix the dispatch f… bg\n * sonnet-5.5 · high · alpha\n *\n * The `bg` marker is right-aligned, the description takes what is left, and a\n * finished row is drawn in the muted role throughout apart from its glyph.\n */\nexport function layoutRow(\n row: SubagentRow,\n width: number = SIDEBAR_ROW_WIDTH,\n frame = 0,\n): { first: Segment[]; second: Segment[] } {\n const finished = row.state !== \"running\"\n const glyph =\n row.state === \"running\"\n ? SPINNER_FRAMES[frame % SPINNER_FRAMES.length]\n : row.state === \"done\"\n ? \"✓\"\n : \"✗\"\n const marker = row.background ? \"bg\" : \"\"\n const reserved = marker ? marker.length + 1 : 0\n const agentWidth = Math.max(1, Math.min(Array.from(row.agent).length, width - 2 - reserved - 6))\n const agent = truncate(row.agent, agentWidth)\n const room = width - 2 - Array.from(agent).length - 1 - reserved\n const description = room > 0 ? truncate(row.description, room) : \"\"\n const used = 2 + Array.from(agent).length + (description ? 1 + Array.from(description).length : 0)\n const first: Segment[] = [\n { text: glyph, role: row.state === \"running\" ? \"spinner\" : row.state === \"done\" ? \"done\" : \"error\" },\n { text: \" \", role: \"muted\" },\n { text: agent, role: finished ? \"muted\" : \"agent\" },\n ]\n if (description) {\n first.push({ text: \" \", role: \"muted\" })\n first.push({ text: description, role: finished ? \"muted\" : \"description\" })\n }\n if (marker) {\n first.push({ text: \" \".repeat(Math.max(1, width - used - marker.length)), role: \"muted\" })\n first.push({ text: marker, role: \"muted\" })\n }\n // The effort is the one piece in colour: it is what changes from one\n // subagent to the next and what costs the most, so it is the one to spot.\n const second: Segment[] = []\n if (row.label) {\n const pieces = truncate(row.label, width - 2).split(\" · \")\n const effortAt = row.effort ? pieces.indexOf(row.effort) : -1\n second.push({ text: \" \", role: \"muted\" })\n pieces.forEach((piece, index) => {\n if (index > 0) second.push({ text: \" · \", role: \"muted\" })\n const role = index !== effortAt ? \"muted\" : finished ? \"effort-faded\" : \"effort\"\n second.push({ text: piece, role })\n })\n }\n return { first, second }\n}\n\n/** The collapsed heading's count: `2 running` or `3 done`. */\nexport function collapsedSummary(rows: SubagentRow[]): string {\n const running = rows.filter((row) => row.state === \"running\").length\n return running > 0 ? `${running} running` : `${rows.length} done`\n}\n","/**\n * What each opencode session's `claude` process REALLY spawned as: model,\n * effort and account. Written by the server half on every turn that reaches\n * the CLI, read by the TUI half's `Subagents` sidebar section (`src/tui.ts`).\n *\n * ## Why a file, and why the TUI cannot read this anywhere else\n *\n * Measured on opencode 1.18.35 (h #g234): an `implementor` subagent whose\n * agent file says `forceModel: claude-sonnet-5-5` and `reasoningEffort: high`\n * spawned as exactly that, while the finished `task` part's `metadata.model`\n * said `claude-code-default/claude-opus-5-5` and no message carried an effort\n * at all. Everything that changes a spawn on the plugin side (an agent's\n * `forceModel` and `reasoningEffort`, `defaultSubagentModel`, the dispatch form\n * of h #g227, the fallback chain of h #g162, an account failover) is invisible\n * to opencode by construction, so showing opencode's own view would be showing\n * the wrong model exactly when the operator configured it not to be. The two\n * halves do not share memory on either major (1.x runs the server half in a\n * worker thread, 2.x in a subprocess), so the record goes through the state\n * directory the resume and dispatch stores already use.\n *\n * ## Shape, and what is never written\n *\n * ```json\n * { \"version\": 1, \"sessions\": {\n * \"ses_abc\": { \"model\": \"claude-sonnet-5-5\", \"effort\": \"high\", \"account\": \"alpha\", \"at\": 1760000000000 } } }\n * ```\n *\n * A model id, an effort level and an account NAME: the same three strings the\n * dispatch store keeps (h #g228), none secret. No prompt, no path, no session\n * key. `account` is written only on a multi-account install, where it tells\n * two configured accounts apart; on a single-account install there is nothing\n * to tell apart and it stays out.\n *\n * ## Cost and failure\n *\n * One write per session per process, and another only when what it spawns as\n * changes: `recordSessionSpawn` compares against what this process last wrote\n * and returns before touching the disk otherwise. Same discipline as the\n * resume store (h #g197): merge-on-write so two opencode processes do not erase\n * each other, atomic rename, `0600` in the `0700` directory, bounded at\n * `MAX_SPAWN_RECORDS` newest sessions and `SPAWN_RECORD_MAX_AGE_MS`. A missing,\n * unreadable or half-written file is an empty store, which costs the sidebar\n * its effort and falls back to the model opencode asked for. Two writers racing\n * can drop one entry; the next turn of that session writes it again, and the\n * only thing it ever costs is a label.\n *\n * Imports nothing that logs, because the TUI bundle reads it on the terminal's\n * own thread (h #g193): every function here is silent and never throws.\n */\nimport { mkdirSync, readFileSync, renameSync, statSync, writeFileSync } from \"node:fs\"\nimport { homedir } from \"node:os\"\nimport { join } from \"node:path\"\n\nexport interface SpawnRecord {\n model?: string\n effort?: string\n account?: string\n at: number\n}\n\n/** Sessions worth remembering at once. A subagent fan-out names a handful. */\nexport const MAX_SPAWN_RECORDS = 256\n\n/** A session not spawned this long ago is not one the sidebar is showing. */\nexport const SPAWN_RECORD_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000\n\nlet pathOverride: string | null = null\nconst lastWritten = new Map<string, string>()\n\nexport function spawnRecordStorePath(): string {\n if (pathOverride) return pathOverride\n const stateRoot = process.env.XDG_STATE_HOME || join(homedir(), \".local\", \"state\")\n return join(stateRoot, \"opencode-claude-code-plugin\", \"session-spawns.json\")\n}\n\nfunction isRecord(value: unknown): value is SpawnRecord {\n const record = value as SpawnRecord | null\n if (!record || typeof record !== \"object\" || Array.isArray(record)) return false\n if (typeof record.at !== \"number\" || !Number.isFinite(record.at)) return false\n const ok = (field: unknown) => field === undefined || typeof field === \"string\"\n if (!ok(record.model) || !ok(record.effort) || !ok(record.account)) return false\n return !!record.model\n}\n\nfunction readEntries(file: string): Map<string, SpawnRecord> {\n const out = new Map<string, SpawnRecord>()\n try {\n const parsed = JSON.parse(readFileSync(file, \"utf8\"))\n const entries = parsed && typeof parsed === \"object\" ? parsed.sessions : null\n if (!entries || typeof entries !== \"object\") return out\n const cutoff = Date.now() - SPAWN_RECORD_MAX_AGE_MS\n const valid = Object.entries(entries as Record<string, unknown>)\n .filter((entry): entry is [string, SpawnRecord] => isRecord(entry[1]))\n .filter(([, record]) => record.at >= cutoff)\n .sort((a, b) => a[1].at - b[1].at)\n for (const [key, record] of valid) {\n out.set(key, {\n model: record.model,\n ...(record.effort ? { effort: record.effort } : {}),\n ...(record.account ? { account: record.account } : {}),\n at: record.at,\n })\n }\n } catch {\n // ENOENT before the first spawn, or a file another process is renaming\n // into place: the empty store is the answer.\n }\n return out\n}\n\n/**\n * Record what `sessionID` spawned as. Returns whether the disk was written,\n * which is false for an unchanged record, an unusable id and a failed write\n * alike: the caller has nothing to do about any of them.\n */\nexport function recordSessionSpawn(\n sessionID: string,\n spawn: { model: string; effort?: string; account?: string },\n now: number = Date.now(),\n): boolean {\n if (!sessionID || sessionID === \"default\" || !spawn.model) return false\n const record: SpawnRecord = {\n model: spawn.model,\n ...(spawn.effort ? { effort: spawn.effort } : {}),\n ...(spawn.account ? { account: spawn.account } : {}),\n at: now,\n }\n const fingerprint = `${record.model}\\n${record.effort ?? \"\"}\\n${record.account ?? \"\"}`\n if (lastWritten.get(sessionID) === fingerprint) return false\n const file = spawnRecordStorePath()\n const merged = readEntries(file)\n merged.delete(sessionID)\n merged.set(sessionID, record)\n const cutoff = now - SPAWN_RECORD_MAX_AGE_MS\n const kept = [...merged.entries()]\n .filter(([, entry]) => entry.at >= cutoff)\n .sort((a, b) => a[1].at - b[1].at)\n .slice(-MAX_SPAWN_RECORDS)\n try {\n mkdirSync(join(file, \"..\"), { recursive: true, mode: 0o700 })\n const tmp = `${file}.${process.pid}.tmp`\n writeFileSync(tmp, JSON.stringify({ version: 1, sessions: Object.fromEntries(kept) }), {\n mode: 0o600,\n })\n renameSync(tmp, file)\n } catch {\n return false\n }\n if (lastWritten.size >= MAX_SPAWN_RECORDS) lastWritten.clear()\n lastWritten.set(sessionID, fingerprint)\n return true\n}\n\n/**\n * A reader that re-parses the file only when it changed. The TUI calls `read()`\n * on every refresh; an unchanged file costs one `stat`.\n */\nexport function createSpawnRecordReader(file: () => string = spawnRecordStorePath) {\n let identity = \"\"\n let cached = new Map<string, SpawnRecord>()\n return {\n read(): Map<string, SpawnRecord> {\n const path = file()\n let next = \"\"\n try {\n const stats = statSync(path)\n next = `${stats.mtimeMs}:${stats.size}:${stats.ino}`\n } catch {\n next = \"missing\"\n }\n if (next !== identity) {\n identity = next\n cached = next === \"missing\" ? new Map() : readEntries(path)\n }\n return cached\n },\n }\n}\n\nexport function _setSpawnRecordStorePathForTests(path: string | null): void {\n pathOverride = path\n lastWritten.clear()\n}\n"],"mappings":"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","names":["text"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@khalilgharbaoui/opencode-claude-code-plugin",
3
- "version": "0.54.0",
3
+ "version": "0.54.2",
4
4
  "description": "Claude Code CLI provider plugin for opencode",
5
5
  "homepage": "https://opencode-claude-code-plugin.dev/",
6
6
  "funding": {
@@ -227,6 +227,7 @@ their secret values. Arbitrary MCP `{env:NAME}` placeholders are outside this li
227
227
  | `ANTHROPIC_AUTH_TOKEN` | CLI auth-token input; same strip/warning rule. Never display it. |
228
228
  | `DISABLE_AUTOUPDATER` | Set to `1` on every spawned `claude`, and only when the user has not set it. Keeps the CLI from updating mid-session, which would invalidate the cached version that gates `--thinking-display summarized`, `--plugin-dir` and fast mode. Not a provider option: a user-set value (including `0`, meaning keep updating) is never overwritten, which is the intended escape hatch. Tell a user who wants CLI autoupdates to export `DISABLE_AUTOUPDATER=0`, not to look for a config key. |
229
229
  | `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC` | Set to `1` on every spawned `claude` under the same never-overwrite rule. Suppresses non-essential CLI network traffic and independently blocks auto-update. An empty string counts as user-set and is left alone; the CLI reads it as off. |
230
+ | `MCP_TIMEOUT` | Set to `120000` on every spawned `claude` (both transports), only when the user has not set it. Claude Code's 30 s default let a subagent give up on the plugin's in-process proxy while opencode was busy, and run with no tools ("no shell tool"). A subagent that reports no tools, or a slow first reply in a very long session, points here. |
230
231
  | `OPENCODE_CONFIG` | Explicit config file, also read by the disk MCP bridge before project layers. |
231
232
  | `OPENCODE_CONFIG_DIR` | Additional `.opencode`-style config/skill root. The plugin's direct agent-file fallback does not use it; agents must reach the config hook or a supported agent directory. |
232
233
  | `OPENCODE_WORKTREE` | Overrides the disk MCP bridge's project walk-up boundary. opencode 1.x layering only: the opencode 2 layering walks to the filesystem root and applies no worktree boundary at all. |
@@ -884,7 +885,8 @@ Always on, no option. While the viewed session has subagents, the sidebar ends w
884
885
  `Subagents` section: one row each, running first (spinner in the theme accent), then up
885
886
  to three finished ones (✓ or ✗, greyed out, gone after ten minutes), with the agent type,
886
887
  the task description, a dim `bg` for background dispatches, and a muted second line
887
- `model · effort · account` (account only on a multi-account install). That line is what
888
+ `model · effort · account` (account only on a multi-account install; the effort alone in
889
+ the theme's warning colour, faded on a finished row). That line is what
888
890
  the plugin really spawned, read from `$XDG_STATE_HOME/opencode-claude-code-plugin/session-spawns.json`,
889
891
  so `forceModel`, an agent's `reasoningEffort`, a dispatch-form answer, the fallback chain
890
892
  and a failover all show there, while opencode's own footer shows the model it asked for.