Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Quickstart: build an adapter

The “build a harness for spt-core” hello-world: take the reference mock adapter apart, register it, drive the contract with real commands, then swap in your own harness. No spt-core source required — the public contract is the manifest plus the spt api surface.

Integrating an agent harness and a building a driven surface (notifier, robot, sensor) are the same contract with a different manifest body. For the latter, read this page first, then Shells: getting started.

0. What an adapter is

A TOML manifest that declares what varies for your harness — how to spawn a session, which of your hook events fire which spt api command, how spt-core can read session history — plus whatever your harness already has (hooks, plugin config). Command templates are opaque strings: spt-core fills {key} placeholders and runs them. It never parses out a model, a tool list, or a flag. Your harness’s business stays yours.

1. Get the reference adapter

Every release ships the mock adapter’s source. With spt-core installed:

curl -fsSL -o mock-adapter.zip \
  https://github.com/SaberMage/spt-releases/releases/latest/download/mock-adapter.zip
unzip mock-adapter.zip -d mock-adapter

(Windows: irm -OutFile mock-adapter.zip https://github.com/SaberMage/spt-releases/releases/latest/download/mock-adapter.zip then Expand-Archive mock-adapter.zip mock-adapter.)

The interesting file is mock-adapter/manifest.toml. It is deliberately harness-agnostic — generic event names, a trivial mock-session helper standing in for a real harness binary.

2. Read the manifest

The header is the only mandatory section:

[adapter]
name = "mock"
kind = "harness"                  # or "shell" (a driven surface)
version = "1.0.0"
min_spt_core_version = "1.0.0"    # compat gate, readable before any install/update
hostable_types = ["LiveAgent", "ReadyAgent", "Worker"]

Inbound: your harness’s hook events, each firing one spt api command:

[hooks.SessionStart]
fires = "api seed --pid {parent_pid} --session-id {session_id} --adapter {adapter_name}"
reads = ["session_id", "parent_pid"]
can_inject = true     # this hook can surface text back into the agent's context

[hooks.Idle]
fires = "api state idle"
can_inject = false    # no inject channel -> spt-core uses its sentinel/relay fallback

can_inject is the load-bearing harness-varying fact: when a hook can’t put text in front of the agent, spt-core routes around it automatically.

Outbound: opaque session templates spt-core spawns with {key} placeholders filled:

[session.self]
command = "mock-session --id {id} --session-id {session_id}"
detach = true
keys = ["id", "session_id"]

A real adapter’s template is your harness’s full command line — model, flags, tools, everything — exactly as you’d type it.

The rest declares history access ([history]), env bridging ([env.*]), input injection ([inject]), and session identity ([identity]). Every section beyond [adapter] is optional; the manifest reference covers them all.

3. Validate and register

Two layers of validation, both mechanical:

  • Schema — your manifest must validate against manifest.schema.json. The schema is generated from the same code that parses manifests, so it is always current; closed vocabularies (adapter kinds, history strategies, update avenues, …) are enums in it.
  • Registrationspt adapter add parses, validates (including cross-field rules the schema can’t express), and registers in one step:
$ spt adapter add ./mock-adapter
ADAPTER_ADD:mock:Harness:Copy (registered)
ADAPTER_INSTALL_SKIP: no [update] avenue (manifest-only adapter)
$ spt adapter list
mock: Harness Copy active (from ./mock-adapter)

A bad manifest is rejected here with a message naming the offending field — nothing half-registers.

4. Drive the contract

Every machinery call your adapter makes carries --adapter <name> — that’s the rule that makes multi-harness nodes unambiguous. Ask spt-core what your adapter declared:

$ spt api --adapter mock --manifest ./mock-adapter/manifest.toml capability
LiveAgent
ReadyAgent
Worker

Now the harness-hosted startup flow, exactly what your SessionStart hook will fire (here with a stand-in pid):

$ spt api --adapter mock seed --pid 4242 --session-id demo-session-1
SEEDED:4242

seed records an ephemeral hand-off keyed by the parent process id; the session’s listener then consumes it with spt api … listen and holds the perch. That seed→listen pair is harness-hosted startup. (The other direction — spt-core spawning the session itself from your [session.self] template, then api bind — is spt-hosted startup. Both are in the spt api reference.)

5. Make it yours

  1. Copy manifest.toml, set name, version, and your real hostable_types.
  2. Point [hooks.*] at the events your harness actually fires, with honest can_inject values.
  3. Replace each [session.*].command with your harness’s real command line.
  4. Pick the [history] strategy your harness permits (binary that emits history → fetcher; transcript file on disk → locate_normalize; you push via api history-lognative).
  5. Validate against the schema, spt adapter add it, and fire the capability/seed calls above against your own manifest.

Building adapters against this contract is unrestricted and royalty-free — see the license split.

Next