memkin 0.4.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/README.en.md +108 -836
  2. package/README.md +108 -836
  3. package/README.zh-CN.md +80 -44
  4. package/dist/cli-stores.d.ts +12 -0
  5. package/dist/cli-stores.d.ts.map +1 -1
  6. package/dist/cli-stores.js +15 -1
  7. package/dist/cli-stores.js.map +1 -1
  8. package/dist/cli.js +94 -6
  9. package/dist/cli.js.map +1 -1
  10. package/dist/collectors/agent/claude-code.d.ts.map +1 -1
  11. package/dist/collectors/agent/claude-code.js +10 -29
  12. package/dist/collectors/agent/claude-code.js.map +1 -1
  13. package/dist/collectors/agent/codex.d.ts.map +1 -1
  14. package/dist/collectors/agent/codex.js +24 -10
  15. package/dist/collectors/agent/codex.js.map +1 -1
  16. package/dist/collectors/agent/collector.d.ts +38 -0
  17. package/dist/collectors/agent/collector.d.ts.map +1 -1
  18. package/dist/collectors/agent/collector.js +91 -20
  19. package/dist/collectors/agent/collector.js.map +1 -1
  20. package/dist/collectors/agent/hermes.d.ts.map +1 -1
  21. package/dist/collectors/agent/hermes.js +5 -1
  22. package/dist/collectors/agent/hermes.js.map +1 -1
  23. package/dist/collectors/feishu/sources/calendar.d.ts.map +1 -1
  24. package/dist/collectors/feishu/sources/calendar.js +18 -7
  25. package/dist/collectors/feishu/sources/calendar.js.map +1 -1
  26. package/dist/consolidator/consolidator.d.ts +9 -1
  27. package/dist/consolidator/consolidator.d.ts.map +1 -1
  28. package/dist/consolidator/consolidator.js +7 -0
  29. package/dist/consolidator/consolidator.js.map +1 -1
  30. package/dist/consolidator/entity-merge.d.ts +33 -0
  31. package/dist/consolidator/entity-merge.d.ts.map +1 -0
  32. package/dist/consolidator/entity-merge.js +148 -0
  33. package/dist/consolidator/entity-merge.js.map +1 -0
  34. package/dist/core/agent-session-scanner.d.ts +23 -0
  35. package/dist/core/agent-session-scanner.d.ts.map +1 -0
  36. package/dist/core/agent-session-scanner.js +69 -0
  37. package/dist/core/agent-session-scanner.js.map +1 -0
  38. package/dist/core/canonicalize.d.ts +8 -0
  39. package/dist/core/canonicalize.d.ts.map +1 -1
  40. package/dist/core/canonicalize.js +12 -2
  41. package/dist/core/canonicalize.js.map +1 -1
  42. package/dist/core/config.d.ts +6 -0
  43. package/dist/core/config.d.ts.map +1 -1
  44. package/dist/core/config.js.map +1 -1
  45. package/dist/core/identity-resolver.d.ts +53 -3
  46. package/dist/core/identity-resolver.d.ts.map +1 -1
  47. package/dist/core/identity-resolver.js +112 -17
  48. package/dist/core/identity-resolver.js.map +1 -1
  49. package/dist/core/person-identity.d.ts +38 -3
  50. package/dist/core/person-identity.d.ts.map +1 -1
  51. package/dist/core/person-identity.js +58 -29
  52. package/dist/core/person-identity.js.map +1 -1
  53. package/dist/core/pipeline-factory.d.ts.map +1 -1
  54. package/dist/core/pipeline-factory.js +2 -1
  55. package/dist/core/pipeline-factory.js.map +1 -1
  56. package/dist/core/pipeline.js +3 -3
  57. package/dist/core/pipeline.js.map +1 -1
  58. package/dist/daemon/autostart/daemon-state.d.ts +32 -0
  59. package/dist/daemon/autostart/daemon-state.d.ts.map +1 -1
  60. package/dist/daemon/autostart/daemon-state.js +27 -1
  61. package/dist/daemon/autostart/daemon-state.js.map +1 -1
  62. package/dist/daemon/scheduler.d.ts +10 -0
  63. package/dist/daemon/scheduler.d.ts.map +1 -1
  64. package/dist/daemon/scheduler.js +20 -1
  65. package/dist/daemon/scheduler.js.map +1 -1
  66. package/dist/demo/seed.d.ts +32 -0
  67. package/dist/demo/seed.d.ts.map +1 -0
  68. package/dist/demo/seed.js +185 -0
  69. package/dist/demo/seed.js.map +1 -0
  70. package/dist/embedded-assets.generated.d.ts.map +1 -1
  71. package/dist/embedded-assets.generated.js +2 -2
  72. package/dist/embedded-assets.generated.js.map +1 -1
  73. package/dist/eval/golden.d.ts +107 -0
  74. package/dist/eval/golden.d.ts.map +1 -0
  75. package/dist/eval/golden.js +74 -0
  76. package/dist/eval/golden.js.map +1 -0
  77. package/dist/eval/judge.d.ts +82 -0
  78. package/dist/eval/judge.d.ts.map +1 -0
  79. package/dist/eval/judge.js +130 -0
  80. package/dist/eval/judge.js.map +1 -0
  81. package/dist/eval/manifest.d.ts +107 -0
  82. package/dist/eval/manifest.d.ts.map +1 -0
  83. package/dist/eval/manifest.js +84 -0
  84. package/dist/eval/manifest.js.map +1 -0
  85. package/dist/eval/metrics.d.ts +87 -0
  86. package/dist/eval/metrics.d.ts.map +1 -0
  87. package/dist/eval/metrics.js +97 -0
  88. package/dist/eval/metrics.js.map +1 -0
  89. package/dist/extractors/noise-filter.d.ts +7 -3
  90. package/dist/extractors/noise-filter.d.ts.map +1 -1
  91. package/dist/extractors/noise-filter.js +16 -3
  92. package/dist/extractors/noise-filter.js.map +1 -1
  93. package/dist/extractors/signal-extractor.d.ts.map +1 -1
  94. package/dist/extractors/signal-extractor.js +13 -2
  95. package/dist/extractors/signal-extractor.js.map +1 -1
  96. package/dist/lifecycle/legacy-migration.d.ts +6 -2
  97. package/dist/lifecycle/legacy-migration.d.ts.map +1 -1
  98. package/dist/lifecycle/legacy-migration.js +69 -4
  99. package/dist/lifecycle/legacy-migration.js.map +1 -1
  100. package/dist/server/api.d.ts.map +1 -1
  101. package/dist/server/api.js +18 -7
  102. package/dist/server/api.js.map +1 -1
  103. package/dist/server/backfill-routes.d.ts.map +1 -1
  104. package/dist/server/backfill-routes.js +2 -1
  105. package/dist/server/backfill-routes.js.map +1 -1
  106. package/dist/server/mcp.d.ts +4 -4
  107. package/dist/store/agent-sessions.d.ts +59 -0
  108. package/dist/store/agent-sessions.d.ts.map +1 -0
  109. package/dist/store/agent-sessions.js +119 -0
  110. package/dist/store/agent-sessions.js.map +1 -0
  111. package/dist/store/database.d.ts +20 -1
  112. package/dist/store/database.d.ts.map +1 -1
  113. package/dist/store/database.js +44 -6
  114. package/dist/store/database.js.map +1 -1
  115. package/dist/store/embedding.d.ts +20 -2
  116. package/dist/store/embedding.d.ts.map +1 -1
  117. package/dist/store/embedding.js +20 -5
  118. package/dist/store/embedding.js.map +1 -1
  119. package/dist/store/entity-suggestions.d.ts +62 -0
  120. package/dist/store/entity-suggestions.d.ts.map +1 -0
  121. package/dist/store/entity-suggestions.js +80 -0
  122. package/dist/store/entity-suggestions.js.map +1 -0
  123. package/dist/store/migrations/index.d.ts.map +1 -1
  124. package/dist/store/migrations/index.js +91 -0
  125. package/dist/store/migrations/index.js.map +1 -1
  126. package/dist/store/pages.d.ts.map +1 -1
  127. package/dist/store/pages.js +12 -3
  128. package/dist/store/pages.js.map +1 -1
  129. package/package.json +1 -1
