Principal and authority
Every agent acts as a known principal, within an authority scope you give it.
AgentBuilder.build() requires both. They are the agent's accountability wiring: the principal says who acts, and the scope says what that actor may do and for how long. A liability sink, which records each action, is optional.
What the builder requires
principal(AgentPrincipal): the identity the agent acts as.LocalIdentityProvider.issue(AgentDescriptor)creates one for local use.authorityScope(AuthorityScope): what the agent may do.AuthorityScope.unrestricted(Duration)allows every action type and target for a time window.AuthorityScope.permanent()never expires.liabilitySink(LiabilitySink): optional. Without one, no liability records are built.
Without a principal or a scope, build() throws IllegalStateException and names the missing part. See Create an agent for a full builder chain.
Wire a direct Agent subclass
A direct subclass does not go through the builder, so it wires its own ActionExecutor. Call setPrincipal and setDefaultScope on it and pass it to the four-argument Agent constructor. The annotated agent example shows this.
An executor that is missing either one throws AccountabilityWiringException when an action is dispatched, before the action's body runs. It is not retried. In a chat, the turn ends on that tool call.
Narrow the scope
package com.example.tnsai.docs;
import com.tnsai.accountability.AuthorityScope;
import com.tnsai.enums.ActionType;
import java.math.BigDecimal;
import java.time.Duration;
import java.time.Instant;
import java.util.Optional;
import java.util.Set;
public final class AuthorityScopeExample {
private AuthorityScopeExample() {}
public static AuthorityScope localOnly() {
return new AuthorityScope(
Set.of(ActionType.LOCAL),
Set.of("billing"),
Optional.of(new BigDecimal("5.00")),
Duration.ofHours(8),
Instant.now());
}
}| Field | Meaning |
|---|---|
allowedActions | Action types the agent may run. Empty allows all |
targetSystems | Systems the agent may act on. Empty allows all |
spendCeilingUSD | Spending limit, read by the x402 payment broker |
validFor | How long the scope lasts from issuedAt. null never expires |
issuedAt | When the scope starts |
When the scope is checked
When a liability sink is set, the scope is recorded in each liability record. It is enforced only for actions that declare an access policy, with @Security or @ExecutionPolicy on the method or its class. For those actions:
- With
tnsai-qualityon the classpath, itsSecurityEnforcerdenies the call with aSecurityExceptionwhen the scope has expired, does not permit the action type, or lists target systems that exclude the action's target. - Without
tnsai-quality, the call is refused, because no provider can evaluate the declared policy.
An action with neither annotation runs whatever its scope says. To bound one, declare a policy on it. See Enforcement and Execution policy.
Related
- Audit agent actions: identity, liability records, tiers and reputation in depth.
- Create an agent: the builder chain that sets the principal and scope.
- Enforcement: which checks run on each action.
- Restrict who can trigger an action