@oh-my-pi/pi-coding-agent 17.2.1 → 17.2.3
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 +61 -0
- package/dist/{CHANGELOG-dj46zzrm.md → CHANGELOG-c5hpqt9r.md} +61 -0
- package/dist/cli.js +3856 -3850
- package/dist/types/config/keybindings.d.ts +6 -1
- package/dist/types/config/settings-schema.d.ts +16 -5
- package/dist/types/edit/hashline/block-resolver.d.ts +5 -5
- package/dist/types/edit/hashline/diff.d.ts +4 -4
- package/dist/types/eval/executor-base.d.ts +27 -0
- package/dist/types/eval/js/context-manager.d.ts +7 -0
- package/dist/types/eval/js/executor.d.ts +2 -0
- package/dist/types/extensibility/extensions/runner.d.ts +64 -5
- package/dist/types/extensibility/extensions/types.d.ts +15 -0
- package/dist/types/extensibility/extensions/wrapper.d.ts +1 -1
- package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +1 -1
- package/dist/types/live/visualizer.d.ts +3 -1
- package/dist/types/modes/acp/acp-agent.d.ts +3 -3
- package/dist/types/modes/components/ask-dialog.d.ts +0 -1
- package/dist/types/modes/components/keybinding-hints.d.ts +1 -1
- package/dist/types/modes/controllers/mcp-command-controller.d.ts +13 -0
- package/dist/types/modes/interactive-mode.d.ts +1 -0
- package/dist/types/modes/theme/theme.d.ts +1 -1
- package/dist/types/modes/types.d.ts +2 -0
- package/dist/types/secrets/index.d.ts +22 -0
- package/dist/types/secrets/obfuscator.d.ts +1 -1
- package/dist/types/session/agent-session-types.d.ts +4 -0
- package/dist/types/session/agent-session.d.ts +9 -0
- package/dist/types/session/codex-auto-reset.d.ts +182 -64
- package/dist/types/session/session-storage.d.ts +5 -2
- package/dist/types/session/session-tools.d.ts +2 -1
- package/dist/types/session/turn-recovery.d.ts +7 -1
- package/dist/types/system-prompt.d.ts +8 -0
- package/dist/types/task/executor.d.ts +27 -0
- package/dist/types/task/persisted-revive.d.ts +7 -0
- package/dist/types/tools/bash-interceptor.d.ts +1 -1
- package/dist/types/tools/browser/attach.d.ts +9 -1
- package/dist/types/tools/browser/launch.d.ts +50 -1
- package/dist/types/tools/browser/registry.d.ts +7 -0
- package/dist/types/tools/browser/shared-daemon.d.ts +26 -0
- package/dist/types/tools/browser/tab-protocol.d.ts +8 -0
- package/dist/types/tools/browser/tab-supervisor.d.ts +1 -0
- package/dist/types/tools/browser/tab-worker.d.ts +3 -1
- package/dist/types/tools/shell-tokenize.d.ts +11 -0
- package/dist/types/utils/image-loading.d.ts +2 -0
- package/dist/types/web/search/providers/duckduckgo.d.ts +9 -0
- package/dist/types/web/search/types.d.ts +2 -2
- package/package.json +12 -12
- package/src/commit/agentic/index.ts +2 -1
- package/src/commit/pipeline.ts +3 -2
- package/src/config/keybindings.ts +6 -1
- package/src/config/settings-schema.ts +16 -3
- package/src/edit/hashline/block-resolver.ts +5 -5
- package/src/edit/hashline/diff.ts +22 -15
- package/src/edit/hashline/execute.ts +14 -13
- package/src/edit/renderer.ts +3 -3
- package/src/eval/executor-base.ts +39 -0
- package/src/eval/jl/executor.ts +13 -2
- package/src/eval/js/context-manager.ts +97 -14
- package/src/eval/js/executor.ts +3 -0
- package/src/eval/js/index.ts +1 -0
- package/src/eval/py/executor.ts +55 -15
- package/src/eval/rb/executor.ts +8 -1
- package/src/extensibility/extensions/runner.ts +121 -4
- package/src/extensibility/extensions/types.ts +15 -0
- package/src/extensibility/extensions/wrapper.ts +19 -2
- package/src/extensibility/legacy-pi-coding-agent-shim.ts +5 -5
- package/src/launch/broker.ts +59 -41
- package/src/live/visualizer.ts +8 -1
- package/src/lsp/index.ts +51 -11
- package/src/main.ts +3 -0
- package/src/modes/acp/acp-agent.ts +16 -5
- package/src/modes/components/ask-dialog.ts +65 -23
- package/src/modes/components/assistant-message.ts +5 -10
- package/src/modes/components/custom-editor.ts +1 -1
- package/src/modes/components/keybinding-hints.ts +4 -13
- package/src/modes/components/status-line/segments.ts +4 -5
- package/src/modes/components/tool-execution.ts +4 -5
- package/src/modes/controllers/input-controller.ts +18 -40
- package/src/modes/controllers/live-command-controller.ts +1 -0
- package/src/modes/controllers/mcp-command-controller.ts +28 -11
- package/src/modes/interactive-mode.ts +59 -2
- package/src/modes/theme/theme.ts +13 -6
- package/src/modes/types.ts +2 -0
- package/src/modes/utils/hotkeys-markdown.ts +1 -0
- package/src/prompts/system/plan-mode-active.md +5 -5
- package/src/sdk.ts +47 -16
- package/src/secrets/index.ts +83 -3
- package/src/secrets/obfuscator.ts +70 -15
- package/src/session/agent-session-types.ts +7 -1
- package/src/session/agent-session.ts +394 -113
- package/src/session/codex-auto-reset.ts +594 -123
- package/src/session/eval-runner.ts +2 -0
- package/src/session/messages.ts +1 -1
- package/src/session/session-maintenance.ts +50 -3
- package/src/session/session-manager.ts +4 -3
- package/src/session/session-stats.ts +14 -3
- package/src/session/session-storage.ts +47 -15
- package/src/session/session-tools.ts +30 -2
- package/src/session/turn-recovery.ts +15 -2
- package/src/slash-commands/builtin-registry.ts +21 -15
- package/src/slash-commands/helpers/reset-usage.ts +2 -0
- package/src/system-prompt.ts +13 -1
- package/src/task/executor.ts +168 -15
- package/src/task/persisted-revive.ts +26 -1
- package/src/telemetry-export.ts +8 -4
- package/src/tools/bash-interceptor.ts +85 -8
- package/src/tools/bash.ts +12 -6
- package/src/tools/browser/attach.ts +35 -10
- package/src/tools/browser/launch.ts +110 -16
- package/src/tools/browser/registry.ts +140 -31
- package/src/tools/browser/shared-daemon.ts +190 -0
- package/src/tools/browser/tab-protocol.ts +8 -0
- package/src/tools/browser/tab-supervisor.ts +31 -4
- package/src/tools/browser/tab-worker.ts +32 -2
- package/src/tools/browser.ts +1 -1
- package/src/tools/output-meta.ts +16 -1
- package/src/tools/shell-tokenize.ts +101 -0
- package/src/utils/image-loading.ts +8 -3
- package/src/utils/shell-snapshot.ts +31 -21
- package/src/web/search/providers/codex.ts +31 -6
- package/src/web/search/providers/duckduckgo.ts +183 -15
- package/src/web/search/types.ts +6 -1
|
@@ -2,8 +2,8 @@ import type { AuthStorage } from "@oh-my-pi/pi-ai";
|
|
|
2
2
|
import type { SearchResponse, SearchSource } from "../../../web/search/types";
|
|
3
3
|
import { SearchProviderError } from "../../../web/search/types";
|
|
4
4
|
import type { QuerySyntax } from "../query";
|
|
5
|
-
import { formatScraperQuery } from "../query";
|
|
6
|
-
import { clampNumResults } from "../utils";
|
|
5
|
+
import { formatScraperQuery, parseSearchQuery } from "../query";
|
|
6
|
+
import { clampNumResults, dateToAgeSeconds } from "../utils";
|
|
7
7
|
import type { SearchParams } from "./base";
|
|
8
8
|
import { SearchProvider } from "./base";
|
|
9
9
|
import { browserFetch } from "./browser-page";
|
|
@@ -35,6 +35,8 @@ interface ParsedResult {
|
|
|
35
35
|
title: string;
|
|
36
36
|
url: string;
|
|
37
37
|
snippet?: string;
|
|
38
|
+
/** ISO timestamp lifted from the result row, when DDG emits one. */
|
|
39
|
+
publishedDate?: string;
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
/**
|
|
@@ -81,6 +83,23 @@ function unwrapResultUrl(href: string): string | undefined {
|
|
|
81
83
|
return undefined;
|
|
82
84
|
}
|
|
83
85
|
|
|
86
|
+
/**
|
|
87
|
+
* Lift a result row's publication timestamp from DDG markup. Recent DDG HTML
|
|
88
|
+
* renders it as a bare `<span> 2026-07-30T20:19:00.0000000</span>`
|
|
89
|
+
* inside `result__extras__url`. Restrict the scan to that container so a
|
|
90
|
+
* date-shaped value in a `<span class="result__snippet">` is not attributed
|
|
91
|
+
* as publication metadata.
|
|
92
|
+
*/
|
|
93
|
+
function extractPublishedDate(block: string): string | undefined {
|
|
94
|
+
const extrasUrl = /<div\b[^>]*\bclass="[^"]*\bresult__extras__url\b[^"]*"[^>]*>([\s\S]*?)<\/div>/i.exec(block)?.[1];
|
|
95
|
+
if (!extrasUrl) return undefined;
|
|
96
|
+
for (const match of extrasUrl.matchAll(/<span\b[^>]*>([\s\S]*?)<\/span>/gi)) {
|
|
97
|
+
const text = decodeHtmlText(match[1]);
|
|
98
|
+
if (/^\d{4}-\d{2}-\d{2}(?:[T ]\d{2}:\d{2}|$)/.test(text)) return text;
|
|
99
|
+
}
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
|
|
84
103
|
/**
|
|
85
104
|
* Walk the HTML page and pull out result blocks in document order.
|
|
86
105
|
*
|
|
@@ -106,10 +125,34 @@ function parseHtmlResults(html: string): ParsedResult[] {
|
|
|
106
125
|
if (!titleText) continue;
|
|
107
126
|
const snippet = snippetRe.exec(block);
|
|
108
127
|
const snippetText = snippet ? decodeHtmlText(snippet[1]) : undefined;
|
|
109
|
-
results.push({
|
|
128
|
+
results.push({
|
|
129
|
+
title: titleText,
|
|
130
|
+
url,
|
|
131
|
+
snippet: snippetText || undefined,
|
|
132
|
+
publishedDate: extractPublishedDate(block),
|
|
133
|
+
});
|
|
110
134
|
}
|
|
111
135
|
return results;
|
|
112
136
|
}
|
|
137
|
+
/**
|
|
138
|
+
* Extract the hidden fields from DDG's next-page form.
|
|
139
|
+
*
|
|
140
|
+
* Attribute order varies across responses, so each input tag is parsed
|
|
141
|
+
* independently instead of matching one fixed HTML layout.
|
|
142
|
+
*/
|
|
143
|
+
function parseContinuationForm(html: string): URLSearchParams | undefined {
|
|
144
|
+
for (const formMatch of html.matchAll(/<form\b[^>]*>([\s\S]*?)<\/form>/gi)) {
|
|
145
|
+
const form = new URLSearchParams();
|
|
146
|
+
for (const inputMatch of formMatch[1].matchAll(/<input\b[^>]*>/gi)) {
|
|
147
|
+
const input = inputMatch[0];
|
|
148
|
+
const name = /\bname\s*=\s*(["'])(.*?)\1/i.exec(input)?.[2];
|
|
149
|
+
const value = /\bvalue\s*=\s*(["'])(.*?)\1/i.exec(input)?.[2];
|
|
150
|
+
if (name && value !== undefined) form.append(decodeHtmlText(name), decodeHtmlText(value));
|
|
151
|
+
}
|
|
152
|
+
if (form.has("s") && form.has("vqd")) return form;
|
|
153
|
+
}
|
|
154
|
+
return undefined;
|
|
155
|
+
}
|
|
113
156
|
|
|
114
157
|
/**
|
|
115
158
|
* `true` when the page DDG returned is the bot-challenge modal instead of
|
|
@@ -137,19 +180,131 @@ const DDG_QUERY_SYNTAX: QuerySyntax = {
|
|
|
137
180
|
filetype: true,
|
|
138
181
|
};
|
|
139
182
|
|
|
140
|
-
|
|
183
|
+
/**
|
|
184
|
+
* DuckDuckGo's documented `kl` values.
|
|
185
|
+
*
|
|
186
|
+
* The codes resemble `region-language` locales but contain provider-specific
|
|
187
|
+
* identifiers (`jp-jp`, `tw-tzh`, `uk-en`) that cannot be derived mechanically.
|
|
188
|
+
*/
|
|
189
|
+
const DDG_KL_CODES = new Set([
|
|
190
|
+
"xa-ar",
|
|
191
|
+
"xa-en",
|
|
192
|
+
"ar-es",
|
|
193
|
+
"au-en",
|
|
194
|
+
"at-de",
|
|
195
|
+
"be-fr",
|
|
196
|
+
"be-nl",
|
|
197
|
+
"br-pt",
|
|
198
|
+
"bg-bg",
|
|
199
|
+
"ca-en",
|
|
200
|
+
"ca-fr",
|
|
201
|
+
"ct-ca",
|
|
202
|
+
"cl-es",
|
|
203
|
+
"cn-zh",
|
|
204
|
+
"co-es",
|
|
205
|
+
"hr-hr",
|
|
206
|
+
"cz-cs",
|
|
207
|
+
"dk-da",
|
|
208
|
+
"ee-et",
|
|
209
|
+
"fi-fi",
|
|
210
|
+
"fr-fr",
|
|
211
|
+
"de-de",
|
|
212
|
+
"gr-el",
|
|
213
|
+
"hk-tzh",
|
|
214
|
+
"hu-hu",
|
|
215
|
+
"in-en",
|
|
216
|
+
"id-id",
|
|
217
|
+
"id-en",
|
|
218
|
+
"ie-en",
|
|
219
|
+
"il-he",
|
|
220
|
+
"it-it",
|
|
221
|
+
"jp-jp",
|
|
222
|
+
"kr-kr",
|
|
223
|
+
"lv-lv",
|
|
224
|
+
"lt-lt",
|
|
225
|
+
"xl-es",
|
|
226
|
+
"my-ms",
|
|
227
|
+
"my-en",
|
|
228
|
+
"mx-es",
|
|
229
|
+
"nl-nl",
|
|
230
|
+
"nz-en",
|
|
231
|
+
"no-no",
|
|
232
|
+
"pe-es",
|
|
233
|
+
"ph-en",
|
|
234
|
+
"ph-tl",
|
|
235
|
+
"pl-pl",
|
|
236
|
+
"pt-pt",
|
|
237
|
+
"ro-ro",
|
|
238
|
+
"ru-ru",
|
|
239
|
+
"sg-en",
|
|
240
|
+
"sk-sk",
|
|
241
|
+
"sl-sl",
|
|
242
|
+
"za-en",
|
|
243
|
+
"es-es",
|
|
244
|
+
"se-sv",
|
|
245
|
+
"ch-de",
|
|
246
|
+
"ch-fr",
|
|
247
|
+
"ch-it",
|
|
248
|
+
"tw-tzh",
|
|
249
|
+
"th-th",
|
|
250
|
+
"tr-tr",
|
|
251
|
+
"ua-uk",
|
|
252
|
+
"uk-en",
|
|
253
|
+
"us-en",
|
|
254
|
+
"ue-es",
|
|
255
|
+
"ve-es",
|
|
256
|
+
"vn-vi",
|
|
257
|
+
"wt-wt",
|
|
258
|
+
]);
|
|
259
|
+
|
|
260
|
+
/** BCP 47 locales whose DDG code does not follow a simple component swap. */
|
|
261
|
+
const DDG_LOCALE_ALIASES: Record<string, string> = {
|
|
262
|
+
"ca-es": "ct-ca",
|
|
263
|
+
"en-gb": "uk-en",
|
|
264
|
+
"es-419": "xl-es",
|
|
265
|
+
"es-us": "ue-es",
|
|
266
|
+
"ja-jp": "jp-jp",
|
|
267
|
+
"ko-kr": "kr-kr",
|
|
268
|
+
"zh-hk": "hk-tzh",
|
|
269
|
+
"zh-tw": "tw-tzh",
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Map a parsed `lang:` locale onto DuckDuckGo's documented `kl` values.
|
|
274
|
+
*
|
|
275
|
+
* Shared queries use `language-region` order while DDG generally uses
|
|
276
|
+
* `region-language`. Provider-specific exceptions resolve through
|
|
277
|
+
* {@link DDG_LOCALE_ALIASES}; all other values must survive the documented
|
|
278
|
+
* allowlist after swapping or the caller keeps its default region.
|
|
279
|
+
*/
|
|
280
|
+
export function localeToKl(lang: string | undefined): string | undefined {
|
|
281
|
+
if (!lang) return undefined;
|
|
282
|
+
const locale = lang.toLowerCase().replaceAll("_", "-");
|
|
283
|
+
const alias = DDG_LOCALE_ALIASES[locale];
|
|
284
|
+
if (alias) return alias;
|
|
285
|
+
const match = /^([a-z]{2})-([a-z]{2})$/.exec(locale);
|
|
286
|
+
if (!match) return undefined;
|
|
287
|
+
const candidate = `${match[2]}-${match[1]}`;
|
|
288
|
+
return DDG_KL_CODES.has(candidate) ? candidate : undefined;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function createDuckDuckGoForm(params: SearchParams): URLSearchParams {
|
|
292
|
+
const parsed = params.parsedQuery ?? parseSearchQuery(params.query);
|
|
141
293
|
const form = new URLSearchParams({
|
|
142
|
-
q: formatScraperQuery(params.query,
|
|
143
|
-
kl: "us-en",
|
|
294
|
+
q: formatScraperQuery(params.query, parsed, DDG_QUERY_SYNTAX),
|
|
295
|
+
kl: localeToKl(parsed.lang) ?? "us-en",
|
|
144
296
|
});
|
|
145
297
|
const df = params.recency ? RECENCY_TO_DDG_DF[params.recency] : undefined;
|
|
146
298
|
if (df) form.set("df", df);
|
|
147
299
|
// Add b: "" parameter as specified in the browser fetch template to match real browser form submission
|
|
148
300
|
form.set("b", "");
|
|
301
|
+
return form;
|
|
302
|
+
}
|
|
149
303
|
|
|
304
|
+
async function callDuckDuckGoHtml(params: SearchParams, form: URLSearchParams, signal: AbortSignal): Promise<string> {
|
|
150
305
|
const page = await browserFetch(DUCKDUCKGO_HTML_URL, {
|
|
151
306
|
fetch: params.fetch ?? fetch,
|
|
152
|
-
signal
|
|
307
|
+
signal,
|
|
153
308
|
referer: "https://html.duckduckgo.com/",
|
|
154
309
|
init: {
|
|
155
310
|
method: "POST",
|
|
@@ -179,16 +334,29 @@ async function callDuckDuckGoHtml(params: SearchParams): Promise<string> {
|
|
|
179
334
|
/** Execute a DuckDuckGo web search via the no-JS HTML frontend. */
|
|
180
335
|
export async function searchDuckDuckGo(params: SearchParams): Promise<SearchResponse> {
|
|
181
336
|
const numResults = clampNumResults(params.numSearchResults ?? params.limit, DEFAULT_NUM_RESULTS, MAX_NUM_RESULTS);
|
|
182
|
-
const
|
|
183
|
-
const parsed = parseHtmlResults(html);
|
|
184
|
-
|
|
337
|
+
const signal = withHardTimeout(params.signal);
|
|
185
338
|
const sources: SearchSource[] = [];
|
|
186
339
|
const seen = new Set<string>();
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
340
|
+
let form: URLSearchParams | undefined = createDuckDuckGoForm(params);
|
|
341
|
+
|
|
342
|
+
while (form && sources.length < numResults) {
|
|
343
|
+
const html = await callDuckDuckGoHtml(params, form, signal);
|
|
344
|
+
const sourceCount = sources.length;
|
|
345
|
+
for (const result of parseHtmlResults(html)) {
|
|
346
|
+
if (seen.has(result.url)) continue;
|
|
347
|
+
seen.add(result.url);
|
|
348
|
+
sources.push({
|
|
349
|
+
title: result.title,
|
|
350
|
+
url: result.url,
|
|
351
|
+
snippet: result.snippet,
|
|
352
|
+
publishedDate: result.publishedDate,
|
|
353
|
+
ageSeconds: dateToAgeSeconds(result.publishedDate),
|
|
354
|
+
});
|
|
355
|
+
if (sources.length >= numResults) break;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (sources.length === sourceCount) break;
|
|
359
|
+
form = parseContinuationForm(html);
|
|
192
360
|
}
|
|
193
361
|
|
|
194
362
|
return { provider: "duckduckgo", sources };
|
package/src/web/search/types.ts
CHANGED
|
@@ -30,7 +30,12 @@ export const SEARCH_PROVIDER_OPTIONS = [
|
|
|
30
30
|
label: "OpenAI",
|
|
31
31
|
description: "OpenAI's native web_search (uses ChatGPT OAuth via /login openai-codex)",
|
|
32
32
|
},
|
|
33
|
-
{
|
|
33
|
+
{
|
|
34
|
+
value: "xai",
|
|
35
|
+
label: "xAI",
|
|
36
|
+
description:
|
|
37
|
+
"Grok web search via xAI Responses API (uses SuperGrok/X Premium+ OAuth via /login xai-oauth, or XAI_API_KEY)",
|
|
38
|
+
},
|
|
34
39
|
{ value: "zai", label: "Z.AI", description: "Calls Z.AI webSearchPrime MCP" },
|
|
35
40
|
{ value: "exa", label: "Exa", description: "API via /login exa or EXA_API_KEY; explicit keyless fallback via MCP" },
|
|
36
41
|
{ value: "tinyfish", label: "TinyFish", description: "Requires TINYFISH_API_KEY" },
|