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.
@@ -0,0 +1,55 @@
1
+ const appendRoutingText = (parts, value) => {
2
+ if (value === null || value === undefined)
3
+ return;
4
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
5
+ const text = String(value).trim();
6
+ if (text)
7
+ parts.push(text);
8
+ return;
9
+ }
10
+ if (Array.isArray(value)) {
11
+ for (const item of value)
12
+ appendRoutingText(parts, item);
13
+ return;
14
+ }
15
+ if (typeof value === "object") {
16
+ for (const [key, nested] of Object.entries(value)) {
17
+ appendRoutingText(parts, key);
18
+ appendRoutingText(parts, nested);
19
+ }
20
+ }
21
+ };
22
+ export const routingText = (values) => {
23
+ const parts = [];
24
+ for (const value of values)
25
+ appendRoutingText(parts, value);
26
+ return Array.from(new Set(parts)).join("\n");
27
+ };
28
+ export const buildMemoryRoutingInput = (input) => {
29
+ const metadata = {
30
+ ...(input.metadata ?? {}),
31
+ ...(input.context ?? {}),
32
+ };
33
+ return {
34
+ destination: input.destination,
35
+ cwd: input.cwd,
36
+ content: routingText([input.content, input.entities, metadata, ...(input.extraContent ?? [])]),
37
+ entities: input.entities,
38
+ metadata,
39
+ };
40
+ };
41
+ export const mergeRoutingInputs = (base, override) => {
42
+ if (!override)
43
+ return base;
44
+ return {
45
+ destination: override.destination ?? base.destination,
46
+ cwd: override.cwd ?? base.cwd,
47
+ content: routingText([base.content, override.content]),
48
+ entities: Array.from(new Set([...(base.entities ?? []), ...(override.entities ?? [])])),
49
+ metadata: {
50
+ ...(base.metadata ?? {}),
51
+ ...(override.metadata ?? {}),
52
+ },
53
+ };
54
+ };
55
+ //# sourceMappingURL=routing-context.js.map
package/dist/status.d.ts CHANGED
@@ -58,6 +58,7 @@ export interface MaintenanceStatusReport {
58
58
  state_path: string;
59
59
  relation_inference: MaintenanceJobStatus;
60
60
  entity_typing: MaintenanceJobStatus;
61
+ memory_quality_rollups: MaintenanceJobStatus;
61
62
  }
62
63
  export interface UiStatusReport {
63
64
  status: DiagnosticState;
package/dist/status.js CHANGED
@@ -235,13 +235,18 @@ function collectMaintenanceStatus() {
235
235
  last_summary: job.last_summary,
236
236
  };
237
237
  };
238
- const summaries = [state.jobs.relation_inference.last_summary, state.jobs.entity_typing.last_summary];
238
+ const summaries = [
239
+ state.jobs.relation_inference.last_summary,
240
+ state.jobs.entity_typing.last_summary,
241
+ state.jobs.memory_quality_rollups.last_summary,
242
+ ];
239
243
  const hasError = summaries.some((summary) => summary?.status === "error");
240
244
  return {
241
245
  status: hasError ? "warn" : "ok",
242
246
  state_path: MAINTENANCE_STATE_PATH,
243
247
  relation_inference: jobStatus("relation_inference"),
244
248
  entity_typing: jobStatus("entity_typing"),
249
+ memory_quality_rollups: jobStatus("memory_quality_rollups"),
245
250
  };
246
251
  }
