lancedb-opencode-pro 0.1.4 → 0.1.6

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 CHANGED
@@ -272,7 +272,7 @@ Example output:
272
272
  ```json
273
273
  {
274
274
  "scope": "project:my-project",
275
- "totalEvents": 12,
275
+ "totalEvents": 14,
276
276
  "capture": {
277
277
  "considered": 4,
278
278
  "stored": 3,
@@ -283,11 +283,24 @@ Example output:
283
283
  }
284
284
  },
285
285
  "recall": {
286
- "requested": 3,
286
+ "requested": 4,
287
287
  "injected": 2,
288
- "returnedResults": 2,
289
- "hitRate": 0.67,
290
- "injectionRate": 0.67
288
+ "returnedResults": 3,
289
+ "hitRate": 0.75,
290
+ "injectionRate": 0.5,
291
+ "auto": {
292
+ "requested": 3,
293
+ "injected": 2,
294
+ "returnedResults": 2,
295
+ "hitRate": 0.67,
296
+ "injectionRate": 0.67
297
+ },
298
+ "manual": {
299
+ "requested": 1,
300
+ "returnedResults": 1,
301
+ "hitRate": 1
302
+ },
303
+ "manualRescueRatio": 0.33
291
304
  },
292
305
  "feedback": {
293
306
  "missing": 1,
@@ -306,7 +319,10 @@ Example output:
306
319
  Key fields:
307
320
 
308
321
  - `capture.successRate`: how often a considered candidate was stored.
309
- - `recall.hitRate`: how often a recall request returned at least one result.
322
+ - `recall.hitRate`: blended rate across auto and manual recall — how often any recall request returned at least one result.
323
+ - `recall.auto.*`: metrics for automatic recall injected into the system prompt during `experimental.chat.system.transform`.
324
+ - `recall.manual.*`: metrics for user-initiated `memory_search` calls; `injected` is always false for manual searches.
325
+ - `recall.manualRescueRatio`: `manual.requested / auto.requested` — a proxy for how often users still need to search manually despite automatic recall.
310
326
  - `feedback.falsePositiveRate`: wrong-memory reports divided by stored memories.
311
327
  - `feedback.falseNegativeRate`: missing-memory reports relative to capture attempts.
312
328
 
@@ -315,6 +331,8 @@ Key fields:
315
331
  In real OpenCode usage, auto-capture and recall happen in the background, so explicit `memory_feedback_*` events are often sparse.
316
332
 
317
333
  - Treat `capture.*` and `recall.*` as system-health metrics: they show whether the memory pipeline is running.
334
+ - Treat `recall.auto.*` and `recall.manual.*` separately: auto metrics reflect pipeline health; manual metrics reflect whether users still need to rescue context manually.
335
+ - Treat `recall.manualRescueRatio` as a proxy for manual rescue rate: a high ratio suggests automatic recall is not surfacing relevant context on its own.
318
336
  - Treat repeated-context reduction, clarification burden, manual memory rescue, correction signals, and sampled audits as product-value signals: they show whether memory actually helped the user.
319
337
  - Treat `feedback.* = 0` as insufficient evidence, not proof that memory quality is good.
320
338
  - Treat a high `recall.hitRate` or `recall.injectionRate` as recall availability only; those values do not prove usefulness by themselves.
package/dist/index.js CHANGED
@@ -64,6 +64,7 @@ const plugin = async (input) => {
64
64
  await state.store.putEvent({
65
65
  id: generateId(),
66
66
  type: "recall",
67
+ source: "system-transform",
67
68
  scope: activeScope,
68
69
  sessionID: eventInput.sessionID,
69
70
  timestamp: Date.now(),
@@ -117,6 +118,17 @@ const plugin = async (input) => {
117
118
  recencyHalfLifeHours: state.config.retrieval.recencyHalfLifeHours,
118
119
  importanceWeight: state.config.retrieval.importanceWeight,
119
120
  });
121
+ await state.store.putEvent({
122
+ id: generateId(),
123
+ type: "recall",
124
+ source: "manual-search",
125
+ scope: activeScope,
126
+ sessionID: context.sessionID,
127
+ timestamp: Date.now(),
128
+ resultCount: results.length,
129
+ injected: false,
130
+ metadataJson: JSON.stringify({ source: "manual-search" }),
131
+ });
120
132
  if (results.length === 0)
121
133
  return "No relevant memory found.";
122
134
  return results
package/dist/store.d.ts CHANGED
@@ -44,4 +44,5 @@ export declare class MemoryStore {
44
44
  private readEventsByScopes;
45
45
  private readByScopes;
46
46
  private ensureIndexes;
47
+ private ensureEventTableCompatibility;
47
48
  }
package/dist/store.js CHANGED
@@ -3,6 +3,7 @@ import { dirname } from "node:path";
3
3
  import { tokenize } from "./utils.js";
4
4
  const TABLE_NAME = "memories";
5
5
  const EVENTS_TABLE_NAME = "effectiveness_events";
6
+ const EVENTS_SOURCE_COLUMN = "source";
6
7
  export class MemoryStore {
7
8
  dbPath;
8
9
  lancedb = null;
@@ -59,6 +60,7 @@ export class MemoryStore {
59
60
  skipReason: "",
60
61
  resultCount: 0,
61
62
  injected: false,
63
+ source: "",
62
64
  feedbackType: "",
63
65
  helpful: -1,
64
66
  reason: "",
@@ -68,6 +70,7 @@ export class MemoryStore {
68
70
  this.eventTable = await this.connection.createTable(EVENTS_TABLE_NAME, [bootstrapEvent]);
69
71
  await this.eventTable.delete("id = '__bootstrap__'");
70
72
  }
73
+ await this.ensureEventTableCompatibility();
71
74
  await this.ensureIndexes();
72
75
  }
73
76
  async put(record) {
@@ -89,6 +92,7 @@ export class MemoryStore {
89
92
  skipReason: event.type === "capture" ? event.skipReason ?? "" : "",
90
93
  resultCount: event.type === "recall" ? event.resultCount : 0,
91
94
  injected: event.type === "recall" ? event.injected : false,
95
+ source: event.type === "recall" ? event.source ?? "" : "",
92
96
  feedbackType: event.type === "feedback" ? event.feedbackType : "",
93
97
  helpful: event.type === "feedback" ? (event.helpful === undefined ? -1 : event.helpful ? 1 : 0) : -1,
94
98
  reason: event.type === "feedback" ? event.reason ?? "" : "",
@@ -207,6 +211,11 @@ export class MemoryStore {
207
211
  let recallRequested = 0;
208
212
  let recallInjected = 0;
209
213
  let recallReturnedResults = 0;
214
+ let autoRecallRequested = 0;
215
+ let autoRecallInjected = 0;
216
+ let autoRecallReturnedResults = 0;
217
+ let manualRecallRequested = 0;
218
+ let manualRecallReturnedResults = 0;
210
219
  let feedbackMissing = 0;
211
220
  let feedbackWrong = 0;
212
221
  let feedbackUsefulPositive = 0;
@@ -230,6 +239,19 @@ export class MemoryStore {
230
239
  recallReturnedResults += 1;
231
240
  if (event.injected)
232
241
  recallInjected += 1;
242
+ const recallSource = event.source ?? "system-transform";
243
+ if (recallSource === "manual-search") {
244
+ manualRecallRequested += 1;
245
+ if (event.resultCount > 0)
246
+ manualRecallReturnedResults += 1;
247
+ }
248
+ else {
249
+ autoRecallRequested += 1;
250
+ if (event.resultCount > 0)
251
+ autoRecallReturnedResults += 1;
252
+ if (event.injected)
253
+ autoRecallInjected += 1;
254
+ }
233
255
  }
234
256
  if (event.type === "feedback") {
235
257
  if (event.feedbackType === "missing")
@@ -262,6 +284,19 @@ export class MemoryStore {
262
284
  returnedResults: recallReturnedResults,
263
285
  hitRate: recallRequested === 0 ? 0 : recallReturnedResults / recallRequested,
264
286
  injectionRate: recallRequested === 0 ? 0 : recallInjected / recallRequested,
287
+ auto: {
288
+ requested: autoRecallRequested,
289
+ injected: autoRecallInjected,
290
+ returnedResults: autoRecallReturnedResults,
291
+ hitRate: autoRecallRequested === 0 ? 0 : autoRecallReturnedResults / autoRecallRequested,
292
+ injectionRate: autoRecallRequested === 0 ? 0 : autoRecallInjected / autoRecallRequested,
293
+ },
294
+ manual: {
295
+ requested: manualRecallRequested,
296
+ returnedResults: manualRecallReturnedResults,
297
+ hitRate: manualRecallRequested === 0 ? 0 : manualRecallReturnedResults / manualRecallRequested,
298
+ },
299
+ manualRescueRatio: autoRecallRequested === 0 ? 0 : manualRecallRequested / autoRecallRequested,
265
300
  },
266
301
  feedback: {
267
302
  missing: feedbackMissing,
@@ -346,6 +381,7 @@ export class MemoryStore {
346
381
  "skipReason",
347
382
  "resultCount",
348
383
  "injected",
384
+ "source",
349
385
  "feedbackType",
350
386
  "helpful",
351
387
  "reason",
@@ -411,6 +447,21 @@ export class MemoryStore {
411
447
  this.indexState.ftsError = error instanceof Error ? error.message : String(error);
412
448
  }
413
449
  }
450
+ async ensureEventTableCompatibility() {
451
+ const table = this.requireEventTable();
452
+ const schema = await table.schema();
453
+ const hasSourceColumn = schema.fields.some((field) => field.name === EVENTS_SOURCE_COLUMN);
454
+ if (hasSourceColumn) {
455
+ return;
456
+ }
457
+ try {
458
+ await table.addColumns([{ name: EVENTS_SOURCE_COLUMN, valueSql: "CAST(NULL AS STRING)" }]);
459
+ }
460
+ catch (error) {
461
+ const reason = error instanceof Error ? error.message : String(error);
462
+ throw new Error(`Failed to patch ${EVENTS_TABLE_NAME} schema for ${EVENTS_SOURCE_COLUMN}: ${reason}`);
463
+ }
464
+ }
414
465
  }
415
466
  function normalizeRow(row) {
416
467
  const vectorRaw = row.vector;
@@ -456,11 +507,14 @@ function normalizeEventRow(row) {
456
507
  };
457
508
  }
458
509
  if (row.type === "recall") {
510
+ const sourceRaw = typeof row.source === "string" && row.source.length > 0 ? row.source : "system-transform";
511
+ const source = sourceRaw === "manual-search" ? "manual-search" : "system-transform";
459
512
  return {
460
513
  ...base,
461
514
  type: "recall",
462
515
  resultCount: Number(row.resultCount ?? 0),
463
516
  injected: Boolean(row.injected),
517
+ source,
464
518
  };
465
519
  }
466
520
  if (row.type === "feedback") {
package/dist/types.d.ts CHANGED
@@ -4,6 +4,7 @@ export type MemoryCategory = "preference" | "fact" | "decision" | "entity" | "ot
4
4
  export type CaptureOutcome = "considered" | "skipped" | "stored";
5
5
  export type CaptureSkipReason = "empty-buffer" | "below-min-chars" | "no-positive-signal" | "initialization-unavailable" | "embedding-unavailable" | "empty-embedding";
6
6
  export type FeedbackType = "missing" | "wrong" | "useful";
7
+ export type RecallSource = "system-transform" | "manual-search";
7
8
  export interface EmbeddingConfig {
8
9
  provider: EmbeddingProvider;
9
10
  model: string;
@@ -76,6 +77,7 @@ export interface RecallEvent extends MemoryEffectivenessEventBase {
76
77
  type: "recall";
77
78
  resultCount: number;
78
79
  injected: boolean;
80
+ source?: RecallSource;
79
81
  }
80
82
  export interface FeedbackEvent extends MemoryEffectivenessEventBase {
81
83
  type: "feedback";
@@ -101,6 +103,19 @@ export interface EffectivenessSummary {
101
103
  returnedResults: number;
102
104
  hitRate: number;
103
105
  injectionRate: number;
106
+ auto: {
107
+ requested: number;
108
+ injected: number;
109
+ returnedResults: number;
110
+ hitRate: number;
111
+ injectionRate: number;
112
+ };
113
+ manual: {
114
+ requested: number;
115
+ returnedResults: number;
116
+ hitRate: number;
117
+ };
118
+ manualRescueRatio: number;
104
119
  };
105
120
  feedback: {
106
121
  missing: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lancedb-opencode-pro",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "LanceDB-backed long-term memory provider for OpenCode",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -63,5 +63,10 @@
63
63
  "devDependencies": {
64
64
  "@types/node": "^22.13.9",
65
65
  "typescript": "^5.8.2"
66
- }
66
+ },
67
+ "directories": {
68
+ "doc": "docs",
69
+ "test": "test"
70
+ },
71
+ "author": ""
67
72
  }