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: podman or docker for the sandbox and Telegram voice notes.
  • Optional: tmux for 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
S
Description
AI skills analysis project
Readme
625 KiB
Languages
Python 100%