mirror of
https://github.com/browseros-ai/BrowserOS.git
synced 2026-05-13 23:53:25 +00:00
* chore: bump server version
* fix: move session dirs to ~/.browseros/sessions and update skill paths
Session directories now live under ~/.browseros/sessions/{conversationId}/
instead of executionDir/sessions/. Adds 30-day cleanup for stale sessions
at server startup. Updates 6 default skills to reference the working
directory instead of hardcoding ~/Downloads/.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: rename sessionExecutionDir to workingDir across server
Consistent naming for the per-conversation working directory:
- ResolvedAgentConfig.sessionExecutionDir → workingDir
- ToolDirectories.executionDir → workingDir
- resolveExecutionPath() → resolveWorkingPath()
- buildBrowserToolSet param: executionDir → workingDir
Server-level executionDir (DB, logs) unchanged.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address PR review — restore emoji folder name, refresh session mtime
- Revert "Read Later" back to "📚 Read Later" to avoid creating
duplicate bookmark folders for existing users
- Touch session dir mtime on each message via utimes() so cleanup
correctly reflects last activity, not just directory creation time
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address PR review round 2 — remove dead executionDir, fix emoji
- Remove executionDir from ChatServiceDeps and ChatRouteDeps since
resolveSessionDir now uses getSessionsDir() directly
- Fix missed emoji in notification format template
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
88 lines
2.0 KiB
TypeScript
88 lines
2.0 KiB
TypeScript
import { resolve } from 'node:path'
|
|
import type { z } from 'zod'
|
|
import type { Browser } from '../browser/browser'
|
|
import { ToolResponse, type ToolResult } from './response'
|
|
|
|
export interface ToolDefinition {
|
|
name: string
|
|
description: string
|
|
input: z.ZodType
|
|
output?: z.ZodType
|
|
handler: ToolHandler
|
|
}
|
|
|
|
export type ToolHandler = (
|
|
args: unknown,
|
|
ctx: ToolContext,
|
|
response: ToolResponse,
|
|
) => Promise<void>
|
|
|
|
export interface ToolDirectories {
|
|
workingDir: string
|
|
resourcesDir?: string
|
|
}
|
|
|
|
export type ToolContext = {
|
|
browser: Browser
|
|
directories: ToolDirectories
|
|
}
|
|
|
|
export function resolveWorkingPath(
|
|
ctx: ToolContext,
|
|
targetPath: string,
|
|
cwd?: string,
|
|
): string {
|
|
return resolve(cwd ?? ctx.directories.workingDir, targetPath)
|
|
}
|
|
|
|
export function defineTool<
|
|
TInput extends z.ZodType,
|
|
TOutput extends z.ZodType | undefined = undefined,
|
|
>(config: {
|
|
name: string
|
|
description: string
|
|
input: TInput
|
|
output?: TOutput
|
|
handler: (
|
|
args: z.infer<TInput>,
|
|
ctx: ToolContext,
|
|
response: ToolResponse,
|
|
) => Promise<void>
|
|
}): ToolDefinition {
|
|
return config as ToolDefinition
|
|
}
|
|
|
|
export async function executeTool(
|
|
tool: ToolDefinition,
|
|
args: unknown,
|
|
ctx: ToolContext,
|
|
signal: AbortSignal,
|
|
): Promise<ToolResult> {
|
|
const response = new ToolResponse()
|
|
|
|
if (signal.aborted) {
|
|
response.error('Request was aborted')
|
|
return response.toResult()
|
|
}
|
|
|
|
try {
|
|
await tool.handler(args, ctx, response)
|
|
} catch (err) {
|
|
const message = err instanceof Error ? err.message : String(err)
|
|
response.error(`Internal error in ${tool.name}: ${message}`)
|
|
}
|
|
|
|
const result = await response.build(ctx.browser)
|
|
|
|
// TODO: nikhil -- maybe add to tool context instead of ugly args casting
|
|
const pageId = (args as Record<string, unknown>).page
|
|
if (typeof pageId === 'number') {
|
|
const tabId = ctx.browser.getTabIdForPage(pageId)
|
|
if (tabId !== undefined) {
|
|
result.metadata = { ...result.metadata, tabId }
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|