om-memory-system 3.0.0
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/LICENSE +21 -0
- package/README.md +587 -0
- package/dist/adapters/opencode/auto-capture-host.d.ts +3 -0
- package/dist/adapters/opencode/auto-capture-host.js +29 -0
- package/dist/adapters/opencode/auto-capture-summary.d.ts +4 -0
- package/dist/adapters/opencode/auto-capture-summary.js +147 -0
- package/dist/adapters/opencode/conversation.d.ts +2 -0
- package/dist/adapters/opencode/conversation.js +50 -0
- package/dist/adapters/opencode/user-prompt.d.ts +9 -0
- package/dist/adapters/opencode/user-prompt.js +26 -0
- package/dist/adapters/pi/capture.d.ts +41 -0
- package/dist/adapters/pi/capture.js +91 -0
- package/dist/adapters/pi/conversation.d.ts +38 -0
- package/dist/adapters/pi/conversation.js +118 -0
- package/dist/adapters/pi/extension.d.ts +18 -0
- package/dist/adapters/pi/extension.js +234 -0
- package/dist/adapters/pi/import-command.d.ts +29 -0
- package/dist/adapters/pi/import-command.js +232 -0
- package/dist/adapters/pi/profile.d.ts +39 -0
- package/dist/adapters/pi/profile.js +148 -0
- package/dist/adapters/pi/provider.d.ts +50 -0
- package/dist/adapters/pi/provider.js +77 -0
- package/dist/config.d.ts +290 -0
- package/dist/config.js +742 -0
- package/dist/core/capture-context.d.ts +4 -0
- package/dist/core/capture-context.js +132 -0
- package/dist/core/capture.d.ts +15 -0
- package/dist/core/capture.js +68 -0
- package/dist/core/extraction.d.ts +80 -0
- package/dist/core/extraction.js +140 -0
- package/dist/core/host.d.ts +52 -0
- package/dist/core/host.js +1 -0
- package/dist/core/memory-operations.d.ts +27 -0
- package/dist/core/memory-operations.js +247 -0
- package/dist/core/retrieval.d.ts +12 -0
- package/dist/core/retrieval.js +72 -0
- package/dist/importer/discovery.d.ts +31 -0
- package/dist/importer/discovery.js +88 -0
- package/dist/importer/importer.d.ts +87 -0
- package/dist/importer/importer.js +260 -0
- package/dist/importer/ledger.d.ts +44 -0
- package/dist/importer/ledger.js +137 -0
- package/dist/importer/session-loader.d.ts +19 -0
- package/dist/importer/session-loader.js +12 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +601 -0
- package/dist/plugin.d.ts +9 -0
- package/dist/plugin.js +11 -0
- package/dist/services/ai/ai-provider-factory.d.ts +7 -0
- package/dist/services/ai/ai-provider-factory.js +41 -0
- package/dist/services/ai/api-key-placeholder.d.ts +1 -0
- package/dist/services/ai/api-key-placeholder.js +14 -0
- package/dist/services/ai/internal-capture-sessions.d.ts +6 -0
- package/dist/services/ai/internal-capture-sessions.js +33 -0
- package/dist/services/ai/opencode-diagnostics.d.ts +7 -0
- package/dist/services/ai/opencode-diagnostics.js +36 -0
- package/dist/services/ai/opencode-host-config.d.ts +10 -0
- package/dist/services/ai/opencode-host-config.js +51 -0
- package/dist/services/ai/opencode-provider-loader.d.ts +3 -0
- package/dist/services/ai/opencode-provider-loader.js +4 -0
- package/dist/services/ai/opencode-provider.d.ts +92 -0
- package/dist/services/ai/opencode-provider.js +419 -0
- package/dist/services/ai/opencode-sdk-client.d.ts +6 -0
- package/dist/services/ai/opencode-sdk-client.js +38 -0
- package/dist/services/ai/profile-llm-client.d.ts +2 -0
- package/dist/services/ai/profile-llm-client.js +27 -0
- package/dist/services/ai/provider-config.d.ts +17 -0
- package/dist/services/ai/provider-config.js +30 -0
- package/dist/services/ai/providers/anthropic-messages.d.ts +34 -0
- package/dist/services/ai/providers/anthropic-messages.js +220 -0
- package/dist/services/ai/providers/base-provider.d.ts +24 -0
- package/dist/services/ai/providers/base-provider.js +24 -0
- package/dist/services/ai/providers/google-gemini.d.ts +15 -0
- package/dist/services/ai/providers/google-gemini.js +228 -0
- package/dist/services/ai/providers/minimax.d.ts +35 -0
- package/dist/services/ai/providers/minimax.js +55 -0
- package/dist/services/ai/providers/openai-chat-completion.d.ts +22 -0
- package/dist/services/ai/providers/openai-chat-completion.js +387 -0
- package/dist/services/ai/providers/openai-responses.d.ts +13 -0
- package/dist/services/ai/providers/openai-responses.js +182 -0
- package/dist/services/ai/providers/orcarouter.d.ts +51 -0
- package/dist/services/ai/providers/orcarouter.js +66 -0
- package/dist/services/ai/session/ai-session-manager.d.ts +24 -0
- package/dist/services/ai/session/ai-session-manager.js +211 -0
- package/dist/services/ai/session/session-types.d.ts +42 -0
- package/dist/services/ai/session/session-types.js +1 -0
- package/dist/services/ai/tools/tool-schema.d.ts +40 -0
- package/dist/services/ai/tools/tool-schema.js +24 -0
- package/dist/services/ai/validators/user-profile-validator.d.ts +12 -0
- package/dist/services/ai/validators/user-profile-validator.js +114 -0
- package/dist/services/api-handlers.d.ts +186 -0
- package/dist/services/api-handlers.js +1284 -0
- package/dist/services/auth-token.d.ts +14 -0
- package/dist/services/auth-token.js +59 -0
- package/dist/services/auto-capture.d.ts +3 -0
- package/dist/services/auto-capture.js +127 -0
- package/dist/services/cleanup-service.d.ts +22 -0
- package/dist/services/cleanup-service.js +109 -0
- package/dist/services/client.d.ts +134 -0
- package/dist/services/client.js +327 -0
- package/dist/services/context.d.ts +10 -0
- package/dist/services/context.js +27 -0
- package/dist/services/cors.d.ts +12 -0
- package/dist/services/cors.js +52 -0
- package/dist/services/deduplication-service.d.ts +28 -0
- package/dist/services/deduplication-service.js +111 -0
- package/dist/services/embedding.d.ts +36 -0
- package/dist/services/embedding.js +200 -0
- package/dist/services/jsonc.d.ts +6 -0
- package/dist/services/jsonc.js +76 -0
- package/dist/services/language-detector.d.ts +2 -0
- package/dist/services/language-detector.js +33 -0
- package/dist/services/legacy-migration.d.ts +77 -0
- package/dist/services/legacy-migration.js +343 -0
- package/dist/services/logger.d.ts +1 -0
- package/dist/services/logger.js +119 -0
- package/dist/services/memory-portability-service.d.ts +37 -0
- package/dist/services/memory-portability-service.js +340 -0
- package/dist/services/memory-scope.d.ts +16 -0
- package/dist/services/memory-scope.js +43 -0
- package/dist/services/migration-service.d.ts +42 -0
- package/dist/services/migration-service.js +307 -0
- package/dist/services/onnxruntime-resolve.d.ts +24 -0
- package/dist/services/onnxruntime-resolve.js +174 -0
- package/dist/services/privacy.d.ts +26 -0
- package/dist/services/privacy.js +69 -0
- package/dist/services/request-timeouts.d.ts +9 -0
- package/dist/services/request-timeouts.js +11 -0
- package/dist/services/runtime-require.d.ts +17 -0
- package/dist/services/runtime-require.js +75 -0
- package/dist/services/secret-resolver.d.ts +1 -0
- package/dist/services/secret-resolver.js +65 -0
- package/dist/services/shard-inventory-service.d.ts +69 -0
- package/dist/services/shard-inventory-service.js +315 -0
- package/dist/services/shard-path-migration-service.d.ts +31 -0
- package/dist/services/shard-path-migration-service.js +367 -0
- package/dist/services/tag-prefix-migration.d.ts +60 -0
- package/dist/services/tag-prefix-migration.js +439 -0
- package/dist/services/tag-prefix-scan.d.ts +26 -0
- package/dist/services/tag-prefix-scan.js +91 -0
- package/dist/services/tags.d.ts +29 -0
- package/dist/services/tags.js +251 -0
- package/dist/services/turso/connection-manager.d.ts +15 -0
- package/dist/services/turso/connection-manager.js +163 -0
- package/dist/services/turso/cross-process-write-lock.d.ts +1 -0
- package/dist/services/turso/cross-process-write-lock.js +89 -0
- package/dist/services/turso/legacy-migrator.d.ts +1 -0
- package/dist/services/turso/legacy-migrator.js +540 -0
- package/dist/services/turso/lifecycle.d.ts +1 -0
- package/dist/services/turso/lifecycle.js +14 -0
- package/dist/services/turso/operation-lock.d.ts +2 -0
- package/dist/services/turso/operation-lock.js +61 -0
- package/dist/services/turso/ready.d.ts +2 -0
- package/dist/services/turso/ready.js +36 -0
- package/dist/services/turso/shard-manager.d.ts +38 -0
- package/dist/services/turso/shard-manager.js +496 -0
- package/dist/services/turso/sqlite-handle-release.d.ts +16 -0
- package/dist/services/turso/sqlite-handle-release.js +56 -0
- package/dist/services/turso/turso-db.d.ts +18 -0
- package/dist/services/turso/turso-db.js +53 -0
- package/dist/services/turso/types.d.ts +42 -0
- package/dist/services/turso/types.js +1 -0
- package/dist/services/turso/vector-search.d.ts +41 -0
- package/dist/services/turso/vector-search.js +384 -0
- package/dist/services/turso/vector-utils.d.ts +7 -0
- package/dist/services/turso/vector-utils.js +67 -0
- package/dist/services/user-memory-learning.d.ts +97 -0
- package/dist/services/user-memory-learning.js +673 -0
- package/dist/services/user-profile/ai-cleanup.d.ts +58 -0
- package/dist/services/user-profile/ai-cleanup.js +486 -0
- package/dist/services/user-profile/profile-context.d.ts +1 -0
- package/dist/services/user-profile/profile-context.js +105 -0
- package/dist/services/user-profile/profile-utils.d.ts +2 -0
- package/dist/services/user-profile/profile-utils.js +45 -0
- package/dist/services/user-profile/types.d.ts +92 -0
- package/dist/services/user-profile/types.js +1 -0
- package/dist/services/user-profile/user-profile-manager.d.ts +65 -0
- package/dist/services/user-profile/user-profile-manager.js +1660 -0
- package/dist/services/user-prompt/user-prompt-manager.d.ts +53 -0
- package/dist/services/user-prompt/user-prompt-manager.js +303 -0
- package/dist/services/web-api-auth.d.ts +3 -0
- package/dist/services/web-api-auth.js +25 -0
- package/dist/services/web-auth.d.ts +46 -0
- package/dist/services/web-auth.js +105 -0
- package/dist/services/web-server.d.ts +57 -0
- package/dist/services/web-server.js +659 -0
- package/dist/shared/api/index.d.ts +4 -0
- package/dist/shared/api/index.js +2 -0
- package/dist/shared/api/portability-schemas.d.ts +56 -0
- package/dist/shared/api/portability-schemas.js +39 -0
- package/dist/shared/api/schemas.d.ts +295 -0
- package/dist/shared/api/schemas.js +134 -0
- package/dist/shared/api/types.d.ts +28 -0
- package/dist/shared/api/types.js +1 -0
- package/dist/types/index.d.ts +27 -0
- package/dist/types/index.js +1 -0
- package/dist/utils/context-limit.d.ts +10 -0
- package/dist/utils/context-limit.js +43 -0
- package/dist/utils/math.d.ts +7 -0
- package/dist/utils/math.js +48 -0
- package/dist/utils/profile.d.ts +2 -0
- package/dist/utils/profile.js +15 -0
- package/dist/v2/adapter.d.ts +5 -0
- package/dist/v2/adapter.js +218 -0
- package/dist/v2/legacy-client.d.ts +15 -0
- package/dist/v2/legacy-client.js +257 -0
- package/dist/v2/memory-bridge.d.ts +18 -0
- package/dist/v2/memory-bridge.js +29 -0
- package/dist/v2/plugin.d.ts +3 -0
- package/dist/v2/plugin.js +24 -0
- package/dist/web/assets/index-DPjEpD1h.css +2 -0
- package/dist/web/assets/index-do26c4JJ.js +122 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
- package/dist/web/favicon.ico +0 -0
- package/dist/web/index.html +14 -0
- package/dist/web/omms-icon.png +0 -0
- package/package.json +138 -0
package/dist/config.js
ADDED
|
@@ -0,0 +1,742 @@
|
|
|
1
|
+
import { existsSync, readFileSync, mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { stripJsoncComments } from "./services/jsonc.js";
|
|
5
|
+
import { log } from "./services/logger.js";
|
|
6
|
+
import { resolveSecretValue } from "./services/secret-resolver.js";
|
|
7
|
+
import { isPlaceholderApiKey } from "./services/ai/api-key-placeholder.js";
|
|
8
|
+
import { resolveDefaultStoragePath, runLegacyStoreMigration, legacyMigrationPaths, } from "./services/legacy-migration.js";
|
|
9
|
+
const OMMS_CONFIG_DIR = join(homedir(), ".config", "omms");
|
|
10
|
+
const DATA_DIR = join(homedir(), ".omms");
|
|
11
|
+
const LEGACY_CONFIG_FILES = [
|
|
12
|
+
join(homedir(), ".config", "opencode", "opencode-mem.jsonc"),
|
|
13
|
+
join(homedir(), ".config", "opencode", "opencode-mem.json"),
|
|
14
|
+
];
|
|
15
|
+
// Dual-read: the omms config wins; the legacy opencode-mem config is read
|
|
16
|
+
// only when no omms config file exists (decision D13 rule 6). The legacy
|
|
17
|
+
// file is never written.
|
|
18
|
+
const CONFIG_FILES = [
|
|
19
|
+
join(OMMS_CONFIG_DIR, "omms.jsonc"),
|
|
20
|
+
join(OMMS_CONFIG_DIR, "omms.json"),
|
|
21
|
+
...LEGACY_CONFIG_FILES,
|
|
22
|
+
];
|
|
23
|
+
if (!existsSync(OMMS_CONFIG_DIR)) {
|
|
24
|
+
mkdirSync(OMMS_CONFIG_DIR, { recursive: true });
|
|
25
|
+
}
|
|
26
|
+
if (!existsSync(DATA_DIR)) {
|
|
27
|
+
mkdirSync(DATA_DIR, { recursive: true });
|
|
28
|
+
}
|
|
29
|
+
const DEFAULTS = {
|
|
30
|
+
// Default store: ~/.omms/data (see resolveDefaultStoragePath for the
|
|
31
|
+
// legacy-layout fallback while a legacy store exists unmigrated).
|
|
32
|
+
storagePath: join(DATA_DIR, "data"),
|
|
33
|
+
embeddingModel: "Xenova/nomic-embed-text-v1",
|
|
34
|
+
embeddingDimensions: 768,
|
|
35
|
+
embeddingUseTaskPrefixes: false,
|
|
36
|
+
similarityThreshold: 0.6,
|
|
37
|
+
maxMemories: 10,
|
|
38
|
+
maxProfileItems: 5,
|
|
39
|
+
injectProfile: true,
|
|
40
|
+
// omms since the tag prefix migration: new memories carry the fork's
|
|
41
|
+
// identity end to end, and the startup gate (tag-prefix-migration.ts)
|
|
42
|
+
// rewrites any remaining opencode_ rows before the first write is served.
|
|
43
|
+
containerTagPrefix: "omms",
|
|
44
|
+
autoCaptureEnabled: true,
|
|
45
|
+
autoCaptureMaxIterations: 5,
|
|
46
|
+
autoCaptureIterationTimeout: 30000,
|
|
47
|
+
autoCaptureMaxRetries: 3,
|
|
48
|
+
autoCaptureMaxContextBytes: 131072,
|
|
49
|
+
aiSessionRetentionDays: 7,
|
|
50
|
+
webServerEnabled: true,
|
|
51
|
+
webServerPort: 4747,
|
|
52
|
+
webServerHost: "127.0.0.1",
|
|
53
|
+
maxVectorsPerShard: 50000,
|
|
54
|
+
autoCleanupEnabled: true,
|
|
55
|
+
autoCleanupRetentionDays: 30,
|
|
56
|
+
deduplicationEnabled: true,
|
|
57
|
+
deduplicationSimilarityThreshold: 0.9,
|
|
58
|
+
userProfileAnalysisInterval: 10,
|
|
59
|
+
userProfileMaxContextBytes: 32768,
|
|
60
|
+
userProfileDisplayPreferences: 20,
|
|
61
|
+
userProfileDisplayPatterns: 15,
|
|
62
|
+
userProfileDisplayWorkflows: 10,
|
|
63
|
+
userProfileStaleDays: 2,
|
|
64
|
+
userProfileInjectPreferences: 5,
|
|
65
|
+
userProfileInjectPatterns: 5,
|
|
66
|
+
userProfileInjectWorkflows: 3,
|
|
67
|
+
userProfileConfidenceDecayDays: 30,
|
|
68
|
+
userProfileChangelogRetentionCount: 5,
|
|
69
|
+
userProfileEmbeddingThresholdSameCat: 0.8,
|
|
70
|
+
userProfileEmbeddingThresholdSameCatWeak: 0.5,
|
|
71
|
+
userProfileEmbeddingThresholdCrossCat: 0.9,
|
|
72
|
+
userProfileEmbeddingThresholdCrossCatWeak: 0.8,
|
|
73
|
+
userProfileCentroidDriftThreshold: 0.65,
|
|
74
|
+
userProfileEmbeddingMinDescriptionLength: 5,
|
|
75
|
+
userProfileMinEvidenceForRetention: 3,
|
|
76
|
+
userProfileAutoCleanupEnabled: true,
|
|
77
|
+
userProfileAutoCleanupInterval: 100,
|
|
78
|
+
userProfileValidationEnabled: false,
|
|
79
|
+
showAutoCaptureToasts: true,
|
|
80
|
+
showUserProfileToasts: true,
|
|
81
|
+
showErrorToasts: true,
|
|
82
|
+
memory: {
|
|
83
|
+
defaultScope: "project",
|
|
84
|
+
},
|
|
85
|
+
compaction: {
|
|
86
|
+
enabled: true,
|
|
87
|
+
memoryLimit: 10,
|
|
88
|
+
},
|
|
89
|
+
chatMessage: {
|
|
90
|
+
enabled: true,
|
|
91
|
+
maxMemories: 3,
|
|
92
|
+
excludeCurrentSession: true,
|
|
93
|
+
maxAgeDays: undefined,
|
|
94
|
+
injectOn: "first",
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
function expandPath(path) {
|
|
98
|
+
if (path.startsWith("~/")) {
|
|
99
|
+
return join(homedir(), path.slice(2));
|
|
100
|
+
}
|
|
101
|
+
if (path === "~") {
|
|
102
|
+
return homedir();
|
|
103
|
+
}
|
|
104
|
+
return path;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Load the first config file that exists, in priority order. A file that
|
|
108
|
+
* exists but cannot be read or parsed is not skipped: falling through would let
|
|
109
|
+
* a lower-priority (legacy) file silently supply settings such as storagePath.
|
|
110
|
+
*/
|
|
111
|
+
function loadConfigFromPaths(paths) {
|
|
112
|
+
const path = paths.find((candidate) => existsSync(candidate));
|
|
113
|
+
if (!path)
|
|
114
|
+
return {};
|
|
115
|
+
try {
|
|
116
|
+
const content = readFileSync(path, "utf-8");
|
|
117
|
+
const json = stripJsoncComments(content);
|
|
118
|
+
return JSON.parse(json);
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
log("Config file is invalid; using defaults instead of lower-priority files", {
|
|
122
|
+
path,
|
|
123
|
+
error: String(error),
|
|
124
|
+
});
|
|
125
|
+
return {};
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const GLOBAL_ONLY_REMOTE_PROVIDER_FIELDS = [
|
|
129
|
+
"embeddingApiUrl",
|
|
130
|
+
"embeddingApiKey",
|
|
131
|
+
"memoryProvider",
|
|
132
|
+
"memoryApiUrl",
|
|
133
|
+
"memoryApiKey",
|
|
134
|
+
];
|
|
135
|
+
function assertProjectRemoteProviderConfigIsSafe(projectConfig) {
|
|
136
|
+
const configuredFields = GLOBAL_ONLY_REMOTE_PROVIDER_FIELDS.filter((field) => Object.prototype.hasOwnProperty.call(projectConfig, field));
|
|
137
|
+
if (configuredFields.length > 0) {
|
|
138
|
+
throw new Error(`Project config cannot set remote provider fields: ${configuredFields.join(", ")}. ` +
|
|
139
|
+
`Move them to the global config at ${CONFIG_FILES[0]}.`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
const CONFIG_TEMPLATE = `{
|
|
143
|
+
// ============================================
|
|
144
|
+
// omms (Opinionated Modular Memory System) Configuration
|
|
145
|
+
// ============================================
|
|
146
|
+
|
|
147
|
+
// Storage location for the vector database. Leave unset to use the
|
|
148
|
+
// default ~/.omms/data (a legacy ~/.opencode-mem/data store is migrated
|
|
149
|
+
// there automatically on first start; see docs/omms-migration.md).
|
|
150
|
+
// "storagePath": "~/.omms/data",
|
|
151
|
+
|
|
152
|
+
"userEmailOverride": "",
|
|
153
|
+
"userNameOverride": "",
|
|
154
|
+
|
|
155
|
+
// ============================================
|
|
156
|
+
// Embedding Model (for similarity search)
|
|
157
|
+
// ============================================
|
|
158
|
+
// Local = Hugging Face / ONNX via @huggingface/transformers (not Apple MLX).
|
|
159
|
+
// Remote = set BOTH embeddingApiUrl and embeddingApiKey (OpenAI-compatible /embeddings).
|
|
160
|
+
|
|
161
|
+
// Default: Nomic Embed v1 (768 dimensions, 8192 context, multilingual)
|
|
162
|
+
"embeddingModel": "Xenova/nomic-embed-text-v1",
|
|
163
|
+
|
|
164
|
+
// Opt-in Nomic task prefixes (search_document: / search_query:). After enabling,
|
|
165
|
+
// re-index existing memories so store and query vectors stay aligned.
|
|
166
|
+
// "embeddingUseTaskPrefixes": true,
|
|
167
|
+
|
|
168
|
+
// Auto-detected dimensions (no need to set manually)
|
|
169
|
+
// "embeddingDimensions": 768,
|
|
170
|
+
|
|
171
|
+
// Other recommended local models:
|
|
172
|
+
// "embeddingModel": "Xenova/jina-embeddings-v2-base-en", // 768 dims, English-only, 8192 context
|
|
173
|
+
// "embeddingModel": "Xenova/jina-embeddings-v2-small-en", // 512 dims, faster, 8192 context
|
|
174
|
+
// "embeddingModel": "Xenova/all-MiniLM-L6-v2", // 384 dims, very fast, 512 context
|
|
175
|
+
// "embeddingModel": "Xenova/all-mpnet-base-v2", // 768 dims, good quality, 512 context
|
|
176
|
+
|
|
177
|
+
// Optional: OpenAI-compatible API for embeddings (both URL and key required)
|
|
178
|
+
// "embeddingApiUrl": "https://api.openai.com/v1",
|
|
179
|
+
// "embeddingApiKey": "env://OPENAI_API_KEY", // or "sk-..." / "file:///path/to/key"
|
|
180
|
+
// "embeddingModel": "text-embedding-3-small", // 1536 dims, auto-detected
|
|
181
|
+
|
|
182
|
+
// ============================================
|
|
183
|
+
// Web Server Settings
|
|
184
|
+
// ============================================
|
|
185
|
+
|
|
186
|
+
// Enable web UI for managing memories (accessible at http://localhost:4747)
|
|
187
|
+
"webServerEnabled": true,
|
|
188
|
+
|
|
189
|
+
// Port for web UI server
|
|
190
|
+
"webServerPort": 4747,
|
|
191
|
+
|
|
192
|
+
// Host address for web UI (use 127.0.0.1 for local only, 0.0.0.0 for network access)
|
|
193
|
+
"webServerHost": "127.0.0.1",
|
|
194
|
+
|
|
195
|
+
// Optional HTTP Basic Auth for the web UI. Accepts literal, env://, or file:// secrets.
|
|
196
|
+
// "webServerAuthPassword": "",
|
|
197
|
+
// "webServerAuthUsername": "",
|
|
198
|
+
|
|
199
|
+
// Required when webServerHost is not loopback. Protects /api/* with Bearer / X-Opencode-Mem-Token.
|
|
200
|
+
// "webServerApiToken": "env://OMMS_WEB_TOKEN",
|
|
201
|
+
|
|
202
|
+
// ============================================
|
|
203
|
+
// Database Settings
|
|
204
|
+
// ============================================
|
|
205
|
+
|
|
206
|
+
// Maximum vectors per database shard (auto-creates new shard when limit reached)
|
|
207
|
+
"maxVectorsPerShard": 50000,
|
|
208
|
+
|
|
209
|
+
// Automatically delete old memories based on retention period
|
|
210
|
+
"autoCleanupEnabled": true,
|
|
211
|
+
|
|
212
|
+
// Days to keep memories before auto-cleanup (only if autoCleanupEnabled is true)
|
|
213
|
+
"autoCleanupRetentionDays": 30,
|
|
214
|
+
|
|
215
|
+
// Automatically detect and remove duplicate memories
|
|
216
|
+
"deduplicationEnabled": true,
|
|
217
|
+
|
|
218
|
+
// Similarity threshold (0-1) for detecting duplicates (higher = stricter)
|
|
219
|
+
"deduplicationSimilarityThreshold": 0.90,
|
|
220
|
+
|
|
221
|
+
// ============================================
|
|
222
|
+
// Memory Scope Settings
|
|
223
|
+
// ============================================
|
|
224
|
+
|
|
225
|
+
// Default scope for memory list/search queries
|
|
226
|
+
// "project" keeps queries within the current project, "all-projects" searches across all project shards
|
|
227
|
+
"memory": {
|
|
228
|
+
"defaultScope": "project"
|
|
229
|
+
},
|
|
230
|
+
|
|
231
|
+
// ============================================
|
|
232
|
+
// OpenCode Provider Settings (RECOMMENDED)
|
|
233
|
+
// ============================================
|
|
234
|
+
|
|
235
|
+
// Use any provider that is already authenticated in opencode for auto-capture
|
|
236
|
+
// and user profile learning. The plugin calls opencode's session.prompt API
|
|
237
|
+
// (with structured output) instead of talking to provider HTTPS endpoints
|
|
238
|
+
// directly, so opencode owns the auth, token refresh, and provider routing.
|
|
239
|
+
//
|
|
240
|
+
// No separate API key is needed in this plugin — whatever you configured in
|
|
241
|
+
// opencode (OAuth like Claude Pro/Max, GitHub Copilot personal/business,
|
|
242
|
+
// bring-your-own API key, custom provider, ...) just works.
|
|
243
|
+
//
|
|
244
|
+
// If NOT set, falls back to the manual config (memoryApiKey/memoryApiUrl/memoryModel below).
|
|
245
|
+
//
|
|
246
|
+
// Examples (the provider name must be one returned by 'opencode providers list'):
|
|
247
|
+
// Anthropic (OAuth/API key): "opencodeProvider": "anthropic", "opencodeModel": "claude-haiku-4-5-20251001"
|
|
248
|
+
// OpenAI (API key): "opencodeProvider": "openai", "opencodeModel": "gpt-4o-mini"
|
|
249
|
+
// GitHub Copilot: "opencodeProvider": "github-copilot", "opencodeModel": "gpt-4o-mini"
|
|
250
|
+
//
|
|
251
|
+
// "opencodeProvider": "anthropic",
|
|
252
|
+
// "opencodeModel": "claude-haiku-4-5-20251001",
|
|
253
|
+
|
|
254
|
+
// ============================================
|
|
255
|
+
// Auto-Capture Settings
|
|
256
|
+
// ============================================
|
|
257
|
+
|
|
258
|
+
// IMPORTANT: Auto-capture only runs after either opencodeProvider/opencodeModel
|
|
259
|
+
// above is configured, or the manual fallback below is uncommented with real values.
|
|
260
|
+
// It runs in background without blocking your main session
|
|
261
|
+
// Note: Ollama may not support tool calling. Use OpenAI, Anthropic, or Groq for best results.
|
|
262
|
+
|
|
263
|
+
"autoCaptureEnabled": true,
|
|
264
|
+
|
|
265
|
+
// Provider type: "openai-chat" | "openai-responses" | "anthropic" | "minimax" | "orcarouter"
|
|
266
|
+
// Note: "openai-chat" is a generic OpenAI API-compatible mode.
|
|
267
|
+
// Any service that follows the OpenAI Chat Completions API can use it via custom "memoryApiUrl".
|
|
268
|
+
"memoryProvider": "openai-chat",
|
|
269
|
+
|
|
270
|
+
// Manual fallback. Uncomment all 3 lines and replace memoryApiKey before use:
|
|
271
|
+
// "memoryModel": "gpt-4o-mini",
|
|
272
|
+
// "memoryApiUrl": "https://api.openai.com/v1",
|
|
273
|
+
// "memoryApiKey": "sk-...",
|
|
274
|
+
|
|
275
|
+
// API Key Formats:
|
|
276
|
+
// Direct value: "sk-..."
|
|
277
|
+
// From file: "file://~/.config/litellm-key.txt"
|
|
278
|
+
// From env variable: "env://LITELLM_API_KEY"
|
|
279
|
+
|
|
280
|
+
// Examples for different providers:
|
|
281
|
+
// Any OpenAI-compatible endpoint can use the "openai-chat" provider pattern below.
|
|
282
|
+
// Common examples: DeepSeek, Qwen (via Alibaba Cloud ModelStudio),
|
|
283
|
+
// Zhipu GLM (BigModel platform), and Kimi (Moonshot AI platform).
|
|
284
|
+
|
|
285
|
+
// OpenAI Chat Completion (default, backward compatible):
|
|
286
|
+
// "memoryProvider": "openai-chat"
|
|
287
|
+
// "memoryModel": "gpt-4o-mini"
|
|
288
|
+
// "memoryApiUrl": "https://api.openai.com/v1"
|
|
289
|
+
// "memoryApiKey": "sk-..."
|
|
290
|
+
|
|
291
|
+
// DeepSeek (OpenAI-compatible example):
|
|
292
|
+
// "memoryProvider": "openai-chat"
|
|
293
|
+
// "memoryModel": "deepseek-chat"
|
|
294
|
+
// "memoryApiUrl": "https://api.deepseek.com/v1"
|
|
295
|
+
// "memoryApiKey": "sk-..."
|
|
296
|
+
|
|
297
|
+
// OpenAI Responses API (recommended, with session support):
|
|
298
|
+
// "memoryProvider": "openai-responses"
|
|
299
|
+
// "memoryModel": "gpt-4o"
|
|
300
|
+
// "memoryApiUrl": "https://api.openai.com/v1"
|
|
301
|
+
// "memoryApiKey": "sk-..."
|
|
302
|
+
|
|
303
|
+
// Anthropic (with session support):
|
|
304
|
+
// "memoryProvider": "anthropic"
|
|
305
|
+
// "memoryModel": "claude-3-5-haiku-20241022"
|
|
306
|
+
// "memoryApiUrl": "https://api.anthropic.com/v1"
|
|
307
|
+
// "memoryApiKey": "sk-ant-..."
|
|
308
|
+
|
|
309
|
+
// MiniMax (Anthropic Messages-compatible endpoint, with session support):
|
|
310
|
+
// "memoryProvider": "minimax"
|
|
311
|
+
// "memoryModel": "MiniMax-M3"
|
|
312
|
+
// "memoryApiUrl": "https://api.minimax.io" // global endpoint
|
|
313
|
+
// "memoryApiKey": "<MiniMax API key>"
|
|
314
|
+
// // China endpoint: "memoryApiUrl": "https://api.minimaxi.com"
|
|
315
|
+
// // Optional adaptive thinking for MiniMax-M3:
|
|
316
|
+
// "memoryExtraParams": { "thinking": { "type": "adaptive" } }
|
|
317
|
+
|
|
318
|
+
// OrcaRouter (OpenAI-compatible gateway, namespaced model IDs, with session support):
|
|
319
|
+
// "memoryProvider": "orcarouter"
|
|
320
|
+
// "memoryApiKey": "<OrcaRouter API key>"
|
|
321
|
+
// // memoryApiUrl and memoryModel are optional — they default to
|
|
322
|
+
// // https://api.orcarouter.ai/v1 and "orcarouter/auto" (a routing alias).
|
|
323
|
+
// // OrcaRouter rejects bare model names, so if you set memoryModel, use a
|
|
324
|
+
// // namespaced ID such as "openai/gpt-5.5" or "deepseek/deepseek-v4-flash".
|
|
325
|
+
// "memoryModel": "openai/gpt-5.5"
|
|
326
|
+
|
|
327
|
+
// Groq (OpenAI-compatible, use openai-chat provider):
|
|
328
|
+
// "memoryProvider": "openai-chat"
|
|
329
|
+
// "memoryModel": "llama-3.3-70b-versatile"
|
|
330
|
+
// "memoryApiUrl": "https://api.groq.com/openai/v1"
|
|
331
|
+
// "memoryApiKey": "gsk_..."
|
|
332
|
+
|
|
333
|
+
// Maximum iterations for multi-turn AI analysis (for openai-responses, anthropic, and minimax)
|
|
334
|
+
"autoCaptureMaxIterations": 5,
|
|
335
|
+
|
|
336
|
+
// Timeout per iteration in milliseconds (30 seconds default)
|
|
337
|
+
"autoCaptureIterationTimeout": 30000,
|
|
338
|
+
|
|
339
|
+
// Maximum number of times to retry capturing a prompt if it fails (due to network, API errors, etc.)
|
|
340
|
+
"autoCaptureMaxRetries": 3,
|
|
341
|
+
|
|
342
|
+
// Maximum UTF-8 bytes for the auto-capture markdown context sent to the summary model.
|
|
343
|
+
// Prevents HTTP 400 context overflows on models with ~131K token windows (e.g. Groq Llama).
|
|
344
|
+
// Rough guide: tokens ≈ bytes / 4 for mixed code/prose.
|
|
345
|
+
"autoCaptureMaxContextBytes": 131072,
|
|
346
|
+
|
|
347
|
+
// Days to keep AI session history before cleanup
|
|
348
|
+
"aiSessionRetentionDays": 7,
|
|
349
|
+
|
|
350
|
+
// Temperature for AI API requests (set to false to omit parameter for models that don't support it)
|
|
351
|
+
// Some reasoning models (like o1, o3, gpt-5) don't support temperature parameter
|
|
352
|
+
// Set to false and add "memoryTemperature": false in config when using such models
|
|
353
|
+
"memoryTemperature": 0.3,
|
|
354
|
+
|
|
355
|
+
// Extra parameters to include in API request body
|
|
356
|
+
// Useful for local inference servers (e.g. llama-server with --jinja) that support
|
|
357
|
+
// additional parameters like disabling thinking/reasoning mode
|
|
358
|
+
// Example for Qwen3 models: { "enable_thinking": false }
|
|
359
|
+
// "memoryExtraParams": {},
|
|
360
|
+
|
|
361
|
+
// Language for auto-capture summaries (default: "auto" for auto-detection)
|
|
362
|
+
// Options: "auto", "en", "id", "zh", "ja", "es", "fr", "de", "ru", "pt", "ar", "ko"
|
|
363
|
+
// "autoCaptureLanguage": "auto",
|
|
364
|
+
|
|
365
|
+
// ============================================
|
|
366
|
+
// Toast Notifications
|
|
367
|
+
// ============================================
|
|
368
|
+
|
|
369
|
+
// Show toast when memory is auto-captured
|
|
370
|
+
"showAutoCaptureToasts": true,
|
|
371
|
+
|
|
372
|
+
// Show toast when user profile is updated
|
|
373
|
+
"showUserProfileToasts": true,
|
|
374
|
+
|
|
375
|
+
// Show toast for error messages
|
|
376
|
+
"showErrorToasts": true,
|
|
377
|
+
|
|
378
|
+
// ============================================
|
|
379
|
+
// User Profile System
|
|
380
|
+
// ============================================
|
|
381
|
+
|
|
382
|
+
// Analyze user prompts every N prompts to build/update your user profile
|
|
383
|
+
// When N uncaptured prompts accumulate, AI will analyze them to identify:
|
|
384
|
+
// - User preferences (code style, communication style, tool preferences)
|
|
385
|
+
// - User patterns (recurring topics, problem domains, technical interests)
|
|
386
|
+
// - User workflows (development habits, sequences, learning style)
|
|
387
|
+
// - Skill level (overall and per-domain assessment)
|
|
388
|
+
"userProfileAnalysisInterval": 10,
|
|
389
|
+
|
|
390
|
+
// Days before inactive items (all types) are eligible for removal
|
|
391
|
+
"userProfileStaleDays": 2,
|
|
392
|
+
|
|
393
|
+
// Number of preferences shown in UI
|
|
394
|
+
"userProfileDisplayPreferences": 20,
|
|
395
|
+
|
|
396
|
+
// Number of patterns shown in UI
|
|
397
|
+
"userProfileDisplayPatterns": 15,
|
|
398
|
+
|
|
399
|
+
// Number of workflows shown in UI
|
|
400
|
+
"userProfileDisplayWorkflows": 10,
|
|
401
|
+
|
|
402
|
+
// Number of preferences injected into LLM conversation context
|
|
403
|
+
// Keep this small — the strongest signals are enough; more dilute LLM attention
|
|
404
|
+
"userProfileInjectPreferences": 5,
|
|
405
|
+
|
|
406
|
+
// Number of patterns injected into LLM conversation context
|
|
407
|
+
"userProfileInjectPatterns": 5,
|
|
408
|
+
|
|
409
|
+
// Number of workflows injected into LLM conversation context
|
|
410
|
+
"userProfileInjectWorkflows": 3,
|
|
411
|
+
|
|
412
|
+
// Days before preference confidence starts to decay (if not reinforced)
|
|
413
|
+
// Preferences that aren't seen again will gradually lose confidence and be removed
|
|
414
|
+
"userProfileConfidenceDecayDays": 30,
|
|
415
|
+
|
|
416
|
+
// Number of profile versions to keep in changelog (for rollback/debugging)
|
|
417
|
+
// Older versions are automatically cleaned up
|
|
418
|
+
"userProfileChangelogRetentionCount": 5,
|
|
419
|
+
|
|
420
|
+
// Minimum evidence count for a preference/pattern to survive confidence decay
|
|
421
|
+
// Items confirmed fewer times are more likely to be pruned when confidence decays
|
|
422
|
+
"userProfileMinEvidenceForRetention": 3,
|
|
423
|
+
|
|
424
|
+
// Periodically merge duplicate or irrelevant profile items with the configured AI provider
|
|
425
|
+
"userProfileAutoCleanupEnabled": true,
|
|
426
|
+
// Number of analyzed user prompts between automatic AI cleanup runs
|
|
427
|
+
"userProfileAutoCleanupInterval": 100,
|
|
428
|
+
|
|
429
|
+
// Enable LLM validation of existing preferences against recent behavior.
|
|
430
|
+
// When enabled, each analysis round checks if top-5 preferences still match recent prompts.
|
|
431
|
+
// Experimental — disabled by default.
|
|
432
|
+
"userProfileValidationEnabled": false,
|
|
433
|
+
|
|
434
|
+
// ============================================
|
|
435
|
+
// Search Settings
|
|
436
|
+
// ============================================
|
|
437
|
+
|
|
438
|
+
// Minimum similarity score (0-1) for memory search results
|
|
439
|
+
"similarityThreshold": 0.6,
|
|
440
|
+
|
|
441
|
+
// Maximum number of memories to return in search results
|
|
442
|
+
"maxMemories": 10,
|
|
443
|
+
|
|
444
|
+
// ============================================
|
|
445
|
+
// Advanced Settings
|
|
446
|
+
// ============================================
|
|
447
|
+
|
|
448
|
+
// Inject user profile into AI context (preferences, patterns, workflows)
|
|
449
|
+
"injectProfile": true
|
|
450
|
+
}
|
|
451
|
+
`;
|
|
452
|
+
function ensureConfigExists() {
|
|
453
|
+
const configPath = join(OMMS_CONFIG_DIR, "omms.jsonc");
|
|
454
|
+
const hasOmmsConfig = CONFIG_FILES.slice(0, 2).some((path) => existsSync(path));
|
|
455
|
+
const hasLegacyConfig = LEGACY_CONFIG_FILES.some((path) => existsSync(path));
|
|
456
|
+
// The template is created only when NO config exists at all. When a legacy
|
|
457
|
+
// opencode-mem config is present, dual-read keeps using it and no omms
|
|
458
|
+
// config file is written (writing one would permanently shadow the legacy
|
|
459
|
+
// fallback on the next start).
|
|
460
|
+
if (hasOmmsConfig || hasLegacyConfig) {
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
try {
|
|
464
|
+
writeFileSync(configPath, CONFIG_TEMPLATE, "utf-8");
|
|
465
|
+
console.log(`\n✓ Created config template: ${configPath}`);
|
|
466
|
+
console.log(" Edit this file to customise omms settings.\n");
|
|
467
|
+
}
|
|
468
|
+
catch {
|
|
469
|
+
// ignore if the template cannot be written
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
ensureConfigExists();
|
|
473
|
+
function getEmbeddingDimensions(model) {
|
|
474
|
+
const dimensionMap = {
|
|
475
|
+
// Local Xenova models
|
|
476
|
+
"Xenova/nomic-embed-text-v1": 768,
|
|
477
|
+
"Xenova/nomic-embed-text-v1-unsupervised": 768,
|
|
478
|
+
"Xenova/nomic-embed-text-v1-ablated": 768,
|
|
479
|
+
"Xenova/jina-embeddings-v2-base-en": 768,
|
|
480
|
+
"Xenova/jina-embeddings-v2-base-zh": 768,
|
|
481
|
+
"Xenova/jina-embeddings-v2-base-de": 768,
|
|
482
|
+
"Xenova/jina-embeddings-v2-small-en": 512,
|
|
483
|
+
"Xenova/all-MiniLM-L6-v2": 384,
|
|
484
|
+
"Xenova/all-MiniLM-L12-v2": 384,
|
|
485
|
+
"Xenova/all-mpnet-base-v2": 768,
|
|
486
|
+
"Xenova/bge-base-en-v1.5": 768,
|
|
487
|
+
"Xenova/bge-small-en-v1.5": 384,
|
|
488
|
+
"Xenova/gte-small": 384,
|
|
489
|
+
"Xenova/GIST-small-Embedding-v0": 384,
|
|
490
|
+
"Xenova/text-embedding-ada-002": 1536,
|
|
491
|
+
// OpenAI API models
|
|
492
|
+
"text-embedding-3-small": 1536,
|
|
493
|
+
"text-embedding-3-large": 3072,
|
|
494
|
+
"text-embedding-ada-002": 1536,
|
|
495
|
+
// Cohere API models
|
|
496
|
+
"embed-english-v3.0": 1024,
|
|
497
|
+
"embed-multilingual-v3.0": 1024,
|
|
498
|
+
"embed-english-light-v3.0": 384,
|
|
499
|
+
"embed-multilingual-light-v3.0": 384,
|
|
500
|
+
// Google API models
|
|
501
|
+
"text-embedding-004": 768,
|
|
502
|
+
"text-multilingual-embedding-002": 768,
|
|
503
|
+
// Voyage AI models
|
|
504
|
+
"voyage-3": 1024,
|
|
505
|
+
"voyage-3-lite": 512,
|
|
506
|
+
"voyage-code-3": 1024,
|
|
507
|
+
};
|
|
508
|
+
return dimensionMap[model] || 768;
|
|
509
|
+
}
|
|
510
|
+
export function normalizeAutoCaptureMaxContextBytes(value) {
|
|
511
|
+
if (!Number.isInteger(value) || value < 16384 || value > 16 * 1024 * 1024) {
|
|
512
|
+
throw new Error(`Invalid autoCaptureMaxContextBytes config: ${value}`);
|
|
513
|
+
}
|
|
514
|
+
return value;
|
|
515
|
+
}
|
|
516
|
+
export function normalizeAutoCleanupRetentionDays(value) {
|
|
517
|
+
if (!Number.isSafeInteger(value) || value <= 0) {
|
|
518
|
+
throw new Error(`Invalid autoCleanupRetentionDays config: ${value}`);
|
|
519
|
+
}
|
|
520
|
+
return value;
|
|
521
|
+
}
|
|
522
|
+
function buildConfig(fileConfig) {
|
|
523
|
+
const memoryApiKey = resolveSecretValue(fileConfig.memoryApiKey);
|
|
524
|
+
const embeddingDimensions = fileConfig.embeddingDimensions ??
|
|
525
|
+
getEmbeddingDimensions(fileConfig.embeddingModel ?? DEFAULTS.embeddingModel);
|
|
526
|
+
const autoCaptureMaxContextBytes = normalizeAutoCaptureMaxContextBytes(fileConfig.autoCaptureMaxContextBytes ?? DEFAULTS.autoCaptureMaxContextBytes);
|
|
527
|
+
const userProfileAutoCleanupInterval = fileConfig.userProfileAutoCleanupInterval ?? DEFAULTS.userProfileAutoCleanupInterval;
|
|
528
|
+
if (!Number.isInteger(embeddingDimensions) ||
|
|
529
|
+
embeddingDimensions <= 0 ||
|
|
530
|
+
embeddingDimensions > 65536) {
|
|
531
|
+
throw new Error(`Invalid embeddingDimensions config: ${embeddingDimensions}`);
|
|
532
|
+
}
|
|
533
|
+
if (!Number.isInteger(userProfileAutoCleanupInterval) || userProfileAutoCleanupInterval <= 0) {
|
|
534
|
+
throw new Error(`Invalid userProfileAutoCleanupInterval config: ${userProfileAutoCleanupInterval}`);
|
|
535
|
+
}
|
|
536
|
+
return {
|
|
537
|
+
storagePath: fileConfig.storagePath
|
|
538
|
+
? expandPath(fileConfig.storagePath)
|
|
539
|
+
: resolveDefaultStoragePath(),
|
|
540
|
+
userEmailOverride: fileConfig.userEmailOverride,
|
|
541
|
+
userNameOverride: fileConfig.userNameOverride,
|
|
542
|
+
embeddingModel: fileConfig.embeddingModel ?? DEFAULTS.embeddingModel,
|
|
543
|
+
embeddingDimensions,
|
|
544
|
+
embeddingUseTaskPrefixes: fileConfig.embeddingUseTaskPrefixes ?? DEFAULTS.embeddingUseTaskPrefixes,
|
|
545
|
+
embeddingApiUrl: fileConfig.embeddingApiUrl,
|
|
546
|
+
embeddingApiKey: fileConfig.embeddingApiUrl
|
|
547
|
+
? resolveSecretValue(fileConfig.embeddingApiKey ?? process.env.OPENAI_API_KEY)
|
|
548
|
+
: undefined,
|
|
549
|
+
similarityThreshold: fileConfig.similarityThreshold ?? DEFAULTS.similarityThreshold,
|
|
550
|
+
maxMemories: fileConfig.maxMemories ?? DEFAULTS.maxMemories,
|
|
551
|
+
maxProfileItems: fileConfig.maxProfileItems ?? DEFAULTS.maxProfileItems,
|
|
552
|
+
injectProfile: fileConfig.injectProfile ?? DEFAULTS.injectProfile,
|
|
553
|
+
containerTagPrefix: fileConfig.containerTagPrefix ?? DEFAULTS.containerTagPrefix,
|
|
554
|
+
autoCaptureEnabled: fileConfig.autoCaptureEnabled ?? DEFAULTS.autoCaptureEnabled,
|
|
555
|
+
autoCaptureMaxIterations: fileConfig.autoCaptureMaxIterations ?? DEFAULTS.autoCaptureMaxIterations,
|
|
556
|
+
autoCaptureIterationTimeout: fileConfig.autoCaptureIterationTimeout ?? DEFAULTS.autoCaptureIterationTimeout,
|
|
557
|
+
autoCaptureMaxRetries: fileConfig.autoCaptureMaxRetries ?? DEFAULTS.autoCaptureMaxRetries,
|
|
558
|
+
autoCaptureMaxContextBytes,
|
|
559
|
+
autoCaptureLanguage: fileConfig.autoCaptureLanguage,
|
|
560
|
+
memoryProvider: (fileConfig.memoryProvider ?? "openai-chat"),
|
|
561
|
+
memoryModel: fileConfig.memoryModel,
|
|
562
|
+
memoryApiUrl: fileConfig.memoryApiUrl,
|
|
563
|
+
memoryApiKey,
|
|
564
|
+
memoryTemperature: fileConfig.memoryTemperature,
|
|
565
|
+
memoryExtraParams: fileConfig.memoryExtraParams,
|
|
566
|
+
opencodeProvider: fileConfig.opencodeProvider,
|
|
567
|
+
opencodeModel: fileConfig.opencodeModel,
|
|
568
|
+
piProvider: fileConfig.piProvider,
|
|
569
|
+
piModel: fileConfig.piModel,
|
|
570
|
+
autoCaptureProviderStatus: getAutoCaptureProviderStatus({
|
|
571
|
+
opencodeProvider: fileConfig.opencodeProvider,
|
|
572
|
+
opencodeModel: fileConfig.opencodeModel,
|
|
573
|
+
memoryModel: fileConfig.memoryModel,
|
|
574
|
+
memoryApiUrl: fileConfig.memoryApiUrl,
|
|
575
|
+
memoryApiKey,
|
|
576
|
+
}),
|
|
577
|
+
aiSessionRetentionDays: fileConfig.aiSessionRetentionDays ?? DEFAULTS.aiSessionRetentionDays,
|
|
578
|
+
webServerEnabled: fileConfig.webServerEnabled ?? DEFAULTS.webServerEnabled,
|
|
579
|
+
webServerPort: fileConfig.webServerPort ?? DEFAULTS.webServerPort,
|
|
580
|
+
webServerHost: fileConfig.webServerHost ?? DEFAULTS.webServerHost,
|
|
581
|
+
webServerAuthPassword: resolveSecretValue(fileConfig.webServerAuthPassword),
|
|
582
|
+
webServerAuthUsername: fileConfig.webServerAuthUsername,
|
|
583
|
+
webServerApiToken: fileConfig.webServerApiToken
|
|
584
|
+
? resolveSecretValue(fileConfig.webServerApiToken)
|
|
585
|
+
: undefined,
|
|
586
|
+
maxVectorsPerShard: fileConfig.maxVectorsPerShard ?? DEFAULTS.maxVectorsPerShard,
|
|
587
|
+
autoCleanupEnabled: fileConfig.autoCleanupEnabled ?? DEFAULTS.autoCleanupEnabled,
|
|
588
|
+
autoCleanupRetentionDays: normalizeAutoCleanupRetentionDays(fileConfig.autoCleanupRetentionDays ?? DEFAULTS.autoCleanupRetentionDays),
|
|
589
|
+
deduplicationEnabled: fileConfig.deduplicationEnabled ?? DEFAULTS.deduplicationEnabled,
|
|
590
|
+
deduplicationSimilarityThreshold: fileConfig.deduplicationSimilarityThreshold ?? DEFAULTS.deduplicationSimilarityThreshold,
|
|
591
|
+
userProfileAnalysisInterval: fileConfig.userProfileAnalysisInterval ?? DEFAULTS.userProfileAnalysisInterval,
|
|
592
|
+
userProfileMaxContextBytes: fileConfig.userProfileMaxContextBytes ?? DEFAULTS.userProfileMaxContextBytes,
|
|
593
|
+
userProfileDisplayPreferences: fileConfig.userProfileDisplayPreferences ?? DEFAULTS.userProfileDisplayPreferences,
|
|
594
|
+
userProfileDisplayPatterns: fileConfig.userProfileDisplayPatterns ?? DEFAULTS.userProfileDisplayPatterns,
|
|
595
|
+
userProfileDisplayWorkflows: fileConfig.userProfileDisplayWorkflows ?? DEFAULTS.userProfileDisplayWorkflows,
|
|
596
|
+
userProfileInjectPreferences: fileConfig.userProfileInjectPreferences ?? DEFAULTS.userProfileInjectPreferences,
|
|
597
|
+
userProfileInjectPatterns: fileConfig.userProfileInjectPatterns ?? DEFAULTS.userProfileInjectPatterns,
|
|
598
|
+
userProfileInjectWorkflows: fileConfig.userProfileInjectWorkflows ?? DEFAULTS.userProfileInjectWorkflows,
|
|
599
|
+
userProfileConfidenceDecayDays: fileConfig.userProfileConfidenceDecayDays ?? DEFAULTS.userProfileConfidenceDecayDays,
|
|
600
|
+
userProfileChangelogRetentionCount: fileConfig.userProfileChangelogRetentionCount ?? DEFAULTS.userProfileChangelogRetentionCount,
|
|
601
|
+
userProfileEmbeddingThresholdSameCat: fileConfig.userProfileEmbeddingThresholdSameCat ??
|
|
602
|
+
DEFAULTS.userProfileEmbeddingThresholdSameCat,
|
|
603
|
+
userProfileEmbeddingThresholdSameCatWeak: fileConfig.userProfileEmbeddingThresholdSameCatWeak ??
|
|
604
|
+
DEFAULTS.userProfileEmbeddingThresholdSameCatWeak,
|
|
605
|
+
userProfileEmbeddingThresholdCrossCat: fileConfig.userProfileEmbeddingThresholdCrossCat ??
|
|
606
|
+
DEFAULTS.userProfileEmbeddingThresholdCrossCat,
|
|
607
|
+
userProfileEmbeddingThresholdCrossCatWeak: fileConfig.userProfileEmbeddingThresholdCrossCatWeak ??
|
|
608
|
+
DEFAULTS.userProfileEmbeddingThresholdCrossCatWeak,
|
|
609
|
+
userProfileCentroidDriftThreshold: fileConfig.userProfileCentroidDriftThreshold ?? DEFAULTS.userProfileCentroidDriftThreshold,
|
|
610
|
+
userProfileEmbeddingMinDescriptionLength: fileConfig.userProfileEmbeddingMinDescriptionLength ??
|
|
611
|
+
DEFAULTS.userProfileEmbeddingMinDescriptionLength,
|
|
612
|
+
userProfileMinEvidenceForRetention: fileConfig.userProfileMinEvidenceForRetention ?? DEFAULTS.userProfileMinEvidenceForRetention,
|
|
613
|
+
userProfileAutoCleanupEnabled: fileConfig.userProfileAutoCleanupEnabled ?? DEFAULTS.userProfileAutoCleanupEnabled,
|
|
614
|
+
userProfileAutoCleanupInterval,
|
|
615
|
+
userProfileValidationEnabled: fileConfig.userProfileValidationEnabled ?? DEFAULTS.userProfileValidationEnabled,
|
|
616
|
+
userProfileStaleDays: fileConfig.userProfileStaleDays ?? DEFAULTS.userProfileStaleDays,
|
|
617
|
+
showAutoCaptureToasts: fileConfig.showAutoCaptureToasts ?? DEFAULTS.showAutoCaptureToasts,
|
|
618
|
+
showUserProfileToasts: fileConfig.showUserProfileToasts ?? DEFAULTS.showUserProfileToasts,
|
|
619
|
+
showErrorToasts: fileConfig.showErrorToasts ?? DEFAULTS.showErrorToasts,
|
|
620
|
+
memory: {
|
|
621
|
+
defaultScope: fileConfig.memory?.defaultScope ?? DEFAULTS.memory.defaultScope,
|
|
622
|
+
},
|
|
623
|
+
compaction: {
|
|
624
|
+
enabled: fileConfig.compaction?.enabled ?? DEFAULTS.compaction.enabled,
|
|
625
|
+
memoryLimit: fileConfig.compaction?.memoryLimit ?? DEFAULTS.compaction.memoryLimit,
|
|
626
|
+
},
|
|
627
|
+
chatMessage: {
|
|
628
|
+
enabled: fileConfig.chatMessage?.enabled ?? DEFAULTS.chatMessage.enabled,
|
|
629
|
+
maxMemories: fileConfig.chatMessage?.maxMemories ?? DEFAULTS.chatMessage.maxMemories,
|
|
630
|
+
excludeCurrentSession: fileConfig.chatMessage?.excludeCurrentSession ?? DEFAULTS.chatMessage.excludeCurrentSession,
|
|
631
|
+
maxAgeDays: fileConfig.chatMessage?.maxAgeDays,
|
|
632
|
+
injectOn: (fileConfig.chatMessage?.injectOn ?? DEFAULTS.chatMessage.injectOn),
|
|
633
|
+
},
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
const _globalFileConfig = loadConfigFromPaths(CONFIG_FILES);
|
|
637
|
+
let lastFileConfig = _globalFileConfig;
|
|
638
|
+
export let CONFIG = buildConfig(_globalFileConfig);
|
|
639
|
+
function hasValue(value) {
|
|
640
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
641
|
+
}
|
|
642
|
+
export { isPlaceholderApiKey };
|
|
643
|
+
export function getAutoCaptureProviderStatus(config) {
|
|
644
|
+
const hasOpencodeProvider = hasValue(config.opencodeProvider);
|
|
645
|
+
const hasOpencodeModel = hasValue(config.opencodeModel);
|
|
646
|
+
if (hasOpencodeProvider && hasOpencodeModel) {
|
|
647
|
+
return { ready: true, mode: "opencode", issues: [] };
|
|
648
|
+
}
|
|
649
|
+
const issues = [];
|
|
650
|
+
if (!hasOpencodeProvider)
|
|
651
|
+
issues.push("opencodeProvider is not configured");
|
|
652
|
+
if (!hasOpencodeModel)
|
|
653
|
+
issues.push("opencodeModel is not configured");
|
|
654
|
+
const hasMemoryModel = hasValue(config.memoryModel);
|
|
655
|
+
const hasMemoryApiUrl = hasValue(config.memoryApiUrl);
|
|
656
|
+
const hasMemoryApiKey = hasValue(config.memoryApiKey);
|
|
657
|
+
const hasPlaceholderMemoryApiKey = isPlaceholderApiKey(config.memoryApiKey);
|
|
658
|
+
// The orcarouter provider presets its endpoint and default model, so only
|
|
659
|
+
// an API key is required for the manual fallback path.
|
|
660
|
+
if (config.memoryProvider === "orcarouter") {
|
|
661
|
+
if (!hasMemoryApiKey)
|
|
662
|
+
issues.push("memoryApiKey is not configured");
|
|
663
|
+
if (hasPlaceholderMemoryApiKey)
|
|
664
|
+
issues.push("memoryApiKey contains a placeholder value");
|
|
665
|
+
if (hasMemoryApiKey && !hasPlaceholderMemoryApiKey) {
|
|
666
|
+
return { ready: true, mode: "manual", issues: [] };
|
|
667
|
+
}
|
|
668
|
+
return { ready: false, issues };
|
|
669
|
+
}
|
|
670
|
+
if (!hasMemoryModel)
|
|
671
|
+
issues.push("memoryModel is not configured");
|
|
672
|
+
if (!hasMemoryApiUrl)
|
|
673
|
+
issues.push("memoryApiUrl is not configured");
|
|
674
|
+
if (!hasMemoryApiKey)
|
|
675
|
+
issues.push("memoryApiKey is not configured");
|
|
676
|
+
if (hasPlaceholderMemoryApiKey)
|
|
677
|
+
issues.push("memoryApiKey contains a placeholder value");
|
|
678
|
+
if (hasMemoryModel && hasMemoryApiUrl && hasMemoryApiKey && !hasPlaceholderMemoryApiKey) {
|
|
679
|
+
return { ready: true, mode: "manual", issues: [] };
|
|
680
|
+
}
|
|
681
|
+
return { ready: false, issues };
|
|
682
|
+
}
|
|
683
|
+
export function hasAutoCaptureProviderConfig(config = CONFIG) {
|
|
684
|
+
return getAutoCaptureProviderStatus(config).ready;
|
|
685
|
+
}
|
|
686
|
+
export function initConfig(directory) {
|
|
687
|
+
// omms project overrides win; the legacy opencode-mem file is still read.
|
|
688
|
+
const projectPaths = [
|
|
689
|
+
join(directory, ".opencode", "omms.jsonc"),
|
|
690
|
+
join(directory, ".opencode", "omms.json"),
|
|
691
|
+
join(directory, ".opencode", "opencode-mem.jsonc"),
|
|
692
|
+
join(directory, ".opencode", "opencode-mem.json"),
|
|
693
|
+
];
|
|
694
|
+
const globalConfig = loadConfigFromPaths(CONFIG_FILES);
|
|
695
|
+
const projectConfig = loadConfigFromPaths(projectPaths);
|
|
696
|
+
assertProjectRemoteProviderConfigIsSafe(projectConfig);
|
|
697
|
+
const projectOverrides = { ...projectConfig };
|
|
698
|
+
delete projectOverrides.autoCleanupEnabled;
|
|
699
|
+
delete projectOverrides.autoCleanupRetentionDays;
|
|
700
|
+
const merged = { ...globalConfig, ...projectOverrides };
|
|
701
|
+
lastFileConfig = merged;
|
|
702
|
+
CONFIG = buildConfig(merged);
|
|
703
|
+
}
|
|
704
|
+
/**
|
|
705
|
+
* Rebuild CONFIG from the last loaded file config. Used after the legacy
|
|
706
|
+
* migration runs so the storage default can flip from the legacy layout to
|
|
707
|
+
* ~/.omms/data when the migration marker says it succeeded.
|
|
708
|
+
*/
|
|
709
|
+
function refreshStorageResolution() {
|
|
710
|
+
CONFIG = buildConfig(lastFileConfig);
|
|
711
|
+
}
|
|
712
|
+
/**
|
|
713
|
+
* initConfig plus the one-time legacy store migration (decision D13).
|
|
714
|
+
*
|
|
715
|
+
* The migration only applies while the resolved store sits on a DEFAULT
|
|
716
|
+
* layout (legacy ~/.opencode-mem/data or omms ~/.omms/data). The legacy
|
|
717
|
+
* config template pinned "storagePath": "~/.opencode-mem/data" explicitly,
|
|
718
|
+
* so key-presence cannot be the skip signal; a genuinely custom store path
|
|
719
|
+
* (the user pointing storage elsewhere) skips migration entirely.
|
|
720
|
+
* OMMS_SKIP_LEGACY_MIGRATION=1 (set by the test preload) disables the
|
|
721
|
+
* automatic run without affecting resolution.
|
|
722
|
+
*/
|
|
723
|
+
export function initConfigWithLegacyMigration(directory) {
|
|
724
|
+
initConfig(directory);
|
|
725
|
+
const { legacyDataDir, ommsDataDir } = legacyMigrationPaths();
|
|
726
|
+
const usesDefaultLayout = CONFIG.storagePath === legacyDataDir || CONFIG.storagePath === ommsDataDir;
|
|
727
|
+
if (usesDefaultLayout && process.env.OMMS_SKIP_LEGACY_MIGRATION !== "1") {
|
|
728
|
+
runLegacyStoreMigration();
|
|
729
|
+
refreshStorageResolution();
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
export function isConfigured() {
|
|
733
|
+
return true;
|
|
734
|
+
}
|
|
735
|
+
/**
|
|
736
|
+
* The containerTagPrefix explicitly set in the merged config files, or
|
|
737
|
+
* undefined when only the default applies. The tag prefix migration warns
|
|
738
|
+
* when this is "opencode" after migration (the override matches no rows).
|
|
739
|
+
*/
|
|
740
|
+
export function getExplicitContainerTagPrefix() {
|
|
741
|
+
return lastFileConfig.containerTagPrefix;
|
|
742
|
+
}
|