fix: register tools via setTools(), add Tools/Code capability badges
Some checks are pending
CI / build-check-test (push) Waiting to run

- Fix 'Tool browser not found': createTools callback was silently ignored
  by Agent constructor. Now uses agent.setTools() after instantiation.
- Add Wrench (Tools) and Code icons + filter buttons to ModelSelector
  alongside existing Brain (Reasoning) and Image (Vision).
- Add refreshSidebar() call in createAgent for session list updates.
This commit is contained in:
JAE 2026-03-27 04:44:28 +00:00
parent fedc60fd0f
commit 11af96265a
2 changed files with 51 additions and 16 deletions

View file

@ -405,20 +405,18 @@ Persist information across sessions. Use to save important context, user prefere
} }
renderApp(); renderApp();
}, },
createTools: async (runtimeProvidersFactory: any) => {
const replTool = createJavaScriptReplTool();
replTool.runtimeProvidersFactory = runtimeProvidersFactory;
return [
replTool,
createWebSearchTool(),
createBashTool(),
createBrowserTool(),
createImageGenTool(),
createTTSTool(),
...createMemoryTools(),
];
},
}); });
// Register tools with the agent
const replTool = createJavaScriptReplTool();
agent.setTools([
replTool,
createWebSearchTool(),
createBashTool(),
createBrowserTool(),
createImageGenTool(),
createTTSTool(),
...createMemoryTools(),
]);
costTracker.bindAgent(agent); costTracker.bindAgent(agent);
chatPanel?.setAgent(agent); chatPanel?.setAgent(agent);
// Hook: live model badge + immediate empty state hide // Hook: live model badge + immediate empty state hide
@ -438,6 +436,7 @@ Persist information across sessions. Use to save important context, user prefere
} }
}); });
if (!currentSessionId) currentSessionId = crypto.randomUUID(); if (!currentSessionId) currentSessionId = crypto.randomUUID();
await refreshSidebar();
}; };
const loadSession = async (sessionId: string): Promise<boolean> => { const loadSession = async (sessionId: string): Promise<boolean> => {

View file

@ -7,7 +7,7 @@ import { DialogBase } from "@mariozechner/mini-lit/dist/DialogBase.js";
import { html, type PropertyValues, type TemplateResult } from "lit"; import { html, type PropertyValues, type TemplateResult } from "lit";
import { customElement, state } from "lit/decorators.js"; import { customElement, state } from "lit/decorators.js";
import { createRef, ref } from "lit/directives/ref.js"; import { createRef, ref } from "lit/directives/ref.js";
import { Brain, Image as ImageIcon } from "lucide"; import { Brain, Code, Globe, Image as ImageIcon, Wrench } from "lucide";
import { Input } from "../components/Input.js"; import { Input } from "../components/Input.js";
import { getAppStorage } from "../storage/app-storage.js"; import { getAppStorage } from "../storage/app-storage.js";
import type { AutoDiscoveryProviderType } from "../storage/stores/custom-providers-store.js"; import type { AutoDiscoveryProviderType } from "../storage/stores/custom-providers-store.js";
@ -52,6 +52,8 @@ export class ModelSelector extends DialogBase {
@state() searchQuery = ""; @state() searchQuery = "";
@state() filterThinking = false; @state() filterThinking = false;
@state() filterVision = false; @state() filterVision = false;
@state() filterTools = false;
@state() filterCode = false;
@state() private filterProvider = ""; @state() private filterProvider = "";
@state() customProvidersLoading = false; @state() customProvidersLoading = false;
@state() selectedIndex = 0; @state() selectedIndex = 0;
@ -246,6 +248,12 @@ export class ModelSelector extends DialogBase {
if (this.filterVision) { if (this.filterVision) {
filteredModels = filteredModels.filter(({ model }) => model.input.includes("image")); filteredModels = filteredModels.filter(({ model }) => model.input.includes("image"));
} }
if (this.filterTools) {
filteredModels = filteredModels.filter(({ model }) => (model as any).tags?.includes("tools"));
}
if (this.filterCode) {
filteredModels = filteredModels.filter(({ model }) => (model as any).tags?.includes("code") || (model as any).tags?.includes("optimized-for-code"));
}
if (this.filterProvider) { if (this.filterProvider) {
filteredModels = filteredModels.filter(({ provider }) => provider === this.filterProvider); filteredModels = filteredModels.filter(({ provider }) => provider === this.filterProvider);
} }
@ -378,6 +386,32 @@ export class ModelSelector extends DialogBase {
className: "rounded-full", className: "rounded-full",
children: html`<span class="inline-flex items-center gap-1">${icon(ImageIcon, "sm")} ${i18n("Vision")}</span>`, children: html`<span class="inline-flex items-center gap-1">${icon(ImageIcon, "sm")} ${i18n("Vision")}</span>`,
})} })}
${Button({
variant: this.filterTools ? "default" : "secondary",
size: "sm",
onClick: () => {
this.filterTools = !this.filterTools;
this.selectedIndex = 0;
if (this.scrollContainerRef.value) {
this.scrollContainerRef.value.scrollTop = 0;
}
},
className: "rounded-full",
children: html`<span class="inline-flex items-center gap-1">${icon(Wrench, "sm")} Tools</span>`,
})}
${Button({
variant: this.filterCode ? "default" : "secondary",
size: "sm",
onClick: () => {
this.filterCode = !this.filterCode;
this.selectedIndex = 0;
if (this.scrollContainerRef.value) {
this.scrollContainerRef.value.scrollTop = 0;
}
},
className: "rounded-full",
children: html`<span class="inline-flex items-center gap-1">${icon(Code, "sm")} Code</span>`,
})}
</div> </div>
${this.renderProviderTabs()} ${this.renderProviderTabs()}
@ -411,8 +445,10 @@ export class ModelSelector extends DialogBase {
</div> </div>
<div class="flex items-center justify-between text-xs text-muted-foreground"> <div class="flex items-center justify-between text-xs text-muted-foreground">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<span class="${model.reasoning ? "" : "opacity-30"}">${icon(Brain, "sm")}</span> <span class="${model.reasoning ? "" : "opacity-30"}" title="Reasoning">${icon(Brain, "sm")}</span>
<span class="${model.input.includes("image") ? "" : "opacity-30"}">${icon(ImageIcon, "sm")}</span> <span class="${model.input.includes("image") ? "" : "opacity-30"}" title="Vision">${icon(ImageIcon, "sm")}</span>
<span class="${(model as any).tags?.includes("tools") ? "" : "opacity-30"}" title="Tool Use">${icon(Wrench, "sm")}</span>
<span class="${(model as any).tags?.includes("code") || (model as any).tags?.includes("optimized-for-code") ? "" : "opacity-30"}" title="Code">${icon(Code, "sm")}</span>
<span>${this.formatTokens(model.contextWindow)}K/${this.formatTokens(model.maxTokens)}K</span> <span>${this.formatTokens(model.contextWindow)}K/${this.formatTokens(model.maxTokens)}K</span>
</div> </div>
<span>${formatModelCost(model.cost)}</span> <span>${formatModelCost(model.cost)}</span>