feat(server): inference state-graph + supervisor, memory tools, MCP client, schema, routes
- Add state-graph.ts: typed state machine for inference lifecycle - Add supervisor.ts: agent supervisor pattern for multi-agent coordination - Add export-formatter.ts: structured export formatting - Add manage_memory.ts: memory CRUD tool for agent persistence - Add get_wiki_article.ts: codecontext wiki article retrieval - Extend memory/index.ts: 3-tier memory (context/daily/core) - Extend MCP client: mcp-config.ts env-var substitution - Update schema.sql: agent_sessions, tasks, pending_changes extensions - Update API types: MessageMetadata, ErrorReason, AgentSessionConfig - Update routes: chats, messages, sessions — column renames and agent_session_id - Update inference: error handler, payload builder, stream phase, turn orchestrator
This commit is contained in:
75
apps/server/src/services/inference/supervisor.ts
Normal file
75
apps/server/src/services/inference/supervisor.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
// Supervisor agent: routes user requests to the best agent via a cheap LLM
|
||||
// classification call. Activated when session.agent_id === 'supervisor'.
|
||||
|
||||
import type { Agent } from '../../types/api.js';
|
||||
import { taskModelCompletion } from '../task-model.js';
|
||||
|
||||
export interface SupervisorRoute {
|
||||
agent_id: string;
|
||||
confidence: number;
|
||||
reasoning: string;
|
||||
}
|
||||
|
||||
const SUPERVISOR_SYSTEM_PROMPT = `You are a router. Given the user's request and the available agents, choose the best agent to handle the request.
|
||||
|
||||
Rules:
|
||||
- Match the request to the agent whose description and toolset best fits the task.
|
||||
- For code review / bug finding requests → code-reviewer
|
||||
- For debugging / diagnosing failures → debugger
|
||||
- For refactoring / simplifying code → refactorer
|
||||
- For architecture / design / planning → architect or planner
|
||||
- For security audits → security-auditor
|
||||
- For building prompts for other agents → prompt-builder
|
||||
- For exploring / understanding unfamiliar code → recon
|
||||
- For implementing / writing code changes → builder
|
||||
- Respond with ONLY the agent id (e.g. "builder") or "none" if no agent fits.
|
||||
- Do not include any other text, punctuation, or explanation.`;
|
||||
|
||||
const MAX_ROUTING_TOKENS = 30;
|
||||
|
||||
/**
|
||||
* Given the user's latest message and available agents, classifies which agent
|
||||
* should handle this turn. Returns null to fall through to default (no agent).
|
||||
*/
|
||||
export async function resolveSupervisorTurn(
|
||||
latestUserMessage: string,
|
||||
agents: Agent[],
|
||||
fallbackModel?: string,
|
||||
): Promise<SupervisorRoute | null> {
|
||||
// Build agent listing — skip the supervisor itself to avoid self-routing.
|
||||
const agentList = agents
|
||||
.filter((a) => a.id !== 'supervisor')
|
||||
.map((a) => `- ${a.id}: ${a.description} (${a.tools.length} tools)`)
|
||||
.join('\n');
|
||||
|
||||
if (!agentList) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const userPrompt = `Available agents:\n${agentList}\n\nUser request: ${latestUserMessage.slice(0, 2000)}`;
|
||||
|
||||
const response = await taskModelCompletion({
|
||||
system: SUPERVISOR_SYSTEM_PROMPT,
|
||||
user: userPrompt,
|
||||
maxTokens: MAX_ROUTING_TOKENS,
|
||||
temperature: 0.1,
|
||||
fallbackModel,
|
||||
});
|
||||
|
||||
const agentId = response.trim().toLowerCase();
|
||||
if (!agentId || agentId === 'none') {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Map back to a real agent to validate the id.
|
||||
const matched = agents.find((a) => a.id === agentId);
|
||||
if (!matched) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
agent_id: matched.id,
|
||||
confidence: 1,
|
||||
reasoning: `supervisor routed to "${matched.name}" based on request classification`,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user