247
252
  async function collectUiStatus(opts) {
@@ -368,6 +373,7 @@ export function formatStatusReport(report) {
368
373
  lines.push(`Daemon: ${icon(report.daemon.status)} ${report.daemon.running ? `running (PID ${report.daemon.pid})` : "stopped"}`);
369
374
  lines.push(`Maint: ${icon(report.maintenance.status)} ${maintenanceJobSummary("relations", report.maintenance.relation_inference)}`);
370
375
  lines.push(` ${maintenanceJobSummary("entity typing", report.maintenance.entity_typing)}`);
376
+ lines.push(` ${maintenanceJobSummary("quality rollups", report.maintenance.memory_quality_rollups)}`);
371
377
  lines.push(`UI: ${icon(report.ui.status)} ${report.ui.checked ? report.ui.url : "not checked"}${report.ui.error ? ` — ${report.ui.error}` : ""}`);
372
378
  lines.push(`MCP: ${icon(report.mcp.status)} ${report.mcp.message}`);
373
379
  return lines.join("\n");
@@ -8,6 +8,7 @@ Use this path when you want memory shared across machines, but you still want em
8
8
  - A Qdrant API key.
9
9
  - Ollama installed locally.
10
10
  - The default embedding model pulled with `ollama pull qwen3-embedding:0.6b`.
11
+ - The default LLM model pulled with `ollama pull qwen2.5:7b`.
11
12
 
12
13
  ## Config
13
14
 
@@ -9,6 +9,7 @@ This setup is best for private/free testing rather than long-term team use. Extr
9
9
  - Qdrant running locally, usually with Docker.
10
10
  - Ollama installed locally.
11
11
  - The default embedding model pulled with `ollama pull qwen3-embedding:0.6b`.
12
+ - The default LLM model pulled with `ollama pull qwen2.5:7b`.
12
13
 
13
14
  ## Config
14
15
 
@@ -11,7 +11,7 @@ cat > ~/.bikky/config.json <<'JSON'
11
11
  "embedding": {
12
12
  "provider": "openai",
13
13
  "model": "text-embedding-3-small",
14
- "dimensions": 1536,
14
+ "dimensions": 1024,
15
15
  "api_key": "sk-..."
16
16
  },
17
17
  "llm": {
@@ -24,7 +24,7 @@ JSON
24
24
  bikky status
25
25
  ```
26
26
 
27
- Config lives at `~/.bikky/config.json`, or at `BIKKY_HOME/config.json` when `BIKKY_HOME` is set. Environment variables override the config file.
27
+ Config lives at `~/.bikky/config.json`, or at `BIKKY_HOME/config.json` when `BIKKY_HOME` is set. Environment variables can supply or override most scalar settings. Provider API key env vars such as `OPENAI_API_KEY` and `PORTKEY_API_KEY` are fallback values when the matching `api_key` field is omitted from config; remove the config value when you want key rotation through the environment.
28
28
 
29
29
  ## Origin identity
30
30
 
@@ -64,7 +64,7 @@ Best for performance and teams. Qdrant Cloud stores vectors, and hosted embeddin
64
64
  "embedding": {
65
65
  "provider": "openai",
66
66
  "model": "text-embedding-3-small",
67
- "dimensions": 1536,
67
+ "dimensions": 1024,
68
68
  "api_key": "sk-..."
69
69
  },
70
70
  "llm": {
@@ -88,7 +88,7 @@ Best for local vector storage with hosted extraction and embedding quality.
88
88
  "embedding": {
89
89
  "provider": "openai",
90
90
  "model": "text-embedding-3-small",
91
- "dimensions": 1536,
91
+ "dimensions": 1024,
92
92
  "api_key": "sk-..."
93
93
  },
94
94
  "llm": {
@@ -99,7 +99,7 @@ Best for local vector storage with hosted extraction and embedding quality.
99
99
  }
100
100
  ```
101
101
 
102
- `qdrant_api_key` is optional. Leave it empty or omit it for local or unauthenticated self-hosted Qdrant. Prefer env vars for hosted model auth? Omit `api_key` above and set `OPENAI_API_KEY` instead.
102
+ `qdrant_api_key` is optional. Leave it empty or omit it for local or unauthenticated self-hosted Qdrant. Prefer env vars for hosted model auth? Omit `api_key` above and set `OPENAI_API_KEY` or `PORTKEY_API_KEY` for the selected provider instead.
103
103
 
104
104
  ### Local and free
105
105
 
@@ -148,15 +148,17 @@ export QDRANT_API_KEY="..." # only needed for Qdrant Cloud or authenticated sel
148
148
 
149
149
  Useful basics:
150
150
 
151
- | Env var | Config field | Notes |
152
- | ---------------- | ---------------- | --------------------------------------------------------------------------- |
153
- | `QDRANT_URL` | `qdrant_url` | Required unless set in config |
154
- | `QDRANT_API_KEY` | `qdrant_api_key` | Optional for local/unauthenticated Qdrant; usually needed for Qdrant Cloud |
155
- | `BIKKY_HOME` | | Moves the config/log/state directory from `~/.bikky` |
156
- | `BIKKY_USER_ID` | `identity.user_id` | Explicit human user id for origin provisioning |
157
- | `BIKKY_USER_NAME` | `identity.user_name` | Human-readable human user name for origin provisioning |
158
- | `BIKKY_AGENT_ID` | | Optional local automated-agent id stored in `origin.agent` |
159
- | `BIKKY_AGENT_NAME` | | Optional local automated-agent label stored in `origin.agent` |
151
+ | Env var | Config field | Notes |
152
+ | ------------------ | --------------------- | --------------------------------------------------------------------------------------- |
153
+ | `QDRANT_URL` | `qdrant_url` | Required unless set in config |
154
+ | `QDRANT_API_KEY` | `qdrant_api_key` | Optional for local/unauthenticated Qdrant; usually needed for Qdrant Cloud |
155
+ | `OPENAI_API_KEY` | `embedding.api_key` / `llm.api_key` | Fallback key for `openai` when `api_key` is omitted from config |
156
+ | `PORTKEY_API_KEY` | `embedding.api_key` / `llm.api_key` | Fallback key for `portkey` when `api_key` is omitted from config |
157
+ | `BIKKY_HOME` | | Moves the config/log/state directory from `~/.bikky` |
158
+ | `BIKKY_USER_ID` | `identity.user_id` | Explicit human user id for origin provisioning |
159
+ | `BIKKY_USER_NAME` | `identity.user_name` | Human-readable human user name for origin provisioning |
160
+ | `BIKKY_AGENT_ID` | — | Optional local automated-agent id stored in `origin.agent` |
161
+ | `BIKKY_AGENT_NAME` | — | Optional local automated-agent label stored in `origin.agent` |
160
162
 
161
163
  ## Provider options
162
164
 
@@ -167,7 +169,7 @@ Use these exact values in `embedding.provider` and `llm.provider`. Both fields a
167
169
  | `ollama` | Yes | Yes | Local and free defaults | None |
168
170
  | `openai` | Yes | Yes | Simple hosted models | `OPENAI_API_KEY` or `api_key` |
169
171
  | `bedrock` | Yes | Yes | AWS-managed models | AWS credentials or IAM role |
170
- | `portkey` | Yes | Yes | Gateway/routing over other providers | Portkey API key |
172
+ | `portkey` | Yes | Yes | Gateway/routing over other providers | `PORTKEY_API_KEY` or `api_key` |
171
173
 
172
174
  ## Advanced configuration
173
175
 
@@ -193,7 +195,7 @@ These sections are optional references for custom providers, tuning, scoping, an
193
195
  | `embedding.model` | `EMBEDDING_MODEL` | `qwen3-embedding:0.6b` | Embedding model name |
194
196
  | `embedding.dimensions` | `EMBEDDING_DIMENSIONS` | `1024` | Must match the selected model output |
195
197
  | `embedding.base_url` | `EMBEDDING_BASE_URL` | `http://localhost:11434` | Used by local or OpenAI-compatible providers |
196
- | `embedding.api_key` | `OPENAI_API_KEY` | — | Provider API key; can also be set in config |
198
+ | `embedding.api_key` | `OPENAI_API_KEY` / `PORTKEY_API_KEY` | — | Provider API key; env vars are fallback when omitted from config |
197
199
 
198
200
  Common model dimensions:
199
201
 
@@ -205,7 +207,7 @@ Common model dimensions:
205
207
  | `openai` | `text-embedding-3-large` | `3072` |
206
208
  | `bedrock` | `amazon.titan-embed-text-v2:0` | `1024` |
207
209
 
208
- If you change the embedding model, make sure `embedding.dimensions` matches the model output.
210
+ bikky's examples use `1024` because that is the portable default across supported providers. OpenAI 3-series embedding models can return their larger native dimensions above, but they also support shorter outputs; if you change `embedding.dimensions`, make sure the selected model and every Qdrant collection use the same dimension.
209
211
 
210
212
  #### LLM
211
213
 
@@ -216,7 +218,7 @@ The LLM is used by background maintenance features. Ollama is the default.
216
218
  | `llm.provider` | `LLM_PROVIDER` | `ollama` | One of `ollama`, `openai`, `bedrock`, `portkey` |
217
219
  | `llm.model` | `LLM_MODEL` | `qwen2.5:7b` | LLM model name |
218
220
  | `llm.base_url` | `LLM_BASE_URL` | `http://localhost:11434` | Used by local or OpenAI-compatible providers |
219
- | `llm.api_key` | `OPENAI_API_KEY` | — | Provider API key; can also be set in config |
221
+ | `llm.api_key` | `OPENAI_API_KEY` / `PORTKEY_API_KEY` | — | Provider API key; env vars are fallback when omitted from config |
220
222
  | `llm.extra.region` | `AWS_BEDROCK_REGION` / `AWS_REGION` | `us-east-1` | AWS Bedrock region |
221
223
 
222
224
  #### Timeouts and retries
@@ -243,7 +245,7 @@ Retries use jittered exponential backoff for transient errors, rate limits, and
243
245
  "embedding": {
244
246
  "provider": "portkey",
245
247
  "model": "@openai/text-embedding-3-small",
246
- "dimensions": 1536,
248
+ "dimensions": 1024,
247
249
  "api_key": "pk-..."
248
250
  },
249
251
  "llm": {
@@ -282,7 +284,7 @@ Most users only need one Qdrant destination. Use `destinations[]` when you want
282
284
 
283
285
  Each destination has its own Qdrant credentials and collection. Add `description` when you have more than one destination; MCP tools expose those descriptions so LLM clients can pick the right search scope.
284
286
 
285
- Writes still target one destination. A destination can include a `match` block with JavaScript `RegExp` strings for `cwd`, `entity`, `content`, or `metadata`. Destinations are evaluated in array order; the first destination with any matching pattern wins. If no pattern matches, bikky uses the destination marked `default: true`, or the first destination.
287
+ Writes still target one destination. A destination can include a `match` block with JavaScript `RegExp` strings for `cwd`, `entity`, `content`, or `metadata`. For memory writes, `content` matching uses a flattened routing view of the full memory context: stored content, entities, repo, branch, task/workstream keys, metadata, origin, relation fields, and other stored payload fields. Destinations are evaluated in array order; the first destination with any matching pattern wins. If no pattern matches, bikky uses the destination marked `default: true`, or the first destination.
286
288
 
287
289
  Read tools (`memory_recall`, `memory_entity`, and `memory_relations`) can search one destination, the routed destination, or multiple destinations. Configure `default_search_scope` to control the default read behavior:
288
290
 
@@ -298,7 +300,7 @@ MCP clients can override this per call with `search_scope`. The value accepts `"
298
300
  "embedding": {
299
301
  "provider": "openai",
300
302
  "model": "text-embedding-3-small",
301
- "dimensions": 1536
303
+ "dimensions": 1024
302
304
  },
303
305
  "llm": {
304
306
  "provider": "openai",
@@ -351,6 +353,7 @@ MCP clients can override this per call with `search_scope`. The value accepts `"
351
353
  Matching details:
352
354
 
353
355
  - `match.cwd`, `match.entity`, and `match.content` are lists of JavaScript `RegExp` strings.
356
+ - On memory writes, `match.content` sees the full flattened memory context, so a pattern can match values such as `repo`, `branch`, `task_key`, origin fields, metadata values, or relation endpoints even when the visible memory text does not contain that term.
354
357
  - `match.metadata` maps metadata keys to lists of JavaScript `RegExp` strings matched against that key's value.
355
358
  - Matching uses OR logic across fields and within each list; any matching pattern selects the destination.
356
359
  - Put the most specific destinations first because first match wins.
@@ -379,6 +382,10 @@ You normally do not need to tune these. `bikky setup` starts the daemon and regi
379
382
  | `daemon.consolidation_enabled` | `true` | Consolidate summaries into durable patterns |
380
383
  | `daemon.relation_inference_enabled` | `true` | Infer entity relationships |
381
384
  | `daemon.entity_typing_enabled` | `true` | Classify entities for UI/graph filtering |
385
+ | `daemon.memory_quality_rollups_enabled` | `true` | Aggregate recall, feedback, stale, and confidence quality signals |
386
+ | `daemon.memory_quality_rollups_interval_sec` | `3600` | Seconds between memory quality rollup runs |
387
+ | `daemon.memory_quality_rollups_low_confidence_threshold` | `0.6` | Effective confidence below this value is counted as low-confidence |
388
+ | `daemon.memory_quality_rollups_max_scopes_per_run` | `100` | Maximum rollup scopes written per destination per run |
382
389
  | `daemon.staleness_threshold_days` | `30` | Days before a fact is flagged as stale |
383
390
 
384
391
  #### Watcher settings
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bikky",
3
- "version": "0.4.3",
3
+ "version": "0.4.4",
4
4
  "description": "Shared memory for AI coding sessions — MCP server + background daemon",
5
5
  "type": "module",
6
6
  "license": "AGPL-3.0-or-later",