@lossless-claude/lcm 0.2.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 (292) hide show
  1. package/.claude-plugin/commands/lcm-compact.md +31 -0
  2. package/.claude-plugin/commands/lcm-curate.md +31 -0
  3. package/.claude-plugin/commands/lcm-diagnose.md +29 -0
  4. package/.claude-plugin/commands/lcm-doctor.md +23 -0
  5. package/.claude-plugin/commands/lcm-dogfood.md +101 -0
  6. package/.claude-plugin/commands/lcm-import.md +48 -0
  7. package/.claude-plugin/commands/lcm-promote.md +29 -0
  8. package/.claude-plugin/commands/lcm-sensitive.md +55 -0
  9. package/.claude-plugin/commands/lcm-stats.md +19 -0
  10. package/.claude-plugin/commands/lcm-status.md +27 -0
  11. package/.claude-plugin/hooks/README.md +47 -0
  12. package/.claude-plugin/marketplace.json +20 -0
  13. package/.claude-plugin/mcp.mjs +12 -0
  14. package/.claude-plugin/plugin.json +46 -0
  15. package/.claude-plugin/skills/lcm-context/SKILL.md +107 -0
  16. package/.claude-plugin/skills/lcm-dogfood/SKILL.md +102 -0
  17. package/.claude-plugin/skills/lcm-dogfood/references/checks.md +239 -0
  18. package/.claude-plugin/skills/lcm-dogfood/references/known-issues.md +11 -0
  19. package/.claude-plugin/skills/lcm-dogfood/scripts/db-integrity.js +40 -0
  20. package/.claude-plugin/skills/lcm-dogfood/scripts/prompt-search-test.js +35 -0
  21. package/.claude-plugin/skills/lcm-e2e/SKILL.md +61 -0
  22. package/.claude-plugin/skills/lcm-e2e/checklist.md +367 -0
  23. package/.claude-plugin/skills/lossless-claude-upgrade/SKILL.md +47 -0
  24. package/LICENSE +21 -0
  25. package/README.md +231 -0
  26. package/dist/bin/lcm.d.ts +2 -0
  27. package/dist/bin/lcm.js +461 -0
  28. package/dist/bin/lcm.js.map +1 -0
  29. package/dist/installer/dry-run-deps.d.ts +23 -0
  30. package/dist/installer/dry-run-deps.js +66 -0
  31. package/dist/installer/dry-run-deps.js.map +1 -0
  32. package/dist/installer/install.d.ts +39 -0
  33. package/dist/installer/install.js +236 -0
  34. package/dist/installer/install.js.map +1 -0
  35. package/dist/installer/uninstall.d.ts +11 -0
  36. package/dist/installer/uninstall.js +80 -0
  37. package/dist/installer/uninstall.js.map +1 -0
  38. package/dist/src/batch-compact.d.ts +16 -0
  39. package/dist/src/batch-compact.js +121 -0
  40. package/dist/src/batch-compact.js.map +1 -0
  41. package/dist/src/compaction.d.ts +198 -0
  42. package/dist/src/compaction.js +964 -0
  43. package/dist/src/compaction.js.map +1 -0
  44. package/dist/src/connectors/constants.d.ts +5 -0
  45. package/dist/src/connectors/constants.js +6 -0
  46. package/dist/src/connectors/constants.js.map +1 -0
  47. package/dist/src/connectors/installer.d.ts +16 -0
  48. package/dist/src/connectors/installer.js +200 -0
  49. package/dist/src/connectors/installer.js.map +1 -0
  50. package/dist/src/connectors/registry.d.ts +4 -0
  51. package/dist/src/connectors/registry.js +264 -0
  52. package/dist/src/connectors/registry.js.map +1 -0
  53. package/dist/src/connectors/template-service.d.ts +5 -0
  54. package/dist/src/connectors/template-service.js +54 -0
  55. package/dist/src/connectors/template-service.js.map +1 -0
  56. package/dist/src/connectors/templates/base.md +1 -0
  57. package/dist/src/connectors/templates/mcp-base.md +1 -0
  58. package/dist/src/connectors/templates/sections/command-reference.md +15 -0
  59. package/dist/src/connectors/templates/sections/mcp-workflow.md +18 -0
  60. package/dist/src/connectors/templates/sections/workflow.md +29 -0
  61. package/dist/src/connectors/templates/skill/SKILL.md +74 -0
  62. package/dist/src/connectors/types.d.ts +19 -0
  63. package/dist/src/connectors/types.js +10 -0
  64. package/dist/src/connectors/types.js.map +1 -0
  65. package/dist/src/daemon/client.d.ts +9 -0
  66. package/dist/src/daemon/client.js +28 -0
  67. package/dist/src/daemon/client.js.map +1 -0
  68. package/dist/src/daemon/config.d.ts +48 -0
  69. package/dist/src/daemon/config.js +67 -0
  70. package/dist/src/daemon/config.js.map +1 -0
  71. package/dist/src/daemon/lifecycle.d.ts +19 -0
  72. package/dist/src/daemon/lifecycle.js +102 -0
  73. package/dist/src/daemon/lifecycle.js.map +1 -0
  74. package/dist/src/daemon/orientation.d.ts +1 -0
  75. package/dist/src/daemon/orientation.js +9 -0
  76. package/dist/src/daemon/orientation.js.map +1 -0
  77. package/dist/src/daemon/project-queue.d.ts +1 -0
  78. package/dist/src/daemon/project-queue.js +17 -0
  79. package/dist/src/daemon/project-queue.js.map +1 -0
  80. package/dist/src/daemon/project.d.ts +7 -0
  81. package/dist/src/daemon/project.js +25 -0
  82. package/dist/src/daemon/project.js.map +1 -0
  83. package/dist/src/daemon/proxy-manager.d.ts +21 -0
  84. package/dist/src/daemon/proxy-manager.js +205 -0
  85. package/dist/src/daemon/proxy-manager.js.map +1 -0
  86. package/dist/src/daemon/routes/compact.d.ts +13 -0
  87. package/dist/src/daemon/routes/compact.js +195 -0
  88. package/dist/src/daemon/routes/compact.js.map +1 -0
  89. package/dist/src/daemon/routes/describe.d.ts +3 -0
  90. package/dist/src/daemon/routes/describe.js +39 -0
  91. package/dist/src/daemon/routes/describe.js.map +1 -0
  92. package/dist/src/daemon/routes/expand.d.ts +3 -0
  93. package/dist/src/daemon/routes/expand.js +41 -0
  94. package/dist/src/daemon/routes/expand.js.map +1 -0
  95. package/dist/src/daemon/routes/grep.d.ts +3 -0
  96. package/dist/src/daemon/routes/grep.js +43 -0
  97. package/dist/src/daemon/routes/grep.js.map +1 -0
  98. package/dist/src/daemon/routes/ingest.d.ts +3 -0
  99. package/dist/src/daemon/routes/ingest.js +101 -0
  100. package/dist/src/daemon/routes/ingest.js.map +1 -0
  101. package/dist/src/daemon/routes/promote.d.ts +4 -0
  102. package/dist/src/daemon/routes/promote.js +104 -0
  103. package/dist/src/daemon/routes/promote.js.map +1 -0
  104. package/dist/src/daemon/routes/prompt-search.d.ts +3 -0
  105. package/dist/src/daemon/routes/prompt-search.js +65 -0
  106. package/dist/src/daemon/routes/prompt-search.js.map +1 -0
  107. package/dist/src/daemon/routes/recent.d.ts +3 -0
  108. package/dist/src/daemon/routes/recent.js +37 -0
  109. package/dist/src/daemon/routes/recent.js.map +1 -0
  110. package/dist/src/daemon/routes/restore.d.ts +3 -0
  111. package/dist/src/daemon/routes/restore.js +120 -0
  112. package/dist/src/daemon/routes/restore.js.map +1 -0
  113. package/dist/src/daemon/routes/search.d.ts +2 -0
  114. package/dist/src/daemon/routes/search.js +66 -0
  115. package/dist/src/daemon/routes/search.js.map +1 -0
  116. package/dist/src/daemon/routes/status.d.ts +3 -0
  117. package/dist/src/daemon/routes/status.js +80 -0
  118. package/dist/src/daemon/routes/status.js.map +1 -0
  119. package/dist/src/daemon/routes/store.d.ts +2 -0
  120. package/dist/src/daemon/routes/store.js +46 -0
  121. package/dist/src/daemon/routes/store.js.map +1 -0
  122. package/dist/src/daemon/server.d.ts +19 -0
  123. package/dist/src/daemon/server.js +183 -0
  124. package/dist/src/daemon/server.js.map +1 -0
  125. package/dist/src/daemon/summarizer.d.ts +11 -0
  126. package/dist/src/daemon/summarizer.js +51 -0
  127. package/dist/src/daemon/summarizer.js.map +1 -0
  128. package/dist/src/db/config.d.ts +31 -0
  129. package/dist/src/db/config.js +83 -0
  130. package/dist/src/db/config.js.map +1 -0
  131. package/dist/src/db/connection.d.ts +3 -0
  132. package/dist/src/db/connection.js +62 -0
  133. package/dist/src/db/connection.js.map +1 -0
  134. package/dist/src/db/features.d.ts +11 -0
  135. package/dist/src/db/features.js +36 -0
  136. package/dist/src/db/features.js.map +1 -0
  137. package/dist/src/db/migration.d.ts +4 -0
  138. package/dist/src/db/migration.js +499 -0
  139. package/dist/src/db/migration.js.map +1 -0
  140. package/dist/src/db/promoted.d.ts +47 -0
  141. package/dist/src/db/promoted.js +96 -0
  142. package/dist/src/db/promoted.js.map +1 -0
  143. package/dist/src/db/redaction-stats.d.ts +6 -0
  144. package/dist/src/db/redaction-stats.js +16 -0
  145. package/dist/src/db/redaction-stats.js.map +1 -0
  146. package/dist/src/diagnose.d.ts +39 -0
  147. package/dist/src/diagnose.js +432 -0
  148. package/dist/src/diagnose.js.map +1 -0
  149. package/dist/src/doctor/doctor.d.ts +4 -0
  150. package/dist/src/doctor/doctor.js +378 -0
  151. package/dist/src/doctor/doctor.js.map +1 -0
  152. package/dist/src/doctor/types.d.ts +24 -0
  153. package/dist/src/doctor/types.js +2 -0
  154. package/dist/src/doctor/types.js.map +1 -0
  155. package/dist/src/expansion.d.ts +100 -0
  156. package/dist/src/expansion.js +268 -0
  157. package/dist/src/expansion.js.map +1 -0
  158. package/dist/src/hooks/auto-heal.d.ts +12 -0
  159. package/dist/src/hooks/auto-heal.js +49 -0
  160. package/dist/src/hooks/auto-heal.js.map +1 -0
  161. package/dist/src/hooks/compact.d.ts +5 -0
  162. package/dist/src/hooks/compact.js +22 -0
  163. package/dist/src/hooks/compact.js.map +1 -0
  164. package/dist/src/hooks/dispatch.d.ts +7 -0
  165. package/dist/src/hooks/dispatch.js +36 -0
  166. package/dist/src/hooks/dispatch.js.map +1 -0
  167. package/dist/src/hooks/probe-precompact.d.ts +2 -0
  168. package/dist/src/hooks/probe-precompact.js +17 -0
  169. package/dist/src/hooks/probe-precompact.js.map +1 -0
  170. package/dist/src/hooks/probe-sessionstart.d.ts +2 -0
  171. package/dist/src/hooks/probe-sessionstart.js +18 -0
  172. package/dist/src/hooks/probe-sessionstart.js.map +1 -0
  173. package/dist/src/hooks/restore.d.ts +5 -0
  174. package/dist/src/hooks/restore.js +19 -0
  175. package/dist/src/hooks/restore.js.map +1 -0
  176. package/dist/src/hooks/session-end.d.ts +16 -0
  177. package/dist/src/hooks/session-end.js +73 -0
  178. package/dist/src/hooks/session-end.js.map +1 -0
  179. package/dist/src/hooks/user-prompt.d.ts +5 -0
  180. package/dist/src/hooks/user-prompt.js +31 -0
  181. package/dist/src/hooks/user-prompt.js.map +1 -0
  182. package/dist/src/import.d.ts +24 -0
  183. package/dist/src/import.js +119 -0
  184. package/dist/src/import.js.map +1 -0
  185. package/dist/src/large-files.d.ts +28 -0
  186. package/dist/src/large-files.js +413 -0
  187. package/dist/src/large-files.js.map +1 -0
  188. package/dist/src/llm/anthropic.d.ts +9 -0
  189. package/dist/src/llm/anthropic.js +54 -0
  190. package/dist/src/llm/anthropic.js.map +1 -0
  191. package/dist/src/llm/claude-process.d.ts +2 -0
  192. package/dist/src/llm/claude-process.js +55 -0
  193. package/dist/src/llm/claude-process.js.map +1 -0
  194. package/dist/src/llm/codex-process.d.ts +15 -0
  195. package/dist/src/llm/codex-process.js +142 -0
  196. package/dist/src/llm/codex-process.js.map +1 -0
  197. package/dist/src/llm/mock-summarizer.d.ts +9 -0
  198. package/dist/src/llm/mock-summarizer.js +17 -0
  199. package/dist/src/llm/mock-summarizer.js.map +1 -0
  200. package/dist/src/llm/openai.d.ts +10 -0
  201. package/dist/src/llm/openai.js +52 -0
  202. package/dist/src/llm/openai.js.map +1 -0
  203. package/dist/src/llm/types.d.ts +6 -0
  204. package/dist/src/llm/types.js +2 -0
  205. package/dist/src/llm/types.js.map +1 -0
  206. package/dist/src/mcp/server.d.ts +9 -0
  207. package/dist/src/mcp/server.js +128 -0
  208. package/dist/src/mcp/server.js.map +1 -0
  209. package/dist/src/mcp/tools/lcm-describe.d.ts +14 -0
  210. package/dist/src/mcp/tools/lcm-describe.js +12 -0
  211. package/dist/src/mcp/tools/lcm-describe.js.map +1 -0
  212. package/dist/src/mcp/tools/lcm-doctor.d.ts +8 -0
  213. package/dist/src/mcp/tools/lcm-doctor.js +9 -0
  214. package/dist/src/mcp/tools/lcm-doctor.js.map +1 -0
  215. package/dist/src/mcp/tools/lcm-expand.d.ts +18 -0
  216. package/dist/src/mcp/tools/lcm-expand.js +13 -0
  217. package/dist/src/mcp/tools/lcm-expand.js.map +1 -0
  218. package/dist/src/mcp/tools/lcm-grep.d.ts +27 -0
  219. package/dist/src/mcp/tools/lcm-grep.js +15 -0
  220. package/dist/src/mcp/tools/lcm-grep.js.map +1 -0
  221. package/dist/src/mcp/tools/lcm-search.d.ts +33 -0
  222. package/dist/src/mcp/tools/lcm-search.js +15 -0
  223. package/dist/src/mcp/tools/lcm-search.js.map +1 -0
  224. package/dist/src/mcp/tools/lcm-stats.d.ts +14 -0
  225. package/dist/src/mcp/tools/lcm-stats.js +11 -0
  226. package/dist/src/mcp/tools/lcm-stats.js.map +1 -0
  227. package/dist/src/mcp/tools/lcm-store.d.ts +26 -0
  228. package/dist/src/mcp/tools/lcm-store.js +22 -0
  229. package/dist/src/mcp/tools/lcm-store.js.map +1 -0
  230. package/dist/src/memory/index.d.ts +22 -0
  231. package/dist/src/memory/index.js +21 -0
  232. package/dist/src/memory/index.js.map +1 -0
  233. package/dist/src/promotion/dedup.d.ts +19 -0
  234. package/dist/src/promotion/dedup.js +42 -0
  235. package/dist/src/promotion/dedup.js.map +1 -0
  236. package/dist/src/promotion/detector.d.ts +15 -0
  237. package/dist/src/promotion/detector.js +31 -0
  238. package/dist/src/promotion/detector.js.map +1 -0
  239. package/dist/src/prompts/condensed-d1.yaml +38 -0
  240. package/dist/src/prompts/condensed-d2.yaml +32 -0
  241. package/dist/src/prompts/condensed-d3plus.yaml +32 -0
  242. package/dist/src/prompts/leaf-aggressive.yaml +34 -0
  243. package/dist/src/prompts/leaf-normal.yaml +34 -0
  244. package/dist/src/prompts/loader.d.ts +9 -0
  245. package/dist/src/prompts/loader.js +37 -0
  246. package/dist/src/prompts/loader.js.map +1 -0
  247. package/dist/src/prompts/promoted-merge.yaml +18 -0
  248. package/dist/src/prompts/system.yaml +5 -0
  249. package/dist/src/retrieval.d.ts +122 -0
  250. package/dist/src/retrieval.js +214 -0
  251. package/dist/src/retrieval.js.map +1 -0
  252. package/dist/src/scrub.d.ts +46 -0
  253. package/dist/src/scrub.js +184 -0
  254. package/dist/src/scrub.js.map +1 -0
  255. package/dist/src/sensitive.d.ts +4 -0
  256. package/dist/src/sensitive.js +258 -0
  257. package/dist/src/sensitive.js.map +1 -0
  258. package/dist/src/stats.d.ts +34 -0
  259. package/dist/src/stats.js +260 -0
  260. package/dist/src/stats.js.map +1 -0
  261. package/dist/src/store/conversation-store.d.ts +115 -0
  262. package/dist/src/store/conversation-store.js +447 -0
  263. package/dist/src/store/conversation-store.js.map +1 -0
  264. package/dist/src/store/fts5-sanitize.d.ts +23 -0
  265. package/dist/src/store/fts5-sanitize.js +30 -0
  266. package/dist/src/store/fts5-sanitize.js.map +1 -0
  267. package/dist/src/store/full-text-fallback.d.ts +16 -0
  268. package/dist/src/store/full-text-fallback.js +60 -0
  269. package/dist/src/store/full-text-fallback.js.map +1 -0
  270. package/dist/src/store/index.d.ts +4 -0
  271. package/dist/src/store/index.js +3 -0
  272. package/dist/src/store/index.js.map +1 -0
  273. package/dist/src/store/summary-store.d.ts +118 -0
  274. package/dist/src/store/summary-store.js +570 -0
  275. package/dist/src/store/summary-store.js.map +1 -0
  276. package/dist/src/summarize.d.ts +59 -0
  277. package/dist/src/summarize.js +619 -0
  278. package/dist/src/summarize.js.map +1 -0
  279. package/dist/src/transcript.d.ts +7 -0
  280. package/dist/src/transcript.js +51 -0
  281. package/dist/src/transcript.js.map +1 -0
  282. package/dist/src/types.d.ts +116 -0
  283. package/dist/src/types.js +8 -0
  284. package/dist/src/types.js.map +1 -0
  285. package/docs/agent-tools.md +187 -0
  286. package/docs/architecture.md +224 -0
  287. package/docs/configuration.md +168 -0
  288. package/docs/fts5.md +161 -0
  289. package/docs/hook-protocol.md +41 -0
  290. package/docs/privacy.md +101 -0
  291. package/mcp.mjs +17 -0
  292. package/package.json +79 -0
