agentacta 1.3.2 → 1.3.4

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.
package/README.md CHANGED
@@ -14,6 +14,10 @@ One command. Zero config. Full visibility.
14
14
  npx agentacta
15
15
  ```
16
16
 
17
+ <p align="center">
18
+ <img src="screenshots/demo.gif" alt="AgentActa demo" width="800">
19
+ </p>
20
+
17
21
  ---
18
22
 
19
23
  ## Why
@@ -63,6 +67,7 @@ Open `http://localhost:4003` in your browser.
63
67
  AgentActa automatically finds your sessions in:
64
68
  - `~/.openclaw/agents/*/sessions/` (OpenClaw)
65
69
  - `~/.claude/projects/*/` (Claude Code)
70
+ - `~/.codex/sessions/` (Codex CLI)
66
71
 
67
72
  Or point it at a custom path:
68
73
 
@@ -89,7 +94,7 @@ Download any session or search results as Markdown or JSON. Great for sharing, a
89
94
 
90
95
  ## How It Works
91
96
 
92
- AgentActa reads JSONL session files (the standard format used by OpenClaw and Claude Code), parses every message and tool call, and indexes them into a local SQLite database with FTS5 full-text search.
97
+ AgentActa reads JSONL session files (including OpenClaw, Claude Code, and Codex CLI formats), parses every message and tool call, and indexes them into a local SQLite database with FTS5 full-text search.
93
98
 
94
99
  The web UI is a single-page app served by a lightweight Node.js HTTP server. No frameworks, no build step, no external dependencies beyond `better-sqlite3`.
95
100
 
@@ -197,7 +202,7 @@ All data stays local. AgentActa runs entirely on your machine — no cloud servi
197
202
 
198
203
  - ✅ [OpenClaw](https://github.com/openclaw/openclaw)
199
204
  - ✅ Claude Code
200
- - 🔜 Codex CLI
205
+ - Codex CLI
201
206
  - 🔜 Custom JSONL formats
202
207
 
203
208
  ## Contributing
package/indexer.js CHANGED
@@ -6,6 +6,25 @@ const { loadConfig } = require('./config');
6
6
  const REINDEX = process.argv.includes('--reindex');
7
7
  const WATCH = process.argv.includes('--watch');
8
8
 
9
+ function listJsonlFiles(baseDir, recursive = false) {
10
+ if (!fs.existsSync(baseDir)) return [];
11
+ const out = [];
12
+
13
+ function walk(dir) {
14
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
15
+ const full = path.join(dir, entry.name);
16
+ if (entry.isDirectory()) {
17
+ if (recursive) walk(full);
18
+ continue;
19
+ }
20
+ if (entry.isFile() && entry.name.endsWith('.jsonl')) out.push(full);
21
+ }
22
+ }
23
+
24
+ walk(baseDir);
25
+ return out;
26
+ }
27
+
9
28
  function discoverSessionDirs(config) {
10
29
  const dirs = [];
11
30
  const home = process.env.HOME;
@@ -48,6 +67,12 @@ function discoverSessionDirs(config) {
48
67
  }
49
68
  }
50
69
 
