import type { TuiPlugin } from '@opencode-ai/plugin/tui'; import type { JSX } from '@opentui/solid'; import { type TuiSnapshot } from './tui-state'; export interface ActiveTmuxPaneRegistration { sessionId?: string; paneId?: string; ownerPid: number; lastRecordedAt: number; } /** Route shapes accepted by `syncTmuxPaneRegistration`: v1 `{ name, params }` and v2 `{ type, sessionID }`. */ export type TuiRouteView = { name?: string; params?: { sessionID?: unknown; }; } | { type?: string; sessionID?: string; }; export declare function syncTmuxPaneRegistration(route: TuiRouteView, registration: ActiveTmuxPaneRegistration, now?: number): void; export declare function splitSidebarModelId(model: string): { provider?: string; model: string; }; export declare function getSidebarAgentNames(snapshot: TuiSnapshot): string[]; /** * Remote-attach fallback (#1133): the server-side plugin writes * tui-state.json on the server's filesystem, which a remote TUI cannot * see, so every model renders as "pending". Resolve agent models through * the host SDK instead. Only fills gaps — local snapshot entries win. * * v1 TUI (`api.client`, `@opencode-ai/sdk/v2`): `app.agents({ directory })` * with `{ name, model: { providerID, modelID } }`. * v2 TUI: `agent.list({ location: { directory } })` or * `v2.agent.list(...)` with `{ id, model: { providerID, id } }`. */ export declare function fetchRemoteAgentModels(client: unknown, directory: string): Promise>; /** Local snapshot entries win; remote fills empty/missing agent models (#1133). */ export declare function applyRemoteAgentModels(snapshot: TuiSnapshot, remote: Record): TuiSnapshot; /** Skip overlapping sidebar refreshes so a slow host fetch cannot pile up. */ export declare function createSerializedRefresh(run: () => Promise): () => void; /** Drop a refresh whose directory changed while the host fetch was in flight. */ export declare function isRefreshCurrent(startedDirectory: string, currentDirectory: string): boolean; export declare function getActiveSidebarAgentNames(snapshot: TuiSnapshot, visibleRootID?: string): ReadonlySet; /** One clickable sidebar destination: an active subagent session. */ export interface SidebarSessionTarget { sessionID: string; agentName: string; alias?: string; model?: string; status?: 'busy' | 'retry'; } export interface SidebarAgentTargets { agentName: string; sessions: SidebarSessionTarget[]; } /** * Group the active subagent sessions of the visible conversation by agent * for the clickable sidebar. Mirrors the scoping of * getActiveSidebarAgentNames (#1147) with two refinements: * - Only sessions with a known parent link are offered as destinations: * a root session running an agent directly (e.g. a top-level chat with * agent=oracle) is not a subagent of this conversation. * - Without a visible route session there is no conversation to scope to; * return no targets rather than exposing cross-conversation navigation. * Stable ordering: by alias (numeric suffix aware, ora-2 < ora-10), then * by sessionID. */ export declare function getSidebarAgentTargets(snapshot: TuiSnapshot, visibleRootID?: string): SidebarAgentTargets[]; /** Natural sort for alias counters: ora-2 sorts before ora-10. */ export declare function compareAliasNumeric(a: string, b: string): number; /** Short distinctive id fallback when a session has no board alias. */ export declare function shortSessionID(sessionID: string): string; /** * Per-window sidebar interaction state. `navigate` is feature-detected at * startup: without it the sidebar renders informatively (no handlers). * Expansion state is local to this window and never persisted. */ export interface SidebarInteraction { navigate?: (sessionID: string) => void; expandedAgents: () => ReadonlySet; toggleAgent: (agentName: string) => void; /** Reset expansion when the project directory or visible root changes. */ syncScope: (directory: string, rootID: string | undefined) => void; /** True when this TUI has a non-empty text selection (skip click). */ hasSelectedText?: () => boolean; } export declare function createSidebarInteraction(navigate: ((sessionID: string) => void) | undefined, hasSelectedText?: () => boolean): SidebarInteraction; /** Build a guarded navigation callback from a raw route navigate fn. */ export declare function makeRouteNavigator(owner: object | undefined, methodName: 'navigate', v2Shape: boolean): ((sessionID: string) => void) | undefined; export declare function getSidebarActivityIndicator(active: boolean, now?: number): string; export declare function selectionGuard(renderer: { getSelection?: () => unknown; }): () => boolean; export declare function resolveHoverBackground(theme: { background?: unknown; backgroundElement?: unknown; text?: unknown; hover?: unknown; }): unknown; export declare function getContrastForeground(accent: unknown, themeText: unknown, themeBackground: unknown): unknown; export declare function readConfigInvalid(directory: string): boolean; export declare function readCompactSidebar(directory: string): boolean; /** * Position slim's sidebar section according to its index in the host's * effective plugin list (`tuiConfig.plugin`): index 0 → 110 (right after * the host's context section, above most third-party plugins), each later * index one band of 100 later. This only moves slim's own slot; other * plugins retain their own order, and no relative ordering with them is * guaranteed. v1 hosts only — the v2 slot claim API has no order * parameter. When the spec is absent or the list is unavailable, the * historic default (900) applies. */ export declare function resolveSidebarSlotOrder(pluginList: unknown, pluginName: string): number; interface V2TuiThemeTokens { text: { default: unknown; subdued: unknown; }; background: { default: unknown; }; border: { default: unknown; }; /** Optional semantic tokens; v2 hosts may omit them. */ success?: unknown; warning?: unknown; } interface V2TuiSlotClaim { append?: string; prepend?: string; before?: string; after?: string; replace?: string; render: (input: { sessionID: string; }) => JSX.Element; } interface V2TuiContext { location?: { directory: string; }; client?: unknown; renderer: { requestRender: () => void; getSelection?: () => unknown; }; theme: V2TuiThemeTokens; ui: { slot: (claim: V2TuiSlotClaim) => () => void; router: { current: () => { type?: string; sessionID?: string; }; /** Optional navigation capability; absent on hosts that don't expose it. */ navigate?: (route: { type: string; sessionID: string; }) => void; }; }; } /** * Dual contract: v1 hosts validate `{ id, tui }`, opencode2 validates * `{ id, setup }`; both ignore extra keys. Fixes #1002. */ interface TuiDualContractModule { id: string; tui: TuiPlugin; setup: (ctx: V2TuiContext) => Promise void)>; } declare const plugin: TuiDualContractModule; export default plugin;