feat: add privacy-aware security manager onboarding
All checks were successful
Codex Template Compliance / template-compliance (pull_request) Successful in 7s
Build / test-and-image (pull_request) Successful in 52s

This commit is contained in:
2026-07-05 21:39:55 +02:00
parent d13652a70b
commit 0c7ddc53b4
14 changed files with 767 additions and 8 deletions

View File

@@ -17,6 +17,9 @@ import { generateLLMIdeas } from './lib/llm/ideas.mjs';
import { TelegramChatAssistant, buildTelegramChatContext } from './lib/llm/telegram-chat.mjs';
import { TerminalAgent } from './lib/agent/terminal-agent.mjs';
import { createTerminalToolRegistry } from './lib/agent/terminal-tools.mjs';
import { SecurityProfileStore } from './lib/security/security-profile-store.mjs';
import { SecurityOnboarding } from './lib/security/security-onboarding.mjs';
import { evaluateSecurityAlertPolicy } from './lib/security/security-alert-policy.mjs';
import { TelegramAlerter } from './lib/alerts/telegram.mjs';
import { DiscordAlerter } from './lib/alerts/discord.mjs';
import { getFetchMetrics } from './apis/utils/fetch.mjs';
@@ -56,12 +59,22 @@ await intelligenceStore.init();
const llmProvider = createLLMProvider(config.llm);
const telegramAlerter = new TelegramAlerter(config.telegram);
const discordAlerter = new DiscordAlerter(config.discord || {});
const securityProfileStore = new SecurityProfileStore(
join(RUNS_DIR, 'security-profile.enc'),
config.security.profileEncryptionKey,
).init();
const securityOnboarding = new SecurityOnboarding({
store: securityProfileStore,
alerter: telegramAlerter,
chatId: config.telegram.chatId,
});
const terminalToolRegistry = createTerminalToolRegistry({
getData: () => currentData,
getHealth: () => buildHealth(),
getDelta: () => memory.getLastDelta(),
buildBrief,
intelligenceStore,
securityProfileStore,
triggerSweep: () => runSweepCycle().catch(error => console.error('[Agent] Confirmed sweep failed:', error.message)),
isSweepInProgress: () => sweepInProgress,
telegramAlerter,
@@ -205,7 +218,10 @@ if (telegramAlerter.isConfigured) {
return { text: `${result.answer}${traceSuffix}`, parseMode: null };
};
telegramAlerter.onMessage((text, msg) => answerTelegramQuestion(text, msg));
telegramAlerter.onMessage((text, msg) => {
const onboarding = securityOnboarding.handleMessage(text, msg);
return onboarding.handled ? onboarding.response : answerTelegramQuestion(text, msg);
});
telegramAlerter.onCommand('/ask', async (args, _messageId, msg) => {
if (!args.trim()) return { text: 'Usage: /ask <question>', parseMode: null };
@@ -217,6 +233,25 @@ if (telegramAlerter.isConfigured) {
return { text: 'AI conversation history cleared.', parseMode: null };
});
telegramAlerter.onCommand('/onboarding', async (_args, _messageId, msg) => {
if (!config.security.onboardingEnabled) return { text: 'Security Manager onboarding is disabled.', parseMode: null };
await securityOnboarding.start(msg?.chat?.id || config.telegram.chatId);
return null;
});
telegramAlerter.onCommand('/language', async (_args, _messageId, msg) => {
if (!config.security.onboardingEnabled) return { text: 'Security Manager onboarding is disabled.', parseMode: null };
await securityOnboarding.start(msg?.chat?.id || config.telegram.chatId, { languageOnly: true });
return null;
});
telegramAlerter.onCommand('/profile', async () => ({
text: securityOnboarding.profileText(),
parseMode: null,
}));
telegramAlerter.onCommand('/profile_delete', async () => securityOnboarding.deletePrompt());
telegramAlerter.onCommand('/tools', async () => ({
text: terminalAgent.listTools().map(tool => `${tool.mutating ? '[confirm]' : '[read]'} ${tool.name}: ${tool.description}`).join('\n'),
parseMode: null,
@@ -244,6 +279,8 @@ if (telegramAlerter.isConfigured) {
telegramAlerter.onCommand('/confirm', async (args, _messageId, msg) => confirmAgentAction(args.trim(), msg?.chat?.id || config.telegram.chatId));
telegramAlerter.onCommand('/cancel', async (args, _messageId, msg) => cancelAgentAction(args.trim(), msg?.chat?.id || config.telegram.chatId));
telegramAlerter.onCallback(async (data, query) => {
const onboarding = await securityOnboarding.handleCallback(data, query);
if (onboarding.handled) return onboarding.response;
const [operation, id] = String(data).split(':', 2);
const chatId = query.message?.chat?.id || config.telegram.chatId;
if (operation === 'agent_confirm') return confirmAgentAction(id, chatId);
@@ -257,6 +294,11 @@ if (telegramAlerter.isConfigured) {
// Start polling for bot commands
telegramAlerter.startPolling(config.telegram.botPollingInterval);
if (config.security.onboardingEnabled) {
securityOnboarding.ensureStarted().catch(error => {
console.error('[Security Manager] Initial onboarding failed:', error.message);
});
}
}
// === Discord Bot ===
@@ -392,6 +434,7 @@ app.get('/api/metrics', (req, res) => {
news: currentData?.newsMeta || {},
llm: getLLMStatus(),
memory: intelligenceStore.status(),
securityProfile: securityProfileStore.status(),
});
});
@@ -667,6 +710,7 @@ function buildHealth() {
refreshIntervalMinutes: config.refreshIntervalMinutes,
language: currentLanguage,
memory: intelligenceStore.status(),
securityProfile: securityProfileStore.status(),
};
}
@@ -867,15 +911,21 @@ function shouldRunProactiveAgent(delta) {
async function runProactiveAgent(data, delta) {
if (telegramAlerter.getMuteStatus().muted) return false;
const prompt = `Evaluate the latest sweep for a proactive operator notification. Cross-check material changes with source health, evidence, scenarios, memory, and predictions as needed. Do not call mutating tools. Delta summary: ${JSON.stringify(delta?.summary || {})}`;
const prompt = `Evaluate the latest sweep as the operator's Security Manager. Use the security profile when available to assess geographic and personal relevance, dependencies, urgency, and preferred language. Cross-check material changes with source health, evidence, scenarios, memory, and predictions as needed. Distinguish verified facts from inference. Do not call mutating tools. Delta summary: ${JSON.stringify(delta?.summary || {})}`;
const result = await terminalAgent.analyzeProactively(prompt, {
context: buildTelegramChatContext(data, buildHealth()),
runtime: { data, delta },
});
if (result.pendingAction) return false;
if (!result.notify) {
const profile = securityProfileStore.getAgentProfile();
const policy = evaluateSecurityAlertPolicy(result, profile);
if (!profile && !result.notify) {
return telegramAlerter.evaluateAndAlert(null, delta, memory);
}
if (!policy.send) {
console.log(`[Security Manager] Proactive notification suppressed: ${policy.reason}`);
return false;
}
const evidence = result.evidence?.length ? `\nEvidence:\n${result.evidence.map(item => `- ${item}`).join('\n')}` : '';
const tools = [...new Set((result.trace || []).filter(item => item.status === 'ok').map(item => item.tool))];
const trace = tools.length ? `\nTools: ${tools.join(', ')}` : '';