Files
tai/taicpp/tests/foundation_probe.cpp
T
retoorandClaude Sonnet 5.5 4583265122 taicpp foundation: vault, settings, debug log, sessions keyed by shell/pane, REPL, plexer
Full port of the Store (17 tables, FTS5, per-profile slices, sessions, turn state,
bots, settings, MCP registry), DebugLog, SETTINGS_CATALOG and boot_settings,
ShellSession (as ShellContext), Conversation persistence, Repl/Boot, and the
PlexerController family with FlowValidator/PlexerFlow. Verified by replaying 643
operations through tai.py and the C++ code (results, tables, FTS, schema, files
identical) and by restarting the REPL under per-pane shells and checking the vault.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-25 02:02:31 +02:00

518 lines
30 KiB
C++

// Scenario runner for tests/foundation_parity.py: replays a JSON list of
// Store operations against a real vault in $TAI_HOME and prints the results as
// sorted-key JSON. The python side replays the same scenario through tai.py;
// the driver diffs both results and both vault dumps.
//
// Usage: foundation_probe <scenario.json> (needs TAI_HOME, TAI_SESSION)
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <functional>
#include <iostream>
#include <map>
#include <sstream>
#include "config/config.h"
#include "config/prompts.h"
#include "config/settings_rules.h"
#include <unistd.h>
#include <chrono>
#include <thread>
#include "plexer/flow_validator.h"
#include "plexer/plexer.h"
#include "repl/repl.h"
#include "repl/repl_style.h"
#include "session/conversation.h"
#include "store/debuglog.h"
#include "plexer/plexer_flow.h"
#include "session/shell_context.h"
#include "store/fts.h"
#include "store/patterns.h"
#include "store/tag_rules.h"
#include "store/json.h"
#include "store/spill.h"
#include "store/store.h"
using detz::Config;
using detz::store::Json;
using detz::store::NullSeal;
using detz::store::Store;
namespace {
struct Args {
const Json* pos;
const Json* kw;
const Json* get(size_t i, const char* name) const {
if (pos && pos->is_array() && i < pos->size()) return &pos->items()[i];
if (kw && kw->is_object()) return kw->find(name);
return nullptr;
}
bool present(size_t i, const char* name) const { const Json* v = get(i, name); return v != nullptr; }
std::string str(size_t i, const char* name, const std::string& d = "") const { const Json* v = get(i, name); return (v && v->is_string()) ? v->as_string() : d; }
long long num(size_t i, const char* name, long long d) const { const Json* v = get(i, name); return (v && v->is_number()) ? v->as_int() : d; }
std::vector<std::string> list(size_t i, const char* name) const {
std::vector<std::string> out;
const Json* v = get(i, name);
if (v && v->is_array()) for (auto& x : v->items()) out.push_back(x.as_string());
return out;
}
Json json(size_t i, const char* name) const { const Json* v = get(i, name); return v ? *v : Json(); }
bool is_null(size_t i, const char* name) const { const Json* v = get(i, name); return !v || v->is_null(); }
std::optional<std::string> opt_str(size_t i, const char* name) const { const Json* v = get(i, name); if (!v || v->is_null()) return std::nullopt; return v->as_string(); }
};
Json strs(const std::vector<std::string>& v) { Json a = Json::array(); for (auto& s : v) a.push(Json(s)); return a; }
Json arr(const std::vector<Json>& v) { Json a = Json::array(); for (auto& s : v) a.push(s); return a; }
Json opt(const std::optional<Json>& v) { return v ? *v : Json(); }
Json opt_s(const std::optional<std::string>& v) { return v ? Json(*v) : Json(); }
Json value_json(const detz::SettingValue& v) {
switch (v.kind) {
case detz::SettingValue::Kind::Null: return Json();
case detz::SettingValue::Kind::Bool: return Json(v.b);
case detz::SettingValue::Kind::Float: return Json(v.d);
default: return Json(v.s);
}
}
Json setting_json(const detz::store::SettingRow& r) {
Json o = Json::object();
o.set("key", r.key); o.set("scope", r.scope); o.set("type", r.type); o.set("default", value_json(r.def));
o.set("required", r.required); o.set("nullable", r.nullable);
o.set("options", r.has_options ? strs(r.options) : Json());
o.set("preferred", Json()); o.set("encrypted", r.encrypted); o.set("env", r.env); o.set("desc", r.desc);
o.set("stored", opt_s(r.stored)); o.set("value", value_json(r.value)); o.set("source", r.source);
o.set("created", opt_s(r.created)); o.set("updated", opt_s(r.updated));
return o;
}
Json rows_json(const std::vector<detz::store::SettingRow>& rows) {
Json a = Json::array();
for (auto& r : rows) a.push(setting_json(r));
return a;
}
Json events_json(const std::vector<detz::store::EventRow>& rows) {
Json a = Json::array();
for (auto& r : rows) a.push(arr({Json(r.ts), Json(r.role), Json(r.kind), Json(r.text)}));
return a;
}
using Handler = std::function<Json(Store&, const Args&)>;
std::map<std::string, Handler> handlers() {
std::map<std::string, Handler> h;
h["add_record"] = [](Store& s, const Args& a) { return Json(s.add_record(a.str(0, "kind"), a.str(1, "title"), a.str(2, "content"), a.list(3, "tags"), a.str(4, "profile"))); };
h["get_record"] = [](Store& s, const Args& a) { return opt(s.get_record(a.str(0, "record_id"), a.str(1, "profile"))); };
h["read_record"] = [](Store& s, const Args& a) { return opt(s.read_record(a.str(0, "record_id"), a.num(1, "offset", 0), a.num(2, "limit", 4000), a.str(3, "profile"))); };
h["search_records"] = [](Store& s, const Args& a) { return arr(s.search_records(a.str(0, "query"), a.str(1, "kind"), a.list(2, "tags"), static_cast<int>(a.num(3, "limit", 8)), a.str(4, "profile"))); };
h["delete_record"] = [](Store& s, const Args& a) { return Json(s.delete_record(a.str(0, "record_id"), a.str(1, "profile"))); };
h["save_secret"] = [](Store& s, const Args& a) {
std::optional<std::vector<std::string>> tags;
if (!a.is_null(3, "tags")) tags = a.list(3, "tags");
s.save_secret(a.str(0, "name"), a.str(1, "value"), a.json(2, "meta"), tags, a.str(4, "profile"));
return Json();
};
h["secret_meta"] = [](Store& s, const Args& a) { return s.secret_meta(a.str(0, "name"), a.str(1, "profile")); };
h["list_secret_infos"] = [](Store& s, const Args& a) { return arr(s.list_secret_infos(a.str(0, "profile"))); };
h["load_secret"] = [](Store& s, const Args& a) { return opt_s(s.load_secret(a.str(0, "name"), a.str(1, "profile"))); };
h["delete_secret"] = [](Store& s, const Args& a) { return Json(s.delete_secret(a.str(0, "name"), a.str(1, "profile"))); };
h["redact"] = [](Store& s, const Args& a) { return Json(s.redact(a.str(0, "text"), a.str(1, "profile"))); };
h["known_tags"] = [](Store& s, const Args& a) { auto t = s.known_tags(a.str(0, "profile")); return strs(std::vector<std::string>(t.begin(), t.end())); };
h["canonical_tags"] = [](Store& s, const Args& a) { return strs(s.canonical_tags(a.list(0, "tags"), nullptr, a.str(2, "profile"))); };
h["tag_counts"] = [](Store& s, const Args& a) {
Json out = Json::array();
for (auto& [t, n] : s.tag_counts(a.str(0, "prefix"), static_cast<int>(a.num(1, "limit", 50)), a.str(2, "profile"))) out.push(arr({Json(t), Json(static_cast<long long>(n))}));
return out;
};
h["tag_item"] = [](Store& s, const Args& a) { s.tag_item(a.str(0, "item"), a.list(1, "tags"), a.str(2, "profile")); return Json(); };
h["set_item_tags"] = [](Store& s, const Args& a) { s.set_item_tags(a.str(0, "item"), a.list(1, "tags"), a.str(2, "profile")); return Json(); };
h["item_tags"] = [](Store& s, const Args& a) { return strs(s.item_tags(a.str(0, "item"), a.str(1, "profile"))); };
h["tagged_items"] = [](Store& s, const Args& a) { auto r = s.tagged_items(a.list(0, "tags"), a.str(1, "profile")); return r ? strs(*r) : Json(); };
h["add_edge"] = [](Store& s, const Args& a) {
try { return Json(s.add_edge(a.str(0, "src"), a.str(1, "dst"), a.str(2, "relation", "linked"), a.str(3, "profile"))); }
catch (const detz::store::ValueError& e) { Json o = Json::object(); o.set("ValueError", e.what()); return o; }
};
h["edges_for"] = [](Store& s, const Args& a) { return arr(s.edges_for(a.str(0, "node"), a.str(1, "profile"))); };
h["remove_edges"] = [](Store& s, const Args& a) { return Json(static_cast<long long>(s.remove_edges(a.str(0, "node"), a.str(1, "profile")))); };
h["traverse"] = [](Store& s, const Args& a) { return arr(s.traverse(a.str(0, "start"), static_cast<int>(a.num(1, "depth", 2)), static_cast<int>(a.num(2, "limit", 50)), a.str(3, "profile"))); };
h["add_chunks"] = [](Store& s, const Args& a) { s.add_chunks(a.str(0, "parent_kind"), a.str(1, "parent_id"), a.list(2, "texts"), a.str(3, "profile")); return Json(); };
h["get_chunks_by_ids"] = [](Store& s, const Args& a) {
std::vector<int64_t> ids;
const Json* v = a.get(0, "ids");
if (v && v->is_array()) for (auto& x : v->items()) ids.push_back(x.as_int());
Json o = Json::object();
for (auto& [id, d] : s.get_chunks_by_ids(ids, a.str(1, "profile"))) o.set(std::to_string(id), d);
return o;
};
h["audit_event"] = [](Store& s, const Args& a) {
return Json(static_cast<long long>(s.audit_event(a.str(0, "actor"), a.str(1, "action"), a.str(2, "path"), a.str(3, "message"), a.opt_str(4, "old"), a.opt_str(5, "new"), a.list(6, "tags"),
a.is_null(7, "old_size") ? -1 : a.num(7, "old_size", -1), a.is_null(8, "new_size") ? -1 : a.num(8, "new_size", -1), a.str(9, "profile"))));
};
h["audit_history"] = [](Store& s, const Args& a) { return arr(s.audit_history(a.str(0, "path"), a.str(1, "tag"), static_cast<int>(a.num(2, "limit", 20)), a.str(3, "profile"))); };
h["audit_get"] = [](Store& s, const Args& a) { return opt(s.audit_get(a.num(0, "row_id", 0), a.str(1, "profile"))); };
h["audit_search"] = [](Store& s, const Args& a) { return arr(s.audit_search(a.str(0, "query"), a.str(1, "profile"), static_cast<int>(a.num(2, "limit", 20)))); };
h["upsert_file_record"] = [](Store& s, const Args& a) { return Json(s.upsert_file_record(a.str(0, "path"), a.str(1, "content"), a.is_null(2, "true_size") ? -1 : a.num(2, "true_size", -1), a.str(3, "profile"))); };
h["file_record_id"] = [](Store& s, const Args& a) { return opt_s(s.file_record_id(a.str(0, "path"), a.str(1, "profile"))); };
h["delete_file_record"] = [](Store& s, const Args& a) { return Json(s.delete_file_record(a.str(0, "path"), a.str(1, "profile"))); };
h["log_event"] = [](Store& s, const Args& a) { s.log_event(a.str(0, "profile"), a.str(1, "role"), a.str(2, "kind"), a.str(3, "text"), a.list(4, "tags")); return Json(); };
h["search_events"] = [](Store& s, const Args& a) { return events_json(s.search_events(a.str(0, "profile"), a.str(1, "query"), static_cast<int>(a.num(2, "limit", 8)), a.list(3, "tags"))); };
h["events_like"] = [](Store& s, const Args& a) { return events_json(s.events_like(a.str(0, "profile"), a.str(1, "query"), static_cast<int>(a.num(2, "limit", 8)), a.list(3, "tags"))); };
h["history_add"] = [](Store& s, const Args& a) { return Json(s.history_add(a.str(0, "profile"), a.str(1, "bot"), a.str(2, "text"))); };
h["history_load"] = [](Store& s, const Args& a) { return strs(s.history_load(a.str(0, "profile"), static_cast<int>(a.num(1, "limit", 1000)))); };
h["history_last"] = [](Store& s, const Args& a) { return opt_s(s.history_last(a.str(0, "profile"))); };
h["add_schedule"] = [](Store& s, const Args& a) {
std::optional<std::vector<std::string>> tags;
if (!a.is_null(6, "tags")) tags = a.list(6, "tags");
return Json(static_cast<long long>(s.add_schedule(a.str(0, "name"), a.str(1, "prompt"), a.str(2, "profile"), a.num(3, "every_sec", 0), a.str(4, "next_run"), a.num(5, "timeout", 0), tags)));
};
h["remove_schedule"] = [](Store& s, const Args& a) { return Json(s.remove_schedule(a.num(0, "row_id", 0), a.str(1, "profile"))); };
h["list_schedules"] = [](Store& s, const Args& a) { return arr(s.list_schedules(a.str(0, "profile"))); };
h["fts_search"] = [](Store& s, const Args& a) {
Json out = Json::array();
for (auto& hit : s.fts_search(a.str(0, "query"), a.list(1, "kinds"), a.str(2, "profile"), static_cast<int>(a.num(3, "limit", 10)))) {
Json o = Json::object();
if (hit.item_numeric) o.set("item", static_cast<long long>(std::stoll(hit.item))); else o.set("item", hit.item); o.set("kind", hit.kind); o.set("sub", hit.sub); o.set("title", hit.title); o.set("snippet", hit.snippet); o.set("rank", hit.rank);
out.push(o);
}
return out;
};
h["save_system"] = [](Store& s, const Args& a) { s.save_system(a.str(0, "name"), a.str(1, "text")); return Json(); };
h["load_system"] = [](Store& s, const Args& a) { return opt_s(s.load_system(a.str(0, "name"))); };
h["save_session"] = [](Store& s, const Args& a) {
std::vector<Json> msgs;
const Json* v = a.get(1, "messages");
if (v && v->is_array()) msgs = v->items();
s.save_session(a.str(0, "name"), msgs, a.str(2, "bot", "main"), a.str(3, "ctx"));
return Json();
};
h["load_session"] = [](Store& s, const Args& a) { return arr(s.load_session(a.str(0, "name"), a.str(1, "bot", "main"), a.str(2, "ctx"))); };
h["save_turn_state"] = [](Store& s, const Args& a) { s.save_turn_state(a.str(0, "profile"), a.str(1, "bot"), a.json(2, "state"), a.str(3, "ctx")); return Json(); };
h["load_turn_state"] = [](Store& s, const Args& a) { return s.load_turn_state(a.str(0, "profile"), a.str(1, "bot", "main"), a.str(2, "ctx")); };
h["save_bots"] = [](Store& s, const Args& a) { s.save_bots(a.str(0, "profile"), a.json(1, "bots")); return Json(); };
h["load_bots"] = [](Store& s, const Args& a) { return s.load_bots(a.str(0, "profile")); };
h["list_bots"] = [](Store& s, const Args& a) { return arr(s.list_bots(a.str(0, "profile"))); };
h["resolve_bot"] = [](Store& s, const Args& a) { return opt_s(s.resolve_bot(a.str(0, "profile"), a.str(1, "mention"))); };
h["save_bot_system"] = [](Store& s, const Args& a) { s.save_bot_system(a.str(0, "profile"), a.str(1, "bot"), a.str(2, "text")); return Json(); };
h["load_bot_system"] = [](Store& s, const Args& a) { return opt_s(s.load_bot_system(a.str(0, "profile"), a.str(1, "bot"))); };
h["list_profiles"] = [](Store& s, const Args&) { return strs(s.list_profiles()); };
h["get_or_create_settings"] = [](Store& s, const Args& a) {
try {
std::optional<std::vector<std::string>> keys;
if (!a.is_null(0, "keys")) {
const Json* v = a.get(0, "keys");
keys = v->is_string() ? std::vector<std::string>{v->as_string()} : a.list(0, "keys");
}
return rows_json(s.get_or_create_settings(keys, a.str(1, "scope", "global")));
} catch (const detz::store::ValueError& e) { Json o = Json::object(); o.set("ValueError", e.what()); return o; }
};
h["get_all_settings"] = [](Store& s, const Args& a) {
try { return rows_json(s.get_all_settings(a.str(0, "scope", "global"))); }
catch (const detz::store::ValueError& e) { Json o = Json::object(); o.set("ValueError", e.what()); return o; }
};
h["upsert_settings"] = [](Store& s, const Args& a) {
try {
std::map<std::string, Json> items;
Json v = a.json(0, "items");
if (v.is_object()) for (auto& m : v.members()) items[m.first] = m.second;
return rows_json(s.upsert_settings(items, a.str(1, "scope", "global")));
} catch (const detz::store::ValueError& e) { Json o = Json::object(); o.set("ValueError", e.what()); return o; }
};
h["mcp_save_server"] = [](Store& s, const Args& a) {
try { s.mcp_save_server(a.json(0, "entry")); return Json(); }
catch (const detz::store::ValueError& e) { Json o = Json::object(); o.set("ValueError", e.what()); return o; }
};
h["mcp_get_server"] = [](Store& s, const Args& a) { return opt(s.mcp_get_server(a.str(0, "name"))); };
h["mcp_list_servers"] = [](Store& s, const Args&) { return arr(s.mcp_list_servers()); };
h["mcp_remove_server"] = [](Store& s, const Args& a) { s.mcp_remove_server(a.str(0, "name")); return Json(); };
h["mcp_set_enabled"] = [](Store& s, const Args& a) { const Json* v = a.get(2, "enabled"); s.mcp_set_enabled(a.str(0, "server"), a.str(1, "profile"), v && v->is_bool() && v->as_bool()); return Json(); };
h["mcp_server_enabled"] = [](Store& s, const Args& a) { return Json(s.mcp_server_enabled(a.str(0, "server"), a.str(1, "profile"))); };
h["mcp_enabled_servers"] = [](Store& s, const Args& a) { return arr(s.mcp_enabled_servers(a.str(0, "profile"))); };
h["mcp_store_tools"] = [](Store& s, const Args& a) {
std::vector<Json> tools;
const Json* v = a.get(1, "tools");
if (v && v->is_array()) tools = v->items();
s.mcp_store_tools(a.str(0, "server"), tools);
return Json();
};
h["mcp_cached_tools"] = [](Store& s, const Args& a) { return arr(s.mcp_cached_tools(a.str(0, "server"))); };
h["mcp_tool_pins"] = [](Store& s, const Args& a) { Json o = Json::object(); for (auto& [k, v] : s.mcp_tool_pins(a.str(0, "server"))) o.set(k, v); return o; };
h["load_dir_defaults"] = [](Store& s, const Args& a) { Json o = Json::object(); for (auto& [k, v] : s.load_dir_defaults(a.str(0, "directory"))) o.set(k, v); return o; };
h["save_dir_defaults"] = [](Store& s, const Args& a) { s.save_dir_defaults(a.str(0, "directory"), a.str(1, "provider"), a.str(2, "model"), a.str(3, "bot"), a.list(4, "clear")); return Json(); };
h["spill_or_truncate"] = [](Store& s, const Args& a) { return Json(detz::store::Spill::spill_or_truncate(&s, a.str(0, "kind"), a.str(1, "title"), a.str(2, "text"), a.list(3, "tags"), a.str(4, "profile"))); };
return h;
}
// Replays scripted replies for PlexerFlow and logs every screen it was asked.
class FakeController : public detz::plexer::PlexerController {
public:
explicit FakeController(Json replies) : replies_(std::move(replies)) {}
Json send_keys(const std::string&, bool) override { return Json(); }
Json read_pane(int) override { return Json(""); }
Json status() override { return Json(); }
Json ask(const Json& screen, const Json&) override {
asked.push(screen);
Json r = replies_.items()[next_++];
return r;
}
Json asked = Json::array();
private:
Json replies_;
size_t next_{0};
};
Json err_json(const char* what) { Json o = Json::object(); o.set("ValueError", what); return o; }
// Captures everything written to fd 1 while alive (the REPL prints with printf).
class StdoutCapture {
public:
StdoutCapture() {
std::fflush(stdout);
saved_ = ::dup(1);
file_ = std::tmpfile();
::dup2(::fileno(file_), 1);
}
std::string finish() {
std::fflush(stdout);
::dup2(saved_, 1);
::close(saved_);
std::string out;
std::rewind(file_);
char buf[4096];
size_t n;
while ((n = std::fread(buf, 1, sizeof buf, file_)) > 0) out.append(buf, n);
std::fclose(file_);
return out;
}
private:
int saved_;
std::FILE* file_;
};
class NullHost : public detz::repl::ReplHost {
public:
std::string run_turn(const std::string&) override { return ""; }
};
void add_pure_handlers(std::map<std::string, Handler>& h) {
using namespace detz;
h["statusbar"] = [](Store&, const Args& a) { return Json(repl::ReplStyle::statusbar(a.str(0, "version"), a.str(1, "profile"), a.str(2, "directory"), a.json(3, "yolo").as_bool(), static_cast<int>(a.num(4, "agents", 0)), static_cast<size_t>(a.num(5, "width", 0)))); };
h["strip_ansi"] = [](Store&, const Args& a) { return Json(repl::ReplStyle::strip_ansi(a.str(0, "text"))); };
h["truncate_ansi"] = [](Store&, const Args& a) { return Json(repl::ReplStyle::truncate_ansi(a.str(0, "text"), static_cast<size_t>(a.num(1, "width", 0)))); };
h["glow_prompt"] = [](Store&, const Args&) { return Json(repl::ReplStyle::glow_prompt()); };
h["glow_title"] = [](Store&, const Args& a) { return Json(repl::ReplStyle::glow_title(a.str(0, "version"), a.str(1, "profile"))); };
h["sleep"] =[](Store&, const Args& a) { std::this_thread::sleep_for(std::chrono::duration<double>(a.json(0, "seconds").as_double())); return Json(); };
h["native_ctl"] = [](Store&, const Args& a) {
plexer::NativePlexerController ctl(a.str(0, "sock"), a.str(1, "pane"));
std::string method = a.str(2, "method");
Json args = a.json(3, "args");
try {
if (method == "type_text") { ctl.type_text(args.items()[0].as_string(), 0.0, args.items()[1].as_bool()); return Json(); }
if (method == "call") return ctl.call(args.items()[0]);
if (method == "send_keys") return ctl.send_keys(args.items()[0].as_string(), args.items()[1].as_bool());
if (method == "read_pane") return ctl.read_pane(static_cast<int>(args.items()[0].as_int()));
if (method == "status") return ctl.status();
return ctl.ask(args.items()[0], args.items()[1]);
} catch (const plexer::PlexerError& e) {
Json o = Json::object();
o.set("PlexerError", e.what());
return o;
}
};
h["tmux_ctl"] = [](Store&, const Args& a) {
plexer::TmuxPlexerController ctl(a.str(0, "pane"));
std::string method = a.str(1, "method");
Json args = a.json(2, "args");
try {
if (method == "type_text") { ctl.type_text(args.items()[0].as_string(), 0.0, args.items()[1].as_bool()); return Json(); }
if (method == "send_keys") return ctl.send_keys(args.items()[0].as_string(), args.items()[1].as_bool());
if (method == "read_pane") return ctl.read_pane(static_cast<int>(args.items()[0].as_int()));
if (method == "status") return ctl.status();
return ctl.ask(args.items()[0], args.items()[1]);
} catch (const plexer::PlexerError& e) {
Json o = Json::object();
o.set("PlexerError", e.what());
return o;
}
};
h["log_query"] = [](Store& s, const Args& a) {
Json f = a.json(0, "filter");
store::LogFilter lf;
lf.level = f.get_string("level"); lf.component = f.get_string("component"); lf.event = f.get_string("event");
lf.profile = f.get_string("profile"); lf.bot = f.get_string("bot"); lf.chat = f.get_string("chat"); lf.run = f.get_string("run");
if (const Json* l = f.find("limit")) lf.limit = static_cast<int>(l->as_int());
return arr(s.log_query(lf));
};
h["debug_log"] = [](Store& s, const Args& a) {
auto& log = store::DebugLog::Global();
log.configure(a.str(0, "sink"), a.str(1, "fmt"), s.config().home, &s);
for (auto& ev : a.json(2, "events").items()) {
const auto& it = ev.items();
Json kw = it[4];
log.event(it[0].as_string(), it[1].as_string(), it[2].as_string(), it[3].as_string(), kw.get_string("profile"), kw.get_string("bot"), kw.get_string("chat"),
kw.has("meta") ? *kw.find("meta") : Json());
}
std::string run = log.run;
log.close();
log.reset();
std::ifstream in(s.config().home + "/debug.log", std::ios::binary);
std::stringstream ss;
ss << in.rdbuf();
return arr({Json(run), Json(ss.str())});
};
h["repl_script"] = [](Store& s, const Args& a) {
Config cfg = s.config();
cfg.profile = "default";
const char* tai_session = std::getenv("TAI_SESSION");
session::Conversation conv(s, cfg, true, false, false, tai_session ? tai_session : "");
NullHost host;
repl::Repl::Options opts;
repl::Repl app(conv, host, opts);
Json outputs = Json::array();
for (auto& text : a.json(0, "script").items()) {
StdoutCapture cap;
bool cont = app.handle_command(text.as_string());
outputs.push(arr({Json(cap.finish()), Json(cont)}));
}
Json msgs = Json::array();
for (size_t k = 1; k < conv.messages().size(); k++) msgs.push(conv.messages()[k]);
return arr({outputs, arr({Json(conv.profile()), Json(conv.bot()), msgs})});
};
h["prompt"] = [](Store&, const Args& a) {
std::string n = a.str(0, "name");
if (n == "DEFAULT_SYSTEM") return Json(Prompts::DefaultSystem());
if (n == "MERGE_SYSTEM") return Json(Prompts::MergeSystem());
if (n == "COMPACT_SYSTEM") return Json(Prompts::CompactSystem());
if (n == "AUTONOMOUS_NOTE") return Json(Prompts::AutonomousNote());
if (n == "HELP_TEXT") return Json(Prompts::HelpText());
if (n == "LEGACY_PASSWORD_NOTE") return Json(Prompts::LegacyPasswordNote());
return Json(Prompts::VaultMemoryNote());
};
h["capability_block"] = [](Store&, const Args& a) { return Json(Prompts::CapabilityBlock(a.json(0, "yolo").is_bool() && a.json(0, "yolo").as_bool(), a.json(1, "auto").is_bool() && a.json(1, "auto").as_bool())); };
h["orientation_block"] = [](Store&, const Args& a) { return Json(Prompts::OrientationBlock(a.str(0, "directory"), a.json(1, "sandbox").is_bool() && a.json(1, "sandbox").as_bool())); };
h["settings_catalog"] = [](Store&, const Args&) {
Json o = Json::object();
for (auto& [k, s] : SettingsCatalog()) {
Json e = Json::object();
e.set("key", s.key); e.set("env", s.env); e.set("type", s.type); e.set("default", value_json(s.def)); e.set("desc", s.help);
e.set("required", s.required); e.set("nullable", s.nullable); e.set("encrypted", s.encrypted);
e.set("options", s.hasOptions ? strs(s.options) : Json());
e.set("preferred", Json());
e.set("true_words", s.trueWords.empty() ? Json() : strs(s.trueWords));
e.set("false_words", s.falseWords.empty() ? Json() : strs(s.falseWords));
o.set(k, e);
}
return o;
};
h["json_dumps"] = [](Store&, const Args& a) { Json v = a.json(0, "value"); return arr({Json(v.dumps()), Json(v.dumps(true))}); };
h["json_roundtrip"] = [](Store&, const Args& a) {
try { return Json(Json::loads(a.str(0, "text")).dumps(true)); }
catch (const store::JsonError&) { return err_json("x"); }
};
h["float_repr"] = [](Store&, const Args& a) { return Json(Json::float_repr(a.json(0, "v").as_double())); };
h["normalize_tag"] = [](Store&, const Args& a) { return Json(store::TagRules::normalize_tag(a.str(0, "raw"))); };
h["normalize_tags"] = [](Store&, const Args& a) { return strs(store::TagRules::normalize_tags(a.list(0, "items"))); };
h["singular_noun"] = [](Store&, const Args& a) { return Json(store::TagRules::singular_noun(a.str(0, "word"))); };
h["tag_variants"] = [](Store&, const Args& a) { return strs(store::TagRules::tag_variants(a.str(0, "tag"))); };
h["parse_node_id"] = [](Store&, const Args& a) { auto r = store::TagRules::parse_node_id(a.str(0, "raw")); return r ? arr({Json(r->first), Json(r->second)}) : Json(); };
h["fts_match_variants"] = [](Store&, const Args& a) { auto t = store::Fts::escape(a.str(0, "query")); return t ? strs(store::Fts::match_variants(*t)) : Json(); };
h["fts_rank"] = [](Store&, const Args& a) { return Json(store::Fts::rank(a.str(0, "table"))); };
h["profile_re"] = [](Store&, const Args& a) { return Json(store::Patterns::profile(a.str(0, "s"))); };
h["bot_re"] = [](Store&, const Args& a) { return Json(store::Patterns::bot(a.str(0, "s"))); };
h["secret_re"] = [](Store&, const Args& a) { return Json(store::Patterns::secret(a.str(0, "s"))); };
h["mcp_server_re"] = [](Store&, const Args& a) { return Json(store::Patterns::mcp_server(a.str(0, "s"))); };
h["normalize_setting_value"] = [](Store&, const Args& a) {
try {
auto r = SettingsRules::Normalize(a.str(0, "key"), SettingsCatalog().at(a.str(0, "key")), a.json(1, "value"));
return opt_s(r);
} catch (const store::ValueError& e) { return err_json(e.what()); }
};
h["parse_setting_env"] = [](Store&, const Args& a) {
try { return value_json(SettingsRules::ParseEnv(SettingsCatalog().at(a.str(0, "key")), a.str(1, "raw"))); }
catch (const store::ValueError& e) { return err_json(e.what()); }
};
h["parse_setting_stored"] = [](Store&, const Args& a) {
try { return value_json(SettingsRules::ParseStored(SettingsCatalog().at(a.str(0, "key")), a.str(1, "stored"))); }
catch (const store::ValueError&) { return err_json("could not convert"); }
};
h["parse_backends"] = [](Store&, const Args& a) {
Json out = Json::array();
for (auto& b : ParseBackends(a.str(0, "spec"))) {
Json o = Json::object();
o.set("label", b.label); o.set("base", b.base); o.set("key", b.key); o.set("model", b.model); o.set("profile", b.profile);
out.push(o);
}
return out;
};
h["split_models"] = [](Store&, const Args& a) { return strs(SplitModels(a.str(0, "spec"))); };
h["chunk_lines"] = [](Store&, const Args& a) { return strs(store::Spill::chunk_lines(a.str(0, "text"), static_cast<int>(a.num(1, "max_lines", 60)), static_cast<int>(a.num(2, "overlap_lines", 3)), static_cast<int>(a.num(3, "max_chars", 4000)))); };
h["truncate"] = [](Store&, const Args& a) { return Json(store::Spill::truncate(a.str(0, "text"), static_cast<size_t>(a.num(1, "head", 1500)), static_cast<size_t>(a.num(2, "tail", 500)))); };
h["shell_current"] = [](Store&, const Args& a) {
std::map<std::string, std::string> env;
Json e = a.json(0, "env");
if (e.is_object()) for (auto& m : e.members()) env[m.first] = m.second.as_string();
auto c = session::ShellContext::current(&env, a.num(1, "pid", 0), a.str(2, "proc_root"), a.json(3, "now").as_double(), a.num(4, "clk_tck", 100));
Json chain = Json::array();
for (auto& x : c.chain) {
Json o = Json::object();
o.set("pid", static_cast<long long>(x.pid)); o.set("ppid", static_cast<long long>(x.ppid)); o.set("comm", x.comm); o.set("argv0", x.argv0);
o.set("name", x.name); o.set("start", x.start); o.set("start_iso", x.start_iso); o.set("sid", x.sid);
chain.push(o);
}
return arr({Json(c.sid), Json(c.shell), Json(static_cast<long long>(c.pid)), Json(c.started), c.parent ? Json(*c.parent) : Json(), chain});
};
h["sanitize_sid"] = [](Store&, const Args& a) { return Json(session::ShellContext::sanitize(a.str(0, "value"))); };
h["validate_flow_spec"] = [](Store&, const Args& a) {
auto r = plexer::FlowValidator::spec(a.json(0, "spec"));
return arr({r.second.empty() ? r.first : Json(), Json(r.second)});
};
h["run_flow"] = [](Store&, const Args& a) {
auto v = plexer::FlowValidator::spec(a.json(0, "spec"));
if (!v.second.empty()) return arr({Json(), Json(v.second)});
FakeController fake(a.json(1, "replies"));
plexer::PlexerFlow flow(v.first);
Json outcome = flow.run(fake);
return arr({outcome, fake.asked});
};
}
Json substitute(const Json& v, const std::vector<Json>& results) {
if (v.is_string() && v.as_string().size() > 2 && v.as_string().rfind("$R", 0) == 0) {
size_t idx = std::stoul(v.as_string().substr(2));
if (idx < results.size()) return results[idx];
}
if (v.is_array()) { Json a = Json::array(); for (auto& x : v.items()) a.push(substitute(x, results)); return a; }
if (v.is_object()) { Json o = Json::object(); for (auto& m : v.members()) o.set(m.first, substitute(m.second, results)); return o; }
return v;
}
} // namespace
int main(int argc, char** argv) {
if (argc < 2) { std::fprintf(stderr, "usage: foundation_probe scenario.json\n"); return 2; }
std::ifstream in(argv[1], std::ios::binary);
std::stringstream ss;
ss << in.rdbuf();
Json scenario = Json::loads(ss.str());
Config config = Config::Build("default", false, false);
NullSeal seal;
Store store(config, seal);
auto table = handlers();
add_pure_handlers(table);
std::vector<Json> results;
for (auto& step : scenario.items()) {
std::string op = step.items()[0].as_string();
Json pos = step.size() > 1 ? substitute(step.items()[1], results) : Json::array();
Json kw = step.size() > 2 ? substitute(step.items()[2], results) : Json::object();
auto it = table.find(op);
if (it == table.end()) { std::fprintf(stderr, "unknown op %s\n", op.c_str()); return 3; }
Args args{&pos, &kw};
results.push_back(it->second(store, args));
}
store.close();
Json out = Json::array();
for (auto& r : results) out.push(r);
std::cout << out.dumps(true) << std::endl;
return 0;
}