Files
BrowserOS/reference-code/old-lib/tools/tab/GroupTabsTool.ts
Felarof 8245dfe0ff Rewrite Agent Loop (#7)
* clean-up bunch of files for re-write

* more clean-up and adding basic agent

* Minor fix moved types into respective files.

* Deleted bunch of old files

backup

Update gitignore

Deleted a bunch of files

Remove message manager

Deleted old docs

Update rules

rename Profiler to profiler

* Temporarily adding old code

* Adding two small things back

* backup

* Implemented LangChainProvider and updated cursor rules

backup

LangChainProvider

curosr rules

* Implement tests for LangChainProvider -- unit test and integration test

integration test passes

integration test backup

* Tool Design

Tools Desing

tools design

* NavigationTool ready

NavigationTool ready

NavigationTool ready

NaivgationTool ready

backup

* MessageManager

MessageManager

backup

* Fixed integration test

* Agent design new

Updated agent design and added bunch of /NTN commands

agent new design

* Delete old agent design

* MessageManagerReadOnly class

* PlannerTool ready

PlannerTool almost ready

* ToolManager and DoneTool

* Integration of BrowserAgent

* BrowserAgent implementation v0.1

* BrowserAgent small fix v0.2

* Tool calling design

too call design

tool design claude

* Update agent tool design with // NTN

* add zod-to-json npm install

* BrowserAGent v0.3

* BrowserAgent v0.4

* BrowserAgent v0.5

* fixes

* Build error fixes in my NEWLY added code

build errors fix

* Build error fixes in old code (integration work)

backup

* Comment StreamEventProcessor for now, it is not used

* Small build error fix

* Small rename

* Added integration test to check structuredLLM and changed to 4o-mini

change default to nxtscape

integration test

* Small docstring

* Simplified BrowserAgent code and added integration test

Simplified BrowserAgent code

BrowserAGent integrationt est

* Update CLAUDE.md with project memory and instructions on how to write code

Update CLAUDE.md with project memory and instructions on how to write code

Project Memory

* Just a mova.. Moved ToolManager outside. Build works.

* TabOperations tool

TabOperations Tool and fixing some test

tab operations

* Update CLAUDE.md

* Added ClassificationTool

classifiction tool

classification prommpt

* Refactored and simplified PlannerTool unit test and integration test

* Updated Plnnaer tool

* Update CLAUDE.md

* BrowserAgent modified to do classification

BrowserAgent with classification

* minor fix to ToolManager

* Instead of ToolCall and ToolResult -- just updating message manager once

* minor fix to BrowserAgent integration test

* Changed done to "done_tool"

* Updated CLAUDE.md to reflect understanding of claude

* Uncommented stream event processor

* Renamed EventBus to StreamEventBus

* Commented StreamEventProcessor

* Event Processor

* Integrated EventProcessor with BrowserAgent

Added EventProcessor to BrowserAgetn

* Renamed StreamEventBus to EventBus

* Made EventBus required parameter in ExecutionContext

* PlanGenerator rewrite

PlanGenerator rewrite

backup

* For simple task, explicitly tell it to call done tool

* Max attempts for simple task

* backup

* Revert "backup"

This reverts commit 7d79a3d4d5774bfef79ec9827878b74edad3593f.

* Consolidating where EventBus and EventProcessor are created and initialized

backup

* Update CLAUDE.md

Update CLAUDE.md

* Improving agent loop code

Cleaned up processTooCall

classification task

* Create test-writer subAgent

test-agent-prompt

test agent prompt

test-agent-prompt

Update test-writer.md

* BrowserAgent test

Browseragent test

BrowserAgent test

* BrowserAgent refactor

backup

backup

* Minor fixes

* Minor fix

* minor change -- NEW AGENT LOOP IS WORKING WELL

* Update cursor rules

* Small change

* Improved BrowserAgent integration test

Improved BrowserAgent integration test

* Small change

* Update CLAUDE.md

* Different tools

* FindElementTool is ready

Find element update

backup

find element backup

* Updated to test strings to say "tests..."

* ScrollTool is ready

* RefreshStateTool is updated as well

* MessageManager updated

* SearchTool is ready

backup

* Interaction Element is also ready

* Add debugMessage emitter

* ValidatorTool ready and tests are passing

Validation Tool

validator tool

backup

backup

* GroupTabs tool ready

* Registered all the tools

* Planning changed to 5 steps

* BrowserAgent integration test fix

* Minor string changes

* backup

* Removed too many confusing events in EventProcessor -- there is only event.info right now

* Abort control implemented

backup

Abort

* Formatter for toolResult

Formatter for toolResult

backup

* Always render using Markdown

* Minor fix

---------

Co-authored-by: Nikhil Sonti <nikhilsv92@gmail.com>
2025-07-29 08:14:45 -07:00

223 lines
7.1 KiB
TypeScript

import { z } from 'zod';
import { NxtscapeTool } from '../base/NxtscapeTool';
import { ToolConfig } from '../base/ToolConfig';
import { ExecutionContext } from '@/lib/runtime/ExecutionContext';
import { TabInfo } from './TabOperationsTool';
/**
* Schema for group tabs input
*/
export const GroupTabsInputSchema = z.object({
tabIds: z.array(z.number()).min(1), // Array of tab IDs to group (at least 1 required)
groupName: z.string().optional(), // Optional name for the group
color: z.enum(['grey', 'blue', 'red', 'yellow', 'green', 'pink', 'purple', 'cyan', 'orange']).optional(), // Group color
windowId: z.number().optional() // Optional window ID where group should be created
});
export type GroupTabsInput = z.infer<typeof GroupTabsInputSchema>;
/**
* Schema for group tabs output
*/
export const GroupTabsOutputSchema = z.object({
success: z.boolean(), // Whether the operation succeeded
groupId: z.number().optional(), // ID of the created group
groupName: z.string().optional(), // Name of the group
color: z.string(), // Color of the group
tabCount: z.number(), // Number of tabs in the group
message: z.string() // Human-readable summary message
});
export type GroupTabsOutput = z.infer<typeof GroupTabsOutputSchema>;
/**
* Tool for grouping browser tabs together
*/
export class GroupTabsTool extends NxtscapeTool<GroupTabsInput, GroupTabsOutput> {
constructor(executionContext: ExecutionContext) {
const config: ToolConfig<GroupTabsInput, GroupTabsOutput> = {
name: 'group_tabs',
description: 'Group browser tabs together. Takes an array of tab IDs and creates a group with optional name and color. Colors: grey, blue, red, yellow, green, pink, purple, cyan, orange.',
category: 'tab_management',
version: '1.0.0',
inputSchema: GroupTabsInputSchema,
outputSchema: GroupTabsOutputSchema,
examples: [
{
description: 'Group work-related tabs with a blue color',
input: {
tabIds: [123, 456],
groupName: 'Work Research',
color: 'blue'
},
output: {
success: true,
groupId: 1,
groupName: 'Work Research',
groupedCount: 2,
groupedTabs: [
{ id: 123, title: 'GitHub', url: 'https://github.com' },
{ id: 456, title: 'Documentation', url: 'https://docs.example.com' }
],
message: 'Successfully grouped 2 tabs into "Work Research"'
}
}
],
streamingConfig: {
displayName: 'Group Tabs',
icon: '📁',
progressMessage: 'Organizing tabs into groups...'
}
};
super(config, executionContext);
}
/**
* Override: Generate contextual display message based on arguments
*/
getProgressMessage(args: GroupTabsInput): string {
try {
// Parse args safely
// Note: args should already be parsed by StreamEventProcessor
const tabIds = args?.tabIds;
const groupName = args?.groupName;
if (tabIds && Array.isArray(tabIds)) {
const count = tabIds.length;
const tabText = count === 1 ? 'tab' : 'tabs';
if (groupName) {
return `Grouping ${count} ${tabText} into "${groupName}"`;
}
return `Grouping ${count} ${tabText}`;
}
return 'Organizing tabs into groups...'; // Fallback to default
} catch {
return 'Organizing tabs into groups...'; // Fallback on any error
}
}
/**
* Override: Format group creation result for display
* Returns user-friendly group creation information
*/
FormatResultForUI(output: GroupTabsOutput): string {
if (!output.success) {
return `${output.message}`;
}
const tabCount = output.tabCount;
const tabText = tabCount === 1 ? 'tab' : 'tabs';
if (output.groupName) {
return `📁 Created "${output.groupName}" group with ${tabCount} ${tabText}`;
}
return `📁 Grouped ${tabCount} ${tabText} together`;
}
protected async execute(input: GroupTabsInput): Promise<GroupTabsOutput> {
try {
// Validate that all tab IDs exist
const allTabs = await this.getAllTabs(input.windowId);
const validTabIds = input.tabIds.filter(tabId =>
allTabs.some(tab => tab.id === tabId)
);
if (validTabIds.length === 0) {
return {
success: false,
color: input.color || 'blue',
tabCount: 0,
message: `No valid tabs found for the provided tab IDs: ${input.tabIds.join(', ')}`
};
}
if (validTabIds.length !== input.tabIds.length) {
const invalidIds = input.tabIds.filter(id => !validTabIds.includes(id));
console.warn(`[group_tabs] Some tab IDs were invalid and skipped: ${invalidIds.join(', ')}`);
}
// Create the group with the valid tab IDs
const groupOptions: chrome.tabs.GroupOptions = {
tabIds: validTabIds
};
if (input.windowId) {
groupOptions.createProperties = { windowId: input.windowId };
}
// Group the tabs
const groupId = await chrome.tabs.group(groupOptions);
// Update group properties if name or color specified
const color = input.color || 'blue';
// Check if chrome.tabGroups API is available
if (chrome.tabGroups && chrome.tabGroups.update) {
const updateProperties: chrome.tabGroups.UpdateProperties = {
color: color
};
if (input.groupName) {
updateProperties.title = input.groupName;
}
await chrome.tabGroups.update(groupId, updateProperties);
} else {
console.warn('[group_tabs] chrome.tabGroups API not available, cannot set group properties');
}
return {
success: true,
groupId,
groupName: input.groupName,
color,
tabCount: validTabIds.length,
message: `Successfully created group${input.groupName ? ` "${input.groupName}"` : ''} with ${validTabIds.length} tab(s)`
};
} catch (error) {
console.error('[group_tabs] Error:', error);
return {
success: false,
color: input.color || 'blue',
tabCount: 0,
message: `Error grouping tabs: ${error instanceof Error ? error.message : String(error)}`
};
}
}
/**
* Get all tabs with their information
*/
private async getAllTabs(windowId?: number): Promise<TabInfo[]> {
const queryOptions: chrome.tabs.QueryInfo = {};
if (windowId !== undefined) {
queryOptions.windowId = windowId;
}
// get window id from current tab
// we should only group tabs on the same window
const currentTab = await chrome.tabs.getCurrent();
if (currentTab && windowId === undefined) {
windowId = currentTab.windowId;
}
const tabs = await chrome.tabs.query(queryOptions);
return tabs
.filter(tab => tab.id !== undefined && tab.url && tab.title)
.map(tab => ({
id: tab.id!,
url: tab.url!,
title: tab.title!,
active: tab.active || false,
windowId: tab.windowId || 0
}));
}
}