mirror of
https://bitbucket.org/siakitem/my-pi.git
synced 2026-10-26 10:45:22 +00:00
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:
co-authored by
Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
parent
56cbc83296
commit
f9a7f743dd
@@ -10,6 +10,7 @@ function tool(name: string): ToolInfo {
|
||||
name,
|
||||
description: `Full description for ${name}`,
|
||||
parameters: { type: "object", properties: {} } as ToolInfo["parameters"],
|
||||
exposure: "direct",
|
||||
sourceInfo: { source: "extension", scope: "user", path: `/test/${name}.ts` } as unknown as ToolInfo["sourceInfo"],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ function tool(name: string, description: string): ToolInfo {
|
||||
name,
|
||||
description,
|
||||
parameters: { type: "object", properties: { query: { type: "string" } } } as ToolInfo["parameters"],
|
||||
exposure: "direct",
|
||||
sourceInfo: { source: "extension", scope: "user", path: `/test/${name}.ts` } as unknown as ToolInfo["sourceInfo"],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ test("bundle defaults keep the committed tool list", () => {
|
||||
"skill_manage",
|
||||
"context_tree_query",
|
||||
"overflow_query",
|
||||
"codemode",
|
||||
]);
|
||||
assert.deepEqual(bundleFallbackConfig(), {
|
||||
alwaysEnabled: [...BUNDLE_TOOL_SEARCH_DEFAULTS.alwaysEnabled],
|
||||
|
||||
@@ -5,7 +5,7 @@ import { join } from "node:path";
|
||||
import test from "node:test";
|
||||
|
||||
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
||||
import toolSearchExtension, { platformToolPolicy } from "../extensions/index.ts";
|
||||
import toolSearchExtension, { createToolSearchExtension, platformToolPolicy } from "../extensions/index.ts";
|
||||
|
||||
interface ToolSearchResult {
|
||||
details: {
|
||||
@@ -49,13 +49,12 @@ function testContext(overrides: Partial<ExtensionContext> = {}): ExtensionContex
|
||||
test("loads whole groups and evicts the least-recently-used group", async () => {
|
||||
const agentDir = await mkdtemp(join(tmpdir(), "my-pi-tool-search-extension-"));
|
||||
const previousAgentDir = process.env.PI_CODING_AGENT_DIR;
|
||||
const previousConfigPath = process.env.PI_TOOL_SEARCH_CONFIG_PATH;
|
||||
const configPath = join(agentDir, "tool-search-config.json");
|
||||
process.env.PI_CODING_AGENT_DIR = agentDir;
|
||||
process.env.PI_TOOL_SEARCH_CONFIG_PATH = join(agentDir, "tool-search-config.json");
|
||||
|
||||
try {
|
||||
await writeFile(
|
||||
process.env.PI_TOOL_SEARCH_CONFIG_PATH,
|
||||
configPath,
|
||||
JSON.stringify({
|
||||
alwaysEnabled: ["codegraph_explore", "lsp_diagnostics"],
|
||||
showToolSearchFooterStatus: false,
|
||||
@@ -97,7 +96,7 @@ test("loads whole groups and evicts the least-recently-used group", async () =>
|
||||
on: (event: string, handler: (...args: any[]) => unknown) => handlers.set(event, handler),
|
||||
} as unknown as ExtensionAPI;
|
||||
|
||||
toolSearchExtension(api);
|
||||
createToolSearchExtension({ configPath })(api);
|
||||
const ctx = testContext({
|
||||
hasUI: true,
|
||||
ui: {
|
||||
@@ -159,8 +158,6 @@ test("loads whole groups and evicts the least-recently-used group", async () =>
|
||||
} finally {
|
||||
if (previousAgentDir === undefined) delete process.env.PI_CODING_AGENT_DIR;
|
||||
else process.env.PI_CODING_AGENT_DIR = previousAgentDir;
|
||||
if (previousConfigPath === undefined) delete process.env.PI_TOOL_SEARCH_CONFIG_PATH;
|
||||
else process.env.PI_TOOL_SEARCH_CONFIG_PATH = previousConfigPath;
|
||||
await rm(agentDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
@@ -328,6 +325,57 @@ test("keeps PowerShell with Bash only on Windows", () => {
|
||||
assert.ok(windows.coreTools.includes("powershell"));
|
||||
});
|
||||
|
||||
test("drops hidden and script-only tools but keeps reachable exposures", () => {
|
||||
const definitions = [
|
||||
sourceTool("read", "Read a file"),
|
||||
{ ...sourceTool("hidden_helper", "Internal helper"), exposure: "hidden" },
|
||||
{ ...sourceTool("script_only", "Only callable from scripts"), exposure: "codemode" },
|
||||
{ ...sourceTool("codemode", "Batch several tool calls in one script"), exposure: "model-only" },
|
||||
{ ...sourceTool("lazy_tool", "Activated on demand"), exposure: "deferred" },
|
||||
];
|
||||
|
||||
const { tools } = platformToolPolicy(definitions, "darwin");
|
||||
assert.deepEqual(tools.map((tool) => tool.name), ["read", "codemode", "lazy_tool"]);
|
||||
});
|
||||
|
||||
test("pins a model-only codemode tool instead of hiding it", async () => {
|
||||
const agentDir = await mkdtemp(join(tmpdir(), "my-pi-tool-search-codemode-"));
|
||||
const previousAgentDir = process.env.PI_CODING_AGENT_DIR;
|
||||
process.env.PI_CODING_AGENT_DIR = agentDir;
|
||||
|
||||
try {
|
||||
const handlers = new Map<string, (...args: any[]) => unknown>();
|
||||
const registered = new Map<string, RegisteredTool>();
|
||||
const activeCalls: string[][] = [];
|
||||
const sourceTools = [
|
||||
sourceTool("read", "Read a file"),
|
||||
{ ...sourceTool("codemode", "Batch several tool calls in one script"), exposure: "model-only" },
|
||||
];
|
||||
const api = {
|
||||
getAllTools: () => sourceTools,
|
||||
registerTool: (definition: unknown) => {
|
||||
const tool = definition as RegisteredTool & { name: string };
|
||||
registered.set(tool.name, tool);
|
||||
},
|
||||
registerCommand: () => {},
|
||||
setActiveTools: (names: string[]) => activeCalls.push([...names]),
|
||||
on: (event: string, handler: (...args: any[]) => unknown) => handlers.set(event, handler),
|
||||
} as unknown as ExtensionAPI;
|
||||
// The bundle config pins "codemode" in alwaysEnabled; exposure "model-only"
|
||||
// stays reachable, so it must land in the active set, not a dynamic group.
|
||||
toolSearchExtension(api);
|
||||
handlers.get("session_start")?.({}, testContext());
|
||||
|
||||
assert.ok(activeCalls.at(-1)?.includes("codemode"));
|
||||
assert.ok(activeCalls.at(-1)?.includes("tool_search"));
|
||||
assert.ok(activeCalls.at(-1)?.includes("read"));
|
||||
} finally {
|
||||
if (previousAgentDir === undefined) delete process.env.PI_CODING_AGENT_DIR;
|
||||
else process.env.PI_CODING_AGENT_DIR = previousAgentDir;
|
||||
await rm(agentDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test("loads an exact known group without waiting for unknown-tool model enrichment", async () => {
|
||||
const agentDir = await mkdtemp(join(tmpdir(), "my-pi-tool-search-fast-path-"));
|
||||
const previousAgentDir = process.env.PI_CODING_AGENT_DIR;
|
||||
|
||||
Reference in New Issue
Block a user