Menu
popagent
publicLatest change cfe185c25c9dd469690ca202ee3a02629e6925be - fix: align agent capabilities by AkurAI Build
import { describe, expect, test } from "bun:test";
import { AGENT_CONTEXT_KEYS, agentContextValue, createAgentRequestContext } from "./agent-context";
import type { RequestContext } from "@mastra/core/request-context";
import { createWorkspaceTools } from "@mastra/core/workspace";
import { agent } from "./agent";
import { resolveModel } from "./models";
import { agentSettings } from "./agent-settings";
import { agentRuntimeSettings } from "./agent-runtime-settings";
import { agentSkills } from "./agent-skills";
import { TaskAgentCommunicationSession } from "./task-agent-communication";
describe("agent", () => {
test("uses the configured orchestrator model", async () => {
const runtime = await agentRuntimeSettings.get();
const model = await agent.getModel();
expect(model.modelId).toBe(runtime.defaultModel);
});
test("exposes native memory and destination-bound secret tools", async () => {
const names = Object.keys(await agent.listTools());
expect(names).toEqual(expect.arrayContaining([
"retainMemory",
"recallMemory",
"reflectMemory",
"editMemory",
"useBrowserSecret",
"bifrostNavigator",
"repoBrief",
]));
expect(names).not.toEqual(expect.arrayContaining([
"remember",
"storeSecret",
"recallSecret",
"updateSecret",
]));
});
test("binds task communication to the supervisor and delegated participant identities", async () => {
const communicationSession = new TaskAgentCommunicationSession({
taskId: "task-communication",
workspaceId: "default",
resourceId: "popagent-user",
participants: ["orchistrator", "researcher"],
});
const requestContext = createAgentRequestContext({
resourceId: "popagent-user",
sessionId: "task:communication",
turnId: "turn-communication",
model: (await agentRuntimeSettings.get()).defaultModel,
workspaceId: "default",
executionSource: "task",
taskId: "task-communication",
communicationSession,
});
const supervisorTools = await agent.listTools({
requestContext: requestContext as unknown as RequestContext,
});
const delegated = await agent.listAgents({
requestContext: requestContext as unknown as RequestContext,
});
const researcher = delegated.researcher! as unknown as typeof agent;
const researcherTools = await researcher.listTools({
requestContext: requestContext as unknown as RequestContext,
});
expect(Object.keys(supervisorTools)).toContain("agentCommunication");
expect(Object.keys(researcherTools)).toContain("agentCommunication");
await communicationSession.send({
from: "orchistrator",
to: "researcher",
body: "Inspect the shared boundary.",
});
const instructions = await researcher.getInstructions({
requestContext: requestContext as unknown as RequestContext,
});
expect(instructions).toContain("Task coordination");
expect(instructions).toContain("Inspect the shared boundary.");
expect(communicationSession.inbox("researcher")).toEqual([]);
});
test("removes memory and credential tools for trusted self-update execution", async () => {
const requestContext = createAgentRequestContext({
resourceId: "popagent-user",
sessionId: "task:self-update",
turnId: "turn-self-update",
model: (await agentRuntimeSettings.get()).defaultModel,
workspaceId: "default",
executionSource: "self-update",
taskId: "self-update-task",
selfUpdateWorkspacePath: `${process.env.POPAGENT_DATA_DIR ?? `${process.cwd()}/data`}/self-update/test`,
});
const names = Object.keys(await agent.listTools({ requestContext: requestContext as unknown as RequestContext }));
expect(names).not.toContain("retainMemory");
expect(names).not.toContain("editMemory");
expect(names).not.toContain("useBrowserSecret");
expect(names).not.toContain("bifrostNavigator");
expect(names).toContain("recallMemory");
expect(names).toContain("reflectMemory");
});
test("contains Build maintenance delegation and excludes release promotion", async () => {
const requestContext = createAgentRequestContext({
resourceId: "popagent-user",
sessionId: "task:build-maintenance",
turnId: "turn-build-maintenance",
model: (await agentRuntimeSettings.get()).defaultModel,
workspaceId: "default",
executionSource: "build-maintenance",
taskId: "build-maintenance-task",
selfUpdateWorkspacePath: `${process.env.POPAGENT_DATA_DIR ?? `${process.cwd()}/data`}/build-maintenance/test`,
});
const delegated = await agent.listAgents({ requestContext: requestContext as unknown as RequestContext });
expect(Object.keys(delegated)).toEqual(["reviewer", "build-maintainer"]);
const buildMaintainer = delegated["build-maintainer"]! as unknown as typeof agent;
expect(Object.keys(await buildMaintainer.listTools())).toContain("akuraiRunQueue");
expect(Object.keys(await buildMaintainer.listTools())).not.toContain("akuraiRunPromote");
});
test("constrains specialist workspace capabilities by role", async () => {
const delegated = await agent.listAgents();
expect(Object.keys(delegated)).toEqual([
"researcher", "implementer", "reviewer",
"build-maintainer", "build-release-manager", "community-steward",
]);
const specialists = {
researcher: delegated.researcher! as unknown as typeof agent,
implementer: delegated.implementer! as unknown as typeof agent,
reviewer: delegated.reviewer! as unknown as typeof agent,
buildMaintainer: delegated["build-maintainer"]! as unknown as typeof agent,
buildReleaseManager: delegated["build-release-manager"]! as unknown as typeof agent,
communitySteward: delegated["community-steward"]! as unknown as typeof agent,
};
expect(await specialists.buildMaintainer.getWorkspace()).toBeDefined();
expect(await specialists.buildReleaseManager.getWorkspace()).toBeUndefined();
expect(await specialists.communitySteward.getWorkspace()).toBeUndefined();
expect(Object.keys(await specialists.buildMaintainer.listTools())).toContain("akuraiRunQueue");
expect(Object.keys(await specialists.buildMaintainer.listTools())).not.toContain("akuraiRunPromote");
expect(Object.keys(await specialists.buildReleaseManager.listTools())).toContain("akuraiRunPromote");
expect(Object.keys(await specialists.communitySteward.listTools())).not.toContain("akuraiRunQueue");
const supervisorWorkspace = await agent.getWorkspace();
expect(supervisorWorkspace?.lsp).toBeDefined();
expect(await specialists.implementer.getWorkspace()).toBe(supervisorWorkspace);
for (const id of ["researcher", "reviewer"] as const) {
const workspace = await specialists[id].getWorkspace();
expect(workspace).not.toBe(supervisorWorkspace);
expect(workspace?.lsp).toBeDefined();
const tools = Object.keys(await createWorkspaceTools(workspace!));
expect(tools).toEqual(expect.arrayContaining([
"mastra_workspace_read_file",
"mastra_workspace_list_files",
"mastra_workspace_grep",
"mastra_workspace_lsp_inspect",
]));
expect(tools).not.toEqual(expect.arrayContaining([
"mastra_workspace_write_file",
"mastra_workspace_edit_file",
"mastra_workspace_delete",
"mastra_workspace_execute_command",
]));
}
const implementerWorkspace = await specialists.implementer.getWorkspace();
const implementerTools = Object.keys(await createWorkspaceTools(implementerWorkspace!));
expect(implementerTools).toEqual(expect.arrayContaining([
"mastra_workspace_write_file",
"mastra_workspace_edit_file",
"mastra_workspace_lsp_inspect",
"mastra_workspace_execute_command",
]));
const runtime = await agentRuntimeSettings.get();
for (const specialist of Object.values(specialists)) {
expect((await specialist.getDefaultOptions()).maxSteps).toBe(runtime.specialistMaxSteps);
expect((await specialist.getDefaultOptions()).toolCallConcurrency)
.toEqual({ limit: runtime.toolConcurrency, strategy: "called" });
}
});
test("withholds researcher when deterministic evidence is implementation-ready", async () => {
const requestContext = createAgentRequestContext({
resourceId: "popagent-user",
sessionId: "brief-session",
turnId: "brief-turn",
model: (await agentRuntimeSettings.get()).defaultModel,
repositoryBriefReady: true,
});
const delegated = await agent.listAgents({ requestContext: requestContext as unknown as RequestContext });
expect(delegated.researcher).toBeUndefined();
expect(delegated.implementer).toBeDefined();
expect(delegated.reviewer).toBeDefined();
});
test("persists delegated messages with isolated thread identity", async () => {
const requestContext = createAgentRequestContext({
resourceId: "popagent-user",
sessionId: "delegation-persistence",
turnId: "delegation-persistence-turn",
model: "cx/gpt-5.3-codex-spark",
});
const tools = await (agent as unknown as { listAgentTools: (input: Record<string, unknown>) => Promise<Record<string, { execute?: (input: unknown, context: unknown) => Promise<{ text: string }> }>> }).listAgentTools({
requestContext: requestContext as RequestContext,
methodType: "stream",
threadId: "delegation-persistence",
resourceId: "popagent-user",
});
const researcher = tools["agent-researcher"];
expect(researcher).toBeDefined();
const result = await researcher!.execute!({ prompt: "Reply with exactly: delegated" }, {
abortSignal: AbortSignal.timeout(30_000),
messages: [],
toolCallId: "delegation-persistence-tool",
writer: { write: async () => {} },
} as never);
expect(result.text).toContain("delegated");
});
test("bounds supervisor tool loops from persisted runtime settings", async () => {
const runtime = await agentRuntimeSettings.get();
expect((await agent.getDefaultOptions()).maxSteps).toBe(runtime.supervisorMaxSteps);
expect((await agent.getDefaultOptions()).toolCallConcurrency)
.toEqual({ limit: runtime.toolConcurrency, strategy: "called" });
});
test("routes role-specific models into specialist runs", async () => {
const specialists = await agent.listAgents();
const requestContext = createAgentRequestContext({
resourceId: "popagent-user",
sessionId: "session-1",
turnId: "turn-1",
model: "cx/gpt-5.6-sol",
});
expect(agentContextValue(requestContext, AGENT_CONTEXT_KEYS.model)).toBe("cx/gpt-5.6-sol");
expect((await agent.getModel({ requestContext: requestContext as RequestContext })).modelId)
.toBe("cx/gpt-5.6-sol");
expect((await (specialists.researcher as unknown as typeof agent).getModel({
requestContext: requestContext as RequestContext,
})).modelId).toBe("codex/gpt-5.6-luna-medium");
expect((await (specialists.implementer as unknown as typeof agent).getModel({
requestContext: requestContext as RequestContext,
})).modelId).toBe("cx/gpt-5.6-sol");
expect((await (specialists.reviewer as unknown as typeof agent).getModel({
requestContext: requestContext as RequestContext,
})).modelId).toBe("codex/gpt-5.6-luna-medium");
});
test("applies persisted specialist capabilities on the next resolution", async () => {
const implementer = (await agent.listAgents()).implementer! as unknown as typeof agent;
const original = await agentSettings.get("implementer");
if (!original) throw new Error("implementer settings missing");
try {
await agentSettings.update("implementer", {
...original,
workspaceAccess: "none",
browserAccess: "none",
delegationEnabled: false,
tools: ["getTime", "repoBrief", "searchDocumentation", "bifrostNavigator", "useBrowserSecret"],
});
expect(Object.keys(await implementer.listTools())).toContain("getTime");
expect(Object.keys(await implementer.listTools())).not.toContain("webSearch");
expect(Object.keys(await implementer.listTools())).not.toContain("repoBrief");
expect(Object.keys(await implementer.listTools())).not.toContain("searchDocumentation");
expect(Object.keys(await implementer.listTools())).not.toContain("bifrostNavigator");
expect(Object.keys(await implementer.listTools())).not.toContain("useBrowserSecret");
expect(await implementer.getWorkspace()).toBeUndefined();
expect((await agent.listAgents()).implementer).toBeUndefined();
} finally {
await agentSettings.update("implementer", original);
await agent.listAgents();
}
});
test("loads specialist instructions entirely from the database", async () => {
const researcher = (await agent.listAgents()).researcher!;
const original = await agentSettings.get("researcher");
if (!original) throw new Error("researcher settings missing");
const custom = `# Role: Database Researcher\n\nPrefer primary sources ${crypto.randomUUID()}`;
try {
await agentSettings.update("researcher", { ...original, instructions: custom });
expect(await researcher.getInstructions()).toBe(
`Display identity: ${original.name}.\n\n${custom}`,
);
} finally {
await agentSettings.update("researcher", original);
}
});
test("applies persisted specialist display identity and delegation description at runtime", async () => {
const researcher = (await agent.listAgents()).researcher!;
const original = await agentSettings.get("researcher");
if (!original) throw new Error("researcher settings missing");
const customName = `Source Analyst ${crypto.randomUUID().slice(0, 8)}`;
const customDescription = `Delegated source analysis ${crypto.randomUUID().slice(0, 8)}`;
try {
await agentSettings.update("researcher", {
...original,
name: customName,
description: customDescription,
});
expect(await researcher.getInstructions()).toContain(`Display identity: ${customName}.`);
expect(researcher.getDescription()).toBe(customDescription);
} finally {
await agentSettings.update("researcher", original);
await agent.listAgents();
}
});
test("resolves persisted skills for specialist agents", async () => {
const researcher = (await agent.listAgents()).researcher!;
expect((await (researcher as unknown as typeof agent).listSkills()).map((item) => item.name))
.toContain("codebase-investigation");
const name = `research-sources-${crypto.randomUUID().slice(0, 8)}`;
const skill = await agentSkills.create("researcher", {
name,
description: "Use for source-sensitive research.",
instructions: "Prefer primary sources and report URLs.",
references: {},
enabled: true,
userInvocable: false,
});
try {
expect((await (researcher as unknown as typeof agent).listSkills()).map((item) => item.name)).toContain(name);
} finally {
await agentSkills.delete("researcher", skill.id);
}
});
test("loads supervisor instructions and provenance from the database", async () => {
const stored = await agentSettings.get("orchistrator");
expect(stored?.sourceUrls).toEqual(
expect.arrayContaining([expect.stringContaining("engineering-multi-agent-systems-architect.md")]),
);
expect(await agent.getInstructions()).toContain(stored!.instructions);
});
test("answers with a per-call model override", async () => {
const out = await agent.stream("Reply with exactly the word: pong", {
model: resolveModel("cx/gpt-5.3-codex-spark"),
});
expect((await out.text).toLowerCase()).toContain("pong");
}, 60000);
});