Extensions and providers
01A package can plug into the build: run a step, transform a stage, or hand a task to an agent. This page explains how such plug-ins are declared, how a project turns them on, and how to see which ones ran and why.
vibe extensions --path hello-vibe
Extensions
→ [0] @vibe/package/skill/org.vibevm.world/wal/wal-status — point=phase:package handler=builtin provider=org.vibevm.world/wal@1.0.0 tier=preset state=effective
1 extension declaration(s), 1 effective
Points and contributions
03The lifecycle exposes named extension points, strings of the form family:name. The phase: family, the group of points that share one prefix, is the nine phases. The slot: family names places inside a phase where a stack or a discipline expects to plug something. The compile: family is the boot compiler's own pipeline, where a package may transform the text an agent will read. A contribution binds a handler to a point and is declared in a manifest as an [[extension]] table, in a package or in the project itself.
04 An extension point is a string<family>:<name>. Three families:phase:— the default-lifecycle phases andclean(project-scoped moments:phase:build,phase:create, …);compile:— the compiler's two tiers: the §7.2 staged positions (compile:source,compile:document,compile:lane,compile:emitted) and the §7.4 pass tier's single pointcompile:pass, whose position inside the pipeline is carried by the contribution'spass = { … }declaration;slot:— the per-package materialisation moments that already exist as PROP-020 (slot:pre-install,slot:post-install), re-stated in this vocabulary without changing their semantics, timing, or manifest spelling ([[hooks]](../modules/vibe-workspace/PROP-020-install-hooks.xml#manifest) keeps working verbatim; it is now sugar for twoslot:contributions).
05
A contribution binds a handler to a point. It is declared as an [[extension]] table — in a package manifest (the package ships and offers the behaviour) or in the project manifest (the host adds its own). The shape is one grammar for every family:
06A contribution carries an id, which is also the key a project uses to disable or replace it; the same handler may be bound several times under different ids with different configuration.
07 Fields:id(execution identity — Maven's execution id; also the disable/override key),point,handler(§6),config(free TOML delivered to the handler verbatim — Babel's plugin options),auto(§3.3), and optionallywhen(a guard:kind = "rust-stack"-style applicability conditions, future work behind this anchor).
Five kinds of handler
| Kind | What runs |
|---|---|
builtin |
a handler compiled into vibe, such as a logger |
script |
a shell or PowerShell script shipped by the package, resolved by platform |
binary |
a program the package delivers and vibe builds on install |
native |
a dynamic library loaded in process through a C ABI, from an exact prebuilt for the platform |
agent |
a prompt: work handed to the hosting agent, or to a configured model provider when a person runs vibe at a terminal |
09 A contribution'shandler.kindis one of five. Two wrap machinery that exists; three are new. All five are legal at everyphase:/slot:point; thecompile:family acceptsbuiltinandnativeonly (§8.5 explains why).
10Every handler receives one context envelope, a versioned JSON document that names the project, the phase, the artifacts produced so far and the configuration of the contribution, and answers with a reply envelope that may declare new artifacts. The envelope is how phases talk to each other.
11 Every handler, in every family, receives one context envelope — a versioned JSON document (the wire form forscript/binary/nativehandlers; builtin handlers get the same data as a Rust struct; agent handlers get its prose projection). Modelled on what Maven hands a Mojo — MavenProject, MavenSession, MojoExecution, per-parameter configuration — translated to our world and extended with what the owner predicted: «в стадии будет попадать много больше, чем их просто название».
12A native handler is a dynamic library with exactly four C symbols; the request and the reply cross that boundary as versioned JSON, one family per library, and the vibe-ext crate gives an author safe macros so that no unsafe is ever written by hand. A closed-source handler ships prebuilt for each platform; the exact prebuilt wins, declared source may build instead, and absence refuses rather than skips. A compile: transform runs inside lane regeneration, so it may only be a builtin or an admitted native handler, never a script, a binary or an agent. A contribution that wants the low-level pass tier says so with compiler_internals = true, and then still needs the host's activation.
13 A native handler is a cdylib exposing exactly four ABI-1 C symbols:vibe_ext_abi,vibe_ext_manifest,vibe_ext_invoke, andvibe_ext_free. The Rust ABI is never the wire; versioned JSON request/reply roots carry lifecycle, compiler or mechanism-family values, one family per image. ABI/manifest admission precedes invoke, replies are bounded, plugin panic is contained, and the host frees owned reply memory exactly once. Evidence:fd81a003,bfaea140,9f7b8854,ed6e7c2a,9465291e,24b1fe4a.
14vibe-extpublishes safe lifecycle, compiler and mechanism-family author macros over the same four ABI-1 symbols; one cdylib belongs to one family. Authors never touchunsafe. Evidence begins atbfaea140and extends through24b1fe4a.
15 Closed-source plugins ship prebuilt. The exact current-platform prebuilt wins; otherwise declared source may build under the provider root, and absence refuses rather than skipping. Prebuilt bytes are ordinary package content and both paths enter the same record, immutable-image, ABI/schema and loader admission laws. Platform projection occurs before build/load. Evidence:1baac652,269bec0d,bb50aeab.
16 Acompile:transform runs inside lane regeneration, potentially per document. Script/binary spawn and agent nondeterminism are therefore forbidden: compiler contributions execute only asbuiltinor admittednativehandlers. R5/R6 retain this closed production dispatch.
17 A contribution enters the pass tier by declaringcompiler_internals = trueon its[[extension]]table — the owner-mandated flag, a single conspicuous boolean whose presence means "this plugin asked for the low-level API". Without it,pass = …is a validation error. With it: host activation is always required; the activated pass list and positions bind the generated artifact header and freshness; and narration names each internals plugin. Grammar and refusal landed inb2a6efb5; retained owner-plan carriage landed throughcf51582a.
Switching contributions on
18Installing a package is the consent to run its contributions: a dependency's phase and slot contributions are active as soon as the package is installed. The consuming manifest can activate a contribution that is not automatic, disable one by its id, or override its configuration. There are no consent dialogs at run time; there is total transparency instead.
19 Host activation and override. The consuming manifest activates a dependency's non-auto contribution, or re-configures an auto one, by reference:[[extensions.use]] ref = "org.vibevm.x/y#xml-squeeze"with optionalconfigoverride;[extensions] disable = ["org.vibevm.x/y#announce"]turns any contribution off. The pluralextensionsnamespace is the host-control surface; singular[[extension]]remains only the declaration table nested in the manifest that provides the contribution. The reference key is<group>/<name>#<id>— stable, printable, greppable.
20 The owner's trade (2026-08-25): no consent dialogs, total transparency instead — debuggers, scanners, and internals specialists must be able to see instantly what the extension machine is doing. Observability is therefore a REQUIREMENT of the machine, specified with it, not a later courtesy:
21Within one point, contributions run in a fixed order: the built-in bindings of the effective preset first, then the packages' contributions in dependency order, then the project's own; the order is derived, never negotiated.
22 Within one extension point, contributions run in this order: (1) built-in bindings of the effective kind-preset (§4.5) — Maven's «packaging first»; (2) dependency-declared contributions, in lockfile package order (deterministic, no filesystem enumeration), then declaration order within a package; (3) host-manifest contributions and[[extensions.use]]activations, in declaration order. First-to-last, no reversals anywhere — one rule a user can hold in their head, against Babel's plugins-vs-presets special case (##NOT-FROM-BABEL).
Seeing what ran
23vibe extensions lists every declared contribution in the installed world with its point, handler and origin; --json gives the same to a scanner. Everything a contribution can do is declared, so a scanner needs no execution to audit a project. Every run prints the contributions it will execute before it starts, and a run can be traced pass by pass with --trace-compile into .vibe/trace/.
24 The extension registry is a query surface:vibe extensions(with--json) lists every declared contribution in the installed world — id, point, handler kind, providing package and version, config,compiler_internalsflag, auto/host-activated state, disabled state, and for natives the artifact path + build state + content hash. One command answers "what runs on my project and who brought it".vibe list/showmark internals-bearing packages, as §7.4.3 already requires.
25 Lifecycle-owned compilation is traceable, LLVM-style: the landed R3.4 surface is directinstall, every default phase/chain,updateandreinstall; compile sites owned byinit, publish staging and uninstall remain explicitly outside this epoch until they gain their own command-owned JTD report boundary. On the landed surface,--trace-compile(or selected-manifest[compile] trace = true) attempts to certify the compiler's generated JTD IR after every successful pass while the run budget and writer permit, the-print-after-allgenre, and aggregates every pass outcome/timing, the-time-passesgenre. The index records why a snapshot is absent (snapshot-skipped-budget,snapshot-failed, or pass/verifier failure), so observer refusal never becomes compiler failure. One project run lives under.vibe/trace/<run>/:index.jsonis the generated authority, while certified snapshots use a reversible Windows-safe name carrying the global sequence, encoded pass, scope kind, encoded scope label, encoded artifact id and occurrence ordinal; exact scope/attempt identity remains in the event→scope relation in the index. A name beyond the physical cap uses a digest suffix whose full identity remains in the index. Thus two parse documents, package units and node artifacts cannot collide, and adjacent certified snapshots answer “what did THIS pass change?” without a trace-only IR dialect.
26vibe tools lists the binaries and servers the installed packages brought, which is the registry of what a contribution of kind binary may name.
Providers
28A contribution of kind agent needs someone to run its prompt. Under an agent host, vibe parks the task for that agent and resumes when the declared outputs exist. At a terminal, vibe can call a configured model provider, the first one being any OpenAI-compatible endpoint, and it reads the provider's credentials only when a non-fresh agent step actually reaches the point of calling it. Every model-enhanced feature declares whether it is off, assisting or required, and off is the default.
29 The first provider id is exactlyopenai-compatible: synchronous object-safeLLMProvider::chat, generated epoch-1 Chat request/response, blocking bounded transport and provider-independent usage. User config owns provider/model/endpoint/token-file; selected project[llm]owns default provider/model and an optional credential env source. Project provider/model win independently; endpoint remains operator-owned. A nonempty project env source wins over a token file and fails honestly when absent. Keyed traffic requires HTTPS; keyless HTTP is literal loopback only; redirects are disabled, loopback bypasses ambient proxies, response bodies/timeouts are bounded, and keys/query/body/raw provider responses never enter diagnostics.
30 Each genuinely LLM-enhanceable feature declaresoff | assist | required; an undeclared enhancement mode isoff.offruns only the algorithmic implementation.assistmay call the configured provider and falls back to the algorithmic result with a visible degradation record on unavailable/failure.requiredis an explicit operator choice and fails with remediation when the paid enhancement cannot run. The existence ofrequirednever permits removal of the subsystem's algorithmic mode. A purehandler = { kind = "agent" }contribution is a separately declared agent workload, not an enhancement mode: once activated, its absence would be a silent skip and##AGENT-CLI/##AGENT-HANDSHAKEgovern it.
Edge cases and rules
31A contribution may carry a selector that limits it to matching files or targets, so a formatter binds to one language's sources and not to the rest of the tree.
32 Selectors (the Webpack loadertest-rule genre, 2026-08-25 audit): a contribution may carryapplies_to = { packages = ["org.x/*"], paths = ["vibevm/vibespecs/prompts/**"] }— declarative include/exclude scoping evaluated by the ENGINE, so a per-document transform (tier-1compile:source/document) or a per-slot moment runs only where declared, visible to §3.5's registry, instead of every plugin re-implementing filtering privately inconfig. Absent selector = applies everywhere its point fires, today's behaviour.
33A native handler crosses a C-and-JSON boundary, never the Rust ABI, so a library built by another toolchain version still loads.
34 8.1 The ABI is C + JSON, never the Rust ABI
35Enabled model work carries per-run ceilings on calls and tokens; a step that would exceed them stops rather than spends.
36 Each enabled enhancement or explicit agent workload carries per-run call, input-token and output-token ceilings. Narration names feature/contribution, mode, provider/model, reason, cache posture and ceilings before spend without exposing secrets. Completed state records provider-reported usage. Exceeding a ceiling follows the selected contract:assistfalls back;requiredor an explicit agent workload fails. Exact default ceilings and manifest grammar land with the first enhancement that consumes them; no global create budget exists.