chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:58:18 +08:00
commit 6d5d58c1a9
18293 changed files with 3502153 additions and 0 deletions
@@ -0,0 +1,406 @@
import { describe, expect, it, test } from "vitest";
import * as fs from "node:fs";
import * as path from "node:path";
const repoRoot = path.resolve(__dirname, "..", "..");
const integrationsDir = path.join(repoRoot, "examples", "integrations");
const migratedIntegrations = [
"crewai-flows",
"llamaindex",
"langgraph-fastapi",
"pydantic-ai",
"mcp-apps",
"agent-spec",
"strands-python",
"crewai-crews",
] as const;
const a2aMiddlewareRoot = path.join(integrationsDir, "a2a-middleware");
const appRoots: Record<(typeof migratedIntegrations)[number], string> = {
"crewai-flows": "src/app",
llamaindex: "src/app",
"langgraph-fastapi": "src/app",
"pydantic-ai": "src/app",
"mcp-apps": "app",
"agent-spec": "src/app",
"strands-python": "src/app",
"crewai-crews": "src/app",
};
function readIntegrationFile(
integration: string,
relativePath: string,
): string {
return fs.readFileSync(
path.join(integrationsDir, integration, relativePath),
"utf8",
);
}
function readOptionalIntegrationFile(
integration: string,
relativePath: string,
): string {
const filePath = path.join(integrationsDir, integration, relativePath);
return fs.existsSync(filePath) ? fs.readFileSync(filePath, "utf8") : "";
}
function readA2AMiddlewareFile(pathFromRoot: string): string {
return fs.readFileSync(path.join(a2aMiddlewareRoot, pathFromRoot), "utf8");
}
describe("batch-2 Intelligence integration migration", () => {
for (const integration of migratedIntegrations) {
it(`${integration} has the env-gated Intelligence runtime route`, () => {
const route = readIntegrationFile(
integration,
`${appRoots[integration]}/api/copilotkit/[[...slug]]/route.ts`,
);
expect(route).toContain("CopilotKitIntelligence");
expect(route).toContain("process.env.COPILOTKIT_LICENSE_TOKEN");
expect(route).toContain(
"licenseToken: process.env.COPILOTKIT_LICENSE_TOKEN",
);
expect(route).toContain('id: "demo-user"');
expect(route).toContain("new InMemoryAgentRunner()");
expect(route).toContain("export const GET = handle(app)");
expect(route).toContain("export const POST = handle(app)");
expect(route).toContain("export const PATCH = handle(app)");
expect(route).toContain("export const DELETE = handle(app)");
});
it(`${integration} forces REST transport for thread routes`, () => {
const layout = readIntegrationFile(
integration,
`${appRoots[integration]}/layout.tsx`,
);
const page = readIntegrationFile(
integration,
`${appRoots[integration]}/page.tsx`,
);
expect(`${layout}\n${page}`).toContain("useSingleEndpoint={false}");
});
it(`${integration} wires the threads drawer into the chat thread context`, () => {
const page = readIntegrationFile(
integration,
`${appRoots[integration]}/page.tsx`,
);
expect(page).toContain("ThreadsDrawer");
expect(page).toContain("ThreadsPanelGate");
expect(page).toContain("CopilotChatConfigurationProvider");
expect(page).toContain("threadId");
expect(page).toContain("onThreadChange={setThreadId}");
if (integration === "mcp-apps") {
expect(page).toContain('key={threadId ?? "new-thread"}');
expect(page).toContain("threadId={threadId}");
const drawer = readIntegrationFile(
integration,
"app/components/threads-drawer/threads-drawer.tsx",
);
expect(drawer).toContain("onThreadChange(undefined)");
expect(drawer).not.toContain("crypto.randomUUID()");
}
});
it(`${integration} exposes the client-safe threads enabled gate`, () => {
const nextConfig = readIntegrationFile(integration, "next.config.ts");
expect(nextConfig).toContain("NEXT_PUBLIC_COPILOTKIT_THREADS_ENABLED");
expect(nextConfig).toContain("process.env.COPILOTKIT_LICENSE_TOKEN");
});
it(`${integration} documents the local Intelligence environment`, () => {
const envExample = readOptionalIntegrationFile(
integration,
".env.example",
);
expect(envExample).toContain("COPILOTKIT_LICENSE_TOKEN");
expect(envExample).toContain("INTELLIGENCE_API_KEY");
expect(envExample).toContain("INTELLIGENCE_API_URL");
expect(envExample).toContain("INTELLIGENCE_GATEWAY_WS_URL");
});
it(`${integration} pins CopilotKit packages to the threads-capable release`, () => {
const packageJson = JSON.parse(
readIntegrationFile(integration, "package.json"),
) as { dependencies?: Record<string, string> };
expect(packageJson.dependencies?.["@copilotkit/react-core"]).toBe(
"1.59.3",
);
expect(packageJson.dependencies?.["@copilotkit/runtime"]).toBe("1.59.3");
});
}
});
test("a2a-middleware runtime route is gated for Intelligence threads", () => {
const route = readA2AMiddlewareFile(
"app/api/copilotkit/[[...slug]]/route.ts",
);
expect(route).toContain("CopilotKitIntelligence");
expect(route).toContain(
"class RuntimeA2AMiddlewareAgent extends A2AMiddlewareAgent",
);
expect(route).toContain("const isolatedAgent = new A2AMiddlewareAgent");
expect(route).toContain("new HttpAgent({");
expect(route).toContain("isolatedAgent.setMessages(parameters.messages)");
expect(route).toContain("return isolatedAgent.runAgent(");
expect(route).toContain("process.env.COPILOTKIT_LICENSE_TOKEN");
expect(route).toContain("process.env.INTELLIGENCE_API_KEY");
expect(route).toContain("process.env.INTELLIGENCE_API_URL");
expect(route).toContain("process.env.INTELLIGENCE_GATEWAY_WS_URL");
expect(route).toContain('id: "demo-user"');
expect(route).toContain("licenseToken: process.env.COPILOTKIT_LICENSE_TOKEN");
expect(route).toContain(": { runner: new InMemoryAgentRunner() }");
expect(route).toContain("export const GET = handle(app);");
expect(route).toContain("export const POST = handle(app);");
expect(route).toContain("export const PATCH = handle(app);");
expect(route).toContain("export const DELETE = handle(app);");
});
test("a2a-middleware preserves its three-agent URL configuration", () => {
const route = readA2AMiddlewareFile(
"app/api/copilotkit/[[...slug]]/route.ts",
);
expect(route).toContain("process.env.RESEARCH_AGENT_URL");
expect(route).toContain("process.env.ANALYSIS_AGENT_URL");
expect(route).toContain("process.env.ORCHESTRATOR_URL");
expect(route).toContain('agentId: "a2a_chat"');
expect(route).toContain("agentUrls: [researchAgentUrl, analysisAgentUrl]");
expect(route).toContain("orchestrationAgentUrl: orchestratorUrl");
});
test("a2a-middleware page uses REST transport for Threads APIs", () => {
const page = readA2AMiddlewareFile("app/page.tsx");
expect(page).toContain('runtimeUrl="/api/copilotkit"');
expect(page).toContain("useSingleEndpoint={false}");
expect(page).toContain('agentId="a2a_chat"');
expect(page).toContain("ThreadsDrawer");
expect(page).toContain("ThreadsPanelGate");
expect(page).toContain("CopilotChatConfigurationProvider");
expect(page).toContain("const [threadId, setThreadId]");
expect(page).toContain("threadId={threadId}");
});
test("a2a-middleware chat keeps A2A visualization tools inside the configured chat", () => {
const chat = readA2AMiddlewareFile("components/chat.tsx");
expect(chat).toContain("useFrontendTool");
expect(chat).toContain('name: "send_message_to_a2a_agent"');
expect(chat).toContain("MessageToA2A");
expect(chat).toContain("MessageFromA2A");
expect(chat).not.toContain("<CopilotKit");
});
test("a2a-middleware exposes local Intelligence env documentation", () => {
const envExample = readA2AMiddlewareFile(".env.example");
expect(envExample).toContain("GOOGLE_API_KEY=");
expect(envExample).toContain("OPENAI_API_KEY=");
expect(envExample).toContain("COPILOTKIT_LICENSE_TOKEN=");
expect(envExample).toContain("INTELLIGENCE_API_KEY=");
expect(envExample).toContain("INTELLIGENCE_API_URL=http://localhost:4201");
expect(envExample).toContain(
"INTELLIGENCE_GATEWAY_WS_URL=ws://localhost:4401",
);
});
test("a2a-middleware package is pinned to the Intelligence-ready CopilotKit SDK", () => {
const packageJson = JSON.parse(readA2AMiddlewareFile("package.json")) as {
dependencies: Record<string, string>;
};
expect(packageJson.dependencies["@copilotkit/react-core"]).toBe("1.59.3");
expect(packageJson.dependencies["@copilotkit/runtime"]).toBe("1.59.3");
expect(packageJson.dependencies["lucide-react"]).toBeDefined();
});
test("a2a-middleware Next config enables the Threads feature flag", () => {
const nextConfig = readA2AMiddlewareFile("next.config.ts");
expect(nextConfig).toContain('output: "standalone"');
expect(nextConfig).toContain("NEXT_PUBLIC_COPILOTKIT_THREADS_ENABLED");
expect(nextConfig).toContain("process.env");
expect(nextConfig).toContain("COPILOTKIT_LICENSE_TOKEN");
});
const a2aA2uiRoot = path.join(integrationsDir, "a2a-a2ui");
function readA2AA2uiFile(pathFromRoot: string): string {
return fs.readFileSync(path.join(a2aA2uiRoot, pathFromRoot), "utf8");
}
test("a2a-a2ui runtime route is gated for Intelligence threads", () => {
const route = readA2AA2uiFile("app/api/copilotkit/[[...slug]]/route.tsx");
expect(route).toContain("CopilotKitIntelligence");
expect(route).toContain("class RuntimeA2AAgent extends A2AAgent");
expect(route).toContain("const isolatedAgent = new A2AAgent");
expect(route).toContain("isolatedAgent.setMessages(parameters.messages)");
expect(route).toContain("return isolatedAgent.runAgent(");
expect(route).toContain("process.env.COPILOTKIT_LICENSE_TOKEN");
expect(route).toContain("process.env.INTELLIGENCE_API_KEY");
expect(route).toContain("process.env.INTELLIGENCE_API_URL");
expect(route).toContain("process.env.INTELLIGENCE_GATEWAY_WS_URL");
expect(route).toContain('id: "demo-user"');
expect(route).toContain("licenseToken: process.env.COPILOTKIT_LICENSE_TOKEN");
expect(route).toContain(": { runner: new InMemoryAgentRunner() }");
expect(route).toContain("a2ui: {}");
expect(route).toContain("export const GET = handle(app);");
expect(route).toContain("export const POST = handle(app);");
expect(route).toContain("export const PATCH = handle(app);");
expect(route).toContain("export const DELETE = handle(app);");
});
test("a2a-a2ui page uses REST transport for Threads APIs", () => {
const page = readA2AA2uiFile("app/page.tsx");
expect(page).toContain('runtimeUrl="/api/copilotkit"');
expect(page).toContain('agentId="default"');
expect(page).toContain("useSingleEndpoint={false}");
expect(page).toContain("a2ui={{ theme }}");
expect(page).toContain("const activityRenderers = [a2uiV08Renderer];");
expect(page).toContain("renderActivityMessages={activityRenderers}");
});
test("a2a-a2ui page wires a threads drawer into the active chat thread", () => {
const page = readA2AA2uiFile("app/page.tsx");
expect(page).toContain("ThreadsDrawer");
expect(page).toContain("ThreadsPanelGate");
expect(page).toContain("CopilotChatConfigurationProvider");
expect(page).toContain("const [threadId, setThreadId]");
expect(page).toContain('agentId="default"');
expect(page).toContain("threadId={threadId}");
});
test("a2a-a2ui exposes local Intelligence env documentation", () => {
const envExample = readA2AA2uiFile(".env.example");
const gitignore = readA2AA2uiFile(".gitignore");
expect(envExample).toContain("OPENAI_API_KEY=");
expect(envExample).toContain("COPILOTKIT_LICENSE_TOKEN=");
expect(envExample).toContain("INTELLIGENCE_API_KEY=");
expect(envExample).toContain("INTELLIGENCE_API_URL=http://localhost:4201");
expect(envExample).toContain(
"INTELLIGENCE_GATEWAY_WS_URL=ws://localhost:4401",
);
expect(gitignore).toContain("!.env.example");
});
test("a2a-a2ui package is pinned to the Intelligence-ready CopilotKit SDK", () => {
const packageJson = JSON.parse(readA2AA2uiFile("package.json")) as {
dependencies: Record<string, string>;
};
expect(packageJson.dependencies["@copilotkit/a2ui-renderer"]).toBe("1.59.3");
expect(packageJson.dependencies["@copilotkit/react-core"]).toBe("1.59.3");
expect(packageJson.dependencies["@copilotkit/runtime"]).toBe("1.59.3");
expect(packageJson.dependencies["lucide-react"]).toBeDefined();
});
test("a2a-a2ui Next config enables the Threads feature flag", () => {
const nextConfig = readA2AA2uiFile("next.config.js");
expect(nextConfig).toContain('output: "standalone"');
expect(nextConfig).toContain(
"NEXT_PUBLIC_COPILOTKIT_THREADS_ENABLED: process.env.COPILOTKIT_LICENSE_TOKEN",
);
});
const agentcoreRoot = path.join(integrationsDir, "agentcore");
function readAgentcoreFile(pathFromRoot: string): string {
return fs.readFileSync(path.join(agentcoreRoot, pathFromRoot), "utf8");
}
describe("agentcore Intelligence integration migration", () => {
it("gates the Hono runtime bridge with CopilotKit Intelligence", () => {
const runtime = readAgentcoreFile(
"infra-cdk/lambdas/copilotkit-runtime/src/runtime.ts",
);
expect(runtime).toContain("CopilotKitIntelligence");
expect(runtime).toContain("process.env.COPILOTKIT_LICENSE_TOKEN");
expect(runtime).toContain("process.env.INTELLIGENCE_API_KEY");
expect(runtime).toContain("process.env.INTELLIGENCE_API_URL");
expect(runtime).toContain("process.env.INTELLIGENCE_GATEWAY_WS_URL");
expect(runtime).toContain(
"licenseToken: process.env.COPILOTKIT_LICENSE_TOKEN",
);
expect(runtime).toContain('id: "demo-user"');
expect(runtime).toContain(": { runner: new AgentCoreRunner() }");
expect(runtime).toContain('basePath: "/copilotkit"');
});
it("forces REST transport and threads context in the Vite frontend", () => {
const chat = readAgentcoreFile(
"frontend/src/components/chat/CopilotKit/index.tsx",
);
expect(chat).toContain("useSingleEndpoint={false}");
expect(chat).toContain("ThreadsDrawer");
expect(chat).toContain("ThreadsPanelGate");
expect(chat).toContain("CopilotChatConfigurationProvider");
expect(chat).toContain("const [threadId, setThreadId]");
expect(chat).toContain("threadId={threadId}");
expect(chat).toContain("runtimeUrl={runtimeUrl}");
expect(chat).toContain("headers={headers}");
});
it("exposes the client-safe threads enabled gate for Vite", () => {
const viteConfig = readAgentcoreFile("frontend/vite.config.ts");
const lockedState = readAgentcoreFile(
"frontend/src/components/threads-drawer/locked-state.tsx",
);
expect(viteConfig).toContain("VITE_COPILOTKIT_THREADS_ENABLED");
expect(viteConfig).toContain("process.env.COPILOTKIT_LICENSE_TOKEN");
expect(lockedState).toContain(
"import.meta.env.VITE_COPILOTKIT_THREADS_ENABLED",
);
});
it("documents and wires local Intelligence environment variables", () => {
const dockerEnv = readAgentcoreFile("docker/.env.example");
const compose = readAgentcoreFile("docker/docker-compose.yml");
for (const envName of [
"COPILOTKIT_LICENSE_TOKEN",
"INTELLIGENCE_API_KEY",
"INTELLIGENCE_API_URL",
"INTELLIGENCE_GATEWAY_WS_URL",
]) {
expect(dockerEnv).toContain(envName);
expect(compose).toContain(envName);
}
});
it("pins AgentCore frontend and runtime packages to threads-capable versions", () => {
const frontendPackageJson = JSON.parse(
readAgentcoreFile("frontend/package.json"),
) as { dependencies?: Record<string, string> };
const runtimePackageJson = JSON.parse(
readAgentcoreFile("infra-cdk/lambdas/copilotkit-runtime/package.json"),
) as { dependencies?: Record<string, string> };
expect(frontendPackageJson.dependencies?.["@copilotkit/react-core"]).toBe(
"1.59.3",
);
expect(runtimePackageJson.dependencies?.["@copilotkit/runtime"]).toBe(
"1.59.3",
);
expect(runtimePackageJson.dependencies?.["@ag-ui/client"]).toBe("0.0.53");
});
});
@@ -0,0 +1,234 @@
import { mkdtemp, mkdir, writeFile, readFile, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import {
syncPluginSkills,
RESERVED_LIFECYCLE_SLUGS,
} from "../sync-plugin-skills.js";
// Fixture helper — builds a miniature repo layout inside a tmpdir that mimics
// the CopilotKit monorepo shape.
async function makeRepo(root: string) {
const pkgRoot = join(root, "packages");
// Two package meta-skills — deliberately different shapes to exercise
// the recursive-copy path and the single-file path.
await mkdir(join(pkgRoot, "runtime/skills/runtime/references"), {
recursive: true,
});
await writeFile(
join(pkgRoot, "runtime/skills/runtime/SKILL.md"),
"---\nname: runtime\n---\n# Runtime\n",
);
await writeFile(
join(pkgRoot, "runtime/skills/runtime/references/setup-endpoint.md"),
"# Setup\n",
);
await mkdir(join(pkgRoot, "a2ui-renderer/skills/a2ui-renderer"), {
recursive: true,
});
await writeFile(
join(pkgRoot, "a2ui-renderer/skills/a2ui-renderer/SKILL.md"),
"---\nname: a2ui-renderer\n---\n# A2UI\n",
);
// Pre-existing standalone skill at the mirror root — must be left alone.
await mkdir(join(root, "skills/copilotkit-setup"), { recursive: true });
await writeFile(
join(root, "skills/copilotkit-setup/SKILL.md"),
"---\nname: copilotkit-setup\n---\n# Setup\n",
);
}
describe("syncPluginSkills", () => {
let repo: string;
beforeEach(async () => {
repo = await mkdtemp(join(tmpdir(), "ck-plugin-sync-"));
});
afterEach(async () => {
await rm(repo, { recursive: true, force: true });
});
it("copies package skill SKILL.md and references into the mirror", async () => {
await makeRepo(repo);
const result = await syncPluginSkills({ cwd: repo, mode: "write" });
expect(result.exitCode).toBe(0);
const runtimeSkill = await readFile(
join(repo, "skills/runtime/SKILL.md"),
"utf8",
);
expect(runtimeSkill).toBe("---\nname: runtime\n---\n# Runtime\n");
const runtimeRef = await readFile(
join(repo, "skills/runtime/references/setup-endpoint.md"),
"utf8",
);
expect(runtimeRef).toBe("# Setup\n");
const a2uiSkill = await readFile(
join(repo, "skills/a2ui-renderer/SKILL.md"),
"utf8",
);
expect(a2uiSkill).toBe("---\nname: a2ui-renderer\n---\n# A2UI\n");
});
it("does not modify pre-existing standalone skills", async () => {
await makeRepo(repo);
await syncPluginSkills({ cwd: repo, mode: "write" });
const standalone = await readFile(
join(repo, "skills/copilotkit-setup/SKILL.md"),
"utf8",
);
expect(standalone).toBe("---\nname: copilotkit-setup\n---\n# Setup\n");
});
it("errors with exit code 2 if a package skill collides with a reserved lifecycle slug", async () => {
const pkgRoot = join(repo, "packages");
await mkdir(join(pkgRoot, "rogue/skills/copilotkit-setup"), {
recursive: true,
});
await writeFile(
join(pkgRoot, "rogue/skills/copilotkit-setup/SKILL.md"),
"collision\n",
);
const result = await syncPluginSkills({ cwd: repo, mode: "write" });
expect(result.exitCode).toBe(2);
expect(result.message).toMatch(/collides with reserved lifecycle slug/);
});
it("check mode returns exitCode 0 when mirror is in sync", async () => {
await makeRepo(repo);
await syncPluginSkills({ cwd: repo, mode: "write" });
const result = await syncPluginSkills({ cwd: repo, mode: "check" });
expect(result.exitCode).toBe(0);
});
it("check mode returns exitCode 1 when mirror has drifted", async () => {
await makeRepo(repo);
await syncPluginSkills({ cwd: repo, mode: "write" });
// Simulate a maintainer editing the package source without re-running sync.
await writeFile(
join(repo, "packages/runtime/skills/runtime/SKILL.md"),
"---\nname: runtime\n---\n# Runtime (edited)\n",
);
const result = await syncPluginSkills({ cwd: repo, mode: "check" });
expect(result.exitCode).toBe(1);
expect(result.message).toMatch(/drift detected/i);
expect(result.message).toContain("skills/runtime/SKILL.md");
});
it("check mode flags orphan files in the mirror (e.g., skill deleted from source)", async () => {
await makeRepo(repo);
await syncPluginSkills({ cwd: repo, mode: "write" });
await rm(join(repo, "packages/a2ui-renderer"), { recursive: true });
const result = await syncPluginSkills({ cwd: repo, mode: "check" });
expect(result.exitCode).toBe(1);
expect(result.message).toMatch(/orphan/i);
expect(result.message).toContain("skills/a2ui-renderer");
});
it("exports the reserved lifecycle slug set", () => {
expect(RESERVED_LIFECYCLE_SLUGS).toContain("copilotkit-setup");
expect(RESERVED_LIFECYCLE_SLUGS).toContain("copilotkit-self-update");
expect(RESERVED_LIFECYCLE_SLUGS.size).toBe(8);
});
// Version sync — the plugin version tracks packages/runtime/package.json.
async function addVersionFixtures(
repoRoot: string,
runtimePkgVersion: string,
initialPluginVersion: string,
) {
await writeFile(
join(repoRoot, "packages/runtime/package.json"),
JSON.stringify(
{ name: "@copilotkit/runtime", version: runtimePkgVersion },
null,
2,
) + "\n",
);
await mkdir(join(repoRoot, ".claude-plugin"), { recursive: true });
await writeFile(
join(repoRoot, ".claude-plugin/plugin.json"),
JSON.stringify(
{ name: "copilotkit", version: initialPluginVersion },
null,
2,
) + "\n",
);
await writeFile(
join(repoRoot, ".claude-plugin/marketplace.json"),
JSON.stringify(
{
name: "copilotkit",
metadata: { version: initialPluginVersion },
plugins: [
{ name: "copilotkit", source: "./", version: initialPluginVersion },
],
},
null,
2,
) + "\n",
);
}
it("write mode copies runtime package.json version into plugin.json and marketplace.json", async () => {
await makeRepo(repo);
await addVersionFixtures(repo, "1.56.2", "0.0.0");
await syncPluginSkills({ cwd: repo, mode: "write" });
const plugin = JSON.parse(
await readFile(join(repo, ".claude-plugin/plugin.json"), "utf8"),
);
const market = JSON.parse(
await readFile(join(repo, ".claude-plugin/marketplace.json"), "utf8"),
);
expect(plugin.version).toBe("1.56.2");
expect(market.plugins[0].version).toBe("1.56.2");
// metadata.version tracks the runtime package too — both marketplace fields
// must move together so neither rots independently.
expect(market.metadata.version).toBe("1.56.2");
});
it("write mode re-syncs marketplace.json metadata.version when it lags plugins[0].version", async () => {
await makeRepo(repo);
await addVersionFixtures(repo, "1.56.2", "1.56.2");
// Simulate the historical drift: plugins[0] was bumped by an earlier sync
// but metadata.version was left behind because it was unmanaged.
const marketPath = join(repo, ".claude-plugin/marketplace.json");
const market = JSON.parse(await readFile(marketPath, "utf8"));
market.metadata.version = "1.55.0";
await writeFile(marketPath, JSON.stringify(market, null, 2) + "\n");
const drift = await syncPluginSkills({ cwd: repo, mode: "check" });
expect(drift.exitCode).toBe(1);
expect(drift.message).toMatch(/metadata\.version/i);
expect(drift.message).toContain("1.55.0");
await syncPluginSkills({ cwd: repo, mode: "write" });
const fixed = JSON.parse(await readFile(marketPath, "utf8"));
expect(fixed.metadata.version).toBe("1.56.2");
expect(fixed.plugins[0].version).toBe("1.56.2");
});
it("check mode detects plugin.json version drift", async () => {
await makeRepo(repo);
await addVersionFixtures(repo, "1.56.2", "0.0.0");
await syncPluginSkills({ cwd: repo, mode: "write" });
// Simulate a maintainer bumping the package but forgetting to run sync.
await writeFile(
join(repo, "packages/runtime/package.json"),
JSON.stringify(
{ name: "@copilotkit/runtime", version: "1.57.0" },
null,
2,
) + "\n",
);
const result = await syncPluginSkills({ cwd: repo, mode: "check" });
expect(result.exitCode).toBe(1);
expect(result.message).toMatch(/version.*drift/i);
expect(result.message).toContain("1.57.0");
});
});
@@ -0,0 +1,283 @@
import { describe, it, expect } from "vitest";
import * as fs from "node:fs";
import * as path from "node:path";
import * as os from "node:os";
import {
loadAllowlist,
stripProviderPrefix,
looksLikeModelName,
extractModelNames,
validateFiles,
} from "../validate-doc-model-names";
// ---------------------------------------------------------------------------
// stripProviderPrefix
// ---------------------------------------------------------------------------
describe("stripProviderPrefix", () => {
it("strips known provider prefixes", () => {
expect(stripProviderPrefix("openai/gpt-5.4")).toBe("gpt-5.4");
expect(stripProviderPrefix("anthropic/claude-sonnet-4")).toBe(
"claude-sonnet-4",
);
expect(stripProviderPrefix("google/gemini-2.5-pro")).toBe("gemini-2.5-pro");
expect(stripProviderPrefix("cohere/command-r-plus")).toBe("command-r-plus");
expect(stripProviderPrefix("meta/llama-4-scout")).toBe("llama-4-scout");
expect(stripProviderPrefix("mistral/mistral-large")).toBe("mistral-large");
expect(stripProviderPrefix("azure/gpt-4o")).toBe("gpt-4o");
expect(stripProviderPrefix("bedrock/claude-sonnet-4")).toBe(
"claude-sonnet-4",
);
expect(stripProviderPrefix("vertex/gemini-2.5-flash")).toBe(
"gemini-2.5-flash",
);
});
it("returns the name unchanged when no prefix matches", () => {
expect(stripProviderPrefix("gpt-5.4")).toBe("gpt-5.4");
expect(stripProviderPrefix("claude-sonnet-4")).toBe("claude-sonnet-4");
});
});
// ---------------------------------------------------------------------------
// looksLikeModelName
// ---------------------------------------------------------------------------
describe("looksLikeModelName", () => {
it("recognizes known model prefixes", () => {
expect(looksLikeModelName("gpt-5.4")).toBe(true);
expect(looksLikeModelName("claude-sonnet-4")).toBe(true);
expect(looksLikeModelName("gemini-2.5-pro")).toBe(true);
expect(looksLikeModelName("o1")).toBe(true);
expect(looksLikeModelName("o1-mini")).toBe(true);
expect(looksLikeModelName("o3-mini")).toBe(true);
expect(looksLikeModelName("o4-mini")).toBe(true);
expect(looksLikeModelName("command-r-plus")).toBe(true);
expect(looksLikeModelName("command-a")).toBe(true);
expect(looksLikeModelName("mistral-large")).toBe(true);
expect(looksLikeModelName("llama-4-scout")).toBe(true);
});
it("rejects non-model strings", () => {
expect(looksLikeModelName("react")).toBe(false);
expect(looksLikeModelName("next.js")).toBe(false);
expect(looksLikeModelName("typescript")).toBe(false);
expect(looksLikeModelName("")).toBe(false);
});
});
// ---------------------------------------------------------------------------
// extractModelNames
// ---------------------------------------------------------------------------
describe("extractModelNames", () => {
it('extracts model from model="..." in fenced code block', () => {
const content = [
"Some text",
"```python",
'ChatOpenAI(model="gpt-5.4-mini")',
"```",
].join("\n");
const results = extractModelNames(content);
expect(results).toEqual([{ model: "gpt-5.4-mini", line: 3 }]);
});
it('extracts model from model: "..." pattern', () => {
const content = [
"```tsx",
'const config = { model: "claude-sonnet-4" };',
"```",
].join("\n");
const results = extractModelNames(content);
expect(results).toEqual([{ model: "claude-sonnet-4", line: 2 }]);
});
it('extracts model from "model": "..." JSON pattern', () => {
const content = [
"```json",
"{",
' "model": "gemini-2.5-flash"',
"}",
"```",
].join("\n");
const results = extractModelNames(content);
expect(results).toEqual([{ model: "gemini-2.5-flash", line: 3 }]);
});
it("extracts model from single-quoted values", () => {
const content = ["```python", "model='gpt-4o'", "```"].join("\n");
const results = extractModelNames(content);
expect(results).toEqual([{ model: "gpt-4o", line: 2 }]);
});
it("extracts model from inline code", () => {
const content = 'Use `model="gpt-5.4"` for best results.';
const results = extractModelNames(content);
expect(results).toEqual([{ model: "gpt-5.4", line: 1 }]);
});
it("strips provider prefixes from model names", () => {
const content = ["```tsx", 'model="openai/gpt-5.4-mini"', "```"].join("\n");
const results = extractModelNames(content);
expect(results).toEqual([{ model: "gpt-5.4-mini", line: 2 }]);
});
it("handles bare provider-prefixed names", () => {
const content = ["```", "openai/gpt-4o-mini", "```"].join("\n");
const results = extractModelNames(content);
expect(results).toEqual([{ model: "gpt-4o-mini", line: 2 }]);
});
it("extracts multiple models from one file", () => {
const content = [
"```python",
'a = ChatOpenAI(model="gpt-5.4")',
'b = ChatAnthropic(model="claude-sonnet-4")',
"```",
].join("\n");
const results = extractModelNames(content);
expect(results).toHaveLength(2);
expect(results[0].model).toBe("gpt-5.4");
expect(results[1].model).toBe("claude-sonnet-4");
});
it("ignores text outside code blocks", () => {
const content = [
'We recommend model="gpt-5.4" for production.',
"",
"This is plain text, not code.",
].join("\n");
// No fenced block, no inline code — nothing extracted
const results = extractModelNames(content);
expect(results).toEqual([]);
});
it("ignores empty and whitespace-only strings", () => {
const content = ["```", 'model=""', "model=' '", "```"].join("\n");
const results = extractModelNames(content);
expect(results).toEqual([]);
});
it("does not extract non-model strings from code blocks", () => {
const content = [
"```tsx",
'const name = "react-component";',
'import something from "next/router";',
"```",
].join("\n");
const results = extractModelNames(content);
expect(results).toEqual([]);
});
});
// ---------------------------------------------------------------------------
// loadAllowlist
// ---------------------------------------------------------------------------
describe("loadAllowlist", () => {
it("loads all model names from allowlist JSON", () => {
const allowlistPath = path.resolve(
__dirname,
"../../showcase/shell-docs/model-allowlist.json",
);
const allowed = loadAllowlist(allowlistPath);
expect(allowed.has("gpt-5.4")).toBe(true);
expect(allowed.has("claude-sonnet-4")).toBe(true);
expect(allowed.has("gemini-2.5-pro")).toBe(true);
expect(allowed.has("command-r-plus")).toBe(true);
expect(allowed.has("llama-4-scout")).toBe(true);
});
it("excludes the _comment field", () => {
const allowlistPath = path.resolve(
__dirname,
"../../showcase/shell-docs/model-allowlist.json",
);
const allowed = loadAllowlist(allowlistPath);
// _comment value should not be in the set
expect(
allowed.has(
"Maintained list of valid AI model names for docs. Update when providers release new models. CI validates docs against this list.",
),
).toBe(false);
});
});
// ---------------------------------------------------------------------------
// validateFiles (integration)
// ---------------------------------------------------------------------------
describe("validateFiles", () => {
function createTempDir(): string {
return fs.mkdtempSync(path.join(os.tmpdir(), "model-validate-"));
}
it("returns no violations when all models are in the allowlist", () => {
const dir = createTempDir();
const allowlist = path.join(dir, "allowlist.json");
fs.writeFileSync(
allowlist,
JSON.stringify({ openai: ["gpt-5.4"], anthropic: ["claude-sonnet-4"] }),
);
fs.writeFileSync(
path.join(dir, "test.mdx"),
["```python", 'ChatOpenAI(model="gpt-5.4")', "```"].join("\n"),
);
const violations = validateFiles(dir, allowlist);
expect(violations).toEqual([]);
fs.rmSync(dir, { recursive: true });
});
it("flags model names not in the allowlist", () => {
const dir = createTempDir();
const allowlist = path.join(dir, "allowlist.json");
fs.writeFileSync(allowlist, JSON.stringify({ openai: ["gpt-5.4"] }));
fs.writeFileSync(
path.join(dir, "test.mdx"),
["```python", 'model="gpt-99"', "```"].join("\n"),
);
const violations = validateFiles(dir, allowlist);
expect(violations).toHaveLength(1);
expect(violations[0].model).toBe("gpt-99");
expect(violations[0].file).toBe("test.mdx");
fs.rmSync(dir, { recursive: true });
});
it("scans subdirectories for .mdx files", () => {
const dir = createTempDir();
const sub = path.join(dir, "guides");
fs.mkdirSync(sub);
const allowlist = path.join(dir, "allowlist.json");
fs.writeFileSync(allowlist, JSON.stringify({ openai: ["gpt-5.4"] }));
fs.writeFileSync(
path.join(sub, "deep.mdx"),
["```", 'model="gpt-unknown"', "```"].join("\n"),
);
const violations = validateFiles(dir, allowlist);
expect(violations).toHaveLength(1);
expect(violations[0].file).toBe("guides/deep.mdx");
fs.rmSync(dir, { recursive: true });
});
});
@@ -0,0 +1,201 @@
import { afterEach, describe, expect, it } from "vitest";
import * as fs from "node:fs";
import * as os from "node:os";
import * as path from "node:path";
import {
validatePins,
formatViolations,
} from "../validate-integration-pins.js";
function makePkg(deps: Record<string, string>): string {
return JSON.stringify({ name: "fixture", dependencies: deps });
}
function setupFixture(
integrations: Record<string, Record<string, string>>,
): string {
const root = fs.mkdtempSync(path.join(os.tmpdir(), "validate-pins-"));
for (const [name, deps] of Object.entries(integrations)) {
const dir = path.join(root, name);
fs.mkdirSync(dir);
fs.writeFileSync(path.join(dir, "package.json"), makePkg(deps));
}
return root;
}
describe("validatePins", () => {
let fixtureDir: string;
afterEach(() => {
if (fixtureDir) fs.rmSync(fixtureDir, { recursive: true, force: true });
});
const ENFORCED = new Set(["adk"]);
it("returns no violations when every enforced integration matches the expected version", () => {
fixtureDir = setupFixture({
adk: { "@copilotkit/react-core": "1.56.4" },
});
const violations = validatePins({
expectedVersion: "1.56.4",
integrationsDir: fixtureDir,
enforced: ENFORCED,
});
expect(violations).toEqual([]);
});
it("flags stale exact-pinned versions in enforced integrations", () => {
fixtureDir = setupFixture({
adk: { "@copilotkit/react-core": "1.55.2" },
});
const violations = validatePins({
expectedVersion: "1.56.4",
integrationsDir: fixtureDir,
enforced: ENFORCED,
});
expect(violations).toHaveLength(1);
expect(violations[0].reason).toBe("stale");
});
it("flags floating dist-tag pins like 'latest' and 'next'", () => {
fixtureDir = setupFixture({
adk: { "@copilotkit/react-core": "latest" },
});
const violations = validatePins({
expectedVersion: "1.56.4",
integrationsDir: fixtureDir,
enforced: ENFORCED,
});
expect(violations).toHaveLength(1);
expect(violations[0].reason).toBe("floating-tag");
});
it("ignores integrations not on the allowlist (the 14 others left for QA)", () => {
fixtureDir = setupFixture({
adk: { "@copilotkit/react-core": "1.56.4" },
mastra: { "@copilotkit/react-core": "1.55.2" },
"mcp-apps": { "@copilotkit/runtime": "1.52.1" },
"a2a-middleware": { "@copilotkit/react-core": "latest" },
});
const violations = validatePins({
expectedVersion: "1.56.4",
integrationsDir: fixtureDir,
enforced: ENFORCED,
});
expect(violations).toEqual([]);
});
it("ignores intentional pre-release pins (e.g. ag-ui pre-release tags)", () => {
fixtureDir = setupFixture({
adk: {
"@copilotkit/react-core": "0.0.0-mme-ag-ui-0-0-46-20260227141603",
},
});
const violations = validatePins({
expectedVersion: "1.56.4",
integrationsDir: fixtureDir,
enforced: ENFORCED,
});
expect(violations).toEqual([]);
});
it("ignores non-@copilotkit dependencies entirely", () => {
fixtureDir = setupFixture({
adk: {
"@copilotkit/react-core": "1.56.4",
next: "16.1.1",
"@ag-ui/client": "0.0.52",
},
});
const violations = validatePins({
expectedVersion: "1.56.4",
integrationsDir: fixtureDir,
enforced: ENFORCED,
});
expect(violations).toEqual([]);
});
it("checks devDependencies too", () => {
fixtureDir = fs.mkdtempSync(path.join(os.tmpdir(), "validate-pins-dev-"));
const dir = path.join(fixtureDir, "adk");
fs.mkdirSync(dir);
fs.writeFileSync(
path.join(dir, "package.json"),
JSON.stringify({
name: "adk",
devDependencies: { "@copilotkit/react-core": "1.55.2" },
}),
);
const violations = validatePins({
expectedVersion: "1.56.4",
integrationsDir: fixtureDir,
enforced: ENFORCED,
});
expect(violations).toHaveLength(1);
expect(violations[0].dep).toBe("@copilotkit/react-core");
});
it("skips integrations without a package.json", () => {
fixtureDir = fs.mkdtempSync(path.join(os.tmpdir(), "validate-pins-skip-"));
fs.mkdirSync(path.join(fixtureDir, "adk"));
const violations = validatePins({
expectedVersion: "1.56.4",
integrationsDir: fixtureDir,
enforced: ENFORCED,
});
expect(violations).toEqual([]);
});
});
describe("formatViolations", () => {
it("returns an empty string when there are no violations", () => {
expect(formatViolations([], "1.56.4")).toBe("");
});
it("formats violations with reason, dep, and expected version", () => {
const out = formatViolations(
[
{
integration: "adk",
dep: "@copilotkit/react-core",
pinned: "1.55.2",
reason: "stale",
},
{
integration: "a2a-middleware",
dep: "@copilotkit/runtime",
pinned: "latest",
reason: "floating-tag",
},
],
"1.56.4",
);
expect(out).toContain("Found 2 stale pin(s)");
expect(out).toContain("[stale] adk: @copilotkit/react-core@1.55.2");
expect(out).toContain(
"[floating-tag] a2a-middleware: @copilotkit/runtime@latest",
);
expect(out).toContain("expected 1.56.4");
});
});
describe("live tree", () => {
it("examples/integrations/* @copilotkit pins match the monorepo release version", () => {
const repoRoot = path.resolve(__dirname, "..", "..");
const reactCorePkg = JSON.parse(
fs.readFileSync(
path.join(repoRoot, "packages", "react-core", "package.json"),
"utf8",
),
);
const expectedVersion = reactCorePkg.version;
const violations = validatePins({
expectedVersion,
integrationsDir: path.join(repoRoot, "examples", "integrations"),
});
if (violations.length > 0) {
console.error(formatViolations(violations, expectedVersion));
}
expect(violations).toEqual([]);
});
});