@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 +1 -1
- package/dist/sqlite.js +18 -1
- package/dist/wikilinks.js +41 -0
- package/package.json +1 -1
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 =
|
|
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 =
|
|
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