entroly-wasm 0.9.0

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.
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Workspace Change Listener — pure-JS port of entroly/change_listener.py
3
+ * Detects file changes, marks beliefs stale, recompiles, verifies.
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+ const { SOURCE_EXTS } = require('./vault');
11
+
12
+ class WorkspaceChangeListener {
13
+ constructor(vault, compiler, verifier, changePipe, projectDir) {
14
+ this._vault = vault;
15
+ this._compiler = compiler;
16
+ this._verifier = verifier;
17
+ this._changePipe = changePipe;
18
+ this._projectDir = projectDir || process.cwd();
19
+ this._lastScan = {}; // file -> mtime
20
+ this._watcher = null;
21
+ this._running = false;
22
+ }
23
+
24
+ scanOnce(force = false, maxFiles = 100) {
25
+ const changes = { new: [], modified: [], deleted: [] };
26
+ const currentFiles = {};
27
+ const walk = (dir, depth = 0) => {
28
+ if (depth > 8) return;
29
+ let entries;
30
+ try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return; }
31
+ for (const e of entries) {
32
+ if (e.name.startsWith('.') || e.name === 'node_modules' || e.name === '__pycache__' || e.name === 'target') continue;
33
+ const full = path.join(dir, e.name);
34
+ if (e.isDirectory()) { walk(full, depth + 1); continue; }
35
+ if (!SOURCE_EXTS.has(path.extname(e.name))) continue;
36
+ try {
37
+ const stat = fs.statSync(full);
38
+ const mtime = stat.mtimeMs;
39
+ const rel = path.relative(this._projectDir, full).replace(/\\/g, '/');
40
+ currentFiles[rel] = mtime;
41
+ if (!this._lastScan[rel]) changes.new.push(rel);
42
+ else if (force || mtime > this._lastScan[rel]) changes.modified.push(rel);
43
+ } catch {}
44
+ }
45
+ };
46
+ walk(this._projectDir);
47
+
48
+ // Detect deletions
49
+ for (const rel of Object.keys(this._lastScan)) {
50
+ if (!currentFiles[rel]) changes.deleted.push(rel);
51
+ }
52
+ this._lastScan = currentFiles;
53
+
54
+ const allChanged = [...changes.new, ...changes.modified].slice(0, maxFiles);
55
+ let beliefsStaled = 0, beliefsRecompiled = 0;
56
+
57
+ if (allChanged.length > 0) {
58
+ const staleResult = this._vault.markBeliefsStaleForFiles(allChanged);
59
+ beliefsStaled = staleResult.updated_entities ? staleResult.updated_entities.length : 0;
60
+
61
+ // Recompile changed files
62
+ for (const rel of allChanged.slice(0, 20)) {
63
+ try {
64
+ const fp = path.join(this._projectDir, rel);
65
+ if (fs.existsSync(fp)) {
66
+ const dir = path.dirname(fp);
67
+ const result = this._compiler.compileDirectory(dir, 1);
68
+ beliefsRecompiled += result.beliefs_written;
69
+ }
70
+ } catch {}
71
+ }
72
+ }
73
+
74
+ return {
75
+ status: 'scanned',
76
+ new_files: changes.new.length,
77
+ modified_files: changes.modified.length,
78
+ deleted_files: changes.deleted.length,
79
+ beliefs_staled: beliefsStaled,
80
+ beliefs_recompiled: beliefsRecompiled,
81
+ total_tracked: Object.keys(currentFiles).length,
82
+ engine: 'javascript',
83
+ };
84
+ }
85
+
86
+ start(intervalS = 120, maxFiles = 100, forceInitial = false) {
87
+ if (this._running) return { status: 'already_running', engine: 'javascript' };
88
+ this._running = true;
89
+
90
+ // Initial scan
91
+ const initial = this.scanOnce(forceInitial, maxFiles);
92
+
93
+ // Set up interval
94
+ this._interval = setInterval(() => {
95
+ try { this.scanOnce(false, maxFiles); } catch {}
96
+ }, intervalS * 1000);
97
+
98
+ // Unref so it doesn't keep the process alive
99
+ if (this._interval.unref) this._interval.unref();
100
+
101
+ return {
102
+ status: 'started',
103
+ interval_s: intervalS,
104
+ initial_scan: initial,
105
+ engine: 'javascript',
106
+ };
107
+ }
108
+
109
+ stop() {
110
+ if (this._interval) { clearInterval(this._interval); this._interval = null; }
111
+ this._running = false;
112
+ return { status: 'stopped', engine: 'javascript' };
113
+ }
114
+ }
115
+
116
+ module.exports = { WorkspaceChangeListener };
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "entroly-wasm",
3
+ "version": "0.9.0",
4
+ "description": "Information-theoretic context optimization for AI coding agents. Zero-friction, pure WebAssembly — no Python dependency.",
5
+ "main": "index.js",
6
+ "types": "pkg/entroly_wasm.d.ts",
7
+ "bin": {
8
+ "entroly-wasm": "./js/cli.js"
9
+ },
10
+ "files": [
11
+ "index.js",
12
+ "pkg/entroly_wasm_bg.wasm",
13
+ "pkg/entroly_wasm.js",
14
+ "pkg/entroly_wasm.d.ts",
15
+ "js/cli.js",
16
+ "js/server.js",
17
+ "js/config.js",
18
+ "js/auto_index.js",
19
+ "js/checkpoint.js",
20
+ "js/autotune.js",
21
+ "js/vault.js",
22
+ "js/cogops.js",
23
+ "js/workspace.js",
24
+ "js/repo_map.js",
25
+ "js/skills.js",
26
+ "js/multimodal.js",
27
+ "js/value_tracker.js",
28
+ "js/agentskills_export.js",
29
+ "js/gateways.js",
30
+ "js/vault_observer.js",
31
+ "js/distill.js",
32
+ "js/federation.js",
33
+ "js/evolution_daemon.js"
34
+ ],
35
+ "scripts": {
36
+ "serve": "node js/server.js",
37
+ "optimize": "node js/cli.js optimize",
38
+ "health": "node js/cli.js health",
39
+ "demo": "node js/cli.js demo",
40
+ "autotune": "node js/cli.js autotune",
41
+ "test": "node test_wasm_e2e.js"
42
+ },
43
+ "keywords": [
44
+ "entroly", "context-optimization", "mcp", "mcp-server", "ai", "agents",
45
+ "wasm", "webassembly", "information-theory", "knapsack", "entropy", "autotune"
46
+ ],
47
+ "repository": { "type": "git", "url": "https://github.com/juyterman1000/entroly" },
48
+ "author": "entroly",
49
+ "license": "Apache-2.0",
50
+ "engines": { "node": ">=16.0.0" }
51
+ }
@@ -0,0 +1,156 @@
1
+ /* tslint:disable */
2
+ /* eslint-disable */
3
+
4
+ export class WasmEntrolyEngine {
5
+ free(): void;
6
+ [Symbol.dispose](): void;
7
+ /**
8
+ * Advance the turn counter and apply Ebbinghaus decay.
9
+ */
10
+ advance_turn(): void;
11
+ /**
12
+ * Analyze codebase health (A-F grade).
13
+ */
14
+ analyze_health(): any;
15
+ /**
16
+ * Clear EGSC cache.
17
+ */
18
+ cache_clear(): void;
19
+ /**
20
+ * Cache hit rate.
21
+ */
22
+ cache_hit_rate(): number;
23
+ /**
24
+ * Cache empty check.
25
+ */
26
+ cache_is_empty(): boolean;
27
+ /**
28
+ * Cache entry count.
29
+ */
30
+ cache_len(): number;
31
+ /**
32
+ * Classify a task query.
33
+ */
34
+ classify_task(query: string): any;
35
+ /**
36
+ * Clear all fragments and reset engine.
37
+ */
38
+ clear(): void;
39
+ /**
40
+ * Dependency graph stats.
41
+ */
42
+ dep_graph_stats(): any;
43
+ /**
44
+ * Detect entropy anomalies.
45
+ */
46
+ entropy_anomalies(): any;
47
+ /**
48
+ * Explain why each fragment was selected/excluded in last optimization.
49
+ */
50
+ explain_selection(): any;
51
+ /**
52
+ * Export all fragments as JSON.
53
+ */
54
+ export_fragments(): any;
55
+ /**
56
+ * Export full engine state as JSON string.
57
+ */
58
+ export_state(): any;
59
+ /**
60
+ * Get fragment count.
61
+ */
62
+ fragment_count(): number;
63
+ /**
64
+ * Get the current turn number.
65
+ */
66
+ get_turn(): number;
67
+ /**
68
+ * Hierarchical context compression.
69
+ */
70
+ hierarchical_compress(token_budget: number, query: string): any;
71
+ /**
72
+ * Import engine state from JSON string.
73
+ */
74
+ import_state(json_str: string): any;
75
+ /**
76
+ * Ingest a context fragment into the engine.
77
+ * Pipeline: tokens → SimHash → dedup → entropy → criticality → depgraph → store
78
+ */
79
+ ingest(content: string, source: string, token_count: number, is_pinned: boolean): any;
80
+ /**
81
+ * Create a new Entroly engine with default parameters.
82
+ */
83
+ constructor();
84
+ /**
85
+ * Full optimization pipeline: IOS + PRISM + Channel Coding + Resonance + Causal.
86
+ */
87
+ optimize(token_budget: number, query: string): any;
88
+ /**
89
+ * Query manifold stats.
90
+ */
91
+ query_manifold_stats(): any;
92
+ /**
93
+ * Semantic recall of relevant fragments.
94
+ */
95
+ recall(query: string, top_k: number): any;
96
+ /**
97
+ * Record failed outcome for specific fragment IDs.
98
+ */
99
+ record_failure(fragment_ids_json: string): void;
100
+ /**
101
+ * Record a continuous reward signal.
102
+ */
103
+ record_reward(fragment_ids_json: string, reward: number): void;
104
+ /**
105
+ * Record successful outcome for specific fragment IDs.
106
+ */
107
+ record_success(fragment_ids_json: string): void;
108
+ /**
109
+ * Remove a fragment by ID.
110
+ */
111
+ remove(fragment_id: string): boolean;
112
+ /**
113
+ * Scan a fragment for security vulnerabilities.
114
+ */
115
+ scan_fragment(fragment_id: string): any;
116
+ /**
117
+ * Score context utilization from LLM response.
118
+ */
119
+ score_utilization(response: string): any;
120
+ /**
121
+ * Security report for all fragments.
122
+ */
123
+ security_report(): any;
124
+ /**
125
+ * Semantic dedup report.
126
+ */
127
+ semantic_dedup_report(): any;
128
+ /**
129
+ * Set cost-per-token directly.
130
+ */
131
+ set_cache_cost_per_token(cost: number): void;
132
+ /**
133
+ * Enable/disable channel coding.
134
+ */
135
+ set_channel_coding_enabled(enabled: boolean): void;
136
+ /**
137
+ * Set exploration rate.
138
+ */
139
+ set_exploration_rate(rate: number): void;
140
+ /**
141
+ * Set cost model from model name.
142
+ */
143
+ set_model(model_name: string): void;
144
+ /**
145
+ * Enable/disable query personas.
146
+ */
147
+ set_query_personas_enabled(enabled: boolean): void;
148
+ /**
149
+ * Set scoring weights (normalized to sum=1.0).
150
+ */
151
+ set_weights(w_recency: number, w_frequency: number, w_semantic: number, w_entropy: number): void;
152
+ /**
153
+ * Get full engine statistics as JSON.
154
+ */
155
+ stats(): any;
156
+ }