Skip to content
Closed
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
189 changes: 189 additions & 0 deletions apps/webapp/app/routes/agents.$agentId.status.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
import type { LoaderFunctionArgs } from "@remix-run/node";
import { json } from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";
import { typedjson, useTypedLoaderData } from "remix-typedjson";
import { Header1, Header2 } from "~/components/primitives/Headers";
import { PageBody, PageContainer } from "~/components/layout/AppLayout";
import { Paragraph } from "~/components/primitives/Paragraph";
import { prisma } from "~/db.server";
import { requireUser } from "~/services/auth.server";
import {
Table,
TableBody,
TableCell,
TableHeader,
TableHeaderCell,
TableRow,
} from "~/components/primitives/Table";

export const loader = async ({ request, params }: LoaderFunctionArgs) => {
const user = await requireUser(request);
const { agentId } = params;

if (!agentId) {
throw new Response("Not found", { status: 404 });
}

// Get agent config
const agentConfig = await prisma.agentConfig.findUnique({
where: { id: agentId },
include: {
executions: {
orderBy: { createdAt: "desc" },
take: 10,
},
healthChecks: {
orderBy: { createdAt: "desc" },
take: 5,
},
},
});

if (!agentConfig || agentConfig.userId !== user.id) {
throw new Response("Not found", { status: 404 });
}

return typedjson({
agentConfig,
});
};

function getStatusColor(status: string) {
switch (status) {
case "healthy":
return "text-green-600 bg-green-50";
case "unhealthy":
return "text-red-600 bg-red-50";
case "provisioning":
return "text-yellow-600 bg-yellow-50";
default:
return "text-gray-600 bg-gray-50";
}
}

export default function AgentStatus() {
const { agentConfig } = useTypedLoaderData<typeof loader>();

return (
<PageContainer>
<PageBody>
<Header1>{agentConfig.name}</Header1>

<div className="grid grid-cols-2 gap-6 mt-8">
{/* Basic Info */}
<div className="bg-gray-50 rounded-lg p-6">
<Header2>Configuration</Header2>
<div className="space-y-3 mt-4 text-sm">
<div>
<span className="font-medium">Status:</span>
<span className={`ml-2 px-2 py-1 rounded ${getStatusColor(agentConfig.status)}`}>
{agentConfig.status}
</span>
</div>
<div>
<span className="font-medium">Model:</span>
<span className="ml-2">{agentConfig.model}</span>
</div>
<div>
<span className="font-medium">Platform:</span>
<span className="ml-2">{agentConfig.messagingPlatform}</span>
</div>
<div>
<span className="font-medium">Container:</span>
<span className="ml-2">
{agentConfig.containerName && agentConfig.containerPort
? `${agentConfig.containerName}:${agentConfig.containerPort}`
: "Not provisioned"}
</span>
</div>
<div>
<span className="font-medium">Created:</span>
<span className="ml-2">{new Date(agentConfig.createdAt).toLocaleString()}</span>
</div>
</div>
</div>

{/* Tools */}
<div className="bg-gray-50 rounded-lg p-6">
<Header2>Tools</Header2>
<div className="mt-4">
{Array.isArray(agentConfig.tools) && agentConfig.tools.length > 0 ? (
<ul className="space-y-2">
{(agentConfig.tools as string[]).map((tool) => (
<li key={tool} className="text-sm">
✓ {tool}
</li>
))}
</ul>
) : (
<Paragraph>No tools configured</Paragraph>
)}
</div>
</div>
</div>

{/* Recent Executions */}
{agentConfig.executions.length > 0 && (
<div className="mt-8">
<Header2>Recent Executions</Header2>
<Table>
<TableHeader>
<TableRow>
<TableHeaderCell>Message</TableHeaderCell>
<TableHeaderCell>Response</TableHeaderCell>
<TableHeaderCell>Time (ms)</TableHeaderCell>
<TableHeaderCell>Date</TableHeaderCell>
</TableRow>
</TableHeader>
<TableBody>
{agentConfig.executions.map((exec) => (
<TableRow key={exec.id}>
<TableCell className="max-w-xs truncate">{exec.message}</TableCell>
<TableCell className="max-w-xs truncate">{exec.response}</TableCell>
<TableCell>{exec.executionTimeMs}ms</TableCell>
<TableCell>{new Date(exec.createdAt).toLocaleString()}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
)}

{/* Health History */}
{agentConfig.healthChecks.length > 0 && (
<div className="mt-8">
<Header2>Health Checks</Header2>
<Table>
<TableHeader>
<TableRow>
<TableHeaderCell>Status</TableHeaderCell>
<TableHeaderCell>Response Time</TableHeaderCell>
<TableHeaderCell>Date</TableHeaderCell>
</TableRow>
</TableHeader>
<TableBody>
{agentConfig.healthChecks.map((check) => (
<TableRow key={check.id}>
<TableCell>
<span
className={`px-2 py-1 rounded text-sm ${
check.isHealthy
? "bg-green-50 text-green-600"
: "bg-red-50 text-red-600"
}`}
>
{check.isHealthy ? "✓ Healthy" : "✗ Unhealthy"}
</span>
</TableCell>
<TableCell>{check.responseTimeMs}ms</TableCell>
<TableCell>{new Date(check.createdAt).toLocaleString()}</TableCell>
Comment on lines +176 to +179
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Handle null responseTimeMs gracefully.

responseTimeMs can be null (per schema default is 0, but the provisioning endpoint creates records without setting it). This could display "nullms" in the UI.

💡 Proposed fix
-                    <TableCell>{check.responseTimeMs}ms</TableCell>
+                    <TableCell>{check.responseTimeMs != null ? `${check.responseTimeMs}ms` : "—"}</TableCell>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
</span>
</TableCell>
<TableCell>{check.responseTimeMs}ms</TableCell>
<TableCell>{new Date(check.createdAt).toLocaleString()}</TableCell>
</span>
</TableCell>
<TableCell>{check.responseTimeMs != null ? `${check.responseTimeMs}ms` : "—"}</TableCell>
<TableCell>{new Date(check.createdAt).toLocaleString()}</TableCell>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/webapp/app/routes/agents`.$agentId.status.tsx around lines 176 - 179,
The TableCell currently renders check.responseTimeMs directly which can be null
and show "nullms"; update the rendering in the JSX where check.responseTimeMs is
used so it safely handles null/undefined (e.g., use a fallback like 0 or "—")
and append "ms" only for numeric values; locate the TableCell that references
check.responseTimeMs and change it to conditionally render (check.responseTimeMs
!= null ? `${check.responseTimeMs}ms` : "—") so null values display gracefully
while preserving existing behavior for numbers, and similarly ensure date
rendering (new Date(check.createdAt).toLocaleString()) remains unchanged.

</TableRow>
))}
</TableBody>
</Table>
</div>
)}
</PageBody>
</PageContainer>
);
}
Loading