[AI Chat] Tab Management Tool initial setup (#33985)

* [AI Chat] Tab Management Tool initial setup

Provides a Tool for an AI to manage the browser tabs during a conversation. The tool requires user permission before sending any output data. The permission is saved for the conversation, unless the browser is quit. The UI provides the permission via the existing Permission Challenge functionality in AI Chat - an "allow" or "deny" button with an explanation.
This commit is contained in:
Pete Miller
2026-02-25 08:50:46 -08:00
committed by GitHub
parent f57f455d70
commit 08b9f378ae
20 changed files with 497 additions and 4 deletions
+12
View File
@@ -667,6 +667,17 @@ constexpr flags_ui::FeatureEntry::Choice kVerticalTabCollapseDelayChoices[] = {
#define BRAVE_AI_REWRITER
#endif
#define BRAVE_AI_CHAT_TAB_MANAGEMENT_TOOL_ENTRY \
IF_BUILDFLAG(ENABLE_AI_CHAT_TAB_MANAGEMENT_TOOL, \
EXPAND_FEATURE_ENTRIES({ \
"brave-ai-chat-tab-management-tool", \
"Brave AI Chat Tab Management Tool", \
"AI can offer to sort, group, or close tabs to solve a " \
"user task during a conversation.", \
kOsDesktop, \
FEATURE_VALUE_TYPE(ai_chat::features::kTabManagementTool), \
}))
#if BUILDFLAG(ENABLE_BRAVE_ADS)
#define BRAVE_ADS_FEATURE_ENTRIES \
EXPAND_FEATURE_ENTRIES( \
@@ -1343,6 +1354,7 @@ constexpr flags_ui::FeatureEntry::Choice kVerticalTabCollapseDelayChoices[] = {
BRAVE_DARKER_THEME_FEATURE_ENTRIES \
BRAVE_PAGE_INFO_FEATURE_ENTRIES \
BRAVE_AI_CHAT_FEATURE_ENTRIES \
BRAVE_AI_CHAT_TAB_MANAGEMENT_TOOL_ENTRY \
BRAVE_AI_REWRITER \
BRAVE_ADS_FEATURE_ENTRIES \
BRAVE_LOCAL_AI_MODELS \
+11
View File
@@ -31,6 +31,13 @@ static_library("ai_chat") {
"upload_file_helper.h",
]
if (enable_ai_chat_tab_management_tool) {
sources += [
"tools/tab_management_tool.cc",
"tools/tab_management_tool.h",
]
}
deps = [
"//brave/brave_domains",
"//brave/browser:browser_process",
@@ -255,6 +262,10 @@ source_set("unit_tests") {
"//url",
]
if (enable_ai_chat_tab_management_tool) {
sources += [ "tools/tab_management_tool_unittest.cc" ]
}
if (enable_print_preview) {
sources += [ "print_preview_extractor_unittest.cc" ]
deps += [
+18
View File
@@ -8,12 +8,18 @@
#include <memory>
#include <vector>
#include "base/feature_list.h"
#include "base/memory/weak_ptr.h"
#include "brave/browser/ai_chat/code_execution_tool.h"
#include "brave/components/ai_chat/core/browser/tools/tool.h"
#include "brave/components/ai_chat/core/common/buildflags/buildflags.h"
#include "brave/components/ai_chat/core/common/features.h"
#include "content/public/browser/browser_context.h"
#if BUILDFLAG(ENABLE_AI_CHAT_TAB_MANAGEMENT_TOOL)
#include "brave/browser/ai_chat/tools/tab_management_tool.h"
#endif
namespace ai_chat {
BrowserToolProvider::BrowserToolProvider(
@@ -28,6 +34,13 @@ std::vector<base::WeakPtr<Tool>> BrowserToolProvider::GetTools() {
if (code_execution_tool_) {
tool_ptrs.push_back(code_execution_tool_->GetWeakPtr());
}
#if BUILDFLAG(ENABLE_AI_CHAT_TAB_MANAGEMENT_TOOL)
if (tab_management_tool_) {
tool_ptrs.push_back(tab_management_tool_->GetWeakPtr());
}
#endif
return tool_ptrs;
}
@@ -36,6 +49,11 @@ void BrowserToolProvider::CreateTools(
if (features::IsCodeExecutionToolEnabled()) {
code_execution_tool_ = std::make_unique<CodeExecutionTool>(browser_context);
}
#if BUILDFLAG(ENABLE_AI_CHAT_TAB_MANAGEMENT_TOOL)
if (base::FeatureList::IsEnabled(features::kTabManagementTool)) {
tab_management_tool_ = std::make_unique<TabManagementTool>();
}
#endif
}
} // namespace ai_chat
+6
View File
@@ -12,6 +12,7 @@
#include "base/memory/weak_ptr.h"
#include "brave/components/ai_chat/core/browser/tools/tool.h"
#include "brave/components/ai_chat/core/browser/tools/tool_provider.h"
#include "brave/components/ai_chat/core/common/buildflags/buildflags.h"
namespace content {
class BrowserContext;
@@ -20,6 +21,7 @@ class BrowserContext;
namespace ai_chat {
class CodeExecutionTool;
class TabManagementTool;
// Implementation of ToolProvider that provides browser-specific
// tools for conversations.
@@ -40,7 +42,11 @@ class BrowserToolProvider : public ToolProvider {
private:
void CreateTools(content::BrowserContext* browser_context);
// Browser-specific tools owned by this provider
std::unique_ptr<CodeExecutionTool> code_execution_tool_;
#if BUILDFLAG(ENABLE_AI_CHAT_TAB_MANAGEMENT_TOOL)
std::unique_ptr<TabManagementTool> tab_management_tool_;
#endif
};
} // namespace ai_chat
@@ -0,0 +1,192 @@
// Copyright (c) 2026 The Brave Authors. All rights reserved.
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.
#include "brave/browser/ai_chat/tools/tab_management_tool.h"
#include <optional>
#include <string>
#include <string_view>
#include <vector>
#include "base/json/json_reader.h"
#include "base/values.h"
#include "brave/components/ai_chat/core/browser/tools/tool_input_properties.h"
#include "brave/components/ai_chat/core/browser/tools/tool_utils.h"
#include "brave/components/ai_chat/core/common/buildflags/buildflags.h"
#include "brave/components/ai_chat/core/common/mojom/ai_chat.mojom.h"
#include "brave/components/ai_chat/core/common/mojom/common.mojom.h"
static_assert(BUILDFLAG(ENABLE_AI_CHAT_TAB_MANAGEMENT_TOOL));
static_assert(!BUILDFLAG(IS_ANDROID));
namespace ai_chat {
TabManagementTool::TabManagementTool() = default;
TabManagementTool::~TabManagementTool() = default;
std::string_view TabManagementTool::Name() const {
return mojom::kTabManagementToolName;
}
std::string_view TabManagementTool::Description() const {
return "Manage browser tabs - list, move, close tabs and manage tab "
"groups. Only use this tool when the user explicitly requests tab "
"organization, grouping, moving, or closing tabs. Do not use this "
"tool proactively or when the user's request can be answered without "
"modifying their tabs. "
"This tool can list all open tabs with their window, group, URL and "
"title information, "
"move tabs or entire groups between windows or positions,"
"close tabs, "
"and create or modify tab groups. Groups are per-window, so make any "
"moves before grouping. Use window_id=-1 to move tabs/groups to a new "
"window. "
"Use move_group_id to move an entire group at once (cannot be "
"combined with tab_ids in the same request). "
"When moving tabs, the active tab state is preserved - if you move "
"the active tab, "
"it remains active in its new location. "
"After each operation, the updated tab list is returned. "
"To move both individual tabs and groups, make separate move "
"requests. "
"If possible and you know the operations and IDs ahead of time, "
"try to make multiple parallel requests to use this tool without "
"waiting for the answer. Every time this tool needs to be used, if "
"there has been a user message since the last time it has been used "
"then the list of tabs should be read again as there has been a gap "
"in time and the list might have changed significantly.\nThe list "
"operation should be the first operation as every other operation "
"requires IDs found in the list of tabs and windows. You must provide "
"the plan parameter when using the list operation.";
}
std::optional<base::Value::Dict> TabManagementTool::InputProperties() const {
return CreateInputProperties(
{{"action", StringProperty("The action to perform",
std::vector<std::string>{
"list", "move", "close", "create_group",
"update_group", "remove_from_group"})},
{"plan",
StringProperty("Human readable plan of what the assistant intends to "
"do with the list of tabs and with the tab management "
"tool. This should be provided during the very first "
"list operation in a conversation and allows the user "
"to approve or deny the tab management operations.")},
{"tab_ids",
ArrayProperty("List of tab IDs to operate on (for move, "
"close, create_group, remove_from_group). Cannot be used "
"with move_group_id.",
IntegerProperty("Tab ID"))},
{"move_group_id",
StringProperty("Group ID to move entirely (for move operation). "
"Mutually exclusive with tab_ids - use separate "
"requests to move both individual tabs and groups.")},
{"window_id",
IntegerProperty(
"Target window ID (for move operation). Use -1 to create a new "
"window. If group_id is provided, this will be inferred from that "
"group's window and window_id should not be provided.")},
{"group_id",
StringProperty("Target group ID for update_group or move (for move "
"operation window ID will be inferred by the group "
"windowand is mutually exclusive) or group to update")},
{"index",
IntegerProperty("Target index position (for move operations)")},
{"group_title", StringProperty("Title for new or updated group")},
{"group_color",
StringProperty(
"Color for new or updated group",
std::vector<std::string>{"grey", "blue", "red", "yellow", "green",
"pink", "purple", "cyan", "orange"})},
{"add_to_end", BooleanProperty("Add tabs to end of group instead of "
"beginning (for move to group)")}});
}
std::optional<std::vector<std::string>> TabManagementTool::RequiredProperties()
const {
return std::vector<std::string>{"action"};
}
bool TabManagementTool::IsAgentTool() const {
return true;
}
std::variant<bool, mojom::PermissionChallengePtr>
TabManagementTool::RequiresUserInteractionBeforeHandling(
const mojom::ToolUseEvent& tool_use) const {
if (user_has_granted_permission_) {
return false;
}
// Provide PermissionChallenge only if input is valid and we were provided
// with a plan. If it isn't valid, it will be rejected by UseTool.
auto input = base::JSONReader::ReadDict(tool_use.arguments_json,
base::JSON_PARSE_CHROMIUM_EXTENSIONS);
if (!input.has_value()) {
return false;
}
const auto* plan = input->FindString("plan");
if (!plan || plan->empty()) {
return false;
}
return mojom::PermissionChallenge::New(std::nullopt, *plan);
}
void TabManagementTool::UserPermissionGranted(const std::string& tool_use_id) {
user_has_granted_permission_ = true;
}
void TabManagementTool::UseTool(const std::string& input_json,
UseToolCallback callback) {
auto input = base::JSONReader::ReadDict(input_json,
base::JSON_PARSE_CHROMIUM_EXTENSIONS);
if (!input.has_value()) {
std::move(callback).Run(
CreateContentBlocksForText("Failed to parse input JSON. Please provide "
"valid JSON with an 'action' field."));
return;
}
const auto& dict = input.value();
// Verify we have permission
if (!user_has_granted_permission_) {
// Report to LLM as to why we couldn't grant permission yet
auto* plan = dict.FindString("plan");
if (!plan || plan->empty()) {
// No plan provided, so we can't grant permission
std::move(callback).Run(CreateContentBlocksForText(
"No plan provided which the user will be asked to approve. Provide a "
"plan for the first use of this tool."));
return;
}
// We shouldn't get here since we expect callers to call
// RequiresUserInteractionBeforeHandling first and only call UseTool if
// permission was granted but, just in case, still provide output so the
// conversation can proceed without running this tool.
std::move(callback).Run(CreateContentBlocksForText("Unknown error"));
return;
}
CHECK(user_has_granted_permission_);
const auto* action = dict.FindString("action");
if (!action) {
std::move(callback).Run(CreateContentBlocksForText(
"Missing required 'action' field. Must be one of: list, move, close, "
"create_group, update_group, remove_from_group"));
return;
}
std::move(callback).Run(
CreateContentBlocksForText("This tool is not yet implemented"));
}
} // namespace ai_chat
@@ -0,0 +1,45 @@
// Copyright (c) 2026 The Brave Authors. All rights reserved.
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.
#ifndef BRAVE_BROWSER_AI_CHAT_TOOLS_TAB_MANAGEMENT_TOOL_H_
#define BRAVE_BROWSER_AI_CHAT_TOOLS_TAB_MANAGEMENT_TOOL_H_
#include <string>
#include <vector>
#include "base/memory/weak_ptr.h"
#include "brave/components/ai_chat/core/browser/tools/tool.h"
namespace ai_chat {
class TabManagementTool : public Tool {
public:
TabManagementTool();
~TabManagementTool() override;
TabManagementTool(const TabManagementTool&) = delete;
TabManagementTool& operator=(const TabManagementTool&) = delete;
// Tool implementation
std::string_view Name() const override;
std::string_view Description() const override;
std::optional<base::Value::Dict> InputProperties() const override;
std::optional<std::vector<std::string>> RequiredProperties() const override;
bool IsAgentTool() const override;
std::variant<bool, mojom::PermissionChallengePtr>
RequiresUserInteractionBeforeHandling(
const mojom::ToolUseEvent& tool_use) const override;
void UserPermissionGranted(const std::string& tool_use_id) override;
void UseTool(const std::string& input_json,
UseToolCallback callback) override;
private:
// Conversation-level permission state
bool user_has_granted_permission_ = false;
};
} // namespace ai_chat
#endif // BRAVE_BROWSER_AI_CHAT_TOOLS_TAB_MANAGEMENT_TOOL_H_
@@ -0,0 +1,133 @@
// Copyright (c) 2026 The Brave Authors. All rights reserved.
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.
#include "brave/browser/ai_chat/tools/tab_management_tool.h"
#include <optional>
#include <string>
#include <vector>
#include "base/test/test_future.h"
#include "brave/components/ai_chat/core/common/mojom/ai_chat.mojom.h"
#include "brave/components/ai_chat/core/common/mojom/common.mojom.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace ai_chat {
namespace {
constexpr char kValidInputListWithPlan[] =
R"({"action":"list", "plan":"move things around"})";
constexpr char kValidInputListWithoutPlan[] = R"({"action":"list"})";
constexpr char kValidInputListWithPlanWrongFormat[] =
R"({"action":"list", "plan": true})";
constexpr char kValidInputListWithEmptyPlan[] =
R"({"action":"list", "plan": ""})";
std::string ExtractText(const std::vector<mojom::ContentBlockPtr>& blocks) {
if (blocks.empty() || !blocks[0]->is_text_content_block()) {
return std::string();
}
return blocks[0]->get_text_content_block()->text;
}
std::string RunTool(TabManagementTool* tool, const std::string& json) {
base::test::TestFuture<std::vector<mojom::ContentBlockPtr>> future;
tool->UseTool(json, future.GetCallback());
return ExtractText(future.Get());
}
mojom::ToolUseEventPtr CreateToolUseEvent(const std::string& json) {
return mojom::ToolUseEvent::New(mojom::kTabManagementToolName, "1", json,
std::nullopt, nullptr, false);
}
} // namespace
TEST(TabManagementToolUnitTest, RequiresUserInteractionBeforeHandling) {
TabManagementTool tool;
auto valid_tool_event = CreateToolUseEvent(kValidInputListWithPlan);
// By default, we should ask for permission via user interaction
std::variant<bool, mojom::PermissionChallengePtr> result =
tool.RequiresUserInteractionBeforeHandling(*valid_tool_event);
ASSERT_TRUE(std::holds_alternative<mojom::PermissionChallengePtr>(result));
EXPECT_EQ((std::get<mojom::PermissionChallengePtr>(result))->plan,
"move things around");
// If a plan isn't provided, we'll return that we don't need user interaction,
// since UseTool will fail and tell the AI what's missing.
result = tool.RequiresUserInteractionBeforeHandling(
*CreateToolUseEvent(kValidInputListWithoutPlan));
ASSERT_TRUE(std::holds_alternative<bool>(result));
EXPECT_FALSE((std::get<bool>(result)));
// Same for plan is the wrong format
result = tool.RequiresUserInteractionBeforeHandling(
*CreateToolUseEvent(kValidInputListWithPlanWrongFormat));
ASSERT_TRUE(std::holds_alternative<bool>(result));
EXPECT_FALSE((std::get<bool>(result)));
// Same for empty plan
result = tool.RequiresUserInteractionBeforeHandling(
*CreateToolUseEvent(kValidInputListWithEmptyPlan));
ASSERT_TRUE(std::holds_alternative<bool>(result));
EXPECT_FALSE((std::get<bool>(result)));
}
TEST(TabManagementToolUnitTest, UseTool_Permissions) {
TabManagementTool tool;
// Not providing a plan should provide the reason during UseTool so that
// the error is reported to the AI who is given another chance to provide a
// valid tool call following the directions.
EXPECT_THAT(RunTool(&tool, kValidInputListWithoutPlan),
testing::HasSubstr("No plan provided"));
EXPECT_THAT(RunTool(&tool, kValidInputListWithPlanWrongFormat),
testing::HasSubstr("No plan provided"));
EXPECT_THAT(RunTool(&tool, kValidInputListWithEmptyPlan),
testing::HasSubstr("No plan provided"));
// Sanity check not providing permission should fail.
// Without calling UserPermissionGranted, but with a valid input, UseTool
// should be rejected. This is a safeguard to the caller not calling
// UserPermissionGranted or RequiresUserInteractionBeforeHandling.
EXPECT_THAT(RunTool(&tool, kValidInputListWithPlan),
testing::HasSubstr("Unknown error"));
// Grant permission and verify we proceed to JSON validation.
tool.UserPermissionGranted("");
EXPECT_THAT(RunTool(&tool, "{}"),
testing::HasSubstr("Missing required 'action' field"));
// Subsequent calls should not require permission again.
EXPECT_THAT(RunTool(&tool, "{}"),
testing::HasSubstr("Missing required 'action' field"));
}
TEST(TabManagementToolUnitTest, JsonAndArgumentValidationErrors) {
TabManagementTool tool;
// Grant permission once.
tool.UserPermissionGranted("");
// Parse failure (not a dict)
{
std::string result = RunTool(&tool, "[]");
ASSERT_FALSE(result.empty());
EXPECT_THAT(result, testing::HasSubstr("Failed to parse input JSON"));
}
// Missing action
EXPECT_THAT(RunTool(&tool, "{}"),
testing::HasSubstr("Missing required 'action' field"));
}
} // namespace ai_chat
@@ -23,5 +23,6 @@ buildflag_header("buildflags") {
"ENABLE_AI_CHAT=$enable_ai_chat",
"SERVICE_KEY_AICHAT=\"$service_key_aichat\"",
"ENABLE_BRAVE_AI_CHAT_AGENT_PROFILE=$enable_brave_ai_chat_agent_profile",
"ENABLE_AI_CHAT_TAB_MANAGEMENT_TOOL=$enable_ai_chat_tab_management_tool",
]
}
@@ -11,4 +11,5 @@ declare_args() {
declare_args() {
enable_brave_ai_chat_agent_profile = enable_ai_chat && !is_android
enable_ai_chat_tab_management_tool = enable_ai_chat && !is_android
}
@@ -129,6 +129,12 @@ bool IsTabOrganizationEnabled() {
return base::FeatureList::IsEnabled(features::kTabOrganization);
}
#if BUILDFLAG(ENABLE_AI_CHAT_TAB_MANAGEMENT_TOOL)
BASE_FEATURE(kTabManagementTool,
"BraveTabManagementTool",
base::FEATURE_DISABLED_BY_DEFAULT);
#endif
BASE_FEATURE(kNEARModels,
"AIChatNEARModels",
base::FEATURE_DISABLED_BY_DEFAULT);
@@ -110,6 +110,11 @@ COMPONENT_EXPORT(AI_CHAT_COMMON)
BASE_DECLARE_FEATURE(kTabOrganization);
COMPONENT_EXPORT(AI_CHAT_COMMON) bool IsTabOrganizationEnabled();
#if BUILDFLAG(ENABLE_AI_CHAT_TAB_MANAGEMENT_TOOL)
COMPONENT_EXPORT(AI_CHAT_COMMON)
BASE_DECLARE_FEATURE(kTabManagementTool);
#endif
COMPONENT_EXPORT(AI_CHAT_COMMON)
BASE_DECLARE_FEATURE(kNEARModels);
COMPONENT_EXPORT(AI_CHAT_COMMON) bool IsNEARModelsEnabled();
@@ -3,6 +3,7 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at https://mozilla.org/MPL/2.0/.
import("//brave/components/ai_chat/core/common/buildflags/buildflags.gni")
import("//mojo/public/tools/bindings/mojom.gni")
mojom_component("mojom") {
@@ -26,6 +27,12 @@ mojom_component("mojom") {
"untrusted_frame.mojom",
]
enabled_features = []
if (enable_ai_chat_tab_management_tool) {
enabled_features += [ "enable_ai_chat_tab_management_tool" ]
}
deps = [
"//mojo/public/mojom/base",
"//url/mojom:url_mojom_gurl",
@@ -20,6 +20,10 @@ const string kUserChoiceToolName = "user_choice_tool";
const string kAssistantDetailStorageToolName = "assistant_detail_storage";
const string kMemoryStorageToolName = "memory_storage_tool";
const string kCodeExecutionToolName = "code_execution_tool";
[EnableIf=enable_ai_chat_tab_management_tool]
const string kTabManagementToolName = "tab_management";
const string kClickToolName = "click_element";
const string kDragAndReleaseToolName = "drag_and_release";
const string kMoveMouseToolName = "move_mouse";
@@ -62,6 +62,10 @@ export function getToolLabel(toolName: string, toolInput: any) {
return getLocale(S.CHAT_UI_TOOL_LABEL_WAIT)
case Mojom.CODE_EXECUTION_TOOL_NAME:
return getLocale(S.CHAT_UI_TOOL_LABEL_CODE_EXECUTION)
// <if expr="enable_ai_chat_tab_management_tool">
case Mojom.TAB_MANAGEMENT_TOOL_NAME:
return getLocale(S.CHAT_UI_TOOL_LABEL_TAB_MANAGEMENT)
// </if>
case Mojom.BRAVE_NEWS_SEARCH_TOOL_NAME:
case Mojom.BRAVE_WEB_SEARCH_TOOL_NAME:
// Tool use events for server search tools are not displayed here but
@@ -0,0 +1,27 @@
// Copyright (c) 2026 The Brave Authors. All rights reserved.
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.
// <if expr="enable_ai_chat_tab_management_tool">
import * as React from 'react'
import { formatLocale } from '$web-common/locale'
import * as Mojom from '../../../common/mojom'
// </if>
export function getToolPermissionImplications(toolName: string) {
switch (toolName) {
// <if expr="enable_ai_chat_tab_management_tool">
case Mojom.TAB_MANAGEMENT_TOOL_NAME:
return formatLocale(
S.CHAT_UI_TOOL_TAB_MANAGEMENT_PERMISSION_IMPLICATIONS,
{
$1: (urlString) => <b>{urlString}</b>,
$2: (titleString) => <b>{titleString}</b>,
},
)
// </if>
default:
return null
}
}
@@ -14,18 +14,22 @@ type CustomArgs = {
isInteractive: boolean
hasAssessment: boolean
hasPlan: boolean
hasImplications: boolean
}
const args: CustomArgs = {
isInteractive: true,
hasAssessment: true,
hasPlan: false,
hasImplications: true,
}
export const _ToolPermissionChallenge = {
render: (args: CustomArgs) => {
const toolUseEvent: Mojom.ToolUseEvent = {
toolName: 'toolName',
toolName: args.hasImplications
? Mojom.TAB_MANAGEMENT_TOOL_NAME
: Mojom.CODE_EXECUTION_TOOL_NAME,
id: 'toolId',
argumentsJson: 'toolArguments',
output: undefined,
@@ -9,6 +9,7 @@ import { formatLocale, getLocale } from '$web-common/locale'
import ConversationAreaButton from '../../../common/components/conversation_area_button'
import * as Mojom from '../../../common/mojom'
import { useUntrustedConversationContext } from '../../untrusted_conversation_context'
import { getToolPermissionImplications } from '../assistant_response/get_tool_permission_implications'
import styles from './tool_permission_challenge.module.scss'
interface Props {
@@ -30,6 +31,9 @@ interface Props {
export default function ToolPermissionChallenge(props: Props) {
const conversationContext = useUntrustedConversationContext()
const toolPermissionImplications = getToolPermissionImplications(
props.toolUseEvent.toolName,
)
if (!props.toolUseEvent.permissionChallenge) {
return null
@@ -63,9 +67,12 @@ export default function ToolPermissionChallenge(props: Props) {
)}
{props.toolUseEvent.permissionChallenge?.plan && (
<p className={styles.assessment}>
{props.toolUseEvent.permissionChallenge?.plan}
</p>
<>
{toolPermissionImplications && <p>{toolPermissionImplications}</p>}
<p className={styles.assessment}>
{props.toolUseEvent.permissionChallenge?.plan}
</p>
</>
)}
<ConversationAreaButton
+1
View File
@@ -217,6 +217,7 @@ brave_grit("strings") {
defines = [
"enable_ai_chat=$enable_ai_chat",
"enable_brave_ai_chat_agent_profile=$enable_brave_ai_chat_agent_profile",
"enable_ai_chat_tab_management_tool=$enable_ai_chat_tab_management_tool",
"enable_speedreader=$enable_speedreader",
"enable_tor=$enable_tor",
"enable_brave_vpn=$enable_brave_vpn",
@@ -903,4 +903,12 @@
<message name="IDS_CHAT_UI_CODE_EXECUTION_OUTPUT_LABEL" desc="Label for output section in code execution tool" formatter_data="webui=AiChat">
Output:
</message>
<if expr="enable_ai_chat_tab_management_tool">
<message name="IDS_CHAT_UI_TOOL_LABEL_TAB_MANAGEMENT" desc="Description for the active state of the AI tab management tool" formatter_data="webui=AiChat">
Moving your tabs around
</message>
<message name="IDS_CHAT_UI_TOOL_TAB_MANAGEMENT_PERMISSION_IMPLICATIONS" desc="Implications for allowing the AI tab management tool" formatter_data="webui=AiChat">
Allowing this will send the <ph name="URL_FORMAT_START">$1<ex>&lt;b&gt;</ex></ph>URL<ph name="URL_FORMAT_END">/$1<ex>&lt;/b&gt;</ex></ph> and <ph name="TITLE_FORMAT_START">$2<ex>&lt;b&gt;</ex></ph>Title<ph name="TITLE_FORMAT_END">/$2<ex>&lt;/b&gt;</ex></ph> of all your Tabs and Tab Groups to Brave AI, and allow Brave AI to move them around. This permission applies only to this conversation.
</message>
</if>
</grit-part>
+1
View File
@@ -13,6 +13,7 @@ generated_file("build_flags_json") {
contents = {
enable_ai_chat = enable_ai_chat
enable_ai_chat_tab_management_tool = enable_ai_chat_tab_management_tool
enable_brave_wallet = enable_brave_wallet
is_brave_origin_branded = is_brave_origin_branded
is_ios = is_ios