Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions apps/controller-ext/src/actions/bookmark/UpdateBookmarkAction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/**
* @license
* Copyright 2025 BrowserOS
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { z } from 'zod'
import { BookmarkAdapter } from '@/adapters/BookmarkAdapter'
import { ActionHandler } from '../ActionHandler'

// Input schema
const UpdateBookmarkInputSchema = z.object({
id: z.string().describe('Bookmark ID to update'),
title: z.string().optional().describe('New bookmark title'),
url: z.string().url().optional().describe('New bookmark URL'),
})

// Output schema
const UpdateBookmarkOutputSchema = z.object({
id: z.string().describe('Bookmark ID'),
title: z.string().describe('Updated bookmark title'),
url: z.string().optional().describe('Updated bookmark URL'),
})

type UpdateBookmarkInput = z.infer<typeof UpdateBookmarkInputSchema>
type UpdateBookmarkOutput = z.infer<typeof UpdateBookmarkOutputSchema>

/**
* UpdateBookmarkAction - Update a bookmark's title or URL
*
* Updates an existing bookmark with new title and/or URL.
*
* Input:
* - id: Bookmark ID to update
* - title (optional): New title for the bookmark
* - url (optional): New URL for the bookmark
*
* Output:
* - id: Bookmark ID
* - title: Updated title
* - url: Updated URL
*
* Usage:
* Update a bookmark's title or URL (at least one must be provided).
*
* Example:
* {
* "id": "123",
* "title": "New Title",
* "url": "https://www.example.com"
* }
* // Returns: { id: "123", title: "New Title", url: "https://www.example.com" }
*/
export class UpdateBookmarkAction extends ActionHandler<
UpdateBookmarkInput,
UpdateBookmarkOutput
> {
readonly inputSchema = UpdateBookmarkInputSchema
private bookmarkAdapter = new BookmarkAdapter()

async execute(input: UpdateBookmarkInput): Promise<UpdateBookmarkOutput> {
const changes: { title?: string; url?: string } = {}

if (input.title !== undefined) {
changes.title = input.title
}
if (input.url !== undefined) {
changes.url = input.url
}

if (Object.keys(changes).length === 0) {
throw new Error('At least one of title or url must be provided')
}

const updated = await this.bookmarkAdapter.updateBookmark(input.id, changes)

return {
id: updated.id,
title: updated.title,
url: updated.url,
}
}
}
2 changes: 2 additions & 0 deletions apps/controller-ext/src/background/BrowserOSController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { GetBookmarksAction } from '@/actions/bookmark/GetBookmarksAction'
import { MoveBookmarkAction } from '@/actions/bookmark/MoveBookmarkAction'
import { RemoveBookmarkAction } from '@/actions/bookmark/RemoveBookmarkAction'
import { RemoveBookmarkTreeAction } from '@/actions/bookmark/RemoveBookmarkTreeAction'
import { UpdateBookmarkAction } from '@/actions/bookmark/UpdateBookmarkAction'
import { CaptureScreenshotAction } from '@/actions/browser/CaptureScreenshotAction'
import { CaptureScreenshotPointerAction } from '@/actions/browser/CaptureScreenshotPointerAction'
import { ClearAction } from '@/actions/browser/ClearAction'
Expand Down Expand Up @@ -204,6 +205,7 @@ export class BrowserOSController {
this.actionRegistry.register('getBookmarks', new GetBookmarksAction())
this.actionRegistry.register('createBookmark', new CreateBookmarkAction())
this.actionRegistry.register('removeBookmark', new RemoveBookmarkAction())
this.actionRegistry.register('updateBookmark', new UpdateBookmarkAction())
this.actionRegistry.register(
'createBookmarkFolder',
new CreateBookmarkFolderAction(),
Expand Down
21 changes: 18 additions & 3 deletions apps/server/src/agent/prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,10 +135,25 @@ When user asks to "organize tabs", "group tabs", or "clean up tabs":

Use when built-in tools cannot accomplish the task.

## Bookmarks & History
- \`browser_get_bookmarks(folderId?)\` - Get bookmarks
- \`browser_create_bookmark(title, url, parentId?)\` - Create bookmark
## Bookmarks
- \`browser_get_bookmarks(folderId?)\` - Get all bookmarks or from specific folder
- \`browser_create_bookmark(title, url, parentId?)\` - Create bookmark (use parentId to place in folder)
- \`browser_update_bookmark(bookmarkId, title?, url?)\` - Edit bookmark title or URL
- \`browser_remove_bookmark(bookmarkId)\` - Delete bookmark
- \`browser_create_bookmark_folder(title, parentId?)\` - Create folder (returns folderId to use as parentId)
- \`browser_get_bookmark_children(folderId)\` - Get contents of a folder
- \`browser_move_bookmark(bookmarkId, parentId?, index?)\` - Move bookmark or folder to new location
- \`browser_remove_bookmark_tree(folderId, confirm)\` - Delete folder and all contents

**Organizing bookmarks into folders:**
\`\`\`
1. browser_create_bookmark_folder("Work") → folderId: "123"
2. browser_create_bookmark("Docs", "https://docs.google.com", parentId="123")
3. browser_move_bookmark(existingBookmarkId, parentId="123")
\`\`\`
Use \`browser_get_bookmarks\` to find existing folder IDs, or create new folders with \`browser_create_bookmark_folder\`.

## History
- \`browser_search_history(query, maxResults?)\` - Search history
- \`browser_get_recent_history(count?)\` - Recent history

Expand Down
5 changes: 4 additions & 1 deletion apps/server/src/tools/controller-based/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export {
moveBookmark,
removeBookmark,
removeBookmarkTree,
updateBookmark,
} from './tools/bookmarks'
// Content Extraction
export { getPageContent } from './tools/content'
Expand Down Expand Up @@ -77,6 +78,7 @@ import {
moveBookmark,
removeBookmark,
removeBookmarkTree,
updateBookmark,
} from './tools/bookmarks'
import { getPageContent } from './tools/content'
import { clickCoordinates, typeAtCoordinates } from './tools/coordinates'
Expand Down Expand Up @@ -106,7 +108,7 @@ import {
} from './tools/tab-management'
import { createWindow } from './tools/window-management'

// Array export for convenience (36 tools total)
// Array export for convenience (37 tools total)
export const allControllerTools = [
getActiveTab,
listTabs,
Expand Down Expand Up @@ -142,6 +144,7 @@ export const allControllerTools = [
getBookmarkChildren,
moveBookmark,
removeBookmarkTree,
updateBookmark,
searchHistory,
getRecentHistory,
createWindow,
Expand Down
60 changes: 55 additions & 5 deletions apps/server/src/tools/controller-based/tools/bookmarks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,21 @@ export const getBookmarks = defineTool<z.ZodRawShape, Context, Response>({

export const createBookmark = defineTool<z.ZodRawShape, Context, Response>({
name: 'browser_create_bookmark',
description: 'Create a new bookmark',
description:
'Create a new bookmark. Use parentId to place it inside an existing folder or a newly created one.',
annotations: {
category: ToolCategories.BOOKMARKS,
readOnlyHint: false,
},
schema: {
title: z.string().describe('Bookmark title'),
url: z.string().describe('URL to bookmark'),
parentId: z.string().optional().describe('Optional parent folder ID'),
parentId: z
.string()
.optional()
.describe(
'Folder ID to create bookmark in (from browser_get_bookmarks or browser_create_bookmark_folder)',
),
windowId: z.number().optional().describe('Window ID for routing'),
},
handler: async (request, response, context) => {
Expand Down Expand Up @@ -116,13 +122,51 @@ export const removeBookmark = defineTool<z.ZodRawShape, Context, Response>({
},
})

export const updateBookmark = defineTool<z.ZodRawShape, Context, Response>({
name: 'browser_update_bookmark',
description: 'Update a bookmark title or URL',
annotations: {
category: ToolCategories.BOOKMARKS,
readOnlyHint: false,
},
schema: {
bookmarkId: z.string().describe('Bookmark ID to update'),
title: z.string().optional().describe('New title for the bookmark'),
url: z.string().url().optional().describe('New URL for the bookmark'),
windowId: z.number().optional().describe('Window ID for routing'),
},
handler: async (request, response, context) => {
const { bookmarkId, title, url, windowId } = request.params as {
bookmarkId: string
title?: string
url?: string
windowId?: number
}

const result = await context.executeAction('updateBookmark', {
id: bookmarkId,
title,
url,
windowId,
})
const data = result as { id: string; title: string; url?: string }

response.appendResponseLine(`Updated bookmark: ${data.title}`)
if (data.url) {
response.appendResponseLine(`URL: ${data.url}`)
}
response.appendResponseLine(`ID: ${data.id}`)
},
})

export const createBookmarkFolder = defineTool<
z.ZodRawShape,
Context,
Response
>({
name: 'browser_create_bookmark_folder',
description: 'Create a new bookmark folder',
description:
'Create a new bookmark folder. Returns folderId to use as parentId when creating or moving bookmarks into this folder.',
annotations: {
category: ToolCategories.BOOKMARKS,
readOnlyHint: false,
Expand Down Expand Up @@ -214,14 +258,20 @@ export const getBookmarkChildren = defineTool<z.ZodRawShape, Context, Response>(

export const moveBookmark = defineTool<z.ZodRawShape, Context, Response>({
name: 'browser_move_bookmark',
description: 'Move a bookmark or folder to a new location',
description:
'Move a bookmark or folder into a different folder (existing or newly created).',
annotations: {
category: ToolCategories.BOOKMARKS,
readOnlyHint: false,
},
schema: {
bookmarkId: z.string().describe('Bookmark or folder ID to move'),
parentId: z.string().optional().describe('New parent folder ID'),
parentId: z
.string()
.optional()
.describe(
'Destination folder ID (from browser_get_bookmarks or browser_create_bookmark_folder)',
),
index: z
.number()
.int()
Expand Down
Loading