Blog

AI Agent Profile: Make Settings Deliverable

An AI agent profile lists the model, tools, files, and policies a run needs. A delivery contract rejects unsupported settings before the worker starts.

Drew Stone
agent-runtimeagent-profilescoding-agentsai-infrastructure
An editorial still life about the software that runs an agent

An AI agent profile can say, “Use this model, these tools, this prompt, and these files.” The run can still start when the selected execution path knows how to carry only the model and prompt. The answer may look plausible, so the missing tools are easy to blame on the model or the prompt.

The failure happened earlier. The runtime accepted a profile that the chosen path could not deliver.

This article explains how a delivery contract moves that failure to the point where it can still be fixed. The example comes from Tangle’s public agent-runtime source, but the rule applies to any system that saves agent settings and sends them through more than one execution path.

An AI agent profile is safe to launch only when the selected path can account for every requested setting.

The profile describes intent

An agent is a program that uses a language model and tools to complete a task. An agent profile is the saved configuration that describes how that program should run. It can include instructions, model choices, tools, files, permissions, child agents, and lifecycle settings.

A worker is one running copy of the agent. A runtime is the software that starts the worker, sends it inputs, observes its events, and records its result. An execution path is the particular route the runtime uses to create and control that worker.

Materializing a profile means turning the saved description into the concrete inputs that a worker receives. The materialized result might be a prompt, a model request, a mounted directory, a tool registry, and a set of process limits. It is no longer a description. It is the configuration that can affect the run.

That distinction matters because execution paths do not all have the same interface. A prompt-and-model worker may accept two kinds of input. A coding worker may also accept files, command permissions, external connections, and child-agent settings. A remote service may expose a model request but no per-worker environment channel.

MCP, the Model Context Protocol, is an open protocol for connecting language-model applications to external data sources and tools. An MCP connection is therefore more than text in a prompt. It needs a path that can carry its server details, authorization boundary, and lifecycle. The MCP specification describes the protocol’s tools, resources, prompts, and capability negotiation.

A saved profile can outgrow its route

Imagine a support agent profile with these settings:

{
  "model": "support-model",
  "systemPrompt": "Answer from the approved support policy.",
  "tools": ["ticket.read", "ticket.update"],
  "resources": ["support-policy.md"],
  "permissions": ["read-ticket", "write-ticket"]
}

The product offers two routes. The first route sends a prompt and model name to a remote model service. The second route starts a worker with a workspace, tools, and permissions.

If the product sends the profile unchanged to both routes, the first route has three possible behaviors.

BehaviorWhat the user seesObserved system state
Drop unsupported fieldsThe run starts without ticket toolsThe profile was only partly delivered
Reject the whole profileThe run fails before work beginsThe route exposed its limit
Pretend the fields workedThe model talks about tickets but cannot update oneThe system created false capability

The first behavior is difficult to diagnose. The second is easy to correct. The third is dangerous because the user cannot tell that the worker lacks authority.

The contract is the mechanism that makes the second behavior the default. It says which profile settings the path accepts and gives the runtime enough information to reject the rest before the worker starts.

Treat delivery as a set comparison

The runtime does not need to compare two giant configuration objects as opaque blobs. It can compare the changed settings with the selected path’s supported settings.

The core operation is small:

type DeliveryContract = {
  path: string
  supported: Set<string>
}

function unsupportedChanges(
  changed: Set<string>,
  contract: DeliveryContract,
) {
  return [...changed].filter((key) => !contract.supported.has(key))
}

function canLaunch(changed: Set<string>, contract: DeliveryContract) {
  return unsupportedChanges(changed, contract).length === 0
}

This is an illustrative model of the rule, not a copy of the public package API. The important property is that the check operates on individual settings. The runtime can report that tools.ticket.update is unsupported while still accepting systemPrompt.

A real implementation also needs to distinguish “not supplied” from an explicit value. An empty tool list can mean “remove every tool.” false can mean “disable a permission.” 0 can mean “set the limit to zero.” Treating all falsy values as absent silently changes the user’s request.

The check belongs before worker creation. Once a worker has started, it is too late to explain that a connection was discarded during setup.

Validate the effective profile

The profile saved by a product is not always the profile that a worker should receive. Defaults may add a model, a workspace, or a tool that the saved record does not mention. Inheritance may add instructions from a team profile. An update may explicitly remove a tool by sending an empty list.

The delivery check should therefore operate on the effective profile after defaults and inheritance have been resolved. It should still retain the original request so a caller can understand why a setting was present. Otherwise, a route can pass a check against a partial object while dropping a capability supplied by a default.

