@pcircle/memesh 4.8.5 → 4.9.0

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 (226) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.codex-plugin/mcp.json +6 -4
  4. package/.codex-plugin/plugin.json +1 -1
  5. package/AGENTS.md +60 -14
  6. package/README.de.md +83 -556
  7. package/README.md +83 -582
  8. package/README.zh-TW.md +84 -573
  9. package/dashboard/dist/index.html +10 -10
  10. package/dist/cli/view-live.d.ts.map +1 -1
  11. package/dist/cli/view-live.js +154 -384
  12. package/dist/cli/view-live.js.map +1 -1
  13. package/dist/core/agent-router.d.ts +11 -10
  14. package/dist/core/agent-router.d.ts.map +1 -1
  15. package/dist/core/agent-router.js +29 -14
  16. package/dist/core/agent-router.js.map +1 -1
  17. package/dist/core/agent-scope-id.d.ts.map +1 -1
  18. package/dist/core/agent-scope-id.js.map +1 -1
  19. package/dist/core/analytics.d.ts.map +1 -1
  20. package/dist/core/analytics.js.map +1 -1
  21. package/dist/core/config.d.ts +4 -40
  22. package/dist/core/config.d.ts.map +1 -1
  23. package/dist/core/config.js +75 -141
  24. package/dist/core/config.js.map +1 -1
  25. package/dist/core/demo.d.ts.map +1 -1
  26. package/dist/core/demo.js +6 -6
  27. package/dist/core/demo.js.map +1 -1
  28. package/dist/core/doctor.d.ts +1 -6
  29. package/dist/core/doctor.d.ts.map +1 -1
  30. package/dist/core/doctor.js +55 -227
  31. package/dist/core/doctor.js.map +1 -1
  32. package/dist/core/dreamer.d.ts +32 -47
  33. package/dist/core/dreamer.d.ts.map +1 -1
  34. package/dist/core/dreamer.js +211 -703
  35. package/dist/core/dreamer.js.map +1 -1
  36. package/dist/core/install-channel.d.ts.map +1 -1
  37. package/dist/core/install-channel.js +4 -47
  38. package/dist/core/install-channel.js.map +1 -1
  39. package/dist/core/install-id.d.ts.map +1 -1
  40. package/dist/core/install-id.js.map +1 -1
  41. package/dist/core/kg-backfill.d.ts.map +1 -1
  42. package/dist/core/kg-backfill.js.map +1 -1
  43. package/dist/core/lesson-engine.d.ts +0 -5
  44. package/dist/core/lesson-engine.d.ts.map +1 -1
  45. package/dist/core/lesson-engine.js +0 -25
  46. package/dist/core/lesson-engine.js.map +1 -1
  47. package/dist/core/lifecycle.d.ts.map +1 -1
  48. package/dist/core/lifecycle.js +54 -48
  49. package/dist/core/lifecycle.js.map +1 -1
  50. package/dist/core/memory-tool.d.ts.map +1 -1
  51. package/dist/core/memory-tool.js +17 -17
  52. package/dist/core/memory-tool.js.map +1 -1
  53. package/dist/core/operations.d.ts +2 -26
  54. package/dist/core/operations.d.ts.map +1 -1
  55. package/dist/core/operations.js +9 -244
  56. package/dist/core/operations.js.map +1 -1
  57. package/dist/core/paths.d.ts +4 -1
  58. package/dist/core/paths.d.ts.map +1 -1
  59. package/dist/core/paths.js +88 -14
  60. package/dist/core/paths.js.map +1 -1
  61. package/dist/core/product-improvements.js +2 -2
  62. package/dist/core/product-improvements.js.map +1 -1
  63. package/dist/core/schema-export.d.ts.map +1 -1
  64. package/dist/core/schema-export.js +10 -0
  65. package/dist/core/schema-export.js.map +1 -1
  66. package/dist/core/semver.d.ts +7 -0
  67. package/dist/core/semver.d.ts.map +1 -0
  68. package/dist/core/semver.js +49 -0
  69. package/dist/core/semver.js.map +1 -0
  70. package/dist/core/serializer.d.ts.map +1 -1
  71. package/dist/core/serializer.js +69 -57
  72. package/dist/core/serializer.js.map +1 -1
  73. package/dist/core/signal-scorer.d.ts.map +1 -1
  74. package/dist/core/signal-scorer.js.map +1 -1
  75. package/dist/core/transcript-extractor.d.ts +1 -85
  76. package/dist/core/transcript-extractor.d.ts.map +1 -1
  77. package/dist/core/transcript-extractor.js +5 -364
  78. package/dist/core/transcript-extractor.js.map +1 -1
  79. package/dist/core/transcript-source.d.ts +22 -6
  80. package/dist/core/transcript-source.d.ts.map +1 -1
  81. package/dist/core/transcript-source.js +108 -69
  82. package/dist/core/transcript-source.js.map +1 -1
  83. package/dist/core/types.d.ts +1 -17
  84. package/dist/core/types.d.ts.map +1 -1
  85. package/dist/core/version-check.d.ts.map +1 -1
  86. package/dist/core/version-check.js +11 -1
  87. package/dist/core/version-check.js.map +1 -1
  88. package/dist/db.d.ts +0 -34
  89. package/dist/db.d.ts.map +1 -1
  90. package/dist/db.js +3 -287
  91. package/dist/db.js.map +1 -1
  92. package/dist/host-runtime/acp.d.ts.map +1 -1
  93. package/dist/host-runtime/acp.js +2 -2
  94. package/dist/host-runtime/acp.js.map +1 -1
  95. package/dist/host-runtime/claude.d.ts.map +1 -1
  96. package/dist/host-runtime/claude.js +9 -7
  97. package/dist/host-runtime/claude.js.map +1 -1
  98. package/dist/host-runtime/codex-session.d.ts +9 -1
  99. package/dist/host-runtime/codex-session.d.ts.map +1 -1
  100. package/dist/host-runtime/codex-session.js +474 -29
  101. package/dist/host-runtime/codex-session.js.map +1 -1
  102. package/dist/host-runtime/codex.d.ts.map +1 -1
  103. package/dist/host-runtime/codex.js +2 -2
  104. package/dist/host-runtime/codex.js.map +1 -1
  105. package/dist/host-runtime/config.d.ts +1 -0
  106. package/dist/host-runtime/config.d.ts.map +1 -1
  107. package/dist/host-runtime/config.js +4 -0
  108. package/dist/host-runtime/config.js.map +1 -1
  109. package/dist/host-runtime/router-client.d.ts.map +1 -1
  110. package/dist/host-runtime/router-client.js +62 -15
  111. package/dist/host-runtime/router-client.js.map +1 -1
  112. package/dist/host-runtime/router.js +2 -2
  113. package/dist/host-runtime/router.js.map +1 -1
  114. package/dist/knowledge-graph.d.ts.map +1 -1
  115. package/dist/knowledge-graph.js +54 -45
  116. package/dist/knowledge-graph.js.map +1 -1
  117. package/dist/mcp/THIRD_PARTY_NOTICES.txt +217 -0
  118. package/dist/mcp/server.js +30685 -38
  119. package/dist/mcp/server.js.map +6 -1
  120. package/dist/skills-manifest.json +22 -22
  121. package/dist/storage/entity-index.d.ts +0 -1
  122. package/dist/storage/entity-index.d.ts.map +1 -1
  123. package/dist/storage/entity-index.js +0 -7
  124. package/dist/storage/entity-index.js.map +1 -1
  125. package/dist/storage/fts-index.d.ts.map +1 -1
  126. package/dist/storage/fts-index.js +6 -7
  127. package/dist/storage/fts-index.js.map +1 -1
  128. package/dist/storage/graph-repairs.d.ts +0 -5
  129. package/dist/storage/graph-repairs.d.ts.map +1 -1
  130. package/dist/storage/graph-repairs.js +4 -91
  131. package/dist/storage/graph-repairs.js.map +1 -1
  132. package/dist/storage/schema.d.ts +1 -1
  133. package/dist/storage/schema.d.ts.map +1 -1
  134. package/dist/storage/schema.js +1 -2
  135. package/dist/storage/schema.js.map +1 -1
  136. package/dist/storage/sqlite.d.ts +0 -1
  137. package/dist/storage/sqlite.d.ts.map +1 -1
  138. package/dist/storage/sqlite.js.map +1 -1
  139. package/dist/transports/agent-messaging.d.ts.map +1 -1
  140. package/dist/transports/agent-messaging.js +5 -7
  141. package/dist/transports/agent-messaging.js.map +1 -1
  142. package/dist/transports/cli/cli.d.ts.map +1 -1
  143. package/dist/transports/cli/cli.js +52 -674
  144. package/dist/transports/cli/cli.js.map +1 -1
  145. package/dist/transports/http/retired-routes.js +1 -1
  146. package/dist/transports/http/retired-routes.js.map +1 -1
  147. package/dist/transports/http/server.d.ts.map +1 -1
  148. package/dist/transports/http/server.js +13 -232
  149. package/dist/transports/http/server.js.map +1 -1
  150. package/dist/transports/mcp/handlers.d.ts +127 -1
  151. package/dist/transports/mcp/handlers.d.ts.map +1 -1
  152. package/dist/transports/mcp/handlers.js +53 -4
  153. package/dist/transports/mcp/handlers.js.map +1 -1
  154. package/dist/transports/schemas.d.ts +52 -0
  155. package/dist/transports/schemas.d.ts.map +1 -1
  156. package/dist/transports/schemas.js +39 -0
  157. package/dist/transports/schemas.js.map +1 -1
  158. package/docs/platforms/README.md +5 -4
  159. package/docs/platforms/agent-messaging.md +124 -60
  160. package/hooks/hooks.json +13 -2
  161. package/llms-install.md +62 -30
  162. package/package.json +4 -5
  163. package/scripts/hooks/_generated/core-paths.js +88 -14
  164. package/scripts/hooks/_generated/fts-index.js +6 -7
  165. package/scripts/hooks/_generated/schema.js +1 -2
  166. package/scripts/hooks/_shared.js +14 -20
  167. package/scripts/hooks/session-start.js +8 -6
  168. package/scripts/hooks/session-summary.js +19 -374
  169. package/skills/memesh/SKILL.md +19 -14
  170. package/skills/memesh-review/SKILL.md +7 -6
  171. package/dist/core/auto-tagger.d.ts +0 -10
  172. package/dist/core/auto-tagger.d.ts.map +0 -1
  173. package/dist/core/auto-tagger.js +0 -63
  174. package/dist/core/auto-tagger.js.map +0 -1
  175. package/dist/core/conflict-candidates.d.ts +0 -20
  176. package/dist/core/conflict-candidates.d.ts.map +0 -1
  177. package/dist/core/conflict-candidates.js +0 -71
  178. package/dist/core/conflict-candidates.js.map +0 -1
  179. package/dist/core/conflict-judge.d.ts +0 -58
  180. package/dist/core/conflict-judge.d.ts.map +0 -1
  181. package/dist/core/conflict-judge.js +0 -189
  182. package/dist/core/conflict-judge.js.map +0 -1
  183. package/dist/core/digest-validator.d.ts +0 -18
  184. package/dist/core/digest-validator.d.ts.map +0 -1
  185. package/dist/core/digest-validator.js +0 -85
  186. package/dist/core/digest-validator.js.map +0 -1
  187. package/dist/core/embedder.d.ts +0 -20
  188. package/dist/core/embedder.d.ts.map +0 -1
  189. package/dist/core/embedder.js +0 -245
  190. package/dist/core/embedder.js.map +0 -1
  191. package/dist/core/failure-analyzer.d.ts +0 -19
  192. package/dist/core/failure-analyzer.d.ts.map +0 -1
  193. package/dist/core/failure-analyzer.js +0 -83
  194. package/dist/core/failure-analyzer.js.map +0 -1
  195. package/dist/core/json-utils.d.ts +0 -3
  196. package/dist/core/json-utils.d.ts.map +0 -1
  197. package/dist/core/json-utils.js +0 -46
  198. package/dist/core/json-utils.js.map +0 -1
  199. package/dist/core/llm-client.d.ts +0 -22
  200. package/dist/core/llm-client.d.ts.map +0 -1
  201. package/dist/core/llm-client.js +0 -203
  202. package/dist/core/llm-client.js.map +0 -1
  203. package/dist/core/llm-telemetry.d.ts +0 -47
  204. package/dist/core/llm-telemetry.d.ts.map +0 -1
  205. package/dist/core/llm-telemetry.js +0 -117
  206. package/dist/core/llm-telemetry.js.map +0 -1
  207. package/dist/core/llm-validator.d.ts +0 -20
  208. package/dist/core/llm-validator.d.ts.map +0 -1
  209. package/dist/core/llm-validator.js +0 -231
  210. package/dist/core/llm-validator.js.map +0 -1
  211. package/dist/core/ollama-host.d.ts +0 -6
  212. package/dist/core/ollama-host.d.ts.map +0 -1
  213. package/dist/core/ollama-host.js +0 -62
  214. package/dist/core/ollama-host.js.map +0 -1
  215. package/dist/core/output-language.d.ts +0 -6
  216. package/dist/core/output-language.d.ts.map +0 -1
  217. package/dist/core/output-language.js +0 -25
  218. package/dist/core/output-language.js.map +0 -1
  219. package/dist/core/prompt-safety.d.ts +0 -4
  220. package/dist/core/prompt-safety.d.ts.map +0 -1
  221. package/dist/core/prompt-safety.js +0 -20
  222. package/dist/core/prompt-safety.js.map +0 -1
  223. package/dist/storage/vector-index.d.ts +0 -3
  224. package/dist/storage/vector-index.d.ts.map +0 -1
  225. package/dist/storage/vector-index.js +0 -13
  226. package/dist/storage/vector-index.js.map +0 -1
