mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-21 10:16:44 +00:00
fix(ui): localize agent controls
This commit is contained in:
+138
-43
@@ -15,6 +15,7 @@ import type {
|
||||
ToolsCatalogResult,
|
||||
} from "../../api/types.ts";
|
||||
import { controlUiPublicAssetPath } from "../../app/public-assets.ts";
|
||||
import { t } from "../../i18n/index.ts";
|
||||
import { resolveAgentAvatarUrl, resolveAssistantTextAvatar } from "../avatar.ts";
|
||||
import { buildQualifiedChatModelValue } from "../chat/model-ref.ts";
|
||||
import { normalizeLowercaseStringOrEmpty, normalizeOptionalString } from "../string-coerce.ts";
|
||||
@@ -37,95 +38,176 @@ export type AgentToolSection = {
|
||||
tools: AgentToolEntry[];
|
||||
};
|
||||
|
||||
const FALLBACK_TOOL_SECTIONS: AgentToolSection[] = [
|
||||
type FallbackToolEntry = Omit<AgentToolEntry, "description"> & {
|
||||
descriptionKey: string;
|
||||
};
|
||||
|
||||
type FallbackToolSection = Omit<AgentToolSection, "label" | "tools"> & {
|
||||
labelKey: string;
|
||||
tools: FallbackToolEntry[];
|
||||
};
|
||||
|
||||
const FALLBACK_TOOL_SECTIONS: FallbackToolSection[] = [
|
||||
{
|
||||
id: "fs",
|
||||
label: "Files",
|
||||
labelKey: "agents.toolCatalog.groups.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: "read", label: "read", descriptionKey: "agents.toolCatalog.descriptions.read" },
|
||||
{ id: "write", label: "write", descriptionKey: "agents.toolCatalog.descriptions.write" },
|
||||
{ id: "edit", label: "edit", descriptionKey: "agents.toolCatalog.descriptions.edit" },
|
||||
{
|
||||
id: "apply_patch",
|
||||
label: "apply_patch",
|
||||
descriptionKey: "agents.toolCatalog.descriptions.applyPatch",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "runtime",
|
||||
label: "Runtime",
|
||||
labelKey: "agents.toolCatalog.groups.runtime",
|
||||
tools: [
|
||||
{ id: "exec", label: "exec", description: "Run shell commands" },
|
||||
{ id: "process", label: "process", description: "Manage background processes" },
|
||||
{ id: "exec", label: "exec", descriptionKey: "agents.toolCatalog.descriptions.exec" },
|
||||
{
|
||||
id: "process",
|
||||
label: "process",
|
||||
descriptionKey: "agents.toolCatalog.descriptions.process",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "web",
|
||||
label: "Web",
|
||||
labelKey: "agents.toolCatalog.groups.web",
|
||||
tools: [
|
||||
{ id: "web_search", label: "web_search", description: "Search the web" },
|
||||
{ id: "web_fetch", label: "web_fetch", description: "Fetch web content" },
|
||||
{
|
||||
id: "web_search",
|
||||
label: "web_search",
|
||||
descriptionKey: "agents.toolCatalog.descriptions.webSearch",
|
||||
},
|
||||
{
|
||||
id: "web_fetch",
|
||||
label: "web_fetch",
|
||||
descriptionKey: "agents.toolCatalog.descriptions.webFetch",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "memory",
|
||||
label: "Memory",
|
||||
labelKey: "agents.toolCatalog.groups.memory",
|
||||
tools: [
|
||||
{ id: "memory_search", label: "memory_search", description: "Semantic search" },
|
||||
{ id: "memory_get", label: "memory_get", description: "Read memory files" },
|
||||
{
|
||||
id: "memory_search",
|
||||
label: "memory_search",
|
||||
descriptionKey: "agents.toolCatalog.descriptions.memorySearch",
|
||||
},
|
||||
{
|
||||
id: "memory_get",
|
||||
label: "memory_get",
|
||||
descriptionKey: "agents.toolCatalog.descriptions.memoryGet",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "sessions",
|
||||
label: "Sessions",
|
||||
labelKey: "agents.toolCatalog.groups.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: "sessions_list",
|
||||
label: "sessions_list",
|
||||
descriptionKey: "agents.toolCatalog.descriptions.sessionsList",
|
||||
},
|
||||
{
|
||||
id: "sessions_history",
|
||||
label: "sessions_history",
|
||||
descriptionKey: "agents.toolCatalog.descriptions.sessionsHistory",
|
||||
},
|
||||
{
|
||||
id: "sessions_send",
|
||||
label: "sessions_send",
|
||||
descriptionKey: "agents.toolCatalog.descriptions.sessionsSend",
|
||||
},
|
||||
{
|
||||
id: "sessions_spawn",
|
||||
label: "sessions_spawn",
|
||||
descriptionKey: "agents.toolCatalog.descriptions.sessionsSpawn",
|
||||
},
|
||||
{
|
||||
id: "session_status",
|
||||
label: "session_status",
|
||||
descriptionKey: "agents.toolCatalog.descriptions.sessionStatus",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "ui",
|
||||
label: "UI",
|
||||
labelKey: "agents.toolCatalog.groups.ui",
|
||||
tools: [
|
||||
{ id: "browser", label: "browser", description: "Control web browser" },
|
||||
{ id: "canvas", label: "canvas", description: "Control canvases" },
|
||||
{
|
||||
id: "browser",
|
||||
label: "browser",
|
||||
descriptionKey: "agents.toolCatalog.descriptions.browser",
|
||||
},
|
||||
{
|
||||
id: "canvas",
|
||||
label: "canvas",
|
||||
descriptionKey: "agents.toolCatalog.descriptions.canvas",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "messaging",
|
||||
label: "Messaging",
|
||||
tools: [{ id: "message", label: "message", description: "Send messages" }],
|
||||
labelKey: "agents.toolCatalog.groups.messaging",
|
||||
tools: [
|
||||
{
|
||||
id: "message",
|
||||
label: "message",
|
||||
descriptionKey: "agents.toolCatalog.descriptions.message",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "automation",
|
||||
label: "Automation",
|
||||
labelKey: "agents.toolCatalog.groups.automation",
|
||||
tools: [
|
||||
{ id: "cron", label: "cron", description: "Schedule tasks" },
|
||||
{ id: "gateway", label: "gateway", description: "Gateway control" },
|
||||
{ id: "cron", label: "cron", descriptionKey: "agents.toolCatalog.descriptions.cron" },
|
||||
{
|
||||
id: "gateway",
|
||||
label: "gateway",
|
||||
descriptionKey: "agents.toolCatalog.descriptions.gateway",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "nodes",
|
||||
label: "Nodes",
|
||||
tools: [{ id: "nodes", label: "nodes", description: "Nodes + devices" }],
|
||||
labelKey: "agents.toolCatalog.groups.nodes",
|
||||
tools: [
|
||||
{ id: "nodes", label: "nodes", descriptionKey: "agents.toolCatalog.descriptions.nodes" },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "agents",
|
||||
label: "Agents",
|
||||
tools: [{ id: "agents_list", label: "agents_list", description: "List agents" }],
|
||||
labelKey: "agents.toolCatalog.groups.agents",
|
||||
tools: [
|
||||
{
|
||||
id: "agents_list",
|
||||
label: "agents_list",
|
||||
descriptionKey: "agents.toolCatalog.descriptions.agentsList",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "media",
|
||||
label: "Media",
|
||||
tools: [{ id: "image", label: "image", description: "Image understanding" }],
|
||||
labelKey: "agents.toolCatalog.groups.media",
|
||||
tools: [
|
||||
{ id: "image", label: "image", descriptionKey: "agents.toolCatalog.descriptions.image" },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const PROFILE_OPTIONS = [
|
||||
{ id: "minimal", label: "Minimal" },
|
||||
{ id: "coding", label: "Coding" },
|
||||
{ id: "messaging", label: "Messaging" },
|
||||
{ id: "full", label: "Full" },
|
||||
{ id: "minimal", labelKey: "agents.toolCatalog.profiles.minimal" },
|
||||
{ id: "coding", labelKey: "agents.toolCatalog.profiles.coding" },
|
||||
{ id: "messaging", labelKey: "agents.toolCatalog.profiles.messaging" },
|
||||
{ id: "full", labelKey: "agents.toolCatalog.profiles.full" },
|
||||
] as const;
|
||||
|
||||
export function resolveToolSections(
|
||||
@@ -148,16 +230,27 @@ export function resolveToolSections(
|
||||
})),
|
||||
}));
|
||||
}
|
||||
return FALLBACK_TOOL_SECTIONS;
|
||||
return FALLBACK_TOOL_SECTIONS.map((section) => ({
|
||||
id: section.id,
|
||||
label: t(section.labelKey),
|
||||
tools: section.tools.map((tool) => ({
|
||||
id: tool.id,
|
||||
label: tool.label,
|
||||
description: t(tool.descriptionKey),
|
||||
})),
|
||||
}));
|
||||
}
|
||||
|
||||
export function resolveToolProfileOptions(
|
||||
toolsCatalogResult: ToolsCatalogResult | null,
|
||||
): readonly ToolCatalogProfile[] | typeof PROFILE_OPTIONS {
|
||||
): readonly ToolCatalogProfile[] | ReadonlyArray<{ id: string; label: string }> {
|
||||
if (toolsCatalogResult?.profiles?.length) {
|
||||
return toolsCatalogResult.profiles;
|
||||
}
|
||||
return PROFILE_OPTIONS;
|
||||
return PROFILE_OPTIONS.map((profile) => ({
|
||||
id: profile.id,
|
||||
label: t(profile.labelKey),
|
||||
}));
|
||||
}
|
||||
|
||||
type ToolPolicy = {
|
||||
@@ -303,7 +396,9 @@ export function buildAgentContext(
|
||||
runtime,
|
||||
identityName,
|
||||
identityAvatar,
|
||||
skillsLabel: skillFilter ? `${skillCount} selected` : "all skills",
|
||||
skillsLabel: skillFilter
|
||||
? t("agents.overview.selectedSkills", { count: String(skillCount) })
|
||||
: t("agents.overview.allSkills"),
|
||||
isDefault: Boolean(defaultId && agent.id === defaultId),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -635,19 +635,21 @@ function renderWikiPreviewOverlay(props: DreamingProps) {
|
||||
<div class="dreams-diary__preview-panel" @click=${(event: Event) => event.stopPropagation()}>
|
||||
<div class="dreams-diary__preview-header">
|
||||
<div>
|
||||
<div class="dreams-diary__preview-title">${state.wikiPreviewTitle || "Wiki page"}</div>
|
||||
<div class="dreams-diary__preview-title">
|
||||
${state.wikiPreviewTitle || t("dreaming.wiki.previewFallbackTitle")}
|
||||
</div>
|
||||
<div class="dreams-diary__preview-meta">
|
||||
${state.wikiPreviewPath}
|
||||
${state.wikiPreviewUpdatedAt ? ` · ${state.wikiPreviewUpdatedAt}` : ""}
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn btn--subtle btn--sm" @click=${() => closeWikiPreview(props)}>
|
||||
Close
|
||||
${t("dreaming.wiki.close")}
|
||||
</button>
|
||||
</div>
|
||||
<div class="dreams-diary__preview-body">
|
||||
${state.wikiPreviewLoading
|
||||
? html`<div class="dreams-diary__empty-text">Loading wiki page…</div>`
|
||||
? html`<div class="dreams-diary__empty-text">${t("dreaming.wiki.loadingPage")}</div>`
|
||||
: state.wikiPreviewError
|
||||
? html`<div class="dreams-diary__error">${state.wikiPreviewError}</div>`
|
||||
: html`
|
||||
@@ -672,28 +674,11 @@ function renderWikiPreviewOverlay(props: DreamingProps) {
|
||||
function renderDiarySubtabExplainer(activeDiarySubTab: DreamingViewState["activeDiarySubTab"]) {
|
||||
switch (activeDiarySubTab) {
|
||||
case "dreams":
|
||||
return html`
|
||||
<p class="dreams-diary__explainer">
|
||||
This is the raw dream diary the system writes while replaying and consolidating memory;
|
||||
use it to inspect what the memory system is noticing, and where it still looks noisy or
|
||||
thin.
|
||||
</p>
|
||||
`;
|
||||
return html` <p class="dreams-diary__explainer">${t("dreaming.wiki.dreamsExplainer")}</p> `;
|
||||
case "insights":
|
||||
return html`
|
||||
<p class="dreams-diary__explainer">
|
||||
These are imported insights clustered from external history; use them to review what
|
||||
imports surfaced before any of it graduates into durable memory.
|
||||
</p>
|
||||
`;
|
||||
return html` <p class="dreams-diary__explainer">${t("dreaming.wiki.insightsExplainer")}</p> `;
|
||||
case "palace":
|
||||
return html`
|
||||
<p class="dreams-diary__explainer">
|
||||
This is the compiled memory wiki surface the system can search and reason over; use it to
|
||||
inspect actual memory pages, claims, open questions, and contradictions rather than raw
|
||||
imported source chats.
|
||||
</p>
|
||||
`;
|
||||
return html` <p class="dreams-diary__explainer">${t("dreaming.wiki.palaceExplainer")}</p> `;
|
||||
}
|
||||
return nothing;
|
||||
}
|
||||
@@ -883,7 +868,7 @@ function renderAdvancedSection(props: DreamingProps) {
|
||||
?disabled=${props.dreamDiaryActionLoading}
|
||||
@click=${() => props.onCopyDreamingArchivePath()}
|
||||
>
|
||||
Copy archive path
|
||||
${t("dreaming.wiki.copyArchivePath")}
|
||||
</button>
|
||||
`
|
||||
: nothing}
|
||||
@@ -993,7 +978,7 @@ function renderDiaryImportsSection(props: DreamingProps) {
|
||||
if (props.wikiImportInsightsLoading && clusters.length === 0) {
|
||||
return html`
|
||||
<div class="dreams-diary__empty">
|
||||
<div class="dreams-diary__empty-text">Loading imported insights…</div>
|
||||
<div class="dreams-diary__empty-text">${t("dreaming.wiki.loadingInsights")}</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@@ -1001,10 +986,8 @@ function renderDiaryImportsSection(props: DreamingProps) {
|
||||
if (clusters.length === 0) {
|
||||
return html`
|
||||
<div class="dreams-diary__empty">
|
||||
<div class="dreams-diary__empty-text">No imported insights yet</div>
|
||||
<div class="dreams-diary__empty-hint">
|
||||
Run a ChatGPT import with apply to surface clustered imported insights here.
|
||||
</div>
|
||||
<div class="dreams-diary__empty-text">${t("dreaming.wiki.noInsights")}</div>
|
||||
<div class="dreams-diary__empty-hint">${t("dreaming.wiki.noInsightsHint")}</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@@ -1078,7 +1061,7 @@ function renderDiaryImportsSection(props: DreamingProps) {
|
||||
${item.candidateSignals.length > 0
|
||||
? html`
|
||||
<div class="dreams-diary__insight-list">
|
||||
<strong>Potentially useful signals</strong>
|
||||
<strong>${t("dreaming.wiki.candidateSignals")}</strong>
|
||||
${item.candidateSignals.map(
|
||||
(signal) => html`<p class="dreams-diary__insight-line">• ${signal}</p>`,
|
||||
)}
|
||||
@@ -1088,7 +1071,7 @@ function renderDiaryImportsSection(props: DreamingProps) {
|
||||
${item.correctionSignals.length > 0
|
||||
? html`
|
||||
<div class="dreams-diary__insight-list">
|
||||
<strong>Corrections or revisions</strong>
|
||||
<strong>${t("dreaming.wiki.corrections")}</strong>
|
||||
${item.correctionSignals.map(
|
||||
(signal) => html`<p class="dreams-diary__insight-line">• ${signal}</p>`,
|
||||
)}
|
||||
@@ -1098,36 +1081,40 @@ function renderDiaryImportsSection(props: DreamingProps) {
|
||||
${expanded
|
||||
? html`
|
||||
<div class="dreams-diary__insight-list">
|
||||
<strong>Import details</strong>
|
||||
<strong>${t("dreaming.wiki.importDetails")}</strong>
|
||||
${item.firstUserLine
|
||||
? html`
|
||||
<p class="dreams-diary__insight-line">
|
||||
<strong>Started with:</strong> ${item.firstUserLine}
|
||||
<strong>${t("dreaming.wiki.startedWith")}</strong>
|
||||
${item.firstUserLine}
|
||||
</p>
|
||||
`
|
||||
: nothing}
|
||||
${item.lastUserLine && item.lastUserLine !== item.firstUserLine
|
||||
? html`
|
||||
<p class="dreams-diary__insight-line">
|
||||
<strong>Ended on:</strong> ${item.lastUserLine}
|
||||
<strong>${t("dreaming.wiki.endedOn")}</strong>
|
||||
${item.lastUserLine}
|
||||
</p>
|
||||
`
|
||||
: nothing}
|
||||
<p class="dreams-diary__insight-line">
|
||||
<strong>Messages:</strong> ${item.userMessageCount} user ·
|
||||
${item.assistantMessageCount} assistant
|
||||
<strong>${t("dreaming.wiki.messages")}</strong>
|
||||
${item.userMessageCount} user · ${item.assistantMessageCount} assistant
|
||||
</p>
|
||||
${item.riskReasons.length > 0
|
||||
? html`
|
||||
<p class="dreams-diary__insight-line">
|
||||
<strong>Risk reasons:</strong> ${item.riskReasons.join(", ")}
|
||||
<strong>${t("dreaming.wiki.riskReasons")}</strong>
|
||||
${item.riskReasons.join(", ")}
|
||||
</p>
|
||||
`
|
||||
: nothing}
|
||||
${item.labels.length > 0
|
||||
? html`
|
||||
<p class="dreams-diary__insight-line">
|
||||
<strong>Labels:</strong> ${item.labels.join(", ")}
|
||||
<strong>${t("dreaming.wiki.labels")}</strong>
|
||||
${item.labels.join(", ")}
|
||||
</p>
|
||||
`
|
||||
: nothing}
|
||||
@@ -1165,7 +1152,7 @@ function renderDiaryImportsSection(props: DreamingProps) {
|
||||
void openWikiPreview(item.pagePath, props);
|
||||
}}
|
||||
>
|
||||
Open source page
|
||||
${t("dreaming.wiki.openSourcePage")}
|
||||
</button>
|
||||
</div>
|
||||
</article>
|
||||
@@ -1184,7 +1171,7 @@ function renderMemoryPalaceSection(props: DreamingProps) {
|
||||
if (props.wikiMemoryPalaceLoading && clusters.length === 0) {
|
||||
return html`
|
||||
<div class="dreams-diary__empty">
|
||||
<div class="dreams-diary__empty-text">Loading memory palace…</div>
|
||||
<div class="dreams-diary__empty-text">${t("dreaming.wiki.loadingPalace")}</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@@ -1192,11 +1179,8 @@ function renderMemoryPalaceSection(props: DreamingProps) {
|
||||
if (clusters.length === 0) {
|
||||
return html`
|
||||
<div class="dreams-diary__empty">
|
||||
<div class="dreams-diary__empty-text">Memory palace is not populated yet</div>
|
||||
<div class="dreams-diary__empty-hint">
|
||||
Right now the wiki mostly has raw source imports and operational reports. This tab becomes
|
||||
useful once syntheses, entities, or concepts start getting written.
|
||||
</div>
|
||||
<div class="dreams-diary__empty-text">${t("dreaming.wiki.emptyPalace")}</div>
|
||||
<div class="dreams-diary__empty-hint">${t("dreaming.wiki.emptyPalaceHint")}</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@@ -1283,7 +1267,7 @@ function renderMemoryPalaceSection(props: DreamingProps) {
|
||||
${item.claims.length > 0
|
||||
? html`
|
||||
<div class="dreams-diary__insight-list">
|
||||
<strong>Claims</strong>
|
||||
<strong>${t("dreaming.wiki.claims")}</strong>
|
||||
${item.claims.map(
|
||||
(claim) => html`<p class="dreams-diary__insight-line">• ${claim}</p>`,
|
||||
)}
|
||||
@@ -1293,7 +1277,7 @@ function renderMemoryPalaceSection(props: DreamingProps) {
|
||||
${item.questions.length > 0
|
||||
? html`
|
||||
<div class="dreams-diary__insight-list">
|
||||
<strong>Open questions</strong>
|
||||
<strong>${t("dreaming.wiki.openQuestions")}</strong>
|
||||
${item.questions.map(
|
||||
(question) => html`<p class="dreams-diary__insight-line">• ${question}</p>`,
|
||||
)}
|
||||
@@ -1303,7 +1287,7 @@ function renderMemoryPalaceSection(props: DreamingProps) {
|
||||
${item.contradictions.length > 0
|
||||
? html`
|
||||
<div class="dreams-diary__insight-list">
|
||||
<strong>Contradictions</strong>
|
||||
<strong>${t("dreaming.wiki.contradictions")}</strong>
|
||||
${item.contradictions.map(
|
||||
(entry) => html`<p class="dreams-diary__insight-line">• ${entry}</p>`,
|
||||
)}
|
||||
@@ -1313,14 +1297,16 @@ function renderMemoryPalaceSection(props: DreamingProps) {
|
||||
${expanded
|
||||
? html`
|
||||
<div class="dreams-diary__insight-list">
|
||||
<strong>Page details</strong>
|
||||
<strong>${t("dreaming.wiki.pageDetails")}</strong>
|
||||
<p class="dreams-diary__insight-line">
|
||||
<strong>Wiki page:</strong> ${item.pagePath}
|
||||
<strong>${t("dreaming.wiki.wikiPage")}</strong>
|
||||
${item.pagePath}
|
||||
</p>
|
||||
${item.id
|
||||
? html`
|
||||
<p class="dreams-diary__insight-line">
|
||||
<strong>Id:</strong> ${item.id}
|
||||
<strong>${t("dreaming.wiki.id")}</strong>
|
||||
${item.id}
|
||||
</p>
|
||||
`
|
||||
: nothing}
|
||||
@@ -1348,7 +1334,7 @@ function renderMemoryPalaceSection(props: DreamingProps) {
|
||||
void openWikiPreview(item.pagePath, props);
|
||||
}}
|
||||
>
|
||||
Open wiki page
|
||||
${t("dreaming.wiki.openWikiPage")}
|
||||
</button>
|
||||
</div>
|
||||
</article>
|
||||
@@ -1461,7 +1447,7 @@ function renderDiarySection(props: DreamingProps) {
|
||||
props.onViewStateChange();
|
||||
}}
|
||||
>
|
||||
Dreams
|
||||
${t("dreaming.wiki.dreamsTab")}
|
||||
</button>
|
||||
<button
|
||||
class="dreams-diary__subtab ${activeDiarySubTab === "insights"
|
||||
@@ -1474,7 +1460,7 @@ function renderDiarySection(props: DreamingProps) {
|
||||
props.onViewStateChange();
|
||||
}}
|
||||
>
|
||||
Imported Insights
|
||||
${t("dreaming.wiki.insightsTab")}
|
||||
</button>
|
||||
<button
|
||||
class="dreams-diary__subtab ${activeDiarySubTab === "palace"
|
||||
@@ -1487,7 +1473,7 @@ function renderDiarySection(props: DreamingProps) {
|
||||
props.onViewStateChange();
|
||||
}}
|
||||
>
|
||||
Memory Palace
|
||||
${t("dreaming.wiki.palaceTab")}
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
@@ -1514,7 +1500,7 @@ function renderDiarySection(props: DreamingProps) {
|
||||
}}
|
||||
>
|
||||
${memoryWikiUnavailable
|
||||
? "How to enable"
|
||||
? t("dreaming.wiki.howToEnable")
|
||||
: activeDiarySubTab === "dreams"
|
||||
? props.dreamDiaryLoading
|
||||
? t("dreaming.diary.reloading")
|
||||
@@ -1534,18 +1520,20 @@ function renderDiarySection(props: DreamingProps) {
|
||||
${memoryWikiUnavailable
|
||||
? html`
|
||||
<div class="dreams-diary__empty">
|
||||
<div class="dreams-diary__empty-text">Memory Wiki is not enabled</div>
|
||||
<div class="dreams-diary__empty-text">${t("dreaming.wiki.unavailable")}</div>
|
||||
<div class="dreams-diary__empty-hint">
|
||||
Imported Insights and Memory Palace are provided by the bundled
|
||||
<code>memory-wiki</code> plugin.
|
||||
${t("dreaming.wiki.unavailablePluginPrefix")}
|
||||
<code>memory-wiki</code> ${t("dreaming.wiki.unavailablePluginSuffix")}
|
||||
</div>
|
||||
<div class="dreams-diary__empty-hint">
|
||||
Enable <code>plugins.entries.memory-wiki.enabled = true</code>, then reload this
|
||||
tab.
|
||||
${t("dreaming.wiki.enablePrefix")}
|
||||
<code>plugins.entries.memory-wiki.enabled = true</code>${t(
|
||||
"dreaming.wiki.enableSuffix",
|
||||
)}
|
||||
</div>
|
||||
<div class="dreams-diary__empty-actions">
|
||||
<button class="btn btn--subtle btn--sm" @click=${() => props.onOpenConfig()}>
|
||||
Open Config
|
||||
${t("dreaming.wiki.openConfig")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -106,19 +106,19 @@ export function renderAgentOverview(params: {
|
||||
|
||||
return html`
|
||||
<section class="card">
|
||||
<div class="card-title">Overview</div>
|
||||
<div class="card-sub">Workspace paths and identity metadata.</div>
|
||||
<div class="card-title">${t("agents.overview.title")}</div>
|
||||
<div class="card-sub">${t("agents.overview.subtitle")}</div>
|
||||
|
||||
<div class="agents-overview-grid" style="margin-top: 16px;">
|
||||
<div class="agent-kv">
|
||||
<div class="label">Workspace</div>
|
||||
<div class="label">${t("agents.context.workspace")}</div>
|
||||
<div>
|
||||
<openclaw-tooltip content="Open Files tab">
|
||||
<openclaw-tooltip .content=${t("agents.context.openFilesTab")}>
|
||||
<button
|
||||
type="button"
|
||||
class="workspace-link mono"
|
||||
@click=${() => onSelectPanel("files")}
|
||||
aria-label="Open Files tab"
|
||||
aria-label=${t("agents.context.openFilesTab")}
|
||||
>
|
||||
${workspace}
|
||||
</button>
|
||||
@@ -126,11 +126,11 @@ export function renderAgentOverview(params: {
|
||||
</div>
|
||||
</div>
|
||||
<div class="agent-kv">
|
||||
<div class="label">Primary Model</div>
|
||||
<div class="label">${t("agents.context.primaryModel")}</div>
|
||||
<div class="mono">${model}</div>
|
||||
</div>
|
||||
<div class="agent-kv">
|
||||
<div class="label">Runtime</div>
|
||||
<div class="label">${t("agents.context.runtime")}</div>
|
||||
<div class="mono">${runtime}</div>
|
||||
</div>
|
||||
<div class="agent-kv">
|
||||
@@ -138,24 +138,32 @@ export function renderAgentOverview(params: {
|
||||
<div class="mono">${thinkingDefault}</div>
|
||||
</div>
|
||||
<div class="agent-kv">
|
||||
<div class="label">Skills Filter</div>
|
||||
<div>${skillFilter ? `${skillCount} selected` : "all skills"}</div>
|
||||
<div class="label">${t("agents.context.skillsFilter")}</div>
|
||||
<div>
|
||||
${skillFilter
|
||||
? t("agents.overview.selectedSkills", { count: String(skillCount) })
|
||||
: t("agents.overview.allSkills")}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
${configDirty
|
||||
? html`
|
||||
<div class="callout warn" style="margin-top: 16px">
|
||||
You have unsaved config changes.
|
||||
${t("agents.overview.unsavedConfig")}
|
||||
</div>
|
||||
`
|
||||
: nothing}
|
||||
|
||||
<div class="agent-model-select" style="margin-top: 20px;">
|
||||
<div class="label">Model Selection</div>
|
||||
<div class="label">${t("agents.overview.modelSelection")}</div>
|
||||
<div class="agent-model-fields">
|
||||
<label class="field">
|
||||
<span>Primary model${isDefault ? " (default)" : ""}</span>
|
||||
<span>
|
||||
${isDefault
|
||||
? t("agents.overview.primaryModelDefault")
|
||||
: t("agents.overview.primaryModel")}
|
||||
</span>
|
||||
<select
|
||||
.value=${selectedPrimary ?? ""}
|
||||
?disabled=${disabled}
|
||||
@@ -163,10 +171,16 @@ export function renderAgentOverview(params: {
|
||||
onModelChange(agent.id, (e.target as HTMLSelectElement).value || null)}
|
||||
>
|
||||
${isDefault
|
||||
? html` <option value="" ?selected=${!selectedPrimary}>Not set</option> `
|
||||
? html`
|
||||
<option value="" ?selected=${!selectedPrimary}>
|
||||
${t("agents.overview.notSet")}
|
||||
</option>
|
||||
`
|
||||
: html`
|
||||
<option value="" ?selected=${!selectedPrimary}>
|
||||
${defaultPrimary ? `Inherit default (${defaultPrimary})` : "Inherit default"}
|
||||
${defaultPrimary
|
||||
? t("agents.overview.inheritDefaultModel", { model: defaultPrimary })
|
||||
: t("agents.overview.inheritDefault")}
|
||||
</option>
|
||||
`}
|
||||
${buildModelOptions(
|
||||
@@ -178,7 +192,7 @@ export function renderAgentOverview(params: {
|
||||
</select>
|
||||
</label>
|
||||
<div class="field">
|
||||
<span>Fallbacks</span>
|
||||
<span>${t("agents.overview.fallbacks")}</span>
|
||||
<div
|
||||
class="agent-chip-input"
|
||||
@click=${(e: Event) => {
|
||||
@@ -235,7 +249,7 @@ export function renderAgentOverview(params: {
|
||||
?disabled=${configSaving || !configDirty}
|
||||
@click=${onConfigSave}
|
||||
>
|
||||
${configSaving ? "Saving…" : "Save"}
|
||||
${configSaving ? t("common.saving") : t("common.save")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -47,12 +47,12 @@ function buildCatalogBadgeLabels(section: AgentToolSection, tool: AgentToolEntry
|
||||
const pluginId = tool.pluginId ?? section.pluginId;
|
||||
const badges: string[] = [];
|
||||
if (source === "plugin" && pluginId) {
|
||||
badges.push(`Plugin: ${pluginId}`);
|
||||
badges.push(t("agentTools.plugin", { id: pluginId }));
|
||||
} else if (source === "core") {
|
||||
badges.push("Built-In");
|
||||
badges.push(t("agentTools.builtIn"));
|
||||
}
|
||||
if (tool.optional) {
|
||||
badges.push("Optional");
|
||||
badges.push(t("agentTools.optional"));
|
||||
}
|
||||
return badges;
|
||||
}
|
||||
@@ -64,7 +64,7 @@ function buildRowStatusBadges(params: {
|
||||
}) {
|
||||
const badges = buildCatalogBadgeLabels(params.section, params.tool);
|
||||
if (params.activeEntry) {
|
||||
badges.unshift("Live Now");
|
||||
badges.unshift(t("agentTools.liveNow"));
|
||||
}
|
||||
return badges;
|
||||
}
|
||||
@@ -75,24 +75,24 @@ function formatToolPolicyState(params: {
|
||||
denied: boolean;
|
||||
}) {
|
||||
if (params.denied) {
|
||||
return "Disabled by agent override.";
|
||||
return t("agentTools.disabledByOverride");
|
||||
}
|
||||
if (params.allowed && params.baseAllowed) {
|
||||
return "Enabled by the current profile.";
|
||||
return t("agentTools.enabledByProfile");
|
||||
}
|
||||
if (params.allowed) {
|
||||
return "Enabled by agent override.";
|
||||
return t("agentTools.enabledByOverride");
|
||||
}
|
||||
return "Not included in the current profile.";
|
||||
return t("agentTools.notIncluded");
|
||||
}
|
||||
|
||||
function formatToolSourceLabel(section: AgentToolSection, tool: AgentToolEntry) {
|
||||
const source = tool.source ?? section.source;
|
||||
const pluginId = tool.pluginId ?? section.pluginId;
|
||||
if (source === "plugin" && pluginId) {
|
||||
return `Plugin: ${pluginId}`;
|
||||
return t("agentTools.plugin", { id: pluginId });
|
||||
}
|
||||
return "Built-In";
|
||||
return t("agentTools.builtIn");
|
||||
}
|
||||
|
||||
function formatToolAccessSummary(params: {
|
||||
@@ -101,15 +101,15 @@ function formatToolAccessSummary(params: {
|
||||
denied: boolean;
|
||||
}) {
|
||||
if (params.denied) {
|
||||
return "Override Off";
|
||||
return t("agentTools.overrideOff");
|
||||
}
|
||||
if (params.allowed && params.baseAllowed) {
|
||||
return "Enabled";
|
||||
return t("agentTools.enabled");
|
||||
}
|
||||
if (params.allowed) {
|
||||
return "Override On";
|
||||
return t("agentTools.overrideOn");
|
||||
}
|
||||
return "Profile Off";
|
||||
return t("agentTools.profileOff");
|
||||
}
|
||||
|
||||
function formatToolRuntimeSummary(params: {
|
||||
@@ -117,12 +117,12 @@ function formatToolRuntimeSummary(params: {
|
||||
runtimeSessionMatchesSelectedAgent: boolean;
|
||||
}) {
|
||||
if (params.activeEntry) {
|
||||
return "Live Now";
|
||||
return t("agentTools.liveNow");
|
||||
}
|
||||
if (params.runtimeSessionMatchesSelectedAgent) {
|
||||
return "Not Live";
|
||||
return t("agentTools.notLive");
|
||||
}
|
||||
return "Other Agent";
|
||||
return t("agentTools.otherAgent");
|
||||
}
|
||||
|
||||
function toToolAnchorId(toolId: string) {
|
||||
@@ -130,10 +130,6 @@ function toToolAnchorId(toolId: string) {
|
||||
return `agent-tool-${safe}`;
|
||||
}
|
||||
|
||||
function formatCountLabel(count: number, singular: string, plural = `${singular}s`) {
|
||||
return `${count} ${count === 1 ? singular : plural}`;
|
||||
}
|
||||
|
||||
function flattenEffectiveTools(groups: ToolsEffectiveResult["groups"] | null | undefined) {
|
||||
return (groups ?? []).flatMap((group) => group.tools);
|
||||
}
|
||||
@@ -247,10 +243,10 @@ export function renderAgentTools(params: {
|
||||
const profileOptions = resolveToolProfileOptions(params.toolsCatalogResult);
|
||||
const toolSections = resolveToolSections(params.toolsCatalogResult);
|
||||
const profileSource = agentTools.profile
|
||||
? "agent override"
|
||||
? t("agentTools.profileSourceAgent")
|
||||
: globalTools.profile
|
||||
? "global default"
|
||||
: "default";
|
||||
? t("agentTools.profileSourceGlobal")
|
||||
: t("agentTools.profileSourceDefault");
|
||||
const hasAgentAllow = Array.isArray(agentTools.allow) && agentTools.allow.length > 0;
|
||||
const hasGlobalAllow = Array.isArray(globalTools.allow) && globalTools.allow.length > 0;
|
||||
const editable =
|
||||
@@ -365,18 +361,23 @@ export function renderAgentTools(params: {
|
||||
<section class="card">
|
||||
<div class="agent-tools-header">
|
||||
<div class="agent-tools-header__intro">
|
||||
<div class="card-title">Tool Access</div>
|
||||
<div class="card-title">${t("agentTools.title")}</div>
|
||||
<div class="card-sub">
|
||||
Profile + per-tool overrides for this agent.
|
||||
<span class="mono">${enabledCount}/${toolIds.length}</span> enabled.
|
||||
${t("agentTools.subtitle")}
|
||||
<span class="mono"
|
||||
>${t("agentTools.enabledSummary", {
|
||||
enabled: String(enabledCount),
|
||||
total: String(toolIds.length),
|
||||
})}</span
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="agent-tools-header__actions">
|
||||
<button class="btn btn--sm" ?disabled=${!editable} @click=${() => updateAll(true)}>
|
||||
Enable All
|
||||
${t("agentTools.enableAll")}
|
||||
</button>
|
||||
<button class="btn btn--sm" ?disabled=${!editable} @click=${() => updateAll(false)}>
|
||||
Disable All
|
||||
${t("agentTools.disableAll")}
|
||||
</button>
|
||||
<button
|
||||
class="btn btn--sm"
|
||||
@@ -390,43 +391,41 @@ export function renderAgentTools(params: {
|
||||
?disabled=${params.configSaving || !params.configDirty}
|
||||
@click=${params.onConfigSave}
|
||||
>
|
||||
${params.configSaving ? "Saving…" : "Save"}
|
||||
${params.configSaving ? t("common.saving") : t("common.save")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
${!params.configForm
|
||||
? html`
|
||||
<div class="callout info" style="margin-top: 12px">
|
||||
Load the gateway config to adjust tool profiles.
|
||||
</div>
|
||||
<div class="callout info" style="margin-top: 12px">${t("agentTools.loadConfig")}</div>
|
||||
`
|
||||
: nothing}
|
||||
${hasAgentAllow
|
||||
? html`
|
||||
<div class="callout info" style="margin-top: 12px">
|
||||
This agent is using an explicit allowlist in config. Tool overrides are managed in the
|
||||
Config tab.
|
||||
${t("agentTools.explicitAllowlist")}
|
||||
</div>
|
||||
`
|
||||
: nothing}
|
||||
${hasGlobalAllow
|
||||
? html`
|
||||
<div class="callout info" style="margin-top: 12px">
|
||||
Global tools.allow is set. Agent overrides cannot enable tools that are globally
|
||||
blocked.
|
||||
${t("agentTools.globalAllowlist")}
|
||||
</div>
|
||||
`
|
||||
: nothing}
|
||||
${params.toolsCatalogLoading && !params.toolsCatalogResult && !params.toolsCatalogError
|
||||
? html`
|
||||
<div class="callout info" style="margin-top: 12px">Loading runtime tool catalog…</div>
|
||||
<div class="callout info" style="margin-top: 12px">
|
||||
${t("agentTools.loadingCatalog")}
|
||||
</div>
|
||||
`
|
||||
: nothing}
|
||||
${params.toolsCatalogError
|
||||
? html`
|
||||
<div class="callout info" style="margin-top: 12px">
|
||||
Could not load runtime tool catalog. Showing built-in fallback list instead.
|
||||
${t("agentTools.catalogFallback")}
|
||||
</div>
|
||||
`
|
||||
: nothing}
|
||||
@@ -434,16 +433,16 @@ export function renderAgentTools(params: {
|
||||
<div class="agent-tools-overview">
|
||||
<div class="agent-tools-overview__primary">
|
||||
<div class="agent-tools-pane">
|
||||
<div class="label">Available Right Now</div>
|
||||
<div class="label">${t("agentTools.availableNow")}</div>
|
||||
<div class="card-sub">
|
||||
What this agent can use in the current chat session.
|
||||
<span class="mono">${params.runtimeSessionKey || "no session"}</span>
|
||||
${t("agentTools.availableNowSubtitle")}
|
||||
<span class="mono">${params.runtimeSessionKey || t("agentTools.noSession")}</span>
|
||||
</div>
|
||||
${renderEffectiveToolNotices(params.toolsEffectiveResult)}
|
||||
${!params.runtimeSessionMatchesSelectedAgent
|
||||
? html`
|
||||
<div class="callout info" style="margin-top: 12px">
|
||||
Switch chat to this agent to view its live runtime tools.
|
||||
${t("agentTools.switchAgent")}
|
||||
</div>
|
||||
`
|
||||
: params.toolsEffectiveLoading &&
|
||||
@@ -451,19 +450,19 @@ export function renderAgentTools(params: {
|
||||
!params.toolsEffectiveError
|
||||
? html`
|
||||
<div class="callout info" style="margin-top: 12px">
|
||||
Loading available tools…
|
||||
${t("agentTools.loadingAvailable")}
|
||||
</div>
|
||||
`
|
||||
: params.toolsEffectiveError
|
||||
? html`
|
||||
<div class="callout info" style="margin-top: 12px">
|
||||
Could not load available tools for this session.
|
||||
${t("agentTools.availableError")}
|
||||
</div>
|
||||
`
|
||||
: (params.toolsEffectiveResult?.groups?.length ?? 0) === 0
|
||||
? html`
|
||||
<div class="callout info" style="margin-top: 12px">
|
||||
No tools are available for this session right now.
|
||||
${t("agentTools.noAvailable")}
|
||||
</div>
|
||||
`
|
||||
: html`
|
||||
@@ -487,9 +486,13 @@ export function renderAgentTools(params: {
|
||||
? html`
|
||||
<span
|
||||
class="agent-tools-runtime-chip agent-tools-runtime-chip--more"
|
||||
title=${`${hiddenEffectiveToolCount} more live tools are available in the groups below.`}
|
||||
title=${t("agentTools.moreLiveTitle", {
|
||||
count: String(hiddenEffectiveToolCount),
|
||||
})}
|
||||
>
|
||||
+${hiddenEffectiveToolCount} more live tools
|
||||
${t("agentTools.moreLive", {
|
||||
count: String(hiddenEffectiveToolCount),
|
||||
})}
|
||||
</span>
|
||||
`
|
||||
: nothing}
|
||||
@@ -498,7 +501,7 @@ export function renderAgentTools(params: {
|
||||
</div>
|
||||
|
||||
<div class="agent-tools-pane">
|
||||
<div class="label">Quick Presets</div>
|
||||
<div class="label">${t("agentTools.quickPresets")}</div>
|
||||
<div class="agent-tools-buttons">
|
||||
${profileOptions.map(
|
||||
(option) => html`
|
||||
@@ -516,7 +519,7 @@ export function renderAgentTools(params: {
|
||||
?disabled=${!editable}
|
||||
@click=${() => params.onProfileChange(params.agentId, null, false)}
|
||||
>
|
||||
Inherit
|
||||
${t("agentTools.inherit")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -524,25 +527,29 @@ export function renderAgentTools(params: {
|
||||
|
||||
<div class="agent-tools-facts">
|
||||
<div class="agent-tools-fact">
|
||||
<div class="label">Profile</div>
|
||||
<div class="label">${t("agentTools.profile")}</div>
|
||||
<div class="mono">${profile}</div>
|
||||
</div>
|
||||
<div class="agent-tools-fact">
|
||||
<div class="label">Source</div>
|
||||
<div class="label">${t("agentTools.source")}</div>
|
||||
<div>${profileSource}</div>
|
||||
</div>
|
||||
<div class="agent-tools-fact">
|
||||
<div class="label">Enabled</div>
|
||||
<div class="label">${t("agentTools.enabled")}</div>
|
||||
<div class="mono">${enabledCount}/${toolIds.length}</div>
|
||||
</div>
|
||||
<div class="agent-tools-fact">
|
||||
<div class="label">Live</div>
|
||||
<div class="label">${t("agentTools.live")}</div>
|
||||
<div class="mono">${liveToolCount}</div>
|
||||
</div>
|
||||
<div class="agent-tools-fact">
|
||||
<div class="label">Status</div>
|
||||
<div class="label">${t("agentTools.status")}</div>
|
||||
<div class="mono">
|
||||
${params.configSaving ? "saving…" : params.configDirty ? "unsaved" : "saved"}
|
||||
${params.configSaving
|
||||
? t("agentTools.statusSaving")
|
||||
: params.configDirty
|
||||
? t("agentTools.statusUnsaved")
|
||||
: t("agentTools.statusSaved")}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -566,10 +573,15 @@ export function renderAgentTools(params: {
|
||||
<span class="agent-tools-group__title">
|
||||
${section.label}
|
||||
${section.source === "plugin" && section.pluginId
|
||||
? html`<span class="agent-pill">Plugin: ${section.pluginId}</span>`
|
||||
? html`<span class="agent-pill"
|
||||
>${t("agentTools.plugin", { id: section.pluginId })}</span
|
||||
>`
|
||||
: nothing}
|
||||
</span>
|
||||
<span class="agent-tools-group__preview" aria-label="Tool preview">
|
||||
<span
|
||||
class="agent-tools-group__preview"
|
||||
aria-label=${t("agentTools.toolPreview")}
|
||||
>
|
||||
${previewTools.map(
|
||||
(tool) =>
|
||||
html`<span class="mono" translate="no" title=${tool.label}
|
||||
@@ -577,15 +589,37 @@ export function renderAgentTools(params: {
|
||||
>`,
|
||||
)}
|
||||
${remainingPreviewCount > 0
|
||||
? html`<span>+${remainingPreviewCount} more</span>`
|
||||
? html`<span
|
||||
>${t("agentTools.more", {
|
||||
count: String(remainingPreviewCount),
|
||||
})}</span
|
||||
>`
|
||||
: nothing}
|
||||
</span>
|
||||
</span>
|
||||
<span class="agent-tools-group__counts">
|
||||
<span>${formatCountLabel(section.tools.length, "Tool")}</span>
|
||||
<span>${formatCountLabel(enabledSectionCount, "Enabled Tool")}</span>
|
||||
<span
|
||||
>${t(section.tools.length === 1 ? "agentTools.toolsOne" : "agentTools.tools", {
|
||||
count: String(section.tools.length),
|
||||
})}</span
|
||||
>
|
||||
<span
|
||||
>${t(
|
||||
enabledSectionCount === 1
|
||||
? "agentTools.enabledToolsOne"
|
||||
: "agentTools.enabledTools",
|
||||
{ count: String(enabledSectionCount) },
|
||||
)}</span
|
||||
>
|
||||
${activeSectionCount > 0
|
||||
? html`<span>${formatCountLabel(activeSectionCount, "Live Tool")}</span>`
|
||||
? html`<span
|
||||
>${t(
|
||||
activeSectionCount === 1
|
||||
? "agentTools.liveToolsOne"
|
||||
: "agentTools.liveTools",
|
||||
{ count: String(activeSectionCount) },
|
||||
)}</span
|
||||
>`
|
||||
: nothing}
|
||||
</span>
|
||||
</summary>
|
||||
@@ -616,11 +650,11 @@ export function renderAgentTools(params: {
|
||||
</div>
|
||||
<dl class="agent-tool-summary__facts">
|
||||
<div class="agent-tool-summary__fact">
|
||||
<dt class="label">Access</dt>
|
||||
<dt class="label">${t("agentTools.access")}</dt>
|
||||
<dd>${accessSummary}</dd>
|
||||
</div>
|
||||
<div class="agent-tool-summary__fact">
|
||||
<dt class="label">Session</dt>
|
||||
<dt class="label">${t("agentTools.session")}</dt>
|
||||
<dd>${runtimeSummary}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
@@ -636,7 +670,12 @@ export function renderAgentTools(params: {
|
||||
type="checkbox"
|
||||
.checked=${resolved.allowed}
|
||||
?disabled=${!editable}
|
||||
aria-label=${`${resolved.allowed ? "Disable" : "Enable"} ${tool.label}`}
|
||||
aria-label=${t(
|
||||
resolved.allowed
|
||||
? "agentTools.disableNamed"
|
||||
: "agentTools.enableNamed",
|
||||
{ name: tool.label },
|
||||
)}
|
||||
@change=${(e: Event) =>
|
||||
updateTool(tool.id, (e.target as HTMLInputElement).checked)}
|
||||
/>
|
||||
@@ -646,17 +685,17 @@ export function renderAgentTools(params: {
|
||||
<div class="agent-tool-details">
|
||||
<div class="agent-tool-details-strip">
|
||||
<div class="agent-tool-detail agent-tool-detail--inline">
|
||||
<div class="label">Access</div>
|
||||
<div class="label">${t("agentTools.access")}</div>
|
||||
<div>${formatToolPolicyState(resolved)}</div>
|
||||
</div>
|
||||
<div class="agent-tool-detail agent-tool-detail--inline">
|
||||
<div class="label">Source</div>
|
||||
<div class="label">${t("agentTools.source")}</div>
|
||||
<div>${formatToolSourceLabel(section, tool)}</div>
|
||||
</div>
|
||||
${defaultProfiles.length > 0
|
||||
? html`
|
||||
<div class="agent-tool-detail agent-tool-detail--inline">
|
||||
<div class="label">Default Presets</div>
|
||||
<div class="label">${t("agentTools.defaultPresets")}</div>
|
||||
<div class="agent-tool-badges">
|
||||
${defaultProfiles.map(
|
||||
(profileId) =>
|
||||
@@ -667,16 +706,20 @@ export function renderAgentTools(params: {
|
||||
`
|
||||
: nothing}
|
||||
<div class="agent-tool-detail agent-tool-detail--inline">
|
||||
<div class="label">Current Session</div>
|
||||
<div class="label">${t("agentTools.session")}</div>
|
||||
<div>
|
||||
${activeEntry
|
||||
? `Available now via ${renderEffectiveToolBadge(activeEntry)}.`
|
||||
? t("agentTools.availableVia", {
|
||||
source: renderEffectiveToolBadge(activeEntry),
|
||||
})
|
||||
: params.runtimeSessionMatchesSelectedAgent
|
||||
? "Not available in this chat session right now."
|
||||
: "Switch chat to this agent to inspect live availability."}
|
||||
? t("agentTools.unavailableSession")
|
||||
: t("agentTools.inspectAgent")}
|
||||
</div>
|
||||
</div>
|
||||
<a class="agent-tool-jump" href="#${anchorId}"> Link to This Tool </a>
|
||||
<a class="agent-tool-jump" href="#${anchorId}">
|
||||
${t("agentTools.linkTool")}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
@@ -735,9 +778,9 @@ export function renderAgentSkills(params: {
|
||||
<section class="card">
|
||||
<div class="row" style="justify-content: space-between; flex-wrap: wrap;">
|
||||
<div style="min-width: 0;">
|
||||
<div class="card-title">Skills</div>
|
||||
<div class="card-title">${t("agents.skillsPanel.title")}</div>
|
||||
<div class="card-sub">
|
||||
Per-agent skill allowlist and workspace skills.
|
||||
${t("agents.skillsPanel.subtitle")}
|
||||
${totalCount > 0
|
||||
? html`<span class="mono">${enabledCount}/${totalCount}</span>`
|
||||
: nothing}
|
||||
@@ -753,21 +796,21 @@ export function renderAgentSkills(params: {
|
||||
?disabled=${!editable}
|
||||
@click=${() => params.onClear(params.agentId)}
|
||||
>
|
||||
Enable All
|
||||
${t("agentTools.enableAll")}
|
||||
</button>
|
||||
<button
|
||||
class="btn btn--sm"
|
||||
?disabled=${!editable}
|
||||
@click=${() => params.onDisableAll(params.agentId)}
|
||||
>
|
||||
Disable All
|
||||
${t("agentTools.disableAll")}
|
||||
</button>
|
||||
<button
|
||||
class="btn btn--sm"
|
||||
?disabled=${!editable || !usingAllowlist}
|
||||
@click=${() => params.onClear(params.agentId)}
|
||||
>
|
||||
Reset
|
||||
${t("common.reset")}
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
@@ -785,7 +828,7 @@ export function renderAgentSkills(params: {
|
||||
?disabled=${params.configSaving || !params.configDirty}
|
||||
@click=${params.onConfigSave}
|
||||
>
|
||||
${params.configSaving ? "Saving…" : "Save"}
|
||||
${params.configSaving ? t("common.saving") : t("common.save")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -793,25 +836,25 @@ export function renderAgentSkills(params: {
|
||||
${!params.configForm
|
||||
? html`
|
||||
<div class="callout info" style="margin-top: 12px">
|
||||
Load the gateway config to set per-agent skills.
|
||||
${t("agents.skillsPanel.loadConfig")}
|
||||
</div>
|
||||
`
|
||||
: nothing}
|
||||
${usingAllowlist
|
||||
? html`
|
||||
<div class="callout info" style="margin-top: 12px">
|
||||
This agent uses a custom skill allowlist.
|
||||
${t("agents.skillsPanel.customAllowlist")}
|
||||
</div>
|
||||
`
|
||||
: html`
|
||||
<div class="callout info" style="margin-top: 12px">
|
||||
All skills are enabled. Disabling any skill will create a per-agent allowlist.
|
||||
${t("agents.skillsPanel.allEnabled")}
|
||||
</div>
|
||||
`}
|
||||
${!reportReady && !params.loading
|
||||
? html`
|
||||
<div class="callout info" style="margin-top: 12px">
|
||||
Load skills for this agent to view workspace-specific entries.
|
||||
${t("agents.skillsPanel.loadAgent")}
|
||||
</div>
|
||||
`
|
||||
: nothing}
|
||||
@@ -821,20 +864,22 @@ export function renderAgentSkills(params: {
|
||||
|
||||
<div class="filters" style="margin-top: 14px;">
|
||||
<label class="field" style="flex: 1;">
|
||||
<span>Filter</span>
|
||||
<span>${t("agents.skillsPanel.filter")}</span>
|
||||
<input
|
||||
.value=${params.filter}
|
||||
@input=${(e: Event) => params.onFilterChange((e.target as HTMLInputElement).value)}
|
||||
placeholder="Search skills"
|
||||
placeholder=${t("agents.skillsPanel.searchPlaceholder")}
|
||||
autocomplete="off"
|
||||
name="agent-skills-filter"
|
||||
/>
|
||||
</label>
|
||||
<div class="muted">${filtered.length} shown</div>
|
||||
<div class="muted">
|
||||
${t("agents.skillsPanel.shown", { count: String(filtered.length) })}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
${filtered.length === 0
|
||||
? html` <div class="muted" style="margin-top: 16px">No skills found.</div> `
|
||||
? html` <div class="muted" style="margin-top: 16px">${t("agents.skillsPanel.empty")}</div> `
|
||||
: html`
|
||||
<div class="agent-skills-groups" style="margin-top: 16px;">
|
||||
${groups.map((group) =>
|
||||
@@ -904,10 +949,14 @@ function renderAgentSkillRow(
|
||||
<div class="list-sub">${skill.description}</div>
|
||||
${renderSkillStatusChips({ skill })}
|
||||
${missing.length > 0
|
||||
? html`<div class="muted" style="margin-top: 6px;">Missing: ${missing.join(", ")}</div>`
|
||||
? html`<div class="muted" style="margin-top: 6px;">
|
||||
${t("agents.skillsPanel.missing", { items: missing.join(", ") })}
|
||||
</div>`
|
||||
: nothing}
|
||||
${reasons.length > 0
|
||||
? html`<div class="muted" style="margin-top: 6px;">Reason: ${reasons.join(", ")}</div>`
|
||||
? html`<div class="muted" style="margin-top: 6px;">
|
||||
${t("agents.skillsPanel.reason", { items: reasons.join(", ") })}
|
||||
</div>`
|
||||
: nothing}
|
||||
</div>
|
||||
<div class="list-meta">
|
||||
|
||||
Reference in New Issue
Block a user