ideaco 1.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. package/.dockerignore +33 -0
  2. package/.nvmrc +1 -0
  3. package/ARCHITECTURE.md +394 -0
  4. package/Dockerfile +50 -0
  5. package/LICENSE +29 -0
  6. package/README.md +206 -0
  7. package/bin/i18n.js +46 -0
  8. package/bin/ideaco.js +494 -0
  9. package/deploy.sh +15 -0
  10. package/docker-compose.yml +30 -0
  11. package/electron/main.cjs +986 -0
  12. package/electron/preload.cjs +14 -0
  13. package/electron/web-backends.cjs +854 -0
  14. package/jsconfig.json +8 -0
  15. package/next.config.mjs +34 -0
  16. package/package.json +134 -0
  17. package/postcss.config.mjs +6 -0
  18. package/public/demo/dashboard.png +0 -0
  19. package/public/demo/employee.png +0 -0
  20. package/public/demo/messages.png +0 -0
  21. package/public/demo/office.png +0 -0
  22. package/public/demo/requirement.png +0 -0
  23. package/public/logo.jpeg +0 -0
  24. package/public/logo.png +0 -0
  25. package/scripts/prepare-electron.js +67 -0
  26. package/scripts/release.js +76 -0
  27. package/src/app/api/agents/[agentId]/chat/route.js +70 -0
  28. package/src/app/api/agents/[agentId]/conversations/route.js +35 -0
  29. package/src/app/api/agents/[agentId]/route.js +106 -0
  30. package/src/app/api/avatar/route.js +104 -0
  31. package/src/app/api/browse-dir/route.js +44 -0
  32. package/src/app/api/chat/route.js +265 -0
  33. package/src/app/api/company/factory-reset/route.js +43 -0
  34. package/src/app/api/company/route.js +82 -0
  35. package/src/app/api/departments/[deptId]/agents/[agentId]/dismiss/route.js +19 -0
  36. package/src/app/api/departments/route.js +92 -0
  37. package/src/app/api/group-chat-loop/events/route.js +70 -0
  38. package/src/app/api/group-chat-loop/route.js +94 -0
  39. package/src/app/api/mailbox/route.js +100 -0
  40. package/src/app/api/messages/route.js +14 -0
  41. package/src/app/api/providers/[id]/configure/route.js +21 -0
  42. package/src/app/api/providers/[id]/refresh-cookie/route.js +38 -0
  43. package/src/app/api/providers/[id]/test-cookie/route.js +28 -0
  44. package/src/app/api/providers/route.js +11 -0
  45. package/src/app/api/requirements/route.js +242 -0
  46. package/src/app/api/secretary/route.js +65 -0
  47. package/src/app/api/system/cli-backends/route.js +91 -0
  48. package/src/app/api/system/cron/route.js +110 -0
  49. package/src/app/api/system/knowledge/route.js +104 -0
  50. package/src/app/api/system/plugins/route.js +40 -0
  51. package/src/app/api/system/skills/route.js +46 -0
  52. package/src/app/api/system/status/route.js +46 -0
  53. package/src/app/api/talent-market/[profileId]/recall/route.js +22 -0
  54. package/src/app/api/talent-market/[profileId]/route.js +17 -0
  55. package/src/app/api/talent-market/route.js +26 -0
  56. package/src/app/api/teams/route.js +773 -0
  57. package/src/app/api/ws-files/[departmentId]/file/route.js +27 -0
  58. package/src/app/api/ws-files/[departmentId]/files/route.js +22 -0
  59. package/src/app/globals.css +130 -0
  60. package/src/app/layout.jsx +40 -0
  61. package/src/app/page.jsx +97 -0
  62. package/src/components/AgentChatModal.jsx +164 -0
  63. package/src/components/AgentDetailModal.jsx +425 -0
  64. package/src/components/AgentSpyModal.jsx +481 -0
  65. package/src/components/AvatarGrid.jsx +29 -0
  66. package/src/components/BossProfileModal.jsx +162 -0
  67. package/src/components/CachedAvatar.jsx +77 -0
  68. package/src/components/ChatPanel.jsx +219 -0
  69. package/src/components/ChatShared.jsx +255 -0
  70. package/src/components/DepartmentDetail.jsx +842 -0
  71. package/src/components/DepartmentView.jsx +367 -0
  72. package/src/components/FileReference.jsx +260 -0
  73. package/src/components/FilesView.jsx +465 -0
  74. package/src/components/GroupChatView.jsx +799 -0
  75. package/src/components/Mailbox.jsx +926 -0
  76. package/src/components/MessagesView.jsx +112 -0
  77. package/src/components/OnboardingGuide.jsx +209 -0
  78. package/src/components/OrgTree.jsx +151 -0
  79. package/src/components/Overview.jsx +391 -0
  80. package/src/components/PixelOffice.jsx +2281 -0
  81. package/src/components/ProviderGrid.jsx +551 -0
  82. package/src/components/ProvidersBoard.jsx +16 -0
  83. package/src/components/RequirementDetail.jsx +1279 -0
  84. package/src/components/RequirementsBoard.jsx +187 -0
  85. package/src/components/SecretarySettings.jsx +295 -0
  86. package/src/components/SetupWizard.jsx +388 -0
  87. package/src/components/Sidebar.jsx +169 -0
  88. package/src/components/SystemMonitor.jsx +808 -0
  89. package/src/components/TalentMarket.jsx +183 -0
  90. package/src/components/TeamDetail.jsx +697 -0
  91. package/src/core/agent/base-agent.js +104 -0
  92. package/src/core/agent/chat-store.js +602 -0
  93. package/src/core/agent/cli-agent/backends/claude-code/README.md +52 -0
  94. package/src/core/agent/cli-agent/backends/claude-code/config.js +27 -0
  95. package/src/core/agent/cli-agent/backends/codebuddy/README.md +236 -0
  96. package/src/core/agent/cli-agent/backends/codebuddy/config.js +27 -0
  97. package/src/core/agent/cli-agent/backends/codex/README.md +51 -0
  98. package/src/core/agent/cli-agent/backends/codex/config.js +27 -0
  99. package/src/core/agent/cli-agent/backends/index.js +27 -0
  100. package/src/core/agent/cli-agent/backends/registry.js +580 -0
  101. package/src/core/agent/cli-agent/index.js +154 -0
  102. package/src/core/agent/index.js +60 -0
  103. package/src/core/agent/llm-agent/client.js +320 -0
  104. package/src/core/agent/llm-agent/index.js +97 -0
  105. package/src/core/agent/message-bus.js +211 -0
  106. package/src/core/agent/session.js +608 -0
  107. package/src/core/agent/tools.js +596 -0
  108. package/src/core/agent/web-agent/backends/base-backend.js +180 -0
  109. package/src/core/agent/web-agent/backends/chatgpt/client.js +146 -0
  110. package/src/core/agent/web-agent/backends/chatgpt/config.js +148 -0
  111. package/src/core/agent/web-agent/backends/chatgpt/dom-scripts.js +303 -0
  112. package/src/core/agent/web-agent/backends/index.js +91 -0
  113. package/src/core/agent/web-agent/index.js +278 -0
  114. package/src/core/agent/web-agent/web-client.js +407 -0
  115. package/src/core/employee/base-employee.js +1088 -0
  116. package/src/core/employee/index.js +35 -0
  117. package/src/core/employee/knowledge.js +327 -0
  118. package/src/core/employee/lifecycle.js +990 -0
  119. package/src/core/employee/memory/index.js +642 -0
  120. package/src/core/employee/memory/store.js +143 -0
  121. package/src/core/employee/performance.js +224 -0
  122. package/src/core/employee/secretary.js +625 -0
  123. package/src/core/employee/skills.js +398 -0
  124. package/src/core/index.js +38 -0
  125. package/src/core/organization/company.js +2600 -0
  126. package/src/core/organization/department.js +737 -0
  127. package/src/core/organization/group-chat-loop.js +264 -0
  128. package/src/core/organization/index.js +8 -0
  129. package/src/core/organization/persistence.js +111 -0
  130. package/src/core/organization/team.js +267 -0
  131. package/src/core/organization/workforce/hr.js +377 -0
  132. package/src/core/organization/workforce/providers.js +468 -0
  133. package/src/core/organization/workforce/role-archetypes.js +805 -0
  134. package/src/core/organization/workforce/talent-market.js +205 -0
  135. package/src/core/prompts.js +532 -0
  136. package/src/core/requirement.js +1789 -0
  137. package/src/core/system/audit.js +483 -0
  138. package/src/core/system/cron.js +449 -0
  139. package/src/core/system/index.js +7 -0
  140. package/src/core/system/plugin.js +2183 -0
  141. package/src/core/utils/json-parse.js +188 -0
  142. package/src/core/workspace.js +239 -0
  143. package/src/lib/api-i18n.js +211 -0
  144. package/src/lib/avatar.js +268 -0
  145. package/src/lib/client-store.js +1025 -0
  146. package/src/lib/config-validator.js +483 -0
  147. package/src/lib/format-time.js +22 -0
  148. package/src/lib/hooks.js +414 -0
  149. package/src/lib/i18n.js +134 -0
  150. package/src/lib/paths.js +23 -0
  151. package/src/lib/store.js +72 -0
  152. package/src/locales/de.js +393 -0
  153. package/src/locales/en.js +1054 -0
  154. package/src/locales/es.js +393 -0
  155. package/src/locales/fr.js +393 -0
  156. package/src/locales/ja.js +501 -0
  157. package/src/locales/ko.js +513 -0
  158. package/src/locales/zh.js +828 -0
  159. package/tailwind.config.mjs +11 -0