A useful materialization record keeps the three states apart:

{
  "requested": {
    "tools": ["ticket.read", "ticket.update"],
    "resources": ["support-policy.md"]
  },
  "effective": {
    "tools": ["ticket.read", "ticket.update"],
    "resources": ["support-policy.md"]
  },
  "path": "prompt-and-model",
  "status": "rejected",
  "unsupported": ["tools", "resources"]
}

This record is illustrative rather than a promised API shape. Its purpose is to show why a rejected request is better than a silently shortened one. The requested state is what the caller asked for. The effective state is what defaults and inheritance produced. The materialized state is the concrete setup passed to the worker.

Test the boundary with values that look empty but are meaningful. An omitted tools field can mean “use the default tools.” An empty tools array can mean “run with no tools.” false can disable delegation. 0 can disable a retry budget or set a resource limit to zero. The test should assert both the decision and the preserved value.

Input caseRequired assertion
Field omittedThe route applies its documented default or records that no value was supplied
Empty collectionThe route preserves the explicit empty collection
falseThe route preserves the disabled state
0The route preserves the zero limit
Unsupported nested fieldThe route rejects the candidate and names the nested setting
Two settings with one unsupportedThe route does not partially launch the worker

The last case protects against a particularly confusing partial run. If the model and prompt were delivered but the tools were not, the first response may look healthy. The caller then discovers the missing capability only after the model asks for an action. Rejecting before creation makes the error synchronous with the configuration that caused it.

Keep transport and authority separate

A route can carry a tool definition without granting permission to use the tool. It can also grant a filesystem permission while failing to carry the tool that would use it. These are different contracts.

Transport asks whether the worker can receive a setting. Authority asks whether the worker may exercise that setting in this run. Execution asks whether the worker used it and whether the external action succeeded.

For the support example, the route may support the ticket.update tool while the profile’s permission set denies writes. The correct result is not to delete the tool from the profile and pretend the route delivered everything. The runtime should record that the tool is available but the requested action is denied by policy.

This distinction also helps with audits. A materialization record can show that the tool registry was installed. A policy decision can show that the write operation was refused. A trace can show whether the worker requested the operation. An evaluation can check whether the ticket remained unchanged.

One record should not collapse those facts into “tools: true.” The more consequential the side effect, the more important it is to preserve the boundary between capability, permission, request, and outcome.

The public source names different contracts

The public profile-materialization module records named delivery contracts for different execution paths. It expands compound areas such as model and resources into the individual settings that a route must either carry or reject.

The contract names are implementation details, not a score and not proof that the paths behave identically. Three representative shapes make the design easier to understand.

Contract shapeWhat it is meant to carryA reasonable use
Full-profile executionThe canonical profile areas, including tools and resourcesA worker designed to receive the complete profile
Prompt-and-model executionInstructions, model selection, agent program, and metadataA deliberately narrow remote model route
Isolated worktree command executionCoding settings plus files, commands, permissions, and lifecycle controlsA local coding worker in its own worktree

The supported fields are part of the public source contract. They can change as the runtime adds a new worker or removes an old route, so a builder should read the versioned source or package documentation rather than hard-code the list in a separate application.

The profile areas themselves are ordinary product concepts:

AreaExample settingFailure when the route cannot deliver it
InstructionsSystem prompt or task instructionsThe worker follows a generic instruction set
ModelModel name or reasoning optionThe worker uses a different model or default
Agent programWhich program handles the taskThe route starts a different worker
ToolsTicket, browser, or file toolsThe model can request an action that no process can perform
PermissionsRead, write, network, or process accessThe worker has too much or too little authority
External connectionsMCP server or service connectionThe connection never reaches the worker
Child agentsWhether delegation is allowedA supervisor cannot create the requested child
ResourcesFiles, skills, commands, or instructionsThe worker starts without the material it needs
LifecycleHooks, timeouts, or run modeCleanup or cancellation behaves differently
RecordsMetadata, privacy, and run extensionsThe work cannot be joined to its review record

The table is a debugging map. It tells a builder what to inspect when a profile appears to work but one capability is absent.

A mismatch should be actionable

Return to the support profile. The narrow route supports model, systemPrompt, and agentProgram. The profile also changes tools, resources, and permissions.

An actionable error can look like this:

{
  "code": "unsupported_profile_settings",
  "path": "prompt-and-model",
  "unsupported": [
    "tools",
    "resources",
    "permissions"
  ],
  "next": "choose a path that carries these settings or remove them"
}

