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.
Files changed (50) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/CODE_OF_CONDUCT.md +1 -1
  3. package/CONTRIBUTING.md +1 -1
  4. package/README.md +23 -17
  5. package/SUPPORT.md +3 -2
  6. package/dist/config.d.ts +11 -1
  7. package/dist/config.js +88 -20
  8. package/dist/daemon/capture-policy.d.ts +0 -1
  9. package/dist/daemon/capture-policy.js +0 -1
  10. package/dist/daemon/consolidation.d.ts +2 -1
  11. package/dist/daemon/consolidation.js +28 -11
  12. package/dist/daemon/entity-typing.js +10 -0
  13. package/dist/daemon/episode-summary.d.ts +4 -0
  14. package/dist/daemon/episode-summary.js +39 -8
  15. package/dist/daemon/extraction.d.ts +1 -1
  16. package/dist/daemon/extraction.js +52 -17
  17. package/dist/daemon/qdrant.d.ts +32 -10
  18. package/dist/daemon/qdrant.js +177 -60
  19. package/dist/daemon/relations.d.ts +3 -3
  20. package/dist/daemon/relations.js +27 -15
  21. package/dist/daemon/session-index.d.ts +5 -0
  22. package/dist/daemon/session-index.js +36 -9
  23. package/dist/daemon/session-summary.d.ts +3 -0
  24. package/dist/daemon/session-summary.js +48 -15
  25. package/dist/daemon/staleness.js +2 -2
  26. package/dist/daemon/transcript-sources.js +3 -2
  27. package/dist/daemon/watcher.js +2 -0
  28. package/dist/daemon/workstream-summary.d.ts +4 -0
  29. package/dist/daemon/workstream-summary.js +58 -16
  30. package/dist/install.d.ts +11 -0
  31. package/dist/install.js +38 -0
  32. package/dist/llm/embedding/index.js +2 -1
  33. package/dist/llm/embedding/providers/openai.js +8 -2
  34. package/dist/llm/embedding/providers/portkey.js +9 -2
  35. package/dist/llm/inference/index.js +2 -1
  36. package/dist/llm/util.d.ts +12 -0
  37. package/dist/llm/util.js +18 -0
  38. package/dist/mcp/helpers.d.ts +5 -0
  39. package/dist/mcp/helpers.js +27 -3
  40. package/dist/mcp/taxonomy.js +12 -1
  41. package/dist/mcp/tools.js +161 -57
  42. package/dist/mcp/types.d.ts +12 -0
  43. package/dist/package-verifier.d.ts +19 -0
  44. package/dist/package-verifier.js +83 -0
  45. package/dist/provenance/origin.d.ts +57 -0
  46. package/dist/provenance/origin.js +254 -0
  47. package/docs/config/fully-hosted.md +33 -13
  48. package/docs/config/hosted-models.md +33 -13
  49. package/docs/configuration.md +23 -5
  50. package/package.json +6 -2
