learning-agent 0.2.1 → 0.2.2

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/dist/index.d.ts CHANGED
@@ -63,6 +63,24 @@ declare const LessonSchema: z.ZodObject<{
63
63
  }>>;
64
64
  deleted: z.ZodOptional<z.ZodBoolean>;
65
65
  retrievalCount: z.ZodOptional<z.ZodNumber>;
66
+ citation: z.ZodOptional<z.ZodObject<{
67
+ file: z.ZodString;
68
+ line: z.ZodOptional<z.ZodNumber>;
69
+ commit: z.ZodOptional<z.ZodString>;
70
+ }, "strip", z.ZodTypeAny, {
71
+ file: string;
72
+ line?: number | undefined;
73
+ commit?: string | undefined;
74
+ }, {
75
+ file: string;
76
+ line?: number | undefined;
77
+ commit?: string | undefined;
78
+ }>>;
79
+ compactionLevel: z.ZodOptional<z.ZodUnion<[z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>]>>;
80
+ compactedAt: z.ZodOptional<z.ZodString>;
81
+ lastRetrieved: z.ZodOptional<z.ZodString>;
82
+ invalidatedAt: z.ZodOptional<z.ZodString>;
83
+ invalidationReason: z.ZodOptional<z.ZodString>;
66
84
  }, "strip", z.ZodTypeAny, {
67
85
  type: "quick" | "full";
68
86
  id: string;
@@ -86,6 +104,16 @@ declare const LessonSchema: z.ZodObject<{
86
104
  } | undefined;
87
105
  deleted?: boolean | undefined;
88
106
  retrievalCount?: number | undefined;
107
+ citation?: {
108
+ file: string;
109
+ line?: number | undefined;
110
+ commit?: string | undefined;
111
+ } | undefined;
112
+ compactionLevel?: 0 | 1 | 2 | undefined;
113
+ compactedAt?: string | undefined;
114
+ lastRetrieved?: string | undefined;
115
+ invalidatedAt?: string | undefined;
116
+ invalidationReason?: string | undefined;
89
117
  }, {
90
118
  type: "quick" | "full";
91
119
  id: string;
@@ -109,6 +137,16 @@ declare const LessonSchema: z.ZodObject<{
109
137
  } | undefined;
110
138
  deleted?: boolean | undefined;
111
139
  retrievalCount?: number | undefined;
140
+ citation?: {
141
+ file: string;
142
+ line?: number | undefined;
143
+ commit?: string | undefined;
144
+ } | undefined;
145
+ compactionLevel?: 0 | 1 | 2 | undefined;
146
+ compactedAt?: string | undefined;
147
+ lastRetrieved?: string | undefined;
148
+ invalidatedAt?: string | undefined;
149
+ invalidationReason?: string | undefined;
112
150
  }>;
