Skip to content

Reference

This reference is organized by exported API surface. Every exported symbol is listed explicitly below for predictable coverage and navigation.

Module

NimbleAgents.NimbleAgents Module
julia
NimbleAgents

A lightweight Julia framework for building tool-using LLM agents.

source

Core Types and Runtime

NimbleAgents.@tool Macro
julia
@tool [return_direct=true] function f(args...) ... end

Define a Julia function and automatically register it as a NimbleTool (with name, description, and JSON parameter schema inferred from the function signature and its docstring).

A variable <funcname>_tool is created in the calling scope holding the resulting NimbleTool object.

When return_direct=true, the agent loop short-circuits immediately after this tool executes — its return value becomes the agent's final output without any further LLM call. Useful for lookup tools, cache hits, or any tool whose result is already the definitive answer.

Example — standard tool

julia
@tool function add(x::Int, y::Int)
    "Add two integers together."
    x + y
end

Example — return_direct tool

julia
@tool return_direct=true function lookup_faq(question::String)
    "Look up a frequently asked question. Returns a definitive answer."
    faq_db[question]
end
# When the agent calls lookup_faq, its result is returned immediately —
# no follow-up LLM call is made.
source
NimbleAgents.AbstractTool Type
julia
AbstractTool

Abstract supertype for all tool definitions that can be exposed to the model.

source
NimbleAgents.Tool Type
julia
NimbleTool(; name, parameters, description, callable, return_direct, strict)

A tool with all the fields of Tool plus return_direct::Bool.

When return_direct = true, the agent loop short-circuits immediately after this tool executes — its return value becomes the agent's final output without any further LLM call.

Use @tool (or @tool return_direct=true) to create tools; you rarely need to construct NimbleTool directly.

source
NimbleAgents.NimbleTool Type
julia
NimbleTool(; name, parameters, description, callable, return_direct, strict)

A tool with all the fields of Tool plus return_direct::Bool.

When return_direct = true, the agent loop short-circuits immediately after this tool executes — its return value becomes the agent's final output without any further LLM call.

Use @tool (or @tool return_direct=true) to create tools; you rarely need to construct NimbleTool directly.

source
NimbleAgents.ToolMessage Type
julia
ToolMessage(; content=nothing, raw="", tool_call_id="", name="", args=nothing)

Represents a tool result message in conversation history.

source
NimbleAgents.build_tool_map Function
julia
build_tool_map(tools) -> Dict{String, Tool}

Convert a vector of Tool objects into a name-keyed dict for fast dispatch.

source
NimbleAgents.tools_schema Function
julia
tools_schema(tools) -> Vector{Dict}

Render a vector of Tools into the JSON-serialisable list that OpenAI-compatible APIs expect under the tools key.

source
NimbleAgents.dispatch_tool Function
julia
dispatch_tool(tool_map, name, args) -> Any

Look up name in tool_map and call the corresponding tool with args (a Dict{Symbol, Any}). Returns the tool's return value, or rethrows on error.

Argument ordering uses the required list from the tool's JSON schema (which reflects the original parameter order), so this is robust to Julia mangling method argument names in test environments.

source
julia
dispatch_tool(tool_map, msg::ToolMessage) -> Any

Convenience overload that accepts a ToolMessage directly (as returned by NimbleAgents when parsing an LLM response with tool calls).

source
NimbleAgents.Agent Type
julia
Agent(; name, instructions, tools, model, max_iterations, output_type, hooks)

A configured AI agent with a system prompt, a set of tools, and a model.

Fields

  • name::String: Human-readable name for the agent.

  • instructions::Union{String, Function}: The system prompt — what the agent does and how it behaves. Can be a static String or a callable (session, agent) -> String for dynamic prompts (e.g. per-user context, RAG injection, time-aware instructions).

  • tools::Vector{Tool}: Tools the agent can call.

  • model::String: Model identifier (default: "gpt-5.4-mini").

  • max_iterations::Int: Maximum number of LLM calls before the loop stops (default: 10).

  • output_type::Union{Type, Nothing}: When set, the final response is parsed into this Julia struct instead of returned as a plain String.

  • api_kwargs::NamedTuple: Extra keyword arguments passed through to every internal LLM call (aitools, aigenerate, aiextract). Use this for model-specific features like OpenAI or Gemini reasoning config (default: NamedTuple()).

  • hooks::AgentHooks: Optional lifecycle callbacks (default: all no-ops).

  • sub_agents::Vector{Agent}: Child agents the LLM can hand off to. A handoff_tool is generated automatically for each one — no manual wiring needed.

  • retry::RetryConfig: Exponential-backoff retry policy for LLM API calls (default: 3 retries, 0.5s–60s window). Set retry=RetryConfig(max_retries=0) to disable.

  • context::ContextConfig: Context-window management policy. When session.history exceeds context.compact_threshold × context.context_window tokens, older messages are summarised and replaced, keeping the most recent context.keep_last messages verbatim.

  • skills::Vector{Skill}: Explicitly attached skills. Metadata is injected into the system prompt; full instructions are loaded on demand via the built-in read_skill tool.

  • skill_dirs::Vector{String}: Directories to scan for skill subdirectories at run time. Discovered skills are merged with any explicitly listed in skills.

  • max_tool_output::Int: Global character limit for tool result strings inserted into the conversation (default: 0 = unlimited). When a tool result exceeds this limit, it is trimmed with head+tail preservation and an informative gap marker. Per-tool limits (NimbleTool.max_output) override this when set.

  • cache::Union{Nothing, Symbol}: Reserved for provider-specific prompt caching strategies. Cached tokens are tracked in TurnEvent.cache_read_tokens and cache_write_tokens when the provider returns them.

Example — plain text output

julia
@tool function add(x::Int, y::Int)
    "Add two integers."
    x + y
end

agent = Agent(
    name         = "MathBot",
    instructions = "You are a helpful assistant that can do arithmetic.",
    tools        = [add_tool],
)

result = run!(agent, "What is 3 + 4?")  # String

Example — with session memory

julia
session = Session(app_name="MyApp", user_id="alice")
run!(agent, "What is 8 + 14?"; session=session)
run!(agent, "Now multiply that by 3"; session=session)  # remembers 22
source
NimbleAgents.AgentHooks Type
julia
AgentHooks(; before_llm_call, after_llm_call, should_interrupt, on_tool_call, on_tool_result, on_complete)

Optional lifecycle callbacks for an Agent. All fields default to nothing (no-op). Provide a function to observe or log that event.

Callbacks

FieldSignatureFired
before_llm_call(agent, iteration, messages) -> messagesBefore each LLM request — can modify the messages vector
after_llm_call(agent, iteration, response)After each LLM response — before any tool executes
should_interrupt(tool_name, args) -> BoolBefore each tool executes — return true to pause and require human approval
on_tool_call(agent, tool_name, args)Before each tool is executed (after approval)
on_tool_result(agent, tool_name, result)After each tool returns
on_complete(agent, result)When run! is about to return

should_interrupt is the recommended way to gate dangerous tools. When it returns true for any pending tool call, the framework collects all flagged calls, throws a HumanInterrupt, and no tools execute. Call resume!(session, response) then re-run run! to continue.

