agent-sh 0.10.0 → 0.10.2

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 (40) hide show
  1. package/README.md +12 -9
  2. package/dist/agent/agent-loop.d.ts +0 -3
  3. package/dist/agent/agent-loop.js +18 -35
  4. package/dist/agent/conversation-state.js +8 -2
  5. package/dist/agent/nuclear-form.d.ts +2 -0
  6. package/dist/agent/nuclear-form.js +11 -1
  7. package/dist/agent/system-prompt.js +1 -1
  8. package/dist/agent/token-budget.d.ts +8 -12
  9. package/dist/agent/token-budget.js +5 -40
  10. package/dist/agent/tool-registry.js +6 -0
  11. package/dist/agent/types.d.ts +3 -1
  12. package/dist/context-manager.d.ts +1 -21
  13. package/dist/context-manager.js +26 -163
  14. package/dist/event-bus.d.ts +0 -1
  15. package/dist/extension-loader.js +25 -4
  16. package/dist/extensions/agent-backend.js +3 -2
  17. package/dist/extensions/index.js +0 -1
  18. package/dist/extensions/tui-renderer.js +47 -29
  19. package/dist/settings.d.ts +3 -11
  20. package/dist/settings.js +0 -4
  21. package/dist/shell/input-handler.js +14 -9
  22. package/dist/types.d.ts +3 -0
  23. package/dist/utils/ansi.d.ts +6 -1
  24. package/dist/utils/ansi.js +114 -7
  25. package/dist/utils/box-frame.js +8 -2
  26. package/dist/utils/llm-client.d.ts +4 -0
  27. package/dist/utils/llm-client.js +8 -0
  28. package/dist/utils/markdown.d.ts +4 -0
  29. package/dist/utils/markdown.js +136 -48
  30. package/dist/utils/package-version.d.ts +1 -0
  31. package/dist/utils/package-version.js +10 -0
  32. package/dist/utils/shell-output-spill.d.ts +2 -0
  33. package/dist/utils/shell-output-spill.js +81 -0
  34. package/examples/extensions/claude-code-bridge/README.md +14 -0
  35. package/examples/extensions/claude-code-bridge/index.ts +13 -101
  36. package/examples/extensions/pi-bridge/README.md +16 -0
  37. package/examples/extensions/pi-bridge/index.ts +8 -154
  38. package/package.json +9 -1
  39. package/dist/extensions/shell-recall.d.ts +0 -9
  40. package/dist/extensions/shell-recall.js +0 -8