@@ -4,26 +4,9 @@
4
4
  // Extracts knowledge from completed Claude Code sessions
5
5
  // and stores as session-insight entities in MeMesh.
6
6
  //
7
- // THIS HOOK STAYS, and that was an open question rather than an assumption.
8
- // `dream run --from-transcripts` reads the same sessions from their raw JSONL
9
- // and does not depend on this hook having fired, so the obvious next step was
10
- // to retire the hook as redundant. Measured first, on a real graph
11
- // (2026-08-09, 214 active entities, ollama nomic-embed-text, L2 over the same
12
- // `name + observations` text the runtime embeds):
13
- //
14
- // every transcript-mined memory -> its nearest hook-captured entity
15
- // min 0.784 p25 0.821 p50 0.865 max 0.946
16
- // within 0.55: 0 of 47. within 0.70: 0 of 47.
17
- //
18
- // Nothing the transcript miner produced came within 0.78 of anything this hook
19
- // recorded. They are not two views of the same material: this hook records what
20
- // HAPPENED (files touched, commands run, commits), the miner extracts what was
21
- // DECIDED and what was LEARNED. Retiring either one loses a whole category.
22
-
23
7
  import { createRequire } from 'module';
24
8
  import { basename, join } from 'path';
25
- import { existsSync, readFileSync, writeFileSync, mkdirSync, appendFileSync } from 'fs';
26
- import { spawn } from 'child_process';
9
+ import { existsSync, readFileSync } from 'fs';
27
10
  import { pathToFileURL } from 'url';
