open-memex 0.4.0-alpha.5 → 0.4.0-alpha.7

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/AGENTS.md CHANGED
@@ -140,3 +140,6 @@ hold `V2` and `V2/…` simultaneously. Full rules: `CONTRIBUTING.md`.
140
140
  in-development version. New features on a dev branch bump the minor on the alpha
141
141
  line (`0.3.0` → `0.4.0-alpha.1`); fixes bump the patch (`-alpha.1` → `-alpha.2`).
142
142
  The bump goes in the same commit as the feature, never as an afterthought.
143
+ The version number serves the publish: no publish, no mandatory bump. But once a
144
+ version has been pushed to the remote (shared), later changes must bump — two
145
+ different code states must never share one version number.
package/README.md CHANGED
@@ -375,10 +375,13 @@ server with cwd set to your project root (`init` handles this for you).
375
375
  **In progress — `0.4.0`:** team sync — shared memory via git: appdata draft
376
376
  outbox → `sync-status` → `submit` (local branch+commit, push/PR on your Yes)
377
377
  → `promote` / `resolve` review workflow, in-repo `.ai/open-memex/` dir, 1–2
378
- colleague pilot.
378
+ colleague pilot; capture — §3.5 checkpoint distillation in MCP handshake +
379
+ init instructions (agent proposes 1–3 captures, human decides).
379
380
 
380
381
  **Coming — `0.3.0` (stable):** org layer — org memory repo, curator convention,
381
- distill-to-AGENTS.md assist.
382
+ distill-to-AGENTS.md assist, `export`/`import` archive for user portability
383
+ (Markdown + manifest, no walled garden; private excluded by default,
384
+ `-a`/`--all` for full migration).
382
385
 
383
386
  **Future (signal-gated, no version committed):** native agent plugins (Claude Code /
384
387
  Codex hooks as enhancement paths over the same MCP tools); local embeddings as a
package/README.zh-CN.md CHANGED
@@ -365,10 +365,13 @@ project scope 从进程工作目录解析,所以配置 server 时 cwd 要指
365
365
  **进行中 —— `0.4.0`:** 团队同步——用 git 做共享记忆:appdata 草稿箱 →
366
366
  `sync-status` → `submit`(本地分支+commit,push/PR 拿你的 Yes 才做)
367
367
  → `promote` / `resolve` 评审工作流、仓库内 `.ai/open-memex/` 目录,
368
- 找 1–2 个同事做 pilot。
368
+ 找 1–2 个同事做 pilot;捕获——§3.5 检查点蒸馏写进 MCP 握手指令和
369
+ init 指令文件(agent 提议 1–3 条,人来定)。
369
370
 
370
371
  **Coming —— `0.3.0`(稳定版):** 组织层——组织记忆仓库、
371
- curator 约定、distill-to-AGENTS.md 辅助。
372
+ curator 约定、distill-to-AGENTS.md 辅助、`export`/`import` 归档
373
+ (Markdown + manifest,不造围墙花园,用户可带走;
374
+ private 默认不导出,`-a`/`--all` 全量迁移)。
372
375
 
373
376
  **未来(看信号再定,不承诺版本):** 原生 agent 插件
374
377
  (Claude Code / Codex hooks,作为同一套 MCP tools 的增强路径);
package/dist/doctor.js CHANGED
@@ -116,7 +116,7 @@ function mcpCheck() {
116
116
  name,
117
117
  ok: missing.length === 0,
118
118
  detail: missing.length === 0
119
- ? `handshake OK, 5 tools listed (${names.join(", ")})`
119
+ ? `handshake OK, ${names.length} tools listed (${names.join(", ")})`
120
120
  : `missing tools: ${missing.join(", ")}`,
121
121
  });
122
122
  }
