diff --git a/data/agent-profiles.json b/data/agent-profiles.json
index 10dc5ac7..53597cde 100644
--- a/data/agent-profiles.json
+++ b/data/agent-profiles.json
@@ -22,8 +22,9 @@
],
"hostPlatform": "openclaw",
"createdAt": "2026-02-04T00:00:00.000Z",
- "trustScore": 0,
- "stackTitle": "Kai’s Stack"
+ "stackTitle": "Kai\u2019s Stack",
+ "isVerified": false,
+ "missingVerificationPrereqs": true
},
{
"id": "agent_scout_seed",
@@ -46,8 +47,9 @@
],
"hostPlatform": "openclaw",
"createdAt": "2026-02-04T00:00:00.000Z",
- "trustScore": 0,
- "stackTitle": "Scout’s Research Stack"
+ "stackTitle": "Scout\u2019s Research Stack",
+ "isVerified": true,
+ "trustScore": 82
},
{
"id": "agent_link_seed",
@@ -70,7 +72,6 @@
],
"hostPlatform": "openclaw",
"createdAt": "2026-02-04T00:00:00.000Z",
- "trustScore": 0,
- "stackTitle": "Link’s Builder Stack"
+ "stackTitle": "Link\u2019s Builder Stack"
}
]
diff --git a/src/app/agents/[handle]/page.tsx b/src/app/agents/[handle]/page.tsx
index 5b7bd204..c3026a77 100644
--- a/src/app/agents/[handle]/page.tsx
+++ b/src/app/agents/[handle]/page.tsx
@@ -49,7 +49,7 @@ export async function generateMetadata({ params }: { params: Promise<{ handle: s
links: profileForHandle.agentJsonUrl ? { agentJson: profileForHandle.agentJsonUrl } : {},
featured: false,
joinedAt: profileForHandle.createdAt,
- verified: false,
+ verified: profileForHandle.isVerified ?? false,
trustScore: profileForHandle.trustScore,
activity: [],
}
@@ -109,6 +109,50 @@ const platformColors: Record = {
github: "bg-[#8B5CF6]/10 text-[#8B5CF6] border-[#8B5CF6]/20",
};
+type TrustState = "known" | "unknown" | "unverified";
+
+function getTrustState(input: {
+ trustScore?: number;
+ isVerified?: boolean;
+ missingVerificationPrereqs?: boolean;
+}): TrustState {
+ if (typeof input.trustScore === "number" && Number.isFinite(input.trustScore)) {
+ return "known";
+ }
+ if (input.isVerified === false || input.missingVerificationPrereqs) {
+ return "unverified";
+ }
+ return "unknown";
+}
+
+function trustDisplay(input: {
+ trustScore?: number;
+ isVerified?: boolean;
+ missingVerificationPrereqs?: boolean;
+}) {
+ const state = getTrustState(input);
+ if (state === "known") {
+ const score = Math.max(0, Math.min(100, Math.round(input.trustScore ?? 0)));
+ return {
+ title: `Trust score: ${score}`,
+ helper: "Calculated from verification signals and profile quality.",
+ className: "bg-emerald-500/15 text-emerald-300 border-emerald-500/40",
+ };
+ }
+ if (state === "unverified") {
+ return {
+ title: "Trust score: Unverified",
+ helper: "Complete verification to receive a trust score.",
+ className: "bg-amber-500/15 text-amber-300 border-amber-500/40",
+ };
+ }
+ return {
+ title: "Trust score: Not available yet",
+ helper: "We’re still calculating this score.",
+ className: "bg-white/5 text-muted-foreground border-white/15",
+ };
+}
+
export default async function AgentProfilePage({ params }: { params: Promise<{ handle: string }> }) {
const { handle } = await params;
@@ -130,7 +174,7 @@ export default async function AgentProfilePage({ params }: { params: Promise<{ h
links: profileForHandle.agentJsonUrl ? { agentJson: profileForHandle.agentJsonUrl } : {},
featured: false,
joinedAt: profileForHandle.createdAt,
- verified: false,
+ verified: profileForHandle.isVerified ?? false,
trustScore: profileForHandle.trustScore,
activity: [],
}
@@ -154,6 +198,12 @@ export default async function AgentProfilePage({ params }: { params: Promise<{ h
const allAgents = getAgents().filter((a) => a.handle !== handle);
const relatedAgents = allAgents.slice(0, 4);
+ const trust = trustDisplay({
+ trustScore: agent.trustScore,
+ isVerified: profileForHandle?.isVerified ?? agent.verified,
+ missingVerificationPrereqs: profileForHandle?.missingVerificationPrereqs,
+ });
+
const jsonLd = {
"@context": "https://schema.org",
"@type": "ProfilePage",
@@ -218,33 +268,18 @@ export default async function AgentProfilePage({ params }: { params: Promise<{ h
{profileForHandle?.bio || profileForHandle?.description || agent.description}
- {/* Trust Score Badge (v2 feature) */}
- {agent.trustScore !== undefined && (
-
-
-
-
Trust Score
-
- Based on verification, activity, and community feedback
-
-
-
-
= 95 ? "text-emerald-400" :
- agent.trustScore >= 85 ? "text-cyan-400" :
- agent.trustScore >= 75 ? "text-amber-400" : "text-orange-400"
- }`}>
- {agent.trustScore}
-
-
- {agent.trustScore >= 95 ? "Excellent" :
- agent.trustScore >= 85 ? "Very Good" :
- agent.trustScore >= 75 ? "Good" : "Fair"}
-
-
+ {/* Trust Score State */}
+
+
+
+
Trust Score
+
{trust.helper}
+
+ {trust.title}
+
- )}
+
{/* Installed skills */}
diff --git a/src/app/agents/agents-page-client.tsx b/src/app/agents/agents-page-client.tsx
index a6db745f..4f056e3f 100644
--- a/src/app/agents/agents-page-client.tsx
+++ b/src/app/agents/agents-page-client.tsx
@@ -15,7 +15,9 @@ export type DirectoryAgent = {
capabilities: string[];
hostPlatform: string;
createdAt: string;
- trustScore: number;
+ trustScore?: number;
+ isVerified?: boolean;
+ missingVerificationPrereqs?: boolean;
agentJsonUrl?: string;
};
@@ -28,6 +30,8 @@ type SeedAgentShape = {
platforms?: string[];
joinedAt?: string;
trustScore?: number;
+ isVerified?: boolean;
+ missingVerificationPrereqs?: boolean;
links?: { agentJson?: string };
};
@@ -50,7 +54,13 @@ function normalizeAgent(input: DirectoryAgent | SeedAgentShape): DirectoryAgent
capabilities: Array.isArray(input.skills) ? input.skills : [],
hostPlatform: Array.isArray(input.platforms) && input.platforms.length > 0 ? input.platforms[0] : "openclaw",
createdAt: input.joinedAt || new Date(0).toISOString(),
- trustScore: input.trustScore ?? 0,
+ ...(typeof input.trustScore === "number" && Number.isFinite(input.trustScore)
+ ? { trustScore: Math.max(0, Math.min(100, Math.round(input.trustScore))) }
+ : {}),
+ ...(typeof input.isVerified === "boolean" ? { isVerified: input.isVerified } : {}),
+ ...(typeof input.missingVerificationPrereqs === "boolean"
+ ? { missingVerificationPrereqs: input.missingVerificationPrereqs }
+ : {}),
...(input.links?.agentJson ? { agentJsonUrl: input.links.agentJson } : {}),
};
}
@@ -96,7 +106,7 @@ export function AgentsPageClient({ agents: initialAgents }: AgentsPageClientProp
}, [searchQuery, platformFilter, sort]);
const featuredAgents = useMemo(
- () => agents.filter((agent) => agent.trustScore >= 80).slice(0, 6),
+ () => agents.filter((agent) => typeof agent.trustScore === "number" && agent.trustScore >= 80).slice(0, 6),
[agents]
);
@@ -207,7 +217,47 @@ export function AgentsPageClient({ agents: initialAgents }: AgentsPageClientProp
);
}
+type TrustState = "known" | "unknown" | "unverified";
+
+function getTrustState(agent: DirectoryAgent): TrustState {
+ if (typeof agent.trustScore === "number" && Number.isFinite(agent.trustScore)) {
+ return "known";
+ }
+ if (agent.isVerified === false || agent.missingVerificationPrereqs) {
+ return "unverified";
+ }
+ return "unknown";
+}
+
+function trustBadge(state: TrustState, score?: number): { label: string; helper: string; className: string } {
+ if (state === "known") {
+ const clamped = Math.max(0, Math.min(100, Math.round(score ?? 0)));
+ return {
+ label: `Trust score: ${clamped}`,
+ helper: "Calculated from verification signals and profile quality.",
+ className: "bg-emerald-500/15 text-emerald-300 border-emerald-500/40",
+ };
+ }
+
+ if (state === "unverified") {
+ return {
+ label: "Trust score: Unverified",
+ helper: "Complete verification to receive a trust score.",
+ className: "bg-amber-500/15 text-amber-300 border-amber-500/40",
+ };
+ }
+
+ return {
+ label: "Trust score: Not available yet",
+ helper: "We’re still calculating this score.",
+ className: "bg-white/5 text-muted-foreground border-white/15",
+ };
+}
+
function AgentCard({ agent }: { agent: DirectoryAgent }) {
+ const state = getTrustState(agent);
+ const trust = trustBadge(state, agent.trustScore);
+
return (
@@ -224,14 +274,15 @@ function AgentCard({ agent }: { agent: DirectoryAgent }) {
{agent.description}
-
+
{agent.hostPlatform}
-
- trust {agent.trustScore}
+
+ {trust.label}
+
{trust.helper}
{agent.capabilities.length > 0 && (
diff --git a/src/app/skills/[slug]/page.tsx b/src/app/skills/[slug]/page.tsx
index 859a7234..cc155c8a 100644
--- a/src/app/skills/[slug]/page.tsx
+++ b/src/app/skills/[slug]/page.tsx
@@ -32,17 +32,20 @@ export function generateStaticParams() {
return getSkills().map((skill) => ({ slug: skill.slug }));
}
-export function generateMetadata({ params }: { params: { slug: string } }) {
- const skill = getSkillBySlug(params.slug);
- if (!skill) return { title: "Skill Not Found" };
-
+export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }) {
+ const { slug } = await params;
+ const skill = getSkillBySlug(slug);
+ if (!skill) return { title: "Skill Details | forAgents.dev" };
+
+ const safeName = (skill.name || "").trim();
+ const pageTitle = safeName ? `${safeName} | forAgents.dev` : "Skill Details | forAgents.dev";
const ogImageUrl = `https://foragents.dev/api/og/skill/${skill.slug}`;
-
+
return {
- title: `${skill.name} — forAgents.dev`,
+ title: pageTitle,
description: skill.description,
openGraph: {
- title: `${skill.name} — forAgents.dev`,
+ title: pageTitle,
description: skill.description,
url: `https://foragents.dev/skills/${skill.slug}`,
siteName: "forAgents.dev",
@@ -58,7 +61,7 @@ export function generateMetadata({ params }: { params: { slug: string } }) {
},
twitter: {
card: "summary_large_image",
- title: `${skill.name} — forAgents.dev`,
+ title: pageTitle,
description: skill.description,
images: [ogImageUrl],
},
diff --git a/src/lib/server/agentProfiles.ts b/src/lib/server/agentProfiles.ts
index 5b8c6bbc..d094bad1 100644
--- a/src/lib/server/agentProfiles.ts
+++ b/src/lib/server/agentProfiles.ts
@@ -14,7 +14,9 @@ export type AgentProfileRecord = {
hostPlatform: HostPlatform | string;
agentJsonUrl?: string;
createdAt: string;
- trustScore: number;
+ trustScore?: number;
+ isVerified?: boolean;
+ missingVerificationPrereqs?: boolean;
// Legacy compatibility fields used by existing pages/features
domain?: string;
@@ -69,7 +71,13 @@ function normalizeRecord(row: Partial
): AgentProfileRecord {
const hostPlatform = String(row.hostPlatform ?? "openclaw").trim().toLowerCase();
const createdAt = toISOStringOrEpoch(row.createdAt);
- const trustScore = typeof row.trustScore === "number" && Number.isFinite(row.trustScore) ? row.trustScore : 0;
+ const trustScore = typeof row.trustScore === "number" && Number.isFinite(row.trustScore)
+ ? Math.max(0, Math.min(100, Math.round(row.trustScore)))
+ : undefined;
+ const isVerified = typeof row.isVerified === "boolean" ? row.isVerified : undefined;
+ const missingVerificationPrereqs = typeof row.missingVerificationPrereqs === "boolean"
+ ? row.missingVerificationPrereqs
+ : undefined;
return {
id: String(row.id ?? `agent_${handle || "unknown"}`),
@@ -82,7 +90,9 @@ function normalizeRecord(row: Partial): AgentProfileRecord {
? { agentJsonUrl: row.agentJsonUrl.trim() }
: {}),
createdAt,
- trustScore,
+ ...(typeof trustScore === "number" ? { trustScore } : {}),
+ ...(typeof isVerified === "boolean" ? { isVerified } : {}),
+ ...(typeof missingVerificationPrereqs === "boolean" ? { missingVerificationPrereqs } : {}),
...(typeof row.domain === "string" && row.domain.trim() ? { domain: row.domain.trim().toLowerCase() } : {}),
...(typeof row.bio === "string" && row.bio.trim() ? { bio: row.bio.trim() } : {}),
@@ -127,7 +137,9 @@ export async function listAgentProfiles(options: AgentListOptions = {}): Promise
result = [...result].sort((a, b) => {
if (sort === "trust") {
- if (b.trustScore !== a.trustScore) return b.trustScore - a.trustScore;
+ const aScore = typeof a.trustScore === "number" ? a.trustScore : -1;
+ const bScore = typeof b.trustScore === "number" ? b.trustScore : -1;
+ if (bScore !== aScore) return bScore - aScore;
return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime();
}
@@ -177,7 +189,8 @@ export async function createAgentProfile(input: CreateAgentProfileInput): Promis
? { agentJsonUrl: input.agentJsonUrl.trim() }
: {}),
createdAt: now,
- trustScore: 0,
+ isVerified: false,
+ missingVerificationPrereqs: true,
// Keep legacy fields in sync where useful
bio: description,