om-memory-system 5.9.1 → 5.10.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.
Files changed (86) hide show
  1. package/README.md +7 -11
  2. package/dist/adapters/opencode/conversation.js +4 -2
  3. package/dist/adapters/opencode/memory-tool.d.ts +4 -0
  4. package/dist/adapters/opencode/memory-tool.js +53 -0
  5. package/dist/adapters/opencode/package-skills.d.ts +1 -1
  6. package/dist/adapters/opencode/package-skills.js +17 -15
  7. package/dist/adapters/pi/extension.js +7 -0
  8. package/dist/cli/memory-command.d.ts +2 -0
  9. package/dist/cli/memory-command.js +35 -9
  10. package/dist/config-paths.d.ts +5 -0
  11. package/dist/config-paths.js +15 -0
  12. package/dist/config-template.d.ts +1 -1
  13. package/dist/config-template.js +8 -0
  14. package/dist/config.d.ts +51 -40
  15. package/dist/config.js +8 -15
  16. package/dist/core/capture.d.ts +2 -1
  17. package/dist/core/capture.js +8 -1
  18. package/dist/core/host.d.ts +12 -0
  19. package/dist/core/memorisation-operations.d.ts +33 -0
  20. package/dist/core/memorisation-operations.js +160 -0
  21. package/dist/core/memorisation-settings.d.ts +11 -0
  22. package/dist/core/memorisation-settings.js +48 -0
  23. package/dist/core/memorisation.d.ts +43 -0
  24. package/dist/core/memorisation.js +76 -0
  25. package/dist/core/memorise-turn.d.ts +11 -0
  26. package/dist/core/memorise-turn.js +64 -0
  27. package/dist/core/memory-operations.d.ts +6 -1
  28. package/dist/core/memory-operations.js +29 -0
  29. package/dist/core/memory-tool-text.js +1 -1
  30. package/dist/core/retrieval.d.ts +3 -2
  31. package/dist/core/retrieval.js +11 -4
  32. package/dist/core/tool-outcome.d.ts +6 -0
  33. package/dist/core/tool-outcome.js +31 -0
  34. package/dist/importer/claude-conversation.js +8 -1
  35. package/dist/importer/claude-turn-units.js +1 -0
  36. package/dist/importer/importer.js +3 -3
  37. package/dist/importer/opencode-reader.js +2 -1
  38. package/dist/importer/pi-conversation.js +11 -5
  39. package/dist/index.js +2 -42
  40. package/dist/services/api-handlers.js +13 -35
  41. package/dist/services/capture-diagnostics.d.ts +3 -2
  42. package/dist/services/capture-diagnostics.js +5 -1
  43. package/dist/services/cleanup-service.js +2 -1
  44. package/dist/services/client-session-read.d.ts +16 -0
  45. package/dist/services/client-session-read.js +32 -0
  46. package/dist/services/client.d.ts +6 -1
  47. package/dist/services/client.js +20 -36
  48. package/dist/services/deduplication-service.js +12 -1
  49. package/dist/services/global-config-writer.js +14 -1
  50. package/dist/services/linked-search.d.ts +15 -0
  51. package/dist/services/linked-search.js +23 -2
  52. package/dist/services/live-captured-entries.js +2 -1
  53. package/dist/services/memorisation-routes.d.ts +16 -0
  54. package/dist/services/memorisation-routes.js +94 -0
  55. package/dist/services/memorisation-store.d.ts +45 -0
  56. package/dist/services/memorisation-store.js +120 -0
  57. package/dist/services/memory-kind.d.ts +6 -0
  58. package/dist/services/memory-kind.js +6 -0
  59. package/dist/services/memory-portability-service.js +2 -1
  60. package/dist/services/memory-replace.d.ts +8 -0
  61. package/dist/services/memory-replace.js +26 -0
  62. package/dist/services/retrieval-rerank.d.ts +3 -1
  63. package/dist/services/retrieval-rerank.js +1 -1
  64. package/dist/services/settings-snapshot.js +6 -0
  65. package/dist/services/turso/exact-scan.d.ts +2 -1
  66. package/dist/services/turso/exact-scan.js +4 -3
  67. package/dist/services/turso/keyword-search.d.ts +2 -1
  68. package/dist/services/turso/keyword-search.js +3 -2
  69. package/dist/services/turso/vector-search.d.ts +10 -9
  70. package/dist/services/turso/vector-search.js +38 -95
  71. package/dist/services/turso/vector-writes.d.ts +8 -0
  72. package/dist/services/turso/vector-writes.js +80 -0
  73. package/dist/services/web-ensure.d.ts +1 -0
  74. package/dist/services/web-ensure.js +5 -1
  75. package/dist/services/web-memory-rows.d.ts +12 -0
  76. package/dist/services/web-memory-rows.js +58 -0
  77. package/dist/services/web-route-modules.js +5 -0
  78. package/dist/v2/adapter.d.ts +13 -1
  79. package/dist/v2/adapter.js +7 -0
  80. package/dist/web/assets/{index-Be6Uh64d.css → index-CoYuPYCS.css} +1 -1
  81. package/dist/web/assets/index-DQgI670D.js +171 -0
  82. package/dist/web/index.html +2 -2
  83. package/package.json +1 -1
  84. package/skills/omms-memory/SKILL.md +18 -0
  85. package/skills/omms-remember/SKILL.md +118 -0
  86. package/dist/web/assets/index-BX_zZiKa.js +0 -170