package/dist/init.js CHANGED
@@ -47,6 +47,14 @@ You have a local memory MCP server (\`open-memex\`) with eleven tools:
47
47
  - BE PROACTIVE. When the user shares something worth remembering across sessions
48
48
  (a decision, a preference, a project convention, a fix and its cause), call
49
49
  \`memory_add\` without being asked. Keep each memory to one self-contained statement.
50
+ - At checkpoints (session start, end of a work chunk, after the user commits, after
51
+ any memory_* action), DISTILL the session: propose 1–3 short memories capturing the
52
+ useful conclusion — what was learned or decided, how an issue was resolved, what to
53
+ avoid, where the authoritative doc lives — not the raw transcript. Save NOTHING the
54
+ user did not approve; on approval call \`memory_add\` with source "inference" at the
55
+ confirmed scope. If the knowledge already lives in project docs, save a \`reference\`
56
+ memory pointing at the doc instead of copying it. Long-form notes are fine ONLY when
57
+ the user explicitly asks to save one.
50
58
  - Before asking the user about past decisions, conventions, or preferences they may
51
59
  have told you before, call \`memory_search\` first — try a few keyword variants
52
60
  (including the user's own language) when the first search comes up empty.
package/dist/mcp.js CHANGED
@@ -1,9 +1,10 @@
1
1
  /**
2
2
  * open-memex generic MCP server (stdio transport).
3
3
  *
4
- * Exposes the same five memory tools as the opencode plugin
4
+ * Exposes the memory tools as the opencode plugin
5
5
  * (memory_add / memory_search / memory_list / memory_supersede /
6
- * memory_forget) over the Model Context Protocol, so any MCP client —
6
+ * memory_forget, plus memory_status / memory_submit / memory_propose /
7
+ * memory_promote / memory_resolve / memory_pr_status) over the Model Context Protocol, so any MCP client —
7
8
  * VS Code Copilot Chat, Cursor, Claude Code, etc. — can use open-memex
8
9
  * without a host-specific plugin.
9
10
  *
@@ -19,13 +20,28 @@
19
20
  */
20
21
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
21
22
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
23
+ import fs from "node:fs";
24
+ import path from "node:path";
25
+ import { fileURLToPath } from "node:url";
22
26
  import { z } from "zod";
23
27
  import { loadConfig } from "./config.js";
24
28
  import { resolveProjectScope, PERSONAL_SCOPE } from "./scope.js";
25
29
  import { db } from "./store/db.js";
26
30
  import { syncScope } from "./store/sync.js";
27
31
  import { addMemory, searchMemories, listMemories, supersedeMemory, forgetMemory, statusMemories, submitMemoriesOp, proposeMemoriesOp, promoteMemoryOp, resolveMemoryOp, prStatusOp, memoryAddArgs, memorySearchArgs, memoryListArgs, memorySupersedeArgs, memoryForgetArgs, memoryStatusArgs, memorySubmitArgs, memoryProposeArgs, memoryPromoteArgs, memoryResolveArgs, memoryPrStatusArgs, TOOL_DESCRIPTIONS, } from "./tools/ops.js";
28
- const SERVER_VERSION = "0.2.0-alpha";
32
+ // Server version tracks package.json — never hardcode it here again.
33
+ // package.json sits two levels above this file in both layouts
34
+ // (src/mcp.ts and dist/mcp.js), same convention as cli.ts --version.
35
+ const SERVER_VERSION = (() => {
36
+ try {
37
+ const root = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
38
+ const pkg = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8"));
39
+ return typeof pkg.version === "string" ? pkg.version : "0.0.0-unknown";
40
+ }
41
+ catch {
42
+ return "0.0.0-unknown";
43
+ }
44
+ })();
29
45
  /**
30
46
  * D26: session-start guidance delivered through the MCP handshake itself.
31
47
  * The init-written instruction files only exist if the user ran
@@ -53,6 +69,15 @@ propose, promote, resolve, pr_status).
53
69
  - BE PROACTIVE: when the user shares something worth remembering across sessions
54
70
  (a decision, a preference, a project convention, a fix and its cause), call
55
71
  memory_add without being asked. Keep each memory to one self-contained statement.
72
+ - At the same checkpoints (session start, end of a work chunk, after the user
73
+ commits, after any memory_* action), DISTILL the session: propose 1–3 short
74
+ memories capturing the useful conclusion — what was learned or decided, how an
75
+ issue was resolved, what to avoid, where the authoritative doc lives — not the
76
+ raw transcript. Save NOTHING the user did not approve; on approval call
77
+ memory_add with source "inference" at the confirmed scope. If the knowledge
78
+ already lives in project docs, save a \`reference\` memory pointing at the doc
79
+ instead of copying it. Long-form notes are fine ONLY when the user explicitly
80
+ asks to save one.
56
81
  - Before asking the user about past decisions, conventions, or preferences they
57
82
  may have told you before, call memory_search first.
58
83
  - Memories default to this project's scope; use the personal scope for facts
@@ -3,19 +3,21 @@ import path from "node:path";
3
3
  import { randomBytes } from "node:crypto";
4
4
  import yaml from "js-yaml";
5
5
  import { memoriesDirFor, memoriesDirPath, inRepoMemoriesDirPath, } from "../paths.js";
6
- /** v2 content-kind taxonomy (V2-DESIGN §3.1). `type` = what the memory IS. */
6
+ /** v2 content-kind taxonomy (V2-DESIGN §3.1). `type` = what the memory IS
7
+ * (single-valued, drives behavior). D41: 11 types; `warning`→`gotcha`,
8
+ * `workflow`→`howto`, `incident`→`lesson`, `architecture`→`knowledge`. */
7
9
  export const MEMORY_TYPE_TAXONOMY = [
8
- "preference",
9
10
  "fact",
11
+ "preference",
10
12
  "decision",
11
- "lesson",
12
- "warning",
13
- "workflow",
14
- "architecture",
15
13
  "constraint",
16
14
  "todo",
17
15
  "knowledge",
16
+ "howto",
17
+ "gotcha",
18
+ "lesson",
18
19
  "observation",
20
+ "reference",
19
21
  ];
20
22
  const TAXONOMY = new Set(MEMORY_TYPE_TAXONOMY);
21
23
  /** Defensive parse: malformed entries are dropped, never fatal. */
package/dist/tools/ops.js CHANGED
@@ -42,9 +42,13 @@ export const memoryAddArgs = {
42
42
  type: z
43
43
  .enum(MEMORY_TYPE_TAXONOMY)
44
44
  .optional()
45
- .describe("Category of memory. Default: note."),
45
+ .describe("Category of memory. Default: fact."),
46
46
  scope: scopeArg,
47
47
  tags: z.array(z.string()).optional().describe("Optional tags for filtering."),
48
+ source: z
49
+ .string()
50
+ .optional()
51
+ .describe("Where this memory came from. Default: tool. Pass 'inference' for agent-proposed captures at checkpoints (V2-DESIGN §3.5)."),
48
52
  };
49
53
  export const memorySearchArgs = {
50
54
  query: z
@@ -161,7 +165,7 @@ export async function addMemory(getScope, cfg, args) {
161
165
  const fm = buildFrontmatter(s, {
162
166
  type: args.type ?? "fact",
163
167
  tags: args.tags ?? [],
164
- source: "tool",
168
+ source: args.source ?? "tool",
165
169
  });
166
170
  const { filePath } = writeMemoryFile(fm, redacted);
167
171
  const mf = readMemoryFile(filePath);
package/docs/V2-DESIGN.md CHANGED
@@ -25,6 +25,12 @@ Decisions log; Prior Art; solo-dev adoption path.
25
25
  6. **Adapters translate; they never implement memory logic.** All memory logic lives in Core.
26
26
  7. **Memory is the entrance of knowledge, not its final form.** Terminal states are docs / ADRs /
27
27
  AGENTS.md instructions — memory is how knowledge gets captured and found.
28
+ 8. **Distill conversations; do not archive them.** The default unit of memory is the useful
29
+ conclusion from a conversation, not the full transcript: what was learned, how it was resolved,
30
+ and where the authoritative source lives.
31
+ 9. **Portable second brain, not a walled garden.** Long-form personal notes are valid memories when
32
+ the user explicitly saves them; Markdown keeps them readable, exportable, and movable to other
33
+ tools or machines.
28
34
 
29
35
  ---
30
36
 
@@ -40,6 +46,9 @@ Decisions log; Prior Art; solo-dev adoption path.
40
46
  - **MCP is an interface, not the identity.** MCP / CLI / REST / SDK are access layers over the protocol,
41
47
  so the project is never locked to one transport or one agent tool (opencode, VS Code Copilot, Cursor,
42
48
  Claude Code, Windsurf, …).
49
+ - **Second-brain lens:** for individuals, OpenMemex can also be a local Markdown second brain for
50
+ AI-assisted work — similar in spirit to users asking AI to save notes into Obsidian or Notion, but
51
+ with agent recall, scopes, review, and redaction built in from the start.
43
52
  - **Company lens:** at organizational scale the same pain is tribal knowledge — senior engineers'
44
53
  hard-won experience evaporates when they move on, and every incident gets re-debugged by someone
45
54
  new. The current phase therefore prioritizes *capture*: valuable knowledge must land in memory
@@ -132,13 +141,37 @@ canonical_ref: docs/adr-003.md # memory holds a SUMMARY; the doc is canoni
132
141
 
133
142
  ### 3.1 Type taxonomy (content kind)
134
143
 
135
- `preference` `fact` `decision` `lesson` `warning` `workflow` `architecture` `constraint`
136
- `todo` `knowledge` `observation`
144
+ `fact` `preference` `decision` `constraint` `todo` `knowledge` `howto` `gotcha`
145
+ `lesson` `observation` `reference`
137
146
 
138
- `type` describes **what the content is**. `role` describes **how it may be used**.
147
+ `type` describes **what the content is** — single-valued, and it drives behavior
148
+ (lifecycle, review, rendering, retrieval). `role` describes **how it may be used**.
139
149
  A `decision` with `role: knowledge` is retrievable history. Only `role: instruction` may enter
140
150
  instruction context. (Separation adopted from review: mixing usage semantics into `type` was a design smell.)
141
151
 
152
+ One-line definitions:
153
+
154
+ - `fact` — a verifiable atomic statement (timezone, version number, path, account).
155
+ - `preference` — user likes, dislikes, working style.
156
+ - `decision` — a choice made + why; participates in supersede chains.
157
+ - `constraint` — a hard rule that must not be violated (release process, permission boundaries).
158
+ - `todo` — an actionable item with completion state.
159
+ - `knowledge` — declarative knowledge (how a system works, concept explanations).
160
+ - `howto` — steps to accomplish X.
161
+ - `gotcha` — a pitfall: don't do X because Y.
162
+ - `lesson` — a takeaway from experience, including incident postmortems (the incident id goes in `tags`).
163
+ - `observation` — noticed but not yet distilled.
164
+ - `reference` — a pointer to the authoritative doc via `canonical_ref`; the memory holds the summary.
165
+
166
+ A type earns its place only if the system treats it differently. If two candidates
167
+ share lifecycle, retrieval, and rendering, the loser becomes a tag. (D41 merged
168
+ `warning`→`gotcha`, `workflow`→`howto`, `incident`→`lesson`, `architecture`→`knowledge`.)
169
+
170
+ `tags` are retrieval hints, not a second type system: `type` says what the memory is, while tags say
171
+ which topics, tools, subsystems, paths, or incidents it relates to. Write paths should preserve
172
+ human-provided tags and may suggest simple normalized tags (for example `vscode`, `mcp`, `windows`,
173
+ `auth`, `onboarding`) to improve search without changing memory semantics.
174
+
142
175
  ### 3.2 Iron rules
143
176
 
144
177
  - **Iron rule 1 — the instruction gate:** only memories with `role: instruction`
@@ -170,6 +203,34 @@ Content hash + fuzzy match against existing memories. A superseding write does *
170
203
  the old memory becomes `status: superseded` with `superseded_by` pointing forward. History preserved;
171
204
  queries rank `active` first.
172
205
 
206
+ ### 3.5 Conversation distillation
207
+
208
+ OpenMemex does **not** store raw AI chat transcripts by default. A captured memory should usually be
209
+ a short, human-approved artifact distilled from the conversation: the final conclusion, important
210
+ facts, why the answer matters later, and how the issue was resolved. Good distilled memory candidates
211
+ answer some combination of:
212
+
213
+ - What did we learn or decide?
214
+ - What solved the problem, including key commands, files, links, or steps?
215
+ - What mistake or gotcha should the next developer avoid?
216
+ - Which scope owns it: `personal`, `project`, or future `org`?
217
+ - If the information already exists in project docs, where is the authoritative doc?
218
+
219
+ If stable knowledge already lives in a project document, prefer a `type: reference` memory with
220
+ `canonical_ref` pointing at that document over duplicating the full content. Memory should help the
221
+ agent find and apply authoritative docs, not become an outdated second copy of them.
222
+
223
+ Model-suggested captures use `source: inference` and should default to reviewable draft state. The
224
+ agent may propose 1-3 distilled memories at checkpoints or task end, but humans decide whether to
225
+ save them and which scope they belong to. The product goal is remembering the right conclusion at
226
+ the right scope, not remembering everything.
227
+
228
+ Long-form notes are still valid when the user explicitly asks to save a long note, write-up, meeting
229
+ summary, research log, or troubleshooting record. In that case OpenMemex behaves like a Markdown
230
+ second-brain target: store the note as user-authored content, preserve the body, tag it for retrieval,
231
+ and keep the same scope/privacy rules. The distinction is intent: implicit/model-suggested capture
232
+ distills; explicit "save this note" may preserve long-form text.
233
+
173
234
  ---
174
235
 
175
236
  ## 4. Scopes, Visibility & Namespaces
@@ -294,11 +355,21 @@ instructions are never candidates.)
294
355
  | Implicit (opt-in) | end-of-session "should I remember X?"; implicit captures default to `confidence: low` and appear in a separate list view for batch cleanup (regret window). |
295
356
  | Redaction (hard) | `<private>…</private>` stripped; secret patterns are **masked in place** (first 4 chars kept, rest → `x`) and the write proceeds (D14); pre-commit hook scans shared scopes. |
296
357
 
358
+ **Scope routing on capture:** when the user says "save", "remember", or "note this" without an
359
+ explicit scope, the agent should infer ownership from content and current context. Project-specific
360
+ knowledge (repo commands, architecture, code paths, product decisions, team conventions) routes to
361
+ `project`; personal preferences, private working notes, cross-project habits, or content unrelated to
362
+ the current repo routes to `personal`. Ambiguous captures should ask one short clarification instead
363
+ of guessing. `org` capture remains future/curated; a single user's chat never writes org memory
364
+ directly.
365
+
297
366
  ---
298
367
 
299
368
  ## 9. Sync
300
369
 
301
- - **Git is transport; the local SQLite index is the query layer.** Retrieval never walks git.
370
+ - **Git is a transport, not the product boundary; the local SQLite index is the query layer.**
371
+ Retrieval never walks git. Git/GitHub is the default sharing provider because it gives teams branch,
372
+ PR, review, and audit semantics they already trust, but Core must remain provider-agnostic.
302
373
  - one-memory-one-file ⇒ concurrent edits almost never conflict.
303
374
  - **Pull is explicit** (`open-memex pull`), never automatic on session start — no surprise context changes.
304
375
  `pull` = `fetch` + fast-forward only; never auto-commit/push (hard rule for enterprise environments).
@@ -314,6 +385,13 @@ instructions are never candidates.)
314
385
  - **Git unavailable:** projects without git (or with unreachable remotes) remain fully usable.
315
386
  `personal` scope works everywhere; `project` scope degrades to local-only and `open-memex status`
316
387
  annotates it as such. Sync commands fail with a clear message, never with a broken state.
388
+ - **Portability:** because Markdown is the source of truth, memories should be exportable without
389
+ depending on git. A future `open-memex export` command can archive selected scopes/types/tags into a
390
+ portable zip/tar bundle (markdown + manifest) for moving to another computer or importing into
391
+ another application. Export excludes `visibility: private` memories by default; `--all` / `-a`
392
+ includes everything, for a full personal migration to a new machine. API-backed exporters/providers (Notion, Obsidian-compatible vaults, internal
393
+ knowledge systems, enterprise stores) are extension paths over the same memory files and admission
394
+ rules, not separate products.
317
395
 
318
396
  ---
319
397
 
@@ -457,9 +535,10 @@ requirement: personal data never touches third-party services). Benchmarks to tr
457
535
 
458
536
  (Star counts / funding as of Sep 2026 — re-verify before quoting publicly.)
459
537
  - **Phase 3 — Org layer.** Org memory repo · curator convention · `examples/remote-server/` ·
460
- distill-to-AGENTS.md assist.
538
+ distill-to-AGENTS.md assist · export/import archive command for user portability.
461
539
  - **Phase 4 — Future, signal-gated.** Cloud `RemoteProvider` customization only on: multi-private-repo
462
- sharing needs, fine-grained ACL, audit/compliance mandates.
540
+ sharing needs, fine-grained ACL, audit/compliance mandates · optional API-backed exporters/providers
541
+ for enterprise knowledge systems.
463
542
 
464
543
  ## 19. Migration v1 → v2
465
544
 
@@ -732,6 +811,64 @@ requirement: personal data never touches third-party services). Benchmarks to tr
732
811
  fall back to the global usage. *Rationale: AI assistants discover the CLI
733
812
  through --help first — a command that silently swallows --help as a flag
734
813
  teaches the agent nothing. Approved 2026-09-28.*
814
+ - **D37** — Conversation distillation is the memory unit; transcript storage is
815
+ not the default. Capture should preserve the useful conclusion from a human/AI
816
+ session — what was learned, what resolved the issue, what should be avoided,
817
+ and where the authoritative doc lives — as a short reviewable memory. If the
818
+ knowledge already exists in docs, store a `reference` memory with
819
+ `canonical_ref` instead of duplicating the doc. Tags are retrieval hints, not
820
+ content kinds: `type` classifies the memory, tags describe topics/tools/paths.
821
+ *Rationale: full chat logs are noisy, harder to review, and riskier for
822
+ privacy; the value is remembering the right conclusion at the right scope,
823
+ with enough context for the next human or AI session. Approved 2026-09-28.*
824
+ - **D38** — OpenMemex supports a Markdown second-brain use case while keeping
825
+ distillation as the default for implicit/model-suggested capture. If the user
826
+ explicitly asks to save a long note, meeting summary, troubleshooting record,
827
+ or write-up, preserve it as user-authored Markdown with tags and normal
828
+ scope/privacy rules. Git/GitHub sync is the default team-sharing provider, not
829
+ the product boundary: future export/import archives and API-backed providers
830
+ may move the same markdown memories to other computers, applications, or
831
+ enterprise systems. For generic "save/remember/note this" requests, the agent
832
+ routes project-specific knowledge to `project`, personal or repo-unrelated
833
+ knowledge to `personal`, and asks one clarification if ambiguous; `org` remains
834
+ curated/future, never a direct single-user chat write. *Rationale: users also
835
+ want a local AI-assisted second brain, but portability and ownership must stay
836
+ explicit; sharing mechanisms should be provider choices over the same memory
837
+ model, not the identity of the product. Approved 2026-09-28.*
838
+ - **D39** — `type` and `tags` are complementary axes, not alternatives. `type` is
839
+ single-valued: what the memory IS — it drives behavior (lifecycle, review,
840
+ rendering, retrieval: a `todo` can be completed, a `reference` resolves
841
+ `canonical_ref`, a `decision` participates in supersede chains). `tags` are
842
+ multi-valued: what the memory is ABOUT — pure retrieval hints
843
+ (topics, tools, subsystems, paths, incidents). *Rationale: without type the
844
+ system cannot tell "a todo I must do" from "a fact I must know" even when both
845
+ are tagged `auth`; without tags, cross-cutting retrieval ("everything about
846
+ onboarding") would need a combinatorial type explosion. Type answers "how do I
847
+ handle this?", tags answer "how do I find this?". Approved 2026-09-28.*
848
+ - **D40** — `open-memex export` excludes `visibility: private` memories by
849
+ default; `--all` / `-a` includes everything, for a full personal migration to
850
+ a new machine. *Rationale: the safe default protects privacy; the escape hatch
851
+ keeps the "no walled garden" promise — the user can always take everything
852
+ with them. Approved 2026-09-28.*
853
+ - **D42** — §3.5 checkpoint distillation is taught in the MCP handshake
854
+ instructions (`src/mcp.ts` `SERVER_INSTRUCTIONS`) and the `init`-written
855
+ instruction files (`src/init.ts` `INSTRUCTIONS`): at checkpoints the agent
856
+ DISTILLs the session and proposes 1–3 short memories (conclusion, not
857
+ transcript); nothing is saved without user approval. Approved captures are
858
+ saved via `memory_add` with the new optional `source` param set to
859
+ `"inference"` (default `"tool"`). *Rationale: closes the 0.4.0 TODO from D41 —
860
+ the design's capture loop now reaches the agent. Implemented 2026-09-29.*
861
+ - **D41** — The type taxonomy is reconciled to 11 types with one-line definitions
862
+ (§3.1): `fact` `preference` `decision` `constraint` `todo` `knowledge` `howto`
863
+ `gotcha` `lesson` `observation` `reference`. Merged away: `warning`→`gotcha`,
864
+ `workflow`→`howto`, `incident`→`lesson` (incident id goes in `tags`),
865
+ `architecture`→`knowledge` (tag `architecture`). *Rationale: a type earns its
866
+ place only if the system treats it differently (lifecycle, retrieval,
867
+ rendering); otherwise it is a tag. Fifteen types blur classification and hurt
868
+ agent accuracy — eleven keeps each type's behavioral slot distinct. `lesson`
869
+ is deliberately broader than `incident`: a postmortem's shape (timeline, root
870
+ cause, actions) is a template concern, not a type. Code
871
+ `MEMORY_TYPE_TAXONOMY` updated to match. Approved 2026-09-28.*
735
872
 
736
873
  ## Open Questions
737
874
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-memex",
3
- "version": "0.4.0-alpha.5",
3
+ "version": "0.4.0-alpha.7",
4
4
  "description": "Local-first memory layer and protocol for AI coding agents. Markdown source of truth, SQLite FTS5 index, zero cloud.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
package/src/doctor.ts CHANGED
@@ -134,7 +134,7 @@ function mcpCheck(): Promise<Check> {
134
134
  ok: missing.length === 0,
135
135
  detail:
136
136
  missing.length === 0
137
- ? `handshake OK, 5 tools listed (${names.join(", ")})`
137
+ ? `handshake OK, ${names.length} tools listed (${names.join(", ")})`
138
138
  : `missing tools: ${missing.join(", ")}`,
139
139
  });
140
140
  }
package/src/init.ts CHANGED
@@ -58,6 +58,14 @@ You have a local memory MCP server (\`open-memex\`) with eleven tools:
58
58
  - BE PROACTIVE. When the user shares something worth remembering across sessions
59
59
  (a decision, a preference, a project convention, a fix and its cause), call
60
60
  \`memory_add\` without being asked. Keep each memory to one self-contained statement.
61
+ - At checkpoints (session start, end of a work chunk, after the user commits, after
62
+ any memory_* action), DISTILL the session: propose 1–3 short memories capturing the
63
+ useful conclusion — what was learned or decided, how an issue was resolved, what to
64
+ avoid, where the authoritative doc lives — not the raw transcript. Save NOTHING the
65
+ user did not approve; on approval call \`memory_add\` with source "inference" at the
66
+ confirmed scope. If the knowledge already lives in project docs, save a \`reference\`
67
+ memory pointing at the doc instead of copying it. Long-form notes are fine ONLY when
68
+ the user explicitly asks to save one.
61
69
  - Before asking the user about past decisions, conventions, or preferences they may
62
70
  have told you before, call \`memory_search\` first — try a few keyword variants
63
71
  (including the user's own language) when the first search comes up empty.
package/src/mcp.ts CHANGED
@@ -1,9 +1,10 @@
1
1
  /**
2
2
  * open-memex generic MCP server (stdio transport).
3
3
  *
4
- * Exposes the same five memory tools as the opencode plugin
4
+ * Exposes the memory tools as the opencode plugin
5
5
  * (memory_add / memory_search / memory_list / memory_supersede /
6
- * memory_forget) over the Model Context Protocol, so any MCP client —
6
+ * memory_forget, plus memory_status / memory_submit / memory_propose /
7
+ * memory_promote / memory_resolve / memory_pr_status) over the Model Context Protocol, so any MCP client —
7
8
  * VS Code Copilot Chat, Cursor, Claude Code, etc. — can use open-memex
8
9
  * without a host-specific plugin.
9
10
  *
@@ -19,6 +20,9 @@
19
20
  */
20
21
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
21
22
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
23
+ import fs from "node:fs";
24
+ import path from "node:path";
25
+ import { fileURLToPath } from "node:url";
22
26
  import { z } from "zod";
23
27
  import { loadConfig } from "./config.ts";
24
28
  import { resolveProjectScope, PERSONAL_SCOPE, type Scope } from "./scope.ts";
@@ -51,7 +55,18 @@ import {
51
55
  type ToolResult,
52
56
  } from "./tools/ops.ts";
53
57
 
54
- const SERVER_VERSION = "0.2.0-alpha";
58
+ // Server version tracks package.json — never hardcode it here again.
59
+ // package.json sits two levels above this file in both layouts
60
+ // (src/mcp.ts and dist/mcp.js), same convention as cli.ts --version.
61
+ const SERVER_VERSION: string = (() => {
62
+ try {
63
+ const root = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
64
+ const pkg = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8"));
65
+ return typeof pkg.version === "string" ? pkg.version : "0.0.0-unknown";
66
+ } catch {
67
+ return "0.0.0-unknown";
68
+ }
69
+ })();
55
70
 
56
71
  /**
57
72
  * D26: session-start guidance delivered through the MCP handshake itself.
@@ -80,6 +95,15 @@ propose, promote, resolve, pr_status).
80
95
  - BE PROACTIVE: when the user shares something worth remembering across sessions
81
96
  (a decision, a preference, a project convention, a fix and its cause), call
82
97
  memory_add without being asked. Keep each memory to one self-contained statement.
98
+ - At the same checkpoints (session start, end of a work chunk, after the user
99
+ commits, after any memory_* action), DISTILL the session: propose 1–3 short
100
+ memories capturing the useful conclusion — what was learned or decided, how an
101
+ issue was resolved, what to avoid, where the authoritative doc lives — not the
102
+ raw transcript. Save NOTHING the user did not approve; on approval call
103
+ memory_add with source "inference" at the confirmed scope. If the knowledge
104
+ already lives in project docs, save a \`reference\` memory pointing at the doc
105
+ instead of copying it. Long-form notes are fine ONLY when the user explicitly
106
+ asks to save one.
83
107
  - Before asking the user about past decisions, conventions, or preferences they
84
108
  may have told you before, call memory_search first.
85
109
  - Memories default to this project's scope; use the personal scope for facts
@@ -8,19 +8,21 @@ import {
8
8
  inRepoMemoriesDirPath,
9
9
  } from "../paths.ts";
10
10
 
11
- /** v2 content-kind taxonomy (V2-DESIGN §3.1). `type` = what the memory IS. */
11
+ /** v2 content-kind taxonomy (V2-DESIGN §3.1). `type` = what the memory IS
12
+ * (single-valued, drives behavior). D41: 11 types; `warning`→`gotcha`,
13
+ * `workflow`→`howto`, `incident`→`lesson`, `architecture`→`knowledge`. */
12
14
  export const MEMORY_TYPE_TAXONOMY = [
13
- "preference",
14
15
  "fact",
16
+ "preference",
15
17
  "decision",
16
- "lesson",
17
- "warning",
18
- "workflow",
19
- "architecture",
20
18
  "constraint",
21
19
  "todo",
22
20
  "knowledge",
21
+ "howto",
22
+ "gotcha",
23
+ "lesson",
23
24
  "observation",
25
+ "reference",
24
26
  ] as const;
25
27
 
26
28
  const TAXONOMY = new Set<string>(MEMORY_TYPE_TAXONOMY);
package/src/tools/ops.ts CHANGED
@@ -77,9 +77,15 @@ export const memoryAddArgs = {
77
77
  type: z
78
78
  .enum(MEMORY_TYPE_TAXONOMY)
79
79
  .optional()
80
- .describe("Category of memory. Default: note."),
80
+ .describe("Category of memory. Default: fact."),
81
81
  scope: scopeArg,
82
82
  tags: z.array(z.string()).optional().describe("Optional tags for filtering."),
83
+ source: z
84
+ .string()
85
+ .optional()
86
+ .describe(
87
+ "Where this memory came from. Default: tool. Pass 'inference' for agent-proposed captures at checkpoints (V2-DESIGN §3.5).",
88
+ ),
83
89
  };
84
90
  export type MemoryAddArgs = z.infer<z.ZodObject<typeof memoryAddArgs>>;
85
91
 
@@ -239,7 +245,7 @@ export async function addMemory(
239
245
  const fm = buildFrontmatter(s, {
240
246
  type: args.type ?? "fact",
241
247
  tags: args.tags ?? [],
242
- source: "tool",
248
+ source: args.source ?? "tool",
243
249
  });
244
250
  const { filePath } = writeMemoryFile(fm, redacted);
245
251
  const mf = readMemoryFile(filePath);