Skip to content
tnsaijava agent framework

Annotation Runtime Status

A reference of the annotations TnsAI exposes in com.tnsai.annotations, grouped by area, with each one's runtime status so you know what actually takes effect.

This page is the published runtime-status catalog (wired / nested-wired / partial / scaffold). For field tables and usage examples, see the Semantic Annotation Framework.

TnsAI is annotation-driven: you describe an agent, role, action, or tool declaratively and the framework wires the behaviour. A handful of annotations are still being wired or are on their way out — this page tells you which is which, so you don't reach for one that silently does nothing.

Status legend

StatusWhat it means for you
WiredThe runtime honours the annotation and all of its advertised fields. Safe to use.
Nested-wiredHonoured, but only valid inside a parent annotation (e.g. @LLMSpec inside @RoleSpec(llm = …)), not on its own.
PartialHonoured, but only a subset of fields takes effect today; the rest are no-ops until a later release. Notes call out which.
ScaffoldDeclared but not yet wired — using it has no runtime effect yet. Planned.

Annotations that used to exist but were removed are listed under Recently consolidated with their replacements. See the Changelog for the exact release and migration notes.

Core

The annotations almost every agent uses.

@RoleSpec — Wired

Declares a role: identity, goals, domains, and (nested) LLM/memory config.

@RoleSpec(
    name = "Researcher",
    description = "Answers questions with citations from a local corpus.",
    llm = @LLMSpec(provider = LLMSpec.Provider.OPENAI, model = "gpt-4o-mini", temperature = 0.2f))
public class ResearchRole extends Role { /* @ActionSpec methods … */ }

@AgentSpec — Partial

Type-level agent metadata and partial declarative configuration. The runtime consumes members including roles (as a fallback), maxContextTokens, and toolCallFilter, but it does not consume the nested llm member. There is no name (derived from the class) and no role string.

package com.example.tnsai.docs;

import com.tnsai.agents.Agent;
import com.tnsai.annotations.AgentSpec;
import com.tnsai.llm.LLMClient;
import com.tnsai.llm.providers.AnthropicClient;
import com.tnsai.roles.Role;

import java.util.List;

@AgentSpec(description = "A helpful research assistant")
public final class AnnotatedAgentExample extends Agent {

    @Override
    protected LLMClient getLLM() {
        return new AnthropicClient("claude-sonnet-4-20250514");
    }

    @Override
    protected List<Role> getRoles() {
        return List.of(Role.create(QuickstartBuilderExample.AssistantRole.class));
    }

    public static void main(String[] args) {
        var agent = new AnnotatedAgentExample();
        agent.start();
        String answer = agent.chat("What is quantum computing?");
        System.out.println(answer);
    }
}

Direct subclasses must implement the abstract getRoles() method. A non-empty result takes precedence over @AgentSpec.roles; an empty result allows the annotation's public-no-arg role classes to supply the fallback. Provide the agent-level LLM through getLLM() or AgentBuilder.llm(...).

toolCallFilter is Class<? extends ToolCallFilter>. The shipped default is AllowAllToolFilter — that class is the "not set" sentinel, not an allow-list. There is no AllowlistFilter / AllowlistToolFilter type. A real filter is your own ToolCallFilter with a public no-arg constructor.

@ActionSpec — Wired

Marks a method as an agent action/tool, with type, description, and behavioural constraints.

@ActionSpec(type = ActionType.LOCAL, description = "Look up an order",
            mustAlways = {"validate the order id"})
public Order lookup(String orderId) { … }

@State — Wired

Marks a planner field on a Role. The planner reads those fields as a Map to evaluate @Goal conditions and action pre/postconditions. This is not com.tnsai.models.agent.Belief and is not BDI extraction.

@Contract — Wired

Design-by-Contract pre/postconditions and invariants (JEXL), enforced around the action. See Contracts.

Tools

@Tool / @ToolParam — Wired

Expose a POJO method (and its parameters) to the LLM as a callable tool. @Tool carries requiresConfirmation and keywords, which the agent's approval gate (AGENT-V006) reads.

public final class PaymentTools {
    @Tool(description = "Send a wire transfer", requiresConfirmation = true)
    public String wire(@ToolParam("recipient") String to, @ToolParam("amount") double amount) { … }
}

