mirror of
https://bitbucket.org/siakitem/my-pi.git
synced 2026-08-28 08:35:57 +00:00
feat: add routed online search providers
This commit is contained in:
@@ -13,6 +13,9 @@
|
|||||||
- 初始导入快照:`8d196e4ef0884cac8326c366191dad3f585d470a`(`@mzwing/pi-permission-auto-review@0.2.0`)。
|
- 初始导入快照:`8d196e4ef0884cac8326c366191dad3f585d470a`(`@mzwing/pi-permission-auto-review@0.2.0`)。
|
||||||
- 该目录从上游源码仓库导入并由本仓库直接维护,不使用 npm 预编译产物,不是 submodule,也不保留嵌套 `.git`。
|
- 该目录从上游源码仓库导入并由本仓库直接维护,不使用 npm 预编译产物,不是 submodule,也不保留嵌套 `.git`。
|
||||||
- `extensions/fff-override.ts`:强制使用 FFF 官方 `override` 模式,统一接管 `find`、`grep`、`multi_grep` 和 FFF 的 `@` 补全;显式 CLI flag 仍遵循 FFF 官方优先级。
|
- `extensions/fff-override.ts`:强制使用 FFF 官方 `override` 模式,统一接管 `find`、`grep`、`multi_grep` 和 FFF 的 `@` 补全;显式 CLI flag 仍遵循 FFF 官方优先级。
|
||||||
|
- `extensions/tavily-override.ts`:加载 `@tavily/pi-extension`,把上游工具重命名为 `tavily_web_search` / `tavily_web_fetch`,并同步改写 label 与工具提示中的内部名称。
|
||||||
|
- `@keenable/pi-search`:加载 `keenable_search` / `keenable_fetch` 及其 skill,默认 keyless,`KEENABLE_API_KEY` 仅用于提高速率限制。
|
||||||
|
- `extensions/exa.ts`:通过隔离的 `pi-mcp-adapter` 配置连接 Exa 托管 MCP,把原始工具名映射为统一的来源前缀形式 `exa_web_search`、`exa_web_fetch` 和 `exa_web_search_advanced`。
|
||||||
- `context-mode`:加载上游 Pi adapter 与 skills,提供 `ctx_*` 工具、隔离式大输出处理和会话连续性。
|
- `context-mode`:加载上游 Pi adapter 与 skills,提供 `ctx_*` 工具、隔离式大输出处理和会话连续性。
|
||||||
- `extensions/hashline.ts`:部署组合包权威 `config/pi-hashline-edit.json` 后加载 `pi-hashline-edit`,以行哈希锚点覆盖内置 `read` / `edit`;Hashline 自带 `grep` 强制关闭。
|
- `extensions/hashline.ts`:部署组合包权威 `config/pi-hashline-edit.json` 后加载 `pi-hashline-edit`,以行哈希锚点覆盖内置 `read` / `edit`;Hashline 自带 `grep` 强制关闭。
|
||||||
- `extensions/codegraph.ts`:通过隔离的 `pi-mcp-adapter` 配置连接机器现有的 `codegraph serve --mcp`,只暴露 `codegraph_explore`。
|
- `extensions/codegraph.ts`:通过隔离的 `pi-mcp-adapter` 配置连接机器现有的 `codegraph serve --mcp`,只暴露 `codegraph_explore`。
|
||||||
@@ -22,7 +25,7 @@
|
|||||||
- `pi-lsp@0.1.7`:提供声明式 LSP 接入;组合包内置 `typescript-language-server@5.3.0` + `typescript@6.0.3`,并配置机器级 `kotlin-lsp --stdio` 与 `jdtls`。
|
- `pi-lsp@0.1.7`:提供声明式 LSP 接入;组合包内置 `typescript-language-server@5.3.0` + `typescript@6.0.3`,并配置机器级 `kotlin-lsp --stdio` 与 `jdtls`。
|
||||||
- `pi-hermes-memory@0.9.6`:提供持久记忆、会话搜索、后台学习和 secret scanning,默认使用 policy-only 模式。
|
- `pi-hermes-memory@0.9.6`:提供持久记忆、会话搜索、后台学习和 secret scanning,默认使用 policy-only 模式。
|
||||||
- `@ogulcancelik/pi-codex-compaction@0.1.3`:为 `openai-codex` 提供原生远程 compaction,默认阈值为 90%。
|
- `@ogulcancelik/pi-codex-compaction@0.1.3`:为 `openai-codex` 提供原生远程 compaction,默认阈值为 90%。
|
||||||
- 根包还固定安装 `pi-hashline-edit@0.8.3`、`typescript-language-server@5.3.0`、`typescript@6.0.3`、`pi-context-view`、`@firstpick/pi-extension-codex-fast-mode` 和 `@gotgenes/pi-permission-system`。
|
- 根包还固定安装 `@tavily/pi-extension@0.1.2`、`@keenable/pi-search@0.1.2`、`pi-hashline-edit@0.8.3`、`typescript-language-server@5.3.0`、`typescript@6.0.3`、`pi-context-view`、`@firstpick/pi-extension-codex-fast-mode` 和 `@gotgenes/pi-permission-system`。
|
||||||
- `install.sh`:先安装根组合包,再交互检查 Kitty/Solarized Dark、Oh My Zsh/Powerlevel10k/Zsh 插件、CodeGraph、Kotlin LSP、Java 21+ 和 JDT LS;缺失项目只在用户明确选择 `Y` 后安装或配置。Powerlevel10k 默认配置来自仓库内置的 `config/p10k.zsh`(当前 Rainbow/ASCII 单行紧凑主题)。
|
- `install.sh`:先安装根组合包,再交互检查 Kitty/Solarized Dark、Oh My Zsh/Powerlevel10k/Zsh 插件、CodeGraph、Kotlin LSP、Java 21+ 和 JDT LS;缺失项目只在用户明确选择 `Y` 后安装或配置。Powerlevel10k 默认配置来自仓库内置的 `config/p10k.zsh`(当前 Rainbow/ASCII 单行紧凑主题)。
|
||||||
- `update.sh`:先通过 `pi update` 升级根组合包,再只升级当前已安装的终端环境和机器级依赖;未安装项直接跳过。升级前先查询并比较本地与远端版本,只有版本不同时才下载或替换;Powerlevel10k 配置优先从 `pi update` 后的已安装组合包读取,并与 `.zshrc` 受管块一起按内容比较后增量同步。
|
- `update.sh`:先通过 `pi update` 升级根组合包,再只升级当前已安装的终端环境和机器级依赖;未安装项直接跳过。升级前先查询并比较本地与远端版本,只有版本不同时才下载或替换;Powerlevel10k 配置优先从 `pi update` 后的已安装组合包读取,并与 `.zshrc` 受管块一起按内容比较后增量同步。
|
||||||
- `uninstall.sh`:只移除根组合包,不卸载或还原可能被其他项目共享的终端环境、CodeGraph、Kotlin LSP、JDT LS 或 Java。
|
- `uninstall.sh`:只移除根组合包,不卸载或还原可能被其他项目共享的终端环境、CodeGraph、Kotlin LSP、JDT LS 或 Java。
|
||||||
@@ -36,10 +39,11 @@
|
|||||||
- 若以后开启 read 压缩,优先考虑 `readCompaction + smartTruncate`,源码过滤仍独立评估;必须保留精确 `offset/limit` 读取、短文件和行锚点的完整性。
|
- 若以后开启 read 压缩,优先考虑 `readCompaction + smartTruncate`,源码过滤仍独立评估;必须保留精确 `offset/limit` 读取、短文件和行锚点的完整性。
|
||||||
- Context Mode 负责避免批量读取、命令研究和网页原始内容直接撑大上下文;FFF 仍负责精确字面搜索,RTK 仍处理未走 Context Mode 的普通输出。
|
- Context Mode 负责避免批量读取、命令研究和网页原始内容直接撑大上下文;FFF 仍负责精确字面搜索,RTK 仍处理未走 Context Mode 的普通输出。
|
||||||
- Hashline 默认以 2 字符行哈希覆盖内置 `read` / `edit`,`grep` 和 `replaceText` 均关闭;它保证锚定编辑而不负责压缩,大文件上下文节省必须来自先定位/隔离分析、再做最小范围 `offset/limit` 读取。
|
- Hashline 默认以 2 字符行哈希覆盖内置 `read` / `edit`,`grep` 和 `replaceText` 均关闭;它保证锚定编辑而不负责压缩,大文件上下文节省必须来自先定位/隔离分析、再做最小范围 `offset/limit` 读取。
|
||||||
- 工具路由规则不得替换 Pi 默认系统提示词:代码结构、调用关系和待修改 symbol 优先 CodeGraph,定义/引用/类型/诊断优先 LSP;工作区内大文件探索、分析和总结优先 `ctx_execute_file`,日志、构建与不可预测命令输出优先 Context Mode;字面搜索先用 FFF `find` 收敛文件位置,再在收敛路径内用 `grep`/`multi_grep` 获取行号;真正修改前才用小范围 Hashline `read` 获取新鲜锚点并用锚定 `edit`。遇到大量结果、截断或上限时继续缩小 path/glob/pattern,不得靠提高 limit 或倾倒全部结果解决。
|
- 工具路由规则不得替换 Pi 默认系统提示词:代码结构、调用关系和待修改 symbol 优先 CodeGraph,定义/引用/类型/诊断优先 LSP;工作区内大文件探索、分析和总结优先 `ctx_execute_file`,日志、构建与不可预测命令输出优先 Context Mode;字面搜索先用 FFF `find` 收敛文件位置,再在收敛路径内用 `grep`/`multi_grep` 获取行号;真正修改前才用小范围 Hashline `read` 获取新鲜锚点并用锚定 `edit`。在线搜索按场景只先选一个服务:广泛发现、新闻和候选来源用 Tavily,官方/技术/论文和高确定性来源用 Exa,中文、站点限定和日期筛选用 Keenable;只有必要时才跨服务复核。遇到大量结果、截断或上限时继续缩小范围,不得靠提高 limit 或倾倒全部结果解决。
|
||||||
- `.pi-debug/` 是 `/dump-system-prompt` 生成的本地诊断目录,不提交到仓库,也不作为组合包运行时配置源。
|
- `.pi-debug/` 是 `/dump-system-prompt` 生成的本地诊断目录,不提交到仓库,也不作为组合包运行时配置源。
|
||||||
- CodeGraph 扩展只配置 Pi 到外部 `codegraph` 命令的 MCP 连接。根包安装本身不安装 CodeGraph;便捷脚本仅在组合包安装完成且用户明确选择 `Y` 后调用官方安装器。仓库不执行 `codegraph init`,不创建或管理 `.codegraph/`,也不改动索引、更新或遥测设置。
|
- CodeGraph 扩展只配置 Pi 到外部 `codegraph` 命令的 MCP 连接。根包安装本身不安装 CodeGraph;便捷脚本仅在组合包安装完成且用户明确选择 `Y` 后调用官方安装器。仓库不执行 `codegraph init`,不创建或管理 `.codegraph/`,也不改动索引、更新或遥测设置。
|
||||||
- CodeGraph MCP 使用 `keep-alive` 并只直接暴露 `codegraph_explore`;命令缺失、项目未初始化或连接失败时不得阻止其他扩展加载。
|
- CodeGraph MCP 使用 `keep-alive` 并只直接暴露 `codegraph_explore`;命令缺失、项目未初始化或连接失败时不得阻止其他扩展加载。
|
||||||
|
- Exa MCP 使用托管 Streamable HTTP 端点和 `eager` lifecycle;所有在线搜索工具统一采用“来源名 + 原始语义工具名”的命名形式,例如 `tavily_web_search`、`exa_web_search`、`keenable_search`。Exa 高级搜索必须显式约束结果数量和文本长度;Tavily 输出默认控制 `max_results` 且非必要不请求 raw content;Keenable 优先利用中文、站点和日期筛选能力。
|
||||||
- `pi-context-view` 只观察上下文占用,不参与压缩策略。
|
- `pi-context-view` 只观察上下文占用,不参与压缩策略。
|
||||||
- Codex fast mode 只为符合条件的 `openai-codex-responses` 请求设置 priority service tier,由 `/fast-mode` 在会话内控制。
|
- Codex fast mode 只为符合条件的 `openai-codex-responses` 请求设置 priority service tier,由 `/fast-mode` 在会话内控制。
|
||||||
- 权限策略默认允许常规工具,允许 FFF 工具;拒绝 Bash 直搜和敏感凭据路径;Git 非只读操作、包管理、外部目录、文件/系统/网络高风险操作与普通 MCP 调用先由 `pi-permission-system` 判为 `ask`。
|
- 权限策略默认允许常规工具,允许 FFF 工具;拒绝 Bash 直搜和敏感凭据路径;Git 非只读操作、包管理、外部目录、文件/系统/网络高风险操作与普通 MCP 调用先由 `pi-permission-system` 判为 `ask`。
|
||||||
|
|||||||
@@ -5,7 +5,9 @@
|
|||||||
- `@ff-labs/pi-fff@0.10.5`:使用官方 `override` 模式接管 `find`、`grep` 和 `multi_grep`。
|
- `@ff-labs/pi-fff@0.10.5`:使用官方 `override` 模式接管 `find`、`grep` 和 `multi_grep`。
|
||||||
- 本仓库维护的 `pi-rtk-optimizer`:只处理 Bash/read 等输出压缩,默认不改写命令,也不处理搜索工具结果。
|
- 本仓库维护的 `pi-rtk-optimizer`:只处理 Bash/read 等输出压缩,默认不改写命令,也不处理搜索工具结果。
|
||||||
- `context-mode@1.0.169`:通过 Pi adapter 提供 `ctx_*` 工具、上下文隔离和会话连续性。
|
- `context-mode@1.0.169`:通过 Pi adapter 提供 `ctx_*` 工具、上下文隔离和会话连续性。
|
||||||
- `pi-mcp-adapter@2.26.0`:只用于把机器现有的 CodeGraph MCP Server 接入为 `codegraph_explore`。
|
- `@tavily/pi-extension@0.1.2`:提供 Tavily 广泛搜索与页面提取;通过本地 override 暴露为 `tavily_web_search` / `tavily_web_fetch`。
|
||||||
|
- `@keenable/pi-search@0.1.2`:提供紧凑的 `keenable_search` / `keenable_fetch`,并加载其搜索 skill;API key 可选。
|
||||||
|
- `pi-mcp-adapter@2.26.0`:分别用隔离配置接入机器现有的 CodeGraph 和 Exa 托管 MCP。
|
||||||
- `pi-context-view@0.4.2`:查看上下文占用。
|
- `pi-context-view@0.4.2`:查看上下文占用。
|
||||||
- `@firstpick/pi-extension-codex-fast-mode@0.1.1`:为 Codex provider 提供会话级 `/fast-mode`。
|
- `@firstpick/pi-extension-codex-fast-mode@0.1.1`:为 Codex provider 提供会话级 `/fast-mode`。
|
||||||
- `pi-lsp@0.1.7`:为 TypeScript/JavaScript、Kotlin 和 Java 提供 LSP 诊断、跳转与符号工具;TypeScript 后端由组合包内置。
|
- `pi-lsp@0.1.7`:为 TypeScript/JavaScript、Kotlin 和 Java 提供 LSP 诊断、跳转与符号工具;TypeScript 后端由组合包内置。
|
||||||
@@ -115,7 +117,9 @@ CodeGraph 本体不由组合包安装。需要使用 CodeGraph 的机器应自
|
|||||||
|
|
||||||
### 工具与搜索路由
|
### 工具与搜索路由
|
||||||
|
|
||||||
`extensions/tool-routing.ts` 不替换 Pi 默认系统提示词,而是在每轮开始前根据当前激活工具追加简短规则:代码结构、调用关系和待修改 symbol 优先使用 CodeGraph,定义、引用、类型和修改后诊断优先使用 LSP;字面搜索先用 FFF `find` 缩小文件或目录范围,再在已收敛的路径中用 `grep`/`multi_grep` 获取行号。宽泛搜索命中上百或上千结果、发生截断或达到上限时,应继续缩小路径、glob 或 pattern,而不是提高 limit 或输出全部结果。
|
`extensions/tool-routing.ts` 不替换 Pi 默认系统提示词,而是在每轮开始前根据当前激活工具追加简短规则。代码结构、调用关系和待修改 symbol 优先使用 CodeGraph,定义、引用、类型和修改后诊断优先使用 LSP;仓库字面搜索先用 FFF `find` 缩小文件或目录范围,再在已收敛的路径中用 `grep`/`multi_grep` 获取行号。宽泛搜索命中大量结果、发生截断或达到上限时,应继续缩小路径、glob 或 pattern,而不是提高 limit 或输出全部结果。
|
||||||
|
|
||||||
|
在线搜索默认按任务只选一个服务:关键词不确定、新闻/近期话题或需要较多候选来源时优先 Tavily;官方/API 文档、版本信息、论文和高确定性事实确认优先 Exa;中文政策、政府公告、中文网页、站点限定和日期筛选优先 Keenable。只有来源质量或不确定性确实需要时才跨服务复核,避免默认把三套结果全部塞进上下文。
|
||||||
|
|
||||||
日志、测试/构建输出、工作区内大文件的探索/分析/总结以及不可预测的大输出优先交给 Context Mode。只有确实需要精确源码或准备修改时,才用带 `offset/limit` 的 Hashline `read` 读取最小区域并取得 `LINE#HASH` 锚点,随后用锚定 `edit` 修改;成功编辑返回的新锚点可继续复用,只有锚点过期或下一目标区域尚未展示时才重新读取。已有工具结果足够时停止检索,避免对同一问题依次重复调用 CodeGraph、FFF 和 `read`。
|
日志、测试/构建输出、工作区内大文件的探索/分析/总结以及不可预测的大输出优先交给 Context Mode。只有确实需要精确源码或准备修改时,才用带 `offset/limit` 的 Hashline `read` 读取最小区域并取得 `LINE#HASH` 锚点,随后用锚定 `edit` 修改;成功编辑返回的新锚点可继续复用,只有锚点过期或下一目标区域尚未展示时才重新读取。已有工具结果足够时停止检索,避免对同一问题依次重复调用 CodeGraph、FFF 和 `read`。
|
||||||
|
|
||||||
@@ -149,6 +153,14 @@ Context Mode 直接加载 npm 包内置的 Pi adapter 和 skills,不需要额
|
|||||||
FFF 仍负责精确字面搜索;RTK 继续压缩未走 Context Mode 的 Bash、build、test、lint
|
FFF 仍负责精确字面搜索;RTK 继续压缩未走 Context Mode 的 Bash、build、test、lint
|
||||||
和 Git 输出。Context Mode 不替换这两个扩展的现有配置。
|
和 Git 输出。Context Mode 不替换这两个扩展的现有配置。
|
||||||
|
|
||||||
|
### 在线搜索服务
|
||||||
|
|
||||||
|
Tavily 通过 `extensions/tavily-override.ts` 加载官方扩展。override 只改工具身份,不改官方执行逻辑:上游 `web_search` / `web_fetch` 被注册为 `tavily_web_search` / `tavily_web_fetch`,label 和提示词中的内部工具引用也同步带上 Tavily 标识。使用前需设置 `TAVILY_API_KEY`。Tavily 适合广泛发现,但来源可能混杂且输出较长,应从较小的 `max_results` 开始,非必要不返回 raw content。
|
||||||
|
|
||||||
|
Keenable 直接加载 npm 包的扩展和 skill,默认无需 key;设置 `KEENABLE_API_KEY` 可提高速率限制。它适合中文内容、`site` 限定和发布日期筛选。搜索结果保持紧凑,只对选中的 URL 调用 `keenable_fetch`。
|
||||||
|
|
||||||
|
`extensions/exa.ts` 通过已有的 `pi-mcp-adapter` 连接 `https://mcp.exa.ai/mcp`,把 MCP 原始工具映射为统一的来源前缀形式:`exa_web_search`、`exa_web_fetch` 和 `exa_web_search_advanced`。Exa 适合官方文档、技术资料、版本信息和论文;高级搜索必须显式限制结果数量与返回文本长度,避免响应过大。
|
||||||
|
|
||||||
### CodeGraph 只负责连接
|
### CodeGraph 只负责连接
|
||||||
|
|
||||||
`extensions/codegraph.ts` 使用隔离的 `pi-mcp-adapter` 配置启动
|
`extensions/codegraph.ts` 使用隔离的 `pi-mcp-adapter` 配置启动
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
const EXA_TOOL_ALIASES: Record<string, string> = {
|
||||||
|
web_search_exa: "exa_web_search",
|
||||||
|
web_fetch_exa: "exa_web_fetch",
|
||||||
|
web_search_advanced_exa: "exa_web_search_advanced",
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ExaToolDefinition = {
|
||||||
|
name: string;
|
||||||
|
label: string;
|
||||||
|
[key: string]: unknown;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function mapExaToolName(name: string): string {
|
||||||
|
return EXA_TOOL_ALIASES[name] ?? name;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function prefixExaTool<T extends ExaToolDefinition>(definition: T): T {
|
||||||
|
const name = mapExaToolName(definition.name);
|
||||||
|
if (name === definition.name) return definition;
|
||||||
|
|
||||||
|
const displayName = name
|
||||||
|
.slice("exa_".length)
|
||||||
|
.split("_")
|
||||||
|
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
||||||
|
.join(" ");
|
||||||
|
return {
|
||||||
|
...definition,
|
||||||
|
name,
|
||||||
|
label: `Exa ${displayName}`,
|
||||||
|
} as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
type ToolApi = {
|
||||||
|
registerTool: (tool: unknown) => unknown;
|
||||||
|
setActiveTools: (names: string[]) => unknown;
|
||||||
|
unregisterTool?: (name: string) => boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function createExaToolApiOverride<T extends object>(pi: T): T {
|
||||||
|
const api = pi as T & ToolApi;
|
||||||
|
return new Proxy(pi, {
|
||||||
|
get(target, property) {
|
||||||
|
if (property === "registerTool") {
|
||||||
|
return (definition: ExaToolDefinition) => api.registerTool(prefixExaTool(definition));
|
||||||
|
}
|
||||||
|
if (property === "setActiveTools") {
|
||||||
|
return (names: string[]) => api.setActiveTools([...new Set(names.map(mapExaToolName))]);
|
||||||
|
}
|
||||||
|
if (property === "unregisterTool") {
|
||||||
|
return api.unregisterTool ? (name: string) => api.unregisterTool?.(mapExaToolName(name)) : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const value = Reflect.get(target, property, target);
|
||||||
|
return typeof value === "function" ? value.bind(target) : value;
|
||||||
|
},
|
||||||
|
}) as T;
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
||||||
|
import { createMcpAdapter } from "pi-mcp-adapter";
|
||||||
|
import { createExaToolApiOverride } from "./exa-tool-prefix.ts";
|
||||||
|
|
||||||
|
const EXA_TOOLS = ["web_search_exa", "web_fetch_exa", "web_search_advanced_exa"];
|
||||||
|
const EXA_MCP_URL = `https://mcp.exa.ai/mcp?tools=${EXA_TOOLS.join(",")}`;
|
||||||
|
|
||||||
|
const exaAdapter = createMcpAdapter({
|
||||||
|
config: {
|
||||||
|
settings: {
|
||||||
|
disableProxyTool: true,
|
||||||
|
scriptMode: false,
|
||||||
|
},
|
||||||
|
mcpServers: {
|
||||||
|
exa: {
|
||||||
|
url: EXA_MCP_URL,
|
||||||
|
protocolVersion: "auto",
|
||||||
|
lifecycle: "eager",
|
||||||
|
directTools: EXA_TOOLS,
|
||||||
|
includeTools: EXA_TOOLS,
|
||||||
|
toolPrefix: "none",
|
||||||
|
approveTools: false,
|
||||||
|
exposeResources: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/** Connect Pi to Exa while exposing every direct tool as `exa_<original-name>`. */
|
||||||
|
export default function exaExtension(pi: ExtensionAPI): void {
|
||||||
|
exaAdapter(createExaToolApiOverride(pi));
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
||||||
|
import tavilyExtension from "../node_modules/@tavily/pi-extension/index.ts";
|
||||||
|
import { prefixTavilyTool, type TavilyToolDefinition } from "./tavily-tool-prefix.ts";
|
||||||
|
|
||||||
|
|
||||||
|
/** Load Tavily while making the provider explicit in every registered tool name. */
|
||||||
|
export default function tavilyOverrideExtension(pi: ExtensionAPI): void {
|
||||||
|
const prefixedPi = new Proxy(pi, {
|
||||||
|
get(target, property) {
|
||||||
|
if (property === "registerTool") {
|
||||||
|
return (definition: TavilyToolDefinition) =>
|
||||||
|
(target.registerTool as (tool: unknown) => void)(prefixTavilyTool(definition));
|
||||||
|
}
|
||||||
|
|
||||||
|
const value = Reflect.get(target, property, target);
|
||||||
|
return typeof value === "function" ? value.bind(target) : value;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
tavilyExtension(prefixedPi);
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
const TAVILY_TOOL_PREFIX = "tavily_";
|
||||||
|
|
||||||
|
export type TavilyToolDefinition = {
|
||||||
|
name: string;
|
||||||
|
label: string;
|
||||||
|
description: string;
|
||||||
|
promptSnippet?: string;
|
||||||
|
promptGuidelines?: string[];
|
||||||
|
[key: string]: unknown;
|
||||||
|
};
|
||||||
|
|
||||||
|
function rewriteToolReferences(text: string): string {
|
||||||
|
return text.replace(/\bweb_search\b/g, "tavily_web_search").replace(/\bweb_fetch\b/g, "tavily_web_fetch");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function prefixTavilyTool<T extends TavilyToolDefinition>(definition: T): T {
|
||||||
|
return {
|
||||||
|
...definition,
|
||||||
|
name: `${TAVILY_TOOL_PREFIX}${definition.name}`,
|
||||||
|
label: definition.label.startsWith("Tavily ") ? definition.label : `Tavily ${definition.label}`,
|
||||||
|
description: rewriteToolReferences(definition.description),
|
||||||
|
promptSnippet: definition.promptSnippet ? rewriteToolReferences(definition.promptSnippet) : undefined,
|
||||||
|
promptGuidelines: definition.promptGuidelines?.map(rewriteToolReferences),
|
||||||
|
} as T;
|
||||||
|
}
|
||||||
@@ -11,6 +11,10 @@ function hasTool(selectedTools: SelectedTools, name: string): boolean {
|
|||||||
return selectedTools === undefined || selectedTools.includes(name);
|
return selectedTools === undefined || selectedTools.includes(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hasAnyTool(selectedTools: SelectedTools, names: string[]): boolean {
|
||||||
|
return names.some((name) => hasTool(selectedTools, name));
|
||||||
|
}
|
||||||
|
|
||||||
export function buildToolRoutingSection(selectedTools: SelectedTools): string {
|
export function buildToolRoutingSection(selectedTools: SelectedTools): string {
|
||||||
const rules: string[] = [];
|
const rules: string[] = [];
|
||||||
|
|
||||||
@@ -40,6 +44,30 @@ export function buildToolRoutingSection(selectedTools: SelectedTools): string {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const tavilyTools = ["tavily_web_search", "tavily_web_fetch"];
|
||||||
|
const exaTools = ["exa_web_search", "exa_web_fetch", "exa_web_search_advanced"];
|
||||||
|
const keenableTools = ["keenable_search", "keenable_fetch"];
|
||||||
|
if (hasAnyTool(selectedTools, [...tavilyTools, ...exaTools, ...keenableTools])) {
|
||||||
|
rules.push(
|
||||||
|
"- Choose one online search provider based on the task instead of querying Tavily, Exa, and Keenable by default; cross-check with another provider only when source quality or uncertainty warrants it.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (hasAnyTool(selectedTools, tavilyTools)) {
|
||||||
|
rules.push(
|
||||||
|
"- Use Tavily tools for broad discovery when keywords are uncertain, for news/recent/trending topics, or when many candidate sources are useful. Tavily results can mix official pages, blogs, forums, and secondary sources, so keep max_results modest, avoid raw content unless needed, and fetch only selected pages when depth matters.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (hasAnyTool(selectedTools, exaTools)) {
|
||||||
|
rules.push(
|
||||||
|
"- Use Exa tools when source precision matters: official or API documentation, release/version information, academic papers, research material, and high-quality fact confirmation. Use exa_web_fetch to continue reading selected pages; for exa_web_search_advanced, explicitly bound the result count and returned text length to prevent oversized responses.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (hasAnyTool(selectedTools, keenableTools)) {
|
||||||
|
rules.push(
|
||||||
|
"- Use Keenable tools for Chinese-language pages, policies and government notices, site-restricted search, and publication-date filtering. Prefer its compact search results and fetch only selected pages to keep returned content controlled; KEENABLE_API_KEY is optional but raises rate limits.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (hasTool(selectedTools, "ctx_execute") || hasTool(selectedTools, "ctx_execute_file")) {
|
if (hasTool(selectedTools, "ctx_execute") || hasTool(selectedTools, "ctx_execute_file")) {
|
||||||
rules.push(
|
rules.push(
|
||||||
"- Use Context Mode for logs, test/build output, generated data, large-file exploration/analysis/summarization, and any command whose output may be large or unpredictable. Prefer ctx_execute_file over read when deriving an answer from a large file inside the workspace, and print only the derived answer needed for the task.",
|
"- Use Context Mode for logs, test/build output, generated data, large-file exploration/analysis/summarization, and any command whose output may be large or unpredictable. Prefer ctx_execute_file over read when deriving an answer from a large file inside the workspace, and print only the derived answer needed for the task.",
|
||||||
|
|||||||
Generated
+227
@@ -11,7 +11,9 @@
|
|||||||
"@ff-labs/pi-fff": "0.10.5",
|
"@ff-labs/pi-fff": "0.10.5",
|
||||||
"@firstpick/pi-extension-codex-fast-mode": "0.1.1",
|
"@firstpick/pi-extension-codex-fast-mode": "0.1.1",
|
||||||
"@gotgenes/pi-permission-system": "26.2.1",
|
"@gotgenes/pi-permission-system": "26.2.1",
|
||||||
|
"@keenable/pi-search": "0.1.2",
|
||||||
"@ogulcancelik/pi-codex-compaction": "0.1.3",
|
"@ogulcancelik/pi-codex-compaction": "0.1.3",
|
||||||
|
"@tavily/pi-extension": "0.1.2",
|
||||||
"context-mode": "1.0.169",
|
"context-mode": "1.0.169",
|
||||||
"pi-context-view": "0.4.2",
|
"pi-context-view": "0.4.2",
|
||||||
"pi-hashline-edit": "0.8.3",
|
"pi-hashline-edit": "0.8.3",
|
||||||
@@ -334,6 +336,17 @@
|
|||||||
"hono": "^4"
|
"hono": "^4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@keenable/pi-search": {
|
||||||
|
"version": "0.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@keenable/pi-search/-/pi-search-0.1.2.tgz",
|
||||||
|
"integrity": "sha512-3akVIB+0vgaSdsqwFZ46nTHOnjEywgKEkho/l6ixVkJS07T8A8W8gLsg26L526DlaZo+EMW+44EbZASTfKZU6A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@earendil-works/pi-ai": "*",
|
||||||
|
"@earendil-works/pi-coding-agent": "*",
|
||||||
|
"typebox": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@mixmark-io/domino": {
|
"node_modules/@mixmark-io/domino": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@mixmark-io/domino/-/domino-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@mixmark-io/domino/-/domino-2.2.0.tgz",
|
||||||
@@ -690,6 +703,29 @@
|
|||||||
"integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
|
"integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@tavily/core": {
|
||||||
|
"version": "0.7.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tavily/core/-/core-0.7.7.tgz",
|
||||||
|
"integrity": "sha512-6Sc+u4nPEdzLB2D7Ubut/sLI2wR3Wc81P1VCJNTsdFtvgLOpVGGGEYAG9VUCLOzVCeEUT5SzHRIMSnsqJ4Pdow==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^1.7.7",
|
||||||
|
"https-proxy-agent": "^7.0.6",
|
||||||
|
"js-tiktoken": "^1.0.14"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tavily/pi-extension": {
|
||||||
|
"version": "0.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tavily/pi-extension/-/pi-extension-0.1.2.tgz",
|
||||||
|
"integrity": "sha512-12RFWh1ni7rvr+jIHOu1uKpidWeBIVgAMeOvWayUxejab5CQIRchz+83o0f1Fw0wIOH55cziCynO1nOSX2s95Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"@tavily/core": "^0.7.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@earendil-works/pi-ai": "*",
|
||||||
|
"@earendil-works/pi-coding-agent": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@tokenizer/inflate": {
|
"node_modules/@tokenizer/inflate": {
|
||||||
"version": "0.4.1",
|
"version": "0.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.4.1.tgz",
|
||||||
@@ -903,6 +939,15 @@
|
|||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/agent-base": {
|
||||||
|
"version": "7.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
|
||||||
|
"integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 14"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ajv": {
|
"node_modules/ajv": {
|
||||||
"version": "8.20.0",
|
"version": "8.20.0",
|
||||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz",
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz",
|
||||||
@@ -948,6 +993,49 @@
|
|||||||
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
|
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/asynckit": {
|
||||||
|
"version": "0.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||||
|
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/axios": {
|
||||||
|
"version": "1.19.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/axios/-/axios-1.19.0.tgz",
|
||||||
|
"integrity": "sha512-ht/iuYZXEjFxLH/Hkezgd7m6JKlHHXEUSneaDz8uZe1Gj5QZtCnpyDsckvAiEnT89OEbCLmnte4R4sn7P0EKFw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"follow-redirects": "^1.16.0",
|
||||||
|
"form-data": "^4.0.6",
|
||||||
|
"https-proxy-agent": "^5.0.1",
|
||||||
|
"proxy-from-env": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/axios/node_modules/agent-base": {
|
||||||
|
"version": "6.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
|
||||||
|
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"debug": "4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/axios/node_modules/https-proxy-agent": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"agent-base": "6",
|
||||||
|
"debug": "4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/base64-js": {
|
"node_modules/base64-js": {
|
||||||
"version": "1.5.1",
|
"version": "1.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||||
@@ -1122,6 +1210,18 @@
|
|||||||
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
|
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/combined-stream": {
|
||||||
|
"version": "1.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||||
|
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"delayed-stream": "~1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/content-disposition": {
|
"node_modules/content-disposition": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz",
|
||||||
@@ -1312,6 +1412,15 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/delayed-stream": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/depd": {
|
"node_modules/depd": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
||||||
@@ -1407,6 +1516,21 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/es-set-tostringtag": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"es-errors": "^1.3.0",
|
||||||
|
"get-intrinsic": "^1.2.6",
|
||||||
|
"has-tostringtag": "^1.0.2",
|
||||||
|
"hasown": "^2.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/escape-html": {
|
"node_modules/escape-html": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||||
@@ -1624,6 +1748,63 @@
|
|||||||
"url": "https://opencollective.com/express"
|
"url": "https://opencollective.com/express"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/follow-redirects": {
|
||||||
|
"version": "1.16.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz",
|
||||||
|
"integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==",
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "individual",
|
||||||
|
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"debug": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/form-data": {
|
||||||
|
"version": "4.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz",
|
||||||
|
"integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"asynckit": "^0.4.0",
|
||||||
|
"combined-stream": "^1.0.8",
|
||||||
|
"es-set-tostringtag": "^2.1.0",
|
||||||
|
"hasown": "^2.0.4",
|
||||||
|
"mime-types": "^2.1.35"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/form-data/node_modules/mime-db": {
|
||||||
|
"version": "1.52.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||||
|
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/form-data/node_modules/mime-types": {
|
||||||
|
"version": "2.1.35",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||||
|
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"mime-db": "1.52.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/forwarded": {
|
"node_modules/forwarded": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
||||||
@@ -1736,6 +1917,21 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/has-tostringtag": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"has-symbols": "^1.0.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/hasown": {
|
"node_modules/hasown": {
|
||||||
"version": "2.0.4",
|
"version": "2.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz",
|
||||||
@@ -1777,6 +1973,19 @@
|
|||||||
"url": "https://opencollective.com/express"
|
"url": "https://opencollective.com/express"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/https-proxy-agent": {
|
||||||
|
"version": "7.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
|
||||||
|
"integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"agent-base": "^7.1.2",
|
||||||
|
"debug": "4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 14"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/iconv-lite": {
|
"node_modules/iconv-lite": {
|
||||||
"version": "0.7.3",
|
"version": "0.7.3",
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz",
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz",
|
||||||
@@ -1912,6 +2121,15 @@
|
|||||||
"url": "https://github.com/sponsors/panva"
|
"url": "https://github.com/sponsors/panva"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/js-tiktoken": {
|
||||||
|
"version": "1.0.21",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-tiktoken/-/js-tiktoken-1.0.21.tgz",
|
||||||
|
"integrity": "sha512-biOj/6M5qdgx5TKjDnFT1ymSpM5tbd3ylwDtrQvFQSu0Z7bBYko2dF+W/aUkXUPuk6IVpRxk/3Q2sHOzGlS36g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"base64-js": "^1.5.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/json-schema-traverse": {
|
"node_modules/json-schema-traverse": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
||||||
@@ -2324,6 +2542,15 @@
|
|||||||
"node": ">= 0.10"
|
"node": ">= 0.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/proxy-from-env": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/pump": {
|
"node_modules/pump": {
|
||||||
"version": "3.0.4",
|
"version": "3.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz",
|
||||||
|
|||||||
+7
-1
@@ -23,6 +23,9 @@
|
|||||||
"pi": {
|
"pi": {
|
||||||
"extensions": [
|
"extensions": [
|
||||||
"./extensions/fff-override.ts",
|
"./extensions/fff-override.ts",
|
||||||
|
"./extensions/tavily-override.ts",
|
||||||
|
"./node_modules/@keenable/pi-search/src/index.ts",
|
||||||
|
"./extensions/exa.ts",
|
||||||
"./pi-rtk-optimizer/index.ts",
|
"./pi-rtk-optimizer/index.ts",
|
||||||
"./extensions/hashline.ts",
|
"./extensions/hashline.ts",
|
||||||
"./node_modules/context-mode/build/adapters/pi/extension.js",
|
"./node_modules/context-mode/build/adapters/pi/extension.js",
|
||||||
@@ -37,14 +40,17 @@
|
|||||||
"./extensions/tool-routing.ts"
|
"./extensions/tool-routing.ts"
|
||||||
],
|
],
|
||||||
"skills": [
|
"skills": [
|
||||||
"./node_modules/context-mode/skills"
|
"./node_modules/context-mode/skills",
|
||||||
|
"./node_modules/@keenable/pi-search/skills"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ff-labs/pi-fff": "0.10.5",
|
"@ff-labs/pi-fff": "0.10.5",
|
||||||
"@firstpick/pi-extension-codex-fast-mode": "0.1.1",
|
"@firstpick/pi-extension-codex-fast-mode": "0.1.1",
|
||||||
"@gotgenes/pi-permission-system": "26.2.1",
|
"@gotgenes/pi-permission-system": "26.2.1",
|
||||||
|
"@keenable/pi-search": "0.1.2",
|
||||||
"@ogulcancelik/pi-codex-compaction": "0.1.3",
|
"@ogulcancelik/pi-codex-compaction": "0.1.3",
|
||||||
|
"@tavily/pi-extension": "0.1.2",
|
||||||
"context-mode": "1.0.169",
|
"context-mode": "1.0.169",
|
||||||
"pi-context-view": "0.4.2",
|
"pi-context-view": "0.4.2",
|
||||||
"pi-hashline-edit": "0.8.3",
|
"pi-hashline-edit": "0.8.3",
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import assert from "node:assert/strict";
|
||||||
|
import test from "node:test";
|
||||||
|
|
||||||
|
import { createExaToolApiOverride, mapExaToolName, prefixExaTool } from "../extensions/exa-tool-prefix.ts";
|
||||||
|
|
||||||
|
test("Exa tools use a provider prefix followed by the original semantic name", () => {
|
||||||
|
assert.equal(mapExaToolName("web_search_exa"), "exa_web_search");
|
||||||
|
assert.equal(mapExaToolName("web_fetch_exa"), "exa_web_fetch");
|
||||||
|
assert.equal(mapExaToolName("web_search_advanced_exa"), "exa_web_search_advanced");
|
||||||
|
assert.equal(mapExaToolName("unrelated_tool"), "unrelated_tool");
|
||||||
|
|
||||||
|
const tool = prefixExaTool({ name: "web_search_exa", label: "MCP: web_search_exa" });
|
||||||
|
assert.equal(tool.name, "exa_web_search");
|
||||||
|
assert.equal(tool.label, "Exa Web Search");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Exa API override keeps registered and active tool names aligned", () => {
|
||||||
|
const registered: Array<{ name: string; label: string }> = [];
|
||||||
|
let active: string[] = [];
|
||||||
|
const unregistered: string[] = [];
|
||||||
|
const api = createExaToolApiOverride({
|
||||||
|
registerTool(tool: unknown) {
|
||||||
|
registered.push(tool as { name: string; label: string });
|
||||||
|
},
|
||||||
|
setActiveTools(names: string[]) {
|
||||||
|
active = names;
|
||||||
|
},
|
||||||
|
unregisterTool(name: string) {
|
||||||
|
unregistered.push(name);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
api.registerTool({ name: "web_fetch_exa", label: "MCP: web_fetch_exa" });
|
||||||
|
api.setActiveTools(["read", "web_fetch_exa", "web_fetch_exa"]);
|
||||||
|
api.unregisterTool("web_fetch_exa");
|
||||||
|
|
||||||
|
assert.equal(registered[0]?.name, "exa_web_fetch");
|
||||||
|
assert.equal(registered[0]?.label, "Exa Web Fetch");
|
||||||
|
assert.deepEqual(active, ["read", "exa_web_fetch"]);
|
||||||
|
assert.deepEqual(unregistered, ["exa_web_fetch"]);
|
||||||
|
});
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import assert from "node:assert/strict";
|
||||||
|
import test from "node:test";
|
||||||
|
|
||||||
|
import { prefixTavilyTool } from "../extensions/tavily-tool-prefix.ts";
|
||||||
|
|
||||||
|
test("Tavily override prefixes tool identity and internal tool references", () => {
|
||||||
|
const tool = prefixTavilyTool({
|
||||||
|
name: "web_search",
|
||||||
|
label: "Web Search",
|
||||||
|
description: "Use web_search, then web_fetch with Tavily.",
|
||||||
|
promptSnippet: "Call web_search.",
|
||||||
|
promptGuidelines: ["Use web_fetch after web_search."],
|
||||||
|
parameters: {} as never,
|
||||||
|
execute: async () => ({ content: [{ type: "text" as const, text: "ok" }] }),
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(tool.name, "tavily_web_search");
|
||||||
|
assert.equal(tool.label, "Tavily Web Search");
|
||||||
|
assert.equal(tool.description, "Use tavily_web_search, then tavily_web_fetch with Tavily.");
|
||||||
|
assert.equal(tool.promptSnippet, "Call tavily_web_search.");
|
||||||
|
assert.deepEqual(tool.promptGuidelines, ["Use tavily_web_fetch after tavily_web_search."]);
|
||||||
|
});
|
||||||
@@ -44,6 +44,23 @@ test("LSP routing covers navigation and post-edit diagnostics", () => {
|
|||||||
assert.match(section, /post-edit diagnostics/);
|
assert.match(section, /post-edit diagnostics/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("online search routing selects providers by task", () => {
|
||||||
|
const tavily = buildToolRoutingSection(["tavily_web_search", "tavily_web_fetch"]);
|
||||||
|
assert.match(tavily, /broad discovery when keywords are uncertain/);
|
||||||
|
assert.match(tavily, /news\/recent\/trending topics/);
|
||||||
|
assert.doesNotMatch(tavily, /official or API documentation/);
|
||||||
|
|
||||||
|
const exa = buildToolRoutingSection(["exa_web_search", "exa_web_fetch", "exa_web_search_advanced"]);
|
||||||
|
assert.match(exa, /official or API documentation/);
|
||||||
|
assert.match(exa, /explicitly bound the result count and returned text length/);
|
||||||
|
assert.doesNotMatch(exa, /Chinese-language pages/);
|
||||||
|
|
||||||
|
const keenable = buildToolRoutingSection(["keenable_search", "keenable_fetch"]);
|
||||||
|
assert.match(keenable, /Chinese-language pages, policies and government notices/);
|
||||||
|
assert.match(keenable, /site-restricted search/);
|
||||||
|
assert.match(keenable, /publication-date filtering/);
|
||||||
|
});
|
||||||
|
|
||||||
test("routing includes only guidance for active optional tools", () => {
|
test("routing includes only guidance for active optional tools", () => {
|
||||||
const section = buildToolRoutingSection(["read"]);
|
const section = buildToolRoutingSection(["read"]);
|
||||||
|
|
||||||
@@ -52,6 +69,8 @@ test("routing includes only guidance for active optional tools", () => {
|
|||||||
assert.doesNotMatch(section, /Use Context Mode/);
|
assert.doesNotMatch(section, /Use Context Mode/);
|
||||||
assert.doesNotMatch(section, /ctx_execute_file is confined/);
|
assert.doesNotMatch(section, /ctx_execute_file is confined/);
|
||||||
assert.doesNotMatch(section, /fresh LINE#HASH anchors/);
|
assert.doesNotMatch(section, /fresh LINE#HASH anchors/);
|
||||||
|
assert.doesNotMatch(section, /Choose one online search provider/);
|
||||||
|
assert.doesNotMatch(section, /broad discovery when keywords are uncertain/);
|
||||||
assert.match(section, /Use read only when exact source is needed/);
|
assert.match(section, /Use read only when exact source is needed/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user