@agentskit/core
Profile: major-package
The zero-dependency foundation that every AgentsKit package builds on — under the 10 KB gzip budget, edge-ready, works everywhere JavaScript runs.
Tags: ai · agents · llm · agentskit · typescript · orchestration · streaming · chat
Verified proof
- Package metadata and tests live under
packages/core/. - Package guide: https://www.agentskit.io/docs/reference/packages/core
- Stability map: docs/STABILITY.md
How this fits the ecosystem
@agentskit/core is the contract layer: the tiny, stable foundation that makes adapters, tools, skills, memory, retrievers, and runtimes interchangeable.
- AgentsKit: compose it with the other packages in this repo to build agents from small, swappable parts.
- Registry: look for ready agents and templates that already use this layer at registry.agentskit.io.
- Playbook: learn the production patterns behind this layer at playbook.agentskit.io.
Docs: package guide · agent handoff
Why core
- Zero external dependencies — no npm bloat, no audit surprises; installs in milliseconds and works in Node, Deno, edge runtimes, and the browser
- Stable contracts that unlock the whole ecosystem — six ADR-pinned interfaces (
Adapter,Tool,Skill,Memory,Retriever,Runtime) make every package interchangeable - Chat state machine included —
createChatControllerhandles streaming, abort, and message history so you never implement that loop yourself - Under 10 KB gzipped, always — budget enforced in CI; the foundation you can commit to for the long term
Install
npm install @agentskit/core
Quick example
import { createChatController, createInMemoryMemory } from '@agentskit/core'
import { anthropic } from '@agentskit/adapters'
const controller = createChatController({
adapter: anthropic({ apiKey: process.env.ANTHROPIC_API_KEY, model: 'claude-sonnet-4-6' }),
memory: createInMemoryMemory(),
})
await controller.send('Hello!')
console.log(controller.getState().messages)
Durable confirmations and content parts
controller.send accepts a string or ContentPart[]; parts are retained on the user message and content is their text projection. The shared ChatReturn.send signature also accepts parts; existing string callers remain compatible. Structural ChatController implementations must provide the required decide method.
Configure ChatConfig.decisionStore with a conversation-scoped ToolDecisionStore, then call await controller.decide(toolCallId, 'approve') or await controller.decide(toolCallId, 'deny', reason). A controller uses its live conversation when present, including unsaved turns; otherwise it loads configured chat memory, preserving later turns; the claimed snapshot is a fallback only when no current conversation exists. It patches and persists the target tool outcome. The model resumes only for the latest completed assistant message when every confirmation in that target assistant message is decided; tool results follow their originating assistant message in call order. Pending confirmations are saved to configured chat memory and the decision store before proposal completion.
The store implements insert-if-absent putPending, get, atomic claim (pending → claimed, returning a snapshot only to the winner), and settle (persist the matching claim's terminal outcome). Store records must survive process restart, and methods must be linearizable per tool call. An asynchronous read followed by a write is not an atomic claim: use a database conditional update/transaction or an equivalent compare-and-swap, including across processes. Use an application-authorized conversation scope. A database claim can use UPDATE ... WHERE status = 'pending' RETURNING ...; core ships no database implementation.
Identical decisions are idempotent even while claimed: they return the recorded outcome, or the original call while execution is in flight, without invoking the tool or model again. Streamed confirmations are registered before onToolCall. After restart, streamed decisions require current ChatMemory: arrival snapshots are immutable and may omit later calls; without a current conversation, AK_CONFIG_INVALID rejects an incomplete streaming snapshot before claiming. A live controller does not require memory.
Idempotency compares the decision only; a repeated denial with a different reason retains the original reason and outcome. Same-decision terminal replay returns the recorded ToolCall without invoking the tool or model. Unknown IDs raise AK_ACTION_NOT_FOUND; a conflicting decision or a target that is no longer pending raises AK_ACTION_ALREADY_DECIDED. Tool failures are terminal and are fed to the model; retry requires a new proposal ID. A crash after claim leaves an indeterminate record for application reconciliation, never automatic side-effect retry. Serialize different calls in a conversation to protect transcript writes.
For a stuck claimed record, an operator must inspect get(id) and domain execution evidence, stop the original worker, and terminally settle the matching claim with a proven outcome or an explicit indeterminate failure. Never delete or reset the claim to pending. Reconcile the target call in current chat memory, then continue explicitly with a new user turn; any authorized retry needs a new proposal ID. See the operational recovery procedure in ADR 0042.
approve and deny are deprecated. With a decision store they delegate to decide and share its atomic claim and typed errors; without a store they retain generation-local behavior. See ADR 0042.
Features
createChatController— streaming-capable chat state machine with abort support; memory is saved after successful turns, never after failed or aborted turns. Background memory and skill activation failures are surfaced throughonError.createInMemoryMemory— zero-config in-process memory for prototypingcreateLocalStorageMemory— browser/demo persistence; malformed stored data raisesMemoryErrorinstead of silently becoming an empty historycreateStaticRetriever— deterministic token-overlap retrieval for demos and fallbacks; use@agentskit/ragfor semantic production retrievalparseToolArgs— typed JSON argument parsing for adapters that need fail-closed validation;safeParseArgsremains the compatibility helpercreateId(prefix)— prefix plus a cryptographically random UUID;generateId(prefix)remains the compatibility helper used for messages and calls- TypeScript types for every contract:
ToolDefinition,SkillDefinition,AgentEvent,Adapter,ChatMemory,Retriever,ChatController - Event emitter for
AgentEventstreams — observability hooks attach here AgentEvent.correlationis an optional, provider-neutral identity envelope:operationIdis stable across boundaries whilerunId,sessionId,turnId,actionId, andtraceIdretain local meaning- Dual CJS/ESM output, strict TypeScript, no
any - Static source guardrail rules are available from
@agentskit/core/rulesasCORE_RULES; they are intended for tooling and are not imported by the core runtime.
Error handling
AgentsKit ships a didactic error system inspired by the Rust compiler. Every error includes a code, a hint for the fix, and a docsUrl — no more vague "Something went wrong" messages.
import {
AgentsKitError,
AdapterError,
ToolError,
MemoryError,
ConfigError,
ErrorCodes,
} from '@agentskit/core'
try {
await runtime.run(task)
} catch (err) {
if (err instanceof ToolError) {
// err.code → 'AK_TOOL_EXEC_FAILED'
// err.hint → actionable suggestion
// err.docsUrl → https://www.agentskit.io/docs/agents/tools
console.error(err.toString())
// error[AK_TOOL_EXEC_FAILED]: ...
// --> Hint: ...
// --> Docs: https://www.agentskit.io/docs/agents/tools
}
}
Available error codes (via ErrorCodes):
| Code | Thrown by |
|---|---|
AK_ADAPTER_MISSING | adapter not provided to the controller |
AK_ADAPTER_STREAM_FAILED | streaming call to the provider fails |
AK_TOOL_NOT_FOUND | requested tool name is not registered |
AK_TOOL_EXEC_FAILED | execute() throws |
AK_TOOL_PEER_MISSING | optional tool peer dependency is not installed |
AK_TOOL_INVALID_INPUT | tool arguments or proposal are invalid |
AK_TOOL_QUOTA_EXCEEDED | tool execution exceeds its configured quota |
AK_TOOL_FORBIDDEN | tool execution is denied by policy |
AK_MEMORY_LOAD_FAILED | memory.load() fails |
AK_MEMORY_SAVE_FAILED | memory.save() fails |
AK_MEMORY_CLEAR_FAILED | memory.clear() fails |
AK_MEMORY_DESERIALIZE_FAILED | persisted state is corrupt |
AK_MEMORY_PEER_MISSING | optional memory backend is not installed |
AK_MEMORY_REMOTE_HTTP | remote memory request fails |
AK_CONFIG_INVALID | required config is missing or wrong type |
AK_RUNTIME_INVALID_INPUT | runtime input is invalid |
AK_RUNTIME_STEP_FAILED | a runtime step fails |
AK_RUNTIME_DELEGATE_FAILED | delegated agent execution fails |
AK_SANDBOX_DENIED | sandbox policy denies execution |
AK_SANDBOX_INVALID_TOOL | tool is not valid for the sandbox |
AK_SANDBOX_PEER_MISSING | optional sandbox backend is not installed |
AK_SANDBOX_BACKEND_FAILED | sandbox backend fails |
AK_SKILL_INVALID | skill definition is invalid |
AK_SKILL_DUPLICATE | skill identity is duplicated |
Type-safe tools with defineTool
defineTool infers the TypeScript type of execute's args parameter from the JSON Schema — no manual casting.
import { defineTool } from '@agentskit/core'
const greet = defineTool({
name: 'greet',
schema: {
type: 'object',
properties: { name: { type: 'string' } },
required: ['name'],
} as const, // as const is required for inference
execute(args) {
// args.name → string (inferred, not cast)
return `Hello, ${args.name}!`
},
})
Use InferSchemaType<typeof schema> to reference the inferred type elsewhere in your codebase.
Subpath exports (tree-shaken, zero main-bundle weight)
| Subpath | Purpose |
|---|---|
@agentskit/core/agent-schema | Declarative YAML/JSON agent definitions + validator |
@agentskit/core/prompt-experiments | A/B prompts with PostHog / GrowthBook / custom flag providers |
@agentskit/core/auto-summarize | ChatMemory wrapper that folds old turns into a summary |
@agentskit/core/hitl | Approval gates + ApprovalStore |
@agentskit/core/security | PII and secret redaction + injection detector + rate limiter |
@agentskit/core/fuzzy-match | Deterministic Jaro-Winkler matching for KYC, sanctions, deduplication, and entity resolution |
@agentskit/core/finding | Canonical Finding / Severity shape for review and compliance results |
@agentskit/core/compose-tool | Chain N tools into one macro tool |
@agentskit/core/self-debug | Retry failing tools with LLM-corrected arguments |
@agentskit/core/generative-ui | Typed UI element tree + code / markdown / html / chart artifacts |
@agentskit/core/a2a | Agent-to-Agent protocol spec (JSON-RPC over any transport) |
@agentskit/core/manifest | Skill + tool manifest format (MCP-compatible) |
@agentskit/core/eval-format | Portable eval dataset + run-result JSON |
@agentskit/core/memory-validation | Bounded validation for untrusted serialized memory records |
@agentskit/core/tool-proposal | Public helper for routing a validated proposal through controller authorization |
@agentskit/core/hash | RFC 8785 canonical JSON and SHA-256 helpers |
See the core guide for agents for the full contract.
Ecosystem
| Package | Role |
|---|---|
| @agentskit/adapters | LLM chat + embedding providers, router, ensemble, fallback |
| @agentskit/runtime | createRuntime, speculate, topologies, durable execution, background agents |
| @agentskit/react | useChat, headless chat components |
| @agentskit/vue · svelte · solid · react-native · angular | Same ChatReturn contract, one package per framework |
| @agentskit/tools | Built-in tools, 20+ integrations, MCP bridge |
| @agentskit/memory | Chat + vector + hierarchical + encrypted + graph stores |
| @agentskit/rag | Plug-and-play RAG + reranker + loaders |
| @agentskit/skills | Ready-made personas + marketplace |
| @agentskit/observability | Traces, audit log, cost guard, devtools |
| @agentskit/sandbox | Secure code execution + mandatory sandbox policy |
| @agentskit/eval | Eval suites, deterministic replay, snapshots, CI reporter |
| @agentskit/cli | agentskit init / chat / run / ai / dev / doctor |
Contributors
License
MIT — see LICENSE.
Docs
Maturity and compatibility
- Stability: stable — see docs/STABILITY.md
- Node.js 20+ and TypeScript strict mode
- Published as
@agentskit/core
Contributing
See CONTRIBUTING.md and the monorepo LICENSE.