mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-12 07:20:45 +00:00
Gateway/UI: data-driven agents tools catalog with provenance (openclaw#24199) thanks @Takhoffman
Verified: - pnpm install --frozen-lockfile - pnpm build - gh pr checks 24199 --watch --fail-fast Co-authored-by: Takhoffman <781889+Takhoffman@users.noreply.github.com> Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
This commit is contained in:
@@ -13,7 +13,7 @@ import {
|
||||
import { handleAgentEvent, resetToolStream, type AgentEventPayload } from "./app-tool-stream.ts";
|
||||
import type { OpenClawApp } from "./app.ts";
|
||||
import { shouldReloadHistoryForFinalEvent } from "./chat-event-reload.ts";
|
||||
import { loadAgents } from "./controllers/agents.ts";
|
||||
import { loadAgents, loadToolsCatalog } from "./controllers/agents.ts";
|
||||
import { loadAssistantIdentity } from "./controllers/assistant-identity.ts";
|
||||
import { loadChatHistory } from "./controllers/chat.ts";
|
||||
import { handleChatEvent, type ChatEventPayload } from "./controllers/chat.ts";
|
||||
@@ -62,6 +62,9 @@ type GatewayHost = {
|
||||
agentsLoading: boolean;
|
||||
agentsList: AgentsListResult | null;
|
||||
agentsError: string | null;
|
||||
toolsCatalogLoading: boolean;
|
||||
toolsCatalogError: string | null;
|
||||
toolsCatalogResult: import("./types.ts").ToolsCatalogResult | null;
|
||||
debugHealth: HealthSnapshot | null;
|
||||
assistantName: string;
|
||||
assistantAvatar: string | null;
|
||||
@@ -166,6 +169,7 @@ export function connectGateway(host: GatewayHost) {
|
||||
resetToolStream(host as unknown as Parameters<typeof resetToolStream>[0]);
|
||||
void loadAssistantIdentity(host as unknown as OpenClawApp);
|
||||
void loadAgents(host as unknown as OpenClawApp);
|
||||
void loadToolsCatalog(host as unknown as OpenClawApp);
|
||||
void loadNodes(host as unknown as OpenClawApp, { quiet: true });
|
||||
void loadDevices(host as unknown as OpenClawApp, { quiet: true });
|
||||
void refreshActiveTab(host as unknown as Parameters<typeof refreshActiveTab>[0]);
|
||||
|
||||
@@ -8,7 +8,7 @@ import type { AppViewState } from "./app-view-state.ts";
|
||||
import { loadAgentFileContent, loadAgentFiles, saveAgentFile } from "./controllers/agent-files.ts";
|
||||
import { loadAgentIdentities, loadAgentIdentity } from "./controllers/agent-identity.ts";
|
||||
import { loadAgentSkills } from "./controllers/agent-skills.ts";
|
||||
import { loadAgents } from "./controllers/agents.ts";
|
||||
import { loadAgents, loadToolsCatalog } from "./controllers/agents.ts";
|
||||
import { loadChannels } from "./controllers/channels.ts";
|
||||
import { loadChatHistory } from "./controllers/chat.ts";
|
||||
import {
|
||||
@@ -528,9 +528,18 @@ export function renderApp(state: AppViewState) {
|
||||
agentSkillsReport: state.agentSkillsReport,
|
||||
agentSkillsError: state.agentSkillsError,
|
||||
agentSkillsAgentId: state.agentSkillsAgentId,
|
||||
toolsCatalogLoading: state.toolsCatalogLoading,
|
||||
toolsCatalogError: state.toolsCatalogError,
|
||||
toolsCatalogResult: state.toolsCatalogResult,
|
||||
skillsFilter: state.skillsFilter,
|
||||
onRefresh: async () => {
|
||||
await loadAgents(state);
|
||||
const nextSelected =
|
||||
state.agentsSelectedId ??
|
||||
state.agentsList?.defaultId ??
|
||||
state.agentsList?.agents?.[0]?.id ??
|
||||
null;
|
||||
await loadToolsCatalog(state, nextSelected);
|
||||
const agentIds = state.agentsList?.agents?.map((entry) => entry.id) ?? [];
|
||||
if (agentIds.length > 0) {
|
||||
void loadAgentIdentities(state, agentIds);
|
||||
@@ -551,6 +560,9 @@ export function renderApp(state: AppViewState) {
|
||||
state.agentSkillsError = null;
|
||||
state.agentSkillsAgentId = null;
|
||||
void loadAgentIdentity(state, agentId);
|
||||
if (state.agentsPanel === "tools") {
|
||||
void loadToolsCatalog(state, agentId);
|
||||
}
|
||||
if (state.agentsPanel === "files") {
|
||||
void loadAgentFiles(state, agentId);
|
||||
}
|
||||
@@ -570,6 +582,9 @@ export function renderApp(state: AppViewState) {
|
||||
void loadAgentFiles(state, resolvedAgentId);
|
||||
}
|
||||
}
|
||||
if (panel === "tools") {
|
||||
void loadToolsCatalog(state, resolvedAgentId);
|
||||
}
|
||||
if (panel === "skills") {
|
||||
if (resolvedAgentId) {
|
||||
void loadAgentSkills(state, resolvedAgentId);
|
||||
|
||||
@@ -9,7 +9,7 @@ import { scheduleChatScroll, scheduleLogsScroll } from "./app-scroll.ts";
|
||||
import type { OpenClawApp } from "./app.ts";
|
||||
import { loadAgentIdentities, loadAgentIdentity } from "./controllers/agent-identity.ts";
|
||||
import { loadAgentSkills } from "./controllers/agent-skills.ts";
|
||||
import { loadAgents } from "./controllers/agents.ts";
|
||||
import { loadAgents, loadToolsCatalog } from "./controllers/agents.ts";
|
||||
import { loadChannels } from "./controllers/channels.ts";
|
||||
import { loadConfig, loadConfigSchema } from "./controllers/config.ts";
|
||||
import {
|
||||
@@ -204,6 +204,7 @@ export async function refreshActiveTab(host: SettingsHost) {
|
||||
}
|
||||
if (host.tab === "agents") {
|
||||
await loadAgents(host as unknown as OpenClawApp);
|
||||
await loadToolsCatalog(host as unknown as OpenClawApp);
|
||||
await loadConfig(host as unknown as OpenClawApp);
|
||||
const agentIds = host.agentsList?.agents?.map((entry) => entry.id) ?? [];
|
||||
if (agentIds.length > 0) {
|
||||
|
||||
@@ -37,6 +37,7 @@ import type {
|
||||
SessionUsageTimeSeries,
|
||||
SessionsListResult,
|
||||
SkillStatusReport,
|
||||
ToolsCatalogResult,
|
||||
StatusSummary,
|
||||
} from "./types.ts";
|
||||
import type { ChatAttachment, ChatQueueItem, CronFormState } from "./ui-types.ts";
|
||||
@@ -137,6 +138,9 @@ export type AppViewState = {
|
||||
agentsList: AgentsListResult | null;
|
||||
agentsError: string | null;
|
||||
agentsSelectedId: string | null;
|
||||
toolsCatalogLoading: boolean;
|
||||
toolsCatalogError: string | null;
|
||||
toolsCatalogResult: ToolsCatalogResult | null;
|
||||
agentsPanel: "overview" | "files" | "tools" | "skills" | "channels" | "cron";
|
||||
agentFilesLoading: boolean;
|
||||
agentFilesError: string | null;
|
||||
|
||||
@@ -78,6 +78,7 @@ import type {
|
||||
ChannelsStatusSnapshot,
|
||||
SessionsListResult,
|
||||
SkillStatusReport,
|
||||
ToolsCatalogResult,
|
||||
StatusSummary,
|
||||
NostrProfile,
|
||||
} from "./types.ts";
|
||||
@@ -217,6 +218,9 @@ export class OpenClawApp extends LitElement {
|
||||
@state() agentsList: AgentsListResult | null = null;
|
||||
@state() agentsError: string | null = null;
|
||||
@state() agentsSelectedId: string | null = null;
|
||||
@state() toolsCatalogLoading = false;
|
||||
@state() toolsCatalogError: string | null = null;
|
||||
@state() toolsCatalogResult: ToolsCatalogResult | null = null;
|
||||
@state() agentsPanel: "overview" | "files" | "tools" | "skills" | "channels" | "cron" =
|
||||
"overview";
|
||||
@state() agentFilesLoading = false;
|
||||
|
||||
61
ui/src/ui/controllers/agents.test.ts
Normal file
61
ui/src/ui/controllers/agents.test.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { loadToolsCatalog } from "./agents.ts";
|
||||
import type { AgentsState } from "./agents.ts";
|
||||
|
||||
function createState(): { state: AgentsState; request: ReturnType<typeof vi.fn> } {
|
||||
const request = vi.fn();
|
||||
const state: AgentsState = {
|
||||
client: {
|
||||
request,
|
||||
} as unknown as AgentsState["client"],
|
||||
connected: true,
|
||||
agentsLoading: false,
|
||||
agentsError: null,
|
||||
agentsList: null,
|
||||
agentsSelectedId: "main",
|
||||
toolsCatalogLoading: false,
|
||||
toolsCatalogError: null,
|
||||
toolsCatalogResult: null,
|
||||
};
|
||||
return { state, request };
|
||||
}
|
||||
|
||||
describe("loadToolsCatalog", () => {
|
||||
it("loads catalog and stores result", async () => {
|
||||
const { state, request } = createState();
|
||||
const payload = {
|
||||
agentId: "main",
|
||||
profiles: [{ id: "full", label: "Full" }],
|
||||
groups: [
|
||||
{
|
||||
id: "media",
|
||||
label: "Media",
|
||||
source: "core",
|
||||
tools: [{ id: "tts", label: "tts", description: "Text-to-speech", source: "core" }],
|
||||
},
|
||||
],
|
||||
};
|
||||
request.mockResolvedValue(payload);
|
||||
|
||||
await loadToolsCatalog(state, "main");
|
||||
|
||||
expect(request).toHaveBeenCalledWith("tools.catalog", {
|
||||
agentId: "main",
|
||||
includePlugins: true,
|
||||
});
|
||||
expect(state.toolsCatalogResult).toEqual(payload);
|
||||
expect(state.toolsCatalogError).toBeNull();
|
||||
expect(state.toolsCatalogLoading).toBe(false);
|
||||
});
|
||||
|
||||
it("captures request errors for fallback UI handling", async () => {
|
||||
const { state, request } = createState();
|
||||
request.mockRejectedValue(new Error("gateway unavailable"));
|
||||
|
||||
await loadToolsCatalog(state, "main");
|
||||
|
||||
expect(state.toolsCatalogResult).toBeNull();
|
||||
expect(state.toolsCatalogError).toContain("gateway unavailable");
|
||||
expect(state.toolsCatalogLoading).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { GatewayBrowserClient } from "../gateway.ts";
|
||||
import type { AgentsListResult } from "../types.ts";
|
||||
import type { AgentsListResult, ToolsCatalogResult } from "../types.ts";
|
||||
|
||||
export type AgentsState = {
|
||||
client: GatewayBrowserClient | null;
|
||||
@@ -8,6 +8,9 @@ export type AgentsState = {
|
||||
agentsError: string | null;
|
||||
agentsList: AgentsListResult | null;
|
||||
agentsSelectedId: string | null;
|
||||
toolsCatalogLoading: boolean;
|
||||
toolsCatalogError: string | null;
|
||||
toolsCatalogResult: ToolsCatalogResult | null;
|
||||
};
|
||||
|
||||
export async function loadAgents(state: AgentsState) {
|
||||
@@ -35,3 +38,27 @@ export async function loadAgents(state: AgentsState) {
|
||||
state.agentsLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
export async function loadToolsCatalog(state: AgentsState, agentId?: string | null) {
|
||||
if (!state.client || !state.connected) {
|
||||
return;
|
||||
}
|
||||
if (state.toolsCatalogLoading) {
|
||||
return;
|
||||
}
|
||||
state.toolsCatalogLoading = true;
|
||||
state.toolsCatalogError = null;
|
||||
try {
|
||||
const res = await state.client.request<ToolsCatalogResult>("tools.catalog", {
|
||||
agentId: agentId ?? state.agentsSelectedId ?? undefined,
|
||||
includePlugins: true,
|
||||
});
|
||||
if (res) {
|
||||
state.toolsCatalogResult = res;
|
||||
}
|
||||
} catch (err) {
|
||||
state.toolsCatalogError = String(err);
|
||||
} finally {
|
||||
state.toolsCatalogLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -345,6 +345,35 @@ export type AgentsListResult = {
|
||||
agents: GatewayAgentRow[];
|
||||
};
|
||||
|
||||
export type ToolCatalogProfile = {
|
||||
id: "minimal" | "coding" | "messaging" | "full";
|
||||
label: string;
|
||||
};
|
||||
|
||||
export type ToolCatalogEntry = {
|
||||
id: string;
|
||||
label: string;
|
||||
description: string;
|
||||
source: "core" | "plugin";
|
||||
pluginId?: string;
|
||||
optional?: boolean;
|
||||
defaultProfiles: Array<"minimal" | "coding" | "messaging" | "full">;
|
||||
};
|
||||
|
||||
export type ToolCatalogGroup = {
|
||||
id: string;
|
||||
label: string;
|
||||
source: "core" | "plugin";
|
||||
pluginId?: string;
|
||||
tools: ToolCatalogEntry[];
|
||||
};
|
||||
|
||||
export type ToolsCatalogResult = {
|
||||
agentId: string;
|
||||
profiles: ToolCatalogProfile[];
|
||||
groups: ToolCatalogGroup[];
|
||||
};
|
||||
|
||||
export type AgentIdentityResult = {
|
||||
agentId: string;
|
||||
name: string;
|
||||
|
||||
102
ui/src/ui/views/agents-panels-tools-skills.browser.test.ts
Normal file
102
ui/src/ui/views/agents-panels-tools-skills.browser.test.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
import { render } from "lit";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { renderAgentTools } from "./agents-panels-tools-skills.ts";
|
||||
|
||||
function createBaseParams(overrides: Partial<Parameters<typeof renderAgentTools>[0]> = {}) {
|
||||
return {
|
||||
agentId: "main",
|
||||
configForm: {
|
||||
agents: {
|
||||
list: [{ id: "main", tools: { profile: "full" } }],
|
||||
},
|
||||
} as Record<string, unknown>,
|
||||
configLoading: false,
|
||||
configSaving: false,
|
||||
configDirty: false,
|
||||
toolsCatalogLoading: false,
|
||||
toolsCatalogError: null,
|
||||
toolsCatalogResult: null,
|
||||
onProfileChange: () => undefined,
|
||||
onOverridesChange: () => undefined,
|
||||
onConfigReload: () => undefined,
|
||||
onConfigSave: () => undefined,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
describe("agents tools panel (browser)", () => {
|
||||
it("renders per-tool provenance badges and optional marker", async () => {
|
||||
const container = document.createElement("div");
|
||||
render(
|
||||
renderAgentTools(
|
||||
createBaseParams({
|
||||
toolsCatalogResult: {
|
||||
agentId: "main",
|
||||
profiles: [
|
||||
{ id: "minimal", label: "Minimal" },
|
||||
{ id: "coding", label: "Coding" },
|
||||
{ id: "messaging", label: "Messaging" },
|
||||
{ id: "full", label: "Full" },
|
||||
],
|
||||
groups: [
|
||||
{
|
||||
id: "media",
|
||||
label: "Media",
|
||||
source: "core",
|
||||
tools: [
|
||||
{
|
||||
id: "tts",
|
||||
label: "tts",
|
||||
description: "Text-to-speech conversion",
|
||||
source: "core",
|
||||
defaultProfiles: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "plugin:voice-call",
|
||||
label: "voice-call",
|
||||
source: "plugin",
|
||||
pluginId: "voice-call",
|
||||
tools: [
|
||||
{
|
||||
id: "voice_call",
|
||||
label: "voice_call",
|
||||
description: "Voice call tool",
|
||||
source: "plugin",
|
||||
pluginId: "voice-call",
|
||||
optional: true,
|
||||
defaultProfiles: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
),
|
||||
container,
|
||||
);
|
||||
await Promise.resolve();
|
||||
|
||||
const text = container.textContent ?? "";
|
||||
expect(text).toContain("core");
|
||||
expect(text).toContain("plugin:voice-call");
|
||||
expect(text).toContain("optional");
|
||||
});
|
||||
|
||||
it("shows fallback warning when runtime catalog fails", async () => {
|
||||
const container = document.createElement("div");
|
||||
render(
|
||||
renderAgentTools(
|
||||
createBaseParams({
|
||||
toolsCatalogError: "unavailable",
|
||||
toolsCatalogResult: null,
|
||||
}),
|
||||
),
|
||||
container,
|
||||
);
|
||||
await Promise.resolve();
|
||||
|
||||
expect(container.textContent ?? "").toContain("Could not load runtime tool catalog");
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
import { html, nothing } from "lit";
|
||||
import { normalizeToolName } from "../../../../src/agents/tool-policy-shared.js";
|
||||
import type { SkillStatusEntry, SkillStatusReport } from "../types.ts";
|
||||
import type { SkillStatusEntry, SkillStatusReport, ToolsCatalogResult } from "../types.ts";
|
||||
import {
|
||||
isAllowedByPolicy,
|
||||
matchesList,
|
||||
@@ -23,6 +23,9 @@ export function renderAgentTools(params: {
|
||||
configLoading: boolean;
|
||||
configSaving: boolean;
|
||||
configDirty: boolean;
|
||||
toolsCatalogLoading: boolean;
|
||||
toolsCatalogError: string | null;
|
||||
toolsCatalogResult: ToolsCatalogResult | null;
|
||||
onProfileChange: (agentId: string, profile: string | null, clearAllow: boolean) => void;
|
||||
onOverridesChange: (agentId: string, alsoAllow: string[], deny: string[]) => void;
|
||||
onConfigReload: () => void;
|
||||
@@ -50,7 +53,17 @@ export function renderAgentTools(params: {
|
||||
const basePolicy = hasAgentAllow
|
||||
? { allow: agentTools.allow ?? [], deny: agentTools.deny ?? [] }
|
||||
: (resolveToolProfile(profile) ?? undefined);
|
||||
const toolIds = TOOL_SECTIONS.flatMap((section) => section.tools.map((tool) => tool.id));
|
||||
const sections =
|
||||
params.toolsCatalogResult?.groups?.length &&
|
||||
params.toolsCatalogResult.agentId === params.agentId
|
||||
? params.toolsCatalogResult.groups
|
||||
: TOOL_SECTIONS;
|
||||
const profileOptions =
|
||||
params.toolsCatalogResult?.profiles?.length &&
|
||||
params.toolsCatalogResult.agentId === params.agentId
|
||||
? params.toolsCatalogResult.profiles
|
||||
: PROFILE_OPTIONS;
|
||||
const toolIds = sections.flatMap((section) => section.tools.map((tool) => tool.id));
|
||||
|
||||
const resolveAllowed = (toolId: string) => {
|
||||
const baseAllowed = isAllowedByPolicy(toolId, basePolicy);
|
||||
@@ -139,6 +152,15 @@ export function renderAgentTools(params: {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
${
|
||||
params.toolsCatalogError
|
||||
? html`
|
||||
<div class="callout warn" style="margin-top: 12px">
|
||||
Could not load runtime tool catalog. Showing fallback list.
|
||||
</div>
|
||||
`
|
||||
: nothing
|
||||
}
|
||||
${
|
||||
!params.configForm
|
||||
? html`
|
||||
@@ -191,7 +213,7 @@ export function renderAgentTools(params: {
|
||||
<div class="agent-tools-presets" style="margin-top: 16px;">
|
||||
<div class="label">Quick Presets</div>
|
||||
<div class="agent-tools-buttons">
|
||||
${PROFILE_OPTIONS.map(
|
||||
${profileOptions.map(
|
||||
(option) => html`
|
||||
<button
|
||||
class="btn btn--sm ${profile === option.id ? "active" : ""}"
|
||||
@@ -213,18 +235,49 @@ export function renderAgentTools(params: {
|
||||
</div>
|
||||
|
||||
<div class="agent-tools-grid" style="margin-top: 20px;">
|
||||
${TOOL_SECTIONS.map(
|
||||
${sections.map(
|
||||
(section) =>
|
||||
html`
|
||||
<div class="agent-tools-section">
|
||||
<div class="agent-tools-header">${section.label}</div>
|
||||
<div class="agent-tools-header">
|
||||
${section.label}
|
||||
${
|
||||
"source" in section && section.source === "plugin"
|
||||
? html`
|
||||
<span class="mono" style="margin-left: 6px">plugin</span>
|
||||
`
|
||||
: nothing
|
||||
}
|
||||
</div>
|
||||
<div class="agent-tools-list">
|
||||
${section.tools.map((tool) => {
|
||||
const { allowed } = resolveAllowed(tool.id);
|
||||
const catalogTool = tool as {
|
||||
source?: "core" | "plugin";
|
||||
pluginId?: string;
|
||||
optional?: boolean;
|
||||
};
|
||||
const source =
|
||||
catalogTool.source === "plugin"
|
||||
? catalogTool.pluginId
|
||||
? `plugin:${catalogTool.pluginId}`
|
||||
: "plugin"
|
||||
: "core";
|
||||
const isOptional = catalogTool.optional === true;
|
||||
return html`
|
||||
<div class="agent-tool-row">
|
||||
<div>
|
||||
<div class="agent-tool-title mono">${tool.label}</div>
|
||||
<div class="agent-tool-title mono">
|
||||
${tool.label}
|
||||
<span class="mono" style="margin-left: 8px; opacity: 0.8;">${source}</span>
|
||||
${
|
||||
isOptional
|
||||
? html`
|
||||
<span class="mono" style="margin-left: 6px; opacity: 0.8">optional</span>
|
||||
`
|
||||
: nothing
|
||||
}
|
||||
</div>
|
||||
<div class="agent-tool-sub">${tool.description}</div>
|
||||
</div>
|
||||
<label class="cfg-toggle">
|
||||
@@ -245,6 +298,13 @@ export function renderAgentTools(params: {
|
||||
`,
|
||||
)}
|
||||
</div>
|
||||
${
|
||||
params.toolsCatalogLoading
|
||||
? html`
|
||||
<div class="card-sub" style="margin-top: 10px">Refreshing tool catalog…</div>
|
||||
`
|
||||
: nothing
|
||||
}
|
||||
</section>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { html } from "lit";
|
||||
import {
|
||||
listCoreToolSections,
|
||||
PROFILE_OPTIONS as TOOL_PROFILE_OPTIONS,
|
||||
} from "../../../../src/agents/tool-catalog.js";
|
||||
import {
|
||||
expandToolGroups,
|
||||
normalizeToolName,
|
||||
@@ -6,96 +10,9 @@ import {
|
||||
} from "../../../../src/agents/tool-policy-shared.js";
|
||||
import type { AgentIdentityResult, AgentsFilesListResult, AgentsListResult } from "../types.ts";
|
||||
|
||||
export const TOOL_SECTIONS = [
|
||||
{
|
||||
id: "fs",
|
||||
label: "Files",
|
||||
tools: [
|
||||
{ id: "read", label: "read", description: "Read file contents" },
|
||||
{ id: "write", label: "write", description: "Create or overwrite files" },
|
||||
{ id: "edit", label: "edit", description: "Make precise edits" },
|
||||
{ id: "apply_patch", label: "apply_patch", description: "Patch files (OpenAI)" },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "runtime",
|
||||
label: "Runtime",
|
||||
tools: [
|
||||
{ id: "exec", label: "exec", description: "Run shell commands" },
|
||||
{ id: "process", label: "process", description: "Manage background processes" },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "web",
|
||||
label: "Web",
|
||||
tools: [
|
||||
{ id: "web_search", label: "web_search", description: "Search the web" },
|
||||
{ id: "web_fetch", label: "web_fetch", description: "Fetch web content" },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "memory",
|
||||
label: "Memory",
|
||||
tools: [
|
||||
{ id: "memory_search", label: "memory_search", description: "Semantic search" },
|
||||
{ id: "memory_get", label: "memory_get", description: "Read memory files" },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "sessions",
|
||||
label: "Sessions",
|
||||
tools: [
|
||||
{ id: "sessions_list", label: "sessions_list", description: "List sessions" },
|
||||
{ id: "sessions_history", label: "sessions_history", description: "Session history" },
|
||||
{ id: "sessions_send", label: "sessions_send", description: "Send to session" },
|
||||
{ id: "sessions_spawn", label: "sessions_spawn", description: "Spawn sub-agent" },
|
||||
{ id: "session_status", label: "session_status", description: "Session status" },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "ui",
|
||||
label: "UI",
|
||||
tools: [
|
||||
{ id: "browser", label: "browser", description: "Control web browser" },
|
||||
{ id: "canvas", label: "canvas", description: "Control canvases" },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "messaging",
|
||||
label: "Messaging",
|
||||
tools: [{ id: "message", label: "message", description: "Send messages" }],
|
||||
},
|
||||
{
|
||||
id: "automation",
|
||||
label: "Automation",
|
||||
tools: [
|
||||
{ id: "cron", label: "cron", description: "Schedule tasks" },
|
||||
{ id: "gateway", label: "gateway", description: "Gateway control" },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "nodes",
|
||||
label: "Nodes",
|
||||
tools: [{ id: "nodes", label: "nodes", description: "Nodes + devices" }],
|
||||
},
|
||||
{
|
||||
id: "agents",
|
||||
label: "Agents",
|
||||
tools: [{ id: "agents_list", label: "agents_list", description: "List agents" }],
|
||||
},
|
||||
{
|
||||
id: "media",
|
||||
label: "Media",
|
||||
tools: [{ id: "image", label: "image", description: "Image understanding" }],
|
||||
},
|
||||
];
|
||||
export const TOOL_SECTIONS = listCoreToolSections();
|
||||
|
||||
export const PROFILE_OPTIONS = [
|
||||
{ id: "minimal", label: "Minimal" },
|
||||
{ id: "coding", label: "Coding" },
|
||||
{ id: "messaging", label: "Messaging" },
|
||||
{ id: "full", label: "Full" },
|
||||
] as const;
|
||||
export const PROFILE_OPTIONS = TOOL_PROFILE_OPTIONS;
|
||||
|
||||
type ToolPolicy = {
|
||||
allow?: string[];
|
||||
|
||||
@@ -7,6 +7,7 @@ import type {
|
||||
CronJob,
|
||||
CronStatus,
|
||||
SkillStatusReport,
|
||||
ToolsCatalogResult,
|
||||
} from "../types.ts";
|
||||
import {
|
||||
renderAgentFiles,
|
||||
@@ -62,6 +63,9 @@ export type AgentsProps = {
|
||||
agentSkillsReport: SkillStatusReport | null;
|
||||
agentSkillsError: string | null;
|
||||
agentSkillsAgentId: string | null;
|
||||
toolsCatalogLoading: boolean;
|
||||
toolsCatalogError: string | null;
|
||||
toolsCatalogResult: ToolsCatalogResult | null;
|
||||
skillsFilter: string;
|
||||
onRefresh: () => void;
|
||||
onSelectAgent: (agentId: string) => void;
|
||||
@@ -210,6 +214,9 @@ export function renderAgents(props: AgentsProps) {
|
||||
configLoading: props.configLoading,
|
||||
configSaving: props.configSaving,
|
||||
configDirty: props.configDirty,
|
||||
toolsCatalogLoading: props.toolsCatalogLoading,
|
||||
toolsCatalogError: props.toolsCatalogError,
|
||||
toolsCatalogResult: props.toolsCatalogResult,
|
||||
onProfileChange: props.onToolsProfileChange,
|
||||
onOverridesChange: props.onToolsOverridesChange,
|
||||
onConfigReload: props.onConfigReload,
|
||||
|
||||
Reference in New Issue
Block a user