@kolisachint/hoocode-agent 0.4.107 → 0.4.108

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 (26) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/dist/modes/interactive/bash-execution-controller.d.ts +34 -0
  3. package/dist/modes/interactive/bash-execution-controller.d.ts.map +1 -0
  4. package/dist/modes/interactive/bash-execution-controller.js +98 -0
  5. package/dist/modes/interactive/bash-execution-controller.js.map +1 -0
  6. package/dist/modes/interactive/interactive-mode.d.ts +5 -45
  7. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  8. package/dist/modes/interactive/interactive-mode.js +91 -779
  9. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  10. package/dist/modes/interactive/login-controller.d.ts +50 -0
  11. package/dist/modes/interactive/login-controller.d.ts.map +1 -0
  12. package/dist/modes/interactive/login-controller.js +352 -0
  13. package/dist/modes/interactive/login-controller.js.map +1 -0
  14. package/dist/modes/interactive/message-queue-controller.d.ts +49 -0
  15. package/dist/modes/interactive/message-queue-controller.d.ts.map +1 -0
  16. package/dist/modes/interactive/message-queue-controller.js +184 -0
  17. package/dist/modes/interactive/message-queue-controller.js.map +1 -0
  18. package/dist/modes/interactive/model-controller.d.ts +43 -0
  19. package/dist/modes/interactive/model-controller.d.ts.map +1 -0
  20. package/dist/modes/interactive/model-controller.js +184 -0
  21. package/dist/modes/interactive/model-controller.js.map +1 -0
  22. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  23. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  24. package/examples/extensions/sandbox/package.json +1 -1
  25. package/examples/extensions/with-deps/package.json +1 -1
  26. package/package.json +4 -4
package/CHANGELOG.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.4.108] - 2026-07-03
4
+
3
5
  ## [0.4.107] - 2026-07-02
4
6
 