@@ -0,0 +1,185 @@
1
+ /**
2
+ * Demo dataset seeder (launch sprint Task D1).
3
+ *
4
+ * Loads the synthetic English demo pages from `demo/seed/pages/**.md` into a
5
+ * dedicated demo library (see `demo/demo.config.yaml`) using the regular store
6
+ * APIs, then lays down a "last week" timeline computed relative to now — the
7
+ * dataset must never contain hardcoded calendar dates.
8
+ *
9
+ * Idempotent: pages upsert by slug, wikilink edges upsert by (from,to,type),
10
+ * and timeline entries for seeded pages are replaced wholesale on each run.
11
+ */
12
+ import { readdirSync, readFileSync } from "node:fs";
13
+ import { join, relative, sep } from "node:path";
14
+ import { PageStore } from "../store/pages.js";
15
+ import { TimelineStore } from "../store/timeline.js";
16
+ /** Repo-relative location of the demo page markdown files. */
17
+ export const DEMO_PAGES_DIR = "demo/seed/pages";
18
+ /**
19
+ * The demo "last week": every entry is an offset from now, so the dataset
20
+ * stays fresh no matter when the seed runs (no time bombs).
21
+ */
22
+ const TIMELINE = [
23
+ {
24
+ slug: "entities/alice-chen",
25
+ daysAgo: 6,
26
+ hour: 10,
27
+ summary: "Launch scope review with Alice",
28
+ detail: "Walked the full Phoenix launch scope with Alice Chen; she pushed to protect the Friday date over feature completeness.",
29
+ },
30
+ {
31
+ slug: "decisions/phoenix-launch-friday",
32
+ daysAgo: 6,
33
+ hour: 11,
34
+ minute: 30,
35
+ summary: "Decided: ship Phoenix on Friday, cut the onboarding tour from v1",
36
+ detail: "Alice made the call in the scope review — smaller launch on time beats a bigger launch two weeks late.",
37
+ },
38
+ {
39
+ slug: "entities/project-phoenix",
40
+ daysAgo: 5,
41
+ hour: 9,
42
+ summary: "Onboarding tour removed from the launch branch",
43
+ detail: "Feature-flagged code deleted from the v1 branch; backlog item created for post-launch.",
44
+ },
45
+ {
46
+ slug: "tasks/send-alice-pricing-copy",
47
+ daysAgo: 5,
48
+ hour: 15,
49
+ summary: "Alice asked for the final pricing page copy by Wednesday",
50
+ detail: "She wants a doc (not a Slack thread) so she can lock the pricing page layout before Friday.",
51
+ },
52
+ {
53
+ slug: "entities/bob-martinez",
54
+ daysAgo: 4,
55
+ hour: 11,
56
+ summary: "Bob finished the staging Terraform changes",
57
+ detail: "Staging now mirrors production topology; PR open and waiting for review before the launch rehearsal.",
58
+ },
59
+ {
60
+ slug: "knowledge/alice-prefers-async-loom",
61
+ daysAgo: 4,
62
+ hour: 16,
63
+ summary: "Noted: Alice prefers async Loom reviews over live design crits",
64
+ detail: "After canceling the standing crit, Alice said Loom links with specific questions get her fastest feedback.",
65
+ },
66
+ {
67
+ slug: "tasks/qa-signup-flow",
68
+ daysAgo: 3,
69
+ hour: 10,
70
+ summary: "QA pass on the self-serve signup flow",
71
+ detail: "Happy path and invalid-card cases green; found the mobile nav overlap on the pricing page.",
72
+ },
73
+ {
74
+ slug: "tasks/fix-mobile-nav-overlap",
75
+ daysAgo: 3,
76
+ hour: 14,
77
+ summary: "Mobile nav overlap filed as launch blocker",
78
+ detail: "Alice flagged it as embarrassing-if-shipped; fix assigned before the Friday launch.",
79
+ },
80
+ {
81
+ slug: "decisions/pricing-three-tiers",
82
+ daysAgo: 2,
83
+ hour: 11,
84
+ summary: "Pricing locked: Free, Pro, Team",
85
+ detail: "Carol's three-tier proposal approved; pricing page copy updates to match the new tier names.",
86
+ },
87
+ {
88
+ slug: "entities/alice-chen",
89
+ daysAgo: 2,
90
+ hour: 17,
91
+ summary: "Reviewed Alice's Loom walkthrough of the final launch designs",
92
+ detail: "Pricing page and signup flow look ready; two copy nits sent back, no structural changes.",
93
+ },
94
+ {
95
+ slug: "references/phoenix-launch-checklist",
96
+ daysAgo: 1,
97
+ hour: 9,
98
+ minute: 30,
99
+ summary: "Launch checklist review: most gates green",
100
+ detail: "Remaining items: pricing copy handoff to Alice, mobile nav fix merge, on-call briefing.",
101
+ },
102
+ {
103
+ slug: "tasks/write-launch-announcement",
104
+ daysAgo: 1,
105
+ hour: 15,
106
+ summary: "Launch announcement drafted",
107
+ detail: "First full draft done; Carol reviews next, Alice supplies hero art from Figma.",
108
+ },
109
+ {
110
+ slug: "entities/project-phoenix",
111
+ daysAgo: 0,
112
+ hour: 9,
113
+ summary: "Final go/no-go: GO for the Friday launch",
114
+ detail: "All launch-blocking items closed or owned; team confirmed the Friday ship date.",
115
+ },
116
+ ];
117
+ function walkMarkdownFiles(dir) {
118
+ const out = [];
119
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
120
+ const full = join(dir, entry.name);
121
+ if (entry.isDirectory())
122
+ out.push(...walkMarkdownFiles(full));
123
+ else if (entry.isFile() && entry.name.endsWith(".md"))
124
+ out.push(full);
125
+ }
126
+ return out.sort();
127
+ }
128
+ /** `<pagesDir>/entities/alice-chen.md` → slug `entities/alice-chen`. */
129
+ function fileToSlug(pagesDir, file) {
130
+ return relative(pagesDir, file).replace(/\.md$/, "").split(sep).join("/");
131
+ }
132
+ function timelineDate(now, spec) {
133
+ const d = new Date(now);
134
+ d.setDate(d.getDate() - spec.daysAgo);
135
+ d.setHours(spec.hour, spec.minute ?? 0, 0, 0);
136
+ // Same-day entries scheduled after "now" would live in the future — clamp back.
137
+ return (d > now ? now : d).toISOString();
138
+ }
139
+ /**
140
+ * Seed the demo dataset into the given database. Safe to re-run: page and
141
+ * link writes are upserts, and timeline entries for the seeded slugs are
142
+ * replaced (relative dates would otherwise duplicate across days).
143
+ */
144
+ export async function seedDemo(db, opts) {
145
+ const now = opts.now ?? new Date();
146
+ const pages = new PageStore(db.executor);
147
+ const timeline = new TimelineStore(db.executor);
148
+ const files = walkMarkdownFiles(opts.pagesDir);
149
+ const docs = files.map((file) => ({
150
+ slug: fileToSlug(opts.pagesDir, file),
151
+ content: readFileSync(file, "utf-8"),
152
+ }));
153
+ // Pass 1: create every page (chunked for FTS). Wikilinks pointing at pages
154
+ // that appear later in the seed order cannot resolve yet.
155
+ for (const doc of docs) {
156
+ await pages.putPageWithChunks(db.executor, doc.slug, doc.content, { autoWikilink: false });
157
+ }
158
+ // Pass 2: now that all targets exist, wire every [[wikilink]] into edges.
159
+ for (const doc of docs) {
160
+ await pages.putPageWithChunks(db.executor, doc.slug, doc.content);
161
+ }
162
+ // Timeline: replace entries for seeded pages, then insert the relative feed.
163
+ const slugs = docs.map((d) => d.slug);
164
+ await db.executor.query(`DELETE FROM timeline_entries
165
+ WHERE page_id IN (SELECT id FROM pages WHERE slug = ANY($1::text[]))`, [slugs]);
166
+ for (const spec of TIMELINE) {
167
+ await timeline.addEntry(spec.slug, {
168
+ date: timelineDate(now, spec),
169
+ summary: spec.summary,
170
+ detail: spec.detail,
171
+ source: "demo-seed",
172
+ });
173
+ }
174
+ const [pageCount, linkCount, timelineCount] = await Promise.all([
175
+ db.executor.query("SELECT COUNT(*) AS cnt FROM pages"),
176
+ db.executor.query("SELECT COUNT(*) AS cnt FROM links"),
177
+ db.executor.query("SELECT COUNT(*) AS cnt FROM timeline_entries"),
178
+ ]);
179
+ return {
180
+ pages: Number(pageCount.rows[0].cnt),
181
+ links: Number(linkCount.rows[0].cnt),
182
+ timelineEntries: Number(timelineCount.rows[0].cnt),
183
+ };
184
+ }
185
+ //# sourceMappingURL=seed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"seed.js","sourceRoot":"","sources":["../../src/demo/seed.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"embedded-assets.generated.d.ts","sourceRoot":"","sources":["../src/embedded-assets.generated.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,OAAO,EAAE,MAAgB,CAAC;AAEvC,eAAO,MAAM,UAAU,EAAE,MAAsoK,CAAC;AAEhqK,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAK1C,CAAC"}
1
+ {"version":3,"file":"embedded-assets.generated.d.ts","sourceRoot":"","sources":["../src/embedded-assets.generated.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,OAAO,EAAE,MAAgB,CAAC;AAEvC,eAAO,MAAM,UAAU,EAAE,MAAusL,CAAC;AAEjuL,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAK1C,CAAC"}
@@ -1,8 +1,8 @@
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.0";
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)\nCREATE 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\nCREATE INDEX IF NOT EXISTS idx_person_handles_slug ON person_handles (canonical_slug);\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";
4
+ export const VERSION = "0.4.1";
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",
8
8
  "signal-extract.md": "# Signal Extraction Task\n\nExtract structured signals from the provided conversation block following the schema below.\n\n## Output Schema\n\n```typescript\ninterface ExtractionResult {\n source: SourceRef;\n entities: Entity[];\n timeline: TimelineEntry[];\n links: Link[];\n decisions: Decision[];\n tasks: TaskSignal[];\n discoveries: Discovery[];\n knowledge: Knowledge[];\n preferences: Preference[];\n references: Reference[];\n}\n\ninterface SourceRef {\n platform: string; // e.g., \"slack\", \"claude-code\"\n channel: string; // e.g., \"#general\", \"session-123\"\n timestamp: string; // ISO 8601\n message_id?: string;\n thread_id?: string;\n file_path?: string;\n line_range?: { start: number; end: number };\n attachment_id?: string;\n url?: string;\n raw_hash: string; // generated by system\n quote: string; // ≤ 300 chars, most relevant excerpt\n}\n\ninterface Entity {\n slug: string; // Format: {type}/{kebab-name}\n name: string; // Display name\n type: 'person' | 'project' | 'organization' | 'tool' | 'concept';\n context: string; // Brief description\n confidence: 'direct' | 'paraphrased' | 'inferred' | 'speculative';\n}\n\ninterface TimelineEntry {\n date: string; // ISO 8601 or partial (YYYY-MM, YYYY)\n summary: string; // Event description\n entities: string[]; // Slugs of involved entities\n source: SourceRef;\n confidence: 'direct' | 'paraphrased' | 'inferred' | 'speculative';\n}\n\ninterface Link {\n from: string; // Entity slug\n to: string; // Entity slug\n type: 'works_on' | 'works_at' | 'reports_to' | 'collaborates' | 'depends_on' | 'mentions' | 'custom';\n context: string; // Description of relationship\n confidence: 'direct' | 'paraphrased' | 'inferred' | 'speculative';\n source: SourceRef;\n}\n\ninterface Decision {\n summary: string; // What was decided\n reasoning?: string; // Why (if mentioned)\n alternatives?: string[]; // Other options considered\n entities: string[]; // Slugs of entities involved\n date: string; // ISO 8601\n valid_at?: string; // When decision takes effect\n invalid_at?: string; // When decision expires/superseded\n confidence: 'direct' | 'paraphrased' | 'inferred' | 'speculative';\n source: SourceRef;\n}\n\ninterface TaskSignal {\n title: string; // Task description\n status: 'open' | 'in_progress' | 'done' | 'cancelled';\n owner?: string; // Entity slug\n project?: string; // Entity slug\n due_date?: string; // ISO 8601\n valid_at?: string; // When task was created\n invalid_at?: string; // When task was closed\n source: SourceRef;\n confidence: 'direct' | 'paraphrased' | 'inferred' | 'speculative';\n}\n\ninterface Discovery {\n summary: string; // Brief insight\n detail?: string; // Extended explanation\n type: 'procedure' | 'pattern' | 'insight' | 'risk';\n entities: string[]; // Slugs of related entities\n source: SourceRef;\n confidence: 'direct' | 'paraphrased' | 'inferred' | 'speculative';\n}\ninterface Knowledge {\n topic: string; // kebab-case topic tag: \"react-hooks\", \"feishu-api\"\n content: string; // Decontextualized knowledge statement\n source_type: 'conversation' | 'document' | 'teaching';\n related_entities: string[]; // Entity slugs (may reference entities not in this block)\n valid_at?: string; // ISO 8601, when knowledge becomes valid\n invalid_at?: string; // ISO 8601, when knowledge expires\n source: SourceRef;\n confidence: 'direct' | 'paraphrased' | 'inferred' | 'speculative';\n}\n\ninterface Preference {\n summary: string; // \"Prefers async communication over meetings\"\n detail?: string; // Extended explanation\n category: 'communication' | 'tooling' | 'scheduling' | 'workflow' | 'other';\n entities: string[]; // Slugs, usually a person\n source: SourceRef;\n confidence: 'direct' | 'paraphrased' | 'inferred' | 'speculative';\n}\n\ninterface Reference {\n title: string; // Document/resource title\n url: string; // The resource URL — must appear in source text\n summary: string; // ≤100 chars: what the resource is about\n trigger?: string; // When this would be useful to recall later\n entities: string[]; // Slugs of related entities\n source: SourceRef;\n confidence: 'direct' | 'paraphrased' | 'inferred' | 'speculative';\n}\n```\n\n## Extraction Rules\n\n### Entity Slugs\n- Format: `{type}/{kebab-case-name}`\n- Examples: `person/alice-smith`, `project/auth-system`, `tool/github-actions`\n- Be consistent: same entity = same slug across all signals\n- For people: use recognizable name (first name + last initial if ambiguous)\n- For projects/tools: use official name or widely-used abbreviation\n\n### Quotes\n- Maximum 300 characters\n- Select the most informative excerpt\n- Don't fabricate - copy exact text (can trim with \"...\")\n- Should stand alone and convey key context\n\n### Confidence Levels\n- **direct**: Explicitly stated (\"Alice will lead the auth project\")\n- **paraphrased**: Clear implication with different wording (\"Alice is taking charge of authentication\")\n- **inferred**: Reasonable deduction (\"Alice keeps talking about auth, likely leading it\")\n- **speculative**: Uncertain but possible (\"Alice might be involved based on previous mentions\")\n\n### Dates\n- Prefer ISO 8601: `2024-01-15T10:30:00Z`\n- Partial dates OK: `2024-01` (month), `2024` (year)\n- Relative dates: resolve to absolute if possible (\"last Tuesday\" → `2024-01-09`)\n- Uncertain dates: use best estimate with lower confidence\n\n### Empty Results\n- If no signals found, return empty arrays for all categories\n- Still include valid `source` with platform, channel, timestamp, and quote\n\n### Knowledge vs Discovery\n\nKnowledge (知识): Decontextualized, reusable facts or concepts\n ✓ \"React useEffect runs twice in StrictMode during development\"\n ✓ \"Feishu API global rate limit is 50 QPS\"\n ✓ \"TCP three-way handshake prevents stale connection requests\"\n\nDiscovery (发现): Scene-bound, time-bound findings\n ✓ \"Today's integration test found 5s latency on this endpoint\"\n ✓ \"Root cause of this bug is a race condition\"\n ✓ \"Local Docker DNS resolution is broken\"\n\nRules:\n 1. Remove time/scene info — is the statement still valid and valuable? Yes → Knowledge\n 2. Could this be a FAQ or documentation entry as-is? Yes → Knowledge\n 3. If uncertain, prefer Discovery (conservative)\n\n### Knowledge vs Decision\n\nKnowledge: Norms, facts, concepts — objective, verifiable\n ✓ \"Feishu API global rate limit is 50 QPS\"\n ✓ \"Go goroutine initial stack size is 2KB\"\n\nDecision: Team/project choices, commitments — subjective, has an owner\n ✓ \"All services will use OpenTelemetry going forward\"\n ✓ \"We decided to replace MySQL with PostgreSQL\"\n\nRules:\n 1. Has an explicit decision-maker or team commitment? → Decision\n 2. Would this statement hold true for a different team/project? → Knowledge\n 3. If uncertain, look for verbs like \"decided\", \"chose\", \"switched to\" → Decision\n\n### Preference vs Discovery\n\nPreference (偏好): An explicit personal/team statement about how someone likes to work\n ✓ \"I prefer async communication over meetings\"\n ✓ \"We always write tests before implementation\" (framed as standing practice)\n ✓ \"I don't like being pinged after 6pm\"\n\nDiscovery-procedure (发现-流程): A how-to insight observed or recommended in the moment,\nnot framed as someone's standing preference\n ✓ \"Document architectural decisions before implementation\" (a recommendation, not \"I always do X\")\n\nRules:\n 1. Explicitly framed as \"I/we prefer/like/always do X\" with a clear subject? → Preference\n 2. A one-off recommendation or observed practice without standing-preference framing? → Discovery\n 3. If uncertain, prefer Discovery (conservative — Preference requires explicit personal framing)\n\n### Preference category\n\n- `communication`: sync/async style, channels, tone, responsiveness expectations\n- `tooling`: preferred editors, libraries, tools, tool-related workflows\n- `scheduling`: time-of-day habits, meeting patterns, availability\n- `workflow`: process/practice preferences not covered above (review style, docs habits)\n- `other`: doesn't fit the above\n\n### References\n\nA reference is a bookmark **with context** — a shared resource plus enough information\nfor an Agent to know when it would be useful to recall later.\n\nExtract a Reference when:\n ✓ A message shares a URL/doc/named resource AND gives enough context to fill in `summary`\n ✓ \"Check this out: https://example.com/jwt-guide — covers token rotation\" → Reference\n\nDo NOT extract a Reference when:\n ✗ A bare URL with no surrounding context (nothing to put in `summary`/`trigger`)\n ✗ The shared content IS the substance of the conversation — extract that as\n Knowledge/Decision/Discovery instead; References are for bookmarks, not the discussion itself\n\nReference field rules:\n - `url`: must appear verbatim in the source text — never fabricate or guess a URL\n - `summary`: ≤100 chars, what the resource covers\n - `trigger`: when recalling this would help (e.g., \"当排查 JWT 过期问题时\")\n\n### Knowledge source_type\n\n- `conversation`: Extracted from group/private chat messages\n- `document`: Extracted from cloud document content\n- `teaching`: Someone actively explaining/teaching (systematic explanation of principles, steps, processes)\n\n### Knowledge topic\n\n- Output kebab-case ASCII when possible: \"react-hooks\", \"feishu-api\"\n- For Chinese topics, translate to English: \"飞书API\" → \"feishu-api\"\n- The system normalizes non-conforming topics automatically\n\n### Knowledge confidence\n\n- Knowledge should rarely be `speculative`. The system may skip speculative Knowledge entries.\n- Prefer `direct` or `paraphrased` for factual statements.\n\n### Language Policy\n\n- Structural fields (slug, topic, type, status) → English ASCII, kebab-case\n- Natural language fields (name, summary, content, context, detail,\n reasoning, quote) → **SAME LANGUAGE as the source material**\n- If the conversation is in Chinese, write summaries in Chinese\n- If the conversation mixes languages, use the dominant language\n- quote field MUST be verbatim from the original text, never translated\n- slug for Chinese person names: use pinyin\n (e.g., \"李应龙\" → \"person/li-yinglong\")\n\n## Example\n\nSee `examples/agent-session.md` for a complete example with diverse signal types.\n\n## Your Task\n\nProcess the conversation block provided below and output ONLY valid JSON matching ExtractionResult schema.\n",
