Convert to QtSql and extend

This commit is contained in:
Χγφτ Kompanion 2025-10-16 03:45:27 +13:00
parent a04506a081
commit 70848fda6e
10 changed files with 847 additions and 722 deletions

View File

@ -1,60 +1,66 @@
cmake_minimum_required(VERSION 3.22)
project(metal_kompanion_mcp LANGUAGES CXX)
project(Kompanion LANGUAGES CXX)
set(PROJECT_VERSION "0.0.1")
set(QT_MIN_VERSION "6.8.0")
set(KF6_MIN_VERSION "6.8.0")
set(KDE_COMPILERSETTINGS_LEVEL "5.82")
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
include(GNUInstallDirs)
find_package(ECM ${KF6_MIN_VERSION} REQUIRED NO_MODULE)
set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH})
option(KOM_ENABLE_PG "Enable Postgres-backed DAL support" ON)
include(KDEInstallDirs)
include(KDECMakeSettings)
include(KDECompilerSettings NO_POLICY_SCOPE)
include(ECMMarkAsTest)
include(ECMMarkNonGuiExecutable)
include(FeatureSummary)
include(CheckIncludeFile)
include(CheckIncludeFiles)
include(CheckSymbolExists)
include(ECMOptionalAddSubdirectory)
include(KDEClangFormat)
include(ECMDeprecationSettings)
set(KOM_HAVE_PG OFF)
if (KOM_ENABLE_PG)
find_package(PostgreSQL QUIET)
find_package(pqxx QUIET)
if (PostgreSQL_FOUND AND pqxx_FOUND)
set(KOM_HAVE_PG ON)
endif()
include(KDEGitCommitHooks)
find_package(Qt6 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS
Core
Sql
)
option(KOMPANION_USE_GUI "Build optional GUI components using Qt6Gui" ON)
if (KOMPANION_USE_GUI)
find_package(Qt6 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS Gui)
endif()
if (KOM_HAVE_PG)
message(STATUS "kom_dal: Postgres support enabled (HAVE_PG)")
else()
message(STATUS "kom_dal: Postgres support disabled (pqxx/libpq not found)")
endif()
set(KOM_HAVE_PG ${KOM_HAVE_PG} CACHE INTERNAL "kom_dal has Postgres backend")
set(KOMPANION_KCONFIG_TARGET "")
find_package(KF6Config QUIET)
find_package(KF6Config ${KF6_MIN_VERSION} QUIET)
if (KF6Config_FOUND)
set(KOMPANION_KCONFIG_TARGET KF6::ConfigCore)
set(KOMPANION_KCONFIG_TARGET KF6::ConfigCore)
else()
find_package(KF5Config QUIET)
if (KF5Config_FOUND)
set(KOMPANION_KCONFIG_TARGET KF5::ConfigCore)
endif()
find_package(KF5Config QUIET)
if (KF5Config_FOUND)
set(KOMPANION_KCONFIG_TARGET KF5::ConfigCore)
endif()
endif()
add_feature_info("KConfig" NOT (KOMPANION_KCONFIG_TARGET STREQUAL "") "Persist settings via KConfig if available.")
if (KOMPANION_KCONFIG_TARGET STREQUAL "")
message(WARNING "KConfig (KF6/KF5) not found; defaulting to environment-based configuration.")
endif()
find_package(Qt6Test ${QT_MIN_VERSION} CONFIG QUIET)
set_package_properties(Qt6Test PROPERTIES
PURPOSE "Required for tests"
TYPE OPTIONAL
)
add_feature_info("Qt6Test" Qt6Test_FOUND "Required for building tests")
set(KOMPANION_DB_INIT_INSTALL_DIR "${CMAKE_INSTALL_FULL_DATAROOTDIR}/kompanion/db/init")
install(DIRECTORY db/init/ DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/kompanion/db/init FILES_MATCHING PATTERN "*.sql")
set(KOMPANION_DB_INIT_INSTALL_DIR "${KDE_INSTALL_FULL_DATADIR}/kompanion/db/init")
install(DIRECTORY db/init/ DESTINATION ${KDE_INSTALL_DATADIR}/kompanion/db/init FILES_MATCHING PATTERN "*.sql")
option(BUILD_KOMPANION_CLI "Build Kompanion Qt command-line client" ON)
if (BUILD_KOMPANION_CLI)
find_package(Qt6 COMPONENTS Core QUIET)
if (NOT Qt6_FOUND)
message(WARNING "Qt6 Core not found; disabling Kompanion CLI.")
set(BUILD_KOMPANION_CLI OFF)
endif()
endif()
# Placeholder: find Qt and qtmcp when available
# find_package(Qt6 COMPONENTS Core Network REQUIRED)
# find_package(qtmcp REQUIRED)
add_subdirectory(src/dal)
@ -63,14 +69,15 @@ add_executable(kom_mcp
)
target_include_directories(kom_mcp PRIVATE src)
target_link_libraries(kom_mcp PRIVATE kom_dal)
if (NOT KOMPANION_KCONFIG_TARGET STREQUAL "")
target_link_libraries(kom_mcp PRIVATE ${KOMPANION_KCONFIG_TARGET})
target_compile_definitions(kom_mcp PRIVATE HAVE_KCONFIG)
endif()
target_compile_options(kom_mcp PRIVATE -fexceptions)
target_compile_definitions(kom_mcp PRIVATE
PROJECT_SOURCE_DIR="${CMAKE_SOURCE_DIR}"
KOMPANION_DB_INIT_INSTALL_DIR="${KOMPANION_DB_INIT_INSTALL_DIR}"
)
if (NOT KOMPANION_KCONFIG_TARGET STREQUAL "")
target_link_libraries(kom_mcp PRIVATE ${KOMPANION_KCONFIG_TARGET})
target_compile_definitions(kom_mcp PRIVATE HAVE_KCONFIG)
endif()
install(TARGETS kom_mcp RUNTIME DESTINATION bin)
@ -81,15 +88,20 @@ if (BUILD_KOMPANION_CLI)
src/cli/KompanionApp.cpp
)
target_include_directories(kompanion PRIVATE src)
target_link_libraries(kompanion PRIVATE Qt6::Core kom_dal)
target_link_libraries(kompanion PRIVATE
Qt6::Core
Qt6::Sql
kom_dal
)
target_compile_definitions(kompanion PRIVATE
PROJECT_SOURCE_DIR="${CMAKE_SOURCE_DIR}"
KOMPANION_DB_INIT_INSTALL_DIR="${KOMPANION_DB_INIT_INSTALL_DIR}"
)
if (NOT KOMPANION_KCONFIG_TARGET STREQUAL "")
target_link_libraries(kompanion PRIVATE ${KOMPANION_KCONFIG_TARGET})
target_compile_definitions(kompanion PRIVATE HAVE_KCONFIG)
target_link_libraries(kompanion PRIVATE ${KOMPANION_KCONFIG_TARGET})
target_compile_definitions(kompanion PRIVATE HAVE_KCONFIG)
endif()
target_compile_options(kompanion PRIVATE -fexceptions -Wno-unused-function)
install(TARGETS kompanion RUNTIME DESTINATION bin)
endif()
@ -97,3 +109,5 @@ if (BUILD_TESTS)
enable_testing()
add_subdirectory(tests)
endif()
feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES)