28
11
  import {
29
12
  AUTO_CAPTURE_TAG,
@@ -32,7 +15,6 @@ import {
32
15
  extractCitedMemoryIds,
33
16
  getMemeshDirFromDbPath,
34
17
  getProjectName,
35
- importFromPluginRoot,
36
18
  isAutoCaptureEnabled,
37
19
  openHookDb,
38
20
  readUpdateCheckCache,
@@ -47,24 +29,17 @@ import {
47
29
 
48
30
  const require = createRequire(import.meta.url);
49
31
 
50
- // Pre-load dist/core/install-channel.js for auto-update channel detection.
51
- // Same pattern as session-start.js: async ESM import at process init,
52
- // falls back to null if dist is missing (source checkout pre-build).
53
- let _installChannelMod = null;
32
+ let installChannel = null;
54
33
  try {
55
- const _pluginRootForInit = resolvePluginRoot(import.meta.url);
56
- const _modPath = join(_pluginRootForInit, 'dist/core/install-channel.js');
57
- if (existsSync(_modPath)) {
58
- _installChannelMod = await import(pathToFileURL(_modPath).href);
34
+ const pluginRoot = resolvePluginRoot(import.meta.url);
35
+ const modulePath = join(pluginRoot, 'dist/core/install-channel.js');
36
+ if (existsSync(modulePath)) {
37
+ installChannel = await import(pathToFileURL(modulePath).href);
59
38
  }
60
- } catch { /* best-effort */ }
39
+ } catch {
40
+ // Best-effort: source checkouts may not have built dist output yet.
41
+ }
61
42
 
62
- /**
63
- * Run auto-update at Stop hook: reads cache, evaluates policy, and dispatches
64
- * the detached updater runner if warranted. Runs after all session work
65
- * completes, avoiding the TOCTOU race where install would overwrite dist/
66
- * mid-session.
67
- */
68
43
  async function runAutoUpdateAtStop() {
69
44
  try {
70
45
  const pluginRoot = resolvePluginRoot(import.meta.url);
@@ -75,11 +50,9 @@ async function runAutoUpdateAtStop() {
75
50
  const cache = readUpdateCheckCache(installedVersion);
76
51
  const policy = resolveAutoUpdatePolicy(process.env);
77
52
  const decision = decideAutoUpdateHook(installedVersion, cache, policy);
78
- if (decision.run) {
79
- await spawnAutoUpdate(decision.latest, _installChannelMod);
80
- }
53
+ if (decision.run) await spawnAutoUpdate(decision.latest, installChannel);
81
54
  } catch {
82
- // Best-effort never crash the hook.
55
+ // Best-effort: update failures must never break session capture.
83
56
  }
84
57
  }
85
58
 
@@ -183,13 +156,9 @@ function parseTranscript(transcriptPath) {
183
156
  const text = typeof block.content === 'string'
184
157
  ? block.content
185
158
  : JSON.stringify(block.content);
186
- // Same reason as the bash branch, and one more: this array is
187
- // ALSO the payload `analyzeFailure` sends to the configured LLM
188
- // provider. A failed request that echoes its own Authorization
189
- // header — the ordinary shape of an auth error — would be stored
190
- // and then transmitted off the machine. Redacted once here, at
191
- // the point the text enters the process, so every downstream use
192
- // inherits it.
159
+ // Same reason as the bash branch: a failed request may echo its
160
+ // own Authorization header. Redact once, where text enters the
161
+ // process, so every downstream use inherits it.
193
162
  errorsEncountered.push(redactSecrets(text).slice(0, 200));
194
163
  }
195
164
  }
@@ -338,33 +307,12 @@ process.stdin.on('end', async () => {
338
307
  // healthy exit, so it MUST stamp (see stampHookRunOnly).
339
308
  if (toolCallCount < 3) { stampHookRunOnly(process.env, 'session-summary'); return exit0(); }
340
309
 
341
- // Hoisted to outer-try scope so the LLM failure-analysis block
342
- // below (which runs AFTER db.close()) can reference it. Earlier
343
- // version defined projectName inside the inner try-finally and the
344
- // LLM path threw `projectName is not defined` silently — caught by
345
- // the LLM try/catch but logged to stderr. Result: lesson_learned
346
- // creation never actually happened in production.
347
310
  const projectName = getProjectName(cwd);
348
311
 
349
312
  // Open DB via shared helper — applies SCHEMA_SQL + status migration.
350
313
  // { fts: true } guarantees the entities_fts table exists so captureEntity()
351
314
  // can keep it in sync — session-insight memories must be FTS-recallable.
352
315
  //
353
- // sqlite-vec is NOT loaded here, and used to be. The comment said it was
354
- // needed "for embedding-aware recall-effectiveness tracking" — but this
355
- // hook runs exactly two statements, `PRAGMA table_info(entities)` and
356
- // `SELECT id FROM entities WHERE name = ?`, and `captureEntity` in
357
- // _shared.js touches no vectors either. Nothing here has ever used the
358
- // extension.
359
- //
360
- // It was not free. sqlite-vec ships its engine as a per-platform file
361
- // through optionalDependencies, and on a platform it does not publish the
362
- // load threw — past the `require` guard, which never fired because the JS
363
- // wrapper resolves fine and the throw happens later inside
364
- // `sqliteVec.load()`. Measured with the platform binary hidden: the whole
365
- // Stop capture vanished (0 entities against a control run's 1) and the
366
- // user got a `Require stack:` dump on stderr. An extension nobody calls
367
- // was silently costing every session on those platforms its memory.
368
316
  const { db } = openHookDb(process.env, { fts: true });
369
317
  let writeFailed = false;
370
318
  try {
@@ -628,68 +576,17 @@ process.stdin.on('end', async () => {
628
576
  db.close();
629
577
  }
630
578
 
631
- // ── LLM-powered failure analysis (Level 1 only) ──────────────────────
632
- // Runs AFTER the hook's own DB is closed.
633
- // Uses the core module's DB singleton (openDatabase/closeDatabase).
634
- // Wrapped in its own try/catch — never blocks rule-based extraction.
635
- if (errorsEncountered.length > 0 && filesEdited.length > 0) {
636
- try {
637
- // F5: derive pluginRoot strictly from this file's location.
638
- // See `resolvePluginRoot` for the full reasoning.
639
- const pluginRoot = resolvePluginRoot(import.meta.url);
640
- const configMod = await importFromPluginRoot(pluginRoot, 'dist/core/config.js');
641
- const config = configMod.readConfig();
642
-
643
- if (config.llm) {
644
- const { openDatabase, closeDatabase } = await importFromPluginRoot(pluginRoot, 'dist/db.js');
645
- const { analyzeFailure } = await importFromPluginRoot(pluginRoot, 'dist/core/failure-analyzer.js');
646
- const { createLesson } = await importFromPluginRoot(pluginRoot, 'dist/core/lesson-engine.js');
647
-
648
- openDatabase();
649
- try {
650
- // Pass cross-provider failover chain so a stale Anthropic key
651
- // doesn't silently disable Stop-hook lesson generation.
652
- const lesson = await analyzeFailure(errorsEncountered, filesEdited, config.llm, { fallbacks: config.llmFallbacks });
653
- if (lesson) {
654
- createLesson(lesson, projectName);
655
- }
656
- } finally {
657
- closeDatabase();
658
- }
659
- }
660
- } catch (llmErr) {
661
- // LLM analysis failed — rule-based extraction already captured the session.
662
- // Log to stderr so config issues (e.g. invalid API key) are visible.
663
- try { process.stderr.write(`[memesh] LLM failure analysis skipped: ${llmErr?.message || llmErr}\n`); } catch {}
664
- }
665
- }
666
-
667
- // Auto-trigger dream — solves the "Insights tab is empty for users
668
- // who don't know `memesh dream run` exists" problem. Throttled to
669
- // once per project per 24h, gated by minimum activity threshold.
670
- // Background-detached spawn so the hook exits immediately even if
671
- // the LLM call takes 30-60s. See `maybeTriggerDream` for the gate
672
- // logic and dream-history.json schema.
673
- try {
674
- const pluginRoot = resolvePluginRoot(import.meta.url);
675
- const configMod = await importFromPluginRoot(pluginRoot, 'dist/core/config.js');
676
- const config = configMod.readConfig();
677
- maybeTriggerDream(projectName, config, pluginRoot);
678
- } catch (dreamErr) {
679
- try { process.stderr.write(`[memesh] dream auto-trigger skipped: ${dreamErr?.message || dreamErr}\n`); } catch {}
680
- }
681
579
  } catch (err) {
682
580
  // Never crash Claude Code — leave a trace for debugging.
683
581
  //
684
- // Every error is traced now. There used to be a suppression branch for a
685
- // `skip-session-capture:` sentinel, thrown when sqlite-vec was missing
686
- // an extension this hook never used. The thrower is gone, so the branch
687
- // could only ever hide a real error from here on.
582
+ // Every error is traced. A retired suppression sentinel once hid setup
583
+ // failures from this hook; with that branch gone, real capture errors stay
584
+ // visible without crashing the host session.
688
585
  try { process.stderr.write(`[memesh session-summary] ${err?.message || err}\n`); } catch {}
689
586
  }
690
587
 
691
- // Dispatch auto-update if policy + cache permit. Runs after all session work
692
- // so the runner cannot overwrite dist/ while peer hooks are reading it.
588
+ // Update only after all session work so installed files cannot change while
589
+ // this hook is still reading them.
693
590
  await runAutoUpdateAtStop();
694
591
 
695
592
  // Emit NOTHING on success — not `{"suppressOutput": true}`.
@@ -711,173 +608,6 @@ process.stdin.on('end', async () => {
711
608
  function exit0() {
712
609
  process.exit(0);
713
610
  }
714
-
715
- // =============================================================================
716
- // Dream auto-trigger (Phase 2 / Phase 3 background runner)
717
- // =============================================================================
718
- //
719
- // Without an automated trigger, `memesh dream` only runs when the user
720
- // types it into a terminal — and most users never read the docs that
721
- // far. Result: Insights tab stays empty and the KG accumulates 89.7%
722
- // orphan rate (the maintainer's own observation on this DB).
723
- //
724
- // This trigger fires at the END of every Stop hook (after rule-based
725
- // session capture + optional LLM failure analysis). It:
726
- // 1. Loads ~/.memesh/dream-history.json — a per-project record of
727
- // the last dream run timestamp + outcome.
728
- // 2. Throttles: skip if < THROTTLE_HOURS since the project's last
729
- // run, even if the previous run produced zero proposals.
730
- // 3. Activity gate: skip if the project has < MIN_EPISODIC episodic
731
- // entities to draw from in the last WINDOW_DAYS days.
732
- // 4. LLM gate: skip if no LLM provider configured (Phase 2 needs
733
- // Smart Mode — Phase 3 patterns same).
734
- // 5. Spawns `node <pluginRoot>/dist/transports/cli/cli.js dream run
735
- // --project <name> --max-llm-calls 2 --window-days 14` as a
736
- // detached child process. Stdout/stderr go to a per-project log
737
- // under ~/.memesh/dream-runs/ so the user can `tail -f` to see
738
- // progress without the hook blocking.
739
- // 6. Records the run start in dream-history.json BEFORE spawning so
740
- // a long-running spawn doesn't get re-triggered on the next
741
- // Stop within the same window.
742
- //
743
- // The spawned process inherits config from disk, including the
744
- // `llmFallbacks` chain wired in commit 883abd4d, so a primary
745
- // outage falls through to Ollama automatically.
746
-
747
- const DREAM_THROTTLE_HOURS = 24;
748
- const DREAM_MIN_EPISODIC = 10;
749
- const DREAM_WINDOW_DAYS = 14;
750
- const DREAM_MAX_LLM_CALLS = 2;
751
- const DREAM_HISTORY_BASENAME = 'dream-history.json';
752
- const DREAM_LOG_DIRNAME = 'dream-runs';
753
-
754
- const DREAM_EPISODIC_TYPES = [
755
- 'commit',
756
- 'session_keypoint',
757
- 'session-insight',
758
- 'workflow_checkpoint',
759
- 'weekly-summary',
760
- 'weekly_summary',
761
- ];
762
-
763
- // Debug-trace gate. Set MEMESH_DREAM_TRIGGER_DEBUG=1 to emit a stderr
764
- // breadcrumb at every gate decision in maybeTriggerDream. Contract:
765
- // [memesh dream-trigger] <stage>=<value>...
766
- // Matches one line per stage (resolve, llm-gate, throttle-gate,
767
- // activity-gate, history-write, spawn). Stable across releases — if
768
- // you rename a stage, update the matching test fixture too.
769
- function dreamTrigTrace(stage, fields) {
770
- if (process.env.MEMESH_DREAM_TRIGGER_DEBUG !== '1') return;
771
- try {
772
- const parts = Object.entries(fields || {}).map(([k, v]) => {
773
- if (v === undefined) return `${k}=<undef>`;
774
- if (v === null) return `${k}=<null>`;
775
- return `${k}=${typeof v === 'string' ? v : JSON.stringify(v)}`;
776
- });
777
- process.stderr.write(`[memesh dream-trigger] ${stage} ${parts.join(' ')}\n`);
778
- } catch {}
779
- }
780
-
781
- function dreamHistoryPath() {
782
- // Test isolation: tests set MEMESH_DB_PATH to a tmp file, expecting
783
- // sibling state (history, logs) to land beside it. Without honouring
784
- // that, real-home `~/.memesh/dream-history.json` would be polluted
785
- // by every test run. Same precedence as memesh's other state files —
786
- // see `getMemeshDirFromDbPath` in `_shared.js`.
787
- //
788
- // Windows note: on Windows under execFileSync, env vars propagate as
789
- // plain strings (no canonicalisation). If a caller passed MEMESH_DIR
790
- // with mixed separators (e.g. `C:/Users/.../tmp/.memesh`), join() will
791
- // happily mix `/` and `\`. Node fs accepts either — the only divergence
792
- // is in pure string comparisons, which we don't do here. The trace
793
- // below shows the resolved value verbatim so a Windows diagnosis run
794
- // can confirm what actually arrived.
795
- // The helper IS the precedence (MEMESH_DB_PATH > MEMESH_DIR > home).
796
- // A hand-rolled version here inverted it (MEMESH_DIR won over
797
- // MEMESH_DB_PATH), so with both set, dream history landed in a different
798
- // directory than every sibling state file — plus a dead home-fallback
799
- // branch, since the helper always returns a string.
800
- const dir = getMemeshDirFromDbPath();
801
- dreamTrigTrace('resolve', {
802
- src: process.env.MEMESH_DB_PATH ? 'db-path' : (process.env.MEMESH_DIR ? 'env' : 'home'),
803
- MEMESH_DIR: process.env.MEMESH_DIR,
804
- MEMESH_DB_PATH: process.env.MEMESH_DB_PATH,
805
- dir,
806
- platform: process.platform,
807
- });
808
- return { dir, historyFile: join(dir, DREAM_HISTORY_BASENAME), logDir: join(dir, DREAM_LOG_DIRNAME) };
809
- }
810
-
811
- function readDreamHistory() {
812
- try {
813
- const { historyFile } = dreamHistoryPath();
814
- if (!existsSync(historyFile)) return {};
815
- const raw = readFileSync(historyFile, 'utf8');
816
- const parsed = JSON.parse(raw);
817
- return (parsed && typeof parsed === 'object') ? parsed : {};
818
- } catch { return {}; }
819
- }
820
-
821
- function writeDreamHistory(history) {
822
- try {
823
- const { dir, historyFile } = dreamHistoryPath();
824
- mkdirSync(dir, { recursive: true });
825
- writeFileSync(historyFile, JSON.stringify(history, null, 2));
826
- dreamTrigTrace('history-write', { historyFile, ok: true, projects: Object.keys(history) });
827
- } catch (err) {
828
- dreamTrigTrace('history-write', { ok: false, err: err?.message || String(err) });
829
- try { process.stderr.write(`[memesh dream-history write] ${err?.message || err}\n`); } catch {}
830
- }
831
- }
832
-
833
- /**
834
- * Count episodic entities for the given project over the configured
835
- * window. Only issues a SELECT, but the handle is openHookDb's ordinary
836
- * read-write one (it runs the schema/migration chain) — there is no
837
- * read-only variant on the hook side, and this call site must not stamp
838
- * the heartbeat (recordHookRun is per-hook-exit, never per-open).
839
- */
840
- function countEpisodicEntities(projectName) {
841
- let handle;
842
- try {
843
- handle = openHookDb();
844
- // { db, dbPath } — don't call db.prepare() on the wrapper itself (an
845
- // earlier version did, and silently skipped the gate, defeating the
846
- // trigger's whole purpose).
847
- const db = handle.db;
848
- const since = new Date(Date.now() - DREAM_WINDOW_DAYS * 86400000).toISOString();
849
- const types = DREAM_EPISODIC_TYPES.map(() => '?').join(',');
850
- // Project-membership is determined by the `project:<name>` tag only.
851
- // The previous implementation also OR'd on `e.name LIKE 'project-%'`
852
- // as a fallback for legacy entities, but that branch over-counts when
853
- // two projects share a name prefix (e.g. counting `memesh` would
854
- // sweep in `memesh-cloud-keypoint-*` entities). Post-v3.0 episodic
855
- // entities all carry the project tag — the auto-tagger writes it on
856
- // every session_keypoint / commit / session_insight / session_lesson
857
- // / failure_pattern / decision_anchor — so the tag-only path is the
858
- // accurate signal. See v4.2.1 CHANGELOG known-limitations note.
859
- const sql = `SELECT COUNT(DISTINCT e.id) AS n
860
- FROM entities e
861
- INNER JOIN tags t ON t.entity_id = e.id AND t.tag = ?
862
- WHERE e.status = 'active'
863
- AND e.type IN (${types})
864
- AND e.created_at >= ?`;
865
- const projectTag = `project:${projectName}`;
866
- const row = db.prepare(sql).get(projectTag, ...DREAM_EPISODIC_TYPES, since);
867
- return row?.n ?? 0;
868
- } catch (err) {
869
- try { process.stderr.write(`[memesh dream-trigger count] ${err?.message || err}\n`); } catch {}
870
- return 0;
871
- } finally {
872
- try { handle?.db?.close(); } catch {}
873
- }
874
- }
875
-
876
- /**
877
- * Decide whether to fire dream for `projectName` and, if so, spawn
878
- * the detached background runner. Pure side effect — no return value
879
- * used by callers.
880
- */
881
611
  /**
882
612
  * Attachment record types Claude Code uses to persist a hook's own output
883
613
  * into the transcript. Anything memesh injected reaches the transcript
@@ -927,88 +657,3 @@ export function stripHookEchoes(rawTranscript) {
927
657
  }
928
658
  return kept.join('\n');
929
659
  }
930
-
931
- export function maybeTriggerDream(projectName, config, pluginRoot) {
932
- dreamTrigTrace('enter', { projectName, hasLlm: Boolean(config?.llm) });
933
- if (!projectName || projectName === 'unknown') {
934
- dreamTrigTrace('exit', { reason: 'no-project-name', projectName });
935
- return;
936
- }
937
-
938
- // Phase 2 & 3 both need a configured LLM. Without it the dreamer's
939
- // first action is to push `{reason: 'no LLM configured'}` into
940
- // skipped[] and exit, which would still bump our throttle clock
941
- // for no value — so gate here.
942
- if (!config?.llm) {
943
- dreamTrigTrace('exit', { reason: 'llm-gate-fail' });
944
- return;
945
- }
946
- dreamTrigTrace('llm-gate', { ok: true });
947
-
948
- const history = readDreamHistory();
949
- const last = history[projectName];
950
- if (last?.last_run_iso) {
951
- const ageMs = Date.now() - new Date(last.last_run_iso).getTime();
952
- if (Number.isFinite(ageMs) && ageMs < DREAM_THROTTLE_HOURS * 3600 * 1000) {
953
- dreamTrigTrace('exit', { reason: 'throttle-gate-fail', ageMs, last_run_iso: last.last_run_iso });
954
- return; // throttled
955
- }
956
- }
957
- dreamTrigTrace('throttle-gate', { ok: true, prior_last_run_iso: last?.last_run_iso });
958
-
959
- const episodicCount = countEpisodicEntities(projectName);
960
- if (episodicCount < DREAM_MIN_EPISODIC) {
961
- dreamTrigTrace('exit', { reason: 'activity-gate-fail', episodicCount, threshold: DREAM_MIN_EPISODIC });
962
- return;
963
- }
964
- dreamTrigTrace('activity-gate', { ok: true, episodicCount });
965
-
966
- // Activity gate passed — record start BEFORE spawning so we don't
967
- // re-trigger on any subsequent Stop within the window even if the
968
- // child takes 30-60s.
969
- history[projectName] = {
970
- last_run_iso: new Date().toISOString(),
971
- last_episodic_count: episodicCount,
972
- last_window_days: DREAM_WINDOW_DAYS,
973
- };
974
- writeDreamHistory(history);
975
-
976
- // Spawn detached so the hook exits immediately. Stdio routes to a
977
- // per-project log file so the user can inspect dream output without
978
- // the hook blocking on the LLM call.
979
- try {
980
- const { dir, logDir } = dreamHistoryPath();
981
- mkdirSync(logDir, { recursive: true });
982
- const safeProj = projectName.replace(/[^A-Za-z0-9_-]/g, '_').slice(0, 64);
983
- const ts = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
984
- const logFile = join(logDir, `${safeProj}-${ts}.log`);
985
-
986
- // Append a header so a tail -f shows context for the run.
987
- appendFileSync(logFile, `[memesh dream] ${new Date().toISOString()} project=${projectName} episodic_count=${episodicCount} window=${DREAM_WINDOW_DAYS}d max_llm_calls=${DREAM_MAX_LLM_CALLS}\n`);
988
-
989
- const cliPath = join(pluginRoot, 'dist/transports/cli/cli.js');
990
- if (!existsSync(cliPath)) {
991
- appendFileSync(logFile, `[memesh dream] cli.js missing at ${cliPath}, skipping\n`);
992
- return;
993
- }
994
-
995
- const args = [
996
- cliPath, 'dream', 'run',
997
- '--project', projectName,
998
- '--max-llm-calls', String(DREAM_MAX_LLM_CALLS),
999
- '--window-days', String(DREAM_WINDOW_DAYS),
1000
- ];
1001
-
1002
- const logFd = require('fs').openSync(logFile, 'a');
1003
- const child = spawn(process.execPath, args, {
1004
- detached: true,
1005
- stdio: ['ignore', logFd, logFd],
1006
- env: { ...process.env, MEMESH_DIR: dir },
1007
- });
1008
- child.unref();
1009
- dreamTrigTrace('spawn', { ok: true, pid: child.pid, logFile, MEMESH_DIR: dir });
1010
- } catch (err) {
1011
- dreamTrigTrace('spawn', { ok: false, err: err?.message || String(err) });
1012
- try { process.stderr.write(`[memesh dream-trigger spawn] ${err?.message || err}\n`); } catch {}
1013
- }
1014
- }
@@ -23,10 +23,11 @@ Persistent memory for AI agents. The point is continuity: the next session start
23
23
 
24
24
  All examples below use CLI. MCP tools accept the same parameters as JSON objects.
25
25
 
26
- ## All 11 MCP tools
26
+ ## All 12 MCP tools
27
27
 
28
28
  | Tool | Purpose |
29
29
  |---|---|
30
+ | `work_package` | Prepare one bounded untrusted `digest` (calendar cluster) or `transcript` package from the newest Claude Code session under the client's single matching MCP workspace root; submit exactly one strict result or defer. Submit only stages pending human review and retains bounded redacted source turns for comparison; agents cannot apply or reject. No hidden reasoning, raw transcript, transcript path, API key, LLM, embedding, or vector data is exposed or used; hashes identify freshness and workspace scope rather than authentication. |
30
31
  | `remember` | Store knowledge as an entity with observations, tags, and relations |
31
32
  | `recall` | Search stored knowledge; empty query lists recent memories |
32
33
  | `forget` | Archive an entity or remove one exact observation |
@@ -86,7 +87,12 @@ Use the routing and identity fields returned by `fetch`. A reply has this shape
86
87
  }
87
88
  ```
88
89
 
89
- An active compatible managed host can receive a native push, which removes polling for that live delivery. One-time provider enablement and a MeMesh-managed Codex app-server or Claude Channel may be required; ordinary unattached sessions are presence-only/inbound-unavailable. The bundled Gemini ACP adapter is experimental protocol-development code, not a release-gated native-wakeup provider. Adapter imports and a live router socket do not prove host registration or `host_accept`. Do not promise that a stopped, missing, or replaced session will wake up: it is not resumed or silently rerouted. Use the stable principal for logical routing, and an exact session/generation only when delivery must not move to a replacement connection. Local owns durable storage and host-native delivery; Cloud relay, A2A, SSE, discovery, or fetch is not host delivery.
90
+ For a compatible managed host, native delivery removes polling from the inbound path only after exact live-host acceptance. On macOS and Linux, an ordinary Codex CLI session with the MeMesh plugin registers automatically at SessionStart. SessionEnd retains a bounded 45-second idle queue window; a message accepted there becomes model-visible when the same thread resumes, resume replaces the prior exact generation, and expiry removes the registration. This does not wake a stopped UI. Codex Desktop and unattached tasks are not presumed registered unless the exact running session appears in `message discover`. Separate managed Codex app-server and Claude Channel paths may require one-time owner setup. The bundled Gemini ACP adapter is experimental protocol-development code, not a release-gated native-wakeup provider. Adapter imports and a live router socket do not prove host registration or `host_accept`. Do not promise that a stopped, missing, or replaced session will wake up: it is not resumed or silently rerouted, and a failed exact-session native delivery is not replayed automatically. Use the stable principal for logical routing, and an exact session/generation only when delivery must not move to a replacement connection. Local owns durable storage and host-native delivery; Cloud relay, A2A, SSE, discovery, or fetch is not host delivery.
91
+
92
+ When pairing Claude Channel with an automatically registered Codex session, use
93
+ the complete `project` field from `memesh briefing --json` for Claude setup. Do
94
+ not replace it with the repository basename; different project strings are
95
+ different discovery and native-routing scopes.
90
96
 
91
97
  Durable audit does not mean unbounded silent growth. Owners can inspect it with `memesh message storage report --cutoff <ISO timestamp>`, preview bounded terminal-payload tombstones with `memesh message storage prune --cutoff <ISO timestamp>`, and explicitly add `--apply`. Never prune unresolved/offline-pending work. `MEMESH_AGENT_MESSAGE_STORAGE_QUOTA_BYTES` is an optional owner policy; there is no default quota or automatic pruning.
92
98
 
@@ -134,13 +140,12 @@ fabricate a memory or cite a `[mem:id]` that was not actually returned.
134
140
  Recall is bounded by `limit` — a small hit count is not a graph-wide count,
135
141
  and an empty result is not proof nothing was stored: vary the wording or
136
142
  narrow by tag before concluding. Every recall answer includes a `retrieval`
137
- block — `truncated: true` means the window filled (more may exist);
138
- `degraded: true` means semantic search could not run and these are
139
- keyword-only results right now (`memesh doctor` explains why).
143
+ block — `truncated: true` means the window filled (more may exist). Retrieval
144
+ uses the local FTS5 keyword index; it does not call a model or vector service.
140
145
 
141
- ## What's Already Automatic (Claude Code Plugin Hooks)
146
+ ## What's Already Automatic (Plugin Hooks)
142
147
 
143
- If MeMesh is installed as a Claude Code plugin, these happen **without any action from you**:
148
+ With the Claude Code plugin, the first eight rows happen **without any action from you**. The final row is the separate Codex plugin SessionStart/SessionEnd companion lifecycle:
144
149
 
145
150
  | Hook | When | What it does |
146
151
  |------|------|-------------|
@@ -149,10 +154,10 @@ If MeMesh is installed as a Claude Code plugin, these happen **without any actio
149
154
  | **UserPromptSubmit** | When you submit a prompt | Detects "remember this" intent (5 languages) and reminds Claude to use memesh |
150
155
  | **PostToolUse (Bash)** | After `git commit` | Auto-tracks the commit with diff stats as a memory entity |
151
156
  | **PostToolUse (ExitPlanMode/AskUserQuestion)** | A plan is approved or you answer a question | Reminds Claude to `remember` the decision if it's worth keeping — once per tool per session |
152
- | **Stop** | Session ends | Auto-captures session knowledge + runs LLM failure analysis lessons |
157
+ | **Stop** | Session ends | Auto-captures session knowledge and applies the configured update policy |
153
158
  | **PreCompact** | Before context compaction | Saves important knowledge before history is compressed |
154
159
  | **PreToolUse (Bash)** | Before a command runs | Fires accepted lesson-guards — warns when a recorded mistake is about to repeat |
155
- | **SessionStart (Codex, async)** | A configured Codex session starts or resumes | Registers that exact live thread for bounded full-message native delivery; exits without registering outside the configured workspace |
160
+ | **SessionStart/SessionEnd (Codex)** | An ordinary Codex CLI plugin session starts, resumes, or ends | Launches the detached exact-thread companion, replaces its generation on resume, and retires it after the bounded idle queue window; a matching owner-private config may override its project/principal |
156
161
 
157
162
  Because of the SessionStart hook: **in Claude Code, do NOT call `briefing` at
158
163
  session start — it is already in your context.** Call it only mid-session
@@ -226,16 +231,16 @@ memesh forget --name "old-auth-approach" # archive the who
226
231
  Both are soft (recoverable) — nothing is permanently removed.
227
232
 
228
233
  ### Memories are getting verbose or stale
229
- Use the **memesh-review** skill: it analyzes health, finds stale, conflicting
230
- and redundant memories, and proposes cleanup (including `memesh dream`, the
231
- reviewed digest pipeline). Do not hand-compress memories yourself.
234
+ Use the **memesh-review** skill: it prepares bounded `work_package` evidence
235
+ for an already-running local agent, then leaves every proposal pending for
236
+ human review. Do not hand-compress memories yourself.
232
237
 
233
238
  ### Backup, share, health
234
239
  ```bash
235
240
  memesh export --tag "project:myapp" > memories.json
236
241
  memesh import memories.json --merge skip # skip | overwrite | append
237
- memesh status # version, search level, embeddings
238
- memesh reindex # rebuild embeddings after provider change
242
+ memesh status # version, install channel, update state
243
+ memesh reindex --fts # rebuild the local keyword index
239
244
  ```
240
245
 
241
246
  ## Memory hygiene
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: memesh-review
3
- description: Review and optimize the MeMesh memory database. Analyzes health score, finds stale/conflicting/redundant memories, shows work patterns, and suggests cleanup actions. Use when asked to "review memories", "check memory health", "clean up knowledge", or "what's in my memory".
3
+ description: Review and optimize the MeMesh memory database. Analyzes health score, finds stale or noisy memories, shows work patterns, and suggests human-reviewed cleanup actions. Use when asked to "review memories", "check memory health", "clean up knowledge", or "what's in my memory".
4
4
  user-invocable: true
5
5
  ---
6
6
 
@@ -59,11 +59,12 @@ From the recalled data, compute and present:
59
59
 
60
60
  **Verbose (5+ observations)**
61
61
  - "entity-name" (N observations) — note it; there is no one-entity compression
62
- command any more. If the noise is spread across many episodic entries,
63
- suggest `memesh dream run` instead.
62
+ command. If useful knowledge is spread across episodic entries, an already
63
+ running agent can prepare one MCP `work_package` for human review.
64
64
 
65
65
  **Potential conflicts**
66
- - "entity-A" vs "entity-B" — contradicting decisions
66
+ - "entity-A" vs "entity-B" — inspect the text and recommend an explicit
67
+ `contradicts` or `supersedes` relation; MeMesh does not judge it automatically
67
68
 
68
69
  **Noise ratio**
69
70
  - N% auto-tracked (session_keypoint, commit) vs N% intentional knowledge
@@ -71,7 +72,7 @@ From the recalled data, compute and present:
71
72
 
72
73
  ### Recommended Actions
73
74
  1. `memesh forget --name "old-design"` (superseded)
74
- 2. `memesh dream run --project myapp` (propose digests for the noisy clusters, then review)
75
+ 2. Ask the current agent to prepare one MCP `work_package`; then review the staged proposal
75
76
  3. `memesh remember ...` (knowledge gap in [area])
76
77
  ```
77
78
 
@@ -81,7 +82,7 @@ Present the report first. Ask which actions to execute. Then run the commands:
81
82
 
82
83
  ```bash
83
84
  memesh forget --name "outdated-entity"
84
- memesh dream run # then: memesh dream list / accept <id> / reject <id>
85
+ # MCP work_package submits one proposal; then use dream show / accept / reject
85
86
  memesh remember --name "missing-knowledge" --type decision --obs "..."
86
87
  ```
87
88
 
@@ -1,10 +0,0 @@
1
- import type { LLMConfig } from './config.js';
2
- import { type LLMAttempt } from './llm-client.js';
3
- export interface AutoTagOptions {
4
- fallbacks?: LLMConfig[];
5
- onAttempt?: (attempts: LLMAttempt[]) => void;
6
- }
7
- export declare function autoTag(name: string, type: string, observations: string[], llmConfig: LLMConfig, opts?: AutoTagOptions): Promise<string[]>;
8
- export declare function autoTagAndApply(entityId: number, name: string, type: string, observations: string[], llmConfig: LLMConfig, opts?: AutoTagOptions): Promise<void>;
9
- export declare function parseTags(text: string): string[];
10
- //# sourceMappingURL=auto-tagger.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"auto-tagger.d.ts","sourceRoot":"","sources":["../../src/core/auto-tagger.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAW,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAM3D,MAAM,WAAW,cAAc;IAE7B,SAAS,CAAC,EAAE,SAAS,EAAE,CAAC;IAExB,SAAS,CAAC,EAAE,CAAC,QAAQ,EAAE,UAAU,EAAE,KAAK,IAAI,CAAC;CAC9C;AAOD,wBAAsB,OAAO,CAC3B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,EAAE,EACtB,SAAS,EAAE,SAAS,EACpB,IAAI,GAAE,cAAmB,GACxB,OAAO,CAAC,MAAM,EAAE,CAAC,CAgCnB;AAMD,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,EAAE,EACtB,SAAS,EAAE,SAAS,EACpB,IAAI,GAAE,cAAmB,GACxB,OAAO,CAAC,IAAI,CAAC,CAaf;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAehD"}