memkin 0.4.1 → 0.4.2

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.
@@ -1,7 +1,7 @@
1
1
  // AUTO-GENERATED by scripts/gen-embedded-assets.mjs — do not edit by hand.
2
2
  // Source: src/store/schema.sql + src/extractors/prompts/**/*.md
3
3
  // Regenerate with `bun run gen:assets`.
4
- export const VERSION = "0.4.1";
4
+ export const VERSION = "0.4.2";
5
5
  export const SCHEMA_SQL = "CREATE EXTENSION IF NOT EXISTS vector;\nCREATE EXTENSION IF NOT EXISTS pg_trgm;\n\nCREATE TABLE IF NOT EXISTS pages (\n id SERIAL PRIMARY KEY,\n slug TEXT UNIQUE NOT NULL,\n type TEXT NOT NULL,\n title TEXT NOT NULL,\n compiled_truth TEXT NOT NULL DEFAULT '',\n frontmatter JSONB NOT NULL DEFAULT '{}',\n content_hash TEXT,\n halflife_days INTEGER,\n tier TEXT NOT NULL DEFAULT 'hot',\n expires_at TIMESTAMPTZ,\n consolidated_into INTEGER REFERENCES pages(id),\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\n);\n\nCREATE INDEX IF NOT EXISTS idx_pages_title_trgm ON pages USING gin (title gin_trgm_ops);\nCREATE INDEX IF NOT EXISTS idx_pages_compiled_truth_trgm ON pages USING gin (compiled_truth gin_trgm_ops);\n-- idx_pages_tier and idx_pages_expires_at are created by M003 lifecycle_tier migration,\n-- which is the only place that guarantees the tier/expires_at columns exist on upgrade.\n\nCREATE TABLE IF NOT EXISTS content_chunks (\n id SERIAL PRIMARY KEY,\n page_id INTEGER NOT NULL REFERENCES pages(id) ON DELETE CASCADE,\n chunk_index INTEGER NOT NULL,\n UNIQUE(page_id, chunk_index),\n chunk_text TEXT NOT NULL,\n chunk_source TEXT NOT NULL DEFAULT 'compiled_truth',\n token_count INTEGER,\n embedding vector(__EMBEDDING_DIM__),\n model TEXT NOT NULL DEFAULT 'text-embedding-3-large',\n embedded_at TIMESTAMPTZ,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()\n);\n\nCREATE INDEX IF NOT EXISTS idx_chunks_embedding ON content_chunks\n USING hnsw (embedding vector_cosine_ops);\nCREATE INDEX IF NOT EXISTS idx_chunks_chunk_text_trgm ON content_chunks USING gin (chunk_text gin_trgm_ops);\n\nCREATE TABLE IF NOT EXISTS links (\n id SERIAL PRIMARY KEY,\n from_page_id INTEGER NOT NULL REFERENCES pages(id) ON DELETE CASCADE,\n to_page_id INTEGER NOT NULL REFERENCES pages(id) ON DELETE CASCADE,\n link_type TEXT NOT NULL DEFAULT '',\n context TEXT NOT NULL DEFAULT '',\n provenance JSONB,\n source_hash TEXT,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n UNIQUE(from_page_id, to_page_id, link_type)\n);\n\nCREATE INDEX IF NOT EXISTS idx_links_from ON links (from_page_id);\nCREATE INDEX IF NOT EXISTS idx_links_to ON links (to_page_id);\n\nCREATE TABLE IF NOT EXISTS tags (\n id SERIAL PRIMARY KEY,\n page_id INTEGER NOT NULL REFERENCES pages(id) ON DELETE CASCADE,\n tag TEXT NOT NULL,\n UNIQUE(page_id, tag)\n);\n\nCREATE INDEX IF NOT EXISTS idx_tags_tag ON tags (tag);\n\nCREATE TABLE IF NOT EXISTS timeline_entries (\n id SERIAL PRIMARY KEY,\n page_id INTEGER NOT NULL REFERENCES pages(id) ON DELETE CASCADE,\n date TEXT NOT NULL,\n summary TEXT NOT NULL,\n detail TEXT NOT NULL DEFAULT '',\n source TEXT NOT NULL DEFAULT '',\n provenance JSONB,\n tier TEXT NOT NULL DEFAULT 'hot',\n expires_at TIMESTAMPTZ,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n UNIQUE(page_id, date, summary)\n);\n\n-- Identity cache for person slug canonicalization\n-- identity_cache doubles as the person slug canonicalization store via\n-- platform = 'canonical':\n-- external_id = model-produced slug or display name\n-- display_name = canonical person slug (e.g. 'person/wang-jiandu')\n-- slug_hint = original entity.name (used for collision detection)\nCREATE TABLE IF NOT EXISTS identity_cache (\n platform TEXT NOT NULL,\n external_id TEXT NOT NULL,\n display_name TEXT,\n slug_hint TEXT,\n resolved_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n PRIMARY KEY (platform, external_id)\n);\n\nDO $$ BEGIN\n IF NOT EXISTS (\n SELECT 1 FROM information_schema.columns\n WHERE table_name = 'identity_cache' AND column_name = 'slug_hint'\n ) THEN\n ALTER TABLE identity_cache ADD COLUMN slug_hint TEXT;\n END IF;\nEND $$;\n\n-- person_handles: the typed alias layer (Layer 1 of person identity).\n-- Maps any \"handle\" by which a person is known to the canonical person page\n-- slug. A handle is (kind, value); the pair is unique, so one handle resolves\n-- to exactly one person. Merging/aliasing is explicit (see core/person-identity).\n-- kind = 'feishu_open_id' | 'email' | 'name' | 'nickname' | 'slug'\n-- value = canonicalized handle value (lowercased / whitespace-collapsed)\n-- strength = 'strong' (auto-resolvable: open_id/email/name/slug)\n-- | 'weak' (nickname/花名 — only created via explicit link)\n--\n-- M008 generalizes this table into `entity_handles` (rename + entity_type/scope\n-- columns) and leaves `person_handles` behind as a compat VIEW. schema.sql is\n-- re-applied on every boot BEFORE migrations, so this legacy block must be\n-- conditional: once entity_handles exists, creating the table would shadow the\n-- view (and CREATE INDEX ... ON a view errors regardless of IF NOT EXISTS).\nDO $$ BEGIN\n IF NOT EXISTS (\n SELECT 1 FROM pg_class WHERE relname = 'entity_handles' AND relkind = 'r'\n ) THEN\n CREATE TABLE IF NOT EXISTS person_handles (\n kind TEXT NOT NULL,\n value TEXT NOT NULL,\n canonical_slug TEXT NOT NULL,\n strength TEXT NOT NULL DEFAULT 'strong',\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n PRIMARY KEY (kind, value)\n );\n CREATE INDEX IF NOT EXISTS idx_person_handles_slug ON person_handles (canonical_slug);\n END IF;\nEND $$;\n\n-- Engine/embedding metadata (key-value). Used for embedding fingerprint consistency.\nCREATE TABLE IF NOT EXISTS memkin_meta (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n);\n";
