Skip to content

API Stability & Deprecation Policy ​

This page is the authoritative reference for what an extension author may rely on, how the extension API is versioned, and how breaking changes are communicated. It governs the public @hapi/protocol/ext surface — the manifest schema, the three host interfaces, and the contribution-point types documented in Authoring Extensions.

v0.1 is pre-stable

The extension API is at version 0.1.0 (PLUGIN_API_VERSION). Under SemVer §4, a 0.y.z API is pre-stable and anything MAY change. This policy publishes an intent classification on top of that: surfaces marked stable carry a good-faith commitment to the deprecation process below; surfaces marked experimental may change — even break — in a minor release with only a changelog note. The full deprecation guarantee activates at 1.0.

Who this is for

At the current build-time-composition stage, first-party extensions are compiled into the single binary and the maintainer controls both sides — so this policy is not yet externally binding. It becomes load-bearing once the runtime filesystem loader ships and externally-authored extensions exist. It is published now so that loader has a contract to enforce and so the manifest's apiVersion field means something concrete. See Scope & third-party authorship.

Public API surface classification ​

Every export of @hapi/protocol/ext is classified below. Stable = the shape is settled and (where applicable) proven by one or more shipped extension migrations; changes follow the deprecation policy. Experimental = net-new, not yet exercised end-to-end, or deliberately loosely typed; may change in a minor release with only a changelog note.

Version primitives — stable ​

SurfaceKindClassificationNotes
PLUGIN_API_VERSIONconstStableshared/src/ext/version.ts — the canonical version constant
PluginApiVersiontypeStableLiteral type of the constant

Manifest contract — mostly stable ​

FieldClassificationNotes
id, version, apiVersion, displayName, description, defaultEnabled, entryPointsStableThe core third-party author contract. See the manifest schema.
configSchemaStableZod schema for the extension's own config; validated by host.config.get()
requiredAtBuildExperimentalv0.1-specific disable semantics (bypasses the enable check); behavior may change as the preferences UI lands
dependsOn, conflictsWithExperimentalDeclared but not enforced in v0.1 — reserved for a future resolver

Register signatures & config helpers — stable ​

SurfaceClassificationNotes
ServerRegister, WebRegister, CliRegisterStableregister(host) => Promise<void> | void
validateExtensionConfig, safeValidateExtensionConfig, InferExtensionConfigStableZod config-validation helpers

ServerExtensionHost ​

SurfaceClassificationNotes
notifications.addChannelStableProven by notifications-push / notifications-telegram
agents.registerStableWraps the shared AgentRegistry seam. The shipped agent backends (agent-codex / agent-gemini) exercise the CLI host's agents.register, not this one; no server-side agent extension ships yet — so this row is stable by virtue of the shared seam, not a server-side exerciser.
store.addTable, store.addMigrationStableProven by scheduler
config.get<T>()StableReads the extension's own extension_prefs row
events.subscribe, events.emitStableThe extension event bus (ExtensionEvent)
lifecycle.onStartup, lifecycle.onShutdownStableLong-running services start here, never in register()
logStableStructural ExtensionLogger
routes.addStable API, experimental typingProven by scheduler / watchdog / git-rpc / heartbeat / github-issues. The method is load-bearing; its HonoRouteFactory<TDeps = unknown> argument is deliberately opaque — narrow TDeps at the call site. The typing may tighten without an API bump.
capabilities.addExperimentalContribute a capability-catalog entry (#3752), gated on catalog.contribute. Server host only — the CLI/web hosts are stateless/singleton with no live-catalog assembler. The CatalogEntry shape is pre-stable (@hapi/protocol capabilityCatalog, #3741) and may tighten.
services.register, services.getStable API, experimental typingCross-extension handle registry (scheduler publishes, heartbeat reads); handles are unknown — narrow at the call site
late-bound accessors — getSyncEngine, getPushService, getNotificationHub, getSystemEventQueue, getRpcRegistry, getNodeRegistry, getFederationApiToken, getJobRegistry, getJobNodeId, getRawDb, getCoreConfig, getStore, getErrorLoggerStable API, experimental typingReturn unknown and may be null before boot completes — narrow at the call site and null-check. The set of accessors and their return types will tighten as core surfaces stabilize.

WebExtensionHost ​

SurfaceClassificationNotes
toolViews.addCompact, addFullInput, addResultStableProven by tool-views; the ToolViewLike component arg is narrowed to ComponentType<ToolViewProps> at the web layer
sessionDetail.addTab, addInfoField, addActionExperimentalNet-new slot infrastructure; the InfoField/Action slots ship renderable-but-unmounted (mounts land with future extensions)
preferences.addSectionExperimentalConsumption site is owned by the (unlanded) admin-drawer epic; do not rely on it yet
navigation.addRouteExperimentalNavigationRouteSpec still settling; wiring is consumed at router module-eval

CliExtensionHost ​

SurfaceClassificationNotes
commands.addStableProven by agent-codex / agent-gemini
agents.registerStableProven by the CLI agent backends
lifecycle.onSessionStart, onSessionEndStableHandler receives the shared Session

Contribution-point shapes ​

The argument/return shapes in shared/src/ext/contributions.ts inherit the classification of the host method that consumes them. Shapes for stable methods (NotificationChannel, CommandDefinition, CommandContext, AgentBackend, AgentBackendFactory, ExtensionTableSpec, ExtensionMigration, ExtensionEvent, ExtensionEventListener, ExtensionLogger) are stable. Shapes for experimental methods (SessionDetailTabSpec, SessionDetailInfoFieldSpec, SessionDetailActionSpec, PreferenceSectionSpec, NavigationRouteSpec) are experimental. ToolViewLike is an opaque unknown placeholder narrowed per-runtime.

Versioning semantics ​

The Extension API version is PLUGIN_API_VERSION in shared/src/ext/version.ts. It is distinct from a manifest's own version (the extension's release) and its apiVersion (the API version it targets); see the manifest schema.