Example — gate dangerous tools

julia
hooks = AgentHooks(
    should_interrupt = (name, args) -> name in ["send_email", "delete_file"]
)
agent = Agent(name="Bot", instructions="...", hooks=hooks)

try
    run!(agent, "Send an email and delete the log"; session=session)
catch e
    e isa HumanInterrupt || rethrow(e)
    println(e.message)           # "About to call: send_email, delete_file"
    resume!(session, readline()) # inject human response
    run!(agent, "Send an email and delete the log"; session=session)
end

Example — observability only

julia
hooks = AgentHooks(
    on_tool_call   = (ag, name, args)   -> println("calling $name with $args"),
    on_tool_result = (ag, name, result) -> println("$name returned $result"),
    on_complete    = (ag, result)       -> println("done: $result"),
)
source
NimbleAgents.RetryConfig Type
julia
RetryConfig(; max_retries, initial_delay, max_delay, multiplier, jitter,
              retry_on_status, max_parse_retries)

Exponential-backoff retry policy for LLM API calls made inside run!.

Fields

  • max_retries::Int: Maximum number of retry attempts after the first failure (default: 3).

  • initial_delay::Float64: Seconds to wait before the first retry (default: 0.5). Consensus across Anthropic SDK and LangGraph; fast enough for transient errors.

  • max_delay::Float64: Maximum seconds to wait between retries (default: 60.0). Chosen to match the standard 1-minute rate-limit reset window used by both OpenAI and Anthropic — capping here means later retries will wait long enough to clear a sustained 429 burst without hanging indefinitely.

  • multiplier::Float64: Exponential growth factor (default: 2.0). Universal across all reference SDKs (OpenAI, Anthropic, ADK, LangGraph).

  • jitter::Bool: When true, multiplies each delay by a random factor in [0.75, 1.0] (Anthropic-style multiplicative jitter). Prevents thundering-herd when many parallel agents retry simultaneously (default: true).

  • retry_on_status::Vector{Int}: HTTP status codes that warrant a retry.

    • 408 request timeout, 429 rate limit — always transient

    • 500/502/503/504 server-side errors — usually transient

    • 529 Anthropic-specific overload status

    4xx errors outside this list (401, 400, 403, 404) are permanent failures and are never retried regardless of this setting.

  • max_parse_retries::Int: Maximum number of re-prompts when output_type parsing fails (default: 2). On each failure the parse error is fed back to the LLM as a user message so it can correct its response. Set to 0 to disable.

Example

julia
agent = Agent(
    name   = "Bot",
    instructions = "...",
    retry  = RetryConfig(max_retries=5, max_delay=120.0),
)
source
NimbleAgents.ContextConfig Type
julia
ContextConfig(; context_window, compact_threshold, keep_last, summary_model)

Controls automatic context-window management for long-running sessions.

When session.history grows large enough to risk hitting the model's context limit, NimbleAgents compacts it using a hybrid strategy:

  1. The most recent keep_last messages are always kept verbatim — they carry the immediate context the model needs for the current turn.

  2. Everything older is summarised into a single compressed message by the LLM, replacing the raw history.

This mirrors the approach used by Claude Code and Codex: compact at ~80% of the context window, not at 100%, so there is always headroom for the current turn's input and the model's output.

Fields

  • context_window::Int: Total token capacity of the model (default: 400_000). Set to match Claude Opus 4.5/4.6 (400k context, 128k max output).

  • compact_threshold::Float64: Fraction of context_window at which compaction is triggered (default: 0.80). At 80% × 400k = 320k tokens, there is still 80k of headroom — enough for a large current-turn input plus a full output. Claude Code and Codex both use ~80% as their trigger.

  • keep_last::Int: Number of recent messages to preserve verbatim after compaction (default: 20). These are never summarised so the agent retains immediate conversational context.

  • summary_model::Union{String,Nothing}: Model used for the summarisation call. Defaults to nothing, which means the agent's own model is used.

Example

julia
# Long-running session with aggressive compaction
session_agent = Agent(
    name    = "LongBot",
    instructions = "...",
    context = ContextConfig(keep_last=10),
)
source
NimbleAgents.run! Function
julia
run!(agent, input; session, verbose, on_token, approval_channel, approval_timeout) -> Union{String, Any}

Run the agent loop on input.

  • If agent.output_type is nothing → returns a String.

  • If agent.output_type is set → returns an instance of that type.

Arguments

  • session::Union{Session, Nothing}: Pass a Session to retain conversation history, key-value state, and an event log across calls.

  • verbose::Bool: Print iteration info (default true).

  • on_token::Union{Function, Nothing}: When set, the final LLM response is streamed token-by-token. on_token(token::String) is called for each chunk as it arrives. Tool-call rounds are always blocking (streaming + tool calls are not supported by the underlying API). Pass on_token = token -> print(token) to stream directly to the terminal.

  • approval_channel::Union{Channel{String}, Nothing}: When set alongside should_interrupt, the agent pauses mid-loop waiting for a response on this channel instead of throwing HumanInterrupt. Use this for non-blocking approval flows (web servers, notebooks, GUIs) where the human's response arrives asynchronously from another thread or HTTP handler. Put "approve" to proceed, any other string to redirect, or close the channel to abort. The agent holds all its state while waiting — no re-run needed.

  • approval_timeout::Float64: Seconds to wait for a response on approval_channel before throwing an ApprovalTimeout error (default: 300.0).

Example — CLI (blocking, throw-based)

julia
run!(agent, "Write a short poem"; on_token = token -> print(token))

Example — async approval (non-blocking, channel-based)

julia
ch   = Channel{String}(1)
task = Threads.@spawn run!(agent, input; session=session, approval_channel=ch)
# agent is paused waiting for approval — this thread is free
put!(ch, "approve")   # unblocks the agent from anywhere
result = fetch(task)
source
NimbleAgents.HumanInterrupt Type
julia
HumanInterrupt(tool_calls; message)

Thrown from after_llm_call to pause the agent loop before any tool executes.

The LLM has produced a plan (tool_calls) but no side-effects have occurred yet. The caller catches this, presents the pending actions to a human, and either:

  • Approves → calls resume!(session, "Approved") and re-runs run!

  • Rejects → calls resume!(session, "Rejected — do X instead") and re-runs run!

  • Aborts → discards the session entirely

Fields

  • tool_calls: The pending tool calls the LLM intended to execute.

  • message::String: Optional context message (default: "Human approval required").

Example

julia
hooks = AgentHooks(
    after_llm_call = (agent, iter, msg) -> begin
        dangerous = ["delete_file", "send_email", "write_db"]
        pending   = filter(t -> t.name in dangerous, something(msg.tool_calls, []))
        isempty(pending) && return
        throw(HumanInterrupt(pending;
            message = "About to: " * join([t.name for t in pending], ", ")))
    end
)
source
NimbleAgents.ApprovalTimeout Type
julia
ApprovalTimeout(tool_calls, timeout)

Thrown when an approval_channel is provided but no response arrives within approval_timeout seconds.

source
NimbleAgents.resume! Function
julia
resume!(session, human_response)

