JaatoClient
The main entry point for the jaato framework. JaatoClient is a facade
that wraps JaatoRuntime (shared resources) and JaatoSession
(per-agent state), providing a unified interface for connecting to AI providers,
configuring tools, and managing multi-turn conversations.
from shared import JaatoRuntime, JaatoSession
from jaato import JaatoClient, PluginRegistry
client = JaatoClient()
client.connect() # Reads JAATO_PROVIDER and MODEL_NAME from env
registry = PluginRegistry(model_name=client.model_name)
registry.discover()
registry.expose_tool("cli")
client.configure_tools(registry)
response = client.send_message(
"List files in current directory",
on_output=lambda s, t, m: print(t, end="")
)
Constructor
__init__
Creates a new JaatoClient instance with the specified provider.
-
provider_name
str
optional
The model provider to use. If not specified, reads from
JAATO_PROVIDERenv var, falling back to"google_genai".
Supported Providers
| Provider | Name | Models |
|---|---|---|
| Google GenAI | google_genai |
Gemini 1.5, 2.0, 2.5 |
| Anthropic | anthropic |
Claude 3, 3.5, Sonnet 4, Opus 4 |
| GitHub Models | github_models |
GPT-4o, Claude, Gemini, Llama |
| Claude CLI | claude_cli |
Uses Claude Pro/Max subscription |
| Antigravity | antigravity |
Gemini 3, Claude (via Google) |
| Ollama | ollama |
Local models (Qwen, Llama, etc.) |
| Zhipu AI | zhipuai |
GLM-5, GLM-4.7, GLM-4.6, GLM-4.5 |
# Default: uses JAATO_PROVIDER env var or google_genai
client = JaatoClient()
# Google GenAI (Vertex AI / AI Studio)
client = JaatoClient(provider_name="google_genai")
# Anthropic Claude (API key or OAuth)
client = JaatoClient(provider_name="anthropic")
# GitHub Models (multi-model access)
client = JaatoClient(provider_name="github_models")
# Claude CLI (uses subscription, not API credits)
client = JaatoClient(provider_name="claude_cli")
# Antigravity (Google IDE backend)
client = JaatoClient(provider_name="antigravity")
# Ollama (local models)
client = JaatoClient(provider_name="ollama")
# .env file
JAATO_PROVIDER=anthropic
MODEL_NAME=claude-sonnet-4-20250514
# Or for Ollama
JAATO_PROVIDER=ollama
OLLAMA_MODEL=qwen3:32b
Connection
connect
Establishes a connection to the AI provider. When called without arguments,
reads configuration from environment variables (JAATO_PROVIDER,
MODEL_NAME, and provider-specific variables).
-
project
str
optional
Cloud project ID (required for Vertex AI, read from
JAATO_GOOGLE_PROJECTif not set) -
location
str
optional
Provider region (required for Vertex AI, read from
JAATO_GOOGLE_LOCATIONif not set) -
model
str
optional
Model name. If not provided, reads from
MODEL_NAMEenv var (or provider-specific vars likeOLLAMA_MODEL)
is_connected property
Returns True if the client is connected to a provider.
model_name property
Returns the current model name, or None if not connected.
list_available_models
Lists available models from the provider, optionally filtered by prefix.
get_runtime
Returns the underlying JaatoRuntime for advanced use cases like
creating subagent sessions. The runtime holds shared resources (provider config,
registry, permissions, ledger).
get_session
Returns the underlying JaatoSession for direct session manipulation.
verify_auth
allow_interactive: bool = False,
on_message: Optional[Callable[[str], None]] = None
) -> bool
Verify authentication before loading tools. Call this after
connect() but before configure_tools()
for providers that support interactive login (like Anthropic OAuth).
-
allow_interactive
bool
optional
If True and auth is not configured, attempt interactive login (e.g., browser-based OAuth). Default: False.
-
on_message
Callable[[str], None]
optional
Callback for status messages during login (e.g., "Opening browser...").
# Reads JAATO_PROVIDER and MODEL_NAME from env
client = JaatoClient()
client.connect()
# Check connection
if client.is_connected:
print(f"Connected to {client.model_name}")
# Override provider and model in code
client = JaatoClient(provider_name="anthropic")
client.connect(model="claude-sonnet-4-20250514")
# Google GenAI (Vertex AI) with project/location
client = JaatoClient(provider_name="google_genai")
client.connect(
project="my-gcp-project",
location="us-central1",
model="gemini-2.5-flash"
)
# Access runtime for subagent creation
runtime = client.get_runtime()
sub_session = runtime.create_session(
model="claude-sonnet-4-20250514",
tools=["cli", "web_search"],
system_instructions="You are a researcher."
)
sub_response = sub_session.send_message("Research...")
# Access main session directly
main_session = client.get_session()
history = main_session.get_history()
# For providers with OAuth (Anthropic, Antigravity)
client = JaatoClient(provider_name="anthropic")
client.connect() # Reads MODEL_NAME from env
# Verify auth - triggers browser OAuth if needed
if not client.verify_auth(
allow_interactive=True,
on_message=lambda msg: print(f"Auth: {msg}")
):
print("Authentication failed")
return
# Now safe to configure tools
client.configure_tools(registry, permission_plugin, ledger)
Tool Configuration
configure_tools
registry: PluginRegistry,
permission_plugin: Optional[PermissionPlugin] = None,
ledger: Optional[TokenLedger] = None
) -> None
Configures the client with tools from a plugin registry. This is the recommended way to set up tools.
-
registry
PluginRegistry
required
Registry containing exposed tool plugins
-
permission_plugin
PermissionPlugin
optional
Plugin for permission checking before tool execution
-
ledger
TokenLedger
optional
Token accounting ledger
configure_plugins_only
registry: PluginRegistry,
permission_plugin: Optional[PermissionPlugin] = None,
ledger: Optional[TokenLedger] = None
) -> None
Configure plugins without creating a provider session. Use this when authentication is pending and you need user commands available but can't connect to the model yet.
configure_custom_tools
tools: List[ToolSchema],
executors: Dict[str, Callable],
ledger: Optional[TokenLedger] = None,
system_instruction: Optional[str] = None
) -> None
Configures tools directly without using a registry. Useful for custom tool implementations.
from jaato import JaatoClient, PluginRegistry
from shared import PermissionPlugin, TokenLedger
client = JaatoClient()
client.connect() # Reads JAATO_PROVIDER and MODEL_NAME from env
# Setup registry
registry = PluginRegistry(model_name=client.model_name)
registry.discover()
registry.expose_tool("cli")
registry.expose_tool("file_edit")
# Optional: permission control
perm = PermissionPlugin()
perm.initialize({"config_path": "perms.json"})
# Optional: token accounting
ledger = TokenLedger()
# Configure
client.configure_tools(
registry,
permission_plugin=perm,
ledger=ledger
)
from jaato import ToolSchema
# Define custom tool
def get_weather(city: str) -> str:
return f"Weather in {city}: Sunny, 72F"
tools = [
ToolSchema(
name="get_weather",
description="Get current weather",
parameters={
"type": "object",
"properties": {
"city": {"type": "string"}
},
"required": ["city"]
}
)
]
executors = {"get_weather": get_weather}
client.configure_custom_tools(
tools=tools,
executors=executors
)
Messaging
send_message
message: str,
on_output: Optional[OutputCallback] = None,
on_usage_update: Optional[UsageUpdateCallback] = None,
on_gc_threshold: Optional[GCThresholdCallback] = None
) -> str
Sends a message and returns the final response. Handles the full tool execution loop internally, calling callbacks for real-time streaming and usage updates.
-
message
str
required
The user message to send
-
on_output
OutputCallback
optional
Callback for real-time output:
(source: str, text: str, mode: str) -> None -
on_usage_update
UsageUpdateCallback
optional
Callback for real-time token usage:
(usage: TokenUsage) -> None -
on_gc_threshold
GCThresholdCallback
optional
Callback when GC threshold is crossed during streaming:
(percent_used: float, threshold: float) -> None
send_message_with_parts
parts: List[Part],
on_output: OutputCallback
) -> str
Sends a multimodal message with multiple parts (text, images, etc.).
generate
prompt: str,
ledger: Optional[TokenLedger] = None
) -> str
Simple one-shot generation without tools or conversation history. Useful for quick completions.
# Output callback
def on_output(source, text, mode):
"""
source: "model", plugin name, or "system"
text: output text
mode: "write" (new) or "append" (continue)
"""
if mode == "write":
print(f"\n[{source}]", end=" ")
print(text, end="")
# Send message
response = client.send_message(
"What files are in this directory?",
on_output=on_output
)
print(f"\n\nFinal: {response}")
from jaato import Part, Attachment
# Create parts with image
parts = [
Part.from_text("What's in this image?"),
Part(inline_data=Attachment(
mime_type="image/png",
data=open("image.png", "rb").read()
))
]
response = client.send_message_with_parts(
parts,
on_output=on_output
)
# One-shot, no tools, no history
result = client.generate("What is 2 + 2?")
print(result) # "4"
History Management
get_history
Returns the full conversation history as a list of Message objects.
reset_session
Clears the current session. Optionally initializes with a new history.
get_turn_accounting
Returns per-turn statistics including token counts and timing.
get_turn_boundaries
Returns indices marking the start of each turn in the history.
revert_to_turn
Reverts the conversation to a specific turn, removing subsequent messages.
get_context_limit
Returns the model's context window size in tokens.
get_context_usage
Returns current context usage statistics.
# Get conversation history
history = client.get_history()
for msg in history:
print(f"{msg.role}: {msg.text[:50]}...")
# Check turn info
turns = client.get_turn_boundaries()
print(f"Conversation has {len(turns)} turns")
# Get per-turn stats
accounting = client.get_turn_accounting()
for turn in accounting:
print(f"Turn {turn['turn_id']}: "
f"{turn['tokens']} tokens")
# Revert to earlier turn
client.revert_to_turn(2)
# Clear and start fresh
client.reset_session()
# Reset with custom history
client.reset_session(history=custom_history)
# Check context limits
limit = client.get_context_limit()
usage = client.get_context_usage()
print(f"Limit: {limit} tokens")
print(f"Used: {usage['total_tokens']} tokens")
print(f"Available: {limit - usage['total_tokens']}")
Garbage Collection
For long conversations, GC plugins help manage context window limits by automatically removing or summarizing older messages.
set_gc_plugin
plugin: GCPlugin,
config: Optional[GCConfig] = None
) -> None
Enables automatic garbage collection with the specified plugin.
remove_gc_plugin
Disables garbage collection.
manual_gc
Manually triggers garbage collection and returns the result.
get_gc_history
Returns the history of GC operations performed.
from shared.plugins.gc_truncate import (
create_plugin as create_gc
)
from shared.plugins.gc import GCConfig
# Create GC plugin
gc_plugin = create_gc()
gc_plugin.initialize({
"preserve_recent_turns": 10
})
# Configure with threshold (or use JAATO_GC_THRESHOLD env var)
config = GCConfig(
threshold_percent=75.0, # overrides env var default
check_before_send=True
)
client.set_gc_plugin(gc_plugin, config)
# Manual GC
result = client.manual_gc()
print(f"Freed {result.tokens_freed} tokens")
# Check GC history
for gc in client.get_gc_history():
print(f"GC at turn {gc.turn}: "
f"{gc.tokens_freed} freed")
# Disable GC
client.remove_gc_plugin()
Session Persistence
Session plugins enable saving and resuming conversations across application restarts.
set_session_plugin
plugin: SessionPlugin,
config: Optional[SessionConfig] = None
) -> None
save_session
session_id: Optional[str] = None,
user_inputs: Optional[List[str]] = None
) -> str
Saves the current session and returns the session ID.
resume_session
Loads a previously saved session.
list_sessions
Returns a list of all saved sessions.
delete_session
Deletes a saved session.
from shared.plugins.session import (
create_plugin as create_session
)
from shared.plugins.session import SessionConfig
# Setup session plugin
session = create_session()
session.initialize({
"storage_path": ".jaato/sessions"
})
config = SessionConfig(
auto_resume_last=True
)
client.set_session_plugin(session, config)
# ... have conversation ...
# Save session
session_id = client.save_session()
print(f"Saved: {session_id}")
# Later: resume
client.resume_session(session_id)
# List all sessions
for info in client.list_sessions():
print(f"{info.id}: {info.created_at}")
# Delete old session
client.delete_session(old_session_id)
Thinking Mode
For providers that support extended thinking (Anthropic Claude, Gemini), you can enable and configure thinking mode to get more thorough reasoning.
set_thinking_plugin
Set the thinking plugin for controlling reasoning modes.
remove_thinking_plugin
Remove the thinking plugin.
set_thinking_config
Set thinking mode configuration directly (bypasses plugin).
get_thinking_config
Get current thinking configuration.
supports_thinking
Check if the current provider supports thinking mode.
from jaato_sdk.plugins.model_provider.types import ThinkingConfig
# Check support
if client.supports_thinking():
# Enable thinking with budget
client.set_thinking_config(ThinkingConfig(
enabled=True,
budget=10000 # Max thinking tokens
))
response = client.send_message(
"Analyze this complex problem...",
on_output=on_output
)
# Disable thinking
client.set_thinking_config(ThinkingConfig(enabled=False))
from shared.plugins.thinking import create_plugin as create_thinking
# Create and set thinking plugin
thinking = create_thinking()
thinking.initialize({"default_budget": 8192})
client.set_thinking_plugin(thinking)
# User commands now available:
# /thinking on - enable thinking
# /thinking off - disable thinking
# /thinking budget 16000 - set budget
UI Hooks
For rich terminal UIs, you can set hooks to receive agent lifecycle events, enabling progress tracking and accounting displays.
set_ui_hooks
Set UI hooks for agent lifecycle events. The hooks receive callbacks for agent creation, output, turn completion, context updates, and more.
set_terminal_width
Set the terminal width for formatting. Affects enrichment notification formatting to properly wrap and align text.
set_presentation_context
Set display capabilities for the connected client. The context is
injected into the model's system instructions so it adapts output
format (e.g. vertical lists on narrow displays, full tables on wide
ones). Also updates terminal_width for backwards
compatibility. See PresentationContext.
get_model_completions
Get completions for the model command (for shell autocomplete).
from shared.plugins.subagent.ui_hooks import AgentUIHooks
class MyUIHooks(AgentUIHooks):
def on_agent_created(self, agent_id, agent_name, ...):
print(f"Agent {agent_name} created")
def on_agent_output(self, agent_id, source, text, mode):
# Display streaming output
print(text, end="")
def on_agent_turn_completed(self, agent_id, turn_number,
prompt_tokens, output_tokens, ...):
print(f"Turn {turn_number}: {output_tokens} tokens")
def on_agent_context_updated(self, agent_id, total_tokens,
percent_used, ...):
print(f"Context: {percent_used:.1f}% used")
# Set hooks on client
client.set_ui_hooks(MyUIHooks())
client.set_terminal_width(120)
# Or set full presentation context
from jaato import PresentationContext, ClientType
client.set_presentation_context(PresentationContext(
content_width=120,
client_type=ClientType.TERMINAL,
))
User Commands
Plugins can expose user-facing commands that can be executed during a conversation.
get_user_commands
Returns all available user commands from exposed plugins.
execute_user_command
command_name: str,
args: Optional[Dict] = None
) -> tuple[Any, bool]
Executes a user command and returns (result, share_with_model).
# List available commands
commands = client.get_user_commands()
for name, cmd in commands.items():
print(f"/{name}: {cmd.description}")
# Execute a command
result, share = client.execute_user_command(
"todo_list",
args={"filter": "pending"}
)
print(result)
# If share is True, add result to conversation
if share:
client.send_message(
f"Command result: {result}",
on_output=on_output
)
Streaming & Cancellation
jaato supports interruptible requests with streaming output and graceful cancellation. This allows stopping long-running operations mid-turn.
stop
Request cancellation of the current operation. Thread-safe.
Returns True if a cancellation was requested, False
if no operation was in progress.
is_processing property
Returns True if a message is currently being processed.
supports_stop property
Returns True if the current provider supports mid-turn cancellation.
Check this before calling stop() to know if it will be effective.
set_streaming_enabled
Enable or disable streaming mode. When enabled, uses provider's streaming API for real-time token output and cancellation support.
-
enabled
bool
required
Whether to enable streaming mode.
import threading
from jaato import JaatoClient, CancelledException
client = JaatoClient()
client.connect() # Reads JAATO_PROVIDER and MODEL_NAME from env
# ... configure tools ...
# Enable streaming for mid-generation cancellation
client.set_streaming_enabled(True)
# Cancel after 10 seconds
def cancel_after_timeout():
time.sleep(10)
if client.is_processing:
print("Timeout - stopping...")
client.stop()
timer = threading.Thread(target=cancel_after_timeout)
timer.start()
try:
response = client.send_message(
"Write a very long essay...",
on_output=lambda s, t, m: print(t, end="")
)
print(f"\n\nFinished normally")
except CancelledException:
print(f"\n\nRequest was cancelled")
finally:
timer.join()
# With streaming enabled, tokens arrive
# incrementally via the on_output callback
client.set_streaming_enabled(True)
def on_output(source, text, mode):
# mode="write" for new block
# mode="append" for continuation
print(text, end="", flush=True)
response = client.send_message(
"Count from 1 to 100",
on_output=on_output
)
# Output appears token-by-token!
Rate Limiting
jaato provides both proactive (request pacing) and reactive (retry with backoff) rate limiting to handle provider API limits gracefully.
Environment Variables
Configure rate limiting via environment variables:
| Variable | Default | Description |
|---|---|---|
AI_REQUEST_INTERVAL |
0 |
Minimum seconds between API requests (0 = disabled) |
AI_RETRY_ATTEMPTS |
5 |
Max retry attempts on 429 errors |
AI_RETRY_BASE_DELAY |
1.0 |
Initial retry delay in seconds |
AI_RETRY_MAX_DELAY |
30.0 |
Maximum retry delay in seconds |
AI_RETRY_LOG_SILENT |
false |
Suppress retry log messages |
set_retry_callback
callback: Optional[RetryCallback]
) -> None
Set a callback to receive retry notifications. Use this to route retry messages to a status bar or queue instead of console output.
-
callback
RetryCallback
optional
Function signature:
(message: str, attempt: int, max_attempts: int, delay: float) -> None. Set toNoneto revert to console output.
# Prevent 429 errors by limiting request rate
# Example: max 2 requests per second
AI_REQUEST_INTERVAL=0.5
# Retry configuration (reactive)
AI_RETRY_ATTEMPTS=5
AI_RETRY_BASE_DELAY=1.0
AI_RETRY_MAX_DELAY=30.0
# Route retry messages to a queue
# (for rich UIs that don't want console spam)
session = client.get_session()
session.set_retry_callback(
lambda msg, attempt, max_att, delay:
status_queue.put({
"type": "retry",
"message": msg,
"attempt": attempt,
"max_attempts": max_att,
"delay": delay
})
)
# Revert to console output
session.set_retry_callback(None)
Request Flow:
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Request │───>│ RequestPacer │───>│ API Call │
│ │ │ (proactive) │ │ │
└──────────────┘ │ waits if │ └──────┬───────┘
│ interval not │ │
│ elapsed │ v
└──────────────┘ ┌──────────────┐
│ 429 Error? │
│ with_retry │
│ (reactive) │
└──────────────┘
Environment Variables
Configure jaato behavior via environment variables. Set these in
your .env file or shell environment.
Provider Selection
| Variable | Description |
|---|---|
JAATO_PROVIDER |
Default provider: google_genai, anthropic, github_models, claude_cli, antigravity, ollama |
General Settings
| Variable | Default | Description |
|---|---|---|
AI_USE_CHAT_FUNCTIONS |
1 |
Enable function calling mode |
JAATO_GC_THRESHOLD |
80.0 |
GC trigger threshold % |
JAATO_PARALLEL_TOOLS |
true |
Enable parallel tool execution |
JAATO_DEFERRED_TOOLS |
true |
Enable deferred tool loading |
LEDGER_PATH |
- | Output path for token accounting JSONL |
Provider-Specific
See individual provider docs for their environment variables.
# Provider selection (choose one)
JAATO_PROVIDER=anthropic
MODEL_NAME=claude-sonnet-4-20250514
# Anthropic credentials
ANTHROPIC_API_KEY=sk-ant-api03-...
# Google GenAI credentials (if using google_genai)
# GOOGLE_GENAI_API_KEY=AIza...
# Or for Vertex AI:
# JAATO_GOOGLE_PROJECT=my-gcp-project
# JAATO_GOOGLE_LOCATION=us-central1
# GitHub Models credentials (if using github_models)
# GITHUB_TOKEN=ghp_...
# Ollama (if using ollama)
# OLLAMA_HOST=http://localhost:11434
# General settings
AI_USE_CHAT_FUNCTIONS=1
JAATO_PARALLEL_TOOLS=true
JAATO_DEFERRED_TOOLS=true
JAATO_GC_THRESHOLD=80.0
# Rate limiting
AI_REQUEST_INTERVAL=0.5
AI_RETRY_ATTEMPTS=5