feat: add sandbox, agent_configs, and routing config schemas

This commit is contained in:
William Valentin
2026-02-06 15:48:55 -08:00
parent e4eea764e7
commit daf8cac3fe
3 changed files with 127 additions and 1 deletions
+1 -1
View File
@@ -1,2 +1,2 @@
export { loadConfig } from './loader.js';
export { configSchema, type Config, type TelegramConfig, type ModelConfig, type CronJobConfig, type AgentsConfig, type CompactionConfig, type ToolProfile, type ToolOverrideConfig, type ToolsConfig } from './schema.js';
export { configSchema, type Config, type TelegramConfig, type ModelConfig, type CronJobConfig, type AgentsConfig, type CompactionConfig, type ToolProfile, type ToolOverrideConfig, type ToolsConfig, type SandboxConfig, type AgentConfigEntry, type RoutingConfig } from './schema.js';
+88
View File
@@ -1,6 +1,94 @@
import { describe, it, expect } from 'vitest';
import { configSchema } from './schema.js';
describe('configSchema — sandbox', () => {
const minimalConfig = {
telegram: { bot_token: 'test', allowed_chat_ids: [1] },
models: { default: { provider: 'anthropic', model: 'claude-3' } },
};
it('defaults sandbox to disabled', () => {
const result = configSchema.parse(minimalConfig);
expect(result.sandbox.enabled).toBe(false);
expect(result.sandbox.image).toBe('node:22-slim');
expect(result.sandbox.network).toBe('none');
expect(result.sandbox.memory_limit).toBe('512m');
expect(result.sandbox.cpu_limit).toBe('1.0');
expect(result.sandbox.timeout_seconds).toBe(300);
});
it('accepts sandbox config', () => {
const result = configSchema.parse({
...minimalConfig,
sandbox: { enabled: true, image: 'ubuntu:24.04', network: 'bridge' },
});
expect(result.sandbox.enabled).toBe(true);
expect(result.sandbox.image).toBe('ubuntu:24.04');
expect(result.sandbox.network).toBe('bridge');
});
});
describe('configSchema — agent_configs', () => {
const minimalConfig = {
telegram: { bot_token: 'test', allowed_chat_ids: [1] },
models: { default: { provider: 'anthropic', model: 'claude-3' } },
};
it('defaults agent_configs to empty', () => {
const result = configSchema.parse(minimalConfig);
expect(result.agent_configs).toEqual({});
});
it('accepts named agent configs', () => {
const result = configSchema.parse({
...minimalConfig,
agent_configs: {
assistant: {
system_prompt: 'You are helpful.',
model_tier: 'default',
tool_profile: 'messaging',
},
coder: {
model_tier: 'complex',
tool_profile: 'coding',
sandbox: true,
},
},
});
expect(result.agent_configs.assistant.system_prompt).toBe('You are helpful.');
expect(result.agent_configs.assistant.tool_profile).toBe('messaging');
expect(result.agent_configs.coder.sandbox).toBe(true);
});
});
describe('configSchema — routing', () => {
const minimalConfig = {
telegram: { bot_token: 'test', allowed_chat_ids: [1] },
models: { default: { provider: 'anthropic', model: 'claude-3' } },
};
it('defaults routing to empty', () => {
const result = configSchema.parse(minimalConfig);
expect(result.routing.default_agent).toBeUndefined();
expect(result.routing.channels).toEqual({});
expect(result.routing.senders).toEqual({});
});
it('accepts routing config', () => {
const result = configSchema.parse({
...minimalConfig,
routing: {
default_agent: 'assistant',
channels: { discord: 'coder' },
senders: { 'telegram:12345': 'coder' },
},
});
expect(result.routing.default_agent).toBe('assistant');
expect(result.routing.channels.discord).toBe('coder');
expect(result.routing.senders['telegram:12345']).toBe('coder');
});
});
describe('configSchema automation', () => {
const baseConfig = {
telegram: { bot_token: 'test-token', allowed_chat_ids: [123] },
+38
View File
@@ -179,6 +179,38 @@ const toolsSchema = z.object({
providers: z.record(z.string(), toolOverrideSchema).default({}),
}).default({});
// ── Sandbox schemas ───────────────────────────────────────────────────
const sandboxSchema = z.object({
enabled: z.boolean().default(false),
image: z.string().default('node:22-slim'),
workspace_dir: z.string().default('/workspace'),
network: z.enum(['none', 'bridge', 'host']).default('none'),
memory_limit: z.string().default('512m'),
cpu_limit: z.string().default('1.0'),
timeout_seconds: z.number().min(10).max(3600).default(300),
}).default({});
// ── Agent config + routing schemas ────────────────────────────────────
const modelTierEnum = z.enum(['fast', 'default', 'complex', 'local']);
const agentConfigEntrySchema = z.object({
system_prompt: z.string().optional(),
model_tier: modelTierEnum.optional(),
tool_profile: toolProfileEnum.optional(),
tool_overrides: toolOverrideSchema.optional(),
sandbox: z.boolean().default(false),
});
const agentConfigsSchema = z.record(z.string(), agentConfigEntrySchema).default({});
const routingSchema = z.object({
default_agent: z.string().optional(),
channels: z.record(z.string(), z.string()).default({}),
senders: z.record(z.string(), z.string()).default({}),
}).default({});
const promptSchema = z.object({
/** Additional directories to search for prompt template files. */
search_dirs: z.array(z.string()).default([]),
@@ -209,6 +241,9 @@ export const configSchema = z.object({
web_search: webSearchSchema,
prompt: promptSchema,
tools: toolsSchema,
sandbox: sandboxSchema,
agent_configs: agentConfigsSchema,
routing: routingSchema,
});
export type Config = z.infer<typeof configSchema>;
@@ -228,3 +263,6 @@ export type PromptConfig = z.infer<typeof promptSchema>;
export type ToolProfile = z.infer<typeof toolProfileEnum>;
export type ToolOverrideConfig = z.infer<typeof toolOverrideSchema>;
export type ToolsConfig = z.infer<typeof toolsSchema>;
export type SandboxConfig = z.infer<typeof sandboxSchema>;
export type AgentConfigEntry = z.infer<typeof agentConfigEntrySchema>;
export type RoutingConfig = z.infer<typeof routingSchema>;