Step 155: add agent code generation

This commit is contained in:
Bill
2026-02-09 19:21:17 -07:00
parent cc186f628f
commit 7340d5028a
6 changed files with 191 additions and 1 deletions

86
editor/src/AgentCodeGen.h Normal file
View File

@@ -0,0 +1,86 @@
#pragma once
#include "PrimitivesRegistry.h"
#include "ast/Function.h"
#include "ast/Parameter.h"
#include "ast/Statement.h"
#include "ast/Expression.h"
#include <string>
#include <vector>
#include <algorithm>
struct AgentCodeGenResult {
ASTNode* node = nullptr;
std::string note;
std::vector<std::string> usedSymbols;
};
class AgentCodeGen {
public:
AgentCodeGenResult generate(const std::string& spec,
PrimitivesRegistry& registry,
const std::string& language,
bool preferImports) const {
AgentCodeGenResult result;
auto functions = registry.getAvailableFunctions();
if (functions.empty()) {
result.note = "No available functions; returning empty stub.";
result.node = makeEmptyFunction("generated");
return result;
}
PrimitiveSymbol chosen = chooseFunction(functions, preferImports);
result.usedSymbols.push_back(chosen.name);
auto* fn = new Function(nextId("fn"), "generated");
auto* param = new Parameter(nextId("param"), "input");
fn->addChild("parameters", param);
auto* call = new FunctionCall();
call->id = nextId("call");
call->functionName = chosen.name;
call->addChild("arguments", new VariableReference(nextId("var"), "input"));
auto* stmt = new ExpressionStatement();
stmt->id = nextId("exprstmt");
stmt->setChild("expression", call);
fn->addChild("body", stmt);
result.node = fn;
result.note = buildNote(spec, chosen, language);
return result;
}
private:
static PrimitiveSymbol chooseFunction(const std::vector<PrimitiveSymbol>& funcs,
bool preferImports) {
if (preferImports) {
for (const auto& sym : funcs) {
if (sym.source == "import") return sym;
}
}
for (const auto& sym : funcs) {
if (sym.source == "builtin") return sym;
}
return funcs.front();
}
static Function* makeEmptyFunction(const std::string& name) {
auto* fn = new Function(nextId("fn"), name);
return fn;
}
static std::string buildNote(const std::string& spec,
const PrimitiveSymbol& sym,
const std::string& language) {
std::string note = "Generated using " + sym.name + " (" + sym.source + ")";
if (!spec.empty()) note += " for spec: " + spec;
if (!language.empty()) note += " in " + language;
return note;
}
static std::string nextId(const std::string& prefix) {
static int counter = 0;
return prefix + "_" + std::to_string(counter++);
}
};

View File

@@ -49,6 +49,7 @@
#include "ImportManager.h"
#include "PrimitivesRegistry.h"
#include "AgentLibraryPolicy.h"
#include "AgentCodeGen.h"
#include "CompositionPanel.h"
#include "IncrementalOptimizer.h"
#include "EmacsIntegration.h"
@@ -1124,6 +1125,42 @@ struct EditorState {
return response;
}
if (method == "generateCode") {
if (!active() || !isStructured()) {
response["error"] = {{"code", -32000}, {"message", "No structured buffer"}};
return response;
}
Module* ast = activeAST();
if (!ast) {
response["error"] = {{"code", -32001}, {"message", "AST unavailable"}};
return response;
}
auto params = request.contains("params") ? request["params"] : json::object();
std::string spec = params.value("spec", "");
bool preferImports = params.value("preferImports", true);
primitives.setRoot(ast);
primitives.setLanguage(active()->language);
AgentCodeGen gen;
AgentCodeGenResult genRes = gen.generate(spec, primitives, active()->language, preferImports);
if (!genRes.node) {
response["error"] = {{"code", -32020}, {"message", "Code generation failed"}};
return response;
}
json nodeJson = toJson(genRes.node);
deleteTree(genRes.node);
response["result"] = {
{"node", nodeJson},
{"note", genRes.note},
{"usedSymbols", genRes.usedSymbols},
{"language", active()->language}
};
return response;
}
if (method == "applyMutation") {
auto permIt = agentMutationPermissions.find(sessionId);
if (permIt == agentMutationPermissions.end() || !permIt->second) {