* @brief Validates args and forwards to API::CommandRegistry honoring AI safety tags. */
| 2204 | * @brief Validates args and forwards to API::CommandRegistry honoring AI safety tags. |
| 2205 | */ |
| 2206 | QJsonObject AI::ToolDispatcher::executeCommand(const QString& requestedName, |
| 2207 | const QJsonObject& args, |
| 2208 | bool autoConfirmSafe) |
| 2209 | { |
| 2210 | const QString name = canonicalToolName(requestedName); |
| 2211 | Misc::JsonValidator::Limits limits; |
| 2212 | limits.maxFileSize = 1 * 1024 * 1024; |
| 2213 | limits.maxDepth = 32; |
| 2214 | limits.maxArraySize = 1000; |
| 2215 | |
| 2216 | if (!Misc::JsonValidator::validateStructure(QJsonValue(args), limits)) { |
| 2217 | qCWarning(AI::serialStudioAI) << "Tool args validation failed for" << name; |
| 2218 | QJsonObject reply; |
| 2219 | reply[QStringLiteral("ok")] = false; |
| 2220 | reply[QStringLiteral("error")] = QStringLiteral("args_validation_failed"); |
| 2221 | reply[QStringLiteral("repair")] = |
| 2222 | QStringLiteral("The arguments object failed structural validation (too deeply nested, " |
| 2223 | "too many array items, or too large). Call meta.describeCommand{name: " |
| 2224 | "\"%1\"} and rebuild a minimal arguments object that matches the schema.") |
| 2225 | .arg(name); |
| 2226 | return reply; |
| 2227 | } |
| 2228 | |
| 2229 | const auto safety = AI::CommandRegistry::instance().safetyOf(name); |
| 2230 | const bool isDryRunQuery = args.value(QStringLiteral("dryRun")).toBool(false); |
| 2231 | if (safety == Safety::Blocked) |
| 2232 | return makeBlockedReply(name); |
| 2233 | |
| 2234 | if ((safety == Safety::Confirm || safety == Safety::AlwaysConfirm) && !autoConfirmSafe |
| 2235 | && !isDryRunQuery) { |
| 2236 | Q_EMIT confirmationRequested(name, args); |
| 2237 | QJsonObject reply; |
| 2238 | reply[QStringLiteral("ok")] = false; |
| 2239 | reply[QStringLiteral("error")] = QStringLiteral("awaiting_confirmation"); |
| 2240 | reply[QStringLiteral("message")] = |
| 2241 | QStringLiteral("User approval pending. The chat UI is showing an approval card for this " |
| 2242 | "tool call. Do not retry, do not invoke with confirm/force/token params -- " |
| 2243 | "the result will arrive automatically once the user clicks Approve or Deny."); |
| 2244 | reply[QStringLiteral("hint")] = |
| 2245 | QStringLiteral("If you intend many similar destructive operations, queue them all and let " |
| 2246 | "the user use the Approve-all group action in the chat. Retrying this call " |
| 2247 | "will only queue another pending approval -- it cannot bypass the gate."); |
| 2248 | return reply; |
| 2249 | } |
| 2250 | |
| 2251 | if (isAssistantTool(name)) |
| 2252 | return executeAssistantTool(name, args); |
| 2253 | |
| 2254 | if (isFsTool(name)) |
| 2255 | return executeFsTool(name, args); |
| 2256 | |
| 2257 | const auto callId = QUuid::createUuid().toString(QUuid::WithoutBraces); |
| 2258 | const auto response = API::CommandRegistry::instance().execute(name, callId, args); |
| 2259 | |
| 2260 | QJsonObject reply; |
| 2261 | reply[QStringLiteral("ok")] = response.success; |
| 2262 | if (response.success) |
| 2263 | reply[QStringLiteral("result")] = response.result; |
no test coverage detected