# AgentsKit.js — full docs > Every page of https://www.agentskit.io/docs flattened into one file. Designed for LLM ingestion. See also https://www.agentskit.io/llms.txt for the index. Generated at build time from 291 docs pages. --- # AgentsKit.js Source: https://www.agentskit.io/docs > The agent toolkit the JavaScript ecosystem finally has — small packages, one contract, everything composes. Agents shouldn't be a monolith. AgentsKit.js is a family of small, plug-and-play packages covering the whole agent lifecycle in JavaScript: chat UIs, autonomous runtimes, tools, skills, memory, RAG, observability, evaluation, sandboxing. Install what you need. Everything else stays out of your bundle. ## Start here Pick a path by what you're shipping. - **Just want a chat UI?** → [Quick start](/docs/get-started/getting-started/quickstart) · [React](/docs/reference/packages/react) · [Vue](/docs/reference/packages/vue) · [Svelte](/docs/reference/packages/svelte) · [Solid](/docs/reference/packages/solid) · [Angular](/docs/reference/packages/angular) · [React Native](/docs/reference/packages/react-native) · [Ink](/docs/reference/packages/ink) - **Building an autonomous agent?** → [Runtime](/docs/agents/runtime) · [Tools](/docs/agents/tools) · [Skills](/docs/agents/skills) · [Multi-agent topologies](/docs/agents/topologies) - **Need RAG?** → [createRAG](/docs/data/rag/create-rag) · [Loaders](/docs/data/rag/loaders) · [Reranking](/docs/data/rag/rerank) - **Shipping to production?** → [Observability](/docs/production/observability) · [Security](/docs/production/security) · [Evals](/docs/production/evals) · [Durable execution](/docs/agents/durable) ## The substrate Six stable contracts. Every package is an implementation of one. | Contract | Role | Deep dive | |---|---|---| | **Adapter** | LLM provider seam | [concept](/docs/get-started/concepts/adapter) · [implementations](/docs/data/providers) | | **Tool** | Function the model calls | [concept](/docs/get-started/concepts/tool) · [implementations](/docs/agents/tools) | | **Skill** | Declarative persona | [concept](/docs/get-started/concepts/skill) · [implementations](/docs/agents/skills) | | **Memory** | Chat history + vector state | [concept](/docs/get-started/concepts/memory) · [implementations](/docs/data/memory) | | **Retriever** | Context fetching | [concept](/docs/get-started/concepts/retriever) · [implementations](/docs/data/rag) | | **Runtime** | The loop that composes them all | [concept](/docs/get-started/concepts/runtime) · [implementations](/docs/agents) | Learn the six once; swap implementations forever. The [mental model](/docs/get-started/concepts/mental-model) walks through how they compose. ## What's inside - **21 packages** under `@agentskit/*` — foundation, providers, UI bindings, runtime, capabilities, observability, infrastructure. [Full index](/docs/reference/packages/overview). - **7 framework bindings** sharing one `useChat` contract. [UI matrix](/docs/ui). - **20+ provider adapters** (hosted + local + embedders). [Providers](/docs/data/providers). - **60+ recipes** grouped by theme. [Recipes](/docs/reference/recipes). - **Open specs** — A2A, Manifest, Eval Format, AgentSchema — portable JSON. [Specs](/docs/reference/specs). ## Keep reading - **[Comparison](/docs/get-started/comparison)** — AgentsKit vs LangChain, Vercel AI, Mastra, LlamaIndex. - **[Migrating](/docs/get-started/migrating)** — port from Vercel AI SDK, LangChain.js, Mastra. - **[For agents](/docs/for-agents)** — dense LLM-friendly reference per contract. - **[Examples](/docs/reference/examples)** — live interactive demos. - **[Contribute](/docs/reference/contribute)** — built in the open. Your PR helps. - **[Discord](https://discord.gg/zx6z2p4jVb)** — Office Hours every Friday; direct line to maintainers. --- # Agents Source: https://www.agentskit.io/docs/agents > Everything that runs the loop — runtime, tools, skills, delegation, durable execution, topologies, background agents, HITL, self-debug. The runtime is the engine. Everything else plugs into it. ## Core - [Runtime](./runtime) — `createRuntime`, ReAct loop, events, cost + token accounting. - [Tools](./tools) — how tools are invoked, parallelism, confirmation. - [Skills](./skills) — prompts + behaviors bundled into reusable personas. - [Delegation](./delegation) — sub-agents, handoffs, shared context. ## Scale - [Durable execution](./durable) — persist steps, replay, resume. - [Topologies](./topologies) — supervisor · swarm · hierarchical · blackboard. - [Background agents](./background) — cron + webhook triggers. - [Speculate](./speculate) — run N candidates, pick best. ## Production - [Human-in-the-loop](./hitl) — approvals, gated tool calls, review queues. - [Self-debug](./self-debug) — agents that read their own traces and retry. ## Related - [Package: runtime](/docs/reference/packages/runtime) - [For agents: runtime](/docs/for-agents/runtime) - [Concepts: runtime](/docs/get-started/concepts/runtime) --- # Background agents Source: https://www.agentskit.io/docs/agents/background > Trigger runs on a schedule or HTTP webhook. ## Cron scheduler ```ts import { createRuntime, createCronScheduler } from '@agentskit/runtime' const runtime = createRuntime({ adapter, tools }) const scheduler = createCronScheduler({ runtime }) scheduler.add({ id: 'daily-digest', schedule: '0 9 * * *', task: 'Summarize yesterday\'s PRs', }) scheduler.start() ``` Zero-dep cron: `parseSchedule` + `cronMatches` are exported for custom triggers. ## Webhooks ```ts import { createRuntime, createWebhookHandler } from '@agentskit/runtime' const handler = createWebhookHandler({ runtime, map: (req) => ({ task: `Handle ${req.body.event}` }), }) // Wire into Next.js / Express / Hono / Bun / Deno export const POST = (req) => handler(req) ``` ## Related - [Recipe: background agents](/docs/reference/recipes/background-agents) - [Durable](./durable) --- # Multi-Agent Delegation Source: https://www.agentskit.io/docs/agents/delegation > Coordinate multiple specialist agents from a parent agent using directed delegation. Coordinate multiple specialist agents from a parent agent using directed delegation. ## Install ```bash npm install @agentskit/runtime @agentskit/adapters @agentskit/skills @agentskit/tools ``` ## Quick Start ```ts import { createRuntime, createSharedContext } from '@agentskit/runtime' import { anthropic } from '@agentskit/adapters' import { planner, researcher, coder } from '@agentskit/skills' import { webSearch, filesystem } from '@agentskit/tools' const runtime = createRuntime({ adapter: anthropic({ apiKey, model: 'claude-sonnet-4-6' }), }) const result = await runtime.run('Build a landing page about quantum computing', { skill: planner, delegates: { researcher: { skill: researcher, tools: [webSearch()], maxSteps: 3 }, coder: { skill: coder, tools: [...filesystem({ basePath: './src' })], maxSteps: 8 }, }, }) ``` ## How It Works When you configure `delegates`, the runtime auto-generates tools named `delegate_`. The parent LLM calls them like any other tool. Each delegate runs its own ReAct loop and returns a result. ## DelegateConfig ```ts interface DelegateConfig { skill: SkillDefinition // required — the child's behavior tools?: ToolDefinition[] // tools available to the child adapter?: AdapterFactory // optional — different LLM per child maxSteps?: number // default: 5 } ``` ## Shared Context ```ts const ctx = createSharedContext({ project: 'landing-page' }) runtime.run('Build it', { delegates: { ... }, sharedContext: ctx }) // Parent reads/writes ctx.set('key', 'value') ctx.get('key') // Children get read-only view — set() is not available ``` ## Child Isolation - **Fresh messages** — no parent history - **Inherits observers** — events visible in logging - **No memory** — doesn't share parent's memory - **Depth limit** — `maxDelegationDepth` default 3 ## Events ``` [10:00:01] => delegate:start researcher [depth=1] "Research quantum computing" [10:00:03] <= delegate:end researcher (2100ms) "Found 3 papers on..." ``` ## Related - [Runtime](/docs/agents/runtime) — ReAct loop - [Skills](/docs/agents/skills) — behavioral prompts - [Observability](/docs/observability) — trace events --- # Durable execution Source: https://www.agentskit.io/docs/agents/durable > Persist every step. Resume after crash. Replay deterministically. ```ts import { createRuntime, createDurableRunner, createFileStepLog, } from '@agentskit/runtime' const runtime = createRuntime({ adapter, tools }) const durable = createDurableRunner({ runtime, store: createFileStepLog({ path: '.agentskit/steps.jsonl' }), }) await durable.run({ runId: 'r-42', input: 'refactor auth middleware' }) // Crash → restart → resume from last completed step await durable.resume('r-42') ``` ## Step log contract ```ts type StepRecord = { runId: string seq: number kind: 'llm' | 'tool' | 'event' at: number input: unknown output?: unknown error?: string } ``` ## Stores - `createInMemoryStepLog()` — tests. - `createFileStepLog({ path })` — JSONL on disk. - BYO: implement `StepLogStore` (Redis, Postgres, S3, etc.). ## Related - [Recipe: durable execution](/docs/reference/recipes/durable-execution) - [Topologies](./topologies) · [Self-debug](./self-debug) --- # Human-in-the-loop Source: https://www.agentskit.io/docs/agents/hitl > Gate tool calls behind approval. Surface reviews to humans before execution. ## Gate a tool ```ts import { makeTool } from '@agentskit/tools' const deployTool = makeTool({ name: 'deploy', description: 'Deploy to production', schema: z.object({ service: z.string() }), requiresConfirmation: true, execute: async ({ service }) => deploy(service), }) ``` Runtime pauses on invocation and emits `tool.awaiting-approval`. Resume with `chat.approve(toolCallId)` or `chat.deny(toolCallId, reason)`. ## UI - [ToolConfirmation](/docs/ui/tool-confirmation) — drop-in React / Vue / etc. ## Patterns - **Auto-approve low risk:** approve if cost under threshold; gate the rest. - **Review queue:** persist `awaiting-approval` to a DB; humans approve from dashboard. - **Double-sign:** require two approvers; track via shared context. ## Related - [Recipe: HITL approvals](/docs/reference/recipes/hitl-approvals) - [Recipe: confirmation-gated tool](/docs/reference/recipes/confirmation-gated-tool) - [Security → mandatory sandbox](/docs/production/security/mandatory-sandbox) --- # Runtime Source: https://www.agentskit.io/docs/agents/runtime > @agentskit/runtime is the execution engine for autonomous agents. It runs a ReAct loop — observe, think, act — until the model produces a final answer or a step limit is reached. import { ContributeCallout } from '@/components/contribute/contribute-callout' `@agentskit/runtime` is the execution engine for autonomous agents. It runs a ReAct loop — observe, think, act — until the model produces a final answer or a step limit is reached. ## When to use - **Headless** agents (CLI workers, jobs, tests) with tools, memory, retrieval, and optional delegation. - You already use [`@agentskit/adapters`](../data-layer/adapters); the same factories work here. For interactive terminal chat prefer [`@agentskit/ink`](../chat-uis/ink); for browser UI prefer [`@agentskit/react`](../chat-uis/react). ## Install ```bash npm install @agentskit/runtime @agentskit/adapters ``` [`@agentskit/core`](../packages/core) is included transitively; add it explicitly if you need types without pulling the full runtime graph. ## Basic usage ```ts import { createRuntime } from '@agentskit/runtime' import { anthropic } from '@agentskit/adapters' const runtime = createRuntime({ adapter: anthropic({ apiKey: process.env.ANTHROPIC_API_KEY!, model: 'claude-sonnet-4-6' }), }) const result = await runtime.run('What is 3 + 4?') console.log(result.content) // "7" ``` ### Demo adapter (no API key) ```ts import { createRuntime } from '@agentskit/runtime' import { generic } from '@agentskit/adapters' const runtime = createRuntime({ adapter: generic({ /* custom send/parse */ }), }) ``` ## ReAct loop Each call to `runtime.run()` enters the following loop: ``` observe → think → act → observe → ... ``` 1. **Observe** — retrieve context from memory or a retriever and inject it into the prompt. 2. **Think** — send messages + tools to the LLM and stream the response. 3. **Act** — if the LLM calls tools, execute them and append results as `tool` messages. 4. Repeat until the model returns a plain text response or `maxSteps` is reached. ## `RunResult` `runtime.run()` resolves to a `RunResult` object: ```ts interface RunResult { content: string // Final text response from the model messages: Message[] // Full conversation including tool calls and results steps: number // How many loop iterations ran toolCalls: ToolCall[] // Every tool call made during the run durationMs: number // Total wall-clock time } ``` ### Example ```ts const result = await runtime.run('List the files in the current directory', { tools: [shell({ allowed: ['ls'] })], }) console.log(result.content) // Model's final answer console.log(result.steps) // e.g. 2 console.log(result.durationMs) // e.g. 1340 result.toolCalls.forEach(tc => { console.log(tc.name, tc.args, tc.result) }) ``` ## `RuntimeConfig` ```ts interface RuntimeConfig { adapter: AdapterFactory // Required — the LLM provider tools?: ToolDefinition[] // Tools available to the agent systemPrompt?: string // Default system prompt memory?: ChatMemory // Persist and reload conversation history retriever?: Retriever // RAG source injected each step observers?: Observer[] // Event listeners (logging, tracing) maxSteps?: number // Max loop iterations (default: 10) temperature?: number maxTokens?: number delegates?: Record maxDelegationDepth?: number // Default: 3 } ``` ## `RunOptions` Override per-call defaults on `runtime.run(task, options)`: ```ts const result = await runtime.run('Summarize this document', { systemPrompt: 'You are a concise summarizer.', tools: [readFileTool], maxSteps: 5, skill: summarizer, }) ``` ## Aborting a run Pass an `AbortSignal` to cancel mid-run. The runtime checks the signal before each step and before each tool call. ```ts const controller = new AbortController() setTimeout(() => controller.abort(), 5000) // cancel after 5 s const result = await runtime.run('Long running task', { signal: controller.signal, }) ``` ## Memory When a `memory` is configured, the runtime saves all messages at the end of each run. On the next run it reloads prior context automatically. ```ts import { createRuntime } from '@agentskit/runtime' import { createInMemoryMemory } from '@agentskit/core' import { anthropic } from '@agentskit/adapters' const runtime = createRuntime({ adapter: anthropic({ apiKey: process.env.ANTHROPIC_API_KEY!, model: 'claude-sonnet-4-6' }), memory: createInMemoryMemory(), }) await runtime.run('My name is Alice.') const result = await runtime.run('What is my name?') console.log(result.content) // "Your name is Alice." ``` For durable storage use [`sqliteChatMemory` or `redisChatMemory`](../data-layer/memory) from `@agentskit/memory`. Memory is saved after `RunResult` is assembled — if you abort early, partial messages are still persisted up to the abort point. ## Retriever (RAG) Pass a `Retriever` (for example from [`createRAG`](../data-layer/rag)) via `retriever` in `RuntimeConfig`. Each loop step can inject retrieved context before the model thinks — same contract as chat UI. ## Observers `observers` accepts [`Observer`](../packages/core) instances from `@agentskit/core` for low-level events. Pair with [`@agentskit/observability`](../infrastructure/observability) when you need structured traces. ## Troubleshooting | Symptom | Likely fix | |---------|------------| | Hits `maxSteps` with no answer | Model keeps calling tools; raise `maxSteps`, tighten tool descriptions, or adjust system prompt. | | Tool timeout / hang | Add `signal` with a deadline; ensure tools reject on overload. | | No prior context | Confirm `memory` uses the same `conversationId` (for backends that scope by id). | | Empty retrieval | Check embedder dimensions match vector store; verify ingest ran for your corpus. | ## See also [Start here](../getting-started/read-this-first) · [Packages](../packages/overview) · [TypeDoc](pathname:///agentskit/api-reference/) (`@agentskit/runtime`) · [Tools](./tools) · [Skills](./skills) · [Delegation](./delegation) · [@agentskit/core](../packages/core) --- # Self-debug Source: https://www.agentskit.io/docs/agents/self-debug > Agents that read their own traces, diagnose failures, retry with corrections. ## Pattern 1. Run fails or produces low-confidence output. 2. Feed the trace (steps, tool calls, errors) back to the agent. 3. Agent proposes a fix (different tool, different arg, smaller step). 4. Retry with the fix applied. ## Sketch ```ts import { createDurableRunner } from '@agentskit/runtime' const res = await durable.run({ runId, input }) if (res.status === 'error') { const trace = await durable.getTrace(runId) const fix = await debuggerRuntime.run({ input: `Trace failed. Diagnose and suggest fix.\n\n${JSON.stringify(trace)}`, }) await durable.run({ runId: `${runId}-retry`, input: fix.output }) } ``` ## Related - [Durable](./durable) · [Recipe: self-debug](/docs/reference/recipes/self-debug) - [Recipe: time-travel debug](/docs/reference/recipes/time-travel-debug) --- # Skills Source: https://www.agentskit.io/docs/agents/skills > Personas as packages — system prompt + behavior, versioned and composable. ## Ready-made `researcher` · `coder` · `planner` · `critic` · `summarizer` · `codeReviewer` · `sqlGen` · `dataAnalyst` · `translator` ## Composition - `composeSkills(a, b, ...)` — merge skills into one. - `listSkills()` — metadata for every bundled skill. ## Marketplace - `createSkillRegistry` — publish / list / install / unpublish. - Semver range syntax: `1.2.3` / `^` / `~` / `>=` / `*`. - [Recipe: Skill marketplace](/docs/reference/recipes/skill-marketplace). *Per-skill + marketplace deep dives land in step 6 of the docs IA rollout.* ## Related - [Concepts: Skill](/docs/get-started/concepts/skill) - [Package: @agentskit/skills](/docs/reference/packages/skills) - [For agents: skills](/docs/for-agents/skills) --- # Authoring skills Source: https://www.agentskit.io/docs/agents/skills/authoring > A skill = prompt + behavior + metadata. Versioned, composable, shippable. ```ts import { defineSkill } from '@agentskit/skills' export const triageSkill = defineSkill({ name: 'triage', version: '1.0.0', description: 'Classify support tickets into categories', systemPrompt: `You are a triage assistant. Categorize into: billing, tech, feedback.`, examples: [ { input: 'Charge declined', output: 'billing' }, { input: 'App crashes', output: 'tech' }, ], temperature: 0.2, }) ``` ## Fields | Field | Type | Purpose | |---|---|---| | `name` | `string` | registry id | | `version` | `semver` | compatibility | | `systemPrompt` | `string` | prepended to conversation | | `examples` | `{ input, output }[]` | few-shot | | `temperature` / `topP` / `maxTokens` | `number` | sampling overrides | ## Use ```ts const runtime = createRuntime({ adapter, skills: [triageSkill] }) ``` ## Related - [Marketplace](./marketplace) · [Personas](./personas) - [Concepts → Skill](/docs/get-started/concepts/skill) --- # codeReviewer Source: https://www.agentskit.io/docs/agents/skills/code-reviewer > Reviews diffs for bugs, security, style. Comments in PR-review format. ```ts import { codeReviewer } from '@agentskit/skills' import { github } from '@agentskit/tools' const runtime = createRuntime({ adapter, skills: [codeReviewer], tools: [...github({ token: process.env.GITHUB_TOKEN! })], }) await runtime.run('Review PR #123 on AgentsKit-io/agentskit') ``` ## When to reach for it - Automated PR review bot. - Snippet review without a PR context (use with the forthcoming [generic codeReviewerSkill](https://github.com/AgentsKit-io/agentskit/issues/448)). - Pre-commit quality gate. ## Behavior - Reads diff hunk by hunk; comments inline-style with line refs. - Flags bugs > security > style (priority order). - Rejects drive-by nitpicks; prefers actionable suggestions. - Returns `approve` / `request changes` / `comment` with rationale. ## Pairs well with - `github` tool (comment on PRs) - `linear` tool (file follow-up tickets) - [HITL approvals](/docs/agents/hitl) (gate the final "approve") ## Related - [Skills overview](./) · [coder](./coder) · [critic](./critic) - Issue #310 — [prReviewerSkill](https://github.com/AgentsKit-io/agentskit/issues/310) - Issue #454 — [securityAuditorSkill](https://github.com/AgentsKit-io/agentskit/issues/454) --- # coder Source: https://www.agentskit.io/docs/agents/skills/coder > Implements features from specs — TypeScript-first, TDD-leaning, opinionated about code quality. ```ts import { createRuntime } from '@agentskit/runtime' import { coder } from '@agentskit/skills' import { filesystem, shell } from '@agentskit/tools' const runtime = createRuntime({ adapter, skills: [coder], tools: [ ...filesystem({ basePath: './workspace' }), shell({ allowed: ['pnpm', 'node', 'git'] }), ], }) ``` ## When to reach for it - Agent needs to write real code (not snippets). - Multi-file edits, package additions, refactors. - Works best when paired with a sandboxed filesystem. ## Behavior - Reads before writing — lists files, opens existing implementations. - Writes TypeScript strict; prefers named exports. - Tests alongside code (Vitest conventions). - Avoids unsafe casts, runs type-check before declaring done. ## Tools it expects | Tool | Why | |---|---| | `filesystem` | Scoped read/write. | | `shell` (allowlisted) | `pnpm`, `node`, `git`. | ## Compose Pair with [`codeReviewer`](./code-reviewer) for a self-review pass, or delegate to the [`critic`](./critic) skill to stress-test output. ## Safety Always sandbox with `filesystem({ basePath })` and `shell({ allowed })` — coder writes files. For mutation flows use [mandatory sandbox](/docs/production/security/mandatory-sandbox). ## Related - [Skills overview](./) · [codeReviewer](./code-reviewer) - Recipe: [code-reviewer](/docs/reference/recipes/code-reviewer) --- # critic Source: https://www.agentskit.io/docs/agents/skills/critic > Stress-tests proposals. Finds flaws, missing cases, weak assumptions — before they ship. ```ts import { researcher, critic } from '@agentskit/skills' import { composeSkills } from '@agentskit/skills' const thorough = composeSkills(researcher, critic) ``` ## When to reach for it - Second-pass review after [coder](./coder) or [researcher](./researcher). - Red-team on architecture proposals. - Pair with [planner](./planner) for "plan → critique → revise" loops. ## Behavior - Takes an artifact (plan, code, doc) and produces structured critique. - Categorizes findings: blockers → risks → nits. - Offers alternatives, not just objections. - Never invents criteria — grounds critique in stated goals. ## Pair patterns | Pattern | Skill chain | |---|---| | Plan-critique-revise | `planner` → `critic` → `planner` (revise) | | Research then stress | `researcher` → `critic` | | Review committed PRs | `codeReviewer` → `critic` (find what the reviewer missed) | ## Related - [Skills overview](./) · [planner](./planner) - [Agents → Topologies](/docs/agents/topologies#swarm) --- # dataAnalyst Source: https://www.agentskit.io/docs/agents/skills/data-analyst > Analyzes tabular data — loads, inspects, profiles, and explains findings in plain English. ```ts import { dataAnalyst } from '@agentskit/skills' import { s3, documentParsers } from '@agentskit/tools' const runtime = createRuntime({ adapter, skills: [dataAnalyst], tools: [ ...s3({ client, bucket: 'datasets' }), ...documentParsers({ parseXlsx }), ], }) ``` ## When to reach for it - "Look at this CSV and tell me what's interesting." - Column-profile + anomaly hunting. - Lightweight EDA before handing off to a BI tool. ## Behavior - Loads file → reads schema + head/tail. - Reports shape, dtypes, null %, basic stats. - Highlights outliers + correlations worth investigating. - Never fabricates a column that isn't there. ## Related - [Skills overview](./) · [sqlGen](./sql-gen) - Issue #451 — [dataAnalystSkill v2 (tabular-aware)](https://github.com/AgentsKit-io/agentskit/issues/451) - [documentParsers](/docs/agents/tools/integrations/document-parsers) --- # Skill marketplace Source: https://www.agentskit.io/docs/agents/skills/marketplace > Publish, install, version skills. Semver range resolution. ```ts import { createSkillRegistry } from '@agentskit/skills' const registry = createSkillRegistry({ storage: fileStorage({ path: '.agentskit/skills' }), }) await registry.publish(triageSkill) const resolved = await registry.install('triage', '^1.0.0') ``` ## API | Method | Purpose | |---|---| | `publish(skill)` | add a version | | `install(name, range)` | resolve semver range | | `list()` | all available | | `unpublish(name, version)` | remove | ## Ranges `1.2.3` · `^1.2.3` · `~1.2.3` · `>=1.2.0 <2.0.0` · `*`. ## Related - [Recipe: skill marketplace](/docs/reference/recipes/skill-marketplace) --- # Ready-made personas Source: https://www.agentskit.io/docs/agents/skills/personas > Nine skills bundled with @agentskit/skills. Importable, composable. | Skill | Purpose | |---|---| | [`researcher`](./researcher) | gather + cite sources | | [`coder`](./coder) | write code from specs | | [`codeReviewer`](./code-reviewer) | critique diffs, flag bugs | | [`planner`](./planner) | decompose tasks into steps | | [`critic`](./critic) | stress-test a proposal | | [`summarizer`](./summarizer) | terse summaries | | [`sqlGen`](./sql-gen) | schema → SQL | | [`dataAnalyst`](./data-analyst) | analyze tabular data | | [`translator`](./translator) | natural language → natural language | ## Usage ```ts import { researcher, summarizer, composeSkills } from '@agentskit/skills' const combined = composeSkills(researcher, summarizer) const runtime = createRuntime({ adapter, skills: [combined] }) ``` ## Listing ```ts import { listSkills } from '@agentskit/skills' for (const s of listSkills()) console.log(s.name, s.version) ``` ## Related - [Authoring](./authoring) · [Marketplace](./marketplace) --- # planner Source: https://www.agentskit.io/docs/agents/skills/planner > Decomposes vague goals into ordered steps with clear success criteria. Ideal as the top node in a supervisor topology. ```ts import { planner, coder } from '@agentskit/skills' import { supervisor } from '@agentskit/runtime' const team = supervisor({ planner: { runtime: createRuntime({ adapter, skills: [planner] }) }, workers: { coder: { runtime: createRuntime({ adapter, skills: [coder] }) }, }, }) await team.run('Ship a feature flag API with SDK + docs.') ``` ## When to reach for it - Tasks that span multiple specialists. - Problems that need decomposition before execution. - Top-of-tree node in a [supervisor topology](/docs/agents/topologies). ## Behavior - Breaks goals into numbered steps with success criteria. - Routes each step to the named worker best suited for it. - Detects blockers early ("need API key", "need design review"). - Closes the loop: verifies each worker's output meets the step's success criterion. ## Related - [Skills overview](./) · [critic](./critic) - [Agents → Topologies](/docs/agents/topologies) · [Delegation](/docs/agents/delegation) --- # researcher Source: https://www.agentskit.io/docs/agents/skills/researcher > Methodical web-search persona that finds, cross-references, and summarizes with citations. ```ts import { createRuntime } from '@agentskit/runtime' import { researcher } from '@agentskit/skills' import { webSearch } from '@agentskit/tools' const runtime = createRuntime({ adapter, skills: [researcher], tools: [webSearch()], }) ``` ## When to reach for it - You need sourced answers, not opinion. - You want the agent to flag uncertainty instead of speculating. - You're ingesting RAG context and need disciplined citation output. ## Behavior - Breaks the question into sub-queries and searches each independently. - Cross-references across sources; flags contradictions. - Output leads with a direct answer, then cited claims, then a confidence assessment. ## Tools it expects | Tool | Why | |---|---| | `web_search` | Mandatory — required by the system prompt. | | any RAG retriever | Optional — pairs with [`createRAG`](/docs/data/rag/create-rag) for internal corpora. | ## Example output > **Q:** Main differences between PostgreSQL and MySQL for a new web app? > > **A:** PostgreSQL excels at complex queries, JSONB, and strict SQL. MySQL is simpler to set up and faster for read-heavy simple schemas. > Sources: [1] PostgreSQL docs, [2] MySQL reference manual, [3] DB-Engines comparison. > Confidence: high — well-documented, stable differences. ## Compose Pair with [`summarizer`](./summarizer) for long reports, or [`critic`](./critic) to stress-test conclusions: ```ts import { composeSkills } from '@agentskit/skills' const thorough = composeSkills(researcher, critic) ``` ## Related - [Skills overview](./) · [Authoring](./authoring) · [Marketplace](./marketplace) - Issue #453 — [researcherSkill v2 (citation-first)](https://github.com/AgentsKit-io/agentskit/issues/453) --- # sqlGen Source: https://www.agentskit.io/docs/agents/skills/sql-gen > Natural language → SQL with dialect awareness + safety rails. ```ts import { sqlGen } from '@agentskit/skills' import { postgres } from '@agentskit/tools' const runtime = createRuntime({ adapter, skills: [sqlGen], tools: [...postgres({ run, readonly: true, maxRows: 100 })], }) await runtime.run('How many users signed up last 7 days vs the previous 7 days?') ``` ## When to reach for it - Data Q&A agent over a SQL DB. - Dashboard copilots. - Schema-aware natural-language querying. ## Behavior - Asks for schema if none provided; remembers in session. - Emits parameterized queries with explicit `LIMIT`. - Flags risky statements (`DROP`, `UPDATE` without WHERE) and refuses by default. - Dialect-aware: SQLite / Postgres / MySQL / DuckDB. ## Tools it expects - `postgresQuery` or `sqliteQueryTool` (issue #433). - `readonly: true` by default. See [postgres integration](/docs/agents/tools/integrations/postgres). ## Safety - Always run readonly first. Opt-in to writes via a separate, gated tool. - Cap results with `maxRows`. LLMs love `SELECT *` — protect from OOM. ## Related - [Skills overview](./) - Issue #449 — [sqlAnalystSkill](https://github.com/AgentsKit-io/agentskit/issues/449) (sibling). --- # summarizer Source: https://www.agentskit.io/docs/agents/skills/summarizer > Compresses long inputs into short, faithful summaries. Length-aware; preserves citations. ```ts import { summarizer } from '@agentskit/skills' const runtime = createRuntime({ adapter, skills: [summarizer], }) await runtime.run('Summarize this 40-page PDF into 10 bullets.') ``` ## When to reach for it - Digest long docs (PDFs, meeting transcripts, threads). - Rollup: summarize many sources into one. - Pair with [researcher](./researcher) for sourced summaries. ## Behavior - Respects explicit length targets ("10 bullets", "under 100 words"). - Preserves citations + speaker attribution when present. - Declines to summarize when the input contains critical fine-print that must not be collapsed. ## Memory recipe Use alongside `createAutoSummarizingMemory` to fold old turns automatically: ```ts import { createAutoSummarizingMemory } from '@agentskit/memory' const memory = createAutoSummarizingMemory({ summarize: async (msgs) => runtime.run({ input: msgs, skill: summarizer }), triggerAt: 30, keep: 10, }) ``` ## Related - [Skills overview](./) - [Memory → auto-summarize](/docs/data/memory/auto-summarize) - Recipe: [auto-summarize](/docs/reference/recipes/auto-summarize) --- # translator Source: https://www.agentskit.io/docs/agents/skills/translator > High-quality translation between natural languages. Preserves formatting, tone, terminology. ```ts import { translator } from '@agentskit/skills' const runtime = createRuntime({ adapter, skills: [translator], }) await runtime.run('Translate this product page to pt-BR preserving markdown.') ``` ## When to reach for it - App i18n copy drafts (always human-review). - Localizing docs + blog posts. - Transcript translation paired with [whisper](/docs/agents/tools/integrations/whisper). ## Behavior - Preserves markdown / HTML / code blocks exactly. - Keeps product-name + brand terms untranslated unless told otherwise. - Flags untranslatable idioms; proposes locale-aware alternatives. - Maintains tone (formal / friendly / technical) from source. ## Glossary mode (v2 — issue #452) Future version takes a glossary map to force specific term translations. Track: [skill: translatorSkill v2](https://github.com/AgentsKit-io/agentskit/issues/452). ## Related - [Skills overview](./) - [whisper](/docs/agents/tools/integrations/whisper) — translate audio. --- # Speculate Source: https://www.agentskit.io/docs/agents/speculate > Run N candidates in parallel. Pick the best by a user-defined scorer. ```ts import { speculate } from '@agentskit/runtime' const { best, candidates } = await speculate({ candidates: [ { name: 'gpt-4o', run: () => runtime1.run(task) }, { name: 'claude', run: () => runtime2.run(task) }, { name: 'gemini', run: () => runtime3.run(task) }, ], pick: (results) => results.reduce((a, b) => (b.score > a.score ? b : a)), }) ``` ## When to use - Cross-model reliability on hard prompts. - Latency reduction (fire all, take first success). - Cost/quality trade-offs evaluated per run. ## Related - [Recipe: speculative execution](/docs/reference/recipes/speculative-execution) - [createFallbackAdapter](/docs/data/providers/higher-order) --- # Tools Source: https://www.agentskit.io/docs/agents/tools > Every function the agent can call. Built-ins, integrations, MCP bridge, composers. ## Authoring - `defineTool` (core) — JSON-Schema inference. - `defineZodTool` — Zod-based with runtime validation. - `composeTool` — chain N tools into one macro tool. [Recipe](/docs/reference/recipes/tool-composer). - `wrapToolWithSelfDebug` — LLM-corrected retries. [Recipe](/docs/reference/recipes/self-debug). - `createMandatorySandbox` — allow / deny / require-sandbox / validators. [Recipe](/docs/reference/recipes/mandatory-sandbox). ## Built-ins `webSearch` · `fetchUrl` · `filesystem` · `shell` ## Integrations (20+) Dev / chat: `github` · `linear` · `slack` · `notion` · `discord` Google: `gmail` · `googleCalendar` Business: `stripe` · `postgres` · `s3` Scraping: `firecrawl` · `reader` · `documentParsers` Voice / image: `openaiImages` · `elevenlabs` · `whisper` · `deepgram` Data: `maps` · `weather` · `coingecko` Browser: `browserAgent` ## MCP bridge - `createMcpClient` + `toolsFromMcpClient` — consume any MCP server. - `createMcpServer` — publish AgentsKit tools to any MCP host. - [Recipe: MCP bridge](/docs/reference/recipes/mcp-bridge). *Per-integration pages + authoring deep dives land in step 6 of the docs IA rollout.* ## Related - [Concepts: Tool](/docs/get-started/concepts/tool) - [Package: @agentskit/tools](/docs/reference/packages/tools) - [For agents: tools](/docs/for-agents/tools) --- # Authoring tools Source: https://www.agentskit.io/docs/agents/tools/authoring > Define, validate, compose, guard. One contract, multiple flavors. ## defineTool Zero-runtime-dep path. JSON Schema inferred from TS types. ```ts import { defineTool } from '@agentskit/core' export const addTodo = defineTool({ name: 'add_todo', description: 'Add a todo item', schema: { type: 'object', properties: { text: { type: 'string' } }, required: ['text'], }, execute: async ({ text }) => ({ id: crypto.randomUUID(), text }), }) ``` ## defineZodTool Runtime validation. Fails fast on bad args. ```ts import { defineZodTool } from '@agentskit/tools' import { z } from 'zod' export const addTodo = defineZodTool({ name: 'add_todo', description: 'Add a todo item', schema: z.object({ text: z.string().min(1) }), execute: async ({ text }) => ({ id: crypto.randomUUID(), text }), }) ``` ## composeTool Chain N tools into one macro. Each receives previous output. ```ts import { composeTool } from '@agentskit/tools' const research = composeTool({ name: 'research', steps: [webSearch, fetchUrl, summarize], }) ``` [Recipe](/docs/reference/recipes/tool-composer). ## wrapToolWithSelfDebug LLM-corrected retry on schema-mismatch or error. ```ts import { wrapToolWithSelfDebug } from '@agentskit/tools' const safeDeploy = wrapToolWithSelfDebug(deployTool, { adapter, maxRetries: 3 }) ``` [Recipe](/docs/reference/recipes/self-debug). ## createMandatorySandbox Policy wrapper: allow / deny / require-sandbox / validators. ```ts import { createMandatorySandbox } from '@agentskit/tools' const sandboxed = createMandatorySandbox(shellTool, { allow: ['ls', 'cat'], deny: ['rm', 'sudo'], requireSandbox: true, }) ``` [Recipe](/docs/reference/recipes/mandatory-sandbox) · [Security](/docs/production/security/mandatory-sandbox). ## Related - [Built-ins](./builtins) · [Integrations](./integrations) · [MCP](./mcp) - [Concepts → Tool](/docs/get-started/concepts/tool) --- # Built-in tools Source: https://www.agentskit.io/docs/agents/tools/builtins > Ship-ready tools — web, fetch, filesystem, shell. | Tool | Import | Notes | |---|---|---| | `webSearch` | `@agentskit/tools` | BYO provider (Tavily, Brave, SerpAPI, etc.) | | `fetchUrl` | `@agentskit/tools` | HTTP GET + content extraction | | `filesystem` | `@agentskit/tools` | read/write/list, scoped to a root | | `shell` | `@agentskit/tools` | exec commands, sandbox-friendly | ## webSearch ```ts import { webSearch, tavilyProvider } from '@agentskit/tools' const tool = webSearch({ provider: tavilyProvider({ apiKey: process.env.TAVILY_API_KEY! }) }) ``` ## fetchUrl ```ts import { fetchUrl } from '@agentskit/tools' const tool = fetchUrl({ stripBoilerplate: true, maxBytes: 500_000 }) ``` ## filesystem ```ts import { filesystem } from '@agentskit/tools' const tool = filesystem({ root: '/tmp/agent-work', readonly: false }) ``` ## shell ```ts import { shell, createMandatorySandbox } from '@agentskit/tools' const tool = createMandatorySandbox(shell({ cwd: '/tmp/agent-work' }), { deny: ['rm -rf', 'sudo'], requireSandbox: true, }) ``` ## Related - [Authoring](./authoring) · [Integrations](./integrations) - [Security → mandatory sandbox](/docs/production/security/mandatory-sandbox) --- # Integrations Source: https://www.agentskit.io/docs/agents/tools/integrations > 20+ ready-made connectors for the services agents actually need. Each follows the same contract — install, config, execute — and ships granular sub-tools alongside a bundled set. ## Categories | Category | Integrations | |---|---| | **Dev + chat** | [github](./github) · [linear](./linear) · [slack](./slack) · [notion](./notion) · [discord](./discord) | | **Google** | [gmail](./gmail) · [googleCalendar](./google-calendar) | | **Business** | [stripe](./stripe) · [postgres](./postgres) · [s3](./s3) | | **Scraping** | [firecrawl](./firecrawl) · [reader](./reader) · [documentParsers](./document-parsers) | | **Voice + image** | [openaiImages](./openai-images) · [elevenlabs](./elevenlabs) · [whisper](./whisper) · [deepgram](./deepgram) | | **Data** | [maps](./maps) · [weather](./weather) · [coingecko](./coingecko) | | **Browser** | [browserAgent](./browser-agent) (Puppeteer) | ## Usage Import whole integrations for the full tool set, or cherry-pick sub-tools: ```ts import { github, slack, stripeCreatePaymentIntent } from '@agentskit/tools' import { createRuntime } from '@agentskit/runtime' const runtime = createRuntime({ adapter, tools: [ ...github({ token: process.env.GITHUB_TOKEN! }), ...slack({ token: process.env.SLACK_BOT_TOKEN! }), stripeCreatePaymentIntent({ apiKey: process.env.STRIPE_API_KEY! }), ], }) ``` ## Credentials Every integration reads a token or API key from its `config`. No process-env magic, no globals — you pass what you want, where you want. ## Conventions - **Sub-tools** are named `` (e.g. `githubCreateIssue`). - **Bundled tool** exported under the integration name (e.g. `github`) returns an array of sub-tools. - **Config types** exported as `Config`. - **Errors** wrap provider 4xx/5xx with `ToolError` so runtime sees a consistent shape. - **Mandatory sandbox** — wrap risky integrations (shell, postgres write, github create) via `createMandatorySandbox`. ## Related - [Authoring tools](../authoring) — wrap what's missing. - [Built-in tools](../builtins) — webSearch, fetchUrl, filesystem, shell. - [MCP bridge](../mcp) — consume or publish tool sets over MCP. - Recipes: [integrations](/docs/reference/recipes/integrations) · [more integrations](/docs/reference/recipes/more-integrations). --- # browserAgent Source: https://www.agentskit.io/docs/agents/tools/integrations/browser-agent > Puppeteer-backed browser — goto, click, fill, read, screenshot, wait. For agents that need to interact with JS-rendered pages. ```ts import { browserAgent } from '@agentskit/tools' import puppeteer from 'puppeteer' const browser = await puppeteer.launch({ headless: 'new' }) const page = await browser.newPage() const runtime = createRuntime({ adapter, tools: [...browserAgent({ page })], }) ``` ## Sub-tools | Name | Purpose | |---|---| | `browserGoto` | Navigate to a URL | | `browserClick` | Click a selector | | `browserFill` | Type into an input | | `browserRead` | Extract visible text from the page | | `browserScreenshot` | Capture PNG buffer | | `browserWait` | Wait for selector or timeout | Bundled: `browserAgent(config)`. ## Config ```ts type BrowserAgentConfig = { page: BrowserPage // Puppeteer Page (or any matching shape) timeoutMs?: number screenshotPath?: string } ``` ## Example — site QA agent ```ts const runtime = createRuntime({ adapter, systemPrompt: 'Click through the signup flow and screenshot each step. Report errors.', tools: [...browserAgent({ page })], }) ``` ## Safety Headless browsers are powerful — **wrap in [mandatory sandbox](/docs/production/security/mandatory-sandbox)** to restrict allowed domains and block credentials leaks. ## Alternatives - [firecrawl](./firecrawl) — prefer when you only need static content extraction. - [reader](./reader) — single-URL, no interaction. - [fetchUrl](../builtins) — no JS rendering, no cost. ## Related - [Integrations overview](./) --- # coingecko Source: https://www.agentskit.io/docs/agents/tools/integrations/coingecko > CoinGecko — crypto prices + market charts. Free tier, no key required. ```ts import { coingecko } from '@agentskit/tools' const runtime = createRuntime({ adapter, tools: [...coingecko()], }) ``` ## Sub-tools | Name | Purpose | |---|---| | `coingeckoPrice` | Spot price by coin id + vs_currency | | `coingeckoMarketChart` | Historical prices / market caps / volumes | Bundled: `coingecko(config)`. ## Config ```ts type CoinGeckoConfig = { apiKey?: string // Pro tier; optional fetch?: typeof fetch } ``` ## Example ```ts await runtime.run('Compare ETH and SOL price movement over the last 30 days; summarize the divergence.') ``` ## Related - [Integrations overview](./) --- # deepgram Source: https://www.agentskit.io/docs/agents/tools/integrations/deepgram > Deepgram STT — low-latency transcription with speaker diarization. Preferred for realtime + voice-agent flows. ```ts import { deepgram } from '@agentskit/tools' const runtime = createRuntime({ adapter, tools: [...deepgram({ apiKey: process.env.DEEPGRAM_API_KEY! })], }) ``` ## Sub-tools | Name | Purpose | |---|---| | `deepgramTranscribe` | Batch or streaming transcription with diarization | Bundled: `deepgram(config)`. ## Config ```ts type DeepgramConfig = { apiKey: string model?: 'nova-3' | 'nova-2' | 'enhanced' | 'base' language?: string diarize?: boolean fetch?: typeof fetch } ``` ## Example — realtime call agent ```ts const runtime = createRuntime({ adapter, tools: [ ...deepgram({ apiKey, diarize: true }), ...elevenlabs({ apiKey: process.env.ELEVENLABS_API_KEY!, defaultVoiceId }), ], }) ``` ## Related - [Integrations overview](./) · [whisper](./whisper) — batch alternative. - Issue #479 — [voice mode component](https://github.com/AgentsKit-io/agentskit/issues/479). --- # discord Source: https://www.agentskit.io/docs/agents/tools/integrations/discord > Discord Bot API — post messages. For community bots, notifier agents, and interactive slash-command workflows. ```ts import { discord } from '@agentskit/tools' const runtime = createRuntime({ adapter, tools: [...discord({ token: process.env.DISCORD_BOT_TOKEN! })], }) ``` ## Sub-tools | Name | Purpose | |---|---| | `discordPostMessage` | Send a message to a channel (supports embeds) | Bundled: `discord(config)`. ## Config ```ts type DiscordConfig = { token: string // Bot token fetch?: typeof fetch } ``` ## Example — community bot ```ts const runtime = createRuntime({ adapter, systemPrompt: 'You answer AgentsKit questions, cite docs, and @mention maintainers on hard asks.', tools: [ ...discord({ token: process.env.DISCORD_BOT_TOKEN! }), ...ragTool, ], }) await runtime.run('User said: "how do I swap OpenAI for Claude?"') ``` ## Credentials - Create bot at discord.com/developers/applications. - Required intents: `GUILDS`, `GUILD_MESSAGES`. Add `MESSAGE_CONTENT` if parsing user replies. - Invite to server with `bot` + `applications.commands` scopes. ## Related - [Integrations overview](./) · [slack](./slack). - Recipe: [discord-bot](/docs/reference/recipes/discord-bot) — full slash-command + HITL workflow. --- # documentParsers Source: https://www.agentskit.io/docs/agents/tools/integrations/document-parsers > PDF, DOCX, XLSX parsers — BYO parser functions keep core dependency-free. ```ts import { documentParsers } from '@agentskit/tools' import pdfParse from 'pdf-parse' import * as mammoth from 'mammoth' import * as xlsx from 'xlsx' const runtime = createRuntime({ adapter, tools: [...documentParsers({ parsePdf: async (buf) => (await pdfParse(buf)).text, parseDocx: async (buf) => (await mammoth.extractRawText({ buffer: buf })).value, parseXlsx: async (buf) => { const wb = xlsx.read(buf) return wb.SheetNames.map((n) => xlsx.utils.sheet_to_csv(wb.Sheets[n])).join('\n---\n') }, })], }) ``` ## Sub-tools | Name | Purpose | |---|---| | `parsePdf` | Extract text from a PDF buffer | | `parseDocx` | Extract text from a `.docx` buffer | | `parseXlsx` | Extract CSV-flat sheets from `.xlsx` | Bundled: `documentParsers(config)` returns all three. ## Why BYO Core stays zero-dep. You pick parser quality + size trade-offs: - **PDF:** `pdf-parse` (small) / `unpdf` (WASM, browser-safe) / `pdfjs-dist` (Mozilla). - **DOCX:** `mammoth` (most faithful) / `docx4js`. - **XLSX:** `xlsx` (SheetJS) / `exceljs`. ## Example — resume intake ```ts const runtime = createRuntime({ adapter, tools: [ ...s3({ client, bucket: 'resumes' }), ...documentParsers({ parsePdf, parseDocx }), ...rag.tools, ], }) ``` ## Related - [Integrations overview](./) · [s3](./s3). - [RAG loaders](/docs/data/rag/loaders) — `loadPdf` uses the same BYO pattern. --- # elevenlabs Source: https://www.agentskit.io/docs/agents/tools/integrations/elevenlabs > ElevenLabs text-to-speech — high-quality voices in 30+ languages. For narration, voice agents, IVR flows. ```ts import { elevenlabs } from '@agentskit/tools' const runtime = createRuntime({ adapter, tools: [...elevenlabs({ apiKey: process.env.ELEVENLABS_API_KEY! })], }) ``` ## Sub-tools | Name | Purpose | |---|---| | `elevenlabsTts` | Convert text to audio (returns buffer + content-type) | Bundled: `elevenlabs(config)`. ## Config ```ts type ElevenLabsConfig = { apiKey: string defaultVoiceId?: string modelId?: string // e.g. 'eleven_multilingual_v2' fetch?: typeof fetch } ``` ## Example — narrate a digest ```ts const runtime = createRuntime({ adapter, tools: [ ...elevenlabs({ apiKey, defaultVoiceId: '21m00Tcm4TlvDq8ikWAM' }), ...s3({ client, bucket: 'podcast-output' }), ], }) await runtime.run('Summarize today\'s PRs, narrate with ElevenLabs, save MP3 to S3.') ``` ## Related - [Integrations overview](./) · [whisper](./whisper) · [deepgram](./deepgram) — STT pairs. - Issue #479 — [voice mode component](https://github.com/AgentsKit-io/agentskit/issues/479). --- # firecrawl Source: https://www.agentskit.io/docs/agents/tools/integrations/firecrawl > Firecrawl — scrape a URL or crawl a site, returning clean markdown. The workhorse for RAG ingestion from the open web. ```ts import { firecrawl } from '@agentskit/tools' const runtime = createRuntime({ adapter, tools: [...firecrawl({ apiKey: process.env.FIRECRAWL_API_KEY! })], }) ``` ## Sub-tools | Name | Purpose | |---|---| | `firecrawlScrape` | One-shot scrape of a single URL | | `firecrawlCrawl` | Recursive crawl with include/exclude patterns | Bundled: `firecrawl(config)`. ## Config ```ts type FirecrawlConfig = { apiKey: string baseUrl?: string // self-hosted Firecrawl fetch?: typeof fetch } ``` ## Example — on-demand RAG ```ts const runtime = createRuntime({ adapter, tools: [...firecrawl({ apiKey }), ...rag.tools], }) await runtime.run('Fetch https://example.com/changelog and answer what shipped this week.') ``` ## Comparison | Tool | When to use | |---|---| | [firecrawl](./firecrawl) | Structured markdown + crawl trees, API-backed, paid | | [reader](./reader) | Jina Reader — fast, free, single-URL | | [fetchUrl](../builtins) | Zero-dep HTTP GET, no cleanup | | [browserAgent](./browser-agent) | Full JS-rendered interaction | ## Related - [Integrations overview](./) · [RAG loaders](/docs/data/rag/loaders). --- # github Source: https://www.agentskit.io/docs/agents/tools/integrations/github > GitHub REST v3 — search issues, create issues, comment. Pairs with HITL for ship-gating bots. ```ts import { github } from '@agentskit/tools' import { createRuntime } from '@agentskit/runtime' const runtime = createRuntime({ adapter, tools: [...github({ token: process.env.GITHUB_TOKEN! })], }) ``` ## Sub-tools | Name | Purpose | |---|---| | `githubSearchIssues` | Full-text + filter search across issues and PRs | | `githubCreateIssue` | Open an issue (title + body + labels) | | `githubCommentIssue` | Post a comment on an existing issue or PR | Bundled: `github(config)` returns the three above. ## Config ```ts type GitHubConfig = { token: string // personal access token or GitHub App token baseUrl?: string // override for GitHub Enterprise fetch?: typeof fetch } ``` ## Example — triage agent ```ts import { defineZodTool } from '@agentskit/tools' import { github } from '@agentskit/tools' import { createRuntime } from '@agentskit/runtime' const runtime = createRuntime({ adapter, systemPrompt: 'You triage issues: label + link to related + close dupes.', tools: [...github({ token: process.env.GITHUB_TOKEN! })], }) await runtime.run('Triage the last 10 open issues on AgentsKit-io/agentskit') ``` ## Credentials - **Personal access token:** `repo` + `issues` scopes. - **GitHub App:** install on the target repo; pass the installation token. - **GitHub Enterprise:** set `baseUrl` to your instance. ## Safety Write operations (create issue, comment) are idempotent but not destructive. For delete + merge flows wrap with [mandatory sandbox](/docs/production/security/mandatory-sandbox) or require [HITL approval](/docs/agents/hitl). ## Related - [Integrations overview](./) · [linear](./linear) — same shape for Linear. - Recipes: [code-reviewer](/docs/reference/recipes/code-reviewer) · [integrations](/docs/reference/recipes/integrations). - [HITL approvals](/docs/agents/hitl) for gating mutations. --- # gmail Source: https://www.agentskit.io/docs/agents/tools/integrations/gmail > Gmail API — list messages, send email. For inbox-triage agents + notification flows. ```ts import { gmail } from '@agentskit/tools' const runtime = createRuntime({ adapter, tools: [...gmail({ accessToken: process.env.GMAIL_ACCESS_TOKEN! })], }) ``` ## Sub-tools | Name | Purpose | |---|---| | `gmailListMessages` | Query inbox (supports `q` filter syntax) | | `gmailSendEmail` | Compose + send a plain-text or HTML email | Bundled: `gmail(config)`. ## Config ```ts type GmailConfig = { accessToken: string // OAuth access token (refresh on your side) fetch?: typeof fetch } ``` ## Example — inbox triage ```ts const runtime = createRuntime({ adapter, systemPrompt: 'Label, categorize, and draft replies for unread emails. Never send without HITL approval.', tools: [ ...gmail({ accessToken }), hitlTool, // require approval before gmailSendEmail ], }) ``` ## Credentials - **OAuth 2.0 required** — Gmail has no long-lived API keys. - Scopes: `gmail.readonly` + `gmail.send`. - Handle token refresh in your app — this tool accepts short-lived access tokens. ## Safety Always gate `gmailSendEmail` behind [HITL approval](/docs/agents/hitl). Accidental mass-send is hard to undo. ## Related - [Integrations overview](./) · [googleCalendar](./google-calendar) — same OAuth dance. - [HITL approvals](/docs/agents/hitl). --- # googleCalendar Source: https://www.agentskit.io/docs/agents/tools/integrations/google-calendar > Google Calendar API — list events, create events. For scheduling agents + standup prep bots. ```ts import { googleCalendar } from '@agentskit/tools' const runtime = createRuntime({ adapter, tools: [...googleCalendar({ accessToken: process.env.GCAL_ACCESS_TOKEN! })], }) ``` ## Sub-tools | Name | Purpose | |---|---| | `calendarListEvents` | List events in a time range (primary or named calendar) | | `calendarCreateEvent` | Create an event with attendees + conference link | Bundled: `googleCalendar(config)`. ## Config ```ts type GoogleCalendarConfig = { accessToken: string calendarId?: string // default 'primary' fetch?: typeof fetch } ``` ## Example — standup prep ```ts const runtime = createRuntime({ adapter, tools: [...googleCalendar({ accessToken })], }) await runtime.run('What meetings do I have today? Summarize with a 1-sentence agenda each.') ``` ## Credentials - **OAuth 2.0** with scope `calendar.events` (read + write) or `calendar.readonly`. - Refresh tokens managed by your app — tool takes short-lived access tokens. ## Related - [Integrations overview](./) · [gmail](./gmail) — sibling OAuth. --- # linear Source: https://www.agentskit.io/docs/agents/tools/integrations/linear > Linear GraphQL — search issues, create issues. For triage, sprint-planning, release-notes agents. ```ts import { linear } from '@agentskit/tools' const runtime = createRuntime({ adapter, tools: [...linear({ token: process.env.LINEAR_API_KEY! })], }) ``` ## Sub-tools | Name | Purpose | |---|---| | `linearSearchIssues` | Query issues by team, state, labels, assignee | | `linearCreateIssue` | Create an issue with title + description + priority | Bundled: `linear(config)`. ## Config ```ts type LinearConfig = { token: string // Personal API key or OAuth token teamId?: string // Default team for creates fetch?: typeof fetch } ``` ## Example — auto-intake agent ```ts const runtime = createRuntime({ adapter, systemPrompt: 'Turn user reports into Linear issues with the right team + priority.', tools: [...linear({ token, teamId: 'BCK' })], }) await runtime.run('User reports: "Checkout broken on iOS 17, high priority"') ``` ## Credentials - **Personal API key:** workspace-level, full access. Good for single-user bots. - **OAuth:** user-scoped; required for multi-tenant SaaS. ## Related - [Integrations overview](./) · [github](./github) — sibling shape. - Issue #442 — [`linearTriageTool`](https://github.com/AgentsKit-io/agentskit/issues/442) adds triage-specific flows. --- # maps Source: https://www.agentskit.io/docs/agents/tools/integrations/maps > Geocoding + reverse-geocoding via any provider with an HTTP API (Google, Mapbox, OpenCage, Positionstack, Nominatim). ```ts import { maps } from '@agentskit/tools' const runtime = createRuntime({ adapter, tools: [...maps({ provider: 'google', apiKey: process.env.GOOGLE_MAPS_KEY!, })], }) ``` ## Sub-tools | Name | Purpose | |---|---| | `mapsGeocode` | Address → lat/lng | | `mapsReverseGeocode` | lat/lng → address | Bundled: `maps(config)`. ## Config ```ts type MapsConfig = { provider?: 'google' | 'mapbox' | 'opencage' | 'nominatim' apiKey?: string fetch?: typeof fetch } ``` ## Example — travel planner ```ts const runtime = createRuntime({ adapter, tools: [ ...maps({ provider: 'mapbox', apiKey }), ...weather(), ], }) await runtime.run('Find the coordinates of "Lisbon waterfront" and check the weather there tomorrow.') ``` ## Related - [Integrations overview](./) · [weather](./weather). --- # notion Source: https://www.agentskit.io/docs/agents/tools/integrations/notion > Notion API — search workspace, create pages. Pair with RAG to turn Notion into agent-queryable memory. ```ts import { notion } from '@agentskit/tools' const runtime = createRuntime({ adapter, tools: [...notion({ token: process.env.NOTION_TOKEN! })], }) ``` ## Sub-tools | Name | Purpose | |---|---| | `notionSearch` | Search pages + databases by query | | `notionCreatePage` | Create a page inside a parent page or database | Bundled: `notion(config)`. ## Config ```ts type NotionConfig = { token: string // Integration internal token version?: string // API version (defaults to latest tested) fetch?: typeof fetch } ``` ## Example — knowledge-base search ```ts const runtime = createRuntime({ adapter, systemPrompt: 'Answer from the engineering Notion; always cite the page title + URL.', tools: [...notion({ token })], }) await runtime.run('What\'s our on-call escalation policy?') ``` ## Credentials - Create integration at notion.so/my-integrations. - **Share** the target pages + databases with the integration (Notion gates access). ## Pair with RAG For semantic search across Notion, use [`loadNotionPage`](/docs/data/rag/loaders) to ingest content into a vector store — faster + cheaper for repeated queries than calling the API every turn. ## Related - [Integrations overview](./) · [RAG loaders](/docs/data/rag/loaders). --- # openaiImages Source: https://www.agentskit.io/docs/agents/tools/integrations/openai-images > OpenAI Images API (DALL-E / gpt-image) — generate images from prompts. ```ts import { openaiImages } from '@agentskit/tools' const runtime = createRuntime({ adapter, tools: [...openaiImages({ apiKey: process.env.OPENAI_API_KEY! })], }) ``` ## Sub-tools | Name | Purpose | |---|---| | `openaiImagesGenerate` | Create images from a text prompt | Bundled: `openaiImages(config)`. ## Config ```ts type OpenAIImagesConfig = { apiKey: string model?: 'gpt-image-1' | 'dall-e-3' | 'dall-e-2' defaultSize?: '1024x1024' | '1792x1024' | '1024x1792' fetch?: typeof fetch } ``` ## Example — marketing visual assistant ```ts const runtime = createRuntime({ adapter, tools: [ ...openaiImages({ apiKey, model: 'gpt-image-1' }), ...s3({ client, bucket: 'creatives' }), ], }) await runtime.run('Generate 3 hero images for an "AI for JavaScript" launch post, upload to S3.') ``` ## Cost Image generation is orders of magnitude more expensive than text — wrap via [costGuard](/docs/production/observability/cost-guard) or require HITL. ## Related - [Integrations overview](./) - [HITL](/docs/agents/hitl) · [costGuard](/docs/production/observability/cost-guard). --- # postgres Source: https://www.agentskit.io/docs/agents/tools/integrations/postgres > Postgres query tool — BYO runner keeps the adapter client-agnostic (`postgres.js`, `pg`, Drizzle, Prisma, Neon). ```ts import { postgres } from '@agentskit/tools' import postgresJs from 'postgres' const sql = postgresJs(process.env.DATABASE_URL!) const runtime = createRuntime({ adapter, tools: [...postgres({ run: async (query, params) => sql.unsafe(query, params as unknown[]), })], }) ``` ## Sub-tools | Name | Purpose | |---|---| | `postgresQuery` | Execute parameterized SQL, return rows + row-count | Bundled: `postgres(config)`. ## Config ```ts type PostgresConfig = { run: (query: string, params: unknown[]) => Promise readonly?: boolean // block non-SELECT statements maxRows?: number // cap on result size } ``` ## Example — data analyst agent ```ts import { postgres } from '@agentskit/tools' import { sqlAnalystSkill } from '@agentskit/skills' const runtime = createRuntime({ adapter, skills: [sqlAnalystSkill], tools: [...postgres({ run, readonly: true, maxRows: 100 })], }) await runtime.run('How many users signed up last week vs the week before?') ``` ## Safety - Default to `readonly: true`. Let the agent read before it ever writes. - For writes, wrap via [mandatory sandbox](/docs/production/security/mandatory-sandbox) with an allowlist of tables. - Never pass string-interpolated queries — always use the `params` array. ## Related - [Integrations overview](./) - Issue #447 — [read/write split helper](https://github.com/AgentsKit-io/agentskit/issues/447). - [sqliteQueryTool](https://github.com/AgentsKit-io/agentskit/issues/433) — local alternative. --- # reader Source: https://www.agentskit.io/docs/agents/tools/integrations/reader > Jina Reader — turn any URL into clean markdown. Free, fast, no API key required for casual use. ```ts import { reader } from '@agentskit/tools' const runtime = createRuntime({ adapter, tools: [...reader()], }) ``` ## Sub-tools | Name | Purpose | |---|---| | `readerFetch` | Fetch + clean a single URL via r.jina.ai | Bundled: `reader(config)`. ## Config ```ts type ReaderConfig = { apiKey?: string // for higher rate limits baseUrl?: string // default https://r.jina.ai fetch?: typeof fetch } ``` ## Example ```ts await runtime.run('Read https://news.ycombinator.com/item?id=40000000 and list the three most upvoted arguments.') ``` ## When to reach for reader vs alternatives - **reader** — zero-config, text-only, free tier. - **[firecrawl](./firecrawl)** — paid, structured markdown + crawl trees. - **[fetchUrl](../builtins)** — lowest-level; keeps HTML; zero-dep. - **[browserAgent](./browser-agent)** — when you need to click / fill / wait. ## Related - [Integrations overview](./) · [firecrawl](./firecrawl). --- # s3 Source: https://www.agentskit.io/docs/agents/tools/integrations/s3 > AWS S3 — get, put, list objects. BYO client keeps the tool AWS-SDK-version-agnostic. ```ts import { s3 } from '@agentskit/tools' import { S3Client } from '@aws-sdk/client-s3' const client = new S3Client({ region: 'us-east-1' }) const runtime = createRuntime({ adapter, tools: [...s3({ client, bucket: 'agent-scratch' })], }) ``` ## Sub-tools | Name | Purpose | |---|---| | `s3GetObject` | Read a key (text or signed-URL modes) | | `s3PutObject` | Write bytes to a key with optional content-type | | `s3ListObjects` | List keys under a prefix with pagination | Bundled: `s3(config)`. ## Config ```ts type S3Config = { client: S3Client // from @aws-sdk/client-s3 (or R2, MinIO, etc) bucket: string prefix?: string // scope all operations under a prefix } ``` ## Works with S3-compatible APIs Pass an `S3Client` configured for Cloudflare R2, MinIO, Backblaze B2 — same tool. ## Example — resume scanner ```ts const runtime = createRuntime({ adapter, tools: [ ...s3({ client, bucket: 'resumes', prefix: 'incoming/' }), ...documentParsers({ parse: pdfParse }), ], }) await runtime.run('List resumes uploaded today and extract contact info.') ``` ## Safety - Scope to a `prefix`; the tool enforces it on list + get + put. - For delete flows, gate via [mandatory sandbox](/docs/production/security/mandatory-sandbox). ## Related - [Integrations overview](./) - Issue #446 — [cloudflareR2Tool](https://github.com/AgentsKit-io/agentskit/issues/446). --- # slack Source: https://www.agentskit.io/docs/agents/tools/integrations/slack > Slack Web API — post messages, search history. For notifier bots + conversational search over a workspace. ```ts import { slack } from '@agentskit/tools' const runtime = createRuntime({ adapter, tools: [...slack({ token: process.env.SLACK_BOT_TOKEN! })], }) ``` ## Sub-tools | Name | Purpose | |---|---| | `slackPostMessage` | Post to a channel or DM (supports threading + blocks) | | `slackSearch` | `search.messages` — query your workspace history | Bundled: `slack(config)`. ## Config ```ts type SlackConfig = { token: string // Bot token (xoxb-...) or user token (xoxp-...) defaultChannel?: string fetch?: typeof fetch } ``` ## Example — daily digest bot ```ts const runtime = createRuntime({ adapter, tools: [ ...slack({ token, defaultChannel: 'C012345' }), ...github({ token: process.env.GITHUB_TOKEN! }), ], }) await runtime.run('Summarize yesterday\'s merged PRs and post to #eng-updates') ``` ## Scopes - **Bot tokens** need: `chat:write`, `channels:history`, `search:read` (user-token-only for `search`). - Create a Slack app → OAuth & Permissions → install to workspace. ## Related - [Integrations overview](./) · [discord](./discord) — sibling. - Recipes: [discord-bot](/docs/reference/recipes/discord-bot) (mirror for Discord). --- # stripe Source: https://www.agentskit.io/docs/agents/tools/integrations/stripe > Stripe API — create customers, payment intents. For checkout assistants + dunning agents. ```ts import { stripe } from '@agentskit/tools' const runtime = createRuntime({ adapter, tools: [...stripe({ apiKey: process.env.STRIPE_API_KEY! })], }) ``` ## Sub-tools | Name | Purpose | |---|---| | `stripeCreateCustomer` | Create a customer with email + metadata | | `stripeCreatePaymentIntent` | Server-side intent creation for a checkout flow | Bundled: `stripe(config)`. ## Config ```ts type StripeConfig = { apiKey: string // restricted or secret key (use restricted for agents) apiVersion?: string // e.g. '2025-01-27.acacia' fetch?: typeof fetch } ``` ## Example — checkout assistant ```ts const runtime = createRuntime({ adapter, systemPrompt: 'Gather name + email + amount, then create Stripe customer + intent. Never call Stripe without confirm.', tools: [ ...stripe({ apiKey: process.env.STRIPE_API_KEY! }), hitlTool, ], }) ``` ## Safety - **Use restricted keys** scoped to just `customers:write` + `payment_intents:write`. - **Always gate via [HITL](/docs/agents/hitl)** — money moves. - Log every call via [signed audit log](/docs/production/observability/audit-log). ## Related - [Integrations overview](./) - Issue #437 — [stripeWebhookTool](https://github.com/AgentsKit-io/agentskit/issues/437) inbound events. --- # weather Source: https://www.agentskit.io/docs/agents/tools/integrations/weather > Current weather by lat/lng. Free tier via open-meteo by default; swap provider via config. ```ts import { weather } from '@agentskit/tools' const runtime = createRuntime({ adapter, tools: [...weather()], }) ``` ## Sub-tools | Name | Purpose | |---|---| | `weatherCurrent` | Current conditions at a coordinate | Bundled: `weather(config)`. ## Config ```ts type WeatherConfig = { provider?: 'open-meteo' | 'openweathermap' | 'weatherapi' apiKey?: string // required except for open-meteo units?: 'metric' | 'imperial' fetch?: typeof fetch } ``` ## Example ```ts await runtime.run('What\'s the weather at 38.72, -9.14 right now?') ``` ## Related - [Integrations overview](./) · [maps](./maps) — pair for NL → coords. --- # whisper Source: https://www.agentskit.io/docs/agents/tools/integrations/whisper > OpenAI Whisper — speech-to-text for audio transcription. 99 languages. ```ts import { whisper } from '@agentskit/tools' const runtime = createRuntime({ adapter, tools: [...whisper({ apiKey: process.env.OPENAI_API_KEY! })], }) ``` ## Sub-tools | Name | Purpose | |---|---| | `whisperTranscribe` | Transcribe an audio buffer → text + segments | Bundled: `whisper(config)`. ## Config ```ts type WhisperConfig = { apiKey: string model?: 'whisper-1' | 'gpt-4o-transcribe' | 'gpt-4o-mini-transcribe' defaultLanguage?: string // ISO-639-1 hint fetch?: typeof fetch } ``` ## Example — meeting notes agent ```ts const runtime = createRuntime({ adapter, tools: [ ...s3({ client, bucket: 'recordings' }), ...whisper({ apiKey }), ], }) await runtime.run('Transcribe the latest recording from S3 and draft meeting notes with action items.') ``` ## Comparison | Tool | Latency | Cost | Speaker diarization | |---|---|---|---| | [whisper](./whisper) | medium | low | no | | [deepgram](./deepgram) | low (realtime) | medium | yes | ## Related - [Integrations overview](./) · [elevenlabs](./elevenlabs) — TTS pair. --- # MCP bridge Source: https://www.agentskit.io/docs/agents/tools/mcp > Consume or publish Model Context Protocol tools. Interop with Claude Desktop, Cursor, Continue, etc. ## Consume an MCP server ```ts import { createMcpClient, toolsFromMcpClient } from '@agentskit/tools' const client = await createMcpClient({ transport: 'stdio', command: 'my-mcp-server', }) const mcpTools = await toolsFromMcpClient(client) const runtime = createRuntime({ adapter, tools: [...mcpTools, ...myTools] }) ``` ## Publish AgentsKit tools as MCP ```ts import { createMcpServer } from '@agentskit/tools' const server = createMcpServer({ name: 'agentskit-devtools', version: '0.1.0', tools: [github(...), slack(...)], }) await server.listen({ transport: 'stdio' }) ``` ## Transports - `stdio` — sub-process pipe - `http` — HTTP/SSE - `ws` — WebSocket ## Related - [Recipe: MCP bridge](/docs/reference/recipes/mcp-bridge) - [Specs → A2A](/docs/reference/specs/a2a) --- # Topologies Source: https://www.agentskit.io/docs/agents/topologies > Four proven multi-agent patterns — supervisor, swarm, hierarchical, blackboard. ## supervisor One planner routes tasks to specialists. Specialists return; planner decides next step. ```ts import { supervisor } from '@agentskit/runtime' const team = supervisor({ planner: { runtime: plannerRuntime, name: 'planner' }, workers: { coder: { runtime: coderRuntime }, reviewer: { runtime: reviewerRuntime }, }, }) await team.run('Ship a new auth endpoint') ``` ## swarm Peer agents pass control directly via handoff tools. No central planner. ```ts import { swarm } from '@agentskit/runtime' const team = swarm({ agents: { triage: { runtime: triageRuntime, handoffsTo: ['billing', 'tech'] }, billing: { runtime: billingRuntime, handoffsTo: ['tech'] }, tech: { runtime: techRuntime, handoffsTo: ['billing'] }, }, entry: 'triage', }) ``` ## hierarchical Tree of supervisors. Great for large problem decomposition. ```ts import { hierarchical } from '@agentskit/runtime' const org = hierarchical({ root: { runtime: ceo }, children: [ { runtime: engLead, children: [{ runtime: backend }, { runtime: frontend }] }, { runtime: designLead }, ], }) ``` ## blackboard Shared scratchpad. Agents read + write to a common context; triggers based on state. ```ts import { blackboard, createSharedContext } from '@agentskit/runtime' const context = createSharedContext({ todo: [], done: [] }) const board = blackboard({ context, agents: { ... } }) ``` ## Related - [Recipe: multi-agent topologies](/docs/reference/recipes/multi-agent-topologies) - [Delegation](./delegation) · [Durable](./durable) --- # Data layer Source: https://www.agentskit.io/docs/data > Memory, RAG, and providers — where the agent reads and writes. Three substrates every agent touches: - **[Memory](/docs/data/memory)** — chat history + vector stores + higher-order wrappers (hierarchical, encrypted, graph, personalization). - **[RAG](/docs/data/rag)** — chunk, embed, retrieve, rerank, hybrid. Six document loaders ship built in. - **[Providers](/docs/data/providers)** — LLM chat + embedder adapters (20+), plus higher-order router / ensemble / fallback. ## Related - [Concepts: Memory](/docs/get-started/concepts/memory) · [Retriever](/docs/get-started/concepts/retriever) · [Adapter](/docs/get-started/concepts/adapter) - [Packages: memory](/docs/reference/packages/memory) · [rag](/docs/reference/packages/rag) · [adapters](/docs/reference/packages/adapters) --- # Memory Source: https://www.agentskit.io/docs/data/memory > Chat memory + vector stores + wrappers that make long-running agents practical. ## Chat memory (ordered history) - `createInMemoryMemory` — default, zero deps. - `createLocalStorageMemory` — browser-persisted. - `fileChatMemory` — JSON file. - `sqliteChatMemory` — SQLite-backed. - `redisChatMemory` — Redis-backed. ## Vector memory - `fileVectorMemory` — pure JS, file-persisted. - `redisVectorMemory` — Redis Vector. - `pgvector` — BYO SQL runner. [Recipe](/docs/reference/recipes/vector-adapters). - `pinecone` / `qdrant` / `chroma` / `upstashVector` — HTTP-backed managed stores. ## Higher-order wrappers - `createVirtualizedMemory` — hot window + cold retriever. [Recipe](/docs/reference/recipes/virtualized-memory). - `createHierarchicalMemory` — MemGPT working / recall / archival. [Recipe](/docs/reference/recipes/hierarchical-memory). - `createAutoSummarizingMemory` — fold oldest into a summary. [Recipe](/docs/reference/recipes/auto-summarize). - `createEncryptedMemory` — AES-GCM over any `ChatMemory`. [Recipe](/docs/reference/recipes/encrypted-memory). - `createInMemoryGraph` — knowledge graph (nodes + edges). [Recipe](/docs/reference/recipes/graph-memory). - `createInMemoryPersonalization` — per-subject profile. [Recipe](/docs/reference/recipes/personalization). *Per-backend deep dives land in step 4 of the docs IA rollout.* ## Related - [Concepts: Memory](/docs/get-started/concepts/memory) - [Package: @agentskit/memory](/docs/reference/packages/memory) - [For agents: memory](/docs/for-agents/memory) --- # createAutoSummarizingMemory Source: https://www.agentskit.io/docs/data/memory/auto-summarize > Fold oldest messages into a running summary. Token-budget-friendly. ```ts import { createAutoSummarizingMemory } from '@agentskit/memory' const memory = createAutoSummarizingMemory({ summarize: async (msgs) => adapter.complete({ messages: [{ role: 'system', content: 'Summarize tersely' }, ...msgs], }), triggerAt: 30, keep: 10, }) ``` ## Related - [Recipe: auto-summarize](/docs/reference/recipes/auto-summarize) - [virtualized](./virtualized) · [hierarchical](./hierarchical) --- # chroma Source: https://www.agentskit.io/docs/data/memory/chroma > Chroma vector DB via HTTP. ```ts import { chroma } from '@agentskit/memory' const store = chroma({ url: process.env.CHROMA_URL ?? 'http://localhost:8000', collection: 'agentskit', }) ``` ## Options | Option | Type | |---|---| | `url` | `string` | | `collection` | `string` | | `tenant` | `string?` | | `database` | `string?` | ## Related - [pinecone](./pinecone) · [qdrant](./qdrant) --- # createEncryptedMemory Source: https://www.agentskit.io/docs/data/memory/encrypted > AES-GCM-256 envelope over any ChatMemory. Keys never touch disk in plaintext. ```ts import { createEncryptedMemory } from '@agentskit/memory' const memory = await createEncryptedMemory(innerMemory, { key: process.env.AK_ENCRYPTION_KEY!, }) ``` ## Options | Option | Type | Default | |---|---|---| | `key` | `string \| CryptoKey` | required | | `aad` | `Uint8Array` | empty | ## What it does Wraps `append` / `list` calls: encrypts `parts` before write, decrypts on read. IVs generated per-message. Auth tag verified on read. ## Related - [Recipe: encrypted memory](/docs/reference/recipes/encrypted-memory) - [Security → PII](/docs/production/security/pii-redaction) --- # fileChatMemory Source: https://www.agentskit.io/docs/data/memory/file-chat > JSON-file-backed chat memory. Zero infra. Survives restarts. ```ts import { fileChatMemory } from '@agentskit/memory' const memory = fileChatMemory({ path: '.agentskit/chat.json' }) ``` ## Options | Option | Type | Default | |---|---|---| | `path` | `string` | required | | `maxMessages` | `number` | unlimited | | `encoding` | `'utf-8'` | `'utf-8'` | ## Trade-offs - Good for: CLIs, local dev, desktop apps. - Bad for: multi-process writes (no locking), high-throughput web. ## Related - [sqliteChatMemory](./sqlite) — concurrent, indexed. - [redisChatMemory](./redis-chat) — distributed. - [Concepts → Memory](/docs/get-started/concepts/memory) --- # fileVectorMemory Source: https://www.agentskit.io/docs/data/memory/file-vector > Pure-JS file-persisted vector store. Zero infra. ```ts import { fileVectorMemory } from '@agentskit/memory' const store = fileVectorMemory({ path: '.agentskit/vectors.json', dim: 1536 }) ``` ## Options | Option | Type | Default | |---|---|---| | `path` | `string` | required | | `dim` | `number` | required | | `metric` | `'cosine' \| 'dot' \| 'euclidean'` | `cosine` | ## Scale Fine up to ~10k vectors. For larger corpora use [pgvector](./pgvector), [pinecone](./pinecone), [qdrant](./qdrant), [chroma](./chroma), [upstash-vector](./upstash-vector). ## Related - [RAG](/docs/data/rag) · [Recipe: vector adapters](/docs/reference/recipes/vector-adapters) --- # createInMemoryGraph Source: https://www.agentskit.io/docs/data/memory/graph > Knowledge graph memory — nodes + edges, queryable. ```ts import { createInMemoryGraph } from '@agentskit/memory' const graph = createInMemoryGraph() graph.upsertNode({ id: 'u1', type: 'user', props: { name: 'Ada' } }) graph.upsertEdge({ from: 'u1', to: 'p1', type: 'owns' }) const related = graph.query({ from: 'u1', edgeType: 'owns' }) ``` ## Types - `GraphNode` — `{ id, type, props }` - `GraphEdge` — `{ from, to, type, props? }` - `GraphQuery` — traversal filters. ## Related - [Recipe: graph memory](/docs/reference/recipes/graph-memory) --- # createHierarchicalMemory Source: https://www.agentskit.io/docs/data/memory/hierarchical > MemGPT-style tiers — working / recall / archival. ```ts import { createHierarchicalMemory } from '@agentskit/memory' const memory = createHierarchicalMemory({ working: inMemory, recall: sqlite, archival: vector, workingLimit: 20, recallLimit: 500, }) ``` ## Tiers | Tier | Purpose | Typical store | |---|---|---| | working | hot, in-context | in-memory | | recall | session history | SQLite / Redis | | archival | semantic long-term | vector | Overflow cascades: working → recall → archival summary. ## Related - [Recipe: hierarchical memory](/docs/reference/recipes/hierarchical-memory) --- # createInMemoryPersonalization Source: https://www.agentskit.io/docs/data/memory/personalization > Per-subject profile store. Inject into system prompt. ```ts import { createInMemoryPersonalization, renderProfileContext } from '@agentskit/memory' const profiles = createInMemoryPersonalization() await profiles.set('user-42', { name: 'Ada', tz: 'UTC', likes: ['jazz'] }) const context = renderProfileContext(await profiles.get('user-42')) ``` ## API - `get(subject)` · `set(subject, profile)` · `patch(subject, partial)` · `delete(subject)`. ## Related - [Recipe: personalization](/docs/reference/recipes/personalization) --- # pgvector Source: https://www.agentskit.io/docs/data/memory/pgvector > Postgres + pgvector adapter. BYO SQL runner. ```ts import { pgvector } from '@agentskit/memory' import postgres from 'postgres' const sql = postgres(process.env.DATABASE_URL!) const store = pgvector({ run: async (query, params) => sql.unsafe(query, params as unknown[]), table: 'embeddings', dim: 1536, }) ``` ## Options | Option | Type | Default | |---|---|---| | `run` | `PgVectorRunner` | required | | `table` | `string` | `embeddings` | | `dim` | `number` | required | | `metric` | `'cosine' \| 'l2' \| 'ip'` | `cosine` | ## Why BYO runner Keeps the adapter client-agnostic — works with `postgres.js`, `pg`, Drizzle, Prisma raw, Neon serverless. ## Related - [Recipe: vector adapters](/docs/reference/recipes/vector-adapters) --- # pinecone Source: https://www.agentskit.io/docs/data/memory/pinecone > Managed vector DB. Namespaces + metadata filters. ```ts import { pinecone } from '@agentskit/memory' const store = pinecone({ apiKey: process.env.PINECONE_API_KEY!, indexHost: process.env.PINECONE_INDEX_HOST!, namespace: 'prod', }) ``` ## Options | Option | Type | |---|---| | `apiKey` | `string` | | `indexHost` | `string` | | `namespace` | `string` | | `fetch` | `typeof fetch` | ## Related - [qdrant](./qdrant) · [chroma](./chroma) · [upstash-vector](./upstash-vector) --- # qdrant Source: https://www.agentskit.io/docs/data/memory/qdrant > Self-hosted or cloud Qdrant via HTTP. ```ts import { qdrant } from '@agentskit/memory' const store = qdrant({ url: process.env.QDRANT_URL!, apiKey: process.env.QDRANT_API_KEY, collection: 'agentskit', }) ``` ## Options | Option | Type | |---|---| | `url` | `string` | | `apiKey` | `string?` | | `collection` | `string` | | `fetch` | `typeof fetch` | ## Related - [pinecone](./pinecone) · [chroma](./chroma) --- # redisChatMemory Source: https://www.agentskit.io/docs/data/memory/redis-chat > Redis-backed chat memory for multi-instance + serverless. ```ts import { redisChatMemory } from '@agentskit/memory' import { createClient } from 'redis' const client = createClient({ url: process.env.REDIS_URL }) await client.connect() const memory = redisChatMemory({ client, keyPrefix: 'ak:chat:' }) ``` ## Options | Option | Type | Default | |---|---|---| | `client` | `RedisClientAdapter` | required | | `keyPrefix` | `string` | `ak:chat:` | | `ttlSeconds` | `number` | unset | ## Storage layout Each session = one Redis list. Messages pushed as JSON strings. Optional TTL per key. ## Related - [redisVectorMemory](./redis-vector) · [sqliteChatMemory](./sqlite) --- # redisVectorMemory Source: https://www.agentskit.io/docs/data/memory/redis-vector > Redis Stack / Redis 8+ vector index. Metadata filtering + HNSW. ```ts import { redisVectorMemory } from '@agentskit/memory' import { createClient } from 'redis' const client = createClient({ url: process.env.REDIS_URL }) await client.connect() const store = redisVectorMemory({ client, indexName: 'ak-idx', dim: 1536, distance: 'COSINE', }) ``` ## Requirements Redis Stack or Redis 8+ with the Search module. ## Related - [redisChatMemory](./redis-chat) --- # sqliteChatMemory Source: https://www.agentskit.io/docs/data/memory/sqlite > SQLite-backed chat memory. Indexed by session + timestamp. ```ts import { sqliteChatMemory } from '@agentskit/memory' const memory = sqliteChatMemory({ path: '.agentskit/chat.db' }) ``` ## Options | Option | Type | Default | |---|---|---| | `path` | `string` | required | | `table` | `string` | `messages` | | `pragma` | `Record` | WAL | ## Schema `messages(id TEXT PRIMARY KEY, session_id TEXT, role TEXT, parts JSON, created_at INTEGER)` ## Trade-offs - Good for: embedded apps, CLI, single-host servers, Electron. - Uses WAL by default — safe for concurrent readers. ## Related - [fileChatMemory](./file-chat) · [redisChatMemory](./redis-chat) - [Concepts → Memory](/docs/get-started/concepts/memory) --- # upstashVector Source: https://www.agentskit.io/docs/data/memory/upstash-vector > Serverless HTTP vector DB from Upstash. ```ts import { upstashVector } from '@agentskit/memory' const store = upstashVector({ url: process.env.UPSTASH_VECTOR_REST_URL!, token: process.env.UPSTASH_VECTOR_REST_TOKEN!, namespace: 'prod', }) ``` ## Options | Option | Type | |---|---| | `url` | `string` | | `token` | `string` | | `namespace` | `string?` | ## Related - [pinecone](./pinecone) · [qdrant](./qdrant) --- # createVirtualizedMemory Source: https://www.agentskit.io/docs/data/memory/virtualized > Hot-window + cold retriever. Keep recent messages; retrieve old ones on demand. ```ts import { createVirtualizedMemory } from '@agentskit/memory' const memory = createVirtualizedMemory({ window: 20, cold: vectorStore, embed: openaiEmbedder({ apiKey }), }) ``` ## When to use Long-running sessions where most context is stale but some old turns matter. Cheaper than always-on full history. ## Related - [Recipe: virtualized memory](/docs/reference/recipes/virtualized-memory) --- # Providers Source: https://www.agentskit.io/docs/data/providers > 20+ LLM chat + embedder adapters, plus higher-order adapters that compose candidates. ## Hosted chat adapters `anthropic` · `openai` · `gemini` · `grok` · `deepseek` · `kimi` · `mistral` · `cohere` · `together` · `groq` · `fireworks` · `openrouter` · `huggingface` · `langchain` · `langgraph` · `vercelAI` · `generic` ## Local runtimes `ollama` · `lmstudio` · `vllm` · `llamacpp` ## Embedders `openaiEmbedder` · `geminiEmbedder` · `ollamaEmbedder` · `deepseekEmbedder` · `grokEmbedder` · `kimiEmbedder` · `createOpenAICompatibleEmbedder` ## Higher-order adapters - `createRouter` — auto-pick by cost / latency / tags / custom. [Recipe](/docs/reference/recipes/adapter-router). - `createEnsembleAdapter` — fan-out + merge. [Recipe](/docs/reference/recipes/adapter-ensemble). - `createFallbackAdapter` — ordered try-next. [Recipe](/docs/reference/recipes/fallback-chain). *Per-provider pages land in step 4 of the docs IA rollout.* ## Related - [Concepts: Adapter](/docs/get-started/concepts/adapter) - [Package: @agentskit/adapters](/docs/reference/packages/adapters) - [For agents: adapters](/docs/for-agents/adapters) --- # anthropic Source: https://www.agentskit.io/docs/data/providers/anthropic > Anthropic chat adapter — Claude Opus / Sonnet / Haiku. Streaming, tool-calls, vision, long-context. ```ts import { anthropic } from '@agentskit/adapters' const adapter = anthropic({ apiKey: process.env.ANTHROPIC_API_KEY!, model: 'claude-sonnet-4-6', }) ``` ## Options | Option | Type | Default | |---|---|---| | `apiKey` | `string` | required | | `model` | `string` | required | | `baseUrl` | `string` | `https://api.anthropic.com` | | `version` | `string` | `2023-06-01` | | `fetch` | `typeof fetch` | global | ## Model examples `claude-opus-4-7` · `claude-sonnet-4-6` · `claude-haiku-4-5-20251001` · `claude-3-5-haiku` · `claude-3-5-sonnet`. ## Env | Var | Purpose | |---|---| | `ANTHROPIC_API_KEY` | API key | ## Notes - Streaming via SSE; `content_block_delta` events → `text` and `tool_use` chunks. - Vision: pass `{ type: 'image', source: { ... } }` content parts. - Long-context: Sonnet supports 1M-token context when `anthropic-beta: context-1m-2025-08-07` set via `headers`. ## Related - [Providers overview](./) · [AWS Bedrock](https://github.com/AgentsKit-io/agentskit/issues/426) for Claude on AWS --- # cohere Source: https://www.agentskit.io/docs/data/providers/cohere > Cohere Command — enterprise-focused RAG-friendly models with citations. ```ts import { cohere } from '@agentskit/adapters' const adapter = cohere({ apiKey: process.env.COHERE_API_KEY!, model: 'command-r-plus', }) ``` ## Options | Option | Type | Default | |---|---|---| | `apiKey` | `string` | required | | `model` | `string` | required | | `baseUrl` | `string` | `https://api.cohere.com/v2` | | `fetch` | `typeof fetch` | global | ## Model examples `command-r-plus` · `command-r` · `command`. ## Env | Var | Purpose | |---|---| | `COHERE_API_KEY` | API key | ## Notes - Native citations in responses when `documents` are passed — pairs with [RAG](/docs/data/rag). - Cohere also provides [rerankers](/docs/data/rag/rerank). ## Related - [Providers overview](./) · [RAG reranking recipe](/docs/reference/recipes/rag-reranking) --- # deepseek Source: https://www.agentskit.io/docs/data/providers/deepseek > DeepSeek — cost-efficient reasoning + chat models. OpenAI-compatible API. ```ts import { deepseek } from '@agentskit/adapters' const adapter = deepseek({ apiKey: process.env.DEEPSEEK_API_KEY!, model: 'deepseek-chat', }) ``` ## Options | Option | Type | Default | |---|---|---| | `apiKey` | `string` | required | | `model` | `string` | required | | `baseUrl` | `string` | `https://api.deepseek.com` | | `fetch` | `typeof fetch` | global | ## Model examples `deepseek-chat` · `deepseek-reasoner`. ## Env | Var | Purpose | |---|---| | `DEEPSEEK_API_KEY` | API key | ## Notes - Strong price/performance for agents that tool-call heavily. - Reasoner variant exposes chain-of-thought via separate field. ## Related - [Providers overview](./) · [Embedders → deepseek](./deepseek-embedder) --- # deepseekEmbedder Source: https://www.agentskit.io/docs/data/providers/deepseek-embedder > DeepSeek embedding model. Compatible with createOpenAICompatibleEmbedder under the hood. ```ts import { deepseekEmbedder } from '@agentskit/adapters' const embed = deepseekEmbedder({ apiKey: process.env.DEEPSEEK_API_KEY!, }) ``` ## Options | Option | Type | Default | |---|---|---| | `apiKey` | `string` | required | | `model` | `string` | provider default | | `baseUrl` | `string` | `https://api.deepseek.com` | ## Related - [Embedders overview](./embedders) · [deepseek](./deepseek) --- # Embedders Source: https://www.agentskit.io/docs/data/providers/embedders > Turn text into vectors. Used by RAG + vector memory. | Embedder | Import | Model examples | |---|---|---| | [OpenAI](./openai-embedder) | `openaiEmbedder` | `text-embedding-3-small`, `...-large` | | [Gemini](./gemini-embedder) | `geminiEmbedder` | `text-embedding-004` | | [Ollama](./ollama-embedder) | `ollamaEmbedder` | `nomic-embed-text`, `mxbai-embed-large` | | [DeepSeek](./deepseek-embedder) | `deepseekEmbedder` | provider defaults | | [Grok](./grok-embedder) | `grokEmbedder` | provider defaults | | [Kimi](./kimi-embedder) | `kimiEmbedder` | provider defaults | | [OpenAI-compatible](./openai-compatible-embedder) | `createOpenAICompatibleEmbedder` | any `/v1/embeddings` endpoint | ## Contract ```ts type Embedder = { embed: (texts: string[]) => Promise dim: number } ``` ## Usage ```ts import { openaiEmbedder } from '@agentskit/adapters' const embed = openaiEmbedder({ apiKey: process.env.OPENAI_API_KEY!, model: 'text-embedding-3-small', }) ``` ## Related - [RAG](/docs/data/rag) · [Memory backends](/docs/data/memory) --- # fireworks Source: https://www.agentskit.io/docs/data/providers/fireworks > Fireworks AI — fast open-model inference with fine-tuning + function-calling support. ```ts import { fireworks } from '@agentskit/adapters' const adapter = fireworks({ apiKey: process.env.FIREWORKS_API_KEY!, model: 'accounts/fireworks/models/llama-v3p3-70b-instruct', }) ``` ## Options | Option | Type | Default | |---|---|---| | `apiKey` | `string` | required | | `model` | `string` | required | | `baseUrl` | `string` | `https://api.fireworks.ai/inference/v1` | | `fetch` | `typeof fetch` | global | ## Env | Var | Purpose | |---|---| | `FIREWORKS_API_KEY` | API key | ## Related - [Providers overview](./) · [together](./together) --- # gemini Source: https://www.agentskit.io/docs/data/providers/gemini > Google Gemini chat adapter — Gemini 2.5 Pro / Flash. Streaming, tool-calls, vision, 1M+ context. ```ts import { gemini } from '@agentskit/adapters' const adapter = gemini({ apiKey: process.env.GOOGLE_API_KEY!, model: 'gemini-2.5-flash', }) ``` ## Options | Option | Type | Default | |---|---|---| | `apiKey` | `string` | required | | `model` | `string` | required | | `baseUrl` | `string` | `https://generativelanguage.googleapis.com` | | `apiVersion` | `string` | `v1beta` | | `fetch` | `typeof fetch` | global | ## Model examples `gemini-2.5-pro` · `gemini-2.5-flash` · `gemini-2.5-flash-8b` · `gemini-2.0-flash-exp`. ## Env | Var | Purpose | |---|---| | `GOOGLE_API_KEY` | API key from aistudio.google.com | ## Notes - Streaming via SSE `streamGenerateContent`. - Multimodal: inline image data or URI parts. - For Vertex AI (IAM + GCP billing) use the forthcoming [vertexAdapter](https://github.com/AgentsKit-io/agentskit/issues/427). ## Related - [Providers overview](./) · [Embedders → gemini](./gemini-embedder) --- # geminiEmbedder Source: https://www.agentskit.io/docs/data/providers/gemini-embedder > Google Gemini embeddings — text-embedding-004. Strong multilingual. ```ts import { geminiEmbedder } from '@agentskit/adapters' const embed = geminiEmbedder({ apiKey: process.env.GOOGLE_API_KEY!, model: 'text-embedding-004', }) ``` ## Options | Option | Type | Default | |---|---|---| | `apiKey` | `string` | required | | `model` | `string` | `text-embedding-004` | | `baseUrl` | `string` | `https://generativelanguage.googleapis.com` | ## Models - `text-embedding-004` — 768 dims, multilingual. ## Related - [Embedders overview](./embedders) --- # generic Source: https://www.agentskit.io/docs/data/providers/generic > Turn any `ReadableStream` into an AgentsKit adapter — the lowest-level escape hatch. ```ts import { generic } from '@agentskit/adapters' const adapter = generic({ stream: async ({ messages }) => { const res = await fetch('https://my-llm.example.com/v1/chat', { method: 'POST', body: JSON.stringify({ messages }), }) return res.body! // ReadableStream }, parseChunk: (bytes) => { // Convert provider bytes to { type: 'text'|'tool-call'|'done', ... } return parseMyProviderSse(bytes) }, }) ``` ## When to reach for it - Provider has no off-the-shelf adapter. - Internal LLM gateway with custom protocol. - Proxying through your own backend that already reshapes chunks. ## Related - [Providers overview](./) · Recipe: [custom-adapter](/docs/reference/recipes/custom-adapter) --- # grok Source: https://www.agentskit.io/docs/data/providers/grok > xAI Grok — streaming chat + tool calls. OpenAI-compatible API surface. ```ts import { grok } from '@agentskit/adapters' const adapter = grok({ apiKey: process.env.XAI_API_KEY!, model: 'grok-4', }) ``` ## Options | Option | Type | Default | |---|---|---| | `apiKey` | `string` | required | | `model` | `string` | required | | `baseUrl` | `string` | `https://api.x.ai/v1` | | `fetch` | `typeof fetch` | global | ## Model examples `grok-4` · `grok-code-fast-1` · `grok-4-fast`. ## Env | Var | Purpose | |---|---| | `XAI_API_KEY` | API key from x.ai | ## Notes - OpenAI-compatible `/chat/completions` endpoint — works with many OpenAI SDK idioms. - Streaming via SSE. ## Related - [Providers overview](./) · [Embedders → grok](./grok-embedder) --- # grokEmbedder Source: https://www.agentskit.io/docs/data/providers/grok-embedder > xAI embedding model. ```ts import { grokEmbedder } from '@agentskit/adapters' const embed = grokEmbedder({ apiKey: process.env.XAI_API_KEY!, }) ``` ## Options | Option | Type | Default | |---|---|---| | `apiKey` | `string` | required | | `model` | `string` | provider default | | `baseUrl` | `string` | `https://api.x.ai/v1` | ## Related - [Embedders overview](./embedders) · [grok](./grok) --- # groq Source: https://www.agentskit.io/docs/data/providers/groq > Groq — ultra-low-latency inference on custom LPU hardware. Llama + Mixtral + Gemma. ```ts import { groq } from '@agentskit/adapters' const adapter = groq({ apiKey: process.env.GROQ_API_KEY!, model: 'llama-3.3-70b-versatile', }) ``` ## Options | Option | Type | Default | |---|---|---| | `apiKey` | `string` | required | | `model` | `string` | required | | `baseUrl` | `string` | `https://api.groq.com/openai/v1` | | `fetch` | `typeof fetch` | global | ## Why groq - Sub-100 ms first-token latency — best for realtime voice + chat. - OpenAI-compatible. ## Env | Var | Purpose | |---|---| | `GROQ_API_KEY` | API key | ## Related - [Providers overview](./) · [voice mode component (issue #479)](https://github.com/AgentsKit-io/agentskit/issues/479) --- # Higher-order adapters Source: https://www.agentskit.io/docs/data/providers/higher-order > Compose adapters — route, ensemble, fallback. ## createRouter Pick an adapter per request by cost, latency, tags, or custom policy. ```ts import { createRouter, openai, anthropic } from '@agentskit/adapters' const adapter = createRouter({ candidates: { fast: openai(...), smart: anthropic(...) }, route: (req) => (req.tags?.includes('code') ? 'smart' : 'fast'), }) ``` [Recipe](/docs/reference/recipes/adapter-router). ## createEnsembleAdapter Fan out to N candidates, merge per strategy (first, majority, custom). ```ts import { createEnsembleAdapter } from '@agentskit/adapters' const adapter = createEnsembleAdapter({ candidates: [openai(...), anthropic(...), gemini(...)], strategy: 'first-success', }) ``` [Recipe](/docs/reference/recipes/adapter-ensemble). ## createFallbackAdapter Try candidates in order until one succeeds. ```ts import { createFallbackAdapter } from '@agentskit/adapters' const adapter = createFallbackAdapter([primary, secondary, tertiary]) ``` [Recipe](/docs/reference/recipes/fallback-chain). ## Related - [Hosted](./hosted) · [Local](./local) --- # Hosted chat adapters Source: https://www.agentskit.io/docs/data/providers/hosted > 17 managed-LLM adapters. Same contract; swap by changing one import. All return `Adapter` — call `.complete()` or `.stream()`. ## Adapters | Adapter | Import | Env | |---|---|---| | [OpenAI](./openai) | `openai` | `OPENAI_API_KEY` | | [Anthropic](./anthropic) | `anthropic` | `ANTHROPIC_API_KEY` | | [Google Gemini](./gemini) | `gemini` | `GOOGLE_API_KEY` | | [xAI Grok](./grok) | `grok` | `XAI_API_KEY` | | [DeepSeek](./deepseek) | `deepseek` | `DEEPSEEK_API_KEY` | | [Kimi (Moonshot)](./kimi) | `kimi` | `KIMI_API_KEY` | | [Mistral](./mistral) | `mistral` | `MISTRAL_API_KEY` | | [Cohere](./cohere) | `cohere` | `COHERE_API_KEY` | | [Together](./together) | `together` | `TOGETHER_API_KEY` | | [Groq](./groq) | `groq` | `GROQ_API_KEY` | | [Fireworks](./fireworks) | `fireworks` | `FIREWORKS_API_KEY` | | [OpenRouter](./openrouter) | `openrouter` | `OPENROUTER_API_KEY` | | [Hugging Face](./huggingface) | `huggingface` | `HF_TOKEN` | | [LangChain](./langchain) | `langchain` | — | | [LangGraph](./langgraph) | `langgraph` | — | | [Vercel AI SDK](./vercel-ai) | `vercelAI` | — | | [Generic](./generic) | `generic` | BYO `ReadableStream` | ## Usage ```ts import { openai } from '@agentskit/adapters' const adapter = openai({ apiKey: process.env.OPENAI_API_KEY!, model: 'gpt-4o' }) ``` ## Related - [Local runtimes](./local) · [Embedders](./embedders) · [Higher-order](./higher-order) - [Recipe: more providers](/docs/reference/recipes/more-providers) --- # huggingface Source: https://www.agentskit.io/docs/data/providers/huggingface > Hugging Face Inference Endpoints + Serverless — run any HF-hosted chat model. ```ts import { huggingface } from '@agentskit/adapters' const adapter = huggingface({ apiKey: process.env.HF_TOKEN!, model: 'meta-llama/Meta-Llama-3-70B-Instruct', }) ``` ## Options | Option | Type | Default | |---|---|---| | `apiKey` | `string` | required | | `model` | `string` | required | | `baseUrl` | `string` | `https://api-inference.huggingface.co` | | `fetch` | `typeof fetch` | global | ## Env | Var | Purpose | |---|---| | `HF_TOKEN` | Read token from hf.co/settings/tokens | ## Notes - Serverless tier has cold starts. Pin a dedicated Inference Endpoint for production latency. - For open weights locally see [ollama](./ollama) · [vllm](./vllm) · [llamacpp](./llamacpp). ## Related - [Providers overview](./) --- # kimi Source: https://www.agentskit.io/docs/data/providers/kimi > Moonshot AI Kimi — long-context, OpenAI-compatible. ```ts import { kimi } from '@agentskit/adapters' const adapter = kimi({ apiKey: process.env.KIMI_API_KEY!, model: 'moonshot-v1-128k', }) ``` ## Options | Option | Type | Default | |---|---|---| | `apiKey` | `string` | required | | `model` | `string` | required | | `baseUrl` | `string` | `https://api.moonshot.cn/v1` | | `fetch` | `typeof fetch` | global | ## Model examples `moonshot-v1-8k` · `moonshot-v1-32k` · `moonshot-v1-128k` · `kimi-k2-0905`. ## Env | Var | Purpose | |---|---| | `KIMI_API_KEY` | API key | ## Notes - Popular in APAC. OpenAI-compatible surface. - 128k-context variants for long-doc workflows. ## Related - [Providers overview](./) · [Embedders → kimi](./kimi-embedder) --- # kimiEmbedder Source: https://www.agentskit.io/docs/data/providers/kimi-embedder > Moonshot Kimi embedding model. ```ts import { kimiEmbedder } from '@agentskit/adapters' const embed = kimiEmbedder({ apiKey: process.env.KIMI_API_KEY!, }) ``` ## Options | Option | Type | Default | |---|---|---| | `apiKey` | `string` | required | | `model` | `string` | provider default | | `baseUrl` | `string` | `https://api.moonshot.cn/v1` | ## Related - [Embedders overview](./embedders) · [kimi](./kimi) --- # langchain Source: https://www.agentskit.io/docs/data/providers/langchain > Wrap any LangChain.js `ChatModel` as an AgentsKit adapter. ```ts import { langchain } from '@agentskit/adapters' import { ChatAnthropic } from '@langchain/anthropic' const adapter = langchain({ model: new ChatAnthropic({ model: 'claude-sonnet-4-6' }), }) ``` ## Options | Option | Type | Default | |---|---|---| | `model` | `ChatModel` | required | ## Why langchain - Reuse LangChain model instances in AgentsKit runtimes. - Migrate incrementally — swap one layer at a time. ## Related - [Providers overview](./) · [langgraph](./langgraph) · [vercelAI](./vercel-ai) · [Migrating → LangChain.js](/docs/get-started/migrating/from-langchain) --- # langgraph Source: https://www.agentskit.io/docs/data/providers/langgraph > Wrap a LangGraph graph as an AgentsKit adapter — streamEvents surfaced as chunks. ```ts import { langgraph } from '@agentskit/adapters' import { graph } from './my-langgraph-graph' const adapter = langgraph({ graph }) ``` ## Options | Option | Type | |---|---| | `graph` | LangGraph-compiled graph | | `streamMode` | `'values' \| 'updates' \| 'messages'` | ## Why langgraph - Use existing LangGraph orchestration inside AgentsKit UI/runtime. - Surface `streamEvents` as standard adapter chunks. ## Related - [Providers overview](./) · [langchain](./langchain) --- # llamacpp Source: https://www.agentskit.io/docs/data/providers/llamacpp > llama.cpp server — run GGUF models on CPU or GPU with minimal overhead. ```ts import { llamacpp } from '@agentskit/adapters' const adapter = llamacpp({ url: 'http://localhost:8080', }) ``` ## Options | Option | Type | Default | |---|---|---| | `url` | `string` | `http://localhost:8080` | | `fetch` | `typeof fetch` | global | ## Why llamacpp - Runs everywhere, including Raspberry Pi + embedded. - GGUF quantizations from 4-bit to 16-bit. ## Related - [Providers overview](./) · [ollama](./ollama) · [vllm](./vllm) --- # lmstudio Source: https://www.agentskit.io/docs/data/providers/lmstudio > LM Studio — desktop app that exposes an OpenAI-compatible server over local models. ```ts import { lmstudio } from '@agentskit/adapters' const adapter = lmstudio({ model: 'lmstudio-community/Llama-3.3-70B-Instruct-GGUF', url: 'http://localhost:1234/v1', }) ``` ## Options | Option | Type | Default | |---|---|---| | `model` | `string` | required | | `url` | `string` | `http://localhost:1234/v1` | | `apiKey` | `string` | — (optional if enabled) | | `fetch` | `typeof fetch` | global | ## Notes - GUI-first: download models via LM Studio app, then hit the local server. - OpenAI-compatible; most tooling works unchanged. ## Related - [Providers overview](./) · [ollama](./ollama) · [vllm](./vllm) --- # Local runtimes Source: https://www.agentskit.io/docs/data/providers/local > Run fully offline — Ollama, LM Studio, vLLM, llama.cpp. | Adapter | Import | Default URL | |---|---|---| | [Ollama](./ollama) | `ollama` | `http://localhost:11434` | | [LM Studio](./lmstudio) | `lmstudio` | `http://localhost:1234/v1` | | [vLLM](./vllm) | `vllm` | `http://localhost:8000/v1` | | [llama.cpp](./llamacpp) | `llamacpp` | `http://localhost:8080` | ## Usage ```ts import { ollama } from '@agentskit/adapters' const adapter = ollama({ model: 'llama3.2', url: 'http://localhost:11434' }) ``` ## Related - [Hosted](./hosted) · [Embedders](./embedders) --- # mistral Source: https://www.agentskit.io/docs/data/providers/mistral > Mistral — open-weights-friendly family including Mistral Large, Codestral, Mixtral. ```ts import { mistral } from '@agentskit/adapters' const adapter = mistral({ apiKey: process.env.MISTRAL_API_KEY!, model: 'mistral-large-latest', }) ``` ## Options | Option | Type | Default | |---|---|---| | `apiKey` | `string` | required | | `model` | `string` | required | | `baseUrl` | `string` | `https://api.mistral.ai/v1` | | `fetch` | `typeof fetch` | global | ## Model examples `mistral-large-latest` · `mistral-small-latest` · `codestral-latest` · `mixtral-8x22b`. ## Env | Var | Purpose | |---|---| | `MISTRAL_API_KEY` | API key | ## Notes - Codestral is code-specialized; pair with [coder](/docs/agents/skills/coder) skill. - Open-weights variants available — see [llama.cpp](./llamacpp) for self-hosted. ## Related - [Providers overview](./) --- # ollama Source: https://www.agentskit.io/docs/data/providers/ollama > Ollama — local LLMs on your laptop. Zero-cost, offline, private. ```ts import { ollama } from '@agentskit/adapters' const adapter = ollama({ model: 'llama3.2', url: 'http://localhost:11434', }) ``` ## Options | Option | Type | Default | |---|---|---| | `model` | `string` | required | | `url` | `string` | `http://localhost:11434` | | `fetch` | `typeof fetch` | global | ## Why ollama - Zero config, zero cost, offline. - Supports tool calling for models that implement it (Llama 3.1+, Qwen 2.5+). - Install: `curl -fsSL https://ollama.com/install.sh | sh`. ## Notes - Speed proportional to local GPU/Apple-Silicon throughput. - For team shared servers pair with [`createRouter`](./higher-order) to fail-over to a hosted adapter on load. ## Related - [Providers overview](./) · [Local runtimes](./local) · [Embedders → ollama](./ollama-embedder) --- # ollamaEmbedder Source: https://www.agentskit.io/docs/data/providers/ollama-embedder > Ollama-hosted embedding models — nomic-embed-text, mxbai-embed-large, all-minilm. ```ts import { ollamaEmbedder } from '@agentskit/adapters' const embed = ollamaEmbedder({ model: 'nomic-embed-text', url: 'http://localhost:11434', }) ``` ## Options | Option | Type | Default | |---|---|---| | `model` | `string` | required | | `url` | `string` | `http://localhost:11434` | ## Recommended models - `nomic-embed-text` — 768 dims, fast. - `mxbai-embed-large` — 1024 dims, higher quality. - `all-minilm` — 384 dims, tiny + fast. ## Related - [Embedders overview](./embedders) · [ollama](./ollama) --- # openai Source: https://www.agentskit.io/docs/data/providers/openai > OpenAI chat adapter — GPT-4o, o-series, GPT-5. Streaming, tool-calls, parallel tools, multimodal. ```ts import { openai } from '@agentskit/adapters' const adapter = openai({ apiKey: process.env.OPENAI_API_KEY!, model: 'gpt-4o', }) ``` ## Options | Option | Type | Default | |---|---|---| | `apiKey` | `string` | required | | `model` | `string` | required | | `baseUrl` | `string` | `https://api.openai.com/v1` | | `organization` | `string` | — | | `project` | `string` | — | | `fetch` | `typeof fetch` | global | ## Model examples `gpt-4o` · `gpt-4o-mini` · `gpt-5` · `o1` · `o3-mini` · `gpt-4.1`. ## Env | Var | Purpose | |---|---| | `OPENAI_API_KEY` | API key (standard or session key) | ## Notes - Streaming via SSE; emits `text` + `tool-call` + `done` chunks. - Parallel tool calling enabled by default. - Multimodal: pass `{ type: 'image', url }` content parts. - Azure OpenAI users should use [azureOpenAIAdapter](https://github.com/AgentsKit-io/agentskit/issues/428) — different routing. ## Related - [Providers overview](./) · [Hosted](./hosted) · [Embedders](./embedders) — `openaiEmbedder` - Recipe: [custom-adapter](/docs/reference/recipes/custom-adapter) --- # createOpenAICompatibleEmbedder Source: https://www.agentskit.io/docs/data/providers/openai-compatible-embedder > Escape hatch for any OpenAI-compatible `/v1/embeddings` endpoint — LMS, vLLM, Cerebras, Voyage, Jina, internal gateways. ```ts import { createOpenAICompatibleEmbedder } from '@agentskit/adapters' const embed = createOpenAICompatibleEmbedder({ apiKey: process.env.VOYAGE_API_KEY!, baseUrl: 'https://api.voyageai.com/v1', model: 'voyage-3-large', }) ``` ## Options | Option | Type | Default | |---|---|---| | `apiKey` | `string` | required (unless the endpoint is keyless) | | `baseUrl` | `string` | required | | `model` | `string` | required | | `dimensions` | `number` | — | | `fetch` | `typeof fetch` | global | ## Use cases - Voyage, Jina, Cerebras, Cohere (via compat), Mistral embed, any private gateway. - Local servers (vLLM, LM Studio, llama.cpp) that expose `/v1/embeddings`. ## Related - [Embedders overview](./embedders) · Issue #466 — [voyage + jina rerankers](https://github.com/AgentsKit-io/agentskit/issues/466) --- # openaiEmbedder Source: https://www.agentskit.io/docs/data/providers/openai-embedder > OpenAI text embeddings — text-embedding-3-small / -large. Default choice for most RAG stacks. ```ts import { openaiEmbedder } from '@agentskit/adapters' const embed = openaiEmbedder({ apiKey: process.env.OPENAI_API_KEY!, model: 'text-embedding-3-small', }) ``` ## Options | Option | Type | Default | |---|---|---| | `apiKey` | `string` | required | | `model` | `string` | `text-embedding-3-small` | | `dimensions` | `number` | model default | | `baseUrl` | `string` | `https://api.openai.com/v1` | | `fetch` | `typeof fetch` | global | ## Models - `text-embedding-3-small` — 1536 dims, fast + cheap. - `text-embedding-3-large` — 3072 dims, best quality. ## Env | Var | Purpose | |---|---| | `OPENAI_API_KEY` | API key | ## Related - [Embedders overview](./embedders) · [createRAG](/docs/data/rag/create-rag) --- # openrouter Source: https://www.agentskit.io/docs/data/providers/openrouter > OpenRouter — one API to route across 100+ providers with transparent pricing. ```ts import { openrouter } from '@agentskit/adapters' const adapter = openrouter({ apiKey: process.env.OPENROUTER_API_KEY!, model: 'anthropic/claude-sonnet-4', appUrl: 'https://www.agentskit.io', appName: 'AgentsKit', }) ``` ## Options | Option | Type | Default | |---|---|---| | `apiKey` | `string` | required | | `model` | `string` | required | | `baseUrl` | `string` | `https://openrouter.ai/api/v1` | | `appUrl` | `string` | — (optional attribution) | | `appName` | `string` | — | | `fetch` | `typeof fetch` | global | ## Why openrouter - Single key, many providers. Good for experimentation. - Transparent per-model pricing; automatic fallbacks. - Pair with [`createFallbackAdapter`](./higher-order) for extra resilience. ## Env | Var | Purpose | |---|---| | `OPENROUTER_API_KEY` | API key | ## Related - [Providers overview](./) · [higher-order adapters](./higher-order) --- # together Source: https://www.agentskit.io/docs/data/providers/together > Together AI — hosts 200+ open models behind one OpenAI-compatible API. ```ts import { together } from '@agentskit/adapters' const adapter = together({ apiKey: process.env.TOGETHER_API_KEY!, model: 'meta-llama/Llama-3.3-70B-Instruct-Turbo', }) ``` ## Options | Option | Type | Default | |---|---|---| | `apiKey` | `string` | required | | `model` | `string` | required | | `baseUrl` | `string` | `https://api.together.xyz/v1` | | `fetch` | `typeof fetch` | global | ## Why together - Cheapest hosted path to Llama / Qwen / DeepSeek / Mixtral. - OpenAI-compatible — swap from openai in one line. - Good for price-sensitive batch workloads. ## Env | Var | Purpose | |---|---| | `TOGETHER_API_KEY` | API key | ## Related - [Providers overview](./) · [groq](./groq) · [fireworks](./fireworks) · [openrouter](./openrouter) --- # vercelAI Source: https://www.agentskit.io/docs/data/providers/vercel-ai > Wrap a Vercel AI SDK `LanguageModel` as an AgentsKit adapter. ```ts import { vercelAI } from '@agentskit/adapters' import { openai as vercelOpenAI } from '@ai-sdk/openai' const adapter = vercelAI({ model: vercelOpenAI('gpt-4o'), }) ``` ## Options | Option | Type | |---|---| | `model` | `LanguageModel` (from `ai` SDK) | ## Why vercelAI - Migrate from Vercel AI SDK incrementally. - Keep your existing `@ai-sdk/*` provider choices. ## Related - [Providers overview](./) · [Migrating → Vercel AI SDK](/docs/get-started/migrating/from-vercel-ai-sdk) --- # vllm Source: https://www.agentskit.io/docs/data/providers/vllm > vLLM — high-throughput self-hosted inference with OpenAI-compatible API. For production workloads on your own GPUs. ```ts import { vllm } from '@agentskit/adapters' const adapter = vllm({ model: 'meta-llama/Llama-3.3-70B-Instruct', url: 'http://localhost:8000/v1', }) ``` ## Options | Option | Type | Default | |---|---|---| | `model` | `string` | required | | `url` | `string` | `http://localhost:8000/v1` | | `fetch` | `typeof fetch` | global | ## Why vllm - PagedAttention + continuous batching → best-in-class throughput. - OpenAI-compatible; cluster-friendly. ## Related - [Providers overview](./) · [ollama](./ollama) · [llamacpp](./llamacpp) --- # RAG Source: https://www.agentskit.io/docs/data/rag > Plug-and-play retrieval. Chunk, embed, search, rerank, loaders. ## Core pipeline - `createRAG({ embed, store })` — one-liner ingest + retrieve + search. - `chunkText({ chunkSize, chunkOverlap, split })` — standalone splitter. ## Reranking + hybrid - `createRerankedRetriever` — plug in Cohere Rerank, BGE, or the built-in BM25. - `createHybridRetriever` — vector + BM25 blend with weighted normalization. - [Recipe: RAG reranking](/docs/reference/recipes/rag-reranking). ## Document loaders - `loadUrl`, `loadGitHubFile`, `loadGitHubTree`, `loadNotionPage`, `loadConfluencePage`, `loadGoogleDriveFile`, `loadPdf` (BYO parser). - [Recipe: Document loaders](/docs/reference/recipes/doc-loaders). *Per-loader + per-reranker deep dives land in step 4 of the docs IA rollout.* ## Related - [Concepts: Retriever](/docs/get-started/concepts/retriever) - [Package: @agentskit/rag](/docs/reference/packages/rag) - [For agents: rag](/docs/for-agents/rag) --- # Chunking Source: https://www.agentskit.io/docs/data/rag/chunking > Split docs before embedding. Sensible defaults; override per doc type. ```ts import { chunkText } from '@agentskit/rag' const chunks = chunkText(longDoc, { chunkSize: 800, chunkOverlap: 120, split: 'paragraph', }) ``` ## Options | Option | Type | Default | |---|---|---| | `chunkSize` | `number` | `1000` | | `chunkOverlap` | `number` | `100` | | `split` | `'sentence' \| 'paragraph' \| 'markdown' \| 'code' \| 'char'` | `paragraph` | ## Rules of thumb - Prose: `paragraph`, 800/120. - Markdown: `markdown`, 1200/150. - Code: `code`, 1500/0. ## Related - [RAG overview](./) · [createRAG](./create-rag) --- # createRAG Source: https://www.agentskit.io/docs/data/rag/create-rag > One-liner RAG pipeline — chunk, embed, store, retrieve, search. ```ts import { createRAG } from '@agentskit/rag' import { openaiEmbedder } from '@agentskit/adapters' import { fileVectorMemory } from '@agentskit/memory' const rag = createRAG({ embed: openaiEmbedder({ apiKey: process.env.OPENAI_API_KEY! }), store: fileVectorMemory({ path: '.agentskit/vec.json', dim: 1536 }), }) await rag.ingest([{ id: 'doc-1', text: longDoc }]) const hits = await rag.retrieve('How does token budgeting work?') ``` ## API | Method | Purpose | |---|---| | `ingest(docs, opts?)` | chunk + embed + store | | `retrieve(query, opts?)` | vector top-k | | `search(query, opts?)` | alias for retrieve with filters | | `delete(ids)` | remove docs | ## Options - `chunkSize` / `chunkOverlap` / `split` — passed to [chunking](./chunking). - `topK` — default retrieve count. ## Related - [Rerank](./rerank) · [Hybrid](./hybrid) · [Loaders](./loaders) --- # Hybrid search Source: https://www.agentskit.io/docs/data/rag/hybrid > Vector + BM25 blend with weighted normalization. ```ts import { createHybridRetriever } from '@agentskit/rag' const retriever = createHybridRetriever({ vector: rag, lexical: bm25Index, weights: { vector: 0.7, lexical: 0.3 }, }) ``` ## When to use Queries with rare keywords or exact tokens (error codes, SKUs, identifiers) that vector alone misses. ## Related - [Rerank](./rerank) · [createRAG](./create-rag) --- # Document loaders Source: https://www.agentskit.io/docs/data/rag/loaders > Fetch + normalize documents from URLs, GitHub, Notion, Confluence, Google Drive, PDFs. All loaders return `LoadedDocument[]` ready for `rag.ingest`. ## URL ```ts import { loadUrl } from '@agentskit/rag' const docs = await loadUrl('https://example.com/post') ``` Strips boilerplate; keeps main content + title. ## GitHub ```ts import { loadGitHubFile, loadGitHubTree } from '@agentskit/rag' const single = await loadGitHubFile({ owner, repo, path: 'README.md', ref: 'main' }) const tree = await loadGitHubTree({ owner, repo, ref: 'main', include: ['**/*.md'] }) ``` Requires `GITHUB_TOKEN` for private repos. ## Notion ```ts import { loadNotionPage } from '@agentskit/rag' const docs = await loadNotionPage({ token: process.env.NOTION_TOKEN!, pageId }) ``` ## Confluence ```ts import { loadConfluencePage } from '@agentskit/rag' const docs = await loadConfluencePage({ baseUrl, auth, pageId }) ``` ## Google Drive ```ts import { loadGoogleDriveFile } from '@agentskit/rag' const docs = await loadGoogleDriveFile({ fileId, accessToken }) ``` ## PDF ```ts import { loadPdf } from '@agentskit/rag' const docs = await loadPdf({ buffer, parse: myPdfParse }) ``` BYO parser (e.g. `pdf-parse`, `unpdf`) to keep core deps zero. ## Related - [createRAG](./create-rag) · [Recipe: doc loaders](/docs/reference/recipes/doc-loaders) --- # Rerank Source: https://www.agentskit.io/docs/data/rag/rerank > Post-retrieval reranker. Cohere / BGE / built-in BM25. ```ts import { createRerankedRetriever, cohereReranker } from '@agentskit/rag' const retriever = createRerankedRetriever({ retriever: rag, rerank: cohereReranker({ apiKey: process.env.COHERE_API_KEY! }), topN: 5, }) ``` ## Rerankers - `cohereReranker({ apiKey, model? })` - `bgeReranker({ url, model? })` — self-hosted. - `bm25Reranker()` — zero-dep fallback. ## Related - [Hybrid](./hybrid) · [Recipe: RAG reranking](/docs/reference/recipes/rag-reranking) --- # For agents — overview Source: https://www.agentskit.io/docs/for-agents > Dense, LLM-friendly reference for every AgentsKit package. Designed to paste into an agent's context window. If you're an AI agent reading this: every page in this section is a condensed reference for one AgentsKit package. Each page follows the same structure so you can skim quickly and quote accurately: 1. **Purpose** — one sentence. 2. **Install** — exact npm command. 3. **Primary exports** — name + one-line summary. 4. **Minimal example** — copy-pastable, always working. 5. **Common patterns** — 2–4 bullets with cross-links. 6. **Related packages** — what to read next. 7. **Source** — link to the package code + README. Human-facing docs (with narrative, screenshots, and storytelling) live under [/docs/concepts](/docs/get-started/concepts), [/docs/recipes](/docs/reference/recipes), and [/docs/examples](/docs/reference/examples). ## Packages covered ### Core - [@agentskit/core](/docs/for-agents/core) — contracts, controller, primitives. ### Adapters + runtime - [@agentskit/adapters](/docs/for-agents/adapters) — provider adapters + router + ensemble + fallback. - [@agentskit/runtime](/docs/for-agents/runtime) — ReAct loop + durable execution + topologies + speculate + background agents. ### UI bindings (same ChatReturn contract) - [@agentskit/react](/docs/for-agents/react) - [@agentskit/ink](/docs/for-agents/ink) (terminal) - [@agentskit/vue](/docs/for-agents/vue) - [@agentskit/svelte](/docs/for-agents/svelte) - [@agentskit/solid](/docs/for-agents/solid) - [@agentskit/react-native](/docs/for-agents/react-native) - [@agentskit/angular](/docs/for-agents/angular) ### Capabilities - [@agentskit/tools](/docs/for-agents/tools) — built-in + integrations + MCP bridge. - [@agentskit/memory](/docs/for-agents/memory) — chat + vector + graph + encrypted. - [@agentskit/rag](/docs/for-agents/rag) — ingestion + retrieval + reranking + loaders. - [@agentskit/skills](/docs/for-agents/skills) — personas + marketplace. ### Observability + evaluation - [@agentskit/observability](/docs/for-agents/observability) — trace viewer + audit log + devtools. - [@agentskit/eval](/docs/for-agents/eval) — suites + replay + snapshots + diff. ### Infrastructure - [@agentskit/sandbox](/docs/for-agents/sandbox) — secure code execution + policy. - [@agentskit/cli](/docs/for-agents/cli) — init / chat / run / doctor / dev / ai. ## How to use this section - **Quoting**: prefer these pages over README text — pages are version-synchronized with the code. - **Linking**: every page ends with "Related packages" + "Source" for pivoting. - **Subpaths**: most packages expose subpath exports (e.g. `@agentskit/core/security`). They're always listed under "Primary exports". --- # @agentskit/adapters — for agents Source: https://www.agentskit.io/docs/for-agents/adapters > Provider adapters (OpenAI-compatible + native) + router + ensemble + fallback + generic factory. ## Purpose Every LLM provider, one contract. Stream, tool calls, retry, abort all normalized. Plus higher-order adapters that compose candidates (router, ensemble, fallback) and a `createAdapter` factory for custom providers. ## Install ```bash npm install @agentskit/adapters ``` ## Primary exports ### Native adapters - `anthropic`, `openai`, `gemini`, `grok`, `ollama`, `deepseek`, `kimi`, `langchain`, `langgraph`, `vercelAI`, `generic`. - `createAdapter({ send, parse, abort })` — build your own. See [Custom adapter recipe](/docs/reference/recipes/custom-adapter). ### OpenAI-compatible providers `mistral`, `cohere`, `together`, `groq`, `fireworks`, `openrouter`, `huggingface`, `lmstudio`, `vllm`, `llamacpp`. All share the `createOpenAICompatibleAdapter` base; each exposes a default `baseUrl` and accepts an override. ### Composition - `createRouter({ candidates, policy, classify, onRoute })` — pick one per request by cost / latency / tags / custom. See [Adapter router](/docs/reference/recipes/adapter-router). - `createEnsembleAdapter({ candidates, aggregate })` — fan-out + merge (majority-vote / concat / longest / fn). See [Ensemble](/docs/reference/recipes/adapter-ensemble). - `createFallbackAdapter([candidates], { shouldRetry, onFallback })` — try in order, fall through on open / first-chunk / zero-chunk failures. See [Fallback chain](/docs/reference/recipes/fallback-chain). ### Testing + utilities - `mockAdapter`, `recordingAdapter`, `replayAdapter`, `inMemorySink` — ship without network. - `simulateStream`, `chunkText`, `fetchWithRetry` — lower-level helpers. ### Embedders - `openaiEmbedder`, `geminiEmbedder`, `ollamaEmbedder`, `deepseekEmbedder`, `grokEmbedder`, `kimiEmbedder`, `createOpenAICompatibleEmbedder`. ## Minimal example ```ts import { openai } from '@agentskit/adapters' const adapter = openai({ apiKey: process.env.OPENAI_API_KEY!, model: 'gpt-4o-mini' }) ``` ## Common patterns - Rank candidates by cost and fall back on errors: compose `createRouter` with `createFallbackAdapter`. - A/B providers without users: [`speculate`](/docs/reference/recipes/speculative-execution) or [`replayAgainst`](/docs/reference/recipes/replay-different-model). - Test without keys: pair `recordingAdapter` + `replayAdapter` ([deterministic replay](/docs/reference/recipes/deterministic-replay)). ## Related packages - [@agentskit/core](/docs/for-agents/core) — the `AdapterFactory` contract lives here. - [@agentskit/runtime](/docs/for-agents/runtime) - [@agentskit/eval](/docs/for-agents/eval) ## Source - npm: https://www.npmjs.com/package/@agentskit/adapters - repo: https://github.com/AgentsKit-io/agentskit/tree/main/packages/adapters --- # @agentskit/angular — for agents Source: https://www.agentskit.io/docs/for-agents/angular > Angular service exposing chat state as Signal + RxJS Observable. ## Install ```bash npm install @agentskit/angular # peers: npm install @angular/core rxjs ``` ## Primary exports - `AgentskitChat` — `@Injectable({ providedIn: 'root' })` service with: - `init(config): ChatReturn` — bootstrap the controller. - `state: WritableSignal` — template-friendly. - `stream$: Observable` — RxJS interop. - Actions: `send`, `stop`, `retry`, `setInput`, `clear`, `approve`, `deny`. - `ngOnDestroy` auto-unsubscribes. ## Minimal example ```ts import { Component, inject } from '@angular/core' import { AgentskitChat } from '@agentskit/angular' @Component({ selector: 'ak-chat', standalone: true, template: `
{{ m.content }}
`, }) export class ChatComponent { chat = inject(AgentskitChat) constructor() { this.chat.init({ adapter }) } } ``` ## Related - [@agentskit/react](/docs/for-agents/react) — canonical contract. - [@agentskit/vue](/docs/for-agents/vue). ## Source - npm: https://www.npmjs.com/package/@agentskit/angular - repo: https://github.com/AgentsKit-io/agentskit/tree/main/packages/angular --- # @agentskit/cli — for agents Source: https://www.agentskit.io/docs/for-agents/cli > agentskit CLI — init, chat, run, dev, doctor, ai, tunnel, rag, config. ## Install ```bash npx @agentskit/cli # or: npm install -g @agentskit/cli ``` ## Commands | Command | Purpose | |---|---| | `agentskit init` | Scaffold a new project (templates: react, ink, runtime, multi-agent) | | `agentskit chat` | Interactive chat (Ink-based) | | `agentskit run ""` | Run an agent once; supports `--provider`, `--model`, `--api-key`, `--base-url`, `--verbose` | | `agentskit dev` | Dev server with hot-reload | | `agentskit doctor` | Diagnose env (providers, keys, tooling) | | `agentskit ai ""` | NL → `AgentSchema` + scaffolded project. See [agentskit ai](/docs/reference/recipes/agentskit-ai) | | `agentskit tunnel` | ngrok-style tunnel for webhooks | | `agentskit rag` | Local RAG helpers (ingest / search) | | `agentskit config` | Read / write local config | ## Programmatic helpers - `@agentskit/cli/ai` — `scaffoldAgent`, `writeScaffold`, `createAdapterPlanner`. ## Related - [@agentskit/core](/docs/for-agents/core) - [@agentskit/runtime](/docs/for-agents/runtime) ## Source - npm: https://www.npmjs.com/package/@agentskit/cli - repo: https://github.com/AgentsKit-io/agentskit/tree/main/packages/cli --- # @agentskit/core — for agents Source: https://www.agentskit.io/docs/for-agents/core > Zero-dependency foundation. Contracts, chat controller, primitives, and a dozen feature subpaths. ## Purpose Stable TypeScript contracts (Adapter, Tool, Memory, Retriever, Skill, Runtime) + `createChatController` + primitives. Target: <10 KB gzipped, zero runtime deps. ## Install ```bash npm install @agentskit/core ``` ## Primary exports (main entry) - `createChatController(config)` — state machine behind every UI binding. Returns `{ getState, subscribe, send, stop, retry, edit, regenerate, setInput, clear, approve, deny, updateConfig }`. - `defineTool(config)` — typed tool factory with JSON-Schema input inference. - `createInMemoryMemory` / `createLocalStorageMemory` — default `ChatMemory` implementations. - `createStaticRetriever` / `formatRetrievedDocuments` — RAG-less retrieval. - `executeToolCall`, `consumeStream`, `createEventEmitter`, `safeParseArgs`, `generateId`, `buildMessage` — low-level helpers. - `AgentsKitError`, `AdapterError`, `ToolError`, `MemoryError`, `ConfigError`, `ErrorCodes` — error taxonomy. ## Subpath exports (zero main-bundle weight) | Subpath | Purpose | Recipe | |---|---|---| | `@agentskit/core/agent-schema` | Declarative agent YAML/JSON + validator | [Schema-first agents](/docs/reference/recipes/schema-first-agent) | | `@agentskit/core/prompt-experiments` | A/B prompts with feature flags | [A/B prompts](/docs/reference/recipes/prompt-experiments) | | `@agentskit/core/auto-summarize` | Auto-summarizing `ChatMemory` wrapper | [Auto-summarize](/docs/reference/recipes/auto-summarize) | | `@agentskit/core/hitl` | Approval gates + `ApprovalStore` | [HITL approvals](/docs/reference/recipes/hitl-approvals) | | `@agentskit/core/security` | PII redactor + injection detector + rate limiter | [PII](/docs/reference/recipes/pii-redaction), [Injection](/docs/reference/recipes/prompt-injection), [Rate limit](/docs/reference/recipes/rate-limiting) | | `@agentskit/core/compose-tool` | Chain N tools into one | [Tool composer](/docs/reference/recipes/tool-composer) | | `@agentskit/core/self-debug` | Retry tools with LLM-corrected args | [Self-debug](/docs/reference/recipes/self-debug) | | `@agentskit/core/generative-ui` | Typed UI element tree + artifacts | [Gen UI](/docs/reference/recipes/generative-ui) | | `@agentskit/core/a2a` | Agent-to-Agent protocol spec | [Open specs](/docs/reference/recipes/open-specs) | | `@agentskit/core/manifest` | Skill + tool manifest format (MCP-compat) | [Open specs](/docs/reference/recipes/open-specs) | | `@agentskit/core/eval-format` | Portable eval dataset + run-result | [Open specs](/docs/reference/recipes/open-specs) | ## Minimal example ```ts import { createChatController } from '@agentskit/core' import { anthropic } from '@agentskit/adapters' const controller = createChatController({ adapter: anthropic({ apiKey: process.env.ANTHROPIC_API_KEY!, model: 'claude-sonnet-4-6' }), }) controller.subscribe(() => console.log(controller.getState().messages)) await controller.send('Hello') ``` ## Common patterns - Wire a controller into any framework via `getState` + `subscribe` (see the framework bindings under [For agents](/docs/for-agents)). - Compose tools with [`composeTool`](/docs/reference/recipes/tool-composer) or wrap failing ones with [`wrapToolWithSelfDebug`](/docs/reference/recipes/self-debug). - Gate risky operations with [`createApprovalGate`](/docs/reference/recipes/hitl-approvals) + HITL + signed [audit log](/docs/reference/recipes/audit-log). ## Related packages - [@agentskit/adapters](/docs/for-agents/adapters) - [@agentskit/runtime](/docs/for-agents/runtime) - [@agentskit/react](/docs/for-agents/react) ## Source - npm: https://www.npmjs.com/package/@agentskit/core - repo: https://github.com/AgentsKit-io/agentskit/tree/main/packages/core --- # @agentskit/eval — for agents Source: https://www.agentskit.io/docs/for-agents/eval > Evaluation harness + deterministic replay + snapshot testing + diff + CI reporters. ## Install ```bash npm install @agentskit/eval ``` ## Primary exports - `runEval({ agent, suite })` — run an `EvalSuite` against any async agent fn. ### Subpaths | Subpath | Contents | |---|---| | `@agentskit/eval/replay` | `createRecordingAdapter`, `createReplayAdapter`, cassettes, `createTimeTravelSession`, `replayAgainst`, `summarizeReplay`. See [Deterministic replay](/docs/reference/recipes/deterministic-replay), [Time travel](/docs/reference/recipes/time-travel-debug), [Replay-different-model](/docs/reference/recipes/replay-different-model). | | `@agentskit/eval/snapshot` | `matchPromptSnapshot` (exact / normalized / similarity). See [Snapshots](/docs/reference/recipes/prompt-snapshots). | | `@agentskit/eval/diff` | `promptDiff`, `attributePromptChange`, `formatDiff`. See [Prompt diff](/docs/reference/recipes/prompt-diff). | | `@agentskit/eval/ci` | `renderJUnit`, `renderMarkdown`, `renderGitHubAnnotations`, `reportToCi`. See [Evals in CI](/docs/reference/recipes/evals-ci). | ## Minimal example ```ts import { runEval } from '@agentskit/eval' const result = await runEval({ agent: async (input) => (await runtime.run(input)).content, suite: { name: 'qa', cases: [{ input: 'Capital of France?', expected: 'Paris' }], }, }) console.log(`${result.passed}/${result.totalCases}`) ``` ## Related - [@agentskit/runtime](/docs/for-agents/runtime). - [@agentskit/core/eval-format](/docs/reference/recipes/open-specs) — portable format spec. ## Source - npm: https://www.npmjs.com/package/@agentskit/eval - repo: https://github.com/AgentsKit-io/agentskit/tree/main/packages/eval --- # @agentskit/ink — for agents Source: https://www.agentskit.io/docs/for-agents/ink > Terminal chat UI via Ink. Same useChat contract as the React binding. ## Install ```bash npm install @agentskit/ink ``` ## Primary exports - `useChat(config): ChatReturn` — mirrors `@agentskit/react`. - ``, ``, ``, ``, `` — Ink components with keyboard nav + ANSI theming. ## Minimal example ```tsx import { render } from 'ink' import { ChatContainer } from '@agentskit/ink' import { anthropic } from '@agentskit/adapters' render() ``` ## Related packages - [@agentskit/react](/docs/for-agents/react) — sibling binding, same contract. - [@agentskit/cli](/docs/for-agents/cli) — `agentskit chat` ships ink internally. ## Source - npm: https://www.npmjs.com/package/@agentskit/ink - repo: https://github.com/AgentsKit-io/agentskit/tree/main/packages/ink --- # @agentskit/memory — for agents Source: https://www.agentskit.io/docs/for-agents/memory > Chat + vector memory backends, plus hierarchical, encrypted, graph, and personalization stores. ## Install ```bash npm install @agentskit/memory ``` ## Primary exports ### Chat memory (ordered history) - `fileChatMemory({ path })` — JSON file. - `sqliteChatMemory` — SQLite-backed. - `redisChatMemory` — Redis-backed. ### Vector memory - `fileVectorMemory({ path })` — JSON file. - `redisVectorMemory` — Redis Vector. - `pgvector`, `pinecone`, `qdrant`, `chroma`, `upstashVector` — BYO-client / HTTP vector adapters. See [Vector adapters](/docs/reference/recipes/vector-adapters). ### Higher-order memory - `createVirtualizedMemory` — hot window + cold retriever. - `createHierarchicalMemory` — MemGPT tiers (working / recall / archival). - `createAutoSummarizingMemory` (from `@agentskit/core/auto-summarize`) — fold oldest into a summary. - `createEncryptedMemory` — AES-GCM over any `ChatMemory`. See [Encrypted memory](/docs/reference/recipes/encrypted-memory). - `createInMemoryGraph` — knowledge graph (nodes + edges + BFS). See [Graph memory](/docs/reference/recipes/graph-memory). - `createInMemoryPersonalization` + `renderProfileContext` — per-subject profile. See [Personalization](/docs/reference/recipes/personalization). ## Minimal example ```ts import { createInMemoryMemory } from '@agentskit/core' import { createVirtualizedMemory } from '@agentskit/memory' const memory = createVirtualizedMemory(createInMemoryMemory(), { maxActive: 50 }) ``` ## Related - [@agentskit/rag](/docs/for-agents/rag) — embedders + retrievers on top of vector memory. - [@agentskit/core](/docs/for-agents/core) — the `ChatMemory` / `VectorMemory` contracts. ## Source - npm: https://www.npmjs.com/package/@agentskit/memory - repo: https://github.com/AgentsKit-io/agentskit/tree/main/packages/memory --- # @agentskit/observability — for agents Source: https://www.agentskit.io/docs/for-agents/observability > Console + LangSmith + OpenTelemetry logging, token counters, cost guard, trace viewer, signed audit log, devtools server. ## Install ```bash npm install @agentskit/observability ``` ## Primary exports ### Loggers / tracers - `consoleLogger(config?)` — local dev logging. - `langsmith(config)` — LangSmith observer. - `opentelemetry(config)` — OTel observer. - `createTraceTracker({ onSpanStart, onSpanEnd })` — low-level span lifecycle. ### Guards + counters - `costGuard(options)` — dollar ceiling per run. - `priceFor`, `computeCost`, `DEFAULT_PRICES`. - `approximateCounter`, `countTokens`, `countTokensDetailed`, `createProviderCounter`. ### Local trace viewer - `createFileTraceSink(dir)`, `buildTraceReport`, `renderTraceViewerHtml`. See [Trace viewer](/docs/reference/recipes/trace-viewer). ### Devtools server - `createDevtoolsServer`, `toSseFrame`. See [Devtools server](/docs/reference/recipes/devtools-server). ### Signed audit log - `createSignedAuditLog`, `createInMemoryAuditStore`. Hash-chain + HMAC. See [Audit log](/docs/reference/recipes/audit-log). ## Minimal example ```ts import { consoleLogger, costGuard } from '@agentskit/observability' import { createRuntime } from '@agentskit/runtime' const runtime = createRuntime({ adapter, observers: [consoleLogger(), costGuard({ maxUsd: 0.5 })], }) ``` ## Related - [@agentskit/eval](/docs/for-agents/eval) - [@agentskit/runtime](/docs/for-agents/runtime) ## Source - npm: https://www.npmjs.com/package/@agentskit/observability - repo: https://github.com/AgentsKit-io/agentskit/tree/main/packages/observability --- # @agentskit/rag — for agents Source: https://www.agentskit.io/docs/for-agents/rag > Plug-and-play RAG. Chunking + ingest + retrieve + rerank + hybrid + six document loaders. ## Install ```bash npm install @agentskit/rag ``` ## Primary exports - `createRAG({ embed, store, chunkSize, chunkOverlap, topK, threshold })` — `ingest(docs)` + `retrieve(request)` + `search(query)`. - `chunkText({ chunkSize, chunkOverlap, split })` — lower-level splitter. - `createRerankedRetriever(base, { candidatePool, topK, rerank })` — pluggable reranker (BM25 default). See [RAG reranking](/docs/reference/recipes/rag-reranking). - `createHybridRetriever(base, { vectorWeight, bm25Weight })` — vector + BM25 hybrid. - `bm25Score`, `bm25Rerank` — standalone helpers. ### Document loaders - `loadUrl`, `loadGitHubFile`, `loadGitHubTree`, `loadNotionPage`, `loadConfluencePage`, `loadGoogleDriveFile`, `loadPdf` (BYO parser). See [Doc loaders](/docs/reference/recipes/doc-loaders). ## Minimal example ```ts import { createRAG, loadGitHubTree } from '@agentskit/rag' import { fileVectorMemory } from '@agentskit/memory' import { openaiEmbedder } from '@agentskit/adapters' const rag = createRAG({ embed: openaiEmbedder({ apiKey }), store: fileVectorMemory({ path: './kb.json' }), }) await rag.ingest(await loadGitHubTree('org', 'repo', { token })) const hits = await rag.search('onboarding flow') ``` ## Related - [@agentskit/memory](/docs/for-agents/memory) — vector stores. - [@agentskit/adapters](/docs/for-agents/adapters) — embedders. ## Source - npm: https://www.npmjs.com/package/@agentskit/rag - repo: https://github.com/AgentsKit-io/agentskit/tree/main/packages/rag --- # @agentskit/react — for agents Source: https://www.agentskit.io/docs/for-agents/react > React hooks + headless chat components driving createChatController. ## Purpose `useChat` hook returning `ChatReturn` (state + actions) + headless components with `data-ak-*` attributes for theming. ## Install ```bash npm install @agentskit/react ``` ## Primary exports - `useChat(config): ChatReturn` — same contract as every other framework binding. - ``, ``, ``, ``, ``, ``, ``, ``. - Re-exports `createChatController`, all `@agentskit/core` types, and helpers. - `@agentskit/react/theme` — CSS variable theme. ## Minimal example ```tsx import { useChat } from '@agentskit/react' import { anthropic } from '@agentskit/adapters' export function Chat() { const chat = useChat({ adapter: anthropic({ apiKey: key, model: 'claude-sonnet-4-6' }) }) return (
{ e.preventDefault(); chat.send(chat.input) }}> {chat.messages.map(m =>
{m.content}
)} chat.setInput(e.target.value)} />
) } ``` ## Related frameworks (same contract) - [@agentskit/vue](/docs/for-agents/vue) - [@agentskit/svelte](/docs/for-agents/svelte) - [@agentskit/solid](/docs/for-agents/solid) - [@agentskit/react-native](/docs/for-agents/react-native) - [@agentskit/angular](/docs/for-agents/angular) - [@agentskit/ink](/docs/for-agents/ink) ## Source - npm: https://www.npmjs.com/package/@agentskit/react - repo: https://github.com/AgentsKit-io/agentskit/tree/main/packages/react --- # @agentskit/react-native — for agents Source: https://www.agentskit.io/docs/for-agents/react-native > React Native / Expo hook for AgentsKit. Metro + Hermes safe. ## Install ```bash npm install @agentskit/react-native # peers: npm install react react-native ``` ## Primary exports - `useChat(config): ChatReturn` — identical contract to `@agentskit/react`, imported from pure React (no DOM). ## Minimal example ```tsx import { useChat } from '@agentskit/react-native' import { View, TextInput, FlatList, Pressable, Text } from 'react-native' export function Chat({ adapter }) { const chat = useChat({ adapter }) return ( m.id} renderItem={({ item }) => {item.content}} /> chat.send(chat.input)}>Send ) } ``` ## Related - [@agentskit/react](/docs/for-agents/react) — same contract, web. ## Source - npm: https://www.npmjs.com/package/@agentskit/react-native - repo: https://github.com/AgentsKit-io/agentskit/tree/main/packages/react-native --- # @agentskit/runtime — for agents Source: https://www.agentskit.io/docs/for-agents/runtime > Standalone agent runtime (ReAct loop) + speculate + topologies + durable execution + background agents. ## Purpose Run an agent without a UI. Supports reflection, planning, multi-agent orchestration, durable step logs, cron + webhooks, speculative execution. ## Install ```bash npm install @agentskit/runtime ``` ## Primary exports - `createRuntime({ adapter, tools, memory, skills, observers, ... })` — one-line agent; `runtime.run(task)` returns `{ content, steps, ... }`. - `createSharedContext` — typed shared context across tools. - `speculate({ candidates, pick, timeoutMs })` — race adapters, abort losers. See [Speculative execution](/docs/reference/recipes/speculative-execution). - `supervisor`, `swarm`, `hierarchical`, `blackboard` — multi-agent topologies. See [Topologies](/docs/reference/recipes/multi-agent-topologies). - `createDurableRunner` + `createInMemoryStepLog` / `createFileStepLog` — Temporal-style step-log durability. See [Durable execution](/docs/reference/recipes/durable-execution). - `createCronScheduler` (5-field cron + `every:`) + `createWebhookHandler` — background agents. See [Background agents](/docs/reference/recipes/background-agents). ## Minimal example ```ts import { createRuntime } from '@agentskit/runtime' import { anthropic } from '@agentskit/adapters' const runtime = createRuntime({ adapter: anthropic({ apiKey: process.env.ANTHROPIC_API_KEY!, model: 'claude-sonnet-4-6' }), }) const result = await runtime.run('Summarize the quarterly report.') console.log(result.content) ``` ## Common patterns - Survive crashes: wrap side effects in `runner.step(id, fn)` (durable). - A/B across models: `speculate` or `replayAgainst`. - Compose agents: supervisor / swarm / hierarchical / blackboard. - React to events: `createWebhookHandler` + `createCronScheduler`. ## Related packages - [@agentskit/core](/docs/for-agents/core) - [@agentskit/adapters](/docs/for-agents/adapters) - [@agentskit/skills](/docs/for-agents/skills) - [@agentskit/observability](/docs/for-agents/observability) ## Source - npm: https://www.npmjs.com/package/@agentskit/runtime - repo: https://github.com/AgentsKit-io/agentskit/tree/main/packages/runtime --- # @agentskit/sandbox — for agents Source: https://www.agentskit.io/docs/for-agents/sandbox > Secure code execution (E2B / WebContainer) + mandatory sandbox policy wrapper. ## Install ```bash npm install @agentskit/sandbox ``` ## Primary exports - `createSandbox(config?)` — spawn a sandbox; default backend probes E2B. - `sandboxTool()` — ready-made `code_execution` tool (js / python). - `createE2BBackend(config)` — bring your own E2B API key. - Types: `Sandbox`, `SandboxConfig`, `SandboxBackend`, `ExecuteOptions`, `ExecuteResult`. ### Policy wrapper - `createMandatorySandbox({ sandbox, policy })` — enforce allow / deny / requireSandbox / validators across every tool. See [Mandatory sandbox](/docs/reference/recipes/mandatory-sandbox). ## Minimal example ```ts import { sandboxTool, createMandatorySandbox } from '@agentskit/sandbox' import { shell, filesystem, webSearch } from '@agentskit/tools' const policy = createMandatorySandbox({ sandbox: sandboxTool(), policy: { requireSandbox: ['shell'], deny: ['filesystem'] }, }) const safeTools = [shell(), filesystem({ basePath }), webSearch()].map(t => policy.wrap(t)) ``` ## Related - [@agentskit/tools](/docs/for-agents/tools) — tools to wrap. - [@agentskit/core/hitl](/docs/reference/recipes/hitl-approvals) — human approvals on top. ## Source - npm: https://www.npmjs.com/package/@agentskit/sandbox - repo: https://github.com/AgentsKit-io/agentskit/tree/main/packages/sandbox --- # @agentskit/skills — for agents Source: https://www.agentskit.io/docs/for-agents/skills > Ready-made personas (system prompt + behavior) + composition + marketplace registry. ## Install ```bash npm install @agentskit/skills ``` ## Primary exports ### Ready-made skills - `researcher` — methodical web researcher. - `coder` — TDD-first coder. - `planner` — step-by-step planner. - `critic` — critic / reviewer. - `summarizer` — structured summarizer. - `codeReviewer` — PR review with severity tags. - `sqlGen` — NL → parameterized SQL. - `dataAnalyst` — hypothesis-driven data analysis. - `translator` — faithful translator. ### Composition + discovery - `composeSkills(a, b, ...)` — merge skills into one. - `listSkills()` — metadata for every bundled skill. ### Marketplace - `createSkillRegistry(initial?)` — publish / list / install / unpublish. - `parseSemver`, `compareSemver`, `matchesRange` — tiny semver helpers. - Types: `SkillPackage`, `SkillRegistry`, `SkillRegistryQuery`. See [Skill marketplace](/docs/reference/recipes/skill-marketplace). ## Minimal example ```ts import { createRuntime } from '@agentskit/runtime' import { researcher } from '@agentskit/skills' const runtime = createRuntime({ adapter, systemPrompt: researcher.systemPrompt, tools: [/* webSearch() etc. */], }) ``` ## Related - [@agentskit/core](/docs/for-agents/core) — `SkillDefinition` contract. - [@agentskit/runtime](/docs/for-agents/runtime). ## Source - npm: https://www.npmjs.com/package/@agentskit/skills - repo: https://github.com/AgentsKit-io/agentskit/tree/main/packages/skills --- # @agentskit/solid — for agents Source: https://www.agentskit.io/docs/for-agents/solid > Solid hook for AgentsKit. ## Install ```bash npm install @agentskit/solid # peer: npm install solid-js ``` ## Primary exports - `useChat(config): ChatReturn` — Solid hook backed by `createStore` + `onCleanup`. ## Minimal example ```tsx import { useChat } from '@agentskit/solid' const chat = useChat({ adapter }) return
{chat.messages.length} messages
``` ## Related - [@agentskit/react](/docs/for-agents/react) - [@agentskit/vue](/docs/for-agents/vue) ## Source - npm: https://www.npmjs.com/package/@agentskit/solid - repo: https://github.com/AgentsKit-io/agentskit/tree/main/packages/solid --- # @agentskit/svelte — for agents Source: https://www.agentskit.io/docs/for-agents/svelte > Svelte 5 store for AgentsKit. ## Install ```bash npm install @agentskit/svelte # peer: npm install svelte ``` ## Primary exports - `createChatStore(config): SvelteChatStore` — `Readable` + action methods + `destroy()`. ## Minimal example ```svelte {#each $chat.messages as m (m.id)}

{m.content}

{/each}
chat.send($chat.input)}>
``` ## Related - [@agentskit/react](/docs/for-agents/react) - [@agentskit/vue](/docs/for-agents/vue) ## Source - npm: https://www.npmjs.com/package/@agentskit/svelte - repo: https://github.com/AgentsKit-io/agentskit/tree/main/packages/svelte --- # @agentskit/tools — for agents Source: https://www.agentskit.io/docs/for-agents/tools > Built-in executable tools + 20+ third-party integrations + bidirectional MCP bridge. ## Install ```bash npm install @agentskit/tools ``` ## Primary exports (main entry) - `webSearch`, `fetchUrl`, `filesystem`, `shell` — core built-ins. - `defineZodTool` — define tools with Zod schemas. - `listTools` — discovery helper. ## Subpath exports | Subpath | Contents | |---|---| | `@agentskit/tools/mcp` | `createMcpClient`, `createMcpServer`, `toolsFromMcpClient`, `createStdioTransport`, `createInMemoryTransportPair`. See [MCP bridge](/docs/reference/recipes/mcp-bridge). | | `@agentskit/tools/integrations` | `github`, `linear`, `slack`, `notion`, `discord`, `gmail`, `googleCalendar`, `stripe`, `postgres`, `s3`, `firecrawl`, `reader`, `documentParsers`, `openaiImages`, `elevenlabs`, `whisper`, `deepgram`, `maps`, `weather`, `coingecko`, `browserAgent`. See [Integrations](/docs/reference/recipes/integrations) + [More integrations](/docs/reference/recipes/more-integrations). | ## Minimal example ```ts import { webSearch } from '@agentskit/tools' import { github } from '@agentskit/tools/integrations' const tools = [ webSearch(), ...github({ token: process.env.GITHUB_TOKEN! }), ] ``` ## Related - [@agentskit/core/compose-tool](/docs/reference/recipes/tool-composer) — chain tools. - [@agentskit/sandbox](/docs/for-agents/sandbox) — enforce a sandbox policy across tools. - [@agentskit/runtime](/docs/for-agents/runtime). ## Source - npm: https://www.npmjs.com/package/@agentskit/tools - repo: https://github.com/AgentsKit-io/agentskit/tree/main/packages/tools --- # @agentskit/vue — for agents Source: https://www.agentskit.io/docs/for-agents/vue > Vue 3 composable + ChatContainer component. ## Install ```bash npm install @agentskit/vue # peer: npm install vue ``` ## Primary exports - `useChat(config): ChatReturn` — Vue 3 composable, reactive via `reactive()` + auto-cleanup on scope dispose. - `` — headless container using `data-ak-*` attributes. ## Minimal example ```ts import { useChat } from '@agentskit/vue' const chat = useChat({ adapter }) // chat.messages, chat.input are reactive // chat.send(text), chat.setInput(value), etc. ``` ## Related frameworks - [@agentskit/react](/docs/for-agents/react) — canonical contract. - [@agentskit/svelte](/docs/for-agents/svelte), [@agentskit/solid](/docs/for-agents/solid), [@agentskit/angular](/docs/for-agents/angular). ## Source - npm: https://www.npmjs.com/package/@agentskit/vue - repo: https://github.com/AgentsKit-io/agentskit/tree/main/packages/vue --- # Get started Source: https://www.agentskit.io/docs/get-started > Quickstart, mental model, comparisons, migrations. New here? Three steps: 1. **[Quickstart](./getting-started/quickstart)** — streaming chat in under 10 lines. 2. **[Mental model](./concepts/mental-model)** — how the six contracts compose. 3. **[Packages overview](/docs/reference/packages/overview)** — everything that ships under `@agentskit/*`. ## What's here - **[Getting started](./getting-started)** — installation, quickstart, first-steps guides. - **[Concepts](./concepts)** — the six contracts (Adapter, Tool, Skill, Memory, Retriever, Runtime) + implementations index. - **[Comparison](./comparison)** — AgentsKit vs LangChain / Vercel AI / Mastra / LlamaIndex. - **[Migrating](./migrating)** — port from Vercel AI SDK, LangChain.js, Mastra. - **[Announcements](./announcements)** — release notes + milestones. - **[Manifesto](https://github.com/AgentsKit-io/agentskit/blob/main/MANIFESTO.md)** — why AgentsKit exists, what it stands for. ## Then pick a path - Chat UI → [UI tab](/docs/ui) - Autonomous agent → [Agents tab](/docs/agents) - RAG / memory → [Data tab](/docs/data) - Production ops → [Production tab](/docs/production) - API lookup → [Reference tab](/docs/reference) --- # @agentskit/core hits 1.0 Source: https://www.agentskit.io/docs/get-started/announcements/core-v1 > The JavaScript agent toolkit that doesn't lock you in. 5.17 KB gzipped. Zero dependencies. Six contracts pinned to ADRs. Forever stable at the minor level. # The JavaScript agent toolkit just hit 1.0 **5.17 KB gzipped. Zero dependencies. Six contracts pinned to ADRs.** Today, `@agentskit/core` graduates to **v1.0.0** — the stable substrate for every agent you'll ship in JavaScript this decade. ```bash npm install @agentskit/core@1 ``` --- ## The numbers behind v1 | | | |---|---| | **Core bundle** | 5.17 KB gzipped — **48% under** the 10 KB Manifesto budget | | **Runtime deps** | Zero | | **Packages** | 14, independently installable | | **Tests** | 538 passing across the ecosystem | | **ADR-pinned contracts** | 6 (Adapter, Tool, Memory, Retriever, Skill, Runtime) | | **PRs through the gauntlet** | 18 in Phase 0 + 9 in Phase 1, zero contract breakage | Every number above is enforced in CI. The budget isn't a goal — it's a gate. --- ## The problem with building agents in JavaScript today Pick your poison: - **Heavyweight SDKs** that drag 200 KB of transitive dependencies into your bundle, couple you to one provider, and ship breaking changes every minor version. - **Glue primitives yourself** — spend six months writing adapter layers, retry logic, tool contracts, memory abstractions, and an evaluation harness before you ship one line of product code. - **Pick a framework** — inherit its opinions, its abstractions, its lifetimes, and the day it stops being maintained you're stuck. None of these are fine. The agent era deserves a substrate that works like React does for UI: small, stable, composable, and out of your way. --- ## What AgentsKit.js actually is AgentsKit.js is a family of **14 small packages** built on one **5 KB core**. Every package is plug-and-play. Every contract is open. Every combination composes. ``` @agentskit/core ← pure foundation, zero deps, v1.0.0 ├── adapters ← OpenAI, Anthropic, Gemini, Ollama, LangChain, Vercel AI, any ReadableStream ├── react ← useChat + headless components for the browser ├── ink ← same hooks, same components, for the terminal ├── cli ← agentskit init / chat / dev / doctor / tunnel ├── runtime ← standalone agent loop (ReAct, reflection, multi-agent) ├── tools ← browser, fs, web search, code exec, email, calendar ├── skills ← researcher, critic, planner, coder, summarizer ├── memory ← in-memory, file, SQLite, Redis, vector ├── rag ← plug-and-play retrieval in 3 lines ├── sandbox ← E2B + WebContainer code execution ├── observability ← LangSmith, OpenTelemetry, cost guards ├── eval ← agent benchmarking for CI └── templates ← production-ready starters ``` Install the two you need. Skip the twelve you don't. The API surface you use is the only surface you pay for. --- ## Why v1 is a promise, not a version bump Shipping a "1.0" means almost nothing. **Keeping it stable** is the work. With v1, `@agentskit/core` commits to: - **API frozen at the minor level.** Breaking changes require a major bump and a deprecation cycle. - **Contracts pinned to ADRs.** Any change to Adapter, Tool, Memory, Retriever, Skill, or Runtime contracts requires a new ADR + coordinated major bump across affected packages. - **Deprecations live for ≥ 1 minor release** before removal. - **Every user-facing change** ships with a changeset describing the impact. The substrate stops moving. Your code stops breaking. --- ## What else shipped this cycle (Phase 1) Nine developer-experience + runtime features across nine PRs: ### `agentskit init` — interactive starter in 60 seconds Five questions, four templates (`react`, `ink`, `runtime`, `multi-agent`), demo provider runs zero-config. ### `agentskit doctor` — one-shot diagnostics Node version, package manager, deps, config, provider keys, reachability. Non-zero exit on fail — CI-ready. ### `agentskit dev` — hot-reload for agents chokidar watcher, debounced restarts, `r` to restart, `q` to quit. Stop babysitting your agent process. ### Auto-retry with exponential backoff Built into **every adapter**. Transient failures (408, 429, 5xx) retry with jitter. Terminal failures (4xx auth) don't. Respects `Retry-After`. ### Dry-run primitives `mockAdapter`, `recordingAdapter`, `replayAdapter`, `inMemorySink`. Test agents without burning tokens. Foundation for deterministic replay. ### `costGuard` Enforce a dollar budget per run. Observer + `AbortController`. Tracks usage, computes cost, aborts cleanly. Custom pricing tables supported. ### `edit` + `regenerate` on `ChatController` Optimistic-UI contract preserved. React and Ink hooks mirror the new surface automatically. --- ## Breaking changes from 0.4.x Two import swaps. Both driven by Manifesto principle 1 — **core works in any environment**, including browsers: ```diff - import { createFileMemory } from '@agentskit/core' + import { fileChatMemory } from '@agentskit/memory' - import { loadConfig } from '@agentskit/core' + import { loadConfig } from '@agentskit/cli' ``` That's it. Both helpers were Node-only and crashed browser consumers. Moving them to the packages that already carry Node dependencies restored universal core compatibility. Full guide: [Migrating from 0.4 to 1.0](/docs/get-started/migrating). --- ## The ten principles that got us here From the [Manifesto](https://github.com/AgentsKit-io/agentskit/blob/main/MANIFESTO.md), with v1 scorecards: 1. ✅ Core under 10 KB gzipped, zero deps — **5.17 KB, zero deps** 2. ✅ Every package plug-and-play — **14 packages, independently installable** 3. ✅ Interop radical — **any combination composes** 4. ✅ Zero lock-in — **all contracts open, storage local-first** 5. ✅ Agent-first — **runtime, skills, delegation are first-class** 6. ✅ Docs are product — **74 Fumadocs routes, 7 ADRs, 13 recipes, 3 migration guides** 7. ✅ TypeScript rigor — **no `any`, named exports only, strict mode** 8. ✅ Small, deep, testable modules — **538 tests, coverage gates in CI** 9. ✅ Predictable beats clever — **one entry point per primitive** 10. ✅ Open by default — **roadmap, RFCs, ADRs all public** --- ## What Phase 2 looks like Additive. The substrate holds. Highlights from the [Master PRD](https://github.com/AgentsKit-io/agentskit/issues/113): - Deterministic replay + prompt diff + time-travel debug - Hierarchical memory (MemGPT-style) + hybrid search + reranking - Durable execution, multi-agent topologies, HITL primitives - PII redaction, prompt injection detector, audit log None of it requires a breaking change to core. That's the point of v1. --- ## Built by one. Ready for many. Everything you just read — 14 packages, 538 tests, 6 ADR contracts, two full release phases — was shipped by **one maintainer** ([Emerson Braun](https://github.com/EmersonBraun)). That's a signal about the architecture, not a flex. The substrate is small enough, the contracts are clear enough, and the tests are rigorous enough that one person can hold the whole thing in their head. **That's the point.** It means you can, too. And it means every PR from here forward compounds. **The gaps where your name could go next:** - **New adapters** — Cohere, Mistral, Together, Groq, Bedrock, Azure OpenAI - **New tools** — anything in [`@agentskit/tools`](/docs/reference/packages) that doesn't exist yet - **New skills** — your best system prompt, packaged and shareable - **New templates** — starter kits for real use cases (customer support, research assistant, code reviewer) - **Translations** — docs in your language (i18n RFC lands Phase 2) - **Recipes** — one page, one problem, one working solution Every contract is pinned to an ADR. Every package ships with tests and a size budget. The bar is high, the scope is clear, and the code is small. Open an issue, send a PR, or just [star the repo](https://github.com/AgentsKit-io/agentskit) — the roadmap is public and every RFC is open. **[Public roadmap →](https://github.com/orgs/AgentsKit-io/projects/1)** · **[Contributing guide →](/docs/contributing)** The board is maintainer-curated, but the inbox is open to everyone: file an [issue](https://github.com/AgentsKit-io/agentskit/issues) or a [discussion](https://github.com/AgentsKit-io/agentskit/discussions) and it gets triaged onto the roadmap. --- ## Start building ```bash npm install @agentskit/core@1 @agentskit/react @agentskit/adapters # or pnpm add @agentskit/core@1 @agentskit/react @agentskit/adapters ``` A working chat in under 10 lines: **[Quick start →](/docs/get-started/getting-started/quickstart)** The mental model behind every package: **[Concepts →](/docs/get-started/concepts/mental-model)** Why this exists: **[Origin →](https://github.com/AgentsKit-io/agentskit/blob/main/ORIGIN.md)** --- *Day one is over. Day two starts with a real v1.* --- # AgentsKit vs. alternatives Source: https://www.agentskit.io/docs/get-started/comparison > How AgentsKit compares to LangChain.js, Vercel AI SDK, Mastra, and LlamaIndex.js across the surface area that actually matters in production. AgentsKit is one of several ways to build agents in JavaScript. This page is opinionated but honest — each column lists what each framework genuinely optimizes for, with a link you can follow to verify. ## Positioning in one line | Framework | Positioning | |-----------|-------------| | **AgentsKit** | One toolkit — same contracts across chat UI, runtime, memory, RAG, tools, observability. Zero lock-in; swap pieces without breaking the agent. | | [LangChain.js](https://js.langchain.com/docs/introduction/) | Huge integration catalog. Heavy on abstractions; focused on chains + agents. | | [Vercel AI SDK](https://sdk.vercel.ai/) | Best-in-class streaming + React hooks. Chat-first; less coverage beyond the message loop. | | [Mastra](https://mastra.ai/) | TypeScript-first agent framework with workflows + memory built-in. | | [LlamaIndex.js](https://ts.llamaindex.ai/) | RAG-first. Strong indexing + query engines. | ## Feature comparison | Capability | AgentsKit | LangChain.js | Vercel AI SDK | Mastra | LlamaIndex.js | |------------|:--------:|:------------:|:-------------:|:------:|:-------------:| | Streaming chat controller | ✅ | ✅ | ✅ | ✅ | ✅ | | React hook (`useChat`) | ✅ | partial | ✅ | partial | — | | Vue / Svelte / Solid / Angular / RN bindings | ✅ (same contract) | — | community | — | — | | Terminal UI | ✅ (`@agentskit/ink`) | — | — | — | — | | ReAct runtime | ✅ | ✅ | — | ✅ | ✅ | | Deterministic replay | ✅ (`@agentskit/eval/replay`) | — | — | — | — | | Multi-agent topologies (supervisor / swarm / blackboard / hierarchical) | ✅ | partial (LangGraph) | — | partial | — | | Durable execution (step log) | ✅ | via LangGraph | — | ✅ | — | | Human-in-the-loop primitives | ✅ (`@agentskit/core/hitl`) | partial | — | ✅ | — | | Built-in prompt injection detector | ✅ (`@agentskit/core/security`) | — | — | — | — | | Built-in signed audit log | ✅ (`@agentskit/observability`) | — | — | — | — | | Token-bucket rate limiting | ✅ | — | — | — | — | | Mandatory tool sandbox policy | ✅ (`@agentskit/sandbox`) | — | — | — | — | | Bidirectional MCP bridge | ✅ (`@agentskit/tools/mcp`) | client only | partial | client only | client only | | Vector backends | pgvector, Pinecone, Qdrant, Chroma, Upstash, Redis, file | 30+ | — | Postgres, SQLite | 30+ | | Document loaders (URL / GitHub / Notion / Confluence / Drive / PDF) | ✅ | ✅ (largest catalog) | — | partial | ✅ | | Evals in CI (JUnit + step-summary annotations) | ✅ (`@agentskit/eval/ci`) | partial | — | — | partial | | Core bundle size | **< 10 KB gzipped** | > 100 KB | ~30 KB | ~50 KB | > 100 KB | | Zero runtime deps (core) | ✅ | — | — | — | — | | Edge / Deno / Bun runtime | ✅ | partial | ✅ | partial | partial | "partial" means the capability exists but requires stitching multiple packages or a community port — not a built-in, contract-level feature. ## When to pick which - **AgentsKit** — you want one mental model across web, terminal, mobile, and server; you care about bundle size, observability, and production gates (HITL, rate limits, audit, sandbox). - **LangChain.js** — you need the widest catalog of prebuilt integrations today and are happy to wrap them yourself. - **Vercel AI SDK** — you only need a streaming chat hook inside a Next.js / React app and don't want extra surface area. - **Mastra** — you want a workflow-first agent framework with built-in memory and agent registry. - **LlamaIndex.js** — your product is a retrieval engine; you want sophisticated query engines out of the box. ## Migration guides - [Migrate from LangChain.js](/docs/get-started/migrating) - [Migrate from Vercel AI SDK](/docs/get-started/migrating) - [Migrate from Mastra](/docs/get-started/migrating) ## See also - [Mental model](/docs/get-started/concepts/mental-model) - [Recipes](/docs/reference/recipes) - [For-agents reference](/docs/for-agents) --- # Concepts Source: https://www.agentskit.io/docs/get-started/concepts > Six contracts. Every AgentsKit package is an implementation of one of them. AgentsKit has six stable contracts. Every package — every provider, every memory backend, every tool, every UI binding — plugs into one of them. Learn the shape once; swap implementations forever. ## The six contracts | Contract | What it abstracts | Page | |---|---|---| | **Adapter** | LLM providers | [adapter](./adapter) | | **Tool** | callable functions | [tool](./tool) | | **Skill** | personas + prompts | [skill](./skill) | | **Memory** | conversation + vector state | [memory](./memory) | | **Retriever** | context retrieval | [retriever](./retriever) | | **Runtime** | the orchestration loop | [runtime](./runtime) | ## Start here - [Mental model](./mental-model) — how the six contracts compose. - [Implementations](./implementations) — every concrete implementation per contract. - [Errors](./errors) — the error taxonomy every contract honors. ## See also - [Packages overview](/docs/reference/packages/overview) — packages grouped by role. - [For agents](/docs/for-agents) — dense LLM-friendly reference per contract. - [Comparison](/docs/get-started/comparison) — AgentsKit vs LangChain / Vercel AI / Mastra. --- # Adapter Source: https://www.agentskit.io/docs/get-started/concepts/adapter > The seam between AgentsKit and an LLM provider. The same interface for OpenAI, Anthropic, Gemini, Ollama, or anything that streams tokens. An **Adapter** is how AgentsKit talks to an LLM provider. It is the only layer that knows whether you're calling OpenAI, Anthropic, Gemini, Ollama, a local model, or a deterministic mock for tests. Every other package — runtime, hooks, UI — speaks to one shape and never has to care which provider is behind it. This is what makes "swap providers in one line" actually true. ## The interface ```ts import type { AdapterFactory, AdapterRequest, StreamSource, StreamChunk } from '@agentskit/core' export type AdapterFactory = { createSource: (request: AdapterRequest) => StreamSource } export interface StreamSource { stream: () => AsyncIterableIterator abort: () => void } ``` That's the whole contract from a consumer's point of view. You hand a factory to `useChat`, `createRuntime`, or any other AgentsKit primitive, and it does the rest. ## Using a built-in adapter ```ts import { anthropic, openai, ollama } from '@agentskit/adapters' const adapter = anthropic({ apiKey: process.env.ANTHROPIC_API_KEY!, model: 'claude-sonnet-4-6', }) ``` Configuration goes in at construction time — API key, model, base URL. The returned `AdapterFactory` is reusable across requests. ## How a stream looks The adapter emits `StreamChunk`s as the model speaks: ```ts type StreamChunk = { type: 'text' | 'tool_call' | 'tool_result' | 'reasoning' | 'error' | 'done' content?: string toolCall?: { id, name, args, result? } metadata?: Record } ``` Every stream ends with **exactly one** of: - A `done` chunk (success) - An `error` chunk (failure) - The iterator returning because the consumer called `abort()` That terminal-chunk guarantee is what stops the "did the stream end or hang?" ambiguity that haunts most agent libraries. ## When to write your own Use a built-in adapter unless one of these is true: - **Your provider isn't covered yet.** Adapters for new providers are 50–100 lines of code; see `packages/adapters/src` for examples. - **You need a custom routing/ensemble/fallback layer.** Wrap N adapters in your own factory and decide which to call per request. - **You're writing a deterministic mock for tests.** Yield a fixed `StreamChunk[]`, return from the iterator. That's it. If you're tempted to write a custom adapter just to add caching or log every call, don't. Use an observer (see [Runtime](./runtime)) instead. ## Common pitfalls | Pitfall | What to do instead | |---|---| | Calling the network from `createSource` | Defer all I/O to `stream()` — see invariant A1 | | Mutating the input `messages` array | Treat it as read-only; copy if you need to transform | | Ending the stream silently after the last text chunk | Always emit `{ type: 'done' }` | | Throwing from `stream()` on a provider error | Emit `{ type: 'error', metadata: { error } }` instead | | Calling `stream()` twice on the same `StreamSource` | Call `createSource()` again to get a fresh source | ## Going deeper The full list of invariants (ten of them, A1–A10) is in [ADR 0001 — Adapter contract](https://github.com/AgentsKit-io/agentskit/blob/main/docs/architecture/adrs/0001-adapter-contract.md). Read it before publishing a new adapter package. --- # Error Handling Source: https://www.agentskit.io/docs/get-started/concepts/errors > AgentsKit's didactic error system — Rust-compiler-style errors with codes, hints, and doc links baked in. AgentsKit uses a **didactic error system** inspired by the Rust compiler. Every error carries a machine-readable `code`, a human-readable `hint` explaining how to fix the problem, and a `docsUrl` pointing directly to the relevant documentation — so you know what went wrong and how to fix it without searching. ## The base class ```ts import { AgentsKitError } from '@agentskit/core' class AgentsKitError extends Error { readonly code: string // e.g. 'AK_TOOL_EXEC_FAILED' readonly hint: string | undefined // actionable fix suggestion readonly docsUrl: string | undefined // direct link to relevant docs readonly cause: unknown // original error, if any } ``` `toString()` formats like a compiler diagnostic: ``` error[AK_ADAPTER_MISSING]: No adapter provided --> Hint: Pass an adapter when creating the chat controller, e.g. createChatController({ adapter: openaiAdapter() }) --> Docs: https://www.agentskit.io/docs/adapters ``` ## Error subclasses All errors extend `AgentsKitError`. The subclasses set a default `docsUrl` for their domain: | Class | Default docs link | Thrown when | |-------|-------------------|-------------| | `AdapterError` | `/docs/adapters` | Adapter is missing or a stream call fails | | `ToolError` | `/docs/agents/tools` | A tool is not found or `execute` throws | | `MemoryError` | `/docs/memory` | Memory load, save, or deserialization fails | | `ConfigError` | `/docs/configuration` | Required configuration is absent or invalid | ## ErrorCodes All code strings live in the `ErrorCodes` constant so you never use bare string literals: ```ts import { ErrorCodes } from '@agentskit/core' // Adapter errors ErrorCodes.AK_ADAPTER_MISSING // adapter not provided to the controller ErrorCodes.AK_ADAPTER_STREAM_FAILED // provider streaming call failed // Tool errors ErrorCodes.AK_TOOL_NOT_FOUND // tool name not registered ErrorCodes.AK_TOOL_EXEC_FAILED // execute() threw // Memory errors ErrorCodes.AK_MEMORY_LOAD_FAILED // memory.load() failed ErrorCodes.AK_MEMORY_SAVE_FAILED // memory.save() failed ErrorCodes.AK_MEMORY_DESERIALIZE_FAILED // persisted state is corrupt // Config errors ErrorCodes.AK_CONFIG_INVALID // missing or wrong type in config ``` ## Catching and narrowing ```ts import { AgentsKitError, AdapterError, ToolError, MemoryError, ErrorCodes } from '@agentskit/core' try { await runtime.run(task) } catch (err) { if (err instanceof AdapterError) { console.error('Adapter problem:', err.hint) console.error('Docs:', err.docsUrl) } else if (err instanceof ToolError) { if (err.code === ErrorCodes.AK_TOOL_NOT_FOUND) { console.error(`Tool not registered: ${err.message}`) } else { // AK_TOOL_EXEC_FAILED — execution threw console.error('Tool failed:', err.toString()) if (err.cause instanceof Error) { console.error('Caused by:', err.cause.message) } } } else if (err instanceof AgentsKitError) { // any other AgentsKit-originating error console.error(err.toString()) } else { throw err // re-throw — not an AgentsKit error } } ``` ## Throwing in custom code When you author custom tools, memory adapters, or adapters, use the matching subclass so consumers can catch by type: ```ts import { ToolError, MemoryError, ErrorCodes } from '@agentskit/core' // In a custom tool export const fetchRecord = { name: 'fetch_record', async execute(args: { id: string }) { const record = await db.find(args.id) if (!record) { throw new ToolError({ code: ErrorCodes.AK_TOOL_EXEC_FAILED, message: `Record "${args.id}" not found in database`, hint: 'Ensure the record exists before calling fetch_record.', cause: undefined, }) } return record }, } // In a custom memory adapter async function load(): Promise { try { return JSON.parse(await fs.readFile(path, 'utf8')) } catch (cause) { throw new MemoryError({ code: ErrorCodes.AK_MEMORY_LOAD_FAILED, message: `Failed to load messages from ${path}`, hint: 'Check that the file exists and is valid JSON.', cause, }) } } ``` ## Why not plain `Error`? | | Plain `Error` | `AgentsKitError` | |---|---|---| | Machine-readable identity | no | `code` | | Actionable fix in the error | no | `hint` | | Docs link in the error | no | `docsUrl` | | Original cause preserved | manual | `cause` | | Console output | stack trace | compiler-style diagnostic | Using plain `Error` in ecosystem packages forces every caller to parse `message` strings — brittle and untyped. `AgentsKitError` subclasses give callers a stable, typed interface to react to specific failure modes without string parsing. ## See also [@agentskit/core exports](../packages/core) · [Tools](./tool) · [Memory](./memory) · [Adapters](./adapter) --- # Implementations index Source: https://www.agentskit.io/docs/get-started/concepts/implementations > Every concrete implementation of every AgentsKit contract. One page to answer "what ships with this?". Scope: implementations bundled in official `@agentskit/*` packages. Community packages are listed in the [skill marketplace](/docs/agents/skills/marketplace) and the [tool ecosystem](/docs/agents/tools/integrations). ## Adapter — LLM providers **Contract:** [concepts/adapter](./adapter) · **Package:** [@agentskit/adapters](/docs/reference/packages/adapters) **Hosted (17):** `openai`, `anthropic`, `gemini`, `grok`, `deepseek`, `kimi`, `mistral`, `cohere`, `together`, `groq`, `fireworks`, `openrouter`, `huggingface`, `langchain`, `langgraph`, `vercelAI`, `generic` — [deep dive](/docs/data/providers/hosted) **Local (4):** `ollama`, `lmstudio`, `vllm`, `llamacpp` — [deep dive](/docs/data/providers/local) **Embedders (7):** `openaiEmbedder`, `geminiEmbedder`, `ollamaEmbedder`, `deepseekEmbedder`, `grokEmbedder`, `kimiEmbedder`, `createOpenAICompatibleEmbedder` — [deep dive](/docs/data/providers/embedders) **Higher-order:** `createRouter`, `createEnsembleAdapter`, `createFallbackAdapter` — [deep dive](/docs/data/providers/higher-order) ## Tool — callable functions **Contract:** [concepts/tool](./tool) · **Package:** [@agentskit/tools](/docs/reference/packages/tools) **Authoring:** `defineTool`, `defineZodTool`, `composeTool`, `wrapToolWithSelfDebug`, `createMandatorySandbox` — [deep dive](/docs/agents/tools/authoring) **Built-ins:** `webSearch`, `fetchUrl`, `filesystem`, `shell` — [deep dive](/docs/agents/tools/builtins) **Integrations (20+):** `github`, `linear`, `slack`, `notion`, `discord`, `gmail`, `googleCalendar`, `stripe`, `postgres`, `s3`, `firecrawl`, `reader`, `documentParsers`, `openaiImages`, `elevenlabs`, `whisper`, `deepgram`, `maps`, `weather`, `coingecko`, `browserAgent` — [deep dive](/docs/agents/tools/integrations) **MCP:** `createMcpClient`, `toolsFromMcpClient`, `createMcpServer` — [deep dive](/docs/agents/tools/mcp) ## Skill — personas + prompts **Contract:** [concepts/skill](./skill) · **Package:** [@agentskit/skills](/docs/reference/packages/skills) **Built-in personas (9):** `researcher`, `coder`, `codeReviewer`, `planner`, `critic`, `summarizer`, `sqlGen`, `dataAnalyst`, `translator` — [deep dive](/docs/agents/skills/personas) **Authoring + composition:** `defineSkill`, `composeSkills`, `listSkills` — [deep dive](/docs/agents/skills/authoring) **Marketplace:** `createSkillRegistry` with semver resolver — [deep dive](/docs/agents/skills/marketplace) ## Memory — chat + vector + wrappers **Contract:** [concepts/memory](./memory) · **Package:** [@agentskit/memory](/docs/reference/packages/memory) **Chat:** `fileChatMemory`, `sqliteChatMemory`, `redisChatMemory` — [file](/docs/data/memory/file-chat) · [sqlite](/docs/data/memory/sqlite) · [redis](/docs/data/memory/redis-chat) **Vector:** `fileVectorMemory`, `redisVectorMemory`, `pgvector`, `pinecone`, `qdrant`, `chroma`, `upstashVector` — [file](/docs/data/memory/file-vector) · [redis](/docs/data/memory/redis-vector) · [pgvector](/docs/data/memory/pgvector) · [pinecone](/docs/data/memory/pinecone) · [qdrant](/docs/data/memory/qdrant) · [chroma](/docs/data/memory/chroma) · [upstash](/docs/data/memory/upstash-vector) **Wrappers:** `createEncryptedMemory`, `createHierarchicalMemory`, `createVirtualizedMemory`, `createAutoSummarizingMemory`, `createInMemoryGraph`, `createInMemoryPersonalization` — [encrypted](/docs/data/memory/encrypted) · [hierarchical](/docs/data/memory/hierarchical) · [virtualized](/docs/data/memory/virtualized) · [auto-summarize](/docs/data/memory/auto-summarize) · [graph](/docs/data/memory/graph) · [personalization](/docs/data/memory/personalization) ## Retriever — RAG pipeline **Contract:** [concepts/retriever](./retriever) · **Package:** [@agentskit/rag](/docs/reference/packages/rag) **Core:** `createRAG`, `chunkText` — [createRAG](/docs/data/rag/create-rag) · [chunking](/docs/data/rag/chunking) **Rerankers:** `createRerankedRetriever`, `cohereReranker`, `bgeReranker`, `bm25Reranker` — [deep dive](/docs/data/rag/rerank) **Hybrid:** `createHybridRetriever` — [deep dive](/docs/data/rag/hybrid) **Loaders (7):** `loadUrl`, `loadGitHubFile`, `loadGitHubTree`, `loadNotionPage`, `loadConfluencePage`, `loadGoogleDriveFile`, `loadPdf` — [deep dive](/docs/data/rag/loaders) ## Runtime — orchestration loop **Contract:** [concepts/runtime](./runtime) · **Package:** [@agentskit/runtime](/docs/reference/packages/runtime) **Core:** `createRuntime`, `createSharedContext` — [runtime](/docs/agents/runtime) **Durable:** `createDurableRunner`, `createInMemoryStepLog`, `createFileStepLog` — [deep dive](/docs/agents/durable) **Topologies:** `supervisor`, `swarm`, `hierarchical`, `blackboard` — [deep dive](/docs/agents/topologies) **Background:** `createCronScheduler`, `createWebhookHandler` — [deep dive](/docs/agents/background) **Speculate:** `speculate` — [deep dive](/docs/agents/speculate) ## UI bindings — one contract, seven frameworks **Contract:** mirrors `ChatReturn` — [UI → useChat](/docs/ui/use-chat) **Packages:** [react](/docs/reference/packages/react) · [vue](/docs/reference/packages/vue) · [svelte](/docs/reference/packages/svelte) · [solid](/docs/reference/packages/solid) · [react-native](/docs/reference/packages/react-native) · [angular](/docs/reference/packages/angular) · [ink](/docs/reference/packages/ink) ## Related - [Concepts overview](./) · [Mental model](./mental-model) - [Packages overview](/docs/reference/packages/overview) --- # Memory Source: https://www.agentskit.io/docs/get-started/concepts/memory > Two contracts, not one. ChatMemory persists conversation history. VectorMemory stores embeddings for semantic recall. Different problems, different backends. **Memory** in AgentsKit is two contracts, deliberately split: - **ChatMemory** — the ordered history of a session. Append to it, load it back. Redis is great at this. - **VectorMemory** — embeddings indexed for semantic retrieval. The substrate for RAG. pgvector, Pinecone, Qdrant are great at this. Plus a third primitive — **EmbedFn** — for turning text into vectors. LangChain unifies these. The result is implementations that half-fulfill both. We split them so a backend implements only what it does well. ## ChatMemory ```ts import type { ChatMemory } from '@agentskit/core' export interface ChatMemory { load: () => MaybePromise save: (messages: Message[]) => MaybePromise clear?: () => MaybePromise } ``` `save` is **replace-all** — not append. Controllers append in memory and flush the whole state at the end of a turn. This removes the "did I dedupe?" branching that haunts append-based stores. ```ts import { sqliteChatMemory } from '@agentskit/memory' const memory = sqliteChatMemory({ path: './sessions/user-42.db' }) createRuntime({ adapter, memory, /* ... */ }) ``` The runtime calls `load()` at the start of each `run()` and `save()` after a successful run. Aborted or failed runs do **not** save — atomicity comes for free. ## VectorMemory ```ts import type { VectorMemory, VectorDocument } from '@agentskit/core' export interface VectorMemory { store: (docs: VectorDocument[]) => MaybePromise search: ( embedding: number[], options?: { topK?: number; threshold?: number }, ) => MaybePromise delete?: (ids: string[]) => MaybePromise } ``` `store` is **upsert by id**. Re-indexing the same document is cheap and idempotent — no duplicate-id errors. ```ts import { fileVectorMemory } from '@agentskit/memory' import { openaiEmbedder } from '@agentskit/adapters' const store = fileVectorMemory({ path: './embeddings.json' }) const embed = openaiEmbedder({ apiKey: KEY, model: 'text-embedding-3-small' }) await store.store([ { id: 'doc-1', content: 'AgentsKit core is 10KB gzipped.', embedding: await embed('AgentsKit core is 10KB gzipped.') }, ]) const hits = await store.search(await embed('how big is the core?'), { topK: 3 }) ``` Search results come **descending by score**. `threshold` is exclusive from below (`> 0.7`, not `>= 0.7`). ## EmbedFn ```ts export type EmbedFn = (text: string) => Promise ``` A pure function. Same input + same model = same output. No randomness allowed. Built-in embedders: ```ts import { openaiEmbedder, ollamaEmbedder, geminiEmbedder } from '@agentskit/adapters' const embed = openaiEmbedder({ apiKey: KEY, model: 'text-embedding-3-small' }) ``` ## Built-in backends | Backend | ChatMemory | VectorMemory | |---|:---:|:---:| | `fileChatMemory` | ✓ | — | | `fileVectorMemory` | — | ✓ | | `sqliteChatMemory` | ✓ | — | | `redisChatMemory` | ✓ | — | | `redisVectorMemory` | — | ✓ | More coming in Phase 3 (pgvector, Pinecone, Qdrant, Chroma, Weaviate, Turso, Cloudflare Vectorize, Upstash). Each is a small adapter implementing six or eight invariants. ## When to write your own - **Your storage backend isn't covered yet.** A new ChatMemory backend is ~50 lines. A new VectorMemory backend is ~100. See the existing implementations as templates. - **You need encryption at rest.** Wrap an existing backend with an encrypting proxy — neither contract changes. - **You're testing.** A pure in-memory `ChatMemory` is twelve lines and instantly replayable. ## Common pitfalls | Pitfall | What to do instead | |---|---| | Implementing `save` as append-with-dedup | Replace-all (CM2). Trust the consumer to send the correct full state. | | Returning `null` from `load()` on empty | Return `[]`. Empty state is success, not absence. | | Mixing embedding dimensions in one VectorMemory | Pick one model per store; reject mismatches at construction. | | Padding `search` results to reach `topK` | Return fewer documents — `topK` is an upper bound, not a floor. | | Random embeddings or non-deterministic embedders | Cache by input; never inject randomness. EmbedFn must be stable. | ## Going deeper The full list of invariants (six for ChatMemory, eight for VectorMemory, three for EmbedFn) is in [ADR 0003 — Memory contract](https://github.com/AgentsKit-io/agentskit/blob/main/docs/architecture/adrs/0003-memory-contract.md). --- # Mental model Source: https://www.agentskit.io/docs/get-started/concepts/mental-model > The six concepts every AgentsKit user should know. AgentsKit composes around six core concepts. Together they describe everything the framework does. If you understand these six, you can predict how any package behaves. ## The six concepts Runtime
the loop that
composes everything"] Adapter["Adapter
talks to
the LLM"] Tools["Tools
model can
call them"] Skill["Skill
model becomes
this persona"] Memory["Memory
chat history
+ vectors"] Retriever["Retriever
finds relevant
context"] Runtime --> Adapter Runtime --> Tools Runtime --> Skill Skill --> Memory Skill --> Retriever `} /> ## What each one does | Concept | One-line definition | | --- | --- | | **[Adapter](/docs/get-started/concepts/adapter)** | The seam to an LLM provider (OpenAI, Anthropic, etc.) | | **[Tool](/docs/get-started/concepts/tool)** | A function the model can **call** | | **[Skill](/docs/get-started/concepts/skill)** | A persona the model **becomes** (system prompt + behavior) | | **[Memory](/docs/get-started/concepts/memory)** | Chat history and vector storage | | **[Retriever](/docs/get-started/concepts/retriever)** | Fetches relevant context (RAG, web search, etc.) | | **[Runtime](/docs/get-started/concepts/runtime)** | The loop that composes them all | ## The two distinctions that matter most ### 1. Tool vs Skill A **tool** is a function. A **skill** is a persona. A tool: "give me the weather in Madrid." The model **calls** it. A skill: "you are a meticulous code reviewer who insists on tests." The model **becomes** it. Confusing them is the recurring failure mode in agent libraries. We make the distinction load-bearing — see [ADR 0002 (Tool)](https://github.com/AgentsKit-io/agentskit/blob/main/docs/architecture/adrs/0002-tool-contract.md) and [ADR 0005 (Skill)](https://github.com/AgentsKit-io/agentskit/blob/main/docs/architecture/adrs/0005-skill-contract.md). ### 2. ChatMemory vs VectorMemory Two different problems. Two different contracts. Two different backends. **ChatMemory** is the ordered history of a session. Redis is great at this. **VectorMemory** is embeddings indexed for semantic retrieval. pgvector or Qdrant are great at this. Forcing one interface to do both — as some other libraries do — produces implementations that half-fulfill both sides. We split them. ## Where to go next - Pick a concept page above to dig deeper - Or jump to the [quick start](/docs/get-started/getting-started/quickstart) and see them in code --- # Retriever Source: https://www.agentskit.io/docs/get-started/concepts/retriever > One narrow interface that serves RAG, BM25, web search, code search, and memory recall through composition. A **Retriever** answers one question: *"given a query and the conversation so far, what documents are relevant?"* It is the inversion-of-control seam between **agent talking** and **agent reading**. The runtime calls `retrieve()` once per turn before generating, hands the results to the model as context, and the model responds. The contract is intentionally narrow — one method — so RAG, BM25, hybrid search, web search, code search, and conversational memory recall all fit the same shape. Composition handles the variety. ## The interface ```ts import type { Retriever, RetrieverRequest, RetrievedDocument } from '@agentskit/core' export interface Retriever { retrieve: (request: RetrieverRequest) => MaybePromise } export interface RetrieverRequest { query: string messages: Message[] } export interface RetrievedDocument { id: string content: string source?: string score?: number metadata?: Record } ``` `query` is the focused string. `messages` is the surrounding conversation, available for retrievers that want to do query rewriting or conversational compression. Most retrievers ignore it. ## Using RAG ```ts import { createRAG } from '@agentskit/rag' import { fileVectorMemory } from '@agentskit/memory' import { openaiEmbedder } from '@agentskit/adapters' const rag = createRAG({ store: fileVectorMemory({ path: './embeddings.json' }), embed: openaiEmbedder({ apiKey: KEY, model: 'text-embedding-3-small' }), topK: 5, }) await rag.ingest([ { content: 'AgentsKit core is 10KB gzipped.' }, { content: 'Adapters are interchangeable per ADR 0001.' }, ]) createRuntime({ adapter, retriever: rag }) ``` `RAG` extends `Retriever`, so it plugs directly into the runtime. The runtime calls `retrieve()` on every turn. ## Web search as a retriever Anything that returns ranked documents fits: ```ts const webRetriever: Retriever = { async retrieve({ query }) { const res = await fetch(`https://api.example-search.com?q=${query}`) const hits = await res.json() return hits.map((h, i) => ({ id: h.url, content: h.snippet, source: h.url, score: hits.length - i, // rank-as-score is fine })) }, } ``` ## Composing retrievers A composite retriever is itself a `Retriever`. This is what makes reranking, hybrid search, and ensembles fall out naturally. ```ts function hybrid(vectors: Retriever, keywords: Retriever): Retriever { return { async retrieve(request) { const [v, k] = await Promise.all([ vectors.retrieve(request), keywords.retrieve(request), ]) return mergeAndRerank([...v, ...k]) // your choice of reranker }, } } createRuntime({ adapter, retriever: hybrid(vectorRAG, bm25) }) ``` The runtime never sees the difference. The composer is responsible for renormalizing scores from heterogeneous sources. ## Empty results are success, not failure ```ts const docs = await retriever.retrieve({ query: 'something we don't know about', messages: [] }) // docs is [], no error ``` `[]` means "no relevant documents" — a valid answer. Errors throw (`Promise` rejection); they are not encoded in the result. This is intentional asymmetry vs Adapter (errors are chunks) and Tool (errors are status). Retrievers are typically called once per turn, synchronously from the runtime's perspective. Wrapping every `retrieve` in error chunks would add ceremony for no gain. ## When to write your own - **You have a custom search backend** (internal docs, code search, ticket search, etc.) — implement `Retriever` and you're done. - **You're building a reranker, ensemble, or hybrid** — wrap N retrievers in your own. - **You're testing** — return a fixed array. ## Common pitfalls | Pitfall | What to do instead | |---|---| | Returning `null` instead of `[]` on no results | Always return an array. Empty is success. | | Mixing scored and unscored documents in one result | Either every doc has `score`, or none do (R6) | | Inconsistent score scale across calls | Pick one (cosine, BM25, normalized rank) and keep it for the lifetime of the retriever instance | | Mutating the input request | Treat `RetrieverRequest` as read-only | | Configuring topK per call | Configure at construction; the contract has no per-call options in v1 | ## Going deeper The full list of invariants (eleven of them, R1–R11) is in [ADR 0004 — Retriever contract](https://github.com/AgentsKit-io/agentskit/blob/main/docs/architecture/adrs/0004-retriever-contract.md). --- # Runtime Source: https://www.agentskit.io/docs/get-started/concepts/runtime > The conductor. Owns the loop, tool execution, memory persistence, retrieval, delegation, observability, and abort. The **Runtime** is where everything composes. It owns the loop: take a task, send it to the adapter, parse tool calls, execute tools, feed results back, decide when to stop. It also owns multi-agent delegation, observability, memory persistence, RAG retrieval, and confirmation gating. Every other concept (Adapter, Tool, Memory, Retriever, Skill) is **substrate**. The Runtime is the **conductor**. ## The interface ```ts import { createRuntime } from '@agentskit/runtime' const runtime = createRuntime(config) const result = await runtime.run(task, options?) ``` That's the whole surface. One factory. One method. No `start`, no `init`, no `step`. Streaming events come through observers, not extra methods. ## Configuration ```ts import type { RuntimeConfig } from '@agentskit/runtime' interface RuntimeConfig { adapter: AdapterFactory // required tools?: ToolDefinition[] systemPrompt?: string memory?: ChatMemory retriever?: Retriever observers?: Observer[] maxSteps?: number // default: 10 (hard cap) temperature?: number maxTokens?: number delegates?: Record maxDelegationDepth?: number // default: 3 onConfirm?: (call: ToolCall) => MaybePromise } ``` ## Running a task ```ts import { createRuntime } from '@agentskit/runtime' import { openai } from '@agentskit/adapters' import { webSearch, filesystem } from '@agentskit/tools' import { sqliteChatMemory } from '@agentskit/memory' const runtime = createRuntime({ adapter: openai({ apiKey: KEY, model: 'gpt-4o' }), tools: [webSearch(), ...filesystem({ basePath: './workspace' })], memory: sqliteChatMemory({ path: './sessions/agent-1.db' }), maxSteps: 10, }) const result = await runtime.run('Research the top 3 AI frameworks and save a summary') console.log(result.content) // the final assistant message console.log(result.steps) // think → act cycles taken console.log(result.toolCalls) // every tool call made console.log(result.messages) // full conversation including this run console.log(result.durationMs) // wall time ``` ## Hard step cap (non-negotiable) `maxSteps` is a **hard** cap. Every "infinite loop bug" in agent libraries traces to a soft cap the user can override. AgentsKit doesn't allow that. Pick a generous number for your use case, but the cap is the cap. ## Tool resolution order When the model emits a tool call, the runtime resolves it in this order: 1. `RunOptions.tools` (per-call) 2. `RuntimeConfig.tools` (per-runtime) 3. Tools contributed by an active skill via `onActivate` Last wins on name collision in the same scope. Later scopes shadow earlier ones. A name not found in any scope produces a tool error chunk back to the model — the runtime **does not throw**. The model can react and try a different tool. ## Memory atomicity If `memory` is configured, the runtime calls `load()` at the start of `run()` and `save()` after a successful run. **Failed or aborted runs do not save.** This preserves the ChatMemory atomicity invariant — your memory is never half-updated. ## Retrieval per turn If `retriever` is configured, `retrieve()` is called **once per `run()`** with the original task as the query. Results are inserted into the system prompt or as a context message. This is a deliberate v1 simplification. ReAct-style per-step retrieval is possible via a tool-shaped retriever or a custom runtime; we picked the simpler default. ## Observers (read-only telemetry) ```ts import type { AgentEvent, Observer } from '@agentskit/core' const consoleObserver: Observer = { name: 'console', on(event: AgentEvent) { if (event.type === 'llm:start') console.log('→ model') if (event.type === 'llm:end') console.log(`done in ${event.durationMs}ms`) if (event.type === 'tool:start') console.log(` ⚙ ${event.name}(${JSON.stringify(event.args)})`) if (event.type === 'tool:end') console.log(` ✓ ${event.name}`) }, } createRuntime({ adapter, tools, observers: [consoleObserver] }) ``` Observers see everything. Observers change nothing. If you want to mutate (rewrite tool calls, redact prompts), wrap the runtime — don't try to do it in an observer. Failures in observers are caught; they don't break the loop. ## Delegation ```ts import { planner, researcher, coder } from '@agentskit/skills' await runtime.run('Build a landing page about quantum computing', { skill: planner, delegates: { researcher: { skill: researcher, tools: [webSearch()], maxSteps: 3 }, coder: { skill: coder, tools: [...filesystem({ basePath: './src' })], maxSteps: 8 }, }, }) ``` Each delegate is materialized as a tool the model can call (`delegate_researcher`, `delegate_coder`). To the model: just another tool call. To the runtime: a recursive `run()` with depth tracking. `maxDelegationDepth` (default 3) is a behavioral cap — at the limit, delegates are simply not offered to the model. ## Aborting ```ts const controller = new AbortController() const promise = runtime.run('long task', { signal: controller.signal }) // Later... controller.abort() await promise // rejects with AbortError ``` When aborted: in-flight stream stops, loop exits, memory is **not** saved, observers receive `run-aborted`, the promise rejects. ## Errors are categorized The runtime distinguishes: | Category | Behavior | |---|---| | **Adapter error** | Loop terminates, error in result | | **Tool error** (returned or thrown) | Fed back to the model as tool result, loop continues | | **Confirmation refusal** | Fed back as tool error explaining the refusal | | **Memory / retriever error** | Loop terminates, error propagated | | **Programmer error** (bad config) | Throws synchronously from `createRuntime` or `run` start | This is what makes meaningful retry/fallback strategies possible. ## When to write your own runtime Almost never. Wrap the built-in runtime instead: - **Durable execution** — wrapper that persists state at each step, resumes from checkpoint. Same `run()` signature. - **Sandboxed execution** — wrapper that swaps every tool's `execute` with a sandboxed version. - **Replay runtime** — wrapper that asserts the loop matches a recorded trace. The contract is small enough that wrapping is cheaper than reimplementing. ## Common pitfalls | Pitfall | What to do instead | |---|---| | Setting `maxSteps: Infinity` "just to be safe" | Pick a generous finite number. The cap exists for a reason. | | Using observers to redact or mutate | Wrap the runtime | | Expecting memory to save on failure for "audit" purposes | Use an observer for audit logging; memory saves only on success | | Ignoring `abort` signal in long-running tools | Threading abort into tools is a follow-up; for now, use `maxTokens`/timeouts inside tools | | Confusing `tools` per-call vs per-runtime | Per-call options take precedence. Read RT3. | ## Going deeper The full list of invariants (fourteen of them, RT1–RT14) is in [ADR 0006 — Runtime contract](https://github.com/AgentsKit-io/agentskit/blob/main/docs/architecture/adrs/0006-runtime-contract.md). --- # Skill Source: https://www.agentskit.io/docs/get-started/concepts/skill > A persona the model becomes — a system prompt plus optional examples, tools, and delegates. Pure declarative configuration. A **Skill** is a packaged behavior. A system prompt that turns a generic LLM into a domain-specialized agent: a researcher, a critic, a code reviewer, a customer support triager. Optional examples make it few-shot. Optional tool and delegate references make it composable. Skills are not tools. A tool is a function the model **calls**. A skill is a persona the model **becomes**. Confusing the two produces the LangChain "agents" mess where you can't tell what `.invoke()` versus `.run()` versus `.stream()` does. ## The interface ```ts import type { SkillDefinition } from '@agentskit/core' export interface SkillDefinition { name: string description: string systemPrompt: string examples?: Array<{ input: string; output: string }> tools?: string[] // tool names, resolved at activation delegates?: string[] // skill names, resolved at activation temperature?: number metadata?: Record onActivate?: () => MaybePromise<{ tools?: ToolDefinition[] }> } ``` Notice: **no `run()` method**. A skill is not invokable. The runtime activates it and proceeds with normal model calls plus the skill's prompt, tools, and temperature. ## Defining a skill ```ts import type { SkillDefinition } from '@agentskit/core' export const codeReviewer: SkillDefinition = { name: 'code_reviewer', description: 'Reviews TypeScript pull requests for bugs, missing tests, and style.', systemPrompt: `You are a meticulous senior TypeScript engineer reviewing a PR. Always: - Read the diff fully before commenting - Flag missing tests as severity:high - Flag any 'any' usage with a concrete suggestion - Approve only after you've checked the test runner output - End with a one-line summary verdict`, tools: ['filesystem_read', 'shell'], examples: [ { input: 'Review the diff in PR #42', output: '...comment-by-comment review with a verdict at the end...', }, ], temperature: 0.2, } ``` The skill is just data. It can be serialized, versioned, traded in a marketplace, or generated by another agent. ## Using a built-in skill ```ts import { researcher, critic, planner } from '@agentskit/skills' const result = await runtime.run('What changed in JavaScript runtimes in the last year?', { skill: researcher, }) ``` ## Tools by reference Skills don't define tools inline. They reference tool **names**, and the runtime resolves them against the registry at activation: ```ts const supportTriager: SkillDefinition = { name: 'support_triager', description: 'Routes tickets to the right team.', systemPrompt: '...', tools: ['lookup_customer', 'list_open_tickets', 'tag_ticket'], } createRuntime({ adapter, tools: [lookupCustomer, listOpenTickets, tagTicket], }) await runtime.run('Triage all unassigned tickets', { skill: supportTriager }) ``` This is what enables tool reuse across skills, and what makes the marketplace possible. ## Delegation: skills calling skills ```ts import { planner, researcher, coder } from '@agentskit/skills' await runtime.run('Build a landing page about quantum computing', { skill: planner, delegates: { researcher: { skill: researcher, tools: [webSearch()], maxSteps: 3 }, coder: { skill: coder, tools: [...filesystem({ basePath: './src' })], maxSteps: 8 }, }, }) ``` The runtime materializes each delegate as a tool the planner can call (`delegate_researcher`, `delegate_coder`). To the model, delegation is just another tool call. To the runtime, it's a recursive `run()` with depth tracking. ## Dynamic per-user tools (`onActivate`) The one and only escape hatch in an otherwise-pure-data contract: ```ts const supportAgent: SkillDefinition = { name: 'support_agent', description: 'Acts on behalf of the current user.', systemPrompt: '...', async onActivate() { const userToken = await getCurrentUserToken() return { tools: [createGmailTool(userToken), createCalendarTool(userToken)], } }, } ``` Use this **only** for credential-bound tools that can't be constructed at the registry level. General initialization belongs elsewhere. ## When to write your own Almost always, if the built-in skills don't cover your domain. A skill is a system prompt plus a few fields — it's the cheapest concept in AgentsKit to author. The hard part is the prompt itself. Read existing skills (`packages/skills/src/researcher.ts`, `critic.ts`, `coder.ts`, `summarizer.ts`, `planner.ts`) to see the prompt patterns we use. They're not magic; they're disciplined. ## Common pitfalls | Pitfall | What to do instead | |---|---| | Putting tool implementations inside the skill | Reference by name; register the tool with the runtime | | Doing I/O when defining the skill (top-level await) | Move it into `onActivate` or out of the skill entirely | | Trying to call `skill.run()` | Skills aren't invokable — pass them to `runtime.run({ skill })` | | Multi-turn examples encoded as one input/output | Encode the demonstration in `systemPrompt` prose for v1 | | Skill versioning via filename | Use `metadata.version` until the manifest spec lands | ## Going deeper The full list of invariants (twelve of them, S1–S12) is in [ADR 0005 — Skill contract](https://github.com/AgentsKit-io/agentskit/blob/main/docs/architecture/adrs/0005-skill-contract.md). --- # Tool Source: https://www.agentskit.io/docs/get-started/concepts/tool > A function the model can call, with JSON-schema-typed arguments and a JSON-serializable result. A **Tool** is a function the model can request by name. It is the primary mechanism by which an agent acts on the world: reading files, calling APIs, searching the web, executing code, editing documents, sending Slack messages. If a Skill is what the model **becomes**, a Tool is what the model **calls**. That distinction is load-bearing — confusing the two is the recurring failure mode in agent libraries. ## The interface ```ts import type { ToolDefinition } from '@agentskit/core' import type { JSONSchema7 } from 'json-schema' export interface ToolDefinition { name: string description?: string schema?: JSONSchema7 requiresConfirmation?: boolean execute?: ( args: Record, context: ToolExecutionContext, ) => MaybePromise | AsyncIterable init?: () => MaybePromise dispose?: () => MaybePromise tags?: string[] category?: string } ``` ## Defining a tool ```ts import type { ToolDefinition } from '@agentskit/core' export const getWeather: ToolDefinition = { name: 'get_weather', description: 'Get the current weather for a city.', schema: { type: 'object', properties: { city: { type: 'string', description: 'City name, e.g. "Madrid"' }, }, required: ['city'], }, async execute(args) { const res = await fetch(`https://wttr.in/${args.city}?format=j1`) return await res.json() }, } ``` The model sees `name`, `description`, and `schema`. The runtime validates `args` against `schema` before calling `execute`. Your function never receives malformed input. ## Using built-in tools ```ts import { webSearch, filesystem, shell } from '@agentskit/tools' createRuntime({ adapter, tools: [ webSearch(), ...filesystem({ basePath: './workspace' }), shell({ allowed: ['ls', 'cat'] }), ], }) ``` ## Confirmation gates Set `requiresConfirmation: true` and the runtime will pause before executing, asking your `onConfirm` handler: ```ts const dangerousTool: ToolDefinition = { name: 'delete_file', schema: { /* ... */ }, requiresConfirmation: true, async execute(args) { /* ... */ }, } createRuntime({ adapter, tools: [dangerousTool], onConfirm: async (call) => { return await showConfirmationDialog(call) }, }) ``` There is no timeout-based auto-approval. If the runtime requires confirmation but no `onConfirm` is configured, execution is refused. This matters for security-critical tools and is non-negotiable. ## Streaming tool execution Long-running tools can yield progress as an `AsyncIterable`: ```ts const slowTool: ToolDefinition = { name: 'process_dataset', schema: { /* ... */ }, async *execute(args) { yield 'Loading dataset...' const data = await loadDataset(args.path) yield `Processing ${data.length} rows...` const result = await process(data) return result // the last yielded value is the recorded result }, } ``` Consumers see partial values as informational; only the last one is recorded as the official result. ## Declaration-only tools (MCP-friendly) A tool without `execute` is a **declaration** — the model can call it, and the caller (typically a client or an MCP bridge) handles the actual execution. This is what makes the AgentsKit Tool contract a thin mapping over MCP's tool spec. ```ts const remoteTool: ToolDefinition = { name: 'lookup_customer', description: 'Find a customer by email.', schema: { type: 'object', properties: { email: { type: 'string' } } }, // no execute — runs elsewhere } ``` ## Type-safe tools with `defineTool` Writing `ToolDefinition` objects directly types `args` as `Record`, which means you must cast inside `execute`. The `defineTool` factory infers the args type directly from the JSON Schema so you get full type-safety and autocomplete at zero runtime cost. ```ts import { defineTool } from '@agentskit/core' const createTask = defineTool({ name: 'create_task', description: 'Create a task in the project tracker.', schema: { type: 'object', properties: { title: { type: 'string' }, priority: { type: 'string', enum: ['low', 'medium', 'high'] }, dueDate: { type: 'string' }, }, required: ['title', 'priority'], } as const, // <-- required for inference async execute(args) { // args.title → string (required, inferred) // args.priority → string (required, inferred) // args.dueDate → string | undefined (optional, inferred) return await tracker.create({ title: args.title, priority: args.priority, dueDate: args.dueDate }) }, }) ``` The `as const` assertion on `schema` narrows the literal types so `InferSchemaType` can map them to TypeScript. Without it, `args` falls back to `Record`. ### Using the inferred type standalone ```ts import type { InferSchemaType } from '@agentskit/core' const schema = { type: 'object', properties: { query: { type: 'string' }, limit: { type: 'integer' }, }, required: ['query'], } as const type SearchArgs = InferSchemaType // → { query: string; limit?: number } ``` ## Zod-based tools with `defineZodTool` If you already use [Zod](https://zod.dev) in your project, `@agentskit/tools` ships `defineZodTool` — a factory that types `execute` args from a Zod schema and also validates them at runtime via `schema.parse`. Zod is **not bundled** — it must be installed as a peer dependency. You also supply the JSON Schema conversion yourself (e.g. via `zod-to-json-schema`), which keeps the Zod dependency fully optional. ```ts import { z } from 'zod' import { zodToJsonSchema } from 'zod-to-json-schema' import { defineZodTool } from '@agentskit/tools' import type { JSONSchema7 } from 'json-schema' const sendEmail = defineZodTool({ name: 'send_email', description: 'Send an email to a recipient.', schema: z.object({ to: z.string().email(), subject: z.string(), body: z.string(), cc: z.string().email().optional(), }), toJsonSchema: (s) => zodToJsonSchema(s) as JSONSchema7, async execute(args) { // args.to → string (Zod-validated at runtime) // args.subject → string // args.body → string // args.cc → string | undefined await mailer.send({ to: args.to, subject: args.subject, body: args.body, cc: args.cc }) return { sent: true } }, }) ``` `defineZodTool` wraps `execute` to call `schema.parse(args)` before your function runs. Invalid input raises a Zod `ZodError` — the runtime surfaces it as a tool error rather than crashing the agent. ### Install peer dependencies ```bash npm install zod zod-to-json-schema ``` ### When to use `defineTool` vs `defineZodTool` | | `defineTool` | `defineZodTool` | |---|---|---| | **Deps** | none (core only) | zod + zod-to-json-schema | | **Runtime validation** | no | yes (via `schema.parse`) | | **Schema authoring** | JSON Schema (`as const`) | Zod API | | **JSON Schema for adapter** | you write it | converted automatically | Use `defineTool` when you want zero extra dependencies and are comfortable with JSON Schema. Use `defineZodTool` when Zod is already in your stack and you want runtime validation as a safety net. ## Common pitfalls | Pitfall | What to do instead | |---|---| | Returning a Date or Buffer from `execute` | Serialize: `date.toISOString()`, `buffer.toString('base64')` | | Throwing from `execute` on a recoverable error | Return `{ error: '...' }` so the model can react and retry | | Implementing your own confirmation timeout | Don't. Use `requiresConfirmation` + `onConfirm` properly | | Doing I/O at definition time (top-level `await`) | Move it into `init()` or `execute()` — see invariant T10 | | Naming tools with spaces or special characters | Names must match `^[a-zA-Z_][a-zA-Z0-9_-]{0,63}$` | ## Going deeper The full list of invariants (twelve of them, T1–T12) is in [ADR 0002 — Tool contract](https://github.com/AgentsKit-io/agentskit/blob/main/docs/architecture/adrs/0002-tool-contract.md). --- # For AI Agents Source: https://www.agentskit.io/docs/get-started/getting-started/for-ai-agents > The entire AgentsKit API in one page. Paste this into your LLM context. The entire AgentsKit API in one page. Paste this into your LLM context. ## Chat (React / Ink) ```tsx import { useChat, ChatContainer, Message, InputBar } from '@agentskit/react' // or @agentskit/ink import { anthropic } from '@agentskit/adapters' const chat = useChat({ adapter: anthropic({ apiKey, model: 'claude-sonnet-4-6' }), tools, skills }) chat.send('message') // send and stream response chat.stop() // abort stream chat.retry() // retry last chat.messages // Message[] chat.status // 'idle' | 'streaming' | 'error' ``` ## Adapters — swap in one line ```ts import { anthropic, openai, gemini, ollama, deepseek, grok, kimi, vercelAI, generic } from '@agentskit/adapters' anthropic({ apiKey, model }) // openai({ apiKey, model }) gemini({ apiKey, model }) // ollama({ model }) vercelAI({ api: '/api/chat' }) // generic({ send: async (msgs) => ReadableStream }) ``` ## Running Agents (no UI) ```ts import { createRuntime } from '@agentskit/runtime' const runtime = createRuntime({ adapter, tools, skills, observers, memory }) const result = await runtime.run('task', { skill, delegates, signal }) // → { content, messages, steps, toolCalls, durationMs } ``` ## Tools ```ts import { webSearch, filesystem, shell, listTools } from '@agentskit/tools' webSearch() // DuckDuckGo (no key) webSearch({ provider: 'serper', apiKey }) // Serper filesystem({ basePath: './workspace' }) // → [read_file, write_file, list_directory] shell({ timeout: 10_000, allowed: ['ls', 'cat'] }) ``` ## Skills ```ts import { researcher, coder, planner, critic, summarizer, composeSkills } from '@agentskit/skills' runtime.run('task', { skill: researcher }) const combined = composeSkills(researcher, coder) // merges prompts, tools, delegates ``` ## Multi-Agent Delegation ```ts runtime.run('Build a landing page', { delegates: { researcher: { skill: researcher, tools: [webSearch()], maxSteps: 3 }, coder: { skill: coder, tools: [...filesystem({ basePath: './src' })] }, }, sharedContext: createSharedContext(), }) ``` ## Memory ```ts import { sqliteChatMemory, fileVectorMemory } from '@agentskit/memory' sqliteChatMemory({ path: './chat.db' }) // ChatMemory fileVectorMemory({ path: './vectors' }) // VectorMemory (vectra) ``` ## RAG ```ts import { createRAG } from '@agentskit/rag' const rag = createRAG({ embed: openaiEmbedder({ apiKey }), store: fileVectorMemory({ path: './v' }) }) await rag.ingest(documents) const docs = await rag.retrieve('query') ``` ## Observability ```ts import { consoleLogger, langsmith, opentelemetry } from '@agentskit/observability' createRuntime({ adapter, observers: [consoleLogger(), langsmith({ apiKey })] }) ``` ## Sandbox ```ts import { sandboxTool } from '@agentskit/sandbox' // → ToolDefinition for secure JS/Python execution via E2B ``` ## Eval ```ts import { runEval } from '@agentskit/eval' const result = await runEval({ agent: async (input) => runtime.run(input).then(r => r.content), suite: { name: 'qa', cases: [{ input: 'Q', expected: 'A' }] }, }) // → { accuracy, passed, failed, results: [{ latencyMs, tokenUsage? }] } ``` ## Types ```ts Message { id, role, content, status, toolCalls?, metadata?, createdAt } ToolCall { id, name, args, result?, error?, status } ToolDefinition { name, description?, schema?, execute?, init?, dispose?, tags?, category? } SkillDefinition { name, description, systemPrompt, tools?, delegates?, onActivate? } RunResult { content, messages, steps, toolCalls, durationMs } AgentEvent = 'llm:start' | 'llm:end' | 'tool:start' | 'tool:end' | 'agent:step' | 'agent:delegate:start' | ... ``` --- # Installation Source: https://www.agentskit.io/docs/get-started/getting-started/installation > Install the AgentsKit packages you need — and only those. Every AgentsKit package is independently installable. Pick what you need. ![agentskit install](/demos/install.gif) ## React chat UI ```bash npm install @agentskit/react @agentskit/adapters ``` ## Terminal chat (Ink) ```bash npm install @agentskit/ink @agentskit/adapters ``` ## Headless agent runtime ```bash npm install @agentskit/runtime @agentskit/adapters ``` ## Adding tools, skills, memory, RAG ```bash npm install @agentskit/tools @agentskit/skills @agentskit/memory @agentskit/rag ``` ## CLI ```bash npm install -g @agentskit/cli agentskit init ``` ### Scaffold a project ![agentskit init](/demos/init.gif) ### Diagnose your setup Run `agentskit doctor` to verify your environment, installed packages, and provider configuration in one command. ![agentskit doctor output](/demos/doctor.gif) ### Run an ad-hoc agent Stream a real LLM task straight from the terminal — no project required. ![agentskit run streaming](/demos/chat-streaming.gif) ## Why so many packages? Because every package is a **product** with its own contract, its own versioning, and its own substitutability. A React app pulling in `@agentskit/ink` would ship dead code; a Node service pulling in `@agentskit/react` would crash. The 10KB `@agentskit/core` is the only thing every package transitively depends on — and it stays under 10KB by manifesto. --- # Quick start Source: https://www.agentskit.io/docs/get-started/getting-started/quickstart > Build a working AI chat in under 10 lines. ## Basic chat ```tsx title="app/chat.tsx" import { useChat, ChatContainer, Message, InputBar } from '@agentskit/react' import { anthropic } from '@agentskit/adapters' import '@agentskit/react/theme' function App() { const chat = useChat({ adapter: anthropic({ apiKey: 'your-key', model: 'claude-sonnet-4-6' }), }) return ( {chat.messages.map(msg => ( ))} ) } ``` ## What you got | Component | Role | | --- | --- | | `useChat` | A chat session bound to an adapter; manages messages, streaming, errors | | `ChatContainer` | Auto-scrolling layout | | `Message` | Renders one message with streaming support | | `InputBar` | Text input that sends on Enter | Out of the box: streaming, message history, default styling. No extra setup. ## Swap providers The adapter is the only thing that changes. Everything else stays the same. ```tsx import { openai } from '@agentskit/adapters' const chat = useChat({ adapter: openai({ apiKey: 'your-key', model: 'gpt-4o' }), }) ``` This is the **plug-and-play** principle in practice — no rewrites for a provider swap. ## Headless mode Skip the theme import and style everything yourself — components render with `data-ak-*` attributes only: ```tsx import { useChat, ChatContainer, Message, InputBar } from '@agentskit/react' // No theme import function App() { const chat = useChat({ adapter: myAdapter }) return ( {chat.messages.map(msg => ( ))} ) } ``` ## Try the official examples - `apps/example-react` — full chat with tool calls and memory - `apps/example-ink` — same chat in your terminal - `apps/example-runtime` — autonomous agent (no UI) - `apps/example-multi-agent` — supervisor + workers --- # Start here Source: https://www.agentskit.io/docs/get-started/getting-started/read-this-first > Three steps to land. Copy a working chat, pick your path, ship. ## Three steps 1. **[Quick start](./quickstart)** — copy-paste a streaming chat. Swap the adapter in one line. 2. **[Packages overview](/docs/reference/packages/overview)** — all 21 `@agentskit/*` packages grouped by role. 3. **Open the guide for what you ship** — React, Ink, runtime, RAG, evals. Follow the links from the overview. ## Reference - **Signatures and types:** [Generated API (TypeDoc)](pathname:///agentskit/api-reference/) — run `pnpm --filter @agentskit/docs docs:api` once for local previews. - **Install cheat sheet:** [Installation](./installation). - **Mental model:** [How the six contracts compose](/docs/get-started/concepts/mental-model). ## Pick a path - Chat UI → [Quick start](./quickstart) - Autonomous agent → [Runtime](/docs/agents/runtime) - RAG pipeline → [createRAG](/docs/data/rag/create-rag) - Production ops → [Observability](/docs/production/observability) · [Security](/docs/production/security) · [Evals](/docs/production/evals) --- # Migrating to AgentsKit Source: https://www.agentskit.io/docs/get-started/migrating > Guides for moving from other frameworks. Honest about what transfers easily and what doesn't. AgentsKit isn't in conflict with the libraries you're already using — but if you're hitting their limits, these guides walk you through the move. | From | Guide | |---|---| | Vercel AI SDK | [`from-vercel-ai-sdk`](/docs/get-started/migrating/from-vercel-ai-sdk) | | LangChain.js | [`from-langchain`](/docs/get-started/migrating/from-langchain) | | Mastra | [`from-mastra`](/docs/get-started/migrating/from-mastra) | ## Before you migrate Read the "[When you should NOT use AgentsKit](../../#when-you-should-not-use-agentskit)" section of the README. If your use case falls in there, stay where you are — that's the honest answer. ## What stays the same Regardless of what you're coming from: - **Your prompts** — system prompts, few-shot examples, tool descriptions transfer as-is - **Your tool implementations** — the functions that do the work (fetch, read file, call API) don't change - **Your provider accounts** — same API keys, same billing ## What changes - **The shape of the client library** — imports, hooks, class instances, method names - **How tools are declared** — AgentsKit uses a `ToolDefinition` object with `schema` (JSON Schema 7) - **How the agent loop runs** — AgentsKit's `createRuntime` owns `maxSteps`, delegation, memory, retrieval ## Philosophy Migration guides here are **honest**: we show the before/after, call out what AgentsKit does better, and call out what the other framework does better. If we can't beat the other option on a given axis, we say so. --- # From LangChain.js Source: https://www.agentskit.io/docs/get-started/migrating/from-langchain > Side-by-side migration guide. Map your LangChain.js code to AgentsKit — with honest callouts about where LangChain still fits. import { ContributeCallout } from '@/components/contribute/contribute-callout' LangChain.js is the Swiss Army knife of JS agent libraries. It does a lot, and if it fits your use case it gets you moving fast. Come to AgentsKit when you hit one of these: - You're tired of **200MB of transitive dependencies** and slow cold starts - You want **one way to do one thing** — not three overlapping abstractions per feature - You need **small, formal contracts** to build on top of, not a big flexible base class - You want to **ship to the edge** (Cloudflare Workers, Deno Deploy) without reaching for a subset This page maps the LangChain.js patterns you probably have to AgentsKit equivalents. ## Quick reference | LangChain.js | AgentsKit | Notes | |---|---|---| | `new ChatOpenAI({ model })` | `openai({ apiKey, model })` from `@agentskit/adapters` | Returns an `AdapterFactory` | | `.invoke(messages)` / `.stream(messages)` | `adapter.createSource({ messages }).stream()` | Single streaming API, see [Concepts: Adapter](../concepts/adapter) | | `ChatPromptTemplate.fromMessages([...])` | Plain strings or a `SkillDefinition.systemPrompt` | No template engine in core | | `StructuredTool` / `tool()` | `ToolDefinition` with `schema` (JSON Schema 7) | Convert Zod → JSON Schema if needed | | `AgentExecutor` | `createRuntime({ ... }).run(task)` | See [Concepts: Runtime](../concepts/runtime) | | `BufferMemory`, `ConversationBufferMemory` | `sqliteChatMemory`, `redisChatMemory`, `fileChatMemory` | Split from vector memory per [ADR 0003](https://github.com/AgentsKit-io/agentskit/blob/main/docs/architecture/adrs/0003-memory-contract.md) | | `VectorStore` + `OpenAIEmbeddings` | `VectorMemory` + `EmbedFn` | Same split into two narrow contracts | | `RetrievalQAChain` | `createRAG({ store, embed })` as a `Retriever` | Drop it into `createRuntime({ retriever })` | | `RunnableSequence` / LCEL | Plain functions, `runtime.run()`, or a composite runtime | AgentsKit doesn't have a DSL; composition is JavaScript | | `LangGraph` state machine | Multi-agent via `delegates` on the runtime | Topologies covered by one primitive (RT10) | | Callbacks / Handlers | `Observer[]` in runtime config | Read-only, composable | ## 1. Basic chat with streaming ### Before — LangChain.js ```ts import { ChatOpenAI } from '@langchain/openai' const chat = new ChatOpenAI({ model: 'gpt-4o', apiKey: process.env.OPENAI_API_KEY, streaming: true, }) const stream = await chat.stream([ { role: 'system', content: 'You are a helpful assistant.' }, { role: 'user', content: 'Hello!' }, ]) for await (const chunk of stream) { process.stdout.write(chunk.content as string) } ``` ### After — AgentsKit ```ts import { openai } from '@agentskit/adapters' const adapter = openai({ apiKey: process.env.OPENAI_API_KEY!, model: 'gpt-4o' }) const source = adapter.createSource({ messages: [ { id: '1', role: 'system', content: 'You are a helpful assistant.' }, { id: '2', role: 'user', content: 'Hello!' }, ], }) for await (const chunk of source.stream()) { if (chunk.type === 'text') process.stdout.write(chunk.content ?? '') } ``` **What's different** - No class — `openai()` returns a plain `AdapterFactory` - Streaming is the default; you don't set `streaming: true` - Chunks are tagged by `type` (`text`, `tool_call`, `done`, etc.) — you filter rather than assume every chunk is text - Every stream ends with a `done` chunk (never silently) — see [ADR 0001](https://github.com/AgentsKit-io/agentskit/blob/main/docs/architecture/adrs/0001-adapter-contract.md) A3 ## 2. Prompts and templates ### Before — LangChain.js ```ts import { ChatPromptTemplate } from '@langchain/core/prompts' import { ChatOpenAI } from '@langchain/openai' const prompt = ChatPromptTemplate.fromMessages([ ['system', 'You are a {role}. Keep answers under {maxWords} words.'], ['user', '{question}'], ]) const chat = new ChatOpenAI({ model: 'gpt-4o' }) const chain = prompt.pipe(chat) const res = await chain.invoke({ role: 'code reviewer', maxWords: 100, question: 'Review this diff: ...', }) ``` ### After — AgentsKit Two options, depending on whether this is a one-off or a reusable persona. **One-off**: plain JavaScript template literals. ```ts const role = 'code reviewer' const maxWords = 100 const systemPrompt = `You are a ${role}. Keep answers under ${maxWords} words.` const runtime = createRuntime({ adapter: openai({ apiKey: KEY, model: 'gpt-4o' }), systemPrompt, }) const result = await runtime.run('Review this diff: ...') ``` **Reusable**: a `SkillDefinition`. ```ts import type { SkillDefinition } from '@agentskit/core' const codeReviewer: SkillDefinition = { name: 'code_reviewer', description: 'Reviews code changes concisely.', systemPrompt: 'You are a code reviewer. Keep answers under 100 words.', } const result = await runtime.run('Review this diff: ...', { skill: codeReviewer }) ``` **Why no template engine**: the 10KB core (Manifesto principle 1) can't carry one, and JavaScript already has template literals and string interpolation. If you need conditional prompt logic, that's just JavaScript. ## 3. Tool calling ### Before — LangChain.js ```ts import { tool } from '@langchain/core/tools' import { z } from 'zod' import { ChatOpenAI } from '@langchain/openai' const getWeather = tool( async ({ city }) => { const res = await fetch(`https://wttr.in/${city}?format=j1`) return await res.json() }, { name: 'get_weather', description: 'Get the weather for a city', schema: z.object({ city: z.string() }), }, ) const model = new ChatOpenAI({ model: 'gpt-4o' }).bindTools([getWeather]) ``` ### After — AgentsKit ```ts import type { ToolDefinition } from '@agentskit/core' import { createRuntime } from '@agentskit/runtime' import { openai } from '@agentskit/adapters' const getWeather: ToolDefinition = { name: 'get_weather', description: 'Get the weather for a city', schema: { type: 'object', properties: { city: { type: 'string' } }, required: ['city'], }, async execute(args) { const res = await fetch(`https://wttr.in/${args.city}?format=j1`) return await res.json() }, } const runtime = createRuntime({ adapter: openai({ apiKey: KEY, model: 'gpt-4o' }), tools: [getWeather], }) ``` **Zod → JSON Schema bridge**: if you're attached to Zod, keep it as the source of truth and convert: ```ts import { zodToJsonSchema } from 'zod-to-json-schema' import { z } from 'zod' const schema = z.object({ city: z.string() }) const getWeather: ToolDefinition = { name: 'get_weather', schema: zodToJsonSchema(schema) as JSONSchema7, async execute(args) { /* ... */ }, } ``` **What you gain**: confirmation gates (`requiresConfirmation: true` + `onConfirm`), streaming tool execution (return an `AsyncIterable`), execute-optional tools (MCP-friendly declarations). See [Concepts: Tool](../concepts/tool). ## 4. AgentExecutor → Runtime This is the biggest win. `AgentExecutor` is a catch-all with many knobs. `createRuntime` is a single factory with formal invariants. ### Before — LangChain.js ```ts import { AgentExecutor, createReactAgent } from 'langchain/agents' import { ChatPromptTemplate } from '@langchain/core/prompts' import { ChatOpenAI } from '@langchain/openai' import { tool } from '@langchain/core/tools' const prompt = await ChatPromptTemplate.fromMessages([ ['system', 'You are a research assistant.'], ['human', '{input}'], ['placeholder', '{agent_scratchpad}'], ]) const agent = await createReactAgent({ llm: new ChatOpenAI({ model: 'gpt-4o' }), tools: [webSearchTool, filesystemTool], prompt, }) const executor = new AgentExecutor({ agent, tools: [webSearchTool, filesystemTool], maxIterations: 10, returnIntermediateSteps: true, }) const result = await executor.invoke({ input: 'Research the top 3 AI frameworks' }) ``` ### After — AgentsKit ```ts import { createRuntime } from '@agentskit/runtime' import { openai } from '@agentskit/adapters' import { webSearch, filesystem } from '@agentskit/tools' const runtime = createRuntime({ adapter: openai({ apiKey: KEY, model: 'gpt-4o' }), tools: [webSearch(), ...filesystem({ basePath: './workspace' })], systemPrompt: 'You are a research assistant.', maxSteps: 10, }) const result = await runtime.run('Research the top 3 AI frameworks') console.log(result.content) // final answer console.log(result.steps) // iterations taken console.log(result.toolCalls) // every tool call with args + result console.log(result.messages) // full conversation console.log(result.durationMs) ``` **What's different** - No separate agent/executor split — `createRuntime` is the composition point - `maxSteps` is a **hard** cap (RT4). Every agent library's worst bug — infinite loops from soft caps — is ruled out by contract. - Intermediate steps are on the result by default (no `returnIntermediateSteps` flag) - Adding memory, retrieval, delegation means adding one field to the config — no new class ## 5. Memory LangChain's memory hierarchy (BufferMemory, ConversationBufferMemory, ConversationSummaryMemory, VectorStoreRetrieverMemory) collapses to **two** contracts in AgentsKit. ### Before — LangChain.js ```ts import { BufferMemory } from 'langchain/memory' import { ChatOpenAI } from '@langchain/openai' import { ConversationChain } from 'langchain/chains' const memory = new BufferMemory() const chat = new ConversationChain({ llm: new ChatOpenAI({ model: 'gpt-4o' }), memory, }) await chat.invoke({ input: 'My name is Ava.' }) await chat.invoke({ input: 'What is my name?' }) // remembers ``` ### After — AgentsKit ```ts import { createRuntime } from '@agentskit/runtime' import { openai } from '@agentskit/adapters' import { sqliteChatMemory } from '@agentskit/memory' const runtime = createRuntime({ adapter: openai({ apiKey: KEY, model: 'gpt-4o' }), memory: sqliteChatMemory({ path: './session.db' }), }) await runtime.run('My name is Ava.') await runtime.run('What is my name?') // persists across processes too ``` **What you gain**: atomicity — failed or aborted runs don't save (RT7 + CM4). No half-updated memory state. ## 6. Retrieval (RetrievalQAChain) ### Before — LangChain.js ```ts import { MemoryVectorStore } from 'langchain/vectorstores/memory' import { OpenAIEmbeddings } from '@langchain/openai' import { RetrievalQAChain } from 'langchain/chains' import { ChatOpenAI } from '@langchain/openai' const vectorStore = await MemoryVectorStore.fromTexts( texts, texts.map((_, i) => ({ id: i })), new OpenAIEmbeddings(), ) const chain = RetrievalQAChain.fromLLM( new ChatOpenAI({ model: 'gpt-4o' }), vectorStore.asRetriever(), ) const res = await chain.invoke({ query: 'What was said about X?' }) ``` ### After — AgentsKit ```ts import { createRuntime } from '@agentskit/runtime' import { openai, openaiEmbedder } from '@agentskit/adapters' import { createRAG } from '@agentskit/rag' import { fileVectorMemory } from '@agentskit/memory' const rag = createRAG({ store: fileVectorMemory({ path: './embeddings.json' }), embed: openaiEmbedder({ apiKey: KEY, model: 'text-embedding-3-small' }), }) await rag.ingest(texts.map((content, i) => ({ id: String(i), content }))) const runtime = createRuntime({ adapter: openai({ apiKey: KEY, model: 'gpt-4o' }), retriever: rag, }) const result = await runtime.run('What was said about X?') ``` **What you gain**: `RAG` is a `Retriever`. So is a web search tool. So is a memory recall. Same shape, composable for reranking and hybrid search without a new primitive. See [Concepts: Retriever](../concepts/retriever). ## 7. Chains / LCEL (RunnableSequence) LCEL is a DSL embedded in LangChain. AgentsKit doesn't have one — **composition is plain JavaScript**. ### Before — LangChain.js ```ts import { RunnableSequence } from '@langchain/core/runnables' import { StringOutputParser } from '@langchain/core/output_parsers' const chain = RunnableSequence.from([ prompt, model, new StringOutputParser(), ]) const result = await chain.invoke({ topic: 'quantum computing' }) ``` ### After — AgentsKit ```ts async function explain(topic: string): Promise { const result = await runtime.run(`Explain ${topic} in 100 words.`) return result.content } const result = await explain('quantum computing') ``` That's it. If you want reusable composition across functions, extract more functions. If you want ordered multi-step orchestration, use delegation (next section). ## 8. LangGraph → delegation LangGraph is LangChain's state-machine workflow engine. AgentsKit covers the common patterns (supervisor, swarm, hierarchical, blackboard) via `delegates` on the runtime. See [Concepts: Runtime](../concepts/runtime) RT10 and [Recipe: Multi-agent research team](../recipes/research-team). Complex, long-running, checkpointed workflows are **Phase 3** in AgentsKit (durable execution, #156). Until that lands, if your workflow genuinely needs stateful graph semantics — stay on LangGraph for that piece. ## 9. Callbacks → Observers ### Before — LangChain.js ```ts const model = new ChatOpenAI({ callbacks: [ { handleLLMStart: (llm, prompts) => console.log('→', prompts), handleLLMEnd: (out) => console.log('←', out.llmOutput?.tokenUsage), }, ], }) ``` ### After — AgentsKit ```ts import type { Observer } from '@agentskit/core' const telemetry: Observer = { name: 'telemetry', on(event) { if (event.type === 'llm:start') console.log('→ model') if (event.type === 'llm:end') console.log(`${event.durationMs}ms`, event.usage) if (event.type === 'agent:step') console.log(`step ${event.step}: ${event.action}`) }, } createRuntime({ adapter, tools, observers: [telemetry] }) ``` Observers are read-only (RT9), composable (it's an array), and first-class in the contract. ## Where LangChain.js still wins Honest callouts — choose LangChain over AgentsKit when: - **You need an integration AgentsKit doesn't have yet.** LangChain's integration catalog is vast; ours is focused. If your vector store / loader / obscure provider only has a LangChain integration, write a small adapter to AgentsKit or stay on LangChain for that piece. - **You already use LangSmith for tracing and evals.** That ecosystem is deep. We'll integrate — observers make it straightforward — but if you rely on LangSmith-specific features, stay put. - **You need LangGraph's explicit state-machine workflows today.** Durable checkpointed graph execution is Fase 3 in AgentsKit. Not parity yet. - **You want the "one library, many solutions" model.** AgentsKit deliberately has fewer ways to do things. If flexibility at the cost of indirection is your preference, LangChain is designed for it. - **You're in a team where everyone already knows LangChain.** The migration cost isn't zero. Weigh the runtime + contracts + 10KB core against that. If none of those apply and the dependency weight / abstraction leakage is hurting, migrate. ## Incremental migration You don't have to go all-in. A pragmatic path: 1. **Keep LangChain for existing pipelines** — don't rewrite working code 2. **Adopt AgentsKit for new features** — a CLI agent, a terminal chat, a new autonomous workflow 3. **Port the chat layer first** — `@agentskit/react` components + `createRuntime` 4. **Migrate tools next** — `ToolDefinition` is a flat object, easier to read than `StructuredTool` 5. **Consolidate memory + retrieval** when you want the atomicity guarantees 6. **Leave LangGraph in place** until durable execution lands in Fase 3 The Adapter contract is the pivot point: once you have one `AdapterFactory`, every surface in AgentsKit accepts it. ## Dependency size check Run this on your project and compare: ```bash du -sh node_modules/@langchain node_modules/langchain 2>/dev/null | awk '{ sum += $1 } END { print sum "MB of LangChain" }' du -sh node_modules/@agentskit 2>/dev/null | awk '{ print $1 " of AgentsKit" }' ``` The difference is typically **100–200 MB** in a modest project. That's cold starts, CI time, install time, container size, and blast radius for transitive CVE advisories. ## Related - [Concepts: Runtime](../concepts/runtime) — the `createRuntime` anchor - [Recipe: Chat with RAG](../recipes/rag-chat) — the equivalent of `RetrievalQAChain` in 30 lines - [ADR 0001 — Adapter contract](https://github.com/AgentsKit-io/agentskit/blob/main/docs/architecture/adrs/0001-adapter-contract.md) — why provider swap is actually one line - [README — When you should NOT use AgentsKit](../../#when-you-should-not-use-agentskit) — the honest decision matrix --- # From Mastra Source: https://www.agentskit.io/docs/get-started/migrating/from-mastra > Side-by-side migration guide. Map your Mastra code to AgentsKit — with honest callouts about where Mastra still wins. import { ContributeCallout } from '@/components/contribute/contribute-callout' Mastra is the closest philosophical cousin to AgentsKit: an agent-first JS framework, not a chat SDK. If you're on Mastra and productive, the case for migrating is weaker than migrating from Vercel AI SDK or LangChain — Mastra already gets most of what we think is important. Come to AgentsKit when: - You want **formal, versioned contracts** (ADRs 0001–0006) you can build on top of, not an evolving class hierarchy - You need **≤ 10KB core** for edge / browser / embedded use — Mastra's core is heavier - You prefer **composition via plain functions** over an `Agent` class - You want **tools by reference** (names resolved via registry) instead of inline tool objects wired into each agent Stay on Mastra when it's doing what you need — see "Where Mastra still wins" at the bottom. ## Quick reference | Mastra | AgentsKit | Notes | |---|---|---| | `new Agent({ name, model, instructions, tools })` | `createRuntime({ adapter, tools })` + `SkillDefinition` | Behavior (prompt + rules) lives in a Skill (ADR 0005); orchestration in Runtime (ADR 0006) | | `agent.generate(prompt)` / `agent.stream(prompt)` | `runtime.run(task)` | Single method — no split between sync/stream | | `createTool({ id, inputSchema, execute })` | `ToolDefinition` with `schema` (JSON Schema 7) | Zod → JSON Schema via `zod-to-json-schema` if you want to keep Zod | | `new Mastra({ agents, workflows })` | Plain objects, imported where needed | No orchestrator container; the Runtime is the smallest unit | | `Memory` class with working memory + semantic recall | `ChatMemory` + `VectorMemory` + `Retriever` | Split into three narrow contracts per [ADR 0003](https://github.com/AgentsKit-io/agentskit/blob/main/docs/architecture/adrs/0003-memory-contract.md) and [ADR 0004](https://github.com/AgentsKit-io/agentskit/blob/main/docs/architecture/adrs/0004-retriever-contract.md) | | `RAGAgent` | `createRAG` + `createRuntime({ retriever })` | RAG is a `Retriever`, not a specialized Agent | | `createWorkflow` / step graph | `delegates` on the Runtime | Supervisor/swarm/hierarchical via RT10 — no separate graph DSL in v1 | | `createVectorQueryTool` | Tool that calls a `Retriever` internally | Keeps Retriever as the substrate; tools are just functions | | Telemetry (OpenTelemetry built-in) | `Observer[]` + optional `@agentskit/observability` integrations | Observers read-only per RT9 | | Voice (`agent.voice.speak`) | Not yet shipped | See "Where Mastra still wins" | ## 1. Basic agent ### Before — Mastra ```ts import { Agent } from '@mastra/core/agent' import { openai } from '@ai-sdk/openai' const assistant = new Agent({ name: 'assistant', instructions: 'You are a helpful assistant. Be concise.', model: openai('gpt-4o'), }) const res = await assistant.generate('Hello!') console.log(res.text) ``` ### After — AgentsKit Two flavors, depending on whether the persona is reusable. **Inline** (one-off): ```ts import { createRuntime } from '@agentskit/runtime' import { openai } from '@agentskit/adapters' const runtime = createRuntime({ adapter: openai({ apiKey: KEY, model: 'gpt-4o' }), systemPrompt: 'You are a helpful assistant. Be concise.', }) const result = await runtime.run('Hello!') console.log(result.content) ``` **Reusable persona** (Skill): ```ts import type { SkillDefinition } from '@agentskit/core' const assistant: SkillDefinition = { name: 'assistant', description: 'General-purpose concise helper', systemPrompt: 'You are a helpful assistant. Be concise.', } const result = await runtime.run('Hello!', { skill: assistant }) ``` **What's different**: AgentsKit separates the persona (Skill) from the runner (Runtime). Same behavior, two reusable primitives instead of one class. ## 2. Tool calling ### Before — Mastra ```ts import { createTool } from '@mastra/core/tools' import { z } from 'zod' const getWeather = createTool({ id: 'get-weather', description: 'Get the weather for a city', inputSchema: z.object({ city: z.string() }), execute: async ({ context }) => { const { city } = context const res = await fetch(`https://wttr.in/${city}?format=j1`) return await res.json() }, }) const assistant = new Agent({ name: 'assistant', model: openai('gpt-4o'), tools: { getWeather }, }) ``` ### After — AgentsKit ```ts import type { ToolDefinition } from '@agentskit/core' import { createRuntime } from '@agentskit/runtime' import { openai } from '@agentskit/adapters' const getWeather: ToolDefinition = { name: 'get_weather', description: 'Get the weather for a city', schema: { type: 'object', properties: { city: { type: 'string' } }, required: ['city'], }, async execute(args) { const res = await fetch(`https://wttr.in/${args.city}?format=j1`) return await res.json() }, } const runtime = createRuntime({ adapter: openai({ apiKey: KEY, model: 'gpt-4o' }), tools: [getWeather], }) ``` **Differences to notice** - Tools are **flat objects**, not class instances. Easier to serialize, inspect, generate programmatically. - `schema` is **JSON Schema 7**, not Zod. Convert at the userland edge if you want to keep Zod as source: ```ts import { zodToJsonSchema } from 'zod-to-json-schema' const schema = zodToJsonSchema(z.object({ city: z.string() })) as JSONSchema7 ``` - `requiresConfirmation: true` (Tool T9) + `onConfirm` on the runtime (RT6) give you a formal human-in-the-loop gate — no auto-approve timeout. Mastra's equivalent is ad-hoc. ## 3. Memory Mastra's `Memory` combines working memory and semantic recall in one class. AgentsKit splits them so backends implement only what they do well. ### Before — Mastra ```ts import { Memory } from '@mastra/memory' import { LibSQLStore } from '@mastra/libsql' const memory = new Memory({ storage: new LibSQLStore({ url: 'file:./storage.db' }), options: { workingMemory: { enabled: true }, semanticRecall: { topK: 3, messageRange: 5 }, }, }) const agent = new Agent({ name: 'assistant', memory, /* ... */ }) ``` ### After — AgentsKit ```ts import { createRuntime } from '@agentskit/runtime' import { openai, openaiEmbedder } from '@agentskit/adapters' import { sqliteChatMemory, fileVectorMemory } from '@agentskit/memory' import { createRAG } from '@agentskit/rag' const rag = createRAG({ store: fileVectorMemory({ path: './embeddings.json' }), embed: openaiEmbedder({ apiKey: KEY, model: 'text-embedding-3-small' }), topK: 3, }) const runtime = createRuntime({ adapter: openai({ apiKey: KEY, model: 'gpt-4o' }), memory: sqliteChatMemory({ path: './session.db' }), // ordered chat history (ChatMemory) retriever: rag, // semantic recall (Retriever) }) ``` **Why the split matters** - SQLite is great at ordered message history; mediocre at ANN search. pgvector/Pinecone are the opposite. - A unified interface forces every backend to half-fulfill both. - Atomicity: `memory.save()` is replace-all with run-boundary atomicity (CM2 + RT7) — failed runs don't corrupt state. ## 4. RAG ### Before — Mastra ```ts import { RAGAgent } from '@mastra/rag' // Plus specific vector-store + embedder wiring from @mastra/* ``` ### After — AgentsKit RAG is just a `Retriever`. Any `Runtime` accepts one. ```ts import { createRuntime } from '@agentskit/runtime' import { openai, openaiEmbedder } from '@agentskit/adapters' import { createRAG } from '@agentskit/rag' import { fileVectorMemory } from '@agentskit/memory' const rag = createRAG({ store: fileVectorMemory({ path: './embeddings.json' }), embed: openaiEmbedder({ apiKey: KEY, model: 'text-embedding-3-small' }), }) await rag.ingest([ { id: 'doc-1', content: 'AgentsKit core is 10KB gzipped.' }, ]) const runtime = createRuntime({ adapter: openai({ apiKey: KEY, model: 'gpt-4o' }), retriever: rag, }) ``` The Retriever contract makes RAG, BM25, hybrid, and web search the same shape — composable without extra primitives. ## 5. Workflows → delegation Mastra has a graph-based workflow DSL. AgentsKit covers the common patterns (supervisor, swarm, hierarchical, blackboard) via `delegates` on the Runtime (RT10). ### Before — Mastra ```ts import { createWorkflow, createStep } from '@mastra/core/workflows' const researchStep = createStep({ id: 'research', /* ... */ }) const writeStep = createStep({ id: 'write', /* ... */ }) const researchWorkflow = createWorkflow({ id: 'research-workflow', steps: [researchStep, writeStep], }) ``` ### After — AgentsKit ```ts import { planner, researcher } from '@agentskit/skills' import type { SkillDefinition } from '@agentskit/core' const writer: SkillDefinition = { name: 'writer', description: 'Synthesizes research findings into a structured report.', systemPrompt: 'You are a precise technical writer. ...', } await runtime.run('Research WebGPU and write a report', { skill: planner, delegates: { researcher: { skill: researcher, tools: [webSearch()], maxSteps: 5 }, writer: { skill: writer, tools: [...filesystem({ basePath: './out' })], maxSteps: 3 }, }, }) ``` Each delegate is materialized as a tool the planner can call — to the model, delegation is just another tool call. No separate graph DSL. **Where this tradeoff favors Mastra**: explicit, long-running, checkpointed workflows with complex control flow. Durable execution is Phase 3 in AgentsKit (#156). ## 6. Evals Both frameworks ship eval primitives. Same idea, different shape. ### AgentsKit ```ts import { runEval } from '@agentskit/eval' import { createRuntime } from '@agentskit/runtime' import { openai } from '@agentskit/adapters' const runtime = createRuntime({ adapter: openai({ apiKey: KEY, model: 'gpt-4o-mini' }), }) const report = await runEval({ runtime, dataset: [ { input: '2 + 2?', expected: '4', score: (o) => o.includes('4') ? 1 : 0 }, ], concurrency: 4, }) expect(report.averageScore).toBeGreaterThanOrEqual(0.8) ``` See [Recipe: Eval suite for an agent](../recipes/eval-suite). ## 7. Telemetry ### Before — Mastra OpenTelemetry auto-wired via `telemetry: { serviceName, enabled }` on the `Mastra` orchestrator. ### After — AgentsKit ```ts import type { Observer } from '@agentskit/core' const telemetry: Observer = { name: 'telemetry', on(event) { if (event.type === 'llm:start') console.time('model') if (event.type === 'llm:end') { console.timeEnd('model') console.log(`${event.durationMs}ms`) } if (event.type === 'agent:step') console.log(`step ${event.step}: ${event.action}`) }, } createRuntime({ adapter, tools, observers: [telemetry] }) ``` The Observer contract (RT9) is read-only, composable (it's an array), and integrations (OpenTelemetry, LangSmith, PostHog) plug in as additional observers rather than as framework features. ## 8. No orchestrator container Mastra centralizes `new Mastra({ agents, workflows, integrations })`. AgentsKit doesn't — the Runtime is the smallest composable unit, and there's no layer above it. ### Why - **Startup cost**: instantiating an orchestrator with N agents means paying for all of them upfront. Per ADR 0006 RT1, AgentsKit runtimes are config-only until `run()` is called. - **Testability**: a single runtime with a mock adapter is easier to test than an orchestrator that wires multiple agents. - **Edge-ready**: a `Mastra` container is heavier than the 10KB we commit to for the core. Edge functions benefit from the minimal surface. If you want a "registry of runtimes", build one in userland with a `Map`. The contract is small enough that the pattern is trivial. ## Where Mastra still wins Honest callouts — choose Mastra over AgentsKit when: - **You need voice today.** Mastra ships `agent.voice.speak(...)` / `agent.voice.listen(...)` with provider integrations. AgentsKit's voice story is planned but not shipped. - **You want explicit, long-running workflows with checkpointing.** Mastra's workflow engine handles suspend/resume natively. AgentsKit's durable execution is Phase 3 (#156). - **You prefer a class-per-agent mental model.** Some teams find `new Agent({ ... })` easier to reason about than `skill + runtime`. Taste call. - **You use Mastra Cloud / Studio.** That's a real ecosystem with CI integrations, evals-as-a-service, playground. AgentsKit Cloud is planned (Phase 4); not here yet. - **You rely on Mastra's integrations catalog** for specific third-party systems (analytics, webhooks, database bindings). AgentsKit focuses on core composition; integration breadth is growing. If none of those apply and the class hierarchy feels heavy, migrate. ## Migration checklist A pragmatic incremental path: 1. **Start with a single Mastra `Agent`** — convert it to `createRuntime` + `SkillDefinition`. One file, one change. 2. **Port tools one by one** — `createTool` → `ToolDefinition`. Zod users keep Zod; convert at the edge. 3. **Swap `Memory` for `ChatMemory` + `Retriever`** — same functionality, two small contracts. 4. **Replace `Mastra` orchestrator with a plain registry** — a `Map` or whatever fits. 5. **Move workflows to `delegates`** — supervisor pattern first, complex graphs last. 6. **Wrap with observers** for the telemetry you already have. 7. **Leave voice in Mastra** until AgentsKit ships it. ## Bundle size check ```bash du -sh node_modules/@mastra node_modules/@agentskit 2>/dev/null \ | awk '{ print $0 }' ``` Typical Mastra footprint is measured in tens of megabytes across the packages you pull in; AgentsKit weighs in at single-digit megabytes for a comparable surface. Edge deployments feel this most. ## Related - [Concepts: Skill vs Tool](../concepts/mental-model) — the load-bearing distinction - [Concepts: Memory](../concepts/memory) — why ChatMemory and VectorMemory are split - [Recipe: Multi-agent research team](../recipes/research-team) — the `delegates` pattern end-to-end - [README — When you should NOT use AgentsKit](../../#when-you-should-not-use-agentskit) --- # From Vercel AI SDK Source: https://www.agentskit.io/docs/get-started/migrating/from-vercel-ai-sdk > Side-by-side migration guide. Map your Vercel AI SDK code to AgentsKit — with honest callouts about where each wins. import { ContributeCallout } from '@/components/contribute/contribute-callout' Vercel AI SDK is an excellent chat SDK. If you're on it and happy, stay. Come to AgentsKit when you hit one of these: - You need a real **agent runtime** (ReAct loop, tools+skills+memory+delegation) without writing it yourself - You want to **swap providers in one line**, not rewrite route handlers - You want **formal contracts** for adapters, tools, memory, retrieval, skills, runtime - You need **terminal, CLI, or headless** surfaces — Vercel AI SDK is React-first This page maps common Vercel AI SDK patterns to their AgentsKit equivalents. ## Quick reference | Vercel AI SDK | AgentsKit | Notes | |---|---|---| | `streamText({ model, messages })` | `adapter.createSource({ messages }).stream()` | Adapter is the seam; see [Concepts: Adapter](../concepts/adapter) | | `useChat()` (App Router) | `useChat({ adapter })` from `@agentskit/react` | Same name, different shape. See below. | | `tool({ description, parameters, execute })` | `ToolDefinition` with `schema` (JSON Schema 7) | Zod → JSON Schema via `zod-to-json-schema` | | `generateText({ ..., maxSteps })` | `createRuntime({ ..., maxSteps }).run(task)` | AgentsKit's runtime has memory, retrieval, delegation built in | | `experimental_StreamData` | `observers[]` or `StreamChunk.metadata` | Observers are read-only, composable | | `openai('gpt-4o')` | `openai({ apiKey, model: 'gpt-4o' })` | AgentsKit adapters take an options object | | Route handler (`app/api/chat/route.ts`) | Client hook uses adapter directly, or server action | AgentsKit works in RSC, route handlers, edge, Node | ## 1. Basic streaming chat (client hook) ### Before — Vercel AI SDK ```tsx // app/chat.tsx 'use client' import { useChat } from 'ai/react' export default function Chat() { const { messages, input, handleInputChange, handleSubmit } = useChat({ api: '/api/chat', }) return (
{messages.map(m => (
{m.role}: {m.content}
))}
) } ``` ```ts // app/api/chat/route.ts import { streamText } from 'ai' import { openai } from '@ai-sdk/openai' export async function POST(req: Request) { const { messages } = await req.json() const result = streamText({ model: openai('gpt-4o'), messages }) return result.toDataStreamResponse() } ``` ### After — AgentsKit ```tsx // app/chat.tsx 'use client' import { useChat, ChatContainer, Message, InputBar } from '@agentskit/react' import { openai } from '@agentskit/adapters' import '@agentskit/react/theme' const adapter = openai({ apiKey: KEY, model: 'gpt-4o' }) export default function Chat() { const chat = useChat({ adapter }) return ( {chat.messages.map(m => )} ) } ``` **What's different** - No route handler required for client-side-only demos — the adapter calls the provider directly. For production, proxy through a server action or route handler so your key isn't in the browser. - Components are provided (`ChatContainer`, `Message`, `InputBar`) but they're headless; swap them for your own freely. - Return type of `useChat` is `ChatReturn`, not the Vercel AI SDK's shape. Keys you'll care about: `messages`, `input`, `setInput`, `send`, `stop`, `status`, `retry`. ## 2. Securing the API key (server side) Vercel AI SDK pushes you toward a route handler. AgentsKit supports route handlers, server actions, or a proxied adapter — your choice. ### Server action variant (recommended) ```ts // app/actions.ts 'use server' import { openai } from '@agentskit/adapters' import { createRuntime } from '@agentskit/runtime' const runtime = createRuntime({ adapter: openai({ apiKey: process.env.OPENAI_API_KEY!, model: 'gpt-4o' }), }) export async function ask(prompt: string) { const result = await runtime.run(prompt) return result.content } ``` ```tsx // app/page.tsx 'use client' import { ask } from './actions' // Call ask(prompt) from a form or event handler; stream via RSC Suspense ``` ### Route handler variant (same-shape migration) If you prefer to keep a `/api/chat` endpoint, proxy through a route handler that yields `StreamChunk` as text chunks. See `apps/example-react` in the repo for a working reference. ## 3. Tool calling ### Before — Vercel AI SDK ```ts import { tool, streamText } from 'ai' import { z } from 'zod' const weatherTool = tool({ description: 'Get the weather for a city', parameters: z.object({ city: z.string() }), execute: async ({ city }) => { const res = await fetch(`https://wttr.in/${city}?format=j1`) return await res.json() }, }) const result = streamText({ model: openai('gpt-4o'), messages, tools: { weather: weatherTool }, maxSteps: 5, }) ``` ### After — AgentsKit ```ts import type { ToolDefinition } from '@agentskit/core' import { createRuntime } from '@agentskit/runtime' import { openai } from '@agentskit/adapters' const weatherTool: ToolDefinition = { name: 'weather', description: 'Get the weather for a city', schema: { type: 'object', properties: { city: { type: 'string' } }, required: ['city'], }, async execute(args) { const res = await fetch(`https://wttr.in/${args.city}?format=j1`) return await res.json() }, } const runtime = createRuntime({ adapter: openai({ apiKey: KEY, model: 'gpt-4o' }), tools: [weatherTool], maxSteps: 5, }) ``` **Differences to notice** - Tools use **JSON Schema 7**, not Zod. Convert with `zodToJsonSchema(yourZodSchema)` if you want to keep Zod as source. - Tool `name` is explicit (it's an identity — see [ADR 0002](https://github.com/AgentsKit-io/agentskit/blob/main/docs/architecture/adrs/0002-tool-contract.md) T1). - `maxSteps` is a **hard** cap, not a suggestion. Pick a generous number; the runtime will not loop past it. - You can set `requiresConfirmation: true` and wire `onConfirm` on the runtime — Vercel AI SDK doesn't formalize this. ## 4. Multi-provider swap ### Before — Vercel AI SDK ```ts // route handler has to know which SDK to import import { openai } from '@ai-sdk/openai' import { anthropic } from '@ai-sdk/anthropic' const model = useClaude ? anthropic('claude-sonnet-4-6') : openai('gpt-4o') const result = streamText({ model, messages }) ``` ### After — AgentsKit ```ts import { openai, anthropic } from '@agentskit/adapters' const adapter = useClaude ? anthropic({ apiKey, model: 'claude-sonnet-4-6' }) : openai({ apiKey, model: 'gpt-4o' }) useChat({ adapter }) // or createRuntime({ adapter }) ``` **Also possible and not possible in Vercel AI SDK**: router and ensemble adapters. Coming in Fase 3 (#145, #146). ## 5. Agent runtime with memory + retrieval This is the biggest win. Vercel AI SDK has no runtime — you write the loop yourself. AgentsKit has one, with formal contracts. ### Vercel AI SDK pattern ```ts // You write this yourself — ~50 lines, easy to get wrong async function runAgent(task: string) { const messages = [{ role: 'user', content: task }] for (let step = 0; step < 10; step++) { const result = await generateText({ model, messages, tools }) // parse tool calls, execute, append results, decide when to stop... } } ``` ### AgentsKit ```ts import { createRuntime } from '@agentskit/runtime' import { openai } from '@agentskit/adapters' import { webSearch, filesystem } from '@agentskit/tools' import { sqliteChatMemory } from '@agentskit/memory' import { createRAG } from '@agentskit/rag' import { fileVectorMemory } from '@agentskit/memory' import { openaiEmbedder } from '@agentskit/adapters' const rag = createRAG({ store: fileVectorMemory({ path: './embeddings.json' }), embed: openaiEmbedder({ apiKey: KEY, model: 'text-embedding-3-small' }), }) const runtime = createRuntime({ adapter: openai({ apiKey: KEY, model: 'gpt-4o' }), tools: [webSearch(), ...filesystem({ basePath: './workspace' })], memory: sqliteChatMemory({ path: './sessions/user-42.db' }), retriever: rag, maxSteps: 10, }) const result = await runtime.run('Research the top 3 AI frameworks and save a summary') ``` Memory load + save, retrieval per turn, tool resolution, error categorization, abort semantics — all handled by the runtime per [ADR 0006](https://github.com/AgentsKit-io/agentskit/blob/main/docs/architecture/adrs/0006-runtime-contract.md). ## 6. Multi-agent / delegation Vercel AI SDK does not ship this. You'd implement it yourself. ```ts import { planner, researcher, coder } from '@agentskit/skills' await runtime.run('Build a landing page about quantum computing', { skill: planner, delegates: { researcher: { skill: researcher, tools: [webSearch()], maxSteps: 3 }, coder: { skill: coder, tools: [...filesystem({ basePath: './src' })], maxSteps: 8 }, }, }) ``` See [Recipe: Multi-agent research team](../recipes/research-team) for the full walkthrough. ## 7. Observability / telemetry ### Vercel AI SDK ```ts const result = streamText({ model, messages, onFinish: ({ usage }) => console.log(usage), experimental_telemetry: { isEnabled: true }, }) ``` ### AgentsKit ```ts import type { Observer } from '@agentskit/core' const telemetry: Observer = { name: 'telemetry', on(event) { if (event.type === 'llm:start') console.time('model') if (event.type === 'llm:end') { console.timeEnd('model') console.log(`${event.durationMs}ms`, event.usage) } if (event.type === 'agent:step') console.log(`step ${event.step}: ${event.action}`) }, } createRuntime({ adapter, tools, observers: [telemetry] }) ``` Observers are composable (array), read-only, and first-class in the contract (RT9). Plug in LangSmith, OpenTelemetry, PostHog, or your own logger. ## Where Vercel AI SDK still wins Honest callouts — choose Vercel AI SDK over AgentsKit when: - **You need `generateObject` with strict schema output** right now. AgentsKit will ship a structured-output primitive in Fase 2; today you'd handle it manually. - **You're shipping a consumer chat SDK** and want the smallest possible footprint with no agent concepts. Vercel AI SDK is purpose-built for that. - **Your team has deep investment in the Vercel ecosystem** (AI Elements, Vercel AI Gateway, etc.) and doesn't need a runtime. - **You want v1.0-stability right now.** Vercel AI SDK is post-1.0; AgentsKit is pre-1.0 (with formal contracts already locked — but that's not the same as v1.0.0). If none of those apply and you want the runtime + contracts, migrate. ## Incremental migration You don't have to migrate everything at once: 1. **Keep your Vercel AI SDK route handler** for existing endpoints 2. **Add AgentsKit for new features** — a CLI, a terminal chat, an autonomous agent 3. **Port the chat hook** when you want `@agentskit/react`'s components 4. **Consolidate when the runtime becomes the hub** — one adapter instance across React, Ink, runtime The Adapter contract (ADR 0001) is the pivot point: once you have one `AdapterFactory`, every surface in AgentsKit accepts it. ## Related - [README — When you should NOT use AgentsKit](../../#when-you-should-not-use-agentskit) — the honest decision matrix - [Concepts: Adapter](../concepts/adapter) — why this migration is mostly a rename - [Recipe: Chat with RAG](../recipes/rag-chat) — the equivalent of Vercel AI SDK + LangChain stitched together, in 30 lines --- # Production Source: https://www.agentskit.io/docs/production > Observability, security, evals, CLI — everything you need to trust an agent in production. Ship with confidence. Four pillars: ## Observability Trace, log, audit, cost-guard every run. - [Overview](./observability) · [Loggers](./observability/loggers) · [Trace viewer](./observability/trace-viewer) · [Devtools](./observability/devtools) · [Cost guard](./observability/cost-guard) · [Audit log](./observability/audit-log) ## Security Five primitives every production agent needs. - [Overview](./security) · [PII redaction](./security/pii-redaction) · [Prompt injection](./security/prompt-injection) · [Rate limiting](./security/rate-limiting) · [Mandatory sandbox](./security/mandatory-sandbox) ## Evaluation Measure quality with numbers, not vibes. - [Overview](./evals) · [Suites](./evals/suites) · [Replay](./evals/replay) · [Snapshots](./evals/snapshots) · [CI reporters](./evals/ci) ## CLI Nine commands wrapping every part of AgentsKit. - [Overview](./cli) · [init](./cli/init) · [chat](./cli/chat) · [run](./cli/run) · [ai](./cli/ai) · [dev](./cli/dev) · [doctor](./cli/doctor) --- # CLI Source: https://www.agentskit.io/docs/production/cli > Nine commands wrapping every part of AgentsKit — init, chat, run, dev, doctor, ai, tunnel, rag, config. ## Commands | Command | Purpose | |---|---| | `agentskit init` | Scaffold a new project (react / ink / runtime / multi-agent). | | `agentskit chat` | Interactive chat in the terminal (Ink). | | `agentskit run ""` | Run an agent once. `--provider`, `--model`, `--api-key`, `--base-url`, `--verbose`. | | `agentskit dev` | Dev server with hot-reload. | | `agentskit doctor` | Diagnose env (providers, keys, tooling). | | `agentskit ai ""` | NL → typed `AgentSchema` + scaffold. [Recipe](/docs/reference/recipes/agentskit-ai). | | `agentskit tunnel` | ngrok-style tunnel for webhooks. | | `agentskit rag` | Local RAG helpers (ingest / search). | | `agentskit config` | Read / write local config. | *Per-command deep dives land in step 6 of the docs IA rollout.* ## Related - [Package: @agentskit/cli](/docs/reference/packages/cli) - [For agents: cli](/docs/for-agents/cli) --- # agentskit ai Source: https://www.agentskit.io/docs/production/cli/ai > Natural language → typed AgentSchema + scaffold. ```bash agentskit ai "I want a Discord bot that answers questions from a Notion workspace" ``` Produces: 1. `AgentSchema` JSON (typed, validated). 2. Scaffold files (`tools.ts`, `runtime.ts`, optional UI). 3. `.env.example` with required keys. ## Flags | Flag | Purpose | |---|---| | `--out ` | scaffold target | | `--schema-only` | emit only the AgentSchema JSON | | `--provider` / `--model` | LLM used for the authoring step | ## Related - [Recipe: agentskit ai](/docs/reference/recipes/agentskit-ai) - [Specs → AgentSchema](/docs/reference/specs/agent-schema) --- # agentskit chat Source: https://www.agentskit.io/docs/production/cli/chat > Interactive terminal chat. Ink-based. No setup required. ```bash agentskit chat --provider openai --model gpt-4o ``` ## Flags | Flag | Purpose | |---|---| | `--provider` | `openai` · `anthropic` · `ollama` · ... | | `--model` | provider-specific model id | | `--api-key` | override env | | `--base-url` | OpenAI-compatible endpoints | | `--system` | system prompt string or `@path` | | `--memory ` | file-backed chat memory | | `--tools ` | load tools from module path | ## Related - [Package: ink](/docs/reference/packages/ink) - [CLI → run](./run) --- # agentskit dev Source: https://www.agentskit.io/docs/production/cli/dev > Local dev server with hot-reload. Devtools + trace feed attached. ```bash agentskit dev ``` Starts: - Agent runtime with hot reload on tool / skill changes. - Local devtools UI at `http://localhost:4301`. - SSE trace feed for any external dashboard. ## Flags | Flag | Purpose | |---|---| | `--port` | devtools port (default 4301) | | `--entry ` | runtime entry module | | `--watch ` | extra reload paths | ## Related - [Observability → Devtools](/docs/production/observability/devtools) --- # agentskit doctor Source: https://www.agentskit.io/docs/production/cli/doctor > Diagnose your environment. Providers, keys, tooling, versions. ```bash agentskit doctor ``` Checks: - Node / Bun version - Provider API keys (reachable, valid) - Local runtimes (Ollama / LM Studio / vLLM) - MCP servers on PATH - Tunnel binaries - Disk permissions for default memory paths ## Flags | Flag | Purpose | |---|---| | `--json` | machine-readable report | | `--fix` | attempt automated fixes (install missing deps, etc.) | ## Related - [CLI → init](./init) --- # agentskit init Source: https://www.agentskit.io/docs/production/cli/init > Scaffold a new project — react, ink, runtime, or multi-agent template. ```bash pnpm dlx agentskit init # or npm create agentskit@latest ``` Interactive. Picks template + provider + memory backend. ## Flags | Flag | Purpose | |---|---| | `--template ` | `react` / `ink` / `runtime` / `multi-agent` | | `--provider ` | `openai` / `anthropic` / `ollama` / ... | | `--name ` | target directory | | `--no-install` | skip `pnpm install` | ## Templates - **react** — Vite + `@agentskit/react` + `useChat` + headless components - **ink** — terminal chat via `@agentskit/ink` - **runtime** — standalone `createRuntime` with ReAct loop - **multi-agent** — supervisor topology starter ## Related - [Packages → templates](/docs/reference/packages/templates) - [CLI → ai](./ai) --- # agentskit run Source: https://www.agentskit.io/docs/production/cli/run > One-shot agent run from the terminal. Pipe inputs, capture outputs. ```bash agentskit run "Summarize today's PRs" --provider openai --model gpt-4o ``` Piped stdin: ```bash cat README.md | agentskit run "Extract TODO items" ``` ## Flags | Flag | Purpose | |---|---| | `--provider` / `--model` / `--api-key` / `--base-url` | adapter config | | `--verbose` | print every step + tool call | | `--json` | machine-readable output | | `--schema ` | output schema (Zod or JSON Schema) | ## Related - [CLI → chat](./chat) · [CLI → ai](./ai) - [Runtime](/docs/agents/runtime) --- # Evals Source: https://www.agentskit.io/docs/production/evals > Measure quality with numbers, not vibes. Suites, replay, snapshots, diff, CI reporters. ## Suites - `runEval({ agent, suite })` — run any `EvalSuite` against any async agent fn. [Recipe](/docs/reference/recipes/eval-suite). ## Deterministic replay - `createRecordingAdapter` + `createReplayAdapter` — bit-for-bit replay. [Recipe](/docs/reference/recipes/deterministic-replay). - `createTimeTravelSession` — rewind + override + fork. [Recipe](/docs/reference/recipes/time-travel-debug). - `replayAgainst` — A/B cassette vs different model. [Recipe](/docs/reference/recipes/replay-different-model). ## Snapshots + diff - `matchPromptSnapshot` — Jest-style with exact / normalized / similarity. [Recipe](/docs/reference/recipes/prompt-snapshots). - `promptDiff` + `attributePromptChange` — git-blame for prompts. [Recipe](/docs/reference/recipes/prompt-diff). ## CI reporters - `reportToCi` + `renderJUnit` + `renderMarkdown` + `renderGitHubAnnotations`. [Recipe](/docs/reference/recipes/evals-ci). ## Open format - `@agentskit/core/eval-format` — portable eval JSON spec. [Specs](/docs/reference/specs). *Per-primitive deep dives land in step 6 of the docs IA rollout.* ## Related - [Package: @agentskit/eval](/docs/reference/packages/eval) - [For agents: eval](/docs/for-agents/eval) --- # CI reporters Source: https://www.agentskit.io/docs/production/evals/ci > JUnit, Markdown, GitHub annotations — for any CI stack. ```ts import { reportToCi, renderJUnit, renderMarkdown, renderGitHubAnnotations, } from '@agentskit/eval' const report = await runEval({ agent, suite }) await reportToCi({ report, output: [ { kind: 'junit', path: 'eval-report.xml' }, { kind: 'markdown', path: 'eval-report.md' }, { kind: 'github-annotations' }, // auto-writes to ::error / ::warning ], }) ``` ## GitHub Actions ```yaml - run: pnpm eval - uses: actions/upload-artifact@v4 with: name: eval-report path: eval-report.md ``` ## Related - [Recipe: evals CI](/docs/reference/recipes/evals-ci) - [Suites](./suites) --- # Deterministic replay Source: https://www.agentskit.io/docs/production/evals/replay > Record once, replay forever. Test without hitting the network. ## Record ```ts import { createRecordingAdapter } from '@agentskit/eval' const rec = createRecordingAdapter({ inner: openai({ apiKey }), cassettePath: '.agentskit/cassettes/triage.jsonl', }) ``` Run your suite once with `rec` — every call captured. ## Replay ```ts import { createReplayAdapter } from '@agentskit/eval' const replay = createReplayAdapter({ cassettePath: '.agentskit/cassettes/triage.jsonl', }) ``` Use `replay` in CI — zero network, deterministic. ## Time travel ```ts import { createTimeTravelSession } from '@agentskit/eval' const session = createTimeTravelSession({ cassettePath }) session.rewindTo(step) session.override(step, { output: 'alternate response' }) const forked = session.fork() ``` ## Replay against different model ```ts import { replayAgainst } from '@agentskit/eval' const diff = await replayAgainst({ cassettePath, adapter: anthropic(...), }) ``` ## Related - [Recipe: deterministic replay](/docs/reference/recipes/deterministic-replay) - [Recipe: time-travel debug](/docs/reference/recipes/time-travel-debug) - [Recipe: replay different model](/docs/reference/recipes/replay-different-model) --- # Prompt snapshots + diff Source: https://www.agentskit.io/docs/production/evals/snapshots > Jest-style snapshots for prompts. Git-blame for every change. ## matchPromptSnapshot ```ts import { matchPromptSnapshot } from '@agentskit/eval' await matchPromptSnapshot({ name: 'triage-v1', actual: renderedPrompt, mode: 'exact', // | 'normalized' | 'similarity' path: '.agentskit/snapshots', similarityThreshold: 0.95, }) ``` ## promptDiff + attributePromptChange ```ts import { promptDiff, attributePromptChange } from '@agentskit/eval' const delta = promptDiff(before, after) const attribution = attributePromptChange(delta, history) ``` ## Related - [Recipe: prompt snapshots](/docs/reference/recipes/prompt-snapshots) - [Recipe: prompt diff](/docs/reference/recipes/prompt-diff) --- # Eval suites Source: https://www.agentskit.io/docs/production/evals/suites > Run any async agent fn against an EvalSuite. Pass/fail per case. ```ts import { runEval } from '@agentskit/eval' const suite = { name: 'support-triage', cases: [ { id: 'refund', input: 'How do I get a refund?', assert: (out) => out.includes('refund policy'), }, ], } const report = await runEval({ agent: async (input) => runtime.run({ input }).then((r) => r.output), suite, }) console.log(report.passRate, report.failures) ``` ## Assertions - boolean fn → pass/fail - async LLM-as-judge → `({ pass, rationale })` - regex → match required ## Metrics Built-in: `passRate`, `latencyP50`, `latencyP95`, `tokensTotal`, `usdTotal`. ## Related - [Replay](./replay) · [Snapshots](./snapshots) · [CI](./ci) - [Recipe: eval suite](/docs/reference/recipes/eval-suite) --- # Observability Source: https://www.agentskit.io/docs/production/observability > Trace, log, audit, and cost-guard your agents end-to-end. ## Loggers + tracers - `consoleLogger` — local dev. - `langsmith` — LangSmith observer. - `opentelemetry` — OTel observer. - `createTraceTracker` — low-level span lifecycle. ## Local trace viewer - `createFileTraceSink` + `renderTraceViewerHtml` — offline Jaeger-style HTML. [Recipe](/docs/reference/recipes/trace-viewer). ## Devtools server - `createDevtoolsServer` + `toSseFrame` — live feed for any devtools UI. [Recipe](/docs/reference/recipes/devtools-server). ## Cost + tokens - `costGuard` — hard $ ceiling per run. - `approximateCounter`, `createProviderCounter` — token accounting. ## Audit - `createSignedAuditLog` — hash-chain + HMAC tamper-evident log. [Recipe](/docs/reference/recipes/audit-log). *Per-observer deep dives land in step 6 of the docs IA rollout.* ## Related - [Package: @agentskit/observability](/docs/reference/packages/observability) - [For agents: observability](/docs/for-agents/observability) - [Evals](/docs/production/evals) · [Security](/docs/production/security) --- # Signed audit log Source: https://www.agentskit.io/docs/production/observability/audit-log > Hash-chained, HMAC-signed audit log. Tamper-evident record of every step. ```ts import { createSignedAuditLog } from '@agentskit/observability' const audit = createSignedAuditLog({ path: '.agentskit/audit.log', key: process.env.AK_AUDIT_KEY!, }) const runtime = createRuntime({ adapter, observers: [audit.observer] }) ``` ## Verify ```ts const ok = await audit.verify() // throws on tamper ``` Each record contains `{ seq, at, kind, payload, prevHash, hmac }`. Tampering with any entry breaks the hash chain. ## Related - [Recipe: audit log](/docs/reference/recipes/audit-log) - [Security → PII](/docs/production/security/pii-redaction) --- # Cost + token accounting Source: https://www.agentskit.io/docs/production/observability/cost-guard > Cap runs by dollars or tokens. Stop before you're surprised. ## costGuard ```ts import { costGuard } from '@agentskit/observability' const runtime = createRuntime({ adapter, observers: [costGuard({ maxUsd: 0.50, onExceed: 'throw' })], }) ``` `onExceed`: `'throw' | 'stop' | 'warn'`. ## Token counters ```ts import { approximateCounter, createProviderCounter } from '@agentskit/observability' // Zero-dep heuristic const fast = approximateCounter() // Provider-accurate (uses adapter-reported usage when available) const exact = createProviderCounter({ adapter }) ``` ## Related - [Recipe: cost guard](/docs/reference/recipes/cost-guard) - [Recipe: token budget](/docs/reference/recipes/token-budget) - [Security → rate limiting](/docs/production/security/rate-limiting) --- # Devtools server Source: https://www.agentskit.io/docs/production/observability/devtools > Live SSE feed of agent events. Power any custom dashboard. ```ts import { createDevtoolsServer, toSseFrame } from '@agentskit/observability' const devtools = createDevtoolsServer() const runtime = createRuntime({ adapter, observers: [devtools.observer] }) // Next.js / Hono / Bun SSE route export const GET = () => new Response( new ReadableStream({ start(controller) { devtools.subscribe((event) => controller.enqueue(new TextEncoder().encode(toSseFrame(event))), ) }, }), { headers: { 'content-type': 'text/event-stream' } }, ) ``` Connect any web UI with `EventSource('/devtools')`. ## Related - [Recipe: devtools server](/docs/reference/recipes/devtools-server) - [Trace viewer](./trace-viewer) --- # Loggers + tracers Source: https://www.agentskit.io/docs/production/observability/loggers > Console, LangSmith, OpenTelemetry. Pick one; attach to runtime. ```ts import { createRuntime } from '@agentskit/runtime' import { consoleLogger, langsmith, opentelemetry } from '@agentskit/observability' const runtime = createRuntime({ adapter, tools, observers: [ consoleLogger(), langsmith({ apiKey: process.env.LANGSMITH_API_KEY!, project: 'prod' }), opentelemetry({ serviceName: 'agent-worker' }), ], }) ``` ## Observers | Observer | Purpose | Env | |---|---|---| | `consoleLogger()` | dev | — | | `langsmith({ apiKey, project })` | traces UI | `LANGSMITH_API_KEY` | | `opentelemetry({ serviceName })` | OTel pipeline | OTLP endpoint | | `createTraceTracker()` | BYO span lifecycle | — | Every observer receives the same event stream — `chat.start`, `llm.call`, `tool.call`, `tool.result`, `error`, `chat.end`. ## Related - [Trace viewer](./trace-viewer) · [Devtools](./devtools) --- # Trace viewer Source: https://www.agentskit.io/docs/production/observability/trace-viewer > Offline Jaeger-style HTML trace of every run. No hosting required. ```ts import { createFileTraceSink, renderTraceViewerHtml } from '@agentskit/observability' const sink = createFileTraceSink({ path: '.agentskit/traces.jsonl' }) const runtime = createRuntime({ adapter, observers: [sink.observer] }) await runtime.run(task) const html = await renderTraceViewerHtml({ source: '.agentskit/traces.jsonl' }) await Bun.write('.agentskit/trace.html', html) ``` Open the generated file in any browser. Zero server. Zero tracking. ## Related - [Recipe: trace viewer](/docs/reference/recipes/trace-viewer) - [Devtools](./devtools) · [Loggers](./loggers) --- # Security Source: https://www.agentskit.io/docs/production/security > The five primitives most production agents want — PII, injection, rate limits, audit, sandbox. ## Primitives - **PII redaction** — `createPIIRedactor` + `DEFAULT_PII_RULES`. [Recipe](/docs/reference/recipes/pii-redaction). - **Prompt injection detector** — heuristics + pluggable model classifier. [Recipe](/docs/reference/recipes/prompt-injection). - **Rate limiting** — token-bucket by user / IP / key. [Recipe](/docs/reference/recipes/rate-limiting). - **Signed audit log** — hash-chain + HMAC. [Recipe](/docs/reference/recipes/audit-log). - **Mandatory sandbox** — allow / deny / require / validators across every tool. [Recipe](/docs/reference/recipes/mandatory-sandbox). - **Human-in-the-loop approvals** — pause / resume / approve with persisted state. [Recipe](/docs/reference/recipes/hitl-approvals). *Per-primitive deep dives land in step 6 of the docs IA rollout.* ## Related - [Package: @agentskit/core/security](/docs/for-agents/core) - [Package: @agentskit/sandbox](/docs/reference/packages/sandbox) - [Observability](/docs/production/observability) --- # Mandatory sandbox Source: https://www.agentskit.io/docs/production/security/mandatory-sandbox > Policy wrapper for tools — allow, deny, require-sandbox, validators. ```ts import { createMandatorySandbox } from '@agentskit/tools' import { shell } from '@agentskit/tools' const guarded = createMandatorySandbox(shell(), { allow: ['ls', 'cat', 'grep'], deny: ['rm', 'sudo', 'curl', 'wget'], requireSandbox: true, validators: [ (args) => args.cmd.length < 256 || 'command too long', ], }) ``` ## Modes | Rule | Effect | |---|---| | `allow: string[]` | only listed cmds pass | | `deny: string[]` | listed cmds rejected | | `requireSandbox: true` | execute via [@agentskit/sandbox](/docs/reference/packages/sandbox) | | `validators: Fn[]` | return `string` to reject with message | ## Sandbox backends E2B (default) · WebContainer · Deno worker — see [sandbox package](/docs/reference/packages/sandbox). ## Related - [Recipe: mandatory sandbox](/docs/reference/recipes/mandatory-sandbox) - [HITL](/docs/agents/hitl) --- # PII redaction Source: https://www.agentskit.io/docs/production/security/pii-redaction > Scrub emails, phones, SSNs, keys before they leave your process. ```ts import { createPIIRedactor, DEFAULT_PII_RULES } from '@agentskit/core/security' const redactor = createPIIRedactor({ rules: DEFAULT_PII_RULES }) const clean = redactor.redact('Ping me at ada@example.com, SSN 123-45-6789') // => 'Ping me at [EMAIL], SSN [SSN]' ``` ## Built-in rules `EMAIL` · `PHONE` · `SSN` · `CREDIT_CARD` · `IPV4` · `IPV6` · `API_KEY_PREFIX` · `AWS_ACCESS_KEY_ID`. ## Custom rules ```ts createPIIRedactor({ rules: [ ...DEFAULT_PII_RULES, { name: 'ORG_ID', pattern: /org_[a-zA-Z0-9]{16}/g, replacement: '[ORG]' }, ], }) ``` ## Pipeline integration Attach as observer to redact events, or pre-process user input before `chat.send`. ## Related - [Recipe: PII redaction](/docs/reference/recipes/pii-redaction) - [Encrypted memory](/docs/data/memory/encrypted) --- # Prompt injection Source: https://www.agentskit.io/docs/production/security/prompt-injection > Detect and block jailbreak patterns in user input or tool results. ```ts import { createInjectionDetector } from '@agentskit/core/security' const detector = createInjectionDetector({ classifier: async (text) => { // Optional LLM-based classifier return adapter.complete({ ... }) }, }) const verdict = await detector.check(userInput) if (verdict.blocked) throw new Error(verdict.reason) ``` ## Heuristic layer Catches common patterns zero-cost: - "ignore previous instructions" - role-swap attempts - system-prompt leak probes - fenced payloads (`<|system|>`, etc.) ## Model classifier layer Pluggable. Use any adapter to score high-risk inputs. ## Where to run it - User input → `chat.send` preprocess. - Tool results → before feeding back into the loop (tool-output can be attacker-controlled). - RAG retrievals → classify each chunk before context-injection. ## Related - [Recipe: prompt injection](/docs/reference/recipes/prompt-injection) - [Mandatory sandbox](./mandatory-sandbox) --- # Rate limiting Source: https://www.agentskit.io/docs/production/security/rate-limiting > Token bucket per user / IP / key. Works with any adapter. ```ts import { createRateLimiter } from '@agentskit/core/security' const limiter = createRateLimiter({ capacity: 10, refillPerSecond: 1, keyBy: (req) => req.userId, }) app.post('/chat', async (req) => { const { allowed, retryAfterMs } = await limiter.take(req) if (!allowed) return new Response('Too Many Requests', { status: 429, headers: { 'retry-after': `${Math.ceil(retryAfterMs / 1000)}` } }) // ... run agent }) ``` ## Storage - In-memory (default) — single-host. - BYO store via `{ get, set }` — Redis / Upstash / any K/V for multi-host. ## Related - [Recipe: rate limiting](/docs/reference/recipes/rate-limiting) - [Cost guard](/docs/production/observability/cost-guard) --- # Reference Source: https://www.agentskit.io/docs/reference > Packages, API reference, recipes, examples, specs, contribute. Every detail of the library lives here. ## Packages - [Overview](./packages/overview) — all 21 `@agentskit/*` packages grouped by role. - Per-package pages: [core](./packages/core) · [adapters](./packages/adapters) · [runtime](./packages/runtime) · [react](./packages/react) · [vue](./packages/vue) · [svelte](./packages/svelte) · [solid](./packages/solid) · [react-native](./packages/react-native) · [angular](./packages/angular) · [ink](./packages/ink) · [tools](./packages/tools) · [memory](./packages/memory) · [rag](./packages/rag) · [skills](./packages/skills) · [observability](./packages/observability) · [eval](./packages/eval) · [sandbox](./packages/sandbox) · [cli](./packages/cli) · [templates](./packages/templates) ## API reference - [For agents](./for-agents) — dense LLM-friendly reference per package. - [TypeDoc HTML](pathname:///agentskit/api-reference/) — signatures + types. ## Learn by example - [Recipes](./recipes) — 60+ copy-paste solutions grouped by theme. - [Examples](./examples) — live interactive demos. ## Open specs - [A2A](./specs/a2a) · [Manifest](./specs/manifest) · [Eval format](./specs/eval-format) · [AgentSchema](./specs/agent-schema) · [Generative UI](./specs/generative-ui) ## Contribute - [Get involved](./contribute) — local setup, commit style, RFC process, roadmap. --- # Contribute Source: https://www.agentskit.io/docs/reference/contribute > AgentsKit is built in the open. Here's how to help — from filing an issue to shipping a new adapter. import { ContributorWall } from '@/components/contribute/contributor-wall' AgentsKit is built in the open. Every package, every doc, every example — MIT-licensed and community-maintained. Your PR makes the ecosystem better for everyone. ## Ways to help | If you have… | Do this | |--------------|---------| | **5 minutes** | ⭐ Star the [repo](https://github.com/AgentsKit-io/agentskit), share the project on X/BlueSky | | **30 minutes** | File a bug, improve a doc (every page has an **Edit on GitHub** link), triage an issue | | **A few hours** | Grab a [good-first-issue](https://github.com/AgentsKit-io/agentskit/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22), answer a [discussion](https://github.com/AgentsKit-io/agentskit/discussions) | | **A weekend** | Ship a new adapter, tool, skill, or example; write a recipe | | **Ongoing** | Become a maintainer — show up consistently, we'll hand you commit access | ## Where to start - **[Good-first-issues →](/docs/reference/contribute/good-first-issues)** — curated issues ready to grab - **[Local setup →](/docs/reference/contribute/local-setup)** — clone, install, run tests - **[Commit style →](/docs/reference/contribute/commit-style)** — conventional commits, changesets - **[RFC process →](/docs/reference/contribute/rfc-process)** — for larger proposals - **[Roadmap →](/docs/reference/contribute/roadmap)** — what we're building next ## Ground rules 1. **Be kind.** Code reviews are about code, not people. 2. **Keep `@agentskit/core` lean.** Zero deps. Under 10KB gzipped. Types + contracts only. 3. **Every package is plug-and-play.** If your change breaks that, rethink it. 4. **Tests > vibes.** If you change behavior, add a test. 5. **Docs ship with code.** Public APIs need docstrings + an example. ## Start a new discussion Not sure what to work on? Pick a prompt and start the conversation. Discussions shape the roadmap. | Topic | Starter | |-------|---------| | 🧩 **Your use case** | [Share how you're using AgentsKit →](https://github.com/AgentsKit-io/agentskit/discussions/new?category=show-and-tell) | | 🔌 **Missing provider / tool** | [Propose an adapter or tool →](https://github.com/AgentsKit-io/agentskit/discussions/new?category=ideas&title=Adapter%3A%20%3Cprovider%3E&body=What%20provider%3F%0AWhy%20does%20it%20matter%3F%0ALink%20to%20API%20docs%3A) | | 🧪 **Testing / DX friction** | [What's clunky? →](https://github.com/AgentsKit-io/agentskit/discussions/new?category=ideas&title=DX%3A%20%3Cwhat%20feels%20clunky%3E) | | 🎨 **Theming / design patterns** | [Share themes or component patterns →](https://github.com/AgentsKit-io/agentskit/discussions/new?category=show-and-tell&title=Theme%3A%20%3Cname%3E) | | 🏗️ **Architecture question** | [Ask how something should compose →](https://github.com/AgentsKit-io/agentskit/discussions/new?category=q-a) | | 📦 **New package idea** | [Propose a package before RFC →](https://github.com/AgentsKit-io/agentskit/discussions/new?category=ideas&title=Package%3A%20%40agentskit%2F%3Cname%3E) | | 🧵 **Migration story** | [Share your migration from X →](https://github.com/AgentsKit-io/agentskit/discussions/new?category=show-and-tell&title=Migrated%20from%20%3Cframework%3E) | | 🐛 **Reproducible bug** | [File a bug with a repro →](https://github.com/AgentsKit-io/agentskit/issues/new?labels=bug&title=bug%3A%20%3Cshort%20description%3E) | | ✨ **Recipe request** | [What recipe do you wish existed? →](https://github.com/AgentsKit-io/agentskit/discussions/new?category=ideas&title=Recipe%3A%20%3Cwhat%20you%27d%20build%3E) | | 📚 **Docs gap** | [Point to what's confusing →](https://github.com/AgentsKit-io/agentskit/discussions/new?category=ideas&title=Docs%3A%20%3Cpage%20or%20topic%3E) | Still undecided? Open a [Q&A discussion](https://github.com/AgentsKit-io/agentskit/discussions/new?category=q-a) — maintainers will help you scope something that fits your time. --- # Commit style Source: https://www.agentskit.io/docs/reference/contribute/commit-style > Conventional commits, changesets, and the PR checklist. ## Conventional commits Format: ``` (): [optional body] [optional footer: closes #nnn] ``` **Types:** `feat`, `fix`, `docs`, `refactor`, `test`, `chore`, `perf`, `ci`, `style`. **Scope:** package or area — `core`, `react`, `adapters`, `docs`, `ci`, etc. **Examples:** ``` feat(adapters): add Mistral adapter with streaming fix(react): useChat stop() races with in-flight token docs(recipes): add cost-guarded chat refactor(core): split controller events into sub-emitter test(ink): cover keyboard navigation across message list ``` Subject ≤ 72 chars, imperative mood ("add", not "added"). Body wraps at 100. ## Enforcement (planned — good-first-issue available) Today, commit-message discipline is review-time: maintainers will flag a non-conventional commit and ask you to amend. We want this **automated** — and wiring it up is a perfect first PR. ### Target setup - **`@commitlint/cli` + `@commitlint/config-conventional`** — validates the message - **`commit-msg` git hook** (via husky or lefthook) — blocks invalid commits locally - **CI check** — `commitlint` step in `.github/workflows/ci.yml` so bad commits fail the PR ### What a complete PR looks like 1. Add `commitlint.config.cjs`: ```js module.exports = { extends: ['@commitlint/config-conventional'] } ``` 2. Add devDeps at the **workspace root** `package.json`: ```json "@commitlint/cli": "^19.0.0", "@commitlint/config-conventional": "^19.0.0", "husky": "^9.0.0" ``` 3. Add `prepare` script + `commit-msg` hook: ```bash pnpm exec husky init echo 'pnpm exec commitlint --edit "$1"' > .husky/commit-msg ``` 4. Add CI step: ```yaml - run: pnpm exec commitlint --from=${{ github.event.pull_request.base.sha }} --to=${{ github.sha }} ``` 5. Update this page to reflect reality. If you want to claim it, comment on issue [`docs(contribute): wire commitlint + husky`](https://github.com/AgentsKit-io/agentskit/issues?q=commitlint) (or open one). ### Until then Eyeball your commit message against the types + examples above. `git commit --amend` is your friend. ## Changesets (for publishable packages) If your PR changes a package that ships to npm, add a changeset: ```bash pnpm changeset ``` Walk through the prompts. This generates a markdown file describing the change and its semver bump. Commit it. ## PR checklist Before marking a PR ready for review: - [ ] `pnpm lint` passes - [ ] `pnpm test` passes - [ ] New/changed behavior has a test - [ ] Public API changes have a docstring + doc example - [ ] Changeset added (if a published package changed) - [ ] Commit messages follow conventional commits - [ ] PR description explains **why**, not just what ## Review cadence - Draft PRs are fine — open early to show direction. - Maintainers triage within 2 business days. - Expect 1–3 review rounds. Be kind, we will too. --- # Docs MDX components Source: https://www.agentskit.io/docs/reference/contribute/docs-components > Embeddable components available inside every .mdx file — Stackblitz, CodeSandbox, GIFs, Mermaid. Every MDX page under `/docs` has access to these components. No import required. ## `` Live editable sandbox for any GitHub path. ```mdx ``` | Prop | Type | Default | |---|---|---| | `project` | `string` | required — `owner/repo/tree/branch/path` or Stackblitz id | | `file` | `string` | — | | `height` | `number` | 520 | | `title` | `string` | auto | | `lazy` | `boolean` | `true` (click-to-load, keeps LCP fast) | ## `` Mirror shape for CodeSandbox-hosted examples. ```mdx ``` ## `` A11y-aware GIF with reduced-motion fallback. ```mdx ``` Respects `prefers-reduced-motion` — shows the PNG poster + a link to the animation for users with motion sensitivity. ## `` Inline diagrams. See [`components/mermaid.tsx`](https://github.com/AgentsKit-io/agentskit/blob/main/apps/docs-next/components/mermaid.tsx). ## Using all together A typical recipe page mixes code blocks, a GIF showing the outcome, and a live Stackblitz: ```mdx ## What you'll build ## Live sandbox ## Copy-paste \`\`\`ts // ... \`\`\` ``` ## Related - [Contribute → package docs](./package-docs) - Issue #481/#482/#483/#484 — future MDX components (LivePlayground, better GIF helpers). --- # Good first issues Source: https://www.agentskit.io/docs/reference/contribute/good-first-issues > Curated issues ready to grab. Pick one, comment on it to claim, and ship a PR. import { GoodFirstIssuesList } from '@/components/contribute/issues-list' Curated, tractable issues. Each ships end-to-end in a few hours. Comment on the issue to claim it — we'll reply within a day. ## Seeded starter issues If the live list above is short, any of these are fair game — open an issue using the PR template and link back to this page. Maintainers will triage + label `good first issue`. ### Dev experience - **Wire commitlint + husky** — enforce conventional commits (see [Commit style](/docs/reference/contribute/commit-style) for the full plan) - **Add `.editorconfig`** — consistent indentation, line endings, trailing newlines across the repo - **Add `pnpm fresh`** script — one-shot clean of `node_modules`, `dist`, `.turbo`, `tsconfig.tsbuildinfo` across the workspace - **VSCode recommended-extensions file** — ship `.vscode/extensions.json` with ESLint, Prettier, Biome, MDX - **`renovate.json`** — opt-in Renovate config with grouped updates for non-major deps - **`pnpm doctor` Turbo-pipelined health check** — verifies Node version, pnpm version, all workspace deps installed ### Documentation - **Fix broken links** — crawl `apps/docs-next/content/docs` with `linkinator` and fix any 404s - **Screenshot every example** — add static PNGs to each `/docs/reference/examples/*` page for SEO + social-card previews - **Dark-mode contrast audit** — run Axe DevTools on every docs page, file sub-issues per contrast failure - **Translate the homepage to `pt-BR` / `es`** — Fumadocs i18n is already wired; add strings - **Add an ADR index page** — auto-generated list of all ADRs with status (proposed / accepted / superseded) ### Adapters - **Add `@agentskit/adapters/mistral`** — implement the `Adapter` contract against Mistral's streaming REST API - **Add `@agentskit/adapters/groq`** — Groq chat-completions with streaming - **Add `@agentskit/adapters/cerebras`** — Cerebras inference with streaming - **Add `@agentskit/adapters/perplexity`** — Perplexity Sonar with citations - **Token-usage normalization** — ensure every adapter emits `usage` in the same shape via `simulateStream` helper ### Tools - **`@agentskit/tools/datetime`** — `now()`, `parse()`, `format()`, `addDays()` — deterministic, zero-deps - **`@agentskit/tools/math`** — expression evaluator (mathjs wrapper) with schema validation - **`@agentskit/tools/http`** — safe HTTP fetch with allow-list + rate limit - **`@agentskit/tools/sqlite`** — read-only query tool against a local SQLite file, parameterized only - **Confirmation wrapper** — higher-order tool that prompts the user before executing a destructive tool ### React / Ink - **``** — copy / regenerate / edit buttons that wire into `useChat` - **Virtualized message list** — drop-in replacement for long chats (1000+ messages) - **Ink mouse support** — click-to-focus for input bar in supported terminals - **Keyboard shortcut cheat sheet** — `?` opens a modal listing all shortcuts in React + Ink ### Memory / RAG / Eval - **`@agentskit/memory/sqlite`** — persistent chat history via better-sqlite3 - **Chunking strategies** — semantic-split alongside the existing char-split in `@agentskit/rag` - **Citation markers** — `` refs in retrieved-document output that link to sources - **Benchmark dashboard** — `@agentskit/eval` CLI that writes a markdown report to `./eval-report.md` - **Golden-regression tests** — snapshot of eval results blocking PRs that regress > 5% ### Observability - **LangSmith exporter** — `@agentskit/observability/langsmith` with trace + run logging - **OpenTelemetry exporter** — OTLP gRPC + HTTP - **Console pretty-printer** — colored, structured log output for local development ### Examples / recipes - **Realtime cursor chat** — two tabs sharing a session via `@agentskit/memory/redis` - **PDF Q&A with citations** — upload a PDF, ask questions, show source pages - **Discord bot** — webhook-based, streaming responses to a channel - **GitHub PR reviewer** — action that runs an AgentsKit agent on every PR ## How to claim an issue 1. Comment `/claim` (or just "I'll take this") on the issue. 2. Fork, branch, open a draft PR early — it's fine if it's rough. 3. Push `ready for review` when tests pass. 4. A maintainer reviews within 2 business days. ## Don't see anything? - Browse [all help-wanted issues](https://github.com/AgentsKit-io/agentskit/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) - Look at the [roadmap](/docs/reference/contribute/roadmap) - Propose something in [discussions](https://github.com/AgentsKit-io/agentskit/discussions) --- # Local setup Source: https://www.agentskit.io/docs/reference/contribute/local-setup > Clone, install, and run the AgentsKit monorepo locally in under 2 minutes. Clone, install, run. Monorepo uses pnpm workspaces + Turborepo. ## Prerequisites - Node.js **22+** - pnpm **9+** (`corepack enable`) - Git ## Clone + install ```bash git clone https://github.com/AgentsKit-io/agentskit.git cd agentskit pnpm install ``` ## Everyday commands ```bash pnpm build # build all packages (turborepo-cached) pnpm test # run every package's vitest suite pnpm lint # tsc --noEmit across the workspace pnpm dev # watch mode — rebuilds packages on save ``` ## Hot reload `pnpm dev` runs tsup `--watch` across every package in parallel. When you save a source file: 1. The changed package rebuilds in < 1s (incremental). 2. Any **app** consuming it (docs-next, example-react, example-ink) hot-reloads automatically — Next.js HMR for `docs-next` / `example-react`, tsx watch for `example-ink`. 3. No manual rebuild, no restart. **Tip:** run two terminals: ```bash # terminal 1 — rebuild packages on save pnpm dev # terminal 2 — run the app you're iterating on pnpm --filter @agentskit/example-react dev # or pnpm --filter @agentskit/docs-next dev ``` Edit `packages/react/src/useChat.ts` → example-react re-renders in ~1s. No config needed. ## Single package ```bash pnpm --filter @agentskit/core test pnpm --filter @agentskit/react build pnpm --filter @agentskit/adapters test -- --watch ``` ## Run the docs site locally ```bash pnpm --filter @agentskit/docs-next dev ``` Open http://localhost:3000. ## Run the React playground ```bash pnpm --filter @agentskit/example-react dev ``` ## Monorepo layout ``` packages/ core/ ← zero-deps foundation (10KB) adapters/ ← OpenAI, Anthropic, Gemini, … react/ ← React hooks + UI ink/ ← terminal UI cli/ ← `agentskit` binary runtime/ ← standalone agent runtime tools/ skills/ memory/ rag/ sandbox/ observability/ eval/ templates/ apps/ docs-next/ ← this site (Next.js + Fumadocs) example-react/ ← React playground example-ink/ ← Ink playground example-runtime/ ← runtime-only example ``` ## Troubleshooting - **`Cannot find module '@agentskit/core'`** — run `pnpm build` first. Linting depends on built `dist/` types. - **Lockfile conflicts** — delete `node_modules` + `pnpm-lock.yaml`, run `pnpm install`. - **Node version errors** — make sure `node -v` reports 22+. --- # Package documentation checklist Source: https://www.agentskit.io/docs/reference/contribute/package-docs > Use this when adding or changing a package or its public API. Use this when adding or changing a package or its public API. 1. **Purpose** — When to use / when not to use. 2. **Install** — `npm i` line; note peers (usually `@agentskit/core` via feature packages). 3. **Public surface** — Primary exports aligned with `src/index.ts` (details in TypeDoc). 4. **Configuration** — Options tables for main factories. 5. **Examples** — Happy path + one production-oriented or edge-case example. 6. **Integration** — Links to adjacent packages (keep the **See also** line at the bottom of each guide short). 7. **Troubleshooting** — Short FAQ (errors, env vars, version skew). When exports change, update the guide and ensure `pnpm --filter @agentskit/docs build` still passes (`docs:api` regenerates TypeDoc). --- # RFC process Source: https://www.agentskit.io/docs/reference/contribute/rfc-process > For larger proposals — new packages, breaking changes, or architectural shifts. Keep it lightweight. For anything bigger than a feature — a new package, a breaking change, or an architectural shift — open an RFC before the PR. It saves everyone time. ## When you need an RFC - New published package - Breaking change to a public contract (Adapter, Tool, Skill, Memory, Retriever, Runtime) - Change to the core event stream - Anything that touches 3+ packages Skip the RFC for bug fixes, new adapters/tools/skills (if they implement existing contracts), docs, and internal refactors. ## Process 1. **Open a [discussion](https://github.com/AgentsKit-io/agentskit/discussions)** under the "RFC" category. 2. **Template**: - **Motivation** — what problem are we solving? - **Proposal** — what changes, in plain English - **API sketch** — types + a tiny example - **Alternatives considered** — what else did you look at? - **Migration** — what breaks, how do users migrate? 3. **Wait for 2+ maintainer approvals** (thumbs, not vibes). 4. **Open a tracking issue** with checkboxes. 5. **PR away.** Keep it short. A great RFC is 200–500 words, not 3000. ## Examples of good RFCs Coming soon — link to merged RFCs here. --- # Roadmap Source: https://www.agentskit.io/docs/reference/contribute/roadmap > What we're building next, what's open, what's landed. Living document. Big-picture direction lives here. Day-to-day work lives in [issues](https://github.com/AgentsKit-io/agentskit/issues). ## Now (v1.x) - **Core stability** — freeze public contracts, add semver guarantees - **Adapter coverage** — ship a known-good adapter for every major provider - **Streaming polish** — consistent `consumeStream` semantics across adapters - **Docs** — every package has overview, API reference, and 1+ recipe ## Next (v1.x+) - **Multi-agent orchestration** — planner + workers, first-class delegation - **Sandbox parity** — E2B + WebContainer with identical tool surface - **Eval suite** — out-of-the-box benchmarks for common agent tasks - **Observability** — LangSmith + OpenTelemetry, zero-config defaults ## Later - **Sync / offline agents** — CRDT-backed memory, replicable sessions - **Voice** — streaming STT + TTS tools - **Native mobile UI** — React Native parity with `@agentskit/react` ## Where to help Every bullet above is fair game. Pick one that excites you, open a [discussion](https://github.com/AgentsKit-io/agentskit/discussions), and propose a scope. See [good-first-issues](/docs/reference/contribute/good-first-issues) for small concrete starts. --- # Examples Source: https://www.agentskit.io/docs/reference/examples > Interactive demos. For copy-paste code, see Recipes. import { ContributeCallout } from '@/components/contribute/contribute-callout' Interactive demos showing what you can build with AgentsKit. Every demo page renders a live chat component. **Looking for copy-paste code?** Head to [Recipes](/docs/reference/recipes) — end-to-end runnable snippets grouped by theme. Each example below points to its recipe counterpart. ## Chat patterns | Demo | Runnable recipe | |---|---| | [Basic chat](/docs/reference/examples/basic-chat) | [custom-adapter](/docs/reference/recipes/custom-adapter) | | [Tool use](/docs/reference/examples/tool-use) | [tool-composer](/docs/reference/recipes/tool-composer) | | [Multi-model](/docs/reference/examples/multi-model) | [adapter-router](/docs/reference/recipes/adapter-router) | | [Code assistant](/docs/reference/examples/code-assistant) | [code-reviewer](/docs/reference/recipes/code-reviewer) | | [Customer support](/docs/reference/examples/support-bot) | [discord-bot](/docs/reference/recipes/discord-bot) | | [RAG chat](/docs/reference/examples/rag-chat) | [rag-chat](/docs/reference/recipes/rag-chat) | | [Agent actions](/docs/reference/examples/agent-actions) | [generative-ui](/docs/reference/recipes/generative-ui) | | [Markdown chat](/docs/reference/examples/markdown-chat) | [multi-modal](/docs/reference/recipes/multi-modal) | ## Agents + runtime | Demo | Runnable recipe | |---|---| | [Runtime agent](/docs/reference/examples/runtime-agent) | [schema-first-agent](/docs/reference/recipes/schema-first-agent) | | [Multi-agent planning](/docs/reference/examples/multi-agent) | [multi-agent-topologies](/docs/reference/recipes/multi-agent-topologies) · [research-team](/docs/reference/recipes/research-team) | ## Data pipeline | Demo | Runnable recipe | |---|---| | [RAG pipeline](/docs/reference/examples/rag-pipeline) | [doc-loaders](/docs/reference/recipes/doc-loaders) · [rag-reranking](/docs/reference/recipes/rag-reranking) | | [Eval runner](/docs/reference/examples/eval-runner) | [eval-suite](/docs/reference/recipes/eval-suite) · [evals-ci](/docs/reference/recipes/evals-ci) | ## UI framework integration | Demo | Runnable recipe | |---|---| | [MUI chat](/docs/reference/examples/mui-chat) | [framework-adapters](/docs/reference/recipes/framework-adapters) | | [shadcn chat](/docs/reference/examples/shadcn-chat) | [framework-adapters](/docs/reference/recipes/framework-adapters) | --- # Agent Actions Source: https://www.agentskit.io/docs/reference/examples/agent-actions > AI agents that generate live, interactive UI — task trackers, dashboards, forms. The agent doesn't just respond with text, it builds working interfaces. import { AgentActions } from '@/components/examples/AgentActions' AI agents that generate live, interactive UI — task trackers, dashboards, forms. The agent doesn't just respond with text, it builds working interfaces. ## With AgentsKit Combine `useChat` with custom renderers for tool call results: ```tsx import { useChat, ChatContainer, Message } from '@agentskit/react' function AgentChat() { const chat = useChat({ adapter }) return ( {chat.messages.map(msg => (
{msg.toolCalls?.map(tc => ( ))}
))}
) } ``` --- # Basic Chat Source: https://www.agentskit.io/docs/reference/examples/basic-chat > The simplest use case — streaming AI conversation with auto-scroll, stop button, and keyboard handling. All in 10 lines with AgentsKit. import { BasicChat } from '@/components/examples/BasicChat' The simplest use case — streaming AI conversation with auto-scroll, stop button, and keyboard handling. All in 10 lines with AgentsKit. ## With AgentsKit ```tsx import { useChat, ChatContainer, Message, InputBar } from '@agentskit/react' import { anthropic } from '@agentskit/adapters' import '@agentskit/react/theme' function Chat() { const chat = useChat({ adapter: anthropic({ apiKey: 'key', model: 'claude-sonnet-4-6' }) }) return ( {chat.messages.map(msg => )} ) } ``` --- # Code Assistant Source: https://www.agentskit.io/docs/reference/examples/code-assistant > Streaming code with syntax highlighting. AgentsKit's CodeBlock component renders code beautifully as it streams in. import { CodeAssistant } from '@/components/examples/CodeAssistant' Streaming code with syntax highlighting. AgentsKit's `CodeBlock` component renders code beautifully as it streams in. ## With AgentsKit ```tsx import { useChat, ChatContainer, Message, CodeBlock, Markdown } from '@agentskit/react' function CodeChat() { const chat = useChat({ adapter }) return ( {chat.messages.map(msg => ( ))} ) } ``` --- # Evaluation Runner Source: https://www.agentskit.io/docs/reference/examples/eval-runner > Benchmark your agents against test suites. Measure accuracy, latency, and cost with @agentskit/eval. Benchmark your agents against test suites. Measure accuracy, latency, and cost with `@agentskit/eval`. ## Basic Usage ```typescript import { createEvalRunner } from '@agentskit/eval' import { createRuntime } from '@agentskit/runtime' const runtime = createRuntime({ adapter: yourAdapter }) const runner = createEvalRunner({ agent: (task) => runtime.run(task), }) const results = await runner.run({ name: 'QA accuracy', cases: [ { input: 'What is 2+2?', expected: '4' }, { input: 'Capital of France?', expected: (result) => result.includes('Paris') }, { input: 'Translate "hello" to Spanish', expected: 'hola' }, ], }) console.log(`Accuracy: ${(results.accuracy * 100).toFixed(1)}%`) console.log(`Passed: ${results.passed}/${results.totalCases}`) ``` ## With Custom Metrics ```typescript const results = await runner.run({ name: 'Performance benchmark', cases: [ { input: 'Summarize this article...', expected: (r) => r.length < 500 }, ], }) // Per-case results include latency and token usage results.results.forEach((r) => { console.log(`${r.passed ? 'PASS' : 'FAIL'} | ${r.latencyMs}ms | ${r.input.slice(0, 40)}...`) }) ``` ## CI Integration ```bash # Run evals as part of CI node eval.ts && echo "All evals passed" || exit 1 ``` --- # Markdown Chat Source: https://www.agentskit.io/docs/reference/examples/markdown-chat > Rich formatted responses — headings, tables, code blocks, lists, and blockquotes. AgentsKit's Markdown component renders everything beautifully as it streams. import { MarkdownChat } from '@/components/examples/MarkdownChat' Rich formatted responses — headings, tables, code blocks, lists, and blockquotes. AgentsKit's `Markdown` component renders everything beautifully as it streams. ## With AgentsKit ```tsx import { useChat, ChatContainer, Message, Markdown, InputBar } from '@agentskit/react' function Writer() { const chat = useChat({ adapter }) return ( {chat.messages.map(msg => ( ))} ) } ``` --- # MUI Chat Source: https://www.agentskit.io/docs/reference/examples/mui-chat > AgentsKit's useChat hook styled with Material UI components. This demo recreates the MUI look — in a real app, you'd use actual MUI imports. import { MuiChat } from '@/components/examples/MuiChat' AgentsKit's `useChat` hook styled with Material UI components. This demo recreates the MUI look — in a real app, you'd use actual MUI imports. ## With real MUI ```tsx import { useChat } from '@agentskit/react' import { Paper, TextField, Button, List, ListItem, Avatar, Typography } from '@mui/material' import { anthropic } from '@agentskit/adapters' function MuiChat() { const chat = useChat({ adapter: anthropic({ apiKey: 'key', model: 'claude-sonnet-4-6' }) }) return ( {chat.messages.map(msg => ( {msg.role === 'user' ? 'U' : 'A'} {msg.content} ))}
{ e.preventDefault(); chat.send(chat.input) }} style={{ display: 'flex', padding: 16, gap: 8 }}> chat.setInput(e.target.value)} label="Message" />
) } ``` --- # Multi-Agent Planning Source: https://www.agentskit.io/docs/reference/examples/multi-agent > A planner agent that breaks tasks into steps and delegates to specialists. Built with @agentskit/react and the planner skill. import { MultiAgentChat } from '@/components/examples/MultiAgentChat' A planner agent that breaks tasks into steps and delegates to specialists. Built with `@agentskit/react` and the planner skill. ## Code ```tsx import { useChat, ChatContainer, Message, InputBar } from '@agentskit/react' import { planner } from '@agentskit/skills' import '@agentskit/react/theme' function MultiAgentDemo() { const chat = useChat({ adapter: yourAdapter, skills: [planner], systemPrompt: 'Break tasks into steps. Delegate research and coding to specialists.', }) return ( {chat.messages.map(msg => )} ) } ``` ## With Real Delegation (Runtime) For actual multi-agent execution where child agents run independently: ```typescript import { createRuntime } from '@agentskit/runtime' import { planner, researcher, coder } from '@agentskit/skills' const runtime = createRuntime({ adapter: yourAdapter, delegates: { researcher: { skill: researcher }, coder: { skill: coder }, }, }) const result = await runtime.run('Build a REST API for task management', { skill: planner, }) ``` ## Try it ```bash cd apps/example-multi-agent pnpm dev ``` --- # Multi-Model Comparison Source: https://www.agentskit.io/docs/reference/examples/multi-model > Compare responses from different AI models side-by-side. Same input, different adapters — AgentsKit makes it trivial. import { MultiModelChat } from '@/components/examples/MultiModelChat' Compare responses from different AI models side-by-side. Same input, different adapters — AgentsKit makes it trivial. ## With AgentsKit Just use two `useChat` hooks with different adapters: ```tsx import { useChat, ChatContainer, Message, InputBar } from '@agentskit/react' import { anthropic, openai } from '@agentskit/adapters' function Compare() { const claude = useChat({ adapter: anthropic({ apiKey, model: 'claude-sonnet-4-6' }) }) const gpt = useChat({ adapter: openai({ apiKey, model: 'gpt-4o' }) }) const sendBoth = (text: string) => { claude.send(text) gpt.send(text) } return (
{claude.messages.map(m => )} {gpt.messages.map(m => )}
) } ``` --- # RAG Chat Source: https://www.agentskit.io/docs/reference/examples/rag-chat > Retrieval-Augmented Generation — chat with your documents. Show citations, source references, and document context alongside AI responses. import { RAGChat } from '@/components/examples/RAGChat' Retrieval-Augmented Generation — chat with your documents. Show citations, source references, and document context alongside AI responses. ## With AgentsKit ```tsx import { useChat, ChatContainer, Message, InputBar } from '@agentskit/react' function RAGChat() { const chat = useChat({ adapter: myRAGAdapter, initialMessages: [{ id: 'sys', role: 'system', content: 'Answer based on the provided documents. Cite sources.', status: 'complete', createdAt: new Date() }], }) return ( {chat.messages.map(msg => ( ))} ) } ``` --- # RAG Pipeline Source: https://www.agentskit.io/docs/reference/examples/rag-pipeline > Ingest documents, embed them, and retrieve relevant context during chat. Uses @agentskit/rag with any embedder and vector store. Ingest documents, embed them, and retrieve relevant context during chat. Uses `@agentskit/rag` with any embedder and vector store. ## Setup ```typescript import { createRAG } from '@agentskit/rag' import { openaiEmbedder } from '@agentskit/adapters' const rag = createRAG({ embed: openaiEmbedder({ apiKey: process.env.OPENAI_API_KEY! }), store: yourVectorStore, // SQLite, Redis, or in-memory chunkSize: 512, chunkOverlap: 50, }) ``` ## Ingest Documents ```typescript await rag.ingest([ { id: 'readme', content: readFileSync('README.md', 'utf-8'), source: 'README.md' }, { id: 'guide', content: readFileSync('docs/guide.md', 'utf-8'), source: 'guide.md' }, ]) ``` ## Search Directly ```typescript const results = await rag.search('how to configure tools', { topK: 3 }) results.forEach(doc => { console.log(`[${doc.source}] ${doc.content.slice(0, 100)}...`) }) ``` ## Use with Chat `createRAG` returns a `Retriever` — pass it directly to `useChat` or the runtime: ```tsx import { useChat } from '@agentskit/react' function RAGChat() { const chat = useChat({ adapter: yourAdapter, retriever: rag, // retrieved context auto-injected into system prompt }) // ... render chat UI } ``` ## Custom Chunking ```typescript const rag = createRAG({ embed: yourEmbedder, store: yourStore, split: (text) => text.split('\n\n'), // paragraph-based chunking }) ``` --- # Runtime Agent Source: https://www.agentskit.io/docs/reference/examples/runtime-agent > Run a standalone agent from code — no UI required. The runtime executes a ReAct loop: call tools, observe results, decide next action. Run a standalone agent from code — no UI required. The runtime executes a ReAct loop: call tools, observe results, decide next action. {/* GIF generated from apps/example-runtime/demo.tape via VHS */} {/* ![Runtime Agent Demo](/img/examples/runtime-agent.gif) */} ## Code ```typescript import { createRuntime } from '@agentskit/runtime' import { researcher } from '@agentskit/skills' import type { ToolDefinition, Observer, AgentEvent } from '@agentskit/core' const webSearch: ToolDefinition = { name: 'web_search', description: 'Search the web for information', schema: { type: 'object', properties: { q: { type: 'string', description: 'Search query' } }, required: ['q'], }, execute: async (args) => { // Replace with real search API return `Results for "${args.q}": [1] Paper A, [2] Paper B` }, } const logger: Observer = { name: 'console', on(event: AgentEvent) { if (event.type === 'agent:step') console.error(`[step ${event.step}]`) if (event.type === 'tool:start') console.error(`[tool] ${event.name}`) }, } const runtime = createRuntime({ adapter: yourAdapter, // openai, anthropic, etc. tools: [webSearch], observers: [logger], }) const result = await runtime.run('Research AI safety developments', { skill: researcher, }) console.log(result.content) // Completed in 2 steps, 1 tool call ``` ## Try it ```bash cd apps/example-runtime pnpm dev ``` Set `OPENAI_API_KEY` for real provider, or run without for demo mode. --- # shadcn Chat Source: https://www.agentskit.io/docs/reference/examples/shadcn-chat > AgentsKit's useChat hook styled with shadcn/ui patterns. This demo recreates the shadcn look — in a real app, you'd use actual shadcn components. import { ShadcnChat } from '@/components/examples/ShadcnChat' AgentsKit's `useChat` hook styled with shadcn/ui patterns. This demo recreates the shadcn look — in a real app, you'd use actual shadcn components. ## With real shadcn/ui ```tsx import { useChat } from '@agentskit/react' import { Card, CardContent, CardFooter } from '@/components/ui/card' import { Input } from '@/components/ui/input' import { Button } from '@/components/ui/button' import { ScrollArea } from '@/components/ui/scroll-area' import { Avatar, AvatarFallback } from '@/components/ui/avatar' import { anthropic } from '@agentskit/adapters' function Chat() { const chat = useChat({ adapter: anthropic({ apiKey: 'key', model: 'claude-sonnet-4-6' }) }) return ( {chat.messages.map(msg => (
{msg.role === 'user' ? 'U' : 'A'}
{msg.content}
))}
chat.setInput(e.target.value)} placeholder="Message..." onKeyDown={e => e.key === 'Enter' && chat.send(chat.input)} />
) } ``` --- # Customer Support Bot Source: https://www.agentskit.io/docs/reference/examples/support-bot > Quick replies, typing indicators, escalation flows. Build production support experiences with AgentsKit's headless components. import { SupportBot } from '@/components/examples/SupportBot' Quick replies, typing indicators, escalation flows. Build production support experiences with AgentsKit's headless components. ## With AgentsKit ```tsx import { useChat, ChatContainer, Message, InputBar, ThinkingIndicator } from '@agentskit/react' function SupportChat() { const chat = useChat({ adapter: myAdapter, initialMessages: [{ id: '1', role: 'assistant', content: 'How can I help?', status: 'complete', createdAt: new Date() }], }) return ( {chat.messages.map(msg => )} ) } ``` --- # Tool Use Source: https://www.agentskit.io/docs/reference/examples/tool-use > AI assistants that call functions — weather lookups, web search, database queries. AgentsKit renders tool calls with expandable cards showing arguments and results. import { ToolUseChat } from '@/components/examples/ToolUseChat' AI assistants that call functions — weather lookups, web search, database queries. AgentsKit renders tool calls with expandable cards showing arguments and results. ## With AgentsKit The `ToolCallView` component handles everything: ```tsx import { useChat, ChatContainer, Message, ToolCallView } from '@agentskit/react' function Chat() { const chat = useChat({ adapter }) return ( {chat.messages.map(msg => (
{msg.toolCalls?.map(tc => ( ))}
))}
) } ``` --- # @agentskit/adapters Source: https://www.agentskit.io/docs/reference/packages/adapters > 20+ LLM chat + embedder adapters, plus router / ensemble / fallback. Swap providers with one import line. ## When to reach for it - You need to talk to a hosted LLM (Anthropic, OpenAI, Gemini, Mistral, Cohere, Groq, Together, Fireworks, OpenRouter, Hugging Face, …). - You want local-only (Ollama, LM Studio, vLLM, llama.cpp). - You want to compose multiple candidates (`createRouter`, `createEnsembleAdapter`, `createFallbackAdapter`). - You want to test agents without hitting a real LLM (`mockAdapter`, `recordingAdapter`, `replayAdapter`). ## Install ```bash npm install @agentskit/adapters ``` ## Hello world ```ts import { anthropic } from '@agentskit/adapters' const adapter = anthropic({ apiKey: process.env.ANTHROPIC_API_KEY!, model: 'claude-sonnet-4-6' }) ``` ## Surface Hosted: `anthropic` · `openai` · `gemini` · `grok` · `deepseek` · `kimi` · `mistral` · `cohere` · `together` · `groq` · `fireworks` · `openrouter` · `huggingface` · `langchain` · `langgraph` · `vercelAI` · `generic` Local: `ollama` · `lmstudio` · `vllm` · `llamacpp` Embedders: `openaiEmbedder` · `geminiEmbedder` · `ollamaEmbedder` · `deepseekEmbedder` · `grokEmbedder` · `kimiEmbedder` · `createOpenAICompatibleEmbedder` Higher-order: `createRouter` · `createEnsembleAdapter` · `createFallbackAdapter` Testing: `mockAdapter` · `recordingAdapter` · `replayAdapter` · `inMemorySink` · `simulateStream` · `chunkText` · `fetchWithRetry` ## Recipes - [More providers](/docs/reference/recipes/more-providers) - [Adapter router](/docs/reference/recipes/adapter-router) - [Ensemble](/docs/reference/recipes/adapter-ensemble) - [Fallback chain](/docs/reference/recipes/fallback-chain) - [Custom adapter](/docs/reference/recipes/custom-adapter) - [Simulate stream](/docs/reference/recipes/simulate-stream) ## Stability - **Version:** `0.9.1` - **Tier:** beta - **Contract:** evolving - **Roadmap:** see [packages roadmap](./roadmap) for what this package needs to reach v1.0. ## Related - [Concepts: Adapter](/docs/get-started/concepts/adapter) - [Data → Providers](/docs/data/providers) - [For agents: adapters](/docs/for-agents/adapters) ## Source npm: [@agentskit/adapters](https://www.npmjs.com/package/@agentskit/adapters) · repo: [packages/adapters](https://github.com/AgentsKit-io/agentskit/tree/main/packages/adapters) --- # @agentskit/angular Source: https://www.agentskit.io/docs/reference/packages/angular > Angular service exposing chat state as a Signal + RxJS Observable. Same contract as @agentskit/react. ## Install ```bash npm install @agentskit/angular @angular/core rxjs @agentskit/adapters ``` ## Hello world ```ts import { Component, inject } from '@angular/core' import { AgentskitChat } from '@agentskit/angular' @Component({ selector: 'ak-chat', standalone: true, template: `
{{ m.content }}
`, }) export class ChatComponent { chat = inject(AgentskitChat) constructor() { this.chat.init({ adapter }) } } ``` ## Surface - `AgentskitChat` — `@Injectable({ providedIn: 'root' })`: - `init(config)` — bootstrap the controller. - `state: WritableSignal` — template-friendly. - `stream$: Observable` — RxJS. - Actions: `send` · `stop` · `retry` · `setInput` · `clear` · `approve` · `deny`. ## Siblings [React](/docs/reference/packages/react) · [Vue](/docs/reference/packages/vue) · [Svelte](/docs/reference/packages/svelte) · [Solid](/docs/reference/packages/solid) · [React Native](/docs/reference/packages/react-native) · [Ink](/docs/reference/packages/ink) ## Stability - **Version:** `0.2.0` - **Tier:** alpha - **Contract:** evolving - **Roadmap:** see [packages roadmap](./roadmap) for what this package needs to reach v1.0. ## Related - [UI + hooks](/docs/ui) - [For agents: angular](/docs/for-agents/angular) ## Source npm: [@agentskit/angular](https://www.npmjs.com/package/@agentskit/angular) · repo: [packages/angular](https://github.com/AgentsKit-io/agentskit/tree/main/packages/angular) --- # @agentskit/cli Source: https://www.agentskit.io/docs/reference/packages/cli > The agentskit CLI — init, chat, run, dev, doctor, ai, tunnel, rag, config. ## When to reach for it - You want to scaffold a new agent project. - You want to chat / run an agent from a terminal. - You want `agentskit ai ""` to generate a typed scaffold from plain language. ## Install ```bash npx @agentskit/cli # or globally: npm install -g @agentskit/cli ``` ## Commands | Command | Purpose | |---|---| | `agentskit init` | Scaffold a new project (react / ink / runtime / multi-agent). | | `agentskit chat` | Interactive chat in the terminal (Ink). | | `agentskit run ""` | Run an agent once. | | `agentskit dev` | Dev server with hot-reload. | | `agentskit doctor` | Diagnose env (providers, keys, tooling). | | `agentskit ai ""` | NL → typed `AgentSchema` + scaffolded project. | | `agentskit tunnel` | ngrok-style tunnel for webhooks. | | `agentskit rag` | Local RAG helpers (ingest / search). | | `agentskit config` | Read / write local config. | ## Programmatic helpers - `@agentskit/cli/ai` — `scaffoldAgent` · `writeScaffold` · `createAdapterPlanner`. ## Recipes - [agentskit ai](/docs/reference/recipes/agentskit-ai) ## Stability - **Version:** `0.8.2` - **Tier:** beta - **Contract:** evolving - **Roadmap:** see [packages roadmap](./roadmap) for what this package needs to reach v1.0. ## Related - [CLI (deep dive)](/docs/production/cli) - [Templates](/docs/reference/packages/templates) - [For agents: cli](/docs/for-agents/cli) ## Source npm: [@agentskit/cli](https://www.npmjs.com/package/@agentskit/cli) · repo: [packages/cli](https://github.com/AgentsKit-io/agentskit/tree/main/packages/cli) --- # @agentskit/core Source: https://www.agentskit.io/docs/reference/packages/core > The shared contract layer for AgentsKit: TypeScript types, the headless chat controller, stream helpers, and building blocks used by @agentskit/react, @agentskit/ink, @agentskit/runtime, and adapt import { ContributeCallout } from '@/components/contribute/contribute-callout' The shared **contract layer** for AgentsKit: TypeScript types, the headless chat controller, stream helpers, and building blocks used by `@agentskit/react`, `@agentskit/ink`, `@agentskit/runtime`, and adapters. **No third-party runtime dependencies** — keep this package small and stable. ## When to use - You implement a **custom adapter**, tool, memory, or UI on top of official types. - You need **`createChatController`** without React (advanced integrations). - You want to understand **messages, tool calls, and stream chunks** across the ecosystem. You usually **do not** import `core` directly in a typical React app except for types — prefer [`useChat`](../hooks/use-chat) and [`@agentskit/react`](../chat-uis/react). ## Install ```bash npm install @agentskit/core ``` Most feature packages already depend on `core`; you add it explicitly when authoring libraries or sharing types. ## Public exports (overview) ### Chat controller and config | Export | Role | |--------|------| | `createChatController` | Headless state machine: send, stream, tools, memory, skills, retriever | | Types: `ChatConfig`, `ChatController`, `ChatState`, `ChatReturn` | Configuration and controller shape | The controller merges system prompts, runs retrieval, dispatches tool calls, persists via `ChatMemory`, and exposes subscribe/update patterns consumed by UI packages. ### Primitives and streams | Export | Role | |--------|------| | `buildMessage` | Construct a typed `Message` | | `consumeStream` | Drive `StreamSource` → chunks + completion | | `createEventEmitter` | Internal event bus for observers | | `executeToolCall` | Run a tool from a `ToolCall` payload | | `safeParseArgs` | Parse JSON tool arguments safely | | `createToolLifecycle` | `init` / `dispose` for tools | | `generateId` | Stable IDs for messages and calls | ### Agent loop helpers | Export | Role | |--------|------| | `buildToolMap` | Name → `ToolDefinition` map | | `activateSkills` | Merge skill system prompts and skill-provided tools | | `executeSafeTool` | Guarded execution (confirmation hooks, errors) | ### Memory and RAG (lightweight) | Export | Role | |--------|------| | `createInMemoryMemory`, `createLocalStorageMemory` | Simple bundled memories for tests or demos | | `serializeMessages` / `deserializeMessages` | Persistence helpers | | `createStaticRetriever`, `formatRetrievedDocuments` | Retriever helpers for static context | Heavy backends live in [`@agentskit/memory`](../data-layer/memory); vector stores and chunking in [`@agentskit/rag`](../data-layer/rag). ### Error handling | Export | Role | |--------|------| | `AgentsKitError` | Base error class with `code`, `hint`, and `docsUrl` fields | | `AdapterError` | Thrown on adapter/stream failures; links to adapter docs | | `ToolError` | Thrown on tool lookup or execution failures; links to tool docs | | `MemoryError` | Thrown on memory load/save/deserialize failures; links to memory docs | | `ConfigError` | Thrown on invalid configuration; links to configuration docs | | `ErrorCodes` | `as const` map of all `AK_*` error code strings | See [Error handling](#error-handling-1) below for usage examples. ### Tool factory | Export | Role | |--------|------| | `defineTool` | Create a `ToolDefinition` with automatic type inference from a JSON Schema | | `DefineToolConfig` | Config type for `defineTool` — schema is narrowed to a const type | | `InferSchemaType` | Utility type: extract TypeScript args type from a JSON Schema | See [Type-safe tools with `defineTool`](#type-safe-tools-with-definetool) below. ### Types (high level) `AdapterFactory`, `StreamSource`, `StreamChunk`, `Message`, `ToolDefinition`, `ToolCall`, `SkillDefinition`, `ChatMemory`, `Retriever`, `VectorMemory`, `Observer`, `AgentEvent`, and related types — full signatures in TypeDoc (below). ## Example: inspect types in a custom tool ```ts import type { ToolDefinition, ToolExecutionContext } from '@agentskit/core' export const myTool: ToolDefinition = { name: 'greet', description: 'Greets a user by name.', schema: { type: 'object', properties: { name: { type: 'string' } }, required: ['name'], }, async execute(args: Record, _ctx: ToolExecutionContext) { const name = String(args.name ?? 'world') return `Hello, ${name}!` }, } ``` ## Example: headless controller (advanced) ```ts import { createChatController } from '@agentskit/core' import { anthropic } from '@agentskit/adapters' const chat = createChatController({ adapter: anthropic({ apiKey: process.env.ANTHROPIC_API_KEY!, model: 'claude-sonnet-4-6' }), }) chat.subscribe(() => { console.log(chat.getState().status, chat.getState().messages.length) }) await chat.send('Hello') ``` Prefer `useChat` in React apps — it wraps this pattern with hooks. ## Error handling AgentsKit uses a **didactic error system** inspired by the Rust compiler. Every error carries a machine-readable `code`, a human-readable `hint`, and a direct link to the relevant docs — so you can fix the problem without searching. ### Error classes ```ts import { AgentsKitError, AdapterError, ToolError, MemoryError, ConfigError, ErrorCodes, } from '@agentskit/core' ``` | Class | Thrown when | |-------|-------------| | `AgentsKitError` | Base class — all errors extend this | | `AdapterError` | Adapter is missing or a stream call fails | | `ToolError` | A tool is not found or its `execute` throws | | `MemoryError` | Memory load, save, or deserialization fails | | `ConfigError` | Required configuration is absent or invalid | ### Error shape ```ts class AgentsKitError extends Error { readonly code: string // e.g. 'AK_TOOL_EXEC_FAILED' readonly hint: string | undefined // actionable fix readonly docsUrl: string | undefined // direct docs link readonly cause: unknown // original error, if any } ``` `toString()` formats the error Rust-compiler-style: ``` error[AK_ADAPTER_MISSING]: No adapter provided --> Hint: Pass an adapter when creating the chat controller, e.g. createChatController({ adapter: openaiAdapter() }) --> Docs: https://www.agentskit.io/docs/adapters ``` ### ErrorCodes reference ```ts import { ErrorCodes } from '@agentskit/core' ErrorCodes.AK_ADAPTER_MISSING // adapter not provided ErrorCodes.AK_ADAPTER_STREAM_FAILED // streaming call failed ErrorCodes.AK_TOOL_NOT_FOUND // tool name not registered ErrorCodes.AK_TOOL_EXEC_FAILED // execute() threw ErrorCodes.AK_MEMORY_LOAD_FAILED // memory.load() failed ErrorCodes.AK_MEMORY_SAVE_FAILED // memory.save() failed ErrorCodes.AK_MEMORY_DESERIALIZE_FAILED // corrupt persisted state ErrorCodes.AK_CONFIG_INVALID // missing or bad config ``` ### Catching and narrowing errors ```ts import { AgentsKitError, ToolError, ErrorCodes } from '@agentskit/core' try { await runtime.run(task) } catch (err) { if (err instanceof ToolError) { if (err.code === ErrorCodes.AK_TOOL_EXEC_FAILED) { console.error('Tool execution failed:', err.hint) console.error('See:', err.docsUrl) } } else if (err instanceof AgentsKitError) { // any other AgentsKit-originating error console.error(err.toString()) } else { throw err // re-throw unknown errors } } ``` ### Throwing in custom tools When your own tool implementation encounters a recoverable error, wrap it for consistent formatting: ```ts import { ToolError, ErrorCodes } from '@agentskit/core' export const myTool = { name: 'fetch_record', async execute(args: { id: string }) { const record = await db.find(args.id) if (!record) { throw new ToolError({ code: ErrorCodes.AK_TOOL_EXEC_FAILED, message: `Record ${args.id} not found`, hint: 'Check that the id exists before calling fetch_record.', cause: undefined, }) } return record }, } ``` ## Type-safe tools with `defineTool` The `defineTool` factory infers the TypeScript type of `execute`'s `args` parameter directly from the JSON Schema you provide — no manual type annotation needed. ```ts import { defineTool } from '@agentskit/core' const getWeather = defineTool({ name: 'get_weather', description: 'Get the current weather for a city.', schema: { type: 'object', properties: { city: { type: 'string', description: 'City name, e.g. "Madrid"' }, units: { type: 'string', enum: ['metric', 'imperial'] }, }, required: ['city'], } as const, async execute(args) { // args.city → string (required) // args.units → string | undefined (optional) const res = await fetch(`https://wttr.in/${args.city}?format=j1`) return res.json() }, }) ``` The `as const` assertion on `schema` is what enables the inference — without it TypeScript cannot narrow the property types. ### `InferSchemaType` utility type When you want to reference the inferred args type outside of `defineTool`, use `InferSchemaType`: ```ts import type { InferSchemaType } from '@agentskit/core' const schema = { type: 'object', properties: { query: { type: 'string' }, limit: { type: 'integer' }, }, required: ['query'], } as const type SearchArgs = InferSchemaType // { query: string; limit?: number } ``` ## Troubleshooting | Issue | Mitigation | |-------|------------| | Type errors after upgrade | Pin all `@agentskit/*` to the same semver; `core` types move with the ecosystem. | | `createChatController` vs `useChat` | Controller is framework-agnostic; React hook adds state binding and Strict Mode safety. | | Bundle size concerns | Tree-shake unused exports; avoid importing server-only utilities in client bundles. | ## See also [Start here](../getting-started/read-this-first) · [Packages](./overview) · [TypeDoc](pathname:///agentskit/api-reference/) (`@agentskit/core`) · [React](../chat-uis/react) · [Ink](../chat-uis/ink) · [Adapters](../data-layer/adapters) · [Runtime](../agents/runtime) · [Tools](../agents/tools) · [Skills](../agents/skills) · [useChat](../hooks/use-chat) · [useStream](../hooks/use-stream) · [useReactive](../hooks/use-reactive) ## Stability - **Version:** `1.6.1` - **Tier:** stable - **Contract:** frozen - **Roadmap:** see [packages roadmap](./roadmap) for what this package needs to reach v1.0. --- # @agentskit/eval Source: https://www.agentskit.io/docs/reference/packages/eval > Eval suites + deterministic replay + snapshot testing + prompt diff + CI reporters. ## When to reach for it - You want to score agent quality with numbers, in CI. - You want deterministic replay (record once, replay forever). - You want Jest-style prompt snapshots with semantic tolerance. - You want a "git blame for prompts" — diff + attribution. ## Install ```bash npm install -D @agentskit/eval ``` ## Hello world ```ts import { runEval } from '@agentskit/eval' const result = await runEval({ agent: async (input) => (await runtime.run(input)).content, suite: { name: 'qa', cases: [{ input: 'Capital of France?', expected: 'Paris' }], }, }) console.log(`${result.passed}/${result.totalCases} passed`) ``` ## Surface - `runEval({ agent, suite })`. - `/replay`: `createRecordingAdapter` · `createReplayAdapter` · cassettes · `createTimeTravelSession` · `replayAgainst` · `summarizeReplay`. - `/snapshot`: `matchPromptSnapshot`. - `/diff`: `promptDiff` · `attributePromptChange` · `formatDiff`. - `/ci`: `renderJUnit` · `renderMarkdown` · `renderGitHubAnnotations` · `reportToCi`. ## Recipes - [Eval suite](/docs/reference/recipes/eval-suite) - [Deterministic replay](/docs/reference/recipes/deterministic-replay) - [Time-travel debug](/docs/reference/recipes/time-travel-debug) - [Replay-different-model](/docs/reference/recipes/replay-different-model) - [Prompt snapshots](/docs/reference/recipes/prompt-snapshots) - [Prompt diff](/docs/reference/recipes/prompt-diff) - [Evals in CI](/docs/reference/recipes/evals-ci) ## Stability - **Version:** `0.4.1` - **Tier:** alpha - **Contract:** evolving - **Roadmap:** see [packages roadmap](./roadmap) for what this package needs to reach v1.0. ## Related - [Evals (deep dive)](/docs/production/evals) - [Open Eval Format spec](/docs/reference/specs) - [For agents: eval](/docs/for-agents/eval) ## Source npm: [@agentskit/eval](https://www.npmjs.com/package/@agentskit/eval) · repo: [packages/eval](https://github.com/AgentsKit-io/agentskit/tree/main/packages/eval) --- # @agentskit/ink Source: https://www.agentskit.io/docs/reference/packages/ink > Terminal chat UI on Ink. Same useChat contract as the React binding. ## When to reach for it - You need a CLI / terminal chat. - You want to embed an agent inside a dev tool. ## Install ```bash npm install @agentskit/ink ink @agentskit/adapters ``` ## Hello world ```tsx import { render } from 'ink' import { ChatContainer } from '@agentskit/ink' import { anthropic } from '@agentskit/adapters' render() ``` ## Surface - `useChat(config): ChatReturn` — mirrors `@agentskit/react`. - ``, ``, ``, ``, ``. ## Siblings [React](/docs/reference/packages/react) · [Vue](/docs/reference/packages/vue) · [Svelte](/docs/reference/packages/svelte) · [Solid](/docs/reference/packages/solid) · [React Native](/docs/reference/packages/react-native) · [Angular](/docs/reference/packages/angular) ## Stability - **Version:** `0.7.4` - **Tier:** beta - **Contract:** evolving - **Roadmap:** see [packages roadmap](./roadmap) for what this package needs to reach v1.0. ## Related - [UI + hooks](/docs/ui) - [For agents: ink](/docs/for-agents/ink) - [CLI](/docs/production/cli) (ships `agentskit chat` powered by Ink) ## Source npm: [@agentskit/ink](https://www.npmjs.com/package/@agentskit/ink) · repo: [packages/ink](https://github.com/AgentsKit-io/agentskit/tree/main/packages/ink) --- # @agentskit/memory Source: https://www.agentskit.io/docs/reference/packages/memory > Chat memory + vector stores + hierarchical / encrypted / graph / personalization wrappers. ## When to reach for it - You need persistent chat history (file / SQLite / Redis). - You need vector search (pgvector / Pinecone / Qdrant / Chroma / Upstash / Redis / file). - You need MemGPT-style tiered memory, or client-side encryption, or a knowledge graph. ## Install ```bash npm install @agentskit/memory ``` ## Hello world ```ts import { pgvector, createHierarchicalMemory } from '@agentskit/memory' import { Pool } from 'pg' const pool = new Pool({ connectionString: process.env.DATABASE_URL }) const vectors = pgvector({ runner: { query: async (sql, params) => ({ rows: (await pool.query(sql, params)).rows }) }, }) ``` ## Surface - Chat: `fileChatMemory` · `sqliteChatMemory` · `redisChatMemory`. - Vector: `fileVectorMemory` · `redisVectorMemory` · `pgvector` · `pinecone` · `qdrant` · `chroma` · `upstashVector`. - HoF wrappers: `createHierarchicalMemory` · `createEncryptedMemory` · `createInMemoryGraph` · `createInMemoryPersonalization`. ## Recipes - [Persistent memory](/docs/reference/recipes/persistent-memory) - [Virtualized memory](/docs/reference/recipes/virtualized-memory) - [Hierarchical memory](/docs/reference/recipes/hierarchical-memory) - [Encrypted memory](/docs/reference/recipes/encrypted-memory) - [Vector adapters](/docs/reference/recipes/vector-adapters) - [Graph memory](/docs/reference/recipes/graph-memory) - [Personalization](/docs/reference/recipes/personalization) ## Stability - **Version:** `0.6.1` - **Tier:** beta - **Contract:** evolving - **Roadmap:** see [packages roadmap](./roadmap) for what this package needs to reach v1.0. ## Related - [Concepts: Memory](/docs/get-started/concepts/memory) - [Data → Memory](/docs/data/memory) - [For agents: memory](/docs/for-agents/memory) ## Source npm: [@agentskit/memory](https://www.npmjs.com/package/@agentskit/memory) · repo: [packages/memory](https://github.com/AgentsKit-io/agentskit/tree/main/packages/memory) --- # @agentskit/observability Source: https://www.agentskit.io/docs/reference/packages/observability > Trace viewer, signed audit log, cost guard, devtools, token counters. ## When to reach for it - You need console / LangSmith / OpenTelemetry logging. - You want an offline HTML trace viewer. - You want a hard dollar ceiling (`costGuard`). - You want a tamper-evident audit log (SOC 2 / HIPAA friendly). - You want a live devtools feed (browser extension compatible). ## Install ```bash npm install @agentskit/observability ``` ## Hello world ```ts import { consoleLogger, costGuard } from '@agentskit/observability' import { createRuntime } from '@agentskit/runtime' const runtime = createRuntime({ adapter, observers: [consoleLogger(), costGuard({ maxUsd: 0.5 })], }) ``` ## Surface - Loggers: `consoleLogger` · `langsmith` · `opentelemetry`. - Tracing: `createTraceTracker` · `createFileTraceSink` · `buildTraceReport` · `renderTraceViewerHtml`. - Cost + tokens: `costGuard` · `priceFor` · `computeCost` · `DEFAULT_PRICES` · `approximateCounter` · `countTokens` · `countTokensDetailed` · `createProviderCounter`. - Audit: `createSignedAuditLog` · `createInMemoryAuditStore`. - Devtools: `createDevtoolsServer` · `toSseFrame`. ## Recipes - [Cost-guarded chat](/docs/reference/recipes/cost-guarded-chat) - [Trace viewer](/docs/reference/recipes/trace-viewer) - [Devtools server](/docs/reference/recipes/devtools-server) - [Audit log](/docs/reference/recipes/audit-log) ## Stability - **Version:** `0.5.1` - **Tier:** beta - **Contract:** evolving - **Roadmap:** see [packages roadmap](./roadmap) for what this package needs to reach v1.0. ## Related - [Observability (deep dive)](/docs/production/observability) - [Security](/docs/production/security) - [For agents: observability](/docs/for-agents/observability) ## Source npm: [@agentskit/observability](https://www.npmjs.com/package/@agentskit/observability) · repo: [packages/observability](https://github.com/AgentsKit-io/agentskit/tree/main/packages/observability) --- # Packages overview Source: https://www.agentskit.io/docs/reference/packages/overview > Every AgentsKit package at a glance — what it does, when to reach for it, where to read the deep dive. import { ContributeCallout } from '@/components/contribute/contribute-callout' Twenty-one focused packages under `@agentskit/*`. Install what you need; every UI and runtime layer composes against the zero-dep **[@agentskit/core](./core)**. :::tip API reference Full signatures: **[TypeDoc HTML](pathname:///agentskit/api-reference/)** (generated on `pnpm --filter @agentskit/docs build`; for local dev run `pnpm --filter @agentskit/docs docs:api` once). ::: ## Foundation | Package | One-liner | |---|---| | [@agentskit/core](./core) | Contracts + chat controller + primitives. Under 10 KB gzipped, zero deps. | ## Model providers | Package | One-liner | |---|---| | [@agentskit/adapters](./adapters) | 20+ LLM chat + embedder adapters, plus router / ensemble / fallback. | ## UI bindings (same contract) | Package | One-liner | |---|---| | [@agentskit/react](./react) | `useChat` hook + headless components. | | [@agentskit/ink](./ink) | Terminal chat UI on Ink. | | [@agentskit/vue](./vue) | Vue 3 composable + `ChatContainer`. | | [@agentskit/svelte](./svelte) | Svelte 5 store. | | [@agentskit/solid](./solid) | Solid hook. | | [@agentskit/react-native](./react-native) | React Native / Expo hook. | | [@agentskit/angular](./angular) | Angular service (Signal + RxJS). | ## Agent runtime | Package | One-liner | |---|---| | [@agentskit/runtime](./runtime) | Standalone agent runtime + durable + topologies + speculate + background. | ## Capabilities | Package | One-liner | |---|---| | [@agentskit/tools](./tools) | Built-in tools + 20 integrations + MCP bridge. | | [@agentskit/memory](./memory) | Chat + vector + hierarchical + encrypted + graph memory. | | [@agentskit/rag](./rag) | Chunk + retrieve + rerank + hybrid + six loaders. | | [@agentskit/skills](./skills) | Ready-made personas + marketplace. | ## Observability + evaluation | Package | One-liner | |---|---| | [@agentskit/observability](./observability) | Trace viewer, audit log, cost guard, devtools. | | [@agentskit/eval](./eval) | Suites + replay + snapshots + diff + CI reporter. | ## Infrastructure | Package | One-liner | |---|---| | [@agentskit/sandbox](./sandbox) | Secure code execution + mandatory-sandbox policy. | | [@agentskit/cli](./cli) | `agentskit init / chat / run / ai / dev / doctor`. | | [@agentskit/templates](./templates) | Starter templates used by `agentskit init`. | ## See also - [Roadmap](./roadmap) — stability tier + path to v1.0 for every package. - [For agents](/docs/for-agents) — dense LLM-friendly reference per package. - [Concepts](/docs/get-started/concepts) — the six contracts every package builds on. - [Comparison](/docs/get-started/comparison) — AgentsKit vs. LangChain / Vercel AI / Mastra / LlamaIndex. Maintainers: **[documentation checklist](/docs/reference/contribute/package-docs)**. --- # @agentskit/rag Source: https://www.agentskit.io/docs/reference/packages/rag > Plug-and-play RAG. Chunk, embed, retrieve, rerank, hybrid — plus six document loaders. ## When to reach for it - You want retrieval-augmented generation in a few lines. - You need rerankers (BM25 / Cohere / BGE) or hybrid vector+keyword search. - You want document loaders for URL / GitHub / Notion / Confluence / Google Drive / PDF. ## Install ```bash npm install @agentskit/rag ``` ## Hello world ```ts import { createRAG } from '@agentskit/rag' import { fileVectorMemory } from '@agentskit/memory' import { openaiEmbedder } from '@agentskit/adapters' const rag = createRAG({ embed: openaiEmbedder({ apiKey: process.env.OPENAI_API_KEY! }), store: fileVectorMemory({ path: './kb.json' }), }) await rag.ingest([{ content: 'AgentsKit is a toolkit for AI agents.', source: 'intro' }]) const hits = await rag.search('what is agentskit') ``` ## Surface - `createRAG({ embed, store, chunkSize, chunkOverlap, topK, threshold })`. - `chunkText` — standalone splitter. - `createRerankedRetriever` · `createHybridRetriever` · `bm25Score` · `bm25Rerank`. - Loaders: `loadUrl` · `loadGitHubFile` · `loadGitHubTree` · `loadNotionPage` · `loadConfluencePage` · `loadGoogleDriveFile` · `loadPdf`. ## Recipes - [RAG chat](/docs/reference/recipes/rag-chat) - [PDF Q&A](/docs/reference/recipes/pdf-qa) - [RAG reranking](/docs/reference/recipes/rag-reranking) - [Doc loaders](/docs/reference/recipes/doc-loaders) ## Stability - **Version:** `0.2.1` - **Tier:** alpha - **Contract:** evolving - **Roadmap:** see [packages roadmap](./roadmap) for what this package needs to reach v1.0. ## Related - [Concepts: Retriever](/docs/get-started/concepts/retriever) - [Data → RAG](/docs/data/rag) - [For agents: rag](/docs/for-agents/rag) ## Source npm: [@agentskit/rag](https://www.npmjs.com/package/@agentskit/rag) · repo: [packages/rag](https://github.com/AgentsKit-io/agentskit/tree/main/packages/rag) --- # @agentskit/react Source: https://www.agentskit.io/docs/reference/packages/react > React hooks + headless chat components driving the shared ChatController. ## When to reach for it - You're building a web chat UI in React. - You want streaming + tool calls + memory + skills in one hook. - You want headless components with `data-ak-*` attributes so you style everything with your own CSS / Tailwind / shadcn. ## Install ```bash npm install @agentskit/react @agentskit/core @agentskit/adapters ``` ## Hello world ```tsx import { useChat } from '@agentskit/react' import { anthropic } from '@agentskit/adapters' export function Chat() { const chat = useChat({ adapter: anthropic({ apiKey, model: 'claude-sonnet-4-6' }) }) return (
{ e.preventDefault(); chat.send(chat.input) }}> {chat.messages.map(m =>
{m.content}
)} chat.setInput(e.target.value)} />
) } ``` ## Surface - `useChat(config): ChatReturn`. - ``, ``, ``, ``, ``, ``, ``, ``. - Theme: `@agentskit/react/theme`. ## Siblings (same contract) [Vue](/docs/reference/packages/vue) · [Svelte](/docs/reference/packages/svelte) · [Solid](/docs/reference/packages/solid) · [React Native](/docs/reference/packages/react-native) · [Angular](/docs/reference/packages/angular) · [Ink](/docs/reference/packages/ink) ## Recipes - [Cost-guarded chat](/docs/reference/recipes/cost-guarded-chat) - [Confirmation-gated tool](/docs/reference/recipes/confirmation-gated-tool) - [Edit and regenerate](/docs/reference/recipes/edit-and-regenerate) - [RAG chat](/docs/reference/recipes/rag-chat) ## Stability - **Version:** `0.5.8` - **Tier:** beta - **Contract:** evolving - **Roadmap:** see [packages roadmap](./roadmap) for what this package needs to reach v1.0. ## Related - [Concepts: Runtime](/docs/get-started/concepts/runtime) - [UI + hooks](/docs/ui) - [For agents: react](/docs/for-agents/react) ## Source npm: [@agentskit/react](https://www.npmjs.com/package/@agentskit/react) · repo: [packages/react](https://github.com/AgentsKit-io/agentskit/tree/main/packages/react) --- # @agentskit/react-native Source: https://www.agentskit.io/docs/reference/packages/react-native > React Native / Expo hook. Metro + Hermes safe. Same contract as @agentskit/react. ## Install ```bash npm install @agentskit/react-native react react-native @agentskit/adapters ``` ## Hello world ```tsx import { useChat } from '@agentskit/react-native' import { View, TextInput, FlatList, Pressable, Text } from 'react-native' export function ChatScreen({ adapter }) { const chat = useChat({ adapter }) return ( m.id} renderItem={({ item }) => {item.content}} /> chat.send(chat.input)}>Send ) } ``` ## Surface - `useChat(config): ChatReturn` — mirrors `@agentskit/react`, no DOM imports. ## Siblings [React](/docs/reference/packages/react) · [Vue](/docs/reference/packages/vue) · [Svelte](/docs/reference/packages/svelte) · [Solid](/docs/reference/packages/solid) · [Angular](/docs/reference/packages/angular) · [Ink](/docs/reference/packages/ink) ## Stability - **Version:** `0.2.0` - **Tier:** alpha - **Contract:** evolving - **Roadmap:** see [packages roadmap](./roadmap) for what this package needs to reach v1.0. ## Related - [UI + hooks](/docs/ui) - [For agents: react-native](/docs/for-agents/react-native) ## Source npm: [@agentskit/react-native](https://www.npmjs.com/package/@agentskit/react-native) · repo: [packages/react-native](https://github.com/AgentsKit-io/agentskit/tree/main/packages/react-native) --- # Roadmap Source: https://www.agentskit.io/docs/reference/packages/roadmap > Per-package stability status, current version, and what each package needs to reach v1.0. AgentsKit follows a **package-level semver** model. Each package declares its own stability tier and ships independently. The core is already v1; everything else is tracked below on its path to v1. ## Stability tiers | Tier | Meaning | Breaking changes | |---|---|---| | **stable** | v1.0 or later. Contract frozen per ADRs. | Only via major bump, with deprecation + codemod. | | **beta** | v0.x, surface settled, tests + docs complete. | Possible, but announced in [#beta-log](/docs/get-started/announcements) one release in advance. | | **alpha** | v0.x, API still evolving. | Expected. Follow the changelog. | ## Package status | Package | Version | Stability | Contract | Path to v1.0 | |---|---|---|---|---| | [`@agentskit/core`](./core) | 1.6.x | **stable** | frozen (ADRs 0001–0006) | Shipped. Contract frozen. | | [`@agentskit/adapters`](./adapters) | 0.9.x | **beta** | evolving | Finalize tool-call delta shape across providers; add Bedrock + Cohere + Vertex; adapter contract tests; publish 1.0 | | [`@agentskit/runtime`](./runtime) | 0.6.x | **beta** | evolving | Speculate API finalization; topology API finalization; durable step-log format frozen; publish 1.0 | | [`@agentskit/tools`](./tools) | 0.7.x | **beta** | evolving | Stabilize `composeTool` + `wrapToolWithSelfDebug` APIs; grow integrations to 35+; finalize MCP bridge shape; publish 1.0 | | [`@agentskit/memory`](./memory) | 0.6.x | **beta** | evolving | Stabilize `VectorStore` v1 (metadata filters); add Weaviate + Milvus + Turso + Supabase; publish 1.0 | | [`@agentskit/rag`](./rag) | 0.2.x | **alpha** | evolving | Finalize chunking strategies; reranker contract v1; add 3 more loaders; ship hybrid scorer defaults | | [`@agentskit/skills`](./skills) | 0.5.x | **beta** | evolving | `SkillDefinition` v1; marketplace resolver frozen; grow personas to 15; publish 1.0 | | [`@agentskit/observability`](./observability) | 0.5.x | **beta** | evolving | Trace event schema v1; audit-log format frozen; cost counter parity per adapter; publish 1.0 | | [`@agentskit/eval`](./eval) | 0.4.x | **alpha** | evolving | Suite format v1; replay cassette v1; CI reporter contract v1; snapshots stabilized | | [`@agentskit/sandbox`](./sandbox) | 0.3.x | **alpha** | evolving | E2B backend GA; WebContainer fallback feature parity; policy API v1 | | [`@agentskit/react`](./react) | 0.5.x | **beta** | evolving | `ChatReturn` shape frozen (shared across bindings); theme contract v1; a11y audit pass | | [`@agentskit/vue`](./vue) | 0.2.x | **alpha** | evolving | Parity with React surface; Vue 3 SSR tests; theme contract v1 | | [`@agentskit/svelte`](./svelte) | 0.2.x | **alpha** | evolving | Parity with React surface; Svelte 5 runes API; theme contract v1 | | [`@agentskit/solid`](./solid) | 0.2.x | **alpha** | evolving | Parity with React surface; Solid accessors; theme contract v1 | | [`@agentskit/react-native`](./react-native) | 0.2.x | **alpha** | evolving | Parity with React surface; Expo template; Android+iOS test matrix | | [`@agentskit/angular`](./angular) | 0.2.x | **alpha** | evolving | Parity with React surface; standalone-component template; Signal + RxJS contract v1 | | [`@agentskit/ink`](./ink) | 0.7.x | **beta** | evolving | Parity with React surface; keyboard UX pass; theme contract v1 | | [`@agentskit/cli`](./cli) | 0.8.x | **beta** | evolving | `agentskit init` template gallery; `agentskit ai` prompt hardening; `agentskit doctor` auto-fixes | | [`@agentskit/templates`](./templates) | 0.1.x | **alpha** | evolving | Grow to 8 framework starters; Stackblitz + CodeSandbox links per template | ## Release cadence - **core:** patches only unless an ADR ratifies a major change (rare, deprecation window). - **beta packages:** minor releases every 2–3 weeks; patches as needed. - **alpha packages:** ship when ready; may include breaking changes in minors. - **changesets:** every PR adds a changeset; CI blocks merges without one. ## How to follow - [Announcements](/docs/get-started/announcements) — narrative release notes. - [GitHub Releases](https://github.com/AgentsKit-io/agentskit/releases) — per-tag notes. - [Changesets](https://github.com/AgentsKit-io/agentskit/tree/main/.changeset) — in-flight bumps. - [Project board](https://github.com/orgs/AgentsKit-io/projects/1) — open issues grouped by phase. ## Want to help a package reach v1? Every package lists "path to v1.0" items above as GitHub issues with `type-*` + `package-*` labels. Pick one, open a PR, we ship together. Start with [good first issues](https://github.com/AgentsKit-io/agentskit/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22). ## Related - [Packages overview](./overview) · [Core](./core) - [Contribute](/docs/reference/contribute) · [RFC process](/docs/reference/contribute/rfc-process) --- # @agentskit/runtime Source: https://www.agentskit.io/docs/reference/packages/runtime > Standalone agent runtime. ReAct loop, durable execution, multi-agent topologies, speculative execution, background agents. ## When to reach for it - You want an agent without a UI. - You need durable execution (resume after a crash). - You want ready-made multi-agent topologies. - You want to race adapters (`speculate`) or run on cron / webhooks. ## Install ```bash npm install @agentskit/runtime @agentskit/adapters ``` ## Hello world ```ts import { createRuntime } from '@agentskit/runtime' import { anthropic } from '@agentskit/adapters' const runtime = createRuntime({ adapter: anthropic({ apiKey: process.env.ANTHROPIC_API_KEY!, model: 'claude-sonnet-4-6' }), }) const result = await runtime.run('Summarize the quarterly report.') console.log(result.content) ``` ## Surface - `createRuntime(config)` — headless agent with tools / memory / skills / observers. - `createSharedContext` — typed context across tools. - `createDurableRunner` + `createInMemoryStepLog` / `createFileStepLog` — Temporal-style step log. - `supervisor` · `swarm` · `hierarchical` · `blackboard` — multi-agent topologies. - `speculate` — race adapters, abort losers. - `createCronScheduler` + `createWebhookHandler` — background agents. ## Recipes - [Durable execution](/docs/reference/recipes/durable-execution) - [Multi-agent topologies](/docs/reference/recipes/multi-agent-topologies) - [Speculative execution](/docs/reference/recipes/speculative-execution) - [Background agents](/docs/reference/recipes/background-agents) - [Research team](/docs/reference/recipes/research-team) ## Stability - **Version:** `0.6.1` - **Tier:** beta - **Contract:** evolving - **Roadmap:** see [packages roadmap](./roadmap) for what this package needs to reach v1.0. ## Related - [Concepts: Runtime](/docs/get-started/concepts/runtime) - [Agents (deep dive)](/docs/agents) - [For agents: runtime](/docs/for-agents/runtime) ## Source npm: [@agentskit/runtime](https://www.npmjs.com/package/@agentskit/runtime) · repo: [packages/runtime](https://github.com/AgentsKit-io/agentskit/tree/main/packages/runtime) --- # @agentskit/sandbox Source: https://www.agentskit.io/docs/reference/packages/sandbox > Secure code execution (E2B / WebContainer) + mandatory-sandbox policy wrapper. ## When to reach for it - You want the model to run code safely. - You want a policy layer over every tool (allow / deny / require-sandbox / validators). ## Install ```bash npm install @agentskit/sandbox ``` ## Hello world ```ts import { sandboxTool, createMandatorySandbox } from '@agentskit/sandbox' import { shell, filesystem, webSearch } from '@agentskit/tools' const policy = createMandatorySandbox({ sandbox: sandboxTool(), policy: { requireSandbox: ['shell'], deny: ['filesystem'] }, }) const safeTools = [shell(), filesystem({ basePath }), webSearch()].map(t => policy.wrap(t)) ``` ## Surface - `createSandbox(config?)` — default backend probes E2B. - `sandboxTool()` — ready-made `code_execution` tool (js / python). - `createE2BBackend(config)` — BYO E2B. - `createMandatorySandbox({ sandbox, policy })`. ## Recipes - [Mandatory sandbox](/docs/reference/recipes/mandatory-sandbox) ## Stability - **Version:** `0.3.1` - **Tier:** alpha - **Contract:** evolving - **Roadmap:** see [packages roadmap](./roadmap) for what this package needs to reach v1.0. ## Related - [Security](/docs/production/security) - [For agents: sandbox](/docs/for-agents/sandbox) - [Tools](/docs/reference/packages/tools) — tools to wrap. ## Source npm: [@agentskit/sandbox](https://www.npmjs.com/package/@agentskit/sandbox) · repo: [packages/sandbox](https://github.com/AgentsKit-io/agentskit/tree/main/packages/sandbox) --- # @agentskit/skills Source: https://www.agentskit.io/docs/reference/packages/skills > Ready-made personas + composition + marketplace registry with semver. ## When to reach for it - You want a preset persona (researcher / coder / planner / critic / summarizer / code-reviewer / sql-gen / data-analyst / translator). - You want to publish + install skills by semver. ## Install ```bash npm install @agentskit/skills ``` ## Hello world ```ts import { researcher } from '@agentskit/skills' import { createRuntime } from '@agentskit/runtime' const runtime = createRuntime({ adapter, systemPrompt: researcher.systemPrompt }) ``` ## Surface - Ready-made: `researcher` · `coder` · `planner` · `critic` · `summarizer` · `codeReviewer` · `sqlGen` · `dataAnalyst` · `translator`. - Composition: `composeSkills` · `listSkills`. - Marketplace: `createSkillRegistry` · `parseSemver` · `compareSemver` · `matchesRange`. ## Recipes - [Skill marketplace](/docs/reference/recipes/skill-marketplace) - [Code reviewer](/docs/reference/recipes/code-reviewer) - [Research team](/docs/reference/recipes/research-team) ## Stability - **Version:** `0.5.1` - **Tier:** beta - **Contract:** evolving - **Roadmap:** see [packages roadmap](./roadmap) for what this package needs to reach v1.0. ## Related - [Concepts: Skill](/docs/get-started/concepts/skill) - [Skills (deep dive)](/docs/agents/skills) - [For agents: skills](/docs/for-agents/skills) ## Source npm: [@agentskit/skills](https://www.npmjs.com/package/@agentskit/skills) · repo: [packages/skills](https://github.com/AgentsKit-io/agentskit/tree/main/packages/skills) --- # @agentskit/solid Source: https://www.agentskit.io/docs/reference/packages/solid > Solid hook. Same contract as @agentskit/react. ## Install ```bash npm install @agentskit/solid solid-js @agentskit/adapters ``` ## Hello world ```tsx import { useChat } from '@agentskit/solid' import { anthropic } from '@agentskit/adapters' const chat = useChat({ adapter: anthropic({ apiKey, model: 'claude-sonnet-4-6' }) }) ``` ## Surface - `useChat(config): ChatReturn` — Solid hook backed by `createStore` + `onCleanup`. ## Siblings [React](/docs/reference/packages/react) · [Vue](/docs/reference/packages/vue) · [Svelte](/docs/reference/packages/svelte) · [React Native](/docs/reference/packages/react-native) · [Angular](/docs/reference/packages/angular) · [Ink](/docs/reference/packages/ink) ## Stability - **Version:** `0.2.0` - **Tier:** alpha - **Contract:** evolving - **Roadmap:** see [packages roadmap](./roadmap) for what this package needs to reach v1.0. ## Related - [UI + hooks](/docs/ui) - [For agents: solid](/docs/for-agents/solid) ## Source npm: [@agentskit/solid](https://www.npmjs.com/package/@agentskit/solid) · repo: [packages/solid](https://github.com/AgentsKit-io/agentskit/tree/main/packages/solid) --- # @agentskit/svelte Source: https://www.agentskit.io/docs/reference/packages/svelte > Svelte 5 chat store. Same contract as @agentskit/react. ## Install ```bash npm install @agentskit/svelte svelte @agentskit/adapters ``` ## Hello world ```svelte {#each $chat.messages as m (m.id)}

{m.content}

{/each}
chat.send($chat.input)}>
``` ## Surface - `createChatStore(config): SvelteChatStore` — `Readable` + action methods + `destroy()`. ## Siblings [React](/docs/reference/packages/react) · [Vue](/docs/reference/packages/vue) · [Solid](/docs/reference/packages/solid) · [React Native](/docs/reference/packages/react-native) · [Angular](/docs/reference/packages/angular) · [Ink](/docs/reference/packages/ink) ## Stability - **Version:** `0.2.0` - **Tier:** alpha - **Contract:** evolving - **Roadmap:** see [packages roadmap](./roadmap) for what this package needs to reach v1.0. ## Related - [UI + hooks](/docs/ui) - [For agents: svelte](/docs/for-agents/svelte) ## Source npm: [@agentskit/svelte](https://www.npmjs.com/package/@agentskit/svelte) · repo: [packages/svelte](https://github.com/AgentsKit-io/agentskit/tree/main/packages/svelte) --- # @agentskit/templates Source: https://www.agentskit.io/docs/reference/packages/templates > Authoring toolkit for generating AgentsKit extensions: validated ToolDefinition / SkillDefinition / AdapterFactory objects and on-disk scaffolds (package.json, tsup, tests, README). Depends on import { ContributeCallout } from '@/components/contribute/contribute-callout' Authoring toolkit for **generating** AgentsKit extensions: validated `ToolDefinition` / `SkillDefinition` / `AdapterFactory` objects and **on-disk scaffolds** (package.json, tsup, tests, README). Depends only on [`@agentskit/core`](./core). ## When to use - You publish **custom tools**, **skills**, or **adapters** as standalone packages. - You want **consistent blueprints** (tsup, vitest, TypeScript) across internal plugins. - You need **runtime validation** before registering a template with a runtime or marketplace. ## Install ```bash npm install @agentskit/templates @agentskit/core ``` ## Public API | Export | Role | |--------|------| | `createToolTemplate(config)` | Build a `ToolDefinition` with defaults and validation | | `createSkillTemplate(config)` | Build a `SkillDefinition` with defaults and validation | | `createAdapterTemplate(config)` | Build an `AdapterFactory` + display `name` | | `scaffold(config)` | Write a full package directory (async) | | `validateToolTemplate` / `validateSkillTemplate` / `validateAdapterTemplate` | Assert well-formed definitions | ### `createToolTemplate` `ToolTemplateConfig` extends a partial tool with required `name` and optional `description`, `schema`, `execute`, `tags`, `category`, `requiresConfirmation`, `init`, `dispose`, and `base` merge. ```ts import { createToolTemplate } from '@agentskit/templates' export const rollDice = createToolTemplate({ name: 'roll_dice', description: 'Roll an N-sided die once.', schema: { type: 'object', properties: { sides: { type: 'number', minimum: 2 } }, required: ['sides'], }, async execute(args) { const sides = Number(args.sides) return String(1 + Math.floor(Math.random() * sides)) }, }) ``` Validation **throws** if `name`, `description`, `schema`, or `execute` is missing (LLMs need schema + description for tool calling). ### `createSkillTemplate` Requires `name`, `description`, and `systemPrompt`. Optional: `examples`, `tools`, `delegates`, `temperature`, `onActivate`, `base`. ```ts import { createSkillTemplate } from '@agentskit/templates' export const researcher = createSkillTemplate({ name: 'researcher', description: 'Gather facts before writing.', systemPrompt: 'You are a careful researcher. Cite sources when possible.', tools: ['web_search'], }) ``` ### `createAdapterTemplate` Requires `name` and `createSource` matching `AdapterFactory['createSource']`. ```ts import { createAdapterTemplate } from '@agentskit/templates' export const myAdapter = createAdapterTemplate({ name: 'my-llm', createSource: (request) => { // return StreamSource compatible with @agentskit/core throw new Error('Implement streaming to your backend') }, }) ``` ### `scaffold` Creates a directory `join(dir, name)` with: - `package.json`, `tsconfig.json`, `tsup.config.ts` - `src/index.ts` (stub implementation) - `tests/index.test.ts` - `README.md` ```ts import { scaffold } from '@agentskit/templates' import { join } from 'node:path' const files = await scaffold({ type: 'tool', name: 'my-company-search', dir: join(process.cwd(), 'packages'), description: 'Internal web search tool', }) console.log('Created:', files) ``` `ScaffoldType` is `'tool' | 'skill' | 'adapter'`. Register scaffolded packages like any other tool, skill, or adapter via [`createRuntime`](../agents/runtime) or [`useChat`](../hooks/use-chat). ## Troubleshooting | Error | Cause | |-------|--------| | `Tool requires a name` | Pass `name` to `createToolTemplate`. | | `requires a schema` | JSON Schema required for function calling. | | `Skill ... requires a systemPrompt` | Skills must define behavior via `systemPrompt`. | | `Adapter requires createSource` | Factory must expose `createSource(request)`. | ## See also [Start here](../getting-started/read-this-first) · [Packages](./overview) · [TypeDoc](pathname:///agentskit/api-reference/) (`@agentskit/templates`) · [@agentskit/core](./core) · [Tools](../agents/tools) · [Skills](../agents/skills) · [Adapters](../data-layer/adapters) ## Stability - **Version:** `0.1.10` - **Tier:** alpha - **Contract:** evolving - **Roadmap:** see [packages roadmap](./roadmap) for what this package needs to reach v1.0. --- # @agentskit/tools Source: https://www.agentskit.io/docs/reference/packages/tools > Built-in tools + 20 third-party integrations + bidirectional MCP bridge. ## When to reach for it - You need ready-made tools (web search, file I/O, shell). - You want GitHub / Linear / Slack / Notion / Stripe / Postgres / S3 / … - You want to speak MCP (consume or publish). ## Install ```bash npm install @agentskit/tools ``` ## Hello world ```ts import { webSearch, fetchUrl } from '@agentskit/tools' import { github } from '@agentskit/tools/integrations' import { createRuntime } from '@agentskit/runtime' const tools = [ webSearch(), fetchUrl(), ...github({ token: process.env.GITHUB_TOKEN! }), ] const runtime = createRuntime({ adapter, tools }) ``` ## Surface - Main: `webSearch` · `fetchUrl` · `filesystem` · `shell` · `defineZodTool`. - `/integrations`: `github` · `linear` · `slack` · `notion` · `discord` · `gmail` · `googleCalendar` · `stripe` · `postgres` · `s3` · `firecrawl` · `reader` · `documentParsers` · `openaiImages` · `elevenlabs` · `whisper` · `deepgram` · `maps` · `weather` · `coingecko` · `browserAgent`. - `/mcp`: `createMcpClient` · `createMcpServer` · `toolsFromMcpClient` · `createStdioTransport` · `createInMemoryTransportPair`. ## Recipes - [Integrations](/docs/reference/recipes/integrations) - [More integrations (scraping / voice / maps / browser)](/docs/reference/recipes/more-integrations) - [MCP bridge](/docs/reference/recipes/mcp-bridge) - [Tool composer](/docs/reference/recipes/tool-composer) - [Confirmation-gated tool](/docs/reference/recipes/confirmation-gated-tool) - [Mandatory sandbox](/docs/reference/recipes/mandatory-sandbox) ## Stability - **Version:** `0.7.1` - **Tier:** beta - **Contract:** evolving - **Roadmap:** see [packages roadmap](./roadmap) for what this package needs to reach v1.0. ## Related - [Concepts: Tool](/docs/get-started/concepts/tool) - [Tools (deep dive)](/docs/agents/tools) - [For agents: tools](/docs/for-agents/tools) ## Source npm: [@agentskit/tools](https://www.npmjs.com/package/@agentskit/tools) · repo: [packages/tools](https://github.com/AgentsKit-io/agentskit/tree/main/packages/tools) --- # @agentskit/vue Source: https://www.agentskit.io/docs/reference/packages/vue > Vue 3 composable + ChatContainer component. Same contract as @agentskit/react. ## Install ```bash npm install @agentskit/vue vue @agentskit/adapters ``` ## Hello world ```ts import { useChat } from '@agentskit/vue' import { anthropic } from '@agentskit/adapters' const chat = useChat({ adapter: anthropic({ apiKey, model: 'claude-sonnet-4-6' }) }) // chat.messages + chat.input are reactive // chat.send / chat.setInput / chat.stop / chat.retry / chat.clear ``` ## Surface - `useChat(config): ChatReturn` — reactive via Vue's `reactive()` + auto-cleanup on scope dispose. - `` — headless container using `data-ak-*` attributes. ## Siblings [React](/docs/reference/packages/react) · [Svelte](/docs/reference/packages/svelte) · [Solid](/docs/reference/packages/solid) · [React Native](/docs/reference/packages/react-native) · [Angular](/docs/reference/packages/angular) · [Ink](/docs/reference/packages/ink) ## Stability - **Version:** `0.2.0` - **Tier:** alpha - **Contract:** evolving - **Roadmap:** see [packages roadmap](./roadmap) for what this package needs to reach v1.0. ## Related - [UI + hooks](/docs/ui) - [For agents: vue](/docs/for-agents/vue) ## Source npm: [@agentskit/vue](https://www.npmjs.com/package/@agentskit/vue) · repo: [packages/vue](https://github.com/AgentsKit-io/agentskit/tree/main/packages/vue) --- # Recipes Source: https://www.agentskit.io/docs/reference/recipes > Copy-paste solutions grouped by theme. Every recipe end-to-end, runs as written. 60+ recipes. Each one: single page, install line, code, run. No dependency between recipes — every page stands alone. ## Getting started - [Custom adapter](./custom-adapter) — wrap any LLM API - [More providers](./more-providers) — OpenAI, Anthropic, Gemini, local - [Simulate stream](./simulate-stream) — fake streaming for one-shot providers - [Framework adapters](./framework-adapters) — React / Vue / Svelte / Solid / Ink / RN / Angular ## Chat UI - [RAG chat](./rag-chat) · [PDF Q&A](./pdf-qa) - [Cost-guarded chat](./cost-guarded-chat) · [Persistent memory](./persistent-memory) - [Edit + regenerate](./edit-and-regenerate) · [Progressive tool calls](./progressive-tool-calls) - [Multi-modal](./multi-modal) · [Generative UI](./generative-ui) ## RAG + retrieval - [RAG chat](./rag-chat) · [PDF Q&A](./pdf-qa) - [RAG reranking](./rag-reranking) · [Doc loaders](./doc-loaders) - [Vector adapters](./vector-adapters) ## Memory - [Persistent memory](./persistent-memory) · [Hierarchical memory](./hierarchical-memory) - [Auto-summarize](./auto-summarize) · [Virtualized memory](./virtualized-memory) - [Graph memory](./graph-memory) · [Personalization](./personalization) - [Encrypted memory](./encrypted-memory) ## Adapter composition - [Adapter router](./adapter-router) · [Ensemble](./adapter-ensemble) - [Fallback chain](./fallback-chain) ## Tools + integrations - [Integrations](./integrations) · [More integrations](./more-integrations) - [MCP bridge](./mcp-bridge) · [Tool composer](./tool-composer) - [Self-debug](./self-debug) ## Multi-agent + orchestration - [Multi-agent topologies](./multi-agent-topologies) · [Research team](./research-team) - [Durable execution](./durable-execution) · [Background agents](./background-agents) - [Speculative execution](./speculative-execution) ## Observability + cost - [Cost guard](./cost-guard) · [Token budget](./token-budget) - [Trace viewer](./trace-viewer) · [Devtools server](./devtools-server) - [Audit log](./audit-log) ## Security - [PII redaction](./pii-redaction) · [Prompt injection](./prompt-injection) - [Rate limiting](./rate-limiting) · [Mandatory sandbox](./mandatory-sandbox) - [HITL approvals](./hitl-approvals) · [Confirmation-gated tool](./confirmation-gated-tool) ## Evaluation - [Eval suite](./eval-suite) · [Evals CI](./evals-ci) - [Deterministic replay](./deterministic-replay) · [Replay different model](./replay-different-model) - [Prompt snapshots](./prompt-snapshots) · [Prompt diff](./prompt-diff) - [Prompt experiments](./prompt-experiments) · [Time-travel debug](./time-travel-debug) ## Apps + bots - [Code reviewer](./code-reviewer) · [Discord bot](./discord-bot) - [Schema-first agent](./schema-first-agent) · [agentskit ai](./agentskit-ai) ## Skills + specs - [Skill marketplace](./skill-marketplace) · [Open specs](./open-specs) ## Conventions - Replace `KEY` with your actual API key (use `process.env.X_API_KEY` in real code). - TypeScript; remove types for plain JS. - Each recipe declares its `npm install` line at the top. --- # Adapter ensemble Source: https://www.agentskit.io/docs/reference/recipes/adapter-ensemble > Send one request to N models in parallel, aggregate the answers into a single output. Ensembles are the "wisdom of crowds" move for LLMs: send the same request to several models, then combine their answers. Great for reducing variance on high-stakes outputs (classification labels, structured extraction, critical summaries). ## Install Comes with `@agentskit/adapters`. ## Quick start — majority vote ```ts import { createEnsembleAdapter, anthropic, openai } from '@agentskit/adapters' const ensemble = createEnsembleAdapter({ candidates: [ { id: 'haiku', adapter: anthropic({ model: 'claude-haiku-4-5' }) }, { id: 'sonnet', adapter: anthropic({ model: 'claude-sonnet-4-6' }) }, { id: 'gpt-mini', adapter: openai({ model: 'gpt-4o-mini' }) }, ], // aggregate: 'majority-vote' is the default }) ``` The resulting `AdapterFactory` emits a single `text` chunk carrying the aggregated answer followed by `done`, so it plugs into any runtime that expects a streaming adapter. ## Aggregators | `aggregate` | Behavior | |-------------|----------| | `'majority-vote'` *(default)* | Weighted vote — picks the text with the highest total `weight` (default 1 per candidate) | | `'concat'` | Joins all branches with `\n---\n` — useful for review workflows | | `'longest'` | Picks the branch with the most bytes of text | | `function` | Custom: `(branches) => string`, sync or async | ```ts createEnsembleAdapter({ aggregate: branches => { // Prefer a branch whose output parses as JSON. const parsed = branches.find(b => { try { JSON.parse(b.text); return true } catch { return false } }) return parsed?.text ?? branches[0].text }, candidates: [...], }) ``` ## Weighted vote Give higher-quality models a stronger say. ```ts createEnsembleAdapter({ candidates: [ { id: 'cheap', adapter: haikuFactory, weight: 1 }, { id: 'premium', adapter: sonnetFactory, weight: 3 }, ], }) ``` ## Timeouts and resilience - `timeoutMs` bounds every branch. A branch that times out is marked with an error and skipped. - If **every** branch fails, the adapter throws `all ensemble branches failed` — callers can retry or fall back. - `onBranches` fires once per request with every branch's raw output, so you can log, cost-meter, or audit. ## See also - [Speculative execution](/docs/reference/recipes/speculative-execution) — pick a winner instead of aggregating - [Adapter router](/docs/reference/recipes/adapter-router) — pick one candidate based on policy - [Fallback chain](/docs/reference/recipes/fallback-chain) --- # Adapter router Source: https://www.agentskit.io/docs/reference/recipes/adapter-router > Auto-pick an adapter per request based on cost, latency, capabilities, or a custom classifier. You don't want every call to hit your most expensive model. You also don't want to split the agent in two just to use Haiku for easy questions and Sonnet for hard ones. `createRouter` from `@agentskit/adapters` builds a single `AdapterFactory` that picks among N candidates on every `createSource()`. ## Install Comes with `@agentskit/adapters`. ## Pick the cheapest capable candidate ```ts import { createRouter, anthropic, openai } from '@agentskit/adapters' const router = createRouter({ candidates: [ { id: 'haiku', adapter: anthropic({ model: 'claude-haiku-4-5' }), cost: 0.25 }, { id: 'sonnet', adapter: anthropic({ model: 'claude-sonnet-4-6' }), cost: 3, capabilities: { tools: true } }, { id: 'gpt-mini', adapter: openai({ model: 'gpt-4o-mini' }), cost: 0.15 }, ], // policy: 'cheapest' is the default }) ``` Candidates are filtered against the request's requirements first (e.g. requests with tools filter out `capabilities: { tools: false }`) and then ranked by the `policy`. ## Policies | Policy | Behavior | |--------|----------| | `'cheapest'` *(default)* | Minimum `cost` | | `'fastest'` | Minimum `latencyMs` | | `'capability-match'` | First candidate that satisfies requirements | | `(input) => id` | Custom function, sync or async | ## Classify-then-route Skip the policy entirely when the classifier can pick a specific candidate, or narrow the pool by tags. ```ts const router = createRouter({ classify: request => { const text = request.messages[request.messages.length - 1]?.content ?? '' if (/code|typescript|refactor/i.test(text)) return ['coding'] if (/image|photo|picture/i.test(text)) return 'sonnet' // id wins return undefined }, candidates: [ { id: 'haiku', adapter: anthropic({ model: 'claude-haiku-4-5' }), cost: 0.25, tags: ['fast'] }, { id: 'sonnet', adapter: anthropic({ model: 'claude-sonnet-4-6' }), cost: 3 }, { id: 'coder', adapter: openai({ model: 'gpt-5-codex' }), cost: 2, tags: ['coding'] }, ], }) ``` Resolution order per request: 1. `classify(request)` returns a string → use that candidate id (if present). 2. `classify(request)` returns tags → filter candidates to those with all tags, then apply `policy`. 3. Fall back to `policy` across all capability-matched candidates. ## Observe decisions ```ts createRouter({ onRoute: ({ id, reason, request }) => { console.log(`[router] -> ${id} (${reason})`) }, candidates: [...], }) ``` ## See also - [Speculative execution](/docs/reference/recipes/speculative-execution) — run several candidates in parallel - [Custom adapter](/docs/reference/recipes/custom-adapter) --- # agentskit ai — natural-language agent generator Source: https://www.agentskit.io/docs/reference/recipes/agentskit-ai > Describe an agent in plain English, get a typed AgentSchema + tool stubs + runtime wiring. Writing the first cut of an agent from scratch is tedious busy-work. `npx agentskit ai ""` drives any provider adapter with a planner prompt, validates the returned schema, and scaffolds a fresh project: `agent.json`, `agent.ts`, one tool stub per tool, and a `README.md`. ## Install Comes with `@agentskit/cli`. ## Quick start ```bash npx agentskit ai "A bot that summarizes long PDFs and drafts a Slack message with the 3 key findings." \ --provider anthropic --model claude-sonnet-4-6 \ --out ./pdf-summarizer ``` Output: ``` Planning agent for: "A bot that summarizes..." Wrote 5 file(s) to ./pdf-summarizer + agent.json + agent.ts + README.md + tools/extract_pdf.ts + tools/post_slack.ts ``` Flags: | Flag | Default | Purpose | |------|---------|---------| | `--provider` | `anthropic` | Planner provider | | `--model` | provider default | Planner model id | | `--api-key` | env | Explicit key override | | `--base-url` | — | For OpenAI-compatible endpoints | | `--out` | `./my-agent` | Output directory | | `--overwrite` | `false` | Overwrite existing files | | `--dry-run` | `false` | Print plan + file list without writing | ## What you get `agent.json` — the validated [`AgentSchema`](/docs/reference/recipes/schema-first-agent). `agent.ts` — a typed `createAgent(adapter)` factory that wires tools. `tools/.ts` — one `defineTool(...)` stub per declared tool, with the planner's implementation hint in the docstring. `README.md` — human-readable summary of the agent. ## Programmatic use The CLI is a thin wrapper around two exported helpers: ```ts import { scaffoldAgent, writeScaffold, createAdapterPlanner } from '@agentskit/cli/ai' import { anthropic } from '@agentskit/adapters' const planner = createAdapterPlanner(anthropic({ apiKey: process.env.ANTHROPIC_API_KEY!, model: 'claude-sonnet-4-6' })) const schema = await planner('A bot that reviews pull requests.') const files = scaffoldAgent(schema) await writeScaffold(files, './pr-reviewer') ``` ## See also - [Schema-first agents](/docs/reference/recipes/schema-first-agent) - [Custom adapter](/docs/reference/recipes/custom-adapter) --- # Signed audit log Source: https://www.agentskit.io/docs/reference/recipes/audit-log > Hash-chained, HMAC-signed audit log for SOC 2 / HIPAA friendly evidence — tamper-evident and authenticated. `createSignedAuditLog` is the smallest audit log that does the two things auditors actually ask for: **tamper-evident** (every entry references the previous entry's hash, so splicing or reordering is detectable) and **authenticated** (every entry's body is signed with an HMAC secret, so content edits without the secret are detectable). ## Install Ships with `@agentskit/observability`. ```ts import { createSignedAuditLog, createInMemoryAuditStore, } from '@agentskit/observability' ``` ## Record decisions ```ts const log = createSignedAuditLog({ secret: process.env.AUDIT_SECRET!, store: myPostgresAuditStore(), }) await log.append({ actor: currentUser.id, action: 'delete-record', payload: { table: 'invoices', id: 42 }, }) ``` `append` fills in `seq` (monotonic), `prevHash` (chains to the last entry), and `signature` (HMAC over the canonical body). ## Verify ```ts const result = await log.verify() if (!result.ok) { alert(`audit log broken at seq ${result.brokenAt!.seq} (${result.brokenAt!.reason})`) } ``` `verify()` walks the entire chain and re-computes `prevHash` + `signature` for each entry. Detects two failure modes: - **`prev-hash`** — an entry was inserted, removed, or reordered. - **`signature`** — an entry was edited without the HMAC secret. ## Stores - `createInMemoryAuditStore()` — tests, transient services. - Bring your own with the 4-method contract (`append`, `list`, `last`, optional `clear`) — Postgres, S3-with-object-lock, Timescale, WORM storage, append-only Kafka topic, etc. ## Rotating secrets A secret rotation ends the old chain and starts a new one — the old secret can still verify historical entries, the new secret signs new ones. Keep both live during an overlap window, then retire the old. ## See also - [Prompt injection detector](/docs/reference/recipes/prompt-injection) - [Rate limiting](/docs/reference/recipes/rate-limiting) - [Trace viewer](/docs/reference/recipes/trace-viewer) --- # Auto-summarization Source: https://www.agentskit.io/docs/reference/recipes/auto-summarize > Wrap any ChatMemory so it compacts oldest messages into a summary whenever stored tokens exceed a budget. `compileBudget` trims per-request. `createAutoSummarizingMemory` trims *at rest*: once a session grows past `maxTokens`, the oldest (non-summary) messages get folded into a single summary message via your summarizer, then persisted. Idempotent — summaries are tagged and never re-summarized. ## Install Ships in `@agentskit/core` under the subpath: ```ts import { createAutoSummarizingMemory } from '@agentskit/core/auto-summarize' ``` ## Wire it up ```ts import { createAutoSummarizingMemory } from '@agentskit/core/auto-summarize' import { createInMemoryMemory } from '@agentskit/core' import { createRuntime } from '@agentskit/runtime' import { anthropic } from '@agentskit/adapters' const summarizerAdapter = anthropic({ apiKey: process.env.ANTHROPIC_API_KEY!, model: 'claude-haiku-4-5' }) const memory = createAutoSummarizingMemory(createInMemoryMemory(), { maxTokens: 8_000, keepRecent: 6, summarizer: async messages => { const src = messages.map(m => `${m.role}: ${m.content}`).join('\n') const result = await runOnce(summarizerAdapter, `Summarize the following chat transcript in 3 bullet points:\n\n${src}`) return { id: crypto.randomUUID(), role: 'system', content: result, status: 'complete', createdAt: new Date(), } }, onCompact: info => { console.log(`compacted ${info.droppedCount} msgs: ${info.beforeTokens} → ${info.afterTokens} tokens`) }, }) const runtime = createRuntime({ adapter: mainAdapter, memory }) ``` ## Options | Option | Default | Purpose | |--------|---------|---------| | `maxTokens` | *(required)* | Budget trigger | | `keepRecent` | `4` | Messages always kept verbatim at the tail | | `counter` | `approximateCounter` | Swap for tiktoken for real counts | | `summarizer` | *(required)* | `(messages) => Message` — your compaction prompt | | `onCompact` | — | Observability hook | ## Summary message shape Every summary emitted by `summarizer` gets `metadata.agentskitSummary = true` attached automatically. That tag is what prevents re-summarization — subsequent compactions leave existing summaries alone. ## See also - [Token budget compiler](/docs/reference/recipes/token-budget) — per-request trimming - [Virtualized memory](/docs/reference/recipes/virtualized-memory) — cap *count*, not tokens - [Hierarchical memory](/docs/reference/recipes/hierarchical-memory) — tiered long-term storage --- # Background agents (cron + webhooks) Source: https://www.agentskit.io/docs/reference/recipes/background-agents > Run agents on a schedule or in response to incoming webhooks, without pulling in a job-queue dependency. Two primitives, zero extra deps: - `createCronScheduler` parses a minimal 5-field cron (`*/15 * * * *`) plus an `every:` shortcut. - `createWebhookHandler` returns a framework-agnostic `(req) => res` handler you can mount on Express, Hono, Next API routes, etc. Both accept any `AgentHandle` (anything with `name` + `run(task)`), so they compose with [topologies](/docs/reference/recipes/multi-agent-topologies) and [durable runners](/docs/reference/recipes/durable-execution). ## Install Ships with `@agentskit/runtime`. ## Cron ```ts import { createCronScheduler } from '@agentskit/runtime' const scheduler = createCronScheduler({ jobs: [ { schedule: '0 9 * * 1-5', // weekdays at 9am agent: dailyDigestAgent, task: now => `Generate digest for ${now.toISOString().slice(0, 10)}`, }, { schedule: 'every:60000', // every minute agent: healthCheckAgent, task: 'run health check', }, ], onEvent: e => logger.info('[cron]', e), }) scheduler.start() // ... scheduler.stop() ``` For tests: inject a fake clock and drive ticks manually. ```ts createCronScheduler({ jobs: [...], now: () => fakeClock, scheduleTick: fn => (cleanup = setInterval(fn, 100)), }) ``` `scheduler.tick(now?)` fires every job whose schedule matches `now`. ## Webhooks ```ts import { createWebhookHandler } from '@agentskit/runtime' const handler = createWebhookHandler({ agent: supportTriageAgent, verify: req => req.headers?.['x-signature'] === expectedSignature, extractTask: req => `Triage ticket: ${JSON.stringify(req.body)}`, context: req => ({ tenantId: req.headers?.['x-tenant'] }), }) // Express app.post('/hooks/support', async (req, res) => { const result = await handler({ headers: req.headers, body: req.body }) res.status(result.status).set(result.headers ?? {}).send(result.body) }) // Hono app.post('/hooks/support', async c => { const result = await handler({ headers: Object.fromEntries(c.req.raw.headers), body: await c.req.json() }) return new Response(result.body, { status: result.status, headers: result.headers }) }) ``` Default extractor reads `body.task` for JSON, falls back to the raw string. Default context is pass-through. ## Pair with durable + HITL - Wrap the agent's work in a `createDurableRunner` to survive crashes. - Pause risky side effects behind `createApprovalGate`. A webhook that kicks off a long-running durable flow looks like: ```ts const handler = createWebhookHandler({ agent: { name: 'onboarding', async run(task, ctx) { const runner = createDurableRunner({ store, runId: ctx.tenantId + ':' + ctx.userId }) await runner.step('welcome', () => sendWelcome(...)) await runner.step('provision', () => provision(...)) return 'ok' }, }, }) ``` ## See also - [Durable execution](/docs/reference/recipes/durable-execution) - [HITL approvals](/docs/reference/recipes/hitl-approvals) - [Multi-agent topologies](/docs/reference/recipes/multi-agent-topologies) --- # Code reviewer agent Source: https://www.agentskit.io/docs/reference/recipes/code-reviewer > An agent that reads a git diff, runs the test suite, and posts a structured review. A CLI tool that reviews local changes (diff vs `main`) and prints a structured review. Optionally posts to GitHub. ## Install ```bash npm install @agentskit/runtime @agentskit/adapters @agentskit/tools @agentskit/skills ``` ## The script ```ts title="review.ts" import { createRuntime } from '@agentskit/runtime' import { anthropic } from '@agentskit/adapters' import { shell, filesystem } from '@agentskit/tools' import type { SkillDefinition } from '@agentskit/core' const reviewer: SkillDefinition = { name: 'code_reviewer', description: 'Reviews local diffs against main, focusing on bugs, missing tests, and style.', systemPrompt: `You are a senior TypeScript engineer reviewing a pull request. Workflow: 1. Run \`git diff main\` and read the full diff 2. Identify changed files; read them in full when context matters 3. Run the test suite (\`pnpm test\`) and inspect failures 4. Produce a structured review with severity per comment Output format (markdown): ## Verdict APPROVE | REQUEST_CHANGES | COMMENT ## Comments - **[severity]** file:line — Issue. Suggestion. Severities: high (must fix), medium (should fix), low (nice to have). Always: - Flag missing tests as severity:high - Flag any 'any' usage with a concrete refactor - Verify the test runner output before approving - Keep comments terse — quote-then-suggest`, tools: ['shell', 'filesystem_read'], temperature: 0.2, } const runtime = createRuntime({ adapter: anthropic({ apiKey: KEY, model: 'claude-sonnet-4-6' }), tools: [shell({ allowed: ['git', 'pnpm', 'cat'] }), ...filesystem({ basePath: '.' })], maxSteps: 20, }) const result = await runtime.run('Review the current diff and produce a structured review.', { skill: reviewer, }) console.log(result.content) console.log(`\n— ${result.steps} steps, ${result.toolCalls.length} tool calls`) ``` ## Run it ```bash git checkout my-branch npx tsx review.ts ``` ## Dogfood it on AgentsKit ```bash git clone https://github.com/AgentsKit-io/agentskit cd agentskit git checkout some-pr-branch npx tsx review.ts ``` The agent reads the actual diff, runs `pnpm test`, and produces a real review. ## Tighten the recipe - **Post to GitHub**: pipe the output to `gh pr comment $PR_NUMBER -F -` - **Tighter scope**: only review changes in a specific package via `--filter` - **CI integration**: run on every PR, fail the build on `REQUEST_CHANGES` - **Multiple reviewers**: delegate to specialist skills (security, performance, accessibility) ## Why a skill, not a tool The reviewer is a **persona** the model adopts (workflow, output format, severity rules), not a function the model calls. That makes it a Skill. The actual capabilities (`git diff`, `pnpm test`, file reads) are Tools. See [Concepts: Skill](../concepts/skill). ## Related - [Recipe: Multi-agent research team](./research-team) — same pattern with delegation - [Concepts: Skill](../concepts/skill) --- # Confirmation-gated tool Source: https://www.agentskit.io/docs/reference/recipes/confirmation-gated-tool > A dangerous tool the runtime refuses to execute without explicit human approval. A tool that deletes files. The agent can call it. The runtime pauses for human approval before anything happens. No timeout-based auto-approval — security-critical by design. ## Install ```bash npm install @agentskit/runtime @agentskit/adapters ``` ## The tool ```ts title="delete-file-tool.ts" import type { ToolDefinition } from '@agentskit/core' import { unlink } from 'node:fs/promises' export const deleteFile: ToolDefinition = { name: 'delete_file', description: 'Permanently delete a file.', schema: { type: 'object', properties: { path: { type: 'string', description: 'Absolute or relative path.' }, }, required: ['path'], }, requiresConfirmation: true, // ← the gate async execute(args) { await unlink(args.path as string) return { ok: true } }, } ``` ## The runtime with `onConfirm` ```ts title="agent.ts" import { createRuntime } from '@agentskit/runtime' import { anthropic } from '@agentskit/adapters' import { deleteFile } from './delete-file-tool' import { createInterface } from 'node:readline/promises' const rl = createInterface({ input: process.stdin, output: process.stdout }) const runtime = createRuntime({ adapter: anthropic({ apiKey: KEY, model: 'claude-sonnet-4-6' }), tools: [deleteFile], onConfirm: async (call) => { const args = JSON.stringify(call.args) const answer = await rl.question( `\n⚠ Approve "${call.name}(${args})"? [y/N] `, ) return answer.trim().toLowerCase() === 'y' }, }) const result = await runtime.run('Delete the file ./scratch.txt') console.log(result.content) rl.close() ``` ## Run it ```bash npx tsx agent.ts # ⚠ Approve "delete_file({"path":"./scratch.txt"})"? [y/N] y # Done. ./scratch.txt has been deleted. ``` If you answer `n`, the runtime feeds a refusal back to the model as a tool error, and the agent decides what to do next (typically: explain why it stopped). ## What's enforced by the contract | Behavior | Where it's defined | |---|---| | `requiresConfirmation: true` exists | Tool contract T9 | | Runtime MUST call `onConfirm` first | Runtime contract RT6 | | If `onConfirm` is absent, execution is REFUSED (not allowed) | Runtime contract RT6 | | No timeout-based auto-approval | Tool T9 + Runtime RT6 (non-negotiable) | This means a tool author can mark a tool dangerous and **trust** the runtime to gate it. No "but what if the user forgets to wire `onConfirm`?" — the runtime refuses to execute, period. ## Tighten the recipe - **Web UI** instead of stdin — `onConfirm` returns a Promise that resolves when the user clicks - **Slack / Discord approval** — post a message with ✓/✗ buttons; resolve on click - **Per-tool policies** — a wrapper that auto-approves `read` operations, requires approval for `write` operations, and always refuses `delete` - **Audit log** — wrap `onConfirm` to log every approval/refusal with the args ## Related - [Concepts: Tool](../concepts/tool) - [Concepts: Runtime](../concepts/runtime) — RT6 confirmation --- # Cost guard Source: https://www.agentskit.io/docs/reference/recipes/cost-guard > Enforce a dollar budget per run. Tokens → cost → abort, all via contract primitives. Stop runaway agent loops from blowing a budget. `@agentskit/observability` ships a `costGuard` observer that tracks token usage from every `llm:end` event, computes cost via a pricing table, and aborts the run when the budget is exceeded. ## Install ```bash npm install @agentskit/runtime @agentskit/adapters @agentskit/observability ``` ## The guarded run ```ts title="cost-guarded.ts" import { createRuntime } from '@agentskit/runtime' import { openai } from '@agentskit/adapters' import { costGuard } from '@agentskit/observability' const controller = new AbortController() const runtime = createRuntime({ adapter: openai({ apiKey: KEY, model: 'gpt-4o' }), observers: [ costGuard({ budgetUsd: 0.10, controller, onCost: ({ costUsd, budgetRemainingUsd }) => process.stdout.write( `\r$${costUsd.toFixed(4)} remaining $${budgetRemainingUsd.toFixed(4)}`, ), onExceeded: ({ costUsd, budgetUsd }) => console.warn(`\nBudget exceeded: $${costUsd.toFixed(4)} > $${budgetUsd}`), }), ], }) try { const result = await runtime.run('Write a 5000-word essay on quantum computing', { signal: controller.signal, }) console.log('\nDone:', result.content) } catch (err) { if ((err as Error).name === 'AbortError') { console.log('\nAborted due to cost budget.') } else { throw err } } ``` ## How it works Four primitives compose cleanly: ``` Adapter emits chunk.metadata.usage ↓ Runtime emits llm:end with usage ↓ costGuard accumulates cost, compares to budget ↓ when cost > budget controller.abort() ↓ Runtime RT13: stream stops, memory does not save, promise rejects with AbortError ``` No hardcoded cost logic inside the runtime — the budget lives in userland, the observer watches contract-defined events, and the abort flows through the `AbortController` the runtime already respects. ## Per-model pricing `costGuard` ships a `DEFAULT_PRICES` table (OpenAI, Anthropic, Gemini, Ollama free tier, updated for late 2025 model families). Longest-prefix match wins: `gpt-4o-mini` beats `gpt-4o`. Override any entry via the `prices` option (merged, so you only specify what changed): ```ts costGuard({ budgetUsd: 0.10, controller, prices: { 'my-fine-tuned-model': { input: 0.01, output: 0.03 }, 'gpt-4o': { input: 0.002, output: 0.008 }, // override the default }, }) ``` ## Inspecting the guard state ```ts const guard = costGuard({ budgetUsd: 1.00, controller }) // Live counters during / after the run guard.costUsd() // total in USD guard.promptTokens() // cumulative prompt tokens guard.completionTokens() // cumulative completion tokens guard.exceeded() // boolean guard.reset() // zero counters for a fresh run (same guard) ``` ## Just the math Need the cost helpers without the observer wiring? ```ts import { priceFor, computeCost, DEFAULT_PRICES } from '@agentskit/observability' const price = priceFor('gpt-4o-mini') // { input, output } per 1K tokens const cost = computeCost( { promptTokens: 1500, completionTokens: 500 }, price, ) ``` ## Tighten the recipe - **Per-user quota** — key a counter in Redis by `userId`, reject `run()` before starting if total spend exceeds the user's plan - **Observer composition** — combine with `consoleLogger` and/or a LangSmith/OpenTelemetry observer for audit trail alongside enforcement - **Budget rollover** — call `guard.reset()` at the start of each run if you want per-run isolation; skip it for cumulative enforcement across a session ## Related - [Concepts: Runtime — RT9 observers + RT13 abort](../concepts/runtime) - [Recipe: Cost-guarded chat](./cost-guarded-chat) — same pattern as a UI recipe - [ADR 0006 — Runtime contract](https://github.com/AgentsKit-io/agentskit/blob/main/docs/architecture/adrs/0006-runtime-contract.md) --- # Cost-guarded chat Source: https://www.agentskit.io/docs/reference/recipes/cost-guarded-chat > A chat that aborts the run when token usage exceeds your budget — using only an observer. A chat that tracks per-run token spend and aborts cleanly when the budget is exceeded. No special primitives — just an observer. ## Install ```bash npm install @agentskit/runtime @agentskit/adapters ``` ## The guard ```ts title="cost-guard.ts" import { createRuntime } from '@agentskit/runtime' import { openai } from '@agentskit/adapters' import type { Observer } from '@agentskit/core' // Pricing as of late 2025 — keep in sync with provider docs const PRICE_PER_1K = { input: 0.0025, output: 0.01 } // gpt-4o const BUDGET_USD = 0.10 function createCostGuard(budgetUsd: number, abort: () => void): Observer { let inputTokens = 0 let outputTokens = 0 return { name: 'cost-guard', on(event) { if (event.type === 'llm:end') { // AgentEvent carries usage totals at the end of each LLM call if (event.usage) { inputTokens += event.usage.promptTokens outputTokens += event.usage.completionTokens } const costUsd = (inputTokens / 1000) * PRICE_PER_1K.input + (outputTokens / 1000) * PRICE_PER_1K.output if (costUsd > budgetUsd) { console.warn(`Cost budget exceeded: $${costUsd.toFixed(4)} > $${budgetUsd}`) abort() } } if (event.type === 'agent:step') { const total = (inputTokens / 1000) * PRICE_PER_1K.input + (outputTokens / 1000) * PRICE_PER_1K.output console.log(`Step ${event.step} running cost: $${total.toFixed(4)} (${inputTokens}+${outputTokens} tokens)`) } }, } } const controller = new AbortController() const runtime = createRuntime({ adapter: openai({ apiKey: KEY, model: 'gpt-4o' }), observers: [createCostGuard(BUDGET_USD, () => controller.abort())], }) try { const result = await runtime.run('Write a 5000-word essay on quantum computing', { signal: controller.signal, }) console.log(result.content) } catch (err) { if ((err as Error).name === 'AbortError') { console.log('Aborted due to cost budget.') } else { throw err } } ``` ## Why this works - The `llm:end` event carries a `usage` field (`promptTokens`, `completionTokens`) when the adapter reports it — well-behaved adapters always do - Observers are read-only (RT9) — they can't mutate state, but they can call external APIs (like `controller.abort()`) - Aborting from an observer triggers the Runtime's clean abort path: stream stops, memory **does not save** (RT7+RT13), promise rejects with `AbortError` ## Tighten the recipe - **Per-tool budget** — handle `tool:start` / `tool:end` events in the observer and assign costs (e.g. web search at $0.005/call) - **Per-user quota** — store cumulative spend in Redis keyed by user id; reject before `run()` if over - **Pre-check budget** with `tiktoken` count of the system prompt before sending - **Cost-aware adapter** — wrap the adapter to upgrade/downgrade model based on remaining budget ## Related - [Concepts: Runtime](../concepts/runtime) — observers, abort semantics - [Recipe: Discord bot](./discord-bot) — apply this guard per-channel --- # Custom adapter Source: https://www.agentskit.io/docs/reference/recipes/custom-adapter > Wrap any LLM API as an AgentsKit adapter. Plug-and-play with the rest of the kit in 30 lines. A working adapter for any LLM with an HTTP streaming API. Useful for: - Internal models (your company's fine-tuned model behind an API) - Providers AgentsKit doesn't ship yet - Mocks for tests (deterministic, replayable) ## Install ```bash npm install @agentskit/core ``` ## The adapter ```ts title="my-adapter.ts" import type { AdapterFactory, AdapterRequest, StreamSource, StreamChunk } from '@agentskit/core' export interface MyAdapterOptions { apiKey: string baseUrl: string model: string } export function myAdapter(opts: MyAdapterOptions): AdapterFactory { return { createSource(request: AdapterRequest): StreamSource { const controller = new AbortController() return { // No I/O until stream() is called — invariant A1 async *stream(): AsyncIterableIterator { try { const res = await fetch(`${opts.baseUrl}/v1/chat/completions`, { method: 'POST', headers: { 'authorization': `Bearer ${opts.apiKey}`, 'content-type': 'application/json', }, body: JSON.stringify({ model: opts.model, messages: request.messages, stream: true, }), signal: controller.signal, }) if (!res.ok) { yield { type: 'error', content: `HTTP ${res.status}`, metadata: { error: new Error(await res.text()) }, } return } // Parse server-sent events const reader = res.body!.getReader() const decoder = new TextDecoder() let buffer = '' for (;;) { const { done, value } = await reader.read() if (done) break buffer += decoder.decode(value, { stream: true }) const lines = buffer.split('\n') buffer = lines.pop() ?? '' for (const line of lines) { if (!line.startsWith('data: ')) continue const data = line.slice(6) if (data === '[DONE]') { yield { type: 'done' } return } const json = JSON.parse(data) const content = json.choices?.[0]?.delta?.content if (content) yield { type: 'text', content } } } yield { type: 'done' } } catch (err) { if ((err as Error).name === 'AbortError') return yield { type: 'error', content: (err as Error).message, metadata: { error: err }, } } }, abort: () => controller.abort(), } }, } } ``` ## Use it like any built-in ```ts import { createRuntime } from '@agentskit/runtime' import { myAdapter } from './my-adapter' const runtime = createRuntime({ adapter: myAdapter({ apiKey: process.env.MY_API_KEY!, baseUrl: 'https://api.my-llm.com', model: 'my-model-v1', }), }) const result = await runtime.run('Hello!') console.log(result.content) ``` ## Mock adapter for tests ```ts import type { AdapterFactory, StreamChunk } from '@agentskit/core' export function mockAdapter(chunks: StreamChunk[]): AdapterFactory { return { createSource() { return { async *stream() { for (const chunk of chunks) yield chunk yield { type: 'done' } }, abort: () => {}, } }, } } // In a test: const adapter = mockAdapter([ { type: 'text', content: 'Hello, ' }, { type: 'text', content: 'world!' }, ]) ``` That's a deterministic adapter usable in any test runner. ## Contract checklist Before publishing, verify your adapter against the ten invariants: 1. **A1** No I/O in `createSource` — only when `stream()` runs 2. **A2** Don't call `stream()` twice on one source 3. **A3** Always end with `done`, `error`, or via abort 4. **A4** Each text chunk is independently meaningful 5. **A5** Tool call chunks are atomic (id + name + args together) 6. **A6** `abort()` is always safe — never throws 7. **A7** Don't mutate the input `messages` 8. **A8** Provider-specific data goes in `metadata` 9. **A9** Errors emit chunks; never throw from `stream()` 10. **A10** All config at construction time Full text in [ADR 0001](https://github.com/AgentsKit-io/agentskit/blob/main/docs/architecture/adrs/0001-adapter-contract.md). ## Tighten the recipe - **Tool calling support** — yield `{ type: 'tool_call', toolCall: { id, name, args } }` chunks - **Reasoning streaming** — yield `{ type: 'reasoning', content }` for o1-style models - **Token usage** — yield it on the final chunk in `metadata.usage` so cost guards (see [Cost-guarded chat](./cost-guarded-chat)) can see it - **Retry with backoff** — wrap `fetch` with retries on 429/503 ## Related - [Concepts: Adapter](../concepts/adapter) - ADR 0001 — formal contract --- # Deterministic replay Source: https://www.agentskit.io/docs/reference/recipes/deterministic-replay > Record a real agent session once, then replay it bit-for-bit in tests and bug repros — no more flaky LLM traces. LLMs are non-deterministic. That makes debugging a nightmare: a bug that reproduces locally vanishes the next run, and CI flakes when providers return slightly different tokens. `@agentskit/eval/replay` fixes this by **recording** every `StreamChunk` an adapter produces into a **cassette**, then letting you **replay** it through a fake adapter that is bit-for-bit identical — same tokens, same order, same tool calls, zero network. ## Install ```bash npm install -D @agentskit/eval ``` ## Record once Wrap any real adapter. Every streamed chunk is captured into a `Cassette` object you can persist to disk. ```ts title="record-session.ts" import { createRecordingAdapter, saveCassette } from '@agentskit/eval/replay' import { createRuntime } from '@agentskit/runtime' import { anthropic } from '@agentskit/adapters' const base = anthropic({ apiKey: process.env.ANTHROPIC_API_KEY!, model: 'claude-sonnet-4-6' }) const { factory, cassette } = createRecordingAdapter(base, { seed: 'bug-repro-#427' }) const runtime = createRuntime({ adapter: factory }) await runtime.run('Summarize the quarterly report') await saveCassette('./fixtures/bug-427.cassette.json', cassette) ``` ## Replay forever In tests — or when you're iterating on a fix — swap the real adapter for the cassette. No API keys, no latency, no flake. ```ts title="bug-427.test.ts" import { createReplayAdapter, loadCassette } from '@agentskit/eval/replay' import { createRuntime } from '@agentskit/runtime' const cassette = await loadCassette('./fixtures/bug-427.cassette.json') const runtime = createRuntime({ adapter: createReplayAdapter(cassette) }) const result = await runtime.run('Summarize the quarterly report') expect(result.content).toContain('Q3 revenue') ``` ## Matching modes The replay adapter accepts a `mode` option that controls how incoming requests map to recorded entries. | Mode | Behavior | Use when | |------|----------|----------| | `strict` *(default)* | Request fingerprint (messages + context) must match exactly | Your test sends the same input as the recording | | `sequential` | Returns next unused entry regardless of request | Requests include timestamps or volatile metadata | | `loose` | Matches by last user message content only | You care about the prompt, not the surrounding context | ```ts createReplayAdapter(cassette, { mode: 'sequential' }) ``` ## What goes in a cassette Cassettes are plain JSON. Commit them to git, diff them in PRs. ```json { "version": 1, "seed": "bug-repro-#427", "entries": [ { "request": { "messages": [{ "role": "user", "content": "..." }] }, "chunks": [ { "type": "text", "content": "Hello" }, { "type": "tool_call", "toolCall": { "id": "t1", "name": "search", "args": "{...}" } }, { "type": "done" } ] } ] } ``` ## See also - [Prompt snapshot testing](/docs/reference/recipes/prompt-snapshots) — assert prompts stay stable - [Prompt diff](/docs/reference/recipes/prompt-diff) — attribute output changes to prompt changes - [Eval suite](/docs/reference/recipes/eval-suite) — score agents in CI --- # Devtools server Source: https://www.agentskit.io/docs/reference/recipes/devtools-server > Expose a live feed of agent events so any browser extension or custom dashboard can inspect a running agent. The AgentsKit devtools server is a **transport-agnostic pub/sub hub** for agent events. Plug it into your runtime as an observer; attach any transport (SSE response, WebSocket, in-process sink) as a client. New clients get a replay of the recent ring buffer so they can jump in mid-session. The envelope shape is the contract a browser extension (or your own dashboard) speaks against. ## Install Comes with `@agentskit/observability`. ## Wire it up ```ts import { createDevtoolsServer } from '@agentskit/observability' import { createRuntime } from '@agentskit/runtime' import { anthropic } from '@agentskit/adapters' export const devtools = createDevtoolsServer({ bufferSize: 500 }) export const runtime = createRuntime({ adapter: anthropic({ apiKey: process.env.ANTHROPIC_API_KEY!, model: 'claude-sonnet-4-6' }), observers: [devtools.observer], }) ``` `devtools.observer` receives every `AgentEvent` the runtime emits and fans it out to every attached client. ## Expose over SSE (Node `http` example) ```ts import { createServer } from 'node:http' import { toSseFrame } from '@agentskit/observability' import { devtools } from './runtime' createServer((req, res) => { if (req.url !== '/agentskit/devtools') { res.statusCode = 404 res.end() return } res.writeHead(200, { 'content-type': 'text/event-stream', 'cache-control': 'no-cache', connection: 'keep-alive', }) const detach = devtools.attach({ id: `c-${Date.now()}`, send: envelope => res.write(toSseFrame(envelope)), close: () => res.end(), }) req.on('close', detach) }).listen(4999) ``` Point your browser extension at `http://localhost:4999/agentskit/devtools` and it receives: 1. `{ type: 'hello', protocol: 1, serverId, since }` 2. One `{ type: 'agent-event' }` per retained event 3. `{ type: 'replay-end', seq }` 4. Live feed of new `agent-event` envelopes ## Protocol ```ts type DevtoolsEnvelope = | { type: 'hello'; protocol: 1; serverId: string; since: string } | { type: 'agent-event'; seq: number; at: number; event: AgentEvent } | { type: 'replay-end'; seq: number } ``` `seq` is monotonic per server, `at` is `Date.now()` at publish time. ## Programmatic access No browser needed — `devtools.buffer()` returns a snapshot of retained events for in-process assertions, tests, or CLI tools. ## See also - [Console logger](/docs/reference/recipes/cost-guarded-chat) — simpler observability for local runs - [Cost guard](/docs/reference/recipes/cost-guard) — hard $ ceiling --- # Discord bot Source: https://www.agentskit.io/docs/reference/recipes/discord-bot > A Discord bot powered by AgentsKit. Replies in threads, calls tools, remembers per-channel. A Discord bot that responds to mentions, holds per-channel memory, and can use tools like web search. ## Install ```bash npm install @agentskit/runtime @agentskit/adapters @agentskit/tools @agentskit/memory discord.js ``` ## The bot ```ts title="bot.ts" import { Client, GatewayIntentBits, Partials } from 'discord.js' import { createRuntime } from '@agentskit/runtime' import { anthropic } from '@agentskit/adapters' import { webSearch } from '@agentskit/tools' import { sqliteChatMemory } from '@agentskit/memory' const client = new Client({ intents: [ GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent, ], partials: [Partials.Channel], }) // One memory per channel — keeps conversations separate const runtimeFor = (channelId: string) => createRuntime({ adapter: anthropic({ apiKey: KEY, model: 'claude-sonnet-4-6' }), tools: [webSearch()], memory: sqliteChatMemory({ path: `./data/${channelId}.db` }), systemPrompt: 'You are a helpful Discord bot. Keep replies under 1500 characters. ' + 'Use web search when the user asks about anything time-sensitive.', maxSteps: 6, }) client.on('messageCreate', async (msg) => { // Only respond when mentioned if (msg.author.bot) return if (!msg.mentions.has(client.user!)) return const text = msg.content.replace(/<@!?\d+>/g, '').trim() if (!text) return await msg.channel.sendTyping() try { const result = await runtimeFor(msg.channelId).run(text) await msg.reply(result.content.slice(0, 1900)) } catch (err) { await msg.reply(`Sorry, something broke: ${(err as Error).message}`) } }) client.login(process.env.DISCORD_TOKEN!) ``` ## Run it ```bash DISCORD_TOKEN=your-bot-token npx tsx bot.ts ``` ## Why per-channel runtimes - **Memory isolation** — each channel has its own SQLite file - **Different system prompts per server** become trivial later - **Cheap** — `createRuntime` is config-only (per ADR 0006 RT1), no resources opened until `run()` ## Tighten the recipe - **Slash commands** for explicit invocation instead of mentions - **Streaming** via Discord message edits (chunk by chunk) - **Channel-scoped tools** (e.g. an admin channel gets `shell()`, public channels don't) - **Cost guard** — wrap `runtime` with an observer that aborts after $X. See [Cost-guarded chat](./cost-guarded-chat). ## Related - [Recipe: Persistent memory](./persistent-memory) - [Concepts: Memory](../concepts/memory) — why one ChatMemory per channel --- # Document loaders Source: https://www.agentskit.io/docs/reference/recipes/doc-loaders > One-line fetchers for URL, GitHub, Notion, Confluence, Google Drive, and PDF into your RAG pipeline. Every RAG pipeline starts with "turn an external document into an `InputDocument`". `@agentskit/rag` now ships six loaders that cover the common sources; each accepts a custom `fetch` for tests and returns `InputDocument[]` ready to pipe into `RAG.ingest`. ## Install ```bash npm install @agentskit/rag ``` ## Loaders | Loader | Source | |---|---| | `loadUrl(url)` | Any HTTP URL (raw text / html) | | `loadGitHubFile(owner, repo, path, { ref?, token? })` | Single file via `raw.githubusercontent.com` | | `loadGitHubTree(owner, repo, { filter?, maxFiles? })` | Recursive repo tree, filtered | | `loadNotionPage(pageId, { token })` | Flattens paragraphs + headings | | `loadConfluencePage(pageId, { baseUrl, token })` | Atlassian storage body | | `loadGoogleDriveFile(fileId, { accessToken })` | Drive export as `text/plain` | | `loadPdf(url, { parsePdf })` | BYO PDF parser (`pdf-parse`, `pdfjs`, etc.) | ## Example — RAG over a GitHub repo ```ts import { createRAG, loadGitHubTree } from '@agentskit/rag' import { fileVectorMemory } from '@agentskit/memory' import { openaiEmbedder } from '@agentskit/adapters' const docs = await loadGitHubTree('my-org', 'my-repo', { token: process.env.GITHUB_TOKEN!, filter: path => path.endsWith('.md') || path.endsWith('.ts'), maxFiles: 500, }) const rag = createRAG({ embed: openaiEmbedder({ apiKey: process.env.OPENAI_API_KEY! }), store: fileVectorMemory({ path: './kb.json' }), }) await rag.ingest(docs) ``` ## Example — PDF via any parser ```ts import { loadPdf } from '@agentskit/rag' import pdfParse from 'pdf-parse' const docs = await loadPdf('https://example.com/report.pdf', { parsePdf: async bytes => { const result = await pdfParse(Buffer.from(bytes)) return { text: result.text, pages: result.numpages } }, }) ``` ## See also - [RAG chat](/docs/reference/recipes/rag-chat) - [RAG reranking](/docs/reference/recipes/rag-reranking) --- # Durable execution (Temporal-style) Source: https://www.agentskit.io/docs/reference/recipes/durable-execution > Wrap side-effectful steps so crashes, deploys, and retries replay from a step log instead of starting over. When an agent crashes halfway through a 10-step workflow, the user doesn't want you to start over — they want you to resume. Durable execution gives you that with two primitives: a `StepLogStore` (persistence) and a `runner.step(id, fn)` wrapper (short-circuits to the recorded result if the id already exists in the log). ## Install Ships with `@agentskit/runtime`. ## Wrap side effects in steps ```ts import { createDurableRunner, createFileStepLog, } from '@agentskit/runtime' const store = await createFileStepLog('./runs/user-42.jsonl') const runner = createDurableRunner({ store, runId: 'user-42-onboard', maxAttempts: 3, retryDelayMs: 500, }) await runner.step('create-account', async () => createAccount({ email })) await runner.step('send-welcome', async () => sendEmail({ to: email, template: 'welcome' })) await runner.step('charge-trial', async () => stripe.subscriptions.create({ ... })) ``` Re-run the same code (same `runId`) after a crash — completed steps short-circuit to their recorded values, only the remaining ones execute. ## Stores - `createInMemoryStepLog()` — tests, single-process demos. - `createFileStepLog(path)` — JSONL on disk, append-only, survives restarts. - Bring your own — anything implementing `{ append, get, list, clear? }` works (Redis, Postgres, S3, etc). ## Step contract A step is **idempotent from the log's perspective**: the fn does the side effect, the recorded `result` captures everything downstream steps need. Don't rely on global state outside the result. ```ts const { userId } = await runner.step('create-account', async () => ({ userId: await createAccount(email), })) // Downstream steps use `userId` — NOT global `req.user.id`, which // might not exist on a resumed run. await runner.step('send-welcome', async () => sendEmail({ userId })) ``` ## Retries - `maxAttempts`: total attempts per step (default 1 — fail-fast). - `retryDelayMs`: fixed backoff between attempts (default 0). - A step that fails all attempts is recorded with `status: 'failure'`; replaying the same `stepId` re-throws without running again (so you can diagnose without re-executing expensive failing work). Call `runner.reset()` to wipe the log for a fresh retry. ## Observability ```ts createDurableRunner({ store, runId, onEvent: e => logger.debug('durable', e), }) ``` Events: `step:replay`, `step:start`, `step:success`, `step:failure`. ## See also - [HITL approvals](/docs/reference/recipes/hitl-approvals) — pause a step until a human approves. - [Background agents](/docs/reference/recipes/background-agents) — run durable flows on a cron or webhook. --- # Edit + regenerate messages Source: https://www.agentskit.io/docs/reference/recipes/edit-and-regenerate > Let users correct a prompt, edit the model's answer, or re-run any assistant turn — with correct truncation and streaming. Every serious chat UI needs two operations that `send` / `retry` don't cover: - **Edit** — rewrite a previous message (user prompt or assistant answer) - **Regenerate** — re-run the model from a specific turn, dropping everything after it Both are built into `useChat` (React and Ink) and `createChatController`. ## Install ```bash npm install @agentskit/react @agentskit/adapters ``` ## The UI ```tsx title="app/chat.tsx" 'use client' import { useChat, ChatContainer, Message, InputBar } from '@agentskit/react' import { openai } from '@agentskit/adapters' import '@agentskit/react/theme' export default function Chat() { const chat = useChat({ adapter: openai({ apiKey: KEY, model: 'gpt-4o' }), }) return ( {chat.messages.map(m => (
{m.role === 'assistant' && m.status === 'complete' && ( )} {m.role === 'user' && ( )}
))}
) } ``` ## `regenerate(messageId?)` Re-run the model: ```ts // No id: regenerates the last assistant turn (same as retry) await chat.regenerate() // With id: targets a specific assistant message. // Every turn after it is dropped, the preceding user prompt is replayed. await chat.regenerate(assistantMessage.id) ``` `regenerate` aborts any in-flight stream before re-running. The state updates synchronously (optimistic) so your UI shows the placeholder immediately. ## `edit(messageId, newContent, opts?)` ### Editing an assistant message Replaces content in place. **No regeneration** — useful for reviewers correcting a model's answer inline. ```ts await chat.edit(assistantMessage.id, 'Corrected: the answer is 42.') ``` ### Editing a user message Drops every turn after, optionally regenerates: ```ts // Default: truncate and regenerate await chat.edit(userMessage.id, 'actually, use Python instead') // Just truncate — stay idle await chat.edit(userMessage.id, 'rephrased', { regenerate: false }) ``` ## What happens behind the scenes | Action | Before | After | |---|---|---| | `edit(assistant-id, 'fix')` | `[user, assistant]` | `[user, assistant*]` *(content replaced)* | | `edit(user-id, 'v2')` | `[user, assistant, user2, assistant2]` | `[user*, assistantNEW]` | | `edit(user-id, 'v2', { regenerate: false })` | `[user, assistant, ...]` | `[user*]` | | `regenerate(assistant-id)` | `[user, assistant, user2, assistant2]` | `[user, assistantNEW]` | | `regenerate()` | `[user, assistant]` | `[user, assistantNEW]` | The asterisk marks the edited message. `NEW` marks a fresh assistant placeholder that the new stream lands on. ## Optimistic UI State updates are synchronous, so your React tree re-renders with the truncated history + streaming placeholder before the network round-trip starts. No loading states needed for the truncation itself. ## Common pitfalls | Pitfall | Fix | |---|---| | Calling `edit` on a message that doesn't exist | No-op by design — no throw, no state change | | Calling `regenerate()` with no assistant turn yet | No-op — safe to wire to a button that might fire early | | Editing an assistant message and expecting it to re-run | Pass the **user** message id instead, or call `regenerate(assistantId)` after | | Concurrent `send` + `regenerate` | The second call aborts the first in-flight stream via ADR 0001 A6 | ## Related - [Concepts: Runtime](../concepts/runtime) — abort semantics (RT13) - [Recipe: Persistent memory](./persistent-memory) — edits + truncation still play nice with `ChatMemory` atomicity (CM4) - [ADR 0001 — Adapter contract](https://github.com/AgentsKit-io/agentskit/blob/main/docs/architecture/adrs/0001-adapter-contract.md) — A6 abort safety --- # Encrypted memory Source: https://www.agentskit.io/docs/reference/recipes/encrypted-memory > Client-side AES-GCM encryption for any ChatMemory — keys never leave the caller, backing store only sees ciphertext. `createEncryptedMemory` wraps any existing `ChatMemory` so the backing store only ever sees opaque ciphertext. Users hold the key; rogue middleware, backups, and even the backing service itself can't read the plaintext. Uses Web Crypto (AES-GCM, 256-bit) — available on Node 20+ and all modern browsers. ## Install Ships with `@agentskit/memory`. ## Wire it up ```ts import { createEncryptedMemory, fileChatMemory } from '@agentskit/memory' // A 32-byte key. Generate once per user during onboarding and store // it on their device (iOS Keychain, Android Keystore, OS credential // manager, browser IndexedDB with key derivation from a passphrase). const key = crypto.getRandomValues(new Uint8Array(32)) const memory = await createEncryptedMemory({ backing: fileChatMemory({ path: './sessions/user-42.json' }), key, }) const runtime = createRuntime({ adapter, memory }) ``` On `save`, every message's `content` becomes `""` and the ciphertext is stashed in `metadata.{ciphertext, iv, length}`. On `load`, the process reverses transparently — the agent never knows the encryption is there. ## Additional authenticated data (AAD) Bind ciphertext to context so the same key can't decrypt messages captured from a different tenant / room / session: ```ts const memory = await createEncryptedMemory({ backing, key, aad: new TextEncoder().encode(`tenant:${tenantId}`), }) ``` ## Idempotent Already-encrypted messages (tagged with `metadata.agentskitEncrypted`) are passed through untouched on subsequent saves, so re-reading and re-saving won't double-encrypt. ## Key management - Keys **never** pass through the backing store — they live on the user's device. - Different key → decryption fails with `OperationError`. That's the correct behavior; treat it as "data is lost, key rotation required." - Pair with [Signed audit log](/docs/reference/recipes/audit-log) for regulator-friendly evidence of who accessed what. ## See also - [Persistent memory](/docs/reference/recipes/persistent-memory) - [Vector memory adapters](/docs/reference/recipes/vector-adapters) --- # Eval suite for an agent Source: https://www.agentskit.io/docs/reference/recipes/eval-suite > Score an agent's quality in CI. A test suite for agents, not for code. A vitest test that runs your agent against a dataset of inputs + expected outputs and scores the results. Fail the build when quality regresses. ## Install ```bash npm install -D @agentskit/eval @agentskit/runtime @agentskit/adapters vitest ``` ## The suite ```ts title="evals/suite.ts" import type { EvalSuite } from '@agentskit/eval' export const suite: EvalSuite = { name: 'basic-regression', cases: [ { input: 'What is 2 + 2?', expected: '4', }, { input: 'Translate "hello" to French', expected: (output) => output.toLowerCase().includes('bonjour'), }, { input: 'In one word, what color is the sky?', expected: (output) => output.toLowerCase().includes('blue'), }, ], } ``` ## The eval test ```ts title="evals/agent.eval.test.ts" import { describe, it, expect } from 'vitest' import { runEval } from '@agentskit/eval' import type { AgentFn } from '@agentskit/eval' import { createRuntime } from '@agentskit/runtime' import { openai } from '@agentskit/adapters' import { suite } from './suite' const runtime = createRuntime({ adapter: openai({ apiKey: process.env.OPENAI_API_KEY!, model: 'gpt-4o-mini' }), systemPrompt: 'Be terse and direct.', }) const agent: AgentFn = async (input) => { const result = await runtime.run(input) return result.content } describe('agent quality', () => { it('passes the regression suite above 80%', async () => { const report = await runEval({ agent, suite }) console.log(`Score: ${(report.accuracy * 100).toFixed(1)}%`) console.log(`Passed: ${report.passed} / ${report.totalCases}`) console.log(`Failed: ${report.failed}`) expect(report.accuracy).toBeGreaterThanOrEqual(0.8) }, 60_000) }) ``` ## Run it ```bash npx vitest run evals/ ``` ## Run it in CI ```yaml title=".github/workflows/agent-eval.yml" name: Agent eval on: pull_request: paths: - 'src/agents/**' - 'evals/**' jobs: eval: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 - uses: actions/setup-node@v4 with: node-version: 22 cache: pnpm - run: pnpm install --frozen-lockfile - run: npx vitest run evals/ env: OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} ``` ## LLM-as-judge for fuzzy outputs Hard-coded scoring is brittle for natural-language outputs. Use a model: ```ts import { openai } from '@agentskit/adapters' const judge = openai({ apiKey: KEY, model: 'gpt-4o-mini' }) async function llmScore(output: string, expected: string): Promise { const judgeRuntime = createRuntime({ adapter: judge, systemPrompt: 'Score how well the OUTPUT matches the EXPECTED on a scale 0-1. Reply with only a number.', }) const result = await judgeRuntime.run(`OUTPUT: ${output}\nEXPECTED: ${expected}`) return parseFloat(result.content.trim()) || 0 } ``` Use `llmScore` in your dataset's `score` field for any case where exact match is too strict. ## Tighten the recipe - **Per-skill datasets** — different evals for `researcher`, `coder`, `support_triager` - **Snapshot mode** — record the agent's output, ask reviewers to approve diffs vs the golden snapshot - **Replay deterministic adapters** so eval is fast and free in CI; only run real-model evals nightly - **Track drift over time** — emit metrics to a dashboard; alert when score drops 5%+ ## Related - [Concepts: Runtime](../concepts/runtime) - [Phase 2 roadmap #134](https://github.com/AgentsKit-io/agentskit/issues/134) — deterministic replay --- # Evals in CI Source: https://www.agentskit.io/docs/reference/recipes/evals-ci > Run agent evals on every PR, fail builds below a minimum accuracy, surface results in the PR UI. Agent quality should gate merges the same way unit tests do. `@agentskit/eval/ci` + the bundled `agentskit-evals` composite action wire your suite into GitHub Actions: JUnit report for the test-result UI, Markdown for `$GITHUB_STEP_SUMMARY`, inline annotations on failures, and a minimum-accuracy gate that fails the job when agent quality regresses. ## Install ```bash npm install -D @agentskit/eval ``` ## Author an eval runner ```ts title="evals/run.ts" import { runEval } from '@agentskit/eval' import { reportToCi } from '@agentskit/eval/ci' import { createRuntime } from '@agentskit/runtime' import { anthropic } from '@agentskit/adapters' const runtime = createRuntime({ adapter: anthropic({ apiKey: process.env.ANTHROPIC_API_KEY!, model: 'claude-sonnet-4-6' }), }) const result = await runEval({ agent: async input => (await runtime.run(input)).content, suite: { name: 'qa-baseline', cases: [ { input: 'Capital of France?', expected: 'Paris' }, { input: 'Square root of 64?', expected: '8' }, ], }, }) const min = Number(process.env.AGENTSKIT_EVAL_MIN_ACCURACY ?? '1') const outDir = process.env.AGENTSKIT_EVAL_OUT_DIR ?? 'agentskit-evals' const report = await reportToCi({ suiteName: 'qa-baseline', result, minAccuracy: min, outDir, }) if (!report.pass) process.exit(1) ``` ## Drop in the composite action ```yaml title=".github/workflows/evals.yml" name: evals on: [pull_request] jobs: evals: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: ./.github/actions/agentskit-evals with: script: evals/run.ts min-accuracy: '0.9' env: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} ``` Action inputs: | Input | Default | Purpose | |-------|---------|---------| | `script` | *(required)* | Path to the runner | | `node-version` | `20` | Node.js version | | `package-manager` | `pnpm` | `pnpm` / `npm` / `yarn` | | `min-accuracy` | `1` | Fail below this (0..1) | | `out-dir` | `agentskit-evals` | Reports directory | | `upload-artifact` | `true` | Publish reports as an artifact | ## What you get in the PR - `report.xml` — JUnit, surfaced by test-reporter actions - `report.md` — appended to the workflow summary - `::error::` / `::notice::` annotations inline on the diff - Exit code 1 when accuracy drops below `min-accuracy` ## Reporters Each reporter is also exported for custom pipelines: ```ts import { renderJUnit, renderMarkdown, renderGitHubAnnotations, } from '@agentskit/eval/ci' ``` ## See also - [Eval suite](/docs/reference/recipes/eval-suite) — author the suite itself - [Deterministic replay](/docs/reference/recipes/deterministic-replay) — pin cassettes in CI --- # Fallback chain Source: https://www.agentskit.io/docs/reference/recipes/fallback-chain > Try adapters in order — on error, fall through to the next without duplicating tool calls. Providers go down. APIs rate-limit. Keys rotate. `createFallbackAdapter` takes an ordered list of adapters and tries them in sequence: first one that produces a real chunk wins. Once any adapter has committed (emitted its first non-`done` chunk), mid-stream errors are propagated — no silent cross-candidate retries that would duplicate tool calls or double-charge tokens. ## Install Comes with `@agentskit/adapters`. ## Quick start ```ts import { createFallbackAdapter, anthropic, openai } from '@agentskit/adapters' const adapter = createFallbackAdapter([ { id: 'primary', adapter: anthropic({ model: 'claude-sonnet-4-6' }) }, { id: 'backup', adapter: openai({ model: 'gpt-4o' }) }, { id: 'local', adapter: ollama({ model: 'llama3.1' }) }, ]) ``` ## When does it fall through? - `createSource` throws synchronously - `stream()` throws **before** the first chunk - The adapter returns an async iterable that yields **zero** chunks Once the first chunk is out, the adapter is committed. ## Opt out of retrying specific errors ```ts createFallbackAdapter(candidates, { shouldRetry: (error, index) => { // Don't fall through on auth errors — they'll likely hit every provider. if (/401|403|unauthorized/i.test(error.message)) return false return true }, }) ``` ## Observe hops ```ts createFallbackAdapter(candidates, { onFallback: ({ id, index, error }) => { logger.warn(`[fallback] ${id} (idx=${index}) failed: ${error.message}`) }, }) ``` ## What happens when everything fails The adapter throws `all fallback candidates failed (id1: msg; id2: msg; ...)` with each candidate's last error, so you can diagnose without rerunning. ## See also - [Adapter router](/docs/reference/recipes/adapter-router) — pick by cost/latency, not by order - [Ensemble](/docs/reference/recipes/adapter-ensemble) — combine, not select - [Speculative execution](/docs/reference/recipes/speculative-execution) --- # Vue / Svelte / Solid / React Native / Angular Source: https://www.agentskit.io/docs/reference/recipes/framework-adapters > One package per framework. Same ChatReturn contract as @agentskit/react — pick the binding that matches your stack. Every framework binding ships as its own package and mirrors the `@agentskit/react` contract: same action methods (`send`, `stop`, `retry`, `edit`, `regenerate`, `setInput`, `clear`, `approve`, `deny`), same headless `data-ak-*` attributes on components, same `ChatReturn` shape for state. ## Packages | Package | Surface | Peer dep | |---|---|---| | `@agentskit/react` | `useChat` hook + components | `react ^18|^19` | | `@agentskit/vue` | `useChat` composable + `ChatContainer` | `vue ^3.4` | | `@agentskit/svelte` | `createChatStore` | `svelte ^5` | | `@agentskit/solid` | `useChat` hook | `solid-js ^1.8` | | `@agentskit/react-native` | `useChat` hook (Metro-safe) | `react ^18|^19`, `react-native *` | | `@agentskit/angular` | `AgentskitChat` service (Signal + RxJS) | `@angular/core ^18|^19|^20`, `rxjs ^7` | ## Vue ```ts import { useChat } from '@agentskit/vue' const chat = useChat({ adapter }) // chat.messages, chat.input reactive; chat.send, chat.setInput, ... ``` ## Svelte ```svelte {#each $chat.messages as m (m.id)}

{m.content}

{/each} ``` ## Solid ```tsx import { useChat } from '@agentskit/solid' const chat = useChat({ adapter }) ``` ## React Native / Expo ```tsx import { useChat } from '@agentskit/react-native' ``` ## Angular ```ts import { Component, inject } from '@angular/core' import { AgentskitChat } from '@agentskit/angular' @Component({ selector: 'ak-chat', template: '...' }) export class ChatPage { chat = inject(AgentskitChat) constructor() { this.chat.init({ adapter }) } } ``` Use `chat.state()` inside templates or subscribe to `chat.stream$`. ## See also - [useChat (React)](/docs/ui/use-chat) - [Custom adapter](/docs/reference/recipes/custom-adapter) --- # Generative UI + artifacts Source: https://www.agentskit.io/docs/reference/recipes/generative-ui > A typed JSON schema the agent emits, a framework-agnostic element tree, plus rich artifacts (code, markdown, HTML, charts). Generative UI replaces "the agent writes a paragraph" with "the agent emits a structured element tree and your renderer decides how it looks." `@agentskit/core/generative-ui` ships the typed schema + validators + artifact detectors so every frontend can consume the same payload. ## Install Ships as a `@agentskit/core` subpath. ```ts import { parseUIMessage, validateUIMessage, detectCodeArtifacts, type UIMessage, type Artifact, } from '@agentskit/core/generative-ui' ``` ## Element types | `kind` | Purpose | |---|---| | `text` | Inline text, optional bold | | `heading` | Levels 1–3 | | `list` | Ordered or bullet list of strings | | `button` | Emits an `action` + optional payload on click | | `image` | Image src + alt | | `card` | Titled container with child elements | | `stack` | Row / column layout with children | | `artifact` | Embeds a rich artifact (code, md, html, chart) | ## Artifact types | `type` | Purpose | |---|---| | `code` | Language + source + optional filename | | `markdown` | Markdown source | | `html` | Raw HTML + sandbox hint | | `chart` | `line`/`bar`/`pie`/`scatter`/`area` with rows | ## Agent → JSON → render ```ts const raw = await runtime.run('Show me a cost breakdown') const ui = parseUIMessage(raw.content) render(ui.root) ``` `parseUIMessage` tolerates fenced `\`\`\`json` blocks in surrounding prose, so you don't have to wrestle the model into pure JSON output. ## Extract code blocks from plain text ```ts const artifacts = detectCodeArtifacts(raw.content) // [ { artifact: { type: 'code', language: 'ts', source: '...' }, start, end } ] ``` Useful when the agent still emits prose but you want to lift code blocks into a dedicated renderer (copy button, diff viewer, run-on- click). ## See also - [Custom adapter](/docs/reference/recipes/custom-adapter) - [MCP bridge](/docs/reference/recipes/mcp-bridge) --- # Graph memory Source: https://www.agentskit.io/docs/reference/recipes/graph-memory > Non-linear memory for entities and relationships. Backs anything from in-memory Maps to Neo4j. Not every fact fits in a chat transcript. Facts about people, companies, products, and how they relate live longer than a conversation. `createInMemoryGraph` is the three-method reference implementation you can reach for locally; back the same `GraphMemory` contract with Neo4j / Memgraph / AWS Neptune for production. ## Install Ships with `@agentskit/memory`. ## Model facts ```ts import { createInMemoryGraph } from '@agentskit/memory' const graph = createInMemoryGraph() await graph.upsertNode({ id: 'alice', kind: 'person', properties: { name: 'Alice' } }) await graph.upsertNode({ id: 'acme', kind: 'company', properties: { name: 'Acme Inc.' } }) await graph.upsertEdge({ id: 'e1', label: 'works-at', from: 'alice', to: 'acme' }) const neighbors = await graph.neighbors('alice', { depth: 2 }) ``` ## Contract ```ts interface GraphMemory { upsertNode(node): Promise upsertEdge(edge): Promise getNode(id): Promise findNodes(query?): Promise findEdges(query?): Promise neighbors(id, { depth?, label? }): Promise deleteNode(id): Promise // cascades to touching edges deleteEdge(id): Promise clear?(): Promise } ``` BFS `neighbors` explores outward up to `depth`, optionally filtered by edge `label`. Matches the common agent use-case: "who is related to X through relationship Y, within N hops?" ## See also - [Personalization](/docs/reference/recipes/personalization) - [Hierarchical memory](/docs/reference/recipes/hierarchical-memory) --- # Hierarchical memory (MemGPT-style) Source: https://www.agentskit.io/docs/reference/recipes/hierarchical-memory > Three tiers — working, recall, archival — so long-running agents don't lose context and don't blow the window. Long-running agents need memory at three timescales: the current turn (working), the last few days (recall), and the full history (archival). MemGPT formalized the pattern; `createHierarchicalMemory` ships it as a drop-in `ChatMemory`: - **Working** — hot window always in the prompt. - **Recall** — mid-term, queried on demand (usually a vector store). - **Archival** — cold, source-of-truth store of every message. ## Install ```bash npm install @agentskit/memory ``` ## Wire the three tiers ```ts import { createHierarchicalMemory, fileChatMemory, fileVectorMemory, } from '@agentskit/memory' const archival = fileChatMemory({ path: './sessions/user-42.json' }) const working = fileChatMemory({ path: './sessions/user-42.hot.json' }) const vectorStore = fileVectorMemory({ path: './sessions/user-42.vectors.json' }) const memory = createHierarchicalMemory({ working, archival, workingLimit: 30, recallTopK: 5, recall: { index: async msg => { const embedding = await embed(msg.content) await vectorStore.store([{ id: msg.id, content: msg.content, embedding }]) }, query: async ({ working, topK }) => { const latest = working[working.length - 1] if (!latest) return [] const hits = await vectorStore.search(await embed(latest.content), { topK }) const byId = new Map((await archival.load()).map(m => [m.id, m] as const)) return hits.flatMap(h => (byId.get(h.id) ? [byId.get(h.id)!] : [])) }, }, }) ``` ## Flow - **save()**: archival gets everything; working is trimmed to `workingLimit`; overflow + freshly saved messages are passed to `recall.index` so future turns can surface them. - **load()**: working window + up to `recallTopK` recall hits, spliced chronologically, duplicates filtered. Recall errors are swallowed — a dead vector store degrades gracefully to "working-only", never breaks `load`. ## Without recall Omit `recall` and you get a hard-backed working + archival pair — useful when you want the tiered shape without (yet) hooking up a vector store. ```ts createHierarchicalMemory({ working, archival, workingLimit: 50 }) ``` ## See also - [Persistent memory](/docs/reference/recipes/persistent-memory) - [Virtualized memory](/docs/reference/recipes/virtualized-memory) - [Auto-summarization](/docs/reference/recipes/auto-summarize) --- # Human-in-the-loop approvals Source: https://www.agentskit.io/docs/reference/recipes/hitl-approvals > Pause an agent at a named gate, persist the decision, resume deterministically. A destructive tool call, a risky email, a big refund — some agent actions need human approval. `@agentskit/core/hitl` gives you the three primitives that make that practical: a persisted `Approval` record, a `request → await → decide` API, and a pluggable `ApprovalStore` so the decision survives crashes and worker restarts. ## Install Built into `@agentskit/core` (subpath, zero extra weight on the main bundle). ```ts import { createApprovalGate, createInMemoryApprovalStore } from '@agentskit/core/hitl' ``` ## Pause-resume flow ```ts const gate = createApprovalGate(myStore) async function deleteUserTool({ userId }: { userId: string }) { const approval = await gate.request({ id: `delete-${userId}`, name: 'delete-user', payload: { userId }, }) const decision = await gate.await(approval.id, { timeoutMs: 3_600_000 }) if (decision.status !== 'approved') { throw new Error(`rejected by ${decision.decisionMetadata?.approver ?? 'human'}`) } await db.users.delete(userId) } ``` On the operator side: ```ts await gate.decide('delete-42', 'approved', { approver: 'alice' }) // or await gate.decide('delete-42', 'rejected', { reason: 'account active' }) ``` `gate.request` is **idempotent** on `id` — resuming a crashed run with the same id returns the existing approval (pending or decided) instead of creating a duplicate. ## Stores - `createInMemoryApprovalStore()` — tests, single-process demos. - Bring your own with the 3-method contract (`put` / `get` / `patch`). Redis, Postgres, SQS, DynamoDB — whatever you already run. ## Options `gate.await(id, options?)`: | Option | Default | Purpose | |--------|---------|---------| | `timeoutMs` | `Infinity` | Reject with `timed out` after N ms | | `pollMs` | `500` | Poll interval (store dictates whether polling is actually needed) | | `signal` | — | `AbortSignal` to cancel waiting | ## Pair with durable execution A step whose work should *not* re-run on resume wraps its approval check inside `runner.step(id, fn)`. Once the step is recorded, the next run short-circuits instead of asking the human twice. ```ts await runner.step(`approve-delete-${userId}`, async () => { const approval = await gate.request({ id: `delete-${userId}`, name: 'delete-user', payload: { userId } }) return gate.await(approval.id, { timeoutMs: 3_600_000 }) }) ``` ## See also - [Durable execution](/docs/reference/recipes/durable-execution) - [Confirmation-gated tools](/docs/reference/recipes/confirmation-gated-tool) --- # Provider integrations Source: https://www.agentskit.io/docs/reference/recipes/integrations > Ready-made tools for GitHub, Linear, Slack, Notion, Discord, Gmail, Google Calendar, Stripe, Postgres, and S3. Every integration lives under `@agentskit/tools/integrations`. Each module exports focused `defineTool` factories plus a bundle helper that returns all tools for that provider — mix and match. ## Install ```bash npm install @agentskit/tools ``` ```ts import { github, linear, slack, notion, discord, gmail, googleCalendar, stripe, postgres, s3, } from '@agentskit/tools/integrations' ``` ## Dev + chat ```ts const tools = [ ...github({ token: process.env.GITHUB_TOKEN! }), ...linear({ apiKey: process.env.LINEAR_API_KEY! }), ...slack({ token: process.env.SLACK_BOT_TOKEN! }), ...notion({ token: process.env.NOTION_TOKEN! }), ...discord({ token: process.env.DISCORD_BOT_TOKEN! }), ] ``` | Provider | Tools | |---|---| | `github` | `github_search_issues`, `github_create_issue`, `github_comment_issue` | | `linear` | `linear_search_issues`, `linear_create_issue` | | `slack` | `slack_post_message`, `slack_search` | | `notion` | `notion_search`, `notion_create_page` | | `discord` | `discord_post_message` | ## Google Workspace ```ts const tools = [ ...gmail({ accessToken: oauthToken }), ...googleCalendar({ accessToken: oauthToken, calendarId: 'primary' }), ] ``` `gmail_list_messages`, `gmail_send_email`, `calendar_list_events`, `calendar_create_event`. ## Stripe + storage ```ts const tools = [ ...stripe({ apiKey: process.env.STRIPE_SECRET_KEY! }), ...postgres({ execute: async (sql, params) => { const r = await pg.query(sql, params) return { rows: r.rows, rowCount: r.rowCount ?? 0 } }, allowWrites: false, maxRows: 500, }), ...s3({ client: { getObject: async ({ bucket, key }) => ({ body: await getFromS3(bucket, key) }), putObject: async ({ bucket, key, body }) => ({ etag: await putToS3(bucket, key, body) }), listObjects: async ({ bucket, prefix, limit }) => listS3(bucket, prefix, limit), }, defaultBucket: 'agent-artifacts', }), ] ``` Postgres ships with safe-mode — read-only by default, `VACUUM` / `COPY` / `TRUNCATE` permanently denied, write verbs require `allowWrites: true`, results capped by `maxRows`. The S3 tool accepts any client implementing three methods so you can plug in `@aws-sdk/client-s3`, MinIO, or Cloudflare R2 without bundling a large SDK. ## Pair with - [Mandatory sandbox](/docs/reference/recipes/mandatory-sandbox) — enforce per-tool allow/deny + validators across the whole provider bundle. - [HITL approvals](/docs/reference/recipes/hitl-approvals) — gate destructive actions (issue creation, invoices) behind a human decision. - [Audit log](/docs/reference/recipes/audit-log) — record every tool call for SOC 2 evidence. ## See also - [MCP bridge](/docs/reference/recipes/mcp-bridge) — expose these tools to MCP hosts - [Tool composer](/docs/reference/recipes/tool-composer) — chain into macro tools --- # Mandatory tool sandbox Source: https://www.agentskit.io/docs/reference/recipes/mandatory-sandbox > Enforce a sandbox policy across every tool the agent can call — allow-list, deny-list, require-sandbox, per-tool validators. Powerful tools (shell, filesystem, code execution) shouldn't be run raw. `createMandatorySandbox` wraps every `ToolDefinition` with a policy layer so a bad agent decision can't bypass the rules. Four knobs: - **allow** — explicit allow-list; everything else denied. - **deny** — specific tools are blocked. - **requireSandbox** — listed tools (or `'*'`) must run inside the shared sandbox tool, regardless of their own `execute`. - **validators** — synchronous per-tool argument checks. ## Install Ships with `@agentskit/sandbox`. ## Wire it up ```ts import { createMandatorySandbox, sandboxTool } from '@agentskit/sandbox' import { filesystem, shell, webSearch } from '@agentskit/tools' const policy = createMandatorySandbox({ sandbox: sandboxTool(), policy: { requireSandbox: ['shell'], deny: ['filesystem'], allow: ['shell', 'web_search', 'code_execution'], validators: { web_search: args => { if (typeof args.q !== 'string' || args.q.length > 200) { throw new Error('web_search requires a query ≤ 200 chars') } }, }, onPolicyEvent: e => logger.info('[policy]', e), }, }) const safeTools = [shell(), webSearch(), filesystem({ basePath })].map(t => policy.wrap(t)) const runtime = createRuntime({ adapter, tools: safeTools }) ``` ## How enforcement works - Denied / not-in-allow tools: the wrapper replaces `execute` with a thunk that throws. The runtime surfaces the error to the model rather than running anything. - Require-sandbox tools: the wrapper replaces `execute` with the sandbox tool's `execute`, so the original tool's body never runs. - Validators: run synchronously *before* execution; throw to abort. ## Dry-run `check(tool)` returns `{ allowed, mustSandbox, reason? }` without wrapping — useful for CI rules that fail the build when a new tool would be denied, or for admin dashboards that show the current policy effect. ## Pair with - [HITL approvals](/docs/reference/recipes/hitl-approvals) — require a human decision on top of the sandbox for the riskiest ops. - [Signed audit log](/docs/reference/recipes/audit-log) — record every allow/deny/run decision for SOC 2 evidence. - [Rate limiting](/docs/reference/recipes/rate-limiting) — cap how often any given tool can be invoked per user. ## See also - [Confirmation-gated tools](/docs/reference/recipes/confirmation-gated-tool) — per-call human approval. --- # MCP bridge (bidirectional) Source: https://www.agentskit.io/docs/reference/recipes/mcp-bridge > Consume any MCP server as AgentsKit tools, or expose your AgentsKit tools to MCP hosts — over stdio or any transport. Model Context Protocol (MCP) is the emerging open standard for connecting LLM hosts (Claude Desktop, Cursor, Zed, IDEs) to external tool servers. `@agentskit/tools/mcp` ships a minimal bidirectional bridge — consume MCP servers *as* AgentsKit tools, and expose your AgentsKit tools *to* any MCP host. This is a protocol subset: `initialize` + `tools/list` + `tools/call` over JSON-RPC 2.0. Full MCP (resources, prompts, sampling) is a follow-up. ## Install ```bash npm install @agentskit/tools ``` Transports are framework-agnostic — bring your own stdio, WebSocket, or SSE + POST. An in-memory transport pair ships for tests. ## Consume an MCP server ```ts import { spawn } from 'node:child_process' import { createMcpClient, createStdioTransport, toolsFromMcpClient, } from '@agentskit/tools/mcp' import { createRuntime } from '@agentskit/runtime' const child = spawn('my-mcp-server', ['--flag'], { stdio: ['pipe', 'pipe', 'inherit'] }) const transport = createStdioTransport(child) const client = createMcpClient({ transport }) await client.initialize() const tools = await toolsFromMcpClient(client) const runtime = createRuntime({ adapter, tools }) // Later: await client.close() ``` `toolsFromMcpClient` advertises every MCP tool as a native `ToolDefinition` — schemas pass through, errors propagate, results are flattened from the MCP `content[]` array into a single string. ## Publish AgentsKit tools as an MCP server ```ts import { createMcpServer, createStdioTransport } from '@agentskit/tools/mcp' import { webSearch, fetchUrl } from '@agentskit/tools' const transport = createStdioTransport(process as unknown as { stdin: typeof process.stdin stdout: typeof process.stdout on?: typeof process.on }) createMcpServer({ transport, tools: [webSearch(), fetchUrl()], serverInfo: { name: 'my-agentskit-mcp', version: '1.0.0' }, onEvent: e => console.error('[mcp]', e), }) ``` Your process now speaks MCP on stdin/stdout. Point Claude Desktop, Cursor, or any MCP host at the binary and your AgentsKit tools show up as first-class. ## Transports | Transport | Provider | |---|---| | `createStdioTransport(child)` | newline-delimited JSON over stdin/stdout | | `createInMemoryTransportPair()` | paired in-process transports — tests, in-process bridges | | Your own | implement the `McpTransport` contract (`send` + `onMessage` + optional `onClose` + `close`) — WebSocket, SSE + POST, etc. | ## See also - [Tool composer](/docs/reference/recipes/tool-composer) — chain N tools into one macro tool - [Mandatory sandbox](/docs/reference/recipes/mandatory-sandbox) — enforce policy on imported MCP tools - [More providers](/docs/reference/recipes/more-providers) --- # Scraping, voice, maps, browser agent Source: https://www.agentskit.io/docs/reference/recipes/more-integrations > Firecrawl + Jina Reader, OpenAI Images + ElevenLabs + Whisper + Deepgram, Nominatim + OpenWeatherMap + CoinGecko, and a BYO-Playwright browser agent. All under `@agentskit/tools/integrations`. Same pattern as S21 — focused `defineTool` factories + a bundle helper per provider. ## Scraping + parsing ```ts import { firecrawl, reader, documentParsers } from '@agentskit/tools/integrations' const tools = [ ...firecrawl({ apiKey: process.env.FIRECRAWL_API_KEY! }), ...reader({ apiKey: process.env.JINA_TOKEN }), ...documentParsers({ parsePdf: async bytes => { const { default: pdf } = await import('pdf-parse') const r = await pdf(Buffer.from(bytes)) return { text: r.text, pages: r.numpages } }, }), ] ``` - `firecrawl_scrape` / `firecrawl_crawl` — managed scraper with JS rendering. - `reader_fetch` — zero-dep Jina Reader wrapper; returns LLM-ready text. - `parse_pdf` / `parse_docx` / `parse_xlsx` — BYO parser functions so you pick the native-dep story. ## Image + voice ```ts import { openaiImages, elevenlabs, whisper, deepgram } from '@agentskit/tools/integrations' const tools = [ ...openaiImages({ apiKey: process.env.OPENAI_API_KEY!, model: 'gpt-image-1' }), ...elevenlabs({ apiKey: process.env.ELEVENLABS_API_KEY! }), ...whisper({ apiKey: process.env.OPENAI_API_KEY! }), ...deepgram({ apiKey: process.env.DEEPGRAM_API_KEY! }), ] ``` - `openai_image_generate` — text → image, returns URL or base64. - `elevenlabs_tts` — text → MPEG audio bytes (base64 in the result). - `whisper_transcribe` / `deepgram_transcribe` — audio URL → transcript. Binary outputs are base64-encoded so they pass safely through JSON tool results. Persist or stream them on the caller side. ## Maps / weather / finance ```ts import { maps, weather, coingecko } from '@agentskit/tools/integrations' const tools = [ ...maps({ userAgent: 'myapp/1.0 (contact@example.com)' }), ...weather({ apiKey: process.env.OPENWEATHERMAP_KEY! }), ...coingecko(), // works anonymously; add apiKey for pro ] ``` - `maps_geocode` / `maps_reverse_geocode` — OpenStreetMap Nominatim, free with a required user agent identifying your app. - `weather_current` — OpenWeatherMap current conditions. - `coingecko_price` / `coingecko_market_chart` — crypto prices + series. ## Browser agent (BYO Playwright) Bundling Playwright is a ~200 MB footgun. Instead, the browser agent takes a `BrowserPage` contract with six methods. Wire your own Playwright/Puppeteer/Chromium DevTools Protocol page into it. ```ts import { chromium } from 'playwright' import { browserAgent, type BrowserPage } from '@agentskit/tools/integrations' const browser = await chromium.launch() const raw = await browser.newPage() const page: BrowserPage = { goto: async url => { await raw.goto(url) }, click: async selector => { await raw.click(selector) }, fill: async (selector, value) => { await raw.fill(selector, value) }, textContent: async selector => (await raw.textContent(selector)) ?? '', screenshot: async () => (await raw.screenshot({ type: 'png' })).toString('base64'), waitForSelector: async (selector, options) => { await raw.waitForSelector(selector, { timeout: options?.timeoutMs }) }, } const tools = browserAgent({ page }) ``` Tools: `browser_goto`, `browser_click`, `browser_fill`, `browser_read`, `browser_wait_for`, `browser_screenshot`. Pair with [mandatory sandbox](/docs/reference/recipes/mandatory-sandbox) to keep every browser call behind your policy. ## See also - [Provider integrations](/docs/reference/recipes/integrations) — S21 set (GitHub, Slack, Stripe, Postgres, S3, ...) - [MCP bridge](/docs/reference/recipes/mcp-bridge) — expose any of these to an MCP host - [Tool composer](/docs/reference/recipes/tool-composer) --- # More provider adapters Source: https://www.agentskit.io/docs/reference/recipes/more-providers > Mistral, Cohere, Together, Groq, Fireworks, OpenRouter, Hugging Face, LM Studio, vLLM, llama.cpp — one line each. Every major OpenAI-compatible provider ships as a thin wrapper around the shared OpenAI adapter. Pick one, pass your `apiKey` + `model`, done. Override `baseUrl` for self-hosted variants or regional endpoints. ## Install ```bash npm install @agentskit/adapters ``` ## Hosted providers ```ts import { mistral, cohere, together, groq, fireworks, openrouter, huggingface } from '@agentskit/adapters' const a = mistral({ apiKey: process.env.MISTRAL_API_KEY!, model: 'mistral-large-latest' }) const b = cohere({ apiKey: process.env.COHERE_API_KEY!, model: 'command-r-plus' }) const c = together({ apiKey: process.env.TOGETHER_API_KEY!, model: 'meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo' }) const d = groq({ apiKey: process.env.GROQ_API_KEY!, model: 'llama-3.3-70b-versatile' }) const e = fireworks({ apiKey: process.env.FIREWORKS_API_KEY!, model: 'accounts/fireworks/models/qwen2p5-72b-instruct' }) const f = openrouter({ apiKey: process.env.OPENROUTER_API_KEY!, model: 'anthropic/claude-sonnet-4-6' }) const g = huggingface({ apiKey: process.env.HF_TOKEN!, model: 'meta-llama/Meta-Llama-3.1-8B-Instruct' }) ``` | Adapter | Default `baseUrl` | |---------|-------------------| | `mistral` | `https://api.mistral.ai/v1` | | `cohere` | `https://api.cohere.com/compatibility/v1` | | `together` | `https://api.together.xyz/v1` | | `groq` | `https://api.groq.com/openai/v1` | | `fireworks` | `https://api.fireworks.ai/inference/v1` | | `openrouter` | `https://openrouter.ai/api/v1` | | `huggingface` | `https://router.huggingface.co/v1` | ## Local runtimes ```ts import { ollama, lmstudio, vllm, llamacpp } from '@agentskit/adapters' const local1 = ollama({ model: 'llama3.1' }) // http://localhost:11434 const local2 = lmstudio({ apiKey: 'na', model: 'qwen2.5' }) // http://localhost:1234/v1 const local3 = vllm({ apiKey: 'na', model: 'mistral-7b' }) // http://localhost:8000/v1 const local4 = llamacpp({ apiKey: 'na', model: 'llama-3' }) // http://localhost:8080/v1 ``` All four talk OpenAI-compatible APIs, so the router / ensemble / fallback / replay primitives all work unchanged. ## Override the URL Regional endpoints, self-hosted deployments, gateways — pass `baseUrl`: ```ts mistral({ apiKey, model: 'mistral-large', baseUrl: 'https://mistral-eu.mycompany.com/v1' }) ``` ## See also - [Adapter router](/docs/reference/recipes/adapter-router) — auto-pick among them - [Fallback chain](/docs/reference/recipes/fallback-chain) — graceful degradation - [Speculative execution](/docs/reference/recipes/speculative-execution) — race across providers --- # Multi-agent topologies Source: https://www.agentskit.io/docs/reference/recipes/multi-agent-topologies > Ready-made supervisor, swarm, hierarchical, and blackboard builders — four ways to combine agents into one. Picking a topology is half the battle with multi-agent systems. `@agentskit/runtime` ships the four patterns that actually show up in production; each takes `AgentHandle`s (anything with a `name` + `run(task)` method) and returns a new `AgentHandle` you can plug back into the rest of your system. ## Install Ships with `@agentskit/runtime`. ## Supervisor A planner agent delegates to workers, then synthesizes. Good for "decompose → delegate → merge" patterns. ```ts import { supervisor } from '@agentskit/runtime' const team = supervisor({ supervisor: plannerAgent, workers: [researcherAgent, coderAgent], maxRounds: 2, route: (task, workers) => (/code/i.test(task) ? workers[1]! : workers[0]!), }) await team.run('Research quantum sort, then implement it in Python.') ``` ## Swarm Every member sees the same task, runs in parallel, results get merged. Good for ensembling answers, voting, or "fan out and pick the best." ```ts import { swarm } from '@agentskit/runtime' const team = swarm({ members: [anthropicAgent, openAiAgent, geminiAgent], timeoutMs: 30_000, merge: results => results.map(r => r.output).join('\n\n---\n\n'), }) ``` One or more members can fail — the merger still runs as long as any member returned. ## Hierarchical A routing tree. Start at the root, descend as long as a child matches (by tag or custom route), then execute the leaf. ```ts import { hierarchical } from '@agentskit/runtime' const tree = hierarchical({ root: { agent: triageAgent, children: [ { agent: billingAgent, tags: ['refund', 'invoice', 'billing'] }, { agent: technicalAgent, tags: ['bug', 'error', 'crash'] }, ], }, maxDepth: 3, }) ``` ## Blackboard Every agent reads and writes a shared scratchpad. Iterate until `isDone` says stop. Good for planner + critic loops or collaborative drafting. ```ts import { blackboard } from '@agentskit/runtime' const team = blackboard({ agents: [plannerAgent, coderAgent, criticAgent], maxIterations: 3, isDone: board => board.includes('FINAL OUTPUT:'), }) ``` ## Observing Every topology accepts an `onEvent` observer: ```ts supervisor({ supervisor, workers, onEvent: e => logger.info('[topo]', e.topology, e.phase, e.agent), }) ``` Phases: `dispatch` / `agent:start` / `agent:end` / `merge` / `done`. ## See also - [Durable execution](/docs/reference/recipes/durable-execution) — wrap the whole topology in a step log. - [Background agents](/docs/reference/recipes/background-agents) — run a topology on a schedule. --- # Unified multi-modal Source: https://www.agentskit.io/docs/reference/recipes/multi-modal > One API for text, image, audio, video, and file inputs — regardless of provider. Every provider has its own multi-modal shape. OpenAI wants `{ type: 'image_url', image_url: {...} }`, Anthropic wants `{ type: 'image', source: {...} }`, Gemini wants parts-with-inline- data. `@agentskit/core` provides a provider-neutral `ContentPart` model — adapters that understand a modality read the parts, the rest fall back to a text projection. ## Install Built into `@agentskit/core`. ## Build a multi-modal message ```ts import { textPart, imagePart, audioPart, filePart, partsToText, } from '@agentskit/core' import type { Message } from '@agentskit/core' const parts = [ textPart('What is in this screenshot?'), imagePart('https://cdn.example.com/screenshot.png', { detail: 'high', mimeType: 'image/png' }), ] const message: Message = { id: crypto.randomUUID(), role: 'user', content: partsToText(parts), // text projection: "What is...\n[image: ...]" parts, // adapters that support vision read this status: 'complete', createdAt: new Date(), } ``` ## Part kinds | Builder | `type` | Notes | |---------|--------|-------| | `textPart(text)` | `'text'` | Plain text segment | | `imagePart(src, { mimeType?, detail? })` | `'image'` | Data URL, http(s), or provider-hosted id | | `audioPart(src, { durationSec? })` | `'audio'` | | | `videoPart(src, { durationSec? })` | `'video'` | | | `filePart(src, { filename? })` | `'file'` | PDF, CSV, arbitrary binary | ## In an adapter A vision-aware adapter reads `msg.parts` and maps each entry to its provider's shape. A text-only adapter keeps reading `msg.content` and sees a safe projection like `"caption\n[image: pic.png]"`. ```ts import { normalizeContent, filterParts } from '@agentskit/core' function toOpenAIMessage(m: Message) { const { parts } = normalizeContent(m.content, m.parts) return { role: m.role, content: parts.map(p => { if (p.type === 'text') return { type: 'text', text: p.text } if (p.type === 'image') return { type: 'image_url', image_url: { url: p.source, detail: p.detail } } return { type: 'text', text: `[${p.type}]` } }), } } // Quickly grab every attached image: const images = filterParts(parts, 'image') ``` ## See also - [Custom adapter](/docs/reference/recipes/custom-adapter) - [PDF Q&A](/docs/reference/recipes/pdf-qa) --- # Open specs — A2A, Manifest, Eval Format Source: https://www.agentskit.io/docs/reference/recipes/open-specs > Three small, versioned specs so agents, skill packs, and eval datasets travel across tools. Three open specs ship as typed subpaths of `@agentskit/core`. Every spec is a stable JSON shape + a validator — no runtime behavior coupling. ## `@agentskit/core/a2a` — Agent-to-Agent Protocol JSON-RPC 2.0 methods for one agent to invoke another. | Method | Purpose | |---|---| | `agent/card` | Discover an agent's skills + schemas | | `task/invoke` | Run a skill | | `task/cancel` | Stop a running task | | `task/approve` | Deliver a HITL decision | | `task/status` | Stream progress / partial output | ```ts import { validateAgentCard, A2A_PROTOCOL_VERSION } from '@agentskit/core/a2a' ``` Agent Card shape is compatible with directory-style marketplaces — publish the JSON, let any A2A client discover + invoke. ## `@agentskit/core/manifest` — Skill & Tool Manifest A packaging format for distributing skills + tools together. Tool entries mirror MCP's `inputSchema`, so a manifest can round-trip through an MCP server without information loss. ```ts import { validateManifest, MANIFEST_VERSION } from '@agentskit/core/manifest' ``` ## `@agentskit/core/eval-format` — Open Eval Format Portable eval dataset + run-result payload. ```ts import { validateEvalSuite, validateEvalRunResult, matchesExpectation, EVAL_FORMAT_VERSION, } from '@agentskit/core/eval-format' ``` Expectation kinds: literal `contains`, regex (`body` + `flags`), normalized equality, semantic similarity (runner-provided embedder). One dataset → many runners (AgentsKit, custom in-house, CI evals). ## Why specs live in `@agentskit/core` - Types travel with the framework — no version drift between the spec doc and the TS definitions. - Zero runtime cost: each spec is its own subpath, nothing goes into the main bundle. - Adding a new spec is one tsup entry + one package.json export. ## See also - [Evals in CI](/docs/reference/recipes/evals-ci) — plug `matchesExpectation` into CI gates - [MCP bridge](/docs/reference/recipes/mcp-bridge) - [Skill marketplace](/docs/reference/recipes/skill-marketplace) --- # PDF Q&A Source: https://www.agentskit.io/docs/reference/recipes/pdf-qa > Ask questions about a local PDF file. Extract, chunk, embed, retrieve, answer. A CLI tool that lets you ask questions about any PDF. Useful for research papers, contracts, manuals. ## Install ```bash npm install @agentskit/runtime @agentskit/adapters @agentskit/rag @agentskit/memory pdf-parse ``` ## The script ```ts title="ask-pdf.ts" import { createRuntime } from '@agentskit/runtime' import { openai, openaiEmbedder } from '@agentskit/adapters' import { createRAG } from '@agentskit/rag' import { fileVectorMemory } from '@agentskit/memory' import { readFileSync } from 'node:fs' import pdfParse from 'pdf-parse' const [pdfPath, ...questionParts] = process.argv.slice(2) const question = questionParts.join(' ') if (!pdfPath || !question) { console.error('Usage: tsx ask-pdf.ts ""') process.exit(1) } // 1. Extract text const data = await pdfParse(readFileSync(pdfPath)) const fullText = data.text // 2. Chunk (simple: paragraphs of 500-ish chars) function chunk(text: string, size = 500): string[] { const paragraphs = text.split(/\n\n+/) const chunks: string[] = [] let current = '' for (const p of paragraphs) { if ((current + p).length > size) { if (current) chunks.push(current) current = p } else { current += '\n\n' + p } } if (current) chunks.push(current) return chunks } // 3. Index in-memory (per-PDF, ephemeral) const rag = createRAG({ store: fileVectorMemory({ path: `./.cache/${pdfPath}.embeddings.json` }), embed: openaiEmbedder({ apiKey: KEY, model: 'text-embedding-3-small' }), topK: 4, }) await rag.ingest( chunk(fullText).map((content, i) => ({ id: `chunk-${i}`, content, source: `${pdfPath}#${i}`, })), ) // 4. Ask const runtime = createRuntime({ adapter: openai({ apiKey: KEY, model: 'gpt-4o-mini' }), retriever: rag, systemPrompt: 'Answer using only the provided document excerpts. ' + 'Cite passages by their source index. Say "not found in the document" if absent.', }) const result = await runtime.run(question) console.log(result.content) ``` ## Run it ```bash npx tsx ask-pdf.ts paper.pdf "What is the main contribution of this work?" ``` ## Why this works - **Per-PDF cache** at `./.cache/.embeddings.json` — second run is instant - **Source citations** because `RetrievedDocument.source` makes it into the prompt - **No memory** — each invocation is independent; perfect for one-shot Q&A ## Tighten the recipe - **Smarter chunking**: respect headings via a markdown converter (e.g. `mammoth` for DOCX) - **Multi-file**: pass `--dir ./papers` and ingest every PDF in the folder - **Citation linking**: convert `chunk-7` back to a page number with `pdf-parse`'s page metadata ## Related - [Recipe: Chat with RAG](./rag-chat) — same idea with a UI - [Concepts: Retriever](../concepts/retriever) --- # Persistent memory across sessions Source: https://www.agentskit.io/docs/reference/recipes/persistent-memory > A chat that remembers yesterday's conversation. SQLite-backed. Works in 5 lines. A chat that picks up where you left off — across processes, deploys, machines. ## Install ```bash npm install @agentskit/runtime @agentskit/adapters @agentskit/memory ``` ## The chat ```ts title="chat.ts" import { createRuntime } from '@agentskit/runtime' import { openai } from '@agentskit/adapters' import { sqliteChatMemory } from '@agentskit/memory' const runtime = createRuntime({ adapter: openai({ apiKey: KEY, model: 'gpt-4o-mini' }), memory: sqliteChatMemory({ path: './sessions/user-42.db' }), }) const reply = await runtime.run(process.argv.slice(2).join(' ')) console.log(reply.content) ``` ## Run it ```bash npx tsx chat.ts "My favorite framework is AgentsKit. Remember that." # > Got it. # Later, in a different process: npx tsx chat.ts "What's my favorite framework?" # > AgentsKit. ``` ## What's happening - `sqliteChatMemory({ path })` returns a `ChatMemory` (per ADR 0003) - Runtime calls `memory.load()` at the start of `run()` → conversation rehydrates - After `run()` succeeds, runtime calls `memory.save(messages)` with the full updated history - **Failed or aborted runs do NOT save** — atomicity is built into the contract ## Per-user, per-channel, per-X A new memory instance per scope: ```ts function runtimeFor(userId: string) { return createRuntime({ adapter, memory: sqliteChatMemory({ path: `./sessions/${userId}.db` }), }) } ``` Cheap because `createRuntime` is config-only (RT1). ## Migrate to Redis when you outgrow SQLite The contract is identical: ```ts import { redisChatMemory } from '@agentskit/memory' memory: redisChatMemory({ url: process.env.REDIS_URL!, key: `session:${userId}`, }), ``` No code in your runtime changes. That's the plug-and-play promise (Manifesto principle 2). ## Tighten the recipe - **Hash long histories** — wrap `memory` with a proxy that summarizes when it grows beyond N tokens (Phase-2 work, #153) - **TTL** — use Redis `EXPIRE` for ephemeral sessions - **Encrypt at rest** — wrap with an encrypting proxy; the contract doesn't change ## Related - [Concepts: Memory](../concepts/memory) — split contracts, replace-all save - [Recipe: Discord bot](./discord-bot) — same pattern, per-channel --- # Personalization Source: https://www.agentskit.io/docs/reference/recipes/personalization > Persisted user profile that conditions every agent response. A user's preferences shouldn't live in a single conversation. The personalization store is a `get` / `set` / `merge` contract over a `{ subjectId, traits, updatedAt }` profile — conditioning happens by prepending the rendered profile to the system prompt. ## Install Ships with `@agentskit/memory`. ## Use ```ts import { createInMemoryPersonalization, renderProfileContext, } from '@agentskit/memory' const profiles = createInMemoryPersonalization() await profiles.merge('user-42', { preferredLanguage: 'pt-BR', tone: 'concise', dietaryConstraints: ['vegetarian'], }) const profile = await profiles.get('user-42') const systemExtras = renderProfileContext(profile) const systemPrompt = `You are a helpful assistant.\n\n${systemExtras}` ``` `renderProfileContext` skips null / empty entries and returns `''` when the profile has nothing to add — safe to concatenate always. ## Update from the agent Capture preferences automatically via a tool the model can call: ```ts defineTool({ name: 'update_profile', description: "Save a new fact about the current user's preferences.", schema: { type: 'object', properties: { key: { type: 'string' }, value: { type: 'string' } }, required: ['key', 'value'], } as const, async execute({ key, value }, ctx) { await profiles.merge(ctx.call.args.subjectId as string, { [key]: value }) return 'saved' }, }) ``` ## See also - [Graph memory](/docs/reference/recipes/graph-memory) — relationships, not just scalars - [HITL approvals](/docs/reference/recipes/hitl-approvals) — gate updates that change sensitive preferences --- # PII redaction Source: https://www.agentskit.io/docs/reference/recipes/pii-redaction > Strip emails, phone numbers, SSNs, and other PII from messages before they hit the model or your logs. Leaking user PII into an LLM request is the security incident no one plans for. `@agentskit/core/security` ships a tiny regex-based redactor that handles the common patterns (email, phone, SSN, IPv4, credit-card, UUID) and lets you add your own rules. Regex is not enough for high-stakes use — layer a model-based PII detector on top for production. But the 20-line version catches the low-hanging incidents *today*. ## Install Built into `@agentskit/core` via subpath (no main-bundle weight). ```ts import { createPIIRedactor } from '@agentskit/core/security' ``` ## Scrub a string ```ts const redactor = createPIIRedactor() const { value, hits } = redactor.redact( 'Contact alice@corp.com at +1 555-123-4567 — SSN 123-45-6789', ) console.log(value) // → 'Contact [REDACTED_EMAIL] at [REDACTED_PHONE] — SSN [REDACTED_SSN]' console.log(hits) // → [{ rule: 'email', count: 1 }, ...] ``` ## Scrub a whole conversation ```ts import type { Message } from '@agentskit/core' const { value: safeMessages, hits } = redactor.redactMessages(messages) ``` Pipe `safeMessages` into the adapter; log `hits` so you know which rules fired without having to log the payload. ## Custom rules ```ts const redactor = createPIIRedactor({ rules: [ { name: 'api-key', pattern: /sk-[A-Za-z0-9]{32,}/g, replacer: '[REDACTED_KEY]' }, { name: 'iban', pattern: /[A-Z]{2}\d{2}[A-Z0-9]{11,30}/g, replacer: '[REDACTED_IBAN]' }, ], }) ``` Pass `DEFAULT_PII_RULES` in to extend rather than replace the defaults. ```ts import { DEFAULT_PII_RULES, createPIIRedactor } from '@agentskit/core/security' createPIIRedactor({ rules: [...DEFAULT_PII_RULES, myCustomRule], }) ``` ## See also - [Prompt injection detector](/docs/reference/recipes/prompt-injection) - [Signed audit log](/docs/reference/recipes/audit-log) --- # Progressive tool calls Source: https://www.agentskit.io/docs/reference/recipes/progressive-tool-calls > Start executing a tool before the model finishes streaming its arguments. A common latency win: the model is still typing JSON args for a `search(query, limit, filters)` call, but you already have `query` — and `query` is the only field the tool actually needs to start working. `@agentskit/core` ships two primitives for this pattern. ## Install Built into `@agentskit/core`. ## Parse args progressively `createProgressiveArgParser` consumes JSON text in arbitrary chunks and fires an event per top-level field whose value is syntactically complete. ```ts import { createProgressiveArgParser } from '@agentskit/core' const p = createProgressiveArgParser() p.push('{"query"') // -> [] p.push(':"pirates"') // -> [{ field: 'query', value: 'pirates' }] p.push(', "limit": 10}') // -> [{ field: 'limit', value: 10 }] p.end() ``` It handles escaped strings and nested objects/arrays, which are parsed atomically when their enclosing top-level field closes. ## Fire a tool early `executeToolProgressively` wires the parser into a tool. By default it starts executing as soon as the **first** field arrives; pass `triggerFields` to require specific fields before kicking off. ```ts import { executeToolProgressively, defineTool } from '@agentskit/core' const search = defineTool({ name: 'search', schema: { type: 'object', properties: { query: { type: 'string' }, limit: { type: 'number' } } } as const, execute: async ({ query, limit }) => { return fetch(`/api/search?q=${query}&limit=${limit ?? 20}`).then(r => r.json()) }, }) async function* argStream() { yield '{"query":"open source"' // ...LLM still generating... yield ', "limit": 5}' } const { execution, finalArgs } = executeToolProgressively(search, argStream(), { messages: [], callId: 'call_1', }, { triggerFields: ['query'] }) const result = await execution ``` - `execution` resolves once the tool returns. - `finalArgs` reflects the complete object after the stream closes. ## See also - [Custom adapter](/docs/reference/recipes/custom-adapter) — emit `tool_call` chunks with partial args - [Deterministic replay](/docs/reference/recipes/deterministic-replay) — record progressive runs for debugging --- # Prompt diff Source: https://www.agentskit.io/docs/reference/recipes/prompt-diff > Git blame for prompts — find which prompt change is responsible for an output change. You shipped a prompt tweak. A week later the outputs look different, and you have no idea which line did it. `@agentskit/eval/diff` solves that: line-level diff + heuristic attribution that points at the prompt lines most likely responsible for an output shift. ## Install ```bash npm install -D @agentskit/eval ``` ## Diff two prompt versions ```ts title="compare-prompts.ts" import { promptDiff, formatDiff } from '@agentskit/eval/diff' const diff = promptDiff(oldPrompt, newPrompt) console.log(formatDiff(diff)) // You are a helpful assistant. // - Answer briefly. // + Answer with pirate slang. ``` Each entry in `diff.lines` is `{ op: 'equal' | 'add' | 'remove', lineNo, content }`. Totals (`added`, `removed`, `changed`) are on the result. ## Attribute an output change Given the old/new prompt **and** the old/new output, attribute which changed prompt lines probably caused the output shift. Simple token overlap — good enough to rank suspects. ```ts import { attributePromptChange } from '@agentskit/eval/diff' const report = attributePromptChange({ oldPrompt: 'You are a helpful assistant.\nAnswer briefly.', newPrompt: 'You are a helpful assistant.\nAnswer with pirate slang.', oldOutput: 'Hello, how can I help?', newOutput: 'Ahoy matey, what be yer query?', }) console.log(report.suspectLines) // [{ op: 'add', lineNo: 2, content: 'Answer with pirate slang.' }] console.log(report.score) // 1.0 — every changed line overlaps the output delta ``` ## Pair with replay + snapshots The workflow: 1. **Record** the old session with [`createRecordingAdapter`](/docs/reference/recipes/deterministic-replay). 2. **Tweak** the prompt, generate a new output. 3. **Snapshot** the new output with [`matchPromptSnapshot`](/docs/reference/recipes/prompt-snapshots). If it matches — you're done. 4. If it doesn't, **attribute** with `attributePromptChange` to see which tweak is load-bearing. You now have the LLM-equivalent of `git bisect` for prompts. ## See also - [Deterministic replay](/docs/reference/recipes/deterministic-replay) - [Prompt snapshot testing](/docs/reference/recipes/prompt-snapshots) --- # A/B prompts with feature flags Source: https://www.agentskit.io/docs/reference/recipes/prompt-experiments > Ship multiple prompts, route users deterministically, measure which wins. Picking a new prompt is a product decision. Ship the old and new versions side-by-side, route each user deterministically, and let your analytics decide the winner. `@agentskit/core/prompt-experiments` is the 1 KB glue that wires any feature-flag provider (PostHog, GrowthBook, Unleash, custom) to a typed A/B prompt picker with sticky-hash fallback. ## Install Built into `@agentskit/core`. ```ts import { createPromptExperiment, flagResolver, } from '@agentskit/core/prompt-experiments' ``` ## Sticky-hash baseline (no flag provider) Good for smoke tests, demos, or when you haven't picked a flag service yet. Same `subjectId` always maps to the same variant. ```ts import { createPromptExperiment, stickyResolver } from '@agentskit/core/prompt-experiments' const exp = createPromptExperiment({ name: 'support-tone', variants: [ { id: 'v1', prompt: 'Be concise and formal.', weight: 1 }, { id: 'v2', prompt: 'Be warm and playful.', weight: 1 }, ], resolve: stickyResolver(), onExposure: d => analytics.track('prompt-exposure', d), }) const { prompt, variantId } = await exp.pick({ subjectId: currentUser.id }) ``` ## Plug in your flag provider `flagResolver` wraps any `(name, context) => variantId` function — PostHog's `getFeatureFlagPayload`, GrowthBook's `getFeatureValue`, Unleash, LaunchDarkly. If the provider returns an unknown variant (rollout paused, flag misconfigured, network error), the picker falls back to the sticky resolver so users still see *some* prompt. ```ts import posthog from 'posthog-node' const exp = createPromptExperiment({ name: 'support-tone', variants: [ { id: 'control', prompt: 'Be concise and formal.' }, { id: 'playful', prompt: 'Be warm and playful.' }, ], resolve: flagResolver(async (name, ctx) => { return posthog.getFeatureFlag(name, ctx.subjectId ?? 'anon') as string }, 'support-tone'), onExposure: d => { posthog.capture({ distinctId: d.subjectId ?? 'anon', event: '$feature_flag_called', properties: { $feature_flag: d.name, $feature_flag_response: d.variantId, fallback: d.fallback }, }) }, }) ``` ## Decision shape ```ts { name: 'support-tone', variantId: 'playful', prompt: 'Be warm and playful.', fallback: false, // true if the custom resolver failed } ``` Every call hits `onExposure`, so your analytics pipeline can attribute downstream events (conversions, satisfaction, regenerations) to the variant. ## Multiple variants per property `prompt` is typed on the variant so you can A/B whole message structures, not just strings: ```ts createPromptExperiment<{ system: string; temperature: number }>({ name: 'agent-config', variants: [ { id: 'cold', prompt: { system: 'You are precise.', temperature: 0 } }, { id: 'warm', prompt: { system: 'You are warm.', temperature: 0.7 } }, ], resolve: flagResolver(getVariant, 'agent-config'), }) ``` ## See also - [Eval suite](/docs/reference/recipes/eval-suite) — score each variant quantitatively - [Evals in CI](/docs/reference/recipes/evals-ci) — gate the winner --- # Prompt injection detector Source: https://www.agentskit.io/docs/reference/recipes/prompt-injection > Score incoming text for injection attempts — heuristics + optional model classifier (Llama Guard, Rebuff). Prompt injection is user input that tries to rewrite the agent's instructions. `createInjectionDetector` gives you a two-layer defense: cheap regex heuristics for the common patterns, and a pluggable model classifier for the subtle ones. The verdict is the max of both signals. ## Install ```ts import { createInjectionDetector } from '@agentskit/core/security' ``` ## Heuristic-only (fast, free) ```ts const detector = createInjectionDetector() const verdict = await detector.check(userMessage) if (verdict.blocked) { audit.append({ actor: userId, action: 'injection_blocked', payload: verdict }) return 'Sorry, that request was blocked.' } ``` Default heuristics catch the usual suspects: "ignore previous instructions", "you are now a...", system-prompt leakage, developer mode, policy bypass phrasing, tool-call smuggling, role confusion. ## Layer a model classifier (Llama Guard, Prompt Guard, Rebuff) ```ts const detector = createInjectionDetector({ threshold: 0.7, classifier: async input => { const res = await fetch('https://api.example.com/llama-guard', { method: 'POST', body: JSON.stringify({ text: input }), headers: { 'content-type': 'application/json', authorization: `Bearer ${process.env.LG_KEY}` }, }) const { unsafe_score } = (await res.json()) as { unsafe_score: number } return unsafe_score }, }) ``` Classifier errors are swallowed — you degrade to heuristic-only instead of rejecting all traffic when the upstream flakes. ## Verdict shape ```ts { score: number, // max(heuristic, classifier) blocked: boolean, // score >= threshold hits: [{ name, weight }], // heuristic hits source: 'heuristic' | 'hybrid', } ``` ## Add your own heuristics ```ts import { DEFAULT_INJECTION_HEURISTICS, createInjectionDetector } from '@agentskit/core/security' createInjectionDetector({ heuristics: [ ...DEFAULT_INJECTION_HEURISTICS, { name: 'off-topic-divert', pattern: /let['’]s talk about something else/i, weight: 0.5 }, ], }) ``` ## See also - [PII redaction](/docs/reference/recipes/pii-redaction) - [Rate limiting](/docs/reference/recipes/rate-limiting) --- # Prompt snapshot testing Source: https://www.agentskit.io/docs/reference/recipes/prompt-snapshots > Jest-style snapshot tests for prompts, with semantic tolerance so small wording drift doesn't break CI. Prompts are code. They should be reviewed in PRs and tested like code. `@agentskit/eval/snapshot` gives you snapshot testing — the same "write once, assert next time" workflow as Jest — with one twist that matters for LLM outputs: **semantic tolerance**. Exact-match snapshots are too brittle for model outputs. Normalized and similarity-based modes let you assert intent without pinning every comma. ## Install ```bash npm install -D @agentskit/eval ``` ## Quick start ```ts title="prompts.test.ts" import { matchPromptSnapshot } from '@agentskit/eval/snapshot' import { expect, it } from 'vitest' it('reviewer skill system prompt stays stable', async () => { const actual = buildReviewerSystemPrompt({ language: 'typescript' }) const result = await matchPromptSnapshot(actual, './__snapshots__/reviewer.snap.md') expect(result.matched).toBe(true) }) ``` First run creates the snapshot file. Next run compares. Update snapshots on purpose with `UPDATE_SNAPSHOTS=1 vitest` or `{ update: true }`. ## Matching modes | Mode | What matches | Use for | |------|--------------|---------| | `{ kind: 'exact' }` *(default)* | Byte-for-byte | Source-of-truth prompt templates | | `{ kind: 'normalized' }` | Case + punctuation + whitespace ignored | Prompts with cosmetic drift | | `{ kind: 'similarity', threshold }` | Jaccard token similarity ≥ threshold | LLM-generated prompts or summaries | | `{ kind: 'similarity', threshold, embed }` | Cosine of embeddings ≥ threshold | Full semantic assertions | ```ts await matchPromptSnapshot(actual, path, { mode: { kind: 'similarity', threshold: 0.85 }, }) ``` ## Embedding-based snapshots Plug in any embedding function — OpenAI, local, whatever — to compare snapshots by meaning instead of tokens. ```ts import { OpenAI } from 'openai' const openai = new OpenAI() async function embed(text: string) { const r = await openai.embeddings.create({ model: 'text-embedding-3-small', input: text }) return r.data[0].embedding } await matchPromptSnapshot(output, './__snapshots__/answer.snap.txt', { mode: { kind: 'similarity', threshold: 0.9, embed }, }) ``` ## Low-level primitives If you're building your own harness, the comparison logic is exposed: ```ts import { comparePrompt, jaccard, cosine, normalize } from '@agentskit/eval/snapshot' await comparePrompt('hello world', 'hello, world!', { kind: 'normalized' }) // => { matched: true, reason: 'normalized match', ... } ``` ## See also - [Deterministic replay](/docs/reference/recipes/deterministic-replay) — lock the whole session - [Prompt diff](/docs/reference/recipes/prompt-diff) — see exactly what changed --- # Chat with RAG Source: https://www.agentskit.io/docs/reference/recipes/rag-chat > A streaming React chat that answers from your own documents. Vector store, embeddings, retrieval, hooked up in 30 lines. A working chat UI grounded in your own content. The model answers using whatever docs you ingest — nothing else. ## Install ```bash npm install @agentskit/react @agentskit/adapters @agentskit/rag @agentskit/memory @agentskit/runtime ``` ## Index your docs (one-time) ```ts title="scripts/ingest.ts" import { createRAG } from '@agentskit/rag' import { fileVectorMemory } from '@agentskit/memory' import { openaiEmbedder } from '@agentskit/adapters' import { readFileSync, readdirSync } from 'node:fs' import { join } from 'node:path' const rag = createRAG({ store: fileVectorMemory({ path: './embeddings.json' }), embed: openaiEmbedder({ apiKey: KEY, model: 'text-embedding-3-small' }), }) const docs = readdirSync('./content').map(name => ({ id: name, content: readFileSync(join('./content', name), 'utf8'), source: name, })) await rag.ingest(docs) console.log(`Indexed ${docs.length} documents.`) ``` Run once: `npx tsx scripts/ingest.ts`. ## The chat ```tsx title="app/chat.tsx" 'use client' import { useChat, ChatContainer, Message, InputBar } from '@agentskit/react' import { openai } from '@agentskit/adapters' import { createRAG } from '@agentskit/rag' import { fileVectorMemory } from '@agentskit/memory' import { openaiEmbedder } from '@agentskit/adapters' import '@agentskit/react/theme' const rag = createRAG({ store: fileVectorMemory({ path: './embeddings.json' }), embed: openaiEmbedder({ apiKey: KEY, model: 'text-embedding-3-small' }), }) export default function Chat() { const chat = useChat({ adapter: openai({ apiKey: KEY, model: 'gpt-4o' }), retriever: rag, systemPrompt: 'Answer using only the provided context. If unsure, say so.', }) return ( {chat.messages.map(m => )} ) } ``` The `retriever` option is enough — `useChat` calls `retrieve()` once per turn and feeds the documents into the system prompt automatically. ## Verify Ask a question that's in your indexed docs. Then ask one that isn't — the model should say "I don't have information on that." ## Tighten the recipe - **Cite sources**: each `RetrievedDocument` has a `source` field. Render it under each assistant message. - **Tune retrieval**: pass `topK` and `threshold` to `createRAG` to control how many docs reach the model. - **Re-rank**: wrap `rag` in a composite retriever that calls a reranking model. See [Retriever](../concepts/retriever). - **Hot-reload index**: replace `fileVectorMemory` with `pgvector` or another backend if you index frequently. ## Related - [Concepts: Memory](../concepts/memory) — ChatMemory vs VectorMemory - [Concepts: Retriever](../concepts/retriever) — composing retrievers --- # RAG reranking + hybrid search Source: https://www.agentskit.io/docs/reference/recipes/rag-reranking > Wrap your vector retriever with BM25 hybrid scoring and pluggable rerankers (Cohere Rerank, BGE, etc.). Vector search is fast, but often misses exact-keyword matches and ranks weakly on specifics. Two additions to `@agentskit/rag` fix both: `createHybridRetriever` merges BM25 with vector scores, and `createRerankedRetriever` runs any external reranker over the top-N candidates. ## Install Ships with `@agentskit/rag`. ## Reranked retrieval (Cohere / BGE / BM25) ```ts import { createRerankedRetriever, createRAG } from '@agentskit/rag' import { fileVectorMemory } from '@agentskit/memory' import { openaiEmbedder } from '@agentskit/adapters' const base = createRAG({ embed: openaiEmbedder({ apiKey: process.env.OPENAI_API_KEY! }), store: fileVectorMemory({ path: './kb.vectors.json' }), topK: 20, }) const reranked = createRerankedRetriever(base, { candidatePool: 20, topK: 5, rerank: async ({ query, documents }) => { const res = await fetch('https://api.cohere.ai/v1/rerank', { method: 'POST', headers: { 'authorization': `Bearer ${process.env.COHERE_API_KEY}`, 'content-type': 'application/json' }, body: JSON.stringify({ model: 'rerank-english-v3.0', query, documents: documents.map(d => d.content), }), }) const data = await res.json() return data.results.map((r: { index: number; relevance_score: number }) => ({ ...documents[r.index], score: r.relevance_score, })) }, }) const hits = await reranked.retrieve({ query: 'how do refunds work?', messages: [] }) ``` No reranker function? The default is BM25 — good baseline, zero deps. ## Hybrid vector + BM25 Great when users mix exact product names or SKUs with fuzzy intent: ```ts import { createHybridRetriever } from '@agentskit/rag' const hybrid = createHybridRetriever(base, { vectorWeight: 0.6, bm25Weight: 0.4, topK: 5, }) ``` Scores are normalized to `[0, 1]` within the candidate set before mixing, so the weights behave as you'd expect. ## BM25 standalone If you need a pure-keyword pass somewhere else in the stack: ```ts import { bm25Score } from '@agentskit/rag' const ranked = bm25Score('refund policy', documents, { k1: 1.5, b: 0.75 }) ``` ## See also - [RAG chat](/docs/reference/recipes/rag-chat) — wire a retriever into a runtime - [PDF Q&A](/docs/reference/recipes/pdf-qa) --- # Rate limiting Source: https://www.agentskit.io/docs/reference/recipes/rate-limiting > Token-bucket rate limits keyed by user / IP / API key with per-tier bucket config. `createRateLimiter` is a drop-in token-bucket limiter. Pick the key (user id, IP, API key) and the bucket (per-tier capacity + refill) — everything else is a single `check(context)` call per request. In-memory by default — good for single-process services. Swap for a Redis-backed implementation with the same contract when you go multi-worker. ## Install ```ts import { createRateLimiter } from '@agentskit/core/security' ``` ## Basic usage ```ts const limiter = createRateLimiter<{ userId: string }>({ keyOf: ctx => ctx.userId, buckets: { default: { capacity: 60, refill: 60, windowMs: 60_000 }, // 60 req/min }, }) const decision = limiter.check({ userId: req.user.id }) if (!decision.allowed) { res.status(429).set('retry-after', Math.ceil(decision.retryAfterMs / 1000)).end() return } ``` ## Per-tier buckets ```ts const limiter = createRateLimiter<{ userId: string; tier: 'free' | 'pro' }>({ keyOf: ctx => ctx.userId, bucketOf: ctx => ctx.tier, buckets: { free: { capacity: 10, refill: 10, windowMs: 60_000 }, pro: { capacity: 1000, refill: 1000, windowMs: 60_000 }, }, }) ``` `bucketOf` can return any key in `buckets` — e.g. `'ip'` for anonymous requests, `'user'` for authenticated, `'admin'` for bypass. ## Decision shape ```ts { allowed: boolean, remaining: number, retryAfterMs: number, // 0 when allowed key: string, bucket: string, } ``` ## Observability ```ts limiter.inspect() // snapshot of { key, bucket, tokens } for dashboards ``` ## Resets On logout / key rotation / manual override: ```ts limiter.reset(userId) ``` ## Scaling beyond a single process For multi-worker deployments, implement the same `RateLimiter` interface against Redis (use `INCR` + `EXPIRE` for a fixed window or Lua for a token bucket). The return type is identical, so nothing above this line changes. ## See also - [Cost guard](/docs/reference/recipes/cost-guard) — dollar ceiling, complementary to request limits - [Prompt injection detector](/docs/reference/recipes/prompt-injection) --- # Replay a session against a different model Source: https://www.agentskit.io/docs/reference/recipes/replay-different-model > Re-run a recorded cassette through any adapter to compare quality, latency, or cost without touching production traffic. You recorded a production trace with [deterministic replay](/docs/reference/recipes/deterministic-replay). Now you want to A/B it against a cheaper model, a new provider, or your own fine-tune — without rerunning real user traffic. `replayAgainst` does exactly that: iterate every recorded turn, drive the candidate adapter with the same `AdapterRequest`, and return a per-turn comparison. ## Install ```bash npm install -D @agentskit/eval ``` ## Compare a cassette against a candidate ```ts import { loadCassette, replayAgainst, summarizeReplay } from '@agentskit/eval/replay' import { anthropic, openai } from '@agentskit/adapters' const cassette = await loadCassette('./fixtures/production.cassette.json') const candidate = openai({ apiKey: process.env.OPENAI_API_KEY!, model: 'gpt-4o-mini' }) const turns = await replayAgainst(cassette, candidate, { concurrency: 4 }) const summary = summarizeReplay(turns) console.log(`avg similarity: ${(summary.avgSimilarity * 100).toFixed(1)}%`) console.log(`worst turn: ${(summary.minSimilarity * 100).toFixed(1)}%`) console.log(`errors: ${summary.errorCount}/${summary.turnCount}`) ``` Each entry in `turns` has: ```ts { turn: number, input: string, recorded: { text, chunkCount }, candidate: { text, chunkCount, error? }, similarity: number, // Jaccard over tokens, 0..1 } ``` ## Options | Option | Default | Purpose | |--------|---------|---------| | `concurrency` | `1` | Run N candidate turns in parallel | | `limit` | all | Stop after N turns (smoke tests) | ## Typical uses - Quick cost/quality sweep before swapping a production model. - Regression check after a fine-tune. - Adversarial review: replay a bug-repro cassette through a stronger model to confirm the failure is environmental, not prompt-design. Pair with [Prompt diff](/docs/reference/recipes/prompt-diff) or the [Eval suite](/docs/reference/recipes/eval-suite) for richer comparison metrics. ## See also - [Deterministic replay](/docs/reference/recipes/deterministic-replay) - [Speculative execution](/docs/reference/recipes/speculative-execution) --- # Multi-agent research team Source: https://www.agentskit.io/docs/reference/recipes/research-team > A planner that delegates to a researcher and a writer. Real multi-agent in 30 lines. A research workflow with three roles: a planner that decomposes the task, a researcher that finds sources, and a writer that synthesizes a final report. ## Install ```bash npm install @agentskit/runtime @agentskit/adapters @agentskit/skills @agentskit/tools ``` ## The team ```ts title="research.ts" import { createRuntime } from '@agentskit/runtime' import { anthropic, openai } from '@agentskit/adapters' import { planner, researcher } from '@agentskit/skills' import { webSearch, filesystem } from '@agentskit/tools' import type { SkillDefinition } from '@agentskit/core' const writer: SkillDefinition = { name: 'writer', description: 'Synthesizes research findings into a clear, structured report.', systemPrompt: `You are a precise technical writer. Take the research notes you receive and produce a report with: - TL;DR (3 bullets) - Body organized by theme (not by source) - Inline citations [1] [2] linking to source URLs - "Open questions" section if the research surfaced uncertainty Be terse. Cut adjectives. Keep paragraphs short.`, tools: ['filesystem_write'], temperature: 0.4, } const runtime = createRuntime({ adapter: anthropic({ apiKey: KEY, model: 'claude-sonnet-4-6' }), // planner uses this tools: [], // planner uses delegates, no direct tools maxSteps: 10, maxDelegationDepth: 2, }) const result = await runtime.run( 'Research the current state of WebGPU support across browsers and write a report at ./out/webgpu.md', { skill: planner, delegates: { researcher: { skill: researcher, adapter: openai({ apiKey: KEY, model: 'gpt-4o-mini' }), // cheaper for research tools: [webSearch()], maxSteps: 5, }, writer: { skill: writer, tools: [...filesystem({ basePath: './out' })], maxSteps: 3, }, }, }, ) console.log(result.content) console.log(`\n— ${result.steps} steps total, ${result.toolCalls.length} tool calls`) ``` ## Run it ```bash mkdir -p out && npx tsx research.ts cat out/webgpu.md ``` ## What's happening 1. **Planner** reads the task, decides to delegate research, then writing 2. Calls `delegate_researcher("Find current WebGPU support across browsers")` — that's just a tool call to the model 3. The runtime spawns a sub-runtime with the researcher skill + web search; returns the findings 4. Planner calls `delegate_writer("Synthesize this into ./out/webgpu.md: ...")` 5. Writer skill writes the file via `filesystem_write` 6. Planner returns the final summary Each delegate gets its own `maxSteps` budget. Total run is bounded by the planner's `maxSteps` × `maxDelegationDepth`. ## Why mixed adapters - **Planner uses Claude Sonnet 4.6** — better at task decomposition - **Researcher uses GPT-4o-mini** — cheaper, good enough for retrieval-heavy work - **Writer reuses the planner's adapter** — fewer config knobs The Adapter contract makes this trivial. See [ADR 0001](https://github.com/AgentsKit-io/agentskit/blob/main/docs/architecture/adrs/0001-adapter-contract.md). ## Tighten the recipe - **Critic delegate** that reviews the writer's output before returning - **Citation verifier** delegate that checks every URL is reachable - **Cost cap per delegate** via observer that aborts when exceeded - **Resumable** — use durable execution (post-Phase-3 #156) for runs that take hours ## Related - [Recipe: Code reviewer](./code-reviewer) — same pattern, single agent - [Concepts: Skill](../concepts/skill) — delegates by reference - [Concepts: Runtime](../concepts/runtime) — delegation as tool --- # Schema-first agents Source: https://www.agentskit.io/docs/reference/recipes/schema-first-agent > Define your agent in YAML or JSON and get a typed AgentSchema you can feed the runtime. When an agent is defined by *declaration* instead of imperative code, it becomes diffable, reviewable, and portable. `@agentskit/core` ships a zero-dependency `AgentSchema` validator — bring your own YAML parser if you want YAML, or use JSON out of the box. ## Install Built into `@agentskit/core`. ## JSON (no extra deps) ```json title="agents/support-bot.json" { "name": "support-bot", "description": "First-line customer support triage.", "systemPrompt": "You are a calm, precise triage agent...", "model": { "provider": "anthropic", "model": "claude-sonnet-4-6" }, "tools": [ { "name": "search_kb", "description": "Search the knowledge base", "schema": { "type": "object", "properties": { "query": { "type": "string" } } } } ], "memory": { "kind": "localStorage", "key": "support-bot" }, "skills": ["researcher"] } ``` ```ts import { parseAgentSchema } from '@agentskit/core/agent-schema' import { readFileSync } from 'node:fs' const schema = parseAgentSchema(readFileSync('agents/support-bot.json', 'utf8')) // ^? AgentSchema (typed) ``` ## YAML (bring your own parser) ```yaml title="agents/support-bot.yaml" name: support-bot model: provider: anthropic model: claude-sonnet-4-6 tools: - name: search_kb description: Search the knowledge base ``` ```ts import { parseAgentSchema } from '@agentskit/core' import { parse as parseYaml } from 'yaml' // or 'js-yaml' import { readFileSync } from 'node:fs' const schema = parseAgentSchema(readFileSync('agents/support-bot.yaml', 'utf8'), { parser: parseYaml, }) ``` ## Compile to a typed TS module Useful for monorepos that want `import { agent } from './agent.gen.ts'`. ```ts import { parseAgentSchema, renderAgentSchemaModule } from '@agentskit/core/agent-schema' import { writeFileSync, readFileSync } from 'node:fs' const schema = parseAgentSchema(readFileSync('agents/support-bot.json', 'utf8')) writeFileSync('agents/agent.gen.ts', renderAgentSchemaModule(schema)) ``` ## Fields reference | Field | Required | Notes | |-------|----------|-------| | `name` | yes | Must match `/[a-zA-Z_][a-zA-Z0-9_-]*/` | | `description` | no | Free-form | | `systemPrompt` | no | Persona / behavior for the model | | `model.provider` | yes | `anthropic` / `openai` / `gemini` / ... | | `model.model` / `temperature` / `maxTokens` / `baseUrl` | no | Provider config | | `tools[]` | no | `name` + optional `description`, `schema`, `implementation` hint, `requiresConfirmation`, `tags` | | `memory.kind` | no | `inMemory` / `localStorage` / `custom` | | `skills[]` | no | References to `@agentskit/skills` ids | | `metadata` | no | Free-form | ## See also - [`agentskit ai`](/docs/reference/recipes/agentskit-ai) — generate a schema from natural language - [Adapter router](/docs/reference/recipes/adapter-router) --- # Self-debug tool Source: https://www.agentskit.io/docs/reference/recipes/self-debug > On tool error, let the agent read the error + schema and draft corrected arguments for a retry. Tool calls fail for boring reasons: the model hallucinated a field, missed a required arg, or passed a string where a number was expected. `wrapToolWithSelfDebug` gives your tool a feedback loop — on failure, a user-supplied "debugger" sees the error + schema + args and returns corrected arguments for a retry. ## Install Ships in `@agentskit/core/self-debug` subpath (no main-bundle weight). ## Wrap any tool ```ts import { wrapToolWithSelfDebug, createLlmSelfDebugger } from '@agentskit/core/self-debug' import { anthropic } from '@agentskit/adapters' import { createRuntime } from '@agentskit/runtime' const smart = anthropic({ apiKey: process.env.ANTHROPIC_API_KEY!, model: 'claude-haiku-4-5' }) async function complete(prompt: string): Promise { const runtime = createRuntime({ adapter: smart }) const r = await runtime.run(prompt) return r.content } const resilientSearch = wrapToolWithSelfDebug( searchTool, createLlmSelfDebugger(complete), { maxAttempts: 2 }, ) ``` The LLM-backed debugger sees: 1. The tool's name, description, and JSON Schema. 2. The previous attempt's arguments. 3. The error message. It emits corrected JSON. If it cannot recover, it returns `{"giveUp": true}` and the original error is rethrown. ## Custom debuggers You don't have to use an LLM — any heuristic works: ```ts const pinnedRetry = wrapToolWithSelfDebug(tool, ({ error, args }) => { if (/unknown field "limit"/.test(error.message)) { const { limit: _, ...rest } = args return { args: rest } } return { args: null } }) ``` ## Observability ```ts wrapToolWithSelfDebug(tool, debugger, { maxAttempts: 3, onEvent: e => logger.info('[self-debug]', e), }) ``` Events: `success` / `failure` / `retry` / `give-up`. ## See also - [Tool composer](/docs/reference/recipes/tool-composer) — pipeline tools into a macro - [Mandatory sandbox](/docs/reference/recipes/mandatory-sandbox) — combine with per-tool validators --- # Wrap a non-streaming endpoint Source: https://www.agentskit.io/docs/reference/recipes/simulate-stream > Turn a one-shot provider into a streaming adapter so UIs see identical ergonomics. Some providers only expose non-streaming endpoints — an internal service, a legacy API, a research model. But your consumers (useChat, the runtime) expect a streaming `StreamSource`. `simulateStream` from `@agentskit/adapters` fetches once and yields the response as a sequence of chunks so everything downstream keeps working. ## Install ```bash npm install @agentskit/adapters @agentskit/core ``` ## A wrapped adapter ```ts title="my-adapter.ts" import type { AdapterFactory, AdapterRequest } from '@agentskit/core' import { simulateStream } from '@agentskit/adapters' export interface MyAdapterConfig { baseUrl: string apiKey: string model: string } export function myAdapter(config: MyAdapterConfig): AdapterFactory { return { capabilities: { // Tell downstream consumers what shape they'll see streaming: true, // yes — we synthesize streaming from one-shot tools: false, }, createSource: (request: AdapterRequest) => { return simulateStream( // 1. Real fetch — defer every I/O until stream() is called (ADR 0001 A1) (signal) => fetch(`${config.baseUrl}/v1/complete`, { method: 'POST', signal, headers: { 'content-type': 'application/json', 'authorization': `Bearer ${config.apiKey}`, }, body: JSON.stringify({ model: config.model, messages: request.messages.map(m => ({ role: m.role, content: m.content })), }), }), // 2. Extractor — turn the non-streaming JSON into the final text async (response) => { const body = await response.json() as { text: string } return body.text }, // 3. Error label used in error chunks 'MyAPI', // 4. Streaming behavior (all optional) { chunkSize: 32, delayMs: 8, retry: { maxAttempts: 3 } }, ) }, } } ``` Wire it like any built-in adapter: ```ts const adapter = myAdapter({ baseUrl: '...', apiKey: KEY, model: 'internal-v1' }) // In a chat UI useChat({ adapter }) // In a runtime createRuntime({ adapter }) ``` ## What `simulateStream` actually does 1. Calls your `doFetch` (once) — retry + abort handling are free via `fetchWithRetry` 2. Calls your `extractText` to pull the final string out of the response 3. Splits the text with `chunkText` at whitespace boundaries into ~`chunkSize` pieces 4. Yields each piece as a `{ type: 'text', content }` chunk with `delayMs` between them 5. Yields the terminal `{ type: 'done' }` chunk (ADR 0001 A3) ## Options | Option | Default | What | |---|---|---| | `chunkSize` | 32 | Target characters per chunk (prefers whitespace boundaries within 8 chars of the target) | | `delayMs` | 8 | Delay between chunks — tune for visual pace | | `retry` | — | `RetryOptions` passed to `fetchWithRetry` — same shape as every other adapter | ## Just the chunker Sometimes you only need the splitter: ```ts import { chunkText } from '@agentskit/adapters' const chunks = chunkText('a long paragraph of prose', 40) // ['a long paragraph of ', 'prose'] ``` ## Contract checklist Before publishing a `simulateStream`-based adapter, verify against ADR 0001: - [ ] `createSource` does no I/O (A1) — your fetch is inside the returned `stream()` - [ ] Stream always ends with `done` or `error` (A3) — `simulateStream` handles this - [ ] `abort()` is safe (A6) — `simulateStream` wires the AbortSignal - [ ] No input mutation (A7) — transform inputs via a copy if needed Full checklist in [ADR 0001 — Adapter contract](https://github.com/AgentsKit-io/agentskit/blob/main/docs/architecture/adrs/0001-adapter-contract.md). ## Related - [Concepts: Adapter](../concepts/adapter) - [Recipe: Custom adapter](./custom-adapter) — when you want full control over the streaming parser - [Capabilities](../concepts/adapter) — advertising `streaming: true` so routers/ensembles treat you as streaming-compatible --- # Skill marketplace + ready-made skills Source: https://www.agentskit.io/docs/reference/recipes/skill-marketplace > Publish + install versioned skills through a registry. Four new ready-made skills. Skills are prompts + behavior packaged for reuse. `@agentskit/skills` now ships a tiny marketplace primitive — publish semver-pinned `SkillPackage`s, query them, `install` the latest matching range — and four new ready-made skills on top of the existing researcher / coder / planner / critic / summarizer set. ## Install ```bash npm install @agentskit/skills ``` ## Ready-made skills (S24 additions) | Skill | Purpose | |---|---| | `codeReviewer` | Rigorous PR review with severity-tagged findings. | | `sqlGen` | Natural language → parameterized Postgres queries. | | `dataAnalyst` | Hypothesize → query → interpret business data. | | `translator` | Faithful translation that preserves formatting. | ```ts import { codeReviewer, sqlGen, dataAnalyst, translator } from '@agentskit/skills' import { createRuntime, composeSkills } from '@agentskit/runtime' const runtime = createRuntime({ adapter, systemPrompt: codeReviewer.systemPrompt, }) ``` ## Marketplace primitives ```ts import { createSkillRegistry, parseSemver, compareSemver, matchesRange, } from '@agentskit/skills' const registry = createSkillRegistry() await registry.publish({ version: '1.0.0', publisher: 'acme', tags: ['ops'], skill: myOpsBot, }) // Install the latest ^1 version: const pkg = await registry.install('ops-bot', '^1.0.0') ``` Range syntax: - `1.2.3` (exact) - `^1.2.3` (same major) - `~1.2.3` (same minor) - `>=1.2.3` (min version) - `*` (any) Enough for a basic marketplace. Layer `semver` on top if you need full npm-compatible ranges. Bring your own backing store (Postgres table of `(name, version)` rows, S3 manifest + CDN, Git-backed, etc.) by implementing the same `SkillRegistry` contract. ## See also - [Brainstorm / compose skills](/docs/get-started/concepts/skill) - [Agentskit AI](/docs/reference/recipes/agentskit-ai) — generate a skill from natural language --- # Speculative execution Source: https://www.agentskit.io/docs/reference/recipes/speculative-execution > Run the same request across N adapters in parallel, keep the winner, abort the losers. Latency matters. So does quality. `speculate` lets you have both: kick off a cheap+fast adapter and a slow+accurate one together, take the first to finish, and cancel the loser before it burns tokens. ## Install Built into `@agentskit/runtime` — nothing extra to install. ## Quick start — fastest wins ```ts import { speculate } from '@agentskit/runtime' import { anthropic, openai } from '@agentskit/adapters' const result = await speculate({ candidates: [ { id: 'haiku', adapter: anthropic({ apiKey: ..., model: 'claude-haiku-4-5' }) }, { id: 'sonnet', adapter: anthropic({ apiKey: ..., model: 'claude-sonnet-4-6' }) }, ], request: { messages: [{ id: '1', role: 'user', content: 'Summarize this.', status: 'complete', createdAt: new Date() }], }, }) console.log(result.winner.id, result.winner.text) console.log('loser latency:', result.losers.map(l => l.latencyMs)) ``` The loser is aborted as soon as the winner settles. ## Picker strategies | `pick` | Behavior | |--------|----------| | `'first'` *(default)* | First candidate to finish without error | | `'longest'` | Candidate with the most output text | | `function` | Custom picker: receives all results, returns winner id | ```ts await speculate({ candidates: [...], request, pick: results => { // Prefer the candidate whose output contains a JSON object. const parsed = results.find(r => r.text.trim().startsWith('{')) return parsed?.id ?? results[0].id }, }) ``` ## Timeout Bound each candidate with `timeoutMs`. A candidate that times out is aborted and marked with an error, but doesn't fail the whole run as long as another candidate succeeds. ```ts await speculate({ candidates: [...], request, timeoutMs: 5_000, }) ``` ## Opt out of aborting a loser `abortOnLoser: false` keeps a candidate running to completion even after it's declared the loser — useful when you want to record all variants for offline analysis. ```ts { id: 'sonnet', adapter: sonnet, abortOnLoser: false } ``` ## Result shape ```ts { winner: { id, text, chunks, latencyMs, error?, aborted? }, losers: SpeculativeResult[], all: SpeculativeResult[], } ``` ## See also - [Deterministic replay](/docs/reference/recipes/deterministic-replay) — pin a winning trace - [Token budget](/docs/reference/recipes/token-budget) — control cost per run --- # Time-travel debug Source: https://www.agentskit.io/docs/reference/recipes/time-travel-debug > Step through a recorded agent session, rewrite a tool result, and replay from that point forward. You recorded a session with [deterministic replay](/docs/reference/recipes/deterministic-replay). The bug happens after a specific tool call returns bad data. You want to rewrite the tool result and re-run from there — without re-recording. `createTimeTravelSession` from `@agentskit/eval/replay` wraps a cassette in a cursor: step through it, `override` any chunk, `fork` at an index to get a fresh cassette, and hand that cassette to a replay adapter. ## Install ```bash npm install -D @agentskit/eval ``` ## Step through a session ```ts import { loadCassette, createTimeTravelSession } from '@agentskit/eval/replay' const cassette = await loadCassette('./fixtures/bug-427.cassette.json') const session = createTimeTravelSession(cassette) console.log('total chunks:', session.length) let chunk = session.step() while (chunk) { console.log(session.cursor, chunk) chunk = session.step() } ``` ## Rewrite a tool result, fork, replay The workflow: 1. Find the chunk index where the broken tool result was emitted. 2. `override(index, {...})` with a corrected chunk. 3. `fork(index + 1)` — everything up to and including the fix, discarding the broken tail. 4. Feed the forked cassette into `createReplayAdapter` to re-run. ```ts import { createReplayAdapter, loadCassette, createTimeTravelSession } from '@agentskit/eval/replay' import { createRuntime } from '@agentskit/runtime' const session = createTimeTravelSession(await loadCassette('./fixtures/bug-427.cassette.json')) session.override(12, { type: 'tool_call', toolCall: { id: 'call_3', name: 'lookup_user', args: '{"id":42}', result: '{"plan":"pro"}' }, }) const fork = session.fork(13) const runtime = createRuntime({ adapter: createReplayAdapter(fork, { mode: 'sequential' }) }) const rerun = await runtime.run('/* same initial prompt */') ``` ## API | Method | Description | |--------|-------------| | `length` | Total flattened chunk count | | `cursor` | Current read position | | `peek(i)` | Read chunk at absolute index without moving cursor | | `step()` | Return chunk at cursor, advance cursor | | `seek(i)` | Jump cursor to absolute index | | `override(i, chunk)` | Replace chunk at index — returns prior value | | `fork(i)` | Return a new cassette containing chunks `[0, i)` | | `snapshot()` | Full copy of current (possibly mutated) cassette | ## See also - [Deterministic replay](/docs/reference/recipes/deterministic-replay) - [Prompt diff](/docs/reference/recipes/prompt-diff) --- # Token budget compiler Source: https://www.agentskit.io/docs/reference/recipes/token-budget > Declare a token budget, let AgentsKit trim messages and summarize history to fit. Context windows are finite. Long chats, tool-heavy runs, and big system prompts blow past the limit; the usual response is a random slice of the last N messages and a prayer. `compileBudget` replaces the prayer with a declared budget and three well-defined trimming strategies. ## Install Built into `@agentskit/core` — nothing extra to install. ## Quick start ```ts import { compileBudget } from '@agentskit/core' const compiled = await compileBudget({ budget: 16_000, reserveForOutput: 1_000, systemPrompt: 'You are a helpful assistant.', messages: history, tools: availableTools, }) if (!compiled.fits) { console.warn('Still over budget:', compiled.tokens) } // Pass compiled.messages + compiled.systemPrompt to your adapter. ``` ## Strategies | Strategy | Behavior | Good for | |----------|----------|----------| | `drop-oldest` *(default)* | Remove oldest turns until it fits | Plain chat, no memory of early turns needed | | `sliding-window` | Keep only the most recent N turns | Agents that care about recency, not history | | `summarize` | Drop oldest, then fold them into a single summary message | Long-running agents that need *some* memory of the past | ```ts await compileBudget({ budget: 8_000, messages, strategy: 'summarize', summarizer: async dropped => ({ id: 'summary', role: 'system', content: `Summary of ${dropped.length} earlier turns: ...`, status: 'complete', createdAt: new Date(), }), }) ``` ## Token counter Defaults to a zero-dependency approximate counter (`chars / 4`). Swap in a real tokenizer — `tiktoken`, provider-specific, your own — via the `counter` option: ```ts import type { TokenCounter } from '@agentskit/core' const tiktokenCounter: TokenCounter = { name: 'tiktoken', async count(messages) { /* ... */ }, } await compileBudget({ budget: 10_000, messages, counter: tiktokenCounter }) ``` ## Result shape ```ts { messages: Message[], // trimmed (or augmented with summary) systemPrompt?: string, // unchanged tokens: { system: number, messages: number, tools: number, total: number, budget: number, // budget - reserveForOutput }, dropped: Message[], fits: boolean, strategy: 'drop-oldest' | 'sliding-window' | 'summarize', } ``` `keepRecent` protects the last N turns even if the budget can't accommodate them — `fits: false` signals that case so you can alert rather than silently truncate. ## See also - [Cost guard](/docs/reference/recipes/cost-guard) — hard dollar ceiling per run - [Deterministic replay](/docs/reference/recipes/deterministic-replay) --- # Tool composer Source: https://www.agentskit.io/docs/reference/recipes/tool-composer > Chain N tools into a single macro tool — a fixed recipe the model can invoke with one schema. Some agent capabilities are always the same multi-step recipe: fetch → parse → rerank → summarize. Letting the model pick each step adds latency and unreliability; baking the recipe into a single tool gives the model one lever and you predictable behavior. `composeTool` takes N sub-tools, a mapper per step, and an optional finalizer — and returns one `ToolDefinition` the model sees as a single tool. ## Install Ships in `@agentskit/core` under a subpath: ```ts import { composeTool } from '@agentskit/core/compose-tool' ``` ## Chain three tools into one ```ts import { composeTool } from '@agentskit/core/compose-tool' import { defineTool } from '@agentskit/core' import { fetchUrl, webSearch } from '@agentskit/tools' const summarize = defineTool({ name: 'summarize', schema: { type: 'object', properties: { text: { type: 'string' } }, required: ['text'] } as const, execute: async ({ text }) => `summary: ${text.slice(0, 80)}...`, }) const research = composeTool<{ query: string }>({ name: 'research', description: 'Search the web, fetch the top result, summarize it.', schema: { type: 'object', properties: { query: { type: 'string' } }, required: ['query'] }, steps: [ { tool: webSearch(), mapArgs: ({ args }) => ({ query: args.query, limit: 1 }), }, { tool: fetchUrl(), mapArgs: ({ state }) => ({ url: (state as { results: { url: string }[] }).results[0]!.url }), }, { tool: summarize, mapArgs: ({ state }) => ({ text: String(state) }), }, ], }) ``` ## Step contract Each `steps[i]`: ```ts { tool, mapArgs({ args, state, prior }) => Record, mapResult?(result, { args, state, prior }) => newState, stopWhen?(state, { args, prior }) => boolean, // short-circuit the chain } ``` - `args` — the macro tool's original input. - `state` — output of the previous step (after `mapResult`). - `prior` — every intermediate output in declaration order. Return a `finalize({ args, prior, state })` to produce a different return value than the last step's state. ## Stop when done A step can short-circuit the rest of the chain if its `stopWhen` predicate returns true — useful for early termination in cache-hit scenarios. ```ts { tool: cacheCheck, mapArgs: ({ args }) => ({ key: args.query }), stopWhen: state => state !== null, } ``` ## Observability ```ts composeTool({ ..., onStep: e => logger.debug('[compose]', e), }) ``` Events: `start` / `end` / `skip`, with step index + tool name. ## See also - [MCP bridge](/docs/reference/recipes/mcp-bridge) — expose composed tools to MCP hosts - [Mandatory sandbox](/docs/reference/recipes/mandatory-sandbox) - [Custom adapter](/docs/reference/recipes/custom-adapter) --- # Local trace viewer Source: https://www.agentskit.io/docs/reference/recipes/trace-viewer > Persist agent spans to disk and open a self-contained HTML waterfall — Jaeger-style, no server required. Cloud trace viewers are great, but for local development you want something you can open offline, share as an artifact, and inspect without a network round-trip. `@agentskit/observability` ships a tiny file-backed trace sink plus a zero-dependency HTML renderer that produces a single-file gantt view of any run. ## Install Comes with `@agentskit/observability`. ## Record spans to disk ```ts import { createFileTraceSink, createTraceTracker } from '@agentskit/observability' import { createRuntime } from '@agentskit/runtime' import { anthropic } from '@agentskit/adapters' const sink = createFileTraceSink('./traces') const tracker = createTraceTracker({ onSpanStart: sink.onSpanStart, onSpanEnd: sink.onSpanEnd, }) const runtime = createRuntime({ adapter: anthropic({ apiKey: process.env.ANTHROPIC_API_KEY!, model: 'claude-sonnet-4-6' }), observers: [{ name: 'tracker', on: e => tracker.handle(e) }], }) await runtime.run('What happened in Q3?') const out = await sink.flush({ traceId: 'q3-summary' }) console.log('json:', out.json, 'html:', out.html) ``` Open `./traces/q3-summary.html` in a browser — it's a self-contained page with no external requests. ## Programmatic rendering If you already have spans from somewhere else (OpenTelemetry export, a saved log file), turn them into a report and render HTML directly. ```ts import { buildTraceReport, renderTraceViewerHtml } from '@agentskit/observability' const report = buildTraceReport('my-trace', mySpans) await Bun.write('trace.html', renderTraceViewerHtml(report)) ``` ## What's in a report ```ts { traceId: string, startTime: number, endTime: number, durationMs: number, spanCount: number, errorCount: number, spans: TraceSpan[], // sorted by startTime } ``` ## See also - [Devtools server](/docs/reference/recipes/devtools-server) — live feed for an external UI - [OpenTelemetry observer](/docs/reference/recipes/cost-guarded-chat) --- # Vector memory adapters Source: https://www.agentskit.io/docs/reference/recipes/vector-adapters > Drop-in VectorMemory for pgvector, Pinecone, Qdrant, Chroma, and Upstash Vector. `@agentskit/memory` ships five new `VectorMemory` implementations. Each targets a different deployment story — SQL-native (pgvector), serverless HTTP (Pinecone, Upstash), self-hosted REST (Qdrant, Chroma). All obey the same three-method contract (`store` / `search` / `delete`), so you can A/B providers without touching agent code. ## Install ```bash npm install @agentskit/memory ``` ## Postgres + pgvector BYO SQL runner so you pick the driver (`pg`, `postgres`, `@neondatabase/serverless`, Supabase client). ```ts import { pgvector } from '@agentskit/memory' import { Pool } from 'pg' const pool = new Pool({ connectionString: process.env.DATABASE_URL }) const memory = pgvector({ runner: { query: async (sql, params) => { const r = await pool.query(sql, params) return { rows: r.rows } }, }, table: 'agentskit_vectors', }) ``` Expects a table like: ```sql CREATE TABLE agentskit_vectors ( id text primary key, content text, embedding vector(1536), metadata jsonb ); ``` ## Pinecone ```ts import { pinecone } from '@agentskit/memory' const memory = pinecone({ apiKey: process.env.PINECONE_API_KEY!, indexUrl: 'https://-.svc..pinecone.io', namespace: 'prod', }) ``` ## Qdrant ```ts import { qdrant } from '@agentskit/memory' const memory = qdrant({ url: process.env.QDRANT_URL!, apiKey: process.env.QDRANT_API_KEY, collection: 'agents', }) ``` ## Chroma ```ts import { chroma } from '@agentskit/memory' const memory = chroma({ url: 'http://localhost:8000', collection: 'agents', }) ``` ## Upstash Vector ```ts import { upstashVector } from '@agentskit/memory' const memory = upstashVector({ url: process.env.UPSTASH_VECTOR_URL!, token: process.env.UPSTASH_VECTOR_TOKEN!, }) ``` ## Shared contract All five implement: ```ts interface VectorMemory { store(docs: VectorDocument[]): Promise search(embedding: number[], opts?: { topK?: number; threshold?: number }): Promise delete?(ids: string[]): Promise } ``` Results include a normalized `score` in `[0, 1]` (higher is better). pgvector converts cosine distance; Chroma converts `1 - distance`; Pinecone / Qdrant / Upstash pass through the native score. ## See also - [RAG reranking](/docs/reference/recipes/rag-reranking) — wrap any of these with BM25 hybrid - [Hierarchical memory](/docs/reference/recipes/hierarchical-memory) — use as the recall tier - [Encrypted memory](/docs/reference/recipes/encrypted-memory) — layer on top for zero-trust --- # Virtualized memory Source: https://www.agentskit.io/docs/reference/recipes/virtualized-memory > Transparently handle giant conversations by keeping a hot window active and paging the rest. A chat session with 10k messages shouldn't blow up `load()`. `createVirtualizedMemory` wraps any `ChatMemory` with a fixed "hot" window — recent messages always loaded — and lets you plug in a retriever to surface relevant older messages on demand. ## Install Built into `@agentskit/core`. ## Quick start ```ts import { createInMemoryMemory, createVirtualizedMemory } from '@agentskit/core' const backing = createInMemoryMemory(history) // 10k messages const memory = createVirtualizedMemory(backing, { maxActive: 50 }) const visible = await memory.load() // latest 50 await memory.save([...visible, newMsg]) // cold 9,950 preserved ``` - `maxActive` caps the number of recent messages returned. - Backing store always holds everything — `size()` / `loadAll()` expose the full history. - `save` merges visible messages with the cold tail, so load → mutate → save doesn't silently truncate history. ## Surface older messages on demand Plug in a retriever. Typical impl: embed the latest user message and hit a vector store for the top-K cold matches. ```ts const memory = createVirtualizedMemory(backing, { maxActive: 30, maxRetrieved: 5, retriever: async ({ hot, cold, maxRetrieved }) => { const latest = hot[hot.length - 1] const embedding = await embed(latest.content) const hits = await vectorStore.search(embedding, { topK: maxRetrieved }) return cold.filter(m => hits.some(h => h.id === m.id)) }, }) const merged = await memory.load() // returns retrieved cold msgs spliced chronologically before hot window ``` Retrieved messages are spliced in chronological order with the hot window, and duplicates are filtered out. ## When to use this - Long-running assistants where users scroll back weeks later. - Agents whose task history grows unbounded (background crons). - Any session that would otherwise OOM on `load()`. Pair with [token budget](/docs/reference/recipes/token-budget) — virtualized memory caps *count*, `compileBudget` caps *tokens*. ## See also - [Persistent memory](/docs/reference/recipes/persistent-memory) - [Token budget compiler](/docs/reference/recipes/token-budget) --- # Open specs Source: https://www.agentskit.io/docs/reference/specs > Three portable JSON contracts — Agent-to-Agent, Skill+Tool Manifest, Eval Format. Small, stable, versioned JSON shapes. Each is a subpath of `@agentskit/core`: types + validator + zero runtime dep. - **A2A (Agent-to-Agent)** — `agent/card` · `task/invoke` · `task/cancel` · `task/approve` · `task/status`. JSON-RPC 2.0. [Recipe](/docs/reference/recipes/open-specs). - **Manifest** — packaging format for skills + tools. Tool entries mirror MCP `inputSchema` so manifests round-trip. - **Eval Format** — portable eval dataset + run-result. `matchesExpectation` supports literal / regex / normalized / semantic similarity. *Per-spec deep dives land in step 6 of the docs IA rollout.* ## Related - [Package: @agentskit/core](/docs/reference/packages/core) (subpaths `/a2a`, `/manifest`, `/eval-format`) - [MCP bridge](/docs/agents/tools) — pairs with the Manifest spec --- # A2A — Agent-to-Agent Source: https://www.agentskit.io/docs/reference/specs/a2a > JSON-RPC 2.0 contract for one agent to invoke another across process or network. Subpath: `@agentskit/core/a2a`. ## Methods | Method | Purpose | |---|---| | `agent/card` | discovery — returns agent metadata, tools, skills | | `task/invoke` | start a task | | `task/cancel` | abort running task | | `task/approve` | HITL approval | | `task/status` | poll state | ## Request shape ```json { "jsonrpc": "2.0", "id": 1, "method": "task/invoke", "params": { "input": "summarize last week's PRs", "context": {} } } ``` ## Implementation ```ts import { createA2AServer, createA2AClient } from '@agentskit/core/a2a' const server = createA2AServer({ runtime }) const client = createA2AClient({ url: 'https://agent.example.com/rpc' }) const { taskId } = await client.invoke({ input: '...' }) ``` ## Related - [Specs overview](./) · [Manifest](./manifest) - [Recipe: open specs](/docs/reference/recipes/open-specs) --- # AgentSchema Source: https://www.agentskit.io/docs/reference/specs/agent-schema > Typed, validated definition of an agent — adapter, tools, skills, memory, rag, observers. Subpath: `@agentskit/core/agent-schema`. ## Shape ```json { "name": "support-bot", "adapter": { "kind": "openai", "model": "gpt-4o" }, "tools": ["webSearch", "github"], "skills": ["triage", "summarizer"], "memory": { "kind": "sqlite", "path": ".agentskit/chat.db" }, "rag": { "kind": "file-vector", "path": ".agentskit/vec.json", "dim": 1536 }, "observers": [{ "kind": "costGuard", "maxUsd": 0.5 }] } ``` ## Validation ```ts import { parseAgentSchema, buildRuntime } from '@agentskit/core/agent-schema' const schema = parseAgentSchema(json) // throws on invalid const runtime = await buildRuntime(schema) ``` ## CLI `agentskit ai` emits this shape. See [CLI → ai](/docs/production/cli/ai). ## Generative UI Render the full agent as a form; edit live. See [Generative UI](./generative-ui). ## Related - [CLI → ai](/docs/production/cli/ai) - [Manifest](./manifest) · [A2A](./a2a) --- # Eval format Source: https://www.agentskit.io/docs/reference/specs/eval-format > Portable JSON for eval datasets + run results. Tool-agnostic. Subpath: `@agentskit/core/eval-format`. ## Dataset ```json { "name": "triage-v1", "version": "1.0.0", "cases": [ { "id": "refund", "input": "How do I get a refund?", "expect": { "kind": "regex", "value": "refund policy" } } ] } ``` ## Expectation kinds | Kind | Match rule | |---|---| | `literal` | exact string equality | | `regex` | RegExp test | | `normalized` | whitespace + case-insensitive | | `similarity` | cosine ≥ threshold (needs embedder) | ## API ```ts import { matchesExpectation, parseEvalSuite } from '@agentskit/core/eval-format' const suite = parseEvalSuite(json) const ok = matchesExpectation(output, suite.cases[0].expect) ``` ## Related - [Evals → Suites](/docs/production/evals/suites) - [A2A](./a2a) · [Manifest](./manifest) --- # Generative UI Source: https://www.agentskit.io/docs/reference/specs/generative-ui > Schema-driven renderers. LLM outputs typed JSON; UI reflects it. Subpath: `@agentskit/core/generative-ui`. ## Shape ```ts type GenerativeBlock = | { kind: 'text'; content: string } | { kind: 'table'; columns: string[]; rows: unknown[][] } | { kind: 'form'; fields: FormField[]; onSubmit: { tool: string } } | { kind: 'chart'; spec: ChartSpec } | { kind: 'citation'; url: string; title?: string } ``` ## Validator ```ts import { parseGenerativeBlock } from '@agentskit/core/generative-ui' const block = parseGenerativeBlock(llmOutput) ``` ## Renderer contract Every UI binding (React / Vue / Svelte / Solid / Angular / RN / Ink) ships a `` component. Swap kinds without changing the render call. ## Related - [Recipe: generative UI](/docs/reference/recipes/generative-ui) - [UI → Data attributes](/docs/ui/data-attributes) --- # Manifest Source: https://www.agentskit.io/docs/reference/specs/manifest > Portable packaging for skills + tools. MCP-compatible tool entries. Subpath: `@agentskit/core/manifest`. ## Shape ```json { "name": "my-skills", "version": "0.1.0", "skills": [ { "name": "triage", "version": "1.0.0", "systemPrompt": "..." } ], "tools": [ { "name": "search_docs", "description": "Search internal docs", "inputSchema": { "type": "object", "properties": { "query": { "type": "string" } } } } ] } ``` ## Validator ```ts import { parseManifest } from '@agentskit/core/manifest' const manifest = parseManifest(json) // throws on invalid ``` ## MCP compatibility `tools[].inputSchema` mirrors MCP exactly — manifests round-trip into MCP servers. ## Related - [A2A](./a2a) · [AgentSchema](./agent-schema) - [Tools → MCP bridge](/docs/agents/tools/mcp) --- # UI + hooks Source: https://www.agentskit.io/docs/ui > Every AgentsKit UI binding exposes the same contract. Pick the framework; the API stays the same. One hook, seven bindings. Every framework package mirrors `@agentskit/react`'s contract — same `useChat` return shape, same headless components, same `data-ak-*` hooks. ## Bindings | Package | Primitive | Peer dep | |---|---|---| | [`@agentskit/react`](/docs/reference/packages/react) | `useChat` + `` | `react ^18\|^19` | | [`@agentskit/ink`](/docs/reference/packages/ink) | `useChat` + `` (terminal) | `ink ^5` | | [`@agentskit/vue`](/docs/reference/packages/vue) | `useChat` composable + `` | `vue ^3.4` | | [`@agentskit/svelte`](/docs/reference/packages/svelte) | `createChatStore` | `svelte ^5` | | [`@agentskit/solid`](/docs/reference/packages/solid) | `useChat` | `solid-js ^1.8` | | [`@agentskit/react-native`](/docs/reference/packages/react-native) | `useChat` (Metro-safe) | `react` + `react-native` | | [`@agentskit/angular`](/docs/reference/packages/angular) | `AgentskitChat` service | `@angular/core ^18\|^19\|^20` | ## The hook - [useChat](./use-chat) — contract, events, per-framework shape. ## Components - [ChatContainer](./chat-container) - [Message](./message) - [InputBar](./input-bar) - [ToolCallView](./tool-call-view) - [ToolConfirmation](./tool-confirmation) - [ThinkingIndicator](./thinking-indicator) ## Styling - [Data attributes](./data-attributes) — every stylable hook. - [Theming](./theming) — CSS variables + presets. ## Related - [Concepts → Runtime](/docs/get-started/concepts/runtime) - [For agents → React](/docs/for-agents/react) · [Ink](/docs/for-agents/ink) --- # ChatContainer Source: https://www.agentskit.io/docs/ui/chat-container > Headless scrollable transcript container. Auto-scroll on new messages, virtualized-ready. Top-level wrapper for a chat transcript. Renders `data-ak-chat-container` with auto-scroll anchor. No hardcoded styles — theme via [data attributes](./data-attributes) and [CSS variables](./theming). ## Props | Prop | Type | Default | |---|---|---| | `children` | `ReactNode` | — | | `autoScroll` | `boolean` | `true` | | `className` | `string` | — | ## Per-framework | Framework | Import | |---|---| | React | `import { ChatContainer } from '@agentskit/react'` | | Vue | `import { ChatContainer } from '@agentskit/vue'` | | Svelte | `import ChatContainer from '@agentskit/svelte/ChatContainer.svelte'` | | Solid | `import { ChatContainer } from '@agentskit/solid'` | | React Native | `import { ChatContainer } from '@agentskit/react-native'` — `ScrollView`-backed | | Angular | `` — from `AgentskitUiModule` | | Ink | `import { ChatContainer } from '@agentskit/ink'` — Ink `` | ## Example ```tsx import { ChatContainer, Message, InputBar, useChat } from '@agentskit/react' export function App() { const chat = useChat({ adapter: openai(...) }) return ( {chat.messages.map((m) => )} ) } ``` ## Related - [Message](./message) · [InputBar](./input-bar) - [Theming](./theming) · [Data attributes](./data-attributes) --- # Data attributes Source: https://www.agentskit.io/docs/ui/data-attributes > AgentsKit UI components are headless. Every stylable hook is a data-ak-* attribute so your CSS (or your LLM) can target them reliably. Components emit zero styles. They emit **data attributes**. Style with your favorite CSS stack; generate UI with an LLM and the attributes tell it exactly where to hook. ## Reference | Attribute | Where | Values | |---|---|---| | `data-ak-chat-container` | root of [ChatContainer](./chat-container) | — | | `data-ak-message` | root of [Message](./message) | — | | `data-ak-role` | on `Message` root | `user` · `assistant` · `tool` · `system` | | `data-ak-streaming` | on `Message` root | `true` · `false` | | `data-ak-part` | every message part | `text` · `tool-call` · `tool-result` · `image` · `file` | | `data-ak-tool-call` | root of [ToolCallView](./tool-call-view) | — | | `data-ak-status` | on `ToolCallView` | `pending` · `running` · `done` · `error` · `awaiting-approval` | | `data-ak-input-bar` | root of [InputBar](./input-bar) | — | | `data-ak-thinking` | root of [ThinkingIndicator](./thinking-indicator) | — | | `data-ak-markdown` | root of `Markdown` | — | | `data-ak-code-block` | root of `CodeBlock` | — | ## Generative UI Because selectors are stable and framework-agnostic, LLMs can generate CSS / className overrides that work cross-framework. See [Generative UI recipe](/docs/reference/recipes/generative-ui). ## Related - [Theming](./theming) — CSS variables + preset themes --- # InputBar Source: https://www.agentskit.io/docs/ui/input-bar > Text input + submit. Disabled while streaming. Keyboard shortcuts built in. Controlled input tied to a `ChatReturn`. Submit on Enter, Shift+Enter for newline. Disabled whenever `chat.status === 'streaming'`. Emits `data-ak-input-bar`. ## Props | Prop | Type | Default | |---|---|---| | `chat` | `ChatReturn` | — | | `placeholder` | `string` | `'Type a message...'` | | `disabled` | `boolean` | `false` | | `submitOn` | `'enter' \| 'mod+enter'` | `'enter'` | ## Per-framework | Framework | Import | |---|---| | React | `import { InputBar } from '@agentskit/react'` | | Vue | `import { InputBar } from '@agentskit/vue'` | | Svelte | `import InputBar from '@agentskit/svelte/InputBar.svelte'` | | Solid | `import { InputBar } from '@agentskit/solid'` | | React Native | `import { InputBar } from '@agentskit/react-native'` — `TextInput`-backed | | Angular | `` | | Ink | `import { InputBar } from '@agentskit/ink'` — Ink `` | ## Related - [ChatContainer](./chat-container) · [useChat](./use-chat) --- # Message Source: https://www.agentskit.io/docs/ui/message > Renders one message — user, assistant, tool, or system. Streaming-aware, role-aware. Headless single-message renderer. Role in `data-ak-role`. Streaming state in `data-ak-streaming`. Content chunks (text, tool-call, tool-result, image, file) each emit a `data-ak-part` wrapper. ## Props | Prop | Type | Default | |---|---|---| | `message` | `Message` | — | | `avatar` | `ReactNode` | — | | `actions` | `ReactNode` | — | | `renderPart` | `(part) => ReactNode` | built-in | ## Message shape ```ts type Message = { id: string role: 'user' | 'assistant' | 'tool' | 'system' parts: MessagePart[] createdAt: number streaming?: boolean } ``` ## Per-framework | Framework | Import | |---|---| | React | `import { Message } from '@agentskit/react'` | | Vue | `import { Message } from '@agentskit/vue'` | | Svelte | `import Message from '@agentskit/svelte/Message.svelte'` | | Solid | `import { Message } from '@agentskit/solid'` | | React Native | `import { Message } from '@agentskit/react-native'` | | Angular | `` | | Ink | `import { Message } from '@agentskit/ink'` | ## Related - [ChatContainer](./chat-container) · [ToolCallView](./tool-call-view) - [Theming](./theming) --- # Theming Source: https://www.agentskit.io/docs/ui/theming > CSS variables + optional preset themes. One source of truth across every framework binding. AgentsKit ships headless components — theming is your call. Defaults come from a lightweight stylesheet at `@agentskit/react/theme` (equivalent for Vue / Svelte / Solid / RN). ## Import a preset ```ts import '@agentskit/react/theme' ``` ## CSS variables ```css :root { --ak-bg: #0b0f17; --ak-surface: #111827; --ak-border: #1f2937; --ak-text: #e5e7eb; --ak-muted: #9ca3af; --ak-accent: #6366f1; --ak-danger: #ef4444; --ak-radius: 12px; --ak-font: ui-sans-serif, system-ui, sans-serif; --ak-mono: ui-monospace, SFMono-Regular, monospace; } ``` ## Dark / light Variables flip on `[data-theme="light"]` / `[data-theme="dark"]`. ## Tailwind Wire Tailwind to the CSS variables: ```js theme: { extend: { colors: { ak: { bg: 'var(--ak-bg)', accent: 'var(--ak-accent)', }, }, }, } ``` ## Native + terminal - **React Native:** theme via `ThemeProvider` prop (JS object, same keys). - **Ink:** ANSI palette from the same keys (`ak.accent` → chalk hex). ## Related - [Data attributes](./data-attributes) --- # ThinkingIndicator Source: https://www.agentskit.io/docs/ui/thinking-indicator > Visibility flag for streaming / reasoning states. Renders only while `visible`. Emits `data-ak-thinking`. ## Props | Prop | Type | Default | |---|---|---| | `visible` | `boolean` | — | | `label` | `string` | `'Thinking...'` | ## Usage ```tsx ``` ## Per-framework | Framework | Import | |---|---| | React | `import { ThinkingIndicator } from '@agentskit/react'` | | Vue | `import { ThinkingIndicator } from '@agentskit/vue'` | | Svelte | `import ThinkingIndicator from '@agentskit/svelte/ThinkingIndicator.svelte'` | | Solid | `import { ThinkingIndicator } from '@agentskit/solid'` | | React Native | `import { ThinkingIndicator } from '@agentskit/react-native'` | | Angular | `` | | Ink | `import { ThinkingIndicator } from '@agentskit/ink'` — animated dots | ## Related - [useChat](./use-chat) — read `status` for visibility --- # ToolCallView Source: https://www.agentskit.io/docs/ui/tool-call-view > Render a tool invocation — name, args, result, status. Works for all tools. Shows one tool call's lifecycle: `pending` → `running` → `done` | `error`. Emits `data-ak-tool-call` with `data-ak-status`. ## Props | Prop | Type | Default | |---|---|---| | `toolCall` | `ToolCall` | — | | `collapsed` | `boolean` | `false` | | `renderArgs` | `(args) => ReactNode` | JSON view | | `renderResult` | `(result) => ReactNode` | JSON view | ## ToolCall shape ```ts type ToolCall = { id: string name: string args: unknown status: 'pending' | 'running' | 'done' | 'error' result?: unknown error?: string } ``` ## Per-framework | Framework | Import | |---|---| | React | `import { ToolCallView } from '@agentskit/react'` | | Vue | `import { ToolCallView } from '@agentskit/vue'` | | Svelte | `import ToolCallView from '@agentskit/svelte/ToolCallView.svelte'` | | Solid | `import { ToolCallView } from '@agentskit/solid'` | | React Native | `import { ToolCallView } from '@agentskit/react-native'` | | Angular | `` | | Ink | `import { ToolCallView } from '@agentskit/ink'` | ## Related - [ToolConfirmation](./tool-confirmation) — human-in-the-loop approval gate - [Tools package](/docs/reference/packages/tools) --- # ToolConfirmation Source: https://www.agentskit.io/docs/ui/tool-confirmation > Human-in-the-loop approval UI for guarded tool calls. Shown when a tool's `requiresConfirmation` is set. Approve or deny forwards to `chat.approve` / `chat.deny`. Blocks the run until resolved. ## Props | Prop | Type | |---|---| | `toolCall` | `ToolCall` | | `onApprove` | `() => void` | | `onDeny` | `(reason?: string) => void` | ## Example ```tsx {chat.messages .flatMap((m) => m.parts) .filter((p) => p.type === 'tool-call' && p.status === 'awaiting-approval') .map((tc) => ( chat.approve(tc.id)} onDeny={(reason) => chat.deny(tc.id, reason)} /> ))} ``` ## Per-framework Surface is identical to [ToolCallView](./tool-call-view). Angular: ``. ## Related - [HITL approvals recipe](/docs/reference/recipes/hitl-approvals) - [Security → mandatory sandbox](/docs/production/security/mandatory-sandbox) --- # useChat Source: https://www.agentskit.io/docs/ui/use-chat > The one hook every framework binding exposes. Same input, same return, same events. `useChat` is the contract every AgentsKit UI binding mirrors. It wraps `createChatController` from `@agentskit/core` with the idioms of your host framework (React hook, Vue composable, Svelte store, Solid signal, Angular service, React Native hook, Ink hook). ## Contract ```ts type ChatReturn = { messages: Message[] status: 'idle' | 'streaming' | 'error' | 'awaiting-tool' error: Error | null send: (text: string, opts?: SendOptions) => Promise retry: () => Promise stop: () => void clear: () => void approve: (toolCallId: string, result?: unknown) => void deny: (toolCallId: string, reason?: string) => void edit: (messageId: string, text: string) => Promise } ``` ## Inputs ```ts type ChatConfig = { adapter: Adapter tools?: Tool[] skills?: Skill[] memory?: Memory rag?: Retriever system?: string onEvent?: (e: ChatEvent) => void onError?: (e: Error) => void } ``` ## Per-framework usage | Framework | Import | Shape | |---|---|---| | React | `import { useChat } from '@agentskit/react'` | `const chat = useChat(config)` | | Vue | `import { useChat } from '@agentskit/vue'` | `const chat = useChat(config)` — values are `ref`s | | Svelte | `import { createChatStore } from '@agentskit/svelte'` | `const chat = createChatStore(config)` — Svelte 5 runes | | Solid | `import { useChat } from '@agentskit/solid'` | `const chat = useChat(config)` — accessors | | React Native | `import { useChat } from '@agentskit/react-native'` | Metro-safe, no DOM | | Angular | `import { AgentskitChat } from '@agentskit/angular'` | `constructor(private chat: AgentskitChat)` — `Signal` + `RxJS` | | Ink | `import { useChat } from '@agentskit/ink'` | Terminal-safe | ## Events (`onEvent`) `chat.start` · `message.append` · `message.update` · `tool.call` · `tool.result` · `error` · `status.change` · `chat.end`. Full schema: [Concepts → Events](/docs/get-started/concepts/events). ## Related - Components: [ChatContainer](./chat-container) · [Message](./message) · [InputBar](./input-bar) · [ToolCallView](./tool-call-view) · [ThinkingIndicator](./thinking-indicator) - [Data attributes](./data-attributes) · [Theming](./theming) - [For agents → Contract](/docs/for-agents/contract)