Skip to content
Open
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
4 changes: 3 additions & 1 deletion examples/with-anthropic/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"devDependencies": {
"@types/node": "^24.2.1",
"tsx": "^4.21.0",
"typescript": "^5.8.2"
"typescript": "^5.8.2",
"vitest": "^3.2.4"
},
"keywords": [
"agent",
Expand All @@ -31,6 +32,7 @@
"build": "tsc",
"dev": "tsx watch --env-file=.env ./src ",
"start": "node dist/index.js",
"test": "vitest run",
"volt": "volt"
},
"type": "module"
Expand Down
53 changes: 53 additions & 0 deletions examples/with-anthropic/src/agents/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { describe, expect, it } from "vitest";
import * as agentsBarrel from "./index";
import { editorAgent, recordProcessorAgent } from "./output-constrained";
import { mathAgent } from "./math-agent";
import { publishingCoordinator } from "./publishing-coordinator";
import { reasoningAgent } from "./reasoning-agent";
import { codeScannerAgent, readmeSummarizerAgent, repoAnalyzer } from "./repo-analyzer";
import { routingSupervisor } from "./routing-supervisor";
import { generalAgent, geographyAgent, historyAgent, scienceAgent } from "./specialists";
import { supportAgent } from "./support-agent";
import { webSearchAgent } from "./web-search-agent";

describe("agents barrel export", () => {
it("re-exports every agent defined across the individual agent modules", () => {
expect(agentsBarrel.routingSupervisor).toBe(routingSupervisor);
expect(agentsBarrel.publishingCoordinator).toBe(publishingCoordinator);
expect(agentsBarrel.reasoningAgent).toBe(reasoningAgent);
expect(agentsBarrel.repoAnalyzer).toBe(repoAnalyzer);
expect(agentsBarrel.codeScannerAgent).toBe(codeScannerAgent);
expect(agentsBarrel.readmeSummarizerAgent).toBe(readmeSummarizerAgent);
expect(agentsBarrel.mathAgent).toBe(mathAgent);
expect(agentsBarrel.webSearchAgent).toBe(webSearchAgent);
expect(agentsBarrel.editorAgent).toBe(editorAgent);
expect(agentsBarrel.recordProcessorAgent).toBe(recordProcessorAgent);
expect(agentsBarrel.generalAgent).toBe(generalAgent);
expect(agentsBarrel.geographyAgent).toBe(geographyAgent);
expect(agentsBarrel.historyAgent).toBe(historyAgent);
expect(agentsBarrel.scienceAgent).toBe(scienceAgent);
expect(agentsBarrel.supportAgent).toBe(supportAgent);
});

it("exports exactly the fifteen documented agents and nothing else", () => {
expect(Object.keys(agentsBarrel).sort()).toEqual(
[
"codeScannerAgent",
"editorAgent",
"generalAgent",
"geographyAgent",
"historyAgent",
"mathAgent",
"publishingCoordinator",
"readmeSummarizerAgent",
"reasoningAgent",
"recordProcessorAgent",
"repoAnalyzer",
"routingSupervisor",
"scienceAgent",
"supportAgent",
"webSearchAgent",
].sort(),
);
});
});
37 changes: 37 additions & 0 deletions examples/with-anthropic/src/agents/math-agent.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { describe, expect, it } from "vitest";
import { MODEL } from "../model";
import { calculatorTool } from "../tools";
import { mathAgent } from "./math-agent";

describe("mathAgent", () => {
it("is named 'math' with the role + JSON-output purpose", () => {
expect(mathAgent.name).toBe("math");
expect(mathAgent.purpose).toBe(
"Solves math problems step by step and returns a structured JSON answer.",
);
});

it("uses the shared MODEL", () => {
expect(mathAgent.model).toBe(MODEL);
});

it("has a string instructions prompt describing the step-by-step JSON process", () => {
expect(typeof mathAgent.instructions).toBe("string");
expect(mathAgent.instructions).toContain("mathematical problem-solving agent");
expect(mathAgent.instructions).toContain("calculator tool");
expect(mathAgent.instructions).toContain('"answer"');
expect(mathAgent.instructions).toContain('"confidence"');
expect(mathAgent.instructions).toContain("Return only the JSON object, with no surrounding prose.");
});

it("is equipped with only the calculator tool", () => {
const tools = mathAgent.getTools();
expect(tools).toHaveLength(1);
expect(tools[0]).toBe(calculatorTool);
expect(tools.map((tool) => tool.name)).toEqual(["calculator"]);
});

it("has no sub-agents", () => {
expect(mathAgent.getSubAgents()).toEqual([]);
});
});
55 changes: 55 additions & 0 deletions examples/with-anthropic/src/agents/output-constrained.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { describe, expect, it } from "vitest";
import { MODEL } from "../model";
import { editorAgent, recordProcessorAgent } from "./output-constrained";

