cartethyia 2.7.6 → 2.7.7
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/.env.example +4 -2
- package/README.md +25 -1
- package/dist/dashboard/assets/{BannedPage-B1N603cM.js → BannedPage-B05aF8Dg.js} +1 -1
- package/dist/dashboard/assets/BannedPage-B05aF8Dg.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolDetailPage-9q4_4NoK.js → CliToolDetailPage-CLUhmjUy.js} +2 -2
- package/dist/dashboard/assets/CliToolDetailPage-CLUhmjUy.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-DwshGtK-.js → CliToolsPage-CQmrqqrk.js} +1 -1
- package/dist/dashboard/assets/CliToolsPage-CQmrqqrk.js.gz +0 -0
- package/dist/dashboard/assets/{CombosPage-D762MIkp.js → CombosPage-CHG_1Wr3.js} +1 -1
- package/dist/dashboard/assets/CombosPage-CHG_1Wr3.js.gz +0 -0
- package/dist/dashboard/assets/{ConsoleLogPage-KRYjHZsX.js → ConsoleLogPage-DPgbklad.js} +1 -1
- package/dist/dashboard/assets/ConsoleLogPage-DPgbklad.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-pnfAqr_-.js +1 -0
- package/dist/dashboard/assets/CustomizationPage-pnfAqr_-.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-DGJuU2A6.js +1 -0
- package/dist/dashboard/assets/HealthEventsModal-DGJuU2A6.js.gz +0 -0
- package/dist/dashboard/assets/{InjectSystemPromptPanel-d5v3K_Yc.js → InjectSystemPromptPanel-C-e2AMz6.js} +2 -2
- package/dist/dashboard/assets/InjectSystemPromptPanel-C-e2AMz6.js.gz +0 -0
- package/dist/dashboard/assets/{LoginPage-Ccv4tjNS.js → LoginPage-J8wPbmfT.js} +1 -1
- package/dist/dashboard/assets/LoginPage-J8wPbmfT.js.gz +0 -0
- package/dist/dashboard/assets/{OverviewPage-DN9fYNWA.js → OverviewPage-BSQnF2Lp.js} +1 -1
- package/dist/dashboard/assets/OverviewPage-BSQnF2Lp.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-DK_e0FJz.js +7 -0
- package/dist/dashboard/assets/ProviderDetailPage-DK_e0FJz.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-D_a3r6Gc.js +1 -0
- package/dist/dashboard/assets/ProvidersPage-D_a3r6Gc.js.gz +0 -0
- package/dist/dashboard/assets/{ProxyPage-DxjvUzSD.js → ProxyPage-BBTRBAQ3.js} +1 -1
- package/dist/dashboard/assets/ProxyPage-BBTRBAQ3.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-B2WqMSG1.js +1 -0
- package/dist/dashboard/assets/QuotaPage-B2WqMSG1.js.gz +0 -0
- package/dist/dashboard/assets/{SettingsPage-BW4AKj2H.js → SettingsPage-D7xp5Ij_.js} +1 -1
- package/dist/dashboard/assets/SettingsPage-D7xp5Ij_.js.gz +0 -0
- package/dist/dashboard/assets/{SetupPage-44BZrxFC.js → SetupPage-LND6_Ss7.js} +1 -1
- package/dist/dashboard/assets/SetupPage-LND6_Ss7.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-DRPavCdU.js → StudioPage-BzNnm6Bi.js} +2 -2
- package/dist/dashboard/assets/StudioPage-BzNnm6Bi.js.gz +0 -0
- package/dist/dashboard/assets/{UsagePage-B7M7LvMM.js → UsagePage-C1TbL24N.js} +2 -2
- package/dist/dashboard/assets/UsagePage-C1TbL24N.js.gz +0 -0
- package/dist/dashboard/assets/{entry-D6WvojeD.js → entry-vqGb8yVz.js} +3 -3
- package/dist/dashboard/assets/entry-vqGb8yVz.js.gz +0 -0
- package/dist/dashboard/assets/formatters-DP-5E01-.js +1 -0
- package/dist/dashboard/assets/formatters-DP-5E01-.js.gz +0 -0
- package/dist/dashboard/assets/{index-Ko4dLRnV.js → index-D36vCZht.js} +2 -2
- package/dist/dashboard/assets/index-D36vCZht.js.gz +0 -0
- package/dist/dashboard/assets/{select-BHqpd-bg.js → select-C9Ssle9b.js} +1 -1
- package/dist/dashboard/assets/select-C9Ssle9b.js.gz +0 -0
- package/dist/dashboard/index.html +2 -2
- package/dist/dashboard/index.html.gz +0 -0
- package/dist/dashboard/precompressed-assets.json +22 -22
- package/migrations/full-postgres/0043_add_provider_custom_user_agent.sql +14 -0
- package/migrations/lite-sqlite3/0043_add_provider_custom_user_agent.sql +10 -0
- package/package.json +3 -1
- package/src/config/security.ts +1 -1
- package/src/config/spec.ts +13 -6
- package/src/console/cli-tools/cartethyia-websearch-plugin.ts +186 -0
- package/src/console/cli-tools/injectors/opencode.ts +42 -14
- package/src/console/providers/catalog/contracts.ts +4 -0
- package/src/console/providers/catalog/routes.ts +1 -0
- package/src/console/providers/detail/routes.ts +8 -0
- package/src/console/providers/detail/store.ts +4 -0
- package/src/console/quota/account-quota-tenant.ts +74 -0
- package/src/observability/telemetry-buffer.ts +2 -1
- package/src/persistence/boot.ts +13 -4
- package/src/persistence/schema.ts +7 -1
- package/src/protocol/canonical-model.ts +7 -0
- package/src/protocol/request/gemini.ts +20 -1
- package/src/protocol/tool-identity.ts +15 -1
- package/src/providers/default-registry.ts +8 -2
- package/src/providers/discovery/discovery-types.ts +18 -0
- package/src/providers/discovery/probe-phases.ts +45 -0
- package/src/providers/discovery/probing-service.ts +41 -6
- package/src/providers/integrations/antigravity/antigravity-protocol.ts +20 -2
- package/src/providers/integrations/antigravity/antigravity.ts +24 -0
- package/src/providers/integrations/codex/codex.ts +28 -1
- package/src/providers/integrations/qoder/chat-proto.ts +328 -0
- package/src/providers/integrations/qoder/claim-qoder-credits.ts +258 -0
- package/src/providers/integrations/qoder/qoder-adapter.ts +275 -0
- package/src/providers/integrations/qoder/qoder-auth.ts +201 -0
- package/src/providers/integrations/qoder/qoder-body.ts +78 -0
- package/src/providers/integrations/qoder/qoder-catalog.ts +5 -1
- package/src/providers/integrations/qoder/qoder-chat.ts +514 -0
- package/src/providers/integrations/qoder/qoder-cosy.ts +162 -0
- package/src/providers/integrations/qoder/qoder-grpc.ts +667 -0
- package/src/providers/integrations/qoder/qoder-ide.ts +584 -0
- package/src/providers/integrations/qoder/qoder-oauth.ts +4 -4
- package/src/providers/integrations/qoder/qoder-quota.ts +1 -1
- package/src/providers/integrations/qoder/qoder-realm.ts +40 -4
- package/src/providers/integrations/qoder/qoder-types.ts +51 -0
- package/src/providers/integrations/qoder/qoder-umid.ts +123 -8
- package/src/providers/integrations/qoder/qoder.ts +147 -1889
- package/src/providers/provider-contracts.ts +7 -0
- package/src/providers/provider-usage.ts +9 -6
- package/src/security/abuse.ts +5 -5
- package/src/transport/dispatch/upstream.ts +4 -2
- package/src/transport/routing/route-catalog.ts +8 -0
- package/src/transport/routing/route-model.ts +4 -0
- package/src/transport/routing/router.ts +8 -117
- package/dist/dashboard/assets/BannedPage-B1N603cM.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-9q4_4NoK.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-DwshGtK-.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-D762MIkp.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-KRYjHZsX.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-DXsxk5uM.js +0 -1
- package/dist/dashboard/assets/CustomizationPage-DXsxk5uM.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-BEjAjD4F.js +0 -1
- package/dist/dashboard/assets/HealthEventsModal-BEjAjD4F.js.gz +0 -0
- package/dist/dashboard/assets/InjectSystemPromptPanel-d5v3K_Yc.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-Ccv4tjNS.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-DN9fYNWA.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-BY0ieUgz.js +0 -5
- package/dist/dashboard/assets/ProviderDetailPage-BY0ieUgz.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-DNsbfea2.js +0 -1
- package/dist/dashboard/assets/ProvidersPage-DNsbfea2.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-DxjvUzSD.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-OaHScjpX.js +0 -1
- package/dist/dashboard/assets/QuotaPage-OaHScjpX.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-BW4AKj2H.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-44BZrxFC.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-DRPavCdU.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-B7M7LvMM.js.gz +0 -0
- package/dist/dashboard/assets/entry-D6WvojeD.js.gz +0 -0
- package/dist/dashboard/assets/formatters-u7JPB5BX.js +0 -1
- package/dist/dashboard/assets/formatters-u7JPB5BX.js.gz +0 -0
- package/dist/dashboard/assets/index-Ko4dLRnV.js.gz +0 -0
- package/dist/dashboard/assets/select-BHqpd-bg.js.gz +0 -0
- package/src/providers/integrations/qoder/qoder-text-tools.ts +0 -277
|
@@ -74,6 +74,17 @@ export interface ProbeModelRequest {
|
|
|
74
74
|
reasoningEffort?: ProbeReasoningEffort;
|
|
75
75
|
maxOutputTokens?: number;
|
|
76
76
|
stream?: boolean;
|
|
77
|
+
/**
|
|
78
|
+
* Provider-specific transport override for the probe.
|
|
79
|
+
*
|
|
80
|
+
* `undefined` (default) probes through the same transport live traffic
|
|
81
|
+
* uses. A provider may expose extra surfaces that only a probe can force —
|
|
82
|
+
* Qoder's IDE surface (`/model/v1`) is the only one that echoes strategy
|
|
83
|
+
* metadata (`provider_model`, `target`, …) alongside the answer, while the
|
|
84
|
+
* production transports (gRPC / COSY) do not. Forcing it makes the probe
|
|
85
|
+
* result the richest available source of truth.
|
|
86
|
+
*/
|
|
87
|
+
surface?: "default" | "cosy" | "grpc" | "ide";
|
|
77
88
|
}
|
|
78
89
|
export interface ProbeModelResult {
|
|
79
90
|
accountId?: string;
|
|
@@ -93,6 +104,13 @@ export interface ProbeModelResult {
|
|
|
93
104
|
reportedModel?: string;
|
|
94
105
|
statusCode?: number;
|
|
95
106
|
error?: string;
|
|
107
|
+
/**
|
|
108
|
+
* Provider-specific upstream metadata about the model that actually served
|
|
109
|
+
* the turn (native model id, supply target, tier). Filled when the probed
|
|
110
|
+
* transport exposes it — Qoder's IDE surface reports it in `raw_usage`;
|
|
111
|
+
* the production transports do not.
|
|
112
|
+
*/
|
|
113
|
+
providerMetadata?: Readonly<Record<string, unknown>>;
|
|
96
114
|
/** Normalized hits a `websearch` probe returned, so the console can show them. */
|
|
97
115
|
searchResults?: ReadonlyArray<{
|
|
98
116
|
readonly title: string;
|
|
@@ -150,6 +150,27 @@ export function extractSample(events: readonly CanonicalEvent[]): string | undef
|
|
|
150
150
|
return trimmed.length > 0 ? trimmed.slice(0, 200) : undefined;
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
+
/**
|
|
154
|
+
* Reads the model an adapter reported in its canonical events.
|
|
155
|
+
*
|
|
156
|
+
* The HTTP-only `readReportedModel` inspects the raw response body, so it
|
|
157
|
+
* cannot see the model on non-HTTP transports (Qoder gRPC). Adapters surface
|
|
158
|
+
* the upstream echo as `response_start`/`message_start` with a `model` field;
|
|
159
|
+
* this reads that as a fallback.
|
|
160
|
+
*/
|
|
161
|
+
export function modelFromEvents(events: readonly CanonicalEvent[]): string | undefined {
|
|
162
|
+
for (const event of events) {
|
|
163
|
+
if (
|
|
164
|
+
(event.type === "response_start" || event.type === "message_start") &&
|
|
165
|
+
typeof event.model === "string" &&
|
|
166
|
+
event.model.trim().length > 0
|
|
167
|
+
) {
|
|
168
|
+
return event.model.trim();
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return undefined;
|
|
172
|
+
}
|
|
173
|
+
|
|
153
174
|
export function hasMeaningfulOutput(events: readonly CanonicalEvent[]): boolean {
|
|
154
175
|
return events.some((event) => {
|
|
155
176
|
if (event.type === "tool_call_delta") return true;
|
|
@@ -581,6 +602,30 @@ export async function loadProbePreferences(args: {
|
|
|
581
602
|
return { probeReasoning, payloadCapture };
|
|
582
603
|
}
|
|
583
604
|
|
|
605
|
+
/**
|
|
606
|
+
* The probe's default identity prompt (`What model are you...`) works for
|
|
607
|
+
* every Qoder roster row except `cmodel` (virtual id `cantus`): that row's
|
|
608
|
+
* gRPC surface answers the identity question with `406 Upstream error [stream]`
|
|
609
|
+
* from the upstream proxy (verified live 2026-11-04; dmodel/qmodel/smodel all
|
|
610
|
+
* accept it). Returns the neutral replacement prompt when the probe targets
|
|
611
|
+
* that row with no explicit prompt, `undefined` otherwise.
|
|
612
|
+
*
|
|
613
|
+
* The replacement prompt must never *ask* an identity question: the upstream
|
|
614
|
+
* guardrail is content-based and swallows the reply (empty sample) for any
|
|
615
|
+
* phrasing that interrogates identity — even indirect ones (`what would you
|
|
616
|
+
* call yourself`, `which model are you`, `perkenalkan dirimu`). A plain
|
|
617
|
+
* greeting that *asserts* the wrong identity (`halo gpt`) makes the model
|
|
618
|
+
* correct us on its own, and returns a sample 3/3 runs (verified live
|
|
619
|
+
* 2026-11-04). We keep that phrasing — it is 100% consistent and the model
|
|
620
|
+
* names itself ("saya Claude... buatan Anthropic") without us ever asking.
|
|
621
|
+
*/
|
|
622
|
+
export function qoderCmodelProbePrompt(providerId: string, modelId: string, prompt: string | undefined): string | undefined {
|
|
623
|
+
if (prompt !== undefined) return undefined;
|
|
624
|
+
if (providerId !== "qoder" && providerId !== "qodercn") return undefined;
|
|
625
|
+
if (modelId !== "cmodel" && modelId !== "cantus") return undefined;
|
|
626
|
+
return "halo gpt";
|
|
627
|
+
}
|
|
628
|
+
|
|
584
629
|
/**
|
|
585
630
|
* Phase 5 — build the canonical probe request. The probe prompt must pass every
|
|
586
631
|
* provider's content-policy review (a bare "hi" trips CodeBuddy's 11140 safety
|
|
@@ -42,10 +42,12 @@ import {
|
|
|
42
42
|
buildProbeCanonicalRequest,
|
|
43
43
|
computeProbeVerdict,
|
|
44
44
|
extractSample,
|
|
45
|
+
modelFromEvents,
|
|
45
46
|
GROK_407_PROBE_PROMPT,
|
|
46
47
|
grok407ProbeFailure,
|
|
47
48
|
hasMeaningfulOutput,
|
|
48
49
|
loadProbePreferences,
|
|
50
|
+
qoderCmodelProbePrompt,
|
|
49
51
|
recordProbeHealth,
|
|
50
52
|
resolveProbeAdapter,
|
|
51
53
|
resolveProbeTarget,
|
|
@@ -186,7 +188,16 @@ export class ProviderProbingService {
|
|
|
186
188
|
request: ProbeModelRequest,
|
|
187
189
|
): Promise<ProbeModelResult> {
|
|
188
190
|
const startedAt = Date.now();
|
|
189
|
-
|
|
191
|
+
// Qoder's IDE surface is the only transport that echoes provider metadata
|
|
192
|
+
// (`provider_model`, `target`, …) in its raw_usage, so probes for Qoder
|
|
193
|
+
// default to it unless the operator picked a surface explicitly. This
|
|
194
|
+
// covers every entry point (single model, all models, all accounts).
|
|
195
|
+
const normalizedRequest: ProbeModelRequest =
|
|
196
|
+
(providerId === "qoder" || providerId === "qodercn") &&
|
|
197
|
+
(request.surface === undefined || request.surface === "default")
|
|
198
|
+
? { ...request, surface: "ide" }
|
|
199
|
+
: request;
|
|
200
|
+
const modelId = normalizedRequest.modelId;
|
|
190
201
|
// One provider snapshot per probe: the contract gate, the account gate, the
|
|
191
202
|
// custom-adapter fallback, and the wire context all read this same row, so a
|
|
192
203
|
// concurrent edit cannot make two reads disagree.
|
|
@@ -207,10 +218,10 @@ export class ProviderProbingService {
|
|
|
207
218
|
db: this.db,
|
|
208
219
|
tenantId,
|
|
209
220
|
providerId,
|
|
210
|
-
requestedAccountId:
|
|
221
|
+
requestedAccountId: normalizedRequest.accountId,
|
|
211
222
|
requiresAccount,
|
|
212
223
|
allowCoolingAccount:
|
|
213
|
-
providerId === "grok" &&
|
|
224
|
+
providerId === "grok" && normalizedRequest.prompt === GROK_407_PROBE_PROMPT,
|
|
214
225
|
});
|
|
215
226
|
if (!account.ok) {
|
|
216
227
|
return { ok: false, latencyMs: Date.now() - startedAt, error: account.error };
|
|
@@ -236,9 +247,16 @@ export class ProviderProbingService {
|
|
|
236
247
|
wireFamily,
|
|
237
248
|
request,
|
|
238
249
|
});
|
|
250
|
+
// Qoder's gRPC surface refuses a probe that asks a model to identify
|
|
251
|
+
// itself (406 from the upstream proxy) for one roster row (`cmodel`,
|
|
252
|
+
// virtual id `cantus`). dmodel/qmodel/smodel accept the identity prompt
|
|
253
|
+
// fine — only cmodel refuses it — so the workaround is scoped to that
|
|
254
|
+
// row, not to every Qoder probe, and never overrides an explicit prompt.
|
|
255
|
+
const qoderCmodelPrompt = qoderCmodelProbePrompt(providerId, modelId, normalizedRequest.prompt);
|
|
256
|
+
const probeRequest = qoderCmodelPrompt === undefined ? normalizedRequest : { ...normalizedRequest, prompt: qoderCmodelPrompt };
|
|
239
257
|
const canonicalRequest = buildProbeCanonicalRequest({
|
|
240
258
|
modelId,
|
|
241
|
-
request,
|
|
259
|
+
request: probeRequest,
|
|
242
260
|
probeReasoning,
|
|
243
261
|
sourceSurface,
|
|
244
262
|
});
|
|
@@ -257,6 +275,11 @@ export class ProviderProbingService {
|
|
|
257
275
|
// The model row's own output ceiling, so a probe with a max-output
|
|
258
276
|
// ceiling (OpenCode free) shapes with the model's limit, not a guess.
|
|
259
277
|
...(outputLimit == null ? {} : { output_limit: outputLimit }),
|
|
278
|
+
// Probe-only transport override, when the operator asked for one. Live
|
|
279
|
+
// dispatch never sets this.
|
|
280
|
+
...(normalizedRequest.surface === undefined || normalizedRequest.surface === "default"
|
|
281
|
+
? {}
|
|
282
|
+
: { probe_surface: normalizedRequest.surface }),
|
|
260
283
|
};
|
|
261
284
|
|
|
262
285
|
const probeSignal = AbortSignal.timeout(30_000);
|
|
@@ -308,7 +331,7 @@ export class ProviderProbingService {
|
|
|
308
331
|
`Provider ${providerId} has no web search transport (request {requestId}). Know your tools — this one just isn't in the box 🧰`,
|
|
309
332
|
);
|
|
310
333
|
}
|
|
311
|
-
const searchProbeQuery =
|
|
334
|
+
const searchProbeQuery = normalizedRequest.prompt?.trim() || "Cartethyia gateway probe";
|
|
312
335
|
const response = await adapter.websearch(
|
|
313
336
|
{ model: modelId, query: searchProbeQuery, max_results: 3 },
|
|
314
337
|
candidate,
|
|
@@ -574,7 +597,16 @@ export class ProviderProbingService {
|
|
|
574
597
|
}
|
|
575
598
|
|
|
576
599
|
const sample = extractSample(events);
|
|
577
|
-
|
|
600
|
+
// The HTTP-only `readReportedModel` covers response-body transports; a
|
|
601
|
+
// non-HTTP transport (Qoder gRPC) reports the upstream echo as a
|
|
602
|
+
// `response_start` event instead — fall back to that when the body read
|
|
603
|
+
// came up empty.
|
|
604
|
+
const reportedModel =
|
|
605
|
+
(await probeCapture?.reportedModel?.catch(() => undefined)) ?? modelFromEvents(events);
|
|
606
|
+
// Provider-specific upstream model metadata (Qoder IDE surface raw_usage):
|
|
607
|
+
// carried on the terminal event by the adapter, surfaced verbatim.
|
|
608
|
+
const terminal = events.find((event) => event.type === "terminal");
|
|
609
|
+
const providerMetadata = terminal?.type === "terminal" ? terminal.provider_metadata : undefined;
|
|
578
610
|
return {
|
|
579
611
|
...(accountId ? { accountId } : {}),
|
|
580
612
|
ok,
|
|
@@ -583,6 +615,9 @@ export class ProviderProbingService {
|
|
|
583
615
|
...(upstreamStatusCode === undefined ? {} : { statusCode: upstreamStatusCode }),
|
|
584
616
|
...(sample ? { sample } : {}),
|
|
585
617
|
...(reportedModel === undefined ? {} : { reportedModel }),
|
|
618
|
+
...(providerMetadata === undefined || Object.keys(providerMetadata).length === 0
|
|
619
|
+
? {}
|
|
620
|
+
: { providerMetadata }),
|
|
586
621
|
...(searchResults !== undefined && searchResults.length > 0 ? { searchResults } : {}),
|
|
587
622
|
...(errorMessage ? { error: errorMessage } : {}),
|
|
588
623
|
};
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
import { createHash } from "node:crypto";
|
|
19
19
|
import { providerBaseUrl } from "../../provider-metadata";
|
|
20
|
-
import type
|
|
20
|
+
import { defineModel, type ModelDefinition } from "../../provider-model-definition";
|
|
21
21
|
import { isRecord } from "../../../protocol/codecs";
|
|
22
22
|
import { modelsDevCatalog } from "../../discovery/models-dev-catalog";
|
|
23
23
|
import type { CanonicalRequest, ReasoningIntent } from "../../../protocol/canonical-model";
|
|
@@ -485,7 +485,25 @@ export async function discoverAntigravityModels(
|
|
|
485
485
|
(await response.json()) as unknown,
|
|
486
486
|
);
|
|
487
487
|
if (models === null) continue;
|
|
488
|
-
|
|
488
|
+
// Keep the static websearch row on top of discovered rows: the live
|
|
489
|
+
// catalog does not publish search SKUs, and `/v1/search` needs a
|
|
490
|
+
// `service_kind: "websearch"` row to route.
|
|
491
|
+
return [
|
|
492
|
+
...models,
|
|
493
|
+
defineModel({
|
|
494
|
+
id: "gemini-2.5-flash",
|
|
495
|
+
providerId: "antigravity",
|
|
496
|
+
serviceKind: "websearch",
|
|
497
|
+
wireFamily: "chat",
|
|
498
|
+
// Empty endpoint — see `ANTIGRAVITY_SEARCH_MODELS`.
|
|
499
|
+
endpoint: "",
|
|
500
|
+
ctx: 1_000_000,
|
|
501
|
+
out: 65_535,
|
|
502
|
+
vision: true,
|
|
503
|
+
reasoning: true,
|
|
504
|
+
toolCall: false,
|
|
505
|
+
}),
|
|
506
|
+
];
|
|
489
507
|
} catch {
|
|
490
508
|
// Best-effort: a failed live catalog must never break static routing.
|
|
491
509
|
}
|
|
@@ -116,6 +116,30 @@ export const ANTIGRAVITY_MODELS: readonly ModelDefinition[] = [
|
|
|
116
116
|
antigravityModel("gpt-oss-120b", 131_072, 32_768, true),
|
|
117
117
|
];
|
|
118
118
|
|
|
119
|
+
/**
|
|
120
|
+
* Web-search rows served by `/v1/search`. Antigravity grounds search through a
|
|
121
|
+
* Gemini chat call with a `web_search` tool (see `websearch`); the cheap flash
|
|
122
|
+
* SKU is the default search model.
|
|
123
|
+
*/
|
|
124
|
+
export const ANTIGRAVITY_SEARCH_MODELS: readonly ModelDefinition[] = [
|
|
125
|
+
defineModel({
|
|
126
|
+
id: "gemini-2.5-flash",
|
|
127
|
+
providerId: "antigravity",
|
|
128
|
+
serviceKind: "websearch",
|
|
129
|
+
wireFamily: "chat",
|
|
130
|
+
// Empty endpoint: the adapter resolves the URL from the request stream flag
|
|
131
|
+
// (`GENERATE_PATH`), so the row only needs to exist as a websearch route.
|
|
132
|
+
// An empty value also keeps it distinct from the llm row (the unique key is
|
|
133
|
+
// provider+model+endpoint).
|
|
134
|
+
endpoint: "",
|
|
135
|
+
ctx: 1_000_000,
|
|
136
|
+
out: 65_535,
|
|
137
|
+
vision: true,
|
|
138
|
+
reasoning: true,
|
|
139
|
+
toolCall: false,
|
|
140
|
+
}),
|
|
141
|
+
];
|
|
142
|
+
|
|
119
143
|
function normalizeAntigravityGrounding(data: unknown, maxResults: number): readonly WebSearchResult[] {
|
|
120
144
|
if (!isRecord(data)) return [];
|
|
121
145
|
const chunks = Array.isArray(data.groundingChunks) ? data.groundingChunks : [];
|
|
@@ -145,6 +145,30 @@ export const CODEX_MODELS: readonly ModelDefinition[] = [
|
|
|
145
145
|
];
|
|
146
146
|
const CODEX_SEARCH_DEFAULT_MODEL = "gpt-5.6-luna";
|
|
147
147
|
|
|
148
|
+
/**
|
|
149
|
+
* Web-search rows served by `/v1/search`. Codex grounds search through a
|
|
150
|
+
* Responses call with a `web_search` tool (see `websearch`); the cheap 5.6
|
|
151
|
+
* SKU is the default search model.
|
|
152
|
+
*/
|
|
153
|
+
export const CODEX_SEARCH_MODELS: readonly ModelDefinition[] = [
|
|
154
|
+
defineModel({
|
|
155
|
+
id: "gpt-5.6-luna",
|
|
156
|
+
providerId: "codex",
|
|
157
|
+
serviceKind: "websearch",
|
|
158
|
+
wireFamily: "responses",
|
|
159
|
+
// Empty endpoint: the adapter falls back to
|
|
160
|
+
// `/backend-api/codex/responses` when the candidate path is empty (see
|
|
161
|
+
// `dispatch`), and an empty value keeps the row distinct from the llm row
|
|
162
|
+
// in the (provider, model, endpoint) unique key.
|
|
163
|
+
endpoint: "",
|
|
164
|
+
ctx: 1_000_000,
|
|
165
|
+
out: 128_000,
|
|
166
|
+
vision: true,
|
|
167
|
+
reasoning: true,
|
|
168
|
+
toolCall: false,
|
|
169
|
+
}),
|
|
170
|
+
];
|
|
171
|
+
|
|
148
172
|
function normalizeCodexSearchCitations(
|
|
149
173
|
citations: readonly unknown[],
|
|
150
174
|
answerText: string,
|
|
@@ -528,7 +552,10 @@ export async function discoverCodexModels(input: {
|
|
|
528
552
|
),
|
|
529
553
|
);
|
|
530
554
|
}
|
|
531
|
-
|
|
555
|
+
// Keep the static websearch rows on top of discovered llm rows: the codex
|
|
556
|
+
// registry does not publish search SKUs, and `/v1/search` needs a
|
|
557
|
+
// `service_kind: "websearch"` row to route.
|
|
558
|
+
return [...byId.values(), ...CODEX_SEARCH_MODELS].sort((a, b) => a.modelId.localeCompare(b.modelId));
|
|
532
559
|
}
|
|
533
560
|
|
|
534
561
|
/** Reads just enough of an untyped body to tell SSE from JSON, then replays it. */
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generated from `vendor/qoder-proto/chat.proto` (Qoder desktop client v1.1.81,
|
|
3
|
+
* `qoder-worker-runtime.mjs`). Do not edit by hand — regenerate from the
|
|
4
|
+
* proto file via `scripts/scratch/gen-chat-proto.ts`.
|
|
5
|
+
*/
|
|
6
|
+
export const CHAT_PROTO_SOURCE = `syntax = "proto3";
|
|
7
|
+
|
|
8
|
+
package model.chat;
|
|
9
|
+
|
|
10
|
+
import "google/protobuf/struct.proto";
|
|
11
|
+
import "google/protobuf/wrappers.proto";
|
|
12
|
+
|
|
13
|
+
option java_multiple_files = true;
|
|
14
|
+
option java_package = "com.qoder.grpc.model.chat";
|
|
15
|
+
option java_outer_classname = "ChatProto";
|
|
16
|
+
|
|
17
|
+
// Reasoning effort level for model reasoning
|
|
18
|
+
enum ReasoningEffort {
|
|
19
|
+
REASONING_EFFORT_UNSPECIFIED = 0;
|
|
20
|
+
REASONING_EFFORT_NONE = 1;
|
|
21
|
+
REASONING_EFFORT_LOW = 2;
|
|
22
|
+
REASONING_EFFORT_MEDIUM = 3;
|
|
23
|
+
REASONING_EFFORT_HIGH = 4;
|
|
24
|
+
REASONING_EFFORT_XHIGH = 5;
|
|
25
|
+
REASONING_EFFORT_MAX = 6;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// ─── Request messages ────────────────────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
// Chat completion request matching OpenAI-compatible API
|
|
31
|
+
message ChatCompletionRequest {
|
|
32
|
+
string model = 1;
|
|
33
|
+
repeated ChatMessage messages = 2;
|
|
34
|
+
google.protobuf.DoubleValue temperature = 3;
|
|
35
|
+
google.protobuf.DoubleValue top_p = 4;
|
|
36
|
+
google.protobuf.Int32Value max_tokens = 5;
|
|
37
|
+
bool stream = 6;
|
|
38
|
+
repeated string stop = 7;
|
|
39
|
+
google.protobuf.DoubleValue presence_penalty = 8;
|
|
40
|
+
google.protobuf.DoubleValue frequency_penalty = 9;
|
|
41
|
+
google.protobuf.Int32Value n = 10;
|
|
42
|
+
google.protobuf.StringValue user = 11;
|
|
43
|
+
google.protobuf.Int32Value seed = 12;
|
|
44
|
+
Reasoning reasoning = 13;
|
|
45
|
+
google.protobuf.BoolValue parallel_tool_calls = 14;
|
|
46
|
+
map<string, double> logit_bias = 15;
|
|
47
|
+
repeated Tool tools = 16;
|
|
48
|
+
google.protobuf.Value tool_choice = 17;
|
|
49
|
+
ResponseFormat response_format = 18;
|
|
50
|
+
repeated string modalities = 19;
|
|
51
|
+
StreamOptions stream_options = 20;
|
|
52
|
+
map<string, ChatPatchList> patches = 21;
|
|
53
|
+
ChatMetadata metadata = 22;
|
|
54
|
+
google.protobuf.Struct extras = 23;
|
|
55
|
+
CustomModelConfig custom_model = 24;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Wrapper for repeated ChatPatch (used in patches map value)
|
|
59
|
+
message ChatPatchList {
|
|
60
|
+
repeated ChatPatch patches = 1;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Chat message in the conversation (request side)
|
|
64
|
+
message ChatMessage {
|
|
65
|
+
string cache_id = 1;
|
|
66
|
+
string role = 2;
|
|
67
|
+
// content can be plain text or a list of multimodal content parts
|
|
68
|
+
oneof content_type {
|
|
69
|
+
string text_content = 3;
|
|
70
|
+
ContentPartList parts_content = 12;
|
|
71
|
+
}
|
|
72
|
+
google.protobuf.StringValue name = 4;
|
|
73
|
+
repeated ToolCall tool_calls = 5;
|
|
74
|
+
google.protobuf.StringValue tool_call_id = 6;
|
|
75
|
+
google.protobuf.StringValue tool_call_call_id = 7;
|
|
76
|
+
google.protobuf.StringValue reasoning_content = 8;
|
|
77
|
+
ReasoningItem reasoning_item = 9;
|
|
78
|
+
google.protobuf.Struct extends = 10;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Wrapper for repeated ContentPart
|
|
82
|
+
message ContentPartList {
|
|
83
|
+
repeated ContentPart parts = 1;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Content part for multimodal messages
|
|
87
|
+
message ContentPart {
|
|
88
|
+
string type = 1;
|
|
89
|
+
google.protobuf.StringValue text = 2;
|
|
90
|
+
ImageUrl image_url = 3;
|
|
91
|
+
InputAudio input_audio = 4;
|
|
92
|
+
CacheControl cache_control = 5;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Cache control configuration for content parts
|
|
96
|
+
message CacheControl {
|
|
97
|
+
string type = 1;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Image URL in message content
|
|
101
|
+
message ImageUrl {
|
|
102
|
+
string url = 1;
|
|
103
|
+
google.protobuf.StringValue detail = 2;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Audio input in message content
|
|
107
|
+
message InputAudio {
|
|
108
|
+
string data = 1;
|
|
109
|
+
string format = 2;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Reasoning item for model series (e.g. gpt-5)
|
|
113
|
+
message ReasoningItem {
|
|
114
|
+
string id = 1;
|
|
115
|
+
google.protobuf.StringValue encrypted_content = 2;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Streaming options
|
|
119
|
+
message StreamOptions {
|
|
120
|
+
google.protobuf.BoolValue include_usage = 1;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Tool available for the model to call
|
|
124
|
+
message Tool {
|
|
125
|
+
string cache_id = 1;
|
|
126
|
+
string type = 2;
|
|
127
|
+
Function function = 3;
|
|
128
|
+
Advisor advisor = 4;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Function definition
|
|
132
|
+
message Function {
|
|
133
|
+
string name = 1;
|
|
134
|
+
google.protobuf.StringValue description = 2;
|
|
135
|
+
google.protobuf.Struct parameters = 3;
|
|
136
|
+
google.protobuf.BoolValue strict = 4;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Advisor definition
|
|
140
|
+
message Advisor {
|
|
141
|
+
string name = 1;
|
|
142
|
+
string model = 2;
|
|
143
|
+
int32 max_uses = 3;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Tool call made by the assistant
|
|
147
|
+
message ToolCall {
|
|
148
|
+
google.protobuf.StringValue id = 1;
|
|
149
|
+
google.protobuf.StringValue call_id = 2;
|
|
150
|
+
google.protobuf.StringValue type = 3;
|
|
151
|
+
FunctionCall function = 4;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Function call details
|
|
155
|
+
message FunctionCall {
|
|
156
|
+
string name = 1;
|
|
157
|
+
google.protobuf.Value arguments = 2;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Reasoning configuration
|
|
161
|
+
message Reasoning {
|
|
162
|
+
ReasoningEffort effort = 1;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Response format specification
|
|
166
|
+
message ResponseFormat {
|
|
167
|
+
string type = 1;
|
|
168
|
+
JsonSchema json_schema = 2;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// JSON schema for structured outputs
|
|
172
|
+
message JsonSchema {
|
|
173
|
+
string name = 1;
|
|
174
|
+
google.protobuf.StringValue description = 2;
|
|
175
|
+
google.protobuf.Struct schema = 3;
|
|
176
|
+
google.protobuf.BoolValue strict = 4;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Metadata attached to the chat request
|
|
180
|
+
message ChatMetadata {
|
|
181
|
+
WorkspaceMetadata workspace = 1;
|
|
182
|
+
BusinessMetadata business = 2;
|
|
183
|
+
ContextMetadata context = 3;
|
|
184
|
+
UserMetadata user = 4;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Workspace metadata
|
|
188
|
+
message WorkspaceMetadata {
|
|
189
|
+
int32 file_count = 1;
|
|
190
|
+
repeated string languages = 2;
|
|
191
|
+
string codebase_status = 3;
|
|
192
|
+
string codebase_soft_status = 4;
|
|
193
|
+
string codebase_external_id = 5;
|
|
194
|
+
string data_policy = 6;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Business metadata
|
|
198
|
+
message BusinessMetadata {
|
|
199
|
+
string id = 1;
|
|
200
|
+
string name = 2;
|
|
201
|
+
int64 begin_at = 3;
|
|
202
|
+
string product = 4;
|
|
203
|
+
string version = 5;
|
|
204
|
+
string type = 6;
|
|
205
|
+
string stage = 7;
|
|
206
|
+
string sub_task = 8;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Context metadata for request tracing and client identification
|
|
210
|
+
message ContextMetadata {
|
|
211
|
+
string request_id = 1;
|
|
212
|
+
string session_id = 2;
|
|
213
|
+
string task_id = 3;
|
|
214
|
+
string request_set_id = 4;
|
|
215
|
+
string machine_id = 5;
|
|
216
|
+
string client_type = 6;
|
|
217
|
+
string source_session_id = 7;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// User metadata
|
|
221
|
+
message UserMetadata {
|
|
222
|
+
string user_id = 1;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Custom model configuration (bring-your-own-key)
|
|
226
|
+
message CustomModelConfig {
|
|
227
|
+
string name = 1;
|
|
228
|
+
string url = 2;
|
|
229
|
+
string style = 3;
|
|
230
|
+
string provider = 4;
|
|
231
|
+
string type = 5;
|
|
232
|
+
string model = 6;
|
|
233
|
+
google.protobuf.Struct parameters = 7;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Patch to apply to the chat request
|
|
237
|
+
message ChatPatch {
|
|
238
|
+
string cache_id = 1;
|
|
239
|
+
string action = 2;
|
|
240
|
+
google.protobuf.Int32Value position = 3;
|
|
241
|
+
google.protobuf.StringValue content = 4;
|
|
242
|
+
google.protobuf.StringValue name = 5;
|
|
243
|
+
google.protobuf.StringValue rename_to = 6;
|
|
244
|
+
google.protobuf.StringValue description = 7;
|
|
245
|
+
google.protobuf.Struct parameters = 8;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// ─── Response messages ───────────────────────────────────────────────────────
|
|
249
|
+
|
|
250
|
+
// Shared response chat message type (used in both streaming delta and
|
|
251
|
+
// non-streaming message fields)
|
|
252
|
+
message ResponseChatMessage {
|
|
253
|
+
string role = 1;
|
|
254
|
+
string content = 2;
|
|
255
|
+
string reasoning_content = 3;
|
|
256
|
+
repeated google.protobuf.Struct tool_calls = 4;
|
|
257
|
+
google.protobuf.Value reasoning_item = 5;
|
|
258
|
+
google.protobuf.Struct extends = 6;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Single choice in the non-streaming response
|
|
262
|
+
message NonStreamChoice {
|
|
263
|
+
int32 index = 1;
|
|
264
|
+
ResponseChatMessage message = 2;
|
|
265
|
+
string finish_reason = 3;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Chat completion response (non-streaming)
|
|
269
|
+
message ChatCompletionResponse {
|
|
270
|
+
int64 code = 1;
|
|
271
|
+
string id = 2;
|
|
272
|
+
string object = 3;
|
|
273
|
+
int64 created = 4;
|
|
274
|
+
string model = 5;
|
|
275
|
+
repeated NonStreamChoice choices = 6;
|
|
276
|
+
Usage usage = 7;
|
|
277
|
+
google.protobuf.Struct extras = 8;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Single choice in the streaming chunk
|
|
281
|
+
message StreamChoice {
|
|
282
|
+
int32 index = 1;
|
|
283
|
+
ResponseChatMessage delta = 2;
|
|
284
|
+
string finish_reason = 3;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Streaming response chunk
|
|
288
|
+
message ChatCompletionChunk {
|
|
289
|
+
int64 code = 1;
|
|
290
|
+
string id = 2;
|
|
291
|
+
string object = 3;
|
|
292
|
+
int64 created = 4;
|
|
293
|
+
string model = 5;
|
|
294
|
+
repeated StreamChoice choices = 6;
|
|
295
|
+
Usage usage = 7;
|
|
296
|
+
google.protobuf.Struct extras = 8;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Token usage statistics
|
|
300
|
+
message Usage {
|
|
301
|
+
int32 prompt_tokens = 1;
|
|
302
|
+
int32 completion_tokens = 2;
|
|
303
|
+
int32 total_tokens = 3;
|
|
304
|
+
PromptTokensDetails prompt_tokens_details = 4;
|
|
305
|
+
CompletionTokensDetails completion_tokens_details = 5;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Prompt token details
|
|
309
|
+
message PromptTokensDetails {
|
|
310
|
+
int32 cached_tokens = 1;
|
|
311
|
+
int32 cacheable_tokens = 2;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Completion token details
|
|
315
|
+
message CompletionTokensDetails {
|
|
316
|
+
int32 reasoning_tokens = 1;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// ─── Service definition ──────────────────────────────────────────────────────
|
|
320
|
+
|
|
321
|
+
service ChatService {
|
|
322
|
+
// Unary non-streaming chat completion
|
|
323
|
+
rpc ChatCompletion(ChatCompletionRequest) returns (ChatCompletionResponse);
|
|
324
|
+
|
|
325
|
+
// Server-streaming chat completion; each message is one SSE chunk
|
|
326
|
+
rpc ChatCompletionStream(ChatCompletionRequest) returns (stream ChatCompletionChunk);
|
|
327
|
+
}
|
|
328
|
+
`;
|