agent-sh 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/README.md +37 -115
  2. package/dist/agent/agent-loop.d.ts +86 -0
  3. package/dist/agent/agent-loop.js +704 -0
  4. package/dist/agent/conversation-state.d.ts +27 -0
  5. package/dist/agent/conversation-state.js +59 -0
  6. package/dist/agent/index.d.ts +11 -0
  7. package/dist/agent/index.js +9 -0
  8. package/dist/agent/skills.d.ts +25 -0
  9. package/dist/agent/skills.js +186 -0
  10. package/dist/agent/subagent.d.ts +37 -0
  11. package/dist/agent/subagent.js +119 -0
  12. package/dist/agent/system-prompt.d.ts +14 -0
  13. package/dist/agent/system-prompt.js +103 -0
  14. package/dist/agent/tool-registry.d.ts +15 -0
  15. package/dist/agent/tool-registry.js +30 -0
  16. package/dist/agent/tools/bash.d.ts +7 -0
  17. package/dist/agent/tools/bash.js +71 -0
  18. package/dist/agent/tools/display.d.ts +13 -0
  19. package/dist/agent/tools/display.js +70 -0
  20. package/dist/agent/tools/edit-file.d.ts +2 -0
  21. package/dist/agent/tools/edit-file.js +148 -0
  22. package/dist/agent/tools/glob.d.ts +2 -0
  23. package/dist/agent/tools/glob.js +87 -0
  24. package/dist/agent/tools/grep.d.ts +2 -0
  25. package/dist/agent/tools/grep.js +168 -0
  26. package/dist/agent/tools/list-skills.d.ts +2 -0
  27. package/dist/agent/tools/list-skills.js +28 -0
  28. package/dist/agent/tools/ls.d.ts +2 -0
  29. package/dist/agent/tools/ls.js +72 -0
  30. package/dist/agent/tools/read-file.d.ts +10 -0
  31. package/dist/agent/tools/read-file.js +101 -0
  32. package/dist/agent/tools/user-shell.d.ts +13 -0
  33. package/dist/agent/tools/user-shell.js +84 -0
  34. package/dist/agent/tools/write-file.d.ts +2 -0
  35. package/dist/agent/tools/write-file.js +82 -0
  36. package/dist/agent/types.d.ts +78 -0
  37. package/dist/agent/types.js +1 -0
  38. package/dist/core.d.ts +22 -14
  39. package/dist/core.js +256 -36
  40. package/dist/event-bus.d.ts +98 -17
  41. package/dist/event-bus.js +10 -1
  42. package/dist/extension-loader.d.ts +1 -1
  43. package/dist/extension-loader.js +10 -1
  44. package/dist/extensions/command-suggest.d.ts +10 -0
  45. package/dist/extensions/command-suggest.js +41 -0
  46. package/dist/extensions/slash-commands.d.ts +1 -1
  47. package/dist/extensions/slash-commands.js +161 -64
  48. package/dist/extensions/tui-renderer.js +426 -126
  49. package/dist/index.js +110 -129
  50. package/dist/input-handler.js +78 -9
  51. package/dist/output-parser.d.ts +7 -0
  52. package/dist/output-parser.js +27 -0
  53. package/dist/settings.d.ts +53 -2
  54. package/dist/settings.js +46 -3
  55. package/dist/shell.js +35 -28
  56. package/dist/types.d.ts +33 -6
  57. package/dist/utils/box-frame.d.ts +3 -1
  58. package/dist/utils/box-frame.js +12 -5
  59. package/dist/utils/diff.js +10 -0
  60. package/dist/utils/llm-client.d.ts +45 -0
  61. package/dist/utils/llm-client.js +60 -0
  62. package/dist/utils/markdown.d.ts +1 -0
  63. package/dist/utils/markdown.js +25 -3
  64. package/dist/utils/stream-transform.js +20 -47
  65. package/dist/utils/tool-display.d.ts +4 -0
  66. package/dist/utils/tool-display.js +35 -8
  67. package/examples/extensions/claude-code-bridge/README.md +35 -0
  68. package/examples/extensions/claude-code-bridge/index.ts +194 -0
  69. package/examples/extensions/claude-code-bridge/package.json +11 -0
  70. package/examples/extensions/openrouter.ts +87 -0
  71. package/examples/extensions/pi-bridge/README.md +35 -0
  72. package/examples/extensions/pi-bridge/index.ts +263 -0
  73. package/examples/extensions/pi-bridge/package.json +13 -0
  74. package/examples/extensions/secret-guard.ts +100 -0
  75. package/examples/extensions/subagents.ts +87 -0
  76. package/package.json +3 -5
  77. package/dist/acp-client.d.ts +0 -105
  78. package/dist/acp-client.js +0 -684
  79. package/dist/extensions/shell-exec.d.ts +0 -24
  80. package/dist/extensions/shell-exec.js +0 -188
  81. package/dist/mcp-server.d.ts +0 -13
  82. package/dist/mcp-server.js +0 -234
  83. package/examples/pi-agent-sh.ts +0 -166
