sa3p is a Rust workspace for executing LLM-issued XML tool commands without JSON-in-JSON tool payloads.
This repository is intentionally scoped as a standalone, open-source runtime component that fits under the broader SA3P umbrella. It focuses on reliable local execution primitives that can later plug into larger host/guest and model-runtime systems.
- A streaming XML parser that can process arbitrarily chunked input.
- A Sans-IO execution engine for file and terminal operations.
- Local host implementations for filesystem and terminal backends.
- A binary frame protocol with stream multiplexing primitives.
- End-to-end tests that run parser -> engine -> host and validate protocol mapping.
sa3p-parser- Streaming parser for XML-style command tags.
- Emits both generic parser events and protocol-ready
Instructionvalues. - Special case:
<write_file>body emitsInstruction::WriteChunk(Vec<u8>)for direct protocol mapping.
sa3p-engine- Sans-IO command executor with
VirtualFileSystemandTerminalProvidertraits. - Implements
write_file,read_file,apply_edit,list_files,terminal, andterminal_signalsemantics. - Produces deterministic per-turn state headers.
- Sans-IO command executor with
sa3p-host-impl- Local
std::fs+ process backends for the engine traits. - Includes atomic writes, ignore-aware tree listing, timeout-based process detachment, and signals.
- Local
sa3p-protocol- Binary frame encoder/decoder and incremental frame codec.
- Multiplexing queue by stream ID.
- Instruction-to-frame mapping for
WriteChunk -> opcode 0x02.
Supported XML tags:
<write_file path="...">...</write_file><read_file path="..." start_line=".." end_line="..." /><apply_edit path="..."><search>...</search><replace>...</replace><list_files path="..." /><terminal>...</terminal><terminal_signal pid="123" signal="SIGINT" />
Detailed reference: docs/COMMANDS.md
cargo test --workspaceuse sa3p_engine::Engine;
use sa3p_host_impl::{LocalTerminal, LocalVfs};
use sa3p_parser::InstructionParser;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let cwd = std::env::current_dir()?;
let mut parser = InstructionParser::new();
let mut instructions = Vec::new();
let xml = r#"
<write_file path="src/demo.txt">hello\nworld\n</write_file>
<read_file path="src/demo.txt" start_line="1" end_line="20" />
<terminal>printf done</terminal>
"#;
// Feed incrementally to support streamed model output.
for chunk in xml.as_bytes().chunks(8) {
instructions.extend(parser.feed(chunk)?);
}
instructions.extend(parser.finish()?);
let vfs = LocalVfs::new(&cwd);
let terminal = LocalTerminal::new(&cwd);
let mut engine = Engine::new(vfs, terminal);
let turn = engine.execute_turn(instructions)?;
println!("{}", turn.state_header.render());
Ok(())
}Instruction::WriteChunk(Vec<u8>) is designed for direct frame mapping:
use sa3p_protocol::frame_from_instruction;
let stream_id = 1;
for instruction in &instructions {
if let Some(frame) = frame_from_instruction(stream_id, instruction) {
let bytes = frame.encode();
// send bytes to socket/vsock transport
}
}sa3p-protocol uses a fixed header:
[MAGIC 4B][STREAM_ID 4B][OPCODE 1B][PAYLOAD_LEN 4B][PAYLOAD]- Magic:
SA3P - Opcode examples:
0x01: VFS_READ0x02: VFS_WRITE_CHUNK0x03: VFS_RENAME0x04: PTY_SPAWN0x05: PTY_INPUT
Full protocol details: docs/PROTOCOL.md
scripts/release.sh check
scripts/release.sh dry-run
PUBLISH_CONFIRM=1 scripts/release.sh publishThis is an implementation-focused, actively evolving codebase. APIs are usable and tested, but still considered early-stage (0.1.x) while the broader SA3P ecosystem evolves.
MIT