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.
@@ -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
- "human",
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
- "human",
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
- "human",
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: "human",
199
- person_profile: "human",
200
- ownership_note: "human",
201
- working_agreement: "human",
202
- activity_event: "human",
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
- "human.personal_productivity": 180,
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 resolved = resolveDestOrError(routingInput({
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: 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 resolved = resolveDestOrError(routingInput({
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", "human", "system"] } });
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 conventions?\n" +
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 you learn human context: a preference, owner, working agreement, person profile, or durable activity event?\n" +
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
  });
@@ -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;
@@ -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", "Human", "System"];
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", "Human", "System"];
17
+ export declare const ALLOWED_BRIEF_HEADINGS: readonly ["Engineering", "Product", "System"];
18
18
  export {};
19
19
  //# sourceMappingURL=brief.d.ts.map
@@ -12,7 +12,6 @@ export const BRIEF_PROMPT_DESCRIPTOR = {
12
12
  const ALLOWED_HEADINGS = [
13
13
  "Engineering",
14
14
  "Product",
15
- "Human",
16
15
  "System",
17
16
  ];
18
17
  const SYSTEM_TEMPLATE = (allowedHeadings) => `<role>
@@ -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 → Human → R6 wins → preference."
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 → Human → R7 wins → activity_event."`;
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, Human, and System-aligned facts that help a future agent continue useful work.",
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