5
7
  ## [0.4.106] - 2026-07-01
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Bash command execution for the interactive mode (the `!cmd` prompt mode).
3
+ *
4
+ * Runs a bash command through the session (letting extensions intercept via
5
+ * the user_bash event), renders a BashExecutionComponent for it, and streams
6
+ * output into that component. Commands started while the agent is streaming are
7
+ * parked in the pending area and moved into the chat transcript once the turn
8
+ * finishes. Extracted from interactive-mode.ts behind a narrow
9
+ * BashExecutionControllerDeps interface.
10
+ */
11
+ import type { Container, TUI } from "@kolisachint/hoocode-tui";
12
+ import type { AgentSession } from "../../core/agent-session.js";
13
+ /** The slice of the interactive mode the bash execution needs. */
14
+ export interface BashExecutionControllerDeps {
15
+ /** The active session (read at call time; the session can be swapped). */
16
+ get session(): AgentSession;
17
+ ui: TUI;
18
+ /** Holds bash rows started while the agent is streaming. */
19
+ pendingMessagesContainer: Container;
20
+ /** The chat transcript. */
21
+ chatContainer: Container;
22
+ showError(errorMessage: string): void;
23
+ }
24
+ export declare class BashExecutionController {
25
+ private readonly deps;
26
+ private bashComponent;
27
+ private pendingBashComponents;
28
+ constructor(deps: BashExecutionControllerDeps);
29
+ private get session();
30
+ /** Move pending bash components from pending area to chat */
31
+ flushPendingBashComponents(): void;
32
+ handleBashCommand(command: string, excludeFromContext?: boolean): Promise<void>;
33
+ }
34
+ //# sourceMappingURL=bash-execution-controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bash-execution-controller.d.ts","sourceRoot":"","sources":["../../../src/modes/interactive/bash-execution-controller.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAIhE,kEAAkE;AAClE,MAAM,WAAW,2BAA2B;IAC3C,0EAA0E;IAC1E,IAAI,OAAO,IAAI,YAAY,CAAC;IAC5B,EAAE,EAAE,GAAG,CAAC;IACR,4DAA4D;IAC5D,wBAAwB,EAAE,SAAS,CAAC;IACpC,2BAA2B;IAC3B,aAAa,EAAE,SAAS,CAAC;IACzB,SAAS,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CACtC;AAED,qBAAa,uBAAuB;IAOvB,OAAO,CAAC,QAAQ,CAAC,IAAI;IALjC,OAAO,CAAC,aAAa,CAAiD;IAGtE,OAAO,CAAC,qBAAqB,CAAgC;IAE7D,YAA6B,IAAI,EAAE,2BAA2B,EAAI;IAElE,OAAO,KAAK,OAAO,GAElB;IAED,6DAA6D;IAC7D,0BAA0B,IAAI,IAAI,CAMjC;IAEK,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,kBAAkB,UAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAqFlF;CACD","sourcesContent":["/**\n * Bash command execution for the interactive mode (the `!cmd` prompt mode).\n *\n * Runs a bash command through the session (letting extensions intercept via\n * the user_bash event), renders a BashExecutionComponent for it, and streams\n * output into that component. Commands started while the agent is streaming are\n * parked in the pending area and moved into the chat transcript once the turn\n * finishes. Extracted from interactive-mode.ts behind a narrow\n * BashExecutionControllerDeps interface.\n */\n\nimport type { Container, TUI } from \"@kolisachint/hoocode-tui\";\nimport type { AgentSession } from \"../../core/agent-session.js\";\nimport type { TruncationResult } from \"../../core/tools/truncate.js\";\nimport { BashExecutionComponent } from \"./components/bash-execution.js\";\n\n/** The slice of the interactive mode the bash execution needs. */\nexport interface BashExecutionControllerDeps {\n\t/** The active session (read at call time; the session can be swapped). */\n\tget session(): AgentSession;\n\tui: TUI;\n\t/** Holds bash rows started while the agent is streaming. */\n\tpendingMessagesContainer: Container;\n\t/** The chat transcript. */\n\tchatContainer: Container;\n\tshowError(errorMessage: string): void;\n}\n\nexport class BashExecutionController {\n\t// Current bash execution component\n\tprivate bashComponent: BashExecutionComponent | undefined = undefined;\n\n\t// Pending bash components (shown in pending area, moved to chat on submit)\n\tprivate pendingBashComponents: BashExecutionComponent[] = [];\n\n\tconstructor(private readonly deps: BashExecutionControllerDeps) {}\n\n\tprivate get session(): AgentSession {\n\t\treturn this.deps.session;\n\t}\n\n\t/** Move pending bash components from pending area to chat */\n\tflushPendingBashComponents(): void {\n\t\tfor (const component of this.pendingBashComponents) {\n\t\t\tthis.deps.pendingMessagesContainer.removeChild(component);\n\t\t\tthis.deps.chatContainer.addChild(component);\n\t\t}\n\t\tthis.pendingBashComponents = [];\n\t}\n\n\tasync handleBashCommand(command: string, excludeFromContext = false): Promise<void> {\n\t\tconst extensionRunner = this.session.extensionRunner;\n\n\t\t// Emit user_bash event to let extensions intercept\n\t\tconst eventResult = await extensionRunner.emitUserBash({\n\t\t\ttype: \"user_bash\",\n\t\t\tcommand,\n\t\t\texcludeFromContext,\n\t\t\tcwd: this.session.sessionManager.getCwd(),\n\t\t});\n\n\t\t// If extension returned a full result, use it directly\n\t\tif (eventResult?.result) {\n\t\t\tconst result = eventResult.result;\n\n\t\t\t// Create UI component for display\n\t\t\tthis.bashComponent = new BashExecutionComponent(command, this.deps.ui, excludeFromContext);\n\t\t\tif (this.session.isStreaming) {\n\t\t\t\tthis.deps.pendingMessagesContainer.addChild(this.bashComponent);\n\t\t\t\tthis.pendingBashComponents.push(this.bashComponent);\n\t\t\t} else {\n\t\t\t\tthis.deps.chatContainer.addChild(this.bashComponent);\n\t\t\t}\n\n\t\t\t// Show output and complete\n\t\t\tif (result.output) {\n\t\t\t\tthis.bashComponent.appendOutput(result.output);\n\t\t\t}\n\t\t\tthis.bashComponent.setComplete(\n\t\t\t\tresult.exitCode,\n\t\t\t\tresult.cancelled,\n\t\t\t\tresult.truncated ? ({ truncated: true, content: result.output } as TruncationResult) : undefined,\n\t\t\t\tresult.fullOutputPath,\n\t\t\t);\n\n\t\t\t// Record the result in session\n\t\t\tthis.session.recordBashResult(command, result, { excludeFromContext });\n\t\t\tthis.bashComponent = undefined;\n\t\t\tthis.deps.ui.requestRender();\n\t\t\treturn;\n\t\t}\n\n\t\t// Normal execution path (possibly with custom operations)\n\t\tconst isDeferred = this.session.isStreaming;\n\t\tthis.bashComponent = new BashExecutionComponent(command, this.deps.ui, excludeFromContext);\n\n\t\tif (isDeferred) {\n\t\t\t// Show in pending area when agent is streaming\n\t\t\tthis.deps.pendingMessagesContainer.addChild(this.bashComponent);\n\t\t\tthis.pendingBashComponents.push(this.bashComponent);\n\t\t} else {\n\t\t\t// Show in chat immediately when agent is idle\n\t\t\tthis.deps.chatContainer.addChild(this.bashComponent);\n\t\t}\n\t\tthis.deps.ui.requestRender();\n\n\t\ttry {\n\t\t\tconst result = await this.session.executeBash(\n\t\t\t\tcommand,\n\t\t\t\t(chunk) => {\n\t\t\t\t\tif (this.bashComponent) {\n\t\t\t\t\t\tthis.bashComponent.appendOutput(chunk);\n\t\t\t\t\t\tthis.deps.ui.requestRender();\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t{ excludeFromContext, operations: eventResult?.operations },\n\t\t\t);\n\n\t\t\tif (this.bashComponent) {\n\t\t\t\tthis.bashComponent.setComplete(\n\t\t\t\t\tresult.exitCode,\n\t\t\t\t\tresult.cancelled,\n\t\t\t\t\tresult.truncated ? ({ truncated: true, content: result.output } as TruncationResult) : undefined,\n\t\t\t\t\tresult.fullOutputPath,\n\t\t\t\t);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tif (this.bashComponent) {\n\t\t\t\tthis.bashComponent.setComplete(undefined, false);\n\t\t\t}\n\t\t\tthis.deps.showError(`Bash command failed: ${error instanceof Error ? error.message : \"Unknown error\"}`);\n\t\t}\n\n\t\tthis.bashComponent = undefined;\n\t\tthis.deps.ui.requestRender();\n\t}\n}\n"]}
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Bash command execution for the interactive mode (the `!cmd` prompt mode).
3
+ *
4
+ * Runs a bash command through the session (letting extensions intercept via
5
+ * the user_bash event), renders a BashExecutionComponent for it, and streams
6
+ * output into that component. Commands started while the agent is streaming are
7
+ * parked in the pending area and moved into the chat transcript once the turn
8
+ * finishes. Extracted from interactive-mode.ts behind a narrow
9
+ * BashExecutionControllerDeps interface.
10
+ */
11
+ import { BashExecutionComponent } from "./components/bash-execution.js";
12
+ export class BashExecutionController {
13
+ deps;
14
+ // Current bash execution component
15
+ bashComponent = undefined;
16
+ // Pending bash components (shown in pending area, moved to chat on submit)
17
+ pendingBashComponents = [];
18
+ constructor(deps) {
19
+ this.deps = deps;
20
+ }
21
+ get session() {
22
+ return this.deps.session;
23
+ }
24
+ /** Move pending bash components from pending area to chat */
25
+ flushPendingBashComponents() {
26
+ for (const component of this.pendingBashComponents) {
27
+ this.deps.pendingMessagesContainer.removeChild(component);
28
+ this.deps.chatContainer.addChild(component);
29
+ }
30
+ this.pendingBashComponents = [];
31
+ }
32
+ async handleBashCommand(command, excludeFromContext = false) {
33
+ const extensionRunner = this.session.extensionRunner;
34
+ // Emit user_bash event to let extensions intercept
35
+ const eventResult = await extensionRunner.emitUserBash({
36
+ type: "user_bash",
37
+ command,
38
+ excludeFromContext,
39
+ cwd: this.session.sessionManager.getCwd(),
40
+ });
41
+ // If extension returned a full result, use it directly
42
+ if (eventResult?.result) {
43
+ const result = eventResult.result;
44
+ // Create UI component for display
45
+ this.bashComponent = new BashExecutionComponent(command, this.deps.ui, excludeFromContext);
46
+ if (this.session.isStreaming) {
47
+ this.deps.pendingMessagesContainer.addChild(this.bashComponent);
48
+ this.pendingBashComponents.push(this.bashComponent);
49
+ }
50
+ else {
51
+ this.deps.chatContainer.addChild(this.bashComponent);
52
+ }
53
+ // Show output and complete
54
+ if (result.output) {
55
+ this.bashComponent.appendOutput(result.output);
56
+ }
57
+ this.bashComponent.setComplete(result.exitCode, result.cancelled, result.truncated ? { truncated: true, content: result.output } : undefined, result.fullOutputPath);
58
+ // Record the result in session
59
+ this.session.recordBashResult(command, result, { excludeFromContext });
60
+ this.bashComponent = undefined;
61
+ this.deps.ui.requestRender();
62
+ return;
63
+ }
64
+ // Normal execution path (possibly with custom operations)
65
+ const isDeferred = this.session.isStreaming;
66
+ this.bashComponent = new BashExecutionComponent(command, this.deps.ui, excludeFromContext);
67
+ if (isDeferred) {
68
+ // Show in pending area when agent is streaming
69
+ this.deps.pendingMessagesContainer.addChild(this.bashComponent);
70
+ this.pendingBashComponents.push(this.bashComponent);
71
+ }
72
+ else {
73
+ // Show in chat immediately when agent is idle
74
+ this.deps.chatContainer.addChild(this.bashComponent);
75
+ }
76
+ this.deps.ui.requestRender();
77
+ try {
78
+ const result = await this.session.executeBash(command, (chunk) => {
79
+ if (this.bashComponent) {
80
+ this.bashComponent.appendOutput(chunk);
81
+ this.deps.ui.requestRender();
82
+ }
83
+ }, { excludeFromContext, operations: eventResult?.operations });
84
+ if (this.bashComponent) {
85
+ this.bashComponent.setComplete(result.exitCode, result.cancelled, result.truncated ? { truncated: true, content: result.output } : undefined, result.fullOutputPath);
86
+ }
87
+ }
88
+ catch (error) {
89
+ if (this.bashComponent) {
90
+ this.bashComponent.setComplete(undefined, false);
91
+ }
92
+ this.deps.showError(`Bash command failed: ${error instanceof Error ? error.message : "Unknown error"}`);
93
+ }
94
+ this.bashComponent = undefined;
95
+ this.deps.ui.requestRender();
96
+ }
97
+ }
98
+ //# sourceMappingURL=bash-execution-controller.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bash-execution-controller.js","sourceRoot":"","sources":["../../../src/modes/interactive/bash-execution-controller.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Bash command execution for the interactive mode (the `!cmd` prompt mode).\n *\n * Runs a bash command through the session (letting extensions intercept via\n * the user_bash event), renders a BashExecutionComponent for it, and streams\n * output into that component. Commands started while the agent is streaming are\n * parked in the pending area and moved into the chat transcript once the turn\n * finishes. Extracted from interactive-mode.ts behind a narrow\n * BashExecutionControllerDeps interface.\n */\n\nimport type { Container, TUI } from \"@kolisachint/hoocode-tui\";\nimport type { AgentSession } from \"../../core/agent-session.js\";\nimport type { TruncationResult } from \"../../core/tools/truncate.js\";\nimport { BashExecutionComponent } from \"./components/bash-execution.js\";\n\n/** The slice of the interactive mode the bash execution needs. */\nexport interface BashExecutionControllerDeps {\n\t/** The active session (read at call time; the session can be swapped). */\n\tget session(): AgentSession;\n\tui: TUI;\n\t/** Holds bash rows started while the agent is streaming. */\n\tpendingMessagesContainer: Container;\n\t/** The chat transcript. */\n\tchatContainer: Container;\n\tshowError(errorMessage: string): void;\n}\n\nexport class BashExecutionController {\n\t// Current bash execution component\n\tprivate bashComponent: BashExecutionComponent | undefined = undefined;\n\n\t// Pending bash components (shown in pending area, moved to chat on submit)\n\tprivate pendingBashComponents: BashExecutionComponent[] = [];\n\n\tconstructor(private readonly deps: BashExecutionControllerDeps) {}\n\n\tprivate get session(): AgentSession {\n\t\treturn this.deps.session;\n\t}\n\n\t/** Move pending bash components from pending area to chat */\n\tflushPendingBashComponents(): void {\n\t\tfor (const component of this.pendingBashComponents) {\n\t\t\tthis.deps.pendingMessagesContainer.removeChild(component);\n\t\t\tthis.deps.chatContainer.addChild(component);\n\t\t}\n\t\tthis.pendingBashComponents = [];\n\t}\n\n\tasync handleBashCommand(command: string, excludeFromContext = false): Promise<void> {\n\t\tconst extensionRunner = this.session.extensionRunner;\n\n\t\t// Emit user_bash event to let extensions intercept\n\t\tconst eventResult = await extensionRunner.emitUserBash({\n\t\t\ttype: \"user_bash\",\n\t\t\tcommand,\n\t\t\texcludeFromContext,\n\t\t\tcwd: this.session.sessionManager.getCwd(),\n\t\t});\n\n\t\t// If extension returned a full result, use it directly\n\t\tif (eventResult?.result) {\n\t\t\tconst result = eventResult.result;\n\n\t\t\t// Create UI component for display\n\t\t\tthis.bashComponent = new BashExecutionComponent(command, this.deps.ui, excludeFromContext);\n\t\t\tif (this.session.isStreaming) {\n\t\t\t\tthis.deps.pendingMessagesContainer.addChild(this.bashComponent);\n\t\t\t\tthis.pendingBashComponents.push(this.bashComponent);\n\t\t\t} else {\n\t\t\t\tthis.deps.chatContainer.addChild(this.bashComponent);\n\t\t\t}\n\n\t\t\t// Show output and complete\n\t\t\tif (result.output) {\n\t\t\t\tthis.bashComponent.appendOutput(result.output);\n\t\t\t}\n\t\t\tthis.bashComponent.setComplete(\n\t\t\t\tresult.exitCode,\n\t\t\t\tresult.cancelled,\n\t\t\t\tresult.truncated ? ({ truncated: true, content: result.output } as TruncationResult) : undefined,\n\t\t\t\tresult.fullOutputPath,\n\t\t\t);\n\n\t\t\t// Record the result in session\n\t\t\tthis.session.recordBashResult(command, result, { excludeFromContext });\n\t\t\tthis.bashComponent = undefined;\n\t\t\tthis.deps.ui.requestRender();\n\t\t\treturn;\n\t\t}\n\n\t\t// Normal execution path (possibly with custom operations)\n\t\tconst isDeferred = this.session.isStreaming;\n\t\tthis.bashComponent = new BashExecutionComponent(command, this.deps.ui, excludeFromContext);\n\n\t\tif (isDeferred) {\n\t\t\t// Show in pending area when agent is streaming\n\t\t\tthis.deps.pendingMessagesContainer.addChild(this.bashComponent);\n\t\t\tthis.pendingBashComponents.push(this.bashComponent);\n\t\t} else {\n\t\t\t// Show in chat immediately when agent is idle\n\t\t\tthis.deps.chatContainer.addChild(this.bashComponent);\n\t\t}\n\t\tthis.deps.ui.requestRender();\n\n\t\ttry {\n\t\t\tconst result = await this.session.executeBash(\n\t\t\t\tcommand,\n\t\t\t\t(chunk) => {\n\t\t\t\t\tif (this.bashComponent) {\n\t\t\t\t\t\tthis.bashComponent.appendOutput(chunk);\n\t\t\t\t\t\tthis.deps.ui.requestRender();\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t{ excludeFromContext, operations: eventResult?.operations },\n\t\t\t);\n\n\t\t\tif (this.bashComponent) {\n\t\t\t\tthis.bashComponent.setComplete(\n\t\t\t\t\tresult.exitCode,\n\t\t\t\t\tresult.cancelled,\n\t\t\t\t\tresult.truncated ? ({ truncated: true, content: result.output } as TruncationResult) : undefined,\n\t\t\t\t\tresult.fullOutputPath,\n\t\t\t\t);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tif (this.bashComponent) {\n\t\t\t\tthis.bashComponent.setComplete(undefined, false);\n\t\t\t}\n\t\t\tthis.deps.showError(`Bash command failed: ${error instanceof Error ? error.message : \"Unknown error\"}`);\n\t\t}\n\n\t\tthis.bashComponent = undefined;\n\t\tthis.deps.ui.requestRender();\n\t}\n}\n"]}
@@ -2,10 +2,9 @@
2
2
  * Interactive mode for the coding agent.
