@datacore-one/mcp 1.4.1 → 1.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -0
- package/dist/index.js +1510 -279
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -41,7 +41,13 @@ function fullConfig(basePath) {
|
|
|
41
41
|
engramsPath: path.join(basePath, ".datacore", "learning", "engrams.yaml"),
|
|
42
42
|
journalPath: path.join(basePath, "0-personal", "journal"),
|
|
43
43
|
knowledgePath: path.join(basePath, "0-personal", "3-knowledge"),
|
|
44
|
-
packsPath: path.join(basePath, ".datacore", "learning", "packs")
|
|
44
|
+
packsPath: path.join(basePath, ".datacore", "learning", "packs"),
|
|
45
|
+
schemasPath: path.join(basePath, ".datacore", "learning", "schemas.yaml"),
|
|
46
|
+
exchangeInboxPath: path.join(basePath, ".datacore", "learning", "exchange", "inbox"),
|
|
47
|
+
exchangeOutboxPath: path.join(basePath, ".datacore", "learning", "exchange", "outbox"),
|
|
48
|
+
knowledgeSurfacingPath: path.join(basePath, ".datacore", "state", "knowledge-surfacing.yaml"),
|
|
49
|
+
archivePath: path.join(basePath, ".datacore", "learning", "archive"),
|
|
50
|
+
statePath: path.join(basePath, ".datacore", "state")
|
|
45
51
|
};
|
|
46
52
|
}
|
|
47
53
|
function coreConfig(basePath) {
|
|
@@ -51,12 +57,18 @@ function coreConfig(basePath) {
|
|
|
51
57
|
engramsPath: path.join(basePath, "engrams.yaml"),
|
|
52
58
|
journalPath: path.join(basePath, "journal"),
|
|
53
59
|
knowledgePath: path.join(basePath, "knowledge"),
|
|
54
|
-
packsPath: path.join(basePath, "packs")
|
|
60
|
+
packsPath: path.join(basePath, "packs"),
|
|
61
|
+
schemasPath: path.join(basePath, "schemas.yaml"),
|
|
62
|
+
exchangeInboxPath: path.join(basePath, "exchange", "inbox"),
|
|
63
|
+
exchangeOutboxPath: path.join(basePath, "exchange", "outbox"),
|
|
64
|
+
knowledgeSurfacingPath: path.join(basePath, "state", "knowledge-surfacing.yaml"),
|
|
65
|
+
archivePath: path.join(basePath, "archive"),
|
|
66
|
+
statePath: path.join(basePath, "state")
|
|
55
67
|
};
|
|
56
68
|
}
|
|
57
69
|
function initCore(basePath) {
|
|
58
70
|
const isFirstRun2 = !fs.existsSync(path.join(basePath, "engrams.yaml"));
|
|
59
|
-
for (const dir of ["journal", "knowledge", "packs"]) {
|
|
71
|
+
for (const dir of ["journal", "knowledge", "packs", "exchange/inbox", "exchange/outbox", "archive", "state"]) {
|
|
60
72
|
const dirPath = path.join(basePath, dir);
|
|
61
73
|
if (!fs.existsSync(dirPath)) {
|
|
62
74
|
fs.mkdirSync(dirPath, { recursive: true });
|
|
@@ -228,6 +240,21 @@ var ConfigSchema = z.object({
|
|
|
228
240
|
consider_cap: z.number().default(5),
|
|
229
241
|
spread_cap: z.number().default(3),
|
|
230
242
|
spread_budget: z.number().default(480)
|
|
243
|
+
}).default({}),
|
|
244
|
+
co_access: z.object({
|
|
245
|
+
new_strength: z.number().default(0.1),
|
|
246
|
+
increment: z.number().default(0.05),
|
|
247
|
+
max_strength: z.number().default(0.95),
|
|
248
|
+
decay_rate: z.number().default(0.05),
|
|
249
|
+
prune_threshold: z.number().default(0.05)
|
|
250
|
+
}).default({}),
|
|
251
|
+
learning: z.object({
|
|
252
|
+
decay_rate: z.number().default(0.05),
|
|
253
|
+
abstraction_threshold: z.number().default(2),
|
|
254
|
+
legacy_audit_rate: z.number().default(3),
|
|
255
|
+
auto_defer_learning_review: z.boolean().default(false),
|
|
256
|
+
daily_review_max_items: z.number().default(5),
|
|
257
|
+
onboarding_max_items: z.number().default(15)
|
|
231
258
|
}).default({})
|
|
232
259
|
});
|
|
233
260
|
var cachedConfig = null;
|
|
@@ -252,7 +279,7 @@ function getConfig() {
|
|
|
252
279
|
// package.json
|
|
253
280
|
var package_default = {
|
|
254
281
|
name: "@datacore-one/mcp",
|
|
255
|
-
version: "1.
|
|
282
|
+
version: "1.5.1",
|
|
256
283
|
description: "Datacore MCP server \u2014 The Software of You",
|
|
257
284
|
type: "module",
|
|
258
285
|
bin: {
|
|
@@ -350,7 +377,9 @@ var TOOLS = [
|
|
|
350
377
|
dual_coding: z2.object({
|
|
351
378
|
example: z2.string().optional().describe("Concrete example illustrating the engram"),
|
|
352
379
|
analogy: z2.string().optional().describe("Analogy to aid understanding")
|
|
353
|
-
}).optional().describe("Dual coding: example and/or analogy for richer encoding")
|
|
380
|
+
}).optional().describe("Dual coding: example and/or analogy for richer encoding"),
|
|
381
|
+
abstract: z2.string().nullable().optional().describe("Abstract engram ID this was derived from"),
|
|
382
|
+
derived_from: z2.string().nullable().optional().describe("Source engram ID this was derived from")
|
|
354
383
|
})
|
|
355
384
|
},
|
|
356
385
|
{
|
|
@@ -497,6 +526,40 @@ var TOOLS = [
|
|
|
497
526
|
module: z2.string().optional().describe("Module name (omit for all modules)")
|
|
498
527
|
})
|
|
499
528
|
},
|
|
529
|
+
{
|
|
530
|
+
name: "datacore.knowledge.scan",
|
|
531
|
+
description: "Scan zettel knowledge base for engram candidates, or run consolidation pass to find low-RS and duplicate engrams.",
|
|
532
|
+
inputSchema: z2.object({
|
|
533
|
+
action: z2.enum(["scan_zettels", "scan_status", "consolidation_pass"]).describe("Action: scan_zettels | scan_status | consolidation_pass"),
|
|
534
|
+
confirm: z2.boolean().optional().describe("Confirm consolidation execution (default: preview only)")
|
|
535
|
+
})
|
|
536
|
+
},
|
|
537
|
+
{
|
|
538
|
+
name: "datacore.exchange",
|
|
539
|
+
description: "Exchange engrams via Learning Exchange Packets (LEP). Export your public engrams, import from others, or check inbox/outbox status.",
|
|
540
|
+
inputSchema: z2.object({
|
|
541
|
+
action: z2.enum(["export", "import", "status"]).describe("Action: export | import | status"),
|
|
542
|
+
engram_ids: z2.array(z2.string()).optional().describe("Specific engram IDs to export"),
|
|
543
|
+
filter_domain: z2.string().optional().describe("Filter export by domain prefix"),
|
|
544
|
+
path: z2.string().optional().describe("Path to LEP packet file (for import)"),
|
|
545
|
+
sender: z2.string().optional().describe("Sender identity (for export)"),
|
|
546
|
+
confirm: z2.boolean().optional().describe("Confirm import (default: preview only)"),
|
|
547
|
+
fitness_threshold: z2.number().optional().describe("Minimum fitness score for import (default: 0.3)"),
|
|
548
|
+
source_cap_percent: z2.number().optional().describe("Max percentage of engrams from one source (default: 0.20)")
|
|
549
|
+
})
|
|
550
|
+
},
|
|
551
|
+
{
|
|
552
|
+
name: "datacore.schemas",
|
|
553
|
+
description: "Manage knowledge schemas \u2014 detect clusters, activate, archive, merge, split, or migrate legacy relations.",
|
|
554
|
+
inputSchema: z2.object({
|
|
555
|
+
action: z2.enum(["list", "detect", "activate", "archive", "merge", "split", "migrate"]).describe("Action: list | detect | activate | archive | merge | split | migrate"),
|
|
556
|
+
id: z2.string().optional().describe("Schema ID (for activate/archive/merge/split)"),
|
|
557
|
+
target_id: z2.string().optional().describe("Target schema ID (for merge)"),
|
|
558
|
+
member_ids: z2.array(z2.string()).optional().describe("Member engram IDs to extract (for split)"),
|
|
559
|
+
name: z2.string().optional().describe("Name for new schema (for split)"),
|
|
560
|
+
confirm: z2.boolean().optional().describe("Confirm destructive action (for migrate)")
|
|
561
|
+
})
|
|
562
|
+
},
|
|
500
563
|
{
|
|
501
564
|
name: "datacore.resolve",
|
|
502
565
|
description: "Resolve a pending engagement event: reconsolidation (contradiction challenge), discovery (cross-domain insight), or challenge (weekly goal). The agent presents options to the user and calls this with their choice.",
|
|
@@ -616,7 +679,8 @@ var AssociationSchema = z3.object({
|
|
|
616
679
|
target_type: z3.enum(["engram", "document"]),
|
|
617
680
|
target: z3.string(),
|
|
618
681
|
strength: z3.number().min(0).max(0.95),
|
|
619
|
-
type: z3.enum(["semantic", "temporal", "causal", "co_accessed"])
|
|
682
|
+
type: z3.enum(["semantic", "temporal", "causal", "co_accessed"]),
|
|
683
|
+
updated_at: z3.string().optional()
|
|
620
684
|
});
|
|
621
685
|
var DualCodingSchema = z3.object({
|
|
622
686
|
example: z3.string().optional(),
|
|
@@ -643,7 +707,7 @@ var FeedbackSignalsSchema = z3.object({
|
|
|
643
707
|
neutral: z3.number().int().default(0)
|
|
644
708
|
});
|
|
645
709
|
var EngramSchema = z3.object({
|
|
646
|
-
id: z3.string().regex(/^ENG-[A-Za-z0-9-]+$/),
|
|
710
|
+
id: z3.string().regex(/^(ENG|ABS)-[A-Za-z0-9-]+$/),
|
|
647
711
|
version: z3.number().int().min(1),
|
|
648
712
|
status: z3.enum(["active", "dormant", "retired", "candidate"]),
|
|
649
713
|
consolidated: z3.boolean().default(false),
|
|
@@ -824,12 +888,29 @@ function generateEngramId(existingEngrams) {
|
|
|
824
888
|
const padWidth = nextSeq > 999 ? String(nextSeq).length : 3;
|
|
825
889
|
return `${prefix}${String(nextSeq).padStart(padWidth, "0")}`;
|
|
826
890
|
}
|
|
891
|
+
function generateAbstractId(existingEngrams) {
|
|
892
|
+
const now = /* @__PURE__ */ new Date();
|
|
893
|
+
const date = now.toISOString().split("T")[0].replace(/-/g, "").slice(0, 8);
|
|
894
|
+
const prefix = `ABS-${date.slice(0, 4)}-${date.slice(4)}-`;
|
|
895
|
+
let maxSeq = 0;
|
|
896
|
+
for (const e of existingEngrams) {
|
|
897
|
+
if (e.id.startsWith(prefix)) {
|
|
898
|
+
const seq = parseInt(e.id.slice(prefix.length), 10);
|
|
899
|
+
if (seq > maxSeq) maxSeq = seq;
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
const nextSeq = maxSeq + 1;
|
|
903
|
+
const padWidth = nextSeq > 999 ? String(nextSeq).length : 3;
|
|
904
|
+
return `${prefix}${String(nextSeq).padStart(padWidth, "0")}`;
|
|
905
|
+
}
|
|
827
906
|
async function handleLearn(args2, engramsPath, service) {
|
|
828
907
|
const engrams = loadEngrams(engramsPath);
|
|
829
908
|
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
830
909
|
const autoPromote = getConfig().engrams.auto_promote;
|
|
910
|
+
const isAbstract = args2.abstract !== void 0 && args2.abstract !== null;
|
|
911
|
+
const id = isAbstract ? generateAbstractId(engrams) : generateEngramId(engrams);
|
|
831
912
|
const engram = {
|
|
832
|
-
id
|
|
913
|
+
id,
|
|
833
914
|
version: 2,
|
|
834
915
|
status: autoPromote ? "active" : "candidate",
|
|
835
916
|
consolidated: false,
|
|
@@ -856,8 +937,8 @@ async function handleLearn(args2, engramsPath, service) {
|
|
|
856
937
|
last_accessed: today
|
|
857
938
|
},
|
|
858
939
|
pack: null,
|
|
859
|
-
abstract: null,
|
|
860
|
-
derived_from: null
|
|
940
|
+
abstract: args2.abstract ?? null,
|
|
941
|
+
derived_from: args2.derived_from ?? null
|
|
861
942
|
};
|
|
862
943
|
engrams.push(engram);
|
|
863
944
|
saveEngrams(engramsPath, engrams);
|
|
@@ -897,9 +978,9 @@ async function handleLearn(args2, engramsPath, service) {
|
|
|
897
978
|
}
|
|
898
979
|
|
|
899
980
|
// src/tools/inject-tool.ts
|
|
900
|
-
import * as
|
|
981
|
+
import * as fs6 from "fs";
|
|
901
982
|
import * as path4 from "path";
|
|
902
|
-
import * as
|
|
983
|
+
import * as yaml4 from "js-yaml";
|
|
903
984
|
|
|
904
985
|
// src/decay.ts
|
|
905
986
|
var DECAY_RATE = 0.05;
|
|
@@ -911,6 +992,15 @@ function decayedStrength(retrievalStrength, lastAccessed, now) {
|
|
|
911
992
|
const days = Math.max(0, (current.getTime() - last.getTime()) / MS_PER_DAY);
|
|
912
993
|
return Math.max(retrievalStrength * Math.exp(-DECAY_RATE * days), FLOOR);
|
|
913
994
|
}
|
|
995
|
+
function decayedCoAccessStrength(strength, updatedAt, now, decayRate, pruneThreshold) {
|
|
996
|
+
const rate = decayRate ?? DECAY_RATE;
|
|
997
|
+
const threshold = pruneThreshold ?? FLOOR;
|
|
998
|
+
const last = new Date(updatedAt);
|
|
999
|
+
const current = now ?? /* @__PURE__ */ new Date();
|
|
1000
|
+
const days = Math.max(0, (current.getTime() - last.getTime()) / MS_PER_DAY);
|
|
1001
|
+
const decayed = strength * Math.exp(-rate * days);
|
|
1002
|
+
return decayed < threshold ? 0 : decayed;
|
|
1003
|
+
}
|
|
914
1004
|
function engramState(retrievalStrength) {
|
|
915
1005
|
if (retrievalStrength >= 0.5) return "active";
|
|
916
1006
|
if (retrievalStrength >= 0.3) return "fading";
|
|
@@ -949,6 +1039,19 @@ function anchorBoost(engram, taskWords) {
|
|
|
949
1039
|
}
|
|
950
1040
|
return Math.min(boost, 2);
|
|
951
1041
|
}
|
|
1042
|
+
var SCHEMA_BOOST = 2;
|
|
1043
|
+
function schemaBoost(engramId, schemaMap, scoredAboveMin) {
|
|
1044
|
+
const schemas = schemaMap.get(engramId);
|
|
1045
|
+
if (!schemas) return 0;
|
|
1046
|
+
for (const schema of schemas) {
|
|
1047
|
+
for (const memberId of schema.members) {
|
|
1048
|
+
if (memberId !== engramId && scoredAboveMin.has(memberId)) {
|
|
1049
|
+
return SCHEMA_BOOST;
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
return 0;
|
|
1054
|
+
}
|
|
952
1055
|
function flattenRelations(engram) {
|
|
953
1056
|
if (!engram.relations) return [];
|
|
954
1057
|
const associations = [];
|
|
@@ -1055,7 +1158,7 @@ function fillTokenBudget(scored, maxTokens) {
|
|
|
1055
1158
|
}
|
|
1056
1159
|
return { selected: result, tokens_used: tokensUsed };
|
|
1057
1160
|
}
|
|
1058
|
-
function selectAndSpread(ctx, personalEngrams, packs) {
|
|
1161
|
+
function selectAndSpread(ctx, personalEngrams, packs, schemas = []) {
|
|
1059
1162
|
const config = getConfig();
|
|
1060
1163
|
const spreadCap = config.injection?.spread_cap ?? 3;
|
|
1061
1164
|
const spreadBudget = config.injection?.spread_budget ?? 480;
|
|
@@ -1085,15 +1188,33 @@ function selectAndSpread(ctx, personalEngrams, packs) {
|
|
|
1085
1188
|
}
|
|
1086
1189
|
}
|
|
1087
1190
|
}
|
|
1088
|
-
const
|
|
1089
|
-
const
|
|
1090
|
-
for (const e of filtered) {
|
|
1191
|
+
const maxKm = Math.max(...scored.map((e) => e.keyword_match), 1);
|
|
1192
|
+
for (const e of scored) {
|
|
1091
1193
|
e.keyword_match = e.keyword_match / maxKm * 10;
|
|
1092
1194
|
}
|
|
1093
|
-
|
|
1195
|
+
const schemaMap = /* @__PURE__ */ new Map();
|
|
1196
|
+
for (const schema of schemas) {
|
|
1197
|
+
if (schema.status !== "active" && schema.status !== "consolidated") continue;
|
|
1198
|
+
for (const memberId of schema.members) {
|
|
1199
|
+
const existing = schemaMap.get(memberId);
|
|
1200
|
+
if (existing) existing.push(schema);
|
|
1201
|
+
else schemaMap.set(memberId, [schema]);
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
const aBoosts = /* @__PURE__ */ new Map();
|
|
1205
|
+
for (const e of scored) {
|
|
1094
1206
|
const aBoost = anchorBoost(e, promptWords);
|
|
1207
|
+
aBoosts.set(e.id, aBoost);
|
|
1095
1208
|
e.score = e.keyword_match + aBoost;
|
|
1096
1209
|
}
|
|
1210
|
+
const scoredAboveMin = new Set(
|
|
1211
|
+
scored.filter((e) => e.score >= minRelevance).map((e) => e.id)
|
|
1212
|
+
);
|
|
1213
|
+
for (const e of scored) {
|
|
1214
|
+
const sBoost = schemaBoost(e.id, schemaMap, scoredAboveMin);
|
|
1215
|
+
e.score = e.keyword_match + (aBoosts.get(e.id) ?? 0) + sBoost;
|
|
1216
|
+
}
|
|
1217
|
+
const filtered = scored.filter((s) => s.score >= minRelevance);
|
|
1097
1218
|
filtered.sort((a, b) => b.score - a.score);
|
|
1098
1219
|
const { selected: directives, tokens_used: directiveTokens } = fillTokenBudget(filtered, maxTokens);
|
|
1099
1220
|
const directiveIds = new Set(directives.map((e) => e.id));
|
|
@@ -1134,7 +1255,9 @@ function selectAndSpread(ctx, personalEngrams, packs) {
|
|
|
1134
1255
|
if (visited.has(assoc.target)) continue;
|
|
1135
1256
|
const target = engramMap.get(assoc.target);
|
|
1136
1257
|
if (!target || target.status !== "active") continue;
|
|
1137
|
-
const
|
|
1258
|
+
const effectiveStrength = assoc.type === "co_accessed" && assoc.updated_at ? decayedCoAccessStrength(assoc.strength, assoc.updated_at) : assoc.strength;
|
|
1259
|
+
if (effectiveStrength <= 0) continue;
|
|
1260
|
+
const spreadScore = directive.score / maxFirstPass * effectiveStrength;
|
|
1138
1261
|
if (spreadScore < minRelevance * 0.5) continue;
|
|
1139
1262
|
const spreadEngram = {
|
|
1140
1263
|
...target,
|
|
@@ -1165,10 +1288,62 @@ function selectAndSpread(ctx, personalEngrams, packs) {
|
|
|
1165
1288
|
};
|
|
1166
1289
|
}
|
|
1167
1290
|
|
|
1291
|
+
// src/schemas/schema-definition.ts
|
|
1292
|
+
import * as fs5 from "fs";
|
|
1293
|
+
import * as yaml3 from "js-yaml";
|
|
1294
|
+
import { z as z4 } from "zod";
|
|
1295
|
+
var SchemaDefinitionSchema = z4.object({
|
|
1296
|
+
id: z4.string(),
|
|
1297
|
+
name: z4.string(),
|
|
1298
|
+
members: z4.array(z4.string()),
|
|
1299
|
+
confidence: z4.number().min(0).max(1),
|
|
1300
|
+
status: z4.enum(["candidate", "active", "consolidated", "archived"]),
|
|
1301
|
+
shared_anchors: z4.array(z4.string()),
|
|
1302
|
+
created: z4.string(),
|
|
1303
|
+
updated: z4.string()
|
|
1304
|
+
});
|
|
1305
|
+
function loadSchemas(filePath) {
|
|
1306
|
+
if (!fs5.existsSync(filePath)) return [];
|
|
1307
|
+
try {
|
|
1308
|
+
const raw = yaml3.load(fs5.readFileSync(filePath, "utf8"));
|
|
1309
|
+
if (!raw?.schemas || !Array.isArray(raw.schemas)) return [];
|
|
1310
|
+
const valid = [];
|
|
1311
|
+
for (const entry of raw.schemas) {
|
|
1312
|
+
const result = SchemaDefinitionSchema.safeParse(entry);
|
|
1313
|
+
if (result.success) valid.push(result.data);
|
|
1314
|
+
}
|
|
1315
|
+
return valid;
|
|
1316
|
+
} catch {
|
|
1317
|
+
return [];
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
function saveSchemas(filePath, schemas) {
|
|
1321
|
+
const content = yaml3.dump({ schemas }, { lineWidth: 120, noRefs: true, quotingType: '"' });
|
|
1322
|
+
const tmpPath = filePath + ".tmp." + process.pid;
|
|
1323
|
+
fs5.writeFileSync(tmpPath, content);
|
|
1324
|
+
fs5.renameSync(tmpPath, filePath);
|
|
1325
|
+
}
|
|
1326
|
+
function generateSchemaId(existing) {
|
|
1327
|
+
const now = /* @__PURE__ */ new Date();
|
|
1328
|
+
const date = now.toISOString().split("T")[0].replace(/-/g, "").slice(0, 8);
|
|
1329
|
+
const prefix = `SCH-${date.slice(0, 4)}-${date.slice(4)}-`;
|
|
1330
|
+
let maxSeq = 0;
|
|
1331
|
+
for (const s of existing) {
|
|
1332
|
+
if (s.id.startsWith(prefix)) {
|
|
1333
|
+
const seq = parseInt(s.id.slice(prefix.length), 10);
|
|
1334
|
+
if (seq > maxSeq) maxSeq = seq;
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
const nextSeq = maxSeq + 1;
|
|
1338
|
+
const padWidth = nextSeq > 999 ? String(nextSeq).length : 3;
|
|
1339
|
+
return `${prefix}${String(nextSeq).padStart(padWidth, "0")}`;
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1168
1342
|
// src/tools/inject-tool.ts
|
|
1169
1343
|
async function handleInject(args2, paths) {
|
|
1170
1344
|
const personalEngrams = loadEngrams(paths.engramsPath);
|
|
1171
1345
|
const packs = loadAllPacks(paths.packsPath);
|
|
1346
|
+
const schemas = paths.schemasPath ? loadSchemas(paths.schemasPath) : [];
|
|
1172
1347
|
const ctx = {
|
|
1173
1348
|
prompt: args2.prompt,
|
|
1174
1349
|
scope: args2.scope,
|
|
@@ -1176,13 +1351,14 @@ async function handleInject(args2, paths) {
|
|
|
1176
1351
|
maxTokens: args2.max_tokens,
|
|
1177
1352
|
minRelevance: args2.min_relevance
|
|
1178
1353
|
};
|
|
1179
|
-
const result = selectAndSpread(ctx, personalEngrams, packs);
|
|
1354
|
+
const result = selectAndSpread(ctx, personalEngrams, packs, schemas);
|
|
1180
1355
|
const totalCount = result.directives.length + result.consider.length;
|
|
1181
1356
|
if (totalCount === 0) {
|
|
1182
1357
|
return {
|
|
1183
1358
|
text: "",
|
|
1184
1359
|
count: 0,
|
|
1185
1360
|
tokens_used: { directives: 0, consider: 0 },
|
|
1361
|
+
injected_personal_ids: [],
|
|
1186
1362
|
_hints: buildHints({
|
|
1187
1363
|
next: "No engrams matched this task. Use datacore.recall to search all sources, or datacore.learn to record new knowledge.",
|
|
1188
1364
|
related: ["datacore.recall", "datacore.learn"]
|
|
@@ -1202,7 +1378,7 @@ async function handleInject(args2, paths) {
|
|
|
1202
1378
|
lines.push(formatEngram(e, totalCount));
|
|
1203
1379
|
}
|
|
1204
1380
|
}
|
|
1205
|
-
const relatedDocs = paths.basePath ? result.related_documents.filter((doc) =>
|
|
1381
|
+
const relatedDocs = paths.basePath ? result.related_documents.filter((doc) => fs6.existsSync(path4.join(paths.basePath, doc.path))) : result.related_documents;
|
|
1206
1382
|
if (relatedDocs.length > 0) {
|
|
1207
1383
|
lines.push("\n" + formatRelatedDocs(relatedDocs));
|
|
1208
1384
|
}
|
|
@@ -1217,6 +1393,7 @@ async function handleInject(args2, paths) {
|
|
|
1217
1393
|
text: lines.join("\n"),
|
|
1218
1394
|
count: totalCount,
|
|
1219
1395
|
tokens_used: result.tokens_used,
|
|
1396
|
+
injected_personal_ids: injectedIds,
|
|
1220
1397
|
related_documents: relatedDocs.length > 0 ? relatedDocs.length : void 0,
|
|
1221
1398
|
_hints: buildHints({
|
|
1222
1399
|
next: `After task, call datacore.feedback on helpful/unhelpful engrams.${idsList}`,
|
|
@@ -1243,10 +1420,10 @@ function updateUsageTracking(engramsPath, allPersonal, selected) {
|
|
|
1243
1420
|
}
|
|
1244
1421
|
}
|
|
1245
1422
|
function atomicWriteYaml(filePath, data) {
|
|
1246
|
-
const content =
|
|
1423
|
+
const content = yaml4.dump(data, { lineWidth: 120, noRefs: true, quotingType: '"' });
|
|
1247
1424
|
const tmpPath = filePath + ".tmp." + process.pid;
|
|
1248
|
-
|
|
1249
|
-
|
|
1425
|
+
fs6.writeFileSync(tmpPath, content);
|
|
1426
|
+
fs6.renameSync(tmpPath, filePath);
|
|
1250
1427
|
}
|
|
1251
1428
|
function formatEngram(engram, totalCount) {
|
|
1252
1429
|
if (totalCount < 10) {
|
|
@@ -1282,7 +1459,7 @@ function formatRelatedDocs(docs) {
|
|
|
1282
1459
|
}
|
|
1283
1460
|
|
|
1284
1461
|
// src/tools/search.ts
|
|
1285
|
-
import * as
|
|
1462
|
+
import * as fs7 from "fs";
|
|
1286
1463
|
import * as path5 from "path";
|
|
1287
1464
|
var CONTENT_CACHE_MAX = 500;
|
|
1288
1465
|
var contentCache = /* @__PURE__ */ new Map();
|
|
@@ -1290,7 +1467,7 @@ function getCachedContent(filePath) {
|
|
|
1290
1467
|
const entry = contentCache.get(filePath);
|
|
1291
1468
|
if (!entry) return null;
|
|
1292
1469
|
try {
|
|
1293
|
-
const stat =
|
|
1470
|
+
const stat = fs7.statSync(filePath);
|
|
1294
1471
|
if (stat.mtimeMs === entry.mtime) return entry.content;
|
|
1295
1472
|
} catch {
|
|
1296
1473
|
}
|
|
@@ -1299,7 +1476,7 @@ function getCachedContent(filePath) {
|
|
|
1299
1476
|
}
|
|
1300
1477
|
function setCachedContent(filePath, content) {
|
|
1301
1478
|
try {
|
|
1302
|
-
const mtime =
|
|
1479
|
+
const mtime = fs7.statSync(filePath).mtimeMs;
|
|
1303
1480
|
if (contentCache.size >= CONTENT_CACHE_MAX) {
|
|
1304
1481
|
const firstKey = contentCache.keys().next().value;
|
|
1305
1482
|
if (firstKey) contentCache.delete(firstKey);
|
|
@@ -1336,13 +1513,13 @@ async function keywordSearch(args2, paths) {
|
|
|
1336
1513
|
return { results: results.slice(0, limit), method: "keyword" };
|
|
1337
1514
|
}
|
|
1338
1515
|
function searchDir(dirPath, query) {
|
|
1339
|
-
if (!
|
|
1516
|
+
if (!fs7.existsSync(dirPath)) return [];
|
|
1340
1517
|
const results = [];
|
|
1341
1518
|
const queryLower = query.toLowerCase();
|
|
1342
1519
|
for (const file of walkDir(dirPath)) {
|
|
1343
1520
|
if (!file.endsWith(".md")) continue;
|
|
1344
1521
|
const content = getCachedContent(file) ?? (() => {
|
|
1345
|
-
const c =
|
|
1522
|
+
const c = fs7.readFileSync(file, "utf8");
|
|
1346
1523
|
setCachedContent(file, c);
|
|
1347
1524
|
return c;
|
|
1348
1525
|
})();
|
|
@@ -1358,7 +1535,7 @@ function searchDir(dirPath, query) {
|
|
|
1358
1535
|
}
|
|
1359
1536
|
function walkDir(dir) {
|
|
1360
1537
|
const files = [];
|
|
1361
|
-
for (const entry of
|
|
1538
|
+
for (const entry of fs7.readdirSync(dir, { withFileTypes: true })) {
|
|
1362
1539
|
const fullPath = path5.join(dir, entry.name);
|
|
1363
1540
|
if (entry.isDirectory()) files.push(...walkDir(fullPath));
|
|
1364
1541
|
else files.push(fullPath);
|
|
@@ -1396,7 +1573,7 @@ function extractDate(filePath) {
|
|
|
1396
1573
|
}
|
|
1397
1574
|
|
|
1398
1575
|
// src/tools/ingest.ts
|
|
1399
|
-
import * as
|
|
1576
|
+
import * as fs8 from "fs";
|
|
1400
1577
|
import * as path6 from "path";
|
|
1401
1578
|
async function handleIngest(args2, paths) {
|
|
1402
1579
|
const contentError = validateContent(args2.content);
|
|
@@ -1409,7 +1586,7 @@ async function handleIngest(args2, paths) {
|
|
|
1409
1586
|
const slug = (args2.title ?? "ingested").toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 50);
|
|
1410
1587
|
const fileName = `${timestamp}-${slug}.md`;
|
|
1411
1588
|
const filePath = path6.join(paths.knowledgePath, fileName);
|
|
1412
|
-
|
|
1589
|
+
fs8.mkdirSync(path6.dirname(filePath), { recursive: true });
|
|
1413
1590
|
const frontmatter = `---
|
|
1414
1591
|
title: "${args2.title ?? "Ingested Note"}"
|
|
1415
1592
|
created: "${(/* @__PURE__ */ new Date()).toISOString()}"
|
|
@@ -1420,7 +1597,7 @@ type: ingested
|
|
|
1420
1597
|
const tagLine = args2.tags?.length ? `
|
|
1421
1598
|
${args2.tags.map((t) => `#${t}`).join(" ")}
|
|
1422
1599
|
` : "";
|
|
1423
|
-
|
|
1600
|
+
fs8.writeFileSync(filePath, `${frontmatter}${args2.content}
|
|
1424
1601
|
${tagLine}`);
|
|
1425
1602
|
const suggestions = extractEngramSuggestions(args2.content);
|
|
1426
1603
|
return {
|
|
@@ -1454,11 +1631,11 @@ function extractEngramSuggestions(content) {
|
|
|
1454
1631
|
}
|
|
1455
1632
|
|
|
1456
1633
|
// src/tools/status.ts
|
|
1457
|
-
import * as
|
|
1634
|
+
import * as fs10 from "fs";
|
|
1458
1635
|
import * as path8 from "path";
|
|
1459
1636
|
|
|
1460
1637
|
// src/trust.ts
|
|
1461
|
-
import * as
|
|
1638
|
+
import * as fs9 from "fs";
|
|
1462
1639
|
import * as path7 from "path";
|
|
1463
1640
|
import * as crypto from "crypto";
|
|
1464
1641
|
function computePackChecksum(packDir) {
|
|
@@ -1467,8 +1644,8 @@ function computePackChecksum(packDir) {
|
|
|
1467
1644
|
let hasContent = false;
|
|
1468
1645
|
for (const file of files) {
|
|
1469
1646
|
const filePath = path7.join(packDir, file);
|
|
1470
|
-
if (
|
|
1471
|
-
hash.update(
|
|
1647
|
+
if (fs9.existsSync(filePath)) {
|
|
1648
|
+
hash.update(fs9.readFileSync(filePath));
|
|
1472
1649
|
hasContent = true;
|
|
1473
1650
|
}
|
|
1474
1651
|
}
|
|
@@ -1480,185 +1657,185 @@ function verifyPackChecksum(packDir, expected) {
|
|
|
1480
1657
|
}
|
|
1481
1658
|
|
|
1482
1659
|
// src/engagement/types.ts
|
|
1483
|
-
import { z as
|
|
1484
|
-
var IdentitySchema =
|
|
1485
|
-
mode:
|
|
1486
|
-
pseudonym:
|
|
1487
|
-
erc8004_address:
|
|
1488
|
-
erc8004_registered:
|
|
1660
|
+
import { z as z5 } from "zod";
|
|
1661
|
+
var IdentitySchema = z5.object({
|
|
1662
|
+
mode: z5.enum(["private", "anonymous", "verified"]).default("private"),
|
|
1663
|
+
pseudonym: z5.string().nullable().default(null),
|
|
1664
|
+
erc8004_address: z5.string().nullable().default(null),
|
|
1665
|
+
erc8004_registered: z5.boolean().default(false)
|
|
1489
1666
|
});
|
|
1490
|
-
var XPHistoryEntrySchema =
|
|
1491
|
-
date:
|
|
1492
|
-
earned:
|
|
1493
|
-
base_earned:
|
|
1494
|
-
multiplier:
|
|
1495
|
-
actions:
|
|
1667
|
+
var XPHistoryEntrySchema = z5.object({
|
|
1668
|
+
date: z5.string(),
|
|
1669
|
+
earned: z5.number(),
|
|
1670
|
+
base_earned: z5.number(),
|
|
1671
|
+
multiplier: z5.number(),
|
|
1672
|
+
actions: z5.array(z5.string())
|
|
1496
1673
|
});
|
|
1497
|
-
var TierHistoryEntrySchema =
|
|
1498
|
-
tier:
|
|
1499
|
-
date:
|
|
1674
|
+
var TierHistoryEntrySchema = z5.object({
|
|
1675
|
+
tier: z5.string(),
|
|
1676
|
+
date: z5.string()
|
|
1500
1677
|
});
|
|
1501
|
-
var MultiplierEntrySchema =
|
|
1502
|
-
type:
|
|
1503
|
-
factor:
|
|
1504
|
-
since:
|
|
1678
|
+
var MultiplierEntrySchema = z5.object({
|
|
1679
|
+
type: z5.string(),
|
|
1680
|
+
factor: z5.number(),
|
|
1681
|
+
since: z5.string()
|
|
1505
1682
|
});
|
|
1506
|
-
var ChallengeSchema =
|
|
1507
|
-
id:
|
|
1508
|
-
type:
|
|
1509
|
-
tier:
|
|
1510
|
-
description:
|
|
1511
|
-
criteria:
|
|
1512
|
-
metric:
|
|
1513
|
-
target_delta:
|
|
1683
|
+
var ChallengeSchema = z5.object({
|
|
1684
|
+
id: z5.string(),
|
|
1685
|
+
type: z5.string(),
|
|
1686
|
+
tier: z5.string(),
|
|
1687
|
+
description: z5.string(),
|
|
1688
|
+
criteria: z5.object({
|
|
1689
|
+
metric: z5.string(),
|
|
1690
|
+
target_delta: z5.number()
|
|
1514
1691
|
}),
|
|
1515
|
-
baseline_stats:
|
|
1516
|
-
bonus_xp:
|
|
1517
|
-
started_at:
|
|
1518
|
-
expires_at:
|
|
1692
|
+
baseline_stats: z5.record(z5.number()),
|
|
1693
|
+
bonus_xp: z5.number(),
|
|
1694
|
+
started_at: z5.string(),
|
|
1695
|
+
expires_at: z5.string()
|
|
1519
1696
|
});
|
|
1520
|
-
var ChallengeHistorySchema =
|
|
1521
|
-
type:
|
|
1522
|
-
tier:
|
|
1523
|
-
completed:
|
|
1524
|
-
date:
|
|
1697
|
+
var ChallengeHistorySchema = z5.object({
|
|
1698
|
+
type: z5.string(),
|
|
1699
|
+
tier: z5.string(),
|
|
1700
|
+
completed: z5.boolean(),
|
|
1701
|
+
date: z5.string()
|
|
1525
1702
|
});
|
|
1526
|
-
var ReconsolidationPendingSchema =
|
|
1527
|
-
engram_id:
|
|
1528
|
-
contradicting_id:
|
|
1529
|
-
statement:
|
|
1530
|
-
contradiction:
|
|
1531
|
-
evidence_strength:
|
|
1532
|
-
confidence:
|
|
1533
|
-
detected_at:
|
|
1534
|
-
expires_at:
|
|
1703
|
+
var ReconsolidationPendingSchema = z5.object({
|
|
1704
|
+
engram_id: z5.string(),
|
|
1705
|
+
contradicting_id: z5.string(),
|
|
1706
|
+
statement: z5.string(),
|
|
1707
|
+
contradiction: z5.string(),
|
|
1708
|
+
evidence_strength: z5.enum(["weak", "moderate", "strong"]),
|
|
1709
|
+
confidence: z5.number(),
|
|
1710
|
+
detected_at: z5.string(),
|
|
1711
|
+
expires_at: z5.string()
|
|
1535
1712
|
});
|
|
1536
|
-
var DiscoverySchema =
|
|
1537
|
-
id:
|
|
1538
|
-
engram_a:
|
|
1539
|
-
engram_b:
|
|
1540
|
-
connection:
|
|
1541
|
-
offered_at:
|
|
1713
|
+
var DiscoverySchema = z5.object({
|
|
1714
|
+
id: z5.string(),
|
|
1715
|
+
engram_a: z5.object({ id: z5.string(), domain: z5.string(), statement: z5.string() }),
|
|
1716
|
+
engram_b: z5.object({ id: z5.string(), domain: z5.string(), statement: z5.string() }),
|
|
1717
|
+
connection: z5.string(),
|
|
1718
|
+
offered_at: z5.string()
|
|
1542
1719
|
});
|
|
1543
|
-
var EngagementProfileSchema =
|
|
1544
|
-
version:
|
|
1720
|
+
var EngagementProfileSchema = z5.object({
|
|
1721
|
+
version: z5.literal(4),
|
|
1545
1722
|
identity: IdentitySchema.default({}),
|
|
1546
|
-
xp:
|
|
1547
|
-
total:
|
|
1548
|
-
this_week:
|
|
1549
|
-
history:
|
|
1723
|
+
xp: z5.object({
|
|
1724
|
+
total: z5.number().default(0),
|
|
1725
|
+
this_week: z5.number().default(0),
|
|
1726
|
+
history: z5.array(XPHistoryEntrySchema).default([])
|
|
1550
1727
|
}).default({}),
|
|
1551
|
-
tier:
|
|
1552
|
-
current:
|
|
1553
|
-
achieved_at:
|
|
1554
|
-
history:
|
|
1728
|
+
tier: z5.object({
|
|
1729
|
+
current: z5.string().default("Seed"),
|
|
1730
|
+
achieved_at: z5.string().nullable().default(null),
|
|
1731
|
+
history: z5.array(TierHistoryEntrySchema).default([])
|
|
1555
1732
|
}).default({}),
|
|
1556
|
-
multipliers:
|
|
1557
|
-
active:
|
|
1558
|
-
effective:
|
|
1733
|
+
multipliers: z5.object({
|
|
1734
|
+
active: z5.array(MultiplierEntrySchema).default([]),
|
|
1735
|
+
effective: z5.number().default(1)
|
|
1559
1736
|
}).default({}),
|
|
1560
|
-
consistency:
|
|
1561
|
-
active_days_30:
|
|
1562
|
-
best_run:
|
|
1563
|
-
last_active:
|
|
1737
|
+
consistency: z5.object({
|
|
1738
|
+
active_days_30: z5.number().default(0),
|
|
1739
|
+
best_run: z5.number().default(0),
|
|
1740
|
+
last_active: z5.string().nullable().default(null)
|
|
1564
1741
|
}).default({}),
|
|
1565
|
-
challenges:
|
|
1742
|
+
challenges: z5.object({
|
|
1566
1743
|
active: ChallengeSchema.nullable().default(null),
|
|
1567
|
-
completed:
|
|
1568
|
-
dismissed:
|
|
1569
|
-
graduated:
|
|
1570
|
-
history:
|
|
1744
|
+
completed: z5.number().default(0),
|
|
1745
|
+
dismissed: z5.number().default(0),
|
|
1746
|
+
graduated: z5.boolean().default(false),
|
|
1747
|
+
history: z5.array(ChallengeHistorySchema).default([])
|
|
1571
1748
|
}).default({}),
|
|
1572
|
-
reconsolidation:
|
|
1573
|
-
pending:
|
|
1574
|
-
total_resolved:
|
|
1575
|
-
outcomes:
|
|
1576
|
-
defended:
|
|
1577
|
-
revised:
|
|
1578
|
-
retired:
|
|
1579
|
-
dismissed:
|
|
1749
|
+
reconsolidation: z5.object({
|
|
1750
|
+
pending: z5.array(ReconsolidationPendingSchema).default([]),
|
|
1751
|
+
total_resolved: z5.number().default(0),
|
|
1752
|
+
outcomes: z5.object({
|
|
1753
|
+
defended: z5.number().default(0),
|
|
1754
|
+
revised: z5.number().default(0),
|
|
1755
|
+
retired: z5.number().default(0),
|
|
1756
|
+
dismissed: z5.number().default(0)
|
|
1580
1757
|
}).default({}),
|
|
1581
|
-
response_rate:
|
|
1758
|
+
response_rate: z5.number().default(0)
|
|
1582
1759
|
}).default({}),
|
|
1583
|
-
discoveries:
|
|
1584
|
-
pending:
|
|
1585
|
-
total:
|
|
1586
|
-
last_offered:
|
|
1587
|
-
explored:
|
|
1588
|
-
noted:
|
|
1589
|
-
explore_rate:
|
|
1760
|
+
discoveries: z5.object({
|
|
1761
|
+
pending: z5.array(DiscoverySchema).default([]),
|
|
1762
|
+
total: z5.number().default(0),
|
|
1763
|
+
last_offered: z5.string().nullable().default(null),
|
|
1764
|
+
explored: z5.number().default(0),
|
|
1765
|
+
noted: z5.number().default(0),
|
|
1766
|
+
explore_rate: z5.number().default(0)
|
|
1590
1767
|
}).default({}),
|
|
1591
|
-
ai_performance:
|
|
1592
|
-
total_injections:
|
|
1593
|
-
feedback_count:
|
|
1594
|
-
helpful_ratio:
|
|
1595
|
-
top_engrams:
|
|
1596
|
-
id:
|
|
1597
|
-
injections:
|
|
1598
|
-
positive_ratio:
|
|
1768
|
+
ai_performance: z5.object({
|
|
1769
|
+
total_injections: z5.number().default(0),
|
|
1770
|
+
feedback_count: z5.number().default(0),
|
|
1771
|
+
helpful_ratio: z5.number().default(0),
|
|
1772
|
+
top_engrams: z5.array(z5.object({
|
|
1773
|
+
id: z5.string(),
|
|
1774
|
+
injections: z5.number(),
|
|
1775
|
+
positive_ratio: z5.number()
|
|
1599
1776
|
})).default([]),
|
|
1600
|
-
unused_60d:
|
|
1777
|
+
unused_60d: z5.array(z5.string()).default([])
|
|
1601
1778
|
}).default({}),
|
|
1602
|
-
reputation:
|
|
1603
|
-
score:
|
|
1604
|
-
components:
|
|
1605
|
-
feedback_ratio:
|
|
1606
|
-
stake_amount:
|
|
1607
|
-
tenure_days:
|
|
1608
|
-
reconsolidation_honesty:
|
|
1779
|
+
reputation: z5.object({
|
|
1780
|
+
score: z5.number().default(0),
|
|
1781
|
+
components: z5.object({
|
|
1782
|
+
feedback_ratio: z5.number().default(0),
|
|
1783
|
+
stake_amount: z5.number().default(0),
|
|
1784
|
+
tenure_days: z5.number().default(0),
|
|
1785
|
+
reconsolidation_honesty: z5.number().default(0)
|
|
1609
1786
|
}).default({}),
|
|
1610
|
-
last_calculated:
|
|
1787
|
+
last_calculated: z5.string().nullable().default(null)
|
|
1611
1788
|
}).default({}),
|
|
1612
|
-
leaderboard:
|
|
1613
|
-
mode:
|
|
1614
|
-
display_name:
|
|
1615
|
-
position:
|
|
1789
|
+
leaderboard: z5.object({
|
|
1790
|
+
mode: z5.enum(["private", "anonymous", "verified"]).default("private"),
|
|
1791
|
+
display_name: z5.string().nullable().default(null),
|
|
1792
|
+
position: z5.number().nullable().default(null)
|
|
1616
1793
|
}).default({}),
|
|
1617
|
-
badge:
|
|
1618
|
-
preview_svg:
|
|
1619
|
-
nft_token_id:
|
|
1620
|
-
last_generated:
|
|
1794
|
+
badge: z5.object({
|
|
1795
|
+
preview_svg: z5.string().nullable().default(null),
|
|
1796
|
+
nft_token_id: z5.string().nullable().default(null),
|
|
1797
|
+
last_generated: z5.string().nullable().default(null)
|
|
1621
1798
|
}).default({}),
|
|
1622
|
-
stats:
|
|
1623
|
-
total_engrams_created:
|
|
1624
|
-
total_feedback_given:
|
|
1625
|
-
total_engrams_retired:
|
|
1626
|
-
total_packs_exported:
|
|
1627
|
-
total_feedback_received:
|
|
1628
|
-
feedback_positive_ratio:
|
|
1629
|
-
domains_covered:
|
|
1630
|
-
public_engrams:
|
|
1631
|
-
first_activity:
|
|
1799
|
+
stats: z5.object({
|
|
1800
|
+
total_engrams_created: z5.number().default(0),
|
|
1801
|
+
total_feedback_given: z5.number().default(0),
|
|
1802
|
+
total_engrams_retired: z5.number().default(0),
|
|
1803
|
+
total_packs_exported: z5.number().default(0),
|
|
1804
|
+
total_feedback_received: z5.number().default(0),
|
|
1805
|
+
feedback_positive_ratio: z5.number().default(0),
|
|
1806
|
+
domains_covered: z5.number().default(0),
|
|
1807
|
+
public_engrams: z5.number().default(0),
|
|
1808
|
+
first_activity: z5.string().nullable().default(null)
|
|
1632
1809
|
}).default({})
|
|
1633
1810
|
});
|
|
1634
|
-
var XPEventSchema =
|
|
1635
|
-
action_key:
|
|
1636
|
-
xp_base:
|
|
1637
|
-
multiplier:
|
|
1638
|
-
xp_earned:
|
|
1639
|
-
timestamp:
|
|
1640
|
-
context:
|
|
1811
|
+
var XPEventSchema = z5.object({
|
|
1812
|
+
action_key: z5.string(),
|
|
1813
|
+
xp_base: z5.number(),
|
|
1814
|
+
multiplier: z5.number(),
|
|
1815
|
+
xp_earned: z5.number(),
|
|
1816
|
+
timestamp: z5.string(),
|
|
1817
|
+
context: z5.record(z5.unknown()).optional()
|
|
1641
1818
|
});
|
|
1642
|
-
var XPResultSchema =
|
|
1819
|
+
var XPResultSchema = z5.object({
|
|
1643
1820
|
event: XPEventSchema,
|
|
1644
|
-
tier_change:
|
|
1645
|
-
from:
|
|
1646
|
-
to:
|
|
1647
|
-
message:
|
|
1821
|
+
tier_change: z5.object({
|
|
1822
|
+
from: z5.string(),
|
|
1823
|
+
to: z5.string(),
|
|
1824
|
+
message: z5.string()
|
|
1648
1825
|
}).nullable()
|
|
1649
1826
|
});
|
|
1650
|
-
var XPActionSchema =
|
|
1651
|
-
xp:
|
|
1652
|
-
trigger:
|
|
1653
|
-
condition:
|
|
1654
|
-
daily_limit:
|
|
1655
|
-
cooldown_days:
|
|
1656
|
-
reciprocity_cap:
|
|
1657
|
-
description:
|
|
1827
|
+
var XPActionSchema = z5.object({
|
|
1828
|
+
xp: z5.number(),
|
|
1829
|
+
trigger: z5.string(),
|
|
1830
|
+
condition: z5.string().optional(),
|
|
1831
|
+
daily_limit: z5.number().optional(),
|
|
1832
|
+
cooldown_days: z5.number().optional(),
|
|
1833
|
+
reciprocity_cap: z5.number().optional(),
|
|
1834
|
+
description: z5.string()
|
|
1658
1835
|
});
|
|
1659
|
-
var XPActionRegistrySchema =
|
|
1660
|
-
version:
|
|
1661
|
-
actions:
|
|
1836
|
+
var XPActionRegistrySchema = z5.object({
|
|
1837
|
+
version: z5.number(),
|
|
1838
|
+
actions: z5.record(XPActionSchema)
|
|
1662
1839
|
});
|
|
1663
1840
|
var TIER_THRESHOLDS = [
|
|
1664
1841
|
{ name: "Seed", minXP: 0 },
|
|
@@ -1824,7 +2001,7 @@ async function handleStatus(paths, updateAvailable2, engagementService2) {
|
|
|
1824
2001
|
for (const regPack of packs_default.packs) {
|
|
1825
2002
|
if (!regPack.checksum) continue;
|
|
1826
2003
|
const packDir = path8.join(paths.packsPath, regPack.id);
|
|
1827
|
-
if (!
|
|
2004
|
+
if (!fs10.existsSync(packDir)) continue;
|
|
1828
2005
|
const result = verifyPackChecksum(packDir, regPack.checksum);
|
|
1829
2006
|
packIntegrity.push({ name: regPack.id, valid: result.valid });
|
|
1830
2007
|
}
|
|
@@ -1841,7 +2018,7 @@ async function handleStatus(paths, updateAvailable2, engagementService2) {
|
|
|
1841
2018
|
}
|
|
1842
2019
|
const { date: today } = localDate();
|
|
1843
2020
|
const todayJournal = path8.join(paths.journalPath, `${today}.md`);
|
|
1844
|
-
if (!
|
|
2021
|
+
if (!fs10.existsSync(todayJournal)) {
|
|
1845
2022
|
recommendations.push("No journal entry today. Use datacore.capture to start one.");
|
|
1846
2023
|
}
|
|
1847
2024
|
if (updateAvailable2) {
|
|
@@ -1888,9 +2065,9 @@ async function handleStatus(paths, updateAvailable2, engagementService2) {
|
|
|
1888
2065
|
return statusResult;
|
|
1889
2066
|
}
|
|
1890
2067
|
function countFiles(dir, ext) {
|
|
1891
|
-
if (!
|
|
2068
|
+
if (!fs10.existsSync(dir)) return 0;
|
|
1892
2069
|
let count = 0;
|
|
1893
|
-
for (const entry of
|
|
2070
|
+
for (const entry of fs10.readdirSync(dir, { withFileTypes: true })) {
|
|
1894
2071
|
const fullPath = path8.join(dir, entry.name);
|
|
1895
2072
|
if (entry.isDirectory()) count += countFiles(fullPath, ext);
|
|
1896
2073
|
else if (entry.name.endsWith(ext)) count++;
|
|
@@ -1898,12 +2075,12 @@ function countFiles(dir, ext) {
|
|
|
1898
2075
|
return count;
|
|
1899
2076
|
}
|
|
1900
2077
|
function countDirs(dir) {
|
|
1901
|
-
if (!
|
|
1902
|
-
return
|
|
2078
|
+
if (!fs10.existsSync(dir)) return 0;
|
|
2079
|
+
return fs10.readdirSync(dir, { withFileTypes: true }).filter((d) => d.isDirectory()).length;
|
|
1903
2080
|
}
|
|
1904
2081
|
|
|
1905
2082
|
// src/tools/discover.ts
|
|
1906
|
-
import * as
|
|
2083
|
+
import * as fs11 from "fs";
|
|
1907
2084
|
import * as path9 from "path";
|
|
1908
2085
|
function handleDiscover(args2, packsDir) {
|
|
1909
2086
|
const bundledDir = path9.join(
|
|
@@ -1913,17 +2090,17 @@ function handleDiscover(args2, packsDir) {
|
|
|
1913
2090
|
);
|
|
1914
2091
|
let packs = packs_default.packs.map((p) => {
|
|
1915
2092
|
const localDir = path9.join(packsDir, p.id);
|
|
1916
|
-
const installed =
|
|
2093
|
+
const installed = fs11.existsSync(path9.join(localDir, "SKILL.md"));
|
|
1917
2094
|
let installedVersion;
|
|
1918
2095
|
if (installed) {
|
|
1919
2096
|
try {
|
|
1920
|
-
const content =
|
|
2097
|
+
const content = fs11.readFileSync(path9.join(localDir, "SKILL.md"), "utf8");
|
|
1921
2098
|
const match = content.match(/version:\s*["']?([^"'\n]+)/);
|
|
1922
2099
|
installedVersion = match?.[1];
|
|
1923
2100
|
} catch {
|
|
1924
2101
|
}
|
|
1925
2102
|
}
|
|
1926
|
-
const bundled =
|
|
2103
|
+
const bundled = fs11.existsSync(path9.join(bundledDir, p.id, "SKILL.md"));
|
|
1927
2104
|
return {
|
|
1928
2105
|
...p,
|
|
1929
2106
|
installed,
|
|
@@ -1954,9 +2131,9 @@ function handleDiscover(args2, packsDir) {
|
|
|
1954
2131
|
}
|
|
1955
2132
|
|
|
1956
2133
|
// src/tools/install.ts
|
|
1957
|
-
import * as
|
|
2134
|
+
import * as fs12 from "fs";
|
|
1958
2135
|
import * as path10 from "path";
|
|
1959
|
-
import * as
|
|
2136
|
+
import * as yaml5 from "js-yaml";
|
|
1960
2137
|
import * as os2 from "os";
|
|
1961
2138
|
import { execSync } from "child_process";
|
|
1962
2139
|
async function handleInstall(args2, packsDir) {
|
|
@@ -1971,33 +2148,33 @@ async function handleInstall(args2, packsDir) {
|
|
|
1971
2148
|
srcDir = resolved.path;
|
|
1972
2149
|
}
|
|
1973
2150
|
const skillPath = path10.join(srcDir, "SKILL.md");
|
|
1974
|
-
if (!
|
|
2151
|
+
if (!fs12.existsSync(skillPath)) {
|
|
1975
2152
|
return { success: false, error: "No SKILL.md found in source directory" };
|
|
1976
2153
|
}
|
|
1977
|
-
const skillContent =
|
|
2154
|
+
const skillContent = fs12.readFileSync(skillPath, "utf8");
|
|
1978
2155
|
const frontmatterMatch = skillContent.match(/^---\n([\s\S]*?)\n---/);
|
|
1979
2156
|
if (!frontmatterMatch) {
|
|
1980
2157
|
return { success: false, error: "No YAML frontmatter in SKILL.md" };
|
|
1981
2158
|
}
|
|
1982
|
-
const manifest =
|
|
2159
|
+
const manifest = yaml5.load(frontmatterMatch[1]);
|
|
1983
2160
|
const packId = manifest?.["x-datacore"]?.id;
|
|
1984
2161
|
const newVersion = manifest?.version;
|
|
1985
2162
|
if (!packId) {
|
|
1986
2163
|
return { success: false, error: "Missing x-datacore.id in SKILL.md frontmatter" };
|
|
1987
2164
|
}
|
|
1988
2165
|
const destDir = path10.join(packsDir, packId);
|
|
1989
|
-
if (
|
|
1990
|
-
const existingContent =
|
|
2166
|
+
if (fs12.existsSync(path10.join(destDir, "SKILL.md"))) {
|
|
2167
|
+
const existingContent = fs12.readFileSync(path10.join(destDir, "SKILL.md"), "utf8");
|
|
1991
2168
|
const existingMatch = existingContent.match(/version:\s*["']?([^"'\n]+)/);
|
|
1992
2169
|
const existingVersion = existingMatch?.[1];
|
|
1993
2170
|
if (existingVersion === newVersion) {
|
|
1994
2171
|
return { success: true, pack_id: packId, already_current: true };
|
|
1995
2172
|
}
|
|
1996
|
-
|
|
1997
|
-
|
|
2173
|
+
fs12.rmSync(destDir, { recursive: true, force: true });
|
|
2174
|
+
fs12.cpSync(srcDir, destDir, { recursive: true });
|
|
1998
2175
|
return { success: true, pack_id: packId, upgraded: true };
|
|
1999
2176
|
}
|
|
2000
|
-
|
|
2177
|
+
fs12.cpSync(srcDir, destDir, { recursive: true });
|
|
2001
2178
|
const checksumVerified = verifyInstalledChecksum(packId, destDir);
|
|
2002
2179
|
return { success: true, pack_id: packId, checksum_verified: checksumVerified ?? void 0 };
|
|
2003
2180
|
}
|
|
@@ -2008,15 +2185,15 @@ function verifyInstalledChecksum(packId, destDir) {
|
|
|
2008
2185
|
return result.valid;
|
|
2009
2186
|
}
|
|
2010
2187
|
async function downloadPack(url) {
|
|
2011
|
-
const tmpDir =
|
|
2188
|
+
const tmpDir = fs12.mkdtempSync(path10.join(os2.tmpdir(), "datacore-pack-"));
|
|
2012
2189
|
try {
|
|
2013
2190
|
const res = await fetch(url, { signal: AbortSignal.timeout(3e4) });
|
|
2014
2191
|
if (!res.ok) return { error: `Download failed: HTTP ${res.status}` };
|
|
2015
2192
|
const buffer = Buffer.from(await res.arrayBuffer());
|
|
2016
2193
|
const archivePath = path10.join(tmpDir, "pack.tar.gz");
|
|
2017
|
-
|
|
2194
|
+
fs12.writeFileSync(archivePath, buffer);
|
|
2018
2195
|
const extractDir = path10.join(tmpDir, "extracted");
|
|
2019
|
-
|
|
2196
|
+
fs12.mkdirSync(extractDir);
|
|
2020
2197
|
execSync(`tar xzf ${JSON.stringify(archivePath)} -C ${JSON.stringify(extractDir)}`, { timeout: 1e4 });
|
|
2021
2198
|
const packRoot = findPackRoot(extractDir);
|
|
2022
2199
|
if (!packRoot) return { error: "Downloaded archive does not contain SKILL.md" };
|
|
@@ -2026,8 +2203,8 @@ async function downloadPack(url) {
|
|
|
2026
2203
|
}
|
|
2027
2204
|
}
|
|
2028
2205
|
function findPackRoot(dir) {
|
|
2029
|
-
if (
|
|
2030
|
-
for (const entry of
|
|
2206
|
+
if (fs12.existsSync(path10.join(dir, "SKILL.md"))) return dir;
|
|
2207
|
+
for (const entry of fs12.readdirSync(dir, { withFileTypes: true })) {
|
|
2031
2208
|
if (entry.isDirectory()) {
|
|
2032
2209
|
const found = findPackRoot(path10.join(dir, entry.name));
|
|
2033
2210
|
if (found) return found;
|
|
@@ -2049,20 +2226,20 @@ function resolvePackId(packId, packsDir) {
|
|
|
2049
2226
|
"packs",
|
|
2050
2227
|
packId
|
|
2051
2228
|
);
|
|
2052
|
-
if (
|
|
2229
|
+
if (fs12.existsSync(path10.join(bundledDir, "SKILL.md"))) {
|
|
2053
2230
|
return { path: bundledDir };
|
|
2054
2231
|
}
|
|
2055
2232
|
const localDir = path10.join(packsDir, packId);
|
|
2056
|
-
if (
|
|
2233
|
+
if (fs12.existsSync(path10.join(localDir, "SKILL.md"))) {
|
|
2057
2234
|
return { path: localDir };
|
|
2058
2235
|
}
|
|
2059
2236
|
return { error: `Pack "${packId}" is registered but not available locally. It may need to be downloaded manually.` };
|
|
2060
2237
|
}
|
|
2061
2238
|
|
|
2062
2239
|
// src/tools/export.ts
|
|
2063
|
-
import * as
|
|
2240
|
+
import * as fs13 from "fs";
|
|
2064
2241
|
import * as path11 from "path";
|
|
2065
|
-
import * as
|
|
2242
|
+
import * as yaml6 from "js-yaml";
|
|
2066
2243
|
async function handleExport(args2, paths, service) {
|
|
2067
2244
|
const allEngrams = loadEngrams(paths.engramsPath);
|
|
2068
2245
|
let selected = allEngrams.filter((e) => e.status === "active");
|
|
@@ -2105,13 +2282,13 @@ async function handleExport(args2, paths, service) {
|
|
|
2105
2282
|
}
|
|
2106
2283
|
};
|
|
2107
2284
|
}
|
|
2108
|
-
if (
|
|
2285
|
+
if (fs13.existsSync(packDir)) {
|
|
2109
2286
|
return {
|
|
2110
2287
|
success: false,
|
|
2111
2288
|
error: `Pack directory already exists at ${packDir}. Remove it first or use a different name.`
|
|
2112
2289
|
};
|
|
2113
2290
|
}
|
|
2114
|
-
|
|
2291
|
+
fs13.mkdirSync(packDir, { recursive: true });
|
|
2115
2292
|
const skillContent = `---
|
|
2116
2293
|
name: "${args2.name}"
|
|
2117
2294
|
description: "${args2.description}"
|
|
@@ -2130,7 +2307,7 @@ ${args2.description}
|
|
|
2130
2307
|
|
|
2131
2308
|
Exported ${selected.length} engrams.
|
|
2132
2309
|
`;
|
|
2133
|
-
|
|
2310
|
+
fs13.writeFileSync(path11.join(packDir, "SKILL.md"), skillContent);
|
|
2134
2311
|
const exportEngrams = selected.map((e) => ({
|
|
2135
2312
|
id: e.id,
|
|
2136
2313
|
version: e.version,
|
|
@@ -2151,9 +2328,9 @@ Exported ${selected.length} engrams.
|
|
|
2151
2328
|
},
|
|
2152
2329
|
feedback_signals: { positive: 0, negative: 0 }
|
|
2153
2330
|
}));
|
|
2154
|
-
|
|
2331
|
+
fs13.writeFileSync(
|
|
2155
2332
|
path11.join(packDir, "engrams.yaml"),
|
|
2156
|
-
|
|
2333
|
+
yaml6.dump({ engrams: exportEngrams }, { lineWidth: 120, noRefs: true, quotingType: '"' })
|
|
2157
2334
|
);
|
|
2158
2335
|
if (service?.isEnabled() && selected.length >= 5) {
|
|
2159
2336
|
try {
|
|
@@ -2165,16 +2342,16 @@ Exported ${selected.length} engrams.
|
|
|
2165
2342
|
}
|
|
2166
2343
|
|
|
2167
2344
|
// src/modules.ts
|
|
2168
|
-
import * as
|
|
2345
|
+
import * as fs14 from "fs";
|
|
2169
2346
|
import * as path12 from "path";
|
|
2170
|
-
import * as
|
|
2347
|
+
import * as yaml7 from "js-yaml";
|
|
2171
2348
|
function discoverModules(storage2) {
|
|
2172
2349
|
const modules = [];
|
|
2173
2350
|
if (storage2.mode !== "full") return modules;
|
|
2174
2351
|
const globalModulesDir = path12.join(storage2.basePath, ".datacore", "modules");
|
|
2175
2352
|
modules.push(...scanModulesDir(globalModulesDir, "global"));
|
|
2176
2353
|
try {
|
|
2177
|
-
const entries =
|
|
2354
|
+
const entries = fs14.readdirSync(storage2.basePath);
|
|
2178
2355
|
for (const entry of entries) {
|
|
2179
2356
|
if (/^\d+-/.test(entry)) {
|
|
2180
2357
|
const spaceModulesDir = path12.join(storage2.basePath, entry, ".datacore", "modules");
|
|
@@ -2187,16 +2364,16 @@ function discoverModules(storage2) {
|
|
|
2187
2364
|
}
|
|
2188
2365
|
function scanModulesDir(modulesDir, scope, spaceName) {
|
|
2189
2366
|
const modules = [];
|
|
2190
|
-
if (!
|
|
2367
|
+
if (!fs14.existsSync(modulesDir)) return modules;
|
|
2191
2368
|
try {
|
|
2192
|
-
const entries =
|
|
2369
|
+
const entries = fs14.readdirSync(modulesDir);
|
|
2193
2370
|
for (const entry of entries) {
|
|
2194
2371
|
const modulePath = path12.join(modulesDir, entry);
|
|
2195
2372
|
const manifestPath = path12.join(modulePath, "module.yaml");
|
|
2196
|
-
if (!
|
|
2373
|
+
if (!fs14.existsSync(manifestPath)) continue;
|
|
2197
2374
|
try {
|
|
2198
|
-
const raw =
|
|
2199
|
-
const manifest =
|
|
2375
|
+
const raw = fs14.readFileSync(manifestPath, "utf-8");
|
|
2376
|
+
const manifest = yaml7.load(raw);
|
|
2200
2377
|
if (!manifest || !manifest.name) continue;
|
|
2201
2378
|
modules.push({
|
|
2202
2379
|
name: manifest.name,
|
|
@@ -2218,7 +2395,7 @@ async function loadModuleTools(modules, storage2) {
|
|
|
2218
2395
|
const declaredTools = mod.manifest.provides?.tools;
|
|
2219
2396
|
if (!declaredTools || declaredTools.length === 0) continue;
|
|
2220
2397
|
const toolsIndexPath = path12.join(mod.modulePath, "tools", "index.js");
|
|
2221
|
-
if (!
|
|
2398
|
+
if (!fs14.existsSync(toolsIndexPath)) continue;
|
|
2222
2399
|
try {
|
|
2223
2400
|
const toolsModule = await import(toolsIndexPath);
|
|
2224
2401
|
const moduleTools2 = toolsModule.tools || toolsModule.default?.tools || [];
|
|
@@ -2318,7 +2495,7 @@ async function handleModulesInfo(args2, storage2, cachedModules) {
|
|
|
2318
2495
|
}
|
|
2319
2496
|
|
|
2320
2497
|
// src/tools/modules-health.ts
|
|
2321
|
-
import * as
|
|
2498
|
+
import * as fs15 from "fs";
|
|
2322
2499
|
import * as path13 from "path";
|
|
2323
2500
|
async function handleModulesHealth(args2, storage2, cachedModules) {
|
|
2324
2501
|
const modules = cachedModules ?? discoverModules(storage2);
|
|
@@ -2341,10 +2518,10 @@ async function handleModulesHealth(args2, storage2, cachedModules) {
|
|
|
2341
2518
|
async function checkModule(mod, storage2) {
|
|
2342
2519
|
const issues = [];
|
|
2343
2520
|
const manifest = mod.manifest;
|
|
2344
|
-
if (!
|
|
2521
|
+
if (!fs15.existsSync(path13.join(mod.modulePath, "SKILL.md"))) {
|
|
2345
2522
|
issues.push("Missing SKILL.md (ecosystem entry point)");
|
|
2346
2523
|
}
|
|
2347
|
-
if (!
|
|
2524
|
+
if (!fs15.existsSync(path13.join(mod.modulePath, "CLAUDE.base.md"))) {
|
|
2348
2525
|
issues.push("Missing CLAUDE.base.md (AI context)");
|
|
2349
2526
|
}
|
|
2350
2527
|
if (!manifest.manifest_version || manifest.manifest_version < 2) {
|
|
@@ -2361,7 +2538,7 @@ async function checkModule(mod, storage2) {
|
|
|
2361
2538
|
const declaredTools = provides?.tools || [];
|
|
2362
2539
|
if (declaredTools.length > 0) {
|
|
2363
2540
|
const toolsIndex = path13.join(mod.modulePath, "tools", "index.js");
|
|
2364
|
-
if (!
|
|
2541
|
+
if (!fs15.existsSync(toolsIndex)) {
|
|
2365
2542
|
issues.push(`Declares ${declaredTools.length} tools but tools/index.js not found`);
|
|
2366
2543
|
} else {
|
|
2367
2544
|
try {
|
|
@@ -2381,12 +2558,12 @@ async function checkModule(mod, storage2) {
|
|
|
2381
2558
|
const suspectDirs = ["output", "data", "state"];
|
|
2382
2559
|
for (const dir of suspectDirs) {
|
|
2383
2560
|
const fullPath = path13.join(mod.modulePath, dir);
|
|
2384
|
-
if (
|
|
2561
|
+
if (fs15.existsSync(fullPath) && fs15.statSync(fullPath).isDirectory()) {
|
|
2385
2562
|
issues.push(`Data dir '${dir}/' found in module code (should be in space data path)`);
|
|
2386
2563
|
}
|
|
2387
2564
|
}
|
|
2388
2565
|
try {
|
|
2389
|
-
const entries =
|
|
2566
|
+
const entries = fs15.readdirSync(mod.modulePath);
|
|
2390
2567
|
for (const entry of entries) {
|
|
2391
2568
|
if (suspectExts.some((ext) => entry.endsWith(ext))) {
|
|
2392
2569
|
issues.push(`Data file '${entry}' found in module code dir`);
|
|
@@ -2594,18 +2771,18 @@ async function handleBatchFeedback(signals, engramsPath, packsPath, service) {
|
|
|
2594
2771
|
}
|
|
2595
2772
|
|
|
2596
2773
|
// src/tools/session-start.ts
|
|
2597
|
-
import * as
|
|
2774
|
+
import * as fs19 from "fs";
|
|
2598
2775
|
import * as path18 from "path";
|
|
2599
2776
|
import * as crypto2 from "crypto";
|
|
2600
2777
|
|
|
2601
2778
|
// src/engagement/service.ts
|
|
2602
|
-
import * as
|
|
2779
|
+
import * as fs18 from "fs";
|
|
2603
2780
|
import * as path17 from "path";
|
|
2604
2781
|
|
|
2605
2782
|
// src/engagement/profile.ts
|
|
2606
|
-
import * as
|
|
2783
|
+
import * as fs16 from "fs";
|
|
2607
2784
|
import * as path15 from "path";
|
|
2608
|
-
import * as
|
|
2785
|
+
import * as yaml8 from "js-yaml";
|
|
2609
2786
|
var PROFILE_DIR = "engagement";
|
|
2610
2787
|
var PROFILE_FILE = "profile.yaml";
|
|
2611
2788
|
function engagementDir(basePath) {
|
|
@@ -2619,16 +2796,16 @@ function createDefaultProfile() {
|
|
|
2619
2796
|
}
|
|
2620
2797
|
function loadProfile(basePath) {
|
|
2621
2798
|
const filePath = profilePath(basePath);
|
|
2622
|
-
if (!
|
|
2799
|
+
if (!fs16.existsSync(filePath)) {
|
|
2623
2800
|
return createDefaultProfile();
|
|
2624
2801
|
}
|
|
2625
2802
|
try {
|
|
2626
|
-
const raw =
|
|
2803
|
+
const raw = yaml8.load(fs16.readFileSync(filePath, "utf8"));
|
|
2627
2804
|
return EngagementProfileSchema.parse(raw);
|
|
2628
2805
|
} catch (err) {
|
|
2629
2806
|
logger.warning(`Engagement profile corrupted, backing up and creating fresh: ${err}`);
|
|
2630
2807
|
try {
|
|
2631
|
-
|
|
2808
|
+
fs16.copyFileSync(filePath, filePath + ".bak");
|
|
2632
2809
|
} catch {
|
|
2633
2810
|
}
|
|
2634
2811
|
return createDefaultProfile();
|
|
@@ -2636,33 +2813,33 @@ function loadProfile(basePath) {
|
|
|
2636
2813
|
}
|
|
2637
2814
|
function saveProfile(basePath, profile) {
|
|
2638
2815
|
const dir = engagementDir(basePath);
|
|
2639
|
-
if (!
|
|
2640
|
-
|
|
2816
|
+
if (!fs16.existsSync(dir)) {
|
|
2817
|
+
fs16.mkdirSync(dir, { recursive: true });
|
|
2641
2818
|
}
|
|
2642
2819
|
const filePath = profilePath(basePath);
|
|
2643
|
-
const content =
|
|
2820
|
+
const content = yaml8.dump(profile, { lineWidth: 120, noRefs: true, quotingType: '"' });
|
|
2644
2821
|
const tmpPath = filePath + ".tmp." + process.pid;
|
|
2645
|
-
|
|
2646
|
-
|
|
2822
|
+
fs16.writeFileSync(tmpPath, content);
|
|
2823
|
+
fs16.renameSync(tmpPath, filePath);
|
|
2647
2824
|
}
|
|
2648
2825
|
function ensureEngagementDir(basePath) {
|
|
2649
2826
|
const dir = engagementDir(basePath);
|
|
2650
|
-
if (!
|
|
2651
|
-
|
|
2827
|
+
if (!fs16.existsSync(dir)) {
|
|
2828
|
+
fs16.mkdirSync(dir, { recursive: true });
|
|
2652
2829
|
}
|
|
2653
2830
|
const gitignorePath = path15.join(basePath, ".datacore", ".gitignore");
|
|
2654
|
-
if (
|
|
2655
|
-
const content =
|
|
2831
|
+
if (fs16.existsSync(gitignorePath)) {
|
|
2832
|
+
const content = fs16.readFileSync(gitignorePath, "utf8");
|
|
2656
2833
|
if (!content.includes("engagement/profile.yaml")) {
|
|
2657
|
-
|
|
2834
|
+
fs16.appendFileSync(gitignorePath, "\nengagement/profile.yaml\nengagement/badge.svg\n");
|
|
2658
2835
|
}
|
|
2659
2836
|
}
|
|
2660
2837
|
}
|
|
2661
2838
|
|
|
2662
2839
|
// src/engagement/actions.ts
|
|
2663
|
-
import * as
|
|
2840
|
+
import * as fs17 from "fs";
|
|
2664
2841
|
import * as path16 from "path";
|
|
2665
|
-
import * as
|
|
2842
|
+
import * as yaml9 from "js-yaml";
|
|
2666
2843
|
var BUNDLED_ACTIONS = {
|
|
2667
2844
|
version: 1,
|
|
2668
2845
|
actions: {
|
|
@@ -2735,11 +2912,11 @@ var BUNDLED_ACTIONS = {
|
|
|
2735
2912
|
};
|
|
2736
2913
|
function loadActions(basePath) {
|
|
2737
2914
|
const actionsPath = path16.join(basePath, ".datacore", "engagement", "xp-actions.yaml");
|
|
2738
|
-
if (!
|
|
2915
|
+
if (!fs17.existsSync(actionsPath)) {
|
|
2739
2916
|
return BUNDLED_ACTIONS;
|
|
2740
2917
|
}
|
|
2741
2918
|
try {
|
|
2742
|
-
const raw =
|
|
2919
|
+
const raw = yaml9.load(fs17.readFileSync(actionsPath, "utf8"));
|
|
2743
2920
|
return XPActionRegistrySchema.parse(raw);
|
|
2744
2921
|
} catch (err) {
|
|
2745
2922
|
logger.warning(`Malformed xp-actions.yaml, using defaults: ${err}`);
|
|
@@ -2749,12 +2926,12 @@ function loadActions(basePath) {
|
|
|
2749
2926
|
function writeDefaultActions(basePath) {
|
|
2750
2927
|
const dir = path16.join(basePath, ".datacore", "engagement");
|
|
2751
2928
|
const actionsPath = path16.join(dir, "xp-actions.yaml");
|
|
2752
|
-
if (
|
|
2753
|
-
if (!
|
|
2754
|
-
|
|
2929
|
+
if (fs17.existsSync(actionsPath)) return;
|
|
2930
|
+
if (!fs17.existsSync(dir)) {
|
|
2931
|
+
fs17.mkdirSync(dir, { recursive: true });
|
|
2755
2932
|
}
|
|
2756
|
-
const content =
|
|
2757
|
-
|
|
2933
|
+
const content = yaml9.dump(BUNDLED_ACTIONS, { lineWidth: 120, noRefs: true, quotingType: '"' });
|
|
2934
|
+
fs17.writeFileSync(actionsPath, content);
|
|
2758
2935
|
}
|
|
2759
2936
|
|
|
2760
2937
|
// src/engagement/engine.ts
|
|
@@ -2996,9 +3173,9 @@ var EngagementService = class {
|
|
|
2996
3173
|
const profilePath2 = path17.join(this.basePath, ".datacore", "engagement", "profile.yaml");
|
|
2997
3174
|
const engramsPath = path17.join(this.basePath, ".datacore", "learning", "engrams.yaml");
|
|
2998
3175
|
const coreEngramsPath = path17.join(this.basePath, "engrams.yaml");
|
|
2999
|
-
if (!
|
|
3000
|
-
const actualEngramsPath =
|
|
3001
|
-
if (
|
|
3176
|
+
if (!fs18.existsSync(profilePath2)) {
|
|
3177
|
+
const actualEngramsPath = fs18.existsSync(engramsPath) ? engramsPath : coreEngramsPath;
|
|
3178
|
+
if (fs18.existsSync(actualEngramsPath)) {
|
|
3002
3179
|
const engrams = loadEngrams(actualEngramsPath);
|
|
3003
3180
|
if (engrams.length > 0) {
|
|
3004
3181
|
this.profile = migrateProfile(this.basePath, engrams);
|
|
@@ -3444,21 +3621,29 @@ function dismissChallenge(profile, challengeId) {
|
|
|
3444
3621
|
}
|
|
3445
3622
|
|
|
3446
3623
|
// src/tools/session-start.ts
|
|
3447
|
-
async function handleSessionStart(args2, storage2, bridge, engagementService2) {
|
|
3624
|
+
async function handleSessionStart(args2, storage2, bridge, engagementService2, tracker) {
|
|
3625
|
+
if (storage2.mode === "full") {
|
|
3626
|
+
for (const dir of [storage2.archivePath, storage2.exchangeInboxPath, storage2.exchangeOutboxPath, storage2.statePath]) {
|
|
3627
|
+
if (!fs19.existsSync(dir)) fs19.mkdirSync(dir, { recursive: true });
|
|
3628
|
+
}
|
|
3629
|
+
}
|
|
3448
3630
|
const session_id = crypto2.randomUUID();
|
|
3449
3631
|
let engrams = null;
|
|
3450
3632
|
if (args2.task) {
|
|
3451
3633
|
const injectResult = await handleInject(
|
|
3452
3634
|
{ prompt: args2.task, session_id, scope: args2.tags?.length ? `tags:${args2.tags.join(",")}` : void 0 },
|
|
3453
|
-
{ engramsPath: storage2.engramsPath, packsPath: storage2.packsPath, basePath: storage2.basePath }
|
|
3635
|
+
{ engramsPath: storage2.engramsPath, packsPath: storage2.packsPath, basePath: storage2.basePath, schemasPath: storage2.schemasPath }
|
|
3454
3636
|
);
|
|
3455
3637
|
if (injectResult.count > 0) {
|
|
3456
3638
|
engrams = { text: injectResult.text, count: injectResult.count };
|
|
3457
3639
|
}
|
|
3640
|
+
if (tracker && injectResult.injected_personal_ids.length > 0) {
|
|
3641
|
+
tracker.trackInjected(session_id, injectResult.injected_personal_ids);
|
|
3642
|
+
}
|
|
3458
3643
|
}
|
|
3459
3644
|
const { date: today } = localDate();
|
|
3460
3645
|
const journalFile = path18.join(storage2.journalPath, `${today}.md`);
|
|
3461
|
-
const journal_today =
|
|
3646
|
+
const journal_today = fs19.existsSync(journalFile) ? fs19.readFileSync(journalFile, "utf8") : null;
|
|
3462
3647
|
const allEngrams = loadEngrams(storage2.engramsPath);
|
|
3463
3648
|
const pending_candidates = allEngrams.filter((e) => e.status === "candidate").length;
|
|
3464
3649
|
const recommendations = [];
|
|
@@ -3578,7 +3763,14 @@ Positive feedback strengthens engrams. Unused ones naturally decay.`;
|
|
|
3578
3763
|
var SESSION_GUIDE_SHORT = `Session started. Workflow: work \u2192 feedback \u2192 session.end.`;
|
|
3579
3764
|
|
|
3580
3765
|
// src/tools/session-end.ts
|
|
3581
|
-
async function handleSessionEnd(args2, storage2, engagementService2) {
|
|
3766
|
+
async function handleSessionEnd(args2, storage2, engagementService2, tracker) {
|
|
3767
|
+
if (args2.session_id && tracker) {
|
|
3768
|
+
const pairs = tracker.getCoAccessPairs(args2.session_id);
|
|
3769
|
+
if (pairs.length > 0) {
|
|
3770
|
+
writeCoAccessAssociations(storage2.engramsPath, pairs);
|
|
3771
|
+
}
|
|
3772
|
+
tracker.clear(args2.session_id);
|
|
3773
|
+
}
|
|
3582
3774
|
const captureResult = await handleCapture(
|
|
3583
3775
|
{ type: "journal", content: args2.summary, tags: args2.tags },
|
|
3584
3776
|
storage2
|
|
@@ -3622,6 +3814,43 @@ async function handleSessionEnd(args2, storage2, engagementService2) {
|
|
|
3622
3814
|
})
|
|
3623
3815
|
};
|
|
3624
3816
|
}
|
|
3817
|
+
function writeCoAccessAssociations(engramsPath, pairs) {
|
|
3818
|
+
const engrams = loadEngrams(engramsPath);
|
|
3819
|
+
const map = new Map(engrams.map((e) => [e.id, e]));
|
|
3820
|
+
const config = getConfig().co_access;
|
|
3821
|
+
let changed = false;
|
|
3822
|
+
for (const [idA, idB] of pairs) {
|
|
3823
|
+
const a = map.get(idA);
|
|
3824
|
+
const b = map.get(idB);
|
|
3825
|
+
if (!a || !b || a.pack || b.pack) continue;
|
|
3826
|
+
changed = strengthenCoAccess(a, idB, config) || changed;
|
|
3827
|
+
changed = strengthenCoAccess(b, idA, config) || changed;
|
|
3828
|
+
}
|
|
3829
|
+
if (changed) {
|
|
3830
|
+
atomicWriteYaml(engramsPath, { engrams });
|
|
3831
|
+
}
|
|
3832
|
+
}
|
|
3833
|
+
function strengthenCoAccess(engram, targetId, config) {
|
|
3834
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
3835
|
+
const existing = engram.associations.find(
|
|
3836
|
+
(a) => a.target === targetId && a.type === "co_accessed"
|
|
3837
|
+
);
|
|
3838
|
+
if (existing) {
|
|
3839
|
+
const newStrength = Math.min(existing.strength + config.increment, config.max_strength);
|
|
3840
|
+
if (newStrength === existing.strength && existing.updated_at === today) return false;
|
|
3841
|
+
existing.strength = newStrength;
|
|
3842
|
+
existing.updated_at = today;
|
|
3843
|
+
return true;
|
|
3844
|
+
}
|
|
3845
|
+
engram.associations.push({
|
|
3846
|
+
target_type: "engram",
|
|
3847
|
+
target: targetId,
|
|
3848
|
+
strength: config.new_strength,
|
|
3849
|
+
type: "co_accessed",
|
|
3850
|
+
updated_at: today
|
|
3851
|
+
});
|
|
3852
|
+
return true;
|
|
3853
|
+
}
|
|
3625
3854
|
|
|
3626
3855
|
// src/tools/recall.ts
|
|
3627
3856
|
async function handleRecall(args2, storage2, bridge) {
|
|
@@ -3913,14 +4142,961 @@ function handleChallengeResolve(args2, service) {
|
|
|
3913
4142
|
return { success: false, type: "challenge", action: args2.action, error: 'Invalid action. Must be "complete" or "dismiss".' };
|
|
3914
4143
|
}
|
|
3915
4144
|
|
|
4145
|
+
// src/tools/schemas.ts
|
|
4146
|
+
import * as fs20 from "fs";
|
|
4147
|
+
|
|
4148
|
+
// src/schema-detection.ts
|
|
4149
|
+
var MIN_STRENGTH = 0.4;
|
|
4150
|
+
var MIN_MEMBERS = 3;
|
|
4151
|
+
var MIN_SHARED_ANCHORS = 2;
|
|
4152
|
+
var K_CORE = 2;
|
|
4153
|
+
var MAX_EDGES = 1e4;
|
|
4154
|
+
var STALE_DAYS = 90;
|
|
4155
|
+
function detectSchemas(engrams, existing) {
|
|
4156
|
+
const adjacency = /* @__PURE__ */ new Map();
|
|
4157
|
+
const engramMap = new Map(engrams.map((e) => [e.id, e]));
|
|
4158
|
+
const seenEdges = /* @__PURE__ */ new Set();
|
|
4159
|
+
for (const engram of engrams) {
|
|
4160
|
+
if (engram.status !== "active") continue;
|
|
4161
|
+
for (const assoc of engram.associations) {
|
|
4162
|
+
if (assoc.target_type !== "engram") continue;
|
|
4163
|
+
const target = engramMap.get(assoc.target);
|
|
4164
|
+
if (!target || target.status !== "active") continue;
|
|
4165
|
+
const effectiveStrength = assoc.type === "co_accessed" && assoc.updated_at ? decayedCoAccessStrength(assoc.strength, assoc.updated_at) : assoc.strength;
|
|
4166
|
+
if (effectiveStrength < MIN_STRENGTH) continue;
|
|
4167
|
+
if (!adjacency.has(engram.id)) adjacency.set(engram.id, /* @__PURE__ */ new Set());
|
|
4168
|
+
if (!adjacency.has(assoc.target)) adjacency.set(assoc.target, /* @__PURE__ */ new Set());
|
|
4169
|
+
adjacency.get(engram.id).add(assoc.target);
|
|
4170
|
+
adjacency.get(assoc.target).add(engram.id);
|
|
4171
|
+
const edgeKey = engram.id < assoc.target ? `${engram.id}:${assoc.target}` : `${assoc.target}:${engram.id}`;
|
|
4172
|
+
seenEdges.add(edgeKey);
|
|
4173
|
+
}
|
|
4174
|
+
}
|
|
4175
|
+
if (seenEdges.size > MAX_EDGES) {
|
|
4176
|
+
return {
|
|
4177
|
+
created: [],
|
|
4178
|
+
updated: [],
|
|
4179
|
+
flagged: [],
|
|
4180
|
+
warning: `Association graph has ${seenEdges.size} edges (limit: ${MAX_EDGES}). Skipping detection to prevent performance issues.`
|
|
4181
|
+
};
|
|
4182
|
+
}
|
|
4183
|
+
let changed = true;
|
|
4184
|
+
while (changed) {
|
|
4185
|
+
changed = false;
|
|
4186
|
+
for (const [node, neighbors] of adjacency) {
|
|
4187
|
+
if (neighbors.size < K_CORE) {
|
|
4188
|
+
for (const neighbor of neighbors) {
|
|
4189
|
+
adjacency.get(neighbor)?.delete(node);
|
|
4190
|
+
}
|
|
4191
|
+
adjacency.delete(node);
|
|
4192
|
+
changed = true;
|
|
4193
|
+
}
|
|
4194
|
+
}
|
|
4195
|
+
}
|
|
4196
|
+
const visited = /* @__PURE__ */ new Set();
|
|
4197
|
+
const components = [];
|
|
4198
|
+
for (const node of adjacency.keys()) {
|
|
4199
|
+
if (visited.has(node)) continue;
|
|
4200
|
+
const component = [];
|
|
4201
|
+
const queue = [node];
|
|
4202
|
+
visited.add(node);
|
|
4203
|
+
while (queue.length > 0) {
|
|
4204
|
+
const current = queue.shift();
|
|
4205
|
+
component.push(current);
|
|
4206
|
+
for (const neighbor of adjacency.get(current) ?? []) {
|
|
4207
|
+
if (!visited.has(neighbor)) {
|
|
4208
|
+
visited.add(neighbor);
|
|
4209
|
+
queue.push(neighbor);
|
|
4210
|
+
}
|
|
4211
|
+
}
|
|
4212
|
+
}
|
|
4213
|
+
components.push(component);
|
|
4214
|
+
}
|
|
4215
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
4216
|
+
const created = [];
|
|
4217
|
+
const updated = [];
|
|
4218
|
+
for (const component of components) {
|
|
4219
|
+
if (component.length < MIN_MEMBERS) continue;
|
|
4220
|
+
const anchorCounts = /* @__PURE__ */ new Map();
|
|
4221
|
+
for (const id of component) {
|
|
4222
|
+
const engram = engramMap.get(id);
|
|
4223
|
+
if (!engram) continue;
|
|
4224
|
+
for (const anchor of engram.knowledge_anchors) {
|
|
4225
|
+
anchorCounts.set(anchor.path, (anchorCounts.get(anchor.path) ?? 0) + 1);
|
|
4226
|
+
}
|
|
4227
|
+
}
|
|
4228
|
+
const sharedAnchors = Array.from(anchorCounts.entries()).filter(([, count]) => count >= 2).map(([path23]) => path23);
|
|
4229
|
+
if (sharedAnchors.length < MIN_SHARED_ANCHORS) continue;
|
|
4230
|
+
const memberScore = Math.min(component.length / 10, 1);
|
|
4231
|
+
const anchorScore = Math.min(sharedAnchors.length / 5, 1);
|
|
4232
|
+
let totalStrength = 0;
|
|
4233
|
+
let edgePairs = 0;
|
|
4234
|
+
for (const id of component) {
|
|
4235
|
+
const neighbors = adjacency.get(id) ?? /* @__PURE__ */ new Set();
|
|
4236
|
+
for (const neighbor of neighbors) {
|
|
4237
|
+
if (component.includes(neighbor)) {
|
|
4238
|
+
totalStrength += getMaxStrength(engramMap.get(id), neighbor, engramMap);
|
|
4239
|
+
edgePairs++;
|
|
4240
|
+
}
|
|
4241
|
+
}
|
|
4242
|
+
}
|
|
4243
|
+
const avgStrength = edgePairs > 0 ? totalStrength / edgePairs : 0;
|
|
4244
|
+
const confidence = Math.round(memberScore * anchorScore * avgStrength * 1e3) / 1e3;
|
|
4245
|
+
const componentSet = new Set(component);
|
|
4246
|
+
let matched = false;
|
|
4247
|
+
for (const schema of existing) {
|
|
4248
|
+
if (schema.status === "archived") continue;
|
|
4249
|
+
const schemaSet = new Set(schema.members);
|
|
4250
|
+
const intersection = component.filter((id) => schemaSet.has(id)).length;
|
|
4251
|
+
const union = (/* @__PURE__ */ new Set([...component, ...schema.members])).size;
|
|
4252
|
+
const jaccard = intersection / union;
|
|
4253
|
+
if (jaccard >= 0.5) {
|
|
4254
|
+
updated.push({
|
|
4255
|
+
...schema,
|
|
4256
|
+
members: component.sort(),
|
|
4257
|
+
shared_anchors: sharedAnchors,
|
|
4258
|
+
confidence,
|
|
4259
|
+
updated: today
|
|
4260
|
+
});
|
|
4261
|
+
matched = true;
|
|
4262
|
+
break;
|
|
4263
|
+
}
|
|
4264
|
+
}
|
|
4265
|
+
if (!matched) {
|
|
4266
|
+
const allExisting = [...existing, ...created];
|
|
4267
|
+
created.push({
|
|
4268
|
+
id: generateSchemaId(allExisting),
|
|
4269
|
+
name: `Schema from ${component.length} engrams`,
|
|
4270
|
+
members: component.sort(),
|
|
4271
|
+
confidence,
|
|
4272
|
+
status: "candidate",
|
|
4273
|
+
shared_anchors: sharedAnchors,
|
|
4274
|
+
created: today,
|
|
4275
|
+
updated: today
|
|
4276
|
+
});
|
|
4277
|
+
}
|
|
4278
|
+
}
|
|
4279
|
+
const flagged = [];
|
|
4280
|
+
const staleDate = /* @__PURE__ */ new Date();
|
|
4281
|
+
staleDate.setDate(staleDate.getDate() - STALE_DAYS);
|
|
4282
|
+
const staleDateStr = staleDate.toISOString().split("T")[0];
|
|
4283
|
+
for (const schema of existing) {
|
|
4284
|
+
if (schema.status === "archived") continue;
|
|
4285
|
+
if (schema.updated < staleDateStr) {
|
|
4286
|
+
flagged.push(schema);
|
|
4287
|
+
}
|
|
4288
|
+
}
|
|
4289
|
+
return { created, updated, flagged };
|
|
4290
|
+
}
|
|
4291
|
+
function getMaxStrength(engram, targetId, engramMap) {
|
|
4292
|
+
let max = 0;
|
|
4293
|
+
for (const assoc of engram.associations) {
|
|
4294
|
+
if (assoc.target === targetId) {
|
|
4295
|
+
const effective = assoc.type === "co_accessed" && assoc.updated_at ? decayedCoAccessStrength(assoc.strength, assoc.updated_at) : assoc.strength;
|
|
4296
|
+
if (effective > max) max = effective;
|
|
4297
|
+
}
|
|
4298
|
+
}
|
|
4299
|
+
const target = engramMap.get(targetId);
|
|
4300
|
+
if (target) {
|
|
4301
|
+
for (const assoc of target.associations) {
|
|
4302
|
+
if (assoc.target === engram.id) {
|
|
4303
|
+
const effective = assoc.type === "co_accessed" && assoc.updated_at ? decayedCoAccessStrength(assoc.strength, assoc.updated_at) : assoc.strength;
|
|
4304
|
+
if (effective > max) max = effective;
|
|
4305
|
+
}
|
|
4306
|
+
}
|
|
4307
|
+
}
|
|
4308
|
+
return max;
|
|
4309
|
+
}
|
|
4310
|
+
|
|
4311
|
+
// src/tools/schemas.ts
|
|
4312
|
+
async function handleSchemas(args2, paths) {
|
|
4313
|
+
switch (args2.action) {
|
|
4314
|
+
case "list":
|
|
4315
|
+
return listSchemas(paths.schemasPath);
|
|
4316
|
+
case "detect":
|
|
4317
|
+
return detectAction(paths);
|
|
4318
|
+
case "activate":
|
|
4319
|
+
return setStatus(paths.schemasPath, args2.id, "active");
|
|
4320
|
+
case "archive":
|
|
4321
|
+
return setStatus(paths.schemasPath, args2.id, "archived");
|
|
4322
|
+
case "merge":
|
|
4323
|
+
return mergeSchemas(paths.schemasPath, args2.id, args2.target_id);
|
|
4324
|
+
case "split":
|
|
4325
|
+
return splitSchema(paths.schemasPath, args2.id, args2.member_ids, args2.name);
|
|
4326
|
+
case "migrate":
|
|
4327
|
+
return migrateRelations(paths.engramsPath, args2.confirm);
|
|
4328
|
+
default:
|
|
4329
|
+
throw new Error(`Unknown schemas action: ${args2.action}`);
|
|
4330
|
+
}
|
|
4331
|
+
}
|
|
4332
|
+
function listSchemas(schemasPath) {
|
|
4333
|
+
const schemas = loadSchemas(schemasPath);
|
|
4334
|
+
return {
|
|
4335
|
+
schemas: schemas.map((s) => ({
|
|
4336
|
+
id: s.id,
|
|
4337
|
+
name: s.name,
|
|
4338
|
+
status: s.status,
|
|
4339
|
+
members: s.members.length,
|
|
4340
|
+
confidence: s.confidence,
|
|
4341
|
+
shared_anchors: s.shared_anchors.length,
|
|
4342
|
+
updated: s.updated
|
|
4343
|
+
})),
|
|
4344
|
+
total: schemas.length,
|
|
4345
|
+
_hints: buildHints({
|
|
4346
|
+
next: schemas.length === 0 ? 'No schemas yet. Use action="detect" to discover schemas from association graph.' : 'Use action="detect" to update schemas from current associations.',
|
|
4347
|
+
related: ["datacore.schemas"]
|
|
4348
|
+
})
|
|
4349
|
+
};
|
|
4350
|
+
}
|
|
4351
|
+
function detectAction(paths) {
|
|
4352
|
+
const engrams = loadEngrams(paths.engramsPath);
|
|
4353
|
+
const existing = loadSchemas(paths.schemasPath);
|
|
4354
|
+
const result = detectSchemas(engrams, existing);
|
|
4355
|
+
if (result.warning) {
|
|
4356
|
+
return { warning: result.warning, created: 0, updated: 0, flagged: 0 };
|
|
4357
|
+
}
|
|
4358
|
+
const updatedIds = new Set(result.updated.map((s) => s.id));
|
|
4359
|
+
const final = [
|
|
4360
|
+
...existing.filter((s) => !updatedIds.has(s.id)),
|
|
4361
|
+
...result.updated,
|
|
4362
|
+
...result.created
|
|
4363
|
+
];
|
|
4364
|
+
const dir = paths.schemasPath.substring(0, paths.schemasPath.lastIndexOf("/"));
|
|
4365
|
+
if (dir && !fs20.existsSync(dir)) fs20.mkdirSync(dir, { recursive: true });
|
|
4366
|
+
saveSchemas(paths.schemasPath, final);
|
|
4367
|
+
return {
|
|
4368
|
+
created: result.created.length,
|
|
4369
|
+
updated: result.updated.length,
|
|
4370
|
+
flagged: result.flagged.length,
|
|
4371
|
+
flagged_ids: result.flagged.map((s) => s.id),
|
|
4372
|
+
total: final.length,
|
|
4373
|
+
_hints: buildHints({
|
|
4374
|
+
next: result.created.length > 0 ? `${result.created.length} new candidate schema(s) found. Use action="activate" to promote.` : "Schema detection complete.",
|
|
4375
|
+
related: ["datacore.schemas"]
|
|
4376
|
+
})
|
|
4377
|
+
};
|
|
4378
|
+
}
|
|
4379
|
+
function setStatus(schemasPath, id, newStatus) {
|
|
4380
|
+
const schemas = loadSchemas(schemasPath);
|
|
4381
|
+
const schema = schemas.find((s) => s.id === id);
|
|
4382
|
+
if (!schema) throw new Error(`Schema not found: ${id}`);
|
|
4383
|
+
schema.status = newStatus;
|
|
4384
|
+
schema.updated = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
4385
|
+
saveSchemas(schemasPath, schemas);
|
|
4386
|
+
return {
|
|
4387
|
+
id: schema.id,
|
|
4388
|
+
status: newStatus,
|
|
4389
|
+
_hints: buildHints({ next: `Schema ${id} is now ${newStatus}.`, related: ["datacore.schemas"] })
|
|
4390
|
+
};
|
|
4391
|
+
}
|
|
4392
|
+
function mergeSchemas(schemasPath, sourceId, targetId) {
|
|
4393
|
+
const schemas = loadSchemas(schemasPath);
|
|
4394
|
+
const source = schemas.find((s) => s.id === sourceId);
|
|
4395
|
+
const target = schemas.find((s) => s.id === targetId);
|
|
4396
|
+
if (!source) throw new Error(`Source schema not found: ${sourceId}`);
|
|
4397
|
+
if (!target) throw new Error(`Target schema not found: ${targetId}`);
|
|
4398
|
+
const memberSet = /* @__PURE__ */ new Set([...target.members, ...source.members]);
|
|
4399
|
+
target.members = Array.from(memberSet).sort();
|
|
4400
|
+
const anchorSet = /* @__PURE__ */ new Set([...target.shared_anchors, ...source.shared_anchors]);
|
|
4401
|
+
target.shared_anchors = Array.from(anchorSet);
|
|
4402
|
+
target.confidence = Math.max(target.confidence, source.confidence);
|
|
4403
|
+
const statusOrder = { archived: 0, candidate: 1, active: 2, consolidated: 3 };
|
|
4404
|
+
if (statusOrder[source.status] > statusOrder[target.status]) {
|
|
4405
|
+
target.status = source.status;
|
|
4406
|
+
}
|
|
4407
|
+
target.updated = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
4408
|
+
source.status = "archived";
|
|
4409
|
+
source.updated = target.updated;
|
|
4410
|
+
saveSchemas(schemasPath, schemas);
|
|
4411
|
+
return {
|
|
4412
|
+
merged_into: target.id,
|
|
4413
|
+
archived: source.id,
|
|
4414
|
+
members: target.members.length,
|
|
4415
|
+
_hints: buildHints({ next: `Merged ${sourceId} into ${targetId}.`, related: ["datacore.schemas"] })
|
|
4416
|
+
};
|
|
4417
|
+
}
|
|
4418
|
+
function splitSchema(schemasPath, id, memberIds, name) {
|
|
4419
|
+
const schemas = loadSchemas(schemasPath);
|
|
4420
|
+
const schema = schemas.find((s) => s.id === id);
|
|
4421
|
+
if (!schema) throw new Error(`Schema not found: ${id}`);
|
|
4422
|
+
const schemaMembers = new Set(schema.members);
|
|
4423
|
+
for (const memberId of memberIds) {
|
|
4424
|
+
if (!schemaMembers.has(memberId)) throw new Error(`${memberId} is not a member of ${id}`);
|
|
4425
|
+
}
|
|
4426
|
+
schema.members = schema.members.filter((m) => !memberIds.includes(m));
|
|
4427
|
+
schema.updated = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
4428
|
+
const newSchema = {
|
|
4429
|
+
id: generateSchemaId(schemas),
|
|
4430
|
+
name: name ?? `Split from ${schema.name}`,
|
|
4431
|
+
members: memberIds.sort(),
|
|
4432
|
+
confidence: schema.confidence * 0.8,
|
|
4433
|
+
status: "candidate",
|
|
4434
|
+
shared_anchors: [],
|
|
4435
|
+
created: schema.updated,
|
|
4436
|
+
updated: schema.updated
|
|
4437
|
+
};
|
|
4438
|
+
schemas.push(newSchema);
|
|
4439
|
+
saveSchemas(schemasPath, schemas);
|
|
4440
|
+
return {
|
|
4441
|
+
original: { id: schema.id, remaining_members: schema.members.length },
|
|
4442
|
+
new_schema: { id: newSchema.id, members: newSchema.members.length },
|
|
4443
|
+
_hints: buildHints({ next: `Split complete. New schema: ${newSchema.id}`, related: ["datacore.schemas"] })
|
|
4444
|
+
};
|
|
4445
|
+
}
|
|
4446
|
+
function migrateRelations(engramsPath, confirm) {
|
|
4447
|
+
const engrams = loadEngrams(engramsPath);
|
|
4448
|
+
let migratedCount = 0;
|
|
4449
|
+
let totalAssociations = 0;
|
|
4450
|
+
for (const engram of engrams) {
|
|
4451
|
+
if (engram.pack) continue;
|
|
4452
|
+
if (!engram.relations) continue;
|
|
4453
|
+
const converted = flattenRelations(engram);
|
|
4454
|
+
if (converted.length === 0) continue;
|
|
4455
|
+
if (confirm) {
|
|
4456
|
+
const existingTargets = new Set(engram.associations.map((a) => `${a.target}:${a.type}`));
|
|
4457
|
+
for (const assoc of converted) {
|
|
4458
|
+
const key = `${assoc.target}:${assoc.type}`;
|
|
4459
|
+
if (!existingTargets.has(key)) {
|
|
4460
|
+
engram.associations.push(assoc);
|
|
4461
|
+
totalAssociations++;
|
|
4462
|
+
}
|
|
4463
|
+
}
|
|
4464
|
+
delete engram.relations;
|
|
4465
|
+
migratedCount++;
|
|
4466
|
+
} else {
|
|
4467
|
+
migratedCount++;
|
|
4468
|
+
totalAssociations += converted.length;
|
|
4469
|
+
}
|
|
4470
|
+
}
|
|
4471
|
+
if (confirm && migratedCount > 0) {
|
|
4472
|
+
atomicWriteYaml(engramsPath, { engrams });
|
|
4473
|
+
}
|
|
4474
|
+
return {
|
|
4475
|
+
action: confirm ? "executed" : "preview",
|
|
4476
|
+
engrams_with_relations: migratedCount,
|
|
4477
|
+
associations_created: totalAssociations,
|
|
4478
|
+
_hints: buildHints({
|
|
4479
|
+
next: confirm ? `Migrated ${migratedCount} engram(s) from relations to associations.` : `Preview: ${migratedCount} engram(s) to migrate. Set confirm=true to execute.`,
|
|
4480
|
+
related: ["datacore.schemas"]
|
|
4481
|
+
})
|
|
4482
|
+
};
|
|
4483
|
+
}
|
|
4484
|
+
|
|
4485
|
+
// src/tools/exchange.ts
|
|
4486
|
+
import * as fs21 from "fs";
|
|
4487
|
+
import * as path19 from "path";
|
|
4488
|
+
import * as yaml10 from "js-yaml";
|
|
4489
|
+
|
|
4490
|
+
// src/exchange.ts
|
|
4491
|
+
import { z as z6 } from "zod";
|
|
4492
|
+
var LEPEngramSchema = z6.object({
|
|
4493
|
+
id: z6.string(),
|
|
4494
|
+
type: z6.enum(["behavioral", "terminological", "procedural", "architectural"]),
|
|
4495
|
+
scope: z6.string(),
|
|
4496
|
+
statement: z6.string(),
|
|
4497
|
+
rationale: z6.string().optional(),
|
|
4498
|
+
domain: z6.string().optional(),
|
|
4499
|
+
tags: z6.array(z6.string()).default([]),
|
|
4500
|
+
fitness: z6.number().min(0).max(1),
|
|
4501
|
+
provenance: z6.object({
|
|
4502
|
+
origin: z6.string(),
|
|
4503
|
+
chain: z6.array(z6.string()).default([])
|
|
4504
|
+
}).optional()
|
|
4505
|
+
});
|
|
4506
|
+
var LEPPacketSchema = z6.object({
|
|
4507
|
+
id: z6.string(),
|
|
4508
|
+
sender: z6.string(),
|
|
4509
|
+
signature: z6.string().nullable().default(null),
|
|
4510
|
+
created: z6.string(),
|
|
4511
|
+
engrams: z6.array(LEPEngramSchema)
|
|
4512
|
+
});
|
|
4513
|
+
function calculateFitness(engram, allEngrams) {
|
|
4514
|
+
const adoptionCount = engram.derivation_count;
|
|
4515
|
+
const adoptionBase = Math.min(Math.log2(adoptionCount + 1) / 6, 1);
|
|
4516
|
+
const associatedScopes = /* @__PURE__ */ new Set();
|
|
4517
|
+
for (const assoc of engram.associations) {
|
|
4518
|
+
if (assoc.target_type !== "engram") continue;
|
|
4519
|
+
const target = allEngrams.find((e) => e.id === assoc.target);
|
|
4520
|
+
if (target) {
|
|
4521
|
+
const prefix = target.scope.split(":")[0];
|
|
4522
|
+
associatedScopes.add(prefix);
|
|
4523
|
+
}
|
|
4524
|
+
}
|
|
4525
|
+
const envDiversity = Math.max(associatedScopes.size, 1);
|
|
4526
|
+
const envDiversityNorm = Math.min(envDiversity / 5, 1);
|
|
4527
|
+
const adoptionScore = adoptionBase * envDiversityNorm * 0.4;
|
|
4528
|
+
const rsScore = engram.activation.retrieval_strength * 0.3;
|
|
4529
|
+
const createdDate = parseEngramDate(engram.id);
|
|
4530
|
+
const ageDays = createdDate ? Math.max(0, (Date.now() - createdDate.getTime()) / 864e5) : 0;
|
|
4531
|
+
const ageScore = Math.min(Math.log(ageDays + 1) / 7, 1) * 0.2;
|
|
4532
|
+
const feedback = engram.feedback_signals;
|
|
4533
|
+
const totalFeedback = feedback ? feedback.positive + feedback.negative + feedback.neutral : 0;
|
|
4534
|
+
const contradictionRate = totalFeedback > 0 && feedback ? feedback.negative / totalFeedback : 0;
|
|
4535
|
+
const contradictionScore = (1 - contradictionRate) * 0.1;
|
|
4536
|
+
return Math.round((adoptionScore + rsScore + ageScore + contradictionScore) * 1e3) / 1e3;
|
|
4537
|
+
}
|
|
4538
|
+
function parseEngramDate(id) {
|
|
4539
|
+
const match = id.match(/^ENG-(\d{4})-(\d{4})-/);
|
|
4540
|
+
if (!match) return null;
|
|
4541
|
+
const year = match[1];
|
|
4542
|
+
const mmdd = match[2];
|
|
4543
|
+
const month = mmdd.slice(0, 2);
|
|
4544
|
+
const day = mmdd.slice(2, 4);
|
|
4545
|
+
return /* @__PURE__ */ new Date(`${year}-${month}-${day}`);
|
|
4546
|
+
}
|
|
4547
|
+
function createLEPPacket(engrams, allEngrams, sender) {
|
|
4548
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
4549
|
+
const id = `LEP-${today.replace(/-/g, "").slice(0, 4)}-${today.replace(/-/g, "").slice(4)}-${String(Date.now()).slice(-3)}`;
|
|
4550
|
+
const lepEngrams = engrams.filter((e) => e.visibility === "public" || e.visibility === "template").map((e) => ({
|
|
4551
|
+
id: e.id,
|
|
4552
|
+
type: e.type,
|
|
4553
|
+
scope: e.scope,
|
|
4554
|
+
statement: e.statement,
|
|
4555
|
+
rationale: e.rationale,
|
|
4556
|
+
domain: e.domain,
|
|
4557
|
+
tags: e.tags,
|
|
4558
|
+
fitness: calculateFitness(e, allEngrams),
|
|
4559
|
+
provenance: {
|
|
4560
|
+
origin: sender,
|
|
4561
|
+
chain: [id]
|
|
4562
|
+
}
|
|
4563
|
+
}));
|
|
4564
|
+
return { id, sender, signature: null, created: today, engrams: lepEngrams };
|
|
4565
|
+
}
|
|
4566
|
+
function validateLEPPacket(raw) {
|
|
4567
|
+
return LEPPacketSchema.parse(raw);
|
|
4568
|
+
}
|
|
4569
|
+
function levenshteinDistance(a, b) {
|
|
4570
|
+
const m = a.length;
|
|
4571
|
+
const n = b.length;
|
|
4572
|
+
const dp = Array.from({ length: n + 1 }, (_, i) => i);
|
|
4573
|
+
for (let i = 1; i <= m; i++) {
|
|
4574
|
+
let prev = dp[0];
|
|
4575
|
+
dp[0] = i;
|
|
4576
|
+
for (let j = 1; j <= n; j++) {
|
|
4577
|
+
const tmp = dp[j];
|
|
4578
|
+
dp[j] = a[i - 1] === b[j - 1] ? prev : 1 + Math.min(prev, dp[j], dp[j - 1]);
|
|
4579
|
+
prev = tmp;
|
|
4580
|
+
}
|
|
4581
|
+
}
|
|
4582
|
+
return dp[n];
|
|
4583
|
+
}
|
|
4584
|
+
function normalizeStatement(s) {
|
|
4585
|
+
return s.toLowerCase().replace(/[^\w\s]/g, "").replace(/\s+/g, " ").trim();
|
|
4586
|
+
}
|
|
4587
|
+
function importLEPEngrams(packet, existing, config = {}) {
|
|
4588
|
+
const sourceCapPercent = config.sourceCapPercent ?? 0.2;
|
|
4589
|
+
const fitnessThreshold = config.fitnessThreshold ?? 0.3;
|
|
4590
|
+
const personalCount = existing.filter((e) => !e.pack).length;
|
|
4591
|
+
if (personalCount > 0) {
|
|
4592
|
+
const fromSender = existing.filter((e) => e.provenance?.origin === packet.sender).length;
|
|
4593
|
+
if (fromSender / personalCount > sourceCapPercent) {
|
|
4594
|
+
return {
|
|
4595
|
+
imported: 0,
|
|
4596
|
+
skipped_fitness: 0,
|
|
4597
|
+
skipped_duplicate: 0,
|
|
4598
|
+
skipped_source_cap: true,
|
|
4599
|
+
candidates: []
|
|
4600
|
+
};
|
|
4601
|
+
}
|
|
4602
|
+
}
|
|
4603
|
+
const existingNormalized = existing.map((e) => normalizeStatement(e.statement));
|
|
4604
|
+
let skippedFitness = 0;
|
|
4605
|
+
let skippedDuplicate = 0;
|
|
4606
|
+
const candidates = [];
|
|
4607
|
+
const now = /* @__PURE__ */ new Date();
|
|
4608
|
+
const today = now.toISOString().split("T")[0];
|
|
4609
|
+
const trialExpiry = new Date(now);
|
|
4610
|
+
trialExpiry.setDate(trialExpiry.getDate() + 30);
|
|
4611
|
+
const trialExpiryTag = `_trial_expires:${trialExpiry.toISOString().split("T")[0]}`;
|
|
4612
|
+
for (const lepEngram of packet.engrams) {
|
|
4613
|
+
if (lepEngram.fitness < fitnessThreshold) {
|
|
4614
|
+
skippedFitness++;
|
|
4615
|
+
continue;
|
|
4616
|
+
}
|
|
4617
|
+
const normalized = normalizeStatement(lepEngram.statement);
|
|
4618
|
+
let isDuplicate = false;
|
|
4619
|
+
for (const existingNorm of existingNormalized) {
|
|
4620
|
+
const dist = levenshteinDistance(normalized, existingNorm);
|
|
4621
|
+
const maxLen = Math.max(normalized.length, existingNorm.length);
|
|
4622
|
+
if (maxLen > 0 && dist / maxLen < 0.15) {
|
|
4623
|
+
isDuplicate = true;
|
|
4624
|
+
break;
|
|
4625
|
+
}
|
|
4626
|
+
}
|
|
4627
|
+
if (isDuplicate) {
|
|
4628
|
+
skippedDuplicate++;
|
|
4629
|
+
continue;
|
|
4630
|
+
}
|
|
4631
|
+
const newId = generateEngramId(existing);
|
|
4632
|
+
candidates.push({
|
|
4633
|
+
id: newId,
|
|
4634
|
+
statement: lepEngram.statement,
|
|
4635
|
+
fitness: lepEngram.fitness
|
|
4636
|
+
});
|
|
4637
|
+
const newEngram = {
|
|
4638
|
+
id: newId,
|
|
4639
|
+
version: 2,
|
|
4640
|
+
status: "candidate",
|
|
4641
|
+
consolidated: false,
|
|
4642
|
+
type: lepEngram.type,
|
|
4643
|
+
scope: lepEngram.scope,
|
|
4644
|
+
visibility: "private",
|
|
4645
|
+
statement: lepEngram.statement,
|
|
4646
|
+
rationale: lepEngram.rationale,
|
|
4647
|
+
derivation_count: 1,
|
|
4648
|
+
domain: lepEngram.domain,
|
|
4649
|
+
knowledge_anchors: [],
|
|
4650
|
+
associations: [],
|
|
4651
|
+
tags: [...lepEngram.tags, "_trial", trialExpiryTag],
|
|
4652
|
+
activation: {
|
|
4653
|
+
retrieval_strength: 0.5,
|
|
4654
|
+
storage_strength: 0.3,
|
|
4655
|
+
frequency: 0,
|
|
4656
|
+
last_accessed: today
|
|
4657
|
+
},
|
|
4658
|
+
pack: packet.id,
|
|
4659
|
+
abstract: null,
|
|
4660
|
+
derived_from: lepEngram.id,
|
|
4661
|
+
provenance: {
|
|
4662
|
+
origin: lepEngram.provenance?.origin ?? packet.sender,
|
|
4663
|
+
chain: [...lepEngram.provenance?.chain ?? [], packet.id],
|
|
4664
|
+
signature: null,
|
|
4665
|
+
license: "cc-by-sa-4.0"
|
|
4666
|
+
}
|
|
4667
|
+
};
|
|
4668
|
+
existing.push(newEngram);
|
|
4669
|
+
existingNormalized.push(normalized);
|
|
4670
|
+
}
|
|
4671
|
+
return {
|
|
4672
|
+
imported: candidates.length,
|
|
4673
|
+
skipped_fitness: skippedFitness,
|
|
4674
|
+
skipped_duplicate: skippedDuplicate,
|
|
4675
|
+
skipped_source_cap: false,
|
|
4676
|
+
candidates
|
|
4677
|
+
};
|
|
4678
|
+
}
|
|
4679
|
+
|
|
4680
|
+
// src/tools/exchange.ts
|
|
4681
|
+
async function handleExchange(args2, paths, engagementService2) {
|
|
4682
|
+
switch (args2.action) {
|
|
4683
|
+
case "export":
|
|
4684
|
+
return exportAction(args2, paths, engagementService2);
|
|
4685
|
+
case "import":
|
|
4686
|
+
return importAction(args2, paths);
|
|
4687
|
+
case "status":
|
|
4688
|
+
return statusAction(paths);
|
|
4689
|
+
default:
|
|
4690
|
+
throw new Error(`Unknown exchange action: ${args2.action}`);
|
|
4691
|
+
}
|
|
4692
|
+
}
|
|
4693
|
+
async function exportAction(args2, paths, engagementService2) {
|
|
4694
|
+
const allEngrams = loadEngrams(paths.engramsPath);
|
|
4695
|
+
const sender = args2.sender ?? "anonymous";
|
|
4696
|
+
let candidates = allEngrams.filter(
|
|
4697
|
+
(e) => !e.pack && e.status === "active" && (e.visibility === "public" || e.visibility === "template")
|
|
4698
|
+
);
|
|
4699
|
+
if (args2.engram_ids?.length) {
|
|
4700
|
+
const idSet = new Set(args2.engram_ids);
|
|
4701
|
+
candidates = candidates.filter((e) => idSet.has(e.id));
|
|
4702
|
+
}
|
|
4703
|
+
if (args2.filter_domain) {
|
|
4704
|
+
candidates = candidates.filter((e) => e.domain?.startsWith(args2.filter_domain));
|
|
4705
|
+
}
|
|
4706
|
+
if (candidates.length === 0) {
|
|
4707
|
+
return {
|
|
4708
|
+
exported: 0,
|
|
4709
|
+
message: "No eligible engrams found. Only public/template visibility engrams can be exported.",
|
|
4710
|
+
_hints: buildHints({ next: "Set visibility to public or template on engrams you want to share.", related: ["datacore.learn"] })
|
|
4711
|
+
};
|
|
4712
|
+
}
|
|
4713
|
+
const packet = createLEPPacket(candidates, allEngrams, sender);
|
|
4714
|
+
fs21.mkdirSync(paths.exchangeOutboxPath, { recursive: true });
|
|
4715
|
+
const outPath = path19.join(paths.exchangeOutboxPath, `${packet.id}.yaml`);
|
|
4716
|
+
fs21.writeFileSync(outPath, yaml10.dump(packet, { lineWidth: 120, noRefs: true, quotingType: '"' }));
|
|
4717
|
+
if (engagementService2?.isEnabled()) {
|
|
4718
|
+
try {
|
|
4719
|
+
await engagementService2.award("pack_exported", { count: packet.engrams.length });
|
|
4720
|
+
} catch {
|
|
4721
|
+
}
|
|
4722
|
+
}
|
|
4723
|
+
return {
|
|
4724
|
+
exported: packet.engrams.length,
|
|
4725
|
+
packet_id: packet.id,
|
|
4726
|
+
path: outPath,
|
|
4727
|
+
_hints: buildHints({ next: `Exported ${packet.engrams.length} engram(s) to ${outPath}`, related: ["datacore.exchange"] })
|
|
4728
|
+
};
|
|
4729
|
+
}
|
|
4730
|
+
async function importAction(args2, paths) {
|
|
4731
|
+
if (!args2.path) throw new Error("path is required for import action");
|
|
4732
|
+
if (!fs21.existsSync(args2.path)) throw new Error(`File not found: ${args2.path}`);
|
|
4733
|
+
const raw = yaml10.load(fs21.readFileSync(args2.path, "utf8"));
|
|
4734
|
+
const packet = validateLEPPacket(raw);
|
|
4735
|
+
const existing = loadEngrams(paths.engramsPath);
|
|
4736
|
+
const result = importLEPEngrams(packet, existing, {
|
|
4737
|
+
fitnessThreshold: args2.fitness_threshold,
|
|
4738
|
+
sourceCapPercent: args2.source_cap_percent
|
|
4739
|
+
});
|
|
4740
|
+
if (result.skipped_source_cap) {
|
|
4741
|
+
return {
|
|
4742
|
+
imported: 0,
|
|
4743
|
+
message: `Source cap exceeded: too many engrams already from ${packet.sender}.`,
|
|
4744
|
+
_hints: buildHints({ next: "Remove some imported engrams from this source first.", related: ["datacore.forget"] })
|
|
4745
|
+
};
|
|
4746
|
+
}
|
|
4747
|
+
if (args2.confirm && result.imported > 0) {
|
|
4748
|
+
saveEngrams(paths.engramsPath, existing);
|
|
4749
|
+
return {
|
|
4750
|
+
imported: result.imported,
|
|
4751
|
+
skipped_fitness: result.skipped_fitness,
|
|
4752
|
+
skipped_duplicate: result.skipped_duplicate,
|
|
4753
|
+
candidates: result.candidates,
|
|
4754
|
+
_hints: buildHints({
|
|
4755
|
+
next: `Imported ${result.imported} engram(s) as candidates. Use datacore.promote to activate.`,
|
|
4756
|
+
related: ["datacore.promote"]
|
|
4757
|
+
})
|
|
4758
|
+
};
|
|
4759
|
+
}
|
|
4760
|
+
return {
|
|
4761
|
+
action: "preview",
|
|
4762
|
+
would_import: result.imported,
|
|
4763
|
+
skipped_fitness: result.skipped_fitness,
|
|
4764
|
+
skipped_duplicate: result.skipped_duplicate,
|
|
4765
|
+
candidates: result.candidates,
|
|
4766
|
+
_hints: buildHints({
|
|
4767
|
+
next: result.imported > 0 ? `Preview: ${result.imported} engram(s) would be imported. Set confirm=true to execute.` : "No engrams passed fitness and duplicate filters.",
|
|
4768
|
+
related: ["datacore.exchange"]
|
|
4769
|
+
})
|
|
4770
|
+
};
|
|
4771
|
+
}
|
|
4772
|
+
function statusAction(paths) {
|
|
4773
|
+
const inboxCount = countYamlFiles(paths.exchangeInboxPath);
|
|
4774
|
+
const outboxCount = countYamlFiles(paths.exchangeOutboxPath);
|
|
4775
|
+
return {
|
|
4776
|
+
inbox: inboxCount,
|
|
4777
|
+
outbox: outboxCount,
|
|
4778
|
+
_hints: buildHints({
|
|
4779
|
+
next: inboxCount > 0 ? `${inboxCount} packet(s) in inbox. Use action="import" with path to process.` : "No packets in inbox.",
|
|
4780
|
+
related: ["datacore.exchange"]
|
|
4781
|
+
})
|
|
4782
|
+
};
|
|
4783
|
+
}
|
|
4784
|
+
function countYamlFiles(dir) {
|
|
4785
|
+
if (!fs21.existsSync(dir)) return 0;
|
|
4786
|
+
return fs21.readdirSync(dir).filter((f) => f.endsWith(".yaml")).length;
|
|
4787
|
+
}
|
|
4788
|
+
|
|
4789
|
+
// src/knowledge-surfacing.ts
|
|
4790
|
+
import * as fs22 from "fs";
|
|
4791
|
+
import * as path20 from "path";
|
|
4792
|
+
import * as yaml11 from "js-yaml";
|
|
4793
|
+
import { z as z7 } from "zod";
|
|
4794
|
+
var ZettelCandidateSchema = z7.object({
|
|
4795
|
+
path: z7.string(),
|
|
4796
|
+
title: z7.string(),
|
|
4797
|
+
suggested_statement: z7.string(),
|
|
4798
|
+
suggested_type: z7.enum(["behavioral", "terminological", "procedural", "architectural"]),
|
|
4799
|
+
suggested_scope: z7.string(),
|
|
4800
|
+
suggested_anchors: z7.array(z7.string()),
|
|
4801
|
+
confidence: z7.number().min(0).max(1),
|
|
4802
|
+
status: z7.enum(["pending", "accepted", "rejected"]),
|
|
4803
|
+
created: z7.string()
|
|
4804
|
+
});
|
|
4805
|
+
var defaultState = {
|
|
4806
|
+
last_zettel_scan: null,
|
|
4807
|
+
scanned_paths: [],
|
|
4808
|
+
zettel_candidates: [],
|
|
4809
|
+
last_consolidation: null,
|
|
4810
|
+
consolidation_results: null
|
|
4811
|
+
};
|
|
4812
|
+
function loadKnowledgeSurfacing(filePath) {
|
|
4813
|
+
if (!fs22.existsSync(filePath)) return { ...defaultState };
|
|
4814
|
+
try {
|
|
4815
|
+
const raw = yaml11.load(fs22.readFileSync(filePath, "utf8"));
|
|
4816
|
+
return { ...defaultState, ...raw };
|
|
4817
|
+
} catch {
|
|
4818
|
+
return { ...defaultState };
|
|
4819
|
+
}
|
|
4820
|
+
}
|
|
4821
|
+
function saveKnowledgeSurfacing(filePath, state) {
|
|
4822
|
+
const dir = path20.dirname(filePath);
|
|
4823
|
+
if (!fs22.existsSync(dir)) fs22.mkdirSync(dir, { recursive: true });
|
|
4824
|
+
const content = yaml11.dump(state, { lineWidth: 120, noRefs: true, quotingType: '"' });
|
|
4825
|
+
const tmpPath = filePath + ".tmp." + process.pid;
|
|
4826
|
+
fs22.writeFileSync(tmpPath, content);
|
|
4827
|
+
fs22.renameSync(tmpPath, filePath);
|
|
4828
|
+
}
|
|
4829
|
+
var ACTIONABLE_WORDS = /\b(should|always|never|when|before|after|must|avoid|prefer|ensure|make sure)\b/i;
|
|
4830
|
+
var PROCEDURAL_PATTERNS = /(\d+\.\s|\bhow to\b|step \d+)/i;
|
|
4831
|
+
var DECISION_PATTERNS = /\b(because|due to|trade-?off|we chose|decided|rationale)\b/i;
|
|
4832
|
+
function scanZettels(knowledgePath, state) {
|
|
4833
|
+
const zettelDir = path20.join(knowledgePath, "zettel");
|
|
4834
|
+
if (!fs22.existsSync(zettelDir)) return [];
|
|
4835
|
+
const scannedSet = new Set(state.scanned_paths);
|
|
4836
|
+
const candidates = [];
|
|
4837
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
4838
|
+
const files = globMd(zettelDir);
|
|
4839
|
+
for (const filePath of files) {
|
|
4840
|
+
const relativePath = path20.relative(path20.dirname(knowledgePath), filePath);
|
|
4841
|
+
if (scannedSet.has(relativePath)) continue;
|
|
4842
|
+
const content = fs22.readFileSync(filePath, "utf8");
|
|
4843
|
+
const candidate = analyzeZettel(content, relativePath, today);
|
|
4844
|
+
if (candidate) candidates.push(candidate);
|
|
4845
|
+
state.scanned_paths.push(relativePath);
|
|
4846
|
+
}
|
|
4847
|
+
state.last_zettel_scan = today;
|
|
4848
|
+
return candidates;
|
|
4849
|
+
}
|
|
4850
|
+
function globMd(dir) {
|
|
4851
|
+
const results = [];
|
|
4852
|
+
if (!fs22.existsSync(dir)) return results;
|
|
4853
|
+
for (const entry of fs22.readdirSync(dir, { withFileTypes: true })) {
|
|
4854
|
+
const full = path20.join(dir, entry.name);
|
|
4855
|
+
if (entry.isDirectory()) {
|
|
4856
|
+
results.push(...globMd(full));
|
|
4857
|
+
} else if (entry.name.endsWith(".md")) {
|
|
4858
|
+
results.push(full);
|
|
4859
|
+
}
|
|
4860
|
+
}
|
|
4861
|
+
return results;
|
|
4862
|
+
}
|
|
4863
|
+
function analyzeZettel(content, relativePath, today) {
|
|
4864
|
+
const lines = content.split("\n");
|
|
4865
|
+
const title = extractTitle2(lines) ?? path20.basename(relativePath, ".md");
|
|
4866
|
+
const hasActionable = ACTIONABLE_WORDS.test(content);
|
|
4867
|
+
const hasProcedural = PROCEDURAL_PATTERNS.test(content);
|
|
4868
|
+
const hasDecision = DECISION_PATTERNS.test(content);
|
|
4869
|
+
if (!hasActionable && !hasProcedural && !hasDecision) return null;
|
|
4870
|
+
let type = "behavioral";
|
|
4871
|
+
if (hasProcedural) type = "procedural";
|
|
4872
|
+
else if (hasDecision) type = "architectural";
|
|
4873
|
+
let confidence = 0.4;
|
|
4874
|
+
if (hasActionable) confidence += 0.3;
|
|
4875
|
+
if (hasProcedural || hasDecision) confidence += 0.1;
|
|
4876
|
+
confidence = Math.min(confidence, 1);
|
|
4877
|
+
if (confidence < 0.4) return null;
|
|
4878
|
+
const statement = extractActionableSentence(content) ?? `${title}: ${firstParagraph(content)}`;
|
|
4879
|
+
return {
|
|
4880
|
+
path: relativePath,
|
|
4881
|
+
title,
|
|
4882
|
+
suggested_statement: statement.slice(0, 300),
|
|
4883
|
+
suggested_type: type,
|
|
4884
|
+
suggested_scope: "global",
|
|
4885
|
+
suggested_anchors: [relativePath],
|
|
4886
|
+
confidence: Math.round(confidence * 100) / 100,
|
|
4887
|
+
status: "pending",
|
|
4888
|
+
created: today
|
|
4889
|
+
};
|
|
4890
|
+
}
|
|
4891
|
+
function extractTitle2(lines) {
|
|
4892
|
+
for (const line of lines) {
|
|
4893
|
+
if (line.startsWith("# ")) return line.slice(2).trim();
|
|
4894
|
+
}
|
|
4895
|
+
return null;
|
|
4896
|
+
}
|
|
4897
|
+
function extractActionableSentence(content) {
|
|
4898
|
+
const sentences = content.split(/(?<=[.!?])\s+/);
|
|
4899
|
+
for (const sentence of sentences) {
|
|
4900
|
+
if (ACTIONABLE_WORDS.test(sentence) && sentence.length > 20) {
|
|
4901
|
+
return sentence.replace(/^[-*#>\s]+/, "").trim();
|
|
4902
|
+
}
|
|
4903
|
+
}
|
|
4904
|
+
return null;
|
|
4905
|
+
}
|
|
4906
|
+
function firstParagraph(content) {
|
|
4907
|
+
const lines = content.split("\n");
|
|
4908
|
+
const para = [];
|
|
4909
|
+
let started = false;
|
|
4910
|
+
for (const line of lines) {
|
|
4911
|
+
if (line.startsWith("#")) continue;
|
|
4912
|
+
if (line.trim() === "") {
|
|
4913
|
+
if (started) break;
|
|
4914
|
+
continue;
|
|
4915
|
+
}
|
|
4916
|
+
started = true;
|
|
4917
|
+
para.push(line.trim());
|
|
4918
|
+
}
|
|
4919
|
+
return para.join(" ").slice(0, 200);
|
|
4920
|
+
}
|
|
4921
|
+
function normalizeStatement2(s) {
|
|
4922
|
+
return s.toLowerCase().replace(/[^\w\s]/g, "").replace(/\s+/g, " ").trim();
|
|
4923
|
+
}
|
|
4924
|
+
function consolidationPass(engramsPath, confirm = false) {
|
|
4925
|
+
const engrams = loadEngrams(engramsPath);
|
|
4926
|
+
const active = engrams.filter((e) => e.status === "active" && !e.pack);
|
|
4927
|
+
const thirtyDaysAgo = /* @__PURE__ */ new Date();
|
|
4928
|
+
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
|
|
4929
|
+
const thirtyDaysAgoStr = thirtyDaysAgo.toISOString().split("T")[0];
|
|
4930
|
+
const lowRs = active.filter(
|
|
4931
|
+
(e) => e.activation.retrieval_strength < 0.15 && e.activation.last_accessed < thirtyDaysAgoStr
|
|
4932
|
+
).map((e) => ({
|
|
4933
|
+
id: e.id,
|
|
4934
|
+
rs: e.activation.retrieval_strength,
|
|
4935
|
+
last_accessed: e.activation.last_accessed
|
|
4936
|
+
}));
|
|
4937
|
+
const normalized = active.map((e) => ({
|
|
4938
|
+
id: e.id,
|
|
4939
|
+
norm: normalizeStatement2(e.statement),
|
|
4940
|
+
rs: e.activation.retrieval_strength
|
|
4941
|
+
}));
|
|
4942
|
+
const parent = /* @__PURE__ */ new Map();
|
|
4943
|
+
const find = (x) => {
|
|
4944
|
+
let root = x;
|
|
4945
|
+
while (parent.has(root) && parent.get(root) !== root) root = parent.get(root);
|
|
4946
|
+
let curr = x;
|
|
4947
|
+
while (curr !== root) {
|
|
4948
|
+
const next = parent.get(curr) ?? curr;
|
|
4949
|
+
parent.set(curr, root);
|
|
4950
|
+
curr = next;
|
|
4951
|
+
}
|
|
4952
|
+
return root;
|
|
4953
|
+
};
|
|
4954
|
+
const union = (a, b) => {
|
|
4955
|
+
const ra = find(a);
|
|
4956
|
+
const rb = find(b);
|
|
4957
|
+
if (ra !== rb) parent.set(ra, rb);
|
|
4958
|
+
};
|
|
4959
|
+
for (const item of normalized) parent.set(item.id, item.id);
|
|
4960
|
+
for (let i = 0; i < normalized.length; i++) {
|
|
4961
|
+
for (let j = i + 1; j < normalized.length; j++) {
|
|
4962
|
+
const a = normalized[i];
|
|
4963
|
+
const b = normalized[j];
|
|
4964
|
+
const maxLen = Math.max(a.norm.length, b.norm.length);
|
|
4965
|
+
if (maxLen === 0) continue;
|
|
4966
|
+
const dist = levenshteinDistance(a.norm, b.norm);
|
|
4967
|
+
if (dist / maxLen < 0.3) {
|
|
4968
|
+
union(a.id, b.id);
|
|
4969
|
+
}
|
|
4970
|
+
}
|
|
4971
|
+
}
|
|
4972
|
+
const clusters = /* @__PURE__ */ new Map();
|
|
4973
|
+
for (const item of normalized) {
|
|
4974
|
+
const root = find(item.id);
|
|
4975
|
+
if (!clusters.has(root)) clusters.set(root, []);
|
|
4976
|
+
clusters.get(root).push(item.id);
|
|
4977
|
+
}
|
|
4978
|
+
const duplicateClusters = [];
|
|
4979
|
+
for (const [, members] of clusters) {
|
|
4980
|
+
if (members.length < 2) continue;
|
|
4981
|
+
const sorted = members.map((id) => ({ id, rs: normalized.find((n) => n.id === id).rs })).sort((a, b) => b.rs - a.rs);
|
|
4982
|
+
duplicateClusters.push({
|
|
4983
|
+
representative: sorted[0].id,
|
|
4984
|
+
duplicates: sorted.slice(1).map((s) => s.id),
|
|
4985
|
+
similarity: 0.7
|
|
4986
|
+
// approximate
|
|
4987
|
+
});
|
|
4988
|
+
}
|
|
4989
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
4990
|
+
if (confirm) {
|
|
4991
|
+
const toRetire = /* @__PURE__ */ new Set([
|
|
4992
|
+
...lowRs.map((e) => e.id),
|
|
4993
|
+
...duplicateClusters.flatMap((c) => c.duplicates)
|
|
4994
|
+
]);
|
|
4995
|
+
for (const engram of engrams) {
|
|
4996
|
+
if (toRetire.has(engram.id)) {
|
|
4997
|
+
engram.status = "retired";
|
|
4998
|
+
}
|
|
4999
|
+
}
|
|
5000
|
+
for (const cluster of duplicateClusters) {
|
|
5001
|
+
const rep = engrams.find((e) => e.id === cluster.representative);
|
|
5002
|
+
if (rep) rep.derivation_count += cluster.duplicates.length;
|
|
5003
|
+
}
|
|
5004
|
+
if (toRetire.size > 0) {
|
|
5005
|
+
const content = yaml11.dump({ engrams }, { lineWidth: 120, noRefs: true, quotingType: '"' });
|
|
5006
|
+
const tmpPath = engramsPath + ".tmp." + process.pid;
|
|
5007
|
+
fs22.writeFileSync(tmpPath, content);
|
|
5008
|
+
fs22.renameSync(tmpPath, engramsPath);
|
|
5009
|
+
}
|
|
5010
|
+
}
|
|
5011
|
+
return {
|
|
5012
|
+
low_rs_engrams: lowRs,
|
|
5013
|
+
duplicate_clusters: duplicateClusters,
|
|
5014
|
+
action_taken: confirm ? "executed" : "preview",
|
|
5015
|
+
executed_at: confirm ? today : null
|
|
5016
|
+
};
|
|
5017
|
+
}
|
|
5018
|
+
|
|
5019
|
+
// src/tools/knowledge-scan.ts
|
|
5020
|
+
async function handleKnowledgeScan(args2, paths) {
|
|
5021
|
+
switch (args2.action) {
|
|
5022
|
+
case "scan_zettels":
|
|
5023
|
+
return scanZettelsAction(paths);
|
|
5024
|
+
case "scan_status":
|
|
5025
|
+
return scanStatusAction(paths.knowledgeSurfacingPath);
|
|
5026
|
+
case "consolidation_pass":
|
|
5027
|
+
return consolidationAction(paths, args2.confirm);
|
|
5028
|
+
default:
|
|
5029
|
+
throw new Error(`Unknown knowledge.scan action: ${args2.action}`);
|
|
5030
|
+
}
|
|
5031
|
+
}
|
|
5032
|
+
function scanZettelsAction(paths) {
|
|
5033
|
+
const state = loadKnowledgeSurfacing(paths.knowledgeSurfacingPath);
|
|
5034
|
+
const candidates = scanZettels(paths.knowledgePath, state);
|
|
5035
|
+
state.zettel_candidates.push(...candidates);
|
|
5036
|
+
saveKnowledgeSurfacing(paths.knowledgeSurfacingPath, state);
|
|
5037
|
+
return {
|
|
5038
|
+
new_candidates: candidates.length,
|
|
5039
|
+
total_scanned: state.scanned_paths.length,
|
|
5040
|
+
total_pending: state.zettel_candidates.filter((c) => c.status === "pending").length,
|
|
5041
|
+
candidates: candidates.map((c) => ({
|
|
5042
|
+
path: c.path,
|
|
5043
|
+
title: c.title,
|
|
5044
|
+
suggested_statement: c.suggested_statement,
|
|
5045
|
+
suggested_type: c.suggested_type,
|
|
5046
|
+
confidence: c.confidence
|
|
5047
|
+
})),
|
|
5048
|
+
_hints: buildHints({
|
|
5049
|
+
next: candidates.length > 0 ? `Found ${candidates.length} zettel-to-engram candidate(s). Review and use datacore.learn to create engrams.` : "No new candidates found.",
|
|
5050
|
+
related: ["datacore.learn", "datacore.knowledge.scan"]
|
|
5051
|
+
})
|
|
5052
|
+
};
|
|
5053
|
+
}
|
|
5054
|
+
function scanStatusAction(surfacingPath) {
|
|
5055
|
+
const state = loadKnowledgeSurfacing(surfacingPath);
|
|
5056
|
+
const pending = state.zettel_candidates.filter((c) => c.status === "pending").length;
|
|
5057
|
+
const accepted = state.zettel_candidates.filter((c) => c.status === "accepted").length;
|
|
5058
|
+
const rejected = state.zettel_candidates.filter((c) => c.status === "rejected").length;
|
|
5059
|
+
return {
|
|
5060
|
+
last_scan: state.last_zettel_scan,
|
|
5061
|
+
scanned_paths: state.scanned_paths.length,
|
|
5062
|
+
candidates: { pending, accepted, rejected, total: state.zettel_candidates.length },
|
|
5063
|
+
last_consolidation: state.last_consolidation,
|
|
5064
|
+
_hints: buildHints({
|
|
5065
|
+
next: pending > 0 ? `${pending} pending candidate(s). Use scan_zettels to find more, or review candidates.` : "All candidates processed.",
|
|
5066
|
+
related: ["datacore.knowledge.scan"]
|
|
5067
|
+
})
|
|
5068
|
+
};
|
|
5069
|
+
}
|
|
5070
|
+
function consolidationAction(paths, confirm) {
|
|
5071
|
+
const result = consolidationPass(paths.engramsPath, confirm ?? false);
|
|
5072
|
+
const state = loadKnowledgeSurfacing(paths.knowledgeSurfacingPath);
|
|
5073
|
+
state.last_consolidation = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
5074
|
+
state.consolidation_results = result;
|
|
5075
|
+
saveKnowledgeSurfacing(paths.knowledgeSurfacingPath, state);
|
|
5076
|
+
return {
|
|
5077
|
+
low_rs_count: result.low_rs_engrams.length,
|
|
5078
|
+
duplicate_clusters: result.duplicate_clusters.length,
|
|
5079
|
+
action: result.action_taken,
|
|
5080
|
+
low_rs_engrams: result.low_rs_engrams.slice(0, 10),
|
|
5081
|
+
clusters: result.duplicate_clusters.slice(0, 5).map((c) => ({
|
|
5082
|
+
representative: c.representative,
|
|
5083
|
+
duplicates: c.duplicates.length
|
|
5084
|
+
})),
|
|
5085
|
+
_hints: buildHints({
|
|
5086
|
+
next: result.action_taken === "preview" ? `Preview: ${result.low_rs_engrams.length} low-RS + ${result.duplicate_clusters.length} duplicate cluster(s). Set confirm=true to execute.` : `Consolidated: retired ${result.low_rs_engrams.length + result.duplicate_clusters.flatMap((c) => c.duplicates).length} engram(s).`,
|
|
5087
|
+
related: ["datacore.knowledge.scan"]
|
|
5088
|
+
})
|
|
5089
|
+
};
|
|
5090
|
+
}
|
|
5091
|
+
|
|
3916
5092
|
// src/resources.ts
|
|
3917
5093
|
import {
|
|
3918
5094
|
ListResourcesRequestSchema,
|
|
3919
5095
|
ReadResourceRequestSchema,
|
|
3920
5096
|
ListResourceTemplatesRequestSchema
|
|
3921
5097
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
3922
|
-
import * as
|
|
3923
|
-
import * as
|
|
5098
|
+
import * as fs23 from "fs";
|
|
5099
|
+
import * as path21 from "path";
|
|
3924
5100
|
function registerResources(server, storage2) {
|
|
3925
5101
|
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
|
|
3926
5102
|
resources: [
|
|
@@ -4001,11 +5177,11 @@ function registerResources(server, storage2) {
|
|
|
4001
5177
|
const journalMatch = uri.match(/^datacore:\/\/journal\/(.+)$/);
|
|
4002
5178
|
if (journalMatch) {
|
|
4003
5179
|
const dateStr = journalMatch[1] === "today" ? localDate().date : journalMatch[1];
|
|
4004
|
-
const filePath =
|
|
4005
|
-
if (!
|
|
5180
|
+
const filePath = path21.join(storage2.journalPath, `${dateStr}.md`);
|
|
5181
|
+
if (!fs23.existsSync(filePath)) {
|
|
4006
5182
|
return { contents: [{ uri, mimeType: "text/markdown", text: `No journal entry for ${dateStr}` }] };
|
|
4007
5183
|
}
|
|
4008
|
-
return { contents: [{ uri, mimeType: "text/markdown", text:
|
|
5184
|
+
return { contents: [{ uri, mimeType: "text/markdown", text: fs23.readFileSync(filePath, "utf8") }] };
|
|
4009
5185
|
}
|
|
4010
5186
|
const engramMatch = uri.match(/^datacore:\/\/engrams\/(.+)$/);
|
|
4011
5187
|
if (engramMatch) {
|
|
@@ -4251,8 +5427,8 @@ function registerPrompts(server) {
|
|
|
4251
5427
|
|
|
4252
5428
|
// src/datacortex.ts
|
|
4253
5429
|
import { execFile } from "child_process";
|
|
4254
|
-
import * as
|
|
4255
|
-
import * as
|
|
5430
|
+
import * as fs24 from "fs";
|
|
5431
|
+
import * as path22 from "path";
|
|
4256
5432
|
var DatacortexBridge = class {
|
|
4257
5433
|
pythonPath;
|
|
4258
5434
|
scriptPath;
|
|
@@ -4262,11 +5438,11 @@ var DatacortexBridge = class {
|
|
|
4262
5438
|
}
|
|
4263
5439
|
findBridgeScript(datacorePath) {
|
|
4264
5440
|
const candidates = [
|
|
4265
|
-
|
|
4266
|
-
|
|
5441
|
+
path22.join(datacorePath, ".datacore", "modules", "datacortex", "lib", "bridge.py"),
|
|
5442
|
+
path22.join(datacorePath, ".datacore", "modules", "datacortex", "bridge.py")
|
|
4267
5443
|
];
|
|
4268
5444
|
for (const candidate of candidates) {
|
|
4269
|
-
if (
|
|
5445
|
+
if (fs24.existsSync(candidate)) return candidate;
|
|
4270
5446
|
}
|
|
4271
5447
|
return null;
|
|
4272
5448
|
}
|
|
@@ -4318,6 +5494,44 @@ var DatacortexBridge = class {
|
|
|
4318
5494
|
}
|
|
4319
5495
|
};
|
|
4320
5496
|
|
|
5497
|
+
// src/session-tracker.ts
|
|
5498
|
+
var SessionTracker = class {
|
|
5499
|
+
sessions = /* @__PURE__ */ new Map();
|
|
5500
|
+
trackInjected(sessionId, engramIds) {
|
|
5501
|
+
if (!sessionId || engramIds.length === 0) return;
|
|
5502
|
+
let set = this.sessions.get(sessionId);
|
|
5503
|
+
if (!set) {
|
|
5504
|
+
set = /* @__PURE__ */ new Set();
|
|
5505
|
+
this.sessions.set(sessionId, set);
|
|
5506
|
+
}
|
|
5507
|
+
for (const id of engramIds) {
|
|
5508
|
+
set.add(id);
|
|
5509
|
+
}
|
|
5510
|
+
}
|
|
5511
|
+
getCoAccessPairs(sessionId) {
|
|
5512
|
+
const set = this.sessions.get(sessionId);
|
|
5513
|
+
if (!set || set.size < 2) return [];
|
|
5514
|
+
const ids = Array.from(set).sort();
|
|
5515
|
+
const pairs = [];
|
|
5516
|
+
for (let i = 0; i < ids.length; i++) {
|
|
5517
|
+
for (let j = i + 1; j < ids.length; j++) {
|
|
5518
|
+
pairs.push([ids[i], ids[j]]);
|
|
5519
|
+
}
|
|
5520
|
+
}
|
|
5521
|
+
return pairs;
|
|
5522
|
+
}
|
|
5523
|
+
getInjectedIds(sessionId) {
|
|
5524
|
+
const set = this.sessions.get(sessionId);
|
|
5525
|
+
return set ? Array.from(set) : [];
|
|
5526
|
+
}
|
|
5527
|
+
clear(sessionId) {
|
|
5528
|
+
this.sessions.delete(sessionId);
|
|
5529
|
+
}
|
|
5530
|
+
get size() {
|
|
5531
|
+
return this.sessions.size;
|
|
5532
|
+
}
|
|
5533
|
+
};
|
|
5534
|
+
|
|
4321
5535
|
// src/server.ts
|
|
4322
5536
|
var storage;
|
|
4323
5537
|
var updateAvailable = null;
|
|
@@ -4327,6 +5541,7 @@ var isFirstRun = false;
|
|
|
4327
5541
|
var serverRef = null;
|
|
4328
5542
|
var datacortexBridge = null;
|
|
4329
5543
|
var engagementService = null;
|
|
5544
|
+
var sessionTracker = new SessionTracker();
|
|
4330
5545
|
function getEngagementService() {
|
|
4331
5546
|
if (!engagementService || engagementService.basePath !== storage.basePath) {
|
|
4332
5547
|
const config = getConfig();
|
|
@@ -4382,7 +5597,7 @@ function createServer() {
|
|
|
4382
5597
|
serverRef = server;
|
|
4383
5598
|
return server;
|
|
4384
5599
|
}
|
|
4385
|
-
var ENGRAM_MUTATING_TOOLS = /* @__PURE__ */ new Set(["datacore.learn", "datacore.forget", "datacore.feedback", "datacore.session.end", "datacore.promote", "datacore.resolve"]);
|
|
5600
|
+
var ENGRAM_MUTATING_TOOLS = /* @__PURE__ */ new Set(["datacore.learn", "datacore.forget", "datacore.feedback", "datacore.session.end", "datacore.promote", "datacore.resolve", "datacore.schemas", "datacore.exchange", "datacore.knowledge.scan"]);
|
|
4386
5601
|
async function routeTool(name, args2) {
|
|
4387
5602
|
const coreTool = TOOLS.find((t) => t.name === name);
|
|
4388
5603
|
if (coreTool) {
|
|
@@ -4396,7 +5611,7 @@ async function routeTool(name, args2) {
|
|
|
4396
5611
|
result = await handleLearn(validated, storage.engramsPath, getEngagementService());
|
|
4397
5612
|
break;
|
|
4398
5613
|
case "datacore.inject":
|
|
4399
|
-
result = await handleInject(validated, { engramsPath: storage.engramsPath, packsPath: storage.packsPath, basePath: storage.basePath });
|
|
5614
|
+
result = await handleInject(validated, { engramsPath: storage.engramsPath, packsPath: storage.packsPath, basePath: storage.basePath, schemasPath: storage.schemasPath });
|
|
4400
5615
|
break;
|
|
4401
5616
|
case "datacore.search":
|
|
4402
5617
|
result = await handleSearch(validated, { journalPath: storage.journalPath, knowledgePath: storage.knowledgePath }, datacortexBridge);
|
|
@@ -4414,10 +5629,10 @@ async function routeTool(name, args2) {
|
|
|
4414
5629
|
result = await handleFeedback(validated, storage.engramsPath, storage.packsPath, getEngagementService());
|
|
4415
5630
|
break;
|
|
4416
5631
|
case "datacore.session.start":
|
|
4417
|
-
result = await handleSessionStart(validated, storage, datacortexBridge, getEngagementService());
|
|
5632
|
+
result = await handleSessionStart(validated, storage, datacortexBridge, getEngagementService(), sessionTracker);
|
|
4418
5633
|
break;
|
|
4419
5634
|
case "datacore.session.end":
|
|
4420
|
-
result = await handleSessionEnd(validated, storage, getEngagementService());
|
|
5635
|
+
result = await handleSessionEnd(validated, storage, getEngagementService(), sessionTracker);
|
|
4421
5636
|
break;
|
|
4422
5637
|
case "datacore.recall":
|
|
4423
5638
|
result = await handleRecall(validated, { engramsPath: storage.engramsPath, journalPath: storage.journalPath, knowledgePath: storage.knowledgePath }, datacortexBridge);
|
|
@@ -4434,6 +5649,15 @@ async function routeTool(name, args2) {
|
|
|
4434
5649
|
case "datacore.packs.export":
|
|
4435
5650
|
result = await handleExport(validated, { engramsPath: storage.engramsPath, packsPath: storage.packsPath }, getEngagementService());
|
|
4436
5651
|
break;
|
|
5652
|
+
case "datacore.knowledge.scan":
|
|
5653
|
+
result = await handleKnowledgeScan(validated, { knowledgePath: storage.knowledgePath, knowledgeSurfacingPath: storage.knowledgeSurfacingPath, engramsPath: storage.engramsPath });
|
|
5654
|
+
break;
|
|
5655
|
+
case "datacore.exchange":
|
|
5656
|
+
result = await handleExchange(validated, { engramsPath: storage.engramsPath, exchangeInboxPath: storage.exchangeInboxPath, exchangeOutboxPath: storage.exchangeOutboxPath }, getEngagementService());
|
|
5657
|
+
break;
|
|
5658
|
+
case "datacore.schemas":
|
|
5659
|
+
result = await handleSchemas(validated, { schemasPath: storage.schemasPath, engramsPath: storage.engramsPath });
|
|
5660
|
+
break;
|
|
4437
5661
|
case "datacore.resolve":
|
|
4438
5662
|
result = await handleResolve(validated, storage.engramsPath, getEngagementService());
|
|
4439
5663
|
break;
|
|
@@ -4452,6 +5676,13 @@ async function routeTool(name, args2) {
|
|
|
4452
5676
|
if (ENGRAM_MUTATING_TOOLS.has(name) && serverRef) {
|
|
4453
5677
|
notifyEngramsChanged(serverRef);
|
|
4454
5678
|
}
|
|
5679
|
+
if (name === "datacore.inject" && result && typeof result === "object") {
|
|
5680
|
+
const injectResult = result;
|
|
5681
|
+
const sessionId = validated.session_id;
|
|
5682
|
+
if (sessionId && injectResult.injected_personal_ids?.length) {
|
|
5683
|
+
sessionTracker.trackInjected(sessionId, injectResult.injected_personal_ids);
|
|
5684
|
+
}
|
|
5685
|
+
}
|
|
4455
5686
|
return result;
|
|
4456
5687
|
}
|
|
4457
5688
|
const modTool = moduleTools.find((t) => t.fullName === name);
|