mirror of
https://bitbucket.org/siakitem/my-pi.git
synced 2026-10-26 10:45:22 +00:00
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:
co-authored by
Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
parent
a6d8416c76
commit
2a6bf8c929
@@ -0,0 +1,100 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
export interface OverflowSettings {
|
||||
enabled: boolean;
|
||||
thresholdChars: number;
|
||||
headLines: number;
|
||||
tailLines: number;
|
||||
maxSignalLines: number;
|
||||
signalPattern: string;
|
||||
signalRegex: RegExp;
|
||||
maxLineChars: number;
|
||||
exemptTools: string[];
|
||||
retentionDays: number;
|
||||
maxStoreBytes: number;
|
||||
maxEntryBytes: number;
|
||||
}
|
||||
|
||||
export const DEFAULT_SIGNAL_PATTERN =
|
||||
"\\b(error|errors|fail|failed|failure|warn|warning|exception|traceback|panic|fatal)\\b";
|
||||
|
||||
export const OVERFLOW_DEFAULTS = {
|
||||
enabled: true,
|
||||
thresholdChars: 24_000,
|
||||
headLines: 40,
|
||||
tailLines: 120,
|
||||
maxSignalLines: 40,
|
||||
signalPattern: DEFAULT_SIGNAL_PATTERN,
|
||||
maxLineChars: 500,
|
||||
exemptTools: ["read", "edit", "write", "overflow_query", "ask_user_question"],
|
||||
retentionDays: 7,
|
||||
maxStoreBytes: 1_073_741_824,
|
||||
maxEntryBytes: 67_108_864,
|
||||
} as const;
|
||||
|
||||
// Bundle-level `config/` is the single source of truth: the extension reads
|
||||
// `config/pi-overflow.json` from inside the installed bundle (resolved
|
||||
// relative to this package-root source file), never `settings.json`.
|
||||
export const OVERFLOW_CONFIG_PATH = fileURLToPath(
|
||||
new URL("../config/pi-overflow.json", import.meta.url),
|
||||
);
|
||||
|
||||
function isObject(value: unknown): value is Record<string, unknown> {
|
||||
return value !== null && typeof value === "object" && !Array.isArray(value);
|
||||
}
|
||||
|
||||
function bool(value: unknown, fallback: boolean): boolean {
|
||||
return typeof value === "boolean" ? value : fallback;
|
||||
}
|
||||
|
||||
function intInRange(value: unknown, fallback: number, min: number, max: number): number {
|
||||
return typeof value === "number" && Number.isInteger(value) && value >= min && value <= max
|
||||
? value
|
||||
: fallback;
|
||||
}
|
||||
|
||||
function compileSignalPattern(value: unknown): { pattern: string; regex: RegExp } {
|
||||
const pattern = typeof value === "string" && value.length > 0 ? value : DEFAULT_SIGNAL_PATTERN;
|
||||
try {
|
||||
return { pattern, regex: new RegExp(pattern, "i") };
|
||||
} catch {
|
||||
return { pattern: DEFAULT_SIGNAL_PATTERN, regex: new RegExp(DEFAULT_SIGNAL_PATTERN, "i") };
|
||||
}
|
||||
}
|
||||
|
||||
function stringList(value: unknown, fallback: readonly string[]): string[] {
|
||||
if (!Array.isArray(value)) return [...fallback];
|
||||
const list = value.filter((item): item is string => typeof item === "string" && item.length > 0);
|
||||
return [...new Set(list)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Read `config/pi-overflow.json`. Every field is validated independently and
|
||||
* falls back to its bundle default when missing or invalid; an unreadable or
|
||||
* non-object file yields all defaults. Tests inject an explicit path.
|
||||
*/
|
||||
export function readOverflowSettings(configPath: string = OVERFLOW_CONFIG_PATH): OverflowSettings {
|
||||
let parsed: unknown = {};
|
||||
try {
|
||||
parsed = JSON.parse(readFileSync(configPath, "utf8"));
|
||||
} catch {
|
||||
parsed = {};
|
||||
}
|
||||
const config = isObject(parsed) ? parsed : {};
|
||||
const signal = compileSignalPattern(config.signalPattern);
|
||||
return {
|
||||
enabled: bool(config.enabled, OVERFLOW_DEFAULTS.enabled),
|
||||
thresholdChars: intInRange(config.thresholdChars, OVERFLOW_DEFAULTS.thresholdChars, 1, 10_000_000),
|
||||
headLines: intInRange(config.headLines, OVERFLOW_DEFAULTS.headLines, 0, 10_000),
|
||||
tailLines: intInRange(config.tailLines, OVERFLOW_DEFAULTS.tailLines, 0, 10_000),
|
||||
maxSignalLines: intInRange(config.maxSignalLines, OVERFLOW_DEFAULTS.maxSignalLines, 0, 1_000),
|
||||
signalPattern: signal.pattern,
|
||||
signalRegex: signal.regex,
|
||||
maxLineChars: intInRange(config.maxLineChars, OVERFLOW_DEFAULTS.maxLineChars, 10, 100_000),
|
||||
exemptTools: stringList(config.exemptTools, OVERFLOW_DEFAULTS.exemptTools),
|
||||
retentionDays: intInRange(config.retentionDays, OVERFLOW_DEFAULTS.retentionDays, 0, 3_650),
|
||||
maxStoreBytes: intInRange(config.maxStoreBytes, OVERFLOW_DEFAULTS.maxStoreBytes, 1_024, 1_099_511_627_776),
|
||||
maxEntryBytes: intInRange(config.maxEntryBytes, OVERFLOW_DEFAULTS.maxEntryBytes, 1_024, 1_099_511_627_776),
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user