Inject a human approval/rejection into session.history so the agent can continue after a HumanInterrupt. Call run! again after this.

julia
try
    run!(agent, input; session=session)
catch e
    e isa HumanInterrupt || rethrow(e)
    println("Pending: ", e.message)
    response = readline()
    resume!(session, response)
    run!(agent, input; session=session)
end
source

Guardrails and Rate Limits

NimbleAgents.Guardrail Type
julia
Guardrail(; name, check, on=:input)

A check that runs at the boundary of the agent loop.

Fields

  • name::String: Human-readable label shown in verbose output.

  • check::Function: (value::String) -> GuardrailResult. Return Pass() to continue, Block(reason) to halt, or Modify(new_value) to rewrite.

  • on::Symbol: When to run — :input (before the agent loop) or :output (after the agent produces its final response). Default: :input.

Examples

julia
# Rule-based input guardrail
no_pii = Guardrail(
    name  = "no_pii",
    on    = :input,
    check = input -> occursin(r"\d{3}-\d{2}-\d{4}", input) ?
                     Block("Input contains a Social Security Number.") : Pass(),
)

# Sanitising input guardrail
strip_html = Guardrail(
    name  = "strip_html",
    on    = :input,
    check = input -> Modify(replace(input, r"<[^>]+>" => "")),
)

# Output guardrail
no_links = Guardrail(
    name  = "no_links",
    on    = :output,
    check = output -> occursin(r"https?://", output) ?
                      Block("Response contained external links.") : Pass(),
)

agent = Agent(
    name       = "SafeBot",
    guardrails = [no_pii, strip_html, no_links],
    ...
)
source
NimbleAgents.Pass Type
julia
Pass()

Guardrail result — allow execution to continue unchanged.

source
NimbleAgents.Block Type
julia
Block(reason::String)

Guardrail result — halt execution and return reason as the agent's response.

source
NimbleAgents.Modify Type
julia
Modify(value::String)

Guardrail result — replace the current input or output with value and continue.

source
NimbleAgents.RateLimiter Type
julia
RateLimiter

Token-bucket rate limiter. Allows up to rate requests per second, with a burst capacity equal to rate.

source
NimbleAgents.set_rate_limit! Function
julia
set_rate_limit!(model::String, requests_per_second::Real)
set_rate_limit!(::Symbol, requests_per_second::Real)

Set a rate limit for a specific model (e.g. "gpt-5.4-mini") or for all models (:default). The limiter allows up to requests_per_second LLM calls per second with short bursts up to that same number.

Example

julia
# Limit gpt-5.4-mini to 10 requests/second
set_rate_limit!("gpt-5.4-mini", 10)

# Limit all models to 20 requests/second (unless overridden per-model)
set_rate_limit!(:default, 20)
source
NimbleAgents.remove_rate_limit! Function
julia
remove_rate_limit!(model::String)
remove_rate_limit!(::Symbol)

Remove a previously set rate limit.

source

Pricing and Cost

NimbleAgents.set_model_pricing! Function
julia
set_model_pricing!(model, input_per_million, output_per_million)

Register USD pricing for a model.

input_per_million and output_per_million are the cost per 1 million tokens.

Example

julia
set_model_pricing!("gpt-5.4-mini", 0.40, 1.60)
set_model_pricing!("claude-sonnet-4-6", 3.00, 15.00)
source
NimbleAgents.get_model_pricing Function
julia
get_model_pricing(model) -> Union{NamedTuple{(:input,:output)}, Nothing}

Look up pricing for a model. Returns nothing if no pricing is registered.

source
NimbleAgents.remove_model_pricing! Function
julia
remove_model_pricing!(model)

Remove a previously registered pricing entry.

source
NimbleAgents.compact! Function
julia
compact!(session, agent) -> Bool

Check whether session.history is approaching the model's context limit and, if so, compress older messages into a summary while keeping the most recent agent.context.keep_last messages verbatim.

Returns true if compaction was performed, false otherwise.

The hybrid strategy:

  • Recent messages (keep_last) are always preserved — they carry the immediate context the model needs.

  • Older messages are replaced by a single LLM-generated summary injected as a UserMessage tagged [Conversation Summary].

Compaction is triggered when estimated token usage exceeds context_window × compact_threshold (default: 80% of 400k = 320k tokens).

source

Sessions and Persistence

NimbleAgents.Session Type
julia
Session(; id, app_name, user_id)

An in-memory session that persists conversation state across multiple run! calls.

Inspired by Google ADK's Session model. Holds three things:

  • history — the message log the LLM sees on every turn

  • state — a free-form key-value store for cross-turn variables

  • events — an audit log of every turn (inputs, outputs, tool calls, token usage)

Fields

  • id::String: Unique session identifier (auto-generated UUID if not provided).

  • app_name::String: Name of the application using this session.

  • user_id::String: Identifier for the user (default "default").

  • history::Vector{AbstractMessage}: Accumulated message history.

  • state::Dict{String,Any}: Cross-turn key-value store.

  • events::Vector{TurnEvent}: Ordered log of every completed turn.

  • created_at::Float64: time() when the session was created.

  • updated_at::Float64: time() when the session was last saved (used for TTL expiry).

  • lock::ReentrantLock: Protects history and events for concurrent fan_out / spawn_subagents calls.

Example

julia
session = Session(app_name="MathApp", user_id="alice")

run!(agent, "What is 8 + 14?"; session=session)
run!(agent, "Now multiply that by 3"; session=session)

# Inspect history
length(session)           # number of messages
session.state["score"]    # cross-turn variable set by a tool or hook
session.events[1]         # TurnEvent for the first run! call
source
NimbleAgents.TurnEvent Type
julia
TurnEvent

A record of one complete run! invocation — one "turn" in the conversation.

Fields

  • agent::String: Name of the agent that handled this turn.

  • model::String: LLM model identifier used for this turn.

  • input::String: The user message for this turn.

  • output::Any: The final response returned by run!.

  • tool_calls::Vector{ToolEvent}: All tool calls made during this turn, in order.

  • llm_calls::Int: Number of LLM requests made.

  • input_tokens::Int: Total input tokens used across all LLM calls this turn.

  • output_tokens::Int: Total output tokens used across all LLM calls this turn.

  • cache_read_tokens::Int: Tokens read from prompt cache (discounted cost).

  • cache_write_tokens::Int: Tokens written to prompt cache (premium cost on Anthropic).

  • cost::Float64: Estimated USD cost for this turn (cache-adjusted when available).

  • elapsed::Float64: Wall-clock time in seconds for the whole turn.

  • timestamp::Float64: time() when run! was called.

source
NimbleAgents.ToolEvent Type
julia
ToolEvent

A record of one tool call made during a run! turn.

Fields

  • name::String: Tool name.

  • args::Dict{Symbol,Any}: Arguments passed to the tool.

  • result::Any: Return value (or nothing if an error occurred).

  • error::Union{String,Nothing}: Error message if the tool threw, otherwise nothing.

  • timestamp::Float64: time() when the tool was called.

source
NimbleAgents.reset! Function
julia
reset!(session)

Clear history, state, and events from the session. Preserves id/app_name/user_id.