package/README.md CHANGED
@@ -4,6 +4,7 @@ An agent that lives in a shell — not a shell that lives in an agent.
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/agent-sh.svg)](https://www.npmjs.com/package/agent-sh)
6
6
  [![license](https://img.shields.io/npm/l/agent-sh.svg)](https://github.com/guanyilun/agent-sh/blob/main/LICENSE)
7
+ [![website](https://img.shields.io/badge/website-agent--sh.dev-blue)](https://agent-sh.dev)
7
8
 
8
9
  ![demo](assets/demo.gif)
9
10
 
@@ -44,7 +45,7 @@ Requires Node.js 18+.
44
45
 
45
46
  **Context that just works.** Every query includes your cwd, recent commands, and their output. Run a failing test, type `> fix this`, and agent-sh knows exactly what happened. Context management works like shell history — continuous, persistent across restarts, no sessions to manage. See [Context Management](docs/context-management.md).
46
47
 
47
- **Any LLM, any backend.** agent-sh works with any OpenAI-compatible API out of the box. Define multiple providers in settings and cycle between models at runtime with Shift+Tab. Or swap in a completely different agent — [Claude Code](examples/extensions/claude-code-bridge/) and [pi](examples/extensions/pi-bridge/) run as drop-in backend extensions.
48
+ **Any LLM, any backend.** agent-sh works with any OpenAI-compatible API out of the box. Define multiple providers in settings and switch models at runtime with `/model <name>`. Or swap in a completely different agent — [Claude Code](examples/extensions/claude-code-bridge/) and [pi](examples/extensions/pi-bridge/) run as drop-in backend extensions.
48
49
 
49
50
  **Extensible by design.** The entire system is built on a typed event bus. Extensions can add custom input modes, content transforms (render LaTeX as images, Mermaid as diagrams), themes, slash commands, or replace the agent backend entirely. The built-in TUI renderer is itself just an extension.
50
51
 
@@ -52,14 +53,16 @@ Requires Node.js 18+.
52
53
 
53
54
  ## Documentation
54
55
 
55
- - [Usage Guide](docs/usage.md) providers, models, configuration
56
- - [Internal Agent](docs/agent.md) — tools, context, streaming
57
- - [Context Management](docs/context-management.md) — three-tier history, token budget
58
- - [Architecture](docs/architecture.md) — design philosophy, component overview
59
- - [Extensions](docs/extensions.md) — event bus, content transforms, custom backends, theming
60
- - [TUI Composition](docs/tui-composition.md) — compositor, render surfaces, stream routing
61
- - [Library Usage](docs/library.md) — embedding agent-sh in your own apps
62
- - [Troubleshooting](docs/troubleshooting.md) — common errors and debug mode
56
+ Start with **Usage** to get running, then **Architecture** for the mental model.
57
+
58
+ 1. [Usage Guide](docs/usage.md) — install, run, configure providers and models
59
+ 2. [Architecture](docs/architecture.md) — pure kernel + extensions, the shell ↔ agent boundary
60
+ 3. [The Built-in Agent: ash](docs/agent.md) — query flow, tools, system prompt, model switching
61
+ 4. [Context Management](docs/context-management.md) — shell-output spill, three-tier conversation compaction, recall APIs
62
+ 5. [Extensions](docs/extensions.md) — event bus, content transforms, custom agent backends, theming
63
+ 6. [TUI Composition](docs/tui-composition.md) — compositor, render surfaces, stream routing
64
+ 7. [Library Usage](docs/library.md) — embedding agent-sh in your own apps
65
+ 8. [Troubleshooting](docs/troubleshooting.md) — common errors and debug mode
63
66
 
64
67
  ## Development
65
68
 
@@ -4,7 +4,6 @@
4
4
  * Subscribes to bus events in constructor:
5
5
  * - agent:submit → run query through LLM tool loop
6
6
  * - agent:cancel-request → abort current loop
7
- * - config:cycle → cycle through modes
8
7
  *
9
8
  * Emits bus events during execution:
10
9
  * - agent:query, agent:processing-start/done, agent:response-chunk/done
@@ -36,7 +35,6 @@ export declare class AgentLoop implements AgentBackend {
36
35
  private historyFile;
37
36
  private conversation;
38
37
  private fileReadCache;
39
- private tokenBudget;
40
38
  private modes;
41
39
  private currentModeIndex;
42
40
  private boundListeners;
@@ -104,7 +102,6 @@ export declare class AgentLoop implements AgentBackend {
104
102
  private cancel;
105
103
  /** Check if reasoning_effort should be sent for the current model/provider. */
106
104
  private shouldSendReasoningEffort;
107
- private cycleMode;
108
105
  private get currentMode();
109
106
  private get currentModel();
110
107
  /**
@@ -8,7 +8,8 @@ import { HistoryFile } from "./history-file.js";
8
8
  import { nucleate, formatNuclearLine, isReadOnly } from "./nuclear-form.js";
9
9
  import { STATIC_SYSTEM_PROMPT, buildDynamicContext, buildStaticByCwd, formatSkillsBlock, loadGlobalAgentsMd } from "./system-prompt.js";
10
10
  import { createToolUI } from "../utils/tool-interactive.js";
11
- import { TokenBudget, RESPONSE_RESERVE, DEFAULT_CONTEXT_WINDOW } from "./token-budget.js";
11
+ import { RESPONSE_RESERVE, DEFAULT_CONTEXT_WINDOW } from "./token-budget.js";
12
+ import { PACKAGE_VERSION } from "../utils/package-version.js";
12
13
  import { getSettings, updateSettings } from "../settings.js";
13
14
  import { createToolProtocol } from "./tool-protocol.js";
14
15
  // Core tool factories
@@ -40,7 +41,6 @@ export class AgentLoop {
40
41
  historyFile;
41
42
  conversation;
42
43
  fileReadCache = new Map();
43
- tokenBudget;
44
44
  modes;
45
45
  currentModeIndex = 0;
46
46
  boundListeners = [];
@@ -105,8 +105,6 @@ export class AgentLoop {
105
105
  ? config.modes
106
106
  : [{ model: config.llmClient.model }];
107
107
  this.currentModeIndex = config.initialModeIndex ?? 0;
108
- // Unified token budget — adapts to current model's context window
109
- this.tokenBudget = new TokenBudget(this.currentMode.contextWindow);
110
108
  // Tool protocol — controls how tools are presented to the LLM
111
109
  this.toolProtocol = createToolProtocol(getSettings().toolMode ?? "api");
112
110
  // Register core tools
@@ -115,8 +113,6 @@ export class AgentLoop {
115
113
  const protocolTools = this.toolProtocol.getProtocolTools?.() ?? [];
116
114
  for (const t of protocolTools)
117
115
  this.registerTool(t);
118
- // Update token budget with tool count
119
- this.tokenBudget.update(undefined, this.toolRegistry.all().length);
120
116
  // Register handlers — extensions can advise these
121
117
  this.registerHandlers();
122
118
  // Subscribe to bus-based tool/instruction registration from extensions.
@@ -165,7 +161,6 @@ export class AgentLoop {
165
161
  else {
166
162
  this.llmClient.model = m.model;
167
163
  }
168
- this.tokenBudget.update(m.contextWindow, this.toolRegistry.all().length);
169
164
  this.bus.emit("config:changed", {});
170
165
  });
171
166
  const getToolsPipe = () => ({ tools: this.getTools() });
@@ -184,7 +179,6 @@ export class AgentLoop {
184
179
  on("agent:cancel-request", (e) => {
185
180
  this.abortController?.abort(e.silent ? "silent" : undefined);
186
181
  });
187
- on("config:cycle", () => this.cycleMode());
188
182
  on("config:switch-model", ({ model: target }) => {
189
183
  const idx = this.modes.findIndex((m) => m.model === target);
190
184
  if (idx === -1) {
@@ -199,9 +193,8 @@ export class AgentLoop {
199
193
  else {
200
194
  this.llmClient.model = m.model;
201
195
  }
202
- this.tokenBudget.update(m.contextWindow, this.toolRegistry.all().length);
203
196
  const label = m.provider ? `${m.provider}: ${m.model}` : m.model;
204
- this.bus.emit("agent:info", { name: "ash", version: "0.4", model: m.model, provider: m.provider, contextWindow: m.contextWindow });
197
+ this.bus.emit("agent:info", { name: "ash", version: PACKAGE_VERSION, model: m.model, provider: m.provider, contextWindow: m.contextWindow });
205
198
  // Persist as the new default — selection survives restart.
206
199
  // Safe even for dynamic providers: agent-backend defers mode
207
200
  // resolution to `core:extensions-loaded`, so the extension gets
@@ -287,6 +280,9 @@ export class AgentLoop {
287
280
  if (beforeTokens > this.peakConversationTokens) {
288
281
  this.peakConversationTokens = beforeTokens;
289
282
  }
283
+ // The "File unchanged" stub assumes the prior read output is still
284
+ // in context; compaction can evict it. Clear so the next read re-emits.
285
+ this.fileReadCache.clear();
290
286
  });
291
287
  on("shell:cwd-change", ({ cwd }) => {
292
288
  const projectSkills = discoverProjectSkills(cwd);
@@ -428,30 +424,6 @@ export class AgentLoop {
428
424
  return false;
429
425
  return true;
430
426
  }
431
- cycleMode() {
432
- const prevMode = this.modes[this.currentModeIndex];
433
- this.currentModeIndex =
434
- (this.currentModeIndex + 1) % this.modes.length;
435
- const newMode = this.modes[this.currentModeIndex];
436
- // Reconfigure LlmClient if provider changed
437
- if (newMode.provider !== prevMode.provider && newMode.providerConfig) {
438
- this.llmClient.reconfigure({
439
- apiKey: newMode.providerConfig.apiKey,
440
- baseURL: newMode.providerConfig.baseURL,
441
- model: newMode.model,
442
- });
443
- }
444
- else {
445
- this.llmClient.model = newMode.model;
446
- }
447
- this.tokenBudget.update(newMode.contextWindow, this.toolRegistry.all().length);
448
- const label = newMode.provider
449
- ? `${newMode.provider}: ${newMode.model}`
450
- : newMode.model;
451
- this.bus.emit("agent:info", { name: "ash", version: "0.4", model: newMode.model, provider: newMode.provider, contextWindow: newMode.contextWindow });
452
- this.bus.emit("ui:info", { message: `Model: ${label}` });
453
- this.bus.emit("config:changed", {});
454
- }
455
427
  get currentMode() {
456
428
  return this.modes[this.currentModeIndex];
457
429
  }
@@ -1066,7 +1038,18 @@ export class AgentLoop {
1066
1038
  const contextWindow = this.currentMode.contextWindow ?? DEFAULT_CONTEXT_WINDOW;
1067
1039
  const threshold = Math.floor((contextWindow - RESPONSE_RESERVE) * getSettings().autoCompactThreshold);
1068
1040
  if (totalEstimate > threshold) {
1069
- this.compactWithHooks(threshold);
1041
+ // Compact deeply — shallow targets buy only 1–2 turns of runway on
1042
+ // tool-heavy workloads.
1043
+ const target = Math.floor(threshold * 0.25);
1044
+ const result = this.compactWithHooks(target, 6);
1045
+ if (!result) {
1046
+ // Auto-compact fired but nothing was evictable. This can happen
1047
+ // in short conversations with heavy tool output where the pin
1048
+ // fraction consumes all turns. Log it so it's not silent.
1049
+ this.bus.emit("ui:info", {
1050
+ message: `[auto-compact] above threshold (${totalEstimate.toLocaleString()} > ${threshold.toLocaleString()}) but nothing to evict — conversation may be too short`,
1051
+ });
1052
+ }
1070
1053
  cachedSystemPrompt = undefined;
1071
1054
  }
1072
1055
  const currentCwd = this.contextManager.getCwd();
@@ -217,12 +217,18 @@ export class ConversationState {
217
217
  if (!force && convEstimate <= convTarget)
218
218
  return null;
219
219
  const turns = this.parseTurns();
220
- if (turns.length <= 2)
220
+ // With force, allow compacting down to 1 turn (the current response).
221
+ // Without force, keep at least 2 turns (user + agent) to avoid
222
+ // annihilating a young conversation.
223
+ if (turns.length <= (force ? 1 : 2))
221
224
  return null;
222
225
  // Cap the pinned window so enough turns remain evictable.
223
226
  const maxPinnedFraction = force ? 0.4 : 0.6;
224
227
  const maxPinned = Math.max(2, Math.floor(turns.length * maxPinnedFraction));
225
- const pinnedCount = Math.min(recentTurnsToKeep, turns.length - 1, maxPinned);
228
+ // Ensure at least 1 turn is evictable when force is true, even in
229
+ // very short conversations (e.g. 3 turns with heavy tool output).
230
+ const maxPinnedForced = force ? Math.min(maxPinned, turns.length - 2) : maxPinned;
231
+ const pinnedCount = Math.min(recentTurnsToKeep, turns.length - 1, Math.max(1, maxPinnedForced));
226
232
  for (let i = 0; i < turns.length; i++) {
227
233
  turns[i].priority = this.inferPriority(turns[i].messages);
228
234
  }
@@ -43,6 +43,8 @@ export declare function createSessionMarker(iid: string, seq?: number): NuclearE
43
43
  export declare function isSessionMarker(entry: NuclearEntry): boolean;
44
44
  /** Read-only tools whose results are dropped at Tier 1→2 (agent can re-read). */
45
45
  export declare const READ_ONLY_TOOLS: Set<string>;
46
+ export declare function registerReadOnlyTool(name: string): void;
47
+ export declare function unregisterReadOnlyTool(name: string): void;
46
48
  /** State-changing tools whose summaries are kept in nuclear memory. */
47
49
  export declare const WRITE_TOOLS: Set<string>;
48
50
  /**
@@ -15,6 +15,14 @@ export function isSessionMarker(entry) {
15
15
  export const READ_ONLY_TOOLS = new Set([
16
16
  "read_file", "grep", "glob", "ls", "search",
17
17
  ]);
18
+ /** Extensions opt their tools in via ToolRegistry.register when readOnly is set. */
19
+ const extraReadOnlyTools = new Set();
20
+ export function registerReadOnlyTool(name) {
21
+ extraReadOnlyTools.add(name);
22
+ }
23
+ export function unregisterReadOnlyTool(name) {
24
+ extraReadOnlyTools.delete(name);
25
+ }
18
26
  /** State-changing tools whose summaries are kept in nuclear memory. */
19
27
  export const WRITE_TOOLS = new Set([
20
28
  "write_file", "edit_file", "write", "edit", "patch",
@@ -188,7 +196,9 @@ export function deserializeEntry(line) {
188
196
  // ── Classification helpers ────────────────────────────────────────
189
197
  /** Check if a nuclear entry represents a read-only action (should be dropped). */
190
198
  export function isReadOnly(entry) {
191
- return entry.kind === "tool" && entry.tool != null && READ_ONLY_TOOLS.has(entry.tool);
199
+ if (entry.kind !== "tool" || entry.tool == null)
200
+ return false;
201
+ return READ_ONLY_TOOLS.has(entry.tool) || extraReadOnlyTools.has(entry.tool);
192
202
  }
193
203
  // ── Internal helpers ──────────────────────────────────────────────
194
204
  function truncate(text, maxLen) {
@@ -89,7 +89,7 @@ function loadConventionFiles(dir) {
89
89
  * Static system prompt — identical across all queries, cacheable.
90
90
  * Contains only identity and behavioral instructions.
91
91
  */
92
- export const STATIC_SYSTEM_PROMPT = `You are an AI coding assistant running inside agent-sh, a terminal shell.
92
+ export const STATIC_SYSTEM_PROMPT = `You are ash, an AI coding assistant running inside agent-sh, a terminal shell.
93
93
  You have access to the user's shell environment and can read, write, and execute code.
94
94
  You share the user's working directory, environment variables, and shell history.
95
95
  agent-sh documentation is at ${path.join(CODE_DIR, "docs")} — start with README.md for an index. Read the docs when you need to understand how the runtime works.
@@ -1,14 +1,10 @@
1
+ /**
2
+ * Shared token-budget constants used by auto-compaction.
3
+ *
4
+ * RESPONSE_RESERVE: tokens reserved for the model's output.
5
+ * DEFAULT_CONTEXT_WINDOW: fallback when the active mode doesn't declare one.
6
+ */
1
7
  /** Response reserve — tokens reserved for the model's output. */
2
- declare const RESPONSE_RESERVE = 8192;
8
+ export declare const RESPONSE_RESERVE = 8192;
3
9
  /** Fallback when contextWindow is unknown. */
4
- declare const DEFAULT_CONTEXT_WINDOW = 60000;
5
- export { RESPONSE_RESERVE, DEFAULT_CONTEXT_WINDOW };
6
- export declare class TokenBudget {
7
- private contextWindow;
8
- private toolCount;
9
- constructor(contextWindow?: number, toolCount?: number);
10
- /** Update when model or tool set changes. */
11
- update(contextWindow?: number, toolCount?: number): void;
12
- /** Token budget for the shell context stream. */
13
- get shellBudgetTokens(): number;
14
- }
10
+ export declare const DEFAULT_CONTEXT_WINDOW = 60000;
@@ -1,45 +1,10 @@
1
1
  /**
2
- * Token budget for shell context sizing.
2
+ * Shared token-budget constants used by auto-compaction.
3
3
  *
4
- * Computes how much of the context window to allocate to shell history
5
- * (user commands and outputs situational awareness). The remaining
6
- * space is for the conversation, system prompt, tools, and response.
7
- *
8
- * Shell context is sized loosely — chars/4 accuracy is fine for this.
9
- * Conversation and compaction decisions use API-grounded token counts
10
- * (see ConversationState.estimatePromptTokens).
4
+ * RESPONSE_RESERVE: tokens reserved for the model's output.
5
+ * DEFAULT_CONTEXT_WINDOW: fallback when the active mode doesn't declare one.
11
6
  */
12
- import { getSettings } from "../settings.js";
13
- const SYSTEM_PROMPT_OVERHEAD = 800;
14
- const DYNAMIC_CONTEXT_OVERHEAD = 500; // conventions, metadata, skills list
15
- const TOKENS_PER_TOOL_DEFINITION = 50;
16
7
  /** Response reserve — tokens reserved for the model's output. */
17
- const RESPONSE_RESERVE = 8192;
8
+ export const RESPONSE_RESERVE = 8192;
18
9
  /** Fallback when contextWindow is unknown. */
19
- const DEFAULT_CONTEXT_WINDOW = 60_000;
20
- export { RESPONSE_RESERVE, DEFAULT_CONTEXT_WINDOW };
21
- export class TokenBudget {
22
- contextWindow;
23
- toolCount;
24
- constructor(contextWindow, toolCount = 0) {
25
- this.contextWindow = contextWindow ?? DEFAULT_CONTEXT_WINDOW;
26
- this.toolCount = toolCount;
27
- }
28
- /** Update when model or tool set changes. */
29
- update(contextWindow, toolCount) {
30
- if (contextWindow != null)
31
- this.contextWindow = contextWindow;
32
- if (toolCount != null)
33
- this.toolCount = toolCount;
34
- }
35
- /** Token budget for the shell context stream. */
36
- get shellBudgetTokens() {
37
- const overhead = SYSTEM_PROMPT_OVERHEAD +
38
- DYNAMIC_CONTEXT_OVERHEAD +
39
- this.toolCount * TOKENS_PER_TOOL_DEFINITION +
40
- RESPONSE_RESERVE;
41
- const contentBudget = Math.max(0, this.contextWindow - overhead);
42
- const ratio = getSettings().shellContextRatio;
43
- return Math.floor(contentBudget * ratio);
44
- }
45
- }
10
+ export const DEFAULT_CONTEXT_WINDOW = 60_000;
@@ -1,3 +1,4 @@
1
+ import { registerReadOnlyTool, unregisterReadOnlyTool } from "./nuclear-form.js";
1
2
  /**
2
3
  * Registry for agent tools. Holds tool definitions and converts them
3
4
  * to OpenAI-compatible function schemas for API calls.
@@ -6,9 +7,14 @@ export class ToolRegistry {
6
7
  tools = new Map();
7
8
  register(tool) {
8
9
  this.tools.set(tool.name, tool);
10
+ if (tool.readOnly)
11
+ registerReadOnlyTool(tool.name);
12
+ else
13
+ unregisterReadOnlyTool(tool.name);
9
14
  }
10
15
  unregister(name) {
11
16
  this.tools.delete(name);
17
+ unregisterReadOnlyTool(name);
12
18
  }
13
19
  get(name) {
14
20
  return this.tools.get(name);
@@ -4,7 +4,6 @@
4
4
  * Backends self-wire to bus events in their constructor:
5
5
  * - agent:submit → handle queries
6
6
  * - agent:cancel-request → handle cancellation
7
- * - config:cycle → handle mode switching
8
7
  *
9
8
  * They emit bus events for results:
10
9
  * - agent:response-chunk, agent:tool-started, agent:tool-completed, etc.
@@ -78,6 +77,9 @@ export interface ToolDefinition {
78
77
  showOutput?: boolean;
79
78
  /** Whether this tool may modify files — triggers file watcher (default: false). */
80
79
  modifiesFiles?: boolean;
80
+ /** Results are re-fetchable; nuclear compaction drops the tool_result
81
+ * body on eviction (like the builtin read_file/grep/ls). Default: false. */
82
+ readOnly?: boolean;
81
83
  /** Whether to gate execution via permission:request (default: false). */
82
84
  requiresPermission?: boolean;
83
85
  /** Derive display metadata (icon kind, file paths) for the TUI. */
@@ -4,26 +4,13 @@ export declare class ContextManager {
4
4
  private exchanges;
5
5
  private nextId;
6
6
  private currentCwd;
7
- private sessionStart;
8
- private firstPrompt;
9
7
  private agentShellActive;
10
- private handlers;
11
- constructor(bus: EventBus, handlers?: HandlerRegistry);
8
+ constructor(bus: EventBus, _handlers?: HandlerRegistry);
12
9
  getCwd(): string;
13
- /**
14
- * Build the <shell_context> block for the agent prompt.
15
- * Pipeline: window → truncate → format
16
- */
17
- getContext(budget?: number): string;
18
10
  /**
19
11
  * Regex/keyword search across all exchanges. Returns formatted results.
20
12
  */
21
13
  search(query: string): string;
22
- /**
23
- * Return content for specific exchange IDs.
24
- * Optional start/end restrict to a line range (1-indexed).
25
- */
26
- expand(ids: number[], start?: number, end?: number): string;
27
14
  /**
28
15
  * Return shell events with id > afterId, formatted as an incremental
29
16
  * delta suitable for injection into conversation history. Skips
@@ -45,17 +32,10 @@ export declare class ContextManager {
45
32
  * One-line summaries of last N exchanges.
46
33
  */
47
34
  getRecentSummary(n?: number): string;
48
- /**
49
- * Parse and handle shell_recall commands.
50
- */
51
- handleRecallCommand(command: string): string;
52
35
  /**
53
36
  * Clear exchange history (used by /clear command).
54
37
  */
55
38
  clear(): void;
56
- private applyWindow;
57
- private applyTruncation;
58
- private formatContext;
59
39
  private addExchange;
60
40
  private formatExchangeTruncated;
61
41
  private formatExchangeFull;