import { detectViolations } from './lib/detectors.js'; import { loadPolicy, checkPolicy } from './lib/policy.js'; import { addEvent, addViolation, getStats } from './lib/audit.js'; const AGENT_PATTERNS = { 'claude.ai': 'claude-code', 'chatgpt.com': 'chatgpt', 'chat.openai.com': 'chatgpt', 'github.com': 'github-copilot', 'cursor.sh': 'cursor' }; function detectAgent(url) { const hostname = new URL(url).hostname; for (const [domain, agent] of Object.entries(AGENT_PATTERNS)) { if (hostname.includes(domain)) return agent; } return 'unknown'; } chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { if (message.type === 'AGENT_EVENT') { handleAgentEvent(message.payload, sender.tab?.url || 'unknown').then(() => { sendResponse({ success: true }); }).catch(err => { console.error('Agent Police Department: event error', err); sendResponse({ success: false, error: err.message }); }); return true; } if (message.type === 'GET_STATS') { getStats().then(stats => sendResponse({ success: true, stats })).catch(err => { sendResponse({ success: false, error: err.message }); }); return true; } if (message.type === 'GET_AUDIT_LOG') { import('./lib/audit.js').then(m => m.getAuditLog(message.limit || 100)).then(log => { sendResponse({ success: true, log }); }).catch(err => sendResponse({ success: false, error: err.message })); return true; } if (message.type === 'EXPORT_AUDIT') { import('./lib/audit.js').then(m => m.exportAuditLog()).then(data => { sendResponse({ success: true, data }); }).catch(err => sendResponse({ success: false, error: err.message })); return true; } }); async function handleAgentEvent(event, tabUrl) { event.agent = event.agent || detectAgent(tabUrl); event.url = tabUrl; event.timestamp = event.timestamp || new Date().toISOString(); event.id = event.id || crypto.randomUUID(); const policy = await loadPolicy(); const policyResult = checkPolicy(event, policy); event.outcome = policyResult.outcome; event.severity = policyResult.severity; if (policyResult.ruleId) event.rule_triggered = policyResult.ruleId; const violations = await detectViolations(event, policy); await addEvent(event); if (violations.length > 0) { for (const v of violations) { await addViolation(v); } await chrome.storage.local.set({ lastViolation: violations[0], lastViolationAt: Date.now() }); await chrome.action.setBadgeText({ text: String(violations.length) }); await chrome.action.setBadgeBackgroundColor({ color: '#DC2626' }); } const stats = await getStats(); await chrome.storage.local.set({ stats }); } chrome.runtime.onInstalled.addListener(async (details) => { if (details.reason === 'install') { await chrome.storage.local.set({ installDate: new Date().toISOString(), version: chrome.runtime.getManifest().version }); await loadPolicy(); } }); chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => { if (changeInfo.status === 'complete' && tab.url) { const agent = detectAgent(tab.url); if (agent !== 'unknown') { chrome.storage.local.get(['activeAgents']).then(result => { const agents = result.activeAgents || {}; agents[agent] = { tabId, url: tab.url, lastSeen: Date.now() }; return chrome.storage.local.set({ activeAgents: agents }); }); } } }); chrome.alarms?.create?.('cleanup', { periodInMinutes: 60 }); chrome.alarms?.onAlarm?.addListener(async (alarm) => { if (alarm.name === 'cleanup') { const result = await chrome.storage.local.get(['activeAgents']); const agents = result.activeAgents || {}; const now = Date.now(); const stale = 5 * 60 * 1000; for (const [agent, data] of Object.entries(agents)) { if (now - data.lastSeen > stale) delete agents[agent]; } await chrome.storage.local.set({ activeAgents: agents }); } }); console.log('Agent Police Department: background service worker loaded');