@@ -0,0 +1,483 @@
1
+ /**
2
+ * Security Audit System - Enterprise-grade operation logging and access control
3
+ *
4
+ * Distilled from OpenClaw's security module (vendor/openclaw/src/security/)
5
+ * Re-implemented for the AI enterprise simulation context
6
+ *
7
+ * Features:
8
+ * - Audit trail for all tool calls and agent actions
9
+ * - Dangerous command detection and approval workflow
10
+ * - Secret scanning in agent outputs
11
+ * - Permission policies per agent/department
12
+ */
13
+ import { v4 as uuidv4 } from 'uuid';
14
+ import fs from 'fs';
15
+ import path from 'path';
16
+ import { AUDIT_DIR } from '../../lib/paths.js';
17
+
18
+ /**
19
+ * Audit event severity levels
20
+ */
21
+ export const AuditLevel = {
22
+ INFO: 'info',
23
+ WARN: 'warn',
24
+ CRITICAL: 'critical',
25
+ };
26
+
27
+ /**
28
+ * Audit event categories
29
+ */
30
+ export const AuditCategory = {
31
+ TOOL_CALL: 'tool_call',
32
+ LLM_REQUEST: 'llm_request',
33
+ FILE_ACCESS: 'file_access',
34
+ SHELL_EXEC: 'shell_exec',
35
+ AGENT_ACTION: 'agent_action',
36
+ AUTH: 'auth',
37
+ CONFIG_CHANGE: 'config_change',
38
+ SECRET_DETECTED: 'secret_detected',
39
+ };
40
+
41
+ /**
42
+ * Dangerous patterns in shell commands
43
+ */
44
+ const DANGEROUS_PATTERNS = [
45
+ /\brm\s+-rf?\s+\//, // rm -rf /
46
+ /\bsudo\b/, // sudo commands
47
+ /\bcurl\b.*\|\s*bash/, // curl | bash
48
+ /\bwget\b.*\|\s*sh/, // wget | sh
49
+ /\bchmod\s+777\b/, // chmod 777
50
+ /\bdd\s+if=/, // dd disk operations
51
+ /\bmkfs\b/, // filesystem creation
52
+ /\b(shutdown|reboot|halt)\b/, // system shutdown
53
+ /\bkill\s+-9\s+1\b/, // kill init
54
+ />\s*\/dev\/sd[a-z]/, // write to disk device
55
+ /\beval\b.*\$\(/, // eval with command substitution
56
+ ];
57
+
58
+ /**
59
+ * Secret patterns for scanning
60
+ */
61
+ const SECRET_PATTERNS = [
62
+ { name: 'AWS Key', pattern: /AKIA[0-9A-Z]{16}/ },
63
+ { name: 'Generic API Key', pattern: /(?:api[_-]?key|apikey)\s*[:=]\s*['"][a-zA-Z0-9]{20,}['"]/i },
64
+ { name: 'JWT Token', pattern: /eyJ[a-zA-Z0-9_-]{10,}\.eyJ[a-zA-Z0-9_-]{10,}\.[a-zA-Z0-9_-]{10,}/ },
65
+ { name: 'Private Key', pattern: /-----BEGIN (?:RSA |EC )?PRIVATE KEY-----/ },
66
+ { name: 'GitHub Token', pattern: /gh[ps]_[a-zA-Z0-9]{36}/ },
67
+ { name: 'OpenAI Key', pattern: /sk-[a-zA-Z0-9]{20,}/ },
68
+ { name: 'Password in URL', pattern: /:\/\/[^:]+:[^@]+@/ },
69
+ ];
70
+
71
+ /**
72
+ * Audit Logger - Records all security-relevant events
73
+ */
74
+ export class AuditLogger {
75
+ /**
76
+ * @param {object} options
77
+ * @param {string} options.logDir - Directory for audit log files
78
+ * @param {number} options.maxEvents - Max events to keep in memory
79
+ * @param {boolean} options.persistToDisk - Whether to write logs to disk
80
+ * @param {Function} options.onCritical - Callback for critical events
81
+ */
82
+ constructor(options = {}) {
83
+ this.logDir = options.logDir || AUDIT_DIR;
84
+ this.maxEvents = options.maxEvents ?? 1000;
85
+ this.persistToDisk = options.persistToDisk ?? true;
86
+ this.onCritical = options.onCritical || null;
87
+
88
+ // In-memory event buffer
89
+ this.events = [];
90
+
91
+ // Ensure log directory exists
92
+ if (this.persistToDisk && !fs.existsSync(this.logDir)) {
93
+ fs.mkdirSync(this.logDir, { recursive: true });
94
+ }
95
+ }
96
+
97
+ /**
98
+ * Log an audit event
99
+ * @param {object} event
100
+ * @param {string} event.category - Event category (AuditCategory)
101
+ * @param {string} event.level - Severity level (AuditLevel)
102
+ * @param {string} event.agentId - Agent that triggered the event
103
+ * @param {string} event.agentName - Agent name
104
+ * @param {string} event.action - Description of the action
105
+ * @param {object} event.details - Additional event details
106
+ * @param {boolean} event.blocked - Whether the action was blocked
107
+ * @returns {object} The logged event
108
+ */
109
+ log(event) {
110
+ const auditEvent = {
111
+ id: uuidv4(),
112
+ timestamp: new Date().toISOString(),
113
+ category: event.category || AuditCategory.AGENT_ACTION,
114
+ level: event.level || AuditLevel.INFO,
115
+ agentId: event.agentId || 'system',
116
+ agentName: event.agentName || 'System',
117
+ action: event.action || '',
118
+ details: event.details || {},
119
+ blocked: event.blocked || false,
120
+ };
121
+
122
+ // Add to in-memory buffer
123
+ this.events.push(auditEvent);
124
+ if (this.events.length > this.maxEvents) {
125
+ this.events.shift(); // Remove oldest
126
+ }
127
+
128
+ // Persist to disk
129
+ if (this.persistToDisk) {
130
+ this._persistEvent(auditEvent);
131
+ }
132
+
133
+ // Critical event callback
134
+ if (auditEvent.level === AuditLevel.CRITICAL && this.onCritical) {
135
+ try { this.onCritical(auditEvent); } catch {}
136
+ }
137
+
138
+ // Console output for critical/warn
139
+ if (auditEvent.level === AuditLevel.CRITICAL) {
140
+ console.error(`🚨 [AUDIT CRITICAL] ${auditEvent.action}`, auditEvent.details);
141
+ } else if (auditEvent.level === AuditLevel.WARN) {
142
+ console.warn(`⚠️ [AUDIT WARN] ${auditEvent.action}`);
143
+ }
144
+
145
+ return auditEvent;
146
+ }
147
+
148
+ /**
149
+ * Persist event to daily log file
150
+ */
151
+ _persistEvent(event) {
152
+ try {
153
+ const date = new Date().toISOString().split('T')[0]; // YYYY-MM-DD
154
+ const logFile = path.join(this.logDir, `audit-${date}.jsonl`);
155
+ fs.appendFileSync(logFile, JSON.stringify(event) + '\n', 'utf-8');
156
+ } catch (err) {
157
+ console.error('[AuditLogger] Failed to persist event:', err.message);
158
+ }
159
+ }
160
+
161
+ /**
162
+ * Query events with filters
163
+ * @param {object} filters
164
+ * @param {string} filters.category
165
+ * @param {string} filters.level
166
+ * @param {string} filters.agentId
167
+ * @param {boolean} filters.blocked
168
+ * @param {number} filters.limit
169
+ * @returns {Array} Matching events
170
+ */
171
+ query(filters = {}) {
172
+ let results = [...this.events];
173
+
174
+ if (filters.category) results = results.filter(e => e.category === filters.category);
175
+ if (filters.level) results = results.filter(e => e.level === filters.level);
176
+ if (filters.agentId) results = results.filter(e => e.agentId === filters.agentId);
177
+ if (filters.blocked !== undefined) results = results.filter(e => e.blocked === filters.blocked);
178
+
179
+ // Most recent first
180
+ results.reverse();
181
+
182
+ if (filters.limit) results = results.slice(0, filters.limit);
183
+
184
+ return results;
185
+ }
186
+
187
+ /**
188
+ * Get audit summary statistics
189
+ * @returns {object}
190
+ */
191
+ getSummary() {
192
+ const total = this.events.length;
193
+ const byLevel = { info: 0, warn: 0, critical: 0 };
194
+ const byCategory = {};
195
+ let blocked = 0;
196
+
197
+ for (const event of this.events) {
198
+ byLevel[event.level] = (byLevel[event.level] || 0) + 1;
199
+ byCategory[event.category] = (byCategory[event.category] || 0) + 1;
200
+ if (event.blocked) blocked++;
201
+ }
202
+
203
+ return { total, byLevel, byCategory, blocked };
204
+ }
205
+
206
+ /**
207
+ * Clear in-memory events
208
+ */
209
+ clear() {
210
+ this.events = [];
211
+ }
212
+ }
213
+
214
+ /**
215
+ * Security Guard - Validates actions before execution
216
+ */
217
+ export class SecurityGuard {
218
+ /**
219
+ * @param {AuditLogger} auditLogger
220
+ * @param {object} options
221
+ * @param {boolean} options.blockDangerous - Whether to block dangerous commands
222
+ * @param {boolean} options.scanSecrets - Whether to scan for secrets
223
+ * @param {Array} options.allowedCommands - Whitelist of allowed shell commands
224
+ */
225
+ constructor(auditLogger, options = {}) {
226
+ this.audit = auditLogger;
227
+ this.blockDangerous = options.blockDangerous ?? true;
228
+ this.scanSecrets = options.scanSecrets ?? true;
229
+ this.allowedCommands = options.allowedCommands || [
230
+ 'ls', 'cat', 'head', 'tail', 'grep', 'find', 'wc',
231
+ 'node', 'npm', 'npx', 'echo', 'mkdir', 'cp', 'mv',
232
+ 'tree', 'pwd', 'which', 'git',
233
+ 'curl', 'wget', 'date', 'python', 'python3', 'env', 'sort', 'uniq', 'awk', 'sed', 'jq',
234
+ ];
235
+
236
+ // Per-agent permission policies
237
+ // { agentId: { canExecShell, canWriteFiles, canAccessNetwork, ... } }
238
+ this.policies = new Map();
239
+ }
240
+
241
+ /**
242
+ * Set permission policy for an agent
243
+ * @param {string} agentId
244
+ * @param {object} policy
245
+ */
246
+ setPolicy(agentId, policy) {
247
+ this.policies.set(agentId, {
248
+ canExecShell: true,
249
+ canWriteFiles: true,
250
+ canDeleteFiles: false,
251
+ canAccessNetwork: false,
252
+ canSendMessages: true,
253
+ maxFileSize: 1024 * 1024, // 1MB
254
+ ...policy,
255
+ });
256
+ }
257
+
258
+ /**
259
+ * Get policy for an agent (default permissive)
260
+ * @param {string} agentId
261
+ * @returns {object}
262
+ */
263
+ getPolicy(agentId) {
264
+ return this.policies.get(agentId) || {
265
+ canExecShell: true,
266
+ canWriteFiles: true,
267
+ canDeleteFiles: false,
268
+ canAccessNetwork: false,
269
+ canSendMessages: true,
270
+ maxFileSize: 1024 * 1024,
271
+ };
272
+ }
273
+
274
+ /**
275
+ * Validate a shell command before execution
276
+ * @param {string} command
277
+ * @param {string} agentId
278
+ * @param {string} agentName
279
+ * @returns {{ allowed: boolean, reason?: string }}
280
+ */
281
+ validateShellCommand(command, agentId, agentName = '') {
282
+ const policy = this.getPolicy(agentId);
283
+
284
+ // Check policy permission
285
+ if (!policy.canExecShell) {
286
+ this.audit.log({
287
+ category: AuditCategory.SHELL_EXEC,
288
+ level: AuditLevel.WARN,
289
+ agentId,
290
+ agentName,
291
+ action: `Shell execution denied by policy: ${command}`,
292
+ details: { command },
293
+ blocked: true,
294
+ });
295
+ return { allowed: false, reason: 'Agent does not have shell execution permission' };
296
+ }
297
+
298
+ // Check command whitelist
299
+ const cmdName = command.trim().split(/\s+/)[0];
300
+ if (!this.allowedCommands.includes(cmdName)) {
301
+ this.audit.log({
302
+ category: AuditCategory.SHELL_EXEC,
303
+ level: AuditLevel.WARN,
304
+ agentId,
305
+ agentName,
306
+ action: `Command not in whitelist: ${cmdName}`,
307
+ details: { command, cmdName },
308
+ blocked: true,
309
+ });
310
+ return { allowed: false, reason: `Command "${cmdName}" is not in the allowed list` };
311
+ }
312
+
313
+ // Check dangerous patterns
314
+ if (this.blockDangerous) {
315
+ for (const pattern of DANGEROUS_PATTERNS) {
316
+ if (pattern.test(command)) {
317
+ this.audit.log({
318
+ category: AuditCategory.SHELL_EXEC,
319
+ level: AuditLevel.CRITICAL,
320
+ agentId,
321
+ agentName,
322
+ action: `Dangerous command blocked: ${command}`,
323
+ details: { command, matchedPattern: pattern.toString() },
324
+ blocked: true,
325
+ });
326
+ return { allowed: false, reason: `Dangerous command pattern detected` };
327
+ }
328
+ }
329
+ }
330
+
331
+ // Log approved command
332
+ this.audit.log({
333
+ category: AuditCategory.SHELL_EXEC,
334
+ level: AuditLevel.INFO,
335
+ agentId,
336
+ agentName,
337
+ action: `Shell command approved: ${command}`,
338
+ details: { command },
339
+ blocked: false,
340
+ });
341
+
342
+ return { allowed: true };
343
+ }
344
+
345
+ /**
346
+ * Validate file write operation
347
+ * @param {string} filePath
348
+ * @param {string} content
349
+ * @param {string} agentId
350
+ * @param {string} agentName
351
+ * @returns {{ allowed: boolean, reason?: string }}
352
+ */
353
+ validateFileWrite(filePath, content, agentId, agentName = '') {
354
+ const policy = this.getPolicy(agentId);
355
+
356
+ if (!policy.canWriteFiles) {
357
+ this.audit.log({
358
+ category: AuditCategory.FILE_ACCESS,
359
+ level: AuditLevel.WARN,
360
+ agentId,
361
+ agentName,
362
+ action: `File write denied by policy: ${filePath}`,
363
+ details: { filePath },
364
+ blocked: true,
365
+ });
366
+ return { allowed: false, reason: 'Agent does not have file write permission' };
367
+ }
368
+
369
+ // Check file size
370
+ const size = Buffer.byteLength(content, 'utf-8');
371
+ if (size > policy.maxFileSize) {
372
+ this.audit.log({
373
+ category: AuditCategory.FILE_ACCESS,
374
+ level: AuditLevel.WARN,
375
+ agentId,
376
+ agentName,
377
+ action: `File too large: ${filePath} (${size} bytes)`,
378
+ details: { filePath, size, maxSize: policy.maxFileSize },
379
+ blocked: true,
380
+ });
381
+ return { allowed: false, reason: `File size (${size} bytes) exceeds limit (${policy.maxFileSize} bytes)` };
382
+ }
383
+
384
+ this.audit.log({
385
+ category: AuditCategory.FILE_ACCESS,
386
+ level: AuditLevel.INFO,
387
+ agentId,
388
+ agentName,
389
+ action: `File write: ${filePath} (${size} bytes)`,
390
+ details: { filePath, size },
391
+ });
392
+
393
+ return { allowed: true };
394
+ }
395
+
396
+ /**
397
+ * Scan text for secrets/credentials
398
+ * @param {string} text - Text to scan
399
+ * @param {string} context - Where the text came from
400
+ * @param {string} agentId
401
+ * @returns {{ clean: boolean, findings: Array }}
402
+ */
403
+ scanForSecrets(text, context = '', agentId = 'system') {
404
+ if (!this.scanSecrets || !text) return { clean: true, findings: [] };
405
+
406
+ const findings = [];
407
+
408
+ for (const { name, pattern } of SECRET_PATTERNS) {
409
+ const match = pattern.exec(text);
410
+ if (match) {
411
+ findings.push({
412
+ type: name,
413
+ position: match.index,
414
+ preview: match[0].slice(0, 8) + '***',
415
+ });
416
+ }
417
+ }
418
+
419
+ if (findings.length > 0) {
420
+ this.audit.log({
421
+ category: AuditCategory.SECRET_DETECTED,
422
+ level: AuditLevel.CRITICAL,
423
+ agentId,
424
+ action: `Secrets detected in ${context}: ${findings.map(f => f.type).join(', ')}`,
425
+ details: { context, findings },
426
+ blocked: false, // Log but don't block - leave decision to caller
427
+ });
428
+ }
429
+
430
+ return { clean: findings.length === 0, findings };
431
+ }
432
+
433
+ /**
434
+ * Log a tool call
435
+ * @param {string} toolName
436
+ * @param {object} args
437
+ * @param {string} agentId
438
+ * @param {string} agentName
439
+ */
440
+ logToolCall(toolName, args, agentId, agentName = '') {
441
+ this.audit.log({
442
+ category: AuditCategory.TOOL_CALL,
443
+ level: AuditLevel.INFO,
444
+ agentId,
445
+ agentName,
446
+ action: `Tool call: ${toolName}`,
447
+ details: { toolName, args: this._sanitizeArgs(args) },
448
+ });
449
+ }
450
+
451
+ /**
452
+ * Log an LLM API request
453
+ */
454
+ logLLMRequest(providerId, model, tokenCount, agentId, agentName = '') {
455
+ this.audit.log({
456
+ category: AuditCategory.LLM_REQUEST,
457
+ level: AuditLevel.INFO,
458
+ agentId,
459
+ agentName,
460
+ action: `LLM request: ${providerId} (${model})`,
461
+ details: { providerId, model, tokenCount },
462
+ });
463
+ }
464
+
465
+ /**
466
+ * Sanitize arguments for logging (remove sensitive data)
467
+ */
468
+ _sanitizeArgs(args) {
469
+ if (!args || typeof args !== 'object') return args;
470
+ const sanitized = { ...args };
471
+ const sensitiveKeys = ['apiKey', 'password', 'token', 'secret', 'credential'];
472
+ for (const key of Object.keys(sanitized)) {
473
+ if (sensitiveKeys.some(sk => key.toLowerCase().includes(sk))) {
474
+ sanitized[key] = '***REDACTED***';
475
+ }
476
+ }
477
+ return sanitized;
478
+ }
479
+ }
480
+
481
+ // Global singletons
482
+ export const auditLogger = new AuditLogger();
483
+ export const securityGuard = new SecurityGuard(auditLogger);