- client: stdio + Streamable HTTP transports, 2025-11-25
handshake/list/call/ping, vault mcp_servers with sealed headers,
per-profile enable, description-hash pins, tool cache
- mcp_normalize: pasted JSON/TOML configs (type repair, ${VAR}
expansion from env then vault secrets, allow/deny lists);
github URLs resolve via registry/package/README with live
handshake verification before storing
- bridge: enabled tools enter lazy select_tools as
mcp_<server>_<tool>, dispatch fallback, approval like non-safe
shell, drift re-prompt, 64KB spill, per-call audit
- 7 agent tools: mcp_add/list/enable/disable/remove/tools/call
(native count 42->60 in README, was already stale at 53)
- server: --serve-mcp KEY (+ --mcp-port/--mcp-bind) serves all 60
native tools plus chat (agent turn, per-session history) and
completions (stateless, current model) over single-threaded
HTTP with Bearer auth, sessions, idle expiry; yolo documented
- rotation + plaintext-boot refusal cover sealed headers
- mcp.md contract with drift tests; 47 new tests; live loopback
verified (client->serve over real HTTP)
7.6 KiB
AGENTS.md
Conventions
- Use plain
importandfromstatements in Python files. - Use the
.pyextension for Python source and test files. tai.pyis the entire product in one standalone stdlib-only file: CLI entry plus the stableimport taisurface, grouped into classes (instance classes for stateful clusters, static utility classes for stateless groups). It must import nothing outside the standard library; a bare copy of the file has to import and run anywhere. Canonical patch targets aretai.<Class>.<name>(or the module attribute for module-level data).- There are two kinds of test, in two different places, on purpose — see "Testing" below for the full split. In short: unit tests are top-level
test_*.py; integration tests aretests/<topic>.py(notest_prefix) built ontai_test.py. - The in-
tai.pyTUI framework is documented in tui.md — read it before touching anyTui*code. - The grr terminal client (
tai.py --tui, theGrr*classes) is specified in grrtui.md: read it before touching anyGrr*code, and keep its screen, key and cap tables in step with the code (GrrSpecTestsfails when they drift). It is a client of the grr server and shows what the server's/api/specand/locales.jsonsay; never copy a string, color or limit of the server intotai.py.tests/grr_tui.pydrives the real binary against a real grr server (sibling../grr,GRR_DIRto move it) and a real model. - The terminal multiplexer (
Mplex*classes,tai.py --mplex,~/.tai/mplex.toml) is specified in mplex.md: read it before touching anyMplex*code, and keep its option, command and key tables in step with the code (MplexSpecTestsfails when they drift). - Interactive UI flows (
plexer_show,PlexerFlow,PlexerController.ask, theshow/result/cancelcontrol commands) are specified in uiflow.md: read it before touching any flow code, and keep its contract and cap tables in step with the code (UiflowDocTestsfails when they drift). - CAP protocol support (
Cap*classes,--cap-manifest,--cap-speak, orchestrator drivers) is specified in cap.md: read it before touching anyCap*code, and keep its event, error and manifest tables in step with the code (CapDocTestsfails when they drift). - MCP support (
Mcp*classes,mcp_*tools,--serve-mcp) is specified in mcp.md: read it before touching anyMcp*code, and keep its wire, caps, and tool tables in step with the code (McpDocTestsfails when they drift). - Skills (
discover_skills,SKILL_BLUEPRINTS,BUNDLED_SKILLS, theskillsinstall target) are specified in skills.md: read it before touching skill code or embedded skill text, and keep its bundled-skill and adaptation tables in step with the code (BundledSkillsTestsfails when they drift). - Settings (
SETTINGS_CATALOG, thesettingstable,get_or_create_settings/upsert_settings/get_all_settings,boot_settings) are specified in settings.md: read it before touching settings code, and keep its catalog table in step with the code (SettingsTestsfails when they drift).
Testing
Unit tests: test_tai.py, test_seal.py, at the repository top level, named test_*.py, unittest-based, import tai.py as a module directly, no subprocess, fast. python3 test_tai.py / python3 test_seal.py, or a single test via python3 -m unittest test_tai.SomeClass.test_method.
Integration tests: tests/<topic>.py (e.g. tests/rag.py), deliberately not named test_*.py and not at the top level, so the two kinds are never confused. These drive the real tai.py binary as a subprocess (one-shot or REPL) and check actual CLI behavior, using tai_test.py (the framework, at the repository root) as the one and only base to build on — never a one-off subprocess/asyncio harness per file. They must exercise a real LLM call through the real binary — never a mocked model or a database write standing in for what the agent would have done; the resulting flakiness from a free, rotating backend is accepted, not engineered away. Full conventions, that rule's full rationale, the required isolation rule (always a throwaway TAI_HOME), and why verification must check vault state rather than scrape CLI rendering, are in tests/README.md — read it before adding a new one. Run with python3 tests/<topic>.py (--filter, --interactive, --timeout all supported).
Profiles and bots
- A profile is a user-created identity (
--profile NAME,/profile NAME);defaultis the fallback when none is given. Each profile owns a separate slice of every vault table; the only cross-profile knowledge is the identity list itself. - A bot is a role inside one profile. Every profile starts with
main; more are added withcreate_bot. There are no built-in bots.
Key Features
- Autonomous decision making
- Multi-profile support
- Background subagent orchestration
- Secure vault storage
- Container sandbox support
- Progress tracking
- Interactive REPL
- Command-line interface
Usage Examples
-
Start the main agent:
./tai.py -
Use a specific profile:
./tai.py --profile work -
Run a one-shot command:
./tai.py what is 2+3, use the shell -
Check agent status:
./tai.py --version -
Install the agent:
./tai.py --install
Development Guidelines
- Write clear, concise code
- Follow Python conventions
- Test thoroughly
- Use the agent for documentation
- Keep code maintainable
- Document decisions
- Version control changes
- Collaborate effectively
Command Output Rule
Every command an agent runs for the user (shell, ssh, sandbox box) must stream live to the user in both modes, in addition to being returned to the agent. It is a message, not a blocking spinner: a fixed-height window (REPL LiveStream, chat TUI live block) that keeps scrolling its last lines in place while the user keeps chatting. If the command stops on a prompt (sudo, ssh, y/n), it runs on a pty and asks the user through the same channel as ask_user (agent.prompter / REPL input); the answer is typed into the command, a decline sends end-of-file, and waiting for the user never counts against the timeout. Any new tool that runs a process must go through run_live with Tools.shell_stream and Tools.shell_ask, never a bare subprocess.run. Details: tui.md (chat scene).
Research Rule
All external research conducted for this project (web searches, reading documentation to settle a design question, competitive/prior-art analysis) is archived under research/, maintained under the auspices of the Molodetz Institute — Research & Retrieval Division. The full specification — folder layout, the three required document types (Source Dossier, Source Citation Card, Conclusion Document), required metadata, and the quality bar — lives in research/README.md; research/INDEX.md is the living registry and statistics page. Read research/README.md in full before writing into this archive.
The binding rule, in short: a research task is not done until it is written into research/. Every source actually consulted gets its own document (Tier 1 if read and synthesized, Tier 2 if only seen in a result listing — never skipped, never inflated past what's actually known), every decision the research produced gets its own Conclusion document in that subject's conclusions/ folder, and research/INDEX.md gets updated in the same pass. Check research/INDEX.md for an existing subject before opening a new one.