feat(config): add councils schema and defaults
This commit is contained in:
@@ -590,6 +590,47 @@ automation:
|
||||
# Be concise and match the operator's tone. Skip marketing emails.
|
||||
# Never send messages without explicit instruction — draft only.
|
||||
|
||||
# ── Councils Pipeline ────────────────────────────────────────────────
|
||||
# Deterministic dual-council orchestration (D/P groups) with bridge-only
|
||||
# exchange and a deterministic meta merge. Requires matching agent_configs
|
||||
# for the configured role names below.
|
||||
#
|
||||
# councils:
|
||||
# enabled: false
|
||||
# defaults:
|
||||
# max_rounds: 2
|
||||
# ideas_per_round: 6
|
||||
# top_ideas_for_bridge: 3
|
||||
# bridge_packet_max_chars: 2500
|
||||
# bridge_field_max_bullets: 6
|
||||
# bridge_entry_max_chars: 300
|
||||
# novelty_delta_threshold: 10
|
||||
# repetition_threshold: 70
|
||||
# strict_grounding: false
|
||||
# strict_meta_validation: true
|
||||
# groups:
|
||||
# D:
|
||||
# arbiter_agent: council_d_arbiter
|
||||
# freethinker_agent: council_d_freethinker
|
||||
# group_prompt_prefix: Optimize for feasibility and speed-to-test. Prefer boring-but-true.
|
||||
# novelty_bias: low
|
||||
# risk_tolerance: low
|
||||
# forbidden_approaches:
|
||||
# - moonshots
|
||||
# - handwavy AI claims
|
||||
# - unverified assumptions
|
||||
# P:
|
||||
# arbiter_agent: council_p_arbiter
|
||||
# freethinker_agent: council_p_freethinker
|
||||
# group_prompt_prefix: Optimize for reframing and non-obvious leverage. Weird is fine; label speculation.
|
||||
# novelty_bias: high
|
||||
# risk_tolerance: high
|
||||
# forbidden_approaches:
|
||||
# - incremental tweaks
|
||||
# - obvious best practices
|
||||
# - purely conventional solutions
|
||||
# meta_arbiter_agent: council_meta_arbiter
|
||||
|
||||
# Optional: explicit intent rules for agent routing.
|
||||
# If enabled, these rules are evaluated before default sender/channel routing.
|
||||
# The research agent can already be auto-routed by prefix (`research ...`, `look up ...`)
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
export { loadConfig, deepMerge } from './loader.js';
|
||||
export { persistConfig } from './persistence.js';
|
||||
export { configSchema, MODEL_PROVIDERS, type ModelProvider, type Config, type TelegramConfig, type ModelConfig, type CronJobConfig, type AgentsConfig, type CompactionConfig, type ToolProfile, type ToolOverrideConfig, type ToolsConfig, type SandboxConfig, type AgentConfigEntry, type RoutingConfig, type ServerConfig, type BackupConfig, type K8sConfig, type TtsConfig } from './schema.js';
|
||||
export { configSchema, MODEL_PROVIDERS, type ModelProvider, type Config, type TelegramConfig, type ModelConfig, type CronJobConfig, type AgentsConfig, type CompactionConfig, type ToolProfile, type ToolOverrideConfig, type ToolsConfig, type SandboxConfig, type AgentConfigEntry, type CouncilsConfig, type RoutingConfig, type ServerConfig, type BackupConfig, type K8sConfig, type TtsConfig } from './schema.js';
|
||||
|
||||
@@ -412,6 +412,71 @@ describe('configSchema — agent_configs', () => {
|
||||
});
|
||||
});
|
||||
|
||||
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.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',
|
||||
group_prompt_prefix: 'd',
|
||||
novelty_bias: 'low',
|
||||
risk_tolerance: 'medium',
|
||||
forbidden_approaches: ['x'],
|
||||
},
|
||||
P: {
|
||||
arbiter_agent: 'p_arb',
|
||||
freethinker_agent: 'p_ft',
|
||||
group_prompt_prefix: 'p',
|
||||
novelty_bias: 'high',
|
||||
risk_tolerance: 'high',
|
||||
forbidden_approaches: ['y'],
|
||||
},
|
||||
},
|
||||
meta_arbiter_agent: 'meta',
|
||||
},
|
||||
});
|
||||
|
||||
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.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] },
|
||||
|
||||
@@ -860,6 +860,58 @@ const agentConfigEntrySchema = z.object({
|
||||
|
||||
const agentConfigsSchema = z.record(z.string(), agentConfigEntrySchema).default({});
|
||||
|
||||
const councilsGroupConfigSchema = z.object({
|
||||
arbiter_agent: z.string().min(1),
|
||||
freethinker_agent: z.string().min(1),
|
||||
group_prompt_prefix: z.string().min(1),
|
||||
novelty_bias: z.enum(['low', 'medium', 'high']).default('medium'),
|
||||
risk_tolerance: z.enum(['low', 'medium', 'high']).default('medium'),
|
||||
forbidden_approaches: z.array(z.string().min(1)).default([]),
|
||||
});
|
||||
|
||||
const councilsSchema = z.object({
|
||||
enabled: z.boolean().default(false),
|
||||
defaults: z.object({
|
||||
max_rounds: z.number().int().min(1).max(6).default(2),
|
||||
ideas_per_round: z.number().int().min(1).max(20).default(6),
|
||||
top_ideas_for_bridge: z.number().int().min(1).max(10).default(3),
|
||||
bridge_packet_max_chars: z.number().int().min(500).max(20_000).default(2500),
|
||||
bridge_field_max_bullets: z.number().int().min(1).max(20).default(6),
|
||||
bridge_entry_max_chars: z.number().int().min(20).max(2000).default(300),
|
||||
novelty_delta_threshold: z.number().int().min(0).max(100).default(10),
|
||||
repetition_threshold: z.number().int().min(0).max(100).default(70),
|
||||
}).default({}),
|
||||
strict_grounding: z.boolean().default(false),
|
||||
strict_meta_validation: z.boolean().default(true),
|
||||
groups: z.object({
|
||||
D: councilsGroupConfigSchema.default({
|
||||
arbiter_agent: 'council_d_arbiter',
|
||||
freethinker_agent: 'council_d_freethinker',
|
||||
group_prompt_prefix: 'Optimize for feasibility and speed-to-test. Prefer boring-but-true.',
|
||||
novelty_bias: 'low',
|
||||
risk_tolerance: 'low',
|
||||
forbidden_approaches: [
|
||||
'moonshots',
|
||||
'handwavy AI claims',
|
||||
'unverified assumptions',
|
||||
],
|
||||
}),
|
||||
P: councilsGroupConfigSchema.default({
|
||||
arbiter_agent: 'council_p_arbiter',
|
||||
freethinker_agent: 'council_p_freethinker',
|
||||
group_prompt_prefix: 'Optimize for reframing and non-obvious leverage. Weird is fine; label speculation.',
|
||||
novelty_bias: 'high',
|
||||
risk_tolerance: 'high',
|
||||
forbidden_approaches: [
|
||||
'incremental tweaks',
|
||||
'obvious best practices',
|
||||
'purely conventional solutions',
|
||||
],
|
||||
}),
|
||||
}).default({}),
|
||||
meta_arbiter_agent: z.string().min(1).default('council_meta_arbiter'),
|
||||
}).default({});
|
||||
|
||||
const routingSchema = z.object({
|
||||
default_agent: z.string().optional(),
|
||||
channels: z.record(z.string(), z.string()).default({}),
|
||||
@@ -1005,6 +1057,7 @@ export const configSchema = z.object({
|
||||
tools: toolsSchema,
|
||||
sandbox: sandboxSchema,
|
||||
agent_configs: agentConfigsSchema,
|
||||
councils: councilsSchema,
|
||||
routing: routingSchema,
|
||||
intents: intentsSchema,
|
||||
routing_policy: routingPolicySchema,
|
||||
@@ -1046,6 +1099,7 @@ 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 CouncilsConfig = z.infer<typeof councilsSchema>;
|
||||
export type RoutingConfig = z.infer<typeof routingSchema>;
|
||||
export type IntentTargetType = z.infer<typeof intentTargetTypeSchema>;
|
||||
export type IntentRuleConfig = z.infer<typeof intentRuleSchema>;
|
||||
|
||||
Reference in New Issue
Block a user