feat(serve): filesystem-first agents (eve parity) — agent-dir, cron schedules, serve daemon, tools/#73
Merged
Merged
Conversation
eve-style: a single directory defines a durable agent by convention.
`AgentDir::load(dir)` SYNTHESIZES existing config objects (no new runtime):
- instructions.md (required) -> SystemPromptSlots.role (a prompt SLOT, NOT a
system-prompt override — BOUNDARIES/response-format/verification stay
harness-owned)
- agent.acl (optional) -> CodeConfig (else default)
- skills/ -> appended to CodeConfig.skill_dirs (existing format)
- schedules/*.md -> Vec<ScheduleSpec> (YAML frontmatter `cron:` + body prompt)
- channels/*.{md,acl} -> Vec<ChannelSpec> (parsed; adapters not yet built)
Distinct from the existing agent_dirs/register_agent_dir (worker-subagent dirs).
Reuses CodeConfig::from_file, SystemPromptSlots, the skills *.md format.
Tests: loads the convention into slots + specs; missing instructions.md errors;
frontmatter split. Build + fmt + clippy clean.
First slice of eve-style filesystem-first durable agents (P0). Next: P1 cron
schedules, then the serve daemon.
Behind a new `serve` feature (default off → library embedders pay nothing). - ScheduledJob::parse — compiles a ScheduleSpec's cron (accepts standard 5-field `min hour dom mon dow` and 6-field `sec min hour dom mon dow`); next_fire_after. - Scheduler — skips disabled specs, rejects bad cron, drives one tokio loop per job (sleep-until-next-fire → fire → repeat) until a CancellationToken fires. - ScheduleSink trait — the seam the serve daemon implements to route the schedule's markdown prompt through AgentSession::send (a FULL harness turn: context, tool visibility, safety gate, verification — never a raw model call). Adds `cron` as an optional dep gated by `serve`. Tests: 5-field cron next-fire (deterministic), invalid-cron error, disabled-skip, and an every-second cron that fires >= 1 in ~2.2s then stops on cancel. Default build unaffected; clippy clean. Second slice of eve-style filesystem-first durable agents (P1). Next: the serve daemon (session registry + wire the sink to AgentSession::send + rehydrate).
…ssions serve_agent_dir + SessionScheduleSink build one durable AgentSession per enabled schedule (stable id schedule:<name>); each fire is a FULL harness turn via AgentSession::send, never a raw model call. Gated behind the `serve` feature. Static audit + cargo check --features serve clean.
…SDKs Mirrors the add_mcp_server binding pattern. Agent.serve_agent_dir(dir, workspace, options?) -> ServeHandle (stop()/is_stopped()); enables the core `serve` feature + tokio-util in both SDK crates. cargo check -j1 clean for a3s-code-node and a3s-code-py.
tools/ -> MCP-or-sandboxed-QuickJS mapping honoring the harness guardrail (tool definition from the dir; visibility+safety stay harness-owned). ChannelAdapter/ChannelSink seam for HTTP/Slack/Discord where every inbound message is a full session.send turn. No implementation.
…ions AgentDir::load now parses tools/*.md (kind: mcp) into ToolSpec::Mcp(McpServerConfig) — lenient deserialize, dedup by name, unknown-kind fails closed. install_agent_dir_tools registers each into the per-schedule session via the existing add_mcp_server path, so tools land as mcp__<server>__* gated by the session permission policy: tool definition from the dir, visibility+safety stay harness-owned. Sandboxed-script (kind: script) backend is the next increment. cargo check + 5 parser + 7 serve tests pass (-j1).
… fire Closes the cross-module integration gap the in-crate unit tests stubbed out: - test_agent_dir_tools.rs (hermetic, --features serve): AgentDir::load + install_agent_dir_tools fail CLOSED when an MCP server command can't be spawned; plus the empty-install Ok path. 2/2 pass in normal CI. - test_serve_agent_dir_real_llm.rs (#[ignore], real provider): a cron schedule fires a FULL real AgentSession::send harness turn (recording-sink asserts non-empty returned text), and serve_agent_dir runs a real fire then stops cleanly on cancel. 2/2 pass against .a3s/config.acl (glm5.1, 46s). Adds async-trait dev-dep for the ScheduleSink impl in the integration test.
…LLM schedule fire Hermetic unit test (inline ACL): serve a no-schedule dir, assert the returned ServeHandle reports not-stopped, stop() is idempotent and flips is_stopped(). Integration test (real provider, skipped without .a3s/config.acl): an every-second schedule fires real harness turns through the daemon — the PyO3 boundary must survive them (a panic would abort the process) — then stop() shuts down clean. Both pass against the rebuilt module (glm5.1).
…AgentDir The napi build regenerated index.js + generated.d.ts to include serveAgentDir + ServeHandle — the earlier binding commit added the Rust but not the generated glue, so the API was not reachable from JS/TS until now. test_serve.mjs: hermetic ServeHandle lifecycle (exports + stop/isStopped idempotent) and a real-provider integration test (every-second schedule fires real harness turns through the daemon — a napi panic would abort the process — then clean stop; self-skips without .a3s/config.acl). Wired into npm test. Both pass; existing smoke still green.
ZhiXiao-Lin
pushed a commit
to A3S-Lab/a3s
that referenced
this pull request
Jun 17, 2026
…rve feature Adds the Filesystem-First Agents user doc (apps/docs/.../agent-dir.mdx) + sidebar nav entry, and advances the crates/code submodule fd4d068 -> 6e33653 (AI45Lab/Code#73: agent-dir convention, cron schedules, serve daemon, tools/ MCP backend, Python+Node SDK bindings, full unit + integration tests).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a filesystem-first agent layer (vercel/eve parity, harness-respecting): a single directory defines a durable agent by convention, served by a cron daemon, with declarative
tools/. Exposed in Rust + Python + TypeScript SDKs.Built as a thin layer over existing primitives — no new runtime, no new prompt system. Gated behind the
serveCargo feature, so library-only embedders pay nothing.The agent directory
Harness guardrails (the deliberate divergence from eve)
instructions.mdis a prompt slot, not a system-prompt override — BOUNDARIES / response-format / verification stay authoritative.AgentSession::send(context, tool visibility, safety gate, verification), never a raw model call.tools/definition comes from the directory, but visibility + safety stay harness-owned: anmcpspec is registered via the normaladd_mcp_serverpath (mcp__<server>__<tool>, permission-gated), never as arbitrary host code.What's included
servefeature):AgentDir::loadconvention loader;Scheduler/ScheduledJob(5/6-field cron);serve_agent_dirdaemon (one durable session per schedule, stable idschedule:<name>);tools/→install_agent_dir_tools.serveAgentDir/serve_agent_dir+ServeHandle(stop/isStopped) in both Node (napi) and Python (PyO3). Node JS/TS glue regenerated so the API is reachable from JS/TS.agent-dir.mdx, in the monorepo root) +tools/→MCP and channels design docs.Tests
#[ignore]).ServeHandlelifecycle (unit) + real-LLM schedule fire (integration).npm test(real part self-skips without config).Real-LLM integration tests pass against
.a3s/config.acl(glm5.1). All commitscargo fmt-clean.Out of scope (documented as next-increment)
channels/adapters (HTTP/Slack/Discord) — design-only (manual/CHANNELS_DESIGN.md).tools/backend (kind: script) — design-only (manual/AGENT_DIR_TOOLS_DESIGN.md).apps/docspages live in the a3s monorepo root (separate from this submodule PR).