| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879 |
- import { feature } from 'bun:bundle'
- import partition from 'lodash-es/partition.js'
- import uniqBy from 'lodash-es/uniqBy.js'
- import { COORDINATOR_MODE_ALLOWED_TOOLS } from '../constants/tools.js'
- import { isMcpTool } from '../services/mcp/utils.js'
- import type { Tool, ToolPermissionContext, Tools } from '../Tool.js'
- // MCP tool name suffixes for PR activity subscription. These are lightweight
- // orchestration actions the coordinator calls directly rather than delegating
- // to workers. Matched by suffix since the MCP server name prefix may vary.
- const PR_ACTIVITY_TOOL_SUFFIXES = [
- 'subscribe_pr_activity',
- 'unsubscribe_pr_activity',
- ]
- export function isPrActivitySubscriptionTool(name: string): boolean {
- return PR_ACTIVITY_TOOL_SUFFIXES.some(suffix => name.endsWith(suffix))
- }
- // Dead code elimination: conditional imports for feature-gated modules
- /* eslint-disable @typescript-eslint/no-require-imports */
- const coordinatorModeModule = feature('COORDINATOR_MODE')
- ? (require('../coordinator/coordinatorMode.js') as typeof import('../coordinator/coordinatorMode.js'))
- : null
- /* eslint-enable @typescript-eslint/no-require-imports */
- /**
- * Filters a tool array to the set allowed in coordinator mode.
- * Shared between the REPL path (mergeAndFilterTools) and the headless
- * path (main.tsx) so both stay in sync.
- *
- * PR activity subscription tools are always allowed since subscription
- * management is orchestration.
- */
- export function applyCoordinatorToolFilter(tools: Tools): Tools {
- return tools.filter(
- t =>
- COORDINATOR_MODE_ALLOWED_TOOLS.has(t.name) ||
- isPrActivitySubscriptionTool(t.name),
- )
- }
- /**
- * Pure function that merges tool pools and applies coordinator mode filtering.
- *
- * Lives in a React-free file so print.ts can import it without pulling
- * react/ink into the SDK module graph. The useMergedTools hook delegates
- * to this function inside useMemo.
- *
- * @param initialTools - Extra tools to include (built-in + startup MCP from props).
- * @param assembled - Tools from assembleToolPool (built-in + MCP, deduped).
- * @param mode - The permission context mode.
- * @returns Merged, deduplicated, and coordinator-filtered tool array.
- */
- export function mergeAndFilterTools(
- initialTools: Tools,
- assembled: Tools,
- mode: ToolPermissionContext['mode'],
- ): Tools {
- // Merge initialTools on top - they take precedence in deduplication.
- // initialTools may include built-in tools (from getTools() in REPL.tsx) which
- // overlap with assembled tools. uniqBy handles this deduplication.
- // Partition-sort for prompt-cache stability (same as assembleToolPool):
- // built-ins must stay a contiguous prefix for the server's cache policy.
- const [mcp, builtIn] = partition(
- uniqBy([...initialTools, ...assembled], 'name'),
- isMcpTool,
- )
- const byName = (a: Tool, b: Tool) => a.name.localeCompare(b.name)
- const tools = [...builtIn.sort(byName), ...mcp.sort(byName)]
- if (feature('COORDINATOR_MODE') && coordinatorModeModule) {
- if (coordinatorModeModule.isCoordinatorMode()) {
- return applyCoordinatorToolFilter(tools)
- }
- }
- return tools
- }
|