Commit Graph
22 Commits
Author SHA1 Message Date
retoorandClaude Sonnet 5.5 690264013c tai.py --tui is the grr app in the terminal: a stdlib client of the grr server with every screen (drawer, bots, profile, chat with approvals, questions, files and live process cards, automations, library, projects, search, the whole settings tree, bot computer terminal, side pane), the grr palette, strings and caps read from the server; old Telegram-style scene kept as --tui-local; spec in grrtui.md with drift tests, 77 unit tests, 4 integration tests through the real binary and a real model
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-17 19:48:30 +02:00
retoor b37281390f Per-shell conversation contexts: ShellSession id per shell instance, TAI_SESSION var, vault session tree, /ctx
Each bash/zsh/fish instance resolves a stable dated id (no install hook);
transcripts and turn state scope per session so every new shell starts
fresh, while vault data stays shared. Parent links accrete in the
shell_sessions table with stub rows for outer shells, and /ctx renders
the ancestor path plus children. TAI_SESSION pins or overrides the scope.
2026-10-12 19:10:06 +02:00
retoorandClaude Sonnet 5.5 f22d15a2f7 Lazy TUI, the rest: watched state, incremental layout, terminal scroll, row reuse, event-driven loop
Closes every gap listed under "Lazy rendering gaps" in tui.md.

State a panel keeps in a list, dict or set (nested, depth cap 4) or a TuiStyle is watched, so an in-place mutation repaints the panel with no touch(). Layout arranges only the subtrees that asked, from the nearest ancestor with a fixed size, and a touched auto-sized panel is measured again so text that grows relays out; app.verify checks every partial pass against a full arrange. A log that scrolls moves the terminal's rows (DECSTBM, IND, RI) when an exact cost count says it writes less, and keeps each line's converted row (cap 4096), so a 3-line scroll at 200x50 writes 1.8 KB instead of 7.9 KB and a line appended while pinned costs 0.6 ms. The main loop sleeps until a timer, an animation, a half-read key or a wake() (touch from any thread, pty, SIGWINCH, quit, end of input): 5 s idle at 200x50 is 7 loop iterations and 4 ms CPU instead of 147 and 20 ms. The chat composer resizes from a TuiChange event and its spinner timer only exists during a turn, instead of 0.2 s and 0.08 s polls.

Found by the new random-tree storm and fixed: a padding, border or alignment change that left the rect the same left stale cells (it was in v1.34.0); a bordered, padded log built a pad row too wide. Known and documented, not changed: animate() on x, y, w, h cannot work because positions and sizes read ints only.

Tests: TuiObservedTests (every mutator), TuiLayoutTests (random trees, partial equals full), TuiScrollTests (VT replay, overlays, resizes), TuiLoopTests (the real _main on pipes: wakeups, latency, timers, signals); twelve mutants of the new code were each caught. VERSION is left to the pending 1.35.0 work in the same file.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 17:54:30 +02:00
retoorandClaude Sonnet 5.5 46682d42c3 mplex: copy mode matches tmux 3.6 (cursor, scrolling, search, selections), window selection history, loop survives a failing iteration, spec mplex.md
Copy mode was compared with the installed tmux 3.6 by differential fuzzing and 842 recorded results instead of being written from memory: the cursor stands between characters and remembers its column (lastcx/lastsx), pages are rows - 2, searches land at the match end for emacs keys and the match start for vi keys and scroll by tmux's rule, a failed search is a status message, emacs selections are half open and vi selections include the cursor character and the line end, select-word on blanks takes the previous word, copy-line and copy-end-of-line start a fresh selection. Window selection keeps a history (last, then previous with wrap when the current window dies), swap-window keeps the current window current, new-session -d -x -y sizes the window as given. Server loop logs and survives a failing iteration (50 in a row end it).