source
NimbleAgents.Artifact Type
julia
Artifact(; session_id, name, type, content_type, path, metadata)

A named, typed output produced by an agent during a session.

Fields

  • id::String: UUID identifying this artifact.

  • session_id::String: Session that produced this artifact.

  • name::String: Human-readable label.

  • type::Symbol: :file, :plot, :data, or :text.

  • content_type::String: MIME type ("image/png", "text/csv", etc.).

  • path::String: Path to the artifact file.

  • metadata::Dict{String,Any}: Arbitrary extra info (source tool, size, etc.).

  • created_at::Float64: time() when registered.

source
NimbleAgents.register_artifact! Function
julia
register_artifact!(session, path; name, store) -> Artifact

Register a file as an artifact in the session. If store is provided, the file is copied into the artifact store directory; otherwise the original path is recorded as-is.

Called automatically by run! for return_artifact=true tools, by eval_julia_tool for saved plots, and by save_artifact_tool.

source
NimbleAgents.AbstractSessionStore Type
julia
AbstractSessionStore

Interface for session persistence backends. Implement:

  • save!(store, session)

  • load(store, session_id) -> Union{Session, Nothing}

  • delete!(store, session_id)

  • list(store; app_name, user_id) -> Vector{String}

  • store_artifacts_dir(store) -> String

source
NimbleAgents.InMemorySessionStore Type
julia
InMemorySessionStore()

An in-memory session store. Sessions are kept for the lifetime of the process and lost on restart. Suitable as the default for serve() and for testing.

Artifacts are stored in a temp directory that is also ephemeral.

julia
store   = InMemorySessionStore()
session = Session(app_name="MyApp", user_id="alice")
save!(store, session)
load(store, session.id)  # → same Session object
source
NimbleAgents.JSONSessionStore Type
julia
JSONSessionStore(dir)

Persist sessions as JSON files in dir. Artifacts are copied into dir/../artifacts/<session_id>/.

julia
store = JSONSessionStore(".nimble/sessions")
save!(store, session)
session = load(store, session_id)
source
NimbleAgents.SQLiteSessionStore Type
julia
SQLiteSessionStore(path; artifacts_dir=nothing)

SQLite-backed AbstractSessionStore implementation, loaded via NimbleAgentsSQLiteExt when SQLite.jl and DBInterface.jl are available.

source
NimbleAgents.save! Function
julia
save!(store, session) -> Session

Persist session into store.

Arguments

  • store: Session store backend.

  • session::Session: Session to persist.

Returns

  • Session: The persisted session object.
source
NimbleAgents.load Function
julia
load(store, session_id) -> Union{Session, Nothing}

Load a persisted session by id.

Arguments

  • store: Session store backend.

  • session_id::String: Session identifier to load.

Returns

  • Session: Loaded session when found.

  • nothing: If no persisted session exists for session_id.

source
NimbleAgents.list Function
julia
list(store; app_name=nothing, user_id=nothing) -> Vector{String}

List persisted session ids, optionally filtered by scope.

Arguments

  • store: Session store backend.

  • app_name::Union{String,Nothing}: Optional application filter.

  • user_id::Union{String,Nothing}: Optional user filter.

Returns

  • Vector{String}: Matching session ids.
source
NimbleAgents.store_artifacts_dir Function
julia
store_artifacts_dir(store) -> String

Return the directory where a session store persists artifact files.

Arguments

  • store: Session store backend.

Returns

  • String: Absolute or relative artifacts directory path.
source
NimbleAgents.cleanup! Function
julia
cleanup!(store; max_age, before) -> Int

Delete expired sessions from the store and return the number removed.

Arguments

  • store: Session store backend.

  • max_age::Real: Delete sessions not updated in the last max_age seconds.

  • before::Float64: Delete sessions with updated_at < before (Unix timestamp).

Provide exactly one of the two keyword arguments.

Returns

  • Int: Number of sessions deleted.
source

Memory

NimbleAgents.AbstractMemoryService Type
julia
AbstractMemoryService

Abstract type for memory backends. Implementations must define:

  • add_memory!(service, content; user_id, app_name, metadata, session_id) -> MemoryEntry

  • search_memory(service, query; user_id, app_name, top_k) -> Vector{MemoryEntry}

  • delete_memory!(service, id)

  • list_memories(service; user_id, app_name) -> Vector{MemoryEntry}

  • close!(service)

source
NimbleAgents.InMemoryMemoryService Type
julia
InMemoryMemoryService()

In-memory memory backend using keyword search. Good for testing and short-lived applications. All data is lost when the process exits.

source
NimbleAgents.SQLiteMemoryService Type
julia
SQLiteMemoryService(path)

SQLite-backed AbstractMemoryService implementation, loaded via NimbleAgentsSQLiteExt when SQLite.jl and DBInterface.jl are available.

source
NimbleAgents.MemoryEntry Type
julia
MemoryEntry(; content, user_id, app_name, metadata, source_session_id)

A single stored fact in the memory system.

Fields

  • id::String: Unique identifier (auto-generated UUID).

  • content::String: The fact text.

  • user_id::String: User who owns this memory.

  • app_name::String: Application scope.

  • metadata::Dict{String,Any}: Arbitrary key-value metadata.

  • source_session_id::Union{String,Nothing}: Session that created this memory.

  • created_at::Float64: time() when the memory was created.

source
NimbleAgents.add_memory! Function
julia
add_memory!(service, content; user_id="default", app_name="NimbleAgents", metadata=Dict{String,Any}(), session_id=nothing) -> MemoryEntry

Store a memory entry for a user/application scope.

source
NimbleAgents.search_memory Function
julia
search_memory(service, query; user_id="default", app_name="NimbleAgents", top_k=5) -> Vector{MemoryEntry}

Return the top matching memories for query in the given scope.

source
NimbleAgents.delete_memory! Function
julia
delete_memory!(service, id)

Delete a memory entry by id.

source
NimbleAgents.list_memories Function
julia
list_memories(service; user_id=nothing, app_name=nothing) -> Vector{MemoryEntry}

List memories, optionally filtered by user and/or app.

source

Skills

NimbleAgents.Skill Type
julia
Skill(; name, description, path)

A filesystem-based capability package. Contains a SKILL.md file with instructions that the agent can load on demand.

Use discover_skills(dirs) to auto-discover skills from directories, or construct directly with an explicit path.

Fields

  • name::String: Skill identifier (from YAML frontmatter).

  • description::String: One-line description used in the agent's system prompt to help it decide when to load this skill.

  • path::String: Absolute path to the skill directory.

source
NimbleAgents.discover_skills Function
julia
discover_skills(dirs::Vector{String}) -> Vector{Skill}

Scan each directory in dirs for skill subdirectories. A valid skill directory must contain a SKILL.md file with name and description frontmatter.

Returns all discovered skills. Silently skips directories with missing or malformed SKILL.md files.

Example

julia
skills = discover_skills([".nimble/skills", joinpath(homedir(), ".nimble", "skills")])
source

Multi-Agent and Handoffs

NimbleAgents.Handoff Type
julia
Handoff

