Skip to content

Latest commit

 

History

History
201 lines (148 loc) · 10.7 KB

plugins.md

File metadata and controls

201 lines (148 loc) · 10.7 KB

Plugins

Bcode uses plugins for domain behavior rather than treating extensibility as a thin callback layer. Model providers, tools, authentication methods, commands, skills, workflows, session importers, visual adapters, and complete TUI surfaces can all be plugin-owned.

The initial plugin runtime loads native Rust dynamic libraries. Plugins are trusted native code, not sandboxed extensions. Invocation callback lifetime, unsafe-boundary, and performance migration requirements are defined in Native plugin invocation safety and performance architecture.

Tool catalog freshness

list_tools may return cache_policy: {"version": 1, "runtime_stable": true}. This is a plugin-owned promise that tool definitions and discovery policy remain immutable for the loaded runtime, including configuration and credentials. Omitted, false, or unknown-version policies are volatile. Plugins whose definitions depend on mutable credentials, configuration, environment, or service state must not opt in. Invocation authorization is always separate from discovery and is never cached here.

The server coalesces discovery for concurrent callers and reuses explicitly stable provider lists across turns. Volatile providers refresh at turn preparation. Each turn pins an immutable snapshot for model definitions, streamed producer routing, and tool batch routing; another session's discovery cannot change that mapping. Ordinary warm dispatch and streamed presentation do not initiate full-plugin catalog sweeps. Replacing the loaded runtime invalidates its snapshots, including when IDs/manifests are unchanged; a snapshot from a disabled/replaced runtime cannot authorize routing.

Failed providers are recorded separately and their stale lists are excluded. Successful stable provider lists remain reusable; a partial snapshot is not a complete catalog. Discovery uses tools.execution.preparation_timeout_ms for each provider, including admission waiting. The cancellable whole sweep has no competing deadline, so later provider waves cannot be starved by earlier timeouts. Concurrent refresh failures are shared only for matching deadline policies. Malformed response/host contract failures are permanent; unknown infrastructure and service failures remain retryable. Mixed failures still allow recoverable providers to recover. Before dispatching any sibling call, an unresolved requested name in a partial snapshot triggers recovery; known pinned names retain their original producer. Only complete discovery can establish that a requested name does not exist. Tools-disabled turns skip discovery, and invalid model targets fail before catalog retries. Cold unavailable discovery retries with cancellable host-preparation backoff before model startup. Partial discovery can expose healthy providers; unavailable requested names remain distinguishable from a complete-catalog tool-not-found result. A failed refresh does not mutate snapshots already pinned by earlier turns.

Bundled and discovered plugins

Release builds can statically bundle plugins into the Bcode executable. Bundling makes a plugin available; activation policy determines whether it is loaded. Bundled defaults can be disabled, and available plugins can be enabled explicitly.

Bcode also discovers manifest-driven native plugins from:

<current-directory>/.bcode/plugins
$XDG_CONFIG_HOME/bcode/plugins
~/.config/bcode/plugins
<directory-containing-bcode>/plugins

A discovery root may contain a plugin manifest directly or plugin subdirectories containing bcode-plugin.toml.

Inspect available plugins and services with:

bcode plugin list
bcode plugin services
bcode plugin check

Live terminal viewport control

Native TUI visual adapters may propose invocation input through viewport_input. The TUI calls this outside painting for active artifact-backed invocations, using transcript geometry rather than output length. The shell adapter subtracts its four-column inset and proposes a typed PTY resize. Recorded dimensions acknowledge the change; unchanged dimensions and terminal invocations produce no proposal. The host bounds batches to 64 inputs and retries at most every 250 ms during runtime updates. Adapters without this optional native capability retain presentation-only reflow.

Viewport control uses a persistent application client connection. The first successfully queued input claims an invocation for that connection; competing connections receive invocation_input_not_controller. Disconnect or detach releases control. Queue acceptance does not mean execution completed, and input IDs do not provide generic deduplication. Shell resize is convergent: repeated current dimensions produce neither PTY mutation nor recording frames. Other input protocols must define their own retry safety.

The shell plugin owns PTY resizing and ordered recording frames. Terminal replay is initialized from recording dimensions, never viewport defaults. Completed output continues to reflow through BMUX content projections without execution input.

Selection

Plugin loading and model-callable tool exposure are separate choices:

