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/README.md
CHANGED
|
@@ -2,48 +2,69 @@
|
|
|
2
2
|
|
|
3
3
|
<p align="center"><b>Persistent memory for AI coding agents — built for teams and multi-agent engineering workflows.</b></p>
|
|
4
4
|
|
|
5
|
-
bikky gives AI coding agents (GitHub Copilot, Claude Code, Cursor, and other MCP clients) long-term memory that persists across sessions, across tools, and across your whole team. When multiple engineers, agents, or repos need to build on the same knowledge base, bikky captures what's learned *during* sessions so future sessions start smarter.
|
|
6
|
-
|
|
7
|
-
### Who it's for
|
|
8
|
-
|
|
9
|
-
- 👥 **Teams & software factories** — What one engineer's agent learns today, every agent on the team can recall tomorrow. Shared memory turns institutional knowledge into something queryable instead of tribal — onboarding accelerates, conventions stop drifting, and the same lesson never gets re-learned twice.
|
|
10
|
-
- 🤖 **Multi-agent engineering workflows** — Multiple Cursor / Claude Code / Copilot sessions can share codebase context, conventions, and recent decisions instead of re-learning them from scratch.
|
|
11
|
-
|
|
12
5
|
<p align="center">
|
|
13
6
|
<img src="https://raw.githubusercontent.com/bikky-dev/bikky/main/docs/diagrams/team-memory.svg" alt="Memory — facts flow from individual sessions into a self-curating knowledge store shared across your team" width="720" />
|
|
14
7
|
</p>
|
|
15
8
|
|
|
16
|
-
<p align="center"><i>
|
|
9
|
+
<p align="center"><i>Selected knowledge flows from supported sessions into a store that curates itself over time — deduplicating, distilling, and decaying stale facts — so future sessions can start with more context.</i></p>
|
|
17
10
|
|
|
18
|
-
|
|
11
|
+
bikky provides long-term memory for MCP-capable AI coding agents. It exposes memory tools over the Model Context Protocol (MCP), stores facts in Qdrant, and can run a local daemon that extracts durable facts from supported transcript sources. Teams can share memory across tools, repos, and engineers without treating chat history or closed PRs as the source of truth.
|
|
19
12
|
|
|
20
|
-
###
|
|
13
|
+
### Who it's for
|
|
14
|
+
|
|
15
|
+
- 👥 **Teams & software factories** — What one engineer's agent learns today can be recalled by other agents on the team tomorrow. Shared memory makes institutional knowledge queryable, helps onboarding, and reduces convention drift and repeated rediscovery.
|
|
16
|
+
- 🤖 **Multi-agent engineering workflows** — Multiple MCP-capable agent sessions can share codebase context, conventions, and recent decisions instead of re-learning them from scratch.
|
|
21
17
|
|
|
22
|
-
|
|
18
|
+
---
|
|
23
19
|
|
|
24
|
-
### How bikky
|
|
20
|
+
### How bikky works
|
|
25
21
|
|
|
26
|
-
bikky gives your agent memory tools and runs a small background service after `bikky setup`. You keep working normally; bikky captures useful facts, organizes them, recalls them in future sessions, and keeps the store tidy over time.
|
|
22
|
+
bikky gives your agent memory tools and runs a small background service after `bikky setup`. You keep working normally; bikky captures useful facts from supported transcript sources, organizes them, recalls them in future sessions, and keeps the store tidy over time.
|
|
27
23
|
|
|
28
|
-
- **Capture** — Facts are extracted automatically from session transcripts
|
|
29
|
-
- **Classify** — Memories are grouped as **engineering**, **product**,
|
|
30
|
-
- **Recall** —
|
|
24
|
+
- **Capture** — Facts are extracted automatically from supported session transcripts without requiring manual notes for every fact.
|
|
25
|
+
- **Classify** — Memories are grouped as **engineering**, **product**, or **system** so they stay easy to browse and filter.
|
|
26
|
+
- **Recall** — New sessions can recall from the same store via semantic search.
|
|
31
27
|
- **Curate** — bikky merges duplicates, fades stale facts, resolves contradictions, distills recurring patterns, and builds an entity graph over time.
|
|
32
|
-
- **Compound** —
|
|
28
|
+
- **Compound** — Later sessions can start with more context because memory accumulates.
|
|
33
29
|
- **Route** — Optionally keep team, client, or environment-specific memory in separate Qdrant destinations from one install. See [separate memory stores](#optional-separate-memory-stores).
|
|
34
30
|
|
|
35
31
|
Subtypes keep recall precise without making setup harder:
|
|
36
32
|
|
|
37
|
-
- **Engineering** — codebase maps, architecture decisions, infra topology, access patterns, operational procedures, troubleshooting gotchas, and
|
|
33
|
+
- **Engineering** — codebase maps, architecture decisions, infra topology, access patterns, operational procedures, troubleshooting gotchas, conventions, preferences, person/ownership context, working agreements, and durable activity events.
|
|
38
34
|
- **Product** — domain rules, product decisions, requirements, user workflows, roadmap items, success metrics, and market insights.
|
|
39
|
-
- **Human** — preferences, person profiles, ownership notes, working agreements, and activity events.
|
|
40
35
|
- **System** — session indexes, episodes, workstreams, and feedback signals.
|
|
41
36
|
|
|
42
37
|
---
|
|
43
38
|
|
|
39
|
+
## Supported integrations
|
|
40
|
+
|
|
41
|
+
bikky has two integration surfaces: MCP tool access for agents and optional background transcript capture. Tool access is broader than transcript capture.
|
|
42
|
+
|
|
43
|
+
### Coding agents and MCP clients
|
|
44
|
+
|
|
45
|
+
| Client or agent | MCP tool access | `bikky setup` registration | Background transcript capture |
|
|
46
|
+
| --- | --- | --- | --- |
|
|
47
|
+
| GitHub Copilot | Supported | Supported via `~/.copilot/mcp-config.json` | Supported from `~/.copilot/session-state` |
|
|
48
|
+
| Claude Code | Supported | Supported via the `claude` CLI or `~/.claude.json` fallback | Supported from `~/.claude/projects` |
|
|
49
|
+
| Cursor and other stdio MCP clients | Standard MCP server is available via `npx -y bikky mcp` | Not auto-configured today | No built-in watcher today |
|
|
50
|
+
|
|
51
|
+
If your client can launch a stdio MCP server, it can use bikky's memory tools after manual configuration. bikky does not currently ship Cursor-specific setup or transcript parsing. Automatic transcript ingestion is implemented for GitHub Copilot and Claude Code.
|
|
52
|
+
|
|
53
|
+
### Storage and model providers
|
|
54
|
+
|
|
55
|
+
| Component | Supported today | Notes |
|
|
56
|
+
| --- | --- | --- |
|
|
57
|
+
| Vector store | Qdrant | Local Docker, Qdrant Cloud, or self-hosted Qdrant. Qdrant is required. |
|
|
58
|
+
| `embedding.provider` | `ollama`, `openai`, `bedrock`, `portkey` | Used to embed memories for semantic search. |
|
|
59
|
+
| `llm.provider` | `ollama`, `openai`, `bedrock`, `portkey` | Used for extraction, curation, distillation, and relation inference. |
|
|
60
|
+
|
|
61
|
+
Portkey support means bikky talks to Portkey as the configured gateway; upstream model availability, routing, and fallbacks are controlled by your Portkey configuration. Providers not listed above are not built in today, but the provider registry is designed to make additions small and reviewable.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
44
65
|
## Quick start
|
|
45
66
|
|
|
46
|
-
This is the fastest path to a working memory store: Qdrant runs locally, while hosted embeddings and LLM calls
|
|
67
|
+
This is the fastest path to a working memory store: Qdrant runs locally, while hosted embeddings and LLM calls handle extraction and recall without running local models.
|
|
47
68
|
|
|
48
69
|
```bash
|
|
49
70
|
# 1. Pull and run Qdrant (vector store)
|
|
@@ -60,7 +81,7 @@ cat > ~/.bikky/config.json <<'JSON'
|
|
|
60
81
|
"embedding": {
|
|
61
82
|
"provider": "openai",
|
|
62
83
|
"model": "text-embedding-3-small",
|
|
63
|
-
"dimensions":
|
|
84
|
+
"dimensions": 1024,
|
|
64
85
|
"api_key": "sk-..."
|
|
65
86
|
},
|
|
66
87
|
"llm": {
|
|
@@ -73,19 +94,19 @@ JSON
|
|
|
73
94
|
# qdrant_api_key is optional; leave it empty or omit it for local Qdrant.
|
|
74
95
|
# Prefer env vars? Omit api_key above and set OPENAI_API_KEY instead.
|
|
75
96
|
|
|
76
|
-
# 3. Register bikky with
|
|
77
|
-
bikky setup # writes MCP config for Copilot + Claude Code, then starts the daemon
|
|
97
|
+
# 3. Register bikky with supported clients and start the background service
|
|
98
|
+
bikky setup # writes MCP config for GitHub Copilot + Claude Code, then starts the daemon
|
|
78
99
|
```
|
|
79
100
|
|
|
80
101
|
`npm install -g bikky` runs a best-effort postinstall setup hook for convenience. It never fails the install, and you should still run `bikky setup` after writing your config to make setup explicit and repeatable.
|
|
81
102
|
|
|
82
|
-
Restart your editor. The memory tools appear automatically in
|
|
103
|
+
Restart your editor. The memory tools appear automatically in GitHub Copilot and Claude Code; configure other stdio MCP clients manually with `npx -y bikky mcp`.
|
|
83
104
|
|
|
84
105
|
```bash
|
|
85
106
|
bikky status # confirms Qdrant, embeddings, daemon, and UI health
|
|
86
107
|
```
|
|
87
108
|
|
|
88
|
-
|
|
109
|
+
At this point, you can continue with local Qdrant or move the vector store to Qdrant Cloud later for a shared team setup.
|
|
89
110
|
|
|
90
111
|
For other deployment shapes — fully hosted, 100% local, or hosted Qdrant with local models — see [Setup options](#setup-options).
|
|
91
112
|
|
|
@@ -105,18 +126,18 @@ bikky supports four common setup shapes. Pick based on where you want Qdrant to
|
|
|
105
126
|
| **LLM** | One provider | Portkey · OpenAI · Ollama · Bedrock |
|
|
106
127
|
| **Docker** *(optional)* | Only if you run Qdrant locally | Docker Desktop, OrbStack, colima, etc. |
|
|
107
128
|
|
|
108
|
-
Both `embedding.provider` and `llm.provider` accept the same values: `ollama`, `openai`, `bedrock`, or `portkey`.
|
|
129
|
+
Both `embedding.provider` and `llm.provider` accept the same values: `ollama`, `openai`, `bedrock`, or `portkey`. Portkey can be used as a hosted gateway when you want one configured provider in bikky and upstream routing/fallbacks managed outside bikky. The documented examples use **1024-dimensional embeddings** because that size works across the built-in provider examples. Some providers expose larger native dimensions (for example OpenAI `text-embedding-3-small` can return 1536), but using 1024 keeps the documented setup portable without rebuilding every collection.
|
|
109
130
|
|
|
110
131
|
> ⚠️ **Qdrant Cloud free tier does not include automatic backups.** Deleted collections cannot be recovered. If your memory data is valuable, use a paid Qdrant Cloud plan (which includes daily backups), run Qdrant locally with your own backup strategy, or periodically export snapshots via the [Qdrant snapshots API](https://qdrant.tech/documentation/concepts/snapshots/).
|
|
111
132
|
|
|
112
133
|
### Choose a setup
|
|
113
134
|
|
|
114
|
-
| Setup |
|
|
115
|
-
| -------------------------------- | -------------------------------------------------------------- |
|
|
116
|
-
| **Fully hosted** |
|
|
117
|
-
| **Local Qdrant + hosted models** |
|
|
118
|
-
| **Local and free** |
|
|
119
|
-
| **Hosted Qdrant + local Ollama** |
|
|
135
|
+
| Setup | Use when | Config |
|
|
136
|
+
| -------------------------------- | -------------------------------------------------------------- | ----------------------------------------------------------------------- |
|
|
137
|
+
| **Fully hosted** | Teams want managed vector storage and hosted models | [Fully hosted config][fully-hosted-config] |
|
|
138
|
+
| **Local Qdrant + hosted models** | You want local vector storage with hosted extraction/embedding | [Hosted models config][hosted-models-config] |
|
|
139
|
+
| **Local and free** | You are evaluating locally and can accept local-model quality | [Local config guide][local-config] |
|
|
140
|
+
| **Hosted Qdrant + local Ollama** | You want shared vectors while keeping model calls local | [Hosted Qdrant + local models][hosted-qdrant-local-models-config] |
|
|
120
141
|
|
|
121
142
|
### Configuration basics
|
|
122
143
|
|
|
@@ -196,9 +217,9 @@ bikky-ui # opens http://localhost:1422
|
|
|
196
217
|
<p align="center"><i>Dashboard — memory stats, category breakdown, and recent facts at a glance</i></p>
|
|
197
218
|
|
|
198
219
|
<p align="center">
|
|
199
|
-
<img src="https://raw.githubusercontent.com/bikky-dev/bikky/main/docs/screenshots/memory.png" alt="Memory browser — search, filter, and browse
|
|
220
|
+
<img src="https://raw.githubusercontent.com/bikky-dev/bikky/main/docs/screenshots/memory.png" alt="Memory browser — search, filter, and browse current user-facing memories" width="720" />
|
|
200
221
|
</p>
|
|
201
|
-
<p align="center"><i>Memory browser — search, filter by category
|
|
222
|
+
<p align="center"><i>Memory browser — search, filter by category, subtype, entity, usefulness, date, and sort order</i></p>
|
|
202
223
|
|
|
203
224
|
<p align="center">
|
|
204
225
|
<img src="https://raw.githubusercontent.com/bikky-dev/bikky/main/docs/screenshots/graph.png" alt="Entity graph — interactive visualization of entity relationships" width="720" />
|
|
@@ -207,11 +228,15 @@ bikky-ui # opens http://localhost:1422
|
|
|
207
228
|
|
|
208
229
|
The UI reads from your existing `~/.bikky/config.json` (or `BIKKY_HOME/config.json`) — no extra configuration required.
|
|
209
230
|
|
|
231
|
+
By default, the dashboard, memory list, and search results show current user-facing memories only. Internal telemetry, system lifecycle summaries (`session_index`, `episode`, `workstream`), entity sidecars, and superseded archive records are hidden from the main views so counts match what you normally mean by "memories." Diagnostic API queries can still request those records explicitly, including superseded records with `include_superseded=true`.
|
|
232
|
+
|
|
233
|
+
Memory cards and detail pages also surface provenance from canonical `origin` metadata: the configured user, origin surface/operation, agent, last operation, repo, branch, workstream, task, session, and episode when present. Older records that only have legacy `source`, `actor_id`, or `metadata.actor_label` still display useful fallback labels.
|
|
234
|
+
|
|
210
235
|
## CLI
|
|
211
236
|
|
|
212
237
|
```bash
|
|
213
238
|
bikky mcp # start MCP server (stdio) — used by editors
|
|
214
|
-
bikky setup # install MCP configs for Copilot + Claude Code, then start the daemon
|
|
239
|
+
bikky setup # install MCP configs for GitHub Copilot + Claude Code, then start the daemon
|
|
215
240
|
bikky start # alias for setup
|
|
216
241
|
bikky stop # stop the background daemon
|
|
217
242
|
bikky daemon # run the daemon in the foreground
|
package/dist/config.d.ts
CHANGED
|
@@ -61,6 +61,10 @@ export interface DaemonConfig {
|
|
|
61
61
|
entity_typing_enabled: boolean;
|
|
62
62
|
entity_typing_interval_sec: number;
|
|
63
63
|
entity_typing_max_entities_per_run: number;
|
|
64
|
+
memory_quality_rollups_enabled: boolean;
|
|
65
|
+
memory_quality_rollups_interval_sec: number;
|
|
66
|
+
memory_quality_rollups_low_confidence_threshold: number;
|
|
67
|
+
memory_quality_rollups_max_scopes_per_run: number;
|
|
64
68
|
staleness_threshold_days: number;
|
|
65
69
|
}
|
|
66
70
|
export interface QdrantClientConfig {
|
|
@@ -168,7 +172,7 @@ export interface ConfigFileDiagnostics {
|
|
|
168
172
|
issues: ConfigIssue[];
|
|
169
173
|
}
|
|
170
174
|
declare const DEFAULTS: BikkyConfig;
|
|
171
|
-
export declare const CONFIG_ENV_KEYS: readonly ["QDRANT_URL", "QDRANT_API_KEY", "BIKKY_COLLECTION", "EMBEDDING_PROVIDER", "EMBEDDING_MODEL", "EMBEDDING_BASE_URL", "EMBEDDING_DIMENSIONS", "OPENAI_API_KEY", "PORTKEY_API_KEY", "LLM_PROVIDER", "LLM_MODEL", "LLM_BASE_URL", "LLM_FALLBACK_PROVIDER", "AWS_PROFILE", "AWS_BEDROCK_REGION", "AWS_REGION", "QDRANT_TIMEOUT_MS", "QDRANT_RETRIES", "QDRANT_RETRY_BASE_DELAY_MS", "BIKKY_EMBEDDING_TIMEOUT_MS", "BIKKY_EMBEDDING_RETRIES", "BIKKY_EMBEDDING_RETRY_BASE_DELAY_MS", "BIKKY_LLM_TIMEOUT_MS", "BIKKY_LLM_RETRIES", "BIKKY_LLM_RETRY_BASE_DELAY_MS", "BIKKY_DAEMON_RELATION_INFERENCE_ENABLED", "BIKKY_DAEMON_RELATION_INFERENCE_INTERVAL_SEC", "BIKKY_DAEMON_RELATION_INFERENCE_MAX_PAIRS_PER_RUN", "BIKKY_DAEMON_ENTITY_TYPING_ENABLED", "BIKKY_DAEMON_ENTITY_TYPING_INTERVAL_SEC", "BIKKY_DAEMON_ENTITY_TYPING_MAX_ENTITIES_PER_RUN", "BIKKY_USER_ID", "BIKKY_USER_NAME", "BIKKY_AGENT_ID", "BIKKY_AGENT_NAME", "BIKKY_ACTOR_ID", "BIKKY_ACTOR_LABEL"];
|
|
175
|
+
export declare const CONFIG_ENV_KEYS: readonly ["QDRANT_URL", "QDRANT_API_KEY", "BIKKY_COLLECTION", "EMBEDDING_PROVIDER", "EMBEDDING_MODEL", "EMBEDDING_BASE_URL", "EMBEDDING_DIMENSIONS", "OPENAI_API_KEY", "PORTKEY_API_KEY", "LLM_PROVIDER", "LLM_MODEL", "LLM_BASE_URL", "LLM_FALLBACK_PROVIDER", "AWS_PROFILE", "AWS_BEDROCK_REGION", "AWS_REGION", "QDRANT_TIMEOUT_MS", "QDRANT_RETRIES", "QDRANT_RETRY_BASE_DELAY_MS", "BIKKY_EMBEDDING_TIMEOUT_MS", "BIKKY_EMBEDDING_RETRIES", "BIKKY_EMBEDDING_RETRY_BASE_DELAY_MS", "BIKKY_LLM_TIMEOUT_MS", "BIKKY_LLM_RETRIES", "BIKKY_LLM_RETRY_BASE_DELAY_MS", "BIKKY_DAEMON_RELATION_INFERENCE_ENABLED", "BIKKY_DAEMON_RELATION_INFERENCE_INTERVAL_SEC", "BIKKY_DAEMON_RELATION_INFERENCE_MAX_PAIRS_PER_RUN", "BIKKY_DAEMON_ENTITY_TYPING_ENABLED", "BIKKY_DAEMON_ENTITY_TYPING_INTERVAL_SEC", "BIKKY_DAEMON_ENTITY_TYPING_MAX_ENTITIES_PER_RUN", "BIKKY_DAEMON_MEMORY_QUALITY_ROLLUPS_ENABLED", "BIKKY_DAEMON_MEMORY_QUALITY_ROLLUPS_INTERVAL_SEC", "BIKKY_DAEMON_MEMORY_QUALITY_ROLLUPS_LOW_CONFIDENCE_THRESHOLD", "BIKKY_DAEMON_MEMORY_QUALITY_ROLLUPS_MAX_SCOPES_PER_RUN", "BIKKY_USER_ID", "BIKKY_USER_NAME", "BIKKY_AGENT_ID", "BIKKY_AGENT_NAME", "BIKKY_ACTOR_ID", "BIKKY_ACTOR_LABEL"];
|
|
172
176
|
export declare function validateConfigObject(raw: unknown): ConfigIssue[];
|
|
173
177
|
export declare function inspectConfigFile(configPath?: string): ConfigFileDiagnostics;
|
|
174
178
|
export declare function getActiveConfigEnvOverrides(env?: NodeJS.ProcessEnv): string[];
|
package/dist/config.js
CHANGED
|
@@ -92,6 +92,10 @@ const DEFAULTS = {
|
|
|
92
92
|
entity_typing_enabled: true,
|
|
93
93
|
entity_typing_interval_sec: 900,
|
|
94
94
|
entity_typing_max_entities_per_run: 5,
|
|
95
|
+
memory_quality_rollups_enabled: true,
|
|
96
|
+
memory_quality_rollups_interval_sec: 3600,
|
|
97
|
+
memory_quality_rollups_low_confidence_threshold: 0.6,
|
|
98
|
+
memory_quality_rollups_max_scopes_per_run: 100,
|
|
95
99
|
staleness_threshold_days: 30,
|
|
96
100
|
},
|
|
97
101
|
identity: {
|
|
@@ -142,6 +146,10 @@ export const CONFIG_ENV_KEYS = [
|
|
|
142
146
|
"BIKKY_DAEMON_ENTITY_TYPING_ENABLED",
|
|
143
147
|
"BIKKY_DAEMON_ENTITY_TYPING_INTERVAL_SEC",
|
|
144
148
|
"BIKKY_DAEMON_ENTITY_TYPING_MAX_ENTITIES_PER_RUN",
|
|
149
|
+
"BIKKY_DAEMON_MEMORY_QUALITY_ROLLUPS_ENABLED",
|
|
150
|
+
"BIKKY_DAEMON_MEMORY_QUALITY_ROLLUPS_INTERVAL_SEC",
|
|
151
|
+
"BIKKY_DAEMON_MEMORY_QUALITY_ROLLUPS_LOW_CONFIDENCE_THRESHOLD",
|
|
152
|
+
"BIKKY_DAEMON_MEMORY_QUALITY_ROLLUPS_MAX_SCOPES_PER_RUN",
|
|
145
153
|
"BIKKY_USER_ID",
|
|
146
154
|
"BIKKY_USER_NAME",
|
|
147
155
|
"BIKKY_AGENT_ID",
|
|
@@ -190,6 +198,10 @@ const daemonConfigFileSchema = z.object({
|
|
|
190
198
|
entity_typing_enabled: z.boolean().optional(),
|
|
191
199
|
entity_typing_interval_sec: nonNegativeInt.optional(),
|
|
192
200
|
entity_typing_max_entities_per_run: nonNegativeInt.optional(),
|
|
201
|
+
memory_quality_rollups_enabled: z.boolean().optional(),
|
|
202
|
+
memory_quality_rollups_interval_sec: nonNegativeInt.optional(),
|
|
203
|
+
memory_quality_rollups_low_confidence_threshold: z.number().min(0).max(1).optional(),
|
|
204
|
+
memory_quality_rollups_max_scopes_per_run: positiveInt.optional(),
|
|
193
205
|
staleness_threshold_days: nonNegativeInt.optional(),
|
|
194
206
|
}).passthrough();
|
|
195
207
|
const watcherConfigFileSchema = z.object({
|
|
@@ -696,6 +708,22 @@ export function loadConfig() {
|
|
|
696
708
|
const entityTypingMax = positiveInt(process.env.BIKKY_DAEMON_ENTITY_TYPING_MAX_ENTITIES_PER_RUN);
|
|
697
709
|
if (entityTypingMax !== null)
|
|
698
710
|
config.daemon.entity_typing_max_entities_per_run = entityTypingMax;
|
|
711
|
+
const qualityRollupsEnabled = booleanEnv(process.env.BIKKY_DAEMON_MEMORY_QUALITY_ROLLUPS_ENABLED);
|
|
712
|
+
if (qualityRollupsEnabled !== null)
|
|
713
|
+
config.daemon.memory_quality_rollups_enabled = qualityRollupsEnabled;
|
|
714
|
+
const qualityRollupsInterval = positiveInt(process.env.BIKKY_DAEMON_MEMORY_QUALITY_ROLLUPS_INTERVAL_SEC);
|
|
715
|
+
if (qualityRollupsInterval !== null)
|
|
716
|
+
config.daemon.memory_quality_rollups_interval_sec = qualityRollupsInterval;
|
|
717
|
+
const qualityRollupsThresholdRaw = process.env.BIKKY_DAEMON_MEMORY_QUALITY_ROLLUPS_LOW_CONFIDENCE_THRESHOLD;
|
|
718
|
+
if (qualityRollupsThresholdRaw) {
|
|
719
|
+
const threshold = Number.parseFloat(qualityRollupsThresholdRaw);
|
|
720
|
+
if (Number.isFinite(threshold) && threshold >= 0 && threshold <= 1) {
|
|
721
|
+
config.daemon.memory_quality_rollups_low_confidence_threshold = threshold;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
const qualityRollupsMaxScopes = positiveInt(process.env.BIKKY_DAEMON_MEMORY_QUALITY_ROLLUPS_MAX_SCOPES_PER_RUN);
|
|
725
|
+
if (qualityRollupsMaxScopes !== null)
|
|
726
|
+
config.daemon.memory_quality_rollups_max_scopes_per_run = qualityRollupsMaxScopes;
|
|
699
727
|
if (process.env.BIKKY_USER_ID)
|
|
700
728
|
config.identity.user_id = process.env.BIKKY_USER_ID;
|
|
701
729
|
if (process.env.BIKKY_USER_NAME)
|
|
@@ -360,7 +360,6 @@ const formatHealthReport = (report) => {
|
|
|
360
360
|
const CATEGORY_TO_HEADING = {
|
|
361
361
|
engineering: "Engineering",
|
|
362
362
|
product: "Product",
|
|
363
|
-
human: "Human",
|
|
364
363
|
system: "System",
|
|
365
364
|
// Legacy stored categories remain readable before any data migration.
|
|
366
365
|
codebase: "Engineering",
|
|
@@ -371,9 +370,10 @@ const CATEGORY_TO_HEADING = {
|
|
|
371
370
|
projects: "System",
|
|
372
371
|
observation: "Engineering",
|
|
373
372
|
observations: "Engineering",
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
373
|
+
human: "Engineering",
|
|
374
|
+
preferences: "Engineering",
|
|
375
|
+
people: "Engineering",
|
|
376
|
+
team: "Engineering",
|
|
377
377
|
};
|
|
378
378
|
const generateMemoryBrief = async (_config) => {
|
|
379
379
|
if (!qdrant.isReady())
|
|
@@ -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;
|
|
@@ -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));
|
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.js
CHANGED
|
@@ -15,6 +15,7 @@ import { buildResolver } from "../routing.js";
|
|
|
15
15
|
import { DEFAULT_DOMAIN, QDRANT_INDEXES, categoryForMemorySubtype, layerForMemorySubtype, normalizeCategory, normalizeDomain, normalizeKind, validateMemorySubtype, } from "../mcp/taxonomy.js";
|
|
16
16
|
import { combineRedactions, redactStorageText, } from "../privacy/redaction.js";
|
|
17
17
|
import { buildOperationOrigin } from "../provenance/origin.js";
|
|
18
|
+
import { buildMemoryRoutingInput, mergeRoutingInputs } from "../routing-context.js";
|
|
18
19
|
// ---------------------------------------------------------------------------
|
|
19
20
|
// State
|
|
20
21
|
// ---------------------------------------------------------------------------
|
|
@@ -56,10 +57,8 @@ const pathForDestination = (urlPath, destination) => {
|
|
|
56
57
|
return urlPath;
|
|
57
58
|
return urlPath.replace(/^\/collections\/[^/]+/, `/collections/${destination.collection}`);
|
|
58
59
|
};
|
|
59
|
-
const routingInputForFact = (fact, normalizedContent, normalizedEntities, extraMetadata = {}) =>
|
|
60
|
-
|
|
61
|
-
entities: normalizedEntities,
|
|
62
|
-
metadata: {
|
|
60
|
+
const routingInputForFact = (fact, normalizedContent, normalizedEntities, extraMetadata = {}) => {
|
|
61
|
+
const metadata = {
|
|
63
62
|
...(fact.metadata ?? {}),
|
|
64
63
|
...extraMetadata,
|
|
65
64
|
category: fact.category,
|
|
@@ -75,8 +74,31 @@ const routingInputForFact = (fact, normalizedContent, normalizedEntities, extraM
|
|
|
75
74
|
...(fact.repo ? { repo: fact.repo } : {}),
|
|
76
75
|
...(fact.branch ? { branch: fact.branch } : {}),
|
|
77
76
|
...(fact.surface ? { surface: fact.surface } : {}),
|
|
78
|
-
|
|
79
|
-
})
|
|
77
|
+
...(fact.issue_id ? { issue_id: fact.issue_id } : {}),
|
|
78
|
+
...(fact.pr_id ? { pr_id: fact.pr_id } : {}),
|
|
79
|
+
...(fact.source_event_ids ? { source_event_ids: fact.source_event_ids } : {}),
|
|
80
|
+
...(fact.source_fact_ids ? { source_fact_ids: fact.source_fact_ids } : {}),
|
|
81
|
+
...(fact.source_episode_ids ? { source_episode_ids: fact.source_episode_ids } : {}),
|
|
82
|
+
...(fact.prompt_version ? { prompt_version: fact.prompt_version } : {}),
|
|
83
|
+
...(fact.capture_policy_version ? { capture_policy_version: fact.capture_policy_version } : {}),
|
|
84
|
+
...(fact.review_status ? { review_status: fact.review_status } : {}),
|
|
85
|
+
...(fact.volatility ? { volatility: fact.volatility } : {}),
|
|
86
|
+
...(fact.valid_from ? { valid_from: fact.valid_from } : {}),
|
|
87
|
+
...(fact.expires_at ? { expires_at: fact.expires_at } : {}),
|
|
88
|
+
...(fact.confidence_reason ? { confidence_reason: fact.confidence_reason } : {}),
|
|
89
|
+
...(fact.relation ? {
|
|
90
|
+
from_entity: fact.relation.from,
|
|
91
|
+
relation_type: fact.relation.type,
|
|
92
|
+
to_entity: fact.relation.to,
|
|
93
|
+
} : {}),
|
|
94
|
+
};
|
|
95
|
+
return buildMemoryRoutingInput({
|
|
96
|
+
content: normalizedContent,
|
|
97
|
+
entities: normalizedEntities,
|
|
98
|
+
metadata,
|
|
99
|
+
extraContent: [fact.origin, fact.last_operation_origin, fact.relation],
|
|
100
|
+
});
|
|
101
|
+
};
|
|
80
102
|
// ---------------------------------------------------------------------------
|
|
81
103
|
// Init — reads credentials from loadConfig()
|
|
82
104
|
// ---------------------------------------------------------------------------
|
|
@@ -373,12 +395,12 @@ const storeFact = async (fact, routeInput) => {
|
|
|
373
395
|
if (redaction.redacted) {
|
|
374
396
|
payload.redaction = redaction;
|
|
375
397
|
}
|
|
376
|
-
const destination = resolveDestination(
|
|
398
|
+
const destination = resolveDestination(mergeRoutingInputs(routingInputForFact(fact, redactedContent.text, payload.entities, {
|
|
377
399
|
category: normalizedCategory,
|
|
378
400
|
domain: normalizedDomain,
|
|
379
401
|
kind: normalizedKind,
|
|
380
402
|
...(normalizedSubtype ? { memory_subtype: normalizedSubtype } : {}),
|
|
381
|
-
}));
|
|
403
|
+
}), routeInput));
|
|
382
404
|
const vector = await embed(redactedContent.text);
|
|
383
405
|
await qdrantRequest("PUT", `/collections/${destination.collection}/points`, {
|
|
384
406
|
points: [{ id, vector, payload }],
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Backend aggregation for memory quality telemetry.
|
|
3
|
+
*/
|
|
4
|
+
import type { BikkyConfig, Destination } from "../config.js";
|
|
5
|
+
import type { FactPayload } from "../mcp/types.js";
|
|
6
|
+
import type { LogFn } from "./qdrant.js";
|
|
7
|
+
export type QualityScopeType = "destination" | "repo" | "workstream_key" | "task_key" | "entity" | "origin_user" | "origin_agent";
|
|
8
|
+
export interface QualityPoint {
|
|
9
|
+
id: string;
|
|
10
|
+
destination: string;
|
|
11
|
+
payload: Partial<FactPayload>;
|
|
12
|
+
}
|
|
13
|
+
export interface QualityRollup {
|
|
14
|
+
destination: string;
|
|
15
|
+
scope_type: QualityScopeType;
|
|
16
|
+
scope_value: string;
|
|
17
|
+
active_fact_count: number;
|
|
18
|
+
recall_count: number;
|
|
19
|
+
useful_count: number;
|
|
20
|
+
misleading_count: number;
|
|
21
|
+
wrong_count: number;
|
|
22
|
+
stale_count: number;
|
|
23
|
+
low_confidence_count: number;
|
|
24
|
+
generated_at: string;
|
|
25
|
+
source_fact_ids: string[];
|
|
26
|
+
source_event_ids: string[];
|
|
27
|
+
}
|
|
28
|
+
export interface QualityRollupResult {
|
|
29
|
+
destinations_seen: number;
|
|
30
|
+
facts_seen: number;
|
|
31
|
+
events_seen: number;
|
|
32
|
+
rollups_upserted: number;
|
|
33
|
+
scopes_capped: boolean;
|
|
34
|
+
}
|
|
35
|
+
export interface QualityRollupDeps {
|
|
36
|
+
isReady: () => boolean;
|
|
37
|
+
activeDestinations: () => Destination[];
|
|
38
|
+
qdrantRequest: (method: string, urlPath: string, body?: unknown, destinationRef?: Destination | string | null) => Promise<Record<string, unknown>>;
|
|
39
|
+
embed: (text: string) => Promise<number[]>;
|
|
40
|
+
}
|
|
41
|
+
export declare const setLogger: (fn: LogFn) => void;
|
|
42
|
+
export declare const buildQualityRollups: (input: {
|
|
43
|
+
facts: QualityPoint[];
|
|
44
|
+
events?: QualityPoint[];
|
|
45
|
+
generatedAt?: Date;
|
|
46
|
+
staleThresholdDays?: number;
|
|
47
|
+
lowConfidenceThreshold?: number;
|
|
48
|
+
}) => QualityRollup[];
|
|
49
|
+
export declare const aggregateMemoryQualitySignals: (config: BikkyConfig, deps?: QualityRollupDeps) => Promise<QualityRollupResult>;
|
|
50
|
+
export declare const tick: (config: BikkyConfig, deps?: QualityRollupDeps) => Promise<void>;
|
|
51
|
+
//# sourceMappingURL=quality-rollups.d.ts.map
|