merge(F6): integrate W3 frente F6 into base

This commit is contained in:
diegosouzapw
2026-05-27 22:17:24 -03:00
5 changed files with 252 additions and 0 deletions

View File

@@ -102,6 +102,15 @@ export async function GET() {
"Summarize active rate limits and lockouts",
],
},
{
id: "list-capabilities",
name: "List Capabilities",
description:
"Returns the full catalog of 42 OmniRoute agent skills (22 API + 20 CLI) " +
"with raw URLs for the SKILL.md docs.",
tags: ["discovery", "capabilities"],
examples: ["What can you do?", "List your skills", "Show capabilities"],
},
],
authentication: {
schemes: ["api-key"],

View File

@@ -0,0 +1,72 @@
/**
* A2A Skill: List Capabilities
*
* Returns the full catalog of 42 OmniRoute agent skills (22 API + 20 CLI)
* as a markdown table with raw SKILL.md URLs for orchestrating agents.
*/
import type { A2ATask, TaskArtifact } from "../taskManager";
import { getCatalog, computeCoverage } from "@/lib/agentSkills/catalog";
import type { AgentSkill } from "@/lib/agentSkills/types";
export interface ListCapabilitiesResult {
artifacts: TaskArtifact[];
metadata: {
coverage: {
api: { have: number; total: 22 };
cli: { have: number; total: 20 };
};
totalSkills: 42;
generatedAt: string;
source: "agent-skills-catalog";
};
}
function buildMarkdownTable(skills: AgentSkill[]): string {
const header = "| ID | Name | Category | Area | Endpoints/Commands | Raw URL |";
const separator = "| --- | --- | --- | --- | --- | --- |";
const rows = skills.map((skill) => {
const endpointsOrCommands =
skill.category === "api"
? (skill.endpoints ?? []).join(", ") || "—"
: (skill.cliCommands ?? []).join(", ") || "—";
return `| ${skill.id} | ${skill.name} | ${skill.category} | ${skill.area} | ${endpointsOrCommands} | ${skill.rawUrl} |`;
});
return [header, separator, ...rows].join("\n");
}
export async function executeListCapabilities(_task: A2ATask): Promise<ListCapabilitiesResult> {
const catalog = getCatalog();
const coverage = computeCoverage();
const table = buildMarkdownTable(catalog);
const content = [
`# OmniRoute Agent Skills Catalog`,
``,
`Total: ${catalog.length} skills (${coverage.api.total} API + ${coverage.cli.total} CLI)`,
``,
table,
].join("\n");
return {
artifacts: [
{
type: "text",
content,
},
],
metadata: {
coverage: {
api: { have: coverage.api.have, total: 22 },
cli: { have: coverage.cli.have, total: 20 },
},
totalSkills: 42,
generatedAt: coverage.generatedAt,
source: "agent-skills-catalog",
},
};
}

View File

@@ -37,6 +37,10 @@ export const A2A_SKILL_HANDLERS: Record<string, A2ASkillHandler> = {
const skillModule = await import("./skills/healthReport");
return skillModule.executeHealthReport(task);
},
"list-capabilities": async (task) => {
const skillModule = await import("./skills/listCapabilities");
return skillModule.executeListCapabilities(task);
},
};
export async function executeA2ATaskWithState(

View File

@@ -0,0 +1,84 @@
/**
* Unit tests for GET /.well-known/agent.json (Agent Card endpoint).
*
* Verifies:
* - Response includes 6 skills after the list-capabilities addition
* - list-capabilities entry has the required id, tags, and examples
*/
import test from "node:test";
import assert from "node:assert/strict";
const { GET } = await import("../../src/app/.well-known/agent.json/route.js");
interface AgentSkillEntry {
id: string;
name: string;
description: string;
tags: string[];
examples: string[];
}
interface AgentCard {
name: string;
version: string;
skills: AgentSkillEntry[];
}
test("GET /.well-known/agent.json returns 6 skills", async () => {
const response = await GET();
assert.equal(response.status, 200, "Expected HTTP 200");
const body = (await response.json()) as AgentCard;
assert.ok(Array.isArray(body.skills), "skills is an array");
assert.equal(body.skills.length, 6, "Expected exactly 6 skills");
});
test("Agent Card includes list-capabilities skill entry", async () => {
const response = await GET();
const body = (await response.json()) as AgentCard;
const skill = body.skills.find((s) => s.id === "list-capabilities");
assert.ok(skill, "list-capabilities skill must be present in Agent Card");
});
test("list-capabilities entry has required tags [discovery, capabilities]", async () => {
const response = await GET();
const body = (await response.json()) as AgentCard;
const skill = body.skills.find((s) => s.id === "list-capabilities");
assert.ok(skill, "list-capabilities skill must be present");
assert.ok(Array.isArray(skill.tags), "tags is an array");
assert.ok(skill.tags.includes("discovery"), "tags includes 'discovery'");
assert.ok(skill.tags.includes("capabilities"), "tags includes 'capabilities'");
});
test("list-capabilities entry has at least one example question", async () => {
const response = await GET();
const body = (await response.json()) as AgentCard;
const skill = body.skills.find((s) => s.id === "list-capabilities");
assert.ok(skill, "list-capabilities skill must be present");
assert.ok(Array.isArray(skill.examples), "examples is an array");
assert.ok(skill.examples.length > 0, "examples has at least one entry");
});
test("Agent Card includes all 5 original skills", async () => {
const response = await GET();
const body = (await response.json()) as AgentCard;
const originalIds = [
"smart-routing",
"quota-management",
"provider-discovery",
"cost-analysis",
"health-report",
];
for (const id of originalIds) {
assert.ok(
body.skills.some((s) => s.id === id),
`Original skill '${id}' must be present in Agent Card`,
);
}
});

View File

@@ -0,0 +1,83 @@
/**
* Unit tests for the A2A list-capabilities skill.
*
* Verifies:
* - Return shape matches §3.7 contract
* - Markdown table contains all 42 skill IDs
* - Coverage bounds are within declared totals
* - metadata.source === "agent-skills-catalog"
* - metadata.generatedAt is an ISO datetime string
*/
import assert from "node:assert/strict";
import { test } from "node:test";
import type { A2ATask } from "../../src/lib/a2a/taskManager.js";
import { executeListCapabilities } from "../../src/lib/a2a/skills/listCapabilities.js";
import { API_SKILL_IDS, CLI_SKILL_IDS } from "../../src/lib/agentSkills/catalog.js";
// Minimal stub — executeListCapabilities only receives the task arg but does not use it
const stubTask = {} as A2ATask;
test("executeListCapabilities returns shape matching §3.7 contract", async () => {
const result = await executeListCapabilities(stubTask);
// artifacts array with exactly 1 text artifact
assert.ok(Array.isArray(result.artifacts), "artifacts is an array");
assert.equal(result.artifacts.length, 1, "exactly 1 artifact");
assert.equal(result.artifacts[0].type, "text", "artifact type is 'text'");
assert.ok(typeof result.artifacts[0].content === "string", "artifact content is a string");
// metadata shape
const { metadata } = result;
assert.ok(metadata, "metadata exists");
assert.equal(metadata.source, "agent-skills-catalog", "metadata.source matches");
assert.equal(metadata.totalSkills, 42, "metadata.totalSkills === 42");
assert.ok(metadata.coverage, "metadata.coverage exists");
assert.ok(metadata.coverage.api, "metadata.coverage.api exists");
assert.ok(metadata.coverage.cli, "metadata.coverage.cli exists");
assert.equal(metadata.coverage.api.total, 22, "api.total === 22");
assert.equal(metadata.coverage.cli.total, 20, "cli.total === 20");
});
test("executeListCapabilities markdown table contains all 42 skill IDs", async () => {
const result = await executeListCapabilities(stubTask);
const content = result.artifacts[0].content;
const allIds = [...API_SKILL_IDS, ...CLI_SKILL_IDS] as string[];
assert.equal(allIds.length, 42, "catalog declares 42 skill IDs");
for (const id of allIds) {
assert.ok(content.includes(id), `Markdown table missing skill ID: ${id}`);
}
});
test("metadata.coverage.api.have is within [0, 22]", async () => {
const result = await executeListCapabilities(stubTask);
const { api } = result.metadata.coverage;
assert.ok(api.have >= 0, "api.have >= 0");
assert.ok(api.have <= 22, "api.have <= 22");
});
test("metadata.coverage.cli.have is within [0, 20]", async () => {
const result = await executeListCapabilities(stubTask);
const { cli } = result.metadata.coverage;
assert.ok(cli.have >= 0, "cli.have >= 0");
assert.ok(cli.have <= 20, "cli.have <= 20");
});
test("metadata.generatedAt is a valid ISO datetime", async () => {
const result = await executeListCapabilities(stubTask);
const { generatedAt } = result.metadata;
assert.ok(typeof generatedAt === "string", "generatedAt is a string");
const parsed = new Date(generatedAt);
assert.ok(!isNaN(parsed.getTime()), `generatedAt is not a valid date: ${generatedAt}`);
assert.ok(generatedAt.includes("T"), "generatedAt contains 'T' (ISO format)");
});
test("list-capabilities is registered in A2A_SKILL_HANDLERS", async () => {
const { A2A_SKILL_HANDLERS } = await import("../../src/lib/a2a/taskExecution.js");
assert.ok(
"list-capabilities" in A2A_SKILL_HANDLERS,
"list-capabilities must be in A2A_SKILL_HANDLERS",
);
});