A signal returned by an agent indicating that control should transfer to another agent. Use handoff_tool(agent) to create a Tool that an agent can call to trigger the transfer.

Fields

  • target::Agent: The agent to hand off to.

  • message::String: The message to pass to the target agent (defaults to the current user input if empty).

  • history_filter::HandoffFilter: How to transform conversation history on handoff.

The orchestrator loop in run_pipeline! detects Handoff results and re-runs with the new agent automatically.

source
NimbleAgents.HandoffFilter Type
julia
HandoffFilter

Controls how conversation history is transformed when handing off to the next agent. Pass a HandoffFilter to handoff_tool or run_pipeline! to filter the session history before the receiving agent sees it.

Built-in filters (symbols)

  • :all — pass full history unchanged (default)

  • :none — clear history; receiving agent starts fresh

  • :strip_tools — remove all tool-call and tool-result messages

  • :last_n — keep only the last N messages (use HandoffFilter(:last_n, 5))

Custom filter (function)

Pass a function (history::Vector{AbstractMessage}) -> Vector{AbstractMessage} for full control over what the receiving agent sees.

Examples

julia
# Strip tool messages on handoff
handoff_tool(billing; history_filter = HandoffFilter(:strip_tools))

# Keep only last 3 messages
handoff_tool(billing; history_filter = HandoffFilter(:last_n, 3))

# Custom function
handoff_tool(billing; history_filter = HandoffFilter(msgs -> filter(m -> m isa UserMessage, msgs)))
source
NimbleAgents.handoff_tool Function
julia
handoff_tool(target; name, description, history_filter) -> Tool

Create a Tool that, when called by an agent, signals a handoff to target. The LLM passes a message argument containing what to forward to the next agent.

Arguments

  • target::Agent: The agent to hand off to.

  • name::String: Tool name (default: "handoff_to_$(target.name)").

  • description::String: Tool description.

  • history_filter::HandoffFilter: How to transform conversation history before the receiving agent sees it. Default: HandoffFilter() (pass full history).

Example

julia
billing_agent = Agent(name="Billing", instructions="Handle billing questions.")
support_agent = Agent(
    name         = "Support",
    instructions = "Triage customer requests.",
    tools        = [
        handoff_tool(billing_agent; history_filter=HandoffFilter(:strip_tools)),
    ],
)
source
NimbleAgents.agent_as_tool Function
julia
agent_as_tool(agent; name, description, session) -> Tool

Wrap agent as a Tool that a parent (orchestrator) agent can call. The subagent runs a full run! loop for each call and its result is returned as a string back to the orchestrator.

The shared session is threaded through so the subagent's turns appear in the same event log.

Example

julia
math_agent = Agent(name="Math", instructions="Do arithmetic.", tools=[add_tool])

orchestrator = Agent(
    name         = "Orchestrator",
    instructions = "Route requests to specialist agents.",
    tools        = [agent_as_tool(math_agent)],
)

run!(orchestrator, "What is 3 + 4?")
source
NimbleAgents.run_pipeline! Function
julia
run_pipeline!(agent, input; session, verbose, max_handoffs) -> Any

Like run! but with automatic handoff support. When a tool returns a Handoff, the pipeline transparently re-runs with the target agent and the forwarded message. The loop stops when the active agent returns a plain result (not a Handoff) or max_handoffs is reached.

Arguments

  • agent::Agent: The starting agent.

  • input::String: The initial user message.

  • session: Optional shared Session across all agents in the pipeline.

  • verbose::Bool: Print handoff transitions (default true).

  • max_handoffs::Int: Safety cap on the number of handoffs (default 10).

Example

julia
result = run_pipeline!(triage_agent, "I need help with my bill";
                       session=session)
source
NimbleAgents.loop_pipeline! Function
julia
loop_pipeline!(agents, input; stop_when, max_rounds, session, verbose) -> Any

Run agents in round-robin order, feeding each agent's output as the next agent's input, until stop_when returns true or max_rounds is reached.

Each "round" consists of one pass through all agents in order. After every individual agent run, stop_when(agent, result) is checked — if it returns true, the loop ends immediately and that result is returned.

Arguments

  • agents::Vector{Agent}: Agents to cycle through in order.

  • input::String: The initial user message.

  • stop_when: A function (agent, result) -> Bool that signals termination. Default: always false (loop runs until max_rounds).

  • max_rounds::Int: Safety cap on the number of full rounds (default 5).

  • session: Optional shared Session across all agents.

  • verbose::Bool: Print round/agent transitions (default true).

Example

julia
coder    = Agent(name="Coder",    instructions="Write code based on the task.")
reviewer = Agent(name="Reviewer", instructions="Review code. Say APPROVED if good.")

result = loop_pipeline!(
    [coder, reviewer],
    "Write a fibonacci function";
    max_rounds = 5,
    stop_when  = (agent, result) -> occursin("APPROVED", string(result)),
    session    = Session(),
)
source
NimbleAgents.fan_out Function
julia
fan_out(agent, inputs; reducer, parallel, session, verbose) -> Any

Run agent against each element of inputs, then combine the results.

  • parallel = false (default): runs each input serially in order.

  • parallel = true: spawns each run on the Julia thread pool (Threads.@spawn); result order matches inputs order regardless.

  • reducer: an optional two-argument function (accumulator, result) -> accumulator applied via reduce. Defaults to nothing, which returns Vector{Any}.

  • Each run shares the same session if provided; concurrent writes are protected by session.lock.

Example — serial, default reducer

julia
summaries = fan_out(summarizer, ["chunk 1", "chunk 2", "chunk 3"])
# => Vector{Any} of three responses

Example — parallel with a string-join reducer

julia
report = fan_out(research_agent, topics;
                 parallel = true,
                 reducer  = (acc, x) -> acc * "\n\n" * x)
source
NimbleAgents.spawn_subagents Function
julia
spawn_subagents(pairs; parallel, session, verbose) -> Vector{Any}

Run a list of (agent, input) pairs and return their results in the same order.

  • parallel = false (default): executes each pair serially.

  • parallel = true: spawns each pair concurrently on the Julia thread pool; result order is preserved.

  • Each run shares the same session if provided; concurrent writes are protected by session.lock.

Example — serial

julia
results = spawn_subagents([
    (researcher_agent, "Find facts about X"),
    (analyst_agent,    "Analyse the market for X"),
    (writer_agent,     "Draft an intro for X"),
])
draft = run!(editor_agent, join(results, "\n\n"))

Example — parallel

julia
results = spawn_subagents([
    (researcher_agent, "Topic A"),
    (researcher_agent, "Topic B"),
]; parallel = true, session = session)
source

Tracing and Evaluation

NimbleAgents.Trace Type
julia
Trace(session)
Trace(turns::Vector{TurnEvent})

A lightweight view over a session's event log.

Aggregates token usage, cost, elapsed time, and tool call statistics across all turns. No new data is collected — everything comes from TurnEvent / ToolEvent already recorded by run!.

