@vortex-os/memory-extended 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +244 -0
- package/dist/consolidate/index.d.ts +7 -0
- package/dist/consolidate/index.d.ts.map +1 -0
- package/dist/consolidate/index.js +4 -0
- package/dist/consolidate/index.js.map +1 -0
- package/dist/consolidate/proposer.d.ts +43 -0
- package/dist/consolidate/proposer.d.ts.map +1 -0
- package/dist/consolidate/proposer.js +276 -0
- package/dist/consolidate/proposer.js.map +1 -0
- package/dist/consolidate/query.d.ts +32 -0
- package/dist/consolidate/query.d.ts.map +1 -0
- package/dist/consolidate/query.js +40 -0
- package/dist/consolidate/query.js.map +1 -0
- package/dist/consolidate/store.d.ts +21 -0
- package/dist/consolidate/store.d.ts.map +1 -0
- package/dist/consolidate/store.js +91 -0
- package/dist/consolidate/store.js.map +1 -0
- package/dist/consolidate/types.d.ts +68 -0
- package/dist/consolidate/types.d.ts.map +1 -0
- package/dist/consolidate/types.js +2 -0
- package/dist/consolidate/types.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/frontmatter.d.ts +6 -0
- package/dist/internal/frontmatter.d.ts.map +1 -0
- package/dist/internal/frontmatter.js +38 -0
- package/dist/internal/frontmatter.js.map +1 -0
- package/dist/internal/proactive-curator-helpers.d.ts +171 -0
- package/dist/internal/proactive-curator-helpers.d.ts.map +1 -0
- package/dist/internal/proactive-curator-helpers.js +162 -0
- package/dist/internal/proactive-curator-helpers.js.map +1 -0
- package/dist/mcp/document-tools.d.ts +144 -0
- package/dist/mcp/document-tools.d.ts.map +1 -0
- package/dist/mcp/document-tools.js +319 -0
- package/dist/mcp/document-tools.js.map +1 -0
- package/dist/mcp/index.d.ts +34 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +29 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/install.d.ts +72 -0
- package/dist/mcp/install.d.ts.map +1 -0
- package/dist/mcp/install.js +92 -0
- package/dist/mcp/install.js.map +1 -0
- package/dist/mcp/memory-tools.d.ts +101 -0
- package/dist/mcp/memory-tools.d.ts.map +1 -0
- package/dist/mcp/memory-tools.js +105 -0
- package/dist/mcp/memory-tools.js.map +1 -0
- package/dist/mcp/recall-tool.d.ts +52 -0
- package/dist/mcp/recall-tool.d.ts.map +1 -0
- package/dist/mcp/recall-tool.js +60 -0
- package/dist/mcp/recall-tool.js.map +1 -0
- package/dist/mcp/server.d.ts +32 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +113 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/recall/engine.d.ts +38 -0
- package/dist/recall/engine.d.ts.map +1 -0
- package/dist/recall/engine.js +113 -0
- package/dist/recall/engine.js.map +1 -0
- package/dist/recall/index.d.ts +22 -0
- package/dist/recall/index.d.ts.map +1 -0
- package/dist/recall/index.js +20 -0
- package/dist/recall/index.js.map +1 -0
- package/dist/recall/intent.d.ts +24 -0
- package/dist/recall/intent.d.ts.map +1 -0
- package/dist/recall/intent.js +95 -0
- package/dist/recall/intent.js.map +1 -0
- package/dist/recall/render.d.ts +11 -0
- package/dist/recall/render.d.ts.map +1 -0
- package/dist/recall/render.js +23 -0
- package/dist/recall/render.js.map +1 -0
- package/dist/recall/types.d.ts +72 -0
- package/dist/recall/types.d.ts.map +1 -0
- package/dist/recall/types.js +2 -0
- package/dist/recall/types.js.map +1 -0
- package/dist/sessionArchive/adapters/claude-code.d.ts +3 -0
- package/dist/sessionArchive/adapters/claude-code.d.ts.map +1 -0
- package/dist/sessionArchive/adapters/claude-code.js +276 -0
- package/dist/sessionArchive/adapters/claude-code.js.map +1 -0
- package/dist/sessionArchive/adapters/claude-desktop.d.ts +3 -0
- package/dist/sessionArchive/adapters/claude-desktop.d.ts.map +1 -0
- package/dist/sessionArchive/adapters/claude-desktop.js +234 -0
- package/dist/sessionArchive/adapters/claude-desktop.js.map +1 -0
- package/dist/sessionArchive/adapters/codex.d.ts +3 -0
- package/dist/sessionArchive/adapters/codex.d.ts.map +1 -0
- package/dist/sessionArchive/adapters/codex.js +322 -0
- package/dist/sessionArchive/adapters/codex.js.map +1 -0
- package/dist/sessionArchive/adapters/gemini.d.ts +3 -0
- package/dist/sessionArchive/adapters/gemini.d.ts.map +1 -0
- package/dist/sessionArchive/adapters/gemini.js +248 -0
- package/dist/sessionArchive/adapters/gemini.js.map +1 -0
- package/dist/sessionArchive/adapters/index.d.ts +5 -0
- package/dist/sessionArchive/adapters/index.d.ts.map +1 -0
- package/dist/sessionArchive/adapters/index.js +5 -0
- package/dist/sessionArchive/adapters/index.js.map +1 -0
- package/dist/sessionArchive/index.d.ts +9 -0
- package/dist/sessionArchive/index.d.ts.map +1 -0
- package/dist/sessionArchive/index.js +6 -0
- package/dist/sessionArchive/index.js.map +1 -0
- package/dist/sessionArchive/ingest.d.ts +68 -0
- package/dist/sessionArchive/ingest.d.ts.map +1 -0
- package/dist/sessionArchive/ingest.js +134 -0
- package/dist/sessionArchive/ingest.js.map +1 -0
- package/dist/sessionArchive/normalize.d.ts +15 -0
- package/dist/sessionArchive/normalize.d.ts.map +1 -0
- package/dist/sessionArchive/normalize.js +40 -0
- package/dist/sessionArchive/normalize.js.map +1 -0
- package/dist/sessionArchive/store.d.ts +118 -0
- package/dist/sessionArchive/store.d.ts.map +1 -0
- package/dist/sessionArchive/store.js +491 -0
- package/dist/sessionArchive/store.js.map +1 -0
- package/dist/sessionArchive/types.d.ts +124 -0
- package/dist/sessionArchive/types.d.ts.map +1 -0
- package/dist/sessionArchive/types.js +24 -0
- package/dist/sessionArchive/types.js.map +1 -0
- package/dist/sqlite/drift.d.ts +22 -0
- package/dist/sqlite/drift.d.ts.map +1 -0
- package/dist/sqlite/drift.js +69 -0
- package/dist/sqlite/drift.js.map +1 -0
- package/dist/sqlite/index.d.ts +22 -0
- package/dist/sqlite/index.d.ts.map +1 -0
- package/dist/sqlite/index.js +4 -0
- package/dist/sqlite/index.js.map +1 -0
- package/dist/sqlite/rebuild.d.ts +32 -0
- package/dist/sqlite/rebuild.d.ts.map +1 -0
- package/dist/sqlite/rebuild.js +80 -0
- package/dist/sqlite/rebuild.js.map +1 -0
- package/dist/sqlite/schema.d.ts +15 -0
- package/dist/sqlite/schema.d.ts.map +1 -0
- package/dist/sqlite/schema.js +41 -0
- package/dist/sqlite/schema.js.map +1 -0
- package/dist/sqlite/store.d.ts +49 -0
- package/dist/sqlite/store.d.ts.map +1 -0
- package/dist/sqlite/store.js +179 -0
- package/dist/sqlite/store.js.map +1 -0
- package/dist/sqlite/types.d.ts +57 -0
- package/dist/sqlite/types.d.ts.map +1 -0
- package/dist/sqlite/types.js +2 -0
- package/dist/sqlite/types.js.map +1 -0
- package/dist/vector/backend-brute.d.ts +38 -0
- package/dist/vector/backend-brute.d.ts.map +1 -0
- package/dist/vector/backend-brute.js +112 -0
- package/dist/vector/backend-brute.js.map +1 -0
- package/dist/vector/embedder.d.ts +59 -0
- package/dist/vector/embedder.d.ts.map +1 -0
- package/dist/vector/embedder.js +47 -0
- package/dist/vector/embedder.js.map +1 -0
- package/dist/vector/index.d.ts +24 -0
- package/dist/vector/index.d.ts.map +1 -0
- package/dist/vector/index.js +7 -0
- package/dist/vector/index.js.map +1 -0
- package/dist/vector/math.d.ts +21 -0
- package/dist/vector/math.d.ts.map +1 -0
- package/dist/vector/math.js +34 -0
- package/dist/vector/math.js.map +1 -0
- package/dist/vector/schema.d.ts +14 -0
- package/dist/vector/schema.d.ts.map +1 -0
- package/dist/vector/schema.js +24 -0
- package/dist/vector/schema.js.map +1 -0
- package/dist/vector/segment.d.ts +65 -0
- package/dist/vector/segment.d.ts.map +1 -0
- package/dist/vector/segment.js +72 -0
- package/dist/vector/segment.js.map +1 -0
- package/dist/vector/session.d.ts +90 -0
- package/dist/vector/session.d.ts.map +1 -0
- package/dist/vector/session.js +242 -0
- package/dist/vector/session.js.map +1 -0
- package/dist/vector/store.d.ts +69 -0
- package/dist/vector/store.d.ts.map +1 -0
- package/dist/vector/store.js +131 -0
- package/dist/vector/store.js.map +1 -0
- package/dist/vector/types.d.ts +109 -0
- package/dist/vector/types.d.ts.map +1 -0
- package/dist/vector/types.js +24 -0
- package/dist/vector/types.js.map +1 -0
- package/package.json +96 -0
- package/scripts/mcp-stdio.mjs +143 -0
- package/scripts/rebuild-memory-sqlite.mjs +39 -0
- package/scripts/rebuild-memory-vector.mjs +64 -0
package/README.md
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
# @vortex-os/memory-extended
|
|
2
|
+
|
|
3
|
+
Extended memory layer for VortEX — an **opt-in add-on** that lives on top of `@vortex-os/base`. Adds derived indexes and a conversation archive while keeping the base's markdown `_memory/` layer as the single source of truth.
|
|
4
|
+
|
|
5
|
+
## What it is
|
|
6
|
+
|
|
7
|
+
`memory-extended` ships as a **single npm package** with six cooperating **namespaces**:
|
|
8
|
+
|
|
9
|
+
| Namespace | Purpose | Status |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| `sessionArchive` | Append-only JSONL log of agent sessions + SQLite metadata. Four first-party host adapters (Claude Code CLI, Codex CLI, Gemini CLI, Claude Desktop). | Phase 11b — **shipped (0.1.0)** |
|
|
12
|
+
| `consolidate` | Post-session fact-extraction proposer — read past `sessionArchive` events, propose memory candidates, operator confirms. | Phase 11d — **shipped (0.1.0)** |
|
|
13
|
+
| `sqlite` | Structured store derived from markdown memories — hard-filter queries (`byType` / `byTag` / `byPrivacy` / `updatedSince`), drift detection (log + skip, non-destructive). | Phase 11a — **shipped (0.2.0)**. Five operator decisions recorded in `docs/memory-extended-design.md`. |
|
|
14
|
+
| `vector` | Dense retrieval over memories **and conversation sessions**. Default backend: **in-process brute-force cosine**; vectors in the shared `memory.sqlite`. Host-injected `EmbedFn` (multilingual local default). Sessions are vectorized at topic-chunk granularity via embedding-similarity segmentation. | Phase 11c — **shipped (0.3.0+)** |
|
|
15
|
+
| `recall` | Two-stage hybrid retrieval **engine** (SQLite loose hard-filter → cosine rerank). Returns data, not a report. Hits carry `source` = `memory` \| `session-archive`. `/recall <query>` command lives in `session-rituals`. | Phase 11c — **shipped (0.3.0+)** |
|
|
16
|
+
| `mcp` | MCP server for any MCP host (Claude Desktop, chatbots) over stdio: read tools (`recall`, `list_memories`, `get_memory`) + document tools (`suggest_document` → `write_document` / `decline_document`, a propose-then-write pair), plus a one-line `install` into the host config. Writes are gated behind an explicit second tool call. SDK is an optional dependency. Bin: `vortex-mcp-recall`. | **shipped (0.5.0+)** |
|
|
17
|
+
|
|
18
|
+
The companion module **`proactive-curator`** (shipped inside `@vortex-os/base`) handles the *in-session* counterpart to `consolidate` — live "this looks worth capturing" prompts during an ongoing conversation. `memory-extended/consolidate` reuses its `Proposal` / `LLMJudge` types via the base aggregate, so a single host UX renders both surfaces.
|
|
19
|
+
|
|
20
|
+
## What it is not
|
|
21
|
+
|
|
22
|
+
- It does **not** replace `@vortex-os/base`'s `memorySystem`. The markdown layer remains the source of truth; this package adds *derived* indexes that can be deleted and regenerated at any time.
|
|
23
|
+
- It does **not** write to `_memory/` directly. The `consolidate` namespace proposes; the operator confirms; writes go through `base`'s `memorySystem`.
|
|
24
|
+
- It does **not** require all five namespaces to be present. Use what you need; drop the rest.
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
npm install @vortex-os/memory-extended @vortex-os/base better-sqlite3
|
|
30
|
+
# Optional — only needed if you ingest from Claude Desktop:
|
|
31
|
+
npm install classic-level
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`@vortex-os/base` is a required peer dependency — `consolidate` reuses the `proactiveCurator` namespace it exposes for `Proposal` / `LLMJudge` types. `better-sqlite3` is required for the SQLite metadata layer. `classic-level` is optional — required only when the Claude Desktop adapter is registered.
|
|
35
|
+
|
|
36
|
+
The local embedder (`@huggingface/transformers`) is an **optional dependency** — it installs automatically with this package; nothing to fetch by hand. The default model is **`Xenova/multilingual-e5-small`** (384-dim, 50+ languages incl. Korean, 512-token input, ~470 MB) — a retrieval-tuned multilingual model that downloads once on first `vector`/`recall` use and is cached. It is *asymmetric*: a `"query: "` prefix is applied to search text and `"passage: "` to indexed text automatically (the store passes the right `kind`). To use a *symmetric* model instead (e.g. `Xenova/all-MiniLM-L6-v2`, ~90 MB) pass `{ model: "...", prefixes: null }`. To skip the local model entirely, pass your own `EmbedFn` (e.g. an OpenAI/Voyage adapter).
|
|
37
|
+
|
|
38
|
+
> **Changing the embedder requires a vector rebuild.** Vectors are model-specific; even when the dimension matches (384-dim) the embedding *spaces* differ, so run `npx rebuild-memory-vector` after switching models.
|
|
39
|
+
|
|
40
|
+
## Quick usage — `sessionArchive`
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
import {
|
|
44
|
+
ingest,
|
|
45
|
+
claudeCodeAdapter,
|
|
46
|
+
codexAdapter,
|
|
47
|
+
geminiAdapter,
|
|
48
|
+
claudeDesktopAdapter,
|
|
49
|
+
} from "@vortex-os/memory-extended/sessionArchive";
|
|
50
|
+
|
|
51
|
+
await ingest({
|
|
52
|
+
adapters: [claudeCodeAdapter, codexAdapter, geminiAdapter, claudeDesktopAdapter],
|
|
53
|
+
cwd: process.cwd(),
|
|
54
|
+
dataDir: "./data",
|
|
55
|
+
since: "2026-05-01T00:00:00Z",
|
|
56
|
+
});
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The orchestrator runs each adapter's `detect()` in parallel, calls `list()` on the ones that resolve true, reads each session end-to-end, normalizes events to `VortexTranscriptEvent`, and writes raw + normalized JSONL into `data/_session-archive/` along with SQLite metadata for incremental re-scan.
|
|
60
|
+
|
|
61
|
+
## Quick usage — `sqlite`
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
import { MemorySqliteStore, rebuildFromMemoryDir, driftCheck } from "@vortex-os/memory-extended/sqlite";
|
|
65
|
+
|
|
66
|
+
const store = new MemorySqliteStore("./data/_indexes/memory.sqlite");
|
|
67
|
+
|
|
68
|
+
// One-shot rebuild from data/_memory/*.md (or run `npx rebuild-memory-sqlite`).
|
|
69
|
+
await rebuildFromMemoryDir(store, "./data/_memory");
|
|
70
|
+
|
|
71
|
+
// Four hard-filter helpers.
|
|
72
|
+
const rules = store.byType("feedback");
|
|
73
|
+
const koreanRules = store.byTag("korean");
|
|
74
|
+
const visiblePublic = store.byPrivacy("public");
|
|
75
|
+
const fresh = store.updatedSince("2026-05-01");
|
|
76
|
+
|
|
77
|
+
// Composite query (AND across fields, OR within a list).
|
|
78
|
+
const recent = store.query({
|
|
79
|
+
type: ["feedback", "project"],
|
|
80
|
+
tags: ["vortex"],
|
|
81
|
+
updatedSinceMs: Date.parse("2026-05-20"),
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Drift check — non-destructive. Returns the drifted ids; the operator
|
|
85
|
+
// decides whether to re-run rebuild or treat the markdown as new truth.
|
|
86
|
+
const report = await driftCheck(store, "./data/_memory");
|
|
87
|
+
console.log(`drifted ${report.drifted.length}/${report.rowsScanned}`);
|
|
88
|
+
|
|
89
|
+
store.close();
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
The sqlite file lives under `data/_indexes/` (gitignored). Markdown remains the source of truth — the sqlite index is derived and rebuildable. See the operator decision table in [`docs/memory-extended-design.md`](../../docs/memory-extended-design.md#sqlite-namespace) for the rationale.
|
|
93
|
+
|
|
94
|
+
## Quick usage — `vector` + `recall`
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
import { MemorySqliteStore } from "@vortex-os/memory-extended/sqlite";
|
|
98
|
+
import { MemoryVectorStore, createLocalEmbedder } from "@vortex-os/memory-extended/vector";
|
|
99
|
+
import { recall, renderRecallHits } from "@vortex-os/memory-extended/recall";
|
|
100
|
+
|
|
101
|
+
const dbPath = "./data/_indexes/memory.sqlite";
|
|
102
|
+
const sqlite = new MemorySqliteStore(dbPath);
|
|
103
|
+
const vector = new MemoryVectorStore({ db: dbPath }); // default brute-force cosine backend
|
|
104
|
+
const embed = createLocalEmbedder(); // or your own EmbedFn (OpenAI/Voyage)
|
|
105
|
+
|
|
106
|
+
// One-shot rebuild from the sqlite store (or run `npx rebuild-memory-vector`).
|
|
107
|
+
await vector.rebuild(sqlite, embed); // → { indexed, skipped, pruned }
|
|
108
|
+
|
|
109
|
+
// The engine returns DATA — list it, or phrase one hit in conversation.
|
|
110
|
+
const result = await recall(
|
|
111
|
+
{ query: "tone feedback from 5월", k: 5 }, // intent: type/tag/Korean-month parsed loosely
|
|
112
|
+
{ sqlite, vector, embed },
|
|
113
|
+
);
|
|
114
|
+
console.log(renderRecallHits(result)); // optional compact list render
|
|
115
|
+
|
|
116
|
+
vector.close();
|
|
117
|
+
sqlite.close();
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
The vector index shares the same `memory.sqlite` (a `memory_vectors` table) — one file, one rebuild path. The `/recall <query>` slash command wraps this engine; register it via `createRitualRegistry({ recall: { embed } })` in `@vortex-os/session-rituals`. The engine returns structured hits rather than a report so the host can either render a list or weave a hit into conversation (operator decision 5). See [`docs/memory-extended-design.md`](../../docs/memory-extended-design.md#vector-namespace) for the backend/embedder rationale.
|
|
121
|
+
|
|
122
|
+
**Conversation sessions** are searchable too. `vector.rebuildSessions(sessionArchiveStore, embed)` vectorizes each archived session and stores chunks under `source: "session-archive"` plus a `session_chunks` metadata row for hydration. Run `npx rebuild-memory-vector --sessions`. `recall` then returns session hits alongside memory hits (pass a `SessionChunkStore` as `sessionChunks` to hydrate them; without it session hits are skipped). Filter to one corpus with `/recall <q> --source session-archive`.
|
|
123
|
+
|
|
124
|
+
Granularity is `"turn"` by default — one chunk per user+assistant exchange — which a real-data tuning run found equals or beats topic segmentation while staying simple (`"segment"` mode is available for coarser topic chunks). The bigger quality lever is the **content filter**: only user/assistant text is indexed; tool output (git/ls/npm dumps) and host system-reminder blocks are stripped so the index reflects what was discussed. Recall precision on short conversational turns is bounded by the embedder — the `multilingual-e5-small` default reliably ranks the on-topic hit first (it fixed a discrimination failure the earlier MiniLM default had) at the same weight class; for sharper still, swap in a stronger `EmbedFn` (e.g. `bge-m3`, heavier — best hosted on a GPU and called remotely).
|
|
125
|
+
|
|
126
|
+
## Quick usage — `mcp` (memory tools over stdio)
|
|
127
|
+
|
|
128
|
+
The `mcp` namespace exposes six tools so any MCP-capable host (Claude Desktop,
|
|
129
|
+
chatbots, other agents) can work with the user's memory:
|
|
130
|
+
|
|
131
|
+
**Read tools** — `recall` (semantic search), `list_memories` (filtered
|
|
132
|
+
summaries), `get_memory` (one entry in full).
|
|
133
|
+
|
|
134
|
+
**Document tools** — a deliberate propose-then-write pair so the only write
|
|
135
|
+
surface is gated behind an explicit second call (honoring "auto the plumbing,
|
|
136
|
+
propose the prose" — operational records are auto-kept; formal documents are
|
|
137
|
+
proposed, then written only on acceptance):
|
|
138
|
+
- `suggest_document(topic, name, content, …)` — returns a preview + target path
|
|
139
|
+
+ fingerprint, **writes nothing**. Flags if the target exists or was declined.
|
|
140
|
+
- `write_document(…)` — the explicit second step; creates
|
|
141
|
+
`data/<topic>/<name>.md` with frontmatter (never overwrites) and records the
|
|
142
|
+
acceptance.
|
|
143
|
+
- `decline_document(topic, name)` — suppresses the same suggestion for ~30 days.
|
|
144
|
+
|
|
145
|
+
Because these are memory features, they ship **inside this package** rather than
|
|
146
|
+
as a separate install. The MCP SDK (`@modelcontextprotocol/sdk`) is an
|
|
147
|
+
**optional** dependency, loaded at runtime only when the server runs — consumers
|
|
148
|
+
who never run it pay nothing for it.
|
|
149
|
+
|
|
150
|
+
Run the bundled stdio server (usually launched by the host, not by hand):
|
|
151
|
+
|
|
152
|
+
```sh
|
|
153
|
+
npx vortex-mcp-recall --data-dir ./data
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
It self-resolves the data dir from `--data-dir`, then `VORTEX_DATA_DIR`, then the
|
|
157
|
+
cwd, and reads the recall index at `<dataDir>/_indexes/memory.sqlite`. The
|
|
158
|
+
embedder loads once at startup and is reused for every query.
|
|
159
|
+
|
|
160
|
+
Claude Desktop (`claude_desktop_config.json`, one-time setup):
|
|
161
|
+
|
|
162
|
+
```json
|
|
163
|
+
{
|
|
164
|
+
"mcpServers": {
|
|
165
|
+
"vortex-recall": {
|
|
166
|
+
"command": "npx",
|
|
167
|
+
"args": ["vortex-mcp-recall", "--data-dir", "/absolute/path/to/instance/data"]
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Programmatic use (attach your own transport):
|
|
174
|
+
|
|
175
|
+
```ts
|
|
176
|
+
import { vector, mcp } from "@vortex-os/memory-extended";
|
|
177
|
+
|
|
178
|
+
const server = await mcp.createRecallServer({
|
|
179
|
+
embed: vector.createLocalEmbedder(),
|
|
180
|
+
dbPath: "./data/_indexes/memory.sqlite",
|
|
181
|
+
});
|
|
182
|
+
// await server.connect(transport)
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Quick usage — `consolidate`
|
|
186
|
+
|
|
187
|
+
```ts
|
|
188
|
+
import { SessionArchiveStore } from "@vortex-os/memory-extended/sessionArchive";
|
|
189
|
+
import { Consolidator } from "@vortex-os/memory-extended/consolidate";
|
|
190
|
+
import { proactiveCurator } from "@vortex-os/base";
|
|
191
|
+
|
|
192
|
+
const store = new SessionArchiveStore("./data");
|
|
193
|
+
const consolidator = new Consolidator(store, process.cwd(), {
|
|
194
|
+
defaultLookbackDays: 7,
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
// Host-supplied LLM adapter — see @vortex-os/base "Opt-in /curate surface"
|
|
198
|
+
// for the Claude Code wiring example.
|
|
199
|
+
const llm = new proactiveCurator.ClaudeCodeLLMJudge(async ({ prompt }) => {
|
|
200
|
+
return await invokeMySubAgent(prompt);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
const proposals = await consolidator.propose({}, { llm, now: new Date() });
|
|
204
|
+
// Each proposal carries onAccept / onDecline thunks. Surface them to the
|
|
205
|
+
// operator; on accept the memory file is written under data/_memory/<name>.md
|
|
206
|
+
// via a single create-file action.
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
The consolidator queries recently-ingested sessions from `sessionArchive`, asks the LLM for memory candidates with explicit source-event citations, drops candidates whose fingerprint is on the rejected list (30-day expiry), and returns proposals shaped exactly like `proactive-curator` outputs — so a single `/curate`-style host UX renders both in-session capture and post-session consolidation.
|
|
210
|
+
|
|
211
|
+
## Privacy & network behavior
|
|
212
|
+
|
|
213
|
+
- **Embedding model download.** The `vector` / `recall` namespaces use the
|
|
214
|
+
`e5-small` sentence-embedding model via `@huggingface/transformers`. On first
|
|
215
|
+
use the model weights are fetched (HTTP `GET`, read-only) from `huggingface.co`
|
|
216
|
+
and cached locally. To run fully offline: set `HF_HUB_OFFLINE=1`, pre-seed the
|
|
217
|
+
Hugging Face cache directory, or pass your own `EmbedFn` to skip the bundled
|
|
218
|
+
model entirely. No data is uploaded — the download is one-directional.
|
|
219
|
+
- **Consolidation and the host LLM.** The `consolidate` namespace sends excerpts
|
|
220
|
+
of your archived session transcripts to whatever LLM adapter the host wires in
|
|
221
|
+
(the `LLMJudge` you supply). Those excerpts leave the process only through that
|
|
222
|
+
adapter; choose one whose data-handling you trust.
|
|
223
|
+
- **Local storage permissions.** Archived raw transcripts, the normalized JSONL,
|
|
224
|
+
and the SQLite indexes are written under `data/_session-archive/` with
|
|
225
|
+
owner-only permissions (`0o700` dirs / `0o600` files) on POSIX hosts. This is a
|
|
226
|
+
no-op on Windows.
|
|
227
|
+
- **Verbatim transcript content + git tracking.** The normalized JSONL under
|
|
228
|
+
`data/_session-archive/normalized/` stores **verbatim** conversation content —
|
|
229
|
+
every user and assistant message as it was written. If you enable git tracking
|
|
230
|
+
for that path (the normalized layer is the cross-machine source of truth, so
|
|
231
|
+
this is a reasonable choice), any secret that appeared in a transcript — an API
|
|
232
|
+
token pasted into a prompt, a password in a tool result — is committed and
|
|
233
|
+
synced along with it. To scrub such patterns *before* the normalized files are
|
|
234
|
+
written, supply an ingest-time redaction hook via `AdapterOptions.redact`
|
|
235
|
+
(passed as `options.redact` to `ingest`): it runs on every event after
|
|
236
|
+
normalization and before storage, so a regex that masks token-shaped strings
|
|
237
|
+
keeps them out of the archive entirely.
|
|
238
|
+
|
|
239
|
+
## Design documents
|
|
240
|
+
|
|
241
|
+
- [`docs/memory-extended-design.md`](../../docs/memory-extended-design.md) — cluster overview, sub-phase breakdown, industry pattern alignment
|
|
242
|
+
- [`docs/transcript-adapter-design.md`](../../docs/transcript-adapter-design.md) — `sessionArchive` adapter interface, per-host schema mapping, on-disk storage, SQLite schema, lock-handling UX
|
|
243
|
+
- [`docs/proactive-curator-design.md`](../../docs/proactive-curator-design.md) — in-session counterpart (separate module)
|
|
244
|
+
- [`docs/architecture.md`](../../docs/architecture.md#multi-layer-memory-architecture) — multi-layer memory architecture, source-of-truth policy
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type { ArchivedSession, CollectSessionsOptions, } from "./query.js";
|
|
2
|
+
export { collectRecentSessions, renderSessionForPrompt } from "./query.js";
|
|
3
|
+
export type { RejectedEntry } from "./store.js";
|
|
4
|
+
export { loadRejectedFingerprints, recordAcceptance, recordRejection, resetRejected, } from "./store.js";
|
|
5
|
+
export type { ConsolidateAcceptResult, ConsolidateProposal, ConsolidatorOptions, MemoryCandidate, SourceExcerptId, } from "./types.js";
|
|
6
|
+
export { Consolidator } from "./proposer.js";
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/consolidate/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,eAAe,EACf,sBAAsB,GACvB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAE3E,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EACL,wBAAwB,EACxB,gBAAgB,EAChB,eAAe,EACf,aAAa,GACd,MAAM,YAAY,CAAC;AAEpB,YAAY,EACV,uBAAuB,EACvB,mBAAmB,EACnB,mBAAmB,EACnB,eAAe,EACf,eAAe,GAChB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/consolidate/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAG3E,OAAO,EACL,wBAAwB,EACxB,gBAAgB,EAChB,eAAe,EACf,aAAa,GACd,MAAM,YAAY,CAAC;AAUpB,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { LLMJudge } from "../internal/proactive-curator-helpers.js";
|
|
2
|
+
import type { SessionArchiveStore } from "../sessionArchive/index.js";
|
|
3
|
+
import type { ConsolidateProposal, ConsolidatorOptions } from "./types.js";
|
|
4
|
+
/**
|
|
5
|
+
* Post-session consolidator. Reads recently-ingested sessions from the
|
|
6
|
+
* archive, asks the LLM to extract memory candidates from them, and
|
|
7
|
+
* returns a list of proposals that the host can surface to the user.
|
|
8
|
+
*
|
|
9
|
+
* The proposer is pure — it does not write to `_memory/` during `propose()`.
|
|
10
|
+
* Each returned proposal carries an `onAccept` thunk that writes the
|
|
11
|
+
* memory file (and records the acceptance) and an `onDecline` thunk that
|
|
12
|
+
* records the rejection. This matches the proactive-curator lifecycle so
|
|
13
|
+
* a single host UX can render both surfaces.
|
|
14
|
+
*
|
|
15
|
+
* Construction order (sub-phase 11d in `docs/memory-extended-design.md`):
|
|
16
|
+
* propose()
|
|
17
|
+
* -> query.collectRecentSessions(sinceMs, limit)
|
|
18
|
+
* -> renderSessionForPrompt
|
|
19
|
+
* -> LLMJudge.ask(extract-candidates prompt)
|
|
20
|
+
* -> validate candidates + compute fingerprint
|
|
21
|
+
* -> drop ones already on the rejected list
|
|
22
|
+
* -> return ConsolidateProposal[]
|
|
23
|
+
*
|
|
24
|
+
* Fingerprint shape is `hash(candidate.name + body excerpt + sorted source IDs)`
|
|
25
|
+
* so the same memory can re-surface if the LLM's body shifts but stays
|
|
26
|
+
* suppressed if it lands on the same content again.
|
|
27
|
+
*/
|
|
28
|
+
export declare class Consolidator {
|
|
29
|
+
private readonly store;
|
|
30
|
+
private readonly cwd;
|
|
31
|
+
private readonly defaultLookbackDays;
|
|
32
|
+
private readonly maxSessionsPerRun;
|
|
33
|
+
private readonly maxCandidatesPerRun;
|
|
34
|
+
private readonly rejectionExpiryDays?;
|
|
35
|
+
constructor(store: SessionArchiveStore, cwd: string, options?: ConsolidatorOptions);
|
|
36
|
+
propose(input: {
|
|
37
|
+
readonly sinceMs?: number;
|
|
38
|
+
}, ctx: {
|
|
39
|
+
readonly llm: LLMJudge;
|
|
40
|
+
readonly now: Date;
|
|
41
|
+
}): Promise<readonly ConsolidateProposal[]>;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=proposer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proposer.d.ts","sourceRoot":"","sources":["../../src/consolidate/proposer.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAEV,QAAQ,EAET,MAAM,0CAA0C,CAAC;AAYlD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,KAAK,EAEV,mBAAmB,EACnB,mBAAmB,EAGpB,MAAM,YAAY,CAAC;AAEpB;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAAa,YAAY;IAOrB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,GAAG;IAPtB,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAS;IAC7C,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAS;IAC7C,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAAS;gBAG3B,KAAK,EAAE,mBAAmB,EAC1B,GAAG,EAAE,MAAM,EAC5B,OAAO,CAAC,EAAE,mBAAmB;IAUzB,OAAO,CACX,KAAK,EAAE;QAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,EACpC,GAAG,EAAE;QAAE,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC;QAAC,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAA;KAAE,GAClD,OAAO,CAAC,SAAS,mBAAmB,EAAE,CAAC;CAoE3C"}
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { assertSafeBasename } from "../internal/proactive-curator-helpers.js";
|
|
5
|
+
import { collectRecentSessions, renderSessionForPrompt, } from "./query.js";
|
|
6
|
+
import { loadRejectedFingerprints, recordAcceptance, recordRejection, } from "./store.js";
|
|
7
|
+
/**
|
|
8
|
+
* Post-session consolidator. Reads recently-ingested sessions from the
|
|
9
|
+
* archive, asks the LLM to extract memory candidates from them, and
|
|
10
|
+
* returns a list of proposals that the host can surface to the user.
|
|
11
|
+
*
|
|
12
|
+
* The proposer is pure — it does not write to `_memory/` during `propose()`.
|
|
13
|
+
* Each returned proposal carries an `onAccept` thunk that writes the
|
|
14
|
+
* memory file (and records the acceptance) and an `onDecline` thunk that
|
|
15
|
+
* records the rejection. This matches the proactive-curator lifecycle so
|
|
16
|
+
* a single host UX can render both surfaces.
|
|
17
|
+
*
|
|
18
|
+
* Construction order (sub-phase 11d in `docs/memory-extended-design.md`):
|
|
19
|
+
* propose()
|
|
20
|
+
* -> query.collectRecentSessions(sinceMs, limit)
|
|
21
|
+
* -> renderSessionForPrompt
|
|
22
|
+
* -> LLMJudge.ask(extract-candidates prompt)
|
|
23
|
+
* -> validate candidates + compute fingerprint
|
|
24
|
+
* -> drop ones already on the rejected list
|
|
25
|
+
* -> return ConsolidateProposal[]
|
|
26
|
+
*
|
|
27
|
+
* Fingerprint shape is `hash(candidate.name + body excerpt + sorted source IDs)`
|
|
28
|
+
* so the same memory can re-surface if the LLM's body shifts but stays
|
|
29
|
+
* suppressed if it lands on the same content again.
|
|
30
|
+
*/
|
|
31
|
+
export class Consolidator {
|
|
32
|
+
store;
|
|
33
|
+
cwd;
|
|
34
|
+
defaultLookbackDays;
|
|
35
|
+
maxSessionsPerRun;
|
|
36
|
+
maxCandidatesPerRun;
|
|
37
|
+
rejectionExpiryDays;
|
|
38
|
+
constructor(store, cwd, options) {
|
|
39
|
+
this.store = store;
|
|
40
|
+
this.cwd = cwd;
|
|
41
|
+
this.defaultLookbackDays = options?.defaultLookbackDays ?? 7;
|
|
42
|
+
this.maxSessionsPerRun = options?.maxSessionsPerRun ?? 20;
|
|
43
|
+
this.maxCandidatesPerRun = options?.maxCandidatesPerRun ?? 5;
|
|
44
|
+
if (options?.rejectionExpiryDays !== undefined) {
|
|
45
|
+
this.rejectionExpiryDays = options.rejectionExpiryDays;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
async propose(input, ctx) {
|
|
49
|
+
const sinceMs = input.sinceMs ?? ctx.now.getTime() - this.defaultLookbackDays * 24 * 60 * 60 * 1000;
|
|
50
|
+
const sessions = collectRecentSessions(this.store, {
|
|
51
|
+
sinceMs,
|
|
52
|
+
limit: this.maxSessionsPerRun,
|
|
53
|
+
});
|
|
54
|
+
if (sessions.length === 0)
|
|
55
|
+
return [];
|
|
56
|
+
const transcript = sessions.map(renderSessionForPrompt).join("\n\n---\n\n");
|
|
57
|
+
const prompt = buildExtractPrompt(transcript, this.maxCandidatesPerRun);
|
|
58
|
+
const expected = {
|
|
59
|
+
shape: "json",
|
|
60
|
+
schema: {
|
|
61
|
+
candidates: "MemoryCandidate[]",
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
const raw = await ctx.llm.ask(prompt, expected);
|
|
65
|
+
const candidates = normalizeCandidates(raw, sessions);
|
|
66
|
+
if (candidates.length === 0)
|
|
67
|
+
return [];
|
|
68
|
+
const rejected = await loadRejectedFingerprints(this.cwd, ctx.now);
|
|
69
|
+
const proposals = [];
|
|
70
|
+
for (const c of candidates) {
|
|
71
|
+
const fingerprint = computeCandidateFingerprint(c);
|
|
72
|
+
if (rejected.has(fingerprint))
|
|
73
|
+
continue;
|
|
74
|
+
const filename = `${c.name}.md`;
|
|
75
|
+
const action = {
|
|
76
|
+
kind: "create-file",
|
|
77
|
+
folderPath: "_memory",
|
|
78
|
+
filename,
|
|
79
|
+
body: renderMemoryBody(c, ctx.now),
|
|
80
|
+
};
|
|
81
|
+
const cwd = this.cwd;
|
|
82
|
+
const rejectionExpiryDays = this.rejectionExpiryDays;
|
|
83
|
+
proposals.push({
|
|
84
|
+
kind: "consolidate-memory",
|
|
85
|
+
fingerprint,
|
|
86
|
+
action,
|
|
87
|
+
candidate: c,
|
|
88
|
+
preview: renderPreview(c),
|
|
89
|
+
rationale: `Extracted from ${c.sourceExcerptIds.length} session excerpt(s); ${describeType(c.type)}.`,
|
|
90
|
+
sourceRefs: c.sourceExcerptIds.map(formatSourceRef),
|
|
91
|
+
onAccept: async () => {
|
|
92
|
+
const writtenPath = await applyAction(cwd, action);
|
|
93
|
+
await recordAcceptance(cwd, {
|
|
94
|
+
fingerprint,
|
|
95
|
+
candidateName: c.name,
|
|
96
|
+
writtenPath,
|
|
97
|
+
now: ctx.now,
|
|
98
|
+
});
|
|
99
|
+
return {
|
|
100
|
+
writtenPath,
|
|
101
|
+
actionApplied: "create-file",
|
|
102
|
+
nextActionHint: `Memory written to _memory/${filename}. Edit freely; the source archive entries were not modified.`,
|
|
103
|
+
};
|
|
104
|
+
},
|
|
105
|
+
onDecline: async () => {
|
|
106
|
+
await recordRejection(cwd, {
|
|
107
|
+
fingerprint,
|
|
108
|
+
candidateName: c.name,
|
|
109
|
+
now: ctx.now,
|
|
110
|
+
...(rejectionExpiryDays !== undefined ? { expiryDays: rejectionExpiryDays } : {}),
|
|
111
|
+
});
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
return proposals;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function buildExtractPrompt(transcript, maxCandidates) {
|
|
119
|
+
return [
|
|
120
|
+
`You are reviewing past agent sessions to extract memory candidates worth remembering across future sessions.`,
|
|
121
|
+
``,
|
|
122
|
+
`Read the transcripts and identify up to ${maxCandidates} concrete memory candidates. Each candidate should be either:`,
|
|
123
|
+
` - a rule the operator established ("when X, do Y"),`,
|
|
124
|
+
` - a stable fact about the operator or their setup ("operator's home network uses X"),`,
|
|
125
|
+
` - a decision with durable scope ("we chose A over B for reason R").`,
|
|
126
|
+
``,
|
|
127
|
+
`Do NOT propose routine status updates, transient debugging context, or anything the operator would obviously not want pinned across sessions.`,
|
|
128
|
+
``,
|
|
129
|
+
`Return JSON:`,
|
|
130
|
+
` { "candidates": [ { "name": "<slug-safe-id>", "description": "<one line>", "type": "user|feedback|project|reference", "body": "<markdown body, no frontmatter>", "sourceExcerptIds": [ { "host": "claude-code", "sessionId": "...", "eventIdx": 12 }, ... ] }, ... ] }`,
|
|
131
|
+
``,
|
|
132
|
+
`If nothing is worth promoting, return { "candidates": [] }.`,
|
|
133
|
+
``,
|
|
134
|
+
`Sessions:`,
|
|
135
|
+
transcript,
|
|
136
|
+
].join("\n");
|
|
137
|
+
}
|
|
138
|
+
function normalizeCandidates(raw, sessions) {
|
|
139
|
+
if (typeof raw !== "object" || raw === null)
|
|
140
|
+
return [];
|
|
141
|
+
const obj = raw;
|
|
142
|
+
if (!Array.isArray(obj.candidates))
|
|
143
|
+
return [];
|
|
144
|
+
const validHostSession = new Set(sessions.map((s) => `${s.metadata.host}:${s.metadata.sessionId}`));
|
|
145
|
+
const out = [];
|
|
146
|
+
for (const c of obj.candidates) {
|
|
147
|
+
if (typeof c !== "object" || c === null)
|
|
148
|
+
continue;
|
|
149
|
+
const rec = c;
|
|
150
|
+
const name = slugify(String(rec.name ?? "").trim());
|
|
151
|
+
const description = String(rec.description ?? "").trim();
|
|
152
|
+
const type = String(rec.type ?? "").trim().toLowerCase();
|
|
153
|
+
const body = String(rec.body ?? "").trim();
|
|
154
|
+
if (!name || !description || !type || !body)
|
|
155
|
+
continue;
|
|
156
|
+
const sourceExcerptIds = extractSourceIds(rec.sourceExcerptIds, validHostSession);
|
|
157
|
+
if (sourceExcerptIds.length === 0)
|
|
158
|
+
continue;
|
|
159
|
+
out.push({ name, description, type, body, sourceExcerptIds });
|
|
160
|
+
}
|
|
161
|
+
return out;
|
|
162
|
+
}
|
|
163
|
+
function extractSourceIds(raw, valid) {
|
|
164
|
+
if (!Array.isArray(raw))
|
|
165
|
+
return [];
|
|
166
|
+
const out = [];
|
|
167
|
+
for (const r of raw) {
|
|
168
|
+
if (typeof r !== "object" || r === null)
|
|
169
|
+
continue;
|
|
170
|
+
const rec = r;
|
|
171
|
+
const host = typeof rec.host === "string" ? rec.host : "";
|
|
172
|
+
const sessionId = typeof rec.sessionId === "string" ? rec.sessionId : "";
|
|
173
|
+
const eventIdx = typeof rec.eventIdx === "number" ? rec.eventIdx : NaN;
|
|
174
|
+
if (!host || !sessionId || !Number.isInteger(eventIdx))
|
|
175
|
+
continue;
|
|
176
|
+
if (!valid.has(`${host}:${sessionId}`))
|
|
177
|
+
continue;
|
|
178
|
+
out.push({ host, sessionId, eventIdx });
|
|
179
|
+
}
|
|
180
|
+
return out;
|
|
181
|
+
}
|
|
182
|
+
function slugify(s) {
|
|
183
|
+
return s.toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
184
|
+
}
|
|
185
|
+
function computeCandidateFingerprint(c) {
|
|
186
|
+
const normalized = JSON.stringify({
|
|
187
|
+
name: c.name,
|
|
188
|
+
bodyHead: c.body.slice(0, 200),
|
|
189
|
+
sourceIds: [...c.sourceExcerptIds]
|
|
190
|
+
.map((s) => `${s.host}:${s.sessionId}:${s.eventIdx}`)
|
|
191
|
+
.sort(),
|
|
192
|
+
});
|
|
193
|
+
return createHash("sha256").update(normalized).digest("hex").slice(0, 16);
|
|
194
|
+
}
|
|
195
|
+
function renderMemoryBody(c, now) {
|
|
196
|
+
const ymd = formatYmd(now);
|
|
197
|
+
return `---
|
|
198
|
+
name: ${c.name}
|
|
199
|
+
description: ${c.description}
|
|
200
|
+
type: ${c.type}
|
|
201
|
+
created: ${ymd}
|
|
202
|
+
updated: ${ymd}
|
|
203
|
+
tags: [consolidate, proactive-curator]
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
${c.body}
|
|
207
|
+
|
|
208
|
+
## Sources
|
|
209
|
+
|
|
210
|
+
${c.sourceExcerptIds.map((s) => `- ${formatSourceRef(s)}`).join("\n")}
|
|
211
|
+
`;
|
|
212
|
+
}
|
|
213
|
+
function renderPreview(c) {
|
|
214
|
+
return [
|
|
215
|
+
`name: ${c.name}`,
|
|
216
|
+
`type: ${c.type}`,
|
|
217
|
+
`description: ${c.description}`,
|
|
218
|
+
"",
|
|
219
|
+
c.body,
|
|
220
|
+
"",
|
|
221
|
+
`Sources: ${c.sourceExcerptIds.length} excerpt(s)`,
|
|
222
|
+
].join("\n");
|
|
223
|
+
}
|
|
224
|
+
function describeType(type) {
|
|
225
|
+
switch (type) {
|
|
226
|
+
case "user":
|
|
227
|
+
return "operator profile fact";
|
|
228
|
+
case "feedback":
|
|
229
|
+
return "rule / preference";
|
|
230
|
+
case "project":
|
|
231
|
+
return "project decision";
|
|
232
|
+
case "reference":
|
|
233
|
+
return "stable reference";
|
|
234
|
+
default:
|
|
235
|
+
return `type "${type}"`;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
function formatSourceRef(s) {
|
|
239
|
+
return `${s.host}/${s.sessionId}#${s.eventIdx}`;
|
|
240
|
+
}
|
|
241
|
+
function formatYmd(d) {
|
|
242
|
+
const y = d.getFullYear();
|
|
243
|
+
const m = String(d.getMonth() + 1).padStart(2, "0");
|
|
244
|
+
const day = String(d.getDate()).padStart(2, "0");
|
|
245
|
+
return `${y}-${m}-${day}`;
|
|
246
|
+
}
|
|
247
|
+
async function applyAction(cwd, action) {
|
|
248
|
+
// Structural containment: consolidate always targets the fixed `_memory`
|
|
249
|
+
// system dir (which the canonical path gate rejects by design, so it cannot
|
|
250
|
+
// govern this write), and the filename is a model-derived candidate slug.
|
|
251
|
+
// Pin the folder and enforce a safe basename so a hostile name (separators,
|
|
252
|
+
// traversal, or a Windows colon/ADS) cannot escape `_memory/`.
|
|
253
|
+
if (action.folderPath !== "_memory") {
|
|
254
|
+
throw new Error(`refusing to write consolidated memory outside _memory/: got "${action.folderPath}"`);
|
|
255
|
+
}
|
|
256
|
+
assertSafeBasename(action.filename);
|
|
257
|
+
const folder = join(cwd, "data", action.folderPath);
|
|
258
|
+
await mkdir(folder, { recursive: true });
|
|
259
|
+
const file = join(folder, action.filename);
|
|
260
|
+
// Exclusive create (`wx`): the candidate name is an LLM-derived slug, so it
|
|
261
|
+
// can collide with an existing memory file. Never clobber — surface a clear
|
|
262
|
+
// conflict so the host can rename instead of silently overwriting. `onAccept`
|
|
263
|
+
// has no conflict field in `ConsolidateAcceptResult`, so a thrown Error is the
|
|
264
|
+
// cleanest signal: the host already awaits the accept thunk and propagates it.
|
|
265
|
+
try {
|
|
266
|
+
await writeFile(file, action.body, { encoding: "utf8", flag: "wx" });
|
|
267
|
+
}
|
|
268
|
+
catch (e) {
|
|
269
|
+
if (e.code === "EEXIST") {
|
|
270
|
+
throw new Error(`refusing to overwrite existing memory: ${action.folderPath}/${action.filename}`);
|
|
271
|
+
}
|
|
272
|
+
throw e;
|
|
273
|
+
}
|
|
274
|
+
return file;
|
|
275
|
+
}
|
|
276
|
+
//# sourceMappingURL=proposer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proposer.js","sourceRoot":"","sources":["../../src/consolidate/proposer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { EventRow, SessionMetadataRow, SessionArchiveStore } from "../sessionArchive/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* One archived session's content as the consolidator sees it — metadata
|
|
4
|
+
* plus the full ordered event list. Kept separate from
|
|
5
|
+
* `SessionArchiveStore` so the consolidator can shape its own
|
|
6
|
+
* presentation (LLM prompts, fingerprint inputs) without coupling to the
|
|
7
|
+
* archive's row layout.
|
|
8
|
+
*/
|
|
9
|
+
export interface ArchivedSession {
|
|
10
|
+
readonly metadata: SessionMetadataRow;
|
|
11
|
+
readonly events: readonly EventRow[];
|
|
12
|
+
}
|
|
13
|
+
export interface CollectSessionsOptions {
|
|
14
|
+
/** Only sessions ingested strictly after this epoch-ms cutoff. */
|
|
15
|
+
readonly sinceMs?: number;
|
|
16
|
+
/** Cap the number of sessions returned. Newest-ingested first. */
|
|
17
|
+
readonly limit?: number;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Pull the recently-ingested sessions together with their event bodies.
|
|
21
|
+
* Sessions with zero text events are dropped — they cannot drive a memory
|
|
22
|
+
* proposal, and feeding them to the LLM only wastes tokens.
|
|
23
|
+
*/
|
|
24
|
+
export declare function collectRecentSessions(store: SessionArchiveStore, options?: CollectSessionsOptions): readonly ArchivedSession[];
|
|
25
|
+
/**
|
|
26
|
+
* Render an archived session as a compact transcript for an LLM prompt.
|
|
27
|
+
* Empty / tool-call-only events are dropped (they rarely carry the kind
|
|
28
|
+
* of insight worth promoting to memory); the remaining events are joined
|
|
29
|
+
* with `[<role>]` prefixes for the LLM to follow turn boundaries.
|
|
30
|
+
*/
|
|
31
|
+
export declare function renderSessionForPrompt(session: ArchivedSession): string;
|
|
32
|
+
//# sourceMappingURL=query.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../src/consolidate/query.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,QAAQ,EACR,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,4BAA4B,CAAC;AAEpC;;;;;;GAMG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,CAAC;IACtC,QAAQ,CAAC,MAAM,EAAE,SAAS,QAAQ,EAAE,CAAC;CACtC;AAED,MAAM,WAAW,sBAAsB;IACrC,kEAAkE;IAClE,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,kEAAkE;IAClE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,mBAAmB,EAC1B,OAAO,CAAC,EAAE,sBAAsB,GAC/B,SAAS,eAAe,EAAE,CAa5B;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,eAAe,GAAG,MAAM,CAYvE"}
|