om-memory-system 3.3.0-next.17 → 3.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -5
- package/dist/adapters/opencode/backfill-models.d.ts +25 -0
- package/dist/adapters/opencode/backfill-models.js +39 -0
- package/dist/adapters/opencode/backfill-startup.d.ts +7 -0
- package/dist/adapters/opencode/backfill-startup.js +12 -0
- package/dist/adapters/opencode/import-command.js +3 -0
- package/dist/adapters/pi/backfill-models.d.ts +9 -0
- package/dist/adapters/pi/backfill-models.js +30 -0
- package/dist/adapters/pi/extension.js +50 -0
- package/dist/adapters/pi/import-command.js +3 -0
- package/dist/cli/index.js +13 -1
- package/dist/cli/web-command.d.ts +3 -0
- package/dist/cli/web-command.js +72 -0
- package/dist/config.d.ts +13 -1
- package/dist/config.js +28 -0
- package/dist/importer/auto-backfill.d.ts +20 -0
- package/dist/importer/auto-backfill.js +165 -0
- package/dist/importer/backfill-lock.d.ts +3 -0
- package/dist/importer/backfill-lock.js +44 -0
- package/dist/importer/backfill-model.d.ts +10 -0
- package/dist/importer/backfill-model.js +13 -0
- package/dist/importer/importer.js +14 -0
- package/dist/importer/manual-import-guard.d.ts +4 -0
- package/dist/importer/manual-import-guard.js +9 -0
- package/dist/importer/settings-health.d.ts +1 -2
- package/dist/importer/settings-health.js +2 -11
- package/dist/index.js +30 -0
- package/dist/services/backfill-state.d.ts +25 -0
- package/dist/services/backfill-state.js +68 -0
- package/dist/services/capture-diagnostics.d.ts +9 -0
- package/dist/services/capture-diagnostics.js +73 -6
- package/dist/services/global-config-writer.js +5 -1
- package/dist/services/live-captured-entries.d.ts +3 -0
- package/dist/services/live-captured-entries.js +34 -0
- package/dist/services/safe-health-error.d.ts +2 -0
- package/dist/services/safe-health-error.js +11 -0
- package/dist/services/settings-snapshot.js +14 -2
- package/dist/services/web-autostart.d.ts +29 -0
- package/dist/services/web-autostart.js +208 -0
- package/dist/services/web-server.js +11 -0
- package/dist/web/assets/index-B-_gMSn9.js +124 -0
- package/dist/web/index.html +1 -1
- package/package.json +1 -1
- package/dist/web/assets/index-BMkPht7g.js +0 -124
package/README.md
CHANGED
|
@@ -26,8 +26,7 @@ the other. Everything is stored locally on your machine.
|
|
|
26
26
|
OpenCode v1.
|
|
27
27
|
- **Learns your preferences.** A user profile of your habits builds up over
|
|
28
28
|
time and follows you across projects.
|
|
29
|
-
- **Imports your past sessions.**
|
|
30
|
-
history into memories and a profile.
|
|
29
|
+
- **Imports your past sessions.** Pi and OpenCode import older history after startup. A command provides a preview and manual control.
|
|
31
30
|
- **Lets you look and edit.** A local web page shows every memory and your
|
|
32
31
|
profile.
|
|
33
32
|
- **Keeps private things private.** Text inside `<private>` tags is never
|
|
@@ -98,13 +97,12 @@ See [Configuration](docs/configuration.md#choosing-the-model).
|
|
|
98
97
|
### 3. Check it works
|
|
99
98
|
|
|
100
99
|
Work normally for a few turns, then open `http://127.0.0.1:4747` in your
|
|
101
|
-
browser
|
|
100
|
+
browser. OpenCode or the login web app serves this page. New memories appear on the timeline.
|
|
102
101
|
You can also ask the agent: "search memory for what we changed today".
|
|
103
102
|
|
|
104
103
|
## Import your past history
|
|
105
104
|
|
|
106
|
-
|
|
107
|
-
the agent:
|
|
105
|
+
Older sessions import automatically after a host starts. This makes model calls. To opt out, set `"autoBackfill": false` in `~/.config/omms/omms.jsonc` before starting. For a manual import or custom source, preview inside the agent:
|
|
108
106
|
|
|
109
107
|
```text
|
|
110
108
|
/memory-import-opencode-history --dry-run
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { selectImportModel } from "../../importer/model-selection.js";
|
|
2
|
+
import type { HistoryImportModels } from "../../importer/run-import.js";
|
|
3
|
+
import { createOpencodeImportModels } from "../../services/ai/opencode-import-models.js";
|
|
4
|
+
interface BackfillConfig {
|
|
5
|
+
opencodeBackfillModel?: string;
|
|
6
|
+
opencodeProvider?: string;
|
|
7
|
+
opencodeModel?: string;
|
|
8
|
+
memoryProvider?: string;
|
|
9
|
+
memoryModel?: string;
|
|
10
|
+
memoryApiUrl?: string;
|
|
11
|
+
memoryApiKey?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface OpenCodeBackfillContext {
|
|
14
|
+
connected: string[];
|
|
15
|
+
directory: string;
|
|
16
|
+
configModel: () => Promise<string | null>;
|
|
17
|
+
makeModels?: typeof createOpencodeImportModels;
|
|
18
|
+
externalModels?: typeof selectImportModel;
|
|
19
|
+
}
|
|
20
|
+
/** Apply the live-model rule when inherit is selected; named models need sign-in. */
|
|
21
|
+
export declare function resolveOpencodeBackfillModels(config: BackfillConfig | undefined, context: OpenCodeBackfillContext): Promise<{
|
|
22
|
+
model: string;
|
|
23
|
+
models: HistoryImportModels;
|
|
24
|
+
}>;
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { CONFIG } from "../../config.js";
|
|
2
|
+
import { parseBackfillModel } from "../../importer/backfill-model.js";
|
|
3
|
+
import { selectImportModel } from "../../importer/model-selection.js";
|
|
4
|
+
import { getAutoCaptureProviderStatus } from "../../services/ai/live-model-choice.js";
|
|
5
|
+
import { createOpencodeImportModels, } from "../../services/ai/opencode-import-models.js";
|
|
6
|
+
/** Apply the live-model rule when inherit is selected; named models need sign-in. */
|
|
7
|
+
export async function resolveOpencodeBackfillModels(config = CONFIG, context) {
|
|
8
|
+
const choice = parseBackfillModel(config, "opencode");
|
|
9
|
+
let ref;
|
|
10
|
+
if (choice === "inherit") {
|
|
11
|
+
const live = getAutoCaptureProviderStatus(config);
|
|
12
|
+
if (!live.ready)
|
|
13
|
+
throw new Error(`OpenCode backfill: ${live.issues.join("; ")}`);
|
|
14
|
+
if (live.mode === "manual") {
|
|
15
|
+
const external = (context.externalModels ?? selectImportModel)({});
|
|
16
|
+
return {
|
|
17
|
+
model: `${external.provider}/${external.modelId}`,
|
|
18
|
+
models: { capture: external.capture, profile: external.profile },
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
const selected = live.mode === "opencode"
|
|
22
|
+
? `${config.opencodeProvider}/${config.opencodeModel}`
|
|
23
|
+
: await context.configModel();
|
|
24
|
+
if (!selected)
|
|
25
|
+
throw new Error("OpenCode backfill: no configured default model");
|
|
26
|
+
const parsed = parseBackfillModel({ opencodeBackfillModel: selected }, "opencode");
|
|
27
|
+
if (parsed === "inherit")
|
|
28
|
+
throw new Error("OpenCode backfill: no configured default model");
|
|
29
|
+
ref = { providerID: parsed.provider, modelID: parsed.model };
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
ref = { providerID: choice.provider, modelID: choice.model };
|
|
33
|
+
}
|
|
34
|
+
if (!context.connected.includes(ref.providerID)) {
|
|
35
|
+
throw new Error(`OpenCode backfill provider ${ref.providerID} is not connected`);
|
|
36
|
+
}
|
|
37
|
+
const models = await (context.makeModels ?? createOpencodeImportModels)(ref, context.directory);
|
|
38
|
+
return { model: `${ref.providerID}/${ref.modelID}`, models };
|
|
39
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Start after connected providers load, without delaying plugin initialisation. */
|
|
2
|
+
export declare function startOpencodeBackfill(input: {
|
|
3
|
+
directory: string;
|
|
4
|
+
connected: string[];
|
|
5
|
+
configModel: () => Promise<string | null>;
|
|
6
|
+
notify: (message: string) => void;
|
|
7
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { CONFIG } from "../../config.js";
|
|
2
|
+
import { scheduleAutoBackfill } from "../../importer/auto-backfill.js";
|
|
3
|
+
import { resolveOpencodeBackfillModels } from "./backfill-models.js";
|
|
4
|
+
/** Start after connected providers load, without delaying plugin initialisation. */
|
|
5
|
+
export function startOpencodeBackfill(input) {
|
|
6
|
+
return scheduleAutoBackfill({
|
|
7
|
+
host: "opencode",
|
|
8
|
+
cwd: input.directory,
|
|
9
|
+
resolveModels: () => resolveOpencodeBackfillModels(CONFIG, input),
|
|
10
|
+
notify: input.notify,
|
|
11
|
+
});
|
|
12
|
+
}
|
|
@@ -4,6 +4,7 @@ import { formatHistoryImportReport, summarizeHistoryImportReport, runHistoryImpo
|
|
|
4
4
|
import { createOpencodeImportModels, } from "../../services/ai/opencode-import-models.js";
|
|
5
5
|
export { createOpencodeImportModels } from "../../services/ai/opencode-import-models.js";
|
|
6
6
|
import { loadOpencodeProvider } from "../../services/ai/opencode-provider-loader.js";
|
|
7
|
+
import { beginManualImport } from "../../importer/manual-import-guard.js";
|
|
7
8
|
import { memoryClient } from "../../services/client.js";
|
|
8
9
|
import { log } from "../../services/logger.js";
|
|
9
10
|
/**
|
|
@@ -42,6 +43,7 @@ export async function runOpencodeImportCommand(input) {
|
|
|
42
43
|
if (importCommandRunning)
|
|
43
44
|
return `${name}: an import is already running`;
|
|
44
45
|
importCommandRunning = true;
|
|
46
|
+
const endManualImport = beginManualImport("opencode");
|
|
45
47
|
try {
|
|
46
48
|
let ref = null;
|
|
47
49
|
if (parsed.model || importNeedsModel(parsed)) {
|
|
@@ -82,6 +84,7 @@ export async function runOpencodeImportCommand(input) {
|
|
|
82
84
|
return `${name} failed: ${message}`;
|
|
83
85
|
}
|
|
84
86
|
finally {
|
|
87
|
+
endManualImport();
|
|
85
88
|
importCommandRunning = false;
|
|
86
89
|
}
|
|
87
90
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { HistoryImportModels } from "../../importer/run-import.js";
|
|
2
|
+
import { type PiModelContext } from "./provider.js";
|
|
3
|
+
/** Resolve the Pi backfill independently of the manual import's session-model rule. */
|
|
4
|
+
export declare function resolvePiBackfillModels(ctx: PiModelContext, config?: {
|
|
5
|
+
piBackfillModel?: string;
|
|
6
|
+
}): Promise<{
|
|
7
|
+
model: string;
|
|
8
|
+
models: HistoryImportModels;
|
|
9
|
+
}>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { CONFIG } from "../../config.js";
|
|
2
|
+
import { parseBackfillModel } from "../../importer/backfill-model.js";
|
|
3
|
+
import { createPiLiveModels } from "./live-model.js";
|
|
4
|
+
import { adaptPiProfileModel } from "./profile.js";
|
|
5
|
+
import { createPiCaptureProvider, resolveImportModel } from "./provider.js";
|
|
6
|
+
/** Resolve the Pi backfill independently of the manual import's session-model rule. */
|
|
7
|
+
export async function resolvePiBackfillModels(ctx, config = CONFIG) {
|
|
8
|
+
const choice = parseBackfillModel(config, "pi");
|
|
9
|
+
if (choice === "inherit") {
|
|
10
|
+
const live = createPiLiveModels(ctx);
|
|
11
|
+
const profile = live.profile();
|
|
12
|
+
if (!profile)
|
|
13
|
+
throw new Error("Pi backfill: no live-capture model is available");
|
|
14
|
+
const model = profile.modelId || CONFIG.memoryModel;
|
|
15
|
+
return {
|
|
16
|
+
model: `${profile.provider}/${model ?? "unknown"}`,
|
|
17
|
+
models: { capture: live.capture, profile },
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
const selected = resolveImportModel(ctx, `${choice.provider}/${choice.model}`);
|
|
21
|
+
if (!selected)
|
|
22
|
+
throw new Error(`Pi backfill model not found: ${choice.provider}/${choice.model}`);
|
|
23
|
+
return {
|
|
24
|
+
model: `${selected.provider}/${selected.modelId}`,
|
|
25
|
+
models: {
|
|
26
|
+
capture: createPiCaptureProvider(() => selected),
|
|
27
|
+
profile: adaptPiProfileModel(selected),
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}
|
|
@@ -11,6 +11,7 @@ import { registerPiHistoryImportCommand } from "./import-command.js";
|
|
|
11
11
|
import { performPiProfileLearning } from "./profile.js";
|
|
12
12
|
import { buildRetrievalSection, wrapRetrievalSection } from "../../core/retrieval.js";
|
|
13
13
|
const GLOBAL_PLUGIN_WARMUP_KEY = Symbol.for("omms.plugin.warmedup");
|
|
14
|
+
const GLOBAL_PI_BACKFILL_KEY = Symbol.for("omms.pi.backfill.scheduled");
|
|
14
15
|
const OMMS_STATUS_KEY = "omms";
|
|
15
16
|
const MEMORY_TOOL_MODES = [
|
|
16
17
|
"add",
|
|
@@ -70,6 +71,7 @@ export default function ommsPiExtension(pi) {
|
|
|
70
71
|
// Latest session context, refreshed on session_start and consumed by the
|
|
71
72
|
// import command (command contexts do not expose the model registry).
|
|
72
73
|
let latestCtx = null;
|
|
74
|
+
let backfillSessionId = null;
|
|
73
75
|
let statusVersion = 0;
|
|
74
76
|
const startStatus = (ctx, status) => {
|
|
75
77
|
const version = ++statusVersion;
|
|
@@ -101,7 +103,50 @@ export default function ommsPiExtension(pi) {
|
|
|
101
103
|
// Runs even with tracing off, so turning it off does not leave old traces behind.
|
|
102
104
|
pruneTraces(CONFIG);
|
|
103
105
|
captureState = createPiCaptureState();
|
|
106
|
+
if (CONFIG.webServerAutoStart !== undefined &&
|
|
107
|
+
process.env.OMMS_DISABLE_WEB_AUTOSTART !== "1") {
|
|
108
|
+
void import("../../services/web-autostart.js")
|
|
109
|
+
.then(({ reconcileWebAutostart }) => reconcileWebAutostart(CONFIG))
|
|
110
|
+
.catch((error) => log("Pi login item reconciliation failed", {
|
|
111
|
+
code: error instanceof Error ? error.name : "unknown",
|
|
112
|
+
}));
|
|
113
|
+
}
|
|
104
114
|
const globalScope = globalThis;
|
|
115
|
+
const sessionId = ctx.sessionManager.getSessionId();
|
|
116
|
+
if (isConfigured() &&
|
|
117
|
+
CONFIG.autoBackfill &&
|
|
118
|
+
process.env.OMMS_DISABLE_AUTO_BACKFILL !== "1" &&
|
|
119
|
+
!globalScope[GLOBAL_PI_BACKFILL_KEY] &&
|
|
120
|
+
backfillSessionId !== sessionId) {
|
|
121
|
+
backfillSessionId = sessionId;
|
|
122
|
+
const controller = new AbortController();
|
|
123
|
+
const task = { controller, promise: Promise.resolve() };
|
|
124
|
+
globalScope[GLOBAL_PI_BACKFILL_KEY] = task;
|
|
125
|
+
task.promise = (async () => {
|
|
126
|
+
const { scheduleAutoBackfill } = await import("../../importer/auto-backfill.js");
|
|
127
|
+
await scheduleAutoBackfill({
|
|
128
|
+
host: "pi",
|
|
129
|
+
cwd: ctx.cwd,
|
|
130
|
+
signal: controller.signal,
|
|
131
|
+
resolveModels: async () => {
|
|
132
|
+
const { resolvePiBackfillModels } = await import("./backfill-models.js");
|
|
133
|
+
return resolvePiBackfillModels(ctx);
|
|
134
|
+
},
|
|
135
|
+
notify: (message) => {
|
|
136
|
+
if (!controller.signal.aborted && ctx.hasUI)
|
|
137
|
+
ctx.ui.notify(message, "info");
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
})()
|
|
141
|
+
.catch((error) => log("Pi backfill failed", {
|
|
142
|
+
error: error instanceof Error ? error.message : String(error),
|
|
143
|
+
}))
|
|
144
|
+
.finally(() => {
|
|
145
|
+
if (globalScope[GLOBAL_PI_BACKFILL_KEY] === task) {
|
|
146
|
+
delete globalScope[GLOBAL_PI_BACKFILL_KEY];
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
}
|
|
105
150
|
if (!globalScope[GLOBAL_PLUGIN_WARMUP_KEY] && isConfigured()) {
|
|
106
151
|
(async () => {
|
|
107
152
|
try {
|
|
@@ -192,6 +237,11 @@ export default function ommsPiExtension(pi) {
|
|
|
192
237
|
captureState = createPiCaptureState();
|
|
193
238
|
promptsSinceProfileAnalysis = [];
|
|
194
239
|
globalThis[GLOBAL_PLUGIN_WARMUP_KEY] = false;
|
|
240
|
+
const task = globalThis[GLOBAL_PI_BACKFILL_KEY];
|
|
241
|
+
task?.controller.abort();
|
|
242
|
+
if (task)
|
|
243
|
+
await task.promise;
|
|
244
|
+
backfillSessionId = null;
|
|
195
245
|
try {
|
|
196
246
|
await memoryClient.close();
|
|
197
247
|
}
|
|
@@ -5,6 +5,7 @@ import { historyImportUsage, importNeedsModel, parseHistoryImportArgs, tokenizeI
|
|
|
5
5
|
import { formatHistoryImportReport, summarizeHistoryImportReport, runHistoryImport, } from "../../importer/run-import.js";
|
|
6
6
|
import { createPiCaptureProvider, resolveImportModel } from "./provider.js";
|
|
7
7
|
import { adaptPiProfileModel } from "./profile.js";
|
|
8
|
+
import { beginManualImport } from "../../importer/manual-import-guard.js";
|
|
8
9
|
/**
|
|
9
10
|
* Pi command surface for the historical-session importer:
|
|
10
11
|
*
|
|
@@ -74,6 +75,7 @@ export function registerPiHistoryImportCommand(pi, getExtensionContext) {
|
|
|
74
75
|
return;
|
|
75
76
|
}
|
|
76
77
|
importCommandRunning = true;
|
|
78
|
+
const endManualImport = beginManualImport("pi");
|
|
77
79
|
try {
|
|
78
80
|
// Best-effort warmup so failures surface as per-unit errors instead of
|
|
79
81
|
// every unit failing on embedding initialisation.
|
|
@@ -110,6 +112,7 @@ export function registerPiHistoryImportCommand(pi, getExtensionContext) {
|
|
|
110
112
|
notify(`memory-import-pi-history failed: ${message}`);
|
|
111
113
|
}
|
|
112
114
|
finally {
|
|
115
|
+
endManualImport();
|
|
113
116
|
importCommandRunning = false;
|
|
114
117
|
}
|
|
115
118
|
},
|
package/dist/cli/index.js
CHANGED
|
@@ -11,8 +11,10 @@ const usage = `Usage: om-memory-system <command> [options]
|
|
|
11
11
|
Commands:
|
|
12
12
|
import-opencode-history Import OpenCode history into omms
|
|
13
13
|
import-pi-history Import Pi history into omms
|
|
14
|
+
web Start the web app in the foreground
|
|
15
|
+
web install|uninstall|status Manage the web app login item
|
|
14
16
|
|
|
15
|
-
|
|
17
|
+
Import commands take the same options; run one with --help to see them.
|
|
16
18
|
Inside a session, /memory-import-opencode-history and /memory-import-pi-history
|
|
17
19
|
use that session's model instead of an API key.`;
|
|
18
20
|
export function parseImportArgs(argv) {
|
|
@@ -30,6 +32,16 @@ export async function runCli(argv) {
|
|
|
30
32
|
console.log(usage);
|
|
31
33
|
return 0;
|
|
32
34
|
}
|
|
35
|
+
if (argv[0] === "web") {
|
|
36
|
+
try {
|
|
37
|
+
const { runWebCommand } = await import("./web-command.js");
|
|
38
|
+
return await runWebCommand(argv.slice(1));
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
console.error(`OMMS web command failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
42
|
+
return 1;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
33
45
|
const apiKeyEnv = argv.find((_, index) => argv[index - 1] === "--api-key-env") ??
|
|
34
46
|
argv.find((value) => value.startsWith("--api-key-env="))?.slice("--api-key-env=".length);
|
|
35
47
|
try {
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { WebAutostartOptions } from "../services/web-autostart.js";
|
|
2
|
+
/** Run the standalone web app or manage its per-user login item. */
|
|
3
|
+
export declare function runWebCommand(args: string[], options?: WebAutostartOptions, online?: () => Promise<boolean>): Promise<number>;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
/** Run the standalone web app or manage its per-user login item. */
|
|
3
|
+
export async function runWebCommand(args, options = {}, online) {
|
|
4
|
+
const home = options.home ?? homedir();
|
|
5
|
+
const config = await import("../config.js");
|
|
6
|
+
config.initConfig(home);
|
|
7
|
+
const { installWebAutostart, removeWebAutostart, webAutostartStatus } = await import("../services/web-autostart.js");
|
|
8
|
+
const [action, ...rest] = args;
|
|
9
|
+
if (rest.length ||
|
|
10
|
+
(action && !["install", "uninstall", "status", "--login-item"].includes(action))) {
|
|
11
|
+
console.error("Usage: om-memory-system web [install|uninstall|status]");
|
|
12
|
+
return 1;
|
|
13
|
+
}
|
|
14
|
+
if (action === "install" && !config.CONFIG.webServerEnabled) {
|
|
15
|
+
console.error("OMMS web server is disabled (webServerEnabled is false)");
|
|
16
|
+
return 1;
|
|
17
|
+
}
|
|
18
|
+
if (action === "install" || action === "uninstall") {
|
|
19
|
+
const { readGlobalConfigRevision, writeGlobalConfigKeys } = await import("../services/global-config-writer.js");
|
|
20
|
+
await writeGlobalConfigKeys({ webServerAutoStart: action === "install" }, readGlobalConfigRevision());
|
|
21
|
+
config.initConfig(home);
|
|
22
|
+
const result = action === "install"
|
|
23
|
+
? installWebAutostart({ ...options, start: true })
|
|
24
|
+
: removeWebAutostart({ ...options, start: true });
|
|
25
|
+
console.log(`OMMS login item: ${result.state}`);
|
|
26
|
+
return action === "install" && result.state !== "installed" ? 1 : 0;
|
|
27
|
+
}
|
|
28
|
+
if (action === "status") {
|
|
29
|
+
const status = webAutostartStatus(options);
|
|
30
|
+
const check = online ??
|
|
31
|
+
(async () => {
|
|
32
|
+
const { WebServer } = await import("../services/web-server.js");
|
|
33
|
+
return new WebServer({
|
|
34
|
+
port: config.CONFIG.webServerPort,
|
|
35
|
+
host: config.CONFIG.webServerHost,
|
|
36
|
+
enabled: true,
|
|
37
|
+
apiToken: config.CONFIG.webServerApiToken,
|
|
38
|
+
}).checkServerAvailable();
|
|
39
|
+
});
|
|
40
|
+
console.log(JSON.stringify({ setting: config.CONFIG.webServerAutoStart, item: status, online: await check() }, null, 2));
|
|
41
|
+
return 0;
|
|
42
|
+
}
|
|
43
|
+
if (!config.CONFIG.webServerEnabled) {
|
|
44
|
+
console.error("OMMS web server is disabled (webServerEnabled is false)");
|
|
45
|
+
return 1;
|
|
46
|
+
}
|
|
47
|
+
const { WebAuth } = await import("../services/web-auth.js");
|
|
48
|
+
const { startWebServer } = await import("../services/web-server.js");
|
|
49
|
+
const server = await startWebServer({
|
|
50
|
+
directory: home,
|
|
51
|
+
port: config.CONFIG.webServerPort,
|
|
52
|
+
host: config.CONFIG.webServerHost,
|
|
53
|
+
enabled: true,
|
|
54
|
+
apiToken: config.CONFIG.webServerApiToken,
|
|
55
|
+
auth: new WebAuth({
|
|
56
|
+
password: config.CONFIG.webServerAuthPassword,
|
|
57
|
+
username: config.CONFIG.webServerAuthUsername,
|
|
58
|
+
}),
|
|
59
|
+
});
|
|
60
|
+
console.log(`OMMS web app: ${server.getUrl()}`);
|
|
61
|
+
// The Node HTTP adapter unrefs its socket for plugin use. Keep the CLI alive.
|
|
62
|
+
const keeper = setInterval(() => { }, 60_000);
|
|
63
|
+
const stop = () => {
|
|
64
|
+
clearInterval(keeper);
|
|
65
|
+
process.off("SIGINT", stop);
|
|
66
|
+
process.off("SIGTERM", stop);
|
|
67
|
+
void server.stop();
|
|
68
|
+
};
|
|
69
|
+
process.once("SIGINT", stop);
|
|
70
|
+
process.once("SIGTERM", stop);
|
|
71
|
+
return 0;
|
|
72
|
+
}
|
package/dist/config.d.ts
CHANGED
|
@@ -42,6 +42,10 @@ interface OmmsConfig {
|
|
|
42
42
|
captureTrace?: boolean;
|
|
43
43
|
captureTraceRetentionDays?: number;
|
|
44
44
|
captureAttemptRetentionDays?: number;
|
|
45
|
+
autoBackfill?: boolean;
|
|
46
|
+
opencodeBackfillModel?: string;
|
|
47
|
+
piBackfillModel?: string;
|
|
48
|
+
webServerAutoStart?: boolean;
|
|
45
49
|
webServerEnabled?: boolean;
|
|
46
50
|
webServerPort?: number;
|
|
47
51
|
webServerHost?: string;
|
|
@@ -89,7 +93,7 @@ interface OmmsConfig {
|
|
|
89
93
|
injectOn?: "first" | "always";
|
|
90
94
|
};
|
|
91
95
|
}
|
|
92
|
-
export declare const CONFIG_TEMPLATE = "{\n // ============================================\n // omms (Opinionated Modular Memory System) Configuration\n // ============================================\n \n // Storage location for the vector database. Leave unset to use the\n // default ~/.omms/data (a legacy ~/.opencode-mem/data store is migrated\n // there automatically on first start; see docs/omms-migration.md).\n // \"storagePath\": \"~/.omms/data\",\n\n \"userEmailOverride\": \"\",\n \"userNameOverride\": \"\",\n \n // ============================================\n // Embedding Model (for similarity search)\n // ============================================\n // Local = Hugging Face / ONNX via @huggingface/transformers (not Apple MLX).\n // Remote = set BOTH embeddingApiUrl and embeddingApiKey (OpenAI-compatible /embeddings).\n \n // Default: Nomic Embed v1 (768 dimensions, 8192 context, multilingual)\n \"embeddingModel\": \"Xenova/nomic-embed-text-v1\",\n\n // Opt-in Nomic task prefixes (search_document: / search_query:). After enabling,\n // re-index existing memories so store and query vectors stay aligned.\n // \"embeddingUseTaskPrefixes\": true,\n \n // Auto-detected dimensions (no need to set manually)\n // \"embeddingDimensions\": 768,\n \n // Other recommended local models:\n // \"embeddingModel\": \"Xenova/jina-embeddings-v2-base-en\", // 768 dims, English-only, 8192 context\n // \"embeddingModel\": \"Xenova/jina-embeddings-v2-small-en\", // 512 dims, faster, 8192 context\n // \"embeddingModel\": \"Xenova/all-MiniLM-L6-v2\", // 384 dims, very fast, 512 context\n // \"embeddingModel\": \"Xenova/all-mpnet-base-v2\", // 768 dims, good quality, 512 context\n \n // Optional: OpenAI-compatible API for embeddings (both URL and key required)\n // \"embeddingApiUrl\": \"https://api.openai.com/v1\",\n // \"embeddingApiKey\": \"env://OPENAI_API_KEY\", // or \"sk-...\" / \"file:///path/to/key\"\n // \"embeddingModel\": \"text-embedding-3-small\", // 1536 dims, auto-detected\n \n // ============================================\n // Web Server Settings\n // ============================================\n \n // Enable web UI for managing memories (accessible at http://localhost:4747)\n \"webServerEnabled\": true,\n \n // Port for web UI server\n \"webServerPort\": 4747,\n \n // Host address for web UI (use 127.0.0.1 for local only, 0.0.0.0 for network access)\n \"webServerHost\": \"127.0.0.1\",\n\n // Optional HTTP Basic Auth for the web UI. Accepts literal, env://, or file:// secrets.\n // \"webServerAuthPassword\": \"\",\n // \"webServerAuthUsername\": \"\",\n\n // Required when webServerHost is not loopback. Protects /api/* with Bearer / X-Opencode-Mem-Token.\n // \"webServerApiToken\": \"env://OMMS_WEB_TOKEN\",\n \n // ============================================\n // Database Settings\n // ============================================\n \n // Maximum vectors per database shard (auto-creates new shard when limit reached)\n \"maxVectorsPerShard\": 50000,\n \n // Automatically delete old memories based on retention period\n \"autoCleanupEnabled\": true,\n \n // Days to keep memories before auto-cleanup (only if autoCleanupEnabled is true)\n \"autoCleanupRetentionDays\": 30,\n \n // Automatically detect and remove duplicate memories\n \"deduplicationEnabled\": true,\n \n // Similarity threshold (0-1) for detecting duplicates (higher = stricter)\n \"deduplicationSimilarityThreshold\": 0.90,\n \n // ============================================\n // Memory Scope Settings\n // ============================================\n\n // Default scope for memory list/search queries\n // \"project\" keeps queries within the current project, \"all-projects\" searches across all project shards\n \"memory\": {\n \"defaultScope\": \"project\"\n },\n\n // ============================================\n // Model for auto-capture and profile learning (same rule in OpenCode and Pi)\n // ============================================\n\n // Which model summarises your work, in this order:\n // 1. The host model below: opencodeProvider/opencodeModel in OpenCode,\n // piProvider/piModel in Pi. Set the model to \"inherit\" to follow\n // whatever model the session is using.\n // 2. If no host model is set: the external API (memoryModel/memoryApiUrl/\n // memoryApiKey further down).\n // 3. If neither is set: the session's own model.\n // If the host model fails and the external API is configured, the external\n // API is used instead.\n //\n // Host models go through OpenCode's or Pi's own sign-in (OAuth like Claude\n // Pro/Max, GitHub Copilot, ChatGPT, or your own key there), so no separate API\n // key is needed here.\n //\n // Examples (OpenCode names from 'opencode providers list'; Pi names from its model list):\n // \"opencodeProvider\": \"anthropic\", \"opencodeModel\": \"claude-haiku-4-5-20251001\"\n // \"opencodeProvider\": \"openai\", \"opencodeModel\": \"inherit\"\n // \"piProvider\": \"openai-codex\", \"piModel\": \"gpt-5.6-luna\"\n //\n // \"opencodeProvider\": \"anthropic\",\n // \"opencodeModel\": \"claude-haiku-4-5-20251001\",\n // \"piProvider\": \"openai-codex\",\n // \"piModel\": \"gpt-5.6-luna\",\n\n // ============================================\n // Auto-Capture Settings\n // ============================================\n \n // Auto-capture runs in the background without blocking your main session.\n // Note: Ollama may not support tool calling. Use OpenAI, Anthropic, or Groq for best results.\n \n \"autoCaptureEnabled\": true,\n \n // Provider type: \"openai-chat\" | \"openai-responses\" | \"anthropic\" | \"minimax\" | \"orcarouter\"\n // Note: \"openai-chat\" is a generic OpenAI API-compatible mode.\n // Any service that follows the OpenAI Chat Completions API can use it via custom \"memoryApiUrl\".\n \"memoryProvider\": \"openai-chat\",\n \n // External API (step 2 above, and the fallback when a host model fails).\n // Uncomment all 3 lines and replace memoryApiKey before use:\n // \"memoryModel\": \"gpt-4o-mini\",\n // \"memoryApiUrl\": \"https://api.openai.com/v1\",\n // \"memoryApiKey\": \"sk-...\",\n\n // API Key Formats:\n // Direct value: \"sk-...\"\n // From file: \"file://~/.config/litellm-key.txt\"\n // From env variable: \"env://LITELLM_API_KEY\"\n \n // Examples for different providers:\n // Any OpenAI-compatible endpoint can use the \"openai-chat\" provider pattern below.\n // Common examples: DeepSeek, Qwen (via Alibaba Cloud ModelStudio),\n // Zhipu GLM (BigModel platform), and Kimi (Moonshot AI platform).\n\n // OpenAI Chat Completion (default, backward compatible):\n // \"memoryProvider\": \"openai-chat\"\n // \"memoryModel\": \"gpt-4o-mini\"\n // \"memoryApiUrl\": \"https://api.openai.com/v1\"\n // \"memoryApiKey\": \"sk-...\"\n\n // DeepSeek (OpenAI-compatible example):\n // \"memoryProvider\": \"openai-chat\"\n // \"memoryModel\": \"deepseek-chat\"\n // \"memoryApiUrl\": \"https://api.deepseek.com/v1\"\n // \"memoryApiKey\": \"sk-...\"\n \n // OpenAI Responses API (recommended, with session support):\n // \"memoryProvider\": \"openai-responses\"\n // \"memoryModel\": \"gpt-4o\"\n // \"memoryApiUrl\": \"https://api.openai.com/v1\"\n // \"memoryApiKey\": \"sk-...\"\n \n // Anthropic (with session support):\n // \"memoryProvider\": \"anthropic\"\n // \"memoryModel\": \"claude-3-5-haiku-20241022\"\n // \"memoryApiUrl\": \"https://api.anthropic.com/v1\"\n // \"memoryApiKey\": \"sk-ant-...\"\n\n // MiniMax (Anthropic Messages-compatible endpoint, with session support):\n // \"memoryProvider\": \"minimax\"\n // \"memoryModel\": \"MiniMax-M3\"\n // \"memoryApiUrl\": \"https://api.minimax.io\" // global endpoint\n // \"memoryApiKey\": \"<MiniMax API key>\"\n // // China endpoint: \"memoryApiUrl\": \"https://api.minimaxi.com\"\n // // Optional adaptive thinking for MiniMax-M3:\n // \"memoryExtraParams\": { \"thinking\": { \"type\": \"adaptive\" } }\n\n // OrcaRouter (OpenAI-compatible gateway, namespaced model IDs, with session support):\n // \"memoryProvider\": \"orcarouter\"\n // \"memoryApiKey\": \"<OrcaRouter API key>\"\n // // memoryApiUrl and memoryModel are optional \u2014 they default to\n // // https://api.orcarouter.ai/v1 and \"orcarouter/auto\" (a routing alias).\n // // OrcaRouter rejects bare model names, so if you set memoryModel, use a\n // // namespaced ID such as \"openai/gpt-5.5\" or \"deepseek/deepseek-v4-flash\".\n // \"memoryModel\": \"openai/gpt-5.5\"\n\n // Groq (OpenAI-compatible, use openai-chat provider):\n // \"memoryProvider\": \"openai-chat\"\n // \"memoryModel\": \"llama-3.3-70b-versatile\"\n // \"memoryApiUrl\": \"https://api.groq.com/openai/v1\"\n // \"memoryApiKey\": \"gsk_...\"\n \n // Maximum iterations for multi-turn AI analysis (for openai-responses, anthropic, and minimax)\n \"autoCaptureMaxIterations\": 5,\n \n // Timeout per iteration in milliseconds (30 seconds default)\n \"autoCaptureIterationTimeout\": 30000,\n\n // Maximum number of times to retry capturing a prompt if it fails (due to network, API errors, etc.)\n \"autoCaptureMaxRetries\": 3,\n\n // Maximum UTF-8 bytes for the auto-capture markdown context sent to the summary model.\n // Prevents HTTP 400 context overflows on models with ~131K token windows (e.g. Groq Llama).\n // Rough guide: tokens \u2248 bytes / 4 for mixed code/prose.\n \"autoCaptureMaxContextBytes\": 131072,\n \n // Days to keep AI session history before cleanup\n \"aiSessionRetentionDays\": 7,\n\n // Capture diagnostics: every capture attempt always writes one metadata line\n // (model, stop reason, sizes, outcome) to ~/.omms/omms.log, with no\n // conversation text. Set captureTrace to true to also write each attempt's\n // prompt and raw reply to ~/.omms/traces/ for debugging. Traces can contain\n // conversation content: <private> text and common API key formats are\n // redacted, files are readable only by you, and they are deleted after\n // captureTraceRetentionDays. Only this global file can turn tracing on.\n // \"captureTrace\": false,\n // \"captureTraceRetentionDays\": 7,\n // \"captureAttemptRetentionDays\": 30,\n\n // Temperature for AI API requests (set to false to omit parameter for models that don't support it)\n // Some reasoning models (like o1, o3, gpt-5) don't support temperature parameter\n // Set to false and add \"memoryTemperature\": false in config when using such models\n \"memoryTemperature\": 0.3,\n\n // Extra parameters to include in API request body\n // Useful for local inference servers (e.g. llama-server with --jinja) that support\n // additional parameters like disabling thinking/reasoning mode\n // Example for Qwen3 models: { \"enable_thinking\": false }\n // \"memoryExtraParams\": {},\n\n // Language for auto-capture summaries (default: \"auto\" for auto-detection)\n // Options: \"auto\", \"en\", \"id\", \"zh\", \"ja\", \"es\", \"fr\", \"de\", \"ru\", \"pt\", \"ar\", \"ko\"\n // \"autoCaptureLanguage\": \"auto\",\n\n // ============================================\n // Toast Notifications\n // ============================================\n\n // Show toast when memory is auto-captured\n \"showAutoCaptureToasts\": true,\n\n // Show toast when user profile is updated\n \"showUserProfileToasts\": true,\n\n // Show toast for error messages\n \"showErrorToasts\": true,\n\n // ============================================\n // User Profile System\n // ============================================\n\n // Analyze user prompts every N prompts to build/update your user profile\n // When N uncaptured prompts accumulate, AI will analyze them to identify:\n // - User preferences (code style, communication style, tool preferences)\n // - User patterns (recurring topics, problem domains, technical interests)\n // - User workflows (development habits, sequences, learning style)\n // - Skill level (overall and per-domain assessment)\n \"userProfileAnalysisInterval\": 10,\n\n // Days before inactive items (all types) are eligible for removal\n \"userProfileStaleDays\": 2,\n\n // Number of preferences shown in UI\n \"userProfileDisplayPreferences\": 20,\n \n // Number of patterns shown in UI\n \"userProfileDisplayPatterns\": 15,\n \n // Number of workflows shown in UI\n \"userProfileDisplayWorkflows\": 10,\n \n // Number of preferences injected into LLM conversation context\n // Keep this small \u2014 the strongest signals are enough; more dilute LLM attention\n \"userProfileInjectPreferences\": 5,\n \n // Number of patterns injected into LLM conversation context\n \"userProfileInjectPatterns\": 5,\n \n // Number of workflows injected into LLM conversation context\n \"userProfileInjectWorkflows\": 3,\n \n // Days before preference confidence starts to decay (if not reinforced)\n // Preferences that aren't seen again will gradually lose confidence and be removed\n \"userProfileConfidenceDecayDays\": 30,\n \n // Number of profile versions to keep in changelog (for rollback/debugging)\n // Older versions are automatically cleaned up\n \"userProfileChangelogRetentionCount\": 5,\n\n // Minimum evidence count for a preference/pattern to survive confidence decay\n // Items confirmed fewer times are more likely to be pruned when confidence decays\n \"userProfileMinEvidenceForRetention\": 3,\n\n // Periodically merge duplicate or irrelevant profile items with the configured AI provider\n \"userProfileAutoCleanupEnabled\": true,\n // Number of analyzed user prompts between automatic AI cleanup runs\n \"userProfileAutoCleanupInterval\": 100,\n\n // Enable LLM validation of existing preferences against recent behavior.\n // When enabled, each analysis round checks if top-5 preferences still match recent prompts.\n // Experimental \u2014 disabled by default.\n \"userProfileValidationEnabled\": false,\n\n // ============================================\n // Search Settings\n // ============================================\n \n // Minimum similarity score (0-1) for memory search results\n \"similarityThreshold\": 0.6,\n\n // Maximum number of memories to return in search results\n \"maxMemories\": 10,\n\n // ============================================\n // Advanced Settings\n // ============================================\n \n // Inject user profile into AI context (preferences, patterns, workflows)\n \"injectProfile\": true\n}\n";
|
|
96
|
+
export declare const CONFIG_TEMPLATE = "{\n // ============================================\n // omms (Opinionated Modular Memory System) Configuration\n // ============================================\n \n // Storage location for the vector database. Leave unset to use the\n // default ~/.omms/data (a legacy ~/.opencode-mem/data store is migrated\n // there automatically on first start; see docs/omms-migration.md).\n // \"storagePath\": \"~/.omms/data\",\n\n \"userEmailOverride\": \"\",\n \"userNameOverride\": \"\",\n \n // ============================================\n // Embedding Model (for similarity search)\n // ============================================\n // Local = Hugging Face / ONNX via @huggingface/transformers (not Apple MLX).\n // Remote = set BOTH embeddingApiUrl and embeddingApiKey (OpenAI-compatible /embeddings).\n \n // Default: Nomic Embed v1 (768 dimensions, 8192 context, multilingual)\n \"embeddingModel\": \"Xenova/nomic-embed-text-v1\",\n\n // Opt-in Nomic task prefixes (search_document: / search_query:). After enabling,\n // re-index existing memories so store and query vectors stay aligned.\n // \"embeddingUseTaskPrefixes\": true,\n \n // Auto-detected dimensions (no need to set manually)\n // \"embeddingDimensions\": 768,\n \n // Other recommended local models:\n // \"embeddingModel\": \"Xenova/jina-embeddings-v2-base-en\", // 768 dims, English-only, 8192 context\n // \"embeddingModel\": \"Xenova/jina-embeddings-v2-small-en\", // 512 dims, faster, 8192 context\n // \"embeddingModel\": \"Xenova/all-MiniLM-L6-v2\", // 384 dims, very fast, 512 context\n // \"embeddingModel\": \"Xenova/all-mpnet-base-v2\", // 768 dims, good quality, 512 context\n \n // Optional: OpenAI-compatible API for embeddings (both URL and key required)\n // \"embeddingApiUrl\": \"https://api.openai.com/v1\",\n // \"embeddingApiKey\": \"env://OPENAI_API_KEY\", // or \"sk-...\" / \"file:///path/to/key\"\n // \"embeddingModel\": \"text-embedding-3-small\", // 1536 dims, auto-detected\n \n // ============================================\n // Web Server Settings\n // ============================================\n \n // Start a background import of past chats on each host's next start.\n \"autoBackfill\": true,\n // \"piBackfillModel\": \"inherit\", // or \"provider/model\"\n // \"opencodeBackfillModel\": \"inherit\", // or \"provider/model\"\n\n // Register the web app to start when you log in.\n \"webServerAutoStart\": true,\n // Enable web UI for managing memories (accessible at http://localhost:4747)\n \"webServerEnabled\": true,\n \n // Port for web UI server\n \"webServerPort\": 4747,\n \n // Host address for web UI (use 127.0.0.1 for local only, 0.0.0.0 for network access)\n \"webServerHost\": \"127.0.0.1\",\n\n // Optional HTTP Basic Auth for the web UI. Accepts literal, env://, or file:// secrets.\n // \"webServerAuthPassword\": \"\",\n // \"webServerAuthUsername\": \"\",\n\n // Required when webServerHost is not loopback. Protects /api/* with Bearer / X-Opencode-Mem-Token.\n // \"webServerApiToken\": \"env://OMMS_WEB_TOKEN\",\n \n // ============================================\n // Database Settings\n // ============================================\n \n // Maximum vectors per database shard (auto-creates new shard when limit reached)\n \"maxVectorsPerShard\": 50000,\n \n // Automatically delete old memories based on retention period\n \"autoCleanupEnabled\": true,\n \n // Days to keep memories before auto-cleanup (only if autoCleanupEnabled is true)\n \"autoCleanupRetentionDays\": 30,\n \n // Automatically detect and remove duplicate memories\n \"deduplicationEnabled\": true,\n \n // Similarity threshold (0-1) for detecting duplicates (higher = stricter)\n \"deduplicationSimilarityThreshold\": 0.90,\n \n // ============================================\n // Memory Scope Settings\n // ============================================\n\n // Default scope for memory list/search queries\n // \"project\" keeps queries within the current project, \"all-projects\" searches across all project shards\n \"memory\": {\n \"defaultScope\": \"project\"\n },\n\n // ============================================\n // Model for auto-capture and profile learning (same rule in OpenCode and Pi)\n // ============================================\n\n // Which model summarises your work, in this order:\n // 1. The host model below: opencodeProvider/opencodeModel in OpenCode,\n // piProvider/piModel in Pi. Set the model to \"inherit\" to follow\n // whatever model the session is using.\n // 2. If no host model is set: the external API (memoryModel/memoryApiUrl/\n // memoryApiKey further down).\n // 3. If neither is set: the session's own model.\n // If the host model fails and the external API is configured, the external\n // API is used instead.\n //\n // Host models go through OpenCode's or Pi's own sign-in (OAuth like Claude\n // Pro/Max, GitHub Copilot, ChatGPT, or your own key there), so no separate API\n // key is needed here.\n //\n // Examples (OpenCode names from 'opencode providers list'; Pi names from its model list):\n // \"opencodeProvider\": \"anthropic\", \"opencodeModel\": \"claude-haiku-4-5-20251001\"\n // \"opencodeProvider\": \"openai\", \"opencodeModel\": \"inherit\"\n // \"piProvider\": \"openai-codex\", \"piModel\": \"gpt-5.6-luna\"\n //\n // \"opencodeProvider\": \"anthropic\",\n // \"opencodeModel\": \"claude-haiku-4-5-20251001\",\n // \"piProvider\": \"openai-codex\",\n // \"piModel\": \"gpt-5.6-luna\",\n\n // ============================================\n // Auto-Capture Settings\n // ============================================\n \n // Auto-capture runs in the background without blocking your main session.\n // Note: Ollama may not support tool calling. Use OpenAI, Anthropic, or Groq for best results.\n \n \"autoCaptureEnabled\": true,\n \n // Provider type: \"openai-chat\" | \"openai-responses\" | \"anthropic\" | \"minimax\" | \"orcarouter\"\n // Note: \"openai-chat\" is a generic OpenAI API-compatible mode.\n // Any service that follows the OpenAI Chat Completions API can use it via custom \"memoryApiUrl\".\n \"memoryProvider\": \"openai-chat\",\n \n // External API (step 2 above, and the fallback when a host model fails).\n // Uncomment all 3 lines and replace memoryApiKey before use:\n // \"memoryModel\": \"gpt-4o-mini\",\n // \"memoryApiUrl\": \"https://api.openai.com/v1\",\n // \"memoryApiKey\": \"sk-...\",\n\n // API Key Formats:\n // Direct value: \"sk-...\"\n // From file: \"file://~/.config/litellm-key.txt\"\n // From env variable: \"env://LITELLM_API_KEY\"\n \n // Examples for different providers:\n // Any OpenAI-compatible endpoint can use the \"openai-chat\" provider pattern below.\n // Common examples: DeepSeek, Qwen (via Alibaba Cloud ModelStudio),\n // Zhipu GLM (BigModel platform), and Kimi (Moonshot AI platform).\n\n // OpenAI Chat Completion (default, backward compatible):\n // \"memoryProvider\": \"openai-chat\"\n // \"memoryModel\": \"gpt-4o-mini\"\n // \"memoryApiUrl\": \"https://api.openai.com/v1\"\n // \"memoryApiKey\": \"sk-...\"\n\n // DeepSeek (OpenAI-compatible example):\n // \"memoryProvider\": \"openai-chat\"\n // \"memoryModel\": \"deepseek-chat\"\n // \"memoryApiUrl\": \"https://api.deepseek.com/v1\"\n // \"memoryApiKey\": \"sk-...\"\n \n // OpenAI Responses API (recommended, with session support):\n // \"memoryProvider\": \"openai-responses\"\n // \"memoryModel\": \"gpt-4o\"\n // \"memoryApiUrl\": \"https://api.openai.com/v1\"\n // \"memoryApiKey\": \"sk-...\"\n \n // Anthropic (with session support):\n // \"memoryProvider\": \"anthropic\"\n // \"memoryModel\": \"claude-3-5-haiku-20241022\"\n // \"memoryApiUrl\": \"https://api.anthropic.com/v1\"\n // \"memoryApiKey\": \"sk-ant-...\"\n\n // MiniMax (Anthropic Messages-compatible endpoint, with session support):\n // \"memoryProvider\": \"minimax\"\n // \"memoryModel\": \"MiniMax-M3\"\n // \"memoryApiUrl\": \"https://api.minimax.io\" // global endpoint\n // \"memoryApiKey\": \"<MiniMax API key>\"\n // // China endpoint: \"memoryApiUrl\": \"https://api.minimaxi.com\"\n // // Optional adaptive thinking for MiniMax-M3:\n // \"memoryExtraParams\": { \"thinking\": { \"type\": \"adaptive\" } }\n\n // OrcaRouter (OpenAI-compatible gateway, namespaced model IDs, with session support):\n // \"memoryProvider\": \"orcarouter\"\n // \"memoryApiKey\": \"<OrcaRouter API key>\"\n // // memoryApiUrl and memoryModel are optional \u2014 they default to\n // // https://api.orcarouter.ai/v1 and \"orcarouter/auto\" (a routing alias).\n // // OrcaRouter rejects bare model names, so if you set memoryModel, use a\n // // namespaced ID such as \"openai/gpt-5.5\" or \"deepseek/deepseek-v4-flash\".\n // \"memoryModel\": \"openai/gpt-5.5\"\n\n // Groq (OpenAI-compatible, use openai-chat provider):\n // \"memoryProvider\": \"openai-chat\"\n // \"memoryModel\": \"llama-3.3-70b-versatile\"\n // \"memoryApiUrl\": \"https://api.groq.com/openai/v1\"\n // \"memoryApiKey\": \"gsk_...\"\n \n // Maximum iterations for multi-turn AI analysis (for openai-responses, anthropic, and minimax)\n \"autoCaptureMaxIterations\": 5,\n \n // Timeout per iteration in milliseconds (30 seconds default)\n \"autoCaptureIterationTimeout\": 30000,\n\n // Maximum number of times to retry capturing a prompt if it fails (due to network, API errors, etc.)\n \"autoCaptureMaxRetries\": 3,\n\n // Maximum UTF-8 bytes for the auto-capture markdown context sent to the summary model.\n // Prevents HTTP 400 context overflows on models with ~131K token windows (e.g. Groq Llama).\n // Rough guide: tokens \u2248 bytes / 4 for mixed code/prose.\n \"autoCaptureMaxContextBytes\": 131072,\n \n // Days to keep AI session history before cleanup\n \"aiSessionRetentionDays\": 7,\n\n // Capture diagnostics: every capture attempt always writes one metadata line\n // (model, stop reason, sizes, outcome) to ~/.omms/omms.log, with no\n // conversation text. Set captureTrace to true to also write each attempt's\n // prompt and raw reply to ~/.omms/traces/ for debugging. Traces can contain\n // conversation content: <private> text and common API key formats are\n // redacted, files are readable only by you, and they are deleted after\n // captureTraceRetentionDays. Only this global file can turn tracing on.\n // \"captureTrace\": false,\n // \"captureTraceRetentionDays\": 7,\n // \"captureAttemptRetentionDays\": 30,\n\n // Temperature for AI API requests (set to false to omit parameter for models that don't support it)\n // Some reasoning models (like o1, o3, gpt-5) don't support temperature parameter\n // Set to false and add \"memoryTemperature\": false in config when using such models\n \"memoryTemperature\": 0.3,\n\n // Extra parameters to include in API request body\n // Useful for local inference servers (e.g. llama-server with --jinja) that support\n // additional parameters like disabling thinking/reasoning mode\n // Example for Qwen3 models: { \"enable_thinking\": false }\n // \"memoryExtraParams\": {},\n\n // Language for auto-capture summaries (default: \"auto\" for auto-detection)\n // Options: \"auto\", \"en\", \"id\", \"zh\", \"ja\", \"es\", \"fr\", \"de\", \"ru\", \"pt\", \"ar\", \"ko\"\n // \"autoCaptureLanguage\": \"auto\",\n\n // ============================================\n // Toast Notifications\n // ============================================\n\n // Show toast when memory is auto-captured\n \"showAutoCaptureToasts\": true,\n\n // Show toast when user profile is updated\n \"showUserProfileToasts\": true,\n\n // Show toast for error messages\n \"showErrorToasts\": true,\n\n // ============================================\n // User Profile System\n // ============================================\n\n // Analyze user prompts every N prompts to build/update your user profile\n // When N uncaptured prompts accumulate, AI will analyze them to identify:\n // - User preferences (code style, communication style, tool preferences)\n // - User patterns (recurring topics, problem domains, technical interests)\n // - User workflows (development habits, sequences, learning style)\n // - Skill level (overall and per-domain assessment)\n \"userProfileAnalysisInterval\": 10,\n\n // Days before inactive items (all types) are eligible for removal\n \"userProfileStaleDays\": 2,\n\n // Number of preferences shown in UI\n \"userProfileDisplayPreferences\": 20,\n \n // Number of patterns shown in UI\n \"userProfileDisplayPatterns\": 15,\n \n // Number of workflows shown in UI\n \"userProfileDisplayWorkflows\": 10,\n \n // Number of preferences injected into LLM conversation context\n // Keep this small \u2014 the strongest signals are enough; more dilute LLM attention\n \"userProfileInjectPreferences\": 5,\n \n // Number of patterns injected into LLM conversation context\n \"userProfileInjectPatterns\": 5,\n \n // Number of workflows injected into LLM conversation context\n \"userProfileInjectWorkflows\": 3,\n \n // Days before preference confidence starts to decay (if not reinforced)\n // Preferences that aren't seen again will gradually lose confidence and be removed\n \"userProfileConfidenceDecayDays\": 30,\n \n // Number of profile versions to keep in changelog (for rollback/debugging)\n // Older versions are automatically cleaned up\n \"userProfileChangelogRetentionCount\": 5,\n\n // Minimum evidence count for a preference/pattern to survive confidence decay\n // Items confirmed fewer times are more likely to be pruned when confidence decays\n \"userProfileMinEvidenceForRetention\": 3,\n\n // Periodically merge duplicate or irrelevant profile items with the configured AI provider\n \"userProfileAutoCleanupEnabled\": true,\n // Number of analyzed user prompts between automatic AI cleanup runs\n \"userProfileAutoCleanupInterval\": 100,\n\n // Enable LLM validation of existing preferences against recent behavior.\n // When enabled, each analysis round checks if top-5 preferences still match recent prompts.\n // Experimental \u2014 disabled by default.\n \"userProfileValidationEnabled\": false,\n\n // ============================================\n // Search Settings\n // ============================================\n \n // Minimum similarity score (0-1) for memory search results\n \"similarityThreshold\": 0.6,\n\n // Maximum number of memories to return in search results\n \"maxMemories\": 10,\n\n // ============================================\n // Advanced Settings\n // ============================================\n \n // Inject user profile into AI context (preferences, patterns, workflows)\n \"injectProfile\": true\n}\n";
|
|
93
97
|
export declare function normalizeAutoCaptureMaxContextBytes(value: number): number;
|
|
94
98
|
export declare function normalizeAutoCleanupRetentionDays(value: number): number;
|
|
95
99
|
declare function buildConfig(fileConfig: OmmsConfig): {
|
|
@@ -127,6 +131,10 @@ declare function buildConfig(fileConfig: OmmsConfig): {
|
|
|
127
131
|
captureTrace: boolean;
|
|
128
132
|
captureTraceRetentionDays: number;
|
|
129
133
|
captureAttemptRetentionDays: number;
|
|
134
|
+
autoBackfill: boolean;
|
|
135
|
+
opencodeBackfillModel: string;
|
|
136
|
+
piBackfillModel: string;
|
|
137
|
+
webServerAutoStart: boolean;
|
|
130
138
|
webServerEnabled: boolean;
|
|
131
139
|
webServerPort: number;
|
|
132
140
|
webServerHost: string;
|
|
@@ -215,6 +223,10 @@ export declare let CONFIG: {
|
|
|
215
223
|
captureTrace: boolean;
|
|
216
224
|
captureTraceRetentionDays: number;
|
|
217
225
|
captureAttemptRetentionDays: number;
|
|
226
|
+
autoBackfill: boolean;
|
|
227
|
+
opencodeBackfillModel: string;
|
|
228
|
+
piBackfillModel: string;
|
|
229
|
+
webServerAutoStart: boolean;
|
|
218
230
|
webServerEnabled: boolean;
|
|
219
231
|
webServerPort: number;
|
|
220
232
|
webServerHost: string;
|
package/dist/config.js
CHANGED
|
@@ -6,6 +6,7 @@ import { log } from "./services/logger.js";
|
|
|
6
6
|
import { resolveSecretValue } from "./services/secret-resolver.js";
|
|
7
7
|
import { isPlaceholderApiKey } from "./services/ai/api-key-placeholder.js";
|
|
8
8
|
import { getAutoCaptureProviderStatus } from "./services/ai/live-model-choice.js";
|
|
9
|
+
import { parseBackfillModel } from "./importer/backfill-model.js";
|
|
9
10
|
import { resolveDefaultStoragePath, runLegacyStoreMigration, legacyMigrationPaths, } from "./services/legacy-migration.js";
|
|
10
11
|
const OMMS_CONFIG_DIR = join(homedir(), ".config", "omms");
|
|
11
12
|
const DATA_DIR = join(homedir(), ".omms");
|
|
@@ -51,6 +52,10 @@ const DEFAULTS = {
|
|
|
51
52
|
captureTrace: false,
|
|
52
53
|
captureTraceRetentionDays: 7,
|
|
53
54
|
captureAttemptRetentionDays: 30,
|
|
55
|
+
autoBackfill: true,
|
|
56
|
+
opencodeBackfillModel: "inherit",
|
|
57
|
+
piBackfillModel: "inherit",
|
|
58
|
+
webServerAutoStart: true,
|
|
54
59
|
webServerEnabled: true,
|
|
55
60
|
webServerPort: 4747,
|
|
56
61
|
webServerHost: "127.0.0.1",
|
|
@@ -191,6 +196,13 @@ export const CONFIG_TEMPLATE = `{
|
|
|
191
196
|
// Web Server Settings
|
|
192
197
|
// ============================================
|
|
193
198
|
|
|
199
|
+
// Start a background import of past chats on each host's next start.
|
|
200
|
+
"autoBackfill": true,
|
|
201
|
+
// "piBackfillModel": "inherit", // or "provider/model"
|
|
202
|
+
// "opencodeBackfillModel": "inherit", // or "provider/model"
|
|
203
|
+
|
|
204
|
+
// Register the web app to start when you log in.
|
|
205
|
+
"webServerAutoStart": true,
|
|
194
206
|
// Enable web UI for managing memories (accessible at http://localhost:4747)
|
|
195
207
|
"webServerEnabled": true,
|
|
196
208
|
|
|
@@ -548,6 +560,13 @@ function buildConfig(fileConfig) {
|
|
|
548
560
|
getEmbeddingDimensions(fileConfig.embeddingModel ?? DEFAULTS.embeddingModel);
|
|
549
561
|
const autoCaptureMaxContextBytes = normalizeAutoCaptureMaxContextBytes(fileConfig.autoCaptureMaxContextBytes ?? DEFAULTS.autoCaptureMaxContextBytes);
|
|
550
562
|
const userProfileAutoCleanupInterval = fileConfig.userProfileAutoCleanupInterval ?? DEFAULTS.userProfileAutoCleanupInterval;
|
|
563
|
+
parseBackfillModel(fileConfig, "pi");
|
|
564
|
+
parseBackfillModel(fileConfig, "opencode");
|
|
565
|
+
for (const key of ["autoBackfill", "webServerAutoStart"]) {
|
|
566
|
+
if (fileConfig[key] !== undefined && typeof fileConfig[key] !== "boolean") {
|
|
567
|
+
throw new Error(`Invalid ${key} config`);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
551
570
|
if (!Number.isInteger(embeddingDimensions) ||
|
|
552
571
|
embeddingDimensions <= 0 ||
|
|
553
572
|
embeddingDimensions > 65536) {
|
|
@@ -601,6 +620,10 @@ function buildConfig(fileConfig) {
|
|
|
601
620
|
captureTrace: fileConfig.captureTrace === true,
|
|
602
621
|
captureTraceRetentionDays: Math.max(1, Math.floor(fileConfig.captureTraceRetentionDays ?? DEFAULTS.captureTraceRetentionDays)),
|
|
603
622
|
captureAttemptRetentionDays: normalizeAutoCleanupRetentionDays(fileConfig.captureAttemptRetentionDays ?? DEFAULTS.captureAttemptRetentionDays),
|
|
623
|
+
autoBackfill: fileConfig.autoBackfill ?? DEFAULTS.autoBackfill,
|
|
624
|
+
opencodeBackfillModel: fileConfig.opencodeBackfillModel ?? DEFAULTS.opencodeBackfillModel,
|
|
625
|
+
piBackfillModel: fileConfig.piBackfillModel ?? DEFAULTS.piBackfillModel,
|
|
626
|
+
webServerAutoStart: fileConfig.webServerAutoStart ?? DEFAULTS.webServerAutoStart,
|
|
604
627
|
webServerEnabled: fileConfig.webServerEnabled ?? DEFAULTS.webServerEnabled,
|
|
605
628
|
webServerPort: fileConfig.webServerPort ?? DEFAULTS.webServerPort,
|
|
606
629
|
webServerHost: fileConfig.webServerHost ?? DEFAULTS.webServerHost,
|
|
@@ -751,6 +774,11 @@ export function initConfig(directory, options = {}) {
|
|
|
751
774
|
delete projectOverrides.captureTrace;
|
|
752
775
|
}
|
|
753
776
|
delete projectOverrides.captureTraceRetentionDays;
|
|
777
|
+
delete projectOverrides.autoBackfill;
|
|
778
|
+
delete projectOverrides.opencodeBackfillModel;
|
|
779
|
+
delete projectOverrides.piBackfillModel;
|
|
780
|
+
delete projectOverrides.webServerAutoStart;
|
|
781
|
+
delete projectOverrides.webServerEnabled;
|
|
754
782
|
const merged = { ...globalConfig, ...projectOverrides };
|
|
755
783
|
const nextConfig = buildConfig(merged);
|
|
756
784
|
lastFileConfig = merged;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { BackfillHost } from "./backfill-model.js";
|
|
2
|
+
import { runHistoryImport, type HistoryImportModels } from "./run-import.js";
|
|
3
|
+
type Run = typeof runHistoryImport;
|
|
4
|
+
export interface AutoBackfillOptions {
|
|
5
|
+
host: BackfillHost;
|
|
6
|
+
cwd: string;
|
|
7
|
+
resolveModels: () => Promise<{
|
|
8
|
+
model: string;
|
|
9
|
+
models: HistoryImportModels;
|
|
10
|
+
}>;
|
|
11
|
+
notify: (message: string) => void;
|
|
12
|
+
run?: Run;
|
|
13
|
+
wait?: () => Promise<void>;
|
|
14
|
+
now?: () => number;
|
|
15
|
+
enabled?: () => boolean;
|
|
16
|
+
signal?: AbortSignal;
|
|
17
|
+
}
|
|
18
|
+
/** Schedule one host's history import after startup; callers do not await it. */
|
|
19
|
+
export declare function scheduleAutoBackfill(options: AutoBackfillOptions): Promise<void>;
|
|
20
|
+
export {};
|