entroly-wasm 1.0.62 → 1.0.63

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/index.d.ts CHANGED
@@ -55,6 +55,32 @@ export function wrapOpenAI<TClient>(client: TClient, options?: EntrolyAppSdkOpti
55
55
  export function wrapAnthropic<TClient>(client: TClient, options?: EntrolyAppSdkOptions): unknown;
56
56
  export function wrapGemini<TClient>(client: TClient, options?: EntrolyAppSdkOptions): unknown;
57
57
 
58
+ export interface EntrolyValueReceipt {
59
+ schema_version: "entroly.value-receipt.v1";
60
+ provider_path: Record<string, number | string>;
61
+ local_operations: Record<string, number | string>;
62
+ legacy_unclassified: Record<string, number | string>;
63
+ trust_signals: Record<string, number>;
64
+ pricing: { source: string; as_of: string };
65
+ generated_at_unix: number;
66
+ }
67
+
68
+ export class ValueTracker {
69
+ constructor(dataDir?: string | null);
70
+ record(input?: {
71
+ tokensSaved?: number;
72
+ model?: string;
73
+ duplicates?: number;
74
+ optimized?: boolean;
75
+ source?: "provider" | "proxy" | "gateway" | "sdk" | "npm" | "mcp" | "local" | string;
76
+ }): { tokensSaved: number; costSaved: number };
77
+ getTrends(): Record<string, unknown>;
78
+ getValueReceipt(): EntrolyValueReceipt;
79
+ }
80
+
81
+ export const EVOLUTION_TAX_RATE: number;
82
+ export function estimateCost(tokens: number, model?: string): number;
83
+
58
84
  export interface ContextReceiptDocument {
59
85
  source_path?: string;
60
86
  source?: string;
package/index.js CHANGED
@@ -144,7 +144,7 @@ module.exports = {
144
144
  TaskProfileOptimizer,
145
145
  FeedbackJournal,
146
146
 
147
- // Self-funded evolution budget (C_spent ≤ τ·S(t))
147
+ // Provider-classified evolution budget (C_spent ≤ τ·S_provider(t))
148
148
  ValueTracker,
149
149
  EVOLUTION_TAX_RATE,
150
150
  estimateCost,
package/js/cli.js CHANGED
@@ -8,6 +8,7 @@
8
8
  // entroly health Analyze codebase health (grade A-F)
9
9
  // entroly status Check if server is running
10
10
  // entroly stats Show session statistics
11
+ // entroly value Show an evidence-classified Context Value Receipt
11
12
  // entroly init Auto-detect project + AI tool, generate MCP config
12
13
  // entroly demo Before/after demo showing token savings
13
14
  // entroly clean Clear cached state
@@ -18,6 +19,7 @@ const { autoIndex } = require('./auto_index');
18
19
  const { persistIndex, loadIndex } = require('./checkpoint');
19
20
  const { EntrolyMCPServer } = require('./server');
20
21
  const { runAutotune } = require('./autotune');
22
+ const { getTracker } = require('./value_tracker');
21
23
  const path = require('path');
22
24
  const fs = require('fs');
23
25
  const os = require('os');
@@ -113,6 +115,29 @@ function cmdStats() {
113
115
  console.log(typeof stats === 'string' ? stats : JSON.stringify(stats, null, 2));
114
116
  }
115
117
 
118
+ function cmdValue(args) {
119
+ const receipt = getTracker().getValueReceipt();
120
+ if (args.includes('--json')) {
121
+ console.log(JSON.stringify(receipt, null, 2));
122
+ return;
123
+ }
124
+ const provider = receipt.provider_path;
125
+ const local = receipt.local_operations;
126
+ const legacy = receipt.legacy_unclassified;
127
+ console.log(banner());
128
+ console.log(`\n ${C.BOLD}Context Value Receipt${C.RESET}`);
129
+ console.log(` Provider-bound: ${provider.requests_observed} requests · ${provider.input_tokens_reduced.toLocaleString()} input tokens reduced`);
130
+ console.log(` Modeled API input cost avoided: $${provider.modeled_input_cost_avoided_usd.toFixed(4)} (not an invoice)`);
131
+ if (provider.unpriced_requests) {
132
+ console.log(` Unpriced provider traffic: ${provider.unpriced_requests} requests · ${provider.unpriced_input_tokens.toLocaleString()} reduced tokens`);
133
+ }
134
+ console.log(` Local-only: ${local.operations} operations · ${local.tokens_reduced.toLocaleString()} tokens reduced · $0 claimed`);
135
+ if (legacy.operations || legacy.tokens_reduced) {
136
+ console.log(` Legacy unclassified: ${legacy.operations} operations · ${legacy.tokens_reduced.toLocaleString()} tokens · $0 claimed`);
137
+ }
138
+ console.log(` Pricing: ${receipt.pricing.source}, as of ${receipt.pricing.as_of}`);
139
+ }
140
+
116
141
  function cmdInit() {
117
142
  console.log(banner());
118
143
  console.log();
@@ -286,6 +311,7 @@ function cmdHelp() {
286
311
  console.log(` ${C.CYAN}gateways${C.RESET} Stream evolution events to Telegram/Discord/Slack`);
287
312
  console.log(` ${C.CYAN}init${C.RESET} Auto-detect IDE and generate MCP config`);
288
313
  console.log(` ${C.CYAN}stats${C.RESET} Show session statistics`);
314
+ console.log(` ${C.CYAN}value${C.RESET} Show evidence-classified context value`);
289
315
  console.log(` ${C.CYAN}status${C.RESET} Check environment status`);
290
316
  console.log(` ${C.CYAN}autotune${C.RESET} Run autonomous self-tuning (args: [iterations] [--bench-only])`);
291
317
  console.log(` ${C.CYAN}clean${C.RESET} Clear cached state`);
@@ -306,6 +332,7 @@ switch (cmd) {
306
332
  case 'optimize': case 'opt': cmdOptimize(args); break;
307
333
  case 'health': cmdHealth(); break;
308
334
  case 'stats': cmdStats(); break;
335
+ case 'value': cmdValue(args); break;
309
336
  case 'init': cmdInit(); break;
310
337
  case 'demo': cmdDemo(); break;
311
338
  case 'gateways': case 'gateway': cmdGateways(); break;
@@ -1,10 +1,11 @@
1
1
  /**
2
- * ValueTracker — JS port of entroly/value_tracker.py (schema v3)
2
+ * ValueTracker — JS port of entroly/value_tracker.py (schema v4)
3
3
  *
4
- * Persistent, lifetime-savings accounting with the self-funded evolution
5
- * budget invariant:
4
+ * Persistent, evidence-classified value accounting. Provider-bound reductions
5
+ * may support modeled cost avoidance; local-only and legacy reductions do not.
6
+ * The bounded evolution budget uses only provider-classified value:
6
7
  *
7
- * C_spent(t) ≤ τ · S(t) (τ = 5%)
8
+ * C_spent(t) ≤ τ · S_provider(t) (τ = 5%)
8
9
  *
9
10
  * CROSS-RUNTIME CONTRACT: this writes the SAME file, SAME directory and
10
11
  * SAME JSON schema as the Python tracker so that one shared dashboard
@@ -26,7 +27,8 @@ const os = require('os');
26
27
  const EVOLUTION_TAX_RATE = 0.05;
27
28
  const FILE_NAME = 'value_tracker.json';
28
29
  const ACTIVITY_NAME = 'activity.jsonl';
29
- const SCHEMA_VERSION = 3;
30
+ const SCHEMA_VERSION = 4;
31
+ const PRICING_AS_OF = '2026-05';
30
32
  const MAX_DAILY = 90, MAX_WEEKLY = 52, MAX_MONTHLY = 24, MAX_ACTIVITY = 200;
31
33
 
32
34
  // Per-model $/1M tokens — kept in sync with entroly/value_tracker.py (×1000).
@@ -67,6 +69,20 @@ function estimateCost(tokens, model = '') {
67
69
  return (tokens / 1_000_000) * COST_PER_M[key || 'default'];
68
70
  }
69
71
 
72
+ function hasPricedModel(model = '') {
73
+ if (!model) return false;
74
+ let normalized = String(model).toLowerCase();
75
+ for (const [alias, canonical] of Object.entries(MODEL_ALIASES)) {
76
+ if (normalized.startsWith(alias)) {
77
+ normalized = canonical + normalized.slice(alias.length);
78
+ break;
79
+ }
80
+ }
81
+ return Object.keys(COST_PER_M)
82
+ .filter(key => key !== 'default')
83
+ .some(key => normalized.startsWith(key));
84
+ }
85
+
70
86
  // ── UTC date keys — MUST match Python time.gmtime()-based keys ──────────
71
87
 
72
88
  function _pad(n) { return String(n).padStart(2, '0'); }
@@ -127,6 +143,13 @@ class ValueTracker {
127
143
  lifetime: {
128
144
  tokens_saved: 0, cost_saved_usd: 0.0,
129
145
  requests_optimized: 0, requests_total: 0, duplicates_caught: 0,
146
+ provider_tokens_saved: 0, provider_cost_avoided_usd: 0.0,
147
+ provider_requests: 0, provider_requests_optimized: 0,
148
+ provider_unpriced_tokens: 0, provider_unpriced_requests: 0,
149
+ local_tokens_reduced: 0, local_operations: 0,
150
+ unclassified_tokens_reduced: 0,
151
+ unclassified_cost_estimate_usd: 0.0,
152
+ unclassified_operations: 0,
130
153
  first_seen: now, last_seen: now,
131
154
  evolution_spent_usd: 0.0, evolution_attempts: 0,
132
155
  evolution_successes: 0,
@@ -138,15 +161,57 @@ class ValueTracker {
138
161
  }
139
162
 
140
163
  _migrate(data) {
141
- // Backward-compatible forward migration (v2 → v3): backfill any
142
- // missing keys/buckets without touching existing counters.
164
+ // Backward-compatible forward migration: preserve old mixed counters as
165
+ // unclassified instead of presenting them as provider-bound savings.
143
166
  const base = this._defaults();
144
167
  data.lifetime = data.lifetime || {};
168
+ const previousVersion = Number(data.version || 0);
169
+ if (previousVersion < 4) {
170
+ const lt = data.lifetime;
171
+ if (!('unclassified_tokens_reduced' in lt)) {
172
+ lt.unclassified_tokens_reduced = Number(lt.tokens_saved || 0);
173
+ }
174
+ if (!('unclassified_cost_estimate_usd' in lt)) {
175
+ lt.unclassified_cost_estimate_usd = Number(lt.cost_saved_usd || 0);
176
+ }
177
+ if (!('unclassified_operations' in lt)) {
178
+ lt.unclassified_operations = Number(lt.requests_optimized || 0);
179
+ }
180
+ lt.cost_saved_usd = Number(lt.provider_cost_avoided_usd || 0);
181
+ }
145
182
  for (const [k, v] of Object.entries(base.lifetime)) {
146
183
  if (!(k in data.lifetime)) data.lifetime[k] = v;
147
184
  }
148
185
  for (const b of ['daily', 'weekly', 'monthly']) {
149
186
  if (!data[b] || typeof data[b] !== 'object') data[b] = {};
187
+ if (previousVersion < 4) {
188
+ for (const row of Object.values(data[b])) {
189
+ if (!row || typeof row !== 'object') continue;
190
+ if (!('unclassified_tokens_reduced' in row)) {
191
+ row.unclassified_tokens_reduced = Number(row.tokens_saved || 0);
192
+ }
193
+ if (!('unclassified_cost_estimate_usd' in row)) {
194
+ row.unclassified_cost_estimate_usd = Number(row.cost_saved || 0);
195
+ }
196
+ if (!('unclassified_operations' in row)) {
197
+ row.unclassified_operations = Number(row.requests || 0);
198
+ }
199
+ row.cost_saved = Number(row.provider_cost_avoided_usd || 0);
200
+ const periodDefaults = {
201
+ provider_tokens_saved: 0,
202
+ provider_cost_avoided_usd: 0.0,
203
+ provider_requests: 0,
204
+ provider_requests_optimized: 0,
205
+ provider_unpriced_tokens: 0,
206
+ provider_unpriced_requests: 0,
207
+ local_tokens_reduced: 0,
208
+ local_operations: 0,
209
+ };
210
+ for (const [field, value] of Object.entries(periodDefaults)) {
211
+ if (!(field in row)) row[field] = value;
212
+ }
213
+ }
214
+ }
150
215
  }
151
216
  data.version = SCHEMA_VERSION;
152
217
  return data;
@@ -179,12 +244,43 @@ class ValueTracker {
179
244
  } catch (_) { /* best-effort */ }
180
245
  }
181
246
 
182
- _bump(bucketName, key, tokens, cost) {
247
+ _bump(bucketName, key, tokens, cost, channel, optimized, providerPriced) {
183
248
  const bucket = this._data[bucketName] || (this._data[bucketName] = {});
184
- if (!bucket[key]) bucket[key] = { tokens_saved: 0, cost_saved: 0.0, requests: 0 };
185
- bucket[key].tokens_saved += tokens;
186
- bucket[key].cost_saved = +(bucket[key].cost_saved + cost).toFixed(6);
187
- bucket[key].requests += 1;
249
+ const defaults = {
250
+ tokens_saved: 0, cost_saved: 0.0, requests: 0,
251
+ provider_tokens_saved: 0, provider_cost_avoided_usd: 0.0,
252
+ provider_requests: 0, provider_requests_optimized: 0,
253
+ provider_unpriced_tokens: 0, provider_unpriced_requests: 0,
254
+ local_tokens_reduced: 0, local_operations: 0,
255
+ unclassified_tokens_reduced: 0,
256
+ unclassified_cost_estimate_usd: 0.0,
257
+ unclassified_operations: 0,
258
+ };
259
+ if (!bucket[key]) bucket[key] = { ...defaults };
260
+ const row = bucket[key];
261
+ for (const [field, value] of Object.entries(defaults)) {
262
+ if (!(field in row)) row[field] = value;
263
+ }
264
+ row.tokens_saved += tokens;
265
+ row.requests += 1;
266
+ if (channel === 'provider') {
267
+ row.cost_saved = +(row.cost_saved + cost).toFixed(6);
268
+ row.provider_tokens_saved += tokens;
269
+ row.provider_cost_avoided_usd = +(row.provider_cost_avoided_usd + cost).toFixed(6);
270
+ row.provider_requests += 1;
271
+ if (optimized) row.provider_requests_optimized += 1;
272
+ if (!providerPriced) {
273
+ row.provider_unpriced_tokens += tokens;
274
+ row.provider_unpriced_requests += 1;
275
+ }
276
+ } else if (channel === 'local') {
277
+ row.local_tokens_reduced += tokens;
278
+ row.local_operations += 1;
279
+ } else {
280
+ row.unclassified_tokens_reduced += tokens;
281
+ row.unclassified_cost_estimate_usd = +(row.unclassified_cost_estimate_usd + cost).toFixed(6);
282
+ row.unclassified_operations += 1;
283
+ }
188
284
  const limit = { daily: MAX_DAILY, weekly: MAX_WEEKLY, monthly: MAX_MONTHLY }[bucketName];
189
285
  const ks = Object.keys(bucket);
190
286
  if (ks.length > limit) {
@@ -193,19 +289,44 @@ class ValueTracker {
193
289
  }
194
290
  }
195
291
 
196
- record({ tokensSaved = 0, model = '', duplicates = 0, optimized = true } = {}) {
197
- const cost = estimateCost(tokensSaved, model);
292
+ record({ tokensSaved = 0, model = '', duplicates = 0, optimized = true, source = 'npm' } = {}) {
293
+ tokensSaved = Math.max(0, Number(tokensSaved) || 0);
294
+ const normalizedSource = String(source || 'unclassified').toLowerCase();
295
+ const channel = ['provider', 'proxy', 'gateway'].includes(normalizedSource)
296
+ ? 'provider'
297
+ : ['sdk', 'npm', 'mcp', 'local'].includes(normalizedSource)
298
+ ? 'local' : 'unclassified';
299
+ const estimatedCost = estimateCost(tokensSaved, model);
300
+ const providerPriced = channel !== 'provider' || hasPricedModel(model);
301
+ const cost = providerPriced ? estimatedCost : 0;
198
302
  const now = new Date();
199
303
  const lt = this._data.lifetime;
200
304
  lt.tokens_saved += tokensSaved;
201
- lt.cost_saved_usd = +(lt.cost_saved_usd + cost).toFixed(6);
202
305
  lt.requests_total = (lt.requests_total || 0) + 1;
203
306
  if (optimized) lt.requests_optimized += 1;
204
307
  lt.duplicates_caught = (lt.duplicates_caught || 0) + duplicates;
205
308
  lt.last_seen = Date.now() / 1000;
206
- this._bump('daily', _dayKey(now), tokensSaved, cost);
207
- this._bump('weekly', _weekKey(now), tokensSaved, cost);
208
- this._bump('monthly', _monthKey(now), tokensSaved, cost);
309
+ if (channel === 'provider') {
310
+ lt.cost_saved_usd = +(lt.cost_saved_usd + cost).toFixed(6);
311
+ lt.provider_tokens_saved += tokensSaved;
312
+ lt.provider_cost_avoided_usd = +(lt.provider_cost_avoided_usd + cost).toFixed(6);
313
+ lt.provider_requests += 1;
314
+ if (optimized) lt.provider_requests_optimized += 1;
315
+ if (!providerPriced) {
316
+ lt.provider_unpriced_tokens += tokensSaved;
317
+ lt.provider_unpriced_requests += 1;
318
+ }
319
+ } else if (channel === 'local') {
320
+ lt.local_tokens_reduced += tokensSaved;
321
+ lt.local_operations += 1;
322
+ } else {
323
+ lt.unclassified_tokens_reduced += tokensSaved;
324
+ lt.unclassified_cost_estimate_usd = +(lt.unclassified_cost_estimate_usd + estimatedCost).toFixed(6);
325
+ lt.unclassified_operations += 1;
326
+ }
327
+ this._bump('daily', _dayKey(now), tokensSaved, cost, channel, optimized, providerPriced);
328
+ this._bump('weekly', _weekKey(now), tokensSaved, cost, channel, optimized, providerPriced);
329
+ this._bump('monthly', _monthKey(now), tokensSaved, cost, channel, optimized, providerPriced);
209
330
  this._save();
210
331
  this._activity.push({
211
332
  ts: +(Date.now() / 1000).toFixed(3),
@@ -213,10 +334,12 @@ class ValueTracker {
213
334
  summary: `Optimized request: saved ${tokensSaved.toLocaleString()} tokens`
214
335
  + (model ? ` (${model})` : ''),
215
336
  tokens_saved: tokensSaved,
216
- cost_saved_usd: +cost.toFixed(6),
337
+ cost_saved_usd: +(channel === 'provider' ? cost : 0).toFixed(6),
338
+ modeled_cost_avoided_usd: +(channel === 'provider' ? cost : 0).toFixed(6),
217
339
  model: model || '',
218
340
  duplicates,
219
- source: 'npm',
341
+ source: normalizedSource,
342
+ measurement_channel: channel,
220
343
  });
221
344
  this._saveActivity();
222
345
  return { tokensSaved, costSaved: cost };
@@ -262,7 +385,7 @@ class ValueTracker {
262
385
 
263
386
  getEvolutionBudget() {
264
387
  const lt = this._data.lifetime;
265
- const lifetimeSaved = lt.cost_saved_usd || 0;
388
+ const lifetimeSaved = lt.provider_cost_avoided_usd || 0;
266
389
  const totalSpent = lt.evolution_spent_usd || 0;
267
390
  const totalEarned = lifetimeSaved * EVOLUTION_TAX_RATE;
268
391
  const available = Math.max(0, totalEarned - totalSpent);
@@ -277,7 +400,7 @@ class ValueTracker {
277
400
 
278
401
  recordEvolutionSpend(costUsd, success = false) {
279
402
  const lt = this._data.lifetime;
280
- const lifetimeSaved = lt.cost_saved_usd || 0;
403
+ const lifetimeSaved = lt.provider_cost_avoided_usd || 0;
281
404
  const currentSpent = lt.evolution_spent_usd || 0;
282
405
  const totalEarned = lifetimeSaved * EVOLUTION_TAX_RATE;
283
406
  const available = totalEarned - currentSpent;
@@ -311,6 +434,53 @@ class ValueTracker {
311
434
  };
312
435
  }
313
436
 
437
+ getValueReceipt() {
438
+ const lifetime = { ...this._data.lifetime };
439
+ const daily = this._sortedBucket('daily', 90);
440
+ const providerDays = daily.filter(row => Number(row.provider_requests || 0) > 0).length;
441
+ const localDays = daily.filter(row => Number(row.local_operations || 0) > 0).length;
442
+ return {
443
+ schema_version: 'entroly.value-receipt.v1',
444
+ provider_path: {
445
+ requests_observed: Number(lifetime.provider_requests || 0),
446
+ requests_optimized: Number(lifetime.provider_requests_optimized || 0),
447
+ input_tokens_reduced: Number(lifetime.provider_tokens_saved || 0),
448
+ modeled_input_cost_avoided_usd: Number(
449
+ Number(lifetime.provider_cost_avoided_usd || 0).toFixed(6)
450
+ ),
451
+ active_days: providerDays,
452
+ unpriced_requests: Number(lifetime.provider_unpriced_requests || 0),
453
+ unpriced_input_tokens: Number(lifetime.provider_unpriced_tokens || 0),
454
+ evidence: 'Pre/post token counts on provider-bound requests; modeled dollars are not invoices. Requests without an explicit catalog match remain unpriced.',
455
+ },
456
+ local_operations: {
457
+ operations: Number(lifetime.local_operations || 0),
458
+ tokens_reduced: Number(lifetime.local_tokens_reduced || 0),
459
+ active_days: localDays,
460
+ dollar_claimed_usd: 0.0,
461
+ evidence: 'npm, MCP, SDK, and local reductions; provider delivery is not observable.',
462
+ },
463
+ legacy_unclassified: {
464
+ operations: Number(lifetime.unclassified_operations || 0),
465
+ tokens_reduced: Number(lifetime.unclassified_tokens_reduced || 0),
466
+ historical_cost_estimate_usd: Number(
467
+ Number(lifetime.unclassified_cost_estimate_usd || 0).toFixed(6)
468
+ ),
469
+ dollar_claimed_usd: 0.0,
470
+ evidence: 'Preserved unknown-source history; excluded from provider savings.',
471
+ },
472
+ trust_signals: {
473
+ unsupported_claims_blocked: Number(lifetime.hallucinations_blocked || 0),
474
+ routing_decisions: Number(lifetime.routing_decisions || 0),
475
+ modeled_routing_cost_avoided_usd: Number(
476
+ Number(lifetime.routing_saved_usd || 0).toFixed(6)
477
+ ),
478
+ },
479
+ pricing: { source: 'bundled', as_of: PRICING_AS_OF },
480
+ generated_at_unix: +(Date.now() / 1000).toFixed(3),
481
+ };
482
+ }
483
+
314
484
  getActivity(lastN = 50) {
315
485
  return this._activity.slice(-lastN).reverse();
316
486
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "entroly-wasm",
3
- "version": "1.0.62",
4
- "description": "WebAssembly context engineering for AI agents: local compression, MCP tools, recovery, receipts, and verification without Python.",
3
+ "version": "1.0.63",
4
+ "description": "WebAssembly Context OS for AI agents: local context engineering, compression, recovery, receipts, and verification without Python.",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
7
7
  "bin": {
@@ -77,7 +77,7 @@
77
77
  "type": "git",
78
78
  "url": "https://github.com/juyterman1000/entroly"
79
79
  },
80
- "homepage": "https://juyterman1000.github.io/entroly/",
80
+ "homepage": "https://juyterman1000.github.io/entroly/docs/index.html",
81
81
  "bugs": {
82
82
  "url": "https://github.com/juyterman1000/entroly/issues"
83
83
  },
Binary file