AkurAI Build
Menu

popagent

public

Latest change 4e7296dc8fad231b297fb83b42fe24e7bd4610ab - fix: wake addressed task agents 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 AgentId,
  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. 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,
  ...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 taskParticipantAgents = {
  orchistrator: supervisorAgent,
  ...specialistAgents,
} satisfies Record<AgentId, 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());