TnsAI 0.14.0 (TnsAI@a070d674, TAN-3411) fails registration when a parameter has no @ToolParam(name) and javac emitted argN. The positional @ToolParam("recipient") form above is already safe. timeoutMs ships in Maven Central 0.14.1 as an eighth @Tool member (@since 0.14.0; PR #164 / TAN-2958). A positive value is one wall-clock budget for the full POJO dispatch, including idempotency work; 0 keeps the enclosing agent policy. Expiry is retryable ToolTimeoutException. Interruption is best-effort (TAN-4387). Dynamic tools and MCP server deadlines are out of scope. See Custom Tools.

@ToolExample — Nested-wired

Few-shot examples for a tool; read from @ActionSpec.examples().

@Idempotent — Wired

Marks an action safe to retry; consulted by the retry/fallback layer.

LLM configuration

@LLMSpec — Nested partial

@LLMSpec has @Target({}), so it is valid only as a member of another annotation. @RoleSpec(llm = …) consumes provider, model, temperature, maxTokens, and topP for the role-level client. frequencyPenalty, presencePenalty, apiKeyEnv, endpoint, timeoutMs, and streaming are export metadata only; fallbackModel and systemPrompt are currently unused. provider is LLMSpec.Provider, not a String; that enum is not the LLMClientFactory provider-name inventory.

@AgentSpec(llm = …) is also syntactically valid, but the agent initializer does not consume it today. Use an agent's getLLM() override or AgentBuilder.llm(...). The older type-level @LLM was removed (see below).

Both paragraphs describe the core agent initializer. The SCOP bridge resolves separately and does read the agent tier, as well as endpoint and apiKeyEnv, when it builds an LLMConfiguration — and an external configuration source can override any of them per agent. See External LLM Configuration.

@LLMParam / @LLMParams — Wired

Bind action parameters to LLM-extracted values.

Memory & retrieval

@Memory / @VectorMemory — Wired / Partial

Configure the agent's memory store. Read from the agent class only — on a Role these are introspection/export metadata and do not change memory behaviour. @VectorMemory.provider has no provider SPI lookup yet: inmemory uses the base store with TF-IDF search, and every other value falls back to that same store after logging a notice. @Memory.vector() has no runtime consumer at all.

@MemorySpec — Nested-wired

Memory config nested in @AgentSpec(memory = …); also available programmatically as MemoryConfig.builder() via AgentBuilder.memoryConfig(...).

@KnowledgeSource / @KnowledgeSources — Wired

Declares the corpora a Role retrieves from. All six fields are honoured. It describes ingestion only; how documents are searched is @Retrieval's. FILE is the one bundled loader — URL, DATABASE and MEMORY are SourceLoader SPI extension points an optional module registers against.

A source whose type has no installed loader is skipped with a warning. The Role still binds from whichever sources did load. Binding fails only when every enabled source is unloadable. A missing loader costs part of the corpus; it does not fail dispatch by itself.

@Retrieval — Wired

Retrieval tuning. All 21 fields are honoured and all 8 strategies have a runtime path. queryParam selects a named action argument, while navigatorModel selects the installed TreeNavigator for Strategy.REASONING. GRAPH resolves the bundled FILE-corpus, KnowledgeTools, or Neo4j adapters, while an explicit application provider overrides the framework defaults. Reranking, query expansion (including MULTI_QUERY), and REASONING resolve model SPIs that TnsAI deliberately ships no provider for; the framework side (resolution, validation, capping, and error classification) is complete.

On Maven Central 0.14.1, an unservable rerankerModel or queryExpansionModel fails at dispatch, inside the span @Retrieval.onFailure guards. CONTINUE can then emit an ungrounded answer. GRAPH already treats a missing provider as a configuration error above that guard.

Since 0.15.0 (TnsAI@cb14410d, PR #198 / TAN-5887) those two model names resolve when Role.create establishes the retrieval configuration. An unservable name is a wiring-time configuration error and is not reachable through onFailure. Failures a provider raises while ranking or expanding still follow onFailure. Roles with no effective @Retrieval, and declarations that do not ask for rerank or expansion, do not load those provider graphs. To inspect what resolved without throwing, see RAG diagnostics (RagDiagnostics, @since 0.15.0).

Guardrails & safety

@InputGuardrail / @OutputGuardrail — Wired

Validate/transform input before, and output after, an action. These annotations are not the supervisor Guardrail interface and not the quality tracing Guardrail SPI — see Guardrails.

@Security — Wired

Permission checks and sandboxing, enforced by SecurityEnforcer.

@ApprovalRequired — Wired

Requires human approval before an action runs (raises ApprovalRequiredException).

Resilience & observability

@Resilience — Partial

Umbrella resilience policy. Retry and timeout are enforced today; circuit-breaker, rate-limit, and bulkhead fields are scaffolded for a later release. The builder counterpart is RoleBuilder.resilience(ResilienceConfig…).

@Traced / @Metered — Wired

Tracing and metrics decorators around actions.

@Fallback — Wired

Declares a fallback method, resolved by FallbackResolver.

Lifecycle, events & coordination

@Lifecycle — Wired

Lifecycle phase hooks.

@EventHandler / @EventEmitter — Wired / Scaffold

@EventHandler methods are dispatched by EventHandlerProcessor. @EventEmitter is not yet wired.

@Coordination — Partial

Multi-agent coordination; only the nested negotiation() configuration is honoured today.

@Communication / @CommunicationStyle — Wired

Agent communication style, tone, and languages.

Identity

@DIDSpec — Nested-wired

W3C Decentralised Identifier config, nested in @AgentSpec(did = …).

@GroupMember — Nested-wired

Group membership, nested in @AgentSpec(groups = …).

Scaffold & being-consolidated

These exist but have no runtime effect yet (wiring planned) — don't rely on them: @Norm / @Norms (deontic logic), @EvalMetric (custom evaluation metrics), @QualityGate (release gates), @AuditLog (declarative audit), @OutputFormatSpec (prettyPrint/schema not yet honoured).

Recently consolidated

During the 0.11.x annotation cleanup, 23 unwired annotations were removed in favour of their working counterparts. 0.14.0 continues the same delete-don't-deprecate policy for @Param. If you were using any of these, switch to the replacement:

RemovedUse instead
@Param (removed in 0.14.0)the Java parameter name (-parameters); @ToolParam / @LLMParam if you need a live annotation
@LLM (type-level)@LLMSpec nested in @RoleSpec/@AgentSpec
@RateLimited@Resilience(rateLimit = …)
@OnConnect / @OnDisconnect / @OnMessagethe Channel interface methods
@FSMState / @FSMTransition / @FSMStates / @FSMTransitionsStateMachine.builder()
@SystemPrompt@RoleSpec.description (or @LLMSpec.systemPrompt)
@ChannelSpecthe Channel interface
@Pipeline / @PipelineStepPipelineBuilder
@RequiresPairing@Security
@ContextCompaction, @SlashCommand, @WorkspaceSpec, @Property, @ConfigProperty, @Trigger, @Delegate, @Sanitize, @ContentFilterremoved — no runtime consumer existed

Each removal is a ### Removed entry in the Changelog with the rationale.

See also