@velvetmonkey/vault-core 2.0.134 → 2.0.136

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/sqlite.d.ts CHANGED
@@ -75,7 +75,7 @@ export interface StateDb {
75
75
  close: () => void;
76
76
  }
77
77
  /** Current schema version - bump when schema changes */
78
- export declare const SCHEMA_VERSION = 30;
78
+ export declare const SCHEMA_VERSION = 31;
79
79
  /** State database filename */
80
80
  export declare const STATE_DB_FILENAME = "state.db";
81
81
  /** Directory for flywheel state */
package/dist/sqlite.js CHANGED
@@ -16,7 +16,7 @@ import * as path from 'path';
16
16
  // Constants
17
17
  // =============================================================================
18
18
  /** Current schema version - bump when schema changes */
19
- export const SCHEMA_VERSION = 30;
19
+ export const SCHEMA_VERSION = 31;
20
20
  /** State database filename */
21
21
  export const STATE_DB_FILENAME = 'state.db';
22
22
  /** Directory for flywheel state */
@@ -440,6 +440,21 @@ CREATE TABLE IF NOT EXISTS retrieval_cooccurrence (
440
440
  );
441
441
  CREATE INDEX IF NOT EXISTS idx_retcooc_notes ON retrieval_cooccurrence(note_a, note_b);
442
442
  CREATE INDEX IF NOT EXISTS idx_retcooc_ts ON retrieval_cooccurrence(timestamp);
443
+
444
+ -- Deferred proactive linking queue (v31)
445
+ CREATE TABLE IF NOT EXISTS proactive_queue (
446
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
447
+ note_path TEXT NOT NULL,
448
+ entity TEXT NOT NULL,
449
+ score REAL NOT NULL,
450
+ confidence TEXT NOT NULL,
451
+ queued_at INTEGER NOT NULL,
452
+ expires_at INTEGER NOT NULL,
453
+ status TEXT NOT NULL DEFAULT 'pending',
454
+ applied_at INTEGER,
455
+ UNIQUE(note_path, entity)
456
+ );
457
+ CREATE INDEX IF NOT EXISTS idx_pq_status ON proactive_queue(status, expires_at);
443
458
  `;
444
459
  // =============================================================================
445
460
  // Database Initialization
@@ -583,6 +598,8 @@ function initSchema(db) {
583
598
  // (created by SCHEMA_SQL above via CREATE TABLE IF NOT EXISTS)
584
599
  // v29: index on wikilink_feedback(note_path) for temporal analysis queries
585
600
  // (created by SCHEMA_SQL above via CREATE INDEX IF NOT EXISTS)
601
+ // v31: proactive_queue table (deferred proactive linking)
602
+ // (created by SCHEMA_SQL above via CREATE TABLE IF NOT EXISTS)
586
603
  // v30: token economics columns on tool_invocations
587
604
  if (currentVersion < 30) {
588
605
  const hasResponseTokens = db.prepare(`SELECT COUNT(*) as cnt FROM pragma_table_info('tool_invocations') WHERE name = 'response_tokens'`).get();
package/dist/wikilinks.js CHANGED
@@ -571,11 +571,18 @@ export function resolveAliasWikilinks(content, entities, options = {}) {
571
571
  // Key: alias (lowercase if caseInsensitive)
572
572
  // Value: { entityName: canonical name, aliasText: original alias casing }
573
573
  const aliasMap = new Map();
574
+ // Track ambiguous aliases (shared by multiple entities) — skip these to avoid wrong resolution
575
+ const ambiguousAliases = new Set();
574
576
  for (const entity of entities) {
575
577
  if (typeof entity === 'string')
576
578
  continue;
577
579
  for (const alias of entity.aliases) {
578
580
  const key = caseInsensitive ? alias.toLowerCase() : alias;
581
+ const existing = aliasMap.get(key);
582
+ if (existing && existing.entityName !== entity.name) {
583
+ // Two different entities claim this alias — mark as ambiguous
584
+ ambiguousAliases.add(key);
585
+ }
579
586
  aliasMap.set(key, { entityName: entity.name, aliasText: alias });
580
587
  }
581
588
  // Also map the entity name itself so we can detect if target already points to entity
@@ -611,6 +618,10 @@ export function resolveAliasWikilinks(content, entities, options = {}) {
611
618
  // Target doesn't match any alias or entity name - leave unchanged
612
619
  continue;
613
620
  }
621
+ // Skip ambiguous aliases — multiple entities claim this alias, resolution would be arbitrary
622
+ if (ambiguousAliases.has(targetKey)) {
623
+ continue;
624
+ }
614
625
  // Check if already pointing to the entity name (no resolution needed)
615
626
  const entityNameKey = caseInsensitive ? aliasInfo.entityName.toLowerCase() : aliasInfo.entityName;
616
627
  if (targetKey === entityNameKey) {
@@ -1116,6 +1127,36 @@ export function detectImplicitEntities(content, config = {}) {
1116
1127
  }
1117
1128
  }
1118
1129
  }
1130
+ // Guard: max 4 words — longer phrases are almost always prose, not entity names
1131
+ const wordCount = text.split(/\s+/).length;
1132
+ if (wordCount > 4)
1133
+ continue;
1134
+ // Guard: max 40 chars
1135
+ if (text.length > 40)
1136
+ continue;
1137
+ // Guard: strip trailing punctuation from match text
1138
+ const stripped = text.replace(/[,.:;!?]+$/, '');
1139
+ if (stripped.length < minEntityLength)
1140
+ continue;
1141
+ if (stripped !== text) {
1142
+ end = start + stripped.length;
1143
+ text = stripped;
1144
+ }
1145
+ // Guard: sentence-start capitalization — if match begins at start of line
1146
+ // (after list marker or newline), first word cap is positional, not semantic.
1147
+ // Require at least 2 capitalized words remaining after the first.
1148
+ if (start > 0) {
1149
+ const lineStart = content.lastIndexOf('\n', start - 1) + 1;
1150
+ const before = content.substring(lineStart, start).trim();
1151
+ // After list marker (- * >) or empty (line start), first cap is positional
1152
+ if (before === '' || /^[-*>]+$/.test(before) || /^\d+\.$/.test(before)) {
1153
+ // Already trimmed sentence starters above; this catches the remaining
1154
+ // cases where the first word is capitalized only because of its position
1155
+ const wordsArr = text.split(/\s+/);
1156
+ if (wordsArr.length <= 2)
1157
+ continue; // Too few words to trust positional cap
1158
+ }
1159
+ }
1119
1160
  if (!shouldExclude(text) && !isProtected(start, end)) {
1120
1161
  detected.push({ text, start, end, pattern: 'proper-nouns' });
1121
1162
  seenTexts.add(text.toLowerCase());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@velvetmonkey/vault-core",
3
- "version": "2.0.134",
3
+ "version": "2.0.136",
4
4
  "description": "Shared vault utilities for Flywheel ecosystem (entity scanning, wikilinks, protected zones)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",