Menu
popagent
publicLatest change 7f6c1d0ed24ffc264e50e9966eaf45024245ba71 - feat: add per-task agent communication by AkurAI Build
import { Agent } from "@mastra/core/agent";
import type { RequestContext } from "@mastra/core/request-context";
import { Mastra } from "@mastra/core/mastra";
import {
createFinalResponseGuard,
specialistExecutionOptions,
toolCallConcurrency,
} from "./agent-autonomy";
import { AGENT_CONTEXT_KEYS, agentContextValue } from "./agent-context";
import { agentRuntimeSettings } from "./agent-runtime-settings";
import { agentSettings } from "./agent-settings";
import { agentSkills } from "./agent-skills";
import {
BUILD_AGENT_IDS,
BUILD_AGENT_TOOL_ALLOWLISTS,
DEFAULT_WORKSPACE_ID,
type AgentRuntimeSettings,
type AgentSettings,
type BuildAgentId,
} from "./api-types";
import { evolutionStore } from "./evolution-store";
import { resolveModel } from "./models";
import { logger, observability, setAppLogger } from "./observability";
import { memory, storage } from "./storage";
import { bifrostNavigator } from "./tools/bifrost";
import { getTime } from "./tools/get-time";
import { editMemory, recallMemory, reflectMemory, retainMemory } from "./tools/memory";
import { useBrowserSecret } from "./tools/secrets";
import { webSearch } from "./tools/web-search";
import { searchDocumentation } from "./tools/search-documentation";
import { repoBrief } from "./tools/repo-brief";
import { akuraiBuildTools } from "./tools/akurai-build";
import { createAgentCommunicationTool } from "./tools/agent-communication";
import { agentWorkspaceRuntime } from "./workspace";
async function requiredAgentProfile(id: string): Promise<AgentSettings> {
const settings = await agentSettings.get(id);
if (!settings) throw new Error(`Agent settings not found: ${id}`);
return settings;
}
async function renderedInstructions(
settings: AgentSettings,
requestContext?: RequestContext,
): Promise<string> {
const instructions = settings.instructions.trim();
if (!instructions) throw new Error(`Agent instructions are empty: ${settings.id}`);
const overlay = await evolutionStore.learnedOverlay(settings.id);
const base = `Display identity: ${settings.name.trim()}.\n\n${instructions}`;
const rendered = overlay ? `${base}\n\n## Learned behavior overlay\n${overlay}` : base;
const communication = agentContextValue(requestContext, AGENT_CONTEXT_KEYS.communicationSession);
if (!communication) return rendered;
const incoming = communication.takeContext(settings.id)
.map((message) =>
`- [${message.id}] ${message.from}${message.replyTo ? ` (reply to ${message.replyTo})` : ""}: ${message.body}`)
.join("\n");
const pending = incoming
? `\n\n## Incoming task messages\nThese addressed peer messages were consumed from your task mailbox at this safe model boundary:\n${incoming}`
: "";
return `${rendered}\n\n## Task coordination\nUse the agentCommunication tool to list task peers, exchange concise addressed messages and replies, or wait only when coordination blocks your next step. A queued receipt is pending; only delivered means a waiting peer consumed it.${pending}`;
}
async function configuredRuntime(
requestContext: RequestContext | undefined,
): Promise<AgentRuntimeSettings> {
return agentContextValue(requestContext, AGENT_CONTEXT_KEYS.runtimeSettings)
?? agentRuntimeSettings.get();
}
const modelForContext = async (
{ requestContext }: { requestContext: RequestContext },
) => {
const selected = agentContextValue(requestContext, AGENT_CONTEXT_KEYS.model);
const settings = selected ? undefined : await configuredRuntime(requestContext);
return resolveModel(selected ?? settings!.defaultModel);
};
const modelForRole = (id: string) => async (
{ requestContext }: { requestContext: RequestContext },
) => {
const profile = await requiredAgentProfile(id);
return profile.model ? resolveModel(profile.model) : modelForContext({ requestContext });
};
const sharedSubagentOptions = {
defaultOptions: specialistExecutionOptions,
};
const toolRegistry = {
getTime,
retainMemory,
recallMemory,
reflectMemory,
editMemory,
useBrowserSecret,
bifrostNavigator,
webSearch,
searchDocumentation,
repoBrief,
...akuraiBuildTools,
};
const buildToolSet = new Set<string>(Object.values(BUILD_AGENT_TOOL_ALLOWLISTS).flat());
async function configuredTools(id: string, requestContext?: RequestContext) {
const profile = await requiredAgentProfile(id);
const executionSource = agentContextValue(
requestContext,
AGENT_CONTEXT_KEYS.executionSource,
);
const containedAutonomy = executionSource === "self-update" || executionSource === "build-maintenance";
const isBuildRole = BUILD_AGENT_IDS.includes(id as BuildAgentId);
const buildAllowed = isBuildRole
? BUILD_AGENT_TOOL_ALLOWLISTS[id as BuildAgentId] as readonly string[]
: undefined;
const tools = Object.fromEntries(
profile.tools
.filter((tool) => !buildToolSet.has(tool) || (isBuildRole && buildAllowed?.includes(tool)))
.filter((tool) => !containedAutonomy || !["retainMemory", "editMemory", "useBrowserSecret", "bifrostNavigator"].includes(tool))
.map((tool) => [tool, toolRegistry[tool]]),
) as Partial<typeof toolRegistry>;
const communication = agentContextValue(requestContext, AGENT_CONTEXT_KEYS.communicationSession);
return communication
? { ...tools, agentCommunication: createAgentCommunicationTool(id, communication) }
: tools;
}
const workspaceFor = (id: string) =>
async ({ requestContext }: { requestContext: RequestContext }) => {
const profile = await requiredAgentProfile(id);
if (profile.workspaceAccess === "none") return undefined;
const selectedWorkspace = agentContextValue(requestContext, AGENT_CONTEXT_KEYS.workspaceId);
const executionSource = agentContextValue(
requestContext,
AGENT_CONTEXT_KEYS.executionSource,
);
const containedAutonomy = executionSource === "self-update" || executionSource === "build-maintenance";
const containedWritableMaintainer = executionSource === "build-maintenance" && id === "build-maintainer";
const readOnly = profile.workspaceAccess === "read-only"
|| (containedAutonomy && id !== "orchistrator" && id !== "implementer" && !containedWritableMaintainer);
if (!containedAutonomy) return agentWorkspaceRuntime.resolve(selectedWorkspace, readOnly);
const path = agentContextValue(requestContext, AGENT_CONTEXT_KEYS.selfUpdateWorkspacePath);
if (!path) throw new Error("Autonomous workspace context is unavailable");
return agentWorkspaceRuntime.resolveAutonomous(
selectedWorkspace ?? DEFAULT_WORKSPACE_ID,
path,
readOnly,
executionSource as "self-update" | "build-maintenance",
);
};
let [
supervisorProfile,
researcherProfile,
implementerProfile,
reviewerProfile,
buildMaintainerProfile,
buildReleaseManagerProfile,
communityStewardProfile,
] = await Promise.all([
requiredAgentProfile("orchistrator"),
requiredAgentProfile("researcher"),
requiredAgentProfile("implementer"),
requiredAgentProfile("reviewer"),
requiredAgentProfile("build-maintainer"),
requiredAgentProfile("build-release-manager"),
requiredAgentProfile("community-steward"),
]);
let researcherAgent: Agent;
researcherAgent = new Agent({
id: "researcher",
name: researcherProfile.name,
description: researcherProfile.description,
instructions: async ({ requestContext }) => {
researcherProfile = await requiredAgentProfile("researcher");
researcherAgent.name = researcherProfile.name;
return renderedInstructions(researcherProfile, requestContext);
},
tools: ({ requestContext }) => configuredTools("researcher", requestContext),
skills: () => agentSkills.resolve("researcher"),
workspace: workspaceFor("researcher"),
...sharedSubagentOptions,
model: modelForRole("researcher"),
});
researcherAgent.getDescription = () => researcherProfile.description;
let implementerAgent: Agent;
implementerAgent = new Agent({
id: "implementer",
name: implementerProfile.name,
description: implementerProfile.description,
instructions: async ({ requestContext }) => {
implementerProfile = await requiredAgentProfile("implementer");
implementerAgent.name = implementerProfile.name;
return renderedInstructions(implementerProfile, requestContext);
},
tools: ({ requestContext }) => configuredTools("implementer", requestContext),
skills: () => agentSkills.resolve("implementer"),
workspace: workspaceFor("implementer"),
...sharedSubagentOptions,
model: modelForRole("implementer"),
});
implementerAgent.getDescription = () => implementerProfile.description;
let reviewerAgent: Agent;
reviewerAgent = new Agent({
id: "reviewer",
name: reviewerProfile.name,
description: reviewerProfile.description,
instructions: async ({ requestContext }) => {
reviewerProfile = await requiredAgentProfile("reviewer");
reviewerAgent.name = reviewerProfile.name;
return renderedInstructions(reviewerProfile, requestContext);
},
tools: ({ requestContext }) => configuredTools("reviewer", requestContext),
skills: () => agentSkills.resolve("reviewer"),
workspace: workspaceFor("reviewer"),
...sharedSubagentOptions,
model: modelForRole("reviewer"),
});
reviewerAgent.getDescription = () => reviewerProfile.description;
let buildMaintainerAgent: Agent;
buildMaintainerAgent = new Agent({
id: "build-maintainer",
name: buildMaintainerProfile.name,
description: buildMaintainerProfile.description,
instructions: async ({ requestContext }) => {
buildMaintainerProfile = await requiredAgentProfile("build-maintainer");
buildMaintainerAgent.name = buildMaintainerProfile.name;
return renderedInstructions(buildMaintainerProfile, requestContext);
},
tools: ({ requestContext }) => configuredTools("build-maintainer", requestContext),
skills: () => agentSkills.resolve("build-maintainer"),
workspace: workspaceFor("build-maintainer"),
...sharedSubagentOptions,
model: modelForRole("build-maintainer"),
});
buildMaintainerAgent.getDescription = () => buildMaintainerProfile.description;
let buildReleaseManagerAgent: Agent;
buildReleaseManagerAgent = new Agent({
id: "build-release-manager",
name: buildReleaseManagerProfile.name,
description: buildReleaseManagerProfile.description,
instructions: async ({ requestContext }) => {
buildReleaseManagerProfile = await requiredAgentProfile("build-release-manager");
buildReleaseManagerAgent.name = buildReleaseManagerProfile.name;
return renderedInstructions(buildReleaseManagerProfile, requestContext);
},
tools: ({ requestContext }) => configuredTools("build-release-manager", requestContext),
skills: () => agentSkills.resolve("build-release-manager"),
workspace: workspaceFor("build-release-manager"),
...sharedSubagentOptions,
model: modelForRole("build-release-manager"),
});
buildReleaseManagerAgent.getDescription = () => buildReleaseManagerProfile.description;
let communityStewardAgent: Agent;
communityStewardAgent = new Agent({
id: "community-steward",
name: communityStewardProfile.name,
description: communityStewardProfile.description,
instructions: async ({ requestContext }) => {
communityStewardProfile = await requiredAgentProfile("community-steward");
communityStewardAgent.name = communityStewardProfile.name;
return renderedInstructions(communityStewardProfile, requestContext);
},
tools: ({ requestContext }) => configuredTools("community-steward", requestContext),
skills: () => agentSkills.resolve("community-steward"),
workspace: workspaceFor("community-steward"),
...sharedSubagentOptions,
model: modelForRole("community-steward"),
});
communityStewardAgent.getDescription = () => communityStewardProfile.description;
const specialistAgents = {
researcher: researcherAgent,
implementer: implementerAgent,
reviewer: reviewerAgent,
"build-maintainer": buildMaintainerAgent,
"build-release-manager": buildReleaseManagerAgent,
"community-steward": communityStewardAgent,
};
async function refreshSpecialistProfiles(
{ requestContext }: { requestContext: RequestContext },
) {
[
researcherProfile,
implementerProfile,
reviewerProfile,
buildMaintainerProfile,
buildReleaseManagerProfile,
communityStewardProfile,
] = await Promise.all([
requiredAgentProfile("researcher"),
requiredAgentProfile("implementer"),
requiredAgentProfile("reviewer"),
requiredAgentProfile("build-maintainer"),
requiredAgentProfile("build-release-manager"),
requiredAgentProfile("community-steward"),
]);
researcherAgent.name = researcherProfile.name;
implementerAgent.name = implementerProfile.name;
reviewerAgent.name = reviewerProfile.name;
buildMaintainerAgent.name = buildMaintainerProfile.name;
buildReleaseManagerAgent.name = buildReleaseManagerProfile.name;
communityStewardAgent.name = communityStewardProfile.name;
const repositoryBriefReady = agentContextValue(
requestContext,
AGENT_CONTEXT_KEYS.repositoryBriefReady,
) === true;
const executionSource = agentContextValue(requestContext, AGENT_CONTEXT_KEYS.executionSource);
const containedAutonomy = executionSource === "self-update" || executionSource === "build-maintenance";
const enabled = Object.entries(specialistAgents).filter(([id]) => {
if (executionSource === "build-maintenance") {
if (id === "reviewer") return reviewerProfile.delegationEnabled;
return id === "build-maintainer" && buildMaintainerProfile.delegationEnabled;
}
if (id === "build-maintainer") return buildMaintainerProfile.delegationEnabled;
if (id === "build-release-manager") return buildReleaseManagerProfile.delegationEnabled;
if (id === "community-steward") return communityStewardProfile.delegationEnabled;
if (id === "researcher") return researcherProfile.delegationEnabled && !repositoryBriefReady;
if (id === "implementer") return implementerProfile.delegationEnabled;
if (id === "reviewer") return reviewerProfile.delegationEnabled;
if (executionSource === "self-update") return false;
return false;
});
if (!containedAutonomy) {
return Object.fromEntries(enabled) as typeof specialistAgents;
}
return Object.fromEntries(enabled.map(([id, specialist]) => {
const fork = specialist.__fork();
fork.setBrowser(undefined);
return [id, fork];
})) as typeof specialistAgents;
}
let supervisorAgent: Agent;
supervisorAgent = new Agent({
id: "orchistrator",
name: supervisorProfile.name,
description: supervisorProfile.description,
instructions: async ({ requestContext }) => {
supervisorProfile = await requiredAgentProfile("orchistrator");
supervisorAgent.name = supervisorProfile.name;
return renderedInstructions(supervisorProfile, requestContext);
},
model: modelForRole("orchistrator"),
defaultOptions: async ({ requestContext }) => {
const settings = await configuredRuntime(requestContext);
return {
maxSteps: settings.supervisorMaxSteps,
toolCallConcurrency: toolCallConcurrency(settings),
onIterationComplete: createFinalResponseGuard(settings),
};
},
tools: ({ requestContext }) => configuredTools("orchistrator", requestContext),
memory,
workspace: workspaceFor("orchistrator"),
skills: () => agentSkills.resolve("orchistrator"),
agents: refreshSpecialistProfiles,
});
supervisorAgent.getDescription = () => supervisorProfile.description;
export const agent = supervisorAgent;
export const researcher = researcherAgent;
export const implementer = implementerAgent;
export const reviewer = reviewerAgent;
export const mastra = new Mastra({
agents: { orchistrator: agent },
storage,
logger,
observability,
environment: process.env.POPAGENT_ENVIRONMENT ?? process.env.NODE_ENV,
});
setAppLogger(mastra.getLogger());