retoor retoor@molodetz.nl
tai
tai is a single-file autonomous AI agent written in Python. The entire
implementation lives in tai.py (about 2000 lines) and uses only the Python
standard library: no dependencies, no install step, no build system.
The agent runs as an interactive REPL or as a one-shot command. It reasons through an OpenAI-compatible backend, acts through fourteen tools, keeps per-profile memory, seals its stored state at rest, and can isolate shell and file operations inside a container sandbox.
Requirements
- Python 3.10 or newer, no third-party packages.
- Optional:
podmanordockerfor the sandbox and Telegram voice notes. - Optional:
tmuxfor terminal content capture.
Quick start
./tai.py
./tai.py --profile work
./tai.py --yes
./tai.py --version
./tai.py what is 2+3, use the shell
Trailing arguments form a one-shot prompt: the agent answers once and exits with code 0. Without arguments, tai starts an interactive session.
REPL commands
| Command | Effect |
|---|---|
/profile [name] |
Show the current profile or switch to it |
/profiles |
List all profiles, current marked with * |
/env [target] |
Show or switch execution environment |
/skills |
List loaded skill files |
/fork <task> |
Spawn a background subagent, REPL stays free |
/agents |
List background subagents |
/agent <id> |
Show one subagent result |
/agent clear |
Purge finished subagents |
/compact |
Compress history into a summary |
/clear |
Drop history, keep the system message |
/help |
Show the command overview |
/quit |
Exit |
Any other input is sent to the agent.
Installation
./tai.py --install
This copies tai.py to ~/.local/bin/tai.py as an executable and registers
a guarded command_not_found_handle block in ~/.bashrc (backed up once to
~/.bashrc.bak-tai, idempotent), so unknown shell commands are answered by
the agent instead of failing.
Backends
The primary backend is model.cloud.pravda.education, an OpenAI-compatible
gateway that needs no API key and selects a free model per request. If a
request fails, tai retries it on devplace.net/openai/v1, which requires
DEVPLACE_API_KEY. Both endpoints speak /chat/completions, including
native tool calls and streaming.
Orchestration
/fork <task> spawns a background subagent with its own context while the
REPL stays free (the prompt shows a +N counter). /agents lists workers,
/agent <id> shows a result, /agent clear purges finished ones.
The model itself orchestrates through the fork tool (task, timeout up to
one hour, profile) and the poll tool (id, wait up to two minutes). Workers
get 12 steps, a cooperative deadline, no session writes, and no interactive
approval prompts. Nesting is capped at two levels. Timeouts and errors
surface as statuses, never silently.
Tools
| Tool | Purpose |
|---|---|
shell |
Run a shell command, output truncated |
read_file |
Read a text file, large files truncated |
write_file |
Write content to a file, creating parent directories |
edit_file |
Replace one unique exact text match in a file |
web_search |
Search the web, optionally images or page content |
web_fetch |
Fetch a URL and return its text content |
speak |
Synthesize speech, save MP3, play when possible |
listen |
Record from the microphone and transcribe it |
remember |
Merge knowledge into the profile system message |
recall |
Search past session memory by keyword |
load_skill |
Load a skill file by name |
get_current_terminal_content |
Capture the current tmux pane with scrollback |
fork |
Spawn a background subagent |
poll |
Collect a background subagent result |
Web search runs on rsearch.app.molodetz.nl. Destructive shell commands ask
for confirmation unless --yes is given; read-only commands run directly.
Skills
Standard agent skill files (SKILL.md with name plus description
frontmatter, optional scripts/, references/, assets/, per the Agent
Skills open format) are discovered in ~/.tai/skills/*/ and
./.tai/skills/*/ (project wins on name collisions). Descriptions stay in
context; the agent loads full instructions through load_skill only when
needed. /skills lists what is available.
Sandbox
/env shows the execution environment, /env sandbox switches shell and
file tools into an isolated tai-box container (podman or docker, no mounts,
no shared filesystem), /env home switches back. The image is built
automatically on first use from an embedded Containerfile; every pip
requirement (faster-whisper, edge-tts) lives inside the image while
tai.py itself stays dependency-free. Sandbox commands need no approval
because the container is disposable.
Telegram
./tai.py --install-telegram
./tai.py --uninstall-telegram
Install asks for the bot token up front, verifies it against getMe, stores
it in ~/.tai/telegram.env (0600), builds the sandbox container (used for
voice transcription), and registers a tai-telegram.service systemd user
unit with linger enabled (failures ignored). The bot long-polls, answers
text, transcribes voice notes, and understands /new. It runs without
--yes, so destructive shell commands are denied. Uninstall stops and
removes the service and purges the container and image; the token file and
data stay.
Profiles and memory
Each profile owns a system message plus session history under
~/.tai/profiles, stored with mode 0600.
/profile show current profile
/profile [name] switch profile, creating it when missing
/profiles list all profiles
The remember tool merges an instruction into the current profile system
message through the model itself: it adds facts, updates behavior, or removes
forgotten items while preserving the rest. It fires by default on new
passwords and behavior changes. recall searches the per-profile episodic
log in ~/.tai/memory.db (SQLite). Context is budgeted at roughly 32k
tokens with automatic compaction at 80 percent.
Sealed storage
Storage is sealed by default with a built-in key, which stops casual reads
but not a determined attacker, since the key ships in the source. Set
TAI_PASSPHRASE for real protection: a home sealed with the default key is
re-sealed to your passphrase automatically on first boot, with a notice.
The key comes from PBKDF2-SHA256 (200k rounds) over a random salt in
~/.tai/.seal; values use a per-value nonce with HMAC-SHA256 encrypt-then-
MAC. SQLite access goes through custom tai_enc/tai_dec functions
registered with create_function, so inserts encrypt inline and recall
decrypts before matching. Existing plaintext data is sealed automatically on
first sealed start. A wrong passphrase refuses to start with exit code 2.
Set the variable empty for plaintext storage.
This construction uses only the standard library and is honest file-theft protection, not audited cryptography; high-value secrets still belong in a dedicated manager.
Voice
speak synthesizes free neural speech via the Microsoft Edge Read Aloud
protocol, implemented with socket and ssl from the standard library. No
key, no package. MP3 files land in ~/.tai/audio and play when an OS player
exists. listen records and transcribes when a recorder (arecord, sox,
ffmpeg) and a transcriber (whisper-cpp, whisper) are installed, and
reports exactly what is missing otherwise.
Configuration
| Variable | Purpose | Default |
|---|---|---|
TAI_HOME |
State directory | ~/.tai |
TAI_MODEL |
Model id, ignored by primary gateway | openrouter/free |
DEVPLACE_API_KEY |
Fallback backend credential | Empty, fallback disabled |
TAI_VOICE |
Edge voice name | en-US-EmmaMultilingualNeural |
TAI_PASSPHRASE |
Personal seal key | Unset: built-in key; empty: plaintext |
TELEGRAM_BOT_TOKEN |
Bot token, or set during install | Empty |
Testing
python3 test_seal.py
python3 test_tai.py
Layout
tai.py the entire agent
test_seal.py seal regression tests
test_tai.py skills, telegram, install, and parser tests