feat(subagents): add multi-turn subagent session runtime
This commit is contained in:
+58
-7
@@ -3,13 +3,13 @@ import type { Attachment } from '../channels/types.js';
|
||||
import { isSupportedAudio, transcribeAudio } from '../models/media.js';
|
||||
import { synthesizeSpeechAttachment } from '../models/tts.js';
|
||||
import { supportsAudioInput } from '../models/capabilities.js';
|
||||
import { AgentOrchestrator, type DelegationConfig } from '../backends/index.js';
|
||||
import { AgentOrchestrator, SubagentManager, type DelegationConfig } from '../backends/index.js';
|
||||
import { OutboundAttachmentCollector } from '../backends/native/attachments.js';
|
||||
import type { ExternalBackend, ExternalBackendName } from '../backends/index.js';
|
||||
import type { InboundMessage, OutboundMessage } from '../channels/index.js';
|
||||
import { MemoryStore } from '../memory/index.js';
|
||||
import type { Tool } from '../tools/types.js';
|
||||
import { createMediaSendTool, createAgentDelegateTool, createCouncilRunTool } from '../tools/index.js';
|
||||
import { createMediaSendTool, createAgentDelegateTool, createCouncilRunTool, createSubagentTools } from '../tools/index.js';
|
||||
import type { AgentDelegateDeps } from '../tools/index.js';
|
||||
import { createSandboxedShellTool, createSandboxedProcessStartTool, SandboxManager } from '../sandbox/index.js';
|
||||
import { MODEL_PROVIDERS, type Config, type CouncilsConfig, type ModelConfig, type ModelProvider } from '../config/index.js';
|
||||
@@ -382,10 +382,18 @@ export function createMessageRouter(deps: {
|
||||
setBackendMode?: (mode: BackendRuntimeMode) => void;
|
||||
}): {
|
||||
handler: (msg: InboundMessage, reply: (response: OutboundMessage) => Promise<void>) => Promise<void>;
|
||||
agents: Map<string, { orchestrator: AgentOrchestrator; collector: OutboundAttachmentCollector }>;
|
||||
agents: Map<string, {
|
||||
orchestrator: AgentOrchestrator;
|
||||
collector: OutboundAttachmentCollector;
|
||||
subagentManager?: SubagentManager;
|
||||
}>;
|
||||
} {
|
||||
// Cache agents by session ID + agent config name to avoid recreating on every message
|
||||
const agents = new Map<string, { orchestrator: AgentOrchestrator; collector: OutboundAttachmentCollector }>();
|
||||
const agents = new Map<string, {
|
||||
orchestrator: AgentOrchestrator;
|
||||
collector: OutboundAttachmentCollector;
|
||||
subagentManager?: SubagentManager;
|
||||
}>();
|
||||
const talkModeUntil = new Map<string, number>();
|
||||
const activeRuns = new Map<string, AgentOrchestrator>();
|
||||
const reactionCooldowns = new Map<string, number>();
|
||||
@@ -530,7 +538,16 @@ export function createMessageRouter(deps: {
|
||||
}
|
||||
}
|
||||
|
||||
function getOrCreateAgent(channel: string, senderId: string, metadata?: Record<string, unknown>, agentOverride?: string): { orchestrator: AgentOrchestrator; collector: OutboundAttachmentCollector } {
|
||||
function getOrCreateAgent(
|
||||
channel: string,
|
||||
senderId: string,
|
||||
metadata?: Record<string, unknown>,
|
||||
agentOverride?: string,
|
||||
): {
|
||||
orchestrator: AgentOrchestrator;
|
||||
collector: OutboundAttachmentCollector;
|
||||
subagentManager?: SubagentManager;
|
||||
} {
|
||||
// Resolve agent config name via routing (sender → channel → default fallback)
|
||||
const agentConfigName = agentOverride ?? deps.agentRouter?.resolve(channel, senderId);
|
||||
const agentConfig = agentConfigName ? deps.agentConfigRegistry?.get(agentConfigName) : undefined;
|
||||
@@ -664,6 +681,28 @@ export function createMessageRouter(deps: {
|
||||
effectiveToolRegistry = effectiveToolRegistry.clone();
|
||||
effectiveToolRegistry.register(createMediaSendTool(collector));
|
||||
|
||||
let subagentManager: SubagentManager | undefined;
|
||||
const subagentsEnabled = deps.config.agents.subagents?.enabled ?? true;
|
||||
const maxSubagentSessions = deps.config.agents.subagents?.max_active_sessions ?? 6;
|
||||
if (subagentsEnabled && deps.agentConfigRegistry && deps.agentConfigRegistry.list().length > 0) {
|
||||
subagentManager = new SubagentManager({
|
||||
parentSessionId: session.id,
|
||||
modelRouter: deps.modelRouter,
|
||||
sessionManager: deps.sessionManager,
|
||||
toolRegistry: effectiveToolRegistry,
|
||||
toolExecutor: deps.toolExecutor,
|
||||
agentConfigRegistry: deps.agentConfigRegistry,
|
||||
delegation: delegationConfig,
|
||||
maxDelegationDepth: deps.config.agents.max_delegation_depth ?? 3,
|
||||
defaultPrimaryTier: effectiveTier,
|
||||
maxIterations: deps.config.agents.max_iterations,
|
||||
maxActiveSessions: maxSubagentSessions,
|
||||
});
|
||||
for (const tool of createSubagentTools(subagentManager)) {
|
||||
effectiveToolRegistry.register(tool);
|
||||
}
|
||||
}
|
||||
|
||||
// Register delegation tools with lazy orchestrator reference (resolved after construction)
|
||||
let resolveOrchestrator: ((o: AgentOrchestrator) => void) | undefined;
|
||||
if (deps.agentConfigRegistry && deps.agentConfigRegistry.list().length > 0) {
|
||||
@@ -766,7 +805,7 @@ export function createMessageRouter(deps: {
|
||||
// Resolve the lazy orchestrator reference for agent.delegate
|
||||
resolveOrchestrator?.(orchestrator);
|
||||
|
||||
entry = { orchestrator, collector };
|
||||
entry = { orchestrator, collector, subagentManager };
|
||||
agents.set(sessionId, entry);
|
||||
}
|
||||
return entry;
|
||||
@@ -960,7 +999,12 @@ export function createMessageRouter(deps: {
|
||||
|
||||
const agentConfigName = intentAgentOverride ?? deps.agentRouter?.resolve(msg.channel, msg.senderId);
|
||||
const agentConfig = agentConfigName ? deps.agentConfigRegistry?.get(agentConfigName) : undefined;
|
||||
const { orchestrator: agent, collector } = getOrCreateAgent(msg.channel, msg.senderId, effectiveMetadata, agentConfigName);
|
||||
const { orchestrator: agent, collector, subagentManager } = getOrCreateAgent(
|
||||
msg.channel,
|
||||
msg.senderId,
|
||||
effectiveMetadata,
|
||||
agentConfigName,
|
||||
);
|
||||
|
||||
const commandInput = msg.metadata?.isCommand && typeof msg.metadata.command === 'string'
|
||||
? `/${msg.metadata.command}${msg.metadata.commandArgs ? ` ${msg.metadata.commandArgs}` : ''}`
|
||||
@@ -999,6 +1043,13 @@ export function createMessageRouter(deps: {
|
||||
names.add('council.run');
|
||||
}
|
||||
}
|
||||
if (subagentManager) {
|
||||
names.add('subagent.spawn');
|
||||
names.add('subagent.send');
|
||||
names.add('subagent.list');
|
||||
names.add('subagent.cancel');
|
||||
names.add('subagent.delete');
|
||||
}
|
||||
const sorted = [...names].sort();
|
||||
return [
|
||||
`Available tools (${sorted.length}):`,
|
||||
|
||||
Reference in New Issue
Block a user