6
6
  export const PROMPTS = {
7
7
  "examples/agent-session.md": "# Example: Agent Session Extraction\n\n## Input Conversation\n\n```\n[2024-01-15T10:00:00Z] → Alice: We need to migrate from sessions to JWT for the auth system\n[2024-01-15T10:01:30Z] ← Bob: Makes sense for scaling. I can start this week.\n[2024-01-15T10:02:15Z] → Alice: Perfect. Let's use the HS256 algorithm initially.\n[2024-01-15T10:03:00Z] ← Bob: Got it. Should we add refresh tokens too?\n[2024-01-15T10:03:45Z] → Alice: Yes, 7-day access tokens and 30-day refresh tokens.\n[2024-01-15T10:04:30Z] ← Bob: I'll document the decision in the wiki before implementing.\n```\n\n## Output JSON\n\n```json\n{\n \"source\": {\n \"platform\": \"slack\",\n \"channel\": \"#engineering\",\n \"timestamp\": \"2024-01-15T10:00:00Z\",\n \"thread_id\": \"thread-auth-migration\",\n \"raw_hash\": \"abc123def456\",\n \"quote\": \"We need to migrate from sessions to JWT for the auth system\"\n },\n \"entities\": [\n {\n \"slug\": \"person/alice\",\n \"name\": \"Alice\",\n \"type\": \"person\",\n \"context\": \"Engineering lead discussing auth migration\",\n \"confidence\": \"direct\"\n },\n {\n \"slug\": \"person/bob\",\n \"name\": \"Bob\",\n \"type\": \"person\",\n \"context\": \"Developer implementing JWT auth system\",\n \"confidence\": \"direct\"\n },\n {\n \"slug\": \"project/auth-system\",\n \"name\": \"Auth System\",\n \"type\": \"project\",\n \"context\": \"Authentication system being migrated to JWT\",\n \"confidence\": \"direct\"\n },\n {\n \"slug\": \"tool/jwt\",\n \"name\": \"JWT (JSON Web Tokens)\",\n \"type\": \"tool\",\n \"context\": \"Token-based authentication mechanism replacing sessions\",\n \"confidence\": \"direct\"\n },\n {\n \"slug\": \"concept/refresh-tokens\",\n \"name\": \"Refresh Tokens\",\n \"type\": \"concept\",\n \"context\": \"30-day tokens for obtaining new access tokens\",\n \"confidence\": \"direct\"\n }\n ],\n \"timeline\": [\n {\n \"date\": \"2024-01-15\",\n \"summary\": \"Decision to migrate from sessions to JWT authentication\",\n \"entities\": [\"person/alice\", \"person/bob\", \"project/auth-system\", \"tool/jwt\"],\n \"source\": {\n \"platform\": \"slack\",\n \"channel\": \"#engineering\",\n \"timestamp\": \"2024-01-15T10:00:00Z\",\n \"thread_id\": \"thread-auth-migration\",\n \"raw_hash\": \"abc123def456\",\n \"quote\": \"We need to migrate from sessions to JWT for the auth system\"\n },\n \"confidence\": \"direct\"\n },\n {\n \"date\": \"2024-01-15\",\n \"summary\": \"Bob commits to starting JWT migration this week\",\n \"entities\": [\"person/bob\", \"project/auth-system\"],\n \"source\": {\n \"platform\": \"slack\",\n \"channel\": \"#engineering\",\n \"timestamp\": \"2024-01-15T10:01:30Z\",\n \"thread_id\": \"thread-auth-migration\",\n \"raw_hash\": \"abc123def456\",\n \"quote\": \"Makes sense for scaling. I can start this week.\"\n },\n \"confidence\": \"direct\"\n }\n ],\n \"links\": [\n {\n \"from\": \"person/bob\",\n \"to\": \"project/auth-system\",\n \"type\": \"works_on\",\n \"context\": \"Bob is implementing the JWT migration for auth system\",\n \"confidence\": \"direct\"\n },\n {\n \"from\": \"project/auth-system\",\n \"to\": \"tool/jwt\",\n \"type\": \"depends_on\",\n \"context\": \"Auth system will use JWT for token-based authentication\",\n \"confidence\": \"direct\"\n },\n {\n \"from\": \"person/alice\",\n \"to\": \"project/auth-system\",\n \"type\": \"works_on\",\n \"context\": \"Alice is leading the auth system migration decision\",\n \"confidence\": \"paraphrased\"\n }\n ],\n \"decisions\": [\n {\n \"summary\": \"Migrate authentication from sessions to JWT\",\n \"reasoning\": \"Better for scaling across multiple instances\",\n \"alternatives\": [\"Keep session-based auth\", \"Use OAuth2\"],\n \"entities\": [\"project/auth-system\", \"tool/jwt\"],\n \"date\": \"2024-01-15\",\n \"confidence\": \"direct\",\n \"source\": {\n \"platform\": \"slack\",\n \"channel\": \"#engineering\",\n \"timestamp\": \"2024-01-15T10:00:00Z\",\n \"thread_id\": \"thread-auth-migration\",\n \"raw_hash\": \"abc123def456\",\n \"quote\": \"We need to migrate from sessions to JWT for the auth system\"\n }\n },\n {\n \"summary\": \"Use HS256 algorithm for JWT signing\",\n \"entities\": [\"tool/jwt\"],\n \"date\": \"2024-01-15\",\n \"confidence\": \"direct\",\n \"source\": {\n \"platform\": \"slack\",\n \"channel\": \"#engineering\",\n \"timestamp\": \"2024-01-15T10:02:15Z\",\n \"thread_id\": \"thread-auth-migration\",\n \"raw_hash\": \"abc123def456\",\n \"quote\": \"Let's use the HS256 algorithm initially.\"\n }\n },\n {\n \"summary\": \"Token expiration: 7-day access tokens, 30-day refresh tokens\",\n \"entities\": [\"tool/jwt\", \"concept/refresh-tokens\"],\n \"date\": \"2024-01-15\",\n \"confidence\": \"direct\",\n \"source\": {\n \"platform\": \"slack\",\n \"channel\": \"#engineering\",\n \"timestamp\": \"2024-01-15T10:03:45Z\",\n \"thread_id\": \"thread-auth-migration\",\n \"raw_hash\": \"abc123def456\",\n \"quote\": \"Yes, 7-day access tokens and 30-day refresh tokens.\"\n }\n }\n ],\n \"tasks\": [\n {\n \"title\": \"Implement JWT-based authentication\",\n \"status\": \"open\",\n \"owner\": \"person/bob\",\n \"project\": \"project/auth-system\",\n \"valid_at\": \"2024-01-15T10:01:30Z\",\n \"source\": {\n \"platform\": \"slack\",\n \"channel\": \"#engineering\",\n \"timestamp\": \"2024-01-15T10:01:30Z\",\n \"thread_id\": \"thread-auth-migration\",\n \"raw_hash\": \"abc123def456\",\n \"quote\": \"I can start this week.\"\n },\n \"confidence\": \"direct\"\n },\n {\n \"title\": \"Document JWT migration decision in wiki\",\n \"status\": \"open\",\n \"owner\": \"person/bob\",\n \"project\": \"project/auth-system\",\n \"valid_at\": \"2024-01-15T10:04:30Z\",\n \"source\": {\n \"platform\": \"slack\",\n \"channel\": \"#engineering\",\n \"timestamp\": \"2024-01-15T10:04:30Z\",\n \"thread_id\": \"thread-auth-migration\",\n \"raw_hash\": \"abc123def456\",\n \"quote\": \"I'll document the decision in the wiki before implementing.\"\n },\n \"confidence\": \"direct\"\n }\n ],\n \"discoveries\": [\n {\n \"summary\": \"Session-based auth doesn't scale well across multiple instances\",\n \"type\": \"insight\",\n \"entities\": [\"project/auth-system\"],\n \"source\": {\n \"platform\": \"slack\",\n \"channel\": \"#engineering\",\n \"timestamp\": \"2024-01-15T10:01:30Z\",\n \"thread_id\": \"thread-auth-migration\",\n \"raw_hash\": \"abc123def456\",\n \"quote\": \"Makes sense for scaling.\"\n },\n \"confidence\": \"paraphrased\"\n },\n {\n \"summary\": \"Document architectural decisions before implementation\",\n \"type\": \"procedure\",\n \"entities\": [\"person/bob\"],\n \"source\": {\n \"platform\": \"slack\",\n \"channel\": \"#engineering\",\n \"timestamp\": \"2024-01-15T10:04:30Z\",\n \"thread_id\": \"thread-auth-migration\",\n \"raw_hash\": \"abc123def456\",\n \"quote\": \"I'll document the decision in the wiki before implementing.\"\n },\n \"confidence\": \"direct\"\n }\n ],\n \"knowledge\": [\n {\n \"topic\": \"jwt-token-expiration\",\n \"content\": \"Access tokens should be short-lived (minutes to hours) while refresh tokens can be longer-lived (days to weeks) to balance security with user experience\",\n \"source_type\": \"teaching\",\n \"related_entities\": [\"tool/jwt\", \"concept/refresh-tokens\"],\n \"source\": {\n \"platform\": \"slack\",\n \"channel\": \"#engineering\",\n \"timestamp\": \"2024-01-15T10:03:45Z\",\n \"thread_id\": \"thread-auth-migration\",\n \"raw_hash\": \"abc123def456\",\n \"quote\": \"Yes, 7-day access tokens and 30-day refresh tokens.\"\n },\n \"confidence\": \"paraphrased\"\n }\n ],\n \"preferences\": [],\n \"references\": []\n}\n```\n\n## Key Takeaways from This Example\n\n1. **Entity consistency**: Same person/project mentioned multiple times uses the same slug\n2. **Confidence levels**: Most are \"direct\" (explicit), some \"paraphrased\" (clear implication)\n3. **Rich context**: Each signal includes enough context to understand it standalone\n4. **Quotes**: All under 300 chars, capturing the key statement\n5. **Links**: Connect entities with specific relationship types\n6. **Timeline**: Events ordered chronologically with clear summaries\n7. **Decisions**: Capture not just what was decided, but why and what alternatives existed\n8. **Tasks**: Include owner, status, and timing information\n9. **Discoveries**: Extract both insights (learnings) and procedures (how-to)\n10. **Knowledge**: Extract reusable facts that stand alone without the original conversation context\n11. **Preferences and references**: Always include both arrays even when empty — this conversation has no shared links and no explicit standing-preference statements (the wiki-documentation comment is a one-off recommendation, correctly classified as `discovery.type=procedure`, not duplicated as a preference)\n",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memkin",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
4
4
  "description": "Local-first personal memory system — extracts signals from Feishu/Lark chats and AI-agent sessions into a private knowledge graph, served to any agent via MCP",
5
5
  "type": "module",
6
6
  "bin": {
@@ -49,6 +49,7 @@
49
49
  "@hono/node-server": "^1.19.14",
50
50
  "@modelcontextprotocol/sdk": "^1.29.0",
51
51
  "commander": "^11.1.0",
52
+ "fast-glob": "^3.3.3",
52
53
  "hono": "^4.12.23",
53
54
  "ink": "^7.0.5",
54
55
  "openai": "^6.39.0",
@@ -65,7 +66,6 @@
65
66
  "@types/node": "^25.9.1",
66
67
  "@types/pg": "^8.20.0",
67
68
  "@types/react": "^19.2.16",
68
- "fast-glob": "^3.3.3",
69
69
  "typescript": "^6.0.3",
70
70
  "vitest": "^1.0.4"
71
71
  },