Skip to content

docs: propose host adapters as a published extension point (ADR-0028/0029/0030) - #131

Open
adrianco wants to merge 1 commit into
pacphi:mainfrom
adrianco:docs/hermes-adrs
Open

docs: propose host adapters as a published extension point (ADR-0028/0029/0030)#131
adrianco wants to merge 1 commit into
pacphi:mainfrom
adrianco:docs/hermes-adrs

Conversation

@adrianco

Copy link
Copy Markdown

Documentation only. Three ADRs, all Proposed — no implementation is authorized or claimed. Follows the shape of #112.

The short version

I wanted agentic-kit to manage a fourth agent CLI (Hermes Agent, which is what I use to drive local models). The obvious route was ADR-0017's: an owner module and edits across nine files. Its own references section lists fourteen source files and eight test suites for one host.

That route makes every host your permanent obligation, including hosts you don't run and can't verify. So this proposes the smaller thing instead: publish the seam that already exists.

The seam is already built, and already partly adopted

Reading through 0016/0017/0018, every contract a host adapter needs is already specified and enforced:

Contract Validator
Host descriptor + capabilities validateHostAdapter
Cross-axis invariants validateRegistries (at construction)
Configuration lifecycle validateLifecycleAdapter — detect/plan/apply/verify/undo
Ownership + teardown ownership, mayUndo, undoOwnedValues
Worker execution validateExecutionAdapter, validateWorkerResult
Normalized facts normalizedFacts (schemaVersion: 1, provenance-bearing)
Guidance rows registry(customBlocks) — already user-extensible

And OPENCODE_LIFECYCLE_ADAPTER implements the full five-verb contract, driven from sync.mjs and x/host.mjs through the generic runLifecycle.

What's left is a last mile, and it's small:

  • adapter selection is a named import — runLifecycle({ adapter: OPENCODE_LIFECYCLE_ADAPTER, … })
  • status.mjs hand-rolls a per-host block importing eight functions from lib/opencode.mjs, even though detect already returns normalizedFacts

Both changes delete host-specific code rather than adding it, which is why they seem worth making even if no external adapter is ever registered.

ADR-0029 — the actual ask

An adapter is one module exporting one manifest, validated by the validators above. Four constraints make it safe to publish rather than merely convenient:

  1. Explicit kit.json registration, never naming-convention discovery. Scanning for ak-host-* would make an unrelated npm install sufficient to get third-party code executed inside ak on the next ak status — the fail-open pattern ADR-0023 exists to prevent.
  2. Disclosure, not a sandbox claim. In-process modules can't be sandboxed and the ADR doesn't pretend otherwise; the trust manifest names the package, resolved path, and version before any mutation.
  3. Capability caps — no canBePrimary, aqeProvider, or commandStatusline. Those three carry first-party obligations you can't discharge for code you don't ship. It's exactly the shape OpenCode already occupies, so it's a tested configuration, not new policy.
  4. Fail-closed per adapter. A broken third-party adapter is reported and skipped, never able to brick ak status. Built-ins keep throwing at construction, because a broken built-in is a build error.

Plus contract: 1 and an explicit statement that the surface is unstable while the package is alpha — publishing an extension point acquires an obligation, and that statement is what bounds it.

The package stays zero-runtime-dependency: an adapter is something the user installs and registers, never a dependency of @pacphi/agentic-kit.

ADR-0030 — conformance evidence

A contract never satisfied by code its authors didn't write is a guess. Hermes is a useful first consumer because it's awkward — it breaks five assumptions the built-in hosts share: YAML config, no npm package, plain-text output, no interceptable permission event, no ruflo ENABLE_* flag.

Carrying it needed one widening (a plain-text summary capture alongside createJsonlSummaryCapture) and one guard (npmRoot(host.install.npmPackage) in footprint/install.mjs, since hermes is the first host with no npm package). Everything else fit unmodified.

