/** * Capability layer — Protocol v2's unified registry of LLM-invokable entry points. * * A {@link Capability} is a named, schema-typed entry point that the LLM can call * during a session. Capabilities are registered into a per-session * registry by either side (host platform via `side: 'client'` or runtime via * `side: 'agent'`) and routed by the bridge at invocation time. * * This module defines the wire types only; the runtime registry, the * `defineCapability` helper, and the per-handler logger live in * `@skaile/workspaces/runner`. * * @see {@link MessageV2} envelope that carries capability commands and events * @see {@link ProtocolVersion} for the version contract * * @category Capabilities * @since 2.0.0 */ /** * Visibility scope for a capability. Determines which surfaces the capability * appears on and which callers can invoke it. * * - `'llm'`: registered as an LLM tool, surfaced in the `` system * prompt section. The agent driver can invoke it. * - `'user'`: appears in user-facing surfaces — the command palette (cmdK), * debug menus, capability lists. * - `'runtime'`: invocable only by trusted runner / platform code paths. Never * reaches the LLM tool list. Never reaches the command palette. Used for * host RPCs (`host.refresh_credential`, `host.audit`, `host.notify_user`, * `host.persist_compaction_attempt`) and runner RPCs (`runner.add_mount`, * `runner.set_log_level`, `runner.lifecycle`, ...). * * Multiple audiences are allowed (e.g. `['llm', 'user']`). The default when * {@link Capability.audience} is omitted is `['llm', 'user']`, preserving the * v2.x default visibility for capabilities that haven't migrated yet. * * @category Capabilities * @since 3.0.0 * @docLink packages/types/capabilities#capability-audience */ export type CapabilityAudience = "llm" | "user" | "runtime"; /** * Source of truth for who registered a capability. Used for trust enforcement * (the bridge rejects mismatches between registering side and origin kind) * and for the `capability::` log taxonomy. * * Variants: * - `framework`: built-in (e.g. `__capabilities.list`) * - `client`: host platform (e.g. `platform.react`) * - `agent`: self-registered by the agent runtime * - `flow`: injected by an active flow execution * - `skill`: from a skill's MCP surface * - `mcp`: from a generic MCP server * - `connector`: from a connector (e.g. `postgres.query`) * - `mount`: surfaced by a filesystem mount * - `app`: from an embedded, agent-controllable app (carries the originating * `appId`). Handlers run in the app itself — see {@link Capability.side} `'app'`. * * @category Capabilities * @since 2.0.0 * @docLink packages/types/capabilities#capability-origin */ export type CapabilityOrigin = { kind: "framework"; } | { kind: "client"; } | { kind: "agent"; } | { kind: "flow"; flowId: string; } | { kind: "skill"; skillId: string; } | { kind: "mcp"; serverId: string; } | { kind: "connector"; connectorId: string; } | { kind: "mount"; mountId: string; } | { kind: "app"; appId: string; }; /** * Render contract for a capability that produces a UI surface when invoked. * * When the LLM invokes a capability whose `render` is set, the bridge emits * a {@link RenderInvokedEvent} carrying the capability name and props. The * client renders the configured `source` at the chosen `target`. Clients * without a render layer can fall back to the markdown `fallback` template. * * @category Capabilities * @since 2.0.0 * @docLink packages/types/capabilities#render-spec */ export type RenderSpec = { /** Where the rendered component lives. */ source: { kind: "web-component"; url: string; tagName: string; } | { kind: "iframe"; url: string; }; /** Render placements supported by the component. */ targets: ("chat" | "preview" | "modal" | "input-extension")[]; /** Markdown template with `{{prop}}` placeholders for clients without a render layer. */ fallback?: string; /** True when props may be partial during streaming. */ streaming?: boolean; /** Interaction names the rendered component may fire back as state events. */ interactions?: string[]; }; /** * A named, schema-typed entry point that the LLM can invoke during a session. * * Capabilities can be declared statically (via `ConfigureCommand.capabilities`) * or dynamically (via {@link CapabilityRegisterCommand} / * {@link CapabilityRegisterEvent}). They are cached for replay across * subscriber reconnects and session hibernation. * * @example Defining a client-side fire-and-forget tool (uses the runner's * `defineCapability` helper, which serializes a {@link Capability} to the wire): * ```ts * const reactTool = defineCapability({ * name: 'platform.react', * description: 'Acknowledge a user message with a Unicode emoji.', * side: 'client', * origin: { kind: 'client' }, * fireAndForget: true, * input: z.object({ emoji: z.string(), targetSeq: z.number().optional() }), * handler: async ({ emoji, targetSeq }, ctx) => { ... }, * }); * ``` * * @see {@link RenderCapability} for capabilities that render UI components * @see {@link CapabilityOrigin} for who-registered-it metadata * @see {@link isRenderCapability} for the type-guard * * @category Capabilities * @since 2.0.0 * @docLink packages/types/capabilities#capability */ export type Capability = { /** Unique within a session. Convention: namespaced as `.` (e.g. `platform.react`, `ui.gif`). */ name: string; /** * Human-readable label for user-facing surfaces (cmdK action palette, * capability menus, debug panels). Falls back to {@link Capability.name} * when omitted. Pure presentation — the LLM sees `name`, not `displayName`. */ displayName?: string; /** Human/LLM-readable summary. Shown to the LLM as part of tool registration. */ description: string; /** JSON Schema validating the invocation arguments. Authored as Zod, converted at register time. */ inputSchema: Record; /** Optional return shape. Omit for fire-and-forget effects. */ outputSchema?: Record; /** Where the handler runs. `client` = host platform, `agent` = runtime/skill/connector, `app` = embedded app. */ side: "client" | "agent" | "app"; /** Discriminated tag identifying who registered the capability. Used for trust enforcement and audit. */ origin: CapabilityOrigin; /** Lifetime: `session` survives the whole session, `turn` is dropped at end-of-turn. */ scope?: "session" | "turn"; /** When true, the bridge auto-resolves with `{}` immediately and runs the handler in the background. */ fireAndForget?: boolean; /** When true, the platform shows a confirm UI before dispatch and waits for {@link CapabilityApproveCommand}. */ requiresApproval?: boolean; /** Documentation hint (not enforced) describing whether this is a side-effect, render, or pure query. */ kind?: "effect" | "render" | "query"; /** * Whether this capability should be exposed in user-facing surfaces (cmdK * action palette, capability menus). When omitted, the consumer applies a * default per-origin via {@link isUserInvokable}. * * - `true`: surface in cmdK and capability menus. * - `false`: hide from user surfaces (LLM-only or low-level). * - `undefined`: defer to {@link isUserInvokable}'s inference rules. * * @deprecated Set {@link Capability.audience} instead. `userInvokable` is * kept as a read-only shim for v2.x consumers and is honoured by * {@link isUserInvokable} when {@link Capability.audience} is absent. */ userInvokable?: boolean; /** * Audience scoping for this capability. See {@link CapabilityAudience} for * the meaning of each value and combinations. * * When omitted, consumers treat the capability as `['llm', 'user']` (the * v2.x default). Host RPCs and runner RPCs MUST set this to `['runtime']` * so they never leak to the LLM tool list or the user-facing surfaces. * * @category Capabilities * @since 3.0.0 */ audience?: CapabilityAudience[]; /** Appended to the system prompt's `` section. Use for usage hints the LLM should always see. */ promptFragment?: string; /** * Per-capability override for how long the runner waits on the * `capability_result` round-trip before timing out. When omitted the * runner applies its default (`DEFAULT_CAPABILITY_CALL_TIMEOUT_MS`, 60s). * * Set this for capabilities that legitimately block far longer than a * normal tool call — e.g. `platform.ask_session` suspends the caller's * turn until a peer session answers, which can take minutes. * * @category Capabilities * @since 3.3.0 */ callTimeoutMs?: number; /** Present iff this is a render capability — see {@link RenderCapability}. */ render?: RenderSpec; }; /** * A {@link Capability} that produces a UI surface when invoked. The narrowed * type guarantees `render` is set, which simplifies frontend lookup. * * @see {@link isRenderCapability} * * @category Capabilities * @since 2.0.0 * @docLink packages/types/capabilities#render-capability */ export type RenderCapability = Capability & { render: NonNullable; }; /** * Type-guard for {@link RenderCapability}. Use after {@link Capability} * lookup to narrow the type before reading `render`. * * @example * ```ts * for (const cap of registry.list()) { * if (isRenderCapability(cap)) console.log(cap.render.targets); * } * ``` * * @category Capabilities * @since 2.0.0 * @docLink packages/types/capabilities#is-render-capability */ export declare function isRenderCapability(cap: Capability): cap is RenderCapability; /** * Decide whether a capability should be surfaced in user-facing actions * (cmdK action palette, capability menus). * * Resolution order (first match wins): * * 1. {@link Capability.audience} is set — return `audience.includes('user')`. * Capabilities scoped to `'runtime'` only never appear in user surfaces. * 2. {@link Capability.userInvokable} is set (v2.x shim) — return that value. * 3. Default per-origin inference table below. * * Default-inference rules (when both `audience` and `userInvokable` are * omitted): * | Origin | Default | * | ------------ | ---------------------------------------------------------------------- | * | `framework` | `false` (LLM discovery surfaces, e.g. `__capabilities.list`) | * | `client` | `false` (platform ack tools — `platform.react`, `platform.pass`) | * | `agent` | `false` (agent-defined; assume internal unless flagged) | * | `flow` | `false` (per-cap opt-in) | * | `connector` | `false` (low-level FS / data ops) | * | `mount` | `false` (low-level FS ops) | * | `app` | `false` (LLM-only; app authors opt in via `audience: ['llm','user']`) | * | `mcp` | `true` if `kind === 'effect'`, else `false` | * | `skill` | `true` if `kind` ∈ {`effect`, `render`}, else `false` | * * @example * ```ts * for (const cap of capabilities.values()) { * if (isUserInvokable(cap)) registerCommandPaletteAction(cap); * } * ``` * * @category Capabilities * @since 2.1.0 * @docLink packages/types/capabilities#is-user-invokable */ export declare function isUserInvokable(cap: Capability): boolean; /** * Register one or more capabilities mid-session (client → agent). * * The platform sends this when a new client-side tool becomes available * after `configure` has already returned (e.g. a user installs a new * extension during the session). * * @category Capabilities * @since 2.0.0 * @docLink packages/types/capabilities#capability-register-command */ export type CapabilityRegisterCommand = { type: "capability_register"; capabilities: Capability[]; }; /** * Deregister capabilities by name (client → agent). Names that are not * currently registered are ignored silently. * * @category Capabilities * @since 2.0.0 * @docLink packages/types/capabilities#capability-deregister-command */ export type CapabilityDeregisterCommand = { type: "capability_deregister"; names: string[]; }; /** * Return the result of a previously {@link CapabilityInvokedEvent | invoked} client-side * capability (client → agent). Bridges that registered the capability with * `fireAndForget: true` MAY skip this command; the bridge auto-resolves with `{}`. * * @category Capabilities * @since 2.0.0 * @docLink packages/types/capabilities#capability-result-command */ export type CapabilityResultCommand = { type: "capability_result"; /** Correlation token matching the {@link CapabilityInvokedEvent.callId}. */ callId: string; /** Successful result payload, or an error envelope. */ result: unknown | { error: string; }; }; /** * Approve or reject a capability call that was invoked with * `requiresApproval: true` (client → agent). The platform shows a confirm * UI and forwards the user's decision via this command. * * @category Capabilities * @since 2.0.0 * @docLink packages/types/capabilities#capability-approve-command */ export type CapabilityApproveCommand = { type: "capability_approve"; /** Correlation token matching the {@link CapabilityInvokedEvent.callId}. */ callId: string; /** User's decision. */ decision: "approved" | "rejected"; /** Optional rationale stored alongside the decision for audit. */ feedback?: string; /** User ID of the decider (informational — framework enforces no policy). */ decidedBy: string; }; /** * Register one or more capabilities mid-session (agent → client). Mirrors * {@link CapabilityRegisterCommand} for the agent-initiated direction; used * when the runtime adds a capability after `configure` (e.g. when a flow * starts and injects flow-scoped tools). * * @category Capabilities * @since 2.0.0 * @docLink packages/types/capabilities#capability-register-event */ export type CapabilityRegisterEvent = { type: "capability_register"; capabilities: Capability[]; }; /** * Deregister capabilities by name (agent → client). * * @category Capabilities * @since 2.0.0 * @docLink packages/types/capabilities#capability-deregister-event */ export type CapabilityDeregisterEvent = { type: "capability_deregister"; names: string[]; }; /** * Notify the client that a capability has been invoked by the LLM (agent → * client). For client-side capabilities the platform's handler runs in * response to this event and replies with {@link CapabilityResultCommand}. * * @category Capabilities * @since 2.0.0 * @docLink packages/types/capabilities#capability-invoked-event */ export type CapabilityInvokedEvent = { type: "capability_invoked"; /** Correlation token for the eventual {@link CapabilityResultCommand}. */ callId: string; /** Name of the invoked {@link Capability}. */ name: string; /** Validated input payload (already conforms to the capability's `inputSchema`). */ input: unknown; /** In v2.0.0 the LLM is the only invoker. */ invokedBy: "agent"; }; /** * Notify the client that a render capability should be drawn (agent → * client). Emitted alongside {@link CapabilityInvokedEvent} when the * invoked capability's `render` block is populated. * * Clients without a render layer can fall back to the capability's * `render.fallback` markdown template. * * @category Capabilities * @since 2.0.0 * @docLink packages/types/capabilities#render-invoked-event */ export type RenderInvokedEvent = { type: "render_invoked"; /** Name of the invoked render capability. */ capabilityName: string; /** Props passed to the rendered component (already conforms to `inputSchema`). */ props: unknown; /** Correlation token shared with the matching {@link CapabilityInvokedEvent}. */ callId: string; }; //# sourceMappingURL=capabilities.d.ts.map