sessionmem 1.0.5 → 1.0.6

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 (37) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +372 -365
  3. package/dist/adapters/capabilities/fallbackTools.js +33 -18
  4. package/dist/adapters/claudeMdInjector.js +120 -0
  5. package/dist/adapters/generic.js +83 -12
  6. package/dist/adapters/tools/ping.js +4 -1
  7. package/dist/cli/commands/install.js +18 -1
  8. package/dist/cli/commands/reEmbed.js +47 -0
  9. package/dist/cli/commands/run.js +28 -2
  10. package/dist/cli/commands/savings.js +75 -0
  11. package/dist/cli/commands/uninstall.js +10 -0
  12. package/dist/cli/index.js +14 -0
  13. package/dist/cli/output.js +11 -3
  14. package/dist/core/api/contracts.js +34 -10
  15. package/dist/core/api/memoryCoreService.js +188 -86
  16. package/dist/core/api/sessionLifecycleService.js +12 -2
  17. package/dist/core/config/policyConfig.js +20 -0
  18. package/dist/core/injection/formatStartupInjection.js +2 -1
  19. package/dist/core/injection/tokenBudget.js +8 -0
  20. package/dist/core/retrieve/importance.js +4 -3
  21. package/dist/core/retrieve/recencyBands.js +3 -10
  22. package/dist/core/retrieve/retrieveMemories.js +17 -4
  23. package/dist/core/retrieve/score.js +11 -1
  24. package/dist/core/schema/migrations/005_team_provenance.sql +9 -9
  25. package/dist/core/schema/migrations/006_access_pattern_boosting.sql +5 -0
  26. package/dist/core/schema/migrations/007_feedback_manual_delete.sql +23 -0
  27. package/dist/core/schema/migrations/008_fts5_search.sql +33 -0
  28. package/dist/core/storage/db.js +6 -0
  29. package/dist/core/storage/memoryFeedbackRepo.js +14 -4
  30. package/dist/core/storage/memoryRepo.js +134 -120
  31. package/dist/core/storage/memorySearchRepo.js +87 -13
  32. package/dist/core/storage/sessionEventsRepo.js +19 -9
  33. package/dist/core/storage/summarizationFailuresRepo.js +36 -26
  34. package/dist/core/storage/tokenSavingsRepo.js +20 -0
  35. package/dist/core/summarize/cloudSummarizer.js +21 -5
  36. package/dist/core/summarize/localSummarizer.js +1 -10
  37. package/package.json +50 -48
