@gajae-code/coding-agent 0.15.5 → 0.16.0
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 +85 -1
- package/dist/types/async/job-manager.d.ts +83 -0
- package/dist/types/cli/args.d.ts +2 -0
- package/dist/types/cli/auth-gateway-cli.d.ts +17 -0
- package/dist/types/cli/model-presets-cli.d.ts +9 -0
- package/dist/types/cli/root-flags.d.ts +14 -0
- package/dist/types/cli/update-cli.d.ts +15 -0
- package/dist/types/cli.d.ts +7 -0
- package/dist/types/commands/auth-gateway.d.ts +3 -0
- package/dist/types/commands/gc.d.ts +13 -0
- package/dist/types/commands/launch.d.ts +10 -0
- package/dist/types/commands/model-presets.d.ts +21 -0
- package/dist/types/config/autorouting-tier-map.d.ts +120 -0
- package/dist/types/config/file-lock.d.ts +54 -2
- package/dist/types/config/internal/model-preset-registry-test-state.d.ts +4 -0
- package/dist/types/config/internal/model-preset-registry-test-support.d.ts +3 -0
- package/dist/types/config/keybindings.d.ts +3 -2
- package/dist/types/config/model-preset-registry.d.ts +368 -0
- package/dist/types/config/model-profile-activation.d.ts +39 -0
- package/dist/types/config/model-profiles.d.ts +5 -3
- package/dist/types/config/model-registry.d.ts +17 -5
- package/dist/types/config/settings-schema.d.ts +19 -1
- package/dist/types/config/settings.d.ts +2 -0
- package/dist/types/coordinator-mcp/model-preset.d.ts +7 -5
- package/dist/types/coordinator-mcp/projection-scan.d.ts +54 -0
- package/dist/types/crash/record-loader.d.ts +15 -0
- package/dist/types/extensibility/extensions/types.d.ts +2 -5
- package/dist/types/gjc-runtime/empty-delete-gc.d.ts +58 -0
- package/dist/types/gjc-runtime/gc-runtime.d.ts +2 -0
- package/dist/types/gjc-runtime/launch-worktree-reservation.d.ts +21 -0
- package/dist/types/gjc-runtime/managed-owner-supervisor.d.ts +2 -0
- package/dist/types/gjc-runtime/session-state-lock.d.ts +35 -6
- package/dist/types/gjc-runtime/session-state-sidecar.d.ts +72 -1
- package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +1 -0
- package/dist/types/gjc-runtime/tmux-sessions.d.ts +27 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/main.d.ts +3 -1
- package/dist/types/master-mode/context.d.ts +21 -0
- package/dist/types/master-mode/first-request.d.ts +34 -0
- package/dist/types/master-mode/snapshot.d.ts +32 -0
- package/dist/types/modes/components/frictionless-onboarding-selector.d.ts +10 -0
- package/dist/types/modes/components/jobs-overlay-model.d.ts +4 -2
- package/dist/types/modes/components/oauth-selector.d.ts +1 -0
- package/dist/types/modes/components/status-line/model-name.d.ts +18 -0
- package/dist/types/modes/components/status-line/priority-row.d.ts +34 -0
- package/dist/types/modes/components/status-line/segments.d.ts +10 -0
- package/dist/types/modes/jobs-observer.d.ts +36 -0
- package/dist/types/modes/print-mode.d.ts +17 -0
- package/dist/types/modes/ui-language.d.ts +7 -1
- package/dist/types/sdk/broker/broker.d.ts +80 -1
- package/dist/types/sdk/broker/endpoint-authority.d.ts +24 -0
- package/dist/types/sdk/broker/index.d.ts +3 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +26 -0
- package/dist/types/sdk/broker/master-capability.d.ts +10 -0
- package/dist/types/sdk/broker/session-index.d.ts +40 -11
- package/dist/types/sdk/broker/session-scope.d.ts +72 -0
- package/dist/types/sdk/broker/spawn-authority.d.ts +201 -0
- package/dist/types/sdk/broker/spawn-substrate.d.ts +30 -0
- package/dist/types/sdk/broker/state-version.d.ts +4 -1
- package/dist/types/sdk/broker/worktree-occupancy.d.ts +28 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +3 -0
- package/dist/types/sdk/bus/index.d.ts +6 -0
- package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +3 -0
- package/dist/types/sdk/bus/prompt-reconciliation.d.ts +4 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +1 -1
- package/dist/types/sdk/cli/index.d.ts +1 -0
- package/dist/types/sdk/cli/master-cli.d.ts +63 -0
- package/dist/types/sdk/cli/rows.d.ts +4 -3
- package/dist/types/sdk/cli/session-cli.d.ts +18 -2
- package/dist/types/sdk/client/client.d.ts +3 -2
- package/dist/types/sdk/client/discovery.d.ts +1 -0
- package/dist/types/sdk/host/control/dispatch.d.ts +10 -0
- package/dist/types/sdk/host/control/index.d.ts +1 -1
- package/dist/types/sdk/host/control/operations.d.ts +2 -0
- package/dist/types/sdk/host/control/runtime-gate.d.ts +21 -7
- package/dist/types/sdk/host/host.d.ts +10 -0
- package/dist/types/sdk/host/model-pin.d.ts +7 -2
- package/dist/types/sdk/host/sdk-run-capability.d.ts +1 -0
- package/dist/types/sdk/host/session-runtime.d.ts +67 -1
- package/dist/types/sdk/lifecycle/broker-client.d.ts +6 -0
- package/dist/types/sdk/lifecycle/service.d.ts +15 -3
- package/dist/types/sdk/lifecycle-session.d.ts +2 -0
- package/dist/types/sdk/prompt-deadline-manager.d.ts +11 -1
- package/dist/types/sdk/protocol/adapter-validation.d.ts +2 -0
- package/dist/types/sdk/router/session-router.d.ts +11 -0
- package/dist/types/sdk/session.d.ts +16 -0
- package/dist/types/sdk/startup-capability.d.ts +2 -1
- package/dist/types/session/account-inventory.d.ts +1 -0
- package/dist/types/session/agent-session.d.ts +24 -7
- package/dist/types/session/credential-store-errors.d.ts +1 -0
- package/dist/types/session/fold-coordinator.d.ts +201 -0
- package/dist/types/session/sdk-run-capability-internal.d.ts +7 -0
- package/dist/types/session/sdk-run-capability.d.ts +2 -0
- package/dist/types/session/session-manager.d.ts +65 -0
- package/dist/types/session/yield-queue.d.ts +5 -4
- package/dist/types/setup/frictionless-onboarding.d.ts +1 -0
- package/dist/types/telemetry/control.d.ts +11 -0
- package/dist/types/telemetry/events.d.ts +20 -0
- package/dist/types/telemetry/index.d.ts +3 -0
- package/dist/types/telemetry/transport.d.ts +22 -0
- package/dist/types/tools/bash-interactive.d.ts +23 -0
- package/dist/types/tools/bash.d.ts +4 -0
- package/dist/types/tools/gh.d.ts +8 -0
- package/dist/types/tools/index.d.ts +10 -5
- package/dist/types/web/search/providers/base.d.ts +9 -4
- package/dist/types/web/search/providers/xai.d.ts +2 -1
- package/dist/types/web/search/types.d.ts +14 -0
- package/package.json +10 -7
- package/scripts/generate-hotkeys-docs.ts +4 -0
- package/scripts/generate-sdk-operation-inventory.ts +9 -3
- package/scripts/verify-gjc-sdk-canonicalization.ts +35 -1
- package/src/async/job-manager.ts +671 -39
- package/src/cli/accounts-cli.ts +9 -5
- package/src/cli/args.ts +17 -0
- package/src/cli/auth-broker-cli.ts +40 -12
- package/src/cli/auth-gateway-cli.ts +273 -97
- package/src/cli/config-cli.ts +2 -2
- package/src/cli/fast-help.ts +4 -0
- package/src/cli/model-presets-cli.ts +100 -0
- package/src/cli/root-flags.ts +5 -0
- package/src/cli/update-cli.ts +117 -16
- package/src/cli.ts +6 -2
- package/src/commands/auth-gateway.ts +12 -8
- package/src/commands/gc.ts +19 -0
- package/src/commands/launch.ts +86 -18
- package/src/commands/model-presets.ts +24 -0
- package/src/commands/sdk.ts +113 -21
- package/src/config/autorouting-tier-map.ts +44 -0
- package/src/config/file-lock-gc.ts +9 -4
- package/src/config/file-lock.ts +890 -99
- package/src/config/internal/model-preset-registry-test-state.ts +28 -0
- package/src/config/internal/model-preset-registry-test-support.ts +11 -0
- package/src/config/keybindings.ts +6 -2
- package/src/config/model-preset-registry.ts +2795 -0
- package/src/config/model-profile-activation.ts +115 -28
- package/src/config/model-profile-contract.ts +4 -1
- package/src/config/model-profiles.ts +56 -14
- package/src/config/model-registry.ts +1044 -122
- package/src/config/model-resolver.ts +5 -0
- package/src/config/settings-schema.ts +19 -1
- package/src/config/settings.ts +11 -1
- package/src/coordinator-mcp/model-preset.ts +32 -17
- package/src/coordinator-mcp/projection-scan.ts +592 -0
- package/src/coordinator-mcp/question-state.ts +9 -11
- package/src/coordinator-mcp/server.ts +111 -42
- package/src/coordinator-mcp/session-reaper.ts +9 -3
- package/src/crash/index-store.ts +2 -0
- package/src/crash/record-loader.ts +10 -0
- package/src/extensibility/extensions/loader.ts +0 -1
- package/src/extensibility/extensions/types.ts +2 -3
- package/src/extensibility/gjc-plugins/runtime-adapters.ts +1 -0
- package/src/gjc-runtime/empty-delete-gc.ts +301 -0
- package/src/gjc-runtime/gc-render.ts +29 -0
- package/src/gjc-runtime/gc-runtime.ts +174 -11
- package/src/gjc-runtime/launch-worktree-reservation.ts +242 -0
- package/src/gjc-runtime/managed-owner-supervisor.ts +63 -51
- package/src/gjc-runtime/session-state-lock.ts +1321 -147
- package/src/gjc-runtime/session-state-sidecar.ts +1004 -25
- package/src/gjc-runtime/tmux-owner-isolation-cli.ts +4 -7
- package/src/gjc-runtime/tmux-owner-isolation.ts +2 -2
- package/src/gjc-runtime/tmux-sessions.ts +252 -13
- package/src/hooks/skill-state.ts +6 -0
- package/src/index.ts +1 -0
- package/src/internal-urls/docs-index.generated.ts +14 -13
- package/src/internal-urls/issue-pr-protocol.ts +4 -2
- package/src/lsp/config.ts +4 -3
- package/src/lsp/path-trust.ts +12 -6
- package/src/main.ts +66 -4
- package/src/master-mode/context.ts +50 -0
- package/src/master-mode/first-request.ts +65 -0
- package/src/master-mode/snapshot.ts +205 -0
- package/src/modes/acp/acp-agent.ts +616 -161
- package/src/modes/components/footer.ts +4 -2
- package/src/modes/components/frictionless-onboarding-selector.ts +48 -2
- package/src/modes/components/jobs-overlay-model.ts +99 -16
- package/src/modes/components/model-selector.ts +158 -18
- package/src/modes/components/oauth-selector.ts +6 -1
- package/src/modes/components/settings-selector.ts +1 -0
- package/src/modes/components/status-line/model-name.ts +53 -0
- package/src/modes/components/status-line/priority-row.ts +107 -0
- package/src/modes/components/status-line/segments.ts +29 -10
- package/src/modes/components/tool-status-header.ts +151 -15
- package/src/modes/controllers/extension-ui-controller.ts +1 -1
- package/src/modes/controllers/input-controller.ts +51 -6
- package/src/modes/controllers/selector-controller.ts +191 -96
- package/src/modes/interactive-mode.ts +14 -1
- package/src/modes/jobs-observer.ts +149 -16
- package/src/modes/print-mode.ts +64 -0
- package/src/modes/runtime-init.ts +1 -1
- package/src/modes/ui-language.ts +49 -2
- package/src/modes/utils/injected-user-submission.ts +15 -1
- package/src/prompts/system/master-mode.md +28 -0
- package/src/prompts/tools/bash.md +2 -2
- package/src/prompts/tools/fold-receipt.md +3 -0
- package/src/prompts/tools/job.md +1 -1
- package/src/sdk/acp/adapter.ts +17 -2
- package/src/sdk/broker/broker.ts +1671 -34
- package/src/sdk/broker/endpoint-authority.ts +86 -0
- package/src/sdk/broker/ensure.ts +5 -0
- package/src/sdk/broker/index.ts +3 -0
- package/src/sdk/broker/lifecycle.ts +217 -46
- package/src/sdk/broker/master-capability.ts +232 -0
- package/src/sdk/broker/session-index.ts +121 -17
- package/src/sdk/broker/session-scope.ts +272 -0
- package/src/sdk/broker/spawn-authority.ts +1031 -0
- package/src/sdk/broker/spawn-substrate.ts +548 -0
- package/src/sdk/broker/state-version.ts +26 -14
- package/src/sdk/broker/transport.ts +1 -0
- package/src/sdk/broker/worktree-occupancy.ts +90 -0
- package/src/sdk/bus/chat-daemon-control.ts +5 -2
- package/src/sdk/bus/chat-daemon-runtime.ts +46 -5
- package/src/sdk/bus/index.ts +51 -4
- package/src/sdk/bus/kind-aware-reconciliation.ts +36 -6
- package/src/sdk/bus/prompt-reconciliation.ts +28 -4
- package/src/sdk/bus/slack-conversation.ts +5 -1
- package/src/sdk/bus/telegram-daemon-contract.ts +1 -1
- package/src/sdk/cli/index.ts +1 -0
- package/src/sdk/cli/master-cli.ts +259 -0
- package/src/sdk/cli/rows.ts +11 -8
- package/src/sdk/cli/session-cli.ts +276 -6
- package/src/sdk/client/client.ts +14 -8
- package/src/sdk/client/discovery.ts +3 -1
- package/src/sdk/host/control/dispatch.ts +101 -37
- package/src/sdk/host/control/index.ts +2 -0
- package/src/sdk/host/control/operations.ts +2 -0
- package/src/sdk/host/control/runtime-gate.ts +203 -11
- package/src/sdk/host/host.ts +27 -2
- package/src/sdk/host/model-pin.ts +185 -25
- package/src/sdk/host/sdk-run-capability.ts +1 -0
- package/src/sdk/host/session-runtime.test.ts +581 -43
- package/src/sdk/host/session-runtime.ts +655 -101
- package/src/sdk/lifecycle/broker-client.ts +28 -0
- package/src/sdk/lifecycle/service.ts +279 -20
- package/src/sdk/lifecycle-session.ts +10 -2
- package/src/sdk/prompt-deadline-manager.ts +16 -1
- package/src/sdk/protocol/adapter-validation.ts +27 -0
- package/src/sdk/protocol/operation-inventory.generated.json +53 -2
- package/src/sdk/protocol/operation-registry.ts +17 -4
- package/src/sdk/router/session-router.ts +294 -154
- package/src/sdk/session.ts +440 -167
- package/src/sdk/startup-capability.ts +1 -0
- package/src/session/account-inventory.ts +27 -11
- package/src/session/agent-session.ts +1292 -180
- package/src/session/blob-store.ts +15 -5
- package/src/session/credential-store-errors.ts +2 -0
- package/src/session/fold-coordinator.ts +433 -0
- package/src/session/internal/managed-session-storage.ts +153 -78
- package/src/session/sdk-run-capability-internal.ts +15 -0
- package/src/session/sdk-run-capability.ts +2 -0
- package/src/session/session-manager.ts +342 -28
- package/src/session/yield-queue.ts +24 -10
- package/src/setup/frictionless-onboarding.ts +2 -0
- package/src/setup/provider-presets.json +1 -1
- package/src/slash-commands/helpers/credential-switch.ts +6 -1
- package/src/task/executor.ts +19 -3
- package/src/telemetry/control.ts +26 -0
- package/src/telemetry/events.ts +1268 -0
- package/src/telemetry/index.ts +3 -0
- package/src/telemetry/transport.ts +87 -0
- package/src/tools/bash-interactive.ts +182 -85
- package/src/tools/bash.ts +1028 -109
- package/src/tools/gh.ts +104 -48
- package/src/tools/github-cache.ts +2 -2
- package/src/tools/index.ts +11 -5
- package/src/tools/tool-catalog.generated.ts +1 -1
- package/src/web/search/provider.ts +120 -11
- package/src/web/search/providers/anthropic.ts +89 -17
- package/src/web/search/providers/base.ts +9 -4
- package/src/web/search/providers/gemini.ts +87 -15
- package/src/web/search/providers/openai-compatible.ts +23 -8
- package/src/web/search/providers/xai.ts +98 -12
- package/src/web/search/types.ts +16 -0
|
@@ -20,7 +20,10 @@ export const SEARXNG_BEARER_CREDENTIAL_PREFIX = "gjc-searxng-bearer:";
|
|
|
20
20
|
/**
|
|
21
21
|
* Shared web search parameters passed to providers.
|
|
22
22
|
*
|
|
23
|
-
* `authStorage` is the
|
|
23
|
+
* `authStorage` is the credential source for canonical providers. Native
|
|
24
|
+
* active-model search may instead use `activeModelContext.resolveCredentials`,
|
|
25
|
+
* which is supplied by ModelRegistry so rotating config credentials and
|
|
26
|
+
* generated headers are refreshed before the request.
|
|
24
27
|
* Opening a sibling SQLite handle or calling provider-direct refresh helpers
|
|
25
28
|
* (e.g. `refreshOpenAIOpenAI code backendToken`, `refreshGoogleCloudToken`) is prohibited:
|
|
26
29
|
* it races the broker's per-credential refresh and POSTs the broker sentinel
|
|
@@ -75,8 +78,8 @@ export interface SearchParams {
|
|
|
75
78
|
codeExecution?: Record<string, unknown>;
|
|
76
79
|
urlContext?: Record<string, unknown>;
|
|
77
80
|
/**
|
|
78
|
-
* The
|
|
79
|
-
* handle
|
|
81
|
+
* The canonical source of truth for credentials. Providers MUST consult this
|
|
82
|
+
* handle (`getApiKey` for bearer-style auth, `getOAuthAccess`
|
|
80
83
|
* when identity metadata is required). Do not open `AgentStorage` or any
|
|
81
84
|
* `AuthCredentialStore` directly — that bypasses the broker pipeline and
|
|
82
85
|
* the per-credential single-flight refresh.
|
|
@@ -108,7 +111,9 @@ export abstract class SearchProvider {
|
|
|
108
111
|
abstract isAvailable(authStorage: AuthStorage, settings?: SearchProviderSettings): Promise<boolean> | boolean;
|
|
109
112
|
|
|
110
113
|
/**
|
|
111
|
-
* Execute a search.
|
|
114
|
+
* Execute a search. Canonical credentials MUST be resolved through
|
|
115
|
+
* `params.authStorage`; active-model credentials MUST use the injected
|
|
116
|
+
* resolver when one is present.
|
|
112
117
|
*/
|
|
113
118
|
abstract search(params: SearchParams): Promise<SearchResponse>;
|
|
114
119
|
}
|
|
@@ -16,7 +16,13 @@ import {
|
|
|
16
16
|
} from "@gajae-code/ai/providers/google-gemini-headers";
|
|
17
17
|
import { fetchWithRetry } from "@gajae-code/utils";
|
|
18
18
|
|
|
19
|
-
import type {
|
|
19
|
+
import type {
|
|
20
|
+
ActiveSearchModelContext,
|
|
21
|
+
ActiveSearchModelCredentials,
|
|
22
|
+
SearchCitation,
|
|
23
|
+
SearchResponse,
|
|
24
|
+
SearchSource,
|
|
25
|
+
} from "../../../web/search/types";
|
|
20
26
|
import { SearchProviderError } from "../../../web/search/types";
|
|
21
27
|
import type { SearchParams } from "./base";
|
|
22
28
|
import { SearchProvider } from "./base";
|
|
@@ -38,6 +44,49 @@ const RATE_LIMIT_BUDGET_MS = 30 * 1000;
|
|
|
38
44
|
const GEMINI_PROVIDERS = ["google-gemini-cli", "google-antigravity"] as const;
|
|
39
45
|
type GeminiProviderId = (typeof GEMINI_PROVIDERS)[number];
|
|
40
46
|
|
|
47
|
+
function isGenerativeLanguageWire(api: string): boolean {
|
|
48
|
+
return api === "google-generative-ai" || api === "google-generative-language" || api === "gemini-wire";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function isGoogleWire(api: string): boolean {
|
|
52
|
+
return isGenerativeLanguageWire(api) || api === "google-gemini-cli" || api === "google-vertex";
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const OFFICIAL_GEMINI_HOSTS = new Set([
|
|
56
|
+
"cloudcode-pa.googleapis.com",
|
|
57
|
+
"daily-cloudcode-pa.googleapis.com",
|
|
58
|
+
"daily-cloudcode-pa.sandbox.googleapis.com",
|
|
59
|
+
"generativelanguage.googleapis.com",
|
|
60
|
+
]);
|
|
61
|
+
const CANONICAL_GEMINI_PROVIDERS = new Set(["google", "gemini", "google-gemini-cli", "google-antigravity"]);
|
|
62
|
+
|
|
63
|
+
function isOfficialGeminiBaseUrl(baseUrl: string | undefined): boolean {
|
|
64
|
+
if (!baseUrl?.trim()) return true;
|
|
65
|
+
try {
|
|
66
|
+
const host = new URL(baseUrl).hostname.toLowerCase().replace(/^\[/, "").replace(/\]$/, "").replace(/\.$/, "");
|
|
67
|
+
return OFFICIAL_GEMINI_HOSTS.has(host);
|
|
68
|
+
} catch {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function isCustomGeminiContext(ctx: ActiveSearchModelContext | undefined): boolean {
|
|
74
|
+
if (!ctx || !isGoogleWire(ctx.api)) return false;
|
|
75
|
+
if (ctx.ownerAuthOverride) return true;
|
|
76
|
+
if (!isOfficialGeminiBaseUrl(ctx.baseUrl)) return true;
|
|
77
|
+
const provider = ctx.provider.toLowerCase();
|
|
78
|
+
return Boolean(ctx.resolveCredentials && !CANONICAL_GEMINI_PROVIDERS.has(provider));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function isCustomGenerativeLanguageContext(ctx: ActiveSearchModelContext | undefined): boolean {
|
|
82
|
+
return isGenerativeLanguageWire(ctx?.api ?? "") && isCustomGeminiContext(ctx);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function hasHeader(headers: Record<string, string>, name: string): boolean {
|
|
86
|
+
const normalized = name.toLowerCase();
|
|
87
|
+
return Object.keys(headers).some(key => key.toLowerCase() === normalized);
|
|
88
|
+
}
|
|
89
|
+
|
|
41
90
|
interface GeminiToolParams {
|
|
42
91
|
google_search?: Record<string, unknown>;
|
|
43
92
|
code_execution?: Record<string, unknown>;
|
|
@@ -433,19 +482,28 @@ export async function searchGemini(params: GeminiSearchParams): Promise<SearchRe
|
|
|
433
482
|
* Native Gemini web search over the public Generative Language REST API
|
|
434
483
|
* (`{baseUrl}/v1beta/models/{model}:generateContent`), reusing the ACTIVE
|
|
435
484
|
* model's own API key + baseUrl. This is the "native search over a proxy" path
|
|
436
|
-
* for
|
|
437
|
-
*
|
|
438
|
-
* Code Assist API with OAuth.
|
|
485
|
+
* for Generative Language wire models. Distinct from {@link searchGemini},
|
|
486
|
+
* which speaks the Cloud Code Assist API with OAuth.
|
|
439
487
|
*/
|
|
440
488
|
async function searchGeminiViaGenerativeLanguage(params: SearchParams): Promise<SearchResponse> {
|
|
441
489
|
const ctx = params.activeModelContext;
|
|
442
490
|
if (!ctx) throw new SearchProviderError("gemini", "Gemini web search requires active model context", 400);
|
|
443
|
-
const
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
491
|
+
const activeCredentials: ActiveSearchModelCredentials = ctx.resolveCredentials
|
|
492
|
+
? await ctx.resolveCredentials({ sessionId: params.sessionId, signal: params.signal })
|
|
493
|
+
: {
|
|
494
|
+
apiKey: await params.authStorage.getApiKey(ctx.provider, params.sessionId, {
|
|
495
|
+
baseUrl: ctx.baseUrl,
|
|
496
|
+
modelId: ctx.modelId,
|
|
497
|
+
signal: params.signal,
|
|
498
|
+
}),
|
|
499
|
+
headers: ctx.headers,
|
|
500
|
+
};
|
|
501
|
+
const apiKey = activeCredentials.apiKey;
|
|
502
|
+
const headers = { ...(activeCredentials.headers ?? ctx.headers ?? {}) };
|
|
503
|
+
if (apiKey && !hasHeader(headers, "x-goog-api-key")) headers["x-goog-api-key"] = apiKey;
|
|
504
|
+
if (!apiKey && !hasHeader(headers, "x-goog-api-key")) {
|
|
505
|
+
throw new SearchProviderError("gemini", `No credentials for ${ctx.provider}`, 401);
|
|
506
|
+
}
|
|
449
507
|
|
|
450
508
|
const model = ctx.wireModelId ?? ctx.modelId;
|
|
451
509
|
const base = (ctx.baseUrl ?? "https://generativelanguage.googleapis.com").replace(/\/+$/, "");
|
|
@@ -468,7 +526,10 @@ async function searchGeminiViaGenerativeLanguage(params: SearchParams): Promise<
|
|
|
468
526
|
|
|
469
527
|
const response = await fetch(url, {
|
|
470
528
|
method: "POST",
|
|
471
|
-
headers: {
|
|
529
|
+
headers: {
|
|
530
|
+
...headers,
|
|
531
|
+
...(hasHeader(headers, "content-type") ? {} : { "Content-Type": "application/json" }),
|
|
532
|
+
},
|
|
472
533
|
body: JSON.stringify(body),
|
|
473
534
|
signal: withHardTimeout(params.signal, "llm"),
|
|
474
535
|
});
|
|
@@ -534,11 +595,22 @@ export class GeminiProvider extends SearchProvider {
|
|
|
534
595
|
}
|
|
535
596
|
|
|
536
597
|
search(params: SearchParams): Promise<SearchResponse> {
|
|
537
|
-
// Native-over-proxy: when canonical gemini-cli/antigravity OAuth is
|
|
538
|
-
// absent but the active model speaks the Generative Language wire, reuse
|
|
539
|
-
// its own API key + baseUrl instead of failing closed.
|
|
540
598
|
const ctx = params.activeModelContext;
|
|
541
|
-
|
|
599
|
+
// Native-over-proxy: a custom model's owner-bound resolver must win over
|
|
600
|
+
// canonical Gemini OAuth. Otherwise the request would silently leave the
|
|
601
|
+
// proxy and hit Cloud Code with another registry's identity.
|
|
602
|
+
if (isCustomGeminiContext(ctx)) {
|
|
603
|
+
if (isCustomGenerativeLanguageContext(ctx)) return searchGeminiViaGenerativeLanguage(params);
|
|
604
|
+
return Promise.reject(
|
|
605
|
+
new SearchProviderError(
|
|
606
|
+
"gemini",
|
|
607
|
+
`Gemini native search does not support custom ${ctx?.api ?? ""} transports`,
|
|
608
|
+
400,
|
|
609
|
+
),
|
|
610
|
+
);
|
|
611
|
+
}
|
|
612
|
+
// Preserve the existing active-key fallback when canonical OAuth is absent.
|
|
613
|
+
if (!hasGeminiOAuth(params.authStorage) && ctx && isGenerativeLanguageWire(ctx.api)) {
|
|
542
614
|
return searchGeminiViaGenerativeLanguage(params);
|
|
543
615
|
}
|
|
544
616
|
return searchGemini({
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { SearchCitation, SearchResponse, SearchSource } from "../types";
|
|
1
|
+
import type { ActiveSearchModelCredentials, SearchCitation, SearchResponse, SearchSource } from "../types";
|
|
2
2
|
import { SearchProviderError } from "../types";
|
|
3
3
|
import type { SearchParams } from "./base";
|
|
4
4
|
import { SearchProvider } from "./base";
|
|
@@ -7,6 +7,11 @@ import { classifyProviderHttpError, withHardTimeout } from "./utils";
|
|
|
7
7
|
|
|
8
8
|
type JsonObject = Record<string, unknown>;
|
|
9
9
|
|
|
10
|
+
function hasHeader(headers: Record<string, string>, name: string): boolean {
|
|
11
|
+
const normalized = name.toLowerCase();
|
|
12
|
+
return Object.keys(headers).some(key => key.toLowerCase() === normalized);
|
|
13
|
+
}
|
|
14
|
+
|
|
10
15
|
function isJsonObject(value: unknown): value is JsonObject {
|
|
11
16
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
12
17
|
}
|
|
@@ -154,15 +159,25 @@ export class OpenAICompatibleSearchProvider extends SearchProvider {
|
|
|
154
159
|
if (ctx.api !== "openai-responses" && ctx.api !== "openai-completions") {
|
|
155
160
|
throw new SearchProviderError(this.id, `OpenAI-compatible web search does not support ${ctx.api}`, 400);
|
|
156
161
|
}
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
162
|
+
const activeCredentials: ActiveSearchModelCredentials = ctx.resolveCredentials
|
|
163
|
+
? await ctx.resolveCredentials({ sessionId: params.sessionId, signal: params.signal })
|
|
164
|
+
: {
|
|
165
|
+
apiKey: await params.authStorage.getApiKey(ctx.provider, params.sessionId, {
|
|
166
|
+
baseUrl: ctx.baseUrl,
|
|
167
|
+
modelId: ctx.modelId,
|
|
168
|
+
signal: params.signal,
|
|
169
|
+
}),
|
|
170
|
+
headers: ctx.headers,
|
|
171
|
+
};
|
|
172
|
+
const apiKey = activeCredentials.apiKey;
|
|
173
|
+
const headers = { ...(activeCredentials.headers ?? ctx.headers ?? {}) };
|
|
174
|
+
if (apiKey && !hasHeader(headers, "authorization")) headers.Authorization = `Bearer ${apiKey}`;
|
|
175
|
+
if (!hasHeader(headers, "authorization")) {
|
|
176
|
+
throw new SearchProviderError(this.id, `No credentials for ${ctx.provider}`, 401);
|
|
177
|
+
}
|
|
178
|
+
if (!hasHeader(headers, "content-type")) headers["Content-Type"] = "application/json";
|
|
163
179
|
const model = ctx.wireModelId ?? ctx.modelId;
|
|
164
180
|
const baseUrl = ctx.baseUrl ?? "";
|
|
165
|
-
const headers = { ...(ctx.headers ?? {}), Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" };
|
|
166
181
|
const messages = [
|
|
167
182
|
{ role: "system", content: params.systemPrompt },
|
|
168
183
|
{ role: "user", content: params.query },
|
|
@@ -6,7 +6,14 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import type { AuthStorage } from "@gajae-code/ai/core";
|
|
8
8
|
import { $credentialEnv, $env } from "@gajae-code/utils";
|
|
9
|
-
import type {
|
|
9
|
+
import type {
|
|
10
|
+
ActiveSearchModelContext,
|
|
11
|
+
ActiveSearchModelCredentials,
|
|
12
|
+
SearchCitation,
|
|
13
|
+
SearchResponse,
|
|
14
|
+
SearchSource,
|
|
15
|
+
SearchUsage,
|
|
16
|
+
} from "../../../web/search/types";
|
|
10
17
|
import { SearchProviderError } from "../../../web/search/types";
|
|
11
18
|
import type { SearchParams } from "./base";
|
|
12
19
|
import { SearchProvider } from "./base";
|
|
@@ -49,11 +56,13 @@ export interface XaiSearchParams {
|
|
|
49
56
|
signal?: AbortSignal;
|
|
50
57
|
authStorage: AuthStorage;
|
|
51
58
|
sessionId?: string;
|
|
59
|
+
activeModelContext?: ActiveSearchModelContext;
|
|
52
60
|
}
|
|
53
61
|
|
|
54
62
|
interface XaiAuth {
|
|
55
63
|
bearer: string;
|
|
56
64
|
mode: "api_key" | "oauth";
|
|
65
|
+
headers?: Record<string, string>;
|
|
57
66
|
}
|
|
58
67
|
|
|
59
68
|
interface PreparedXaiTools {
|
|
@@ -67,7 +76,53 @@ function asTrimmed(value: string | undefined): string | undefined {
|
|
|
67
76
|
return trimmed.length > 0 ? trimmed : undefined;
|
|
68
77
|
}
|
|
69
78
|
|
|
70
|
-
function
|
|
79
|
+
function isOpenAICompatWire(api: string): boolean {
|
|
80
|
+
return api === "openai-responses" || api === "openai-completions";
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function looksXaiModelId(modelId: string | undefined): boolean {
|
|
84
|
+
if (!modelId) return false;
|
|
85
|
+
const id = modelId.toLowerCase();
|
|
86
|
+
return id.startsWith("grok-") || id.startsWith("x-ai/grok-") || id.startsWith("xai/grok-");
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function isOfficialXaiBaseUrl(baseUrl: string | undefined): boolean {
|
|
90
|
+
if (!baseUrl?.trim()) return true;
|
|
91
|
+
try {
|
|
92
|
+
return new URL(baseUrl).hostname.toLowerCase().replace(/\.$/, "") === "api.x.ai";
|
|
93
|
+
} catch {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function isCustomXaiContext(ctx: ActiveSearchModelContext | undefined): ctx is ActiveSearchModelContext {
|
|
99
|
+
if (!ctx || !isOpenAICompatWire(ctx.api)) return false;
|
|
100
|
+
if (ctx.ownerAuthOverride) return true;
|
|
101
|
+
const provider = ctx.provider.toLowerCase();
|
|
102
|
+
if (provider === "xai" && !isOfficialXaiBaseUrl(ctx.baseUrl)) return true;
|
|
103
|
+
if (!looksXaiModelId(ctx.wireModelId) && !looksXaiModelId(ctx.modelId)) return false;
|
|
104
|
+
if (!isOfficialXaiBaseUrl(ctx.baseUrl)) return true;
|
|
105
|
+
return provider !== "xai";
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function hasHeader(headers: Record<string, string>, name: string): boolean {
|
|
109
|
+
const normalized = name.toLowerCase();
|
|
110
|
+
return Object.keys(headers).some(key => key.toLowerCase() === normalized);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function explicitCredentialHeader(headers: Record<string, string> | undefined): string | undefined {
|
|
114
|
+
for (const [key, value] of Object.entries(headers ?? {})) {
|
|
115
|
+
const normalized = key.toLowerCase();
|
|
116
|
+
if (normalized === "authorization" || normalized === "x-api-key") return value;
|
|
117
|
+
}
|
|
118
|
+
return undefined;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function getModel(activeModelContext?: ActiveSearchModelContext): string {
|
|
122
|
+
const ctx = activeModelContext;
|
|
123
|
+
if (isCustomXaiContext(ctx)) {
|
|
124
|
+
return ctx.wireModelId ?? ctx.modelId;
|
|
125
|
+
}
|
|
71
126
|
return asTrimmed($env.PI_XAI_WEB_SEARCH_MODEL) ?? asTrimmed($env.XAI_WEB_SEARCH_MODEL) ?? DEFAULT_MODEL;
|
|
72
127
|
}
|
|
73
128
|
|
|
@@ -76,12 +131,14 @@ export function resolveXaiSearchBaseUrlForTest(): string {
|
|
|
76
131
|
return getBaseUrl();
|
|
77
132
|
}
|
|
78
133
|
|
|
79
|
-
function getBaseUrl(): string {
|
|
134
|
+
function getBaseUrl(activeModelContext?: ActiveSearchModelContext): string {
|
|
135
|
+
const ctx = activeModelContext;
|
|
136
|
+
if (isCustomXaiContext(ctx) && ctx.baseUrl) return ctx.baseUrl;
|
|
80
137
|
return asTrimmed($credentialEnv("XAI_SEARCH_BASE_URL")) ?? DEFAULT_BASE_URL;
|
|
81
138
|
}
|
|
82
139
|
|
|
83
|
-
function responsesEndpoint(): string {
|
|
84
|
-
return `${getBaseUrl().replace(/\/+$/, "")}/responses`;
|
|
140
|
+
function responsesEndpoint(activeModelContext?: ActiveSearchModelContext): string {
|
|
141
|
+
return `${getBaseUrl(activeModelContext).replace(/\/+$/, "")}/responses`;
|
|
85
142
|
}
|
|
86
143
|
|
|
87
144
|
async function resolveXaiAuth(
|
|
@@ -89,7 +146,26 @@ async function resolveXaiAuth(
|
|
|
89
146
|
sessionId: string | undefined,
|
|
90
147
|
model: string,
|
|
91
148
|
signal: AbortSignal | undefined,
|
|
149
|
+
activeModelContext?: ActiveSearchModelContext,
|
|
92
150
|
): Promise<XaiAuth | null> {
|
|
151
|
+
const ctx = activeModelContext;
|
|
152
|
+
if (isCustomXaiContext(ctx)) {
|
|
153
|
+
const activeCredentials: ActiveSearchModelCredentials = ctx.resolveCredentials
|
|
154
|
+
? await ctx.resolveCredentials({ sessionId, signal })
|
|
155
|
+
: {
|
|
156
|
+
apiKey: await authStorage.getApiKey(ctx.provider, sessionId, {
|
|
157
|
+
baseUrl: ctx.baseUrl,
|
|
158
|
+
modelId: ctx.modelId,
|
|
159
|
+
signal,
|
|
160
|
+
}),
|
|
161
|
+
headers: ctx.headers,
|
|
162
|
+
};
|
|
163
|
+
const headers = { ...(activeCredentials.headers ?? ctx.headers ?? {}) };
|
|
164
|
+
const bearer = activeCredentials.apiKey ?? explicitCredentialHeader(headers);
|
|
165
|
+
if (!bearer) return null;
|
|
166
|
+
return { bearer, mode: "api_key", headers };
|
|
167
|
+
}
|
|
168
|
+
|
|
93
169
|
const credentialSessionId = sessionId ?? `xai-search:${crypto.randomUUID()}`;
|
|
94
170
|
const bearer = await authStorage.getApiKey("xai", credentialSessionId, {
|
|
95
171
|
baseUrl: getBaseUrl(),
|
|
@@ -409,8 +485,14 @@ function parseUsage(json: any): SearchUsage | undefined {
|
|
|
409
485
|
|
|
410
486
|
/** Execute xAI web/X search through the Responses API search tools. */
|
|
411
487
|
export async function searchXai(params: XaiSearchParams): Promise<SearchResponse> {
|
|
412
|
-
const model = getModel();
|
|
413
|
-
const auth = await resolveXaiAuth(
|
|
488
|
+
const model = getModel(params.activeModelContext);
|
|
489
|
+
const auth = await resolveXaiAuth(
|
|
490
|
+
params.authStorage,
|
|
491
|
+
params.sessionId,
|
|
492
|
+
model,
|
|
493
|
+
params.signal,
|
|
494
|
+
params.activeModelContext,
|
|
495
|
+
);
|
|
414
496
|
if (!auth) {
|
|
415
497
|
throw new SearchProviderError(
|
|
416
498
|
"xai",
|
|
@@ -419,12 +501,15 @@ export async function searchXai(params: XaiSearchParams): Promise<SearchResponse
|
|
|
419
501
|
);
|
|
420
502
|
}
|
|
421
503
|
|
|
422
|
-
const
|
|
504
|
+
const headers = { ...(auth.headers ?? {}) };
|
|
505
|
+
if (!hasHeader(headers, "authorization") && !hasHeader(headers, "x-api-key")) {
|
|
506
|
+
headers.Authorization = `Bearer ${auth.bearer}`;
|
|
507
|
+
}
|
|
508
|
+
if (!hasHeader(headers, "content-type")) headers["Content-Type"] = "application/json";
|
|
509
|
+
|
|
510
|
+
const response = await fetch(responsesEndpoint(params.activeModelContext), {
|
|
423
511
|
method: "POST",
|
|
424
|
-
headers
|
|
425
|
-
Authorization: `Bearer ${auth.bearer}`,
|
|
426
|
-
"Content-Type": "application/json",
|
|
427
|
-
},
|
|
512
|
+
headers,
|
|
428
513
|
body: JSON.stringify(
|
|
429
514
|
buildXaiRequestBody({
|
|
430
515
|
query: params.query,
|
|
@@ -511,6 +596,7 @@ export class XaiProvider extends SearchProvider {
|
|
|
511
596
|
signal: params.signal,
|
|
512
597
|
authStorage: params.authStorage,
|
|
513
598
|
sessionId: params.sessionId,
|
|
599
|
+
activeModelContext: params.activeModelContext,
|
|
514
600
|
});
|
|
515
601
|
}
|
|
516
602
|
}
|
package/src/web/search/types.ts
CHANGED
|
@@ -27,6 +27,18 @@ export type SearchProviderId =
|
|
|
27
27
|
|
|
28
28
|
export type WebSearchMode = "on" | "off" | "auto";
|
|
29
29
|
|
|
30
|
+
/** Credentials and request headers resolved for the active model. */
|
|
31
|
+
export interface ActiveSearchModelCredentials {
|
|
32
|
+
apiKey?: string;
|
|
33
|
+
headers?: Record<string, string>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Refresh-aware resolver supplied by the model registry for native search. */
|
|
37
|
+
export type ActiveSearchModelCredentialResolver = (options: {
|
|
38
|
+
sessionId?: string;
|
|
39
|
+
signal?: AbortSignal;
|
|
40
|
+
}) => Promise<ActiveSearchModelCredentials>;
|
|
41
|
+
|
|
30
42
|
export interface ActiveSearchModelContext {
|
|
31
43
|
provider: string;
|
|
32
44
|
modelId: string;
|
|
@@ -34,6 +46,10 @@ export interface ActiveSearchModelContext {
|
|
|
34
46
|
api: string;
|
|
35
47
|
baseUrl?: string;
|
|
36
48
|
headers?: Record<string, string>;
|
|
49
|
+
/** Resolve the active model key and current headers before each native search. */
|
|
50
|
+
resolveCredentials?: ActiveSearchModelCredentialResolver;
|
|
51
|
+
/** True when registry-owned auth or transport overrides this active model. */
|
|
52
|
+
ownerAuthOverride?: boolean;
|
|
37
53
|
webSearch?: WebSearchMode;
|
|
38
54
|
}
|
|
39
55
|
|