);
}
diff --git a/src/pages/doormile/settings/agentStudio/SkillCardsGrid.jsx b/src/pages/doormile/settings/agentStudio/SkillCardsGrid.jsx
index 1f4d4f4..6cdbac8 100644
--- a/src/pages/doormile/settings/agentStudio/SkillCardsGrid.jsx
+++ b/src/pages/doormile/settings/agentStudio/SkillCardsGrid.jsx
@@ -1,5 +1,5 @@
import React, { useState, useMemo } from 'react';
-import { Search, Plus, X, Layers, AlertCircle } from 'lucide-react';
+import { Search, Plus, X, AlertCircle, Filter, Zap } from 'lucide-react';
import { cn } from '@/lib/utils';
import SkillCard from './SkillCard';
@@ -8,218 +8,190 @@ export default function SkillCardsGrid({
activeAgent,
onTestSkill,
onOpenDetails,
+ onToggleStatus,
onNewSkill,
canCreate = true,
+ canEdit = true,
createDisabledReason = '',
}) {
- const [scopeTab, setScopeTab] = useState('agent'); // 'agent' or 'board'
const [searchQuery, setSearchQuery] = useState('');
const [categoryFilter, setCategoryFilter] = useState('ALL');
+ const [statusFilter, setStatusFilter] = useState('ALL'); // 'ALL' | 'ACTIVE' | 'DISABLED'
+ const [scopeFilter, setScopeFilter] = useState('ALL'); // 'ALL' | 'AGENT' | 'BOARD'
- // Filter by active agent and scope
- const scopedSkills = useMemo(() => {
- return skills.filter((s) => {
- if (scopeTab === 'board') {
- return s.scope === 'board';
- }
- return s.scope !== 'board';
- });
- }, [skills, scopeTab]);
+ const hasBoardSkills = skills.some((s) => s.scope === 'board');
- // Categories present in the current scope
+ // Categories present in the skills
const categories = useMemo(() => {
- const counts = { ALL: scopedSkills.length };
- scopedSkills.forEach((s) => {
- const cat = (s.category || 'GENERAL').toUpperCase();
- counts[cat] = (counts[cat] || 0) + 1;
+ const list = new Set();
+ skills.forEach((s) => {
+ if (s.category) list.add(s.category.toUpperCase());
});
- return counts;
- }, [scopedSkills]);
+ return Array.from(list);
+ }, [skills]);
- // Filter by category and search query
+ // Filter skills
const filteredSkills = useMemo(() => {
- return scopedSkills.filter((s) => {
- const matchesCat =
- categoryFilter === 'ALL' ||
- (s.category || '').toUpperCase() === categoryFilter;
+ return skills.filter((s) => {
+ // Category filter
+ if (categoryFilter !== 'ALL' && (s.category || '').toUpperCase() !== categoryFilter) {
+ return false;
+ }
+ // Status filter
+ const isEnabled = s.enabled !== false;
+ if (statusFilter === 'ACTIVE' && !isEnabled) return false;
+ if (statusFilter === 'DISABLED' && isEnabled) return false;
+
+ // Scope filter
+ if (scopeFilter === 'AGENT' && s.scope === 'board') return false;
+ if (scopeFilter === 'BOARD' && s.scope !== 'board') return false;
+
+ // Search query
const q = searchQuery.toLowerCase().trim();
- const matchesQuery =
- !q ||
+ if (!q) return true;
+
+ return (
(s.title || '').toLowerCase().includes(q) ||
(s.description || '').toLowerCase().includes(q) ||
(s.category || '').toLowerCase().includes(q) ||
(s.integration || '').toLowerCase().includes(q) ||
- (s.prompt || '').toLowerCase().includes(q);
-
- return matchesCat && matchesQuery;
+ (s.prompt || '').toLowerCase().includes(q)
+ );
});
- }, [scopedSkills, categoryFilter, searchQuery]);
+ }, [skills, categoryFilter, statusFilter, scopeFilter, searchQuery]);
- const enabledCount = filteredSkills.filter((s) => s.enabled !== false).length;
- const agentCount = skills.filter((s) => s.scope !== 'board').length;
- const boardCount = skills.filter((s) => s.scope === 'board').length;
+ const activeCount = skills.filter((s) => s.enabled !== false).length;
return (
- {/* 1. Scope Sub-tabs (Agent Skills / Board Skills) */}
-
-
diff --git a/src/pages/doormile/settings/agentStudio/ToolRegistryView.jsx b/src/pages/doormile/settings/agentStudio/ToolRegistryView.jsx
index 902b308..36808c1 100644
--- a/src/pages/doormile/settings/agentStudio/ToolRegistryView.jsx
+++ b/src/pages/doormile/settings/agentStudio/ToolRegistryView.jsx
@@ -11,207 +11,262 @@ import {
Bell,
Database,
X,
- Hash,
- MapPin,
- Layers,
+ Bot,
+ Info,
} from 'lucide-react';
import { cn } from '@/lib/utils';
-/* ─── Kind config ─────────────────────────────────────────────────────────── */
-const KIND = {
+/* ─── Friendly Tool Dictionary ────────────────────────────────────────────── */
+const TOOL_DICTIONARY = {
+ alert_low_battery_rider: {
+ title: 'Rider Battery Warning',
+ desc: 'Sends an urgent notification to riders with low vehicle battery to recharge or report to a hub.',
+ category: 'Fleet & Riders',
+ },
+ assign_express_batch: {
+ title: 'Assign Express Batch',
+ desc: 'Automatically assigns grouped high-priority express orders to optimal nearby riders.',
+ category: 'Orders & Dispatch',
+ },
+ assign_riders: {
+ title: 'Assign Orders to Riders',
+ desc: 'Dispatches and matches unassigned customer orders with active, available delivery riders.',
+ category: 'Orders & Dispatch',
+ },
+ create_single_order: {
+ title: 'Create Express Booking',
+ desc: 'Creates an on-demand express delivery booking upon operator review and confirmation.',
+ category: 'Orders & Dispatch',
+ },
+ decide_assignment_failure: {
+ title: 'Diagnose Assignment Failure',
+ desc: 'Analyzes why an order failed assignment and recommends an immediate resolution.',
+ category: 'Analysis & Diagnostics',
+ },
+ decide_stall_response: {
+ title: 'Resolve Stalled Rider',
+ desc: 'Detects riders stopped mid-route and initiates automatic reassignment or customer updates.',
+ category: 'Analysis & Diagnostics',
+ },
+ dispatch_hub_idle_parcels: {
+ title: 'Dispatch Idle Hub Parcels',
+ desc: 'Coordinates idle riders to pick up parcels waiting at crowded hubs.',
+ category: 'Orders & Dispatch',
+ },
+ enforce_cash_handoff: {
+ title: 'Enforce Cash Hand-off',
+ desc: 'Routes riders carrying high Cash-on-Delivery balances to deposit cash at the nearest hub.',
+ category: 'Fleet & Riders',
+ },
+ enforce_otp_verification: {
+ title: 'Require Delivery OTP',
+ desc: 'Enforces one-time password verification before handing over high-value COD orders.',
+ category: 'Fleet & Riders',
+ },
+ nearby_milers: {
+ title: 'Locate Nearby Riders',
+ desc: 'Searches real-time GPS locations to find active delivery riders within a specified radius.',
+ category: 'Fleet & Riders',
+ },
+ notify_customer: {
+ title: 'Send Customer Update',
+ desc: 'Sends automated SMS or WhatsApp status notifications to the recipient about order delays or arrivals.',
+ category: 'Customer Alerts',
+ },
+ publish_miler_stalled: {
+ title: 'Broadcast Delay Event',
+ desc: 'Broadcasts a stalled delivery alert to the operations team dashboard.',
+ category: 'Customer Alerts',
+ },
+ reassign_booking: {
+ title: 'Reassign to New Rider',
+ desc: 'Transfers a stalled or delayed order to an alternate available rider to protect delivery SLA.',
+ category: 'Orders & Dispatch',
+ },
+ rebalance_riders: {
+ title: 'Rebalance Fleet Zones',
+ desc: 'Directs idle riders from low-demand areas to high-volume delivery zones.',
+ category: 'Fleet & Riders',
+ },
+ scan_bookings: {
+ title: 'Scan Pending Orders',
+ desc: 'Continuously monitors order queues for delayed or unassigned shipments.',
+ category: 'Orders & Dispatch',
+ },
+ simulate_pricing_quote: {
+ title: 'Calculate Delivery Quote',
+ desc: 'Estimates accurate delivery fares and ETA based on routed distance and vehicle type.',
+ category: 'Pricing & Route',
+ },
+ trigger_auto_dispatch: {
+ title: 'Trigger Auto-Dispatch',
+ desc: 'Automatically pairs waiting orders with the closest available riders during peak rush.',
+ category: 'Orders & Dispatch',
+ },
+};
+
+const CATEGORIES = [
+ 'Orders & Dispatch',
+ 'Fleet & Riders',
+ 'Customer Alerts',
+ 'Analysis & Diagnostics',
+];
+
+/** Convert snake_case code into clean Title Case if not in dictionary */
+function formatToolTitle(rawName) {
+ if (TOOL_DICTIONARY[rawName]?.title) return TOOL_DICTIONARY[rawName].title;
+ return rawName
+ .split('_')
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
+ .join(' ');
+}
+
+/** Sanitize developer descriptions into clean operational text */
+function formatToolDescription(tool) {
+ if (TOOL_DICTIONARY[tool.name]?.desc) return TOOL_DICTIONARY[tool.name].desc;
+ let text = tool.description || 'No description provided.';
+ text = text.replace(/REVIEW ONLY\.\s*/gi, '');
+ text = text.replace(/No executor\.\s*/gi, '');
+ text = text.replace(/\(structured output\)\s*/gi, '');
+ text = text.replace(/POST\s+[^\s]+\s+accepts[^\.]+\.\s*/gi, '');
+ text = text.replace(/NOT IMPLEMENTED:[^\.]+\.\s*/gi, '');
+ return text.trim() || 'Executes this automated workflow action.';
+}
+
+const KIND_META = {
read: {
icon: Eye,
- label: 'Read',
- card: 'border-sky-200 bg-sky-50/40',
- iconWrap: 'bg-sky-100 text-sky-600',
- badge: 'bg-sky-100 text-sky-700 border-sky-200',
- dot: 'bg-sky-500',
+ label: 'Lookup',
+ badge: 'bg-sky-50 text-sky-700 border-sky-200',
},
write: {
icon: Edit3,
- label: 'Write',
- card: 'border-violet-200 bg-violet-50/30',
- iconWrap: 'bg-violet-100 text-violet-600',
- badge: 'bg-violet-100 text-violet-700 border-violet-200',
- dot: 'bg-violet-500',
+ label: 'Action',
+ badge: 'bg-violet-50 text-violet-700 border-violet-200',
},
notify: {
icon: Bell,
- label: 'Notify',
- card: 'border-amber-200 bg-amber-50/30',
- iconWrap: 'bg-amber-100 text-amber-600',
- badge: 'bg-amber-100 text-amber-700 border-amber-200',
- dot: 'bg-amber-500',
+ label: 'Alert',
+ badge: 'bg-amber-50 text-amber-700 border-amber-200',
},
};
-const KIND_DEFAULT = {
+
+const DEFAULT_KIND_META = {
icon: Database,
- label: 'General',
- card: 'border-slate-200 bg-white',
- iconWrap: 'bg-slate-100 text-slate-500',
- badge: 'bg-slate-100 text-slate-600 border-slate-200',
- dot: 'bg-slate-400',
+ label: 'Action',
+ badge: 'bg-slate-100 text-slate-700 border-slate-200',
};
-function kindOf(tool) {
- return KIND[(tool.kind || '').toLowerCase()] || KIND_DEFAULT;
-}
-
-/* ─── Stat chip ───────────────────────────────────────────────────────────── */
-function StatChip({ label, value, tone = 'slate' }) {
- const tones = {
- slate: 'bg-slate-100 text-slate-700 border-slate-200',
- amber: 'bg-amber-50 text-amber-700 border-amber-200',
- emerald: 'bg-emerald-50 text-emerald-700 border-emerald-200',
- sky: 'bg-sky-50 text-sky-700 border-sky-200',
- };
- return (
-
- {value}
- {label}
-
- );
-}
-
-/* ─── Parameter pill ──────────────────────────────────────────────────────── */
-function ParamPill({ param }) {
- return (
-
-
-
{param.name}
-
-
- {param.type}
-
- {param.required && (
-
- req
-
- )}
-
-
- {param.description && (
-
{param.description}
- )}
- {param.default !== undefined && (
-
- default: {String(param.default)}
-
- )}
-
- );
-}
-
-/* ─── Tool Card ───────────────────────────────────────────────────────────── */
-function ToolCard({ tool }) {
+function ToolRow({ tool }) {
const [open, setOpen] = useState(false);
- const k = kindOf(tool);
- const KindIcon = k.icon;
+ const meta = KIND_META[(tool.kind || '').toLowerCase()] || DEFAULT_KIND_META;
+ const KindIcon = meta.icon;
+ const friendlyTitle = formatToolTitle(tool.name);
+ const friendlyDesc = formatToolDescription(tool);
const hasParams = tool.parameters && tool.parameters.length > 0;
return (
- {/* Card header — always visible */}
setOpen((v) => !v)}
- className="w-full text-left p-4 flex items-start gap-3 cursor-pointer"
+ className="w-full text-left p-3.5 flex items-center justify-between gap-3 cursor-pointer group"
>
- {/* Kind icon */}
-
-
-
-
- {/* Name + description */}
-
-
-
{tool.name}
-
- {/* Kind badge */}
-
- {k.label}
-
-
- {/* Safety */}
- {tool.requiresConfirmation ? (
-
-
- Gated
-
- ) : (
-
-
- Open
-
- )}
-
- {/* Params count */}
- {hasParams && (
-
-
- {tool.parameters.length}
-
- )}
+
+
+
-
{tool.description}
+
+
+
+ {friendlyTitle}
+
+
+ {/* Code pill */}
+
+ {tool.name}
+
+
+ {/* Kind Badge */}
+
+ {meta.label}
+
+
+ {/* Safety Badge */}
+ {tool.requiresConfirmation ? (
+
+
+ Needs Approval
+
+ ) : (
+
+
+ Automated
+
+ )}
+
+
+
+ {friendlyDesc}
+
+
- {/* Expand chevron */}
-
+
+
+
- {/* Expandable detail panel */}
{open && (
-
- {/* Meta grid */}
- {(tool.target || tool.implementedAt || tool.parentSkill) && (
-
- {tool.target && (
-
-
-
-
Touches
-
{tool.target}
-
-
- )}
- {tool.implementedAt && (
-
-
-
-
Implemented at
-
{tool.implementedAt}
-
-
- )}
+
+
+ Detailed Action:
+ {friendlyDesc}
+
+
+ {tool.parentSkill && (
+
+ Used by Skills:
+ {tool.parentSkill}
)}
- {/* Parameters */}
- {hasParams ? (
-
-
+ {hasParams && (
+
+
- Parameters
+ Required Inputs ({tool.parameters.length})
- {tool.parameters.map((p) =>
)}
+ {tool.parameters.map((p) => (
+
+
+ {p.name}
+
+ {p.type || 'text'}
+
+
+ {p.description && (
+
{p.description}
+ )}
+
+ ))}
- ) : (
-
No parameters — input is open-ended.
)}
)}
@@ -219,131 +274,201 @@ function ToolCard({ tool }) {
);
}
-/* ─── Main view ───────────────────────────────────────────────────────────── */
-export default function ToolRegistryView({ tools = [] }) {
+export default function ToolRegistryView({
+ tools = [],
+ agentTools = [],
+ activeAgent = null,
+}) {
+ const [scope, setScope] = useState('AGENT'); // 'AGENT' | 'ALL'
const [search, setSearch] = useState('');
- const [kindFilter, setKindFilter] = useState('all');
+ const [categoryFilter, setCategoryFilter] = useState('Orders & Dispatch');
- const counts = useMemo(() => {
- const c = { all: tools.length };
- tools.forEach((t) => {
- const k = (t.kind || 'read').toLowerCase();
- c[k] = (c[k] || 0) + 1;
- });
- return c;
- }, [tools]);
-
- const kindTabs = ['all', ...Object.keys(KIND).filter((k) => counts[k])];
+ // If the agent has no linked tools, default to showing all tools
+ const effectiveScope = scope === 'AGENT' && agentTools.length > 0 ? 'AGENT' : 'ALL';
+ const baseTools = effectiveScope === 'AGENT' ? agentTools : tools;
const filtered = useMemo(() => {
const q = search.toLowerCase().trim();
- return tools.filter((t) => {
- const matchesKind = kindFilter === 'all' || (t.kind || 'read').toLowerCase() === kindFilter;
- const matchesSearch =
- !q ||
- t.name.toLowerCase().includes(q) ||
- t.description.toLowerCase().includes(q) ||
- (t.parentSkill || '').toLowerCase().includes(q) ||
- (t.target || '').toLowerCase().includes(q);
- return matchesKind && matchesSearch;
- });
- }, [tools, kindFilter, search]);
+ return baseTools.filter((t) => {
+ // In ALL tools mode, filter by selected category unless searching
+ if (effectiveScope === 'ALL' && !q) {
+ const cat = TOOL_DICTIONARY[t.name]?.category || 'Orders & Dispatch';
+ if (cat !== categoryFilter) return false;
+ }
- const gatedCount = tools.filter((t) => t.requiresConfirmation).length;
- const openCount = tools.length - gatedCount;
+ if (!q) return true;
+ const friendlyTitle = formatToolTitle(t.name).toLowerCase();
+ const friendlyDesc = formatToolDescription(t).toLowerCase();
+ return (
+ t.name.toLowerCase().includes(q) ||
+ friendlyTitle.includes(q) ||
+ friendlyDesc.includes(q)
+ );
+ });
+ }, [baseTools, effectiveScope, categoryFilter, search]);
+
+ const agentName = activeAgent?.name || 'Agent';
return (
-
- {/* ── Stats row ─────────────────────────────────────────────────── */}
-
-
-
-
-
-
+
+ {/* 1. Scope Selector Tabs: Agent Tools vs All Tools */}
+
+
+
{
+ setScope('AGENT');
+ setSearch('');
+ }}
+ className={cn(
+ 'px-3.5 py-1.5 rounded-lg text-xs font-bold transition-all cursor-pointer flex items-center gap-1.5',
+ effectiveScope === 'AGENT'
+ ? 'bg-white text-slate-900 shadow-2xs'
+ : 'text-slate-500 hover:text-slate-800'
+ )}
+ >
+
+ {agentName}'s Tools
+
+ {agentTools.length}
+
+
- {/* Right-side header text */}
-
-
-
- Agent Tool Registry
-
-
- Every capability a skill may call. Write & notify tools need operator confirmation unless the agent is autonomous.
-
+
{
+ setScope('ALL');
+ setSearch('');
+ setCategoryFilter('Orders & Dispatch');
+ }}
+ className={cn(
+ 'px-3.5 py-1.5 rounded-lg text-xs font-bold transition-all cursor-pointer flex items-center gap-1.5',
+ effectiveScope === 'ALL'
+ ? 'bg-white text-slate-900 shadow-2xs'
+ : 'text-slate-500 hover:text-slate-800'
+ )}
+ >
+
+ All System Tools
+
+ {tools.length}
+
+
+
+
+ {/* Helper Note */}
+
+
+ Tools are automated actions agents can execute in your system.
- {/* ── Search + Kind filter ───────────────────────────────────────── */}
-
- {/* Search */}
+ {/* 2. Search & Category Filters */}
+
+ {/* Search Input */}
setSearch(e.target.value)}
- placeholder="Search by name, target system, or description…"
- className="w-full h-10 bg-white border border-slate-200 rounded-xl pl-10 pr-9 text-sm text-slate-800 placeholder:text-slate-400 focus:outline-none focus:border-brand focus:ring-2 focus:ring-brand/10 shadow-sm transition-all"
+ placeholder="Search tools by action, rider, booking..."
+ className="w-full h-10 bg-white border border-slate-200 rounded-xl pl-10 pr-9 text-xs sm:text-sm text-slate-800 placeholder:text-slate-400 focus:outline-none focus:border-brand focus:ring-2 focus:ring-brand/10 shadow-2xs hover:border-slate-300 transition-all"
/>
{search && (
setSearch('')}
- className="absolute right-3 top-1/2 -translate-y-1/2 p-0.5 rounded-full text-slate-400 hover:text-slate-700 hover:bg-slate-100 cursor-pointer transition-colors"
+ className="absolute right-3 top-1/2 -translate-y-1/2 p-1 text-slate-400 hover:text-slate-700 rounded-full hover:bg-slate-100 transition-colors cursor-pointer"
>
)}
- {/* Kind filter tabs */}
-
- {kindTabs.map((k) => {
- const meta = KIND[k];
- const isActive = kindFilter === k;
- return (
- setKindFilter(k)}
- className={cn(
- 'px-3 py-1.5 rounded-lg text-xs font-semibold transition-all cursor-pointer capitalize flex items-center gap-1.5',
- isActive ? 'bg-white text-slate-900 shadow-sm' : 'text-slate-500 hover:text-slate-800'
- )}
- >
- {meta && }
- {k === 'all' ? 'All' : meta?.label || k}
-
+ {CATEGORIES.map((cat) => {
+ const isSelected = categoryFilter === cat;
+ return (
+ setCategoryFilter(cat)}
className={cn(
- 'text-[10px] font-bold',
- isActive ? 'text-slate-600' : 'text-slate-400'
+ 'px-3 py-1.5 rounded-lg text-xs font-semibold whitespace-nowrap cursor-pointer transition-all border',
+ isSelected
+ ? 'bg-slate-900 text-white border-slate-900 shadow-2xs'
+ : 'bg-white text-slate-600 border-slate-200 hover:bg-slate-50'
)}
>
- {counts[k] || 0}
-
-
- );
- })}
-
+ {cat}
+
+ );
+ })}
+
+ )}
- {/* ── Tool Cards ────────────────────────────────────────────────── */}
- {filtered.length === 0 ? (
-
-
-
No tools match your filter
+ {/* 3. Result Count */}
+
+
+ Showing
+ {filtered.length}
+
+ {effectiveScope === 'ALL' && !search
+ ? ` tools in ${categoryFilter}`
+ : ` of ${baseTools.length} ${effectiveScope === 'AGENT' ? `${agentName} tools` : 'tools'}`}
+
+
+
+ {search && (
{ setSearch(''); setKindFilter('all'); }}
- className="mt-3 text-xs text-brand font-semibold hover:underline cursor-pointer"
+ onClick={() => setSearch('')}
+ className="text-xs text-brand font-medium hover:underline cursor-pointer"
>
- Clear filters
+ Clear search
+ )}
+
+
+ {/* 4. Tool List */}
+ {filtered.length === 0 ? (
+
+
+
No tools found
+
+ {search
+ ? `No tools matched "${search}".`
+ : `No tools configured under ${categoryFilter}.`}
+
+ {search && (
+
setSearch('')}
+ className="px-3 py-1.5 rounded-lg bg-slate-100 hover:bg-slate-200 text-xs font-semibold text-slate-700 transition-colors cursor-pointer"
+ >
+ Clear Search
+
+ )}
) : (
-
- {filtered.map((tool) =>
)}
+
+ {filtered.map((tool) => (
+
+ ))}
)}
diff --git a/src/pages/doormile/settings/agentStudio/WiringStatusBanner.jsx b/src/pages/doormile/settings/agentStudio/WiringStatusBanner.jsx
index e276a01..596d2f9 100644
--- a/src/pages/doormile/settings/agentStudio/WiringStatusBanner.jsx
+++ b/src/pages/doormile/settings/agentStudio/WiringStatusBanner.jsx
@@ -1,14 +1,12 @@
-import React from 'react';
-import { AlertTriangle, CheckCircle2, Info, XCircle } from 'lucide-react';
+import React, { useState } from 'react';
+import { AlertTriangle, CheckCircle2, Info, XCircle, ChevronDown, ChevronUp, Cpu } from 'lucide-react';
import { useAiStatus } from '@/lib/doormileHooks';
+import { cn } from '@/lib/utils';
/**
- * What this page's settings actually drive, from GET /admin/ai/status — not a
- * fixed note. It used to say "Half wired" forever, including after AI_engine
- * and the backend were deployed, because nothing checked.
- *
- * Green only when AI_engine has read the registry recently (the backend stamps
- * every poll) and the Test tab has a model. Re-checks every 30 seconds.
+ * What this page's settings actually drive, from GET /admin/ai/status.
+ * Designed with a clean, unobtrusive status bar that can be expanded
+ * for detailed diagnostics when needed.
*/
function ago(iso) {
@@ -49,11 +47,13 @@ export function wiringLines(status) {
export default function WiringStatusBanner() {
const { data, isLoading, isError, error } = useAiStatus();
+ const [expanded, setExpanded] = useState(false);
if (isLoading) {
return (
-
- Checking what is connected…
+
+
+ Checking system connectivity…
);
}
@@ -61,17 +61,15 @@ export default function WiringStatusBanner() {
if (isError) {
const notDeployed = error?.response?.status === 404;
return (
-
-
-
- Connection status unavailable.{' '}
- {notDeployed
- ? 'This API server does not report agent status yet — deploy the current doormile_backend build. Changes here are still saved and audited.'
- : 'The status check failed; it will retry shortly. Changes here are still saved and audited.'}
-
+
+
+
+
+ {notDeployed
+ ? 'API status reporting is starting up. Changes made here are saved safely.'
+ : 'Status check retrying shortly. Changes made here are saved safely.'}
+
+
);
}
@@ -81,28 +79,63 @@ export default function WiringStatusBanner() {
return (
-
- {allOk ? 'All connected.' : 'Partly connected.'}
-
-
- {lines.map((l) => {
- const Icon = l.info ? Info : l.ok ? CheckCircle2 : XCircle;
- const color = l.info ? 'text-slate-400' : l.ok ? 'text-emerald-600' : 'text-amber-600';
- return (
- -
-
- {l.text}
-
- );
- })}
-
+ {/* Sleek top summary bar */}
+
+
+
+
+ {allOk ? 'AI Engine Active' : 'Agent Registry Synced'}
+
+ •
+
+ {allOk
+ ? 'All configured skills and tools are actively wired'
+ : 'Settings are saved to registry; agent runtimes sync automatically'}
+
+
+
+
setExpanded((v) => !v)}
+ className="flex items-center gap-1 text-[11px] font-medium text-slate-500 hover:text-slate-900 px-2 py-1 rounded-md hover:bg-slate-100/80 transition-colors cursor-pointer shrink-0"
+ >
+ {expanded ? 'Hide details' : 'System details'}
+ {expanded ? : }
+
+
+
+ {/* Collapsible diagnostic details */}
+ {expanded && (
+
+
+ Connection Diagnostics
+
+
+ {lines.map((l) => {
+ const Icon = l.info ? Info : l.ok ? CheckCircle2 : XCircle;
+ const color = l.info ? 'text-slate-400' : l.ok ? 'text-emerald-600' : 'text-amber-500';
+ return (
+ -
+
+ {l.text}
+
+ );
+ })}
+
+
+ )}
);
}