feat(pi-overflow): 新增统一的新工具结果展示截断扩展

此前截断分散在多层且互相冲突:Pi bash 保留尾部 50KB,RTK 兜底截断又只留
开头 12000 字符(砍掉报错与完整输出路径),pi-chrome 截开头 30K,web_fetch
各 provider 不一致。新扩展统一负责"新结果给模型看多少":
- capture.ts 最先加载暂存原文,index.ts 最后加载(system-prompt 之前)
- 超过 24K 字符时原文落盘 <agentDir>/my-pi/pi-overflow/(700/600、原子写),
  优先取 bash fullOutputPath 完整文件;模型看到 头 + 中段信号行 + 尾 的有界
  视图,行号基于落盘原文、结尾永远可见
- overflow_query:按 id 正则搜索、行区间、jsonPath 只读查询,id 校验防穿越
- 收编:RTK truncate 默认关闭、删除 pi-chrome truncateText、web_fetch 默认
  每页 20000(上限 100000)、web_search max_results 限定 1–20
- pi-compaction 遮蔽带 details.overflow.id 的结果时在占位符提示 overflow_query
- 登记 tools.jsonc / routing / 权限 allow / tool-search 常驻与分组

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 20:25:44 -07:00
co-authored by Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
parent a6d8416c76
commit 2a6bf8c929
44 changed files with 1882 additions and 34 deletions
+17 -6
View File
@@ -21,8 +21,15 @@ import {
} from "./router.ts";
const DEFAULT_MAX_RESULTS = 8;
const MAX_RESULTS_LIMIT = 20;
const MAX_FETCH_URLS = 10;
const DEFAULT_FETCH_MAX_CHARACTERS = 3000;
const DEFAULT_FETCH_MAX_CHARACTERS = 20000;
const FETCH_MAX_CHARACTERS_LIMIT = 100000;
function clampInt(value: number | undefined, fallback: number, min: number, max: number): number {
if (typeof value !== "number" || !Number.isFinite(value)) return fallback;
return Math.min(Math.max(Math.floor(value), min), max);
}
export interface WebSearchParams {
query: string;
@@ -48,7 +55,7 @@ function formatSearchPayload(payload: unknown): string {
export function toTavilyParams(params: WebSearchParams) {
return {
query: params.query,
max_results: params.max_results ?? DEFAULT_MAX_RESULTS,
max_results: clampInt(params.max_results, DEFAULT_MAX_RESULTS, 1, MAX_RESULTS_LIMIT),
search_depth: (params.depth ?? "basic") as "basic" | "advanced",
include_domains: params.site ? [params.site] : undefined,
start_date: params.published_after,
@@ -59,7 +66,7 @@ export function toTavilyParams(params: WebSearchParams) {
export function toExaParams(params: WebSearchParams) {
return {
query: params.query,
numResults: params.max_results ?? DEFAULT_MAX_RESULTS,
numResults: clampInt(params.max_results, DEFAULT_MAX_RESULTS, 1, MAX_RESULTS_LIMIT),
includeDomains: params.site ? [params.site] : undefined,
startPublishedDate: params.published_after,
endPublishedDate: params.published_before,
@@ -171,7 +178,7 @@ export async function dispatchFetch(
if (urls.length > MAX_FETCH_URLS) {
return toResult(`web_fetch accepts at most ${MAX_FETCH_URLS} URLs per call; received ${urls.length}.`, { error: true });
}
const maxCharacters = params.max_characters ?? DEFAULT_FETCH_MAX_CHARACTERS;
const maxCharacters = clampInt(params.max_characters, DEFAULT_FETCH_MAX_CHARACTERS, 1, FETCH_MAX_CHARACTERS_LIMIT);
const order: SearchProvider[] = ["exa", "keenable"];
if (tavilyAvailable(resolve)) order.push("tavily");
@@ -213,7 +220,9 @@ export default function webSearchExtension(pi: ExtensionAPI): void {
},
max_results: {
type: "number",
description: `Maximum number of results to return (default ${DEFAULT_MAX_RESULTS}).`,
minimum: 1,
maximum: MAX_RESULTS_LIMIT,
description: `Maximum number of results to return (default ${DEFAULT_MAX_RESULTS}, max ${MAX_RESULTS_LIMIT}).`,
},
site: {
type: "string",
@@ -268,7 +277,9 @@ export default function webSearchExtension(pi: ExtensionAPI): void {
},
max_characters: {
type: "number",
description: `Maximum characters to extract per page (default ${DEFAULT_FETCH_MAX_CHARACTERS}).`,
minimum: 1,
maximum: FETCH_MAX_CHARACTERS_LIMIT,
description: `Maximum characters to extract per page (default ${DEFAULT_FETCH_MAX_CHARACTERS}, max ${FETCH_MAX_CHARACTERS_LIMIT}).`,
},
},
required: ["urls"],