Observed since 20 Oct, 22:31 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
Production-ready framework for building AI agents — type-safe, observable, circuit-broken.
Production AI agents you can govern, afford, and prove. Policy, cost control, security, and tamper-evident audit that hold across every tool, MCP server, and sub-agent — with a self-hostable ops backend and compliance-grade evidence.
pip install agent-kit-ai
First-party SDKs and agent frameworks give you a capable loop. agent-kit is for what comes after the demo:
from agent_kit import SUSPEND, Agent, AgentConfig
from agent_kit.cloud import CloudReporter
from agent_kit.durable import SQLiteRunStore
from agent_kit.hooks import Hooks, require_approval
from agent_kit.providers import AnthropicProvider
from agent_kit.scanning import PatternScanner, scan_tool_output
agent = Agent(AnthropicProvider(), tools=[lookup_order, issue_refund, research_agent.as_tool("research", "...")],
config=AgentConfig(
hooks=Hooks(
before_tool=[require_approval("issue_refund")], # a human signs off on every refund
after_tool=[scan_tool_output(PatternScanner())], # poisoned tool output never reaches the model
),
approver=SUSPEND, # approvals can wait for days...
run_store=SQLiteRunStore("runs.db"), # ...and runs survive crashes and deploys
max_run_cost_usd=0.50, # hard cap, sub-agents included
cloud=CloudReporter(project="support", agent_name="refunds"), # fleet metrics, alerts, audit trail (AGENTKIT_BASE_URL)
),
)
Retry, per-provider circuit breaking, cost tracking, and a tamper-evident audit chain are on by default. Every
policy above also applies inside the research sub-agent.
agent-kit verify, no trust in the vendor required.Auditors increasingly ask what your AI agents were allowed to do, what they did, and whether the record can be trusted. agent-kit produces that evidence as a by-product of running the agent:
| What a reviewer asks | What agent-kit gives you |
|---|---|
| Who or what could act, and what was blocked? (SOC 2 CC6 — logical access) | Allowlists, policy hooks, and approval decisions, each recorded in the audit chain |
| How do you detect anomalies? (SOC 2 CC7.2 — monitoring) | Fleet metrics; circuit breaker, error-rate, cost, budget, and flagged-tool-output alerts |
| How do you evaluate and respond to incidents? (SOC 2 CC7.3–CC7.4) | Alert firings with acknowledgement history, and a per-run audit trail to investigate from |
| Can the logs be trusted and handed over? (EU AI Act record-keeping, SOC 2 evidence requests) | Hash-chained audit verified server-side; signed evidence bundles verifiable offline |
| How long is data kept, and how is it disposed of? (SOC 2 C1.2) | Retention policies, legal holds, and signed deletion receipts |
agent-kit doesn't make an organization SOC 2 or EU AI Act compliant, and agent-kit Cloud has no SOC 2 report of its own — it gives your compliance program trustworthy records about your AI agents. Confirm the mapping with your auditor.
Not yet: handoffs — tracked in specs/06-harness-roadmap.md.
import asyncio
from agent_kit import Agent
from agent_kit.providers import AnthropicProvider
async def main():
agent = Agent(AnthropicProvider())
result = await agent.run("Explain the Monty Hall problem in two sentences.")
print(result.output)
print(f"Cost: ${result.total_cost_usd:.4f}")
asyncio.run(main())
import httpx
from agent_kit import tool
@tool(description="Fetch the current price of a crypto asset", idempotent=True)
async def get_price(symbol: str) -> dict:
async with httpx.AsyncClient() as c:
return (await c.get(f"https://api.coingecko.com/api/v3/simple/price?ids={symbol}&vs_currencies=usd")).json()
agent = Agent(AnthropicProvider(), tools=[get_price])
result = await agent.run("What is the current Bitcoin price?")
from agent_kit import Agent, AgentConfig
from agent_kit.types import RetryPolicyConfig, CircuitBreakerConfig
agent = Agent(
provider=AnthropicProvider(),
config=AgentConfig(
system_prompt="You are a helpful assistant.",
retry_policy=RetryPolicyConfig(max_attempts=3),
circuit_breaker=CircuitBreakerConfig(failure_threshold=5),
audit_enabled=True, # tamper-evident Merkle audit chain
),
)
result = await agent.run("Summarize the latest AI research.")
print(f"Audit root hash: {result.audit_root_hash}") # verify integrity later
from agent_kit.orchestrator import LinearPipeline
pipeline = LinearPipeline([
(researcher, "Research this topic: {input}"),
(writer, "Write a blog post from these facts: {input}"),
(editor, "Polish and tighten this draft: {input}"),
])
result = await pipeline.run("The future of AI agent frameworks")
print(result.final_output)
print(f"Total cost: ${result.total_cost_usd:.4f} across {len(result.stage_results)} stages")
Three specialist agents research concurrently, then a fourth synthesizes. The DAG handles dependency resolution and runs independent nodes in parallel.
# examples/research_dag.py
dag = DAGOrchestrator([
TaskNode("market", market_agent, "Market analysis of: {input}"),
TaskNode("tech", tech_agent, "Technical landscape for: {input}"),
TaskNode("risk", risk_agent, "Risk assessment for: {input}"),
TaskNode("synthesis", synthesizer,
"Synthesize:\n{upstream:market}\n{upstream:tech}\n{upstream:risk}",
depends_on=["market", "tech", "risk"]),
])
result = await dag.execute("autonomous AI agents in enterprise production")
$ python examples/research_dag.py
Researching: autonomous AI agents in enterprise production systems
DAG: market + tech + risk (parallel) → synthesis
============================================================
EXECUTIVE BRIEFING
============================================================
Verdict: Enterprise AI agent adoption is accelerating but premature
for mission-critical workflows without proper guardrails.
The market is projected to reach $47B by 2028 (34% CAGR), with
75% of Fortune 500 companies running pilot programs. Technically,
the shift from chain-of-thought to tool-using agents with circuit
breakers and audit trails has made production deployment viable.
However, three risks dominate: regulatory uncertainty around
autonomous decision-making, cost blowouts from retry cascades,
and the "ghost agent" problem — orphaned processes consuming
resources with no human oversight.
Recommendation: Deploy with hard cost ceilings, tamper-evident
audit logging, and circuit breakers on all provider calls.
============================================================
Execution order: market → tech → risk → synthesis
Wall time: 4.2s
Total cost: $0.0183
Total tokens: 4,271
Per-node breakdown:
market $0.0041 982 tokens
tech $0.0044 1,053 tokens
risk $0.0038 891 tokens
synthesis $0.0060 1,345 tokens
Tool allowlisting, Merkle audit chain, and compliance export. The agent has delete_employee registered but it's blocked — only check_pto and submit_pto are allowed.
# examples/safe_agent.py
agent = Agent(
provider=AnthropicProvider(),
tools=[check_pto, submit_pto, delete_employee],
config=AgentConfig(
system_prompt="You are an HR assistant.",
allowed_tools=["check_pto", "submit_pto"], # delete_employee → blocked
),
)
result = await agent.run("I'm employee E003. Can I take 5 days off for vacation?")
$ python examples/safe_agent.py
Running agent with tool allowlist: [check_pto, submit_pto]
(delete_employee is registered but BLOCKED)
============================================================
RESPONSE
============================================================
I checked your PTO balance, Carol. You have 17 days remaining
(20 allocated, 3 used). I've submitted your request for 5 days
for your family vacation.
Your confirmation number is PTO-2026-0042.
============================================================
AUDIT TRAIL
============================================================
Events recorded: 6
Root hash: a1c9f3e7d204b85610ef38c7...
Chain integrity: VERIFIED
Cost: $0.0052 | Tokens: 1,203
Event log:
[0] agent_start actor=run-8f3a2c...
[1] llm_complete actor=anthropic
[2] tool_call actor=check_pto
[3] llm_complete actor=anthropic
[4] tool_call actor=submit_pto
[5] agent_complete actor=run-8f3a2c...
JSONL export: 6 records, 1,847 bytes
First record: agent_start
Last record: agent_complete
Real HTTP tools (no API keys needed), circuit breaker config, console tracing, and optional cloud reporting for fleet-wide visibility.
# examples/cloud_monitored.py
agent = Agent(
provider=AnthropicProvider(),
tools=[get_weather, top_hn_story, country_facts],
config=AgentConfig(
retry_policy=RetryPolicyConfig(max_attempts=3),
circuit_breaker=CircuitBreakerConfig(failure_threshold=5, recovery_timeout_s=30),
tracer=AgentTracer(backend="console"),
cloud=reporter, # optional — ships events to fleet dashboard
),
)
$ python examples/cloud_monitored.py
Cloud reporting: ENABLED (events → fleet dashboard)
Agent: Agent(provider='anthropic', tools=['get_weather', 'top_hn_story', 'country_facts'], model='claude-sonnet-4-20250514')
============================================================
{"span":"agent.run","kind":"agent","duration_ms":3841,"attributes":{"run_id":"e9f1..."}}
{"span":"llm.complete","kind":"llm","duration_ms":1203,"attributes":{"input_tokens":847,"output_tokens":52,"cost_usd":0.0031}}
{"tool_call":"get_weather","duration_ms":340,"success":true}
{"tool_call":"top_hn_story","duration_ms":289,"success":true}
{"tool_call":"country_facts","duration_ms":195,"success":true}
{"cost_event":true,"tokens":1847,"model":"claude-sonnet-4-20250514","usd":0.0058,"cumulative_usd":0.0089}
{"span":"llm.complete","kind":"llm","duration_ms":1814,"attributes":{"input_tokens":1203,"output_tokens":644,"cost_usd":0.0058}}
============================================================
RESPONSE
============================================================
Here's what I found:
🌤 Tokyo: 18°C (64°F), partly cloudy, 62% humidity, wind 8 mph
📰 Top HN story: "Show HN: I built a self-healing Kubernetes operator"
by tobiaswright — 847 points
🇯🇵 Japan: Capital Tokyo, population 125,681,593, East Asia
Languages: Japanese
============================================================
TELEMETRY
============================================================
Turns: 2
Cost: $0.0089
Tokens: 1,847
Audit hash: f7a2e19c830d...
Trace ID: 4c91b7a3-8e2f-4d1a-b3c9-7f2e1a3d5b8c
Events shipped to agent-kit Cloud. View at your fleet dashboard.
from agent_kit.providers import AnthropicProvider
# Anthropic (default — uses ANTHROPIC_API_KEY env var)
provider = AnthropicProvider()
provider = AnthropicProvider(api_key="sk-ant-...", default_model="claude-sonnet-5")
# OpenAI (requires pip install agent-kit-ai[openai])
from agent_kit.providers.openai import OpenAIProvider
provider = OpenAIProvider()
# Ollama (local models)
from agent_kit.providers.ollama import OllamaProvider
provider = OllamaProvider(default_model="llama3.2")
# Any OpenAI-compatible endpoint
from agent_kit.providers.openai import OpenAIProvider
provider = OpenAIProvider(base_url="http://localhost:11434/v1", api_key="none", default_model="mistral")
from agent_kit.observability import AgentTracer
# Zero dependencies — no-op (default)
tracer = AgentTracer()
# Structured JSON to stderr — no extra deps
tracer = AgentTracer(backend="console")
# Full OpenTelemetry (requires pip install agent-kit-ai[otel])
tracer = AgentTracer(backend="otlp", service_name="my-agent", endpoint="http://localhost:4317")
agent = Agent(provider, config=AgentConfig(tracer=tracer))
Every agent run produces a tamper-evident Merkle audit chain:
agent = Agent(provider, config=AgentConfig(audit_enabled=True))
result = await agent.run("Do something important.")
# Verify chain integrity
assert agent.audit.verify()
print(f"Root hash: {result.audit_root_hash}")
# Export to JSONL for compliance storage
with open("audit.jsonl", "w") as f:
f.write(agent.audit.export_jsonl())
The circuit breaker wraps every LLM provider call. It transitions:
CircuitOpenError)recovery_timeout_sfrom agent_kit.types import CircuitBreakerConfig
config = AgentConfig(
circuit_breaker=CircuitBreakerConfig(
failure_threshold=5, # open after 5 consecutive failures
recovery_timeout_s=60.0, # attempt recovery after 60s
success_threshold=2, # 2 successes in half-open → closed
)
)
from agent_kit.types import RetryPolicyConfig, BackoffConfig
policy = RetryPolicyConfig(
max_attempts=3,
backoff=BackoffConfig(
initial_delay_s=1.0,
multiplier=2.0,
max_delay_s=30.0,
jitter=True,
),
retryable_on=["ProviderError", "httpx.TimeoutException"],
)
Lock down which tools an agent can call — enforced at the registry level, not just advisory:
agent = Agent(
provider,
tools=[web_search, read_file, delete_file, send_email],
config=AgentConfig(allowed_tools=["web_search", "read_file"]),
# delete_file and send_email raise ToolNotAllowedError if the LLM tries to call them
)
Pass any type Pydantic can validate — a model, dataclass, TypedDict, list[...], enum — and get a
validated value back. Tools, hooks, budgets, and audit still apply on the way there.
class Quote(BaseModel):
customer: str
total_usd: float
result = await agent.run("Quote Acme for 10 widgets", output_type=Quote)
result.parsed.total_usd # validated Quote; result.output is the raw JSON
AgentConfig(output_retries=2)),
then OutputValidationError is raised. Each failure is audited as output_validation_failed.agent.stream(prompt, output_type=Quote) streams the raw JSON; agent.last_result.parsed holds the value.Full example: examples/typed_output.py.
Long tool-heavy runs stay correct, cached, and inside the context window with no configuration:
turn.cost.cache_read_tokens.
AgentConfig(prompt_caching=False) turns it off.SQLiteMemory.context_budget_tokens (default 150K), the oldest turns are cut once down to half the budget, never
separating a tool call from its result. Between cuts the history only grows at the end, so the prompt
prefix — and the cache — stays valid. Each cut is audited as context_trimmed.config = AgentConfig(
effort="high", # Anthropic output_config.effort / OpenAI reasoning_effort
thinking="adaptive", # Anthropic thinking
provider_options={"thinking": {"display": "summarized"}}, # merged into every request
compaction=Compaction(trigger_tokens=100_000), # Anthropic server-side summarisation
clear_tool_results=ClearToolResults(trigger_tokens=60_000, keep=4),
)
With compaction, the API summarises earlier context itself: client trimming stops, compaction cost is
included in total_cost_usd, and each summarisation is audited as context_compacted (tool-result
clearing as context_edited). memory_window=N still caps history by message count if you want it.
Full example: examples/long_running_agent.py.
Put policy around what an agent does — deny tools, require a human, redact what tools return, or stop the run:
from agent_kit.hooks import Decision, Hooks, deny_tools, require_approval
async def approve(req): # Slack button, approvals API, terminal prompt…
return await slack.ask(f"Run {req.tool_name}({req.arguments})?")
agent = Agent(
provider,
tools=[lookup_order, refund_order, close_account],
config=AgentConfig(
hooks=Hooks(
before_tool=[
deny_tools("close_account", reason="needs a human", stop_run=True),
require_approval("refund_order", reason="refunds move money"),
],
after_tool=[redact_cards], # return Decision.replace(masked_output)
before_llm=[stop_after_hours], # return Decision.deny("outside business hours")
),
approver=approve,
approval_timeout_s=300, # no answer → deny
),
)
| Hook | Can return |
|---|---|
before_tool | allow · deny(reason, stop_run=False) · ask(reason) |
after_tool | allow · replace(output) · deny(reason) |
before_llm | allow · deny(reason) (stops the run) |
Hooks can be sync or async; returning None allows. Everything else fails closed: a hook that raises,
an ask with no approver, a denied or timed-out approval — all deny. A denied tool call reaches the model
as a tool error it can work around; stop_run=True raises RunStoppedByHookError. Every decision is an
audit event (tool_denied, approval_requested, approval_granted, approval_denied,
tool_output_replaced, llm_call_denied). Full example: examples/approval_gate.py.
Give an agent a run store and every run checkpoints at each turn boundary. Runs survive crashes and deploys, and approvals can wait for a human for as long as it takes:
from agent_kit import SUSPEND
from agent_kit.durable import SQLiteRunStore
agent = Agent(provider, tools=[refund], config=AgentConfig(
run_store=SQLiteRunStore("runs.db"),
hooks=Hooks(before_tool=[require_approval("refund")]),
approver=SUSPEND, # park the run instead of waiting inline
))
result = await agent.run("Refund order A-1001", run_id="ticket-9913")
result.status # "suspended"
result.pending_approvals # [PendingApproval(call_id=..., tool_name="refund", arguments={...})]
# hours later, in any process that builds the same Agent:
result = await agent.resume("ticket-9913", approvals={call_id: True})
agent.resume(run_id) continues a run that crashed, failed, or was killed, from its
last checkpoint. A tool that was running when the process died is re-run only if it is marked
idempotent=True; otherwise the model is told the call was interrupted, so a refund never runs twice.RunConflictError before it can execute anything.run_suspended, run_resumed, tool_interrupted events).resume_stream() streams a resumed run; resuming a completed run returns its stored result.RunStore is a small async protocol (save / load / mark_tool_started / list / delete), so Postgres
or Redis stores drop in. Full example: examples/durable_approval.py.
Turn any agent into a tool another agent can delegate to:
research = Agent(provider, tools=[lookup_order]).as_tool("research", "Look up facts about orders.")
refunds = Agent(provider, tools=[issue_refund], config=AgentConfig(
hooks=Hooks(before_tool=[require_approval("issue_refund")]),
)).as_tool("refunds", "Handle a refund request.", output_type=RefundOutcome)
lead = Agent(provider, tools=[research, refunds], config=AgentConfig(
run_store=SQLiteRunStore("runs.db"),
approver=SUSPEND,
hooks=Hooks(before_tool=[deny_tools("close_account", reason="manual only")]),
max_run_cost_usd=1.00,
))
The model calls refunds with a task; every call is a fresh child run with its own memory and audit chain,
so the same agent tool can run several times in one turn. Delegation cannot escape the parent run:
| The child keeps | The child inherits from the calling run |
|---|---|
provider, model, tools, allowed_tools, system prompt, turn limit, retry, circuit breaker, context settings | hooks (run after the child's own — any deny wins), approver, run store, and whatever is left of max_run_cost_usd |
issue_refund asks for approval inside refunds, the lead run suspends and
result.pending_approvals lists it as "<refunds call>/<issue_refund call>".
lead.resume(run_id, approvals={that_id: True}) resumes the child and then the lead.max_run_cost_usd before its next model call and is
included in total_cost_usd.tool_call event records the child's delegated_run_id and final
delegated_root_hash, so tampering with a child chain shows from the parent. Child runs report to
agent-kit Cloud as their own runs with parent_run_id.allow_only(...) on a lead must list the
child tools it permits, too. Nesting is limited by AgentConfig(max_delegation_depth=5).For a fixed graph of agents, use DAGOrchestrator. Full example: examples/delegation.py.
Everything a tool returns — a web page, a document, an MCP result, a delegated agent's answer — lands in the model's context as if it were trustworthy. Screen it first:
from agent_kit.scanning import NullconeScanner, PatternScanner, scan_tool_output
agent = Agent(provider, tools=[fetch_page, research], config=AgentConfig(hooks=Hooks(after_tool=[
scan_tool_output(
PatternScanner(), # local rules, no network
NullconeScanner(), # optional threat-intel lookups
block_at="high", warn_at="medium", # stop_run_at=... is also available
trusted_tools=["lookup_order"], # internal tools you don't need to screen
),
])))
The most severe finding decides what the model sees:
| Severity reached | Model sees |
|---|---|
stop_run_at (off by default) | nothing — the run stops with RunStoppedByHookError |
block_at (default high) | Tool output blocked: possible prompt injection: <rule> (<severity>) |
warn_at (default medium) | the output inside an agentkit_scan envelope that marks it untrusted data |
below warn_at | the output unchanged; the finding is still recorded |
PatternScanner rules (disable any with disable=[...], add your own with extra_rules=[PatternRule(...)]):
| Rule | Severity | Catches |
|---|---|---|
unicode_tags | critical | instructions hidden in invisible Unicode tag characters |
role_token | critical | chat-template control tokens and fake system / tool-result tags |
instruction_override | high | text telling the model to discard its previous instructions or system prompt |
hidden_text | high | bidirectional overrides and runs of zero-width characters |
encoded_payload | high | base64 blobs that decode to either of the two rules above |
exfil_markdown | medium | markdown images and links that carry data out in their query string |
persona_switch | medium | named jailbreak personas and "developer mode" switches |
Rules favour precision: tool output is full of ordinary prose, docs, and code, so everyday phrasing isn't flagged.
NullconeScanner checks URLs, domains, IPs, and hashes found in the output against the
Nullcone threat database. It sends those indicators — never the output, never URL query
strings or credentials — so treat it as data egress. Confidence filtering comes from the API (unverified and
low-score hits are ignored by default); reserved names and private IPs are never looked up; answers are cached; it
fails open on errors unless fail_closed=True, and pauses after HTTP 429.
Every decision with findings appends a tool_output_flagged audit event and reports it to agent-kit Cloud (rule,
severity, JSON path, and matched indicator — no output text), where a tool_output_flagged alert rule with
min_severity pages on-call. A lead agent's scanner also screens every delegated child's tool outputs. Any object
with a name and an async scan(spans) -> list[Finding] method is a scanner. Full example:
examples/scanned_tools.py; design: specs/17-tool-output-scanning.md.
Use tools from any Model Context Protocol server — pip install agent-kit-ai[mcp]:
from agent_kit.tools.mcp import MCPToolset, http, require_approval_unless_read_only, stdio
async with MCPToolset(
stdio("fs", "npx", "-y", "@modelcontextprotocol/server-filesystem", "/srv/docs"),
http("linear", "https://mcp.linear.app/mcp", headers={"Authorization": f"Bearer {key}"}),
) as mcp:
agent = Agent(provider, tools=[*mcp.tools, my_tool], config=AgentConfig(
hooks=Hooks(before_tool=[require_approval_unless_read_only(mcp)]),
approver=approve,
))
await agent.run("Summarise the onboarding docs and file a Linear issue for anything out of date")
# every server disconnected (subprocesses terminated) here
server__tool: allowed_tools, hooks, approvals, budgets, audit, and Cloud reporting all apply.deny_tools("exec") and require_approval("exec") also match shell__exec on any server. allow_only matches exactly — list MCP tools by their full server__tool name, so an allowlist entry never admits a same-named tool from another server. A helper naming a tool the agent (or its child agents) doesn't have logs a warning at the first run.call_timeout_s) reach the model as tool errors.require_approval_unless_read_only(mcp) asks before any MCP tool not marked readOnlyHint — a server can't skip the gate by omitting hints.required server that fails to connect closes the others and raises MCPConnectionError; required=False skips it.Full example: examples/mcp_tools.py.
# Core (Anthropic only)
pip install agent-kit-ai
# With OpenAI support
pip install agent-kit-ai[openai]
# With OpenTelemetry
pip install agent-kit-ai[otel]
# Everything
pip install agent-kit-ai[all]
Connect any agent to the agent-kit Cloud backend — fleet metrics, audit trail storage, alerting, budgets, and SLA context. There is no hosted service yet: run the server yourself (self-hosting guide) and point the SDK at it.
from agent_kit.cloud import CloudReporter
reporter = CloudReporter(
api_key="akt_live_...", # or set AGENTKIT_API_KEY
base_url="https://agentkit.internal.acme.com", # your server, or set AGENTKIT_BASE_URL
project="production",
agent_name="billing-assistant",
)
agent = Agent(
provider=AnthropicProvider(),
config=AgentConfig(cloud=reporter),
)
result = await agent.run("Process this invoice.")
# Events are batched and shipped automatically — no await needed.
CloudReporter is fire-and-forget: network errors are logged at DEBUG level and never propagate to your agent. Performance is completely unaffected by cloud connectivity.
| Event | When |
|---|---|
run_start | Agent loop begins |
turn_complete | Each LLM response + tool calls |
run_complete | Successful finish (includes audit root hash) |
run_error | Unhandled exception |
circuit_state_change | CB opens / half-opens / closes |
audit_flush | Full Merkle chain (if audit_enabled=True) |
CloudReporter(
api_key="akt_live_...",
project="production",
agent_name="billing-assistant",
flush_interval_s=5.0, # how often to batch-send (default 5s)
max_queue_size=1000, # drop events if queue exceeds this
include_output=False, # never ships LLM output text to cloud
)
See docs/cloud-quickstart.md to get started, or docs/self-hosting.md to run the backend yourself.
reporter = CloudReporter(project="support", agent_name="support-bot")
agent = Agent(
AnthropicProvider(),
config=AgentConfig(
cloud=reporter,
max_run_cost_usd=2.00, # per run, enforced locally
enforce_budgets=True, # fleet budgets defined in agent-kit Cloud
),
)
Budgets ($200/day for support-bot) live in agent-kit Cloud at /v1/budgets; when one trips, the next
model call raises BudgetExceededError and a budget_exceeded alert fires. See
docs/cloud-quickstart.md.
Caps fail closed: a model agent-kit has no price for raises UnpricedModelError rather than counting as
$0.00. Price it once at startup — agent_kit.providers.set_price("ft:gpt-4.1:acme", 3.00, 12.00) (USD per
million input / output tokens, longest prefix wins). Ollama models are free and need no price.
Keep it. Adapters report Claude Agent SDK and OpenAI Agents SDK runs — turns, tool calls, subagents, handoffs, guardrails, cost — to the same audit trail, fleet dashboard, and alerts. The audit chain is built on your machine, and nothing changes on the server:
# Claude Agent SDK — pip install agent-kit-ai[claude-agent-sdk]
from agent_kit.integrations.claude_agent_sdk import ClaudeAgentObserver
observer = ClaudeAgentObserver(CloudReporter(project="support"))
options = observer.with_hooks(ClaudeAgentOptions(allowed_tools=["Read", "Grep"]))
async for message in observer.observe(query(prompt=prompt, options=options), prompt=prompt):
... # messages arrive unchanged
# OpenAI Agents SDK — pip install agent-kit-ai[openai-agents]
from agent_kit.integrations.openai_agents import AgentKitTraceProcessor
add_trace_processor(AgentKitTraceProcessor(CloudReporter(project="support")))
Runnable versions: examples/claude_agent_sdk_monitored.py,
examples/openai_agents_monitored.py.
Rising Sun License v1.0. Free for personal use. Commercial deployments connect to the Nous network.
Download counts and popularity will appear here as the collector receives them.
| Version | Published | Direct dependencies | Open version |
|---|---|---|---|
| 0.5.1 | 21 Sept 2026 | 15 |
This describes our collected data; it is not a security assessment.
Observed since 20 Oct, 22:31 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package:f244a29d09095e2cf9d8a267f6065d910c53ada03e1d2ed93bd5a672d41403ad