feat: 接入 Pi 内置 codemode,并修复全面审查发现的问题

- codemode:Tool Search 常驻(alwaysEnabled)、权限 allow(嵌套调用仍逐个过权限门)、
  tools.jsonc 登记使用规则;RTK 跳过 parentToolCallId 嵌套调用,避免丢失 bash
  structuredContent;Tool Search 目录过滤 hidden / codemode(script-only)exposure;
  扩展加载测试加载 builtin:codemode 并断言其常驻激活
- 搜索 key 不再经命令行参数传递:search-auth.mjs 改为 --set-stdin(先读校验再加锁),
  configure.sh 交互输入经管道、非交互用 --<provider> -
- CodeGraph 客户端监听子进程 stdin error,避免 EPIPE 未处理导致崩溃
- 移除 PI_TOOL_SEARCH/HERMES_MEMORY/RTK_CONFIG_PATH 环境变量入口,测试改用工厂 configPath 注入
- pi-subagents 固定 vitest / coverage-istanbul 4.1.10,修复 npm ls 版本冲突
- 补 pi-web-search / pi-worktree UPSTREAM.md 与 pi-minimal-footer AGENTS.md,
  files 白名单补齐并加测试;.codegraph/.gitignore 移出跟踪
- detect_kitty 优先识别官方安装目录

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
云服务部-叶林立
2026-10-21 23:18:34 -07:00
co-authored by Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
parent 56cbc83296
commit f9a7f743dd
46 changed files with 585 additions and 198 deletions
+1 -2
View File
@@ -11,8 +11,6 @@ const TOOL_SEARCH_DIR = join("my-pi", "tool-search");
* agent directory.
*/
export function getToolSearchConfigPath(): string {
const override = process.env.PI_TOOL_SEARCH_CONFIG_PATH;
if (override) return override;
return fileURLToPath(new URL("../../config/tool-search.json", import.meta.url));
}
@@ -33,6 +31,7 @@ export const BUNDLE_TOOL_SEARCH_DEFAULTS = {
"skill_manage",
"context_tree_query",
"overflow_query",
"codemode",
],
showToolSearchFooterStatus: false,
maxActiveGroups: 5,
+26 -7
View File
@@ -28,7 +28,7 @@ import {
createBundleCatalog,
type BundleCatalogResult,
} from "./bundle-groups.ts";
import { getToolSearchCatalogPath, readToolSearchConfig } from "./config.ts";
import { getToolSearchCatalogPath, getToolSearchConfigPath, readToolSearchConfig } from "./config.ts";
const TOOL_SEARCH_NAME = "tool_search";
const POWERSHELL_TOOL_NAME = "powershell";
@@ -39,13 +39,19 @@ type CatalogSource = "bundle" | "cache" | "fallback" | "hybrid" | "model";
type ModelUsage = Awaited<ReturnType<ExtensionContext["modelRegistry"]["complete"]>>["usage"];
type CatalogGenerationResult = { notice?: string; usage?: ModelUsage };
export function platformToolPolicy<T extends { name: string }>(
export function platformToolPolicy<T extends { name: string; exposure?: string }>(
definitions: readonly T[],
platform: NodeJS.Platform,
): { tools: T[]; coreTools: string[] } {
// Script-only and hidden tools are unreachable by the model directly and
// must never enter the catalog (or the pinned set); "model-only" tools like
// codemode stay reachable and can be pinned.
const reachable = definitions.filter(
(tool) => tool.exposure !== "hidden" && tool.exposure !== "codemode",
);
const tools = platform === "win32"
? [...definitions]
: definitions.filter((tool) => tool.name !== POWERSHELL_TOOL_NAME);
? [...reachable]
: reachable.filter((tool) => tool.name !== POWERSHELL_TOOL_NAME);
const coreTools = platform === "win32"
? [...CORE_TOOLS, POWERSHELL_TOOL_NAME]
: [...CORE_TOOLS];
@@ -110,11 +116,11 @@ function groupDescription(catalog: ToolCatalog, source: CatalogSource): string {
].join("\n");
}
export default function toolSearchExtension(pi: ExtensionAPI): void {
function toolSearchExtension(pi: ExtensionAPI, configPath: string): void {
const agentDir = getAgentDir();
const cachePath = getToolSearchCatalogPath(agentDir);
let config = readToolSearchConfig();
let config = readToolSearchConfig(configPath);
let tools: ToolInfo[] = [];
let catalog: ToolCatalog = createFallbackCatalog([], "", {
maxToolsPerGroup: Math.min(config.maxToolsPerGroup, config.maxDynamicTools),
@@ -440,7 +446,7 @@ export default function toolSearchExtension(pi: ExtensionAPI): void {
}
function refreshState(ctx: Pick<ExtensionContext, "ui">, forceReset: boolean): void {
const nextConfig = readToolSearchConfig();
const nextConfig = readToolSearchConfig(configPath);
const registeredTools = pi.getAllTools().filter((tool) => tool.name !== TOOL_SEARCH_NAME);
const platformPolicy = platformToolPolicy(registeredTools, process.platform);
const allTools = platformPolicy.tools;
@@ -542,3 +548,16 @@ export default function toolSearchExtension(pi: ExtensionAPI): void {
if (group && activeGroups.has(group)) activeGroups.set(group, ++clock);
});
}
export interface ToolSearchExtensionOptions {
configPath?: string;
}
export function createToolSearchExtension(
options?: ToolSearchExtensionOptions,
): (pi: ExtensionAPI) => void {
const configPath = options?.configPath ?? getToolSearchConfigPath();
return (pi) => toolSearchExtension(pi, configPath);
}
export default createToolSearchExtension();