From cc186f628fc29467b6045b558385ba50739cf219 Mon Sep 17 00:00:00 2001 From: Bill Date: Mon, 9 Feb 2026 19:18:26 -0700 Subject: [PATCH] Step 154: send library context to agents --- PROGRESS.md | 1 + editor/CMakeLists.txt | 4 +++ editor/src/WebSocketServer.h | 57 ++++++++++++++++++++++++++++++++ editor/tests/step154_test.cpp | 61 +++++++++++++++++++++++++++++++++++ sprint5_plan.md | 2 +- 5 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 editor/tests/step154_test.cpp diff --git a/PROGRESS.md b/PROGRESS.md index 88611d6..a7b55de 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -501,3 +501,4 @@ Sprint 5 in progress. Step 141 (Emacs daemon with user config) done. Next: Step | 2026-02-10 | Codex | Step 151: Go CST-to-AST parsing + generator with receivers, imports, and Go-style control flow. 8/8 tests pass. | | 2026-02-10 | Codex | Step 152: Cross-language projection extended for new languages with annotation adaptation and richer node cloning. 6/6 tests pass. | | 2026-02-10 | Codex | Step 153: Language coverage integration tests across all supported languages + full projection matrix. 208/208 tests pass. | +| 2026-02-10 | Codex | Step 154: Agent library context payload sent on connect with primitives snapshot. 4/4 tests pass. | diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index 0552115..a8699aa 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -872,6 +872,10 @@ target_link_libraries(step153_test PRIVATE tree_sitter_rust tree_sitter_go) +add_executable(step154_test tests/step154_test.cpp) +target_include_directories(step154_test PRIVATE src) +target_link_libraries(step154_test PRIVATE nlohmann_json::nlohmann_json) + find_package(SDL2 CONFIG REQUIRED) find_package(OpenGL REQUIRED) find_package(glad CONFIG REQUIRED) diff --git a/editor/src/WebSocketServer.h b/editor/src/WebSocketServer.h index f94ed32..87efe57 100644 --- a/editor/src/WebSocketServer.h +++ b/editor/src/WebSocketServer.h @@ -19,8 +19,12 @@ #include #include #include +#include +#include #include +#include "PrimitivesRegistry.h" +#include "ast/Module.h" using json = nlohmann::json; // --------------------------------------------------------------------------- @@ -44,6 +48,8 @@ using RequestLogCallback = std::function; +using LibraryContextProvider = + std::function; // --------------------------------------------------------------------------- // WebSocketTransport — abstract transport interface @@ -89,6 +95,42 @@ protected: // --------------------------------------------------------------------------- class WebSocketAgentServer { public: + struct LibraryContext { + json data; + static LibraryContext fromRegistry(PrimitivesRegistry& registry, + Module* root, + const std::string& language) { + LibraryContext ctx; + registry.setRoot(root); + registry.setLanguage(language); + + json funcs = json::array(); + json types = json::array(); + json consts = json::array(); + + auto addSymbol = [](json& arr, const PrimitiveSymbol& sym) { + arr.push_back({ + {"name", sym.name}, + {"kind", sym.kind}, + {"source", sym.source}, + {"library", sym.library} + }); + }; + + for (const auto& sym : registry.getAvailableFunctions()) addSymbol(funcs, sym); + for (const auto& sym : registry.getAvailableTypes()) addSymbol(types, sym); + for (const auto& sym : registry.getAvailableConstants()) addSymbol(consts, sym); + + ctx.data = { + {"language", language}, + {"functions", funcs}, + {"types", types}, + {"constants", consts} + }; + return ctx; + } + }; + explicit WebSocketAgentServer(std::unique_ptr transport) : transport_(std::move(transport)) { @@ -128,6 +170,10 @@ public: requestLogCallback_ = std::move(cb); } + void setLibraryContextProvider(LibraryContextProvider cb) { + libraryContextProvider_ = std::move(cb); + } + // --- session queries --- const AgentSession* getSession(const std::string& sessionId) const { @@ -164,6 +210,16 @@ private: if (sessionEventCallback_) sessionEventCallback_(s, "connected"); + + if (libraryContextProvider_) { + json payload = libraryContextProvider_(sessionId); + json message = { + {"jsonrpc", "2.0"}, + {"method", "libraryContext"}, + {"params", payload} + }; + transport_->sendMessage(sessionId, message.dump()); + } } void onDisconnect(const std::string& sessionId) { @@ -304,6 +360,7 @@ private: JsonRpcHandler requestHandler_; SessionEventCallback sessionEventCallback_; RequestLogCallback requestLogCallback_; + LibraryContextProvider libraryContextProvider_; int port_ = 0; }; diff --git a/editor/tests/step154_test.cpp b/editor/tests/step154_test.cpp new file mode 100644 index 0000000..1d91d1f --- /dev/null +++ b/editor/tests/step154_test.cpp @@ -0,0 +1,61 @@ +// Step 154 TDD Test: Agent library context payload +#include "WebSocketServer.h" +#include "PrimitivesRegistry.h" +#include "ast/Module.h" +#include "ast/ExternalModule.h" +#include "ast/TypeSignature.h" +#include + +static void expect(bool cond, const std::string& name, int& passed, int& failed) { + if (cond) { + std::cout << "Test " << (passed + failed + 1) << " PASS: " << name << "\n"; + ++passed; + } else { + std::cout << "Test " << (passed + failed + 1) << " FAIL: " << name << "\n"; + ++failed; + } +} + +int main() { + int passed = 0; + int failed = 0; + + Module mod; + mod.id = "mod_test"; + mod.name = "Sample"; + mod.targetLanguage = "python"; + + auto* ext = new ExternalModule("ext_numpy", "numpy", "python"); + auto* sig = new TypeSignature("sig_array", "array"); + ext->addChild("signatures", sig); + mod.addChild("externalModules", ext); + + PrimitivesRegistry registry; + registry.setRoot(&mod); + registry.setLanguage("python"); + + auto transport = std::make_unique(); + MockWebSocketTransport* raw = transport.get(); + WebSocketAgentServer server(std::move(transport)); + + server.setLibraryContextProvider([&](const std::string&) -> json { + auto ctx = WebSocketAgentServer::LibraryContext::fromRegistry(registry, &mod, "python"); + return ctx.data; + }); + + server.start(8123); + std::string sid = raw->simulateConnect(); + std::string response = raw->getLastSentMessage(sid); + + expect(!response.empty(), "library context sent", passed, failed); + expect(response.find("libraryContext") != std::string::npos, + "library context payload present", passed, failed); + expect(response.find("numpy") != std::string::npos, + "library name included", passed, failed); + expect(response.find("array") != std::string::npos, + "signature included", passed, failed); + + std::cout << "\n=== Step 154 Results: " << passed << " passed, " + << failed << " failed ===\n"; + return failed == 0 ? 0 : 1; +} diff --git a/sprint5_plan.md b/sprint5_plan.md index 85a51cd..ad29493 100644 --- a/sprint5_plan.md +++ b/sprint5_plan.md @@ -287,7 +287,7 @@ adds full semantic support. Make agents truly useful: they understand libraries, prioritize available primitives, and assist with constructive coding. -- [ ] **Step 154: Agent library context** +- [x] **Step 154: Agent library context** When an agent connects via WebSocket, it receives the current `PrimitivesRegistry` state: available libraries, their functions, type signatures. The agent's `getAST` response includes `ExternalModule`