It would ship as an externally maintained adapter — not vendored here, and not asking you to take on hermes's correctness or NousResearch's release cadence. I'd maintain it.

Two findings are in the ADR because they'd otherwise resurface as bug reports:

  • hermes mcp add isn't safely idempotent — its overwrite prompt defaults to No on EOF (which is what a non-TTY ak sync supplies) and exits zero, so a bare re-add is a silent no-op that reads as convergence.
  • hermes -z sets HERMES_YOLO_MODE=1 by its own headless contract, so unlike OpenCode there's no permission event to intercept and no permission_required result to return. ADR-0030 discloses that at enable time rather than letting a hermes worker appear to carry a guarantee it doesn't have.

ADR-0028 — independent of both

The registry knows exactly one local provider, ollama, while a local model is normally an OpenAI-compatible loopback endpoint (MLX, LM Studio, llama.cpp, vLLM) — frequently under a name the user chose, which no vendor enumeration can cover. One generic local-openai row that deliberately claims less than ollama: no catalogue, no runtime probe, no digest, and no discovery facts this repo hasn't measured.

Useful to the hosts you already ship, and lands on its own merits regardless of what happens to 0029/0030.

Sequencing

docs/HOST-ADAPTER-EXTENSION-PROPOSAL.md §7 lays this out; each step is independently reversible, and declining at ADR-0029 costs nothing already spent. §8 is an honest case against the proposal, including the real cost: a published contract acquires consumers, and consumers constrain refactors.

Happy to take this in a different direction — including "no, absorb hermes in-tree the ADR-0017 way" or "not now" — the ADRs are written to be argued with rather than merged as-is. Unrelated: #130 is a small bug fix that currently blocks new test evidence on Homebrew-node macOS.

markdownlint and internal link checks pass; no source changes in this PR.

…0029/0030)

ADR-0029 asks for one thing: publish the seam that already exists. ADR-0016
specified every contract a host adapter needs, ADR-0017 proved them by using
them, and ADR-0018 generalized execution behind them — sync and host pick
already drive OpenCode through the generic runLifecycle. What is left is a
last mile: adapter selection is a named import, and status hand-rolls a
per-host block importing eight functions from lib/opencode.mjs. Both in-tree
changes delete host-specific code rather than adding it.

The alternative considered and rejected was absorbing each new host in-tree,
as ADR-0017 did for OpenCode. It works, and it is why the contracts exist —
but it makes every host a permanent obligation of whoever maintains this
repository, including hosts they may not run and cannot verify. The first
request for a fourth host is the right moment to decide that once rather than
four times.

Constraints that make the surface safe to publish rather than merely
convenient: explicit kit.json registration, never naming-convention discovery
(an unrelated npm install must not get third-party code executed inside ak);
disclosure rather than a sandbox claim, since in-process adapters cannot be
sandboxed; capability caps on canBePrimary, aqeProvider, and commandStatusline,
matching the shape OpenCode already occupies; fail-closed per adapter, so a
broken third-party adapter cannot brick ak status while built-ins keep throwing
at construction; and contract: 1 with an explicit statement that the surface is
unstable while the package is alpha.

ADR-0030 is the conformance evidence. Hermes Agent breaks five assumptions the
built-in hosts share — YAML config, no npm package, plain-text output, no
interceptable permission event, no ruflo backend flag — and carrying it needed
exactly one widening (a plain-text summary capture alongside the JSONL one) and
one guard (npmRoot on an absent npmPackage). It ships as an externally
maintained adapter, not vendored here.

ADR-0028 is independent of both: the registry knows one local provider,
ollama, while a local model is normally an OpenAI-compatible loopback endpoint,
frequently under a user-chosen name no vendor enumeration can cover.

docs/HOST-ADAPTER-EXTENSION-PROPOSAL.md is the companion product proposal,
following the shape of PR pacphi#112.

All three ADRs are Proposed. No implementation is authorized or claimed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant