/** * Session-scoped canvas facade: everything the TUI needs, with no TUI in it. * * Design: `docs/canvas-extensions-design.md` §11. The pieces underneath — discovery, * the trust gate, availability, the registry — are each small and separately tested. * This is what stitches them into the four questions a user surface actually asks: * what is there, can it run, open this one, close that one. * * It holds no TUI types on purpose. `extensions/core/canvas.ts` renders and supplies * an `AbortSignal` from a cancellable loader; everything decided here stays testable * without a terminal. * * Availability is resolved once and cached, because resolving can spawn * `node --version` (§11.1) and the answer cannot change within a session. */ import type { DiscoveredCanvasExtension } from "./discovery.js"; import { type CanvasSearchRoot } from "./discovery.js"; import { type CanvasAvailability } from "./launch.js"; import { type CanvasLifecycleRefusal, type CanvasRemoveResult, type CanvasRenameResult } from "./lifecycle.js"; import { type CanvasInstance, CanvasRegistry, type CanvasRegistryEvents, type CanvasReloadResult } from "./registry.js"; import type { CanvasCallOptions } from "./runner.js"; /** One canvas a person could open, or has open. */ export interface CanvasListing { extensionId: string; /** Undefined until the extension has been forked, since declarations come from it. */ canvasId: string | undefined; displayName: string | undefined; scope: DiscoveredCanvasExtension["scope"]; /** Why it cannot be opened, if it cannot. */ withheld: "untrusted-workspace" | undefined; /** Instances of this canvas that are currently open. */ open: CanvasInstance[]; } /** What `list()` reports. */ export interface CanvasOverview { /** Absent `reason` means canvases can run here. */ availability: CanvasAvailability; listings: CanvasListing[]; /** Extensions withheld by the trust gate — surfaced, never hidden (§5.1). */ withheldCount: number; /** * Action names per open instance. * * Beside the listings rather than inside them because an action belongs to a * running instance, not to a canvas on disk: a listing exists for extensions * that have never been forked, and those have no actions to report — not zero * of them, none knowable. */ actionsByInstance: Map; } /** Configuration for a session's canvas facade. */ export interface CanvasSessionOptions extends CanvasRegistryEvents { cwd: string; homeDir: string; agentDir?: string; /** Override the search roots; defaults to {@link canvasSearchRoots}. */ roots?: CanvasSearchRoot[]; /** * Override how plugin-shipped canvases are found; defaults to * {@link pluginCanvasExtensions}. Pass `() => []` to look at the search roots * and nothing else. */ pluginExtensions?: () => DiscoveredCanvasExtension[]; /** Override availability resolution, for tests and for hosts that already know. */ resolveRuntime?: () => Promise; } /** Reference to a canvas: an extension id, optionally narrowed to one of its canvases. */ export interface CanvasRef { extensionId: string; canvasId?: string; } /** * Parse `extension` or `extension:canvas`. * * Extension ids are directory names and canvas ids are provider-local, so a single * colon is unambiguous and needs no quoting. */ export declare function parseCanvasRef(input: string): CanvasRef | undefined; export declare class CanvasSession { private readonly options; private readonly roots; private readonly pluginExtensions; /** Cached because resolving can spawn `node --version` and cannot change mid-session. */ private availabilityPromise; /** Created on first successful open, not at construction: listing must not fork. */ private registry; constructor(options: CanvasSessionOptions); /** Discovered extensions, partitioned by the trust gate. Read-only and always safe. */ discover(): { runnable: DiscoveredCanvasExtension[]; withheld: DiscoveredCanvasExtension[]; }; /** Whether canvases can run here. Resolved once per session and cached. */ availability(): Promise; /** * What is installed, what is open, and what is being withheld. * * Deliberately does not fork anything: listing must stay free and safe, so a * `canvasId` is only known for extensions already running. That is the visible * consequence of a canvas having no passive half (§5.1) — even its name comes from * running its code. */ list(): Promise; /** * Open a canvas. * * `options.signal` comes from the caller's cancellable loader, so a person's Esc * reaches the registry's abandon path (§11.6) rather than merely hiding a spinner. */ open(ref: CanvasRef, options?: CanvasCallOptions): Promise; /** Close one open instance. Unknown ids are a no-op, so closing twice is harmless. */ close(instanceId: string): Promise; /** * Re-fork an open extension so an edit to its code takes effect. * * Reached by extension id rather than instance id because a reload restarts the * *process*, and one child serves every instance of every canvas the extension * declares — pretending it could reload one instance would be a lie about what * happens. {@link CanvasRegistry.reload} carries the open instances across. */ reload(extensionId: string, options?: CanvasCallOptions): Promise; /** * Rename a canvas extension, closing anything it has open first. * * Closing is not politeness: the directory is about to move, and an instance * left open would be serving from a path that no longer exists while the * registry still believed it was there. The closed instance ids are returned so * the caller can say what it cost. */ rename(extensionId: string, to: string): Promise; /** Delete a canvas extension, closing anything it has open first. */ remove(extensionId: string): Promise; private closeAllOf; /** The discovered extension with this id, runnable or withheld. */ private find; private notFound; /** Everything that could be renamed or removed, for completions and messages. */ knownExtensionIds(): string[]; /** The extension ids with at least one open instance — what {@link reload} accepts. */ runningExtensionIds(): string[]; /** Every open instance. */ instances(): CanvasInstance[]; /** * The live registry, or undefined if nothing has been opened yet. * * Exposed so the host can hand it to the canvas tools, which read * `listInstances()` and `activeActions()` from it. */ registryOrUndefined(): CanvasRegistry | undefined; /** Advisory cleanup, driven by whoever owns the session clock. */ reapIdle(): Promise; /** Close everything and stop every child. Safe to call twice. */ dispose(): Promise; private ensureRegistry; private soleCanvasId; } //# sourceMappingURL=session.d.ts.map