bikky 0.4.2 → 0.4.4
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 +64 -37
- package/dist/config.d.ts +15 -1
- package/dist/config.js +116 -20
- package/dist/daemon/capture-policy.d.ts +0 -1
- package/dist/daemon/capture-policy.js +0 -2
- package/dist/daemon/consolidation.d.ts +2 -1
- package/dist/daemon/consolidation.js +32 -15
- package/dist/daemon/entity-typing.js +10 -0
- package/dist/daemon/episode-summary.d.ts +4 -0
- package/dist/daemon/episode-summary.js +39 -8
- package/dist/daemon/extraction.d.ts +2 -2
- package/dist/daemon/extraction.js +65 -22
- package/dist/daemon/loop.js +8 -0
- package/dist/daemon/maintenance-state.d.ts +1 -1
- package/dist/daemon/maintenance-state.js +2 -0
- package/dist/daemon/qdrant.d.ts +32 -10
- package/dist/daemon/qdrant.js +199 -60
- package/dist/daemon/quality-rollups.d.ts +51 -0
- package/dist/daemon/quality-rollups.js +378 -0
- package/dist/daemon/relations.d.ts +3 -3
- package/dist/daemon/relations.js +28 -16
- package/dist/daemon/session-index.d.ts +5 -0
- package/dist/daemon/session-index.js +36 -9
- package/dist/daemon/session-summary.d.ts +3 -0
- package/dist/daemon/session-summary.js +48 -15
- package/dist/daemon/staleness.js +3 -3
- package/dist/daemon/transcript-sources.js +3 -2
- package/dist/daemon/watcher.js +2 -0
- package/dist/daemon/workstream-summary.d.ts +4 -0
- package/dist/daemon/workstream-summary.js +58 -16
- package/dist/install.d.ts +11 -0
- package/dist/install.js +38 -0
- package/dist/lifecycle.js +7 -5
- package/dist/llm/embedding/index.js +2 -1
- package/dist/llm/embedding/providers/openai.js +8 -2
- package/dist/llm/embedding/providers/portkey.js +9 -2
- package/dist/llm/inference/index.js +2 -1
- package/dist/llm/util.d.ts +12 -0
- package/dist/llm/util.js +18 -0
- package/dist/mcp/helpers.d.ts +8 -0
- package/dist/mcp/helpers.js +36 -3
- package/dist/mcp/taxonomy.d.ts +9 -13
- package/dist/mcp/taxonomy.js +59 -42
- package/dist/mcp/tools.js +351 -83
- package/dist/mcp/types.d.ts +35 -0
- package/dist/package-verifier.d.ts +19 -0
- package/dist/package-verifier.js +83 -0
- package/dist/prompts/brief.d.ts +2 -2
- package/dist/prompts/brief.js +0 -1
- package/dist/prompts/extraction.js +9 -11
- package/dist/provenance/origin.d.ts +57 -0
- package/dist/provenance/origin.js +254 -0
- package/dist/routing-context.d.ts +16 -0
- package/dist/routing-context.js +55 -0
- package/dist/status.d.ts +1 -0
- package/dist/status.js +7 -1
- package/docs/config/fully-hosted.md +33 -13
- package/docs/config/hosted-models.md +33 -13
- package/docs/config/hosted-qdrant-local-models.md +1 -0
- package/docs/config/local.md +1 -0
- package/docs/configuration.md +42 -17
- package/package.json +2 -2
|
@@ -13,6 +13,7 @@ import { mkdirSync, writeFileSync } from "node:fs";
|
|
|
13
13
|
import { join, dirname } from "node:path";
|
|
14
14
|
import { createHash } from "node:crypto";
|
|
15
15
|
import * as qdrant from "./qdrant.js";
|
|
16
|
+
import { buildOperationOrigin } from "../provenance/origin.js";
|
|
16
17
|
import { chatCompletion } from "../llm/index.js";
|
|
17
18
|
import { categoryValues, normalizeCategory, normalizeDomain } from "../mcp/taxonomy.js";
|
|
18
19
|
import { distillPrompt, DISTILL_PROMPT_DESCRIPTOR, contradictionPrompt, briefPrompt, BRIEF_PROMPT_DESCRIPTOR, safeParseJson, } from "../prompts/index.js";
|
|
@@ -33,6 +34,8 @@ const autoDistill = async (_config, { minSummaries = 5 } = {}) => {
|
|
|
33
34
|
if (!qdrant.isReady())
|
|
34
35
|
return { distilled: false };
|
|
35
36
|
try {
|
|
37
|
+
const destination = qdrant.resolveDestination({}).name;
|
|
38
|
+
const collection = qdrant.collectionForDestination(destination);
|
|
36
39
|
// Find undistilled session summaries (support both legacy and new taxonomy)
|
|
37
40
|
const legacyFilter = {
|
|
38
41
|
must: [
|
|
@@ -47,12 +50,12 @@ const autoDistill = async (_config, { minSummaries = 5 } = {}) => {
|
|
|
47
50
|
],
|
|
48
51
|
};
|
|
49
52
|
const [legacyRes, newRes] = await Promise.all([
|
|
50
|
-
qdrant.qdrantRequest("POST", `/collections/${
|
|
53
|
+
qdrant.qdrantRequest("POST", `/collections/${collection}/points/scroll`, {
|
|
51
54
|
filter: legacyFilter, limit: 50, with_payload: true,
|
|
52
|
-
}),
|
|
53
|
-
qdrant.qdrantRequest("POST", `/collections/${
|
|
55
|
+
}, destination),
|
|
56
|
+
qdrant.qdrantRequest("POST", `/collections/${collection}/points/scroll`, {
|
|
54
57
|
filter: newFilter, limit: 50, with_payload: true,
|
|
55
|
-
}),
|
|
58
|
+
}, destination),
|
|
56
59
|
]);
|
|
57
60
|
// Deduplicate by ID
|
|
58
61
|
const seen = new Set();
|
|
@@ -107,7 +110,6 @@ const autoDistill = async (_config, { minSummaries = 5 } = {}) => {
|
|
|
107
110
|
domain: normalizeDomain(pattern.domain ?? "software_engineering"),
|
|
108
111
|
kind: "distilled",
|
|
109
112
|
entities: Array.isArray(pattern.entities) ? pattern.entities.map(e => String(e).toLowerCase()) : [],
|
|
110
|
-
source: "system",
|
|
111
113
|
confidence: 0.85,
|
|
112
114
|
importance: pattern.importance || 0.7,
|
|
113
115
|
content_hash: hash,
|
|
@@ -116,11 +118,25 @@ const autoDistill = async (_config, { minSummaries = 5 } = {}) => {
|
|
|
116
118
|
distilled_at: new Date().toISOString(),
|
|
117
119
|
distilled_by_prompt: promptStamp,
|
|
118
120
|
},
|
|
119
|
-
|
|
121
|
+
origin: buildOperationOrigin({
|
|
122
|
+
interface: "daemon",
|
|
123
|
+
action: "create",
|
|
124
|
+
subsystem: "consolidation",
|
|
125
|
+
metadata: {
|
|
126
|
+
distilled_from_count: batch.length,
|
|
127
|
+
prompt: promptStamp,
|
|
128
|
+
},
|
|
129
|
+
}),
|
|
130
|
+
}, { destination });
|
|
120
131
|
}
|
|
121
132
|
// Supersede the source summaries
|
|
122
133
|
for (const pt of batch) {
|
|
123
|
-
await qdrant.supersedeFact(pt.id, `distilled:${new Date().toISOString()}
|
|
134
|
+
await qdrant.supersedeFact(pt.id, `distilled:${new Date().toISOString()}`, destination, buildOperationOrigin({
|
|
135
|
+
interface: "daemon",
|
|
136
|
+
action: "supersede",
|
|
137
|
+
subsystem: "consolidation",
|
|
138
|
+
metadata: { prompt: promptStamp },
|
|
139
|
+
}));
|
|
124
140
|
}
|
|
125
141
|
logFn("INFO", `Auto-distill: consolidated ${batch.length} summaries into ${patterns.length} patterns`);
|
|
126
142
|
return { distilled: true, count: patterns.length };
|
|
@@ -143,12 +159,13 @@ const detectContradiction = async (fact, _config, telemetry) => {
|
|
|
143
159
|
try {
|
|
144
160
|
const vector = await qdrant.embed(fact.content);
|
|
145
161
|
// Search across all categories because contradictions can cross category lines.
|
|
146
|
-
const
|
|
162
|
+
const collection = qdrant.collectionForDestination(telemetry?.destination);
|
|
163
|
+
const results = await qdrant.qdrantRequest("POST", `/collections/${collection}/points/search`, {
|
|
147
164
|
vector,
|
|
148
165
|
filter: { must: [{ is_null: { key: "superseded_by" } }] },
|
|
149
166
|
limit: 5,
|
|
150
167
|
with_payload: true,
|
|
151
|
-
});
|
|
168
|
+
}, telemetry?.destination);
|
|
152
169
|
const candidates = (results.result || [])
|
|
153
170
|
.filter(r => r.score >= 0.75 && r.score < 0.92);
|
|
154
171
|
if (candidates.length === 0)
|
|
@@ -343,7 +360,6 @@ const formatHealthReport = (report) => {
|
|
|
343
360
|
const CATEGORY_TO_HEADING = {
|
|
344
361
|
engineering: "Engineering",
|
|
345
362
|
product: "Product",
|
|
346
|
-
human: "Human",
|
|
347
363
|
system: "System",
|
|
348
364
|
// Legacy stored categories remain readable before any data migration.
|
|
349
365
|
codebase: "Engineering",
|
|
@@ -354,9 +370,10 @@ const CATEGORY_TO_HEADING = {
|
|
|
354
370
|
projects: "System",
|
|
355
371
|
observation: "Engineering",
|
|
356
372
|
observations: "Engineering",
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
373
|
+
human: "Engineering",
|
|
374
|
+
preferences: "Engineering",
|
|
375
|
+
people: "Engineering",
|
|
376
|
+
team: "Engineering",
|
|
360
377
|
};
|
|
361
378
|
const generateMemoryBrief = async (_config) => {
|
|
362
379
|
if (!qdrant.isReady())
|
|
@@ -448,8 +465,8 @@ const tick = async (config, opts = {}) => {
|
|
|
448
465
|
}
|
|
449
466
|
};
|
|
450
467
|
/** Reset state (for testing). */
|
|
451
|
-
const _reset = () => {
|
|
452
|
-
consolidationTickCount =
|
|
468
|
+
const _reset = (tickCount = 0) => {
|
|
469
|
+
consolidationTickCount = tickCount;
|
|
453
470
|
};
|
|
454
471
|
export { detectContradiction, tick, setLogger, _reset, };
|
|
455
472
|
//# sourceMappingURL=consolidation.js.map
|
|
@@ -11,6 +11,7 @@ import { entityTypingPrompt, ENTITY_TYPING_PROMPT_DESCRIPTOR, safeParseJson } fr
|
|
|
11
11
|
import * as qdrant from "./qdrant.js";
|
|
12
12
|
import { isAttemptBackedOff, pruneRecentAttempts, readMaintenanceState, recordMaintenanceRun, shouldRunMaintenance, } from "./maintenance-state.js";
|
|
13
13
|
import { isGenericEntity } from "./relations-vocab.js";
|
|
14
|
+
import { buildOperationOrigin } from "../provenance/origin.js";
|
|
14
15
|
const FACTS_SCAN_LIMIT = 200;
|
|
15
16
|
const FACTS_PER_ENTITY = 5;
|
|
16
17
|
const DEFAULT_LOOKBACK_MS = 7 * 24 * 60 * 60 * 1000;
|
|
@@ -171,6 +172,15 @@ const upsertEntityTypePoint = async (candidate, classification, source) => {
|
|
|
171
172
|
classified_at: now,
|
|
172
173
|
updated_at: now,
|
|
173
174
|
created_at: now,
|
|
175
|
+
origin: buildOperationOrigin({
|
|
176
|
+
interface: "daemon",
|
|
177
|
+
action: "create",
|
|
178
|
+
subsystem: "entity_typing",
|
|
179
|
+
metadata: {
|
|
180
|
+
entity_name: candidate.name,
|
|
181
|
+
classification_source: source,
|
|
182
|
+
},
|
|
183
|
+
}),
|
|
174
184
|
source_fact_ids: candidate.factIds,
|
|
175
185
|
...(candidate.workstreamKeys.length > 0 ? { workstream_key: candidate.workstreamKeys[0] } : {}),
|
|
176
186
|
metadata: {
|
|
@@ -2,6 +2,7 @@ import type { BikkyConfig } from "../config.js";
|
|
|
2
2
|
import type { QdrantPayload } from "./qdrant.js";
|
|
3
3
|
import { type RedactionSummary } from "../privacy/redaction.js";
|
|
4
4
|
import { type WorkstreamRegistry } from "./workstream-resolver.js";
|
|
5
|
+
import { type OperationOrigin } from "../provenance/origin.js";
|
|
5
6
|
export { buildEpisodeSummaryMessages } from "../prompts/index.js";
|
|
6
7
|
export interface WorkspaceScope {
|
|
7
8
|
workspaceId?: string;
|
|
@@ -33,6 +34,7 @@ export interface EpisodeSummaryWriteResult {
|
|
|
33
34
|
action: "stored" | "updated" | "skipped";
|
|
34
35
|
factId?: string;
|
|
35
36
|
episodeId?: string;
|
|
37
|
+
destination?: string;
|
|
36
38
|
workstreamKey?: string | null;
|
|
37
39
|
reason?: string;
|
|
38
40
|
}
|
|
@@ -57,6 +59,8 @@ export declare const buildEpisodeSummaryPayload: (input: {
|
|
|
57
59
|
enabled: boolean;
|
|
58
60
|
redactPii: boolean;
|
|
59
61
|
};
|
|
62
|
+
config?: BikkyConfig;
|
|
63
|
+
origin?: OperationOrigin;
|
|
60
64
|
}) => EpisodeSummaryPayloadResult;
|
|
61
65
|
export declare const updateEpisodeSummary: (input: {
|
|
62
66
|
segment: EpisodeSegment;
|
|
@@ -13,6 +13,7 @@ import { CAPTURE_POLICY_VERSION, CAPTURE_TRIGGERS, DEFAULT_CAPTURE_CONTEXT, PROM
|
|
|
13
13
|
import * as qdrant from "./qdrant.js";
|
|
14
14
|
import { combineRedactions, redactStorageText, } from "../privacy/redaction.js";
|
|
15
15
|
import { resolveWorkstreamKey, } from "./workstream-resolver.js";
|
|
16
|
+
import { buildOperationOrigin } from "../provenance/origin.js";
|
|
16
17
|
export { buildEpisodeSummaryMessages } from "../prompts/index.js";
|
|
17
18
|
const DEFAULT_EPISODE_IMPORTANCE = 0.75;
|
|
18
19
|
const contentHash = (text) => createHash("sha256").update(`episode:${text}`).digest("hex");
|
|
@@ -120,6 +121,17 @@ export const buildEpisodeSummaryPayload = (input) => {
|
|
|
120
121
|
]);
|
|
121
122
|
const existingPayload = input.existing?.payload ?? {};
|
|
122
123
|
const workstreamKey = input.draft.workstream_key ?? input.segment.workstream_key ?? null;
|
|
124
|
+
const operationOrigin = input.origin ?? buildOperationOrigin({
|
|
125
|
+
interface: "daemon",
|
|
126
|
+
action: input.existing ? "update" : "create",
|
|
127
|
+
subsystem: "episode_summary",
|
|
128
|
+
config: input.config,
|
|
129
|
+
metadata: {
|
|
130
|
+
session_id: input.sessionId,
|
|
131
|
+
episode_id: input.segment.episode_id,
|
|
132
|
+
event_count: input.segment.event_count,
|
|
133
|
+
},
|
|
134
|
+
});
|
|
123
135
|
const payload = {
|
|
124
136
|
...existingPayload,
|
|
125
137
|
content: redactedContent.text,
|
|
@@ -129,9 +141,9 @@ export const buildEpisodeSummaryPayload = (input) => {
|
|
|
129
141
|
memory_subtype: "episode",
|
|
130
142
|
layer: "episode",
|
|
131
143
|
...(input.scope.workspaceId ? { workspace_id: input.scope.workspaceId } : {}),
|
|
132
|
-
|
|
144
|
+
origin: existingPayload.origin ?? operationOrigin,
|
|
145
|
+
...(input.existing ? { last_operation_origin: operationOrigin } : {}),
|
|
133
146
|
entities: redactedEntities.map((entity) => entity.text.toLowerCase()),
|
|
134
|
-
source: "system",
|
|
135
147
|
confidence: 1.0,
|
|
136
148
|
importance: input.draft.importance,
|
|
137
149
|
content_hash: contentHash(redactedContent.text),
|
|
@@ -184,19 +196,36 @@ const summarizeEpisodeTranscript = async (input) => {
|
|
|
184
196
|
throw new Error("Episode summary LLM returned null");
|
|
185
197
|
return parseEpisodeSummaryDraft(result);
|
|
186
198
|
};
|
|
187
|
-
const findExistingEpisodeSummary = async (episodeId, scope) => {
|
|
188
|
-
const
|
|
199
|
+
const findExistingEpisodeSummary = async (episodeId, scope, destination) => {
|
|
200
|
+
const collection = qdrant.collectionForDestination(destination);
|
|
201
|
+
const result = await qdrant.qdrantRequest("POST", `/collections/${collection}/points/scroll`, {
|
|
189
202
|
filter: buildEpisodeSummaryFilter(episodeId, scope),
|
|
190
203
|
limit: 1,
|
|
191
204
|
with_payload: true,
|
|
192
|
-
});
|
|
205
|
+
}, destination);
|
|
193
206
|
return result.result?.points?.[0] ?? null;
|
|
194
207
|
};
|
|
195
208
|
export const updateEpisodeSummary = async (input) => {
|
|
196
209
|
if (!input.segment.transcript.trim()) {
|
|
197
210
|
return { action: "skipped", reason: "empty_transcript", episodeId: input.segment.episode_id };
|
|
198
211
|
}
|
|
199
|
-
const
|
|
212
|
+
const destination = qdrant.resolveDestination({
|
|
213
|
+
content: input.segment.transcript,
|
|
214
|
+
entities: [
|
|
215
|
+
input.sessionId,
|
|
216
|
+
...(input.segment.workstream_key ? [input.segment.workstream_key] : []),
|
|
217
|
+
],
|
|
218
|
+
metadata: {
|
|
219
|
+
session_id: input.sessionId,
|
|
220
|
+
episode_id: input.segment.episode_id,
|
|
221
|
+
...(input.segment.workstream_key ? { workstream_key: input.segment.workstream_key } : {}),
|
|
222
|
+
memory_subtype: "episode",
|
|
223
|
+
kind: "summary",
|
|
224
|
+
origin_interface: "daemon",
|
|
225
|
+
origin_agent_type: "daemon",
|
|
226
|
+
},
|
|
227
|
+
});
|
|
228
|
+
const existing = await findExistingEpisodeSummary(input.segment.episode_id, input.scope, destination.name);
|
|
200
229
|
const draft = await summarizeEpisodeTranscript({
|
|
201
230
|
transcript: input.segment.transcript,
|
|
202
231
|
sessionId: input.sessionId,
|
|
@@ -222,6 +251,7 @@ export const updateEpisodeSummary = async (input) => {
|
|
|
222
251
|
scope: input.scope,
|
|
223
252
|
now,
|
|
224
253
|
existing,
|
|
254
|
+
config: input.config,
|
|
225
255
|
redactionOptions: {
|
|
226
256
|
enabled: true,
|
|
227
257
|
redactPii: false,
|
|
@@ -229,13 +259,14 @@ export const updateEpisodeSummary = async (input) => {
|
|
|
229
259
|
});
|
|
230
260
|
const vector = await qdrant.embed(String(payload.content));
|
|
231
261
|
const factId = existing?.id ?? randomUUID();
|
|
232
|
-
await qdrant.qdrantRequest("PUT", `/collections/${
|
|
262
|
+
await qdrant.qdrantRequest("PUT", `/collections/${destination.collection}/points`, {
|
|
233
263
|
points: [{ id: factId, vector, payload }],
|
|
234
|
-
});
|
|
264
|
+
}, destination.name);
|
|
235
265
|
return {
|
|
236
266
|
action: existing ? "updated" : "stored",
|
|
237
267
|
factId,
|
|
238
268
|
episodeId: input.segment.episode_id,
|
|
269
|
+
destination: destination.name,
|
|
239
270
|
workstreamKey: resolved.key,
|
|
240
271
|
};
|
|
241
272
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Events-based memory extraction — reads supported coding-agent transcripts,
|
|
3
|
-
* extracts facts via LLM, and stores them in Qdrant with
|
|
3
|
+
* extracts facts via LLM, and stores them in Qdrant with daemon origin metadata.
|
|
4
4
|
*
|
|
5
5
|
* Uses a JSON file for extraction state (high-water byte offsets) instead of SQLite.
|
|
6
6
|
* Copilot session detection uses lock files. Claude Code detection uses
|
|
@@ -10,7 +10,7 @@ import type { BikkyConfig } from "../config.js";
|
|
|
10
10
|
import type { LogFn } from "./qdrant.js";
|
|
11
11
|
import { type TranscriptSource } from "./transcript-sources.js";
|
|
12
12
|
export declare const setLogger: (fn: LogFn) => void;
|
|
13
|
-
export declare const DEFAULT_EXTRACTION_PROMPT = "You are Bikky's memory extraction agent for open-source coding agents. Extract durable, reusable facts that help a future agent continue work without rereading the whole transcript.\n\n## Core rule\nExtract fewer, sharper memories. A candidate fact must be independently useful after the session is gone.\n\n## Quality gate\nEvery fact must pass at least one gate:\n1. GREPPABLE: names a file path, package, symbol, config key, CLI flag, issue/PR, service, or API a future agent can search for.\n2. RUNNABLE: contains a command, URL, setting, port, or procedure that can be executed or checked.\n3. NAVIGABLE: tells a future agent where to look and what that location means.\n4. DECISIVE: records a durable decision, rationale, constraint, convention, or preference.\n5. DIAGNOSTIC: captures a repeatable failure mode, root cause, or troubleshooting gotcha.\n\n## Ontology\n- domain is the activity profile. For coding-agent captures use \"software_engineering\".\n- category is subject matter: engineering | product |
|
|
13
|
+
export declare const DEFAULT_EXTRACTION_PROMPT = "You are Bikky's memory extraction agent for open-source coding agents. Extract durable, reusable facts that help a future agent continue work without rereading the whole transcript.\n\n## Core rule\nExtract fewer, sharper memories. A candidate fact must be independently useful after the session is gone.\n\n## Quality gate\nEvery fact must pass at least one gate:\n1. GREPPABLE: names a file path, package, symbol, config key, CLI flag, issue/PR, service, or API a future agent can search for.\n2. RUNNABLE: contains a command, URL, setting, port, or procedure that can be executed or checked.\n3. NAVIGABLE: tells a future agent where to look and what that location means.\n4. DECISIVE: records a durable decision, rationale, constraint, convention, or preference.\n5. DIAGNOSTIC: captures a repeatable failure mode, root cause, or troubleshooting gotcha.\n\n## Ontology\n- domain is the activity profile. For coding-agent captures use \"software_engineering\".\n- category is subject matter: engineering | product | system.\n- kind is object shape. For this prompt, emit only kind=\"fact\".\n- memory_subtype must be one of:\n codebase_map | architecture_decision | infra_topology | access_pattern | operational_procedure | domain_rule | product_decision | product_requirement | user_workflow | roadmap_item | success_metric | market_insight | troubleshooting_gotcha | preference | person_profile | ownership_note | working_agreement | activity_event.\n\n## Examples\nGOOD:\n- \"The UI smoke tests live in packages/ui/tests/smoke.spec.ts and run through npm run test:e2e with mocked /api/memory/* responses.\"\n- \"Use workspace_id as the tenancy/access boundary; domain is reserved for activity profile such as software_engineering.\"\n- \"If Qdrant order_by fails with a missing index error, create a datetime payload index for the sorted field before retrying.\"\n- \"The memory page should show categories and concrete subtype chips directly; a sub-tab layer makes the ontology harder to understand.\"\n- \"Saber prefers Node's built-in test runner for root tests; do not add Jest just for daemon unit tests.\"\n- \"Saber merged PR #85 after approving the subtype UX copy changes.\"\n\nBAD:\n- \"The tests were fixed.\" (status only)\n- \"We reviewed the code.\" (session narration)\n- \"The deployment succeeded.\" (transient and not reusable)\n- \"The agent used npm.\" (tool narration)\n- \"There was an error.\" (no root cause or reusable detail)\n\n## Output format\nReturn strict JSON:\n{\"facts\":[\n {\n \"content\":\"One self-contained durable fact.\",\n \"category\":\"engineering\",\n \"memory_subtype\":\"codebase_map\",\n \"action_actor\":\"optional actor for activity_event only\",\n \"action_type\":\"optional action verb for activity_event only\",\n \"action_object\":\"optional durable object for activity_event only\",\n \"action_outcome\":\"optional durable outcome for activity_event only\",\n \"entities\":[\"repo-or-tool\",\"specific-module\"],\n \"confidence\":0.9,\n \"importance\":0.7,\n \"quality_score\":0.8,\n \"confidence_reason\":\"Explicitly stated in the transcript.\",\n \"repo\":\"optional/repo-or-package\",\n \"branch\":\"optional-branch\",\n \"task_key\":\"optional issue/PR/task key\",\n \"workstream_key\":\"optional stable workstream key\"\n }\n]}\n\nScoring:\n- confidence: 0.9 explicit, 0.7 strong inference, 0.55 weak but useful inference.\n- importance: 0.8+ for decisions, infra, procedures, access, recurring failures, product requirements, ownership, and state-changing activity events; 0.6+ for useful codebase maps/preferences.\n- quality_score: 0.8+ passes multiple gates, 0.6+ passes one strong gate, below 0.6 should usually be omitted.\n\nIf nothing passes the quality gate, return {\"facts\":[]}.";
|
|
14
14
|
export type Volatility = "stable" | "evolving" | "transient" | "ephemeral";
|
|
15
15
|
export interface ExtractedFact {
|
|
16
16
|
content: string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Events-based memory extraction — reads supported coding-agent transcripts,
|
|
3
|
-
* extracts facts via LLM, and stores them in Qdrant with
|
|
3
|
+
* extracts facts via LLM, and stores them in Qdrant with daemon origin metadata.
|
|
4
4
|
*
|
|
5
5
|
* Uses a JSON file for extraction state (high-water byte offsets) instead of SQLite.
|
|
6
6
|
* Copilot session detection uses lock files. Claude Code detection uses
|
|
@@ -19,7 +19,7 @@ import { CAPTURE_POLICY_VERSION, CAPTURE_TRIGGERS, DEFAULT_CAPTURE_CONTEXT, QUAL
|
|
|
19
19
|
import { shouldSummarizeEvents, updateSessionSummary } from "./session-summary.js";
|
|
20
20
|
import { redactStorageText } from "../privacy/redaction.js";
|
|
21
21
|
import { compareSubtype, hasTypedToken, verifyGrounding, verifyVolatilityCoherence } from "./extraction-rules.js";
|
|
22
|
-
import {
|
|
22
|
+
import { buildOperationOrigin } from "../provenance/origin.js";
|
|
23
23
|
import { discoverClaudeTranscriptMappings, discoverCopilotTranscriptMappings, extractionStateKey, readNewTranscriptEvents, transcriptLabel, } from "./transcript-sources.js";
|
|
24
24
|
// ── Module state ─────────────────────────────────────────────────────────────
|
|
25
25
|
let logFn = (() => { });
|
|
@@ -43,7 +43,7 @@ Every fact must pass at least one gate:
|
|
|
43
43
|
|
|
44
44
|
## Ontology
|
|
45
45
|
- domain is the activity profile. For coding-agent captures use "software_engineering".
|
|
46
|
-
- category is subject matter: engineering | product |
|
|
46
|
+
- category is subject matter: engineering | product | system.
|
|
47
47
|
- kind is object shape. For this prompt, emit only kind="fact".
|
|
48
48
|
- memory_subtype must be one of:
|
|
49
49
|
codebase_map | architecture_decision | infra_topology | access_pattern | operational_procedure | domain_rule | product_decision | product_requirement | user_workflow | roadmap_item | success_metric | market_insight | troubleshooting_gotcha | preference | person_profile | ownership_note | working_agreement | activity_event.
|
|
@@ -210,8 +210,8 @@ export const factQualitySignals = (fact) => {
|
|
|
210
210
|
const isPreferenceLike = subtype === "preference" || subtype === "domain_rule" || subtype === "working_agreement";
|
|
211
211
|
const isDecisionLike = subtype === "architecture_decision" || subtype === "product_decision" || subtype === "troubleshooting_gotcha";
|
|
212
212
|
const isProductLike = subtype === "product_requirement" || subtype === "user_workflow" || subtype === "roadmap_item" || subtype === "success_metric" || subtype === "market_insight";
|
|
213
|
-
const
|
|
214
|
-
const shortUseful = wordCount >= 7 && wordCount <= 22 && (isPreferenceLike || isDecisionLike || isProductLike ||
|
|
213
|
+
const isCollaborationLike = subtype === "person_profile" || subtype === "ownership_note" || subtype === "activity_event";
|
|
214
|
+
const shortUseful = wordCount >= 7 && wordCount <= 22 && (isPreferenceLike || isDecisionLike || isProductLike || isCollaborationLike) && (entities.length > 0 || durableAnchor);
|
|
215
215
|
let score = 0.25;
|
|
216
216
|
if (wordCount >= 8)
|
|
217
217
|
score += 0.1;
|
|
@@ -219,7 +219,7 @@ export const factQualitySignals = (fact) => {
|
|
|
219
219
|
score += 0.1;
|
|
220
220
|
if (durableAnchor)
|
|
221
221
|
score += 0.25;
|
|
222
|
-
if (isPreferenceLike || isDecisionLike || isProductLike ||
|
|
222
|
+
if (isPreferenceLike || isDecisionLike || isProductLike || isCollaborationLike)
|
|
223
223
|
score += 0.15;
|
|
224
224
|
if ((fact.confidence ?? 0) >= 0.75)
|
|
225
225
|
score += 0.1;
|
|
@@ -254,8 +254,16 @@ const subtypeForRawCategoryHint = (rawCategory, category) => {
|
|
|
254
254
|
return "operational_procedure";
|
|
255
255
|
if (hint.includes("decision"))
|
|
256
256
|
return "architecture_decision";
|
|
257
|
-
if (hint.includes("
|
|
257
|
+
if (hint.includes("preference"))
|
|
258
258
|
return "preference";
|
|
259
|
+
if (hint.includes("owner"))
|
|
260
|
+
return "ownership_note";
|
|
261
|
+
if (hint.includes("agreement"))
|
|
262
|
+
return "working_agreement";
|
|
263
|
+
if (hint.includes("activity") || hint.includes("actor"))
|
|
264
|
+
return "activity_event";
|
|
265
|
+
if (hint.includes("people") || hint.includes("person") || hint.includes("team"))
|
|
266
|
+
return "person_profile";
|
|
259
267
|
if (hint.includes("product") || hint.includes("domain"))
|
|
260
268
|
return "domain_rule";
|
|
261
269
|
return subtypeForCategory(normalizeCategory(category));
|
|
@@ -415,11 +423,6 @@ const storeFacts = async (facts, sessionId, config, source) => {
|
|
|
415
423
|
};
|
|
416
424
|
if (source)
|
|
417
425
|
baseMeta.extraction_source = source;
|
|
418
|
-
const actor = resolveActorIdentity({ config });
|
|
419
|
-
if (actor.actor_label)
|
|
420
|
-
baseMeta.actor_label = actor.actor_label;
|
|
421
|
-
if (actor.source)
|
|
422
|
-
baseMeta.actor_source = actor.source;
|
|
423
426
|
let stored = 0;
|
|
424
427
|
for (const fact of facts) {
|
|
425
428
|
const redactedContent = redactStorageText(fact.content);
|
|
@@ -429,12 +432,34 @@ const storeFacts = async (facts, sessionId, config, source) => {
|
|
|
429
432
|
entities: fact.entities.map((entity) => entity.toLowerCase()),
|
|
430
433
|
};
|
|
431
434
|
const hash = contentHash(sanitizedFact.content);
|
|
435
|
+
const routeInput = {
|
|
436
|
+
content: sanitizedFact.content,
|
|
437
|
+
entities: sanitizedFact.entities,
|
|
438
|
+
metadata: {
|
|
439
|
+
...baseMeta,
|
|
440
|
+
...(fact.repo ? { repo: fact.repo } : {}),
|
|
441
|
+
...(fact.branch ? { branch: fact.branch } : {}),
|
|
442
|
+
...(fact.task_key ? { task_key: fact.task_key } : {}),
|
|
443
|
+
...(fact.workstream_key ? { workstream_key: fact.workstream_key } : {}),
|
|
444
|
+
category: fact.category,
|
|
445
|
+
},
|
|
446
|
+
};
|
|
432
447
|
try {
|
|
433
|
-
const dedup = await qdrant.dedupCheck(sanitizedFact.content, hash);
|
|
448
|
+
const dedup = await qdrant.dedupCheck(sanitizedFact.content, hash, undefined, undefined, routeInput);
|
|
434
449
|
if (dedup.action === "skip") {
|
|
435
450
|
// Reinforce existing fact
|
|
436
451
|
if (dedup.existingId) {
|
|
437
|
-
await qdrant.reinforceFact(dedup.existingId, dedup.existingCount || 1
|
|
452
|
+
await qdrant.reinforceFact(dedup.existingId, dedup.existingCount || 1, dedup.destination, buildOperationOrigin({
|
|
453
|
+
interface: "daemon",
|
|
454
|
+
action: "reinforce",
|
|
455
|
+
subsystem: "extraction",
|
|
456
|
+
config,
|
|
457
|
+
metadata: {
|
|
458
|
+
session_id: sessionId,
|
|
459
|
+
...(source ? { transcript_source: source } : {}),
|
|
460
|
+
dedup_action: dedup.action,
|
|
461
|
+
},
|
|
462
|
+
}));
|
|
438
463
|
}
|
|
439
464
|
continue;
|
|
440
465
|
}
|
|
@@ -444,6 +469,7 @@ const storeFacts = async (facts, sessionId, config, source) => {
|
|
|
444
469
|
const contradiction = await detectContradiction(sanitizedFact, config, {
|
|
445
470
|
sessionId,
|
|
446
471
|
workstreamKey: sanitizedFact.workstream_key ?? undefined,
|
|
472
|
+
destination: dedup.destination,
|
|
447
473
|
});
|
|
448
474
|
if (contradiction.contradiction && contradiction.existingId) {
|
|
449
475
|
logFn("INFO", `Extraction: contradiction detected for "${fact.content.slice(0, 60)}..." vs ${contradiction.existingId}: ${contradiction.reason}`);
|
|
@@ -533,7 +559,7 @@ const storeFacts = async (facts, sessionId, config, source) => {
|
|
|
533
559
|
// Downgrade to candidate with reduced confidence rather than dropping
|
|
534
560
|
// outright: similarity is a soft signal, not a hard reject.
|
|
535
561
|
try {
|
|
536
|
-
const badMatch = await qdrant.badExemplarCheck(sanitizedFact.content);
|
|
562
|
+
const badMatch = await qdrant.badExemplarCheck(sanitizedFact.content, undefined, routeInput);
|
|
537
563
|
if (badMatch && badMatch.score >= 0.85) {
|
|
538
564
|
effectiveConfidence = clamp01(effectiveConfidence - 0.2);
|
|
539
565
|
reviewStatus = "candidate";
|
|
@@ -553,7 +579,6 @@ const storeFacts = async (facts, sessionId, config, source) => {
|
|
|
553
579
|
domain: DEFAULT_CAPTURE_CONTEXT.domain,
|
|
554
580
|
memory_subtype: effectiveSubtype,
|
|
555
581
|
entities: sanitizedFact.entities,
|
|
556
|
-
source: "system",
|
|
557
582
|
kind: "fact",
|
|
558
583
|
confidence: effectiveConfidence,
|
|
559
584
|
importance: fact.importance,
|
|
@@ -571,17 +596,35 @@ const storeFacts = async (facts, sessionId, config, source) => {
|
|
|
571
596
|
task_key: fact.task_key,
|
|
572
597
|
workstream_key: fact.workstream_key,
|
|
573
598
|
metadata: factMeta,
|
|
599
|
+
origin: buildOperationOrigin({
|
|
600
|
+
interface: "daemon",
|
|
601
|
+
action: "create",
|
|
602
|
+
subsystem: "extraction",
|
|
603
|
+
config,
|
|
604
|
+
metadata: {
|
|
605
|
+
session_id: sessionId,
|
|
606
|
+
...(source ? { transcript_source: source } : {}),
|
|
607
|
+
capture_policy_version: CAPTURE_POLICY_VERSION,
|
|
608
|
+
},
|
|
609
|
+
}),
|
|
574
610
|
};
|
|
575
|
-
if (actor.actor_id) {
|
|
576
|
-
storePayload.actor_id = actor.actor_id;
|
|
577
|
-
}
|
|
578
611
|
if (dedup.action === "supersede" && dedup.existingId) {
|
|
579
|
-
const newId = await qdrant.storeFact(storePayload);
|
|
580
|
-
await qdrant.supersedeFact(dedup.existingId, newId
|
|
612
|
+
const newId = await qdrant.storeFact(storePayload, routeInput);
|
|
613
|
+
await qdrant.supersedeFact(dedup.existingId, newId, dedup.destination, buildOperationOrigin({
|
|
614
|
+
interface: "daemon",
|
|
615
|
+
action: "supersede",
|
|
616
|
+
subsystem: "extraction",
|
|
617
|
+
config,
|
|
618
|
+
metadata: {
|
|
619
|
+
session_id: sessionId,
|
|
620
|
+
new_fact_id: newId,
|
|
621
|
+
...(source ? { transcript_source: source } : {}),
|
|
622
|
+
},
|
|
623
|
+
}));
|
|
581
624
|
stored++;
|
|
582
625
|
}
|
|
583
626
|
else {
|
|
584
|
-
await qdrant.storeFact(storePayload);
|
|
627
|
+
await qdrant.storeFact(storePayload, routeInput);
|
|
585
628
|
stored++;
|
|
586
629
|
}
|
|
587
630
|
}
|
package/dist/daemon/loop.js
CHANGED
|
@@ -11,6 +11,7 @@ import { tick as extractionTick, setLogger as setExtractionLogger } from "./extr
|
|
|
11
11
|
import { tick as consolidationTick, setLogger as setConsolidationLogger } from "./consolidation.js";
|
|
12
12
|
import { tick as relationsTick, setLogger as setRelationsLogger } from "./relations.js";
|
|
13
13
|
import { tick as entityTypingTick, setLogger as setEntityTypingLogger } from "./entity-typing.js";
|
|
14
|
+
import { tick as qualityRollupsTick, setLogger as setQualityRollupsLogger } from "./quality-rollups.js";
|
|
14
15
|
import { scanStaleFacts, setLogger as setStalenessLogger } from "./staleness.js";
|
|
15
16
|
import { inspectWatcherPaths, formatIssue } from "./watcher-health.js";
|
|
16
17
|
// createLogger returns (LogLevel, ...args) but daemon modules accept (string, ...args).
|
|
@@ -34,6 +35,7 @@ export async function startDaemon() {
|
|
|
34
35
|
setConsolidationLogger(log);
|
|
35
36
|
setRelationsLogger(log);
|
|
36
37
|
setEntityTypingLogger(log);
|
|
38
|
+
setQualityRollupsLogger(log);
|
|
37
39
|
setStalenessLogger(log);
|
|
38
40
|
// Initialize LLM client from config
|
|
39
41
|
initLLM({
|
|
@@ -107,6 +109,12 @@ export async function startDaemon() {
|
|
|
107
109
|
catch (e) {
|
|
108
110
|
log("ERROR", `Entity typing tick failed: ${e.message}`);
|
|
109
111
|
}
|
|
112
|
+
try {
|
|
113
|
+
await qualityRollupsTick(cfg);
|
|
114
|
+
}
|
|
115
|
+
catch (e) {
|
|
116
|
+
log("ERROR", `Memory quality rollups tick failed: ${e.message}`);
|
|
117
|
+
}
|
|
110
118
|
// Staleness scans every 1000 ticks (~83 min at 5s interval)
|
|
111
119
|
if (tickCount % 1000 === 0) {
|
|
112
120
|
try {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { LogFn } from "./qdrant.js";
|
|
2
2
|
export declare const MAINTENANCE_STATE_PATH: string;
|
|
3
|
-
export type MaintenanceJobName = "relation_inference" | "entity_typing";
|
|
3
|
+
export type MaintenanceJobName = "relation_inference" | "entity_typing" | "memory_quality_rollups";
|
|
4
4
|
export interface MaintenanceRunSummary {
|
|
5
5
|
job: MaintenanceJobName;
|
|
6
6
|
ran_at: string;
|
|
@@ -13,6 +13,7 @@ export const defaultMaintenanceState = () => ({
|
|
|
13
13
|
jobs: {
|
|
14
14
|
relation_inference: defaultJobState(),
|
|
15
15
|
entity_typing: defaultJobState(),
|
|
16
|
+
memory_quality_rollups: defaultJobState(),
|
|
16
17
|
},
|
|
17
18
|
});
|
|
18
19
|
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -42,6 +43,7 @@ export const readMaintenanceState = (log = () => { }) => {
|
|
|
42
43
|
jobs: {
|
|
43
44
|
relation_inference: coerceJobState(jobs.relation_inference),
|
|
44
45
|
entity_typing: coerceJobState(jobs.entity_typing),
|
|
46
|
+
memory_quality_rollups: coerceJobState(jobs.memory_quality_rollups),
|
|
45
47
|
},
|
|
46
48
|
};
|
|
47
49
|
}
|
package/dist/daemon/qdrant.d.ts
CHANGED
|
@@ -7,9 +7,12 @@
|
|
|
7
7
|
* `qdrant_api_key` is optional — leave unset for unauthenticated local /
|
|
8
8
|
* self-hosted instances.
|
|
9
9
|
*/
|
|
10
|
+
import { type Destination } from "../config.js";
|
|
10
11
|
import { embed } from "../llm/index.js";
|
|
11
12
|
import type { InitEmbeddingInput } from "../llm/index.js";
|
|
13
|
+
import { type RoutingInput } from "../routing.js";
|
|
12
14
|
import { type RedactionSummary } from "../privacy/redaction.js";
|
|
15
|
+
import { type OperationOrigin } from "../provenance/origin.js";
|
|
13
16
|
export type LogFn = (level: string, ...args: unknown[]) => void;
|
|
14
17
|
export interface QdrantPayload {
|
|
15
18
|
content: string;
|
|
@@ -18,10 +21,15 @@ export interface QdrantPayload {
|
|
|
18
21
|
kind: string;
|
|
19
22
|
layer?: string | null;
|
|
20
23
|
memory_subtype?: string | null;
|
|
24
|
+
origin?: OperationOrigin;
|
|
25
|
+
last_operation_origin?: OperationOrigin;
|
|
26
|
+
/** @deprecated Origin is canonical for new writes. */
|
|
21
27
|
workspace_id?: string;
|
|
28
|
+
/** @deprecated Origin is canonical for new writes. */
|
|
22
29
|
actor_id?: string;
|
|
23
30
|
entities: string[];
|
|
24
|
-
|
|
31
|
+
/** @deprecated Origin is canonical for new writes. */
|
|
32
|
+
source?: string;
|
|
25
33
|
confidence: number;
|
|
26
34
|
importance: number;
|
|
27
35
|
content_hash: string;
|
|
@@ -65,11 +73,15 @@ export interface StoreFact {
|
|
|
65
73
|
layer?: string | null;
|
|
66
74
|
memory_subtype?: string | null;
|
|
67
75
|
entities: string[];
|
|
76
|
+
origin?: OperationOrigin;
|
|
77
|
+
last_operation_origin?: OperationOrigin;
|
|
78
|
+
/** @deprecated Origin is canonical for new writes. */
|
|
68
79
|
source?: string;
|
|
69
80
|
confidence?: number;
|
|
70
81
|
importance?: number;
|
|
71
82
|
content_hash: string;
|
|
72
83
|
workspace_id?: string;
|
|
84
|
+
/** @deprecated Origin is canonical for new writes. */
|
|
73
85
|
actor_id?: string;
|
|
74
86
|
metadata?: Record<string, string | number | boolean | null>;
|
|
75
87
|
session_id?: string | null;
|
|
@@ -100,6 +112,7 @@ export interface StoreFact {
|
|
|
100
112
|
}
|
|
101
113
|
export interface QdrantSearchResult {
|
|
102
114
|
id: string;
|
|
115
|
+
destination?: string;
|
|
103
116
|
score: number;
|
|
104
117
|
content: string;
|
|
105
118
|
category: string;
|
|
@@ -110,6 +123,7 @@ export interface QdrantSearchResult {
|
|
|
110
123
|
}
|
|
111
124
|
export interface QdrantScrollResult {
|
|
112
125
|
id: string;
|
|
126
|
+
destination?: string;
|
|
113
127
|
content: string;
|
|
114
128
|
category: string;
|
|
115
129
|
entities: string[];
|
|
@@ -151,6 +165,7 @@ export interface QdrantScrollFilters {
|
|
|
151
165
|
export type DedupAction = "insert" | "skip" | "supersede";
|
|
152
166
|
export interface DedupResult {
|
|
153
167
|
action: DedupAction;
|
|
168
|
+
destination?: string;
|
|
154
169
|
existingId?: string;
|
|
155
170
|
existingCount?: number;
|
|
156
171
|
score?: number;
|
|
@@ -162,16 +177,23 @@ export interface DedupThresholds {
|
|
|
162
177
|
declare let collection: string;
|
|
163
178
|
declare const setLogger: (fn: LogFn) => void;
|
|
164
179
|
declare const setEmbeddingConfig: (overrides?: Partial<InitEmbeddingInput>) => void;
|
|
180
|
+
type DestinationRef = Destination | string | null | undefined;
|
|
181
|
+
declare const resolveDestination: (input?: RoutingInput) => Destination;
|
|
165
182
|
declare const init: () => boolean;
|
|
166
183
|
declare const isReady: () => boolean;
|
|
167
184
|
declare const ensureCollection: () => Promise<void>;
|
|
168
|
-
declare const qdrantRequest: (method: string, urlPath: string, body?: unknown) => Promise<Record<string, unknown>>;
|
|
169
|
-
declare const
|
|
170
|
-
declare const
|
|
171
|
-
declare const
|
|
172
|
-
declare const
|
|
173
|
-
declare const
|
|
174
|
-
declare const
|
|
185
|
+
declare const qdrantRequest: (method: string, urlPath: string, body?: unknown, destinationRef?: DestinationRef) => Promise<Record<string, unknown>>;
|
|
186
|
+
declare const collectionForDestination: (destinationRef?: DestinationRef) => string;
|
|
187
|
+
declare const destinationNames: () => string[];
|
|
188
|
+
declare const readyDestinations: () => Destination[];
|
|
189
|
+
declare const activeDestinations: () => Destination[];
|
|
190
|
+
declare const searchFacts: (query: string, filters?: QdrantSearchFilters, limit?: number, destinationRef?: DestinationRef) => Promise<QdrantSearchResult[]>;
|
|
191
|
+
declare const scrollFacts: (filters?: QdrantScrollFilters, limit?: number, destinationRef?: DestinationRef) => Promise<QdrantScrollResult[]>;
|
|
192
|
+
declare const scrollFactsAcrossDestinations: (filters?: QdrantScrollFilters, limit?: number) => Promise<QdrantScrollResult[]>;
|
|
193
|
+
declare const storeFact: (fact: StoreFact, routeInput?: RoutingInput) => Promise<string>;
|
|
194
|
+
declare const supersedeFact: (oldFactId: string, newFactId: string, destinationRef?: DestinationRef, origin?: OperationOrigin) => Promise<void>;
|
|
195
|
+
declare const reinforceFact: (factId: string, currentCount: number, destinationRef?: DestinationRef, origin?: OperationOrigin) => Promise<void>;
|
|
196
|
+
declare const dedupCheck: (content: string, contentHashVal: string, { exactThreshold, supersedeThreshold }?: DedupThresholds, workspaceId?: string, routeInput?: RoutingInput) => Promise<DedupResult>;
|
|
175
197
|
/**
|
|
176
198
|
* Check whether incoming content is similar to any fact previously marked as a
|
|
177
199
|
* bad exemplar (via memory_forget). Returns the top similarity score, or null
|
|
@@ -181,10 +203,10 @@ declare const dedupCheck: (content: string, contentHashVal: string, { exactThres
|
|
|
181
203
|
* No hardcoded vocabulary — purely embedding-similarity based, and the
|
|
182
204
|
* exemplar set grows organically every time a user calls memory_forget.
|
|
183
205
|
*/
|
|
184
|
-
declare const badExemplarCheck: (content: string, workspaceId?: string) => Promise<{
|
|
206
|
+
declare const badExemplarCheck: (content: string, workspaceId?: string, routeInput?: RoutingInput) => Promise<{
|
|
185
207
|
score: number;
|
|
186
208
|
exemplarId: string;
|
|
187
209
|
reason?: string;
|
|
188
210
|
} | null>;
|
|
189
|
-
export { init, isReady, ensureCollection, setLogger, setEmbeddingConfig, qdrantRequest, embed, searchFacts, scrollFacts, storeFact, supersedeFact, reinforceFact, dedupCheck, badExemplarCheck, collection, };
|
|
211
|
+
export { init, isReady, ensureCollection, setLogger, setEmbeddingConfig, qdrantRequest, resolveDestination, collectionForDestination, destinationNames, readyDestinations, activeDestinations, embed, searchFacts, scrollFacts, scrollFactsAcrossDestinations, storeFact, supersedeFact, reinforceFact, dedupCheck, badExemplarCheck, collection, };
|
|
190
212
|
//# sourceMappingURL=qdrant.d.ts.map
|