@openclaw/moonshot-provider 2026.10.5-beta.4 → 2026.10.10-beta.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.
@@ -2,7 +2,7 @@ import { createLazyRuntimeMethod, createLazyRuntimeModule } from "openclaw/plugi
2
2
  import { createWebSearchProviderContractFields } from "openclaw/plugin-sdk/provider-web-search-config-contract";
3
3
  //#region extensions/moonshot/src/kimi-web-search-provider.ts
4
4
  const KIMI_CREDENTIAL_PATH = "plugins.entries.moonshot.config.webSearch.apiKey";
5
- const loadKimiWebSearchProviderRuntime = createLazyRuntimeModule(() => import("./kimi-web-search-provider.runtime-f2NJlw96.mjs"));
5
+ const loadKimiWebSearchProviderRuntime = createLazyRuntimeModule(() => import("./kimi-web-search-provider.runtime-Je75Zave.mjs"));
6
6
  const KimiSearchSchema = {
7
7
  type: "object",
8
8
  properties: {
@@ -61,7 +61,7 @@ function createKimiWebSearchProvider() {
61
61
  }),
62
62
  runSetup: createLazyRuntimeMethod(loadKimiWebSearchProviderRuntime, (runtime) => runtime.runKimiSearchProviderSetup),
63
63
  createTool: (ctx) => ({
64
- description: "Search the web using Kimi by Moonshot. Returns AI-synthesized answers with citations from native $web_search.",
64
+ description: "Search the web using Kimi by Moonshot. Returns structured results with titles, URLs, and descriptions.",
65
65
  parameters: KimiSearchSchema,
66
66
  execute: async (args, context) => {
67
67
  const { executeKimiWebSearchProviderTool } = await loadKimiWebSearchProviderRuntime();
@@ -0,0 +1,142 @@
1
+ import { MOONSHOT_BASE_URL, MOONSHOT_CN_BASE_URL, isNativeMoonshotBaseUrl } from "../provider-policy-api.js";
2
+ import "../provider-catalog.js";
3
+ import { asNonArrayRecord, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
4
+ import { createProviderHttpError, normalizeBaseUrl, readProviderJsonObjectResponse } from "openclaw/plugin-sdk/provider-http";
5
+ import { DEFAULT_SEARCH_COUNT, MAX_SEARCH_COUNT, buildSearchCacheKey, buildUnsupportedSearchFilterResponse, mergeScopedSearchConfig, readCachedSearchPayload, readConfiguredSecretString, readPositiveIntegerParam, readProviderEnvValue, readStringParam, resolveProviderWebSearchPluginConfig, resolveSearchCacheTtlMs, resolveSearchCount, resolveSearchTimeoutSeconds, resolveSiteName, setProviderWebSearchPluginConfigValue, withTrustedWebSearchEndpoint, wrapWebContent, writeCachedSearchPayload } from "openclaw/plugin-sdk/provider-web-search";
6
+ //#region extensions/moonshot/src/kimi-web-search-provider.runtime.ts
7
+ const DEFAULT_KIMI_BASE_URL = MOONSHOT_BASE_URL;
8
+ function resolveKimiApiKey(kimi) {
9
+ return readConfiguredSecretString(kimi?.apiKey, "plugins.entries.moonshot.config.webSearch.apiKey") ?? readProviderEnvValue(["KIMI_API_KEY", "MOONSHOT_API_KEY"]);
10
+ }
11
+ function resolveKimiBaseUrl(kimi, openClawConfig) {
12
+ const explicitBaseUrl = normalizeOptionalString(kimi?.baseUrl);
13
+ if (explicitBaseUrl) return normalizeBaseUrl(explicitBaseUrl) || DEFAULT_KIMI_BASE_URL;
14
+ const moonshotBaseUrl = openClawConfig?.models?.providers?.moonshot?.baseUrl;
15
+ if (typeof moonshotBaseUrl === "string") {
16
+ const normalizedMoonshotBaseUrl = normalizeBaseUrl(moonshotBaseUrl);
17
+ if (normalizedMoonshotBaseUrl && isNativeMoonshotBaseUrl(normalizedMoonshotBaseUrl)) return normalizedMoonshotBaseUrl;
18
+ }
19
+ return DEFAULT_KIMI_BASE_URL;
20
+ }
21
+ async function runKimiSearch(params) {
22
+ return withTrustedWebSearchEndpoint({
23
+ url: `${params.baseUrl}/tools/search`,
24
+ timeoutSeconds: params.timeoutSeconds,
25
+ signal: params.signal,
26
+ init: {
27
+ method: "POST",
28
+ headers: {
29
+ "Content-Type": "application/json",
30
+ Authorization: `Bearer ${params.apiKey}`
31
+ },
32
+ body: JSON.stringify({
33
+ text_query: params.query,
34
+ limit: params.count
35
+ })
36
+ }
37
+ }, async (res) => {
38
+ if (!res.ok) throw await createProviderHttpError(res, "Kimi API error");
39
+ const data = await readProviderJsonObjectResponse(res, "Kimi API error");
40
+ if (!Array.isArray(data.search_results)) throw new Error("Kimi API error: malformed JSON response");
41
+ return data.search_results.slice(0, params.count).map((entry) => {
42
+ const result = asNonArrayRecord(entry);
43
+ const title = normalizeOptionalString(result.title);
44
+ const url = normalizeOptionalString(result.url) ?? "";
45
+ const description = normalizeOptionalString(result.snippet);
46
+ return {
47
+ title: title ? wrapWebContent(title, "web_search") : "",
48
+ url,
49
+ description: description ? wrapWebContent(description, "web_search") : "",
50
+ published: normalizeKimiResultDate(result.date),
51
+ siteName: resolveSiteName(url) || void 0
52
+ };
53
+ });
54
+ });
55
+ }
56
+ function normalizeKimiResultDate(value) {
57
+ const date = normalizeOptionalString(value);
58
+ return date && /^\d{4}-\d{2}-\d{2}$/.test(date) ? date : void 0;
59
+ }
60
+ async function executeKimiWebSearchProviderTool(ctx, args, opts) {
61
+ opts?.signal?.throwIfAborted();
62
+ const searchConfig = mergeScopedSearchConfig(ctx.searchConfig, "kimi", resolveProviderWebSearchPluginConfig(ctx.config, "moonshot"));
63
+ const unsupportedResponse = buildUnsupportedSearchFilterResponse(args, "kimi");
64
+ if (unsupportedResponse) return unsupportedResponse;
65
+ const kimiConfig = asNonArrayRecord(searchConfig?.kimi);
66
+ const apiKey = resolveKimiApiKey(kimiConfig);
67
+ if (!apiKey) return {
68
+ error: "missing_kimi_api_key",
69
+ message: "web_search (kimi) needs a Moonshot API key. Set KIMI_API_KEY or MOONSHOT_API_KEY in the Gateway environment, or configure plugins.entries.moonshot.config.webSearch.apiKey. If you do not want to configure a search API key, use web_fetch for a specific URL or the browser tool for interactive pages.",
70
+ docs: "https://docs.openclaw.ai/tools/web"
71
+ };
72
+ const query = readStringParam(args, "query", { required: true });
73
+ const count = resolveSearchCount(readPositiveIntegerParam(args, "count", {
74
+ max: MAX_SEARCH_COUNT,
75
+ message: `count must be an integer from 1 to ${MAX_SEARCH_COUNT}.`
76
+ }) ?? searchConfig?.maxResults, DEFAULT_SEARCH_COUNT);
77
+ const baseUrl = resolveKimiBaseUrl(kimiConfig, ctx.config);
78
+ const cacheKey = buildSearchCacheKey([
79
+ "kimi",
80
+ query,
81
+ baseUrl,
82
+ count
83
+ ]);
84
+ const cacheTtlMs = resolveSearchCacheTtlMs(searchConfig);
85
+ const cached = readCachedSearchPayload(cacheKey, cacheTtlMs);
86
+ if (cached) return cached;
87
+ const start = Date.now();
88
+ const results = await runKimiSearch({
89
+ query,
90
+ count,
91
+ apiKey,
92
+ baseUrl,
93
+ timeoutSeconds: resolveSearchTimeoutSeconds(searchConfig),
94
+ signal: opts?.signal
95
+ });
96
+ opts?.signal?.throwIfAborted();
97
+ const payload = {
98
+ query,
99
+ provider: "kimi",
100
+ count: results.length,
101
+ tookMs: Date.now() - start,
102
+ externalContent: {
103
+ untrusted: true,
104
+ source: "web_search",
105
+ provider: "kimi",
106
+ wrapped: true
107
+ },
108
+ results
109
+ };
110
+ writeCachedSearchPayload(cacheKey, payload, cacheTtlMs);
111
+ return payload;
112
+ }
113
+ async function runKimiSearchProviderSetup(ctx) {
114
+ const existingPluginConfig = resolveProviderWebSearchPluginConfig(ctx.config, "moonshot");
115
+ const normalizedBaseUrl = normalizeBaseUrl(normalizeOptionalString(existingPluginConfig?.baseUrl));
116
+ const isCustomBaseUrl = normalizedBaseUrl && !isNativeMoonshotBaseUrl(normalizedBaseUrl);
117
+ const regionOptions = [];
118
+ if (isCustomBaseUrl) regionOptions.push({
119
+ value: normalizedBaseUrl,
120
+ label: `Keep current (${normalizedBaseUrl})`,
121
+ hint: "custom endpoint"
122
+ });
123
+ regionOptions.push({
124
+ value: MOONSHOT_BASE_URL,
125
+ label: "Moonshot API key (.ai)",
126
+ hint: "api.moonshot.ai"
127
+ }, {
128
+ value: MOONSHOT_CN_BASE_URL,
129
+ label: "Moonshot API key (.cn)",
130
+ hint: "api.moonshot.cn"
131
+ });
132
+ const baseUrl = await ctx.prompter.select({
133
+ message: "Kimi API region",
134
+ options: regionOptions,
135
+ initialValue: normalizedBaseUrl || "https://api.moonshot.ai/v1"
136
+ });
137
+ const next = { ...ctx.config };
138
+ setProviderWebSearchPluginConfigValue(next, "moonshot", "baseUrl", baseUrl);
139
+ return next;
140
+ }
141
+ //#endregion
142
+ export { executeKimiWebSearchProviderTool, runKimiSearchProviderSetup };
@@ -53,7 +53,7 @@ var openclaw_plugin_default = {
53
53
  "input": 3,
54
54
  "output": 15,
55
55
  "cacheRead": .3,
56
- "cacheWrite": 0
56
+ "cacheWrite": 3
57
57
  },
58
58
  "compat": {
59
59
  "supportsReasoningEffort": true,
@@ -139,8 +139,8 @@ var openclaw_plugin_default = {
139
139
  "help": "Kimi base URL override."
140
140
  },
141
141
  "webSearch.model": {
142
- "label": "Kimi Search Model",
143
- "help": "Kimi model override."
142
+ "label": "Kimi Search Model (Deprecated)",
143
+ "help": "Deprecated and ignored. Kimi web search uses the search endpoint without a chat model; this key can be removed."
144
144
  }
145
145
  },
146
146
  contracts: {
@@ -170,7 +170,10 @@ var openclaw_plugin_default = {
170
170
  "properties": {
171
171
  "apiKey": { "type": ["string", "object"] },
172
172
  "baseUrl": { "type": "string" },
173
- "model": { "type": "string" }
173
+ "model": {
174
+ "type": "string",
175
+ "description": "Deprecated and ignored. Kimi web search uses the search endpoint without a chat model."
176
+ }
174
177
  }
175
178
  } }
176
179
  }
package/dist/api.js CHANGED
@@ -1,3 +1,3 @@
1
1
  import { MOONSHOT_BASE_URL, MOONSHOT_CN_BASE_URL, isNativeMoonshotBaseUrl } from "./provider-policy-api.js";
2
- import { MOONSHOT_DEFAULT_MODEL_ID, MOONSHOT_DEFAULT_MODEL_REF, applyMoonshotNativeStreamingUsageCompat, buildMoonshotProvider } from "./provider-catalog.js";
3
- export { MOONSHOT_BASE_URL, MOONSHOT_CN_BASE_URL, MOONSHOT_DEFAULT_MODEL_ID, MOONSHOT_DEFAULT_MODEL_REF, applyMoonshotNativeStreamingUsageCompat, buildMoonshotProvider, isNativeMoonshotBaseUrl };
2
+ import { MOONSHOT_DEFAULT_MODEL_ID, MOONSHOT_DEFAULT_MODEL_REF, buildMoonshotProvider } from "./provider-catalog.js";
3
+ export { MOONSHOT_BASE_URL, MOONSHOT_CN_BASE_URL, MOONSHOT_DEFAULT_MODEL_ID, MOONSHOT_DEFAULT_MODEL_REF, buildMoonshotProvider, isNativeMoonshotBaseUrl };
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
- import { t as openclaw_plugin_default } from "./.setup/openclaw.plugin-DX_vVKdi.mjs";
1
+ import { t as openclaw_plugin_default } from "./.setup/openclaw.plugin-ByEFwJ6V.mjs";
2
2
  import { isMoonshotAlwaysThinkingModelId, isMoonshotK3NativeVideoRoute, resolveThinkingProfile } from "./provider-policy-api.js";
3
3
  import { MOONSHOT_DEFAULT_MODEL_REF, buildMoonshotProvider } from "./provider-catalog.js";
4
4
  import { moonshotMediaUnderstandingProvider } from "./media-understanding-provider.js";
5
5
  import { wrapMoonshotStream } from "./native-video.js";
6
6
  import { applyMoonshotConfig, applyMoonshotConfigCn } from "./onboard.js";
7
- import { t as createKimiWebSearchProvider } from "./.setup/kimi-web-search-provider-u4LLc55G.mjs";
7
+ import { t as createKimiWebSearchProvider } from "./.setup/kimi-web-search-provider-DCbJoku0.mjs";
8
8
  import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth-api-key";
9
9
  import { defineSingleProviderPluginEntry } from "openclaw/plugin-sdk/provider-entry";
10
10
  import { buildOpenAICompatibleReplayPolicy } from "openclaw/plugin-sdk/provider-model-shared";
@@ -1,4 +1,4 @@
1
- import { t as openclaw_plugin_default } from "./.setup/openclaw.plugin-DX_vVKdi.mjs";
1
+ import { t as openclaw_plugin_default } from "./.setup/openclaw.plugin-ByEFwJ6V.mjs";
2
2
  import { MOONSHOT_BASE_URL } from "./provider-policy-api.js";
3
3
  import "./provider-catalog.js";
4
4
  import { describeOpenAiCompatibleVideo } from "openclaw/plugin-sdk/media-understanding";
@@ -1,15 +1,9 @@
1
- import { t as openclaw_plugin_default } from "./.setup/openclaw.plugin-DX_vVKdi.mjs";
1
+ import { t as openclaw_plugin_default } from "./.setup/openclaw.plugin-ByEFwJ6V.mjs";
2
2
  import { MOONSHOT_BASE_URL, MOONSHOT_CN_BASE_URL, isNativeMoonshotBaseUrl } from "./provider-policy-api.js";
3
- import { applyProviderNativeStreamingUsageCompat, buildManifestModelProviderConfig, readManifestProviderDefaultModelRef } from "openclaw/plugin-sdk/provider-catalog-shared";
3
+ import { buildManifestModelProviderConfig, readManifestProviderDefaultModelRef } from "openclaw/plugin-sdk/provider-catalog-shared";
4
4
  //#region extensions/moonshot/provider-catalog.ts
5
5
  const MOONSHOT_DEFAULT_MODEL_REF = readManifestProviderDefaultModelRef(openclaw_plugin_default, "moonshot");
6
6
  const MOONSHOT_DEFAULT_MODEL_ID = MOONSHOT_DEFAULT_MODEL_REF.slice(9);
7
- function applyMoonshotNativeStreamingUsageCompat(provider) {
8
- return applyProviderNativeStreamingUsageCompat({
9
- providerId: "moonshot",
10
- providerConfig: provider
11
- });
12
- }
13
7
  function buildMoonshotProvider() {
14
8
  return buildManifestModelProviderConfig({
15
9
  providerId: "moonshot",
@@ -17,4 +11,4 @@ function buildMoonshotProvider() {
17
11
  });
18
12
  }
19
13
  //#endregion
20
- export { MOONSHOT_BASE_URL, MOONSHOT_CN_BASE_URL, MOONSHOT_DEFAULT_MODEL_ID, MOONSHOT_DEFAULT_MODEL_REF, applyMoonshotNativeStreamingUsageCompat, buildMoonshotProvider, isNativeMoonshotBaseUrl };
14
+ export { MOONSHOT_BASE_URL, MOONSHOT_CN_BASE_URL, MOONSHOT_DEFAULT_MODEL_ID, MOONSHOT_DEFAULT_MODEL_REF, buildMoonshotProvider, isNativeMoonshotBaseUrl };
@@ -1,4 +1,4 @@
1
- import { t as openclaw_plugin_default } from "./.setup/openclaw.plugin-DX_vVKdi.mjs";
1
+ import { t as openclaw_plugin_default } from "./.setup/openclaw.plugin-ByEFwJ6V.mjs";
2
2
  //#region extensions/moonshot/provider-contract-api.ts
3
3
  const noopAuth = async () => ({ profiles: [] });
4
4
  function createMoonshotProvider() {
@@ -1,3 +1,2 @@
1
- import { t as createKimiWebSearchProvider } from "./.setup/kimi-web-search-provider-u4LLc55G.mjs";
1
+ export { t as createKimiWebSearchProvider } from "./.setup/kimi-web-search-provider-DCbJoku0.mjs";
2
2
  import "./web-search-provider.js";
3
- export { createKimiWebSearchProvider };
@@ -1,2 +1 @@
1
- import { t as createKimiWebSearchProvider } from "./.setup/kimi-web-search-provider-u4LLc55G.mjs";
2
- export { createKimiWebSearchProvider };
1
+ export { t as createKimiWebSearchProvider } from "./.setup/kimi-web-search-provider-DCbJoku0.mjs";
@@ -85,7 +85,7 @@
85
85
  "input": 3,
86
86
  "output": 15,
87
87
  "cacheRead": 0.3,
88
- "cacheWrite": 0
88
+ "cacheWrite": 3
89
89
  },
90
90
  "compat": {
91
91
  "supportsReasoningEffort": true,
@@ -190,8 +190,8 @@
190
190
  "help": "Kimi base URL override."
191
191
  },
192
192
  "webSearch.model": {
193
- "label": "Kimi Search Model",
194
- "help": "Kimi model override."
193
+ "label": "Kimi Search Model (Deprecated)",
194
+ "help": "Deprecated and ignored. Kimi web search uses the search endpoint without a chat model; this key can be removed."
195
195
  }
196
196
  },
197
197
  "contracts": {
@@ -245,7 +245,8 @@
245
245
  "type": "string"
246
246
  },
247
247
  "model": {
248
- "type": "string"
248
+ "type": "string",
249
+ "description": "Deprecated and ignored. Kimi web search uses the search endpoint without a chat model."
249
250
  }
250
251
  }
251
252
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/moonshot-provider",
3
- "version": "2026.10.5-beta.4",
3
+ "version": "2026.10.10-beta.1",
4
4
  "description": "OpenClaw Moonshot provider plugin",
5
5
  "type": "module",
6
6
  "openclaw": {
@@ -14,10 +14,10 @@
14
14
  "minHostVersion": ">=2026.6.9"
15
15
  },
16
16
  "compat": {
17
- "pluginApi": ">=2026.10.5-beta.4"
17
+ "pluginApi": ">=2026.10.10-beta.1"
18
18
  },
19
19
  "build": {
20
- "openclawVersion": "2026.10.5-beta.4",
20
+ "openclawVersion": "2026.10.10-beta.1",
21
21
  "bundledDist": false
22
22
  },
23
23
  "release": {
@@ -40,7 +40,7 @@
40
40
  "assets/activity.svg"
41
41
  ],
42
42
  "peerDependencies": {
43
- "openclaw": ">=2026.10.5-beta.4"
43
+ "openclaw": ">=2026.10.10-beta.1"
44
44
  },
45
45
  "peerDependenciesMeta": {
46
46
  "openclaw": {
@@ -1,265 +0,0 @@
1
- import { MOONSHOT_BASE_URL, MOONSHOT_CN_BASE_URL, isNativeMoonshotBaseUrl } from "../provider-policy-api.js";
2
- import "../provider-catalog.js";
3
- import { asNonArrayRecord, isRecord, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
4
- import { createProviderHttpError, normalizeBaseUrl, readProviderJsonObjectResponse } from "openclaw/plugin-sdk/provider-http";
5
- import { MAX_SEARCH_COUNT, buildSearchCacheKey, buildUnsupportedSearchFilterResponse, mergeScopedSearchConfig, readCachedSearchPayload, readConfiguredSecretString, readPositiveIntegerParam, readProviderEnvValue, readStringParam, resolveProviderWebSearchPluginConfig, resolveSearchCacheTtlMs, resolveSearchTimeoutSeconds, setProviderWebSearchPluginConfigValue, withTrustedWebSearchEndpoint, wrapWebContent, writeCachedSearchPayload } from "openclaw/plugin-sdk/provider-web-search";
6
- //#region extensions/moonshot/src/kimi-web-search-provider.runtime.ts
7
- const DEFAULT_KIMI_BASE_URL = MOONSHOT_BASE_URL;
8
- const DEFAULT_KIMI_SEARCH_MODEL = "kimi-k2.6";
9
- /** Models that require explicit thinking disablement for web search. */
10
- const KIMI_THINKING_MODELS = /* @__PURE__ */ new Set(["kimi-k2.6", "kimi-k2.5"]);
11
- const KIMI_WEB_SEARCH_TOOL = {
12
- type: "builtin_function",
13
- function: { name: "$web_search" }
14
- };
15
- function throwMalformedKimiResponse() {
16
- throw new Error("Kimi API error: malformed JSON response");
17
- }
18
- function resolveKimiApiKey(kimi) {
19
- return readConfiguredSecretString(kimi?.apiKey, "plugins.entries.moonshot.config.webSearch.apiKey") ?? readProviderEnvValue(["KIMI_API_KEY", "MOONSHOT_API_KEY"]);
20
- }
21
- function resolveKimiBaseUrl(kimi, openClawConfig) {
22
- const explicitBaseUrl = normalizeOptionalString(kimi?.baseUrl);
23
- if (explicitBaseUrl) return normalizeBaseUrl(explicitBaseUrl) || DEFAULT_KIMI_BASE_URL;
24
- const moonshotBaseUrl = openClawConfig?.models?.providers?.moonshot?.baseUrl;
25
- if (typeof moonshotBaseUrl === "string") {
26
- const normalizedMoonshotBaseUrl = normalizeBaseUrl(moonshotBaseUrl);
27
- if (normalizedMoonshotBaseUrl && isNativeMoonshotBaseUrl(normalizedMoonshotBaseUrl)) return normalizedMoonshotBaseUrl;
28
- }
29
- return DEFAULT_KIMI_BASE_URL;
30
- }
31
- function extractKimiMessageText(message) {
32
- const content = message?.content?.trim();
33
- if (content) return content;
34
- return message?.reasoning_content?.trim() || void 0;
35
- }
36
- function collectKimiCitations(searchResults, toolCalls, citations) {
37
- for (const entry of searchResults) {
38
- const url = isRecord(entry) && typeof entry.url === "string" ? entry.url.trim() : "";
39
- if (url) citations.add(url);
40
- }
41
- for (const toolCall of toolCalls) {
42
- if (!isRecord(toolCall) || !isRecord(toolCall.function)) continue;
43
- const rawArguments = toolCall.function.arguments;
44
- if (typeof rawArguments !== "string" || !rawArguments) continue;
45
- try {
46
- const parsed = JSON.parse(rawArguments);
47
- const parsedUrl = normalizeOptionalString(parsed.url);
48
- if (parsedUrl) citations.add(parsedUrl);
49
- for (const result of parsed.search_results ?? []) {
50
- const resultUrl = normalizeOptionalString(result.url);
51
- if (resultUrl) citations.add(resultUrl);
52
- }
53
- } catch {}
54
- }
55
- }
56
- function hasKimiSearchResults(searchResults) {
57
- return searchResults.some((entry) => isRecord(entry) && (Boolean(normalizeOptionalString(entry.url)) || Boolean(normalizeOptionalString(entry.title)) || Boolean(normalizeOptionalString(entry.content))));
58
- }
59
- function extractKimiToolResultContent(toolCall) {
60
- const rawArguments = toolCall.function?.arguments;
61
- if (typeof rawArguments !== "string" || rawArguments.trim().length === 0) return;
62
- return rawArguments;
63
- }
64
- async function runKimiSearch(params) {
65
- const endpoint = `${params.baseUrl.trim().replace(/\/$/, "")}/chat/completions`;
66
- const messages = [{
67
- role: "user",
68
- content: params.query
69
- }];
70
- const collectedCitations = /* @__PURE__ */ new Set();
71
- let hasGroundingEvidence = false;
72
- for (let round = 0; round < 3; round += 1) {
73
- const next = await withTrustedWebSearchEndpoint({
74
- url: endpoint,
75
- timeoutSeconds: params.timeoutSeconds,
76
- signal: params.signal,
77
- init: {
78
- method: "POST",
79
- headers: {
80
- "Content-Type": "application/json",
81
- Authorization: `Bearer ${params.apiKey}`
82
- },
83
- body: JSON.stringify({
84
- model: params.model,
85
- ...KIMI_THINKING_MODELS.has(params.model) ? { thinking: { type: "disabled" } } : {},
86
- messages,
87
- tools: [KIMI_WEB_SEARCH_TOOL]
88
- })
89
- }
90
- }, async (res) => {
91
- if (!res.ok) throw await createProviderHttpError(res, "Kimi API error");
92
- const data = await readProviderJsonObjectResponse(res, "Kimi API error");
93
- if (!Array.isArray(data.choices)) throwMalformedKimiResponse();
94
- const searchResults = data.search_results ?? [];
95
- if (!Array.isArray(searchResults)) throwMalformedKimiResponse();
96
- const choice = data.choices[0];
97
- if (!isRecord(choice) || !isRecord(choice.message)) throwMalformedKimiResponse();
98
- const message = choice.message;
99
- const toolCalls = message.tool_calls ?? [];
100
- if (!Array.isArray(toolCalls)) throwMalformedKimiResponse();
101
- collectKimiCitations(searchResults, toolCalls, collectedCitations);
102
- hasGroundingEvidence ||= hasKimiSearchResults(searchResults) || collectedCitations.size > 0;
103
- const text = extractKimiMessageText(message);
104
- if (choice.finish_reason === "tool_calls" && toolCalls.length > 0) {
105
- messages.push({
106
- role: "assistant",
107
- content: message.content ?? "",
108
- ...message.reasoning_content ? { reasoning_content: message.reasoning_content } : {},
109
- tool_calls: toolCalls
110
- });
111
- let pushed = false;
112
- for (const toolCall of toolCalls) {
113
- const toolCallId = toolCall.id?.trim();
114
- const toolCallName = toolCall.function?.name?.trim();
115
- const toolContent = extractKimiToolResultContent(toolCall);
116
- if (!toolCallId || !toolCallName || !toolContent) continue;
117
- if (toolCallName === KIMI_WEB_SEARCH_TOOL.function.name) hasGroundingEvidence = true;
118
- pushed = true;
119
- messages.push({
120
- role: "tool",
121
- tool_call_id: toolCallId,
122
- name: toolCallName,
123
- content: toolContent
124
- });
125
- }
126
- if (pushed) return { done: false };
127
- }
128
- if (!text) throwMalformedKimiResponse();
129
- return {
130
- done: true,
131
- content: text,
132
- citations: [...collectedCitations]
133
- };
134
- });
135
- if (next.done) return {
136
- content: next.content,
137
- citations: next.citations,
138
- grounded: hasGroundingEvidence
139
- };
140
- }
141
- throw new Error("Kimi web search exhausted its tool-call rounds without producing a final answer. Retry the query or choose another search provider.");
142
- }
143
- async function executeKimiWebSearchProviderTool(ctx, args, opts) {
144
- opts?.signal?.throwIfAborted();
145
- const searchConfig = mergeScopedSearchConfig(ctx.searchConfig, "kimi", resolveProviderWebSearchPluginConfig(ctx.config, "moonshot"));
146
- const unsupportedResponse = buildUnsupportedSearchFilterResponse(args, "kimi");
147
- if (unsupportedResponse) return unsupportedResponse;
148
- const kimiConfig = asNonArrayRecord(searchConfig?.kimi);
149
- const apiKey = resolveKimiApiKey(kimiConfig);
150
- if (!apiKey) return {
151
- error: "missing_kimi_api_key",
152
- message: "web_search (kimi) needs a Moonshot API key. Set KIMI_API_KEY or MOONSHOT_API_KEY in the Gateway environment, or configure plugins.entries.moonshot.config.webSearch.apiKey. If you do not want to configure a search API key, use web_fetch for a specific URL or the browser tool for interactive pages.",
153
- docs: "https://docs.openclaw.ai/tools/web"
154
- };
155
- const query = readStringParam(args, "query", { required: true });
156
- readPositiveIntegerParam(args, "count", {
157
- max: MAX_SEARCH_COUNT,
158
- message: `count must be an integer from 1 to ${MAX_SEARCH_COUNT}.`
159
- });
160
- const model = normalizeOptionalString(kimiConfig.model) ?? DEFAULT_KIMI_SEARCH_MODEL;
161
- const baseUrl = resolveKimiBaseUrl(kimiConfig, ctx.config);
162
- const cacheKey = buildSearchCacheKey([
163
- "kimi",
164
- query,
165
- baseUrl,
166
- model
167
- ]);
168
- const cacheTtlMs = resolveSearchCacheTtlMs(searchConfig);
169
- const cached = readCachedSearchPayload(cacheKey, cacheTtlMs);
170
- if (cached) return cached;
171
- const start = Date.now();
172
- const result = await runKimiSearch({
173
- query,
174
- apiKey,
175
- baseUrl,
176
- model,
177
- timeoutSeconds: resolveSearchTimeoutSeconds(searchConfig),
178
- signal: opts?.signal
179
- });
180
- opts?.signal?.throwIfAborted();
181
- if (!result.grounded) return {
182
- error: "kimi_web_search_ungrounded",
183
- message: "Kimi returned a chat completion without native web-search grounding. Retry the query, switch to a structured provider such as Brave, or use web_fetch/browser for a specific URL.",
184
- query,
185
- provider: "kimi",
186
- model,
187
- docs: "https://docs.openclaw.ai/tools/kimi-search",
188
- tookMs: Date.now() - start
189
- };
190
- const payload = {
191
- query,
192
- provider: "kimi",
193
- model,
194
- tookMs: Date.now() - start,
195
- externalContent: {
196
- untrusted: true,
197
- source: "web_search",
198
- provider: "kimi",
199
- wrapped: true
200
- },
201
- content: wrapWebContent(result.content),
202
- citations: result.citations
203
- };
204
- writeCachedSearchPayload(cacheKey, payload, cacheTtlMs);
205
- return payload;
206
- }
207
- async function runKimiSearchProviderSetup(ctx) {
208
- const existingPluginConfig = resolveProviderWebSearchPluginConfig(ctx.config, "moonshot");
209
- const normalizedBaseUrl = normalizeBaseUrl(normalizeOptionalString(existingPluginConfig?.baseUrl));
210
- const existingModel = normalizeOptionalString(existingPluginConfig?.model) ?? "";
211
- const isCustomBaseUrl = normalizedBaseUrl && !isNativeMoonshotBaseUrl(normalizedBaseUrl);
212
- const regionOptions = [];
213
- if (isCustomBaseUrl) regionOptions.push({
214
- value: normalizedBaseUrl,
215
- label: `Keep current (${normalizedBaseUrl})`,
216
- hint: "custom endpoint"
217
- });
218
- regionOptions.push({
219
- value: MOONSHOT_BASE_URL,
220
- label: "Moonshot API key (.ai)",
221
- hint: "api.moonshot.ai"
222
- }, {
223
- value: MOONSHOT_CN_BASE_URL,
224
- label: "Moonshot API key (.cn)",
225
- hint: "api.moonshot.cn"
226
- });
227
- const baseUrl = await ctx.prompter.select({
228
- message: "Kimi API region",
229
- options: regionOptions,
230
- initialValue: normalizedBaseUrl || "https://api.moonshot.ai/v1"
231
- });
232
- const currentModelLabel = existingModel ? `Keep current (moonshot/${existingModel})` : `Use default (moonshot/${DEFAULT_KIMI_SEARCH_MODEL})`;
233
- const modelChoice = await ctx.prompter.select({
234
- message: "Kimi web search model",
235
- options: [
236
- {
237
- value: "__keep__",
238
- label: currentModelLabel
239
- },
240
- {
241
- value: "__custom__",
242
- label: "Enter model manually"
243
- },
244
- {
245
- value: DEFAULT_KIMI_SEARCH_MODEL,
246
- label: `moonshot/${DEFAULT_KIMI_SEARCH_MODEL}`
247
- }
248
- ],
249
- initialValue: "__keep__"
250
- });
251
- let model;
252
- if (modelChoice === "__keep__") model = existingModel || DEFAULT_KIMI_SEARCH_MODEL;
253
- else if (modelChoice === "__custom__") model = (await ctx.prompter.text({
254
- message: "Kimi model name",
255
- initialValue: existingModel || DEFAULT_KIMI_SEARCH_MODEL,
256
- placeholder: DEFAULT_KIMI_SEARCH_MODEL
257
- }))?.trim() || DEFAULT_KIMI_SEARCH_MODEL;
258
- else model = modelChoice;
259
- const next = { ...ctx.config };
260
- setProviderWebSearchPluginConfigValue(next, "moonshot", "baseUrl", baseUrl);
261
- setProviderWebSearchPluginConfigValue(next, "moonshot", "model", model);
262
- return next;
263
- }
264
- //#endregion
265
- export { executeKimiWebSearchProviderTool, runKimiSearchProviderSetup };