Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [4.0.0] - 2026-06-21

Milestone release: **filesystem-first agents** ("eve parity"). A single directory
Milestone release: **filesystem-first agents**. A single directory
now defines a durable agent by convention — `instructions.md` (role slot),
`agent.acl` (config), `skills/`, `schedules/` (cron), and `tools/` — served by a
`serve` daemon that runs each schedule as a full harness turn. No breaking changes
Expand Down
54 changes: 54 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1593,6 +1593,59 @@ await session.addMcp({

---

## Filesystem-First Agents

A durable agent can be defined entirely by a **directory** — no code — and run by
the built-in `serve` daemon. One folder holds the agent's role, config, skills,
cron schedules, and tools:

```text
my-agent/
├── instructions.md (required) Role/guidelines — injected as a prompt SLOT.
├── agent.acl (optional) Model, providers, queue.
├── skills/ (optional) *.md skills.
├── schedules/ (optional) *.md cron jobs (frontmatter `cron:` + body prompt).
└── tools/ (optional) *.md tools: `kind: mcp` (MCP server) or
`kind: script` (sandboxed QuickJS over `program`).
```

`AgentDir::load` synthesizes the existing config objects from that folder — it adds
no new runtime or prompt system. `serve_agent_dir` then runs each enabled schedule
on its own durable session, and every fire is a FULL harness turn (context, tool
visibility, safety gate, verification), never a raw model call. `instructions.md`
is a prompt *slot*, so the harness keeps `BOUNDARIES`, the response contract, and
verification authoritative.

```rust
use a3s_code_core::{Agent, config::AgentDir, serve::serve_agent_dir};
use tokio_util::sync::CancellationToken;

let agent_dir = AgentDir::load("./my-agent")?;
let agent = Agent::from_config(agent_dir.config.clone()).await?;
serve_agent_dir(&agent, &agent_dir, "./workspace", None, CancellationToken::new()).await?;
```

From the SDKs:

```js
const handle = await agent.serveAgentDir('./my-agent', './workspace')
// ... runs in the background until:
await handle.stop()
```

- **`tools/`** — `kind: mcp` registers an MCP server (namespaced
`mcp__<server>__<tool>`); `kind: script` exposes a sandboxed QuickJS tool over the
`program` path, with a fail-closed `allowed_tools` allow-list (an omitted list
grants no tools).
- **Rehydrate-on-boot** — pass a `SessionStore` and each schedule session resumes
its accumulated context across daemon restarts (history is restored; the current
`instructions.md` / `skills/` / `tools/` are re-applied each boot).
- Gated behind the `serve` Cargo feature; library-only embedders pay nothing.

See [Filesystem-First Agents](https://a3s-lab.github.io/a3s/docs/code/agent-dir) for the full guide.

---

## Slash Commands

Sessions support slash commands:
Expand Down Expand Up @@ -1683,6 +1736,7 @@ Full reference and guides: [a3s-lab.github.io/a3s/docs/code](https://a3s-lab.git
- [Tools & Structured Output](https://a3s-lab.github.io/a3s/docs/code/tools)
- [AHP Protocol](https://a3s-lab.github.io/a3s/docs/code/ahp-integration)
- [Skills](https://a3s-lab.github.io/a3s/docs/code/skills)
- [Filesystem-First Agents](https://a3s-lab.github.io/a3s/docs/code/agent-dir)
- [Memory](https://a3s-lab.github.io/a3s/docs/code/memory)
- [Security](https://a3s-lab.github.io/a3s/docs/code/security)
- [Hooks](https://a3s-lab.github.io/a3s/docs/code/hooks)
Expand Down
8 changes: 4 additions & 4 deletions core/src/config/agent_dir.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! Filesystem-first agent directory convention (eve-style, harness-respecting).
//! Filesystem-first agent directory convention (harness-respecting).
//!
//! A single directory defines a durable agent by convention:
//!
Expand All @@ -19,7 +19,7 @@
//! [`AgentDir::load`] SYNTHESIZES existing config objects rather than adding a new
//! runtime: `instructions.md` → [`SystemPromptSlots`], `agent.acl` → [`CodeConfig`],
//! `skills/` → `skill_dirs`. Tool definition, visibility, and safety stay
//! harness-owned (the deliberate divergence from eve's user-defined-tools model).
//! harness-owned (the deliberate divergence from user-defined-tools models).

use std::path::{Path, PathBuf};

Expand All @@ -44,7 +44,7 @@ pub struct ScheduleSpec {
/// A tool definition parsed from `tools/<name>.md`, dispatched by `kind`.
///
/// Tool *definition* may come from the directory, but visibility and safety stay
/// harness-owned (the deliberate divergence from eve): an `mcp` spec is registered
/// harness-owned (a deliberate divergence from user-defined-tools models): an `mcp` spec is registered
/// through the normal [`add_mcp_server`](crate::AgentSession) path, so its tools
/// are namespaced `mcp__<server>__<tool>` and gated by the session's permission
/// policy like any other tool.
Expand Down Expand Up @@ -121,7 +121,7 @@ pub struct ScriptToolLimits {
///
/// Distinct from [`CodeConfig::agent_dirs`](crate::config::CodeConfig) /
/// `register_agent_dir`, which scan a directory for **worker/subagent**
/// definitions. An `AgentDir` is the eve-style *primary* agent — the directory
/// definitions. An `AgentDir` is the filesystem-first *primary* agent — the directory
/// that defines this agent's prompt, skills, schedules, and tools.
#[derive(Debug, Clone)]
pub struct AgentDir {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! End-to-end integration test for the eve-style filesystem-first agent directory
//! End-to-end integration test for the filesystem-first agent directory
//! convention: a single on-disk directory with EVERY supported sub-convention
//! (instructions, agent.acl, skills/, schedules/, tools/) loads into a
//! fully-populated [`AgentDir`]. Hermetic — no provider, no network.
Expand Down
4 changes: 2 additions & 2 deletions manual/AGENT_DIR_TOOLS_DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ The loader (`load_tools` in `core/src/config/agent_dir.rs`) parses both into
session build — MCP via `add_mcp_server`, script via the new
`AgentDirScriptTool` (`core/src/tools/agent_dir_script_tool.rs`), a thin facade
over the existing `program` QuickJS path. Scope: how the optional `tools/`
subdirectory of an eve-style agent directory becomes *executable* tools in
subdirectory of a filesystem-first agent directory becomes *executable* tools in
A3S Code without ever running arbitrary host JavaScript or arbitrary host
processes.

Expand All @@ -15,7 +15,7 @@ processes.
> NEVER turned into a free-running host JS/native process, and it NEVER gets to
> define its own tool-visibility or safety policy. Tool *definition* is allowed
> from the directory; tool *visibility* and *safety* remain harness-owned. This
> is the deliberate divergence from eve's user-defined-tools model, documented in
> is the deliberate divergence from user-defined-tools models, documented in
> the `core/src/config/agent_dir.rs` module header, and is why the directory
> selects between two harness-owned backends rather than running arbitrary code.

Expand Down
19 changes: 19 additions & 0 deletions sdk/node/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,25 @@ console.log(await session.mcps())
The positional `addMcpServer(...)` overload and longer
`addMcpServerConfig(...)` alias remain for compatibility.

## Filesystem-First Agents

Define a durable agent as a **directory** — `instructions.md` (required) plus
optional `agent.acl`, `skills/`, `schedules/` (cron), and `tools/` (`kind: mcp` or
`kind: script` sandboxed QuickJS) — and serve its schedules. Each fire is a full
harness turn (context, tool visibility, safety gate, verification). Returns a
handle you must keep and stop explicitly.

```js
const handle = await agent.serveAgentDir('./my-agent', './workspace', {
// Optional: pass a sessionStore so each schedule resumes its accumulated
// context across daemon restarts.
sessionStore: new FileSessionStore('./sessions'),
})
// ... runs in the background until:
await handle.stop()
console.log(handle.isStopped()) // true
```

## HITL Confirmations

Use `permissionPolicy` to decide which tools ask, then `confirmationPolicy` to
Expand Down
20 changes: 20 additions & 0 deletions sdk/python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,26 @@ or `session.tool("task", {...})` when you need raw access.
The old standalone lifecycle control-plane API is intentionally removed from
the 2.0 SDK surface.

## Filesystem-First Agents

Define a durable agent as a **directory** — `instructions.md` (required) plus
optional `agent.acl`, `skills/`, `schedules/` (cron), and `tools/` (`kind: mcp` or
`kind: script` sandboxed QuickJS) — and serve its schedules. Each fire is a full
harness turn (context, tool visibility, safety gate, verification). Returns a
handle you keep and stop explicitly.

```python
opts = SessionOptions()
# Optional: pass a session_store so each schedule resumes its accumulated
# context across daemon restarts.
opts.session_store = FileSessionStore("./sessions")

handle = agent.serve_agent_dir("./my-agent", "./workspace", opts)
# ... runs in the background until:
handle.stop()
print(handle.is_stopped()) # True
```

## License

MIT
Loading