@wierdbytes/pi-memory 0.1.0 → 0.1.1
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/CHANGELOG.md +6 -0
- package/index.js +13 -426
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
package/index.js
CHANGED
|
@@ -90,7 +90,7 @@ ${section}
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
// packages/pi/runtime.ts
|
|
93
|
-
import { existsSync as
|
|
93
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
94
94
|
import { join as join3 } from "node:path";
|
|
95
95
|
// packages/core/src/api.ts
|
|
96
96
|
var INBOX_KINDS = ["preference", "procedure", "pitfall", "fact", "decision", "reference", "other"];
|
|
@@ -100,7 +100,6 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
|
100
100
|
// packages/core/src/layout.ts
|
|
101
101
|
import { homedir } from "node:os";
|
|
102
102
|
import { join, resolve } from "node:path";
|
|
103
|
-
var GLOBAL_SCOPE = "global";
|
|
104
103
|
function expandHome(p) {
|
|
105
104
|
if (p === "~")
|
|
106
105
|
return homedir();
|
|
@@ -238,244 +237,12 @@ function loadConfig(path = homePaths().config) {
|
|
|
238
237
|
function serverUrl(cfg = loadConfig()) {
|
|
239
238
|
return (process.env.WMEM_URL || `http://127.0.0.1:${cfg.port}`).replace(/\/+$/, "");
|
|
240
239
|
}
|
|
241
|
-
// packages/core/src/notes.ts
|
|
242
|
-
import { randomBytes } from "node:crypto";
|
|
243
|
-
function newNoteId(taken) {
|
|
244
|
-
for (;; ) {
|
|
245
|
-
const id = `m-${randomBytes(3).toString("hex")}`;
|
|
246
|
-
if (!taken.has(id)) {
|
|
247
|
-
taken.add(id);
|
|
248
|
-
return id;
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
function cloneNote(n) {
|
|
253
|
-
return {
|
|
254
|
-
...n,
|
|
255
|
-
supersedes: [...n.supersedes],
|
|
256
|
-
evidence: n.evidence.map((e) => ({ ...e, e: [...e.e] })),
|
|
257
|
-
extra: { ...n.extra },
|
|
258
|
-
stats: n.stats ? { ...n.stats } : undefined
|
|
259
|
-
};
|
|
260
|
-
}
|
|
261
|
-
function makeNote(p, date) {
|
|
262
|
-
return {
|
|
263
|
-
id: p.id,
|
|
264
|
-
scope: p.scope,
|
|
265
|
-
title: p.title,
|
|
266
|
-
type: p.type ?? "fact",
|
|
267
|
-
status: p.status ?? "active",
|
|
268
|
-
index: p.index ?? "none",
|
|
269
|
-
when: p.when || undefined,
|
|
270
|
-
body: p.body ?? "",
|
|
271
|
-
pinned: p.pinned ?? false,
|
|
272
|
-
enabled: p.enabled ?? true,
|
|
273
|
-
created: p.created || date,
|
|
274
|
-
updated: p.updated || p.created || date,
|
|
275
|
-
confirmed: p.confirmed || undefined,
|
|
276
|
-
validUntil: p.validUntil || undefined,
|
|
277
|
-
supersededBy: p.supersededBy || undefined,
|
|
278
|
-
supersedes: p.supersedes ?? [],
|
|
279
|
-
evidence: p.evidence ?? [],
|
|
280
|
-
extra: p.extra ?? {},
|
|
281
|
-
stats: p.stats
|
|
282
|
-
};
|
|
283
|
-
}
|
|
284
|
-
var NOTE_COLUMNS = [
|
|
285
|
-
"scope",
|
|
286
|
-
"title",
|
|
287
|
-
"type",
|
|
288
|
-
"status",
|
|
289
|
-
"index",
|
|
290
|
-
"when",
|
|
291
|
-
"body",
|
|
292
|
-
"pinned",
|
|
293
|
-
"enabled",
|
|
294
|
-
"created",
|
|
295
|
-
"updated",
|
|
296
|
-
"confirmed",
|
|
297
|
-
"validUntil",
|
|
298
|
-
"supersededBy",
|
|
299
|
-
"supersedes",
|
|
300
|
-
"evidence",
|
|
301
|
-
"extra"
|
|
302
|
-
];
|
|
303
|
-
function canonical(v) {
|
|
304
|
-
if (v === undefined || v === null)
|
|
305
|
-
return "null";
|
|
306
|
-
if (Array.isArray(v))
|
|
307
|
-
return `[${v.map(canonical).join(",")}]`;
|
|
308
|
-
if (typeof v === "object") {
|
|
309
|
-
const o = v;
|
|
310
|
-
return `{${Object.keys(o).filter((k) => o[k] !== undefined).sort().map((k) => `${JSON.stringify(k)}:${canonical(o[k])}`).join(",")}}`;
|
|
311
|
-
}
|
|
312
|
-
return JSON.stringify(v);
|
|
313
|
-
}
|
|
314
|
-
function columnValue(n, c) {
|
|
315
|
-
const v = n[c];
|
|
316
|
-
if (c === "evidence")
|
|
317
|
-
return canonical(v.map((e) => ({ s: e.s, e: e.e, date: e.date, at: e.at, kind: e.kind })));
|
|
318
|
-
if (v === undefined || v === null || v === "")
|
|
319
|
-
return "null";
|
|
320
|
-
if (typeof v === "string")
|
|
321
|
-
return JSON.stringify(c === "body" || c === "title" ? v.trim() : v);
|
|
322
|
-
return canonical(v);
|
|
323
|
-
}
|
|
324
|
-
function changedColumns(a, b) {
|
|
325
|
-
return NOTE_COLUMNS.filter((c) => columnValue(a, c) !== columnValue(b, c));
|
|
326
|
-
}
|
|
327
|
-
function sameNote(a, b) {
|
|
328
|
-
if (!a || !b)
|
|
329
|
-
return a === b;
|
|
330
|
-
return a.id === b.id && changedColumns(a, b).length === 0;
|
|
331
|
-
}
|
|
332
240
|
// packages/core/src/digest.ts
|
|
333
241
|
import { homedir as homedir2 } from "node:os";
|
|
334
|
-
|
|
335
|
-
// packages/core/src/sessions.ts
|
|
336
|
-
import { createReadStream, existsSync as existsSync2, readdirSync, statSync } from "node:fs";
|
|
337
|
-
import { createInterface } from "node:readline";
|
|
338
|
-
function slim(content) {
|
|
339
|
-
if (!Array.isArray(content))
|
|
340
|
-
return content;
|
|
341
|
-
return content.map((b) => {
|
|
342
|
-
if (!b || typeof b !== "object")
|
|
343
|
-
return b;
|
|
344
|
-
const t = b.type;
|
|
345
|
-
if (t === "image")
|
|
346
|
-
return { type: "image" };
|
|
347
|
-
if (t === "thinking" || t === "redacted_thinking")
|
|
348
|
-
return { type: "thinking" };
|
|
349
|
-
return b;
|
|
350
|
-
});
|
|
351
|
-
}
|
|
352
|
-
function slimEntry(e) {
|
|
353
|
-
if (e.message && typeof e.message === "object") {
|
|
354
|
-
const m = e.message;
|
|
355
|
-
if (m.role === "system")
|
|
356
|
-
e.message = { role: "system" };
|
|
357
|
-
else if ("content" in m)
|
|
358
|
-
m.content = slim(m.content);
|
|
359
|
-
}
|
|
360
|
-
if (e.type === "custom_message" && "content" in e)
|
|
361
|
-
e.content = slim(e.content);
|
|
362
|
-
if (e.type === "compaction")
|
|
363
|
-
delete e.systemMessage;
|
|
364
|
-
return e;
|
|
365
|
-
}
|
|
366
|
-
async function parseSession(file) {
|
|
367
|
-
const entries = new Map;
|
|
368
|
-
let header;
|
|
369
|
-
let lastId;
|
|
370
|
-
let name;
|
|
371
|
-
const rl = createInterface({ input: createReadStream(file, { encoding: "utf8" }), crlfDelay: Infinity });
|
|
372
|
-
for await (const line of rl) {
|
|
373
|
-
if (!line)
|
|
374
|
-
continue;
|
|
375
|
-
let e;
|
|
376
|
-
try {
|
|
377
|
-
e = JSON.parse(line);
|
|
378
|
-
} catch {
|
|
379
|
-
continue;
|
|
380
|
-
}
|
|
381
|
-
if (e.type === "session") {
|
|
382
|
-
header = e;
|
|
383
|
-
continue;
|
|
384
|
-
}
|
|
385
|
-
if (typeof e.id !== "string")
|
|
386
|
-
continue;
|
|
387
|
-
entries.set(e.id, slimEntry(e));
|
|
388
|
-
lastId = e.id;
|
|
389
|
-
if (e.type === "session_info" && typeof e.name === "string")
|
|
390
|
-
name = e.name;
|
|
391
|
-
}
|
|
392
|
-
if (!header)
|
|
393
|
-
return;
|
|
394
|
-
const branch = [];
|
|
395
|
-
const seen = new Set;
|
|
396
|
-
let cur = lastId ? entries.get(lastId) : undefined;
|
|
397
|
-
while (cur && !seen.has(cur.id)) {
|
|
398
|
-
seen.add(cur.id);
|
|
399
|
-
branch.push(cur);
|
|
400
|
-
cur = cur.parentId ? entries.get(cur.parentId) : undefined;
|
|
401
|
-
}
|
|
402
|
-
branch.reverse();
|
|
403
|
-
return { file, header, branch, name, lastEntryId: lastId };
|
|
404
|
-
}
|
|
405
|
-
class SessionCache {
|
|
406
|
-
max;
|
|
407
|
-
map = new Map;
|
|
408
|
-
constructor(max = 8) {
|
|
409
|
-
this.max = max;
|
|
410
|
-
}
|
|
411
|
-
get(file) {
|
|
412
|
-
let p = this.map.get(file);
|
|
413
|
-
if (p) {
|
|
414
|
-
this.map.delete(file);
|
|
415
|
-
this.map.set(file, p);
|
|
416
|
-
return p;
|
|
417
|
-
}
|
|
418
|
-
p = parseSession(file);
|
|
419
|
-
this.map.set(file, p);
|
|
420
|
-
while (this.map.size > this.max)
|
|
421
|
-
this.map.delete(this.map.keys().next().value);
|
|
422
|
-
return p;
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
// packages/core/src/digest.ts
|
|
427
242
|
var HOME_PATH_RE = new RegExp(`(?:^|[\\s'"=(:])((?:~|/Users/[^/\\s'"]+|/home/[^/\\s'"]+|${homedir2().replace(/[.*+?^${}()|[\]\\]/g, "\\$&")})/[^\\s'"\`;|&)<>]+)`, "g");
|
|
428
243
|
var SESSION_EVENT = "wierd-memory";
|
|
429
244
|
// packages/core/src/project.ts
|
|
430
245
|
var cache = new Map;
|
|
431
|
-
// packages/core/src/projects.ts
|
|
432
|
-
class ProjectMap {
|
|
433
|
-
roots;
|
|
434
|
-
byKey = new Map;
|
|
435
|
-
constructor(entries) {
|
|
436
|
-
const byRoot = new Map;
|
|
437
|
-
for (const e of entries)
|
|
438
|
-
if (e.root && e.key && e.root !== "/")
|
|
439
|
-
byRoot.set(e.root.replace(/\/+$/, ""), { key: e.key, project: e.project ?? true });
|
|
440
|
-
const roots = [...byRoot.entries()].map(([root, v]) => ({ root, key: v.key, project: v.project, broad: false }));
|
|
441
|
-
for (const r of roots)
|
|
442
|
-
r.broad = roots.some((o) => o !== r && o.root.startsWith(`${r.root}/`));
|
|
443
|
-
roots.sort((a, b) => b.root.length - a.root.length);
|
|
444
|
-
this.roots = roots;
|
|
445
|
-
for (const r of roots)
|
|
446
|
-
if (!this.byKey.has(r.key) || !r.broad)
|
|
447
|
-
this.byKey.set(r.key, r);
|
|
448
|
-
}
|
|
449
|
-
projectOf(path) {
|
|
450
|
-
return this.roots.find((r) => path === r.root || path.startsWith(`${r.root}/`));
|
|
451
|
-
}
|
|
452
|
-
rootOf(key) {
|
|
453
|
-
return this.byKey.get(key);
|
|
454
|
-
}
|
|
455
|
-
isBroad(key) {
|
|
456
|
-
return this.byKey.get(key)?.broad ?? false;
|
|
457
|
-
}
|
|
458
|
-
isProject(key) {
|
|
459
|
-
const r = this.byKey.get(key);
|
|
460
|
-
return !!r && r.project && !r.broad;
|
|
461
|
-
}
|
|
462
|
-
touched(paths) {
|
|
463
|
-
const counts = new Map;
|
|
464
|
-
for (const [p, n] of paths) {
|
|
465
|
-
const r = this.projectOf(p);
|
|
466
|
-
if (r && !r.broad && r.project)
|
|
467
|
-
counts.set(r.key, (counts.get(r.key) ?? 0) + n);
|
|
468
|
-
}
|
|
469
|
-
return [...counts.entries()].map(([key, n]) => ({ key, n })).sort((a, b) => b.n - a.n);
|
|
470
|
-
}
|
|
471
|
-
primary(cwdKey, paths) {
|
|
472
|
-
const touched = this.touched(paths);
|
|
473
|
-
const workspace = this.isBroad(cwdKey);
|
|
474
|
-
if (!workspace)
|
|
475
|
-
return { key: cwdKey, touched, workspace };
|
|
476
|
-
return { key: touched[0]?.key ?? cwdKey, touched, workspace };
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
246
|
// packages/core/src/prompts.ts
|
|
480
247
|
var OPS_SCHEMA = `Operations — each op has "ref" (unique, e.g. "op1"), "rationale" (why a future session needs this and how we know it is true) and "evidence" (a list of {"s": sessionId | "inbox:<id>", "e": [entryIds]} copied from the input; never invent ids). "scope" is "global", "project" (the project of this pass) or "project:<key>" (another project from the input):
|
|
481
248
|
- {"op":"add","scope":"…","type":"preference|procedure|pitfall|fact|decision|reference","title":"…","body":"…","when":"…","index":"always|pointer|none", …}
|
|
@@ -536,190 +303,10 @@ var MINUTE = 60000;
|
|
|
536
303
|
var HOUR = 60 * MINUTE;
|
|
537
304
|
var DAY = 24 * HOUR;
|
|
538
305
|
// packages/core/src/search.ts
|
|
539
|
-
var RU_PREFIXES = ["пере", "под", "при", "про", "раз", "рас", "за", "вы", "по", "на", "от", "до", "об", "вз", "из"];
|
|
540
306
|
var STOP = new Set("и в во не что он на я с со как а то все она так его но да ты к у же вы за бы по только ее мне было вот от меня еще нет о из ему теперь когда даже ну вдруг ли если уже или ни быть был него до вас нибудь опять уж вам ведь там потом себя ничего ей может они тут где есть надо ней для мы тебя их чем была сам чтоб без будто чего раз тоже себе под будет ж тогда кто этот того потому этого какой совсем ним здесь этом один почти мой тем чтобы нее сейчас были куда зачем всех никогда можно при наконец два об другой хоть после над больше тот через эти нас про всего них какая много разве три эту моя впрочем хорошо свою этой перед иногда лучше чуть том нельзя такой им более всегда конечно всю между the a an and or of to in on for is it this that with be as at by from are was were has have had not but if then so do does did can will just".split(" "));
|
|
541
|
-
function ruForms(word) {
|
|
542
|
-
if (word.length <= 4)
|
|
543
|
-
return [word];
|
|
544
|
-
const out = [word.slice(0, 4)];
|
|
545
|
-
if (word.length >= 6)
|
|
546
|
-
out.push(word.slice(0, 6));
|
|
547
|
-
for (const p of RU_PREFIXES) {
|
|
548
|
-
if (word.startsWith(p) && word.length - p.length >= 5) {
|
|
549
|
-
const rest = word.slice(p.length);
|
|
550
|
-
out.push(rest.slice(0, 4));
|
|
551
|
-
if (rest.length >= 6)
|
|
552
|
-
out.push(rest.slice(0, 6));
|
|
553
|
-
break;
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
return out;
|
|
557
|
-
}
|
|
558
|
-
function enStem(word) {
|
|
559
|
-
if (word.length > 4) {
|
|
560
|
-
for (const suf of ["ing", "ed", "es", "s"])
|
|
561
|
-
if (word.endsWith(suf) && word.length - suf.length >= 3)
|
|
562
|
-
return word.slice(0, -suf.length);
|
|
563
|
-
}
|
|
564
|
-
return word;
|
|
565
|
-
}
|
|
566
|
-
function tokenize(text) {
|
|
567
|
-
const out = [];
|
|
568
|
-
for (const raw of text.toLowerCase().replace(/ё/g, "е").split(/[^\p{L}\p{N}_]+/u)) {
|
|
569
|
-
if (raw.length < 2 || STOP.has(raw))
|
|
570
|
-
continue;
|
|
571
|
-
if (/^[а-я]+$/.test(raw))
|
|
572
|
-
out.push(...ruForms(raw));
|
|
573
|
-
else if (/^[a-z]+$/.test(raw))
|
|
574
|
-
out.push(enStem(raw));
|
|
575
|
-
else
|
|
576
|
-
out.push(raw);
|
|
577
|
-
}
|
|
578
|
-
return out;
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
class Bm25 {
|
|
582
|
-
docs;
|
|
583
|
-
k1;
|
|
584
|
-
b;
|
|
585
|
-
postings = new Map;
|
|
586
|
-
lengths = [];
|
|
587
|
-
avg = 0;
|
|
588
|
-
constructor(docs, k1 = 1.2, b = 0.75) {
|
|
589
|
-
this.docs = docs;
|
|
590
|
-
this.k1 = k1;
|
|
591
|
-
this.b = b;
|
|
592
|
-
let total = 0;
|
|
593
|
-
docs.forEach((doc, i) => {
|
|
594
|
-
const tf = new Map;
|
|
595
|
-
const toks = tokenize(doc.text);
|
|
596
|
-
for (const t of toks)
|
|
597
|
-
tf.set(t, (tf.get(t) ?? 0) + 1);
|
|
598
|
-
for (const [t, n] of tf) {
|
|
599
|
-
let list = this.postings.get(t);
|
|
600
|
-
if (!list)
|
|
601
|
-
this.postings.set(t, list = []);
|
|
602
|
-
list.push([i, n]);
|
|
603
|
-
}
|
|
604
|
-
this.lengths.push(toks.length);
|
|
605
|
-
total += toks.length;
|
|
606
|
-
});
|
|
607
|
-
this.avg = docs.length ? total / docs.length : 0;
|
|
608
|
-
}
|
|
609
|
-
search(query, limit = 10, filter) {
|
|
610
|
-
const terms = [...new Set(tokenize(query))];
|
|
611
|
-
if (!terms.length || !this.docs.length)
|
|
612
|
-
return [];
|
|
613
|
-
const scores = new Map;
|
|
614
|
-
const N = this.docs.length;
|
|
615
|
-
for (const t of terms) {
|
|
616
|
-
const list = this.postings.get(t);
|
|
617
|
-
if (!list)
|
|
618
|
-
continue;
|
|
619
|
-
const idf = Math.log(1 + (N - list.length + 0.5) / (list.length + 0.5));
|
|
620
|
-
for (const [i, tf] of list) {
|
|
621
|
-
const len = this.lengths[i] || 1;
|
|
622
|
-
const s = idf * tf * (this.k1 + 1) / (tf + this.k1 * (1 - this.b + this.b * len / (this.avg || 1)));
|
|
623
|
-
scores.set(i, (scores.get(i) ?? 0) + s);
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
return [...scores.entries()].filter(([i]) => !filter || filter(this.docs[i])).sort((a, b) => b[1] - a[1]).slice(0, limit).map(([i, score]) => ({ doc: this.docs[i], score }));
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
// packages/core/src/workspace.ts
|
|
630
|
-
class Workspace {
|
|
631
|
-
date;
|
|
632
|
-
notes = new Map;
|
|
633
|
-
ids = new Set;
|
|
634
|
-
pending = new Set;
|
|
635
|
-
skills = new Map;
|
|
636
|
-
original = new Map;
|
|
637
|
-
dirty = new Set;
|
|
638
|
-
gone = new Map;
|
|
639
|
-
metas = new Map;
|
|
640
|
-
known = new Set;
|
|
641
|
-
constructor(notes, date, opts = {}) {
|
|
642
|
-
this.date = date;
|
|
643
|
-
for (const n of notes) {
|
|
644
|
-
const copy = cloneNote(n);
|
|
645
|
-
this.scope(copy.scope).push(copy);
|
|
646
|
-
this.ids.add(copy.id);
|
|
647
|
-
this.original.set(copy.id, cloneNote(n));
|
|
648
|
-
}
|
|
649
|
-
for (const s of opts.scopes ?? []) {
|
|
650
|
-
this.known.add(s.id);
|
|
651
|
-
this.scope(s.id);
|
|
652
|
-
}
|
|
653
|
-
for (const id of opts.pending ?? [])
|
|
654
|
-
this.pending.add(id);
|
|
655
|
-
this.scope(GLOBAL_SCOPE);
|
|
656
|
-
}
|
|
657
|
-
scope(scope) {
|
|
658
|
-
let list = this.notes.get(scope);
|
|
659
|
-
if (!list)
|
|
660
|
-
this.notes.set(scope, list = []);
|
|
661
|
-
return list;
|
|
662
|
-
}
|
|
663
|
-
ensureScope(scope, meta) {
|
|
664
|
-
this.scope(scope);
|
|
665
|
-
if (meta && !this.known.has(scope))
|
|
666
|
-
this.metas.set(scope, { id: scope, ...meta });
|
|
667
|
-
}
|
|
668
|
-
all() {
|
|
669
|
-
return [...this.notes.values()].flat();
|
|
670
|
-
}
|
|
671
|
-
find(id) {
|
|
672
|
-
for (const list of this.notes.values())
|
|
673
|
-
for (const n of list)
|
|
674
|
-
if (n.id === id)
|
|
675
|
-
return n;
|
|
676
|
-
return;
|
|
677
|
-
}
|
|
678
|
-
createNote(scope, f, evidence) {
|
|
679
|
-
const note = makeNote({ id: newNoteId(this.ids), scope, title: f.title, type: f.type, index: f.index, when: f.when, body: f.body ?? "", evidence: [...evidence], stats: { helpful: 0, harmful: 0 } }, this.date);
|
|
680
|
-
this.scope(scope).push(note);
|
|
681
|
-
this.pending.add(note.id);
|
|
682
|
-
this.dirty.add(note);
|
|
683
|
-
return note;
|
|
684
|
-
}
|
|
685
|
-
touch(n) {
|
|
686
|
-
this.dirty.add(n);
|
|
687
|
-
}
|
|
688
|
-
moveNote(n, scope) {
|
|
689
|
-
const from = this.scope(n.scope);
|
|
690
|
-
const i = from.indexOf(n);
|
|
691
|
-
if (i >= 0)
|
|
692
|
-
from.splice(i, 1);
|
|
693
|
-
n.scope = scope;
|
|
694
|
-
this.scope(scope).push(n);
|
|
695
|
-
this.dirty.add(n);
|
|
696
|
-
}
|
|
697
|
-
removeNote(n) {
|
|
698
|
-
const list = this.scope(n.scope);
|
|
699
|
-
const i = list.indexOf(n);
|
|
700
|
-
if (i >= 0)
|
|
701
|
-
list.splice(i, 1);
|
|
702
|
-
this.dirty.delete(n);
|
|
703
|
-
this.pending.delete(n.id);
|
|
704
|
-
const before = this.original.get(n.id);
|
|
705
|
-
if (before)
|
|
706
|
-
this.gone.set(n.id, before);
|
|
707
|
-
}
|
|
708
|
-
changes() {
|
|
709
|
-
const notes = [];
|
|
710
|
-
for (const n of this.dirty) {
|
|
711
|
-
const before = this.original.get(n.id);
|
|
712
|
-
if (before && sameNote(before, n))
|
|
713
|
-
continue;
|
|
714
|
-
notes.push({ before, after: cloneNote(n) });
|
|
715
|
-
}
|
|
716
|
-
const scopes = [...this.metas.values()].filter((m) => this.scope(m.id).length > 0 || [...this.skills.values()].some((s) => s.scope === m.id));
|
|
717
|
-
return { notes, removed: [...this.gone.values()], scopes, skills: [...this.skills.values()] };
|
|
718
|
-
}
|
|
719
|
-
}
|
|
720
307
|
// packages/pi/client.ts
|
|
721
308
|
import { execFile, spawn } from "node:child_process";
|
|
722
|
-
import { accessSync, closeSync, constants, existsSync as
|
|
309
|
+
import { accessSync, closeSync, constants, existsSync as existsSync2, mkdirSync as mkdirSync2, openSync } from "node:fs";
|
|
723
310
|
import { homedir as homedir3 } from "node:os";
|
|
724
311
|
import { delimiter, dirname, join as join2, resolve as resolve2 } from "node:path";
|
|
725
312
|
import { fileURLToPath } from "node:url";
|
|
@@ -815,7 +402,7 @@ class Client {
|
|
|
815
402
|
}
|
|
816
403
|
}
|
|
817
404
|
async function launch() {
|
|
818
|
-
if (process.platform === "darwin" && !process.env.WMEM_HOME &&
|
|
405
|
+
if (process.platform === "darwin" && !process.env.WMEM_HOME && existsSync2(PLIST)) {
|
|
819
406
|
const target = `gui/${process.getuid?.() ?? 501}`;
|
|
820
407
|
if (await run("launchctl", ["kickstart", `${target}/${LABEL}`]) === 0)
|
|
821
408
|
return "launchctl kickstart";
|
|
@@ -867,13 +454,13 @@ function onPath(name) {
|
|
|
867
454
|
function checkoutBin() {
|
|
868
455
|
const repo = resolve2(dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
869
456
|
const bin = join2(repo, "bin", "wmem");
|
|
870
|
-
return
|
|
457
|
+
return existsSync2(bin) && existsSync2(join2(repo, "packages", "server", "src", "cli.ts")) ? bin : undefined;
|
|
871
458
|
}
|
|
872
459
|
|
|
873
460
|
// packages/pi/runtime.ts
|
|
874
461
|
var EVENT = SESSION_EVENT;
|
|
875
462
|
function disabledByEnv() {
|
|
876
|
-
return process.env.WMEM_DISABLE === "1" || process.env.WMEM_CHILD === "1"
|
|
463
|
+
return process.env.WMEM_DISABLE === "1" || process.env.WMEM_CHILD === "1";
|
|
877
464
|
}
|
|
878
465
|
var why2 = (err) => err instanceof Error ? err.message : String(err);
|
|
879
466
|
|
|
@@ -949,7 +536,7 @@ class Runtime {
|
|
|
949
536
|
}
|
|
950
537
|
async resourcesFor(cwd) {
|
|
951
538
|
const info = await Promise.race([this.contextFor(cwd), new Promise((r) => setTimeout(() => r(undefined), 2500))]);
|
|
952
|
-
return (info?.skills ?? []).filter((d) =>
|
|
539
|
+
return (info?.skills ?? []).filter((d) => existsSync3(d));
|
|
953
540
|
}
|
|
954
541
|
async guarded(fn) {
|
|
955
542
|
try {
|
|
@@ -968,9 +555,9 @@ class Runtime {
|
|
|
968
555
|
return `Memory is unavailable: ${why2(err)}`;
|
|
969
556
|
}
|
|
970
557
|
}
|
|
971
|
-
search(ctx, query, scope,
|
|
558
|
+
search(ctx, query, scope, projects, limit) {
|
|
972
559
|
return this.guarded(async () => {
|
|
973
|
-
const p = new URLSearchParams({ q: query, scope, projects
|
|
560
|
+
const p = new URLSearchParams({ q: query, scope, projects, cwd: ctx.cwd, limit: String(limit), exclude: ctx.sessionManager.getSessionId() });
|
|
974
561
|
return (await this.client.get(`/api/search?${p}`)).text;
|
|
975
562
|
});
|
|
976
563
|
}
|
|
@@ -997,11 +584,11 @@ class Runtime {
|
|
|
997
584
|
}
|
|
998
585
|
async dreamLog(lines = 40) {
|
|
999
586
|
const s = await this.client.get("/api/dreams?limit=1");
|
|
1000
|
-
const
|
|
1001
|
-
if (!
|
|
587
|
+
const run = s.running ?? s.runs[0];
|
|
588
|
+
if (!run)
|
|
1002
589
|
return "no dreams yet";
|
|
1003
|
-
const text = await this.client.get(`/api/dreams/${
|
|
1004
|
-
return `dream ${
|
|
590
|
+
const text = await this.client.get(`/api/dreams/${run.id}/log?tail=${lines}`);
|
|
591
|
+
return `dream ${run.id} (${run.status}):
|
|
1005
592
|
${text.trimEnd() || "(empty log)"}`;
|
|
1006
593
|
}
|
|
1007
594
|
async notifyReview(ctx) {
|
|
@@ -1119,7 +706,7 @@ function registerTools(pi, rt) {
|
|
|
1119
706
|
|
|
1120
707
|
// packages/pi/index.ts
|
|
1121
708
|
function piWierdMemory(pi) {
|
|
1122
|
-
if (process.env.WMEM_CHILD === "1"
|
|
709
|
+
if (process.env.WMEM_CHILD === "1")
|
|
1123
710
|
return;
|
|
1124
711
|
pi.registerFlag("no-memory", {
|
|
1125
712
|
description: "wierd-memory: no memory injection and no memory tools in this run",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wierdbytes/pi-memory",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Long-term memory for pi: injects the approved wierd-memory index into every session and adds memory_search / memory_read / memory_inbox tools and /memory, /dream, /remember commands. Needs the wierd-memory server.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|