[plugins]
default = "bundled" # bundled | none | all
enabled = ["bcode.vim-edit"]
disabled = ["bcode.blims"]

[tools]
default = "agent" # agent | none | all
enabled = []
disabled = ["vim_edit.apply"]

A plugin may provide commands or UI without exposing a model-callable tool. Agent profiles apply an additional tool policy after plugin selection.

Manifest and service boundaries

A plugin manifest declares stable identity, runtime ABI information, concurrency policy, and versioned contributions. Depending on its domain, it may declare:

  • typed service interfaces and operations;
  • model-callable tools;
  • authentication providers and enrollment methods;
  • command-palette and slash-command contributions;
  • workflow blocks and templates;
  • event subscriptions;
  • structured visual adapters;
  • renderer-native TUI surfaces.

Cross-boundary requests and responses use Bcode-owned serializable contracts. Product hosts own discovery, loading, routing, lifecycle, and contract enforcement; the plugin retains its domain rules.

Build the example plugin

The hello-plugin example is a native dynamic library with a manifest, service, event subscriptions, authentication contribution, and serialized TUI visual adapter. It is primarily a smoke-test fixture, but it demonstrates the complete loading boundary.

Serialized visual presentation roles

Serialized TUI visual responses are versioned independently from tool/artifact schemas. Contract version 1 remains readable and supports concrete terminal foreground colors. Contract version 2 adds renderer-neutral role values for text, muted, accent, info, success, warning, error, and diff states.

When both role and foreground are present, the active renderer theme resolves the semantic role and takes precedence. The concrete color remains a compatibility fallback when no renderer theme is available. Unknown future response versions fail closed and fall through to the normal native or generic presentation path rather than being guessed. A response declaring semantic roles with version 1 is rejected.

The host includes the resolved theme fingerprint in serialized visual context and dynamic cache identity. This metadata may invalidate derived presentation only; plugins must not use it for routing, authorization, dispatch, or persisted outcomes.

cargo build -p bcode_hello_plugin

The built library name is platform-specific:

target/debug/libbcode_hello_plugin.dylib  # macOS
target/debug/libbcode_hello_plugin.so     # Linux
target/debug/bcode_hello_plugin.dll       # Windows

Place the library and an adjusted bcode-plugin.toml together under a discovery root, then run bcode plugin check before enabling it. The repository's smoke-native-plugin.sh demonstrates discovery, loading, service invocation, daemon routing, and event delivery.

Plugin-owned authentication

Plugins declare provider-owned authentication methods during activation. Bcode owns enrollment, vault custody, profile ownership checks, and invocation-time delivery.

Plugin implementations consume semantic credentials through the invocation context rather than constructing host secret keys or opening the vault:

let api_key = context
    .credentials()
    .require("example-provider", "api_key")?;

The provider and credential IDs come from the plugin's registered auth contribution. The host binds plugin ownership from the loaded plugin identity, so plugin code never supplies an owner ID, vault path, storage profile, or backend key. Missing-credential errors are intentionally secret-safe.

Native plugins remain trusted in-process code; this API enforces Bcode's supported ownership and custody architecture rather than claiming adversarial sandbox isolation.

Enabled plugins register their authentication providers and methods. The host owns hidden prompting, secure credential custody, ownership checks, and invocation-scoped delivery; plugins own provider identity, credential declarations, OAuth/device protocols, refresh, verification, and revocation.

bcode auth providers
bcode auth login <provider> [--method <method>]
bcode auth status <provider>
bcode auth logout <provider>

See Dynamic plugin authentication.

Plugin-owned session fork and clone

The bundled bcode.session-derivation plugin exclusively contributes /fork, /clone, and their palette entries. It owns prompt selection, default names, cutoffs, user-facing status, and post-success navigation. The host exposes only versioned generic command context, a typed bcode.session-derivation/v1 application service, generic plugin surfaces, and portable command effects.

Fork prompt discovery is generation-pinned and bounded. Renderer-neutral Markdown presentation is always returned as a fallback; the native TUI adapter adds direct selection. After selection, the plugin separately retrieves the complete canonical user message so truncated preview text never becomes authoritative draft state. Disabling the plugin removes these product workflows without affecting session storage, normal session use, or workflow-owned fixed-generation derivation.

Workflows and presentation

Plugins can contribute typed workflow blocks and immutable templates without owning the generic scheduler or durable workflow store. They can also publish structured presentation payloads with renderer-specific adapters and a generic fallback.

See: