@danielsimonjr/memoryjs 2.2.0 → 2.3.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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # MemoryJS
2
2
 
3
- [![Version](https://img.shields.io/badge/version-2.2.0-blue.svg)](https://github.com/danielsimonjr/memoryjs)
3
+ [![Version](https://img.shields.io/badge/version-2.3.0-blue.svg)](https://github.com/danielsimonjr/memoryjs)
4
4
  [![NPM](https://img.shields.io/npm/v/@danielsimonjr/memoryjs.svg)](https://www.npmjs.com/package/@danielsimonjr/memoryjs)
5
5
  [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
6
6
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue.svg)](https://www.typescriptlang.org/)
package/dist/cli/index.js CHANGED
@@ -6730,7 +6730,30 @@ var init_GraphStorage = __esm({
6730
6730
  // ArtifactEntity extension (types/artifact.ts)
6731
6731
  "artifactType",
6732
6732
  "toolName",
6733
- "shortId"
6733
+ "shortId",
6734
+ // v2.1.0 subclass-manager record fields (sibling to the v2.1.1
6735
+ // UpdateEntitySchema.passthrough fix — same root cause: subclass managers
6736
+ // attach domain records that the persistence allowlist must also admit,
6737
+ // otherwise the records are silently dropped on save and the managers'
6738
+ // list/match/get operations return empty on next load).
6739
+ "heuristicRecord",
6740
+ // HeuristicEntity
6741
+ "decisionRecord",
6742
+ // DecisionEntity (includes nested lifecycle)
6743
+ "exclusionRule",
6744
+ // ExclusionEntity
6745
+ "projectContextRecord",
6746
+ // ProjectContextEntity
6747
+ "toolAffordanceRecord",
6748
+ // ToolAffordanceEntity
6749
+ "prospectiveRecord",
6750
+ // ProspectiveEntity
6751
+ "failureRecord",
6752
+ // FailureEntity
6753
+ "planRecord",
6754
+ // PlanEntity
6755
+ "reflectionRecord"
6756
+ // ReflectionEntity
6734
6757
  ];
6735
6758
  GraphStorage = class {
6736
6759
  /**
@@ -37767,6 +37790,341 @@ function registerDiagCommand(program2) {
37767
37790
 
37768
37791
  // src/cli/commands/index.ts
37769
37792
  init_inspect();
37793
+
37794
+ // src/cli/commands/heuristic.ts
37795
+ init_esm_shims();
37796
+ init_helpers();
37797
+ init_formatters2();
37798
+ function emitJson(payload) {
37799
+ console.log(JSON.stringify(payload, null, 2));
37800
+ }
37801
+ function registerHeuristicCommands(program2) {
37802
+ const h = program2.command("heuristic").description("Manage condition\u2192action heuristic guidelines (add, match, reinforce, conflicts)");
37803
+ h.command("add <condition> <action>").description("Add a heuristic guideline. Returns the assigned id.").option("-p, --priority <n>", "Priority (numeric; higher wins ties)", parseFloat).option("-c, --confidence <n>", "Initial confidence 0\u20131", parseFloat).option("-i, --importance <n>", "Importance 0\u201310", parseFloat).option("-a, --agent-id <id>", "Owning agent ID").action(async (condition, action, opts) => {
37804
+ const options = getOptions(program2);
37805
+ const logger2 = createLogger(options);
37806
+ const ctx = createContext(options);
37807
+ try {
37808
+ const id = await ctx.heuristicManager.add({
37809
+ condition,
37810
+ action,
37811
+ priority: opts.priority,
37812
+ initialConfidence: opts.confidence,
37813
+ importance: opts.importance,
37814
+ agentId: opts.agentId
37815
+ });
37816
+ emitJson({ id });
37817
+ } catch (error) {
37818
+ logger2.error(formatError(error.message));
37819
+ process.exit(1);
37820
+ }
37821
+ });
37822
+ h.command("list").description("List all heuristics").action(async () => {
37823
+ const options = getOptions(program2);
37824
+ const logger2 = createLogger(options);
37825
+ const ctx = createContext(options);
37826
+ try {
37827
+ const heuristics = await ctx.heuristicManager.list();
37828
+ emitJson({ heuristics, count: heuristics.length });
37829
+ } catch (error) {
37830
+ logger2.error(formatError(error.message));
37831
+ process.exit(1);
37832
+ }
37833
+ });
37834
+ h.command("count").description("Print the current heuristic count").action(async () => {
37835
+ const options = getOptions(program2);
37836
+ const logger2 = createLogger(options);
37837
+ const ctx = createContext(options);
37838
+ try {
37839
+ const count = await ctx.heuristicManager.size();
37840
+ emitJson({ count });
37841
+ } catch (error) {
37842
+ logger2.error(formatError(error.message));
37843
+ process.exit(1);
37844
+ }
37845
+ });
37846
+ h.command("get <id>").description("Get a heuristic by id").action(async (id) => {
37847
+ const options = getOptions(program2);
37848
+ const logger2 = createLogger(options);
37849
+ const ctx = createContext(options);
37850
+ try {
37851
+ const heuristic = ctx.heuristicManager.get(id);
37852
+ emitJson({ id, heuristic: heuristic ?? null });
37853
+ } catch (error) {
37854
+ logger2.error(formatError(error.message));
37855
+ process.exit(1);
37856
+ }
37857
+ });
37858
+ h.command("match <input...>").description("Match heuristics whose condition fits the given input").option("-l, --limit <n>", "Max matches to return", (v) => parseInt(v, 10)).option("-s, --min-score <n>", "Minimum match score 0\u20131", parseFloat).action(async (inputParts, opts) => {
37859
+ const options = getOptions(program2);
37860
+ const logger2 = createLogger(options);
37861
+ const ctx = createContext(options);
37862
+ const input = inputParts.join(" ");
37863
+ try {
37864
+ const matches2 = await ctx.heuristicManager.match(input, {
37865
+ limit: opts.limit,
37866
+ minScore: opts.minScore
37867
+ });
37868
+ emitJson({ input, matches: matches2, count: matches2.length });
37869
+ } catch (error) {
37870
+ logger2.error(formatError(error.message));
37871
+ process.exit(1);
37872
+ }
37873
+ });
37874
+ h.command("reinforce <id>").description("Reinforce a heuristic (bump confidence toward 1)").action(async (id) => {
37875
+ const options = getOptions(program2);
37876
+ const logger2 = createLogger(options);
37877
+ const ctx = createContext(options);
37878
+ try {
37879
+ const result = await ctx.heuristicManager.reinforce(id);
37880
+ emitJson({ id, result });
37881
+ } catch (error) {
37882
+ logger2.error(formatError(error.message));
37883
+ process.exit(1);
37884
+ }
37885
+ });
37886
+ h.command("contradict <id>").description("Record a contradiction (decreases confidence)").action(async (id) => {
37887
+ const options = getOptions(program2);
37888
+ const logger2 = createLogger(options);
37889
+ const ctx = createContext(options);
37890
+ try {
37891
+ const result = await ctx.heuristicManager.recordContradiction(id);
37892
+ emitJson({ id, result });
37893
+ } catch (error) {
37894
+ logger2.error(formatError(error.message));
37895
+ process.exit(1);
37896
+ }
37897
+ });
37898
+ h.command("conflicts").description("Detect heuristics with conflicting actions for similar conditions").action(async () => {
37899
+ const options = getOptions(program2);
37900
+ const logger2 = createLogger(options);
37901
+ const ctx = createContext(options);
37902
+ try {
37903
+ const conflicts = await ctx.heuristicManager.detectConflicts();
37904
+ emitJson({ conflicts, count: conflicts.length });
37905
+ } catch (error) {
37906
+ logger2.error(formatError(error.message));
37907
+ process.exit(1);
37908
+ }
37909
+ });
37910
+ h.command("remove <id>").description("Remove a heuristic by id").action(async (id) => {
37911
+ const options = getOptions(program2);
37912
+ const logger2 = createLogger(options);
37913
+ const ctx = createContext(options);
37914
+ try {
37915
+ const removed = await ctx.heuristicManager.remove(id);
37916
+ emitJson({ id, removed });
37917
+ } catch (error) {
37918
+ logger2.error(formatError(error.message));
37919
+ process.exit(1);
37920
+ }
37921
+ });
37922
+ h.command("clear").description("Remove all heuristics. Destructive; no --dry-run yet.").action(async () => {
37923
+ const options = getOptions(program2);
37924
+ const logger2 = createLogger(options);
37925
+ const ctx = createContext(options);
37926
+ try {
37927
+ await ctx.heuristicManager.clear();
37928
+ emitJson({ cleared: true });
37929
+ } catch (error) {
37930
+ logger2.error(formatError(error.message));
37931
+ process.exit(1);
37932
+ }
37933
+ });
37934
+ }
37935
+
37936
+ // src/cli/commands/observationDedup.ts
37937
+ init_esm_shims();
37938
+ init_helpers();
37939
+ init_formatters2();
37940
+ function buildFilter(opts) {
37941
+ const filter = {};
37942
+ const entityType = opts.entityType;
37943
+ if (entityType) {
37944
+ filter.entityType = entityType.includes(",") ? entityType.split(",").map((s) => s.trim()).filter(Boolean) : entityType;
37945
+ }
37946
+ if (typeof opts.projectId === "string") filter.projectId = opts.projectId;
37947
+ if (typeof opts.sessionId === "string") filter.sessionId = opts.sessionId;
37948
+ if (typeof opts.minOccurrences === "number" && opts.minOccurrences >= 2) {
37949
+ filter.minOccurrences = opts.minOccurrences;
37950
+ }
37951
+ if (typeof opts.maxGroups === "number" && opts.maxGroups > 0) {
37952
+ filter.maxGroups = opts.maxGroups;
37953
+ }
37954
+ return filter;
37955
+ }
37956
+ function emitJson2(payload) {
37957
+ console.log(JSON.stringify(payload, null, 2));
37958
+ }
37959
+ function registerObservationDedupCommands(program2) {
37960
+ const od = program2.command("obs-dedup").description("Cross-entity observation duplicate detection (exact and Jaccard similarity)");
37961
+ od.command("find").description("Find observations that appear verbatim across multiple entities").option("--entity-type <type>", "Filter to one entityType (or comma-separated list)").option("--project-id <id>", "Filter to one projectId").option("--session-id <id>", "Filter to one sessionId").option("--min-occurrences <n>", "Minimum occurrences per group (\u22652)", (v) => parseInt(v, 10)).option("--max-groups <n>", "Maximum groups to return", (v) => parseInt(v, 10)).action(async (opts) => {
37962
+ const options = getOptions(program2);
37963
+ const logger2 = createLogger(options);
37964
+ const ctx = createContext(options);
37965
+ try {
37966
+ const filter = buildFilter(opts);
37967
+ const groups = await ctx.observationDedupManager.findDuplicateObservations(filter);
37968
+ emitJson2({ filter, groups, count: groups.length });
37969
+ } catch (error) {
37970
+ logger2.error(formatError(error.message));
37971
+ process.exit(1);
37972
+ }
37973
+ });
37974
+ od.command("find-jaccard").description("Find near-duplicate observations across entities by Jaccard similarity").option("--entity-type <type>", "Filter to one entityType (or comma-separated list)").option("--project-id <id>", "Filter to one projectId").option("--session-id <id>", "Filter to one sessionId").option("--min-occurrences <n>", "Minimum occurrences per group (\u22652)", (v) => parseInt(v, 10)).option("--max-groups <n>", "Maximum groups to return", (v) => parseInt(v, 10)).action(async (opts) => {
37975
+ const options = getOptions(program2);
37976
+ const logger2 = createLogger(options);
37977
+ const ctx = createContext(options);
37978
+ try {
37979
+ const filter = buildFilter(opts);
37980
+ const groups = await ctx.observationDedupManager.findJaccardDuplicates(filter);
37981
+ emitJson2({ filter, groups, count: groups.length });
37982
+ } catch (error) {
37983
+ logger2.error(formatError(error.message));
37984
+ process.exit(1);
37985
+ }
37986
+ });
37987
+ }
37988
+
37989
+ // src/cli/commands/spell.ts
37990
+ init_esm_shims();
37991
+ init_helpers();
37992
+ init_formatters2();
37993
+ function emitJson3(payload) {
37994
+ console.log(JSON.stringify(payload, null, 2));
37995
+ }
37996
+ function registerSpellCommands(program2) {
37997
+ const sp = program2.command("spell").description("Spell-suggest queries against the graph vocabulary");
37998
+ sp.command("suggest <query>").description("Suggest corrections for a (possibly misspelled) query").option("-l, --limit <n>", "Max suggestions to return", (v) => parseInt(v, 10)).option("-s, --min-score <n>", "Minimum match score 0\u20131", parseFloat).option("-d, --max-distance <n>", "Maximum Levenshtein distance", parseFloat).action(async (query, opts) => {
37999
+ const options = getOptions(program2);
38000
+ const logger2 = createLogger(options);
38001
+ const ctx = createContext(options);
38002
+ try {
38003
+ const suggestions = await ctx.spellChecker.suggest(query, {
38004
+ limit: opts.limit,
38005
+ minScore: opts.minScore,
38006
+ maxDistance: opts.maxDistance
38007
+ });
38008
+ emitJson3({ query, suggestions, count: suggestions.length });
38009
+ } catch (error) {
38010
+ logger2.error(formatError(error.message));
38011
+ process.exit(1);
38012
+ }
38013
+ });
38014
+ sp.command("rebuild").description("Rebuild the spell-checker vocabulary index from the graph").action(async () => {
38015
+ const options = getOptions(program2);
38016
+ const logger2 = createLogger(options);
38017
+ const ctx = createContext(options);
38018
+ try {
38019
+ await ctx.spellChecker.rebuild();
38020
+ emitJson3({ rebuilt: true, vocabularySize: ctx.spellChecker.vocabularySize() });
38021
+ } catch (error) {
38022
+ logger2.error(formatError(error.message));
38023
+ process.exit(1);
38024
+ }
38025
+ });
38026
+ sp.command("size").description("Print the current vocabulary size").action(async () => {
38027
+ const options = getOptions(program2);
38028
+ const logger2 = createLogger(options);
38029
+ const ctx = createContext(options);
38030
+ try {
38031
+ emitJson3({ vocabularySize: ctx.spellChecker.vocabularySize() });
38032
+ } catch (error) {
38033
+ logger2.error(formatError(error.message));
38034
+ process.exit(1);
38035
+ }
38036
+ });
38037
+ }
38038
+
38039
+ // src/cli/commands/check.ts
38040
+ init_esm_shims();
38041
+ init_helpers();
38042
+ init_formatters2();
38043
+ async function detectIssues(ctx) {
38044
+ const graph = await ctx.storage.loadGraph();
38045
+ const names = new Set(graph.entities.map((e) => e.name));
38046
+ const orphans = [];
38047
+ for (const r of graph.relations) {
38048
+ const fromMissing = !names.has(r.from);
38049
+ const toMissing = !names.has(r.to);
38050
+ if (fromMissing || toMissing) {
38051
+ orphans.push({
38052
+ from: r.from,
38053
+ to: r.to,
38054
+ relationType: r.relationType,
38055
+ reason: fromMissing && toMissing ? "both-missing" : fromMissing ? "from-missing" : "to-missing"
38056
+ });
38057
+ }
38058
+ }
38059
+ const missing = [];
38060
+ const cycles = [];
38061
+ const byName = /* @__PURE__ */ new Map();
38062
+ for (const e of graph.entities) byName.set(e.name, { name: e.name, parentId: e.parentId });
38063
+ for (const e of graph.entities) {
38064
+ if (!e.parentId) continue;
38065
+ if (!byName.has(e.parentId)) {
38066
+ missing.push({ entity: e.name, parentId: e.parentId });
38067
+ continue;
38068
+ }
38069
+ const visited = /* @__PURE__ */ new Set([e.name]);
38070
+ let cur = byName.get(e.parentId);
38071
+ while (cur && cur.parentId) {
38072
+ if (visited.has(cur.name)) {
38073
+ cycles.push({ entityInCycle: e.name, cycleThrough: cur.name });
38074
+ break;
38075
+ }
38076
+ visited.add(cur.name);
38077
+ cur = byName.get(cur.parentId);
38078
+ }
38079
+ }
38080
+ return { orphans, missing, cycles };
38081
+ }
38082
+ async function applyFixes(ctx, orphans, missing) {
38083
+ let deleted = 0;
38084
+ let cleared = 0;
38085
+ if (orphans.length > 0) {
38086
+ await ctx.relationManager.deleteRelations(
38087
+ orphans.map((o) => ({ from: o.from, to: o.to, relationType: o.relationType }))
38088
+ );
38089
+ deleted = orphans.length;
38090
+ }
38091
+ for (const m of missing) {
38092
+ try {
38093
+ await ctx.hierarchyManager.setEntityParent(m.entity, null);
38094
+ cleared += 1;
38095
+ } catch {
38096
+ }
38097
+ }
38098
+ return { orphanRelationsDeleted: deleted, missingParentsCleared: cleared };
38099
+ }
38100
+ function registerCheckCommand(program2) {
38101
+ program2.command("check").description("Detect orphan relations + missing parents + hierarchy cycles. Dry-run by default.").option("--apply", "Actually delete orphan relations + clear missing parentIds (cycles always left for human review)").action(async (opts) => {
38102
+ const options = getOptions(program2);
38103
+ const logger2 = createLogger(options);
38104
+ const ctx = createContext(options);
38105
+ try {
38106
+ const { orphans, missing, cycles } = await detectIssues(ctx);
38107
+ const ok = orphans.length === 0 && missing.length === 0 && cycles.length === 0;
38108
+ const report = {
38109
+ ok,
38110
+ applied: Boolean(opts.apply),
38111
+ orphanRelations: orphans,
38112
+ missingParents: missing,
38113
+ hierarchyCycles: cycles
38114
+ };
38115
+ if (opts.apply && (orphans.length > 0 || missing.length > 0)) {
38116
+ report.actions = await applyFixes(ctx, orphans, missing);
38117
+ }
38118
+ console.log(JSON.stringify(report, null, 2));
38119
+ if (!ok && !opts.apply) process.exit(1);
38120
+ } catch (error) {
38121
+ logger2.error(formatError(error.message));
38122
+ process.exit(1);
38123
+ }
38124
+ });
38125
+ }
38126
+
38127
+ // src/cli/commands/index.ts
37770
38128
  function registerCommands(program2) {
37771
38129
  registerEntityCommands(program2);
37772
38130
  registerRelationCommands(program2);
@@ -37784,6 +38142,10 @@ function registerCommands(program2) {
37784
38142
  registerSmokeCommand(program2);
37785
38143
  registerDiagCommand(program2);
37786
38144
  registerInspectCommands(program2);
38145
+ registerHeuristicCommands(program2);
38146
+ registerObservationDedupCommands(program2);
38147
+ registerSpellCommands(program2);
38148
+ registerCheckCommand(program2);
37787
38149
  }
37788
38150
 
37789
38151
  // src/cli/index.ts