hebbian 0.9.0 → 0.10.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.
@@ -1 +1 @@
1
- {"version":3,"file":"digest.d.ts","sourceRoot":"","sources":["../src/digest.ts"],"names":[],"mappings":"AAqBA,MAAM,WAAW,YAAY;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,mBAAmB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC;IACtC,QAAQ,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,UAAU,cAAc;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE;QACT,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAC;YAAC,WAAW,CAAC,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;SAAE,CAAC,CAAC;KACpG,CAAC;IACF,aAAa,CAAC,EAAE;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CAClB;AA8CD;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAYjG;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,YAAY,CAwF5G;AAsDD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,cAAc,EAAE,MAAM,GAAG,WAAW,EAAE,CAGtE;AAqCD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE,CAoB1E;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAChC,KAAK,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,EACxF,aAAa,CAAC,EAAE,cAAc,CAAC,eAAe,CAAC,GAC7C,WAAW,GAAG,IAAI,CA8BpB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAChC,KAAK,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,EACxF,aAAa,CAAC,EAAE,cAAc,CAAC,eAAe,CAAC,GAC7C,WAAW,GAAG,IAAI,CAiCpB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,mBAAmB,EAAE,CA2C5E"}
1
+ {"version":3,"file":"digest.d.ts","sourceRoot":"","sources":["../src/digest.ts"],"names":[],"mappings":"AAsBA,MAAM,WAAW,YAAY;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,mBAAmB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC;IACtC,QAAQ,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,UAAU,cAAc;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE;QACT,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAC;YAAC,WAAW,CAAC,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;SAAE,CAAC,CAAC;KACpG,CAAC;IACF,aAAa,CAAC,EAAE;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CAClB;AA8CD;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAYjG;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,YAAY,CA2F5G;AAsDD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,cAAc,EAAE,MAAM,GAAG,WAAW,EAAE,CAGtE;AAqCD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE,CAoB1E;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAChC,KAAK,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,EACxF,aAAa,CAAC,EAAE,cAAc,CAAC,eAAe,CAAC,GAC7C,WAAW,GAAG,IAAI,CA8BpB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAChC,KAAK,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,EACxF,aAAa,CAAC,EAAE,cAAc,CAAC,eAAe,CAAC,GAC7C,WAAW,GAAG,IAAI,CAiCpB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,mBAAmB,EAAE,CA2C5E"}
package/dist/emit.d.ts CHANGED
@@ -2,7 +2,7 @@ import type { Region, Brain, SubsumptionResult } from './types';
2
2
  /**
3
3
  * Generate Tier 1 bootstrap content.
4
4
  */
5
- export declare function emitBootstrap(result: SubsumptionResult, brain: Brain): string;
5
+ export declare function emitBootstrap(result: SubsumptionResult, brain: Brain, brainRoot?: string): string;
6
6
  /**
7
7
  * Generate Tier 2 brain index content.
8
8
  */