Fields

  • turns::Vector{TurnEvent}: All turns in order.

  • total_input_tokens::Int: Sum of input tokens across all turns.

  • total_output_tokens::Int: Sum of output tokens across all turns.

  • total_cache_read_tokens::Int: Sum of tokens read from prompt cache.

  • total_cache_write_tokens::Int: Sum of tokens written to prompt cache.

  • total_tokens::Int: total_input_tokens + total_output_tokens.

  • total_cost::Float64: Estimated total USD cost across all turns (cache-adjusted).

  • total_llm_calls::Int: Total number of LLM requests made.

  • total_tool_calls::Int: Total number of tool calls made.

  • duration::Float64: Wall-clock seconds from first turn start to last turn end.

  • agents::Vector{String}: Unique agent names that handled turns (in order of first appearance).

Example

julia
session = Session(app_name="MyApp", user_id="alice")
run!(agent, "What is 2 + 2?"; session=session)

trace = Trace(session)
println("Cost: $", round(trace.total_cost; digits=4))
print_trace(trace)
save_trace(trace, "trace.json")
source
NimbleAgents.print_trace Function
julia
print_trace(trace; io=stdout)

Print a human-readable summary of a Trace to io.

Arguments

  • trace::Trace: Trace to render.

  • io::IO: Output stream (default: stdout).

julia
━━━ Trace ━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  Turns      : 2
  Agents     : MathBot
  Duration   : 3.42s
  LLM calls  : 3
  Tool calls : 2
  Tokens     : 312 in / 88 out / 400 total

  Turn 1 — MathBot  [1.8s | 2 llm | 1 tool | 210 tok]
    input  : What is 2 + 2?
    output : The answer is 4.
    tools  : add(x=2, y=2)  4

  Turn 2 — MathBot  [1.6s | 1 llm | 1 tool | 190 tok]
    input  : Now multiply by 3.
    output : The result is 12.
    tools  : multiply(x=4, y=3)  12
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
source
NimbleAgents.save_trace Function
julia
save_trace(trace, path)

Serialise a Trace to a JSON file at path.

The JSON structure mirrors the Trace fields — suitable for offline analysis, feeding into an evaluation script, or archiving agent runs.

Arguments

  • trace::Trace: Trace data to persist.

  • path::String: Destination file path.

Returns

  • String: The same path that was written.
source
NimbleAgents.load_trace Function
julia
load_trace(path) -> Dict{String, Any}

Load a previously saved trace from a JSON file. Returns the raw parsed Dict — useful for offline analysis or evaluation scripts.

Arguments

  • path::String: Path to a JSON file produced by save_trace.

Returns

  • Dict{String,Any}: Parsed trace payload.
source
NimbleAgents.EvalCase Type
julia
EvalCase(; input, expected, expected_tools, reference, tags)

A single evaluation test case.

Fields

  • input::String: User message to send to the agent.

  • expected::Union{String, Nothing}: Expected answer text (for text-matching metrics).

  • expected_tools::Union{Vector{String}, Nothing}: Expected tool call names in order.

  • reference::Union{Dict{String,Any}, Nothing}: Arbitrary ground-truth metadata.

  • tags::Vector{String}: Tags for filtering/grouping results.

source
NimbleAgents.EvalResult Type
julia
EvalResult

Result for a single eval case.

Fields

  • case::EvalCase: The original test case.

  • output::Any: Actual agent response.

  • trace::Union{Trace, Nothing}: Full trace from run!.

  • scores::Dict{String, Float64}: Metric name => score (0.0–1.0).

  • passed::Bool: Whether all scores met the pass threshold.

  • error::Union{String, Nothing}: Error message if run! threw.

  • elapsed::Float64: Wall-clock time for this case.

source
NimbleAgents.EvalReport Type
julia
EvalReport(results)

Aggregate report over all eval results. Computes pass rate, mean scores, total cost, and total duration from the individual results.

Fields

  • results::Vector{EvalResult}: All individual results.

  • pass_rate::Float64: Fraction of cases that passed.

  • mean_scores::Dict{String, Float64}: Mean score per metric across all cases.

  • total_cost::Float64: Sum of trace costs.

  • total_duration::Float64: Sum of elapsed times.

  • timestamp::Float64: When the report was created.

source
NimbleAgents.exact_match Function
julia
exact_match(case, output, trace) -> Float64

Returns 1.0 if string(output) equals case.expected exactly, else 0.0. Returns 1.0 if case.expected is nothing (no expectation).

source
NimbleAgents.fuzzy_match Function
julia
fuzzy_match(case, output, trace) -> Float64

Returns 1.0 if case.expected is a case-insensitive substring of output. Otherwise returns a normalised similarity score based on Levenshtein distance. Returns 1.0 if case.expected is nothing.

source
NimbleAgents.tool_trajectory Function
julia
tool_trajectory(case, output, trace) -> Float64

Returns 1.0 if the tool names called (in order) match case.expected_tools exactly. Returns 1.0 if case.expected_tools is nothing.

source
NimbleAgents.tool_coverage Function
julia
tool_coverage(case, output, trace) -> Float64

Returns the fraction of case.expected_tools that were actually called (order-insensitive). Returns 1.0 if case.expected_tools is nothing.

source
NimbleAgents.cost_budget Function
julia
cost_budget(max_cost) -> NamedMetric

Factory: returns a metric that scores 1.0 if trace.total_cost <= max_cost, else 0.0.

source
NimbleAgents.latency_budget Function
julia
latency_budget(max_seconds) -> NamedMetric

Factory: returns a metric that scores 1.0 if trace.duration <= max_seconds, else 0.0.

source
NimbleAgents.run_eval Function
julia
run_eval(agent, cases; metrics, verbose, pass_threshold) -> EvalReport

Run an agent against a vector of EvalCases, score each with the given metrics, and return an EvalReport.

Arguments

  • agent::Agent: The agent to evaluate.

  • cases::Vector{EvalCase}: Test cases.

  • metrics: Vector of metric functions (EvalCase, output, Trace) -> Float64. Default: [exact_match, tool_trajectory].

  • verbose::Bool: Whether to pass verbose=true to run!. Default: false.

  • pass_threshold::Float64: Minimum score for each metric to count as passed. Default: 1.0.

source
NimbleAgents.print_eval Function
julia
print_eval(report; io=stdout)

Print a human-readable summary of an EvalReport.

source
NimbleAgents.save_eval Function
julia
save_eval(report, path)

Serialise an EvalReport to a JSON file.

source
NimbleAgents.load_eval Function
julia
load_eval(path) -> Dict{String, Any}

Load a previously saved eval report from a JSON file.

source

MCP and Server

NimbleAgents.MCPServer Type
julia
MCPServer(; command, args, env, timeout, cache_tools)
MCPServer(; url, headers, timeout, cache_tools)

Describes an MCP server that NimbleAgents can connect to.

Two transports are supported:

  • stdio — spawns the server as a local subprocess. Provide command (and optionally args / env).

  • HTTP — connects to a remote MCP endpoint via HTTP POST. Provide url (and optionally headers for authentication).

Fields (stdio)

  • command::String: Executable to run (e.g. "uvx", "npx", "python").

  • args::Vector{String}: Arguments passed to the command.

  • env::Dict{String,String}: Extra environment variables for the subprocess.