View File

@ -12,6 +12,7 @@ The CLI (`kompanion`) and MCP runner (`kom_mcp`) fall back to this entry when th
## Initialization Wizard
- Run `kompanion --init` to launch an interactive wizard.
- Autodetects reachable Postgres instances (tries `postgresql://kompanion:komup@localhost/kompanion_test`).
- Inspects local socket (`/var/run/postgresql`) and existing databases owned by the current user via `psql -At`, offering them as defaults.
- Prompts for host, port, database, user, password, or Unix socket path with sensible defaults.
- Writes the resulting DSN to `kompanionrc` and exports `PG_DSN` for the current session.
- If the target database is empty, it applies the SQL migrations shipped under `share/kompanion/db/init/*.sql`.

View File

@ -2,7 +2,7 @@
## Interfaces
- `IDatabase` — connect/tx + memory ops (ensureNamespace, upsertItem/Chunks/Embeddings, searchText/searchVector).
- `PgDal`stub implementation for now (no libpq linked).
- `PgDal`Qt6/QSql-based implementation with in-memory fallback.
## SQL Calls (target)
- ensureNamespace: `INSERT ... ON CONFLICT (name) DO UPDATE RETURNING id`
@ -14,12 +14,11 @@
## Next
- Wire `Handlers::upsert_memory` / `search_memory` to `IDatabase`.
- Add libpq (or pqxx) and parameterized statements.
- Add RLS/session GUCs & retries.
- Harden SQL with RLS/session GUCs & retries.
- Expand hybrid search scoring (RRF weights, secret filters).
## Implementation Checklist (2025-10-15)
- Detect `libpq` + `pqxx` in CMake, define `HAVE_PG`, and link `kom_dal` against `PostgreSQL::PostgreSQL` and `pqxx::pqxx`.
- During `PgDal::connect`, prepare statements for namespace ensure/upsert, chunk/embedding insert, text/vector search, and hybrid search.
- Guard runtime selection: `stub://` DSN keeps the current in-memory store; non-stub DSNs require a live Postgres connection.
- Expose environment variables in docs: `PG_DSN` (full libpq connection string) and optional `PGSSLMODE`.
- Surface informative `std::runtime_error` messages when pqxx operations fail so MCP handlers can emit actionable errors.
- Require Qt6::Sql (`QPSQL` driver) at configure time; bail out early when unavailable.
- During `PgDal::connect`, parse DSNs with `QUrl`, open `QSqlDatabase`, and retain in-memory fallback for `stub://`.
- Use `QSqlQuery` with `INSERT ... RETURNING` for namespace/item/chunk/embedding operations.
- Derive DSNs from `kompanionrc` (KConfig) or CLI wizard, and surface informative `std::runtime_error` messages when QSql operations fail.

