bikky 0.4.2 → 0.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +64 -37
- package/dist/config.d.ts +15 -1
- package/dist/config.js +116 -20
- package/dist/daemon/capture-policy.d.ts +0 -1
- package/dist/daemon/capture-policy.js +0 -2
- package/dist/daemon/consolidation.d.ts +2 -1
- package/dist/daemon/consolidation.js +32 -15
- package/dist/daemon/entity-typing.js +10 -0
- package/dist/daemon/episode-summary.d.ts +4 -0
- package/dist/daemon/episode-summary.js +39 -8
- package/dist/daemon/extraction.d.ts +2 -2
- package/dist/daemon/extraction.js +65 -22
- package/dist/daemon/loop.js +8 -0
- package/dist/daemon/maintenance-state.d.ts +1 -1
- package/dist/daemon/maintenance-state.js +2 -0
- package/dist/daemon/qdrant.d.ts +32 -10
- package/dist/daemon/qdrant.js +199 -60
- package/dist/daemon/quality-rollups.d.ts +51 -0
- package/dist/daemon/quality-rollups.js +378 -0
- package/dist/daemon/relations.d.ts +3 -3
- package/dist/daemon/relations.js +28 -16
- package/dist/daemon/session-index.d.ts +5 -0
- package/dist/daemon/session-index.js +36 -9
- package/dist/daemon/session-summary.d.ts +3 -0
- package/dist/daemon/session-summary.js +48 -15
- package/dist/daemon/staleness.js +3 -3
- package/dist/daemon/transcript-sources.js +3 -2
- package/dist/daemon/watcher.js +2 -0
- package/dist/daemon/workstream-summary.d.ts +4 -0
- package/dist/daemon/workstream-summary.js +58 -16
- package/dist/install.d.ts +11 -0
- package/dist/install.js +38 -0
- package/dist/lifecycle.js +7 -5
- package/dist/llm/embedding/index.js +2 -1
- package/dist/llm/embedding/providers/openai.js +8 -2
- package/dist/llm/embedding/providers/portkey.js +9 -2
- package/dist/llm/inference/index.js +2 -1
- package/dist/llm/util.d.ts +12 -0
- package/dist/llm/util.js +18 -0
- package/dist/mcp/helpers.d.ts +8 -0
- package/dist/mcp/helpers.js +36 -3
- package/dist/mcp/taxonomy.d.ts +9 -13
- package/dist/mcp/taxonomy.js +59 -42
- package/dist/mcp/tools.js +351 -83
- package/dist/mcp/types.d.ts +35 -0
- package/dist/package-verifier.d.ts +19 -0
- package/dist/package-verifier.js +83 -0
- package/dist/prompts/brief.d.ts +2 -2
- package/dist/prompts/brief.js +0 -1
- package/dist/prompts/extraction.js +9 -11
- package/dist/provenance/origin.d.ts +57 -0
- package/dist/provenance/origin.js +254 -0
- package/dist/routing-context.d.ts +16 -0
- package/dist/routing-context.js +55 -0
- package/dist/status.d.ts +1 -0
- package/dist/status.js +7 -1
- package/docs/config/fully-hosted.md +33 -13
- package/docs/config/hosted-models.md +33 -13
- package/docs/config/hosted-qdrant-local-models.md +1 -0
- package/docs/config/local.md +1 -0
- package/docs/configuration.md +42 -17
- package/package.json +2 -2
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
|
|
21
14
|
|
|
22
|
-
|
|
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.
|
|
23
17
|
|
|
24
|
-
|
|
18
|
+
---
|
|
19
|
+
|
|
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
|
|
|
@@ -101,22 +122,22 @@ bikky supports four common setup shapes. Pick based on where you want Qdrant to
|
|
|
101
122
|
| ----------------------- | ------------------------------ | ---------------------------------------------------------------------------------------- |
|
|
102
123
|
| **Node.js** | ≥ 20 | `nvm install 20` or your package manager |
|
|
103
124
|
| **Vector store** | Qdrant | Local Docker · [Qdrant Cloud](https://cloud.qdrant.io) · Self-hosted |
|
|
104
|
-
| **Embeddings** | One provider | OpenAI · Ollama · Bedrock
|
|
105
|
-
| **LLM** | One provider | OpenAI · Ollama · Bedrock
|
|
125
|
+
| **Embeddings** | One provider | Portkey · OpenAI · Ollama · Bedrock |
|
|
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
|
|
|
@@ -128,6 +149,8 @@ Pick the setup guide above for the copy-paste config. All setup shapes use the s
|
|
|
128
149
|
|
|
129
150
|
Config lives at `~/.bikky/config.json`, or at `BIKKY_HOME/config.json` when `BIKKY_HOME` is set. You can keep credentials out of the file with environment variables such as `QDRANT_URL`, `QDRANT_API_KEY`, and provider API keys.
|
|
130
151
|
|
|
152
|
+
`bikky setup` also provisions `identity.user_id` / `identity.user_name` when they are missing. New memory writes store canonical `origin` metadata with the configured human user, the acting agent or daemon/UI surface, the interface, and the operation. MCP clients cannot supply or spoof `origin.user`; if config, env, Git, and shell identity detection all fail, bikky falls back to the local hostname.
|
|
153
|
+
|
|
131
154
|
For hosted models, custom providers, multiple profiles, or advanced tuning, use the full configuration guide.
|
|
132
155
|
|
|
133
156
|
> 📖 **Full configuration guide:** [docs/configuration.md][configuration-guide]
|
|
@@ -194,9 +217,9 @@ bikky-ui # opens http://localhost:1422
|
|
|
194
217
|
<p align="center"><i>Dashboard — memory stats, category breakdown, and recent facts at a glance</i></p>
|
|
195
218
|
|
|
196
219
|
<p align="center">
|
|
197
|
-
<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" />
|
|
198
221
|
</p>
|
|
199
|
-
<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>
|
|
200
223
|
|
|
201
224
|
<p align="center">
|
|
202
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" />
|
|
@@ -205,11 +228,15 @@ bikky-ui # opens http://localhost:1422
|
|
|
205
228
|
|
|
206
229
|
The UI reads from your existing `~/.bikky/config.json` (or `BIKKY_HOME/config.json`) — no extra configuration required.
|
|
207
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
|
+
|
|
208
235
|
## CLI
|
|
209
236
|
|
|
210
237
|
```bash
|
|
211
238
|
bikky mcp # start MCP server (stdio) — used by editors
|
|
212
|
-
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
|
|
213
240
|
bikky start # alias for setup
|
|
214
241
|
bikky stop # stop the background daemon
|
|
215
242
|
bikky daemon # run the daemon in the foreground
|
package/dist/config.d.ts
CHANGED
|
@@ -4,6 +4,12 @@
|
|
|
4
4
|
* Resolution order: defaults → ~/.bikky/config.json → env vars.
|
|
5
5
|
* Config directory: ~/.bikky/
|
|
6
6
|
*/
|
|
7
|
+
export declare function getBikkyDir(): string;
|
|
8
|
+
export declare function getConfigPath(): string;
|
|
9
|
+
export declare function getLogDir(): string;
|
|
10
|
+
export declare function getStateDir(): string;
|
|
11
|
+
export declare function getPidPath(): string;
|
|
12
|
+
export declare function getExtractionHealthPath(): string;
|
|
7
13
|
export declare const BIKKY_DIR: string;
|
|
8
14
|
export declare const CONFIG_PATH: string;
|
|
9
15
|
export declare const LOG_DIR: string;
|
|
@@ -55,6 +61,10 @@ export interface DaemonConfig {
|
|
|
55
61
|
entity_typing_enabled: boolean;
|
|
56
62
|
entity_typing_interval_sec: number;
|
|
57
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;
|
|
58
68
|
staleness_threshold_days: number;
|
|
59
69
|
}
|
|
60
70
|
export interface QdrantClientConfig {
|
|
@@ -63,7 +73,11 @@ export interface QdrantClientConfig {
|
|
|
63
73
|
retry_base_delay_ms: number;
|
|
64
74
|
}
|
|
65
75
|
export interface IdentityConfig {
|
|
76
|
+
user_id: string | null;
|
|
77
|
+
user_name: string | null;
|
|
78
|
+
/** @deprecated Use origin.user.id instead. */
|
|
66
79
|
actor_id: string | null;
|
|
80
|
+
/** @deprecated Use origin.user.name / origin.agent.name instead. */
|
|
67
81
|
actor_label: string | null;
|
|
68
82
|
}
|
|
69
83
|
export interface WatcherConfig {
|
|
@@ -158,7 +172,7 @@ export interface ConfigFileDiagnostics {
|
|
|
158
172
|
issues: ConfigIssue[];
|
|
159
173
|
}
|
|
160
174
|
declare const DEFAULTS: BikkyConfig;
|
|
161
|
-
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", "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_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"];
|
|
162
176
|
export declare function validateConfigObject(raw: unknown): ConfigIssue[];
|
|
163
177
|
export declare function inspectConfigFile(configPath?: string): ConfigFileDiagnostics;
|
|
164
178
|
export declare function getActiveConfigEnvOverrides(env?: NodeJS.ProcessEnv): string[];
|
package/dist/config.js
CHANGED
|
@@ -12,15 +12,41 @@ import { z } from "zod";
|
|
|
12
12
|
// Paths
|
|
13
13
|
// ---------------------------------------------------------------------------
|
|
14
14
|
// BIKKY_HOME env var lets tests (and advanced users) override the config dir
|
|
15
|
-
// without touching the real ~/.bikky/.
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
export
|
|
15
|
+
// without touching the real ~/.bikky/. The getter functions below re-read the
|
|
16
|
+
// env var on every call, so changing BIKKY_HOME at runtime (e.g. in a test
|
|
17
|
+
// setup hook) takes effect for all subsequent saveConfig()/loadConfig() calls.
|
|
18
|
+
//
|
|
19
|
+
// The legacy `BIKKY_DIR` / `CONFIG_PATH` / etc. exports are kept for
|
|
20
|
+
// backward-compatibility, but they capture the env state at module load
|
|
21
|
+
// time and should NOT be relied on for safe writes. Internal callers — and
|
|
22
|
+
// any test that wants sandboxing — should call the getter functions instead.
|
|
23
|
+
export function getBikkyDir() {
|
|
24
|
+
return process.env.BIKKY_HOME ?? path.join(os.homedir(), ".bikky");
|
|
25
|
+
}
|
|
26
|
+
export function getConfigPath() {
|
|
27
|
+
return path.join(getBikkyDir(), "config.json");
|
|
28
|
+
}
|
|
29
|
+
export function getLogDir() {
|
|
30
|
+
return path.join(getBikkyDir(), "logs");
|
|
31
|
+
}
|
|
32
|
+
export function getStateDir() {
|
|
33
|
+
return path.join(getBikkyDir(), "state");
|
|
34
|
+
}
|
|
35
|
+
export function getPidPath() {
|
|
36
|
+
return path.join(getStateDir(), "daemon.pid");
|
|
37
|
+
}
|
|
38
|
+
export function getExtractionHealthPath() {
|
|
39
|
+
return path.join(getStateDir(), "extraction-health.json");
|
|
40
|
+
}
|
|
41
|
+
// Legacy constant exports — captured at module load. Prefer the getter
|
|
42
|
+
// functions above when you need fresh values (e.g. inside tests, or after
|
|
43
|
+
// mutating BIKKY_HOME at runtime).
|
|
44
|
+
export const BIKKY_DIR = getBikkyDir();
|
|
45
|
+
export const CONFIG_PATH = getConfigPath();
|
|
46
|
+
export const LOG_DIR = getLogDir();
|
|
47
|
+
export const STATE_DIR = getStateDir();
|
|
48
|
+
export const PID_PATH = getPidPath();
|
|
49
|
+
export const EXTRACTION_HEALTH_PATH = getExtractionHealthPath();
|
|
24
50
|
// ---------------------------------------------------------------------------
|
|
25
51
|
// Defaults
|
|
26
52
|
// ---------------------------------------------------------------------------
|
|
@@ -66,9 +92,15 @@ const DEFAULTS = {
|
|
|
66
92
|
entity_typing_enabled: true,
|
|
67
93
|
entity_typing_interval_sec: 900,
|
|
68
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,
|
|
69
99
|
staleness_threshold_days: 30,
|
|
70
100
|
},
|
|
71
101
|
identity: {
|
|
102
|
+
user_id: null,
|
|
103
|
+
user_name: null,
|
|
72
104
|
actor_id: null,
|
|
73
105
|
actor_label: null,
|
|
74
106
|
},
|
|
@@ -91,6 +123,7 @@ export const CONFIG_ENV_KEYS = [
|
|
|
91
123
|
"EMBEDDING_BASE_URL",
|
|
92
124
|
"EMBEDDING_DIMENSIONS",
|
|
93
125
|
"OPENAI_API_KEY",
|
|
126
|
+
"PORTKEY_API_KEY",
|
|
94
127
|
"LLM_PROVIDER",
|
|
95
128
|
"LLM_MODEL",
|
|
96
129
|
"LLM_BASE_URL",
|
|
@@ -113,6 +146,14 @@ export const CONFIG_ENV_KEYS = [
|
|
|
113
146
|
"BIKKY_DAEMON_ENTITY_TYPING_ENABLED",
|
|
114
147
|
"BIKKY_DAEMON_ENTITY_TYPING_INTERVAL_SEC",
|
|
115
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",
|
|
153
|
+
"BIKKY_USER_ID",
|
|
154
|
+
"BIKKY_USER_NAME",
|
|
155
|
+
"BIKKY_AGENT_ID",
|
|
156
|
+
"BIKKY_AGENT_NAME",
|
|
116
157
|
"BIKKY_ACTOR_ID",
|
|
117
158
|
"BIKKY_ACTOR_LABEL",
|
|
118
159
|
];
|
|
@@ -122,7 +163,7 @@ const CONFIG_ENV_PREFIXES = [
|
|
|
122
163
|
];
|
|
123
164
|
const nonNegativeInt = z.number().int().nonnegative();
|
|
124
165
|
const positiveInt = z.number().int().positive();
|
|
125
|
-
const stringRecord = z.record(z.string());
|
|
166
|
+
const stringRecord = z.record(z.string(), z.string());
|
|
126
167
|
const embeddingConfigFileSchema = z.object({
|
|
127
168
|
provider: z.string().optional(),
|
|
128
169
|
model: z.string().optional(),
|
|
@@ -157,6 +198,10 @@ const daemonConfigFileSchema = z.object({
|
|
|
157
198
|
entity_typing_enabled: z.boolean().optional(),
|
|
158
199
|
entity_typing_interval_sec: nonNegativeInt.optional(),
|
|
159
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(),
|
|
160
205
|
staleness_threshold_days: nonNegativeInt.optional(),
|
|
161
206
|
}).passthrough();
|
|
162
207
|
const watcherConfigFileSchema = z.object({
|
|
@@ -175,6 +220,8 @@ const qdrantClientConfigFileSchema = z.object({
|
|
|
175
220
|
retry_base_delay_ms: nonNegativeInt.optional(),
|
|
176
221
|
}).passthrough();
|
|
177
222
|
const identityConfigFileSchema = z.object({
|
|
223
|
+
user_id: z.string().nullable().optional(),
|
|
224
|
+
user_name: z.string().nullable().optional(),
|
|
178
225
|
actor_id: z.string().nullable().optional(),
|
|
179
226
|
actor_label: z.string().nullable().optional(),
|
|
180
227
|
}).passthrough();
|
|
@@ -183,7 +230,7 @@ const destinationMatchSchema = z.object({
|
|
|
183
230
|
cwd: regexArrayField,
|
|
184
231
|
entity: regexArrayField,
|
|
185
232
|
content: regexArrayField,
|
|
186
|
-
metadata: z.record(z.array(z.string())).optional(),
|
|
233
|
+
metadata: z.record(z.string(), z.array(z.string())).optional(),
|
|
187
234
|
}).passthrough();
|
|
188
235
|
const destinationFileSchema = z.object({
|
|
189
236
|
name: z.string().min(1),
|
|
@@ -442,7 +489,7 @@ export function validateConfigObject(raw) {
|
|
|
442
489
|
validateUrlLike(llm.base_url, "llm.base_url", issues);
|
|
443
490
|
return issues;
|
|
444
491
|
}
|
|
445
|
-
export function inspectConfigFile(configPath =
|
|
492
|
+
export function inspectConfigFile(configPath = getConfigPath()) {
|
|
446
493
|
if (!fs.existsSync(configPath)) {
|
|
447
494
|
return { path: configPath, exists: false, parse_error: null, issues: [] };
|
|
448
495
|
}
|
|
@@ -490,21 +537,41 @@ export function loadConfig() {
|
|
|
490
537
|
if (_config)
|
|
491
538
|
return _config;
|
|
492
539
|
// Ensure dirs exist
|
|
493
|
-
fs.mkdirSync(
|
|
494
|
-
fs.mkdirSync(
|
|
495
|
-
fs.mkdirSync(
|
|
540
|
+
fs.mkdirSync(getBikkyDir(), { recursive: true });
|
|
541
|
+
fs.mkdirSync(getLogDir(), { recursive: true });
|
|
542
|
+
fs.mkdirSync(getStateDir(), { recursive: true });
|
|
496
543
|
// Start from defaults
|
|
497
544
|
let config = structuredClone(DEFAULTS);
|
|
498
545
|
// Merge config file
|
|
499
|
-
|
|
546
|
+
const configPath = getConfigPath();
|
|
547
|
+
let fileConfig = {};
|
|
548
|
+
if (fs.existsSync(configPath)) {
|
|
500
549
|
try {
|
|
501
|
-
|
|
550
|
+
fileConfig = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
502
551
|
config = deepMerge(config, fileConfig);
|
|
503
552
|
}
|
|
504
553
|
catch (e) {
|
|
505
|
-
console.error(`bikky: failed to parse ${
|
|
554
|
+
console.error(`bikky: failed to parse ${configPath}: ${e instanceof Error ? e.message : String(e)}`);
|
|
506
555
|
}
|
|
507
556
|
}
|
|
557
|
+
// Provider/base_url consistency (issue #131): the DEFAULTS.embedding.base_url
|
|
558
|
+
// is the ollama localhost URL, baked in for the default ollama provider. If
|
|
559
|
+
// the user picks a different provider (portkey/openai/bedrock) but doesn't
|
|
560
|
+
// set an explicit base_url, drop the inherited ollama URL so initEmbedding()
|
|
561
|
+
// can apply the provider's own default — otherwise we'd POST every embedding
|
|
562
|
+
// request to localhost:11434 and Ollama would reject the foreign model name.
|
|
563
|
+
const fileEmbedding = (fileConfig.embedding ?? {});
|
|
564
|
+
if (config.embedding.provider !== DEFAULTS.embedding.provider
|
|
565
|
+
&& typeof fileEmbedding.base_url !== "string"
|
|
566
|
+
&& !process.env.EMBEDDING_BASE_URL) {
|
|
567
|
+
config.embedding.base_url = "";
|
|
568
|
+
}
|
|
569
|
+
const fileLlm = (fileConfig.llm ?? {});
|
|
570
|
+
if (config.llm.provider !== DEFAULTS.llm.provider
|
|
571
|
+
&& typeof fileLlm.base_url !== "string"
|
|
572
|
+
&& !process.env.LLM_BASE_URL) {
|
|
573
|
+
config.llm.base_url = "";
|
|
574
|
+
}
|
|
508
575
|
// Env var overrides (highest priority)
|
|
509
576
|
if (process.env.QDRANT_URL)
|
|
510
577
|
config.qdrant_url = process.env.QDRANT_URL;
|
|
@@ -526,6 +593,12 @@ export function loadConfig() {
|
|
|
526
593
|
}
|
|
527
594
|
if (process.env.OPENAI_API_KEY)
|
|
528
595
|
config.embedding.api_key = process.env.OPENAI_API_KEY;
|
|
596
|
+
// Portkey users can supply their gateway key via PORTKEY_API_KEY without
|
|
597
|
+
// needing to repurpose OPENAI_API_KEY. Only applied when the embedding
|
|
598
|
+
// provider is Portkey, so non-Portkey setups remain untouched.
|
|
599
|
+
if (process.env.PORTKEY_API_KEY && config.embedding.provider === "portkey") {
|
|
600
|
+
config.embedding.api_key = process.env.PORTKEY_API_KEY;
|
|
601
|
+
}
|
|
529
602
|
// Generic provider-extras: BIKKY_EMBEDDING_EXTRA_<KEY>=value
|
|
530
603
|
config.embedding.extra = config.embedding.extra ?? {};
|
|
531
604
|
for (const [k, v] of Object.entries(process.env)) {
|
|
@@ -542,6 +615,9 @@ export function loadConfig() {
|
|
|
542
615
|
config.llm.base_url = process.env.LLM_BASE_URL;
|
|
543
616
|
if (process.env.OPENAI_API_KEY && !config.llm.api_key)
|
|
544
617
|
config.llm.api_key = process.env.OPENAI_API_KEY;
|
|
618
|
+
if (process.env.PORTKEY_API_KEY && config.llm.provider === "portkey" && !config.llm.api_key) {
|
|
619
|
+
config.llm.api_key = process.env.PORTKEY_API_KEY;
|
|
620
|
+
}
|
|
545
621
|
if (process.env.LLM_FALLBACK_PROVIDER)
|
|
546
622
|
config.llm.fallback_provider = process.env.LLM_FALLBACK_PROVIDER;
|
|
547
623
|
if (process.env.AWS_PROFILE)
|
|
@@ -632,6 +708,26 @@ export function loadConfig() {
|
|
|
632
708
|
const entityTypingMax = positiveInt(process.env.BIKKY_DAEMON_ENTITY_TYPING_MAX_ENTITIES_PER_RUN);
|
|
633
709
|
if (entityTypingMax !== null)
|
|
634
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;
|
|
727
|
+
if (process.env.BIKKY_USER_ID)
|
|
728
|
+
config.identity.user_id = process.env.BIKKY_USER_ID;
|
|
729
|
+
if (process.env.BIKKY_USER_NAME)
|
|
730
|
+
config.identity.user_name = process.env.BIKKY_USER_NAME;
|
|
635
731
|
if (process.env.BIKKY_ACTOR_ID)
|
|
636
732
|
config.identity.actor_id = process.env.BIKKY_ACTOR_ID;
|
|
637
733
|
if (process.env.BIKKY_ACTOR_LABEL)
|
|
@@ -679,8 +775,8 @@ export function getEffectiveDestinations(config = loadConfig()) {
|
|
|
679
775
|
}
|
|
680
776
|
/** Save config to disk (used by setup command). */
|
|
681
777
|
export function saveConfig(config) {
|
|
682
|
-
fs.mkdirSync(
|
|
683
|
-
fs.writeFileSync(
|
|
778
|
+
fs.mkdirSync(getBikkyDir(), { recursive: true });
|
|
779
|
+
fs.writeFileSync(getConfigPath(), JSON.stringify(config, null, 2) + "\n");
|
|
684
780
|
_config = config;
|
|
685
781
|
}
|
|
686
782
|
/** Reset cached config (for testing). */
|
|
@@ -86,7 +86,6 @@ export declare const CAPTURE_KIND_SUBTYPES: {
|
|
|
86
86
|
export declare const FACT_CATEGORY_TO_SUBTYPE: Record<Category, MemorySubtype>;
|
|
87
87
|
export declare const DEFAULT_CAPTURE_CONTEXT: {
|
|
88
88
|
readonly domain: "software_engineering" | "product_strategy" | "business_operations" | "research" | "personal_productivity";
|
|
89
|
-
readonly source: "system";
|
|
90
89
|
readonly reviewStatus: "candidate";
|
|
91
90
|
readonly volatility: "evolving";
|
|
92
91
|
};
|
|
@@ -105,12 +105,10 @@ export const CAPTURE_KIND_SUBTYPES = {
|
|
|
105
105
|
export const FACT_CATEGORY_TO_SUBTYPE = {
|
|
106
106
|
engineering: "codebase_map",
|
|
107
107
|
product: "domain_rule",
|
|
108
|
-
human: "preference",
|
|
109
108
|
system: "codebase_map",
|
|
110
109
|
};
|
|
111
110
|
export const DEFAULT_CAPTURE_CONTEXT = {
|
|
112
111
|
domain: DEFAULT_DOMAIN,
|
|
113
|
-
source: "system",
|
|
114
112
|
reviewStatus: "candidate",
|
|
115
113
|
// Default fallback volatility when the LLM does not self-judge. Storage path
|
|
116
114
|
// overrides this with the LLM's value (or the volatility verifier's
|
|
@@ -51,6 +51,7 @@ declare const detectContradiction: (fact: {
|
|
|
51
51
|
}, _config: BikkyConfig, telemetry?: {
|
|
52
52
|
sessionId?: string;
|
|
53
53
|
workstreamKey?: string;
|
|
54
|
+
destination?: string;
|
|
54
55
|
}) => Promise<ContradictionResult>;
|
|
55
56
|
/**
|
|
56
57
|
* Main consolidation tick — called from daemon tick loop.
|
|
@@ -58,6 +59,6 @@ declare const detectContradiction: (fact: {
|
|
|
58
59
|
*/
|
|
59
60
|
declare const tick: (config: BikkyConfig, opts?: ConsolidationTickOptions) => Promise<void>;
|
|
60
61
|
/** Reset state (for testing). */
|
|
61
|
-
declare const _reset: () => void;
|
|
62
|
+
declare const _reset: (tickCount?: number) => void;
|
|
62
63
|
export { detectContradiction, tick, setLogger, _reset, };
|
|
63
64
|
//# sourceMappingURL=consolidation.d.ts.map
|