10#include "absl/container/flat_hash_set.h"
11#include "absl/strings/ascii.h"
12#include "absl/strings/str_cat.h"
13#include "absl/strings/str_join.h"
15#include "nlohmann/json.hpp"
27 const std::string lower = absl::AsciiStrToLower(value);
28 return lower ==
"true" || lower ==
"1" || lower ==
"yes" || lower ==
"on";
32 const std::string lower = absl::AsciiStrToLower(value);
33 return lower ==
"false" || lower ==
"0" || lower ==
"no" || lower ==
"off";
38 const ToolDefinition& def,
const std::map<std::string, std::string>& args) {
39 std::vector<std::string> result;
40 const absl::flat_hash_set<std::string> flag_args(def.
flag_args.begin(),
43 for (
const auto& [key, value] : args) {
44 if (flag_args.contains(key)) {
46 result.push_back(absl::StrCat(
"--", key));
52 return absl::InvalidArgumentError(
53 absl::StrCat(
"Flag argument '--", key,
54 "' must use a boolean value when called as a tool"));
57 result.push_back(absl::StrCat(
"--", key,
"=", value));
61 bool has_format =
false;
62 for (
const auto& arg : result) {
63 if (arg.find(
"--format=") == 0) {
69 result.push_back(
"--format=json");
119 return absl::PermissionDeniedError(
121 "' performs a mutating action and is not authorized in "
122 "the current agent configuration"));
125 std::vector<std::string> missing;
127 if (!call.
args.contains(arg)) {
128 missing.push_back(
"--" + arg);
131 if (!missing.empty()) {
132 return absl::InvalidArgumentError(absl::StrCat(
134 "' is missing required arguments: ", absl::StrJoin(missing,
", ")));
137 return absl::OkStatus();
144 nlohmann::json payload;
145 payload[
"tools"] = nlohmann::json::array();
147 payload[
"tools"].push_back(
148 {{
"name", info.name},
149 {
"category", info.category},
150 {
"description", info.description},
151 {
"usage", info.usage},
152 {
"examples", info.examples},
153 {
"requires_rom", info.requires_rom},
154 {
"requires_project", info.requires_project},
155 {
"requires_authorization", info.requires_authorization},
156 {
"required_args", info.required_args},
157 {
"flag_args", info.flag_args}});
159 payload[
"count"] = payload[
"tools"].size();
160 return payload.dump(2);
163 if (call.
tool_name ==
"tools-describe") {
164 const auto it = call.
args.find(
"name");
165 if (it == call.
args.end()) {
166 return absl::InvalidArgumentError(
167 "Tool 'tools-describe' requires --name");
170 if (!info.has_value()) {
171 return absl::NotFoundError(absl::StrCat(
"Tool not found: ", it->second));
174 nlohmann::json payload = {
175 {
"name", info->name},
176 {
"category", info->category},
177 {
"description", info->description},
178 {
"usage", info->usage},
179 {
"examples", info->examples},
180 {
"requires_rom", info->requires_rom},
181 {
"requires_project", info->requires_project},
182 {
"requires_authorization", info->requires_authorization},
183 {
"required_args", info->required_args},
184 {
"flag_args", info->flag_args}};
185 return payload.dump(2);
189 const auto it = call.
args.find(
"query");
190 if (it == call.
args.end()) {
191 return absl::InvalidArgumentError(
"Tool 'tools-search' requires --query");
194 nlohmann::json payload;
195 payload[
"query"] = it->second;
196 payload[
"matches"] = nlohmann::json::array();
198 payload[
"matches"].push_back(
199 {{
"name", info.name},
200 {
"category", info.category},
201 {
"description", info.description},
202 {
"usage", info.usage},
203 {
"requires_rom", info.requires_rom},
204 {
"requires_project", info.requires_project},
205 {
"requires_authorization", info.requires_authorization}});
207 payload[
"count"] = payload[
"matches"].size();
208 return payload.dump(2);
212 return absl::UnimplementedError(
213 absl::StrCat(
"Unhandled meta-tool: ", call.
tool_name));
219 return absl::InvalidArgumentError(
220 absl::StrCat(
"Unknown tool: ", call.
tool_name));
225 return absl::FailedPreconditionError(absl::StrCat(
226 "Tool '", call.
tool_name,
"' disabled by current agent configuration"));
229 absl::Status validation_status =
ValidateCall(tool_def, call);
230 if (!validation_status.ok()) {
231 return validation_status;
240 if (!handler_or.ok()) {
241 return handler_or.status();
243 auto handler = std::move(handler_or.value());
261 auto args_or = ConvertArgsToVector(tool_def, call.
args);
263 return args_or.status();
265 std::vector<std::string> args = std::move(args_or).value();
269 return absl::FailedPreconditionError(
271 "' requires ROM context but none is available"));
276 return absl::FailedPreconditionError(
278 "' requires Project context but none is available"));
282 std::stringstream output_buffer;
283 std::streambuf* old_cout = std::cout.rdbuf(output_buffer.rdbuf());
287 std::cout.rdbuf(old_cout);
293 std::string output = output_buffer.str();
294 if (output.empty()) {
295 return absl::InternalError(
296 absl::StrCat(
"Tool '", call.
tool_name,
"' produced no output"));
304 std::vector<ToolInfo> tools;
307 for (
const auto& def : all_defs) {
309 tools.push_back({def.name, def.category, def.description, def.usage,
310 def.examples, def.requires_rom, def.requires_project,
319 const std::string& tool_name)
const {
328 def->requires_project,
337 const std::string& query)
const {
338 std::vector<ToolInfo> matches;
339 std::string lower_query = absl::AsciiStrToLower(query);
342 for (
const auto& tool : all_tools) {
343 std::string lower_name = absl::AsciiStrToLower(tool.name);
344 std::string lower_desc = absl::AsciiStrToLower(tool.description);
345 std::string lower_category = absl::AsciiStrToLower(tool.category);
347 if (lower_name.find(lower_query) != std::string::npos ||
348 lower_desc.find(lower_query) != std::string::npos ||
349 lower_category.find(lower_query) != std::string::npos) {
350 matches.push_back(tool);
363 auto start_time = std::chrono::high_resolution_clock::now();
367 std::vector<std::future<absl::StatusOr<std::string>>> futures;
368 futures.reserve(batch.
calls.size());
370 for (
const auto& call : batch.
calls) {
371 futures.push_back(std::async(std::launch::async, [
this, &call]() {
377 for (
size_t i = 0; i < futures.size(); ++i) {
378 auto status_or = futures[i].get();
379 if (status_or.ok()) {
380 result.
results[i] = std::move(status_or.value());
381 result.
statuses[i] = absl::OkStatus();
385 result.
statuses[i] = status_or.status();
391 for (
size_t i = 0; i < batch.
calls.size(); ++i) {
393 if (status_or.ok()) {
394 result.
results[i] = std::move(status_or.value());
395 result.
statuses[i] = absl::OkStatus();
399 result.
statuses[i] = status_or.status();
405 auto end_time = std::chrono::high_resolution_clock::now();
407 std::chrono::duration<double, std::milli>(end_time - start_time).count();
std::map< std::string, AsarSymbol > GetSymbolTable() const