View File

@ -1,30 +1,26 @@
# Memory Architecture Roadmap (2025-10-15)
## Current Snapshot
- `PgDal` remains an in-memory stub; `libpq`/`pqxx` are not linked and there is no `HAVE_PG` compile guard.
- `contract_memory` now builds via `tests/contract_memory.cpp` as a dedicated CTest executable (stub DAL still backing it).
- MCP handlers directly invoke the stub DAL and perform ad-hoc JSON parsing.
- No `resources/` descriptors exist for episodic memory APIs or the semantic sync workflow.
- `PgDal` now prefers Qt6/QSql (`QPSQL`) with an in-memory fallback for `stub://` DSNs; schema migrations live in `db/init/`.
- `kompanion --init` guides DSN detection (psql socket probing), applies migrations, and persists config via `~/.config/kompanionrc`.
- MCP handlers still parse JSON manually but leverage the shared DAL; resource descriptors under `resources/memory/kom.memory.v1/` capture episodic/semantic contracts.
- Contract tests (`contract_memory`, `contract_mcp_tools`, `mcp_memory_exchange`) validate the Qt-backed DAL and MCP handlers.
## 1. CTest Target: `contract_memory`
1. Keep `contract_memory.cpp` focused on exercising `PgDal` write/read surfaces; expand as DAL features land.
2. Ensure the executable runs without Postgres by defaulting to `stub://memory` when `PG_DSN` is absent.
3. Layer follow-up assertions once pqxx-backed code exists (e.g., detect `HAVE_PG` and connect to real DB in CI).
3. Layer follow-up assertions once the QSql path is exercised end-to-end (CI can target the packaged test database).
## 2. DAL Prepared Statements (`HAVE_PG`)
## 2. DAL (Qt6/QSql) Evolution
**Dependencies**
- System packages: `libpq` headers and `libpqxx` (>= 7.8).
- CMake: `find_package(PostgreSQL REQUIRED)` and `find_package(pqxx REQUIRED)`; link `PostgreSQL::PostgreSQL` and `pqxx::pqxx`.
- Qt6 (Core, Sql) with the `QPSQL` driver available at runtime.
- KDE Frameworks `ConfigCore` for persisting DSNs in `kompanionrc`.
**Implementation Steps**
1. Extend `src/dal/CMakeLists.txt` to define `HAVE_PG` and link the libraries when detection succeeds.
2. Enhance `PgDal` to manage both modes:
- `stub://` DSN → in-memory containers (status quo).
- Other DSNs → establish `pqxx::connection`, store it via `std::unique_ptr`, and call `conn->prepare(...)` during `connect`.
3. Prepare statements for: `ensure_namespace`, `upsert_item`, `insert_chunk`, `insert_embedding`, `search_text`, `search_vector`, `hybrid_search`.
4. Implement transactional helpers using `pqxx::work`, with RAII to guarantee `commit()`/`abort()` pairing.
5. Translate `pqxx` exceptions into `std::runtime_error` with context so MCP handlers can emit useful error JSON.
6. Document required environment variables (`PG_DSN`, optional `PGSSLMODE`) plus migration expectations in `docs/dal-skeleton.md`.
1. Parse libpq-style DSNs with `QUrl`, open `QSqlDatabase` connections when the DSN is not `stub://`, and maintain the existing in-memory fallback for tests.
2. Use `QSqlQuery` `INSERT ... RETURNING` statements for namespaces, items, chunks, and embeddings; emit vector literals (`[0.1,0.2]`) when targeting pgvector columns.
3. Surface detailed `QSqlError` messages (throwing `std::runtime_error`) so MCP handlers and the CLI can report actionable failures.
4. Share configuration between CLI and MCP runners via KConfig (`Database/PgDsn`), seeded through the new `kompanion --init` wizard.
## 3. MCP `resources/*` & Episodic→Semantic Sync
**Directory Layout**
@ -60,10 +56,10 @@
3. Merge results in C++ with Reciprocal Rank Fusion or weighted sum, ensuring deterministic ordering on ties.
**Handler Integration**
1. Extend `PgDal::hybridSearch` to dispatch to the prepared statements when `HAVE_PG` is defined; reuse in-memory fallback otherwise.
1. Ensure `PgDal::hybridSearch` delegates to SQL-based lexical/vector search when a database connection is active, reusing the in-memory fallback only for `stub://`.
2. Return richer matches (id, score, optional chunk text) to satisfy MCP response schema.
3. Update `HandlersMemory::search_memory` to surface the new scores and annotate whether lexical/vector contributed (optional metadata).
4. Add a contract test scenario once pqxx-backed execution is available (requires live Postgres fixture later).
4. Exercise hybrid queries in contract tests against the packaged test database (`db/scripts/create-test-db.sh`).
## 5. Secret Handling, Snapshots, and CLI Hooks
- **Secret propagation**: episodic `sensitivity` + `embeddable` flags gate embedding generation. DAL queries will add predicates (`metadata->>'sensitivity' != 'secret'`) before hybrid search.
@ -76,9 +72,9 @@
- **CLI UX**: `kompanion --init` guides first-run setup (auto-detects databases, applies schemas); `-I/--interactive` keeps a JSON REPL open, and `-V/--verbose` echoes request/response streams for future HTTP transport parity.
## Next-Step Checklist
- [x] Detect pqxx via CMake and plumb `HAVE_PG`.
- [x] Promote Qt6/QSql backend (QPSQL) as default DAL; retain `stub://` fallback for tests.
- [x] Normalize contract_memory CTest target and remove stale library target.
- [ ] Author `resources/memory/` descriptors and sync job outline.
- [ ] Extend DAL header to carry prepared-statement aware APIs (may introduce new structs).
- [ ] Extend DAL header to expose richer query structs (filters, pagination, secret handling).
- [x] Update `docs/mcp-memory-api.md` to mention episodic sync + hybrid search fields.
- [ ] Create follow-up acf subtasks when concrete implementation begins (pgvector migration, scheduler hook, runtime wiring).

