|
1 | 1 | # A3S Code |
2 | 2 |
|
3 | | -**Rust framework for building agentic AI agents** — Embed agents that read, write, and execute code into any application. Native Node.js and Python bindings included. |
| 3 | +**Embed AI coding agents into any application.** A3S Code is a Rust library with native Python and Node.js bindings. Give an LLM a workspace, a set of tools, and a system prompt — it reads files, runs commands, searches code, and acts on results. |
4 | 4 |
|
5 | | -## Features |
| 5 | +[](https://crates.io/crates/a3s-code-core) |
| 6 | +[](https://pypi.org/project/a3s-code/) |
| 7 | +[](https://www.npmjs.com/package/@a3s-lab/code) |
| 8 | +[](LICENSE) |
6 | 9 |
|
7 | | -- **16 Built-in Tools** — File operations (read, write, edit, patch), search (grep, glob, ls), shell (bash), web (web_fetch, web_search), git (git_worktree), delegation (task, parallel_task, run_team, batch, Skill) |
8 | | -- **Plugin Tools** — Optional tools loaded on demand: `agentic_search` (multi-phase semantic search), `agentic_parse` (LLM-enhanced document parsing). Each plugin automatically registers its companion skill. |
9 | | -- **8 Built-in Skills** — Code assistance (agentic-search, code-search, code-review, explain-code, find-bugs) + tool documentation (builtin-tools, delegate-task, find-skills) |
10 | | -- **Plugin System** — Unified `Plugin` trait for mounting optional tool+skill bundles. Plugins register tools into `ToolRegistry` and companion skills into `SkillRegistry` on load. |
11 | | -- **20 Extension Points** — Trait-based architecture: replace any policy with your own implementation |
12 | | -- **Safe by Default** — Permission system, HITL confirmation, skill-based tool restrictions, error recovery (parse retries, tool timeout, circuit breaker) |
13 | | -- **Multi-Provider** — Anthropic, OpenAI, DeepSeek, Kimi, Together AI, Groq, Ollama, vLLM, any OpenAI-compatible API |
14 | | -- **MCP Integration** — Connect external tool servers via Model Context Protocol |
15 | | -- **Scalable** — Lane-based priority queue with multi-machine task distribution |
| 10 | +--- |
16 | 11 |
|
17 | | -## Installation |
| 12 | +## Install |
18 | 13 |
|
19 | 14 | ```bash |
| 15 | +# Python |
20 | 16 | pip install a3s-code |
| 17 | + |
| 18 | +# Node.js |
| 19 | +npm install @a3s-lab/code |
| 20 | + |
| 21 | +# Rust |
| 22 | +cargo add a3s-code-core |
21 | 23 | ``` |
22 | 24 |
|
| 25 | +--- |
| 26 | + |
23 | 27 | ## Quick Start |
24 | 28 |
|
| 29 | +**1. Create an agent config** (`agent.hcl`): |
| 30 | + |
| 31 | +```hcl |
| 32 | +default_model = "anthropic/claude-sonnet-4-20250514" |
| 33 | +
|
| 34 | +providers { |
| 35 | + name = "anthropic" |
| 36 | + api_key = env("ANTHROPIC_API_KEY") |
| 37 | +} |
| 38 | +``` |
| 39 | + |
| 40 | +**2. Run an agent session:** |
| 41 | + |
25 | 42 | ```python |
26 | 43 | from a3s_code import Agent |
27 | 44 |
|
28 | 45 | agent = Agent.create("agent.hcl") |
29 | 46 | session = agent.session("/my-project") |
30 | 47 |
|
31 | | -result = session.send("What files handle authentication?") |
| 48 | +result = session.send("Find all places where we handle authentication errors") |
32 | 49 | print(result.text) |
33 | 50 | ``` |
34 | 51 |
|
35 | | -## Plugin Tools |
36 | | - |
37 | | -`agentic_search` and `agentic_parse` are opt-in plugins — not loaded by default. Mount them explicitly per session: |
38 | | - |
39 | 52 | ```typescript |
40 | | -// Node.js |
41 | | -import { Agent, AgenticSearch, AgenticParse } from '@a3s-lab/code'; |
| 53 | +import { Agent } from '@a3s-lab/code'; |
42 | 54 |
|
43 | 55 | const agent = await Agent.create('agent.hcl'); |
44 | | -const session = agent.session('.', { |
45 | | - plugins: [new AgenticSearch(), new AgenticParse()], |
46 | | -}); |
| 56 | +const session = agent.session('/my-project'); |
| 57 | + |
| 58 | +const result = await session.send('Find all places where we handle authentication errors'); |
| 59 | +console.log(result.text); |
| 60 | +``` |
| 61 | + |
| 62 | +```rust |
| 63 | +use a3s_code_core::Agent; |
| 64 | + |
| 65 | +let agent = Agent::from_file("agent.hcl").await?; |
| 66 | +let session = agent.session("/my-project", Default::default()).await?; |
| 67 | +let result = session.send("Find all places where we handle authentication errors").await?; |
| 68 | +println!("{}", result.text); |
47 | 69 | ``` |
48 | 70 |
|
| 71 | +--- |
| 72 | + |
| 73 | +## What the LLM Can Do |
| 74 | + |
| 75 | +**16 built-in tools** — always available, no configuration: |
| 76 | + |
| 77 | +| Category | Tools | |
| 78 | +|----------|-------| |
| 79 | +| Files | `read`, `write`, `edit`, `patch` | |
| 80 | +| Search | `grep`, `glob`, `ls` | |
| 81 | +| Shell | `bash` | |
| 82 | +| Web | `web_fetch`, `web_search` | |
| 83 | +| Git | `git_worktree` | |
| 84 | +| Delegation | `task`, `parallel_task`, `run_team`, `batch`, `Skill` | |
| 85 | + |
| 86 | +**Plugin tools** — opt-in, loaded per session: |
| 87 | + |
| 88 | +| Plugin | Tool | What it does | |
| 89 | +|--------|------|--------------| |
| 90 | +| `AgenticSearch` | `agentic_search` | Natural-language code search with IDF-weighted relevance ranking | |
| 91 | +| `AgenticParse` | `agentic_parse` | LLM-enhanced parsing for PDF, Word, CSV, code, and more | |
| 92 | + |
49 | 93 | ```python |
50 | | -# Python |
51 | 94 | from a3s_code import Agent, SessionOptions, AgenticSearch, AgenticParse |
52 | 95 |
|
53 | | -agent = Agent.create("agent.hcl") |
54 | 96 | opts = SessionOptions() |
55 | 97 | opts.plugins = [AgenticSearch(), AgenticParse()] |
56 | 98 | session = agent.session(".", opts) |
57 | 99 | ``` |
58 | 100 |
|
59 | | -When a plugin is loaded: |
60 | | -1. Its **tool** is registered into the session's `ToolRegistry` (the LLM can call it) |
61 | | -2. Its **companion skill** is registered into `SkillRegistry` (appears in the system prompt automatically) |
| 101 | +--- |
62 | 102 |
|
63 | | -### Available Plugins |
| 103 | +## Safety and Control |
64 | 104 |
|
65 | | -| Plugin | Tool | Companion Skill | |
66 | | -|--------|------|-----------------| |
67 | | -| `AgenticSearch` | `agentic_search` — multi-phase semantic code search with IDF-weighted relevance | `agentic-search` — guides the LLM on when/how to use the tool | |
68 | | -| `AgenticParse` | `agentic_parse` — LLM-enhanced document parsing (PDF, Word, CSV, code, etc.) | `agentic-parse` — guides the LLM on parse strategies and extraction | |
| 105 | +Agents run with **explicit permissions**. Nothing executes by default without a policy allowing it: |
69 | 106 |
|
70 | | -### Document Parser Support |
| 107 | +```python |
| 108 | +from a3s_code import SessionOptions, PermissionPolicy, PermissionRule |
71 | 109 |
|
72 | | -For binary formats (PDF, Excel, Word), pass a `DocumentParserRegistry` through the plugin option: |
| 110 | +opts = SessionOptions() |
| 111 | +opts.permission_policy = PermissionPolicy( |
| 112 | + allow=[PermissionRule("read(*)"), PermissionRule("grep(*)")], |
| 113 | + deny=[PermissionRule("bash(*)")], |
| 114 | + default_decision="deny", |
| 115 | +) |
| 116 | +session = agent.session(".", opts) |
| 117 | +``` |
73 | 118 |
|
74 | | -```typescript |
75 | | -import { AgenticParse, DocumentParserRegistry } from '@a3s-lab/code'; |
| 119 | +Other safety features: |
| 120 | +- **Human-in-the-loop confirmation** — prompt before any tool call |
| 121 | +- **Skill-based tool restrictions** — `allowed-tools` in skill frontmatter limits what the LLM can call |
| 122 | +- **AHP integration** — plug in an external harness to block or sanitize tool calls at runtime |
| 123 | +- **Auto-compact** — rolls up context before hitting token limits, keeping sessions running |
76 | 124 |
|
77 | | -const session = agent.session('.', { |
78 | | - plugins: [new AgenticParse({ documentParserRegistry: new DocumentParserRegistry() })], |
79 | | -}); |
80 | | -``` |
| 125 | +--- |
81 | 126 |
|
82 | | -## Slash Commands |
| 127 | +## Persistence and Memory |
83 | 128 |
|
84 | | -Every session includes built-in slash commands dispatched before the LLM: |
| 129 | +Sessions can be saved and resumed. Memory persists across sessions: |
85 | 130 |
|
86 | 131 | ```python |
87 | | -# List all available commands |
88 | | -commands = session.list_commands() |
89 | | -for cmd in commands: |
90 | | - print(f"/{cmd['name']:15s} {cmd['description']}") |
91 | | - |
92 | | -# Built-in commands |
93 | | -result = session.send("/help") # List all commands |
94 | | -result = session.send("/model") # Show current model |
95 | | -result = session.send("/cost") # Token usage and cost |
96 | | -result = session.send("/history") # Conversation stats |
97 | | -result = session.send("/cron-list") # List scheduled tasks |
| 132 | +from a3s_code import SessionOptions, FileSessionStore, FileMemoryStore |
| 133 | + |
| 134 | +opts = SessionOptions() |
| 135 | +opts.session_store = FileSessionStore('./sessions') |
| 136 | +opts.memory_store = FileMemoryStore('./memory') |
| 137 | +opts.session_id = 'my-session' |
| 138 | +opts.auto_save = True |
| 139 | + |
| 140 | +session = agent.session(".", opts) |
| 141 | +resumed = agent.resume_session('my-session', opts) |
98 | 142 | ``` |
99 | 143 |
|
100 | | -### Custom Commands |
| 144 | +--- |
101 | 145 |
|
102 | | -```python |
103 | | -def my_handler(args: str, ctx: dict) -> str: |
104 | | - return f"Model: {ctx['model']}, History: {ctx['history_len']} msgs, args: {args!r}" |
| 146 | +## Multi-Provider |
105 | 147 |
|
106 | | -session.register_command("status", "Show session info", my_handler) |
107 | | -result = session.send("/status hello") |
108 | | -``` |
| 148 | +One config, any LLM: |
109 | 149 |
|
110 | | -## Scheduled Tasks |
| 150 | +```hcl |
| 151 | +default_model = "anthropic/claude-sonnet-4-20250514" |
| 152 | +
|
| 153 | +providers { name = "anthropic"; api_key = env("ANTHROPIC_API_KEY") } |
| 154 | +providers { name = "openai"; api_key = env("OPENAI_API_KEY") } |
| 155 | +providers { name = "deepseek"; api_key = env("DEEPSEEK_API_KEY") } |
| 156 | +providers { name = "kimi"; api_key = env("MOONSHOT_API_KEY") } |
| 157 | +providers { name = "together"; api_key = env("TOGETHER_API_KEY") } |
| 158 | +providers { name = "groq"; api_key = env("GROQ_API_KEY") } |
| 159 | +``` |
111 | 160 |
|
112 | | -Schedule recurring prompts that fire after each `send()` call: |
| 161 | +Switch model per session: |
113 | 162 |
|
114 | 163 | ```python |
115 | | -# Via /loop slash command |
116 | | -r = session.send("/loop 30s check deployment status") |
117 | | -print(r.text) # Scheduled [a1b2c3d4]: "check deployment status" — fires every 30s |
| 164 | +session = agent.session(".", model="openai/gpt-4o") |
| 165 | +``` |
118 | 166 |
|
119 | | -# Programmatic API |
120 | | -task_id = session.schedule_task("summarize recent commits", 300) # every 5 min |
| 167 | +--- |
121 | 168 |
|
122 | | -# List active tasks |
123 | | -for t in session.list_scheduled_tasks(): |
124 | | - print(f"[{t['id']}] every {t['interval_secs']}s — \"{t['prompt']}\"") |
| 169 | +## Skills |
125 | 170 |
|
126 | | -# Cancel |
127 | | -session.cancel_scheduled_task(task_id) |
128 | | -session.send(f"/cron-cancel {task_id}") |
129 | | -``` |
| 171 | +Skills are markdown files that shape LLM behavior — injected into the system prompt automatically: |
130 | 172 |
|
131 | | -**Interval syntax:** `30s`, `5m`, `2h`, `1d`. Leading or trailing with `every` clause. |
| 173 | +```markdown |
| 174 | +--- |
| 175 | +name: safe-reviewer |
| 176 | +description: Review code without modifying files |
| 177 | +allowed-tools: "read(*), grep(*), glob(*)" |
| 178 | +--- |
132 | 179 |
|
133 | | -## Full API |
| 180 | +Review the code in the workspace. You may read and search files, |
| 181 | +but you must not write, edit, or execute anything. |
| 182 | +``` |
134 | 183 |
|
135 | 184 | ```python |
136 | | -from a3s_code import Agent, SessionOptions, DefaultSecurityProvider, FileMemoryStore, FileSessionStore |
137 | | -from a3s_code import AgenticSearch, AgenticParse |
| 185 | +opts = SessionOptions() |
| 186 | +opts.skill_dirs = ["./skills"] |
| 187 | +session = agent.session(".", opts) |
| 188 | +``` |
138 | 189 |
|
139 | | -agent = Agent.create("agent.hcl") |
140 | | -session = agent.session("/my-project", |
141 | | - model="openai/gpt-4o", |
142 | | - builtin_skills=True, |
143 | | - planning=True, |
144 | | -) |
| 190 | +Built-in skills (enabled via `builtin_skills=True`): `agentic-search`, `code-search`, `code-review`, `explain-code`, `find-bugs`, `builtin-tools`, `delegate-task`, `find-skills`. |
145 | 191 |
|
146 | | -# Plugin tools (opt-in) |
147 | | -opts = SessionOptions() |
148 | | -opts.plugins = [AgenticSearch(), AgenticParse()] |
149 | | -session2 = agent.session(".", opts) |
150 | | - |
151 | | -# Send / Stream |
152 | | -result = session.send("Explain the auth module") |
153 | | -for event in session.stream("Refactor auth"): |
154 | | - if event.event_type == "text_delta": |
155 | | - print(event.text, end="", flush=True) |
156 | | - |
157 | | -# Direct tools (bypass LLM) |
158 | | -session.read_file("src/main.py") |
159 | | -session.bash("pytest") |
160 | | -session.glob("**/*.py") |
161 | | -session.grep("TODO") |
162 | | - |
163 | | -# Slash commands & scheduling |
164 | | -session.list_commands() |
165 | | -session.register_command("ping", "Pong!", lambda args, ctx: "pong") |
166 | | -task_id = session.schedule_task("daily report", 86400) |
167 | | -session.list_scheduled_tasks() |
168 | | -session.cancel_scheduled_task(task_id) |
169 | | - |
170 | | -# Memory |
171 | | -session.remember_success("task", ["tool"], "result") |
172 | | -session.recall_similar("auth", 5) |
173 | | - |
174 | | -# Hooks |
175 | | -session.register_hook("audit", "pre_tool_use", handler_fn) |
176 | | - |
177 | | -# MCP |
178 | | -session.add_mcp_server("github", command="npx", args=["-y", "@modelcontextprotocol/server-github"]) |
179 | | -session.mcp_status() |
180 | | -session.tool_names() |
181 | | -session.remove_mcp_server("github") |
182 | | - |
183 | | -# Persistence |
184 | | -opts = SessionOptions() |
185 | | -opts.session_store = FileSessionStore('./sessions') |
186 | | -opts.session_id = 'my-session' |
187 | | -opts.auto_save = True |
188 | | -session2 = agent.session(".", opts) |
189 | | -resumed = agent.resume_session('my-session', opts) |
| 192 | +--- |
| 193 | + |
| 194 | +## Architecture |
| 195 | + |
| 196 | +``` |
| 197 | +Agent (config + provider registry) |
| 198 | + └── Session (workspace + tools + LLM) |
| 199 | + └── AgentLoop (turn-based execution) |
| 200 | + ├── LlmClient → sends messages, receives tool calls |
| 201 | + ├── ToolExecutor → runs tools, enforces permissions |
| 202 | + ├── SkillRegistry → injects skills into system prompt |
| 203 | + └── PluginManager → loads opt-in tool+skill bundles |
190 | 204 | ``` |
191 | 205 |
|
| 206 | +20 trait-based extension points: swap any policy, provider, store, or hook without touching core. |
| 207 | + |
| 208 | +--- |
| 209 | + |
| 210 | +## Documentation |
| 211 | + |
| 212 | +Full reference, examples, and guides: **[a3s.dev/docs/code](https://a3s.dev/docs/code)** |
| 213 | + |
| 214 | +- [Sessions & Options](https://a3s.dev/docs/code/sessions) |
| 215 | +- [Tools](https://a3s.dev/docs/code/tools) |
| 216 | +- [Skills](https://a3s.dev/docs/code/skills) |
| 217 | +- [Plugin System](https://a3s.dev/docs/code/plugins) |
| 218 | +- [Security](https://a3s.dev/docs/code/security) |
| 219 | +- [Examples](https://a3s.dev/docs/code/examples) |
| 220 | + |
| 221 | +--- |
| 222 | + |
192 | 223 | ## License |
193 | 224 |
|
194 | 225 | MIT |
0 commit comments