1168 lines
58 KiB
Python
1168 lines
58 KiB
Python
#!/usr/bin/env python3
|
|
"""Python reference driver for the differential tests of the misc modules (tests/misc_diff.py).
|
|
|
|
Runs one scenario against the real tai.py and prints a line-oriented transcript. The C++ side
|
|
(tests/misc_driver.cpp) prints the same transcript for the same scenario; the orchestrator diffs them.
|
|
Usage: misc_py.py SCENARIO ROOT [prepare]
|
|
"""
|
|
import importlib.util
|
|
import json
|
|
import os
|
|
import sys
|
|
|
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
|
TAI_PY = os.path.join(os.path.dirname(os.path.dirname(HERE)), "tai.py")
|
|
DATA = os.path.join(HERE, "misc_data")
|
|
|
|
|
|
def load_tai():
|
|
spec = importlib.util.spec_from_file_location("tai", TAI_PY)
|
|
mod = importlib.util.module_from_spec(spec)
|
|
sys.modules["tai"] = mod
|
|
spec.loader.exec_module(mod)
|
|
return mod
|
|
|
|
|
|
tai = load_tai()
|
|
|
|
|
|
def data(name):
|
|
with open(os.path.join(DATA, name), encoding="utf-8") as handle:
|
|
return json.load(handle)
|
|
|
|
|
|
def j(value):
|
|
return json.dumps(value)
|
|
|
|
|
|
def out(line=""):
|
|
sys.stdout.write(line + "\n")
|
|
|
|
|
|
def show(label, text):
|
|
out("%s: %s" % (label, j(text)))
|
|
|
|
|
|
# ------------------------------------------------------------------ scenarios without state
|
|
def sc_json(root):
|
|
for case in data("json_cases.json"):
|
|
try:
|
|
value = json.loads(case)
|
|
out("%s => %s" % (j(case), json.dumps(value)))
|
|
except ValueError:
|
|
out("%s => ERR" % j(case))
|
|
|
|
|
|
def sc_datetime(root):
|
|
for case in data("datetime_cases.json"):
|
|
try:
|
|
moment = tai._fromisoformat(case)
|
|
iso = moment.isoformat()
|
|
except Exception:
|
|
iso = "ERR"
|
|
try:
|
|
parsed = tai.parse_schedule_at(case)
|
|
except Exception as exc:
|
|
parsed = "ERR " + str(exc)
|
|
shown = "-"
|
|
if not parsed.startswith("ERR"):
|
|
try:
|
|
shown = j(tai.local_display(parsed))
|
|
except Exception as exc:
|
|
shown = "ERR " + str(exc)
|
|
out("%s => %s | %s | %s" % (j(case), iso, parsed, shown))
|
|
for case in data("duration_cases.json"):
|
|
out("duration %s => %s" % (j(case), j(tai.parse_duration(case))))
|
|
for case in data("delay_cases.json"):
|
|
out("delay %s => %s" % (j(case), j(tai.format_delay(case))))
|
|
|
|
|
|
def sc_chunk(root):
|
|
for case in data("chunk_cases.json"):
|
|
chunks = tai.chunk_lines(case["text"], case["max_lines"], case["overlap"], case["max_chars"])
|
|
out("chunks %d %s" % (len(chunks), j(chunks)))
|
|
out("collapse %s" % j(tai.collapse_repeated_lines(case["text"])))
|
|
out("lines %s" % j(case["text"].splitlines()))
|
|
out("strip %s" % j(case["text"].strip()))
|
|
|
|
|
|
def sc_fts(root):
|
|
for case in data("fts_cases.json"):
|
|
terms = tai.fts_escape(case)
|
|
out("%s => %s" % (j(case), j(terms)))
|
|
if terms:
|
|
out(" match %s" % j(tai.fts_match(terms)))
|
|
out(" or %s" % j(tai.fts_match_or(terms)))
|
|
out(" prefix %s" % j(tai.fts_match_prefix(terms)))
|
|
out(" variants %s" % j(tai.fts_match_variants(terms)))
|
|
out("rank %s" % j(tai.fts_rank("fts_docs")))
|
|
for raw in ["Release Notes!", " a b ", "---", "x" * 40, "été", "a,b;c", "Hello World 2", "..", "UPPER", "a-b-c"]:
|
|
out("tag %s => %s" % (j(raw), j(tai.normalize_tag(raw))))
|
|
out("tags %s" % j(tai.normalize_tags(["A b", "a-b", "", "C!", "d" * 40, "d" * 39 + "e"] + ["t%d" % i for i in range(30)])))
|
|
|
|
|
|
def sc_skills_data(root):
|
|
for name in sorted(tai.BUNDLED_SKILLS):
|
|
out("skill %s upstream %s" % (name, j(tai.BUNDLED_SKILL_UPSTREAM[name])))
|
|
for rel in sorted(tai.BUNDLED_SKILLS[name]):
|
|
text = tai.BUNDLED_SKILLS[name][rel]
|
|
out(" file %s len %d sha8 %s sha256 %s" % (rel, len(text.encode("utf-8")), tai.bundled_skill_digest(text), __import__("hashlib").sha256(text.encode("utf-8")).hexdigest()))
|
|
for name in sorted(tai.SKILL_BLUEPRINTS):
|
|
bp = tai.SKILL_BLUEPRINTS[name]
|
|
out("blueprint %s hint %s scope %s brief %s" % (name, j(bp["hint"]), bp["scope"], j(bp["brief"])))
|
|
out("catalog empty %s" % j(tai.skill_catalog({})))
|
|
out("catalog one %s" % j(tai.skill_catalog({"deep-research": {"description": "x" * 400}, "zeta": {"description": "short"}})))
|
|
out("prompt %s" % j(tai.create_skill_prompt("my-skill", "do the thing", "/tmp/x/my-skill")))
|
|
for raw in ["a", "ab", "a-b", "-a", "a-", "A", "a_b", "a" * 64, "a" * 65, "", "a b", "0x", "a\n"]:
|
|
out("name %s => %s" % (j(raw), bool(tai.SKILL_NAME_RE.match(raw))))
|
|
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 lines in front_cases:
|
|
out("front %s => %s" % (j(lines), j(tai.parse_skill_front(lines))))
|
|
|
|
|
|
# ------------------------------------------------------------------ helpers for filesystem scenarios
|
|
def write(path, text):
|
|
os.makedirs(os.path.dirname(path), exist_ok=True)
|
|
with open(path, "w", encoding="utf-8", newline="") as handle:
|
|
handle.write(text)
|
|
|
|
|
|
def read(path):
|
|
with open(path, encoding="utf-8", newline="") as handle:
|
|
return handle.read()
|
|
|
|
|
|
def tai_home():
|
|
return os.environ["TAI_HOME"]
|
|
|
|
|
|
def sc_skills_files(root):
|
|
home = tai_home()
|
|
out("== front-matter parse")
|
|
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 key, text in cases.items():
|
|
path = os.path.join(root, "parse", key, "SKILL.md")
|
|
write(path, text)
|
|
skill = tai.parse_skill_file(path)
|
|
out("parse %s => %s" % (key, j(skill)))
|
|
out("== discover with external roots and collisions")
|
|
proj = os.path.join(root, "proj")
|
|
write(os.path.join(os.path.expanduser("~"), ".claude", "skills", "ext-home", "SKILL.md"), "---\nname: ext-home\ndescription: from claude home\n---\nbody ext-home\n")
|
|
write(os.path.join(os.path.expanduser("~"), ".claude", "skills", "shared", "SKILL.md"), "---\nname: shared\ndescription: external copy\n---\nbody external\n")
|
|
write(os.path.join(proj, ".claude", "skills", "proj-claude", "SKILL.md"), "---\nname: proj-claude\ndescription: project claude\n---\nbody pc\n")
|
|
write(os.path.join(proj, ".agents", "skills", "proj-agents", "SKILL.md"), "---\nname: proj-agents\ndescription: project agents\n---\nbody pa\n")
|
|
write(os.path.join(home, "skills", "shared", "SKILL.md"), "---\nname: shared\ndescription: tai copy\n---\nbody tai\n")
|
|
write(os.path.join(home, "skills", "native", "SKILL.md"), "---\nname: native\ndescription: native home skill\n---\nbody native\n")
|
|
write(os.path.join(home, "skills", "native", "scripts", "run.sh"), "echo hi\n")
|
|
write(os.path.join(home, "skills", "native", "references", "ref.md"), "ref\n")
|
|
write(os.path.join(home, "skills", "native", "assets", "a.txt"), "a\n")
|
|
write(os.path.join(proj, ".tai", "skills", "proj-tai", "SKILL.md"), "---\nname: proj-tai\ndescription: project tai skill\n---\nbody ptai\n")
|
|
write(os.path.join(home, "skills", "broken", "SKILL.md"), "no front matter\n")
|
|
write(os.path.join(home, "skills", "stray-file.txt"), "x\n")
|
|
tai.save_skill_install_meta(os.path.join(home, "skills"), "native", {"source": "/some/path", "mode": "symlink", "scope": "home", "n": 3, "nested": {"a": [1, None, True]}})
|
|
skills = tai.discover_skills(home, proj)
|
|
for name in skills:
|
|
s = skills[name]
|
|
out("skill %s root=%s managed=%s install=%s note=%s extras=%s" % (name, s["root"], s["managed"], j(s["install"]), j(tai.skill_provenance_note(s)), j(tai.skill_extra_files(s["root"]))))
|
|
out(" desc=%s body=%s" % (j(s["description"]), j(s["body"])))
|
|
out("order %s" % j(list(skills)))
|
|
out("catalog %s" % j(tai.skill_catalog(skills)))
|
|
out("== scope helpers")
|
|
for scope in ("project", "home"):
|
|
out("scope_dir %s %s" % (scope, tai.skill_scope_dir(scope, home, proj, "nm")))
|
|
out("scope_dir %s %s" % (scope, tai.skill_scope_dir(scope, home, proj)))
|
|
empty = os.path.join(root, "emptyproj")
|
|
os.makedirs(empty, exist_ok=True)
|
|
out("default scope empty %s" % tai.default_skill_scope(empty))
|
|
for marker in (".git", ".tai", ".claude/skills", ".agents/skills"):
|
|
d = os.path.join(root, "mk-" + marker.replace("/", "_"))
|
|
os.makedirs(os.path.join(d, marker), exist_ok=True)
|
|
out("default scope %s %s" % (marker, tai.default_skill_scope(d)))
|
|
for path in ("/a/b/.claude/skills/x", "/a/b/.agents/skills/x/", "/a/b/other/x", "/a/.claude/skills/../x", "/a/.claude/skills//x"):
|
|
out("external %s %s" % (path, tai.is_external_skill_root(path)))
|
|
out("meta none %s" % j(tai.load_skill_install_meta(os.path.join(home, "skills"), "nothing")))
|
|
write(os.path.join(home, "skills", ".installs", "bad.json"), "{not json")
|
|
out("meta bad %s" % j(tai.load_skill_install_meta(os.path.join(home, "skills"), "bad")))
|
|
out("meta ok %s" % j(tai.load_skill_install_meta(os.path.join(home, "skills"), "native")))
|
|
|
|
|
|
def sc_skills_ops(root):
|
|
home = tai_home()
|
|
|
|
def tree():
|
|
found = []
|
|
base = os.path.join(home, "skills")
|
|
for dirpath, _dirs, files in os.walk(base):
|
|
for f in files:
|
|
found.append(os.path.relpath(os.path.join(dirpath, f), base))
|
|
return sorted(found)
|
|
|
|
def step(label, mode):
|
|
out("%s => %s" % (label, tai.op_skills(mode, home)))
|
|
out(" status: %s" % tai.status_lines(home)[-2])
|
|
out(" files: %d" % len(tree()))
|
|
|
|
out("-- fresh")
|
|
step("install", "install")
|
|
step("install again", "install")
|
|
step("upgrade clean", "upgrade")
|
|
out("pristine all %s" % j([tai.bundled_skill_pristine(home, n) for n in sorted(tai.BUNDLED_SKILLS)]))
|
|
taste = os.path.join(home, "skills", "taste", "SKILL.md")
|
|
write(taste, "user rewrite")
|
|
out("-- modified taste")
|
|
step("install", "install")
|
|
step("upgrade", "upgrade")
|
|
extra = os.path.join(home, "skills", "taste", "mynotes.md")
|
|
write(extra, "mine")
|
|
step("reinstall", "reinstall")
|
|
out("extra kept %s" % os.path.isfile(extra))
|
|
out("-- crlf edit counts as equal after universal newlines")
|
|
path = os.path.join(home, "skills", "plan", "SKILL.md")
|
|
write(path, read(path).replace("\n", "\r\n"))
|
|
step("upgrade crlf", "upgrade")
|
|
out("-- uninstall with user content")
|
|
gitp = os.path.join(home, "skills", "git", "SKILL.md")
|
|
write(gitp, "user rewrite git")
|
|
gextra = os.path.join(home, "skills", "git", "mynotes.md")
|
|
write(gextra, "mine")
|
|
write(os.path.join(home, "skills", "git", "sub", "deep.txt"), "deep")
|
|
step("uninstall", "uninstall")
|
|
step("uninstall again", "uninstall")
|
|
os.remove(gitp)
|
|
os.remove(gextra)
|
|
os.remove(os.path.join(home, "skills", "git", "sub", "deep.txt"))
|
|
step("uninstall final", "uninstall")
|
|
step("uninstall empty", "uninstall")
|
|
out("-- sidecar-only uninstall")
|
|
tai.save_skill_install_meta(os.path.join(home, "skills"), "taste", {"files": {}})
|
|
step("uninstall sidecar", "uninstall")
|
|
out("-- tracked-digest removal of an edited file")
|
|
step("install", "install")
|
|
meta = tai.load_skill_install_meta(os.path.join(home, "skills"), "taste")
|
|
out("meta %s" % j(sorted(meta)))
|
|
out("meta files %s" % j(meta["files"]))
|
|
out("meta fixed %s" % j({k: meta[k] for k in ("source", "mode", "scope", "upstream", "bundled_version")}))
|
|
os.chmod(os.path.join(home, "skills", "taste"), 0o500)
|
|
step("install readonly", "install")
|
|
os.chmod(os.path.join(home, "skills", "taste"), 0o700)
|
|
out("-- error path: skills dir is a file")
|
|
other = os.path.join(root, "home2")
|
|
os.makedirs(other, exist_ok=True)
|
|
write(os.path.join(other, "skills"), "file not dir")
|
|
out("blocked => %s" % tai.op_skills("install", other))
|
|
out("blocked uninstall => %s" % tai.op_skills("uninstall", other))
|
|
sc_skills_install_tool(root)
|
|
|
|
|
|
class SkillApp:
|
|
def __init__(self, home, cwd):
|
|
self.config = type("C", (), {"home": home})()
|
|
self.profile = "default"
|
|
self.skills = {}
|
|
self.store = None
|
|
self.depth = 0
|
|
self.runner_override = None
|
|
self.approvals = []
|
|
self.refreshed = 0
|
|
self.approve = True
|
|
|
|
def ask_approval(self, text):
|
|
self.approvals.append(text)
|
|
return self.approve
|
|
|
|
def apply_system(self):
|
|
self.refreshed += 1
|
|
|
|
|
|
def sc_skills_install_tool(root):
|
|
home = tai_home()
|
|
proj = os.path.join(root, "proj")
|
|
os.chdir(proj)
|
|
app = SkillApp(home, proj)
|
|
app.skills = tai.discover_skills(home, proj)
|
|
tools = tai.Tools(app)
|
|
out("== install_skill / load_skill / create_skill")
|
|
src = os.path.join(root, "srcskill")
|
|
write(os.path.join(src, "SKILL.md"), "---\nname: from-src\ndescription: installed from a dir\n---\nbody src\n")
|
|
write(os.path.join(src, "scripts", "x.sh"), "echo x\n")
|
|
write(os.path.join(src, "references", "r.md"), "r\n")
|
|
nested = os.path.join(root, "nested")
|
|
write(os.path.join(nested, "inner", "SKILL.md"), "---\nname: inner\ndescription: nested one\n---\nbody inner\n")
|
|
two = os.path.join(root, "two")
|
|
write(os.path.join(two, "a", "SKILL.md"), "---\nname: aa\ndescription: d\n---\nb\n")
|
|
write(os.path.join(two, "b", "SKILL.md"), "---\nname: bb\ndescription: d\n---\nb\n")
|
|
barefile = os.path.join(root, "bare.md")
|
|
write(barefile, "---\nname: bare-one\ndescription: bare md file\n---\nbody bare\n")
|
|
badfile = os.path.join(root, "bad.md")
|
|
write(badfile, "no front\n")
|
|
ext = os.path.join(proj, ".claude", "skills", "extskill")
|
|
write(os.path.join(ext, "SKILL.md"), "---\nname: extskill\ndescription: external one\n---\nbody ext\n")
|
|
notes = []
|
|
|
|
def call(label, args):
|
|
res = tools.dispatch("install_skill", json.dumps(args))
|
|
out("%s => %s" % (label, j(res)))
|
|
|
|
call("empty", {"source": ""})
|
|
call("bad scope", {"source": src, "scope": "mars"})
|
|
call("bad mode", {"source": src, "mode": "link"})
|
|
call("missing", {"source": os.path.join(root, "nope")})
|
|
call("dir home", {"source": src, "scope": "home"})
|
|
call("dir home again denied?", {"source": src, "scope": "home"})
|
|
app.approve = False
|
|
call("dir home denied", {"source": src, "scope": "home"})
|
|
app.approve = True
|
|
call("dir home force", {"source": src, "scope": "home", "force": True})
|
|
call("dir project", {"source": src, "scope": "project", "name": "renamed"})
|
|
call("bad name", {"source": src, "name": "Bad Name"})
|
|
call("nested", {"source": nested, "scope": "home"})
|
|
call("two children", {"source": two, "scope": "home"})
|
|
call("bare file", {"source": barefile, "scope": "home"})
|
|
call("bad file", {"source": badfile})
|
|
call("symlink ext default", {"source": ext, "scope": "home"})
|
|
call("symlink explicit", {"source": src, "scope": "home", "mode": "symlink", "name": "linked", "force": True})
|
|
call("symlink bare", {"source": barefile, "mode": "symlink"})
|
|
call("not a skill source", {"source": "/dev/null"})
|
|
out("approvals %s" % j(app.approvals))
|
|
out("refreshed %d" % app.refreshed)
|
|
out("skills %s" % j(sorted(app.skills)))
|
|
out("== load_skill")
|
|
app.skills = tai.discover_skills(home, proj)
|
|
for name in ("from-src", "deep-research", "extskill", "bot-creator", "nonexistent", ""):
|
|
if name == "bot-creator":
|
|
continue
|
|
out("load %s => %s" % (j(name), j(tools.dispatch("load_skill", json.dumps({"name": name})))))
|
|
out("== create_skill")
|
|
app.runner_override = lambda prompt, profile, timeout: "worker said: " + prompt[:40] + " t=%s" % timeout
|
|
for args in ({"name": "", "brief": "b"}, {"name": "ok-name", "brief": ""}, {"name": "ok-name", "brief": "b", "scope": "mars"},
|
|
{"name": "made", "brief": "make it", "scope": "home", "timeout": 10}, {"name": "made2", "brief": "make it", "timeout": "abc"},
|
|
{"name": "made3", "brief": "x", "timeout": 99999}):
|
|
out("create %s => %s" % (j(args), j(tools.dispatch("create_skill", json.dumps(args)))))
|
|
app.depth = 5
|
|
out("create depth => %s" % j(tools.dispatch("create_skill", json.dumps({"name": "dd", "brief": "b"}))))
|
|
app.depth = 0
|
|
|
|
def worker(prompt, profile, timeout):
|
|
marker = "Create a new agent skill named 'wrote' in "
|
|
path = prompt[len(marker):].split(".\n", 1)[0]
|
|
write(os.path.join(path, "SKILL.md"), "---\nname: wrote\ndescription: written by worker\n---\nbody wrote\n")
|
|
return "done writing " + path.replace(root, "<ROOT>")
|
|
|
|
app.runner_override = worker
|
|
out("create written => %s" % j(tools.dispatch("create_skill", json.dumps({"name": "wrote", "brief": "w", "scope": "home"})).replace(root, "<ROOT>")))
|
|
out("create replaced => %s" % j(tools.dispatch("create_skill", json.dumps({"name": "wrote", "brief": "w", "scope": "home"})).replace(root, "<ROOT>")))
|
|
out("refreshed %d" % app.refreshed)
|
|
|
|
|
|
# ------------------------------------------------------------------ install scenarios
|
|
class FakeBox:
|
|
"""Deterministic stand-in for the container probes (patched into tai.py; the C++ twin is FakeBox in the driver)."""
|
|
|
|
def __init__(self, engine, state, venv, ensure_error):
|
|
self.engine, self.state, self.venv, self.ensure_error = engine, state, venv, ensure_error
|
|
|
|
def install(self):
|
|
tai.container_engine = lambda: self.engine
|
|
tai.box_state = lambda engine: self.state
|
|
tai.box_venv_state = lambda engine: self.venv
|
|
|
|
def ensure():
|
|
if self.ensure_error:
|
|
raise tai.BackendError(self.ensure_error)
|
|
return self.engine
|
|
|
|
tai.ensure_box = ensure
|
|
|
|
|
|
def install_env(root, engine=None, state="running", venv="venv ready", ensure_error=""):
|
|
FakeBox(engine, state, venv, ensure_error).install()
|
|
|
|
|
|
def sc_install_hooks(root):
|
|
install_env(root)
|
|
home = tai_home()
|
|
rc = os.path.join(os.path.expanduser("~"), ".bashrc")
|
|
zrc = os.path.join(os.path.expanduser("~"), ".zshrc")
|
|
write(rc, "# my bashrc\nexport A=1")
|
|
write(zrc, "")
|
|
steps = [
|
|
("bash install", lambda: tai.op_hook("install", home)),
|
|
("bash install again", lambda: tai.op_hook("install", home)),
|
|
("bash upgrade", lambda: tai.op_hook("upgrade", home)),
|
|
("plexer install", lambda: tai.op_plexer_hook("install", home)),
|
|
("plexer again", lambda: tai.op_plexer_hook("install", home)),
|
|
("zsh install", lambda: tai.op_zsh_hook("install", home)),
|
|
("zsh again", lambda: tai.op_zsh_hook("install", home)),
|
|
("fish install", lambda: tai.op_fish_hook("install", home)),
|
|
("zsh plexer install", lambda: tai.op_zsh_plexer_hook("install", home)),
|
|
("fish plexer install", lambda: tai.op_fish_plexer_hook("install", home)),
|
|
("status", lambda: "\n".join(tai.status_lines(home))),
|
|
("bash reinstall", lambda: tai.op_hook("reinstall", home)),
|
|
("plexer reinstall", lambda: tai.op_plexer_hook("reinstall", home)),
|
|
("zsh uninstall", lambda: tai.op_zsh_hook("uninstall", home)),
|
|
("fish uninstall", lambda: tai.op_fish_hook("uninstall", home)),
|
|
("zsh uninstall again", lambda: tai.op_zsh_hook("uninstall", home)),
|
|
("bash uninstall", lambda: tai.op_hook("uninstall", home)),
|
|
("plexer uninstall", lambda: tai.op_plexer_hook("uninstall", home)),
|
|
("zsh plexer uninstall", lambda: tai.op_zsh_plexer_hook("uninstall", home)),
|
|
("fish plexer uninstall", lambda: tai.op_fish_plexer_hook("uninstall", home)),
|
|
("status after", lambda: "\n".join(tai.status_lines(home))),
|
|
]
|
|
for label, fn in steps:
|
|
out("%s => %s" % (label, j(fn())))
|
|
out("rc %s" % j(read(rc)))
|
|
out("zrc %s" % j(read(zrc)))
|
|
out("== stale and duplicated blocks")
|
|
write(rc, "head\n" + tai.BASHRC_MARK_BEGIN + "\nstale content\n" + tai.BASHRC_MARK_END + "\nmid\n" + tai.BASHRC_MARK_BEGIN + "\nsecond stale\n" + tai.BASHRC_MARK_END + "\ntail\n")
|
|
out("install over stale => %s" % j(tai.op_hook("install", home)))
|
|
out("rc %s" % j(read(rc)))
|
|
write(rc, "x\n" + tai.BASHRC_MARK_BEGIN + "\nunterminated\n")
|
|
out("install over unterminated => %s" % j(tai.op_hook("install", home)))
|
|
out("rc %s" % j(read(rc)))
|
|
out("uninstall unterminated => %s" % j(tai.op_hook("uninstall", home)))
|
|
out("rc %s" % j(read(rc)))
|
|
write(rc, "crlf\r\nline\r\n")
|
|
out("install crlf => %s" % j(tai.op_hook("install", home)))
|
|
out("rc %s" % j(read(rc)))
|
|
os.chmod(rc, 0o400)
|
|
out("readonly install => %s" % j(tai.op_plexer_hook("install", home)))
|
|
os.chmod(rc, 0o600)
|
|
blocks = [tai.hook_present, tai.zsh_hook_present, tai.fish_hook_present, tai.plexer_hook_present, tai.zsh_plexer_hook_present, tai.fish_plexer_hook_present]
|
|
out("present %s" % j([fn(rc) for fn in blocks]))
|
|
out("present missing %s" % j([fn(os.path.join(root, "nope")) for fn in blocks]))
|
|
for shell in ("bash", "zsh", "fish", "other"):
|
|
out("rc path %s %s" % (shell, tai.shell_rc_path(shell)))
|
|
os.environ["XDG_CONFIG_HOME"] = os.path.join(root, "xdg")
|
|
out("rc path fish xdg %s" % tai.shell_rc_path("fish"))
|
|
del os.environ["XDG_CONFIG_HOME"]
|
|
out("shell_present %s" % j([tai.shell_present(n) for n in ("zsh", "fish", "bash", "nosuchshell")]))
|
|
|
|
|
|
def sc_install_ops(root):
|
|
install_env(root, engine="podman", state="running", venv="venv ready")
|
|
home = tai_home()
|
|
out("-- binary")
|
|
for mode in ("install", "install", "upgrade", "reinstall", "uninstall", "uninstall", "install"):
|
|
out("binary %s => %s" % (mode, j(tai.op_binary(mode, home))))
|
|
target = os.path.join(os.path.expanduser("~"), ".local", "bin", "tai.py")
|
|
out("binary mode %s size_equal %s" % (oct(os.stat(target).st_mode & 0o777), os.path.getsize(target) == os.path.getsize(tai.__file__)))
|
|
write(target, "VERSION = \"0.0.1\"\n")
|
|
out("binary refresh => %s" % j(tai.op_binary("install", home)))
|
|
out("binary refresh upgrade => %s" % j(tai.op_binary("upgrade", home)))
|
|
out("script_version %s %s %s" % (j(tai.script_version(target)), j(tai.script_version(os.path.join(root, "nope"))), j(tai.script_version(tai.__file__))))
|
|
write(os.path.join(root, "v1"), "x\nVERSION = \"1.2.3\"\ny\n")
|
|
write(os.path.join(root, "v2"), "VERSION = \"1.2\"\n VERSION = \"1.2.3\"\nVERSION = \"a.b.c\"\n")
|
|
write(os.path.join(root, "v3"), "VERSION = \"4.5.6\"")
|
|
write(os.path.join(root, "v4"), "VERSION = \"4.5.6\" \n")
|
|
out("script_version v1..v4 %s" % j([tai.script_version(os.path.join(root, n)) for n in ("v1", "v2", "v3", "v4")]))
|
|
out("-- venv")
|
|
for mode in ("install", "install", "reinstall", "uninstall", "uninstall", "install"):
|
|
out("venv %s => %s" % (mode, j(tai.op_venv(mode, home).replace(root, "<ROOT>"))))
|
|
out("venv helpers %s %s %s" % (j(tai.venv_dir(home).replace(root, "<ROOT>")), j((tai.venv_python(home) or "").replace(root, "<ROOT>")), tai.venv_available()))
|
|
out("-- services (no systemctl)")
|
|
for mode in ("install", "install", "reinstall", "upgrade", "uninstall", "uninstall"):
|
|
out("scheduler %s => %s" % (mode, j(tai.op_scheduler_service(mode, home).replace(root, "<ROOT>"))))
|
|
out("telegram without token => %s" % j(tai.op_telegram_service("install", home)))
|
|
out("service_exec %s" % j(tai.service_exec(home, "/x/tai.py", "--a", "b").replace(root, "<ROOT>")))
|
|
out("unit_dir %s" % j(tai.unit_dir(home).replace(root, "<ROOT>")))
|
|
out("service_state %s" % j(tai.service_state("tai-scheduler.service")))
|
|
out("activate %s" % j(tai.activate_service("tai-scheduler.service")))
|
|
out("remove %s" % j(tai.remove_service_unit(home, "tai-scheduler.service")))
|
|
out("-- container")
|
|
for mode in ("install", "upgrade", "reinstall", "uninstall"):
|
|
out("container %s => %s" % (mode, j(tai.op_container(mode, home))))
|
|
install_env(root, engine="podman", state="stopped", venv="no venv (reinstall container to rebuild)")
|
|
for mode in ("install", "reinstall"):
|
|
out("container stopped %s => %s" % (mode, j(tai.op_container(mode, home))))
|
|
install_env(root, engine="podman", state="running", venv="venv ready", ensure_error="engine exploded")
|
|
out("container failing => %s" % j(tai.op_container("install", home)))
|
|
install_env(root, engine=None)
|
|
for mode in ("install", "uninstall"):
|
|
out("container noengine %s => %s" % (mode, j(tai.op_container(mode, home))))
|
|
install_env(root, engine="podman", state="running", venv="venv ready")
|
|
out("-- install_report")
|
|
for action, targets in (("install", ["skills", "bash-hook", "venv"]), ("status", ["skills"]), ("upgrade", list(tai.INSTALL_TARGETS)), ("uninstall", ["skills", "bash-hook", "plexer-hook", "binary"])):
|
|
out("report %s %s =>\n%s" % (action, targets, tai.install_report(action, targets, home).replace(root, "<ROOT>")))
|
|
out("-- install_self")
|
|
for shell in ("/bin/bash", "/usr/bin/zsh", "/usr/bin/fish", "", "/bin/sh"):
|
|
os.environ["SHELL"] = shell
|
|
out("shell %s" % j(shell))
|
|
sys.stdout.flush()
|
|
tai.install_self()
|
|
out("-- install tool")
|
|
app = SkillApp(home, root)
|
|
app.config.home = home
|
|
tools = tai.Tools(app)
|
|
for args in ({"action": "bogus"}, {"action": "status"}, {"action": "STATUS ", "targets": ["skills"]}, {"action": "install", "targets": ["nope"]}, {"action": "install", "targets": []},
|
|
{"action": "install", "targets": "skills"}, {"action": "install", "targets": ["skills"]}, {"action": "uninstall", "targets": ["skills", "bash-hook"]}, {}):
|
|
app.skills = {}
|
|
res = tools.dispatch("install", json.dumps(args)).replace(root, "<ROOT>")
|
|
out("tool %s => %s | skills=%s" % (j(args), j(res), j(sorted(app.skills))))
|
|
app.approve = False
|
|
out("tool denied => %s" % j(tools.dispatch("install", json.dumps({"action": "install", "targets": ["skills"]}))))
|
|
out("approvals %s" % j(app.approvals))
|
|
|
|
|
|
def sc_install_noengine(root):
|
|
install_env(root, engine=None)
|
|
home = tai_home()
|
|
out("%s" % j(tai.status_lines(home)[-3:]))
|
|
for mode in ("install", "uninstall"):
|
|
out("container %s => %s" % (mode, j(tai.op_container(mode, home))))
|
|
|
|
|
|
def sc_vault_line(root):
|
|
home = tai_home()
|
|
out("empty %s" % j(tai.vault_line(home)))
|
|
write(os.path.join(home, "memory.db"), "")
|
|
out("kept %s" % j(tai.vault_line(home).replace(root, "<ROOT>")))
|
|
|
|
|
|
# ------------------------------------------------------------------ scheduler scenarios
|
|
def open_store(profile="t"):
|
|
class FakeArgs:
|
|
yes = True
|
|
|
|
FakeArgs.profile = profile
|
|
config = tai.Config(FakeArgs)
|
|
seal = tai.Seal(config.home, "")
|
|
store = tai.Store(config, seal)
|
|
return config, seal, store
|
|
|
|
|
|
NOW_FIXED = "2026-10-25T12:00:00+00:00"
|
|
LONG_EVERY = 315360000 # ten years: recurring claims land far in the future so reruns are stable
|
|
|
|
|
|
def prep_sched(root):
|
|
config, seal, store = open_store()
|
|
past = "2026-01-01T00:00:00+00:00"
|
|
store.add_schedule("once-due", "do:once", "t", 0, past, 300)
|
|
store.add_schedule("rec-due", "do:rec", "t", LONG_EVERY, past, 600, ["alpha", "beta"])
|
|
store.add_schedule("future", "do:future", "t", 0, "2099-01-01T00:00:00+00:00", 600, [])
|
|
store.add_schedule("leased", "do:leased", "t", LONG_EVERY, "2026-02-01T00:00:00+00:00", 600)
|
|
store.add_schedule("stale-lease", "do:stale", "t", LONG_EVERY, "2026-03-01T00:00:00+00:00", 600)
|
|
store.add_schedule("exploder", "boom: fail", "t", 0, "2026-04-01T00:00:00+00:00", 300)
|
|
store.add_schedule("other-profile", "do:other", "other", 0, "2026-06-01T00:00:00+00:00", 300)
|
|
store.add_schedule("bad-time", "do:badtime", "t", LONG_EVERY, "!!bad", 300)
|
|
store.add_schedule("", "x" * 100, "t", 0, "2099-06-01T00:00:00+00:00", 300, ["one"])
|
|
store.add_schedule("with-result", "do:result", "t", 3600, "2099-07-01T00:00:00+00:00", 300)
|
|
store.db.execute("UPDATE schedules SET last_status = 'running', updated = ? WHERE name = 'leased'", (tai.datetime.now(tai.timezone.utc).isoformat(),))
|
|
store.db.execute("UPDATE schedules SET last_status = 'running', updated = '2000-01-01T00:00:00+00:00' WHERE name = 'stale-lease'")
|
|
store.db.execute("UPDATE schedules SET last_status = 'done', last_result = ? WHERE name = 'with-result'", ("r" * 200,))
|
|
store.db.commit()
|
|
store.close()
|
|
|
|
|
|
def sched_rows(store, label):
|
|
rows = store.db.execute("SELECT id, name, prompt, profile, every_sec, next_run, timeout, status, last_status, last_result, updated FROM schedules ORDER BY id").fetchall()
|
|
out("-- %s" % label)
|
|
for row in rows:
|
|
out("row %s" % j(list(row)))
|
|
tags = store.db.execute("SELECT item, tag, profile FROM tags WHERE item LIKE 'sched:%' ORDER BY item, tag").fetchall()
|
|
out("tags %s" % j([list(t) for t in tags]))
|
|
|
|
|
|
class Gate:
|
|
"""Blocks 'slow:' tasks until released, like a long-running worker."""
|
|
|
|
def __init__(self):
|
|
self.event = tai.threading.Event()
|
|
|
|
def runner(self, task, profile, timeout):
|
|
if task.startswith("slow:"):
|
|
self.event.wait(30)
|
|
return "slow done " + task
|
|
if "boom" in task:
|
|
raise RuntimeError("boom")
|
|
return "ok:" + task
|
|
|
|
|
|
def quiesce():
|
|
for _ in range(200):
|
|
if tai.threading.active_count() <= 1:
|
|
return
|
|
tai.time.sleep(0.05)
|
|
|
|
|
|
def sc_sched(root):
|
|
config, seal, store = open_store()
|
|
gate = Gate()
|
|
out("== list and format")
|
|
items = store.list_schedules()
|
|
for it in items:
|
|
out("item %s" % j(it))
|
|
out("format:\n%s" % tai.format_schedule_lines(items, tai._fromisoformat(NOW_FIXED)))
|
|
out("format empty: %s" % tai.format_schedule_lines([], tai._fromisoformat(NOW_FIXED)))
|
|
out("other profile: %s" % j(store.list_schedules("other")))
|
|
out("== running leases")
|
|
for status, updated, timeout in (("running", tai.datetime.now(tai.timezone.utc).isoformat(), 600), ("running", "2000-01-01T00:00:00+00:00", 600), ("done", "", 0), ("running", "garbage", 10),
|
|
("running", tai.datetime.now(tai.timezone.utc).isoformat(), 0)):
|
|
out("running %s %s %s => %s" % (status, "now" if updated.startswith("2026") else updated, timeout, tai.schedule_running(status, updated, timeout)))
|
|
out("== sealed rows (added now: Store refuses to open with them present and no passphrase)")
|
|
store.add_schedule("sealed", "tai1$AAAA$BBBB$CCCC", "t", 0, "2026-05-01T00:00:00+00:00", 300)
|
|
store.add_schedule("sealed-corrupt", "tai1$bad", "t", 0, "2026-05-02T00:00:00+00:00", 300)
|
|
out("== tick 1")
|
|
sys.stdout.flush()
|
|
fired = tai.scheduler_tick(config, seal, gate.runner)
|
|
sys.stdout.flush()
|
|
out("fired %d" % fired)
|
|
quiesce()
|
|
sched_rows(store, "after tick 1")
|
|
out("== tick 2")
|
|
out("fired %d" % tai.scheduler_tick(config, seal, gate.runner))
|
|
quiesce()
|
|
out("== cap phase")
|
|
for k in range(9):
|
|
store.add_schedule("slow%d" % k, "slow:%d" % k, "t", LONG_EVERY, "2025-01-0%dT00:00:00+00:00" % (k + 1), 600)
|
|
out("fired %d" % tai.scheduler_tick(config, seal, gate.runner))
|
|
out("fired %d" % tai.scheduler_tick(config, seal, gate.runner))
|
|
out("running agents %d" % tai.running_agents())
|
|
out("fired %d" % tai.scheduler_tick(config, seal, gate.runner))
|
|
sys.stdout.flush()
|
|
sched_rows(store, "after cap ticks")
|
|
out("== AgentsFull restore branch")
|
|
store.add_schedule("full-once", "slow:once", "t", 0, "2025-02-01T00:00:00+00:00", 600)
|
|
real = tai.running_agents
|
|
tai.running_agents = lambda: 0
|
|
out("fired %d" % tai.scheduler_tick(config, seal, gate.runner))
|
|
tai.running_agents = real
|
|
sched_rows(store, "after AgentsFull")
|
|
gate.event.set()
|
|
quiesce()
|
|
sched_rows(store, "after release")
|
|
out("== schedule tool")
|
|
app = SkillApp(tai_home(), root)
|
|
app.store = store
|
|
app.profile = "t"
|
|
tools = tai.Tools(app)
|
|
calls = [
|
|
{"prompt": ""}, {"prompt": "p"}, {"prompt": "p", "at": "2099-01-01T10:00", "every": 120}, {"prompt": "p", "at": "nonsense"}, {"prompt": "p", "every": "abc"},
|
|
{"prompt": "p", "every": 30}, {"prompt": "tool one", "at": "2099-01-01T10:00", "name": "N" * 100, "timeout": 5, "tags": ["Alpha", "beta!", "", "x" * 40]},
|
|
{"prompt": "tool two", "every": 3600, "timeout": "abc", "profile": "t"}, {"prompt": "tool three", "every": "7200", "timeout": 99999},
|
|
{"prompt": "p", "every": 60, "profile": "other"}, {"prompt": "p", "every": 60, "profile": "bad profile!"}, {"prompt": "p", "every": 60, "tags": "notalist"},
|
|
{"prompt": "p", "every": 60, "tags": [1, 2]}, {"prompt": "tool four", "every": 86400, "tags": []}, {"prompt": " padded ", "at": "2099-02-02T02:02:02Z", "timeout": 0},
|
|
{"prompt": "p", "at": "9999-12-31T24:00"}, {"prompt": "p", "at": "0001-01-01T00:00"}, {"prompt": "p", "every": 60.5}, {"prompt": "p", "every": True},
|
|
]
|
|
for args in calls:
|
|
out("schedule %s => %s" % (j(args), j(tools.dispatch("schedule", json.dumps(args)))))
|
|
app.approve = False
|
|
out("schedule denied => %s" % j(tools.dispatch("schedule", json.dumps({"prompt": "denied", "every": 60}))))
|
|
app.approve = True
|
|
out("schedules => %s" % j(tools.dispatch("schedules", "{}")))
|
|
for args in ({"id": 999}, {"id": "abc"}, {"id": 0}, {}, {"id": "13"}, {"id": 14.0}):
|
|
out("unschedule %s => %s" % (j(args), j(tools.dispatch("unschedule", json.dumps(args)))))
|
|
app.approve = False
|
|
out("unschedule denied => %s" % j(tools.dispatch("unschedule", json.dumps({"id": 15}))))
|
|
app.approve = True
|
|
out("approvals %s" % j(app.approvals))
|
|
sched_rows(store, "final")
|
|
out("remove nonexistent %s" % store.remove_schedule(99999))
|
|
store.close()
|
|
|
|
|
|
# ------------------------------------------------------------------ rag scenarios
|
|
WORDS = ("alpha bravo charlie delta echo foxtrot golf hotel india juliet kilo lima mike november oscar papa quebec romeo sierra tango uniform victor whiskey xray yankee zulu "
|
|
"database retrieval indexing scheduler terminal pipeline compiler optimizer allocator tokenizer scanner buffer kernel module package release rollback deployment "
|
|
"café naïve résumé über 中文 snake_case kebab-case CamelCase ALLCAPS numbers123 v2beta").split()
|
|
|
|
|
|
def prep_rag(root):
|
|
import random
|
|
rng = random.Random(7)
|
|
config, seal, store = open_store()
|
|
ids = []
|
|
for index in range(30):
|
|
title = " ".join(rng.choice(WORDS) for _ in range(rng.randint(1, 4)))
|
|
lines = []
|
|
for _ in range(rng.randint(1, 12)):
|
|
lines.append(" ".join(rng.choice(WORDS) for _ in range(rng.randint(3, 14))))
|
|
content = "\n".join(lines)
|
|
kind = rng.choice(["note", "doc", "termlog"])
|
|
rid = store.add_record(kind, title, content, [rng.choice(["x", "y", "zed"])] if index % 3 == 0 else [], "t")
|
|
store.add_chunks("record", rid, tai.chunk_lines(content, 3, 1, 200), "t")
|
|
ids.append(rid)
|
|
store.add_record("note", "unique zebra", "the quick zebra jumps over the lazy dog " * 30, [], "t")
|
|
store.add_record("note", "other profile secret", "alpha bravo hidden", [], "other")
|
|
store.add_edge(ids[0], ids[1], "relates to", "t")
|
|
store.add_edge(ids[2], ids[0], "see also", "t")
|
|
store.add_edge(ids[0], ids[3], "x", "t")
|
|
store.add_edge(ids[0], ids[4], "y", "t")
|
|
store.add_edge(ids[0], ids[5], "z", "t")
|
|
for index in range(6):
|
|
store.audit_event("main", "write", "/tmp/file%d.txt" % index, "wrote alpha file %d" % index, None, "content %d" % index, ["t%d" % index], None, None, "t")
|
|
store.log_event("t", "user", "message", "hello alpha retrieval message")
|
|
store.log_event("t", "assistant", "message", "bravo reply about indexing")
|
|
# implicit pairs: search events followed by reads
|
|
store.log_event("t", "tool", "search-query", "q=alpha\tlimit=5\thits=record:%s,record:%s,chunk:3" % (ids[0], ids[1]))
|
|
store.log_event("t", "tool", "record-read", ids[1])
|
|
store.log_event("t", "tool", "search-query", "q=nothing here\tlimit=5\thits=")
|
|
store.log_event("t", "tool", "search-query", "q=bravo\tlimit=5\thits=%s,record:%s" % (ids[2], ids[3]))
|
|
store.log_event("t", "tool", "record-read", " %s " % ids[9])
|
|
store.log_event("t", "tool", "record-read", ids[2])
|
|
store.log_event("t", "tool", "search-query", "no equals sign")
|
|
store.log_event("t", "tool", "search-query", "q=\thits=x")
|
|
store.log_event("t", "tool", "search-query", "q=a=b\tq=delta\thits=,,record:%s," % ids[6])
|
|
store.log_event("t", "tool", "record-read", "")
|
|
store.close()
|
|
|
|
|
|
def hit_json(h):
|
|
return {"item": h["item"], "kind": h["kind"], "sub": h["sub"], "title": h["title"], "snippet": h["snippet"], "rank": h["rank"]}
|
|
|
|
|
|
def sc_rag(root):
|
|
config, seal, store = open_store()
|
|
out("== fts_search")
|
|
queries = ["alpha", "alpha bravo", "zebra", "café", "résumé naïve", "snake_case", "numbers123", "nothingmatchesthis", "", "!!!", "retrieval index", "wh", "ALPHA", "x" * 50, "unique zebra jumps", "message",
|
|
"file", "ro", "v2beta kernel"]
|
|
for q in queries:
|
|
for kinds in ((), ("record",), ("chunk",), ("audit",), ("event",), ("record", "chunk")):
|
|
for limit in (10, 3):
|
|
hits = store.fts_search(q, kinds, "t", limit)
|
|
out("search %s %s %d => %s" % (j(q), j(list(kinds)), limit, j([hit_json(h) for h in hits])))
|
|
out("other profile %s" % j([hit_json(h) for h in store.fts_search("alpha", ("record",), "other", 10)]))
|
|
out("limits %s" % j([len(store.fts_search("alpha", (), "t", n)) for n in (0, -5, 1, 100, 500)]))
|
|
out("== eval")
|
|
out("tokens %s" % j(tai.RetrievalEval.tokens("The quick Brown fox jumped_over 123456 abcdefg ABCDEFG cafés résumés")))
|
|
out("tokens4 %s" % j(tai.RetrievalEval.tokens("The quick Brown fox", 4)))
|
|
goldens = tai.RetrievalEval.goldens_from_vault(store, "t")
|
|
out("goldens %d" % len(goldens))
|
|
for g in goldens:
|
|
out("golden %s" % j(g))
|
|
result = tai.RetrievalEval.score(store, goldens, "t")
|
|
out("score %s" % j(result))
|
|
pairs = tai.RetrievalEval.implicit_pairs(store, "t")
|
|
out("pairs %s" % j(pairs))
|
|
out("report:\n%s" % tai.RetrievalEval.format_report(result, pairs))
|
|
out("empty report:\n%s" % tai.RetrievalEval.format_report(tai.RetrievalEval.score(store, [], "t"), tai.RetrievalEval.implicit_pairs(store, "nobody")))
|
|
out("small k %s" % j(tai.RetrievalEval.score(store, goldens[:12], "t", ("record",), 1, 2)))
|
|
out("rank_of %s %s %s" % (j(tai.RetrievalEval.rank_of("b", ["a", "b"])), j(tai.RetrievalEval.rank_of("z", ["a"])), j(tai.RetrievalEval.best_rank(["z", "a"], ["q", "a", "z"]))))
|
|
out("metrics %s" % j([tai.RetrievalEval.hit_at_k(3), tai.RetrievalEval.hit_at_k(6), tai.RetrievalEval.hit_at_k(None), tai.RetrievalEval.reciprocal_rank(4), tai.RetrievalEval.ndcg_at_k(3), tai.RetrievalEval.ndcg_at_k(9),
|
|
tai.RetrievalEval.context_precision(["a", "c"], ["a", "b", "c", "d"]), tai.RetrievalEval.context_precision([], ["a"]), tai.RetrievalEval.context_precision(["x"], ["a"])]))
|
|
for text in ("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"):
|
|
out("parse %s => %s" % (j(text), j(tai.RetrievalEval._parse_search_event(text))))
|
|
out("== chunks table")
|
|
rid = store.db.execute("SELECT id FROM records WHERE profile = 't' ORDER BY id LIMIT 1").fetchone()[0]
|
|
store.add_chunks("record", rid, ["one", "two", "three"], "t")
|
|
rows = store.db.execute("SELECT id, parent_kind, parent_id, idx, text, profile FROM chunks WHERE parent_id = ? ORDER BY idx", (rid,)).fetchall()
|
|
out("chunks %s" % j([list(r) for r in rows]))
|
|
ids = [r[0] for r in rows]
|
|
got = store.get_chunks_by_ids(ids + [999999], "t")
|
|
out("by ids %s" % j({str(k): v for k, v in sorted(got.items())}))
|
|
out("by ids empty %s other %s" % (j(store.get_chunks_by_ids([], "t")), j(store.get_chunks_by_ids(ids, "other"))))
|
|
store.add_chunks("record", rid, [], "t")
|
|
out("after clear %d" % store.db.execute("SELECT COUNT(*) FROM chunks WHERE parent_id = ?", (rid,)).fetchone()[0])
|
|
out("== search tool")
|
|
app = SkillApp(tai_home(), root)
|
|
app.store = store
|
|
app.profile = "t"
|
|
tools = tai.Tools(app)
|
|
calls = [{"query": ""}, {"query": "alpha"}, {"query": "alpha", "limit": 3}, {"query": "alpha", "limit": "2"}, {"query": "alpha", "limit": "x"}, {"query": "alpha", "limit": 0}, {"query": "alpha", "limit": -2},
|
|
{"query": "retrieval", "kinds": ["chunk"]}, {"query": "alpha", "kinds": ["record"], "expand": True, "limit": 20}, {"query": "alpha", "kinds": ["bogus"]}, {"query": "alpha", "kinds": []},
|
|
{"query": "alpha", "kinds": "record"}, {"query": "file", "kinds": ["audit"]}, {"query": "message", "kinds": ["event"]}, {"query": "zebra", "kinds": ["record", "chunk", "audit"]},
|
|
{"query": "nothingmatchesthis"}, {"query": "x" * 200}, {"query": " spaced "}, {"query": "café résumé", "limit": 50}]
|
|
for args in calls:
|
|
out("search %s => %s" % (j(args), j(tools.dispatch("search", json.dumps(args)))))
|
|
out("logged %s" % j([list(r) for r in store.db.execute("SELECT profile, role, kind, text FROM events WHERE kind = 'search-query' ORDER BY id DESC LIMIT 25").fetchall()]))
|
|
store.close()
|
|
|
|
|
|
# ------------------------------------------------------------------ telegram scenarios
|
|
class CtxDict(dict):
|
|
def __setitem__(self, key, value):
|
|
out("CTX set %s=%s" % (key, value))
|
|
dict.__setitem__(self, key, value)
|
|
|
|
def pop(self, key, *default):
|
|
out("CTX pop %s" % key)
|
|
return dict.pop(self, key, *default)
|
|
|
|
|
|
class LogStub:
|
|
enabled = False
|
|
|
|
def __init__(self):
|
|
self.context = CtxDict()
|
|
|
|
def event(self, level, component, event, message, profile="", bot="", chat="", meta=None):
|
|
out("LOG %s" % j([level, component, event, message, profile, bot, chat, meta]))
|
|
|
|
|
|
class TgAgent:
|
|
profile = "t"
|
|
bot = "main"
|
|
|
|
def __init__(self, home):
|
|
self.config = type("C", (), {"home": home})()
|
|
self.store = type("S", (), {"seal": None})()
|
|
|
|
def reset_history(self):
|
|
out("AGENT reset_history")
|
|
|
|
def run_turn(self, text, capture=False):
|
|
out("AGENT run_turn %s capture=%s" % (j(text), capture))
|
|
if "explode" in text:
|
|
raise RuntimeError("agent exploded")
|
|
if text == "long":
|
|
return "L" * 9000
|
|
if text == "empty reply":
|
|
return ""
|
|
return "echo:" + text
|
|
|
|
|
|
class StopLoop(Exception):
|
|
pass
|
|
|
|
|
|
def tg_setup(root):
|
|
tai.TELEGRAM_API = os.environ["MISC_TG_API"]
|
|
tai.TELEGRAM_FILE_API = os.environ["MISC_TG_FILE"]
|
|
tai.LOG = LogStub()
|
|
|
|
def ensure():
|
|
out("BOX ensure_box")
|
|
if os.environ.get("MISC_BOX_FAIL"):
|
|
raise tai.BackendError("box down")
|
|
return "eng"
|
|
|
|
def transcribe(engine, audio):
|
|
out("BOX transcribe %s %s" % (engine, j(audio.decode("latin-1"))))
|
|
if audio == b"AUDIO":
|
|
return "voice says hi"
|
|
return "error: model failed"
|
|
|
|
tai.ensure_box = ensure
|
|
tai.box_transcribe = transcribe
|
|
tai.start_scheduler = lambda config, seal: out("start_scheduler called")
|
|
|
|
|
|
def sc_telegram(root):
|
|
tg_setup(root)
|
|
home = tai_home()
|
|
out("== token")
|
|
env_path = tai.telegram_env_path(home)
|
|
out("env path %s" % j(env_path.replace(root, "<ROOT>")))
|
|
os.environ.pop("TELEGRAM_BOT_TOKEN", None)
|
|
out("no file => %s" % j(tai.load_telegram_token(home)))
|
|
for label, body in (("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")):
|
|
write(env_path, body)
|
|
out("file %s => %s" % (label, j(tai.load_telegram_token(home))))
|
|
os.environ["TELEGRAM_BOT_TOKEN"] = "fromenv"
|
|
out("env wins => %s" % j(tai.load_telegram_token(home)))
|
|
os.environ["TELEGRAM_BOT_TOKEN"] = ""
|
|
out("empty env falls back => %s" % j(tai.load_telegram_token(home)))
|
|
os.environ.pop("TELEGRAM_BOT_TOKEN", None)
|
|
os.remove(env_path)
|
|
out("== call / send")
|
|
good = tai.Telegram("good")
|
|
out("getMe %s" % j(good.call("getMe", {})))
|
|
for label, token, method in (("bad token", "bad", "getMe"), ("not json", "nojson", "getMe")):
|
|
try:
|
|
res = tai.Telegram(token).call(method, {})
|
|
out("%s => %s" % (label, j(res)))
|
|
except (OSError, ValueError) as exc:
|
|
out("%s => raised %s" % (label, j(tai.short_error(exc))))
|
|
for text in ("", "short", "S" * 4000, "S" * 4001, "S" * 8000, "S" * 8001, "é" * 4001, "\U0001f600" * 4100, "line\n" * 2000):
|
|
tai.telegram_send(good, 42, text)
|
|
tai.telegram_send(good, "chat-as-string", "hi")
|
|
out("== updates")
|
|
agent = TgAgent(home)
|
|
updates = [
|
|
{"update_id": 1, "message": {"chat": {"id": 111}, "text": "/start"}},
|
|
{"update_id": 2, "message": {"chat": {"id": 111}, "text": "/new"}},
|
|
{"update_id": 3, "message": {"chat": {"id": 111}, "text": "hello there"}},
|
|
{"update_id": 4, "message": {"chat": {"id": 111}, "text": ""}},
|
|
{"update_id": 5, "message": {"chat": {"id": 111}, "text": " "}},
|
|
{"update_id": 6, "message": {"chat": {"id": 0}, "text": "zero chat"}},
|
|
{"update_id": 7, "message": {"text": "no chat"}},
|
|
{"update_id": 8},
|
|
{"update_id": 9, "message": {"chat": {"id": 111}}},
|
|
{"update_id": 10, "message": {"chat": {"id": 111}, "text": "explode please"}},
|
|
{"update_id": 11, "message": {"chat": {"id": 111}, "text": "long"}},
|
|
{"update_id": 12, "message": {"chat": {"id": 111}, "text": "empty reply"}},
|
|
{"update_id": 13, "message": {"chat": {"id": 222}, "voice": {"file_id": "f1"}}},
|
|
{"update_id": 14, "message": {"chat": {"id": 222}, "audio": {"file_id": "f1"}}},
|
|
{"update_id": 15, "message": {"chat": {"id": 222}, "voice": {"file_id": "nofile"}}},
|
|
{"update_id": 16, "message": {"chat": {"id": 222}, "voice": {"file_id": "fbad"}}},
|
|
{"update_id": 17, "message": {"chat": {"id": 222}, "voice": {"file_id": "fmodel"}}},
|
|
{"update_id": 18, "message": {"chat": {"id": 222}, "voice": {}, "text": "text with empty voice"}},
|
|
{"update_id": 19, "message": {"chat": {"id": 222}, "voice": {"file_id": "fmissingfile"}}},
|
|
{"update_id": 20, "message": {"chat": {"id": "str-chat"}, "text": "string chat id"}},
|
|
{"update_id": 21, "message": {"chat": {"id": 111}, "text": "unicode café \U0001f600"}},
|
|
]
|
|
for update in updates:
|
|
out("-- update %s" % j(update))
|
|
try:
|
|
tai.handle_telegram_update(agent, good, update)
|
|
except Exception as exc:
|
|
out("raised %s" % j(tai.short_error(exc)))
|
|
os.environ["MISC_BOX_FAIL"] = "1"
|
|
out("-- box failing")
|
|
try:
|
|
tai.handle_telegram_update(agent, good, {"update_id": 30, "message": {"chat": {"id": 5}, "voice": {"file_id": "f1"}}})
|
|
except Exception as exc:
|
|
out("raised %s" % j(tai.short_error(exc)))
|
|
os.environ.pop("MISC_BOX_FAIL", None)
|
|
out("== run_telegram_bot")
|
|
for label, token in (("missing", None), ("bad auth", "bad"), ("not json", "nojson"), ("no username", "noname"), ("good", "good")):
|
|
os.environ.pop("TELEGRAM_BOT_TOKEN", None)
|
|
if token:
|
|
os.environ["TELEGRAM_BOT_TOKEN"] = token
|
|
out("-- run %s" % label)
|
|
sys.stdout.flush()
|
|
|
|
def stop_sleep(seconds):
|
|
out("sleep %s" % seconds)
|
|
raise StopLoop()
|
|
|
|
tai.time.sleep = stop_sleep
|
|
try:
|
|
res = tai.run_telegram_bot(agent)
|
|
out("returned %s" % res)
|
|
except StopLoop:
|
|
out("stopped by sleep")
|
|
sys.stdout.flush()
|
|
sys.stderr.flush()
|
|
|
|
|
|
def sc_telegram_install(root):
|
|
tg_setup(root)
|
|
home = tai_home()
|
|
install_env(root, engine="podman", state="running", venv="venv ready")
|
|
for label, token in (("good", "good"), ("bad", "bad"), ("nojson", "nojson"), ("noname", "noname")):
|
|
os.environ["TELEGRAM_BOT_TOKEN"] = token
|
|
out("-- install_telegram %s" % label)
|
|
sys.stdout.flush()
|
|
res = tai.install_telegram()
|
|
out("returned %s" % res)
|
|
out("unit exists %s env exists %s" % (os.path.exists(os.path.join(os.path.expanduser("~"), ".config", "systemd", "user", "tai-telegram.service")), os.path.exists(tai.telegram_env_path(home))))
|
|
os.environ.pop("TELEGRAM_BOT_TOKEN", None)
|
|
out("-- install_telegram without token (stdin closed)")
|
|
sys.stdout.flush()
|
|
out("returned %s" % tai.install_telegram())
|
|
out("-- install_telegram box failing")
|
|
install_env(root, engine="podman", ensure_error="no engine here")
|
|
os.environ["TELEGRAM_BOT_TOKEN"] = "good"
|
|
out("returned %s" % tai.install_telegram())
|
|
install_env(root, engine="podman", state="running", venv="venv ready")
|
|
out("-- op_telegram_service")
|
|
for token in ("good", "bad", "nojson", "noname"):
|
|
os.environ["TELEGRAM_BOT_TOKEN"] = token
|
|
for mode in ("install", "install", "reinstall", "upgrade", "uninstall", "uninstall"):
|
|
out("service %s %s => %s" % (token, mode, j(tai.op_telegram_service(mode, home).replace(root, "<ROOT>"))))
|
|
os.environ.pop("TELEGRAM_BOT_TOKEN", None)
|
|
write(tai.telegram_env_path(home), "TELEGRAM_BOT_TOKEN=good\n")
|
|
out("service from env file => %s" % j(tai.op_telegram_service("install", home)))
|
|
out("-- uninstall_telegram")
|
|
out("returned %s" % tai.uninstall_telegram())
|
|
install_env(root, engine=None)
|
|
out("returned noengine %s" % tai.uninstall_telegram())
|
|
|
|
|
|
# ------------------------------------------------------------------ release / audit scenarios
|
|
def prep_release(root):
|
|
config, seal, store = open_store()
|
|
store.close()
|
|
|
|
|
|
def sc_release(root):
|
|
import shutil
|
|
config, seal, store = open_store()
|
|
script = os.path.join(root, "script.py")
|
|
shutil.copy(tai.__file__, script)
|
|
tai.__file__ = script
|
|
folder = os.path.join(root, "backups")
|
|
out("== versions")
|
|
for cur, part in (("1.2.3", "patch"), ("1.2.3", "minor"), ("1.2.3", "major"), ("9.9.9", "patch"), ("1.2.3", "bogus"), ("1.2", "patch"), ("a.b.c", "patch"), ("1.2.3.4", "patch")):
|
|
try:
|
|
out("next %s %s => %s" % (cur, part, tai.next_version(cur, part)))
|
|
except Exception as exc:
|
|
out("next %s %s => raised %s" % (cur, part, j(str(exc))))
|
|
out("== bump")
|
|
for part in ("patch", "minor", "major", "patch"):
|
|
out("bump %s => %s" % (part, j(tai.release_bump(script, part))))
|
|
with open(script, encoding="utf-8") as handle:
|
|
text = handle.read()
|
|
out("version line %s" % j([l for l in text.splitlines() if l.startswith("VERSION = ")]))
|
|
novers = os.path.join(root, "nov.py")
|
|
write(novers, "print('x')\n")
|
|
for f in (novers, os.path.join(root, "missing.py")):
|
|
try:
|
|
tai.release_bump(f, "patch")
|
|
except Exception as exc:
|
|
out("bump error %s" % j(str(exc).replace(root, "<ROOT>")))
|
|
write(os.path.join(root, "crlf.py"), 'a\r\nVERSION = "1.0.0"\r\nb\r\n')
|
|
out("crlf bump %s %s" % (j(tai.release_bump(os.path.join(root, "crlf.py"), "patch")), j(open(os.path.join(root, "crlf.py"), "rb").read().decode())))
|
|
out("== snapshots and pruning")
|
|
first, digest = tai.snapshot_self(script, folder, "9.9.9")
|
|
out("snapshot %s %s" % (j(os.path.basename(first)), digest))
|
|
out("has digest %s %s" % (tai.backup_has_digest(folder, digest), tai.backup_has_digest(folder, "deadbeef")))
|
|
out("has digest nofolder %s" % tai.backup_has_digest(os.path.join(root, "nofolder"), digest))
|
|
for index in range(14):
|
|
name = os.path.join(folder, "tai-0.0.%d-20200101T000000Z-%08d.py" % (index, index))
|
|
write(name, "x")
|
|
os.utime(name, (1000 + index, 1000 + index))
|
|
write(os.path.join(folder, "notes.txt"), "keep")
|
|
out("pruned %d" % tai.prune_backups(folder))
|
|
out("left %s" % j(sorted(os.listdir(folder))).replace(os.path.basename(first), "<SNAP>"))
|
|
out("prune missing %d" % tai.prune_backups(os.path.join(root, "nofolder")))
|
|
out("== ensure_self_backup")
|
|
shutil.rmtree(folder)
|
|
home = tai_home()
|
|
first = tai.ensure_self_backup(config, store, script)
|
|
out("ensure first %s" % j(os.path.basename(first)))
|
|
out("ensure second %s" % j(tai.ensure_self_backup(config, store, script)))
|
|
rows = store.db.execute("SELECT actor, action, path, message, old_size, new_size, tags FROM audit ORDER BY id").fetchall()
|
|
out("audit %s" % j([list(r) for r in rows]))
|
|
out("== do_release")
|
|
try:
|
|
tai.do_release(store, script, folder, "patch", " ")
|
|
except Exception as exc:
|
|
out("blank message %s" % j(str(exc)))
|
|
res = tai.do_release(store, script, folder, "minor", " ship it ", "tester")
|
|
out("do_release %s" % j([res[0], res[1], os.path.basename(res[2]), res[3]]))
|
|
out("== release tool")
|
|
app = SkillApp(home, root)
|
|
app.store = store
|
|
app.profile = "t"
|
|
tools = tai.Tools(app)
|
|
for args in ({"part": "", "message": "m"}, {"part": "patch", "message": ""}, {"part": "huge", "message": "m"}, {"part": "Patch", "message": "tool release"}, {"part": "major", "message": "m" * 300}):
|
|
out("release %s => %s" % (j(args), j(tools.dispatch("release", json.dumps(args)).replace(root, "<ROOT>"))))
|
|
app.approve = False
|
|
out("release denied => %s" % j(tools.dispatch("release", json.dumps({"part": "patch", "message": "no"}))))
|
|
app.approve = True
|
|
write(script, "nothing here\n")
|
|
out("release no version => %s" % j(tools.dispatch("release", json.dumps({"part": "patch", "message": "m"}))))
|
|
out("approvals %s" % j(app.approvals))
|
|
out("== capped helpers")
|
|
out("capped %s" % j([tai.capped_text(None), tai.capped_text("abc"), tai.capped_text("é" * 20001)[1], tai.capped_text("x" * 20001)[0][-60:], tai.trunc_marker(5)]))
|
|
big = os.path.join(root, "big.txt")
|
|
write(big, "line\r\nsecond\rthird\n" + "é" * 30000)
|
|
for cap in (20000, 50, 100000):
|
|
r = tai.read_capped(big, cap)
|
|
out("read_capped %d => %s %s" % (cap, j(r[0][:40] if r[0] else r[0]), j(r[1])))
|
|
write(os.path.join(root, "bad.bin"), "")
|
|
with open(os.path.join(root, "bad.bin"), "wb") as handle:
|
|
handle.write(b"ok \xff\xfe bad \xc3 end")
|
|
out("read_capped invalid %s" % j(tai.read_capped(os.path.join(root, "bad.bin"))))
|
|
out("read_capped missing %s" % j(tai.read_capped(os.path.join(root, "missing"))))
|
|
store.close()
|
|
|
|
|
|
def prep_audit(root):
|
|
config, seal, store = open_store()
|
|
store.audit_event("main", "write", os.path.join("area", "a.txt"), "first write", None, "hello\n", ["file", "Doc"], None, None, "t")
|
|
store.audit_event("main", "edit", os.path.join("area", "a.txt"), "edit it", "hello\n", "hello world\n", ["file"], None, None, "t")
|
|
store.audit_event("main", "delete", os.path.join("area", "a.txt"), "", "hello world\n", None, [], None, None, "t")
|
|
store.audit_event("main", "write", os.path.join("area", "big.txt"), "big", None, "B" * 25000, ["file"], None, None, "t")
|
|
store.audit_event("main", "write", os.path.join("area", "sized.txt"), "oversize", None, "small", [], None, 99999, "t")
|
|
store.audit_event("main", "snapshot", "/x/tai.py", "backed up", None, None, ["snapshot"], None, None, "t")
|
|
store.audit_event("main", "release", "/x/tai.py", "released", "1.0.0", "1.0.1", ["release", "v1.0.1"], None, None, "t")
|
|
store.audit_event("main", "write", "sandbox:/box/f.txt", "in box", None, "boxed", [], None, None, "t")
|
|
store.audit_event("other", "write", "/o/f.txt", "other profile", None, "o", [], None, None, "other")
|
|
store.audit_event("main", "write", os.path.join("area", "sub", "deep", "r.txt"), "restore deep", None, "deep content", ["file"], None, None, "t")
|
|
store.audit_event("main", "write", os.path.join("area", "u.txt"), "unicode é alpha", None, "café \U0001f600", ["file"], None, None, "t")
|
|
store.audit_event("main", "noop", "/n", "no content", None, None, [], None, None, "t")
|
|
store.tag_item("mem:t1", ["alpha", "beta"], "t")
|
|
store.tag_item("mem:t2", ["alpha"], "t")
|
|
store.tag_item("mem:t3", ["a_b", "a%c", "alphabet"], "t")
|
|
store.close()
|
|
|
|
|
|
class AuditApp(SkillApp):
|
|
def __init__(self, home, root, store):
|
|
SkillApp.__init__(self, home, root)
|
|
self.store = store
|
|
self.profile = "t"
|
|
self.env = "home"
|
|
|
|
|
|
def sc_audit(root):
|
|
config, seal, store = open_store()
|
|
app = AuditApp(tai_home(), root, store)
|
|
tools = tai.Tools(app)
|
|
|
|
def call(name, args):
|
|
res = tools.dispatch(name, json.dumps(args))
|
|
out("%s %s => %s" % (name, j(args), j(res.replace(root, "<ROOT>"))))
|
|
|
|
out("== audit")
|
|
for args in ({}, {"limit": 3}, {"path": os.path.join("area", "a.txt")}, {"tag": "file"}, {"tag": "nomatch"}, {"query": "alpha"}, {"query": "write"}, {"query": "zzzz"}, {"limit": "x"}, {"limit": 500}, {"query": "unicode"}):
|
|
call("audit", args)
|
|
out("== tags")
|
|
for args in ({}, {"prefix": "al"}, {"prefix": "A_"}, {"prefix": "a%"}, {"prefix": "zzz"}, {"limit": 1}, {"limit": "x"}, {"prefix": " Al "}):
|
|
call("tags", args)
|
|
out("== restore")
|
|
for args in ({}, {"id": "x"}, {"id": 9999}, {"id": 6}, {"id": 7}, {"id": 1}, {"id": 3}, {"id": 4}, {"id": 5}, {"id": 12}, {"id": 10}, {"id": 11}):
|
|
call("restore", args)
|
|
for rel in ("a.txt", "sub/deep/r.txt", "u.txt"):
|
|
path = os.path.join("area", rel)
|
|
out("file %s %s" % (rel, j(open(path, encoding="utf-8").read() if os.path.exists(path) else None)))
|
|
app.approve = False
|
|
call("restore", {"id": 2})
|
|
app.approve = True
|
|
out("approvals %s" % j([a.replace(root, "<ROOT>") for a in app.approvals]))
|
|
store.close()
|
|
|
|
|
|
SCENARIOS = {
|
|
"release": sc_release,
|
|
"audit": sc_audit,
|
|
"telegram": sc_telegram,
|
|
"telegram_install": sc_telegram_install,
|
|
"rag": sc_rag,
|
|
"sched": sc_sched,
|
|
"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,
|
|
"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,
|
|
}
|
|
PREPARE = {"sched": prep_sched, "rag": prep_rag, "release": prep_release, "audit": prep_audit}
|
|
|
|
|
|
def main():
|
|
name, root = sys.argv[1], sys.argv[2]
|
|
prepare = len(sys.argv) > 3 and sys.argv[3] == "prepare"
|
|
os.chdir(os.path.join(root, "proj"))
|
|
if prepare:
|
|
fn = PREPARE.get(name)
|
|
if fn:
|
|
fn(root)
|
|
return 0
|
|
SCENARIOS[name](root)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|