@@ -0,0 +1,51 @@
1
+ import { readFileSync } from "node:fs";
2
+ function extractText(content) {
3
+ if (typeof content === "string")
4
+ return content;
5
+ if (Array.isArray(content)) {
6
+ return content
7
+ .map((b) => {
8
+ if (b.type === "text" && typeof b.text === "string")
9
+ return b.text;
10
+ if (b.type === "tool_result")
11
+ return extractText(b.content);
12
+ return "";
13
+ })
14
+ .filter(Boolean)
15
+ .join("\n");
16
+ }
17
+ return "";
18
+ }
19
+ export function estimateTokens(text) {
20
+ return Math.max(1, Math.ceil(text.length / 4));
21
+ }
22
+ export function parseTranscript(transcriptPath) {
23
+ let raw;
24
+ try {
25
+ raw = readFileSync(transcriptPath, "utf-8");
26
+ }
27
+ catch {
28
+ return [];
29
+ }
30
+ const messages = [];
31
+ for (const line of raw.split("\n")) {
32
+ const trimmed = line.trim();
33
+ if (!trimmed)
34
+ continue;
35
+ try {
36
+ const obj = JSON.parse(trimmed);
37
+ const role = obj.message?.role;
38
+ if (!role || !["user", "assistant", "system"].includes(role))
39
+ continue;
40
+ const content = extractText(obj.message?.content);
41
+ if (!content.trim())
42
+ continue;
43
+ messages.push({ role, content, tokenCount: estimateTokens(content) });
44
+ }
45
+ catch {
46
+ // skip malformed lines
47
+ }
48
+ }
49
+ return messages;
50
+ }
51
+ //# sourceMappingURL=transcript.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transcript.js","sourceRoot":"","sources":["../../src/transcript.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAsBvC,SAAS,WAAW,CAAC,OAA0C;IAC7D,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,OAAO,CAAC;IAChD,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3B,OAAO,OAAO;aACX,GAAG,CAAC,CAAC,CAAe,EAAE,EAAE;YACvB,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ;gBAAE,OAAO,CAAC,CAAC,IAAI,CAAC;YACnE,IAAI,CAAC,CAAC,IAAI,KAAK,aAAa;gBAAE,OAAO,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;YAC5D,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC;aACD,MAAM,CAAC,OAAO,CAAC;aACf,IAAI,CAAC,IAAI,CAAC,CAAC;IAChB,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,cAAsB;IACpD,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,YAAY,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC;IAC9C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,QAAQ,GAAoB,EAAE,CAAC;IACrC,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACnC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,CAAC,OAAO;YAAE,SAAS;QACvB,IAAI,CAAC;YACH,MAAM,GAAG,GAAmB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAChD,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC;YAC/B,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,MAAM,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC;gBAAE,SAAS;YACvE,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAClD,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE;gBAAE,SAAS;YAC9B,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACxE,CAAC;QAAC,MAAM,CAAC;YACP,uBAAuB;QACzB,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Core type definitions for the LCM plugin.
3
+ *
4
+ * These types define the contracts between LCM and Claude Code,
5
+ * abstracting away direct imports from core internals.
6
+ */
7
+ import type { LcmConfig } from "./db/config.js";
8
+ /**
9
+ * Minimal LLM completion interface needed by LCM for summarization.
10
+ * Matches the signature of completeSimple from @mariozechner/pi-ai.
11
+ */
12
+ export type CompletionContentBlock = {
13
+ type: string;
14
+ text?: string;
15
+ [key: string]: unknown;
16
+ };
17
+ export type CompletionResult = {
18
+ content: CompletionContentBlock[];
19
+ [key: string]: unknown;
20
+ };
21
+ export type CompleteFn = (params: {
22
+ provider?: string;
23
+ model: string;
24
+ apiKey?: string;
25
+ providerApi?: string;
26
+ authProfileId?: string;
27
+ agentDir?: string;
28
+ runtimeConfig?: unknown;
29
+ messages: Array<{
30
+ role: string;
31
+ content: unknown;
32
+ }>;
33
+ system?: string;
34
+ maxTokens: number;
35
+ temperature?: number;
36
+ reasoning?: string;
37
+ }) => Promise<CompletionResult>;
38
+ /**
39
+ * Gateway RPC call interface.
40
+ */
41
+ export type CallGatewayFn = (params: {
42
+ method: string;
43
+ params?: Record<string, unknown>;
44
+ timeoutMs?: number;
45
+ }) => Promise<unknown>;
46
+ /**
47
+ * Model resolution function — resolves model aliases and defaults.
48
+ * When providerHint is supplied, it takes precedence over env/defaults.
49
+ */
50
+ export type ResolveModelFn = (modelRef?: string, providerHint?: string) => {
51
+ provider: string;
52
+ model: string;
53
+ };
54
+ /**
55
+ * API key resolution function.
56
+ */
57
+ export type ApiKeyLookupOptions = {
58
+ profileId?: string;
59
+ preferredProfile?: string;
60
+ };
61
+ export type GetApiKeyFn = (provider: string, model: string, options?: ApiKeyLookupOptions) => Promise<string | undefined>;
62
+ export type RequireApiKeyFn = (provider: string, model: string, options?: ApiKeyLookupOptions) => Promise<string>;
63
+ /**
64
+ * Session key utilities.
65
+ */
66
+ export type ParseAgentSessionKeyFn = (sessionKey: string) => {
67
+ agentId: string;
68
+ suffix: string;
69
+ } | null;
70
+ export type IsSubagentSessionKeyFn = (sessionKey: string) => boolean;
71
+ /**
72
+ * Dependencies injected into the LCM engine at registration time.
73
+ * These replace all direct imports from Claude Code.
74
+ */
75
+ export interface LcmDependencies {
76
+ /** LCM configuration (from env vars + plugin config) */
77
+ config: LcmConfig;
78
+ /** LLM completion function for summarization */
79
+ complete: CompleteFn;
80
+ /** Gateway RPC call function (for subagent spawning, session ops) */
81
+ callGateway: CallGatewayFn;
82
+ /** Resolve model alias to provider/model pair */
83
+ resolveModel: ResolveModelFn;
84
+ /** Get API key for a provider/model pair */
85
+ getApiKey: GetApiKeyFn;
86
+ /** Require API key (throws if missing) */
87
+ requireApiKey: RequireApiKeyFn;
88
+ /** Parse agent session key into components */
89
+ parseAgentSessionKey: ParseAgentSessionKeyFn;
90
+ /** Check if a session key is a subagent key */
91
+ isSubagentSessionKey: IsSubagentSessionKeyFn;
92
+ /** Normalize an agent ID */
93
+ normalizeAgentId: (id?: string) => string;
94
+ /** Build system prompt for subagent sessions */
95
+ buildSubagentSystemPrompt: (params: {
96
+ depth: number;
97
+ maxDepth: number;
98
+ taskSummary?: string;
99
+ }) => string;
100
+ /** Read the latest assistant reply from a session's messages */
101
+ readLatestAssistantReply: (messages: unknown[]) => string | undefined;
102
+ /** Sanitize tool use/result pairing in message arrays */
103
+ /** Resolve the Claude Code agent directory */
104
+ resolveAgentDir: () => string;
105
+ /** Resolve runtime session id from an agent session key */
106
+ resolveSessionIdFromSessionKey: (sessionKey: string) => Promise<string | undefined>;
107
+ /** Agent lane constant for subagents */
108
+ agentLaneSubagent: string;
109
+ /** Logger */
110
+ log: {
111
+ info: (msg: string) => void;
112
+ warn: (msg: string) => void;
113
+ error: (msg: string) => void;
114
+ debug: (msg: string) => void;
115
+ };
116
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Core type definitions for the LCM plugin.
3
+ *
4
+ * These types define the contracts between LCM and Claude Code,
5
+ * abstracting away direct imports from core internals.
6
+ */
7
+ export {};
8
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
@@ -0,0 +1,187 @@
1
+ # Agent tools
2
+
3
+ LCM provides four tools for agents to search, inspect, and recall information from compacted conversation history.
4
+
5
+ ## Usage patterns
6
+
7
+ ### Escalation pattern: grep → describe → expand_query
8
+
9
+ Most recall tasks follow this escalation:
10
+
11
+ 1. **`lcm_grep`** — Find relevant summaries or messages by keyword/regex
12
+ 2. **`lcm_describe`** — Inspect a specific summary's full content (cheap, no sub-agent)
13
+ 3. **`lcm_expand_query`** — Deep recall: spawn a sub-agent to expand the DAG and answer a focused question
14
+
15
+ Start with grep. If the snippet is enough, stop. If you need full summary content, use describe. If you need details that were compressed away, use expand_query.
16
+
17
+ ### When to expand
18
+
19
+ Summaries are lossy by design. The "Expand for details about:" footer at the end of each summary lists what was dropped. Use `lcm_expand_query` when you need:
20
+
21
+ - Exact commands, error messages, or config values
22
+ - File paths and specific code changes
23
+ - Decision rationale beyond what the summary captured
24
+ - Tool call sequences and their outputs
25
+ - Verbatim quotes or specific data points
26
+
27
+ `lcm_expand_query` is bounded (~120s, scoped sub-agent) and relatively cheap. Don't ration it.
28
+
29
+ ## Tool reference
30
+
31
+ ### lcm_grep
32
+
33
+ Search across messages and/or summaries using regex or full-text search.
34
+
35
+ **Parameters:**
36
+
37
+ | Param | Type | Required | Default | Description |
38
+ |-------|------|----------|---------|-------------|
39
+ | `pattern` | string | ✅ | — | Search pattern |
40
+ | `mode` | string | | `"regex"` | `"regex"` or `"full_text"` |
41
+ | `scope` | string | | `"both"` | `"messages"`, `"summaries"`, or `"both"` |
42
+ | `conversationId` | number | | current | Specific conversation to search |
43
+ | `allConversations` | boolean | | `false` | Search all conversations |
44
+ | `since` | string | | — | ISO timestamp lower bound |
45
+ | `before` | string | | — | ISO timestamp upper bound |
46
+ | `limit` | number | | 50 | Max results (1–200) |
47
+
48
+ **Returns:** Array of matches with:
49
+ - `id` — Message or summary ID
50
+ - `type` — `"message"` or `"summary"`
51
+ - `snippet` — Truncated content around the match
52
+ - `conversationId` — Which conversation
53
+ - `createdAt` — Timestamp
54
+ - For summaries: `depth`, `kind`, `summaryId`
55
+
56
+ **Examples:**
57
+
58
+ ```
59
+ # Full-text search across all conversations
60
+ lcm_grep(pattern: "database migration", mode: "full_text", allConversations: true)
61
+
62
+ # Regex search in summaries only
63
+ lcm_grep(pattern: "config\\.threshold.*0\\.[0-9]+", scope: "summaries")
64
+
65
+ # Recent messages containing a specific term
66
+ lcm_grep(pattern: "deployment", since: "2026-02-19T00:00:00Z", scope: "messages")
67
+ ```
68
+
69
+ ### lcm_describe
70
+
71
+ Look up metadata and content for a specific summary or stored file.
72
+
73
+ **Parameters:**
74
+
75
+ | Param | Type | Required | Default | Description |
76
+ |-------|------|----------|---------|-------------|
77
+ | `id` | string | ✅ | — | `sum_xxx` for summaries, `file_xxx` for files |
78
+ | `conversationId` | number | | current | Scope to a specific conversation |
79
+ | `allConversations` | boolean | | `false` | Allow cross-conversation lookups |
80
+
81
+ **Returns for summaries:**
82
+ - Full summary content
83
+ - Metadata: depth, kind, token count, created timestamp
84
+ - Time range (earliestAt, latestAt)
85
+ - Descendant count
86
+ - Parent summary IDs (for condensed summaries)
87
+ - Child summary IDs
88
+ - Source message IDs (for leaf summaries)
89
+ - File IDs referenced in the summary
90
+
91
+ **Returns for files:**
92
+ - File content (full text)
93
+ - Metadata: fileName, mimeType, byteSize
94
+ - Exploration summary
95
+ - Storage path
96
+
97
+ **Examples:**
98
+
99
+ ```
100
+ # Inspect a summary from context
101
+ lcm_describe(id: "sum_abc123def456")
102
+
103
+ # Retrieve a stored large file
104
+ lcm_describe(id: "file_789abc012345")
105
+ ```
106
+
107
+ ### lcm_expand_query
108
+
109
+ Answer a focused question by expanding summaries through the DAG. Spawns a bounded sub-agent that walks parent links down to source material and returns a compact answer.
110
+
111
+ **Parameters:**
112
+
113
+ | Param | Type | Required | Default | Description |
114
+ |-------|------|----------|---------|-------------|
115
+ | `prompt` | string | ✅ | — | The question to answer |
116
+ | `query` | string | ✅* | — | Text query to find summaries (if no `summaryIds`) |
117
+ | `summaryIds` | string[] | ✅* | — | Specific summary IDs to expand (if no `query`) |
118
+ | `maxTokens` | number | | 2000 | Answer length cap |
119
+ | `conversationId` | number | | current | Scope to a specific conversation |
120
+ | `allConversations` | boolean | | `false` | Search across all conversations |
121
+
122
+ *One of `query` or `summaryIds` is required.
123
+
124
+ **Returns:**
125
+ - `answer` — The focused answer text
126
+ - `citedIds` — Summary IDs that contributed to the answer
127
+ - `expandedSummaryCount` — How many summaries were expanded
128
+ - `totalSourceTokens` — Total tokens read from the DAG
129
+ - `truncated` — Whether the answer was truncated to fit maxTokens
130
+
131
+ **Examples:**
132
+
133
+ ```
134
+ # Find and expand summaries about a topic
135
+ lcm_expand_query(
136
+ query: "OAuth authentication fix",
137
+ prompt: "What was the root cause and what commits fixed it?"
138
+ )
139
+
140
+ # Expand specific summaries you already have
141
+ lcm_expand_query(
142
+ summaryIds: ["sum_abc123", "sum_def456"],
143
+ prompt: "What were the exact file changes?"
144
+ )
145
+
146
+ # Cross-conversation search
147
+ lcm_expand_query(
148
+ query: "deployment procedure",
149
+ prompt: "What's the current deployment process?",
150
+ allConversations: true
151
+ )
152
+ ```
153
+
154
+ ### lcm_expand
155
+
156
+ Low-level DAG expansion tool. **Only available to sub-agents** spawned by `lcm_expand_query`. Main agents should always use `lcm_expand_query` instead.
157
+
158
+ This tool is what the expansion sub-agent uses internally to walk the summary DAG, read source messages, and build its answer.
159
+
160
+ ## Tips for agent developers
161
+
162
+ ### Configuring agent prompts
163
+
164
+ Add instructions to your agent's system prompt so it knows when to use LCM tools:
165
+
166
+ ```markdown
167
+ ## Memory & Context
168
+
169
+ Use LCM tools for recall:
170
+ 1. `lcm_grep` — Search all conversations by keyword/regex
171
+ 2. `lcm_describe` — Inspect a specific summary (cheap, no sub-agent)
172
+ 3. `lcm_expand_query` — Deep recall with sub-agent expansion
173
+
174
+ When summaries in context have an "Expand for details about:" footer
175
+ listing something you need, use `lcm_expand_query` to get the full detail.
176
+ ```
177
+
178
+ ### Conversation scoping
179
+
180
+ By default, tools operate on the current conversation. Use `allConversations: true` to search across all of them (all agents, all sessions). Use `conversationId` to target a specific conversation you already know about (from previous grep results).
181
+
182
+ ### Performance considerations
183
+
184
+ - `lcm_grep` and `lcm_describe` are fast (direct database queries)
185
+ - `lcm_expand_query` spawns a sub-agent and takes ~30–120 seconds
186
+ - The sub-agent has a 120-second timeout with cleanup guarantees
187
+ - Token caps (`LCM_MAX_EXPAND_TOKENS`) prevent runaway expansion
@@ -0,0 +1,224 @@
1
+ # Architecture
2
+
3
+ This document describes how lossless-claude works internally — the data model, compaction lifecycle, context assembly, and expansion system.
4
+
5
+ ## Data model
6
+
7
+ ### Conversations and messages
8
+
9
+ Every Claude Code session maps to a **conversation**. The first time a session ingests a message, LCM creates a conversation record keyed by the runtime session ID.
10
+
11
+ Messages are stored with:
12
+ - **seq** — Monotonically increasing sequence number within the conversation
13
+ - **role** — `user`, `assistant`, `system`, or `tool`
14
+ - **content** — Plain text extraction of the message
15
+ - **tokenCount** — Estimated token count (~4 chars/token)
16
+ - **createdAt** — Insertion timestamp
17
+
18
+ Each message also has **message_parts** — structured content blocks that preserve the original shape (text blocks, tool calls, tool results, reasoning, file content, etc.). This allows the assembler to reconstruct rich content when building model context, not just flat text.
19
+
20
+ ### The summary DAG
21
+
22
+ Summaries form a directed acyclic graph with two node types:
23
+
24
+ **Leaf summaries** (depth 0, kind `"leaf"`):
25
+ - Created from a chunk of raw messages
26
+ - Linked to source messages via `summary_messages`
27
+ - Contain a narrative summary with timestamps
28
+ - Typically 800–1200 tokens
29
+
30
+ **Condensed summaries** (depth 1+, kind `"condensed"`):
31
+ - Created from a chunk of summaries at the same depth
32
+ - Linked to parent summaries via `summary_parents`
33
+ - Each depth tier uses a progressively more abstract prompt
34
+ - Typically 1500–2000 tokens
35
+
36
+ Every summary carries:
37
+ - **summaryId** — `sum_` + 16 hex chars (SHA-256 of content + timestamp)
38
+ - **conversationId** — Which conversation it belongs to
39
+ - **depth** — Position in the hierarchy (0 = leaf)
40
+ - **earliestAt / latestAt** — Time range of source material
41
+ - **descendantCount** — Total number of ancestor summaries (transitive)
42
+ - **fileIds** — References to large files mentioned in the source
43
+ - **tokenCount** — Estimated tokens
44
+
45
+ ### Context items
46
+
47
+ The **context_items** table maintains the ordered list of what the model sees for each conversation. Each entry is either a message reference or a summary reference, identified by ordinal.
48
+
49
+ When compaction creates a summary from a range of messages (or summaries), the source items are replaced by a single summary item. This keeps the context list compact while preserving ordering.
50
+
51
+ ## Compaction lifecycle
52
+
53
+ ### Ingestion
54
+
55
+ When Claude Code processes a turn, it calls the context engine's lifecycle hooks:
56
+
57
+ 1. **bootstrap** — On session start, reconciles the JSONL session file with the LCM database. Imports any messages that exist in the file but not in LCM (crash recovery).
58
+ 2. **ingest** / **ingestBatch** — Persists new messages to the database and appends them to context_items.
59
+ 3. **afterTurn** — After the model responds, ingests new messages, then evaluates whether compaction should run.
60
+
61
+ ### Leaf compaction
62
+
63
+ The **leaf pass** converts raw messages into leaf summaries:
64
+
65
+ 1. Identify the oldest contiguous chunk of raw messages outside the **fresh tail** (protected recent messages).
66
+ 2. Cap the chunk at `leafChunkTokens` (default 20k tokens).
67
+ 3. Concatenate message content with timestamps.
68
+ 4. Resolve the most recent prior summary for continuity (passed as `previous_context` so the LLM avoids repeating known information).
69
+ 5. Send to the LLM with the leaf prompt.
70
+ 6. Normalize provider response blocks (Anthropic/OpenAI text, output_text, and nested content/summary shapes) into plain text.
71
+ 7. If normalization is empty, log provider/model/block-type diagnostics and fall back to deterministic truncation.
72
+ 8. If the summary is larger than the input (LLM failure), retry with the aggressive prompt. If still too large, fall back to deterministic truncation.
73
+ 9. Persist the summary, link to source messages, and replace the message range in context_items.
74
+
75
+ ### Condensation
76
+
77
+ The **condensed pass** merges summaries at the same depth into a higher-level summary:
78
+
79
+ 1. Find the shallowest depth with enough contiguous same-depth summaries (≥ `leafMinFanout` for d0, ≥ `condensedMinFanout` for d1+).
80
+ 2. Concatenate their content with time range headers.
81
+ 3. Send to the LLM with the depth-appropriate prompt (d1, d2, or d3+).
82
+ 4. Apply the same escalation strategy (normal → aggressive → truncation fallback).
83
+ 5. Persist with depth = targetDepth + 1, link to parent summaries, replace the range in context_items.
84
+
85
+ ### Compaction modes
86
+
87
+ **Incremental (after each turn):**
88
+ - Checks if raw tokens outside the fresh tail exceed `leafChunkTokens`
89
+ - If so, runs one leaf pass
90
+ - If `incrementalMaxDepth != 0`, follows with condensation passes up to that depth (`-1` for unlimited)
91
+ - Best-effort: failures don't break the conversation
92
+
93
+ **Full sweep (manual `/compact` or overflow):**
94
+ - Phase 1: Repeatedly runs leaf passes until no more eligible chunks
95
+ - Phase 2: Repeatedly runs condensation passes starting from the shallowest eligible depth
96
+ - Each pass checks for progress; stops if no tokens were saved
97
+
98
+ **Budget-targeted (`compactUntilUnder`):**
99
+ - Runs up to `maxRounds` (default 10) of full sweeps
100
+ - Stops when context is under the target token count
101
+ - Used by the overflow recovery path
102
+
103
+ ### Three-level escalation
104
+
105
+ Every summarization attempt follows this escalation:
106
+
107
+ 1. **Normal** — Standard prompt, temperature 0.2
108
+ 2. **Aggressive** — Tighter prompt requesting only durable facts, temperature 0.1, lower target tokens
109
+ 3. **Fallback** — Deterministic truncation to ~512 tokens with `[Truncated for context management]` marker
110
+
111
+ This ensures compaction always makes progress, even if the LLM produces poor output.
112
+
113
+ ## Context assembly
114
+
115
+ The assembler runs before each model turn and builds the message array:
116
+
117
+ ```
118
+ [summary₁, summary₂, ..., summaryₙ, message₁, message₂, ..., messageₘ]
119
+ ├── budget-constrained ──┤ ├──── fresh tail (always included) ────┤
120
+ ```
121
+
122
+ ### Steps
123
+
124
+ 1. Fetch all context_items ordered by ordinal.
125
+ 2. Resolve each item — summaries become user messages with XML wrappers; messages are reconstructed from parts.
126
+ 3. Split into evictable prefix and protected fresh tail (last `freshTailCount` raw messages).
127
+ 4. Compute fresh tail token cost (always included, even if over budget).
128
+ 5. Fill remaining budget from the evictable set, keeping newest items and dropping oldest.
129
+ 6. Normalize assistant content to array blocks (Anthropic API compatibility).
130
+ 7. Sanitize tool-use/result pairing (ensures every tool_result has a matching tool_use).
131
+
132
+ ### XML summary format
133
+
134
+ Summaries are presented to the model as user messages wrapped in XML:
135
+
136
+ ```xml
137
+ <summary id="sum_abc123" kind="leaf" depth="0" descendant_count="0"
138
+ earliest_at="2026-02-17T07:37:00" latest_at="2026-02-17T08:23:00">
139
+ <content>
140
+ ...summary text with timestamps...
141
+
142
+ Expand for details about: exact error messages, full config diff, intermediate debugging steps
143
+ </content>
144
+ </summary>
145
+ ```
146
+
147
+ Condensed summaries also include parent references:
148
+
149
+ ```xml
150
+ <summary id="sum_def456" kind="condensed" depth="1" descendant_count="8" ...>
151
+ <parents>
152
+ <summary_ref id="sum_aaa111" />
153
+ <summary_ref id="sum_bbb222" />
154
+ </parents>
155
+ <content>...</content>
156
+ </summary>
157
+ ```
158
+
159
+ The XML attributes give the model enough metadata to reason about summary age, scope, and how to drill deeper. The `<parents>` section enables targeted expansion of specific source summaries.
160
+
161
+ ## Expansion system
162
+
163
+ When summaries are too compressed for a task, agents use `lcm_expand_query` to recover detail.
164
+
165
+ ### How it works
166
+
167
+ 1. Agent calls `lcm_expand_query` with a `prompt` and either `summaryIds` or a `query`.
168
+ 2. If `query` is provided, `lcm_grep` finds matching summaries first.
169
+ 3. A **delegation grant** is created, scoping the sub-agent to the relevant conversation(s) with a token cap.
170
+ 4. A sub-agent session is spawned with the expansion task.
171
+ 5. The sub-agent walks the DAG: it can read summary content, follow parent links, access source messages, and inspect stored files.
172
+ 6. The sub-agent returns a focused answer (default ≤ 2000 tokens) with cited summary IDs.
173
+ 7. The grant is revoked and the sub-agent session is cleaned up.
174
+
175
+ ### Security model
176
+
177
+ Expansion uses a delegation grant system:
178
+
179
+ - **Grants** are created at spawn time, scoped to specific conversation IDs
180
+ - **Token caps** limit how much content the sub-agent can access
181
+ - **TTL** ensures grants expire even if cleanup fails
182
+ - **Revocation** happens on completion, cancellation, or sweep
183
+
184
+ The sub-agent only gets `lcm_expand` (the low-level tool), not `lcm_expand_query` — preventing recursive sub-agent spawning.
185
+
186
+ ## Large file handling
187
+
188
+ Files embedded in user messages (typically via `<file>` blocks from tool output) are checked at ingestion:
189
+
190
+ 1. Parse file blocks from message content.
191
+ 2. For each block exceeding `largeFileTokenThreshold` (default 25k tokens):
192
+ - Generate a unique file ID (`file_` prefix)
193
+ - Store the content to `~/.claude/lcm-files/<conversation_id>/<file_id>.<ext>`
194
+ - Generate a ~200 token exploration summary (structural analysis, key sections, etc.)
195
+ - Insert a `large_files` record with metadata
196
+ - Replace the file block in the message with a compact reference
197
+ 3. The `lcm_describe` tool can retrieve full file content by ID.
198
+
199
+ This prevents a single large file paste from consuming the entire context window while keeping the content accessible.
200
+
201
+ ## Session reconciliation
202
+
203
+ LCM handles crash recovery through **bootstrap reconciliation**:
204
+
205
+ 1. On session start, read the JSONL session file (Claude Code's ground truth).
206
+ 2. Compare against the LCM database.
207
+ 3. Find the most recent message that exists in both (the "anchor").
208
+ 4. Import any messages after the anchor that are in JSONL but not in LCM.
209
+
210
+ This handles the case where Claude Code wrote messages to the session file but crashed before LCM could persist them.
211
+
212
+ ## Operation serialization
213
+
214
+ All mutating operations (ingest, compact) are serialized per-session using a promise queue. This prevents races between concurrent afterTurn/compact calls for the same conversation without blocking operations on different conversations.
215
+
216
+ ## Authentication
217
+
218
+ LCM needs to call an LLM for summarization. It resolves credentials through a three-tier cascade:
219
+
220
+ 1. **Auth profiles** — Claude Code's OAuth/token/API-key profile system (`auth-profiles.json`), checked in priority order
221
+ 2. **Environment variables** — Standard provider env vars (`ANTHROPIC_API_KEY`, etc.)
222
+ 3. **Custom provider key** — From models config (e.g., `models.json`)
223
+
224
+ For OAuth providers (e.g., Anthropic via Claude Max), LCM handles token refresh and credential persistence automatically.