feat: add privacy-aware security manager onboarding
This commit is contained in:
56
server.mjs
56
server.mjs
@@ -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(', ')}` : '';
|
||||
|
||||
Reference in New Issue
Block a user