feat: add setup flow for dedicated research agent

This commit is contained in:
William Valentin
2026-02-17 15:27:09 -08:00
parent a055f4d338
commit be993146c0
7 changed files with 94 additions and 2 deletions
+9
View File
@@ -85,6 +85,15 @@ describe('ConfigBuilder', () => {
expect(obj.server.token).toBe('my-secret-token');
});
it('enables research agent config', () => {
const builder = new ConfigBuilder();
builder.setResearchAgentEnabled({ modelTier: 'complex' });
const obj = builder.build();
expect(obj.agent_configs?.research?.model_tier).toBe('complex');
expect(obj.agent_configs?.research?.tool_profile).toBe('messaging');
expect(typeof obj.agent_configs?.research?.system_prompt).toBe('string');
});
it('applies operator automation pack defaults', () => {
const builder = new ConfigBuilder();
builder.applyOperatorPack({
+30
View File
@@ -33,6 +33,16 @@ export interface SetupConfig {
sandbox?: { enabled?: boolean };
pairing?: { enabled?: boolean };
tools?: { profile?: string };
agent_configs?: Record<string, {
model_tier?: 'fast' | 'default' | 'complex' | 'local';
tool_profile?: string;
system_prompt?: string;
}>;
intents?: {
enabled?: boolean;
match_threshold?: number;
rules?: Array<Record<string, unknown>>;
};
automation?: {
cron?: Array<Record<string, unknown>>;
webhooks?: Array<Record<string, unknown>>;
@@ -62,6 +72,10 @@ interface OperatorPackOptions {
enableMinioSync?: boolean;
}
interface ResearchAgentOptions {
modelTier: 'fast' | 'default' | 'complex' | 'local';
}
export class ConfigBuilder {
private config: SetupConfig;
@@ -157,6 +171,22 @@ export class ConfigBuilder {
this.config.tools = { profile };
}
setResearchAgentEnabled(options: ResearchAgentOptions): void {
const agentConfigs = (this.config.agent_configs ?? {}) as Record<string, Record<string, unknown>>;
const existing = (agentConfigs.research ?? {}) as Record<string, unknown>;
agentConfigs.research = {
...existing,
model_tier: options.modelTier,
tool_profile: 'messaging',
system_prompt: [
'You are a research agent. Find, verify, and synthesize information for the operator.',
'Prefer primary sources and include concrete dates and links when available.',
'Keep output structured and concise.',
].join(' '),
};
this.config.agent_configs = agentConfigs;
}
setWebhooksEnabled(secret?: string): void {
const automation = (this.config.automation ?? {}) as Record<string, unknown>;
if (secret) {
+4 -2
View File
@@ -52,14 +52,16 @@ describe('setupMemory', () => {
});
describe('setupSecurity', () => {
it('enables sandbox and pairing', async () => {
const rl = mockReadline(['y', 'y', '']);
it('enables sandbox, pairing, and research agent', async () => {
const rl = mockReadline(['y', 'y', '', '', '']);
const p = createPrompter(rl);
const builder = new ConfigBuilder();
await setupSecurity(p, builder);
const config = builder.build();
expect(config.sandbox!.enabled).toBe(true);
expect(config.pairing!.enabled).toBe(true);
expect(config.agent_configs?.research?.model_tier).toBe('complex');
expect(config.agent_configs?.research?.tool_profile).toBe('messaging');
});
});
+17
View File
@@ -8,6 +8,13 @@ const TOOL_PROFILES = [
{ label: 'full (unrestricted)', value: 'full' },
];
const RESEARCH_AGENT_TIERS = [
{ label: 'complex (recommended)', value: 'complex' as const },
{ label: 'default', value: 'default' as const },
{ label: 'fast', value: 'fast' as const },
{ label: 'local', value: 'local' as const },
];
export async function setupSecurity(p: Prompter, builder: ConfigBuilder): Promise<void> {
p.println(' Docker sandboxing runs tool commands in isolated containers.');
p.println(' Requires Docker installed and running.');
@@ -34,4 +41,14 @@ export async function setupSecurity(p: Prompter, builder: ConfigBuilder): Promis
p.println(' minimal — status checks only (read-only, safest)');
const profile = await p.choose('Tool policy profile:', TOOL_PROFILES);
builder.setToolProfile(profile);
p.println();
p.println(' Research agent adds a dedicated specialist for deep web research.');
p.println(' Enables /research command and automatic routing for messages starting with "research ..." or "look up ...".');
const enableResearchAgent = await p.confirm('Enable a dedicated research agent?', true);
if (enableResearchAgent) {
const tier = await p.choose('Research agent model tier:', RESEARCH_AGENT_TIERS);
builder.setResearchAgentEnabled({ modelTier: tier });
p.println(`✓ Research agent enabled (tier=${tier})`);
}
}