Reference#
This page lists Conduit's public surface: the package entrypoints, types, adapter factories, host profiles, lifecycle helpers, conformance API, and the repository's npm scripts. Concepts explains how the pieces fit together.
Entrypoints#
| Import | Exports |
|---|---|
@kontourai/conduit |
Core types, createManifestHarnessAdapter, createInProcessHostAdapter, the Claude Code, Codex, OpenCode, Strands, and VoltAgent profiles and factories, bindAdapterLifecycle, and the conformance API |
@kontourai/conduit/pi |
piCapabilities, createPiAdapter, createPiLifecycleHandlers, and Pi binding types |
@kontourai/conduit/kiro |
kiroCapabilities, createKiroAdapter, evaluateKiroHook, UnsupportedKiroHookEventError, and Kiro hook types |
@kontourai/conduit/conformance/host-conformance.json |
The committed reference conformance report |
@kontourai/conduit/schemas/host-conformance.schema.json |
JSON Schema for conformance reports |
All code entrypoints are ES modules with TypeScript declarations. The package requires Node 22 or newer.
Core types#
| Type | Shape |
|---|---|
IntegrationFidelity |
"native" | "approximated" | "observational" | "static-only" | "unavailable" |
LifecyclePhase |
"session-start" | "before-model" | "before-tool" | "after-tool" | "stop" |
AssetKind |
"skill" | "agent" | "hook" | "prompt" | "command" | "context" |
LifecycleInfluence |
{ decision, contextInjection }, both IntegrationFidelity |
HostCapabilities |
lifecycle (per phase), optional influence (per phase), contextInjection and blocking (deprecated but required), and install (per asset kind) |
NormalizedHostCapabilities |
lifecycle, influence, and install, all explicit |
PortableAsset |
{ id, kind, content, targetHint? } |
InstallationReceipt |
{ hostId, installed: { id, kind, digest }[], skipped: { id, kind, reason }[] } |
LifecycleEvent |
{ phase, sessionId, context? } |
LifecycleOutcome |
{ decision: "allow" | "deny" | "observe", reason?, modelContext? } |
AgentHostAdapter |
{ id, capabilities(), install(assets), project(event, outcome) } |
install and project return promises. An installation receipt and its
installed and skipped arrays are frozen, but the freeze is shallow: the
entries inside the arrays are not frozen.
Adapter factories#
createManifestHarnessAdapter#
createManifestHarnessAdapter(options: ManifestHarnessOptions): AgentHostAdapter
ManifestHarnessOptions is { id, capabilities, resolveTarget(asset), write(target, content) }.
resolveTarget returns a target string or undefined. write may return a
promise, which install awaits.
install calls resolveTarget for each asset, in order. It skips the asset
with reason unsupported if the kind's install fidelity is unavailable, or
unmapped if no target was returned. Otherwise it calls write and records the
asset's digest.
createInProcessHostAdapter#
createInProcessHostAdapter(options: InProcessHostOptions): AgentHostAdapter
InProcessHostOptions is { id, capabilities, installAsset?(asset), applyOutcome(event, outcome) }.
install skips an asset with reason unsupported if installAsset is missing
or the kind's install fidelity is unavailable, and otherwise calls
installAsset. project awaits applyOutcome and then applies outcome
projection to its result.
Named factories#
Each named factory fixes the adapter id and capability profile and takes only
the caller's bindings.
| Factory | Entrypoint | id |
Binding type |
|---|---|---|---|
createClaudeCodeAdapter |
@kontourai/conduit |
claude-code |
LocalHarnessBinding |
createCodexAdapter |
@kontourai/conduit |
codex |
LocalHarnessBinding |
createOpenCodeAdapter |
@kontourai/conduit |
opencode |
LocalHarnessBinding |
createPiAdapter |
@kontourai/conduit/pi |
pi |
LocalHarnessBinding |
createKiroAdapter |
@kontourai/conduit/kiro |
kiro |
LocalHarnessBinding |
createStrandsAdapter |
@kontourai/conduit |
strands |
FrameworkBridge |
createVoltAgentAdapter |
@kontourai/conduit |
voltagent |
FrameworkBridge |
LocalHarnessBinding is { resolveTarget, write }. FrameworkBridge is
{ installAsset?, applyOutcome }.
Host profiles#
Each profile is exported as a frozen HostCapabilities constant:
claudeCodeCapabilities, codexCapabilities, openCodeCapabilities,
strandsCapabilities, and voltAgentCapabilities from the main entrypoint,
piCapabilities from /pi, and kiroCapabilities from /kiro.
Lifecycle, decision, and context fidelity for every phase is listed in the host conformance matrix. Install fidelity per asset kind:
| Profile | skill | agent | hook | prompt | command | context |
|---|---|---|---|---|---|---|
| claude-code | native | native | native | native | native | static-only |
| codex | native | unavailable | approximated | static-only | unavailable | static-only |
| opencode | native | native | native | native | native | native |
| pi | native | unavailable | native | native | approximated | static-only |
| kiro | native | native | native | native | unavailable | static-only |
| strands | approximated | approximated | native | native | approximated | native |
| voltagent | approximated | native | native | native | approximated | native |
Notes recorded in the source and the committed evidence:
- Codex exposes no lifecycle surface except
before-tool, which can block and inject context, and anobservationalstop. Itshookinstall isapproximatedbecause a copied project hook is not active until Codex trusts its exact definition. - Pi targets Pi 0.80.6 and later extensions. It can block at
before-tooland inject context atbefore-model. - Kiro targets Kiro CLI 2.13 and later custom-agent hooks, not standalone
IDE hook files. It can block at
before-toolandstop, and inject context atsession-startandbefore-model. - Strands and VoltAgent are caller-bound. Conduit does not depend on either framework package.
Lifecycle binding#
bindAdapterLifecycle#
bindAdapterLifecycle(options: LifecycleBindingOptions): () => void
LifecycleBindingOptions is { adapter, registrar, evaluate(event) }, where
registrar is a LifecycleRegistrar:
interface LifecycleRegistrar {
register(phase: LifecyclePhase, handler: LifecycleHandler): void | (() => void);
}
type LifecycleHandler = (event: LifecycleEvent) => Promise<LifecycleOutcome>;
For each phase whose lifecycle fidelity is not unavailable, it calls
registrar.register once with a handler that runs evaluate and then
adapter.project. The handler throws if it receives an event for a different
phase. The returned function calls every disposer that register returned, in
reverse registration order. Disposal happens once: calling the returned function
again is a no-op.
Pi entrypoint#
createPiLifecycleHandlers#
createPiLifecycleHandlers(options: PiExtensionBindingOptions): PiLifecycleHandlers
PiExtensionBindingOptions is { adapter, sessionId(phase, event, context), evaluate(event) }.
sessionId returns the host's session identity. Each handler builds a
LifecycleEvent with context: { hostEvent: event }, runs evaluate, and
projects the result through the adapter.
| Handler | Register on Pi event | Phase | Returns |
|---|---|---|---|
sessionStart |
session_start |
session-start |
nothing |
beforeAgentStart |
before_agent_start |
before-model |
{ systemPrompt } with the projected modelContext appended after a blank line, or undefined when there is none |
toolCall |
tool_call |
before-tool |
{ block: true, reason } when the projected decision is deny, otherwise undefined |
toolResult |
tool_result |
after-tool |
nothing |
agentSettled |
agent_settled |
stop |
nothing |
Installation goes through createPiAdapter, which needs explicit
resolveTarget and write bindings.
Kiro entrypoint#
evaluateKiroHook#
evaluateKiroHook(options: KiroHookBindingOptions, event: KiroHookEvent): Promise<KiroHookResult>
KiroHookBindingOptions is { adapter, evaluate(event) }. KiroHookEvent is
the decoded hook JSON: hook_event_name, cwd, session_id, and the optional
prompt, tool_name, tool_input, tool_response, and
assistant_response. The caller reads and parses stdin, then writes the result's
stdout and stderr unchanged and exits with exitCode.
Hook events map to phases as follows. session_id becomes the event's
sessionId, and the hook event is passed as context.hostEvent.
| Kiro hook | Phase |
|---|---|
agentSpawn |
session-start |
userPromptSubmit |
before-model |
preToolUse |
before-tool |
postToolUse |
after-tool |
stop |
stop |
Any other hook_event_name is refused: evaluateKiroHook rejects with
UnsupportedKiroHookEventError (code: "UNSUPPORTED_KIRO_HOOK_EVENT", with
the received name in hookEventName) before evaluate runs. Conduit does not
guess a phase for an event it cannot classify. The caller decides how its hook
process reports the error; exiting with a status other than 0 or 2 makes
Kiro treat it as a hook failure rather than a block.
The projected outcome becomes a KiroHookResult:
| Hook and projected outcome | Result |
|---|---|
preToolUse denied |
exitCode: 2, stderr set to the reason, or Tool use denied |
stop denied |
exitCode: 0, stdout set to {"decision":"block","reason":...}, with reason defaulting to Completion denied |
agentSpawn or userPromptSubmit with modelContext |
exitCode: 0, stdout set to the context |
| Anything else | exitCode: 0 |
Installation goes through createKiroAdapter, which needs explicit
resolveTarget and write bindings. Registering the executable bridge in a
Kiro custom-agent configuration is the application's job.
Conformance API#
| Export | Purpose |
|---|---|
probeHostConformance(adapter) |
Run the executable probes, including the per-phase lifecycle-, decision-, context-, and decision-context- checks listed in Concepts, and return ConformanceResult[] ({ check, status: "pass" | "fail", detail? }) |
createConformanceReport(inputs) |
Probe each EvidenceInput and return a ConformanceReport sorted by adapter id |
deriveConformanceLimitations(capabilities, results) |
Return the sorted capability.* and probe.* limitations |
normalizeCapabilities(capabilities) |
Resolve legacy aggregate fields into explicit per-phase influence |
serializeConformanceReport(report) |
JSON with two-space indentation and a trailing newline |
parseConformanceReport(serialized) |
Parse a report; throws UnsupportedConformanceSchemaVersionError unless schemaVersion is "2" |
renderConformanceMatrix(report) |
Render the Markdown matrix used in host-conformance.md |
EvidenceInput is { adapter, evidenceScope, adapterVersion, hostId, hostVersion, limitations? }.
evidenceScope is "adapter-contract" or "host-bound".
ConformanceReport is { schemaVersion: "2", adapters: AdapterConformanceEvidence[] }.
Each AdapterConformanceEvidence has evidenceScope, adapterId,
adapterVersion, hostId, hostVersion, normalized capabilities,
limitations, and results.
UnsupportedConformanceSchemaVersionError has code
UNSUPPORTED_CONFORMANCE_SCHEMA_VERSION and a schemaVersion property holding
the version it found. parseConformanceReport checks only the schema version;
it does not validate the rest of the report against the JSON Schema.
Repository scripts#
These npm scripts run in a checkout of this repository.
| Script | What it does |
|---|---|
npm run build |
Remove dist/ and compile with tsc |
npm run typecheck |
Type-check without emitting |
npm test |
Build, then run the compiled tests. It fails if fewer than 3 test files are found. |
npm run conformance:generate |
Build, then rewrite conformance/host-conformance.json and docs/host-conformance.md from the shipped profiles |
npm run conformance:check |
Build, then fail if either generated artifact is stale |
npm run verify |
Typecheck, test, conformance check, and npm pack --dry-run |
npm run docs:check |
Run the docs-kit gate on the published docs |
npm run docs:build |
Build the documentation site |
npm run veritas:readiness |
Run veritas readiness --working-tree |