@volter/supercode-harness-sdk
v0.3.56
Published
TypeScript client for Volter Harness's harness.v1 session and runtime service
Readme
Volter Harness SDK
This zero-dependency Node client launches supercode harness serve and exposes
the versioned persisted-session, local inventory, transfer, and live-runtime
primitives. The original low-level request methods remain available; managed
objects add cleanup, aborts, typed async iteration, and sequence-gap recovery.
Bounded session search
discover({ query: 'release' }) keeps its metadata-only search semantics. Opt in
to searching the native opening/latest message candidates as well:
const page = await client.discover({
query: 'release', search_previews: true, limit: 50,
workspace: process.cwd(),
});
// page.receipt.searched_previews === true; total_matched covers all matches.
// Keep the same query and pass page.next_cursor to read the next page.The same operation is available directly, without the fleet tool:
supercode discover --query release --search-previews --limit 50 --pageSearch is case-insensitive and combines metadata with the existing native
preview readers before pagination. It requires nonblank text and a positive
limit when supplied. Returned matches include topic and latest candidates,
even without include_topic_candidates. It does not search full histories,
images, or product-specific title overrides. Available candidates depend on
the native format: opening candidates currently cover Claude/Codex; latest
candidates use each format's existing reader. Codex's first history topic is
reused; later history entries are not searched.
The existing readers retain at most eight candidates per edge, capped at 4096
characters each; JSONL uses 512 KiB edges and an up-to-4 MiB latest fallback.
Workspace, profile, family and time filters run first. Exact counting still
requires checking previews across the eligible inventory on every page;
limit bounds returned rows, not total I/O. Memory holds the descriptor inventory,
Codex's first-topic map, the returned page, and one row's preview enrichment at
a time—not every session's enriched history. Narrow the query's inventory when
possible; this is an explicit one-shot operation, not a background search index.
Retained sessions.index.subscribe rejects search_previews: it must not
rescan the inventory on each live update. Use discover instead. The SDK also
requires the service's searched_previews: true receipt, rejecting older
services that would silently ignore the option. Ordinary metadata discovery
and live subscriptions remain unchanged.
Transports
The package root spawns supercode harness serve as a Node child process.
The client core is transport-agnostic: import it from
@volter/supercode-harness-sdk/core and supply any object that
implements HarnessTransport (start, write, close, running) over a
process you own. A browser host running Volter Harness as a WebAssembly Linux
program in a Worker supplies one over that program's stdio; the core imports
nothing from Node.
import { SupercodeHarnessClient } from '@volter/supercode-harness-sdk/core';
const client = new SupercodeHarnessClient({ transport: myTransport });import { SupercodeHarnessClient } from '@volter/supercode-harness-sdk';
const client = new SupercodeHarnessClient();
const { harnesses } = await client.listHarnesses({ workspace: process.cwd() });
const { next_cursor, sessions } = await client.discover({
limit: 50,
query: 'release',
workspace: process.cwd(),
});
// Discovery returns independently resumable root conversations by default.
// Tree/import tooling can opt into native child rollouts such as Codex subagents:
const completeTree = await client.discover({ include_child_sessions: true });
const mirror = client.session(sessions[0].locator);
const { session, window } = await mirror.loadWindow({
inline_media: 'metadata',
message_tail: 200,
});
const controller = new AbortController();
for await (const event of mirror.follow({
signal: controller.signal,
view: {
tailMessages: 500,
maxMessageChars: 16_000,
includeSubagents: false,
displayHistory: true,
},
})) {
// initial/full snapshots, append events, unsequenced `runtime_state`
// lifecycle events, and recoverable watch errors
console.log(event);
}
const runtime = await client.startManagedRuntime({
harness: 'codex',
cwd: process.cwd(),
policy: 'yolo',
});
// Open the same live runtime in a terminal without exposing its bearer token.
const { launch } = await runtime.terminalInstructions();
runtime.on('event', (event) => console.log(event.type, event.raw));
await runtime.sendInput('Explain this screenshot.', {
imageUrls: ['data:image/png;base64,...'],
});Hosts that provide their own terminal or tmux transport can start a new native interactive session without duplicating harness command-line knowledge:
import { createNativeInteractiveStart } from '@volter/supercode-harness-sdk';
const { initialInput, launch } = createNativeInteractiveStart({
harness: 'codex',
cwd: process.cwd(),
prompt: 'Inspect the current build and explain any failures.',
policy: 'default',
});
const session = await terminalHost.launch(launch); // spawn(program, arguments), never a shell command string
await terminalHost.sendInput(session.id, initialInput);The persistent TUI starts before the initial prompt is submitted through its owned PTY, so prompt
text never appears in argv or a process listing. Native interactive start currently covers Claude
Code and Codex; unsupported harnesses fail explicitly. The older createNativeStartLaunch() helper
remains available for hosts that intentionally use a positional prompt.
A discovered session that is running right now carries live_status and
delivery, the door a message reaches it by (runtime, native, hook or
stored). Claude Code publishes busy/idle; current Codex versions may not
keep a rollout file open while the TUI is active. A local terminal embedder
can pair Volter Harness with the retired package/native-host.js to retain an
unresolved candidate privately and resolve its exact durable ID plus idle proof
only when the user explicitly requests a handoff:
const outcome = await client.messageSession(sessions[0].locator, 'rebase onto main, please');
if (outcome.delivered_to_bus) console.log('handed to', outcome.target.name);
else console.log('refused:', outcome.refusal.reason, outcome.refusal.message);
const advisory = outcome.inbound_controls?.advisories[0];
if (advisory) {
console.warn(advisory.message);
// Only after an explicit user decision:
await client.configureHarness(
'claude-code',
[advisory.recommendation.change],
outcome.inbound_controls.revision,
);
}A refusal (not_live, identity_mismatch, delivery_failed, too_long,
invalid_sender) is a normal result, not a thrown error. delivered_to_bus
is deliberately the strongest claim available: the text reached the receiving
session's inbox, that session's own inbound-message controls decide whether it
is read, and it shows up in the mirror through the followed transcript — often
seconds later — rather than as a turn you own. Delivery spawns a one-shot
headless Claude restricted to the two documented cross-session tools; Volter Harness
does not write the receiver's Unix socket, whose wire frame is undocumented.
harnessSettings('claude-code') provides the same user-level preflight report
without sending. Managed, project, or command-line policy may still override
the user setting for a particular target process. Choosing accept trusts
messages from the user's other Claude Code sessions; the receiving session's
ordinary tool and permission controls still apply.
Authentication remains native to each coding harness. A desktop or local-web host can request a redacted report and then execute the returned plan in its own visible terminal:
const report = await client.harnessAuthenticationMethods('codex');
const plan = await client.beginHarnessAuthentication('codex', {
environment: localBrowserAvailable ? 'local_browser' : 'headless',
cwd: workspace,
});
await terminalHost.run(plan.launch);
const verified = await client.verifyHarnessAuthentication('codex');The browser-capable plan lets the native CLI open its own sign-in page. Codex's headless plan uses its documented device-code flow. No plan contains a token, and the service never owns the native sign-in process.
Discovery covers Claude Code, Codex, Gemini CLI, Grok, OpenCode, Pi, and
Volter Harness's native store. query is case-insensitive across harness, id,
title, workspace, and model; next_cursor is opaque and may be supplied as
cursor on the next call. workspace matches sessions recorded in exactly that
folder; add workspace_subtree: true to match the folder and every folder under
it (Teams sync rules use this). loadWindow bounds message history and can replace
inline data URLs with media_reference metadata. message_tail cannot be
combined with message_offset or message_limit. Every bounded result also
returns a full-session summary, so first/last message and completion state do
not change merely because older message bodies were omitted from the window.
For Hermes, session.ended_at and session.end_reason expose its recorded session
end, including terminal Kanban workers without channel bindings. A missing end
is unknown; summary.end_of_turn alone does not establish session or task completion.
The window's older_items/newer_items count omitted normalized conversation
and tool entries, allowing a renderer to disclose truncation exactly.
For native Claude JSONL with include_subagents: false, explicit loadWindow
requests use a read-only index and the existing graph/record decoders. Counts,
summaries, tool ordering and media policy are unchanged. It still scans the
source; retained memory is record metadata plus native residue, the largest
decoded assistant group, and requested/summary payloads—not the whole transcript.
Other formats and recursive requests retain their existing loaders.
Claude followers outside display_history can normalize simple appended
message records without re-decoding old payloads. They compare every prior byte
against the retained verbatim source; tools, chunk extensions, branches,
rewrites, partial records and child-tree changes use the canonical loader.
This reduces repeated decode work, not retained full-history memory or all
prefix I/O. The existing event, recovery and polling contracts are unchanged.
Managed runtime iterators end when the native transport closes or when the
parent client is closed. Terminal transport errors arrive through the common
event stream (and the non-special runtimeError event); native EOF is a
typed closed event. A closed managed runtime rejects further input.
A request timeout or AbortSignal rejects the caller promptly; it does not cancel
a native launch already executing in the service. For startRuntime,
resumeRuntime, and the deprecated persisted-resume attachRuntime (including
their managed variants), the SDK retains ownership of an abandoned request and
closes its exact connection if a successful reply arrives later. A runtime already
delivered to the caller stays caller-owned. attachExistingRuntime is not treated
as a new owned launch.
At most 64 runtime opens may await a service reply, including abandoned opens.
Further opens reject with SupercodeRpcError.code === 'RUNTIME_OPEN_CAPACITY'
until replies arrive; this is not a limit on live sessions. Receipts are never
evicted to make room for more launches. A transport exit/close clears them and
cleanup never restarts a dead service. If a late connection's close request fails,
the client emits runtimeCleanupError with (error, { connection }); hosts should
surface that failure rather than claim the runtime was reclaimed. Cleanup is not
a guarantee when the transport or service cannot acknowledge it, and this does not
retroactively recover connections abandoned by an older SDK.
The same late-reply ownership rule applies to transcript follows, activity
subscriptions, and index subscriptions: an abandoned open is unsubscribed when
its receipt arrives. Up to 64 subscription opens may await replies (separate
from runtime opens); further opens reject with SUBSCRIPTION_OPEN_CAPACITY.
Failed cleanup emits subscriptionCleanupError(error, { subscription }).
ManagedRuntime.close() is single-flight and records success only after the
close RPC succeeds or a terminal runtime event confirms shutdown. A cancelled
or failed close remains retryable; mutations are refused while a close is in
flight. Event iterators detach their listeners on abort, return, or runtime
termination, including when no subsequent event arrives.
Startup has a deadline covering transport startup and the capability handshake.
Closing during startup rejects its waiter, and retry waits for transport teardown.
Events are scoped to the transport generation: a departing process cannot feed
messages into or disconnect its replacement. Pre-aborted requests do not start
a service. Custom transports must ensure close() also cancels any pending
resource creation in their start() implementation.
For an ACP agent, pass a stable harness id, protocol: 'acp', and a launch
command. OpenCode can join a running TUI/server only when that process was
started on a known URL; pass it as base_url. Persisted continuation is
resumeRuntime; attachExistingRuntime is reserved for a genuine live
endpoint attach and reports unsupported capability honestly.
listHarnesses() is passive by default. Session counts are also opt-in because
some users have thousands of transcripts: pass { include_sessions: true } or
use supercode harnesses list --sessions. { probe: 'handshake' } opens an
empty protocol session, observes its transport through startup stabilization,
and closes it without submitting a model prompt or spending model tokens.
load() and follow() are read-only, so they report the same fidelity +
residue pair on the session itself and default to viewing at semantic
fidelity: a Claude Code transcript that has been compacted or resumed across
files routinely contains a live record whose parentUuid was pruned, and a
mirror needs to render it, not refuse it. The stitched-together view names
every dangling uuid in session.residue and reports
session.fidelity === 'semantic' — never continue from one. Pass
{ fidelity: 'byte_lossless' } to get the strict refusal instead; every
continuation/transfer method below is lossless-only and has no view mode.
Frontends should also pass view: it bounds the trailing message window and
each individual text field, avoids eagerly attaching Claude Code's child
transcript tree, and keeps human-visible Codex history across model-context
compaction. Omitting view preserves the complete legacy read contract.
Import/export/translate/branch/handoff methods return typed artifacts with an
explicit fidelity classification and named residue. Same-format
line-oriented exports replay captured native bytes; logical stores such as
OpenCode SQLite are classified separately and carry a recovery member. Claude
Code subagent and Grok bundle files appear in files; cross-format exports
preserve the portable conversation but cannot
claim that target schemas retain every source-native record. Structured
launches are argument arrays, never shell-quoted command strings. A handoff
artifact names its actual wire format, which can differ from
the destination harness. Grok has no import command: its handoff artifact is
Grok's own transcript, materializeSession writes the store entry, and the id it
returns replaces {materialized_session_id} in the structured launch.
reduceSession() is the rate-limit-rescue primitive. It strictly loads the
source, creates a bounded view with the core reversible reduction engine, and
writes the complete sidecar, reduction log, and working view to Volter Harness's
session store. The service reloads those files, verifies every pointer, and
byte-exactly inverts the view before returning a ReductionReceipt and target
bootstrap prompt. A small or non-lossless source is refused; no optimistic
fallback is returned.
Native agent configuration (Node hosts)
@volter/supercode-harness-sdk/configuration exports
createAgentConfigurationHost({ sources }). Each trusted source supplies a key,
harness, optional native home, display/scope labels and an explicit writable
capability. Browser requests supply only the source key, one { key, value }
change and the revision returned by inspect(source); they cannot choose paths
or executables. configure(source, change, expectedRevision) rereads the native
value before returning saved: true. value: null removes the override.
The initial adapters expose Hermes model/provider via hermes config set/unset
and Claude Code model/cross-session inbound policy via its native settings JSON.
Hermes needs a CLI version supporting those commands. Credentials are never read
or projected. Other harnesses return an explicit unavailable report. This is a
general capability contract; it does not claim all native preferences are editable.
The existing harnessSettings RPC remains the contextual interoperability API.
These are user-home settings, not per-conversation effective values. Environment, managed policy, workspace and launch overrides can take precedence; existing sessions are not restarted. Revisions reject observed stale edits and writes through this host are serialized by native path. Hermes owns its write operation: there is no cross-process compare-and-swap guarantee against a simultaneous native CLI editor. Native refusals and failed rereads are errors, never success receipts.
createAgentProfileHost (Node /configuration) composes the existing native
profile inventory/create doors with a host-owned subject-to-profile binding.
Its reports use the same AgentConfigurationPanel as native settings. Hermes
profiles are supported; other harnesses explicitly report unavailable activation.
The host supplies fixed source keys and optional new-profile names. The browser
selects discovered names or explicitly creates a separate profile; creation does
not clone credentials, install aliases, or alter the native active profile.
resolve(source) returns SDK constructor options, discovery homes and an opaque
configuration-home identity. Pass those options to the resident's own
SupercodeHarnessClient for both start and resume. Default returns no overrides.
A selected profile that disappears fails closed. configuration(source) returns
the existing native settings host for that home (null for the shared default).
The embedding host must serialize binding changes with runtime startup, reject
switches during active turns and retain separate resume records per identity.
Profiles scope credentials, configuration and storage; this is not a claim of
per-session model overrides or isolation from inherited provider environment.
For resource inspection, construct a client with the resolution's clientOptions,
pass it through scopeAgentResourceClient(client, resolution), and pass
resolution.sourceOptions into the shared source-inventory host. Hermes native
inventories enumerate sibling profiles: this adapter keeps skills under the
activated home's skill root and selects that home's default memory store.
These are trusted host options, not browser-supplied filesystem paths.
MCP doors
@volter/supercode-harness-sdk/mcp-doors is where each harness accepts an MCP server,
transcribed from its inventory row, and a writer per door:
| Harness | Door | What mountMcpServer writes |
|---|---|---|
| claude-code | flag | <home>/mcp-<name>.json, .mcp.json-shaped; the launch takes --mcp-config <file> |
| codex | toml | [mcp_servers.<name>] in <home>/config.toml (home is $CODEX_HOME), idempotently |
| opencode | json | mcp.<name> in <home>/opencode.json |
| pi | none | nothing: pi ships no MCP client |
| hermes, openclaw, any other | acp | nothing; returns the mcpServers entry for session/new |
import { mountMcpServer } from '@volter/supercode-harness-sdk/mcp-doors';
const mount = mountMcpServer({ harness: 'codex', name: 'retake', command: 'supercode-retake', args: ['mcp'], home: process.env.CODEX_HOME });
// mount.door === 'toml'; mount.path names the file writtenWith url (and optional headers) instead of command, the harness connects to a streamable-HTTP
server already running and spawns nothing: Claude Code's file names it {type: "http", url}, Codex's
block url = …, OpenCode's entry {type: "remote", url}. The acp door carries stdio servers only
and refuses a URL.
The per-door pieces (writeMcpConfigFile, serverToml, upsertTomlBlock, opencodeEntry,
acpEntry, …) are exported for a caller that mounts several servers through one door.
Native management host adapters
The ./management Node entry exports createAgentSetupHost for declared native
Hermes setup/configuration/messaging/pairing/memory commands. The embedding host
supplies trusted profile resolution and a structured Terminal launcher returning
{ wait, cancel, close }. Reports contain operation/status, not command paths or
environment. Tool exit never substitutes for runtime or authentication verification.
readAgentMemoryDocument and writeAgentMemoryDocument operate on documents from
a trusted scoped native inventory. Supported regular UTF-8 Markdown files are
bounded to 1 MiB; edits require a SHA-256 revision and verify native readback.
These wrap the harness's file representation, not a new memory service.
createAgentProfileHost.remove deletes a selected non-default native Hermes
profile after revision checking and resets the binding. Hosts must protect any
other application bindings before allowing deletion.
