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/sdk.js
CHANGED
|
@@ -674,6 +674,13 @@ var init_persistence = __esm({
|
|
|
674
674
|
});
|
|
675
675
|
|
|
676
676
|
// src/store/graph-store.ts
|
|
677
|
+
var graph_store_exports = {};
|
|
678
|
+
__export(graph_store_exports, {
|
|
679
|
+
GraphSqliteStore: () => GraphSqliteStore,
|
|
680
|
+
getGraphStore: () => getGraphStore,
|
|
681
|
+
initGraphStore: () => initGraphStore,
|
|
682
|
+
resetGraphStore: () => resetGraphStore
|
|
683
|
+
});
|
|
677
684
|
import path6 from "path";
|
|
678
685
|
import fs5 from "fs";
|
|
679
686
|
function safeJsonParse(val, fallback) {
|
|
@@ -709,6 +716,16 @@ function getGraphStore() {
|
|
|
709
716
|
if (!_graphStore) throw new Error("[memorix] GraphStore not initialized \u2014 call initGraphStore() first");
|
|
710
717
|
return _graphStore;
|
|
711
718
|
}
|
|
719
|
+
function resetGraphStore() {
|
|
720
|
+
if (_graphStore) {
|
|
721
|
+
try {
|
|
722
|
+
_graphStore.close();
|
|
723
|
+
} catch {
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
_graphStore = null;
|
|
727
|
+
_graphDataDir = null;
|
|
728
|
+
}
|
|
712
729
|
var GraphSqliteStore, _graphStore, _graphDataDir;
|
|
713
730
|
var init_graph_store = __esm({
|
|
714
731
|
"src/store/graph-store.ts"() {
|
|
@@ -1040,7 +1057,8 @@ var init_types = __esm({
|
|
|
1040
1057
|
"why-it-exists": "[WHY]",
|
|
1041
1058
|
"decision": "[DECISION]",
|
|
1042
1059
|
"trade-off": "[TRADEOFF]",
|
|
1043
|
-
"reasoning": "[REASONING]"
|
|
1060
|
+
"reasoning": "[REASONING]",
|
|
1061
|
+
"probe": "[PROBE]"
|
|
1044
1062
|
};
|
|
1045
1063
|
TOPIC_KEY_FAMILIES = {
|
|
1046
1064
|
"architecture": ["architecture", "design", "adr", "structure", "pattern"],
|
|
@@ -1059,6 +1077,15 @@ var init_types = __esm({
|
|
|
1059
1077
|
});
|
|
1060
1078
|
|
|
1061
1079
|
// src/store/mini-skill-store.ts
|
|
1080
|
+
var mini_skill_store_exports = {};
|
|
1081
|
+
__export(mini_skill_store_exports, {
|
|
1082
|
+
MiniSkillGracefulDegrade: () => MiniSkillGracefulDegrade,
|
|
1083
|
+
MiniSkillSqliteStore: () => MiniSkillSqliteStore,
|
|
1084
|
+
getMiniSkillStore: () => getMiniSkillStore,
|
|
1085
|
+
initMiniSkillStore: () => initMiniSkillStore,
|
|
1086
|
+
isMiniSkillStoreInitialized: () => isMiniSkillStoreInitialized,
|
|
1087
|
+
resetMiniSkillStore: () => resetMiniSkillStore
|
|
1088
|
+
});
|
|
1062
1089
|
import path7 from "path";
|
|
1063
1090
|
import fs6 from "fs";
|
|
1064
1091
|
function skillToRow(skill) {
|
|
@@ -1112,6 +1139,10 @@ function getMiniSkillStore() {
|
|
|
1112
1139
|
}
|
|
1113
1140
|
return _store;
|
|
1114
1141
|
}
|
|
1142
|
+
function resetMiniSkillStore() {
|
|
1143
|
+
_store = null;
|
|
1144
|
+
_storeDataDir = null;
|
|
1145
|
+
}
|
|
1115
1146
|
async function initMiniSkillStore(dataDir) {
|
|
1116
1147
|
if (_store && _storeDataDir === dataDir) return _store;
|
|
1117
1148
|
_store = null;
|
|
@@ -1358,6 +1389,12 @@ async function promoteToMiniSkill(projectDir2, projectId, observations2, options
|
|
|
1358
1389
|
`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.`
|
|
1359
1390
|
);
|
|
1360
1391
|
}
|
|
1392
|
+
const probes = observations2.filter((o) => o.type === "probe");
|
|
1393
|
+
if (probes.length > 0) {
|
|
1394
|
+
throw new Error(
|
|
1395
|
+
`Cannot promote probe observations \u2014 they are operational heartbeats, not durable knowledge. Blocked: ${probes.map((o) => `#${o.id} "${o.title.substring(0, 60)}"`).join(", ")}.`
|
|
1396
|
+
);
|
|
1397
|
+
}
|
|
1361
1398
|
if (!options?.force) {
|
|
1362
1399
|
const commandLogs = observations2.filter((o) => COMMAND_LOG_TITLE.test(o.title));
|
|
1363
1400
|
if (commandLogs.length > 0) {
|
|
@@ -4411,6 +4448,10 @@ async function searchObservations(options) {
|
|
|
4411
4448
|
if (statusFilter === "all") return true;
|
|
4412
4449
|
const doc = hit.document;
|
|
4413
4450
|
return (doc.status || "active") === statusFilter;
|
|
4451
|
+
}).filter((hit) => {
|
|
4452
|
+
if (options.type === "probe") return true;
|
|
4453
|
+
const doc = hit.document;
|
|
4454
|
+
return doc.type !== "probe";
|
|
4414
4455
|
}).map((hit) => {
|
|
4415
4456
|
const doc = hit.document;
|
|
4416
4457
|
const obsType = doc.type;
|
|
@@ -6792,6 +6833,11 @@ function hasContradiction(oldText, newText) {
|
|
|
6792
6833
|
];
|
|
6793
6834
|
return negationPatterns.some((p) => p.test(newText));
|
|
6794
6835
|
}
|
|
6836
|
+
function normalizedSearchSimilarity(score) {
|
|
6837
|
+
if (!Number.isFinite(score) || score <= 0) return 0;
|
|
6838
|
+
if (score <= 1) return score;
|
|
6839
|
+
return 0;
|
|
6840
|
+
}
|
|
6795
6841
|
function mergeNarratives(oldNarrative, newNarrative) {
|
|
6796
6842
|
if (newNarrative.length > oldNarrative.length * 1.5) return newNarrative;
|
|
6797
6843
|
if (oldNarrative.length > newNarrative.length * 1.5) return oldNarrative;
|
|
@@ -6882,12 +6928,13 @@ function scoreCandidate(extracted, candidate) {
|
|
|
6882
6928
|
`${extracted.title} ${extracted.narrative}`,
|
|
6883
6929
|
`${candidate.title} ${candidate.narrative}`
|
|
6884
6930
|
);
|
|
6885
|
-
const
|
|
6931
|
+
const searchSimilarity = normalizedSearchSimilarity(candidate.score);
|
|
6932
|
+
const score = searchSimilarity * 0.6 + (entityMatch ? 0.2 : 0) + contentOverlap * 0.2;
|
|
6886
6933
|
const newLength = extracted.narrative.length + extracted.facts.join(" ").length;
|
|
6887
6934
|
const oldLength = candidate.narrative.length + candidate.facts.length;
|
|
6888
6935
|
const richer = newLength > oldLength * 1.15;
|
|
6889
6936
|
const contradiction = hasContradiction(candidate.narrative, extracted.narrative);
|
|
6890
|
-
return { score, entityMatch, richer, contradiction };
|
|
6937
|
+
return { score, searchSimilarity, entityMatch, richer, contradiction };
|
|
6891
6938
|
}
|
|
6892
6939
|
async function runResolve(extracted, projectId, searchMemories, getObservation2, useLLM = false) {
|
|
6893
6940
|
const query = `${extracted.title} ${extracted.narrative.substring(0, 200)}`;
|
|
@@ -6910,7 +6957,7 @@ async function runResolve(extracted, projectId, searchMemories, getObservation2,
|
|
|
6910
6957
|
}));
|
|
6911
6958
|
scored.sort((a, b) => b.score - a.score);
|
|
6912
6959
|
const best = scored[0];
|
|
6913
|
-
if (best.
|
|
6960
|
+
if (best.searchSimilarity >= SIMILARITY_DUPLICATE) {
|
|
6914
6961
|
if (best.richer) {
|
|
6915
6962
|
const existing = getObservation2(best.hit.observationId);
|
|
6916
6963
|
const oldFacts = existing?.facts ?? best.hit.facts.split("\n").filter(Boolean);
|
|
@@ -7097,7 +7144,8 @@ var init_evaluate = __esm({
|
|
|
7097
7144
|
"how-it-works": 0.6,
|
|
7098
7145
|
"discovery": 0.55,
|
|
7099
7146
|
"what-changed": 0.45,
|
|
7100
|
-
"session-request": 0.4
|
|
7147
|
+
"session-request": 0.4,
|
|
7148
|
+
"probe": 0.1
|
|
7101
7149
|
};
|
|
7102
7150
|
SPECIFICITY_PATTERNS = [
|
|
7103
7151
|
/\b\d+\.\d+\.\d+\b/,
|
|
@@ -7561,6 +7609,9 @@ function scoreObservationForSessionContext(obs, projectTokens, now = Date.now())
|
|
|
7561
7609
|
if (obs.valueCategory === "core") {
|
|
7562
7610
|
score += 2;
|
|
7563
7611
|
}
|
|
7612
|
+
if (obs.type === "probe") {
|
|
7613
|
+
score -= 100;
|
|
7614
|
+
}
|
|
7564
7615
|
return score;
|
|
7565
7616
|
}
|
|
7566
7617
|
async function startSession(projectDir2, projectId, opts) {
|
|
@@ -7769,7 +7820,8 @@ var init_session = __esm({
|
|
|
7769
7820
|
"how-it-works": "[INFO]",
|
|
7770
7821
|
"what-changed": "[CHANGE]",
|
|
7771
7822
|
"why-it-exists": "[DECISION]",
|
|
7772
|
-
"session-request": "[SESSION]"
|
|
7823
|
+
"session-request": "[SESSION]",
|
|
7824
|
+
"probe": "[PROBE]"
|
|
7773
7825
|
};
|
|
7774
7826
|
TYPE_WEIGHTS2 = {
|
|
7775
7827
|
"gotcha": 6,
|
|
@@ -7848,11 +7900,17 @@ function getValueCategoryMultiplier(doc) {
|
|
|
7848
7900
|
return 1;
|
|
7849
7901
|
}
|
|
7850
7902
|
function getEffectiveRetentionDays(doc) {
|
|
7903
|
+
const typeOverride = TYPE_RETENTION_OVERRIDE[doc.type];
|
|
7904
|
+
if (typeOverride !== void 0) {
|
|
7905
|
+
const raw2 = typeOverride * getSourceRetentionMultiplier(doc);
|
|
7906
|
+
return Math.max(MIN_RETENTION_DAYS, raw2);
|
|
7907
|
+
}
|
|
7851
7908
|
const importance = getImportanceLevel(doc);
|
|
7852
7909
|
const raw = RETENTION_DAYS[importance] * getSourceRetentionMultiplier(doc) * getValueCategoryMultiplier(doc);
|
|
7853
7910
|
return Math.max(MIN_RETENTION_DAYS, raw);
|
|
7854
7911
|
}
|
|
7855
7912
|
function isImmune(doc) {
|
|
7913
|
+
if (doc.type === "probe") return false;
|
|
7856
7914
|
if (doc.valueCategory === "core") return true;
|
|
7857
7915
|
const importance = getImportanceLevel(doc);
|
|
7858
7916
|
if (importance === "critical") return true;
|
|
@@ -7861,6 +7919,7 @@ function isImmune(doc) {
|
|
|
7861
7919
|
return concepts.some((c) => PROTECTED_TAGS.has(c));
|
|
7862
7920
|
}
|
|
7863
7921
|
function getImmunityReason(doc) {
|
|
7922
|
+
if (doc.type === "probe") return null;
|
|
7864
7923
|
if (doc.valueCategory === "core") return "core valueCategory (formation-classified)";
|
|
7865
7924
|
const importance = getImportanceLevel(doc);
|
|
7866
7925
|
if (importance === "critical") return "critical importance";
|
|
@@ -8017,7 +8076,7 @@ async function archiveExpired(projectDir2, referenceTime, accessMap) {
|
|
|
8017
8076
|
return { archived: archivedCount, remaining: activeObs.length - archivedCount };
|
|
8018
8077
|
});
|
|
8019
8078
|
}
|
|
8020
|
-
var RETENTION_DAYS, BASE_IMPORTANCE, TYPE_IMPORTANCE, PROTECTED_TAGS, MIN_ACCESS_FOR_IMMUNITY, MIN_RETENTION_DAYS;
|
|
8079
|
+
var RETENTION_DAYS, BASE_IMPORTANCE, TYPE_IMPORTANCE, TYPE_RETENTION_OVERRIDE, PROTECTED_TAGS, MIN_ACCESS_FOR_IMMUNITY, MIN_RETENTION_DAYS;
|
|
8021
8080
|
var init_retention = __esm({
|
|
8022
8081
|
"src/memory/retention.ts"() {
|
|
8023
8082
|
"use strict";
|
|
@@ -8045,7 +8104,12 @@ var init_retention = __esm({
|
|
|
8045
8104
|
"what-changed": "low",
|
|
8046
8105
|
"why-it-exists": "medium",
|
|
8047
8106
|
discovery: "low",
|
|
8048
|
-
"session-request": "low"
|
|
8107
|
+
"session-request": "low",
|
|
8108
|
+
probe: "low"
|
|
8109
|
+
};
|
|
8110
|
+
TYPE_RETENTION_OVERRIDE = {
|
|
8111
|
+
probe: 7
|
|
8112
|
+
// Operational heartbeats: expire after ~7 days regardless of source/valueCategory
|
|
8049
8113
|
};
|
|
8050
8114
|
PROTECTED_TAGS = /* @__PURE__ */ new Set(["keep", "important", "pinned", "critical"]);
|
|
8051
8115
|
MIN_ACCESS_FOR_IMMUNITY = 3;
|
|
@@ -9773,6 +9837,449 @@ var init_project_classification = __esm({
|
|
|
9773
9837
|
}
|
|
9774
9838
|
});
|
|
9775
9839
|
|
|
9840
|
+
// src/wiki/generator.ts
|
|
9841
|
+
var generator_exports = {};
|
|
9842
|
+
__export(generator_exports, {
|
|
9843
|
+
contextualHasSubstance: () => contextualHasSubstance,
|
|
9844
|
+
generateKnowledgeBase: () => generateKnowledgeBase,
|
|
9845
|
+
isCommandLog: () => isCommandLog,
|
|
9846
|
+
isEligible: () => isEligible,
|
|
9847
|
+
isExcludedType: () => isExcludedType
|
|
9848
|
+
});
|
|
9849
|
+
function isExcludedType(o) {
|
|
9850
|
+
return o.type === "probe";
|
|
9851
|
+
}
|
|
9852
|
+
function isCommandLog(o) {
|
|
9853
|
+
return COMMAND_LOG_TITLE3.test(o.title || "");
|
|
9854
|
+
}
|
|
9855
|
+
function isInactive(o) {
|
|
9856
|
+
const status = o.status ?? "active";
|
|
9857
|
+
return status !== "active";
|
|
9858
|
+
}
|
|
9859
|
+
function isOtherProject(o, projectId) {
|
|
9860
|
+
return o.projectId !== projectId;
|
|
9861
|
+
}
|
|
9862
|
+
function contextualHasSubstance(o) {
|
|
9863
|
+
if (o.valueCategory !== "contextual") return true;
|
|
9864
|
+
const hasFacts = (o.facts?.length ?? 0) > 0;
|
|
9865
|
+
const hasConcepts = (o.concepts?.length ?? 0) > 0;
|
|
9866
|
+
const hasFiles = (o.filesModified?.length ?? 0) > 0;
|
|
9867
|
+
const hasEntity = !!(o.entityName && o.entityName !== "quick-note" && o.entityName !== "unknown");
|
|
9868
|
+
return hasFacts || hasConcepts || hasFiles || hasEntity;
|
|
9869
|
+
}
|
|
9870
|
+
function isEligible(o, projectId) {
|
|
9871
|
+
if (isExcludedType(o)) return false;
|
|
9872
|
+
if (isCommandLog(o)) return false;
|
|
9873
|
+
if (isInactive(o)) return false;
|
|
9874
|
+
if (isOtherProject(o, projectId)) return false;
|
|
9875
|
+
if (o.valueCategory === "ephemeral") return false;
|
|
9876
|
+
if (!contextualHasSubstance(o)) return false;
|
|
9877
|
+
return true;
|
|
9878
|
+
}
|
|
9879
|
+
function obsToItem(o) {
|
|
9880
|
+
const ref = {
|
|
9881
|
+
kind: o.source === "git" ? "git" : "observation",
|
|
9882
|
+
id: `obs:${o.id}`,
|
|
9883
|
+
title: o.title
|
|
9884
|
+
};
|
|
9885
|
+
return {
|
|
9886
|
+
title: o.title,
|
|
9887
|
+
summary: o.narrative?.slice(0, 200) || "",
|
|
9888
|
+
type: o.type,
|
|
9889
|
+
entityName: o.entityName || void 0,
|
|
9890
|
+
refs: [ref]
|
|
9891
|
+
};
|
|
9892
|
+
}
|
|
9893
|
+
function skillToItem(s) {
|
|
9894
|
+
const ref = {
|
|
9895
|
+
kind: "mini-skill",
|
|
9896
|
+
id: `skill:${s.id}`,
|
|
9897
|
+
title: s.title
|
|
9898
|
+
};
|
|
9899
|
+
const obsRefs = s.sourceObservationIds.map(
|
|
9900
|
+
(oid) => ({ kind: "observation", id: `obs:${oid}` })
|
|
9901
|
+
);
|
|
9902
|
+
return {
|
|
9903
|
+
title: s.title,
|
|
9904
|
+
summary: s.instruction?.slice(0, 200) || "",
|
|
9905
|
+
type: "mini-skill",
|
|
9906
|
+
entityName: s.sourceEntity || void 0,
|
|
9907
|
+
refs: [ref, ...obsRefs]
|
|
9908
|
+
};
|
|
9909
|
+
}
|
|
9910
|
+
function buildGitSection(observations2) {
|
|
9911
|
+
const gitObs = observations2.filter(
|
|
9912
|
+
(o) => o.source === "git" && o.sourceDetail === "git-ingest"
|
|
9913
|
+
);
|
|
9914
|
+
const items = gitObs.map(obsToItem);
|
|
9915
|
+
return {
|
|
9916
|
+
id: "git-backed-facts",
|
|
9917
|
+
title: "Git-backed Facts",
|
|
9918
|
+
items,
|
|
9919
|
+
empty: items.length === 0
|
|
9920
|
+
};
|
|
9921
|
+
}
|
|
9922
|
+
function buildSkillsSection(skills) {
|
|
9923
|
+
const items = skills.map(skillToItem);
|
|
9924
|
+
return {
|
|
9925
|
+
id: "promoted-skills",
|
|
9926
|
+
title: "Promoted Skills",
|
|
9927
|
+
items,
|
|
9928
|
+
empty: items.length === 0
|
|
9929
|
+
};
|
|
9930
|
+
}
|
|
9931
|
+
function buildProjectOverview(projectId, eligibleObs, skills) {
|
|
9932
|
+
const refs = [
|
|
9933
|
+
...eligibleObs.slice(0, 5).map((o) => ({
|
|
9934
|
+
kind: o.source === "git" ? "git" : "observation",
|
|
9935
|
+
id: `obs:${o.id}`,
|
|
9936
|
+
title: o.title
|
|
9937
|
+
})),
|
|
9938
|
+
...skills.slice(0, 5).map((s) => ({
|
|
9939
|
+
kind: "mini-skill",
|
|
9940
|
+
id: `skill:${s.id}`,
|
|
9941
|
+
title: s.title
|
|
9942
|
+
}))
|
|
9943
|
+
];
|
|
9944
|
+
if (refs.length === 0) {
|
|
9945
|
+
return {
|
|
9946
|
+
id: "project-overview",
|
|
9947
|
+
title: "Project Overview",
|
|
9948
|
+
items: [],
|
|
9949
|
+
empty: true
|
|
9950
|
+
};
|
|
9951
|
+
}
|
|
9952
|
+
const lines = [];
|
|
9953
|
+
lines.push(`Project: ${projectId}`);
|
|
9954
|
+
lines.push(`Observations in KB: ${eligibleObs.length}`);
|
|
9955
|
+
lines.push(`Promoted skills: ${skills.length}`);
|
|
9956
|
+
const entityCounts = /* @__PURE__ */ new Map();
|
|
9957
|
+
for (const o of eligibleObs) {
|
|
9958
|
+
if (o.entityName) {
|
|
9959
|
+
entityCounts.set(o.entityName, (entityCounts.get(o.entityName) || 0) + 1);
|
|
9960
|
+
}
|
|
9961
|
+
}
|
|
9962
|
+
const topEntities = [...entityCounts.entries()].sort((a, b) => b[1] - a[1]).slice(0, 5);
|
|
9963
|
+
if (topEntities.length > 0) {
|
|
9964
|
+
lines.push(`Top entities: ${topEntities.map(([name, count2]) => `${name} (${count2})`).join(", ")}`);
|
|
9965
|
+
}
|
|
9966
|
+
return {
|
|
9967
|
+
id: "project-overview",
|
|
9968
|
+
title: "Project Overview",
|
|
9969
|
+
items: [{
|
|
9970
|
+
title: projectId,
|
|
9971
|
+
summary: lines.join("\n"),
|
|
9972
|
+
type: "overview",
|
|
9973
|
+
refs
|
|
9974
|
+
}]
|
|
9975
|
+
};
|
|
9976
|
+
}
|
|
9977
|
+
function generateKnowledgeBase(options) {
|
|
9978
|
+
const { projectId, observations: observations2, miniSkills } = options;
|
|
9979
|
+
const eligible = observations2.filter((o) => isEligible(o, projectId));
|
|
9980
|
+
const scopedMiniSkills = miniSkills.filter((s) => s.projectId === projectId);
|
|
9981
|
+
const typedSections = SECTION_DEFS.map((def) => {
|
|
9982
|
+
const matched = eligible.filter(def.typeMatch);
|
|
9983
|
+
const items = matched.map(obsToItem);
|
|
9984
|
+
return {
|
|
9985
|
+
id: def.id,
|
|
9986
|
+
title: def.title,
|
|
9987
|
+
items,
|
|
9988
|
+
empty: items.length === 0
|
|
9989
|
+
};
|
|
9990
|
+
});
|
|
9991
|
+
const projectOverview = buildProjectOverview(projectId, eligible, scopedMiniSkills);
|
|
9992
|
+
const gitSection = buildGitSection(eligible);
|
|
9993
|
+
const skillsSection = buildSkillsSection(scopedMiniSkills);
|
|
9994
|
+
const sections = [
|
|
9995
|
+
projectOverview,
|
|
9996
|
+
...typedSections,
|
|
9997
|
+
gitSection,
|
|
9998
|
+
skillsSection
|
|
9999
|
+
];
|
|
10000
|
+
const allRefs = sections.flatMap((s) => s.items.flatMap((i) => i.refs));
|
|
10001
|
+
const obsRefCount = allRefs.filter((r) => r.kind === "observation" || r.kind === "git").length;
|
|
10002
|
+
const skillRefCount = allRefs.filter((r) => r.kind === "mini-skill").length;
|
|
10003
|
+
return {
|
|
10004
|
+
title: "Knowledge Base",
|
|
10005
|
+
subtitle: "LLM Wiki",
|
|
10006
|
+
projectId,
|
|
10007
|
+
generatedAt: options.generatedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
10008
|
+
sections,
|
|
10009
|
+
stats: {
|
|
10010
|
+
observationsUsed: eligible.length,
|
|
10011
|
+
miniSkillsUsed: scopedMiniSkills.length,
|
|
10012
|
+
refs: obsRefCount + skillRefCount
|
|
10013
|
+
}
|
|
10014
|
+
};
|
|
10015
|
+
}
|
|
10016
|
+
var COMMAND_LOG_TITLE3, SECTION_DEFS;
|
|
10017
|
+
var init_generator = __esm({
|
|
10018
|
+
"src/wiki/generator.ts"() {
|
|
10019
|
+
"use strict";
|
|
10020
|
+
init_esm_shims();
|
|
10021
|
+
COMMAND_LOG_TITLE3 = /^(Ran:|Command:|Executed:)\s/i;
|
|
10022
|
+
SECTION_DEFS = [
|
|
10023
|
+
{
|
|
10024
|
+
id: "core-decisions",
|
|
10025
|
+
title: "Core Decisions",
|
|
10026
|
+
typeMatch: (o) => o.type === "decision" || o.type === "trade-off" || o.type === "reasoning"
|
|
10027
|
+
},
|
|
10028
|
+
{
|
|
10029
|
+
id: "operational-knowledge",
|
|
10030
|
+
title: "Operational Knowledge",
|
|
10031
|
+
typeMatch: (o) => o.type === "how-it-works" || o.type === "what-changed" || o.type === "why-it-exists" || o.type === "discovery" || o.type === "session-request"
|
|
10032
|
+
},
|
|
10033
|
+
{
|
|
10034
|
+
id: "known-gotchas",
|
|
10035
|
+
title: "Known Gotchas",
|
|
10036
|
+
typeMatch: (o) => o.type === "gotcha" || o.type === "problem-solution"
|
|
10037
|
+
}
|
|
10038
|
+
];
|
|
10039
|
+
}
|
|
10040
|
+
});
|
|
10041
|
+
|
|
10042
|
+
// src/wiki/knowledge-graph.ts
|
|
10043
|
+
var knowledge_graph_exports = {};
|
|
10044
|
+
__export(knowledge_graph_exports, {
|
|
10045
|
+
generateKnowledgeGraph: () => generateKnowledgeGraph
|
|
10046
|
+
});
|
|
10047
|
+
function inferEdges(nodes, entityNameIndex) {
|
|
10048
|
+
const edges = [];
|
|
10049
|
+
const seen = /* @__PURE__ */ new Set();
|
|
10050
|
+
const maxRelatesEdgesPerEntitySection = 8;
|
|
10051
|
+
function addEdge(source, target, edgeType) {
|
|
10052
|
+
const key = `${source}:${edgeType}:${target}`;
|
|
10053
|
+
if (seen.has(key)) return;
|
|
10054
|
+
if (source === target) return;
|
|
10055
|
+
seen.add(key);
|
|
10056
|
+
edges.push({
|
|
10057
|
+
id: `e_${edges.length}_${source}_${target}`,
|
|
10058
|
+
source,
|
|
10059
|
+
target,
|
|
10060
|
+
edgeType
|
|
10061
|
+
});
|
|
10062
|
+
}
|
|
10063
|
+
function rankNodesForEntity(a, b) {
|
|
10064
|
+
const evidenceDiff = (b.evidenceCount || 0) - (a.evidenceCount || 0);
|
|
10065
|
+
if (evidenceDiff !== 0) return evidenceDiff;
|
|
10066
|
+
return a.id.localeCompare(b.id);
|
|
10067
|
+
}
|
|
10068
|
+
for (const [, group] of entityNameIndex) {
|
|
10069
|
+
if (group.length < 2) continue;
|
|
10070
|
+
const bySection = /* @__PURE__ */ new Map();
|
|
10071
|
+
for (const node of group) {
|
|
10072
|
+
const sectionGroup = bySection.get(node.sectionId) || [];
|
|
10073
|
+
sectionGroup.push(node);
|
|
10074
|
+
bySection.set(node.sectionId, sectionGroup);
|
|
10075
|
+
}
|
|
10076
|
+
for (const sectionGroup of bySection.values()) {
|
|
10077
|
+
const ranked = [...sectionGroup].sort(rankNodesForEntity);
|
|
10078
|
+
if (ranked.length === 2) {
|
|
10079
|
+
addEdge(ranked[0].id, ranked[1].id, "relates_to");
|
|
10080
|
+
addEdge(ranked[1].id, ranked[0].id, "relates_to");
|
|
10081
|
+
continue;
|
|
10082
|
+
}
|
|
10083
|
+
const anchor = ranked[0];
|
|
10084
|
+
for (const node of ranked.slice(1, maxRelatesEdgesPerEntitySection + 1)) {
|
|
10085
|
+
addEdge(anchor.id, node.id, "relates_to");
|
|
10086
|
+
}
|
|
10087
|
+
}
|
|
10088
|
+
const sectionAnchors = [...bySection.entries()].map(([sectionId, sectionGroup]) => ({
|
|
10089
|
+
sectionId,
|
|
10090
|
+
anchor: [...sectionGroup].sort(rankNodesForEntity)[0]
|
|
10091
|
+
})).sort((a, b) => sectionPriority(a.sectionId) - sectionPriority(b.sectionId));
|
|
10092
|
+
for (let i = 0; i < sectionAnchors.length; i++) {
|
|
10093
|
+
for (let j = i + 1; j < sectionAnchors.length; j++) {
|
|
10094
|
+
const from = sectionAnchors[i].anchor;
|
|
10095
|
+
const to = sectionAnchors[j].anchor;
|
|
10096
|
+
const edgeType = sectionPriority(sectionAnchors[i].sectionId) === sectionPriority(sectionAnchors[j].sectionId) ? "relates_to" : "supports";
|
|
10097
|
+
addEdge(from.id, to.id, edgeType);
|
|
10098
|
+
}
|
|
10099
|
+
}
|
|
10100
|
+
}
|
|
10101
|
+
return edges;
|
|
10102
|
+
}
|
|
10103
|
+
function sectionPriority(sectionId) {
|
|
10104
|
+
const order = ["core-decisions", "known-gotchas", "operational-knowledge", "git-backed-facts", "promoted-skills"];
|
|
10105
|
+
const idx = order.indexOf(sectionId);
|
|
10106
|
+
return idx >= 0 ? idx : order.length;
|
|
10107
|
+
}
|
|
10108
|
+
function sectionIdForObs(o) {
|
|
10109
|
+
if (GIT_SECTION.typeMatch(o)) return GIT_SECTION.id;
|
|
10110
|
+
for (const def of SECTION_DEFS2) {
|
|
10111
|
+
if (def.typeMatch(o)) return def.id;
|
|
10112
|
+
}
|
|
10113
|
+
return "operational-knowledge";
|
|
10114
|
+
}
|
|
10115
|
+
function mapRelationType(relationType) {
|
|
10116
|
+
const lower = relationType.toLowerCase();
|
|
10117
|
+
if (lower.includes("support") || lower.includes("depend")) return "supports";
|
|
10118
|
+
if (lower.includes("relat") || lower.includes("connect") || lower.includes("associat")) return "relates_to";
|
|
10119
|
+
if (lower.includes("mention") || lower.includes("refer")) return "mentions";
|
|
10120
|
+
if (lower.includes("deriv") || lower.includes("origin") || lower.includes("source")) return "derived_from";
|
|
10121
|
+
return "relates_to";
|
|
10122
|
+
}
|
|
10123
|
+
function generateKnowledgeGraph(options) {
|
|
10124
|
+
const { projectId, observations: observations2, miniSkills, graphEntities, graphRelations } = options;
|
|
10125
|
+
const eligible = observations2.filter((o) => isEligible(o, projectId));
|
|
10126
|
+
const scopedMiniSkills = miniSkills.filter((s) => s.projectId === projectId);
|
|
10127
|
+
const nodes = [];
|
|
10128
|
+
const entityNameIndex = /* @__PURE__ */ new Map();
|
|
10129
|
+
for (const o of eligible) {
|
|
10130
|
+
const sectionId = sectionIdForObs(o);
|
|
10131
|
+
const ref = {
|
|
10132
|
+
kind: o.source === "git" ? "git" : "observation",
|
|
10133
|
+
id: `obs:${o.id}`,
|
|
10134
|
+
title: o.title
|
|
10135
|
+
};
|
|
10136
|
+
const node = {
|
|
10137
|
+
id: `obs:${o.id}`,
|
|
10138
|
+
label: o.title,
|
|
10139
|
+
nodeType: o.type,
|
|
10140
|
+
sectionId,
|
|
10141
|
+
entityName: o.entityName || void 0,
|
|
10142
|
+
evidenceCount: (o.facts?.length ?? 0) + (o.concepts?.length ?? 0) + (o.filesModified?.length ?? 0),
|
|
10143
|
+
summary: o.narrative?.slice(0, 200) || "",
|
|
10144
|
+
refs: [ref]
|
|
10145
|
+
};
|
|
10146
|
+
nodes.push(node);
|
|
10147
|
+
if (o.entityName) {
|
|
10148
|
+
const group = entityNameIndex.get(o.entityName) || [];
|
|
10149
|
+
group.push(node);
|
|
10150
|
+
entityNameIndex.set(o.entityName, group);
|
|
10151
|
+
}
|
|
10152
|
+
}
|
|
10153
|
+
for (const s of scopedMiniSkills) {
|
|
10154
|
+
const ref = {
|
|
10155
|
+
kind: "mini-skill",
|
|
10156
|
+
id: `skill:${s.id}`,
|
|
10157
|
+
title: s.title
|
|
10158
|
+
};
|
|
10159
|
+
const obsRefs = s.sourceObservationIds.map(
|
|
10160
|
+
(oid) => ({ kind: "observation", id: `obs:${oid}` })
|
|
10161
|
+
);
|
|
10162
|
+
const node = {
|
|
10163
|
+
id: `skill:${s.id}`,
|
|
10164
|
+
label: s.title,
|
|
10165
|
+
nodeType: "mini-skill",
|
|
10166
|
+
sectionId: "promoted-skills",
|
|
10167
|
+
entityName: s.sourceEntity || void 0,
|
|
10168
|
+
evidenceCount: obsRefs.length,
|
|
10169
|
+
summary: s.instruction?.slice(0, 200) || "",
|
|
10170
|
+
refs: [ref, ...obsRefs]
|
|
10171
|
+
};
|
|
10172
|
+
nodes.push(node);
|
|
10173
|
+
if (s.sourceEntity) {
|
|
10174
|
+
const group = entityNameIndex.get(s.sourceEntity) || [];
|
|
10175
|
+
group.push(node);
|
|
10176
|
+
entityNameIndex.set(s.sourceEntity, group);
|
|
10177
|
+
}
|
|
10178
|
+
}
|
|
10179
|
+
const edges = inferEdges(nodes, entityNameIndex);
|
|
10180
|
+
for (const s of scopedMiniSkills) {
|
|
10181
|
+
const skillNodeId = `skill:${s.id}`;
|
|
10182
|
+
for (const oid of s.sourceObservationIds) {
|
|
10183
|
+
const obsNodeId = `obs:${oid}`;
|
|
10184
|
+
if (nodes.some((n) => n.id === obsNodeId)) {
|
|
10185
|
+
edges.push({
|
|
10186
|
+
id: `e_${edges.length}_${skillNodeId}_${obsNodeId}`,
|
|
10187
|
+
source: skillNodeId,
|
|
10188
|
+
target: obsNodeId,
|
|
10189
|
+
edgeType: "derived_from"
|
|
10190
|
+
});
|
|
10191
|
+
}
|
|
10192
|
+
}
|
|
10193
|
+
}
|
|
10194
|
+
if (graphRelations && graphRelations.length > 0) {
|
|
10195
|
+
const entityNodeMap = /* @__PURE__ */ new Map();
|
|
10196
|
+
for (const n of nodes) {
|
|
10197
|
+
if (n.entityName && !entityNodeMap.has(n.entityName)) {
|
|
10198
|
+
entityNodeMap.set(n.entityName, n.id);
|
|
10199
|
+
}
|
|
10200
|
+
}
|
|
10201
|
+
const seen = new Set(edges.map((e) => `${e.source}:${e.edgeType}:${e.target}`));
|
|
10202
|
+
for (const rel of graphRelations) {
|
|
10203
|
+
const srcId = entityNodeMap.get(rel.from);
|
|
10204
|
+
const tgtId = entityNodeMap.get(rel.to);
|
|
10205
|
+
if (!srcId || !tgtId) continue;
|
|
10206
|
+
if (srcId === tgtId) continue;
|
|
10207
|
+
const edgeType = mapRelationType(rel.relationType);
|
|
10208
|
+
const key = `${srcId}:${edgeType}:${tgtId}`;
|
|
10209
|
+
if (seen.has(key)) continue;
|
|
10210
|
+
seen.add(key);
|
|
10211
|
+
edges.push({
|
|
10212
|
+
id: `e_gr_${edges.length}_${srcId}_${tgtId}`,
|
|
10213
|
+
source: srcId,
|
|
10214
|
+
target: tgtId,
|
|
10215
|
+
edgeType
|
|
10216
|
+
});
|
|
10217
|
+
}
|
|
10218
|
+
}
|
|
10219
|
+
const sectionCounts = {};
|
|
10220
|
+
for (const n of nodes) {
|
|
10221
|
+
sectionCounts[n.sectionId] = (sectionCounts[n.sectionId] || 0) + 1;
|
|
10222
|
+
}
|
|
10223
|
+
const clusters = ALL_SECTIONS.filter((def) => (sectionCounts[def.id] ?? 0) > 0).map((def) => ({
|
|
10224
|
+
id: `cluster:${def.id}`,
|
|
10225
|
+
label: def.label,
|
|
10226
|
+
sectionId: def.id,
|
|
10227
|
+
nodeCount: sectionCounts[def.id] ?? 0
|
|
10228
|
+
}));
|
|
10229
|
+
const stats = {
|
|
10230
|
+
totalNodes: nodes.length,
|
|
10231
|
+
totalEdges: edges.length,
|
|
10232
|
+
clusterCount: clusters.length,
|
|
10233
|
+
sectionCounts
|
|
10234
|
+
};
|
|
10235
|
+
return {
|
|
10236
|
+
title: "Knowledge Graph",
|
|
10237
|
+
projectId,
|
|
10238
|
+
generatedAt: options.generatedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
10239
|
+
nodes,
|
|
10240
|
+
edges,
|
|
10241
|
+
clusters,
|
|
10242
|
+
stats
|
|
10243
|
+
};
|
|
10244
|
+
}
|
|
10245
|
+
var SECTION_DEFS2, GIT_SECTION, SKILLS_SECTION, ALL_SECTIONS;
|
|
10246
|
+
var init_knowledge_graph = __esm({
|
|
10247
|
+
"src/wiki/knowledge-graph.ts"() {
|
|
10248
|
+
"use strict";
|
|
10249
|
+
init_esm_shims();
|
|
10250
|
+
init_generator();
|
|
10251
|
+
SECTION_DEFS2 = [
|
|
10252
|
+
{
|
|
10253
|
+
id: "core-decisions",
|
|
10254
|
+
label: "Core Decisions",
|
|
10255
|
+
typeMatch: (o) => o.type === "decision" || o.type === "trade-off" || o.type === "reasoning"
|
|
10256
|
+
},
|
|
10257
|
+
{
|
|
10258
|
+
id: "operational-knowledge",
|
|
10259
|
+
label: "Operational Knowledge",
|
|
10260
|
+
typeMatch: (o) => o.type === "how-it-works" || o.type === "what-changed" || o.type === "why-it-exists" || o.type === "discovery" || o.type === "session-request"
|
|
10261
|
+
},
|
|
10262
|
+
{
|
|
10263
|
+
id: "known-gotchas",
|
|
10264
|
+
label: "Known Gotchas",
|
|
10265
|
+
typeMatch: (o) => o.type === "gotcha" || o.type === "problem-solution"
|
|
10266
|
+
}
|
|
10267
|
+
];
|
|
10268
|
+
GIT_SECTION = {
|
|
10269
|
+
id: "git-backed-facts",
|
|
10270
|
+
label: "Git-backed Facts",
|
|
10271
|
+
typeMatch: (o) => o.source === "git" && o.sourceDetail === "git-ingest"
|
|
10272
|
+
};
|
|
10273
|
+
SKILLS_SECTION = {
|
|
10274
|
+
id: "promoted-skills",
|
|
10275
|
+
label: "Promoted Skills",
|
|
10276
|
+
typeMatch: () => false
|
|
10277
|
+
// skills handled separately
|
|
10278
|
+
};
|
|
10279
|
+
ALL_SECTIONS = [...SECTION_DEFS2, GIT_SECTION, SKILLS_SECTION];
|
|
10280
|
+
}
|
|
10281
|
+
});
|
|
10282
|
+
|
|
9776
10283
|
// src/dashboard/server.ts
|
|
9777
10284
|
var server_exports = {};
|
|
9778
10285
|
__export(server_exports, {
|
|
@@ -9914,6 +10421,7 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
9914
10421
|
const typeCounts = {};
|
|
9915
10422
|
for (const obs of observations2) {
|
|
9916
10423
|
const t = obs.type || "unknown";
|
|
10424
|
+
if (t === "probe") continue;
|
|
9917
10425
|
typeCounts[t] = (typeCounts[t] || 0) + 1;
|
|
9918
10426
|
}
|
|
9919
10427
|
const sourceCounts = { git: 0, agent: 0, manual: 0 };
|
|
@@ -9955,7 +10463,7 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
9955
10463
|
else if (score >= 1) retentionSummary.stale++;
|
|
9956
10464
|
else retentionSummary.archive++;
|
|
9957
10465
|
}
|
|
9958
|
-
const sorted = [...observations2].sort((a, b) => (b.id || 0) - (a.id || 0)).slice(0, 10);
|
|
10466
|
+
const sorted = [...observations2].filter((o) => o.type !== "probe").sort((a, b) => (b.id || 0) - (a.id || 0)).slice(0, 10);
|
|
9959
10467
|
let embeddingStatus = { enabled: false, provider: "", dimensions: 0 };
|
|
9960
10468
|
try {
|
|
9961
10469
|
const { getEmbeddingProvider: getEmbeddingProvider2 } = await Promise.resolve().then(() => (init_provider(), provider_exports));
|
|
@@ -9995,7 +10503,7 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
9995
10503
|
const allObs = await getObservationStore().loadAll();
|
|
9996
10504
|
const observations2 = filterActiveByProject(allObs, effectiveProjectId);
|
|
9997
10505
|
const now = Date.now();
|
|
9998
|
-
const scored = observations2.map((obs) => {
|
|
10506
|
+
const scored = observations2.filter((obs) => obs.type !== "probe").map((obs) => {
|
|
9999
10507
|
const age = now - new Date(obs.createdAt || now).getTime();
|
|
10000
10508
|
const ageHours = age / (1e3 * 60 * 60);
|
|
10001
10509
|
const importance = obs.importance ?? 5;
|
|
@@ -10027,6 +10535,50 @@ async function handleApi(req, res, dataDir, projectId, projectName, baseDir, pro
|
|
|
10027
10535
|
});
|
|
10028
10536
|
break;
|
|
10029
10537
|
}
|
|
10538
|
+
case "/knowledge": {
|
|
10539
|
+
const { generateKnowledgeBase: generateKnowledgeBase2 } = await Promise.resolve().then(() => (init_generator(), generator_exports));
|
|
10540
|
+
const { initObservations: initObservations2, getAllObservations: getAllObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
10541
|
+
const { initMiniSkillStore: initMiniSkillStore2, getMiniSkillStore: getMiniSkillStore2 } = await Promise.resolve().then(() => (init_mini_skill_store(), mini_skill_store_exports));
|
|
10542
|
+
await initObservations2(effectiveDataDir);
|
|
10543
|
+
await initMiniSkillStore2(effectiveDataDir);
|
|
10544
|
+
const allObs = getAllObservations2();
|
|
10545
|
+
const skills = await getMiniSkillStore2().loadByProject(effectiveProjectId);
|
|
10546
|
+
const overview = generateKnowledgeBase2({
|
|
10547
|
+
projectId: effectiveProjectId,
|
|
10548
|
+
observations: allObs,
|
|
10549
|
+
miniSkills: skills
|
|
10550
|
+
});
|
|
10551
|
+
sendJson(res, overview);
|
|
10552
|
+
break;
|
|
10553
|
+
}
|
|
10554
|
+
case "/knowledge-graph": {
|
|
10555
|
+
const { generateKnowledgeGraph: generateKnowledgeGraph2 } = await Promise.resolve().then(() => (init_knowledge_graph(), knowledge_graph_exports));
|
|
10556
|
+
const { initObservations: initObservations2, getAllObservations: getAllObservations2 } = await Promise.resolve().then(() => (init_observations(), observations_exports));
|
|
10557
|
+
const { initMiniSkillStore: initMiniSkillStore2, getMiniSkillStore: getMiniSkillStore2 } = await Promise.resolve().then(() => (init_mini_skill_store(), mini_skill_store_exports));
|
|
10558
|
+
const { initGraphStore: initGraphStore2, getGraphStore: getGraphStore2 } = await Promise.resolve().then(() => (init_graph_store(), graph_store_exports));
|
|
10559
|
+
await initObservations2(effectiveDataDir);
|
|
10560
|
+
await initMiniSkillStore2(effectiveDataDir);
|
|
10561
|
+
await initGraphStore2(effectiveDataDir);
|
|
10562
|
+
const allObs = getAllObservations2();
|
|
10563
|
+
const skills = await getMiniSkillStore2().loadByProject(effectiveProjectId);
|
|
10564
|
+
const fullGraph = { entities: getGraphStore2().loadEntities(), relations: getGraphStore2().loadRelations() };
|
|
10565
|
+
const graphObs = await getObservationStore().loadAll();
|
|
10566
|
+
const projectEntityNames = new Set(
|
|
10567
|
+
graphObs.filter((o) => o.projectId === effectiveProjectId && (o.status ?? "active") === "active" && o.entityName).map((o) => o.entityName)
|
|
10568
|
+
);
|
|
10569
|
+
const scopedEntities = fullGraph.entities.filter((e) => projectEntityNames.has(e.name));
|
|
10570
|
+
const scopedEntityNameSet = new Set(scopedEntities.map((e) => e.name));
|
|
10571
|
+
const scopedRelations = fullGraph.relations.filter((r) => scopedEntityNameSet.has(r.from) && scopedEntityNameSet.has(r.to));
|
|
10572
|
+
const graph = generateKnowledgeGraph2({
|
|
10573
|
+
projectId: effectiveProjectId,
|
|
10574
|
+
observations: allObs,
|
|
10575
|
+
miniSkills: skills,
|
|
10576
|
+
graphEntities: scopedEntities,
|
|
10577
|
+
graphRelations: scopedRelations
|
|
10578
|
+
});
|
|
10579
|
+
sendJson(res, graph);
|
|
10580
|
+
break;
|
|
10581
|
+
}
|
|
10030
10582
|
case "/config": {
|
|
10031
10583
|
const os4 = await import("os");
|
|
10032
10584
|
const { existsSync: existsSync10 } = await import("fs");
|
|
@@ -10263,13 +10815,20 @@ async function serveStatic(req, res, staticDir) {
|
|
|
10263
10815
|
const ext = path14.extname(filePath);
|
|
10264
10816
|
res.writeHead(200, {
|
|
10265
10817
|
"Content-Type": MIME_TYPES[ext] || "application/octet-stream",
|
|
10266
|
-
"Cache-Control": "no-cache"
|
|
10818
|
+
"Cache-Control": "no-store, no-cache, must-revalidate, max-age=0",
|
|
10819
|
+
"Pragma": "no-cache",
|
|
10820
|
+
"Expires": "0"
|
|
10267
10821
|
});
|
|
10268
10822
|
res.end(data);
|
|
10269
10823
|
} catch {
|
|
10270
10824
|
try {
|
|
10271
10825
|
const indexData = await fs12.readFile(path14.join(staticDir, "index.html"));
|
|
10272
|
-
res.writeHead(200, {
|
|
10826
|
+
res.writeHead(200, {
|
|
10827
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
10828
|
+
"Cache-Control": "no-store, no-cache, must-revalidate, max-age=0",
|
|
10829
|
+
"Pragma": "no-cache",
|
|
10830
|
+
"Expires": "0"
|
|
10831
|
+
});
|
|
10273
10832
|
res.end(indexData);
|
|
10274
10833
|
} catch {
|
|
10275
10834
|
sendError(res, "Not found", 404);
|
|
@@ -11740,6 +12299,8 @@ import { spawnSync } from 'node:child_process';
|
|
|
11740
12299
|
export const MemorixPlugin = async ({ project, client, $, directory, worktree }) => {
|
|
11741
12300
|
// Generate a stable session ID for this plugin lifetime
|
|
11742
12301
|
const sessionId = \`opencode-\${Date.now().toString(36)}-\${Math.random().toString(36).slice(2, 8)}\`;
|
|
12302
|
+
let pendingAssistantResponse = null;
|
|
12303
|
+
let lastDeliveredAssistantKey = '';
|
|
11743
12304
|
|
|
11744
12305
|
/**
|
|
11745
12306
|
* Send event JSON to \`memorix hook\` via child_process.spawnSync.
|
|
@@ -11772,6 +12333,33 @@ export const MemorixPlugin = async ({ project, client, $, directory, worktree })
|
|
|
11772
12333
|
}
|
|
11773
12334
|
}
|
|
11774
12335
|
|
|
12336
|
+
function extractMessageInfo(input) {
|
|
12337
|
+
if (input && typeof input === 'object') {
|
|
12338
|
+
if (input.info && typeof input.info === 'object') return input.info;
|
|
12339
|
+
if (input.message && typeof input.message === 'object') return input.message;
|
|
12340
|
+
if (input.properties && typeof input.properties === 'object') {
|
|
12341
|
+
if (input.properties.info && typeof input.properties.info === 'object') return input.properties.info;
|
|
12342
|
+
if (input.properties.message && typeof input.properties.message === 'object') return input.properties.message;
|
|
12343
|
+
}
|
|
12344
|
+
}
|
|
12345
|
+
return input;
|
|
12346
|
+
}
|
|
12347
|
+
|
|
12348
|
+
function extractMessageText(message) {
|
|
12349
|
+
if (!message || typeof message !== 'object') return '';
|
|
12350
|
+
if (typeof message.content === 'string') return message.content.trim();
|
|
12351
|
+
const parts = Array.isArray(message.parts) ? message.parts : [];
|
|
12352
|
+
return parts
|
|
12353
|
+
.map((part) => {
|
|
12354
|
+
if (!part || typeof part !== 'object') return '';
|
|
12355
|
+
if (typeof part.text === 'string') return part.text;
|
|
12356
|
+
if (typeof part.content === 'string') return part.content;
|
|
12357
|
+
return '';
|
|
12358
|
+
})
|
|
12359
|
+
.join('')
|
|
12360
|
+
.trim();
|
|
12361
|
+
}
|
|
12362
|
+
|
|
11775
12363
|
return {
|
|
11776
12364
|
/** Session created \u2014 record session start */
|
|
11777
12365
|
'session.created': async ({ session }) => {
|
|
@@ -11784,6 +12372,21 @@ export const MemorixPlugin = async ({ project, client, $, directory, worktree })
|
|
|
11784
12372
|
|
|
11785
12373
|
/** Session idle \u2014 record session end */
|
|
11786
12374
|
'session.idle': async ({ session }) => {
|
|
12375
|
+
if (pendingAssistantResponse?.text) {
|
|
12376
|
+
const deliveryKey = pendingAssistantResponse.id
|
|
12377
|
+
? \`\${pendingAssistantResponse.id}:\${pendingAssistantResponse.text}\`
|
|
12378
|
+
: pendingAssistantResponse.text;
|
|
12379
|
+
if (deliveryKey !== lastDeliveredAssistantKey) {
|
|
12380
|
+
runHook({
|
|
12381
|
+
agent: 'opencode',
|
|
12382
|
+
hook_event_name: 'message.updated',
|
|
12383
|
+
ai_response: pendingAssistantResponse.text,
|
|
12384
|
+
message_id: pendingAssistantResponse.id,
|
|
12385
|
+
cwd: directory,
|
|
12386
|
+
});
|
|
12387
|
+
lastDeliveredAssistantKey = deliveryKey;
|
|
12388
|
+
}
|
|
12389
|
+
}
|
|
11787
12390
|
runHook({
|
|
11788
12391
|
agent: 'opencode',
|
|
11789
12392
|
hook_event_name: 'session.idle',
|
|
@@ -11812,6 +12415,19 @@ export const MemorixPlugin = async ({ project, client, $, directory, worktree })
|
|
|
11812
12415
|
});
|
|
11813
12416
|
},
|
|
11814
12417
|
|
|
12418
|
+
/** Message updated \u2014 cache the latest assistant response until session.idle */
|
|
12419
|
+
'message.updated': async (input, output) => {
|
|
12420
|
+
const message = extractMessageInfo(input);
|
|
12421
|
+
const role = message?.role ?? input?.role ?? input?.info?.role;
|
|
12422
|
+
if (role !== 'assistant') return;
|
|
12423
|
+
const text = extractMessageText(message);
|
|
12424
|
+
if (!text) return;
|
|
12425
|
+
pendingAssistantResponse = {
|
|
12426
|
+
id: message?.id ?? input?.id ?? input?.messageID,
|
|
12427
|
+
text,
|
|
12428
|
+
};
|
|
12429
|
+
},
|
|
12430
|
+
|
|
11815
12431
|
/** Session compacted \u2014 record post-compact event */
|
|
11816
12432
|
'session.compacted': async ({ session }) => {
|
|
11817
12433
|
runHook({
|
|
@@ -12061,7 +12677,7 @@ async function installHooks(agent, projectRoot, global = false) {
|
|
|
12061
12677
|
return {
|
|
12062
12678
|
agent,
|
|
12063
12679
|
configPath: pluginPath,
|
|
12064
|
-
events: ["session_start", "session_end", "post_tool", "post_edit", "post_compact", "post_command"],
|
|
12680
|
+
events: ["session_start", "session_end", "post_tool", "post_edit", "post_compact", "post_command", "post_response"],
|
|
12065
12681
|
generated: { note: "OpenCode plugin installed at " + pluginPath }
|
|
12066
12682
|
};
|
|
12067
12683
|
}
|
|
@@ -12519,7 +13135,7 @@ var init_installers = __esm({
|
|
|
12519
13135
|
"src/hooks/installers/index.ts"() {
|
|
12520
13136
|
"use strict";
|
|
12521
13137
|
init_esm_shims();
|
|
12522
|
-
OPENCODE_PLUGIN_VERSION =
|
|
13138
|
+
OPENCODE_PLUGIN_VERSION = 6;
|
|
12523
13139
|
}
|
|
12524
13140
|
});
|
|
12525
13141
|
|
|
@@ -12961,7 +13577,8 @@ function getTypeIcon(type) {
|
|
|
12961
13577
|
"why-it-exists": "[WHY]",
|
|
12962
13578
|
"decision": "[DECISION]",
|
|
12963
13579
|
"trade-off": "[TRADEOFF]",
|
|
12964
|
-
"reasoning": "[REASONING]"
|
|
13580
|
+
"reasoning": "[REASONING]",
|
|
13581
|
+
"probe": "[PROBE]"
|
|
12965
13582
|
};
|
|
12966
13583
|
return icons[type] ?? "[UNKNOWN]";
|
|
12967
13584
|
}
|
|
@@ -15460,7 +16077,8 @@ var OBSERVATION_TYPES = [
|
|
|
15460
16077
|
"discovery",
|
|
15461
16078
|
"why-it-exists",
|
|
15462
16079
|
"decision",
|
|
15463
|
-
"trade-off"
|
|
16080
|
+
"trade-off",
|
|
16081
|
+
"probe"
|
|
15464
16082
|
];
|
|
15465
16083
|
function coerceNumberArray(val) {
|
|
15466
16084
|
if (Array.isArray(val)) return val.map(Number);
|
|
@@ -15689,7 +16307,7 @@ The path should point to a directory containing a .git folder.`
|
|
|
15689
16307
|
};
|
|
15690
16308
|
const server = existingServer ?? new McpServer({
|
|
15691
16309
|
name: "memorix",
|
|
15692
|
-
version: true ? "1.0.
|
|
16310
|
+
version: true ? "1.0.9" : "1.0.1"
|
|
15693
16311
|
});
|
|
15694
16312
|
const originalRegisterTool = server.registerTool.bind(server);
|
|
15695
16313
|
server.registerTool = ((name, ...args) => {
|