import * as fs from "node:fs"; import * as path from "node:path"; import * as os from "node:os"; import { AgentContext, AgentResult } from "./base.js"; import { PlannerAgent } from "./planner.js"; import { ExecutorAgent } from "./executor.js"; import { VerifierAgent } from "./verifier.js"; import { ResearcherAgent } from "./researcher.js"; import { ChallengerAgent } from "./challenger.js"; import { SecurityAuditorAgent } from "./security-auditor.js"; import { DebuggerAgent } from "./debugger.js"; import { DocWriterAgent } from "./doc-writer.js"; import { DocVerifierAgent } from "./doc-verifier.js"; import { CodeReviewerAgent } from "./code-reviewer.js"; import { IdeationAgent } from "./ideation-agent.js"; import { RoadmapperAgent } from "./roadmapper.js"; import { PlanCheckerAgent } from "./plan-checker.js"; import { ProjectResearcherAgent } from "./project-researcher.js"; import { ResearchSynthesizerAgent } from "./research-synthesizer.js"; import { SolutionWriterAgent } from "./solution-writer.js"; import { PhaseResearcherAgent } from "./phase-researcher.js"; import { TesterAgent } from "./tester.js"; const NON_ORCHESTRATOR_AGENTS: Array<{ name: string; factory: () => { execute(ctx: AgentContext): Promise; name: string } }> = [ { name: "planner", factory: () => new PlannerAgent() }, { name: "executor", factory: () => new ExecutorAgent() }, { name: "verifier", factory: () => new VerifierAgent() }, { name: "researcher", factory: () => new ResearcherAgent() }, { name: "challenger", factory: () => new ChallengerAgent() }, { name: "security-auditor", factory: () => new SecurityAuditorAgent() }, { name: "debugger", factory: () => new DebuggerAgent() }, { name: "doc-writer", factory: () => new DocWriterAgent() }, { name: "doc-verifier", factory: () => new DocVerifierAgent() }, { name: "code-reviewer", factory: () => new CodeReviewerAgent() }, { name: "ideation-agent", factory: () => new IdeationAgent() }, { name: "roadmapper", factory: () => new RoadmapperAgent() }, { name: "plan-checker", factory: () => new PlanCheckerAgent() }, { name: "project-researcher", factory: () => new ProjectResearcherAgent() }, { name: "research-synthesizer", factory: () => new ResearchSynthesizerAgent() }, { name: "solution-writer", factory: () => new SolutionWriterAgent() }, { name: "phase-researcher", factory: () => new PhaseResearcherAgent() }, { name: "tester", factory: () => new TesterAgent() }, ]; describe("All agents have intrinsic mechanical logic", () => { let tempDir: string; beforeEach(() => { tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "ciagent-mechanical-test-")); fs.mkdirSync(path.join(tempDir, ".ciagent"), { recursive: true }); fs.mkdirSync(path.join(tempDir, "src"), { recursive: true }); fs.writeFileSync( path.join(tempDir, ".ciagent", "config.json"), JSON.stringify({ autonomy: { level: "full", escalation_hooks: [], clarify_budget: 10, decision_confidence_threshold: 0.6, max_revision_iterations: 3, max_verification_retries: 2, escalation_timeout_ms: 300000 }, model_profile: "quality", parallelization: { enabled: false, max_concurrent_agents: 5, min_plans_for_parallel: 2 }, verification: { automated_only: true, escalate_visual: true, escalate_external_integration: true, test_first: false }, security: { auto_accept_low_severity: true, auto_mitigate_medium_severity: true, escalate_high_severity: true }, git: { branching_strategy: "phase", auto_commit: false, auto_push: false }, backend: { provider: "auto", agent_backends: { opencode: { enabled: false } }, llm_backends: {} }, }, null, 2) ); fs.writeFileSync( path.join(tempDir, ".ciagent", "PROJECT.md"), "# Project: Mechanical Test\n\n## Core Value\nValidate mechanical agent logic\n\n## Requirements\n### Active\n- REQ-01: Agent runs mechanically\n\n## Key Decisions\n\n## Constraints\n- Test only" ); fs.writeFileSync( path.join(tempDir, ".ciagent", "REQUIREMENTS.md"), "# Requirements\n\n## V1\n### Functional\n| ID | Description | Priority |\n|------|------|------|\n| REQ-01 | Agent test | high |\n\n## Traceability\n| Requirement | Phase | Status |\n|------|------|------|\n| REQ-01 | 1 | in_progress |" ); fs.writeFileSync( path.join(tempDir, ".ciagent", "ROADMAP.md"), "# Roadmap\n\n## Phases\n\n| # | Name | Description | Requirements | Depends On | Status |\n|------|------|------|------|------|------|\n| 1 | Test | Agent test phase | REQ-01 | | in_progress |" ); fs.writeFileSync( path.join(tempDir, ".ciagent", "ARCHITECTURE.md"), "# Architecture\n\n## Overview\nTest architecture\n\n## Components\n| Name | Description | Boundaries | Depends On |\n|------|------|------|------|\n| core | Core | src/core/ | | \n\n## Build Order\n1. Build core\n\n## Data Flow\nTest flow" ); fs.writeFileSync( path.join(tempDir, "package.json"), JSON.stringify({ name: "mech-test", version: "0.1.0", scripts: { test: "echo ok" } }) ); fs.writeFileSync(path.join(tempDir, "tsconfig.json"), "{}"); fs.writeFileSync(path.join(tempDir, "src", "app.ts"), "export function main() { return 1; }"); }); afterEach(() => { try { fs.rmSync(tempDir, { recursive: true, force: true }); } catch {} }); it("every non-orchestrator agent produces meaningful output without backend", async () => { const context: AgentContext = { project_path: tempDir, phase: 1, stage: "plan", specification: "Test mechanical agent logic execution", config_path: path.join(tempDir, ".ciagent", "config.json"), }; expect(NON_ORCHESTRATOR_AGENTS.length).toBe(18); const results: Record = {}; for (const { name, factory } of NON_ORCHESTRATOR_AGENTS) { const agent = factory(); expect(agent.name).toBe(name); let result: AgentResult; try { result = await agent.execute(context); } catch (err) { result = { success: false, output: "", artifacts_created: [], decisions: 0, escalations: 0, duration_ms: 0, error: err instanceof Error ? err.message : String(err), }; } const errorText = (result.error || "").toLowerCase(); const hasStubError = errorText.includes("requires an intelligence backend") || errorText.includes("no intelligence backend available"); results[name] = { success: result.success, error: result.error, hasStubError, }; } const agentsWithStubErrors = Object.entries(results) .filter(([, r]) => r.hasStubError) .map(([name]) => name); expect(agentsWithStubErrors).toEqual([]); }); });