Redouble AI Nucleo documentation
Part 13 · Inside the runtime

Inside the artifact tools

ai.redouble.nucleo.harness.artifacts.tools · nucleo-core

This page is for people working on the runtime itself: why retrieval goes through the registry, what conveyance guarantees, how the registry reaches a tool, and the classes of ai.redouble.nucleo.harness.artifacts.tools. The guide for the two tools is Artifact tools.


Why retrieval goes through the registry

Handing the LLM the full object and asking it to carry the parts it needs would collapse the property the artifact system is built on. Two things hold only because content travels outside the model's output:

Content integrity. An agent cannot paraphrase, truncate, or renumber what it never writes. The model's response schema has no slot for artifact content, so identifiers that look authoritative and drift silently under paraphrase (DOIs, PMIDs, claim numbers, SMILES strings) cannot degrade across a chain of agents.

Capability decoupling. A recipient agent receives conveyed artifacts without holding the tool that produced them, that tool's permissions, or any credential for dereferencing the data. It has the data because the framework carried it. This is what lets agents stay specialized: a downstream agent that reasons over a claim record does not need the claim-fetching tool in its own registry, and its tool list stays small.

Conveyance is deep-equal: field content preserved recursively to primitive values, along with type information, the distinction between null and absent, and binary fidelity. Within a JVM the recipient sees the same instance. Across a serialization boundary a round trip preserves those properties without requiring a canonical byte sequence.

Artifact Reference Format

All artifact references use the canonical format: «artifact:type~uuid» (with guillemet delimiters). This is the format used in:

ArtifactRegistry.normalizeToKey() is the single normalization function that maps any variant (bare ID, with/without prefix, with/without guillemets) to this canonical format.

Tools

ToolPurpose
get_artifact_fieldRetrieve the content of a specific field, in slices of maxChars from offset. Accepts snake_case or camelCase field names
search_artifact_contentExact case-insensitive search across all String fields of every artifact, the reachable tier included

Both tools read the live artifact via reflection. They walk the class hierarchy so inherited fields are visible.

Registry Injection

Tools need access to the conversation's artifact registry. The ArtifactRegistryAware interface enables this:

public interface ArtifactRegistryAware {
    void setArtifactRegistry(ArtifactRegistry registry);
    ArtifactRegistry getArtifactRegistry();
}

AbstractThinker.submitToolCall() checks if a tool implements this interface and injects the registry before execution. A tool of this package dispatched without that injection - run outside a thinker - fails with SystemException: the missing registry is the caller's wiring fault, never a result the model reads.

The injected registry is not always the full one. When the invoked tool is itself a thinker whose input declares artifactRefs, it receives ArtifactRegistry.filter(refs): a registry holding only the artifacts that were conveyed to it. A sub-agent sees what it was given and nothing else, which bounds how much a compromised or misdirected agent can reach as well as how much it must reason over.

Classes

ClassPurpose
ArtifactRegistryAwareInterface for registry injection
GetArtifactFieldToolRetrieves full field content
GetArtifactFieldInputInput: artifactRef, fieldName, offset, maxChars
GetArtifactFieldOutputOutput: content, fieldName, length, truncated flag
SearchArtifactContentToolSearches across artifact content
SearchArtifactContentInputInput: query, optional artifactType and artifactRef filters, maxResults, contextChars
SearchArtifactContentOutputOutput: list of matches, totalMatches
SearchMatchSingle match with snippet and position
ReflectiveFieldsShared field lookup for LLM-supplied names: snake_case/camelCase resolution up the class hierarchy (find), and the addressable field-name surface (fieldNames)
KeyedRecordAn artifact whose filterable fields are a dynamic name->value set rather than static Java fields - the bridge for record-shaped artifacts whose columns are decided at run time, so a list-processing predicate can address a data-defined field by the name the caller asked for
^