AkurAI Build
Menu

popagent

public

Latest change 4565eb356975518dfc0202eab29b56dff51e8f71 - add deterministic agent health validation by AkurAI Build

import { createTool, type ToolExecutionContext } from "@mastra/core/tools";
import { z } from "zod";
import { AGENT_CONTEXT_KEYS, agentContextValue } from "../agent-context";
import { codeIntelligence, type CodeIntelligenceService } from "../code-intelligence";
import { resolveAutonomousWorkspacePath } from "../workspace";

const relativePathSchema = z.string().trim().min(1).max(1_024);
const serviceSchema = z.object({ evidence: z.string() });
const symbolSchema = z.object({
  definition: z.string(),
  callers: z.string(),
  callees: z.string(),
});
const impactSchema = z.object({
  paths: z.array(z.string()),
  branchImpact: z.string(),
  files: z.array(z.object({ path: z.string(), evidence: z.string() })),
  unresolved: z.array(z.string()),
});

type Service = Pick<CodeIntelligenceService, "codeContext" | "symbolContext" | "changeImpact">;

function executionRepositoryPath(context: ToolExecutionContext): string | undefined {
  const source = agentContextValue(context.requestContext, AGENT_CONTEXT_KEYS.executionSource);
  if (source !== "self-update" && source !== "build-maintenance") return undefined;
  const path = agentContextValue(context.requestContext, AGENT_CONTEXT_KEYS.selfUpdateWorkspacePath);
  if (!path) throw new Error("Autonomous workspace context is unavailable");
  return resolveAutonomousWorkspacePath(path, source);
}

export function createCodeIntelligenceTools(service: Service = codeIntelligence) {
  const codeContext = createTool({
    id: "code-context",
    description: "Preferred first repository discovery tool. Return a bounded local hybrid-search evidence pack for a behavior or concept before broad file reads. Use grep for exact exhaustive text and LSP for semantic references.",
    inputSchema: z.object({
      query: z.string().trim().min(1).max(2_000),
      limit: z.number().int().min(1).max(20).default(8),
    }),
    outputSchema: serviceSchema,
    execute: async ({ query, limit }, context) => {
      const workspaceId = agentContextValue(context.requestContext, AGENT_CONTEXT_KEYS.workspaceId);
      if (!workspaceId) throw new Error("Workspace context is required for code context");
      return service.codeContext(workspaceId, query, limit, executionRepositoryPath(context), context.abortSignal);
    },
  });

  const symbolContext = createTool({
    id: "symbol-context",
    description: "Inspect one known symbol through the local index: definition candidates plus direct callers and callees. Use LSP when precise type-aware references or rename safety is required.",
    inputSchema: z.object({
      symbol: z.string().trim().min(1).max(512),
      filePath: relativePathSchema.optional(),
    }),
    outputSchema: symbolSchema,
    execute: async ({ symbol, filePath }, context) => {
      const workspaceId = agentContextValue(context.requestContext, AGENT_CONTEXT_KEYS.workspaceId);
      if (!workspaceId) throw new Error("Workspace context is required for symbol context");
      return service.symbolContext(
        workspaceId,
        symbol,
        filePath,
        executionRepositoryPath(context),
        context.abortSignal,
      );
    },
  });

  const changeImpact = createTool({
    id: "change-impact",
    description: "Run bounded local blast-radius analysis before editing. Omit paths to inspect current working-tree changes; returns branch, dependency, caller/callee, and likely test evidence with unresolved edges.",
    inputSchema: z.object({
      paths: z.array(relativePathSchema).max(8).default([]),
    }),
    outputSchema: impactSchema,
    execute: async ({ paths }, context) => {
      const workspaceId = agentContextValue(context.requestContext, AGENT_CONTEXT_KEYS.workspaceId);
      if (!workspaceId) throw new Error("Workspace context is required for change impact");
      return service.changeImpact(
        workspaceId,
        paths,
        executionRepositoryPath(context),
        context.abortSignal,
      );
    },
  });

  return { codeContext, symbolContext, changeImpact };
}

export const { codeContext, symbolContext, changeImpact } = createCodeIntelligenceTools();