@@ -0,0 +1,263 @@
1
+ /**
2
+ * Pi bridge — runs pi's full coding agent in-process as agent-sh's backend.
3
+ *
4
+ * Uses pi's own AgentSession with its full configuration: model registry,
5
+ * provider settings, extensions, session management, and tool system.
6
+ * Agent-sh provides the shell frontend and TUI rendering.
7
+ *
8
+ * In addition to pi's built-in tools, this bridge registers `user_shell`
9
+ * so pi can execute commands in agent-sh's live PTY (visible to the user,
10
+ * affects shell state like cd/export/source).
11
+ *
12
+ * Setup:
13
+ * npm install @mariozechner/pi-agent-core @mariozechner/pi-ai @mariozechner/pi-coding-agent
14
+ *
15
+ * Usage:
16
+ * agent-sh -e examples/extensions/pi-bridge
17
+ */
18
+ import type { AgentEvent } from "@mariozechner/pi-agent-core";
19
+ import {
20
+ createAgentSessionServices,
21
+ createAgentSessionFromServices,
22
+ createAgentSessionRuntime,
23
+ SessionManager,
24
+ } from "@mariozechner/pi-coding-agent";
25
+ import { Type } from "@sinclair/typebox";
26
+ import type { ExtensionContext } from "../../src/types.js";
27
+ import type { EventBus } from "../../src/event-bus.js";
28
+
29
+ // ── agent-sh context injected via tool promptGuidelines + promptSnippet ──
30
+
31
+ // ── user_shell as a pi ToolDefinition ─────────────────────────────
32
+ function createUserShellToolDef(bus: EventBus) {
33
+ // Track agent-sh's live cwd so user_shell always runs in the right place
34
+ let liveCwd = process.cwd();
35
+ bus.on("shell:cwd-change", ({ cwd }) => { liveCwd = cwd; });
36
+
37
+ const schema = Type.Object({
38
+ command: Type.String({ description: "Command to execute in user's shell" }),
39
+ return_output: Type.Optional(
40
+ Type.Boolean({
41
+ description:
42
+ "Whether to return the command output. Default false — output is shown directly to the user.",
43
+ }),
44
+ ),
45
+ });
46
+
47
+ return {
48
+ name: "user_shell",
49
+ label: "user_shell",
50
+ description:
51
+ "Run a command with lasting effects in the user's live shell (cd, export, " +
52
+ "install packages, start servers) or show output the user wants to see. " +
53
+ "Output is shown directly to the user. Set return_output=true only " +
54
+ "if you need to inspect the result.",
55
+ promptSnippet: "Execute commands in the user's live terminal (PTY).",
56
+ promptGuidelines: [
57
+ "You are running inside agent-sh, a terminal wrapper.",
58
+ "Use your standard tools (bash, file ops) for investigation — output goes to you, not the user.",
59
+ "Use user_shell to run commands in the user's live shell when they ask to see output or need lasting effects (cd, install, start servers).",
60
+ "Default to standard tools. Use user_shell when the user is the intended audience for the output or the command has real effects.",
61
+ ],
62
+ parameters: schema,
63
+
64
+ async execute(_toolCallId, params) {
65
+ const command = params.command;
66
+ const returnOutput = params.return_output ?? false;
67
+
68
+ const result = await bus.emitPipeAsync("shell:exec-request", {
69
+ command,
70
+ output: "",
71
+ cwd: liveCwd,
72
+ done: false,
73
+ });
74
+
75
+ const text = returnOutput
76
+ ? result.output || "(no output)"
77
+ : "Command executed.";
78
+
79
+ return { content: [{ type: "text", text }], details: undefined };
80
+ },
81
+ };
82
+ }
83
+
84
+ // ── Extension entry point ─────────────────────────────────────────
85
+ export default function activate(ctx: ExtensionContext): void {
86
+ const { bus } = ctx;
87
+ const cwd = process.cwd();
88
+
89
+ const userShellTool = createUserShellToolDef(bus);
90
+
91
+ // ── Boot pi session (async — register backend synchronously first) ──
92
+ let session: any = null;
93
+ let runtime: any = null;
94
+ let booting = true;
95
+
96
+ const boot = async () => {
97
+ try {
98
+ // Pi loads its own config: ~/.pi/agent/settings.json, models, extensions
99
+ const services = await createAgentSessionServices({ cwd });
100
+ const sessionManager = SessionManager.inMemory(cwd);
101
+
102
+ // createRuntime factory — returns { session, services, ... } as expected
103
+ // by createAgentSessionRuntime
104
+ const createRuntime = async (opts: any) => {
105
+ const result = await createAgentSessionFromServices({
106
+ services,
107
+ sessionManager: opts.sessionManager ?? sessionManager,
108
+ customTools: [userShellTool],
109
+ });
110
+ return { ...result, services };
111
+ };
112
+
113
+ runtime = await createAgentSessionRuntime(createRuntime, {
114
+ cwd,
115
+ sessionManager,
116
+ });
117
+ session = runtime.session;
118
+
119
+ // Subscribe to pi events → agent-sh bus
120
+ let fullResponseText = "";
121
+
122
+ session.subscribe((event: AgentEvent) => {
123
+ switch (event.type) {
124
+ case "agent_start":
125
+ fullResponseText = "";
126
+ break;
127
+
128
+ case "message_update": {
129
+ const ame = (event as any).assistantMessageEvent;
130
+ if (ame.type === "text_delta") {
131
+ bus.emitTransform("agent:response-chunk", {
132
+ blocks: [{ type: "text" as const, text: ame.delta }],
133
+ });
134
+ fullResponseText += ame.delta;
135
+ } else if (ame.type === "thinking_delta") {
136
+ bus.emit("agent:thinking-chunk", { text: ame.delta });
137
+ }
138
+ break;
139
+ }
140
+
141
+ case "tool_execution_start":
142
+ bus.emit("agent:tool-started", {
143
+ title: (event as any).toolName,
144
+ toolCallId: (event as any).toolCallId,
145
+ kind: (event as any).toolName === "user_shell" || (event as any).toolName === "bash"
146
+ ? "execute"
147
+ : "read",
148
+ });
149
+ break;
150
+
151
+ case "tool_execution_update": {
152
+ const pr = (event as any).partialResult as
153
+ | { content?: Array<{ type: string; text?: string }> }
154
+ | undefined;
155
+ if (pr?.content) {
156
+ for (const c of pr.content) {
157
+ if (c.type === "text" && c.text) {
158
+ bus.emit("agent:tool-output-chunk", { chunk: c.text });
159
+ }
160
+ }
161
+ }
162
+ break;
163
+ }
164
+
165
+ case "tool_execution_end":
166
+ bus.emit("agent:tool-completed", {
167
+ toolCallId: (event as any).toolCallId,
168
+ exitCode: (event as any).isError ? 1 : 0,
169
+ kind: (event as any).toolName === "user_shell" || (event as any).toolName === "bash"
170
+ ? "execute"
171
+ : "read",
172
+ });
173
+ break;
174
+
175
+ case "agent_end":
176
+ bus.emitTransform("agent:response-done", {
177
+ response: fullResponseText,
178
+ });
179
+ bus.emit("agent:processing-done", {});
180
+ break;
181
+ }
182
+ });
183
+
184
+ // Report model info
185
+ const model = session.model;
186
+ bus.emit("agent:info", {
187
+ name: "pi",
188
+ version: "0.66",
189
+ model: model ? `${model.provider}/${model.id}` : undefined,
190
+ });
191
+
192
+ booting = false;
193
+ } catch (err) {
194
+ booting = false;
195
+ bus.emit("ui:error", {
196
+ message: `pi-bridge: failed to initialize — ${err instanceof Error ? err.message : String(err)}`,
197
+ });
198
+ }
199
+ };
200
+
201
+ // ── Bus listeners (wired on start, unwired on kill) ────────────
202
+ const listeners: Array<{ event: string; fn: Function }> = [];
203
+
204
+ const wireListeners = () => {
205
+ const onSubmit = async ({ query }: any) => {
206
+ if (!session) {
207
+ bus.emit("agent:error", {
208
+ message: booting ? "pi is still starting up..." : "pi session not initialized",
209
+ });
210
+ bus.emit("agent:processing-done", {});
211
+ return;
212
+ }
213
+
214
+ bus.emit("agent:query", { query });
215
+ bus.emit("agent:processing-start", {});
216
+
217
+ try {
218
+ await session.prompt(query);
219
+ } catch (err) {
220
+ bus.emit("agent:error", {
221
+ message: err instanceof Error ? err.message : String(err),
222
+ });
223
+ bus.emit("agent:processing-done", {});
224
+ }
225
+ };
226
+
227
+ const onCancel = async () => { await session?.abort(); };
228
+ const onReset = async () => {
229
+ await runtime?.newSession();
230
+ session = runtime?.session;
231
+ };
232
+
233
+ bus.on("agent:submit", onSubmit);
234
+ bus.on("agent:cancel-request", onCancel);
235
+ bus.on("agent:reset-session", onReset);
236
+ listeners.push(
237
+ { event: "agent:submit", fn: onSubmit },
238
+ { event: "agent:cancel-request", fn: onCancel },
239
+ { event: "agent:reset-session", fn: onReset },
240
+ );
241
+ };
242
+
243
+ const unwireListeners = () => {
244
+ for (const { event, fn } of listeners) bus.off(event as any, fn as any);
245
+ listeners.length = 0;
246
+ };
247
+
248
+ // ── Register as backend ───────────────────────────────────────
249
+ bus.emit("agent:register-backend", {
250
+ name: "pi",
251
+ start: async () => {
252
+ await boot();
253
+ wireListeners();
254
+ },
255
+ kill: () => {
256
+ unwireListeners();
257
+ runtime?.dispose();
258
+ session = null;
259
+ runtime = null;
260
+ booting = true;
261
+ },
262
+ });
263
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "agent-sh-pi-bridge",
3
+ "version": "0.1.0",
4
+ "description": "Pi coding agent backend for agent-sh",
5
+ "type": "module",
6
+ "main": "index.ts",
7
+ "dependencies": {
8
+ "@mariozechner/pi-agent-core": "^0.66.0",
9
+ "@mariozechner/pi-ai": "^0.66.0",
10
+ "@mariozechner/pi-coding-agent": "^0.66.0",
11
+ "@sinclair/typebox": "^0.34.0"
12
+ }
13
+ }
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Secret guard extension.
3
+ *
4
+ * Redacts sensitive patterns (API keys, tokens, passwords) from tool output
5
+ * — both the streamed terminal display and the content sent back to the LLM.
6
+ *
7
+ * Usage:
8
+ * agent-sh -e ./examples/extensions/secret-guard.ts
9
+ *
10
+ * # Or install permanently:
11
+ * cp examples/extensions/secret-guard.ts ~/.agent-sh/extensions/
12
+ *
13
+ * Configuration (~/.agent-sh/settings.json):
14
+ * {
15
+ * "secret-guard": {
16
+ * "extraPatterns": ["CUSTOM_\\w+=\\S+"],
17
+ * "redactText": "***REDACTED***"
18
+ * }
19
+ * }
20
+ */
21
+ import type { ExtensionContext } from "agent-sh/types";
22
+
23
+ // Common secret patterns — each matches key=value or key: value formats
24
+ const DEFAULT_PATTERNS = [
25
+ // API keys and tokens (generic)
26
+ /(?:api[_-]?key|api[_-]?secret|access[_-]?token|auth[_-]?token|secret[_-]?key|private[_-]?key)\s*[=:]\s*\S+/gi,
27
+ // AWS
28
+ /(?:AKIA|ASIA)[A-Z0-9]{16}/g,
29
+ /(?:aws_secret_access_key|aws_session_token)\s*[=:]\s*\S+/gi,
30
+ // Bearer tokens
31
+ /Bearer\s+[A-Za-z0-9\-._~+/]+=*/g,
32
+ // GitHub tokens
33
+ /gh[pousr]_[A-Za-z0-9_]{36,}/g,
34
+ // Anthropic / OpenAI keys
35
+ /sk-(?:ant-)?[A-Za-z0-9\-_]{10,}/g,
36
+ // Generic long hex/base64 secrets (env var assignment)
37
+ /(?:SECRET|TOKEN|PASSWORD|PASSWD|API_KEY|PRIVATE_KEY)\s*[=:]\s*\S+/gi,
38
+ // Connection strings with passwords
39
+ /[a-z+]+:\/\/[^:]+:[^@\s]+@/gi,
40
+ ];
41
+
42
+ export default function activate(ctx: ExtensionContext) {
43
+ const { bus } = ctx;
44
+ const config = ctx.getExtensionSettings("secret-guard", {
45
+ extraPatterns: [] as string[],
46
+ redactText: "***REDACTED***",
47
+ });
48
+
49
+ const patterns = [
50
+ ...DEFAULT_PATTERNS,
51
+ ...config.extraPatterns.map((p: string) => new RegExp(p, "gi")),
52
+ ];
53
+
54
+ function redact(text: string): string {
55
+ let result = text;
56
+ for (const pattern of patterns) {
57
+ // Reset lastIndex for stateful regex (global flag)
58
+ pattern.lastIndex = 0;
59
+ result = result.replace(pattern, config.redactText);
60
+ }
61
+ return result;
62
+ }
63
+
64
+ // Redact the dynamic context (shell history, cwd, etc.) before it's sent
65
+ // to the LLM. This is the chokepoint — everything the model sees passes
66
+ // through dynamic-context:build.
67
+ ctx.advise("dynamic-context:build", (next) => {
68
+ return redact(next());
69
+ });
70
+
71
+ // Advise tool:execute to wrap both streaming output and final result.
72
+ // Chunks from child processes arrive at arbitrary byte boundaries, so a
73
+ // secret like "sk-ant-abc123" could be split across two chunks. We
74
+ // line-buffer: accumulate until we see '\n', redact complete lines, flush.
75
+ ctx.advise("tool:execute", async (next, toolCtx) => {
76
+ const origOnChunk = toolCtx.onChunk;
77
+ if (origOnChunk) {
78
+ let buf = "";
79
+ toolCtx.onChunk = (chunk: string) => {
80
+ buf += chunk;
81
+ const lastNl = buf.lastIndexOf("\n");
82
+ if (lastNl !== -1) {
83
+ // Flush all complete lines, redacted
84
+ origOnChunk(redact(buf.slice(0, lastNl + 1)));
85
+ buf = buf.slice(lastNl + 1);
86
+ }
87
+ };
88
+
89
+ const result = await next(toolCtx);
90
+
91
+ // Flush any remaining partial line
92
+ if (buf) origOnChunk(redact(buf));
93
+
94
+ return { ...result, content: redact(result.content) };
95
+ }
96
+
97
+ const result = await next(toolCtx);
98
+ return { ...result, content: redact(result.content) };
99
+ });
100
+ }
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Subagent extension — lets the main agent spawn focused sub-agents.
3
+ *
4
+ * The main agent gets a `spawn_agent` tool that creates a fresh agent
5
+ * with its own context. The LLM decides how to specialize — no
6
+ * predefined categories, no registry, no config.
7
+ *
8
+ * Usage:
9
+ * agent-sh -e ./examples/extensions/subagents.ts
10
+ */
11
+ import type { ExtensionContext } from "../../src/types.js";
12
+ import { runSubagent } from "../../src/agent/subagent.js";
13
+
14
+ export default function activate(ctx: ExtensionContext): void {
15
+ const { bus, llmClient, contextManager } = ctx;
16
+ if (!llmClient) return;
17
+
18
+ const allToolNames = () => ctx.getTools().map(t => t.name);
19
+
20
+ ctx.registerTool({
21
+ name: "spawn_agent",
22
+ description:
23
+ "Spawn a subagent with its own fresh context to handle a focused task. " +
24
+ "Use this to delegate work that needs investigation or multiple tool calls, " +
25
+ "without polluting your main conversation context. " +
26
+ "The subagent runs to completion and returns its result.",
27
+ input_schema: {
28
+ type: "object",
29
+ properties: {
30
+ task: {
31
+ type: "string",
32
+ description: "Clear description of what the subagent should do",
33
+ },
34
+ tools: {
35
+ type: "array",
36
+ items: { type: "string" },
37
+ description: `Tool names the subagent can use. Available: ${allToolNames().join(", ")}`,
38
+ },
39
+ },
40
+ required: ["task"],
41
+ },
42
+
43
+ showOutput: false,
44
+
45
+ getDisplayInfo: () => ({
46
+ kind: "execute",
47
+ }),
48
+
49
+ async execute(args) {
50
+ const task = args.task as string;
51
+ const toolNames = args.tools as string[] | undefined;
52
+
53
+ const allTools = ctx.getTools();
54
+ // Filter to requested tools, or give all tools (minus spawn_agent to prevent recursion)
55
+ const tools = toolNames
56
+ ? allTools.filter(t => toolNames.includes(t.name))
57
+ : allTools.filter(t => t.name !== "spawn_agent");
58
+
59
+ const systemPrompt =
60
+ `You are a focused subagent. Complete the task and return a clear, concise result.\n` +
61
+ `Working directory: ${contextManager.getCwd()}`;
62
+
63
+ try {
64
+ const result = await runSubagent({
65
+ llmClient,
66
+ tools,
67
+ systemPrompt,
68
+ task,
69
+ bus,
70
+ maxIterations: 25,
71
+ });
72
+
73
+ return {
74
+ content: result || "(no response)",
75
+ exitCode: 0,
76
+ isError: false,
77
+ };
78
+ } catch (err) {
79
+ return {
80
+ content: `Subagent error: ${err instanceof Error ? err.message : String(err)}`,
81
+ exitCode: 1,
82
+ isError: true,
83
+ };
84
+ }
85
+ },
86
+ });
87
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "agent-sh",
3
- "version": "0.4.0",
4
- "description": "A shell-first terminal where any ACP-compatible AI agent is one keystroke away",
3
+ "version": "0.6.0",
4
+ "description": "A shell-first terminal where AI is one keystroke away",
5
5
  "type": "module",
6
6
  "main": "dist/core.js",
7
7
  "types": "dist/core.d.ts",
@@ -39,8 +39,6 @@
39
39
  "dev": "tsx src/index.ts",
40
40
  "build": "tsc",
41
41
  "start": "node dist/index.js",
42
- "pi": "node dist/index.js --agent pi-acp",
43
- "claude": "node dist/index.js --agent claude-agent-acp",
44
42
  "prepublishOnly": "npm run build"
45
43
  },
