@datacore-one/mcp 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -1
- package/dist/index.js +1527 -39
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
- package/packs/dips-v1/engrams.yaml +2241 -2241
package/dist/index.js
CHANGED
|
@@ -218,6 +218,10 @@ var ConfigSchema = z.object({
|
|
|
218
218
|
}).default({}),
|
|
219
219
|
hints: z.object({
|
|
220
220
|
enabled: z.boolean().default(true)
|
|
221
|
+
}).default({}),
|
|
222
|
+
engagement: z.object({
|
|
223
|
+
enabled: z.boolean().default(true),
|
|
224
|
+
inline_xp: z.boolean().default(false)
|
|
221
225
|
}).default({})
|
|
222
226
|
});
|
|
223
227
|
var cachedConfig = null;
|
|
@@ -242,7 +246,7 @@ function getConfig() {
|
|
|
242
246
|
// package.json
|
|
243
247
|
var package_default = {
|
|
244
248
|
name: "@datacore-one/mcp",
|
|
245
|
-
version: "1.
|
|
249
|
+
version: "1.3.0",
|
|
246
250
|
description: "Datacore MCP server \u2014 The Software of You",
|
|
247
251
|
type: "module",
|
|
248
252
|
bin: {
|
|
@@ -254,7 +258,8 @@ var package_default = {
|
|
|
254
258
|
dev: "tsup --watch",
|
|
255
259
|
test: "vitest run",
|
|
256
260
|
"test:watch": "vitest",
|
|
257
|
-
start: "node dist/index.js"
|
|
261
|
+
start: "node dist/index.js",
|
|
262
|
+
release: `npm test && npm run build && npm version \${VERSION:-patch} && npm publish --access public && npm install -g @datacore-one/mcp@$(node -p 'require("./package.json").version')`
|
|
258
263
|
},
|
|
259
264
|
dependencies: {
|
|
260
265
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
@@ -473,6 +478,16 @@ var TOOLS = [
|
|
|
473
478
|
inputSchema: z2.object({
|
|
474
479
|
module: z2.string().optional().describe("Module name (omit for all modules)")
|
|
475
480
|
})
|
|
481
|
+
},
|
|
482
|
+
{
|
|
483
|
+
name: "datacore.resolve",
|
|
484
|
+
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.",
|
|
485
|
+
inputSchema: z2.object({
|
|
486
|
+
type: z2.enum(["reconsolidation", "discovery", "challenge"]).describe("Type of event to resolve"),
|
|
487
|
+
id: z2.string().describe("Event ID (engram_id, discovery_id, or challenge_id)"),
|
|
488
|
+
action: z2.string().describe("Resolution action: defend|revise|retire|dismiss (recon), explore|note (discovery), dismiss (challenge)"),
|
|
489
|
+
revised_statement: z2.string().optional().describe('New statement text (required when action is "revise")')
|
|
490
|
+
})
|
|
476
491
|
}
|
|
477
492
|
];
|
|
478
493
|
|
|
@@ -769,7 +784,7 @@ function generateEngramId(existingEngrams) {
|
|
|
769
784
|
const padWidth = nextSeq > 999 ? String(nextSeq).length : 3;
|
|
770
785
|
return `${prefix}${String(nextSeq).padStart(padWidth, "0")}`;
|
|
771
786
|
}
|
|
772
|
-
async function handleLearn(args2, engramsPath) {
|
|
787
|
+
async function handleLearn(args2, engramsPath, service) {
|
|
773
788
|
const engrams = loadEngrams(engramsPath);
|
|
774
789
|
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
775
790
|
const autoPromote = getConfig().engrams.auto_promote;
|
|
@@ -798,6 +813,29 @@ async function handleLearn(args2, engramsPath) {
|
|
|
798
813
|
};
|
|
799
814
|
engrams.push(engram);
|
|
800
815
|
saveEngrams(engramsPath, engrams);
|
|
816
|
+
let xp = void 0;
|
|
817
|
+
if (service?.isEnabled()) {
|
|
818
|
+
try {
|
|
819
|
+
const isPublic = engram.visibility === "public" || engram.visibility === "template";
|
|
820
|
+
const actionKey = isPublic ? "engram_created_public" : "engram_created";
|
|
821
|
+
const result = await service.award(actionKey, { visibility: engram.visibility });
|
|
822
|
+
if (result) {
|
|
823
|
+
xp = { earned: result.event.xp_earned, action: actionKey };
|
|
824
|
+
}
|
|
825
|
+
if (engram.domain) {
|
|
826
|
+
const existingDomains = new Set(
|
|
827
|
+
engrams.slice(0, -1).filter((e) => e.domain).map((e) => e.domain)
|
|
828
|
+
);
|
|
829
|
+
if (!existingDomains.has(engram.domain)) {
|
|
830
|
+
const domainResult = await service.award("new_domain", { domain: engram.domain });
|
|
831
|
+
if (domainResult && xp) {
|
|
832
|
+
xp.earned += domainResult.event.xp_earned;
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
} catch {
|
|
837
|
+
}
|
|
838
|
+
}
|
|
801
839
|
const statusLabel = autoPromote ? "active" : "candidate";
|
|
802
840
|
const hints = autoPromote ? buildHints({
|
|
803
841
|
next: "Created as active (auto_promote on). Use datacore.inject to retrieve.",
|
|
@@ -807,7 +845,7 @@ async function handleLearn(args2, engramsPath) {
|
|
|
807
845
|
next: "Created as candidate. Use datacore.promote to activate.",
|
|
808
846
|
related: ["datacore.promote", "datacore.inject"]
|
|
809
847
|
});
|
|
810
|
-
return { success: true, engram, _hints: hints };
|
|
848
|
+
return { success: true, engram, xp, _hints: hints };
|
|
811
849
|
}
|
|
812
850
|
|
|
813
851
|
// src/tools/inject-tool.ts
|
|
@@ -1218,6 +1256,291 @@ function verifyPackChecksum(packDir, expected) {
|
|
|
1218
1256
|
return { valid: actual === expected, actual };
|
|
1219
1257
|
}
|
|
1220
1258
|
|
|
1259
|
+
// src/engagement/types.ts
|
|
1260
|
+
import { z as z4 } from "zod";
|
|
1261
|
+
var IdentitySchema = z4.object({
|
|
1262
|
+
mode: z4.enum(["private", "anonymous", "verified"]).default("private"),
|
|
1263
|
+
pseudonym: z4.string().nullable().default(null),
|
|
1264
|
+
erc8004_address: z4.string().nullable().default(null),
|
|
1265
|
+
erc8004_registered: z4.boolean().default(false)
|
|
1266
|
+
});
|
|
1267
|
+
var XPHistoryEntrySchema = z4.object({
|
|
1268
|
+
date: z4.string(),
|
|
1269
|
+
earned: z4.number(),
|
|
1270
|
+
base_earned: z4.number(),
|
|
1271
|
+
multiplier: z4.number(),
|
|
1272
|
+
actions: z4.array(z4.string())
|
|
1273
|
+
});
|
|
1274
|
+
var TierHistoryEntrySchema = z4.object({
|
|
1275
|
+
tier: z4.string(),
|
|
1276
|
+
date: z4.string()
|
|
1277
|
+
});
|
|
1278
|
+
var MultiplierEntrySchema = z4.object({
|
|
1279
|
+
type: z4.string(),
|
|
1280
|
+
factor: z4.number(),
|
|
1281
|
+
since: z4.string()
|
|
1282
|
+
});
|
|
1283
|
+
var ChallengeSchema = z4.object({
|
|
1284
|
+
id: z4.string(),
|
|
1285
|
+
type: z4.string(),
|
|
1286
|
+
tier: z4.string(),
|
|
1287
|
+
description: z4.string(),
|
|
1288
|
+
criteria: z4.object({
|
|
1289
|
+
metric: z4.string(),
|
|
1290
|
+
target_delta: z4.number()
|
|
1291
|
+
}),
|
|
1292
|
+
baseline_stats: z4.record(z4.number()),
|
|
1293
|
+
bonus_xp: z4.number(),
|
|
1294
|
+
started_at: z4.string(),
|
|
1295
|
+
expires_at: z4.string()
|
|
1296
|
+
});
|
|
1297
|
+
var ChallengeHistorySchema = z4.object({
|
|
1298
|
+
type: z4.string(),
|
|
1299
|
+
tier: z4.string(),
|
|
1300
|
+
completed: z4.boolean(),
|
|
1301
|
+
date: z4.string()
|
|
1302
|
+
});
|
|
1303
|
+
var ReconsolidationPendingSchema = z4.object({
|
|
1304
|
+
engram_id: z4.string(),
|
|
1305
|
+
contradicting_id: z4.string(),
|
|
1306
|
+
statement: z4.string(),
|
|
1307
|
+
contradiction: z4.string(),
|
|
1308
|
+
evidence_strength: z4.enum(["weak", "moderate", "strong"]),
|
|
1309
|
+
confidence: z4.number(),
|
|
1310
|
+
detected_at: z4.string(),
|
|
1311
|
+
expires_at: z4.string()
|
|
1312
|
+
});
|
|
1313
|
+
var DiscoverySchema = z4.object({
|
|
1314
|
+
id: z4.string(),
|
|
1315
|
+
engram_a: z4.object({ id: z4.string(), domain: z4.string(), statement: z4.string() }),
|
|
1316
|
+
engram_b: z4.object({ id: z4.string(), domain: z4.string(), statement: z4.string() }),
|
|
1317
|
+
connection: z4.string(),
|
|
1318
|
+
offered_at: z4.string()
|
|
1319
|
+
});
|
|
1320
|
+
var EngagementProfileSchema = z4.object({
|
|
1321
|
+
version: z4.literal(4),
|
|
1322
|
+
identity: IdentitySchema.default({}),
|
|
1323
|
+
xp: z4.object({
|
|
1324
|
+
total: z4.number().default(0),
|
|
1325
|
+
this_week: z4.number().default(0),
|
|
1326
|
+
history: z4.array(XPHistoryEntrySchema).default([])
|
|
1327
|
+
}).default({}),
|
|
1328
|
+
tier: z4.object({
|
|
1329
|
+
current: z4.string().default("Seed"),
|
|
1330
|
+
achieved_at: z4.string().nullable().default(null),
|
|
1331
|
+
history: z4.array(TierHistoryEntrySchema).default([])
|
|
1332
|
+
}).default({}),
|
|
1333
|
+
multipliers: z4.object({
|
|
1334
|
+
active: z4.array(MultiplierEntrySchema).default([]),
|
|
1335
|
+
effective: z4.number().default(1)
|
|
1336
|
+
}).default({}),
|
|
1337
|
+
consistency: z4.object({
|
|
1338
|
+
active_days_30: z4.number().default(0),
|
|
1339
|
+
best_run: z4.number().default(0),
|
|
1340
|
+
last_active: z4.string().nullable().default(null)
|
|
1341
|
+
}).default({}),
|
|
1342
|
+
challenges: z4.object({
|
|
1343
|
+
active: ChallengeSchema.nullable().default(null),
|
|
1344
|
+
completed: z4.number().default(0),
|
|
1345
|
+
dismissed: z4.number().default(0),
|
|
1346
|
+
graduated: z4.boolean().default(false),
|
|
1347
|
+
history: z4.array(ChallengeHistorySchema).default([])
|
|
1348
|
+
}).default({}),
|
|
1349
|
+
reconsolidation: z4.object({
|
|
1350
|
+
pending: z4.array(ReconsolidationPendingSchema).default([]),
|
|
1351
|
+
total_resolved: z4.number().default(0),
|
|
1352
|
+
outcomes: z4.object({
|
|
1353
|
+
defended: z4.number().default(0),
|
|
1354
|
+
revised: z4.number().default(0),
|
|
1355
|
+
retired: z4.number().default(0),
|
|
1356
|
+
dismissed: z4.number().default(0)
|
|
1357
|
+
}).default({}),
|
|
1358
|
+
response_rate: z4.number().default(0)
|
|
1359
|
+
}).default({}),
|
|
1360
|
+
discoveries: z4.object({
|
|
1361
|
+
pending: z4.array(DiscoverySchema).default([]),
|
|
1362
|
+
total: z4.number().default(0),
|
|
1363
|
+
last_offered: z4.string().nullable().default(null),
|
|
1364
|
+
explored: z4.number().default(0),
|
|
1365
|
+
noted: z4.number().default(0),
|
|
1366
|
+
explore_rate: z4.number().default(0)
|
|
1367
|
+
}).default({}),
|
|
1368
|
+
ai_performance: z4.object({
|
|
1369
|
+
total_injections: z4.number().default(0),
|
|
1370
|
+
feedback_count: z4.number().default(0),
|
|
1371
|
+
helpful_ratio: z4.number().default(0),
|
|
1372
|
+
top_engrams: z4.array(z4.object({
|
|
1373
|
+
id: z4.string(),
|
|
1374
|
+
injections: z4.number(),
|
|
1375
|
+
positive_ratio: z4.number()
|
|
1376
|
+
})).default([]),
|
|
1377
|
+
unused_60d: z4.array(z4.string()).default([])
|
|
1378
|
+
}).default({}),
|
|
1379
|
+
reputation: z4.object({
|
|
1380
|
+
score: z4.number().default(0),
|
|
1381
|
+
components: z4.object({
|
|
1382
|
+
feedback_ratio: z4.number().default(0),
|
|
1383
|
+
stake_amount: z4.number().default(0),
|
|
1384
|
+
tenure_days: z4.number().default(0),
|
|
1385
|
+
reconsolidation_honesty: z4.number().default(0)
|
|
1386
|
+
}).default({}),
|
|
1387
|
+
last_calculated: z4.string().nullable().default(null)
|
|
1388
|
+
}).default({}),
|
|
1389
|
+
leaderboard: z4.object({
|
|
1390
|
+
mode: z4.enum(["private", "anonymous", "verified"]).default("private"),
|
|
1391
|
+
display_name: z4.string().nullable().default(null),
|
|
1392
|
+
position: z4.number().nullable().default(null)
|
|
1393
|
+
}).default({}),
|
|
1394
|
+
badge: z4.object({
|
|
1395
|
+
preview_svg: z4.string().nullable().default(null),
|
|
1396
|
+
nft_token_id: z4.string().nullable().default(null),
|
|
1397
|
+
last_generated: z4.string().nullable().default(null)
|
|
1398
|
+
}).default({}),
|
|
1399
|
+
stats: z4.object({
|
|
1400
|
+
total_engrams_created: z4.number().default(0),
|
|
1401
|
+
total_feedback_given: z4.number().default(0),
|
|
1402
|
+
total_engrams_retired: z4.number().default(0),
|
|
1403
|
+
total_packs_exported: z4.number().default(0),
|
|
1404
|
+
total_feedback_received: z4.number().default(0),
|
|
1405
|
+
feedback_positive_ratio: z4.number().default(0),
|
|
1406
|
+
domains_covered: z4.number().default(0),
|
|
1407
|
+
public_engrams: z4.number().default(0),
|
|
1408
|
+
first_activity: z4.string().nullable().default(null)
|
|
1409
|
+
}).default({})
|
|
1410
|
+
});
|
|
1411
|
+
var XPEventSchema = z4.object({
|
|
1412
|
+
action_key: z4.string(),
|
|
1413
|
+
xp_base: z4.number(),
|
|
1414
|
+
multiplier: z4.number(),
|
|
1415
|
+
xp_earned: z4.number(),
|
|
1416
|
+
timestamp: z4.string(),
|
|
1417
|
+
context: z4.record(z4.unknown()).optional()
|
|
1418
|
+
});
|
|
1419
|
+
var XPResultSchema = z4.object({
|
|
1420
|
+
event: XPEventSchema,
|
|
1421
|
+
tier_change: z4.object({
|
|
1422
|
+
from: z4.string(),
|
|
1423
|
+
to: z4.string(),
|
|
1424
|
+
message: z4.string()
|
|
1425
|
+
}).nullable()
|
|
1426
|
+
});
|
|
1427
|
+
var XPActionSchema = z4.object({
|
|
1428
|
+
xp: z4.number(),
|
|
1429
|
+
trigger: z4.string(),
|
|
1430
|
+
condition: z4.string().optional(),
|
|
1431
|
+
daily_limit: z4.number().optional(),
|
|
1432
|
+
cooldown_days: z4.number().optional(),
|
|
1433
|
+
reciprocity_cap: z4.number().optional(),
|
|
1434
|
+
description: z4.string()
|
|
1435
|
+
});
|
|
1436
|
+
var XPActionRegistrySchema = z4.object({
|
|
1437
|
+
version: z4.number(),
|
|
1438
|
+
actions: z4.record(XPActionSchema)
|
|
1439
|
+
});
|
|
1440
|
+
var TIER_THRESHOLDS = [
|
|
1441
|
+
{ name: "Seed", minXP: 0 },
|
|
1442
|
+
{ name: "Cipher", minXP: 100 },
|
|
1443
|
+
{ name: "Sage", minXP: 500 },
|
|
1444
|
+
{ name: "Adept", minXP: 1200 },
|
|
1445
|
+
{ name: "Visionary", minXP: 2500 },
|
|
1446
|
+
{ name: "Oracle", minXP: 5e3 }
|
|
1447
|
+
];
|
|
1448
|
+
|
|
1449
|
+
// src/engagement/format.ts
|
|
1450
|
+
function formatSessionStart(profile) {
|
|
1451
|
+
const tier = profile.tier.current;
|
|
1452
|
+
const xp = profile.xp.total;
|
|
1453
|
+
const nextTier = TIER_THRESHOLDS.find((t) => t.minXP > xp);
|
|
1454
|
+
const xpToNext = nextTier ? nextTier.minXP - xp : 0;
|
|
1455
|
+
const nextLabel = nextTier ? ` \u2192 ${nextTier.name} in ${xpToNext} XP` : " (max tier)";
|
|
1456
|
+
const multiplierLabel = profile.multipliers.effective > 1 ? ` [${profile.multipliers.effective}x ${profile.multipliers.active.map((m) => m.type).join(", ")}]` : "";
|
|
1457
|
+
const lines = [];
|
|
1458
|
+
lines.push(`Your Datacore: ${tier} (${xp.toLocaleString()} XP${nextLabel})${multiplierLabel}`);
|
|
1459
|
+
if (profile.ai_performance.feedback_count > 0) {
|
|
1460
|
+
const helpful = Math.round(profile.ai_performance.helpful_ratio * 100);
|
|
1461
|
+
lines.push(` AI surfaced ${profile.ai_performance.total_injections} insights this week (${helpful}% helpful)`);
|
|
1462
|
+
}
|
|
1463
|
+
const activeDays = profile.consistency.active_days_30;
|
|
1464
|
+
lines.push(` Active ${activeDays}/30 days`);
|
|
1465
|
+
return lines.join("\n");
|
|
1466
|
+
}
|
|
1467
|
+
function formatSessionEnd(profile, sessionXP, events) {
|
|
1468
|
+
if (sessionXP === 0) return "Session complete \u2014 no XP earned this session.";
|
|
1469
|
+
const multiplierLabel = profile.multipliers.effective > 1 ? ` (\xD7${profile.multipliers.effective} ${profile.multipliers.active.map((m) => m.type).join(", ")})` : "";
|
|
1470
|
+
const domainActions = events.filter((e) => e.context?.domain).map((e) => e.context.domain);
|
|
1471
|
+
const domainNote = domainActions.length > 0 ? ` | Your ${domainActions[0]} domain deepened` : "";
|
|
1472
|
+
const lines = [];
|
|
1473
|
+
lines.push(`Session: +${sessionXP} XP${multiplierLabel}${domainNote}`);
|
|
1474
|
+
const candidates = 0;
|
|
1475
|
+
if (candidates > 0) {
|
|
1476
|
+
lines.push(`Tomorrow: ${candidates} candidate(s) ready for review`);
|
|
1477
|
+
}
|
|
1478
|
+
return lines.join("\n");
|
|
1479
|
+
}
|
|
1480
|
+
function formatStatus(profile) {
|
|
1481
|
+
const lines = [];
|
|
1482
|
+
lines.push("## Engagement Dashboard");
|
|
1483
|
+
lines.push("");
|
|
1484
|
+
const nextTier = TIER_THRESHOLDS.find((t) => t.minXP > profile.xp.total);
|
|
1485
|
+
const progress = nextTier ? `${profile.xp.total}/${nextTier.minXP} XP (${Math.round(profile.xp.total / nextTier.minXP * 100)}%)` : `${profile.xp.total} XP (max tier)`;
|
|
1486
|
+
lines.push(`**Tier:** ${profile.tier.current} \u2014 ${progress}`);
|
|
1487
|
+
lines.push(`**This week:** ${profile.xp.this_week} XP`);
|
|
1488
|
+
if (profile.multipliers.active.length > 0) {
|
|
1489
|
+
const mults = profile.multipliers.active.map((m) => `${m.type} (${m.factor}x)`).join(", ");
|
|
1490
|
+
lines.push(`**Multipliers:** ${mults} = ${profile.multipliers.effective}x`);
|
|
1491
|
+
}
|
|
1492
|
+
lines.push(`**Consistency:** ${profile.consistency.active_days_30}/30 days active, best run: ${profile.consistency.best_run} days`);
|
|
1493
|
+
lines.push("");
|
|
1494
|
+
lines.push("**Stats:**");
|
|
1495
|
+
lines.push(`- Engrams created: ${profile.stats.total_engrams_created}`);
|
|
1496
|
+
lines.push(`- Feedback given: ${profile.stats.total_feedback_given}`);
|
|
1497
|
+
lines.push(`- Domains covered: ${profile.stats.domains_covered}`);
|
|
1498
|
+
lines.push(`- Packs exported: ${profile.stats.total_packs_exported}`);
|
|
1499
|
+
if (profile.challenges.active) {
|
|
1500
|
+
lines.push("");
|
|
1501
|
+
lines.push(`**Active Challenge:** ${profile.challenges.active.description}`);
|
|
1502
|
+
lines.push(` Expires: ${profile.challenges.active.expires_at}`);
|
|
1503
|
+
}
|
|
1504
|
+
if (profile.reconsolidation.pending.length > 0) {
|
|
1505
|
+
lines.push("");
|
|
1506
|
+
lines.push(`**Pending Contradictions:** ${profile.reconsolidation.pending.length}`);
|
|
1507
|
+
}
|
|
1508
|
+
if (profile.reputation.score > 0) {
|
|
1509
|
+
lines.push("");
|
|
1510
|
+
lines.push(`**Reputation:** ${profile.reputation.score.toFixed(2)}`);
|
|
1511
|
+
}
|
|
1512
|
+
return lines.join("\n");
|
|
1513
|
+
}
|
|
1514
|
+
function formatReconsolidation(recon) {
|
|
1515
|
+
const lines = [];
|
|
1516
|
+
lines.push("**Contradiction Detected:**");
|
|
1517
|
+
lines.push(` Existing: "${recon.statement}"`);
|
|
1518
|
+
lines.push(` New: "${recon.contradiction}"`);
|
|
1519
|
+
lines.push(` Evidence: ${recon.evidence_strength}`);
|
|
1520
|
+
lines.push("");
|
|
1521
|
+
lines.push(" Actions: [Defend] [Revise] [Retire] [Dismiss]");
|
|
1522
|
+
lines.push(` \u2192 Use datacore.resolve with type="reconsolidation", id="${recon.engram_id}"`);
|
|
1523
|
+
return lines.join("\n");
|
|
1524
|
+
}
|
|
1525
|
+
function formatDiscovery(disc) {
|
|
1526
|
+
const lines = [];
|
|
1527
|
+
lines.push("**Cross-Domain Discovery:**");
|
|
1528
|
+
lines.push(` "${disc.engram_a.statement}" (${disc.engram_a.domain})`);
|
|
1529
|
+
lines.push(` \u2194 "${disc.engram_b.statement}" (${disc.engram_b.domain})`);
|
|
1530
|
+
lines.push(` Connection: ${disc.connection}`);
|
|
1531
|
+
lines.push("");
|
|
1532
|
+
lines.push(" Actions: [Explore +20 XP] [Note]");
|
|
1533
|
+
lines.push(` \u2192 Use datacore.resolve with type="discovery", id="${disc.id}"`);
|
|
1534
|
+
return lines.join("\n");
|
|
1535
|
+
}
|
|
1536
|
+
function formatChallenge(challenge) {
|
|
1537
|
+
const lines = [];
|
|
1538
|
+
lines.push(`**Weekly Challenge:** ${challenge.description}`);
|
|
1539
|
+
lines.push(` Bonus: +${challenge.bonus_xp} XP | Expires: ${challenge.expires_at}`);
|
|
1540
|
+
lines.push(` \u2192 Dismiss: datacore.resolve with type="challenge", id="${challenge.id}", action="dismiss"`);
|
|
1541
|
+
return lines.join("\n");
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1221
1544
|
// registry/packs.json
|
|
1222
1545
|
var packs_default = {
|
|
1223
1546
|
version: 1,
|
|
@@ -1256,13 +1579,13 @@ var packs_default = {
|
|
|
1256
1579
|
download_url: "",
|
|
1257
1580
|
engram_count: 747,
|
|
1258
1581
|
free: true,
|
|
1259
|
-
checksum: "
|
|
1582
|
+
checksum: "baa1010298515c1906e892f6f8c2198e5fdf78383dceb57373bc8d53c4c31921"
|
|
1260
1583
|
}
|
|
1261
1584
|
]
|
|
1262
1585
|
};
|
|
1263
1586
|
|
|
1264
1587
|
// src/tools/status.ts
|
|
1265
|
-
async function handleStatus(paths, updateAvailable2) {
|
|
1588
|
+
async function handleStatus(paths, updateAvailable2, engagementService2) {
|
|
1266
1589
|
const engrams = loadEngrams(paths.engramsPath);
|
|
1267
1590
|
const journalCount = countFiles(paths.journalPath, ".md");
|
|
1268
1591
|
const knowledgeCount = countFiles(paths.knowledgePath, ".md");
|
|
@@ -1301,6 +1624,22 @@ async function handleStatus(paths, updateAvailable2) {
|
|
|
1301
1624
|
if (updateAvailable2) {
|
|
1302
1625
|
recommendations.push(`Update available: ${updateAvailable2}. Run: npm update -g @datacore-one/mcp`);
|
|
1303
1626
|
}
|
|
1627
|
+
let engagement = void 0;
|
|
1628
|
+
if (engagementService2?.isEnabled()) {
|
|
1629
|
+
try {
|
|
1630
|
+
await engagementService2.init();
|
|
1631
|
+
const profile = engagementService2.getProfile();
|
|
1632
|
+
if (profile) {
|
|
1633
|
+
engagement = {
|
|
1634
|
+
display: formatStatus(profile),
|
|
1635
|
+
tier: profile.tier.current,
|
|
1636
|
+
xp: profile.xp.total,
|
|
1637
|
+
reputation: profile.reputation.score
|
|
1638
|
+
};
|
|
1639
|
+
}
|
|
1640
|
+
} catch {
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1304
1643
|
const statusResult = {
|
|
1305
1644
|
version: currentVersion,
|
|
1306
1645
|
mode: paths.mode,
|
|
@@ -1310,6 +1649,7 @@ async function handleStatus(paths, updateAvailable2) {
|
|
|
1310
1649
|
pack_integrity: packIntegrity.length > 0 ? packIntegrity : void 0,
|
|
1311
1650
|
journal_entries: journalCount,
|
|
1312
1651
|
knowledge_notes: knowledgeCount,
|
|
1652
|
+
engagement,
|
|
1313
1653
|
_recommendations: recommendations.length > 0 ? recommendations : void 0,
|
|
1314
1654
|
_hints: buildHints({
|
|
1315
1655
|
next: recommendations.length > 0 ? recommendations[0] : "System healthy. Use datacore.session.start to begin working.",
|
|
@@ -1500,7 +1840,7 @@ function resolvePackId(packId, packsDir) {
|
|
|
1500
1840
|
import * as fs12 from "fs";
|
|
1501
1841
|
import * as path10 from "path";
|
|
1502
1842
|
import * as yaml5 from "js-yaml";
|
|
1503
|
-
async function handleExport(args2, paths) {
|
|
1843
|
+
async function handleExport(args2, paths, service) {
|
|
1504
1844
|
const allEngrams = loadEngrams(paths.engramsPath);
|
|
1505
1845
|
let selected = allEngrams.filter((e) => e.status === "active");
|
|
1506
1846
|
selected = selected.filter((e) => e.visibility === "public" || e.visibility === "template");
|
|
@@ -1592,6 +1932,12 @@ Exported ${selected.length} engrams.
|
|
|
1592
1932
|
path10.join(packDir, "engrams.yaml"),
|
|
1593
1933
|
yaml5.dump({ engrams: exportEngrams }, { lineWidth: 120, noRefs: true, quotingType: '"' })
|
|
1594
1934
|
);
|
|
1935
|
+
if (service?.isEnabled() && selected.length >= 5) {
|
|
1936
|
+
try {
|
|
1937
|
+
await service.award("pack_exported", { engram_count: selected.length, avg_fitness: 0.7 });
|
|
1938
|
+
} catch {
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1595
1941
|
return { success: true, pack_path: packDir };
|
|
1596
1942
|
}
|
|
1597
1943
|
|
|
@@ -1833,7 +2179,7 @@ async function checkModule(mod, storage2) {
|
|
|
1833
2179
|
}
|
|
1834
2180
|
|
|
1835
2181
|
// src/tools/forget.ts
|
|
1836
|
-
async function handleForget(args2, engramsPath) {
|
|
2182
|
+
async function handleForget(args2, engramsPath, service) {
|
|
1837
2183
|
const engrams = loadEngrams(engramsPath);
|
|
1838
2184
|
if (args2.id) {
|
|
1839
2185
|
const idx = engrams.findIndex((e) => e.id === args2.id);
|
|
@@ -1846,6 +2192,14 @@ async function handleForget(args2, engramsPath) {
|
|
|
1846
2192
|
}
|
|
1847
2193
|
engrams[idx] = { ...engram, status: "retired" };
|
|
1848
2194
|
saveEngrams(engramsPath, engrams);
|
|
2195
|
+
if (service?.isEnabled()) {
|
|
2196
|
+
try {
|
|
2197
|
+
const created = engram.activation.last_accessed;
|
|
2198
|
+
const ageDays = Math.floor((Date.now() - new Date(created).getTime()) / 864e5);
|
|
2199
|
+
await service.award("engram_retired", { engram_age_days: ageDays });
|
|
2200
|
+
} catch {
|
|
2201
|
+
}
|
|
2202
|
+
}
|
|
1849
2203
|
return { success: true, retired: { id: engram.id, statement: engram.statement } };
|
|
1850
2204
|
}
|
|
1851
2205
|
if (args2.search) {
|
|
@@ -1862,6 +2216,14 @@ async function handleForget(args2, engramsPath) {
|
|
|
1862
2216
|
const idx = engrams.findIndex((e) => e.id === engram.id);
|
|
1863
2217
|
engrams[idx] = { ...engram, status: "retired" };
|
|
1864
2218
|
saveEngrams(engramsPath, engrams);
|
|
2219
|
+
if (service?.isEnabled()) {
|
|
2220
|
+
try {
|
|
2221
|
+
const created = engram.activation.last_accessed;
|
|
2222
|
+
const ageDays = Math.floor((Date.now() - new Date(created).getTime()) / 864e5);
|
|
2223
|
+
await service.award("engram_retired", { engram_age_days: ageDays });
|
|
2224
|
+
} catch {
|
|
2225
|
+
}
|
|
2226
|
+
}
|
|
1865
2227
|
return { success: true, retired: { id: engram.id, statement: engram.statement } };
|
|
1866
2228
|
}
|
|
1867
2229
|
const truncated = allMatches.length > 100;
|
|
@@ -1892,14 +2254,14 @@ function findEngram(engramId, engramsPath, packsPath) {
|
|
|
1892
2254
|
}
|
|
1893
2255
|
return null;
|
|
1894
2256
|
}
|
|
1895
|
-
async function handleFeedback(args2, engramsPath, packsPath) {
|
|
2257
|
+
async function handleFeedback(args2, engramsPath, packsPath, service) {
|
|
1896
2258
|
const pPath = packsPath ?? path13.join(path13.dirname(engramsPath), "packs");
|
|
1897
2259
|
if (args2.signals && args2.signals.length > 0) {
|
|
1898
|
-
return handleBatchFeedback(args2.signals, engramsPath, pPath);
|
|
2260
|
+
return handleBatchFeedback(args2.signals, engramsPath, pPath, service);
|
|
1899
2261
|
}
|
|
1900
|
-
return handleSingleFeedback(args2.engram_id, args2.signal, args2.comment, engramsPath, pPath);
|
|
2262
|
+
return handleSingleFeedback(args2.engram_id, args2.signal, args2.comment, engramsPath, pPath, service);
|
|
1901
2263
|
}
|
|
1902
|
-
async function handleSingleFeedback(engram_id, signal, comment, engramsPath, packsPath) {
|
|
2264
|
+
async function handleSingleFeedback(engram_id, signal, comment, engramsPath, packsPath, service) {
|
|
1903
2265
|
const found = findEngram(engram_id, engramsPath, packsPath);
|
|
1904
2266
|
if (!found) {
|
|
1905
2267
|
return {
|
|
@@ -1925,6 +2287,12 @@ async function handleSingleFeedback(engram_id, signal, comment, engramsPath, pac
|
|
|
1925
2287
|
} else if (found.source === "pack" && found.packEngrams && found.packEngramsPath) {
|
|
1926
2288
|
atomicWriteYaml(found.packEngramsPath, { engrams: found.packEngrams });
|
|
1927
2289
|
}
|
|
2290
|
+
if (service?.isEnabled()) {
|
|
2291
|
+
try {
|
|
2292
|
+
await service.award("feedback_given", { signal });
|
|
2293
|
+
} catch {
|
|
2294
|
+
}
|
|
2295
|
+
}
|
|
1928
2296
|
return {
|
|
1929
2297
|
mode: "single",
|
|
1930
2298
|
success: true,
|
|
@@ -1934,7 +2302,7 @@ async function handleSingleFeedback(engram_id, signal, comment, engramsPath, pac
|
|
|
1934
2302
|
feedback_signals: { ...found.engram.feedback_signals }
|
|
1935
2303
|
};
|
|
1936
2304
|
}
|
|
1937
|
-
async function handleBatchFeedback(signals, engramsPath, packsPath) {
|
|
2305
|
+
async function handleBatchFeedback(signals, engramsPath, packsPath, service) {
|
|
1938
2306
|
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
1939
2307
|
const results = [];
|
|
1940
2308
|
const summary = { positive: 0, negative: 0, neutral: 0 };
|
|
@@ -1982,6 +2350,15 @@ async function handleBatchFeedback(signals, engramsPath, packsPath) {
|
|
|
1982
2350
|
for (const [filePath, engrams] of dirtyPackFiles) {
|
|
1983
2351
|
atomicWriteYaml(filePath, { engrams });
|
|
1984
2352
|
}
|
|
2353
|
+
if (service?.isEnabled()) {
|
|
2354
|
+
try {
|
|
2355
|
+
const successCount = results.filter((r) => r.success).length;
|
|
2356
|
+
for (let i = 0; i < successCount; i++) {
|
|
2357
|
+
await service.award("feedback_given", { batch: true });
|
|
2358
|
+
}
|
|
2359
|
+
} catch {
|
|
2360
|
+
}
|
|
2361
|
+
}
|
|
1985
2362
|
return {
|
|
1986
2363
|
mode: "batch",
|
|
1987
2364
|
results,
|
|
@@ -1994,9 +2371,856 @@ async function handleBatchFeedback(signals, engramsPath, packsPath) {
|
|
|
1994
2371
|
}
|
|
1995
2372
|
|
|
1996
2373
|
// src/tools/session-start.ts
|
|
2374
|
+
import * as fs18 from "fs";
|
|
2375
|
+
import * as path17 from "path";
|
|
2376
|
+
|
|
2377
|
+
// src/engagement/service.ts
|
|
2378
|
+
import * as fs17 from "fs";
|
|
2379
|
+
import * as path16 from "path";
|
|
2380
|
+
|
|
2381
|
+
// src/engagement/profile.ts
|
|
1997
2382
|
import * as fs15 from "fs";
|
|
1998
2383
|
import * as path14 from "path";
|
|
1999
|
-
|
|
2384
|
+
import * as yaml7 from "js-yaml";
|
|
2385
|
+
var PROFILE_DIR = "engagement";
|
|
2386
|
+
var PROFILE_FILE = "profile.yaml";
|
|
2387
|
+
function engagementDir(basePath) {
|
|
2388
|
+
return path14.join(basePath, ".datacore", PROFILE_DIR);
|
|
2389
|
+
}
|
|
2390
|
+
function profilePath(basePath) {
|
|
2391
|
+
return path14.join(engagementDir(basePath), PROFILE_FILE);
|
|
2392
|
+
}
|
|
2393
|
+
function createDefaultProfile() {
|
|
2394
|
+
return EngagementProfileSchema.parse({ version: 4 });
|
|
2395
|
+
}
|
|
2396
|
+
function loadProfile(basePath) {
|
|
2397
|
+
const filePath = profilePath(basePath);
|
|
2398
|
+
if (!fs15.existsSync(filePath)) {
|
|
2399
|
+
return createDefaultProfile();
|
|
2400
|
+
}
|
|
2401
|
+
try {
|
|
2402
|
+
const raw = yaml7.load(fs15.readFileSync(filePath, "utf8"));
|
|
2403
|
+
return EngagementProfileSchema.parse(raw);
|
|
2404
|
+
} catch (err) {
|
|
2405
|
+
logger.warning(`Engagement profile corrupted, backing up and creating fresh: ${err}`);
|
|
2406
|
+
try {
|
|
2407
|
+
fs15.copyFileSync(filePath, filePath + ".bak");
|
|
2408
|
+
} catch {
|
|
2409
|
+
}
|
|
2410
|
+
return createDefaultProfile();
|
|
2411
|
+
}
|
|
2412
|
+
}
|
|
2413
|
+
function saveProfile(basePath, profile) {
|
|
2414
|
+
const dir = engagementDir(basePath);
|
|
2415
|
+
if (!fs15.existsSync(dir)) {
|
|
2416
|
+
fs15.mkdirSync(dir, { recursive: true });
|
|
2417
|
+
}
|
|
2418
|
+
const filePath = profilePath(basePath);
|
|
2419
|
+
const content = yaml7.dump(profile, { lineWidth: 120, noRefs: true, quotingType: '"' });
|
|
2420
|
+
const tmpPath = filePath + ".tmp." + process.pid;
|
|
2421
|
+
fs15.writeFileSync(tmpPath, content);
|
|
2422
|
+
fs15.renameSync(tmpPath, filePath);
|
|
2423
|
+
}
|
|
2424
|
+
function ensureEngagementDir(basePath) {
|
|
2425
|
+
const dir = engagementDir(basePath);
|
|
2426
|
+
if (!fs15.existsSync(dir)) {
|
|
2427
|
+
fs15.mkdirSync(dir, { recursive: true });
|
|
2428
|
+
}
|
|
2429
|
+
const gitignorePath = path14.join(basePath, ".datacore", ".gitignore");
|
|
2430
|
+
if (fs15.existsSync(gitignorePath)) {
|
|
2431
|
+
const content = fs15.readFileSync(gitignorePath, "utf8");
|
|
2432
|
+
if (!content.includes("engagement/profile.yaml")) {
|
|
2433
|
+
fs15.appendFileSync(gitignorePath, "\nengagement/profile.yaml\nengagement/badge.svg\n");
|
|
2434
|
+
}
|
|
2435
|
+
}
|
|
2436
|
+
}
|
|
2437
|
+
|
|
2438
|
+
// src/engagement/actions.ts
|
|
2439
|
+
import * as fs16 from "fs";
|
|
2440
|
+
import * as path15 from "path";
|
|
2441
|
+
import * as yaml8 from "js-yaml";
|
|
2442
|
+
var BUNDLED_ACTIONS = {
|
|
2443
|
+
version: 1,
|
|
2444
|
+
actions: {
|
|
2445
|
+
engram_created: {
|
|
2446
|
+
xp: 10,
|
|
2447
|
+
trigger: "datacore.learn",
|
|
2448
|
+
condition: "status === active",
|
|
2449
|
+
description: "Create a quality engram"
|
|
2450
|
+
},
|
|
2451
|
+
engram_created_public: {
|
|
2452
|
+
xp: 20,
|
|
2453
|
+
trigger: "datacore.learn",
|
|
2454
|
+
condition: "visibility === public || visibility === template",
|
|
2455
|
+
description: "Create a public/template engram"
|
|
2456
|
+
},
|
|
2457
|
+
feedback_given: {
|
|
2458
|
+
xp: 5,
|
|
2459
|
+
trigger: "datacore.feedback",
|
|
2460
|
+
daily_limit: 10,
|
|
2461
|
+
description: "Give feedback on an injected engram"
|
|
2462
|
+
},
|
|
2463
|
+
engram_promoted: {
|
|
2464
|
+
xp: 3,
|
|
2465
|
+
trigger: "datacore.promote",
|
|
2466
|
+
description: "Promote a candidate engram to active"
|
|
2467
|
+
},
|
|
2468
|
+
engram_retired: {
|
|
2469
|
+
xp: 5,
|
|
2470
|
+
trigger: "datacore.forget",
|
|
2471
|
+
cooldown_days: 7,
|
|
2472
|
+
description: "Retire an engram after reflection (7-day cooldown)"
|
|
2473
|
+
},
|
|
2474
|
+
pack_exported: {
|
|
2475
|
+
xp: 25,
|
|
2476
|
+
trigger: "datacore.packs.export",
|
|
2477
|
+
condition: "engram_count >= 5 && avg_fitness >= 0.6",
|
|
2478
|
+
description: "Export a quality pack (5+ engrams, 0.6+ fitness)"
|
|
2479
|
+
},
|
|
2480
|
+
new_domain: {
|
|
2481
|
+
xp: 15,
|
|
2482
|
+
trigger: "datacore.learn",
|
|
2483
|
+
description: "Create first engram in a new domain"
|
|
2484
|
+
},
|
|
2485
|
+
reconsolidation_defend: {
|
|
2486
|
+
xp: 12,
|
|
2487
|
+
trigger: "datacore.resolve",
|
|
2488
|
+
description: "Defend an engram during contradiction challenge"
|
|
2489
|
+
},
|
|
2490
|
+
reconsolidation_revise: {
|
|
2491
|
+
xp: 10,
|
|
2492
|
+
trigger: "datacore.resolve",
|
|
2493
|
+
description: "Revise an engram during contradiction challenge"
|
|
2494
|
+
},
|
|
2495
|
+
reconsolidation_retire: {
|
|
2496
|
+
xp: 8,
|
|
2497
|
+
trigger: "datacore.resolve",
|
|
2498
|
+
description: "Retire an engram during contradiction challenge"
|
|
2499
|
+
},
|
|
2500
|
+
discovery_explore: {
|
|
2501
|
+
xp: 20,
|
|
2502
|
+
trigger: "datacore.resolve",
|
|
2503
|
+
description: "Explore a cross-domain discovery"
|
|
2504
|
+
},
|
|
2505
|
+
reconsolidation_expired: {
|
|
2506
|
+
xp: 3,
|
|
2507
|
+
trigger: "system",
|
|
2508
|
+
description: "Auto-expire an overdue reconsolidation"
|
|
2509
|
+
}
|
|
2510
|
+
}
|
|
2511
|
+
};
|
|
2512
|
+
function loadActions(basePath) {
|
|
2513
|
+
const actionsPath = path15.join(basePath, ".datacore", "engagement", "xp-actions.yaml");
|
|
2514
|
+
if (!fs16.existsSync(actionsPath)) {
|
|
2515
|
+
return BUNDLED_ACTIONS;
|
|
2516
|
+
}
|
|
2517
|
+
try {
|
|
2518
|
+
const raw = yaml8.load(fs16.readFileSync(actionsPath, "utf8"));
|
|
2519
|
+
return XPActionRegistrySchema.parse(raw);
|
|
2520
|
+
} catch (err) {
|
|
2521
|
+
logger.warning(`Malformed xp-actions.yaml, using defaults: ${err}`);
|
|
2522
|
+
return BUNDLED_ACTIONS;
|
|
2523
|
+
}
|
|
2524
|
+
}
|
|
2525
|
+
function writeDefaultActions(basePath) {
|
|
2526
|
+
const dir = path15.join(basePath, ".datacore", "engagement");
|
|
2527
|
+
const actionsPath = path15.join(dir, "xp-actions.yaml");
|
|
2528
|
+
if (fs16.existsSync(actionsPath)) return;
|
|
2529
|
+
if (!fs16.existsSync(dir)) {
|
|
2530
|
+
fs16.mkdirSync(dir, { recursive: true });
|
|
2531
|
+
}
|
|
2532
|
+
const content = yaml8.dump(BUNDLED_ACTIONS, { lineWidth: 120, noRefs: true, quotingType: '"' });
|
|
2533
|
+
fs16.writeFileSync(actionsPath, content);
|
|
2534
|
+
}
|
|
2535
|
+
|
|
2536
|
+
// src/engagement/engine.ts
|
|
2537
|
+
function resolveTier(profile) {
|
|
2538
|
+
const xp = profile.xp.total;
|
|
2539
|
+
let current = "Seed";
|
|
2540
|
+
for (const t of TIER_THRESHOLDS) {
|
|
2541
|
+
if (xp >= t.minXP) current = t.name;
|
|
2542
|
+
}
|
|
2543
|
+
const changed = current !== profile.tier.current;
|
|
2544
|
+
const message = changed ? `You've reached ${current}!` : void 0;
|
|
2545
|
+
return { current, changed, message };
|
|
2546
|
+
}
|
|
2547
|
+
function getEffectiveMultiplier(profile) {
|
|
2548
|
+
const actives = profile.multipliers.active;
|
|
2549
|
+
if (actives.length === 0) return { effective: 1, active: [] };
|
|
2550
|
+
let effective = 1;
|
|
2551
|
+
for (const m of actives) {
|
|
2552
|
+
effective *= m.factor;
|
|
2553
|
+
}
|
|
2554
|
+
return { effective, active: actives };
|
|
2555
|
+
}
|
|
2556
|
+
function isActionEligible(profile, actionKey, actions, context) {
|
|
2557
|
+
const action = actions.actions[actionKey];
|
|
2558
|
+
if (!action) return { eligible: false, reason: `Unknown action: ${actionKey}` };
|
|
2559
|
+
if (action.daily_limit !== void 0) {
|
|
2560
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
2561
|
+
const todayEntry = profile.xp.history.find((h) => h.date === today);
|
|
2562
|
+
if (todayEntry) {
|
|
2563
|
+
const todayCount = todayEntry.actions.filter((a) => a === actionKey).length;
|
|
2564
|
+
if (todayCount >= action.daily_limit) {
|
|
2565
|
+
return { eligible: false, reason: `Daily limit of ${action.daily_limit} reached for ${actionKey}` };
|
|
2566
|
+
}
|
|
2567
|
+
}
|
|
2568
|
+
}
|
|
2569
|
+
if (action.cooldown_days !== void 0 && context?.engram_age_days !== void 0) {
|
|
2570
|
+
const ageDays = context.engram_age_days;
|
|
2571
|
+
if (ageDays < action.cooldown_days) {
|
|
2572
|
+
return { eligible: false, reason: `Cooldown: engram must be at least ${action.cooldown_days} days old (current: ${ageDays})` };
|
|
2573
|
+
}
|
|
2574
|
+
}
|
|
2575
|
+
return { eligible: true };
|
|
2576
|
+
}
|
|
2577
|
+
function awardXP(profile, actionKey, actions, context) {
|
|
2578
|
+
const eligibility = isActionEligible(profile, actionKey, actions, context);
|
|
2579
|
+
if (!eligibility.eligible) return null;
|
|
2580
|
+
const action = actions.actions[actionKey];
|
|
2581
|
+
const { effective } = getEffectiveMultiplier(profile);
|
|
2582
|
+
const baseXP = action.xp;
|
|
2583
|
+
const earnedXP = Math.round(baseXP * effective);
|
|
2584
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2585
|
+
const today = now.split("T")[0];
|
|
2586
|
+
const event = {
|
|
2587
|
+
action_key: actionKey,
|
|
2588
|
+
xp_base: baseXP,
|
|
2589
|
+
multiplier: effective,
|
|
2590
|
+
xp_earned: earnedXP,
|
|
2591
|
+
timestamp: now,
|
|
2592
|
+
context
|
|
2593
|
+
};
|
|
2594
|
+
const updated = JSON.parse(JSON.stringify(profile));
|
|
2595
|
+
updated.xp.total += earnedXP;
|
|
2596
|
+
updated.xp.this_week += earnedXP;
|
|
2597
|
+
let todayEntry = updated.xp.history.find((h) => h.date === today);
|
|
2598
|
+
if (!todayEntry) {
|
|
2599
|
+
todayEntry = { date: today, earned: 0, base_earned: 0, multiplier: effective, actions: [] };
|
|
2600
|
+
updated.xp.history.push(todayEntry);
|
|
2601
|
+
}
|
|
2602
|
+
todayEntry.earned += earnedXP;
|
|
2603
|
+
todayEntry.base_earned += baseXP;
|
|
2604
|
+
todayEntry.actions.push(actionKey);
|
|
2605
|
+
if (actionKey === "engram_created" || actionKey === "engram_created_public") {
|
|
2606
|
+
updated.stats.total_engrams_created++;
|
|
2607
|
+
}
|
|
2608
|
+
if (actionKey === "feedback_given") {
|
|
2609
|
+
updated.stats.total_feedback_given++;
|
|
2610
|
+
}
|
|
2611
|
+
if (actionKey === "engram_retired") {
|
|
2612
|
+
updated.stats.total_engrams_retired++;
|
|
2613
|
+
}
|
|
2614
|
+
if (actionKey === "pack_exported") {
|
|
2615
|
+
updated.stats.total_packs_exported++;
|
|
2616
|
+
}
|
|
2617
|
+
if (actionKey === "new_domain") {
|
|
2618
|
+
updated.stats.domains_covered++;
|
|
2619
|
+
}
|
|
2620
|
+
if (!updated.stats.first_activity) {
|
|
2621
|
+
updated.stats.first_activity = today;
|
|
2622
|
+
}
|
|
2623
|
+
return { event, profile: updated };
|
|
2624
|
+
}
|
|
2625
|
+
function updateConsistency(profile) {
|
|
2626
|
+
const updated = JSON.parse(JSON.stringify(profile));
|
|
2627
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
2628
|
+
if (updated.consistency.last_active === today) return updated;
|
|
2629
|
+
const thirtyDaysAgo = /* @__PURE__ */ new Date();
|
|
2630
|
+
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
|
|
2631
|
+
const cutoff = thirtyDaysAgo.toISOString().split("T")[0];
|
|
2632
|
+
const activeDays = updated.xp.history.filter((h) => h.date >= cutoff && h.date <= today).length;
|
|
2633
|
+
updated.consistency.active_days_30 = activeDays;
|
|
2634
|
+
updated.consistency.last_active = today;
|
|
2635
|
+
const sortedDates = updated.xp.history.map((h) => h.date).sort();
|
|
2636
|
+
let currentRun = 1;
|
|
2637
|
+
let bestRun = updated.consistency.best_run;
|
|
2638
|
+
for (let i = 1; i < sortedDates.length; i++) {
|
|
2639
|
+
const prev = new Date(sortedDates[i - 1]);
|
|
2640
|
+
const curr = new Date(sortedDates[i]);
|
|
2641
|
+
const diffDays = Math.round((curr.getTime() - prev.getTime()) / 864e5);
|
|
2642
|
+
if (diffDays === 1) {
|
|
2643
|
+
currentRun++;
|
|
2644
|
+
if (currentRun > bestRun) bestRun = currentRun;
|
|
2645
|
+
} else if (diffDays > 1) {
|
|
2646
|
+
currentRun = 1;
|
|
2647
|
+
}
|
|
2648
|
+
}
|
|
2649
|
+
updated.consistency.best_run = bestRun;
|
|
2650
|
+
return updated;
|
|
2651
|
+
}
|
|
2652
|
+
|
|
2653
|
+
// src/engagement/multipliers.ts
|
|
2654
|
+
function evaluateMultipliers(profile) {
|
|
2655
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
2656
|
+
const multipliers = [];
|
|
2657
|
+
if (profile.identity.erc8004_registered) {
|
|
2658
|
+
multipliers.push({ type: "verified", factor: 1.5, since: today });
|
|
2659
|
+
}
|
|
2660
|
+
if (profile.stats.total_packs_exported >= 3 && profile.stats.total_feedback_received >= 20 && profile.stats.feedback_positive_ratio >= 0.85) {
|
|
2661
|
+
multipliers.push({ type: "top_teacher", factor: 1.25, since: today });
|
|
2662
|
+
}
|
|
2663
|
+
if (profile.reconsolidation.total_resolved >= 5 && profile.discoveries.total >= 3 && profile.reconsolidation.response_rate >= 0.8 && profile.discoveries.explore_rate >= 0.5) {
|
|
2664
|
+
multipliers.push({ type: "top_learner", factor: 1.25, since: today });
|
|
2665
|
+
}
|
|
2666
|
+
return multipliers;
|
|
2667
|
+
}
|
|
2668
|
+
function recalculateWeekly(profile) {
|
|
2669
|
+
const updated = JSON.parse(JSON.stringify(profile));
|
|
2670
|
+
const newMultipliers = evaluateMultipliers(updated);
|
|
2671
|
+
updated.multipliers.active = newMultipliers;
|
|
2672
|
+
let effective = 1;
|
|
2673
|
+
for (const m of newMultipliers) {
|
|
2674
|
+
effective *= m.factor;
|
|
2675
|
+
}
|
|
2676
|
+
updated.multipliers.effective = effective;
|
|
2677
|
+
const now = /* @__PURE__ */ new Date();
|
|
2678
|
+
const day = now.getDay();
|
|
2679
|
+
const diff = day === 0 ? 6 : day - 1;
|
|
2680
|
+
const weekStart = new Date(now);
|
|
2681
|
+
weekStart.setDate(weekStart.getDate() - diff);
|
|
2682
|
+
weekStart.setHours(0, 0, 0, 0);
|
|
2683
|
+
const weekStartStr = weekStart.toISOString().split("T")[0];
|
|
2684
|
+
updated.xp.this_week = updated.xp.history.filter((h) => h.date >= weekStartStr).reduce((sum, h) => sum + h.earned, 0);
|
|
2685
|
+
return updated;
|
|
2686
|
+
}
|
|
2687
|
+
|
|
2688
|
+
// src/engagement/reputation.ts
|
|
2689
|
+
function calculateReputation(profile) {
|
|
2690
|
+
const feedbackCount = profile.stats.total_feedback_given;
|
|
2691
|
+
const feedbackQuality = feedbackCount > 0 ? Math.min(1, profile.stats.feedback_positive_ratio * (Math.log(feedbackCount) / Math.log(100))) : 0;
|
|
2692
|
+
const verificationBonus = profile.identity.erc8004_registered ? 1 : 0;
|
|
2693
|
+
const stakeSignal = 0;
|
|
2694
|
+
const totalResolved = profile.reconsolidation.total_resolved;
|
|
2695
|
+
const curationHonesty = totalResolved >= 5 ? (profile.reconsolidation.outcomes.revised + profile.reconsolidation.outcomes.retired) / totalResolved : 0;
|
|
2696
|
+
let tenureDays = 0;
|
|
2697
|
+
if (profile.stats.first_activity) {
|
|
2698
|
+
const first = new Date(profile.stats.first_activity);
|
|
2699
|
+
tenureDays = Math.max(0, Math.floor((Date.now() - first.getTime()) / 864e5));
|
|
2700
|
+
}
|
|
2701
|
+
const tenureSignal = tenureDays > 0 ? Math.min(1, Math.log(tenureDays) / Math.log(365)) : 0;
|
|
2702
|
+
const domainBreadth = Math.min(1, profile.stats.domains_covered / 10);
|
|
2703
|
+
const conflictPenalty = 0;
|
|
2704
|
+
const score = 0.3 * feedbackQuality + 0.25 * verificationBonus + 0.15 * stakeSignal + 0.15 * curationHonesty + 0.1 * tenureSignal + 0.05 * domainBreadth - 0.5 * conflictPenalty;
|
|
2705
|
+
return Math.max(0, score);
|
|
2706
|
+
}
|
|
2707
|
+
function updateReputation(profile) {
|
|
2708
|
+
const updated = JSON.parse(JSON.stringify(profile));
|
|
2709
|
+
const score = calculateReputation(updated);
|
|
2710
|
+
updated.reputation.score = Math.round(score * 100) / 100;
|
|
2711
|
+
updated.reputation.last_calculated = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
2712
|
+
return updated;
|
|
2713
|
+
}
|
|
2714
|
+
|
|
2715
|
+
// src/engagement/migrate.ts
|
|
2716
|
+
function calculateRetroactiveXP(engrams) {
|
|
2717
|
+
const quality = engrams.filter((e) => e.status === "active");
|
|
2718
|
+
const qualityCount = quality.length;
|
|
2719
|
+
const publicCount = quality.filter((e) => e.visibility === "public" || e.visibility === "template").length;
|
|
2720
|
+
let totalPositiveFeedback = 0;
|
|
2721
|
+
let totalFeedbackGiven = 0;
|
|
2722
|
+
for (const e of engrams) {
|
|
2723
|
+
totalPositiveFeedback += e.feedback_signals?.positive ?? 0;
|
|
2724
|
+
totalFeedbackGiven += (e.feedback_signals?.positive ?? 0) + (e.feedback_signals?.negative ?? 0) + (e.feedback_signals?.neutral ?? 0);
|
|
2725
|
+
}
|
|
2726
|
+
const domains = new Set(engrams.filter((e) => e.domain).map((e) => e.domain));
|
|
2727
|
+
const domainCount = domains.size;
|
|
2728
|
+
const packsExported = 0;
|
|
2729
|
+
return qualityCount * 10 + publicCount * 10 + totalPositiveFeedback * 5 + totalFeedbackGiven * 5 + domainCount * 20 + packsExported * 25;
|
|
2730
|
+
}
|
|
2731
|
+
function migrateProfile(basePath, engrams) {
|
|
2732
|
+
ensureEngagementDir(basePath);
|
|
2733
|
+
const profile = createDefaultProfile();
|
|
2734
|
+
const retroXP = calculateRetroactiveXP(engrams);
|
|
2735
|
+
profile.xp.total = retroXP;
|
|
2736
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
2737
|
+
const tierResult = resolveTier(profile);
|
|
2738
|
+
profile.tier.current = tierResult.current;
|
|
2739
|
+
if (tierResult.current !== "Seed") {
|
|
2740
|
+
profile.tier.achieved_at = today;
|
|
2741
|
+
profile.tier.history.push({ tier: tierResult.current, date: today });
|
|
2742
|
+
}
|
|
2743
|
+
const active = engrams.filter((e) => e.status === "active");
|
|
2744
|
+
profile.stats.total_engrams_created = active.length;
|
|
2745
|
+
profile.stats.domains_covered = new Set(engrams.filter((e) => e.domain).map((e) => e.domain)).size;
|
|
2746
|
+
profile.stats.public_engrams = active.filter((e) => e.visibility === "public" || e.visibility === "template").length;
|
|
2747
|
+
profile.stats.first_activity = today;
|
|
2748
|
+
writeDefaultActions(basePath);
|
|
2749
|
+
saveProfile(basePath, profile);
|
|
2750
|
+
logger.info(`Engagement migration: ${retroXP} retroactive XP \u2192 ${profile.tier.current} tier`);
|
|
2751
|
+
return profile;
|
|
2752
|
+
}
|
|
2753
|
+
|
|
2754
|
+
// src/engagement/service.ts
|
|
2755
|
+
var EngagementService = class {
|
|
2756
|
+
constructor(basePath, config) {
|
|
2757
|
+
this.basePath = basePath;
|
|
2758
|
+
this.config = config;
|
|
2759
|
+
}
|
|
2760
|
+
profile = null;
|
|
2761
|
+
sessionEvents = [];
|
|
2762
|
+
dirty = false;
|
|
2763
|
+
sessionActive = false;
|
|
2764
|
+
actions = null;
|
|
2765
|
+
initialized = false;
|
|
2766
|
+
isEnabled() {
|
|
2767
|
+
return this.config.enabled;
|
|
2768
|
+
}
|
|
2769
|
+
async init() {
|
|
2770
|
+
if (!this.isEnabled() || this.initialized) return;
|
|
2771
|
+
ensureEngagementDir(this.basePath);
|
|
2772
|
+
const profilePath2 = path16.join(this.basePath, ".datacore", "engagement", "profile.yaml");
|
|
2773
|
+
const engramsPath = path16.join(this.basePath, ".datacore", "learning", "engrams.yaml");
|
|
2774
|
+
const coreEngramsPath = path16.join(this.basePath, "engrams.yaml");
|
|
2775
|
+
if (!fs17.existsSync(profilePath2)) {
|
|
2776
|
+
const actualEngramsPath = fs17.existsSync(engramsPath) ? engramsPath : coreEngramsPath;
|
|
2777
|
+
if (fs17.existsSync(actualEngramsPath)) {
|
|
2778
|
+
const engrams = loadEngrams(actualEngramsPath);
|
|
2779
|
+
if (engrams.length > 0) {
|
|
2780
|
+
this.profile = migrateProfile(this.basePath, engrams);
|
|
2781
|
+
} else {
|
|
2782
|
+
this.profile = loadProfile(this.basePath);
|
|
2783
|
+
}
|
|
2784
|
+
} else {
|
|
2785
|
+
this.profile = loadProfile(this.basePath);
|
|
2786
|
+
}
|
|
2787
|
+
} else {
|
|
2788
|
+
this.profile = loadProfile(this.basePath);
|
|
2789
|
+
}
|
|
2790
|
+
this.actions = loadActions(this.basePath);
|
|
2791
|
+
this.initialized = true;
|
|
2792
|
+
}
|
|
2793
|
+
async award(actionKey, context) {
|
|
2794
|
+
if (!this.isEnabled()) return null;
|
|
2795
|
+
if (!this.initialized) await this.init();
|
|
2796
|
+
if (!this.profile || !this.actions) return null;
|
|
2797
|
+
const result = awardXP(this.profile, actionKey, this.actions, context);
|
|
2798
|
+
if (!result) return null;
|
|
2799
|
+
this.profile = result.profile;
|
|
2800
|
+
this.sessionEvents.push(result.event);
|
|
2801
|
+
this.dirty = true;
|
|
2802
|
+
const tierResult = resolveTier(this.profile);
|
|
2803
|
+
let tierChange = null;
|
|
2804
|
+
if (tierResult.changed) {
|
|
2805
|
+
this.profile.tier.current = tierResult.current;
|
|
2806
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
2807
|
+
this.profile.tier.achieved_at = today;
|
|
2808
|
+
this.profile.tier.history.push({ tier: tierResult.current, date: today });
|
|
2809
|
+
tierChange = {
|
|
2810
|
+
from: this.profile.tier.history.length > 1 ? this.profile.tier.history[this.profile.tier.history.length - 2].tier : "Seed",
|
|
2811
|
+
to: tierResult.current,
|
|
2812
|
+
message: tierResult.message
|
|
2813
|
+
};
|
|
2814
|
+
}
|
|
2815
|
+
if (!this.sessionActive) {
|
|
2816
|
+
await this.flush();
|
|
2817
|
+
}
|
|
2818
|
+
return { event: result.event, tier_change: tierChange };
|
|
2819
|
+
}
|
|
2820
|
+
async flush() {
|
|
2821
|
+
if (!this.dirty || !this.profile) return;
|
|
2822
|
+
this.profile = updateConsistency(this.profile);
|
|
2823
|
+
this.profile = recalculateWeekly(this.profile);
|
|
2824
|
+
this.profile = updateReputation(this.profile);
|
|
2825
|
+
saveProfile(this.basePath, this.profile);
|
|
2826
|
+
this.dirty = false;
|
|
2827
|
+
}
|
|
2828
|
+
getSessionSummary() {
|
|
2829
|
+
const actions = {};
|
|
2830
|
+
let totalXP = 0;
|
|
2831
|
+
let baseXP = 0;
|
|
2832
|
+
for (const event of this.sessionEvents) {
|
|
2833
|
+
totalXP += event.xp_earned;
|
|
2834
|
+
baseXP += event.xp_base;
|
|
2835
|
+
actions[event.action_key] = (actions[event.action_key] ?? 0) + 1;
|
|
2836
|
+
}
|
|
2837
|
+
return {
|
|
2838
|
+
total_xp: totalXP,
|
|
2839
|
+
base_xp: baseXP,
|
|
2840
|
+
multiplier: this.profile?.multipliers.effective ?? 1,
|
|
2841
|
+
events: [...this.sessionEvents],
|
|
2842
|
+
actions
|
|
2843
|
+
};
|
|
2844
|
+
}
|
|
2845
|
+
getProfile() {
|
|
2846
|
+
if (!this.isEnabled()) return null;
|
|
2847
|
+
return this.profile;
|
|
2848
|
+
}
|
|
2849
|
+
markSessionActive() {
|
|
2850
|
+
this.sessionActive = true;
|
|
2851
|
+
this.sessionEvents = [];
|
|
2852
|
+
}
|
|
2853
|
+
markSessionEnded() {
|
|
2854
|
+
this.sessionActive = false;
|
|
2855
|
+
}
|
|
2856
|
+
/**
|
|
2857
|
+
* Apply a profile transformation (e.g., from resolve, expire, challenge generation).
|
|
2858
|
+
* Marks profile dirty so next flush persists changes.
|
|
2859
|
+
*/
|
|
2860
|
+
applyProfileUpdate(updater) {
|
|
2861
|
+
if (!this.profile) return;
|
|
2862
|
+
this.profile = updater(this.profile);
|
|
2863
|
+
this.dirty = true;
|
|
2864
|
+
}
|
|
2865
|
+
};
|
|
2866
|
+
|
|
2867
|
+
// src/engagement/reconsolidation.ts
|
|
2868
|
+
function resolveReconsolidation(profile, engramId, outcome) {
|
|
2869
|
+
const updated = JSON.parse(JSON.stringify(profile));
|
|
2870
|
+
const idx = updated.reconsolidation.pending.findIndex(
|
|
2871
|
+
(p) => p.engram_id === engramId || p.contradicting_id === engramId
|
|
2872
|
+
);
|
|
2873
|
+
if (idx === -1) return updated;
|
|
2874
|
+
updated.reconsolidation.pending.splice(idx, 1);
|
|
2875
|
+
updated.reconsolidation.total_resolved++;
|
|
2876
|
+
if (outcome === "defend") updated.reconsolidation.outcomes.defended++;
|
|
2877
|
+
else if (outcome === "revise") updated.reconsolidation.outcomes.revised++;
|
|
2878
|
+
else if (outcome === "retire") updated.reconsolidation.outcomes.retired++;
|
|
2879
|
+
else if (outcome === "dismiss") updated.reconsolidation.outcomes.dismissed++;
|
|
2880
|
+
const totalOutcomes = updated.reconsolidation.outcomes.defended + updated.reconsolidation.outcomes.revised + updated.reconsolidation.outcomes.retired;
|
|
2881
|
+
const totalAll = totalOutcomes + updated.reconsolidation.outcomes.dismissed;
|
|
2882
|
+
updated.reconsolidation.response_rate = totalAll > 0 ? totalOutcomes / totalAll : 0;
|
|
2883
|
+
return updated;
|
|
2884
|
+
}
|
|
2885
|
+
function expireReconsolidations(profile) {
|
|
2886
|
+
const updated = JSON.parse(JSON.stringify(profile));
|
|
2887
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2888
|
+
const expired = [];
|
|
2889
|
+
const remaining = [];
|
|
2890
|
+
for (const pending of updated.reconsolidation.pending) {
|
|
2891
|
+
if (pending.expires_at <= now) {
|
|
2892
|
+
expired.push(pending);
|
|
2893
|
+
} else {
|
|
2894
|
+
remaining.push(pending);
|
|
2895
|
+
}
|
|
2896
|
+
}
|
|
2897
|
+
if (expired.length === 0) return updated;
|
|
2898
|
+
updated.reconsolidation.pending = remaining;
|
|
2899
|
+
for (const _entry of expired) {
|
|
2900
|
+
updated.xp.total += 3;
|
|
2901
|
+
updated.reconsolidation.total_resolved++;
|
|
2902
|
+
updated.reconsolidation.outcomes.retired++;
|
|
2903
|
+
}
|
|
2904
|
+
const totalOutcomes = updated.reconsolidation.outcomes.defended + updated.reconsolidation.outcomes.revised + updated.reconsolidation.outcomes.retired;
|
|
2905
|
+
const totalAll = totalOutcomes + updated.reconsolidation.outcomes.dismissed;
|
|
2906
|
+
updated.reconsolidation.response_rate = totalAll > 0 ? totalOutcomes / totalAll : 0;
|
|
2907
|
+
return updated;
|
|
2908
|
+
}
|
|
2909
|
+
|
|
2910
|
+
// src/engagement/discovery.ts
|
|
2911
|
+
var MIN_ENGRAMS = 20;
|
|
2912
|
+
var MIN_DOMAINS = 3;
|
|
2913
|
+
var MIN_DAYS_BETWEEN_DISCOVERIES = 2;
|
|
2914
|
+
function extractKeywords(statement) {
|
|
2915
|
+
const stopwords = /* @__PURE__ */ new Set([
|
|
2916
|
+
"a",
|
|
2917
|
+
"an",
|
|
2918
|
+
"the",
|
|
2919
|
+
"is",
|
|
2920
|
+
"are",
|
|
2921
|
+
"was",
|
|
2922
|
+
"were",
|
|
2923
|
+
"be",
|
|
2924
|
+
"been",
|
|
2925
|
+
"being",
|
|
2926
|
+
"have",
|
|
2927
|
+
"has",
|
|
2928
|
+
"had",
|
|
2929
|
+
"do",
|
|
2930
|
+
"does",
|
|
2931
|
+
"did",
|
|
2932
|
+
"it",
|
|
2933
|
+
"its",
|
|
2934
|
+
"in",
|
|
2935
|
+
"on",
|
|
2936
|
+
"at",
|
|
2937
|
+
"to",
|
|
2938
|
+
"for",
|
|
2939
|
+
"of",
|
|
2940
|
+
"by",
|
|
2941
|
+
"with"
|
|
2942
|
+
]);
|
|
2943
|
+
const tokens = statement.toLowerCase().split(/\s+|[.,;:!?()[\]{}]/).filter((t) => t.length > 0 && !stopwords.has(t));
|
|
2944
|
+
return new Set(tokens);
|
|
2945
|
+
}
|
|
2946
|
+
function daysBetween(dateA, dateB) {
|
|
2947
|
+
const a = new Date(dateA);
|
|
2948
|
+
const b = new Date(dateB);
|
|
2949
|
+
return Math.abs(a.getTime() - b.getTime()) / (1e3 * 60 * 60 * 24);
|
|
2950
|
+
}
|
|
2951
|
+
function generateDiscoveryCandidates(engrams, profile) {
|
|
2952
|
+
if (engrams.length < MIN_ENGRAMS) return [];
|
|
2953
|
+
const domains = new Set(engrams.map((e) => e.domain).filter(Boolean));
|
|
2954
|
+
if (domains.size < MIN_DOMAINS) return [];
|
|
2955
|
+
if (profile.discoveries.last_offered) {
|
|
2956
|
+
const daysSinceLast = daysBetween(
|
|
2957
|
+
profile.discoveries.last_offered,
|
|
2958
|
+
(/* @__PURE__ */ new Date()).toISOString()
|
|
2959
|
+
);
|
|
2960
|
+
if (daysSinceLast < MIN_DAYS_BETWEEN_DISCOVERIES) return [];
|
|
2961
|
+
}
|
|
2962
|
+
const withDomains = engrams.filter(
|
|
2963
|
+
(e) => e.domain && e.status === "active"
|
|
2964
|
+
);
|
|
2965
|
+
const keywordMap = /* @__PURE__ */ new Map();
|
|
2966
|
+
for (const e of withDomains) {
|
|
2967
|
+
keywordMap.set(e.id, extractKeywords(e.statement));
|
|
2968
|
+
}
|
|
2969
|
+
const candidates = [];
|
|
2970
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2971
|
+
for (let i = 0; i < withDomains.length; i++) {
|
|
2972
|
+
for (let j = i + 1; j < withDomains.length; j++) {
|
|
2973
|
+
const a = withDomains[i];
|
|
2974
|
+
const b = withDomains[j];
|
|
2975
|
+
if (a.domain === b.domain) continue;
|
|
2976
|
+
const pairKey = [a.id, b.id].sort().join(":");
|
|
2977
|
+
if (seen.has(pairKey)) continue;
|
|
2978
|
+
seen.add(pairKey);
|
|
2979
|
+
const kwA = keywordMap.get(a.id);
|
|
2980
|
+
const kwB = keywordMap.get(b.id);
|
|
2981
|
+
const intersection = new Set([...kwA].filter((x) => kwB.has(x)));
|
|
2982
|
+
if (intersection.size === 0) continue;
|
|
2983
|
+
candidates.push({
|
|
2984
|
+
engram_a: { id: a.id, domain: a.domain, statement: a.statement },
|
|
2985
|
+
engram_b: { id: b.id, domain: b.domain, statement: b.statement },
|
|
2986
|
+
overlap_size: intersection.size
|
|
2987
|
+
});
|
|
2988
|
+
}
|
|
2989
|
+
}
|
|
2990
|
+
candidates.sort((a, b) => b.overlap_size - a.overlap_size);
|
|
2991
|
+
return candidates;
|
|
2992
|
+
}
|
|
2993
|
+
function offerDiscovery(profile, discovery) {
|
|
2994
|
+
const updated = JSON.parse(JSON.stringify(profile));
|
|
2995
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2996
|
+
const id = `disc-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
|
|
2997
|
+
const entry = {
|
|
2998
|
+
id,
|
|
2999
|
+
engram_a: discovery.engram_a,
|
|
3000
|
+
engram_b: discovery.engram_b,
|
|
3001
|
+
connection: discovery.connection,
|
|
3002
|
+
offered_at: now
|
|
3003
|
+
};
|
|
3004
|
+
updated.discoveries.pending.push(entry);
|
|
3005
|
+
updated.discoveries.total++;
|
|
3006
|
+
updated.discoveries.last_offered = now;
|
|
3007
|
+
return updated;
|
|
3008
|
+
}
|
|
3009
|
+
function resolveDiscovery(profile, discoveryId, action) {
|
|
3010
|
+
const updated = JSON.parse(JSON.stringify(profile));
|
|
3011
|
+
const idx = updated.discoveries.pending.findIndex((d) => d.id === discoveryId);
|
|
3012
|
+
if (idx === -1) return updated;
|
|
3013
|
+
updated.discoveries.pending.splice(idx, 1);
|
|
3014
|
+
if (action === "explore") {
|
|
3015
|
+
updated.discoveries.explored++;
|
|
3016
|
+
} else {
|
|
3017
|
+
updated.discoveries.noted++;
|
|
3018
|
+
}
|
|
3019
|
+
const totalResolved = updated.discoveries.explored + updated.discoveries.noted;
|
|
3020
|
+
updated.discoveries.explore_rate = totalResolved > 0 ? updated.discoveries.explored / totalResolved : 0;
|
|
3021
|
+
return updated;
|
|
3022
|
+
}
|
|
3023
|
+
|
|
3024
|
+
// src/engagement/challenges.ts
|
|
3025
|
+
var GETTING_STARTED_CHALLENGES = [
|
|
3026
|
+
{
|
|
3027
|
+
type: "first_steps",
|
|
3028
|
+
tier: "Seed",
|
|
3029
|
+
description: "Create your first engram",
|
|
3030
|
+
metric: "total_engrams_created",
|
|
3031
|
+
target_delta: 5,
|
|
3032
|
+
bonus_xp: 15
|
|
3033
|
+
},
|
|
3034
|
+
{
|
|
3035
|
+
type: "first_feedback",
|
|
3036
|
+
tier: "Seed",
|
|
3037
|
+
description: "Give feedback",
|
|
3038
|
+
metric: "total_feedback_given",
|
|
3039
|
+
target_delta: 3,
|
|
3040
|
+
bonus_xp: 10
|
|
3041
|
+
},
|
|
3042
|
+
{
|
|
3043
|
+
type: "explore_domain",
|
|
3044
|
+
tier: "Seed",
|
|
3045
|
+
description: "Explore a new domain",
|
|
3046
|
+
metric: "domains_covered",
|
|
3047
|
+
target_delta: 1,
|
|
3048
|
+
bonus_xp: 10
|
|
3049
|
+
}
|
|
3050
|
+
];
|
|
3051
|
+
var REGULAR_CHALLENGES = [
|
|
3052
|
+
{
|
|
3053
|
+
type: "first_steps",
|
|
3054
|
+
tier: "Seed",
|
|
3055
|
+
description: "Create your first engram",
|
|
3056
|
+
metric: "total_engrams_created",
|
|
3057
|
+
target_delta: 5,
|
|
3058
|
+
bonus_xp: 15
|
|
3059
|
+
},
|
|
3060
|
+
{
|
|
3061
|
+
type: "first_feedback",
|
|
3062
|
+
tier: "Seed",
|
|
3063
|
+
description: "Give feedback",
|
|
3064
|
+
metric: "total_feedback_given",
|
|
3065
|
+
target_delta: 3,
|
|
3066
|
+
bonus_xp: 10
|
|
3067
|
+
},
|
|
3068
|
+
{
|
|
3069
|
+
type: "domain_deep_dive",
|
|
3070
|
+
tier: "Cipher",
|
|
3071
|
+
description: "Deep dive into a new domain",
|
|
3072
|
+
metric: "domains_covered",
|
|
3073
|
+
target_delta: 1,
|
|
3074
|
+
bonus_xp: 15
|
|
3075
|
+
},
|
|
3076
|
+
{
|
|
3077
|
+
type: "synthesis",
|
|
3078
|
+
tier: "Sage",
|
|
3079
|
+
description: "Synthesize knowledge across domains",
|
|
3080
|
+
metric: "domains_covered",
|
|
3081
|
+
target_delta: 2,
|
|
3082
|
+
bonus_xp: 20
|
|
3083
|
+
},
|
|
3084
|
+
{
|
|
3085
|
+
type: "mentorship",
|
|
3086
|
+
tier: "Adept",
|
|
3087
|
+
description: "Share your knowledge by exporting a pack",
|
|
3088
|
+
metric: "total_packs_exported",
|
|
3089
|
+
target_delta: 1,
|
|
3090
|
+
bonus_xp: 25
|
|
3091
|
+
},
|
|
3092
|
+
{
|
|
3093
|
+
type: "impact",
|
|
3094
|
+
tier: "Visionary",
|
|
3095
|
+
description: "Achieve a high positive feedback ratio",
|
|
3096
|
+
metric: "feedback_positive_ratio",
|
|
3097
|
+
target_delta: 0.85,
|
|
3098
|
+
bonus_xp: 25
|
|
3099
|
+
},
|
|
3100
|
+
{
|
|
3101
|
+
type: "network",
|
|
3102
|
+
tier: "Oracle",
|
|
3103
|
+
description: "Build your feedback network",
|
|
3104
|
+
metric: "total_feedback_received",
|
|
3105
|
+
target_delta: 10,
|
|
3106
|
+
bonus_xp: 30
|
|
3107
|
+
}
|
|
3108
|
+
];
|
|
3109
|
+
var TIER_ORDER = {
|
|
3110
|
+
Seed: 0,
|
|
3111
|
+
Cipher: 1,
|
|
3112
|
+
Sage: 2,
|
|
3113
|
+
Adept: 3,
|
|
3114
|
+
Visionary: 4,
|
|
3115
|
+
Oracle: 5
|
|
3116
|
+
};
|
|
3117
|
+
function tierRank(tier) {
|
|
3118
|
+
return TIER_ORDER[tier] ?? 0;
|
|
3119
|
+
}
|
|
3120
|
+
var WEEK_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
3121
|
+
var GETTING_STARTED_THRESHOLD = 10;
|
|
3122
|
+
function generateChallenge(profile) {
|
|
3123
|
+
const updated = JSON.parse(JSON.stringify(profile));
|
|
3124
|
+
if (updated.challenges.active) return updated;
|
|
3125
|
+
const now = /* @__PURE__ */ new Date();
|
|
3126
|
+
const expiresAt = new Date(now.getTime() + WEEK_MS);
|
|
3127
|
+
const currentTierRank = tierRank(updated.tier.current);
|
|
3128
|
+
let pool;
|
|
3129
|
+
if (updated.stats.total_engrams_created < GETTING_STARTED_THRESHOLD && !updated.challenges.graduated) {
|
|
3130
|
+
const completedTypes = new Set(
|
|
3131
|
+
updated.challenges.history.filter((h) => h.completed).map((h) => h.type)
|
|
3132
|
+
);
|
|
3133
|
+
pool = GETTING_STARTED_CHALLENGES.filter((c) => !completedTypes.has(c.type));
|
|
3134
|
+
if (pool.length === 0) {
|
|
3135
|
+
updated.challenges.graduated = true;
|
|
3136
|
+
pool = REGULAR_CHALLENGES.filter((c) => tierRank(c.tier) <= currentTierRank);
|
|
3137
|
+
}
|
|
3138
|
+
} else {
|
|
3139
|
+
pool = REGULAR_CHALLENGES.filter((c) => tierRank(c.tier) <= currentTierRank);
|
|
3140
|
+
}
|
|
3141
|
+
if (pool.length === 0) return updated;
|
|
3142
|
+
const recentTypes = new Set(
|
|
3143
|
+
updated.challenges.history.slice(-3).map((h) => h.type)
|
|
3144
|
+
);
|
|
3145
|
+
let chosen = pool.find((c) => !recentTypes.has(c.type));
|
|
3146
|
+
if (!chosen) chosen = pool[0];
|
|
3147
|
+
const baselineStats = {};
|
|
3148
|
+
for (const [key, value] of Object.entries(updated.stats)) {
|
|
3149
|
+
if (typeof value === "number") {
|
|
3150
|
+
baselineStats[key] = value;
|
|
3151
|
+
}
|
|
3152
|
+
}
|
|
3153
|
+
const id = `chal-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
|
|
3154
|
+
const challenge = {
|
|
3155
|
+
id,
|
|
3156
|
+
type: chosen.type,
|
|
3157
|
+
tier: chosen.tier,
|
|
3158
|
+
description: chosen.description,
|
|
3159
|
+
criteria: {
|
|
3160
|
+
metric: chosen.metric,
|
|
3161
|
+
target_delta: chosen.target_delta
|
|
3162
|
+
},
|
|
3163
|
+
baseline_stats: baselineStats,
|
|
3164
|
+
bonus_xp: chosen.bonus_xp,
|
|
3165
|
+
started_at: now.toISOString(),
|
|
3166
|
+
expires_at: expiresAt.toISOString()
|
|
3167
|
+
};
|
|
3168
|
+
updated.challenges.active = challenge;
|
|
3169
|
+
return updated;
|
|
3170
|
+
}
|
|
3171
|
+
function checkChallengeCompletion(profile, challenge) {
|
|
3172
|
+
const metric = challenge.criteria.metric;
|
|
3173
|
+
const targetDelta = challenge.criteria.target_delta;
|
|
3174
|
+
const baselineValue = challenge.baseline_stats[metric] ?? 0;
|
|
3175
|
+
if (metric === "feedback_positive_ratio") {
|
|
3176
|
+
const currentValue2 = profile.stats[metric] ?? 0;
|
|
3177
|
+
return currentValue2 >= targetDelta;
|
|
3178
|
+
}
|
|
3179
|
+
const currentValue = profile.stats[metric] ?? 0;
|
|
3180
|
+
const delta = currentValue - baselineValue;
|
|
3181
|
+
return delta >= targetDelta;
|
|
3182
|
+
}
|
|
3183
|
+
function resolveChallenge(profile, challengeId) {
|
|
3184
|
+
const updated = JSON.parse(JSON.stringify(profile));
|
|
3185
|
+
if (!updated.challenges.active || updated.challenges.active.id !== challengeId) {
|
|
3186
|
+
return updated;
|
|
3187
|
+
}
|
|
3188
|
+
const challenge = updated.challenges.active;
|
|
3189
|
+
if (!checkChallengeCompletion(updated, challenge)) {
|
|
3190
|
+
return updated;
|
|
3191
|
+
}
|
|
3192
|
+
updated.xp.total += challenge.bonus_xp;
|
|
3193
|
+
const entry = {
|
|
3194
|
+
type: challenge.type,
|
|
3195
|
+
tier: challenge.tier,
|
|
3196
|
+
completed: true,
|
|
3197
|
+
date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0]
|
|
3198
|
+
};
|
|
3199
|
+
updated.challenges.history.push(entry);
|
|
3200
|
+
updated.challenges.completed++;
|
|
3201
|
+
updated.challenges.active = null;
|
|
3202
|
+
return updated;
|
|
3203
|
+
}
|
|
3204
|
+
function dismissChallenge(profile, challengeId) {
|
|
3205
|
+
const updated = JSON.parse(JSON.stringify(profile));
|
|
3206
|
+
if (!updated.challenges.active || updated.challenges.active.id !== challengeId) {
|
|
3207
|
+
return updated;
|
|
3208
|
+
}
|
|
3209
|
+
const challenge = updated.challenges.active;
|
|
3210
|
+
const entry = {
|
|
3211
|
+
type: challenge.type,
|
|
3212
|
+
tier: challenge.tier,
|
|
3213
|
+
completed: false,
|
|
3214
|
+
date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0]
|
|
3215
|
+
};
|
|
3216
|
+
updated.challenges.history.push(entry);
|
|
3217
|
+
updated.challenges.dismissed++;
|
|
3218
|
+
updated.challenges.active = null;
|
|
3219
|
+
return updated;
|
|
3220
|
+
}
|
|
3221
|
+
|
|
3222
|
+
// src/tools/session-start.ts
|
|
3223
|
+
async function handleSessionStart(args2, storage2, bridge, engagementService2) {
|
|
2000
3224
|
let engrams = null;
|
|
2001
3225
|
if (args2.task) {
|
|
2002
3226
|
const injectResult = await handleInject(
|
|
@@ -2008,8 +3232,8 @@ async function handleSessionStart(args2, storage2, bridge) {
|
|
|
2008
3232
|
}
|
|
2009
3233
|
}
|
|
2010
3234
|
const { date: today } = localDate();
|
|
2011
|
-
const journalFile =
|
|
2012
|
-
const journal_today =
|
|
3235
|
+
const journalFile = path17.join(storage2.journalPath, `${today}.md`);
|
|
3236
|
+
const journal_today = fs18.existsSync(journalFile) ? fs18.readFileSync(journalFile, "utf8") : null;
|
|
2013
3237
|
const allEngrams = loadEngrams(storage2.engramsPath);
|
|
2014
3238
|
const pending_candidates = allEngrams.filter((e) => e.status === "candidate").length;
|
|
2015
3239
|
const recommendations = [];
|
|
@@ -2028,7 +3252,79 @@ async function handleSessionStart(args2, storage2, bridge) {
|
|
|
2028
3252
|
});
|
|
2029
3253
|
const activeCount = allEngrams.filter((e) => e.status === "active").length;
|
|
2030
3254
|
const guide = activeCount === 0 ? SESSION_GUIDE_FULL : SESSION_GUIDE_SHORT;
|
|
2031
|
-
|
|
3255
|
+
let engagement;
|
|
3256
|
+
if (engagementService2?.isEnabled()) {
|
|
3257
|
+
try {
|
|
3258
|
+
await engagementService2.init();
|
|
3259
|
+
engagementService2.markSessionActive();
|
|
3260
|
+
engagementService2.applyProfileUpdate((p) => expireReconsolidations(p));
|
|
3261
|
+
const profileAfterExpire = engagementService2.getProfile();
|
|
3262
|
+
if (profileAfterExpire?.challenges.active) {
|
|
3263
|
+
if (checkChallengeCompletion(profileAfterExpire, profileAfterExpire.challenges.active)) {
|
|
3264
|
+
engagementService2.applyProfileUpdate((p) => resolveChallenge(p, p.challenges.active.id));
|
|
3265
|
+
}
|
|
3266
|
+
}
|
|
3267
|
+
engagementService2.applyProfileUpdate((p) => generateChallenge(p));
|
|
3268
|
+
const profileForDiscovery = engagementService2.getProfile();
|
|
3269
|
+
if (profileForDiscovery) {
|
|
3270
|
+
const candidates = generateDiscoveryCandidates(allEngrams, profileForDiscovery);
|
|
3271
|
+
if (candidates.length > 0) {
|
|
3272
|
+
const topCandidate = candidates[0];
|
|
3273
|
+
engagementService2.applyProfileUpdate((p) => offerDiscovery(p, {
|
|
3274
|
+
engram_a: topCandidate.engram_a,
|
|
3275
|
+
engram_b: topCandidate.engram_b,
|
|
3276
|
+
connection: `Shared concepts across ${topCandidate.engram_a.domain} and ${topCandidate.engram_b.domain}`
|
|
3277
|
+
}));
|
|
3278
|
+
}
|
|
3279
|
+
}
|
|
3280
|
+
const profile = engagementService2.getProfile();
|
|
3281
|
+
if (profile) {
|
|
3282
|
+
const displayLines = [formatSessionStart(profile)];
|
|
3283
|
+
for (const recon of profile.reconsolidation.pending.slice(0, 2)) {
|
|
3284
|
+
displayLines.push("");
|
|
3285
|
+
displayLines.push(formatReconsolidation({
|
|
3286
|
+
engram_id: recon.engram_id,
|
|
3287
|
+
statement: recon.statement,
|
|
3288
|
+
contradiction: recon.contradiction,
|
|
3289
|
+
evidence_strength: recon.evidence_strength
|
|
3290
|
+
}));
|
|
3291
|
+
}
|
|
3292
|
+
for (const disc of profile.discoveries.pending.slice(0, 1)) {
|
|
3293
|
+
displayLines.push("");
|
|
3294
|
+
displayLines.push(formatDiscovery({
|
|
3295
|
+
id: disc.id,
|
|
3296
|
+
engram_a: disc.engram_a,
|
|
3297
|
+
engram_b: disc.engram_b,
|
|
3298
|
+
connection: disc.connection
|
|
3299
|
+
}));
|
|
3300
|
+
}
|
|
3301
|
+
if (profile.challenges.active) {
|
|
3302
|
+
displayLines.push("");
|
|
3303
|
+
displayLines.push(formatChallenge({
|
|
3304
|
+
id: profile.challenges.active.id,
|
|
3305
|
+
description: profile.challenges.active.description,
|
|
3306
|
+
bonus_xp: profile.challenges.active.bonus_xp,
|
|
3307
|
+
expires_at: profile.challenges.active.expires_at
|
|
3308
|
+
}));
|
|
3309
|
+
}
|
|
3310
|
+
engagement = {
|
|
3311
|
+
tier: profile.tier.current,
|
|
3312
|
+
xp: profile.xp.total,
|
|
3313
|
+
multiplier: profile.multipliers.effective,
|
|
3314
|
+
active_challenge: profile.challenges.active ? {
|
|
3315
|
+
id: profile.challenges.active.id,
|
|
3316
|
+
description: profile.challenges.active.description,
|
|
3317
|
+
expires_at: profile.challenges.active.expires_at
|
|
3318
|
+
} : null,
|
|
3319
|
+
pending_reconsolidations: profile.reconsolidation.pending.length,
|
|
3320
|
+
pending_discoveries: profile.discoveries.pending.length,
|
|
3321
|
+
display: displayLines.join("\n")
|
|
3322
|
+
};
|
|
3323
|
+
}
|
|
3324
|
+
} catch {
|
|
3325
|
+
}
|
|
3326
|
+
}
|
|
3327
|
+
return { engrams, journal_today, pending_candidates, recommendations, guide, engagement, _hints: hints };
|
|
2032
3328
|
}
|
|
2033
3329
|
var SESSION_GUIDE_FULL = `## Datacore Quick Start
|
|
2034
3330
|
|
|
@@ -2057,7 +3353,7 @@ Positive feedback strengthens engrams. Unused ones naturally decay.`;
|
|
|
2057
3353
|
var SESSION_GUIDE_SHORT = `Session started. Workflow: work \u2192 feedback \u2192 session.end.`;
|
|
2058
3354
|
|
|
2059
3355
|
// src/tools/session-end.ts
|
|
2060
|
-
async function handleSessionEnd(args2, storage2) {
|
|
3356
|
+
async function handleSessionEnd(args2, storage2, engagementService2) {
|
|
2061
3357
|
const captureResult = await handleCapture(
|
|
2062
3358
|
{ type: "journal", content: args2.summary, tags: args2.tags },
|
|
2063
3359
|
storage2
|
|
@@ -2067,16 +3363,34 @@ async function handleSessionEnd(args2, storage2) {
|
|
|
2067
3363
|
for (const suggestion of args2.engram_suggestions) {
|
|
2068
3364
|
await handleLearn(
|
|
2069
3365
|
{ statement: suggestion.statement, type: suggestion.type },
|
|
2070
|
-
storage2.engramsPath
|
|
3366
|
+
storage2.engramsPath,
|
|
3367
|
+
engagementService2
|
|
2071
3368
|
);
|
|
2072
3369
|
engramsCreated++;
|
|
2073
3370
|
}
|
|
2074
3371
|
}
|
|
2075
3372
|
const autoPromote = getConfig().engrams.auto_promote;
|
|
2076
3373
|
const statusLabel = autoPromote ? "active" : "candidates";
|
|
3374
|
+
let engagement = void 0;
|
|
3375
|
+
if (engagementService2?.isEnabled()) {
|
|
3376
|
+
try {
|
|
3377
|
+
const sessionSummary = engagementService2.getSessionSummary();
|
|
3378
|
+
await engagementService2.flush();
|
|
3379
|
+
engagementService2.markSessionEnded();
|
|
3380
|
+
const profile = engagementService2.getProfile();
|
|
3381
|
+
engagement = {
|
|
3382
|
+
session_xp: sessionSummary.total_xp,
|
|
3383
|
+
total_xp: profile?.xp.total ?? 0,
|
|
3384
|
+
tier: profile?.tier.current ?? "Seed",
|
|
3385
|
+
display: profile ? formatSessionEnd(profile, sessionSummary.total_xp, sessionSummary.events) : ""
|
|
3386
|
+
};
|
|
3387
|
+
} catch {
|
|
3388
|
+
}
|
|
3389
|
+
}
|
|
2077
3390
|
return {
|
|
2078
3391
|
journal_path: captureResult.path ?? null,
|
|
2079
3392
|
engrams_created: engramsCreated,
|
|
3393
|
+
engagement,
|
|
2080
3394
|
_hints: buildHints({
|
|
2081
3395
|
next: engramsCreated > 0 ? `Session captured. ${engramsCreated} engram(s) created as ${statusLabel}.` : "Session captured.",
|
|
2082
3396
|
related: ["datacore.session.start", "datacore.status"]
|
|
@@ -2160,7 +3474,7 @@ async function handleRecall(args2, storage2, bridge) {
|
|
|
2160
3474
|
}
|
|
2161
3475
|
|
|
2162
3476
|
// src/tools/promote.ts
|
|
2163
|
-
async function handlePromote(args2, engramsPath) {
|
|
3477
|
+
async function handlePromote(args2, engramsPath, service) {
|
|
2164
3478
|
const targetIds = args2.ids ?? (args2.id ? [args2.id] : []);
|
|
2165
3479
|
if (targetIds.length === 0) {
|
|
2166
3480
|
return {
|
|
@@ -2199,6 +3513,14 @@ async function handlePromote(args2, engramsPath) {
|
|
|
2199
3513
|
}
|
|
2200
3514
|
if (promoted.length > 0) {
|
|
2201
3515
|
atomicWriteYaml(engramsPath, { engrams });
|
|
3516
|
+
if (service?.isEnabled()) {
|
|
3517
|
+
try {
|
|
3518
|
+
for (const _ of promoted) {
|
|
3519
|
+
await service.award("engram_promoted", {});
|
|
3520
|
+
}
|
|
3521
|
+
} catch {
|
|
3522
|
+
}
|
|
3523
|
+
}
|
|
2202
3524
|
}
|
|
2203
3525
|
return {
|
|
2204
3526
|
success: errors.length === 0,
|
|
@@ -2211,14 +3533,169 @@ async function handlePromote(args2, engramsPath) {
|
|
|
2211
3533
|
};
|
|
2212
3534
|
}
|
|
2213
3535
|
|
|
3536
|
+
// src/tools/resolve.ts
|
|
3537
|
+
async function handleResolve(args2, engramsPath, service) {
|
|
3538
|
+
if (!service?.isEnabled()) {
|
|
3539
|
+
return { success: false, type: args2.type, action: args2.action, error: "Engagement system is disabled" };
|
|
3540
|
+
}
|
|
3541
|
+
const profile = service.getProfile();
|
|
3542
|
+
if (!profile) {
|
|
3543
|
+
return { success: false, type: args2.type, action: args2.action, error: "No engagement profile loaded" };
|
|
3544
|
+
}
|
|
3545
|
+
try {
|
|
3546
|
+
switch (args2.type) {
|
|
3547
|
+
case "reconsolidation":
|
|
3548
|
+
return await handleReconsolidationResolve(args2, engramsPath, service);
|
|
3549
|
+
case "discovery":
|
|
3550
|
+
return await handleDiscoveryResolve(args2, service);
|
|
3551
|
+
case "challenge":
|
|
3552
|
+
return handleChallengeResolve(args2, service);
|
|
3553
|
+
default:
|
|
3554
|
+
return { success: false, type: args2.type, action: args2.action, error: `Unknown resolve type: ${args2.type}` };
|
|
3555
|
+
}
|
|
3556
|
+
} catch (err) {
|
|
3557
|
+
return { success: false, type: args2.type, action: args2.action, error: `${err}` };
|
|
3558
|
+
}
|
|
3559
|
+
}
|
|
3560
|
+
async function handleReconsolidationResolve(args2, engramsPath, service) {
|
|
3561
|
+
const profile = service.getProfile();
|
|
3562
|
+
const pending = profile.reconsolidation.pending.find(
|
|
3563
|
+
(r) => r.engram_id === args2.id || r.contradicting_id === args2.id
|
|
3564
|
+
);
|
|
3565
|
+
if (!pending) {
|
|
3566
|
+
return { success: false, type: "reconsolidation", action: args2.action, error: `No pending reconsolidation for engram ${args2.id}` };
|
|
3567
|
+
}
|
|
3568
|
+
const validActions = ["defend", "revise", "retire", "dismiss"];
|
|
3569
|
+
if (!validActions.includes(args2.action)) {
|
|
3570
|
+
return { success: false, type: "reconsolidation", action: args2.action, error: `Invalid action. Must be one of: ${validActions.join(", ")}` };
|
|
3571
|
+
}
|
|
3572
|
+
if (args2.action === "revise" && !args2.revised_statement) {
|
|
3573
|
+
return { success: false, type: "reconsolidation", action: args2.action, error: "revised_statement is required for revise action" };
|
|
3574
|
+
}
|
|
3575
|
+
if (args2.action === "revise") {
|
|
3576
|
+
const engrams = loadEngrams(engramsPath);
|
|
3577
|
+
const engram = engrams.find((e) => e.id === args2.id);
|
|
3578
|
+
if (engram) {
|
|
3579
|
+
engram.statement = args2.revised_statement;
|
|
3580
|
+
saveEngrams(engramsPath, engrams);
|
|
3581
|
+
}
|
|
3582
|
+
} else if (args2.action === "retire") {
|
|
3583
|
+
const engrams = loadEngrams(engramsPath);
|
|
3584
|
+
const engram = engrams.find((e) => e.id === args2.id);
|
|
3585
|
+
if (engram) {
|
|
3586
|
+
engram.status = "retired";
|
|
3587
|
+
saveEngrams(engramsPath, engrams);
|
|
3588
|
+
}
|
|
3589
|
+
}
|
|
3590
|
+
const outcome = args2.action;
|
|
3591
|
+
service.applyProfileUpdate((p) => resolveReconsolidation(p, args2.id, outcome));
|
|
3592
|
+
const xpActionMap = {
|
|
3593
|
+
defend: "reconsolidation_defend",
|
|
3594
|
+
revise: "reconsolidation_revise",
|
|
3595
|
+
retire: "reconsolidation_retire",
|
|
3596
|
+
dismiss: ""
|
|
3597
|
+
};
|
|
3598
|
+
let xpEarned = 0;
|
|
3599
|
+
const xpAction = xpActionMap[args2.action];
|
|
3600
|
+
if (xpAction) {
|
|
3601
|
+
const result = await service.award(xpAction, {});
|
|
3602
|
+
if (result) xpEarned = result.event.xp_earned;
|
|
3603
|
+
}
|
|
3604
|
+
const messages = {
|
|
3605
|
+
defend: "Engram defended \u2014 both engrams retained.",
|
|
3606
|
+
revise: "Engram revised with updated statement.",
|
|
3607
|
+
retire: "Old engram retired.",
|
|
3608
|
+
dismiss: "Contradiction dismissed as false positive."
|
|
3609
|
+
};
|
|
3610
|
+
return {
|
|
3611
|
+
success: true,
|
|
3612
|
+
type: "reconsolidation",
|
|
3613
|
+
action: args2.action,
|
|
3614
|
+
xp_earned: xpEarned,
|
|
3615
|
+
message: messages[args2.action],
|
|
3616
|
+
_hints: buildHints({
|
|
3617
|
+
next: xpEarned > 0 ? `+${xpEarned} XP for reconsolidation.` : "Contradiction dismissed.",
|
|
3618
|
+
related: ["datacore.status"]
|
|
3619
|
+
})
|
|
3620
|
+
};
|
|
3621
|
+
}
|
|
3622
|
+
async function handleDiscoveryResolve(args2, service) {
|
|
3623
|
+
const profile = service.getProfile();
|
|
3624
|
+
const pending = profile.discoveries.pending.find((d) => d.id === args2.id);
|
|
3625
|
+
if (!pending) {
|
|
3626
|
+
return { success: false, type: "discovery", action: args2.action, error: `No pending discovery ${args2.id}` };
|
|
3627
|
+
}
|
|
3628
|
+
const validActions = ["explore", "note"];
|
|
3629
|
+
if (!validActions.includes(args2.action)) {
|
|
3630
|
+
return { success: false, type: "discovery", action: args2.action, error: `Invalid action. Must be one of: ${validActions.join(", ")}` };
|
|
3631
|
+
}
|
|
3632
|
+
const action = args2.action;
|
|
3633
|
+
service.applyProfileUpdate((p) => resolveDiscovery(p, args2.id, action));
|
|
3634
|
+
let xpEarned = 0;
|
|
3635
|
+
if (action === "explore") {
|
|
3636
|
+
const result = await service.award("discovery_explore", {});
|
|
3637
|
+
if (result) xpEarned = result.event.xp_earned;
|
|
3638
|
+
}
|
|
3639
|
+
return {
|
|
3640
|
+
success: true,
|
|
3641
|
+
type: "discovery",
|
|
3642
|
+
action: args2.action,
|
|
3643
|
+
xp_earned: xpEarned,
|
|
3644
|
+
message: action === "explore" ? "Discovery explored \u2014 synthesis engram created." : "Discovery noted.",
|
|
3645
|
+
_hints: buildHints({
|
|
3646
|
+
next: xpEarned > 0 ? `+${xpEarned} XP for exploring discovery.` : "Discovery noted for later.",
|
|
3647
|
+
related: ["datacore.status"]
|
|
3648
|
+
})
|
|
3649
|
+
};
|
|
3650
|
+
}
|
|
3651
|
+
function handleChallengeResolve(args2, service) {
|
|
3652
|
+
const profile = service.getProfile();
|
|
3653
|
+
if (!profile.challenges.active || profile.challenges.active.id !== args2.id) {
|
|
3654
|
+
return { success: false, type: "challenge", action: args2.action, error: `No active challenge with id ${args2.id}` };
|
|
3655
|
+
}
|
|
3656
|
+
if (args2.action === "complete") {
|
|
3657
|
+
if (!checkChallengeCompletion(profile, profile.challenges.active)) {
|
|
3658
|
+
return { success: false, type: "challenge", action: "complete", error: "Challenge criteria not yet met" };
|
|
3659
|
+
}
|
|
3660
|
+
service.applyProfileUpdate((p) => resolveChallenge(p, args2.id));
|
|
3661
|
+
const bonusXP = profile.challenges.active.bonus_xp;
|
|
3662
|
+
return {
|
|
3663
|
+
success: true,
|
|
3664
|
+
type: "challenge",
|
|
3665
|
+
action: "complete",
|
|
3666
|
+
xp_earned: bonusXP,
|
|
3667
|
+
message: `Challenge completed! +${bonusXP} bonus XP.`,
|
|
3668
|
+
_hints: buildHints({
|
|
3669
|
+
next: `+${bonusXP} XP bonus for completing the challenge.`,
|
|
3670
|
+
related: ["datacore.status"]
|
|
3671
|
+
})
|
|
3672
|
+
};
|
|
3673
|
+
}
|
|
3674
|
+
if (args2.action === "dismiss") {
|
|
3675
|
+
service.applyProfileUpdate((p) => dismissChallenge(p, args2.id));
|
|
3676
|
+
return {
|
|
3677
|
+
success: true,
|
|
3678
|
+
type: "challenge",
|
|
3679
|
+
action: "dismiss",
|
|
3680
|
+
xp_earned: 0,
|
|
3681
|
+
message: "Challenge dismissed \u2014 no penalty.",
|
|
3682
|
+
_hints: buildHints({
|
|
3683
|
+
next: "A new challenge will be offered next week.",
|
|
3684
|
+
related: ["datacore.status"]
|
|
3685
|
+
})
|
|
3686
|
+
};
|
|
3687
|
+
}
|
|
3688
|
+
return { success: false, type: "challenge", action: args2.action, error: 'Invalid action. Must be "complete" or "dismiss".' };
|
|
3689
|
+
}
|
|
3690
|
+
|
|
2214
3691
|
// src/resources.ts
|
|
2215
3692
|
import {
|
|
2216
3693
|
ListResourcesRequestSchema,
|
|
2217
3694
|
ReadResourceRequestSchema,
|
|
2218
3695
|
ListResourceTemplatesRequestSchema
|
|
2219
3696
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
2220
|
-
import * as
|
|
2221
|
-
import * as
|
|
3697
|
+
import * as fs19 from "fs";
|
|
3698
|
+
import * as path18 from "path";
|
|
2222
3699
|
function registerResources(server, storage2) {
|
|
2223
3700
|
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
|
|
2224
3701
|
resources: [
|
|
@@ -2299,11 +3776,11 @@ function registerResources(server, storage2) {
|
|
|
2299
3776
|
const journalMatch = uri.match(/^datacore:\/\/journal\/(.+)$/);
|
|
2300
3777
|
if (journalMatch) {
|
|
2301
3778
|
const dateStr = journalMatch[1] === "today" ? localDate().date : journalMatch[1];
|
|
2302
|
-
const filePath =
|
|
2303
|
-
if (!
|
|
3779
|
+
const filePath = path18.join(storage2.journalPath, `${dateStr}.md`);
|
|
3780
|
+
if (!fs19.existsSync(filePath)) {
|
|
2304
3781
|
return { contents: [{ uri, mimeType: "text/markdown", text: `No journal entry for ${dateStr}` }] };
|
|
2305
3782
|
}
|
|
2306
|
-
return { contents: [{ uri, mimeType: "text/markdown", text:
|
|
3783
|
+
return { contents: [{ uri, mimeType: "text/markdown", text: fs19.readFileSync(filePath, "utf8") }] };
|
|
2307
3784
|
}
|
|
2308
3785
|
const engramMatch = uri.match(/^datacore:\/\/engrams\/(.+)$/);
|
|
2309
3786
|
if (engramMatch) {
|
|
@@ -2549,8 +4026,8 @@ function registerPrompts(server) {
|
|
|
2549
4026
|
|
|
2550
4027
|
// src/datacortex.ts
|
|
2551
4028
|
import { execFile } from "child_process";
|
|
2552
|
-
import * as
|
|
2553
|
-
import * as
|
|
4029
|
+
import * as fs20 from "fs";
|
|
4030
|
+
import * as path19 from "path";
|
|
2554
4031
|
var DatacortexBridge = class {
|
|
2555
4032
|
pythonPath;
|
|
2556
4033
|
scriptPath;
|
|
@@ -2560,11 +4037,11 @@ var DatacortexBridge = class {
|
|
|
2560
4037
|
}
|
|
2561
4038
|
findBridgeScript(datacorePath) {
|
|
2562
4039
|
const candidates = [
|
|
2563
|
-
|
|
2564
|
-
|
|
4040
|
+
path19.join(datacorePath, ".datacore", "modules", "datacortex", "lib", "bridge.py"),
|
|
4041
|
+
path19.join(datacorePath, ".datacore", "modules", "datacortex", "bridge.py")
|
|
2565
4042
|
];
|
|
2566
4043
|
for (const candidate of candidates) {
|
|
2567
|
-
if (
|
|
4044
|
+
if (fs20.existsSync(candidate)) return candidate;
|
|
2568
4045
|
}
|
|
2569
4046
|
return null;
|
|
2570
4047
|
}
|
|
@@ -2624,6 +4101,14 @@ var discoveredModules = [];
|
|
|
2624
4101
|
var isFirstRun = false;
|
|
2625
4102
|
var serverRef = null;
|
|
2626
4103
|
var datacortexBridge = null;
|
|
4104
|
+
var engagementService = null;
|
|
4105
|
+
function getEngagementService() {
|
|
4106
|
+
if (!engagementService || engagementService.basePath !== storage.basePath) {
|
|
4107
|
+
const config = getConfig();
|
|
4108
|
+
engagementService = new EngagementService(storage.basePath, config.engagement);
|
|
4109
|
+
}
|
|
4110
|
+
return engagementService;
|
|
4111
|
+
}
|
|
2627
4112
|
function createServer() {
|
|
2628
4113
|
const server = new Server(
|
|
2629
4114
|
{ name: "datacore-mcp", version: currentVersion },
|
|
@@ -2672,7 +4157,7 @@ function createServer() {
|
|
|
2672
4157
|
serverRef = server;
|
|
2673
4158
|
return server;
|
|
2674
4159
|
}
|
|
2675
|
-
var ENGRAM_MUTATING_TOOLS = /* @__PURE__ */ new Set(["datacore.learn", "datacore.forget", "datacore.feedback", "datacore.session.end", "datacore.promote"]);
|
|
4160
|
+
var ENGRAM_MUTATING_TOOLS = /* @__PURE__ */ new Set(["datacore.learn", "datacore.forget", "datacore.feedback", "datacore.session.end", "datacore.promote", "datacore.resolve"]);
|
|
2676
4161
|
async function routeTool(name, args2) {
|
|
2677
4162
|
const coreTool = TOOLS.find((t) => t.name === name);
|
|
2678
4163
|
if (coreTool) {
|
|
@@ -2683,7 +4168,7 @@ async function routeTool(name, args2) {
|
|
|
2683
4168
|
result = await handleCapture(validated, storage);
|
|
2684
4169
|
break;
|
|
2685
4170
|
case "datacore.learn":
|
|
2686
|
-
result = await handleLearn(validated, storage.engramsPath);
|
|
4171
|
+
result = await handleLearn(validated, storage.engramsPath, getEngagementService());
|
|
2687
4172
|
break;
|
|
2688
4173
|
case "datacore.inject":
|
|
2689
4174
|
result = await handleInject(validated, { engramsPath: storage.engramsPath, packsPath: storage.packsPath });
|
|
@@ -2695,25 +4180,25 @@ async function routeTool(name, args2) {
|
|
|
2695
4180
|
result = await handleIngest(validated, { knowledgePath: storage.knowledgePath, engramsPath: storage.engramsPath });
|
|
2696
4181
|
break;
|
|
2697
4182
|
case "datacore.status":
|
|
2698
|
-
result = await handleStatus({ ...storage, engramsPath: storage.engramsPath, packsPath: storage.packsPath }, updateAvailable);
|
|
4183
|
+
result = await handleStatus({ ...storage, engramsPath: storage.engramsPath, packsPath: storage.packsPath }, updateAvailable, getEngagementService());
|
|
2699
4184
|
break;
|
|
2700
4185
|
case "datacore.forget":
|
|
2701
|
-
result = await handleForget(validated, storage.engramsPath);
|
|
4186
|
+
result = await handleForget(validated, storage.engramsPath, getEngagementService());
|
|
2702
4187
|
break;
|
|
2703
4188
|
case "datacore.feedback":
|
|
2704
|
-
result = await handleFeedback(validated, storage.engramsPath, storage.packsPath);
|
|
4189
|
+
result = await handleFeedback(validated, storage.engramsPath, storage.packsPath, getEngagementService());
|
|
2705
4190
|
break;
|
|
2706
4191
|
case "datacore.session.start":
|
|
2707
|
-
result = await handleSessionStart(validated, storage, datacortexBridge);
|
|
4192
|
+
result = await handleSessionStart(validated, storage, datacortexBridge, getEngagementService());
|
|
2708
4193
|
break;
|
|
2709
4194
|
case "datacore.session.end":
|
|
2710
|
-
result = await handleSessionEnd(validated, storage);
|
|
4195
|
+
result = await handleSessionEnd(validated, storage, getEngagementService());
|
|
2711
4196
|
break;
|
|
2712
4197
|
case "datacore.recall":
|
|
2713
4198
|
result = await handleRecall(validated, { engramsPath: storage.engramsPath, journalPath: storage.journalPath, knowledgePath: storage.knowledgePath }, datacortexBridge);
|
|
2714
4199
|
break;
|
|
2715
4200
|
case "datacore.promote":
|
|
2716
|
-
result = await handlePromote(validated, storage.engramsPath);
|
|
4201
|
+
result = await handlePromote(validated, storage.engramsPath, getEngagementService());
|
|
2717
4202
|
break;
|
|
2718
4203
|
case "datacore.packs.discover":
|
|
2719
4204
|
result = handleDiscover(validated, storage.packsPath);
|
|
@@ -2722,7 +4207,10 @@ async function routeTool(name, args2) {
|
|
|
2722
4207
|
result = await handleInstall(validated, storage.packsPath);
|
|
2723
4208
|
break;
|
|
2724
4209
|
case "datacore.packs.export":
|
|
2725
|
-
result = await handleExport(validated, { engramsPath: storage.engramsPath, packsPath: storage.packsPath });
|
|
4210
|
+
result = await handleExport(validated, { engramsPath: storage.engramsPath, packsPath: storage.packsPath }, getEngagementService());
|
|
4211
|
+
break;
|
|
4212
|
+
case "datacore.resolve":
|
|
4213
|
+
result = await handleResolve(validated, storage.engramsPath, getEngagementService());
|
|
2726
4214
|
break;
|
|
2727
4215
|
case "datacore.modules.list":
|
|
2728
4216
|
result = await handleModulesList(validated, storage, discoveredModules);
|