This response is illustrative. Its job is to show the information a caller needs, not to promise a particular error spelling.

The error should identify the path and the unsupported settings. Listing every supported setting can help when the caller needs to choose a different route. Returning only “profile invalid” forces the caller to repeat the run with guesses.

There is a second useful check: compare the profile before and after materialization. If a route claims to support resources, the resulting worker setup should contain the requested resource or a clear failure. A declaration is not evidence that the behavior occurred.

A delivery contract is not an implementation guarantee

The contract answers one question. Can this route accept and attempt to deliver this setting?

It does not answer three others.

  1. Does the worker implement the behavior represented by the setting?
  2. Does the model provider expose the requested capability?
  3. Did the run produce the result the user wanted?

Keep those boundaries visible.

EvidenceNarrow claim it supportsClaim it does not support
Contract check passedThe route accepted the requested profile surfaceEvery tool will work
Worker setup recordThe runtime sent particular inputs to the workerThe worker used them correctly
Model responseThe provider returned that responseThe response is true or complete
Tool traceThe worker requested or ran a tool, depending on the traceThe requested action achieved its goal
EvaluationA defined check scored the resultThe check covers every production failure

A trace is the record of a run’s inputs, actions, tool events, outputs, and failures. It lets a reviewer see whether a setting was delivered and what happened afterward. An evaluation is a structured check of a result against a stated criterion. It can catch a missing ticket update, but it cannot retroactively make an unsupported tool available.

Do not confuse delivery with Tangle’s service layers

The profile contract sits below several other Tangle concepts. Those concepts solve different problems.

A router is a model-access layer that chooses or forwards an inference request to a provider. It can decide which model answers, but it does not automatically provide a filesystem or ticket tool.

A Blueprint is a reusable service template with a defined interface and execution requirements. A service instance is one live deployment of that template. An operator is the compute provider that runs a service instance. The Tangle glossary defines those protocol terms.

x402 is an HTTP payment flow for machine-to-machine requests. Payment can authorize a job request, but settlement does not prove that the job received every profile setting or completed successfully. The x402 project documents the payment protocol.

Attestation is cryptographic evidence about a confidential execution environment. It can help a caller verify which protected machine or image it is talking to. It does not prove that a model made a correct decision or that a tool changed the intended record. Tangle’s runtime documentation describes attestation as a prerequisite for its confidential runtime mode.

These layers can surround a profile-aware worker. They do not replace the delivery check.

Source and package versions are separate evidence

The public repository is a source record. An installable package is the version an application installs. Those records can differ while a change is moving through release automation.

The published package and its release notes determine what an application can install. Check that package version and the source version your application pins before depending on a new contract.

This distinction matters for debugging. If the public source contains a fix but the installed package predates it, the right action is to upgrade or pin the source version. Changing the prompt will not make an old runtime reject an unsupported setting.

Choose before launch

Use a delivery contract when any profile can run through more than one path. Compare the changed settings with the selected path before starting a worker. Reject the candidate with the missing settings when the comparison fails.

Then test the next boundary. If the route accepts the profile, record the materialized inputs and run a task that exercises the new capability. An accepted configuration is a prerequisite for a useful run, not a passing result.

The generic execution-path deletion shows what happens when a shared route gains enough profile fidelity to replace a specialized one. The worker observability guide follows the next boundary, where the runtime must report what the worker did. For the workspace that carries files and commands, continue to the LLM sandbox environment guide.

What is an AI agent profile?

An AI agent profile is a saved description of the model, instructions, tools, permissions, resources, and lifecycle settings a worker should receive.

What does profile materialization mean?

It means converting saved profile settings into concrete worker inputs such as a prompt, model request, tool registry, files, and process limits.

Why does a runtime need several delivery contracts?

Different execution paths expose different inputs. A narrow model route cannot promise the same settings as a coding worker with a filesystem and tools.

What happens when a profile requests an unsupported setting?

The runtime should reject the candidate before launch and identify the setting and path that cannot carry it. The caller can select another route or remove that change.

Does a passing contract prove the agent will use the setting correctly?

No. It proves only that the selected path accepts the requested profile surface. Worker behavior, provider support, tool outcomes, and task-specific evaluation remain separate checks.

Does x402 payment or confidential-runtime attestation prove profile delivery?

No. x402 proves a payment flow settled, and attestation can prove facts about a protected environment. Neither one proves that a worker received every requested tool, file, or instruction.