@oh-my-pi/pi-coding-agent 15.3.2 → 15.4.1
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 +104 -0
- package/dist/types/cli/file-processor.d.ts +1 -1
- package/dist/types/config/settings-schema.d.ts +45 -3
- package/dist/types/config/settings.d.ts +1 -1
- package/dist/types/debug/raw-sse.d.ts +2 -0
- package/dist/types/edit/file-read-cache.d.ts +15 -4
- package/dist/types/edit/index.d.ts +3 -8
- package/dist/types/edit/renderer.d.ts +1 -2
- package/dist/types/eval/__tests__/shared-executors.test.d.ts +1 -0
- package/dist/types/eval/js/shared/local-module-loader.d.ts +16 -0
- package/dist/types/eval/js/shared/rewrite-imports.d.ts +4 -0
- package/dist/types/eval/js/shared/runtime.d.ts +14 -8
- package/dist/types/eval/py/executor.d.ts +1 -2
- package/dist/types/eval/py/kernel.d.ts +6 -0
- package/dist/types/eval/py/tool-bridge.d.ts +1 -5
- package/dist/types/eval/session-id.d.ts +3 -0
- package/dist/types/extensibility/extensions/types.d.ts +1 -3
- package/dist/types/hashline/anchors.d.ts +15 -9
- package/dist/types/hashline/constants.d.ts +0 -2
- package/dist/types/hashline/diff.d.ts +1 -2
- package/dist/types/hashline/executor.d.ts +52 -0
- package/dist/types/hashline/hash.d.ts +44 -93
- package/dist/types/hashline/index.d.ts +2 -1
- package/dist/types/hashline/input.d.ts +2 -9
- package/dist/types/hashline/recovery.d.ts +3 -9
- package/dist/types/hashline/tokenizer.d.ts +91 -0
- package/dist/types/hashline/types.d.ts +5 -7
- package/dist/types/modes/components/extensions/types.d.ts +0 -4
- package/dist/types/modes/types.d.ts +1 -0
- package/dist/types/modes/utils/ui-helpers.d.ts +1 -0
- package/dist/types/sdk.d.ts +2 -0
- package/dist/types/session/agent-session.d.ts +11 -15
- package/dist/types/session/agent-storage.d.ts +11 -10
- package/dist/types/slash-commands/acp-builtins.d.ts +3 -3
- package/dist/types/slash-commands/types.d.ts +0 -5
- package/dist/types/task/executor.d.ts +2 -0
- package/dist/types/tool-discovery/tool-index.d.ts +0 -50
- package/dist/types/tools/index.d.ts +2 -8
- package/dist/types/tools/match-line-format.d.ts +4 -4
- package/dist/types/tools/output-schema-validator.d.ts +64 -0
- package/dist/types/tools/review.d.ts +13 -0
- package/dist/types/tools/search-tool-bm25.d.ts +1 -1
- package/dist/types/tools/search.d.ts +4 -3
- package/dist/types/utils/edit-mode.d.ts +1 -1
- package/dist/types/web/kagi.d.ts +4 -2
- package/dist/types/web/parallel.d.ts +4 -3
- package/dist/types/web/scrapers/types.d.ts +2 -1
- package/dist/types/web/search/index.d.ts +12 -4
- package/dist/types/web/search/provider.d.ts +2 -1
- package/dist/types/web/search/providers/anthropic.d.ts +9 -4
- package/dist/types/web/search/providers/base.d.ts +34 -2
- package/dist/types/web/search/providers/brave.d.ts +8 -1
- package/dist/types/web/search/providers/codex.d.ts +13 -9
- package/dist/types/web/search/providers/exa.d.ts +10 -1
- package/dist/types/web/search/providers/gemini.d.ts +20 -23
- package/dist/types/web/search/providers/jina.d.ts +2 -1
- package/dist/types/web/search/providers/kagi.d.ts +4 -1
- package/dist/types/web/search/providers/kimi.d.ts +10 -1
- package/dist/types/web/search/providers/parallel.d.ts +3 -2
- package/dist/types/web/search/providers/perplexity.d.ts +5 -2
- package/dist/types/web/search/providers/searxng.d.ts +2 -1
- package/dist/types/web/search/providers/synthetic.d.ts +5 -8
- package/dist/types/web/search/providers/tavily.d.ts +11 -4
- package/dist/types/web/search/providers/utils.d.ts +8 -6
- package/dist/types/web/search/providers/zai.d.ts +12 -3
- package/package.json +7 -7
- package/src/cli/file-processor.ts +12 -2
- package/src/cli.ts +0 -8
- package/src/commands/commit.ts +8 -8
- package/src/config/prompt-templates.ts +6 -6
- package/src/config/settings-schema.ts +47 -3
- package/src/config/settings.ts +5 -5
- package/src/debug/raw-sse.ts +68 -3
- package/src/edit/file-read-cache.ts +68 -25
- package/src/edit/index.ts +6 -37
- package/src/edit/renderer.ts +9 -47
- package/src/edit/streaming.ts +43 -56
- package/src/eval/__tests__/shared-executors.test.ts +520 -0
- package/src/eval/js/context-manager.ts +64 -53
- package/src/eval/js/shared/local-module-loader.ts +265 -0
- package/src/eval/js/shared/prelude.txt +4 -0
- package/src/eval/js/shared/rewrite-imports.ts +85 -0
- package/src/eval/js/shared/runtime.ts +129 -86
- package/src/eval/js/worker-core.ts +23 -38
- package/src/eval/py/executor.ts +155 -84
- package/src/eval/py/kernel.ts +10 -1
- package/src/eval/py/prelude.py +22 -24
- package/src/eval/py/runner.py +203 -85
- package/src/eval/py/tool-bridge.ts +17 -10
- package/src/eval/session-id.ts +8 -0
- package/src/exec/bash-executor.ts +27 -16
- package/src/extensibility/extensions/runner.ts +0 -1
- package/src/extensibility/extensions/types.ts +1 -3
- package/src/hashline/anchors.ts +56 -65
- package/src/hashline/apply.ts +29 -31
- package/src/hashline/constants.ts +0 -3
- package/src/hashline/diff-preview.ts +4 -5
- package/src/hashline/diff.ts +30 -4
- package/src/hashline/execute.ts +91 -26
- package/src/hashline/executor.ts +239 -0
- package/src/hashline/grammar.lark +12 -10
- package/src/hashline/hash.ts +69 -114
- package/src/hashline/index.ts +2 -1
- package/src/hashline/input.ts +48 -41
- package/src/hashline/prefixes.ts +21 -11
- package/src/hashline/recovery.ts +63 -71
- package/src/hashline/stream.ts +2 -2
- package/src/hashline/tokenizer.ts +467 -0
- package/src/hashline/types.ts +6 -8
- package/src/internal-urls/docs-index.generated.ts +7 -7
- package/src/modes/components/extensions/types.ts +0 -5
- package/src/modes/components/session-observer-overlay.ts +11 -2
- package/src/modes/components/tree-selector.ts +10 -2
- package/src/modes/controllers/command-controller.ts +1 -3
- package/src/modes/controllers/extension-ui-controller.ts +10 -11
- package/src/modes/controllers/selector-controller.ts +5 -5
- package/src/modes/types.ts +4 -1
- package/src/modes/utils/ui-helpers.ts +4 -0
- package/src/prompts/agents/explore.md +1 -1
- package/src/prompts/tools/ast-edit.md +1 -1
- package/src/prompts/tools/ast-grep.md +1 -1
- package/src/prompts/tools/eval.md +1 -1
- package/src/prompts/tools/hashline.md +73 -94
- package/src/prompts/tools/read.md +4 -4
- package/src/prompts/tools/search.md +3 -3
- package/src/sdk.ts +17 -23
- package/src/session/agent-session.ts +59 -66
- package/src/session/agent-storage.ts +13 -14
- package/src/slash-commands/acp-builtins.ts +3 -3
- package/src/slash-commands/types.ts +0 -6
- package/src/task/executor.ts +26 -57
- package/src/task/index.ts +8 -4
- package/src/tool-discovery/tool-index.ts +0 -134
- package/src/tools/ast-edit.ts +36 -13
- package/src/tools/ast-grep.ts +45 -4
- package/src/tools/browser/tab-worker.ts +3 -2
- package/src/tools/eval.ts +2 -1
- package/src/tools/fetch.ts +23 -14
- package/src/tools/index.ts +2 -8
- package/src/tools/irc.ts +59 -5
- package/src/tools/match-line-format.ts +5 -7
- package/src/tools/output-schema-validator.ts +132 -0
- package/src/tools/read.ts +142 -31
- package/src/tools/review.ts +23 -0
- package/src/tools/search-tool-bm25.ts +3 -30
- package/src/tools/search.ts +48 -16
- package/src/tools/write.ts +3 -3
- package/src/tools/yield.ts +32 -41
- package/src/utils/edit-mode.ts +1 -2
- package/src/utils/file-mentions.ts +2 -2
- package/src/web/kagi.ts +15 -6
- package/src/web/parallel.ts +9 -6
- package/src/web/scrapers/types.ts +7 -1
- package/src/web/scrapers/youtube.ts +13 -7
- package/src/web/search/index.ts +37 -11
- package/src/web/search/provider.ts +5 -3
- package/src/web/search/providers/anthropic.ts +30 -21
- package/src/web/search/providers/base.ts +35 -2
- package/src/web/search/providers/brave.ts +4 -4
- package/src/web/search/providers/codex.ts +118 -89
- package/src/web/search/providers/exa.ts +3 -2
- package/src/web/search/providers/gemini.ts +58 -155
- package/src/web/search/providers/jina.ts +4 -4
- package/src/web/search/providers/kagi.ts +17 -11
- package/src/web/search/providers/kimi.ts +29 -13
- package/src/web/search/providers/parallel.ts +171 -23
- package/src/web/search/providers/perplexity.ts +38 -37
- package/src/web/search/providers/searxng.ts +3 -1
- package/src/web/search/providers/synthetic.ts +16 -19
- package/src/web/search/providers/tavily.ts +23 -18
- package/src/web/search/providers/utils.ts +11 -17
- package/src/web/search/providers/zai.ts +16 -8
- package/dist/types/hashline/parser.d.ts +0 -7
- package/dist/types/mcp/discoverable-tool-metadata.d.ts +0 -7
- package/dist/types/tools/vim.d.ts +0 -58
- package/dist/types/vim/buffer.d.ts +0 -41
- package/dist/types/vim/commands.d.ts +0 -6
- package/dist/types/vim/engine.d.ts +0 -47
- package/dist/types/vim/parser.d.ts +0 -3
- package/dist/types/vim/render.d.ts +0 -25
- package/dist/types/vim/types.d.ts +0 -182
- package/src/hashline/parser.ts +0 -246
- package/src/mcp/discoverable-tool-metadata.ts +0 -24
- package/src/prompts/tools/vim.md +0 -98
- package/src/tools/vim.ts +0 -949
- package/src/vim/buffer.ts +0 -309
- package/src/vim/commands.ts +0 -382
- package/src/vim/engine.ts +0 -2409
- package/src/vim/parser.ts +0 -134
- package/src/vim/render.ts +0 -252
- package/src/vim/types.ts +0 -197
package/src/tools/search.ts
CHANGED
|
@@ -9,6 +9,7 @@ import { prompt, untilAborted } from "@oh-my-pi/pi-utils";
|
|
|
9
9
|
import * as z from "zod/v4";
|
|
10
10
|
import { getFileReadCache } from "../edit/file-read-cache";
|
|
11
11
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
12
|
+
import { computeFileHash, formatHashlineHeader } from "../hashline/hash";
|
|
12
13
|
import type { Theme } from "../modes/theme/theme";
|
|
13
14
|
import searchDescription from "../prompts/tools/search.md" with { type: "text" };
|
|
14
15
|
import { DEFAULT_MAX_COLUMN, type TruncationResult, truncateHead } from "../session/streaming-output";
|
|
@@ -38,13 +39,13 @@ import {
|
|
|
38
39
|
import { ToolError } from "./tool-errors";
|
|
39
40
|
import { toolResult } from "./tool-result";
|
|
40
41
|
|
|
42
|
+
const searchPathEntrySchema = z.string().describe("file, directory, glob, or internal URL to search");
|
|
41
43
|
const searchSchema = z
|
|
42
44
|
.object({
|
|
43
45
|
pattern: z.string().describe("regex pattern"),
|
|
44
46
|
paths: z
|
|
45
|
-
.
|
|
46
|
-
.
|
|
47
|
-
.describe("files, directories, globs, or internal URLs to search"),
|
|
47
|
+
.union([searchPathEntrySchema, z.array(searchPathEntrySchema).min(1)])
|
|
48
|
+
.describe("file, directory, glob, internal URL, or array of those to search"),
|
|
48
49
|
i: z.boolean().optional().describe("case-insensitive search"),
|
|
49
50
|
gitignore: z.boolean().optional().describe("respect gitignore"),
|
|
50
51
|
skip: z
|
|
@@ -55,6 +56,9 @@ const searchSchema = z
|
|
|
55
56
|
.strict();
|
|
56
57
|
|
|
57
58
|
export type SearchToolInput = z.infer<typeof searchSchema>;
|
|
59
|
+
export function toPathList(input: string | string[] | undefined): string[] {
|
|
60
|
+
return typeof input === "string" ? [input] : (input ?? []);
|
|
61
|
+
}
|
|
58
62
|
|
|
59
63
|
/** Maximum number of distinct files surfaced in a single response. The
|
|
60
64
|
* agent paginates further pages via `skip`. */
|
|
@@ -236,7 +240,7 @@ export class SearchTool implements AgentTool<typeof searchSchema, SearchToolDeta
|
|
|
236
240
|
_onUpdate?: AgentToolUpdateCallback<SearchToolDetails>,
|
|
237
241
|
_toolContext?: AgentToolContext,
|
|
238
242
|
): Promise<AgentToolResult<SearchToolDetails>> {
|
|
239
|
-
const { pattern, paths, i, gitignore, skip } = params;
|
|
243
|
+
const { pattern, paths: rawPaths, i, gitignore, skip } = params;
|
|
240
244
|
|
|
241
245
|
return untilAborted(signal, async () => {
|
|
242
246
|
const normalizedPattern = pattern.trim();
|
|
@@ -248,6 +252,7 @@ export class SearchTool implements AgentTool<typeof searchSchema, SearchToolDeta
|
|
|
248
252
|
if (normalizedSkip < 0 || !Number.isFinite(normalizedSkip)) {
|
|
249
253
|
throw new ToolError("Skip must be a non-negative number");
|
|
250
254
|
}
|
|
255
|
+
const paths = toPathList(rawPaths);
|
|
251
256
|
for (const entry of paths) {
|
|
252
257
|
if (containsTopLevelComma(entry)) {
|
|
253
258
|
throw new ToolError('paths is an array — pass ["a", "b"] not ["a,b"]');
|
|
@@ -303,7 +308,6 @@ export class SearchTool implements AgentTool<typeof searchSchema, SearchToolDeta
|
|
|
303
308
|
}
|
|
304
309
|
const { globFilter } = scope;
|
|
305
310
|
const baseDisplayMode = resolveFileDisplayMode(this.session);
|
|
306
|
-
const immutableDisplayMode = resolveFileDisplayMode(this.session, { immutable: true });
|
|
307
311
|
|
|
308
312
|
const effectiveOutputMode = GrepOutputMode.Content;
|
|
309
313
|
// Multi-scope = more than one file may match. We fetch up to
|
|
@@ -485,14 +489,27 @@ export class SearchTool implements AgentTool<typeof searchSchema, SearchToolDeta
|
|
|
485
489
|
matchesByFile.get(relativePath)!.push(match);
|
|
486
490
|
}
|
|
487
491
|
const displayLines: string[] = [];
|
|
492
|
+
const hashContexts = new Map<string, { absolutePath: string; fileHash: string }>();
|
|
493
|
+
if (baseDisplayMode.hashLines) {
|
|
494
|
+
for (const relativePath of fileList) {
|
|
495
|
+
if (archiveDisplaySet.has(relativePath)) continue;
|
|
496
|
+
const absoluteFilePath = path.resolve(this.session.cwd, relativePath);
|
|
497
|
+
if (immutableSourcePaths.has(absoluteFilePath)) continue;
|
|
498
|
+
try {
|
|
499
|
+
const fullText = await Bun.file(absoluteFilePath).text();
|
|
500
|
+
const fileHash = computeFileHash(fullText);
|
|
501
|
+
hashContexts.set(relativePath, { absolutePath: absoluteFilePath, fileHash });
|
|
502
|
+
} catch {
|
|
503
|
+
// Best-effort: if the file disappeared between grep and render, fall back to plain line output.
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
}
|
|
488
507
|
const renderMatchesForFile = (relativePath: string): { model: string[]; display: string[] } => {
|
|
489
508
|
const modelOut: string[] = [];
|
|
490
509
|
const displayOut: string[] = [];
|
|
491
510
|
const fileMatches = matchesByFile.get(relativePath) ?? [];
|
|
492
|
-
const
|
|
493
|
-
const useHashLines =
|
|
494
|
-
? immutableDisplayMode.hashLines
|
|
495
|
-
: baseDisplayMode.hashLines;
|
|
511
|
+
const hashContext = hashContexts.get(relativePath);
|
|
512
|
+
const useHashLines = hashContext !== undefined;
|
|
496
513
|
const lineNumberWidth = fileMatches.reduce((width, match) => {
|
|
497
514
|
let nextWidth = Math.max(width, String(match.lineNumber).length);
|
|
498
515
|
for (const ctx of match.contextBefore ?? []) {
|
|
@@ -533,17 +550,21 @@ export class SearchTool implements AgentTool<typeof searchSchema, SearchToolDeta
|
|
|
533
550
|
}
|
|
534
551
|
fileMatchCounts.set(relativePath, (fileMatchCounts.get(relativePath) ?? 0) + 1);
|
|
535
552
|
}
|
|
536
|
-
if (cacheEntries.length > 0 &&
|
|
537
|
-
getFileReadCache(this.session).recordSparse(
|
|
553
|
+
if (cacheEntries.length > 0 && hashContext) {
|
|
554
|
+
getFileReadCache(this.session).recordSparse(hashContext.absolutePath, cacheEntries, {
|
|
555
|
+
fileHash: hashContext.fileHash,
|
|
556
|
+
});
|
|
538
557
|
}
|
|
539
558
|
return { model: modelOut, display: displayOut };
|
|
540
559
|
};
|
|
541
560
|
if (isDirectory) {
|
|
542
561
|
const grouped = formatGroupedFiles(fileList, relativePath => {
|
|
543
562
|
const rendered = renderMatchesForFile(relativePath);
|
|
563
|
+
const hashContext = hashContexts.get(relativePath);
|
|
544
564
|
return {
|
|
545
565
|
modelLines: rendered.model,
|
|
546
566
|
displayLines: rendered.display,
|
|
567
|
+
headerSuffix: hashContext ? `#${hashContext.fileHash}` : "",
|
|
547
568
|
skip: rendered.model.length === 0,
|
|
548
569
|
};
|
|
549
570
|
});
|
|
@@ -552,6 +573,15 @@ export class SearchTool implements AgentTool<typeof searchSchema, SearchToolDeta
|
|
|
552
573
|
} else {
|
|
553
574
|
for (const relativePath of fileList) {
|
|
554
575
|
const rendered = renderMatchesForFile(relativePath);
|
|
576
|
+
if (rendered.model.length === 0) continue;
|
|
577
|
+
if (outputLines.length > 0) {
|
|
578
|
+
outputLines.push("");
|
|
579
|
+
displayLines.push("");
|
|
580
|
+
}
|
|
581
|
+
const hashContext = hashContexts.get(relativePath);
|
|
582
|
+
if (hashContext) {
|
|
583
|
+
outputLines.push(formatHashlineHeader(relativePath, hashContext.fileHash));
|
|
584
|
+
}
|
|
555
585
|
outputLines.push(...rendered.model);
|
|
556
586
|
displayLines.push(...rendered.display);
|
|
557
587
|
}
|
|
@@ -607,7 +637,7 @@ export class SearchTool implements AgentTool<typeof searchSchema, SearchToolDeta
|
|
|
607
637
|
|
|
608
638
|
interface SearchRenderArgs {
|
|
609
639
|
pattern: string;
|
|
610
|
-
paths?: string[];
|
|
640
|
+
paths?: string | string[];
|
|
611
641
|
i?: boolean;
|
|
612
642
|
gitignore?: boolean;
|
|
613
643
|
skip?: number;
|
|
@@ -618,8 +648,9 @@ const COLLAPSED_TEXT_LIMIT = PREVIEW_LIMITS.COLLAPSED_LINES * 2;
|
|
|
618
648
|
export const searchToolRenderer = {
|
|
619
649
|
inline: true,
|
|
620
650
|
renderCall(args: SearchRenderArgs, _options: RenderResultOptions, uiTheme: Theme): Component {
|
|
651
|
+
const paths = toPathList(args.paths);
|
|
621
652
|
const meta: string[] = [];
|
|
622
|
-
if (
|
|
653
|
+
if (paths.length) meta.push(`in ${paths.join(", ")}`);
|
|
623
654
|
if (args.i) meta.push("case:insensitive");
|
|
624
655
|
if (args.gitignore === false) meta.push("gitignore:false");
|
|
625
656
|
if (args.skip !== undefined && args.skip > 0) meta.push(`skip:${args.skip}`);
|
|
@@ -745,11 +776,12 @@ export const searchToolRenderer = {
|
|
|
745
776
|
let contextDir = searchBase ?? "";
|
|
746
777
|
return group.map(line => {
|
|
747
778
|
if (line.startsWith("## ")) {
|
|
748
|
-
// Strip optional ` (suffix)`
|
|
779
|
+
// Strip optional ` (suffix)` and `#hash` before resolving.
|
|
749
780
|
const fileName = line
|
|
750
781
|
.slice(3)
|
|
751
782
|
.trimEnd()
|
|
752
|
-
.replace(/\s+\([^)]*\)\s*$/, "")
|
|
783
|
+
.replace(/\s+\([^)]*\)\s*$/, "")
|
|
784
|
+
.replace(/#[0-9a-f]+$/, "");
|
|
753
785
|
const absPath = contextDir && fileName ? path.join(contextDir, fileName) : undefined;
|
|
754
786
|
const styled = uiTheme.fg("dim", line);
|
|
755
787
|
return absPath ? fileHyperlink(absPath, styled) : styled;
|
|
@@ -760,7 +792,7 @@ export const searchToolRenderer = {
|
|
|
760
792
|
.trimEnd()
|
|
761
793
|
.replace(/\s+\([^)]*\)\s*$/, "");
|
|
762
794
|
const isDirectory = raw.endsWith("/");
|
|
763
|
-
const name = raw.replace(/\/$/, "");
|
|
795
|
+
const name = isDirectory ? raw.replace(/\/$/, "") : raw.replace(/#[0-9a-f]+$/, "");
|
|
764
796
|
if (isDirectory) {
|
|
765
797
|
if (searchBase) {
|
|
766
798
|
contextDir = name === "." ? searchBase : path.join(searchBase, name);
|
package/src/tools/write.ts
CHANGED
|
@@ -74,8 +74,8 @@ export interface WriteToolDetails {
|
|
|
74
74
|
/**
|
|
75
75
|
* Strip hashline display prefixes from write content.
|
|
76
76
|
*
|
|
77
|
-
* Only active when hashline edit mode is enabled — the model sees `
|
|
78
|
-
* prefixes in read output and sometimes copies them into write content.
|
|
77
|
+
* Only active when hashline edit mode is enabled — the model sees `¶PATH#HASH`
|
|
78
|
+
* headers plus `LINE:` prefixes in read output and sometimes copies them into write content.
|
|
79
79
|
*/
|
|
80
80
|
function stripWriteContent(session: ToolSession, content: string): { text: string; stripped: boolean } {
|
|
81
81
|
if (!resolveFileDisplayMode(session).hashLines) {
|
|
@@ -658,7 +658,7 @@ export class WriteTool implements AgentTool<typeof writeSchema, WriteToolDetails
|
|
|
658
658
|
context?: AgentToolContext,
|
|
659
659
|
): Promise<AgentToolResult<WriteToolDetails>> {
|
|
660
660
|
return untilAborted(signal, async () => {
|
|
661
|
-
// Strip hashline display prefixes (LINE
|
|
661
|
+
// Strip hashline display prefixes (¶PATH#HASH + LINE:) if the model copied them from read output
|
|
662
662
|
const { text: cleanContent, stripped } = stripWriteContent(this.session, content);
|
|
663
663
|
const internalRouter = InternalUrlRouter.instance();
|
|
664
664
|
if (internalRouter.canHandle(path)) {
|
package/src/tools/yield.ts
CHANGED
|
@@ -8,15 +8,13 @@ import type { TSchema } from "@oh-my-pi/pi-ai/types";
|
|
|
8
8
|
import {
|
|
9
9
|
dereferenceJsonSchema,
|
|
10
10
|
isValidJsonSchema,
|
|
11
|
-
type JsonSchemaValidationIssue,
|
|
12
11
|
type JsonSchemaValidationResult,
|
|
13
12
|
sanitizeSchemaForStrictMode,
|
|
14
13
|
tryEnforceStrictSchema,
|
|
15
|
-
validateJsonSchemaValue,
|
|
16
14
|
} from "@oh-my-pi/pi-ai/utils/schema";
|
|
17
15
|
import { subprocessToolRegistry } from "../task/subprocess-tool-registry";
|
|
18
16
|
import type { ToolSession } from ".";
|
|
19
|
-
import {
|
|
17
|
+
import { buildOutputValidator, formatAllValidationIssues } from "./output-schema-validator";
|
|
20
18
|
|
|
21
19
|
export interface YieldDetails {
|
|
22
20
|
data: unknown;
|
|
@@ -34,16 +32,6 @@ function formatSchema(schema: unknown): string {
|
|
|
34
32
|
}
|
|
35
33
|
}
|
|
36
34
|
|
|
37
|
-
function formatJsonSchemaIssues(issues: ReadonlyArray<JsonSchemaValidationIssue> | undefined): string {
|
|
38
|
-
if (!issues || issues.length === 0) return "Unknown schema validation error.";
|
|
39
|
-
return issues
|
|
40
|
-
.map(issue => {
|
|
41
|
-
const path = issue.path.length === 0 ? "" : `${issue.path.map(seg => String(seg)).join("/")}: `;
|
|
42
|
-
return `${path}${issue.message}`;
|
|
43
|
-
})
|
|
44
|
-
.join("; ");
|
|
45
|
-
}
|
|
46
|
-
|
|
47
35
|
function looseRecordSchema(description: string): Record<string, unknown> {
|
|
48
36
|
return {
|
|
49
37
|
type: "object",
|
|
@@ -100,6 +88,15 @@ function wrapYieldParameters(dataSchema: Record<string, unknown>): Record<string
|
|
|
100
88
|
};
|
|
101
89
|
}
|
|
102
90
|
|
|
91
|
+
/**
|
|
92
|
+
* Max consecutive schema-validation failures before the yield tool overrides validation
|
|
93
|
+
* and lets non-conforming data through. The override is a safety net for schemas the
|
|
94
|
+
* JTD→JSON-Schema converter cannot fully express; it should not be reached during normal
|
|
95
|
+
* model retries. Three matches the existing "3 reminders" pattern elsewhere in the agent
|
|
96
|
+
* runtime.
|
|
97
|
+
*/
|
|
98
|
+
const MAX_SCHEMA_RETRIES = 3;
|
|
99
|
+
|
|
103
100
|
export class YieldTool implements AgentTool<TSchema, YieldDetails> {
|
|
104
101
|
readonly name = "yield";
|
|
105
102
|
readonly label = "Submit Result";
|
|
@@ -120,21 +117,14 @@ export class YieldTool implements AgentTool<TSchema, YieldDetails> {
|
|
|
120
117
|
let parameters: TSchema;
|
|
121
118
|
|
|
122
119
|
try {
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
if (normalizedSchema !== undefined && normalizedSchema !== false && !schemaError) {
|
|
133
|
-
if (!isValidJsonSchema(normalizedSchema)) {
|
|
134
|
-
schemaError = "invalid JSON schema";
|
|
135
|
-
} else {
|
|
136
|
-
validate = value => validateJsonSchemaValue(normalizedSchema, value);
|
|
137
|
-
}
|
|
120
|
+
const {
|
|
121
|
+
validator,
|
|
122
|
+
jsonSchema: normalizedSchema,
|
|
123
|
+
normalized,
|
|
124
|
+
error: schemaError,
|
|
125
|
+
} = buildOutputValidator(session.outputSchema);
|
|
126
|
+
if (validator) {
|
|
127
|
+
validate = value => validator.validate(value);
|
|
138
128
|
}
|
|
139
129
|
|
|
140
130
|
const schemaHint = formatSchema(normalizedSchema ?? session.outputSchema);
|
|
@@ -142,21 +132,15 @@ export class YieldTool implements AgentTool<TSchema, YieldDetails> {
|
|
|
142
132
|
? `Structured JSON output (output schema invalid; accepting unconstrained object): ${schemaError}`
|
|
143
133
|
: `Structured output matching the schema:\n${schemaHint}`;
|
|
144
134
|
let sanitizedSchema: Record<string, unknown> | undefined;
|
|
145
|
-
if (
|
|
146
|
-
|
|
147
|
-
normalizedSchema != null &&
|
|
148
|
-
typeof normalizedSchema === "object" &&
|
|
149
|
-
!Array.isArray(normalizedSchema)
|
|
150
|
-
) {
|
|
151
|
-
const normalizedRecord = normalizedSchema as Record<string, unknown>;
|
|
152
|
-
const strictProbe = tryEnforceStrictSchema(normalizedRecord);
|
|
135
|
+
if (!schemaError && normalizedSchema !== undefined) {
|
|
136
|
+
const strictProbe = tryEnforceStrictSchema(normalizedSchema);
|
|
153
137
|
if (strictProbe.strict) {
|
|
154
|
-
sanitizedSchema = sanitizeSchemaForStrictMode(
|
|
138
|
+
sanitizedSchema = sanitizeSchemaForStrictMode(normalizedSchema);
|
|
155
139
|
} else {
|
|
156
|
-
sanitizedSchema =
|
|
140
|
+
sanitizedSchema = normalizedSchema;
|
|
157
141
|
this.strict = false;
|
|
158
142
|
}
|
|
159
|
-
} else if (!schemaError &&
|
|
143
|
+
} else if (!schemaError && normalized === true) {
|
|
160
144
|
sanitizedSchema = {};
|
|
161
145
|
this.strict = false;
|
|
162
146
|
}
|
|
@@ -229,8 +213,15 @@ export class YieldTool implements AgentTool<TSchema, YieldDetails> {
|
|
|
229
213
|
const parsed = this.#validate(data);
|
|
230
214
|
if (!parsed.success) {
|
|
231
215
|
this.#schemaValidationFailures++;
|
|
232
|
-
if (this.#schemaValidationFailures <=
|
|
233
|
-
|
|
216
|
+
if (this.#schemaValidationFailures <= MAX_SCHEMA_RETRIES) {
|
|
217
|
+
const remaining = MAX_SCHEMA_RETRIES - this.#schemaValidationFailures;
|
|
218
|
+
const retryHint =
|
|
219
|
+
remaining > 0
|
|
220
|
+
? ` Call yield again with the corrected shape — ${remaining} retry attempt(s) remain before the schema constraint is dropped.`
|
|
221
|
+
: " Call yield again with the corrected shape — this is the final retry before the schema constraint is dropped.";
|
|
222
|
+
throw new Error(
|
|
223
|
+
`Output does not match schema: ${formatAllValidationIssues(parsed.issues)}.${retryHint}`,
|
|
224
|
+
);
|
|
234
225
|
}
|
|
235
226
|
schemaValidationOverridden = true;
|
|
236
227
|
}
|
package/src/utils/edit-mode.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { $env } from "@oh-my-pi/pi-utils";
|
|
2
2
|
|
|
3
|
-
export type EditMode = "replace" | "patch" | "hashline" | "
|
|
3
|
+
export type EditMode = "replace" | "patch" | "hashline" | "apply_patch";
|
|
4
4
|
|
|
5
5
|
export const DEFAULT_EDIT_MODE: EditMode = "hashline";
|
|
6
6
|
|
|
@@ -9,7 +9,6 @@ const EDIT_MODE_IDS = {
|
|
|
9
9
|
hashline: "hashline",
|
|
10
10
|
patch: "patch",
|
|
11
11
|
replace: "replace",
|
|
12
|
-
vim: "vim",
|
|
13
12
|
} as const satisfies Record<string, EditMode>;
|
|
14
13
|
|
|
15
14
|
export const EDIT_MODES = Object.keys(EDIT_MODE_IDS) as EditMode[];
|
|
@@ -12,7 +12,7 @@ import type { ImageContent } from "@oh-my-pi/pi-ai";
|
|
|
12
12
|
import { glob } from "@oh-my-pi/pi-natives";
|
|
13
13
|
import { fuzzyMatch } from "@oh-my-pi/pi-tui";
|
|
14
14
|
import { formatAge, formatBytes, readImageMetadata } from "@oh-my-pi/pi-utils";
|
|
15
|
-
import {
|
|
15
|
+
import { computeFileHash, formatHashlineHeader, formatNumberedLines } from "../hashline/hash";
|
|
16
16
|
import type { FileMentionMessage } from "../session/messages";
|
|
17
17
|
import {
|
|
18
18
|
DEFAULT_MAX_BYTES,
|
|
@@ -356,7 +356,7 @@ export async function generateFileMentionMessages(
|
|
|
356
356
|
const content = await Bun.file(absolutePath).text();
|
|
357
357
|
let { output, lineCount } = buildTextOutput(content);
|
|
358
358
|
if (options?.useHashLines) {
|
|
359
|
-
output =
|
|
359
|
+
output = `${formatHashlineHeader(resolvedPath, computeFileHash(content))}\n${formatNumberedLines(output)}`;
|
|
360
360
|
}
|
|
361
361
|
files.push({ path: resolvedPath, content: output, lineCount });
|
|
362
362
|
} catch {
|
package/src/web/kagi.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import type { AuthStorage } from "@oh-my-pi/pi-ai";
|
|
2
|
+
import { withHardTimeout } from "./search/providers/utils";
|
|
3
3
|
|
|
4
4
|
const KAGI_SEARCH_URL = "https://kagi.com/api/v0/search";
|
|
5
5
|
|
|
@@ -97,6 +97,7 @@ function parseKagiErrorResponse(statusCode: number, responseText: string): KagiA
|
|
|
97
97
|
|
|
98
98
|
export interface KagiSearchOptions {
|
|
99
99
|
limit?: number;
|
|
100
|
+
sessionId?: string;
|
|
100
101
|
signal?: AbortSignal;
|
|
101
102
|
}
|
|
102
103
|
|
|
@@ -113,8 +114,12 @@ export interface KagiSearchResult {
|
|
|
113
114
|
relatedQuestions: string[];
|
|
114
115
|
}
|
|
115
116
|
|
|
116
|
-
export async function findKagiApiKey(
|
|
117
|
-
|
|
117
|
+
export async function findKagiApiKey(
|
|
118
|
+
authStorage: AuthStorage,
|
|
119
|
+
sessionId?: string,
|
|
120
|
+
signal?: AbortSignal,
|
|
121
|
+
): Promise<string | null> {
|
|
122
|
+
return (await authStorage.getApiKey("kagi", sessionId, { signal })) ?? null;
|
|
118
123
|
}
|
|
119
124
|
|
|
120
125
|
function getAuthHeaders(apiKey: string): Record<string, string> {
|
|
@@ -124,8 +129,12 @@ function getAuthHeaders(apiKey: string): Record<string, string> {
|
|
|
124
129
|
};
|
|
125
130
|
}
|
|
126
131
|
|
|
127
|
-
export async function searchWithKagi(
|
|
128
|
-
|
|
132
|
+
export async function searchWithKagi(
|
|
133
|
+
query: string,
|
|
134
|
+
options: KagiSearchOptions = {},
|
|
135
|
+
authStorage: AuthStorage,
|
|
136
|
+
): Promise<KagiSearchResult> {
|
|
137
|
+
const apiKey = await findKagiApiKey(authStorage, options.sessionId, options.signal);
|
|
129
138
|
if (!apiKey) {
|
|
130
139
|
throw new KagiApiError("Kagi credentials not found. Set KAGI_API_KEY or login with 'omp /login kagi'.");
|
|
131
140
|
}
|
package/src/web/parallel.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getEnvApiKey } from "@oh-my-pi/pi-ai";
|
|
2
|
+
import type { AgentStorage } from "../session/agent-storage";
|
|
2
3
|
import { findCredential, withHardTimeout } from "./search/providers/utils";
|
|
3
4
|
|
|
4
5
|
const PARALLEL_API_URL = "https://api.parallel.ai";
|
|
@@ -73,8 +74,8 @@ export class ParallelApiError extends Error {
|
|
|
73
74
|
}
|
|
74
75
|
}
|
|
75
76
|
|
|
76
|
-
export
|
|
77
|
-
return findCredential(getEnvApiKey("parallel"), "parallel");
|
|
77
|
+
export function findParallelApiKey(storage: AgentStorage | null | undefined): string | null {
|
|
78
|
+
return findCredential(storage, getEnvApiKey("parallel"), "parallel");
|
|
78
79
|
}
|
|
79
80
|
|
|
80
81
|
export function getParallelExtractContent(document: ParallelExtractDocument): string {
|
|
@@ -284,9 +285,10 @@ function parseExtractPayload(payload: unknown): ParallelExtractResult {
|
|
|
284
285
|
export async function searchWithParallel(
|
|
285
286
|
objective: string,
|
|
286
287
|
queries: string[],
|
|
287
|
-
options: ParallelSearchOptions
|
|
288
|
+
options: ParallelSearchOptions,
|
|
289
|
+
storage: AgentStorage | null | undefined,
|
|
288
290
|
): Promise<ParallelSearchResult> {
|
|
289
|
-
const apiKey =
|
|
291
|
+
const apiKey = findParallelApiKey(storage);
|
|
290
292
|
if (!apiKey) {
|
|
291
293
|
throw new ParallelApiError(
|
|
292
294
|
"Parallel credentials not found. Set PARALLEL_API_KEY or login with 'omp /login parallel'.",
|
|
@@ -316,9 +318,10 @@ export async function searchWithParallel(
|
|
|
316
318
|
|
|
317
319
|
export async function extractWithParallel(
|
|
318
320
|
urls: string[],
|
|
319
|
-
options: ParallelExtractOptions
|
|
321
|
+
options: ParallelExtractOptions,
|
|
322
|
+
storage: AgentStorage | null | undefined,
|
|
320
323
|
): Promise<ParallelExtractResult> {
|
|
321
|
-
const apiKey =
|
|
324
|
+
const apiKey = findParallelApiKey(storage);
|
|
322
325
|
if (!apiKey) {
|
|
323
326
|
throw new ParallelApiError(
|
|
324
327
|
"Parallel credentials not found. Set PARALLEL_API_KEY or login with 'omp /login parallel'.",
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import { ptree } from "@oh-my-pi/pi-utils";
|
|
5
5
|
import type TurndownService from "turndown";
|
|
6
6
|
|
|
7
|
+
import type { AgentStorage } from "../../session/agent-storage";
|
|
7
8
|
import { ToolAbortError } from "../../tools/tool-errors";
|
|
8
9
|
|
|
9
10
|
export { formatNumber } from "@oh-my-pi/pi-utils";
|
|
@@ -19,7 +20,12 @@ export interface RenderResult {
|
|
|
19
20
|
notes: string[];
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
export type SpecialHandler = (
|
|
23
|
+
export type SpecialHandler = (
|
|
24
|
+
url: string,
|
|
25
|
+
timeout: number,
|
|
26
|
+
signal?: AbortSignal,
|
|
27
|
+
storage?: AgentStorage | null,
|
|
28
|
+
) => Promise<RenderResult | null>;
|
|
23
29
|
|
|
24
30
|
export const MAX_OUTPUT_CHARS = 500_000;
|
|
25
31
|
export const MAX_BYTES = 50 * 1024 * 1024;
|
|
@@ -3,6 +3,7 @@ import * as os from "node:os";
|
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import { ptree, Snowflake } from "@oh-my-pi/pi-utils";
|
|
5
5
|
import { settings } from "../../config/settings";
|
|
6
|
+
import type { AgentStorage } from "../../session/agent-storage";
|
|
6
7
|
import { throwIfAborted } from "../../tools/tool-errors";
|
|
7
8
|
import { ensureTool } from "../../utils/tools-manager";
|
|
8
9
|
import { extractWithParallel, findParallelApiKey, getParallelExtractContent } from "../parallel";
|
|
@@ -101,6 +102,7 @@ export const handleYouTube: SpecialHandler = async (
|
|
|
101
102
|
url: string,
|
|
102
103
|
timeout: number,
|
|
103
104
|
userSignal?: AbortSignal,
|
|
105
|
+
storage?: AgentStorage | null,
|
|
104
106
|
): Promise<RenderResult | null> => {
|
|
105
107
|
throwIfAborted(userSignal);
|
|
106
108
|
const yt = parseYouTubeUrl(url);
|
|
@@ -112,14 +114,18 @@ export const handleYouTube: SpecialHandler = async (
|
|
|
112
114
|
const videoUrl = `https://www.youtube.com/watch?v=${yt.videoId}`;
|
|
113
115
|
|
|
114
116
|
// Prefer Parallel extract when credentials are available
|
|
115
|
-
if (settings.get("providers.parallelFetch") &&
|
|
117
|
+
if (settings.get("providers.parallelFetch") && findParallelApiKey(storage)) {
|
|
116
118
|
try {
|
|
117
|
-
const parallelResult = await extractWithParallel(
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
119
|
+
const parallelResult = await extractWithParallel(
|
|
120
|
+
[videoUrl],
|
|
121
|
+
{
|
|
122
|
+
objective: "Extract the main content of this YouTube video page",
|
|
123
|
+
excerpts: true,
|
|
124
|
+
fullContent: false,
|
|
125
|
+
signal,
|
|
126
|
+
},
|
|
127
|
+
storage,
|
|
128
|
+
);
|
|
123
129
|
const firstDocument = parallelResult.results[0];
|
|
124
130
|
if (firstDocument) {
|
|
125
131
|
const content = getParallelExtractContent(firstDocument);
|
package/src/web/search/index.ts
CHANGED
|
@@ -3,15 +3,16 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Single tool supporting Anthropic, Perplexity, Exa, Brave, Jina, Kimi, Gemini, Codex, Tavily, Kagi, Z.AI, SearXNG, and Synthetic
|
|
5
5
|
* providers with provider-specific parameters exposed conditionally.
|
|
6
|
-
*
|
|
7
6
|
*/
|
|
8
7
|
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
|
|
8
|
+
import type { AuthStorage } from "@oh-my-pi/pi-ai";
|
|
9
9
|
import { prompt } from "@oh-my-pi/pi-utils";
|
|
10
10
|
import * as z from "zod/v4";
|
|
11
11
|
import type { CustomTool, CustomToolContext, RenderResultOptions } from "../../extensibility/custom-tools/types";
|
|
12
12
|
import type { Theme } from "../../modes/theme/theme";
|
|
13
13
|
import webSearchSystemPrompt from "../../prompts/system/web-search.md" with { type: "text" };
|
|
14
14
|
import webSearchDescription from "../../prompts/tools/web-search.md" with { type: "text" };
|
|
15
|
+
import { discoverAuthStorage } from "../../sdk";
|
|
15
16
|
import type { ToolSession } from "../../tools";
|
|
16
17
|
import { formatAge } from "../../tools/render-utils";
|
|
17
18
|
import { throwIfAborted } from "../../tools/tool-errors";
|
|
@@ -114,18 +115,25 @@ function formatForLLM(response: SearchResponse): string {
|
|
|
114
115
|
return parts.join("\n");
|
|
115
116
|
}
|
|
116
117
|
|
|
118
|
+
interface ExecuteSearchOptions {
|
|
119
|
+
authStorage: AuthStorage;
|
|
120
|
+
sessionId?: string;
|
|
121
|
+
signal?: AbortSignal;
|
|
122
|
+
}
|
|
123
|
+
|
|
117
124
|
/** Execute web search */
|
|
118
125
|
async function executeSearch(
|
|
119
126
|
_toolCallId: string,
|
|
120
127
|
params: SearchQueryParams,
|
|
121
|
-
|
|
128
|
+
options: ExecuteSearchOptions,
|
|
122
129
|
): Promise<{ content: Array<{ type: "text"; text: string }>; details: SearchRenderDetails }> {
|
|
130
|
+
const { authStorage, sessionId, signal } = options;
|
|
123
131
|
const providers =
|
|
124
132
|
params.provider && params.provider !== "auto"
|
|
125
|
-
? await getSearchProvider(params.provider).then(provider =>
|
|
126
|
-
provider.isAvailable() ? [provider] : resolveProviderChain("auto"),
|
|
133
|
+
? await getSearchProvider(params.provider).then(async provider =>
|
|
134
|
+
(await provider.isAvailable(authStorage)) ? [provider] : resolveProviderChain(authStorage, "auto"),
|
|
127
135
|
)
|
|
128
|
-
: await resolveProviderChain();
|
|
136
|
+
: await resolveProviderChain(authStorage);
|
|
129
137
|
if (providers.length === 0) {
|
|
130
138
|
const message = "No web search provider configured.";
|
|
131
139
|
return {
|
|
@@ -148,6 +156,8 @@ async function executeSearch(
|
|
|
148
156
|
numSearchResults: params.num_search_results,
|
|
149
157
|
temperature: params.temperature,
|
|
150
158
|
signal,
|
|
159
|
+
authStorage,
|
|
160
|
+
sessionId,
|
|
151
161
|
});
|
|
152
162
|
|
|
153
163
|
const text = formatForLLM(response);
|
|
@@ -190,18 +200,27 @@ async function executeSearch(
|
|
|
190
200
|
|
|
191
201
|
/**
|
|
192
202
|
* Execute a web search query for CLI/testing workflows.
|
|
203
|
+
*
|
|
204
|
+
* `authStorage` may be omitted; in that case we discover one via the standard
|
|
205
|
+
* factory (`discoverAuthStorage`), which honours `OMP_AUTH_BROKER_URL` and
|
|
206
|
+
* otherwise opens the local SQLite credential store.
|
|
193
207
|
*/
|
|
194
208
|
export async function runSearchQuery(
|
|
195
209
|
params: SearchQueryParams,
|
|
210
|
+
options: { authStorage?: AuthStorage; sessionId?: string; signal?: AbortSignal } = {},
|
|
196
211
|
): Promise<{ content: Array<{ type: "text"; text: string }>; details: SearchRenderDetails }> {
|
|
197
|
-
|
|
212
|
+
const authStorage = options.authStorage ?? (await discoverAuthStorage());
|
|
213
|
+
return executeSearch("cli-web-search", params, {
|
|
214
|
+
authStorage,
|
|
215
|
+
sessionId: options.sessionId,
|
|
216
|
+
signal: options.signal,
|
|
217
|
+
});
|
|
198
218
|
}
|
|
199
219
|
|
|
200
220
|
/**
|
|
201
221
|
* Web search tool implementation.
|
|
202
222
|
*
|
|
203
223
|
* Supports Anthropic, Perplexity, Exa, Brave, Jina, Kimi, Gemini, Codex, Z.AI, SearXNG, and Synthetic providers with automatic fallback.
|
|
204
|
-
* Session is accepted for interface consistency but not used.
|
|
205
224
|
*/
|
|
206
225
|
export class WebSearchTool implements AgentTool<typeof webSearchSchema, SearchRenderDetails> {
|
|
207
226
|
readonly name = "web_search";
|
|
@@ -212,7 +231,10 @@ export class WebSearchTool implements AgentTool<typeof webSearchSchema, SearchRe
|
|
|
212
231
|
readonly loadMode = "discoverable";
|
|
213
232
|
readonly summary = "Search the web for up-to-date information";
|
|
214
233
|
|
|
215
|
-
|
|
234
|
+
#session: ToolSession;
|
|
235
|
+
|
|
236
|
+
constructor(session: ToolSession) {
|
|
237
|
+
this.#session = session;
|
|
216
238
|
this.description = prompt.render(webSearchDescription);
|
|
217
239
|
}
|
|
218
240
|
|
|
@@ -223,7 +245,9 @@ export class WebSearchTool implements AgentTool<typeof webSearchSchema, SearchRe
|
|
|
223
245
|
_onUpdate?: AgentToolUpdateCallback<SearchRenderDetails>,
|
|
224
246
|
_context?: AgentToolContext,
|
|
225
247
|
): Promise<AgentToolResult<SearchRenderDetails>> {
|
|
226
|
-
|
|
248
|
+
const authStorage = this.#session.authStorage ?? (await discoverAuthStorage());
|
|
249
|
+
const sessionId = this.#session.getSessionId?.() ?? undefined;
|
|
250
|
+
return executeSearch(_toolCallId, params, { authStorage, sessionId, signal });
|
|
227
251
|
}
|
|
228
252
|
}
|
|
229
253
|
|
|
@@ -238,10 +262,12 @@ export const webSearchCustomTool: CustomTool<typeof webSearchSchema, SearchRende
|
|
|
238
262
|
toolCallId: string,
|
|
239
263
|
params: SearchToolParams,
|
|
240
264
|
_onUpdate,
|
|
241
|
-
|
|
265
|
+
ctx: CustomToolContext,
|
|
242
266
|
signal?: AbortSignal,
|
|
243
267
|
) {
|
|
244
|
-
|
|
268
|
+
const authStorage = ctx.modelRegistry?.authStorage ?? (await discoverAuthStorage());
|
|
269
|
+
const sessionId = ctx.sessionManager.getSessionId();
|
|
270
|
+
return executeSearch(toolCallId, params, { authStorage, sessionId, signal });
|
|
245
271
|
},
|
|
246
272
|
|
|
247
273
|
renderCall(args: SearchToolParams, options: RenderResultOptions, theme: Theme) {
|