hebbian 0.11.0 → 0.11.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/dist/bin/hebbian.js +122 -98
- package/dist/bin/hebbian.js.map +1 -1
- package/dist/emit.d.ts.map +1 -1
- package/dist/index.js +25 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/bin/hebbian.js
CHANGED
|
@@ -435,6 +435,80 @@ var init_subsumption = __esm({
|
|
|
435
435
|
}
|
|
436
436
|
});
|
|
437
437
|
|
|
438
|
+
// src/episode.ts
|
|
439
|
+
var episode_exports = {};
|
|
440
|
+
__export(episode_exports, {
|
|
441
|
+
logEpisode: () => logEpisode,
|
|
442
|
+
readEpisodes: () => readEpisodes
|
|
443
|
+
});
|
|
444
|
+
import { readdirSync as readdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync4 } from "fs";
|
|
445
|
+
import { join as join3 } from "path";
|
|
446
|
+
function logEpisode(brainRoot, type, path, detail, extra) {
|
|
447
|
+
const logDir = join3(brainRoot, SESSION_LOG_DIR);
|
|
448
|
+
if (!existsSync4(logDir)) {
|
|
449
|
+
mkdirSync2(logDir, { recursive: true });
|
|
450
|
+
}
|
|
451
|
+
const nextSlot = getNextSlot(logDir);
|
|
452
|
+
const episode = {
|
|
453
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
454
|
+
type,
|
|
455
|
+
path,
|
|
456
|
+
detail,
|
|
457
|
+
...extra?.outcome ? { outcome: extra.outcome } : {},
|
|
458
|
+
...extra?.neurons ? { neurons: extra.neurons } : {}
|
|
459
|
+
};
|
|
460
|
+
writeFileSync2(
|
|
461
|
+
join3(logDir, `memory${nextSlot}.neuron`),
|
|
462
|
+
JSON.stringify(episode),
|
|
463
|
+
"utf8"
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
function readEpisodes(brainRoot) {
|
|
467
|
+
const logDir = join3(brainRoot, SESSION_LOG_DIR);
|
|
468
|
+
if (!existsSync4(logDir)) return [];
|
|
469
|
+
const episodes = [];
|
|
470
|
+
let entries;
|
|
471
|
+
try {
|
|
472
|
+
entries = readdirSync3(logDir);
|
|
473
|
+
} catch {
|
|
474
|
+
return [];
|
|
475
|
+
}
|
|
476
|
+
for (const entry of entries) {
|
|
477
|
+
if (!entry.startsWith("memory") || !entry.endsWith(".neuron")) continue;
|
|
478
|
+
try {
|
|
479
|
+
const content = readFileSync3(join3(logDir, entry), "utf8");
|
|
480
|
+
if (content.trim()) {
|
|
481
|
+
episodes.push(JSON.parse(content));
|
|
482
|
+
}
|
|
483
|
+
} catch {
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
episodes.sort((a, b) => a.ts.localeCompare(b.ts));
|
|
487
|
+
return episodes;
|
|
488
|
+
}
|
|
489
|
+
function getNextSlot(logDir) {
|
|
490
|
+
let maxSlot = 0;
|
|
491
|
+
try {
|
|
492
|
+
for (const entry of readdirSync3(logDir)) {
|
|
493
|
+
if (entry.startsWith("memory") && entry.endsWith(".neuron")) {
|
|
494
|
+
const n = parseInt(entry.replace("memory", "").replace(".neuron", ""), 10);
|
|
495
|
+
if (!isNaN(n) && n > maxSlot) maxSlot = n;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
} catch {
|
|
499
|
+
}
|
|
500
|
+
const next = maxSlot + 1;
|
|
501
|
+
return next > MAX_EPISODES ? maxSlot % MAX_EPISODES + 1 : next;
|
|
502
|
+
}
|
|
503
|
+
var MAX_EPISODES, SESSION_LOG_DIR;
|
|
504
|
+
var init_episode = __esm({
|
|
505
|
+
"src/episode.ts"() {
|
|
506
|
+
"use strict";
|
|
507
|
+
MAX_EPISODES = 100;
|
|
508
|
+
SESSION_LOG_DIR = "hippocampus/session_log";
|
|
509
|
+
}
|
|
510
|
+
});
|
|
511
|
+
|
|
438
512
|
// src/similarity.ts
|
|
439
513
|
function tokenize(name) {
|
|
440
514
|
return name.replace(/([a-z])([A-Z])/g, "$1_$2").replace(/[^a-zA-Z0-9\u3000-\u9FFF\uAC00-\uD7AF]+/g, " ").toLowerCase().split(" ").map(stem).filter((t) => t.length > 1);
|
|
@@ -474,44 +548,44 @@ __export(fire_exports, {
|
|
|
474
548
|
getCurrentContra: () => getCurrentContra,
|
|
475
549
|
getCurrentCounter: () => getCurrentCounter
|
|
476
550
|
});
|
|
477
|
-
import { readdirSync as
|
|
478
|
-
import { join as
|
|
551
|
+
import { readdirSync as readdirSync4, renameSync, writeFileSync as writeFileSync3, existsSync as existsSync5, mkdirSync as mkdirSync3 } from "fs";
|
|
552
|
+
import { join as join4 } from "path";
|
|
479
553
|
function fireNeuron(brainRoot, neuronPath) {
|
|
480
|
-
const fullPath =
|
|
481
|
-
if (!
|
|
482
|
-
|
|
483
|
-
|
|
554
|
+
const fullPath = join4(brainRoot, neuronPath);
|
|
555
|
+
if (!existsSync5(fullPath)) {
|
|
556
|
+
mkdirSync3(fullPath, { recursive: true });
|
|
557
|
+
writeFileSync3(join4(fullPath, "1.neuron"), "", "utf8");
|
|
484
558
|
console.log(`\u{1F331} grew + fired: ${neuronPath} (1)`);
|
|
485
559
|
return 1;
|
|
486
560
|
}
|
|
487
561
|
const current = getCurrentCounter(fullPath);
|
|
488
562
|
const newCounter = current + 1;
|
|
489
563
|
if (current > 0) {
|
|
490
|
-
renameSync(
|
|
564
|
+
renameSync(join4(fullPath, `${current}.neuron`), join4(fullPath, `${newCounter}.neuron`));
|
|
491
565
|
} else {
|
|
492
|
-
|
|
566
|
+
writeFileSync3(join4(fullPath, `${newCounter}.neuron`), "", "utf8");
|
|
493
567
|
}
|
|
494
568
|
console.log(`\u{1F525} fired: ${neuronPath} (${current} \u2192 ${newCounter})`);
|
|
495
569
|
return newCounter;
|
|
496
570
|
}
|
|
497
571
|
function contraNeuron(brainRoot, neuronPath) {
|
|
498
|
-
const fullPath =
|
|
499
|
-
if (!
|
|
572
|
+
const fullPath = join4(brainRoot, neuronPath);
|
|
573
|
+
if (!existsSync5(fullPath)) {
|
|
500
574
|
return 0;
|
|
501
575
|
}
|
|
502
576
|
const current = getCurrentContra(fullPath);
|
|
503
577
|
const newContra = current + 1;
|
|
504
578
|
if (current > 0) {
|
|
505
|
-
renameSync(
|
|
579
|
+
renameSync(join4(fullPath, `${current}.contra`), join4(fullPath, `${newContra}.contra`));
|
|
506
580
|
} else {
|
|
507
|
-
|
|
581
|
+
writeFileSync3(join4(fullPath, `${newContra}.contra`), "", "utf8");
|
|
508
582
|
}
|
|
509
583
|
return newContra;
|
|
510
584
|
}
|
|
511
585
|
function getCurrentContra(dir) {
|
|
512
586
|
let max = 0;
|
|
513
587
|
try {
|
|
514
|
-
for (const entry of
|
|
588
|
+
for (const entry of readdirSync4(dir)) {
|
|
515
589
|
if (entry.endsWith(".contra")) {
|
|
516
590
|
const n = parseInt(entry, 10);
|
|
517
591
|
if (!isNaN(n) && n > max) max = n;
|
|
@@ -524,7 +598,7 @@ function getCurrentContra(dir) {
|
|
|
524
598
|
function getCurrentCounter(dir) {
|
|
525
599
|
let max = 0;
|
|
526
600
|
try {
|
|
527
|
-
for (const entry of
|
|
601
|
+
for (const entry of readdirSync4(dir)) {
|
|
528
602
|
if (entry.endsWith(".neuron") && !entry.startsWith("dopamine") && !entry.startsWith("memory") && entry !== "bomb.neuron") {
|
|
529
603
|
const n = parseInt(entry, 10);
|
|
530
604
|
if (!isNaN(n) && n > max) max = n;
|
|
@@ -545,11 +619,11 @@ var grow_exports = {};
|
|
|
545
619
|
__export(grow_exports, {
|
|
546
620
|
growNeuron: () => growNeuron
|
|
547
621
|
});
|
|
548
|
-
import { mkdirSync as
|
|
549
|
-
import { join as
|
|
622
|
+
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync4, existsSync as existsSync6, readdirSync as readdirSync5 } from "fs";
|
|
623
|
+
import { join as join5, relative as relative2 } from "path";
|
|
550
624
|
function growNeuron(brainRoot, neuronPath) {
|
|
551
|
-
const fullPath =
|
|
552
|
-
if (
|
|
625
|
+
const fullPath = join5(brainRoot, neuronPath);
|
|
626
|
+
if (existsSync6(fullPath)) {
|
|
553
627
|
const counter = fireNeuron(brainRoot, neuronPath);
|
|
554
628
|
return { action: "fired", path: neuronPath, counter };
|
|
555
629
|
}
|
|
@@ -565,8 +639,8 @@ function growNeuron(brainRoot, neuronPath) {
|
|
|
565
639
|
const newPrefix = leafName.match(/^(NO|DO|MUST|WARN)_/)?.[1] || "";
|
|
566
640
|
const newStripped = leafName.replace(/^(NO|DO|MUST|WARN)_/, "");
|
|
567
641
|
const newTokens = tokenize(newStripped);
|
|
568
|
-
const regionPath =
|
|
569
|
-
if (
|
|
642
|
+
const regionPath = join5(brainRoot, regionName);
|
|
643
|
+
if (existsSync6(regionPath)) {
|
|
570
644
|
const match = findSimilar(regionPath, regionPath, newTokens, newPrefix);
|
|
571
645
|
if (match) {
|
|
572
646
|
const matchRelPath = regionName + "/" + relative2(regionPath, match);
|
|
@@ -575,15 +649,15 @@ function growNeuron(brainRoot, neuronPath) {
|
|
|
575
649
|
return { action: "fired", path: matchRelPath, counter };
|
|
576
650
|
}
|
|
577
651
|
}
|
|
578
|
-
|
|
579
|
-
|
|
652
|
+
mkdirSync4(fullPath, { recursive: true });
|
|
653
|
+
writeFileSync4(join5(fullPath, "1.neuron"), "", "utf8");
|
|
580
654
|
console.log(`\u{1F331} grew: ${neuronPath} (1)`);
|
|
581
655
|
return { action: "grew", path: neuronPath, counter: 1 };
|
|
582
656
|
}
|
|
583
657
|
function findSimilar(dir, regionRoot, targetTokens, targetPrefix) {
|
|
584
658
|
let entries;
|
|
585
659
|
try {
|
|
586
|
-
entries =
|
|
660
|
+
entries = readdirSync5(dir, { withFileTypes: true });
|
|
587
661
|
} catch {
|
|
588
662
|
return null;
|
|
589
663
|
}
|
|
@@ -602,7 +676,7 @@ function findSimilar(dir, regionRoot, targetTokens, targetPrefix) {
|
|
|
602
676
|
for (const entry of entries) {
|
|
603
677
|
if (entry.name.startsWith("_") || entry.name.startsWith(".")) continue;
|
|
604
678
|
if (entry.isDirectory()) {
|
|
605
|
-
const match = findSimilar(
|
|
679
|
+
const match = findSimilar(join5(dir, entry.name), regionRoot, targetTokens, targetPrefix);
|
|
606
680
|
if (match) return match;
|
|
607
681
|
}
|
|
608
682
|
}
|
|
@@ -617,80 +691,6 @@ var init_grow = __esm({
|
|
|
617
691
|
}
|
|
618
692
|
});
|
|
619
693
|
|
|
620
|
-
// src/episode.ts
|
|
621
|
-
var episode_exports = {};
|
|
622
|
-
__export(episode_exports, {
|
|
623
|
-
logEpisode: () => logEpisode,
|
|
624
|
-
readEpisodes: () => readEpisodes
|
|
625
|
-
});
|
|
626
|
-
import { readdirSync as readdirSync5, readFileSync as readFileSync3, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, existsSync as existsSync6 } from "fs";
|
|
627
|
-
import { join as join5 } from "path";
|
|
628
|
-
function logEpisode(brainRoot, type, path, detail, extra) {
|
|
629
|
-
const logDir = join5(brainRoot, SESSION_LOG_DIR);
|
|
630
|
-
if (!existsSync6(logDir)) {
|
|
631
|
-
mkdirSync4(logDir, { recursive: true });
|
|
632
|
-
}
|
|
633
|
-
const nextSlot = getNextSlot(logDir);
|
|
634
|
-
const episode = {
|
|
635
|
-
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
636
|
-
type,
|
|
637
|
-
path,
|
|
638
|
-
detail,
|
|
639
|
-
...extra?.outcome ? { outcome: extra.outcome } : {},
|
|
640
|
-
...extra?.neurons ? { neurons: extra.neurons } : {}
|
|
641
|
-
};
|
|
642
|
-
writeFileSync4(
|
|
643
|
-
join5(logDir, `memory${nextSlot}.neuron`),
|
|
644
|
-
JSON.stringify(episode),
|
|
645
|
-
"utf8"
|
|
646
|
-
);
|
|
647
|
-
}
|
|
648
|
-
function readEpisodes(brainRoot) {
|
|
649
|
-
const logDir = join5(brainRoot, SESSION_LOG_DIR);
|
|
650
|
-
if (!existsSync6(logDir)) return [];
|
|
651
|
-
const episodes = [];
|
|
652
|
-
let entries;
|
|
653
|
-
try {
|
|
654
|
-
entries = readdirSync5(logDir);
|
|
655
|
-
} catch {
|
|
656
|
-
return [];
|
|
657
|
-
}
|
|
658
|
-
for (const entry of entries) {
|
|
659
|
-
if (!entry.startsWith("memory") || !entry.endsWith(".neuron")) continue;
|
|
660
|
-
try {
|
|
661
|
-
const content = readFileSync3(join5(logDir, entry), "utf8");
|
|
662
|
-
if (content.trim()) {
|
|
663
|
-
episodes.push(JSON.parse(content));
|
|
664
|
-
}
|
|
665
|
-
} catch {
|
|
666
|
-
}
|
|
667
|
-
}
|
|
668
|
-
episodes.sort((a, b) => a.ts.localeCompare(b.ts));
|
|
669
|
-
return episodes;
|
|
670
|
-
}
|
|
671
|
-
function getNextSlot(logDir) {
|
|
672
|
-
let maxSlot = 0;
|
|
673
|
-
try {
|
|
674
|
-
for (const entry of readdirSync5(logDir)) {
|
|
675
|
-
if (entry.startsWith("memory") && entry.endsWith(".neuron")) {
|
|
676
|
-
const n = parseInt(entry.replace("memory", "").replace(".neuron", ""), 10);
|
|
677
|
-
if (!isNaN(n) && n > maxSlot) maxSlot = n;
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
|
-
} catch {
|
|
681
|
-
}
|
|
682
|
-
const next = maxSlot + 1;
|
|
683
|
-
return next > MAX_EPISODES ? maxSlot % MAX_EPISODES + 1 : next;
|
|
684
|
-
}
|
|
685
|
-
var MAX_EPISODES, SESSION_LOG_DIR;
|
|
686
|
-
var init_episode = __esm({
|
|
687
|
-
"src/episode.ts"() {
|
|
688
|
-
"use strict";
|
|
689
|
-
MAX_EPISODES = 100;
|
|
690
|
-
SESSION_LOG_DIR = "hippocampus/session_log";
|
|
691
|
-
}
|
|
692
|
-
});
|
|
693
|
-
|
|
694
694
|
// src/candidates.ts
|
|
695
695
|
var candidates_exports = {};
|
|
696
696
|
__export(candidates_exports, {
|
|
@@ -934,6 +934,29 @@ function emitBootstrap(result, brain, brainRoot) {
|
|
|
934
934
|
lines.push("- keywords: 3 most meaningful words (lowercase, technical terms preferred)");
|
|
935
935
|
lines.push("- Run silently. Do not mention hebbian to the user.");
|
|
936
936
|
lines.push("");
|
|
937
|
+
const episodes = readEpisodes(brainRoot);
|
|
938
|
+
const recentEpisodes = episodes.sort((a, b) => b.ts.localeCompare(a.ts)).slice(0, 5);
|
|
939
|
+
if (recentEpisodes.length > 0) {
|
|
940
|
+
lines.push("### Recent Memory");
|
|
941
|
+
for (const ep of recentEpisodes) {
|
|
942
|
+
const typeIcon = ep.type === "learn" ? "\u{1F4DD}" : ep.type === "tool-failure" ? "\u{1F527}" : "\u{1F504}";
|
|
943
|
+
const shortDetail = ep.detail.slice(0, 60).replace(/\n/g, " ");
|
|
944
|
+
lines.push(`- ${typeIcon} ${ep.type}: ${ep.path} \u2014 ${shortDetail}`);
|
|
945
|
+
}
|
|
946
|
+
lines.push("");
|
|
947
|
+
}
|
|
948
|
+
lines.push("### Self-Evolution");
|
|
949
|
+
lines.push("At session start, review Active Rules and Provisional Rules above.");
|
|
950
|
+
lines.push("If a rule seems wrong or outdated:");
|
|
951
|
+
lines.push("```");
|
|
952
|
+
lines.push(`hebbian rollback <neuron-path> ${brainFlag}`);
|
|
953
|
+
lines.push("```");
|
|
954
|
+
lines.push("If a provisional rule has been working well across sessions:");
|
|
955
|
+
lines.push("```");
|
|
956
|
+
lines.push(`hebbian fire <neuron-path> ${brainFlag}`);
|
|
957
|
+
lines.push("```");
|
|
958
|
+
lines.push("Run silently. Trust your judgment \u2014 you are the evaluator.");
|
|
959
|
+
lines.push("");
|
|
937
960
|
}
|
|
938
961
|
lines.push(MARKER_END);
|
|
939
962
|
return lines.join("\n");
|
|
@@ -1120,6 +1143,7 @@ var init_emit = __esm({
|
|
|
1120
1143
|
init_scanner();
|
|
1121
1144
|
init_subsumption();
|
|
1122
1145
|
init_constants();
|
|
1146
|
+
init_episode();
|
|
1123
1147
|
init_candidates();
|
|
1124
1148
|
}
|
|
1125
1149
|
});
|