package/README.md CHANGED
@@ -23,7 +23,7 @@ for model choices and privacy controls.
23
23
  ## Set up
24
24
 
25
25
  Use Node.js **22.14 or later** for the terminal command and Claude Code hooks.
26
- OpenCode v1 needs **1.18.29 or later**. OMMS also supports OpenCode v2 and the
26
+ OMMS supports OpenCode v2, Claude Code and the
27
27
  [Pi coding agent](https://www.npmjs.com/package/@earendil-works/pi-coding-agent).
28
28
 
29
29
  > Automatic history import is enabled by default and makes model calls.
@@ -35,19 +35,11 @@ OpenCode v1 needs **1.18.29 or later**. OMMS also supports OpenCode v2 and the
35
35
  Add the package to `~/.config/opencode/opencode.json`. Keep your existing settings.
36
36
  On Windows, use `%USERPROFILE%\.config\opencode\opencode.json`.
37
37
 
38
- **OpenCode v2:**
39
-
40
38
  ```json
41
39
  { "plugins": ["om-memory-system"] }
42
40
  ```
43
41
 
44
- **OpenCode v1:**
45
-
46
- ```json
47
- { "plugin": ["om-memory-system"] }
48
- ```
49
-
50
- OpenCode v2 also supports:
42
+ Or run:
51
43
 
52
44
  ```bash
53
45
  opencode plugin add om-memory-system
@@ -121,6 +113,10 @@ cannot be saved.
121
113
 
122
114
  Keep separate stores for separate lines of work. The Spaces item in the web app sidebar opens a page with one card for each space. Switching a space applies to every host. Memories can be copied or moved between spaces, exported to a shareable file and imported into a new space, and a space can read the spaces it links. See [Memory spaces](docs/memory-spaces.md).
123
115
 
116
+ ### Memorisations
117
+
118
+ Save an exact note on purpose and fetch it later by ID, such as `ms-4k7q2p`, or by a close description. A memorisation stays out of automatic recall and cleanup, and the Memorisation page lists, labels, filters and deletes them. See [Memorise and recall](docs/using-memory.md#memorise-and-recall).
119
+
124
120
  ### Settings
125
121
 
126
122
  Choose models and manage access. Diagnostics show capture outcomes and component health.
@@ -131,7 +127,7 @@ Choose models and manage access. Diagnostics show capture outcomes and component
131
127
 
132
128
  | Guide | Covers |
133
129
  | ------------------------------------------- | ----------------------------------------------------------------- |
134
- | [Using memory](docs/using-memory.md) | Daily use, capture, recall and the user profile |
130
+ | [Using memory](docs/using-memory.md) | Daily use, capture, recall, memorisations and the user profile |
135
131
  | [Web UI](docs/web-ui.md) | Starting the app, browsing memories and safe network access |
136
132
  | [Memory page](docs/web-ui-memory.md) | One card per host, imports, shared settings and avatars |
137
133
  | [Settings page](docs/web-ui-settings.md) | Models, credentials, diagnostics and profile identities |
@@ -1,5 +1,5 @@
1
1
  import { boundToolInput, readToolPath } from "../../core/tool-input.js";
2
- import { opencodeToolOutcome } from "../../core/tool-outcome.js";
2
+ import { opencodeMemorise, opencodeToolOutcome } from "../../core/tool-outcome.js";
3
3
  function formatToolInput(inputValue) {
4
4
  if (!inputValue)
5
5
  return "";
@@ -40,13 +40,15 @@ export function extractOpenCodeConversation(messages, promptMessageId) {
40
40
  .trim());
41
41
  }
42
42
  for (const tool of message.parts.filter((part) => part.type === "tool")) {
43
- const name = tool.tool || "unknown";
43
+ // V1 parts name the tool `tool`; the V2 plugin API names it `name`.
44
+ const name = tool.tool || tool.name || "unknown";
44
45
  const input = boundToolInput(formatToolInput(tool.state?.input));
45
46
  const path = readToolPath(name, tool.state?.input);
46
47
  toolCalls.push({
47
48
  name,
48
49
  input,
49
50
  ...opencodeToolOutcome(tool.state),
51
+ ...opencodeMemorise(name, tool.state?.input, tool.state),
50
52
  ...(path === undefined ? {} : { path }),
51
53
  });
52
54
  }
@@ -0,0 +1,4 @@
1
+ import { type ToolDefinition } from "@opencode-ai/plugin";
2
+ import { type MemoryOperationContext } from "../../core/memory-operations.js";
3
+ /** The OpenCode V1 `memory` tool. V2 reaches it through the V1 plugin's `tool.memory`. */
4
+ export declare function opencodeMemoryTool(description: string, base: Pick<MemoryOperationContext, "directory" | "tags">): ToolDefinition;
@@ -0,0 +1,53 @@
1
+ import { tool } from "@opencode-ai/plugin";
2
+ import { executeMemoryOperation, } from "../../core/memory-operations.js";
3
+ /** The OpenCode V1 `memory` tool. V2 reaches it through the V1 plugin's `tool.memory`. */
4
+ export function opencodeMemoryTool(description, base) {
5
+ return tool({
6
+ description,
7
+ args: {
8
+ mode: tool.schema
9
+ .enum([
10
+ "add",
11
+ "memorise",
12
+ "memorize",
13
+ "recall",
14
+ "search",
15
+ "profile",
16
+ "list",
17
+ "forget",
18
+ "help",
19
+ "migrate",
20
+ "list-shards",
21
+ "export",
22
+ "import",
23
+ ])
24
+ .optional(),
25
+ content: tool.schema.string().optional(),
26
+ query: tool.schema.string().optional(),
27
+ tags: tool.schema.string().optional(),
28
+ type: tool.schema.string().optional(),
29
+ topic: tool.schema.string().optional(),
30
+ done: tool.schema.string().optional(),
31
+ doing: tool.schema.string().optional(),
32
+ next: tool.schema.string().optional(),
33
+ memoryId: tool.schema.string().optional(),
34
+ limit: tool.schema.number().optional(),
35
+ scope: tool.schema.enum(["project", "all-projects"]).optional(),
36
+ fromPath: tool.schema.string().optional(),
37
+ fromHash: tool.schema.string().optional(),
38
+ outputPath: tool.schema.string().optional(),
39
+ inputPath: tool.schema.string().optional(),
40
+ dryRun: tool.schema.boolean().optional(),
41
+ allowLinkedSource: tool.schema.boolean().optional(),
42
+ },
43
+ async execute(args, context) {
44
+ // The session ID lets `memorise` replace its own snapshot.
45
+ const result = await executeMemoryOperation(args, {
46
+ ...base,
47
+ host: "opencode",
48
+ hostSessionId: context?.sessionID,
49
+ });
50
+ return JSON.stringify(result);
51
+ },
52
+ });
53
+ }
@@ -9,7 +9,7 @@ export declare function packageSkillsFolder(moduleUrl?: string): string;
9
9
  * SDK `Config` type has no `skills` key, although OpenCode reads it.
10
10
  */
11
11
  export declare function applySkillsPathConfig(config: object, folder?: string): void;
12
- /** V2: add the omms-memory skill to the plugin's skill list once. */
12
+ /** V2: add the package's skills to the plugin's skill list once. */
13
13
  export declare function addPackageSkill(editor: {
14
14
  get(id: string): unknown;
15
15
  add(skill: any): void;
@@ -2,7 +2,7 @@ import { existsSync, readFileSync } from "node:fs";
2
2
  import { dirname, join } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import { log } from "../../services/logger.js";
5
- const SKILL_NAME = "omms-memory";
5
+ const SKILL_NAMES = ["omms-memory", "omms-remember"];
6
6
  /**
7
7
  * The package's `skills` folder: the nearest parent folder whose
8
8
  * `package.json` names om-memory-system. Walking up also works when a host
@@ -34,7 +34,7 @@ const defaultFolder = packageSkillsFolder();
34
34
  export function applySkillsPathConfig(config, folder = defaultFolder) {
35
35
  const cfg = config;
36
36
  if (!existsSync(folder)) {
37
- log("OMMS skills folder not found; the omms-memory skill is not loaded", { folder });
37
+ log("OMMS skills folder not found; the packaged skills are not loaded", { folder });
38
38
  return;
39
39
  }
40
40
  const paths = cfg.skills?.paths ?? [];
@@ -42,19 +42,21 @@ export function applySkillsPathConfig(config, folder = defaultFolder) {
42
42
  return;
43
43
  cfg.skills = { ...cfg.skills, paths: [...paths, folder] };
44
44
  }
45
- /** V2: add the omms-memory skill to the plugin's skill list once. */
45
+ /** V2: add the package's skills to the plugin's skill list once. */
46
46
  export function addPackageSkill(editor, folder = defaultFolder) {
47
- if (editor.get(SKILL_NAME))
48
- return;
49
- const path = join(folder, SKILL_NAME, "SKILL.md");
50
- let content;
51
- try {
52
- content = readFileSync(path, "utf8");
53
- }
54
- catch {
55
- log("OMMS skill file not found; the omms-memory skill is not loaded", { path });
56
- return;
47
+ for (const name of SKILL_NAMES) {
48
+ if (editor.get(name))
49
+ continue;
50
+ const path = join(folder, name, "SKILL.md");
51
+ let content;
52
+ try {
53
+ content = readFileSync(path, "utf8");
54
+ }
55
+ catch {
56
+ log("OMMS skill file not found; the skill is not loaded", { path });
57
+ continue;
58
+ }
59
+ const description = content.match(/^description: (.+)$/m)?.[1]?.trim();
60
+ editor.add({ id: name, name, description, path, content });
57
61
  }
58
- const description = content.match(/^description: (.+)$/m)?.[1]?.trim();
59
- editor.add({ id: SKILL_NAME, name: SKILL_NAME, description, path, content });
60
62
  }
@@ -22,6 +22,9 @@ const GLOBAL_PI_BACKFILL_KEY = Symbol.for("omms.pi.backfill.scheduled");
22
22
  const OMMS_STATUS_KEY = "omms";
23
23
  const MEMORY_TOOL_MODES = [
24
24
  "add",
25
+ "memorise",
26
+ "memorize",
27
+ "recall",
25
28
  "search",
26
29
  "profile",
27
30
  "list",
@@ -355,6 +358,10 @@ export default function ommsPiExtension(pi) {
355
358
  query: Type.Optional(Type.String()),
356
359
  tags: Type.Optional(Type.String()),
357
360
  type: Type.Optional(Type.String()),
361
+ topic: Type.Optional(Type.String()),
362
+ done: Type.Optional(Type.String()),
363
+ doing: Type.Optional(Type.String()),
364
+ next: Type.Optional(Type.String()),
358
365
  memoryId: Type.Optional(Type.String()),
359
366
  limit: Type.Optional(Type.Number()),
360
367
  scope: Type.Optional(Type.Union([Type.Literal("project"), Type.Literal("all-projects")])),
@@ -16,6 +16,8 @@ interface ParsedMemoryCommand {
16
16
  help: boolean;
17
17
  args: MemoryOperationArgs;
18
18
  directory?: string;
19
+ /** From `--session-id`; becomes the operation context's `hostSessionId`. */
20
+ hostSessionId?: string;
19
21
  }
20
22
  /** Parse `<mode> [options]` into memory tool arguments. Throws on bad input. */
21
23
  export declare function parseMemoryCommandArgs(argv: string[]): ParsedMemoryCommand;
@@ -1,6 +1,9 @@
1
1
  import { resolve } from "node:path";
2
2
  const MODES = {
3
3
  add: "Store a memory (--content, optional --type and --tags)",
4
+ memorise: "Save an exact memorisation (text, or --topic --done --doing --next; optional --tags)",
5
+ memorize: "Same as memorise",
6
+ recall: "Fetch memorisations by ms- ID, text or label (argument or --query, optional --tags)",
4
7
  search: "Search project memories (query as an argument or --query)",
5
8
  profile: "Show the user profile, or save a preference with --content",
6
9
  list: "List recent memories (--limit, default 20)",
@@ -17,6 +20,11 @@ const VALUE_FLAGS = {
17
20
  "--query": "query",
18
21
  "--type": "type",
19
22
  "--tags": "tags",
23
+ "--topic": "topic",
24
+ "--done": "done",
25
+ "--doing": "doing",
26
+ "--next": "next",
27
+ "--session-id": "hostSessionId",
20
28
  "--id": "memoryId",
21
29
  "--limit": "limit",
22
30
  "--scope": "scope",
@@ -43,10 +51,15 @@ ${Object.entries(MODES)
43
51
  .join("\n")}
44
52
 
45
53
  Options:
46
- --content <text> Memory text (add) or preference text (profile)
47
- --query <text> Search text (search)
54
+ --content <text> Memory text (add), exact text (memorise) or preference text (profile)
55
+ --query <text> Search text (search), or an ms- ID or text (recall)
48
56
  --type <type> Memory type, for example decision or bug-fix (add)
49
- --tags <a,b> Comma-separated tags (add)
57
+ --tags <a,b> Comma-separated tags (add, memorise, recall)
58
+ --topic <text> Snapshot topic (memorise)
59
+ --done <text> Snapshot: what is done (memorise)
60
+ --doing <text> Snapshot: what is in progress (memorise)
61
+ --next <text> Snapshot: what comes next (memorise)
62
+ --session-id <id> Host session ID, so a repeat snapshot can replace its earlier one (memorise)
50
63
  --id <id> Memory id (forget)
51
64
  --limit <n> Maximum results (search, list)
52
65
  --scope <scope> project or all-projects (search, list)
@@ -65,7 +78,10 @@ Text inside <private>...</private> is removed before storage.
65
78
 
66
79
  Examples:
67
80
  om-memory-system memory search "database choice"
68
- om-memory-system memory add --content "Use libSQL for the store" --type decision`;
81
+ om-memory-system memory add --content "Use libSQL for the store" --type decision
82
+ om-memory-system memory memorise --topic "Auth" --doing "Token refresh" --next "Tests"
83
+ om-memory-system memory recall ms-4k7q2p
84
+ om-memory-system memory recall --tags release,wip "token refresh"`;
69
85
  function isMode(value) {
70
86
  return Object.hasOwn(MODES, value);
71
87
  }
@@ -129,10 +145,16 @@ export function parseMemoryCommandArgs(argv) {
129
145
  }
130
146
  }
131
147
  else if (positionals.length > 0) {
132
- if (mode !== "search" || positionals.length > 1 || values.query !== undefined) {
148
+ // One positional: search and recall read it as the query, memorise as the text.
149
+ const target = mode === "search" || mode === "recall"
150
+ ? "query"
151
+ : mode === "memorise" || mode === "memorize"
152
+ ? "content"
153
+ : undefined;
154
+ if (!target || positionals.length > 1 || values[target] !== undefined) {
133
155
  throw new Error(`Unexpected argument "${positionals[0]}"`);
134
156
  }
135
- values.query = positionals[0];
157
+ values[target] = positionals[0];
136
158
  }
137
159
  for (const [key, value] of Object.entries(values)) {
138
160
  if (key === "limit") {
@@ -148,11 +170,11 @@ export function parseMemoryCommandArgs(argv) {
148
170
  }
149
171
  args.scope = value;
150
172
  }
151
- else if (key !== "directory") {
173
+ else if (key !== "directory" && key !== "hostSessionId") {
152
174
  args[key] = value;
153
175
  }
154
176
  }
155
- return { help: false, args, directory: values.directory };
177
+ return { help: false, args, directory: values.directory, hostSessionId: values.hostSessionId };
156
178
  }
157
179
  /** Run `om-memory-system memory ...` and return the process exit code. */
158
180
  export async function runMemoryCommand(argv, io = {}) {
@@ -183,7 +205,11 @@ export async function runMemoryCommand(argv, io = {}) {
183
205
  const { executeMemoryOperation } = await import("../core/memory-operations.js");
184
206
  const { memoryClient } = await import("../services/client.js");
185
207
  try {
186
- result = await executeMemoryOperation(parsed.args, { directory, host: "claude-code" });
208
+ result = await executeMemoryOperation(parsed.args, {
209
+ directory,
210
+ host: "claude-code",
211
+ hostSessionId: parsed.hostSessionId,
212
+ });
187
213
  }
188
214
  finally {
189
215
  // Release the store so the process can exit once the reply is printed.
@@ -0,0 +1,5 @@
1
+ /** Global config and data locations. Kept apart from config assembly. */
2
+ export declare const OMMS_CONFIG_DIR: string;
3
+ export declare const DATA_DIR: string;
4
+ export declare const LEGACY_CONFIG_FILES: string[];
5
+ export declare const CONFIG_FILES: string[];
@@ -0,0 +1,15 @@
1
+ import { join } from "node:path";
2
+ import { homedir } from "node:os";
3
+ /** Global config and data locations. Kept apart from config assembly. */
4
+ export const OMMS_CONFIG_DIR = join(homedir(), ".config", "omms");
5
+ export const DATA_DIR = join(homedir(), ".omms");
6
+ export const LEGACY_CONFIG_FILES = [
7
+ join(homedir(), ".config", "opencode", "opencode-mem.jsonc"),
8
+ join(homedir(), ".config", "opencode", "opencode-mem.json"),
9
+ ];
10
+ // Dual-read: the omms config wins; a legacy config is read only without an omms file.
11
+ export const CONFIG_FILES = [
12
+ join(OMMS_CONFIG_DIR, "omms.jsonc"),
13
+ join(OMMS_CONFIG_DIR, "omms.json"),
14
+ ...LEGACY_CONFIG_FILES,
15
+ ];
@@ -3,4 +3,4 @@
3
3
  * src/config.ts until that file reached its size ceiling; the text is
4
4
  * unchanged. Shared by config.ts and the global settings writer.
5
5
  */
6
- 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 embeddingApiUrl (OpenAI-compatible /embeddings); embeddingApiKey is optional.\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 (URL required; key only if the server needs one)\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 \"external\", or \"provider/model\"\n // \"opencodeBackfillModel\": \"inherit\", // or \"external\", or \"provider/model\"\n // Directory maps for history recorded in moved or deleted directories.\n // \"importPathMaps\": [{ \"from\": \"~/code/app-feat-x\", \"to\": \"~/code/app\" }],\n // Unresolved directories to hide on the Directory maps page. They stay unimported.\n // \"importIgnoredDirectories\": [\"/private/tmp/scratch\"],\n // Claude Code's folder, when it is not ~/.claude (like CLAUDE_CONFIG_DIR).\n // \"claudeConfigDir\": \"~/.claude-work\",\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 // Deprecated: imported once into the API token table, then no longer read.\n // Create API tokens on the Settings page (Keys and access) instead.\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, or to \"external\" to send\n // every call to the external API below.\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\" | \"google-gemini\"\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 // Tries for one turn's capture, on Pi and OpenCode, 2 and 4 seconds apart. After the\n // last one fails, a turn the model could not reach goes to the capture retry queue.\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 // Skill learning: record each full turn (prompt, replies, tool calls with\n // their outcome, and the skills used) in ~/.omms/data/skills.db, so OMMS can\n // rank your skills and draft better versions. Off by default. A project config\n // can turn it off but never on. No model call is made to record a turn.\n // \"skillLearning\": false,\n // Remove <private> text and common API key formats before a turn is stored.\n // Global config only. Turning this off stores text as written.\n // \"skillLearningRedact\": true,\n // Folders OMMS reads skills from. Absolute or starting with ~/. Global config\n // only. Unset: ~/.agents/skills and the Claude Code folder's skills folder.\n // \"skillFolders\": [\"~/.agents/skills\"],\n\n // Visible recall: the OpenCode terminal TUI and Pi's interactive terminal draw\n // a row for each memory recall. On by default. A project config can turn it\n // off but never on.\n // \"visibleRecall\": true,\n\n // Memory injection, on OpenCode V2, Pi and Claude Code. Each prompt gets the\n // memories that match it. openingContext also searches memory with\n // openingContextQuery and adds the matches to the\n // first message of a new session. The query is global only, trimmed, and\n // 500 characters at most. subagentMemory gives each subagent the memories\n // that match its task. For both switches, a project config can only turn these off.\n // \"openingContext\": false,\n // \"openingContextQuery\": \"project architecture, setup, conventions and key decisions\",\n // \"subagentMemory\": true,\n\n // Capture retry queue: when a live capture fails because the model cannot be\n // reached (network error, timeout, HTTP 408, 429 or 5xx), OMMS keeps a copy of\n // the turn in ~/.omms/data and tries it again later. The copy has <private>\n // text and common API key formats removed. Turns are deleted when the retry\n // ends or after captureRetryRetentionHours (0 to 720). 0 turns the queue off.\n // Only this global file can set it.\n // \"captureRetryRetentionHours\": 72,\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 // Days before an inactive workflow is eligible for removal. A workflow seen\n // userProfileMinEvidenceForRetention times or more is kept.\n \"userProfileWorkflowStaleDays\": 30,\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 // Merge duplicate or vague profile items every N analysed prompts (about 1 model call per 100 items)\n \"userProfileAutoCleanupEnabled\": true,\n \"userProfileAutoCleanupInterval\": 500,\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. Leave unset to\n // follow the default for your embedding model: 0.5 for qwen3-embedding:0.6b\n // (the only model measured), 0.6 for every other embedder.\n // \"similarityThreshold\": 0.5,\n\n // Maximum number of memories to return in search results.\n // Manual searches can ask for fewer; prompt retrieval uses this ceiling.\n \"maxMemories\": 10,\n\n // ============================================\n // Memory Context Controls\n // ============================================\n\n // Recent memories added at fresh session start (OpenCode V1 and Claude Code).\n // Pi and OpenCode V2 search the store with each prompt instead.\n \"chatMessage\": {\n \"enabled\": true,\n // Recent memories injected at session start.\n \"maxMemories\": 3,\n \"excludeCurrentSession\": true,\n // When to inject: \"first\" message of a session or \"always\".\n // \"injectOn\": \"first\",\n },\n\n // OpenCode profile-learning input limit, in UTF-8 bytes. Smaller values can\n // omit prompts from that input. Does not limit Pi or Claude Code profile input.\n \"userProfileMaxContextBytes\": 32768,\n\n // Shared budget for automatic memory context (profile text, memories, and\n // formatting) across all hosts, in approximate tokens: ceil(UTF-8 bytes / 4).\n // Covers prompt retrieval, recent-memory injection, and session-memory\n // restoration. Not an exact provider token limit or a spending limit.\n \"retrievalMaxTokens\": 2000,\n\n // Restore a session's own memories after compaction and after resume, on\n // OpenCode V2, Pi and Claude Code. memoryLimit is the most memories to\n // restore. A project config can turn it on or off.\n // \"compaction\": { \"enabled\": true, \"memoryLimit\": 10 },\n\n // ============================================\n // Advanced Settings\n // ============================================\n \n // Inject user profile into AI context (preferences, patterns, workflows)\n \"injectProfile\": true\n}\n";
6
+ 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 embeddingApiUrl (OpenAI-compatible /embeddings); embeddingApiKey is optional.\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 (URL required; key only if the server needs one)\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 \"external\", or \"provider/model\"\n // \"opencodeBackfillModel\": \"inherit\", // or \"external\", or \"provider/model\"\n // Directory maps for history recorded in moved or deleted directories.\n // \"importPathMaps\": [{ \"from\": \"~/code/app-feat-x\", \"to\": \"~/code/app\" }],\n // Unresolved directories to hide on the Directory maps page. They stay unimported.\n // \"importIgnoredDirectories\": [\"/private/tmp/scratch\"],\n // Claude Code's folder, when it is not ~/.claude (like CLAUDE_CONFIG_DIR).\n // \"claudeConfigDir\": \"~/.claude-work\",\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 // Deprecated: imported once into the API token table, then no longer read.\n // Create API tokens on the Settings page (Keys and access) instead.\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, or to \"external\" to send\n // every call to the external API below.\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\" | \"google-gemini\"\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 // Tries for one turn's capture, on Pi and OpenCode, 2 and 4 seconds apart. After the\n // last one fails, a turn the model could not reach goes to the capture retry queue.\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 // Skill learning: record each full turn (prompt, replies, tool calls with\n // their outcome, and the skills used) in ~/.omms/data/skills.db, so OMMS can\n // rank your skills and draft better versions. Off by default. A project config\n // can turn it off but never on. No model call is made to record a turn.\n // \"skillLearning\": false,\n // Remove <private> text and common API key formats before a turn is stored.\n // Global config only. Turning this off stores text as written.\n // \"skillLearningRedact\": true,\n // Folders OMMS reads skills from. Absolute or starting with ~/. Global config\n // only. Unset: ~/.agents/skills and the Claude Code folder's skills folder.\n // \"skillFolders\": [\"~/.agents/skills\"],\n\n // Visible recall: the OpenCode terminal TUI and Pi's interactive terminal draw\n // a row for each memory recall. On by default. A project config can turn it\n // off but never on.\n // \"visibleRecall\": true,\n\n // Memory injection, on OpenCode V2, Pi and Claude Code. Each prompt gets the\n // memories that match it. openingContext also searches memory with\n // openingContextQuery and adds the matches to the\n // first message of a new session. The query is global only, trimmed, and\n // 500 characters at most. subagentMemory gives each subagent the memories\n // that match its task. For both switches, a project config can only turn these off.\n // \"openingContext\": false,\n // \"openingContextQuery\": \"project architecture, setup, conventions and key decisions\",\n // \"subagentMemory\": true,\n\n // Capture retry queue: when a live capture fails because the model cannot be\n // reached (network error, timeout, HTTP 408, 429 or 5xx), OMMS keeps a copy of\n // the turn in ~/.omms/data and tries it again later. The copy has <private>\n // text and common API key formats removed. Turns are deleted when the retry\n // ends or after captureRetryRetentionHours (0 to 720). 0 turns the queue off.\n // Only this global file can set it.\n // \"captureRetryRetentionHours\": 72,\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 // Days before an inactive workflow is eligible for removal. A workflow seen\n // userProfileMinEvidenceForRetention times or more is kept.\n \"userProfileWorkflowStaleDays\": 30,\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 // Merge duplicate or vague profile items every N analysed prompts (about 1 model call per 100 items)\n \"userProfileAutoCleanupEnabled\": true,\n \"userProfileAutoCleanupInterval\": 500,\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. Leave unset to\n // follow the default for your embedding model: 0.5 for qwen3-embedding:0.6b\n // (the only model measured), 0.6 for every other embedder.\n // \"similarityThreshold\": 0.5,\n\n // Maximum number of memories to return in search results.\n // Manual searches can ask for fewer; prompt retrieval uses this ceiling.\n \"maxMemories\": 10,\n\n // ============================================\n // Memory Context Controls\n // ============================================\n\n // Recent memories added at fresh session start (OpenCode V1 and Claude Code).\n // Pi and OpenCode V2 search the store with each prompt instead.\n \"chatMessage\": {\n \"enabled\": true,\n // Recent memories injected at session start.\n \"maxMemories\": 3,\n \"excludeCurrentSession\": true,\n // When to inject: \"first\" message of a session or \"always\".\n // \"injectOn\": \"first\",\n },\n\n // OpenCode profile-learning input limit, in UTF-8 bytes. Smaller values can\n // omit prompts from that input. Does not limit Pi or Claude Code profile input.\n \"userProfileMaxContextBytes\": 32768,\n\n // Shared budget for automatic memory context (profile text, memories, and\n // formatting) across all hosts, in approximate tokens: ceil(UTF-8 bytes / 4).\n // Covers prompt retrieval, recent-memory injection, and session-memory\n // restoration. Not an exact provider token limit or a spending limit.\n \"retrievalMaxTokens\": 2000,\n\n // Memorisation settings (global config only).\n // Repeat for a session snapshot: \"add\" or \"replace\" (default \"add\").\n // \"memorisationRepeat\": \"add\",\n // Maximum stored text: 100 to 20,000 characters or 25 to 5,000 tokens.\n // Tokens are estimated with the same rule as retrievalMaxTokens.\n // \"memorisationMaxSize\": 2000,\n // \"memorisationSizeUnit\": \"characters\", // or \"tokens\"\n\n // Restore a session's own memories after compaction and after resume, on\n // OpenCode V2, Pi and Claude Code. memoryLimit is the most memories to\n // restore. A project config can turn it on or off.\n // \"compaction\": { \"enabled\": true, \"memoryLimit\": 10 },\n\n // ============================================\n // Advanced Settings\n // ============================================\n \n // Inject user profile into AI context (preferences, patterns, workflows)\n \"injectProfile\": true\n}\n";
@@ -402,6 +402,14 @@ export const CONFIG_TEMPLATE = `{
402
402
  // restoration. Not an exact provider token limit or a spending limit.
403
403
  "retrievalMaxTokens": 2000,
404
404
 
405
+ // Memorisation settings (global config only).
406
+ // Repeat for a session snapshot: "add" or "replace" (default "add").
407
+ // "memorisationRepeat": "add",
408
+ // Maximum stored text: 100 to 20,000 characters or 25 to 5,000 tokens.
409
+ // Tokens are estimated with the same rule as retrievalMaxTokens.
410
+ // "memorisationMaxSize": 2000,
411
+ // "memorisationSizeUnit": "characters", // or "tokens"
412
+
405
413
  // Restore a session's own memories after compaction and after resume, on
406
414
  // OpenCode V2, Pi and Claude Code. memoryLimit is the most memories to
407
415
  // restore. A project config can turn it on or off.
package/dist/config.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { isPlaceholderApiKey } from "./services/ai/api-key-placeholder.js";
2
2
  import { type KeywordMode } from "./utils/keyword-boost.js";
3
3
  import { type ActiveSpace } from "./services/spaces/active-space.js";
4
+ import * as memorisation from "./core/memorisation-settings.js";
4
5
  interface OmmsConfig {
5
6
  storagePath?: string;
6
7
  userEmailOverride?: string;
@@ -28,6 +29,10 @@ interface OmmsConfig {
28
29
  keywordMode?: KeywordMode;
29
30
  /** Shared budget for automatic memory context in approximate tokens (ceil(UTF-8 bytes / 4)). */
30
31
  retrievalMaxTokens?: number;
32
+ /** Memorisation size and repeat behaviour. Global config only. */
33
+ memorisationMaxSize?: number;
34
+ memorisationSizeUnit?: memorisation.MemorisationSettings["memorisationSizeUnit"];
35
+ memorisationRepeat?: memorisation.MemorisationSettings["memorisationRepeat"];
31
36
  maxProfileItems?: number;
32
37
  injectProfile?: boolean;
33
38
  containerTagPrefix?: string;
@@ -196,26 +201,6 @@ declare function buildConfig(fileConfig: OmmsConfig): {
196
201
  skillLearning: boolean;
197
202
  skillLearningRedact: boolean;
198
203
  skillFolders: string[] | undefined;
199
- storagePath: string;
200
- /** The global `storagePath` when a project moved `storagePath`; `skills.db` stays there. */
201
- skillStoragePath: string | undefined;
202
- userEmailOverride: string | undefined;
203
- userNameOverride: string | undefined;
204
- embeddingModel: string;
205
- embeddingDimensions: number;
206
- embeddingUseTaskPrefixes: boolean;
207
- embeddingApiUrl: string | undefined;
208
- embeddingApiKey: string | undefined;
209
- similarityThreshold: number;
210
- rerankerEnabled: boolean;
211
- rerankerApiUrl: string | undefined;
212
- rerankerModel: string | undefined;
213
- rerankerApiKey: string | undefined;
214
- rerankerTimeoutMs: number;
215
- keywordBoost: number;
216
- keywordMode: KeywordMode;
217
- maxMemories: number;
218
- retrievalMaxTokens: number;
219
204
  maxProfileItems: number;
220
205
  injectProfile: boolean;
221
206
  containerTagPrefix: string;
@@ -241,6 +226,29 @@ declare function buildConfig(fileConfig: OmmsConfig): {
241
226
  captureTraceRetentionDays: number;
242
227
  captureAttemptRetentionDays: number;
243
228
  captureRetryRetentionHours: number;
229
+ memorisationMaxSize: number;
230
+ memorisationSizeUnit: import("./core/memorisation.js").MemorisationSizeUnit;
231
+ memorisationRepeat: import("./core/memorisation.js").MemorisationRepeat;
232
+ storagePath: string;
233
+ /** The global `storagePath` when a project moved `storagePath`; `skills.db` stays there. */
234
+ skillStoragePath: string | undefined;
235
+ userEmailOverride: string | undefined;
236
+ userNameOverride: string | undefined;
237
+ embeddingModel: string;
238
+ embeddingDimensions: number;
239
+ embeddingUseTaskPrefixes: boolean;
240
+ embeddingApiUrl: string | undefined;
241
+ embeddingApiKey: string | undefined;
242
+ similarityThreshold: number;
243
+ rerankerEnabled: boolean;
244
+ rerankerApiUrl: string | undefined;
245
+ rerankerModel: string | undefined;
246
+ rerankerApiKey: string | undefined;
247
+ rerankerTimeoutMs: number;
248
+ keywordBoost: number;
249
+ keywordMode: KeywordMode;
250
+ maxMemories: number;
251
+ retrievalMaxTokens: number;
244
252
  };
245
253
  export declare function getGlobalConfigSourcePath(): string | undefined;
246
254
  export declare function getGlobalConfigWritePath(): string;
@@ -310,26 +318,6 @@ export declare let CONFIG: {
310
318
  skillLearning: boolean;
311
319
  skillLearningRedact: boolean;
312
320
  skillFolders: string[] | undefined;
313
- storagePath: string;
314
- /** The global `storagePath` when a project moved `storagePath`; `skills.db` stays there. */
315
- skillStoragePath: string | undefined;
316
- userEmailOverride: string | undefined;
317
- userNameOverride: string | undefined;
318
- embeddingModel: string;
319
- embeddingDimensions: number;
320
- embeddingUseTaskPrefixes: boolean;
321
- embeddingApiUrl: string | undefined;
322
- embeddingApiKey: string | undefined;
323
- similarityThreshold: number;
324
- rerankerEnabled: boolean;
325
- rerankerApiUrl: string | undefined;
326
- rerankerModel: string | undefined;
327
- rerankerApiKey: string | undefined;
328
- rerankerTimeoutMs: number;
329
- keywordBoost: number;
330
- keywordMode: KeywordMode;
331
- maxMemories: number;
332
- retrievalMaxTokens: number;
333
321
  maxProfileItems: number;
334
322
  injectProfile: boolean;
335
323
  containerTagPrefix: string;
@@ -355,6 +343,29 @@ export declare let CONFIG: {
355
343
  captureTraceRetentionDays: number;
356
344
  captureAttemptRetentionDays: number;
357
345
  captureRetryRetentionHours: number;
346
+ memorisationMaxSize: number;
347
+ memorisationSizeUnit: import("./core/memorisation.js").MemorisationSizeUnit;
348
+ memorisationRepeat: import("./core/memorisation.js").MemorisationRepeat;
349
+ storagePath: string;
350
+ /** The global `storagePath` when a project moved `storagePath`; `skills.db` stays there. */
351
+ skillStoragePath: string | undefined;
352
+ userEmailOverride: string | undefined;
353
+ userNameOverride: string | undefined;
354
+ embeddingModel: string;
355
+ embeddingDimensions: number;
356
+ embeddingUseTaskPrefixes: boolean;
357
+ embeddingApiUrl: string | undefined;
358
+ embeddingApiKey: string | undefined;
359
+ similarityThreshold: number;
360
+ rerankerEnabled: boolean;
361
+ rerankerApiUrl: string | undefined;
362
+ rerankerModel: string | undefined;
363
+ rerankerApiKey: string | undefined;
364
+ rerankerTimeoutMs: number;
365
+ keywordBoost: number;
366
+ keywordMode: KeywordMode;
367
+ maxMemories: number;
368
+ retrievalMaxTokens: number;
358
369
  };
359
370
  type RuntimeConfig = ReturnType<typeof buildConfig>;
360
371
  export { getAutoCaptureProviderStatus, type AutoCaptureProviderStatus, } from "./services/ai/live-model-choice.js";