@@ -1 +1 @@
1
- {"version":3,"file":"embedded-assets.generated.js","sourceRoot":"","sources":["../src/embedded-assets.generated.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,gEAAgE;AAChE,wCAAwC;AAExC,MAAM,CAAC,MAAM,OAAO,GAAW,OAAO,CAAC;AAEvC,MAAM,CAAC,MAAM,UAAU,GAAW,6nKAA6nK,CAAC;AAEhqK,MAAM,CAAC,MAAM,OAAO,GAA2B;IAC7C,2BAA2B,EAAE,ipTAAipT;IAC9qT,mBAAmB,EAAE,2sVAA2sV;IAChuV,iBAAiB,EAAE,8kCAA8kC;IACjmC,WAAW,EAAE,+kFAA+kF;CAC7lF,CAAC"}
1
+ {"version":3,"file":"embedded-assets.generated.js","sourceRoot":"","sources":["../src/embedded-assets.generated.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,gEAAgE;AAChE,wCAAwC;AAExC,MAAM,CAAC,MAAM,OAAO,GAAW,OAAO,CAAC;AAEvC,MAAM,CAAC,MAAM,UAAU,GAAW,8rLAA8rL,CAAC;AAEjuL,MAAM,CAAC,MAAM,OAAO,GAA2B;IAC7C,2BAA2B,EAAE,ipTAAipT;IAC9qT,mBAAmB,EAAE,2sVAA2sV;IAChuV,iBAAiB,EAAE,8kCAA8kC;IACjmC,WAAW,EAAE,+kFAA+kF;CAC7lF,CAAC"}
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Golden annotation format: for a given session, the set of signals a human
3
+ * annotator judges the pipeline *should* have extracted (`should_record`), plus
4
+ * illustrative examples of things it should *not* have extracted
5
+ * (`should_not_record`) — e.g. transient chatter, unconfirmed proposals.
6
+ *
7
+ * This module only defines and validates the format. The actual human annotation
8
+ * of real sessions is a separate, manual step (see docs/eval-annotation-guide.md)
9
+ * — golden datasets built from real (potentially private) sessions are gitignored
10
+ * and kept locally; only a sanitized example fixture ships in the repo.
11
+ *
12
+ * Spec: memoark-2026-07-06-extraction-quality-redesign.md §10, §5 (signal shape).
13
+ */
14
+ import { z } from "zod";
15
+ export declare const GoldenSignalTypeSchema: z.ZodEnum<["decision", "task", "reference", "preference", "knowledge", "discovery"]>;
16
+ export type GoldenSignalType = z.infer<typeof GoldenSignalTypeSchema>;
17
+ export declare const GoldenAuthoritySchema: z.ZodEnum<["user_confirmed", "assistant_proposed", "assistant_claimed"]>;
18
+ export type GoldenAuthority = z.infer<typeof GoldenAuthoritySchema>;
19
+ export declare const GoldenSignalSchema: z.ZodObject<{
20
+ type: z.ZodEnum<["decision", "task", "reference", "preference", "knowledge", "discovery"]>;
21
+ authority: z.ZodEnum<["user_confirmed", "assistant_proposed", "assistant_claimed"]>;
22
+ topic: z.ZodString;
23
+ what: z.ZodString;
24
+ }, "strip", z.ZodTypeAny, {
25
+ type: "task" | "reference" | "decision" | "knowledge" | "discovery" | "preference";
26
+ what: string;
27
+ topic: string;
28
+ authority: "user_confirmed" | "assistant_proposed" | "assistant_claimed";
29
+ }, {
30
+ type: "task" | "reference" | "decision" | "knowledge" | "discovery" | "preference";
31
+ what: string;
32
+ topic: string;
33
+ authority: "user_confirmed" | "assistant_proposed" | "assistant_claimed";
34
+ }>;
35
+ export type GoldenSignal = z.infer<typeof GoldenSignalSchema>;
36
+ export declare const NegativeExampleSchema: z.ZodObject<{
37
+ what: z.ZodString;
38
+ reason: z.ZodOptional<z.ZodString>;
39
+ }, "strip", z.ZodTypeAny, {
40
+ what: string;
41
+ reason?: string | undefined;
42
+ }, {
43
+ what: string;
44
+ reason?: string | undefined;
45
+ }>;
46
+ export type NegativeExample = z.infer<typeof NegativeExampleSchema>;
47
+ export declare const GoldenAnnotationSchema: z.ZodObject<{
48
+ session_ref: z.ZodString;
49
+ should_record: z.ZodArray<z.ZodObject<{
50
+ type: z.ZodEnum<["decision", "task", "reference", "preference", "knowledge", "discovery"]>;
51
+ authority: z.ZodEnum<["user_confirmed", "assistant_proposed", "assistant_claimed"]>;
52
+ topic: z.ZodString;
53
+ what: z.ZodString;
54
+ }, "strip", z.ZodTypeAny, {
55
+ type: "task" | "reference" | "decision" | "knowledge" | "discovery" | "preference";
56
+ what: string;
57
+ topic: string;
58
+ authority: "user_confirmed" | "assistant_proposed" | "assistant_claimed";
59
+ }, {
60
+ type: "task" | "reference" | "decision" | "knowledge" | "discovery" | "preference";
61
+ what: string;
62
+ topic: string;
63
+ authority: "user_confirmed" | "assistant_proposed" | "assistant_claimed";
64
+ }>, "many">;
65
+ should_not_record: z.ZodArray<z.ZodObject<{
66
+ what: z.ZodString;
67
+ reason: z.ZodOptional<z.ZodString>;
68
+ }, "strip", z.ZodTypeAny, {
69
+ what: string;
70
+ reason?: string | undefined;
71
+ }, {
72
+ what: string;
73
+ reason?: string | undefined;
74
+ }>, "many">;
75
+ }, "strip", z.ZodTypeAny, {
76
+ session_ref: string;
77
+ should_record: {
78
+ type: "task" | "reference" | "decision" | "knowledge" | "discovery" | "preference";
79
+ what: string;
80
+ topic: string;
81
+ authority: "user_confirmed" | "assistant_proposed" | "assistant_claimed";
82
+ }[];
83
+ should_not_record: {
84
+ what: string;
85
+ reason?: string | undefined;
86
+ }[];
87
+ }, {
88
+ session_ref: string;
89
+ should_record: {
90
+ type: "task" | "reference" | "decision" | "knowledge" | "discovery" | "preference";
91
+ what: string;
92
+ topic: string;
93
+ authority: "user_confirmed" | "assistant_proposed" | "assistant_claimed";
94
+ }[];
95
+ should_not_record: {
96
+ what: string;
97
+ reason?: string | undefined;
98
+ }[];
99
+ }>;
100
+ export type GoldenAnnotation = z.infer<typeof GoldenAnnotationSchema>;
101
+ /**
102
+ * Load and validate a golden annotation from a JSON file on disk.
103
+ * Throws (with a descriptive message) if the file is missing, not valid JSON,
104
+ * or fails schema validation.
105
+ */
106
+ export declare function loadGolden(path: string): Promise<GoldenAnnotation>;
107
+ //# sourceMappingURL=golden.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"golden.d.ts","sourceRoot":"","sources":["../../src/eval/golden.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,eAAO,MAAM,sBAAsB,sFAOjC,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAGtE,eAAO,MAAM,qBAAqB,0EAIhC,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;EAK7B,CAAC;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D,eAAO,MAAM,qBAAqB;;;;;;;;;EAGhC,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAIjC,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE;;;;GAIG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAwBxE"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Golden annotation format: for a given session, the set of signals a human
3
+ * annotator judges the pipeline *should* have extracted (`should_record`), plus
4
+ * illustrative examples of things it should *not* have extracted
5
+ * (`should_not_record`) — e.g. transient chatter, unconfirmed proposals.
6
+ *
7
+ * This module only defines and validates the format. The actual human annotation
8
+ * of real sessions is a separate, manual step (see docs/eval-annotation-guide.md)
9
+ * — golden datasets built from real (potentially private) sessions are gitignored
10
+ * and kept locally; only a sanitized example fixture ships in the repo.
11
+ *
12
+ * Spec: memoark-2026-07-06-extraction-quality-redesign.md §10, §5 (signal shape).
13
+ */
14
+ import { readFile } from "node:fs/promises";
15
+ import { z } from "zod";
16
+ // Mirrors the signal `type` discriminant from the v4 distillation contract (spec §5).
17
+ // Kept as a local literal union here (rather than importing from the not-yet-built
18
+ // PR-2 distiller contract) so PR-1 has no dependency on PR-2.
19
+ export const GoldenSignalTypeSchema = z.enum([
20
+ "decision",
21
+ "task",
22
+ "reference",
23
+ "preference",
24
+ "knowledge",
25
+ "discovery",
26
+ ]);
27
+ // Mirrors the `authority` discriminant from spec §5.
28
+ export const GoldenAuthoritySchema = z.enum([
29
+ "user_confirmed",
30
+ "assistant_proposed",
31
+ "assistant_claimed",
32
+ ]);
33
+ export const GoldenSignalSchema = z.object({
34
+ type: GoldenSignalTypeSchema,
35
+ authority: GoldenAuthoritySchema,
36
+ topic: z.string().min(1),
37
+ what: z.string().min(1),
38
+ });
39
+ export const NegativeExampleSchema = z.object({
40
+ what: z.string().min(1),
41
+ reason: z.string().min(1).optional(),
42
+ });
43
+ export const GoldenAnnotationSchema = z.object({
44
+ session_ref: z.string().min(1),
45
+ should_record: z.array(GoldenSignalSchema),
46
+ should_not_record: z.array(NegativeExampleSchema),
47
+ });
48
+ /**
49
+ * Load and validate a golden annotation from a JSON file on disk.
50
+ * Throws (with a descriptive message) if the file is missing, not valid JSON,
51
+ * or fails schema validation.
52
+ */
53
+ export async function loadGolden(path) {
54
+ let raw;
55
+ try {
56
+ raw = await readFile(path, "utf-8");
57
+ }
58
+ catch (err) {
59
+ throw new Error(`Failed to read golden annotation at ${path}: ${err instanceof Error ? err.message : String(err)}`);
60
+ }
61
+ let parsed;
62
+ try {
63
+ parsed = JSON.parse(raw);
64
+ }
65
+ catch (err) {
66
+ throw new Error(`Failed to parse golden annotation at ${path} as JSON: ${err instanceof Error ? err.message : String(err)}`);
67
+ }
68
+ const result = GoldenAnnotationSchema.safeParse(parsed);
69
+ if (!result.success) {
70
+ throw new Error(`Invalid golden annotation at ${path}: ${result.error.message}`);
71
+ }
72
+ return result.data;
73
+ }
74
+ //# sourceMappingURL=golden.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"golden.js","sourceRoot":"","sources":["../../src/eval/golden.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,sFAAsF;AACtF,mFAAmF;AACnF,8DAA8D;AAC9D,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC,IAAI,CAAC;IAC3C,UAAU;IACV,MAAM;IACN,WAAW;IACX,YAAY;IACZ,WAAW;IACX,WAAW;CACZ,CAAC,CAAC;AAGH,qDAAqD;AACrD,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,IAAI,CAAC;IAC1C,gBAAgB;IAChB,oBAAoB;IACpB,mBAAmB;CACpB,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,IAAI,EAAE,sBAAsB;IAC5B,SAAS,EAAE,qBAAqB;IAChC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACxB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CACxB,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5C,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACvB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;CACrC,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC7C,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC9B,aAAa,EAAE,CAAC,CAAC,KAAK,CAAC,kBAAkB,CAAC;IAC1C,iBAAiB,EAAE,CAAC,CAAC,KAAK,CAAC,qBAAqB,CAAC;CAClD,CAAC,CAAC;AAGH;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAY;IAC3C,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACtC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CACb,uCAAuC,IAAI,KAAK,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CACnG,CAAC;IACJ,CAAC;IAED,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CACb,wCAAwC,IAAI,aAAa,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAC5G,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,sBAAsB,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IACxD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,gCAAgC,IAAI,KAAK,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;IACnF,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * LLM-as-judge semantic matcher: compares pipeline-extracted signals against a
3
+ * golden annotation for the same session and buckets each signal into
4
+ * matched / missed / extra.
5
+ *
6
+ * Matching rule (spec §10): same `type` AND semantically equivalent `what`.
7
+ * Type is checked programmatically (cheap, deterministic); semantic equivalence
8
+ * within a type is delegated to a JudgeClient, which in production wraps the
9
+ * existing LLMProvider abstraction (see createLLMJudgeClient below) so no new
10
+ * LLM dependency is introduced — the same MiniMax-backed provider used
11
+ * elsewhere in the codebase is reused here.
12
+ *
13
+ * The judge is calibrated against 5 hand-picked human judgment examples before
14
+ * being trusted on golden data — see CALIBRATION_EXAMPLES and calibrateJudge().
15
+ */
16
+ import type { LLMProvider } from "../extractors/providers/types.js";
17
+ import type { GoldenAnnotation, GoldenAuthority, GoldenSignal, GoldenSignalType } from "./golden.js";
18
+ export interface PipelineSignal {
19
+ type: GoldenSignalType;
20
+ topic: string;
21
+ what: string;
22
+ authority?: GoldenAuthority;
23
+ }
24
+ /**
25
+ * Minimal interface the judge depends on: "are these two descriptions of a
26
+ * signal semantically equivalent?" Kept narrow and injectable so tests can
27
+ * supply a deterministic mock instead of calling a real LLM.
28
+ */
29
+ export interface JudgeClient {
30
+ isEquivalent(a: string, b: string): Promise<boolean>;
31
+ }
32
+ export interface JudgeResult {
33
+ matched: Array<{
34
+ pipeline: PipelineSignal;
35
+ golden: GoldenSignal;
36
+ }>;
37
+ missed: GoldenSignal[];
38
+ extra: PipelineSignal[];
39
+ }
40
+ /**
41
+ * Compare pipeline output signals against a golden annotation.
42
+ *
43
+ * Algorithm: for each pipeline signal (in order), find the first not-yet-matched
44
+ * golden signal of the same `type` that the judge client considers semantically
45
+ * equivalent. Matched pairs are removed from further consideration (one pipeline
46
+ * signal matches at most one golden signal, and vice versa) — this prevents a
47
+ * single golden signal from being "matched" multiple times when the judge client
48
+ * is permissive.
49
+ */
50
+ export declare function judge(output: PipelineSignal[], golden: GoldenAnnotation, client: JudgeClient): Promise<JudgeResult>;
51
+ /**
52
+ * Five hand-picked (pipeline text, golden text, expectedEquivalent) examples used
53
+ * to calibrate a JudgeClient before trusting it on real golden data (spec §10:
54
+ * "judge 先用 5 个人工判例校准").
55
+ */
56
+ export declare const CALIBRATION_EXAMPLES: Array<{
57
+ a: string;
58
+ b: string;
59
+ expectedEquivalent: boolean;
60
+ }>;
61
+ /**
62
+ * Run a JudgeClient against CALIBRATION_EXAMPLES and report how many of the 5
63
+ * hand-labeled examples it got right. Callers should require a high pass rate
64
+ * (e.g. 5/5 or 4/5) before trusting the client for real evaluation runs.
65
+ */
66
+ export declare function calibrateJudge(client: JudgeClient): Promise<{
67
+ total: number;
68
+ correct: number;
69
+ failures: Array<{
70
+ a: string;
71
+ b: string;
72
+ expected: boolean;
73
+ actual: boolean;
74
+ }>;
75
+ }>;
76
+ /**
77
+ * Build a JudgeClient backed by the existing LLMProvider abstraction (the same
78
+ * one used by the extraction pipeline, configured for MiniMax elsewhere in
79
+ * config). No new LLM dependency is introduced.
80
+ */
81
+ export declare function createLLMJudgeClient(provider: LLMProvider): JudgeClient;
82
+ //# sourceMappingURL=judge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"judge.d.ts","sourceRoot":"","sources":["../../src/eval/judge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AACpE,OAAO,KAAK,EACV,gBAAgB,EAChB,eAAe,EACf,YAAY,EACZ,gBAAgB,EACjB,MAAM,aAAa,CAAC;AAErB,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,gBAAgB,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,eAAe,CAAC;CAC7B;AAED;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACtD;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,cAAc,CAAC;QAAC,MAAM,EAAE,YAAY,CAAA;KAAE,CAAC,CAAC;IACnE,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,KAAK,EAAE,cAAc,EAAE,CAAC;CACzB;AAED;;;;;;;;;GASG;AACH,wBAAsB,KAAK,CACzB,MAAM,EAAE,cAAc,EAAE,EACxB,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,WAAW,CAAC,CA4BtB;AAED;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,EAAE,KAAK,CAAC;IACvC,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,kBAAkB,EAAE,OAAO,CAAC;CAC7B,CA0BA,CAAC;AAEF;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC;IACjE,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,KAAK,CAAC;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CAC/E,CAAC,CAeD;AAQD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,WAAW,GAAG,WAAW,CAgBvE"}