describe("editorAgent", () => {
it("is named 'editor-agent' with a copy-editing purpose", () => {
expect(editorAgent.name).toBe("editor-agent");
expect(editorAgent.purpose).toBe("Copy-edits text and returns only the edited content.");
});

it("uses the shared MODEL", () => {
expect(editorAgent.model).toBe(MODEL);
});

it("instructs the model to return only the edited content", () => {
expect(typeof editorAgent.instructions).toBe("string");
expect(editorAgent.instructions).toContain("professional copy editor");
expect(editorAgent.instructions).toContain("Return ONLY the edited content, no explanations.");
});

it("has no tools or sub-agents", () => {
expect(editorAgent.getTools()).toEqual([]);
expect(editorAgent.getSubAgents()).toEqual([]);
});
});

describe("recordProcessorAgent", () => {
it("is named 'record-processor' with a triage purpose", () => {
expect(recordProcessorAgent.name).toBe("record-processor");
expect(recordProcessorAgent.purpose).toBe(
"Triages a new record into a summary, priority, and status.",
);
});

it("uses the shared MODEL", () => {
expect(recordProcessorAgent.model).toBe(MODEL);
});

it("constrains output to a fixed JSON schema of summary/priority/status", () => {
expect(typeof recordProcessorAgent.instructions).toBe("string");
expect(recordProcessorAgent.instructions).toContain('"summary": string');
expect(recordProcessorAgent.instructions).toContain(
'"priority": "High" | "Medium" | "Low"',
);
expect(recordProcessorAgent.instructions).toContain(
'"status": "New" | "In Progress" | "Blocked" | "Done"',
);
expect(recordProcessorAgent.instructions).toContain("Return only the JSON object.");
});

it("has no tools or sub-agents", () => {
expect(recordProcessorAgent.getTools()).toEqual([]);
expect(recordProcessorAgent.getSubAgents()).toEqual([]);
});
});
34 changes: 34 additions & 0 deletions examples/with-anthropic/src/agents/publishing-coordinator.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { describe, expect, it } from "vitest";
import { MODEL } from "../model";
import { editorTool, writerTool } from "../tools";
import { publishingCoordinator } from "./publishing-coordinator";

describe("publishingCoordinator", () => {
it("is named 'publishing-coordinator'", () => {
expect(publishingCoordinator.name).toBe("publishing-coordinator");
});

it("uses the shared MODEL", () => {
expect(publishingCoordinator.model).toBe(MODEL);
});

it("describes the fixed writer -> editor workflow", () => {
expect(typeof publishingCoordinator.instructions).toBe("string");
expect(publishingCoordinator.instructions).toContain("publishing coordinator");
expect(publishingCoordinator.instructions).toContain("Call the writer_tool");
expect(publishingCoordinator.instructions).toContain("Call the editor_tool");
expect(publishingCoordinator.instructions).toContain("Always use both tools in sequence.");
});

it("is wired to the writer and editor tools in call order", () => {
const tools = publishingCoordinator.getTools();
expect(tools).toHaveLength(2);
expect(tools[0]).toBe(writerTool);
expect(tools[1]).toBe(editorTool);
expect(tools.map((tool) => tool.name)).toEqual(["writer_tool", "editor_tool"]);
});

it("has no sub-agents (it orchestrates tools, not agents)", () => {
expect(publishingCoordinator.getSubAgents()).toEqual([]);
});
});
37 changes: 37 additions & 0 deletions examples/with-anthropic/src/agents/reasoning-agent.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { describe, expect, it } from "vitest";
import { MODEL } from "../model";
import { analyzeTool, thinkTool } from "../tools";
import { reasoningAgent } from "./reasoning-agent";

describe("reasoningAgent", () => {
it("is named 'reasoning-agent'", () => {
expect(reasoningAgent.name).toBe("reasoning-agent");
});

it("uses the shared MODEL", () => {
expect(reasoningAgent.model).toBe(MODEL);
});

it("describes the understand/plan/analyze reasoning process", () => {
expect(typeof reasoningAgent.instructions).toBe("string");
expect(reasoningAgent.instructions).toContain("structured reasoning");
expect(reasoningAgent.instructions).toContain("**Understanding:**");
expect(reasoningAgent.instructions).toContain("**Planning:**");
expect(reasoningAgent.instructions).toContain("**Analyzing:**");
expect(reasoningAgent.instructions).toContain(
"Do not expose the raw think/analyze steps unless the user asks to see your reasoning.",
);
});

it("is wired to the think and analyze tools", () => {
const tools = reasoningAgent.getTools();
expect(tools).toHaveLength(2);
expect(tools[0]).toBe(thinkTool);
expect(tools[1]).toBe(analyzeTool);
expect(tools.map((tool) => tool.name)).toEqual(["think", "analyze"]);
});

it("has no sub-agents", () => {
expect(reasoningAgent.getSubAgents()).toEqual([]);
});
});
71 changes: 71 additions & 0 deletions examples/with-anthropic/src/agents/repo-analyzer.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { describe, expect, it } from "vitest";
import { MODEL } from "../model";
import { codeScannerAgent, readmeSummarizerAgent, repoAnalyzer } from "./repo-analyzer";