70
+ // Scan ~/.codex/sessions recursively (Codex CLI stores nested YYYY/MM/DD/*.jsonl)
71
+ const codexSessions = path.join(home, '.codex/sessions');
72
+ if (fs.existsSync(codexSessions) && fs.statSync(codexSessions).isDirectory()) {
73
+ dirs.push({ path: codexSessions, agent: 'codex-cli', recursive: true });
74
+ }
75
+
51
76
  if (!dirs.length) {
52
77
  // Fallback to hardcoded
53
78
  const fallback = path.join(home, '.openclaw/agents/main/sessions');
@@ -63,6 +88,28 @@ function isHeartbeat(text) {
63
88
  return lower.includes('heartbeat') || lower.includes('heartbeat_ok');
64
89
  }
65
90
 
91
+ function isBoilerplatePrompt(text) {
92
+ if (!text) return false;
93
+ const lower = text.toLowerCase();
94
+ return lower.includes('<permissions instructions>')
95
+ || lower.includes('filesystem sandboxing defines which files can be read or written')
96
+ || lower.includes('# agents.md instructions for ');
97
+ }
98
+
99
+ function isSummaryCandidate(text) {
100
+ if (!text || text.trim().length <= 10) return false;
101
+ if (isHeartbeat(text)) return false;
102
+ if (isBoilerplatePrompt(text)) return false;
103
+ return true;
104
+ }
105
+
106
+ function stripLeadingDatetimePrefix(text) {
107
+ if (!text) return text;
108
+ return text
109
+ .replace(/^\[(Mon|Tue|Wed|Thu|Fri|Sat|Sun)\s+\d{4}-\d{2}-\d{2}[^\]]*\]\s*/i, '')
110
+ .trim();
111
+ }
112
+
66
113
  function extractContent(msg) {
67
114
  if (!msg || !msg.content) return '';
68
115
  if (typeof msg.content === 'string') return msg.content;
@@ -94,17 +141,60 @@ function extractToolResult(msg) {
94
141
  return null;
95
142
  }
96
143
 
144
+ function extractCodexMessageText(content) {
145
+ if (!content) return '';
146
+ if (typeof content === 'string') return content;
147
+ if (!Array.isArray(content)) return '';
148
+
149
+ return content
150
+ .map((part) => {
151
+ if (!part || typeof part !== 'object') return '';
152
+ if (typeof part.text === 'string') return part.text;
153
+ if (typeof part.output_text === 'string') return part.output_text;
154
+ if (typeof part.input_text === 'string') return part.input_text;
155
+ return '';
156
+ })
157
+ .filter(Boolean)
158
+ .join('\n');
159
+ }
160
+
97
161
  function extractFilePaths(toolName, toolArgs) {
98
162
  const paths = [];
99
163
  if (!toolArgs) return paths;
164
+
165
+ const maybePath = (value) => {
166
+ if (typeof value !== 'string') return;
167
+ if (value.startsWith('/') || value.startsWith('~/') || value.startsWith('./') || value.startsWith('../')) {
168
+ paths.push(value);
169
+ return;
170
+ }
171
+ if (value.includes('/') || value.includes('\\')) paths.push(value);
172
+ };
173
+
174
+ const visit = (obj) => {
175
+ if (!obj || typeof obj !== 'object') return;
176
+ if (Array.isArray(obj)) {
177
+ for (const item of obj) visit(item);
178
+ return;
179
+ }
180
+
181
+ for (const [key, value] of Object.entries(obj)) {
182
+ if (typeof value === 'string') {
183
+ if (['path', 'file_path', 'filePath', 'file', 'filename', 'cwd', 'workdir', 'directory', 'dir'].includes(key)) {
184
+ maybePath(value);
185
+ }
186
+ } else if (value && typeof value === 'object') {
187
+ visit(value);
188
+ }
189
+ }
190
+ };
191
+
100
192
  try {
101
193
  const args = typeof toolArgs === 'string' ? JSON.parse(toolArgs) : toolArgs;
102
- // Common field names for file paths
103
- for (const key of ['path', 'file_path', 'filePath', 'file', 'filename']) {
104
- if (args[key] && typeof args[key] === 'string') paths.push(args[key]);
105
- }
194
+ visit(args);
106
195
  } catch {}
107
- return paths;
196
+
197
+ return [...new Set(paths)];
108
198
  }
109
199
 
110
200
  function aliasProject(project, config) {
@@ -177,9 +267,20 @@ function indexFile(db, filePath, agentName, stmts, archiveMode, config) {
177
267
  let initialPrompt = null;
178
268
  let firstMessageId = null;
179
269
  let firstMessageTimestamp = null;
270
+ let codexProvider = null;
271
+ let codexSource = null;
272
+ let sawSnapshotRecord = false;
273
+ let sawNonSnapshotRecord = false;
274
+
275
+ let firstLine;
276
+ try {
277
+ firstLine = JSON.parse(lines[0]);
278
+ } catch {
279
+ return { skipped: true };
280
+ }
180
281
 
181
- const firstLine = JSON.parse(lines[0]);
182
282
  let isClaudeCode = false;
283
+ let isCodexCli = false;
183
284
 
184
285
  if (firstLine.type === 'session') {
185
286
  // OpenClaw format
@@ -204,6 +305,20 @@ function indexFile(db, filePath, agentName, stmts, archiveMode, config) {
204
305
  sessionId = path.basename(filePath, '.jsonl');
205
306
  sessionStart = new Date(firstLine.timestamp || Date.now()).toISOString();
206
307
  }
308
+ } else if (firstLine.type === 'session_meta') {
309
+ // Codex CLI format
310
+ isCodexCli = true;
311
+ const meta = firstLine.payload || {};
312
+ sessionId = meta.id || path.basename(filePath, '.jsonl');
313
+ sessionStart = meta.timestamp || firstLine.timestamp || new Date().toISOString();
314
+ sessionType = 'codex-cli';
315
+ agent = 'codex-cli';
316
+ if (meta.model) {
317
+ model = meta.model;
318
+ modelsSet.add(meta.model);
319
+ }
320
+ codexProvider = meta.model_provider || null;
321
+ codexSource = meta.source || null;
207
322
  } else {
208
323
  return { skipped: true };
209
324
  }
@@ -214,8 +329,9 @@ function indexFile(db, filePath, agentName, stmts, archiveMode, config) {
214
329
  const projectCounts = new Map();
215
330
 
216
331
  // Seed project from session cwd when available (helps chat-only sessions)
217
- if (firstLine && firstLine.cwd) {
218
- const p = extractProjectFromPath(firstLine.cwd);
332
+ const sessionCwd = (firstLine && firstLine.cwd) || (firstLine && firstLine.payload && firstLine.payload.cwd);
333
+ if (sessionCwd) {
334
+ const p = extractProjectFromPath(sessionCwd, config);
219
335
  if (p) projectCounts.set(p, 1);
220
336
  }
221
337
 
@@ -223,6 +339,111 @@ function indexFile(db, filePath, agentName, stmts, archiveMode, config) {
223
339
  let obj;
224
340
  try { obj = JSON.parse(line); } catch { continue; }
225
341
 
342
+ if (obj.type === 'file-history-snapshot') sawSnapshotRecord = true;
343
+ else sawNonSnapshotRecord = true;
344
+
345
+ if (isCodexCli) {
346
+ if (obj.type === 'session_meta') {
347
+ const meta = obj.payload || {};
348
+ if (meta.id) sessionId = meta.id;
349
+ if (meta.timestamp && !sessionStart) sessionStart = meta.timestamp;
350
+ if (meta.model) {
351
+ if (!model) model = meta.model;
352
+ modelsSet.add(meta.model);
353
+ }
354
+ if (meta.model_provider) codexProvider = meta.model_provider;
355
+ if (meta.source) codexSource = meta.source;
356
+ if (meta.model_provider && !model) model = meta.model_provider;
357
+ continue;
358
+ }
359
+
360
+ if (obj.type === 'turn_context' && obj.payload) {
361
+ const tc = obj.payload;
362
+ if (tc.model && typeof tc.model === 'string') {
363
+ if (!model || model === codexProvider) model = tc.model;
364
+ modelsSet.add(tc.model);
365
+ }
366
+ continue;
367
+ }
368
+
369
+ if (obj.type === 'response_item' && obj.payload) {
370
+ const p = obj.payload;
371
+ const ts = obj.timestamp || sessionStart;
372
+ const eventId = `evt-${obj.type}-${Date.parse(ts) || Math.random()}`;
373
+
374
+ if (p.type === 'function_call') {
375
+ const toolName = p.name || p.tool_name || '';
376
+ const toolArgs = typeof p.arguments === 'string' ? p.arguments : JSON.stringify(p.arguments || {});
377
+ const callBaseId = p.call_id || p.id || eventId;
378
+ pendingEvents.push([`${callBaseId}:call`, sessionId, ts, 'tool_call', 'assistant', null, toolName, toolArgs, null]);
379
+ toolCount++;
380
+
381
+ const fps = extractFilePaths(toolName, toolArgs);
382
+ for (const fp of fps) {
383
+ fileActivities.push([sessionId, fp, 'read', ts]);
384
+ const project = extractProjectFromPath(fp, config);
385
+ if (project) projectCounts.set(project, (projectCounts.get(project) || 0) + 1);
386
+ }
387
+ sessionEnd = ts;
388
+ continue;
389
+ }
390
+
391
+ if (p.type === 'function_call_output') {
392
+ const output = (typeof p.output === 'string' ? p.output : JSON.stringify(p.output || '')).slice(0, 10000);
393
+ const resultBaseId = p.call_id || p.id || eventId;
394
+ pendingEvents.push([`${resultBaseId}:result`, sessionId, ts, 'tool_result', 'tool', output, p.name || p.tool_name || '', null, output]);
395
+ sessionEnd = ts;
396
+ continue;
397
+ }
398
+
399
+ if (p.type === 'message') {
400
+ const rawRole = p.role || 'assistant';
401
+ const role = rawRole === 'assistant' ? 'assistant' : 'user';
402
+ const content = extractCodexMessageText(p.content);
403
+ if (content) {
404
+ pendingEvents.push([p.id || eventId, sessionId, ts, 'message', role, content, null, null, null]);
405
+ msgCount++;
406
+ if (!summary && role === 'user' && isSummaryCandidate(content)) summary = content.slice(0, 200);
407
+ if (!initialPrompt && role === 'user' && isSummaryCandidate(content)) {
408
+ initialPrompt = content.slice(0, 500);
409
+ firstMessageId = p.id || eventId;
410
+ firstMessageTimestamp = ts;
411
+ }
412
+ }
413
+ sessionEnd = ts;
414
+ continue;
415
+ }
416
+ }
417
+
418
+ if (obj.type === 'event_msg' && obj.payload) {
419
+ const p = obj.payload;
420
+ const ts = obj.timestamp || sessionStart;
421
+ const eventId = `evt-${p.type || 'event'}-${Date.parse(ts) || Math.random()}`;
422
+
423
+ if (p.type === 'agent_message' && p.message) {
424
+ pendingEvents.push([eventId, sessionId, ts, 'message', 'assistant', p.message, null, null, null]);
425
+ msgCount++;
426
+ sessionEnd = ts;
427
+ continue;
428
+ }
429
+
430
+ if (p.type === 'user_message' && p.message) {
431
+ pendingEvents.push([eventId, sessionId, ts, 'message', 'user', p.message, null, null, null]);
432
+ msgCount++;
433
+ if (!summary && isSummaryCandidate(p.message)) summary = p.message.slice(0, 200);
434
+ if (!initialPrompt && isSummaryCandidate(p.message)) {
435
+ initialPrompt = p.message.slice(0, 500);
436
+ firstMessageId = eventId;
437
+ firstMessageTimestamp = ts;
438
+ }
439
+ sessionEnd = ts;
440
+ continue;
441
+ }
442
+ }
443
+
444
+ continue;
445
+ }
446
+
226
447
  if (obj.type === 'session' || obj.type === 'model_change' || obj.type === 'thinking_level_change' || obj.type === 'custom' || obj.type === 'file-history-snapshot') {
227
448
  if (obj.type === 'model_change' && obj.modelId) {
228
449
  if (!model) model = obj.modelId; // First model for backwards compat
@@ -289,12 +510,12 @@ function indexFile(db, filePath, agentName, stmts, archiveMode, config) {
289
510
  if (content) {
290
511
  pendingEvents.push([eventId, sessionId, ts, 'message', role, content, null, null, null]);
291
512
  msgCount++;
292
- // Better summary: skip heartbeat messages
293
- if (!summary && role === 'user' && !isHeartbeat(content)) {
513
+ // Better summary: skip heartbeat/boilerplate messages
514
+ if (!summary && role === 'user' && isSummaryCandidate(content)) {
294
515
  summary = content.slice(0, 200);
295
516
  }
296
517
  // Capture initial prompt from first substantial user message
297
- if (!initialPrompt && role === 'user' && content.trim().length > 10 && !isHeartbeat(content)) {
518
+ if (!initialPrompt && role === 'user' && isSummaryCandidate(content)) {
298
519
  initialPrompt = content.slice(0, 500); // Limit to 500 chars
299
520
  firstMessageId = eventId;
300
521
  firstMessageTimestamp = ts;
@@ -321,9 +542,25 @@ function indexFile(db, filePath, agentName, stmts, archiveMode, config) {
321
542
  }
322
543
  }
323
544
 
324
- // If no real summary found, check if it's a heartbeat session
545
+ // Classify snapshot-only Claude files explicitly (avoid heartbeat mislabel)
546
+ if (isClaudeCode && sawSnapshotRecord && !sawNonSnapshotRecord) {
547
+ sessionType = 'snapshot';
548
+ if (!summary) summary = 'Claude file snapshot';
549
+ }
550
+
551
+ // Normalize summary text
552
+ if (summary) summary = stripLeadingDatetimePrefix(summary);
553
+
554
+ // If no real summary found, set a sensible default
325
555
  if (!summary) {
326
- summary = 'Heartbeat session';
556
+ if (isCodexCli) {
557
+ const parts = ['Codex CLI session'];
558
+ if (codexProvider) parts.push(`provider=${codexProvider}`);
559
+ if (codexSource) parts.push(`source=${codexSource}`);
560
+ summary = parts.join(' · ');
561
+ } else {
562
+ summary = 'Heartbeat session';
563
+ }
327
564
  }
328
565
 
329
566
  // Infer session type from first user message content
@@ -389,9 +626,8 @@ function run() {
389
626
 
390
627
  let allFiles = [];
391
628
  for (const dir of sessionDirs) {
392
- const files = fs.readdirSync(dir.path)
393
- .filter(f => f.endsWith('.jsonl'))
394
- .map(f => ({ path: path.join(dir.path, f), agent: dir.agent }));
629
+ const files = listJsonlFiles(dir.path, !!dir.recursive)
630
+ .map(filePath => ({ path: filePath, agent: dir.agent }));
395
631
  allFiles.push(...files);
396
632
  }
397
633
 
@@ -418,8 +654,33 @@ function run() {
418
654
 
419
655
  if (WATCH) {
420
656
  console.log('\nWatching for changes...');
657
+ const rescanTimers = new Map();
658
+
421
659
  for (const dir of sessionDirs) {
422
660
  fs.watch(dir.path, { persistent: true }, (eventType, filename) => {
661
+ // Recursive sources (e.g. ~/.codex/sessions/YYYY/MM/DD/*.jsonl):
662
+ // fs.watch on Linux does not watch nested dirs recursively, so on any root event
663
+ // run a debounced full rescan of known JSONL files under this source.
664
+ if (dir.recursive) {
665
+ const key = dir.path;
666
+ if (rescanTimers.get(key)) clearTimeout(rescanTimers.get(key));
667
+ const t = setTimeout(() => {
668
+ try {
669
+ const files = listJsonlFiles(dir.path, true);
670
+ let changed = 0;
671
+ for (const filePath of files) {
672
+ const result = indexFile(db, filePath, dir.agent, stmts, archiveMode, config);
673
+ if (!result.skipped) changed++;
674
+ }
675
+ if (changed > 0) console.log(`Re-indexed ${changed} files (${dir.agent})`);
676
+ } catch (err) {
677
+ console.error(`Error rescanning ${dir.path}:`, err.message);
678
+ }
679
+ }, 500);
680
+ rescanTimers.set(key, t);
681
+ return;
682
+ }
683
+
423
684
  if (!filename || !filename.endsWith('.jsonl')) return;
424
685
  const filePath = path.join(dir.path, filename);
425
686
  if (!fs.existsSync(filePath)) return;
@@ -444,13 +705,13 @@ function indexAll(db, config) {
444
705
  const stmts = createStmts(db);
445
706
  let totalSessions = 0;
446
707
  for (const dir of sessionDirs) {
447
- const files = fs.readdirSync(dir.path).filter(f => f.endsWith('.jsonl'));
448
- for (const file of files) {
708
+ const files = listJsonlFiles(dir.path, !!dir.recursive);
709
+ for (const filePath of files) {
449
710
  try {
450
- const result = indexFile(db, path.join(dir.path, file), dir.agent, stmts, archiveMode, config);
711
+ const result = indexFile(db, filePath, dir.agent, stmts, archiveMode, config);
451
712
  if (!result.skipped) totalSessions++;
452
713
  } catch (err) {
453
- console.error(`Error indexing ${file}:`, err.message);
714
+ console.error(`Error indexing ${path.basename(filePath)}:`, err.message);
454
715
  }
455
716
  }
456
717
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentacta",
3
- "version": "1.3.2",
3
+ "version": "1.3.4",
4
4
  "description": "Audit trail and search engine for AI agent sessions",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -42,13 +42,13 @@
42
42
  },
43
43
  "homepage": "https://github.com/mirajchokshi/agentacta",
44
44
  "dependencies": {
45
- "better-sqlite3": "^11.0.0"
46
- },
47
- "devDependencies": {
48
- "puppeteer-core": "^24.37.3",
49
- "puppeteer-screen-recorder": "^3.0.6"
45
+ "better-sqlite3": "^12.6.2"
50
46
  },
51
47
  "engines": {
52
48
  "node": ">=18.0.0"
49
+ },
50
+ "overrides": {
51
+ "tar-fs": "^3.0.6",
52
+ "npmlog": "^7.0.1"
53
53
  }
54
54
  }
package/public/app.js CHANGED
@@ -226,12 +226,20 @@ function normalizeAgentLabel(a) {
226
226
  return a;
227
227
  }
228
228
 
229
+ function isInternalProjectTag(tag) {
230
+ if (!tag) return true;
231
+ if (tag.startsWith('agent:')) return true;
232
+ if (tag.startsWith('claude:')) return true;
233
+ return false;
234
+ }
235
+
229
236
  function renderProjectTags(s) {
230
237
  let projects = [];
231
238
  if (s.projects) {
232
239
  try { projects = JSON.parse(s.projects); } catch {}
233
240
  }
234
- return projects.map(p => `<span class="session-project">${escHtml(p)}</span>`).join('');
241
+ const visible = [...new Set(projects)].filter(p => !isInternalProjectTag(p));
242
+ return visible.map(p => `<span class="session-project">${escHtml(p)}</span>`).join('');
235
243
  }
236
244
 
237
245
  function renderModelTags(s) {
@@ -246,6 +254,8 @@ function renderModelTags(s) {
246
254
  function renderSessionItem(s) {
247
255
  const duration = fmtDuration(s.start_time, s.end_time);
248
256
  const timeRange = `${fmtTime(s.start_time)} \u2192 ${s.end_time ? fmtTimeOnly(s.end_time) : 'now'}`;
257
+ const isSubagent = s.session_type === 'subagent';
258
+ const showAgentTag = s.agent && s.agent !== 'main' && !isSubagent;
249
259
 
250
260
  return `
251
261
  <div class="session-item" data-id="${s.id}">
@@ -253,8 +263,8 @@ function renderSessionItem(s) {
253
263
  <span class="session-time">${timeRange} \u00b7 ${duration}</span>
254
264
  <span style="display:flex;gap:6px;align-items:center;flex-wrap:wrap">
255
265
  ${renderProjectTags(s)}
256
- ${s.agent && s.agent !== 'main' ? `<span class="session-agent">${escHtml(normalizeAgentLabel(s.agent))}</span>` : ''}
257
- ${s.session_type ? `<span class="session-type">${escHtml(s.session_type)}</span>` : ''}
266
+ ${showAgentTag ? `<span class="session-agent">${escHtml(normalizeAgentLabel(s.agent))}</span>` : ''}
267
+ ${s.session_type && s.session_type !== normalizeAgentLabel(s.agent || '') ? `<span class="session-type">${escHtml(s.session_type)}</span>` : ''}
258
268
  ${renderModelTags(s)}
259
269
  </span>
260
270
  </div>
@@ -459,7 +469,7 @@ async function viewSession(id) {
459
469
  <span style="display:flex;gap:6px;align-items:center;flex-wrap:wrap">
460
470
  ${renderProjectTags(s)}
461
471
  ${s.agent && s.agent !== 'main' ? `<span class="session-agent">${escHtml(normalizeAgentLabel(s.agent))}</span>` : ''}
462
- ${s.session_type ? `<span class="session-type">${escHtml(s.session_type)}</span>` : ''}
472
+ ${s.session_type && s.session_type !== normalizeAgentLabel(s.agent || '') ? `<span class="session-type">${escHtml(s.session_type)}</span>` : ''}
463
473
  ${renderModelTags(s)}
464
474
  </span>
465
475
  </div>
package/public/style.css CHANGED
@@ -405,8 +405,8 @@ body {
405
405
  .session-project {
406
406
  font-size: 11px;
407
407
  font-weight: 500;
408
- color: var(--green);
409
- background: var(--green-soft);
408
+ color: #7fb4ff;
409
+ background: rgba(39, 94, 182, 0.18);
410
410
  padding: 2px 10px;
411
411
  border-radius: 10px;
412
412
  }