import { describe, it, expect } from 'vitest'; import { configSchema } from './schema.js'; describe('configSchema — telegram', () => { const minimalConfig = { telegram: { bot_token: 'test', allowed_chat_ids: [1] }, models: { default: { provider: 'anthropic', model: 'claude-3' } }, }; it('defaults telegram require_mention to false', () => { const result = configSchema.parse(minimalConfig); expect(result.telegram?.require_mention).toBe(false); }); }); 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 — server', () => { const minimalConfig = { telegram: { bot_token: 'test', allowed_chat_ids: [1] }, models: { default: { provider: 'anthropic', model: 'claude-3' } }, }; it('defaults max_request_body_bytes', () => { const result = configSchema.parse(minimalConfig); expect(result.server.max_request_body_bytes).toBe(1_048_576); }); it('accepts custom max_request_body_bytes', () => { const result = configSchema.parse({ ...minimalConfig, server: { max_request_body_bytes: 2048 }, }); expect(result.server.max_request_body_bytes).toBe(2048); }); it('defaults ws_rate_limit settings', () => { const result = configSchema.parse(minimalConfig); expect(result.server.ws_rate_limit.enabled).toBe(true); expect(result.server.ws_rate_limit.capacity).toBe(30); expect(result.server.ws_rate_limit.refill_per_sec).toBe(15); expect(result.server.ws_rate_limit.max_violations).toBe(8); expect(result.server.ws_rate_limit.violation_window_ms).toBe(10_000); }); it('accepts custom ws_rate_limit settings', () => { const result = configSchema.parse({ ...minimalConfig, server: { ws_rate_limit: { enabled: true, capacity: 5, refill_per_sec: 2, max_violations: 3, violation_window_ms: 2000, }, }, }); expect(result.server.ws_rate_limit.capacity).toBe(5); expect(result.server.ws_rate_limit.refill_per_sec).toBe(2); expect(result.server.ws_rate_limit.max_violations).toBe(3); expect(result.server.ws_rate_limit.violation_window_ms).toBe(2000); }); it('defaults queue settings', () => { const result = configSchema.parse(minimalConfig); expect(result.server.queue.mode).toBe('collect'); expect(result.server.queue.cap).toBe(50); expect(result.server.queue.overflow).toBe('drop_old'); expect(result.server.queue.debounce_ms).toBe(0); expect(result.server.queue.summarize_overflow).toBe(true); expect(result.server.queue.overrides.channels).toEqual({}); expect(result.server.queue.overrides.sessions).toEqual({}); }); it('accepts custom queue settings', () => { const result = configSchema.parse({ ...minimalConfig, server: { queue: { mode: 'steer_backlog', cap: 10, overflow: 'drop_new', debounce_ms: 250, summarize_overflow: false, }, }, }); expect(result.server.queue.mode).toBe('steer_backlog'); expect(result.server.queue.cap).toBe(10); expect(result.server.queue.overflow).toBe('drop_new'); expect(result.server.queue.debounce_ms).toBe(250); expect(result.server.queue.summarize_overflow).toBe(false); }); it('accepts queue override settings', () => { const result = configSchema.parse({ ...minimalConfig, server: { queue: { overrides: { channels: { ws: { mode: 'collect', cap: 5 }, }, sessions: { 'ws:vip-user': { mode: 'interrupt', overflow: 'drop_new', debounce_ms: 1000, summarize_overflow: false }, }, }, }, }, }); expect(result.server.queue.overrides.channels.ws.mode).toBe('collect'); expect(result.server.queue.overrides.channels.ws.cap).toBe(5); expect(result.server.queue.overrides.sessions['ws:vip-user'].mode).toBe('interrupt'); expect(result.server.queue.overrides.sessions['ws:vip-user'].overflow).toBe('drop_new'); expect(result.server.queue.overrides.sessions['ws:vip-user'].debounce_ms).toBe(1000); expect(result.server.queue.overrides.sessions['ws:vip-user'].summarize_overflow).toBe(false); }); it('defaults discovery settings', () => { const result = configSchema.parse(minimalConfig); expect(result.server.discovery.enabled).toBe(false); expect(result.server.discovery.service_name).toBe('flynn-gateway'); expect(result.server.discovery.service_type).toBe('_flynn._tcp'); expect(result.server.discovery.txt).toEqual({}); }); it('defaults node policy settings', () => { const result = configSchema.parse(minimalConfig); expect(result.server.nodes.enabled).toBe(false); expect(result.server.nodes.allowed_roles).toEqual(['companion']); expect(result.server.nodes.feature_gates).toEqual({}); expect(result.server.nodes.location.enabled).toBe(false); expect(result.server.nodes.push.enabled).toBe(false); }); it('defaults webchat push settings', () => { const result = configSchema.parse(minimalConfig); expect(result.server.webchat_push.enabled).toBe(false); expect(result.server.webchat_push.vapid_public_key).toBeUndefined(); expect(result.server.webchat_push.max_subscriptions).toBe(5000); }); it('accepts custom node policy settings', () => { const result = configSchema.parse({ ...minimalConfig, server: { nodes: { enabled: true, allowed_roles: ['companion', 'observer'], feature_gates: { 'ui.canvas': true, 'fs.sync': false, }, location: { enabled: true, }, push: { enabled: true, }, }, }, }); expect(result.server.nodes.enabled).toBe(true); expect(result.server.nodes.allowed_roles).toEqual(['companion', 'observer']); expect(result.server.nodes.feature_gates['ui.canvas']).toBe(true); expect(result.server.nodes.feature_gates['fs.sync']).toBe(false); expect(result.server.nodes.location.enabled).toBe(true); expect(result.server.nodes.push.enabled).toBe(true); }); it('accepts custom discovery settings', () => { const result = configSchema.parse({ ...minimalConfig, server: { discovery: { enabled: true, service_name: 'flynn-dev', service_type: '_custom._tcp', txt: { env: 'dev', }, }, }, }); expect(result.server.discovery.enabled).toBe(true); expect(result.server.discovery.service_name).toBe('flynn-dev'); expect(result.server.discovery.service_type).toBe('_custom._tcp'); expect(result.server.discovery.txt).toEqual({ env: 'dev' }); }); it('accepts custom webchat push settings', () => { const result = configSchema.parse({ ...minimalConfig, server: { webchat_push: { enabled: true, vapid_public_key: 'BOrSAMPLEPUBLICKEY____', max_subscriptions: 42, }, }, }); expect(result.server.webchat_push.enabled).toBe(true); expect(result.server.webchat_push.vapid_public_key).toBe('BOrSAMPLEPUBLICKEY____'); expect(result.server.webchat_push.max_subscriptions).toBe(42); }); }); describe('configSchema — browser', () => { const minimalConfig = { telegram: { bot_token: 'test', allowed_chat_ids: [1] }, models: { default: { provider: 'anthropic', model: 'claude-3' } }, }; it('defaults browser tools to disabled with safe runtime defaults', () => { const result = configSchema.parse(minimalConfig); expect(result.browser.enabled).toBe(false); expect(result.browser.headless).toBe(true); expect(result.browser.max_pages).toBe(5); expect(result.browser.default_timeout).toBe(30000); }); it('accepts explicit browser config', () => { const result = configSchema.parse({ ...minimalConfig, browser: { enabled: true, executable_path: '/usr/bin/chromium', headless: false, max_pages: 3, default_timeout: 45000, }, }); expect(result.browser.enabled).toBe(true); expect(result.browser.executable_path).toBe('/usr/bin/chromium'); expect(result.browser.headless).toBe(false); expect(result.browser.max_pages).toBe(3); expect(result.browser.default_timeout).toBe(45000); }); }); describe('configSchema — backup', () => { const minimalConfig = { telegram: { bot_token: 'test', allowed_chat_ids: [1] }, models: { default: { provider: 'anthropic', model: 'claude-3' } }, }; it('defaults backup settings', () => { const result = configSchema.parse(minimalConfig); expect(result.backup.enabled).toBe(false); expect(result.backup.schedule).toBeUndefined(); expect(result.backup.interval).toBe('24h'); expect(result.backup.run_on_start).toBe(false); expect(result.backup.notify).toBeUndefined(); expect(result.backup.failure_threshold).toBe(1); expect(result.backup.notify_recovery).toBe(true); expect(result.backup.include_vectors).toBe(true); expect(result.backup.minio.enabled).toBe(false); expect(result.backup.minio.prefix).toBe('flynn'); expect(result.backup.minio.secure).toBe(true); expect(result.backup.minio.mc_path).toBeUndefined(); }); it('accepts custom backup settings', () => { const result = configSchema.parse({ ...minimalConfig, backup: { enabled: true, schedule: '0 2 * * *', interval: '12h', run_on_start: true, notify: { channel: 'telegram', peer: '123' }, failure_threshold: 3, notify_recovery: false, local_dir: '/tmp/flynn-backups', include_vectors: false, minio: { enabled: true, endpoint: 'localhost:9000', access_key: 'key', secret_key: 'secret', bucket: 'flynn-backups', prefix: 'daily', secure: false, mc_path: '/usr/local/bin/mc', }, }, }); expect(result.backup.enabled).toBe(true); expect(result.backup.schedule).toBe('0 2 * * *'); expect(result.backup.interval).toBe('12h'); expect(result.backup.run_on_start).toBe(true); expect(result.backup.notify).toEqual({ channel: 'telegram', peer: '123' }); expect(result.backup.failure_threshold).toBe(3); expect(result.backup.notify_recovery).toBe(false); expect(result.backup.local_dir).toBe('/tmp/flynn-backups'); expect(result.backup.include_vectors).toBe(false); expect(result.backup.minio.enabled).toBe(true); expect(result.backup.minio.endpoint).toBe('localhost:9000'); expect(result.backup.minio.bucket).toBe('flynn-backups'); expect(result.backup.minio.prefix).toBe('daily'); expect(result.backup.minio.secure).toBe(false); expect(result.backup.minio.mc_path).toBe('/usr/local/bin/mc'); }); }); describe('configSchema — sessions', () => { const minimalConfig = { telegram: { bot_token: 'test', allowed_chat_ids: [1] }, models: { default: { provider: 'anthropic', model: 'claude-3' } }, }; it('defaults end_summary settings', () => { const result = configSchema.parse(minimalConfig); expect(result.sessions.ttl).toBe('30d'); expect(result.sessions.end_summary.enabled).toBe(false); expect(result.sessions.end_summary.tier).toBe('fast'); expect(result.sessions.end_summary.max_messages).toBe(50); expect(result.sessions.end_summary.max_input_chars).toBe(20000); expect(result.sessions.end_summary.max_tokens).toBe(512); expect(result.sessions.end_summary.write_to_memory).toBe(true); expect(result.sessions.end_summary.memory_namespace).toBe('session/summaries'); }); it('accepts custom end_summary settings', () => { const result = configSchema.parse({ ...minimalConfig, sessions: { ttl: '7d', end_summary: { enabled: true, tier: 'complex', max_messages: 100, max_input_chars: 50000, max_tokens: 1024, write_to_memory: false, memory_namespace: 'notes/session', }, }, }); expect(result.sessions.ttl).toBe('7d'); expect(result.sessions.end_summary.enabled).toBe(true); expect(result.sessions.end_summary.tier).toBe('complex'); expect(result.sessions.end_summary.max_messages).toBe(100); expect(result.sessions.end_summary.max_input_chars).toBe(50000); expect(result.sessions.end_summary.max_tokens).toBe(1024); expect(result.sessions.end_summary.write_to_memory).toBe(false); expect(result.sessions.end_summary.memory_namespace).toBe('notes/session'); }); }); 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', backend: 'codex', 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.backend).toBe('codex'); expect(result.agent_configs.assistant.tool_profile).toBe('messaging'); expect(result.agent_configs.coder.sandbox).toBe(true); }); }); describe('configSchema — councils', () => { const minimalConfig = { telegram: { bot_token: 'test', allowed_chat_ids: [1] }, models: { default: { provider: 'anthropic', model: 'claude-3' } }, }; it('defaults councils config with deterministic caps and group presets', () => { const result = configSchema.parse(minimalConfig); expect(result.councils.enabled).toBe(false); expect(result.councils.defaults.max_rounds).toBe(2); expect(result.councils.defaults.top_ideas_for_bridge).toBe(3); expect(result.councils.defaults.bridge_packet_max_chars).toBe(2500); expect(result.councils.groups.D.model_tier).toBe('complex'); expect(result.councils.groups.P.model_tier).toBe('complex'); expect(result.councils.meta_model_tier).toBe('complex'); expect(result.councils.groups.D.novelty_bias).toBe('low'); expect(result.councils.groups.P.novelty_bias).toBe('high'); expect(result.councils.meta_arbiter_agent).toBe('council_meta_arbiter'); }); it('accepts explicit councils overrides', () => { const result = configSchema.parse({ ...minimalConfig, councils: { enabled: true, defaults: { max_rounds: 3, ideas_per_round: 5, top_ideas_for_bridge: 2, bridge_packet_max_chars: 1800, bridge_field_max_bullets: 4, bridge_entry_max_chars: 200, novelty_delta_threshold: 5, repetition_threshold: 80, }, strict_grounding: true, strict_meta_validation: true, groups: { D: { arbiter_agent: 'd_arb', freethinker_agent: 'd_ft', model_tier: 'default', group_prompt_prefix: 'd', novelty_bias: 'low', risk_tolerance: 'medium', forbidden_approaches: ['x'], }, P: { arbiter_agent: 'p_arb', freethinker_agent: 'p_ft', model_tier: 'fast', group_prompt_prefix: 'p', novelty_bias: 'high', risk_tolerance: 'high', forbidden_approaches: ['y'], }, }, meta_arbiter_agent: 'meta', meta_model_tier: 'local', }, }); expect(result.councils.enabled).toBe(true); expect(result.councils.defaults.max_rounds).toBe(3); expect(result.councils.groups.D.arbiter_agent).toBe('d_arb'); expect(result.councils.groups.P.freethinker_agent).toBe('p_ft'); expect(result.councils.groups.D.model_tier).toBe('default'); expect(result.councils.meta_model_tier).toBe('local'); expect(result.councils.strict_grounding).toBe(true); expect(result.councils.meta_arbiter_agent).toBe('meta'); }); }); describe('configSchema — backends', () => { const minimalConfig = { telegram: { bot_token: 'test', allowed_chat_ids: [1] }, models: { default: { provider: 'anthropic', model: 'claude-3' } }, }; it('defaults backend config fields', () => { const result = configSchema.parse(minimalConfig); expect(result.backends.claude_code.enabled).toBe(false); expect(result.backends.claude_code.args).toEqual([]); expect(result.backends.claude_code.timeout_ms).toBe(120000); expect(result.backends.opencode.enabled).toBe(false); expect(result.backends.opencode.args).toEqual([]); expect(result.backends.codex.enabled).toBe(false); expect(result.backends.gemini.enabled).toBe(false); expect(result.backends.native.enabled).toBe(true); }); it('accepts explicit codex/gemini backend config', () => { const result = configSchema.parse({ ...minimalConfig, backends: { default: 'codex', codex: { enabled: true, path: '/usr/local/bin/codex', args: ['run'], timeout_ms: 300000 }, gemini: { enabled: true, path: '/usr/local/bin/gemini', args: ['chat'], timeout_ms: 60000 }, }, }); expect(result.backends.default).toBe('codex'); expect(result.backends.codex.enabled).toBe(true); expect(result.backends.codex.path).toBe('/usr/local/bin/codex'); expect(result.backends.codex.args).toEqual(['run']); expect(result.backends.codex.timeout_ms).toBe(300000); expect(result.backends.gemini.enabled).toBe(true); expect(result.backends.gemini.path).toBe('/usr/local/bin/gemini'); expect(result.backends.gemini.args).toEqual(['chat']); expect(result.backends.gemini.timeout_ms).toBe(60000); }); }); 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 — per-tier fallback', () => { const minimalConfig = { telegram: { bot_token: 'test', allowed_chat_ids: [1] }, models: { default: { provider: 'anthropic', model: 'claude-3' } }, }; it('accepts per-tier fallback config', () => { const result = configSchema.parse({ ...minimalConfig, models: { default: { provider: 'anthropic', model: 'claude-sonnet-4-5-20250929', fallback: { provider: 'github', model: 'claude-sonnet-4-5-20250929' }, }, fast: { provider: 'anthropic', model: 'claude-haiku-4-5-20251001', fallback: { provider: 'github', model: 'claude-haiku-4-5-20251001' }, }, }, }); expect(result.models.default.fallback?.provider).toBe('github'); expect(result.models.fast?.fallback?.provider).toBe('github'); }); it('works without fallback field (backward compat)', () => { const result = configSchema.parse(minimalConfig); expect(result.models.default.fallback).toBeUndefined(); }); it('fallback does not itself accept a nested fallback', () => { const result = configSchema.parse({ ...minimalConfig, models: { default: { provider: 'anthropic', model: 'claude-3', fallback: { provider: 'github', model: 'claude-3', fallback: { provider: 'ollama', model: 'llama' }, }, }, }, }); // Zod strips unknown keys from the base schema, so nested fallback is dropped expect((result.models.default.fallback as Record)?.fallback).toBeUndefined(); }); }); describe('configSchema — models auth_mode', () => { const minimalConfig = { telegram: { bot_token: 'test', allowed_chat_ids: [1] }, models: { default: { provider: 'anthropic', model: 'claude-3' } }, }; it('accepts auth_mode values per tier', () => { const result = configSchema.parse({ ...minimalConfig, models: { default: { provider: 'openai', model: 'gpt-4o', auth_mode: 'api_key' }, fast: { provider: 'openai', model: 'gpt-4o-mini', auth_mode: 'oauth' }, }, }); expect(result.models.default.auth_mode).toBe('api_key'); expect(result.models.fast?.auth_mode).toBe('oauth'); }); it('rejects invalid auth_mode values', () => { expect(() => { configSchema.parse({ ...minimalConfig, models: { default: { provider: 'openai', model: 'gpt-4o', auth_mode: 'bogus' }, }, }); }).toThrow(/auth_mode/i); }); it('accepts vercel provider id', () => { const result = configSchema.parse({ ...minimalConfig, models: { default: { provider: 'vercel', model: 'openai/gpt-4.1', endpoint: 'https://ai-gateway.vercel.sh/v1', api_key: 'test-key' }, }, }); expect(result.models.default.provider).toBe('vercel'); }); it('accepts minimax and moonshot provider ids', () => { const minimax = configSchema.parse({ ...minimalConfig, models: { default: { provider: 'minimax', model: 'MiniMax-M1', api_key: 'test-key' }, }, }); expect(minimax.models.default.provider).toBe('minimax'); const moonshot = configSchema.parse({ ...minimalConfig, models: { default: { provider: 'moonshot', model: 'moonshot-v1-8k', api_key: 'test-key' }, }, }); expect(moonshot.models.default.provider).toBe('moonshot'); }); it('accepts multiple api_keys per model tier', () => { const result = configSchema.parse({ ...minimalConfig, models: { default: { provider: 'openai', model: 'gpt-4o', api_keys: ['sk-1', 'sk-2'], }, }, }); expect(result.models.default.api_keys).toEqual(['sk-1', 'sk-2']); }); it('accepts auth_profile_cooldown_ms per model tier', () => { const result = configSchema.parse({ ...minimalConfig, models: { default: { provider: 'openai', model: 'gpt-4o', api_keys: ['sk-1', 'sk-2'], auth_profile_cooldown_ms: 30000, }, }, }); expect(result.models.default.auth_profile_cooldown_ms).toBe(30000); }); it('rejects invalid auth_profile_cooldown_ms values', () => { expect(() => { configSchema.parse({ ...minimalConfig, models: { default: { provider: 'openai', model: 'gpt-4o', auth_profile_cooldown_ms: -1, }, }, }); }).toThrow(/auth_profile_cooldown_ms/i); }); }); describe('configSchema — matrix', () => { const minimalConfig = { telegram: { bot_token: 'test', allowed_chat_ids: [1] }, models: { default: { provider: 'anthropic', model: 'claude-3' } }, }; it('accepts matrix config and defaults sync_timeout_ms', () => { const result = configSchema.parse({ ...minimalConfig, matrix: { homeserver_url: 'https://matrix.example.org', access_token: 'syt_test_token', allowed_room_ids: ['!room1:example.org'], require_mention: true, }, }); expect(result.matrix).toBeDefined(); if (!result.matrix) { throw new Error('Expected matrix config'); } expect(result.matrix.homeserver_url).toBe('https://matrix.example.org'); expect(result.matrix.access_token).toBe('syt_test_token'); expect(result.matrix.sync_timeout_ms).toBe(30000); }); it('matrix config is optional', () => { const result = configSchema.parse(minimalConfig); expect(result.matrix).toBeUndefined(); }); it('rejects invalid homeserver_url', () => { expect(() => { configSchema.parse({ ...minimalConfig, matrix: { homeserver_url: 'not-a-url', access_token: 'token', }, }); }).toThrow(/homeserver/i); }); }); describe('configSchema — signal', () => { const minimalConfig = { telegram: { bot_token: 'test', allowed_chat_ids: [1] }, models: { default: { provider: 'anthropic', model: 'claude-3' } }, }; it('accepts signal config and defaults polling fields', () => { const result = configSchema.parse({ ...minimalConfig, signal: { account: '+15551234567', }, }); expect(result.signal).toBeDefined(); if (!result.signal) { throw new Error('Expected signal config'); } expect(result.signal.account).toBe('+15551234567'); expect(result.signal.signal_cli_path).toBe('signal-cli'); expect(result.signal.poll_interval_ms).toBe(5000); expect(result.signal.send_timeout_ms).toBe(15000); expect(result.signal.require_mention).toBe(true); }); it('signal config is optional', () => { const result = configSchema.parse(minimalConfig); expect(result.signal).toBeUndefined(); }); }); describe('configSchema — audio talk mode', () => { const minimalConfig = { telegram: { bot_token: 'test', allowed_chat_ids: [1] }, models: { default: { provider: 'anthropic', model: 'claude-3' } }, }; it('defaults talk_mode fields', () => { const result = configSchema.parse(minimalConfig); expect(result.audio.talk_mode.enabled).toBe(false); expect(result.audio.talk_mode.wake_phrase).toBe('hey flynn'); expect(result.audio.talk_mode.timeout_ms).toBe(120000); expect(result.audio.talk_mode.allow_manual_toggle).toBe(true); }); it('accepts custom talk_mode settings', () => { const result = configSchema.parse({ ...minimalConfig, audio: { talk_mode: { enabled: true, wake_phrase: 'ok flynn', timeout_ms: 300000, allow_manual_toggle: false, }, }, }); expect(result.audio.talk_mode.enabled).toBe(true); expect(result.audio.talk_mode.wake_phrase).toBe('ok flynn'); expect(result.audio.talk_mode.timeout_ms).toBe(300000); expect(result.audio.talk_mode.allow_manual_toggle).toBe(false); }); }); describe('configSchema — tts', () => { const minimalConfig = { telegram: { bot_token: 'test', allowed_chat_ids: [1] }, models: { default: { provider: 'anthropic', model: 'claude-3' } }, }; it('defaults tts fields', () => { const result = configSchema.parse(minimalConfig); expect(result.tts.enabled).toBe(false); expect(result.tts.enabled_channels).toEqual([]); expect(result.tts.provider).toBeUndefined(); }); it('accepts custom tts provider settings', () => { const result = configSchema.parse({ ...minimalConfig, tts: { enabled: true, enabled_channels: ['telegram', 'discord'], provider: { type: 'custom', endpoint: 'https://example.com/v1/audio/speech', api_key: 'sk-test', model: 'gpt-4o-mini-tts', voice: 'nova', format: 'wav', }, }, }); expect(result.tts.enabled).toBe(true); expect(result.tts.enabled_channels).toEqual(['telegram', 'discord']); expect(result.tts.provider).toMatchObject({ type: 'custom', endpoint: 'https://example.com/v1/audio/speech', api_key: 'sk-test', model: 'gpt-4o-mini-tts', voice: 'nova', format: 'wav', }); }); }); describe('configSchema — mattermost', () => { const minimalConfig = { telegram: { bot_token: 'test', allowed_chat_ids: [1] }, models: { default: { provider: 'anthropic', model: 'claude-3' } }, }; it('accepts mattermost config and defaults optional fields', () => { const result = configSchema.parse({ ...minimalConfig, mattermost: { server_url: 'https://mattermost.example.com', bot_token: 'mm-token', }, }); expect(result.mattermost).toBeDefined(); if (!result.mattermost) { throw new Error('Expected mattermost config'); } expect(result.mattermost.server_url).toBe('https://mattermost.example.com'); expect(result.mattermost.allowed_channel_ids).toEqual([]); expect(result.mattermost.require_mention).toBe(true); expect(result.mattermost.mention_name).toBe('flynn'); expect(result.mattermost.poll_interval_ms).toBe(3000); }); it('mattermost config is optional', () => { const result = configSchema.parse(minimalConfig); expect(result.mattermost).toBeUndefined(); }); }); describe('configSchema — teams', () => { const minimalConfig = { telegram: { bot_token: 'test', allowed_chat_ids: [1] }, models: { default: { provider: 'anthropic', model: 'claude-3' } }, }; it('accepts teams config and defaults allowlist/mention fields', () => { const result = configSchema.parse({ ...minimalConfig, teams: { app_id: 'app-id', app_password: 'app-password', }, }); expect(result.teams).toBeDefined(); if (!result.teams) { throw new Error('Expected teams config'); } expect(result.teams.app_id).toBe('app-id'); expect(result.teams.allowed_conversation_ids).toEqual([]); expect(result.teams.require_mention).toBe(true); }); }); describe('configSchema — google_chat', () => { const minimalConfig = { telegram: { bot_token: 'test', allowed_chat_ids: [1] }, models: { default: { provider: 'anthropic', model: 'claude-3' } }, }; it('accepts google_chat config and defaults filters', () => { const result = configSchema.parse({ ...minimalConfig, google_chat: { service_account_key_file: '/tmp/gchat-service-account.json', }, }); expect(result.google_chat).toBeDefined(); if (!result.google_chat) { throw new Error('Expected google_chat config'); } expect(result.google_chat.service_account_key_file).toBe('/tmp/gchat-service-account.json'); expect(result.google_chat.allowed_space_names).toEqual([]); expect(result.google_chat.require_mention).toBe(true); }); }); describe('configSchema — bluebubbles', () => { const minimalConfig = { telegram: { bot_token: 'test', allowed_chat_ids: [1] }, models: { default: { provider: 'anthropic', model: 'claude-3' } }, }; it('accepts bluebubbles config and defaults optional fields', () => { const result = configSchema.parse({ ...minimalConfig, bluebubbles: { endpoint: 'http://localhost:1234', api_key: 'bb-key', }, }); expect(result.bluebubbles).toBeDefined(); if (!result.bluebubbles) { throw new Error('Expected bluebubbles config'); } expect(result.bluebubbles.allowed_chat_guids).toEqual([]); expect(result.bluebubbles.require_mention).toBe(true); expect(result.bluebubbles.mention_name).toBe('flynn'); }); }); describe('configSchema — line', () => { const minimalConfig = { telegram: { bot_token: 'test', allowed_chat_ids: [1] }, models: { default: { provider: 'anthropic', model: 'claude-3' } }, }; it('accepts line config and defaults optional fields', () => { const result = configSchema.parse({ ...minimalConfig, line: { channel_access_token: 'line-token', channel_secret: 'line-secret', }, }); expect(result.line).toBeDefined(); if (!result.line) { throw new Error('Expected line config'); } expect(result.line.channel_access_token).toBe('line-token'); expect(result.line.channel_secret).toBe('line-secret'); expect(result.line.allowed_source_ids).toEqual([]); expect(result.line.require_mention).toBe(true); expect(result.line.mention_name).toBe('flynn'); expect(result.line.minio.enabled).toBe(false); expect(result.line.minio.prefix).toBe('flynn/channels/line'); expect(result.line.minio.secure).toBe(true); expect(result.line.minio.expires).toBe('24h'); }); it('accepts line-specific minio overrides', () => { const result = configSchema.parse({ ...minimalConfig, line: { channel_access_token: 'line-token', channel_secret: 'line-secret', minio: { enabled: true, endpoint: 'localhost:9000', access_key: 'line-key', secret_key: 'line-secret', bucket: 'line-attachments', prefix: 'line/files', secure: false, expires: '12h', mc_path: '/usr/local/bin/mc', }, }, }); if (!result.line) { throw new Error('Expected line config'); } expect(result.line.minio.enabled).toBe(true); expect(result.line.minio.endpoint).toBe('localhost:9000'); expect(result.line.minio.access_key).toBe('line-key'); expect(result.line.minio.secret_key).toBe('line-secret'); expect(result.line.minio.bucket).toBe('line-attachments'); expect(result.line.minio.prefix).toBe('line/files'); expect(result.line.minio.secure).toBe(false); expect(result.line.minio.expires).toBe('12h'); expect(result.line.minio.mc_path).toBe('/usr/local/bin/mc'); }); }); describe('configSchema — feishu', () => { const minimalConfig = { telegram: { bot_token: 'test', allowed_chat_ids: [1] }, models: { default: { provider: 'anthropic', model: 'claude-3' } }, }; it('accepts feishu config and defaults optional fields', () => { const result = configSchema.parse({ ...minimalConfig, feishu: { app_id: 'cli_a1b2c3', app_secret: 'secret', }, }); expect(result.feishu).toBeDefined(); if (!result.feishu) { throw new Error('Expected feishu config'); } expect(result.feishu.app_id).toBe('cli_a1b2c3'); expect(result.feishu.app_secret).toBe('secret'); expect(result.feishu.allowed_chat_ids).toEqual([]); expect(result.feishu.require_mention).toBe(true); expect(result.feishu.mention_name).toBe('flynn'); }); }); describe('configSchema — zalo', () => { const minimalConfig = { telegram: { bot_token: 'test', allowed_chat_ids: [1] }, models: { default: { provider: 'anthropic', model: 'claude-3' } }, }; it('accepts zalo config and defaults optional fields', () => { const result = configSchema.parse({ ...minimalConfig, zalo: { oa_access_token: 'oa-token', }, }); expect(result.zalo).toBeDefined(); if (!result.zalo) { throw new Error('Expected zalo config'); } expect(result.zalo.oa_access_token).toBe('oa-token'); expect(result.zalo.allowed_user_ids).toEqual([]); expect(result.zalo.require_mention).toBe(true); expect(result.zalo.mention_name).toBe('flynn'); expect(result.zalo.minio.enabled).toBe(false); expect(result.zalo.minio.prefix).toBe('flynn/channels/zalo'); expect(result.zalo.minio.secure).toBe(true); expect(result.zalo.minio.expires).toBe('24h'); }); it('accepts zalo-specific minio overrides', () => { const result = configSchema.parse({ ...minimalConfig, zalo: { oa_access_token: 'oa-token', minio: { enabled: true, endpoint: 'localhost:9000', access_key: 'zalo-key', secret_key: 'zalo-secret', bucket: 'zalo-attachments', prefix: 'zalo/files', secure: false, expires: '8h', mc_path: '/usr/local/bin/mc', }, }, }); if (!result.zalo) { throw new Error('Expected zalo config'); } expect(result.zalo.minio.enabled).toBe(true); expect(result.zalo.minio.endpoint).toBe('localhost:9000'); expect(result.zalo.minio.access_key).toBe('zalo-key'); expect(result.zalo.minio.secret_key).toBe('zalo-secret'); expect(result.zalo.minio.bucket).toBe('zalo-attachments'); expect(result.zalo.minio.prefix).toBe('zalo/files'); expect(result.zalo.minio.secure).toBe(false); expect(result.zalo.minio.expires).toBe('8h'); expect(result.zalo.minio.mc_path).toBe('/usr/local/bin/mc'); }); }); describe('configSchema — whatsapp', () => { const minimalConfig = { telegram: { bot_token: 'test', allowed_chat_ids: [1] }, models: { default: { provider: 'anthropic', model: 'claude-3' } }, }; it('defaults whatsapp no_sandbox to false', () => { const result = configSchema.parse({ ...minimalConfig, whatsapp: {}, }); expect(result.whatsapp?.no_sandbox).toBe(false); }); it('accepts whatsapp no_sandbox override', () => { const result = configSchema.parse({ ...minimalConfig, whatsapp: { no_sandbox: true }, }); expect(result.whatsapp?.no_sandbox).toBe(true); }); }); describe('configSchema — skills watcher', () => { const minimalConfig = { telegram: { bot_token: 'test', allowed_chat_ids: [1] }, models: { default: { provider: 'anthropic', model: 'claude-3' } }, }; it('defaults skills watcher settings', () => { const result = configSchema.parse(minimalConfig); expect(result.skills.load.watch).toBe(false); expect(result.skills.load.watch_debounce_ms).toBe(250); expect(result.skills.installation_execution).toBe('disabled'); expect(result.skills.allow_shell_runner).toBe(false); expect(result.skills.shell_runner_allowlist).toEqual([]); expect(result.skills.shell_runner_governance.owner).toBeUndefined(); expect(result.skills.shell_runner_governance.review_cadence_days).toBe(7); expect(result.skills.shell_runner_governance.promotion_min_success_rate).toBe(0.9); }); it('accepts explicit installation execution policy', () => { const enabled = configSchema.parse({ ...minimalConfig, skills: { installation_execution: 'enabled', allow_shell_runner: true, shell_runner_allowlist: ['npm install*'], shell_runner_governance: { owner: 'skills-team', review_cadence_days: 14, promotion_min_success_rate: 0.95, }, }, }); expect(enabled.skills.installation_execution).toBe('enabled'); expect(enabled.skills.allow_shell_runner).toBe(true); expect(enabled.skills.shell_runner_allowlist).toEqual(['npm install*']); expect(enabled.skills.shell_runner_governance.owner).toBe('skills-team'); expect(enabled.skills.shell_runner_governance.review_cadence_days).toBe(14); expect(enabled.skills.shell_runner_governance.promotion_min_success_rate).toBe(0.95); }); it('accepts explicit watcher settings', () => { const result = configSchema.parse({ ...minimalConfig, skills: { load: { watch: true, watch_debounce_ms: 500, }, }, }); expect(result.skills.load.watch).toBe(true); expect(result.skills.load.watch_debounce_ms).toBe(500); }); }); describe('configSchema — k8s', () => { const baseConfig = { telegram: { bot_token: 'test-token', allowed_chat_ids: [123] }, models: { default: { provider: 'anthropic', model: 'claude-sonnet' } }, }; it('accepts config without k8s section', () => { const result = configSchema.parse(baseConfig); expect(result.k8s).toBeUndefined(); }); it('accepts k8s config with namespace restrictions', () => { const result = configSchema.parse({ ...baseConfig, k8s: { enabled: true, kubectl_path: '/usr/local/bin/kubectl', default_namespace: 'observability', allowed_namespaces: ['observability', 'platform'], }, }); expect(result.k8s?.enabled).toBe(true); expect(result.k8s?.kubectl_path).toBe('/usr/local/bin/kubectl'); expect(result.k8s?.default_namespace).toBe('observability'); expect(result.k8s?.allowed_namespaces).toEqual(['observability', 'platform']); }); }); describe('configSchema automation', () => { const baseConfig = { telegram: { bot_token: 'test-token', allowed_chat_ids: [123] }, models: { default: { provider: 'anthropic', model: 'claude-sonnet' } }, }; it('accepts config without automation section', () => { const result = configSchema.parse(baseConfig); expect(result.automation).toBeDefined(); expect(result.automation.delivery_mode).toBe('shared_session'); expect(result.automation.reactions).toEqual([]); expect(result.automation.cron).toEqual([]); expect(result.automation.daily_briefing.enabled).toBe(false); expect(result.automation.daily_briefing.schedule).toBe('0 8 * * *'); expect(result.automation.daily_briefing.name).toBe('daily-briefing'); expect(result.automation.daily_briefing.dedupe_per_local_day).toBe(true); expect(result.automation.minio_sync.enabled).toBe(false); expect(result.automation.minio_sync.interval).toBe('6h'); expect(result.automation.minio_sync.tasks).toEqual([]); }); it('accepts isolated automation delivery mode', () => { const result = configSchema.parse({ ...baseConfig, automation: { delivery_mode: 'isolated_job', }, }); expect(result.automation.delivery_mode).toBe('isolated_job'); }); it('accepts announce automation delivery mode', () => { const result = configSchema.parse({ ...baseConfig, automation: { delivery_mode: 'announce', }, }); expect(result.automation.delivery_mode).toBe('announce'); }); it('accepts config with cron jobs', () => { const result = configSchema.parse({ ...baseConfig, automation: { cron: [{ name: 'morning-briefing', schedule: '0 9 * * *', message: 'Good morning!', output: { channel: 'telegram', peer: '123' }, }], }, }); expect(result.automation.cron).toHaveLength(1); expect(result.automation.cron[0].name).toBe('morning-briefing'); expect(result.automation.cron[0].enabled).toBe(true); // default }); it('accepts reactions config with filters', () => { const result = configSchema.parse({ ...baseConfig, automation: { reactions: [{ name: 'boss-email', on: ['gmail'], filter: { contains: 'boss@company.com', regex: 'urgent|asap', metadata: { from: 'boss@company.com' }, }, run: 'Summarize and propose next actions:\n\n{{text}}', }], }, }); expect(result.automation.reactions).toHaveLength(1); expect(result.automation.reactions[0]).toMatchObject({ name: 'boss-email', enabled: true, on: ['gmail'], run: 'Summarize and propose next actions:\n\n{{text}}', }); expect(result.automation.reactions[0].filter).toMatchObject({ contains: 'boss@company.com', regex: 'urgent|asap', metadata: { from: 'boss@company.com' }, }); }); it('rejects cron job with empty name', () => { expect(() => configSchema.parse({ ...baseConfig, automation: { cron: [{ name: '', schedule: '0 9 * * *', message: 'test', output: { channel: 'telegram', peer: '123' }, }], }, })).toThrow(); }); it('rejects cron job with empty schedule', () => { expect(() => configSchema.parse({ ...baseConfig, automation: { cron: [{ name: 'test', schedule: '', message: 'test', output: { channel: 'telegram', peer: '123' }, }], }, })).toThrow(); }); it('accepts cron job with optional fields', () => { const result = configSchema.parse({ ...baseConfig, automation: { cron: [{ name: 'test', schedule: '0 9 * * *', message: 'test', output: { channel: 'telegram', peer: '123' }, enabled: false, timezone: 'America/New_York', once_per_local_day: true, }], }, }); expect(result.automation.cron[0].enabled).toBe(false); expect(result.automation.cron[0].timezone).toBe('America/New_York'); expect(result.automation.cron[0].once_per_local_day).toBe(true); }); it('accepts daily briefing automation config', () => { const result = configSchema.parse({ ...baseConfig, automation: { daily_briefing: { enabled: true, name: 'weekday-briefing', schedule: '0 7 * * 1-5', timezone: 'America/New_York', dedupe_per_local_day: false, output: { channel: 'telegram', peer: '123' }, prompt: 'Custom briefing prompt', model_tier: 'fast', }, }, }); expect(result.automation.daily_briefing.enabled).toBe(true); expect(result.automation.daily_briefing.name).toBe('weekday-briefing'); expect(result.automation.daily_briefing.schedule).toBe('0 7 * * 1-5'); expect(result.automation.daily_briefing.timezone).toBe('America/New_York'); expect(result.automation.daily_briefing.dedupe_per_local_day).toBe(false); expect(result.automation.daily_briefing.output).toEqual({ channel: 'telegram', peer: '123' }); expect(result.automation.daily_briefing.prompt).toBe('Custom briefing prompt'); expect(result.automation.daily_briefing.model_tier).toBe('fast'); }); it('defaults heartbeat extended thresholds and checks', () => { const result = configSchema.parse(baseConfig); expect(result.automation.heartbeat.notify_cooldown).toBe('30m'); expect(result.automation.heartbeat.process_memory_threshold_mb).toBe(1500); expect(result.automation.heartbeat.backup_failure_threshold).toBe(1); expect(result.automation.heartbeat.provider_error_rate_threshold).toBe(0.5); expect(result.automation.heartbeat.provider_error_min_calls).toBe(5); expect(result.automation.heartbeat.checks).toContain('process_memory'); expect(result.automation.heartbeat.checks).toContain('backup'); expect(result.automation.heartbeat.checks).toContain('provider_errors'); }); it('accepts scheduled minio sync automation config', () => { const result = configSchema.parse({ ...baseConfig, automation: { minio_sync: { enabled: true, interval: '3h', run_on_start: true, notify_on_success: true, notify: { channel: 'telegram', peer: '123' }, tasks: [{ prefix: 'knowledge/', namespace_base: 'global/knowledge/minio', mode: 'replace', max_objects: 50, max_chars_per_object: 12000, force: true, }], }, }, }); expect(result.automation.minio_sync.enabled).toBe(true); expect(result.automation.minio_sync.interval).toBe('3h'); expect(result.automation.minio_sync.run_on_start).toBe(true); expect(result.automation.minio_sync.notify_on_success).toBe(true); expect(result.automation.minio_sync.notify).toEqual({ channel: 'telegram', peer: '123' }); expect(result.automation.minio_sync.tasks).toHaveLength(1); expect(result.automation.minio_sync.tasks[0]).toMatchObject({ prefix: 'knowledge/', namespace_base: 'global/knowledge/minio', mode: 'replace', max_objects: 50, max_chars_per_object: 12000, force: true, }); }); }); describe('configSchema — intents', () => { const minimalConfig = { telegram: { bot_token: 'test', allowed_chat_ids: [1] }, models: { default: { provider: 'anthropic', model: 'claude-3' } }, }; it('defaults intents to disabled with no rules', () => { const result = configSchema.parse(minimalConfig); expect(result.intents.enabled).toBe(false); expect(result.intents.rules).toEqual([]); }); it('accepts intent rule config', () => { const result = configSchema.parse({ ...minimalConfig, intents: { enabled: true, match_threshold: 0.6, rules: [ { name: 'deploy-rule', patterns: ['deploy *'], target: { type: 'agent', name: 'coder' }, priority: 5, enabled: true, }, ], }, }); expect(result.intents.enabled).toBe(true); expect(result.intents.rules[0].target.type).toBe('agent'); expect(result.intents.rules[0].target.name).toBe('coder'); }); }); describe('configSchema — routing_policy', () => { const minimalConfig = { telegram: { bot_token: 'test', allowed_chat_ids: [1] }, models: { default: { provider: 'anthropic', model: 'claude-3' } }, }; it('defaults routing_policy values', () => { const result = configSchema.parse(minimalConfig); expect(result.routing_policy.enabled).toBe(false); expect(result.routing_policy.fast_path_threshold).toBe(0.85); expect(result.routing_policy.llm_threshold).toBe(0.5); expect(result.routing_policy.default_path).toBe('llm'); }); }); describe('configSchema — history_index', () => { const minimalConfig = { telegram: { bot_token: 'test', allowed_chat_ids: [1] }, models: { default: { provider: 'anthropic', model: 'claude-3' } }, }; it('defaults history indexing config', () => { const result = configSchema.parse(minimalConfig); expect(result.history_index.enabled).toBe(false); expect(result.history_index.max_keywords).toBe(8); expect(result.history_index.search_limit).toBe(10); }); }); describe('configSchema — memory injection strategy', () => { const minimalConfig = { telegram: { bot_token: 'test', allowed_chat_ids: [1] }, models: { default: { provider: 'anthropic', model: 'claude-3' } }, }; it('defaults memory injection settings', () => { const result = configSchema.parse(minimalConfig); expect(result.memory.injection_strategy).toBe('all'); expect(result.memory.max_injection_tokens).toBe(2000); expect(result.memory.proactive_extract.enabled).toBe(false); expect(result.memory.proactive_extract.min_tool_calls).toBe(1); expect(result.memory.proactive_extract.namespace).toBe('global'); expect(result.memory.daily_log.enabled).toBe(false); expect(result.memory.daily_log.namespace_prefix).toBe('daily'); expect(result.memory.daily_log.include_session_metadata).toBe(true); expect(result.memory.daily_log.max_user_chars).toBe(2000); expect(result.memory.daily_log.max_assistant_chars).toBe(4000); expect(result.memory.qmd.enabled).toBe(false); expect(result.memory.qmd.top_k).toBe(8); expect(result.memory.qmd.min_score).toBe(0.15); }); it('accepts adaptive memory injection settings', () => { const result = configSchema.parse({ ...minimalConfig, memory: { injection_strategy: 'adaptive', max_injection_tokens: 1200, }, }); expect(result.memory.injection_strategy).toBe('adaptive'); expect(result.memory.max_injection_tokens).toBe(1200); }); it('accepts qmd backend settings', () => { const result = configSchema.parse({ ...minimalConfig, memory: { qmd: { enabled: true, top_k: 12, min_score: 0.2, }, }, }); expect(result.memory.qmd.enabled).toBe(true); expect(result.memory.qmd.top_k).toBe(12); expect(result.memory.qmd.min_score).toBe(0.2); }); it('accepts proactive extraction and daily log settings', () => { const result = configSchema.parse({ ...minimalConfig, memory: { proactive_extract: { enabled: true, min_tool_calls: 3, namespace: 'global/facts', }, daily_log: { enabled: true, namespace_prefix: 'memory', include_session_metadata: false, max_user_chars: 1200, max_assistant_chars: 2400, }, }, }); expect(result.memory.proactive_extract.enabled).toBe(true); expect(result.memory.proactive_extract.min_tool_calls).toBe(3); expect(result.memory.proactive_extract.namespace).toBe('global/facts'); expect(result.memory.daily_log.enabled).toBe(true); expect(result.memory.daily_log.namespace_prefix).toBe('memory'); expect(result.memory.daily_log.include_session_metadata).toBe(false); expect(result.memory.daily_log.max_user_chars).toBe(1200); expect(result.memory.daily_log.max_assistant_chars).toBe(2400); }); }); describe('configSchema — compaction importance threshold', () => { const minimalConfig = { telegram: { bot_token: 'test', allowed_chat_ids: [1] }, models: { default: { provider: 'anthropic', model: 'claude-3' } }, }; it('defaults compaction importance threshold to disabled behavior', () => { const result = configSchema.parse(minimalConfig); expect(result.compaction.importance_threshold).toBe(1); expect(result.compaction.proactive.enabled).toBe(false); expect(result.compaction.proactive.warn_pct).toBe(75); expect(result.compaction.proactive.checkpoint_pct).toBe(85); expect(result.compaction.proactive.auto_compact_pct).toBe(95); expect(result.compaction.proactive.checkpoint_cooldown_ms).toBe(300000); expect(result.compaction.proactive.memory_namespace).toBe('session/checkpoints'); }); it('accepts a custom importance threshold', () => { const result = configSchema.parse({ ...minimalConfig, compaction: { importance_threshold: 0.5, proactive: { enabled: true, warn_pct: 70, checkpoint_pct: 82, auto_compact_pct: 93, checkpoint_cooldown_ms: 120000, memory_namespace: 'notes/checkpoints', }, }, }); expect(result.compaction.importance_threshold).toBe(0.5); expect(result.compaction.proactive.enabled).toBe(true); expect(result.compaction.proactive.warn_pct).toBe(70); expect(result.compaction.proactive.checkpoint_pct).toBe(82); expect(result.compaction.proactive.auto_compact_pct).toBe(93); expect(result.compaction.proactive.checkpoint_cooldown_ms).toBe(120000); expect(result.compaction.proactive.memory_namespace).toBe('notes/checkpoints'); }); }); describe('configSchema — prompt context level', () => { const minimalConfig = { telegram: { bot_token: 'test', allowed_chat_ids: [1] }, models: { default: { provider: 'anthropic', model: 'claude-3' } }, }; it('defaults prompt.context_level to normal', () => { const result = configSchema.parse(minimalConfig); expect(result.prompt.context_level).toBe('normal'); }); it('accepts valid context levels', () => { const result = configSchema.parse({ ...minimalConfig, prompt: { context_level: 'debug', }, }); expect(result.prompt.context_level).toBe('debug'); }); it('rejects invalid context levels', () => { expect(() => configSchema.parse({ ...minimalConfig, prompt: { context_level: 'verbose', }, })).toThrow(); }); }); describe('configSchema — agents truthfulness/autonomy', () => { const minimalConfig = { telegram: { bot_token: 'test', allowed_chat_ids: [1] }, models: { default: { provider: 'anthropic', model: 'claude-3' } }, }; it('defaults to standard truthfulness and autonomy', () => { const result = configSchema.parse(minimalConfig); expect(result.agents.truthfulness_mode).toBe('standard'); expect(result.agents.autonomy_level).toBe('standard'); expect(result.agents.sensitive_mode).toBe('confirm_without_elevation'); expect(result.agents.immutable_denylist).toEqual( expect.arrayContaining([ expect.objectContaining({ tool: 'shell.exec', args_pattern: 'git push origin main' }), expect.objectContaining({ tool: 'shell.exec', args_pattern: 'git reset --hard' }), ]), ); }); it('accepts explicit truthfulness and autonomy modes', () => { const result = configSchema.parse({ ...minimalConfig, agents: { truthfulness_mode: 'strict', autonomy_level: 'conservative', sensitive_mode: 'confirm_without_elevation', immutable_denylist: [ { tool: 'shell.exec', args_pattern: 'rm -rf /', reason: 'too destructive' }, ], }, }); expect(result.agents.truthfulness_mode).toBe('strict'); expect(result.agents.autonomy_level).toBe('conservative'); expect(result.agents.sensitive_mode).toBe('confirm_without_elevation'); expect(result.agents.immutable_denylist).toEqual([ { tool: 'shell.exec', args_pattern: 'rm -rf /', reason: 'too destructive' }, ]); }); it('rejects invalid truthfulness_mode', () => { expect(() => configSchema.parse({ ...minimalConfig, agents: { truthfulness_mode: 'always', }, })).toThrow(); }); it('rejects invalid autonomy_level', () => { expect(() => configSchema.parse({ ...minimalConfig, agents: { autonomy_level: 'manual', }, })).toThrow(); }); it('rejects invalid sensitive_mode', () => { expect(() => configSchema.parse({ ...minimalConfig, agents: { sensitive_mode: 'allow_everything', }, })).toThrow(); }); }); describe('configSchema — skills registry source', () => { const minimalConfig = { telegram: { bot_token: 'test', allowed_chat_ids: [1] }, models: { default: { provider: 'anthropic', model: 'claude-3' } }, }; it('accepts skills.registry_source when provided', () => { const result = configSchema.parse({ ...minimalConfig, skills: { registry_source: 'https://registry.example/catalog.json', }, }); expect(result.skills.registry_source).toBe('https://registry.example/catalog.json'); }); });