From 182827d6125d1e89c96bcd924c75a525afed0d2e Mon Sep 17 00:00:00 2001 From: William Valentin Date: Wed, 18 Feb 2026 12:15:37 -0800 Subject: [PATCH] feat(config): support assistant briefing runtime edits and setup defaults --- src/cli/setup/automation.test.ts | 11 +++++++ src/cli/setup/config.test.ts | 10 ++++++ src/cli/setup/config.ts | 15 +++++++++ src/gateway/handlers/config.ts | 44 +++++++++++++++++++++++++++ src/gateway/handlers/handlers.test.ts | 11 +++++++ 5 files changed, 91 insertions(+) diff --git a/src/cli/setup/automation.test.ts b/src/cli/setup/automation.test.ts index f1c9519..17d8b5e 100644 --- a/src/cli/setup/automation.test.ts +++ b/src/cli/setup/automation.test.ts @@ -68,6 +68,16 @@ describe('setupAutomation', () => { expect(briefing.enabled).toBe(true); expect(briefing.schedule).toBe('0 7 * * 1-5'); expect(minioSync.enabled).toBe(true); + expect(automation.delivery_mode).toBe('announce'); + expect((config.memory as Record).daily_log).toEqual({ + enabled: true, + namespace_prefix: 'daily', + }); + expect((config.memory as Record).proactive_extract).toEqual({ + enabled: true, + min_tool_calls: 1, + namespace: 'global', + }); }); it('allows overriding operator pack output routing', async () => { @@ -102,6 +112,7 @@ describe('setupAutomation', () => { expect((heartbeat.notify as Record).channel).toBe('discord'); expect((briefing.output as Record).peer).toBe('ops-room'); expect(automation.minio_sync).toBeUndefined(); + expect(automation.delivery_mode).toBe('announce'); }); it('leaves operator pack disabled when not selected', async () => { diff --git a/src/cli/setup/config.test.ts b/src/cli/setup/config.test.ts index 3d78676..49c9700 100644 --- a/src/cli/setup/config.test.ts +++ b/src/cli/setup/config.test.ts @@ -116,5 +116,15 @@ describe('ConfigBuilder', () => { expect((obj.automation as Record)?.heartbeat).toBeDefined(); expect((obj.automation as Record)?.daily_briefing).toBeDefined(); expect((obj.automation as Record)?.minio_sync).toBeDefined(); + expect((obj.automation as Record)?.delivery_mode).toBe('announce'); + expect((obj.memory as Record)?.daily_log).toEqual({ + enabled: true, + namespace_prefix: 'daily', + }); + expect((obj.memory as Record)?.proactive_extract).toEqual({ + enabled: true, + min_tool_calls: 1, + namespace: 'global', + }); }); }); diff --git a/src/cli/setup/config.ts b/src/cli/setup/config.ts index 096953b..3006749 100644 --- a/src/cli/setup/config.ts +++ b/src/cli/setup/config.ts @@ -257,12 +257,16 @@ export class ConfigBuilder { applyOperatorPack(options: OperatorPackOptions): void { const automation = (this.config.automation ?? {}) as Record; const backup = (this.config.backup ?? {}) as Record; + const memory = (this.config.memory ?? {}) as Record; backup.enabled = true; backup.schedule = options.backupSchedule; backup.run_on_start = true; backup.notify = { channel: options.outputChannel, peer: options.outputPeer }; + // Personal-assistant mode defaults: proactive push and continuity-first memory cadence. + automation.delivery_mode = 'announce'; + automation.heartbeat = { enabled: true, notify: { channel: options.outputChannel, peer: options.outputPeer }, @@ -298,8 +302,19 @@ export class ConfigBuilder { }; } + memory.daily_log = { + enabled: true, + namespace_prefix: 'daily', + }; + memory.proactive_extract = { + enabled: true, + min_tool_calls: 1, + namespace: 'global', + }; + this.config.automation = automation; this.config.backup = backup; + this.config.memory = memory; } build(): SetupConfig { diff --git a/src/gateway/handlers/config.ts b/src/gateway/handlers/config.ts index 181627f..981fbe5 100644 --- a/src/gateway/handlers/config.ts +++ b/src/gateway/handlers/config.ts @@ -176,6 +176,50 @@ const PATCHABLE_KEYS: Record boolean config.automation.daily_briefing.enabled = value; return true; }, + 'automation.daily_briefing.output.channel': (config, value) => { + if (typeof value !== 'string' || value.trim().length === 0) {return false;} + config.automation ??= {} as Config['automation']; + config.automation.daily_briefing ??= {} as Config['automation']['daily_briefing']; + config.automation.daily_briefing.output ??= { channel: 'webchat', peer: 'assistant' }; + config.automation.daily_briefing.output.channel = value.trim(); + return true; + }, + 'automation.daily_briefing.output.peer': (config, value) => { + if (typeof value !== 'string' || value.trim().length === 0) {return false;} + config.automation ??= {} as Config['automation']; + config.automation.daily_briefing ??= {} as Config['automation']['daily_briefing']; + config.automation.daily_briefing.output ??= { channel: 'webchat', peer: 'assistant' }; + config.automation.daily_briefing.output.peer = value.trim(); + return true; + }, + 'automation.daily_briefing.prompt': (config, value) => { + if (typeof value !== 'string' || value.trim().length === 0) {return false;} + config.automation ??= {} as Config['automation']; + config.automation.daily_briefing ??= {} as Config['automation']['daily_briefing']; + config.automation.daily_briefing.prompt = value; + return true; + }, + 'automation.daily_briefing.schedule': (config, value) => { + if (typeof value !== 'string' || value.trim().length === 0) {return false;} + config.automation ??= {} as Config['automation']; + config.automation.daily_briefing ??= {} as Config['automation']['daily_briefing']; + config.automation.daily_briefing.schedule = value.trim(); + return true; + }, + 'automation.daily_briefing.timezone': (config, value) => { + if (typeof value !== 'string' || value.trim().length === 0) {return false;} + config.automation ??= {} as Config['automation']; + config.automation.daily_briefing ??= {} as Config['automation']['daily_briefing']; + config.automation.daily_briefing.timezone = value.trim(); + return true; + }, + 'automation.daily_briefing.model_tier': (config, value) => { + if (value !== 'fast' && value !== 'default' && value !== 'complex' && value !== 'local') {return false;} + config.automation ??= {} as Config['automation']; + config.automation.daily_briefing ??= {} as Config['automation']['daily_briefing']; + config.automation.daily_briefing.model_tier = value; + return true; + }, 'memory.daily_log.enabled': (config, value) => { if (typeof value !== 'boolean') {return false;} config.memory ??= {} as Config['memory']; diff --git a/src/gateway/handlers/handlers.test.ts b/src/gateway/handlers/handlers.test.ts index e93e252..adc76cb 100644 --- a/src/gateway/handlers/handlers.test.ts +++ b/src/gateway/handlers/handlers.test.ts @@ -1145,6 +1145,9 @@ describe('config handlers', () => { 'server.nodes.push.enabled': true, 'automation.delivery_mode': 'announce', 'automation.daily_briefing.enabled': true, + 'automation.daily_briefing.output.channel': 'telegram', + 'automation.daily_briefing.output.peer': '12345', + 'automation.daily_briefing.model_tier': 'fast', 'memory.daily_log.enabled': true, 'memory.proactive_extract.enabled': true, 'memory.proactive_extract.min_tool_calls': 2, @@ -1165,6 +1168,9 @@ describe('config handlers', () => { 'server.nodes.push.enabled', 'automation.delivery_mode', 'automation.daily_briefing.enabled', + 'automation.daily_briefing.output.channel', + 'automation.daily_briefing.output.peer', + 'automation.daily_briefing.model_tier', 'memory.daily_log.enabled', 'memory.proactive_extract.enabled', 'memory.proactive_extract.min_tool_calls', @@ -1182,6 +1188,9 @@ describe('config handlers', () => { expect(config.server.nodes.push.enabled).toBe(true); expect(getPath(config, 'automation', 'delivery_mode')).toBe('announce'); expect(getPath(config, 'automation', 'daily_briefing', 'enabled')).toBe(true); + expect(getPath(config, 'automation', 'daily_briefing', 'output', 'channel')).toBe('telegram'); + expect(getPath(config, 'automation', 'daily_briefing', 'output', 'peer')).toBe('12345'); + expect(getPath(config, 'automation', 'daily_briefing', 'model_tier')).toBe('fast'); expect(getPath(config, 'memory', 'daily_log', 'enabled')).toBe(true); expect(getPath(config, 'memory', 'proactive_extract', 'enabled')).toBe(true); expect(getPath(config, 'memory', 'proactive_extract', 'min_tool_calls')).toBe(2); @@ -1222,6 +1231,7 @@ describe('config handlers', () => { 'server.queue.cap': 0, 'memory.proactive_extract.min_tool_calls': 99, 'tts.enabled_channels': [1, 2, 3], + 'automation.daily_briefing.model_tier': 'ultra', }, }, }; @@ -1234,6 +1244,7 @@ describe('config handlers', () => { 'server.queue.cap', 'memory.proactive_extract.min_tool_calls', 'tts.enabled_channels', + 'automation.daily_briefing.model_tier', ]); expect(r.persisted).toBe(false); });