@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.
- package/.claude-plugin/commands/lcm-compact.md +31 -0
- package/.claude-plugin/commands/lcm-curate.md +31 -0
- package/.claude-plugin/commands/lcm-diagnose.md +29 -0
- package/.claude-plugin/commands/lcm-doctor.md +23 -0
- package/.claude-plugin/commands/lcm-dogfood.md +101 -0
- package/.claude-plugin/commands/lcm-import.md +48 -0
- package/.claude-plugin/commands/lcm-promote.md +29 -0
- package/.claude-plugin/commands/lcm-sensitive.md +55 -0
- package/.claude-plugin/commands/lcm-stats.md +19 -0
- package/.claude-plugin/commands/lcm-status.md +27 -0
- package/.claude-plugin/hooks/README.md +47 -0
- package/.claude-plugin/marketplace.json +20 -0
- package/.claude-plugin/mcp.mjs +12 -0
- package/.claude-plugin/plugin.json +46 -0
- package/.claude-plugin/skills/lcm-context/SKILL.md +107 -0
- package/.claude-plugin/skills/lcm-dogfood/SKILL.md +102 -0
- package/.claude-plugin/skills/lcm-dogfood/references/checks.md +239 -0
- package/.claude-plugin/skills/lcm-dogfood/references/known-issues.md +11 -0
- package/.claude-plugin/skills/lcm-dogfood/scripts/db-integrity.js +40 -0
- package/.claude-plugin/skills/lcm-dogfood/scripts/prompt-search-test.js +35 -0
- package/.claude-plugin/skills/lcm-e2e/SKILL.md +61 -0
- package/.claude-plugin/skills/lcm-e2e/checklist.md +367 -0
- package/.claude-plugin/skills/lossless-claude-upgrade/SKILL.md +47 -0
- package/LICENSE +21 -0
- package/README.md +231 -0
- package/dist/bin/lcm.d.ts +2 -0
- package/dist/bin/lcm.js +461 -0
- package/dist/bin/lcm.js.map +1 -0
- package/dist/installer/dry-run-deps.d.ts +23 -0
- package/dist/installer/dry-run-deps.js +66 -0
- package/dist/installer/dry-run-deps.js.map +1 -0
- package/dist/installer/install.d.ts +39 -0
- package/dist/installer/install.js +236 -0
- package/dist/installer/install.js.map +1 -0
- package/dist/installer/uninstall.d.ts +11 -0
- package/dist/installer/uninstall.js +80 -0
- package/dist/installer/uninstall.js.map +1 -0
- package/dist/src/batch-compact.d.ts +16 -0
- package/dist/src/batch-compact.js +121 -0
- package/dist/src/batch-compact.js.map +1 -0
- package/dist/src/compaction.d.ts +198 -0
- package/dist/src/compaction.js +964 -0
- package/dist/src/compaction.js.map +1 -0
- package/dist/src/connectors/constants.d.ts +5 -0
- package/dist/src/connectors/constants.js +6 -0
- package/dist/src/connectors/constants.js.map +1 -0
- package/dist/src/connectors/installer.d.ts +16 -0
- package/dist/src/connectors/installer.js +200 -0
- package/dist/src/connectors/installer.js.map +1 -0
- package/dist/src/connectors/registry.d.ts +4 -0
- package/dist/src/connectors/registry.js +264 -0
- package/dist/src/connectors/registry.js.map +1 -0
- package/dist/src/connectors/template-service.d.ts +5 -0
- package/dist/src/connectors/template-service.js +54 -0
- package/dist/src/connectors/template-service.js.map +1 -0
- package/dist/src/connectors/templates/base.md +1 -0
- package/dist/src/connectors/templates/mcp-base.md +1 -0
- package/dist/src/connectors/templates/sections/command-reference.md +15 -0
- package/dist/src/connectors/templates/sections/mcp-workflow.md +18 -0
- package/dist/src/connectors/templates/sections/workflow.md +29 -0
- package/dist/src/connectors/templates/skill/SKILL.md +74 -0
- package/dist/src/connectors/types.d.ts +19 -0
- package/dist/src/connectors/types.js +10 -0
- package/dist/src/connectors/types.js.map +1 -0
- package/dist/src/daemon/client.d.ts +9 -0
- package/dist/src/daemon/client.js +28 -0
- package/dist/src/daemon/client.js.map +1 -0
- package/dist/src/daemon/config.d.ts +48 -0
- package/dist/src/daemon/config.js +67 -0
- package/dist/src/daemon/config.js.map +1 -0
- package/dist/src/daemon/lifecycle.d.ts +19 -0
- package/dist/src/daemon/lifecycle.js +102 -0
- package/dist/src/daemon/lifecycle.js.map +1 -0
- package/dist/src/daemon/orientation.d.ts +1 -0
- package/dist/src/daemon/orientation.js +9 -0
- package/dist/src/daemon/orientation.js.map +1 -0
- package/dist/src/daemon/project-queue.d.ts +1 -0
- package/dist/src/daemon/project-queue.js +17 -0
- package/dist/src/daemon/project-queue.js.map +1 -0
- package/dist/src/daemon/project.d.ts +7 -0
- package/dist/src/daemon/project.js +25 -0
- package/dist/src/daemon/project.js.map +1 -0
- package/dist/src/daemon/proxy-manager.d.ts +21 -0
- package/dist/src/daemon/proxy-manager.js +205 -0
- package/dist/src/daemon/proxy-manager.js.map +1 -0
- package/dist/src/daemon/routes/compact.d.ts +13 -0
- package/dist/src/daemon/routes/compact.js +195 -0
- package/dist/src/daemon/routes/compact.js.map +1 -0
- package/dist/src/daemon/routes/describe.d.ts +3 -0
- package/dist/src/daemon/routes/describe.js +39 -0
- package/dist/src/daemon/routes/describe.js.map +1 -0
- package/dist/src/daemon/routes/expand.d.ts +3 -0
- package/dist/src/daemon/routes/expand.js +41 -0
- package/dist/src/daemon/routes/expand.js.map +1 -0
- package/dist/src/daemon/routes/grep.d.ts +3 -0
- package/dist/src/daemon/routes/grep.js +43 -0
- package/dist/src/daemon/routes/grep.js.map +1 -0
- package/dist/src/daemon/routes/ingest.d.ts +3 -0
- package/dist/src/daemon/routes/ingest.js +101 -0
- package/dist/src/daemon/routes/ingest.js.map +1 -0
- package/dist/src/daemon/routes/promote.d.ts +4 -0
- package/dist/src/daemon/routes/promote.js +104 -0
- package/dist/src/daemon/routes/promote.js.map +1 -0
- package/dist/src/daemon/routes/prompt-search.d.ts +3 -0
- package/dist/src/daemon/routes/prompt-search.js +65 -0
- package/dist/src/daemon/routes/prompt-search.js.map +1 -0
- package/dist/src/daemon/routes/recent.d.ts +3 -0
- package/dist/src/daemon/routes/recent.js +37 -0
- package/dist/src/daemon/routes/recent.js.map +1 -0
- package/dist/src/daemon/routes/restore.d.ts +3 -0
- package/dist/src/daemon/routes/restore.js +120 -0
- package/dist/src/daemon/routes/restore.js.map +1 -0
- package/dist/src/daemon/routes/search.d.ts +2 -0
- package/dist/src/daemon/routes/search.js +66 -0
- package/dist/src/daemon/routes/search.js.map +1 -0
- package/dist/src/daemon/routes/status.d.ts +3 -0
- package/dist/src/daemon/routes/status.js +80 -0
- package/dist/src/daemon/routes/status.js.map +1 -0
- package/dist/src/daemon/routes/store.d.ts +2 -0
- package/dist/src/daemon/routes/store.js +46 -0
- package/dist/src/daemon/routes/store.js.map +1 -0
- package/dist/src/daemon/server.d.ts +19 -0
- package/dist/src/daemon/server.js +183 -0
- package/dist/src/daemon/server.js.map +1 -0
- package/dist/src/daemon/summarizer.d.ts +11 -0
- package/dist/src/daemon/summarizer.js +51 -0
- package/dist/src/daemon/summarizer.js.map +1 -0
- package/dist/src/db/config.d.ts +31 -0
- package/dist/src/db/config.js +83 -0
- package/dist/src/db/config.js.map +1 -0
- package/dist/src/db/connection.d.ts +3 -0
- package/dist/src/db/connection.js +62 -0
- package/dist/src/db/connection.js.map +1 -0
- package/dist/src/db/features.d.ts +11 -0
- package/dist/src/db/features.js +36 -0
- package/dist/src/db/features.js.map +1 -0
- package/dist/src/db/migration.d.ts +4 -0
- package/dist/src/db/migration.js +499 -0
- package/dist/src/db/migration.js.map +1 -0
- package/dist/src/db/promoted.d.ts +47 -0
- package/dist/src/db/promoted.js +96 -0
- package/dist/src/db/promoted.js.map +1 -0
- package/dist/src/db/redaction-stats.d.ts +6 -0
- package/dist/src/db/redaction-stats.js +16 -0
- package/dist/src/db/redaction-stats.js.map +1 -0
- package/dist/src/diagnose.d.ts +39 -0
- package/dist/src/diagnose.js +432 -0
- package/dist/src/diagnose.js.map +1 -0
- package/dist/src/doctor/doctor.d.ts +4 -0
- package/dist/src/doctor/doctor.js +378 -0
- package/dist/src/doctor/doctor.js.map +1 -0
- package/dist/src/doctor/types.d.ts +24 -0
- package/dist/src/doctor/types.js +2 -0
- package/dist/src/doctor/types.js.map +1 -0
- package/dist/src/expansion.d.ts +100 -0
- package/dist/src/expansion.js +268 -0
- package/dist/src/expansion.js.map +1 -0
- package/dist/src/hooks/auto-heal.d.ts +12 -0
- package/dist/src/hooks/auto-heal.js +49 -0
- package/dist/src/hooks/auto-heal.js.map +1 -0
- package/dist/src/hooks/compact.d.ts +5 -0
- package/dist/src/hooks/compact.js +22 -0
- package/dist/src/hooks/compact.js.map +1 -0
- package/dist/src/hooks/dispatch.d.ts +7 -0
- package/dist/src/hooks/dispatch.js +36 -0
- package/dist/src/hooks/dispatch.js.map +1 -0
- package/dist/src/hooks/probe-precompact.d.ts +2 -0
- package/dist/src/hooks/probe-precompact.js +17 -0
- package/dist/src/hooks/probe-precompact.js.map +1 -0
- package/dist/src/hooks/probe-sessionstart.d.ts +2 -0
- package/dist/src/hooks/probe-sessionstart.js +18 -0
- package/dist/src/hooks/probe-sessionstart.js.map +1 -0
- package/dist/src/hooks/restore.d.ts +5 -0
- package/dist/src/hooks/restore.js +19 -0
- package/dist/src/hooks/restore.js.map +1 -0
- package/dist/src/hooks/session-end.d.ts +16 -0
- package/dist/src/hooks/session-end.js +73 -0
- package/dist/src/hooks/session-end.js.map +1 -0
- package/dist/src/hooks/user-prompt.d.ts +5 -0
- package/dist/src/hooks/user-prompt.js +31 -0
- package/dist/src/hooks/user-prompt.js.map +1 -0
- package/dist/src/import.d.ts +24 -0
- package/dist/src/import.js +119 -0
- package/dist/src/import.js.map +1 -0
- package/dist/src/large-files.d.ts +28 -0
- package/dist/src/large-files.js +413 -0
- package/dist/src/large-files.js.map +1 -0
- package/dist/src/llm/anthropic.d.ts +9 -0
- package/dist/src/llm/anthropic.js +54 -0
- package/dist/src/llm/anthropic.js.map +1 -0
- package/dist/src/llm/claude-process.d.ts +2 -0
- package/dist/src/llm/claude-process.js +55 -0
- package/dist/src/llm/claude-process.js.map +1 -0
- package/dist/src/llm/codex-process.d.ts +15 -0
- package/dist/src/llm/codex-process.js +142 -0
- package/dist/src/llm/codex-process.js.map +1 -0
- package/dist/src/llm/mock-summarizer.d.ts +9 -0
- package/dist/src/llm/mock-summarizer.js +17 -0
- package/dist/src/llm/mock-summarizer.js.map +1 -0
- package/dist/src/llm/openai.d.ts +10 -0
- package/dist/src/llm/openai.js +52 -0
- package/dist/src/llm/openai.js.map +1 -0
- package/dist/src/llm/types.d.ts +6 -0
- package/dist/src/llm/types.js +2 -0
- package/dist/src/llm/types.js.map +1 -0
- package/dist/src/mcp/server.d.ts +9 -0
- package/dist/src/mcp/server.js +128 -0
- package/dist/src/mcp/server.js.map +1 -0
- package/dist/src/mcp/tools/lcm-describe.d.ts +14 -0
- package/dist/src/mcp/tools/lcm-describe.js +12 -0
- package/dist/src/mcp/tools/lcm-describe.js.map +1 -0
- package/dist/src/mcp/tools/lcm-doctor.d.ts +8 -0
- package/dist/src/mcp/tools/lcm-doctor.js +9 -0
- package/dist/src/mcp/tools/lcm-doctor.js.map +1 -0
- package/dist/src/mcp/tools/lcm-expand.d.ts +18 -0
- package/dist/src/mcp/tools/lcm-expand.js +13 -0
- package/dist/src/mcp/tools/lcm-expand.js.map +1 -0
- package/dist/src/mcp/tools/lcm-grep.d.ts +27 -0
- package/dist/src/mcp/tools/lcm-grep.js +15 -0
- package/dist/src/mcp/tools/lcm-grep.js.map +1 -0
- package/dist/src/mcp/tools/lcm-search.d.ts +33 -0
- package/dist/src/mcp/tools/lcm-search.js +15 -0
- package/dist/src/mcp/tools/lcm-search.js.map +1 -0
- package/dist/src/mcp/tools/lcm-stats.d.ts +14 -0
- package/dist/src/mcp/tools/lcm-stats.js +11 -0
- package/dist/src/mcp/tools/lcm-stats.js.map +1 -0
- package/dist/src/mcp/tools/lcm-store.d.ts +26 -0
- package/dist/src/mcp/tools/lcm-store.js +22 -0
- package/dist/src/mcp/tools/lcm-store.js.map +1 -0
- package/dist/src/memory/index.d.ts +22 -0
- package/dist/src/memory/index.js +21 -0
- package/dist/src/memory/index.js.map +1 -0
- package/dist/src/promotion/dedup.d.ts +19 -0
- package/dist/src/promotion/dedup.js +42 -0
- package/dist/src/promotion/dedup.js.map +1 -0
- package/dist/src/promotion/detector.d.ts +15 -0
- package/dist/src/promotion/detector.js +31 -0
- package/dist/src/promotion/detector.js.map +1 -0
- package/dist/src/prompts/condensed-d1.yaml +38 -0
- package/dist/src/prompts/condensed-d2.yaml +32 -0
- package/dist/src/prompts/condensed-d3plus.yaml +32 -0
- package/dist/src/prompts/leaf-aggressive.yaml +34 -0
- package/dist/src/prompts/leaf-normal.yaml +34 -0
- package/dist/src/prompts/loader.d.ts +9 -0
- package/dist/src/prompts/loader.js +37 -0
- package/dist/src/prompts/loader.js.map +1 -0
- package/dist/src/prompts/promoted-merge.yaml +18 -0
- package/dist/src/prompts/system.yaml +5 -0
- package/dist/src/retrieval.d.ts +122 -0
- package/dist/src/retrieval.js +214 -0
- package/dist/src/retrieval.js.map +1 -0
- package/dist/src/scrub.d.ts +46 -0
- package/dist/src/scrub.js +184 -0
- package/dist/src/scrub.js.map +1 -0
- package/dist/src/sensitive.d.ts +4 -0
- package/dist/src/sensitive.js +258 -0
- package/dist/src/sensitive.js.map +1 -0
- package/dist/src/stats.d.ts +34 -0
- package/dist/src/stats.js +260 -0
- package/dist/src/stats.js.map +1 -0
- package/dist/src/store/conversation-store.d.ts +115 -0
- package/dist/src/store/conversation-store.js +447 -0
- package/dist/src/store/conversation-store.js.map +1 -0
- package/dist/src/store/fts5-sanitize.d.ts +23 -0
- package/dist/src/store/fts5-sanitize.js +30 -0
- package/dist/src/store/fts5-sanitize.js.map +1 -0
- package/dist/src/store/full-text-fallback.d.ts +16 -0
- package/dist/src/store/full-text-fallback.js +60 -0
- package/dist/src/store/full-text-fallback.js.map +1 -0
- package/dist/src/store/index.d.ts +4 -0
- package/dist/src/store/index.js +3 -0
- package/dist/src/store/index.js.map +1 -0
- package/dist/src/store/summary-store.d.ts +118 -0
- package/dist/src/store/summary-store.js +570 -0
- package/dist/src/store/summary-store.js.map +1 -0
- package/dist/src/summarize.d.ts +59 -0
- package/dist/src/summarize.js +619 -0
- package/dist/src/summarize.js.map +1 -0
- package/dist/src/transcript.d.ts +7 -0
- package/dist/src/transcript.js +51 -0
- package/dist/src/transcript.js.map +1 -0
- package/dist/src/types.d.ts +116 -0
- package/dist/src/types.js +8 -0
- package/dist/src/types.js.map +1 -0
- package/docs/agent-tools.md +187 -0
- package/docs/architecture.md +224 -0
- package/docs/configuration.md +168 -0
- package/docs/fts5.md +161 -0
- package/docs/hook-protocol.md +41 -0
- package/docs/privacy.md +101 -0
- package/mcp.mjs +17 -0
- package/package.json +79 -0
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import { Type } from "@sinclair/typebox";
|
|
2
|
+
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
3
|
+
const SNIPPET_MAX_CHARS = 200;
|
|
4
|
+
/** Truncate content to a short snippet for display. */
|
|
5
|
+
function truncateSnippet(content, maxChars = SNIPPET_MAX_CHARS) {
|
|
6
|
+
if (content.length <= maxChars) {
|
|
7
|
+
return content;
|
|
8
|
+
}
|
|
9
|
+
return content.slice(0, maxChars) + "...";
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Resolve the effective expansion token cap by applying a configured default
|
|
13
|
+
* and an explicit upper bound.
|
|
14
|
+
*/
|
|
15
|
+
export function resolveExpansionTokenCap(input) {
|
|
16
|
+
const maxExpandTokens = Math.max(1, Math.trunc(input.maxExpandTokens));
|
|
17
|
+
const requestedTokenCap = input.requestedTokenCap;
|
|
18
|
+
if (typeof requestedTokenCap !== "number" || !Number.isFinite(requestedTokenCap)) {
|
|
19
|
+
return maxExpandTokens;
|
|
20
|
+
}
|
|
21
|
+
return Math.min(Math.max(1, Math.trunc(requestedTokenCap)), maxExpandTokens);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Convert a single RetrievalEngine.expand() result into the ExpansionResult
|
|
25
|
+
* entry format, truncating content to short snippets.
|
|
26
|
+
*/
|
|
27
|
+
function toExpansionEntry(summaryId, raw) {
|
|
28
|
+
return {
|
|
29
|
+
summaryId,
|
|
30
|
+
children: raw.children.map((c) => ({
|
|
31
|
+
summaryId: c.summaryId,
|
|
32
|
+
kind: c.kind,
|
|
33
|
+
snippet: truncateSnippet(c.content),
|
|
34
|
+
tokenCount: c.tokenCount,
|
|
35
|
+
})),
|
|
36
|
+
messages: raw.messages.map((m) => ({
|
|
37
|
+
messageId: m.messageId,
|
|
38
|
+
role: m.role,
|
|
39
|
+
snippet: truncateSnippet(m.content),
|
|
40
|
+
tokenCount: m.tokenCount,
|
|
41
|
+
})),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/** Collect all referenced summary IDs from an expansion entry. */
|
|
45
|
+
function collectCitedIds(entry) {
|
|
46
|
+
const ids = [entry.summaryId];
|
|
47
|
+
for (const child of entry.children) {
|
|
48
|
+
ids.push(child.summaryId);
|
|
49
|
+
}
|
|
50
|
+
return ids;
|
|
51
|
+
}
|
|
52
|
+
// ── ExpansionOrchestrator ────────────────────────────────────────────────────
|
|
53
|
+
export class ExpansionOrchestrator {
|
|
54
|
+
retrieval;
|
|
55
|
+
constructor(retrieval) {
|
|
56
|
+
this.retrieval = retrieval;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Expand each summary ID using the RetrievalEngine, collecting results and
|
|
60
|
+
* enforcing a global token cap across all expansions.
|
|
61
|
+
*/
|
|
62
|
+
async expand(request) {
|
|
63
|
+
const maxDepth = request.maxDepth ?? 3;
|
|
64
|
+
const tokenCap = request.tokenCap ?? Infinity;
|
|
65
|
+
const includeMessages = request.includeMessages ?? false;
|
|
66
|
+
const result = {
|
|
67
|
+
expansions: [],
|
|
68
|
+
citedIds: [],
|
|
69
|
+
totalTokens: 0,
|
|
70
|
+
truncated: false,
|
|
71
|
+
};
|
|
72
|
+
const citedSet = new Set();
|
|
73
|
+
for (const summaryId of request.summaryIds) {
|
|
74
|
+
if (result.truncated) {
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
// Calculate remaining budget for this expansion
|
|
78
|
+
const remainingBudget = tokenCap - result.totalTokens;
|
|
79
|
+
if (remainingBudget <= 0) {
|
|
80
|
+
result.truncated = true;
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
const raw = await this.retrieval.expand({
|
|
84
|
+
summaryId,
|
|
85
|
+
depth: maxDepth,
|
|
86
|
+
includeMessages,
|
|
87
|
+
tokenCap: remainingBudget,
|
|
88
|
+
});
|
|
89
|
+
const entry = toExpansionEntry(summaryId, raw);
|
|
90
|
+
result.expansions.push(entry);
|
|
91
|
+
result.totalTokens += raw.estimatedTokens;
|
|
92
|
+
// Track cited IDs
|
|
93
|
+
for (const id of collectCitedIds(entry)) {
|
|
94
|
+
citedSet.add(id);
|
|
95
|
+
}
|
|
96
|
+
if (raw.truncated) {
|
|
97
|
+
result.truncated = true;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
result.citedIds = [...citedSet];
|
|
101
|
+
return result;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Convenience method: grep for matching summaries, then expand the top results.
|
|
105
|
+
* Combines the routing pass (grep) with the deep expansion pass.
|
|
106
|
+
*/
|
|
107
|
+
async describeAndExpand(input) {
|
|
108
|
+
const grepResult = await this.retrieval.grep({
|
|
109
|
+
query: input.query,
|
|
110
|
+
mode: input.mode,
|
|
111
|
+
scope: "summaries",
|
|
112
|
+
conversationId: input.conversationId,
|
|
113
|
+
});
|
|
114
|
+
const summaryIds = [...grepResult.summaries]
|
|
115
|
+
.sort((a, b) => {
|
|
116
|
+
const recencyDelta = b.createdAt.getTime() - a.createdAt.getTime();
|
|
117
|
+
if (recencyDelta !== 0) {
|
|
118
|
+
return recencyDelta;
|
|
119
|
+
}
|
|
120
|
+
const aRank = a.rank ?? Number.POSITIVE_INFINITY;
|
|
121
|
+
const bRank = b.rank ?? Number.POSITIVE_INFINITY;
|
|
122
|
+
return aRank - bRank;
|
|
123
|
+
})
|
|
124
|
+
.map((s) => s.summaryId);
|
|
125
|
+
if (summaryIds.length === 0) {
|
|
126
|
+
return {
|
|
127
|
+
expansions: [],
|
|
128
|
+
citedIds: [],
|
|
129
|
+
totalTokens: 0,
|
|
130
|
+
truncated: false,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
return this.expand({
|
|
134
|
+
summaryIds,
|
|
135
|
+
maxDepth: input.maxDepth,
|
|
136
|
+
tokenCap: input.tokenCap,
|
|
137
|
+
includeMessages: false,
|
|
138
|
+
conversationId: input.conversationId ?? 0,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// ── Distill for subagent ─────────────────────────────────────────────────────
|
|
143
|
+
/**
|
|
144
|
+
* Format an ExpansionResult into a compact text payload suitable for passing
|
|
145
|
+
* to a subagent or returning to the main agent.
|
|
146
|
+
*/
|
|
147
|
+
export function distillForSubagent(result) {
|
|
148
|
+
const lines = [];
|
|
149
|
+
lines.push(`## Expansion Results (${result.expansions.length} summaries, ${result.totalTokens} total tokens)`);
|
|
150
|
+
lines.push("");
|
|
151
|
+
for (const entry of result.expansions) {
|
|
152
|
+
// Determine kind from children presence: if it has children it was a condensed node
|
|
153
|
+
const kind = entry.children.length > 0 ? "condensed" : "leaf";
|
|
154
|
+
const tokenSum = entry.children.reduce((sum, c) => sum + c.tokenCount, 0) +
|
|
155
|
+
entry.messages.reduce((sum, m) => sum + m.tokenCount, 0);
|
|
156
|
+
lines.push(`### ${entry.summaryId} (${kind}, ${tokenSum} tokens)`);
|
|
157
|
+
if (entry.children.length > 0) {
|
|
158
|
+
lines.push(`Children: ${entry.children.map((c) => c.summaryId).join(", ")}`);
|
|
159
|
+
}
|
|
160
|
+
if (entry.messages.length > 0) {
|
|
161
|
+
const msgParts = entry.messages.map((m) => `msg#${m.messageId} (${m.role}, ${m.tokenCount} tokens)`);
|
|
162
|
+
lines.push(`Messages: ${msgParts.join(", ")}`);
|
|
163
|
+
}
|
|
164
|
+
// Show a snippet for children that have content
|
|
165
|
+
for (const child of entry.children) {
|
|
166
|
+
if (child.snippet) {
|
|
167
|
+
lines.push(`[Snippet: ${truncateSnippet(child.snippet)}]`);
|
|
168
|
+
break; // Only show one snippet per entry to keep it compact
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
lines.push("");
|
|
172
|
+
}
|
|
173
|
+
if (result.citedIds.length > 0) {
|
|
174
|
+
lines.push(`Cited IDs for follow-up: ${result.citedIds.join(", ")}`);
|
|
175
|
+
}
|
|
176
|
+
lines.push(`[Truncated: ${result.truncated ? "yes" : "no"}]`);
|
|
177
|
+
return lines.join("\n");
|
|
178
|
+
}
|
|
179
|
+
// ── Tool definition ──────────────────────────────────────────────────────────
|
|
180
|
+
const LcmExpansionSchema = Type.Object({
|
|
181
|
+
summaryIds: Type.Optional(Type.Array(Type.String(), {
|
|
182
|
+
description: "Summary IDs to expand (e.g. sum_abc123). Required if query is not provided.",
|
|
183
|
+
})),
|
|
184
|
+
query: Type.Optional(Type.String({
|
|
185
|
+
description: "Text query to grep for matching summaries before expanding. " +
|
|
186
|
+
"If provided, summaryIds is ignored and the top grep results are expanded instead.",
|
|
187
|
+
})),
|
|
188
|
+
maxDepth: Type.Optional(Type.Number({
|
|
189
|
+
description: "Max traversal depth per summary (default: 3).",
|
|
190
|
+
minimum: 1,
|
|
191
|
+
maximum: 10,
|
|
192
|
+
})),
|
|
193
|
+
tokenCap: Type.Optional(Type.Number({
|
|
194
|
+
description: "Max tokens across the entire expansion result.",
|
|
195
|
+
minimum: 1,
|
|
196
|
+
})),
|
|
197
|
+
includeMessages: Type.Optional(Type.Boolean({
|
|
198
|
+
description: "Whether to include raw source messages at leaf level (default: false).",
|
|
199
|
+
})),
|
|
200
|
+
});
|
|
201
|
+
/**
|
|
202
|
+
* Build a tool definition object for LCM expansion that can be registered as
|
|
203
|
+
* an agent tool. Follows the pattern used in `src/agents/tools/`.
|
|
204
|
+
*
|
|
205
|
+
* Requires an already-initialised ExpansionOrchestrator and an LcmConfig
|
|
206
|
+
* (for the default tokenCap).
|
|
207
|
+
*/
|
|
208
|
+
export function buildExpansionToolDefinition(options) {
|
|
209
|
+
const { orchestrator, config, conversationId } = options;
|
|
210
|
+
return {
|
|
211
|
+
name: "lcm_expand",
|
|
212
|
+
description: "Expand compacted conversation summaries from LCM (Lossless Context Management). " +
|
|
213
|
+
"Traverses the summary DAG to retrieve children and source messages. " +
|
|
214
|
+
"Use this to drill into previously-compacted context when you need detail " +
|
|
215
|
+
"that was summarised away. Returns a compact text payload with cited IDs for follow-up.",
|
|
216
|
+
parameters: LcmExpansionSchema,
|
|
217
|
+
execute: async (_toolCallId, params) => {
|
|
218
|
+
const summaryIds = params.summaryIds;
|
|
219
|
+
const query = typeof params.query === "string" ? params.query.trim() : undefined;
|
|
220
|
+
const maxDepth = typeof params.maxDepth === "number" ? Math.trunc(params.maxDepth) : undefined;
|
|
221
|
+
const requestedTokenCap = typeof params.tokenCap === "number" ? Math.trunc(params.tokenCap) : undefined;
|
|
222
|
+
const tokenCap = resolveExpansionTokenCap({
|
|
223
|
+
requestedTokenCap,
|
|
224
|
+
maxExpandTokens: config.maxExpandTokens,
|
|
225
|
+
});
|
|
226
|
+
const includeMessages = typeof params.includeMessages === "boolean" ? params.includeMessages : false;
|
|
227
|
+
let result;
|
|
228
|
+
if (query) {
|
|
229
|
+
// Grep-first path: find summaries matching the query, then expand
|
|
230
|
+
result = await orchestrator.describeAndExpand({
|
|
231
|
+
query,
|
|
232
|
+
mode: "full_text",
|
|
233
|
+
conversationId,
|
|
234
|
+
maxDepth,
|
|
235
|
+
tokenCap,
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
else if (summaryIds && summaryIds.length > 0) {
|
|
239
|
+
// Direct expansion of specific summary IDs
|
|
240
|
+
result = await orchestrator.expand({
|
|
241
|
+
summaryIds,
|
|
242
|
+
maxDepth,
|
|
243
|
+
tokenCap,
|
|
244
|
+
includeMessages,
|
|
245
|
+
conversationId,
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
const text = "Error: either summaryIds or query must be provided.";
|
|
250
|
+
return {
|
|
251
|
+
content: [{ type: "text", text }],
|
|
252
|
+
details: { error: text },
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
const distilled = distillForSubagent(result);
|
|
256
|
+
return {
|
|
257
|
+
content: [{ type: "text", text: distilled }],
|
|
258
|
+
details: {
|
|
259
|
+
expansionCount: result.expansions.length,
|
|
260
|
+
citedIds: result.citedIds,
|
|
261
|
+
totalTokens: result.totalTokens,
|
|
262
|
+
truncated: result.truncated,
|
|
263
|
+
},
|
|
264
|
+
};
|
|
265
|
+
},
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
//# sourceMappingURL=expansion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expansion.js","sourceRoot":"","sources":["../../src/expansion.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface AutoHealDeps {
|
|
2
|
+
readFileSync: (path: string, encoding: string) => string;
|
|
3
|
+
writeFileSync: (path: string, data: string) => void;
|
|
4
|
+
existsSync: (path: string) => boolean;
|
|
5
|
+
mkdirSync: (path: string, opts?: {
|
|
6
|
+
recursive: boolean;
|
|
7
|
+
}) => void;
|
|
8
|
+
appendFileSync: (path: string, data: string) => void;
|
|
9
|
+
settingsPath: string;
|
|
10
|
+
logPath: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function validateAndFixHooks(deps?: AutoHealDeps): void;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync, appendFileSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join, dirname } from "node:path";
|
|
4
|
+
import { REQUIRED_HOOKS, mergeClaudeSettings } from "../../installer/install.js";
|
|
5
|
+
function defaultDeps() {
|
|
6
|
+
return {
|
|
7
|
+
readFileSync: (p, enc) => readFileSync(p, enc),
|
|
8
|
+
writeFileSync,
|
|
9
|
+
existsSync,
|
|
10
|
+
mkdirSync,
|
|
11
|
+
appendFileSync,
|
|
12
|
+
settingsPath: join(homedir(), ".claude", "settings.json"),
|
|
13
|
+
logPath: join(homedir(), ".lossless-claude", "auto-heal.log"),
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
function hasHookCommand(entries, command) {
|
|
17
|
+
return entries.some((entry) => Array.isArray(entry.hooks) && entry.hooks.some((h) => h.command === command));
|
|
18
|
+
}
|
|
19
|
+
export function validateAndFixHooks(deps = defaultDeps()) {
|
|
20
|
+
try {
|
|
21
|
+
if (!deps.existsSync(deps.settingsPath))
|
|
22
|
+
return;
|
|
23
|
+
const settings = JSON.parse(deps.readFileSync(deps.settingsPath, "utf-8"));
|
|
24
|
+
// Hooks are owned by plugin.json — if they leaked into settings.json
|
|
25
|
+
// (from old installer or manual edits), remove them to prevent double-firing.
|
|
26
|
+
const hooks = settings.hooks ?? {};
|
|
27
|
+
const hasDuplicates = REQUIRED_HOOKS.some(({ event, command }) => {
|
|
28
|
+
const entries = hooks[event];
|
|
29
|
+
return Array.isArray(entries) && hasHookCommand(entries, command);
|
|
30
|
+
});
|
|
31
|
+
if (!hasDuplicates)
|
|
32
|
+
return;
|
|
33
|
+
// Clean up: remove lcm hooks from settings.json (MCP config is preserved)
|
|
34
|
+
const merged = mergeClaudeSettings(settings);
|
|
35
|
+
deps.mkdirSync(dirname(deps.settingsPath), { recursive: true });
|
|
36
|
+
deps.writeFileSync(deps.settingsPath, JSON.stringify(merged, null, 2));
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
try {
|
|
40
|
+
deps.mkdirSync(dirname(deps.logPath), { recursive: true });
|
|
41
|
+
const msg = `[${new Date().toISOString()}] auto-heal error: ${err instanceof Error ? err.message : String(err)}\n`;
|
|
42
|
+
deps.appendFileSync(deps.logPath, msg);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// Last resort: silently fail
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=auto-heal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-heal.js","sourceRoot":"","sources":["../../../src/hooks/auto-heal.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ensureDaemon } from "../daemon/lifecycle.js";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
export async function handlePreCompact(stdin, client, port) {
|
|
5
|
+
const daemonPort = port ?? 3737;
|
|
6
|
+
const pidFilePath = join(homedir(), ".lossless-claude", "daemon.pid");
|
|
7
|
+
const { connected } = await ensureDaemon({ port: daemonPort, pidFilePath, spawnTimeoutMs: 5000 });
|
|
8
|
+
if (!connected)
|
|
9
|
+
return { exitCode: 0, stdout: "" };
|
|
10
|
+
try {
|
|
11
|
+
const input = JSON.parse(stdin || "{}");
|
|
12
|
+
const result = await client.post("/compact", {
|
|
13
|
+
...input,
|
|
14
|
+
client: "claude",
|
|
15
|
+
});
|
|
16
|
+
return { exitCode: 2, stdout: result.summary || "" };
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return { exitCode: 0, stdout: "" };
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=compact.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compact.js","sourceRoot":"","sources":["../../../src/hooks/compact.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,KAAa,EAAE,MAAoB,EAAE,IAAa;IACvF,MAAM,UAAU,GAAG,IAAI,IAAI,IAAI,CAAC;IAChC,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,kBAAkB,EAAE,YAAY,CAAC,CAAC;IACtE,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,YAAY,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC;IAClG,IAAI,CAAC,SAAS;QAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IAEnD,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC;QACxC,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,IAAI,CAAsB,UAAU,EAAE;YAChE,GAAG,KAAK;YACR,MAAM,EAAE,QAAQ;SACjB,CAAC,CAAC;QACH,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC;IACvD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IACrC,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const HOOK_COMMANDS: readonly ["compact", "restore", "session-end", "user-prompt"];
|
|
2
|
+
export type HookCommand = typeof HOOK_COMMANDS[number];
|
|
3
|
+
export declare function isHookCommand(cmd: string): cmd is HookCommand;
|
|
4
|
+
export declare function dispatchHook(command: HookCommand, stdinText: string): Promise<{
|
|
5
|
+
exitCode: number;
|
|
6
|
+
stdout: string;
|
|
7
|
+
}>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { validateAndFixHooks } from "./auto-heal.js";
|
|
2
|
+
export const HOOK_COMMANDS = ["compact", "restore", "session-end", "user-prompt"];
|
|
3
|
+
export function isHookCommand(cmd) {
|
|
4
|
+
return HOOK_COMMANDS.includes(cmd);
|
|
5
|
+
}
|
|
6
|
+
export async function dispatchHook(command, stdinText) {
|
|
7
|
+
validateAndFixHooks();
|
|
8
|
+
const { DaemonClient } = await import("../daemon/client.js");
|
|
9
|
+
const { loadDaemonConfig } = await import("../daemon/config.js");
|
|
10
|
+
const { join } = await import("node:path");
|
|
11
|
+
const { homedir } = await import("node:os");
|
|
12
|
+
const config = loadDaemonConfig(join(homedir(), ".lossless-claude", "config.json"));
|
|
13
|
+
const port = config.daemon?.port ?? 3737;
|
|
14
|
+
const client = new DaemonClient(`http://127.0.0.1:${port}`);
|
|
15
|
+
switch (command) {
|
|
16
|
+
case "compact": {
|
|
17
|
+
const { handlePreCompact } = await import("./compact.js");
|
|
18
|
+
return handlePreCompact(stdinText, client, port);
|
|
19
|
+
}
|
|
20
|
+
case "restore": {
|
|
21
|
+
const { handleSessionStart } = await import("./restore.js");
|
|
22
|
+
return handleSessionStart(stdinText, client, port);
|
|
23
|
+
}
|
|
24
|
+
case "session-end": {
|
|
25
|
+
const { handleSessionEnd } = await import("./session-end.js");
|
|
26
|
+
return handleSessionEnd(stdinText, client, port);
|
|
27
|
+
}
|
|
28
|
+
case "user-prompt": {
|
|
29
|
+
const { handleUserPromptSubmit } = await import("./user-prompt.js");
|
|
30
|
+
return handleUserPromptSubmit(stdinText, client, port);
|
|
31
|
+
}
|
|
32
|
+
default:
|
|
33
|
+
throw new Error(`Unknown hook command: ${command}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=dispatch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dispatch.js","sourceRoot":"","sources":["../../../src/hooks/dispatch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAErD,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,aAAa,EAAE,aAAa,CAAU,CAAC;AAG3F,MAAM,UAAU,aAAa,CAAC,GAAW;IACvC,OAAQ,aAAmC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;AAC5D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,OAAoB,EACpB,SAAiB;IAEjB,mBAAmB,EAAE,CAAC;IAEtB,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,qBAAqB,CAAC,CAAC;IAC7D,MAAM,EAAE,gBAAgB,EAAE,GAAG,MAAM,MAAM,CAAC,qBAAqB,CAAC,CAAC;IACjE,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC;IAC3C,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;IAC5C,MAAM,MAAM,GAAG,gBAAgB,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,kBAAkB,EAAE,aAAa,CAAC,CAAC,CAAC;IACpF,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,IAAI,IAAI,IAAI,CAAC;IACzC,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC,oBAAoB,IAAI,EAAE,CAAC,CAAC;IAE5D,QAAQ,OAAO,EAAE,CAAC;QAChB,KAAK,SAAS,CAAC,CAAC,CAAC;YACf,MAAM,EAAE,gBAAgB,EAAE,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,CAAC;YAC1D,OAAO,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;QACnD,CAAC;QACD,KAAK,SAAS,CAAC,CAAC,CAAC;YACf,MAAM,EAAE,kBAAkB,EAAE,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,CAAC;YAC5D,OAAO,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;QACrD,CAAC;QACD,KAAK,aAAa,CAAC,CAAC,CAAC;YACnB,MAAM,EAAE,gBAAgB,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAC;YAC9D,OAAO,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;QACnD,CAAC;QACD,KAAK,aAAa,CAAC,CAAC,CAAC;YACnB,MAAM,EAAE,sBAAsB,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAC;YACpE,OAAO,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;QACzD,CAAC;QACD;YACE,MAAM,IAAI,KAAK,CAAC,yBAAyB,OAAO,EAAE,CAAC,CAAC;IACxD,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// src/hooks/probe-precompact.ts
|
|
3
|
+
// Probe hook: dumps PreCompact stdin to ~/.lossless-claude/precompact-probe.json
|
|
4
|
+
// Install temporarily in ~/.claude/settings.json to verify hook input schema
|
|
5
|
+
import { writeFileSync, mkdirSync } from "node:fs";
|
|
6
|
+
import { homedir } from "node:os";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
const chunks = [];
|
|
9
|
+
process.stdin.on("data", (c) => chunks.push(c));
|
|
10
|
+
process.stdin.on("end", () => {
|
|
11
|
+
const raw = Buffer.concat(chunks).toString("utf-8");
|
|
12
|
+
const dir = join(homedir(), ".lossless-claude");
|
|
13
|
+
mkdirSync(dir, { recursive: true });
|
|
14
|
+
writeFileSync(join(dir, "precompact-probe.json"), raw);
|
|
15
|
+
process.exit(0); // exit 0 = allow native compaction
|
|
16
|
+
});
|
|
17
|
+
//# sourceMappingURL=probe-precompact.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"probe-precompact.js","sourceRoot":"","sources":["../../../src/hooks/probe-precompact.ts"],"names":[],"mappings":";AACA,gCAAgC;AAChC,iFAAiF;AACjF,6EAA6E;AAC7E,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,MAAM,GAAa,EAAE,CAAC;AAC5B,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAS,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AACxD,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;IAC3B,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IACpD,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,kBAAkB,CAAC,CAAC;IAChD,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACpC,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,uBAAuB,CAAC,EAAE,GAAG,CAAC,CAAC;IACvD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,mCAAmC;AACtD,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// src/hooks/probe-sessionstart.ts
|
|
3
|
+
// Probe hook: appends SessionStart stdin to ~/.lossless-claude/sessionstart-probe.jsonl
|
|
4
|
+
// Install temporarily to verify source field values (especially "compact")
|
|
5
|
+
import { appendFileSync, mkdirSync } from "node:fs";
|
|
6
|
+
import { homedir } from "node:os";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
const chunks = [];
|
|
9
|
+
process.stdin.on("data", (c) => chunks.push(c));
|
|
10
|
+
process.stdin.on("end", () => {
|
|
11
|
+
const raw = Buffer.concat(chunks).toString("utf-8");
|
|
12
|
+
const dir = join(homedir(), ".lossless-claude");
|
|
13
|
+
mkdirSync(dir, { recursive: true });
|
|
14
|
+
const entry = `${new Date().toISOString()} ${raw}\n`;
|
|
15
|
+
appendFileSync(join(dir, "sessionstart-probe.jsonl"), entry);
|
|
16
|
+
process.exit(0);
|
|
17
|
+
});
|
|
18
|
+
//# sourceMappingURL=probe-sessionstart.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"probe-sessionstart.js","sourceRoot":"","sources":["../../../src/hooks/probe-sessionstart.ts"],"names":[],"mappings":";AACA,kCAAkC;AAClC,wFAAwF;AACxF,2EAA2E;AAC3E,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,MAAM,GAAa,EAAE,CAAC;AAC5B,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAS,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AACxD,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;IAC3B,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IACpD,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,kBAAkB,CAAC,CAAC;IAChD,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACpC,MAAM,KAAK,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,IAAI,GAAG,IAAI,CAAC;IACrD,cAAc,CAAC,IAAI,CAAC,GAAG,EAAE,0BAA0B,CAAC,EAAE,KAAK,CAAC,CAAC;IAC7D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ensureDaemon } from "../daemon/lifecycle.js";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
export async function handleSessionStart(stdin, client, port) {
|
|
5
|
+
const daemonPort = port ?? 3737;
|
|
6
|
+
const pidFilePath = join(homedir(), ".lossless-claude", "daemon.pid");
|
|
7
|
+
const { connected } = await ensureDaemon({ port: daemonPort, pidFilePath, spawnTimeoutMs: 5000 });
|
|
8
|
+
if (!connected)
|
|
9
|
+
return { exitCode: 0, stdout: "" };
|
|
10
|
+
try {
|
|
11
|
+
const input = JSON.parse(stdin || "{}");
|
|
12
|
+
const result = await client.post("/restore", input);
|
|
13
|
+
return { exitCode: 0, stdout: result.context || "" };
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return { exitCode: 0, stdout: "" };
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=restore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"restore.js","sourceRoot":"","sources":["../../../src/hooks/restore.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,KAAa,EAAE,MAAoB,EAAE,IAAa;IACzF,MAAM,UAAU,GAAG,IAAI,IAAI,IAAI,CAAC;IAChC,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,kBAAkB,EAAE,YAAY,CAAC,CAAC;IACtE,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,YAAY,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC;IAClG,IAAI,CAAC,SAAS;QAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IAEnD,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC;QACxC,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,IAAI,CAAsB,UAAU,EAAE,KAAK,CAAC,CAAC;QACzE,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC;IACvD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IACrC,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { DaemonClient } from "../daemon/client.js";
|
|
2
|
+
/**
|
|
3
|
+
* Fire a compact request to the daemon without blocking the hook process.
|
|
4
|
+
*
|
|
5
|
+
* Uses a raw http.request with socket.unref() so the Node.js event loop
|
|
6
|
+
* does not wait for a response — the process exits as soon as the request
|
|
7
|
+
* is sent. The daemon receives and processes the request independently.
|
|
8
|
+
*
|
|
9
|
+
* This is intentionally separate from DaemonClient.post() (which uses fetch
|
|
10
|
+
* and keeps the event loop alive until a response is received).
|
|
11
|
+
*/
|
|
12
|
+
export declare function fireCompactRequest(port: number, body: Record<string, unknown>): void;
|
|
13
|
+
export declare function handleSessionEnd(stdin: string, client: DaemonClient, port?: number): Promise<{
|
|
14
|
+
exitCode: number;
|
|
15
|
+
stdout: string;
|
|
16
|
+
}>;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { ensureDaemon } from "../daemon/lifecycle.js";
|
|
2
|
+
import { loadDaemonConfig } from "../daemon/config.js";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
import { request } from "node:http";
|
|
6
|
+
import { Buffer } from "node:buffer";
|
|
7
|
+
/**
|
|
8
|
+
* Fire a compact request to the daemon without blocking the hook process.
|
|
9
|
+
*
|
|
10
|
+
* Uses a raw http.request with socket.unref() so the Node.js event loop
|
|
11
|
+
* does not wait for a response — the process exits as soon as the request
|
|
12
|
+
* is sent. The daemon receives and processes the request independently.
|
|
13
|
+
*
|
|
14
|
+
* This is intentionally separate from DaemonClient.post() (which uses fetch
|
|
15
|
+
* and keeps the event loop alive until a response is received).
|
|
16
|
+
*/
|
|
17
|
+
export function fireCompactRequest(port, body) {
|
|
18
|
+
const json = JSON.stringify(body);
|
|
19
|
+
const req = request({
|
|
20
|
+
hostname: "127.0.0.1",
|
|
21
|
+
port,
|
|
22
|
+
path: "/compact",
|
|
23
|
+
method: "POST",
|
|
24
|
+
headers: {
|
|
25
|
+
"Content-Type": "application/json",
|
|
26
|
+
"Content-Length": Buffer.byteLength(json),
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
req.on("socket", (socket) => {
|
|
30
|
+
// Defer unref until after the request body is flushed so the /compact
|
|
31
|
+
// request reliably reaches the daemon before the process is allowed to exit.
|
|
32
|
+
req.on("finish", () => socket.unref());
|
|
33
|
+
});
|
|
34
|
+
req.on("error", () => { }); // non-fatal
|
|
35
|
+
req.write(json);
|
|
36
|
+
req.end();
|
|
37
|
+
}
|
|
38
|
+
export async function handleSessionEnd(stdin, client, port) {
|
|
39
|
+
const daemonPort = port ?? 3737;
|
|
40
|
+
const pidFilePath = join(homedir(), ".lossless-claude", "daemon.pid");
|
|
41
|
+
const { connected } = await ensureDaemon({
|
|
42
|
+
port: daemonPort,
|
|
43
|
+
pidFilePath,
|
|
44
|
+
spawnTimeoutMs: 5000,
|
|
45
|
+
});
|
|
46
|
+
if (!connected)
|
|
47
|
+
return { exitCode: 0, stdout: "" };
|
|
48
|
+
try {
|
|
49
|
+
const input = JSON.parse(stdin || "{}");
|
|
50
|
+
const ingestResult = await client.post("/ingest", input);
|
|
51
|
+
// Auto-compact if conversation exceeds threshold
|
|
52
|
+
const configPath = join(homedir(), ".lossless-claude", "config.json");
|
|
53
|
+
const config = loadDaemonConfig(configPath);
|
|
54
|
+
const threshold = config.compaction.autoCompactMinTokens;
|
|
55
|
+
if (threshold > 0 &&
|
|
56
|
+
typeof ingestResult.totalTokens === "number" &&
|
|
57
|
+
ingestResult.totalTokens >= threshold) {
|
|
58
|
+
// Fire-and-forget via unreffed http.request — does not block the event loop.
|
|
59
|
+
// The daemon receives and compacts independently after the hook process exits.
|
|
60
|
+
fireCompactRequest(daemonPort, {
|
|
61
|
+
session_id: input.session_id,
|
|
62
|
+
cwd: input.cwd,
|
|
63
|
+
skip_ingest: true,
|
|
64
|
+
client: "claude",
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
return { exitCode: 0, stdout: "" };
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return { exitCode: 0, stdout: "" };
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=session-end.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-end.js","sourceRoot":"","sources":["../../../src/hooks/session-end.ts"],"names":[],"mappings":"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"}
|