context-mode 1.0.117 → 1.0.118

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.
@@ -6,14 +6,14 @@
6
6
  },
7
7
  "metadata": {
8
8
  "description": "Claude Code plugins by Mert Koseoğlu",
9
- "version": "1.0.117"
9
+ "version": "1.0.118"
10
10
  },
11
11
  "plugins": [
12
12
  {
13
13
  "name": "context-mode",
14
14
  "source": "./",
15
15
  "description": "Claude Code MCP plugin that saves 98% of your context window. Sandboxed code execution in 11 languages, FTS5 knowledge base with BM25 ranking, and intent-driven search.",
16
- "version": "1.0.117",
16
+ "version": "1.0.118",
17
17
  "author": {
18
18
  "name": "Mert Koseoğlu"
19
19
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "context-mode",
3
- "version": "1.0.117",
3
+ "version": "1.0.118",
4
4
  "description": "MCP server that saves 98% of your context window with session continuity. Sandboxed code execution in 11 languages, FTS5 knowledge base with BM25 ranking, and automatic state restore across compactions.",
5
5
  "author": {
6
6
  "name": "Mert Koseoğlu",
@@ -3,7 +3,7 @@
3
3
  "name": "Context Mode",
4
4
  "kind": "tool",
5
5
  "description": "OpenClaw plugin that saves 98% of your context window. Sandboxed code execution in 11 languages, FTS5 knowledge base with BM25 ranking, and intent-driven search.",
6
- "version": "1.0.117",
6
+ "version": "1.0.118",
7
7
  "sandbox": {
8
8
  "mode": "permissive",
9
9
  "filesystem_access": "full",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "context-mode",
3
- "version": "1.0.117",
3
+ "version": "1.0.118",
4
4
  "description": "OpenClaw plugin that saves 98% of your context window. Sandboxed code execution in 11 languages, FTS5 knowledge base with BM25 ranking, and intent-driven search.",
5
5
  "author": {
6
6
  "name": "Mert Koseoğlu",
@@ -212,7 +212,7 @@ async function main() {
212
212
  const {
213
213
  getRealBytesStats,
214
214
  getMultiAdapterLifetimeStats,
215
- OPUS_INPUT_PRICE_PER_TOKEN,
215
+ kb,
216
216
  } = analytics;
217
217
 
218
218
  // Sessions dir doesn't exist yet — first ever launch
@@ -251,14 +251,31 @@ async function main() {
251
251
  multi = null;
252
252
  }
253
253
 
254
- const PRICE = OPUS_INPUT_PRICE_PER_TOKEN ?? (15 / 1_000_000);
255
- const lifetimeTokens = lifetime?.totalSavedTokens ?? 0;
256
- const sessionTokens = conversation?.totalSavedTokens ?? 0;
257
- const lifetimeUsd = lifetimeTokens * PRICE;
258
- const sessionUsd = sessionTokens * PRICE;
254
+ // v1.0.118: drop the $ math ctx_stats's narrative renderer is the source
255
+ // of truth and uses byte-based metrics. Statusline mirrors the same
256
+ // formulas so the two displays never diverge again.
257
+ //
258
+ // Lifetime bytes multi-adapter aggregate when present, else local-DB
259
+ // real bytes. Mirrors src/session/analytics.ts:1684 narrative renderer.
260
+ const lifetimeBytes = (multi?.totalBytes && multi.totalBytes > 0)
261
+ ? multi.totalBytes
262
+ : (lifetime?.totalSavedTokens ?? 0) * 4;
259
263
 
260
- // Reduction % — bytes avoided + snapshot bytes vs returned bytes.
261
- // Mirrors persistStats() math in src/server.ts:565-568.
264
+ // This-chat bytesreal bytes accounting (data + bytes-avoided + snapshot).
265
+ const sessionBytes = conversation
266
+ ? ((conversation.eventDataBytes ?? 0)
267
+ + (conversation.bytesAvoided ?? 0)
268
+ + (conversation.snapshotBytes ?? 0))
269
+ : 0;
270
+
271
+ // Per-day average — same lifetime-day computation ctx_stats opener uses.
272
+ const sinceMs = lifetime?.firstEventMs ?? multi?.perAdapter?.[0]?.firstMs ?? 0;
273
+ const lifetimeDays = sinceMs > 0
274
+ ? Math.max(1, Math.round((Date.now() - sinceMs) / 86_400_000))
275
+ : 0;
276
+ const perDayBytes = lifetimeDays > 0 ? lifetimeBytes / lifetimeDays : 0;
277
+
278
+ // Reduction % — same as before (bytes-avoided + snapshot vs returned).
262
279
  const totalReturned = lifetime?.bytesReturned ?? 0;
263
280
  const totalKept =
264
281
  (lifetime?.bytesAvoided ?? 0)
@@ -271,36 +288,26 @@ async function main() {
271
288
 
272
289
  const dot = statusDot(pct);
273
290
 
274
- // Multi-adapter aggregation. Real adapters = those passing the isReal
275
- // filter (>=100 events, >=5 distinct projects, recent activity, avg
276
- // bytes >= 50). When 2+ real adapters exist, surface a cross-tool $.
277
- // multi.totalBytes is dataBytes + rescueBytes, NOT bytes-avoided — so
278
- // it's a different (and typically smaller) lens than getRealBytesStats.
279
- // Render the multi $ alongside lifetime $ rather than instead of it.
291
+ // Cross-tool count used in the headline when 2+ real adapters detected.
280
292
  const realAdapters = (multi?.perAdapter ?? []).filter((a) => a?.isReal);
281
- const multiTotalTokens = (multi?.totalBytes ?? 0) / 4;
282
- const multiUsd = multiTotalTokens * PRICE;
283
- const showMultiAdapter = realAdapters.length >= 2 && multiUsd > 0;
284
-
285
- // BRAND-NEW: no local SessionDB data at all → headline.
286
- // Multi-adapter alone (without local data) means another tool has
287
- // history but THIS Claude session is fresh — still show headline,
288
- // not someone else's lifetime $, to avoid surprising users with a
289
- // number they can't trace to their current adapter.
290
- if (lifetimeTokens === 0 && sessionTokens === 0) {
293
+ const showMultiAdapter = realAdapters.length >= 2;
294
+
295
+ // BRAND-NEW: no data at all marketing headline.
296
+ if (lifetimeBytes === 0 && sessionBytes === 0) {
291
297
  process.stdout.write(
292
298
  `${brand("context-mode")} ${green("●")} ${dim("saves ~98% of context window")}`,
293
299
  );
294
300
  return;
295
301
  }
296
302
 
297
- // FRESH session, no session $ yet — lead with persistence value.
298
- if (sessionUsd === 0 && lifetimeUsd > 0) {
299
- const blocks = [
300
- `${bold(fmtUsd(lifetimeUsd))} ${dim("saved across sessions")}`,
301
- ];
303
+ // FRESH session, no this-chat data yet — lead with lifetime number.
304
+ if (sessionBytes === 0 && lifetimeBytes > 0) {
305
+ const blocks = [`${bold(kb(lifetimeBytes))} ${dim("kept out")}`];
306
+ if (perDayBytes > 0) {
307
+ blocks.push(`${bold(kb(perDayBytes) + "/day")}`);
308
+ }
302
309
  if (showMultiAdapter) {
303
- blocks.push(`${bold(fmtUsd(multiUsd))} ${dim(`across ${realAdapters.length} tools`)}`);
310
+ blocks.push(`${dim(`across ${realAdapters.length} tools`)}`);
304
311
  }
305
312
  blocks.push(dim("preserved across compact, restart & upgrade"));
306
313
  process.stdout.write(
@@ -309,18 +316,18 @@ async function main() {
309
316
  return;
310
317
  }
311
318
 
312
- // ACTIVE: session $ · lifetime $ · [multi $] · % efficient
319
+ // ACTIVE: this-chat · lifetime · [N tools] · % efficient
313
320
  const valueBlocks = [
314
- `${bold(fmtUsd(sessionUsd))} ${dim("saved this session")}`,
321
+ `${bold(kb(sessionBytes))} ${dim("this chat")}`,
315
322
  ];
316
- if (lifetimeUsd > 0) {
317
- valueBlocks.push(`${bold(fmtUsd(lifetimeUsd))} ${dim("saved across sessions")}`);
323
+ if (lifetimeBytes > 0) {
324
+ valueBlocks.push(`${bold(kb(lifetimeBytes))} ${dim("lifetime")}`);
318
325
  }
319
326
  if (showMultiAdapter) {
320
- valueBlocks.push(`${bold(fmtUsd(multiUsd))} ${dim(`across ${realAdapters.length} tools`)}`);
327
+ valueBlocks.push(`${dim(`across ${realAdapters.length} tools`)}`);
321
328
  }
322
329
  if (pct > 0) {
323
- valueBlocks.push(`${bold(`${pct}%`)} ${dim("efficient")}`);
330
+ valueBlocks.push(`${bold(`${pct}%`)} ${dim("kept out")}`);
324
331
  }
325
332
 
326
333
  const head = `${brand("context-mode")} ${dot} `;
@@ -480,6 +480,27 @@ export declare const autoMemoryLabels: Record<string, string>;
480
480
  * each tool's own surface area.
481
481
  */
482
482
  export declare const adapterLabels: Record<string, string>;
483
+ /**
484
+ * Format a byte count for the narrative dashboard.
485
+ *
486
+ * Single-unit auto-scale (Grafana / CloudWatch / Datadog convention).
487
+ * Decimals shrink as the integer part grows so the number stays readable
488
+ * at every magnitude. Max output width is 8 characters which fits the
489
+ * existing `padStart(8)` callsites in Sections 1, 3, 4.
490
+ *
491
+ * < 1 KB → "X B" e.g. "100 B"
492
+ * 1 KB – < 100 KB → "X.Y KB" e.g. "4.7 KB", "92.8 KB"
493
+ * 100 KB – < 1 MB → "X KB" e.g. "227 KB", "976 KB"
494
+ * 1 MB – < 100 MB → "X.Y MB" e.g. "4.5 MB", "11.6 MB"
495
+ * 100 MB – < 1 GB → "X MB" e.g. "178 MB", "906 MB"
496
+ * 1 GB – < 100 GB → "X.YY GB" e.g. "1.00 GB", "11.36 GB"
497
+ * ≥ 100 GB → "X.Y GB" e.g. "216.6 GB"
498
+ *
499
+ * Replaced the dual-unit "X KB (0.YY MB)" form because the parenthetical
500
+ * rounded to 0.00 / 0.01 in the common range and added noise without
501
+ * information. Scale awareness comes from the unit jump between rows.
502
+ */
503
+ export declare function kb(b: number): string;
483
504
  /**
484
505
  * Locale + IANA-timezone detection for the narrative renderer.
485
506
  *
@@ -1059,7 +1059,7 @@ function adapterLabel(name) {
1059
1059
  * rounded to 0.00 / 0.01 in the common range and added noise without
1060
1060
  * information. Scale awareness comes from the unit jump between rows.
1061
1061
  */
1062
- function kb(b) {
1062
+ export function kb(b) {
1063
1063
  if (!Number.isFinite(b) || b <= 0)
1064
1064
  return "0 B";
1065
1065
  if (b < 1024)
@@ -3,7 +3,7 @@
3
3
  "name": "Context Mode",
4
4
  "kind": "tool",
5
5
  "description": "OpenClaw plugin that saves 98% of your context window. Sandboxed code execution in 11 languages, FTS5 knowledge base with BM25 ranking, and intent-driven search.",
6
- "version": "1.0.117",
6
+ "version": "1.0.118",
7
7
  "sandbox": {
8
8
  "mode": "permissive",
9
9
  "filesystem_access": "full",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "context-mode",
3
- "version": "1.0.117",
3
+ "version": "1.0.118",
4
4
  "type": "module",
5
5
  "description": "MCP plugin that saves 98% of your context window. Works with Claude Code, Gemini CLI, VS Code Copilot, OpenCode, and Codex CLI. Sandboxed code execution, FTS5 knowledge base, and intent-driven search.",
6
6
  "author": "Mert Koseoğlu",