@gajae-code/coding-agent 0.5.4 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/dist/types/cli/web-search-cli.d.ts +12 -0
- package/dist/types/commands/rlm.d.ts +10 -0
- package/dist/types/commands/web-search.d.ts +54 -0
- package/dist/types/config/keybindings.d.ts +10 -0
- package/dist/types/config/model-profiles.d.ts +2 -1
- package/dist/types/config/model-registry.d.ts +3 -0
- package/dist/types/config/models-config-schema.d.ts +3 -0
- package/dist/types/config/settings-schema.d.ts +61 -3
- package/dist/types/edit/notebook.d.ts +3 -0
- package/dist/types/eval/py/executor.d.ts +3 -0
- package/dist/types/eval/py/kernel.d.ts +3 -1
- package/dist/types/eval/py/runtime.d.ts +9 -1
- package/dist/types/exec/bash-executor.d.ts +4 -0
- package/dist/types/extensibility/custom-tools/types.d.ts +2 -0
- package/dist/types/extensibility/custom-tools/wrapper.d.ts +1 -0
- package/dist/types/extensibility/extensions/types.d.ts +2 -0
- package/dist/types/extensibility/extensions/wrapper.d.ts +1 -0
- package/dist/types/gjc-runtime/launch-tmux.d.ts +6 -0
- package/dist/types/gjc-runtime/session-state-sidecar.d.ts +14 -0
- package/dist/types/gjc-runtime/tmux-common.d.ts +6 -0
- package/dist/types/gjc-runtime/tmux-gc.d.ts +3 -3
- package/dist/types/gjc-runtime/tmux-sessions.d.ts +4 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +18 -0
- package/dist/types/goals/state.d.ts +1 -1
- package/dist/types/goals/tools/goal-tool.d.ts +2 -0
- package/dist/types/main.d.ts +11 -0
- package/dist/types/modes/components/custom-editor.d.ts +4 -2
- package/dist/types/modes/components/custom-model-preset-wizard.d.ts +12 -0
- package/dist/types/modes/components/model-selector.d.ts +5 -2
- package/dist/types/modes/components/status-line.d.ts +4 -1
- package/dist/types/modes/controllers/input-controller.d.ts +3 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +1 -0
- package/dist/types/modes/print-mode.d.ts +6 -0
- package/dist/types/modes/rpc/rpc-client.d.ts +21 -0
- package/dist/types/modes/rpc/rpc-socket-security.d.ts +7 -0
- package/dist/types/modes/rpc/rpc-types.d.ts +13 -0
- package/dist/types/modes/shared/agent-wire/command-dispatch.d.ts +2 -0
- package/dist/types/modes/shared/agent-wire/unattended-session.d.ts +1 -0
- package/dist/types/rlm/artifacts.d.ts +9 -0
- package/dist/types/rlm/complete-research-tool.d.ts +35 -0
- package/dist/types/rlm/data-context.d.ts +6 -0
- package/dist/types/rlm/index.d.ts +35 -0
- package/dist/types/rlm/notebook.d.ts +12 -0
- package/dist/types/rlm/preset.d.ts +23 -0
- package/dist/types/rlm/python-tool.d.ts +16 -0
- package/dist/types/rlm/report.d.ts +14 -0
- package/dist/types/rlm/types.d.ts +37 -0
- package/dist/types/sdk.d.ts +7 -0
- package/dist/types/session/agent-session.d.ts +21 -0
- package/dist/types/tools/bash-allowed-prefixes.d.ts +6 -1
- package/dist/types/tools/browser/attach.d.ts +19 -3
- package/dist/types/tools/browser/registry.d.ts +15 -0
- package/dist/types/tools/browser/render.d.ts +3 -0
- package/dist/types/tools/browser.d.ts +18 -1
- package/dist/types/tools/computer/render.d.ts +17 -0
- package/dist/types/tools/computer.d.ts +465 -0
- package/dist/types/tools/index.d.ts +24 -1
- package/dist/types/tools/job.d.ts +13 -0
- package/dist/types/tools/tool-timeouts.d.ts +5 -0
- package/dist/types/web/search/index.d.ts +32 -2
- package/dist/types/web/search/providers/base.d.ts +22 -0
- package/dist/types/web/search/providers/xai.d.ts +64 -0
- package/dist/types/web/search/types.d.ts +11 -3
- package/package.json +7 -7
- package/src/cli/web-search-cli.ts +123 -8
- package/src/cli.ts +2 -0
- package/src/commands/rlm.ts +19 -0
- package/src/commands/web-search.ts +66 -0
- package/src/config/keybindings.ts +11 -0
- package/src/config/model-profiles.ts +11 -3
- package/src/config/model-registry.ts +55 -1
- package/src/config/models-config-schema.ts +1 -0
- package/src/config/settings-schema.ts +67 -1
- package/src/edit/notebook.ts +6 -2
- package/src/eval/py/executor.ts +8 -1
- package/src/eval/py/kernel.ts +9 -4
- package/src/eval/py/runtime.ts +153 -32
- package/src/exec/bash-executor.ts +10 -4
- package/src/extensibility/custom-tools/types.ts +2 -0
- package/src/extensibility/custom-tools/wrapper.ts +2 -0
- package/src/extensibility/extensions/types.ts +2 -0
- package/src/extensibility/extensions/wrapper.ts +1 -0
- package/src/gjc-runtime/launch-tmux.ts +129 -1
- package/src/gjc-runtime/session-state-sidecar.ts +61 -1
- package/src/gjc-runtime/tmux-common.ts +26 -2
- package/src/gjc-runtime/tmux-gc.ts +40 -27
- package/src/gjc-runtime/tmux-sessions.ts +13 -1
- package/src/gjc-runtime/ultragoal-runtime.ts +340 -18
- package/src/goals/runtime.ts +4 -3
- package/src/goals/state.ts +1 -1
- package/src/goals/tools/goal-tool.ts +16 -3
- package/src/internal-urls/docs-index.generated.ts +13 -9
- package/src/main.ts +28 -3
- package/src/modes/components/custom-editor.ts +13 -4
- package/src/modes/components/custom-model-preset-wizard.ts +293 -0
- package/src/modes/components/hook-selector.ts +1 -1
- package/src/modes/components/model-selector.ts +72 -29
- package/src/modes/components/skill-message.ts +62 -8
- package/src/modes/components/status-line.ts +13 -1
- package/src/modes/controllers/input-controller.ts +60 -11
- package/src/modes/controllers/selector-controller.ts +39 -0
- package/src/modes/interactive-mode.ts +1 -1
- package/src/modes/print-mode.ts +14 -4
- package/src/modes/rpc/rpc-client.ts +250 -80
- package/src/modes/rpc/rpc-mode.ts +6 -12
- package/src/modes/rpc/rpc-socket-security.ts +103 -0
- package/src/modes/rpc/rpc-types.ts +10 -0
- package/src/modes/shared/agent-wire/command-dispatch.ts +7 -0
- package/src/modes/shared/agent-wire/command-validation.ts +1 -0
- package/src/modes/shared/agent-wire/scopes.ts +1 -0
- package/src/modes/shared/agent-wire/unattended-session.ts +9 -0
- package/src/modes/utils/hotkeys-markdown.ts +4 -2
- package/src/modes/utils/ui-helpers.ts +2 -2
- package/src/prompts/goals/goal-continuation.md +1 -0
- package/src/prompts/goals/goal-mode-active.md +1 -0
- package/src/prompts/system/rlm-report-command.md +1 -0
- package/src/prompts/system/rlm-research.md +23 -0
- package/src/prompts/tools/bash.md +23 -2
- package/src/prompts/tools/browser.md +7 -3
- package/src/prompts/tools/computer.md +74 -0
- package/src/prompts/tools/goal.md +3 -0
- package/src/prompts/tools/job.md +9 -1
- package/src/prompts/tools/web-search.md +7 -0
- package/src/rlm/artifacts.ts +60 -0
- package/src/rlm/complete-research-tool.ts +163 -0
- package/src/rlm/data-context.ts +26 -0
- package/src/rlm/index.ts +339 -0
- package/src/rlm/notebook.ts +108 -0
- package/src/rlm/preset.ts +76 -0
- package/src/rlm/python-tool.ts +68 -0
- package/src/rlm/report.ts +70 -0
- package/src/rlm/types.ts +40 -0
- package/src/sdk.ts +12 -0
- package/src/session/agent-session.ts +48 -3
- package/src/slash-commands/builtin-registry.ts +17 -0
- package/src/tools/bash-allowed-prefixes.ts +84 -1
- package/src/tools/bash.ts +80 -13
- package/src/tools/browser/attach.ts +103 -3
- package/src/tools/browser/registry.ts +176 -2
- package/src/tools/browser/render.ts +9 -1
- package/src/tools/browser.ts +33 -0
- package/src/tools/computer/render.ts +78 -0
- package/src/tools/computer.ts +640 -0
- package/src/tools/index.ts +41 -1
- package/src/tools/job.ts +88 -5
- package/src/tools/json-tree.ts +42 -29
- package/src/tools/renderers.ts +2 -0
- package/src/tools/tool-timeouts.ts +1 -0
- package/src/web/search/index.ts +27 -2
- package/src/web/search/provider.ts +16 -1
- package/src/web/search/providers/base.ts +22 -0
- package/src/web/search/providers/xai.ts +511 -0
- package/src/web/search/render.ts +7 -0
- package/src/web/search/types.ts +11 -1
|
@@ -32,6 +32,28 @@ export interface SearchParams {
|
|
|
32
32
|
maxOutputTokens?: number;
|
|
33
33
|
numSearchResults?: number;
|
|
34
34
|
temperature?: number;
|
|
35
|
+
/** xAI-specific search surface. Defaults to web_search when omitted. */
|
|
36
|
+
xaiSearchMode?: "web" | "x" | "web_and_x";
|
|
37
|
+
/** xAI web_search domain allow-list (max 5). */
|
|
38
|
+
allowedDomains?: string[];
|
|
39
|
+
/** xAI web_search domain deny-list (max 5). */
|
|
40
|
+
excludedDomains?: string[];
|
|
41
|
+
/** xAI x_search handle allow-list (max 20). */
|
|
42
|
+
allowedXHandles?: string[];
|
|
43
|
+
/** xAI x_search handle deny-list (max 20). */
|
|
44
|
+
excludedXHandles?: string[];
|
|
45
|
+
/** xAI x_search lower date bound, ISO8601 date such as YYYY-MM-DD. */
|
|
46
|
+
fromDate?: string;
|
|
47
|
+
/** xAI x_search upper date bound, ISO8601 date such as YYYY-MM-DD. */
|
|
48
|
+
toDate?: string;
|
|
49
|
+
/** xAI web_search/x_search image understanding. */
|
|
50
|
+
enableImageUnderstanding?: boolean;
|
|
51
|
+
/** xAI web_search image search result embedding. */
|
|
52
|
+
enableImageSearch?: boolean;
|
|
53
|
+
/** xAI x_search video understanding. */
|
|
54
|
+
enableVideoUnderstanding?: boolean;
|
|
55
|
+
/** xAI Responses include=["no_inline_citations"]. */
|
|
56
|
+
noInlineCitations?: boolean;
|
|
35
57
|
googleSearch?: Record<string, unknown>;
|
|
36
58
|
codeExecution?: Record<string, unknown>;
|
|
37
59
|
urlContext?: Record<string, unknown>;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* xAI Web/X Search Provider
|
|
3
|
+
*
|
|
4
|
+
* Uses xAI's Responses API with the built-in web_search and x_search tools.
|
|
5
|
+
* Endpoint: POST https://api.x.ai/v1/responses
|
|
6
|
+
*/
|
|
7
|
+
import type { AuthStorage } from "@gajae-code/ai";
|
|
8
|
+
import type { SearchCitation, SearchResponse } from "../../../web/search/types";
|
|
9
|
+
import type { SearchParams } from "./base";
|
|
10
|
+
import { SearchProvider } from "./base";
|
|
11
|
+
declare const XAI_SEARCH_MODES: readonly ["web", "x", "web_and_x"];
|
|
12
|
+
export type XaiSearchMode = (typeof XAI_SEARCH_MODES)[number];
|
|
13
|
+
export interface XaiSearchParams {
|
|
14
|
+
query: string;
|
|
15
|
+
system_prompt?: string;
|
|
16
|
+
num_results?: number;
|
|
17
|
+
max_output_tokens?: number;
|
|
18
|
+
temperature?: number;
|
|
19
|
+
recency?: "day" | "week" | "month" | "year";
|
|
20
|
+
xai_search_mode?: XaiSearchMode;
|
|
21
|
+
allowed_domains?: string[];
|
|
22
|
+
excluded_domains?: string[];
|
|
23
|
+
allowed_x_handles?: string[];
|
|
24
|
+
excluded_x_handles?: string[];
|
|
25
|
+
from_date?: string;
|
|
26
|
+
to_date?: string;
|
|
27
|
+
enable_image_understanding?: boolean;
|
|
28
|
+
enable_image_search?: boolean;
|
|
29
|
+
enable_video_understanding?: boolean;
|
|
30
|
+
no_inline_citations?: boolean;
|
|
31
|
+
signal?: AbortSignal;
|
|
32
|
+
authStorage: AuthStorage;
|
|
33
|
+
sessionId?: string;
|
|
34
|
+
}
|
|
35
|
+
export declare function buildXaiRequestBody(params: {
|
|
36
|
+
query: string;
|
|
37
|
+
systemPrompt: string;
|
|
38
|
+
model: string;
|
|
39
|
+
maxOutputTokens?: number;
|
|
40
|
+
temperature?: number;
|
|
41
|
+
recency?: XaiSearchParams["recency"];
|
|
42
|
+
xaiSearchMode?: XaiSearchMode;
|
|
43
|
+
allowedDomains?: string[];
|
|
44
|
+
excludedDomains?: string[];
|
|
45
|
+
allowedXHandles?: string[];
|
|
46
|
+
excludedXHandles?: string[];
|
|
47
|
+
fromDate?: string;
|
|
48
|
+
toDate?: string;
|
|
49
|
+
enableImageUnderstanding?: boolean;
|
|
50
|
+
enableImageSearch?: boolean;
|
|
51
|
+
enableVideoUnderstanding?: boolean;
|
|
52
|
+
noInlineCitations?: boolean;
|
|
53
|
+
}): Record<string, unknown>;
|
|
54
|
+
export declare function parseXaiCitations(json: any): SearchCitation[];
|
|
55
|
+
/** Execute xAI web/X search through the Responses API search tools. */
|
|
56
|
+
export declare function searchXai(params: XaiSearchParams): Promise<SearchResponse>;
|
|
57
|
+
/** Search provider for xAI web and X search. */
|
|
58
|
+
export declare class XaiProvider extends SearchProvider {
|
|
59
|
+
readonly id = "xai";
|
|
60
|
+
readonly label = "xAI";
|
|
61
|
+
isAvailable(authStorage: AuthStorage): boolean;
|
|
62
|
+
search(params: SearchParams): Promise<SearchResponse>;
|
|
63
|
+
}
|
|
64
|
+
export {};
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Unified types for web search responses across supported providers.
|
|
5
5
|
*/
|
|
6
6
|
/** Supported web search providers */
|
|
7
|
-
export type SearchProviderId = "duckduckgo" | "exa" | "brave" | "jina" | "kimi" | "zai" | "anthropic" | "perplexity" | "gemini" | "codex" | "tavily" | "parallel" | "kagi" | "synthetic" | "searxng" | "openai-compatible";
|
|
7
|
+
export type SearchProviderId = "duckduckgo" | "exa" | "brave" | "jina" | "kimi" | "zai" | "anthropic" | "perplexity" | "gemini" | "codex" | "xai" | "tavily" | "parallel" | "kagi" | "synthetic" | "searxng" | "openai-compatible";
|
|
8
8
|
export type WebSearchMode = "on" | "off" | "auto";
|
|
9
9
|
export interface ActiveSearchModelContext {
|
|
10
10
|
provider: string;
|
|
@@ -15,7 +15,7 @@ export interface ActiveSearchModelContext {
|
|
|
15
15
|
headers?: Record<string, string>;
|
|
16
16
|
webSearch?: WebSearchMode;
|
|
17
17
|
}
|
|
18
|
-
export declare const CONFIGURABLE_SEARCH_PROVIDER_IDS: readonly ["duckduckgo", "exa", "brave", "jina", "kimi", "zai", "anthropic", "perplexity", "gemini", "codex", "tavily", "parallel", "kagi", "synthetic", "searxng"];
|
|
18
|
+
export declare const CONFIGURABLE_SEARCH_PROVIDER_IDS: readonly ["duckduckgo", "exa", "brave", "jina", "kimi", "zai", "anthropic", "perplexity", "gemini", "codex", "xai", "tavily", "parallel", "kagi", "synthetic", "searxng"];
|
|
19
19
|
export declare function isSearchProviderId(value: string): value is SearchProviderId;
|
|
20
20
|
export declare function isConfigurableSearchProviderId(value: string): value is (typeof CONFIGURABLE_SEARCH_PROVIDER_IDS)[number];
|
|
21
21
|
export declare function isSearchProviderPreference(value: string): value is (typeof CONFIGURABLE_SEARCH_PROVIDER_IDS)[number] | "auto";
|
|
@@ -40,8 +40,16 @@ export interface SearchCitation {
|
|
|
40
40
|
export interface SearchUsage {
|
|
41
41
|
inputTokens?: number;
|
|
42
42
|
outputTokens?: number;
|
|
43
|
-
/**
|
|
43
|
+
/** Number of web search requests made */
|
|
44
44
|
searchRequests?: number;
|
|
45
|
+
/** Number of xAI X Search requests made */
|
|
46
|
+
xSearchRequests?: number;
|
|
47
|
+
/** Number of image search requests made */
|
|
48
|
+
imageSearchRequests?: number;
|
|
49
|
+
/** Number of image-understanding/view-image requests made */
|
|
50
|
+
imageUnderstandingRequests?: number;
|
|
51
|
+
/** Number of video-understanding requests made */
|
|
52
|
+
videoUnderstandingRequests?: number;
|
|
45
53
|
/** Perplexity: combined token count */
|
|
46
54
|
totalTokens?: number;
|
|
47
55
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@gajae-code/coding-agent",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.6.1",
|
|
5
5
|
"description": "Gajae Code CLI with read, bash, edit, write tools and session management",
|
|
6
6
|
"homepage": "https://gaebal-gajae.dev",
|
|
7
7
|
"author": "Yeachan-Heo",
|
|
@@ -51,12 +51,12 @@
|
|
|
51
51
|
"@agentclientprotocol/sdk": "0.21.0",
|
|
52
52
|
"@babel/parser": "^7.29.3",
|
|
53
53
|
"@mozilla/readability": "^0.6.0",
|
|
54
|
-
"@gajae-code/stats": "0.
|
|
55
|
-
"@gajae-code/agent-core": "0.
|
|
56
|
-
"@gajae-code/ai": "0.
|
|
57
|
-
"@gajae-code/natives": "0.
|
|
58
|
-
"@gajae-code/tui": "0.
|
|
59
|
-
"@gajae-code/utils": "0.
|
|
54
|
+
"@gajae-code/stats": "0.6.1",
|
|
55
|
+
"@gajae-code/agent-core": "0.6.1",
|
|
56
|
+
"@gajae-code/ai": "0.6.1",
|
|
57
|
+
"@gajae-code/natives": "0.6.1",
|
|
58
|
+
"@gajae-code/tui": "0.6.1",
|
|
59
|
+
"@gajae-code/utils": "0.6.1",
|
|
60
60
|
"@puppeteer/browsers": "^2.13.0",
|
|
61
61
|
"@types/turndown": "5.0.6",
|
|
62
62
|
"@xterm/headless": "^6.0.0",
|
|
@@ -6,9 +6,15 @@
|
|
|
6
6
|
|
|
7
7
|
import { APP_NAME } from "@gajae-code/utils";
|
|
8
8
|
import chalk from "chalk";
|
|
9
|
+
import { Settings } from "../config/settings";
|
|
9
10
|
import { initTheme, theme } from "../modes/theme/theme";
|
|
10
|
-
import {
|
|
11
|
-
|
|
11
|
+
import {
|
|
12
|
+
isConfigurableSearchProviderId,
|
|
13
|
+
isSearchProviderPreference,
|
|
14
|
+
runSearchQuery,
|
|
15
|
+
type SearchQueryParams,
|
|
16
|
+
} from "../web/search/index";
|
|
17
|
+
import { SEARCH_PROVIDER_ORDER, setPreferredSearchProvider, setSearchFallbackProviders } from "../web/search/provider";
|
|
12
18
|
import { renderSearchResult } from "../web/search/render";
|
|
13
19
|
import type { SearchProviderId } from "../web/search/types";
|
|
14
20
|
|
|
@@ -18,11 +24,45 @@ export interface SearchCommandArgs {
|
|
|
18
24
|
recency?: "day" | "week" | "month" | "year";
|
|
19
25
|
limit?: number;
|
|
20
26
|
expanded: boolean;
|
|
27
|
+
xaiSearchMode?: SearchQueryParams["xai_search_mode"];
|
|
28
|
+
allowedDomains?: string[];
|
|
29
|
+
excludedDomains?: string[];
|
|
30
|
+
allowedXHandles?: string[];
|
|
31
|
+
excludedXHandles?: string[];
|
|
32
|
+
fromDate?: string;
|
|
33
|
+
toDate?: string;
|
|
34
|
+
enableImageUnderstanding?: boolean;
|
|
35
|
+
enableImageSearch?: boolean;
|
|
36
|
+
enableVideoUnderstanding?: boolean;
|
|
37
|
+
noInlineCitations?: boolean;
|
|
21
38
|
}
|
|
22
39
|
|
|
23
40
|
const PROVIDERS: Array<SearchProviderId | "auto"> = ["auto", ...SEARCH_PROVIDER_ORDER];
|
|
24
41
|
|
|
25
42
|
const RECENCY_OPTIONS: SearchCommandArgs["recency"][] = ["day", "week", "month", "year"];
|
|
43
|
+
const XAI_SEARCH_MODES: Array<NonNullable<SearchCommandArgs["xaiSearchMode"]>> = ["web", "x", "web_and_x"];
|
|
44
|
+
|
|
45
|
+
function appendCsv(existing: string[] | undefined, raw: string | undefined): string[] | undefined {
|
|
46
|
+
const values = raw
|
|
47
|
+
?.split(",")
|
|
48
|
+
.map(value => value.trim())
|
|
49
|
+
.filter(Boolean);
|
|
50
|
+
if (!values?.length) return existing;
|
|
51
|
+
return [...(existing ?? []), ...values];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function splitFlag(raw: string): { flag: string; inlineValue?: string } {
|
|
55
|
+
const equals = raw.indexOf("=");
|
|
56
|
+
if (raw.startsWith("-") && equals > 0) return { flag: raw.slice(0, equals), inlineValue: raw.slice(equals + 1) };
|
|
57
|
+
return { flag: raw };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function readValue(args: string[], index: number, flag: string, inlineValue: string | undefined): string {
|
|
61
|
+
if (inlineValue !== undefined) return inlineValue;
|
|
62
|
+
const value = args[index + 1];
|
|
63
|
+
if (!value) throw new Error(`${flag} requires a value`);
|
|
64
|
+
return value;
|
|
65
|
+
}
|
|
26
66
|
|
|
27
67
|
/**
|
|
28
68
|
* Parse web search subcommand arguments.
|
|
@@ -41,17 +81,53 @@ export function parseSearchArgs(args: string[]): SearchCommandArgs | undefined {
|
|
|
41
81
|
const positional: string[] = [];
|
|
42
82
|
|
|
43
83
|
for (let i = 1; i < args.length; i++) {
|
|
44
|
-
const arg = args[i];
|
|
84
|
+
const { flag: arg, inlineValue } = splitFlag(args[i]);
|
|
85
|
+
const value = () => readValue(args, i, arg, inlineValue);
|
|
86
|
+
const consumeSeparateValue = () => {
|
|
87
|
+
if (inlineValue === undefined) i++;
|
|
88
|
+
};
|
|
45
89
|
if (arg === "--provider") {
|
|
46
|
-
result.provider =
|
|
90
|
+
result.provider = value() as SearchCommandArgs["provider"];
|
|
91
|
+
consumeSeparateValue();
|
|
47
92
|
} else if (arg === "--recency") {
|
|
48
|
-
result.recency =
|
|
93
|
+
result.recency = value() as SearchCommandArgs["recency"];
|
|
94
|
+
consumeSeparateValue();
|
|
49
95
|
} else if (arg === "--limit" || arg === "-l") {
|
|
50
|
-
result.limit = Number.parseInt(
|
|
96
|
+
result.limit = Number.parseInt(value(), 10);
|
|
97
|
+
consumeSeparateValue();
|
|
98
|
+
} else if (arg === "--xai-mode") {
|
|
99
|
+
result.xaiSearchMode = value() as SearchCommandArgs["xaiSearchMode"];
|
|
100
|
+
consumeSeparateValue();
|
|
101
|
+
} else if (arg === "--allowed-domain" || arg === "--allowed-domains") {
|
|
102
|
+
result.allowedDomains = appendCsv(result.allowedDomains, value());
|
|
103
|
+
consumeSeparateValue();
|
|
104
|
+
} else if (arg === "--excluded-domain" || arg === "--excluded-domains") {
|
|
105
|
+
result.excludedDomains = appendCsv(result.excludedDomains, value());
|
|
106
|
+
consumeSeparateValue();
|
|
107
|
+
} else if (arg === "--allowed-x-handle" || arg === "--allowed-x-handles") {
|
|
108
|
+
result.allowedXHandles = appendCsv(result.allowedXHandles, value());
|
|
109
|
+
consumeSeparateValue();
|
|
110
|
+
} else if (arg === "--excluded-x-handle" || arg === "--excluded-x-handles") {
|
|
111
|
+
result.excludedXHandles = appendCsv(result.excludedXHandles, value());
|
|
112
|
+
consumeSeparateValue();
|
|
113
|
+
} else if (arg === "--from-date") {
|
|
114
|
+
result.fromDate = value();
|
|
115
|
+
consumeSeparateValue();
|
|
116
|
+
} else if (arg === "--to-date") {
|
|
117
|
+
result.toDate = value();
|
|
118
|
+
consumeSeparateValue();
|
|
119
|
+
} else if (arg === "--image-understanding") {
|
|
120
|
+
result.enableImageUnderstanding = true;
|
|
121
|
+
} else if (arg === "--image-search") {
|
|
122
|
+
result.enableImageSearch = true;
|
|
123
|
+
} else if (arg === "--video-understanding") {
|
|
124
|
+
result.enableVideoUnderstanding = true;
|
|
125
|
+
} else if (arg === "--no-inline-citations") {
|
|
126
|
+
result.noInlineCitations = true;
|
|
51
127
|
} else if (arg === "--compact") {
|
|
52
128
|
result.expanded = false;
|
|
53
129
|
} else if (!arg.startsWith("-")) {
|
|
54
|
-
positional.push(
|
|
130
|
+
positional.push(args[i]);
|
|
55
131
|
}
|
|
56
132
|
}
|
|
57
133
|
|
|
@@ -80,18 +156,46 @@ export async function runSearchCommand(cmd: SearchCommandArgs): Promise<void> {
|
|
|
80
156
|
process.exit(1);
|
|
81
157
|
}
|
|
82
158
|
|
|
159
|
+
if (cmd.xaiSearchMode && !XAI_SEARCH_MODES.includes(cmd.xaiSearchMode)) {
|
|
160
|
+
process.stderr.write(`${chalk.red(`Error: Invalid xAI mode "${cmd.xaiSearchMode}"`)}\n`);
|
|
161
|
+
process.stderr.write(`${chalk.dim(`Valid xAI modes: ${XAI_SEARCH_MODES.join(", ")}`)}\n`);
|
|
162
|
+
process.exit(1);
|
|
163
|
+
}
|
|
164
|
+
|
|
83
165
|
if (cmd.limit !== undefined && Number.isNaN(cmd.limit)) {
|
|
84
166
|
process.stderr.write(`${chalk.red("Error: --limit must be a number")}\n`);
|
|
85
167
|
process.exit(1);
|
|
86
168
|
}
|
|
87
169
|
|
|
88
170
|
await initTheme();
|
|
171
|
+
const settings = await Settings.init();
|
|
172
|
+
const configuredProvider = settings.get("providers.webSearch");
|
|
173
|
+
if (typeof configuredProvider === "string" && isSearchProviderPreference(configuredProvider)) {
|
|
174
|
+
setPreferredSearchProvider(configuredProvider);
|
|
175
|
+
}
|
|
176
|
+
const configuredFallback = settings.get("web_search.fallback");
|
|
177
|
+
if (Array.isArray(configuredFallback)) {
|
|
178
|
+
setSearchFallbackProviders(
|
|
179
|
+
configuredFallback.filter(value => typeof value === "string" && isConfigurableSearchProviderId(value)),
|
|
180
|
+
);
|
|
181
|
+
}
|
|
89
182
|
|
|
90
183
|
const params: SearchQueryParams = {
|
|
91
184
|
query: cmd.query,
|
|
92
185
|
provider: cmd.provider,
|
|
93
186
|
recency: cmd.recency,
|
|
94
187
|
limit: cmd.limit,
|
|
188
|
+
xai_search_mode: cmd.xaiSearchMode,
|
|
189
|
+
allowed_domains: cmd.allowedDomains,
|
|
190
|
+
excluded_domains: cmd.excludedDomains,
|
|
191
|
+
allowed_x_handles: cmd.allowedXHandles,
|
|
192
|
+
excluded_x_handles: cmd.excludedXHandles,
|
|
193
|
+
from_date: cmd.fromDate,
|
|
194
|
+
to_date: cmd.toDate,
|
|
195
|
+
enable_image_understanding: cmd.enableImageUnderstanding,
|
|
196
|
+
enable_image_search: cmd.enableImageSearch,
|
|
197
|
+
enable_video_understanding: cmd.enableVideoUnderstanding,
|
|
198
|
+
no_inline_citations: cmd.noInlineCitations,
|
|
95
199
|
};
|
|
96
200
|
|
|
97
201
|
const result = await runSearchQuery(params);
|
|
@@ -121,7 +225,18 @@ ${chalk.bold("Arguments:")}
|
|
|
121
225
|
|
|
122
226
|
${chalk.bold("Options:")}
|
|
123
227
|
--provider <name> Provider: ${PROVIDERS.join(", ")}
|
|
124
|
-
--recency <value> Recency filter
|
|
228
|
+
--recency <value> Recency filter: ${RECENCY_OPTIONS.join(", ")}
|
|
229
|
+
--xai-mode <mode> xAI mode: web, x, web_and_x
|
|
230
|
+
--allowed-domain(s) d xAI web_search domain allow-list (comma-separated, repeatable)
|
|
231
|
+
--excluded-domain(s) d xAI web_search domain deny-list (comma-separated, repeatable)
|
|
232
|
+
--allowed-x-handle(s) h xAI x_search handle allow-list (comma-separated, repeatable)
|
|
233
|
+
--excluded-x-handle(s) h xAI x_search handle deny-list (comma-separated, repeatable)
|
|
234
|
+
--from-date <date> xAI x_search start date (ISO8601)
|
|
235
|
+
--to-date <date> xAI x_search end date (ISO8601)
|
|
236
|
+
--image-understanding Enable xAI image understanding
|
|
237
|
+
--image-search Enable xAI web image search
|
|
238
|
+
--video-understanding Enable xAI X video understanding
|
|
239
|
+
--no-inline-citations Disable xAI inline citation markdown
|
|
125
240
|
-l, --limit <n> Max results to return
|
|
126
241
|
--compact Render condensed output
|
|
127
242
|
-h, --help Show this help
|
package/src/cli.ts
CHANGED
|
@@ -35,6 +35,7 @@ const commands: CommandEntry[] = [
|
|
|
35
35
|
{ name: "gc", load: () => import("./commands/gc").then(m => m.default) },
|
|
36
36
|
{ name: "ralplan", load: () => import("./commands/ralplan").then(m => m.default) },
|
|
37
37
|
{ name: "config", load: () => import("./commands/config").then(m => m.default) },
|
|
38
|
+
{ name: "web-search", aliases: ["q"], load: () => import("./commands/web-search").then(m => m.default) },
|
|
38
39
|
{ name: "mcp-serve", load: () => import("./commands/mcp-serve").then(m => m.default) },
|
|
39
40
|
{
|
|
40
41
|
name: "contribute-pr",
|
|
@@ -42,6 +43,7 @@ const commands: CommandEntry[] = [
|
|
|
42
43
|
load: () => import("./commands/contribution-prep").then(m => m.default),
|
|
43
44
|
},
|
|
44
45
|
{ name: "deep-interview", load: () => import("./commands/deep-interview").then(m => m.default) },
|
|
46
|
+
{ name: "rlm", load: () => import("./commands/rlm").then(m => m.default) },
|
|
45
47
|
{ name: "update", load: () => import("./commands/update").then(m => m.default) },
|
|
46
48
|
{ name: "launch", load: () => import("./commands/launch").then(m => m.default) },
|
|
47
49
|
];
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `gjc rlm` — opt-in Jupyter-style research session with a persistent Python kernel.
|
|
3
|
+
*/
|
|
4
|
+
import { Command } from "@gajae-code/utils/cli";
|
|
5
|
+
import { runRlmCommand } from "../rlm";
|
|
6
|
+
|
|
7
|
+
export default class Rlm extends Command {
|
|
8
|
+
static description = "Opt-in research session: persistent Python kernel, live notebook, synthesized report";
|
|
9
|
+
static strict = false;
|
|
10
|
+
static examples = [
|
|
11
|
+
"# Start an interactive research session\n gjc rlm",
|
|
12
|
+
'# Seed the session with an initial question\n gjc rlm "What drives the spike in the orders table?"',
|
|
13
|
+
"# Point the session at a data description\n gjc rlm --data ./datasets/DATA.md",
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
async run(): Promise<void> {
|
|
17
|
+
await runRlmCommand(this.argv);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -9,6 +9,22 @@ const PROVIDERS: Array<string> = ["auto", ...SEARCH_PROVIDER_ORDER];
|
|
|
9
9
|
|
|
10
10
|
const RECENCY: NonNullable<SearchCommandArgs["recency"]>[] = ["day", "week", "month", "year"];
|
|
11
11
|
|
|
12
|
+
type ListFlagValue = string | string[] | undefined;
|
|
13
|
+
|
|
14
|
+
function appendCsv(existing: string[] | undefined, raw: ListFlagValue): string[] | undefined {
|
|
15
|
+
const rawValues = Array.isArray(raw) ? raw : raw === undefined ? [] : [raw];
|
|
16
|
+
const values = rawValues
|
|
17
|
+
.flatMap(value => value.split(","))
|
|
18
|
+
.map(value => value.trim())
|
|
19
|
+
.filter(Boolean);
|
|
20
|
+
if (values.length === 0) return existing;
|
|
21
|
+
return [...(existing ?? []), ...values];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function combineCsv(...values: ListFlagValue[]): string[] | undefined {
|
|
25
|
+
return values.reduce<string[] | undefined>((acc, value) => appendCsv(acc, value), undefined);
|
|
26
|
+
}
|
|
27
|
+
|
|
12
28
|
export default class Search extends Command {
|
|
13
29
|
static description = "Test web search providers";
|
|
14
30
|
|
|
@@ -22,6 +38,45 @@ export default class Search extends Command {
|
|
|
22
38
|
provider: Flags.string({ description: "Search provider", options: PROVIDERS }),
|
|
23
39
|
recency: Flags.string({ description: "Recency filter", options: RECENCY }),
|
|
24
40
|
limit: Flags.integer({ char: "l", description: "Max results to return" }),
|
|
41
|
+
"xai-mode": Flags.string({ description: "xAI mode", options: ["web", "x", "web_and_x"] }),
|
|
42
|
+
"allowed-domain": Flags.string({
|
|
43
|
+
description: "xAI web_search allowed domains, comma-separated",
|
|
44
|
+
multiple: true,
|
|
45
|
+
}),
|
|
46
|
+
"allowed-domains": Flags.string({
|
|
47
|
+
description: "xAI web_search allowed domains, comma-separated",
|
|
48
|
+
multiple: true,
|
|
49
|
+
}),
|
|
50
|
+
"excluded-domain": Flags.string({
|
|
51
|
+
description: "xAI web_search excluded domains, comma-separated",
|
|
52
|
+
multiple: true,
|
|
53
|
+
}),
|
|
54
|
+
"excluded-domains": Flags.string({
|
|
55
|
+
description: "xAI web_search excluded domains, comma-separated",
|
|
56
|
+
multiple: true,
|
|
57
|
+
}),
|
|
58
|
+
"allowed-x-handle": Flags.string({
|
|
59
|
+
description: "xAI x_search allowed handles, comma-separated",
|
|
60
|
+
multiple: true,
|
|
61
|
+
}),
|
|
62
|
+
"allowed-x-handles": Flags.string({
|
|
63
|
+
description: "xAI x_search allowed handles, comma-separated",
|
|
64
|
+
multiple: true,
|
|
65
|
+
}),
|
|
66
|
+
"excluded-x-handle": Flags.string({
|
|
67
|
+
description: "xAI x_search excluded handles, comma-separated",
|
|
68
|
+
multiple: true,
|
|
69
|
+
}),
|
|
70
|
+
"excluded-x-handles": Flags.string({
|
|
71
|
+
description: "xAI x_search excluded handles, comma-separated",
|
|
72
|
+
multiple: true,
|
|
73
|
+
}),
|
|
74
|
+
"from-date": Flags.string({ description: "xAI x_search start date (ISO8601)" }),
|
|
75
|
+
"to-date": Flags.string({ description: "xAI x_search end date (ISO8601)" }),
|
|
76
|
+
"image-understanding": Flags.boolean({ description: "Enable xAI image understanding" }),
|
|
77
|
+
"image-search": Flags.boolean({ description: "Enable xAI web image search" }),
|
|
78
|
+
"video-understanding": Flags.boolean({ description: "Enable xAI X video understanding" }),
|
|
79
|
+
"no-inline-citations": Flags.boolean({ description: "Disable xAI inline citation markdown" }),
|
|
25
80
|
compact: Flags.boolean({ description: "Render condensed output" }),
|
|
26
81
|
};
|
|
27
82
|
|
|
@@ -35,6 +90,17 @@ export default class Search extends Command {
|
|
|
35
90
|
recency: flags.recency as SearchCommandArgs["recency"],
|
|
36
91
|
limit: flags.limit,
|
|
37
92
|
expanded: !flags.compact,
|
|
93
|
+
xaiSearchMode: flags["xai-mode"] as SearchCommandArgs["xaiSearchMode"],
|
|
94
|
+
allowedDomains: combineCsv(flags["allowed-domain"], flags["allowed-domains"]),
|
|
95
|
+
excludedDomains: combineCsv(flags["excluded-domain"], flags["excluded-domains"]),
|
|
96
|
+
allowedXHandles: combineCsv(flags["allowed-x-handle"], flags["allowed-x-handles"]),
|
|
97
|
+
excludedXHandles: combineCsv(flags["excluded-x-handle"], flags["excluded-x-handles"]),
|
|
98
|
+
fromDate: flags["from-date"],
|
|
99
|
+
toDate: flags["to-date"],
|
|
100
|
+
enableImageUnderstanding: flags["image-understanding"],
|
|
101
|
+
enableImageSearch: flags["image-search"],
|
|
102
|
+
enableVideoUnderstanding: flags["video-understanding"],
|
|
103
|
+
noInlineCitations: flags["no-inline-citations"],
|
|
38
104
|
};
|
|
39
105
|
|
|
40
106
|
await runSearchCommand(cmd);
|
|
@@ -27,8 +27,10 @@ interface AppKeybindings {
|
|
|
27
27
|
"app.model.select": true;
|
|
28
28
|
"app.model.selectTemporary": true;
|
|
29
29
|
"app.tools.expand": true;
|
|
30
|
+
"app.tool.backgroundFold": true;
|
|
30
31
|
"app.editor.external": true;
|
|
31
32
|
"app.message.followUp": true;
|
|
33
|
+
"app.message.queue": true;
|
|
32
34
|
"app.message.dequeue": true;
|
|
33
35
|
"app.clipboard.pasteImage": true;
|
|
34
36
|
"app.clipboard.copyLine": true;
|
|
@@ -106,6 +108,10 @@ export const KEYBINDINGS = {
|
|
|
106
108
|
defaultKeys: "ctrl+o",
|
|
107
109
|
description: "Expand tools",
|
|
108
110
|
},
|
|
111
|
+
"app.tool.backgroundFold": {
|
|
112
|
+
defaultKeys: "ctrl+b",
|
|
113
|
+
description: "Fold/background supported foreground tool",
|
|
114
|
+
},
|
|
109
115
|
"app.editor.external": {
|
|
110
116
|
defaultKeys: "ctrl+g",
|
|
111
117
|
description: "Open external editor",
|
|
@@ -114,6 +120,10 @@ export const KEYBINDINGS = {
|
|
|
114
120
|
defaultKeys: "ctrl+enter",
|
|
115
121
|
description: "Send follow-up message",
|
|
116
122
|
},
|
|
123
|
+
"app.message.queue": {
|
|
124
|
+
defaultKeys: "alt+enter",
|
|
125
|
+
description: "Queue message for next turn",
|
|
126
|
+
},
|
|
117
127
|
"app.message.dequeue": {
|
|
118
128
|
defaultKeys: "alt+up",
|
|
119
129
|
description: "Dequeue message",
|
|
@@ -217,6 +227,7 @@ const KEYBINDING_NAME_MIGRATIONS = {
|
|
|
217
227
|
toggleThinking: "app.thinking.toggle",
|
|
218
228
|
externalEditor: "app.editor.external",
|
|
219
229
|
followUp: "app.message.followUp",
|
|
230
|
+
queue: "app.message.queue",
|
|
220
231
|
dequeue: "app.message.dequeue",
|
|
221
232
|
pasteImage: "app.clipboard.pasteImage",
|
|
222
233
|
copyLine: "app.clipboard.copyLine",
|
|
@@ -6,6 +6,7 @@ export type ModelProfileRole = GjcModelAssignmentTargetId;
|
|
|
6
6
|
export interface ModelProfileDefinition {
|
|
7
7
|
name: string;
|
|
8
8
|
requiredProviders: string[];
|
|
9
|
+
displayName?: string;
|
|
9
10
|
/**
|
|
10
11
|
* Optional groups of providers that are interchangeable fallbacks.
|
|
11
12
|
* Each group is an array of provider ids where at least one must be
|
|
@@ -323,8 +324,14 @@ const PROFILE_RECOMMENDATIONS: Record<string, string> = {
|
|
|
323
324
|
"minimax-code": "minimax-medium",
|
|
324
325
|
};
|
|
325
326
|
|
|
326
|
-
export function getModelProfilePresentation(
|
|
327
|
-
|
|
327
|
+
export function getModelProfilePresentation(
|
|
328
|
+
profile: string | Pick<ModelProfileDefinition, "name" | "displayName">,
|
|
329
|
+
): ModelProfilePresentation {
|
|
330
|
+
const name = typeof profile === "string" ? profile : profile.name;
|
|
331
|
+
const displayName = typeof profile === "string" ? undefined : profile.displayName;
|
|
332
|
+
const presentation = PROFILE_PRESENTATION[name];
|
|
333
|
+
if (presentation) return presentation;
|
|
334
|
+
return { displayName: displayName ?? name, providerGroup: "CUSTOM" };
|
|
328
335
|
}
|
|
329
336
|
|
|
330
337
|
export function groupModelProfilesForPresetLanding(
|
|
@@ -333,7 +340,7 @@ export function groupModelProfilesForPresetLanding(
|
|
|
333
340
|
const groups = new Map<string, ModelProfileDefinition[]>();
|
|
334
341
|
for (const group of PROFILE_GROUP_ORDER) groups.set(group, []);
|
|
335
342
|
for (const profile of profiles.values()) {
|
|
336
|
-
const group = getModelProfilePresentation(profile
|
|
343
|
+
const group = getModelProfilePresentation(profile).providerGroup;
|
|
337
344
|
if (!groups.has(group)) groups.set(group, []);
|
|
338
345
|
groups.get(group)?.push(profile);
|
|
339
346
|
}
|
|
@@ -365,6 +372,7 @@ export function mergeModelProfiles(userProfiles?: ModelsConfig["profiles"]): Map
|
|
|
365
372
|
const modelMapping = { ...definition.model_mapping };
|
|
366
373
|
profiles.set(name, {
|
|
367
374
|
name,
|
|
375
|
+
displayName: definition.display_name,
|
|
368
376
|
requiredProviders: aggregateModelProfileRequiredProviders(definition.required_providers, { modelMapping }),
|
|
369
377
|
modelMapping,
|
|
370
378
|
source: "user",
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as fs from "node:fs/promises";
|
|
1
2
|
import * as path from "node:path";
|
|
2
3
|
import {
|
|
3
4
|
type Api,
|
|
@@ -45,11 +46,17 @@ import {
|
|
|
45
46
|
formatCanonicalVariantSelector,
|
|
46
47
|
type ModelEquivalenceConfig,
|
|
47
48
|
} from "./model-equivalence";
|
|
48
|
-
import {
|
|
49
|
+
import {
|
|
50
|
+
aggregateModelProfileRequiredProviders,
|
|
51
|
+
type ModelProfileDefinition,
|
|
52
|
+
mergeModelProfiles,
|
|
53
|
+
} from "./model-profiles";
|
|
49
54
|
import {
|
|
50
55
|
type ModelOverride,
|
|
56
|
+
type ModelProfileConfig,
|
|
51
57
|
type ModelsConfig,
|
|
52
58
|
ModelsConfigSchema,
|
|
59
|
+
ProfileDefinitionSchema,
|
|
53
60
|
type ProviderAuthMode,
|
|
54
61
|
type ProviderDiscovery,
|
|
55
62
|
} from "./models-config-schema";
|
|
@@ -1489,6 +1496,53 @@ export class ModelRegistry {
|
|
|
1489
1496
|
getAvailableModelProfileNames(): string[] {
|
|
1490
1497
|
return [...this.#modelProfiles.keys()].sort((a, b) => a.localeCompare(b));
|
|
1491
1498
|
}
|
|
1499
|
+
|
|
1500
|
+
async saveCustomModelProfile(name: string, definition: ModelProfileConfig): Promise<ModelProfileDefinition> {
|
|
1501
|
+
const normalizedName = name.trim();
|
|
1502
|
+
if (!normalizedName) throw new Error("Profile name is required.");
|
|
1503
|
+
const checkedDefinition = ProfileDefinitionSchema.safeParse(definition);
|
|
1504
|
+
if (!checkedDefinition.success) {
|
|
1505
|
+
const first = checkedDefinition.error.issues[0];
|
|
1506
|
+
const where = first?.path.length ? `/${first.path.map(String).join("/")}` : "root";
|
|
1507
|
+
throw new Error(`Custom model profile is invalid at ${where}: ${first?.message ?? "unknown schema error"}`);
|
|
1508
|
+
}
|
|
1509
|
+
const loaded = this.#modelsConfigFile.tryLoad();
|
|
1510
|
+
if (loaded.status === "error") {
|
|
1511
|
+
throw new Error(
|
|
1512
|
+
`Cannot create custom model profile because ${this.#modelsConfigFile.path()} is invalid. Fix the existing config before saving a new preset.`,
|
|
1513
|
+
);
|
|
1514
|
+
}
|
|
1515
|
+
const current = loaded.value ?? this.#modelsConfigFile.createDefault();
|
|
1516
|
+
if (mergeModelProfiles(current.profiles).has(normalizedName)) {
|
|
1517
|
+
throw new Error(`Custom model profile already exists: ${normalizedName}. Choose a unique preset id.`);
|
|
1518
|
+
}
|
|
1519
|
+
const next: ModelsConfig = {
|
|
1520
|
+
...current,
|
|
1521
|
+
profiles: {
|
|
1522
|
+
...(current.profiles ?? {}),
|
|
1523
|
+
[normalizedName]: definition,
|
|
1524
|
+
},
|
|
1525
|
+
};
|
|
1526
|
+
const checkedConfig = ModelsConfigSchema.safeParse(next);
|
|
1527
|
+
if (!checkedConfig.success) {
|
|
1528
|
+
const first = checkedConfig.error.issues[0];
|
|
1529
|
+
const where = first?.path.length ? `/${first.path.map(String).join("/")}` : "root";
|
|
1530
|
+
throw new Error(`Generated models config is invalid at ${where}: ${first?.message ?? "unknown schema error"}`);
|
|
1531
|
+
}
|
|
1532
|
+
await fs.mkdir(path.dirname(this.#modelsConfigFile.path()), { recursive: true });
|
|
1533
|
+
await Bun.write(this.#modelsConfigFile.path(), Bun.YAML.stringify(checkedConfig.data, null, 2));
|
|
1534
|
+
this.#modelsConfigFile.invalidate();
|
|
1535
|
+
this.#reloadStaticModels();
|
|
1536
|
+
const modelMapping = { ...definition.model_mapping };
|
|
1537
|
+
const profile: ModelProfileDefinition = {
|
|
1538
|
+
name: normalizedName,
|
|
1539
|
+
displayName: definition.display_name,
|
|
1540
|
+
requiredProviders: aggregateModelProfileRequiredProviders(definition.required_providers, { modelMapping }),
|
|
1541
|
+
modelMapping,
|
|
1542
|
+
source: "user",
|
|
1543
|
+
};
|
|
1544
|
+
return profile;
|
|
1545
|
+
}
|
|
1492
1546
|
applyConfiguredModelBindings(targetSettings: Settings): void {
|
|
1493
1547
|
this.#modelBindingsTargetSettings = targetSettings;
|
|
1494
1548
|
this.#applyConfiguredModelBindingsToTarget();
|
|
@@ -108,6 +108,7 @@ export const ProfileModelMappingSchema = z.partialRecord(ProfileRoleSchema, Prof
|
|
|
108
108
|
export const ProfileDefinitionSchema = z
|
|
109
109
|
.object({
|
|
110
110
|
required_providers: z.array(z.string().min(1)).min(1),
|
|
111
|
+
display_name: z.string().min(1).optional(),
|
|
111
112
|
model_mapping: ProfileModelMappingSchema,
|
|
112
113
|
})
|
|
113
114
|
.strict();
|