@@ -0,0 +1,83 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ const privateIdentityPathPattern = () => new RegExp([
4
+ ["saber", "zrelli", "private"].join("-"),
5
+ ["saber", "apate"].join("-"),
6
+ "ap" + "ate",
7
+ ].join("|"), "i");
8
+ const privateIdentityContentPattern = () => new RegExp([
9
+ ["saber", "zrelli", "private"].join("-"),
10
+ ["saber", "apate"].join("-"),
11
+ ["apate", "ai"].join("-"),
12
+ ["apate", "com"].join("\\."),
13
+ ].join("|"), "i");
14
+ export const TEXT_EXTENSIONS = new Set([
15
+ ".css",
16
+ ".d.ts",
17
+ ".html",
18
+ ".js",
19
+ ".json",
20
+ ".md",
21
+ ".svg",
22
+ ".txt",
23
+ ]);
24
+ export const FORBIDDEN_PATH_PATTERNS = [
25
+ { re: /(^|\/)(tasks|node_modules|\.git)(\/|$)/, reason: "task, node_modules, or git metadata" },
26
+ { re: /(^|\/)\.env($|\.)/, reason: "environment files" },
27
+ { re: /(\.test|\.itest)\.(js|d\.ts)$/i, reason: "compiled test artifacts" },
28
+ { re: /(^|\/)(id_rsa|id_ed25519|.*\.pem|.*\.key)$/i, reason: "private key material" },
29
+ { re: /(^|\/)Users\/|\/Users\//i, reason: "absolute local user paths" },
30
+ { re: privateIdentityPathPattern(), reason: "private identity references" },
31
+ ];
32
+ export const FORBIDDEN_CONTENT_PATTERNS = [
33
+ { re: /gh[pousr]_[A-Za-z0-9_]{20,}/, reason: "GitHub token" },
34
+ { re: /github_pat_[A-Za-z0-9_]{20,}/, reason: "GitHub fine-grained token" },
35
+ { re: /sk-[A-Za-z0-9]{20,}/, reason: "OpenAI-style API key" },
36
+ { re: /AKIA[0-9A-Z]{16}/, reason: "AWS access key id" },
37
+ { re: /AIza[0-9A-Za-z_-]{35}/, reason: "Google API key" },
38
+ { re: /xox[baprs]-[0-9A-Za-z-]{20,}/, reason: "Slack token" },
39
+ { re: /-----BEGIN [A-Z ]*PRIVATE KEY-----/, reason: "private key block" },
40
+ { re: /\/Users\/saber\b/i, reason: "local user path" },
41
+ { re: privateIdentityContentPattern(), reason: "private identity reference" },
42
+ ];
43
+ export function normalizePackPath(file) {
44
+ return file.replace(/^package\//, "");
45
+ }
46
+ export function findForbiddenPath(file, patterns = FORBIDDEN_PATH_PATTERNS) {
47
+ return patterns.find(({ re }) => re.test(file)) ?? null;
48
+ }
49
+ export function findForbiddenContent(content, patterns = FORBIDDEN_CONTENT_PATTERNS) {
50
+ return patterns.find(({ re }) => re.test(content)) ?? null;
51
+ }
52
+ export function assertPackageContents(pkg, files, patterns = FORBIDDEN_PATH_PATTERNS) {
53
+ const fileSet = new Set(files);
54
+ for (const requiredPath of pkg.requiredPaths) {
55
+ if (!fileSet.has(requiredPath)) {
56
+ throw new Error(`${pkg.name} package is missing required file: ${requiredPath}`);
57
+ }
58
+ }
59
+ for (const file of files) {
60
+ const forbidden = findForbiddenPath(file, patterns);
61
+ if (forbidden) {
62
+ throw new Error(`${pkg.name} package includes forbidden path (${forbidden.reason}): ${file}`);
63
+ }
64
+ }
65
+ }
66
+ export function shouldScanTextByMetadata(file, size, textExtensions = TEXT_EXTENSIONS) {
67
+ if (size > 2_000_000)
68
+ return false;
69
+ const lower = file.toLowerCase();
70
+ if (lower.endsWith(".d.ts"))
71
+ return true;
72
+ return textExtensions.has(path.extname(lower));
73
+ }
74
+ export function shouldScanText(file) {
75
+ return shouldScanTextByMetadata(file, fs.statSync(file).size);
76
+ }
77
+ export function assertSafeTextContent(pkgName, relPath, content) {
78
+ const forbidden = findForbiddenContent(content);
79
+ if (forbidden) {
80
+ throw new Error(`${pkgName} package includes forbidden content (${forbidden.reason}) in ${relPath}`);
81
+ }
82
+ }
83
+ //# sourceMappingURL=package-verifier.js.map
@@ -0,0 +1,57 @@
1
+ import type { BikkyConfig } from "../config.js";
2
+ export type OriginIdentityType = "user" | "coding_agent" | "daemon" | "ui" | "api" | "cli" | "docs" | "system" | "unknown";
3
+ export type OriginIdentitySource = "config" | "shell" | "git" | "env" | "hostname";
4
+ export type OriginInterface = "mcp" | "daemon" | "ui" | "api" | "cli" | "system";
5
+ export type OriginAction = "create" | "update" | "delete" | "verify" | "forget" | "review" | "correct" | "reinforce" | "supersede" | "feedback";
6
+ export type OriginMetadataValue = string | number | boolean | null;
7
+ export interface OriginIdentity {
8
+ type: OriginIdentityType;
9
+ id: string | null;
10
+ name: string | null;
11
+ source: OriginIdentitySource;
12
+ }
13
+ export interface OperationOrigin {
14
+ schema_version: 1;
15
+ user: OriginIdentity | null;
16
+ agent: OriginIdentity;
17
+ interface: OriginInterface;
18
+ operation: {
19
+ action: OriginAction;
20
+ tool?: string;
21
+ route?: string;
22
+ subsystem?: string;
23
+ outcome?: string;
24
+ };
25
+ metadata?: Record<string, OriginMetadataValue>;
26
+ }
27
+ export interface ResolveUserIdentityOptions {
28
+ config?: Pick<BikkyConfig, "identity"> | null;
29
+ env?: NodeJS.ProcessEnv;
30
+ cwd?: string;
31
+ hostname?: string;
32
+ shellUsername?: string | null;
33
+ }
34
+ export interface ResolveAgentIdentityOptions {
35
+ type?: OriginIdentityType;
36
+ interface?: OriginInterface;
37
+ env?: NodeJS.ProcessEnv;
38
+ hostname?: string;
39
+ }
40
+ export interface BuildOperationOriginInput extends ResolveUserIdentityOptions {
41
+ interface: OriginInterface;
42
+ action: OriginAction;
43
+ tool?: string;
44
+ route?: string;
45
+ subsystem?: string;
46
+ outcome?: string;
47
+ agentType?: OriginIdentityType;
48
+ metadata?: Record<string, unknown>;
49
+ }
50
+ export declare function normalizeOriginId(value: string | null | undefined): string | null;
51
+ export declare function resolveUserIdentity(options?: ResolveUserIdentityOptions): OriginIdentity;
52
+ export declare function inferUserIdentity(options?: Omit<ResolveUserIdentityOptions, "config">): OriginIdentity;
53
+ export declare function resolveAgentIdentity(options?: ResolveAgentIdentityOptions): OriginIdentity;
54
+ export declare function sanitizeOriginMetadata(metadata: Record<string, unknown> | undefined): Record<string, OriginMetadataValue> | undefined;
55
+ export declare function buildOperationOrigin(input: BuildOperationOriginInput): OperationOrigin;
56
+ export declare function isOperationOrigin(value: unknown): value is OperationOrigin;
57
+ //# sourceMappingURL=origin.d.ts.map
@@ -0,0 +1,254 @@
1
+ import crypto from "node:crypto";
2
+ import { execFileSync } from "node:child_process";
3
+ import os from "node:os";
4
+ const MAX_NAME_LENGTH = 128;
5
+ const MAX_OPERATION_FIELD_LENGTH = 128;
6
+ const MAX_METADATA_KEYS = 32;
7
+ const MAX_METADATA_KEY_LENGTH = 64;
8
+ const MAX_METADATA_STRING_LENGTH = 256;
9
+ const hash = (value) => crypto.createHash("sha256").update(value.trim().toLowerCase()).digest("hex").slice(0, 12);
10
+ const slug = (value) => value.trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64);
11
+ const looksLikeEmail = (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value.trim());
12
+ const cleanName = (value) => {
13
+ const trimmed = value?.trim();
14
+ if (!trimmed || looksLikeEmail(trimmed))
15
+ return null;
16
+ return trimmed.slice(0, MAX_NAME_LENGTH);
17
+ };
18
+ const cleanOperationField = (value) => {
19
+ const trimmed = value?.trim();
20
+ return trimmed ? trimmed.slice(0, MAX_OPERATION_FIELD_LENGTH) : undefined;
21
+ };
22
+ export function normalizeOriginId(value) {
23
+ const trimmed = value?.trim();
24
+ if (!trimmed)
25
+ return null;
26
+ if (looksLikeEmail(trimmed))
27
+ return `email:${hash(trimmed)}`;
28
+ const normalized = slug(trimmed);
29
+ return normalized || `id:${hash(trimmed)}`;
30
+ }
31
+ function generatedId(prefix, value) {
32
+ const trimmed = value?.trim();
33
+ if (!trimmed)
34
+ return null;
35
+ if (looksLikeEmail(trimmed))
36
+ return `${prefix}:email:${hash(trimmed)}`;
37
+ const normalized = slug(trimmed);
38
+ return normalized ? `${prefix}:${normalized}` : `${prefix}:${hash(trimmed)}`;
39
+ }
40
+ function configuredIdentity(type, rawId, rawName, source) {
41
+ const name = cleanName(rawName);
42
+ const id = normalizeOriginId(rawId) ?? (name ? generatedId(type === "user" ? "user" : type, name) : null);
43
+ if (!id && !name)
44
+ return null;
45
+ return { type, id, name, source };
46
+ }
47
+ function readGitConfig(key, cwd) {
48
+ try {
49
+ const value = execFileSync("git", ["config", "--get", key], {
50
+ cwd,
51
+ encoding: "utf8",
52
+ stdio: ["ignore", "pipe", "ignore"],
53
+ timeout: 1000,
54
+ }).trim();
55
+ return value || undefined;
56
+ }
57
+ catch {
58
+ return undefined;
59
+ }
60
+ }
61
+ function gitUserIdentity(cwd) {
62
+ const gitName = readGitConfig("user.name", cwd);
63
+ const gitEmail = readGitConfig("user.email", cwd);
64
+ const nameSlug = slug(gitName ?? "");
65
+ if (gitEmail) {
66
+ return {
67
+ type: "user",
68
+ id: nameSlug ? `git:${nameSlug}:${hash(gitEmail)}` : `git:${hash(gitEmail)}`,
69
+ name: cleanName(gitName),
70
+ source: "git",
71
+ };
72
+ }
73
+ if (nameSlug) {
74
+ return {
75
+ type: "user",
76
+ id: `git:${nameSlug}`,
77
+ name: cleanName(gitName),
78
+ source: "git",
79
+ };
80
+ }
81
+ return null;
82
+ }
83
+ function shellUserName(options) {
84
+ if (Object.prototype.hasOwnProperty.call(options, "shellUsername") && options.shellUsername === null) {
85
+ return null;
86
+ }
87
+ const explicit = cleanName(options.shellUsername);
88
+ if (explicit)
89
+ return explicit;
90
+ try {
91
+ const user = os.userInfo().username;
92
+ const cleaned = cleanName(user);
93
+ if (cleaned)
94
+ return cleaned;
95
+ }
96
+ catch {
97
+ // ignore and fall through to env-derived shell names
98
+ }
99
+ const env = options.env ?? process.env;
100
+ return cleanName(env.USER ?? env.LOGNAME ?? env.USERNAME);
101
+ }
102
+ function hostnameValue(hostname) {
103
+ const explicit = hostname?.trim();
104
+ if (explicit)
105
+ return explicit;
106
+ const detected = os.hostname().trim();
107
+ return detected || "unknown-host";
108
+ }
109
+ function hostnameIdentity(type, hostname, prefix) {
110
+ const host = hostnameValue(hostname);
111
+ return {
112
+ type,
113
+ id: generatedId(prefix ?? "host", host),
114
+ name: cleanName(host),
115
+ source: "hostname",
116
+ };
117
+ }
118
+ export function resolveUserIdentity(options = {}) {
119
+ const env = options.env ?? process.env;
120
+ const fromConfig = configuredIdentity("user", options.config?.identity.user_id, options.config?.identity.user_name, "config");
121
+ if (fromConfig)
122
+ return fromConfig;
123
+ const fromEnv = configuredIdentity("user", env.BIKKY_USER_ID, env.BIKKY_USER_NAME, "env");
124
+ if (fromEnv)
125
+ return fromEnv;
126
+ const fromGit = gitUserIdentity(options.cwd);
127
+ if (fromGit)
128
+ return fromGit;
129
+ const shellName = shellUserName(options);
130
+ if (shellName) {
131
+ return {
132
+ type: "user",
133
+ id: generatedId("shell", shellName),
134
+ name: shellName,
135
+ source: "shell",
136
+ };
137
+ }
138
+ return hostnameIdentity("user", options.hostname);
139
+ }
140
+ export function inferUserIdentity(options = {}) {
141
+ return resolveUserIdentity({ ...options, config: null });
142
+ }
143
+ function defaultAgentType(originInterface) {
144
+ switch (originInterface) {
145
+ case "mcp":
146
+ return "coding_agent";
147
+ case "daemon":
148
+ return "daemon";
149
+ case "ui":
150
+ return "ui";
151
+ case "api":
152
+ return "api";
153
+ case "cli":
154
+ return "cli";
155
+ case "system":
156
+ return "system";
157
+ }
158
+ }
159
+ function defaultAgentName(type, originInterface) {
160
+ if (type === "coding_agent" && originInterface === "mcp")
161
+ return "Bikky MCP client";
162
+ if (type === "daemon")
163
+ return "Bikky daemon";
164
+ if (type === "ui")
165
+ return "Bikky UI";
166
+ if (type === "api")
167
+ return "Bikky API";
168
+ if (type === "cli")
169
+ return "Bikky CLI";
170
+ if (type === "system")
171
+ return "Bikky system";
172
+ if (type === "docs")
173
+ return "Bikky docs importer";
174
+ return "Bikky";
175
+ }
176
+ export function resolveAgentIdentity(options = {}) {
177
+ const env = options.env ?? process.env;
178
+ const type = options.type ?? (options.interface ? defaultAgentType(options.interface) : "unknown");
179
+ const envIdentity = configuredIdentity(type, env.BIKKY_AGENT_ID, env.BIKKY_AGENT_NAME, "env");
180
+ if (envIdentity)
181
+ return envIdentity;
182
+ const host = hostnameValue(options.hostname);
183
+ return {
184
+ type,
185
+ id: generatedId(type, host),
186
+ name: cleanName(`${defaultAgentName(type, options.interface)} on ${host}`),
187
+ source: "hostname",
188
+ };
189
+ }
190
+ export function sanitizeOriginMetadata(metadata) {
191
+ if (!metadata)
192
+ return undefined;
193
+ const sanitized = {};
194
+ for (const [rawKey, rawValue] of Object.entries(metadata).slice(0, MAX_METADATA_KEYS)) {
195
+ const key = rawKey.trim().replace(/[^a-zA-Z0-9_.-]+/g, "_").slice(0, MAX_METADATA_KEY_LENGTH);
196
+ if (!key)
197
+ continue;
198
+ if (rawValue === null || typeof rawValue === "boolean") {
199
+ sanitized[key] = rawValue;
200
+ }
201
+ else if (typeof rawValue === "number") {
202
+ if (Number.isFinite(rawValue))
203
+ sanitized[key] = rawValue;
204
+ }
205
+ else if (typeof rawValue === "string") {
206
+ sanitized[key] = rawValue.slice(0, MAX_METADATA_STRING_LENGTH);
207
+ }
208
+ }
209
+ return Object.keys(sanitized).length > 0 ? sanitized : undefined;
210
+ }
211
+ export function buildOperationOrigin(input) {
212
+ const operation = {
213
+ action: input.action,
214
+ };
215
+ const tool = cleanOperationField(input.tool);
216
+ const route = cleanOperationField(input.route);
217
+ const subsystem = cleanOperationField(input.subsystem);
218
+ const outcome = cleanOperationField(input.outcome);
219
+ if (tool)
220
+ operation.tool = tool;
221
+ if (route)
222
+ operation.route = route;
223
+ if (subsystem)
224
+ operation.subsystem = subsystem;
225
+ if (outcome)
226
+ operation.outcome = outcome;
227
+ const origin = {
228
+ schema_version: 1,
229
+ user: resolveUserIdentity(input),
230
+ agent: resolveAgentIdentity({
231
+ type: input.agentType,
232
+ interface: input.interface,
233
+ env: input.env,
234
+ hostname: input.hostname,
235
+ }),
236
+ interface: input.interface,
237
+ operation,
238
+ };
239
+ const metadata = sanitizeOriginMetadata(input.metadata);
240
+ if (metadata)
241
+ origin.metadata = metadata;
242
+ return origin;
243
+ }
244
+ export function isOperationOrigin(value) {
245
+ if (!value || typeof value !== "object")
246
+ return false;
247
+ const candidate = value;
248
+ return candidate.schema_version === 1
249
+ && Boolean(candidate.agent)
250
+ && typeof candidate.interface === "string"
251
+ && Boolean(candidate.operation)
252
+ && typeof candidate.operation?.action === "string";
253
+ }
254
+ //# sourceMappingURL=origin.js.map
@@ -1,16 +1,16 @@
1
1
  # Fully hosted config
2
2
 
3
- Best for performance and teams. This setup uses Qdrant Cloud for managed vector storage and OpenAI-compatible hosted models for extraction, curation, and recall.
3
+ Best for performance and teams. This setup uses Qdrant Cloud for managed vector storage and a hosted gateway/provider for extraction, curation, and recall.
4
4
 
5
5
  ## What you need
6
6
 
7
7
  - A Qdrant Cloud cluster URL.
8
8
  - A Qdrant API key.
9
- - An OpenAI API key, or another hosted provider configured in the full configuration guide.
9
+ - A Portkey API key (recommended) — one key, many upstream providers, with built-in routing, fallbacks, and observability. Get one at [portkey.ai](https://portkey.ai). Or use OpenAI / Bedrock directly.
10
10
 
11
- For both `embedding.provider` and `llm.provider`, possible values are `openai`, `bedrock`, or `portkey` for hosted models. `ollama` is also supported when you want local model calls.
11
+ For both `embedding.provider` and `llm.provider`, possible values are `portkey`, `openai`, or `bedrock` for hosted models. `ollama` is also supported when you want local model calls.
12
12
 
13
- ## Config
13
+ ## Config (recommended: Portkey)
14
14
 
15
15
  Save this as `~/.bikky/config.json`:
16
16
 
@@ -18,10 +18,36 @@ Save this as `~/.bikky/config.json`:
18
18
  {
19
19
  "qdrant_url": "https://your-cluster.cloud.qdrant.io:6333",
20
20
  "qdrant_api_key": "your-qdrant-api-key",
21
+ "embedding": {
22
+ "provider": "portkey",
23
+ "model": "@openai/text-embedding-3-small",
24
+ "dimensions": 1024
25
+ },
26
+ "llm": {
27
+ "provider": "portkey",
28
+ "model": "@anthropic/claude-sonnet-4"
29
+ }
30
+ }
31
+ ```
32
+
33
+ Then export the gateway key:
34
+
35
+ ```bash
36
+ export PORTKEY_API_KEY="pk-..."
37
+ ```
38
+
39
+ bikky uses **1024-dimensional embeddings** as the canonical default. This is portable across modern providers (OpenAI 3-small/3-large via Matryoshka truncation, Cohere v3, Voyage, Mistral, Bedrock Titan v2, BGE/E5) so you can switch providers later without re-embedding.
40
+
41
+ `qdrant_api_key` is optional only for unauthenticated self-hosted Qdrant. Qdrant Cloud usually requires it.
42
+
43
+ ## Alternative: OpenAI directly
44
+
45
+ ```json
46
+ {
21
47
  "embedding": {
22
48
  "provider": "openai",
23
49
  "model": "text-embedding-3-small",
24
- "dimensions": 1536,
50
+ "dimensions": 1024,
25
51
  "api_key": "sk-..."
26
52
  },
27
53
  "llm": {
@@ -32,13 +58,7 @@ Save this as `~/.bikky/config.json`:
32
58
  }
33
59
  ```
34
60
 
35
- `qdrant_api_key` is optional only for unauthenticated self-hosted Qdrant. Qdrant Cloud usually requires it.
36
-
37
- Prefer not to store hosted model keys in the config file? Omit `api_key` above and set:
38
-
39
- ```bash
40
- export OPENAI_API_KEY="sk-..."
41
- ```
61
+ Or set `OPENAI_API_KEY` in the environment instead of the config file.
42
62
 
43
63
  ## Check it
44
64
 
@@ -54,4 +74,4 @@ bikky stop && bikky start
54
74
 
55
75
  Then restart your editor so its MCP process reloads.
56
76
 
57
- For Bedrock, Portkey, custom base URLs, or model-specific dimensions, see the [full configuration guide](../configuration.md).
77
+ For Bedrock, custom base URLs, or model-specific dimensions, see the [full configuration guide](https://github.com/bikky-dev/bikky/blob/main/docs/configuration.md).
@@ -1,15 +1,15 @@
1
1
  # Local Qdrant + hosted models config
2
2
 
3
- This setup keeps Qdrant local while hosted embeddings and LLM calls handle extraction, curation, and recall.
3
+ This setup keeps Qdrant local while a hosted gateway/provider handles extraction, curation, and recall.
4
4
 
5
5
  ## What you need
6
6
 
7
7
  - Qdrant running locally, usually with Docker.
8
- - An OpenAI API key, or another hosted provider configured in the full configuration guide.
8
+ - A Portkey API key (recommended) — one key, many upstream providers, with built-in routing, fallbacks, and observability. Get one at [portkey.ai](https://portkey.ai). Or use OpenAI / Bedrock directly.
9
9
 
10
- For both `embedding.provider` and `llm.provider`, possible values are `openai`, `bedrock`, or `portkey` for hosted models. `ollama` is also supported when you want local model calls.
10
+ For both `embedding.provider` and `llm.provider`, possible values are `portkey`, `openai`, or `bedrock` for hosted models. `ollama` is also supported when you want local model calls.
11
11
 
12
- ## Config
12
+ ## Config (recommended: Portkey)
13
13
 
14
14
  Save this as `~/.bikky/config.json`:
15
15
 
@@ -17,10 +17,36 @@ Save this as `~/.bikky/config.json`:
17
17
  {
18
18
  "qdrant_url": "http://localhost:6333",
19
19
  "qdrant_api_key": "",
20
+ "embedding": {
21
+ "provider": "portkey",
22
+ "model": "@openai/text-embedding-3-small",
23
+ "dimensions": 1024
24
+ },
25
+ "llm": {
26
+ "provider": "portkey",
27
+ "model": "@anthropic/claude-sonnet-4"
28
+ }
29
+ }
30
+ ```
31
+
32
+ Then export the gateway key:
33
+
34
+ ```bash
35
+ export PORTKEY_API_KEY="pk-..."
36
+ ```
37
+
38
+ bikky uses **1024-dimensional embeddings** as the canonical default. This is portable across modern providers (OpenAI 3-small/3-large via Matryoshka truncation, Cohere v3, Voyage, Mistral, Bedrock Titan v2, BGE/E5) so you can switch providers later without re-embedding.
39
+
40
+ `qdrant_api_key` is optional. Leave it empty or omit it for local or unauthenticated self-hosted Qdrant.
41
+
42
+ ## Alternative: OpenAI directly
43
+
44
+ ```json
45
+ {
20
46
  "embedding": {
21
47
  "provider": "openai",
22
48
  "model": "text-embedding-3-small",
23
- "dimensions": 1536,
49
+ "dimensions": 1024,
24
50
  "api_key": "sk-..."
25
51
  },
26
52
  "llm": {
@@ -31,13 +57,7 @@ Save this as `~/.bikky/config.json`:
31
57
  }
32
58
  ```
33
59
 
34
- `qdrant_api_key` is optional. Leave it empty or omit it for local or unauthenticated self-hosted Qdrant.
35
-
36
- Prefer not to store hosted model keys in the config file? Omit `api_key` above and set:
37
-
38
- ```bash
39
- export OPENAI_API_KEY="sk-..."
40
- ```
60
+ Or set `OPENAI_API_KEY` in the environment instead of the config file.
41
61
 
42
62
  ## Check it
43
63
 
@@ -47,4 +67,4 @@ bikky status
47
67
 
48
68
  If you started from a fresh install, run `bikky setup` after writing the config, then restart your editor so its MCP process reloads.
49
69
 
50
- For Bedrock, Portkey, custom base URLs, or model-specific dimensions, see the [full configuration guide](../configuration.md).
70
+ For Bedrock, custom base URLs, or model-specific dimensions, see the [full configuration guide](https://github.com/bikky-dev/bikky/blob/main/docs/configuration.md).
@@ -26,18 +26,32 @@ bikky status
26
26
 
27
27
  Config lives at `~/.bikky/config.json`, or at `BIKKY_HOME/config.json` when `BIKKY_HOME` is set. Environment variables override the config file.
28
28
 
29
+ ## Origin identity
30
+
31
+ New memory writes store canonical `origin` metadata. `origin.user` is the configured human identity, `origin.agent` is the automated actor or local surface, `origin.interface` is the entry point (`mcp`, `daemon`, `ui`, `api`, `cli`, or `system`), and `origin.operation` records the create/update/delete/verify/forget/review/correct/reinforce/supersede/feedback action.
32
+
33
+ `bikky setup` provisions `identity.user_id` and `identity.user_name` if they are missing. It never overwrites an existing configured user. Runtime detection uses this order:
34
+
35
+ 1. `identity.user_id` / `identity.user_name` from config
36
+ 2. `BIKKY_USER_ID` / `BIKKY_USER_NAME`
37
+ 3. Git config (`user.name`, `user.email`; email-like identifiers are hashed)
38
+ 4. Shell/OS username
39
+ 5. Hostname fallback
40
+
41
+ MCP callers cannot pass `origin` or override the human user. Use `BIKKY_AGENT_ID` / `BIKKY_AGENT_NAME` only when you need to label the local automated agent process explicitly; otherwise bikky labels the daemon/UI/MCP surface from the hostname.
42
+
29
43
  ## Common setups
30
44
 
31
45
  If you know which path you want, start with the focused guide:
32
46
 
33
47
  | Setup | Best for | Guide |
34
48
  | ----------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------ |
35
- | Fully hosted | Best performance and teams; managed vector storage and models | [Fully hosted config](config/fully-hosted.md) |
36
- | Local Qdrant + hosted models | Local vector storage with hosted extraction and embedding | [Hosted models config](config/hosted-models.md) |
37
- | Local and free | Local evaluation; quality depends on local models | [Local config guide](config/local.md) |
38
- | Hosted Qdrant + local models | Shared vector storage while keeping model calls local | [Hosted Qdrant + local models](config/hosted-qdrant-local-models.md) |
49
+ | Fully hosted | Best performance and teams; managed vector storage and models | [Fully hosted config](https://github.com/bikky-dev/bikky/blob/main/docs/config/fully-hosted.md) |
50
+ | Local Qdrant + hosted models | Local vector storage with hosted extraction and embedding | [Hosted models config](https://github.com/bikky-dev/bikky/blob/main/docs/config/hosted-models.md) |
51
+ | Local and free | Local evaluation; quality depends on local models | [Local config guide](https://github.com/bikky-dev/bikky/blob/main/docs/config/local.md) |
52
+ | Hosted Qdrant + local models | Shared vector storage while keeping model calls local | [Hosted Qdrant + local models](https://github.com/bikky-dev/bikky/blob/main/docs/config/hosted-qdrant-local-models.md) |
39
53
 
40
- Privacy-conscious users should also read the [privacy-first quickstart](privacy-first.md), which combines local Qdrant, local Ollama models, and transcript-capture disable controls.
54
+ Privacy-conscious users should also read the [privacy-first quickstart](https://github.com/bikky-dev/bikky/blob/main/docs/privacy-first.md), which combines local Qdrant, local Ollama models, and transcript-capture disable controls.
41
55
 
42
56
  ### Fully hosted
43
57
 
@@ -139,6 +153,10 @@ Useful basics:
139
153
  | `QDRANT_URL` | `qdrant_url` | Required unless set in config |
140
154
  | `QDRANT_API_KEY` | `qdrant_api_key` | Optional for local/unauthenticated Qdrant; usually needed for Qdrant Cloud |
141
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` |
142
160
 
143
161
  ## Provider options
144
162
 
package/package.json CHANGED
@@ -1,9 +1,13 @@
1
1
  {
2
2
  "name": "bikky",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
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",
7
+ "author": {
8
+ "name": "Saber Zrelli",
9
+ "url": "https://github.com/zrelli-s"
10
+ },
7
11
  "repository": {
8
12
  "type": "git",
9
13
  "url": "git+https://github.com/bikky-dev/bikky.git"
@@ -56,7 +60,7 @@
56
60
  "@aws-sdk/client-bedrock-runtime": "^3.1006.0",
57
61
  "@modelcontextprotocol/sdk": "^1.29.0",
58
62
  "pino": "^10.3.1",
59
- "zod": "^3.24.0"
63
+ "zod": "^4.4.1"
60
64
  },
61
65
  "devDependencies": {
62
66
  "@types/node": "^25.6.0",