bilmem 0.1.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.
@@ -0,0 +1,1975 @@
1
+ // src/core/relations.ts
2
+ var EvidenceGraph = class _EvidenceGraph {
3
+ fromMap = /* @__PURE__ */ new Map();
4
+ toMap = /* @__PURE__ */ new Map();
5
+ constructor(relations = []) {
6
+ for (const rel of relations) {
7
+ this.addRelation(rel);
8
+ }
9
+ }
10
+ addRelation(relation) {
11
+ if (!relation || !relation.from || !relation.to || !relation.type) {
12
+ return;
13
+ }
14
+ const existing = this.getRelationsBetween(relation.from, relation.to);
15
+ const dup = existing.find((r) => r.type === relation.type);
16
+ if (dup) {
17
+ dup.weight = (dup.weight ?? 1) + (relation.weight ?? 1);
18
+ return;
19
+ }
20
+ const fromList = this.fromMap.get(relation.from) ?? [];
21
+ fromList.push(relation);
22
+ this.fromMap.set(relation.from, fromList);
23
+ const toList = this.toMap.get(relation.to) ?? [];
24
+ toList.push(relation);
25
+ this.toMap.set(relation.to, toList);
26
+ }
27
+ getRelationsFrom(fromId) {
28
+ return this.fromMap.get(fromId) ?? [];
29
+ }
30
+ getRelationsTo(toId) {
31
+ return this.toMap.get(toId) ?? [];
32
+ }
33
+ getRelationsBetween(fromId, toId) {
34
+ const fromList = this.fromMap.get(fromId);
35
+ if (!fromList) return [];
36
+ return fromList.filter((r) => r.to === toId);
37
+ }
38
+ getAllRelations() {
39
+ const all = [];
40
+ for (const list of this.fromMap.values()) {
41
+ all.push(...list);
42
+ }
43
+ return all;
44
+ }
45
+ findCoChanged(file, threshold = 1) {
46
+ const relations = this.getRelationsFrom(file).filter((r) => r.type === "CO_CHANGED");
47
+ const result = [];
48
+ for (const rel of relations) {
49
+ const w = rel.weight ?? 1;
50
+ if (w >= threshold) {
51
+ result.push({ file: rel.to, weight: w });
52
+ }
53
+ }
54
+ return result.sort((a, b) => b.weight - a.weight);
55
+ }
56
+ findConnected(id, maxDepth = 2) {
57
+ const visited = /* @__PURE__ */ new Set();
58
+ const queue = [{ node: id, depth: 0 }];
59
+ while (queue.length > 0) {
60
+ const { node, depth } = queue.shift();
61
+ if (visited.has(node)) continue;
62
+ visited.add(node);
63
+ if (depth < maxDepth) {
64
+ const outRels = this.getRelationsFrom(node);
65
+ for (const r of outRels) {
66
+ if (!visited.has(r.to)) {
67
+ queue.push({ node: r.to, depth: depth + 1 });
68
+ }
69
+ }
70
+ const inRels = this.getRelationsTo(node);
71
+ for (const r of inRels) {
72
+ if (!visited.has(r.from)) {
73
+ queue.push({ node: r.from, depth: depth + 1 });
74
+ }
75
+ }
76
+ }
77
+ }
78
+ visited.delete(id);
79
+ return Array.from(visited);
80
+ }
81
+ toJSON() {
82
+ return this.getAllRelations();
83
+ }
84
+ static fromJSON(relations) {
85
+ return new _EvidenceGraph(relations);
86
+ }
87
+ };
88
+
89
+ // src/core/knowledge.ts
90
+ import { createHash } from "crypto";
91
+
92
+ // src/core/quarantine.ts
93
+ function determineInitialStage(knowledge) {
94
+ if (knowledge.type === "fact" || knowledge.type === "architecture" || knowledge.confidenceTier === "high" || knowledge.confidence !== void 0 && knowledge.confidence >= 0.85) {
95
+ return "active";
96
+ }
97
+ if (knowledge.type === "rule" && ((knowledge.evidence?.length ?? 0) >= 2 || knowledge.confidence !== void 0 && knowledge.confidence >= 0.75)) {
98
+ return "probation";
99
+ }
100
+ if (knowledge.confidence !== void 0 && knowledge.confidence >= 0.7) {
101
+ return "probation";
102
+ }
103
+ return "candidate";
104
+ }
105
+ function evaluatePromotion(knowledge) {
106
+ const stage = knowledge.stage ?? "candidate";
107
+ const supportCount = knowledge.metrics.support_count;
108
+ const reuseCount = knowledge.metrics.reuse_count;
109
+ const successCount = Math.max(knowledge.outcomesSummary?.successful ?? 0, knowledge.metrics.successful_outcomes ?? 0);
110
+ const misleadingCount = Math.max(knowledge.outcomesSummary?.misleading ?? 0, knowledge.metrics.failed_outcomes ?? 0);
111
+ if (stage === "candidate") {
112
+ if (supportCount >= 2 || knowledge.evidence && knowledge.evidence.length >= 2 || knowledge.confidence >= 0.85) {
113
+ return {
114
+ promoted: true,
115
+ newStage: "probation",
116
+ reason: `Supported by ${supportCount} sources / ${knowledge.evidence.length} evidence nodes`
117
+ };
118
+ }
119
+ } else if (stage === "probation") {
120
+ if (successCount > 0 && misleadingCount === 0 || reuseCount >= 2 && misleadingCount === 0) {
121
+ return {
122
+ promoted: true,
123
+ newStage: "active",
124
+ reason: `Verified by ${successCount} successful task outcomes and ${reuseCount} reuses`
125
+ };
126
+ }
127
+ }
128
+ return { promoted: false, newStage: stage };
129
+ }
130
+ function calculateKnowledgeValue(knowledge) {
131
+ const reuse = knowledge.metrics.reuse_count;
132
+ const success = knowledge.outcomesSummary?.successful ?? knowledge.metrics.successful_outcomes;
133
+ const misleading = knowledge.outcomesSummary?.misleading ?? knowledge.metrics.failed_outcomes;
134
+ const evidenceCount = Math.min(knowledge.evidence.length, 6);
135
+ const confidence = knowledge.confidence;
136
+ let value = reuse * 2.5 + success * 4 + evidenceCount * 1.5 + confidence * 2 - misleading * 5;
137
+ if (knowledge.status === "active") value += 5;
138
+ if (knowledge.status === "needs_revalidation") value -= 3;
139
+ if (knowledge.status === "contradicted") value -= 8;
140
+ if (knowledge.status === "stale") value -= 4;
141
+ if (knowledge.stage === "active") value += 3;
142
+ if (knowledge.stage === "candidate") value -= 1;
143
+ return Math.round(value * 10) / 10;
144
+ }
145
+ function recordOutcomeOnKnowledge(knowledge, outcome) {
146
+ const now = (/* @__PURE__ */ new Date()).toISOString();
147
+ const summary = knowledge.outcomesSummary ?? {
148
+ applied: 0,
149
+ successful: 0,
150
+ misleading: 0,
151
+ neutral: 0
152
+ };
153
+ summary.applied++;
154
+ let actionTaken;
155
+ if (outcome.result === "success") {
156
+ summary.successful++;
157
+ knowledge.metrics.successful_outcomes++;
158
+ knowledge.metrics.reuse_count++;
159
+ const promo = evaluatePromotion({ ...knowledge, outcomesSummary: summary });
160
+ if (promo.promoted) {
161
+ knowledge.stage = promo.newStage;
162
+ actionTaken = `Promoted to ${promo.newStage} (${promo.reason})`;
163
+ const history = knowledge.history ?? [];
164
+ history.push({
165
+ timestamp: now,
166
+ status: knowledge.status,
167
+ action: "promote",
168
+ reason: promo.reason
169
+ });
170
+ knowledge.history = history;
171
+ }
172
+ } else if (outcome.result === "failure" || outcome.result === "reverted") {
173
+ summary.misleading++;
174
+ knowledge.metrics.failed_outcomes++;
175
+ if (summary.misleading >= 2 && summary.misleading > summary.successful) {
176
+ knowledge.status = "needs_revalidation";
177
+ actionTaken = `Marked needs_revalidation: ${summary.misleading} misleading outcomes recorded`;
178
+ const history = knowledge.history ?? [];
179
+ history.push({
180
+ timestamp: now,
181
+ status: "needs_revalidation",
182
+ action: "demote",
183
+ reason: actionTaken
184
+ });
185
+ knowledge.history = history;
186
+ }
187
+ } else {
188
+ summary.neutral++;
189
+ }
190
+ knowledge.outcomesSummary = summary;
191
+ knowledge.value = calculateKnowledgeValue(knowledge);
192
+ knowledge.updatedAt = now;
193
+ return { updatedKnowledge: knowledge, actionTaken };
194
+ }
195
+ function enforceKnowledgeBudget(knowledgeList, budget) {
196
+ if (!budget || !budget.active || budget.active <= 0) {
197
+ return { modifiedList: knowledgeList, demotedCount: 0 };
198
+ }
199
+ const activeItems = knowledgeList.filter((k) => k.status === "active" && k.stage === "active");
200
+ if (activeItems.length <= budget.active) {
201
+ return { modifiedList: knowledgeList, demotedCount: 0 };
202
+ }
203
+ activeItems.sort((a, b) => calculateKnowledgeValue(a) - calculateKnowledgeValue(b));
204
+ const excessCount = activeItems.length - budget.active;
205
+ const toDemote = new Set(activeItems.slice(0, excessCount).map((k) => k.id));
206
+ const now = (/* @__PURE__ */ new Date()).toISOString();
207
+ const modifiedList = knowledgeList.map((k) => {
208
+ if (toDemote.has(k.id)) {
209
+ const history = k.history ?? [];
210
+ k.stage = "probation";
211
+ k.status = "probation";
212
+ k.updatedAt = now;
213
+ k.history = [
214
+ ...history,
215
+ {
216
+ timestamp: now,
217
+ stage: "probation",
218
+ status: "probation",
219
+ action: "budget_demotion",
220
+ reason: `Active items exceeded budget (${budget.active}). Demoted to probation based on knowledge value.`
221
+ }
222
+ ];
223
+ return k;
224
+ }
225
+ return k;
226
+ });
227
+ return { modifiedList, demotedCount: excessCount };
228
+ }
229
+
230
+ // src/core/knowledge.ts
231
+ function tierToConfidence(tier) {
232
+ switch (tier) {
233
+ case "low":
234
+ return 0.5;
235
+ case "medium":
236
+ return 0.75;
237
+ case "high":
238
+ return 0.95;
239
+ }
240
+ }
241
+ function confidenceToTier(score) {
242
+ if (score >= 0.85) return "high";
243
+ if (score >= 0.65) return "medium";
244
+ return "low";
245
+ }
246
+ function hashString(input) {
247
+ return createHash("sha256").update(input.trim().toLowerCase()).digest("hex").slice(0, 16);
248
+ }
249
+ function createObservation(params) {
250
+ const provenance = params.provenance ?? {
251
+ type: "file",
252
+ path: params.source,
253
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
254
+ };
255
+ const contentForHash = `${params.source}:${params.claim}:${provenance.commit || provenance.timestamp || Date.now()}`;
256
+ const id = `obs_${hashString(contentForHash)}`;
257
+ return {
258
+ id,
259
+ source: params.source,
260
+ claim: params.claim.trim(),
261
+ provenance,
262
+ confidence: Math.min(1, Math.max(0, params.confidence ?? 0.8)),
263
+ positive: Array.from(new Set(params.positive?.map((p) => p.trim().toLowerCase()) ?? [])),
264
+ negative: Array.from(new Set(params.negative?.map((n) => n.trim().toLowerCase()) ?? [])),
265
+ details: params.details,
266
+ recordedAt: (/* @__PURE__ */ new Date()).toISOString()
267
+ };
268
+ }
269
+ function createEvidence(observation, type = "supports") {
270
+ const sourceHash = hashString(observation.source);
271
+ const id = `evi_${sourceHash}`;
272
+ return {
273
+ id,
274
+ source: observation.source,
275
+ sourceHash,
276
+ observationIds: [observation.id],
277
+ type,
278
+ claim: observation.claim,
279
+ provenance: observation.provenance,
280
+ weight: 1,
281
+ firstObservedAt: observation.recordedAt,
282
+ lastObservedAt: observation.recordedAt
283
+ };
284
+ }
285
+ function createKnowledge(params) {
286
+ const now = (/* @__PURE__ */ new Date()).toISOString();
287
+ const confidenceTier = params.confidenceTier ?? (params.confidence !== void 0 ? confidenceToTier(params.confidence) : void 0);
288
+ const confidence = params.confidence !== void 0 ? params.confidence : confidenceTier ? tierToConfidence(confidenceTier) : 0.85;
289
+ const dependsOn = new Set(params.depends_on ?? []);
290
+ if (params.evidence) {
291
+ for (const ev of params.evidence) {
292
+ if (ev.startsWith("file:") || ev.startsWith("test:") || ev.startsWith("code:")) {
293
+ dependsOn.add(ev);
294
+ } else if (ev.includes("/") || ev.endsWith(".json") || ev.endsWith(".yaml") || ev.endsWith(".yml")) {
295
+ dependsOn.add(`file:${ev}`);
296
+ }
297
+ }
298
+ }
299
+ const initialStage = params.stage ?? determineInitialStage({
300
+ type: params.type ?? "lesson",
301
+ evidence: params.evidence ?? [],
302
+ confidence
303
+ });
304
+ const status = params.status ?? (initialStage === "candidate" ? "candidate" : "active");
305
+ const history = params.history ?? [
306
+ {
307
+ timestamp: now,
308
+ stage: initialStage,
309
+ status,
310
+ reason: `Initial knowledge recorded (${params.type ?? "lesson"})`,
311
+ source: params.evidence?.[0]
312
+ }
313
+ ];
314
+ const knowledgeItem = {
315
+ id: params.id.trim(),
316
+ type: params.type ?? "lesson",
317
+ status,
318
+ stage: initialStage,
319
+ statement: params.statement.trim(),
320
+ positive: Array.from(new Set(params.positive?.map((p) => p.trim().toLowerCase()) ?? [])),
321
+ negative: Array.from(new Set(params.negative?.map((n) => n.trim().toLowerCase()) ?? [])),
322
+ relations: Array.from(new Set(params.relations?.map((r) => r.trim()) ?? [])),
323
+ depends_on: Array.from(dependsOn),
324
+ resolves: params.resolves ? Array.from(new Set(params.resolves.map((r) => r.trim()))) : void 0,
325
+ question: params.question?.trim(),
326
+ confidence: Math.min(1, Math.max(0, confidence)),
327
+ confidenceTier,
328
+ evidence: Array.from(new Set(params.evidence ?? [])),
329
+ scope: params.scope ?? "project",
330
+ tags: Array.from(new Set(params.tags?.map((t) => t.trim().toLowerCase()) ?? [])),
331
+ temporal: {
332
+ revision: 1
333
+ },
334
+ metrics: {
335
+ reuse_count: 0,
336
+ support_count: 1,
337
+ contradiction_count: 0,
338
+ successful_outcomes: 0,
339
+ failed_outcomes: 0
340
+ },
341
+ outcomesSummary: params.outcomesSummary ?? {
342
+ applied: 0,
343
+ successful: 0,
344
+ misleading: 0,
345
+ neutral: 0
346
+ },
347
+ history,
348
+ value: 0,
349
+ createdAt: now,
350
+ updatedAt: now
351
+ };
352
+ knowledgeItem.value = calculateKnowledgeValue(knowledgeItem);
353
+ return knowledgeItem;
354
+ }
355
+ function calculateEffectiveConfidence(knowledge) {
356
+ const m = knowledge.metrics;
357
+ const base = knowledge.confidence;
358
+ const evRatio = (m.support_count + 1) / (m.support_count + 3 * m.contradiction_count + 1);
359
+ const totalOutcomes = m.successful_outcomes + m.failed_outcomes;
360
+ let outRatio = 1;
361
+ let evWeight = 0.6;
362
+ let outWeight = 0.4;
363
+ if (totalOutcomes === 0) {
364
+ evWeight = 0.85;
365
+ outWeight = 0.15;
366
+ } else {
367
+ outRatio = (m.successful_outcomes + 1) / (totalOutcomes + 1);
368
+ }
369
+ const effective = base * (evWeight * evRatio + outWeight * outRatio);
370
+ return Math.round(Math.min(1, Math.max(0.05, effective)) * 100) / 100;
371
+ }
372
+ var knowledgeTokenCache = /* @__PURE__ */ new WeakMap();
373
+ function tokenize(text) {
374
+ return new Set(
375
+ text.toLowerCase().replace(/[^a-z0-9_-]/g, " ").split(/\s+/).filter((t) => t.length > 2)
376
+ );
377
+ }
378
+ function getCachedTokens(k) {
379
+ let tokens = knowledgeTokenCache.get(k);
380
+ if (!tokens) {
381
+ tokens = tokenize(k.statement);
382
+ knowledgeTokenCache.set(k, tokens);
383
+ }
384
+ return tokens;
385
+ }
386
+ function jaccardSimilarity(setA, setB) {
387
+ if (setA.size === 0 && setB.size === 0) return 1;
388
+ let intersection = 0;
389
+ for (const item of setA) {
390
+ if (setB.has(item)) intersection++;
391
+ }
392
+ const union = setA.size + setB.size - intersection;
393
+ return union === 0 ? 0 : intersection / union;
394
+ }
395
+ var POLAR_ANTONYMS = [
396
+ ["enabled", "disabled"],
397
+ ["enable", "disable"],
398
+ ["active", "inactive"],
399
+ ["true", "false"],
400
+ ["on", "off"],
401
+ ["yes", "no"],
402
+ ["allowed", "disallowed"],
403
+ ["allowed", "prohibited"],
404
+ ["allowed", "forbidden"],
405
+ ["permit", "deny"],
406
+ ["allow", "forbid"],
407
+ ["allow", "prohibit"],
408
+ ["always", "never"],
409
+ ["should", "should not"],
410
+ ["must", "must not"],
411
+ ["required", "optional"],
412
+ ["required", "forbidden"],
413
+ ["valid", "invalid"],
414
+ ["safe", "unsafe"],
415
+ ["sync", "async"],
416
+ ["synchronous", "asynchronous"],
417
+ ["public", "private"]
418
+ ];
419
+ var NEGATION_PATTERNS = [
420
+ /\bnot\b/i,
421
+ /\bnever\b/i,
422
+ /\bno\b/i,
423
+ /\bcannot\b/i,
424
+ /\bwithout\b/i,
425
+ /\bdisallow\b/i,
426
+ /\bforbid\b/i,
427
+ /\bprohibit\b/i,
428
+ /\bavoid\b/i
429
+ ];
430
+ function detectPolarContradiction(stmtA, stmtB) {
431
+ const lowerA = stmtA.toLowerCase();
432
+ const lowerB = stmtB.toLowerCase();
433
+ for (const [pos, neg] of POLAR_ANTONYMS) {
434
+ const aHasPos = new RegExp(`\\b${pos}\\b`, "i").test(lowerA);
435
+ const aHasNeg = new RegExp(`\\b${neg}\\b`, "i").test(lowerA);
436
+ const bHasPos = new RegExp(`\\b${pos}\\b`, "i").test(lowerB);
437
+ const bHasNeg = new RegExp(`\\b${neg}\\b`, "i").test(lowerB);
438
+ if (aHasPos && bHasNeg || aHasNeg && bHasPos) {
439
+ return {
440
+ contradicted: true,
441
+ reason: `Polar opposite terms detected: '${pos}' vs '${neg}'`
442
+ };
443
+ }
444
+ }
445
+ const aHasNegation = NEGATION_PATTERNS.some((p) => p.test(lowerA));
446
+ const bHasNegation = NEGATION_PATTERNS.some((p) => p.test(lowerB));
447
+ if (aHasNegation !== bHasNegation) {
448
+ return {
449
+ contradicted: true,
450
+ reason: "Asymmetric negation detected (one statement asserts, the other negates)"
451
+ };
452
+ }
453
+ return { contradicted: false };
454
+ }
455
+ function reconcileKnowledge(candidate, existingList) {
456
+ const candidateId = candidate.id?.trim();
457
+ const candidateStmt = candidate.statement.trim();
458
+ const candidateTokens = tokenize(candidateStmt);
459
+ const candidatePos = new Set((candidate.positive ?? []).map((p) => p.trim().toLowerCase()));
460
+ if (candidateId) {
461
+ const directMatch = existingList.find((k) => k.id === candidateId);
462
+ if (directMatch) {
463
+ if (directMatch.statement.trim().toLowerCase() === candidateStmt.toLowerCase()) {
464
+ const hasNewEvidence = (candidate.evidence ?? []).some((e) => !directMatch.evidence.includes(e));
465
+ if (!hasNewEvidence) {
466
+ return {
467
+ action: "IGNORE",
468
+ targetId: directMatch.id,
469
+ knowledge: directMatch,
470
+ reason: "Identical statement and evidence already recorded"
471
+ };
472
+ }
473
+ return {
474
+ action: "SUPPORT",
475
+ targetId: directMatch.id,
476
+ knowledge: directMatch,
477
+ reason: "Confirms existing knowledge with new evidence source"
478
+ };
479
+ }
480
+ const opposingPositive = (candidate.negative ?? []).some((n) => directMatch.positive.includes(n));
481
+ const opposingNegative = (candidate.positive ?? []).some((p) => directMatch.negative.includes(p));
482
+ const polarCheck = detectPolarContradiction(candidateStmt, directMatch.statement);
483
+ if (opposingPositive || opposingNegative || polarCheck.contradicted) {
484
+ const distinctCues = Array.from(candidatePos).filter((c) => !directMatch.positive.includes(c));
485
+ const mentionsEnvA = /(development|dev|local|staging|test)/i.test(candidateStmt);
486
+ const mentionsEnvB = /(production|prod)/i.test(directMatch.statement);
487
+ const mentionsEnvBDev = /(development|dev|local|staging|test)/i.test(directMatch.statement);
488
+ const mentionsEnvAProd = /(production|prod)/i.test(candidateStmt);
489
+ if (distinctCues.length > 0 || mentionsEnvA && mentionsEnvB || mentionsEnvBDev && mentionsEnvAProd) {
490
+ return {
491
+ action: "SPECIALIZE",
492
+ targetId: directMatch.id,
493
+ knowledge: directMatch,
494
+ reason: `Conflicting claim applies under specialized context cues: [${distinctCues.join(", ")}] (${polarCheck.reason || "opposing polarity"})`
495
+ };
496
+ }
497
+ return {
498
+ action: "CONTRADICT",
499
+ targetId: directMatch.id,
500
+ knowledge: directMatch,
501
+ reason: `Contradicts existing statement (${polarCheck.reason || "opposing polarity"}) for ID "${directMatch.id}"`
502
+ };
503
+ }
504
+ return {
505
+ action: "UPDATE",
506
+ targetId: directMatch.id,
507
+ knowledge: directMatch,
508
+ reason: "Refines or updates existing knowledge statement"
509
+ };
510
+ }
511
+ }
512
+ let bestMatch = null;
513
+ let bestSim = 0;
514
+ let bestPolarCheck = { contradicted: false };
515
+ for (const existing of existingList) {
516
+ const existingTokens = getCachedTokens(existing);
517
+ const sim = jaccardSimilarity(candidateTokens, existingTokens);
518
+ const polarCheck = detectPolarContradiction(candidateStmt, existing.statement);
519
+ if (polarCheck.contradicted) {
520
+ let sharedCount = 0;
521
+ for (const t of candidateTokens) {
522
+ if (existingTokens.has(t)) sharedCount++;
523
+ }
524
+ const sharedCues = (candidate.positive ?? []).filter((p) => existing.positive.includes(p));
525
+ if (sharedCount >= 2 || sharedCues.length >= 1 && sim >= 0.35 || sim >= 0.4) {
526
+ bestMatch = existing;
527
+ bestSim = Math.max(sim, 0.85);
528
+ bestPolarCheck = polarCheck;
529
+ break;
530
+ }
531
+ }
532
+ if (sim > bestSim) {
533
+ bestSim = sim;
534
+ bestMatch = existing;
535
+ bestPolarCheck = polarCheck;
536
+ }
537
+ }
538
+ if (bestMatch && bestSim >= 0.7) {
539
+ const polarCheck = bestPolarCheck.contradicted ? bestPolarCheck : detectPolarContradiction(candidateStmt, bestMatch.statement);
540
+ const hasOpposingCues = (candidate.negative ?? []).some((n) => bestMatch.positive.includes(n)) || (candidate.positive ?? []).some((p) => bestMatch.negative.includes(p));
541
+ if (polarCheck.contradicted || hasOpposingCues) {
542
+ const distinctCues = Array.from(candidatePos).filter((c) => !bestMatch.positive.includes(c));
543
+ const mentionsEnvA = /(development|dev|local|staging|test)/i.test(candidateStmt);
544
+ const mentionsEnvB = /(production|prod)/i.test(bestMatch.statement);
545
+ const mentionsEnvBDev = /(development|dev|local|staging|test)/i.test(bestMatch.statement);
546
+ const mentionsEnvAProd = /(production|prod)/i.test(candidateStmt);
547
+ if (distinctCues.length > 0 || mentionsEnvA && mentionsEnvB || mentionsEnvBDev && mentionsEnvAProd) {
548
+ return {
549
+ action: "SPECIALIZE",
550
+ targetId: bestMatch.id,
551
+ knowledge: bestMatch,
552
+ reason: `High textual similarity (${Math.round(bestSim * 100)}%) with polar distinction across contexts (${polarCheck.reason || "opposing cues"})`,
553
+ score: bestSim
554
+ };
555
+ }
556
+ return {
557
+ action: "CONTRADICT",
558
+ targetId: bestMatch.id,
559
+ knowledge: bestMatch,
560
+ reason: `Contradicting statement (${polarCheck.reason || "opposing cues"}) despite high textual similarity (${Math.round(bestSim * 100)}%)`,
561
+ score: bestSim
562
+ };
563
+ }
564
+ if (bestSim >= 0.9) {
565
+ return {
566
+ action: "MERGE",
567
+ targetId: bestMatch.id,
568
+ knowledge: bestMatch,
569
+ reason: `Nearly identical statement (${Math.round(bestSim * 100)}% match) with compatible polarity; merging cues and evidence`,
570
+ score: bestSim
571
+ };
572
+ }
573
+ return {
574
+ action: "SUPPORT",
575
+ targetId: bestMatch.id,
576
+ knowledge: bestMatch,
577
+ reason: `Supports existing knowledge (${Math.round(bestSim * 100)}% similarity) with compatible polarity`,
578
+ score: bestSim
579
+ };
580
+ }
581
+ return {
582
+ action: "NEW",
583
+ reason: "No conflicting or matching knowledge found; creating new knowledge entry"
584
+ };
585
+ }
586
+ function applyReconciliation(reconciliation, candidate, existingList, storage) {
587
+ const now = (/* @__PURE__ */ new Date()).toISOString();
588
+ switch (reconciliation.action) {
589
+ case "SUPPORT": {
590
+ const target = existingList.find((k) => k.id === reconciliation.targetId);
591
+ target.metrics.support_count++;
592
+ target.metrics.last_used = now;
593
+ storage?.appendMetricEvent({
594
+ type: "knowledge_strengthened",
595
+ knowledgeId: target.id,
596
+ timestamp: Date.now()
597
+ });
598
+ if (candidate.evidence) {
599
+ for (const e of candidate.evidence) {
600
+ if (!target.evidence.includes(e)) {
601
+ target.evidence.push(e);
602
+ }
603
+ }
604
+ }
605
+ if (candidate.positive) {
606
+ for (const p of candidate.positive) {
607
+ const lp = p.trim().toLowerCase();
608
+ if (!target.positive.includes(lp)) target.positive.push(lp);
609
+ }
610
+ }
611
+ if (!target.history) {
612
+ target.history = [
613
+ {
614
+ timestamp: target.createdAt || now,
615
+ stage: target.stage,
616
+ status: target.status,
617
+ reason: "Initial knowledge recorded",
618
+ source: target.evidence?.[0]
619
+ }
620
+ ];
621
+ }
622
+ const promotion = evaluatePromotion(target);
623
+ if (promotion.promoted && promotion.newStage) {
624
+ target.stage = promotion.newStage;
625
+ if (target.stage === "active" && target.status === "candidate") {
626
+ target.status = "active";
627
+ }
628
+ target.history.push({
629
+ timestamp: now,
630
+ stage: target.stage,
631
+ status: target.status,
632
+ reason: promotion.reason || `Promoted to ${target.stage}`,
633
+ source: candidate.evidence?.[0]
634
+ });
635
+ } else {
636
+ target.history.push({
637
+ timestamp: now,
638
+ stage: target.stage,
639
+ status: target.status,
640
+ reason: reconciliation.reason || "Confirmed with supporting evidence",
641
+ source: candidate.evidence?.[0]
642
+ });
643
+ }
644
+ target.confidence = calculateEffectiveConfidence(target);
645
+ target.value = calculateKnowledgeValue(target);
646
+ target.updatedAt = now;
647
+ return { updatedKnowledge: target, action: "SUPPORT", createdId: target.id };
648
+ }
649
+ case "MERGE": {
650
+ const target = existingList.find((k) => k.id === reconciliation.targetId);
651
+ target.metrics.support_count++;
652
+ target.metrics.last_used = now;
653
+ storage?.appendMetricEvent({
654
+ type: "knowledge_merged",
655
+ knowledgeId: target.id,
656
+ targetId: reconciliation.targetId,
657
+ timestamp: Date.now()
658
+ });
659
+ if (candidate.evidence) {
660
+ for (const e of candidate.evidence) {
661
+ if (!target.evidence.includes(e)) target.evidence.push(e);
662
+ }
663
+ }
664
+ if (candidate.positive) {
665
+ for (const p of candidate.positive) {
666
+ const lp = p.trim().toLowerCase();
667
+ if (!target.positive.includes(lp)) target.positive.push(lp);
668
+ }
669
+ }
670
+ if (candidate.negative) {
671
+ for (const n of candidate.negative) {
672
+ const ln = n.trim().toLowerCase();
673
+ if (!target.negative.includes(ln)) target.negative.push(ln);
674
+ }
675
+ }
676
+ if (!target.history) {
677
+ target.history = [
678
+ {
679
+ timestamp: target.createdAt || now,
680
+ stage: target.stage,
681
+ status: target.status,
682
+ reason: "Initial knowledge recorded",
683
+ source: target.evidence?.[0]
684
+ }
685
+ ];
686
+ }
687
+ const promotion = evaluatePromotion(target);
688
+ if (promotion.promoted && promotion.newStage) {
689
+ target.stage = promotion.newStage;
690
+ if (target.stage === "active" && target.status === "candidate") {
691
+ target.status = "active";
692
+ }
693
+ target.history.push({
694
+ timestamp: now,
695
+ stage: target.stage,
696
+ status: target.status,
697
+ reason: promotion.reason || `Promoted to ${target.stage}`,
698
+ source: candidate.evidence?.[0]
699
+ });
700
+ } else {
701
+ target.history.push({
702
+ timestamp: now,
703
+ stage: target.stage,
704
+ status: target.status,
705
+ reason: reconciliation.reason || "Merged with compatible knowledge candidate",
706
+ source: candidate.evidence?.[0]
707
+ });
708
+ }
709
+ target.confidence = calculateEffectiveConfidence(target);
710
+ target.value = calculateKnowledgeValue(target);
711
+ target.updatedAt = now;
712
+ return { updatedKnowledge: target, action: "MERGE", createdId: target.id };
713
+ }
714
+ case "UPDATE": {
715
+ const target = existingList.find((k) => k.id === reconciliation.targetId);
716
+ target.statement = candidate.statement;
717
+ if (candidate.type) target.type = candidate.type;
718
+ if (candidate.positive) {
719
+ target.positive = Array.from(/* @__PURE__ */ new Set([...target.positive, ...candidate.positive.map((p) => p.toLowerCase())]));
720
+ }
721
+ if (candidate.negative) {
722
+ target.negative = Array.from(/* @__PURE__ */ new Set([...target.negative, ...candidate.negative.map((n) => n.toLowerCase())]));
723
+ }
724
+ if (candidate.evidence) {
725
+ target.evidence = Array.from(/* @__PURE__ */ new Set([...target.evidence, ...candidate.evidence]));
726
+ }
727
+ target.temporal = {
728
+ revision: (target.temporal?.revision ?? 1) + 1,
729
+ valid_from: { timestamp: now }
730
+ };
731
+ if (!target.history) {
732
+ target.history = [
733
+ {
734
+ timestamp: target.createdAt || now,
735
+ stage: target.stage,
736
+ status: target.status,
737
+ reason: "Initial knowledge recorded",
738
+ source: target.evidence?.[0]
739
+ }
740
+ ];
741
+ }
742
+ target.history.push({
743
+ timestamp: now,
744
+ stage: target.stage,
745
+ status: target.status,
746
+ reason: reconciliation.reason || "Updated statement and context",
747
+ source: candidate.evidence?.[0]
748
+ });
749
+ target.updatedAt = now;
750
+ target.confidence = calculateEffectiveConfidence(target);
751
+ target.value = calculateKnowledgeValue(target);
752
+ storage?.appendMetricEvent({
753
+ type: "knowledge_strengthened",
754
+ knowledgeId: target.id,
755
+ timestamp: Date.now()
756
+ });
757
+ return { updatedKnowledge: target, action: "UPDATE", createdId: target.id };
758
+ }
759
+ case "CONTRADICT": {
760
+ const target = existingList.find((k) => k.id === reconciliation.targetId);
761
+ target.metrics.contradiction_count++;
762
+ if (candidate.evidence) {
763
+ for (const e of candidate.evidence) {
764
+ if (!target.evidence.includes(e)) target.evidence.push(e);
765
+ }
766
+ }
767
+ if (target.metrics.contradiction_count > target.metrics.support_count) {
768
+ target.status = "contradicted";
769
+ }
770
+ if (!target.history) {
771
+ target.history = [
772
+ {
773
+ timestamp: target.createdAt || now,
774
+ stage: target.stage,
775
+ status: target.status,
776
+ reason: "Initial knowledge recorded",
777
+ source: target.evidence?.[0]
778
+ }
779
+ ];
780
+ }
781
+ target.history.push({
782
+ timestamp: now,
783
+ stage: target.stage,
784
+ status: target.status,
785
+ reason: reconciliation.reason || "Contradicted by opposing claim",
786
+ source: candidate.evidence?.[0]
787
+ });
788
+ target.confidence = calculateEffectiveConfidence(target);
789
+ target.value = calculateKnowledgeValue(target);
790
+ target.updatedAt = now;
791
+ storage?.appendMetricEvent({
792
+ type: "contradiction_detected",
793
+ knowledgeId: target.id,
794
+ timestamp: Date.now()
795
+ });
796
+ return { updatedKnowledge: target, action: "CONTRADICT", createdId: target.id };
797
+ }
798
+ case "SPECIALIZE": {
799
+ const parent = existingList.find((k) => k.id === reconciliation.targetId);
800
+ const subContext = candidate.positive?.[0] || "specialized";
801
+ const newId = candidate.id && candidate.id !== parent?.id ? candidate.id : `${parent ? parent.id : "knowledge"}.${subContext}`;
802
+ const created = createKnowledge({
803
+ id: newId,
804
+ type: candidate.type ?? parent?.type ?? "lesson",
805
+ statement: candidate.statement,
806
+ positive: candidate.positive ?? [],
807
+ negative: candidate.negative ?? [],
808
+ relations: parent ? [parent.id, ...candidate.relations ?? []] : candidate.relations ?? [],
809
+ confidence: candidate.confidence ?? 0.85,
810
+ evidence: candidate.evidence ?? [],
811
+ scope: candidate.scope ?? parent?.scope ?? "project",
812
+ tags: candidate.tags ?? parent?.tags ?? []
813
+ });
814
+ if (parent) {
815
+ if (!parent.relations.includes(newId)) {
816
+ parent.relations.push(newId);
817
+ }
818
+ if (!parent.history) {
819
+ parent.history = [
820
+ {
821
+ timestamp: parent.createdAt || now,
822
+ stage: parent.stage,
823
+ status: parent.status,
824
+ reason: "Initial knowledge recorded"
825
+ }
826
+ ];
827
+ }
828
+ parent.history.push({
829
+ timestamp: now,
830
+ stage: parent.stage,
831
+ status: parent.status,
832
+ reason: `Specialized into contextual branch ${newId}`
833
+ });
834
+ }
835
+ storage?.appendMetricEvent({
836
+ type: "knowledge_specialized",
837
+ knowledgeId: created.id,
838
+ timestamp: Date.now()
839
+ });
840
+ return { updatedKnowledge: created, action: "SPECIALIZE", createdId: created.id };
841
+ }
842
+ case "IGNORE": {
843
+ const target = existingList.find((k) => k.id === reconciliation.targetId);
844
+ storage?.appendMetricEvent({
845
+ type: "duplicate_ignored",
846
+ timestamp: Date.now()
847
+ });
848
+ return { updatedKnowledge: target, action: "IGNORE", createdId: target.id };
849
+ }
850
+ case "NEW":
851
+ default: {
852
+ const newId = candidate.id || `kn_${hashString(candidate.statement)}`;
853
+ const created = createKnowledge({
854
+ id: newId,
855
+ type: candidate.type,
856
+ statement: candidate.statement,
857
+ positive: candidate.positive,
858
+ negative: candidate.negative,
859
+ relations: candidate.relations,
860
+ confidence: candidate.confidence,
861
+ evidence: candidate.evidence,
862
+ scope: candidate.scope,
863
+ tags: candidate.tags
864
+ });
865
+ storage?.appendMetricEvent({
866
+ type: "knowledge_created",
867
+ knowledgeId: created.id,
868
+ timestamp: Date.now()
869
+ });
870
+ return { updatedKnowledge: created, action: "NEW", createdId: created.id };
871
+ }
872
+ }
873
+ }
874
+
875
+ // src/core/unknown.ts
876
+ function createUnknown(params) {
877
+ const now = (/* @__PURE__ */ new Date()).toISOString();
878
+ return {
879
+ id: params.id.trim(),
880
+ state: params.state ?? "UNKNOWN",
881
+ reason: params.reason ?? "NOT_OBSERVED",
882
+ statement: params.statement?.trim(),
883
+ checked: Array.from(new Set(params.checked?.map((c) => c.trim()) ?? [])),
884
+ next: params.next,
885
+ scope: params.scope ?? "project",
886
+ createdAt: now,
887
+ updatedAt: now
888
+ };
889
+ }
890
+ function hasBeenInvestigated(unknowns, target) {
891
+ const normalizedTarget = target.trim().toLowerCase();
892
+ for (const record of unknowns) {
893
+ if (record.id.toLowerCase() === normalizedTarget) {
894
+ return { investigated: true, record, reason: record.reason };
895
+ }
896
+ const checkedMatch = record.checked.some((c) => {
897
+ const nc = c.toLowerCase();
898
+ return nc === normalizedTarget || normalizedTarget.startsWith(nc.replace("/**", ""));
899
+ });
900
+ if (checkedMatch) {
901
+ return { investigated: true, record, reason: record.reason };
902
+ }
903
+ }
904
+ return { investigated: false };
905
+ }
906
+ function updateUnknown(record, updates) {
907
+ const checked = new Set(record.checked);
908
+ if (updates.addChecked) {
909
+ for (const c of updates.addChecked) {
910
+ checked.add(c.trim());
911
+ }
912
+ }
913
+ return {
914
+ ...record,
915
+ state: updates.state ?? record.state,
916
+ reason: updates.reason ?? record.reason,
917
+ statement: updates.statement !== void 0 ? updates.statement : record.statement,
918
+ checked: Array.from(checked),
919
+ next: updates.next !== void 0 ? updates.next : record.next,
920
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
921
+ };
922
+ }
923
+
924
+ // src/core/outcome.ts
925
+ function createOutcome(params) {
926
+ const content = `${params.knowledgeId || "none"}:${params.result}:${Date.now()}`;
927
+ return {
928
+ id: `out_${hashString(content)}`,
929
+ knowledgeId: params.knowledgeId,
930
+ taskId: params.taskId,
931
+ result: params.result,
932
+ context: params.context,
933
+ feedback: params.feedback,
934
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
935
+ };
936
+ }
937
+ function applyOutcomeToKnowledge(knowledge, outcome) {
938
+ const now = (/* @__PURE__ */ new Date()).toISOString();
939
+ knowledge.metrics.reuse_count++;
940
+ knowledge.metrics.last_used = now;
941
+ if (outcome.result === "success") {
942
+ knowledge.metrics.successful_outcomes++;
943
+ } else if (outcome.result === "failure" || outcome.result === "reverted") {
944
+ knowledge.metrics.failed_outcomes++;
945
+ }
946
+ knowledge.confidence = calculateEffectiveConfidence(knowledge);
947
+ knowledge.updatedAt = now;
948
+ return knowledge;
949
+ }
950
+ var MetricsTracker = class {
951
+ metrics = {
952
+ knowledgeHits: 0,
953
+ knowledgeMisses: 0,
954
+ knowledgeReused: 0,
955
+ unknownsResolved: 0,
956
+ repeatedInvestigationsPrevented: 0,
957
+ candidateKnowledgeMerged: 0,
958
+ staleKnowledgeDetected: 0,
959
+ contradictionsDetected: 0,
960
+ knowledgePromoted: 0,
961
+ knowledgeSuperseded: 0
962
+ };
963
+ constructor(initial) {
964
+ if (initial) {
965
+ this.metrics = { ...this.metrics, ...initial };
966
+ }
967
+ }
968
+ recordHit() {
969
+ this.metrics.knowledgeHits++;
970
+ }
971
+ recordMiss() {
972
+ this.metrics.knowledgeMisses++;
973
+ }
974
+ recordReused() {
975
+ this.metrics.knowledgeReused++;
976
+ }
977
+ recordUnknownResolved() {
978
+ this.metrics.unknownsResolved++;
979
+ }
980
+ recordInvestigationPrevented() {
981
+ this.metrics.repeatedInvestigationsPrevented++;
982
+ }
983
+ recordCandidateMerged() {
984
+ this.metrics.candidateKnowledgeMerged++;
985
+ }
986
+ recordStaleDetected() {
987
+ this.metrics.staleKnowledgeDetected++;
988
+ }
989
+ recordContradiction() {
990
+ this.metrics.contradictionsDetected++;
991
+ }
992
+ recordPromoted() {
993
+ this.metrics.knowledgePromoted++;
994
+ }
995
+ recordSuperseded() {
996
+ this.metrics.knowledgeSuperseded++;
997
+ }
998
+ getMetrics() {
999
+ return { ...this.metrics };
1000
+ }
1001
+ toJSON() {
1002
+ return this.metrics;
1003
+ }
1004
+ };
1005
+
1006
+ // src/core/learn.ts
1007
+ import { createHash as createHash2 } from "crypto";
1008
+ function distillTrajectory(trajectory) {
1009
+ const events = trajectory.events || [];
1010
+ if (events.length === 0) return null;
1011
+ const failures = events.filter((e) => e.type === "failure" || e.type === "revert");
1012
+ const successes = events.filter((e) => e.type === "success");
1013
+ const corrections = events.filter((e) => e.type === "user_correction");
1014
+ if (successes.length === 0 && corrections.length === 0 && failures.length === 0) {
1015
+ return null;
1016
+ }
1017
+ const positiveSignals = /* @__PURE__ */ new Set();
1018
+ const negativeSignals = /* @__PURE__ */ new Set();
1019
+ const tags = /* @__PURE__ */ new Set(["lesson", "trajectory"]);
1020
+ for (const f of failures) {
1021
+ negativeSignals.add(f.description.toLowerCase().replace(/[^a-z0-9_-]/g, "-"));
1022
+ if (f.path) negativeSignals.add(f.path.toLowerCase());
1023
+ }
1024
+ for (const s of successes) {
1025
+ positiveSignals.add(s.description.toLowerCase().replace(/[^a-z0-9_-]/g, "-"));
1026
+ if (s.path) positiveSignals.add(s.path.toLowerCase());
1027
+ }
1028
+ for (const c of corrections) {
1029
+ positiveSignals.add(c.description.toLowerCase().replace(/[^a-z0-9_-]/g, "-"));
1030
+ tags.add("user-correction");
1031
+ }
1032
+ const goalTokens = trajectory.goal.toLowerCase().replace(/[^a-z0-9_-]/g, " ").split(/\s+/).filter((w) => w.length > 3);
1033
+ for (const t of goalTokens) {
1034
+ positiveSignals.add(t);
1035
+ }
1036
+ let statement = "";
1037
+ let reasoning = "";
1038
+ if (corrections.length > 0) {
1039
+ const lastCorr = corrections[corrections.length - 1];
1040
+ statement = `Corrected approach for "${trajectory.goal}": ${lastCorr.description}`;
1041
+ reasoning = `Based on user correction during task ${trajectory.taskId}`;
1042
+ } else if (successes.length > 0) {
1043
+ const lastSuccess = successes[successes.length - 1];
1044
+ if (failures.length > 0) {
1045
+ const firstFailure = failures[0];
1046
+ statement = `When addressing "${trajectory.goal}": prefer ${lastSuccess.description} over ${firstFailure.description}`;
1047
+ reasoning = `Initial attempt (${firstFailure.description}) failed/reverted; resolved by ${lastSuccess.description}`;
1048
+ } else {
1049
+ statement = `Effective pattern for "${trajectory.goal}": ${lastSuccess.description}`;
1050
+ reasoning = `Directly succeeded in task ${trajectory.taskId}`;
1051
+ }
1052
+ } else if (failures.length > 0) {
1053
+ const failureList = failures.map((f) => f.description).join(", ");
1054
+ statement = `Avoid for "${trajectory.goal}": approaches [${failureList}] failed`;
1055
+ reasoning = `Recorded ${failures.length} failed attempts in task ${trajectory.taskId}`;
1056
+ }
1057
+ const hash = createHash2("sha256").update(`${trajectory.goal}:${statement}`).digest("hex").slice(0, 12);
1058
+ const id = `lesson.${trajectory.taskId || hash}`;
1059
+ return {
1060
+ id,
1061
+ statement,
1062
+ positive: Array.from(positiveSignals).slice(0, 8),
1063
+ negative: Array.from(negativeSignals).slice(0, 8),
1064
+ confidence: corrections.length > 0 ? 0.95 : successes.length > 0 ? 0.85 : 0.65,
1065
+ evidence: [`trajectory:${trajectory.taskId}`],
1066
+ tags: Array.from(tags),
1067
+ reasoning
1068
+ };
1069
+ }
1070
+ function learnFromTrajectory(trajectory, existingKnowledge = [], options = {}) {
1071
+ const dryRun = options.dryRun ?? false;
1072
+ const storage = options.storage;
1073
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1074
+ const taskId = trajectory.taskId || `task_${Date.now()}`;
1075
+ const events = trajectory.events || [];
1076
+ const observations = [];
1077
+ events.forEach((event, idx) => {
1078
+ let obsType = "agent";
1079
+ let confidence = 0.75;
1080
+ const positive = [taskId, event.type];
1081
+ const negative = [];
1082
+ if (event.type === "user_correction") {
1083
+ obsType = "user";
1084
+ confidence = 0.98;
1085
+ } else if (event.type === "failure" || event.type === "revert") {
1086
+ obsType = "test";
1087
+ confidence = 0.95;
1088
+ negative.push(event.description.toLowerCase().replace(/[^a-z0-9_-]/g, "-"));
1089
+ } else if (event.type === "success") {
1090
+ obsType = "agent";
1091
+ confidence = 0.95;
1092
+ positive.push(event.description.toLowerCase().replace(/[^a-z0-9_-]/g, "-"));
1093
+ } else if (event.type === "hypothesis") {
1094
+ confidence = 0.5;
1095
+ }
1096
+ if (event.path) positive.push(event.path.toLowerCase());
1097
+ observations.push(
1098
+ createObservation({
1099
+ source: `trajectory:${taskId}:${event.type}:${idx}`,
1100
+ claim: `[${event.type.toUpperCase()}] ${event.description}`,
1101
+ provenance: {
1102
+ type: obsType,
1103
+ path: event.path,
1104
+ timestamp: event.timestamp || now
1105
+ },
1106
+ confidence,
1107
+ positive,
1108
+ negative: negative.length > 0 ? negative : void 0,
1109
+ details: { taskId, eventIndex: idx, eventType: event.type, ...event.data || {} }
1110
+ })
1111
+ );
1112
+ });
1113
+ const evidence = observations.map((obs) => {
1114
+ const isFailure = obs.claim.startsWith("[FAILURE]") || obs.claim.startsWith("[REVERT]");
1115
+ return createEvidence(obs, isFailure ? "contradicts" : "supports");
1116
+ });
1117
+ const distilled = distillTrajectory(trajectory);
1118
+ const candidateLessons = [];
1119
+ if (distilled) {
1120
+ candidateLessons.push({
1121
+ id: distilled.id,
1122
+ type: "lesson",
1123
+ statement: distilled.statement,
1124
+ positive: distilled.positive,
1125
+ negative: distilled.negative,
1126
+ confidence: distilled.confidence,
1127
+ evidence: evidence.map((e) => e.source),
1128
+ tags: distilled.tags,
1129
+ scope: "project"
1130
+ });
1131
+ }
1132
+ const reconciliations = [];
1133
+ const currentKnowledgeList = [...existingKnowledge];
1134
+ for (const candidate of candidateLessons) {
1135
+ const recResult = reconcileKnowledge(candidate, currentKnowledgeList);
1136
+ if (dryRun) {
1137
+ reconciliations.push({
1138
+ candidate,
1139
+ result: recResult,
1140
+ action: recResult.action
1141
+ });
1142
+ } else {
1143
+ const { updatedKnowledge, action } = applyReconciliation(
1144
+ recResult,
1145
+ candidate,
1146
+ currentKnowledgeList,
1147
+ storage
1148
+ );
1149
+ reconciliations.push({
1150
+ candidate,
1151
+ result: recResult,
1152
+ appliedKnowledge: updatedKnowledge,
1153
+ action
1154
+ });
1155
+ }
1156
+ }
1157
+ return {
1158
+ taskId,
1159
+ observations,
1160
+ evidence,
1161
+ candidateLessons,
1162
+ reconciliations,
1163
+ dryRun
1164
+ };
1165
+ }
1166
+ function processUserCorrection(params) {
1167
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1168
+ const prov = {
1169
+ type: "user",
1170
+ timestamp: now,
1171
+ ...params.author ? { author: params.author } : {}
1172
+ };
1173
+ const claim = `User correction on ${params.topic}: ${params.correctFact} (supersedes assumption: ${params.incorrectAssumption})`;
1174
+ const observation = createObservation({
1175
+ source: `user:correction:${params.topic.toLowerCase()}`,
1176
+ claim,
1177
+ provenance: prov,
1178
+ confidence: 1,
1179
+ positive: [params.topic.toLowerCase(), ...params.context ?? []],
1180
+ negative: [params.incorrectAssumption.toLowerCase()]
1181
+ });
1182
+ const evidence = createEvidence(observation, "supports");
1183
+ const knowledge = createKnowledge({
1184
+ id: `correction.${params.topic.toLowerCase().replace(/[^a-z0-9_-]/g, ".")}`,
1185
+ type: "rule",
1186
+ status: "active",
1187
+ statement: params.correctFact,
1188
+ positive: [params.topic.toLowerCase(), ...params.context ?? []],
1189
+ negative: [params.incorrectAssumption.toLowerCase()],
1190
+ confidence: 1,
1191
+ confidenceTier: "high",
1192
+ evidence: [evidence.id, observation.source],
1193
+ tags: ["user-correction", params.topic.toLowerCase()]
1194
+ });
1195
+ return { observation, knowledge };
1196
+ }
1197
+
1198
+ // src/core/dependencies.ts
1199
+ function normalizeSourceKey(source) {
1200
+ return source.replace(/^(file|code|test|doc):/, "").trim().toLowerCase();
1201
+ }
1202
+ function buildDependencyIndex(knowledgeList) {
1203
+ const sourceToKnowledge = /* @__PURE__ */ new Map();
1204
+ for (const k of knowledgeList) {
1205
+ const allDependencies = /* @__PURE__ */ new Set();
1206
+ if (k.depends_on) {
1207
+ for (const d of k.depends_on) {
1208
+ allDependencies.add(normalizeSourceKey(d));
1209
+ }
1210
+ }
1211
+ if (k.evidence) {
1212
+ for (const ev of k.evidence) {
1213
+ allDependencies.add(normalizeSourceKey(ev));
1214
+ }
1215
+ }
1216
+ for (const dep of allDependencies) {
1217
+ if (!dep) continue;
1218
+ let set = sourceToKnowledge.get(dep);
1219
+ if (!set) {
1220
+ set = /* @__PURE__ */ new Set();
1221
+ sourceToKnowledge.set(dep, set);
1222
+ }
1223
+ set.add(k.id);
1224
+ }
1225
+ }
1226
+ return { sourceToKnowledge };
1227
+ }
1228
+ function checkKnowledgeStaleness(knowledge, changedSources) {
1229
+ const normChanged = new Set(changedSources.map(normalizeSourceKey));
1230
+ const allDeps = [
1231
+ ...(knowledge.depends_on ?? []).map(normalizeSourceKey),
1232
+ ...knowledge.evidence.map(normalizeSourceKey)
1233
+ ];
1234
+ const changed = [];
1235
+ const unchanged = [];
1236
+ for (const dep of allDeps) {
1237
+ if (!dep) continue;
1238
+ const isChanged = normChanged.has(dep) || Array.from(normChanged).some((c) => dep.includes(c) || c.includes(dep));
1239
+ if (isChanged) {
1240
+ changed.push(dep);
1241
+ } else {
1242
+ unchanged.push(dep);
1243
+ }
1244
+ }
1245
+ if (changed.length > 0) {
1246
+ const reason = `Supporting dependency changed: ${changed.join(", ")}. Unchanged evidence: ${unchanged.join(", ") || "none"}`;
1247
+ return {
1248
+ knowledgeId: knowledge.id,
1249
+ affected: true,
1250
+ status: "needs_revalidation",
1251
+ changedSources: changed,
1252
+ unchangedSources: unchanged,
1253
+ reason
1254
+ };
1255
+ }
1256
+ return {
1257
+ knowledgeId: knowledge.id,
1258
+ affected: false,
1259
+ status: knowledge.status,
1260
+ changedSources: [],
1261
+ unchangedSources: allDeps
1262
+ };
1263
+ }
1264
+ function markStaleKnowledge(knowledgeList, changedSources) {
1265
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1266
+ let affectedCount = 0;
1267
+ const updatedKnowledge = [];
1268
+ for (const k of knowledgeList) {
1269
+ if (k.status === "superseded" || k.status === "invalid" || k.status === "invalidated") {
1270
+ updatedKnowledge.push(k);
1271
+ continue;
1272
+ }
1273
+ const res = checkKnowledgeStaleness(k, changedSources);
1274
+ if (res.affected) {
1275
+ affectedCount++;
1276
+ const history = k.history ?? [];
1277
+ const updated = {
1278
+ ...k,
1279
+ status: "needs_revalidation",
1280
+ updatedAt: now,
1281
+ history: [
1282
+ ...history,
1283
+ {
1284
+ timestamp: now,
1285
+ status: "needs_revalidation",
1286
+ action: "source_changed",
1287
+ reason: res.reason
1288
+ }
1289
+ ]
1290
+ };
1291
+ updatedKnowledge.push(updated);
1292
+ } else {
1293
+ updatedKnowledge.push(k);
1294
+ }
1295
+ }
1296
+ return { updatedKnowledge, affectedCount };
1297
+ }
1298
+ function checkKnowledgeListStaleness(knowledgeList, changedSources) {
1299
+ const { updatedKnowledge, affectedCount } = markStaleKnowledge(knowledgeList, changedSources);
1300
+ const affectedKnowledge = updatedKnowledge.filter((k) => k.status === "needs_revalidation");
1301
+ return { affectedKnowledge, updatedKnowledge, affectedCount };
1302
+ }
1303
+
1304
+ // src/jev/scorer.ts
1305
+ var statementTokenCache = /* @__PURE__ */ new WeakMap();
1306
+ function extractTokens(text) {
1307
+ if (!text) return /* @__PURE__ */ new Set();
1308
+ return new Set(
1309
+ text.toLowerCase().replace(/[^a-z0-9_-]/g, " ").split(/\s+/).filter((t) => t.length > 2)
1310
+ );
1311
+ }
1312
+ function getKnowledgeStatementTokens(k) {
1313
+ let tokens = statementTokenCache.get(k);
1314
+ if (!tokens) {
1315
+ tokens = extractTokens(k.statement);
1316
+ statementTokenCache.set(k, tokens);
1317
+ }
1318
+ return tokens;
1319
+ }
1320
+ function evaluateJev(knowledge, context) {
1321
+ const queryTokens = extractTokens(context.query);
1322
+ const contextPos = new Set((context.positive ?? []).map((p) => p.trim().toLowerCase()));
1323
+ const contextNeg = new Set((context.negative ?? []).map((n) => n.trim().toLowerCase()));
1324
+ const knowPos = new Set((knowledge.positive ?? []).map((p) => p.trim().toLowerCase()));
1325
+ const knowNeg = new Set((knowledge.negative ?? []).map((n) => n.trim().toLowerCase()));
1326
+ const negativeConflicts = [];
1327
+ for (const n of knowNeg) {
1328
+ if (contextPos.has(n) || queryTokens.has(n)) {
1329
+ negativeConflicts.push(`Knowledge forbids "${n}", which was present in context`);
1330
+ }
1331
+ }
1332
+ for (const p of knowPos) {
1333
+ if (contextNeg.has(p)) {
1334
+ negativeConflicts.push(`Context forbids "${p}", which is required by knowledge`);
1335
+ }
1336
+ }
1337
+ const positiveMatches = [];
1338
+ for (const p of knowPos) {
1339
+ if (contextPos.has(p)) {
1340
+ positiveMatches.push(`Context cue: ${p}`);
1341
+ } else {
1342
+ for (const q of queryTokens) {
1343
+ if (p === q || p.startsWith(q) || q.startsWith(p)) {
1344
+ positiveMatches.push(`Query term match: ${p}`);
1345
+ break;
1346
+ }
1347
+ }
1348
+ }
1349
+ }
1350
+ const stmtTokens = getKnowledgeStatementTokens(knowledge);
1351
+ let queryOverlapCount = 0;
1352
+ for (const q of queryTokens) {
1353
+ let matched = false;
1354
+ for (const st of stmtTokens) {
1355
+ if (q === st || q.startsWith(st) || st.startsWith(q)) {
1356
+ matched = true;
1357
+ break;
1358
+ }
1359
+ }
1360
+ if (matched) {
1361
+ queryOverlapCount++;
1362
+ positiveMatches.push(`Statement term: ${q}`);
1363
+ }
1364
+ }
1365
+ const totalCues = Math.max(1, knowPos.size);
1366
+ const matchedCuesCount = positiveMatches.filter((m2) => m2.startsWith("Context cue") || m2.startsWith("Query term")).length;
1367
+ const cueMatchRatio = matchedCuesCount / totalCues;
1368
+ const tokenOverlapScore = queryTokens.size > 0 ? queryOverlapCount / Math.max(1, queryTokens.size) : 0;
1369
+ const rawPositiveScore = 0.65 * cueMatchRatio + 0.35 * tokenOverlapScore;
1370
+ let scopeMultiplier = 1;
1371
+ const targetScope = (context.scope ?? "project").toLowerCase();
1372
+ const knowScope = (knowledge.scope ?? "project").toLowerCase();
1373
+ if (targetScope === knowScope) {
1374
+ scopeMultiplier = 1;
1375
+ } else if (knowScope === "global" || targetScope.startsWith(knowScope)) {
1376
+ scopeMultiplier = 0.85;
1377
+ } else if (knowScope === "project" && targetScope.startsWith("branch:")) {
1378
+ scopeMultiplier = 0.9;
1379
+ } else {
1380
+ scopeMultiplier = 0.3;
1381
+ }
1382
+ const m = knowledge.metrics;
1383
+ const evRatio = (m.support_count + 1) / (m.support_count + 2 * m.contradiction_count + 1);
1384
+ const confidenceFactor = knowledge.confidence * evRatio;
1385
+ let outcomeAdjustment = 0;
1386
+ if (m.successful_outcomes > 0) {
1387
+ outcomeAdjustment += Math.min(0.1, m.successful_outcomes * 0.03);
1388
+ }
1389
+ if (m.failed_outcomes > 0) {
1390
+ outcomeAdjustment -= Math.min(0.2, m.failed_outcomes * 0.05);
1391
+ }
1392
+ let score = (rawPositiveScore * 0.45 + confidenceFactor * 0.4 + outcomeAdjustment) * scopeMultiplier;
1393
+ if (knowledge.status === "stale") {
1394
+ score *= 0.6;
1395
+ } else if (knowledge.status === "contradicted") {
1396
+ score *= 0.3;
1397
+ } else if (knowledge.status === "invalid" || knowledge.status === "superseded") {
1398
+ score *= 0.1;
1399
+ }
1400
+ let status = "POSSIBLE";
1401
+ const reasons = [];
1402
+ if (negativeConflicts.length > 0) {
1403
+ score = Math.max(0.01, score * 0.2);
1404
+ status = "REJECT";
1405
+ reasons.push(`Rejected due to ${negativeConflicts.length} negative contextual conflicts.`);
1406
+ } else if (score >= 0.45 && positiveMatches.length > 0) {
1407
+ status = "MATCH";
1408
+ reasons.push(`Strong contextual match with ${positiveMatches.length} matching signals.`);
1409
+ } else if (score >= 0.25) {
1410
+ status = "POSSIBLE";
1411
+ reasons.push(`Potential match; partial contextual overlap.`);
1412
+ } else {
1413
+ status = "REJECT";
1414
+ reasons.push(`Insufficient contextual match score (${score.toFixed(2)} < 0.25).`);
1415
+ }
1416
+ const finalScore = Math.round(Math.min(1, Math.max(0, score)) * 100) / 100;
1417
+ const explanation = {
1418
+ positiveMatches,
1419
+ negativeConflicts,
1420
+ score: finalScore,
1421
+ confidence: knowledge.confidence,
1422
+ status,
1423
+ reasons
1424
+ };
1425
+ return {
1426
+ knowledge,
1427
+ status,
1428
+ score: finalScore,
1429
+ explanation
1430
+ };
1431
+ }
1432
+
1433
+ // src/jev/matcher.ts
1434
+ function rerankCandidates(candidates, context) {
1435
+ const results = [];
1436
+ for (const candidate of candidates) {
1437
+ const res = evaluateJev(candidate, context);
1438
+ results.push(res);
1439
+ }
1440
+ const statusPriority = {
1441
+ MATCH: 3,
1442
+ POSSIBLE: 2,
1443
+ REJECT: 1
1444
+ };
1445
+ results.sort((a, b) => {
1446
+ const prioDiff = (statusPriority[b.status] ?? 0) - (statusPriority[a.status] ?? 0);
1447
+ if (prioDiff !== 0) return prioDiff;
1448
+ const scoreDiff = b.score - a.score;
1449
+ if (Math.abs(scoreDiff) > 1e-3) return scoreDiff;
1450
+ return b.knowledge.confidence - a.knowledge.confidence;
1451
+ });
1452
+ return results;
1453
+ }
1454
+
1455
+ // src/search/indexer.ts
1456
+ import { create, insert, remove, search } from "@orama/orama";
1457
+ var SearchIndex = class {
1458
+ db = null;
1459
+ idToDocId = /* @__PURE__ */ new Map();
1460
+ async init() {
1461
+ this.db = await create({
1462
+ schema: {
1463
+ id: "string",
1464
+ statement: "string",
1465
+ type: "string",
1466
+ scope: "string",
1467
+ status: "string",
1468
+ positive: "string[]",
1469
+ negative: "string[]",
1470
+ tags: "string[]"
1471
+ }
1472
+ });
1473
+ this.idToDocId.clear();
1474
+ }
1475
+ async buildIndex(knowledgeList) {
1476
+ await this.init();
1477
+ for (const item of knowledgeList) {
1478
+ await this.upsert(item);
1479
+ }
1480
+ }
1481
+ async upsert(item) {
1482
+ if (!this.db) {
1483
+ await this.init();
1484
+ }
1485
+ const existingDocId = this.idToDocId.get(item.id);
1486
+ if (existingDocId) {
1487
+ try {
1488
+ await remove(this.db, existingDocId);
1489
+ } catch {
1490
+ }
1491
+ this.idToDocId.delete(item.id);
1492
+ }
1493
+ const docId = await insert(this.db, {
1494
+ id: item.id,
1495
+ statement: item.statement,
1496
+ type: item.type,
1497
+ scope: item.scope,
1498
+ status: item.status,
1499
+ positive: item.positive,
1500
+ negative: item.negative,
1501
+ tags: item.tags ?? []
1502
+ });
1503
+ this.idToDocId.set(item.id, docId);
1504
+ }
1505
+ async delete(id) {
1506
+ if (!this.db) return;
1507
+ const docId = this.idToDocId.get(id);
1508
+ if (docId) {
1509
+ try {
1510
+ await remove(this.db, docId);
1511
+ } catch {
1512
+ }
1513
+ this.idToDocId.delete(id);
1514
+ }
1515
+ }
1516
+ async searchCandidates(term, limit = 25) {
1517
+ if (!this.db) {
1518
+ return [];
1519
+ }
1520
+ const cleanTerm = term.trim();
1521
+ if (!cleanTerm) {
1522
+ return Array.from(this.idToDocId.keys()).slice(0, limit);
1523
+ }
1524
+ try {
1525
+ const results = await search(this.db, {
1526
+ term: cleanTerm,
1527
+ limit,
1528
+ threshold: 0.2
1529
+ });
1530
+ return results.hits.map((h) => h.document.id);
1531
+ } catch {
1532
+ return Array.from(this.idToDocId.keys()).slice(0, limit);
1533
+ }
1534
+ }
1535
+ size() {
1536
+ return this.idToDocId.size;
1537
+ }
1538
+ };
1539
+
1540
+ // src/search/retriever.ts
1541
+ var Retriever = class {
1542
+ constructor(index, store) {
1543
+ this.index = index;
1544
+ this.store = store;
1545
+ }
1546
+ index;
1547
+ store;
1548
+ /**
1549
+ * Executes the full Bilmem retrieval pipeline:
1550
+ * 1. Query / Context
1551
+ * 2. Candidate retrieval (small candidate set)
1552
+ * 3. JEV contextual reranking
1553
+ * 4. MATCH / POSSIBLE / REJECT classification with explanations
1554
+ */
1555
+ async lookup(options = {}) {
1556
+ const limit = options.limit ?? 5;
1557
+ const query = options.query?.trim() ?? "";
1558
+ const positive = options.positive ?? [];
1559
+ const negative = options.negative ?? [];
1560
+ const scope = options.scope ?? "project";
1561
+ const searchTerms = [];
1562
+ if (query) searchTerms.push(query);
1563
+ if (positive.length > 0) searchTerms.push(...positive.slice(0, 3));
1564
+ const searchTerm = searchTerms.join(" ").trim();
1565
+ let candidateIds = await this.index.searchCandidates(searchTerm, 30);
1566
+ let candidates = [];
1567
+ if (candidateIds.length === 0) {
1568
+ const all = await this.store.getAllKnowledge();
1569
+ candidates = all.filter((k) => k.status !== "invalid" && k.status !== "superseded");
1570
+ } else {
1571
+ for (const id of candidateIds) {
1572
+ const item = await this.store.getKnowledge(id);
1573
+ if (item && item.status !== "invalid" && item.status !== "superseded") {
1574
+ candidates.push(item);
1575
+ }
1576
+ }
1577
+ if (candidates.length < 5) {
1578
+ const all = await this.store.getAllKnowledge();
1579
+ for (const k of all) {
1580
+ if (!candidateIds.includes(k.id) && k.status !== "invalid" && k.status !== "superseded") {
1581
+ candidates.push(k);
1582
+ if (candidates.length >= 20) break;
1583
+ }
1584
+ }
1585
+ }
1586
+ }
1587
+ const queryContext = {
1588
+ query,
1589
+ positive,
1590
+ negative,
1591
+ scope
1592
+ };
1593
+ const reranked = rerankCandidates(candidates, queryContext);
1594
+ const filtered = options.includeRejected ? reranked : reranked.filter((r) => r.status !== "REJECT");
1595
+ return filtered.slice(0, limit);
1596
+ }
1597
+ };
1598
+
1599
+ // src/core/resolver.ts
1600
+ var RELATIVE_COSTS = {
1601
+ USE_KNOWLEDGE: 1,
1602
+ INSPECT_TEST: 2,
1603
+ INSPECT_FILE: 2,
1604
+ INSPECT_SYMBOL: 2.5,
1605
+ CHECK_DOCUMENTATION: 2.5,
1606
+ CHECK_GIT: 3,
1607
+ SEARCH_REPOSITORY: 4,
1608
+ ASK_USER: 3.5,
1609
+ // Context-dependent; cheaper than broad scan when specifically targeted
1610
+ STOP: 1
1611
+ };
1612
+ function tokenize2(text) {
1613
+ return text.toLowerCase().replace(/[^a-z0-9_-]/g, " ").split(/\s+/).filter((t) => t.length > 2);
1614
+ }
1615
+ async function resolveEpistemicQuery(options, storage, retriever) {
1616
+ const question = options.question.trim();
1617
+ const positive = (options.positive ?? []).map((p) => p.trim().toLowerCase());
1618
+ const negative = (options.negative ?? []).map((n) => n.trim().toLowerCase());
1619
+ const scope = options.scope ?? "project";
1620
+ const queryTokens = tokenize2(question);
1621
+ const contextualCues = Array.from(/* @__PURE__ */ new Set([...positive, ...queryTokens]));
1622
+ const allKnowledge = await storage.getAllKnowledge();
1623
+ let jevResults;
1624
+ if (retriever instanceof Retriever) {
1625
+ jevResults = await retriever.lookup({
1626
+ query: question,
1627
+ positive: options.positive,
1628
+ negative: options.negative,
1629
+ scope,
1630
+ limit: options.limit ?? 25,
1631
+ includeRejected: true
1632
+ });
1633
+ } else {
1634
+ let candidatePool = [];
1635
+ if (retriever instanceof SearchIndex) {
1636
+ const queryTerm = queryTokens.slice(0, 5).join(" ");
1637
+ const ids = await retriever.searchCandidates(queryTerm, 30);
1638
+ for (const id of ids) {
1639
+ const item = await storage.getKnowledge(id);
1640
+ if (item) candidatePool.push(item);
1641
+ }
1642
+ }
1643
+ if (candidatePool.length === 0) {
1644
+ candidatePool = allKnowledge;
1645
+ }
1646
+ for (const k of allKnowledge) {
1647
+ if (!candidatePool.some((c) => c.id === k.id) && k.positive.some((p) => contextualCues.includes(p))) {
1648
+ candidatePool.push(k);
1649
+ }
1650
+ }
1651
+ jevResults = rerankCandidates(candidatePool, {
1652
+ positive: options.positive,
1653
+ negative: options.negative,
1654
+ scope
1655
+ });
1656
+ }
1657
+ const matchingItems = [];
1658
+ const rejectedItems = [];
1659
+ for (const res of jevResults) {
1660
+ let jevReason = "INSUFFICIENT_CONTEXT";
1661
+ if (res.explanation.negativeConflicts.length > 0) {
1662
+ jevReason = "HARD_NEGATIVE";
1663
+ } else if (res.explanation.reasons.some((r) => r.toLowerCase().includes("scope"))) {
1664
+ jevReason = "SCOPE_MISMATCH";
1665
+ } else if (res.knowledge.status === "stale" || res.knowledge.status === "needs_revalidation") {
1666
+ jevReason = "STALE_KNOWLEDGE";
1667
+ }
1668
+ await storage.appendMetricEvent({
1669
+ type: "jev_result",
1670
+ knowledgeId: res.knowledge.id,
1671
+ result: res.status,
1672
+ reason: jevReason,
1673
+ timestamp: Date.now()
1674
+ });
1675
+ if (jevReason === "HARD_NEGATIVE") {
1676
+ await storage.appendMetricEvent({
1677
+ type: "jev_hard_negative",
1678
+ knowledgeId: res.knowledge.id,
1679
+ timestamp: Date.now()
1680
+ });
1681
+ }
1682
+ if (res.status === "REJECT") {
1683
+ const conflictMsg = res.explanation.negativeConflicts.length > 0 ? `${res.explanation.negativeConflicts.join(", ")} conflicts with current context` : res.explanation.reasons[0] || "Context mismatch";
1684
+ rejectedItems.push({
1685
+ id: res.knowledge.id,
1686
+ reason: conflictMsg
1687
+ });
1688
+ } else if (res.status === "MATCH" || res.status === "POSSIBLE") {
1689
+ matchingItems.push(res.knowledge);
1690
+ }
1691
+ }
1692
+ const receipts = await storage.getAllReceipts();
1693
+ const previouslyChecked = /* @__PURE__ */ new Set();
1694
+ const previouslyRejectedTargets = /* @__PURE__ */ new Map();
1695
+ for (const r of receipts) {
1696
+ const qOverlap = tokenize2(r.question).filter((t) => queryTokens.includes(t));
1697
+ if (qOverlap.length >= 2 || r.positive && r.positive.some((p) => contextualCues.includes(p))) {
1698
+ await storage.appendMetricEvent({
1699
+ type: "investigation_reused",
1700
+ investigationId: r.id,
1701
+ timestamp: Date.now()
1702
+ });
1703
+ for (const c of r.checked) {
1704
+ previouslyChecked.add(c);
1705
+ }
1706
+ for (const rej of r.rejected) {
1707
+ previouslyRejectedTargets.set(rej.target, rej.reason);
1708
+ }
1709
+ }
1710
+ }
1711
+ for (const m of matchingItems) {
1712
+ for (const ev of m.evidence) {
1713
+ previouslyChecked.add(ev.replace(/^(code|file|test|doc):/, ""));
1714
+ }
1715
+ if (m.depends_on) {
1716
+ for (const dep of m.depends_on) {
1717
+ previouslyChecked.add(dep.replace(/^(code|file|test|doc):/, ""));
1718
+ }
1719
+ }
1720
+ }
1721
+ const staleCandidates = matchingItems.filter((k) => k.status === "needs_revalidation" || k.status === "stale");
1722
+ const contradictedCandidates = matchingItems.filter((k) => k.status === "contradicted");
1723
+ const activeCandidates = matchingItems.filter(
1724
+ (k) => (k.status === "active" || k.stage === "active" || k.stage === "probation") && k.status !== "needs_revalidation" && k.status !== "stale" && k.status !== "contradicted" && k.status !== "invalid" && k.status !== "superseded"
1725
+ );
1726
+ const questionKnowledge = allKnowledge.filter((k) => k.type === "question");
1727
+ let learnedQuestionToAsk;
1728
+ for (const qk of questionKnowledge) {
1729
+ const matchesCues = qk.positive.some((p) => contextualCues.includes(p));
1730
+ const conflicts = qk.negative.some((n) => negative.includes(n));
1731
+ if (matchesCues && !conflicts && qk.question) {
1732
+ learnedQuestionToAsk = {
1733
+ question: qk.question,
1734
+ targetTopic: qk.resolves?.[0] || "uncertainty",
1735
+ reason: `Learned question for [${qk.positive.join(", ")}] uncertainty`
1736
+ };
1737
+ break;
1738
+ }
1739
+ }
1740
+ const coveredTokens = /* @__PURE__ */ new Set();
1741
+ for (const k of activeCandidates) {
1742
+ for (const t of tokenize2(k.statement)) coveredTokens.add(t);
1743
+ for (const p of k.positive) coveredTokens.add(p);
1744
+ }
1745
+ const missingAspects = [];
1746
+ for (const qt of queryTokens) {
1747
+ if (!["how", "what", "why", "when", "where", "are", "the", "for", "with", "does", "from"].includes(qt)) {
1748
+ if (!coveredTokens.has(qt)) {
1749
+ missingAspects.push(qt);
1750
+ }
1751
+ }
1752
+ }
1753
+ let state = "UNKNOWN";
1754
+ let reason;
1755
+ if (activeCandidates.length === 0 && staleCandidates.length > 0) {
1756
+ state = "STALE";
1757
+ reason = "STALE_EVIDENCE";
1758
+ } else if (activeCandidates.length === 0 && contradictedCandidates.length > 0) {
1759
+ state = "CONFLICTED";
1760
+ reason = "CONFLICTING_EVIDENCE";
1761
+ } else if (activeCandidates.length === 0) {
1762
+ state = "UNKNOWN";
1763
+ reason = rejectedItems.length > 0 && matchingItems.length === 0 ? "CONTEXT_MISMATCH" : "NOT_OBSERVED";
1764
+ } else if (missingAspects.length === 0 && activeCandidates.some((k) => k.confidence >= 0.85)) {
1765
+ state = "KNOWN";
1766
+ } else {
1767
+ state = "PARTIALLY_KNOWN";
1768
+ reason = "INSUFFICIENT_EVIDENCE";
1769
+ }
1770
+ const checkedArray = Array.from(previouslyChecked);
1771
+ let nextAction;
1772
+ let fallback;
1773
+ if (state === "KNOWN") {
1774
+ nextAction = {
1775
+ type: "USE_KNOWLEDGE",
1776
+ target: activeCandidates[0].id,
1777
+ reason: `Sufficient high-confidence knowledge available (${activeCandidates[0].id})`,
1778
+ relativeCost: RELATIVE_COSTS.USE_KNOWLEDGE,
1779
+ expectedInfoGain: 0.95
1780
+ };
1781
+ } else if (state === "CONFLICTED") {
1782
+ nextAction = {
1783
+ type: "ASK_USER",
1784
+ reason: "Opposing credible knowledge requires contextual disambiguation",
1785
+ relativeCost: RELATIVE_COSTS.ASK_USER,
1786
+ expectedInfoGain: 0.9
1787
+ };
1788
+ fallback = {
1789
+ type: "ask",
1790
+ question: `Conflicting conventions exist for "${question}". Which context applies?`
1791
+ };
1792
+ } else if (state === "STALE") {
1793
+ const staleItem = staleCandidates[0];
1794
+ const targetSource = staleItem.depends_on?.[0] || staleItem.evidence[0] || "source";
1795
+ nextAction = {
1796
+ type: "INSPECT_FILE",
1797
+ target: targetSource.replace(/^(code|file|test|doc):/, ""),
1798
+ reason: `Revalidate stale knowledge (${staleItem.id}) whose dependencies changed`,
1799
+ relativeCost: RELATIVE_COSTS.INSPECT_FILE,
1800
+ expectedInfoGain: 0.85
1801
+ };
1802
+ } else {
1803
+ if (learnedQuestionToAsk) {
1804
+ nextAction = {
1805
+ type: "ASK_USER",
1806
+ reason: learnedQuestionToAsk.reason,
1807
+ relativeCost: RELATIVE_COSTS.ASK_USER,
1808
+ expectedInfoGain: 0.9
1809
+ };
1810
+ fallback = {
1811
+ type: "ask",
1812
+ question: learnedQuestionToAsk.question
1813
+ };
1814
+ } else {
1815
+ const testCandidates = [
1816
+ `tests/${queryTokens[0] || "module"}.test.ts`,
1817
+ `test/${queryTokens[0] || "module"}.test.ts`,
1818
+ `tests/session-refresh.test.ts`
1819
+ ];
1820
+ const uninspectedTest = testCandidates.find(
1821
+ (t) => !previouslyChecked.has(t) && !previouslyRejectedTargets.has(t)
1822
+ );
1823
+ const fileCandidates = [
1824
+ `src/${queryTokens[0] || "auth"}/${queryTokens[1] || "index"}.ts`,
1825
+ `src/${queryTokens[0] || "index"}.ts`
1826
+ ];
1827
+ const uninspectedFile = fileCandidates.find(
1828
+ (f) => !previouslyChecked.has(f) && !previouslyRejectedTargets.has(f)
1829
+ );
1830
+ for (const t of testCandidates) {
1831
+ if (previouslyChecked.has(t)) {
1832
+ await storage.appendMetricEvent({
1833
+ type: "source_read_avoided",
1834
+ source: t,
1835
+ timestamp: Date.now()
1836
+ });
1837
+ }
1838
+ }
1839
+ for (const f of fileCandidates) {
1840
+ if (previouslyChecked.has(f)) {
1841
+ await storage.appendMetricEvent({
1842
+ type: "source_read_avoided",
1843
+ source: f,
1844
+ timestamp: Date.now()
1845
+ });
1846
+ }
1847
+ }
1848
+ if (uninspectedTest && missingAspects.some((m) => m.includes("order") || m.includes("test") || m.includes("behavior"))) {
1849
+ nextAction = {
1850
+ type: "INSPECT_TEST",
1851
+ target: uninspectedTest,
1852
+ reason: `Targeted test inspection for unconfirmed aspect: ${missingAspects.join(", ")}`,
1853
+ relativeCost: RELATIVE_COSTS.INSPECT_TEST,
1854
+ expectedInfoGain: 0.8
1855
+ };
1856
+ } else if (uninspectedFile) {
1857
+ nextAction = {
1858
+ type: "INSPECT_FILE",
1859
+ target: uninspectedFile,
1860
+ reason: `Direct source inspection for missing context: ${missingAspects.join(", ")}`,
1861
+ relativeCost: RELATIVE_COSTS.INSPECT_FILE,
1862
+ expectedInfoGain: 0.75
1863
+ };
1864
+ } else {
1865
+ nextAction = {
1866
+ type: "SEARCH_REPOSITORY",
1867
+ target: missingAspects.slice(0, 3).join(" ") || question,
1868
+ reason: `Targeted search for unobserved aspects: ${missingAspects.join(", ")}`,
1869
+ relativeCost: RELATIVE_COSTS.SEARCH_REPOSITORY,
1870
+ expectedInfoGain: 0.6
1871
+ };
1872
+ }
1873
+ fallback = {
1874
+ type: "ask",
1875
+ question: `Could you clarify: ${question}? (Missing: ${missingAspects.join(", ") || "specifics"})`
1876
+ };
1877
+ }
1878
+ }
1879
+ if (state === "KNOWN" || nextAction && nextAction.type !== "SEARCH_REPOSITORY" && nextAction.type !== "STOP") {
1880
+ await storage.appendMetricEvent({
1881
+ type: "broad_search_avoided",
1882
+ targetAction: nextAction?.type,
1883
+ timestamp: Date.now()
1884
+ });
1885
+ }
1886
+ for (const k of activeCandidates) {
1887
+ await storage.appendMetricEvent({
1888
+ type: "knowledge_reused",
1889
+ knowledgeId: k.id,
1890
+ timestamp: Date.now()
1891
+ });
1892
+ }
1893
+ if (learnedQuestionToAsk || fallback?.type === "ask") {
1894
+ await storage.appendMetricEvent({
1895
+ type: "question_suggested",
1896
+ question: learnedQuestionToAsk?.question || fallback?.question,
1897
+ timestamp: Date.now()
1898
+ });
1899
+ }
1900
+ await storage.appendMetricEvent({
1901
+ type: "resolve",
1902
+ state,
1903
+ action: nextAction?.type,
1904
+ question,
1905
+ timestamp: Date.now()
1906
+ });
1907
+ const receiptId = `receipt_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 6)}`;
1908
+ const receipt = {
1909
+ id: receiptId,
1910
+ question,
1911
+ checked: checkedArray,
1912
+ findings: activeCandidates.map((c) => c.statement),
1913
+ rejected: rejectedItems.map((r) => ({ target: r.id, reason: r.reason })),
1914
+ missing: missingAspects,
1915
+ next: nextAction,
1916
+ status: state === "KNOWN" ? "resolved" : "unresolved",
1917
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1918
+ positive,
1919
+ negative
1920
+ };
1921
+ await storage.saveReceipt(receipt);
1922
+ return {
1923
+ state,
1924
+ reason,
1925
+ known: activeCandidates.map((k) => ({
1926
+ id: k.id,
1927
+ statement: k.statement,
1928
+ confidence: k.confidenceTier ?? k.confidence,
1929
+ confidenceTier: k.confidenceTier,
1930
+ stage: k.stage
1931
+ })),
1932
+ applicable: positive.length > 0 ? positive : contextualCues.slice(0, 5),
1933
+ rejected: rejectedItems,
1934
+ missing: missingAspects,
1935
+ checked: checkedArray,
1936
+ next: nextAction,
1937
+ fallback,
1938
+ receiptId
1939
+ };
1940
+ }
1941
+ export {
1942
+ EvidenceGraph,
1943
+ MetricsTracker,
1944
+ RELATIVE_COSTS,
1945
+ applyOutcomeToKnowledge,
1946
+ applyReconciliation,
1947
+ buildDependencyIndex,
1948
+ calculateEffectiveConfidence,
1949
+ calculateKnowledgeValue,
1950
+ checkKnowledgeListStaleness,
1951
+ checkKnowledgeStaleness,
1952
+ confidenceToTier,
1953
+ createEvidence,
1954
+ createKnowledge,
1955
+ createObservation,
1956
+ createOutcome,
1957
+ createUnknown,
1958
+ detectPolarContradiction,
1959
+ determineInitialStage,
1960
+ distillTrajectory,
1961
+ enforceKnowledgeBudget,
1962
+ evaluatePromotion,
1963
+ hasBeenInvestigated,
1964
+ hashString,
1965
+ learnFromTrajectory,
1966
+ markStaleKnowledge,
1967
+ normalizeSourceKey,
1968
+ processUserCorrection,
1969
+ reconcileKnowledge,
1970
+ recordOutcomeOnKnowledge,
1971
+ resolveEpistemicQuery,
1972
+ tierToConfidence,
1973
+ updateUnknown
1974
+ };
1975
+ //# sourceMappingURL=index.js.map