The pre-1.0 rule (current — 0.y.z) ​

While the API is 0.y.z:

  • Additive changes do not bump the version at all. New contribution points and new optional manifest fields are added in place — this is the explicit behavior encoded in version.ts ("host interfaces may gain contribution points additively without an API-version bump").
  • Breaking changes to a stable surface bump the minor (0.1.0 → 0.2.0) and follow the deprecation policy.
  • Experimental surfaces may change or break in any release with only a changelog note.

The post-1.0 rule (activates at 1.0.0) ​

Once the API reaches 1.0.0, standard SemVer 2.0.0 applies to the stable surface:

BumpMeaning
major (1.0.0 → 2.0.0)A breaking change to a stable surface item (removal, rename, signature change)
minor (1.0.0 → 1.1.0)An additive change (new contribution point, new optional field), OR the removal/break of an experimental item
patch (1.0.0 → 1.0.1)A bug fix, documentation change, or type narrowing with no signature change

Deprecation policy ​

Modeled on the Node.js deprecation policy: a deprecation moves through three stages before removal.

  1. Documentation-only — the surface is marked @deprecated in its JSDoc, listed in the Active deprecations table with a stable HAPI-EXT-DEPxxxx identifier, and noted in the version history. No runtime change.
  2. Runtime warning — using the surface logs a once-per-process warning naming the deprecation ID and the migration path. The surface still works.
  3. Removal — the surface is deleted.

Cadence & notice period ​

Surface classRequirement before removal
StableAt least one released version at stage 1 (documentation-only) and at least one released version at stage 2 (runtime warning), with a minimum of ~4–6 weeks at documentation-only. Removed only at a minor bump pre-1.0, or a major bump post-1.0.
ExperimentalMay be removed in any release with a changelog note. No staged requirement.

Migration-guide requirement ​

Every stable-surface deprecation MUST ship a migration entry — before/after code — in Migrating to Extensions (or a dedicated deprecations section), referenced by its HAPI-EXT-DEPxxxx ID. Experimental-surface changes are noted in the version history but do not require a full migration guide.

Active deprecations ​

None. No @hapi/protocol/ext surface is deprecated as of 0.1.0.

Deprecation IDSurfaceStageSinceRemoval targetMigration
——————

Compatibility matrix ​

The matrix records which Extension API versions are compatible with which Hapimatic releases. An extension declares the API version it targets via manifest.apiVersion; the host publishes PLUGIN_API_VERSION.

Extension API apiVersionCompatible Hapimatic releasesStatus
0.1.xEvery release shipping PLUGIN_API_VERSION 0.1.0 (current)✅ Supported

Compatibility rule ​

An extension declaring apiVersion X.Y.Z is compatible with a host at PLUGIN_API_VERSION A.B.C when:

  • Pre-1.0 — X.Y == A.B exactly. Any 0.y API is compatible only with a host on the same 0.y (because pre-1.0 minor bumps may be breaking).
  • Post-1.0 — X == A (same major) and host B ≥ extension Y (the host is at least as new as the extension, within the same major).

Enforcement is not yet live

This rule is documentation-only today. Runtime enforcement — rejecting an incompatible manifest.json at load time — is delivered by the runtime filesystem loader (epic #1264 Phase 5). At the current build-time-composition stage every extension compiles against the same PLUGIN_API_VERSION, so a mismatch cannot occur.

Update process ​

The matrix is a release-time artifact. Whenever PLUGIN_API_VERSION changes, add a row here (and mark superseded rows) as part of the same change that edits shared/src/ext/version.ts. The matrix grows meaningfully once multiple API versions and externally-authored extensions coexist.

Version history ​

A changelog of PLUGIN_API_VERSION, newest first. Records the semantics above as applied to each release.

API versionDateChange
0.1.02026-07Initial pre-stable release. Manifest schema, three host interfaces, contribution-point types, register signatures, config validators. Classification and this policy established.

Scope and third-party authorship ​

Hapimatic's extension system ships today as Pattern 1 (build-time composition): extensions are TypeScript modules compiled into the single binary, and there are no externally-authored extensions yet. This policy is authored now, ahead of that audience, on purpose:

  • The subject of the policy — the @hapi/protocol/ext surface and the manifest.apiVersion field — exists today and is exactly what the future runtime loader will validate against. Publishing the version/compatibility rules now gives that loader a spec to build to.
  • The classification and compatibility matrix are the inputs a future extension marketplace / discovery surface renders (API maturity, version compatibility, deprecation notices).

Until the runtime loader ships, treat the stable/experimental classification as a maturity signal and a commitment of intent, not a runtime-enforced guarantee. See the Extension System Architecture epic for the Pattern 1 → Pattern 2 roadmap.

The self-hosted core is licensed under AGPL-3.0.