// C++ side of the differential tests (tests/misc_diff.py): prints the same transcript as tests/misc_py.py. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bundled.h" #include "bundled_data.h" #include "audittool.h" #include "release.h" #include "chunker.h" #include "telegram.h" #include "ragtool.h" #include "retrievaleval.h" #include "install.h" #include "installconsts.h" #include "ftssearch.h" #include "miscdatetime.h" #include "miscjson.h" #include "miscpy.h" #include "miscsha256.h" #include "schedtime.h" #include "schedtool.h" #include "schedvault.h" #include "sqlitedb.h" #include "scheduler.h" #include "schedtool.h" #include "skillfile.h" #include "skilltool.h" using detz::misc::Json; using detz::misc::PyFs; using detz::misc::PyStr; namespace sk = detz::skills; static std::string DATA_DIR; static Json data(const std::string& name) { std::ifstream in(DATA_DIR + "/" + name, std::ios::binary); std::stringstream ss; ss << in.rdbuf(); return Json::loads(ss.str()); } static std::string j(const Json& v) { return v.dumps(); } static std::string js(const std::string& s) { return Json(s).dumps(); } static Json arr_of(const std::vector& v) { Json a = Json::array(); for (const auto& s : v) a.push(s); return a; } static void out(const std::string& line = "") { std::cout << line << "\n"; } static std::string tai_home() { return PyFs::env("TAI_HOME"); } static std::string py_bool(bool b) { return b ? "True" : "False"; } static void write(const std::string& path, const std::string& text) { PyFs::makedirs(PyFs::dirname(path)); PyFs::write_bytes(path, text); } static std::string read(const std::string& path) { return PyFs::read_bytes(path); } // ------------------------------------------------------------------ scenarios without state static void sc_json(const std::string&) { for (const auto& c : data("json_cases.json").items()) { try { Json v = Json::loads(c.as_string()); out(js(c.as_string()) + " => " + v.dumps()); } catch (const detz::misc::JsonError&) { out(js(c.as_string()) + " => ERR"); } } } static void sc_datetime(const std::string&) { for (const auto& c : data("datetime_cases.json").items()) { std::string iso, parsed; try { iso = detz::misc::DateTime::fromisoformat(c.as_string()).isoformat(); } catch (const std::exception&) { iso = "ERR"; } try { parsed = detz::sched::ScheduleTime::parse_schedule_at(c.as_string()); } catch (const std::exception& e) { parsed = std::string("ERR ") + e.what(); } bool err = parsed.rfind("ERR", 0) == 0; std::string shown = "-"; if (!err) { try { shown = js(detz::sched::ScheduleTime::local_display(parsed)); } catch (const std::exception& e) { shown = std::string("ERR ") + e.what(); } } out(js(c.as_string()) + " => " + iso + " | " + parsed + " | " + shown); } for (const auto& c : data("duration_cases.json").items()) { auto d = detz::sched::ScheduleTime::parse_duration(c.as_string()); out("duration " + js(c.as_string()) + " => " + (d ? std::to_string(*d) : std::string("null"))); } for (const auto& c : data("delay_cases.json").items()) { out("delay " + j(c) + " => " + js(detz::sched::ScheduleTime::format_delay(c.as_double()))); } } static void sc_chunk(const std::string&) { for (const auto& c : data("chunk_cases.json").items()) { std::string text = c.get("text")->as_string(); auto chunks = detz::rag::Chunker::chunk_lines(text, static_cast(c.get("max_lines")->as_int()), static_cast(c.get("overlap")->as_int()), static_cast(c.get("max_chars")->as_int())); out("chunks " + std::to_string(chunks.size()) + " " + j(arr_of(chunks))); out("collapse " + js(detz::rag::Chunker::collapse_repeated_lines(text))); out("lines " + j(arr_of(PyStr::splitlines(text)))); out("strip " + js(PyStr::strip(text))); } } static void sc_fts(const std::string&) { using detz::rag::FtsQuery; for (const auto& c : data("fts_cases.json").items()) { auto terms = FtsQuery::fts_escape(c.as_string()); out(js(c.as_string()) + " => " + (terms ? j(arr_of(*terms)) : std::string("null"))); if (terms) { out(" match " + js(FtsQuery::fts_match(*terms))); out(" or " + js(FtsQuery::fts_match_or(*terms))); out(" prefix " + js(FtsQuery::fts_match_prefix(*terms))); out(" variants " + j(arr_of(FtsQuery::fts_match_variants(*terms)))); } } out("rank " + js(FtsQuery::fts_rank("fts_docs"))); for (const std::string& raw : std::vector{"Release Notes!", " a b ", "---", std::string(40, 'x'), "\xc3\xa9t\xc3\xa9", "a,b;c", "Hello World 2", "..", "UPPER", "a-b-c"}) { out("tag " + js(raw) + " => " + js(detz::misc::TagNames::normalize_tag(raw))); } std::vector items = {"A b", "a-b", "", "C!", std::string(40, 'd'), std::string(39, 'd') + "e"}; for (int i = 0; i < 30; ++i) items.push_back("t" + std::to_string(i)); out("tags " + j(arr_of(detz::misc::TagNames::normalize_tags(items)))); } static void sc_skills_data(const std::string&) { for (const auto& kv : sk::BundledData::files()) { out("skill " + kv.first + " upstream " + js(sk::BundledData::upstream().at(kv.first))); for (const auto& f : kv.second) { out(" file " + f.first + " len " + std::to_string(f.second.size()) + " sha8 " + sk::BundledSkills::bundled_skill_digest(f.second) + " sha256 " + detz::misc::Sha256::hex(f.second)); } } for (const auto& kv : sk::BundledData::blueprints()) { out("blueprint " + kv.first + " hint " + js(kv.second.hint) + " scope " + kv.second.scope + " brief " + js(kv.second.brief)); } out("catalog empty " + js(sk::SkillFiles::skill_catalog(sk::SkillSet()))); sk::SkillSet two; sk::Skill a; a.name = "deep-research"; a.description = std::string(400, 'x'); sk::Skill b; b.name = "zeta"; b.description = "short"; two.put(a); two.put(b); out("catalog one " + js(sk::SkillFiles::skill_catalog(two))); out("prompt " + js(sk::SkillFiles::create_skill_prompt("my-skill", "do the thing", "/tmp/x/my-skill"))); for (const std::string& raw : std::vector{"a", "ab", "a-b", "-a", "a-", "A", "a_b", std::string(64, 'a'), std::string(65, 'a'), "", "a b", "0x", "a\n"}) { out("name " + js(raw) + " => " + py_bool(sk::SkillFiles::skill_name_valid(raw))); } std::vector> front_cases = { {"name: x", "description: y"}, {"name: 'x'", "description: \"y z\""}, {"name: x", "description: >", " line one", " line two", "other: v"}, {"name: x", "description: |", " line one", " line two", "tail: 1"}, {"name: x", "description: >-", " folded"}, {" indented: no", "key: va:lue", "noval", "k2:", "k3: ''quoted''"}, {"a: >", "b: 1"}, {"a: |", "\tt1", " t2", "z: 9"}, }; for (const auto& lines : front_cases) { auto front = sk::SkillFiles::parse_skill_front(lines); // json.dumps of a dict keeps the parse (insertion) order; the C++ map is sorted, so compare as sorted key objects Json o = Json::object(); for (const auto& kv : front) o.set(kv.first, kv.second); out("front " + j(arr_of(lines)) + " => " + j(o)); } } // ------------------------------------------------------------------ filesystem scenarios static Json skill_json(const std::optional& s) { if (!s) return Json(); Json o = Json::object(); o.set("name", s->name); o.set("description", s->description); o.set("body", s->body); return o; } static void sc_skills_files(const std::string& root) { std::string home = tai_home(); out("== front-matter parse"); std::vector> cases = { {"ok", "---\nname: ok\ndescription: fine one\n---\n\n\nbody here\n"}, {"crlf", "---\r\nname: crlf\r\ndescription: win\r\n---\r\nbody\r\n"}, {"nofront", "just text\n"}, {"noend", "---\nname: noend\ndescription: d\n"}, {"badname", "---\nname: Bad_Name\ndescription: d\n---\nbody\n"}, {"nodesc", "---\nname: nodesc\n---\nbody\n"}, {"quoted", "---\nname: 'quoted'\ndescription: \"has: colon\"\n---\nbody\n"}, {"folded", "---\nname: folded\ndescription: >\n one\n two\n---\nbody\n"}, {"emptybody", "---\nname: emptybody\ndescription: d\n---"}, }; for (const auto& kv : cases) { std::string path = root + "/parse/" + kv.first + "/SKILL.md"; write(path, kv.second); out("parse " + kv.first + " => " + j(skill_json(sk::SkillFiles::parse_skill_file(path)))); } out("== discover with external roots and collisions"); std::string proj = root + "/proj"; std::string uh = PyFs::home(); write(uh + "/.claude/skills/ext-home/SKILL.md", "---\nname: ext-home\ndescription: from claude home\n---\nbody ext-home\n"); write(uh + "/.claude/skills/shared/SKILL.md", "---\nname: shared\ndescription: external copy\n---\nbody external\n"); write(proj + "/.claude/skills/proj-claude/SKILL.md", "---\nname: proj-claude\ndescription: project claude\n---\nbody pc\n"); write(proj + "/.agents/skills/proj-agents/SKILL.md", "---\nname: proj-agents\ndescription: project agents\n---\nbody pa\n"); write(home + "/skills/shared/SKILL.md", "---\nname: shared\ndescription: tai copy\n---\nbody tai\n"); write(home + "/skills/native/SKILL.md", "---\nname: native\ndescription: native home skill\n---\nbody native\n"); write(home + "/skills/native/scripts/run.sh", "echo hi\n"); write(home + "/skills/native/references/ref.md", "ref\n"); write(home + "/skills/native/assets/a.txt", "a\n"); write(proj + "/.tai/skills/proj-tai/SKILL.md", "---\nname: proj-tai\ndescription: project tai skill\n---\nbody ptai\n"); write(home + "/skills/broken/SKILL.md", "no front matter\n"); write(home + "/skills/stray-file.txt", "x\n"); { Json meta = Json::object(); meta.set("source", "/some/path"); meta.set("mode", "symlink"); meta.set("scope", "home"); meta.set("n", 3); Json nested = Json::object(); Json arr = Json::array(); arr.push(1).push(Json()).push(true); nested.set("a", arr); meta.set("nested", nested); sk::SkillFiles::save_skill_install_meta(home + "/skills", "native", meta); } auto skills = sk::SkillFiles::discover_skills(home, proj); for (const auto& s : skills.items()) { out("skill " + s.name + " root=" + s.root + " managed=" + py_bool(s.managed) + " install=" + (s.install ? j(*s.install) : std::string("null")) + " note=" + js(sk::SkillFiles::skill_provenance_note(s)) + " extras=" + j(arr_of(sk::SkillFiles::skill_extra_files(s.root)))); out(" desc=" + js(s.description) + " body=" + js(s.body)); } std::vector order; for (const auto& s : skills.items()) order.push_back(s.name); out("order " + j(arr_of(order))); out("catalog " + js(sk::SkillFiles::skill_catalog(skills))); out("== scope helpers"); for (const std::string scope : {"project", "home"}) { out("scope_dir " + scope + " " + sk::SkillFiles::skill_scope_dir(scope, home, proj, "nm")); out("scope_dir " + scope + " " + sk::SkillFiles::skill_scope_dir(scope, home, proj)); } std::string empty = root + "/emptyproj"; PyFs::makedirs(empty); out("default scope empty " + sk::SkillFiles::default_skill_scope(empty)); for (const std::string marker : {".git", ".tai", ".claude/skills", ".agents/skills"}) { std::string tag = PyStr::replace_all(marker, "/", "_"); std::string d = root + "/mk-" + tag; PyFs::makedirs(d + "/" + marker); out("default scope " + marker + " " + sk::SkillFiles::default_skill_scope(d)); } for (const std::string path : {"/a/b/.claude/skills/x", "/a/b/.agents/skills/x/", "/a/b/other/x", "/a/.claude/skills/../x", "/a/.claude/skills//x"}) { out("external " + path + " " + py_bool(sk::SkillFiles::is_external_skill_root(path))); } auto none = sk::SkillFiles::load_skill_install_meta(home + "/skills", "nothing"); out("meta none " + (none ? j(*none) : std::string("null"))); write(home + "/skills/.installs/bad.json", "{not json"); auto bad = sk::SkillFiles::load_skill_install_meta(home + "/skills", "bad"); out("meta bad " + (bad ? j(*bad) : std::string("null"))); auto ok = sk::SkillFiles::load_skill_install_meta(home + "/skills", "native"); out("meta ok " + (ok ? j(*ok) : std::string("null"))); } static int count_files(const std::string& base) { return static_cast(PyFs::walk_files(base).size()); } static void sc_skills_install_tool(const std::string& root); static void sc_skills_ops(const std::string& root) { std::string home = tai_home(); sk::BundledSkills bundled; auto step = [&](const std::string& label, const std::string& mode) { out(label + " => " + bundled.op_skills(mode, home)); out(" status: " + sk::BundledSkills::status_line(home)); out(" files: " + std::to_string(count_files(home + "/skills"))); }; out("-- fresh"); step("install", "install"); step("install again", "install"); step("upgrade clean", "upgrade"); { std::string s = "["; bool first = true; for (const auto& n : sk::BundledSkills::names()) { s += std::string(first ? "" : ", ") + (sk::BundledSkills::bundled_skill_pristine(home, n) ? "true" : "false"); first = false; } out("pristine all " + s + "]"); } std::string taste = home + "/skills/taste/SKILL.md"; write(taste, "user rewrite"); out("-- modified taste"); step("install", "install"); step("upgrade", "upgrade"); std::string extra = home + "/skills/taste/mynotes.md"; write(extra, "mine"); step("reinstall", "reinstall"); out("extra kept " + py_bool(PyFs::isfile(extra))); out("-- crlf edit counts as equal after universal newlines"); std::string path = home + "/skills/plan/SKILL.md"; write(path, PyStr::replace_all(read(path), "\n", "\r\n")); step("upgrade crlf", "upgrade"); out("-- uninstall with user content"); std::string gitp = home + "/skills/git/SKILL.md"; write(gitp, "user rewrite git"); std::string gextra = home + "/skills/git/mynotes.md"; write(gextra, "mine"); write(home + "/skills/git/sub/deep.txt", "deep"); step("uninstall", "uninstall"); step("uninstall again", "uninstall"); PyFs::remove(gitp); PyFs::remove(gextra); PyFs::remove(home + "/skills/git/sub/deep.txt"); step("uninstall final", "uninstall"); step("uninstall empty", "uninstall"); out("-- sidecar-only uninstall"); { Json meta = Json::object(); meta.set("files", Json::object()); sk::SkillFiles::save_skill_install_meta(home + "/skills", "taste", meta); } step("uninstall sidecar", "uninstall"); out("-- tracked-digest removal of an edited file"); step("install", "install"); auto meta = sk::SkillFiles::load_skill_install_meta(home + "/skills", "taste"); std::vector keys; for (const auto& m : meta->members()) keys.push_back(m.first); std::sort(keys.begin(), keys.end()); out("meta " + j(arr_of(keys))); out("meta files " + j(*meta->get("files"))); { Json fixed = Json::object(); for (const char* k : {"source", "mode", "scope", "upstream", "bundled_version"}) fixed.set(k, *meta->get(k)); out("meta fixed " + j(fixed)); } chmod((home + "/skills/taste").c_str(), 0500); step("install readonly", "install"); chmod((home + "/skills/taste").c_str(), 0700); out("-- error path: skills dir is a file"); std::string other = root + "/home2"; PyFs::makedirs(other); write(other + "/skills", "file not dir"); out("blocked => " + bundled.op_skills("install", other)); out("blocked uninstall => " + bundled.op_skills("uninstall", other)); sc_skills_install_tool(root); } class SkillApp : public sk::SkillHost { public: std::string home_, cwd_; sk::SkillSet skills_; int depth_{0}; std::vector approvals; int refreshed{0}; bool approve{true}; std::function runner; bool ask_approval(const std::string& what) override { approvals.push_back(what); return approve; } std::string home() const override { return home_; } std::string cwd() const override { return cwd_; } int depth() const override { return depth_; } const sk::SkillSet& skills() const override { return skills_; } void refresh_skills() override { ++refreshed; skills_ = sk::SkillFiles::discover_skills(home_, cwd_); } void audit_skill_install(const std::string&, const std::string&) override {} std::string run_worker(const std::string& prompt, int timeout, int) override { return runner(prompt, timeout); } }; static std::string dispatch(sk::SkillTool& tool, const std::string& name, const Json& args) { try { if (name == "install_skill") return tool.run_install_skill(args); if (name == "load_skill") return tool.run_load_skill(args); if (name == "create_skill") return tool.run_create_skill(args); } catch (const std::exception& e) { return "error: " + PyStr::short_error(e.what()); } return "error: unknown tool " + name; } static Json obj(std::initializer_list> kv) { Json o = Json::object(); for (const auto& p : kv) o.set(p.first, p.second); return o; } static std::string replace_root(std::string s, const std::string& root) { return PyStr::replace_all(s, root, ""); } static void sc_skills_install_tool(const std::string& root) { std::string home = tai_home(); std::string proj = root + "/proj"; if (chdir(proj.c_str()) != 0) return; SkillApp app; app.home_ = home; app.cwd_ = proj; app.skills_ = sk::SkillFiles::discover_skills(home, proj); sk::SkillTool tool(app); out("== install_skill / load_skill / create_skill"); std::string src = root + "/srcskill"; write(src + "/SKILL.md", "---\nname: from-src\ndescription: installed from a dir\n---\nbody src\n"); write(src + "/scripts/x.sh", "echo x\n"); write(src + "/references/r.md", "r\n"); std::string nested = root + "/nested"; write(nested + "/inner/SKILL.md", "---\nname: inner\ndescription: nested one\n---\nbody inner\n"); std::string two = root + "/two"; write(two + "/a/SKILL.md", "---\nname: aa\ndescription: d\n---\nb\n"); write(two + "/b/SKILL.md", "---\nname: bb\ndescription: d\n---\nb\n"); std::string barefile = root + "/bare.md"; write(barefile, "---\nname: bare-one\ndescription: bare md file\n---\nbody bare\n"); std::string badfile = root + "/bad.md"; write(badfile, "no front\n"); std::string ext = proj + "/.claude/skills/extskill"; write(ext + "/SKILL.md", "---\nname: extskill\ndescription: external one\n---\nbody ext\n"); auto call = [&](const std::string& label, const Json& args) { out(label + " => " + js(dispatch(tool, "install_skill", args))); }; call("empty", obj({{"source", ""}})); call("bad scope", obj({{"source", src}, {"scope", "mars"}})); call("bad mode", obj({{"source", src}, {"mode", "link"}})); call("missing", obj({{"source", root + "/nope"}})); call("dir home", obj({{"source", src}, {"scope", "home"}})); call("dir home again denied?", obj({{"source", src}, {"scope", "home"}})); app.approve = false; call("dir home denied", obj({{"source", src}, {"scope", "home"}})); app.approve = true; call("dir home force", obj({{"source", src}, {"scope", "home"}, {"force", true}})); call("dir project", obj({{"source", src}, {"scope", "project"}, {"name", "renamed"}})); call("bad name", obj({{"source", src}, {"name", "Bad Name"}})); call("nested", obj({{"source", nested}, {"scope", "home"}})); call("two children", obj({{"source", two}, {"scope", "home"}})); call("bare file", obj({{"source", barefile}, {"scope", "home"}})); call("bad file", obj({{"source", badfile}})); call("symlink ext default", obj({{"source", ext}, {"scope", "home"}})); call("symlink explicit", obj({{"source", src}, {"scope", "home"}, {"mode", "symlink"}, {"name", "linked"}, {"force", true}})); call("symlink bare", obj({{"source", barefile}, {"mode", "symlink"}})); call("not a skill source", obj({{"source", "/dev/null"}})); out("approvals " + j(arr_of(app.approvals))); out("refreshed " + std::to_string(app.refreshed)); out("skills " + j(arr_of(app.skills_.sorted_names()))); out("== load_skill"); app.skills_ = sk::SkillFiles::discover_skills(home, proj); for (const std::string name : {"from-src", "deep-research", "extskill", "nonexistent", ""}) { out("load " + js(name) + " => " + js(dispatch(tool, "load_skill", obj({{"name", name}})))); } out("== create_skill"); app.runner = [](const std::string& prompt, int timeout) { return "worker said: " + PyStr::prefix(prompt, 40) + " t=" + std::to_string(timeout); }; auto create = [&](const Json& args) { out("create " + j(args) + " => " + js(dispatch(tool, "create_skill", args))); }; create(obj({{"name", ""}, {"brief", "b"}})); create(obj({{"name", "ok-name"}, {"brief", ""}})); create(obj({{"name", "ok-name"}, {"brief", "b"}, {"scope", "mars"}})); create(obj({{"name", "made"}, {"brief", "make it"}, {"scope", "home"}, {"timeout", 10}})); create(obj({{"name", "made2"}, {"brief", "make it"}, {"timeout", "abc"}})); create(obj({{"name", "made3"}, {"brief", "x"}, {"timeout", 99999}})); app.depth_ = 5; out("create depth => " + js(dispatch(tool, "create_skill", obj({{"name", "dd"}, {"brief", "b"}})))); app.depth_ = 0; app.runner = [&](const std::string& prompt, int) { std::string marker = "Create a new agent skill named 'wrote' in "; std::string path = prompt.substr(marker.size()); path = path.substr(0, path.find(".\n")); write(path + "/SKILL.md", "---\nname: wrote\ndescription: written by worker\n---\nbody wrote\n"); return replace_root("done writing " + path, root); }; out("create written => " + js(replace_root(dispatch(tool, "create_skill", obj({{"name", "wrote"}, {"brief", "w"}, {"scope", "home"}})), root))); out("create replaced => " + js(replace_root(dispatch(tool, "create_skill", obj({{"name", "wrote"}, {"brief", "w"}, {"scope", "home"}})), root))); out("refreshed " + std::to_string(app.refreshed)); } // ------------------------------------------------------------------ install scenarios namespace ins = detz::install; class FakeBox : public ins::ContainerPort { public: std::optional engine; std::string state = "running", venv = "venv ready", ensure_error; std::optional container_engine() override { return engine; } std::string box_state(const std::string&) override { return state; } std::string box_venv_state(const std::string&) override { return venv; } std::string ensure_box() override { if (!ensure_error.empty()) throw detz::misc::BackendError(ensure_error); return engine ? *engine : std::string(); } }; static ins::InstallContext install_ctx() { ins::InstallContext ctx; const char* py = std::getenv("MISC_TAI_PY"); ctx.script_path = py ? py : "tai.py"; return ctx; } static void sc_install_hooks(const std::string& root) { FakeBox box; ins::StdIo io; ins::Installer inst(install_ctx(), box, io); std::string home = tai_home(); std::string uh = PyFs::home(); std::string rc = uh + "/.bashrc", zrc = uh + "/.zshrc"; write(rc, "# my bashrc\nexport A=1"); write(zrc, ""); auto status = [&] { return PyStr::join(inst.status_lines(home), "\n"); }; std::vector>> steps = { {"bash install", [&] { return inst.op_hook("install", home); }}, {"bash install again", [&] { return inst.op_hook("install", home); }}, {"bash upgrade", [&] { return inst.op_hook("upgrade", home); }}, {"plexer install", [&] { return inst.op_plexer_hook("install", home); }}, {"plexer again", [&] { return inst.op_plexer_hook("install", home); }}, {"zsh install", [&] { return inst.op_zsh_hook("install", home); }}, {"zsh again", [&] { return inst.op_zsh_hook("install", home); }}, {"fish install", [&] { return inst.op_fish_hook("install", home); }}, {"zsh plexer install", [&] { return inst.op_zsh_plexer_hook("install", home); }}, {"fish plexer install", [&] { return inst.op_fish_plexer_hook("install", home); }}, {"status", status}, {"bash reinstall", [&] { return inst.op_hook("reinstall", home); }}, {"plexer reinstall", [&] { return inst.op_plexer_hook("reinstall", home); }}, {"zsh uninstall", [&] { return inst.op_zsh_hook("uninstall", home); }}, {"fish uninstall", [&] { return inst.op_fish_hook("uninstall", home); }}, {"zsh uninstall again", [&] { return inst.op_zsh_hook("uninstall", home); }}, {"bash uninstall", [&] { return inst.op_hook("uninstall", home); }}, {"plexer uninstall", [&] { return inst.op_plexer_hook("uninstall", home); }}, {"zsh plexer uninstall", [&] { return inst.op_zsh_plexer_hook("uninstall", home); }}, {"fish plexer uninstall", [&] { return inst.op_fish_plexer_hook("uninstall", home); }}, {"status after", status}, }; for (const auto& st : steps) out(st.first + " => " + js(st.second())); out("rc " + js(read(rc))); out("zrc " + js(read(zrc))); out("== stale and duplicated blocks"); const std::string& begin = ins::InstallConsts::BASHRC_MARK_BEGIN(); const std::string& end = ins::InstallConsts::BASHRC_MARK_END(); write(rc, "head\n" + begin + "\nstale content\n" + end + "\nmid\n" + begin + "\nsecond stale\n" + end + "\ntail\n"); out("install over stale => " + js(inst.op_hook("install", home))); out("rc " + js(read(rc))); write(rc, "x\n" + begin + "\nunterminated\n"); out("install over unterminated => " + js(inst.op_hook("install", home))); out("rc " + js(read(rc))); out("uninstall unterminated => " + js(inst.op_hook("uninstall", home))); out("rc " + js(read(rc))); write(rc, "crlf\r\nline\r\n"); out("install crlf => " + js(inst.op_hook("install", home))); out("rc " + js(read(rc))); chmod(rc.c_str(), 0400); out("readonly install => " + js(inst.op_plexer_hook("install", home))); chmod(rc.c_str(), 0600); auto present = [&](const std::string& path) { Json a = Json::array(); for (bool b : {ins::Installer::hook_present(path), ins::Installer::zsh_hook_present(path), ins::Installer::fish_hook_present(path), ins::Installer::plexer_hook_present(path), ins::Installer::zsh_plexer_hook_present(path), ins::Installer::fish_plexer_hook_present(path)}) a.push(b); return a; }; out("present " + j(present(rc))); out("present missing " + j(present(root + "/nope"))); for (const std::string shell : {"bash", "zsh", "fish", "other"}) out("rc path " + shell + " " + ins::Install::shell_rc_path(shell)); setenv("XDG_CONFIG_HOME", (root + "/xdg").c_str(), 1); out("rc path fish xdg " + ins::Install::shell_rc_path("fish")); unsetenv("XDG_CONFIG_HOME"); Json sp = Json::array(); for (const char* n : {"zsh", "fish", "bash", "nosuchshell"}) sp.push(ins::Install::shell_present(n)); out("shell_present " + j(sp)); } class InstallApp : public sk::SkillHost { public: std::string home_, cwd_; sk::SkillSet skills_; std::vector approvals; bool approve{true}; bool ask_approval(const std::string& what) override { approvals.push_back(what); return approve; } std::string home() const override { return home_; } std::string cwd() const override { return cwd_; } const sk::SkillSet& skills() const override { return skills_; } void refresh_skills() override { skills_ = sk::SkillFiles::discover_skills(home_, cwd_); } void audit_skill_install(const std::string&, const std::string&) override {} std::string run_worker(const std::string&, int, int) override { return ""; } }; static void set_box(FakeBox& box, std::optional engine, const std::string& state = "running", const std::string& venv = "venv ready", const std::string& err = "") { box.engine = engine; box.state = state; box.venv = venv; box.ensure_error = err; } static void sc_install_ops(const std::string& root) { FakeBox box; set_box(box, std::string("podman")); ins::StdIo io; ins::Installer inst(install_ctx(), box, io); std::string home = tai_home(); std::string uh = PyFs::home(); out("-- binary"); for (const std::string mode : {"install", "install", "upgrade", "reinstall", "uninstall", "uninstall", "install"}) out("binary " + mode + " => " + js(inst.op_binary(mode, home))); std::string target = uh + "/.local/bin/tai.py"; { struct stat st; stat(target.c_str(), &st); struct stat st2; stat(install_ctx().script_path.c_str(), &st2); char buf[16]; std::snprintf(buf, sizeof buf, "0o%o", static_cast(st.st_mode & 0777)); out(std::string("binary mode ") + buf + " size_equal " + py_bool(st.st_size == st2.st_size)); } write(target, "VERSION = \"0.0.1\"\n"); out("binary refresh => " + js(inst.op_binary("install", home))); out("binary refresh upgrade => " + js(inst.op_binary("upgrade", home))); auto opt_js = [](const std::optional& v) { return v ? js(*v) : std::string("null"); }; out("script_version " + opt_js(inst.script_version(target)) + " " + opt_js(inst.script_version(root + "/nope")) + " " + opt_js(inst.script_version(install_ctx().script_path))); write(root + "/v1", "x\nVERSION = \"1.2.3\"\ny\n"); write(root + "/v2", "VERSION = \"1.2\"\n VERSION = \"1.2.3\"\nVERSION = \"a.b.c\"\n"); write(root + "/v3", "VERSION = \"4.5.6\""); write(root + "/v4", "VERSION = \"4.5.6\" \n"); { std::string s = "["; bool first = true; for (const char* n : {"v1", "v2", "v3", "v4"}) { s += std::string(first ? "" : ", ") + (inst.script_version(root + "/" + n) ? js(*inst.script_version(root + "/" + n)) : std::string("null")); first = false; } out("script_version v1..v4 " + s + "]"); } out("-- venv"); for (const std::string mode : {"install", "install", "reinstall", "uninstall", "uninstall", "install"}) out("venv " + mode + " => " + js(inst.op_venv(mode, home))); { auto vp = ins::Install::venv_python(home); out("venv helpers " + js(ins::Install::venv_dir(home)) + " " + js(vp ? *vp : std::string()) + " " + py_bool(ins::Install::venv_available())); } out("-- services (no systemctl)"); for (const std::string mode : {"install", "install", "reinstall", "upgrade", "uninstall", "uninstall"}) out("scheduler " + mode + " => " + js(inst.op_scheduler_service(mode, home))); out("telegram without token => " + js(inst.op_telegram_service("install", home))); out("service_exec " + js(inst.service_exec(home, "/x/tai.py", {"--a", "b"}))); out("unit_dir " + js(ins::Systemd::unit_dir(home))); { auto st = ins::Systemd::service_state("tai-scheduler.service"); Json a = Json::array(); a.push(st.first).push(st.second); out("service_state " + j(a)); } out("activate " + js(ins::Systemd::activate_service("tai-scheduler.service"))); { auto r = ins::Systemd::remove_service_unit(home, "tai-scheduler.service"); Json a = Json::array(); a.push(r.first).push(r.second); out("remove " + j(a)); } out("-- container"); for (const std::string mode : {"install", "upgrade", "reinstall", "uninstall"}) out("container " + mode + " => " + js(inst.op_container(mode, home))); set_box(box, std::string("podman"), "stopped", "no venv (reinstall container to rebuild)"); for (const std::string mode : {"install", "reinstall"}) out("container stopped " + mode + " => " + js(inst.op_container(mode, home))); set_box(box, std::string("podman"), "running", "venv ready", "engine exploded"); out("container failing => " + js(inst.op_container("install", home))); set_box(box, std::nullopt); for (const std::string mode : {"install", "uninstall"}) out("container noengine " + mode + " => " + js(inst.op_container(mode, home))); set_box(box, std::string("podman")); out("-- install_report"); struct Rep { std::string action; std::vector targets; }; std::vector reps = {{"install", {"skills", "bash-hook", "venv"}}, {"status", {"skills"}}, {"upgrade", ins::InstallConsts::INSTALL_TARGETS()}, {"uninstall", {"skills", "bash-hook", "plexer-hook", "binary"}}}; for (const auto& r : reps) out("report " + r.action + " " + PyStr::replace_all(PyStr::replace_all(j(arr_of(r.targets)), "\"", "'"), ",", ",") + " =>\n" + inst.install_report(r.action, r.targets, home)); out("-- install_self"); for (const std::string shell : {"/bin/bash", "/usr/bin/zsh", "/usr/bin/fish", "", "/bin/sh"}) { setenv("SHELL", shell.c_str(), 1); out("shell " + js(shell)); std::cout.flush(); inst.install_self(); } out("-- install tool"); InstallApp app; app.home_ = home; app.cwd_ = root; auto run_tool = [&](const Json& args) { return inst.run_install(args, home, nullptr, [&](const std::string& w) { return app.ask_approval(w); }, [&] { app.refresh_skills(); }); }; std::vector tool_args = { obj({{"action", "bogus"}}), obj({{"action", "status"}}), obj({{"action", "STATUS "}, {"targets", arr_of({"skills"})}}), obj({{"action", "install"}, {"targets", arr_of({"nope"})}}), obj({{"action", "install"}, {"targets", Json::array()}}), obj({{"action", "install"}, {"targets", "skills"}}), obj({{"action", "install"}, {"targets", arr_of({"skills"})}}), obj({{"action", "uninstall"}, {"targets", arr_of({"skills", "bash-hook"})}}), Json::object()}; for (const auto& args : tool_args) { app.skills_ = sk::SkillSet(); std::string res = run_tool(args); out("tool " + j(args) + " => " + js(res) + " | skills=" + j(arr_of(app.skills_.sorted_names()))); } app.approve = false; out("tool denied => " + js(run_tool(obj({{"action", "install"}, {"targets", arr_of({"skills"})}})))); out("approvals " + j(arr_of(app.approvals))); } static void sc_install_noengine(const std::string&) { FakeBox box; set_box(box, std::nullopt); ins::StdIo io; ins::Installer inst(install_ctx(), box, io); std::string home = tai_home(); auto lines = inst.status_lines(home); Json a = Json::array(); for (std::size_t i = lines.size() - 3; i < lines.size(); ++i) a.push(lines[i]); out(j(a)); for (const std::string mode : {"install", "uninstall"}) out("container " + mode + " => " + js(inst.op_container(mode, home))); } static void sc_vault_line(const std::string&) { std::string home = tai_home(); out("empty " + js(ins::Installer::vault_line(home))); write(home + "/memory.db", ""); out("kept " + js(ins::Installer::vault_line(home))); } // ------------------------------------------------------------------ scheduler scenario namespace sc = detz::sched; using detz::misc::DbValue; using detz::misc::SqliteDb; static Json db_json(const DbValue& v) { if (v.is_null()) return Json(); if (v.is_int()) return Json(v.as_int()); return Json(v.as_text()); } static Json row_json(const detz::misc::DbRow& r) { Json a = Json::array(); for (const auto& v : r) a.push(db_json(v)); return a; } // Seal with passphrase disabled: lock is the identity, unlock follows Seal.unlock's error paths. class DisabledSeal : public sc::SealPort { public: bool enabled() const override { return false; } std::string lock(const std::string& plain) const override { return plain; } std::string unlock(const std::string& sealed) const override { if (!is_sealed(sealed)) throw sc::SealError("not sealed"); std::size_t d1 = sealed.find('$'), d2 = sealed.find('$', d1 + 1), d3 = sealed.find('$', d2 + 1); if (d2 == std::string::npos || d3 == std::string::npos) throw sc::SealError("sealed value is corrupt"); throw sc::SealError("seal check failed"); } std::string safe_unlock(const std::string& sealed) const override { try { return unlock(sealed); } catch (const sc::SealError&) { return "[sealed: decrypt failed]"; } } }; // The tag side of Store as the schedule code uses it (no plural folding needed for the test vocabulary) class SqlTags : public sc::TagPort { public: SqlTags(SqliteDb& db) : db_(db) {} void tag_item(const std::string& item, const std::vector& tags, const std::string& profile) override { for (const auto& t : detz::misc::TagNames::normalize_tags(tags)) db_.execute("INSERT OR IGNORE INTO tags (item, tag, profile) VALUES (?, ?, ?)", {item, t, profile}); } void set_item_tags(const std::string& item, const std::vector& tags, const std::string& profile) override { db_.execute("DELETE FROM tags WHERE item = ? AND profile = ?", {item, profile}); tag_item(item, tags, profile); } std::vector item_tags(const std::string& item, const std::string& profile) override { std::vector out_tags; for (const auto& r : db_.query("SELECT tag FROM tags WHERE item = ? AND profile = ? ORDER BY tag", {item, profile})) out_tags.push_back(r[0].as_text()); return out_tags; } private: SqliteDb& db_; }; // Registry with spawn_agent/poll_agent/running_agents semantics of tai.py and the Gate runner of the Python twin. class FakeAgents : public sc::AgentPort { public: struct Rec { std::string task, status = "running", result; std::thread th; }; std::mutex m; std::map> recs; int next = 1; bool report_zero = false; std::mutex gm; std::condition_variable gcv; bool released = false; ~FakeAgents() override { release(); for (auto& kv : recs) { if (kv.second->th.joinable()) kv.second->th.join(); } } void release() { std::lock_guard lock(gm); released = true; gcv.notify_all(); } int running_agents() override { if (report_zero) return 0; std::lock_guard lock(m); int n = 0; for (auto& kv : recs) n += kv.second->status == "running"; return n; } int spawn_agent(const std::string& task, const std::string&, int64_t) override { std::lock_guard lock(m); int live = 0; for (auto& kv : recs) live += kv.second->status == "running"; if (live >= 8) throw sc::AgentsFull("too many live agents (" + std::to_string(live) + " of 8)"); int id = next++; auto rec = std::make_unique(); rec->task = task; Rec* raw = rec.get(); recs[id] = std::move(rec); raw->th = std::thread([this, raw, task] { std::string result, status; try { if (task.rfind("slow:", 0) == 0) { std::unique_lock lock(gm); gcv.wait_for(lock, std::chrono::seconds(30), [this] { return released; }); result = "slow done " + task; } else if (task.find("boom") != std::string::npos) { throw std::runtime_error("boom"); } else { result = "ok:" + task; } status = result.find("[time limit reached]") != std::string::npos ? "timeout" : "done"; } catch (const std::exception& e) { result = std::string("worker failed: ") + PyStr::short_error(e.what()); status = "error"; } std::lock_guard lock(m); raw->result = result; raw->status = status; }); return id; } std::pair poll_agent(int id, double wait) override { Rec* rec; { std::lock_guard lock(m); auto it = recs.find(id); if (it == recs.end()) return {"missing", "no agent " + std::to_string(id)}; rec = it->second.get(); } auto deadline = std::chrono::steady_clock::now() + std::chrono::duration(wait); while (std::chrono::steady_clock::now() < deadline) { { std::lock_guard lock(m); if (rec->status != "running") break; } std::this_thread::sleep_for(std::chrono::milliseconds(5)); } std::lock_guard lock(m); if (rec->status == "running") return {rec->status, "agent " + std::to_string(id) + " still running (0s elapsed)"}; if (rec->status == "error") return {rec->status, rec->result}; std::string text = rec->result; std::size_t n = PyStr::len(text); if (n > 6000 + 2000 + 50) text = PyStr::prefix(text, 6000) + "\n...[" + std::to_string(n - 6000 - 2000) + " chars truncated]...\n" + PyStr::suffix(text, 2000); if (text.empty()) text = "(empty result)"; return {rec->status, text}; } }; class SchedApp : public sc::ScheduleHost { public: std::vector approvals; bool approve = true; sc::ScheduleVault* vault_ = nullptr; bool ask_approval(const std::string& what) override { approvals.push_back(what); return approve; } std::string active_profile() const override { return "t"; } sc::ScheduleVault& vault() override { return *vault_; } }; static void sched_rows(SqliteDb& db, const std::string& label) { out("-- " + label); for (const auto& r : db.query("SELECT id, name, prompt, profile, every_sec, next_run, timeout, status, last_status, last_result, updated FROM schedules ORDER BY id")) out("row " + j(row_json(r))); Json tags = Json::array(); for (const auto& r : db.query("SELECT item, tag, profile FROM tags WHERE item LIKE 'sched:%' ORDER BY item, tag")) tags.push(row_json(r)); out("tags " + j(tags)); } static void sc_sched(const std::string&) { std::string db_path = tai_home() + "/memory.db"; SqliteDb db(db_path, 30, true); DisabledSeal seal; SqlTags tags(db); FakeAgents agents; sc::Scheduler scheduler(db_path, seal, agents); sc::ScheduleVault vault(db, seal, tags, "t"); auto quiesce = [&] { scheduler.join_watchers(); }; auto item_json = [](const sc::ScheduleItem& it) { Json o = Json::object(); o.set("id", it.id).set("name", it.name).set("prompt", it.prompt).set("profile", it.profile).set("every", it.every).set("next_run", it.next_run).set("timeout", it.timeout); o.set("status", it.status).set("last", it.last).set("result", it.result).set("created", it.created).set("tags", arr_of(it.tags)); return o; }; const std::string NOW = "2026-10-25T12:00:00+00:00"; out("== list and format"); auto items = vault.list_schedules(); for (const auto& it : items) out("item " + j(item_json(it))); out("format:\n" + sc::Scheduler::format_schedule_lines(items, detz::misc::DateTime::fromisoformat(NOW))); out("format empty: " + sc::Scheduler::format_schedule_lines({}, detz::misc::DateTime::fromisoformat(NOW))); { Json a = Json::array(); for (const auto& it : vault.list_schedules("other")) a.push(item_json(it)); out("other profile: " + j(a)); } out("== running leases"); std::string nowiso = detz::misc::DateTime::now_utc().isoformat(); struct Lease { std::string status, updated; int64_t timeout; }; for (const auto& l : std::vector{{"running", nowiso, 600}, {"running", "2000-01-01T00:00:00+00:00", 600}, {"done", "", 0}, {"running", "garbage", 10}, {"running", nowiso, 0}}) { out("running " + l.status + " " + (l.updated.rfind("2026", 0) == 0 ? std::string("now") : l.updated) + " " + std::to_string(l.timeout) + " => " + py_bool(sc::Scheduler::schedule_running(l.status, l.updated, l.timeout))); } out("== sealed rows (added now: Store refuses to open with them present and no passphrase)"); vault.add_schedule("sealed", "tai1$AAAA$BBBB$CCCC", "t", 0, "2026-05-01T00:00:00+00:00", 300); vault.add_schedule("sealed-corrupt", "tai1$bad", "t", 0, "2026-05-02T00:00:00+00:00", 300); out("== tick 1"); std::cout.flush(); int fired = scheduler.scheduler_tick(); out("fired " + std::to_string(fired)); quiesce(); sched_rows(db, "after tick 1"); out("== tick 2"); out("fired " + std::to_string(scheduler.scheduler_tick())); quiesce(); out("== cap phase"); for (int k = 0; k < 9; ++k) vault.add_schedule("slow" + std::to_string(k), "slow:" + std::to_string(k), "t", 315360000, "2025-01-0" + std::to_string(k + 1) + "T00:00:00+00:00", 600); out("fired " + std::to_string(scheduler.scheduler_tick())); out("fired " + std::to_string(scheduler.scheduler_tick())); out("running agents " + std::to_string(agents.running_agents())); out("fired " + std::to_string(scheduler.scheduler_tick())); sched_rows(db, "after cap ticks"); out("== AgentsFull restore branch"); vault.add_schedule("full-once", "slow:once", "t", 0, "2025-02-01T00:00:00+00:00", 600); agents.report_zero = true; out("fired " + std::to_string(scheduler.scheduler_tick())); agents.report_zero = false; sched_rows(db, "after AgentsFull"); agents.release(); quiesce(); sched_rows(db, "after release"); out("== schedule tool"); SchedApp app; app.vault_ = &vault; sc::ScheduleTool tool(app); auto dispatch_tool = [&](const std::string& name, const Json& args) { try { if (name == "schedule") return tool.run_schedule(args); if (name == "unschedule") return tool.run_unschedule(args); return tool.run_schedules(args); } catch (const std::exception& e) { return "error: " + PyStr::short_error(e.what()); } }; std::vector calls = { obj({{"prompt", ""}}), obj({{"prompt", "p"}}), obj({{"prompt", "p"}, {"at", "2099-01-01T10:00"}, {"every", 120}}), obj({{"prompt", "p"}, {"at", "nonsense"}}), obj({{"prompt", "p"}, {"every", "abc"}}), obj({{"prompt", "p"}, {"every", 30}}), obj({{"prompt", "tool one"}, {"at", "2099-01-01T10:00"}, {"name", std::string(100, 'N')}, {"timeout", 5}, {"tags", arr_of({"Alpha", "beta!", "", std::string(40, 'x')})}}), obj({{"prompt", "tool two"}, {"every", 3600}, {"timeout", "abc"}, {"profile", "t"}}), obj({{"prompt", "tool three"}, {"every", "7200"}, {"timeout", 99999}}), obj({{"prompt", "p"}, {"every", 60}, {"profile", "other"}}), obj({{"prompt", "p"}, {"every", 60}, {"profile", "bad profile!"}}), obj({{"prompt", "p"}, {"every", 60}, {"tags", "notalist"}}), obj({{"prompt", "p"}, {"every", 60}, {"tags", [] { Json a = Json::array(); a.push(1).push(2); return a; }()}}), obj({{"prompt", "tool four"}, {"every", 86400}, {"tags", Json::array()}}), obj({{"prompt", " padded "}, {"at", "2099-02-02T02:02:02Z"}, {"timeout", 0}}), obj({{"prompt", "p"}, {"at", "9999-12-31T24:00"}}), obj({{"prompt", "p"}, {"at", "0001-01-01T00:00"}}), obj({{"prompt", "p"}, {"every", 60.5}}), obj({{"prompt", "p"}, {"every", true}}), }; for (const auto& args : calls) out("schedule " + j(args) + " => " + js(dispatch_tool("schedule", args))); app.approve = false; out("schedule denied => " + js(dispatch_tool("schedule", obj({{"prompt", "denied"}, {"every", 60}})))); app.approve = true; out("schedules => " + js(dispatch_tool("schedules", Json::object()))); for (const auto& args : std::vector{obj({{"id", 999}}), obj({{"id", "abc"}}), obj({{"id", 0}}), Json::object(), obj({{"id", "13"}}), obj({{"id", 14.0}})}) { out("unschedule " + j(args) + " => " + js(dispatch_tool("unschedule", args))); } app.approve = false; out("unschedule denied => " + js(dispatch_tool("unschedule", obj({{"id", 15}})))); app.approve = true; out("approvals " + j(arr_of(app.approvals))); sched_rows(db, "final"); out("remove nonexistent " + py_bool(vault.remove_schedule(99999))); } // ------------------------------------------------------------------ rag scenario namespace rg = detz::rag; static Json hit_json(const rg::FtsHit& h) { Json o = Json::object(); o.set("item", db_json(h.item)).set("kind", h.kind).set("sub", h.sub).set("title", h.title).set("snippet", h.snippet).set("rank", h.rank); return o; } static Json ids_json(const std::vector& v) { Json a = Json::array(); for (const auto& x : v) a.push(db_json(x)); return a; } static Json mean_json(const rg::EvalMean& m) { Json o = Json::object(); o.set("hit", m.hit).set("mrr", m.mrr).set("ndcg", m.ndcg).set("cp", m.cp); return o; } static Json result_json(const rg::EvalResult& r) { Json o = Json::object(); o.set("k", r.k).set("goldens", r.goldens).set("overall", mean_json(r.overall)); Json cats = Json::object(); for (const auto& c : r.by_category) { Json e = Json::object(); e.set("goldens", c.second.goldens).set("mean", mean_json(c.second.mean)); cats.set(c.first, e); } o.set("by_category", cats); Json misses = Json::array(); for (const auto& m : r.misses) { Json e = Json::object(); e.set("query", m.query).set("category", m.category); misses.push(e); } o.set("misses", misses); return o; } static Json pairs_json(const rg::ImplicitPairs& p) { Json o = Json::object(); o.set("searches", p.searches).set("read_follow", p.read_follow); Json pairs = Json::array(); for (const auto& x : p.pairs) { Json e = Json::object(); e.set("query", x.query).set("read", x.read).set("rank", x.rank ? Json(*x.rank) : Json()).set("shown", x.shown); pairs.push(e); } o.set("pairs", pairs).set("zero_hit", arr_of(p.zero_hit)).set("zero_hit_count", p.zero_hit_count); return o; } class RagApp : public rg::RagHost { public: RagApp(SqliteDb& db, rg::FtsSearch& fts, rg::ChunkStore& chunks) : db_(db), fts_(fts), chunks_(chunks) {} std::string active_profile() const override { return "t"; } bool sealed() const override { return false; } std::vector fts_search(const std::string& q, const std::vector& kinds, const std::string& profile, int limit) override { return fts_.fts_search(q, kinds, profile, limit); } std::vector events_like(const std::string&, const std::string&, int) override { return {}; } void log_event(const std::string& profile, const std::string& role, const std::string& kind, const std::string& text) override { std::string value = PyStr::prefix(text, 2000); db_.execute("INSERT INTO events (profile, ts, role, kind, text, tags) VALUES (?, ?, ?, ?, ?, ?)", {profile, detz::sched::ScheduleTime::now_iso(), role, kind, value, " " + role + " " + kind + " "}); } std::map get_chunks_by_ids(const std::vector& ids, const std::string& profile) override { return chunks_.get_chunks_by_ids(ids, profile); } std::vector edges_for(const std::string& item, const std::string& profile) override { std::vector out_edges; for (const auto& r : db_.query("SELECT src, dst, relation FROM edges WHERE profile = ? AND (src = ? OR dst = ?)", {profile, item, item})) { out_edges.push_back({r[0].as_text() == item ? r[1].as_text() : r[0].as_text(), r[2].as_text()}); } return out_edges; } std::string profiles_dir() const override { return tai_home() + "/profiles"; } std::string node_title(const std::string& node, const std::string& profile) override { std::size_t colon = node.find(':'); if (colon == std::string::npos) return node; std::string kind = node.substr(0, colon), key = node.substr(colon + 1); if (kind == "mem") { auto rows = db_.query("SELECT title FROM records WHERE profile = ? AND id = ?", {profile, node}); if (rows.empty()) return node; std::string t = rows[0][0].as_text(); return t.empty() ? node : t; } if (kind == "secret") return "secret:" + key; auto rows = db_.query("SELECT name, prompt FROM schedules WHERE profile = ? AND id = ?", {profile, std::stoll(key)}); if (rows.empty()) return node; std::string name = rows[0][0].as_text(); return name.empty() ? "schedule #" + key : name; } private: SqliteDb& db_; rg::FtsSearch& fts_; rg::ChunkStore& chunks_; }; static void sc_rag(const std::string&) { SqliteDb db(tai_home() + "/memory.db", 30, true); rg::FtsSearch fts(db, "t"); rg::ChunkStore chunks(db, "t"); out("== fts_search"); std::vector queries = {"alpha", "alpha bravo", "zebra", "caf\xc3\xa9", "r\xc3\xa9sum\xc3\xa9 na\xc3\xafve", "snake_case", "numbers123", "nothingmatchesthis", "", "!!!", "retrieval index", "wh", "ALPHA", std::string(50, 'x'), "unique zebra jumps", "message", "file", "ro", "v2beta kernel"}; std::vector> kind_sets = {{}, {"record"}, {"chunk"}, {"audit"}, {"event"}, {"record", "chunk"}}; for (const auto& q : queries) { for (const auto& kinds : kind_sets) { for (int limit : {10, 3}) { Json a = Json::array(); for (const auto& h : fts.fts_search(q, kinds, "t", limit)) a.push(hit_json(h)); out("search " + js(q) + " " + j(arr_of(kinds)) + " " + std::to_string(limit) + " => " + j(a)); } } } { Json a = Json::array(); for (const auto& h : fts.fts_search("alpha", {"record"}, "other", 10)) a.push(hit_json(h)); out("other profile " + j(a)); Json l = Json::array(); for (int n : {0, -5, 1, 100, 500}) l.push(static_cast(fts.fts_search("alpha", {}, "t", n).size())); out("limits " + j(l)); } out("== eval"); out("tokens " + j(arr_of(rg::RetrievalEval::tokens("The quick Brown fox jumped_over 123456 abcdefg ABCDEFG caf\xc3\xa9s r\xc3\xa9sum\xc3\xa9s")))); out("tokens4 " + j(arr_of(rg::RetrievalEval::tokens("The quick Brown fox", 4)))); auto goldens = rg::RetrievalEval::goldens_from_vault(db, "t"); out("goldens " + std::to_string(goldens.size())); for (const auto& g : goldens) { Json o = Json::object(); o.set("query", g.query).set("expected", ids_json(g.expected)).set("category", g.category).set("source", db_json(g.source)); out("golden " + j(o)); } auto result = rg::RetrievalEval::score(fts, goldens, "t"); out("score " + j(result_json(result))); auto pairs = rg::RetrievalEval::implicit_pairs(db, "t", false); out("pairs " + j(pairs_json(pairs))); out("report:\n" + rg::RetrievalEval::format_report(result, pairs)); out("empty report:\n" + rg::RetrievalEval::format_report(rg::RetrievalEval::score(fts, {}, "t"), rg::RetrievalEval::implicit_pairs(db, "nobody", false))); out("small k " + j(result_json(rg::RetrievalEval::score(fts, std::vector(goldens.begin(), goldens.begin() + 12), "t", {"record"}, 1, 2)))); { auto ro = [](std::optional r) { return r ? Json(*r) : Json(); }; out("rank_of " + j(ro(rg::RetrievalEval::rank_of(DbValue("b"), {DbValue("a"), DbValue("b")}))) + " " + j(ro(rg::RetrievalEval::rank_of(DbValue("z"), {DbValue("a")}))) + " " + j(ro(rg::RetrievalEval::best_rank({DbValue("z"), DbValue("a")}, {DbValue("q"), DbValue("a"), DbValue("z")})))); Json m = Json::array(); m.push(rg::RetrievalEval::hit_at_k(3)).push(rg::RetrievalEval::hit_at_k(6)).push(rg::RetrievalEval::hit_at_k(std::nullopt)).push(rg::RetrievalEval::reciprocal_rank(4)); m.push(rg::RetrievalEval::ndcg_at_k(3)).push(rg::RetrievalEval::ndcg_at_k(9)); m.push(rg::RetrievalEval::context_precision({DbValue("a"), DbValue("c")}, {DbValue("a"), DbValue("b"), DbValue("c"), DbValue("d")})); m.push(rg::RetrievalEval::context_precision({}, {DbValue("a")})).push(rg::RetrievalEval::context_precision({DbValue("x")}, {DbValue("a")})); out("metrics " + j(m)); } for (const std::string& text : std::vector{"q=alpha\thits=record:m1,m2,,chunk:4", "nothing", "q=\thits=a", "a=b=c\tq= spaced \thits= x ,y ", "q=a\tq=b\thits=record:z"}) { auto parsed = rg::RetrievalEval::parse_search_event(text); Json v; if (parsed) { v = Json::array(); v.push(parsed->first).push(arr_of(parsed->second)); } out("parse " + js(text) + " => " + j(v)); } out("== chunks table"); std::string rid = db.query("SELECT id FROM records WHERE profile = 't' ORDER BY id LIMIT 1")[0][0].as_text(); chunks.add_chunks("record", rid, {"one", "two", "three"}, "t"); Json rows_j = Json::array(); std::vector ids; for (const auto& r : db.query("SELECT id, parent_kind, parent_id, idx, text, profile FROM chunks WHERE parent_id = ? ORDER BY idx", {rid})) { rows_j.push(row_json(r)); ids.push_back(r[0].as_int()); } out("chunks " + j(rows_j)); auto chunk_obj = [](const std::map& m) { Json o = Json::object(); for (const auto& kv : m) { Json c = Json::object(); c.set("parent_kind", kv.second.parent_kind).set("parent_id", kv.second.parent_id).set("idx", kv.second.idx).set("text", kv.second.text); o.set(std::to_string(kv.first), c); } return o; }; std::vector ask = ids; ask.push_back(999999); out("by ids " + j(chunk_obj(chunks.get_chunks_by_ids(ask, "t")))); out("by ids empty " + j(chunk_obj(chunks.get_chunks_by_ids({}, "t"))) + " other " + j(chunk_obj(chunks.get_chunks_by_ids(ids, "other")))); chunks.add_chunks("record", rid, {}, "t"); out("after clear " + std::to_string(db.query("SELECT COUNT(*) FROM chunks WHERE parent_id = ?", {rid})[0][0].as_int())); out("== search tool"); RagApp host(db, fts, chunks); rg::RagTool tool(host); std::vector calls = { obj({{"query", ""}}), obj({{"query", "alpha"}}), obj({{"query", "alpha"}, {"limit", 3}}), obj({{"query", "alpha"}, {"limit", "2"}}), obj({{"query", "alpha"}, {"limit", "x"}}), obj({{"query", "alpha"}, {"limit", 0}}), obj({{"query", "alpha"}, {"limit", -2}}), obj({{"query", "retrieval"}, {"kinds", arr_of({"chunk"})}}), obj({{"query", "alpha"}, {"kinds", arr_of({"record"})}, {"expand", true}, {"limit", 20}}), obj({{"query", "alpha"}, {"kinds", arr_of({"bogus"})}}), obj({{"query", "alpha"}, {"kinds", Json::array()}}), obj({{"query", "alpha"}, {"kinds", "record"}}), obj({{"query", "file"}, {"kinds", arr_of({"audit"})}}), obj({{"query", "message"}, {"kinds", arr_of({"event"})}}), obj({{"query", "zebra"}, {"kinds", arr_of({"record", "chunk", "audit"})}}), obj({{"query", "nothingmatchesthis"}}), obj({{"query", std::string(200, 'x')}}), obj({{"query", " spaced "}}), obj({{"query", "caf\xc3\xa9 r\xc3\xa9sum\xc3\xa9"}, {"limit", 50}}), }; for (const auto& args : calls) { std::string res; try { res = tool.run_search(args); } catch (const std::exception& e) { res = "error: " + PyStr::short_error(e.what()); } out("search " + j(args) + " => " + js(res)); } Json logged = Json::array(); for (const auto& r : db.query("SELECT profile, role, kind, text FROM events WHERE kind = 'search-query' ORDER BY id DESC LIMIT 25")) logged.push(row_json(r)); out("logged " + j(logged)); } // ------------------------------------------------------------------ telegram scenarios namespace tg = detz::telegram; class LogRec : public detz::misc::LogSink { public: void event(const std::string& level, const std::string& component, const std::string& event, const std::string& message, const std::string& profile, const std::string& bot, const std::string& chat, const Json& meta) override { Json a = Json::array(); a.push(level).push(component).push(event).push(message).push(profile).push(bot).push(chat).push(meta); out("LOG " + j(a)); } void set_context(const std::string& key, const std::string& value) override { out("CTX set " + key + "=" + value); } void clear_context(const std::string& key) override { out("CTX pop " + key); } }; class TgAgent : public tg::TelegramAgent { public: std::string home_; std::string profile() const override { return "t"; } std::string bot() const override { return "main"; } std::string home() const override { return home_; } void reset_history() override { out("AGENT reset_history"); } std::string run_turn(const std::string& text) override { out("AGENT run_turn " + js(text) + " capture=True"); if (text.find("explode") != std::string::npos) throw std::runtime_error("agent exploded"); if (text == "long") return std::string(9000, 'L'); if (text == "empty reply") return ""; return "echo:" + text; } void start_scheduler() override { out("start_scheduler called"); } }; class TgBox : public tg::BoxPort { public: std::string ensure_box() override { out("BOX ensure_box"); if (std::getenv("MISC_BOX_FAIL")) throw detz::misc::BackendError("box down"); return "eng"; } std::string box_transcribe(const std::string& engine, const std::string& audio) override { // Python prints the audio decoded as latin-1: each byte becomes one code point std::string latin; for (unsigned char c : audio) { if (c < 0x80) latin += static_cast(c); else { latin += static_cast(0xC0 | (c >> 6)); latin += static_cast(0x80 | (c & 0x3F)); } } out("BOX transcribe " + engine + " " + js(latin)); if (audio == "AUDIO") return "voice says hi"; return "error: model failed"; } }; struct StopLoop {}; static std::string tg_api() { return std::getenv("MISC_TG_API"); } static std::string tg_file() { return std::getenv("MISC_TG_FILE"); } static void sc_telegram(const std::string& root) { LogRec log; TgAgent agent; TgBox box; std::string home = tai_home(); agent.home_ = home; tg::TelegramBot bot(agent, nullptr, &box, log); bot.set_api(tg_api(), tg_file()); out("== token"); std::string env_path = tg::TelegramBot::telegram_env_path(home); out("env path " + js(env_path)); unsetenv("TELEGRAM_BOT_TOKEN"); out("no file => " + js(tg::TelegramBot::load_telegram_token(home))); std::vector> bodies = { {"plain", "TELEGRAM_BOT_TOKEN=abc123\n"}, {"quoted", "TELEGRAM_BOT_TOKEN='abc'\n"}, {"dquoted", "TELEGRAM_BOT_TOKEN=\"x y\"\n"}, {"spaces", "TELEGRAM_BOT_TOKEN= spaced \n"}, {"other first", "OTHER=1\nTELEGRAM_BOT_TOKEN=second\nTELEGRAM_BOT_TOKEN=third\n"}, {"none", "OTHER=1\n"}, {"empty", ""}, {"crlf", "TELEGRAM_BOT_TOKEN=win\r\n"}, {"noeq", "TELEGRAM_BOT_TOKEN\n"}, {"eq in value", "TELEGRAM_BOT_TOKEN=a=b=c\n"}, {"quotes only", "TELEGRAM_BOT_TOKEN=''\n"}, {"indented", " TELEGRAM_BOT_TOKEN=nope\n"}}; for (const auto& b : bodies) { write(env_path, b.second); out("file " + b.first + " => " + js(tg::TelegramBot::load_telegram_token(home))); } setenv("TELEGRAM_BOT_TOKEN", "fromenv", 1); out("env wins => " + js(tg::TelegramBot::load_telegram_token(home))); setenv("TELEGRAM_BOT_TOKEN", "", 1); out("empty env falls back => " + js(tg::TelegramBot::load_telegram_token(home))); unsetenv("TELEGRAM_BOT_TOKEN"); PyFs::remove(env_path); out("== call / send"); tg::Telegram good("good", nullptr, tg_api(), tg_file()); out("getMe " + j(good.call("getMe", Json::object()))); for (const std::pair& c : std::vector>{{"bad token", "bad"}, {"not json", "nojson"}}) { try { tg::Telegram t(c.second, nullptr, tg_api(), tg_file()); out(c.first + " => " + j(t.call("getMe", Json::object()))); } catch (const std::exception& e) { out(c.first + " => raised " + js(PyStr::short_error(e.what()))); } } std::string e1(4001, 'x'); std::string em; for (int i = 0; i < 4001; ++i) em += "\xc3\xa9"; std::string smile; for (int i = 0; i < 4100; ++i) smile += "\xf0\x9f\x98\x80"; std::string lines; for (int i = 0; i < 2000; ++i) lines += "line\n"; for (const std::string& text : std::vector{"", "short", std::string(4000, 'S'), std::string(4001, 'S'), std::string(8000, 'S'), std::string(8001, 'S'), em, smile, lines}) { tg::TelegramBot::telegram_send(good, Json(42), text); } tg::TelegramBot::telegram_send(good, Json("chat-as-string"), "hi"); out("== updates"); auto chat = [](const Json& id) { return obj({{"id", id}}); }; auto msg = [&](const Json& id, std::initializer_list> extra) { Json m = Json::object(); m.set("chat", chat(id)); for (const auto& p : extra) m.set(p.first, p.second); return m; }; std::vector updates; auto upd = [&](int id, const Json& message) { updates.push_back(obj({{"update_id", id}, {"message", message}})); }; upd(1, msg(111, {{"text", "/start"}})); upd(2, msg(111, {{"text", "/new"}})); upd(3, msg(111, {{"text", "hello there"}})); upd(4, msg(111, {{"text", ""}})); upd(5, msg(111, {{"text", " "}})); upd(6, msg(0, {{"text", "zero chat"}})); upd(7, obj({{"text", "no chat"}})); updates.push_back(obj({{"update_id", 8}})); upd(9, msg(111, {})); upd(10, msg(111, {{"text", "explode please"}})); upd(11, msg(111, {{"text", "long"}})); upd(12, msg(111, {{"text", "empty reply"}})); upd(13, msg(222, {{"voice", obj({{"file_id", "f1"}})}})); upd(14, msg(222, {{"audio", obj({{"file_id", "f1"}})}})); upd(15, msg(222, {{"voice", obj({{"file_id", "nofile"}})}})); upd(16, msg(222, {{"voice", obj({{"file_id", "fbad"}})}})); upd(17, msg(222, {{"voice", obj({{"file_id", "fmodel"}})}})); upd(18, msg(222, {{"voice", Json::object()}, {"text", "text with empty voice"}})); upd(19, msg(222, {{"voice", obj({{"file_id", "fmissingfile"}})}})); upd(20, msg(Json("str-chat"), {{"text", "string chat id"}})); upd(21, msg(111, {{"text", "unicode caf\xc3\xa9 \xf0\x9f\x98\x80"}})); for (const auto& update : updates) { out("-- update " + j(update)); try { bot.handle_telegram_update(good, update); } catch (const std::exception& e) { out("raised " + js(PyStr::short_error(e.what()))); } } setenv("MISC_BOX_FAIL", "1", 1); out("-- box failing"); try { bot.handle_telegram_update(good, obj({{"update_id", 30}, {"message", msg(5, {{"voice", obj({{"file_id", "f1"}})}})}})); } catch (const std::exception& e) { out("raised " + js(PyStr::short_error(e.what()))); } unsetenv("MISC_BOX_FAIL"); out("== run_telegram_bot"); struct Run { const char* label; const char* token; }; for (const Run& r : {Run{"missing", nullptr}, Run{"bad auth", "bad"}, Run{"not json", "nojson"}, Run{"no username", "noname"}, Run{"good", "good"}}) { unsetenv("TELEGRAM_BOT_TOKEN"); if (r.token) setenv("TELEGRAM_BOT_TOKEN", r.token, 1); out(std::string("-- run ") + r.label); std::cout.flush(); try { int res = bot.run_telegram_bot(nullptr, [](int seconds) { out("sleep " + std::to_string(seconds)); throw StopLoop(); }); out("returned " + std::to_string(res)); } catch (const StopLoop&) { out("stopped by sleep"); } std::cout.flush(); } (void)root; } static void sc_telegram_install(const std::string& root) { LogRec log; TgAgent agent; TgBox box; FakeBox fbox; set_box(fbox, std::string("podman")); ins::StdIo io; ins::InstallContext ctx = install_ctx(); ctx.telegram_api = tg_api(); ins::Installer inst(ctx, fbox, io); std::string home = tai_home(); struct T { const char* label; const char* token; }; for (const T& t : {T{"good", "good"}, T{"bad", "bad"}, T{"nojson", "nojson"}, T{"noname", "noname"}}) { setenv("TELEGRAM_BOT_TOKEN", t.token, 1); out(std::string("-- install_telegram ") + t.label); std::cout.flush(); int res = inst.install_telegram(); out("returned " + std::to_string(res)); out("unit exists " + py_bool(PyFs::exists(PyFs::home() + "/.config/systemd/user/tai-telegram.service")) + " env exists " + py_bool(PyFs::exists(tg::TelegramBot::telegram_env_path(home)))); } unsetenv("TELEGRAM_BOT_TOKEN"); out("-- install_telegram without token (stdin closed)"); std::cout.flush(); out("returned " + std::to_string(inst.install_telegram())); out("-- install_telegram box failing"); set_box(fbox, std::string("podman"), "running", "venv ready", "no engine here"); setenv("TELEGRAM_BOT_TOKEN", "good", 1); out("returned " + std::to_string(inst.install_telegram())); set_box(fbox, std::string("podman")); out("-- op_telegram_service"); for (const char* token : {"good", "bad", "nojson", "noname"}) { setenv("TELEGRAM_BOT_TOKEN", token, 1); for (const std::string mode : {"install", "install", "reinstall", "upgrade", "uninstall", "uninstall"}) { out(std::string("service ") + token + " " + mode + " => " + js(inst.op_telegram_service(mode, home))); } } unsetenv("TELEGRAM_BOT_TOKEN"); write(tg::TelegramBot::telegram_env_path(home), "TELEGRAM_BOT_TOKEN=good\n"); out("service from env file => " + js(inst.op_telegram_service("install", home))); out("-- uninstall_telegram"); out("returned " + std::to_string(inst.uninstall_telegram())); set_box(fbox, std::nullopt); out("returned noengine " + std::to_string(inst.uninstall_telegram())); (void)root; } // ------------------------------------------------------------------ release / audit scenarios using detz::install::SelfRelease; class NoRecords : public ins::FileRecordPort { public: void upsert_file_record(const std::string&, const std::string&, const std::optional&) override {} // records table: store module }; class RelHost : public ins::ReleaseHost { public: std::vector approvals; bool approve = true; std::string script, home_; ins::AuditVault* vault_ = nullptr; bool ask_approval(const std::string& w) override { approvals.push_back(w); return approve; } std::string home() const override { return home_; } std::string audit_actor() const override { return "main"; } ins::AuditPort& store() override { return *vault_; } std::string script_file() const override { return script; } }; static std::string copy_file_bytes(const std::string& from, const std::string& to) { write(to, PyFs::read_bytes(from)); return to; } static void sc_release(const std::string& root) { SqliteDb db(tai_home() + "/memory.db", 30, true); ins::AuditVault vault(db, "t"); NoRecords records; std::string script = copy_file_bytes(install_ctx().script_path, root + "/script.py"); std::string folder = root + "/backups"; out("== versions"); struct V { const char* cur; const char* part; }; for (const V& v : {V{"1.2.3", "patch"}, V{"1.2.3", "minor"}, V{"1.2.3", "major"}, V{"9.9.9", "patch"}, V{"1.2.3", "bogus"}, V{"1.2", "patch"}, V{"a.b.c", "patch"}, V{"1.2.3.4", "patch"}}) { try { out(std::string("next ") + v.cur + " " + v.part + " => " + SelfRelease::next_version(v.cur, v.part)); } catch (const std::exception& e) { out(std::string("next ") + v.cur + " " + v.part + " => raised " + js(e.what())); } } out("== bump"); for (const char* part : {"patch", "minor", "major", "patch"}) { auto r = SelfRelease::release_bump(script, part); Json a = Json::array(); a.push(r.first).push(r.second); out(std::string("bump ") + part + " => " + j(a)); } { std::string text = PyFs::read_text(script); std::vector vl; for (const auto& l : PyStr::splitlines(text)) { if (l.rfind("VERSION = ", 0) == 0) vl.push_back(l); } out("version line " + j(arr_of(vl))); } write(root + "/nov.py", "print('x')\n"); for (const std::string& f : {root + "/nov.py", root + "/missing.py"}) { try { SelfRelease::release_bump(f, "patch"); } catch (const std::exception& e) { out("bump error " + js(replace_root(e.what(), root))); } } write(root + "/crlf.py", "a\r\nVERSION = \"1.0.0\"\r\nb\r\n"); { auto r = SelfRelease::release_bump(root + "/crlf.py", "patch"); Json a = Json::array(); a.push(r.first).push(r.second); out("crlf bump " + j(a) + " " + js(PyFs::read_bytes(root + "/crlf.py"))); } out("== snapshots and pruning"); auto snap = SelfRelease::snapshot_self(script, folder, "9.9.9"); out("snapshot " + js(PyFs::basename(snap.first)) + " " + snap.second); out("has digest " + py_bool(SelfRelease::backup_has_digest(folder, snap.second)) + " " + py_bool(SelfRelease::backup_has_digest(folder, "deadbeef"))); out("has digest nofolder " + py_bool(SelfRelease::backup_has_digest(root + "/nofolder", snap.second))); for (int i = 0; i < 14; ++i) { char name[128]; std::snprintf(name, sizeof name, "/tai-0.0.%d-20200101T000000Z-%08d.py", i, i); write(folder + name, "x"); struct timespec ts[2] = {{1000 + i, 0}, {1000 + i, 0}}; utimensat(AT_FDCWD, (folder + name).c_str(), ts, 0); } write(folder + "/notes.txt", "keep"); out("pruned " + std::to_string(SelfRelease::prune_backups(folder))); out("left " + PyStr::replace_all(j(arr_of(PyFs::listdir_sorted(folder))), PyFs::basename(snap.first), "")); out("prune missing " + std::to_string(SelfRelease::prune_backups(root + "/nofolder"))); out("== ensure_self_backup"); PyFs::rmtree(folder); auto first = SelfRelease::ensure_self_backup(tai_home(), &vault, &records, script, "1.44.2"); out("ensure first " + (first ? js(PyFs::basename(*first)) : std::string("null"))); auto second = SelfRelease::ensure_self_backup(tai_home(), &vault, &records, script, "1.44.2"); out("ensure second " + (second ? js(PyFs::basename(*second)) : std::string("null"))); { Json rows = Json::array(); for (const auto& r : db.query("SELECT actor, action, path, message, old_size, new_size, tags FROM audit ORDER BY id")) rows.push(row_json(r)); out("audit " + j(rows)); } out("== do_release"); try { SelfRelease::do_release(vault, script, folder, "patch", " "); } catch (const std::exception& e) { out("blank message " + js(e.what())); } { auto r = SelfRelease::do_release(vault, script, folder, "minor", " ship it ", "tester"); Json a = Json::array(); a.push(r.old_version).push(r.new_version).push(PyFs::basename(r.dest)).push(r.row_id); out("do_release " + j(a)); } out("== release tool"); RelHost host; host.script = script; host.home_ = tai_home(); host.vault_ = &vault; ins::ReleaseTool tool(host); auto rel = [&](const Json& args) { try { return tool.run_release(args); } catch (const std::exception& e) { return "error: " + PyStr::short_error(e.what()); } }; for (const Json& args : std::vector{obj({{"part", ""}, {"message", "m"}}), obj({{"part", "patch"}, {"message", ""}}), obj({{"part", "huge"}, {"message", "m"}}), obj({{"part", "Patch"}, {"message", "tool release"}}), obj({{"part", "major"}, {"message", std::string(300, 'm')}})}) { out("release " + j(args) + " => " + js(replace_root(rel(args), root))); } host.approve = false; out("release denied => " + js(rel(obj({{"part", "patch"}, {"message", "no"}})))); host.approve = true; write(script, "nothing here\n"); out("release no version => " + js(rel(obj({{"part", "patch"}, {"message", "m"}})))); out("approvals " + j(arr_of(host.approvals))); out("== capped helpers"); { auto c0 = SelfRelease::capped_text(std::nullopt); auto c1 = SelfRelease::capped_text(std::string("abc")); std::string e; for (int i = 0; i < 20001; ++i) e += "\xc3\xa9"; auto c2 = SelfRelease::capped_text(e); auto c3 = SelfRelease::capped_text(std::string(20001, 'x')); auto pair_json = [](const std::pair, std::optional>& p) { Json a = Json::array(); a.push(p.first ? Json(*p.first) : Json()).push(p.second ? Json(*p.second) : Json()); return a; }; Json all = Json::array(); all.push(pair_json(c0)).push(pair_json(c1)).push(Json(*c2.second)); all.push(Json(PyStr::suffix(*c3.first, 60))).push(SelfRelease::trunc_marker(5)); out("capped " + j(all)); } std::string big = "line\r\nsecond\rthird\n"; for (int i = 0; i < 30000; ++i) big += "\xc3\xa9"; write(root + "/big.txt", big); for (int cap : {20000, 50, 100000}) { auto r = SelfRelease::read_capped(root + "/big.txt", static_cast(cap)); out("read_capped " + std::to_string(cap) + " => " + (r.text ? js(PyStr::prefix(*r.text, 40)) : std::string("null")) + " " + (r.size ? std::to_string(*r.size) : std::string("null"))); } write(root + "/bad.bin", std::string("ok \xff\xfe bad \xc3 end")); { auto r = SelfRelease::read_capped(root + "/bad.bin"); Json a = Json::array(); a.push(r.text ? Json(*r.text) : Json()).push(r.size ? Json(*r.size) : Json()); out("read_capped invalid " + j(a)); } { auto r = SelfRelease::read_capped(root + "/missing"); Json a = Json::array(); a.push(Json()).push(Json()); out("read_capped missing " + j(a)); (void)r; } } class AuditApp : public ins::AuditHost { public: std::vector approvals; bool approve = true; ins::AuditVault* vault_ = nullptr; rg::FtsSearch* fts_ = nullptr; bool ask_approval(const std::string& w) override { approvals.push_back(w); return approve; } std::string active_profile() const override { return "t"; } ins::AuditVault& vault() override { return *vault_; } rg::FtsSearch& fts() override { return *fts_; } void read_files_add(const std::string&) override {} void audit_file(const std::string& action, const std::string& path, const std::optional& o, const std::optional& n, const std::string& message) override { vault_->audit_event_full("main", action, path, message, o, n, {"file", action}, std::nullopt, std::nullopt, "t"); } void upsert_file_record(const std::string&, const std::string&, const std::string&) override {} // records table: store module void note_diff(const std::string&, const std::optional&, const std::optional&) override {} std::string box_restore(const std::string&, const std::string&, const std::string&) override { return "box restore unsupported in this test"; } }; static void sc_audit(const std::string& root) { SqliteDb db(tai_home() + "/memory.db", 30, true); ins::AuditVault vault(db, "t"); rg::FtsSearch fts(db, "t"); AuditApp app; app.vault_ = &vault; app.fts_ = &fts; ins::AuditTool tool(app); auto call = [&](const std::string& name, const Json& args) { std::string res; try { res = name == "audit" ? tool.run_audit(args) : name == "tags" ? tool.run_tags(args) : tool.run_restore(args); } catch (const std::exception& e) { res = "error: " + PyStr::short_error(e.what()); } out(name + " " + j(args) + " => " + js(replace_root(res, root))); }; out("== audit"); for (const Json& a : std::vector{Json::object(), obj({{"limit", 3}}), obj({{"path", "area/a.txt"}}), obj({{"tag", "file"}}), obj({{"tag", "nomatch"}}), obj({{"query", "alpha"}}), obj({{"query", "write"}}), obj({{"query", "zzzz"}}), obj({{"limit", "x"}}), obj({{"limit", 500}}), obj({{"query", "unicode"}})}) call("audit", a); out("== tags"); for (const Json& a : std::vector{Json::object(), obj({{"prefix", "al"}}), obj({{"prefix", "A_"}}), obj({{"prefix", "a%"}}), obj({{"prefix", "zzz"}}), obj({{"limit", 1}}), obj({{"limit", "x"}}), obj({{"prefix", " Al "}})}) call("tags", a); out("== restore"); for (const Json& a : std::vector{Json::object(), obj({{"id", "x"}}), obj({{"id", 9999}}), obj({{"id", 6}}), obj({{"id", 7}}), obj({{"id", 1}}), obj({{"id", 3}}), obj({{"id", 4}}), obj({{"id", 5}}), obj({{"id", 12}}), obj({{"id", 10}}), obj({{"id", 11}})}) call("restore", a); for (const std::string rel : {"a.txt", "sub/deep/r.txt", "u.txt"}) { std::string path = "area/" + rel; out("file " + rel + " " + (PyFs::exists(path) ? js(read(path)) : std::string("null"))); } app.approve = false; call("restore", obj({{"id", 2}})); app.approve = true; Json ap = Json::array(); for (const auto& a : app.approvals) ap.push(replace_root(a, root)); out("approvals " + j(ap)); } int main(int argc, char** argv) { if (argc < 3) return 2; std::string name = argv[1], root = argv[2]; bool prepare = argc > 3 && std::string(argv[3]) == "prepare"; const char* data_dir = std::getenv("MISC_DATA_DIR"); DATA_DIR = data_dir ? data_dir : "tests/misc_data"; if (chdir((root + "/proj").c_str()) != 0) return 2; if (prepare) return 0; // C++ never prepares: the Python side builds fixtures with the real tai.py static const std::map> scenarios = { {"json", sc_json}, {"datetime", sc_datetime}, {"chunk", sc_chunk}, {"fts", sc_fts}, {"skills_data", sc_skills_data}, {"skills_files", sc_skills_files}, {"skills_ops", sc_skills_ops}, {"install_hooks", sc_install_hooks}, {"install_hooks_noshell", sc_install_hooks}, {"install_ops", sc_install_ops}, {"install_noengine", sc_install_noengine}, {"vault_line", sc_vault_line}, {"sched", sc_sched}, {"rag", sc_rag}, {"release", sc_release}, {"audit", sc_audit}, {"telegram", sc_telegram}, {"telegram_install", sc_telegram_install}, }; auto it = scenarios.find(name); if (it == scenarios.end()) { std::cerr << "unknown scenario " << name << "\n"; return 2; } it->second(root); return 0; }