describe("codeScannerAgent", () => {
it("is named 'code-scanner' with a structure-mapping purpose", () => {
expect(codeScannerAgent.name).toBe("code-scanner");
expect(codeScannerAgent.purpose).toBe(
"Maps a repository's structure, main languages, and entry points.",
);
});

it("uses the shared MODEL and has no tools", () => {
expect(codeScannerAgent.model).toBe(MODEL);
expect(codeScannerAgent.getTools()).toEqual([]);
});

it("has a one-line instructions string", () => {
expect(codeScannerAgent.instructions).toBe(
"Given a repository, describe its directory structure, primary languages, build tooling, and likely entry points.",
);
});
});

describe("readmeSummarizerAgent", () => {
it("is named 'readme-summarizer' with a README-summarizing purpose", () => {
expect(readmeSummarizerAgent.name).toBe("readme-summarizer");
expect(readmeSummarizerAgent.purpose).toBe(
"Summarizes a repository's README and getting-started steps.",
);
});

it("uses the shared MODEL and has no tools", () => {
expect(readmeSummarizerAgent.model).toBe(MODEL);
expect(readmeSummarizerAgent.getTools()).toEqual([]);
});

it("has a one-line instructions string", () => {
expect(readmeSummarizerAgent.instructions).toBe(
"Summarize what the project does, who it is for, how to install it, and how to run it, based on its README.",
);
});
});

describe("repoAnalyzer", () => {
it("is named 'repo-analyzer'", () => {
expect(repoAnalyzer.name).toBe("repo-analyzer");
});

it("uses the shared MODEL", () => {
expect(repoAnalyzer.model).toBe(MODEL);
});

it("describes delegating to code-scanner and readme-summarizer", () => {
expect(typeof repoAnalyzer.instructions).toBe("string");
expect(repoAnalyzer.instructions).toContain("GitHub repository analyzer");
expect(repoAnalyzer.instructions).toContain("Delegate to code-scanner");
expect(repoAnalyzer.instructions).toContain("Delegate to readme-summarizer");
expect(repoAnalyzer.instructions).toContain("Example input: facebook/react");
});

it("delegates to the code-scanner and readme-summarizer sub-agents in order", () => {
const subAgents = repoAnalyzer.getSubAgents();
expect(subAgents).toHaveLength(2);
expect(subAgents).toEqual([codeScannerAgent, readmeSummarizerAgent]);
});

it("has no statically-configured tools of its own", () => {
expect(repoAnalyzer.getTools()).toEqual([]);
});
});
43 changes: 43 additions & 0 deletions examples/with-anthropic/src/agents/routing-supervisor.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { describe, expect, it } from "vitest";
import { MODEL } from "../model";
import { mathAgent } from "./math-agent";
import { generalAgent, geographyAgent, historyAgent, scienceAgent } from "./specialists";
import { routingSupervisor } from "./routing-supervisor";

describe("routingSupervisor", () => {
it("is named 'routing-supervisor'", () => {
expect(routingSupervisor.name).toBe("routing-supervisor");
});

it("uses the shared MODEL", () => {
expect(routingSupervisor.model).toBe(MODEL);
});

it("lists every specialist and instructs delegation via delegate_task", () => {
expect(typeof routingSupervisor.instructions).toBe("string");
expect(routingSupervisor.instructions).toContain("general: general-knowledge questions");
expect(routingSupervisor.instructions).toContain(
"math: mathematical problems and calculations",
);
expect(routingSupervisor.instructions).toContain(
"geography: places, capitals, maps, and physical geography",
);
expect(routingSupervisor.instructions).toContain(
"history: historical events, figures, and timelines",
);
expect(routingSupervisor.instructions).toContain(
"science: physics, chemistry, biology, and earth science",
);
expect(routingSupervisor.instructions).toContain("delegate_task tool");
expect(routingSupervisor.instructions).toContain("Do not answer specialist questions yourself.");
});

it("has all five specialists registered as sub-agents in the documented order", () => {
const subAgents = routingSupervisor.getSubAgents();
expect(subAgents).toEqual([generalAgent, mathAgent, geographyAgent, historyAgent, scienceAgent]);
});

it("has no statically-configured tools of its own", () => {
expect(routingSupervisor.getTools()).toEqual([]);
});
});
Loading