syscall-agent is a compact coding agent written in pure C. It talks to
OpenRouter, keeps durable memory in Markdown, and exposes local tools that lean
on OS primitives such as fork, execvp, mmap, rename, kqueue,
inotify, getaddrinfo, non-blocking sockets, and process-table syscalls.
The goal is a small single-binary agent that can inspect code, edit files, perform network lookups, run bounded local commands, and stay usable from both plain CLI mode and a responsive Pi-style TUI.
Important
This is a local coding agent with powerful filesystem and optional process execution tools. Run it in a repository or disposable workspace you trust.
- Pure C implementation with one generated binary.
- OpenRouter chat-completions backend.
- Tool-calling loop with file, web, memory, process, watch, network, and Termux tools.
- Runtime tool catalog, auth status, host diagnostics, and local skill-pack tools.
- Optional syscall-backed command execution with resource limits.
- Atomic file writes via
mkstemp+rename. mmaprange reads for large files and logs.- Persistent
MEMORY.mdwith append locking. - Interactive
--tuimode inspired by Pi Agent. - Android Termux install path and Termux:API device integrations.
- Focused regression tests for TUI commands, agent events, tools, and security checks.
- macOS, Linux, or Android through Termux
- C compiler with C11 support
makelibcurl- OpenRouter API key
On macOS, libcurl is usually already available. On Debian/Ubuntu:
sudo apt-get install build-essential libcurl4-openssl-devOn Android, install Termux from F-Droid or GitHub, then inside Termux:
pkg update
pkg upgrade
pkg install git clang make libcurlFull Android setup, storage, and Termux:API instructions are in docs/termux-install.md.
makeThe binary is written to:
build/agentRun the test suite:
make testexport OPENROUTER_API_KEY=sk-or-...
./build/agent "summarize this repository"
./build/agent -s 20 -m anthropic/claude-3.5-sonnet "find the largest C file and explain it"
echo "what changed recently?" | ./build/agentOpen the TUI:
./build/agent --tuiEnable subprocess tools:
./build/agent --allow-exec "run the tests and summarize failures"| Variable | Purpose |
|---|---|
OPENROUTER_API_KEY |
Required OpenRouter API key. |
OPENROUTER_MODEL |
Default model when -m/--model is not set. |
BRAVE_SEARCH_API_KEY |
Optional Brave Search key; otherwise web search falls back to DuckDuckGo HTML. |
SYSTEM_PROMPT_PATH |
Override SYSTEM_PROMPT.md. |
MEMORY_PATH |
Override MEMORY.md. |
LLA_ALLOW_EXEC=1 |
Enable subprocess tools. |
LLA_ALLOW_UNSAFE_EXEC=1 |
Allow unsandboxed profile=none; implies exec tools. |
SYSCALL_AGENT_AUTH_PROVIDER |
Descriptive provider label shown by auth status tools. Defaults to openrouter. |
SYSCALL_AGENT_SKILLS_DIR |
Optional directory containing local name/SKILL.md skill packs. |
Flags:
--tui open the interactive terminal UI
-s, --steps N max agent-loop iterations (default 10)
-m, --model NAME OpenRouter model id (default openai/gpt-4o-mini)
--system PATH path to SYSTEM_PROMPT.md
--memory PATH path to MEMORY.md
--allow-exec enable exec_command, spawn_bg, bg_read, bg_kill, bg_list
--allow-unsafe-exec also allow profile=none; implies --allow-exec
-v, --verbose trace tool calls to stderr in plain CLI mode
-h, --help show help
The TUI is intentionally small and terminal-native. It uses full-width accent borders, muted status/footer lines, padded user blocks, and compact tool or reasoning panels.
In TUI mode, model responses use OpenRouter streaming. Assistant text appears as
it arrives. Tool-call deltas appear in real time when /verbose tools or
/verbose all is enabled, and reasoning deltas appear in real time when
/verbose reasoning or /verbose all is enabled.
Slash commands:
| Command | Behavior |
|---|---|
/model |
Open a live OpenRouter model picker backed by GET /api/v1/models. |
/models |
Open the same model picker. |
/model N |
Select a built-in quick choice by list index. |
/model provider/model-id |
Set any OpenRouter model id directly. |
/verbose normal |
Show only user and assistant conversation text. |
/verbose tools |
Also show tool calls and tool results. |
/verbose reasoning |
Also show model reasoning fields when returned. |
/verbose reasioning |
Accepted alias for the original requested spelling. |
/verbose all |
Show tools and reasoning output. |
/tools |
Show tool families visible to the model. |
/skills |
List local skill packs from configured skill roots. |
/auth |
Show auth-provider status without printing secrets. |
/sysinfo |
Show host OS, architecture, cwd, CPU count, and page size. |
/new |
Clear the visible transcript. |
/exit |
Leave the TUI. |
Model picker controls:
| Key | Behavior |
|---|---|
| Type text | Filter models by id, name, or description. |
| Up/Down | Move through the filtered list. |
| PageUp/PageDown | Jump through the filtered list. |
| Enter | Select the highlighted model for the current session. |
| Esc | Close the picker without changing the model. |
| Ctrl-R | Refresh the OpenRouter model catalog. |
Reasoning display supports OpenRouter-style reasoning, reasoning_content,
and reasoning_details fields when the selected model returns them, including
streamed choices[].delta.reasoning_details chunks.
Always available:
| Tool | Capability |
|---|---|
list_tools |
List every currently visible tool and one-line description. |
read_file |
Read up to 256 KB from a local file. |
search_files |
Recursively search filenames by glob or substring. |
search_web |
Brave Search or DuckDuckGo HTML search. |
fetch_url |
HTTP/HTTPS GET with raw response output. |
web_fetch |
HTTP/HTTPS GET with HTML stripped to text. |
save_memory |
Append durable notes to MEMORY.md. |
auth_status |
Report configured auth surfaces without exposing secrets. |
system_info |
Inspect host OS, architecture, cwd, CPU count, and page size. |
disk_usage |
Inspect filesystem capacity and inode counts via statvfs. |
env_get |
Read allowlisted configuration environment variables with secret redaction. |
which |
Locate executables on PATH without invoking a shell. |
file_digest |
Compute an FNV-1a 64-bit checksum for change detection. |
grep_text |
Search one text file for literal matching lines. |
list_skills |
List local name/SKILL.md skill packs. |
read_skill |
Read a local skill pack by safe skill name. |
stat |
Inspect metadata without reading file content. |
list_dir |
List directory entries with type, size, and mtime. |
write_file |
Atomic file replacement using a same-directory temp file. |
read_file_range |
mmap-backed byte-range read for large files. |
dns_lookup |
Resolve A and AAAA records through getaddrinfo. |
tcp_check |
Non-blocking TCP reachability probe with duration. |
watch_path |
Wait for file or directory changes. |
list_processes |
List top processes by RSS. |
termux_info |
Detect Termux/Android environment and command availability. |
termux_api_status |
Check Termux:API helper command availability and setup hints. |
termux_storage_status |
Check Android shared-storage symlinks from termux-setup-storage. |
termux_battery_status |
Read Android battery state through Termux:API. |
termux_wifi_info |
Read current Wi-Fi connection details through Termux:API. |
termux_clipboard_get |
Read Android clipboard text through Termux:API. |
termux_clipboard_set |
Set Android clipboard text through Termux:API. |
termux_notification |
Show an Android notification through Termux:API. |
termux_vibrate |
Vibrate the Android device for a bounded duration. |
termux_wake_lock |
Acquire or release Termux's wake lock. |
Termux tools degrade gracefully outside Termux or when the relevant termux-*
command is missing. They invoke fixed Termux commands through argv-only
execvp, never through a shell. See
docs/termux-install.md for Android setup.
Gated by --allow-exec:
| Tool | Capability |
|---|---|
exec_command |
Run an argv-only command, capture stdout/stderr, enforce rlimits. |
spawn_bg |
Start a background command and return a handle. |
bg_read |
Read buffered background output by offset. |
bg_kill |
Terminate a background process. |
bg_list |
List background processes owned by this agent. |
delegate_codex |
Delegate to the official Codex CLI using its own supported auth. |
delegate_copilot |
Delegate to the official GitHub Copilot CLI using its own supported auth. |
exec_command and spawn_bg never invoke a shell. The model must provide an
argv array, so shell metacharacters are ordinary arguments unless the chosen
executable is itself a shell.
Delegation tools are also gated by --allow-exec. They invoke official local
CLIs (codex exec and copilot -p) through argv-only fork/execvp, so users
can use Codex OAuth or GitHub Copilot subscription authentication without
syscall-agent reading or reusing private tokens. Delegation is read-only by
default. mode=workspace-write requires --allow-unsafe-exec.
Model calls currently go through OpenRouter with OPENROUTER_API_KEY. The
agent also reports other common coding-agent auth surfaces so operators can see
what is configured on the machine:
| Surface | Behavior |
|---|---|
| OpenRouter | Used for model requests through OPENROUTER_API_KEY. |
| OpenAI API | Detected through OPENAI_API_KEY for future provider work. |
| Codex CLI OAuth | Detected by auth_status; usable through delegate_codex when the official codex CLI is installed and logged in. |
| GitHub/Copilot | Detected by auth_status; usable through delegate_copilot when the official copilot CLI is installed and logged in. |
syscall-agent does not print, scrape, or repurpose ChatGPT/Codex OAuth or
GitHub Copilot subscription tokens. That keeps provider integration on the
documented side of the boundary while leaving room for official support later.
Skill packs are simple directories containing SKILL.md. The agent searches:
$SYSCALL_AGENT_SKILLS_DIR
./skills
~/.syscall-agent/skills
Use list_skills to discover available packs and read_skill to load one into
the conversation. In the TUI, /skills shows the same roots.
Every subprocess gets these resource limits:
| Limit | Value |
|---|---|
| CPU time | 30 seconds |
| Address space | 512 MB |
| Output file size | 256 MB |
| Open files | 256 |
| Captured stdout/stderr | 256 KB per stream |
OpenRouter SSE streaming is only enabled for the interactive TUI. Plain CLI mode keeps the one-shot response path unless a future flag enables stream printing.
Sandbox profiles:
| Profile | macOS behavior | Linux behavior |
|---|---|---|
readonly |
Read filesystem, write only tmp/devnull paths, no network. | Fails closed until Linux sandboxing is implemented. |
default |
Read filesystem, process spawning, write only tmp/devnull paths, no network. | Fails closed until Linux sandboxing is implemented. |
network |
default plus outbound network. |
Fails closed until Linux sandboxing is implemented. |
build |
Filesystem writes allowed for build loops, no network. | Fails closed until Linux sandboxing is implemented. |
none |
No sandbox; requires --allow-unsafe-exec. |
No sandbox; requires --allow-unsafe-exec. |
HTTP tools accept only http:// and https:// URLs. write_file masks mode
bits to regular permission bits so tool calls cannot request setuid/setgid
outputs.
The agent reads SYSTEM_PROMPT.md and MEMORY.md at startup. MEMORY.md is
created on first use and appended through save_memory.
Override paths:
./build/agent --system prompts/system.md --memory state/MEMORY.md "use this context"src/
main.c CLI entry and flag parsing
agent.c tool-calling loop and event emission
openrouter.c OpenRouter request/response handling
openrouter_models.c OpenRouter model catalog fetch/parse/filter helpers
tui.c raw terminal UI
tools.c shared tool registration and dispatch
tools_meta.c tool catalog, auth, host diagnostics, skills, grep/checksum
tools_termux.c Android Termux detection and Termux:API wrappers
tools_fs.c stat, list_dir, write_file, read_file_range
tools_proc.c exec, background process, process listing
tools_watch.c kqueue/inotify path watching
tools_net.c DNS and TCP probes
os_compat_*.c platform-specific syscall shims
memory.c Markdown memory loading/appending
http.c libcurl wrapper
vendor/
cJSON.c vendored JSON parser
tests/
*_test.c focused regression tests
make clean
make
make testUseful smoke checks:
./build/agent --help
./build/agent -m openai/gpt-4o-mini -s 3 "Reply with OK only"
./build/agent --tuiThe research notes behind the current roadmap live in
docs/research/2026-05-16-roadmap.md.