@classytic/arc-ai
v0.3.0
Published
Agent toolkit on AI SDK v7 — sub-agents, context management, hooks, session, provider tools, memory, multi-tenant scope
Downloads
351
Readme
@classytic/arc-ai
Agent toolkit on AI SDK v7. Composable primitives for production agents — DB-agnostic, tree-shakable, multi-tenant aware.
Not a framework. Small pieces that compose with the AI SDK you already use.
Install
npm i @classytic/arc-ai ai zod
npm i @ai-sdk/anthropic # or @ai-sdk/openai / @ai-sdk/googlearc-ai is provider-agnostic — any AI SDK v7 LanguageModel works. Beyond
the first-party providers above, the same createAgent interface accepts
Ollama (ollama-ai-provider-v2), OpenRouter (@openrouter/ai-sdk-provider —
Kimi K2, DeepSeek, Qwen, Llama, …), vLLM / LM Studio / LocalAI / llama.cpp
via @ai-sdk/openai-compatible, plus Groq / Together / Fireworks / Replicate /
Bedrock / Azure. See skills/arc-ai/references/recipes.md → Picking a provider.
30 seconds
import { createAgent } from '@classytic/arc-ai';
import { anthropic } from '@ai-sdk/anthropic';
import { tool, isStepCount } from 'ai';
import { z } from 'zod';
const agent = await createAgent({
id: 'support',
model: anthropic('claude-haiku-4-5'),
instructions: 'You are a support agent.',
tools: {
lookup: tool({
description: 'Look up an order',
inputSchema: z.object({ orderId: z.string() }),
execute: async ({ orderId }) => db.orders.find(orderId),
}),
},
stopWhen: isStepCount(10),
});
const { text } = await agent.generate({ prompt: 'Where is order 123?' });Skill (read this first)
The package ships a curated skill at skills/arc-ai/:
SKILL.md— entry-point: quickstart, subpath cheat-sheet, pre-flight checklistreferences/recipes.md— opinionated patterns per primitive (memory, sub-agents, sessions, attachments, prompt caching, serverless, picking a provider)references/gotchas.md— 16 sharp edges with one-line fixes
These ship inside node_modules/@classytic/arc-ai/skills/ — open from there if you've installed via npm.
What you get
| Capability | Subpath | Built-in |
|---|---|---|
| Agent lifecycle + hooks | /agent | createAgent, middleware, spawn, callOptionsSchema + prepareCall |
| Per-call typed options (v7) | /agent | callOptionsSchema → prepareCall for runtime model/instructions/tools |
| Self-improvement (Hermes pattern) | /agent/self-improvement | Background review fork that turns agents into learning agents |
| Client-side tool type safety | /agent/infer-ui-message | InferArcAgentUIMessage<typeof agent> for useChat<>() |
| Persistent memory (multi-tenant) | /brain | BrainManager, memoryScope, staleness caveats, cleanup |
| Memory as AI SDK tools | /brain/tools | brainTools(brain, { scope }) → { read_memory, write_memory, forget_memory } |
| Anthropic memory adapter | /brain/anthropic-memory-adapter | anthropicMemoryAdapter(brain, { scope }) for memory_20250818 |
| Sub-agents | /spawn | spawnAgent(child) — child agent as a tool, full UIMessage stream |
| Chat stream endpoint | /session/chat-stream | createChatStream — multimodal-aware, project/folder scoping |
| Multi-provider prompt caching | /caching/* | Anthropic + Bedrock + OpenAI + Google in one helper |
| Multimodal attachments + RAG | /attachments/* | Direct-to-provider files, vector store, chunker, retriever |
| Provider-agnostic tools | /tools | providerTools.webSearch() resolves per provider |
| Image generation + view | /tools | imageGenerationTool, viewImageTool (vision-capable) |
| JSONValue-safe tool outputs | /tools/json-safe, /tools/wrap-output-json-safe | Auto-coerce Date / ObjectId / Buffer / Map / Set / bigint so v7 convertToModelMessages doesn't crash |
| Backend-agnostic filesystem tools | /tools/filesystem | Claude-Code-style read/write/edit/glob/grep |
| Shell tool + sandbox adapter | /tools/shell/* | ShellExecutor interface + Node + sandbox impls |
| Structured edits | /tools/structured-edit/* | smart_edit + multi_edit + apply_patch (V4A) |
| Question / ask-the-user tool | /tools/question/* | HITL inbox + AI SDK tool factory |
| MCP servers as tools | /mcp | loadMcpConfig('.mcp.json') |
| Tool output storage | /tools/output-storage/* | Locator-based persistence, in-memory + S3 |
| Mid-loop message injection | /messaging/* | Queue, UI injection, event bridge, Redis |
| Permissions + approval | /permissions, /approval | 4-layer policy (allow/ask/deny/deny-always) |
| Sandbox abstraction | /sandbox/* | Sandbox contract + in-memory / local-process / E2B / Vercel adapters |
| Guardrails (input/output/step) | /guardrails/* | Regex, keyword-deny, max-length, Zod schema, llm-as-judge |
| Agent-to-agent handoffs | /handoffs/* | First-class routing + history filters + nested-history summariser |
| Swarm coordination | /swarm/* | Agents that share context + hand off via shared bus events |
| Orchestration patterns | /orchestration/* | Concurrent / mixture-of-agents / director / keepalive |
| Runs (audit ledger) | /runs/* | Canonical queryable RunRecord audit trail via onStepFinish/onFinish |
| Run-state machine | /run-state/* | Idle / awaiting_question / awaiting_approval / paused / completed / etc. |
| Event bus | /bus/* | Typed cross-subsystem observability bus + per-source bridges |
| Tracing (OTel + console) | /tracing/* | Bus-driven span export; ConsoleTracingBridge (dev) + OtelBridge (peer) |
| Checkpoint (state branching) | /checkpoint/* | Point-in-time agent state for HITL / eval / branching |
| UI streaming primitives | /ui | composeStream, createUIMessageStream re-exports |
| Model middleware | /middleware | wrapModel, extractReasoning, defaultSettings |
| Model registry / resolution | /registry/* | resolveModel(spec, { registry }) for string-routed multi-tenant configs |
| Serverless-safe stream drainers | /stream/* | consumeAgentStream, consumeChatStream for Trigger/Inngest/Temporal |
| Streamline integration | /integrations/streamline | Durable-workflow bridge (peer: @classytic/streamline) |
| Usage / cost / stop conditions | /usage, /usage/accumulate | Per-request token+cost accumulation, costExceeds / tokenBudget |
| Skills | /skills, /skills/manage-tool, /skills/parse-frontmatter, /skills/curator | Markdown skills loaded into agents, manage-skill tool, curator review |
| Test contracts | /testing, /testing/*-contract | Mock models + reusable store / sandbox / filesystem contracts |
| Mongo stores | /stores/mongo/* | Brain, Conversation, Metadata, Agent, Skill, Vector |
| SQLite stores | /stores/sqlite/* | Brain, Conversation (drizzle-orm + sqlitekit) |
| S3-decorated brain + tool output | /stores/s3/* | Content→S3, metadata→delegate |
Multi-tenant memory (the typical app case)
Same agent, many companies, no cross-tenant leaks:
import { BrainManager, memoryScope } from '@classytic/arc-ai/brain';
import { MongoBrainStore } from '@classytic/arc-ai/stores/mongo/brain';
const brain = new BrainManager({
store: new MongoBrainStore(),
staleThresholdDays: 14,
});
// Write: most-specific scope the app knows
await brain.remember({
type: 'feedback',
name: 'tone',
content: `Be formal.\n**Why:** Enterprise customer.\n**How to apply:** Every reply.`,
scope: memoryScope.agent(req.user.companyId, 'support'),
});
// Read: hierarchical — agent + user + company + global, newest wins
const ctx = await brain.assembleContext(userPrompt, {
scope: memoryScope.hierarchy({
companyId: req.user.companyId,
agentSlug: 'support',
userId: req.user.id,
}),
});
// Offboard — one call, subtree wipe, global untouched
await brain.forgetScope(
memoryScope.hierarchy({
companyId: offboardedCompanyId,
includeGlobal: false,
}),
);Chat endpoint (one call, whole turn)
import { createChatStream } from '@classytic/arc-ai/session/chat-stream';
import { SessionManager } from '@classytic/arc-ai/session/session-manager';
const { response } = await createChatStream({
agent, brain, session,
message: req.body.message,
agentSlug: 'support',
userId: req.user.id,
organizationId: req.user.companyId,
projectId: req.body.projectId, // indexed; cheap list queries
folderPath: req.body.folderPath, // indexed; prefix match
});
return response; // SSE Web Response, x-conversation-id header setDocs
docs/ — task-oriented, token-optimized. Nextra-ready .mdx.
| # | Topic |
|---|---|
| 1 | Quickstart |
| 2 | Agents — createAgent, middleware, hooks, spawn, reasoning |
| 3 | Memory — BrainManager, scope, cleanup, multi-tenant |
| 4 | Sessions — chat stream, project/folder scoping |
| 5 | Tools — providerTools, filesystem, MCP |
| 6 | Messaging & UI — queue, UI injection, composeStream |
| 7 | Testing — mock models, FS contract, store contracts |
| 8 | Mongo Stores — mongokit, defineResource, cleanup |
| 9 | Group Chats — visibility, participants, mentions, ACL |
| 10 | Prompt Caching — multi-provider, tool defs, composition |
| 11 | Patterns — composing prepareStep helpers + queue + cache |
| 12 | Attachments — multimodal direct-to-provider + vector-search RAG |
| 13 | Serverless — drain streams in Trigger / Inngest / Temporal / Lambda |
| 14 | Sandbox — Sandbox interface + reference impl |
| 15 | Image Generation — imageGenerationTool + storage + viewImageTool |
| 16 | SQLite Stores — sqlitekit + drizzle-orm |
| 17 | Provider Capabilities — feature matrix per provider |
| 18 | Handoffs — agent-to-agent routing, history filters, nested summariser |
| 19 | Tracing — bus-driven span export (ConsoleTracingBridge + OtelBridge) |
| 20 | Remote Sandboxes — E2B + Vercel Sandbox peer-optional adapters |
| 21 | Swarm — coordinated agents over a shared bus |
| 22 | Orchestration Patterns — concurrent, mixture-of-agents, director |
| 23 | Plan Mode (recipe) — gated planning before execution |
| 24 | Streaming + Keepalive — long-running stream survival
Attachments — multimodal + RAG in one module
Two paths share a single subpath family. Direct-to-provider (vision, PDF) goes through extractFileParts + convertToModelMessages; vector-search RAG (chunked + embedded + retrieved) is provider-blind.
import { chunkText } from '@classytic/arc-ai/attachments/chunk-text';
import { embedTexts } from '@classytic/arc-ai/attachments/embed-texts';
import { InMemoryVectorStore } from '@classytic/arc-ai/attachments/vector-store';
import { retrieveChunks, formatChunksForPrompt }
from '@classytic/arc-ai/attachments/retrieve-chunks';
import { openai } from '@ai-sdk/openai';
const chunks = chunkText(documentText);
const { embeddings } = await embedTexts({
model: openai.embedding('text-embedding-3-small'),
values: chunks.map((c) => c.content),
});
const store = new InMemoryVectorStore();
await store.upsert(chunks.map((c, i) => ({
id: `${fileId}:${c.index}`, scope: `co:${companyId}`, fileId,
chunkIndex: c.index, content: c.content, embedding: embeddings[i]!,
})));
const hits = await retrieveChunks({
store, embeddingModel: openai.embedding('text-embedding-3-small'),
query: userMessage, scope: `co:${companyId}`,
});
const ragSystemBlock = formatChunksForPrompt(hits);Direct-to-provider needs no glue beyond createChatStream — it already routes file parts through convertToModelMessages. For persistent provider files (Anthropic + OpenAI Files API) use /attachments/upload-to-provider to get back a stable ProviderFileReference.
When NOT to use arc-ai
- Durable workflows (retries, resume, HITL gates) →
@classytic/streamline - REST resource scaffolding →
@classytic/arc - Object storage / blob uploads →
@classytic/mongokit/media(use with arc-ai's attachment helpers, not instead)
arc-ai complements these. It does not replace them.
Requirements
Node.js 22+ · TypeScript 6+ · ESM-only · AI SDK 7.0+ · zod 4+
License
Proprietary — © Classytic. All rights reserved. See LICENSE.
The published npm package (@classytic/arc-ai) is free to install and use in
your applications. The source code is not open source; redistribution,
modification, or re-publication of the source is not permitted without prior
written permission.