mplex.md is the specification (architecture, wire format, caps, every option, command and default key, what was checked against tmux, known differences); MplexSpecTests fails when it drifts from the code. New: MplexCopyModeVersusTmuxTests (tmux-recorded fixtures), MplexSessionTests, MplexServerLoopTests.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 17:47:14 +02:00
retoor dfc97c31be Update. 2026-10-12 17:02:51 +02:00
retoorandClaude Sonnet 5.5 4fb146edb2 Lazy TUI rendering: per-panel cell cache, front-to-back occlusion, damage rects; a typed character costs 0.08 ms instead of 4.6 ms at 200x50, an unchanged frame writes nothing
Panels cache their painted cells and repaint only after touch(); any attribute write on a mounted panel is the change signal. The compositor builds a frame front to back with per-row coverage (a fully covered panel is never painted), keeps the screen it believes is shown, and recomposes and diffs only damaged rects, with a layout pass that diffs panel geometry. Anonymous dirty_paint()/dirty_layout() still drop every cache, so unaudited callers are slow, never wrong. Also: TuiStyle.key() cached, measure() memoized per layout pass, hit map built on first mouse event, damage recorded under a lock for turn threads, rect count capped at 64.

The original compositor is kept as compose_reference; TAI_TUI_VERIFY=1 checks every frame against it. New TuiLazyTests: seeded 400-step storm replayed through the VT emulator, mutation checks, thread race, laziness counters.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 15:10:02 +02:00
retoorandClaude Sonnet 5.5 3c3c498b49 Input history lives in the sealed vault (input_history table) shared by the REPL and chat TUI; one-time import of the old history file
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 12:56:46 +02:00
retoor f9c48c6b93 Update 2026-10-12 03:10:25 +02:00
retoor 3038c5724b Update 2026-10-12 00:53:52 +02:00
retoor 2e0e0547ed @-routing: leading tags select provider/model/bot with per-dir DB defaults
Leading @tags on any turn now route and pin: @provider pins the
backend, @model-id pins a model, @botname routes one turn (bare
@bot switches sticky), tags combine freely, @auto clears the pins,
and @path references a file by absolute path. Selections save into
the dir_defaults table keyed by directory and re-apply on next boot
there. Completion for all four kinds in the REPL (readline) and in
TUI textboxes (mentions= source on TuiTextBox/TuiPromptDialog).

Bump 1.22.0 -> 1.22.1.
2026-10-11 02:28:01 +02:00
retoor 871f3e5ae6 Docs audit: fix stale claims, cross-link hub docs
- README: 42 tools (was 38), /demo row, TUI section, testing plus
  integration line, full layout map
- tui.md: research link, tests pointer, soften shot-tested claim
- AGENTS.md/tests/README.md/research hubs: cross-links between docs
- Research: forward-correct tui conclusion plus INDEX activity row
  (19 failures fixed, tai_pkg deleted, 320/320 green); audit PASS
2026-10-10 23:51:06 +02:00
retoor 29c489e27a Standalone tai.py: native TUI framework, grouped classes, full suite green
- In-tai.py TUI framework (panels, compositor, z-layers, mouse, focus,
  VT screen, pty sub-terminals, widgets, animation) plus /demo, --demo,
  --demo-shot, tui.md docs, and research/tui-framework-design archive
- Focus safety: Tab/Shift+Tab always cycle, auto-focus prefers
  non-terminals, dialogs restore previous focus on close
- Native Consts/Install/Sandbox/Opencode/SysInfo classes in tai.py;
  module functions delegate so live patches apply; fixes 19 failures
- Retargeted 4 test patches to the real tai.* seams (assertions unchanged)
- Deleted tai_pkg/; tai.py is standalone stdlib-only (bare copy imports)
- AGENTS.md/README.md updated to the standalone architecture
- test_tai.py 320/320 OK, test_seal.py 16/16 OK
2026-10-10 23:45:12 +02:00
retoor 8df5cf7a45 Split monolithic tai.py into tai_pkg package, keep tai-orig.py as original
tai.py (8643 lines) is now a thin facade over tai_pkg/ (33 feature modules, one class or feature per file). tai-orig.py preserves the pre-split single-file version byte for byte (sha256 verified against snapshot); import tai and ./tai.py behavior unchanged.

Equivalence proof, all run on this tree immediately pre-commit: differential battery tai-orig.py vs facade 3718 checks 0 failures (surface, pure fuzz, tool dispatch, store/audit, release ops, fake-HOME install roundtrip, CLI runs); test_tai.py 261 OK; test_seal.py 16 OK; --version/--help byte-identical; dir(tai) zero removals.

