@oh-my-pi/pi-coding-agent 17.1.1 → 17.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +31 -0
- package/dist/cli.js +6216 -3984
- package/dist/types/cli/bench-cli.d.ts +1 -0
- package/dist/types/config/model-resolver.d.ts +6 -3
- package/dist/types/config/settings-schema.d.ts +4 -0
- package/dist/types/launch/broker-list-order.test.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +2 -6
- package/dist/types/modes/types.d.ts +8 -5
- package/dist/types/modes/utils/ui-helpers.d.ts +1 -6
- package/dist/types/task/executor.d.ts +3 -1
- package/dist/types/task/structured-subagent.d.ts +3 -0
- package/dist/types/task/types.d.ts +11 -11
- package/dist/types/thinking.d.ts +13 -0
- package/dist/types/tools/eval-format/index.d.ts +7 -0
- package/dist/types/tools/eval-format/javascript.d.ts +2 -0
- package/dist/types/tools/eval-format/julia.d.ts +2 -0
- package/dist/types/tools/eval-format/python.d.ts +2 -0
- package/dist/types/tools/eval-format/ruby.d.ts +2 -0
- package/dist/types/tools/resolve.d.ts +7 -0
- package/dist/types/tools/todo.d.ts +4 -1
- package/dist/types/web/search/providers/base.d.ts +16 -0
- package/dist/types/web/search/providers/brave.d.ts +2 -0
- package/dist/types/web/search/providers/firecrawl.d.ts +2 -0
- package/dist/types/web/search/providers/gemini.d.ts +3 -0
- package/dist/types/web/search/providers/jina.d.ts +2 -0
- package/dist/types/web/search/providers/kagi.d.ts +2 -0
- package/dist/types/web/search/providers/kimi.d.ts +2 -0
- package/dist/types/web/search/providers/parallel.d.ts +2 -0
- package/dist/types/web/search/providers/perplexity.d.ts +3 -0
- package/dist/types/web/search/providers/searxng.d.ts +10 -0
- package/dist/types/web/search/providers/tavily.d.ts +8 -0
- package/dist/types/web/search/query.d.ts +190 -0
- package/package.json +12 -12
- package/src/cli/bench-cli.ts +12 -1
- package/src/cli/web-search-cli.ts +7 -0
- package/src/config/model-resolver.ts +17 -6
- package/src/config/settings-schema.ts +5 -0
- package/src/debug/raw-sse-buffer.ts +157 -30
- package/src/export/share.ts +4 -3
- package/src/launch/broker-list-order.test.ts +89 -0
- package/src/launch/broker.ts +49 -8
- package/src/modes/controllers/input-controller.ts +8 -8
- package/src/modes/interactive-mode.ts +60 -5
- package/src/modes/types.ts +9 -4
- package/src/modes/utils/ui-helpers.ts +7 -8
- package/src/prompts/system/resolve-device-reminder.md +1 -1
- package/src/prompts/system/workflow-notice.md +1 -1
- package/src/prompts/tools/ast-edit.md +1 -0
- package/src/prompts/tools/bash.md +1 -0
- package/src/prompts/tools/eval.md +2 -2
- package/src/prompts/tools/task.md +5 -2
- package/src/prompts/tools/web-search.md +2 -0
- package/src/sdk.ts +8 -4
- package/src/session/agent-session.ts +14 -0
- package/src/session/queued-messages.ts +7 -1
- package/src/task/executor.ts +17 -9
- package/src/task/index.ts +14 -34
- package/src/task/structured-subagent.ts +4 -0
- package/src/task/types.ts +15 -13
- package/src/thinking.ts +27 -0
- package/src/tools/ast-edit.ts +4 -1
- package/src/tools/bash.ts +13 -0
- package/src/tools/browser/render.ts +2 -1
- package/src/tools/eval-format/index.ts +24 -0
- package/src/tools/eval-format/javascript.ts +952 -0
- package/src/tools/eval-format/julia.ts +446 -0
- package/src/tools/eval-format/python.ts +544 -0
- package/src/tools/eval-format/ruby.ts +380 -0
- package/src/tools/eval-render.ts +12 -6
- package/src/tools/resolve.ts +10 -1
- package/src/tools/todo.ts +58 -6
- package/src/web/search/index.ts +28 -5
- package/src/web/search/providers/anthropic.ts +62 -4
- package/src/web/search/providers/base.ts +16 -0
- package/src/web/search/providers/brave.ts +30 -3
- package/src/web/search/providers/codex.ts +14 -1
- package/src/web/search/providers/duckduckgo.ts +23 -1
- package/src/web/search/providers/ecosia.ts +5 -1
- package/src/web/search/providers/exa.ts +28 -1
- package/src/web/search/providers/firecrawl.ts +37 -3
- package/src/web/search/providers/gemini.ts +12 -2
- package/src/web/search/providers/google.ts +2 -1
- package/src/web/search/providers/jina.ts +31 -8
- package/src/web/search/providers/kagi.ts +10 -1
- package/src/web/search/providers/kimi.ts +16 -1
- package/src/web/search/providers/mojeek.ts +14 -1
- package/src/web/search/providers/parallel.ts +48 -2
- package/src/web/search/providers/perplexity.ts +94 -6
- package/src/web/search/providers/searxng.ts +145 -9
- package/src/web/search/providers/startpage.ts +8 -2
- package/src/web/search/providers/synthetic.ts +7 -1
- package/src/web/search/providers/tavily.ts +52 -3
- package/src/web/search/providers/tinyfish.ts +6 -1
- package/src/web/search/providers/xai.ts +49 -2
- package/src/web/search/providers/zai.ts +19 -1
- package/src/web/search/query.ts +850 -0
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import { type ApiKey, type AuthStorage, type FetchImpl, getEnvApiKey, withAuth } from "@oh-my-pi/pi-ai";
|
|
8
8
|
import type { SearchResponse, SearchSource } from "../../../web/search/types";
|
|
9
9
|
import { SearchProviderError } from "../../../web/search/types";
|
|
10
|
+
import { formatQuery, parseSearchQuery } from "../query";
|
|
10
11
|
import { clampNumResults, dateToAgeSeconds } from "../utils";
|
|
11
12
|
import type { SearchParams } from "./base";
|
|
12
13
|
import { SearchProvider } from "./base";
|
|
@@ -20,6 +21,14 @@ export interface TavilySearchParams {
|
|
|
20
21
|
query: string;
|
|
21
22
|
num_results?: number;
|
|
22
23
|
recency?: "day" | "week" | "month" | "year";
|
|
24
|
+
/** `site:` hosts mapped to Tavily's `include_domains`. */
|
|
25
|
+
include_domains?: string[];
|
|
26
|
+
/** `-site:` hosts mapped to Tavily's `exclude_domains`. */
|
|
27
|
+
exclude_domains?: string[];
|
|
28
|
+
/** `after:` inclusive lower bound, ISO `YYYY-MM-DD`, mapped to `start_date`. */
|
|
29
|
+
start_date?: string;
|
|
30
|
+
/** `before:` upper bound, ISO `YYYY-MM-DD`, mapped to `end_date`. */
|
|
31
|
+
end_date?: string;
|
|
23
32
|
signal?: AbortSignal;
|
|
24
33
|
fetch?: FetchImpl;
|
|
25
34
|
}
|
|
@@ -83,7 +92,21 @@ export function buildRequestBody(params: TavilySearchParams): Record<string, unk
|
|
|
83
92
|
include_answer: "advanced",
|
|
84
93
|
include_raw_content: false,
|
|
85
94
|
};
|
|
86
|
-
if (params.
|
|
95
|
+
if (params.include_domains?.length) {
|
|
96
|
+
body.include_domains = params.include_domains;
|
|
97
|
+
}
|
|
98
|
+
if (params.exclude_domains?.length) {
|
|
99
|
+
body.exclude_domains = params.exclude_domains;
|
|
100
|
+
}
|
|
101
|
+
if (params.start_date) {
|
|
102
|
+
body.start_date = params.start_date;
|
|
103
|
+
}
|
|
104
|
+
if (params.end_date) {
|
|
105
|
+
body.end_date = params.end_date;
|
|
106
|
+
}
|
|
107
|
+
// Explicit before:/after: bounds take precedence over the relative recency
|
|
108
|
+
// window; sending both would over-restrict.
|
|
109
|
+
if (params.recency && !params.start_date && !params.end_date) {
|
|
87
110
|
body.time_range = params.recency;
|
|
88
111
|
}
|
|
89
112
|
return body;
|
|
@@ -148,8 +171,19 @@ function hasRenderableResponse(response: SearchResponse): boolean {
|
|
|
148
171
|
return response.sources.length > 0;
|
|
149
172
|
}
|
|
150
173
|
|
|
174
|
+
/** Bare hosts from `site:` values (path parts are enforced by the central lenient filter). */
|
|
175
|
+
function siteHosts(sites: readonly string[]): string[] {
|
|
176
|
+
const hosts = new Set<string>();
|
|
177
|
+
for (const site of sites) {
|
|
178
|
+
const host = site.split("/", 1)[0];
|
|
179
|
+
if (host) hosts.add(host);
|
|
180
|
+
}
|
|
181
|
+
return [...hosts];
|
|
182
|
+
}
|
|
183
|
+
|
|
151
184
|
/** Execute Tavily web search. */
|
|
152
185
|
export async function searchTavily(params: SearchParams): Promise<SearchResponse> {
|
|
186
|
+
const parsed = params.parsedQuery ?? parseSearchQuery(params.query);
|
|
153
187
|
const tavilyParams: TavilySearchParams = {
|
|
154
188
|
query: params.query,
|
|
155
189
|
num_results: params.numSearchResults ?? params.limit,
|
|
@@ -157,6 +191,16 @@ export async function searchTavily(params: SearchParams): Promise<SearchResponse
|
|
|
157
191
|
signal: params.signal,
|
|
158
192
|
fetch: params.fetch,
|
|
159
193
|
};
|
|
194
|
+
if (parsed.hasDirectives) {
|
|
195
|
+
// Tavily prefers clean natural text; re-emit only phrases and -exclusions.
|
|
196
|
+
tavilyParams.query = formatQuery(parsed, { phrases: true, negation: true });
|
|
197
|
+
const include = siteHosts(parsed.sites);
|
|
198
|
+
const exclude = siteHosts(parsed.excludedSites);
|
|
199
|
+
if (include.length > 0) tavilyParams.include_domains = include;
|
|
200
|
+
if (exclude.length > 0) tavilyParams.exclude_domains = exclude;
|
|
201
|
+
if (parsed.after) tavilyParams.start_date = parsed.after;
|
|
202
|
+
if (parsed.before) tavilyParams.end_date = parsed.before;
|
|
203
|
+
}
|
|
160
204
|
const keyOrResolver: ApiKey = params.authStorage.resolver("tavily", {
|
|
161
205
|
sessionId: params.sessionId,
|
|
162
206
|
});
|
|
@@ -171,11 +215,16 @@ export async function searchTavily(params: SearchParams): Promise<SearchResponse
|
|
|
171
215
|
withAuth(keyOrResolver, key => callTavilySearch(key, searchParams), authOptions);
|
|
172
216
|
|
|
173
217
|
const response = toSearchResponse(await callWithAuth(tavilyParams), numResults);
|
|
174
|
-
|
|
218
|
+
const hasTimeFilter = Boolean(tavilyParams.recency || tavilyParams.start_date || tavilyParams.end_date);
|
|
219
|
+
if (!hasTimeFilter || hasRenderableResponse(response)) {
|
|
175
220
|
return response;
|
|
176
221
|
}
|
|
177
222
|
|
|
178
|
-
|
|
223
|
+
// Time filters commonly zero out results; retry once without them.
|
|
224
|
+
return toSearchResponse(
|
|
225
|
+
await callWithAuth({ ...tavilyParams, recency: undefined, start_date: undefined, end_date: undefined }),
|
|
226
|
+
numResults,
|
|
227
|
+
);
|
|
179
228
|
}
|
|
180
229
|
|
|
181
230
|
/** Search provider for Tavily web search. */
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import { type ApiKey, type AuthStorage, type FetchImpl, getEnvApiKey, withAuth } from "@oh-my-pi/pi-ai";
|
|
8
8
|
import type { SearchResponse, SearchSource } from "../../../web/search/types";
|
|
9
9
|
import { SearchProviderError } from "../../../web/search/types";
|
|
10
|
+
import { formatQuery, parseSearchQuery, type QuerySyntax } from "../query";
|
|
10
11
|
import { clampNumResults } from "../utils";
|
|
11
12
|
import type { SearchParams } from "./base";
|
|
12
13
|
import { SearchProvider } from "./base";
|
|
@@ -17,6 +18,9 @@ const DEFAULT_NUM_RESULTS = 10;
|
|
|
17
18
|
const MAX_NUM_RESULTS = 20;
|
|
18
19
|
const MAX_PAGE = 10;
|
|
19
20
|
|
|
21
|
+
/** TinyFish is SERP-backed: common Google-style operators pass through. */
|
|
22
|
+
const TINYFISH_QUERY_SYNTAX: QuerySyntax = { phrases: true, negation: true, site: true, filetype: true };
|
|
23
|
+
|
|
20
24
|
const RECENCY_MINUTES: Record<NonNullable<SearchParams["recency"]>, number> = {
|
|
21
25
|
day: 1440,
|
|
22
26
|
week: 10080,
|
|
@@ -107,8 +111,9 @@ function appendTinyFishSources(sources: SearchSource[], results: readonly TinyFi
|
|
|
107
111
|
export async function searchTinyFish(params: SearchParams): Promise<SearchResponse> {
|
|
108
112
|
const numResults = clampNumResults(params.numSearchResults ?? params.limit, DEFAULT_NUM_RESULTS, MAX_NUM_RESULTS);
|
|
109
113
|
const pageSize = Math.min(numResults, DEFAULT_NUM_RESULTS);
|
|
114
|
+
const parsed = params.parsedQuery ?? parseSearchQuery(params.query);
|
|
110
115
|
const tinyFishParams: TinyFishSearchParams = {
|
|
111
|
-
query: params.query,
|
|
116
|
+
query: parsed.hasDirectives ? formatQuery(parsed, TINYFISH_QUERY_SYNTAX) : params.query,
|
|
112
117
|
num_results: pageSize,
|
|
113
118
|
recency: params.recency,
|
|
114
119
|
signal: params.signal,
|
|
@@ -3,6 +3,7 @@ import { $env } from "@oh-my-pi/pi-utils";
|
|
|
3
3
|
import { resolveXAIHttpTransport, type XAIHttpProvider, type XAIHttpTransport } from "../../../lib/xai-http";
|
|
4
4
|
import type { SearchCitation, SearchResponse, SearchSource, SearchUsage } from "../../../web/search/types";
|
|
5
5
|
import { SearchProviderError } from "../../../web/search/types";
|
|
6
|
+
import { formatQuery, parseSearchQuery, type QuerySyntax } from "../query";
|
|
6
7
|
import { clampNumResults } from "../utils";
|
|
7
8
|
import type { SearchParams } from "./base";
|
|
8
9
|
import { SearchProvider } from "./base";
|
|
@@ -57,14 +58,60 @@ interface XAIResponsesResponse {
|
|
|
57
58
|
usage?: XAIResponsesUsage | null;
|
|
58
59
|
}
|
|
59
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Query syntax re-emitted for the Grok search agent. `site:`/`-site:` are
|
|
63
|
+
* stripped because hosts map natively onto the web_search domain filters;
|
|
64
|
+
* `before:`/`after:` stay in the query text — the Responses web_search tool
|
|
65
|
+
* has no date parameters (`from_date`/`to_date` exist only on `x_search` and
|
|
66
|
+
* the deprecated Live Search `search_parameters`, which now returns 410) and
|
|
67
|
+
* the agent honors the tokens as natural-language hints.
|
|
68
|
+
*/
|
|
69
|
+
const XAI_QUERY_SYNTAX: QuerySyntax = {
|
|
70
|
+
phrases: true,
|
|
71
|
+
negation: true,
|
|
72
|
+
or: true,
|
|
73
|
+
inUrl: true,
|
|
74
|
+
inTitle: true,
|
|
75
|
+
filetype: true,
|
|
76
|
+
dateRange: true,
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/** xAI web_search accepts at most 5 allowed or excluded domains per request. */
|
|
80
|
+
const MAX_DOMAIN_FILTERS = 5;
|
|
81
|
+
|
|
82
|
+
/** Bare hosts of `site:` values (`github.com/anthropics` → `github.com`), deduped, capped at 5; path parts are enforced by the central constraint filter. */
|
|
83
|
+
function domainFilterList(sites: readonly string[]): string[] {
|
|
84
|
+
const hosts = new Set<string>();
|
|
85
|
+
for (const site of sites) {
|
|
86
|
+
const slash = site.indexOf("/");
|
|
87
|
+
hosts.add(slash === -1 ? site : site.slice(0, slash));
|
|
88
|
+
if (hosts.size === MAX_DOMAIN_FILTERS) break;
|
|
89
|
+
}
|
|
90
|
+
return [...hosts];
|
|
91
|
+
}
|
|
92
|
+
|
|
60
93
|
function buildRequestBody(params: SearchParams): Record<string, unknown> {
|
|
94
|
+
const parsed = params.parsedQuery ?? parseSearchQuery(params.query);
|
|
95
|
+
const webSearchTool: Record<string, unknown> = { type: "web_search" };
|
|
96
|
+
let query = params.query;
|
|
97
|
+
if (parsed.hasDirectives) {
|
|
98
|
+
query = formatQuery(parsed, XAI_QUERY_SYNTAX);
|
|
99
|
+
// allowed_domains and excluded_domains are mutually exclusive per
|
|
100
|
+
// request; prefer the allow list, the central filter enforces exclusions.
|
|
101
|
+
if (parsed.sites.length > 0) {
|
|
102
|
+
webSearchTool.filters = { allowed_domains: domainFilterList(parsed.sites) };
|
|
103
|
+
} else if (parsed.excludedSites.length > 0) {
|
|
104
|
+
webSearchTool.filters = { excluded_domains: domainFilterList(parsed.excludedSites) };
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
61
108
|
const body: Record<string, unknown> = {
|
|
62
109
|
model: XAI_WEB_SEARCH_MODEL,
|
|
63
110
|
input: [
|
|
64
111
|
{ role: "system", content: params.systemPrompt },
|
|
65
|
-
{ role: "user", content:
|
|
112
|
+
{ role: "user", content: query },
|
|
66
113
|
],
|
|
67
|
-
tools: [
|
|
114
|
+
tools: [webSearchTool],
|
|
68
115
|
reasoning: { effort: XAI_WEB_SEARCH_REASONING_EFFORT },
|
|
69
116
|
};
|
|
70
117
|
|
|
@@ -8,6 +8,7 @@ import { type ApiKey, type AuthStorage, type FetchImpl, getEnvApiKey, withAuth }
|
|
|
8
8
|
import { isRecord } from "@oh-my-pi/pi-utils";
|
|
9
9
|
import type { SearchResponse, SearchSource } from "../../../web/search/types";
|
|
10
10
|
import { SearchProviderError } from "../../../web/search/types";
|
|
11
|
+
import { formatQuery, parseSearchQuery, type QuerySyntax } from "../query";
|
|
11
12
|
import { dateToAgeSeconds } from "../utils";
|
|
12
13
|
import type { SearchParams } from "./base";
|
|
13
14
|
import { SearchProvider } from "./base";
|
|
@@ -17,6 +18,22 @@ const ZAI_MCP_URL = "https://api.z.ai/api/mcp/web_search_prime/mcp";
|
|
|
17
18
|
const ZAI_TOOL_NAME = "web_search_prime";
|
|
18
19
|
const DEFAULT_NUM_RESULTS = 10;
|
|
19
20
|
|
|
21
|
+
/**
|
|
22
|
+
* webSearchPrime exposes no native filter args (the Web Search API's
|
|
23
|
+
* `search_domain_filter`/`search_recency_filter` are scoped to the
|
|
24
|
+
* `search_pro_jina` engine, not `search-prime`), but its Bing-flavored
|
|
25
|
+
* backend parses the common inline operators. Dates and language are left
|
|
26
|
+
* to the central lenient post-filter.
|
|
27
|
+
*/
|
|
28
|
+
const ZAI_QUERY_SYNTAX: QuerySyntax = {
|
|
29
|
+
phrases: true,
|
|
30
|
+
negation: true,
|
|
31
|
+
site: true,
|
|
32
|
+
inTitle: true,
|
|
33
|
+
inUrl: true,
|
|
34
|
+
filetype: true,
|
|
35
|
+
};
|
|
36
|
+
|
|
20
37
|
export interface ZaiSearchParams {
|
|
21
38
|
query: string;
|
|
22
39
|
num_results?: number;
|
|
@@ -407,8 +424,9 @@ export class ZaiProvider extends SearchProvider {
|
|
|
407
424
|
|
|
408
425
|
search(params: SearchParams): Promise<SearchResponse> {
|
|
409
426
|
const { fetch: fetchOverride } = params as ZaiProviderSearchParams;
|
|
427
|
+
const parsed = params.parsedQuery ?? parseSearchQuery(params.query);
|
|
410
428
|
return searchZai({
|
|
411
|
-
query: params.query,
|
|
429
|
+
query: parsed.hasDirectives ? formatQuery(parsed, ZAI_QUERY_SYNTAX) : params.query,
|
|
412
430
|
num_results: params.numSearchResults ?? params.limit,
|
|
413
431
|
signal: params.signal,
|
|
414
432
|
authStorage: params.authStorage,
|