memorix 1.0.8 → 1.0.9
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 +39 -4
- package/README.md +16 -16
- package/README.zh-CN.md +24 -8
- package/dist/cli/index.js +3424 -1056
- package/dist/cli/index.js.map +1 -1
- package/dist/dashboard/static/app.js +934 -10
- package/dist/dashboard/static/index.html +24 -4
- package/dist/dashboard/static/style.css +663 -0
- package/dist/index.js +635 -17
- package/dist/index.js.map +1 -1
- package/dist/sdk.js +635 -17
- package/dist/sdk.js.map +1 -1
- package/dist/types.d.ts +2 -1
- package/dist/types.js +2 -1
- package/dist/types.js.map +1 -1
- package/docs/SETUP.md +97 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -675,6 +675,13 @@ var init_persistence = __esm({
|
|
|
675
675
|
});
|
|
676
676
|
|
|
677
677
|
// src/store/graph-store.ts
|
|
678
|
+
var graph_store_exports = {};
|
|
679
|
+
__export(graph_store_exports, {
|
|
680
|
+
GraphSqliteStore: () => GraphSqliteStore,
|
|
681
|
+
getGraphStore: () => getGraphStore,
|
|
682
|
+
initGraphStore: () => initGraphStore,
|
|
683
|
+
resetGraphStore: () => resetGraphStore
|
|
684
|
+
});
|
|
678
685
|
import path6 from "path";
|
|
679
686
|
import fs5 from "fs";
|
|
680
687
|
function safeJsonParse(val, fallback) {
|
|
@@ -710,6 +717,16 @@ function getGraphStore() {
|
|
|
710
717
|
if (!_graphStore) throw new Error("[memorix] GraphStore not initialized \u2014 call initGraphStore() first");
|
|
711
718
|
return _graphStore;
|
|
712
719
|
}
|
|
720
|
+
function resetGraphStore() {
|
|
721
|
+
if (_graphStore) {
|
|
722
|
+
try {
|
|
723
|
+
_graphStore.close();
|
|
724
|
+
} catch {
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
_graphStore = null;
|
|
728
|
+
_graphDataDir = null;
|
|
729
|
+
}
|
|
713
730
|
var GraphSqliteStore, _graphStore, _graphDataDir;
|
|
714
731
|
var init_graph_store = __esm({
|
|
715
732
|
"src/store/graph-store.ts"() {
|
|
@@ -1041,7 +1058,8 @@ var init_types = __esm({
|
|
|
1041
1058
|
"why-it-exists": "[WHY]",
|
|
1042
1059
|
"decision": "[DECISION]",
|
|
1043
1060
|
"trade-off": "[TRADEOFF]",
|
|
1044
|
-
"reasoning": "[REASONING]"
|
|
1061
|
+
"reasoning": "[REASONING]",
|
|
1062
|
+
"probe": "[PROBE]"
|
|
1045
1063
|
};
|
|
1046
1064
|
TOPIC_KEY_FAMILIES = {
|
|
1047
1065
|
"architecture": ["architecture", "design", "adr", "structure", "pattern"],
|
|
@@ -1055,6 +1073,15 @@ var init_types = __esm({
|
|
|
1055
1073
|
});
|
|
1056
1074
|
|
|
1057
1075
|
// src/store/mini-skill-store.ts
|
|
1076
|
+
var mini_skill_store_exports = {};
|
|
1077
|
+
__export(mini_skill_store_exports, {
|
|
1078
|
+
MiniSkillGracefulDegrade: () => MiniSkillGracefulDegrade,
|
|
1079
|
+
MiniSkillSqliteStore: () => MiniSkillSqliteStore,
|
|
1080
|
+
getMiniSkillStore: () => getMiniSkillStore,
|
|
1081
|
+
initMiniSkillStore: () => initMiniSkillStore,
|
|
1082
|
+
isMiniSkillStoreInitialized: () => isMiniSkillStoreInitialized,
|
|
1083
|
+
resetMiniSkillStore: () => resetMiniSkillStore
|
|
1084
|
+
});
|
|
1058
1085
|
import path7 from "path";
|
|
1059
1086
|
import fs6 from "fs";
|
|
1060
1087
|
function skillToRow(skill) {
|
|
@@ -1108,6 +1135,10 @@ function getMiniSkillStore() {
|
|
|
1108
1135
|
}
|
|
1109
1136
|
return _store;
|
|
1110
1137
|
}
|
|
1138
|
+
function resetMiniSkillStore() {
|
|
1139
|
+
_store = null;
|
|
1140
|
+
_storeDataDir = null;
|
|
1141
|
+
}
|
|
1111
1142
|
async function initMiniSkillStore(dataDir) {
|
|
1112
1143
|
if (_store && _storeDataDir === dataDir) return _store;
|
|
1113
1144
|
_store = null;
|
|
@@ -1354,6 +1385,12 @@ async function promoteToMiniSkill(projectDir2, projectId, observations2, options
|
|
|
1354
1385
|
`Cannot promote: ${nonActive.length} observation(s) are not active. Blocked: ${nonActive.map((o) => `#${o.id} (${o.status ?? "unknown"})`).join(", ")}. Only active observations can be promoted to permanent knowledge.`
|
|
1355
1386
|
);
|
|
1356
1387
|
}
|
|
1388
|
+
const probes = observations2.filter((o) => o.type === "probe");
|
|
1389
|
+
if (probes.length > 0) {
|
|
1390
|
+
throw new Error(
|
|
1391
|
+
`Cannot promote probe observations \u2014 they are operational heartbeats, not durable knowledge. Blocked: ${probes.map((o) => `#${o.id} "${o.title.substring(0, 60)}"`).join(", ")}.`
|
|
1392
|
+
);
|
|
1393
|
+
}
|
|
1357
1394
|
if (!options?.force) {
|
|
1358
1395
|
const commandLogs = observations2.filter((o) => COMMAND_LOG_TITLE.test(o.title));
|
|
1359
1396
|
if (commandLogs.length > 0) {
|
|
@@ -4407,6 +4444,10 @@ async function searchObservations(options) {
|
|
|
4407
4444
|
if (statusFilter === "all") return true;
|
|
4408
4445
|
const doc = hit.document;
|
|
4409
4446
|
return (doc.status || "active") === statusFilter;
|
|
4447
|
+
}).filter((hit) => {
|
|
4448
|
+
if (options.type === "probe") return true;
|
|
4449
|
+
const doc = hit.document;
|
|
4450
|
+
return doc.type !== "probe";
|
|
4410
4451
|
}).map((hit) => {
|
|
4411
4452
|
const doc = hit.document;
|
|
4412
4453
|
const obsType = doc.type;
|
|
@@ -6788,6 +6829,11 @@ function hasContradiction(oldText, newText) {
|
|
|
6788
6829
|
];
|
|
6789
6830
|
return negationPatterns.some((p) => p.test(newText));
|
|
6790
6831
|
}
|
|
6832
|
+
function normalizedSearchSimilarity(score) {
|
|
6833
|
+
if (!Number.isFinite(score) || score <= 0) return 0;
|
|
6834
|
+
if (score <= 1) return score;
|
|
6835
|
+
return 0;
|
|
6836
|
+
}
|
|
6791
6837
|
function mergeNarratives(oldNarrative, newNarrative) {
|
|
6792
6838
|
if (newNarrative.length > oldNarrative.length * 1.5) return newNarrative;
|
|
6793
6839
|
if (oldNarrative.length > newNarrative.length * 1.5) return oldNarrative;
|
|
@@ -6878,12 +6924,13 @@ function scoreCandidate(extracted, candidate) {
|
|
|
6878
6924
|
`${extracted.title} ${extracted.narrative}`,
|
|
6879
6925
|
`${candidate.title} ${candidate.narrative}`
|
|
6880
6926
|
);
|
|
6881
|
-
const
|
|
6927
|
+
const searchSimilarity = normalizedSearchSimilarity(candidate.score);
|
|
6928
|
+
const score = searchSimilarity * 0.6 + (entityMatch ? 0.2 : 0) + contentOverlap * 0.2;
|
|
6882
6929
|
const newLength = extracted.narrative.length + extracted.facts.join(" ").length;
|
|
6883
6930
|
const oldLength = candidate.narrative.length + candidate.facts.length;
|
|
6884
6931
|
const richer = newLength > oldLength * 1.15;
|
|
6885
6932
|
const contradiction = hasContradiction(candidate.narrative, extracted.narrative);
|
|
6886
|
-
return { score, entityMatch, richer, contradiction };
|
|
6933
|
+
return { score, searchSimilarity, entityMatch, richer, contradiction };
|
|
6887
6934
|
}
|
|
6888
6935
|
async function runResolve(extracted, projectId, searchMemories, getObservation2, useLLM = false) {
|
|
6889
6936
|
const query = `${extracted.title} ${extracted.narrative.substring(0, 200)}`;
|
|
@@ -6906,7 +6953,7 @@ async function runResolve(extracted, projectId, searchMemories, getObservation2,
|
|
|
6906
6953
|
}));
|
|
6907
6954
|
scored.sort((a, b) => b.score - a.score);
|
|
6908
6955
|
const best = scored[0];
|
|
6909
|
-
if (best.
|
|
6956
|
+
if (best.searchSimilarity >= SIMILARITY_DUPLICATE) {
|
|
6910
6957
|
if (best.richer) {
|
|
6911
6958
|
const existing = getObservation2(best.hit.observationId);
|
|
6912
6959
|
const oldFacts = existing?.facts ?? best.hit.facts.split("\n").filter(Boolean);
|
|
@@ -7093,7 +7140,8 @@ var init_evaluate = __esm({
|
|
|
7093
7140
|
"how-it-works": 0.6,
|
|
7094
7141
|
"discovery": 0.55,
|
|
7095
7142
|
"what-changed": 0.45,
|
|
7096
|
-
"session-request": 0.4
|
|
7143
|
+
"session-request": 0.4,
|
|
7144
|
+
"probe": 0.1
|
|
7097
7145
|
};
|
|
7098
7146
|
SPECIFICITY_PATTERNS = [
|
|
7099
7147
|
/\b\d+\.\d+\.\d+\b/,
|
|
@@ -7557,6 +7605,9 @@ function scoreObservationForSessionContext(obs, projectTokens, now = Date.now())
|
|
|
7557
7605
|
if (obs.valueCategory === "core") {
|
|
7558
7606
|
score += 2;
|
|
7559
7607
|
}
|
|
7608
|
+
if (obs.type === "probe") {
|
|
7609
|
+
score -= 100;
|
|
7610
|
+
}
|
|
7560
7611
|
return score;
|
|
7561
7612
|
}
|
|
7562
7613
|
async function startSession(projectDir2, projectId, opts) {
|
|
@@ -7765,7 +7816,8 @@ var init_session = __esm({
|
|
|
7765
7816
|
"how-it-works": "[INFO]",
|
|
7766
7817
|
"what-changed": "[CHANGE]",
|
|
7767
7818
|
"why-it-exists": "[DECISION]",
|
|
7768
|
-
"session-request": "[SESSION]"
|
|
7819
|
+
"session-request": "[SESSION]",
|
|
7820
|
+
"probe": "[PROBE]"
|
|
7769
7821
|
};
|
|
7770
7822
|
TYPE_WEIGHTS2 = {
|
|
7771
7823
|
"gotcha": 6,
|
|
@@ -7844,11 +7896,17 @@ function getValueCategoryMultiplier(doc) {
|
|
|
7844
7896
|
return 1;
|
|
7845
7897
|
}
|
|
7846
7898
|
function getEffectiveRetentionDays(doc) {
|
|
7899
|
+
const typeOverride = TYPE_RETENTION_OVERRIDE[doc.type];
|
|
7900
|
+
if (typeOverride !== void 0) {
|
|
7901
|
+
const raw2 = typeOverride * getSourceRetentionMultiplier(doc);
|
|
7902
|
+
return Math.max(MIN_RETENTION_DAYS, raw2);
|
|
7903
|
+
}
|
|
7847
7904
|
const importance = getImportanceLevel(doc);
|
|
7848
7905
|
const raw = RETENTION_DAYS[importance] * getSourceRetentionMultiplier(doc) * getValueCategoryMultiplier(doc);
|
|
7849
7906
|
return Math.max(MIN_RETENTION_DAYS, raw);
|
|
7850
7907
|
}
|
|
7851
7908
|
function isImmune(doc) {
|
|
7909
|
+
if (doc.type === "probe") return false;
|
|
7852
7910
|
if (doc.valueCategory === "core") return true;
|
|
7853
7911
|
const importance = getImportanceLevel(doc);
|
|
7854
7912
|
if (importance === "critical") return true;
|
|
@@ -7857,6 +7915,7 @@ function isImmune(doc) {
|
|
|
7857
7915
|
return concepts.some((c) => PROTECTED_TAGS.has(c));
|
|
7858
7916
|
}
|
|
7859
7917
|
function getImmunityReason(doc) {
|
|
7918
|
+
if (doc.type === "probe") return null;
|
|
7860
7919
|
if (doc.valueCategory === "core") return "core valueCategory (formation-classified)";
|
|
7861
7920
|
const importance = getImportanceLevel(doc);
|
|
7862
7921
|
if (importance === "critical") return "critical importance";
|
|
@@ -8013,7 +8072,7 @@ async function archiveExpired(projectDir2, referenceTime, accessMap) {
|
|
|
8013
8072
|
return { archived: archivedCount, remaining: activeObs.length - archivedCount };
|
|
8014
8073
|
});
|
|
8015
8074
|
}
|
|
8016
|
-
var RETENTION_DAYS, BASE_IMPORTANCE, TYPE_IMPORTANCE, PROTECTED_TAGS, MIN_ACCESS_FOR_IMMUNITY, MIN_RETENTION_DAYS;
|
|
8075
|
+
var RETENTION_DAYS, BASE_IMPORTANCE, TYPE_IMPORTANCE, TYPE_RETENTION_OVERRIDE, PROTECTED_TAGS, MIN_ACCESS_FOR_IMMUNITY, MIN_RETENTION_DAYS;
|
|
8017
8076
|
var init_retention = __esm({
|
|
8018
8077
|
"src/memory/retention.ts"() {
|
|
8019
8078
|
"use strict";
|
|
@@ -8041,7 +8100,12 @@ var init_retention = __esm({
|
|
|
8041
8100
|
"what-changed": "low",
|
|
8042
8101
|
"why-it-exists": "medium",
|
|
8043
8102
|
discovery: "low",
|
|
8044
|
-
"session-request": "low"
|
|
8103
|
+
"session-request": "low",
|
|
8104
|
+
probe: "low"
|
|
8105
|
+
};
|
|
8106
|
+
TYPE_RETENTION_OVERRIDE = {
|
|
8107
|
+
probe: 7
|
|
8108
|
+
// Operational heartbeats: expire after ~7 days regardless of source/valueCategory
|
|
8045
8109
|
};
|
|
8046
8110
|
PROTECTED_TAGS = /* @__PURE__ */ new Set(["keep", "important", "pinned", "critical"]);
|
|
8047
8111
|
MIN_ACCESS_FOR_IMMUNITY = 3;
|
|
@@ -9769,6 +9833,449 @@ var init_project_classification = __esm({
|
|
|
9769
9833
|
}
|
|
9770
9834
|
});
|
|
9771
9835
|
|
|
9836
|
+
// src/wiki/generator.ts
|
|
9837
|
+
var generator_exports = {};
|
|
9838
|
+
__export(generator_exports, {
|
|
9839
|
+
contextualHasSubstance: () => contextualHasSubstance,
|
|
9840
|
+
generateKnowledgeBase: () => generateKnowledgeBase,
|
|
9841
|
+
isCommandLog: () => isCommandLog,
|
|
9842
|
+
isEligible: () => isEligible,
|
|
9843
|
+
isExcludedType: () => isExcludedType
|
|
9844
|
+
});
|
|
9845
|
+
function isExcludedType(o) {
|
|
9846
|
+
return o.type === "probe";
|
|
9847
|
+
}
|
|
9848
|
+
function isCommandLog(o) {
|
|
9849
|
+
return COMMAND_LOG_TITLE3.test(o.title || "");
|
|
9850
|
+
}
|
|
9851
|
+
function isInactive(o) {
|
|
9852
|
+
const status = o.status ?? "active";
|
|
9853
|
+
return status !== "active";
|
|
9854
|
+
}
|
|
9855
|
+
function isOtherProject(o, projectId) {
|
|
9856
|
+
return o.projectId !== projectId;
|
|
9857
|
+
}
|
|
9858
|
+
function contextualHasSubstance(o) {
|
|
9859
|
+
if (o.valueCategory !== "contextual") return true;
|
|
9860
|
+
const hasFacts = (o.facts?.length ?? 0) > 0;
|
|
9861
|
+
const hasConcepts = (o.concepts?.length ?? 0) > 0;
|
|
9862
|
+
const hasFiles = (o.filesModified?.length ?? 0) > 0;
|
|
9863
|
+
const hasEntity = !!(o.entityName && o.entityName !== "quick-note" && o.entityName !== "unknown");
|
|
9864
|
+
return hasFacts || hasConcepts || hasFiles || hasEntity;
|
|
9865
|
+
}
|
|
9866
|
+
function isEligible(o, projectId) {
|
|
9867
|
+
if (isExcludedType(o)) return false;
|
|
9868
|
+
if (isCommandLog(o)) return false;
|
|
9869
|
+
if (isInactive(o)) return false;
|
|
9870
|
+
if (isOtherProject(o, projectId)) return false;
|
|
9871
|
+
if (o.valueCategory === "ephemeral") return false;
|
|
9872
|
+
if (!contextualHasSubstance(o)) return false;
|
|
9873
|
+
return true;
|
|
9874
|
+
}
|
|
9875
|
+
function obsToItem(o) {
|
|
9876
|
+
const ref = {
|
|
9877
|
+
kind: o.source === "git" ? "git" : "observation",
|
|
9878
|
+
id: `obs:${o.id}`,
|
|
9879
|
+
title: o.title
|
|
9880
|
+
};
|
|
9881
|
+
return {
|
|
9882
|
+
title: o.title,
|
|
9883
|
+
summary: o.narrative?.slice(0, 200) || "",
|
|
9884
|
+
type: o.type,
|
|
9885
|
+
entityName: o.entityName || void 0,
|
|
9886
|
+
refs: [ref]
|
|
9887
|
+
};
|
|
9888
|
+
}
|
|
9889
|
+
function skillToItem(s) {
|
|
9890
|
+
const ref = {
|
|
9891
|
+
kind: "mini-skill",
|
|
9892
|
+
id: `skill:${s.id}`,
|
|
9893
|
+
title: s.title
|
|
9894
|
+
};
|
|
9895
|
+
const obsRefs = s.sourceObservationIds.map(
|
|
9896
|
+
(oid) => ({ kind: "observation", id: `obs:${oid}` })
|
|
9897
|
+
);
|
|
9898
|
+
return {
|
|
9899
|
+
title: s.title,
|
|
9900
|
+
summary: s.instruction?.slice(0, 200) || "",
|
|
9901
|
+
type: "mini-skill",
|
|
9902
|
+
entityName: s.sourceEntity || void 0,
|
|
9903
|
+
refs: [ref, ...obsRefs]
|
|
9904
|
+
};
|
|
9905
|
+
}
|
|
9906
|
+
function buildGitSection(observations2) {
|
|
9907
|
+
const gitObs = observations2.filter(
|
|
9908
|
+
(o) => o.source === "git" && o.sourceDetail === "git-ingest"
|
|
9909
|
+
);
|
|
9910
|
+
const items = gitObs.map(obsToItem);
|
|
9911
|
+
return {
|
|
9912
|
+
id: "git-backed-facts",
|
|
9913
|
+
title: "Git-backed Facts",
|
|
9914
|
+
items,
|
|
9915
|
+
empty: items.length === 0
|
|
9916
|
+
};
|
|
9917
|
+
}
|
|
9918
|
+
function buildSkillsSection(skills) {
|
|
9919
|
+
const items = skills.map(skillToItem);
|
|
9920
|
+
return {
|
|
9921
|
+
id: "promoted-skills",
|
|
9922
|
+
title: "Promoted Skills",
|
|
9923
|
+
items,
|
|
9924
|
+
empty: items.length === 0
|
|
9925
|
+
};
|
|
9926
|
+
}
|
|
9927
|
+
function buildProjectOverview(projectId, eligibleObs, skills) {
|
|
9928
|
+
const refs = [
|
|
9929
|
+
...eligibleObs.slice(0, 5).map((o) => ({
|
|
9930
|
+
kind: o.source === "git" ? "git" : "observation",
|
|
9931
|
+
id: `obs:${o.id}`,
|
|
9932
|
+
title: o.title
|
|
9933
|
+
})),
|
|
9934
|
+
...skills.slice(0, 5).map((s) => ({
|
|
9935
|
+
kind: "mini-skill",
|
|
9936
|
+
id: `skill:${s.id}`,
|
|
9937
|
+
title: s.title
|
|
9938
|
+
}))
|
|
9939
|
+
];
|
|
9940
|
+
if (refs.length === 0) {
|
|
9941
|
+
return {
|
|
9942
|
+
id: "project-overview",
|
|
9943
|
+
title: "Project Overview",
|
|
9944
|
+
items: [],
|
|
9945
|
+
empty: true
|
|
9946
|
+
};
|
|
9947
|
+
}
|
|
9948
|
+
const lines = [];
|
|
9949
|
+
lines.push(`Project: ${projectId}`);
|
|
9950
|
+
lines.push(`Observations in KB: ${eligibleObs.length}`);
|
|
9951
|
+
lines.push(`Promoted skills: ${skills.length}`);
|
|
9952
|
+
const entityCounts = /* @__PURE__ */ new Map();
|
|
9953
|
+
for (const o of eligibleObs) {
|
|
9954
|
+
if (o.entityName) {
|
|
9955
|
+
entityCounts.set(o.entityName, (entityCounts.get(o.entityName) || 0) + 1);
|
|
9956
|
+
}
|
|
9957
|
+
}
|
|
9958
|
+
const topEntities = [...entityCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 5);
|
|
9959
|
+
if (topEntities.length > 0) {
|
|
9960
|
+
lines.push(`Top entities: ${topEntities.map(([name, count2]) => `${name} (${count2})`).join(", ")}`);
|
|
9961
|
+
}
|
|
9962
|
+
return {
|
|
9963
|
+
id: "project-overview",
|
|
9964
|
+
title: "Project Overview",
|
|
9965
|
+
items: [{
|
|
9966
|
+
title: projectId,
|
|
9967
|
+
summary: lines.join("\n"),
|
|
9968
|
+
type: "overview",
|
|
9969
|
+
refs
|
|
9970
|
+
}]
|
|
9971
|
+
};
|
|
9972
|
+
}
|
|
9973
|
+
function generateKnowledgeBase(options) {
|
|
9974
|
+
const { projectId, observations: observations2, miniSkills } = options;
|
|
9975
|
+
const eligible = observations2.filter((o) => isEligible(o, projectId));
|
|
9976
|
+
const scopedMiniSkills = miniSkills.filter((s) => s.projectId === projectId);
|
|
9977
|
+
const typedSections = SECTION_DEFS.map((def) => {
|
|
9978
|
+
const matched = eligible.filter(def.typeMatch);
|
|
9979
|
+
const items = matched.map(obsToItem);
|
|
9980
|
+
return {
|
|
9981
|
+
id: def.id,
|
|
9982
|
+
title: def.title,
|
|
9983
|
+
items,
|
|
9984
|
+
empty: items.length === 0
|
|
9985
|
+
};
|
|
9986
|
+
});
|
|
9987
|
+
const projectOverview = buildProjectOverview(projectId, eligible, scopedMiniSkills);
|
|
9988
|
+
const gitSection = buildGitSection(eligible);
|
|
9989
|
+
const skillsSection = buildSkillsSection(scopedMiniSkills);
|
|
9990
|
+
const sections = [
|
|
9991
|
+
projectOverview,
|
|
9992
|
+
...typedSections,
|
|
9993
|
+
gitSection,
|
|
9994
|
+
skillsSection
|
|
9995
|
+
];
|
|
9996
|
+
const allRefs = sections.flatMap((s) => s.items.flatMap((i) => i.refs));
|
|
9997
|
+
const obsRefCount = allRefs.filter((r) => r.kind === "observation" || r.kind === "git").length;
|
|
9998
|
+
const skillRefCount = allRefs.filter((r) => r.kind === "mini-skill").length;
|
|
9999
|
+
return {
|
|
10000
|
+
title: "Knowledge Base",
|
|
10001
|
+
subtitle: "LLM Wiki",
|
|
10002
|
+
projectId,
|
|
10003
|
+
generatedAt: options.generatedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
10004
|
+
sections,
|
|
10005
|
+
stats: {
|
|
10006
|
+
observationsUsed: eligible.length,
|
|
10007
|
+
miniSkillsUsed: scopedMiniSkills.length,
|
|
10008
|
+
refs: obsRefCount + skillRefCount
|
|
10009
|
+
}
|
|
10010
|
+
};
|
|
10011
|
+
}
|
|
10012
|
+
var COMMAND_LOG_TITLE3, SECTION_DEFS;
|
|
10013
|
+
var init_generator = __esm({
|
|
10014
|
+
"src/wiki/generator.ts"() {
|
|
10015
|
+
"use strict";
|
|
10016
|
+
init_esm_shims();
|
|
10017
|
+
COMMAND_LOG_TITLE3 = /^(Ran:|Command:|Executed:)\s/i;
|
|
10018
|
+
SECTION_DEFS = [
|
|
10019
|
+
{
|
|
10020
|
+
id: "core-decisions",
|
|
10021
|
+
title: "Core Decisions",
|
|
10022
|
+
typeMatch: (o) => o.type === "decision" || o.type === "trade-off" || o.type === "reasoning"
|
|
10023
|
+
},
|
|
10024
|
+
{
|
|
10025
|
+
id: "operational-knowledge",
|
|
10026
|
+
title: "Operational Knowledge",
|
|
10027
|
+
typeMatch: (o) => o.type === "how-it-works" || o.type === "what-changed" || o.type === "why-it-exists" || o.type === "discovery" || o.type === "session-request"
|
|
10028
|
+
},
|
|
10029
|
+
{
|
|
10030
|
+
id: "known-gotchas",
|
|
10031
|
+
title: "Known Gotchas",
|
|
10032
|
+
typeMatch: (o) => o.type === "gotcha" || o.type === "problem-solution"
|
|
10033
|
+
}
|
|
10034
|
+
];
|
|
10035
|
+
}
|
|
10036
|
+
});
|
|
10037
|
+
|
|
10038
|
+
// src/wiki/knowledge-graph.ts
|
|
10039
|
+
var knowledge_graph_exports = {};
|
|
10040
|
+
__export(knowledge_graph_exports, {
|
|
10041
|
+
generateKnowledgeGraph: () => generateKnowledgeGraph
|
|
10042
|
+
});
|
|
10043
|
+
function inferEdges(nodes, entityNameIndex) {
|
|
10044
|
+
const edges = [];
|
|
10045
|
+
const seen = /* @__PURE__ */ new Set();
|
|
10046
|
+
const maxRelatesEdgesPerEntitySection = 8;
|
|
10047
|
+
function addEdge(source, target, edgeType) {
|
|
10048
|
+
const key = `${source}:${edgeType}:${target}`;
|
|
10049
|
+
if (seen.has(key)) return;
|
|
10050
|
+
if (source === target) return;
|
|
10051
|
+
seen.add(key);
|
|
10052
|
+
edges.push({
|
|
10053
|
+
id: `e_${edges.length}_${source}_${target}`,
|
|
10054
|
+
source,
|
|
10055
|
+
target,
|
|
10056
|
+
edgeType
|
|
10057
|
+
});
|
|
10058
|
+
}
|
|
10059
|
+
function rankNodesForEntity(a, b) {
|
|
10060
|
+
const evidenceDiff = (b.evidenceCount || 0) - (a.evidenceCount || 0);
|
|
10061
|
+
if (evidenceDiff !== 0) return evidenceDiff;
|
|
10062
|
+
return a.id.localeCompare(b.id);
|
|
10063
|
+
}
|
|
10064
|
+
for (const [, group] of entityNameIndex) {
|
|
10065
|
+
if (group.length < 2) continue;
|
|
10066
|
+
const bySection = /* @__PURE__ */ new Map();
|
|
10067
|
+
for (const node of group) {
|
|
10068
|
+
const sectionGroup = bySection.get(node.sectionId) || [];
|
|
10069
|
+
sectionGroup.push(node);
|
|
10070
|
+
bySection.set(node.sectionId, sectionGroup);
|
|
10071
|
+
}
|
|
10072
|
+
for (const sectionGroup of bySection.values()) {
|
|
10073
|
+
const ranked = [...sectionGroup].sort(rankNodesForEntity);
|
|
10074
|
+
if (ranked.length === 2) {
|
|
10075
|
+
addEdge(ranked[0].id, ranked[1].id, "relates_to");
|
|
10076
|
+
addEdge(ranked[1].id, ranked[0].id, "relates_to");
|
|
10077
|
+
continue;
|
|
10078
|
+
}
|
|
10079
|
+
const anchor = ranked[0];
|
|
10080
|
+
for (const node of ranked.slice(1, maxRelatesEdgesPerEntitySection + 1)) {
|
|
10081
|
+
addEdge(anchor.id, node.id, "relates_to");
|
|
10082
|
+
}
|
|
10083
|
+
}
|
|
10084
|
+
const sectionAnchors = [...bySection.entries()].map(([sectionId, sectionGroup]) => ({
|
|
10085
|
+
sectionId,
|
|
10086
|
+
anchor: [...sectionGroup].sort(rankNodesForEntity)[0]
|
|
10087
|
+
})).sort((a, b) => sectionPriority(a.sectionId) - sectionPriority(b.sectionId));
|
|
10088
|
+
for (let i = 0; i < sectionAnchors.length; i++) {
|
|
10089
|
+
for (let j = i + 1; j < sectionAnchors.length; j++) {
|
|
10090
|
+
const from = sectionAnchors[i].anchor;
|
|
10091
|
+
const to = sectionAnchors[j].anchor;
|
|
10092
|
+
const edgeType = sectionPriority(sectionAnchors[i].sectionId) === sectionPriority(sectionAnchors[j].sectionId) ? "relates_to" : "supports";
|
|
10093
|
+
addEdge(from.id, to.id, edgeType);
|
|
10094
|
+
}
|
|
10095
|
+
}
|
|
10096
|
+
}
|
|
10097
|
+
return edges;
|
|
10098
|
+
}
|
|
10099
|
+
function sectionPriority(sectionId) {
|
|
10100
|
+
const order = ["core-decisions", "known-gotchas", "operational-knowledge", "git-backed-facts", "promoted-skills"];
|
|
10101
|
+
const idx = order.indexOf(sectionId);
|
|
10102
|
+
return idx >= 0 ? idx : order.length;
|
|
10103
|
+
}
|
|
10104
|
+
function sectionIdForObs(o) {
|
|
10105
|
+
if (GIT_SECTION.typeMatch(o)) return GIT_SECTION.id;
|
|
10106
|
+
for (const def of SECTION_DEFS2) {
|
|
10107
|
+
if (def.typeMatch(o)) return def.id;
|
|
10108
|
+
}
|
|
10109
|
+
return "operational-knowledge";
|
|
10110
|
+
}
|
|
10111
|
+
function mapRelationType(relationType) {
|
|
10112
|
+
const lower = relationType.toLowerCase();
|
|
10113
|
+
if (lower.includes("support") || lower.includes("depend")) return "supports";
|
|
10114
|
+
if (lower.includes("relat") || lower.includes("connect") || lower.includes("associat")) return "relates_to";
|
|
10115
|
+
if (lower.includes("mention") || lower.includes("refer")) return "mentions";
|
|
10116
|
+
if (lower.includes("deriv") || lower.includes("origin") || lower.includes("source")) return "derived_from";
|
|
10117
|
+
return "relates_to";
|
|
10118
|
+
}
|
|
10119
|
+
function generateKnowledgeGraph(options) {
|
|
10120
|
+
const { projectId, observations: observations2, miniSkills, graphEntities, graphRelations } = options;
|
|
10121
|
+
const eligible = observations2.filter((o) => isEligible(o, projectId));
|
|
10122
|
+
const scopedMiniSkills = miniSkills.filter((s) => s.projectId === projectId);
|
|
10123
|
+
const nodes = [];
|
|
10124
|
+
const entityNameIndex = /* @__PURE__ */ new Map();
|
|
10125
|
+
for (const o of eligible) {
|
|
10126
|
+
const sectionId = sectionIdForObs(o);
|
|
10127
|
+
const ref = {
|
|
10128
|
+
kind: o.source === "git" ? "git" : "observation",
|
|
10129
|
+
id: `obs:${o.id}`,
|
|
10130
|
+
title: o.title
|
|
10131
|
+
};
|
|
10132
|
+
const node = {
|
|
10133
|
+
id: `obs:${o.id}`,
|
|
10134
|
+
label: o.title,
|
|
10135
|
+
nodeType: o.type,
|
|
10136
|
+
sectionId,
|
|
10137
|
+
entityName: o.entityName || void 0,
|
|
10138
|
+
evidenceCount: (o.facts?.length ?? 0) + (o.concepts?.length ?? 0) + (o.filesModified?.length ?? 0),
|
|
10139
|
+
summary: o.narrative?.slice(0, 200) || "",
|
|
10140
|
+
refs: [ref]
|
|
10141
|
+
};
|
|
10142
|
+
nodes.push(node);
|
|
10143
|
+
if (o.entityName) {
|
|
10144
|
+
const group = entityNameIndex.get(o.entityName) || [];
|
|
10145
|
+
group.push(node);
|
|
10146
|
+
entityNameIndex.set(o.entityName, group);
|
|
10147
|
+
}
|
|
10148
|
+
}
|
|
10149
|
+
for (const s of scopedMiniSkills) {
|
|
10150
|
+
const ref = {
|
|
10151
|
+
kind: "mini-skill",
|
|
10152
|
+
id: `skill:${s.id}`,
|
|
10153
|
+
title: s.title
|
|
10154
|
+
};
|
|
10155
|
+
const obsRefs = s.sourceObservationIds.map(
|
|
10156
|
+
(oid) => ({ kind: "observation", id: `obs:${oid}` })
|
|
10157
|
+
);
|
|
10158
|
+
const node = {
|
|
10159
|
+
id: `skill:${s.id}`,
|
|
10160
|
+
label: s.title,
|
|
10161
|
+
nodeType: "mini-skill",
|
|
10162
|
+
sectionId: "promoted-skills",
|
|
10163
|
+
entityName: s.sourceEntity || void 0,
|
|
10164
|
+
evidenceCount: obsRefs.length,
|
|
10165
|
+
summary: s.instruction?.slice(0, 200) || "",
|
|
10166
|
+
refs: [ref, ...obsRefs]
|
|
10167
|
+
};
|
|
10168
|
+
nodes.push(node);
|
|
10169
|
+
if (s.sourceEntity) {
|
|
10170
|
+
const group = entityNameIndex.get(s.sourceEntity) || [];
|
|
10171
|
+
group.push(node);
|
|
10172
|
+
entityNameIndex.set(s.sourceEntity, group);
|
|
10173
|
+
}
|
|
10174
|
+
}
|
|
10175
|
+
const edges = inferEdges(nodes, entityNameIndex);
|
|
10176
|
+
for (const s of scopedMiniSkills) {
|
|
10177
|
+
const skillNodeId = `skill:${s.id}`;
|
|
10178
|
+
for (const oid of s.sourceObservationIds) {
|
|
10179
|
+
const obsNodeId = `obs:${oid}`;
|
|
10180
|
+
if (nodes.some((n) => n.id === obsNodeId)) {
|
|
10181
|
+
edges.push({
|
|
10182
|
+
id: `e_${edges.length}_${skillNodeId}_${obsNodeId}`,
|
|
10183
|
+
source: skillNodeId,
|
|
10184
|
+
target: obsNodeId,
|
|
10185
|
+
edgeType: "derived_from"
|
|
10186
|
+
});
|
|
10187
|
+
}
|
|
10188
|
+
}
|
|
10189
|
+
}
|
|
10190
|
+
if (graphRelations && graphRelations.length > 0) {
|
|
10191
|
+
const entityNodeMap = /* @__PURE__ */ new Map();
|
|
10192
|
+
for (const n of nodes) {
|
|
10193
|
+
if (n.entityName && !entityNodeMap.has(n.entityName)) {
|
|
10194
|
+
entityNodeMap.set(n.entityName, n.id);
|
|
10195
|
+
}
|
|
10196
|
+
}
|
|
10197
|
+
const seen = new Set(edges.map((e) => `${e.source}:${e.edgeType}:${e.target}`));
|
|
10198
|
+
for (const rel of graphRelations) {
|
|
10199
|
+
const srcId = entityNodeMap.get(rel.from);
|
|
10200
|
+
const tgtId = entityNodeMap.get(rel.to);
|
|
10201
|
+
if (!srcId || !tgtId) continue;
|
|
10202
|
+
if (srcId === tgtId) continue;
|
|
10203
|
+
const edgeType = mapRelationType(rel.relationType);
|
|
10204
|
+
const key = `${srcId}:${edgeType}:${tgtId}`;
|
|
10205
|
+
if (seen.has(key)) continue;
|
|
10206
|
+
seen.add(key);
|
|
10207
|
+
edges.push({
|
|
10208
|
+
id: `e_gr_${edges.length}_${srcId}_${tgtId}`,
|
|
10209
|
+
source: srcId,
|
|
10210
|
+
target: tgtId,
|
|
10211
|
+
edgeType
|
|
10212
|
+
});
|
|
10213
|
+
}
|
|
10214
|
+
}
|
|
10215
|
+
const sectionCounts = {};
|
|
10216
|
+
for (const n of nodes) {
|
|
10217
|
+
sectionCounts[n.sectionId] = (sectionCounts[n.sectionId] || 0) + 1;
|
|
10218
|
+
}
|
|
10219
|
+
const clusters = ALL_SECTIONS.filter((def) => (sectionCounts[def.id] ?? 0) > 0).map((def) => ({
|
|
10220
|
+
id: `cluster:${def.id}`,
|
|
10221
|
+
label: def.label,
|
|
10222
|
+
sectionId: def.id,
|
|
10223
|
+
nodeCount: sectionCounts[def.id] ?? 0
|
|
10224
|
+
}));
|
|
10225
|
+
const stats = {
|
|
10226
|
+
totalNodes: nodes.length,
|
|
10227
|
+
totalEdges: edges.length,
|
|
10228
|
+
clusterCount: clusters.length,
|
|
10229
|
+
sectionCounts
|
|
10230
|
+
};
|
|
10231
|
+
return {
|
|
10232
|
+
title: "Knowledge Graph",
|
|
10233
|
+
projectId,
|
|
10234
|
+
generatedAt: options.generatedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
10235
|
+
nodes,
|
|
10236
|
+
edges,
|
|
10237
|
+
clusters,
|
|
10238
|
+
stats
|
|
10239
|
+
};
|
|
10240
|
+
}
|
|
10241
|
+
var SECTION_DEFS2, GIT_SECTION, SKILLS_SECTION, ALL_SECTIONS;
|
|
10242
|
+
var init_knowledge_graph = __esm({
|
|
10243
|
+
"src/wiki/knowledge-graph.ts"() {
|
|
10244
|
+
"use strict";
|
|
10245
|
+
init_esm_shims();
|
|
10246
|
+
init_generator();
|
|
10247
|
+
SECTION_DEFS2 = [
|
|
10248
|
+
{
|
|
10249
|
+
id: "core-decisions",
|
|
10250
|
+
label: "Core Decisions",
|
|
10251
|
+
typeMatch: (o) => o.type === "decision" || o.type === "trade-off" || o.type === "reasoning"
|
|
10252
|
+
},
|
|
10253
|
+
{
|
|
10254
|
+
id: "operational-knowledge",
|
|
10255
|
+
label: "Operational Knowledge",
|
|
10256
|
+
typeMatch: (o) => o.type === "how-it-works" || o.type === "what-changed" || o.type === "why-it-exists" || o.type === "discovery" || o.type === "session-request"
|
|
10257
|
+
},
|
|
10258
|
+
{
|
|
10259
|
+
id: "known-gotchas",
|
|
10260
|
+
label: "Known Gotchas",
|
|
10261
|
+
typeMatch: (o) => o.type === "gotcha" || o.type === "problem-solution"
|
|
10262
|
+
}
|
|
10263
|
+
];
|
|
10264
|
+
GIT_SECTION = {
|
|
10265
|
+
id: "git-backed-facts",
|
|
10266
|
+
label: "Git-backed Facts",
|
|
10267
|
+
typeMatch: (o) => o.source === "git" && o.sourceDetail === "git-ingest"
|
|
10268
|
+
};
|
|
10269
|
+
SKILLS_SECTION = {
|
|
10270
|
+
id: "promoted-skills",
|
|
10271
|
+
label: "Promoted Skills",
|
|
10272
|
+
typeMatch: () => false
|
|
10273
|
+
// skills handled separately
|
|
10274
|
+
};
|
|
10275
|
+
ALL_SECTIONS = [...SECTION_DEFS2, GIT_SECTION, SKILLS_SECTION];
|
|
10276
|
+
}
|
|
10277
|
+
});
|
|
10278
|
+
|
|
9772
10279
|
// src/dashboard/server.ts
|
|
9773
10280
|
var server_exports = {};
|
|
9774
10281
|
__export(server_exports, {
|
|
@@ -9910,6 +10417,7 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
9910
10417
|
const typeCounts = {};
|
|
9911
10418
|
for (const obs of observations2) {
|
|
9912
10419
|
const t = obs.type || "unknown";
|
|
10420
|
+
if (t === "probe") continue;
|
|
9913
10421
|
typeCounts[t] = (typeCounts[t] || 0) + 1;
|
|
9914
10422
|
}
|
|
9915
10423
|
const sourceCounts = { git: 0, agent: 0, manual: 0 };
|
|
@@ -9951,7 +10459,7 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
9951
10459
|
else if (score >= 1) retentionSummary.stale++;
|
|
9952
10460
|
else retentionSummary.archive++;
|
|
9953
10461
|
}
|
|
9954
|
-
const sorted = [...observations2].sort((a, b) => (b.id || 0) - (a.id || 0)).slice(0, 10);
|
|
10462
|
+
const sorted = [...observations2].filter((o) => o.type !== "probe").sort((a, b) => (b.id || 0) - (a.id || 0)).slice(0, 10);
|
|
9955
10463
|
let embeddingStatus = { enabled: false, provider: "", dimensions: 0 };
|
|
9956
10464
|
try {
|
|
9957
10465
|
const { getEmbeddingProvider: getEmbeddingProvider2 } = await Promise.resolve().then(() => (init_provider(), provider_exports));
|
|
@@ -9991,7 +10499,7 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
9991
10499
|
const allObs = await getObservationStore().loadAll();
|
|
9992
10500
|
const observations2 = filterActiveByProject(allObs, effectiveProjectId);
|
|
9993
10501
|
const now = Date.now();
|
|
9994
|
-
const scored = observations2.map((obs) => {
|
|
10502
|
+
const scored = observations2.filter((obs) => obs.type !== "probe").map((obs) => {
|
|
9995
10503
|
const age = now - new Date(obs.createdAt || now).getTime();
|
|
9996
10504
|
const ageHours = age / (1e3 * 60 * 60);
|
|
9997
10505
|
const importance = obs.importance ?? 5;
|
|
@@ -10023,6 +10531,50 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
10023
10531
|
});
|
|
10024
10532
|
break;
|
|
10025
10533
|
}
|
|
10534
|
+
case "/knowledge": {
|
|
10535
|
+
const { generateKnowledgeBase: generateKnowledgeBase2 } = await Promise.resolve().then(() => (init_generator(), generator_exports));
|
|
10536
|
+
const { initObservations: initObservations2, getAllObservations: getAllObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
10537
|
+
const { initMiniSkillStore: initMiniSkillStore2, getMiniSkillStore: getMiniSkillStore2 } = await Promise.resolve().then(() => (init_mini_skill_store(), mini_skill_store_exports));
|
|
10538
|
+
await initObservations2(effectiveDataDir);
|
|
10539
|
+
await initMiniSkillStore2(effectiveDataDir);
|
|
10540
|
+
const allObs = getAllObservations2();
|
|
10541
|
+
const skills = await getMiniSkillStore2().loadByProject(effectiveProjectId);
|
|
10542
|
+
const overview = generateKnowledgeBase2({
|
|
10543
|
+
projectId: effectiveProjectId,
|
|
10544
|
+
observations: allObs,
|
|
10545
|
+
miniSkills: skills
|
|
10546
|
+
});
|
|
10547
|
+
sendJson(res, overview);
|
|
10548
|
+
break;
|
|
10549
|
+
}
|
|
10550
|
+
case "/knowledge-graph": {
|
|
10551
|
+
const { generateKnowledgeGraph: generateKnowledgeGraph2 } = await Promise.resolve().then(() => (init_knowledge_graph(), knowledge_graph_exports));
|
|
10552
|
+
const { initObservations: initObservations2, getAllObservations: getAllObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
10553
|
+
const { initMiniSkillStore: initMiniSkillStore2, getMiniSkillStore: getMiniSkillStore2 } = await Promise.resolve().then(() => (init_mini_skill_store(), mini_skill_store_exports));
|
|
10554
|
+
const { initGraphStore: initGraphStore2, getGraphStore: getGraphStore2 } = await Promise.resolve().then(() => (init_graph_store(), graph_store_exports));
|
|
10555
|
+
await initObservations2(effectiveDataDir);
|
|
10556
|
+
await initMiniSkillStore2(effectiveDataDir);
|
|
10557
|
+
await initGraphStore2(effectiveDataDir);
|
|
10558
|
+
const allObs = getAllObservations2();
|
|
10559
|
+
const skills = await getMiniSkillStore2().loadByProject(effectiveProjectId);
|
|
10560
|
+
const fullGraph = { entities: getGraphStore2().loadEntities(), relations: getGraphStore2().loadRelations() };
|
|
10561
|
+
const graphObs = await getObservationStore().loadAll();
|
|
10562
|
+
const projectEntityNames = new Set(
|
|
10563
|
+
graphObs.filter((o) => o.projectId === effectiveProjectId && (o.status ?? "active") === "active" && o.entityName).map((o) => o.entityName)
|
|
10564
|
+
);
|
|
10565
|
+
const scopedEntities = fullGraph.entities.filter((e) => projectEntityNames.has(e.name));
|
|
10566
|
+
const scopedEntityNameSet = new Set(scopedEntities.map((e) => e.name));
|
|
10567
|
+
const scopedRelations = fullGraph.relations.filter((r) => scopedEntityNameSet.has(r.from) && scopedEntityNameSet.has(r.to));
|
|
10568
|
+
const graph = generateKnowledgeGraph2({
|
|
10569
|
+
projectId: effectiveProjectId,
|
|
10570
|
+
observations: allObs,
|
|
10571
|
+
miniSkills: skills,
|
|
10572
|
+
graphEntities: scopedEntities,
|
|
10573
|
+
graphRelations: scopedRelations
|
|
10574
|
+
});
|
|
10575
|
+
sendJson(res, graph);
|
|
10576
|
+
break;
|
|
10577
|
+
}
|
|
10026
10578
|
case "/config": {
|
|
10027
10579
|
const os4 = await import("os");
|
|
10028
10580
|
const { existsSync: existsSync10 } = await import("fs");
|
|
@@ -10259,13 +10811,20 @@ async function serveStatic(req, res, staticDir) {
|
|
|
10259
10811
|
const ext = path14.extname(filePath);
|
|
10260
10812
|
res.writeHead(200, {
|
|
10261
10813
|
"Content-Type": MIME_TYPES[ext] || "application/octet-stream",
|
|
10262
|
-
"Cache-Control": "no-cache"
|
|
10814
|
+
"Cache-Control": "no-store, no-cache, must-revalidate, max-age=0",
|
|
10815
|
+
"Pragma": "no-cache",
|
|
10816
|
+
"Expires": "0"
|
|
10263
10817
|
});
|
|
10264
10818
|
res.end(data);
|
|
10265
10819
|
} catch {
|
|
10266
10820
|
try {
|
|
10267
10821
|
const indexData = await fs12.readFile(path14.join(staticDir, "index.html"));
|
|
10268
|
-
res.writeHead(200, {
|
|
10822
|
+
res.writeHead(200, {
|
|
10823
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
10824
|
+
"Cache-Control": "no-store, no-cache, must-revalidate, max-age=0",
|
|
10825
|
+
"Pragma": "no-cache",
|
|
10826
|
+
"Expires": "0"
|
|
10827
|
+
});
|
|
10269
10828
|
res.end(indexData);
|
|
10270
10829
|
} catch {
|
|
10271
10830
|
sendError(res, "Not found", 404);
|
|
@@ -11736,6 +12295,8 @@ import { spawnSync } from 'node:child_process';
|
|
|
11736
12295
|
export const MemorixPlugin = async ({ project, client, $, directory, worktree }) => {
|
|
11737
12296
|
// Generate a stable session ID for this plugin lifetime
|
|
11738
12297
|
const sessionId = \`opencode-\${Date.now().toString(36)}-\${Math.random().toString(36).slice(2, 8)}\`;
|
|
12298
|
+
let pendingAssistantResponse = null;
|
|
12299
|
+
let lastDeliveredAssistantKey = '';
|
|
11739
12300
|
|
|
11740
12301
|
/**
|
|
11741
12302
|
* Send event JSON to \`memorix hook\` via child_process.spawnSync.
|
|
@@ -11768,6 +12329,33 @@ export const MemorixPlugin = async ({ project, client, $, directory, worktree })
|
|
|
11768
12329
|
}
|
|
11769
12330
|
}
|
|
11770
12331
|
|
|
12332
|
+
function extractMessageInfo(input) {
|
|
12333
|
+
if (input && typeof input === 'object') {
|
|
12334
|
+
if (input.info && typeof input.info === 'object') return input.info;
|
|
12335
|
+
if (input.message && typeof input.message === 'object') return input.message;
|
|
12336
|
+
if (input.properties && typeof input.properties === 'object') {
|
|
12337
|
+
if (input.properties.info && typeof input.properties.info === 'object') return input.properties.info;
|
|
12338
|
+
if (input.properties.message && typeof input.properties.message === 'object') return input.properties.message;
|
|
12339
|
+
}
|
|
12340
|
+
}
|
|
12341
|
+
return input;
|
|
12342
|
+
}
|
|
12343
|
+
|
|
12344
|
+
function extractMessageText(message) {
|
|
12345
|
+
if (!message || typeof message !== 'object') return '';
|
|
12346
|
+
if (typeof message.content === 'string') return message.content.trim();
|
|
12347
|
+
const parts = Array.isArray(message.parts) ? message.parts : [];
|
|
12348
|
+
return parts
|
|
12349
|
+
.map((part) => {
|
|
12350
|
+
if (!part || typeof part !== 'object') return '';
|
|
12351
|
+
if (typeof part.text === 'string') return part.text;
|
|
12352
|
+
if (typeof part.content === 'string') return part.content;
|
|
12353
|
+
return '';
|
|
12354
|
+
})
|
|
12355
|
+
.join('')
|
|
12356
|
+
.trim();
|
|
12357
|
+
}
|
|
12358
|
+
|
|
11771
12359
|
return {
|
|
11772
12360
|
/** Session created \u2014 record session start */
|
|
11773
12361
|
'session.created': async ({ session }) => {
|
|
@@ -11780,6 +12368,21 @@ export const MemorixPlugin = async ({ project, client, $, directory, worktree })
|
|
|
11780
12368
|
|
|
11781
12369
|
/** Session idle \u2014 record session end */
|
|
11782
12370
|
'session.idle': async ({ session }) => {
|
|
12371
|
+
if (pendingAssistantResponse?.text) {
|
|
12372
|
+
const deliveryKey = pendingAssistantResponse.id
|
|
12373
|
+
? \`\${pendingAssistantResponse.id}:\${pendingAssistantResponse.text}\`
|
|
12374
|
+
: pendingAssistantResponse.text;
|
|
12375
|
+
if (deliveryKey !== lastDeliveredAssistantKey) {
|
|
12376
|
+
runHook({
|
|
12377
|
+
agent: 'opencode',
|
|
12378
|
+
hook_event_name: 'message.updated',
|
|
12379
|
+
ai_response: pendingAssistantResponse.text,
|
|
12380
|
+
message_id: pendingAssistantResponse.id,
|
|
12381
|
+
cwd: directory,
|
|
12382
|
+
});
|
|
12383
|
+
lastDeliveredAssistantKey = deliveryKey;
|
|
12384
|
+
}
|
|
12385
|
+
}
|
|
11783
12386
|
runHook({
|
|
11784
12387
|
agent: 'opencode',
|
|
11785
12388
|
hook_event_name: 'session.idle',
|
|
@@ -11808,6 +12411,19 @@ export const MemorixPlugin = async ({ project, client, $, directory, worktree })
|
|
|
11808
12411
|
});
|
|
11809
12412
|
},
|
|
11810
12413
|
|
|
12414
|
+
/** Message updated \u2014 cache the latest assistant response until session.idle */
|
|
12415
|
+
'message.updated': async (input, output) => {
|
|
12416
|
+
const message = extractMessageInfo(input);
|
|
12417
|
+
const role = message?.role ?? input?.role ?? input?.info?.role;
|
|
12418
|
+
if (role !== 'assistant') return;
|
|
12419
|
+
const text = extractMessageText(message);
|
|
12420
|
+
if (!text) return;
|
|
12421
|
+
pendingAssistantResponse = {
|
|
12422
|
+
id: message?.id ?? input?.id ?? input?.messageID,
|
|
12423
|
+
text,
|
|
12424
|
+
};
|
|
12425
|
+
},
|
|
12426
|
+
|
|
11811
12427
|
/** Session compacted \u2014 record post-compact event */
|
|
11812
12428
|
'session.compacted': async ({ session }) => {
|
|
11813
12429
|
runHook({
|
|
@@ -12057,7 +12673,7 @@ async function installHooks(agent, projectRoot, global = false) {
|
|
|
12057
12673
|
return {
|
|
12058
12674
|
agent,
|
|
12059
12675
|
configPath: pluginPath,
|
|
12060
|
-
events: ["session_start", "session_end", "post_tool", "post_edit", "post_compact", "post_command"],
|
|
12676
|
+
events: ["session_start", "session_end", "post_tool", "post_edit", "post_compact", "post_command", "post_response"],
|
|
12061
12677
|
generated: { note: "OpenCode plugin installed at " + pluginPath }
|
|
12062
12678
|
};
|
|
12063
12679
|
}
|
|
@@ -12515,7 +13131,7 @@ var init_installers = __esm({
|
|
|
12515
13131
|
"src/hooks/installers/index.ts"() {
|
|
12516
13132
|
"use strict";
|
|
12517
13133
|
init_esm_shims();
|
|
12518
|
-
OPENCODE_PLUGIN_VERSION =
|
|
13134
|
+
OPENCODE_PLUGIN_VERSION = 6;
|
|
12519
13135
|
}
|
|
12520
13136
|
});
|
|
12521
13137
|
|
|
@@ -13046,7 +13662,8 @@ function getTypeIcon(type) {
|
|
|
13046
13662
|
"why-it-exists": "[WHY]",
|
|
13047
13663
|
"decision": "[DECISION]",
|
|
13048
13664
|
"trade-off": "[TRADEOFF]",
|
|
13049
|
-
"reasoning": "[REASONING]"
|
|
13665
|
+
"reasoning": "[REASONING]",
|
|
13666
|
+
"probe": "[PROBE]"
|
|
13050
13667
|
};
|
|
13051
13668
|
return icons[type] ?? "[UNKNOWN]";
|
|
13052
13669
|
}
|
|
@@ -15545,7 +16162,8 @@ var OBSERVATION_TYPES = [
|
|
|
15545
16162
|
"discovery",
|
|
15546
16163
|
"why-it-exists",
|
|
15547
16164
|
"decision",
|
|
15548
|
-
"trade-off"
|
|
16165
|
+
"trade-off",
|
|
16166
|
+
"probe"
|
|
15549
16167
|
];
|
|
15550
16168
|
function coerceNumberArray(val) {
|
|
15551
16169
|
if (Array.isArray(val)) return val.map(Number);
|
|
@@ -15774,7 +16392,7 @@ The path should point to a directory containing a .git folder.`
|
|
|
15774
16392
|
};
|
|
15775
16393
|
const server = existingServer ?? new McpServer({
|
|
15776
16394
|
name: "memorix",
|
|
15777
|
-
version: true ? "1.0.
|
|
16395
|
+
version: true ? "1.0.9" : "1.0.1"
|
|
15778
16396
|
});
|
|
15779
16397
|
const originalRegisterTool = server.registerTool.bind(server);
|
|
15780
16398
|
server.registerTool = ((name, ...args) => {
|