fix(ui): handle available_agents/available_skills response keys and nested agent/skill content
All checks were successful
Build and Deploy DevOpsDash / build-image (push) Successful in 8s

- Use r.available_agents and r.available_skills field names from MCP proxy
- Fix extractContent to unwrap agents[0].content / skills[0].content
- Fix Alpine x-for key to include item.name for agents/skills
- Fix selected state comparison to handle name-keyed items
This commit is contained in:
Henrik Jess Nielsen
2026-05-09 17:13:10 +02:00
parent 685e4271c1
commit 8905dd2040

View File

@@ -363,10 +363,10 @@
<!-- List --> <!-- List -->
<div class="w-72 border-r border-gray-800 overflow-y-auto p-2 space-y-1 shrink-0"> <div class="w-72 border-r border-gray-800 overflow-y-auto p-2 space-y-1 shrink-0">
<div class="text-xs text-gray-600 px-2 py-1" x-text="filteredItems.length+' items'"></div> <div class="text-xs text-gray-600 px-2 py-1" x-text="filteredItems.length+' items'"></div>
<template x-for="item in filteredItems" :key="item.id||item.storage_filename||item.title"> <template x-for="item in filteredItems" :key="item.id||item.storage_filename||item.name||item.title">
<button @click="select(item)" <button @click="select(item)"
class="w-full text-left px-3 py-2 rounded-lg transition-colors text-sm" class="w-full text-left px-3 py-2 rounded-lg transition-colors text-sm"
:class="selectedItem?.id===item.id||selectedItem?.storage_filename===item.storage_filename?'bg-indigo-600/20 border border-indigo-500/40 text-white':'hover:bg-gray-900 text-gray-300 border border-transparent'"> :class="(selectedItem?.id&&selectedItem?.id===item.id)||(selectedItem?.storage_filename&&selectedItem?.storage_filename===item.storage_filename)||(selectedItem?.name&&selectedItem?.name===item.name)?'bg-indigo-600/20 border border-indigo-500/40 text-white':'hover:bg-gray-900 text-gray-300 border border-transparent'">
<div class="font-medium truncate text-sm" x-text="item.title||item.name||item.storage_filename"></div> <div class="font-medium truncate text-sm" x-text="item.title||item.name||item.storage_filename"></div>
<template x-if="item.category||item.entity_type"> <template x-if="item.category||item.entity_type">
<div class="text-xs text-gray-600 mt-0.5" x-text="item.category||item.entity_type"></div> <div class="text-xs text-gray-600 mt-0.5" x-text="item.category||item.entity_type"></div>
@@ -618,18 +618,16 @@ function knowledgeTab() {
memories: '/api/v1/memories', memories: '/api/v1/memories',
}[this.subTab]; }[this.subTab];
const r = await fetch(url).then(r=>r.json()); const r = await fetch(url).then(r=>r.json());
this.items = r.entries||r.adrs||r.memories||r.agents||r.skills||r.howtos|| this.items = r.entries||r.adrs||r.memories||r.files||
(r.agents_list ? Object.entries(r.agents_list||{}).map(([n,c])=>({name:n,content:c})) : null)|| r.available_agents||r.available_skills||
this.flattenAgents(r)||[]; r.agents||r.skills||r.howtos||
this.flattenFilesOrHowtos(r)||[];
} catch(e) { this.items = []; } } catch(e) { this.items = []; }
this.loading = false; this.loading = false;
this.filterList(); this.filterList();
}, },
flattenAgents(r) { flattenFilesOrHowtos(r) {
// agents/skills return nested structure if(r.files && Array.isArray(r.files)) return r.files;
if(r.agents) return Array.isArray(r.agents) ? r.agents : Object.entries(r.agents).map(([k,v])=>({name:k,...(typeof v==='string'?{content:v}:v)}));
if(r.skills) return Array.isArray(r.skills) ? r.skills : Object.entries(r.skills).map(([k,v])=>({name:k,...(typeof v==='string'?{content:v}:v)}));
if(r.files) return r.files;
return null; return null;
}, },
filterList() { filterList() {
@@ -662,6 +660,9 @@ function knowledgeTab() {
// If it's JSON, pull out content field // If it's JSON, pull out content field
try { try {
const j = JSON.parse(raw); const j = JSON.parse(raw);
// agents/skills return {agents:[{content:...}]} or {skills:[{content:...}]}
if(j.agents && j.agents[0]) return j.agents[0].content||JSON.stringify(j,null,2);
if(j.skills && j.skills[0]) return j.skills[0].content||JSON.stringify(j,null,2);
return j.content||j.raw||j.definition||JSON.stringify(j,null,2); return j.content||j.raw||j.definition||JSON.stringify(j,null,2);
} catch { return raw; } } catch { return raw; }
} }