View File

@ -4,8 +4,5 @@ add_library(kom_dal STATIC
target_compile_features(kom_dal PUBLIC cxx_std_20)
target_include_directories(kom_dal PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
if (KOM_HAVE_PG)
target_compile_definitions(kom_dal PUBLIC HAVE_PG)
target_link_libraries(kom_dal PUBLIC pqxx::pqxx PostgreSQL::PostgreSQL)
endif()
target_link_libraries(kom_dal PUBLIC Qt6::Core Qt6::Sql)
target_compile_options(kom_dal PRIVATE -fexceptions)

File diff suppressed because it is too large Load Diff

View File

@ -2,19 +2,16 @@
#include "IDatabase.hpp"
#include <QSqlDatabase>
#include <QString>
#include <chrono>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#ifdef HAVE_PG
#include <pqxx/pqxx>
#endif
namespace kom {
struct NamespaceRow {
@ -27,11 +24,11 @@ struct ItemRow {
std::string namespace_id;
std::optional<std::string> key;
std::string content_json;
std::string metadata_json = "{}";
std::optional<std::string> text;
std::string metadata_json;
std::vector<std::string> tags;
int revision = 1;
std::chrono::system_clock::time_point created_at{};
std::chrono::system_clock::time_point created_at;
std::optional<std::chrono::system_clock::time_point> expires_at;
};
@ -79,9 +76,9 @@ public:
const std::optional<std::string>& key,
const std::vector<std::string>& tags,
const std::optional<std::string>& sinceIso,
int limit) const;
int limit);
// IDatabase overrides (stubbed for now to operate on the in-memory backing store).
// IDatabase overrides
std::pair<std::string, int> upsertItem(
const std::string& namespace_id,
const std::optional<std::string>& key,
@ -99,40 +96,61 @@ public:
int k) override;
private:
std::string allocateId(std::size_t& counter, const std::string& prefix);
static std::string toLower(const std::string& value);
static std::string toIso8601(std::chrono::system_clock::time_point tp);
static bool includesAllTags(const std::vector<std::string>& haystack,
const std::vector<std::string>& needles);
bool isExpired(const ItemRow& row,
std::chrono::system_clock::time_point now) const;
void resetInMemoryStore();
static std::string toPgArrayLiteral(const std::vector<std::string>& values);
static std::string escapePgArrayElement(const std::string& value);
static std::string toPgVectorLiteral(const std::vector<float>& values);
#ifdef HAVE_PG
void prepareStatements();
NamespaceRow mapNamespaceRow(const pqxx::row& row) const;
ItemRow mapItemRow(const pqxx::row& row) const;
std::vector<std::string> parseTextArrayField(const pqxx::field& field) const;
std::optional<NamespaceRow> pgEnsureNamespace(const std::string& name);
std::optional<NamespaceRow> pgFindNamespace(const std::string& name) const;
std::pair<std::string, int> pgUpsertItem(const ItemRow& row);
std::vector<std::string> pgUpsertChunks(const std::vector<ChunkRow>& chunks);
void pgUpsertEmbeddings(const std::vector<EmbeddingRow>& embeddings);
std::vector<ItemRow> pgSearchText(const std::string& namespaceId,
const std::string& query,
int limit);
std::vector<std::pair<std::string, float>> pgSearchVector(
struct ConnectionConfig {
QString host;
int port = 5432;
QString dbname;
QString user;
QString password;
bool useSocket = false;
QString socketPath;
QString options;
};
bool hasDatabase() const;
bool openDatabase(const std::string& dsn);
void closeDatabase();
QSqlDatabase database() const;
ConnectionConfig parseDsn(const std::string& dsn) const;
NamespaceRow sqlEnsureNamespace(const std::string& name);
std::optional<NamespaceRow> sqlFindNamespace(const std::string& name) const;
std::pair<std::string, int> sqlUpsertItem(const ItemRow& row);
std::vector<std::string> sqlUpsertChunks(const std::vector<ChunkRow>& chunks);
void sqlUpsertEmbeddings(const std::vector<EmbeddingRow>& embeddings);
std::vector<ItemRow> sqlSearchText(const std::string& namespaceId,
const std::string& query,
int limit) const;
std::vector<std::pair<std::string, float>> sqlSearchVector(
const std::string& namespaceId,
const std::vector<float>& embedding,
int limit);
std::optional<ItemRow> pgGetItemById(const std::string& id) const;
#endif
int limit) const;
std::optional<ItemRow> sqlGetItemById(const std::string& id) const;
std::vector<ItemRow> sqlFetchContext(const std::string& namespaceId,
const std::optional<std::string>& key,
const std::vector<std::string>& tags,
const std::optional<std::string>& sinceIso,
int limit) const;
std::vector<std::string> sqlHybridSearch(const std::vector<float>& query_vec,
const std::string& model,
const std::string& namespace_id,
const std::string& query_text,
int k);
std::string allocateId(std::size_t& counter, const std::string& prefix);
static std::string toLower(const std::string& value);
static bool isStubDsn(const std::string& dsn);
static std::string escapePgArrayElement(const std::string& value);
static std::string toPgArrayLiteral(const std::vector<std::string>& values);
static std::string toPgVectorLiteral(const std::vector<float>& values);
static std::vector<std::string> parsePgTextArray(const QString& value);
bool connected_ = false;
bool useInMemory_ = true;
std::string dsn_;
QString connectionName_;
bool transactionActive_ = false;
std::size_t nextNamespaceId_ = 1;
std::size_t nextItemId_ = 1;
@ -146,12 +164,6 @@ private:
std::unordered_map<std::string, ChunkRow> chunks_;
std::unordered_map<std::string, std::vector<std::string>> chunksByItem_;
std::unordered_map<std::string, EmbeddingRow> embeddings_;
#ifdef HAVE_PG
std::unique_ptr<pqxx::connection> connection_;
mutable std::unique_ptr<pqxx::work> activeTx_;
bool statementsPrepared_ = false;
#endif
};
} // namespace kom

