@oh-my-pi/pi-coding-agent 17.0.0 → 17.0.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 +128 -0
- package/dist/cli.js +4006 -3966
- package/dist/types/advisor/advise-tool.d.ts +5 -2
- package/dist/types/advisor/config.d.ts +14 -6
- package/dist/types/advisor/runtime.d.ts +20 -11
- package/dist/types/autolearn/controller.d.ts +1 -0
- package/dist/types/cli-commands.d.ts +14 -1
- package/dist/types/config/model-discovery.d.ts +17 -0
- package/dist/types/config/model-registry.d.ts +4 -0
- package/dist/types/config/model-resolver.d.ts +6 -2
- package/dist/types/config/models-config-schema.d.ts +10 -0
- package/dist/types/config/models-config.d.ts +6 -0
- package/dist/types/config/settings-schema.d.ts +32 -7
- package/dist/types/config/settings.d.ts +50 -0
- package/dist/types/cursor.d.ts +11 -0
- package/dist/types/discovery/helpers.d.ts +5 -2
- package/dist/types/discovery/substitute-plugin-root.d.ts +20 -7
- package/dist/types/exec/bash-executor.d.ts +2 -0
- package/dist/types/extensibility/extensions/managed-timers.d.ts +15 -0
- package/dist/types/extensibility/extensions/runner.d.ts +7 -0
- package/dist/types/extensibility/extensions/types.d.ts +18 -0
- package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +40 -0
- package/dist/types/extensibility/shared-events.d.ts +6 -0
- package/dist/types/extensibility/utils.d.ts +2 -2
- package/dist/types/internal-urls/agent-protocol.d.ts +1 -0
- package/dist/types/lib/xai-http.d.ts +11 -0
- package/dist/types/mcp/transports/stdio.d.ts +13 -1
- package/dist/types/modes/acp/acp-agent.d.ts +3 -0
- package/dist/types/modes/acp/acp-mode.d.ts +6 -2
- package/dist/types/modes/components/advisor-config.d.ts +8 -1
- package/dist/types/modes/components/assistant-message.d.ts +2 -0
- package/dist/types/modes/components/custom-editor-plugin-ctor.test.d.ts +1 -0
- package/dist/types/modes/components/custom-editor.d.ts +23 -1
- package/dist/types/modes/components/hook-editor.d.ts +16 -2
- package/dist/types/modes/components/login-dialog.test.d.ts +1 -0
- package/dist/types/modes/components/model-hub.d.ts +5 -2
- package/dist/types/modes/components/session-selector.d.ts +4 -0
- package/dist/types/modes/components/tool-execution.d.ts +8 -1
- package/dist/types/modes/components/tool-execution.test.d.ts +1 -0
- package/dist/types/modes/controllers/command-controller.d.ts +8 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +3 -1
- package/dist/types/modes/interactive-mode.d.ts +6 -0
- package/dist/types/modes/noninteractive-dispose.test.d.ts +1 -0
- package/dist/types/modes/print-mode.d.ts +1 -1
- package/dist/types/modes/types.d.ts +10 -0
- package/dist/types/modes/utils/transcript-render-helpers.d.ts +2 -2
- package/dist/types/modes/warp-events.d.ts +24 -0
- package/dist/types/modes/warp-events.test.d.ts +1 -0
- package/dist/types/plan-mode/model-transition.d.ts +47 -0
- package/dist/types/plan-mode/model-transition.test.d.ts +1 -0
- package/dist/types/registry/agent-lifecycle.d.ts +26 -1
- package/dist/types/sdk.d.ts +13 -2
- package/dist/types/session/agent-session.d.ts +43 -11
- package/dist/types/session/session-history-format.d.ts +10 -0
- package/dist/types/session/session-manager.d.ts +14 -0
- package/dist/types/session/streaming-output.d.ts +2 -0
- package/dist/types/slash-commands/helpers/active-oauth-account.d.ts +9 -0
- package/dist/types/slash-commands/types.d.ts +4 -4
- package/dist/types/system-prompt.d.ts +1 -1
- package/dist/types/task/label.d.ts +1 -1
- package/dist/types/telemetry-export.d.ts +34 -9
- package/dist/types/tools/approval.d.ts +8 -0
- package/dist/types/tools/bash.d.ts +2 -0
- package/dist/types/tools/browser/registry.d.ts +7 -3
- package/dist/types/tools/browser/tab-supervisor.d.ts +2 -0
- package/dist/types/tools/essential-tools.d.ts +29 -0
- package/dist/types/tools/image-gen.d.ts +2 -1
- package/dist/types/tools/index.d.ts +4 -1
- package/dist/types/tools/manage-skill.d.ts +2 -0
- package/dist/types/tools/xdev.d.ts +11 -2
- package/dist/types/utils/title-generator.d.ts +2 -1
- package/dist/types/web/search/index.d.ts +2 -0
- package/dist/types/web/search/providers/base.d.ts +3 -0
- package/dist/types/web/search/providers/kimi.d.ts +4 -1
- package/dist/types/web/search/types.d.ts +1 -1
- package/package.json +21 -16
- package/src/advisor/__tests__/advisor.test.ts +1358 -86
- package/src/advisor/__tests__/config.test.ts +58 -2
- package/src/advisor/advise-tool.ts +7 -3
- package/src/advisor/config.ts +76 -24
- package/src/advisor/runtime.ts +445 -92
- package/src/autolearn/controller.ts +23 -28
- package/src/cli/bench-cli.ts +4 -1
- package/src/cli/grep-cli.ts +2 -1
- package/src/cli-commands.ts +57 -23
- package/src/cli.ts +5 -1
- package/src/config/model-discovery.ts +81 -21
- package/src/config/model-registry.ts +34 -6
- package/src/config/model-resolver.ts +57 -12
- package/src/config/models-config-schema.ts +1 -0
- package/src/config/settings-schema.ts +43 -6
- package/src/config/settings.ts +405 -25
- package/src/cursor.ts +20 -3
- package/src/debug/report-bundle.ts +40 -4
- package/src/discovery/codex.ts +14 -4
- package/src/discovery/helpers.ts +28 -5
- package/src/discovery/substitute-plugin-root.ts +23 -7
- package/src/exec/bash-executor.ts +14 -5
- package/src/export/html/template.js +2 -0
- package/src/extensibility/custom-tools/loader.ts +3 -3
- package/src/extensibility/custom-tools/wrapper.ts +2 -1
- package/src/extensibility/extensions/loader.ts +3 -3
- package/src/extensibility/extensions/managed-timers.ts +83 -0
- package/src/extensibility/extensions/runner.ts +26 -0
- package/src/extensibility/extensions/types.ts +18 -0
- package/src/extensibility/extensions/wrapper.ts +2 -1
- package/src/extensibility/hooks/loader.ts +3 -3
- package/src/extensibility/legacy-pi-coding-agent-shim.ts +96 -1
- package/src/extensibility/plugins/legacy-pi-compat.ts +225 -22
- package/src/extensibility/plugins/manager.ts +2 -2
- package/src/extensibility/shared-events.ts +6 -0
- package/src/extensibility/utils.ts +91 -25
- package/src/internal-urls/__tests__/agent-protocol-nested.test.ts +73 -0
- package/src/internal-urls/agent-protocol.ts +73 -39
- package/src/irc/bus.ts +22 -3
- package/src/launch/broker.ts +3 -2
- package/src/launch/client.ts +2 -2
- package/src/launch/presence.ts +2 -2
- package/src/lib/xai-http.ts +28 -2
- package/src/lsp/client.ts +1 -1
- package/src/main.ts +11 -8
- package/src/mcp/manager.ts +9 -3
- package/src/mcp/transports/stdio.ts +103 -23
- package/src/modes/acp/acp-agent.ts +28 -9
- package/src/modes/acp/acp-event-mapper.ts +8 -0
- package/src/modes/acp/acp-mode.ts +18 -4
- package/src/modes/components/advisor-config.ts +65 -3
- package/src/modes/components/ask-dialog.ts +1 -1
- package/src/modes/components/assistant-message.ts +35 -14
- package/src/modes/components/chat-transcript-builder.ts +2 -0
- package/src/modes/components/custom-editor-plugin-ctor.test.ts +36 -0
- package/src/modes/components/custom-editor.ts +62 -1
- package/src/modes/components/hook-editor.ts +35 -5
- package/src/modes/components/login-dialog.test.ts +56 -0
- package/src/modes/components/login-dialog.ts +7 -3
- package/src/modes/components/model-hub.ts +138 -42
- package/src/modes/components/plan-review-overlay.ts +51 -10
- package/src/modes/components/session-selector.ts +10 -0
- package/src/modes/components/status-line/component.test.ts +1 -0
- package/src/modes/components/status-line/component.ts +2 -4
- package/src/modes/components/status-line/segments.ts +21 -6
- package/src/modes/components/tool-execution.test.ts +101 -0
- package/src/modes/components/tool-execution.ts +117 -9
- package/src/modes/components/transcript-container.ts +30 -2
- package/src/modes/controllers/command-controller-shared.ts +1 -1
- package/src/modes/controllers/command-controller.ts +168 -48
- package/src/modes/controllers/event-controller.ts +6 -0
- package/src/modes/controllers/extension-ui-controller.ts +4 -22
- package/src/modes/controllers/input-controller.ts +12 -12
- package/src/modes/controllers/selector-controller.ts +208 -46
- package/src/modes/interactive-mode.ts +205 -65
- package/src/modes/noninteractive-dispose.test.ts +60 -0
- package/src/modes/print-mode.ts +10 -5
- package/src/modes/rpc/host-tools.ts +2 -1
- package/src/modes/rpc/rpc-mode.ts +31 -7
- package/src/modes/theme/theme.ts +2 -2
- package/src/modes/types.ts +10 -0
- package/src/modes/utils/interactive-context-helpers.ts +3 -1
- package/src/modes/utils/transcript-render-helpers.ts +3 -2
- package/src/modes/warp-events.test.ts +794 -0
- package/src/modes/warp-events.ts +232 -0
- package/src/plan-mode/model-transition.test.ts +60 -0
- package/src/plan-mode/model-transition.ts +51 -0
- package/src/prompts/system/system-prompt.md +1 -1
- package/src/registry/agent-lifecycle.ts +133 -18
- package/src/sdk.ts +227 -44
- package/src/session/agent-session.ts +1364 -382
- package/src/session/session-history-format.ts +20 -5
- package/src/session/session-listing.ts +6 -5
- package/src/session/session-loader.ts +9 -0
- package/src/session/session-manager.ts +48 -0
- package/src/session/session-persistence.ts +11 -0
- package/src/session/streaming-output.ts +3 -1
- package/src/slash-commands/builtin-registry.ts +9 -0
- package/src/slash-commands/helpers/active-oauth-account.ts +16 -0
- package/src/slash-commands/types.ts +4 -4
- package/src/system-prompt.ts +2 -2
- package/src/task/executor.ts +1 -1
- package/src/task/label.ts +2 -0
- package/src/telemetry-export.ts +453 -97
- package/src/tools/approval.ts +11 -0
- package/src/tools/bash.ts +71 -38
- package/src/tools/browser/registry.ts +9 -2
- package/src/tools/browser/tab-supervisor.ts +65 -10
- package/src/tools/browser.ts +4 -3
- package/src/tools/essential-tools.ts +45 -0
- package/src/tools/eval.ts +3 -0
- package/src/tools/gh.ts +169 -2
- package/src/tools/image-gen.ts +624 -513
- package/src/tools/index.ts +10 -6
- package/src/tools/manage-skill.ts +5 -3
- package/src/tools/output-meta.ts +7 -0
- package/src/tools/path-utils.ts +8 -5
- package/src/tools/read.ts +48 -3
- package/src/tools/write.ts +22 -4
- package/src/tools/xdev.ts +14 -3
- package/src/utils/title-generator.ts +15 -4
- package/src/web/search/index.ts +21 -6
- package/src/web/search/providers/base.ts +3 -0
- package/src/web/search/providers/kimi.ts +18 -12
- package/src/web/search/providers/xai.ts +40 -9
- package/src/web/search/types.ts +6 -1
package/src/tools/index.ts
CHANGED
|
@@ -74,6 +74,7 @@ export * from "./bash";
|
|
|
74
74
|
export * from "./browser";
|
|
75
75
|
export * from "./checkpoint";
|
|
76
76
|
export * from "./debug";
|
|
77
|
+
export * from "./essential-tools";
|
|
77
78
|
export * from "./eval";
|
|
78
79
|
export * from "./eval-backends";
|
|
79
80
|
export * from "./gh";
|
|
@@ -160,7 +161,9 @@ export interface ToolSession {
|
|
|
160
161
|
/** Pre-loaded workspace tree (forwarded to subagents to skip re-scanning) */
|
|
161
162
|
workspaceTree?: WorkspaceTree;
|
|
162
163
|
/** Pre-loaded skills */
|
|
163
|
-
skills?: Skill[];
|
|
164
|
+
skills?: readonly Skill[];
|
|
165
|
+
/** Rediscover live session skills after a tool mutates their backing files. */
|
|
166
|
+
refreshSkills?: () => Promise<void>;
|
|
164
167
|
/** Pre-loaded prompt templates */
|
|
165
168
|
promptTemplates?: PromptTemplate[];
|
|
166
169
|
/** Pre-loaded rules (forwarded to subagents to skip re-discovery). */
|
|
@@ -566,15 +569,16 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P
|
|
|
566
569
|
);
|
|
567
570
|
let tools = baseResults.filter((r): r is Tool => r !== null);
|
|
568
571
|
|
|
569
|
-
// xd://
|
|
570
|
-
//
|
|
571
|
-
// default tool sets
|
|
572
|
-
const xdevEnabled =
|
|
572
|
+
// Always create the xd:// registry when enabled so SDK assembly can mount
|
|
573
|
+
// discoverable custom/MCP tools later. Explicitly requested built-ins keep
|
|
574
|
+
// their top-level presentation; default tool sets mount discoverable built-ins.
|
|
575
|
+
const xdevEnabled = session.settings.get("tools.xdev");
|
|
576
|
+
const mountBuiltinTools = requestedTools === undefined;
|
|
573
577
|
if (xdevEnabled) {
|
|
574
578
|
const mounted: Tool[] = [];
|
|
575
579
|
const kept: Tool[] = [];
|
|
576
580
|
for (const tool of tools) {
|
|
577
|
-
const mountable = isMountableUnderXdev(tool) && tool.name in BUILTIN_TOOLS;
|
|
581
|
+
const mountable = mountBuiltinTools && isMountableUnderXdev(tool) && tool.name in BUILTIN_TOOLS;
|
|
578
582
|
(mountable ? mounted : kept).push(tool);
|
|
579
583
|
}
|
|
580
584
|
session.xdevRegistry = new XdevRegistry(mounted);
|
|
@@ -45,16 +45,17 @@ export class ManageSkillTool implements AgentTool<typeof manageSkillSchema> {
|
|
|
45
45
|
readonly loadMode = "essential" as const;
|
|
46
46
|
readonly summary = "Create, update, or delete an isolated managed skill";
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
constructor(private readonly refreshSkills?: () => Promise<void>) {}
|
|
49
|
+
|
|
50
50
|
static createIf(session: ToolSession): ManageSkillTool | null {
|
|
51
51
|
if (!session.settings.get("autolearn.enabled")) return null;
|
|
52
|
-
return new ManageSkillTool();
|
|
52
|
+
return new ManageSkillTool(session.refreshSkills);
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
async execute(_id: string, params: ManageSkillParams): Promise<AgentToolResult> {
|
|
56
56
|
if (params.action === "delete") {
|
|
57
57
|
await deleteManagedSkill(params.name);
|
|
58
|
+
await this.refreshSkills?.();
|
|
58
59
|
return {
|
|
59
60
|
content: [{ type: "text", text: `Deleted managed skill "${params.name}".` }],
|
|
60
61
|
details: { action: "delete", name: params.name },
|
|
@@ -90,6 +91,7 @@ export class ManageSkillTool implements AgentTool<typeof manageSkillSchema> {
|
|
|
90
91
|
description: params.description,
|
|
91
92
|
body: params.body,
|
|
92
93
|
});
|
|
94
|
+
await this.refreshSkills?.();
|
|
93
95
|
const relativePath = path.relative(getManagedSkillsDir(), skillPath);
|
|
94
96
|
const verb = params.action === "create" ? "Created" : "Updated";
|
|
95
97
|
return {
|
package/src/tools/output-meta.ts
CHANGED
|
@@ -191,6 +191,13 @@ export class OutputMetaBuilder {
|
|
|
191
191
|
|
|
192
192
|
/** Add truncation info from OutputSummary. No-op if not truncated. */
|
|
193
193
|
truncationFromSummary(summary: OutputSummary, options: TruncationSummaryOptions): this {
|
|
194
|
+
// A per-line column cap only trims individual lines (with a `…` marker);
|
|
195
|
+
// it is not a window/byte truncation, so surface it as its own limit
|
|
196
|
+
// notice rather than a "Showing lines X-Y … limit" range. This runs even
|
|
197
|
+
// when the output is otherwise complete (`truncated === false`).
|
|
198
|
+
if (summary.columnMax != null && summary.columnMax > 0 && (summary.columnTruncatedLines ?? 0) > 0) {
|
|
199
|
+
this.columnTruncated(summary.columnMax);
|
|
200
|
+
}
|
|
194
201
|
if (!summary.truncated) return this;
|
|
195
202
|
|
|
196
203
|
const { direction, startLine = 1, totalFileLines } = options;
|
package/src/tools/path-utils.ts
CHANGED
|
@@ -148,11 +148,14 @@ export function expandTilde(filePath: string, home?: string): string {
|
|
|
148
148
|
|
|
149
149
|
export function expandPath(filePath: string): string {
|
|
150
150
|
// Some models intermittently prefix an otherwise-valid path with a stray
|
|
151
|
-
// `:` (e.g. `:/abs/path`, `:../rel
|
|
152
|
-
//
|
|
153
|
-
//
|
|
154
|
-
//
|
|
155
|
-
|
|
151
|
+
// `:` (e.g. `:/abs/path`, `:../rel`, or the Windows forms `:C:\repo\file`
|
|
152
|
+
// and `:.\src`). No real path starts with `:` and it never begins a
|
|
153
|
+
// selector against an absolute/relative path, so strip it before
|
|
154
|
+
// resolution — mirroring the `@`-prefix normalization above and the
|
|
155
|
+
// implicit stripping `write` already tolerates (issues #5508, #5624). The
|
|
156
|
+
// lookahead admits POSIX (`/`, `~`, `./`, `../`) and Windows (`\`, `.\`,
|
|
157
|
+
// `..\`, drive-letter `C:`) path shapes.
|
|
158
|
+
const deColoned = /^:(?=[/\\~]|\.\.?[/\\]|[A-Za-z]:)/.test(filePath) ? filePath.slice(1) : filePath;
|
|
156
159
|
const normalized = stripWindowsExtendedLengthPathPrefix(
|
|
157
160
|
stripFileUrl(normalizeUnicodeSpaces(normalizeAtPrefix(deColoned))),
|
|
158
161
|
);
|
package/src/tools/read.ts
CHANGED
|
@@ -36,7 +36,7 @@ import {
|
|
|
36
36
|
import { normalizeToLF } from "../edit/normalize";
|
|
37
37
|
import { isNotebookPath, readEditableNotebookText } from "../edit/notebook";
|
|
38
38
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
39
|
-
import { InternalUrlRouter, resolveLocalUrlToFile } from "../internal-urls";
|
|
39
|
+
import { InternalUrlRouter, resolveLocalUrlToFile, resolveLocalUrlToPath } from "../internal-urls";
|
|
40
40
|
import { type ResolvedArtifactFile, resolveArtifactFile } from "../internal-urls/artifact-protocol";
|
|
41
41
|
import { parseInternalUrl } from "../internal-urls/parse";
|
|
42
42
|
import type { InternalUrl } from "../internal-urls/types";
|
|
@@ -920,6 +920,32 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
920
920
|
});
|
|
921
921
|
}
|
|
922
922
|
|
|
923
|
+
/**
|
|
924
|
+
* Recover the active approved plan when a model rewrites its `local://` URL
|
|
925
|
+
* as a same-basename path in the working-directory root.
|
|
926
|
+
*
|
|
927
|
+
* Only missing cwd-root paths qualify, so a real working-tree file always
|
|
928
|
+
* wins and unrelated paths cannot escape into the session artifact sandbox.
|
|
929
|
+
*/
|
|
930
|
+
#approvedPlanAlias(missingAbsolutePath: string): string | undefined {
|
|
931
|
+
const planReferencePath = this.session.getPlanReferencePath?.();
|
|
932
|
+
if (!planReferencePath?.startsWith("local:")) return undefined;
|
|
933
|
+
|
|
934
|
+
const requestedPath = path.resolve(missingAbsolutePath);
|
|
935
|
+
if (path.dirname(requestedPath) !== path.resolve(this.session.cwd)) return undefined;
|
|
936
|
+
|
|
937
|
+
const localProtocolOptions = this.session.localProtocolOptions ?? {
|
|
938
|
+
getArtifactsDir: () => this.session.getArtifactsDir?.() ?? null,
|
|
939
|
+
getSessionId: () => this.session.getSessionId?.() ?? null,
|
|
940
|
+
};
|
|
941
|
+
try {
|
|
942
|
+
const approvedPlanPath = resolveLocalUrlToPath(planReferencePath, localProtocolOptions);
|
|
943
|
+
return path.basename(requestedPath) === path.basename(approvedPlanPath) ? approvedPlanPath : undefined;
|
|
944
|
+
} catch {
|
|
945
|
+
return undefined;
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
|
|
923
949
|
async #tryReadDelimitedPaths(
|
|
924
950
|
readPath: string,
|
|
925
951
|
signal?: AbortSignal,
|
|
@@ -2292,7 +2318,8 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
2292
2318
|
isDirectory = stat.isDirectory();
|
|
2293
2319
|
} catch (error) {
|
|
2294
2320
|
if (isNotFoundError(error)) {
|
|
2295
|
-
// Attempt unique suffix resolution before falling back to
|
|
2321
|
+
// Attempt unique suffix resolution before falling back to the approved-plan
|
|
2322
|
+
// alias or fuzzy suggestions. Existing workspace files retain precedence.
|
|
2296
2323
|
if (!isRemoteMountPath(absolutePath)) {
|
|
2297
2324
|
const suffixMatch = await this.#findSuffixMatchCached(suffixCache, localReadPath, signal);
|
|
2298
2325
|
if (suffixMatch) {
|
|
@@ -2303,12 +2330,30 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
2303
2330
|
isDirectory = retryStat.isDirectory();
|
|
2304
2331
|
suffixResolution = { from: localReadPath, to: suffixMatch.displayPath };
|
|
2305
2332
|
} catch {
|
|
2306
|
-
// Suffix match candidate no longer stats —
|
|
2333
|
+
// Suffix match candidate no longer stats — continue through
|
|
2334
|
+
// approved-plan recovery and the original not-found error.
|
|
2307
2335
|
}
|
|
2308
2336
|
}
|
|
2309
2337
|
}
|
|
2310
2338
|
|
|
2339
|
+
let recoveredApprovedPlan = false;
|
|
2311
2340
|
if (!suffixResolution) {
|
|
2341
|
+
const approvedPlanPath = this.#approvedPlanAlias(absolutePath);
|
|
2342
|
+
if (approvedPlanPath) {
|
|
2343
|
+
try {
|
|
2344
|
+
const approvedPlanStat = await Bun.file(approvedPlanPath).stat();
|
|
2345
|
+
absolutePath = approvedPlanPath;
|
|
2346
|
+
fileSize = approvedPlanStat.size;
|
|
2347
|
+
isDirectory = approvedPlanStat.isDirectory();
|
|
2348
|
+
recoveredApprovedPlan = true;
|
|
2349
|
+
} catch {
|
|
2350
|
+
// The referenced plan disappeared after resolution; continue through
|
|
2351
|
+
// the ordinary delimited-path fallback and not-found error.
|
|
2352
|
+
}
|
|
2353
|
+
}
|
|
2354
|
+
}
|
|
2355
|
+
|
|
2356
|
+
if (!recoveredApprovedPlan && !suffixResolution) {
|
|
2312
2357
|
const delimitedResult = await this.#tryReadDelimitedPaths(readPath, signal);
|
|
2313
2358
|
if (delimitedResult) return delimitedResult;
|
|
2314
2359
|
throw new ToolError(`Path '${localReadPath}' not found`);
|
package/src/tools/write.ts
CHANGED
|
@@ -36,7 +36,7 @@ import {
|
|
|
36
36
|
writeArchive,
|
|
37
37
|
} from "../utils/zip";
|
|
38
38
|
import { routeWriteThroughBridge } from "./acp-bridge";
|
|
39
|
-
import { truncateForPrompt } from "./approval";
|
|
39
|
+
import { resolveToolTier, truncateForPrompt } from "./approval";
|
|
40
40
|
import { assertEditableFile } from "./auto-generated-guard";
|
|
41
41
|
import {
|
|
42
42
|
type ConflictEntry,
|
|
@@ -398,9 +398,27 @@ export class WriteTool implements AgentTool<typeof writeSchema, WriteToolDetails
|
|
|
398
398
|
if (xdevTarget.name === REPORT_ISSUE_DEVICE_NAME) return "write";
|
|
399
399
|
if (xdevTarget.name && isResolutionDeviceName(xdevTarget.name)) return "read";
|
|
400
400
|
const inst = xdevTarget.name ? this.session.xdevRegistry?.get(xdevTarget.name) : undefined;
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
401
|
+
if (!inst) return "exec";
|
|
402
|
+
// Decode the device JSON payload and evaluate the mounted tool's own
|
|
403
|
+
// approval (which may be argument-dependent, e.g. ast_edit is read-tier
|
|
404
|
+
// for internal-URL paths, debug is read-tier for inspection actions).
|
|
405
|
+
// Malformed JSON, non-object payloads, missing content, and approval
|
|
406
|
+
// functions that reject schema-invalid objects stay exec so the gate
|
|
407
|
+
// fails closed — the dispatch itself rejects invalid arguments too.
|
|
408
|
+
const rawContent = (args as Partial<WriteParams>).content;
|
|
409
|
+
if (typeof rawContent !== "string") return "exec";
|
|
410
|
+
let parsed: unknown;
|
|
411
|
+
try {
|
|
412
|
+
parsed = JSON.parse(rawContent);
|
|
413
|
+
} catch {
|
|
414
|
+
return "exec";
|
|
415
|
+
}
|
|
416
|
+
if (!isRecord(parsed)) return "exec";
|
|
417
|
+
try {
|
|
418
|
+
return resolveToolTier(inst, parsed);
|
|
419
|
+
} catch {
|
|
420
|
+
return "exec";
|
|
421
|
+
}
|
|
404
422
|
}
|
|
405
423
|
// Remote SSH writes open an outbound connection and run a remote shell —
|
|
406
424
|
// gate them like the exec-tier `ssh` tool, ahead of the handler-write
|
package/src/tools/xdev.ts
CHANGED
|
@@ -44,15 +44,26 @@ import { ToolError } from "./tool-errors";
|
|
|
44
44
|
*/
|
|
45
45
|
export const XDEV_KEEP_TOP_LEVEL: Record<string, true> = { todo: true, ask: true, grep: true };
|
|
46
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Tools that carry the `xd://` transport itself and therefore can never be
|
|
49
|
+
* mounted as devices: `read xd://` lists/documents devices and
|
|
50
|
+
* `write xd://<tool>` executes them. Demoting either leaves every mounted
|
|
51
|
+
* device unreachable (issue #5764), so they stay top-level regardless of a
|
|
52
|
+
* declared `loadMode`.
|
|
53
|
+
*/
|
|
54
|
+
export const XDEV_TRANSPORT_TOOLS: Record<string, true> = { read: true, write: true };
|
|
55
|
+
|
|
47
56
|
/**
|
|
48
57
|
* Whether an enabled tool is presented under `xd://` (rather than top-level)
|
|
49
58
|
* while the `xd://` transport is active. Discoverable tools mount unless they
|
|
50
|
-
* are pinned top-level by {@link XDEV_KEEP_TOP_LEVEL}
|
|
51
|
-
*
|
|
59
|
+
* are pinned top-level by {@link XDEV_KEEP_TOP_LEVEL} or carry the transport
|
|
60
|
+
* itself ({@link XDEV_TRANSPORT_TOOLS}); essential tools never do. The caller
|
|
61
|
+
* gates this on the transport being active (a session-owned
|
|
52
62
|
* {@link XdevRegistry} existing).
|
|
53
63
|
*/
|
|
54
64
|
export function isMountableUnderXdev(tool: { name: string; loadMode?: ToolLoadMode }): boolean {
|
|
55
|
-
|
|
65
|
+
if (tool.name in XDEV_TRANSPORT_TOOLS || tool.name in XDEV_KEEP_TOP_LEVEL) return false;
|
|
66
|
+
return tool.loadMode === "discoverable";
|
|
56
67
|
}
|
|
57
68
|
|
|
58
69
|
/** Dispatch metadata carried on write-tool details for renderer delegation. */
|
|
@@ -68,6 +68,7 @@ function getTitleModel(registry: ModelRegistry, settings: Settings, currentModel
|
|
|
68
68
|
* resolver instead of a pre-evaluated value ensures the metadata's account_uuid
|
|
69
69
|
* reflects the credential actually selected for this request.
|
|
70
70
|
* @param customSystemPrompt Optional title-specific system prompt override
|
|
71
|
+
* @param signal Session-lifecycle cancellation for background title requests
|
|
71
72
|
*/
|
|
72
73
|
export async function generateSessionTitle(
|
|
73
74
|
firstMessage: string,
|
|
@@ -77,6 +78,7 @@ export async function generateSessionTitle(
|
|
|
77
78
|
currentModel?: Model<Api>,
|
|
78
79
|
metadataResolver?: (provider: string) => Record<string, unknown> | undefined,
|
|
79
80
|
customSystemPrompt?: string,
|
|
81
|
+
signal?: AbortSignal,
|
|
80
82
|
): Promise<string | null> {
|
|
81
83
|
// Defer titling for greetings / acknowledgements / empty input. The default
|
|
82
84
|
// tiny title model can't reliably decline trivial input, so this happens
|
|
@@ -97,7 +99,7 @@ export async function generateSessionTitle(
|
|
|
97
99
|
sessionId,
|
|
98
100
|
currentModel,
|
|
99
101
|
metadataResolver,
|
|
100
|
-
|
|
102
|
+
signal,
|
|
101
103
|
titleSystemPrompt,
|
|
102
104
|
);
|
|
103
105
|
}
|
|
@@ -117,9 +119,18 @@ export async function generateSessionTitle(
|
|
|
117
119
|
return null;
|
|
118
120
|
}
|
|
119
121
|
try {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
122
|
+
let localTitle: string | null;
|
|
123
|
+
if (signal) {
|
|
124
|
+
localTitle = await tinyTitleClient.generate(
|
|
125
|
+
tinyModel,
|
|
126
|
+
firstMessage,
|
|
127
|
+
titleSystemPrompt ? { signal, systemPrompt: titleSystemPrompt } : { signal },
|
|
128
|
+
);
|
|
129
|
+
} else if (titleSystemPrompt) {
|
|
130
|
+
localTitle = await tinyTitleClient.generate(tinyModel, firstMessage, { systemPrompt: titleSystemPrompt });
|
|
131
|
+
} else {
|
|
132
|
+
localTitle = await tinyTitleClient.generate(tinyModel, firstMessage);
|
|
133
|
+
}
|
|
123
134
|
if (!localTitle) {
|
|
124
135
|
logger.warn("title-generator: local tiny model produced no title; skipping (no online fallback)", {
|
|
125
136
|
sessionId,
|
package/src/web/search/index.ts
CHANGED
|
@@ -8,6 +8,7 @@ import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallb
|
|
|
8
8
|
import type { AuthStorage } from "@oh-my-pi/pi-ai";
|
|
9
9
|
import { prompt } from "@oh-my-pi/pi-utils";
|
|
10
10
|
import { type } from "arktype";
|
|
11
|
+
import { ModelRegistry } from "../../config/model-registry";
|
|
11
12
|
import { settings } from "../../config/settings";
|
|
12
13
|
import type { CustomTool, CustomToolContext, RenderResultOptions } from "../../extensibility/custom-tools/types";
|
|
13
14
|
import type { Theme } from "../../modes/theme/theme";
|
|
@@ -118,6 +119,7 @@ function hasRenderableSearchContent(response: SearchResponse): boolean {
|
|
|
118
119
|
|
|
119
120
|
interface ExecuteSearchOptions {
|
|
120
121
|
authStorage: AuthStorage;
|
|
122
|
+
modelRegistry?: ModelRegistry;
|
|
121
123
|
sessionId?: string;
|
|
122
124
|
signal?: AbortSignal;
|
|
123
125
|
}
|
|
@@ -128,7 +130,7 @@ async function executeSearch(
|
|
|
128
130
|
params: SearchQueryParams,
|
|
129
131
|
options: ExecuteSearchOptions,
|
|
130
132
|
): Promise<{ content: Array<{ type: "text"; text: string }>; details: SearchRenderDetails }> {
|
|
131
|
-
const { authStorage, sessionId, signal } = options;
|
|
133
|
+
const { authStorage, modelRegistry, sessionId, signal } = options;
|
|
132
134
|
const explicitProvider = params.provider;
|
|
133
135
|
let candidates: SearchProviderCandidate[];
|
|
134
136
|
if (explicitProvider && explicitProvider !== "auto") {
|
|
@@ -187,6 +189,7 @@ async function executeSearch(
|
|
|
187
189
|
temperature: params.temperature,
|
|
188
190
|
signal,
|
|
189
191
|
authStorage,
|
|
192
|
+
modelRegistry,
|
|
190
193
|
sessionId,
|
|
191
194
|
antigravityEndpointMode,
|
|
192
195
|
geminiModel,
|
|
@@ -246,16 +249,18 @@ async function executeSearch(
|
|
|
246
249
|
*/
|
|
247
250
|
export async function runSearchQuery(
|
|
248
251
|
params: SearchQueryParams,
|
|
249
|
-
options: { authStorage?: AuthStorage; sessionId?: string; signal?: AbortSignal } = {},
|
|
252
|
+
options: { authStorage?: AuthStorage; modelRegistry?: ModelRegistry; sessionId?: string; signal?: AbortSignal } = {},
|
|
250
253
|
): Promise<{ content: Array<{ type: "text"; text: string }>; details: SearchRenderDetails }> {
|
|
251
|
-
const createdAuthStorage = options.authStorage ? undefined : await discoverAuthStorage();
|
|
252
|
-
const authStorage = options.authStorage ?? createdAuthStorage;
|
|
254
|
+
const createdAuthStorage = options.authStorage || options.modelRegistry ? undefined : await discoverAuthStorage();
|
|
255
|
+
const authStorage = options.authStorage ?? options.modelRegistry?.authStorage ?? createdAuthStorage;
|
|
253
256
|
if (!authStorage) {
|
|
254
257
|
throw new Error("Failed to initialize authentication storage");
|
|
255
258
|
}
|
|
259
|
+
const modelRegistry = options.modelRegistry ?? (createdAuthStorage ? new ModelRegistry(authStorage) : undefined);
|
|
256
260
|
try {
|
|
257
261
|
return await executeSearch("cli-web-search", params, {
|
|
258
262
|
authStorage,
|
|
263
|
+
modelRegistry,
|
|
259
264
|
sessionId: options.sessionId,
|
|
260
265
|
signal: options.signal,
|
|
261
266
|
});
|
|
@@ -295,7 +300,12 @@ export class WebSearchTool implements AgentTool<typeof webSearchSchema, SearchRe
|
|
|
295
300
|
): Promise<AgentToolResult<SearchRenderDetails>> {
|
|
296
301
|
const authStorage = this.#session.authStorage ?? (await discoverAuthStorage());
|
|
297
302
|
const sessionId = this.#session.getSessionId?.() ?? undefined;
|
|
298
|
-
return executeSearch(_toolCallId, params, {
|
|
303
|
+
return executeSearch(_toolCallId, params, {
|
|
304
|
+
authStorage,
|
|
305
|
+
modelRegistry: this.#session.modelRegistry,
|
|
306
|
+
sessionId,
|
|
307
|
+
signal,
|
|
308
|
+
});
|
|
299
309
|
}
|
|
300
310
|
}
|
|
301
311
|
|
|
@@ -316,7 +326,12 @@ export const webSearchCustomTool: CustomTool<typeof webSearchSchema, SearchRende
|
|
|
316
326
|
) {
|
|
317
327
|
const authStorage = ctx.modelRegistry?.authStorage ?? (await discoverAuthStorage());
|
|
318
328
|
const sessionId = ctx.sessionManager.getSessionId();
|
|
319
|
-
return executeSearch(toolCallId, params, {
|
|
329
|
+
return executeSearch(toolCallId, params, {
|
|
330
|
+
authStorage,
|
|
331
|
+
modelRegistry: ctx.modelRegistry,
|
|
332
|
+
sessionId,
|
|
333
|
+
signal,
|
|
334
|
+
});
|
|
320
335
|
},
|
|
321
336
|
|
|
322
337
|
renderCall(args: SearchToolParams, options: RenderResultOptions, theme: Theme) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AuthStorage, FetchImpl } from "@oh-my-pi/pi-ai";
|
|
2
|
+
import type { ModelRegistry } from "../../../config/model-registry";
|
|
2
3
|
import type { SearchProviderId, SearchResponse } from "../types";
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -45,6 +46,8 @@ export interface SearchParams {
|
|
|
45
46
|
* the per-credential single-flight refresh.
|
|
46
47
|
*/
|
|
47
48
|
authStorage: AuthStorage;
|
|
49
|
+
/** Provider/model transport settings used by native search endpoints. */
|
|
50
|
+
modelRegistry?: ModelRegistry;
|
|
48
51
|
/**
|
|
49
52
|
* Optional session id used as the round-robin / sticky key when selecting
|
|
50
53
|
* among multiple credentials for the same provider. Pass through from the
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Kimi Web Search Provider
|
|
3
3
|
*
|
|
4
|
-
* Uses
|
|
4
|
+
* Uses the Kimi Code search API to retrieve web results. This is the Kimi Code
|
|
5
|
+
* membership service, distinct from the Moonshot Open Platform — it requires a
|
|
6
|
+
* Kimi Code Console credential (`omp /login kimi-code` or an explicit
|
|
7
|
+
* `MOONSHOT_SEARCH_API_KEY` / `KIMI_SEARCH_API_KEY`), not `MOONSHOT_API_KEY`.
|
|
5
8
|
* Endpoint: POST https://api.kimi.com/coding/v1/search
|
|
6
9
|
*/
|
|
7
10
|
import { type ApiKey, type AuthStorage, type FetchImpl, withAuth } from "@oh-my-pi/pi-ai";
|
|
@@ -58,11 +61,17 @@ function resolveBaseUrl(): string {
|
|
|
58
61
|
}
|
|
59
62
|
|
|
60
63
|
/**
|
|
61
|
-
* Resolve the Kimi search credential. Highest precedence is the
|
|
62
|
-
* otherwise an AuthStorage-backed resolver for
|
|
63
|
-
*
|
|
64
|
-
* the central force-refresh / sibling-rotate retry. Returns
|
|
65
|
-
* neither is configured.
|
|
64
|
+
* Resolve the Kimi Code search credential. Highest precedence is the explicit
|
|
65
|
+
* search-key env override; otherwise an AuthStorage-backed resolver for a
|
|
66
|
+
* stored `kimi-code` credential (from `omp /login kimi-code`), so a stale token
|
|
67
|
+
* triggers the central force-refresh / sibling-rotate retry. Returns
|
|
68
|
+
* `undefined` when neither is configured.
|
|
69
|
+
*
|
|
70
|
+
* The endpoint (`https://api.kimi.com/coding/v1/search`) is the Kimi Code
|
|
71
|
+
* membership service, which has a different credential system from the Moonshot
|
|
72
|
+
* Open Platform (`https://api.moonshot.ai`). A stored `moonshot` credential
|
|
73
|
+
* (or `MOONSHOT_API_KEY`) is NOT accepted here — it 401s against Kimi Code
|
|
74
|
+
* (issue #5762).
|
|
66
75
|
*/
|
|
67
76
|
async function resolveKey(
|
|
68
77
|
authStorage: AuthStorage,
|
|
@@ -72,10 +81,8 @@ async function resolveKey(
|
|
|
72
81
|
const envKey = asTrimmed($env.MOONSHOT_SEARCH_API_KEY) ?? asTrimmed($env.KIMI_SEARCH_API_KEY);
|
|
73
82
|
if (envKey) return envKey;
|
|
74
83
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
if (stored) return authStorage.resolver(provider, { sessionId });
|
|
78
|
-
}
|
|
84
|
+
const stored = await authStorage.getApiKey("kimi-code", sessionId, { signal });
|
|
85
|
+
if (stored) return authStorage.resolver("kimi-code", { sessionId });
|
|
79
86
|
return undefined;
|
|
80
87
|
}
|
|
81
88
|
|
|
@@ -127,7 +134,7 @@ export async function searchKimi(params: KimiSearchParams): Promise<SearchRespon
|
|
|
127
134
|
const keyOrResolver = await resolveKey(params.authStorage, params.sessionId, params.signal);
|
|
128
135
|
if (!keyOrResolver) {
|
|
129
136
|
throw new Error(
|
|
130
|
-
"Kimi search credentials not found.
|
|
137
|
+
"Kimi search credentials not found. Kimi web search uses the Kimi Code service (api.kimi.com); set MOONSHOT_SEARCH_API_KEY / KIMI_SEARCH_API_KEY to a Kimi Code Console key, or login with 'omp /login kimi-code'. A Moonshot Open Platform key (MOONSHOT_API_KEY) is not accepted here.",
|
|
131
138
|
);
|
|
132
139
|
}
|
|
133
140
|
|
|
@@ -176,7 +183,6 @@ export class KimiProvider extends SearchProvider {
|
|
|
176
183
|
return (
|
|
177
184
|
!!asTrimmed($env.MOONSHOT_SEARCH_API_KEY) ||
|
|
178
185
|
!!asTrimmed($env.KIMI_SEARCH_API_KEY) ||
|
|
179
|
-
authStorage.hasAuth("moonshot") ||
|
|
180
186
|
authStorage.hasAuth("kimi-code")
|
|
181
187
|
);
|
|
182
188
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type ApiKey, type ApiKeyResolver, type AuthStorage, withAuth } from "@oh-my-pi/pi-ai";
|
|
2
2
|
import { $env } from "@oh-my-pi/pi-utils";
|
|
3
|
+
import { resolveXAIHttpTransport, type XAIHttpProvider, type XAIHttpTransport } from "../../../lib/xai-http";
|
|
3
4
|
import type { SearchCitation, SearchResponse, SearchSource, SearchUsage } from "../../../web/search/types";
|
|
4
5
|
import { SearchProviderError } from "../../../web/search/types";
|
|
5
6
|
import { clampNumResults } from "../utils";
|
|
@@ -7,7 +8,7 @@ import type { SearchParams } from "./base";
|
|
|
7
8
|
import { SearchProvider } from "./base";
|
|
8
9
|
import { classifyProviderHttpError, withHardTimeout } from "./utils";
|
|
9
10
|
|
|
10
|
-
const
|
|
11
|
+
const XAI_DEFAULT_BASE_URL = "https://api.x.ai/v1";
|
|
11
12
|
const XAI_WEB_SEARCH_MODEL = "grok-4.3";
|
|
12
13
|
const DEFAULT_NUM_RESULTS = 10;
|
|
13
14
|
const MAX_NUM_RESULTS = 30;
|
|
@@ -75,10 +76,12 @@ async function postXAIResponses(
|
|
|
75
76
|
apiKey: string,
|
|
76
77
|
params: SearchParams,
|
|
77
78
|
body: Record<string, unknown>,
|
|
79
|
+
transport: XAIHttpTransport,
|
|
78
80
|
): Promise<Response> {
|
|
79
|
-
return (params.fetch ?? fetch)(
|
|
81
|
+
return (params.fetch ?? fetch)(`${transport.baseURL.replace(/\/+$/, "")}/responses`, {
|
|
80
82
|
method: "POST",
|
|
81
83
|
headers: {
|
|
84
|
+
...transport.headers,
|
|
82
85
|
"Content-Type": "application/json",
|
|
83
86
|
Authorization: `Bearer ${apiKey}`,
|
|
84
87
|
},
|
|
@@ -93,9 +96,13 @@ function throwXAIResponsesError(status: number, errorText: string): never {
|
|
|
93
96
|
throw new SearchProviderError("xai", `xAI Responses API error (${status}): ${errorText}`, status);
|
|
94
97
|
}
|
|
95
98
|
|
|
96
|
-
async function callXAIResponses(
|
|
99
|
+
async function callXAIResponses(
|
|
100
|
+
apiKey: string,
|
|
101
|
+
params: SearchParams,
|
|
102
|
+
transport: XAIHttpTransport,
|
|
103
|
+
): Promise<XAIResponsesResponse> {
|
|
97
104
|
const requestBody = buildRequestBody(params);
|
|
98
|
-
const response = await postXAIResponses(apiKey, params, requestBody);
|
|
105
|
+
const response = await postXAIResponses(apiKey, params, requestBody, transport);
|
|
99
106
|
|
|
100
107
|
if (!response.ok) {
|
|
101
108
|
throwXAIResponsesError(response.status, await response.text());
|
|
@@ -235,19 +242,24 @@ function shouldPreferXAIOAuth(authStorage: AuthStorage): boolean {
|
|
|
235
242
|
return true;
|
|
236
243
|
}
|
|
237
244
|
|
|
238
|
-
|
|
245
|
+
interface XAIWebSearchAuth {
|
|
246
|
+
provider: XAIHttpProvider;
|
|
247
|
+
keyOrResolver: ApiKey;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function resolveXAIWebSearchAuth(params: SearchParams): XAIWebSearchAuth {
|
|
239
251
|
const xaiResolver = params.authStorage.resolver("xai", {
|
|
240
252
|
sessionId: params.sessionId,
|
|
241
253
|
});
|
|
242
254
|
const xaiOAuthOrigin = params.authStorage.getCredentialOrigin("xai-oauth");
|
|
243
255
|
if (!shouldPreferXAIOAuth(params.authStorage)) {
|
|
244
|
-
return xaiResolver;
|
|
256
|
+
return { provider: "xai", keyOrResolver: xaiResolver };
|
|
245
257
|
}
|
|
246
258
|
|
|
247
259
|
const xaiOAuthResolver = params.authStorage.resolver("xai-oauth", {
|
|
248
260
|
sessionId: params.sessionId,
|
|
249
261
|
});
|
|
250
|
-
|
|
262
|
+
const keyOrResolver: ApiKeyResolver = async ctx => {
|
|
251
263
|
const xaiOAuthKey = await xaiOAuthResolver(ctx);
|
|
252
264
|
if (xaiOAuthKey) {
|
|
253
265
|
const borrowedSharedEnvKey =
|
|
@@ -259,14 +271,33 @@ function resolveXAIWebSearchApiKey(params: SearchParams): ApiKeyResolver {
|
|
|
259
271
|
}
|
|
260
272
|
return xaiResolver(ctx);
|
|
261
273
|
};
|
|
274
|
+
return { provider: "xai-oauth", keyOrResolver };
|
|
262
275
|
}
|
|
263
276
|
|
|
264
277
|
/** Execute xAI Responses API web search. */
|
|
265
278
|
export async function searchXAI(params: SearchParams): Promise<SearchResponse> {
|
|
266
|
-
const
|
|
279
|
+
const auth = resolveXAIWebSearchAuth(params);
|
|
280
|
+
const transport = params.modelRegistry
|
|
281
|
+
? resolveXAIHttpTransport(params.modelRegistry, auth.provider, XAI_WEB_SEARCH_MODEL)
|
|
282
|
+
: { baseURL: XAI_DEFAULT_BASE_URL };
|
|
283
|
+
const customEndpoint = transport.baseURL.replace(/\/+$/, "") !== XAI_DEFAULT_BASE_URL;
|
|
284
|
+
const credentialOrigin = params.authStorage.getCredentialOrigin(auth.provider);
|
|
285
|
+
if (
|
|
286
|
+
customEndpoint &&
|
|
287
|
+
auth.provider === "xai-oauth" &&
|
|
288
|
+
(credentialOrigin?.kind === "oauth" || credentialOrigin?.kind === "env")
|
|
289
|
+
) {
|
|
290
|
+
throw new SearchProviderError(
|
|
291
|
+
"xai",
|
|
292
|
+
`Refusing to send official xAI OAuth credentials to custom endpoint ${transport.baseURL}. Configure an API key for provider "xai-oauth".`,
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
const keyOrResolver: ApiKey = customEndpoint
|
|
296
|
+
? params.authStorage.resolver(auth.provider, { sessionId: params.sessionId })
|
|
297
|
+
: auth.keyOrResolver;
|
|
267
298
|
|
|
268
299
|
const resultCap = clampNumResults(params.numSearchResults ?? params.limit, DEFAULT_NUM_RESULTS, MAX_NUM_RESULTS);
|
|
269
|
-
const response = await withAuth(keyOrResolver, (key: string) => callXAIResponses(key, params), {
|
|
300
|
+
const response = await withAuth(keyOrResolver, (key: string) => callXAIResponses(key, params, transport), {
|
|
270
301
|
signal: params.signal,
|
|
271
302
|
missingKeyMessage: 'xAI credentials not found. Set XAI_API_KEY or configure an API key for provider "xai".',
|
|
272
303
|
});
|
package/src/web/search/types.ts
CHANGED
|
@@ -39,7 +39,12 @@ export const SEARCH_PROVIDER_OPTIONS = [
|
|
|
39
39
|
{ value: "tavily", label: "Tavily", description: "Requires TAVILY_API_KEY" },
|
|
40
40
|
{ value: "firecrawl", label: "Firecrawl", description: "Requires FIRECRAWL_API_KEY" },
|
|
41
41
|
{ value: "brave", label: "Brave", description: "Requires BRAVE_API_KEY" },
|
|
42
|
-
{
|
|
42
|
+
{
|
|
43
|
+
value: "kimi",
|
|
44
|
+
label: "Kimi",
|
|
45
|
+
description:
|
|
46
|
+
"Kimi Code search (requires a Kimi Code Console key via KIMI_SEARCH_API_KEY/MOONSHOT_SEARCH_API_KEY or /login kimi-code; not MOONSHOT_API_KEY)",
|
|
47
|
+
},
|
|
43
48
|
{ value: "parallel", label: "Parallel", description: "Requires PARALLEL_API_KEY" },
|
|
44
49
|
{ value: "synthetic", label: "Synthetic", description: "Requires SYNTHETIC_API_KEY" },
|
|
45
50
|
{ value: "searxng", label: "SearXNG", description: "Requires SEARXNG_ENDPOINT or searxng.endpoint" },
|