Menu
popagent
publicLatest change 248b7c0673ea9d544f9e4738ee4d3e8ec2925924 - Add native BifrOSt Navigator browser provider with per-browser status LEDs 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 { browserSettings } from "./browser-settings";
import { agentSkills } from "./agent-skills";
import { companyAgentProfiles } from "./company-roster";
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 { changeImpact, codeContext, symbolContext } from "./tools/code-intelligence";
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;
}
/**
* Clone an agent independently of its singleton, isolating concurrent runs.
*
* Relies on Mastra's documented-internal `Agent.__fork()` (see
* node_modules/@mastra/core/dist/agent/agent.d.ts): there is no public API
* for this, so every isolated-clone call site in this codebase must go
* through this helper (or `forkWithoutBrowser`) instead of calling
* `__fork()` directly.
*/
export function forkAgent(target: Agent): Agent {
return target.__fork();
}
/** Clone an agent with browser tools disabled for a contained autonomous run. */
export function forkWithoutBrowser(target: Agent): Agent {
const fork = forkAgent(target);
fork.setBrowser(undefined);
return fork;
}
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. An idle addressed peer is woken in its own Mastra run. Never reply on another agent's behalf: after sending, wait for that recipient's reply when you need an answer. A queued receipt remains pending; delivered means an active waiter 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,
codeContext,
symbolContext,
changeImpact,
...akuraiBuildTools,
};
const buildToolSet = new Set<string>(Object.values(BUILD_AGENT_TOOL_ALLOWLISTS).flat());
const workspaceTools: Record<string, true> = {
repoBrief: true,
codeContext: true,
symbolContext: true,
changeImpact: true,
searchDocumentation: true,
};
const browserTools: Record<string, true> = {
bifrostNavigator: true,
useBrowserSecret: true,
};
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;
// Selecting the native BifrOSt Navigator provider grants its bridge tool to
// every role with browser access, mirroring how the headless provider is
// attached; the tool itself still enforces read-only operation limits.
const browser = await browserSettings.get();
const nativeBrowser = browser.enabled && browser.provider === "bifrost-navigator";
const assigned = nativeBrowser && profile.browserAccess !== "none" && !profile.tools.includes("bifrostNavigator")
? [...profile.tools, "bifrostNavigator" as const]
: profile.tools;
const tools = Object.fromEntries(
assigned
.filter((tool) => !buildToolSet.has(tool) || (isBuildRole && buildAllowed?.includes(tool)))
.filter((tool) => profile.workspaceAccess !== "none" || !workspaceTools[tool])
.filter((tool) => profile.browserAccess !== "none" || !browserTools[tool])
.filter((tool) => profile.browserAccess === "interactive" || tool !== "useBrowserSecret")
.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 createCompanySpecialist(id: string) {
let profile = await requiredAgentProfile(id);
let specialist: Agent;
specialist = new Agent({
id,
name: profile.name,
description: profile.description,
instructions: async ({ requestContext }) => {
profile = await requiredAgentProfile(id);
specialist.name = profile.name;
return renderedInstructions(profile, requestContext);
},
tools: ({ requestContext }) => configuredTools(id, requestContext),
skills: () => agentSkills.resolve(id),
workspace: workspaceFor(id),
...sharedSubagentOptions,
model: modelForRole(id),
});
specialist.getDescription = () => profile.description;
return {
id,
agent: specialist,
enabled: () => profile.delegationEnabled,
refresh: async () => {
profile = await requiredAgentProfile(id);
specialist.name = profile.name;
},
};
}
const companySpecialistRuntimes = await Promise.all(
companyAgentProfiles.filter((profile) => !profile.established)
.map((profile) => createCompanySpecialist(profile.id)),
);
const companySpecialistRuntimeById = new Map(
companySpecialistRuntimes.map((runtime) => [runtime.id, runtime]),
);
const allSpecialistAgents: Record<string, Agent> = {
...specialistAgents,
...Object.fromEntries(companySpecialistRuntimes.map((runtime) => [runtime.id, runtime.agent])),
};
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;
await Promise.all(companySpecialistRuntimes.map((runtime) => runtime.refresh()));
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(allSpecialistAgents).filter(([id]) => {
const companySpecialist = companySpecialistRuntimeById.get(id);
if (companySpecialist) return !containedAutonomy && companySpecialist.enabled();
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);
}
return Object.fromEntries(enabled.map(([id, specialist]) => [id, forkWithoutBrowser(specialist)]));
}
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 taskParticipantAgents = {
orchistrator: supervisorAgent,
...allSpecialistAgents,
} as Record<string, Agent>;
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());