@tangle-network/agent-interface
v2.12.0
Published
Shared TypeScript types and Zod schemas that define the contract between Tangle agents, the sidecar, and provider adapters: capabilities, agent profiles, message parts, and harness descriptors. This is the canonical home for those shapes; higher-level pac
Readme
@tangle-network/agent-interface
Shared TypeScript types and Zod schemas that define the contract between Tangle agents, the sidecar, and provider adapters: capabilities, agent profiles, message parts, and harness descriptors. This is the canonical home for those shapes; higher-level packages import from here rather than redefining them.
Agent instances
AgentProfile describes behavior. AgentInstanceSpec describes one optional managed Agent inside an existing execution environment. The environment remains the computer and security boundary, so it may host zero, one, or many Agent instances.
import type { AgentInstanceSpec } from "@tangle-network/agent-interface/agent-instance";
const planner = {
id: "planner",
profile: {
name: "planner",
harness: "opencode",
prompt: { systemPrompt: "Plan before editing." },
},
workspace: { mode: "shared" },
} satisfies AgentInstanceSpec;The portable contract owns only inline profile and harness selection, shared or isolated workspace intent, public lifecycle state, a provider-sanitized failure summary, and idempotent stop shapes. Credentials, HTTP routes, process identifiers, placement, billing, snapshots, local resource controls, grants, and fencing remain provider-private.
shared means ordinary same-computer file visibility. It is not automatic merge behavior or tenant isolation. isolated asks the provider for a private writable view and explicit inspect or commit behavior. Providers must reject unsatisfied machine requirements rather than silently replacing or migrating a live environment.
WorkspaceRequest.cwd is an explicitly based path reference.
Use base: "repository" for a portable repository-relative POSIX path.
Use base: "host" for a provider-owned native host path.
The shared schema rejects unsafe repository paths and control characters in both path forms.
It canonicalizes redundant . segments and separators for repository paths.
Use . for the repository root.
Providers advertise accepted path bases under AgentEnvironmentCapabilities.workspace.cwdBases.
To migrate a string cwd, wrap it in the base that owns its path.
Use the repository base for Tangle and other portable workspace providers.
Use the host base for CLI Bridge native process paths.
The public AgentInstanceRecord contains a credential-free profile identity, not the full profile or provider request. Existing session APIs can implement this contract without a new service: one instance maps to one managed session, compatible sessions may reuse a backend process, and stop maps to idempotent session deletion or process release.
Durable runs, interactions, and context
AgentRunControlRef identifies a retained run without depending on a live JavaScript object and may carry the provider's admission digest so reconstruction can reject changed-input reuse.
RuntimeEventEnvelope adds stable run, event, sequence, cursor, and timestamp fields around the existing StreamEvent union, and its runtime schema validates every canonical event variant.
The child-task event reports one update of a provider-native child task (a subagent, worker, or delegated task) with a stable childId, an optional parentChildId, a lifecycle status, start and update times, and the runner, model, usage, and terminal reason when the provider reports them.
Its sourceEventId identifies the exact update, so a consumer applies the first event with a given sourceEventId and ignores later copies during replay or reconnect.
Identity never depends on the bounded raw payload, and a provider that cannot report a stable childId emits no child-task event.
The canonical cancelled status identifies caller cancellation and remains distinct from failed.
Providers advertise retainedControl only when exact run, result, event, cancellation, replay, detach, turn, and session identity are all implemented together.
AgentEnvironment.metadata is the detached snapshot returned by create or get, so recovery can check persisted annotations without listing environments.
AgentEnvironment.creation reports what the create call that returned the object did: created when the call provisioned the environment, replayed when an existing environment matched the idempotency key.
It is a per-call fact, so a same-key replay returns a view of the same environment with creation: "replayed", and the value is absent when the provider cannot prove either outcome.
A consumer never destroys an environment whose creation it cannot prove, because another caller can hold it.
Metadata can include caller-authored values and does not prove authorization or authorship.
AgentSession.cancelRun() accepts a canonical request digest bound to one operation and AgentExactRunControlRef, so a caller can safely repeat the same cancellation after losing the first acknowledgement.
Its acknowledgement repeats the operation, digest, and run coordinates and distinguishes a known cancellation effect from conflict or unknown state.
An environment advertises interactions only when it can originate and answer typed requests.
RequestedInteractions defines the bounded per-turn posture for well-known and namespaced provider interaction kinds.
permission, question, and plan keep portable meanings across providers.
AgentTurnInput.interactions and AgentExecutionInput.interactions carry that posture through shared execution boundaries.
An omitted posture leaves provider defaults unchanged, while an empty object enables no interaction kind for that turn.
AgentEnvironmentCapabilitiesSchema strictly validates the complete capability document at runtime, including all-or-nothing durable branching declarations.
Optional provider methods must be absent when their capability is false so clients cannot expose an action the provider has denied.
The capability names supported request kinds, answer field types, response scopes, secret answers, concurrency, replay, and response idempotency.
AgentEnvironment.respondToInteraction() and AgentSession.respondToInteraction() bind each response to its run, environment, optional provider session, interaction, and caller operation identifier.
Their acknowledgement distinguishes acceptance, exact prior resolution, conflicting prior resolution, expiry, cancellation, unknown interaction, unknown run, binding mismatch, and transport failure.
Acknowledgements deliberately contain no answer value or answer hash because both can disclose low-entropy secret answers.
Response data is accepted only after validateInteractionResponse() checks it against the exact outstanding request, rejects undeclared fields, and enforces the request's permission scopes.
An omitted permission scope permits only a one-time response; session and persistent grants must be explicit.
The legacy SdkProviderAdapter.respondToInteraction(response) remains source-compatible, while new adapters use respondToInteractionCommand(command) for exact binding and durable acknowledgement.
Portable conversation transfer reuses BackendMessage and InputPart rather than defining another message format.
Planning is represented separately from execution: a plan embeds the immutable source, lists every message and part decision, names the destination runner, contains the exact derived context, and carries a canonical digest.
Every plan request has a canonical request digest, and every ready, over-limit, or unsupported result repeats the request identifier and digest.
portableContextPlanResultMatchesRequest() verifies that the result belongs to the exact request, the returned source and destination match, and a ready plan stays within its requested token limit.
Partial input or output always requires explicit user or policy acceptance, even when no individual message was transformed.
ContextTransferRequest binds an operation identifier to that accepted plan, while ContextTransferResult distinguishes first admission, exact replay, changed-input conflict, and unknown transport outcome.
contextTransferResultMatchesRequest() checks the operation identifier and request digest for every outcome before a caller accepts, retries, or reports it.
Its successful receipt repeats the exact destination and carries the provider's session-creation operation and timestamp, identifying the one fresh provider session that admitted the context.
Providers expose this operation through AgentEnvironmentProvider.contextTransfer.
They advertise contextTransfer only when transfer, retry idempotency, and lookup are all durable.
An accepted destination can set CreateAgentEnvironmentInput.requestedId to bind environment creation to its exact identifier.
NativeContextBoundaryProof is the separate path for same-session continuation and includes the exact run identity.
NativeContextContinuationRequest.turnDigest binds the operation to the exact new JSON-stable user turn; timeout and abort controls live outside that turn under AgentNativeContextContinuationOptions.
Continuation is valid only when the provider atomically proves the recorded token, revision, digest, or message boundary, sends zero copied history, and applies retry or changed-input conflict semantics.
Providers advertise nativeContinuation only when both guarantees are implemented and expose AgentSession.continueNative() as the single durable operation.
When nativeContinuation.admissionControl is true, continueNative() calls onAdmission once with the exact new run before it waits for terminal output.
This lets a runtime stream, inspect, and cancel the continued run through the same retained-control methods.
An accepted or replayed operation returns its original turn result and exact current control reference; AgentNativeContextContinuationResultSchema validates that shape and agentNativeContextContinuationResultMatchesRequest() checks its request and retained-session bindings.
A changed request with the same operation identifier conflicts without dispatch.
Providers that support recoverable workspace copies expose workspaceBranching and set branching.retrySafe, branching.lookup, and branching.cleanup together.
Checkpoint and fork requests bind an idempotency key to a canonical request digest.
Every returned resource repeats and validates that identity, lookups recover remote success after caller restart, changed-input key reuse returns a conflict, and cleanup binds its acknowledgement to the exact provider and target.
A checkpoint with dependent forks returns in_use plus the blocking environment identifiers and remains recoverable until those forks are destroyed.
The older checkpoint() and fork() methods remain source-compatible for providers that have not yet implemented recovery semantics, but clients must not present them as durable workspace branching.
CreateAgentEnvironmentInput.idempotencyKey makes generic environment creation one retry-safe operation.
When a caller repeats that key, the provider must canonicalize every create field except the key and attempt signal.
The same canonical input must return or reconstruct the same environment, including after an ambiguous provider response.
The same key with any changed create field must reject before a second create effect.
Providers backed by a remote service must forward the key and retain its canonical input through environment reconstruction.
The existing AgentEnvironmentProvider.create() method carries this contract; it does not add a second create method or capability flag.
All new wire values have exported Zod schemas on the package root.
Omitting interactions and nativeContinuation, or leaving the three durable branching flags false, is the compatible declaration for existing providers.
profile.systemPrompt declares two independent bits rather than one flag.
replace means the provider deletes the harness's own system prompt and installs prompt.systemPrompt; append means it keeps that prompt and adds prompt.appendSystemPrompt to it.
A provider that can only append must declare replace: false and refuse a profile carrying systemPrompt, because quietly appending a requested replacement leaves the instructions the caller asked to delete in force.
Install
pnpm add @tangle-network/agent-interfaceUsage
import type {
AgentEnvironmentProvider,
} from "@tangle-network/agent-interface/environment-provider";
import type {
BackendCapabilities,
ProviderCapabilities,
} from "@tangle-network/agent-interface";
const caps: ProviderCapabilities = {
supportsVision: true,
supportsLogprobs: false,
supportsToolCalls: true,
supportsComputerUse: false,
};
const provider: AgentEnvironmentProvider = {
name: "example",
capabilities: () => ({
profile: {
namedProfiles: false,
// Most harnesses can only add to their built-in prompt, not delete it.
systemPrompt: { replace: false, append: true },
instructions: true,
tools: true,
permissions: true,
mcp: true,
subagents: false,
resources: { files: true, instructions: true, tools: true },
hooks: false,
modes: false,
runtimeUpdate: false,
validation: true,
},
streaming: { live: true, replay: false, detach: false, turnIdempotency: false },
sessions: { continue: false, list: false, messages: false },
workspace: { read: true, write: true, exec: true, git: false, upload: false, download: false },
branching: { checkpoint: false, fork: false },
placement: false,
usage: true,
confidential: false,
}),
create: async () => {
throw new Error("implement provider create()");
},
};When caller environment values merge into a bridge or harness process, reject names for which isRuntimeProcessControlEnvironmentName(name) returns true.
Use isCredentialBearingProfileConfigName(name) before retaining public config.
These checks do not apply to a replacement environment owned by caller code.
Profile knowledge base
@tangle-network/agent-interface/profile-kb holds how to get the best from each frontier harness and model this platform runs.
Every entry cites a vendor source or a command run on a dated check.
Guidance is specific to one harness or model and never compares models.
withProfileKb(profile) composes harness guidance, then model guidance, then the profile's own text into the profile's prompt:
import { withProfileKb } from "@tangle-network/agent-interface/profile-kb";
const worker = withProfileKb({
harness: "claude-code",
model: { default: "claude-opus-5-5" },
prompt: { appendSystemPrompt: "Cite the file you read." },
});Guidance goes into appendSystemPrompt where the harness owns an additive system-prompt control, and into instructions otherwise.
Recomposing replaces the guidance withProfileKb wrote earlier, so a second call, or a call with an executor's harness or model override, yields a stable profile.
The knowledge base owns the block sources harness, model, and learning (PROFILE_KB_SOURCES).
composeAgentProfileGuidance is the underlying composer for other knowledge layers.
Give each layer its own source name and pass it as replaceSources: the composition then replaces only those sources and keeps every other block in place. Without replaceSources, it replaces every block.
The block marker is reserved for composers: a hand-written <profile-guidance source="model" ...> block counts as a knowledge-base block and is replaced.
The module also exports the data (profileKbHarnesses, profileKbModels), operator notes for launching each harness and model, platform learnings (admitted only after an agent-eval check reproduced them), and profileKbDiscrepancies, which records where a vendor source states a requested name differently.
Failed execution accounting
An adapter can reject with AgentExecutionError and retain observed usage and timing in its immutable receipt.
Absent fields remain unknown.
The receipt records a lower bound, not complete accounting or a successful outcome.
Hosts must preserve it when publishing a failed or cancelled execution.
Exact process environments
Providers may expose the optional exactProcess capability for isolated, reproducible process execution.
It is separate from agent-backed create() because it guarantees a fresh environment, immutable image identity, explicit resources, bounded exact-byte file reads, shell-free argv, replacement process environment, recoverable output and terminal reason, bounded network access, and collision-safe idempotent recovery without starting a provider-managed agent.
Higher-level runtimes can use this primitive for measured candidates without making candidate lifecycle part of the provider contract.
Providers must omit the capability unless every property is enforced on their real execution path.
Frozen improvement candidates
AgentCandidateBundle is the portable output of an improvement run: a recursively strict profile, an explicit disabled/no-op/changed code result, a shell-free launch, optional knowledge, isolated memory, ancestry, and spend.
Execution either pins a candidate-selected container in the bundle or delegates container selection to the benchmark evaluator.
For evaluator-owned task images, the protected runtime creates a separate plan for every candidate/task pair and binds the exact result shape (workspace change or bounded typed output), UTF-8 instruction bytes, selected OCI index, manifest, platform, task workspace, model, launch, counted attempt, retry policy, and tool-step limit before execution.
Execution limits may also carry maxTotalTokens, which bounds the accounted inputTokens + outputTokens total for one arm.
Instruction delivery is closed to one final argv element, exact stdin bytes followed by EOF, or a fixed file path exposed through TANGLE_CANDIDATE_TASK_PATH.
That plan also binds the profile target workspace and every mounted path; benchmark adapters must restore or exclude task-targeted profile paths before capturing the submitted solution patch.
Resources are embedded, addressed through closed S3/IPFS locators, or pinned to a full GitHub commit plus content digest.
An imported resource may also retain its immutable source identity and revision, exact source digest, validated SPDX expression or content-pinned custom license, attribution and notices, and an ordered normalization/transformation digest chain.
These provenance fields are part of the candidate digest; changing or omitting an obligation produces a different candidate identity.
Candidate-authored process configuration is explicitly public; model authorization is evaluator-mediated and secret values never belong in the bundle.
Because prompts and inline files are arbitrary text, producers must also run their normal secret scanner before persistence.
Candidate bundles reject unregistered backend extensions instead of accepting an untyped behavior or credential channel.
Each terminal model settlement carries the raw and accounted input tokens for every router call, plus the router's usageWithinLimits result.
The settlement usage input total equals accounted input tokens, so aggregate limits cannot be recomputed from raw provider input alone.
agentCandidateBundleSchema.parse() proves only that the wire shape is valid.
Before execution, an integrity verifier must omit only the top-level digest, canonicalize the rest with RFC 8785, hash the UTF-8 bytes to lowercase sha256:<hex>, verify every artifact, apply any Git patch to the declared base tree, and emit an AgentCandidateMaterializationReceipt.
Artifact and OCI resolvers must also reject redirects or DNS results that reach loopback, private, or link-local addresses; schema parsing cannot prove network resolution safety.
Attach the materialization and AgentCandidateRunReceipt records to the benchmark run so the result names the exact profile plan, code tree, launch plan, selected OCI manifest/platform and source, model, memory isolation, trace, termination, harness, and container that ran.
A timeout, signal, or cancellation remains distinct from a process exit; if the protected evaluator cannot recover complete usage and trace evidence, it must mark the cell as invalid capture instead of minting a zero-usage receipt.
The three code states are intentionally distinct:
{ kind: "disabled", reason: "control" }is the fixed control;reason: "not-applicable"keeps code unchanged while another surface is optimized.{ kind: "no-op" }means a proposer ran and returned no change.{ kind: "git-patch" }carries a non-empty binary Git diff whose resulting tree must be verified.
Versioning
This package follows semantic versioning from 1.0.0.
- A minor release is additive. A new export, a new optional field, and a new member on an exported union are minor.
- A patch release is a fix. A behaviour correction that keeps every declared type is patch.
- A major release removes or narrows. A deleted export, a removed member, a narrowed type, and a new required field are major.
Declare this package with a caret range, and set the floor to the lowest minor you actually use.
A package that reads an export added in 1.4.0 declares ^1.4.0, because ^1.0.0 lets a resolver keep 1.0.0.
A caret range admits every later additive minor without a consumer release.
Do not declare a single-generation window such as >=1.4.0 <1.5.0; that shape forces a coordinated release across every repository on each minor.
Write a default branch in every switch over an exported union.
An exhaustiveness check that assigns the remaining case to never fails when a minor adds a member, and this promise does not cover it.
License
MIT
AgentEnvironmentEvent.usageMode describes token usage within one turn.
delta contributes new usage; cumulative includes earlier contributions and replaces the prior turn total.
Repeated cumulative totals do not add spend.
An absent mode means the aggregation semantics are unknown.
Materialization receipts cover all three model token ceilings at their exact requested paths.
A strict profile rejects unsupported ceilings instead of silently accepting them.
