@digitalforgestudios/openclaw-sulcus 2.0.0 → 3.1.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.
- package/index.ts +223 -212
- package/openclaw.plugin.json +23 -15
- package/package.json +4 -2
- package/wasm/sulcus_wasm.js +607 -0
- package/wasm/sulcus_wasm_bg.wasm +0 -0
package/index.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { spawn, ChildProcess } from "node:child_process";
|
|
2
|
-
import { createInterface } from "node:readline";
|
|
3
1
|
import { resolve } from "node:path";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
4
3
|
import { Type } from "@sinclair/typebox";
|
|
5
4
|
|
|
6
5
|
// ─── STATIC AWARENESS ───────────────────────────────────────────────────────
|
|
@@ -8,154 +7,174 @@ import { Type } from "@sinclair/typebox";
|
|
|
8
7
|
// This is the absolute minimum the LLM needs to know Sulcus exists.
|
|
9
8
|
// It fires even if build_context crashes, times out, or returns empty.
|
|
10
9
|
// Build static awareness with runtime backend info
|
|
11
|
-
function buildStaticAwareness(backendMode: string, namespace: string
|
|
10
|
+
function buildStaticAwareness(backendMode: string, namespace: string) {
|
|
12
11
|
return `## Persistent Memory (Sulcus)
|
|
13
12
|
You have Sulcus — a persistent, reactive, thermodynamic memory system with reactive triggers.
|
|
14
13
|
Memories survive across sessions. They have heat (0.0–1.0) that decays over time.
|
|
15
14
|
|
|
16
|
-
**Connection:** Backend: ${backendMode} | Namespace: ${namespace}
|
|
15
|
+
**Connection:** Backend: ${backendMode} | Namespace: ${namespace}
|
|
17
16
|
|
|
18
17
|
**Your memory tools:**
|
|
19
18
|
- \`memory_store\` — Save important information (preferences, facts, procedures, decisions, lessons)
|
|
20
|
-
Parameters: content, memory_type (episodic|semantic|preference|procedural|fact)
|
|
19
|
+
Parameters: content, memory_type (episodic|semantic|preference|procedural|fact)
|
|
21
20
|
- \`memory_recall\` — Search memories semantically. Use before answering about past work, decisions, or people.
|
|
22
21
|
Parameters: query, limit
|
|
23
22
|
|
|
24
23
|
**When to store:** User states a preference, important decision made, correction given, lesson learned, anything worth surviving this session.
|
|
25
24
|
**When to search:** Questions about prior work/decisions, context seems incomplete, user references past conversations.
|
|
26
25
|
|
|
27
|
-
**Memory types:** episodic (events, fast decay) · semantic (knowledge, slow) · preference (opinions, slower) · procedural (how-tos, slowest) · fact (data, slow)
|
|
28
|
-
**Decay classes:** volatile (hours) · normal (days) · stable (weeks) · permanent (never)
|
|
29
|
-
**Pinning:** is_pinned=true prevents decay. Use for critical knowledge.
|
|
30
|
-
**Triggers:** Reactive rules on memory events. Active triggers and recent fires appear in your context below.`;
|
|
26
|
+
**Memory types:** episodic (events, fast decay) · semantic (knowledge, slow) · preference (opinions, slower) · procedural (how-tos, slowest) · fact (data, slow)`;
|
|
31
27
|
}
|
|
32
28
|
|
|
33
29
|
// Legacy static string for backward compat (overwritten at register time)
|
|
34
|
-
let STATIC_AWARENESS = buildStaticAwareness("local", "default"
|
|
30
|
+
let STATIC_AWARENESS = buildStaticAwareness("local", "default");
|
|
35
31
|
|
|
36
32
|
// Fallback context when build_context fails — includes the cheatsheet
|
|
37
33
|
// but warns that dynamic context is unavailable.
|
|
38
34
|
const FALLBACK_AWARENESS = `<sulcus_context token_budget="500">
|
|
39
35
|
<cheatsheet>
|
|
40
36
|
You have Sulcus — persistent memory with reactive triggers.
|
|
41
|
-
STORE: memory_store (content, memory_type
|
|
37
|
+
STORE: memory_store (content, memory_type)
|
|
42
38
|
FIND: memory_recall (query, limit)
|
|
43
|
-
MANAGE: memory_boost / memory_deprecate / memory_relate / memory_reclassify
|
|
44
|
-
PIN: Set is_pinned=true to make a memory permanent (immune to decay).
|
|
45
|
-
TRIGGERS: create_trigger to set reactive rules on your memory graph
|
|
46
39
|
TYPES: episodic (fast fade), semantic (slow), preference, procedural (slowest), fact
|
|
47
40
|
⚠️ Context build failed this turn — use memory_recall to search manually.
|
|
48
41
|
Below is your active context. Search for deeper recall. Unlimited storage.
|
|
49
42
|
</cheatsheet>
|
|
50
43
|
</sulcus_context>`;
|
|
51
44
|
|
|
52
|
-
//
|
|
53
|
-
//
|
|
54
|
-
//
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
private
|
|
59
|
-
private
|
|
60
|
-
private
|
|
61
|
-
|
|
62
|
-
|
|
45
|
+
// ─── NATIVE LIB LOADER ──────────────────────────────────────────────────────
|
|
46
|
+
// Loads libsulcus_store.dylib (embedded PG) and libsulcus_vectors.dylib (embeddings)
|
|
47
|
+
// via koffi FFI. Provides queryFn and embedFn callbacks for SulcusMem.create().
|
|
48
|
+
|
|
49
|
+
class NativeLibLoader {
|
|
50
|
+
private koffi: any = null;
|
|
51
|
+
private storeLib: any = null;
|
|
52
|
+
private vectorsLib: any = null;
|
|
53
|
+
private vectorsHandle: any = null;
|
|
54
|
+
|
|
55
|
+
// koffi function handles
|
|
56
|
+
private fn_store_init: any = null;
|
|
57
|
+
private fn_store_query: any = null;
|
|
58
|
+
private fn_store_free: any = null;
|
|
59
|
+
private fn_vectors_create: any = null;
|
|
60
|
+
private fn_vectors_text: any = null;
|
|
61
|
+
private fn_vectors_free: any = null;
|
|
62
|
+
|
|
63
|
+
public loaded = false;
|
|
64
|
+
public error: string | null = null;
|
|
63
65
|
|
|
64
66
|
constructor(
|
|
65
|
-
private
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
) {
|
|
69
|
-
this.configPath = configPath;
|
|
70
|
-
this.spawnEnv = spawnEnv;
|
|
71
|
-
}
|
|
67
|
+
private storeLibPath: string,
|
|
68
|
+
private vectorsLibPath: string
|
|
69
|
+
) {}
|
|
72
70
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
env: {
|
|
83
|
-
...process.env,
|
|
84
|
-
RUST_LOG: "info",
|
|
85
|
-
...this.spawnEnv, // SULCUS_SERVER_URL and SULCUS_API_KEY forwarded here
|
|
86
|
-
}
|
|
87
|
-
});
|
|
71
|
+
init(logger: any): void {
|
|
72
|
+
try {
|
|
73
|
+
// koffi is a pure-JS FFI library — no native compilation needed
|
|
74
|
+
this.koffi = require("koffi");
|
|
75
|
+
} catch (e: any) {
|
|
76
|
+
this.error = `koffi not available: ${e.message}`;
|
|
77
|
+
logger.warn(`memory-sulcus: ${this.error}`);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
88
80
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
81
|
+
// ── Load libsulcus_store.dylib ──
|
|
82
|
+
if (!existsSync(this.storeLibPath)) {
|
|
83
|
+
this.error = `libsulcus_store not found at ${this.storeLibPath}`;
|
|
84
|
+
logger.warn(`memory-sulcus: ${this.error}`);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (!existsSync(this.vectorsLibPath)) {
|
|
88
|
+
this.error = `libsulcus_vectors not found at ${this.vectorsLibPath}`;
|
|
89
|
+
logger.warn(`memory-sulcus: ${this.error}`);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
this.storeLib = this.koffi.load(this.storeLibPath);
|
|
95
|
+
this.fn_store_init = this.storeLib.func("sulcus_store_init", "int", ["str", "uint16"]);
|
|
96
|
+
this.fn_store_query = this.storeLib.func("sulcus_store_query", "char*", ["str"]);
|
|
97
|
+
this.fn_store_free = this.storeLib.func("sulcus_store_free_string", "void", ["char*"]);
|
|
98
|
+
} catch (e: any) {
|
|
99
|
+
this.error = `Failed to load libsulcus_store: ${e.message}`;
|
|
100
|
+
logger.warn(`memory-sulcus: ${this.error}`);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
97
103
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
104
|
+
try {
|
|
105
|
+
this.vectorsLib = this.koffi.load(this.vectorsLibPath);
|
|
106
|
+
this.fn_vectors_create = this.vectorsLib.func("sulcus_vectors_create", "void*", []);
|
|
107
|
+
this.fn_vectors_text = this.vectorsLib.func("sulcus_vectors_text", "char*", ["void*", "str"]);
|
|
108
|
+
this.fn_vectors_free = this.vectorsLib.func("sulcus_vectors_free_string", "void", ["char*"]);
|
|
109
|
+
} catch (e: any) {
|
|
110
|
+
this.error = `Failed to load libsulcus_vectors: ${e.message}`;
|
|
111
|
+
logger.warn(`memory-sulcus: ${this.error}`);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// ── Initialise embedded PG store ──
|
|
116
|
+
try {
|
|
117
|
+
const dataDir = resolve(process.env.HOME || "~", ".sulcus/data");
|
|
118
|
+
const port = 15432;
|
|
119
|
+
const rc = this.fn_store_init(dataDir, port);
|
|
120
|
+
if (rc !== 0) {
|
|
121
|
+
this.error = `sulcus_store_init returned ${rc}`;
|
|
122
|
+
logger.warn(`memory-sulcus: ${this.error}`);
|
|
123
|
+
return;
|
|
101
124
|
}
|
|
102
|
-
|
|
103
|
-
this.
|
|
104
|
-
|
|
125
|
+
} catch (e: any) {
|
|
126
|
+
this.error = `sulcus_store_init failed: ${e.message}`;
|
|
127
|
+
logger.warn(`memory-sulcus: ${this.error}`);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
105
130
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
});
|
|
131
|
+
// ── Create embed handle ──
|
|
132
|
+
try {
|
|
133
|
+
this.vectorsHandle = this.fn_vectors_create();
|
|
134
|
+
} catch (e: any) {
|
|
135
|
+
this.error = `sulcus_vectors_create failed: ${e.message}`;
|
|
136
|
+
logger.warn(`memory-sulcus: ${this.error}`);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
this.loaded = true;
|
|
141
|
+
logger.info(`memory-sulcus: native libs loaded (store: ${this.storeLibPath}, vectors: ${this.vectorsLibPath})`);
|
|
117
142
|
}
|
|
118
143
|
|
|
119
|
-
async
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
});
|
|
139
|
-
this.child!.stdin!.write(JSON.stringify(request) + "\n");
|
|
140
|
-
});
|
|
144
|
+
// queryFn: async (sql: string, params: any[]) => any[]
|
|
145
|
+
// Calls sulcus_store_query which accepts plain SQL (params inlined by caller or
|
|
146
|
+
// passed as a JSON payload — the store lib handles parameterisation internally).
|
|
147
|
+
makeQueryFn(): (sql: string, params: any[]) => Promise<any[]> {
|
|
148
|
+
return async (sql: string, _params: any[]): Promise<any[]> => {
|
|
149
|
+
if (!this.loaded) throw new Error("Sulcus store not available");
|
|
150
|
+
// The store lib's query function takes a single JSON-encoded request
|
|
151
|
+
const payload = JSON.stringify({ sql, params: _params });
|
|
152
|
+
const raw: string = this.fn_store_query(payload);
|
|
153
|
+
if (!raw) return [];
|
|
154
|
+
try {
|
|
155
|
+
const parsed = JSON.parse(raw);
|
|
156
|
+
return Array.isArray(parsed) ? parsed : (parsed?.rows ?? [parsed]);
|
|
157
|
+
} finally {
|
|
158
|
+
// NOTE: koffi manages string memory automatically for char* returns;
|
|
159
|
+
// no manual free needed with koffi's default charset handling.
|
|
160
|
+
// If the ABI requires explicit free, call fn_store_free here.
|
|
161
|
+
}
|
|
162
|
+
};
|
|
141
163
|
}
|
|
142
164
|
|
|
143
|
-
|
|
144
|
-
|
|
165
|
+
// embedFn: async (text: string) => Float32Array
|
|
166
|
+
// Calls sulcus_vectors_text which returns a JSON float array string.
|
|
167
|
+
makeEmbedFn(): (text: string) => Promise<Float32Array> {
|
|
168
|
+
return async (text: string): Promise<Float32Array> => {
|
|
169
|
+
if (!this.loaded) throw new Error("Sulcus vectors not available");
|
|
170
|
+
const raw: string = this.fn_vectors_text(this.vectorsHandle, text);
|
|
171
|
+
if (!raw) throw new Error("sulcus_vectors_text returned null");
|
|
172
|
+
const arr: number[] = JSON.parse(raw);
|
|
173
|
+
return new Float32Array(arr);
|
|
174
|
+
};
|
|
145
175
|
}
|
|
146
176
|
}
|
|
147
177
|
|
|
148
|
-
// NOTE: SulcusRestClient was removed — the plugin no longer makes any HTTP/REST
|
|
149
|
-
// calls. All communication with sulcus-local goes through MCP over stdio via
|
|
150
|
-
// SulcusClient above. If serverUrl/apiKey are provided in config, they are
|
|
151
|
-
// forwarded as SULCUS_SERVER_URL / SULCUS_API_KEY environment variables to the
|
|
152
|
-
// sulcus-local spawn so that sulcus-sync (dylib) can replicate to the cloud
|
|
153
|
-
// without this plugin touching the network.
|
|
154
|
-
|
|
155
|
-
// NOTE: SiuClassifier / ClientSiu / SiuModel were removed — sulcus-local
|
|
156
|
-
// already has fastembed + ONNX for embeddings and handles classification
|
|
157
|
-
// internally. A future PR may expose classification via an MCP tool if needed.
|
|
158
|
-
|
|
159
178
|
// ─── PRE-SEND FILTER ─────────────────────────────────────────────────────────
|
|
160
179
|
// Rule-based junk filter. Catches obvious noise before it hits the API.
|
|
161
180
|
|
|
@@ -176,65 +195,83 @@ const JUNK_PATTERNS = [
|
|
|
176
195
|
|
|
177
196
|
function isJunkMemory(text: string): boolean {
|
|
178
197
|
if (!text || text.length < 10) return true;
|
|
179
|
-
if (text.length > 10000) return true;
|
|
198
|
+
if (text.length > 10000) return true;
|
|
180
199
|
for (const pattern of JUNK_PATTERNS) {
|
|
181
200
|
if (pattern.test(text.trim())) return true;
|
|
182
201
|
}
|
|
183
202
|
return false;
|
|
184
203
|
}
|
|
185
204
|
|
|
205
|
+
// ─── PLUGIN ──────────────────────────────────────────────────────────────────
|
|
206
|
+
|
|
186
207
|
const sulcusPlugin = {
|
|
187
208
|
id: "memory-sulcus",
|
|
188
209
|
name: "Sulcus vMMU",
|
|
189
|
-
description: "Sulcus-backed vMMU memory for OpenClaw — thermodynamic decay, reactive triggers, local-first
|
|
210
|
+
description: "Sulcus-backed vMMU memory for OpenClaw — thermodynamic decay, reactive triggers, local-first",
|
|
190
211
|
kind: "memory" as const,
|
|
191
212
|
|
|
192
213
|
register(api: any) {
|
|
193
|
-
|
|
194
|
-
const
|
|
214
|
+
// ── Configuration ──
|
|
215
|
+
const libDir = api.config?.libDir
|
|
216
|
+
? resolve(api.config.libDir)
|
|
217
|
+
: resolve(process.env.HOME || "~", ".sulcus/lib");
|
|
218
|
+
|
|
219
|
+
const storeLibPath = api.config?.storeLibPath
|
|
220
|
+
? resolve(api.config.storeLibPath)
|
|
221
|
+
: resolve(libDir, process.platform === "darwin" ? "libsulcus_store.dylib" : "libsulcus_store.so");
|
|
222
|
+
|
|
223
|
+
const vectorsLibPath = api.config?.vectorsLibPath
|
|
224
|
+
? resolve(api.config.vectorsLibPath)
|
|
225
|
+
: resolve(libDir, process.platform === "darwin" ? "libsulcus_vectors.dylib" : "libsulcus_vectors.so");
|
|
226
|
+
|
|
227
|
+
const wasmDir = api.config?.wasmDir
|
|
228
|
+
? resolve(api.config.wasmDir)
|
|
229
|
+
: resolve(__dirname, "wasm");
|
|
230
|
+
|
|
195
231
|
// Default namespace = agent name (prevents everything landing in "default")
|
|
196
|
-
// Priority: explicit namespace config > agentId config > pluginConfig.agentId > "default"
|
|
197
232
|
const agentId = api.config?.agentId || api.pluginConfig?.agentId;
|
|
198
233
|
const namespace = api.config?.namespace === "default" && agentId
|
|
199
234
|
? agentId
|
|
200
235
|
: (api.config?.namespace || agentId || "default");
|
|
201
236
|
|
|
202
|
-
//
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
const serverUrl = api.config?.serverUrl || "";
|
|
206
|
-
const apiKey = api.config?.apiKey || "";
|
|
207
|
-
|
|
208
|
-
// Build spawn env: pass cloud config to sulcus-local via environment
|
|
209
|
-
const spawnEnv: Record<string, string> = {};
|
|
210
|
-
if (serverUrl) spawnEnv["SULCUS_SERVER_URL"] = serverUrl;
|
|
211
|
-
if (apiKey) spawnEnv["SULCUS_API_KEY"] = apiKey;
|
|
237
|
+
// ── Load native dylibs ──
|
|
238
|
+
const nativeLoader = new NativeLibLoader(storeLibPath, vectorsLibPath);
|
|
239
|
+
nativeLoader.init(api.logger);
|
|
212
240
|
|
|
213
|
-
|
|
241
|
+
// ── Load WASM module ──
|
|
242
|
+
let sulcusMem: any = null;
|
|
243
|
+
let backendMode = "unavailable";
|
|
214
244
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
245
|
+
if (nativeLoader.loaded) {
|
|
246
|
+
const wasmJsPath = resolve(wasmDir, "sulcus_wasm.js");
|
|
247
|
+
if (existsSync(wasmJsPath)) {
|
|
248
|
+
try {
|
|
249
|
+
const { SulcusMem, on_init } = require(wasmJsPath);
|
|
250
|
+
// on_init sets up WASM internals (panic hooks etc.)
|
|
251
|
+
if (typeof on_init === "function") on_init();
|
|
252
|
+
|
|
253
|
+
const queryFn = nativeLoader.makeQueryFn();
|
|
254
|
+
const embedFn = nativeLoader.makeEmbedFn();
|
|
255
|
+
sulcusMem = SulcusMem.create(queryFn, embedFn);
|
|
256
|
+
backendMode = "wasm";
|
|
257
|
+
api.logger.info(`memory-sulcus: SulcusMem created via WASM (wasm: ${wasmJsPath})`);
|
|
258
|
+
} catch (e: any) {
|
|
259
|
+
api.logger.warn(`memory-sulcus: WASM load failed: ${e.message}`);
|
|
260
|
+
backendMode = "unavailable";
|
|
261
|
+
}
|
|
262
|
+
} else {
|
|
263
|
+
api.logger.warn(`memory-sulcus: WASM module not found at ${wasmJsPath}`);
|
|
230
264
|
}
|
|
231
|
-
|
|
265
|
+
} else {
|
|
266
|
+
api.logger.warn(`memory-sulcus: native libs unavailable — ${nativeLoader.error}`);
|
|
232
267
|
}
|
|
233
268
|
|
|
269
|
+
const isAvailable = sulcusMem !== null;
|
|
270
|
+
|
|
234
271
|
// Update static awareness with runtime info
|
|
235
|
-
STATIC_AWARENESS = buildStaticAwareness(backendMode, namespace
|
|
272
|
+
STATIC_AWARENESS = buildStaticAwareness(backendMode, namespace);
|
|
236
273
|
|
|
237
|
-
api.logger.info(`memory-sulcus: registered (
|
|
274
|
+
api.logger.info(`memory-sulcus: registered (backend: ${backendMode}, namespace: ${namespace}, available: ${isAvailable})`);
|
|
238
275
|
|
|
239
276
|
// ── Core memory tools ──
|
|
240
277
|
|
|
@@ -247,12 +284,14 @@ const sulcusPlugin = {
|
|
|
247
284
|
limit: Type.Optional(Type.Number({ default: 5, description: "Maximum number of results to return (1-10)." }))
|
|
248
285
|
}),
|
|
249
286
|
async execute(_id: string, params: any) {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
287
|
+
if (!isAvailable) {
|
|
288
|
+
throw new Error(`Sulcus unavailable: ${nativeLoader.error || "WASM not loaded"}`);
|
|
289
|
+
}
|
|
290
|
+
const res = await sulcusMem.search_memory(params.query, params.limit ?? 5);
|
|
291
|
+
const results = res?.results ?? res;
|
|
253
292
|
return {
|
|
254
293
|
content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
|
|
255
|
-
details: {
|
|
294
|
+
details: { results, backend: backendMode, namespace }
|
|
256
295
|
};
|
|
257
296
|
}
|
|
258
297
|
}, { name: "memory_recall" });
|
|
@@ -260,10 +299,9 @@ const sulcusPlugin = {
|
|
|
260
299
|
api.registerTool({
|
|
261
300
|
name: "memory_store",
|
|
262
301
|
label: "Memory Store",
|
|
263
|
-
description: "Record information in Sulcus memory. Supports Markdown formatting. You control the memory type
|
|
302
|
+
description: "Record information in Sulcus memory. Supports Markdown formatting. You control the memory type at creation time.",
|
|
264
303
|
parameters: Type.Object({
|
|
265
304
|
content: Type.String({ description: "Memory content. Supports Markdown formatting for structured content." }),
|
|
266
|
-
fold_name: Type.Optional(Type.String({ description: `Memory namespace/fold. Defaults to "${namespace}" (agent namespace).` })),
|
|
267
305
|
memory_type: Type.Optional(Type.Union([
|
|
268
306
|
Type.Literal("episodic"),
|
|
269
307
|
Type.Literal("semantic"),
|
|
@@ -271,15 +309,6 @@ const sulcusPlugin = {
|
|
|
271
309
|
Type.Literal("procedural"),
|
|
272
310
|
Type.Literal("fact")
|
|
273
311
|
], { description: "Memory type. preference=user preferences, procedural=how-to/processes, fact=stable knowledge, semantic=concepts/relationships, episodic=events/experiences. Default: episodic" })),
|
|
274
|
-
decay_class: Type.Optional(Type.Union([
|
|
275
|
-
Type.Literal("volatile"),
|
|
276
|
-
Type.Literal("normal"),
|
|
277
|
-
Type.Literal("stable"),
|
|
278
|
-
Type.Literal("permanent")
|
|
279
|
-
], { description: "Decay rate. volatile=fast decay, normal=default, stable=slow decay, permanent=never decays" })),
|
|
280
|
-
is_pinned: Type.Optional(Type.Boolean({ description: "Pin memory to freeze heat at current value, preventing ALL decay. Pinned memories never lose heat." })),
|
|
281
|
-
min_heat: Type.Optional(Type.Number({ description: "Minimum heat floor (0.0-1.0). Memory will never decay below this value." })),
|
|
282
|
-
key_points: Type.Optional(Type.Array(Type.String(), { description: "Key points to index for search. Extracted highlights." }))
|
|
283
312
|
}),
|
|
284
313
|
async execute(_id: string, params: any) {
|
|
285
314
|
// Pre-send junk filter
|
|
@@ -291,25 +320,14 @@ const sulcusPlugin = {
|
|
|
291
320
|
};
|
|
292
321
|
}
|
|
293
322
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
if (res?.error === "storage_limit_reached") {
|
|
297
|
-
return {
|
|
298
|
-
content: [{ type: "text", text: `⚠️ Storage limit reached: ${res.message}` }],
|
|
299
|
-
details: res
|
|
300
|
-
};
|
|
323
|
+
if (!isAvailable) {
|
|
324
|
+
throw new Error(`Sulcus unavailable: ${nativeLoader.error || "WASM not loaded"}`);
|
|
301
325
|
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
namespace,
|
|
305
|
-
server: serverUrl || "local",
|
|
306
|
-
sync_available: !!serverUrl,
|
|
307
|
-
siu_classified: false,
|
|
308
|
-
};
|
|
309
|
-
const provenanceStr = `[${provenance.backend}] namespace: ${provenance.namespace || namespace}, server: ${provenance.server || "local"}`;
|
|
326
|
+
|
|
327
|
+
const res = await sulcusMem.add_memory(params.content, params.memory_type ?? null);
|
|
310
328
|
return {
|
|
311
|
-
content: [{ type: "text", text: `Stored [${params.memory_type || "episodic"}] memory: "${(params.content || "").substring(0, 80)}..." → ${
|
|
312
|
-
details: { ...res,
|
|
329
|
+
content: [{ type: "text", text: `Stored [${params.memory_type || "episodic"}] memory: "${(params.content || "").substring(0, 80)}..." → [${backendMode}] namespace: ${namespace}` }],
|
|
330
|
+
details: { ...res, backend: backendMode, namespace }
|
|
313
331
|
};
|
|
314
332
|
}
|
|
315
333
|
}, { name: "memory_store" });
|
|
@@ -317,26 +335,35 @@ const sulcusPlugin = {
|
|
|
317
335
|
api.registerTool({
|
|
318
336
|
name: "memory_status",
|
|
319
337
|
label: "Memory Status",
|
|
320
|
-
description: "Check Sulcus memory backend status: connection, namespace, capabilities, and
|
|
338
|
+
description: "Check Sulcus memory backend status: connection, namespace, capabilities, and hot nodes.",
|
|
321
339
|
parameters: Type.Object({}),
|
|
322
340
|
async execute(_id: string, _params: any) {
|
|
323
|
-
if (!
|
|
341
|
+
if (!isAvailable) {
|
|
324
342
|
return {
|
|
325
343
|
content: [{ type: "text", text: JSON.stringify({
|
|
326
344
|
status: "unavailable",
|
|
327
345
|
backend: backendMode,
|
|
328
346
|
namespace,
|
|
329
|
-
|
|
330
|
-
|
|
347
|
+
error: nativeLoader.error || "WASM not loaded",
|
|
348
|
+
storeLib: storeLibPath,
|
|
349
|
+
vectorsLib: vectorsLibPath,
|
|
350
|
+
wasmDir,
|
|
331
351
|
}, null, 2) }],
|
|
332
352
|
};
|
|
333
353
|
}
|
|
334
354
|
try {
|
|
335
|
-
|
|
336
|
-
const
|
|
355
|
+
const hotNodes = await sulcusMem.list_hot_nodes(20);
|
|
356
|
+
const nodeList = hotNodes?.nodes ?? hotNodes ?? [];
|
|
357
|
+
const count = Array.isArray(nodeList) ? nodeList.length : 0;
|
|
337
358
|
return {
|
|
338
|
-
content: [{ type: "text", text: JSON.stringify(
|
|
339
|
-
|
|
359
|
+
content: [{ type: "text", text: JSON.stringify({
|
|
360
|
+
status: "ok",
|
|
361
|
+
backend: backendMode,
|
|
362
|
+
namespace,
|
|
363
|
+
hot_node_count: count,
|
|
364
|
+
hot_nodes: nodeList,
|
|
365
|
+
}, null, 2) }],
|
|
366
|
+
details: { status: "ok", backend: backendMode, namespace, count }
|
|
340
367
|
};
|
|
341
368
|
} catch (e: any) {
|
|
342
369
|
return {
|
|
@@ -344,7 +371,6 @@ const sulcusPlugin = {
|
|
|
344
371
|
status: "error",
|
|
345
372
|
backend: backendMode,
|
|
346
373
|
namespace,
|
|
347
|
-
server: serverUrl || "none (env forwarding only)",
|
|
348
374
|
error: e.message,
|
|
349
375
|
}, null, 2) }],
|
|
350
376
|
};
|
|
@@ -371,32 +397,23 @@ const sulcusPlugin = {
|
|
|
371
397
|
api.logger.debug(`memory-sulcus: autoRecall is disabled, skipping context build`);
|
|
372
398
|
return;
|
|
373
399
|
}
|
|
400
|
+
if (!isAvailable) return;
|
|
374
401
|
api.logger.info(`memory-sulcus: before_agent_start hook triggered for agent ${event.agentId}`);
|
|
375
402
|
if (!event.prompt) return;
|
|
376
403
|
try {
|
|
377
|
-
api.logger.debug(`memory-sulcus:
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
// - plain XML string (new format, post-4dca467)
|
|
383
|
-
// - { context: "...", token_estimate: N } (old format)
|
|
384
|
-
// The MCP client resolves to the parsed JSON if valid, or the raw MCP result object.
|
|
385
|
-
let context: string | undefined;
|
|
386
|
-
if (typeof res === "string") {
|
|
387
|
-
context = res;
|
|
388
|
-
} else if (res?.context) {
|
|
389
|
-
context = res.context;
|
|
390
|
-
} else if (res?.content?.[0]?.text) {
|
|
391
|
-
context = res.content[0].text;
|
|
404
|
+
api.logger.debug(`memory-sulcus: searching context for prompt: ${event.prompt.substring(0, 50)}...`);
|
|
405
|
+
const res = await sulcusMem.search_memory(event.prompt, 5);
|
|
406
|
+
const results = res?.results ?? [];
|
|
407
|
+
if (!results || results.length === 0) {
|
|
408
|
+
return { prependSystemContext: FALLBACK_AWARENESS };
|
|
392
409
|
}
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
api.logger.
|
|
399
|
-
return { prependSystemContext:
|
|
410
|
+
// Format results as a concise XML context block
|
|
411
|
+
const items = results.map((r: any) =>
|
|
412
|
+
` <memory id="${r.id}" heat="${(r.current_heat ?? r.score ?? 0).toFixed(2)}" type="${r.memory_type ?? "unknown"}">${r.label ?? r.pointer_summary ?? ""}</memory>`
|
|
413
|
+
).join("\n");
|
|
414
|
+
const context = `<sulcus_context token_budget="500" namespace="${namespace}">\n${items}\n</sulcus_context>`;
|
|
415
|
+
api.logger.info(`memory-sulcus: injecting ${results.length} recalled memories (${context.length} chars)`);
|
|
416
|
+
return { prependSystemContext: context };
|
|
400
417
|
} catch (e) {
|
|
401
418
|
// build_context failed — inject fallback so the LLM isn't flying blind
|
|
402
419
|
api.logger.warn(`memory-sulcus: context build failed: ${e} — injecting fallback awareness`);
|
|
@@ -405,18 +422,12 @@ const sulcusPlugin = {
|
|
|
405
422
|
});
|
|
406
423
|
|
|
407
424
|
// agent_end: Do NOT auto-record raw conversation turns.
|
|
408
|
-
// The LLM has
|
|
409
|
-
// Auto-recording every turn flooded the store with 2000+ junk episodic nodes
|
|
410
|
-
// containing placeholder vectors and raw JSON conversation payloads.
|
|
425
|
+
// The LLM has memory_store as a tool — it decides what's worth remembering.
|
|
411
426
|
api.on("agent_end", async (event: any) => {
|
|
412
427
|
api.logger.debug(`memory-sulcus: agent_end hook triggered for agent ${event.agentId} (no auto-record)`);
|
|
413
428
|
});
|
|
414
429
|
|
|
415
|
-
|
|
416
|
-
id: "memory-sulcus",
|
|
417
|
-
start: () => client.start(),
|
|
418
|
-
stop: () => client.stop()
|
|
419
|
-
});
|
|
430
|
+
// No service registration needed — no background process to manage
|
|
420
431
|
}
|
|
421
432
|
};
|
|
422
433
|
|
package/openclaw.plugin.json
CHANGED
|
@@ -2,23 +2,23 @@
|
|
|
2
2
|
"id": "openclaw-sulcus",
|
|
3
3
|
"kind": "memory",
|
|
4
4
|
"name": "Sulcus",
|
|
5
|
-
"description": "Reactive, thermodynamic memory for AI agents.
|
|
5
|
+
"description": "Reactive, thermodynamic memory for AI agents. Runs entirely local via WASM + native dylibs (embedded PostgreSQL, sulcus-vectors). No network calls. Optional cloud sync via sulcus-sync dylib when serverUrl/apiKey are configured.",
|
|
6
6
|
"privacy": {
|
|
7
|
-
"consentModel": "
|
|
8
|
-
"consentNote": "Plugin
|
|
9
|
-
"crossNamespaceAccess": "
|
|
10
|
-
"crossNamespaceNote": "
|
|
7
|
+
"consentModel": "local-first",
|
|
8
|
+
"consentNote": "Plugin runs entirely in-process. No network calls. All data stays local in ~/.sulcus/. Cloud sync is opt-in via serverUrl/apiKey config, handled by sulcus-sync dylib loaded by sulcus-local.",
|
|
9
|
+
"crossNamespaceAccess": "local-only",
|
|
10
|
+
"crossNamespaceNote": "All memory operations are local. Cross-namespace access only applies if cloud sync is configured separately.",
|
|
11
11
|
"dataFlows": [
|
|
12
12
|
{
|
|
13
|
-
"direction": "
|
|
14
|
-
"destination": "
|
|
15
|
-
"data": "Memory text content, search queries",
|
|
16
|
-
"trigger": "User invokes memory_store/
|
|
17
|
-
"auth": "
|
|
13
|
+
"direction": "local-only",
|
|
14
|
+
"destination": "Local embedded PostgreSQL (~/.sulcus/data/)",
|
|
15
|
+
"data": "Memory text content, embeddings, search queries",
|
|
16
|
+
"trigger": "User invokes memory_store/memory_recall tools. Auto-recall opt-in via config.",
|
|
17
|
+
"auth": "None — all local"
|
|
18
18
|
}
|
|
19
19
|
],
|
|
20
|
-
"storage": "Memories stored
|
|
21
|
-
"optOut": "autoRecall
|
|
20
|
+
"storage": "Memories stored locally in embedded PostgreSQL at ~/.sulcus/data/. Zero network by default.",
|
|
21
|
+
"optOut": "autoRecall defaults to false. No automatic context injection unless explicitly enabled."
|
|
22
22
|
},
|
|
23
23
|
"configSchema": {
|
|
24
24
|
"type": "object",
|
|
@@ -26,12 +26,20 @@
|
|
|
26
26
|
"properties": {
|
|
27
27
|
"serverUrl": {
|
|
28
28
|
"type": "string",
|
|
29
|
-
"description": "Sulcus server URL"
|
|
30
|
-
"default": "https://api.sulcus.ca"
|
|
29
|
+
"description": "Sulcus cloud server URL (for sulcus-sync replication). Passed as SULCUS_SERVER_URL env var. Leave empty for local-only."
|
|
31
30
|
},
|
|
32
31
|
"apiKey": {
|
|
33
32
|
"type": "string",
|
|
34
|
-
"description": "Sulcus API key (
|
|
33
|
+
"description": "Sulcus API key (for sulcus-sync replication). Passed as SULCUS_API_KEY env var. Leave empty for local-only."
|
|
34
|
+
},
|
|
35
|
+
"libDir": {
|
|
36
|
+
"type": "string",
|
|
37
|
+
"description": "Directory containing native dylibs (libsulcus_store, libsulcus_vectors)",
|
|
38
|
+
"default": "~/.sulcus/lib"
|
|
39
|
+
},
|
|
40
|
+
"wasmDir": {
|
|
41
|
+
"type": "string",
|
|
42
|
+
"description": "Directory containing WASM module (sulcus_wasm.js, sulcus_wasm_bg.wasm). Defaults to plugin's wasm/ subdirectory."
|
|
35
43
|
},
|
|
36
44
|
"agentId": {
|
|
37
45
|
"type": "string",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@digitalforgestudios/openclaw-sulcus",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "Sulcus — reactive, thermodynamic memory plugin for OpenClaw. Opt-in persistent memory with heat-based decay, semantic search, and cross-agent sync. Auto-recall and auto-capture disabled by default.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"openclaw",
|
|
@@ -39,11 +39,13 @@
|
|
|
39
39
|
},
|
|
40
40
|
"files": [
|
|
41
41
|
"index.ts",
|
|
42
|
+
"wasm/",
|
|
42
43
|
"openclaw.plugin.json",
|
|
43
44
|
"README.md"
|
|
44
45
|
],
|
|
45
46
|
"dependencies": {
|
|
46
|
-
"@sinclair/typebox": "^0.34.0"
|
|
47
|
+
"@sinclair/typebox": "^0.34.0",
|
|
48
|
+
"koffi": "^2.9.0"
|
|
47
49
|
},
|
|
48
50
|
"peerDependencies": {
|
|
49
51
|
"openclaw": ">=2026.3.0"
|
|
@@ -0,0 +1,607 @@
|
|
|
1
|
+
/* @ts-self-types="./sulcus_wasm.d.ts" */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The main SULCUS memory handle. Create once; keep alive for the session.
|
|
5
|
+
*/
|
|
6
|
+
class SulcusMem {
|
|
7
|
+
static __wrap(ptr) {
|
|
8
|
+
ptr = ptr >>> 0;
|
|
9
|
+
const obj = Object.create(SulcusMem.prototype);
|
|
10
|
+
obj.__wbg_ptr = ptr;
|
|
11
|
+
SulcusMemFinalization.register(obj, obj.__wbg_ptr, obj);
|
|
12
|
+
return obj;
|
|
13
|
+
}
|
|
14
|
+
__destroy_into_raw() {
|
|
15
|
+
const ptr = this.__wbg_ptr;
|
|
16
|
+
this.__wbg_ptr = 0;
|
|
17
|
+
SulcusMemFinalization.unregister(this);
|
|
18
|
+
return ptr;
|
|
19
|
+
}
|
|
20
|
+
free() {
|
|
21
|
+
const ptr = this.__destroy_into_raw();
|
|
22
|
+
wasm.__wbg_sulcusmem_free(ptr, 0);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Record a new image memory.
|
|
26
|
+
*
|
|
27
|
+
* @param label Human-readable label for the image (optional).
|
|
28
|
+
* @param bitmap The raw image bytes (Uint8Array).
|
|
29
|
+
* @param mime MIME type (e.g., "image/png").
|
|
30
|
+
* @param namespace Optional: partition memory by namespace.
|
|
31
|
+
* @returns `{ id: string, status: "added" }`
|
|
32
|
+
* @param {string | null | undefined} label
|
|
33
|
+
* @param {Uint8Array} bitmap
|
|
34
|
+
* @param {string} mime
|
|
35
|
+
* @param {string | null} [namespace]
|
|
36
|
+
* @returns {Promise<any>}
|
|
37
|
+
*/
|
|
38
|
+
add_image_memory(label, bitmap, mime, namespace) {
|
|
39
|
+
var ptr0 = isLikeNone(label) ? 0 : passStringToWasm0(label, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
40
|
+
var len0 = WASM_VECTOR_LEN;
|
|
41
|
+
const ptr1 = passArray8ToWasm0(bitmap, wasm.__wbindgen_malloc);
|
|
42
|
+
const len1 = WASM_VECTOR_LEN;
|
|
43
|
+
const ptr2 = passStringToWasm0(mime, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
44
|
+
const len2 = WASM_VECTOR_LEN;
|
|
45
|
+
var ptr3 = isLikeNone(namespace) ? 0 : passStringToWasm0(namespace, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
46
|
+
var len3 = WASM_VECTOR_LEN;
|
|
47
|
+
const ret = wasm.sulcusmem_add_image_memory(this.__wbg_ptr, ptr0, len0, ptr1, len1, ptr2, len2, ptr3, len3);
|
|
48
|
+
return ret;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Record a new memory.
|
|
52
|
+
*
|
|
53
|
+
* @param text The raw text to remember.
|
|
54
|
+
* @param memory_type Optional: "episodic" | "semantic" | "preference" | "procedural".
|
|
55
|
+
* @returns `{ id: string, status: "added" }`
|
|
56
|
+
* @param {string} text
|
|
57
|
+
* @param {string | null} [memory_type]
|
|
58
|
+
* @returns {Promise<any>}
|
|
59
|
+
*/
|
|
60
|
+
add_memory(text, memory_type) {
|
|
61
|
+
const ptr0 = passStringToWasm0(text, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
62
|
+
const len0 = WASM_VECTOR_LEN;
|
|
63
|
+
var ptr1 = isLikeNone(memory_type) ? 0 : passStringToWasm0(memory_type, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
64
|
+
var len1 = WASM_VECTOR_LEN;
|
|
65
|
+
const ret = wasm.sulcusmem_add_memory(this.__wbg_ptr, ptr0, len0, ptr1, len1);
|
|
66
|
+
return ret;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Create a new `SulcusMem` instance.
|
|
70
|
+
*
|
|
71
|
+
* @param query_fn `async (sql: string, params: any[]) => any[]`
|
|
72
|
+
* @param embed_fn `async (text: string) => Float32Array`
|
|
73
|
+
* @param embed_image_fn Optional: `async (bitmap: Uint8Array) => Float32Array`
|
|
74
|
+
* @param {Function} query_fn
|
|
75
|
+
* @param {Function} embed_fn
|
|
76
|
+
* @param {Function | null} [embed_image_fn]
|
|
77
|
+
* @returns {SulcusMem}
|
|
78
|
+
*/
|
|
79
|
+
static create(query_fn, embed_fn, embed_image_fn) {
|
|
80
|
+
const ret = wasm.sulcusmem_create(query_fn, embed_fn, isLikeNone(embed_image_fn) ? 0 : addToExternrefTable0(embed_image_fn));
|
|
81
|
+
return SulcusMem.__wrap(ret);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* List nodes ordered by current_heat DESC.
|
|
85
|
+
*
|
|
86
|
+
* @param limit Max nodes to return (default 20).
|
|
87
|
+
* @returns `{ nodes: Array<{ id, label, pointer_summary, current_heat, memory_type }> }`
|
|
88
|
+
* @param {number | null} [limit]
|
|
89
|
+
* @returns {Promise<any>}
|
|
90
|
+
*/
|
|
91
|
+
list_hot_nodes(limit) {
|
|
92
|
+
const ret = wasm.sulcusmem_list_hot_nodes(this.__wbg_ptr, isLikeNone(limit) ? 0x100000001 : (limit) >>> 0);
|
|
93
|
+
return ret;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Search for similar memories using an image as query (CLIP).
|
|
97
|
+
*
|
|
98
|
+
* @param bitmap The raw image bytes (Uint8Array).
|
|
99
|
+
* @param limit Max results (default 10).
|
|
100
|
+
* @returns `{ results: Array<{ id, label, pointer_summary, score }> }`
|
|
101
|
+
* @param {Uint8Array} bitmap
|
|
102
|
+
* @param {number | null} [limit]
|
|
103
|
+
* @returns {Promise<any>}
|
|
104
|
+
*/
|
|
105
|
+
search_by_image(bitmap, limit) {
|
|
106
|
+
const ptr0 = passArray8ToWasm0(bitmap, wasm.__wbindgen_malloc);
|
|
107
|
+
const len0 = WASM_VECTOR_LEN;
|
|
108
|
+
const ret = wasm.sulcusmem_search_by_image(this.__wbg_ptr, ptr0, len0, isLikeNone(limit) ? 0x100000001 : (limit) >>> 0);
|
|
109
|
+
return ret;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Hybrid FTS + cosine similarity search using native pgvector operators.
|
|
113
|
+
*
|
|
114
|
+
* @param query Natural language query.
|
|
115
|
+
* @param limit Max results (default 10).
|
|
116
|
+
* @returns `{ results: Array<{ id, label, pointer_summary, score }> }`
|
|
117
|
+
* @param {string} query
|
|
118
|
+
* @param {number | null} [limit]
|
|
119
|
+
* @returns {Promise<any>}
|
|
120
|
+
*/
|
|
121
|
+
search_memory(query, limit) {
|
|
122
|
+
const ptr0 = passStringToWasm0(query, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
123
|
+
const len0 = WASM_VECTOR_LEN;
|
|
124
|
+
const ret = wasm.sulcusmem_search_memory(this.__wbg_ptr, ptr0, len0, isLikeNone(limit) ? 0x100000001 : (limit) >>> 0);
|
|
125
|
+
return ret;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Run one thermodynamics cycle: decay all nodes, spread heat along edges,
|
|
129
|
+
* rebuild `active_index`.
|
|
130
|
+
*
|
|
131
|
+
* @param decay Heat decay factor per tick (default 0.85).
|
|
132
|
+
* @param spread Spreading activation weight (default 0.5).
|
|
133
|
+
* @param limit Max nodes kept in `active_index` (default 20).
|
|
134
|
+
* @returns `{ status: "tick_complete" }`
|
|
135
|
+
* @param {number} decay
|
|
136
|
+
* @param {number} spread
|
|
137
|
+
* @param {number} limit
|
|
138
|
+
* @returns {Promise<any>}
|
|
139
|
+
*/
|
|
140
|
+
tick(decay, spread, limit) {
|
|
141
|
+
const ret = wasm.sulcusmem_tick(this.__wbg_ptr, decay, spread, limit);
|
|
142
|
+
return ret;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Run one thermodynamics cycle using the configurable ThermoConfig engine.
|
|
146
|
+
*
|
|
147
|
+
* @param config_json JSON string of ThermoConfig (or `null` for defaults).
|
|
148
|
+
* @returns `{ status: "tick_complete", engine: "thermo_v2", ... }`
|
|
149
|
+
* @param {string | null} [config_json]
|
|
150
|
+
* @returns {Promise<any>}
|
|
151
|
+
*/
|
|
152
|
+
tick_v2(config_json) {
|
|
153
|
+
var ptr0 = isLikeNone(config_json) ? 0 : passStringToWasm0(config_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
154
|
+
var len0 = WASM_VECTOR_LEN;
|
|
155
|
+
const ret = wasm.sulcusmem_tick_v2(this.__wbg_ptr, ptr0, len0);
|
|
156
|
+
return ret;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (Symbol.dispose) SulcusMem.prototype[Symbol.dispose] = SulcusMem.prototype.free;
|
|
160
|
+
exports.SulcusMem = SulcusMem;
|
|
161
|
+
|
|
162
|
+
function on_init() {
|
|
163
|
+
wasm.on_init();
|
|
164
|
+
}
|
|
165
|
+
exports.on_init = on_init;
|
|
166
|
+
|
|
167
|
+
function __wbg_get_imports() {
|
|
168
|
+
const import0 = {
|
|
169
|
+
__proto__: null,
|
|
170
|
+
__wbg___wbindgen_debug_string_0bc8482c6e3508ae: function(arg0, arg1) {
|
|
171
|
+
const ret = debugString(arg1);
|
|
172
|
+
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
173
|
+
const len1 = WASM_VECTOR_LEN;
|
|
174
|
+
getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
|
|
175
|
+
getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
|
|
176
|
+
},
|
|
177
|
+
__wbg___wbindgen_is_function_0095a73b8b156f76: function(arg0) {
|
|
178
|
+
const ret = typeof(arg0) === 'function';
|
|
179
|
+
return ret;
|
|
180
|
+
},
|
|
181
|
+
__wbg___wbindgen_is_undefined_9e4d92534c42d778: function(arg0) {
|
|
182
|
+
const ret = arg0 === undefined;
|
|
183
|
+
return ret;
|
|
184
|
+
},
|
|
185
|
+
__wbg___wbindgen_string_get_72fb696202c56729: function(arg0, arg1) {
|
|
186
|
+
const obj = arg1;
|
|
187
|
+
const ret = typeof(obj) === 'string' ? obj : undefined;
|
|
188
|
+
var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
189
|
+
var len1 = WASM_VECTOR_LEN;
|
|
190
|
+
getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
|
|
191
|
+
getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
|
|
192
|
+
},
|
|
193
|
+
__wbg___wbindgen_throw_be289d5034ed271b: function(arg0, arg1) {
|
|
194
|
+
throw new Error(getStringFromWasm0(arg0, arg1));
|
|
195
|
+
},
|
|
196
|
+
__wbg__wbg_cb_unref_d9b87ff7982e3b21: function(arg0) {
|
|
197
|
+
arg0._wbg_cb_unref();
|
|
198
|
+
},
|
|
199
|
+
__wbg_call_389efe28435a9388: function() { return handleError(function (arg0, arg1) {
|
|
200
|
+
const ret = arg0.call(arg1);
|
|
201
|
+
return ret;
|
|
202
|
+
}, arguments); },
|
|
203
|
+
__wbg_call_4708e0c13bdc8e95: function() { return handleError(function (arg0, arg1, arg2) {
|
|
204
|
+
const ret = arg0.call(arg1, arg2);
|
|
205
|
+
return ret;
|
|
206
|
+
}, arguments); },
|
|
207
|
+
__wbg_call_812d25f1510c13c8: function() { return handleError(function (arg0, arg1, arg2, arg3) {
|
|
208
|
+
const ret = arg0.call(arg1, arg2, arg3);
|
|
209
|
+
return ret;
|
|
210
|
+
}, arguments); },
|
|
211
|
+
__wbg_error_7534b8e9a36f1ab4: function(arg0, arg1) {
|
|
212
|
+
let deferred0_0;
|
|
213
|
+
let deferred0_1;
|
|
214
|
+
try {
|
|
215
|
+
deferred0_0 = arg0;
|
|
216
|
+
deferred0_1 = arg1;
|
|
217
|
+
console.error(getStringFromWasm0(arg0, arg1));
|
|
218
|
+
} finally {
|
|
219
|
+
wasm.__wbindgen_free(deferred0_0, deferred0_1, 1);
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
__wbg_getRandomValues_71d446877d8b0ad4: function() { return handleError(function (arg0, arg1) {
|
|
223
|
+
globalThis.crypto.getRandomValues(getArrayU8FromWasm0(arg0, arg1));
|
|
224
|
+
}, arguments); },
|
|
225
|
+
__wbg_instanceof_Float32Array_c882a172bf41d92a: function(arg0) {
|
|
226
|
+
let result;
|
|
227
|
+
try {
|
|
228
|
+
result = arg0 instanceof Float32Array;
|
|
229
|
+
} catch (_) {
|
|
230
|
+
result = false;
|
|
231
|
+
}
|
|
232
|
+
const ret = result;
|
|
233
|
+
return ret;
|
|
234
|
+
},
|
|
235
|
+
__wbg_instanceof_Promise_0094681e3519d6ec: function(arg0) {
|
|
236
|
+
let result;
|
|
237
|
+
try {
|
|
238
|
+
result = arg0 instanceof Promise;
|
|
239
|
+
} catch (_) {
|
|
240
|
+
result = false;
|
|
241
|
+
}
|
|
242
|
+
const ret = result;
|
|
243
|
+
return ret;
|
|
244
|
+
},
|
|
245
|
+
__wbg_length_9a7876c9728a0979: function(arg0) {
|
|
246
|
+
const ret = arg0.length;
|
|
247
|
+
return ret;
|
|
248
|
+
},
|
|
249
|
+
__wbg_new_3eb36ae241fe6f44: function() {
|
|
250
|
+
const ret = new Array();
|
|
251
|
+
return ret;
|
|
252
|
+
},
|
|
253
|
+
__wbg_new_8a6f238a6ece86ea: function() {
|
|
254
|
+
const ret = new Error();
|
|
255
|
+
return ret;
|
|
256
|
+
},
|
|
257
|
+
__wbg_new_b5d9e2fb389fef91: function(arg0, arg1) {
|
|
258
|
+
try {
|
|
259
|
+
var state0 = {a: arg0, b: arg1};
|
|
260
|
+
var cb0 = (arg0, arg1) => {
|
|
261
|
+
const a = state0.a;
|
|
262
|
+
state0.a = 0;
|
|
263
|
+
try {
|
|
264
|
+
return wasm_bindgen__convert__closures_____invoke__h8e726e14fdbe3765(a, state0.b, arg0, arg1);
|
|
265
|
+
} finally {
|
|
266
|
+
state0.a = a;
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
const ret = new Promise(cb0);
|
|
270
|
+
return ret;
|
|
271
|
+
} finally {
|
|
272
|
+
state0.a = state0.b = 0;
|
|
273
|
+
}
|
|
274
|
+
},
|
|
275
|
+
__wbg_new_from_slice_a3d2629dc1826784: function(arg0, arg1) {
|
|
276
|
+
const ret = new Uint8Array(getArrayU8FromWasm0(arg0, arg1));
|
|
277
|
+
return ret;
|
|
278
|
+
},
|
|
279
|
+
__wbg_new_no_args_1c7c842f08d00ebb: function(arg0, arg1) {
|
|
280
|
+
const ret = new Function(getStringFromWasm0(arg0, arg1));
|
|
281
|
+
return ret;
|
|
282
|
+
},
|
|
283
|
+
__wbg_parse_708461a1feddfb38: function() { return handleError(function (arg0, arg1) {
|
|
284
|
+
const ret = JSON.parse(getStringFromWasm0(arg0, arg1));
|
|
285
|
+
return ret;
|
|
286
|
+
}, arguments); },
|
|
287
|
+
__wbg_prototypesetcall_c7e6a26aeade796d: function(arg0, arg1, arg2) {
|
|
288
|
+
Float32Array.prototype.set.call(getArrayF32FromWasm0(arg0, arg1), arg2);
|
|
289
|
+
},
|
|
290
|
+
__wbg_push_8ffdcb2063340ba5: function(arg0, arg1) {
|
|
291
|
+
const ret = arg0.push(arg1);
|
|
292
|
+
return ret;
|
|
293
|
+
},
|
|
294
|
+
__wbg_queueMicrotask_0aa0a927f78f5d98: function(arg0) {
|
|
295
|
+
const ret = arg0.queueMicrotask;
|
|
296
|
+
return ret;
|
|
297
|
+
},
|
|
298
|
+
__wbg_queueMicrotask_5bb536982f78a56f: function(arg0) {
|
|
299
|
+
queueMicrotask(arg0);
|
|
300
|
+
},
|
|
301
|
+
__wbg_resolve_002c4b7d9d8f6b64: function(arg0) {
|
|
302
|
+
const ret = Promise.resolve(arg0);
|
|
303
|
+
return ret;
|
|
304
|
+
},
|
|
305
|
+
__wbg_stack_0ed75d68575b0f3c: function(arg0, arg1) {
|
|
306
|
+
const ret = arg1.stack;
|
|
307
|
+
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
|
308
|
+
const len1 = WASM_VECTOR_LEN;
|
|
309
|
+
getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
|
|
310
|
+
getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
|
|
311
|
+
},
|
|
312
|
+
__wbg_static_accessor_GLOBAL_12837167ad935116: function() {
|
|
313
|
+
const ret = typeof global === 'undefined' ? null : global;
|
|
314
|
+
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
|
|
315
|
+
},
|
|
316
|
+
__wbg_static_accessor_GLOBAL_THIS_e628e89ab3b1c95f: function() {
|
|
317
|
+
const ret = typeof globalThis === 'undefined' ? null : globalThis;
|
|
318
|
+
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
|
|
319
|
+
},
|
|
320
|
+
__wbg_static_accessor_SELF_a621d3dfbb60d0ce: function() {
|
|
321
|
+
const ret = typeof self === 'undefined' ? null : self;
|
|
322
|
+
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
|
|
323
|
+
},
|
|
324
|
+
__wbg_static_accessor_WINDOW_f8727f0cf888e0bd: function() {
|
|
325
|
+
const ret = typeof window === 'undefined' ? null : window;
|
|
326
|
+
return isLikeNone(ret) ? 0 : addToExternrefTable0(ret);
|
|
327
|
+
},
|
|
328
|
+
__wbg_stringify_8d1cc6ff383e8bae: function() { return handleError(function (arg0) {
|
|
329
|
+
const ret = JSON.stringify(arg0);
|
|
330
|
+
return ret;
|
|
331
|
+
}, arguments); },
|
|
332
|
+
__wbg_then_0d9fe2c7b1857d32: function(arg0, arg1, arg2) {
|
|
333
|
+
const ret = arg0.then(arg1, arg2);
|
|
334
|
+
return ret;
|
|
335
|
+
},
|
|
336
|
+
__wbg_then_b9e7b3b5f1a9e1b5: function(arg0, arg1) {
|
|
337
|
+
const ret = arg0.then(arg1);
|
|
338
|
+
return ret;
|
|
339
|
+
},
|
|
340
|
+
__wbindgen_cast_0000000000000001: function(arg0, arg1) {
|
|
341
|
+
// Cast intrinsic for `Closure(Closure { dtor_idx: 170, function: Function { arguments: [Externref], shim_idx: 171, ret: Unit, inner_ret: Some(Unit) }, mutable: true }) -> Externref`.
|
|
342
|
+
const ret = makeMutClosure(arg0, arg1, wasm.wasm_bindgen__closure__destroy__he93a1b3377269182, wasm_bindgen__convert__closures_____invoke__h668261b123d2ac6c);
|
|
343
|
+
return ret;
|
|
344
|
+
},
|
|
345
|
+
__wbindgen_cast_0000000000000002: function(arg0) {
|
|
346
|
+
// Cast intrinsic for `F64 -> Externref`.
|
|
347
|
+
const ret = arg0;
|
|
348
|
+
return ret;
|
|
349
|
+
},
|
|
350
|
+
__wbindgen_cast_0000000000000003: function(arg0, arg1) {
|
|
351
|
+
// Cast intrinsic for `Ref(String) -> Externref`.
|
|
352
|
+
const ret = getStringFromWasm0(arg0, arg1);
|
|
353
|
+
return ret;
|
|
354
|
+
},
|
|
355
|
+
__wbindgen_init_externref_table: function() {
|
|
356
|
+
const table = wasm.__wbindgen_externrefs;
|
|
357
|
+
const offset = table.grow(4);
|
|
358
|
+
table.set(0, undefined);
|
|
359
|
+
table.set(offset + 0, undefined);
|
|
360
|
+
table.set(offset + 1, null);
|
|
361
|
+
table.set(offset + 2, true);
|
|
362
|
+
table.set(offset + 3, false);
|
|
363
|
+
},
|
|
364
|
+
};
|
|
365
|
+
return {
|
|
366
|
+
__proto__: null,
|
|
367
|
+
"./sulcus_wasm_bg.js": import0,
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function wasm_bindgen__convert__closures_____invoke__h668261b123d2ac6c(arg0, arg1, arg2) {
|
|
372
|
+
wasm.wasm_bindgen__convert__closures_____invoke__h668261b123d2ac6c(arg0, arg1, arg2);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function wasm_bindgen__convert__closures_____invoke__h8e726e14fdbe3765(arg0, arg1, arg2, arg3) {
|
|
376
|
+
wasm.wasm_bindgen__convert__closures_____invoke__h8e726e14fdbe3765(arg0, arg1, arg2, arg3);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const SulcusMemFinalization = (typeof FinalizationRegistry === 'undefined')
|
|
380
|
+
? { register: () => {}, unregister: () => {} }
|
|
381
|
+
: new FinalizationRegistry(ptr => wasm.__wbg_sulcusmem_free(ptr >>> 0, 1));
|
|
382
|
+
|
|
383
|
+
function addToExternrefTable0(obj) {
|
|
384
|
+
const idx = wasm.__externref_table_alloc();
|
|
385
|
+
wasm.__wbindgen_externrefs.set(idx, obj);
|
|
386
|
+
return idx;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
const CLOSURE_DTORS = (typeof FinalizationRegistry === 'undefined')
|
|
390
|
+
? { register: () => {}, unregister: () => {} }
|
|
391
|
+
: new FinalizationRegistry(state => state.dtor(state.a, state.b));
|
|
392
|
+
|
|
393
|
+
function debugString(val) {
|
|
394
|
+
// primitive types
|
|
395
|
+
const type = typeof val;
|
|
396
|
+
if (type == 'number' || type == 'boolean' || val == null) {
|
|
397
|
+
return `${val}`;
|
|
398
|
+
}
|
|
399
|
+
if (type == 'string') {
|
|
400
|
+
return `"${val}"`;
|
|
401
|
+
}
|
|
402
|
+
if (type == 'symbol') {
|
|
403
|
+
const description = val.description;
|
|
404
|
+
if (description == null) {
|
|
405
|
+
return 'Symbol';
|
|
406
|
+
} else {
|
|
407
|
+
return `Symbol(${description})`;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
if (type == 'function') {
|
|
411
|
+
const name = val.name;
|
|
412
|
+
if (typeof name == 'string' && name.length > 0) {
|
|
413
|
+
return `Function(${name})`;
|
|
414
|
+
} else {
|
|
415
|
+
return 'Function';
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
// objects
|
|
419
|
+
if (Array.isArray(val)) {
|
|
420
|
+
const length = val.length;
|
|
421
|
+
let debug = '[';
|
|
422
|
+
if (length > 0) {
|
|
423
|
+
debug += debugString(val[0]);
|
|
424
|
+
}
|
|
425
|
+
for(let i = 1; i < length; i++) {
|
|
426
|
+
debug += ', ' + debugString(val[i]);
|
|
427
|
+
}
|
|
428
|
+
debug += ']';
|
|
429
|
+
return debug;
|
|
430
|
+
}
|
|
431
|
+
// Test for built-in
|
|
432
|
+
const builtInMatches = /\[object ([^\]]+)\]/.exec(toString.call(val));
|
|
433
|
+
let className;
|
|
434
|
+
if (builtInMatches && builtInMatches.length > 1) {
|
|
435
|
+
className = builtInMatches[1];
|
|
436
|
+
} else {
|
|
437
|
+
// Failed to match the standard '[object ClassName]'
|
|
438
|
+
return toString.call(val);
|
|
439
|
+
}
|
|
440
|
+
if (className == 'Object') {
|
|
441
|
+
// we're a user defined class or Object
|
|
442
|
+
// JSON.stringify avoids problems with cycles, and is generally much
|
|
443
|
+
// easier than looping through ownProperties of `val`.
|
|
444
|
+
try {
|
|
445
|
+
return 'Object(' + JSON.stringify(val) + ')';
|
|
446
|
+
} catch (_) {
|
|
447
|
+
return 'Object';
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
// errors
|
|
451
|
+
if (val instanceof Error) {
|
|
452
|
+
return `${val.name}: ${val.message}\n${val.stack}`;
|
|
453
|
+
}
|
|
454
|
+
// TODO we could test for more things here, like `Set`s and `Map`s.
|
|
455
|
+
return className;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
function getArrayF32FromWasm0(ptr, len) {
|
|
459
|
+
ptr = ptr >>> 0;
|
|
460
|
+
return getFloat32ArrayMemory0().subarray(ptr / 4, ptr / 4 + len);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
function getArrayU8FromWasm0(ptr, len) {
|
|
464
|
+
ptr = ptr >>> 0;
|
|
465
|
+
return getUint8ArrayMemory0().subarray(ptr / 1, ptr / 1 + len);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
let cachedDataViewMemory0 = null;
|
|
469
|
+
function getDataViewMemory0() {
|
|
470
|
+
if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer.detached === true || (cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer)) {
|
|
471
|
+
cachedDataViewMemory0 = new DataView(wasm.memory.buffer);
|
|
472
|
+
}
|
|
473
|
+
return cachedDataViewMemory0;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
let cachedFloat32ArrayMemory0 = null;
|
|
477
|
+
function getFloat32ArrayMemory0() {
|
|
478
|
+
if (cachedFloat32ArrayMemory0 === null || cachedFloat32ArrayMemory0.byteLength === 0) {
|
|
479
|
+
cachedFloat32ArrayMemory0 = new Float32Array(wasm.memory.buffer);
|
|
480
|
+
}
|
|
481
|
+
return cachedFloat32ArrayMemory0;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
function getStringFromWasm0(ptr, len) {
|
|
485
|
+
ptr = ptr >>> 0;
|
|
486
|
+
return decodeText(ptr, len);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
let cachedUint8ArrayMemory0 = null;
|
|
490
|
+
function getUint8ArrayMemory0() {
|
|
491
|
+
if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) {
|
|
492
|
+
cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer);
|
|
493
|
+
}
|
|
494
|
+
return cachedUint8ArrayMemory0;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
function handleError(f, args) {
|
|
498
|
+
try {
|
|
499
|
+
return f.apply(this, args);
|
|
500
|
+
} catch (e) {
|
|
501
|
+
const idx = addToExternrefTable0(e);
|
|
502
|
+
wasm.__wbindgen_exn_store(idx);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
function isLikeNone(x) {
|
|
507
|
+
return x === undefined || x === null;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
function makeMutClosure(arg0, arg1, dtor, f) {
|
|
511
|
+
const state = { a: arg0, b: arg1, cnt: 1, dtor };
|
|
512
|
+
const real = (...args) => {
|
|
513
|
+
|
|
514
|
+
// First up with a closure we increment the internal reference
|
|
515
|
+
// count. This ensures that the Rust closure environment won't
|
|
516
|
+
// be deallocated while we're invoking it.
|
|
517
|
+
state.cnt++;
|
|
518
|
+
const a = state.a;
|
|
519
|
+
state.a = 0;
|
|
520
|
+
try {
|
|
521
|
+
return f(a, state.b, ...args);
|
|
522
|
+
} finally {
|
|
523
|
+
state.a = a;
|
|
524
|
+
real._wbg_cb_unref();
|
|
525
|
+
}
|
|
526
|
+
};
|
|
527
|
+
real._wbg_cb_unref = () => {
|
|
528
|
+
if (--state.cnt === 0) {
|
|
529
|
+
state.dtor(state.a, state.b);
|
|
530
|
+
state.a = 0;
|
|
531
|
+
CLOSURE_DTORS.unregister(state);
|
|
532
|
+
}
|
|
533
|
+
};
|
|
534
|
+
CLOSURE_DTORS.register(real, state, state);
|
|
535
|
+
return real;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
function passArray8ToWasm0(arg, malloc) {
|
|
539
|
+
const ptr = malloc(arg.length * 1, 1) >>> 0;
|
|
540
|
+
getUint8ArrayMemory0().set(arg, ptr / 1);
|
|
541
|
+
WASM_VECTOR_LEN = arg.length;
|
|
542
|
+
return ptr;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
function passStringToWasm0(arg, malloc, realloc) {
|
|
546
|
+
if (realloc === undefined) {
|
|
547
|
+
const buf = cachedTextEncoder.encode(arg);
|
|
548
|
+
const ptr = malloc(buf.length, 1) >>> 0;
|
|
549
|
+
getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf);
|
|
550
|
+
WASM_VECTOR_LEN = buf.length;
|
|
551
|
+
return ptr;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
let len = arg.length;
|
|
555
|
+
let ptr = malloc(len, 1) >>> 0;
|
|
556
|
+
|
|
557
|
+
const mem = getUint8ArrayMemory0();
|
|
558
|
+
|
|
559
|
+
let offset = 0;
|
|
560
|
+
|
|
561
|
+
for (; offset < len; offset++) {
|
|
562
|
+
const code = arg.charCodeAt(offset);
|
|
563
|
+
if (code > 0x7F) break;
|
|
564
|
+
mem[ptr + offset] = code;
|
|
565
|
+
}
|
|
566
|
+
if (offset !== len) {
|
|
567
|
+
if (offset !== 0) {
|
|
568
|
+
arg = arg.slice(offset);
|
|
569
|
+
}
|
|
570
|
+
ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0;
|
|
571
|
+
const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len);
|
|
572
|
+
const ret = cachedTextEncoder.encodeInto(arg, view);
|
|
573
|
+
|
|
574
|
+
offset += ret.written;
|
|
575
|
+
ptr = realloc(ptr, len, offset, 1) >>> 0;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
WASM_VECTOR_LEN = offset;
|
|
579
|
+
return ptr;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
|
|
583
|
+
cachedTextDecoder.decode();
|
|
584
|
+
function decodeText(ptr, len) {
|
|
585
|
+
return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len));
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
const cachedTextEncoder = new TextEncoder();
|
|
589
|
+
|
|
590
|
+
if (!('encodeInto' in cachedTextEncoder)) {
|
|
591
|
+
cachedTextEncoder.encodeInto = function (arg, view) {
|
|
592
|
+
const buf = cachedTextEncoder.encode(arg);
|
|
593
|
+
view.set(buf);
|
|
594
|
+
return {
|
|
595
|
+
read: arg.length,
|
|
596
|
+
written: buf.length
|
|
597
|
+
};
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
let WASM_VECTOR_LEN = 0;
|
|
602
|
+
|
|
603
|
+
const wasmPath = `${__dirname}/sulcus_wasm_bg.wasm`;
|
|
604
|
+
const wasmBytes = require('fs').readFileSync(wasmPath);
|
|
605
|
+
const wasmModule = new WebAssembly.Module(wasmBytes);
|
|
606
|
+
const wasm = new WebAssembly.Instance(wasmModule, __wbg_get_imports()).exports;
|
|
607
|
+
wasm.__wbindgen_start();
|
|
Binary file
|