View File

@ -10,6 +10,10 @@
#ifdef HAVE_KCONFIG
#include <KConfigGroup>
#include <KSharedConfig>
#else
#include <QDir>
#include <QSettings>
#include <QStandardPaths>
#endif
#include "mcp/KomMcpServer.hpp"
@ -62,8 +66,21 @@ std::optional<std::string> read_dsn_from_config() {
return dsn.toStdString();
}
#else
QString configFilePath() {
QString base = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation);
if (base.isEmpty()) {
base = QDir::homePath();
}
return QDir(base).filePath(QStringLiteral("kompanionrc"));
}
std::optional<std::string> read_dsn_from_config() {
return std::nullopt;
QSettings settings(configFilePath(), QSettings::IniFormat);
const QString dsn = settings.value(QStringLiteral("Database/PgDsn")).toString();
if (dsn.isEmpty()) {
return std::nullopt;
}
return dsn.toStdString();
}
#endif

View File

@ -33,7 +33,7 @@ MCP backend for Kompanion: memory/context/embedding provider over MCP, built fro
| #18 | ⬜ todo | 492 | **Expose Agentic-Control-Framework as a tool** | Wrap ACF endpoints into a too... |
| #19 | ⬜ todo | 509 | **DAL skeleton + SQL calls (pgvector)** | Create DAL interfaces and pgv... |
| #20 | ⬜ todo | 491 | **Claude Code integration rescue plan** | Stabilize Qwen-2.5-Coder insi... |
| #21 | ⬜ todo | 510 | **DAL Phase 1: libpq/pqxx wiring + SQL calls** | Link pqxx, implement PgDal ag... |
| #21 | ⬜ todo | 510 | **DAL Phase 1: Qt6/QSql wiring + SQL calls** | Use QPSQL via Qt6::Sql, implement PgDal ag... |
| #22 | ⬜ todo | 490 | **Handlers → DAL integration** | Wire kom.memory.v1.upsert_mem... |
| #23 | ⬜ todo | 511 | **Contract tests: DAL-backed tools** | Expand CTest to cover DAL-bac... |
@ -48,8 +48,8 @@ MCP backend for Kompanion: memory/context/embedding provider over MCP, built fro
| ID | Status | Title |
|:--:|:------:|:------|
| #21.1 | ⬜ todo | CMake: find_package(pqxx) and link; CI env var DSN |
| #21.2 | ⬜ todo | PgDal: implement connect/tx + prepared statements |
| #21.1 | ⬜ todo | CMake: require Qt6::Sql (QPSQL); CI env var DSN |
| #21.2 | ⬜ todo | PgDal: implement QSql connect/tx + prepared statements |
| #21.3 | ⬜ todo | SQL: ensureNamespace, upsertItem/Chunks/Embeddings |
| #21.4 | ⬜ todo | Search: FTS/trgm + vector <-> with filters (namespace/thread/tags) |
@ -60,4 +60,3 @@ MCP backend for Kompanion: memory/context/embedding provider over MCP, built fro
| #22.1 | ⬜ todo | Replace ad-hoc JSON with parser (nlohmann/json or simdjson) |
| #22.2 | ⬜ todo | Validate request bodies against schemas before DAL calls |
| #22.3 | ⬜ todo | Scope & sensitivity enforcement (namespace/user + skip secret embeddings) |

View File

@ -3,6 +3,7 @@ add_executable(test_mcp_tools
)
target_include_directories(test_mcp_tools PRIVATE ${PROJECT_SOURCE_DIR}/src)
target_link_libraries(test_mcp_tools PRIVATE kom_dal)
target_compile_options(test_mcp_tools PRIVATE -fexceptions)
add_test(NAME contract_mcp_tools COMMAND test_mcp_tools)
@ -11,6 +12,7 @@ add_executable(contract_memory
)
target_include_directories(contract_memory PRIVATE ${PROJECT_SOURCE_DIR}/src)
target_link_libraries(contract_memory PRIVATE kom_dal)
target_compile_options(contract_memory PRIVATE -fexceptions)
add_test(NAME contract_memory COMMAND contract_memory)
@ -19,5 +21,6 @@ add_executable(test_memory_exchange
)
target_include_directories(test_memory_exchange PRIVATE ${PROJECT_SOURCE_DIR}/src)
target_link_libraries(test_memory_exchange PRIVATE kom_dal)
target_compile_options(test_memory_exchange PRIVATE -fexceptions)
add_test(NAME mcp_memory_exchange COMMAND test_memory_exchange)