@ssweens/pi-compaxxt 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ssweens
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,78 @@
1
+ # pi-compaxxt
2
+
3
+ ```bash
4
+ pi install @ssweens/pi-compaxxt
5
+ ```
6
+
7
+ Enhanced compaction for [pi](https://github.com/badlogic/pi-mono). Two improvements to every compaction, zero extra LLM calls.
8
+
9
+ ## Features
10
+
11
+ ### Session Context
12
+
13
+ Every compaction summary is prepended with the current session file path and thread ID:
14
+
15
+ ```markdown
16
+ ## Session Context
17
+ **Session:** `/Users/you/.pi/agent/sessions/abc123/session.jsonl`
18
+ **Thread ID:** `e7f2a3c1-...`
19
+
20
+ Use the `session_query` tool to retrieve specific context from messages that were summarized away.
21
+
22
+ ---
23
+
24
+ ## Goal
25
+ ...
26
+ ```
27
+
28
+ After compaction, the LLM knows exactly where to look if it needs older context that was summarized away. The bundled `session_query` tool makes this retrieval cheap — it doesn't load the full session, just answers a targeted question from it.
29
+
30
+ ### LLM-Judged Important Files
31
+
32
+ The compaction prompt is augmented to ask the LLM to identify the most goal-relevant files as part of generating the summary. The file sections are restructured:
33
+
34
+ ```xml
35
+ <important-files>
36
+ src/core/compaction.ts
37
+ extensions/handoff.ts
38
+ </important-files>
39
+
40
+ <modified-files>
41
+ src/core/compaction.ts
42
+ extensions/handoff.ts
43
+ src/utils.ts
44
+ </modified-files>
45
+
46
+ <other-read-files>
47
+ package.json
48
+ docs/api.md
49
+ </other-read-files>
50
+ ```
51
+
52
+ Files are selected using these criteria:
53
+ - Directly related to accomplishing the goal
54
+ - Contain reference code or patterns to follow
55
+ - Will need to be read, edited, or created
56
+ - Provide important context or constraints
57
+
58
+ `<modified-files>` is left untouched (intentional overlap with important-files is fine). `<read-files>` becomes `<other-read-files>` with the important ones pruned out.
59
+
60
+ If the LLM doesn't output a parseable `## Most Important Files` section, the extension falls back to the default `<read-files>`/`<modified-files>` format silently.
61
+
62
+ ## Components
63
+
64
+ | Component | Type | Description |
65
+ |-----------|------|-------------|
66
+ | [compaction.ts](extensions/compaction.ts) | Extension | `session_before_compact` hook — session context + file restructuring |
67
+ | [session-query.ts](extensions/session-query.ts) | Extension | `session_query` tool for querying session history |
68
+ | [pi-session-query/](skills/pi-session-query/SKILL.md) | Skill | Instructions for using `session_query` |
69
+
70
+ ## Notes
71
+
72
+ - If you also have `@ssweens/pi-handoff` installed, both packages register the `session_query` tool. Pi will warn about the duplicate — it's harmless, one will shadow the other.
73
+ - Works with `/compact [instructions]` — user instructions are preserved and the file importance prompt is appended after them.
74
+ - On any compaction error, falls back to pi's default compaction silently (with a warning notification).
75
+
76
+ ## License
77
+
78
+ MIT
@@ -0,0 +1,209 @@
1
+ /**
2
+ * Pi-Compaxxt Compaction Extension
3
+ *
4
+ * Enhances pi's default compaction with two features:
5
+ *
6
+ * 1. Session context block prepended to every summary — session file path and
7
+ * thread ID so the post-compaction LLM can use session_query to retrieve
8
+ * older context that was summarized away.
9
+ *
10
+ * 2. LLM-judged <important-files> section — the compaction prompt is augmented
11
+ * to ask the LLM to identify the most goal-relevant files as part of
12
+ * generating the summary (one LLM call, no extra cost). The file sections
13
+ * are then restructured:
14
+ * <important-files> — LLM-ranked top 3-5 files
15
+ * <modified-files> — all modified files, unchanged from default
16
+ * <other-read-files> — read-only files minus the important ones
17
+ */
18
+
19
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
20
+ import { compact } from "@mariozechner/pi-coding-agent";
21
+
22
+ // ---------------------------------------------------------------------------
23
+ // Session context block
24
+ // ---------------------------------------------------------------------------
25
+
26
+ function buildSessionContextBlock(
27
+ sessionFile: string | undefined,
28
+ leafId: string | null,
29
+ ): string {
30
+ const lines: string[] = ["## Session Context"];
31
+ if (sessionFile) lines.push(`**Session:** \`${sessionFile}\``);
32
+ if (leafId) lines.push(`**Thread ID:** \`${leafId}\``);
33
+ lines.push(
34
+ "",
35
+ "Use the `session_query` tool to retrieve specific context from messages that were summarized away.",
36
+ "",
37
+ "---",
38
+ "",
39
+ );
40
+ return lines.join("\n");
41
+ }
42
+
43
+ // ---------------------------------------------------------------------------
44
+ // Important files: parsing + file section restructuring
45
+ // ---------------------------------------------------------------------------
46
+
47
+ /**
48
+ * Parse the ## Most Important Files section from the LLM summary.
49
+ * Validates each path against the known file list to guard against hallucination.
50
+ */
51
+ function parseImportantFiles(summary: string, knownFiles: Set<string>): string[] {
52
+ const match = summary.match(/\n## Most Important Files\n([\s\S]+?)(?=\n\n<|$)/);
53
+ if (!match) return [];
54
+
55
+ return match[1]
56
+ .split("\n")
57
+ .map((line) => line.replace(/^[-*]\s*/, "").trim()) // strip bullets if LLM added them
58
+ .map((line) => line.split(/\s+/)[0]) // strip any inline explanation after the path
59
+ .filter((path) => path.length > 0 && knownFiles.has(path));
60
+ }
61
+
62
+ /**
63
+ * Restructure the file XML sections in the summary:
64
+ * - Remove ## Most Important Files from markdown (now encoded in XML)
65
+ * - Remove <read-files> and replace with <other-read-files> (pruned)
66
+ * - Insert <important-files> before <modified-files>
67
+ * - Leave <modified-files> untouched (may overlap with important-files — intentional)
68
+ */
69
+ function restructureFileSections(
70
+ summary: string,
71
+ importantFiles: string[],
72
+ readFiles: string[],
73
+ ): string {
74
+ const importantSet = new Set(importantFiles);
75
+
76
+ // Remove the ## Most Important Files markdown section
77
+ let result = summary.replace(/\n## Most Important Files\n[\s\S]+?(?=\n\n<|$)/, "");
78
+
79
+ // Remove the existing <read-files> block entirely
80
+ result = result.replace(/\n\n<read-files>\n[\s\S]+?\n<\/read-files>/, "");
81
+
82
+ // Compute other-read-files: read-only files not in the important list
83
+ const otherReadFiles = readFiles.filter((f) => !importantSet.has(f));
84
+
85
+ const importantSection = `<important-files>\n${importantFiles.join("\n")}\n</important-files>`;
86
+ const otherReadSection =
87
+ otherReadFiles.length > 0
88
+ ? `<other-read-files>\n${otherReadFiles.join("\n")}\n</other-read-files>`
89
+ : "";
90
+
91
+ // Insert <important-files> before <modified-files> if present, otherwise append
92
+ if (result.includes("<modified-files>")) {
93
+ result = result.replace(
94
+ "\n\n<modified-files>",
95
+ `\n\n${importantSection}\n\n<modified-files>`,
96
+ );
97
+ if (otherReadSection) {
98
+ result = result.replace("</modified-files>", `</modified-files>\n\n${otherReadSection}`);
99
+ }
100
+ } else {
101
+ // Read-only session — no modified-files section
102
+ result += `\n\n${importantSection}`;
103
+ if (otherReadSection) result += `\n\n${otherReadSection}`;
104
+ }
105
+
106
+ return result;
107
+ }
108
+
109
+ // ---------------------------------------------------------------------------
110
+ // Extension
111
+ // ---------------------------------------------------------------------------
112
+
113
+ export default function (pi: ExtensionAPI) {
114
+ pi.on("session_before_compact", async (event, ctx) => {
115
+ if (!ctx.model) return;
116
+
117
+ const apiKey = await ctx.modelRegistry.getApiKey(ctx.model);
118
+ if (!apiKey) return;
119
+
120
+ const { preparation, customInstructions: userInstructions, signal } = event;
121
+
122
+ // Equivalent to computeFileLists() from compaction/utils — not re-exported by the package
123
+ const modified = new Set([...preparation.fileOps.edited, ...preparation.fileOps.written]);
124
+ const readFiles = [...preparation.fileOps.read].filter((f) => !modified.has(f)).sort();
125
+ const modifiedFiles = [...modified].sort();
126
+ const allFiles = [...readFiles, ...modifiedFiles];
127
+
128
+ // Build file importance instruction, respecting any user /compact [instructions]
129
+ let combinedInstructions = userInstructions ?? "";
130
+
131
+ if (allFiles.length > 0) {
132
+ const fileLines: string[] = [];
133
+ if (modifiedFiles.length > 0) {
134
+ fileLines.push(`Modified:\n${modifiedFiles.map((f) => ` ${f}`).join("\n")}`);
135
+ }
136
+ if (readFiles.length > 0) {
137
+ fileLines.push(`Read-only:\n${readFiles.map((f) => ` ${f}`).join("\n")}`);
138
+ }
139
+
140
+ const fileImportanceInstruction = `After all other sections, add:
141
+
142
+ ## Most Important Files
143
+ Identify files that are:
144
+ - Directly related to accomplishing the goal
145
+ - Contain reference code or patterns to follow
146
+ - Will need to be read, edited, or created
147
+ - Provide important context or constraints
148
+
149
+ List 3-5 files from those accessed this session, most important first.
150
+ One path per line, no bullets, no explanation.
151
+
152
+ All files accessed this session:
153
+ ${fileLines.join("\n\n")}`;
154
+
155
+ combinedInstructions = combinedInstructions
156
+ ? `${combinedInstructions}\n\n${fileImportanceInstruction}`
157
+ : fileImportanceInstruction;
158
+ }
159
+
160
+ try {
161
+ const result = await compact(
162
+ preparation,
163
+ ctx.model,
164
+ apiKey,
165
+ combinedInstructions || undefined,
166
+ signal,
167
+ );
168
+
169
+ if (signal.aborted) return;
170
+
171
+ let summary = result.summary;
172
+
173
+ // Parse and restructure file sections
174
+ if (allFiles.length > 0) {
175
+ const knownFiles = new Set(allFiles);
176
+ const importantFiles = parseImportantFiles(summary, knownFiles);
177
+ if (importantFiles.length > 0) {
178
+ summary = restructureFileSections(summary, importantFiles, readFiles);
179
+ }
180
+ // If LLM didn't follow the format, summary falls back to default
181
+ // <read-files>/<modified-files> sections untouched
182
+ }
183
+
184
+ // Prepend session context block
185
+ const sessionFile = ctx.sessionManager.getSessionFile();
186
+ const leafId = ctx.sessionManager.getLeafId();
187
+ if (sessionFile || leafId) {
188
+ summary = buildSessionContextBlock(sessionFile, leafId) + summary;
189
+ }
190
+
191
+ return {
192
+ compaction: {
193
+ summary,
194
+ firstKeptEntryId: result.firstKeptEntryId,
195
+ tokensBefore: result.tokensBefore,
196
+ details: result.details,
197
+ },
198
+ };
199
+ } catch (err) {
200
+ if (!signal.aborted) {
201
+ ctx.ui.notify(
202
+ `pi-compaxxt: compaction failed, using default. ${err instanceof Error ? err.message : String(err)}`,
203
+ "warning",
204
+ );
205
+ }
206
+ return; // fall back to default compaction
207
+ }
208
+ });
209
+ }
@@ -0,0 +1,219 @@
1
+ /**
2
+ * Session Query Extension - Query previous pi sessions
3
+ *
4
+ * Provides a tool the model can use to query past sessions for context,
5
+ * decisions, code changes, or other information.
6
+ *
7
+ * Works with handoff: when a handoff prompt includes "Parent session: <path>",
8
+ * the model can use this tool to look up details from that session.
9
+ *
10
+ * Based on pi-amplike's session-query, enhanced with:
11
+ * - Better error handling
12
+ * - Rendered results with markdown support
13
+ * - Session metadata in response
14
+ */
15
+
16
+ import { complete, type Message } from "@mariozechner/pi-ai";
17
+ import type { ExtensionAPI, SessionEntry } from "@mariozechner/pi-coding-agent";
18
+ import {
19
+ SessionManager,
20
+ convertToLlm,
21
+ serializeConversation,
22
+ getMarkdownTheme,
23
+ } from "@mariozechner/pi-coding-agent";
24
+ import { Container, Markdown, Spacer, Text } from "@mariozechner/pi-tui";
25
+ import { Type } from "@sinclair/typebox";
26
+ import * as fs from "node:fs";
27
+
28
+ // Maximum characters of serialized conversation to send to the query LLM.
29
+ // Prevents blowing context when the parent session is very large.
30
+ // ~100k chars ≈ ~25-30k tokens for most models — leaves room for the
31
+ // question, system prompt, and answer within a 128k context window.
32
+ const MAX_SESSION_CHARS = 100_000;
33
+
34
+ const QUERY_SYSTEM_PROMPT = `Extract information relevant to the question from the session history.
35
+ Return a concise answer using bullet points where appropriate.
36
+ Use code pointers (path/to/file.ts:42 or path/to/file.ts#functionName) when referencing specific code.
37
+ If the information is not in the session, say so clearly.`;
38
+
39
+ export default function (pi: ExtensionAPI) {
40
+ pi.registerTool({
41
+ name: "session_query",
42
+ label: (params) => `Query Session: ${params.question}`,
43
+ description:
44
+ "Query a previous pi session file for context, decisions, or information. Use when you need to look up what happened in a parent session or any other session. The sessionPath should be the full path to a .jsonl session file.",
45
+
46
+ parameters: Type.Object({
47
+ sessionPath: Type.String({
48
+ description:
49
+ "Full path to the session file (e.g., /home/user/.pi/agent/sessions/.../session.jsonl)",
50
+ }),
51
+ question: Type.String({
52
+ description:
53
+ "What you want to know about that session (e.g., 'What files were modified?' or 'What approach was chosen?')",
54
+ }),
55
+ }),
56
+
57
+ renderResult(result, _options, theme) {
58
+ const container = new Container();
59
+
60
+ if (result.content && result.content[0]?.text) {
61
+ const text = result.content[0].text;
62
+
63
+ // Check for error format
64
+ if (result.details?.error) {
65
+ container.addChild(new Text(theme.fg("error", text), 0, 0));
66
+ return container;
67
+ }
68
+
69
+ // Parse structured response: **Query:** question\n\n---\n\nanswer
70
+ const match = text.match(/\*\*Query:\*\* (.+?)\n\n---\n\n([\s\S]+)/);
71
+
72
+ if (match) {
73
+ const [, query, answer] = match;
74
+ container.addChild(new Text(theme.bold("Query: ") + theme.fg("accent", query), 0, 0));
75
+ container.addChild(new Spacer(1));
76
+ // Render the answer as markdown
77
+ container.addChild(
78
+ new Markdown(answer.trim(), 0, 0, getMarkdownTheme(), {
79
+ color: (text: string) => theme.fg("toolOutput", text),
80
+ }),
81
+ );
82
+ } else {
83
+ // Fallback for other formats
84
+ container.addChild(new Text(theme.fg("toolOutput", text), 0, 0));
85
+ }
86
+
87
+ // Show metadata if available
88
+ if (result.details?.messageCount) {
89
+ const truncNote = result.details.truncated ? ", truncated" : "";
90
+ container.addChild(new Spacer(1));
91
+ container.addChild(
92
+ new Text(
93
+ theme.fg("dim", `(${result.details.messageCount} messages in session${truncNote})`),
94
+ 0,
95
+ 0,
96
+ ),
97
+ );
98
+ }
99
+ }
100
+
101
+ return container;
102
+ },
103
+
104
+ async execute(toolCallId, params, signal, onUpdate, ctx) {
105
+ const { sessionPath, question } = params;
106
+
107
+ // Helper for error returns
108
+ const errorResult = (text: string) => ({
109
+ content: [{ type: "text" as const, text }],
110
+ details: { error: true },
111
+ });
112
+
113
+ // Validate session path
114
+ if (!sessionPath.endsWith(".jsonl")) {
115
+ return errorResult(
116
+ `Error: Invalid session path. Expected a .jsonl file, got: ${sessionPath}`,
117
+ );
118
+ }
119
+
120
+ // Check if file exists
121
+ if (!fs.existsSync(sessionPath)) {
122
+ return errorResult(`Error: Session file not found: ${sessionPath}`);
123
+ }
124
+
125
+ onUpdate?.({
126
+ content: [
127
+ {
128
+ type: "text",
129
+ text: `Querying session: ${question}`,
130
+ },
131
+ ],
132
+ details: { status: "loading", question },
133
+ });
134
+
135
+ // Load the session
136
+ let sessionManager: SessionManager;
137
+ try {
138
+ sessionManager = SessionManager.open(sessionPath);
139
+ } catch (err) {
140
+ return errorResult(`Error loading session: ${err}`);
141
+ }
142
+
143
+ // Get conversation from the session
144
+ const branch = sessionManager.getBranch();
145
+ const messages = branch
146
+ .filter((entry): entry is SessionEntry & { type: "message" } => entry.type === "message")
147
+ .map((entry) => entry.message);
148
+
149
+ if (messages.length === 0) {
150
+ return {
151
+ content: [{ type: "text" as const, text: "Session is empty - no messages found." }],
152
+ details: { empty: true },
153
+ };
154
+ }
155
+
156
+ // Serialize the conversation, truncating if too large
157
+ const llmMessages = convertToLlm(messages);
158
+ let conversationText = serializeConversation(llmMessages);
159
+ let truncated = false;
160
+
161
+ if (conversationText.length > MAX_SESSION_CHARS) {
162
+ // Keep the tail (most recent context) — more likely to be relevant
163
+ conversationText = "…[earlier messages truncated]…\n\n"
164
+ + conversationText.slice(-MAX_SESSION_CHARS);
165
+ truncated = true;
166
+ }
167
+
168
+ // Use LLM to answer the question
169
+ if (!ctx.model) {
170
+ return errorResult("Error: No model available to analyze the session.");
171
+ }
172
+
173
+ try {
174
+ const apiKey = await ctx.modelRegistry.getApiKey(ctx.model);
175
+
176
+ const userMessage: Message = {
177
+ role: "user",
178
+ content: [
179
+ {
180
+ type: "text",
181
+ text: `## Session Conversation\n\n${conversationText}\n\n## Question\n\n${question}`,
182
+ },
183
+ ],
184
+ timestamp: Date.now(),
185
+ };
186
+
187
+ const response = await complete(
188
+ ctx.model,
189
+ { systemPrompt: QUERY_SYSTEM_PROMPT, messages: [userMessage] },
190
+ { apiKey, signal },
191
+ );
192
+
193
+ if (response.stopReason === "aborted") {
194
+ return {
195
+ content: [{ type: "text" as const, text: "Query was cancelled." }],
196
+ details: { cancelled: true },
197
+ };
198
+ }
199
+
200
+ const answer = response.content
201
+ .filter((c): c is { type: "text"; text: string } => c.type === "text")
202
+ .map((c) => c.text)
203
+ .join("\n");
204
+
205
+ return {
206
+ content: [{ type: "text" as const, text: `**Query:** ${question}\n\n---\n\n${answer}` }],
207
+ details: {
208
+ sessionPath,
209
+ question,
210
+ messageCount: messages.length,
211
+ truncated,
212
+ },
213
+ };
214
+ } catch (err) {
215
+ return errorResult(`Error querying session: ${err}`);
216
+ }
217
+ },
218
+ });
219
+ }
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@ssweens/pi-compaxxt",
3
+ "version": "1.0.0",
4
+ "description": "Enhanced compaction for pi — session context and LLM-ranked important files",
5
+ "keywords": [
6
+ "pi-package"
7
+ ],
8
+ "type": "module",
9
+ "license": "MIT",
10
+ "author": "ssweens",
11
+ "files": [
12
+ "extensions/",
13
+ "skills/",
14
+ "README.md",
15
+ "LICENSE"
16
+ ],
17
+ "pi": {
18
+ "extensions": [
19
+ "extensions"
20
+ ],
21
+ "skills": [
22
+ "skills"
23
+ ]
24
+ },
25
+ "peerDependencies": {
26
+ "@mariozechner/pi-ai": "*",
27
+ "@mariozechner/pi-coding-agent": "*",
28
+ "@mariozechner/pi-tui": "*",
29
+ "@sinclair/typebox": "*"
30
+ }
31
+ }
@@ -0,0 +1,58 @@
1
+ ---
2
+ name: pi-session-query
3
+ description: Query previous pi sessions to retrieve context, decisions, code changes, or other information. Use when you need to look up what happened in a parent session or any other session file.
4
+ disable-model-invocation: true
5
+ ---
6
+
7
+ # Pi Session Query
8
+
9
+ Query pi session files to retrieve context from past conversations.
10
+
11
+ This skill is automatically invoked in handed-off sessions when you need to look up details from the parent session.
12
+
13
+ ## When to Use
14
+
15
+ - When the handoff summary references a "Parent session" or "Ancestor sessions" path
16
+ - When you need specific details not included in the handoff summary
17
+ - When you need to verify a decision or approach from the parent or an ancestor session
18
+ - When you need file paths or code snippets from earlier work
19
+
20
+ ## Usage
21
+
22
+ Use the `session_query` tool:
23
+
24
+ ```
25
+ session_query(sessionPath, question)
26
+ ```
27
+
28
+ **Parameters:**
29
+ - `sessionPath`: Full path to the session file (provided in the "Parent session:" line)
30
+ - `question`: Specific question about that session
31
+
32
+ ## Examples
33
+
34
+ ```typescript
35
+ // Find what files were changed
36
+ session_query("/path/to/session.jsonl", "What files were modified?")
37
+
38
+ // Get approach details
39
+ session_query("/path/to/session.jsonl", "What approach was chosen for authentication?")
40
+
41
+ // Get specific code decisions
42
+ session_query("/path/to/session.jsonl", "What error handling pattern was used?")
43
+
44
+ // Summarize key decisions
45
+ session_query("/path/to/session.jsonl", "Summarize the key decisions made")
46
+ ```
47
+
48
+ ## Best Practices
49
+
50
+ 1. **Be specific** - Ask targeted questions for better results
51
+ 2. **Reference code** - Ask about specific files or functions when relevant
52
+ 3. **Verify before assuming** - If the handoff summary seems incomplete, query for details
53
+ 4. **Don't over-query** - The handoff summary should have most context; query only when needed
54
+ 5. **Check ancestors** - If the parent session doesn't have the info, try ancestor sessions listed in the handoff
55
+
56
+ ## How It Works
57
+
58
+ The tool loads the referenced session file, extracts the conversation history, and uses the LLM to answer your question based on its contents. This allows context retrieval without loading the full parent session into your context window.