ui: Refactor Built-In Tools naming (Server/Browser) (#27271)
* server: rename built-in tools to server tools * ui: rename built-in tools to server/browser tools
This commit is contained in:
+1
-1
@@ -35,7 +35,7 @@
|
||||
<span>
|
||||
Run llama-server with <code>{CLI_FLAGS.TOOLS}</code> flag to enable
|
||||
|
||||
<strong>Built-in Tools</strong>.
|
||||
<strong>Server Tools</strong>.
|
||||
</span>
|
||||
</span>
|
||||
|
||||
|
||||
+2
-2
@@ -62,7 +62,7 @@
|
||||
// it, the picker still opens for manual entry but explains why search is
|
||||
// unavailable instead of firing searches that would only fail. Browse is
|
||||
// hidden too: it resolves the picked folder name through the same tool.
|
||||
const fileSearchKey = $derived(toolsStore.getPermissionKey(BuiltInTool.FILE_GLOB_SEARCH));
|
||||
const fileSearchKey = $derived(toolsStore.getPermissionKey(BuiltInTool.SERVER_FILE_GLOB_SEARCH));
|
||||
const fileSearchEnabled = $derived(
|
||||
fileSearchKey !== null && toolsStore.isToolEnabled(fileSearchKey)
|
||||
);
|
||||
@@ -212,7 +212,7 @@
|
||||
// so the caller fails visibly instead of committing a bare leaf name.
|
||||
async function resolveNativeName(name: string): Promise<string | null> {
|
||||
try {
|
||||
const res = await ToolsService.executeToolRaw(BuiltInTool.FILE_GLOB_SEARCH, {
|
||||
const res = await ToolsService.executeToolRaw(BuiltInTool.SERVER_FILE_GLOB_SEARCH, {
|
||||
include: buildCaseInsensitiveGlob(name),
|
||||
limit: SEARCH.NATIVE_LIMIT,
|
||||
max_depth: SEARCH.NATIVE_MAX_DEPTH,
|
||||
|
||||
+1
-1
@@ -51,7 +51,7 @@
|
||||
// When the server does not expose file_glob_search (started without
|
||||
// --tools) or the user disabled it, the picker still opens but explains
|
||||
// why instead of firing searches that would only fail.
|
||||
const fileSearchKey = $derived(toolsStore.getPermissionKey(BuiltInTool.FILE_GLOB_SEARCH));
|
||||
const fileSearchKey = $derived(toolsStore.getPermissionKey(BuiltInTool.SERVER_FILE_GLOB_SEARCH));
|
||||
const fileSearchEnabled = $derived(
|
||||
fileSearchKey !== null && toolsStore.isToolEnabled(fileSearchKey)
|
||||
);
|
||||
|
||||
+10
-10
@@ -35,19 +35,19 @@
|
||||
|
||||
{#if isSearchCall}
|
||||
<ChatMessageToolCallBlockSearchResults {section} {open} {isStreaming} {onToggle} />
|
||||
{:else if section.toolName === BuiltInTool.GET_DATETIME}
|
||||
{:else if section.toolName === BuiltInTool.BROWSER_GET_DATETIME}
|
||||
<ChatMessageToolCallBlockGetDatetime {section} {isStreaming} />
|
||||
{:else if section.toolName === BuiltInTool.GET_INFO}
|
||||
{:else if section.toolName === BuiltInTool.SERVER_GET_INFO}
|
||||
<ChatMessageToolCallBlockGetInfo {section} {isStreaming} />
|
||||
{:else if section.toolName === BuiltInTool.READ_FILE}
|
||||
{:else if section.toolName === BuiltInTool.SERVER_READ_FILE}
|
||||
<ChatMessageToolCallBlockReadFile {section} {open} {isStreaming} {onToggle} />
|
||||
{:else if section.toolName === BuiltInTool.READ_MEDIA}
|
||||
{:else if section.toolName === BuiltInTool.BROWSER_READ_MEDIA}
|
||||
<ChatMessageToolCallBlockReadMedia {section} {open} {isStreaming} {onToggle} />
|
||||
{:else if section.toolName === BuiltInTool.EDIT_FILE}
|
||||
{:else if section.toolName === BuiltInTool.SERVER_EDIT_FILE}
|
||||
<ChatMessageToolCallBlockEditFile {section} {open} {isStreaming} {onToggle} />
|
||||
{:else if section.toolName === BuiltInTool.WRITE_FILE}
|
||||
{:else if section.toolName === BuiltInTool.SERVER_WRITE_FILE}
|
||||
<ChatMessageToolCallBlockWriteFile {section} {open} {isStreaming} {onToggle} />
|
||||
{:else if section.toolName === BuiltInTool.EXEC_SHELL_COMMAND}
|
||||
{:else if section.toolName === BuiltInTool.SERVER_EXEC_SHELL_COMMAND}
|
||||
<ChatMessageToolCallBlockExecShellCommand
|
||||
{section}
|
||||
{open}
|
||||
@@ -56,11 +56,11 @@
|
||||
{attachments}
|
||||
{onToggle}
|
||||
/>
|
||||
{:else if section.toolName === BuiltInTool.FILE_GLOB_SEARCH}
|
||||
{:else if section.toolName === BuiltInTool.SERVER_FILE_GLOB_SEARCH}
|
||||
<ChatMessageToolCallBlockFileGlobSearch {section} {open} {isStreaming} {onToggle} />
|
||||
{:else if section.toolName === BuiltInTool.GREP_SEARCH}
|
||||
{:else if section.toolName === BuiltInTool.SERVER_GREP_SEARCH}
|
||||
<ChatMessageToolCallBlockGrepSearch {section} {open} {isStreaming} {onToggle} />
|
||||
{:else if section.toolName === BuiltInTool.RUN_JAVASCRIPT}
|
||||
{:else if section.toolName === BuiltInTool.BROWSER_RUN_JAVASCRIPT}
|
||||
<ChatMessageToolCallBlockRunJavascript {section} {open} {isStreaming} {onToggle} />
|
||||
{:else}
|
||||
<ChatMessageToolCallBlockDefault {section} {open} {isStreaming} {attachments} {onToggle} />
|
||||
|
||||
+2
-2
@@ -12,7 +12,7 @@
|
||||
import {
|
||||
classifyToolResult,
|
||||
formatJsonPretty,
|
||||
getBuiltinToolUi,
|
||||
getToolUi,
|
||||
parseToolResultWithMedia
|
||||
} from '$lib/utils';
|
||||
import { createBase64DataUrl } from '$lib/utils/data-url';
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
let { attachments, isStreaming, onToggle, open, section }: Props = $props();
|
||||
|
||||
const title = $derived(getBuiltinToolUi(section.toolName)?.label ?? section.toolName ?? '');
|
||||
const title = $derived(getToolUi(section.toolName)?.label ?? section.toolName ?? '');
|
||||
const outputKind = $derived(classifyToolResult(section.toolResult));
|
||||
const parsedLines: ToolResultLine[] = $derived(
|
||||
section.toolResult ? parseToolResultWithMedia(section.toolResult, attachments) : []
|
||||
|
||||
+2
-2
@@ -6,7 +6,7 @@
|
||||
import { MAX_HEIGHT_CODE_BLOCK } from '$lib/constants';
|
||||
import { FileTypeText } from '$lib/enums';
|
||||
import type { AgenticSection } from '$lib/types';
|
||||
import { getBuiltinToolUi } from '$lib/utils';
|
||||
import { getToolUi } from '$lib/utils';
|
||||
|
||||
interface Props {
|
||||
section: AgenticSection;
|
||||
@@ -18,7 +18,7 @@
|
||||
let { isStreaming, onToggle, open, section }: Props = $props();
|
||||
|
||||
const runJsMeta = $derived(parseRunJavascriptMeta(section));
|
||||
const title = $derived(getBuiltinToolUi(section.toolName)?.label ?? section.toolName ?? '');
|
||||
const title = $derived(getToolUi(section.toolName)?.label ?? section.toolName ?? '');
|
||||
</script>
|
||||
|
||||
<ToolCallBlock {section} {open} {isStreaming} meta={runJsMeta} {title} {onToggle}>
|
||||
|
||||
+3
-3
@@ -14,8 +14,8 @@
|
||||
import { ICON_CLASS_DEFAULT, ICON_CLASS_SPIN } from '$lib/constants';
|
||||
import { AgenticSectionType } from '$lib/enums';
|
||||
import { mcpStore } from '$lib/stores';
|
||||
import type { AgenticSection, BuiltinToolUiEntry } from '$lib/types';
|
||||
import { getBuiltinToolUi } from '$lib/utils';
|
||||
import type { AgenticSection, ToolUiEntry } from '$lib/types';
|
||||
import { getToolUi } from '$lib/utils';
|
||||
import type { Component, Snippet } from 'svelte';
|
||||
|
||||
type ToolCallBlockMetaWithError = TMeta & { errorMessage?: string };
|
||||
@@ -82,7 +82,7 @@
|
||||
const showSpinner = $derived(isPending || (isStreamingCall && isStreaming) || extraLiveStreaming);
|
||||
const isCodeStreaming = $derived(isStreaming && (isPending || isStreamingCall));
|
||||
|
||||
const toolUi: BuiltinToolUiEntry | null = $derived(getBuiltinToolUi(section.toolName));
|
||||
const toolUi: ToolUiEntry | null = $derived(getToolUi(section.toolName));
|
||||
const toolIcon: Component = $derived(
|
||||
spinIconWhenActive && showSpinner ? Loader2 : (toolUi?.icon ?? Wrench)
|
||||
);
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ export type EditFileMeta = {
|
||||
};
|
||||
|
||||
export function parseEditFileMeta(section: AgenticSection): EditFileMeta | null {
|
||||
const args = parseToolArgs(BuiltInTool.EDIT_FILE, section, { partial: true });
|
||||
const args = parseToolArgs(BuiltInTool.SERVER_EDIT_FILE, section, { partial: true });
|
||||
|
||||
if (!args) return null;
|
||||
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ export type ExecShellCommandMeta = {
|
||||
};
|
||||
|
||||
export function parseExecShellCommandMeta(section: AgenticSection): ExecShellCommandMeta | null {
|
||||
const args = parseToolArgs(BuiltInTool.EXEC_SHELL_COMMAND, section);
|
||||
const args = parseToolArgs(BuiltInTool.SERVER_EXEC_SHELL_COMMAND, section);
|
||||
|
||||
if (!args) return null;
|
||||
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ export type FileGlobSearchMeta = {
|
||||
};
|
||||
|
||||
export function parseFileGlobSearchMeta(section: AgenticSection): FileGlobSearchMeta | null {
|
||||
const args = parseToolArgs(BuiltInTool.FILE_GLOB_SEARCH, section);
|
||||
const args = parseToolArgs(BuiltInTool.SERVER_FILE_GLOB_SEARCH, section);
|
||||
|
||||
if (!args) return null;
|
||||
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ export type GrepSearchMeta = {
|
||||
};
|
||||
|
||||
export function parseGrepSearchMeta(section: AgenticSection): GrepSearchMeta | null {
|
||||
const args = parseToolArgs(BuiltInTool.GREP_SEARCH, section);
|
||||
const args = parseToolArgs(BuiltInTool.SERVER_GREP_SEARCH, section);
|
||||
|
||||
if (!args) return null;
|
||||
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ export type ReadFileMeta = {
|
||||
};
|
||||
|
||||
export function parseReadFileMeta(section: AgenticSection): ReadFileMeta | null {
|
||||
const args = parseToolArgs(BuiltInTool.READ_FILE, section, { partial: true });
|
||||
const args = parseToolArgs(BuiltInTool.SERVER_READ_FILE, section, { partial: true });
|
||||
|
||||
if (!args) return null;
|
||||
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ export type RunJavascriptMeta = {
|
||||
};
|
||||
|
||||
export function parseRunJavascriptMeta(section: AgenticSection): RunJavascriptMeta | null {
|
||||
const args = parseToolArgs(BuiltInTool.RUN_JAVASCRIPT, section);
|
||||
const args = parseToolArgs(BuiltInTool.BROWSER_RUN_JAVASCRIPT, section);
|
||||
|
||||
if (!args) return null;
|
||||
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ export type WriteFileMeta = {
|
||||
};
|
||||
|
||||
export function parseWriteFileMeta(section: AgenticSection): WriteFileMeta | null {
|
||||
const args = parseToolArgs(BuiltInTool.WRITE_FILE, section, { partial: true });
|
||||
const args = parseToolArgs(BuiltInTool.SERVER_WRITE_FILE, section, { partial: true });
|
||||
|
||||
if (!args) return null;
|
||||
|
||||
|
||||
+2
-2
@@ -61,8 +61,8 @@
|
||||
{:else}
|
||||
{@const source = toolsStore.getToolSource(toolName)}
|
||||
{@const providerName =
|
||||
source === ToolSource.BUILTIN
|
||||
? TOOL_SERVER_LABELS[ToolSource.BUILTIN]
|
||||
source === ToolSource.SERVER
|
||||
? TOOL_SERVER_LABELS[ToolSource.SERVER]
|
||||
: source === ToolSource.CUSTOM
|
||||
? TOOL_SERVER_LABELS[ToolSource.CUSTOM]
|
||||
: 'MCP Tools'}
|
||||
|
||||
@@ -278,7 +278,7 @@ export { default as ChatFormInput } from './ChatForm/ChatFormInput/ChatFormInput
|
||||
/**
|
||||
* Working directory selector for agent mode. Renders a chip below the chat
|
||||
* form; clicking it opens a popover with a directory picker backed by the
|
||||
* server's `file_glob_search` built-in tool (POST /tools). The picked
|
||||
* server's `file_glob_search` server tool (POST /tools). The picked
|
||||
* directory is exposed via `bind:directory`; changing it records a
|
||||
* synthetic "Set working directory to ..." user message into chat history
|
||||
* and is enforced on tool calls via the `x-tool-cwd` request header.
|
||||
@@ -380,7 +380,7 @@ export { default as ChatFormPickerListItemSkeleton } from './ChatForm/ChatFormPi
|
||||
|
||||
/**
|
||||
* `@`-triggered file/folder mention picker. Resolves `@<query>` in the chat
|
||||
* input to a filesystem match via the server's `file_glob_search` built-in
|
||||
* input to a filesystem match via the server's `file_glob_search` server tool
|
||||
* tool, scoped to the conversation cwd (or server home when unset).
|
||||
* Selection splices a `[name](file:///<abs path>)` link into the input.
|
||||
*/
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import { ICON_CLASS_DEFAULT } from '$lib/constants';
|
||||
import { ToolSource } from '$lib/enums/tools.enums';
|
||||
import { mcpStore, permissionsStore, toolsStore } from '$lib/stores';
|
||||
import { getBuiltinToolUi } from '$lib/utils';
|
||||
import { getToolUi } from '$lib/utils';
|
||||
import { SvelteSet } from 'svelte/reactivity';
|
||||
|
||||
let expandedGroups = new SvelteSet<string>();
|
||||
@@ -69,12 +69,12 @@
|
||||
|
||||
{#each group.tools as entry (entry.key)}
|
||||
{@const toolName = entry.definition.function.name}
|
||||
{@const builtinUi =
|
||||
entry.source === ToolSource.BUILTIN || entry.source === ToolSource.FRONTEND
|
||||
? getBuiltinToolUi(toolName)
|
||||
{@const toolUi =
|
||||
entry.source === ToolSource.SERVER || entry.source === ToolSource.BROWSER
|
||||
? getToolUi(toolName)
|
||||
: null}
|
||||
{@const displayLabel = builtinUi?.label ?? toolName}
|
||||
{@const IconComponent = builtinUi?.icon ?? null}
|
||||
{@const displayLabel = toolUi?.label ?? toolName}
|
||||
{@const IconComponent = toolUi?.icon ?? null}
|
||||
{@const isEnabled = toolsStore.isToolEnabled(entry.key)}
|
||||
{@const permissionKey = entry.key}
|
||||
{@const isAlwaysAllowed = permissionsStore.hasTool(permissionKey)}
|
||||
|
||||
@@ -69,7 +69,7 @@ export { default as SettingsChatFields } from './SettingsChat/SettingsChatFields
|
||||
/**
|
||||
* **SettingsChatToolsTab** - Tools configuration tab for chat settings
|
||||
*
|
||||
* Displays available tools grouped by source (built-in, MCP, custom) with
|
||||
* Displays available tools grouped by source (server, browser, MCP, custom) with
|
||||
* toggles to enable/disable individual tools and tool groups. Shows MCP
|
||||
* server favicons and permission management controls.
|
||||
*/
|
||||
|
||||
@@ -2,7 +2,9 @@ import { CLI_FLAGS } from './cli-flags.constants';
|
||||
import { BuiltInTool, JsonSchemaType, ToolCallType } from '$lib/enums';
|
||||
import type { OpenAIToolDefinition } from '$lib/types';
|
||||
|
||||
export const BROWSER_INFO_TOOL_NAME = BuiltInTool.GET_INFO;
|
||||
// get_info is served by the server, but the browser falls back to this
|
||||
// implementation when the server does not provide it - same wire name.
|
||||
export const BROWSER_INFO_TOOL_NAME = BuiltInTool.SERVER_GET_INFO;
|
||||
|
||||
/** UA token to OS name, first match wins - Android and iOS UAs also carry the Linux / Mac OS X tokens */
|
||||
export const BROWSER_INFO_OS_UA_PATTERNS: readonly [RegExp, string][] = [
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
// Registry of built-in and frontend (browser) tools whose renderer
|
||||
// shows a recognizable icon and friendly label inline in the chat UI.
|
||||
//
|
||||
// To add a new built-in tool, add an entry to BUILTIN_TOOL_UI. To give a
|
||||
// tool a custom title or body renderer, add a dedicated component under
|
||||
// ChatMessageToolCall/ and route it in ChatMessageToolCallBlock.svelte
|
||||
// (see ChatMessageToolCallBlockGetDatetime and
|
||||
// ChatMessageToolCallBlockSearchResults for prior art).
|
||||
|
||||
import {
|
||||
Braces,
|
||||
Clock,
|
||||
Eye,
|
||||
FilePen,
|
||||
FilePlus,
|
||||
FileSearch,
|
||||
FileText,
|
||||
Info,
|
||||
SearchCode,
|
||||
Terminal
|
||||
} from '@lucide/svelte';
|
||||
import { BuiltInTool, ToolSource } from '$lib/enums';
|
||||
import type { BuiltinToolUiEntry } from '$lib/types';
|
||||
|
||||
export const BUILTIN_TOOL_UI: Readonly<Record<BuiltInTool, BuiltinToolUiEntry>> = {
|
||||
[BuiltInTool.EDIT_FILE]: { icon: FilePen, label: 'Edit file', source: ToolSource.BUILTIN },
|
||||
[BuiltInTool.EXEC_SHELL_COMMAND]: {
|
||||
icon: Terminal,
|
||||
label: 'Run command',
|
||||
source: ToolSource.BUILTIN
|
||||
},
|
||||
[BuiltInTool.FILE_GLOB_SEARCH]: {
|
||||
icon: FileSearch,
|
||||
label: 'Search files',
|
||||
source: ToolSource.BUILTIN
|
||||
},
|
||||
[BuiltInTool.GET_DATETIME]: { icon: Clock, label: 'Current time', source: ToolSource.FRONTEND },
|
||||
[BuiltInTool.GET_INFO]: { icon: Info, label: 'Runtime info', source: ToolSource.BUILTIN },
|
||||
[BuiltInTool.GREP_SEARCH]: {
|
||||
icon: SearchCode,
|
||||
label: 'Search in files',
|
||||
source: ToolSource.BUILTIN
|
||||
},
|
||||
[BuiltInTool.READ_FILE]: { icon: FileText, label: 'Read file', source: ToolSource.BUILTIN },
|
||||
[BuiltInTool.READ_MEDIA]: { icon: Eye, label: 'Read media', source: ToolSource.FRONTEND },
|
||||
[BuiltInTool.RUN_JAVASCRIPT]: {
|
||||
icon: Braces,
|
||||
label: 'Run JavaScript',
|
||||
source: ToolSource.FRONTEND
|
||||
},
|
||||
[BuiltInTool.WRITE_FILE]: { icon: FilePlus, label: 'Write file', source: ToolSource.BUILTIN }
|
||||
} as const;
|
||||
@@ -1,7 +1,7 @@
|
||||
import { BuiltInTool, JsonSchemaType, ToolCallType } from '$lib/enums';
|
||||
import type { OpenAIToolDefinition } from '$lib/types';
|
||||
|
||||
export const GET_DATETIME_TOOL_NAME = BuiltInTool.GET_DATETIME;
|
||||
export const GET_DATETIME_TOOL_NAME = BuiltInTool.BROWSER_GET_DATETIME;
|
||||
|
||||
export function buildGetDatetimeToolDefinition(): OpenAIToolDefinition {
|
||||
return {
|
||||
|
||||
@@ -15,7 +15,7 @@ export * from './context-gauge-popup.constants';
|
||||
export * from './conversation-import.constants';
|
||||
export * from './binary-detection.constants';
|
||||
export * from './content-detection.constants';
|
||||
export * from './built-in-tools.constants';
|
||||
export * from './tool-ui.constants';
|
||||
export * from './cache.constants';
|
||||
export * from './chat-form.constants';
|
||||
export * from './cli-flags.constants';
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
} from '$lib/enums';
|
||||
import type { OpenAIToolDefinition } from '$lib/types';
|
||||
|
||||
export const READ_MEDIA_TOOL_NAME = BuiltInTool.READ_MEDIA;
|
||||
export const READ_MEDIA_TOOL_NAME = BuiltInTool.BROWSER_READ_MEDIA;
|
||||
|
||||
// header lines of the tool result, parsed back by the read_media renderer
|
||||
export const PREFIX_FILE = 'File: ';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { BuiltInTool } from '$lib/enums';
|
||||
|
||||
export const SANDBOX_TOOL_NAME = BuiltInTool.RUN_JAVASCRIPT;
|
||||
export const SANDBOX_TOOL_NAME = BuiltInTool.BROWSER_RUN_JAVASCRIPT;
|
||||
|
||||
export const SANDBOX_TIMEOUT_MS_DEFAULT = 10000;
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
// Registry of server and browser tools whose renderer
|
||||
// shows a recognizable icon and friendly label inline in the chat UI.
|
||||
//
|
||||
// To add a new tool, add an entry to TOOL_UI. To give a
|
||||
// tool a custom title or body renderer, add a dedicated component under
|
||||
// ChatMessageToolCall/ and route it in ChatMessageToolCallBlock.svelte
|
||||
// (see ChatMessageToolCallBlockGetDatetime and
|
||||
// ChatMessageToolCallBlockSearchResults for prior art).
|
||||
|
||||
import {
|
||||
Braces,
|
||||
Clock,
|
||||
Eye,
|
||||
FilePen,
|
||||
FilePlus,
|
||||
FileSearch,
|
||||
FileText,
|
||||
Info,
|
||||
SearchCode,
|
||||
Terminal
|
||||
} from '@lucide/svelte';
|
||||
import { BuiltInTool, ToolSource } from '$lib/enums';
|
||||
import type { ToolUiEntry } from '$lib/types';
|
||||
|
||||
export const TOOL_UI: Readonly<Record<BuiltInTool, ToolUiEntry>> = {
|
||||
[BuiltInTool.BROWSER_GET_DATETIME]: {
|
||||
icon: Clock,
|
||||
label: 'Current time',
|
||||
source: ToolSource.BROWSER
|
||||
},
|
||||
[BuiltInTool.BROWSER_READ_MEDIA]: { icon: Eye, label: 'Read media', source: ToolSource.BROWSER },
|
||||
[BuiltInTool.BROWSER_RUN_JAVASCRIPT]: {
|
||||
icon: Braces,
|
||||
label: 'Run JavaScript',
|
||||
source: ToolSource.BROWSER
|
||||
},
|
||||
[BuiltInTool.SERVER_EDIT_FILE]: { icon: FilePen, label: 'Edit file', source: ToolSource.SERVER },
|
||||
[BuiltInTool.SERVER_EXEC_SHELL_COMMAND]: {
|
||||
icon: Terminal,
|
||||
label: 'Run command',
|
||||
source: ToolSource.SERVER
|
||||
},
|
||||
[BuiltInTool.SERVER_FILE_GLOB_SEARCH]: {
|
||||
icon: FileSearch,
|
||||
label: 'Search files',
|
||||
source: ToolSource.SERVER
|
||||
},
|
||||
[BuiltInTool.SERVER_GET_INFO]: { icon: Info, label: 'Runtime info', source: ToolSource.SERVER },
|
||||
[BuiltInTool.SERVER_GREP_SEARCH]: {
|
||||
icon: SearchCode,
|
||||
label: 'Search in files',
|
||||
source: ToolSource.SERVER
|
||||
},
|
||||
[BuiltInTool.SERVER_READ_FILE]: { icon: FileText, label: 'Read file', source: ToolSource.SERVER },
|
||||
[BuiltInTool.SERVER_WRITE_FILE]: {
|
||||
icon: FilePlus,
|
||||
label: 'Write file',
|
||||
source: ToolSource.SERVER
|
||||
}
|
||||
} as const;
|
||||
@@ -18,15 +18,15 @@ export const UI_DATA_ATTRS = {
|
||||
} as const;
|
||||
|
||||
export const TOOL_GROUP_LABELS = {
|
||||
[ToolSource.BUILTIN]: 'Built-in',
|
||||
[ToolSource.BROWSER]: 'Browser',
|
||||
[ToolSource.CUSTOM]: 'JSON Schema',
|
||||
[ToolSource.FRONTEND]: 'Browser'
|
||||
[ToolSource.SERVER]: 'Server'
|
||||
} as const;
|
||||
|
||||
export const TOOL_SERVER_LABELS = {
|
||||
[ToolSource.BUILTIN]: 'Built-in Tools',
|
||||
[ToolSource.BROWSER]: 'Browser Tools',
|
||||
[ToolSource.CUSTOM]: 'Custom Tools',
|
||||
[ToolSource.FRONTEND]: 'Browser Tools'
|
||||
[ToolSource.SERVER]: 'Server Tools'
|
||||
} as const;
|
||||
|
||||
export const TOOLTIP_DELAY_DURATION = 500;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
export enum ToolSource {
|
||||
BUILTIN = 'builtin',
|
||||
MCP = 'mcp',
|
||||
BROWSER = 'browser',
|
||||
CUSTOM = 'custom',
|
||||
FRONTEND = 'frontend'
|
||||
MCP = 'mcp',
|
||||
SERVER = 'server'
|
||||
}
|
||||
|
||||
export enum ToolPermissionDecision {
|
||||
@@ -28,22 +28,28 @@ export enum GlobSearchType {
|
||||
}
|
||||
|
||||
/**
|
||||
* Wire-format identifiers for built-in and frontend tools. The string
|
||||
* Wire-format identifiers for server and browser tools. The string
|
||||
* value matches what the model emits in tool call names, so comparing
|
||||
* against `BuiltInTool.READ_FILE` is equivalent to comparing against the
|
||||
* raw `'read_file'` literal - the enum just keeps the two in lock-step
|
||||
* and gives TypeScript a single source of truth for autocomplete / rename
|
||||
* support.
|
||||
* against `BuiltInTool.SERVER_READ_FILE` is equivalent to comparing
|
||||
* against the raw `'read_file'` literal - the enum just keeps the two in
|
||||
* lock-step and gives TypeScript a single source of truth for autocomplete
|
||||
* / rename support.
|
||||
*
|
||||
* The `SERVER_` / `BROWSER_` prefixes mirror the tool's primary source
|
||||
* (llama-server vs llama-ui). `get_info` is the exception: it is served by
|
||||
* the server, but llama-ui falls back to a browser implementation when the
|
||||
* server does not provide it, so it can surface under both categories in
|
||||
* the UI while keeping a single wire name.
|
||||
*/
|
||||
export enum BuiltInTool {
|
||||
READ_FILE = 'read_file',
|
||||
READ_MEDIA = 'read_media',
|
||||
EDIT_FILE = 'edit_file',
|
||||
WRITE_FILE = 'write_file',
|
||||
GET_DATETIME = 'get_datetime',
|
||||
GET_INFO = 'get_info',
|
||||
FILE_GLOB_SEARCH = 'file_glob_search',
|
||||
GREP_SEARCH = 'grep_search',
|
||||
EXEC_SHELL_COMMAND = 'exec_shell_command',
|
||||
RUN_JAVASCRIPT = 'run_javascript'
|
||||
BROWSER_GET_DATETIME = 'get_datetime',
|
||||
BROWSER_READ_MEDIA = 'read_media',
|
||||
BROWSER_RUN_JAVASCRIPT = 'run_javascript',
|
||||
SERVER_EDIT_FILE = 'edit_file',
|
||||
SERVER_EXEC_SHELL_COMMAND = 'exec_shell_command',
|
||||
SERVER_FILE_GLOB_SEARCH = 'file_glob_search',
|
||||
SERVER_GET_INFO = 'get_info',
|
||||
SERVER_GREP_SEARCH = 'grep_search',
|
||||
SERVER_READ_FILE = 'read_file',
|
||||
SERVER_WRITE_FILE = 'write_file'
|
||||
}
|
||||
|
||||
@@ -46,13 +46,13 @@ export function useToolsPanel(): UseToolsPanelReturn {
|
||||
|
||||
// Tools endpoint is unreachable (404) — server started without --tools
|
||||
if (toolsStore.isToolsEndpointUnreachable) {
|
||||
return `To enable Built-In Tools you need to run llama-server with ${CLI_FLAGS.TOOLS} all or ${CLI_FLAGS.TOOLS} <name> flag. To see MCP Tools you need to add / enable MCP Server(s).`;
|
||||
return `To enable Server Tools you need to run llama-server with ${CLI_FLAGS.TOOLS} all or ${CLI_FLAGS.TOOLS} <name> flag. To see MCP Tools you need to add / enable MCP Server(s).`;
|
||||
}
|
||||
|
||||
// Other errors — return null so UI shows "Failed to load tools"
|
||||
if (toolsStore.error) return null;
|
||||
|
||||
return `To enable Built-In Tools you need to run llama-server with ${CLI_FLAGS.TOOLS} all or ${CLI_FLAGS.TOOLS} <name> flag. To see MCP Tools you need to add / enable MCP Server(s).`;
|
||||
return `To enable Server Tools you need to run llama-server with ${CLI_FLAGS.TOOLS} all or ${CLI_FLAGS.TOOLS} <name> flag. To see MCP Tools you need to add / enable MCP Server(s).`;
|
||||
});
|
||||
|
||||
function isGroupChecked(group: ToolGroup): boolean {
|
||||
@@ -95,8 +95,8 @@ export function useToolsPanel(): UseToolsPanelReturn {
|
||||
}
|
||||
|
||||
function handleOpen(): void {
|
||||
if (toolsStore.builtinTools.length === 0 && !toolsStore.loading) {
|
||||
toolsStore.fetchBuiltinTools();
|
||||
if (toolsStore.serverTools.length === 0 && !toolsStore.loading) {
|
||||
toolsStore.fetchServerTools();
|
||||
}
|
||||
|
||||
mcpStore.runHealthChecksForServers(mcpStore.getServers().filter((s) => s.enabled));
|
||||
|
||||
@@ -262,9 +262,9 @@ export { ParameterSyncService } from './parameter-sync.service';
|
||||
export { MCPService } from './mcp.service';
|
||||
|
||||
/**
|
||||
* **SandboxService** - Frontend JavaScript execution in a browser sandbox
|
||||
* **SandboxService** - Browser JavaScript execution in a browser sandbox
|
||||
*
|
||||
* Stateless executor for the run_javascript frontend tool. Model generated
|
||||
* Stateless executor for the run_javascript browser tool. Model generated
|
||||
* code runs in a Web Worker spawned inside a sandboxed iframe with an opaque
|
||||
* origin: no access to the app origin, its storage or its API, and outgoing
|
||||
* requests carry a null origin. The code never touches a main thread, so the
|
||||
@@ -274,7 +274,7 @@ export { MCPService } from './mcp.service';
|
||||
* **Architecture & Relationships:**
|
||||
* - **SandboxService** (this class): Stateless sandbox execution
|
||||
* - **toolsStore**: Exposes the tool definition when the sandbox is enabled
|
||||
* - **agenticStore**: Dispatches ToolSource.FRONTEND calls here
|
||||
* - **agenticStore**: Dispatches ToolSource.BROWSER calls here
|
||||
*
|
||||
* @see buildSandboxToolDefinition in utils/sandbox-tool - tool schema sent to the LLM
|
||||
* @see agenticStore in stores/agentic.svelte.ts - tool dispatch
|
||||
|
||||
@@ -28,15 +28,15 @@ function fileExtension(path: string): string {
|
||||
}
|
||||
|
||||
/**
|
||||
* **ReadMediaService** - frontend executor for the `read_media` tool
|
||||
* **ReadMediaService** - browser executor for the `read_media` tool
|
||||
*
|
||||
* The tool is synthetic: no such tool exists on the server. It reads the file
|
||||
* through the built-in `read_file` tool with the `base64` response type, then
|
||||
* through the server `read_file` tool with the `base64` response type, then
|
||||
* turns the bytes into a data URI line. The agentic store lifts that line into
|
||||
* an image or audio attachment on the tool result message, which is what makes
|
||||
* the model perceive the file instead of reading a wall of base64.
|
||||
*
|
||||
* Living in the frontend is what lets it exist only for models that can
|
||||
* Living in the browser is what lets it exist only for models that can
|
||||
* actually use the result - the server has no idea which model is selected.
|
||||
*
|
||||
* @see buildReadMediaToolDefinition in constants/read-media.ts - tool schema sent to the LLM
|
||||
@@ -82,7 +82,7 @@ export class ReadMediaService {
|
||||
}
|
||||
|
||||
const raw = await ToolsService.executeToolRaw(
|
||||
BuiltInTool.READ_FILE,
|
||||
BuiltInTool.SERVER_READ_FILE,
|
||||
{ path },
|
||||
signal,
|
||||
cwd,
|
||||
|
||||
@@ -68,7 +68,7 @@ function formatReply(reply: SandboxReply): ToolExecutionResult {
|
||||
|
||||
export class SandboxService {
|
||||
/**
|
||||
* Execute a frontend sandbox tool call and return its output.
|
||||
* Execute a browser sandbox tool call and return its output.
|
||||
* One disposable iframe per execution, removed on completion,
|
||||
* timeout or abort. Removing the iframe terminates the worker
|
||||
* at the browser level, so runaway code cannot outlive it.
|
||||
@@ -79,7 +79,7 @@ export class SandboxService {
|
||||
signal?: AbortSignal
|
||||
): Promise<ToolExecutionResult> {
|
||||
if (toolName !== SANDBOX_TOOL_NAME) {
|
||||
return { content: `Unknown frontend tool: ${toolName}`, isError: true };
|
||||
return { content: `Unknown browser tool: ${toolName}`, isError: true };
|
||||
}
|
||||
|
||||
const code = typeof params.code === 'string' ? params.code : '';
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
import { base } from '$app/paths';
|
||||
import { API_TOOLS, HEADERS } from '$lib/constants';
|
||||
import { ToolResponseField } from '$lib/enums';
|
||||
import type { ServerBuiltinToolInfo, ToolExecutionResult } from '$lib/types';
|
||||
import type { ServerToolInfo, ToolExecutionResult } from '$lib/types';
|
||||
import { apiFetch } from '$lib/utils';
|
||||
import { getJsonHeaders } from '$lib/utils/api-headers';
|
||||
import { parseSseJsonStream, type SseJsonEvent } from '$lib/utils/sse';
|
||||
|
||||
export class ToolsService {
|
||||
/**
|
||||
* Fetch the list of built-in tools from the server.
|
||||
* Fetch the list of server tools from the server.
|
||||
*
|
||||
* @returns Array of tool definitions in OpenAI-compatible format
|
||||
*/
|
||||
static async list(): Promise<ServerBuiltinToolInfo[]> {
|
||||
return apiFetch<ServerBuiltinToolInfo[]>(API_TOOLS.LIST);
|
||||
static async list(): Promise<ServerToolInfo[]> {
|
||||
return apiFetch<ServerToolInfo[]>(API_TOOLS.LIST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a built-in tool on the server.
|
||||
* Execute a server tool on the server.
|
||||
*
|
||||
* @param cwd - Working directory for the tool call, sent as the
|
||||
* x-tool-cwd request header. The server resolves relative paths
|
||||
@@ -48,7 +48,7 @@ export class ToolsService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a built-in tool and return the raw JSON response. Unlike
|
||||
* Execute a server tool and return the raw JSON response. Unlike
|
||||
* executeTool, this preserves structured fields (e.g. file_glob_search's
|
||||
* `entries` and `base`) that the flattened ToolExecutionResult drops.
|
||||
*
|
||||
@@ -77,7 +77,7 @@ export class ToolsService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Stream a built-in tool's output chunks from the server. The server
|
||||
* Stream a server tool's output chunks from the server. The server
|
||||
* `POST /tools` endpoint with `{stream: true}` emits `data: {"chunk": "..."}`
|
||||
* events followed by a terminal `data: {"done": true}` (optionally with
|
||||
* `error`). Yields the chunk string for each partial event.
|
||||
|
||||
@@ -326,8 +326,8 @@ class AgenticStore {
|
||||
const maxTurns = Number(settings.agenticMaxTurns) || DEFAULT_AGENTIC_CONFIG.maxTurns;
|
||||
const hasTools =
|
||||
mcpStore.hasEnabledServers(perChatOverrides) ||
|
||||
toolsStore.builtinTools.length > 0 ||
|
||||
toolsStore.frontendTools.length > 0 ||
|
||||
toolsStore.serverTools.length > 0 ||
|
||||
toolsStore.browserTools.length > 0 ||
|
||||
toolsStore.customTools.length > 0;
|
||||
|
||||
return {
|
||||
@@ -455,9 +455,9 @@ class AgenticStore {
|
||||
this._continueResolvers.delete(conversationId);
|
||||
this._steeringMessages.delete(conversationId);
|
||||
|
||||
// Ensure built-in tools are fetched before checking if agentic is enabled
|
||||
if (toolsStore.builtinTools.length === 0 && !toolsStore.loading) {
|
||||
await toolsStore.fetchBuiltinTools();
|
||||
// Ensure server tools are fetched before checking if agentic is enabled
|
||||
if (toolsStore.serverTools.length === 0 && !toolsStore.loading) {
|
||||
await toolsStore.fetchServerTools();
|
||||
}
|
||||
|
||||
const agenticConfig = this.getConfig(settingsStore.config, perChatOverrides);
|
||||
@@ -906,8 +906,8 @@ class AgenticStore {
|
||||
} else {
|
||||
try {
|
||||
if (
|
||||
toolSource === ToolSource.BUILTIN &&
|
||||
toolName === BuiltInTool.EXEC_SHELL_COMMAND &&
|
||||
toolSource === ToolSource.SERVER &&
|
||||
toolName === BuiltInTool.SERVER_EXEC_SHELL_COMMAND &&
|
||||
createToolResultMessage &&
|
||||
updateToolResultMessage
|
||||
) {
|
||||
@@ -938,7 +938,7 @@ class AgenticStore {
|
||||
}
|
||||
}
|
||||
result = accumulated;
|
||||
} else if (toolSource === ToolSource.BUILTIN) {
|
||||
} else if (toolSource === ToolSource.SERVER) {
|
||||
const args = this.parseToolArguments(toolCall.function.arguments);
|
||||
const cwd = conversationsStore.activeConversation?.cwd;
|
||||
const executionResult = await ToolsService.executeTool(toolName, args, signal, cwd);
|
||||
@@ -946,16 +946,16 @@ class AgenticStore {
|
||||
result = executionResult.content;
|
||||
|
||||
if (executionResult.isError) toolSuccess = false;
|
||||
} else if (toolSource === ToolSource.FRONTEND) {
|
||||
} else if (toolSource === ToolSource.BROWSER) {
|
||||
const args = this.parseToolArguments(toolCall.function.arguments);
|
||||
|
||||
let executionResult: ToolExecutionResult;
|
||||
|
||||
if (toolName === BuiltInTool.GET_DATETIME) {
|
||||
if (toolName === BuiltInTool.BROWSER_GET_DATETIME) {
|
||||
executionResult = executeGetDatetimeTool();
|
||||
} else if (toolName === BuiltInTool.GET_INFO) {
|
||||
} else if (toolName === BuiltInTool.SERVER_GET_INFO) {
|
||||
executionResult = executeBrowserInfoTool();
|
||||
} else if (toolName === BuiltInTool.READ_MEDIA) {
|
||||
} else if (toolName === BuiltInTool.BROWSER_READ_MEDIA) {
|
||||
executionResult = await ReadMediaService.executeTool(
|
||||
args,
|
||||
{
|
||||
|
||||
@@ -28,11 +28,11 @@ import { SvelteMap, SvelteSet } from 'svelte/reactivity';
|
||||
/** Stable selection identity for a tool, shared by the disabled set and the permission store */
|
||||
|
||||
class ToolsStore {
|
||||
private _builtinTools = $state<OpenAIToolDefinition[]>([]);
|
||||
private _serverTools = $state<OpenAIToolDefinition[]>([]);
|
||||
private _loading = $state(false);
|
||||
private _error = $state<string | null>(null);
|
||||
private _disabledTools = $state(new SvelteSet<string>());
|
||||
// builtin tools that resolve their paths against the working directory,
|
||||
// server tools that resolve their paths against the working directory,
|
||||
// as declared by the server in its `/tools` listing
|
||||
private _cwdAwareTools = $state(new SvelteSet<string>());
|
||||
private _toolsEndpointUnreachable = $state(false);
|
||||
@@ -58,7 +58,7 @@ class ToolsStore {
|
||||
console.error('[ToolsStore] Failed to load disabled tools from localStorage:', err);
|
||||
}
|
||||
|
||||
this.fetchBuiltinTools();
|
||||
this.fetchServerTools();
|
||||
}
|
||||
|
||||
private persistDisabledTools(): void {
|
||||
@@ -78,10 +78,10 @@ class ToolsStore {
|
||||
return serverId ? `mcp-${serverId}:${name}` : `mcp:${name}`;
|
||||
case ToolSource.CUSTOM:
|
||||
return `custom:${name}`;
|
||||
case ToolSource.FRONTEND:
|
||||
return `frontend:${name}`;
|
||||
case ToolSource.BROWSER:
|
||||
return `browser:${name}`;
|
||||
default:
|
||||
return `builtin:${name}`;
|
||||
return `server:${name}`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,8 +164,8 @@ class ToolsStore {
|
||||
};
|
||||
}
|
||||
|
||||
get builtinTools(): OpenAIToolDefinition[] {
|
||||
return this._builtinTools;
|
||||
get serverTools(): OpenAIToolDefinition[] {
|
||||
return this._serverTools;
|
||||
}
|
||||
|
||||
get serverHome(): string | null {
|
||||
@@ -176,7 +176,7 @@ class ToolsStore {
|
||||
return this.mcpEntries().map((e) => e.definition);
|
||||
}
|
||||
|
||||
get frontendTools(): OpenAIToolDefinition[] {
|
||||
get browserTools(): OpenAIToolDefinition[] {
|
||||
const tools: OpenAIToolDefinition[] = [buildGetDatetimeToolDefinition()];
|
||||
|
||||
if (settingsStore.config.jsSandboxEnabled) {
|
||||
@@ -188,25 +188,25 @@ class ToolsStore {
|
||||
if (readMedia) tools.push(readMedia);
|
||||
|
||||
// provide browser's get_info tool if server doesn't provide one
|
||||
if (!this.hasBuiltinTool(BuiltInTool.GET_INFO)) {
|
||||
if (!this.hasServerTool(BuiltInTool.SERVER_GET_INFO)) {
|
||||
tools.push(buildBrowserInfoToolDefinition());
|
||||
}
|
||||
|
||||
return tools;
|
||||
}
|
||||
|
||||
private hasBuiltinTool(name: BuiltInTool): boolean {
|
||||
return this._builtinTools.some((def) => def.function.name === name);
|
||||
private hasServerTool(name: BuiltInTool): boolean {
|
||||
return this._serverTools.some((def) => def.function.name === name);
|
||||
}
|
||||
|
||||
/**
|
||||
* `read_media` runs in the frontend on top of the server's `read_file`, so it
|
||||
* `read_media` runs in the browser on top of the server's `read_file`, so it
|
||||
* exists only when that tool is served and the active model can perceive the
|
||||
* bytes. The server cannot make this call - it does not know which model the
|
||||
* conversation uses.
|
||||
*/
|
||||
private readMediaTool(): OpenAIToolDefinition | null {
|
||||
if (!this.hasBuiltinTool(BuiltInTool.READ_FILE)) return null;
|
||||
if (!this.hasServerTool(BuiltInTool.SERVER_READ_FILE)) return null;
|
||||
|
||||
const model = modelsStore.selectedModelName ?? modelsStore.models[0]?.model ?? '';
|
||||
|
||||
@@ -304,23 +304,23 @@ class ToolsStore {
|
||||
entries.push(entry);
|
||||
};
|
||||
|
||||
for (const def of this._builtinTools) {
|
||||
for (const def of this._serverTools) {
|
||||
const name = def.function.name;
|
||||
|
||||
push({
|
||||
definition: def,
|
||||
key: this.toolKey(ToolSource.BUILTIN, name),
|
||||
source: ToolSource.BUILTIN
|
||||
key: this.toolKey(ToolSource.SERVER, name),
|
||||
source: ToolSource.SERVER
|
||||
});
|
||||
}
|
||||
|
||||
for (const def of this.frontendTools) {
|
||||
for (const def of this.browserTools) {
|
||||
const name = def.function.name;
|
||||
|
||||
push({
|
||||
definition: def,
|
||||
key: this.toolKey(ToolSource.FRONTEND, name),
|
||||
source: ToolSource.FRONTEND
|
||||
key: this.toolKey(ToolSource.BROWSER, name),
|
||||
source: ToolSource.BROWSER
|
||||
});
|
||||
}
|
||||
|
||||
@@ -384,17 +384,17 @@ class ToolsStore {
|
||||
return entry.serverName ?? '';
|
||||
case ToolSource.CUSTOM:
|
||||
return TOOL_GROUP_LABELS[ToolSource.CUSTOM];
|
||||
case ToolSource.FRONTEND:
|
||||
return TOOL_GROUP_LABELS[ToolSource.FRONTEND];
|
||||
case ToolSource.BROWSER:
|
||||
return TOOL_GROUP_LABELS[ToolSource.BROWSER];
|
||||
default:
|
||||
return TOOL_GROUP_LABELS[ToolSource.BUILTIN];
|
||||
return TOOL_GROUP_LABELS[ToolSource.SERVER];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enabled tool definitions for sending to the LLM.
|
||||
* MCP tool schemas are normalized here so the wire payload is consistent
|
||||
* across all four sources (built-in, frontend/sandbox, MCP, custom JSON).
|
||||
* across all four sources (server, browser/sandbox, MCP, custom JSON).
|
||||
* The API identifies tools by name, so a name is sent at most once.
|
||||
*/
|
||||
getEnabledToolsForLLM(): OpenAIToolDefinition[] {
|
||||
@@ -417,8 +417,8 @@ class ToolsStore {
|
||||
result.push(def);
|
||||
};
|
||||
|
||||
for (const def of this._builtinTools) take(def);
|
||||
for (const def of this.frontendTools) take(def);
|
||||
for (const def of this._serverTools) take(def);
|
||||
for (const def of this.browserTools) take(def);
|
||||
// mcpEntries() over mcpStore directly so wire shape stays normalized and aligned with the tools UI.
|
||||
for (const entry of this.mcpEntries()) take(entry.definition);
|
||||
for (const def of this.customTools) take(def);
|
||||
@@ -542,11 +542,11 @@ class ToolsStore {
|
||||
|
||||
if (entry.serverName) return mcpStore.getServerDisplayName(entry.serverName);
|
||||
|
||||
if (entry.source === ToolSource.BUILTIN) return TOOL_SERVER_LABELS[ToolSource.BUILTIN];
|
||||
if (entry.source === ToolSource.SERVER) return TOOL_SERVER_LABELS[ToolSource.SERVER];
|
||||
|
||||
if (entry.source === ToolSource.CUSTOM) return TOOL_SERVER_LABELS[ToolSource.CUSTOM];
|
||||
|
||||
if (entry.source === ToolSource.FRONTEND) return TOOL_SERVER_LABELS[ToolSource.FRONTEND];
|
||||
if (entry.source === ToolSource.BROWSER) return TOOL_SERVER_LABELS[ToolSource.BROWSER];
|
||||
|
||||
return '';
|
||||
}
|
||||
@@ -556,27 +556,27 @@ class ToolsStore {
|
||||
return this.findEntryByName(toolName)?.key ?? null;
|
||||
}
|
||||
|
||||
/** Check if there are any enabled tools available (builtin, MCP, or custom) */
|
||||
/** Check if there are any enabled tools available (server, MCP, or custom) */
|
||||
get hasEnabledTools(): boolean {
|
||||
return this.getEnabledToolsForLLM().length > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a working directory is worth setting: at least one builtin tool
|
||||
* Check if a working directory is worth setting: at least one server tool
|
||||
* that reads it is both served and left enabled by the user.
|
||||
*/
|
||||
get hasEnabledCwdTools(): boolean {
|
||||
return this._builtinTools.some((def) => {
|
||||
return this._serverTools.some((def) => {
|
||||
const name = def.function.name;
|
||||
|
||||
return (
|
||||
this._cwdAwareTools.has(name) &&
|
||||
!this._disabledTools.has(this.toolKey(ToolSource.BUILTIN, name))
|
||||
!this._disabledTools.has(this.toolKey(ToolSource.SERVER, name))
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
async fetchBuiltinTools(): Promise<void> {
|
||||
async fetchServerTools(): Promise<void> {
|
||||
if (this._loading) return;
|
||||
|
||||
this._loading = true;
|
||||
@@ -586,7 +586,7 @@ class ToolsStore {
|
||||
try {
|
||||
const toolInfos = await ToolsService.list();
|
||||
|
||||
this._builtinTools = toolInfos.map((info) => info.definition);
|
||||
this._serverTools = toolInfos.map((info) => info.definition);
|
||||
this._cwdAwareTools = new SvelteSet(
|
||||
toolInfos.filter((info) => info.uses_cwd).map((info) => info.tool)
|
||||
);
|
||||
@@ -599,9 +599,9 @@ class ToolsStore {
|
||||
// TODO: check status code instead of relying on message
|
||||
if (errorMessage.includes('this feature is disabled')) {
|
||||
this._toolsEndpointUnreachable = true;
|
||||
console.info('[ToolsStore] Built-in tools are disabled on the server');
|
||||
console.info('[ToolsStore] Server tools are disabled on the server');
|
||||
} else {
|
||||
console.error('[ToolsStore] Failed to fetch built-in tools:', err);
|
||||
console.error('[ToolsStore] Failed to fetch server tools:', err);
|
||||
}
|
||||
} finally {
|
||||
this._loading = false;
|
||||
@@ -618,7 +618,7 @@ class ToolsStore {
|
||||
if (this._serverHome !== undefined) return this._serverHome;
|
||||
|
||||
try {
|
||||
const res = await ToolsService.executeToolRaw(BuiltInTool.FILE_GLOB_SEARCH, {
|
||||
const res = await ToolsService.executeToolRaw(BuiltInTool.SERVER_FILE_GLOB_SEARCH, {
|
||||
limit: 1,
|
||||
max_depth: 1,
|
||||
path: HOME_TILDE,
|
||||
|
||||
@@ -147,7 +147,7 @@ export type {
|
||||
ServerStatus,
|
||||
ToolCallParams,
|
||||
ToolExecutionResult,
|
||||
ServerBuiltinToolInfo,
|
||||
ServerToolInfo,
|
||||
Tool,
|
||||
Prompt,
|
||||
GetPromptResult,
|
||||
@@ -208,7 +208,7 @@ export type {
|
||||
export type { DesktopIconStripItem } from './navigation';
|
||||
|
||||
// Tools types
|
||||
export type { ToolEntry, ToolGroup, BuiltinToolUiEntry } from './tools';
|
||||
export type { ToolEntry, ToolGroup, ToolUiEntry } from './tools';
|
||||
|
||||
// Reasoning
|
||||
export type { ReasoningEffortLevel } from './reasoning';
|
||||
|
||||
Vendored
+2
-2
@@ -285,10 +285,10 @@ export interface ToolExecutionResult {
|
||||
isError: boolean;
|
||||
}
|
||||
|
||||
export interface ServerBuiltinToolInfo {
|
||||
export interface ServerToolInfo {
|
||||
display_name: string;
|
||||
tool: string;
|
||||
type: ToolSource.BUILTIN;
|
||||
type: ToolSource.SERVER;
|
||||
permissions: {
|
||||
write: boolean;
|
||||
};
|
||||
|
||||
Vendored
+4
-4
@@ -3,12 +3,12 @@ import type { ToolSource } from '$lib/enums';
|
||||
import type { Component } from 'svelte';
|
||||
|
||||
/**
|
||||
* UI metadata for a built-in or frontend tool, keyed by its `BuiltInTool` id.
|
||||
* UI metadata for a server or browser tool, keyed by its `BuiltInTool` id.
|
||||
*/
|
||||
export interface BuiltinToolUiEntry {
|
||||
export interface ToolUiEntry {
|
||||
icon: Component;
|
||||
label: string;
|
||||
source: ToolSource.BUILTIN | ToolSource.FRONTEND;
|
||||
source: ToolSource.SERVER | ToolSource.BROWSER;
|
||||
}
|
||||
|
||||
export interface ToolEntry {
|
||||
@@ -17,7 +17,7 @@ export interface ToolEntry {
|
||||
serverName?: string;
|
||||
/** For MCP tools, the server ID (used for permission keys) */
|
||||
serverId?: string;
|
||||
/** Stable selection identity: builtin:name, mcp-<serverId>:name, mcp:name, custom:name */
|
||||
/** Stable selection identity: server:name, mcp-<serverId>:name, mcp:name, custom:name */
|
||||
key: string;
|
||||
definition: OpenAIToolDefinition;
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
import { BUILTIN_TOOL_UI } from '$lib/constants';
|
||||
import type { BuiltinToolUiEntry } from '$lib/types';
|
||||
|
||||
/**
|
||||
* Resolve the UI metadata (label + icon) for a built-in tool by its name.
|
||||
* Falls back to null for unknown or non-built-in tools so callers can render
|
||||
* a generic chrome instead.
|
||||
*/
|
||||
export function getBuiltinToolUi(toolName: string | undefined): BuiltinToolUiEntry | null {
|
||||
if (!toolName) return null;
|
||||
|
||||
return (BUILTIN_TOOL_UI as Record<string, BuiltinToolUiEntry>)[toolName] ?? null;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Frontend executor for the `get_datetime` tool. It runs in the browser, so it
|
||||
* Browser executor for the `get_datetime` tool. It runs in the browser, so it
|
||||
* reports the user's own clock and time zone instead of the server's UTC time -
|
||||
* a chat about "tomorrow" means the user's tomorrow, not the host's.
|
||||
*
|
||||
|
||||
@@ -42,7 +42,7 @@ export async function runGlobSearch(
|
||||
}
|
||||
|
||||
const res = await ToolsService.executeToolRaw(
|
||||
BuiltInTool.FILE_GLOB_SEARCH,
|
||||
BuiltInTool.SERVER_FILE_GLOB_SEARCH,
|
||||
{ include: args.include, limit, max_depth: args.maxDepth, path: args.path, type },
|
||||
signal
|
||||
);
|
||||
|
||||
@@ -127,7 +127,7 @@ export { sanitizeKeyValuePairKey, sanitizeKeyValuePairValue } from './sanitize';
|
||||
// Image error fallback utilities
|
||||
export { getImageErrorFallbackHtml } from './image-error-fallback';
|
||||
|
||||
// SSE-with-JSON stream iterator (used by built-in tool streaming, decoupled
|
||||
// SSE-with-JSON stream iterator (used by server tool streaming, decoupled
|
||||
// from chat.service.ts which embeds its own SSE parser for resume support)
|
||||
export { parseSseJsonStream } from './sse';
|
||||
|
||||
@@ -310,7 +310,7 @@ export {
|
||||
withAbortSignal
|
||||
} from './abort';
|
||||
|
||||
// Tool-call meta utilities. Parsers for each built-in tool live next to
|
||||
// Tool-call meta utilities. Parsers for each server tool live next to
|
||||
// their renderer family under
|
||||
// `src/lib/components/app/chat/ChatMessages/ChatMessage/ChatMessageToolCall/parsers/`.
|
||||
// This module only carries the helpers that genuinely cross tool
|
||||
@@ -321,7 +321,7 @@ export { tryParseToolResultObject } from './tool-call-meta';
|
||||
// Per-tool UI metadata (label + icon) used by the tool-call chrome.
|
||||
// Re-exported through $lib/utils so renderer components can read the
|
||||
// label without depending on $lib/constants directly.
|
||||
export { getBuiltinToolUi } from './built-in-tools';
|
||||
export { getToolUi } from './tool-ui';
|
||||
|
||||
// Chat command picker
|
||||
|
||||
@@ -331,7 +331,7 @@ export { getChatCommands } from './chat-commands';
|
||||
// SANDBOX_TOOL_DEFINITION is deprecated; kept for backward compatibility.
|
||||
export { buildSandboxToolDefinition, SANDBOX_TOOL_DEFINITION } from './sandbox-tool';
|
||||
|
||||
// Frontend `get_datetime` executor (the browser clock, not the server's)
|
||||
// Browser `get_datetime` executor (the browser clock, not the server's)
|
||||
export { executeGetDatetimeTool } from './get-datetime';
|
||||
|
||||
// Browser fallback for the server's get_info tool
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import { TOOL_UI } from '$lib/constants';
|
||||
import type { ToolUiEntry } from '$lib/types';
|
||||
|
||||
/**
|
||||
* Resolve the UI metadata (label + icon) for a server or browser tool by its
|
||||
* name. Falls back to null for unknown tools so callers can render a generic
|
||||
* chrome instead.
|
||||
*/
|
||||
export function getToolUi(toolName: string | undefined): ToolUiEntry | null {
|
||||
if (!toolName) return null;
|
||||
|
||||
return (TOOL_UI as Record<string, ToolUiEntry>)[toolName] ?? null;
|
||||
}
|
||||
Reference in New Issue
Block a user