113
151
  declare const TombstoneSchema: z.ZodObject<{
114
152
  id: z.ZodString;
package/dist/index.js CHANGED
@@ -22,7 +22,23 @@ var PatternSchema = z.object({
22
22
  bad: z.string(),
23
23
  good: z.string()
24
24
  });
25
+ var CitationSchema = z.object({
26
+ file: z.string().min(1),
27
+ // Source file path (required, non-empty)
28
+ line: z.number().int().positive().optional(),
29
+ // Line number (optional, must be positive)
30
+ commit: z.string().optional()
31
+ // Git commit hash (optional)
32
+ });
25
33
  var SeveritySchema = z.enum(["high", "medium", "low"]);
34
+ var CompactionLevelSchema = z.union([
35
+ z.literal(0),
36
+ // Active
37
+ z.literal(1),
38
+ // Flagged (>90 days)
39
+ z.literal(2)
40
+ // Archived
41
+ ]);
26
42
  var LessonTypeSchema = z.enum(["quick", "full"]);
27
43
  var LessonSchema = z.object({
28
44
  // Core identity (required)
@@ -46,7 +62,20 @@ var LessonSchema = z.object({
46
62
  pattern: PatternSchema.optional(),
47
63
  // Lifecycle fields (optional)
48
64
  deleted: z.boolean().optional(),
49
- retrievalCount: z.number().optional()
65
+ retrievalCount: z.number().optional(),
66
+ // Provenance tracking (optional)
67
+ citation: CitationSchema.optional(),
68
+ // Age-based validity fields (optional)
69
+ compactionLevel: CompactionLevelSchema.optional(),
70
+ // 0=active, 1=flagged, 2=archived
71
+ compactedAt: z.string().optional(),
72
+ // ISO8601 when compaction happened
73
+ lastRetrieved: z.string().optional(),
74
+ // ISO8601 last retrieval time
75
+ // Invalidation fields (optional - for marking lessons as wrong)
76
+ invalidatedAt: z.string().optional(),
77
+ // ISO8601
78
+ invalidationReason: z.string().optional()
50
79
  });
51
80
  var TombstoneSchema = z.object({
52
81
  id: z.string(),
@@ -150,7 +179,15 @@ var SCHEMA_SQL = `
150
179
  retrieval_count INTEGER NOT NULL DEFAULT 0,
151
180
  last_retrieved TEXT,
152
181
  embedding BLOB,
153
- content_hash TEXT
182
+ content_hash TEXT,
183
+ -- v0.2.2 fields
184
+ invalidated_at TEXT,
185
+ invalidation_reason TEXT,
186
+ citation_file TEXT,
187
+ citation_line INTEGER,
188
+ citation_commit TEXT,
189
+ compaction_level INTEGER DEFAULT 0,
190
+ compacted_at TEXT
154
191
  );
155
192
 
156
193
  -- FTS5 virtual table for full-text search
@@ -265,6 +302,28 @@ function rowToLesson(row) {
265
302
  if (row.retrieval_count > 0) {
266
303
  lesson.retrievalCount = row.retrieval_count;
267
304
  }
305
+ if (row.invalidated_at !== null) {
306
+ lesson.invalidatedAt = row.invalidated_at;
307
+ }
308
+ if (row.invalidation_reason !== null) {
309
+ lesson.invalidationReason = row.invalidation_reason;
310
+ }
311
+ if (row.citation_file !== null) {
312
+ lesson.citation = {
313
+ file: row.citation_file,
314
+ ...row.citation_line !== null && { line: row.citation_line },
315
+ ...row.citation_commit !== null && { commit: row.citation_commit }
316
+ };
317
+ }
318
+ if (row.compaction_level !== null && row.compaction_level !== 0) {
319
+ lesson.compactionLevel = row.compaction_level;
320
+ }
321
+ if (row.compacted_at !== null) {
322
+ lesson.compactedAt = row.compacted_at;
323
+ }
324
+ if (row.last_retrieved !== null) {
325
+ lesson.lastRetrieved = row.last_retrieved;
326
+ }
268
327
  return lesson;
269
328
  }
270
329
  function collectCachedEmbeddings(database) {
@@ -278,8 +337,8 @@ function collectCachedEmbeddings(database) {
278
337
  return cache;
279
338
  }
280
339
  var INSERT_LESSON_SQL = `
281
- INSERT INTO lessons (id, type, trigger, insight, evidence, severity, tags, source, context, supersedes, related, created, confirmed, deleted, retrieval_count, last_retrieved, embedding, content_hash)
282
- VALUES (@id, @type, @trigger, @insight, @evidence, @severity, @tags, @source, @context, @supersedes, @related, @created, @confirmed, @deleted, @retrieval_count, @last_retrieved, @embedding, @content_hash)
340
+ INSERT INTO lessons (id, type, trigger, insight, evidence, severity, tags, source, context, supersedes, related, created, confirmed, deleted, retrieval_count, last_retrieved, embedding, content_hash, invalidated_at, invalidation_reason, citation_file, citation_line, citation_commit, compaction_level, compacted_at)
341
+ VALUES (@id, @type, @trigger, @insight, @evidence, @severity, @tags, @source, @context, @supersedes, @related, @created, @confirmed, @deleted, @retrieval_count, @last_retrieved, @embedding, @content_hash, @invalidated_at, @invalidation_reason, @citation_file, @citation_line, @citation_commit, @compaction_level, @compacted_at)
283
342
  `;
284
343
  function getJsonlMtime(repoRoot) {
285
344
  const jsonlPath = join(repoRoot, LESSONS_PATH);
@@ -331,10 +390,17 @@ async function rebuildIndex(repoRoot) {
331
390
  confirmed: lesson.confirmed ? 1 : 0,
332
391
  deleted: lesson.deleted ? 1 : 0,
333
392
  retrieval_count: lesson.retrievalCount ?? 0,
334
- last_retrieved: null,
335
- // Reset on rebuild since we're rebuilding from source
393
+ last_retrieved: lesson.lastRetrieved ?? null,
336
394
  embedding: hasValidCache ? cached.embedding : null,
337
- content_hash: hasValidCache ? cached.contentHash : null
395
+ content_hash: hasValidCache ? cached.contentHash : null,
396
+ // v0.2.2 fields
397
+ invalidated_at: lesson.invalidatedAt ?? null,
398
+ invalidation_reason: lesson.invalidationReason ?? null,
399
+ citation_file: lesson.citation?.file ?? null,
400
+ citation_line: lesson.citation?.line ?? null,
401
+ citation_commit: lesson.citation?.commit ?? null,
402
+ compaction_level: lesson.compactionLevel ?? 0,
403
+ compacted_at: lesson.compactedAt ?? null
338
404
  });
339
405
  }
340
406
  });
@@ -369,6 +435,7 @@ async function searchKeyword(repoRoot, query, limit) {
369
435
  FROM lessons l
370
436
  JOIN lessons_fts fts ON l.rowid = fts.rowid
371
437
  WHERE lessons_fts MATCH ?
438
+ AND l.invalidated_at IS NULL
372
439
  LIMIT ?
373
440
  `
374
441
  ).all(query, limit);
@@ -462,6 +529,7 @@ async function searchVector(repoRoot, query, options) {
462
529
  const queryVector = await embedText(query);
463
530
  const scored = [];
464
531
  for (const lesson of lessons) {
532
+ if (lesson.invalidatedAt) continue;
465
533
  const lessonText = `${lesson.trigger} ${lesson.insight}`;
466
534
  const hash = contentHash(lesson.trigger, lesson.insight);
467
535
  let lessonVector = getCachedEmbedding(repoRoot, lesson.id, hash);
@@ -476,6 +544,14 @@ async function searchVector(repoRoot, query, options) {
476
544
  return scored.slice(0, limit);
477
545
  }
478
546
 
547
+ // src/utils.ts
548
+ var MS_PER_DAY = 24 * 60 * 60 * 1e3;
549
+ function getLessonAgeDays(lesson) {
550
+ const created = new Date(lesson.created).getTime();
551
+ const now = Date.now();
552
+ return Math.floor((now - created) / MS_PER_DAY);
553
+ }
554
+
479
555
  // src/search/ranking.ts
480
556
  var RECENCY_THRESHOLD_DAYS = 30;
481
557
  var HIGH_SEVERITY_BOOST = 1.5;
@@ -496,10 +572,7 @@ function severityBoost(lesson) {
496
572
  }
497
573
  }
498
574
  function recencyBoost(lesson) {
499
- const created = new Date(lesson.created);
500
- const now = /* @__PURE__ */ new Date();
501
- const ageMs = now.getTime() - created.getTime();
502
- const ageDays = Math.floor(ageMs / (1e3 * 60 * 60 * 24));
575
+ const ageDays = getLessonAgeDays(lesson);
503
576
  return ageDays <= RECENCY_THRESHOLD_DAYS ? RECENCY_BOOST : 1;
504
577
  }
505
578
  function confirmationBoost(lesson) {
@@ -690,7 +763,7 @@ function isFullLesson(lesson) {
690
763
  async function loadSessionLessons(repoRoot, limit = DEFAULT_LIMIT2) {
691
764
  const { lessons: allLessons } = await readLessons(repoRoot);
692
765
  const highSeverityLessons = allLessons.filter(
693
- (lesson) => isFullLesson(lesson) && lesson.severity === "high" && lesson.confirmed
766
+ (lesson) => isFullLesson(lesson) && lesson.severity === "high" && lesson.confirmed && !lesson.invalidatedAt
694
767
  );
695
768
  highSeverityLessons.sort((a, b) => {
696
769
  const dateA = new Date(a.created).getTime();