@@ -1,12 +1,10 @@
1
- function toParams(input) {
2
- return {
3
- ...input,
4
- created_at: input.created_at ?? null,
5
- updated_at: input.updated_at ?? null,
6
- };
7
- }
8
- export function insertSummarizationFailure(db, input) {
9
- const stmt = db.prepare(`
1
+ const sumFailStmtCache = new WeakMap();
2
+ function getSumFailStatements(db) {
3
+ let stmts = sumFailStmtCache.get(db);
4
+ if (stmts)
5
+ return stmts;
6
+ stmts = {
7
+ insertFailure: db.prepare(`
10
8
  INSERT INTO summarization_failures (
11
9
  id, project_id, session_id, source_adapter, reason, attempt_count, last_error_json, created_at, updated_at
12
10
  ) VALUES (
@@ -14,26 +12,38 @@ export function insertSummarizationFailure(db, input) {
14
12
  COALESCE(@created_at, strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
15
13
  COALESCE(@updated_at, strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
16
14
  )
17
- `);
18
- stmt.run(toParams(input));
19
- }
20
- export function listSummarizationFailures(db, projectId, sessionId) {
21
- if (sessionId) {
22
- const stmt = db.prepare(`
23
- SELECT
24
- id, project_id, session_id, source_adapter, reason, attempt_count, last_error_json, created_at, updated_at
25
- FROM summarization_failures
26
- WHERE project_id = ? AND session_id = ?
27
- ORDER BY updated_at DESC
28
- `);
29
- return stmt.all(projectId, sessionId);
30
- }
31
- const stmt = db.prepare(`
15
+ `),
16
+ listByProjectAndSession: db.prepare(`
17
+ SELECT
18
+ id, project_id, session_id, source_adapter, reason, attempt_count, last_error_json, created_at, updated_at
19
+ FROM summarization_failures
20
+ WHERE project_id = ? AND session_id = ?
21
+ ORDER BY updated_at DESC
22
+ `),
23
+ listByProject: db.prepare(`
32
24
  SELECT
33
25
  id, project_id, session_id, source_adapter, reason, attempt_count, last_error_json, created_at, updated_at
34
26
  FROM summarization_failures
35
27
  WHERE project_id = ?
36
28
  ORDER BY updated_at DESC
37
- `);
38
- return stmt.all(projectId);
29
+ `),
30
+ };
31
+ sumFailStmtCache.set(db, stmts);
32
+ return stmts;
33
+ }
34
+ function toParams(input) {
35
+ return {
36
+ ...input,
37
+ created_at: input.created_at ?? null,
38
+ updated_at: input.updated_at ?? null,
39
+ };
40
+ }
41
+ export function insertSummarizationFailure(db, input) {
42
+ getSumFailStatements(db).insertFailure.run(toParams(input));
43
+ }
44
+ export function listSummarizationFailures(db, projectId, sessionId) {
45
+ if (sessionId) {
46
+ return getSumFailStatements(db).listByProjectAndSession.all(projectId, sessionId);
47
+ }
48
+ return getSumFailStatements(db).listByProject.all(projectId);
39
49
  }
@@ -0,0 +1,20 @@
1
+ const tokenSavingsStmtCache = new WeakMap();
2
+ function getTokenSavingsStatements(db) {
3
+ let stmts = tokenSavingsStmtCache.get(db);
4
+ if (stmts)
5
+ return stmts;
6
+ stmts = {
7
+ countDistinctSessions: db.prepare("SELECT COUNT(DISTINCT session_id) AS count FROM session_events WHERE project_id = ?"),
8
+ listPayloads: db.prepare("SELECT payload_json FROM session_events WHERE project_id = ?"),
9
+ };
10
+ tokenSavingsStmtCache.set(db, stmts);
11
+ return stmts;
12
+ }
13
+ export function countDistinctSessions(db, projectId) {
14
+ const row = getTokenSavingsStatements(db).countDistinctSessions.get(projectId);
15
+ return row.count;
16
+ }
17
+ export function listEventPayloads(db, projectId) {
18
+ const rows = getTokenSavingsStatements(db).listPayloads.all(projectId);
19
+ return rows.map(r => r.payload_json);
20
+ }
@@ -1,19 +1,35 @@
1
+ import Anthropic from "@anthropic-ai/sdk";
1
2
  import { summarizeLocalSessionEvents } from "./localSummarizer.js";
2
- const DEFAULT_CLOUD_MODEL = "claude-sonnet-4-20250514";
3
+ import { DEFAULT_SUMMARIZER_MODEL } from "../config/policyConfig.js";
4
+ const CLOUD_SYSTEM_PROMPT = "You are a memory compressor for AI coding sessions. Given this session transcript summary, produce a compact, high-signal list of facts, decisions, and context that should be remembered. Be extremely concise.";
3
5
  export async function summarizeWithCloud(input) {
4
6
  if (!input.anthropicApiKey.trim()) {
5
7
  throw new Error("Missing anthropicApiKey");
6
8
  }
7
- const result = await summarizeLocalSessionEvents({
9
+ // Step 1: Preprocess via local summarizer (extract, redact, structure)
10
+ const localResult = await summarizeLocalSessionEvents({
8
11
  events: input.events,
9
12
  summaryTokenCap: input.summaryTokenCap,
10
13
  redactionEnabled: input.redactionEnabled,
11
14
  factMode: input.factMode,
12
15
  redactionRules: input.redactionRules,
13
16
  });
14
- const modelTag = input.model ?? DEFAULT_CLOUD_MODEL;
17
+ // Step 2: Send preprocessed summary to Claude for compression
18
+ const model = input.model ?? DEFAULT_SUMMARIZER_MODEL;
19
+ const client = new Anthropic({ apiKey: input.anthropicApiKey });
20
+ const response = await client.messages.create({
21
+ model,
22
+ max_tokens: input.summaryTokenCap * 2,
23
+ system: CLOUD_SYSTEM_PROMPT,
24
+ messages: [{ role: "user", content: localResult.summary }],
25
+ });
26
+ // Extract text from response content blocks
27
+ const text = response.content
28
+ .filter((block) => block.type === "text")
29
+ .map((block) => block.text)
30
+ .join("\n");
15
31
  return {
16
- summary: `[model:${modelTag}] ${result.summary}`,
17
- warningCodes: result.warningCodes,
32
+ summary: text,
33
+ warningCodes: localResult.warningCodes,
18
34
  };
19
35
  }
@@ -1,16 +1,7 @@
1
1
  import { normalizeEmbeddingText } from "../embed/textNormalize.js";
2
+ import { capTokens, countTokens } from "../injection/tokenBudget.js";
2
3
  import { applyRedaction } from "./redaction.js";
3
4
  import { buildStructuredSummary } from "./summaryShape.js";
4
- function countTokens(text) {
5
- return text.trim().split(/\s+/).filter(Boolean).length;
6
- }
7
- function capTokens(text, cap) {
8
- const tokens = text.trim().split(/\s+/).filter(Boolean);
9
- if (tokens.length <= cap) {
10
- return text;
11
- }
12
- return `${tokens.slice(0, cap).join(" ")} ...`;
13
- }
14
5
  export async function summarizeLocalSessionEvents(input) {
15
6
  const structured = buildStructuredSummary(input.events, {
16
7
  factMode: input.factMode,
package/package.json CHANGED
@@ -1,48 +1,50 @@
1
- {
2
- "name": "sessionmem",
3
- "version": "1.0.5",
4
- "private": false,
5
- "type": "module",
6
- "description": "Local-first MCP memory layer for coding agents across Claude Code, Codex, Cursor, Cline, Windsurf, and other MCP-compatible hosts.",
7
- "license": "MIT",
8
- "author": "kavishdua",
9
- "repository": {
10
- "type": "git",
11
- "url": "git+https://github.com/catfish-1234/sessionmem.git"
12
- },
13
- "mcpName": "io.github.catfish-1234/sessionmem",
14
- "files": [
15
- "dist"
16
- ],
17
- "publishConfig": {
18
- "access": "public"
19
- },
20
- "bin": {
21
- "sessionmem": "./dist/cli/index.js"
22
- },
23
- "scripts": {
24
- "build": "tsc && node scripts/copy-migrations.mjs",
25
- "prepack": "npm run build",
26
- "test": "vitest run --reporter=dot",
27
- "test:schema": "vitest run tests/integration/storage/schema.spec.ts --reporter=dot",
28
- "lint": "eslint .",
29
- "typecheck": "tsc --noEmit",
30
- "benchmark": "node scripts/benchmark.mjs"
31
- },
32
- "dependencies": {
33
- "@modelcontextprotocol/sdk": "^1.29.0",
34
- "better-sqlite3": "^12.4.1",
35
- "commander": "^15.0.0",
36
- "js-tiktoken": "^1.0.21",
37
- "zod": "^4.4.3"
38
- },
39
- "devDependencies": {
40
- "@eslint/js": "^10.0.1",
41
- "@types/better-sqlite3": "^7.6.13",
42
- "eslint": "^10.4.1",
43
- "globals": "^17.6.0",
44
- "typescript": "^6.0.3",
45
- "typescript-eslint": "^8.61.0",
46
- "vitest": "^4.1.8"
47
- }
48
- }
1
+ {
2
+ "name": "sessionmem",
3
+ "version": "1.0.6",
4
+ "private": false,
5
+ "type": "module",
6
+ "description": "Local-first MCP memory layer for coding agents across Claude Code, Codex, Cursor, Cline, Windsurf, and other MCP-compatible hosts.",
7
+ "license": "MIT",
8
+ "author": "kavishdua",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/catfish-1234/sessionmem.git"
12
+ },
13
+ "mcpName": "io.github.catfish-1234/sessionmem",
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "publishConfig": {
18
+ "access": "public"
19
+ },
20
+ "bin": {
21
+ "sessionmem": "./dist/cli/index.js"
22
+ },
23
+ "scripts": {
24
+ "build": "tsc && node scripts/copy-migrations.mjs",
25
+ "prepack": "npm run build",
26
+ "test": "vitest run --reporter=dot",
27
+ "test:schema": "vitest run tests/integration/storage/schema.spec.ts --reporter=dot",
28
+ "lint": "eslint .",
29
+ "typecheck": "tsc --noEmit",
30
+ "benchmark": "node scripts/benchmark.mjs",
31
+ "postversion": "node -e \"const v=require('./package.json').version; ['package/package.json','.claude-plugin/plugin.json','server.json'].forEach(f=>{const o=require('./'+f);o.version=v;if(o.packages&&o.packages[0])o.packages[0].version=v;require('fs').writeFileSync('./'+f,JSON.stringify(o,null,2)+'\\n')})\""
32
+ },
33
+ "dependencies": {
34
+ "@anthropic-ai/sdk": "^0.105.0",
35
+ "@modelcontextprotocol/sdk": "^1.29.0",
36
+ "better-sqlite3": "^12.4.1",
37
+ "commander": "^15.0.0",
38
+ "js-tiktoken": "^1.0.21",
39
+ "zod": "^4.4.3"
40
+ },
41
+ "devDependencies": {
42
+ "@eslint/js": "^10.0.1",
43
+ "@types/better-sqlite3": "^7.6.13",
44
+ "eslint": "^10.4.1",
45
+ "globals": "^17.6.0",
46
+ "typescript": "^6.0.3",
47
+ "typescript-eslint": "^8.61.0",
48
+ "vitest": "^4.1.8"
49
+ }
50
+ }