@oh-my-pi/pi-coding-agent 12.16.0 → 12.17.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +30 -0
- package/package.json +414 -92
- package/src/cli/stats-cli.ts +3 -3
- package/src/exec/bash-executor.ts +92 -58
- package/src/extensibility/custom-tools/types.ts +1 -1
- package/src/extensibility/extensions/types.ts +1 -1
- package/src/extensibility/extensions/wrapper.ts +3 -2
- package/src/lsp/render.ts +1 -1
- package/src/mcp/config.ts +102 -5
- package/src/mcp/index.ts +3 -1
- package/src/mcp/loader.ts +3 -0
- package/src/mcp/manager.ts +3 -0
- package/src/mcp/tool-bridge.ts +2 -2
- package/src/modes/components/tool-execution.ts +2 -2
- package/src/patch/shared.ts +1 -2
- package/src/sdk.ts +2 -0
- package/src/system-prompt.ts +193 -62
- package/src/task/render.ts +1 -1
- package/src/tools/ask.ts +1 -1
- package/src/tools/bash.ts +7 -8
- package/src/tools/calculator.ts +1 -1
- package/src/tools/fetch.ts +1 -0
- package/src/tools/find.ts +1 -1
- package/src/tools/grep.ts +1 -1
- package/src/tools/notebook.ts +1 -1
- package/src/tools/python.ts +1 -1
- package/src/tools/read.ts +1 -1
- package/src/tools/renderers.ts +1 -5
- package/src/tools/review.ts +1 -1
- package/src/tools/ssh.ts +1 -1
- package/src/tools/todo-write.ts +1 -1
- package/src/tools/write.ts +1 -2
- package/src/utils/shell-snapshot.ts +18 -3
- package/src/web/search/index.ts +7 -7
- package/src/web/search/render.ts +1 -0
|
@@ -34,6 +34,8 @@ export interface BashResult {
|
|
|
34
34
|
artifactId?: string;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
const HARD_TIMEOUT_GRACE_MS = 5_000;
|
|
38
|
+
|
|
37
39
|
const shellSessions = new Map<string, Shell>();
|
|
38
40
|
|
|
39
41
|
export async function executeBash(command: string, options?: BashExecutorOptions): Promise<BashResult> {
|
|
@@ -65,74 +67,106 @@ export async function executeBash(command: string, options?: BashExecutorOptions
|
|
|
65
67
|
};
|
|
66
68
|
}
|
|
67
69
|
|
|
70
|
+
const sessionKey = buildSessionKey(shell, prefix, snapshotPath, shellEnv, options?.sessionKey);
|
|
71
|
+
let shellSession = shellSessions.get(sessionKey);
|
|
72
|
+
if (!shellSession) {
|
|
73
|
+
shellSession = new Shell({ sessionEnv: shellEnv, snapshotPath: snapshotPath ?? undefined });
|
|
74
|
+
shellSessions.set(sessionKey, shellSession);
|
|
75
|
+
}
|
|
76
|
+
const signal = options?.signal;
|
|
77
|
+
const abortHandler = () => {
|
|
78
|
+
shellSession.abort(signal?.reason instanceof Error ? signal.reason.message : undefined);
|
|
79
|
+
};
|
|
80
|
+
if (signal) {
|
|
81
|
+
signal.addEventListener("abort", abortHandler, { once: true });
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
let hardTimeoutTimer: NodeJS.Timeout | undefined;
|
|
85
|
+
const hardTimeoutDeferred = Promise.withResolvers<"hard-timeout">();
|
|
86
|
+
const baseTimeoutMs = Math.max(1_000, options?.timeout ?? 300_000);
|
|
87
|
+
const hardTimeoutMs = baseTimeoutMs + HARD_TIMEOUT_GRACE_MS;
|
|
88
|
+
hardTimeoutTimer = setTimeout(() => {
|
|
89
|
+
shellSession.abort(`Hard timeout after ${Math.round(hardTimeoutMs / 1000)}s`);
|
|
90
|
+
hardTimeoutDeferred.resolve("hard-timeout");
|
|
91
|
+
}, hardTimeoutMs);
|
|
92
|
+
|
|
93
|
+
let resetSession = false;
|
|
94
|
+
|
|
68
95
|
try {
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
96
|
+
const runPromise = shellSession.run(
|
|
97
|
+
{
|
|
98
|
+
command: finalCommand,
|
|
99
|
+
cwd: options?.cwd,
|
|
100
|
+
env: options?.env,
|
|
101
|
+
timeoutMs: options?.timeout,
|
|
102
|
+
signal,
|
|
103
|
+
},
|
|
104
|
+
(err, chunk) => {
|
|
105
|
+
if (!err) {
|
|
106
|
+
enqueueChunk(chunk);
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
const winner = await Promise.race([
|
|
112
|
+
runPromise.then(result => ({ kind: "result" as const, result })),
|
|
113
|
+
hardTimeoutDeferred.promise.then(() => ({ kind: "hard-timeout" as const })),
|
|
114
|
+
]);
|
|
115
|
+
|
|
116
|
+
await pendingChunks;
|
|
117
|
+
|
|
118
|
+
if (winner.kind === "hard-timeout") {
|
|
119
|
+
resetSession = true;
|
|
120
|
+
return {
|
|
121
|
+
exitCode: undefined,
|
|
122
|
+
cancelled: true,
|
|
123
|
+
...(await sink.dump(`Command exceeded hard timeout after ${Math.round(hardTimeoutMs / 1000)} seconds`)),
|
|
124
|
+
};
|
|
74
125
|
}
|
|
75
126
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
127
|
+
// Handle timeout
|
|
128
|
+
if (winner.result.timedOut) {
|
|
129
|
+
const annotation = options?.timeout
|
|
130
|
+
? `Command timed out after ${Math.round(options.timeout / 1000)} seconds`
|
|
131
|
+
: "Command timed out";
|
|
132
|
+
resetSession = true;
|
|
133
|
+
return {
|
|
134
|
+
exitCode: undefined,
|
|
135
|
+
cancelled: true,
|
|
136
|
+
...(await sink.dump(annotation)),
|
|
137
|
+
};
|
|
82
138
|
}
|
|
83
139
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
command: finalCommand,
|
|
88
|
-
cwd: options?.cwd,
|
|
89
|
-
env: options?.env,
|
|
90
|
-
timeoutMs: options?.timeout,
|
|
91
|
-
signal,
|
|
92
|
-
},
|
|
93
|
-
(err, chunk) => {
|
|
94
|
-
if (!err) {
|
|
95
|
-
enqueueChunk(chunk);
|
|
96
|
-
}
|
|
97
|
-
},
|
|
98
|
-
);
|
|
99
|
-
|
|
100
|
-
await pendingChunks;
|
|
101
|
-
|
|
102
|
-
// Handle timeout
|
|
103
|
-
if (result.timedOut) {
|
|
104
|
-
const annotation = options?.timeout
|
|
105
|
-
? `Command timed out after ${Math.round(options.timeout / 1000)} seconds`
|
|
106
|
-
: "Command timed out";
|
|
107
|
-
return {
|
|
108
|
-
exitCode: undefined,
|
|
109
|
-
cancelled: true,
|
|
110
|
-
...(await sink.dump(annotation)),
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Handle cancellation
|
|
115
|
-
if (result.cancelled) {
|
|
116
|
-
return {
|
|
117
|
-
exitCode: undefined,
|
|
118
|
-
cancelled: true,
|
|
119
|
-
...(await sink.dump("Command cancelled")),
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Normal completion
|
|
140
|
+
// Handle cancellation
|
|
141
|
+
if (winner.result.cancelled) {
|
|
142
|
+
resetSession = true;
|
|
124
143
|
return {
|
|
125
|
-
exitCode:
|
|
126
|
-
cancelled:
|
|
127
|
-
...(await sink.dump()),
|
|
144
|
+
exitCode: undefined,
|
|
145
|
+
cancelled: true,
|
|
146
|
+
...(await sink.dump("Command cancelled")),
|
|
128
147
|
};
|
|
129
|
-
} finally {
|
|
130
|
-
if (signal) {
|
|
131
|
-
signal.removeEventListener("abort", abortHandler);
|
|
132
|
-
}
|
|
133
148
|
}
|
|
149
|
+
|
|
150
|
+
// Normal completion
|
|
151
|
+
return {
|
|
152
|
+
exitCode: winner.result.exitCode,
|
|
153
|
+
cancelled: false,
|
|
154
|
+
...(await sink.dump()),
|
|
155
|
+
};
|
|
156
|
+
} catch (err) {
|
|
157
|
+
resetSession = true;
|
|
158
|
+
throw err;
|
|
134
159
|
} finally {
|
|
160
|
+
if (hardTimeoutTimer) {
|
|
161
|
+
clearTimeout(hardTimeoutTimer);
|
|
162
|
+
}
|
|
163
|
+
if (signal) {
|
|
164
|
+
signal.removeEventListener("abort", abortHandler);
|
|
165
|
+
}
|
|
135
166
|
await pendingChunks;
|
|
167
|
+
if (resetSession) {
|
|
168
|
+
shellSessions.delete(sessionKey);
|
|
169
|
+
}
|
|
136
170
|
}
|
|
137
171
|
}
|
|
138
172
|
|
|
@@ -182,7 +182,7 @@ export interface CustomTool<TParams extends TSchema = TSchema, TDetails = any> {
|
|
|
182
182
|
/** Called on session lifecycle events - use to reconstruct state or cleanup resources */
|
|
183
183
|
onSession?: (event: CustomToolSessionEvent, ctx: CustomToolContext) => void | Promise<void>;
|
|
184
184
|
/** Custom rendering for tool call display - return a Component */
|
|
185
|
-
renderCall?: (args: Static<TParams>, theme: Theme) => Component;
|
|
185
|
+
renderCall?: (args: Static<TParams>, options: RenderResultOptions, theme: Theme) => Component;
|
|
186
186
|
|
|
187
187
|
/** Custom rendering for tool result display - return a Component */
|
|
188
188
|
renderResult?: (
|
|
@@ -304,7 +304,7 @@ export interface ToolDefinition<TParams extends TSchema = TSchema, TDetails = un
|
|
|
304
304
|
onSession?: (event: ToolSessionEvent, ctx: ExtensionContext) => void | Promise<void>;
|
|
305
305
|
|
|
306
306
|
/** Custom rendering for tool call display */
|
|
307
|
-
renderCall?: (args: Static<TParams>, theme: Theme) => Component;
|
|
307
|
+
renderCall?: (args: Static<TParams>, options: ToolRenderResultOptions, theme: Theme) => Component;
|
|
308
308
|
|
|
309
309
|
/** Custom rendering for tool result display */
|
|
310
310
|
renderResult?: (
|
|
@@ -18,7 +18,7 @@ export class RegisteredToolAdapter implements AgentTool<any, any, any> {
|
|
|
18
18
|
declare parameters: any;
|
|
19
19
|
declare label: string;
|
|
20
20
|
|
|
21
|
-
renderCall?: (args: any, theme: any) => any;
|
|
21
|
+
renderCall?: (args: any, options: any, theme: any) => any;
|
|
22
22
|
renderResult?: (result: any, options: any, theme: any, args?: any) => any;
|
|
23
23
|
|
|
24
24
|
constructor(
|
|
@@ -32,7 +32,8 @@ export class RegisteredToolAdapter implements AgentTool<any, any, any> {
|
|
|
32
32
|
// enters the custom-renderer path, gets undefined back, and silently
|
|
33
33
|
// discards tool result text (extensions without renderers show blank).
|
|
34
34
|
if (registeredTool.definition.renderCall) {
|
|
35
|
-
this.renderCall = (args: any,
|
|
35
|
+
this.renderCall = (args: any, options: any, theme: any) =>
|
|
36
|
+
registeredTool.definition.renderCall!(args, options, theme as Theme);
|
|
36
37
|
}
|
|
37
38
|
if (registeredTool.definition.renderResult) {
|
|
38
39
|
this.renderResult = (result: any, options: any, theme: any, args?: any) =>
|
package/src/lsp/render.ts
CHANGED
|
@@ -31,7 +31,7 @@ import type { LspParams, LspToolDetails } from "./types";
|
|
|
31
31
|
* Render the LSP tool call in the TUI.
|
|
32
32
|
* Shows: "lsp <operation> <file/filecount>"
|
|
33
33
|
*/
|
|
34
|
-
export function renderCall(args: LspParams, theme: Theme): Text {
|
|
34
|
+
export function renderCall(args: LspParams, _options: RenderResultOptions, theme: Theme): Text {
|
|
35
35
|
const actionLabel = (args.action ?? "request").replace(/_/g, " ");
|
|
36
36
|
const queryPreview = args.query ? truncateToWidth(args.query, TRUNCATE_LENGTHS.SHORT) : undefined;
|
|
37
37
|
|
package/src/mcp/config.ts
CHANGED
|
@@ -18,6 +18,8 @@ export interface LoadMCPConfigsOptions {
|
|
|
18
18
|
enableProjectConfig?: boolean;
|
|
19
19
|
/** Whether to filter out Exa MCP servers (default: true) */
|
|
20
20
|
filterExa?: boolean;
|
|
21
|
+
/** Whether to filter out browser MCP servers when builtin browser tool is enabled (default: false) */
|
|
22
|
+
filterBrowser?: boolean;
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
/** Result of loading MCP configs */
|
|
@@ -91,6 +93,7 @@ function convertToLegacyConfig(server: MCPServer): MCPServerConfig {
|
|
|
91
93
|
export async function loadAllMCPConfigs(cwd: string, options?: LoadMCPConfigsOptions): Promise<LoadMCPConfigsResult> {
|
|
92
94
|
const enableProjectConfig = options?.enableProjectConfig ?? true;
|
|
93
95
|
const filterExa = options?.filterExa ?? true;
|
|
96
|
+
const filterBrowser = options?.filterBrowser ?? false;
|
|
94
97
|
|
|
95
98
|
// Load MCP servers via capability system
|
|
96
99
|
const result = await loadCapability<MCPServer>(mcpCapability.id, { cwd });
|
|
@@ -103,8 +106,8 @@ export async function loadAllMCPConfigs(cwd: string, options?: LoadMCPConfigsOpt
|
|
|
103
106
|
// Load user-level disabled servers list
|
|
104
107
|
const disabledServers = new Set(await readDisabledServers(getMCPConfigPath("user", cwd)));
|
|
105
108
|
// Convert to legacy format and preserve source metadata
|
|
106
|
-
|
|
107
|
-
|
|
109
|
+
let configs: Record<string, MCPServerConfig> = {};
|
|
110
|
+
let sources: Record<string, SourceMeta> = {};
|
|
108
111
|
for (const server of servers) {
|
|
109
112
|
const config = convertToLegacyConfig(server);
|
|
110
113
|
if (config.enabled === false || (server._source.level !== "user" && disabledServers.has(server.name))) {
|
|
@@ -114,11 +117,19 @@ export async function loadAllMCPConfigs(cwd: string, options?: LoadMCPConfigsOpt
|
|
|
114
117
|
sources[server.name] = server._source;
|
|
115
118
|
}
|
|
116
119
|
|
|
117
|
-
|
|
120
|
+
let exaApiKeys: string[] = [];
|
|
118
121
|
|
|
119
122
|
if (filterExa) {
|
|
120
|
-
const
|
|
121
|
-
|
|
123
|
+
const exaResult = filterExaMCPServers(configs, sources);
|
|
124
|
+
configs = exaResult.configs;
|
|
125
|
+
sources = exaResult.sources;
|
|
126
|
+
exaApiKeys = exaResult.exaApiKeys;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (filterBrowser) {
|
|
130
|
+
const browserResult = filterBrowserMCPServers(configs, sources);
|
|
131
|
+
configs = browserResult.configs;
|
|
132
|
+
sources = browserResult.sources;
|
|
122
133
|
}
|
|
123
134
|
|
|
124
135
|
return { configs, exaApiKeys, sources };
|
|
@@ -264,3 +275,89 @@ export function validateServerConfig(name: string, config: MCPServerConfig): str
|
|
|
264
275
|
|
|
265
276
|
return errors;
|
|
266
277
|
}
|
|
278
|
+
|
|
279
|
+
/** Known browser automation MCP server names (lowercase) */
|
|
280
|
+
const BROWSER_MCP_NAMES = new Set([
|
|
281
|
+
"puppeteer",
|
|
282
|
+
"playwright",
|
|
283
|
+
"browserbase",
|
|
284
|
+
"browser-tools",
|
|
285
|
+
"browser-use",
|
|
286
|
+
"browser",
|
|
287
|
+
]);
|
|
288
|
+
|
|
289
|
+
/** Patterns matching browser MCP package names in command/args */
|
|
290
|
+
const BROWSER_MCP_PKG_PATTERN =
|
|
291
|
+
// Official packages
|
|
292
|
+
// - @modelcontextprotocol/server-puppeteer
|
|
293
|
+
// - @playwright/mcp
|
|
294
|
+
// - @browserbasehq/mcp-server-browserbase
|
|
295
|
+
// - @agentdeskai/browser-tools-mcp
|
|
296
|
+
// - @agent-infra/mcp-server-browser
|
|
297
|
+
// Community packages: puppeteer-mcp-server, playwright-mcp, pptr-mcp, etc.
|
|
298
|
+
/(?:@modelcontextprotocol\/server-puppeteer|@playwright\/mcp|@browserbasehq\/mcp-server-browserbase|@agentdeskai\/browser-tools-mcp|@agent-infra\/mcp-server-browser|puppeteer-mcp|playwright-mcp|pptr-mcp|browser-use-mcp|mcp-browser-use)/i;
|
|
299
|
+
|
|
300
|
+
/** URL patterns for hosted browser MCP services */
|
|
301
|
+
const BROWSER_MCP_URL_PATTERN = /browserbase\.com|browser-use\.com/i;
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Check if a server config is a browser automation MCP server.
|
|
305
|
+
*/
|
|
306
|
+
export function isBrowserMCPServer(name: string, config: MCPServerConfig): boolean {
|
|
307
|
+
// Check by server name
|
|
308
|
+
if (BROWSER_MCP_NAMES.has(name.toLowerCase())) {
|
|
309
|
+
return true;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Check by URL for HTTP/SSE servers
|
|
313
|
+
if (config.type === "http" || config.type === "sse") {
|
|
314
|
+
const httpConfig = config as { url?: string };
|
|
315
|
+
if (httpConfig.url && BROWSER_MCP_URL_PATTERN.test(httpConfig.url)) {
|
|
316
|
+
return true;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Check by command/args for stdio servers
|
|
321
|
+
if (!config.type || config.type === "stdio") {
|
|
322
|
+
const stdioConfig = config as { command?: string; args?: string[] };
|
|
323
|
+
if (stdioConfig.command && BROWSER_MCP_PKG_PATTERN.test(stdioConfig.command)) {
|
|
324
|
+
return true;
|
|
325
|
+
}
|
|
326
|
+
if (stdioConfig.args?.some(arg => BROWSER_MCP_PKG_PATTERN.test(arg))) {
|
|
327
|
+
return true;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
return false;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/** Result of filtering browser MCP servers */
|
|
335
|
+
export interface BrowserFilterResult {
|
|
336
|
+
/** Configs with browser servers removed */
|
|
337
|
+
configs: Record<string, MCPServerConfig>;
|
|
338
|
+
/** Source metadata for remaining servers */
|
|
339
|
+
sources: Record<string, SourceMeta>;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Filter out browser automation MCP servers.
|
|
344
|
+
* Since we have a native browser tool, we don't need these MCP servers.
|
|
345
|
+
*/
|
|
346
|
+
export function filterBrowserMCPServers(
|
|
347
|
+
configs: Record<string, MCPServerConfig>,
|
|
348
|
+
sources: Record<string, SourceMeta>,
|
|
349
|
+
): BrowserFilterResult {
|
|
350
|
+
const filtered: Record<string, MCPServerConfig> = {};
|
|
351
|
+
const filteredSources: Record<string, SourceMeta> = {};
|
|
352
|
+
|
|
353
|
+
for (const [name, config] of Object.entries(configs)) {
|
|
354
|
+
if (!isBrowserMCPServer(name, config)) {
|
|
355
|
+
filtered[name] = config;
|
|
356
|
+
if (sources[name]) {
|
|
357
|
+
filteredSources[name] = sources[name];
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
return { configs: filtered, sources: filteredSources };
|
|
363
|
+
}
|
package/src/mcp/index.ts
CHANGED
|
@@ -8,10 +8,12 @@
|
|
|
8
8
|
// Client
|
|
9
9
|
export { callTool, connectToServer, disconnectServer, listTools, serverSupportsTools } from "./client";
|
|
10
10
|
// Config
|
|
11
|
-
export type { ExaFilterResult, LoadMCPConfigsOptions, LoadMCPConfigsResult } from "./config";
|
|
11
|
+
export type { BrowserFilterResult, ExaFilterResult, LoadMCPConfigsOptions, LoadMCPConfigsResult } from "./config";
|
|
12
12
|
export {
|
|
13
13
|
extractExaApiKey,
|
|
14
|
+
filterBrowserMCPServers,
|
|
14
15
|
filterExaMCPServers,
|
|
16
|
+
isBrowserMCPServer,
|
|
15
17
|
isExaMCPServer,
|
|
16
18
|
loadAllMCPConfigs,
|
|
17
19
|
validateServerConfig,
|
package/src/mcp/loader.ts
CHANGED
|
@@ -32,6 +32,8 @@ export interface MCPToolsLoadOptions {
|
|
|
32
32
|
enableProjectConfig?: boolean;
|
|
33
33
|
/** Whether to filter out Exa MCP servers (default: true) */
|
|
34
34
|
filterExa?: boolean;
|
|
35
|
+
/** Whether to filter out browser MCP servers when builtin browser tool is enabled (default: false) */
|
|
36
|
+
filterBrowser?: boolean;
|
|
35
37
|
/** SQLite storage for MCP tool cache (null disables cache) */
|
|
36
38
|
cacheStorage?: AgentStorage | null;
|
|
37
39
|
/** Auth storage used to resolve OAuth credentials before initial MCP connect */
|
|
@@ -69,6 +71,7 @@ export async function discoverAndLoadMCPTools(cwd: string, options?: MCPToolsLoa
|
|
|
69
71
|
onConnecting: options?.onConnecting,
|
|
70
72
|
enableProjectConfig: options?.enableProjectConfig,
|
|
71
73
|
filterExa: options?.filterExa,
|
|
74
|
+
filterBrowser: options?.filterBrowser,
|
|
72
75
|
});
|
|
73
76
|
} catch (error) {
|
|
74
77
|
// If discovery fails entirely, return empty result
|
package/src/mcp/manager.ts
CHANGED
|
@@ -68,6 +68,8 @@ export interface MCPDiscoverOptions {
|
|
|
68
68
|
enableProjectConfig?: boolean;
|
|
69
69
|
/** Whether to filter out Exa MCP servers (default: true) */
|
|
70
70
|
filterExa?: boolean;
|
|
71
|
+
/** Whether to filter out browser MCP servers when builtin browser tool is enabled (default: false) */
|
|
72
|
+
filterBrowser?: boolean;
|
|
71
73
|
/** Called when starting to connect to servers */
|
|
72
74
|
onConnecting?: (serverNames: string[]) => void;
|
|
73
75
|
}
|
|
@@ -105,6 +107,7 @@ export class MCPManager {
|
|
|
105
107
|
const { configs, exaApiKeys, sources } = await loadAllMCPConfigs(this.cwd, {
|
|
106
108
|
enableProjectConfig: options?.enableProjectConfig,
|
|
107
109
|
filterExa: options?.filterExa,
|
|
110
|
+
filterBrowser: options?.filterBrowser,
|
|
108
111
|
});
|
|
109
112
|
const result = await this.connectServers(configs, sources, options?.onConnecting);
|
|
110
113
|
result.exaApiKeys = exaApiKeys;
|
package/src/mcp/tool-bridge.ts
CHANGED
|
@@ -198,7 +198,7 @@ export class MCPTool implements CustomTool<TSchema, MCPToolDetails> {
|
|
|
198
198
|
this.mcpServerName = connection.name;
|
|
199
199
|
}
|
|
200
200
|
|
|
201
|
-
renderCall(args: unknown, theme: Theme) {
|
|
201
|
+
renderCall(args: unknown, _options: RenderResultOptions, theme: Theme) {
|
|
202
202
|
return renderMCPCall((args ?? {}) as Record<string, unknown>, theme, this.label);
|
|
203
203
|
}
|
|
204
204
|
|
|
@@ -304,7 +304,7 @@ export class DeferredMCPTool implements CustomTool<TSchema, MCPToolDetails> {
|
|
|
304
304
|
this.#fallbackProviderName = source?.providerName;
|
|
305
305
|
}
|
|
306
306
|
|
|
307
|
-
renderCall(args: unknown, theme: Theme) {
|
|
307
|
+
renderCall(args: unknown, _options: RenderResultOptions, theme: Theme) {
|
|
308
308
|
return renderMCPCall((args ?? {}) as Record<string, unknown>, theme, this.label);
|
|
309
309
|
}
|
|
310
310
|
|
|
@@ -391,7 +391,7 @@ export class ToolExecutionComponent extends Container {
|
|
|
391
391
|
const shouldRenderCall = !this.#result || !mergeCallAndResult;
|
|
392
392
|
if (shouldRenderCall && tool.renderCall) {
|
|
393
393
|
try {
|
|
394
|
-
const callComponent = tool.renderCall(this.#getCallArgsForRender(), theme);
|
|
394
|
+
const callComponent = tool.renderCall(this.#getCallArgsForRender(), this.#renderState, theme);
|
|
395
395
|
if (callComponent) {
|
|
396
396
|
this.#contentBox.addChild(ensureInvalidate(callComponent));
|
|
397
397
|
}
|
|
@@ -453,7 +453,7 @@ export class ToolExecutionComponent extends Container {
|
|
|
453
453
|
if (shouldRenderCall) {
|
|
454
454
|
// Render call component
|
|
455
455
|
try {
|
|
456
|
-
const callComponent = renderer.renderCall(this.#getCallArgsForRender(),
|
|
456
|
+
const callComponent = renderer.renderCall(this.#getCallArgsForRender(), this.#renderState, theme);
|
|
457
457
|
if (callComponent) {
|
|
458
458
|
this.#contentBox.addChild(ensureInvalidate(callComponent));
|
|
459
459
|
}
|
package/src/patch/shared.ts
CHANGED
|
@@ -19,7 +19,6 @@ import {
|
|
|
19
19
|
ToolUIKit,
|
|
20
20
|
truncateDiffByHunk,
|
|
21
21
|
} from "../tools/render-utils";
|
|
22
|
-
import type { RenderCallOptions } from "../tools/renderers";
|
|
23
22
|
import { Ellipsis, Hasher, type RenderCache, renderStatusLine, truncateToWidth } from "../tui";
|
|
24
23
|
import type { DiffError, DiffResult, Operation } from "./types";
|
|
25
24
|
|
|
@@ -254,7 +253,7 @@ function renderDiffSection(
|
|
|
254
253
|
export const editToolRenderer = {
|
|
255
254
|
mergeCallAndResult: true,
|
|
256
255
|
|
|
257
|
-
renderCall(args: EditRenderArgs,
|
|
256
|
+
renderCall(args: EditRenderArgs, options: RenderResultOptions, uiTheme: Theme): Component {
|
|
258
257
|
const ui = new ToolUIKit(uiTheme);
|
|
259
258
|
const rawPath = args.file_path || args.path || "";
|
|
260
259
|
const filePath = shortenPath(rawPath);
|
package/src/sdk.ts
CHANGED
|
@@ -806,6 +806,8 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
806
806
|
enableProjectConfig: settings.get("mcp.enableProjectConfig") ?? true,
|
|
807
807
|
// Always filter Exa - we have native integration
|
|
808
808
|
filterExa: true,
|
|
809
|
+
// Filter browser MCP servers when builtin browser tool is active
|
|
810
|
+
filterBrowser: (settings.get("browser.enabled") as boolean) ?? false,
|
|
809
811
|
cacheStorage: settings.getStorage(),
|
|
810
812
|
authStorage,
|
|
811
813
|
});
|