agent-sh 0.12.2 → 0.12.4

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.
@@ -297,6 +297,18 @@ export class AgentLoop {
297
297
  recallArchiveSize: this.conversation.getRecallArchiveSize(),
298
298
  budgetTokens: this.currentMode.contextWindow ?? DEFAULT_CONTEXT_WINDOW,
299
299
  }));
300
+ this.bus.onPipe("context:snapshot", (payload) => {
301
+ payload.messages = this.conversation.getMessages();
302
+ payload.contextWindow = this.currentMode.contextWindow ?? DEFAULT_CONTEXT_WINDOW;
303
+ payload.activeTokens = this.conversation.estimateTokens();
304
+ return payload;
305
+ });
306
+ this.bus.onPipeAsync("context:compact", async (payload) => {
307
+ const stats = await this.compactWithHooks(0, undefined, false, payload.strategy);
308
+ if (stats)
309
+ payload.stats = { before: stats.before, after: stats.after, evictedCount: stats.evictedCount };
310
+ return payload;
311
+ });
300
312
  // Prior-session preamble (non-blocking). Both the read and the
301
313
  // layout go through advisable handlers.
302
314
  Promise.resolve(this.handlers.call("history:read-recent"))
@@ -469,11 +481,12 @@ export class AgentLoop {
469
481
  * compaction, emit `conversation:after-compact` so listeners
470
482
  * (metrics, UI, agent-awareness notes) can react.
471
483
  */
472
- compactWithHooks(target, keepRecent, force) {
484
+ compactWithHooks(target, keepRecent, force, strategy) {
473
485
  const stats = this.handlers.call("conversation:compact", {
474
486
  target,
475
487
  keepRecent,
476
488
  force: !!force,
489
+ strategy,
477
490
  });
478
491
  if (stats) {
479
492
  this.bus.emit("conversation:after-compact", {
@@ -819,7 +832,24 @@ export class AgentLoop {
819
832
  // Compaction strategy — default delegates to the two-tier pin
820
833
  // strategy in ConversationState; advisors replace wholesale.
821
834
  h.define("conversation:compact", (opts) => {
822
- return this.conversation.compact(opts.target, opts.keepRecent, opts.force);
835
+ const strategy = opts.strategy;
836
+ // Synthesize a CompactResult for manual edits so conversation:after-compact
837
+ // listeners (metrics, file-read cache, system-prompt cache) still run.
838
+ if (strategy?.kind === "rewind" || strategy?.kind === "replace") {
839
+ const before = this.conversation.estimatePromptTokens();
840
+ const beforeLen = this.conversation.getMessages().length;
841
+ const next = strategy.kind === "rewind"
842
+ ? this.conversation.getMessages().slice(0, strategy.toIndex)
843
+ : strategy.messages;
844
+ this.conversation.replaceMessages(next);
845
+ const after = this.conversation.estimatePromptTokens();
846
+ const afterLen = this.conversation.getMessages().length;
847
+ return { before, after, evictedCount: Math.max(0, beforeLen - afterLen) };
848
+ }
849
+ const tgt = strategy?.kind === "two-tier-pin" ? strategy.target : opts.target;
850
+ const keep = strategy?.kind === "two-tier-pin" ? strategy.keepRecent : opts.keepRecent;
851
+ const force = strategy?.kind === "two-tier-pin" ? strategy.force : opts.force;
852
+ return this.conversation.compact(tgt, keep, force);
823
853
  });
824
854
  // Inject a system note mid-loop — used by extensions (subagents,
825
855
  // peer messages) to deliver async results into the next iteration.
@@ -1599,12 +1629,15 @@ export class AgentLoop {
1599
1629
  tc.argumentsJson = "{}";
1600
1630
  }
1601
1631
  }
1632
+ // Echo reasoning only for modes that opt in (e.g. DeepSeek-R1).
1602
1633
  const extras = {};
1603
- if (reasoning && reasoningField)
1604
- extras[reasoningField] = reasoning;
1605
- if (reasoningDetailsByIndex.size > 0) {
1606
- extras.reasoning_details = [...reasoningDetailsByIndex.entries()]
1607
- .sort((a, b) => a[0] - b[0]).map(([, v]) => v);
1634
+ if (this.currentMode.echoReasoning) {
1635
+ if (reasoning && reasoningField)
1636
+ extras[reasoningField] = reasoning;
1637
+ if (reasoningDetailsByIndex.size > 0) {
1638
+ extras.reasoning_details = [...reasoningDetailsByIndex.entries()]
1639
+ .sort((a, b) => a[0] - b[0]).map(([, v]) => v);
1640
+ }
1608
1641
  }
1609
1642
  return {
1610
1643
  text,
@@ -13,7 +13,7 @@
13
13
  import * as fs from "node:fs";
14
14
  import * as path from "node:path";
15
15
  import * as os from "node:os";
16
- import { getSettings } from "../settings.js";
16
+ import { CONFIG_DIR, getSettings } from "../settings.js";
17
17
  /** Parse YAML frontmatter from a SKILL.md file. */
18
18
  function parseFrontmatter(content) {
19
19
  const match = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/);
@@ -133,7 +133,7 @@ export function discoverGlobalSkills() {
133
133
  return _cachedGlobalSkills;
134
134
  const seen = new Set();
135
135
  const skills = [];
136
- addUnique(skills, scanDir(path.join(os.homedir(), ".agent-sh", "skills")), seen);
136
+ addUnique(skills, scanDir(path.join(CONFIG_DIR, "skills")), seen);
137
137
  const settings = getSettings();
138
138
  for (const p of settings.skillPaths ?? []) {
139
139
  addUnique(skills, scanDir(path.resolve(expandHome(p))), seen);
@@ -14,9 +14,8 @@ export function formatSkillsBlock(skills) {
14
14
  + "Load a skill's full content with read_file on its file path when needed.\n\n"
15
15
  + skills.map(s => `- **${s.name}**: ${s.description}\n Path: ${s.filePath}`).join("\n\n");
16
16
  }
17
- // Resolve to the user's home-based config dir — user's standing instructions to the agent
18
- import * as os from "node:os";
19
- const GLOBAL_AGENTS_MD = path.join(os.homedir(), ".agent-sh", "AGENTS.md");
17
+ import { CONFIG_DIR } from "../settings.js";
18
+ const GLOBAL_AGENTS_MD = path.join(CONFIG_DIR, "AGENTS.md");
20
19
  // ── File caches ─────────────────────────────────────────────────────
21
20
  // Convention files (CLAUDE.md/AGENT.md) are walked synchronously from
22
21
  // CWD to root on every query. In practice they almost never change,
package/dist/core.js CHANGED
@@ -27,9 +27,8 @@ import { TerminalBuffer } from "./utils/terminal-buffer.js";
27
27
  import crypto from "node:crypto";
28
28
  import * as fs from "node:fs";
29
29
  import * as path from "node:path";
30
- import * as os from "node:os";
31
30
  import { DefaultCompositor, StdoutSurface } from "./utils/compositor.js";
32
- const STORAGE_ROOT = path.join(os.homedir(), ".agent-sh");
31
+ import { CONFIG_DIR } from "./settings.js";
33
32
  // Re-export types that library consumers need
34
33
  export { EventBus } from "./event-bus.js";
35
34
  export { palette, setPalette, resetPalette } from "./utils/palette.js";
@@ -43,6 +42,7 @@ export function createCore(config) {
43
42
  // short enough to read/remember. Legacy content may have 16-char iids; any
44
43
  // parsers should accept ≥6 hex chars.
45
44
  const instanceId = crypto.randomBytes(3).toString("hex");
45
+ bus.setSource(instanceId);
46
46
  const settings = settingsMod.getSettings();
47
47
  // Expose raw CLI config so the agent backend extension can resolve
48
48
  // providers and create the LLM client.
@@ -170,7 +170,7 @@ export function createCore(config) {
170
170
  createFencedBlockTransform: (o) => streamTransform.createFencedBlockTransform(bus, o),
171
171
  getExtensionSettings: settingsMod.getExtensionSettings,
172
172
  getStoragePath: (namespace) => {
173
- const dir = path.join(STORAGE_ROOT, namespace);
173
+ const dir = path.join(CONFIG_DIR, namespace);
174
174
  fs.mkdirSync(dir, { recursive: true });
175
175
  return dir;
176
176
  },
@@ -188,6 +188,7 @@ export function createCore(config) {
188
188
  list: () => handlers.list(),
189
189
  get terminalBuffer() { return getTerminalBuffer(); },
190
190
  compositor,
191
+ onDispose: () => { },
191
192
  createRemoteSession: (opts) => {
192
193
  const { surface } = opts;
193
194
  const cleanups = [];
@@ -233,6 +233,30 @@ export interface ShellEvents {
233
233
  totalTokens: number;
234
234
  budgetTokens: number;
235
235
  };
236
+ "context:snapshot": {
237
+ messages: unknown[];
238
+ contextWindow: number;
239
+ activeTokens: number;
240
+ };
241
+ "context:compact": {
242
+ strategy?: {
243
+ kind: "two-tier-pin";
244
+ target: number;
245
+ keepRecent?: number;
246
+ force?: boolean;
247
+ } | {
248
+ kind: "rewind";
249
+ toIndex: number;
250
+ } | {
251
+ kind: "replace";
252
+ messages: unknown[];
253
+ };
254
+ stats?: {
255
+ before: number;
256
+ after: number;
257
+ evictedCount: number;
258
+ };
259
+ };
236
260
  "agent:register-backend": {
237
261
  name: string;
238
262
  kill: () => void;
@@ -291,6 +315,7 @@ export interface ShellEvents {
291
315
  id: string;
292
316
  reasoning?: boolean;
293
317
  contextWindow?: number;
318
+ echoReasoning?: boolean;
294
319
  })[];
295
320
  /** Provider supports the reasoning_effort parameter. Default: true. */
296
321
  supportsReasoningEffort?: boolean;
@@ -357,6 +382,14 @@ export type ContentBlock = {
357
382
  type Listener<T> = (payload: T) => void;
358
383
  type PipeListener<T> = (payload: T) => T;
359
384
  type AsyncPipeListener<T> = (payload: T) => T | Promise<T>;
385
+ /** Envelope stamped on every emitted event. */
386
+ export interface BusMeta {
387
+ source: string;
388
+ ts: number;
389
+ id: string;
390
+ name: string;
391
+ }
392
+ export type AnyListener = (name: string, payload: unknown, meta: BusMeta) => void;
360
393
  /**
361
394
  * Typed event bus with two modes:
362
395
  * - emit/on/off: fire-and-forget notifications
@@ -367,12 +400,25 @@ export declare class EventBus {
367
400
  private emitter;
368
401
  private pipeListeners;
369
402
  private asyncPipeListeners;
403
+ private source;
404
+ private nextSeq;
405
+ private anyListeners;
406
+ /** Set the source id stamped onto every emitted event. */
407
+ setSource(src: string): void;
408
+ /** Subscribe to every emitted event with full envelope. Returns unsubscribe. */
409
+ onAny(fn: AnyListener): () => void;
410
+ /** Stamp + dispatch — used by every emit path. */
411
+ private dispatch;
370
412
  /** Subscribe to a fire-and-forget event. */
371
413
  on<K extends keyof ShellEvents>(event: K, fn: Listener<ShellEvents[K]>): void;
372
414
  /** Unsubscribe from a fire-and-forget event. */
373
415
  off<K extends keyof ShellEvents>(event: K, fn: Listener<ShellEvents[K]>): void;
374
416
  /** Emit a fire-and-forget event. */
375
417
  emit<K extends keyof ShellEvents>(event: K, payload: ShellEvents[K]): void;
418
+ /** Re-dispatch an event with externally-supplied meta. Used by bridges
419
+ * and replay tools to preserve the original source/ts/id of remote or
420
+ * recorded events instead of restamping them as locally originated. */
421
+ relay(meta: BusMeta, payload: unknown): void;
376
422
  /**
377
423
  * Transform-then-notify: run the payload through any registered pipe
378
424
  * listeners (transforms), then emit the final result to regular `on`
package/dist/event-bus.js CHANGED
@@ -9,6 +9,40 @@ export class EventBus {
9
9
  emitter = new EventEmitter().setMaxListeners(0);
10
10
  pipeListeners = new Map();
11
11
  asyncPipeListeners = new Map();
12
+ source = "0000";
13
+ nextSeq = 0;
14
+ anyListeners = [];
15
+ /** Set the source id stamped onto every emitted event. */
16
+ setSource(src) {
17
+ this.source = src;
18
+ }
19
+ /** Subscribe to every emitted event with full envelope. Returns unsubscribe. */
20
+ onAny(fn) {
21
+ this.anyListeners.push(fn);
22
+ return () => {
23
+ const i = this.anyListeners.indexOf(fn);
24
+ if (i !== -1)
25
+ this.anyListeners.splice(i, 1);
26
+ };
27
+ }
28
+ /** Stamp + dispatch — used by every emit path. */
29
+ dispatch(name, payload) {
30
+ if (this.anyListeners.length > 0) {
31
+ const meta = {
32
+ source: this.source,
33
+ ts: Date.now(),
34
+ id: `${this.source}:${this.nextSeq++}`,
35
+ name,
36
+ };
37
+ for (const fn of this.anyListeners) {
38
+ try {
39
+ fn(name, payload, meta);
40
+ }
41
+ catch { /* swallow */ }
42
+ }
43
+ }
44
+ this.emitter.emit(name, payload);
45
+ }
12
46
  /** Subscribe to a fire-and-forget event. */
13
47
  on(event, fn) {
14
48
  this.emitter.on(event, fn);
@@ -19,7 +53,21 @@ export class EventBus {
19
53
  }
20
54
  /** Emit a fire-and-forget event. */
21
55
  emit(event, payload) {
22
- this.emitter.emit(event, payload);
56
+ this.dispatch(event, payload);
57
+ }
58
+ /** Re-dispatch an event with externally-supplied meta. Used by bridges
59
+ * and replay tools to preserve the original source/ts/id of remote or
60
+ * recorded events instead of restamping them as locally originated. */
61
+ relay(meta, payload) {
62
+ if (this.anyListeners.length > 0) {
63
+ for (const fn of this.anyListeners) {
64
+ try {
65
+ fn(meta.name, payload, meta);
66
+ }
67
+ catch { /* swallow */ }
68
+ }
69
+ }
70
+ this.emitter.emit(meta.name, payload);
23
71
  }
24
72
  /**
25
73
  * Transform-then-notify: run the payload through any registered pipe
@@ -38,7 +86,7 @@ export class EventBus {
38
86
  }
39
87
  transformed = payload; // fall back to untransformed
40
88
  }
41
- this.emitter.emit(event, transformed);
89
+ this.dispatch(event, transformed);
42
90
  }
43
91
  /** Register a transform listener for a pipeline event. */
44
92
  onPipe(event, fn) {
@@ -103,7 +151,7 @@ export class EventBus {
103
151
  */
104
152
  async emitPipeAsync(event, payload) {
105
153
  // Phase 1: notify (lets renderers prepare for interactive I/O)
106
- this.emitter.emit(event, payload);
154
+ this.dispatch(event, payload);
107
155
  // Phase 2: transform (extensions provide decisions)
108
156
  const listeners = this.asyncPipeListeners.get(event);
109
157
  if (!listeners)
@@ -96,6 +96,7 @@ function createScopedContext(ctx, extensionName) {
96
96
  registerTool: scopedRegisterTool,
97
97
  unregisterTool: ctx.unregisterTool,
98
98
  registerCommand: scopedRegisterCommand,
99
+ onDispose: (fn) => { cleanups.push(fn); },
99
100
  };
100
101
  const dispose = () => {
101
102
  for (const fn of cleanups) {
@@ -32,6 +32,7 @@ export default function agentBackend(ctx) {
32
32
  contextWindow: mc?.contextWindow ?? p.contextWindow,
33
33
  reasoning: mc?.reasoning,
34
34
  supportsReasoningEffort: p.supportsReasoningEffort,
35
+ echoReasoning: mc?.echoReasoning,
35
36
  });
36
37
  }
37
38
  }
@@ -135,7 +136,7 @@ export default function agentBackend(ctx) {
135
136
  }
136
137
  else {
137
138
  modelIds.push(m.id);
138
- caps.set(m.id, { reasoning: m.reasoning, contextWindow: m.contextWindow });
139
+ caps.set(m.id, { reasoning: m.reasoning, contextWindow: m.contextWindow, echoReasoning: m.echoReasoning });
139
140
  }
140
141
  }
141
142
  providerRegistry.set(p.id, {
@@ -156,6 +157,7 @@ export default function agentBackend(ctx) {
156
157
  contextWindow: mc?.contextWindow,
157
158
  reasoning: mc?.reasoning,
158
159
  supportsReasoningEffort: p.supportsReasoningEffort,
160
+ echoReasoning: mc?.echoReasoning,
159
161
  };
160
162
  });
161
163
  bus.emit("config:add-modes", { modes: addModes });
@@ -197,6 +199,7 @@ export default function agentBackend(ctx) {
197
199
  contextWindow: mc?.contextWindow ?? p.contextWindow,
198
200
  reasoning: mc?.reasoning,
199
201
  supportsReasoningEffort: p.supportsReasoningEffort,
202
+ echoReasoning: mc?.echoReasoning,
200
203
  };
201
204
  });
202
205
  bus.emit("config:set-modes", { modes: newModes });
@@ -1,5 +1,11 @@
1
+ import { getSettings } from "../settings.js";
1
2
  const BASE_URL = "https://openrouter.ai/api/v1";
2
3
  const DEFAULT_MODELS = ["anthropic/claude-sonnet-4.6"];
4
+ // Built-in defaults for models requiring reasoning_content echoed back
5
+ // (server 400s without it). Extend or override in settings.json:
6
+ // providers.openrouter.echoReasoningPatterns = ["deepseek", "..."]
7
+ // providers.openrouter.models[*].echoReasoning = true | false
8
+ const BUILTIN_ECHO_REASONING_PATTERNS = [/deepseek/i];
3
9
  export default function activate(ctx) {
4
10
  const apiKey = process.env.OPENROUTER_API_KEY;
5
11
  if (!apiKey)
@@ -14,6 +20,8 @@ export default function activate(ctx) {
14
20
  fetchModels(apiKey).then((models) => {
15
21
  if (models.length === 0)
16
22
  return;
23
+ const userOverrides = readUserOverrides();
24
+ const patterns = readEchoPatterns();
17
25
  ctx.bus.emit("provider:register", {
18
26
  id: "openrouter",
19
27
  apiKey,
@@ -24,10 +32,34 @@ export default function activate(ctx) {
24
32
  id: m.id,
25
33
  reasoning: m.supported_parameters?.includes("reasoning") ?? false,
26
34
  contextWindow: m.context_length,
35
+ echoReasoning: userOverrides.get(m.id) ?? patterns.some((re) => re.test(m.id)),
27
36
  })),
28
37
  });
29
38
  }).catch(() => { });
30
39
  }
40
+ function readEchoPatterns() {
41
+ const userPatterns = getSettings().providers?.openrouter?.echoReasoningPatterns ?? [];
42
+ const compiled = [];
43
+ for (const src of userPatterns) {
44
+ try {
45
+ compiled.push(new RegExp(src, "i"));
46
+ }
47
+ catch { /* skip invalid pattern */ }
48
+ }
49
+ return [...BUILTIN_ECHO_REASONING_PATTERNS, ...compiled];
50
+ }
51
+ function readUserOverrides() {
52
+ const out = new Map();
53
+ const models = getSettings().providers?.openrouter?.models;
54
+ if (!Array.isArray(models))
55
+ return out;
56
+ for (const m of models) {
57
+ if (typeof m === "object" && m && m.echoReasoning !== undefined) {
58
+ out.set(m.id, m.echoReasoning);
59
+ }
60
+ }
61
+ return out;
62
+ }
31
63
  async function fetchModels(apiKey) {
32
64
  const res = await fetch(`${BASE_URL}/models`, {
33
65
  headers: { Authorization: `Bearer ${apiKey}` },
package/dist/init.js CHANGED
@@ -1,7 +1,6 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
- import * as os from "node:os";
4
- const CONFIG_DIR = path.join(os.homedir(), ".agent-sh");
3
+ import { CONFIG_DIR } from "./settings.js";
5
4
  const EXTENSIONS_DIR = path.join(CONFIG_DIR, "extensions");
6
5
  const SETTINGS_PATH = path.join(CONFIG_DIR, "settings.json");
7
6
  const EXAMPLE_PATH = path.join(CONFIG_DIR, "settings.example.json");
@@ -1,3 +1,5 @@
1
+ /** Root config directory. Override via AGENT_SH_HOME for isolated instances
2
+ * (testing, multi-agent setups). Path is resolved at module load. */
1
3
  export declare const CONFIG_DIR: string;
2
4
  /** Per-model capability overrides. */
3
5
  export interface ModelCapabilityConfig {
@@ -7,6 +9,8 @@ export interface ModelCapabilityConfig {
7
9
  reasoning?: boolean;
8
10
  /** Context window size in tokens for this specific model. */
9
11
  contextWindow?: number;
12
+ /** Echo reasoning_content back on assistant turns. Required by DeepSeek. */
13
+ echoReasoning?: boolean;
10
14
  }
11
15
  /** Provider profile — a named LLM configuration. */
12
16
  export interface ProviderConfig {
@@ -20,6 +24,9 @@ export interface ProviderConfig {
20
24
  models?: (string | ModelCapabilityConfig)[];
21
25
  /** Context window size in tokens (e.g. 128000). Used for usage display. */
22
26
  contextWindow?: number;
27
+ /** Case-insensitive regex sources matched against model id; matches default
28
+ * to echoReasoning=true. Per-model echoReasoning still wins. */
29
+ echoReasoningPatterns?: string[];
23
30
  }
24
31
  export interface Settings {
25
32
  /** Extensions to load (npm packages or file paths). */
@@ -136,6 +143,7 @@ export interface ResolvedProvider {
136
143
  modelCapabilities?: Map<string, {
137
144
  reasoning?: boolean;
138
145
  contextWindow?: number;
146
+ echoReasoning?: boolean;
139
147
  }>;
140
148
  }
141
149
  /**
package/dist/settings.js CHANGED
@@ -7,7 +7,11 @@
7
7
  import * as fs from "node:fs";
8
8
  import * as path from "node:path";
9
9
  import * as os from "node:os";
10
- export const CONFIG_DIR = path.join(os.homedir(), ".agent-sh");
10
+ /** Root config directory. Override via AGENT_SH_HOME for isolated instances
11
+ * (testing, multi-agent setups). Path is resolved at module load. */
12
+ export const CONFIG_DIR = process.env.AGENT_SH_HOME
13
+ ? path.resolve(process.env.AGENT_SH_HOME)
14
+ : path.join(os.homedir(), ".agent-sh");
11
15
  const SETTINGS_PATH = path.join(CONFIG_DIR, "settings.json");
12
16
  const DEFAULTS = {
13
17
  extensions: [],
@@ -143,8 +147,8 @@ export function resolveProvider(name) {
143
147
  }
144
148
  else {
145
149
  modelIds.push(m.id);
146
- if (m.reasoning !== undefined || m.contextWindow !== undefined) {
147
- caps.set(m.id, { reasoning: m.reasoning, contextWindow: m.contextWindow });
150
+ if (m.reasoning !== undefined || m.contextWindow !== undefined || m.echoReasoning !== undefined) {
151
+ caps.set(m.id, { reasoning: m.reasoning, contextWindow: m.contextWindow, echoReasoning: m.echoReasoning });
148
152
  }
149
153
  }
150
154
  }
@@ -1,9 +1,6 @@
1
1
  import type { EventBus } from "../event-bus.js";
2
- /**
3
- * Narrow contract between InputHandler and its host (Shell).
4
- * InputHandler never touches the PTY or EventBus directly —
5
- * it goes through this interface for all cross-cutting concerns.
6
- */
2
+ import { TuiInputView } from "./tui-input-view.js";
3
+ /** Narrow contract between InputHandler and its host (Shell). */
7
4
  export interface InputContext {
8
5
  isForegroundBusy(): boolean;
9
6
  getCwd(): string;
@@ -13,6 +10,7 @@ export interface InputContext {
13
10
  redrawPrompt(): void;
14
11
  freshPrompt(): void;
15
12
  }
13
+ /** Line editor + shell-passthrough buffer. Delegates rendering to TuiInputView. */
16
14
  export declare class InputHandler {
17
15
  private ctx;
18
16
  private lineBuffer;
@@ -24,15 +22,13 @@ export declare class InputHandler {
24
22
  private autocompleteActive;
25
23
  private autocompleteIndex;
26
24
  private autocompleteItems;
27
- private autocompleteLines;
28
25
  private history;
29
26
  private historyIndex;
30
27
  private savedBuffer;
31
- private cursorRowsBelow;
32
- private cursorTermCol;
33
28
  private escapeTimer;
34
29
  private bus;
35
30
  private onShowAgentInfo;
31
+ private view;
36
32
  constructor(opts: {
37
33
  ctx: InputContext;
38
34
  bus: EventBus;
@@ -40,29 +36,23 @@ export declare class InputHandler {
40
36
  info: string;
41
37
  model?: string;
42
38
  };
39
+ view?: TuiInputView;
43
40
  });
44
41
  private registerMode;
45
42
  private loadHistory;
46
43
  private saveHistory;
47
- /** Write the mode prompt line with cursor at the correct position. */
48
- private writeModePromptLine;
44
+ private drawPrompt;
49
45
  handleInput(data: string): void;
50
46
  private enterMode;
51
47
  private exitMode;
52
- /** Move to the start of the prompt area and clear everything below. */
53
- private clearPromptArea;
54
48
  printPrompt(): void;
55
- /**
56
- * Called when agent processing completes. Returns true if the input
57
- * handler re-entered a mode (so caller should skip shell prompt).
58
- */
49
+ /** Called when agent processing completes. Returns true if the input
50
+ * handler re-entered a mode (so caller should skip shell prompt). */
59
51
  handleProcessingDone(): boolean;
60
52
  private renderModeInput;
61
53
  private updateAutocomplete;
62
- private renderAutocomplete;
63
54
  private applyAutocomplete;
64
55
  private dismissAutocomplete;
65
- private clearAutocompleteLines;
66
56
  private handleModeInput;
67
57
  private processModeActions;
68
58
  }