46
44
  "keywords": [
@@ -67,10 +65,10 @@
67
65
  "node": ">=18"
68
66
  },
69
67
  "dependencies": {
70
- "@agentclientprotocol/sdk": "^0.18.1",
71
68
  "cli-highlight": "^2.1.11",
72
69
  "marked": "^17.0.6",
73
70
  "node-pty": "^1.2.0-beta.12",
71
+ "openai": "^6.34.0",
74
72
  "tsx": "^4.19.0"
75
73
  },
76
74
  "devDependencies": {
@@ -1,105 +0,0 @@
1
- import type { EventBus } from "./event-bus.js";
2
- import type { ContextManager } from "./context-manager.js";
3
- import type { AgentShellConfig } from "./types.js";
4
- export declare class AcpClient {
5
- private agentProcess;
6
- private connection;
7
- private sessionId;
8
- private bus;
9
- private contextManager;
10
- private config;
11
- private promptInProgress;
12
- private currentResponseText;
13
- private lastResponseText;
14
- private terminalSessions;
15
- private terminalDonePromises;
16
- private terminalCounter;
17
- private fileWatcher;
18
- private pendingToolCalls;
19
- private autoCancelled;
20
- private pendingToolCounter;
21
- private agentInfo;
22
- private modes;
23
- private currentModeId;
24
- constructor(opts: {
25
- bus: EventBus;
26
- contextManager: ContextManager;
27
- config: AgentShellConfig;
28
- });
29
- start(): Promise<void>;
30
- /**
31
- * Send a user query to the agent.
32
- */
33
- private firstPromptSent;
34
- private static readonly SESSION_ORIENTATION;
35
- sendPrompt(query: string, opts?: {
36
- modeInstruction?: string;
37
- modeLabel?: string;
38
- }): Promise<void>;
39
- /**
40
- * Silently cancel the prompt after a shell tool completes.
41
- * Unlike user-initiated cancel(), this doesn't show "(cancelled)" —
42
- * the tool already ran, we just skip the unnecessary LLM follow-up.
43
- */
44
- private autoCancel;
45
- /**
46
- * Cancel the current prompt and force-recover shell mode.
47
- */
48
- cancel(): Promise<void>;
49
- /**
50
- * Start a new ACP session, clearing agent-side conversation history.
51
- */
52
- resetSession(): Promise<void>;
53
- /**
54
- * Get the text of the last agent response (for /copy).
55
- */
56
- getLastResponseText(): string;
57
- /**
58
- * Get agent information for display.
59
- */
60
- getAgentInfo(): {
61
- name: string;
62
- version: string;
63
- } | null;
64
- getModel(): string | undefined;
65
- /**
66
- * Get the current mode (e.g. thinking level).
67
- */
68
- getCurrentMode(): {
69
- id: string;
70
- name: string;
71
- } | null;
72
- /**
73
- * Check if agent is connected.
74
- */
75
- isConnected(): boolean;
76
- /**
77
- * Parse modes from a session response and notify listeners.
78
- */
79
- private updateModes;
80
- /**
81
- * Cycle to the next session mode.
82
- */
83
- private cycleMode;
84
- private log;
85
- /**
86
- * Create the Client handler that responds to agent requests.
87
- */
88
- private createClientHandler;
89
- private handleSessionUpdate;
90
- private handleRequestPermission;
91
- private handleCreateTerminal;
92
- private handleTerminalOutput;
93
- private handleWaitForTerminalExit;
94
- private handleKillTerminal;
95
- private handleReleaseTerminal;
96
- private handleReadTextFile;
97
- private handleWriteTextFile;
98
- /**
99
- * After the agent finishes, check all tracked files for changes
100
- * made via the agent's own tools (not through fs/writeTextFile)
101
- * and show interactive diff previews.
102
- */
103
- private showPendingFileChanges;
104
- kill(): void;
105
- }