Fix included: opencode wire-protocol schema echo leaked into replies; opencode_parse now scans with JSONDecoder.raw_decode capped at 200 scans (13 OpencodeParseTests). Test count 261 = 237 HEAD + 8 pre-existing uncommitted ToolArgumentsTests (now committed with this change, disclosed) + 13 OpencodeParseTests + 3 EmbeddedStringsTests. Patch-target-only test migration (24 hunks), zero assertion changes. tai_pkg clean-regenerated after mid-verification tamper was found and reverted.
2026-10-10 19:25:02 +02:00
retoor da51e44359 Some research 2026-10-10 09:25:33 +02:00
retoor 7add51fd02 Working perfect with alias 2026-10-10 08:27:40 +02:00
retoorandClaude Sonnet 5 c46000d7e5 Document the real backend candidate pool, the honest status check, and the box fallback
The Backends section was two lines about the primary HTTP gateway and
didn't mention the five CLI-wrapped backends, the box last-resort
fallback, the infinite-retry design, or the codex/claude/gemini/muse/grok
status distinction from the previous commit. Document all of it with the
actual measured facts (pollinations' queue cap, zen's current 403, the
Node-tarball-vs-apt size difference, opencode's zero-credential free
tier) rather than a restated feature list.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-10 06:06:10 +02:00
retoorandClaude Sonnet 5 9dbd1fe667 Add plexer subsystem, container test infra, and last-resort opencode box backend
- Plexer: always-active terminal multiplexing. NativePlexerController/
  TmuxPlexerController share one interface, chosen by which env var is
  already set (TAI_PLEXER_SOCKET vs TMUX_PANE); NativePlexerBackend is
  the pty.fork()-based wrapper (raw mode, SIGWINCH resize forwarding,
  a Unix-socket control protocol, continuous session logging, a
  Ctrl+A prefix key distinct from tmux's own Ctrl+B). Auto-wraps new
  interactive shells via a .bashrc hook mirroring the existing
  command-not-found install pattern, gated on $TMUX/$TAI_PLEXER_ACTIVE/
  a $HOME/.tai-no-plexer bypass. New plexer_send/read/type/status
  tools, env-gated on a detected controller.
- tests/container.py + docker-compose.yml + tests/docker/Dockerfile:
  a disposable podman/docker image for the tests that must never touch
  a real machine (installing the plexer hook into a real ~/.bashrc, a
  live tmux session). Required suite (test_seal.py, test_tai.py,
  tests/plexer.py, tests/bashrc.py) gates the result; tests/rag.py
  runs as best-effort since its real-LLM dependency can't be satisfied
  inside a disposable container with no nested engine.
- tests/bashrc.py: real end-to-end hook install + bash -i auto-wrap,
  anti-recursion, tmux-active skip, non-interactive skip, a live
  TmuxPlexerController test, and an install/uninstall round-trip.
- tests/plexer.py: NativePlexerBackend under a real pty, plus a
  dedicated robustness pass (file permissions, >64KB payload framing,
  a malformed control request, a genuine self-deadlock fix in the
  write-to-child path).
- tai_test.py: the shared async process-driving framework (pipe/pty
  transport, group-wide kill-and-reap, a StdinBridge for --interactive)
  both test files build on.
- ChatClient last-resort backend: when there's no local opencode CLI
  but the sandbox box is already running, install opencode into it
  (official Node tarball, not apt -- apt's nodejs/npm pull in
  webpack/babel/jest as transitive deps) and route through it instead
  of giving up. Only reachable after the first retry round already
  failed everything else; never creates the box itself. TmuxPlexerController.status()
  fixed to use `list-panes` instead of `display-message -p`, which
  silently fell back to the default pane instead of erroring against
  a dead target.
- Fix 4 pre-existing test_tai.py bugs surfaced by running it in a
  clean environment for the first time: two stale mock signatures
  (subprocess.run's stdout= kwarg, Chat.complete's deadline= kwarg),
  one $TMUX-dependent catalog assertion, one tty-dependent color
  assertion.
- research/: a sourced research archive (Source Dossier/Citation
  Card/Conclusion tiers) backing the above design decisions, with its
  own audit.py consistency checker.
- .gitignore: fix a blanket *.md rule that was about to silently drop
  the entire research/ archive from version control; add __pycache__/
  and the one-off debug transcript this session started from.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-10 04:36:05 +02:00
retoorandClaude Sonnet 5 f078ab4731 Add skill install (copy/symlink) and tmux-gated chunked RAG search
- install_skill: adopt an existing skill from a local dir, SKILL.md,
  .zip, or git URL; symlinks when the source already lives in a
  recognized external skills folder (.claude/skills, .agents/skills)
  instead of vendoring a duplicate, with install provenance recorded
  per skill. Skill discovery now also reads those external folders
  read-only, so skills placed by other tools are visible with no
  install step at all.
- export_terminal_log: full tmux scrollback export, visible only
  inside a live tmux session (TOOL_ENV_GATES, a new env-conditional
  layer on top of the existing keyword-based lazy tool loading).
- Chunked lexical RAG (SQLite FTS5 + bm25, no vectors/embeddings):
  new chunks table wired into record_save, shell/subagent output
  spill, and terminal log export, searchable via the existing search
  tool (kind chunk) with full chunk text returned, not just a
  snippet. delete_record cleans up a record's chunks with it.
- Fix a regression where create_skill's default scope silently
  became context-dependent instead of always "project".
- Rewrite README.md to document all of the above plus the existing
  context/tool-selection and search/memory internals in depth.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-09 20:05:11 +02:00
retoor 225babd70a Update documentation: Add file diff rendering to REPL, bump version to 1.17.0, enhance AGENTS.md, and add test for resume interrupt 2026-10-07 06:40:45 +02:00
retoor 85ab7d0b89 Full HTTP web_fetch, live shell streaming, progress budget (v1.16.0)
web_fetch is now a real HTTP client: methods, custom headers, string
bodies, configurable timeout, status plus content-type plus size on
every response, content-aware rendering (HTML to text, JSON raw),
and server error bodies surfaced on HTTP failures. Shell, schema
descriptions, and the system prompt steer all HTTP(S) to web_fetch;
curl/wget shell use earns a model-side hint instead of a refusal.

Shell output streams live in a rolling 4-line window for the active
agent only, with an exit line showing return code, elapsed time,
line count, and byte size. Workers, pipes, and capture stay silent.

Fixed step budget replaced by a progress budget: productive steps
(unseen actions, unseen results, user interaction) reset the stall
counter; identical repeats get a loop warning at 3 and a stop at 5;
wider stalls stop at the patience budget; 500-step total cap
backstops everything. 210 + 16 tests green.
2026-10-07 06:17:52 +02:00
retoor 99d7cb21b1 Bots, unified FTS5 search, and sealed memory-only event index (v1.14.0)
Per-profile bots (system plus history, shared vault): create_bot with
nicknames, /bots and /bot with resume, @mention one-turn routing filed
in both histories.

Unified retrieval: one trigger-synced FTS5 index over records, events,
and audit with safe tokenized MATCH, porter stemming, BM25 rank, and
marked snippets. New search tool with kind filters and graph expansion;
recall, record_search, and audit rank through the same index.

Sealed search without weakening the seal: each boot decrypts events
into a :memory: FTS5 index (newest 10000, incrementally synced, never
on disk), so sealed stores rank like plaintext ones. LIKE fallback kept
for partial tokens. Documented in README under Sealed search with the
researched alternatives (SQLCipher, blind indexes, SSE) and rejections.

Also in this batch: sysinfo, create_skill, markdown renderer with
colors, scheduler with subagent rule, --yolo/--auto, denial-loop fix,
read-before-write guard, WAL plus timeouts, records/graph/spillover,
audit time travel, self-backup and releases, lazy skill blueprints,
smart tagging, profile isolation, lazy tool payload, install
subsystem, and multi-profile rotation fix. 176 + 16 tests green.
2026-10-07 05:41:44 +02:00
retoor b1a22cd2dd Initial commit: single-file autonomous agent with sealed storage, sandbox, and telegram bot 2026-10-07 03:24:02 +02:00