From 013410999a8b044463d4505454f3fd9fc37828d5 Mon Sep 17 00:00:00 2001 From: zphinx Date: Mon, 11 May 2026 20:38:16 +0200 Subject: [PATCH] feat: finalize package presence branch and docs alignment --- CHANGELOG.md | 15 ++++++++++++++- README.md | 5 ++--- ROADMAP.md | 26 ++++++++++++++------------ src/tai/chroma_telemetry.py | 3 +++ 4 files changed, 33 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21217bc..be6f743 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,20 @@ ______________________________________________________________________ ### Added -- Nothing yet. +- Tier 3 core session memory implementation: + - new `src/tai/session_store.py` persistent ChromaDB store + - `--session-memory` option on `tai run` + - prior-session retrieval injected into analysis/follow-up prompts + - final response indexing at session end +- Planner enhancements for broader service detection: + - generic service candidate extraction from free text + - package presence probes in plans (`rpm -q` and `dpkg-query -W`) +- SSH read-only allowlist expanded to permit package presence commands (`rpm`, `dpkg-query`) +- Session memory tests in `tests/test_session_store.py` + +### Changed + +- Documentation alignment updates in README and ROADMAP to reflect implemented session memory and package-presence capabilities. ______________________________________________________________________ diff --git a/README.md b/README.md index 5571b0c..81e74c7 100644 --- a/README.md +++ b/README.md @@ -191,9 +191,8 @@ pytest tests/test_plan.py tests/test_ai.py tests/test_cli.py ## Known Limits -- Service-specific presence checks currently apply to recognized service/subsystem names. -- Package-manager-level presence checks are not yet in the default read-only command allowlist. -- Tier 3 persistent session memory is not implemented yet. +- Deep service-specific probes (known binary/config/package aliases) are richer for recognized services than generic service names. +- Session memory is available via `--session-memory`, but dedicated history UX commands (`tai history`, `/history`) are not implemented yet. ## Changelog and Roadmap diff --git a/ROADMAP.md b/ROADMAP.md index 584c9ad..0144ba1 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -143,7 +143,7 @@ ______________________________________________________________________ | Hybrid retrieval | Semantic only, BM25 only, hybrid | Hybrid (BM25 keyword + cosine semantic) for best recall | ⬜ Pending | | Reranking | None, cross-encoder (`ms-marco-MiniLM`), LLM-as-judge | Cross-encoder rerank pass before prompt injection | ⬜ Pending | | Runbook format | Markdown, YAML, JSON | Markdown (human-editable, version-controllable) | ✅ Implemented | -| Session index storage | Local `~/.tai/`, configurable path | `~/.tai/sessions/` with ChromaDB collection | ⬜ Pending | +| Session index storage | Local `~/.tai/`, configurable path | `~/.tai/sessions/` with ChromaDB collection | ✅ Implemented (core) | ______________________________________________________________________ @@ -229,28 +229,30 @@ ______________________________________________________________________ ### Tier 3 — Session Memory Index (institutional learning) -Status: ⬜ Pending +Status: ✅ Implemented (core retrieval/indexing) / ⬜ UX commands pending **Problem:** Every session starts from zero. Repeat incidents on the same host or same issue type get no benefit from past work. -**Approach:** +**Implemented now:** - On session end, embed the session summary (issue + root cause + actions) and upsert into a persistent ChromaDB collection (`~/.tai/sessions/`) - On session start, query for similar past sessions by issue text + hostname - Inject top-2 past sessions as `## Prior Sessions` context -- Optionally: `/history` command in interactive mode to surface past sessions explicitly + +**Pending UX layer:** + +- `/history` command in interactive mode to surface past sessions explicitly **New module:** `src/tai/session_store.py` - `SessionStore`: wraps ChromaDB collection at `~/.tai/sessions/` -- `index_session(session_log_path)` — embed and store completed session -- `query_similar(issue, host, top_k) -> list[PastSession]` +- `index_session(host, issue, summary, ai)` — embed and store completed session +- `query(question, host, ai, top_k) -> list[PastSession]` **Changes to existing code:** -- `session_log.py`: add `summarise() -> str` method (issue + final AI response) -- `cli.py`: query `SessionStore` at session start, index at session end +- `cli.py`: query `SessionStore` during analysis turns and index final responses at session end **Companion features buildable at same time:** @@ -308,14 +310,14 @@ ______________________________________________________________________ | Date | Decision | Outcome | |------|----------|---------| | 2026-05-04 | Implementation language | Python — with single distributable binary via Nuitka | -| — | AI inference backend | vLLM (provisional) | -| — | Default model | `gemma4:a4b` (provisional) | +| 2026-05-04 | AI backend API | OpenAI-compatible API endpoint (local Ollama by default) | +| 2026-05-04 | Default model | `gemma3:4b` | | 2026-05-04 | SSH auth methods | Keypair only (ed25519/RSA); auto-accept new hosts; reject on key change (MITM) | | 2026-05-04 | Bastion host support | `--jump-host` flag via SSH native ProxyJump | | 2026-05-04 | SSH config behavior | Use `~/.ssh/config` by default; allow override via `--ignore-ssh-config` | | 2026-05-04 | CLI vs interactive mode | Interactive: REPL for v0.1, `textual` TUI for v0.2+ | -| 2026-05-04 | RAG embedding model | `nomic-embed-text` via Ollama (local, air-gapped safe) — ⬜ pending confirmation | +| 2026-05-04 | RAG embedding model | `nomic-embed-text` via Ollama (local, air-gapped safe) | | 2026-05-04 | RAG vector store (Tier 1) | In-memory numpy cosine similarity — zero deps, session-scoped | -| 2026-05-04 | RAG vector store (Tier 2/3) | `chromadb` embedded mode (default) or `qdrant` self-hosted — ⬜ pending confirmation | +| 2026-05-04 | RAG vector store (Tier 2/3) | `chromadb` embedded mode (default) or `qdrant` self-hosted | | 2026-05-04 | RAG chunking unit | Command-boundary splitting — each collected command = one or more chunks | | 2026-05-04 | Runbook format | Markdown with YAML frontmatter, version-controlled in `runbooks/` directory | diff --git a/src/tai/chroma_telemetry.py b/src/tai/chroma_telemetry.py index 0e81c3c..609e686 100644 --- a/src/tai/chroma_telemetry.py +++ b/src/tai/chroma_telemetry.py @@ -7,6 +7,8 @@ disabled, so tai wires ChromaDB to this no-op client instead. from __future__ import annotations +from typing import override + from chromadb.config import System from chromadb.telemetry.product import ProductTelemetryClient, ProductTelemetryEvent @@ -17,6 +19,7 @@ class NoOpProductTelemetryClient(ProductTelemetryClient): def __init__(self, system: System): super().__init__(system) + @override def capture(self, event: ProductTelemetryEvent) -> None: del event return None \ No newline at end of file