@@ -1 +1 @@
1
- {"version":3,"file":"emit.d.ts","sourceRoot":"","sources":["../src/emit.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAU,MAAM,EAAE,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAMxE;;GAEG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,iBAAiB,EAAE,KAAK,EAAE,KAAK,GAAG,MAAM,CAuE7E;AAMD;;GAEG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,iBAAiB,EAAE,KAAK,EAAE,KAAK,GAAG,MAAM,CAuDzE;AAMD;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAgDtD;AAMD;;GAEG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAmBpE;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI,CAY9F;AAoCD;;GAEG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAiCvE"}
1
+ {"version":3,"file":"emit.d.ts","sourceRoot":"","sources":["../src/emit.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAU,MAAM,EAAE,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAOxE;;GAEG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,iBAAiB,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAuFjG;AAMD;;GAEG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,iBAAiB,EAAE,KAAK,EAAE,KAAK,GAAG,MAAM,CAuDzE;AAMD;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAgDtD;AAMD;;GAEG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAmBpE;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI,CAY9F;AAoCD;;GAEG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAiCvE"}
package/dist/index.js CHANGED
@@ -614,9 +614,205 @@ function gitSnapshot(brainRoot) {
614
614
  import { watch } from "fs";
615
615
 
616
616
  // src/emit.ts
617
- import { existsSync as existsSync8, readFileSync as readFileSync2, writeFileSync as writeFileSync6, mkdirSync as mkdirSync3 } from "fs";
618
- import { join as join9, dirname } from "path";
619
- function emitBootstrap(result, brain) {
617
+ import { existsSync as existsSync10, readFileSync as readFileSync3, writeFileSync as writeFileSync7, mkdirSync as mkdirSync5 } from "fs";
618
+ import { join as join11, dirname as dirname2 } from "path";
619
+
620
+ // src/candidates.ts
621
+ import { existsSync as existsSync9, mkdirSync as mkdirSync4, readdirSync as readdirSync7, renameSync as renameSync3, rmSync, statSync as statSync3 } from "fs";
622
+ import { join as join10, dirname, relative as relative3 } from "path";
623
+
624
+ // src/episode.ts
625
+ import { readdirSync as readdirSync6, readFileSync as readFileSync2, writeFileSync as writeFileSync6, mkdirSync as mkdirSync3, existsSync as existsSync8 } from "fs";
626
+ import { join as join9 } from "path";
627
+ var MAX_EPISODES = 100;
628
+ var SESSION_LOG_DIR = "hippocampus/session_log";
629
+ function logEpisode(brainRoot, type, path, detail, extra) {
630
+ const logDir = join9(brainRoot, SESSION_LOG_DIR);
631
+ if (!existsSync8(logDir)) {
632
+ mkdirSync3(logDir, { recursive: true });
633
+ }
634
+ const nextSlot = getNextSlot(logDir);
635
+ const episode = {
636
+ ts: (/* @__PURE__ */ new Date()).toISOString(),
637
+ type,
638
+ path,
639
+ detail,
640
+ ...extra?.outcome ? { outcome: extra.outcome } : {},
641
+ ...extra?.neurons ? { neurons: extra.neurons } : {}
642
+ };
643
+ writeFileSync6(
644
+ join9(logDir, `memory${nextSlot}.neuron`),
645
+ JSON.stringify(episode),
646
+ "utf8"
647
+ );
648
+ }
649
+ function readEpisodes(brainRoot) {
650
+ const logDir = join9(brainRoot, SESSION_LOG_DIR);
651
+ if (!existsSync8(logDir)) return [];
652
+ const episodes = [];
653
+ let entries;
654
+ try {
655
+ entries = readdirSync6(logDir);
656
+ } catch {
657
+ return [];
658
+ }
659
+ for (const entry of entries) {
660
+ if (!entry.startsWith("memory") || !entry.endsWith(".neuron")) continue;
661
+ try {
662
+ const content = readFileSync2(join9(logDir, entry), "utf8");
663
+ if (content.trim()) {
664
+ episodes.push(JSON.parse(content));
665
+ }
666
+ } catch {
667
+ }
668
+ }
669
+ episodes.sort((a, b) => a.ts.localeCompare(b.ts));
670
+ return episodes;
671
+ }
672
+ function getNextSlot(logDir) {
673
+ let maxSlot = 0;
674
+ try {
675
+ for (const entry of readdirSync6(logDir)) {
676
+ if (entry.startsWith("memory") && entry.endsWith(".neuron")) {
677
+ const n = parseInt(entry.replace("memory", "").replace(".neuron", ""), 10);
678
+ if (!isNaN(n) && n > maxSlot) maxSlot = n;
679
+ }
680
+ }
681
+ } catch {
682
+ }
683
+ const next = maxSlot + 1;
684
+ return next > MAX_EPISODES ? maxSlot % MAX_EPISODES + 1 : next;
685
+ }
686
+
687
+ // src/candidates.ts
688
+ var CANDIDATE_THRESHOLD = 3;
689
+ var CANDIDATE_DECAY_DAYS = 14;
690
+ var CANDIDATE_SEGMENT = "_candidates";
691
+ function toCandidatePath(neuronPath) {
692
+ const slash = neuronPath.indexOf("/");
693
+ if (slash === -1) throw new Error(`Invalid neuron path (missing region): ${neuronPath}`);
694
+ return `${neuronPath.slice(0, slash)}/${CANDIDATE_SEGMENT}/${neuronPath.slice(slash + 1)}`;
695
+ }
696
+ function fromCandidatePath(candidatePath) {
697
+ return candidatePath.replace(`/${CANDIDATE_SEGMENT}/`, "/");
698
+ }
699
+ function growCandidate(brainRoot, neuronPath) {
700
+ const candidatePath = toCandidatePath(neuronPath);
701
+ const result = growNeuron(brainRoot, candidatePath);
702
+ if (result.counter >= CANDIDATE_THRESHOLD) {
703
+ const ok = moveCandidate(brainRoot, candidatePath, neuronPath);
704
+ if (ok) propagateToShared(brainRoot, neuronPath);
705
+ return { ...result, path: ok ? neuronPath : result.path, promoted: ok };
706
+ }
707
+ console.log(` \u{1F331} candidate (${result.counter}/${CANDIDATE_THRESHOLD}): ${candidatePath}`);
708
+ return { ...result, promoted: false };
709
+ }
710
+ function moveCandidate(brainRoot, candidatePath, targetPath) {
711
+ const src = join10(brainRoot, candidatePath);
712
+ if (!existsSync9(src)) return false;
713
+ const dst = join10(brainRoot, targetPath);
714
+ if (existsSync9(dst)) {
715
+ fireNeuron(brainRoot, targetPath);
716
+ rmSync(src, { recursive: true, force: true });
717
+ } else {
718
+ mkdirSync4(dirname(dst), { recursive: true });
719
+ renameSync3(src, dst);
720
+ }
721
+ console.log(`\u{1F393} promoted: ${candidatePath} \u2192 ${targetPath}`);
722
+ return true;
723
+ }
724
+ function promoteCandidates(brainRoot) {
725
+ const promoted = [];
726
+ const decayed = [];
727
+ const decayMs = CANDIDATE_DECAY_DAYS * 24 * 60 * 60 * 1e3;
728
+ const now = Date.now();
729
+ for (const region of REGIONS) {
730
+ const candidateRoot = join10(brainRoot, region, CANDIDATE_SEGMENT);
731
+ walkNeuronDirs(candidateRoot, (neuronDir) => {
732
+ const rel = relative3(join10(brainRoot, region), neuronDir);
733
+ const candidatePath = `${region}/${rel}`;
734
+ const targetPath = fromCandidatePath(candidatePath);
735
+ const counter = readCounter(neuronDir);
736
+ const mtime = statSync3(neuronDir).mtimeMs;
737
+ if (counter >= CANDIDATE_THRESHOLD) {
738
+ moveCandidate(brainRoot, candidatePath, targetPath);
739
+ propagateToShared(brainRoot, targetPath);
740
+ promoted.push(targetPath);
741
+ } else if (now - mtime > decayMs) {
742
+ rmSync(neuronDir, { recursive: true, force: true });
743
+ decayed.push(candidatePath);
744
+ console.log(`\u{1F480} candidate decayed: ${candidatePath}`);
745
+ }
746
+ });
747
+ }
748
+ return { promoted, decayed };
749
+ }
750
+ function listCandidates(brainRoot) {
751
+ const results = [];
752
+ const now = Date.now();
753
+ for (const region of REGIONS) {
754
+ const candidateRoot = join10(brainRoot, region, CANDIDATE_SEGMENT);
755
+ walkNeuronDirs(candidateRoot, (neuronDir) => {
756
+ const rel = relative3(join10(brainRoot, region), neuronDir);
757
+ const candidatePath = `${region}/${rel}`;
758
+ const targetPath = fromCandidatePath(candidatePath);
759
+ const counter = readCounter(neuronDir);
760
+ const mtime = statSync3(neuronDir).mtimeMs;
761
+ const daysInactive = Math.floor((now - mtime) / (24 * 60 * 60 * 1e3));
762
+ results.push({ candidatePath, targetPath, counter, daysInactive });
763
+ });
764
+ }
765
+ return results;
766
+ }
767
+ function walkNeuronDirs(dir, cb) {
768
+ if (!existsSync9(dir)) return;
769
+ try {
770
+ const entries = readdirSync7(dir, { withFileTypes: true });
771
+ const hasNeuron = entries.some((e) => e.isFile() && e.name.endsWith(".neuron"));
772
+ if (hasNeuron) {
773
+ cb(dir);
774
+ return;
775
+ }
776
+ for (const entry of entries) {
777
+ if (entry.isDirectory() && !entry.name.startsWith(".")) {
778
+ walkNeuronDirs(join10(dir, entry.name), cb);
779
+ }
780
+ }
781
+ } catch {
782
+ }
783
+ }
784
+ function readCounter(dir) {
785
+ try {
786
+ const files = readdirSync7(dir).filter((f) => /^\d+\.neuron$/.test(f));
787
+ if (files.length === 0) return 0;
788
+ return Math.max(...files.map((f) => parseInt(f, 10)));
789
+ } catch {
790
+ return 0;
791
+ }
792
+ }
793
+ function propagateToShared(brainRoot, targetPath) {
794
+ try {
795
+ const agentsIdx = brainRoot.indexOf("/agents/");
796
+ if (agentsIdx === -1) return false;
797
+ const multiBrainRoot = brainRoot.slice(0, agentsIdx);
798
+ const sharedRoot = join10(multiBrainRoot, "shared");
799
+ if (!existsSync9(sharedRoot)) return false;
800
+ const episodes = readEpisodes(brainRoot);
801
+ const neuronName = targetPath.split("/").pop() || "";
802
+ const hasRelevantEpisode = episodes.some(
803
+ (ep) => PROPAGATION_EPISODE_TYPES.includes(ep.type) && (ep.path.includes(neuronName) || ep.detail.includes(neuronName))
804
+ );
805
+ if (!hasRelevantEpisode) return false;
806
+ growNeuron(sharedRoot, targetPath);
807
+ console.log(` \u{1F4E1} propagated to shared: ${targetPath}`);
808
+ return true;
809
+ } catch {
810
+ return false;
811
+ }
812
+ }
813
+
814
+ // src/emit.ts
815
+ function emitBootstrap(result, brain, brainRoot) {
620
816
  const lines = [];
621
817
  const now = (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d+Z$/, "");
622
818
  lines.push(MARKER_START);
@@ -672,6 +868,19 @@ function emitBootstrap(result, brain) {
672
868
  lines.push(`| ${icon} ${region.name} | ${active.length} | ${activation} |`);
673
869
  }
674
870
  lines.push("");
871
+ if (brainRoot) {
872
+ const candidates = listCandidates(brainRoot);
873
+ if (candidates.length > 0) {
874
+ const top = candidates.slice(0, 5);
875
+ lines.push("### Provisional Rules (evaluating)");
876
+ lines.push("Follow these during this session. They graduate after 3 sessions.");
877
+ for (const c of top) {
878
+ const bar = "\u2588".repeat(c.counter) + "\u2591".repeat(Math.max(0, 3 - c.counter));
879
+ lines.push(`- ${bar} ${pathToSentence(c.targetPath)}`);
880
+ }
881
+ lines.push("");
882
+ }
883
+ }
675
884
  lines.push(MARKER_END);
676
885
  return lines.join("\n");
677
886
  }
@@ -766,7 +975,7 @@ function emitRegionRules(region) {
766
975
  function emitToTarget(brainRoot, target) {
767
976
  const brain = scanBrain(brainRoot);
768
977
  const result = runSubsumption(brain);
769
- const content = emitBootstrap(result, brain);
978
+ const content = emitBootstrap(result, brain, brainRoot);
770
979
  if (target === "all") {
771
980
  for (const [name, filePath] of Object.entries(EMIT_TARGETS)) {
772
981
  writeTarget(filePath, content);
@@ -782,33 +991,33 @@ function emitToTarget(brainRoot, target) {
782
991
  }
783
992
  function writeAllTiers(brainRoot, result, brain) {
784
993
  const indexContent = emitIndex(result, brain);
785
- writeFileSync6(join9(brainRoot, "_index.md"), indexContent, "utf8");
994
+ writeFileSync7(join11(brainRoot, "_index.md"), indexContent, "utf8");
786
995
  for (const region of result.activeRegions) {
787
- if (existsSync8(region.path)) {
996
+ if (existsSync10(region.path)) {
788
997
  const rulesContent = emitRegionRules(region);
789
- writeFileSync6(join9(region.path, "_rules.md"), rulesContent, "utf8");
998
+ writeFileSync7(join11(region.path, "_rules.md"), rulesContent, "utf8");
790
999
  }
791
1000
  }
792
1001
  }
793
1002
  function writeTarget(filePath, content) {
794
- const dir = dirname(filePath);
795
- if (dir !== "." && !existsSync8(dir)) {
796
- mkdirSync3(dir, { recursive: true });
1003
+ const dir = dirname2(filePath);
1004
+ if (dir !== "." && !existsSync10(dir)) {
1005
+ mkdirSync5(dir, { recursive: true });
797
1006
  }
798
- if (existsSync8(filePath)) {
799
- const existing = readFileSync2(filePath, "utf8");
1007
+ if (existsSync10(filePath)) {
1008
+ const existing = readFileSync3(filePath, "utf8");
800
1009
  const startIdx = existing.indexOf(MARKER_START);
801
1010
  const endIdx = existing.indexOf(MARKER_END);
802
1011
  if (startIdx !== -1 && endIdx !== -1) {
803
1012
  const before = existing.slice(0, startIdx);
804
1013
  const after = existing.slice(endIdx + MARKER_END.length);
805
- writeFileSync6(filePath, before + content + after, "utf8");
1014
+ writeFileSync7(filePath, before + content + after, "utf8");
806
1015
  return;
807
1016
  }
808
- writeFileSync6(filePath, content + "\n\n" + existing, "utf8");
1017
+ writeFileSync7(filePath, content + "\n\n" + existing, "utf8");
809
1018
  return;
810
1019
  }
811
- writeFileSync6(filePath, content, "utf8");
1020
+ writeFileSync7(filePath, content, "utf8");
812
1021
  }
813
1022
  function printDiag(brain, result) {
814
1023
  console.log("");
@@ -897,8 +1106,8 @@ function computeHash(result) {
897
1106
  }
898
1107
 
899
1108
  // src/init.ts
900
- import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync7, readFileSync as readFileSync3, existsSync as existsSync9, readdirSync as readdirSync6, appendFileSync } from "fs";
901
- import { join as join10, dirname as dirname2 } from "path";
1109
+ import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync8, readFileSync as readFileSync4, existsSync as existsSync11, readdirSync as readdirSync8, appendFileSync } from "fs";
1110
+ import { join as join12, dirname as dirname3 } from "path";
902
1111
  var REGION_TEMPLATES = {
903
1112
  brainstem: {
904
1113
  description: "Absolute principles. Immutable. Read-only conscience.\nP0 \u2014 highest priority. bomb here halts EVERYTHING.",
@@ -930,22 +1139,22 @@ var REGION_TEMPLATES = {
930
1139
  }
931
1140
  };
932
1141
  function initBrain(brainPath) {
933
- if (existsSync9(brainPath)) {
934
- const entries = readdirSync6(brainPath);
1142
+ if (existsSync11(brainPath)) {
1143
+ const entries = readdirSync8(brainPath);
935
1144
  if (entries.some((e) => REGIONS.includes(e))) {
936
1145
  console.log(`\u26A0\uFE0F Brain already exists at ${brainPath}`);
937
1146
  return;
938
1147
  }
939
1148
  }
940
- mkdirSync4(brainPath, { recursive: true });
1149
+ mkdirSync6(brainPath, { recursive: true });
941
1150
  for (const regionName of REGIONS) {
942
- const regionDir = join10(brainPath, regionName);
943
- mkdirSync4(regionDir, { recursive: true });
1151
+ const regionDir = join12(brainPath, regionName);
1152
+ mkdirSync6(regionDir, { recursive: true });
944
1153
  const template = REGION_TEMPLATES[regionName];
945
1154
  const icon = REGION_ICONS[regionName];
946
1155
  const ko = REGION_KO[regionName];
947
- writeFileSync7(
948
- join10(regionDir, "_rules.md"),
1156
+ writeFileSync8(
1157
+ join12(regionDir, "_rules.md"),
949
1158
  `# ${icon} ${regionName} (${ko})
950
1159
 
951
1160
  ${template.description}
@@ -953,15 +1162,15 @@ ${template.description}
953
1162
  "utf8"
954
1163
  );
955
1164
  for (const starter of template.starters) {
956
- const neuronDir = join10(regionDir, starter);
957
- mkdirSync4(neuronDir, { recursive: true });
958
- writeFileSync7(join10(neuronDir, "1.neuron"), "", "utf8");
1165
+ const neuronDir = join12(regionDir, starter);
1166
+ mkdirSync6(neuronDir, { recursive: true });
1167
+ writeFileSync8(join12(neuronDir, "1.neuron"), "", "utf8");
959
1168
  }
960
1169
  }
961
- mkdirSync4(join10(brainPath, "_agents", "global_inbox"), { recursive: true });
962
- mkdirSync4(join10(brainPath, "skills"), { recursive: true });
963
- writeFileSync7(
964
- join10(brainPath, "skills", "_rules.md"),
1170
+ mkdirSync6(join12(brainPath, "_agents", "global_inbox"), { recursive: true });
1171
+ mkdirSync6(join12(brainPath, "skills"), { recursive: true });
1172
+ writeFileSync8(
1173
+ join12(brainPath, "skills", "_rules.md"),
965
1174
  "# Skills Library\n\nExecutable patterns learned through experience.\nNot part of the subsumption cascade \u2014 retrieval only.\n",
966
1175
  "utf8"
967
1176
  );
@@ -974,13 +1183,13 @@ ${template.description}
974
1183
  console.log(` hebbian emit claude --brain ${brainPath}`);
975
1184
  }
976
1185
  function autoGitignore(brainPath) {
977
- let dir = dirname2(brainPath);
1186
+ let dir = dirname3(brainPath);
978
1187
  for (let i = 0; i < 10; i++) {
979
- if (existsSync9(join10(dir, ".git"))) {
980
- const gitignorePath = join10(dir, ".gitignore");
1188
+ if (existsSync11(join12(dir, ".git"))) {
1189
+ const gitignorePath = join12(dir, ".gitignore");
981
1190
  const brainDirName = brainPath.replace(dir + "/", "") + "/";
982
- if (existsSync9(gitignorePath)) {
983
- const content = readFileSync3(gitignorePath, "utf8");
1191
+ if (existsSync11(gitignorePath)) {
1192
+ const content = readFileSync4(gitignorePath, "utf8");
984
1193
  if (content.includes(brainDirName) || content.includes(brainDirName.replace(/\/$/, ""))) {
985
1194
  return;
986
1195
  }
@@ -992,7 +1201,7 @@ ${brainDirName}
992
1201
  console.log(` \u{1F4DD} Added ${brainDirName} to .gitignore`);
993
1202
  return;
994
1203
  }
995
- const parent = dirname2(dir);
1204
+ const parent = dirname3(dir);
996
1205
  if (parent === dir) break;
997
1206
  dir = parent;
998
1207
  }
@@ -1004,202 +1213,6 @@ import { createServer } from "http";
1004
1213
  // src/inbox.ts
1005
1214
  import { readFileSync as readFileSync5, writeFileSync as writeFileSync9, existsSync as existsSync12, mkdirSync as mkdirSync7 } from "fs";
1006
1215
  import { join as join13 } from "path";
1007
-
1008
- // src/candidates.ts
1009
- import { existsSync as existsSync11, mkdirSync as mkdirSync6, readdirSync as readdirSync8, renameSync as renameSync3, rmSync, statSync as statSync3 } from "fs";
1010
- import { join as join12, dirname as dirname3, relative as relative3 } from "path";
1011
-
1012
- // src/episode.ts
1013
- import { readdirSync as readdirSync7, readFileSync as readFileSync4, writeFileSync as writeFileSync8, mkdirSync as mkdirSync5, existsSync as existsSync10 } from "fs";
1014
- import { join as join11 } from "path";
1015
- var MAX_EPISODES = 100;
1016
- var SESSION_LOG_DIR = "hippocampus/session_log";
1017
- function logEpisode(brainRoot, type, path, detail, extra) {
1018
- const logDir = join11(brainRoot, SESSION_LOG_DIR);
1019
- if (!existsSync10(logDir)) {
1020
- mkdirSync5(logDir, { recursive: true });
1021
- }
1022
- const nextSlot = getNextSlot(logDir);
1023
- const episode = {
1024
- ts: (/* @__PURE__ */ new Date()).toISOString(),
1025
- type,
1026
- path,
1027
- detail,
1028
- ...extra?.outcome ? { outcome: extra.outcome } : {},
1029
- ...extra?.neurons ? { neurons: extra.neurons } : {}
1030
- };
1031
- writeFileSync8(
1032
- join11(logDir, `memory${nextSlot}.neuron`),
1033
- JSON.stringify(episode),
1034
- "utf8"
1035
- );
1036
- }
1037
- function readEpisodes(brainRoot) {
1038
- const logDir = join11(brainRoot, SESSION_LOG_DIR);
1039
- if (!existsSync10(logDir)) return [];
1040
- const episodes = [];
1041
- let entries;
1042
- try {
1043
- entries = readdirSync7(logDir);
1044
- } catch {
1045
- return [];
1046
- }
1047
- for (const entry of entries) {
1048
- if (!entry.startsWith("memory") || !entry.endsWith(".neuron")) continue;
1049
- try {
1050
- const content = readFileSync4(join11(logDir, entry), "utf8");
1051
- if (content.trim()) {
1052
- episodes.push(JSON.parse(content));
1053
- }
1054
- } catch {
1055
- }
1056
- }
1057
- episodes.sort((a, b) => a.ts.localeCompare(b.ts));
1058
- return episodes;
1059
- }
1060
- function getNextSlot(logDir) {
1061
- let maxSlot = 0;
1062
- try {
1063
- for (const entry of readdirSync7(logDir)) {
1064
- if (entry.startsWith("memory") && entry.endsWith(".neuron")) {
1065
- const n = parseInt(entry.replace("memory", "").replace(".neuron", ""), 10);
1066
- if (!isNaN(n) && n > maxSlot) maxSlot = n;
1067
- }
1068
- }
1069
- } catch {
1070
- }
1071
- const next = maxSlot + 1;
1072
- return next > MAX_EPISODES ? maxSlot % MAX_EPISODES + 1 : next;
1073
- }
1074
-
1075
- // src/candidates.ts
1076
- var CANDIDATE_THRESHOLD = 3;
1077
- var CANDIDATE_DECAY_DAYS = 14;
1078
- var CANDIDATE_SEGMENT = "_candidates";
1079
- function toCandidatePath(neuronPath) {
1080
- const slash = neuronPath.indexOf("/");
1081
- if (slash === -1) throw new Error(`Invalid neuron path (missing region): ${neuronPath}`);
1082
- return `${neuronPath.slice(0, slash)}/${CANDIDATE_SEGMENT}/${neuronPath.slice(slash + 1)}`;
1083
- }
1084
- function fromCandidatePath(candidatePath) {
1085
- return candidatePath.replace(`/${CANDIDATE_SEGMENT}/`, "/");
1086
- }
1087
- function growCandidate(brainRoot, neuronPath) {
1088
- const candidatePath = toCandidatePath(neuronPath);
1089
- const result = growNeuron(brainRoot, candidatePath);
1090
- if (result.counter >= CANDIDATE_THRESHOLD) {
1091
- const ok = moveCandidate(brainRoot, candidatePath, neuronPath);
1092
- if (ok) propagateToShared(brainRoot, neuronPath);
1093
- return { ...result, path: ok ? neuronPath : result.path, promoted: ok };
1094
- }
1095
- console.log(` \u{1F331} candidate (${result.counter}/${CANDIDATE_THRESHOLD}): ${candidatePath}`);
1096
- return { ...result, promoted: false };
1097
- }
1098
- function moveCandidate(brainRoot, candidatePath, targetPath) {
1099
- const src = join12(brainRoot, candidatePath);
1100
- if (!existsSync11(src)) return false;
1101
- const dst = join12(brainRoot, targetPath);
1102
- if (existsSync11(dst)) {
1103
- fireNeuron(brainRoot, targetPath);
1104
- rmSync(src, { recursive: true, force: true });
1105
- } else {
1106
- mkdirSync6(dirname3(dst), { recursive: true });
1107
- renameSync3(src, dst);
1108
- }
1109
- console.log(`\u{1F393} promoted: ${candidatePath} \u2192 ${targetPath}`);
1110
- return true;
1111
- }
1112
- function promoteCandidates(brainRoot) {
1113
- const promoted = [];
1114
- const decayed = [];
1115
- const decayMs = CANDIDATE_DECAY_DAYS * 24 * 60 * 60 * 1e3;
1116
- const now = Date.now();
1117
- for (const region of REGIONS) {
1118
- const candidateRoot = join12(brainRoot, region, CANDIDATE_SEGMENT);
1119
- walkNeuronDirs(candidateRoot, (neuronDir) => {
1120
- const rel = relative3(join12(brainRoot, region), neuronDir);
1121
- const candidatePath = `${region}/${rel}`;
1122
- const targetPath = fromCandidatePath(candidatePath);
1123
- const counter = readCounter(neuronDir);
1124
- const mtime = statSync3(neuronDir).mtimeMs;
1125
- if (counter >= CANDIDATE_THRESHOLD) {
1126
- moveCandidate(brainRoot, candidatePath, targetPath);
1127
- propagateToShared(brainRoot, targetPath);
1128
- promoted.push(targetPath);
1129
- } else if (now - mtime > decayMs) {
1130
- rmSync(neuronDir, { recursive: true, force: true });
1131
- decayed.push(candidatePath);
1132
- console.log(`\u{1F480} candidate decayed: ${candidatePath}`);
1133
- }
1134
- });
1135
- }
1136
- return { promoted, decayed };
1137
- }
1138
- function listCandidates(brainRoot) {
1139
- const results = [];
1140
- const now = Date.now();
1141
- for (const region of REGIONS) {
1142
- const candidateRoot = join12(brainRoot, region, CANDIDATE_SEGMENT);
1143
- walkNeuronDirs(candidateRoot, (neuronDir) => {
1144
- const rel = relative3(join12(brainRoot, region), neuronDir);
1145
- const candidatePath = `${region}/${rel}`;
1146
- const targetPath = fromCandidatePath(candidatePath);
1147
- const counter = readCounter(neuronDir);
1148
- const mtime = statSync3(neuronDir).mtimeMs;
1149
- const daysInactive = Math.floor((now - mtime) / (24 * 60 * 60 * 1e3));
1150
- results.push({ candidatePath, targetPath, counter, daysInactive });
1151
- });
1152
- }
1153
- return results;
1154
- }
1155
- function walkNeuronDirs(dir, cb) {
1156
- if (!existsSync11(dir)) return;
1157
- try {
1158
- const entries = readdirSync8(dir, { withFileTypes: true });
1159
- const hasNeuron = entries.some((e) => e.isFile() && e.name.endsWith(".neuron"));
1160
- if (hasNeuron) {
1161
- cb(dir);
1162
- return;
1163
- }
1164
- for (const entry of entries) {
1165
- if (entry.isDirectory() && !entry.name.startsWith(".")) {
1166
- walkNeuronDirs(join12(dir, entry.name), cb);
1167
- }
1168
- }
1169
- } catch {
1170
- }
1171
- }
1172
- function readCounter(dir) {
1173
- try {
1174
- const files = readdirSync8(dir).filter((f) => /^\d+\.neuron$/.test(f));
1175
- if (files.length === 0) return 0;
1176
- return Math.max(...files.map((f) => parseInt(f, 10)));
1177
- } catch {
1178
- return 0;
1179
- }
1180
- }
1181
- function propagateToShared(brainRoot, targetPath) {
1182
- try {
1183
- const agentsIdx = brainRoot.indexOf("/agents/");
1184
- if (agentsIdx === -1) return false;
1185
- const multiBrainRoot = brainRoot.slice(0, agentsIdx);
1186
- const sharedRoot = join12(multiBrainRoot, "shared");
1187
- if (!existsSync11(sharedRoot)) return false;
1188
- const episodes = readEpisodes(brainRoot);
1189
- const neuronName = targetPath.split("/").pop() || "";
1190
- const hasRelevantEpisode = episodes.some(
1191
- (ep) => PROPAGATION_EPISODE_TYPES.includes(ep.type) && (ep.path.includes(neuronName) || ep.detail.includes(neuronName))
1192
- );
1193
- if (!hasRelevantEpisode) return false;
1194
- growNeuron(sharedRoot, targetPath);
1195
- console.log(` \u{1F4E1} propagated to shared: ${targetPath}`);
1196
- return true;
1197
- } catch {
1198
- return false;
1199
- }
1200
- }
1201
-
1202
- // src/inbox.ts
1203
1216
  var INBOX_DIR = "_inbox";
1204
1217
  var CORRECTIONS_FILE = "corrections.jsonl";
1205
1218
  var DOPAMINE_ALLOWED_ROLES = ["pm", "admin", "lead"];
@@ -1838,7 +1851,8 @@ function digestTranscript(brainRoot, transcriptPath, sessionId) {
1838
1851
  console.log(`\u{1F527} digest: ${toolFailures.length} tool failure(s), ${retries.length} retry pattern(s) logged`);
1839
1852
  }
1840
1853
  const corrections = extractCorrections(messages);
1841
- if (corrections.length === 0 && toolFailures.length === 0) {
1854
+ const fired = autoFireCandidates(brainRoot, corrections);
1855
+ if (corrections.length === 0 && toolFailures.length === 0 && fired === 0) {
1842
1856
  console.log(`\u{1F4DD} digest: no corrections found in session ${resolvedSessionId}`);
1843
1857
  writeAuditLog(brainRoot, resolvedSessionId, [], totalLines);
1844
1858
  return { corrections: 0, skipped: messages.length, toolFailures: toolFailures.length, transcriptPath, sessionId: resolvedSessionId };
@@ -2037,6 +2051,26 @@ function isNarrativeKorean(text) {
2037
2051
  const markerCount = NARRATIVE_MARKERS.filter((p) => p.test(text)).length;
2038
2052
  return markerCount >= 2;
2039
2053
  }
2054
+ function autoFireCandidates(brainRoot, corrections) {
2055
+ if (corrections.length > 0) return 0;
2056
+ const candidates = listCandidates(brainRoot);
2057
+ if (candidates.length === 0) return 0;
2058
+ let fired = 0;
2059
+ for (const cand of candidates) {
2060
+ try {
2061
+ const newCounter = fireNeuron(brainRoot, cand.candidatePath);
2062
+ fired++;
2063
+ if (newCounter >= CANDIDATE_THRESHOLD) {
2064
+ growCandidate(brainRoot, cand.targetPath);
2065
+ }
2066
+ } catch {
2067
+ }
2068
+ }
2069
+ if (fired > 0) {
2070
+ console.log(`\u{1F504} agent-evaluator: ${fired} candidate(s) advanced (session without corrections)`);
2071
+ }
2072
+ return fired;
2073
+ }
2040
2074
  function detectCorrection(text) {
2041
2075
  const isNegation = NEGATION_PATTERNS.some((p) => p.test(text));
2042
2076
  const isMust = MUST_PATTERNS.some((p) => p.test(text));