Fields (HTTP)

  • url::String: HTTP endpoint URL (e.g. "https://docs.langchain.com/mcp").

  • headers::Dict{String,String}: Request headers, e.g. for auth tokens.

Fields (shared)

  • timeout::Float64: Seconds to wait for each JSON-RPC response (default: 60.0).

  • cache_tools::Bool: Cache tool list after first discovery (default: true).

Examples

julia
# stdio
server = MCPServer(
    command = "uvx",
    args    = ["--from", "mcpdoc", "mcpdoc",
               "--urls", "LangGraph:https://langchain-ai.github.io/langgraph/llms.txt",
               "--transport", "stdio"],
)

# HTTP — no auth
server = MCPServer(url="https://docs.langchain.com/mcp")

# HTTP — with Bearer token
server = MCPServer(
    url     = "https://huggingface.co/mcp",
    headers = Dict("Authorization" => "Bearer hf_xxx"),
)
source
NimbleAgents.MCPClient Type
julia
MCPClient(server)

A live connection to one MCP server via stdio. Created via connect!(MCPClient(server)). Not constructed directly by users — attach MCPServer objects to an Agent and the framework manages client lifetime.

source
NimbleAgents.MCPHTTPClient Type
julia
MCPHTTPClient(server)

A live connection to one MCP server via HTTP POST. Created automatically when an MCPServer is constructed with a url field.

source
NimbleAgents.connect! Function
julia
connect!(client::MCPHTTPClient) -> MCPHTTPClient

Perform the JSON-RPC initialize handshake with the remote HTTP MCP server. Returns the client for chaining.

source
julia
connect!(client::MCPClient) -> MCPClient

Spawn the MCP server subprocess and perform the JSON-RPC initialize handshake. Returns the client (mutated in place) for chaining.

source
NimbleAgents.list_tools Function
julia
list_tools(client::MCPClient) -> Vector{NimbleTool}

Fetch the tool list from the MCP server and return them as NimbleTool objects ready to be passed to an Agent. Results are cached if server.cache_tools.

source
NimbleAgents.close! Function
julia
close!(client::MCPHTTPClient)

No-op for HTTP clients — there is no persistent connection to close. Clears the tool cache.

source
julia
close!(client::MCPClient)

Terminate the MCP server subprocess and clean up I/O handles.

source
NimbleAgents.serve Function
julia
serve(agents; port=8080, host="127.0.0.1", store=InMemorySessionStore())

Start the NimbleAgents web UI server.

Registers agents by name and serves a local browser UI at http://$host:$port. Press Ctrl+C to stop.

Arguments

  • agents::Vector{<:Agent}: Agents to register in the web UI, keyed by agent.name.

  • port::Int: TCP port to listen on (default: 8080).

  • host::String: Host/interface to bind (default: "127.0.0.1").

  • store::AbstractSessionStore: Session persistence backend used by the server.

Returns

  • Nothing: Runs the HTTP server loop until interrupted (Ctrl+C).

Pass a store to persist sessions across server restarts:

julia
serve([agent]; store=JSONSessionStore(".nimble/sessions"))

Multiple threads required

The server spawns agent runs in background threads. Start Julia with at least 2 threads:

julia --project=. -t 4 examples/web/web_ui.jl

Example

julia
using NimbleAgents

agent = Agent(
    name         = "MyBot",
    instructions = "You are a helpful assistant.",
    model        = "gpt-5.4-mini",
)

serve([agent]; port=8080)
# With persistence:
serve([agent]; port=8080, store=JSONSessionStore(".nimble/sessions"))
source
NimbleAgents.chat! Function
julia
chat!(agent::Agent; session, model, verbose)

Start an interactive multi-turn conversation with agent in the Julia REPL.

Tokens stream to stdout as they arrive. Conversation history accumulates in the session across turns, so the agent maintains context throughout the conversation.

Slash Commands

  • /exit or /quit — end the conversation

  • /reset — clear conversation history and start fresh

  • /trace — show token usage, cost, and tool call summary

  • /help — show available commands

Arguments

  • agent::Agent: The agent to chat with.

  • session::Session: Session for conversation history (default: auto-created).

  • verbose::Bool: Show tool call details (default: false).

Example

julia
agent = Agent(name="Bot", instructions="You are helpful.", tools=[search_tool])
chat!(agent)
source
julia
chat!(; model)

Start a conversation with the built-in NimbleAgents documentation bot. It has access to the framework's source code, docs, and examples via filesystem tools.

Requires an LLM API key — set OPENAI_API_KEY, ANTHROPIC_API_KEY, or GOOGLE_API_KEY in your environment or .env file.

Arguments

  • model::String: LLM model to use (default: "gpt-4.1-nano").

Example

julia
using NimbleAgents
chat!()
# You> How do I add guardrails to an agent?
# NimbleAgents> ...
source

External and CLI Tools

NimbleAgents.CLITool Type
julia
CLITool(; name, description, command, args, timeout, working_dir, return_direct)

An AbstractTool that runs a shell command as a subprocess.

Arguments from the LLM are substituted into {arg_name} placeholders in the command vector. Each argument is passed as a separate process argument — no shell string is constructed, making injection structurally impossible.

Fields

  • name::String: Tool name shown to the LLM.

  • description::String: What the tool does and when to use it.

  • command::Vector{String}: Command and arguments with {placeholder} slots.

  • args::Vector{Pair{String,CLIArg}}: Ordered argument definitions.

  • timeout::Float64: Seconds before the subprocess is killed (default: 30.0).

  • working_dir::Union{String,Nothing}: Working directory for the subprocess.

  • return_direct::Bool: Short-circuit the agent loop after this tool (default: false).

Example

julia
git_log = CLITool(
    name        = "git_log",
    description = "Show recent git commits.",
    command     = ["git", "log", "--oneline", "-{n}"],
    args        = ["n" => CLIArg(Int, "Number of commits to show.")],
)

agent = Agent(
    name  = "DevBot",
    tools = [git_log],
)

Example — multiple args

julia
grep_tool = CLITool(
    name        = "grep",
    description = "Search for a pattern in files. Returns matching lines.",
    command     = ["grep", "-rn", "{pattern}", "{path}"],
    args        = [
        "pattern" => CLIArg(String, "Regex pattern to search for."),
        "path"    => CLIArg(String, "File or directory to search in."),
    ],
)
source
NimbleAgents.CLIArg Type
julia
CLIArg(type, description; required=true)

Describes one argument of a CLITool.

Fields

  • type::Type: Julia type for the argument (String, Int, Float64, Bool).

  • description::String: Shown to the LLM in the tool schema.

  • required::Bool: Whether the argument must be provided (default: true).

source
NimbleAgents.ExternalAgentTool Type
julia
ExternalAgentTool(; name, description, command, args, timeout, working_dir,
                    on_output, parse_result)

An AbstractTool for running external CLI agents (Claude Code, Codex, etc.) as subprocesses with real-time progress streaming.

Unlike CLITool (which buffers all output), ExternalAgentTool reads stdout line-by-line and fires on_output(line) for each line as it arrives. This enables progress visibility for long-running agents.

