feat(P03): core agent flesh — VerifierAgent, ResearcherAgent, TesterAgent intrinsic logic

This commit is contained in:
Jon Chery
2026-05-29 18:08:38 +00:00
parent 99df4fe4e2
commit bbabd2dc0a
10 changed files with 1179 additions and 42 deletions
+74 -2
View File
@@ -15,16 +15,26 @@ import {
import { AgentName, ModelProfile } from "../types/config.js";
import { Decision } from "../types/decisions.js";
import { Escalation } from "../types/escalation.js";
import { ToolRegistry, ToolCall, ToolResult } from "./tool-registry.js";
import { ToolRegistry, ToolCall, ToolResult, ToolDefinition } from "./tool-registry.js";
const MAX_TOOL_ROUNDS = 50;
const PERSONA_TOOL_MAP: Record<string, string> = {
read: "readFile",
write: "writeFile",
edit: "editFile",
bash: "runBash",
glob: "glob",
grep: "grep",
};
export abstract class OllamaBaseBackend implements IntelligenceBackend {
abstract readonly name: string;
readonly type: BackendType = "llm";
protected config: LLMBackendConfig;
protected projectPath: string;
protected filteredToolSchema: Array<Record<string, unknown>> | null = null;
constructor(config: LLMBackendConfig | undefined) {
this.config = config || { base_url: "http://localhost:11434", model_profile: "balanced" };
@@ -42,6 +52,9 @@ export abstract class OllamaBaseBackend implements IntelligenceBackend {
const model = this.resolveModel();
const toolRegistry = new ToolRegistry(request.context.project_path);
const allowedTools = this.parsePersonaTools(personaContent);
const filteredDefinitions = this.filterToolDefinitions(toolRegistry.getDefinitions(), allowedTools);
this.filteredToolSchema = this.definitionsToOpenAISchema(filteredDefinitions);
const messages: OllamaMessage[] = [];
messages.push({
@@ -62,7 +75,7 @@ export abstract class OllamaBaseBackend implements IntelligenceBackend {
while (round < MAX_TOOL_ROUNDS) {
round++;
const response = await this.callModel(messages, model, toolRegistry);
const response = await this.callModelWithTools(messages, model, filteredDefinitions);
totalInputTokens += response.usage?.prompt_tokens || 0;
totalOutputTokens += response.usage?.completion_tokens || 0;
@@ -124,6 +137,65 @@ export abstract class OllamaBaseBackend implements IntelligenceBackend {
}
}
protected parsePersonaTools(personaContent: string): string[] | null {
const frontmatterMatch = personaContent.match(/^---\n([\s\S]*?)\n---/);
if (!frontmatterMatch) return null;
const frontmatter = frontmatterMatch[1];
const toolsMatch = frontmatter.match(/tools:\s*\n((?:\s+\w+:.+\n?)+)/);
if (!toolsMatch) {
const inlineMatch = frontmatter.match(/tools:\s*\[([^\]]+)\]/);
if (inlineMatch) {
return inlineMatch[1]
.split(",")
.map((t) => t.trim())
.filter(Boolean)
.map((t) => PERSONA_TOOL_MAP[t] || t);
}
return null;
}
const toolsBlock = toolsMatch[1];
const toolNames: string[] = [];
const lineRegex = /^\s+(\w+):/gm;
let lineMatch;
while ((lineMatch = lineRegex.exec(toolsBlock)) !== null) {
const personaToolName = lineMatch[1];
toolNames.push(PERSONA_TOOL_MAP[personaToolName] || personaToolName);
}
return toolNames.length > 0 ? toolNames : null;
}
protected filterToolDefinitions(definitions: ToolDefinition[], allowedTools: string[] | null): ToolDefinition[] {
if (!allowedTools) return definitions;
const allowedSet = new Set(allowedTools);
return definitions.filter((def) => allowedSet.has(def.name));
}
protected async callModelWithTools(
messages: OllamaMessage[],
model: string,
toolDefinitions: ToolDefinition[]
): Promise<OllamaChatResponse> {
return this.callModel(messages, model, new ToolRegistry(this.projectPath));
}
protected definitionsToOpenAISchema(definitions: ToolDefinition[]): Array<Record<string, unknown>> {
return definitions.map((def) => ({
type: "function",
function: {
name: def.name,
description: def.description,
parameters: def.parameters,
},
}));
}
protected getActiveToolSchema(toolRegistry: ToolRegistry): Array<Record<string, unknown>> {
return this.filteredToolSchema || toolRegistry.getOpenAIToolSchema();
}
protected abstract callModel(
messages: OllamaMessage[],
model: string,
+1 -1
View File
@@ -61,7 +61,7 @@ export class OllamaCloudBackend extends OllamaBaseBackend {
if (m.tool_calls) msg.tool_calls = m.tool_calls;
return msg;
}),
tools: toolRegistry.getOpenAIToolSchema(),
tools: this.getActiveToolSchema(toolRegistry),
stream: false,
};
+1 -1
View File
@@ -48,7 +48,7 @@ export class OllamaLocalBackend extends OllamaBaseBackend {
if (m.tool_calls) msg.tool_calls = m.tool_calls;
return msg;
}),
tools: toolRegistry.getOpenAIToolSchema(),
tools: this.getActiveToolSchema(toolRegistry),
stream: false,
};