bikky 0.4.3 → 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 +60 -35
- package/dist/config.d.ts +5 -1
- package/dist/config.js +28 -0
- package/dist/daemon/capture-policy.js +0 -1
- package/dist/daemon/consolidation.js +4 -4
- package/dist/daemon/extraction.d.ts +1 -1
- package/dist/daemon/extraction.js +13 -5
- 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.js +30 -8
- package/dist/daemon/quality-rollups.d.ts +51 -0
- package/dist/daemon/quality-rollups.js +378 -0
- package/dist/daemon/relations.js +1 -1
- package/dist/daemon/staleness.js +1 -1
- package/dist/lifecycle.js +7 -5
- package/dist/mcp/helpers.d.ts +3 -0
- package/dist/mcp/helpers.js +9 -0
- package/dist/mcp/taxonomy.d.ts +9 -13
- package/dist/mcp/taxonomy.js +47 -41
- package/dist/mcp/tools.js +190 -26
- package/dist/mcp/types.d.ts +23 -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 +1 -1
- 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/hosted-qdrant-local-models.md +1 -0
- package/docs/config/local.md +1 -0
- package/docs/configuration.md +28 -21
- package/package.json +1 -1
package/dist/mcp/taxonomy.js
CHANGED
|
@@ -22,13 +22,6 @@ export const CATEGORIES = {
|
|
|
22
22
|
"Activation should be measured by whether agents successfully reuse recalled memory.",
|
|
23
23
|
],
|
|
24
24
|
},
|
|
25
|
-
human: {
|
|
26
|
-
description: "Human context: explicit preferences, people and roles, ownership, working agreements, and durable actor-action activity events.",
|
|
27
|
-
examples: [
|
|
28
|
-
"Saber prefers concise implementation plans before code changes.",
|
|
29
|
-
"Alex approved PR #85 for merge.",
|
|
30
|
-
],
|
|
31
|
-
},
|
|
32
25
|
system: {
|
|
33
26
|
description: "System context: Bikky-owned lifecycle memory, session indexes, episodes, workstreams, recall/feedback/outcome telemetry, and aggregate rollups.",
|
|
34
27
|
examples: [
|
|
@@ -47,7 +40,6 @@ export const DOMAINS = {
|
|
|
47
40
|
defaultCategories: [
|
|
48
41
|
"engineering",
|
|
49
42
|
"product",
|
|
50
|
-
"human",
|
|
51
43
|
"system",
|
|
52
44
|
],
|
|
53
45
|
},
|
|
@@ -55,7 +47,7 @@ export const DOMAINS = {
|
|
|
55
47
|
description: "Product direction, positioning, roadmap tradeoffs, customer problems, metrics, and market learning.",
|
|
56
48
|
defaultCategories: [
|
|
57
49
|
"product",
|
|
58
|
-
"
|
|
50
|
+
"engineering",
|
|
59
51
|
"system",
|
|
60
52
|
],
|
|
61
53
|
},
|
|
@@ -63,7 +55,7 @@ export const DOMAINS = {
|
|
|
63
55
|
description: "Business process, vendors, finance, legal/admin operations, recurring procedures, and ownership.",
|
|
64
56
|
defaultCategories: [
|
|
65
57
|
"product",
|
|
66
|
-
"
|
|
58
|
+
"engineering",
|
|
67
59
|
"system",
|
|
68
60
|
],
|
|
69
61
|
},
|
|
@@ -78,7 +70,7 @@ export const DOMAINS = {
|
|
|
78
70
|
personal_productivity: {
|
|
79
71
|
description: "Individual productivity, habits, planning preferences, reminders, and personal operating context.",
|
|
80
72
|
defaultCategories: [
|
|
81
|
-
"
|
|
73
|
+
"engineering",
|
|
82
74
|
"product",
|
|
83
75
|
"system",
|
|
84
76
|
],
|
|
@@ -195,11 +187,11 @@ export const MEMORY_SUBTYPE_DEFAULT_CATEGORY = {
|
|
|
195
187
|
success_metric: "product",
|
|
196
188
|
market_insight: "product",
|
|
197
189
|
troubleshooting_gotcha: "engineering",
|
|
198
|
-
preference: "
|
|
199
|
-
person_profile: "
|
|
200
|
-
ownership_note: "
|
|
201
|
-
working_agreement: "
|
|
202
|
-
activity_event: "
|
|
190
|
+
preference: "engineering",
|
|
191
|
+
person_profile: "engineering",
|
|
192
|
+
ownership_note: "engineering",
|
|
193
|
+
working_agreement: "engineering",
|
|
194
|
+
activity_event: "engineering",
|
|
203
195
|
session_index: "system",
|
|
204
196
|
episode: "system",
|
|
205
197
|
workstream: "system",
|
|
@@ -267,19 +259,18 @@ export const DECAY_HALF_LIFE = {
|
|
|
267
259
|
// Software-engineering defaults.
|
|
268
260
|
"engineering.software_engineering": 120,
|
|
269
261
|
"product.software_engineering": 120,
|
|
270
|
-
"human.software_engineering": 180,
|
|
271
262
|
"system.software_engineering": 45,
|
|
272
263
|
// Other domain profiles.
|
|
264
|
+
"engineering.product_strategy": 180,
|
|
273
265
|
"product.product_strategy": 180,
|
|
274
|
-
"human.product_strategy": 180,
|
|
275
266
|
"system.product_strategy": 60,
|
|
267
|
+
"engineering.business_operations": 180,
|
|
276
268
|
"product.business_operations": 120,
|
|
277
|
-
"human.business_operations": 180,
|
|
278
269
|
"system.business_operations": 90,
|
|
279
270
|
"product.research": 120,
|
|
280
271
|
"engineering.research": 120,
|
|
281
272
|
"system.research": 90,
|
|
282
|
-
"
|
|
273
|
+
"engineering.personal_productivity": 180,
|
|
283
274
|
"product.personal_productivity": 90,
|
|
284
275
|
"system.personal_productivity": 45,
|
|
285
276
|
};
|
|
@@ -329,6 +320,22 @@ export const QDRANT_INDEXES = [
|
|
|
329
320
|
{ field_name: "superseded", field_schema: "bool" },
|
|
330
321
|
{ field_name: "superseded_by", field_schema: "keyword" },
|
|
331
322
|
{ field_name: "is_bad_exemplar", field_schema: "bool" },
|
|
323
|
+
{ field_name: "target_fact_id", field_schema: "keyword" },
|
|
324
|
+
{ field_name: "feedback_kind", field_schema: "keyword" },
|
|
325
|
+
{ field_name: "outcome", field_schema: "keyword" },
|
|
326
|
+
{ field_name: "result_count", field_schema: "integer" },
|
|
327
|
+
{ field_name: "scope_type", field_schema: "keyword" },
|
|
328
|
+
{ field_name: "scope_value", field_schema: "keyword" },
|
|
329
|
+
{ field_name: "rollup_type", field_schema: "keyword" },
|
|
330
|
+
{ field_name: "rollup_generated_at", field_schema: "datetime" },
|
|
331
|
+
{ field_name: "active_fact_count", field_schema: "integer" },
|
|
332
|
+
{ field_name: "useful_count", field_schema: "integer" },
|
|
333
|
+
{ field_name: "not_useful_count", field_schema: "integer" },
|
|
334
|
+
{ field_name: "misleading_count", field_schema: "integer" },
|
|
335
|
+
{ field_name: "wrong_count", field_schema: "integer" },
|
|
336
|
+
{ field_name: "irrelevant_count", field_schema: "integer" },
|
|
337
|
+
{ field_name: "stale_count", field_schema: "integer" },
|
|
338
|
+
{ field_name: "low_confidence_count", field_schema: "integer" },
|
|
332
339
|
{ field_name: "useful_feedback_count", field_schema: "integer" },
|
|
333
340
|
{ field_name: "not_useful_feedback_count", field_schema: "integer" },
|
|
334
341
|
{ field_name: "recall_count", field_schema: "integer" },
|
|
@@ -358,16 +365,6 @@ export function normalizeCategory(category) {
|
|
|
358
365
|
normalized.includes("customer") ||
|
|
359
366
|
normalized === "projects")
|
|
360
367
|
return "product";
|
|
361
|
-
if (normalized.includes("human") ||
|
|
362
|
-
normalized.includes("people") ||
|
|
363
|
-
normalized.includes("person") ||
|
|
364
|
-
normalized.includes("owner") ||
|
|
365
|
-
normalized.includes("team") ||
|
|
366
|
-
normalized.includes("preference") ||
|
|
367
|
-
normalized.includes("agreement") ||
|
|
368
|
-
normalized.includes("activity") ||
|
|
369
|
-
normalized.includes("actor"))
|
|
370
|
-
return "human";
|
|
371
368
|
if (normalized.includes("system") ||
|
|
372
369
|
normalized.includes("session") ||
|
|
373
370
|
normalized.includes("episode") ||
|
|
@@ -385,7 +382,16 @@ export function normalizeCategory(category) {
|
|
|
385
382
|
normalized.includes("code") ||
|
|
386
383
|
normalized.includes("architecture") ||
|
|
387
384
|
normalized.includes("troubleshoot") ||
|
|
388
|
-
normalized.includes("observation")
|
|
385
|
+
normalized.includes("observation") ||
|
|
386
|
+
normalized.includes("human") ||
|
|
387
|
+
normalized.includes("people") ||
|
|
388
|
+
normalized.includes("person") ||
|
|
389
|
+
normalized.includes("owner") ||
|
|
390
|
+
normalized.includes("team") ||
|
|
391
|
+
normalized.includes("preference") ||
|
|
392
|
+
normalized.includes("agreement") ||
|
|
393
|
+
normalized.includes("activity") ||
|
|
394
|
+
normalized.includes("actor"))
|
|
389
395
|
return "engineering";
|
|
390
396
|
return DEFAULT_CATEGORY;
|
|
391
397
|
}
|
|
@@ -514,15 +520,6 @@ export function getDecayHalfLife(input) {
|
|
|
514
520
|
rawCategory.includes("market") ||
|
|
515
521
|
rawCategory.includes("customer") ||
|
|
516
522
|
rawCategory === "projects" ||
|
|
517
|
-
rawCategory.includes("human") ||
|
|
518
|
-
rawCategory.includes("people") ||
|
|
519
|
-
rawCategory.includes("person") ||
|
|
520
|
-
rawCategory.includes("owner") ||
|
|
521
|
-
rawCategory.includes("team") ||
|
|
522
|
-
rawCategory.includes("preference") ||
|
|
523
|
-
rawCategory.includes("agreement") ||
|
|
524
|
-
rawCategory.includes("activity") ||
|
|
525
|
-
rawCategory.includes("actor") ||
|
|
526
523
|
rawCategory.includes("system") ||
|
|
527
524
|
rawCategory.includes("session") ||
|
|
528
525
|
rawCategory.includes("episode") ||
|
|
@@ -539,7 +536,16 @@ export function getDecayHalfLife(input) {
|
|
|
539
536
|
rawCategory.includes("code") ||
|
|
540
537
|
rawCategory.includes("architecture") ||
|
|
541
538
|
rawCategory.includes("troubleshoot") ||
|
|
542
|
-
rawCategory.includes("observation")
|
|
539
|
+
rawCategory.includes("observation") ||
|
|
540
|
+
rawCategory.includes("human") ||
|
|
541
|
+
rawCategory.includes("people") ||
|
|
542
|
+
rawCategory.includes("person") ||
|
|
543
|
+
rawCategory.includes("owner") ||
|
|
544
|
+
rawCategory.includes("team") ||
|
|
545
|
+
rawCategory.includes("preference") ||
|
|
546
|
+
rawCategory.includes("agreement") ||
|
|
547
|
+
rawCategory.includes("activity") ||
|
|
548
|
+
rawCategory.includes("actor");
|
|
543
549
|
if (categoryWasProvided && !categoryIsKnown) {
|
|
544
550
|
return DECAY_DEFAULT_HALF_LIFE;
|
|
545
551
|
}
|
package/dist/mcp/tools.js
CHANGED
|
@@ -13,6 +13,7 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
13
13
|
import { inspectWatcherPaths, formatIssue, repairSuspiciousWatcherPaths } from "../daemon/watcher-health.js";
|
|
14
14
|
import { buildOperationOrigin } from "../provenance/origin.js";
|
|
15
15
|
import { addRedactionPayload, combineRedactions, redactStorageText, } from "../privacy/redaction.js";
|
|
16
|
+
import { buildMemoryRoutingInput } from "../routing-context.js";
|
|
16
17
|
// ---------------------------------------------------------------------------
|
|
17
18
|
// Runtime state
|
|
18
19
|
// ---------------------------------------------------------------------------
|
|
@@ -31,6 +32,19 @@ function nowISO() {
|
|
|
31
32
|
function newId() {
|
|
32
33
|
return crypto.randomUUID();
|
|
33
34
|
}
|
|
35
|
+
function outcomeCounterField(outcome) {
|
|
36
|
+
if (outcome === "useful")
|
|
37
|
+
return "useful_count";
|
|
38
|
+
if (outcome === "misleading")
|
|
39
|
+
return "misleading_count";
|
|
40
|
+
if (outcome === "wrong")
|
|
41
|
+
return "wrong_count";
|
|
42
|
+
return "irrelevant_count";
|
|
43
|
+
}
|
|
44
|
+
function payloadCounter(payload, field) {
|
|
45
|
+
const value = payload[field];
|
|
46
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
47
|
+
}
|
|
34
48
|
// Build a RoutingInput from the standard memory-tool fields.
|
|
35
49
|
function routingInput(args) {
|
|
36
50
|
return {
|
|
@@ -41,6 +55,37 @@ function routingInput(args) {
|
|
|
41
55
|
metadata: args.metadata,
|
|
42
56
|
};
|
|
43
57
|
}
|
|
58
|
+
function compactMetadata(values) {
|
|
59
|
+
const metadata = {};
|
|
60
|
+
for (const [key, value] of Object.entries(values)) {
|
|
61
|
+
if (value === undefined || value === null)
|
|
62
|
+
continue;
|
|
63
|
+
metadata[key] = value;
|
|
64
|
+
}
|
|
65
|
+
return metadata;
|
|
66
|
+
}
|
|
67
|
+
function memoryWriteRoutingInput(args) {
|
|
68
|
+
const relationMetadata = args.relation ? {
|
|
69
|
+
from_entity: args.relation.from,
|
|
70
|
+
relation_type: args.relation.type,
|
|
71
|
+
to_entity: args.relation.to,
|
|
72
|
+
} : {};
|
|
73
|
+
return buildMemoryRoutingInput({
|
|
74
|
+
destination: args.destination,
|
|
75
|
+
cwd: process.cwd(),
|
|
76
|
+
content: args.content,
|
|
77
|
+
entities: args.entities,
|
|
78
|
+
metadata: args.metadata,
|
|
79
|
+
context: compactMetadata({
|
|
80
|
+
origin_interface: "mcp",
|
|
81
|
+
origin_action: "create",
|
|
82
|
+
origin_tool: args.tool,
|
|
83
|
+
...(args.context ?? {}),
|
|
84
|
+
...relationMetadata,
|
|
85
|
+
}),
|
|
86
|
+
extraContent: args.relation ? [args.relation] : [],
|
|
87
|
+
});
|
|
88
|
+
}
|
|
44
89
|
// Resolve a destination from a routing input, returning either the destination
|
|
45
90
|
// or an MCP error result if the override is invalid / no destinations exist.
|
|
46
91
|
function resolveDestOrError(input) {
|
|
@@ -82,6 +127,77 @@ function formatScopedFact(point, includeDestination) {
|
|
|
82
127
|
const formatted = formatFact(point);
|
|
83
128
|
return includeDestination ? `[${point._destination.name}] ${formatted}` : formatted;
|
|
84
129
|
}
|
|
130
|
+
async function recordRecallTelemetry(args) {
|
|
131
|
+
const now = nowISO();
|
|
132
|
+
const byDestination = new Map();
|
|
133
|
+
for (const point of args.ranked) {
|
|
134
|
+
const existing = byDestination.get(point._destination.name);
|
|
135
|
+
if (existing) {
|
|
136
|
+
existing.points.push(point);
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
byDestination.set(point._destination.name, { dest: point._destination, points: [point] });
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
for (const { dest, points } of byDestination.values()) {
|
|
143
|
+
const returnedFactIds = points.map((point) => point.id);
|
|
144
|
+
const recallOrigin = mcpOrigin({
|
|
145
|
+
action: "recall",
|
|
146
|
+
tool: "memory_recall",
|
|
147
|
+
metadata: {
|
|
148
|
+
destination: dest.name,
|
|
149
|
+
result_count: points.length,
|
|
150
|
+
search_scope: args.searchScope.name,
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
for (const point of points) {
|
|
154
|
+
const currentCount = point.payload.recall_count ?? 0;
|
|
155
|
+
try {
|
|
156
|
+
await qdrantSetPayload(dest, [point.id], {
|
|
157
|
+
recall_count: currentCount + 1,
|
|
158
|
+
last_recalled_at: now,
|
|
159
|
+
updated_at: now,
|
|
160
|
+
last_operation_origin: recallOrigin,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
catch (e) {
|
|
164
|
+
log("WARN", `Failed to update recall_count for ${point.id}: ${e instanceof Error ? e.message : String(e)}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
const entities = [...new Set(points.flatMap((point) => point.payload.entities ?? []))].slice(0, 25);
|
|
168
|
+
const eventContent = `Recall returned ${points.length} fact(s) from ${dest.name}: ${args.redactedQuery.text}`;
|
|
169
|
+
const redactedEvent = redactStorageText(eventContent);
|
|
170
|
+
const eventPayload = {
|
|
171
|
+
content: redactedEvent.text,
|
|
172
|
+
category: categoryForMemorySubtype("recall_event") ?? "system",
|
|
173
|
+
domain: "software_engineering",
|
|
174
|
+
kind: "telemetry",
|
|
175
|
+
memory_subtype: "recall_event",
|
|
176
|
+
layer: "memory_object",
|
|
177
|
+
entities,
|
|
178
|
+
origin: recallOrigin,
|
|
179
|
+
confidence: 1.0,
|
|
180
|
+
importance: 0.3,
|
|
181
|
+
content_hash: contentHash("recall_event", `${dest.name}:${returnedFactIds.join(",")}:${now}`),
|
|
182
|
+
recall_query: args.redactedQuery.text,
|
|
183
|
+
returned_fact_ids: returnedFactIds,
|
|
184
|
+
result_count: points.length,
|
|
185
|
+
search_scope: args.searchScope.name,
|
|
186
|
+
searched_destinations: args.searchedDestinations,
|
|
187
|
+
failed_destinations: args.failedDestinations.map((failure) => `${failure.destination}: ${failure.error}`),
|
|
188
|
+
created_at: now,
|
|
189
|
+
updated_at: now,
|
|
190
|
+
};
|
|
191
|
+
addRedactionPayload(eventPayload, redactedEvent);
|
|
192
|
+
try {
|
|
193
|
+
const eventVector = await embed(redactedEvent.text);
|
|
194
|
+
await qdrantUpsert(dest, newId(), eventVector, eventPayload);
|
|
195
|
+
}
|
|
196
|
+
catch (e) {
|
|
197
|
+
log("WARN", `Failed to record recall_event: ${e instanceof Error ? e.message : String(e)}`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
85
201
|
function resolveSearchScopeOrError(args) {
|
|
86
202
|
if (args.destination && args.search_scope !== undefined) {
|
|
87
203
|
return {
|
|
@@ -194,9 +310,8 @@ function buildMemoryNudge() {
|
|
|
194
310
|
// session typically produces. The agent picks the best fit.
|
|
195
311
|
return `🧠 Memory nudge: No memory_store calls in ${mins} minutes. ` +
|
|
196
312
|
"Reflect on what's worth persisting:\n" +
|
|
197
|
-
" • engineering — codebase maps, architecture, infra, ops, troubleshooting?\n" +
|
|
313
|
+
" • engineering — codebase maps, architecture, infra, ops, troubleshooting, preferences, ownership, working agreements, activity events?\n" +
|
|
198
314
|
" • product — requirements, decisions, workflows, roadmap, metrics, market insight?\n" +
|
|
199
|
-
" • human — preferences, owners, working agreements, durable activity events?\n" +
|
|
200
315
|
" • system — session, episode, workstream, or quality-rollup memory?\n" +
|
|
201
316
|
"If yes, call memory_store now so future sessions inherit the knowledge.";
|
|
202
317
|
}
|
|
@@ -541,15 +656,6 @@ export function registerTools(mcp) {
|
|
|
541
656
|
return guard;
|
|
542
657
|
lastStoreTime = Date.now();
|
|
543
658
|
const now = nowISO();
|
|
544
|
-
const resolved = resolveDestOrError(routingInput({
|
|
545
|
-
destination,
|
|
546
|
-
content,
|
|
547
|
-
entities,
|
|
548
|
-
metadata,
|
|
549
|
-
}));
|
|
550
|
-
if (resolved.error)
|
|
551
|
-
return resolved.error;
|
|
552
|
-
const dest = resolved.dest;
|
|
553
659
|
const normalizedKind = normalizeKind(kind);
|
|
554
660
|
let normalizedSubtype = null;
|
|
555
661
|
try {
|
|
@@ -592,6 +698,31 @@ export function registerTools(mcp) {
|
|
|
592
698
|
type: redactedRelation.type.text,
|
|
593
699
|
to: redactedRelation.to.text,
|
|
594
700
|
} : null;
|
|
701
|
+
const resolved = resolveDestOrError(memoryWriteRoutingInput({
|
|
702
|
+
tool: "memory_store",
|
|
703
|
+
destination,
|
|
704
|
+
content: redactedContent.text,
|
|
705
|
+
entities: normalizedEntities,
|
|
706
|
+
metadata,
|
|
707
|
+
relation: sanitizedRelation,
|
|
708
|
+
context: {
|
|
709
|
+
category: normalizedCategory,
|
|
710
|
+
domain: normalizedDomain,
|
|
711
|
+
kind: normalizedKind,
|
|
712
|
+
memory_subtype: normalizedSubtype,
|
|
713
|
+
layer: normalizedLayer,
|
|
714
|
+
episode_id,
|
|
715
|
+
workstream_key,
|
|
716
|
+
task_key,
|
|
717
|
+
repo,
|
|
718
|
+
branch,
|
|
719
|
+
review_status,
|
|
720
|
+
supersedes,
|
|
721
|
+
},
|
|
722
|
+
}));
|
|
723
|
+
if (resolved.error)
|
|
724
|
+
return resolved.error;
|
|
725
|
+
const dest = resolved.dest;
|
|
595
726
|
const createOrigin = mcpOrigin({
|
|
596
727
|
action: "create",
|
|
597
728
|
tool: "memory_store",
|
|
@@ -979,6 +1110,13 @@ export function registerTools(mcp) {
|
|
|
979
1110
|
.map((r) => ({ ...r, _combinedScore: computeCombinedScore(r) }))
|
|
980
1111
|
.sort((a, b) => b._combinedScore - a._combinedScore)
|
|
981
1112
|
.slice(0, effectiveLimit);
|
|
1113
|
+
await recordRecallTelemetry({
|
|
1114
|
+
ranked,
|
|
1115
|
+
redactedQuery,
|
|
1116
|
+
searchScope,
|
|
1117
|
+
searchedDestinations,
|
|
1118
|
+
failedDestinations,
|
|
1119
|
+
});
|
|
982
1120
|
const includeDestination = searchScope.destinations.length > 1 || searchScope.name === "all";
|
|
983
1121
|
const lines = ranked.map((r) => formatScopedFact(r, includeDestination));
|
|
984
1122
|
const related = { points: [], errors: [] };
|
|
@@ -1439,7 +1577,9 @@ export function registerTools(mcp) {
|
|
|
1439
1577
|
const located = await locatePoint(fact_id);
|
|
1440
1578
|
if (!located)
|
|
1441
1579
|
return notFoundResult(fact_id);
|
|
1442
|
-
const { dest } = located;
|
|
1580
|
+
const { dest, point } = located;
|
|
1581
|
+
const counterField = outcomeCounterField(outcome);
|
|
1582
|
+
const counterValue = payloadCounter(point.payload, counterField) + 1;
|
|
1443
1583
|
const feedbackOrigin = mcpOrigin({
|
|
1444
1584
|
action: "feedback",
|
|
1445
1585
|
tool: "memory_report_outcome",
|
|
@@ -1447,6 +1587,7 @@ export function registerTools(mcp) {
|
|
|
1447
1587
|
metadata: { destination: dest.name, fact_id },
|
|
1448
1588
|
});
|
|
1449
1589
|
await qdrantSetPayload(dest, [fact_id], {
|
|
1590
|
+
[counterField]: counterValue,
|
|
1450
1591
|
updated_at: now,
|
|
1451
1592
|
last_operation_origin: feedbackOrigin,
|
|
1452
1593
|
});
|
|
@@ -1481,6 +1622,7 @@ export function registerTools(mcp) {
|
|
|
1481
1622
|
fact_id,
|
|
1482
1623
|
destination: dest.name,
|
|
1483
1624
|
outcome,
|
|
1625
|
+
[counterField]: counterValue,
|
|
1484
1626
|
event_id: eventId,
|
|
1485
1627
|
}) }],
|
|
1486
1628
|
};
|
|
@@ -1510,17 +1652,29 @@ export function registerTools(mcp) {
|
|
|
1510
1652
|
lastStoreTime = Date.now();
|
|
1511
1653
|
const now = nowISO();
|
|
1512
1654
|
try {
|
|
1513
|
-
const
|
|
1655
|
+
const normalizedEntities = (entities ?? []).map((e) => e.trim().toLowerCase()).filter(Boolean);
|
|
1656
|
+
const redactedContent = redactStorageText(content);
|
|
1657
|
+
const resolved = resolveDestOrError(memoryWriteRoutingInput({
|
|
1658
|
+
tool: "memory_session_summary",
|
|
1514
1659
|
destination,
|
|
1515
|
-
content,
|
|
1516
|
-
entities:
|
|
1660
|
+
content: redactedContent.text,
|
|
1661
|
+
entities: normalizedEntities,
|
|
1662
|
+
context: {
|
|
1663
|
+
category: categoryForMemorySubtype("session_index") ?? "system",
|
|
1664
|
+
domain: "software_engineering",
|
|
1665
|
+
kind: "summary",
|
|
1666
|
+
memory_subtype: "session_index",
|
|
1667
|
+
layer: layerForMemorySubtype("session_index") ?? "episode",
|
|
1668
|
+
episode_id,
|
|
1669
|
+
workstream_key,
|
|
1670
|
+
task_key,
|
|
1671
|
+
repo,
|
|
1672
|
+
},
|
|
1517
1673
|
}));
|
|
1518
1674
|
if (resolved.error)
|
|
1519
1675
|
return resolved.error;
|
|
1520
1676
|
const dest = resolved.dest;
|
|
1521
|
-
const normalizedEntities = (entities ?? []).map((e) => e.trim().toLowerCase()).filter(Boolean);
|
|
1522
1677
|
const summaryId = newId();
|
|
1523
|
-
const redactedContent = redactStorageText(content);
|
|
1524
1678
|
const vector = await embed(redactedContent.text);
|
|
1525
1679
|
const origin = mcpOrigin({
|
|
1526
1680
|
action: "create",
|
|
@@ -1595,17 +1749,28 @@ export function registerTools(mcp) {
|
|
|
1595
1749
|
lastStoreTime = Date.now();
|
|
1596
1750
|
const now = nowISO();
|
|
1597
1751
|
try {
|
|
1598
|
-
const
|
|
1752
|
+
const normalizedEntities = entities.map((e) => e.trim().toLowerCase()).filter(Boolean);
|
|
1753
|
+
const redactedContent = redactStorageText(content);
|
|
1754
|
+
const resolved = resolveDestOrError(memoryWriteRoutingInput({
|
|
1755
|
+
tool: "memory_distill",
|
|
1599
1756
|
destination,
|
|
1600
|
-
content,
|
|
1601
|
-
entities,
|
|
1757
|
+
content: redactedContent.text,
|
|
1758
|
+
entities: normalizedEntities,
|
|
1759
|
+
context: {
|
|
1760
|
+
category: categoryForMemorySubtype("convention") ?? "engineering",
|
|
1761
|
+
domain: "software_engineering",
|
|
1762
|
+
kind: "distilled",
|
|
1763
|
+
memory_subtype: "convention",
|
|
1764
|
+
layer: layerForMemorySubtype("convention") ?? "domain",
|
|
1765
|
+
task_key,
|
|
1766
|
+
repo,
|
|
1767
|
+
supersedes,
|
|
1768
|
+
},
|
|
1602
1769
|
}));
|
|
1603
1770
|
if (resolved.error)
|
|
1604
1771
|
return resolved.error;
|
|
1605
1772
|
const dest = resolved.dest;
|
|
1606
|
-
const normalizedEntities = entities.map((e) => e.trim().toLowerCase()).filter(Boolean);
|
|
1607
1773
|
const distilledId = newId();
|
|
1608
|
-
const redactedContent = redactStorageText(content);
|
|
1609
1774
|
const vector = await embed(redactedContent.text);
|
|
1610
1775
|
const origin = mcpOrigin({
|
|
1611
1776
|
action: "create",
|
|
@@ -1849,7 +2014,7 @@ export function registerTools(mcp) {
|
|
|
1849
2014
|
try {
|
|
1850
2015
|
const staleThreshold = new Date(Date.now() - STALENESS_DAYS * 86400000).toISOString();
|
|
1851
2016
|
const staleFilter = { must: [] };
|
|
1852
|
-
staleFilter.must.push({ key: "category", match: { any: ["engineering", "product", "
|
|
2017
|
+
staleFilter.must.push({ key: "category", match: { any: ["engineering", "product", "system"] } });
|
|
1853
2018
|
staleFilter.should = [
|
|
1854
2019
|
{ key: "last_reinforced_at", range: { lte: staleThreshold } },
|
|
1855
2020
|
{ is_null: { key: "last_reinforced_at" } },
|
|
@@ -1888,10 +2053,9 @@ export function registerTools(mcp) {
|
|
|
1888
2053
|
}
|
|
1889
2054
|
}
|
|
1890
2055
|
sections.push("🔍 Reflect: think about the LAST 10 minutes of work and answer in your head:\n" +
|
|
1891
|
-
" 1. Did you touch engineering context: code, infra, ops, access, troubleshooting, or
|
|
2056
|
+
" 1. Did you touch engineering context: code, infra, ops, access, troubleshooting, conventions, preferences, ownership, working agreements, or durable activity events?\n" +
|
|
1892
2057
|
" 2. Did you capture product context: a requirement, decision, workflow, roadmap item, metric, or market insight?\n" +
|
|
1893
|
-
" 3. Did
|
|
1894
|
-
" 4. Did the work produce system context: session, episode, workstream, recall, feedback, outcome, or rollup state?\n" +
|
|
2058
|
+
" 3. Did the work produce system context: session, episode, workstream, recall, feedback, outcome, or rollup state?\n" +
|
|
1895
2059
|
"If any answer is yes, call memory_store now — one atomic fact per item, with category/domain/entities.");
|
|
1896
2060
|
return { content: [{ type: "text", text: sections.join("\n\n") }] };
|
|
1897
2061
|
});
|
package/dist/mcp/types.d.ts
CHANGED
|
@@ -87,8 +87,16 @@ export interface FactPayload {
|
|
|
87
87
|
verification_count?: number;
|
|
88
88
|
useful_count?: number;
|
|
89
89
|
not_useful_count?: number;
|
|
90
|
+
useful_feedback_count?: number;
|
|
91
|
+
not_useful_feedback_count?: number;
|
|
92
|
+
misleading_count?: number;
|
|
93
|
+
wrong_count?: number;
|
|
94
|
+
irrelevant_count?: number;
|
|
95
|
+
last_useful_at?: string;
|
|
90
96
|
last_used_at?: string;
|
|
91
97
|
last_feedback_at?: string;
|
|
98
|
+
recall_count?: number;
|
|
99
|
+
last_recalled_at?: string;
|
|
92
100
|
superseded_by: string | null;
|
|
93
101
|
superseded_at: string | null;
|
|
94
102
|
created_at: string;
|
|
@@ -139,9 +147,24 @@ export interface FactPayload {
|
|
|
139
147
|
event_session_id?: string;
|
|
140
148
|
recall_query?: string;
|
|
141
149
|
returned_fact_ids?: string[];
|
|
150
|
+
result_count?: number;
|
|
151
|
+
search_scope?: string;
|
|
152
|
+
searched_destinations?: string[];
|
|
153
|
+
failed_destinations?: string[];
|
|
154
|
+
target_fact_id?: string;
|
|
142
155
|
feedback_note?: string;
|
|
156
|
+
feedback_kind?: string;
|
|
143
157
|
outcome?: string;
|
|
144
158
|
outcome_summary?: string;
|
|
159
|
+
scope_type?: string;
|
|
160
|
+
scope_value?: string;
|
|
161
|
+
rollup_type?: string;
|
|
162
|
+
rollup_generated_at?: string;
|
|
163
|
+
rollup_window_start?: string;
|
|
164
|
+
rollup_window_end?: string;
|
|
165
|
+
active_fact_count?: number;
|
|
166
|
+
stale_count?: number;
|
|
167
|
+
low_confidence_count?: number;
|
|
145
168
|
}
|
|
146
169
|
export interface FilterParams {
|
|
147
170
|
category?: string;
|
package/dist/prompts/brief.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { type PromptDescriptor, type RenderedPrompt } from "./index.js";
|
|
8
8
|
export declare const BRIEF_PROMPT_DESCRIPTOR: PromptDescriptor;
|
|
9
|
-
declare const ALLOWED_HEADINGS: readonly ["Engineering", "Product", "
|
|
9
|
+
declare const ALLOWED_HEADINGS: readonly ["Engineering", "Product", "System"];
|
|
10
10
|
export interface BriefInput {
|
|
11
11
|
/** ISO date string injected into the brief header. */
|
|
12
12
|
generatedAt: string;
|
|
@@ -14,6 +14,6 @@ export interface BriefInput {
|
|
|
14
14
|
sections: Partial<Record<(typeof ALLOWED_HEADINGS)[number], string[]>>;
|
|
15
15
|
}
|
|
16
16
|
export declare const briefPrompt: (input: BriefInput) => RenderedPrompt;
|
|
17
|
-
export declare const ALLOWED_BRIEF_HEADINGS: readonly ["Engineering", "Product", "
|
|
17
|
+
export declare const ALLOWED_BRIEF_HEADINGS: readonly ["Engineering", "Product", "System"];
|
|
18
18
|
export {};
|
|
19
19
|
//# sourceMappingURL=brief.d.ts.map
|
package/dist/prompts/brief.js
CHANGED
|
@@ -12,13 +12,18 @@ export const EXTRACTION_PROMPT_DESCRIPTOR = {
|
|
|
12
12
|
const SUBTYPE_REASONING = `## Subtype reasoning (think step-by-step BEFORE picking memory_subtype)
|
|
13
13
|
For each candidate fact, walk these four top-level categories first, then pick the most concrete memory_subtype:
|
|
14
14
|
|
|
15
|
-
ENGINEERING — how the software is built and operated:
|
|
15
|
+
ENGINEERING — how the software is built and operated, including collaboration preferences:
|
|
16
16
|
codebase_map → file paths, symbols, modules, repo structure
|
|
17
17
|
architecture_decision → engineering/system design choice with rationale
|
|
18
18
|
infra_topology → clusters, services, queues, datastores, regions
|
|
19
19
|
access_pattern → roles, permissions, auth flows, approval gates
|
|
20
20
|
operational_procedure → runbook / deploy / rollout / maintenance / incident steps
|
|
21
21
|
troubleshooting_gotcha → stable failure mode, debugging quirk, surprising behaviour
|
|
22
|
+
preference → explicit style, tooling, or interaction preference
|
|
23
|
+
person_profile → durable role, expertise, person/team context
|
|
24
|
+
ownership_note → owner, approver, escalation path, accountability
|
|
25
|
+
working_agreement → collaboration norm or operating rule
|
|
26
|
+
activity_event → explicit actor-action-object event with durable project value
|
|
22
27
|
convention → produced by distillation, not this extraction prompt
|
|
23
28
|
|
|
24
29
|
PRODUCT — what the product should be and how it succeeds:
|
|
@@ -30,13 +35,6 @@ For each candidate fact, walk these four top-level categories first, then pick t
|
|
|
30
35
|
success_metric → KPI, adoption/retention/quality metric, evaluation target
|
|
31
36
|
market_insight → audience, customer/community feedback, competitor/GTM insight
|
|
32
37
|
|
|
33
|
-
HUMAN — durable people, preferences, and collaboration context:
|
|
34
|
-
preference → explicit style, tooling, or interaction preference
|
|
35
|
-
person_profile → durable role, expertise, person/team context
|
|
36
|
-
ownership_note → owner, approver, escalation path, accountability
|
|
37
|
-
working_agreement → collaboration norm or operating rule
|
|
38
|
-
activity_event → explicit actor-action-object event with durable project value
|
|
39
|
-
|
|
40
38
|
Disambiguation rules (apply in order; first match wins):
|
|
41
39
|
R1. If the fact describes a *failure* or *workaround* → troubleshooting_gotcha (NOT operational_procedure).
|
|
42
40
|
R2. If the fact names *files / modules / symbols* → codebase_map (NOT infra_topology).
|
|
@@ -62,7 +60,7 @@ Example 2 — codebase vs infra:
|
|
|
62
60
|
Example 3 — domain_rule vs preference:
|
|
63
61
|
TEXT: "I prefer kebab-case branch names; it's just my style."
|
|
64
62
|
→ memory_subtype: preference (R6: 'I prefer' + 'my style')
|
|
65
|
-
→ subtype_reason: "Personal style →
|
|
63
|
+
→ subtype_reason: "Personal style → Engineering → R6 wins → preference."
|
|
66
64
|
|
|
67
65
|
Example 4 — product vs engineering decision:
|
|
68
66
|
TEXT: "We decided the memory page should show categories and subtype chips directly because a sub-tab layer made the ontology feel confusing."
|
|
@@ -72,7 +70,7 @@ Example 4 — product vs engineering decision:
|
|
|
72
70
|
Example 5 — durable activity event:
|
|
73
71
|
TEXT: "Saber merged PR #85 after approving the subtype UX copy changes."
|
|
74
72
|
→ memory_subtype: activity_event (R7: actor + durable state-changing action + object)
|
|
75
|
-
→ subtype_reason: "Actor-action-object event tied to a PR →
|
|
73
|
+
→ subtype_reason: "Actor-action-object event tied to a PR → Engineering → R7 wins → activity_event."`;
|
|
76
74
|
const SELF_JUDGMENT = `## Self-judgment (think step-by-step BEFORE emitting each fact)
|
|
77
75
|
|
|
78
76
|
For every candidate fact, judge it on three axes. The verifier downstream USES these values, so be honest — bad self-judgment costs the fact its place in memory.
|
|
@@ -218,7 +216,7 @@ If nothing passes the quality gate, return: {"facts": []}`;
|
|
|
218
216
|
const buildSystem = () => {
|
|
219
217
|
return [
|
|
220
218
|
"<role>",
|
|
221
|
-
"You are Bikky's knowledge-extraction agent for open-source coding agents. You read session transcripts and emit durable Engineering, Product,
|
|
219
|
+
"You are Bikky's knowledge-extraction agent for open-source coding agents. You read session transcripts and emit durable Engineering, Product, and System-aligned facts that help a future agent continue useful work.",
|
|
222
220
|
"</role>",
|
|
223
221
|
"",
|
|
224
222
|
"<task>",
|
|
@@ -2,7 +2,7 @@ import type { BikkyConfig } from "../config.js";
|
|
|
2
2
|
export type OriginIdentityType = "user" | "coding_agent" | "daemon" | "ui" | "api" | "cli" | "docs" | "system" | "unknown";
|
|
3
3
|
export type OriginIdentitySource = "config" | "shell" | "git" | "env" | "hostname";
|
|
4
4
|
export type OriginInterface = "mcp" | "daemon" | "ui" | "api" | "cli" | "system";
|
|
5
|
-
export type OriginAction = "create" | "update" | "delete" | "verify" | "forget" | "review" | "correct" | "reinforce" | "supersede" | "feedback";
|
|
5
|
+
export type OriginAction = "create" | "update" | "delete" | "verify" | "forget" | "review" | "correct" | "reinforce" | "supersede" | "recall" | "aggregate" | "feedback";
|
|
6
6
|
export type OriginMetadataValue = string | number | boolean | null;
|
|
7
7
|
export interface OriginIdentity {
|
|
8
8
|
type: OriginIdentityType;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { RoutingInput } from "./routing.js";
|
|
2
|
+
type RoutingMetadata = Record<string, unknown>;
|
|
3
|
+
export interface MemoryRoutingInput {
|
|
4
|
+
destination?: string | null;
|
|
5
|
+
cwd?: string;
|
|
6
|
+
content?: string | null;
|
|
7
|
+
entities?: ReadonlyArray<string>;
|
|
8
|
+
metadata?: RoutingMetadata | null;
|
|
9
|
+
context?: RoutingMetadata | null;
|
|
10
|
+
extraContent?: ReadonlyArray<unknown>;
|
|
11
|
+
}
|
|
12
|
+
export declare const routingText: (values: ReadonlyArray<unknown>) => string;
|
|
13
|
+
export declare const buildMemoryRoutingInput: (input: MemoryRoutingInput) => RoutingInput;
|
|
14
|
+
export declare const mergeRoutingInputs: (base: RoutingInput, override?: RoutingInput) => RoutingInput;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=routing-context.d.ts.map
|