clawkit-ai 1.1.1 → 1.1.3

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 (2) hide show
  1. package/package.json +13 -3
  2. package/src/sync.mjs +60 -25
package/package.json CHANGED
@@ -1,14 +1,24 @@
1
1
  {
2
2
  "name": "clawkit-ai",
3
- "version": "1.1.1",
3
+ "version": "1.1.3",
4
4
  "description": "Transform your OpenClaw agent into a production-ready AI assistant with memory, skills, and a dashboard",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "clawkit": "./bin/clawkit.mjs"
8
8
  },
9
- "keywords": ["openclaw", "ai", "agent", "skills", "memory", "dashboard", "clawkit"],
9
+ "keywords": [
10
+ "openclaw",
11
+ "ai",
12
+ "agent",
13
+ "skills",
14
+ "memory",
15
+ "dashboard",
16
+ "clawkit"
17
+ ],
10
18
  "author": "ClawKit",
11
19
  "license": "MIT",
12
- "engines": { "node": ">=18" },
20
+ "engines": {
21
+ "node": ">=18"
22
+ },
13
23
  "dependencies": {}
14
24
  }
package/src/sync.mjs CHANGED
@@ -149,6 +149,14 @@ function applyPendingChanges(workspace, changes, quiet) {
149
149
  if (change.type === 'model_switch' && change.model) {
150
150
  if (!quiet) log.info(`Model switch requested: ${change.model} (agent should pick this up)`);
151
151
  }
152
+
153
+ if (change.type === 'clear_context' || (change.type === 'preferences_update' && change.preferences?.clearContext)) {
154
+ const flagPath = join(workspace, 'logs', 'clear-context.flag');
155
+ const logsDir = join(workspace, 'logs');
156
+ if (!existsSync(logsDir)) mkdirSync(logsDir, { recursive: true });
157
+ writeFileSync(flagPath, JSON.stringify({ requested: change.timestamp || new Date().toISOString(), source: 'dashboard' }));
158
+ if (!quiet) log.ok('Clear context requested — flag written to logs/clear-context.flag');
159
+ }
152
160
  }
153
161
 
154
162
  if (toolsChanged) writeFileSync(toolsPath, tools);
@@ -424,33 +432,60 @@ function gatherState(workspace) {
424
432
  state.agentActivity = 'Waiting for instructions';
425
433
  }
426
434
 
427
- // --- Token Usage & Cost ---
428
- state.usage = { inputTokens: 0, outputTokens: 0, contextUsed: 0, contextMax: 0, costEstimate: '', model: '', compactions: 0 };
429
-
430
- // Try to read from OpenClaw gateway config for model
431
- try {
432
- const gwConfig = join(process.env.HOME || '', '.openclaw', 'config.yaml');
433
- if (existsSync(gwConfig)) {
434
- const cfg = readFileSync(gwConfig, 'utf8');
435
- const modelMatch = cfg.match(/model:\s*["']?([^\s"']+)/);
436
- if (modelMatch) state.usage.model = modelMatch[1];
435
+ // --- Token Usage & Cost + Context Breakdown ---
436
+ state.usage = { inputTokens: 0, outputTokens: 0, contextUsed: 0, contextMax: 0, costEstimate: '', model: '', compactions: 0, breakdown: [] };
437
+
438
+ // Calculate context breakdown from injected workspace files
439
+ const injectedFiles = ['AGENTS.md', 'SOUL.md', 'TOOLS.md', 'IDENTITY.md', 'USER.md', 'HEARTBEAT.md', 'MEMORY.md'];
440
+ let totalFileTokens = 0;
441
+ const breakdown = [];
442
+
443
+ for (const fname of injectedFiles) {
444
+ const fpath = join(workspace, fname);
445
+ if (existsSync(fpath)) {
446
+ const bytes = statSync(fpath).size;
447
+ const tokens = Math.round(bytes / 4); // ~4 chars per token
448
+ totalFileTokens += tokens;
449
+ breakdown.push({ label: fname.replace('.md', ''), tokens });
437
450
  }
438
- } catch {}
451
+ }
439
452
 
440
- // Try to get session stats from gateway HTTP API
441
- try {
442
- const statusOut = execSync('curl -s -m 3 http://127.0.0.1:18789/api/session/status 2>/dev/null || true', { timeout: 5000, encoding: 'utf8' });
443
- const statusMatch = statusOut.match(/\{[\s\S]*\}/);
444
- if (statusMatch) {
445
- const sd = JSON.parse(statusMatch[0]);
446
- if (sd.inputTokens || sd.tokensIn) state.usage.inputTokens = sd.inputTokens || sd.tokensIn || 0;
447
- if (sd.outputTokens || sd.tokensOut) state.usage.outputTokens = sd.outputTokens || sd.tokensOut || 0;
448
- if (sd.contextUsed) state.usage.contextUsed = sd.contextUsed;
449
- if (sd.contextMax) state.usage.contextMax = sd.contextMax;
450
- if (sd.compactions) state.usage.compactions = sd.compactions;
451
- if (sd.model) state.usage.model = sd.model;
452
- }
453
- } catch {}
453
+ // Add estimates for system prompt and tools
454
+ breakdown.push({ label: 'System Prompt', tokens: 3000 });
455
+ breakdown.push({ label: 'Tool Definitions', tokens: 2000 });
456
+ breakdown.push({ label: 'Skills List', tokens: 1500 });
457
+ totalFileTokens += 6500;
458
+
459
+ // Sort by size descending
460
+ breakdown.sort((a, b) => b.tokens - a.tokens);
461
+ state.usage.breakdown = breakdown;
462
+ state.usage.systemTokens = totalFileTokens;
463
+
464
+ // Read context stats from logs/context-stats.json (updated by agent)
465
+ const ctxStatsPath = join(workspace, 'logs', 'context-stats.json');
466
+ if (existsSync(ctxStatsPath)) {
467
+ try {
468
+ const cs = JSON.parse(readFileSync(ctxStatsPath, 'utf8'));
469
+ state.usage.model = cs.model || state.usage.model;
470
+ state.usage.inputTokens = cs.inputTokens || 0;
471
+ state.usage.outputTokens = cs.outputTokens || 0;
472
+ state.usage.contextUsed = cs.contextUsed || 0;
473
+ state.usage.contextMax = cs.contextMax || 0;
474
+ state.usage.compactions = cs.compactions || 0;
475
+ } catch {}
476
+ }
477
+
478
+ // Fallback: read model from gateway config
479
+ if (!state.usage.model) {
480
+ try {
481
+ const gwPath = join(process.env.HOME || '', '.openclaw', 'openclaw.json');
482
+ if (existsSync(gwPath)) {
483
+ const gw = JSON.parse(readFileSync(gwPath, 'utf8'));
484
+ const primary = gw.agents?.defaults?.model?.primary;
485
+ if (primary) state.usage.model = primary;
486
+ }
487
+ } catch {}
488
+ }
454
489
 
455
490
  // If we couldn't get from CLI, estimate from daily note sizes
456
491
  if (!state.usage.model) {