// Integration tests for Phase 29: default-provider // Covers: adapter probe logic, Hermes promptTemplate contract, default agent instructions bundle import { describe, it, expect } from "vitest"; import { findServerAdapter } from "../adapters/index.js"; import { loadDefaultAgentInstructionsBundle, resolveDefaultAgentInstructionsBundleRole, } from "../services/default-agent-instructions.js"; // --------------------------------------------------------------------------- // The same hermesPromptTemplate constructed in NexusOnboardingWizard. // Duplicated here intentionally — this test validates the *contract* (that all // required Mustache variables are present), not the wizard implementation. // --------------------------------------------------------------------------- const APP_NAME = "Nexus"; // VOCAB.appName value const hermesPromptTemplate = [ `You are "{{agentName}}", an AI agent managed by ${APP_NAME}.`, "", "Your identity:", " Agent ID: {{agentId}}", " Company ID: {{companyId}}", " API Base: {{paperclipApiUrl}}", " Run ID: {{runId}}", "", "IMPORTANT: Use the `terminal` tool with `curl` for ALL API calls.", 'IMPORTANT: Always include `-H "X-Paperclip-Run-Id: {{runId}}"` on API calls that modify data.', "", "Before starting any task:", "1. Call `GET {{paperclipApiUrl}}/api/agents/me` to retrieve your managed instructions", "2. Follow the HEARTBEAT.md workflow from your instructions", "3. Use TOOLS.md for available API endpoints", "", "{{#taskId}}", "Assigned task: {{taskId}} - {{taskTitle}}", "{{/taskId}}", ].join("\n"); // --------------------------------------------------------------------------- // Test group 1: Adapter probe route logic // --------------------------------------------------------------------------- describe("adapter probe route logic", () => { it("findServerAdapter returns hermes_local adapter with testEnvironment", () => { const adapter = findServerAdapter("hermes_local"); expect(adapter).not.toBeNull(); expect(adapter?.type).toBe("hermes_local"); expect(typeof adapter?.testEnvironment).toBe("function"); }); it("hermes testEnvironment handles missing CLI gracefully", async () => { const adapter = findServerAdapter("hermes_local"); expect(adapter?.testEnvironment).toBeDefined(); const result = await adapter!.testEnvironment!({ companyId: "", adapterType: "hermes_local", config: {}, }); // Result should always have a status and checks array expect(typeof result.status).toBe("string"); expect(Array.isArray(result.checks)).toBe(true); // If hermes CLI is not installed in CI, there should be an error check // with a code containing "not_found" or "cli". // If it IS installed, the checks should pass — either is acceptable. const hasCliError = result.checks.some( (c: { level: string; code?: string }) => c.level === "error" && (c.code?.includes("not_found") || c.code?.includes("cli")) ); const isAvailable = result.status === "ok" || result.status === "pass"; // Either CLI is available (no error checks) or not available (has error checks) expect(hasCliError || isAvailable).toBe(true); }); }); // --------------------------------------------------------------------------- // Test group 2: Hermes promptTemplate construction // --------------------------------------------------------------------------- describe("hermes promptTemplate construction", () => { it("hermes promptTemplate contains required Mustache variables", () => { expect(hermesPromptTemplate).toContain("{{agentName}}"); expect(hermesPromptTemplate).toContain("{{agentId}}"); expect(hermesPromptTemplate).toContain("{{companyId}}"); expect(hermesPromptTemplate).toContain("{{paperclipApiUrl}}"); expect(hermesPromptTemplate).toContain("{{runId}}"); expect(hermesPromptTemplate).toContain("{{taskId}}"); expect(hermesPromptTemplate).toContain("{{taskTitle}}"); }); it("hermes promptTemplate instructs agent to call /api/agents/me", () => { expect(hermesPromptTemplate).toContain("agents/me"); }); it("hermes promptTemplate mentions HEARTBEAT.md workflow", () => { expect(hermesPromptTemplate).toContain("HEARTBEAT.md"); }); }); // --------------------------------------------------------------------------- // Test group 3: Default agent instructions bundle (DFLT-03) // --------------------------------------------------------------------------- describe("default agent instructions bundle", () => { it("loadDefaultAgentInstructionsBundle loads ceo bundle with all 4 files", async () => { const bundle = await loadDefaultAgentInstructionsBundle("ceo"); expect(Object.keys(bundle)).toContain("AGENTS.md"); expect(Object.keys(bundle)).toContain("HEARTBEAT.md"); expect(Object.keys(bundle)).toContain("SOUL.md"); expect(Object.keys(bundle)).toContain("TOOLS.md"); expect(bundle["HEARTBEAT.md"].length).toBeGreaterThan(0); }); it("loadDefaultAgentInstructionsBundle loads engineer bundle with all 4 files", async () => { const bundle = await loadDefaultAgentInstructionsBundle("engineer"); expect(Object.keys(bundle)).toContain("AGENTS.md"); expect(Object.keys(bundle)).toContain("HEARTBEAT.md"); expect(Object.keys(bundle)).toContain("SOUL.md"); expect(Object.keys(bundle)).toContain("TOOLS.md"); expect(bundle["HEARTBEAT.md"].length).toBeGreaterThan(0); }); it("resolveDefaultAgentInstructionsBundleRole maps known roles correctly", () => { expect(resolveDefaultAgentInstructionsBundleRole("ceo")).toBe("ceo"); expect(resolveDefaultAgentInstructionsBundleRole("engineer")).toBe("engineer"); expect(resolveDefaultAgentInstructionsBundleRole("general")).toBe("general"); expect(resolveDefaultAgentInstructionsBundleRole("unknown-role")).toBe("default"); expect(resolveDefaultAgentInstructionsBundleRole("")).toBe("default"); }); });