@oh-my-pi/pi-coding-agent 12.18.3 → 12.19.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 +53 -0
- package/package.json +35 -27
- package/src/async/index.ts +1 -0
- package/src/async/job-manager.ts +341 -0
- package/src/cli/file-processor.ts +3 -3
- package/src/cli/list-models.ts +3 -17
- package/src/cli/stats-cli.ts +3 -22
- package/src/cli/web-search-cli.ts +8 -16
- package/src/commit/agentic/agent.ts +6 -9
- package/src/commit/agentic/index.ts +44 -50
- package/src/commit/agentic/state.ts +0 -9
- package/src/commit/agentic/tools/propose-commit.ts +1 -30
- package/src/commit/agentic/tools/schemas.ts +31 -0
- package/src/commit/agentic/tools/split-commit.ts +1 -30
- package/src/commit/agentic/validation.ts +1 -18
- package/src/commit/analysis/conventional.ts +3 -50
- package/src/commit/analysis/summary.ts +2 -13
- package/src/commit/changelog/detect.ts +4 -1
- package/src/commit/changelog/generate.ts +2 -25
- package/src/commit/changelog/index.ts +1 -2
- package/src/commit/cli.ts +4 -12
- package/src/commit/map-reduce/reduce-phase.ts +2 -43
- package/src/commit/pipeline.ts +7 -15
- package/src/commit/utils.ts +44 -0
- package/src/config/prompt-templates.ts +1 -81
- package/src/config/settings-schema.ts +20 -1
- package/src/config.ts +2 -3
- package/src/debug/index.ts +1 -6
- package/src/debug/system-info.ts +2 -6
- package/src/discovery/builtin.ts +5 -9
- package/src/discovery/helpers.ts +0 -26
- package/src/discovery/ssh.ts +1 -8
- package/src/exa/company.ts +8 -39
- package/src/exa/factory.ts +64 -0
- package/src/exa/index.ts +0 -16
- package/src/exa/linkedin.ts +8 -39
- package/src/exa/mcp-client.ts +0 -64
- package/src/exa/researcher.ts +17 -59
- package/src/exa/search.ts +30 -154
- package/src/extensibility/custom-tools/loader.ts +3 -41
- package/src/extensibility/extensions/loader.ts +2 -9
- package/src/extensibility/hooks/loader.ts +3 -20
- package/src/extensibility/hooks/runner.ts +3 -19
- package/src/extensibility/plugins/installer.ts +2 -1
- package/src/extensibility/plugins/loader.ts +29 -117
- package/src/extensibility/skills.ts +2 -89
- package/src/extensibility/slash-commands.ts +1 -63
- package/src/extensibility/utils.ts +38 -0
- package/src/index.ts +9 -25
- package/src/internal-urls/index.ts +1 -0
- package/src/internal-urls/jobs-protocol.ts +118 -0
- package/src/ipy/kernel.ts +2 -0
- package/src/lsp/config.ts +1 -5
- package/src/lsp/lspmux.ts +0 -17
- package/src/lsp/utils.ts +2 -24
- package/src/main.ts +16 -24
- package/src/mcp/client.ts +1 -46
- package/src/mcp/render.ts +8 -1
- package/src/mcp/tool-cache.ts +1 -5
- package/src/mcp/transports/http.ts +2 -7
- package/src/mcp/transports/stdio.ts +2 -7
- package/src/modes/components/bash-execution.ts +2 -16
- package/src/modes/components/extensions/inspector-panel.ts +8 -18
- package/src/modes/components/footer.ts +10 -50
- package/src/modes/components/model-selector.ts +2 -21
- package/src/modes/components/python-execution.ts +2 -16
- package/src/modes/components/settings-selector.ts +1 -10
- package/src/modes/components/status-line/segments.ts +8 -25
- package/src/modes/components/status-line.ts +14 -31
- package/src/modes/components/tool-execution.ts +8 -2
- package/src/modes/controllers/command-controller.ts +71 -30
- package/src/modes/controllers/event-controller.ts +34 -4
- package/src/modes/controllers/mcp-command-controller.ts +3 -34
- package/src/modes/controllers/selector-controller.ts +2 -2
- package/src/modes/controllers/ssh-command-controller.ts +3 -34
- package/src/modes/interactive-mode.ts +6 -2
- package/src/modes/rpc/rpc-client.ts +1 -5
- package/src/modes/shared.ts +73 -0
- package/src/modes/types.ts +1 -0
- package/src/modes/utils/ui-helpers.ts +26 -2
- package/src/patch/hashline.ts +6 -286
- package/src/patch/index.ts +6 -57
- package/src/patch/normalize.ts +22 -65
- package/src/patch/shared.ts +16 -16
- package/src/prompts/system/custom-system-prompt.md +0 -10
- package/src/prompts/system/system-prompt.md +69 -89
- package/src/prompts/tools/async-result.md +5 -0
- package/src/prompts/tools/bash.md +5 -0
- package/src/prompts/tools/cancel-job.md +7 -0
- package/src/prompts/tools/hashline.md +0 -16
- package/src/prompts/tools/poll-jobs.md +7 -0
- package/src/prompts/tools/task.md +4 -0
- package/src/sdk.ts +70 -6
- package/src/session/agent-session.ts +43 -6
- package/src/session/agent-storage.ts +69 -278
- package/src/session/auth-storage.ts +14 -1430
- package/src/session/session-manager.ts +69 -5
- package/src/session/session-storage.ts +1 -5
- package/src/session/streaming-output.ts +637 -76
- package/src/slash-commands/builtin-registry.ts +8 -0
- package/src/ssh/connection-manager.ts +4 -12
- package/src/ssh/sshfs-mount.ts +3 -7
- package/src/ssh/utils.ts +8 -0
- package/src/system-prompt.ts +24 -90
- package/src/task/executor.ts +11 -1
- package/src/task/index.ts +258 -13
- package/src/task/parallel.ts +32 -0
- package/src/task/render.ts +15 -7
- package/src/task/types.ts +5 -0
- package/src/tools/ask.ts +4 -7
- package/src/tools/bash-interactive.ts +4 -5
- package/src/tools/bash.ts +125 -41
- package/src/tools/cancel-job.ts +93 -0
- package/src/tools/fetch.ts +7 -27
- package/src/tools/find.ts +3 -3
- package/src/tools/gemini-image.ts +15 -14
- package/src/tools/grep.ts +3 -3
- package/src/tools/index.ts +13 -29
- package/src/tools/json-tree.ts +12 -1
- package/src/tools/jtd-to-json-schema.ts +10 -74
- package/src/tools/jtd-to-typescript.ts +10 -72
- package/src/tools/jtd-utils.ts +102 -0
- package/src/tools/notebook.ts +4 -9
- package/src/tools/output-meta.ts +52 -26
- package/src/tools/path-utils.ts +13 -7
- package/src/tools/poll-jobs.ts +178 -0
- package/src/tools/python.ts +32 -35
- package/src/tools/read.ts +61 -82
- package/src/tools/render-utils.ts +8 -159
- package/src/tools/ssh.ts +7 -20
- package/src/tools/submit-result.ts +1 -1
- package/src/tools/tool-errors.ts +0 -30
- package/src/tools/tool-result.ts +1 -2
- package/src/tools/write.ts +8 -10
- package/src/tui/code-cell.ts +8 -3
- package/src/tui/status-line.ts +4 -4
- package/src/tui/types.ts +0 -1
- package/src/tui/utils.ts +1 -14
- package/src/utils/command-args.ts +76 -0
- package/src/utils/file-mentions.ts +15 -19
- package/src/utils/frontmatter.ts +5 -10
- package/src/utils/shell-snapshot.ts +0 -11
- package/src/utils/title-generator.ts +0 -12
- package/src/web/scrapers/artifacthub.ts +7 -16
- package/src/web/scrapers/arxiv.ts +3 -8
- package/src/web/scrapers/aur.ts +8 -22
- package/src/web/scrapers/biorxiv.ts +5 -14
- package/src/web/scrapers/bluesky.ts +13 -36
- package/src/web/scrapers/brew.ts +5 -10
- package/src/web/scrapers/cheatsh.ts +2 -12
- package/src/web/scrapers/chocolatey.ts +63 -26
- package/src/web/scrapers/choosealicense.ts +3 -18
- package/src/web/scrapers/cisa-kev.ts +4 -18
- package/src/web/scrapers/clojars.ts +6 -33
- package/src/web/scrapers/coingecko.ts +25 -33
- package/src/web/scrapers/crates-io.ts +7 -26
- package/src/web/scrapers/crossref.ts +4 -18
- package/src/web/scrapers/devto.ts +11 -41
- package/src/web/scrapers/discogs.ts +7 -10
- package/src/web/scrapers/discourse.ts +6 -31
- package/src/web/scrapers/dockerhub.ts +12 -35
- package/src/web/scrapers/fdroid.ts +8 -33
- package/src/web/scrapers/firefox-addons.ts +10 -34
- package/src/web/scrapers/flathub.ts +7 -24
- package/src/web/scrapers/github-gist.ts +2 -12
- package/src/web/scrapers/github.ts +9 -47
- package/src/web/scrapers/gitlab.ts +130 -185
- package/src/web/scrapers/go-pkg.ts +12 -22
- package/src/web/scrapers/hackage.ts +88 -43
- package/src/web/scrapers/hackernews.ts +25 -45
- package/src/web/scrapers/hex.ts +19 -36
- package/src/web/scrapers/huggingface.ts +26 -91
- package/src/web/scrapers/iacr.ts +3 -8
- package/src/web/scrapers/jetbrains-marketplace.ts +9 -20
- package/src/web/scrapers/lemmy.ts +5 -23
- package/src/web/scrapers/lobsters.ts +16 -28
- package/src/web/scrapers/mastodon.ts +24 -43
- package/src/web/scrapers/maven.ts +6 -21
- package/src/web/scrapers/mdn.ts +7 -11
- package/src/web/scrapers/metacpan.ts +9 -41
- package/src/web/scrapers/musicbrainz.ts +4 -28
- package/src/web/scrapers/npm.ts +8 -25
- package/src/web/scrapers/nuget.ts +14 -37
- package/src/web/scrapers/nvd.ts +6 -28
- package/src/web/scrapers/ollama.ts +7 -34
- package/src/web/scrapers/open-vsx.ts +5 -19
- package/src/web/scrapers/opencorporates.ts +30 -14
- package/src/web/scrapers/openlibrary.ts +49 -33
- package/src/web/scrapers/orcid.ts +4 -18
- package/src/web/scrapers/osv.ts +7 -24
- package/src/web/scrapers/packagist.ts +9 -24
- package/src/web/scrapers/pub-dev.ts +7 -50
- package/src/web/scrapers/pubmed.ts +54 -21
- package/src/web/scrapers/pypi.ts +8 -26
- package/src/web/scrapers/rawg.ts +11 -19
- package/src/web/scrapers/readthedocs.ts +4 -9
- package/src/web/scrapers/reddit.ts +5 -15
- package/src/web/scrapers/repology.ts +8 -20
- package/src/web/scrapers/rfc.ts +5 -14
- package/src/web/scrapers/rubygems.ts +6 -21
- package/src/web/scrapers/searchcode.ts +8 -36
- package/src/web/scrapers/sec-edgar.ts +4 -18
- package/src/web/scrapers/semantic-scholar.ts +15 -35
- package/src/web/scrapers/snapcraft.ts +5 -19
- package/src/web/scrapers/sourcegraph.ts +5 -43
- package/src/web/scrapers/spdx.ts +4 -18
- package/src/web/scrapers/spotify.ts +4 -23
- package/src/web/scrapers/stackoverflow.ts +8 -13
- package/src/web/scrapers/terraform.ts +9 -37
- package/src/web/scrapers/tldr.ts +3 -7
- package/src/web/scrapers/twitter.ts +3 -7
- package/src/web/scrapers/types.ts +105 -27
- package/src/web/scrapers/utils.ts +97 -103
- package/src/web/scrapers/vimeo.ts +7 -27
- package/src/web/scrapers/vscode-marketplace.ts +8 -17
- package/src/web/scrapers/w3c.ts +6 -14
- package/src/web/scrapers/wikidata.ts +5 -19
- package/src/web/scrapers/wikipedia.ts +2 -12
- package/src/web/scrapers/youtube.ts +5 -34
- package/src/web/search/index.ts +0 -9
- package/src/web/search/providers/anthropic.ts +3 -2
- package/src/web/search/providers/brave.ts +3 -18
- package/src/web/search/providers/exa.ts +1 -12
- package/src/web/search/providers/kimi.ts +5 -44
- package/src/web/search/providers/perplexity.ts +1 -12
- package/src/web/search/providers/synthetic.ts +3 -26
- package/src/web/search/providers/utils.ts +36 -0
- package/src/web/search/providers/zai.ts +9 -50
- package/src/web/search/types.ts +0 -28
- package/src/web/search/utils.ts +17 -0
- package/src/tools/output-utils.ts +0 -63
- package/src/tools/truncate.ts +0 -385
- package/src/web/search/auth.ts +0 -178
|
@@ -8,6 +8,7 @@ import { getEnvApiKey } from "@oh-my-pi/pi-ai";
|
|
|
8
8
|
import { findApiKey as findExaKey } from "../../../exa/mcp-client";
|
|
9
9
|
import type { SearchResponse, SearchSource } from "../../../web/search/types";
|
|
10
10
|
import { SearchProviderError } from "../../../web/search/types";
|
|
11
|
+
import { dateToAgeSeconds } from "../utils";
|
|
11
12
|
import type { SearchParams } from "./base";
|
|
12
13
|
import { SearchProvider } from "./base";
|
|
13
14
|
|
|
@@ -88,18 +89,6 @@ async function callExaSearch(apiKey: string, params: ExaSearchParams): Promise<E
|
|
|
88
89
|
return response.json() as Promise<ExaSearchResponse>;
|
|
89
90
|
}
|
|
90
91
|
|
|
91
|
-
/** Calculate age in seconds from ISO date string */
|
|
92
|
-
function dateToAgeSeconds(dateStr: string | null | undefined): number | undefined {
|
|
93
|
-
if (!dateStr) return undefined;
|
|
94
|
-
try {
|
|
95
|
-
const date = new Date(dateStr);
|
|
96
|
-
if (Number.isNaN(date.getTime())) return undefined;
|
|
97
|
-
return Math.floor((Date.now() - date.getTime()) / 1000);
|
|
98
|
-
} catch {
|
|
99
|
-
return undefined;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
92
|
/** Execute Exa web search */
|
|
104
93
|
export async function searchExa(params: ExaSearchParams): Promise<SearchResponse> {
|
|
105
94
|
const apiKey = getEnvApiKey("exa");
|
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { getEnvApiKey } from "@oh-my-pi/pi-ai";
|
|
8
8
|
import { $env } from "@oh-my-pi/pi-utils";
|
|
9
|
-
import { getAgentDbPath } from "@oh-my-pi/pi-utils/dirs";
|
|
10
|
-
import { AgentStorage } from "../../../session/agent-storage";
|
|
11
9
|
import type { SearchResponse, SearchSource } from "../../../web/search/types";
|
|
12
10
|
import { SearchProviderError } from "../../../web/search/types";
|
|
11
|
+
import { clampNumResults, dateToAgeSeconds } from "../utils";
|
|
13
12
|
import type { SearchParams } from "./base";
|
|
14
13
|
import { SearchProvider } from "./base";
|
|
14
|
+
import { findCredential } from "./utils";
|
|
15
15
|
|
|
16
16
|
const KIMI_SEARCH_URL = "https://api.kimi.com/coding/v1/search";
|
|
17
17
|
|
|
@@ -51,53 +51,14 @@ function resolveBaseUrl(): string {
|
|
|
51
51
|
return asTrimmed($env.MOONSHOT_SEARCH_BASE_URL) ?? asTrimmed($env.KIMI_SEARCH_BASE_URL) ?? KIMI_SEARCH_URL;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
if (!value || Number.isNaN(value)) return DEFAULT_NUM_RESULTS;
|
|
56
|
-
return Math.min(MAX_NUM_RESULTS, Math.max(1, value));
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
function dateToAgeSeconds(dateStr: string | undefined): number | undefined {
|
|
60
|
-
if (!dateStr) return undefined;
|
|
61
|
-
try {
|
|
62
|
-
const date = new Date(dateStr);
|
|
63
|
-
if (Number.isNaN(date.getTime())) return undefined;
|
|
64
|
-
return Math.floor((Date.now() - date.getTime()) / 1000);
|
|
65
|
-
} catch {
|
|
66
|
-
return undefined;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Find Kimi search credentials from environment or agent.db credentials.
|
|
72
|
-
* Priority: MOONSHOT_SEARCH_API_KEY / KIMI_SEARCH_API_KEY / MOONSHOT_API_KEY, then agent.db providers "moonshot" or "kimi-code".
|
|
73
|
-
*/
|
|
54
|
+
/** Find Kimi search credentials from environment or agent.db credentials. */
|
|
74
55
|
async function findApiKey(): Promise<string | null> {
|
|
75
56
|
const envKey =
|
|
76
57
|
asTrimmed($env.MOONSHOT_SEARCH_API_KEY) ??
|
|
77
58
|
asTrimmed($env.KIMI_SEARCH_API_KEY) ??
|
|
78
59
|
getEnvApiKey("moonshot") ??
|
|
79
60
|
null;
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
try {
|
|
83
|
-
const storage = await AgentStorage.open(getAgentDbPath());
|
|
84
|
-
for (const provider of ["moonshot", "kimi-code"] as const) {
|
|
85
|
-
const records = storage.listAuthCredentials(provider);
|
|
86
|
-
for (const record of records) {
|
|
87
|
-
const credential = record.credential;
|
|
88
|
-
if (credential.type === "api_key" && credential.key.trim().length > 0) {
|
|
89
|
-
return credential.key;
|
|
90
|
-
}
|
|
91
|
-
if (credential.type === "oauth" && credential.access.trim().length > 0) {
|
|
92
|
-
return credential.access;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
} catch {
|
|
97
|
-
return null;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
return null;
|
|
61
|
+
return findCredential(envKey, "moonshot", "kimi-code");
|
|
101
62
|
}
|
|
102
63
|
|
|
103
64
|
async function callKimiSearch(
|
|
@@ -143,7 +104,7 @@ export async function searchKimi(params: KimiSearchParams): Promise<SearchRespon
|
|
|
143
104
|
);
|
|
144
105
|
}
|
|
145
106
|
|
|
146
|
-
const limit = clampNumResults(params.num_results);
|
|
107
|
+
const limit = clampNumResults(params.num_results, DEFAULT_NUM_RESULTS, MAX_NUM_RESULTS);
|
|
147
108
|
const { response, requestId } = await callKimiSearch(apiKey, {
|
|
148
109
|
query: params.query,
|
|
149
110
|
limit,
|
|
@@ -19,6 +19,7 @@ import type {
|
|
|
19
19
|
SearchSource,
|
|
20
20
|
} from "../../../web/search/types";
|
|
21
21
|
import { SearchProviderError } from "../../../web/search/types";
|
|
22
|
+
import { dateToAgeSeconds } from "../utils";
|
|
22
23
|
import type { SearchParams } from "./base";
|
|
23
24
|
import { SearchProvider } from "./base";
|
|
24
25
|
|
|
@@ -383,18 +384,6 @@ async function callPerplexityOAuth(
|
|
|
383
384
|
};
|
|
384
385
|
}
|
|
385
386
|
|
|
386
|
-
/** Calculate age in seconds from ISO date string */
|
|
387
|
-
function dateToAgeSeconds(dateStr: string | null | undefined): number | undefined {
|
|
388
|
-
if (!dateStr) return undefined;
|
|
389
|
-
try {
|
|
390
|
-
const date = new Date(dateStr);
|
|
391
|
-
if (Number.isNaN(date.getTime())) return undefined;
|
|
392
|
-
return Math.floor((Date.now() - date.getTime()) / 1000);
|
|
393
|
-
} catch {
|
|
394
|
-
return undefined;
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
|
|
398
387
|
function messageContentToText(content: PerplexityMessageOutput["content"]): string {
|
|
399
388
|
if (!content) return "";
|
|
400
389
|
if (typeof content === "string") return content;
|
|
@@ -6,12 +6,11 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { getEnvApiKey } from "@oh-my-pi/pi-ai";
|
|
9
|
-
import { getAgentDbPath } from "@oh-my-pi/pi-utils/dirs";
|
|
10
|
-
import { AgentStorage } from "../../../session/agent-storage";
|
|
11
9
|
import type { SearchResponse, SearchSource } from "../../../web/search/types";
|
|
12
10
|
import { SearchProviderError } from "../../../web/search/types";
|
|
13
11
|
import type { SearchParams } from "./base";
|
|
14
12
|
import { SearchProvider } from "./base";
|
|
13
|
+
import { findCredential } from "./utils";
|
|
15
14
|
|
|
16
15
|
const SYNTHETIC_SEARCH_URL = "https://api.synthetic.new/v2/search";
|
|
17
16
|
|
|
@@ -26,31 +25,9 @@ interface SyntheticSearchResponse {
|
|
|
26
25
|
results: SyntheticSearchResult[];
|
|
27
26
|
}
|
|
28
27
|
|
|
29
|
-
/**
|
|
30
|
-
* Find Synthetic API key from environment or agent.db credentials.
|
|
31
|
-
* Priority: SYNTHETIC_API_KEY env var, then credentials stored under provider "synthetic".
|
|
32
|
-
*/
|
|
28
|
+
/** Find Synthetic API key from environment or agent.db credentials. */
|
|
33
29
|
export async function findApiKey(): Promise<string | null> {
|
|
34
|
-
|
|
35
|
-
if (envKey) return envKey;
|
|
36
|
-
|
|
37
|
-
try {
|
|
38
|
-
const storage = await AgentStorage.open(getAgentDbPath());
|
|
39
|
-
const records = storage.listAuthCredentials("synthetic");
|
|
40
|
-
for (const record of records) {
|
|
41
|
-
const credential = record.credential;
|
|
42
|
-
if (credential.type === "api_key" && credential.key.trim().length > 0) {
|
|
43
|
-
return credential.key;
|
|
44
|
-
}
|
|
45
|
-
if (credential.type === "oauth" && credential.access.trim().length > 0) {
|
|
46
|
-
return credential.access;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
} catch {
|
|
50
|
-
return null;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return null;
|
|
30
|
+
return findCredential(getEnvApiKey("synthetic"), "synthetic");
|
|
54
31
|
}
|
|
55
32
|
|
|
56
33
|
/** Call Synthetic search API. */
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { getAgentDbPath } from "@oh-my-pi/pi-utils/dirs";
|
|
2
|
+
import { AgentStorage } from "../../../session/agent-storage";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Search for an API credential by checking an env-derived key first,
|
|
6
|
+
* then falling back to agent.db stored credentials for the given providers.
|
|
7
|
+
*
|
|
8
|
+
* @param envKey - Pre-resolved environment variable value (or null)
|
|
9
|
+
* @param storageProviders - Provider names to look up in AgentStorage
|
|
10
|
+
*/
|
|
11
|
+
export async function findCredential(
|
|
12
|
+
envKey: string | null | undefined,
|
|
13
|
+
...storageProviders: string[]
|
|
14
|
+
): Promise<string | null> {
|
|
15
|
+
if (envKey) return envKey;
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
const storage = await AgentStorage.open(getAgentDbPath());
|
|
19
|
+
for (const provider of storageProviders) {
|
|
20
|
+
const records = storage.listAuthCredentials(provider);
|
|
21
|
+
for (const record of records) {
|
|
22
|
+
const credential = record.credential;
|
|
23
|
+
if (credential.type === "api_key" && credential.key.trim().length > 0) {
|
|
24
|
+
return credential.key;
|
|
25
|
+
}
|
|
26
|
+
if (credential.type === "oauth" && credential.access.trim().length > 0) {
|
|
27
|
+
return credential.access;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
} catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
@@ -5,13 +5,13 @@
|
|
|
5
5
|
* the unified SearchResponse shape used by the web search tool.
|
|
6
6
|
*/
|
|
7
7
|
import { getEnvApiKey } from "@oh-my-pi/pi-ai";
|
|
8
|
-
import {
|
|
9
|
-
import { AgentStorage } from "../../../session/agent-storage";
|
|
10
|
-
|
|
8
|
+
import { asRecord, asString } from "../../../web/scrapers/utils";
|
|
11
9
|
import type { SearchResponse, SearchSource } from "../../../web/search/types";
|
|
12
10
|
import { SearchProviderError } from "../../../web/search/types";
|
|
11
|
+
import { dateToAgeSeconds } from "../utils";
|
|
13
12
|
import type { SearchParams } from "./base";
|
|
14
13
|
import { SearchProvider } from "./base";
|
|
14
|
+
import { findCredential } from "./utils";
|
|
15
15
|
|
|
16
16
|
const ZAI_MCP_URL = "https://api.z.ai/api/mcp/web_search_prime/mcp";
|
|
17
17
|
const ZAI_TOOL_NAME = "webSearchPrime";
|
|
@@ -50,50 +50,9 @@ interface JsonRpcPayload {
|
|
|
50
50
|
error?: JsonRpcError;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
return typeof value === "object" && value !== null ? (value as Record<string, unknown>) : null;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function asString(value: unknown): string | undefined {
|
|
58
|
-
return typeof value === "string" && value.trim().length > 0 ? value : undefined;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Finds Z.AI API credentials from environment or saved auth storage.
|
|
63
|
-
* Priority: ZAI_API_KEY env var, then credentials stored under provider "zai".
|
|
64
|
-
*/
|
|
53
|
+
/** Find Z.AI API credentials from environment or saved auth storage. */
|
|
65
54
|
export async function findApiKey(): Promise<string | null> {
|
|
66
|
-
|
|
67
|
-
if (envKey) return envKey;
|
|
68
|
-
|
|
69
|
-
try {
|
|
70
|
-
const storage = await AgentStorage.open(getAgentDbPath());
|
|
71
|
-
const records = storage.listAuthCredentials("zai");
|
|
72
|
-
for (const record of records) {
|
|
73
|
-
const credential = record.credential;
|
|
74
|
-
if (credential.type === "api_key" && credential.key.trim().length > 0) {
|
|
75
|
-
return credential.key;
|
|
76
|
-
}
|
|
77
|
-
if (credential.type === "oauth" && credential.access.trim().length > 0) {
|
|
78
|
-
return credential.access;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
} catch {
|
|
82
|
-
return null;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return null;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function dateToAgeSeconds(dateStr: string | undefined): number | undefined {
|
|
89
|
-
if (!dateStr) return undefined;
|
|
90
|
-
try {
|
|
91
|
-
const date = new Date(dateStr);
|
|
92
|
-
if (Number.isNaN(date.getTime())) return undefined;
|
|
93
|
-
return Math.floor((Date.now() - date.getTime()) / 1000);
|
|
94
|
-
} catch {
|
|
95
|
-
return undefined;
|
|
96
|
-
}
|
|
55
|
+
return findCredential(getEnvApiKey("zai"), "zai");
|
|
97
56
|
}
|
|
98
57
|
|
|
99
58
|
async function callZaiTool(apiKey: string, args: Record<string, unknown>): Promise<unknown> {
|
|
@@ -172,7 +131,7 @@ async function callZaiTool(apiKey: string, args: Record<string, unknown>): Promi
|
|
|
172
131
|
const content = Array.isArray(resultRecord.content) ? resultRecord.content : [];
|
|
173
132
|
const errorText = content
|
|
174
133
|
.map(item => asString(asRecord(item)?.text))
|
|
175
|
-
.filter((text): text is string => text
|
|
134
|
+
.filter((text): text is string => text != null)
|
|
176
135
|
.join("\n")
|
|
177
136
|
.trim();
|
|
178
137
|
const statusMatch = errorText.match(/MCP error\s*(-?\d+)/i);
|
|
@@ -298,10 +257,10 @@ function toSources(results: ZaiSearchResult[]): SearchSource[] {
|
|
|
298
257
|
sources.push({
|
|
299
258
|
title: asString(result.title) ?? url,
|
|
300
259
|
url,
|
|
301
|
-
snippet: asString(result.content),
|
|
302
|
-
publishedDate,
|
|
260
|
+
snippet: asString(result.content) ?? undefined,
|
|
261
|
+
publishedDate: publishedDate ?? undefined,
|
|
303
262
|
ageSeconds: dateToAgeSeconds(publishedDate),
|
|
304
|
-
author: asString(result.media),
|
|
263
|
+
author: asString(result.media) ?? undefined,
|
|
305
264
|
});
|
|
306
265
|
}
|
|
307
266
|
return sources;
|
package/src/web/search/types.ts
CHANGED
|
@@ -81,34 +81,6 @@ export class SearchProviderError extends Error {
|
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
/** Auth configuration for Anthropic */
|
|
85
|
-
export interface AnthropicAuthConfig {
|
|
86
|
-
apiKey: string;
|
|
87
|
-
baseUrl: string;
|
|
88
|
-
isOAuth: boolean;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/** models.json structure for provider resolution */
|
|
92
|
-
export interface ModelsJson {
|
|
93
|
-
providers?: Record<
|
|
94
|
-
string,
|
|
95
|
-
{
|
|
96
|
-
baseUrl?: string;
|
|
97
|
-
apiKey?: string;
|
|
98
|
-
api?: string;
|
|
99
|
-
}
|
|
100
|
-
>;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/** OAuth credential for Anthropic API access */
|
|
104
|
-
export interface AnthropicOAuthCredential {
|
|
105
|
-
type: "oauth";
|
|
106
|
-
access: string;
|
|
107
|
-
refresh?: string;
|
|
108
|
-
/** Expiry timestamp in milliseconds */
|
|
109
|
-
expires: number;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
84
|
/** Anthropic API response types */
|
|
113
85
|
export interface AnthropicSearchResult {
|
|
114
86
|
type: "web_search_result";
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Calculate age in seconds from an ISO date string. Returns undefined on invalid input. */
|
|
2
|
+
export function dateToAgeSeconds(dateStr: string | null | undefined): number | undefined {
|
|
3
|
+
if (!dateStr) return undefined;
|
|
4
|
+
try {
|
|
5
|
+
const date = new Date(dateStr);
|
|
6
|
+
if (Number.isNaN(date.getTime())) return undefined;
|
|
7
|
+
return Math.floor((Date.now() - date.getTime()) / 1000);
|
|
8
|
+
} catch {
|
|
9
|
+
return undefined;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/** Clamp a result count to [1, maxVal], returning defaultVal when value is absent or NaN. */
|
|
14
|
+
export function clampNumResults(value: number | undefined, defaultVal: number, maxVal: number): number {
|
|
15
|
+
if (!value || Number.isNaN(value)) return defaultVal;
|
|
16
|
+
return Math.min(maxVal, Math.max(1, value));
|
|
17
|
+
}
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { ArtifactManager } from "../session/artifacts";
|
|
2
|
-
import type { ToolSession } from ".";
|
|
3
|
-
|
|
4
|
-
export interface TailBuffer {
|
|
5
|
-
append(chunk: string): void;
|
|
6
|
-
text(): string;
|
|
7
|
-
bytes(): number;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export function createTailBuffer(maxBytes: number): TailBuffer {
|
|
11
|
-
let buffer = "";
|
|
12
|
-
let bufferBytes = 0;
|
|
13
|
-
|
|
14
|
-
const append = (text: string) => {
|
|
15
|
-
if (!text) return;
|
|
16
|
-
const chunkBytes = Buffer.byteLength(text, "utf-8");
|
|
17
|
-
buffer += text;
|
|
18
|
-
bufferBytes += chunkBytes;
|
|
19
|
-
|
|
20
|
-
if (bufferBytes > maxBytes) {
|
|
21
|
-
const buf = Buffer.from(buffer, "utf-8");
|
|
22
|
-
let start = Math.max(0, buf.length - maxBytes);
|
|
23
|
-
while (start < buf.length && (buf[start] & 0xc0) === 0x80) {
|
|
24
|
-
start++;
|
|
25
|
-
}
|
|
26
|
-
buffer = buf.subarray(start).toString("utf-8");
|
|
27
|
-
bufferBytes = Buffer.byteLength(buffer, "utf-8");
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
return {
|
|
32
|
-
append,
|
|
33
|
-
text: () => buffer,
|
|
34
|
-
bytes: () => bufferBytes,
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function getArtifactManager(session: ToolSession): ArtifactManager | null {
|
|
39
|
-
if (session.artifactManager) {
|
|
40
|
-
return session.artifactManager;
|
|
41
|
-
}
|
|
42
|
-
const sessionFile = session.getSessionFile();
|
|
43
|
-
if (!sessionFile) {
|
|
44
|
-
return null;
|
|
45
|
-
}
|
|
46
|
-
const manager = new ArtifactManager(sessionFile);
|
|
47
|
-
session.artifactManager = manager;
|
|
48
|
-
return manager;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export async function allocateOutputArtifact(
|
|
52
|
-
session: ToolSession,
|
|
53
|
-
toolType: string,
|
|
54
|
-
): Promise<{ artifactPath?: string; artifactId?: string }> {
|
|
55
|
-
const manager = getArtifactManager(session);
|
|
56
|
-
if (!manager) return {};
|
|
57
|
-
try {
|
|
58
|
-
const allocation = await manager.allocatePath(toolType);
|
|
59
|
-
return { artifactPath: allocation.path, artifactId: allocation.id };
|
|
60
|
-
} catch {
|
|
61
|
-
return {};
|
|
62
|
-
}
|
|
63
|
-
}
|