feat: wire agent.delegate tool with sub-agent configs

- Export createAgentDelegateTool through builtin/index.ts → tools/index.ts
- Register agent.delegate in routing.ts with lazy orchestrator pattern
- Add agent.delegate + agents.list to messaging and coding policy profiles
- Add group:agents tool group to policy.ts
- Add research/code/comms agent config examples to default.yaml
- Add research/code/comms agent configs to user config.yaml
- Add 11 tests for agent-delegate tool (all pass)
- Typecheck clean, no regressions
This commit is contained in:
William Valentin
2026-02-17 10:28:29 -08:00
parent 288ef5ac3c
commit 776b47f80f
16 changed files with 890 additions and 4 deletions
+52
View File
@@ -34,6 +34,57 @@ import type { SkillRegistry, SkillInstaller } from '../skills/index.js';
import type { GatewayServer } from '../gateway/index.js';
import { AuditLogger, initAuditLogger } from '../audit/index.js';
import { BackupScheduler } from '../backup/index.js';
import {
ClaudeCodeBackend,
OpenCodeBackend,
CodexBackend,
GeminiBackend,
type ExternalBackend,
type ExternalBackendName,
} from '../backends/index.js';
function createConfiguredExternalBackends(config: Config): {
backends: Partial<Record<ExternalBackendName, ExternalBackend>>;
defaultName?: ExternalBackendName;
} {
const backends: Partial<Record<ExternalBackendName, ExternalBackend>> = {};
if (config.backends.claude_code.enabled) {
backends.claude_code = new ClaudeCodeBackend(
config.backends.claude_code.path,
config.backends.claude_code.args,
config.backends.claude_code.timeout_ms,
);
}
if (config.backends.opencode.enabled) {
backends.opencode = new OpenCodeBackend(
config.backends.opencode.path,
config.backends.opencode.args,
config.backends.opencode.timeout_ms,
);
}
if (config.backends.codex.enabled) {
backends.codex = new CodexBackend(
config.backends.codex.path,
config.backends.codex.args,
config.backends.codex.timeout_ms,
);
}
if (config.backends.gemini.enabled) {
backends.gemini = new GeminiBackend(
config.backends.gemini.path,
config.backends.gemini.args,
config.backends.gemini.timeout_ms,
);
}
const selectedDefault = config.backends.default;
const defaultName = selectedDefault && backends[selectedDefault]
? selectedDefault
: (Object.keys(backends)[0] as ExternalBackendName | undefined);
return { backends, defaultName };
}
export interface DaemonContext {
config: Config;
@@ -163,6 +214,7 @@ export async function startDaemon(config: Config, options?: StartDaemonOptions):
const messageRouter = createMessageRouter({
sessionManager, modelRouter, systemPrompt, toolRegistry, toolExecutor,
config, memoryStore, agentConfigRegistry, agentRouter, sandboxManager, commandRegistry, intentRegistry, routingPolicy, skillRegistry,
...createConfiguredExternalBackends(config),
});
channelRegistry.setMessageHandler(messageRouter.handler);
channelAgents = messageRouter.agents;