Files
whetstone_DSL/editor/src/headless_rpc/DispatchPart4.h

486 lines
23 KiB
C++

if (method == "saveAllBuffers") {
if (!AgentPermissionPolicy::canInvoke(role, method))
return headlessRpcError(id, -32031, "Role not permitted");
json savedPaths = json::array();
int savedCount = 0;
int skippedCount = 0;
for (auto& [path, buf] : state.bufferStates) {
if (!buf->modified) {
++skippedCount;
continue;
}
std::string writePath = path;
if (!state.workspaceRoot.empty()) {
auto [ok, resolved] =
fileOpsResolvePath(state.workspaceRoot, path);
if (!ok) {
++skippedCount;
continue;
}
writePath = resolved;
}
fs::path parentDir = fs::path(writePath).parent_path();
if (!parentDir.empty()) {
std::error_code ec;
fs::create_directories(parentDir, ec);
}
auto [success, msg, bytes] =
fileOpsWrite(writePath, buf->editBuf);
if (success) {
buf->modified = false;
savedPaths.push_back(writePath);
++savedCount;
} else {
++skippedCount;
}
}
return headlessRpcResult(id, {
{"savedCount", savedCount},
{"skippedCount", skippedCount},
{"saved", savedPaths}
});
}
// --- batchQuery ---
if (method == "batchQuery") {
auto params = request.contains("params") ? request["params"]
: json::object();
if (!params.contains("queries") || !params["queries"].is_array())
return headlessRpcError(id, -32602, "Missing queries array");
json results = json::array();
for (const auto& q : params["queries"]) {
std::string subMethod = q.value("method", "");
json subParams = q.contains("params") ? q["params"]
: json::object();
json subReq = {{"jsonrpc", "2.0"}, {"id", 1},
{"method", subMethod}, {"params", subParams}};
json subResp = handleHeadlessAgentRequest(
state, subReq, sessionId);
// Extract result or error from the sub-response
if (subResp.contains("result"))
results.push_back({{"method", subMethod},
{"result", subResp["result"]}});
else if (subResp.contains("error"))
results.push_back({{"method", subMethod},
{"error", subResp["error"]}});
else
results.push_back({{"method", subMethod},
{"error", "Unknown response"}});
}
return headlessRpcResult(id,
{{"results", results}, {"count", (int)results.size()}});
}
// --- saveAnnotatedAST ---
if (method == "saveAnnotatedAST") {
if (!AgentPermissionPolicy::canInvoke(role, method))
return headlessRpcError(id, -32031, "Role not permitted");
auto params = request.contains("params") ? request["params"]
: json::object();
std::string path = params.value("path", "");
if (path.empty() && state.activeBuffer)
path = state.activeBuffer->path;
auto it = state.bufferStates.find(path);
if (it == state.bufferStates.end())
return headlessRpcError(id, -32602, "No buffer: " + path);
Module* ast = it->second->sync.getAST();
auto res = saveSidecarAST(state.workspaceRoot, path, ast);
if (!res.success)
return headlessRpcError(id, -32010, res.error);
return headlessRpcResult(id,
{{"success", true},
{"sidecarPath", res.sidecarPath},
{"annotationCount", res.annotationCount}});
}
// --- loadAnnotatedAST ---
if (method == "loadAnnotatedAST") {
if (!AgentPermissionPolicy::canInvoke(role, method))
return headlessRpcError(id, -32031, "Role not permitted");
auto params = request.contains("params") ? request["params"]
: json::object();
std::string path = params.value("path", "");
if (path.empty() && state.activeBuffer)
path = state.activeBuffer->path;
auto it = state.bufferStates.find(path);
if (it == state.bufferStates.end())
return headlessRpcError(id, -32602, "No buffer: " + path);
Module* ast = it->second->sync.getAST();
auto res = loadSidecarAST(state.workspaceRoot, path, ast);
if (!res.success)
return headlessRpcError(id, -32010, res.error);
return headlessRpcResult(id,
{{"success", true},
{"mergedCount", res.mergedCount},
{"staleCount", res.staleCount}});
}
// --- listAnnotatedFiles ---
if (method == "listAnnotatedFiles") {
if (!AgentPermissionPolicy::canInvoke(role, method))
return headlessRpcError(id, -32031, "Role not permitted");
auto files = listSidecarFiles(state.workspaceRoot);
return headlessRpcResult(id,
{{"files", files}, {"count", (int)files.size()}});
}
// --- setSemanticAnnotation ---
if (method == "setSemanticAnnotation") {
if (!AgentPermissionPolicy::canInvoke(role, method))
return headlessRpcError(id, -32031, "Role not permitted");
auto params = request.contains("params") ? request["params"]
: json::object();
std::string nodeId = params.value("nodeId", "");
std::string type = params.value("type", "");
json fields = params.contains("fields") ? params["fields"]
: json::object();
if (nodeId.empty() || type.empty())
return headlessRpcError(id, -32602, "nodeId and type required");
if (!state.activeBuffer)
return headlessRpcError(id, -32602, "No active buffer");
Module* ast = state.activeAST();
if (!ast)
return headlessRpcError(id, -32602, "No AST available");
ASTNode* target = findNodeById(ast, nodeId);
if (!target)
return headlessRpcError(id, -32602, "Node not found: " + nodeId);
// Map type string to conceptType
std::string conceptType;
if (type == "intent") conceptType = "IntentAnnotation";
else if (type == "complexity") conceptType = "ComplexityAnnotation";
else if (type == "risk") conceptType = "RiskAnnotation";
else if (type == "contract") conceptType = "ContractAnnotation";
else if (type == "tags") conceptType = "SemanticTagAnnotation";
// Type System (Steps 272-273)
else if (type == "bitWidth") conceptType = "BitWidthAnnotation";
else if (type == "endian") conceptType = "EndianAnnotation";
else if (type == "layout") conceptType = "LayoutAnnotation";
else if (type == "nullability") conceptType = "NullabilityAnnotation";
else if (type == "variance") conceptType = "VarianceAnnotation";
else if (type == "identity") conceptType = "IdentityAnnotation";
else if (type == "mut") conceptType = "MutAnnotation";
else if (type == "typeState") conceptType = "TypeStateAnnotation";
// Concurrency (Step 274)
else if (type == "atomic") conceptType = "AtomicAnnotation";
else if (type == "sync") conceptType = "SyncAnnotation";
else if (type == "threadModel") conceptType = "ThreadModelAnnotation";
else if (type == "memoryBarrier") conceptType = "MemoryBarrierAnnotation";
// Async / Error Handling (Step 275)
else if (type == "exec") conceptType = "ExecAnnotation";
else if (type == "blocking") conceptType = "BlockingAnnotation";
else if (type == "parallel") conceptType = "ParallelAnnotation";
else if (type == "trap") conceptType = "TrapAnnotation";
else if (type == "exception") conceptType = "ExceptionAnnotation";
else if (type == "panic") conceptType = "PanicAnnotation";
// Scope & Namespace (Step 276)
else if (type == "binding") conceptType = "BindingAnnotation";
else if (type == "lookup") conceptType = "LookupAnnotation";
else if (type == "capture") conceptType = "CaptureAnnotation";
else if (type == "visibility") conceptType = "VisibilityAnnotation";
else if (type == "namespace") conceptType = "NamespaceAnnotation";
else if (type == "scope") conceptType = "ScopeAnnotation";
// Shim & Escape Hatch (Step 278)
else if (type == "intrinsic") conceptType = "IntrinsicAnnotation";
else if (type == "raw") conceptType = "RawAnnotation";
else if (type == "callingConv") conceptType = "CallingConvAnnotation";
else if (type == "link") conceptType = "LinkAnnotation";
else if (type == "shim") conceptType = "ShimAnnotation";
else if (type == "pointerArithmetic") conceptType = "PointerArithmeticAnnotation";
else if (type == "opaque") conceptType = "OpaqueAnnotation";
// Platform & Provenance (Step 279)
else if (type == "target") conceptType = "TargetAnnotation";
else if (type == "feature") conceptType = "FeatureAnnotation";
else if (type == "original") conceptType = "OriginalAnnotation";
else if (type == "mapping") conceptType = "MappingAnnotation";
// Optimization Completion (Step 280)
else if (type == "tailCall") conceptType = "TailCallAnnotation";
else if (type == "loop") conceptType = "LoopAnnotation";
else if (type == "dataHint") conceptType = "DataAnnotation";
else if (type == "align") conceptType = "AlignAnnotation";
else if (type == "pack") conceptType = "PackAnnotation";
else if (type == "boundsCheck") conceptType = "BoundsCheckAnnotation";
else if (type == "overflow") conceptType = "OverflowAnnotation";
// Meta-Programming (Step 281)
else if (type == "meta") conceptType = "MetaAnnotation";
else if (type == "symbolMode") conceptType = "SymbolAnnotation";
else if (type == "evaluate") conceptType = "EvaluateAnnotation";
else if (type == "template") conceptType = "TemplateAnnotation";
else if (type == "synthetic") conceptType = "SyntheticAnnotation";
// Strategy & Policy (Step 282)
else if (type == "policy") conceptType = "PolicyAnnotation";
else if (type == "ambiguity") conceptType = "AmbiguityAnnotation";
else if (type == "candidate") conceptType = "CandidateAnnotation";
else if (type == "tradeoff") conceptType = "TradeoffAnnotation";
else if (type == "choice") conceptType = "ChoiceAnnotation";
else if (type == "decision") conceptType = "DecisionAnnotation";
// Subject 9: Workflow Routing (Step 315)
else if (type == "contextWidth") conceptType = "ContextWidthAnnotation";
else if (type == "review") conceptType = "ReviewAnnotation";
else if (type == "automatability") conceptType = "AutomatabilityAnnotation";
else if (type == "priority") conceptType = "PriorityAnnotation";
else if (type == "implementationStatus") conceptType = "ImplementationStatusAnnotation";
// Environment Layer (Step 285)
else if (type == "capabilityRequirement") conceptType = "CapabilityRequirement";
else return headlessRpcError(id, -32602, "Unknown annotation type: " + type);
// Remove existing annotation of same type (update semantics)
auto annos = target->getChildren("annotations");
for (auto* a : annos) {
if (a->conceptType == conceptType) {
target->removeChild(a);
break;
}
}
// Create new annotation node
json nodeJson = {{"concept", conceptType}, {"properties", fields}};
ASTNode* newAnno = fromJson(nodeJson);
if (!newAnno)
return headlessRpcError(id, -32010, "Failed to create annotation");
target->addChild("annotations", newAnno);
return headlessRpcResult(id, {{"success", true}, {"type", type}});
}
// --- getSemanticAnnotations ---
if (method == "getSemanticAnnotations") {
if (!AgentPermissionPolicy::canInvoke(role, method))
return headlessRpcError(id, -32031, "Role not permitted");
auto params = request.contains("params") ? request["params"]
: json::object();
std::string nodeId = params.value("nodeId", "");
if (!state.activeBuffer)
return headlessRpcError(id, -32602, "No active buffer");
Module* ast = state.activeAST();
if (!ast)
return headlessRpcError(id, -32602, "No AST available");
if (!nodeId.empty()) {
// Return annotations for a specific node
ASTNode* target = findNodeById(ast, nodeId);
if (!target)
return headlessRpcError(id, -32602, "Node not found: " + nodeId);
json annos = json::array();
for (auto* a : target->getChildren("annotations")) {
if (!isSemanticAnnotation(a->conceptType)) continue;
json entry;
if (a->conceptType == "IntentAnnotation") {
auto* ia = static_cast<IntentAnnotation*>(a);
entry = {{"type", "intent"}, {"summary", ia->summary},
{"category", ia->category}};
} else if (a->conceptType == "ComplexityAnnotation") {
auto* ca = static_cast<ComplexityAnnotation*>(a);
entry = {{"type", "complexity"},
{"timeComplexity", ca->timeComplexity},
{"cognitiveComplexity", ca->cognitiveComplexity},
{"linesOfLogic", ca->linesOfLogic}};
} else if (a->conceptType == "RiskAnnotation") {
auto* ra = static_cast<RiskAnnotation*>(a);
entry = {{"type", "risk"}, {"level", ra->level},
{"reason", ra->reason},
{"dependentCount", ra->dependentCount}};
} else if (a->conceptType == "ContractAnnotation") {
auto* ca = static_cast<ContractAnnotation*>(a);
entry = {{"type", "contract"},
{"preconditions", ca->preconditions},
{"postconditions", ca->postconditions},
{"returnShape", ca->returnShape},
{"sideEffects", ca->sideEffects}};
} else if (a->conceptType == "SemanticTagAnnotation") {
auto* ta = static_cast<SemanticTagAnnotation*>(a);
entry = {{"type", "tags"}, {"tags", ta->tags}};
} else {
// Generic fallback for all other semantic annotations
entry = {{"type", a->conceptType}};
json props = propertiesToJson(a);
for (auto& [k, v] : props.items())
entry[k] = v;
}
if (!entry.empty()) annos.push_back(entry);
}
return headlessRpcResult(id,
{{"nodeId", nodeId}, {"annotations", annos}});
} else {
// Return all annotated nodes
json nodes = json::array();
for (auto* child : ast->allChildren()) {
auto annos = child->getChildren("annotations");
json annoList = json::array();
for (auto* a : annos) {
if (!isSemanticAnnotation(a->conceptType)) continue;
json entry;
if (a->conceptType == "IntentAnnotation") {
auto* ia = static_cast<IntentAnnotation*>(a);
entry = {{"type", "intent"}, {"summary", ia->summary}};
} else if (a->conceptType == "ComplexityAnnotation") {
entry = {{"type", "complexity"}};
} else if (a->conceptType == "RiskAnnotation") {
auto* ra = static_cast<RiskAnnotation*>(a);
entry = {{"type", "risk"}, {"level", ra->level}};
} else if (a->conceptType == "ContractAnnotation") {
entry = {{"type", "contract"}};
} else if (a->conceptType == "SemanticTagAnnotation") {
auto* ta = static_cast<SemanticTagAnnotation*>(a);
entry = {{"type", "tags"}, {"tags", ta->tags}};
} else {
entry = {{"type", a->conceptType}};
json props = propertiesToJson(a);
for (auto& [k, v] : props.items())
entry[k] = v;
}
if (!entry.empty()) annoList.push_back(entry);
}
if (!annoList.empty()) {
nodes.push_back({
{"nodeId", child->id},
{"name", getNodeName(child)},
{"kind", child->conceptType},
{"annotations", annoList}
});
}
}
return headlessRpcResult(id, {{"nodes", nodes}});
}
}
// --- removeSemanticAnnotation ---
if (method == "removeSemanticAnnotation") {
if (!AgentPermissionPolicy::canInvoke(role, method))
return headlessRpcError(id, -32031, "Role not permitted");
auto params = request.contains("params") ? request["params"]
: json::object();
std::string nodeId = params.value("nodeId", "");
std::string type = params.value("type", "");
if (nodeId.empty() || type.empty())
return headlessRpcError(id, -32602, "nodeId and type required");
if (!state.activeBuffer)
return headlessRpcError(id, -32602, "No active buffer");
Module* ast = state.activeAST();
if (!ast)
return headlessRpcError(id, -32602, "No AST available");
ASTNode* target = findNodeById(ast, nodeId);
if (!target)
return headlessRpcError(id, -32602, "Node not found: " + nodeId);
std::string conceptType;
if (type == "intent") conceptType = "IntentAnnotation";
else if (type == "complexity") conceptType = "ComplexityAnnotation";
else if (type == "risk") conceptType = "RiskAnnotation";
else if (type == "contract") conceptType = "ContractAnnotation";
else if (type == "tags") conceptType = "SemanticTagAnnotation";
else return headlessRpcError(id, -32602, "Unknown type: " + type);
bool removed = false;
for (auto* a : target->getChildren("annotations")) {
if (a->conceptType == conceptType) {
target->removeChild(a);
removed = true;
break;
}
}
return headlessRpcResult(id, {{"removed", removed}, {"type", type}});
}
// --- getUnannotatedNodes ---
if (method == "getUnannotatedNodes") {
if (!AgentPermissionPolicy::canInvoke(role, method))
return headlessRpcError(id, -32031, "Role not permitted");
auto params = request.contains("params") ? request["params"]
: json::object();
std::string filterType = params.value("type", "");
bool includeHints = params.value("includeHints", false);
if (!state.activeBuffer)
return headlessRpcError(id, -32602, "No active buffer");
Module* ast = state.activeAST();
if (!ast)
return headlessRpcError(id, -32602, "No AST available");
// Map filter type to conceptType
std::string filterConcept;
if (filterType == "intent") filterConcept = "IntentAnnotation";
else if (filterType == "complexity") filterConcept = "ComplexityAnnotation";
else if (filterType == "risk") filterConcept = "RiskAnnotation";
else if (filterType == "contract") filterConcept = "ContractAnnotation";
else if (filterType == "tags") filterConcept = "SemanticTagAnnotation";
json nodes = json::array();
for (auto* child : ast->allChildren()) {
// Only report Function and Variable nodes
if (child->conceptType != "Function" &&
child->conceptType != "Variable")
continue;
auto annos = child->getChildren("annotations");
bool hasTarget = false;
if (filterConcept.empty()) {
// Check for any semantic annotation
for (auto* a : annos) {
if (isSemanticAnnotation(a->conceptType)) {
hasTarget = true;
break;
}
}
} else {
for (auto* a : annos) {
if (a->conceptType == filterConcept) {
hasTarget = true;
break;
}
}
}
if (!hasTarget) {
json entry = {
{"nodeId", child->id},
{"name", getNodeName(child)},
{"kind", child->conceptType}
};
// Add static analysis hints if requested
if (includeHints) {
json hints;
// Body statement count
auto body = child->getChildren("body");
hints["bodyStatements"] = (int)body.size();
// Side effect heuristic: check for io/network calls
bool hasSideEffects = false;
// Check body statements and their descendants
std::vector<const ASTNode*> toCheck;
for (auto* stmt : body) {
toCheck.push_back(stmt);
for (auto* desc : stmt->allChildren())
toCheck.push_back(desc);
}
for (auto* node : toCheck) {
if (node->conceptType == "FunctionCall" ||
node->conceptType == "MethodCall") {
std::string callee = getNodeName(node);
if (callee.find("print") != std::string::npos ||
callee.find("write") != std::string::npos ||
callee.find("send") != std::string::npos ||
callee.find("read") != std::string::npos ||
callee.find("open") != std::string::npos)
hasSideEffects = true;
}
}
hints["sideEffectHint"] = hasSideEffects
? "possible" : "none";
entry["hints"] = hints;
}
nodes.push_back(entry);
}
}
return headlessRpcResult(id,
{{"nodes", nodes}, {"count", (int)nodes.size()}});
}
// --- setEnvironment ---