Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ui/src/api/__tests__/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ function mockFetchSSE(
/** Collect all chunks from the streamChat async generator. */
async function collectChunks(
model: string,
messages: { role: string; content: string }[],
messages: import("@/lib/types").ChatMessage[],
): Promise<StreamChunk[]> {
const result: StreamChunk[] = [];
for await (const chunk of streamChat(model, messages)) {
Expand Down
20 changes: 17 additions & 3 deletions ui/src/components/InlineAssistant.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,23 @@ export function InlineAssistant({
}
if (chunk.type === "trace") {
// Update local agent activity indicator
handleTraceEvent(chunk as TraceEventChunk, (activity) => {
setActiveAgent(activity.activeAgent ?? null);
});
handleTraceEvent(
chunk as TraceEventChunk,
(activity) => {
setActiveAgent(activity.activeAgent ?? null);
},
{
isActive: false,
activeAgent: null,
agentsSeen: [],
agentStates: {},
liveTimeline: [],
thinkingStream: "",
activeEdges: [],
delegationMessages: [],
completedAt: null,
},
);
continue;
}
}
Expand Down
2 changes: 1 addition & 1 deletion ui/src/components/chat/markdown-renderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ export function MarkdownRenderer({ content, className, onCreateProposal }: Markd
// Route fenced code blocks through our shiki CodeBlock
// Uses react-shiki's isInlineCode helper to distinguish inline vs fenced
code(props: ComponentPropsWithoutRef<"code"> & { node?: unknown }) {
const { children, className: codeClassName, node, ...rest } = props;
const { children, className: codeClassName, node } = props;
const match = /language-(\w+)/.exec(codeClassName || "");
const isInline = node ? isInlineCode(node as Parameters<typeof isInlineCode>[0]) : !match;

Expand Down
4 changes: 2 additions & 2 deletions ui/src/components/ui/data-viewer.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useState, useCallback, useMemo } from "react";
import { Check, Copy, FileJson, FileCode } from "lucide-react";
import { useState, useMemo } from "react";
import { FileJson, FileCode } from "lucide-react";
import yaml from "js-yaml";
import { CodeBlock } from "./code-block";
import { cn } from "@/lib/utils";
Expand Down
9 changes: 0 additions & 9 deletions ui/src/components/ui/thinking-indicator.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,4 @@
import { motion } from "framer-motion";
import { Bot, Sparkles } from "lucide-react";

const THINKING_MESSAGES = [
"Analyzing your smart home...",
"Processing request...",
"Consulting the knowledge graph...",
"Evaluating automations...",
"Crunching the data...",
];

interface ThinkingIndicatorProps {
/** If content has started streaming, show the streaming cursor instead */
Expand Down
2 changes: 1 addition & 1 deletion ui/src/contexts/auth-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ export function AuthProvider({ children }: { children: ReactNode }) {
throw new Error(body?.detail || "Setup failed");
}

const data = await res.json();
await res.json();
setState((s) => ({
...s,
authenticated: true,
Expand Down
2 changes: 0 additions & 2 deletions ui/src/layouts/app-layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,8 @@ import {
Network,
Star,
LogOut,
User,
ChevronDown,
MapPin,
Settings,
} from "lucide-react";
import { cn } from "@/lib/utils";
import { useAuth } from "@/contexts/auth-context";
Expand Down
10 changes: 7 additions & 3 deletions ui/src/lib/__tests__/trace-event-handler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@
*/

import { describe, it, expect, vi, beforeEach } from "vitest";
import type { Mock } from "vitest";
import {
handleTraceEvent,
type TraceEventChunk,
} from "@/lib/trace-event-handler";
import type { AgentActivity, AgentNodeState } from "@/lib/agent-activity-store";
import type { AgentActivity } from "@/lib/agent-activity-store";

type SetActivityFn = (activity: Partial<AgentActivity>) => void;

/** Build a fresh default AgentActivity snapshot. */
function defaultActivity(overrides?: Partial<AgentActivity>): AgentActivity {
Expand All @@ -23,15 +26,16 @@ function defaultActivity(overrides?: Partial<AgentActivity>): AgentActivity {
thinkingStream: "",
activeEdges: [],
delegationMessages: [],
completedAt: null,
...overrides,
};
}

describe("handleTraceEvent", () => {
let setActivity: ReturnType<typeof vi.fn>;
let setActivity: Mock<SetActivityFn>;

beforeEach(() => {
setActivity = vi.fn();
setActivity = vi.fn<SetActivityFn>();
});

// ─── Architect start ─────────────────────────────────────────────────
Expand Down
1 change: 0 additions & 1 deletion ui/src/lib/thinking-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ export function parseThinkingContent(raw: string): ParsedContent {
// Check if this open tag has a corresponding close tag
const tag = openMatch[1].toLowerCase();
const closeTag = `</${tag}>`;
const afterOpen = raw.indexOf(`<${openMatch[1]}>`) + openMatch[0].length;
// If there's no close tag after the open tag, we're mid-thinking
if (!raw.slice(raw.indexOf(`<${openMatch[1]}>`)).includes(closeTag)) {
isThinking = true;
Expand Down
3 changes: 3 additions & 0 deletions ui/src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ export interface Proposal {
deployed_at?: string;
rolled_back_at?: string;
rejection_reason?: string;
ha_automation_id?: string;
ha_disabled?: boolean;
ha_error?: string;
created_at: string;
updated_at: string;
}
Expand Down
2 changes: 1 addition & 1 deletion ui/src/lib/useGlobalActivityStream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const RECONNECT_MAX_MS = 30_000;

export function useGlobalActivityStream() {
const retriesRef = useRef(0);
const timerRef = useRef<ReturnType<typeof setTimeout>>();
const timerRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
const esRef = useRef<EventSource | null>(null);
/** Track all auto-complete timeouts so we can cancel on unmount. */
const pendingTimeouts = useRef(new Set<ReturnType<typeof setTimeout>>());
Expand Down
1 change: 0 additions & 1 deletion ui/src/pages/agents/TeamArchitectureTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
BookOpen,
Wrench,
Code,
Server,
BarChart3,
Brain,
Tags,
Expand Down
15 changes: 0 additions & 15 deletions ui/src/pages/architecture/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
LayoutDashboard,
BookOpen,
Wrench,
Server,
Network,
ArrowDown,
ArrowLeftRight,
Expand All @@ -22,7 +21,6 @@ import { Card } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { useAgents } from "@/api/hooks";
import type { AgentDetail } from "@/lib/types";

// ─── Agent Node Definitions ───────────────────────────────────────────────────

Expand Down Expand Up @@ -394,19 +392,6 @@ export function ArchitecturePage() {
groupedAgents[node.group].push(node);
}

// Filtered edges for highlighting
const visibleEdges =
edgeFilter === "all"
? EDGES
: EDGES.filter((e) => e.type === edgeFilter);

// Check if an agent has visible edges
const hasVisibleEdge = (agentId: string) => {
return visibleEdges.some(
(e) => e.from === agentId || e.to === agentId,
);
};

return (
<div className="space-y-6 p-6">
{/* Header */}
Expand Down
2 changes: 1 addition & 1 deletion ui/src/pages/chat/EmptyState.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ const suggestionVariants = {
transition: {
duration: 0.4,
delay: i * 0.08,
ease: [0.25, 0.46, 0.45, 0.94],
ease: [0.25, 0.46, 0.45, 0.94] as [number, number, number, number],
},
}),
};
Expand Down
4 changes: 2 additions & 2 deletions ui/src/pages/chat/MessageBubble.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const messageVariants = {
visible: {
opacity: 1,
y: 0,
transition: { duration: 0.3, ease: [0.25, 0.46, 0.45, 0.94] },
transition: { duration: 0.3, ease: [0.25, 0.46, 0.45, 0.94] as [number, number, number, number] },
},
};

Expand Down Expand Up @@ -164,7 +164,7 @@ export const MessageBubble = memo(function MessageBubble({
)}
</div>
) : (
<MarkdownRenderer content={msg.content} className="text-sm" onCreateProposal={msg.role === "assistant" ? onCreateProposal : undefined} />
<MarkdownRenderer content={msg.content} className="text-sm" />
)}
</div>

Expand Down
5 changes: 2 additions & 3 deletions ui/src/pages/model-registry.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
ChevronDown,
ChevronRight,
MessageSquare,
Settings2,
Zap,
Clock,
DollarSign,
Expand Down Expand Up @@ -37,7 +36,7 @@ const TIME_RANGES: { label: string; hours: number }[] = [
{ label: "30d", hours: 720 },
];

const _FALLBACK_AGENT_ROLES = [
const AGENT_ROLES = [
"architect",
"data_scientist",
"orchestrator",
Expand Down Expand Up @@ -396,7 +395,7 @@ export function ModelRegistryPage() {

// Derive role list from actual data (+ fallback for empty state)
const dataRoles = useMemo(() => {
if (!perfData || perfData.length === 0) return _FALLBACK_AGENT_ROLES;
if (!perfData || perfData.length === 0) return AGENT_ROLES;
const roles = new Set<string>();
for (const p of perfData) {
if (p.agent_role) roles.add(p.agent_role);
Expand Down
2 changes: 0 additions & 2 deletions ui/src/pages/proposals/ProposalDetail.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { useRef, useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import {
X,
FileCheck,
Expand Down Expand Up @@ -121,7 +120,6 @@ function ReviewNotesPanel({ notes }: { notes: ReviewNote[] }) {
}

export function ProposalDetail({ proposalId, onClose }: ProposalDetailProps) {
const navigate = useNavigate();
const overlayRef = useRef<HTMLDivElement>(null);
const { data: detail, isLoading } = useProposal(proposalId);
const approveMut = useApproveProposal();
Expand Down
8 changes: 3 additions & 5 deletions ui/src/pages/usage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {
DollarSign,
Cpu,
Zap,
Clock,
BarChart3,
TrendingUp,
} from "lucide-react";
Expand All @@ -18,7 +17,6 @@ import {
PieChart,
Pie,
Cell,
Legend,
} from "recharts";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
Expand Down Expand Up @@ -208,8 +206,8 @@ export function UsagePage() {
cx="50%"
cy="50%"
outerRadius={90}
label={({ model, percent }) =>
`${model.split("/").pop()} (${(percent * 100).toFixed(0)}%)`
label={({ name, percent }) =>
`${String(name).split("/").pop()} (${((percent ?? 0) * 100).toFixed(0)}%)`
}
labelLine={false}
fontSize={11}
Expand All @@ -222,7 +220,7 @@ export function UsagePage() {
))}
</Pie>
<Tooltip
formatter={(value: number) => formatCurrency(value)}
formatter={(value) => formatCurrency(Number(value))}
contentStyle={{
backgroundColor: "var(--color-card)",
border: "1px solid var(--color-border)",
Expand Down
2 changes: 1 addition & 1 deletion ui/src/vite-env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

interface Window {
__ENV__?: {
API_URL: string;
API_URL?: string;
};
}
3 changes: 1 addition & 2 deletions ui/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
/// <reference types="vitest" />
import { defineConfig } from "vite";
import { defineConfig } from "vitest/config";
import react from "@vitejs/plugin-react";
import tailwindcss from "@tailwindcss/vite";
import path from "path";
Expand Down
Loading