bikky 0.4.1 → 0.4.3
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/CHANGELOG.md +6 -0
- package/CODE_OF_CONDUCT.md +1 -1
- package/CONTRIBUTING.md +1 -1
- package/README.md +23 -17
- package/SUPPORT.md +3 -2
- package/dist/config.d.ts +11 -1
- package/dist/config.js +88 -20
- package/dist/daemon/capture-policy.d.ts +0 -1
- package/dist/daemon/capture-policy.js +0 -1
- package/dist/daemon/consolidation.d.ts +2 -1
- package/dist/daemon/consolidation.js +28 -11
- 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 +1 -1
- package/dist/daemon/extraction.js +52 -17
- package/dist/daemon/qdrant.d.ts +32 -10
- package/dist/daemon/qdrant.js +177 -60
- package/dist/daemon/relations.d.ts +3 -3
- package/dist/daemon/relations.js +27 -15
- 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 +2 -2
- 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/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 +5 -0
- package/dist/mcp/helpers.js +27 -3
- package/dist/mcp/taxonomy.js +12 -1
- package/dist/mcp/tools.js +161 -57
- package/dist/mcp/types.d.ts +12 -0
- package/dist/package-verifier.d.ts +19 -0
- package/dist/package-verifier.js +83 -0
- package/dist/provenance/origin.d.ts +57 -0
- package/dist/provenance/origin.js +254 -0
- package/docs/config/fully-hosted.md +33 -13
- package/docs/config/hosted-models.md +33 -13
- package/docs/configuration.md +23 -5
- package/package.json +6 -2
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,12 @@ This project uses npm package versions for release tracking:
|
|
|
9
9
|
|
|
10
10
|
## Unreleased
|
|
11
11
|
|
|
12
|
+
## 0.4.2
|
|
13
|
+
|
|
14
|
+
- Republished the core package from current `main` so the npm package README uses GitHub documentation links instead of stale jsDelivr links.
|
|
15
|
+
|
|
16
|
+
## 0.4.1
|
|
17
|
+
|
|
12
18
|
- Public OSS readiness cleanup: package metadata, support docs, public maintainer ownership, package tarball hygiene, and privacy/transcript-capture documentation.
|
|
13
19
|
- Added package verification CI and a privacy-first quickstart for local storage/local model setups.
|
|
14
20
|
|
package/CODE_OF_CONDUCT.md
CHANGED
|
@@ -45,7 +45,7 @@ or harmful.
|
|
|
45
45
|
## Scope
|
|
46
46
|
|
|
47
47
|
This Code of Conduct applies within all community spaces (issues, PRs,
|
|
48
|
-
|
|
48
|
+
Discord/Slack if any), and also applies when an individual is
|
|
49
49
|
officially representing the community in public spaces.
|
|
50
50
|
|
|
51
51
|
## Enforcement
|
package/CONTRIBUTING.md
CHANGED
|
@@ -199,7 +199,7 @@ Configure a fallback chain via `llm.fallback_provider` in config (or
|
|
|
199
199
|
|
|
200
200
|
## License
|
|
201
201
|
|
|
202
|
-
By contributing, you agree that your contributions will be licensed under the project's [AGPL-3.0-or-later](LICENSE) license.
|
|
202
|
+
By contributing, you agree that your contributions will be licensed under the project's [AGPL-3.0-or-later](https://github.com/bikky-dev/bikky/blob/main/LICENSE) license.
|
|
203
203
|
|
|
204
204
|
## Code of conduct
|
|
205
205
|
|
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ bikky gives AI coding agents (GitHub Copilot, Claude Code, Cursor, and other MCP
|
|
|
10
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
11
|
|
|
12
12
|
<p align="center">
|
|
13
|
-
<img src="https://
|
|
13
|
+
<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
14
|
</p>
|
|
15
15
|
|
|
16
16
|
<p align="center"><i>Knowledge flows from every session into a store that curates itself over time — deduplicating, distilling, and decaying stale facts — so every future session starts smarter across the team.</i></p>
|
|
@@ -101,11 +101,11 @@ bikky supports four common setup shapes. Pick based on where you want Qdrant to
|
|
|
101
101
|
| ----------------------- | ------------------------------ | ---------------------------------------------------------------------------------------- |
|
|
102
102
|
| **Node.js** | ≥ 20 | `nvm install 20` or your package manager |
|
|
103
103
|
| **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
|
|
104
|
+
| **Embeddings** | One provider | Portkey · OpenAI · Ollama · Bedrock |
|
|
105
|
+
| **LLM** | One provider | Portkey · OpenAI · Ollama · Bedrock |
|
|
106
106
|
| **Docker** *(optional)* | Only if you run Qdrant locally | Docker Desktop, OrbStack, colima, etc. |
|
|
107
107
|
|
|
108
|
-
Both `embedding.provider` and `llm.provider` accept the same values: `ollama`, `openai`, `bedrock`, or `portkey`.
|
|
108
|
+
Both `embedding.provider` and `llm.provider` accept the same values: `ollama`, `openai`, `bedrock`, or `portkey`. **Portkey is the easiest cloud option** — one API key, any upstream provider, with built-in routing/fallbacks. Bikky's canonical embedding dimension is **1024**, portable across every modern provider.
|
|
109
109
|
|
|
110
110
|
> ⚠️ **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
111
|
|
|
@@ -128,6 +128,8 @@ Pick the setup guide above for the copy-paste config. All setup shapes use the s
|
|
|
128
128
|
|
|
129
129
|
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
130
|
|
|
131
|
+
`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.
|
|
132
|
+
|
|
131
133
|
For hosted models, custom providers, multiple profiles, or advanced tuning, use the full configuration guide.
|
|
132
134
|
|
|
133
135
|
> 📖 **Full configuration guide:** [docs/configuration.md][configuration-guide]
|
|
@@ -165,15 +167,15 @@ Most installs use one Qdrant destination. If you need clean separation later, re
|
|
|
165
167
|
|
|
166
168
|
That is enough for explicit selection in the UI and tools. Add routing rules only when you want automatic placement by cwd, entity, content, or metadata. Search tools can also use `search_scope: "all"` or a named/listed scope when context may span stores. Existing single-Qdrant configs continue to work.
|
|
167
169
|
|
|
168
|
-
> 📖 **Details:** [multi-destination configuration](docs/configuration.md#multi-destination-routing)
|
|
170
|
+
> 📖 **Details:** [multi-destination configuration](https://github.com/bikky-dev/bikky/blob/main/docs/configuration.md#multi-destination-routing)
|
|
169
171
|
|
|
170
|
-
[fully-hosted-config]: https://
|
|
171
|
-
[hosted-models-config]: https://
|
|
172
|
-
[local-config]: https://
|
|
173
|
-
[hosted-qdrant-local-models-config]: https://
|
|
174
|
-
[configuration-guide]: https://
|
|
175
|
-
[privacy-quickstart]: https://
|
|
176
|
-
[contributing]: https://
|
|
172
|
+
[fully-hosted-config]: https://github.com/bikky-dev/bikky/blob/main/docs/config/fully-hosted.md
|
|
173
|
+
[hosted-models-config]: https://github.com/bikky-dev/bikky/blob/main/docs/config/hosted-models.md
|
|
174
|
+
[local-config]: https://github.com/bikky-dev/bikky/blob/main/docs/config/local.md
|
|
175
|
+
[hosted-qdrant-local-models-config]: https://github.com/bikky-dev/bikky/blob/main/docs/config/hosted-qdrant-local-models.md
|
|
176
|
+
[configuration-guide]: https://github.com/bikky-dev/bikky/blob/main/docs/configuration.md
|
|
177
|
+
[privacy-quickstart]: https://github.com/bikky-dev/bikky/blob/main/docs/privacy-first.md
|
|
178
|
+
[contributing]: https://github.com/bikky-dev/bikky/blob/main/CONTRIBUTING.md
|
|
177
179
|
|
|
178
180
|
---
|
|
179
181
|
|
|
@@ -189,17 +191,17 @@ bikky-ui # opens http://localhost:1422
|
|
|
189
191
|
```
|
|
190
192
|
|
|
191
193
|
<p align="center">
|
|
192
|
-
<img src="docs/screenshots/dashboard.png" alt="Dashboard — overview stats, category breakdown, recent facts" width="720" />
|
|
194
|
+
<img src="https://raw.githubusercontent.com/bikky-dev/bikky/main/docs/screenshots/dashboard.png" alt="Dashboard — overview stats, category breakdown, recent facts" width="720" />
|
|
193
195
|
</p>
|
|
194
196
|
<p align="center"><i>Dashboard — memory stats, category breakdown, and recent facts at a glance</i></p>
|
|
195
197
|
|
|
196
198
|
<p align="center">
|
|
197
|
-
<img src="docs/screenshots/memory.png" alt="Memory browser — search, filter, and browse all stored facts" width="720" />
|
|
199
|
+
<img src="https://raw.githubusercontent.com/bikky-dev/bikky/main/docs/screenshots/memory.png" alt="Memory browser — search, filter, and browse all stored facts" width="720" />
|
|
198
200
|
</p>
|
|
199
|
-
<p align="center"><i>Memory browser — search, filter by category/kind/
|
|
201
|
+
<p align="center"><i>Memory browser — search, filter by category/kind/origin, and browse all stored facts</i></p>
|
|
200
202
|
|
|
201
203
|
<p align="center">
|
|
202
|
-
<img src="docs/screenshots/graph.png" alt="Entity graph — interactive visualization of entity relationships" width="720" />
|
|
204
|
+
<img src="https://raw.githubusercontent.com/bikky-dev/bikky/main/docs/screenshots/graph.png" alt="Entity graph — interactive visualization of entity relationships" width="720" />
|
|
203
205
|
</p>
|
|
204
206
|
<p align="center"><i>Entity graph — interactive visualization of how concepts, people, and services relate</i></p>
|
|
205
207
|
|
|
@@ -242,6 +244,10 @@ You can also set `daemon.extract_every_sec` to `0` to disable background extract
|
|
|
242
244
|
|
|
243
245
|
For a local-storage, local-model setup that minimizes what leaves your machine, see the [privacy-first quickstart][privacy-quickstart].
|
|
244
246
|
|
|
247
|
+
## Support and contact
|
|
248
|
+
|
|
249
|
+
For questions, bugs, and feature requests, please use [GitHub issues](https://github.com/bikky-dev/bikky/issues). For maintainer contact, reach Saber Zrelli on GitHub: [@zrelli-s](https://github.com/zrelli-s).
|
|
250
|
+
|
|
245
251
|
## License
|
|
246
252
|
|
|
247
|
-
AGPL-3.0 — see [LICENSE](LICENSE).
|
|
253
|
+
AGPL-3.0 — see [LICENSE](https://github.com/bikky-dev/bikky/blob/main/LICENSE).
|
package/SUPPORT.md
CHANGED
|
@@ -12,11 +12,12 @@ bikky status
|
|
|
12
12
|
|
|
13
13
|
Please redact API keys, access tokens, local file contents, and any private transcript data before posting.
|
|
14
14
|
|
|
15
|
+
For maintainer contact, reach Saber Zrelli on GitHub: [@zrelli-s](https://github.com/zrelli-s).
|
|
16
|
+
|
|
15
17
|
## Bugs
|
|
16
18
|
|
|
17
19
|
Use the bug report template and include a minimal reproduction when possible. If the issue involves setup, include whether you installed with `npm install -g bikky`, `npx bikky`, or a local checkout.
|
|
18
20
|
|
|
19
21
|
## Security reports
|
|
20
22
|
|
|
21
|
-
Do not open a public issue for vulnerabilities. Follow [SECURITY.md](SECURITY.md) and use GitHub private vulnerability reporting.
|
|
22
|
-
|
|
23
|
+
Do not open a public issue for vulnerabilities. Follow [SECURITY.md](https://github.com/bikky-dev/bikky/blob/main/SECURITY.md) and use GitHub private vulnerability reporting.
|
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;
|
|
@@ -63,7 +69,11 @@ export interface QdrantClientConfig {
|
|
|
63
69
|
retry_base_delay_ms: number;
|
|
64
70
|
}
|
|
65
71
|
export interface IdentityConfig {
|
|
72
|
+
user_id: string | null;
|
|
73
|
+
user_name: string | null;
|
|
74
|
+
/** @deprecated Use origin.user.id instead. */
|
|
66
75
|
actor_id: string | null;
|
|
76
|
+
/** @deprecated Use origin.user.name / origin.agent.name instead. */
|
|
67
77
|
actor_label: string | null;
|
|
68
78
|
}
|
|
69
79
|
export interface WatcherConfig {
|
|
@@ -158,7 +168,7 @@ export interface ConfigFileDiagnostics {
|
|
|
158
168
|
issues: ConfigIssue[];
|
|
159
169
|
}
|
|
160
170
|
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"];
|
|
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"];
|
|
162
172
|
export declare function validateConfigObject(raw: unknown): ConfigIssue[];
|
|
163
173
|
export declare function inspectConfigFile(configPath?: string): ConfigFileDiagnostics;
|
|
164
174
|
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
|
// ---------------------------------------------------------------------------
|
|
@@ -69,6 +95,8 @@ const DEFAULTS = {
|
|
|
69
95
|
staleness_threshold_days: 30,
|
|
70
96
|
},
|
|
71
97
|
identity: {
|
|
98
|
+
user_id: null,
|
|
99
|
+
user_name: null,
|
|
72
100
|
actor_id: null,
|
|
73
101
|
actor_label: null,
|
|
74
102
|
},
|
|
@@ -91,6 +119,7 @@ export const CONFIG_ENV_KEYS = [
|
|
|
91
119
|
"EMBEDDING_BASE_URL",
|
|
92
120
|
"EMBEDDING_DIMENSIONS",
|
|
93
121
|
"OPENAI_API_KEY",
|
|
122
|
+
"PORTKEY_API_KEY",
|
|
94
123
|
"LLM_PROVIDER",
|
|
95
124
|
"LLM_MODEL",
|
|
96
125
|
"LLM_BASE_URL",
|
|
@@ -113,6 +142,10 @@ export const CONFIG_ENV_KEYS = [
|
|
|
113
142
|
"BIKKY_DAEMON_ENTITY_TYPING_ENABLED",
|
|
114
143
|
"BIKKY_DAEMON_ENTITY_TYPING_INTERVAL_SEC",
|
|
115
144
|
"BIKKY_DAEMON_ENTITY_TYPING_MAX_ENTITIES_PER_RUN",
|
|
145
|
+
"BIKKY_USER_ID",
|
|
146
|
+
"BIKKY_USER_NAME",
|
|
147
|
+
"BIKKY_AGENT_ID",
|
|
148
|
+
"BIKKY_AGENT_NAME",
|
|
116
149
|
"BIKKY_ACTOR_ID",
|
|
117
150
|
"BIKKY_ACTOR_LABEL",
|
|
118
151
|
];
|
|
@@ -122,7 +155,7 @@ const CONFIG_ENV_PREFIXES = [
|
|
|
122
155
|
];
|
|
123
156
|
const nonNegativeInt = z.number().int().nonnegative();
|
|
124
157
|
const positiveInt = z.number().int().positive();
|
|
125
|
-
const stringRecord = z.record(z.string());
|
|
158
|
+
const stringRecord = z.record(z.string(), z.string());
|
|
126
159
|
const embeddingConfigFileSchema = z.object({
|
|
127
160
|
provider: z.string().optional(),
|
|
128
161
|
model: z.string().optional(),
|
|
@@ -175,6 +208,8 @@ const qdrantClientConfigFileSchema = z.object({
|
|
|
175
208
|
retry_base_delay_ms: nonNegativeInt.optional(),
|
|
176
209
|
}).passthrough();
|
|
177
210
|
const identityConfigFileSchema = z.object({
|
|
211
|
+
user_id: z.string().nullable().optional(),
|
|
212
|
+
user_name: z.string().nullable().optional(),
|
|
178
213
|
actor_id: z.string().nullable().optional(),
|
|
179
214
|
actor_label: z.string().nullable().optional(),
|
|
180
215
|
}).passthrough();
|
|
@@ -183,7 +218,7 @@ const destinationMatchSchema = z.object({
|
|
|
183
218
|
cwd: regexArrayField,
|
|
184
219
|
entity: regexArrayField,
|
|
185
220
|
content: regexArrayField,
|
|
186
|
-
metadata: z.record(z.array(z.string())).optional(),
|
|
221
|
+
metadata: z.record(z.string(), z.array(z.string())).optional(),
|
|
187
222
|
}).passthrough();
|
|
188
223
|
const destinationFileSchema = z.object({
|
|
189
224
|
name: z.string().min(1),
|
|
@@ -442,7 +477,7 @@ export function validateConfigObject(raw) {
|
|
|
442
477
|
validateUrlLike(llm.base_url, "llm.base_url", issues);
|
|
443
478
|
return issues;
|
|
444
479
|
}
|
|
445
|
-
export function inspectConfigFile(configPath =
|
|
480
|
+
export function inspectConfigFile(configPath = getConfigPath()) {
|
|
446
481
|
if (!fs.existsSync(configPath)) {
|
|
447
482
|
return { path: configPath, exists: false, parse_error: null, issues: [] };
|
|
448
483
|
}
|
|
@@ -490,21 +525,41 @@ export function loadConfig() {
|
|
|
490
525
|
if (_config)
|
|
491
526
|
return _config;
|
|
492
527
|
// Ensure dirs exist
|
|
493
|
-
fs.mkdirSync(
|
|
494
|
-
fs.mkdirSync(
|
|
495
|
-
fs.mkdirSync(
|
|
528
|
+
fs.mkdirSync(getBikkyDir(), { recursive: true });
|
|
529
|
+
fs.mkdirSync(getLogDir(), { recursive: true });
|
|
530
|
+
fs.mkdirSync(getStateDir(), { recursive: true });
|
|
496
531
|
// Start from defaults
|
|
497
532
|
let config = structuredClone(DEFAULTS);
|
|
498
533
|
// Merge config file
|
|
499
|
-
|
|
534
|
+
const configPath = getConfigPath();
|
|
535
|
+
let fileConfig = {};
|
|
536
|
+
if (fs.existsSync(configPath)) {
|
|
500
537
|
try {
|
|
501
|
-
|
|
538
|
+
fileConfig = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
502
539
|
config = deepMerge(config, fileConfig);
|
|
503
540
|
}
|
|
504
541
|
catch (e) {
|
|
505
|
-
console.error(`bikky: failed to parse ${
|
|
542
|
+
console.error(`bikky: failed to parse ${configPath}: ${e instanceof Error ? e.message : String(e)}`);
|
|
506
543
|
}
|
|
507
544
|
}
|
|
545
|
+
// Provider/base_url consistency (issue #131): the DEFAULTS.embedding.base_url
|
|
546
|
+
// is the ollama localhost URL, baked in for the default ollama provider. If
|
|
547
|
+
// the user picks a different provider (portkey/openai/bedrock) but doesn't
|
|
548
|
+
// set an explicit base_url, drop the inherited ollama URL so initEmbedding()
|
|
549
|
+
// can apply the provider's own default — otherwise we'd POST every embedding
|
|
550
|
+
// request to localhost:11434 and Ollama would reject the foreign model name.
|
|
551
|
+
const fileEmbedding = (fileConfig.embedding ?? {});
|
|
552
|
+
if (config.embedding.provider !== DEFAULTS.embedding.provider
|
|
553
|
+
&& typeof fileEmbedding.base_url !== "string"
|
|
554
|
+
&& !process.env.EMBEDDING_BASE_URL) {
|
|
555
|
+
config.embedding.base_url = "";
|
|
556
|
+
}
|
|
557
|
+
const fileLlm = (fileConfig.llm ?? {});
|
|
558
|
+
if (config.llm.provider !== DEFAULTS.llm.provider
|
|
559
|
+
&& typeof fileLlm.base_url !== "string"
|
|
560
|
+
&& !process.env.LLM_BASE_URL) {
|
|
561
|
+
config.llm.base_url = "";
|
|
562
|
+
}
|
|
508
563
|
// Env var overrides (highest priority)
|
|
509
564
|
if (process.env.QDRANT_URL)
|
|
510
565
|
config.qdrant_url = process.env.QDRANT_URL;
|
|
@@ -526,6 +581,12 @@ export function loadConfig() {
|
|
|
526
581
|
}
|
|
527
582
|
if (process.env.OPENAI_API_KEY)
|
|
528
583
|
config.embedding.api_key = process.env.OPENAI_API_KEY;
|
|
584
|
+
// Portkey users can supply their gateway key via PORTKEY_API_KEY without
|
|
585
|
+
// needing to repurpose OPENAI_API_KEY. Only applied when the embedding
|
|
586
|
+
// provider is Portkey, so non-Portkey setups remain untouched.
|
|
587
|
+
if (process.env.PORTKEY_API_KEY && config.embedding.provider === "portkey") {
|
|
588
|
+
config.embedding.api_key = process.env.PORTKEY_API_KEY;
|
|
589
|
+
}
|
|
529
590
|
// Generic provider-extras: BIKKY_EMBEDDING_EXTRA_<KEY>=value
|
|
530
591
|
config.embedding.extra = config.embedding.extra ?? {};
|
|
531
592
|
for (const [k, v] of Object.entries(process.env)) {
|
|
@@ -542,6 +603,9 @@ export function loadConfig() {
|
|
|
542
603
|
config.llm.base_url = process.env.LLM_BASE_URL;
|
|
543
604
|
if (process.env.OPENAI_API_KEY && !config.llm.api_key)
|
|
544
605
|
config.llm.api_key = process.env.OPENAI_API_KEY;
|
|
606
|
+
if (process.env.PORTKEY_API_KEY && config.llm.provider === "portkey" && !config.llm.api_key) {
|
|
607
|
+
config.llm.api_key = process.env.PORTKEY_API_KEY;
|
|
608
|
+
}
|
|
545
609
|
if (process.env.LLM_FALLBACK_PROVIDER)
|
|
546
610
|
config.llm.fallback_provider = process.env.LLM_FALLBACK_PROVIDER;
|
|
547
611
|
if (process.env.AWS_PROFILE)
|
|
@@ -632,6 +696,10 @@ export function loadConfig() {
|
|
|
632
696
|
const entityTypingMax = positiveInt(process.env.BIKKY_DAEMON_ENTITY_TYPING_MAX_ENTITIES_PER_RUN);
|
|
633
697
|
if (entityTypingMax !== null)
|
|
634
698
|
config.daemon.entity_typing_max_entities_per_run = entityTypingMax;
|
|
699
|
+
if (process.env.BIKKY_USER_ID)
|
|
700
|
+
config.identity.user_id = process.env.BIKKY_USER_ID;
|
|
701
|
+
if (process.env.BIKKY_USER_NAME)
|
|
702
|
+
config.identity.user_name = process.env.BIKKY_USER_NAME;
|
|
635
703
|
if (process.env.BIKKY_ACTOR_ID)
|
|
636
704
|
config.identity.actor_id = process.env.BIKKY_ACTOR_ID;
|
|
637
705
|
if (process.env.BIKKY_ACTOR_LABEL)
|
|
@@ -679,8 +747,8 @@ export function getEffectiveDestinations(config = loadConfig()) {
|
|
|
679
747
|
}
|
|
680
748
|
/** Save config to disk (used by setup command). */
|
|
681
749
|
export function saveConfig(config) {
|
|
682
|
-
fs.mkdirSync(
|
|
683
|
-
fs.writeFileSync(
|
|
750
|
+
fs.mkdirSync(getBikkyDir(), { recursive: true });
|
|
751
|
+
fs.writeFileSync(getConfigPath(), JSON.stringify(config, null, 2) + "\n");
|
|
684
752
|
_config = config;
|
|
685
753
|
}
|
|
686
754
|
/** 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
|
};
|
|
@@ -110,7 +110,6 @@ export const FACT_CATEGORY_TO_SUBTYPE = {
|
|
|
110
110
|
};
|
|
111
111
|
export const DEFAULT_CAPTURE_CONTEXT = {
|
|
112
112
|
domain: DEFAULT_DOMAIN,
|
|
113
|
-
source: "system",
|
|
114
113
|
reviewStatus: "candidate",
|
|
115
114
|
// Default fallback volatility when the LLM does not self-judge. Storage path
|
|
116
115
|
// 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
|
|
@@ -13,6 +13,7 @@ import { mkdirSync, writeFileSync } from "node:fs";
|
|
|
13
13
|
import { join, dirname } from "node:path";
|
|
14
14
|
import { createHash } from "node:crypto";
|
|
15
15
|
import * as qdrant from "./qdrant.js";
|
|
16
|
+
import { buildOperationOrigin } from "../provenance/origin.js";
|
|
16
17
|
import { chatCompletion } from "../llm/index.js";
|
|
17
18
|
import { categoryValues, normalizeCategory, normalizeDomain } from "../mcp/taxonomy.js";
|
|
18
19
|
import { distillPrompt, DISTILL_PROMPT_DESCRIPTOR, contradictionPrompt, briefPrompt, BRIEF_PROMPT_DESCRIPTOR, safeParseJson, } from "../prompts/index.js";
|
|
@@ -33,6 +34,8 @@ const autoDistill = async (_config, { minSummaries = 5 } = {}) => {
|
|
|
33
34
|
if (!qdrant.isReady())
|
|
34
35
|
return { distilled: false };
|
|
35
36
|
try {
|
|
37
|
+
const destination = qdrant.resolveDestination({}).name;
|
|
38
|
+
const collection = qdrant.collectionForDestination(destination);
|
|
36
39
|
// Find undistilled session summaries (support both legacy and new taxonomy)
|
|
37
40
|
const legacyFilter = {
|
|
38
41
|
must: [
|
|
@@ -47,12 +50,12 @@ const autoDistill = async (_config, { minSummaries = 5 } = {}) => {
|
|
|
47
50
|
],
|
|
48
51
|
};
|
|
49
52
|
const [legacyRes, newRes] = await Promise.all([
|
|
50
|
-
qdrant.qdrantRequest("POST", `/collections/${
|
|
53
|
+
qdrant.qdrantRequest("POST", `/collections/${collection}/points/scroll`, {
|
|
51
54
|
filter: legacyFilter, limit: 50, with_payload: true,
|
|
52
|
-
}),
|
|
53
|
-
qdrant.qdrantRequest("POST", `/collections/${
|
|
55
|
+
}, destination),
|
|
56
|
+
qdrant.qdrantRequest("POST", `/collections/${collection}/points/scroll`, {
|
|
54
57
|
filter: newFilter, limit: 50, with_payload: true,
|
|
55
|
-
}),
|
|
58
|
+
}, destination),
|
|
56
59
|
]);
|
|
57
60
|
// Deduplicate by ID
|
|
58
61
|
const seen = new Set();
|
|
@@ -107,7 +110,6 @@ const autoDistill = async (_config, { minSummaries = 5 } = {}) => {
|
|
|
107
110
|
domain: normalizeDomain(pattern.domain ?? "software_engineering"),
|
|
108
111
|
kind: "distilled",
|
|
109
112
|
entities: Array.isArray(pattern.entities) ? pattern.entities.map(e => String(e).toLowerCase()) : [],
|
|
110
|
-
source: "system",
|
|
111
113
|
confidence: 0.85,
|
|
112
114
|
importance: pattern.importance || 0.7,
|
|
113
115
|
content_hash: hash,
|
|
@@ -116,11 +118,25 @@ const autoDistill = async (_config, { minSummaries = 5 } = {}) => {
|
|
|
116
118
|
distilled_at: new Date().toISOString(),
|
|
117
119
|
distilled_by_prompt: promptStamp,
|
|
118
120
|
},
|
|
119
|
-
|
|
121
|
+
origin: buildOperationOrigin({
|
|
122
|
+
interface: "daemon",
|
|
123
|
+
action: "create",
|
|
124
|
+
subsystem: "consolidation",
|
|
125
|
+
metadata: {
|
|
126
|
+
distilled_from_count: batch.length,
|
|
127
|
+
prompt: promptStamp,
|
|
128
|
+
},
|
|
129
|
+
}),
|
|
130
|
+
}, { destination });
|
|
120
131
|
}
|
|
121
132
|
// Supersede the source summaries
|
|
122
133
|
for (const pt of batch) {
|
|
123
|
-
await qdrant.supersedeFact(pt.id, `distilled:${new Date().toISOString()}
|
|
134
|
+
await qdrant.supersedeFact(pt.id, `distilled:${new Date().toISOString()}`, destination, buildOperationOrigin({
|
|
135
|
+
interface: "daemon",
|
|
136
|
+
action: "supersede",
|
|
137
|
+
subsystem: "consolidation",
|
|
138
|
+
metadata: { prompt: promptStamp },
|
|
139
|
+
}));
|
|
124
140
|
}
|
|
125
141
|
logFn("INFO", `Auto-distill: consolidated ${batch.length} summaries into ${patterns.length} patterns`);
|
|
126
142
|
return { distilled: true, count: patterns.length };
|
|
@@ -143,12 +159,13 @@ const detectContradiction = async (fact, _config, telemetry) => {
|
|
|
143
159
|
try {
|
|
144
160
|
const vector = await qdrant.embed(fact.content);
|
|
145
161
|
// Search across all categories because contradictions can cross category lines.
|
|
146
|
-
const
|
|
162
|
+
const collection = qdrant.collectionForDestination(telemetry?.destination);
|
|
163
|
+
const results = await qdrant.qdrantRequest("POST", `/collections/${collection}/points/search`, {
|
|
147
164
|
vector,
|
|
148
165
|
filter: { must: [{ is_null: { key: "superseded_by" } }] },
|
|
149
166
|
limit: 5,
|
|
150
167
|
with_payload: true,
|
|
151
|
-
});
|
|
168
|
+
}, telemetry?.destination);
|
|
152
169
|
const candidates = (results.result || [])
|
|
153
170
|
.filter(r => r.score >= 0.75 && r.score < 0.92);
|
|
154
171
|
if (candidates.length === 0)
|
|
@@ -448,8 +465,8 @@ const tick = async (config, opts = {}) => {
|
|
|
448
465
|
}
|
|
449
466
|
};
|
|
450
467
|
/** Reset state (for testing). */
|
|
451
|
-
const _reset = () => {
|
|
452
|
-
consolidationTickCount =
|
|
468
|
+
const _reset = (tickCount = 0) => {
|
|
469
|
+
consolidationTickCount = tickCount;
|
|
453
470
|
};
|
|
454
471
|
export { detectContradiction, tick, setLogger, _reset, };
|
|
455
472
|
//# sourceMappingURL=consolidation.js.map
|
|
@@ -11,6 +11,7 @@ import { entityTypingPrompt, ENTITY_TYPING_PROMPT_DESCRIPTOR, safeParseJson } fr
|
|
|
11
11
|
import * as qdrant from "./qdrant.js";
|
|
12
12
|
import { isAttemptBackedOff, pruneRecentAttempts, readMaintenanceState, recordMaintenanceRun, shouldRunMaintenance, } from "./maintenance-state.js";
|
|
13
13
|
import { isGenericEntity } from "./relations-vocab.js";
|
|
14
|
+
import { buildOperationOrigin } from "../provenance/origin.js";
|
|
14
15
|
const FACTS_SCAN_LIMIT = 200;
|
|
15
16
|
const FACTS_PER_ENTITY = 5;
|
|
16
17
|
const DEFAULT_LOOKBACK_MS = 7 * 24 * 60 * 60 * 1000;
|
|
@@ -171,6 +172,15 @@ const upsertEntityTypePoint = async (candidate, classification, source) => {
|
|
|
171
172
|
classified_at: now,
|
|
172
173
|
updated_at: now,
|
|
173
174
|
created_at: now,
|
|
175
|
+
origin: buildOperationOrigin({
|
|
176
|
+
interface: "daemon",
|
|
177
|
+
action: "create",
|
|
178
|
+
subsystem: "entity_typing",
|
|
179
|
+
metadata: {
|
|
180
|
+
entity_name: candidate.name,
|
|
181
|
+
classification_source: source,
|
|
182
|
+
},
|
|
183
|
+
}),
|
|
174
184
|
source_fact_ids: candidate.factIds,
|
|
175
185
|
...(candidate.workstreamKeys.length > 0 ? { workstream_key: candidate.workstreamKeys[0] } : {}),
|
|
176
186
|
metadata: {
|
|
@@ -2,6 +2,7 @@ import type { BikkyConfig } from "../config.js";
|
|
|
2
2
|
import type { QdrantPayload } from "./qdrant.js";
|
|
3
3
|
import { type RedactionSummary } from "../privacy/redaction.js";
|
|
4
4
|
import { type WorkstreamRegistry } from "./workstream-resolver.js";
|
|
5
|
+
import { type OperationOrigin } from "../provenance/origin.js";
|
|
5
6
|
export { buildEpisodeSummaryMessages } from "../prompts/index.js";
|
|
6
7
|
export interface WorkspaceScope {
|
|
7
8
|
workspaceId?: string;
|
|
@@ -33,6 +34,7 @@ export interface EpisodeSummaryWriteResult {
|
|
|
33
34
|
action: "stored" | "updated" | "skipped";
|
|
34
35
|
factId?: string;
|
|
35
36
|
episodeId?: string;
|
|
37
|
+
destination?: string;
|
|
36
38
|
workstreamKey?: string | null;
|
|
37
39
|
reason?: string;
|
|
38
40
|
}
|
|
@@ -57,6 +59,8 @@ export declare const buildEpisodeSummaryPayload: (input: {
|
|
|
57
59
|
enabled: boolean;
|
|
58
60
|
redactPii: boolean;
|
|
59
61
|
};
|
|
62
|
+
config?: BikkyConfig;
|
|
63
|
+
origin?: OperationOrigin;
|
|
60
64
|
}) => EpisodeSummaryPayloadResult;
|
|
61
65
|
export declare const updateEpisodeSummary: (input: {
|
|
62
66
|
segment: EpisodeSegment;
|