3
3
  * Handles TUI rendering and user interaction, delegating business logic to AgentSession.
4
4
  */
5
- import { type ImageContent } from "@kolisachint/hoocode-ai";
5
+ import type { ImageContent } from "@kolisachint/hoocode-ai";
6
6
  import type { AgentSessionRuntime } from "../../core/agent-session-runtime.js";
7
7
  import type { TeamViewConnection } from "../../core/team-view.js";
8
- export declare function isApiKeyLoginProvider(providerId: string, oauthProviderIds: ReadonlySet<string>, builtInProviderIds?: ReadonlySet<string>): boolean;
9
8
  /**
10
9
  * Options for InteractiveMode initialization.
11
10
  */
@@ -55,7 +54,6 @@ export declare class InteractiveMode {
55
54
  private lastEscapeTime;
56
55
  private changelogMarkdown;
57
56
  private startupNoticesShown;
58
- private anthropicSubscriptionWarningShown;
59
57
  private lastStatusSpacer;
60
58
  private lastStatusText;
61
59
  private streamingComponent;
@@ -68,14 +66,11 @@ export declare class InteractiveMode {
68
66
  private taskStoreUnsubscribe?;
69
67
  private signalCleanupHandlers;
70
68
  private isBashMode;
71
- private bashComponent;
72
- private pendingBashComponents;
73
69
  private autoCompactionLoader;
74
70
  private autoCompactionEscapeHandler?;
75
71
  private retryLoader;
76
72
  private retryCountdown;
77
73
  private retryEscapeHandler?;
78
- private compactionQueuedMessages;
79
74
  private shutdownRequested;
80
75
  private dialogs;
81
76
  private extensionTerminalInputUnsubscribers;
@@ -84,6 +79,10 @@ export declare class InteractiveMode {
84
79
  private widgetContainerBelow;
85
80
  private taskPanel;
86
81
  private teamFocus;
82
+ private bashExecution;
83
+ private messageQueue;
84
+ private modelController;
85
+ private loginController;
87
86
  private headerContainer;
88
87
  private builtInHeader;
89
88
  private get session();
@@ -219,7 +218,6 @@ export declare class InteractiveMode {
219
218
  private updateEditorBorderColor;
220
219
  private updateEditorPromptPrefix;
221
220
  private cycleThinkingLevel;
222
- private cycleModel;
223
221
  private toggleToolOutputExpansion;
224
222
  private setToolsExpanded;
225
223
  private toggleThinkingBlockVisibility;
@@ -229,55 +227,17 @@ export declare class InteractiveMode {
229
227
  showWarning(warningMessage: string): void;
230
228
  showNewVersionNotification(newVersion: string): void;
231
229
  showPackageUpdateNotification(packages: string[]): void;
232
- /**
233
- * Get all queued messages (read-only).
234
- * Combines session queue and compaction queue.
235
- */
236
- private getAllQueuedMessages;
237
- /**
238
- * Clear all queued messages and return their contents.
239
- * Clears both session queue and compaction queue.
240
- */
241
- private clearAllQueues;
242
- private updatePendingMessagesDisplay;
243
- private restoreQueuedMessagesToEditor;
244
- private queueCompactionMessage;
245
- private isExtensionCommand;
246
- private flushCompactionQueue;
247
- /** Move pending bash components from pending area to chat */
248
- private flushPendingBashComponents;
249
230
  /**
250
231
  * Shows a selector component in place of the editor.
251
232
  * @param create Factory that receives a `done` callback and returns the component and focus target
252
233
  */
253
234
  private showSelector;
254
235
  private showSettingsSelector;
255
- private findExactModelMatch;
256
- private getModelCandidates;
257
- private updateAvailableProviderCount;
258
- private maybeWarnAboutAnthropicSubscriptionAuth;
259
- private showModelSelector;
260
- private showModelsSelector;
261
236
  private showUserMessageSelector;
262
237
  private showTreeSelector;
263
238
  private showSessionSelector;
264
239
  private handleResumeSession;
265
- private getLoginProviderOptions;
266
- private getLogoutProviderOptions;
267
- private showLoginAuthTypeSelector;
268
- private showLoginProviderSelector;
269
- private showOAuthSelector;
270
- private completeProviderAuthentication;
271
- private showBedrockSetupDialog;
272
- private showApiKeyLoginDialog;
273
- private showOAuthLoginSelect;
274
- private showLoginDialog;
275
240
  private handleReloadCommand;
276
- /**
277
- * Get capitalized display string for an app keybinding action.
278
- */
279
- private getAppKeyDisplay;
280
- private handleBashCommand;
281
241
  private handleCompactCommand;
282
242
  stop(): void;
283
243
  }