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.
AgentProfile.seats is an optional ordered chain for continuing one logical agent
across subscriptions. Each stage pins a harness, provider, model, and account
selector. all-eligible lets the account manager choose each available seat in
its own order; seat names one account. The first stage must equal the profile's
top-level harness, model.provider, and model.default, so the initial run
cannot silently change its authored model. The profile's reasoning effort and
system prompt intent must be supported by every stage. The executor must check
the remaining profile capabilities and resolve seats before spend.
An omitted seats field preserves the legacy single-provider policy. For each
segment, the executor keeps the authored profile for identity and builds a
provider-facing projection without seats, setting its top-level harness and
model to the selected stage. A cross-provider continuation requires an explicit
stage in the authored chain.
Each later stage may replace the profile-level tools and permissions maps.
An explicit {} removes those profile-level controls for that stage; an omitted
map inherits them. If the first stage names a map, it must equal the top-level
map. The executor projects the selected maps before materialization and must
refuse a stage whose native harness cannot honor them. A missing grant map does
not prove which built-in tools the harness actually exposes.
measureAgentProfile(profile) returns the canonical profile digest and JSON byte size,
per-prompt and inline resource UTF-8 bytes and logical lines, enabled/disabled tool and
MCP declarations, and declared subagent counts. Resource rows use JSON Pointers;
GitHub resource content has bytes: null and lines: null until independently resolved.
The summary contains no prompt text, resource text, or server configuration.
Store it with the existing profile record and cache by profileDigest. Measure authored
and effective profiles separately, joined through their execution preparation or
materialization receipts. The function does not load resources or estimate tokens.
Its coverage explicitly leaves tokenization, materialization, and observed use unassessed.
A capability declaration is not evidence that it was delivered, opened, or applied.
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.
AgentTurnInput.profile carries the exact AgentProfile for a turn through its canonical schema.
Keep mounted resources and behavior in this typed field, separate from bounded providerOptions metadata.
Providers must honor the profile or refuse it before dispatch; callers must not assume every provider supports per-turn profile changes.
Provider-specific payload and capability limits still apply.
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.
A provider whose forks hold their own copy of the checkpoint deletes it without waiting for them.
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.
pnpm add @tangle-network/agent-interfaceimport 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.
@tangle-network/agent-interface/profile-kb holds dated harness and model facts, conditional guidance, and reproduced platform learnings.
Sources guide investigation; they do not prove that a technique improves this platform's behavior.
Entries can expose optional claims, each with text, basis (documented, vendor-guidance, local-observation, or hypothesis), audience (agent or operator), and supporting sources.
A source has a URL and check date, with optional authority (primary, secondary, or local) and locator for the supporting section or artifact.
Authority describes the source, independently of the claim's basis.
The maintained entries derive the legacy prompt, operator, and sources arrays from these claims; hypotheses stay inspectable but contribute no prompt or operator text.
Only agent guidance enters the profile prompt. An empty prompt is legitimate when no supported instruction is needed; do not pad it with generic advice.
Keep applicability conditions in the claim's text so composition preserves them.
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, platform learnings (admitted only after an agent-eval check reproduced them), and profileKbDiscrepancies, which preserves dated naming and availability checks.
The source audit is retained in Anthropic and OpenAI, open harnesses and model APIs, and the distillation and consumer review.
Read vendor guidance within its documented, dated scope. Papers and social posts propose hypotheses to evaluate; they do not establish universal agent constraints. A measured conclusion applies to the configuration, tasks, environment, and evidence that supported it.
Keep that scope with the existing evaluation record and link it from the learning's evidence source:
- Requested model and observed served identity, provider route and adapter revision, harness and CLI version, and the authored and effective
AgentProfilerevisions or digests. - Reasoning settings, tool, MCP server and hook versions or definitions, permissions, and the loaded skill and instruction revisions. Distinguish declared configuration from observed delivery and use.
- Task and acceptance-check revisions, environment and relevant resource limits, sampling and budget, measured outcome, errors and counterexamples, and source traces or artifacts.
Unknown identity, usage, or cost stays unknown. A requested model name does not prove which model served the run; partial accounting supports a floor, not a complete total or zero spend.
Before reusing a conclusion after a material change to any of these conditions, review its applicability against the retained evidence. State what still transfers and what needs a new check; use the existing registration and resource authorization for any experiment. Preserve the original record and append the changed scope or new evidence.
The current learning selector matches only harness and model; it does not validate the other conditions above. Keep narrower limits explicit in the learning text, or retain the finding in its evaluation record until it is suitable for composed guidance. The learning records remain the single admission surface. Claim provenance does not automatically revalidate a run or authorize an experiment.
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.
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.
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.
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.
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.
Adapters emit native.session.observed with provider and nativeSessionId after observing the provider conversation identity.
The execution owner binds the observation to its execution and keeps it private.
Public session.updated events retain their resume-key semantics and do not establish native identity.
Native identity does not establish artifact capture completeness.