@voidwire/lore 1.6.0 → 1.6.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/cli.ts +31 -5
- package/index.ts +1 -0
- package/lib/contradiction.ts +10 -11
- package/lib/realtime.ts +26 -19
- package/package.json +1 -1
package/cli.ts
CHANGED
|
@@ -59,6 +59,7 @@ import {
|
|
|
59
59
|
type ObservationSubtype,
|
|
60
60
|
type ObservationConfidence,
|
|
61
61
|
type PurgeableSource,
|
|
62
|
+
type ContradictionDecision,
|
|
62
63
|
} from "./index";
|
|
63
64
|
import { isValidLoreType, LORE_TYPES } from "./lib/types";
|
|
64
65
|
import { runIndexer } from "./lib/indexer";
|
|
@@ -164,6 +165,26 @@ function fail(error: string, code: number = 1): never {
|
|
|
164
165
|
process.exit(code);
|
|
165
166
|
}
|
|
166
167
|
|
|
168
|
+
/**
|
|
169
|
+
* Log contradiction decisions to stderr (CLI context).
|
|
170
|
+
* Only logs non-ADD actions — ADD is the default/expected path.
|
|
171
|
+
*/
|
|
172
|
+
function logContradictionDecisions(decisions: ContradictionDecision[]): void {
|
|
173
|
+
for (const d of decisions) {
|
|
174
|
+
if (d.action === "NOOP") {
|
|
175
|
+
console.error(
|
|
176
|
+
`[contradiction] NOOP: skipped as redundant (topic: ${d.topic})`,
|
|
177
|
+
);
|
|
178
|
+
} else if (d.action === "DELETE+ADD") {
|
|
179
|
+
console.error(
|
|
180
|
+
`[contradiction] DELETE+ADD: replaced rowid ${d.deleteRowid} (topic: ${d.topic})`,
|
|
181
|
+
);
|
|
182
|
+
} else if (d.error) {
|
|
183
|
+
console.error(`[contradiction] ADD (fallback): ${d.error}`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
167
188
|
// ============================================================================
|
|
168
189
|
// Search Command
|
|
169
190
|
// ============================================================================
|
|
@@ -783,8 +804,9 @@ async function handleCaptureTask(args: string[]): Promise<void> {
|
|
|
783
804
|
|
|
784
805
|
if (result.success && result.event) {
|
|
785
806
|
try {
|
|
786
|
-
await indexAndEmbed([result.event]);
|
|
807
|
+
const decisions = await indexAndEmbed([result.event]);
|
|
787
808
|
output(result);
|
|
809
|
+
logContradictionDecisions(decisions);
|
|
788
810
|
console.error("✅ Task logged and indexed");
|
|
789
811
|
process.exit(0);
|
|
790
812
|
} catch (error) {
|
|
@@ -818,8 +840,9 @@ async function handleCaptureKnowledge(args: string[]): Promise<void> {
|
|
|
818
840
|
|
|
819
841
|
if (result.success && result.event) {
|
|
820
842
|
try {
|
|
821
|
-
await indexAndEmbed([result.event]);
|
|
843
|
+
const decisions = await indexAndEmbed([result.event]);
|
|
822
844
|
output(result);
|
|
845
|
+
logContradictionDecisions(decisions);
|
|
823
846
|
console.error("✅ Knowledge logged and indexed");
|
|
824
847
|
process.exit(0);
|
|
825
848
|
} catch (error) {
|
|
@@ -851,8 +874,9 @@ async function handleCaptureNote(args: string[]): Promise<void> {
|
|
|
851
874
|
|
|
852
875
|
if (result.success && result.event) {
|
|
853
876
|
try {
|
|
854
|
-
await indexAndEmbed([result.event]);
|
|
877
|
+
const decisions = await indexAndEmbed([result.event]);
|
|
855
878
|
output(result);
|
|
879
|
+
logContradictionDecisions(decisions);
|
|
856
880
|
console.error("✅ Note logged and indexed");
|
|
857
881
|
process.exit(0);
|
|
858
882
|
} catch (error) {
|
|
@@ -887,8 +911,9 @@ async function handleCaptureTeaching(args: string[]): Promise<void> {
|
|
|
887
911
|
|
|
888
912
|
if (result.success && result.event) {
|
|
889
913
|
try {
|
|
890
|
-
await indexAndEmbed([result.event]);
|
|
914
|
+
const decisions = await indexAndEmbed([result.event]);
|
|
891
915
|
output(result);
|
|
916
|
+
logContradictionDecisions(decisions);
|
|
892
917
|
console.error("✅ Teaching logged and indexed");
|
|
893
918
|
process.exit(0);
|
|
894
919
|
} catch (error) {
|
|
@@ -924,8 +949,9 @@ async function handleCaptureObservation(args: string[]): Promise<void> {
|
|
|
924
949
|
|
|
925
950
|
if (result.success && result.event) {
|
|
926
951
|
try {
|
|
927
|
-
await indexAndEmbed([result.event]);
|
|
952
|
+
const decisions = await indexAndEmbed([result.event]);
|
|
928
953
|
output(result);
|
|
954
|
+
logContradictionDecisions(decisions);
|
|
929
955
|
console.error("✅ Observation logged and indexed");
|
|
930
956
|
process.exit(0);
|
|
931
957
|
} catch (error) {
|
package/index.ts
CHANGED
package/lib/contradiction.ts
CHANGED
|
@@ -27,6 +27,15 @@ export interface ContradictionResult {
|
|
|
27
27
|
deleteRowid?: number;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
/** Decision record returned to callers for logging */
|
|
31
|
+
export interface ContradictionDecision {
|
|
32
|
+
action: ContradictionAction;
|
|
33
|
+
topic: string;
|
|
34
|
+
source: string;
|
|
35
|
+
deleteRowid?: number;
|
|
36
|
+
error?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
30
39
|
// ─── Constants ──────────────────────────────────────────────────────────────
|
|
31
40
|
|
|
32
41
|
const MLX_URL = "http://localhost:8080/v1/chat/completions";
|
|
@@ -133,9 +142,6 @@ Otherwise reply: ADD or NOOP`;
|
|
|
133
142
|
});
|
|
134
143
|
|
|
135
144
|
if (!resp.ok) {
|
|
136
|
-
console.error(
|
|
137
|
-
`[contradiction] MLX returned ${resp.status} — defaulting to ADD`,
|
|
138
|
-
);
|
|
139
145
|
return { action: "ADD" };
|
|
140
146
|
}
|
|
141
147
|
|
|
@@ -145,12 +151,8 @@ Otherwise reply: ADD or NOOP`;
|
|
|
145
151
|
|
|
146
152
|
const raw = json.choices?.[0]?.message?.content?.trim() || "";
|
|
147
153
|
return parseClassification(raw);
|
|
148
|
-
} catch
|
|
154
|
+
} catch {
|
|
149
155
|
// Timeout, network error, or model unavailable — fail open
|
|
150
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
151
|
-
console.error(
|
|
152
|
-
`[contradiction] classification failed (${message}) — defaulting to ADD`,
|
|
153
|
-
);
|
|
154
156
|
return { action: "ADD" };
|
|
155
157
|
}
|
|
156
158
|
}
|
|
@@ -183,9 +185,6 @@ function parseClassification(raw: string): ContradictionResult {
|
|
|
183
185
|
}
|
|
184
186
|
|
|
185
187
|
// Unparseable — default to ADD
|
|
186
|
-
console.error(
|
|
187
|
-
`[contradiction] unparseable response "${raw}" — defaulting to ADD`,
|
|
188
|
-
);
|
|
189
188
|
return { action: "ADD" };
|
|
190
189
|
}
|
|
191
190
|
|
package/lib/realtime.ts
CHANGED
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
isContradictionCheckable,
|
|
30
30
|
findCandidates,
|
|
31
31
|
classifyContradiction,
|
|
32
|
+
type ContradictionDecision,
|
|
32
33
|
} from "./contradiction.js";
|
|
33
34
|
|
|
34
35
|
/**
|
|
@@ -38,8 +39,10 @@ import {
|
|
|
38
39
|
* 2. Generate embeddings with cache (instant semantic search)
|
|
39
40
|
* 3. Insert into embeddings table
|
|
40
41
|
*/
|
|
41
|
-
export async function indexAndEmbed(
|
|
42
|
-
|
|
42
|
+
export async function indexAndEmbed(
|
|
43
|
+
events: CaptureEvent[],
|
|
44
|
+
): Promise<ContradictionDecision[]> {
|
|
45
|
+
if (events.length === 0) return [];
|
|
43
46
|
|
|
44
47
|
const dbPath = getDatabasePath();
|
|
45
48
|
if (!existsSync(dbPath)) {
|
|
@@ -63,8 +66,11 @@ export async function indexAndEmbed(events: CaptureEvent[]): Promise<void> {
|
|
|
63
66
|
// duplicates existing entries. NOOP skips the event, DELETE+ADD
|
|
64
67
|
// removes the old entry before inserting the new one.
|
|
65
68
|
const eventsToIndex: CaptureEvent[] = [];
|
|
69
|
+
const decisions: ContradictionDecision[] = [];
|
|
66
70
|
for (const event of events) {
|
|
67
71
|
const source = getSourceForEvent(event);
|
|
72
|
+
const data = event.data as Record<string, unknown>;
|
|
73
|
+
const topic = String(data.topic || "");
|
|
68
74
|
|
|
69
75
|
if (isContradictionCheckable(source)) {
|
|
70
76
|
try {
|
|
@@ -73,37 +79,34 @@ export async function indexAndEmbed(events: CaptureEvent[]): Promise<void> {
|
|
|
73
79
|
const result = await classifyContradiction(event, candidates);
|
|
74
80
|
|
|
75
81
|
if (result.action === "NOOP") {
|
|
76
|
-
|
|
77
|
-
const topic = String(data.topic || "");
|
|
78
|
-
console.error(
|
|
79
|
-
`[contradiction] NOOP: skipped as redundant (topic: ${topic})`,
|
|
80
|
-
);
|
|
82
|
+
decisions.push({ action: "NOOP", topic, source });
|
|
81
83
|
continue;
|
|
82
84
|
}
|
|
83
85
|
|
|
84
86
|
if (result.action === "DELETE+ADD" && result.deleteRowid) {
|
|
85
87
|
deleteSearchAndEmbedding(db, result.deleteRowid);
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
88
|
+
decisions.push({
|
|
89
|
+
action: "DELETE+ADD",
|
|
90
|
+
topic,
|
|
91
|
+
source,
|
|
92
|
+
deleteRowid: result.deleteRowid,
|
|
93
|
+
});
|
|
94
|
+
} else {
|
|
95
|
+
decisions.push({ action: "ADD", topic, source });
|
|
91
96
|
}
|
|
92
97
|
// ADD falls through to normal insert
|
|
93
98
|
}
|
|
94
99
|
} catch (err) {
|
|
95
100
|
// Fail open — if contradiction check fails, proceed with ADD
|
|
96
101
|
const message = err instanceof Error ? err.message : String(err);
|
|
97
|
-
|
|
98
|
-
`[contradiction] check failed (${message}) — proceeding with ADD`,
|
|
99
|
-
);
|
|
102
|
+
decisions.push({ action: "ADD", topic, source, error: message });
|
|
100
103
|
}
|
|
101
104
|
}
|
|
102
105
|
|
|
103
106
|
eventsToIndex.push(event);
|
|
104
107
|
}
|
|
105
108
|
|
|
106
|
-
if (eventsToIndex.length === 0) return;
|
|
109
|
+
if (eventsToIndex.length === 0) return decisions;
|
|
107
110
|
|
|
108
111
|
// 1. Insert into FTS5 and collect doc IDs
|
|
109
112
|
const docIds: number[] = [];
|
|
@@ -120,6 +123,8 @@ export async function indexAndEmbed(events: CaptureEvent[]): Promise<void> {
|
|
|
120
123
|
for (let i = 0; i < eventsToIndex.length; i++) {
|
|
121
124
|
insertEmbedding(db, docIds[i], embeddings[i], eventsToIndex[i]);
|
|
122
125
|
}
|
|
126
|
+
|
|
127
|
+
return decisions;
|
|
123
128
|
} finally {
|
|
124
129
|
db.close();
|
|
125
130
|
}
|
|
@@ -319,12 +324,14 @@ function insertEmbedding(
|
|
|
319
324
|
|
|
320
325
|
const embeddingBlob = serializeEmbedding(embedding);
|
|
321
326
|
|
|
327
|
+
const timestamp = event.timestamp || new Date().toISOString();
|
|
328
|
+
|
|
322
329
|
const stmt = db.prepare(`
|
|
323
|
-
INSERT INTO embeddings (doc_id, chunk_idx, source, topic, type, embedding)
|
|
324
|
-
VALUES (?, 0, ?, ?, ?, ?)
|
|
330
|
+
INSERT INTO embeddings (doc_id, chunk_idx, source, topic, type, timestamp, embedding)
|
|
331
|
+
VALUES (?, 0, ?, ?, ?, ?, ?)
|
|
325
332
|
`);
|
|
326
333
|
|
|
327
|
-
stmt.run(docId, source, topic, type, embeddingBlob);
|
|
334
|
+
stmt.run(docId, source, topic, type, timestamp, embeddingBlob);
|
|
328
335
|
}
|
|
329
336
|
|
|
330
337
|
/**
|