@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.
- package/CHANGELOG.md +2 -0
- package/dist/modes/interactive/bash-execution-controller.d.ts +34 -0
- package/dist/modes/interactive/bash-execution-controller.d.ts.map +1 -0
- package/dist/modes/interactive/bash-execution-controller.js +98 -0
- package/dist/modes/interactive/bash-execution-controller.js.map +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts +5 -45
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +91 -779
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/login-controller.d.ts +50 -0
- package/dist/modes/interactive/login-controller.d.ts.map +1 -0
- package/dist/modes/interactive/login-controller.js +352 -0
- package/dist/modes/interactive/login-controller.js.map +1 -0
- package/dist/modes/interactive/message-queue-controller.d.ts +49 -0
- package/dist/modes/interactive/message-queue-controller.d.ts.map +1 -0
- package/dist/modes/interactive/message-queue-controller.js +184 -0
- package/dist/modes/interactive/message-queue-controller.js.map +1 -0
- package/dist/modes/interactive/model-controller.d.ts +43 -0
- package/dist/modes/interactive/model-controller.d.ts.map +1 -0
- package/dist/modes/interactive/model-controller.js +184 -0
- package/dist/modes/interactive/model-controller.js.map +1 -0
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package.json +1 -1
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -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 {
|
|
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
|
}
|