@oh-my-pi/pi-coding-agent 17.2.12 → 17.2.14
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 +63 -0
- package/dist/{CHANGELOG-k9ghy5sn.md → CHANGELOG-za420td8.md} +63 -0
- package/dist/cli.js +3658 -3627
- package/dist/types/advisor/delta-split.d.ts +24 -0
- package/dist/types/advisor/runtime.d.ts +2 -2
- package/dist/types/async/job-manager.d.ts +8 -1
- package/dist/types/cli/update-cli.d.ts +53 -1
- package/dist/types/config/keybindings.d.ts +10 -0
- package/dist/types/config/model-resolver.d.ts +15 -2
- package/dist/types/config/settings-schema.d.ts +14 -0
- package/dist/types/discovery/agents-md.d.ts +10 -1
- package/dist/types/eval/runner-cache.d.ts +12 -0
- package/dist/types/extensibility/extensions/runner.d.ts +12 -3
- package/dist/types/extensibility/extensions/types.d.ts +15 -4
- package/dist/types/extensibility/plugins/marketplace/manager.d.ts +4 -1
- package/dist/types/lib/xai-http.d.ts +0 -1
- package/dist/types/mcp/tool-bridge.d.ts +8 -5
- package/dist/types/modes/components/agent-hub-renderer.d.ts +6 -1
- package/dist/types/modes/components/status-line/types.d.ts +4 -0
- package/dist/types/modes/controllers/extension-ui-controller.d.ts +2 -4
- package/dist/types/modes/interactive-mode.d.ts +17 -8
- package/dist/types/modes/types.d.ts +7 -10
- package/dist/types/modes/utils/hotkeys-markdown.d.ts +1 -1
- package/dist/types/session/agent-session-types.d.ts +4 -0
- package/dist/types/session/agent-session.d.ts +24 -3
- package/dist/types/session/messages.d.ts +20 -0
- package/dist/types/session/retry-fallback-chains.d.ts +13 -0
- package/dist/types/session/session-advisors.d.ts +1 -1
- package/dist/types/session/session-history-format.d.ts +10 -0
- package/dist/types/session/session-maintenance.d.ts +1 -1
- package/dist/types/session/session-tools.d.ts +35 -5
- package/dist/types/session/turn-recovery.d.ts +34 -5
- package/dist/types/slash-commands/types.d.ts +5 -1
- package/dist/types/task/executor.d.ts +1 -1
- package/dist/types/tools/approval.d.ts +7 -0
- package/dist/types/tools/builtin-names.d.ts +1 -2
- package/dist/types/tools/index.d.ts +1 -0
- package/dist/types/tools/think.d.ts +38 -0
- package/dist/types/tools/todo.d.ts +14 -15
- package/dist/types/tools/write.d.ts +2 -2
- package/dist/types/utils/local-date.d.ts +2 -0
- package/dist/types/vibe/runtime.d.ts +1 -1
- package/dist/types/web/parallel.d.ts +1 -0
- package/dist/types/web/search/providers/brave.d.ts +8 -3
- package/dist/types/web/search/providers/codex.d.ts +6 -0
- package/dist/types/web/search/providers/firecrawl.d.ts +3 -2
- package/dist/types/web/search/providers/jina.d.ts +3 -3
- package/dist/types/web/search/providers/parallel.d.ts +1 -0
- package/dist/types/web/search/providers/perplexity.d.ts +4 -0
- package/dist/types/web/search/providers/tinyfish.d.ts +2 -0
- package/package.json +13 -13
- package/src/advisor/delta-split.ts +98 -0
- package/src/advisor/runtime.ts +321 -69
- package/src/async/job-manager.ts +14 -3
- package/src/cli/gallery-fixtures/agentic.ts +16 -0
- package/src/cli/plugin-cli.ts +30 -2
- package/src/cli/update-cli.ts +259 -24
- package/src/config/keybindings.ts +52 -9
- package/src/config/model-resolver.ts +19 -3
- package/src/config/settings-schema.ts +16 -0
- package/src/cursor.ts +10 -5
- package/src/discovery/agents-md.ts +61 -23
- package/src/eval/jl/kernel.ts +2 -20
- package/src/eval/py/kernel.ts +2 -20
- package/src/eval/rb/kernel.ts +2 -20
- package/src/eval/runner-cache.ts +41 -0
- package/src/exec/non-interactive-env.ts +14 -3
- package/src/extensibility/extensions/loader.ts +5 -2
- package/src/extensibility/extensions/runner.ts +184 -66
- package/src/extensibility/extensions/types.ts +26 -2
- package/src/extensibility/extensions/wrapper.ts +13 -7
- package/src/extensibility/plugins/marketplace/manager.ts +6 -2
- package/src/hindsight/client.ts +1 -1
- package/src/lib/xai-http.ts +0 -4
- package/src/lsp/client.ts +2 -0
- package/src/lsp/servers.ts +1 -1
- package/src/mcp/tool-bridge.ts +15 -6
- package/src/modes/components/agent-hub-renderer.ts +9 -3
- package/src/modes/components/agent-hub.ts +2 -1
- package/src/modes/components/status-line/component.ts +58 -6
- package/src/modes/components/status-line/segments.ts +12 -1
- package/src/modes/components/status-line/types.ts +1 -0
- package/src/modes/components/user-message.ts +20 -5
- package/src/modes/controllers/event-controller.ts +48 -0
- package/src/modes/controllers/extension-ui-controller.ts +14 -7
- package/src/modes/controllers/input-controller.ts +25 -6
- package/src/modes/controllers/selector-controller.ts +5 -0
- package/src/modes/interactive-mode.ts +315 -129
- package/src/modes/rpc/rpc-frame.ts +13 -5
- package/src/modes/theme/tui-adapters.ts +4 -5
- package/src/modes/types.ts +13 -7
- package/src/modes/utils/hotkeys-markdown.ts +10 -6
- package/src/prompts/system/system-prompt.md +10 -1
- package/src/registry/persisted-agents.ts +43 -8
- package/src/sdk.ts +146 -9
- package/src/session/agent-session-types.ts +4 -0
- package/src/session/agent-session.ts +91 -10
- package/src/session/messages.ts +98 -28
- package/src/session/retry-fallback-chains.ts +14 -0
- package/src/session/session-advisors.ts +32 -15
- package/src/session/session-history-format.ts +15 -1
- package/src/session/session-maintenance.ts +8 -8
- package/src/session/session-manager.ts +6 -2
- package/src/session/session-tools.ts +355 -183
- package/src/session/turn-recovery.ts +225 -47
- package/src/slash-commands/builtin-modes.ts +41 -12
- package/src/slash-commands/types.ts +5 -1
- package/src/task/executor.ts +92 -45
- package/src/task/structured-subagent.ts +5 -5
- package/src/tools/approval.ts +44 -10
- package/src/tools/builtin-names.ts +1 -2
- package/src/tools/fetch.ts +21 -2
- package/src/tools/image-gen.ts +6 -8
- package/src/tools/index.ts +8 -0
- package/src/tools/renderers.ts +2 -0
- package/src/tools/think.ts +62 -0
- package/src/tools/todo.ts +70 -26
- package/src/tools/tts.ts +3 -2
- package/src/tools/write.ts +7 -3
- package/src/utils/local-date.ts +13 -0
- package/src/utils/tools-manager.ts +2 -2
- package/src/vibe/runtime.ts +22 -14
- package/src/web/kagi.ts +91 -34
- package/src/web/parallel.ts +11 -2
- package/src/web/scrapers/crates-io.ts +2 -2
- package/src/web/scrapers/discogs.ts +2 -2
- package/src/web/scrapers/docs-rs.ts +2 -2
- package/src/web/scrapers/github.ts +2 -2
- package/src/web/scrapers/musicbrainz.ts +1 -2
- package/src/web/scrapers/pubmed.ts +2 -2
- package/src/web/scrapers/sec-edgar.ts +2 -2
- package/src/web/search/providers/brave.ts +121 -46
- package/src/web/search/providers/codex.ts +88 -12
- package/src/web/search/providers/exa.ts +45 -10
- package/src/web/search/providers/firecrawl.ts +53 -11
- package/src/web/search/providers/gemini.ts +139 -27
- package/src/web/search/providers/jina.ts +48 -25
- package/src/web/search/providers/parallel.ts +23 -9
- package/src/web/search/providers/perplexity.ts +24 -7
- package/src/web/search/providers/searxng.ts +77 -1
- package/src/web/search/providers/tavily.ts +23 -22
- package/src/web/search/providers/tinyfish.ts +44 -10
- package/src/web/search/providers/xai.ts +85 -14
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
1
2
|
import type { Agent, AgentTool } from "@oh-my-pi/pi-agent-core";
|
|
2
3
|
import type { Model } from "@oh-my-pi/pi-ai";
|
|
3
|
-
import { isRecord, logger, prompt, stringProperty } from "@oh-my-pi/pi-utils";
|
|
4
|
+
import { isRecord, logger, prompt, stringProperty, untilAborted } from "@oh-my-pi/pi-utils";
|
|
4
5
|
import { reset as resetCapabilities } from "../capability";
|
|
5
6
|
import type { ModelRegistry } from "../config/model-registry";
|
|
6
7
|
import { formatModelString } from "../config/model-resolver";
|
|
@@ -67,10 +68,14 @@ interface SessionToolsOptions {
|
|
|
67
68
|
toolRegistry?: Map<string, AgentTool>;
|
|
68
69
|
createVibeTools?: () => AgentTool[];
|
|
69
70
|
createComputerTool?: () => Promise<AgentTool | null>;
|
|
71
|
+
/** Creates the private `think` scratchpad tool for runtime setting changes. */
|
|
72
|
+
createThinkTool?: () => Promise<AgentTool | null>;
|
|
70
73
|
/** Creates the built-in `inspect_image` tool for session-scoped runtime enablement (see {@link SessionTools.setInspectImageMode}). */
|
|
71
74
|
createInspectImageTool?: () => Promise<AgentTool | null>;
|
|
72
75
|
builtInToolNames?: Iterable<string>;
|
|
73
76
|
presentationPinnedToolNames?: ReadonlySet<string>;
|
|
77
|
+
/** MCP tool names whose current registry entries came from the manager snapshot. */
|
|
78
|
+
mcpManagerToolNames?: Iterable<string>;
|
|
74
79
|
ensureWriteRegistered?: () => Promise<boolean>;
|
|
75
80
|
rebuildSystemPrompt?: (
|
|
76
81
|
toolNames: string[],
|
|
@@ -181,10 +186,13 @@ export class SessionTools {
|
|
|
181
186
|
#toolRegistry: Map<string, AgentTool>;
|
|
182
187
|
#createVibeTools: (() => AgentTool[]) | undefined;
|
|
183
188
|
#createComputerTool: SessionToolsOptions["createComputerTool"];
|
|
189
|
+
#createThinkTool: SessionToolsOptions["createThinkTool"];
|
|
184
190
|
#createInspectImageTool: SessionToolsOptions["createInspectImageTool"];
|
|
185
191
|
#installedVibeToolNames = new Set<string>();
|
|
186
192
|
#builtInToolNames: Set<string>;
|
|
187
193
|
#rpcHostToolNames = new Set<string>();
|
|
194
|
+
#mcpManagerToolNames = new Set<string>();
|
|
195
|
+
#extensionMcpTools = new Map<string, AgentTool>();
|
|
188
196
|
#xdev: XdevState | undefined;
|
|
189
197
|
#pendingXdevMountDelta: { added: Set<string>; removed: Set<string> } | undefined;
|
|
190
198
|
/**
|
|
@@ -216,7 +224,8 @@ export class SessionTools {
|
|
|
216
224
|
* prompt carries no catalog (no mounts, or a custom prompt that omits the section).
|
|
217
225
|
*/
|
|
218
226
|
#basePromptXdevNames: ReadonlySet<string> = new Set();
|
|
219
|
-
#
|
|
227
|
+
#toolRegistryMutationScope = new AsyncLocalStorage<boolean>();
|
|
228
|
+
#toolRegistryMutationTail: Promise<void> = Promise.resolve();
|
|
220
229
|
#promptModelKey: string | undefined;
|
|
221
230
|
#rebuildSystemPrompt: SessionToolsOptions["rebuildSystemPrompt"];
|
|
222
231
|
#getLocalCalendarDate: () => string;
|
|
@@ -235,8 +244,20 @@ export class SessionTools {
|
|
|
235
244
|
this.#toolRegistry = options.toolRegistry ?? new Map();
|
|
236
245
|
this.#createVibeTools = options.createVibeTools;
|
|
237
246
|
this.#createComputerTool = options.createComputerTool;
|
|
247
|
+
this.#createThinkTool = options.createThinkTool;
|
|
238
248
|
this.#createInspectImageTool = options.createInspectImageTool;
|
|
239
249
|
this.#builtInToolNames = new Set(options.builtInToolNames ?? []);
|
|
250
|
+
this.#mcpManagerToolNames = new Set(options.mcpManagerToolNames ?? []);
|
|
251
|
+
if (options.mcpManagerToolNames === undefined) {
|
|
252
|
+
for (const name of this.#toolRegistry.keys()) {
|
|
253
|
+
if (isMCPToolName(name)) this.#mcpManagerToolNames.add(name);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
for (const [name, tool] of this.#toolRegistry) {
|
|
257
|
+
if (isMCPToolName(name) && !this.#mcpManagerToolNames.has(name)) {
|
|
258
|
+
this.#extensionMcpTools.set(name, tool);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
240
261
|
this.#presentationPinnedToolNames = options.presentationPinnedToolNames;
|
|
241
262
|
this.#ensureWriteRegistered = options.ensureWriteRegistered;
|
|
242
263
|
this.#rebuildSystemPrompt = options.rebuildSystemPrompt;
|
|
@@ -362,6 +383,65 @@ export class SessionTools {
|
|
|
362
383
|
return this.#builtInToolNames.has(name);
|
|
363
384
|
}
|
|
364
385
|
|
|
386
|
+
/** Updates source provenance when a live registry entry is replaced or restored. */
|
|
387
|
+
setToolBuiltIn(name: string, builtIn: boolean): void {
|
|
388
|
+
if (builtIn) {
|
|
389
|
+
this.#builtInToolNames.add(name);
|
|
390
|
+
} else {
|
|
391
|
+
this.#builtInToolNames.delete(name);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/** Whether the live registry entry is owned by the RPC host. */
|
|
396
|
+
hasRpcHostTool(name: string): boolean {
|
|
397
|
+
return this.#rpcHostToolNames.has(name);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/** Whether the current MCP entry came from the manager snapshot. */
|
|
401
|
+
hasMCPManagerTool(name: string): boolean {
|
|
402
|
+
return this.#mcpManagerToolNames.has(name);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/** Restores manager ownership after a lifecycle registration rollback. */
|
|
406
|
+
setMCPManagerTool(name: string, managerOwned: boolean): void {
|
|
407
|
+
if (managerOwned) {
|
|
408
|
+
this.#mcpManagerToolNames.add(name);
|
|
409
|
+
} else {
|
|
410
|
+
this.#mcpManagerToolNames.delete(name);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/** Current extension-owned MCP entry retained across manager refreshes. */
|
|
415
|
+
getExtensionMCPTool(name: string): AgentTool | undefined {
|
|
416
|
+
return this.#extensionMcpTools.get(name);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/** Updates extension ownership when a lifecycle registration commits or rolls back. */
|
|
420
|
+
setExtensionMCPTool(name: string, tool: AgentTool | undefined): void {
|
|
421
|
+
if (!isMCPToolName(name)) return;
|
|
422
|
+
if (tool) {
|
|
423
|
+
this.#extensionMcpTools.set(name, tool);
|
|
424
|
+
this.#mcpManagerToolNames.delete(name);
|
|
425
|
+
} else {
|
|
426
|
+
this.#extensionMcpTools.delete(name);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/** Serializes every registry and presentation mutation for this session. */
|
|
431
|
+
runToolRegistryMutation<T>(mutation: () => Promise<T>, signal?: AbortSignal): Promise<T> {
|
|
432
|
+
if (this.#toolRegistryMutationScope.getStore()) return untilAborted(signal, mutation);
|
|
433
|
+
const serialized = this.#toolRegistryMutationTail.then(() => {
|
|
434
|
+
signal?.throwIfAborted();
|
|
435
|
+
return this.#toolRegistryMutationScope.run(true, mutation);
|
|
436
|
+
});
|
|
437
|
+
const operation = untilAborted(signal, serialized);
|
|
438
|
+
this.#toolRegistryMutationTail = serialized.then(
|
|
439
|
+
() => undefined,
|
|
440
|
+
() => undefined,
|
|
441
|
+
);
|
|
442
|
+
return operation;
|
|
443
|
+
}
|
|
444
|
+
|
|
365
445
|
/** Names of every registered tool. */
|
|
366
446
|
getAllToolNames(): string[] {
|
|
367
447
|
return Array.from(this.#toolRegistry.keys());
|
|
@@ -401,40 +481,46 @@ export class SessionTools {
|
|
|
401
481
|
}
|
|
402
482
|
|
|
403
483
|
/** Installs and activates the ephemeral vibe tool set. */
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
484
|
+
activateVibeTools(baseToolNames: string[]): Promise<void> {
|
|
485
|
+
return this.runToolRegistryMutation(async () => {
|
|
486
|
+
const createVibeTools = this.#createVibeTools;
|
|
487
|
+
if (!createVibeTools) {
|
|
488
|
+
throw new Error("Vibe tools are unavailable in this session.");
|
|
489
|
+
}
|
|
409
490
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
491
|
+
const tools = createVibeTools();
|
|
492
|
+
const vibeToolNames = tools.map(tool => tool.name);
|
|
493
|
+
if (new Set(vibeToolNames).size !== vibeToolNames.length) {
|
|
494
|
+
throw new Error("Vibe tool names must be unique.");
|
|
495
|
+
}
|
|
415
496
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
497
|
+
for (const tool of tools) {
|
|
498
|
+
if (this.#toolRegistry.has(tool.name)) continue;
|
|
499
|
+
this.#toolRegistry.set(tool.name, this.#wrapRuntimeTool(tool));
|
|
500
|
+
this.#builtInToolNames.add(tool.name);
|
|
501
|
+
this.#installedVibeToolNames.add(tool.name);
|
|
502
|
+
}
|
|
422
503
|
|
|
423
|
-
|
|
504
|
+
await this.#applyActiveToolsByName([...new Set([...baseToolNames, ...vibeToolNames])]);
|
|
505
|
+
});
|
|
424
506
|
}
|
|
425
507
|
|
|
426
508
|
/** Uninstalls vibe tools and activates the replacement set. */
|
|
427
|
-
|
|
428
|
-
this
|
|
429
|
-
|
|
509
|
+
deactivateVibeTools(nextToolNames: string[]): Promise<void> {
|
|
510
|
+
return this.runToolRegistryMutation(async () => {
|
|
511
|
+
this.#uninstallVibeTools();
|
|
512
|
+
await this.#applyActiveToolsByName(nextToolNames);
|
|
513
|
+
});
|
|
430
514
|
}
|
|
431
515
|
|
|
432
516
|
/** Removes vibe tools without restoring a source-session snapshot. */
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
517
|
+
removeVibeToolsPreservingActive(): Promise<void> {
|
|
518
|
+
return this.runToolRegistryMutation(async () => {
|
|
519
|
+
const removed = new Set(this.#installedVibeToolNames);
|
|
520
|
+
this.#uninstallVibeTools();
|
|
521
|
+
const nextActive = this.getActiveToolNames().filter(name => !removed.has(name));
|
|
522
|
+
await this.#applyActiveToolsByName(nextActive);
|
|
523
|
+
});
|
|
438
524
|
}
|
|
439
525
|
|
|
440
526
|
#uninstallVibeTools(): void {
|
|
@@ -633,11 +719,20 @@ export class SessionTools {
|
|
|
633
719
|
}
|
|
634
720
|
|
|
635
721
|
/** Applies an enabled tool set and reconciles its `xd://` partition. */
|
|
636
|
-
|
|
722
|
+
applyActiveToolsByName(toolNames: string[], forcePromptRefresh = false, signal?: AbortSignal): Promise<void> {
|
|
723
|
+
return this.runToolRegistryMutation(
|
|
724
|
+
() => this.#applyActiveToolsByName(toolNames, forcePromptRefresh, signal),
|
|
725
|
+
signal,
|
|
726
|
+
);
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
async #applyActiveToolsByName(toolNames: string[], forcePromptRefresh = false, signal?: AbortSignal): Promise<void> {
|
|
730
|
+
signal?.throwIfAborted();
|
|
637
731
|
toolNames = normalizeToolNames(toolNames);
|
|
638
732
|
let builtInWriteAvailable = this.#builtInToolNames.has("write");
|
|
639
733
|
if (toolNames.includes("write") && !builtInWriteAvailable) {
|
|
640
|
-
|
|
734
|
+
const writeRegistration = this.#ensureWriteRegistered?.();
|
|
735
|
+
builtInWriteAvailable = writeRegistration ? (await untilAborted(signal, writeRegistration)) === true : false;
|
|
641
736
|
if (builtInWriteAvailable) this.#builtInToolNames.add("write");
|
|
642
737
|
}
|
|
643
738
|
const selectedTools = toolNames.flatMap(name => {
|
|
@@ -669,7 +764,8 @@ export class SessionTools {
|
|
|
669
764
|
const activeDeferrableTool = tools.some(tool => tool.deferrable === true);
|
|
670
765
|
const transportNeeded = mountNames.size > 0 || activeDeferrableTool || this.#host.planModeEnabled();
|
|
671
766
|
if (transportNeeded && !builtInWriteAvailable) {
|
|
672
|
-
|
|
767
|
+
const writeRegistration = this.#ensureWriteRegistered?.();
|
|
768
|
+
builtInWriteAvailable = writeRegistration ? (await untilAborted(signal, writeRegistration)) === true : false;
|
|
673
769
|
if (builtInWriteAvailable) this.#builtInToolNames.add("write");
|
|
674
770
|
}
|
|
675
771
|
if (transportNeeded && builtInWriteAvailable) {
|
|
@@ -699,13 +795,14 @@ export class SessionTools {
|
|
|
699
795
|
try {
|
|
700
796
|
if (this.#rebuildSystemPrompt) {
|
|
701
797
|
const signature = this.#computeAppliedToolSignature(validToolNames, tools);
|
|
702
|
-
if (signature !== this.#lastAppliedToolSignature) {
|
|
703
|
-
const built = await this.#rebuildSystemPrompt(validToolNames, this.#toolRegistry);
|
|
798
|
+
if (forcePromptRefresh || signature !== this.#lastAppliedToolSignature) {
|
|
799
|
+
const built = await untilAborted(signal, this.#rebuildSystemPrompt(validToolNames, this.#toolRegistry));
|
|
704
800
|
rebuiltSystemPrompt = built.systemPrompt;
|
|
705
801
|
rebuiltSignature = signature;
|
|
706
802
|
rebuiltXdevCatalogNames = built.xdevCatalogNames;
|
|
707
803
|
}
|
|
708
804
|
}
|
|
805
|
+
signal?.throwIfAborted();
|
|
709
806
|
} catch (error) {
|
|
710
807
|
this.#setMountedNames(previousMounted);
|
|
711
808
|
this.#setActiveToolNames?.(previousActiveToolNames);
|
|
@@ -917,15 +1014,17 @@ export class SessionTools {
|
|
|
917
1014
|
}
|
|
918
1015
|
|
|
919
1016
|
/** Selects enabled tools, ignoring names absent from the registry. */
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
1017
|
+
setActiveToolsByName(toolNames: string[]): Promise<void> {
|
|
1018
|
+
return this.runToolRegistryMutation(async () => {
|
|
1019
|
+
const normalized = normalizeToolNames(toolNames);
|
|
1020
|
+
// Transport-write eligibility keys off the *current* active set: an ordinary
|
|
1021
|
+
// selection change should not demote `write` unless it is already active.
|
|
1022
|
+
await this.#applyToolPresentation(
|
|
1023
|
+
normalized,
|
|
1024
|
+
this.#xdev?.mountedNames ?? new Set(),
|
|
1025
|
+
this.getActiveToolNames().includes("write"),
|
|
1026
|
+
);
|
|
1027
|
+
});
|
|
929
1028
|
}
|
|
930
1029
|
|
|
931
1030
|
/**
|
|
@@ -938,19 +1037,31 @@ export class SessionTools {
|
|
|
938
1037
|
* `xd://` remain mount-eligible, even when the live mount set has drifted.
|
|
939
1038
|
*
|
|
940
1039
|
* Names outside `mountedToolNames` are pinned top-level for this application;
|
|
941
|
-
* names in the mounted subset remain eligible for xdev mounting.
|
|
942
|
-
*
|
|
943
|
-
*
|
|
1040
|
+
* names in the mounted subset remain eligible for xdev mounting. Set
|
|
1041
|
+
* `forcePromptRefresh` when an enabled tool's schema or prompt-visible metadata
|
|
1042
|
+
* changed without changing its name or presentation.
|
|
1043
|
+
*
|
|
1044
|
+
* Delegates the actual apply through {@link applyActiveToolsByName} and restores
|
|
1045
|
+
* the prior runtime selection if that apply throws.
|
|
944
1046
|
*/
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
normalized
|
|
953
|
-
|
|
1047
|
+
setActiveToolPresentation(
|
|
1048
|
+
toolNames: string[],
|
|
1049
|
+
mountedToolNames: string[],
|
|
1050
|
+
forcePromptRefresh = false,
|
|
1051
|
+
signal?: AbortSignal,
|
|
1052
|
+
): Promise<void> {
|
|
1053
|
+
return this.runToolRegistryMutation(async () => {
|
|
1054
|
+
const normalized = normalizeToolNames(toolNames);
|
|
1055
|
+
// Restoration targets a snapshot, so write eligibility comes from the
|
|
1056
|
+
// *target* set rather than whatever happens to be active mid-rollback.
|
|
1057
|
+
await this.#applyToolPresentation(
|
|
1058
|
+
normalized,
|
|
1059
|
+
new Set(normalizeToolNames(mountedToolNames)),
|
|
1060
|
+
normalized.includes("write"),
|
|
1061
|
+
forcePromptRefresh,
|
|
1062
|
+
signal,
|
|
1063
|
+
);
|
|
1064
|
+
}, signal);
|
|
954
1065
|
}
|
|
955
1066
|
|
|
956
1067
|
/**
|
|
@@ -962,6 +1073,8 @@ export class SessionTools {
|
|
|
962
1073
|
normalized: string[],
|
|
963
1074
|
mounted: ReadonlySet<string>,
|
|
964
1075
|
writeSelected: boolean,
|
|
1076
|
+
forcePromptRefresh = false,
|
|
1077
|
+
signal?: AbortSignal,
|
|
965
1078
|
): Promise<void> {
|
|
966
1079
|
const transportWriteActive =
|
|
967
1080
|
writeSelected &&
|
|
@@ -974,7 +1087,7 @@ export class SessionTools {
|
|
|
974
1087
|
normalized.filter(name => !mounted.has(name) && !(name === "write" && transportWriteActive)),
|
|
975
1088
|
);
|
|
976
1089
|
try {
|
|
977
|
-
await this
|
|
1090
|
+
await this.#applyActiveToolsByName(normalized, forcePromptRefresh, signal);
|
|
978
1091
|
} catch (error) {
|
|
979
1092
|
this.#runtimeSelectedToolNames = previousRuntimeSelectedToolNames;
|
|
980
1093
|
throw error;
|
|
@@ -982,24 +1095,26 @@ export class SessionTools {
|
|
|
982
1095
|
}
|
|
983
1096
|
|
|
984
1097
|
/** Replaces memory-backend tools while preserving unrelated selections. */
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
1098
|
+
replaceMemoryTools(tools: AgentTool[]): Promise<void> {
|
|
1099
|
+
return this.runToolRegistryMutation(async () => {
|
|
1100
|
+
const removed = new Set<string>(MEMORY_BACKEND_TOOL_NAMES.filter(name => this.#builtInToolNames.has(name)));
|
|
1101
|
+
const nextActive = this.getEnabledToolNames().filter(name => !removed.has(name));
|
|
1102
|
+
for (const name of removed) {
|
|
1103
|
+
this.#toolRegistry.delete(name);
|
|
1104
|
+
this.#builtInToolNames.delete(name);
|
|
1105
|
+
}
|
|
992
1106
|
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
1107
|
+
for (const tool of tools) {
|
|
1108
|
+
if (!MEMORY_BACKEND_TOOL_NAMES.some(name => name === tool.name) || this.#toolRegistry.has(tool.name)) {
|
|
1109
|
+
continue;
|
|
1110
|
+
}
|
|
1111
|
+
const wrapped = this.#wrapRuntimeTool(tool);
|
|
1112
|
+
this.#toolRegistry.set(wrapped.name, wrapped);
|
|
1113
|
+
this.#builtInToolNames.add(wrapped.name);
|
|
1114
|
+
nextActive.push(wrapped.name);
|
|
996
1115
|
}
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
this.#builtInToolNames.add(wrapped.name);
|
|
1000
|
-
nextActive.push(wrapped.name);
|
|
1001
|
-
}
|
|
1002
|
-
await this.applyActiveToolsByName([...new Set(nextActive)]);
|
|
1116
|
+
await this.#applyActiveToolsByName([...new Set(nextActive)]);
|
|
1117
|
+
});
|
|
1003
1118
|
}
|
|
1004
1119
|
|
|
1005
1120
|
/**
|
|
@@ -1015,34 +1130,67 @@ export class SessionTools {
|
|
|
1015
1130
|
* @returns false when enabling was requested but this session cannot build the
|
|
1016
1131
|
* tool (e.g. restricted child sessions have no factory).
|
|
1017
1132
|
*/
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
if (
|
|
1023
|
-
|
|
1133
|
+
setComputerToolEnabled(enabled: boolean): Promise<boolean> {
|
|
1134
|
+
return this.runToolRegistryMutation(async () => {
|
|
1135
|
+
const logState = (): void => this.#logComputerState("Computer tool state changed", enabled);
|
|
1136
|
+
const active = this.getEnabledToolNames();
|
|
1137
|
+
if (!enabled) {
|
|
1138
|
+
if (active.includes("computer")) {
|
|
1139
|
+
await this.#applyActiveToolsByName(active.filter(name => name !== "computer"));
|
|
1140
|
+
}
|
|
1141
|
+
logState();
|
|
1142
|
+
return true;
|
|
1143
|
+
}
|
|
1144
|
+
if (!this.#toolRegistry.has("computer")) {
|
|
1145
|
+
const tool = await this.#createComputerTool?.();
|
|
1146
|
+
if (tool?.name !== "computer") {
|
|
1147
|
+
const model = this.#host.model();
|
|
1148
|
+
logger.warn("Computer tool could not be created", {
|
|
1149
|
+
model: model ? formatModelString(model) : undefined,
|
|
1150
|
+
});
|
|
1151
|
+
return false;
|
|
1152
|
+
}
|
|
1153
|
+
const wrapped = this.#wrapRuntimeTool(tool);
|
|
1154
|
+
this.#toolRegistry.set(wrapped.name, wrapped);
|
|
1155
|
+
this.#builtInToolNames.add(wrapped.name);
|
|
1156
|
+
}
|
|
1157
|
+
if (!active.includes("computer")) {
|
|
1158
|
+
await this.#applyActiveToolsByName([...active, "computer"]);
|
|
1024
1159
|
}
|
|
1025
1160
|
logState();
|
|
1026
1161
|
return true;
|
|
1027
|
-
}
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1162
|
+
});
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
/**
|
|
1166
|
+
* Session-scoped enable/disable for the private `think` scratchpad tool.
|
|
1167
|
+
*
|
|
1168
|
+
* Enabling constructs the tool once and refreshes the model's tool contract;
|
|
1169
|
+
* disabling removes it from the active set while preserving its registry entry.
|
|
1170
|
+
*
|
|
1171
|
+
* @returns false when enabling was requested but this session cannot build the tool.
|
|
1172
|
+
*/
|
|
1173
|
+
setThinkToolEnabled(enabled: boolean): Promise<boolean> {
|
|
1174
|
+
return this.runToolRegistryMutation(async () => {
|
|
1175
|
+
const active = this.getEnabledToolNames();
|
|
1176
|
+
if (!enabled) {
|
|
1177
|
+
if (active.includes("think")) {
|
|
1178
|
+
await this.#applyActiveToolsByName(active.filter(name => name !== "think"));
|
|
1179
|
+
}
|
|
1180
|
+
return true;
|
|
1036
1181
|
}
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1182
|
+
if (!this.#toolRegistry.has("think")) {
|
|
1183
|
+
const tool = await this.#createThinkTool?.();
|
|
1184
|
+
if (tool?.name !== "think") return false;
|
|
1185
|
+
const wrapped = this.#wrapRuntimeTool(tool);
|
|
1186
|
+
this.#toolRegistry.set(wrapped.name, wrapped);
|
|
1187
|
+
this.#builtInToolNames.add(wrapped.name);
|
|
1188
|
+
}
|
|
1189
|
+
if (!active.includes("think")) {
|
|
1190
|
+
await this.#applyActiveToolsByName([...active, "think"]);
|
|
1191
|
+
}
|
|
1192
|
+
return true;
|
|
1193
|
+
});
|
|
1046
1194
|
}
|
|
1047
1195
|
|
|
1048
1196
|
/** Current effective inspect_image state for `/vision status`. */
|
|
@@ -1065,48 +1213,50 @@ export class SessionTools {
|
|
|
1065
1213
|
* @returns false when the tool should be active but this session cannot
|
|
1066
1214
|
* build it (e.g. restricted child sessions have no factory).
|
|
1067
1215
|
*/
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
const
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
await this.applyActiveToolsByName(active.filter(name => name !== "inspect_image"));
|
|
1092
|
-
return true;
|
|
1093
|
-
}
|
|
1094
|
-
if (!this.#toolRegistry.has("inspect_image")) {
|
|
1095
|
-
const tool = await this.#createInspectImageTool?.();
|
|
1096
|
-
if (tool?.name !== "inspect_image") {
|
|
1097
|
-
logger.warn("inspect_image tool could not be created", {
|
|
1098
|
-
model: this.#host.model()?.id,
|
|
1099
|
-
});
|
|
1216
|
+
reconcileInspectImageTool(): Promise<boolean> {
|
|
1217
|
+
return this.runToolRegistryMutation(async () => {
|
|
1218
|
+
const expected = isInspectImageToolActive({
|
|
1219
|
+
settings: this.#host.settings,
|
|
1220
|
+
getActiveModel: () => this.#host.model(),
|
|
1221
|
+
getInspectImageModeOverride: () => this.#host.getInspectImageModeOverride(),
|
|
1222
|
+
});
|
|
1223
|
+
// Keep the read tool's advertised description in sync BEFORE any prompt
|
|
1224
|
+
// rebuild below, passing the post-change availability so the prompt never
|
|
1225
|
+
// lags a flip in either direction. Per-read lazy sync is the backstop.
|
|
1226
|
+
const syncReadDescription = (available: boolean): void => {
|
|
1227
|
+
const readTool = this.#toolRegistry.get("read") as
|
|
1228
|
+
| { syncInspectImageState?: (available?: boolean) => boolean }
|
|
1229
|
+
| undefined;
|
|
1230
|
+
readTool?.syncInspectImageState?.(available);
|
|
1231
|
+
};
|
|
1232
|
+
const active = this.getEnabledToolNames();
|
|
1233
|
+
const isActive = active.includes("inspect_image");
|
|
1234
|
+
if (expected === isActive) {
|
|
1235
|
+
syncReadDescription(isActive);
|
|
1236
|
+
return true;
|
|
1237
|
+
}
|
|
1238
|
+
if (!expected) {
|
|
1100
1239
|
syncReadDescription(false);
|
|
1101
|
-
|
|
1240
|
+
await this.#applyActiveToolsByName(active.filter(name => name !== "inspect_image"));
|
|
1241
|
+
return true;
|
|
1102
1242
|
}
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1243
|
+
if (!this.#toolRegistry.has("inspect_image")) {
|
|
1244
|
+
const tool = await this.#createInspectImageTool?.();
|
|
1245
|
+
if (tool?.name !== "inspect_image") {
|
|
1246
|
+
logger.warn("inspect_image tool could not be created", {
|
|
1247
|
+
model: this.#host.model()?.id,
|
|
1248
|
+
});
|
|
1249
|
+
syncReadDescription(false);
|
|
1250
|
+
return false;
|
|
1251
|
+
}
|
|
1252
|
+
const wrapped = this.#wrapRuntimeTool(tool);
|
|
1253
|
+
this.#toolRegistry.set(wrapped.name, wrapped);
|
|
1254
|
+
this.#builtInToolNames.add(wrapped.name);
|
|
1255
|
+
}
|
|
1256
|
+
syncReadDescription(true);
|
|
1257
|
+
await this.#applyActiveToolsByName([...active, "inspect_image"]);
|
|
1258
|
+
return true;
|
|
1259
|
+
});
|
|
1110
1260
|
}
|
|
1111
1261
|
|
|
1112
1262
|
/**
|
|
@@ -1115,20 +1265,22 @@ export class SessionTools {
|
|
|
1115
1265
|
* path — including retry-fallback switches that bypass
|
|
1116
1266
|
* {@link syncAfterModelChange}.
|
|
1117
1267
|
*/
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1268
|
+
reconcileInspectImageAfterModelChange(): Promise<void> {
|
|
1269
|
+
return this.runToolRegistryMutation(async () => {
|
|
1270
|
+
const before = this.getEnabledToolNames().includes("inspect_image");
|
|
1271
|
+
const reconciled = await this.reconcileInspectImageTool();
|
|
1272
|
+
const after = this.getEnabledToolNames().includes("inspect_image");
|
|
1273
|
+
if (!reconciled || before === after) return;
|
|
1274
|
+
const model = this.#host.model();
|
|
1275
|
+
const modelName = model ? formatModelString(model) : "the current model";
|
|
1276
|
+
this.#host.emitNotice(
|
|
1277
|
+
"info",
|
|
1278
|
+
after
|
|
1279
|
+
? `inspect_image is now available: ${modelName} has no native image input.`
|
|
1280
|
+
: `inspect_image is now hidden: ${modelName} supports image input natively. Override with /vision on.`,
|
|
1281
|
+
"vision",
|
|
1282
|
+
);
|
|
1283
|
+
});
|
|
1132
1284
|
}
|
|
1133
1285
|
|
|
1134
1286
|
/**
|
|
@@ -1139,16 +1291,22 @@ export class SessionTools {
|
|
|
1139
1291
|
*
|
|
1140
1292
|
* @returns false when `on` was requested but the tool cannot be built here.
|
|
1141
1293
|
*/
|
|
1142
|
-
|
|
1143
|
-
this
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1294
|
+
setInspectImageMode(mode: InspectImageMode): Promise<boolean> {
|
|
1295
|
+
return this.runToolRegistryMutation(async () => {
|
|
1296
|
+
this.#host.setInspectImageModeOverride(mode === "auto" ? undefined : mode);
|
|
1297
|
+
const applied = await this.reconcileInspectImageTool();
|
|
1298
|
+
const { active, model } = this.inspectImageState();
|
|
1299
|
+
logger.debug("inspect_image mode changed", { mode, active, model });
|
|
1300
|
+
return applied;
|
|
1301
|
+
});
|
|
1148
1302
|
}
|
|
1149
1303
|
|
|
1150
1304
|
/** Rebuilds the stable base prompt for the current tools and model. */
|
|
1151
|
-
|
|
1305
|
+
refreshBaseSystemPrompt(): Promise<void> {
|
|
1306
|
+
return this.runToolRegistryMutation(() => this.#refreshBaseSystemPrompt());
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
async #refreshBaseSystemPrompt(): Promise<void> {
|
|
1152
1310
|
if (this.#host.isDisposed() || !this.#rebuildSystemPrompt) return;
|
|
1153
1311
|
const activeToolNames = this.getActiveToolNames();
|
|
1154
1312
|
this.#setActiveToolNames?.(activeToolNames);
|
|
@@ -1299,32 +1457,25 @@ export class SessionTools {
|
|
|
1299
1457
|
*/
|
|
1300
1458
|
refreshMCPTools(mcpTools: CustomTool[]): Promise<void> {
|
|
1301
1459
|
const snapshot = [...mcpTools];
|
|
1302
|
-
|
|
1303
|
-
this.#host.isDisposed() ?
|
|
1460
|
+
return this.runToolRegistryMutation(() =>
|
|
1461
|
+
this.#host.isDisposed() ? Promise.resolve() : this.#applyMCPToolRefresh(snapshot),
|
|
1304
1462
|
);
|
|
1305
|
-
this.#mcpRefreshTail = refresh.catch(() => {});
|
|
1306
|
-
return refresh;
|
|
1307
1463
|
}
|
|
1308
1464
|
|
|
1309
1465
|
async #applyMCPToolRefresh(mcpTools: CustomTool[]): Promise<void> {
|
|
1310
|
-
const
|
|
1311
|
-
const
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
);
|
|
1466
|
+
const previousMcpTools = new Map<string, AgentTool>();
|
|
1467
|
+
for (const [name, tool] of this.#toolRegistry) {
|
|
1468
|
+
if (isMCPToolName(name)) previousMcpTools.set(name, tool);
|
|
1469
|
+
}
|
|
1470
|
+
const previousMcpManagerToolNames = new Set(this.#mcpManagerToolNames);
|
|
1471
|
+
const previousActiveMcpToolNames = this.getEnabledToolNames().filter(isMCPToolName);
|
|
1317
1472
|
const restorePreviousMcpTools = () => {
|
|
1318
1473
|
for (const name of this.#toolRegistry.keys()) {
|
|
1319
1474
|
if (isMCPToolName(name)) this.#toolRegistry.delete(name);
|
|
1320
1475
|
}
|
|
1321
1476
|
for (const [name, tool] of previousMcpTools) this.#toolRegistry.set(name, tool);
|
|
1477
|
+
this.#mcpManagerToolNames = previousMcpManagerToolNames;
|
|
1322
1478
|
};
|
|
1323
|
-
for (const name of existingNames) {
|
|
1324
|
-
if (isMCPToolName(name)) {
|
|
1325
|
-
this.#toolRegistry.delete(name);
|
|
1326
|
-
}
|
|
1327
|
-
}
|
|
1328
1479
|
|
|
1329
1480
|
const getCustomToolContext = (): CustomToolContext => ({
|
|
1330
1481
|
sessionManager: this.#host.sessionManager,
|
|
@@ -1340,20 +1491,36 @@ export class SessionTools {
|
|
|
1340
1491
|
});
|
|
1341
1492
|
|
|
1342
1493
|
const extensionRunner = this.#host.extensionRunner();
|
|
1343
|
-
const
|
|
1344
|
-
for (const customTool of uniqueMcpTools) {
|
|
1494
|
+
const managerTools = deduplicateMCPToolsByName(mcpTools).map(customTool => {
|
|
1345
1495
|
const wrapped = wrapToolWithMetaNotice(CustomToolAdapter.wrap(customTool, getCustomToolContext) as AgentTool);
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1496
|
+
return (extensionRunner ? new ExtensionToolWrapper(wrapped, extensionRunner) : wrapped) as AgentTool;
|
|
1497
|
+
});
|
|
1498
|
+
const managerToolSet = new Set(managerTools);
|
|
1499
|
+
const reconciledTools = deduplicateMCPToolsByName([...this.#extensionMcpTools.values(), ...managerTools]);
|
|
1500
|
+
|
|
1501
|
+
for (const name of this.#toolRegistry.keys()) {
|
|
1502
|
+
if (isMCPToolName(name)) this.#toolRegistry.delete(name);
|
|
1503
|
+
}
|
|
1504
|
+
this.#mcpManagerToolNames.clear();
|
|
1505
|
+
for (const tool of reconciledTools) {
|
|
1506
|
+
this.#toolRegistry.set(tool.name, tool);
|
|
1507
|
+
if (managerToolSet.has(tool)) this.#mcpManagerToolNames.add(tool.name);
|
|
1350
1508
|
}
|
|
1351
1509
|
|
|
1352
|
-
//
|
|
1353
|
-
//
|
|
1354
|
-
const
|
|
1510
|
+
// Connected manager tools become active immediately. Extension-owned MCP
|
|
1511
|
+
// tools retain their prior selection while both sets share one registry.
|
|
1512
|
+
const retainedActiveExtensionToolNames = previousActiveMcpToolNames.filter(
|
|
1513
|
+
name => this.#extensionMcpTools.has(name) && this.#toolRegistry.has(name),
|
|
1514
|
+
);
|
|
1515
|
+
const nextActive = [
|
|
1516
|
+
...new Set([
|
|
1517
|
+
...this.#getActiveNonMCPToolNames(),
|
|
1518
|
+
...this.#mcpManagerToolNames,
|
|
1519
|
+
...retainedActiveExtensionToolNames,
|
|
1520
|
+
]),
|
|
1521
|
+
];
|
|
1355
1522
|
try {
|
|
1356
|
-
await this
|
|
1523
|
+
await this.#applyActiveToolsByName(nextActive);
|
|
1357
1524
|
if (this.#host.isDisposed()) restorePreviousMcpTools();
|
|
1358
1525
|
} catch (error) {
|
|
1359
1526
|
restorePreviousMcpTools();
|
|
@@ -1362,7 +1529,12 @@ export class SessionTools {
|
|
|
1362
1529
|
}
|
|
1363
1530
|
|
|
1364
1531
|
/** Replaces RPC host-owned tools and refreshes the active set before the next model call. */
|
|
1365
|
-
|
|
1532
|
+
refreshRpcHostTools(rpcTools: AgentTool[]): Promise<void> {
|
|
1533
|
+
const snapshot = [...rpcTools];
|
|
1534
|
+
return this.runToolRegistryMutation(() => this.#applyRpcHostToolRefresh(snapshot));
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
async #applyRpcHostToolRefresh(rpcTools: AgentTool[]): Promise<void> {
|
|
1366
1538
|
const nextToolNames = rpcTools.map(tool => tool.name);
|
|
1367
1539
|
const uniqueToolNames = new Set(nextToolNames);
|
|
1368
1540
|
if (uniqueToolNames.size !== nextToolNames.length) {
|
|
@@ -1406,7 +1578,7 @@ export class SessionTools {
|
|
|
1406
1578
|
.filter(tool => !tool.hidden && !previousRpcHostToolNames.has(tool.name))
|
|
1407
1579
|
.map(tool => tool.name);
|
|
1408
1580
|
try {
|
|
1409
|
-
await this
|
|
1581
|
+
await this.#applyActiveToolsByName(
|
|
1410
1582
|
Array.from(new Set([...activeNonRpcToolNames, ...preservedRpcToolNames, ...autoActivatedRpcToolNames])),
|
|
1411
1583
|
);
|
|
1412
1584
|
} catch (error) {
|