Fields

  • name::String: Tool name shown to the LLM.

  • description::String: What the tool does and when to use it.

  • command::Vector{String}: Command with {placeholder} slots (same as CLITool).

  • args::Vector{Pair{String,CLIArg}}: Ordered argument definitions.

  • timeout::Float64: Seconds before the subprocess is killed (default: 300.0).

  • working_dir::Union{String,Nothing}: Working directory for the subprocess.

  • on_output::Union{Function,Nothing}: Called with each stdout line as it arrives. Use for progress logging (default: nothing — silent).

  • parse_result::Union{Function,Nothing}: Post-process the collected output lines into a final result string. Receives Vector{String} of all stdout lines. Default: nothing (returns raw output joined by newlines).

  • return_direct::Bool: Short-circuit the agent loop after this tool (default: false).

Example

julia
coder = ExternalAgentTool(
    name        = "coder",
    description = "Run Claude Code to implement a task.",
    command     = ["claude", "-p", "{task}", "--output-format", "stream-json",
                   "--verbose", "--model", "sonnet"],
    args        = ["task" => CLIArg(String, "The coding task.")],
    timeout     = 300.0,
    on_output   = line -> println("[coder] ", line),
)
source
NimbleAgents.claude_code_tool Function
julia
claude_code_tool(; name, description, model, working_dir, timeout,
                   max_budget, permission_mode, allowed_tools, on_output,
                   system_prompt, session_id, resume, extra_flags)

Create an ExternalAgentTool that delegates tasks to Claude Code via claude -p.

Uses --output-format stream-json --verbose for structured streaming output. The on_output callback receives each line of stream-json as it arrives, enabling real-time progress monitoring.

Authentication

Claude Code must be authenticated before use. Either:

  1. Run claude login once in your terminal (stores credentials in ~/.claude/), or

  2. Set ANTHROPIC_API_KEY in your .env or environment (inherited by subprocess).

Login cannot be done programmatically — it requires an interactive browser OAuth flow.

Arguments

  • name::String: Tool name (default: "claude_code").

  • description::String: Description shown to the LLM.

  • model::String: Claude model to use (default: "sonnet").

  • working_dir::Union{String,Nothing}: Working directory for Claude Code.

  • timeout::Float64: Seconds before kill (default: 300.0).

  • max_budget::Union{Float64,Nothing}: Max USD budget per invocation.

  • permission_mode::String: Permission mode (default: "bypassPermissions").

  • allowed_tools::Union{Vector{String},Nothing}: Restrict available tools.

  • on_output::Union{Function,Nothing}: Called per stream-json line.

  • system_prompt::Union{String,Nothing}: Custom system prompt for Claude Code.

  • session_id::Union{String,Nothing}: Session ID to continue a previous conversation. When set, Claude Code resumes the specified session, retaining full conversation context.

  • resume::Bool: If true, resume the most recent session (default: false). Mutually exclusive with session_id — if both are set, session_id takes precedence.

  • extra_flags::Vector{String}: Additional CLI flags.

Example

julia
coder = claude_code_tool(
    working_dir = "/path/to/repo",
    model       = "sonnet",
    max_budget  = 2.00,
    on_output   = line -> begin
        try
            event = JSON3.read(line)
            if event.type == "assistant"
                println("[claude] ", get(event.message.content[1], :text, ""))
            end
        catch; end
    end,
)

agent = Agent(
    name = "PM",
    instructions = "Delegate implementation tasks to claude_code.",
    tools = [coder],
)

# Resume a previous Claude Code session by ID
coder_resume = claude_code_tool(session_id = "abc-123", working_dir = "/path/to/repo")

# Resume the most recent session
coder_latest = claude_code_tool(resume = true, working_dir = "/path/to/repo")
source
NimbleAgents.codex_tool Function
julia
codex_tool(; name, description, model, working_dir, timeout, on_output, extra_flags)

Create an ExternalAgentTool that delegates tasks to OpenAI Codex CLI via codex -q.

Example

julia
coder = codex_tool(working_dir="/path/to/repo")
source

Built-In Tools

NimbleAgents.read_file_tool Constant
julia
read_file_tool

Built-in NimbleTool that reads file contents and returns them as a string.

source
NimbleAgents.write_file_tool Constant
julia
write_file_tool

Built-in NimbleTool that writes text to a file, creating parent directories when needed.

source
NimbleAgents.edit_file_tool Constant
julia
edit_file_tool

Built-in NimbleTool that replaces one exact string match in a file.

source
NimbleAgents.list_dir_tool Constant
julia
list_dir_tool

Built-in NimbleTool that lists directory entries with basic type/size hints.

source
NimbleAgents.glob_tool Constant
julia
glob_tool

Built-in NimbleTool that finds files matching glob patterns, including **.

source
NimbleAgents.delete_file_tool Constant
julia
delete_file_tool

Built-in NimbleTool that permanently deletes a file path.

source
NimbleAgents.grep_tool Constant
julia
grep_tool

Built-in NimbleTool that searches files/directories with a regular expression.

source
NimbleAgents.find_files_tool Constant
julia
find_files_tool

Built-in NimbleTool that recursively finds file names matching a pattern.

source
NimbleAgents.bash_tool Constant
julia
bash_tool

Built-in NimbleTool that executes shell commands and returns combined output.

source
NimbleAgents.http_get_tool Constant
julia
http_get_tool

Built-in NimbleTool that performs HTTP GET requests and returns response text.

source
NimbleAgents.http_post_tool Constant
julia
http_post_tool

Built-in NimbleTool that sends JSON POST requests and returns response text.

source
NimbleAgents.fetch_webpage_tool Constant
julia
fetch_webpage_tool

Built-in NimbleTool that fetches a webpage and returns cleaned readable text.

source
NimbleAgents.github_trending_tool Constant
julia
github_trending_tool

Built-in NimbleTool that queries GitHub Search API for trending repositories.

source
NimbleAgents.eval_julia_tool Constant
julia
eval_julia_tool

Built-in tool that evaluates Julia code in a persistent sandbox.

The sandbox persists for the lifetime of the session — variables, imports, and function definitions all carry over between calls. The project environment is active, so any package in Project.toml can be loaded with using.

Pair with a Session to get persistent state across turns:

julia
session = Session(app_name="DataSession", user_id="alice")
agent   = Agent(
    name  = "DataBot",
    tools = [eval_julia_tool],
)
run!(agent, "Load DataFrames and create a DataFrame with columns a and b"; session=session)
run!(agent, "Now compute the mean of column a"; session=session)
source
NimbleAgents.save_artifact_tool Constant
julia
save_artifact_tool

Built-in NimbleTool that registers an existing file as a session artifact.

source
NimbleAgents.save_memory_tool Constant
julia
save_memory_tool

Built-in NimbleTool that saves user facts/preferences into long-term memory.

source
NimbleAgents.recall_memory_tool Constant
julia
recall_memory_tool

Built-in NimbleTool that retrieves relevant entries from long-term memory.

source

Provider Schema Marker

NimbleAgents.GeminiOpenAISchema Type
julia
GeminiOpenAISchema <: AbstractOpenAISchema

Marker schema used for Gemini models routed through Google's OpenAI-compatible chat completions endpoint.

source