@jacobbd/relay-ai 0.7.4 → 0.7.6
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/dist/{chunk-EUY2MVOS.js → chunk-GHSURQOK.js} +589 -69
- package/dist/chunk-GHSURQOK.js.map +1 -0
- package/dist/cli.js +131 -32
- package/dist/cli.js.map +1 -1
- package/dist/core/index.js +5 -3
- package/dist/core/index.js.map +1 -1
- package/dist/{ui-command-CF3IEIZP.js → ui-command-27X4WJKC.js} +2 -2
- package/package.json +5 -3
- package/dist/chunk-EUY2MVOS.js.map +0 -1
- /package/dist/{ui-command-CF3IEIZP.js.map → ui-command-27X4WJKC.js.map} +0 -0
|
@@ -11,7 +11,7 @@ import { join } from "path";
|
|
|
11
11
|
// package.json
|
|
12
12
|
var package_default = {
|
|
13
13
|
name: "@jacobbd/relay-ai",
|
|
14
|
-
version: "0.7.
|
|
14
|
+
version: "0.7.6",
|
|
15
15
|
publishConfig: {
|
|
16
16
|
access: "public"
|
|
17
17
|
},
|
|
@@ -48,11 +48,13 @@ var package_default = {
|
|
|
48
48
|
scripts: {
|
|
49
49
|
build: "tsup && tsup --config tsup.core.config.ts && node scripts/copy-ui-assets.mjs",
|
|
50
50
|
dev: "tsup --watch",
|
|
51
|
-
test:
|
|
51
|
+
test: 'vitest run --exclude "tests/debug-*.test.ts"',
|
|
52
|
+
"test:live": "vitest run tests/debug-xai.test.ts tests/debug-openai-oauth.test.ts",
|
|
52
53
|
"test:watch": "vitest",
|
|
53
54
|
typecheck: "tsc --noEmit",
|
|
55
|
+
"release:check": "node scripts/release-metadata.mjs",
|
|
54
56
|
"refresh:models-dev": "node scripts/refresh-models-dev-cache.mjs",
|
|
55
|
-
prepublishOnly:
|
|
57
|
+
prepublishOnly: "npm run release:check && npm run build"
|
|
56
58
|
},
|
|
57
59
|
dependencies: {
|
|
58
60
|
"@ai-sdk/alibaba": "^1.0.26",
|
|
@@ -4174,6 +4176,10 @@ function getUiDebugLogPath() {
|
|
|
4174
4176
|
function getServerDebugLogPath() {
|
|
4175
4177
|
return join8(ensureLogsDir(), SERVER_DEBUG_LOG);
|
|
4176
4178
|
}
|
|
4179
|
+
function getAntigravityDebugLogPath(tracePrefix) {
|
|
4180
|
+
const surface = tracePrefix === "antigravity" ? "app" : tracePrefix;
|
|
4181
|
+
return join8(ensureLogsDir(), `antigravity-${surface}-debug.log`);
|
|
4182
|
+
}
|
|
4177
4183
|
function makeTraceLogger(logPath) {
|
|
4178
4184
|
resetTraceLog(logPath);
|
|
4179
4185
|
return (message) => writeSecureLogLine(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${message}`);
|
|
@@ -4309,6 +4315,240 @@ function maskGatewayModelId(aliasId) {
|
|
|
4309
4315
|
return `anthropic-${reverseSegment(providerSlug)}__${reverseSegment(modelSuffix)}`;
|
|
4310
4316
|
}
|
|
4311
4317
|
|
|
4318
|
+
// src/subagent-route-registry.ts
|
|
4319
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
4320
|
+
var DEFAULT_TTL_MS = 5 * 6e4;
|
|
4321
|
+
var DEFAULT_MAX_ENTRIES = 1024;
|
|
4322
|
+
var ROUTE_MARKER_PATTERN = /(?:\n\n)?<relay-ai-subagent-route token="([0-9a-f-]{36})"\s*\/>/gi;
|
|
4323
|
+
function firstHeader(value) {
|
|
4324
|
+
const first = Array.isArray(value) ? value[0] : value;
|
|
4325
|
+
return typeof first === "string" && first.trim() ? first.trim() : void 0;
|
|
4326
|
+
}
|
|
4327
|
+
function extractClaudeSessionId(headers, body) {
|
|
4328
|
+
const headerSession = firstHeader(headers["x-claude-code-session-id"]);
|
|
4329
|
+
if (headerSession) return headerSession;
|
|
4330
|
+
const userId = body?.metadata?.user_id;
|
|
4331
|
+
if (typeof userId !== "string") return void 0;
|
|
4332
|
+
try {
|
|
4333
|
+
const parsed = JSON.parse(userId);
|
|
4334
|
+
return typeof parsed.session_id === "string" && parsed.session_id.trim() ? parsed.session_id.trim() : void 0;
|
|
4335
|
+
} catch {
|
|
4336
|
+
return void 0;
|
|
4337
|
+
}
|
|
4338
|
+
}
|
|
4339
|
+
function appendSubagentRouteMarker(prompt, token) {
|
|
4340
|
+
return `${prompt}
|
|
4341
|
+
|
|
4342
|
+
<relay-ai-subagent-route token="${token}"/>`;
|
|
4343
|
+
}
|
|
4344
|
+
function findMarkersInUserMessages(body) {
|
|
4345
|
+
if (!Array.isArray(body.messages)) return void 0;
|
|
4346
|
+
const tokens = [];
|
|
4347
|
+
const messages = [...body.messages];
|
|
4348
|
+
for (let messageIndex = 0; messageIndex < messages.length; messageIndex++) {
|
|
4349
|
+
const message = body.messages[messageIndex];
|
|
4350
|
+
if (!message || message.role !== "user") continue;
|
|
4351
|
+
if (typeof message.content === "string") {
|
|
4352
|
+
const matches = [...message.content.matchAll(ROUTE_MARKER_PATTERN)];
|
|
4353
|
+
if (matches.length === 0) continue;
|
|
4354
|
+
tokens.push(...matches.flatMap((match) => match[1] ? [match[1]] : []));
|
|
4355
|
+
messages[messageIndex] = {
|
|
4356
|
+
...message,
|
|
4357
|
+
content: message.content.replace(ROUTE_MARKER_PATTERN, "").trimEnd()
|
|
4358
|
+
};
|
|
4359
|
+
continue;
|
|
4360
|
+
}
|
|
4361
|
+
if (!Array.isArray(message.content)) continue;
|
|
4362
|
+
const content = [...message.content];
|
|
4363
|
+
for (let partIndex = 0; partIndex < message.content.length; partIndex++) {
|
|
4364
|
+
const part = message.content[partIndex];
|
|
4365
|
+
if (!part || part.type !== "text" || typeof part.text !== "string") continue;
|
|
4366
|
+
const matches = [...part.text.matchAll(ROUTE_MARKER_PATTERN)];
|
|
4367
|
+
if (matches.length === 0) continue;
|
|
4368
|
+
tokens.push(...matches.flatMap((match) => match[1] ? [match[1]] : []));
|
|
4369
|
+
content[partIndex] = {
|
|
4370
|
+
...part,
|
|
4371
|
+
text: part.text.replace(ROUTE_MARKER_PATTERN, "").trimEnd()
|
|
4372
|
+
};
|
|
4373
|
+
}
|
|
4374
|
+
messages[messageIndex] = { ...message, content };
|
|
4375
|
+
}
|
|
4376
|
+
return tokens.length > 0 ? { tokens, body: { ...body, messages } } : void 0;
|
|
4377
|
+
}
|
|
4378
|
+
var SubagentRouteRegistry = class {
|
|
4379
|
+
entries = /* @__PURE__ */ new Map();
|
|
4380
|
+
ttlMs;
|
|
4381
|
+
maxEntries;
|
|
4382
|
+
now;
|
|
4383
|
+
constructor(options = {}) {
|
|
4384
|
+
this.ttlMs = options.ttlMs ?? DEFAULT_TTL_MS;
|
|
4385
|
+
this.maxEntries = options.maxEntries ?? DEFAULT_MAX_ENTRIES;
|
|
4386
|
+
this.now = options.now ?? Date.now;
|
|
4387
|
+
}
|
|
4388
|
+
register(sessionId, modelId) {
|
|
4389
|
+
this.cleanup();
|
|
4390
|
+
while (this.entries.size >= this.maxEntries) {
|
|
4391
|
+
const oldest = this.entries.keys().next().value;
|
|
4392
|
+
if (!oldest) break;
|
|
4393
|
+
this.entries.delete(oldest);
|
|
4394
|
+
}
|
|
4395
|
+
const token = randomUUID2();
|
|
4396
|
+
this.entries.set(token, { sessionId, modelId, createdAt: this.now() });
|
|
4397
|
+
return token;
|
|
4398
|
+
}
|
|
4399
|
+
consume(headers, body) {
|
|
4400
|
+
this.cleanup();
|
|
4401
|
+
if (!firstHeader(headers["x-claude-code-agent-id"])) return void 0;
|
|
4402
|
+
const sessionId = extractClaudeSessionId(headers, body);
|
|
4403
|
+
if (!sessionId) return void 0;
|
|
4404
|
+
const marked = findMarkersInUserMessages(body);
|
|
4405
|
+
if (!marked) return void 0;
|
|
4406
|
+
const token = [...marked.tokens].reverse().find((candidate) => this.entries.get(candidate)?.sessionId === sessionId);
|
|
4407
|
+
if (!token) return void 0;
|
|
4408
|
+
const entry = this.entries.get(token);
|
|
4409
|
+
this.entries.delete(token);
|
|
4410
|
+
return { modelId: entry.modelId, body: marked.body };
|
|
4411
|
+
}
|
|
4412
|
+
cleanup() {
|
|
4413
|
+
const cutoff = this.now() - this.ttlMs;
|
|
4414
|
+
for (const [token, entry] of this.entries) {
|
|
4415
|
+
if (entry.createdAt < cutoff) this.entries.delete(token);
|
|
4416
|
+
}
|
|
4417
|
+
}
|
|
4418
|
+
};
|
|
4419
|
+
|
|
4420
|
+
// src/subagent-model-routing.ts
|
|
4421
|
+
var CLAUDE_MODEL_FAMILIES = ["sonnet", "opus", "haiku", "fable"];
|
|
4422
|
+
var CLAUDE_MODEL_FAMILY_SET = new Set(CLAUDE_MODEL_FAMILIES);
|
|
4423
|
+
function isRecord(value) {
|
|
4424
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4425
|
+
}
|
|
4426
|
+
function claudeModelFamily(modelId) {
|
|
4427
|
+
const normalized = modelId.toLowerCase();
|
|
4428
|
+
if (!normalized.startsWith("claude-")) return void 0;
|
|
4429
|
+
return CLAUDE_MODEL_FAMILIES.find((family) => normalized.includes(family));
|
|
4430
|
+
}
|
|
4431
|
+
function isClaudeAgentTool(tool4) {
|
|
4432
|
+
if (tool4.name !== "Agent" || !isRecord(tool4.input_schema)) return false;
|
|
4433
|
+
const properties = tool4.input_schema.properties;
|
|
4434
|
+
if (!isRecord(properties)) return false;
|
|
4435
|
+
return ["description", "prompt", "subagent_type"].every((name) => isRecord(properties[name]));
|
|
4436
|
+
}
|
|
4437
|
+
var UnavailableSubagentModelError = class extends Error {
|
|
4438
|
+
constructor(selector, routing) {
|
|
4439
|
+
const visible = routing.models.slice(0, MAX_MODEL_CATALOG).map((model) => model.id);
|
|
4440
|
+
const omitted = routing.models.length - visible.length;
|
|
4441
|
+
const suffix = omitted > 0 ? `, and ${omitted} more` : "";
|
|
4442
|
+
super(
|
|
4443
|
+
`Subagent model "${selector}" is unavailable in this Relay AI session. Available model ids: ${visible.join(", ")}${suffix}.`
|
|
4444
|
+
);
|
|
4445
|
+
this.selector = selector;
|
|
4446
|
+
this.name = "UnavailableSubagentModelError";
|
|
4447
|
+
}
|
|
4448
|
+
selector;
|
|
4449
|
+
statusCode = 400;
|
|
4450
|
+
};
|
|
4451
|
+
function normalizeClaudeAgentInput(input, routing) {
|
|
4452
|
+
const source = isRecord(input) ? input : {};
|
|
4453
|
+
const normalized = { ...source };
|
|
4454
|
+
if (source.subagent_type === "fork") {
|
|
4455
|
+
return { input: normalized, decision: { kind: "fork" } };
|
|
4456
|
+
}
|
|
4457
|
+
const rawModel = source.model;
|
|
4458
|
+
if (rawModel == null) {
|
|
4459
|
+
normalized.model = routing.parentModelId;
|
|
4460
|
+
return {
|
|
4461
|
+
input: normalized,
|
|
4462
|
+
decision: { kind: "inherit", resolvedModelId: routing.parentModelId }
|
|
4463
|
+
};
|
|
4464
|
+
}
|
|
4465
|
+
if (typeof rawModel !== "string") {
|
|
4466
|
+
throw new UnavailableSubagentModelError(String(rawModel), routing);
|
|
4467
|
+
}
|
|
4468
|
+
const selector = rawModel.trim();
|
|
4469
|
+
if (selector === "" || selector === "inherit") {
|
|
4470
|
+
normalized.model = routing.parentModelId;
|
|
4471
|
+
return {
|
|
4472
|
+
input: normalized,
|
|
4473
|
+
decision: { kind: "inherit", resolvedModelId: routing.parentModelId }
|
|
4474
|
+
};
|
|
4475
|
+
}
|
|
4476
|
+
const exposed = routing.models.find((model) => model.id === selector);
|
|
4477
|
+
if (exposed) {
|
|
4478
|
+
normalized.model = exposed.id;
|
|
4479
|
+
return {
|
|
4480
|
+
input: normalized,
|
|
4481
|
+
decision: { kind: "explicit", resolvedModelId: exposed.id }
|
|
4482
|
+
};
|
|
4483
|
+
}
|
|
4484
|
+
const compatible = routing.models.find((model) => model.compatibilityIds.includes(selector));
|
|
4485
|
+
if (compatible) {
|
|
4486
|
+
normalized.model = compatible.id;
|
|
4487
|
+
return {
|
|
4488
|
+
input: normalized,
|
|
4489
|
+
decision: {
|
|
4490
|
+
kind: "compatibility",
|
|
4491
|
+
requestedModelId: selector,
|
|
4492
|
+
resolvedModelId: compatible.id
|
|
4493
|
+
}
|
|
4494
|
+
};
|
|
4495
|
+
}
|
|
4496
|
+
if (CLAUDE_MODEL_FAMILY_SET.has(selector)) {
|
|
4497
|
+
const family = selector;
|
|
4498
|
+
const nativeModel = routing.models.find((model) => model.family === family);
|
|
4499
|
+
const resolvedModelId = nativeModel?.id ?? routing.parentModelId;
|
|
4500
|
+
normalized.model = resolvedModelId;
|
|
4501
|
+
return {
|
|
4502
|
+
input: normalized,
|
|
4503
|
+
decision: nativeModel ? { kind: "family", requestedModelId: family, resolvedModelId } : { kind: "family-fallback", requestedModelId: family, resolvedModelId }
|
|
4504
|
+
};
|
|
4505
|
+
}
|
|
4506
|
+
throw new UnavailableSubagentModelError(selector, routing);
|
|
4507
|
+
}
|
|
4508
|
+
function prepareClaudeAgentInput(input, routing) {
|
|
4509
|
+
const normalized = normalizeClaudeAgentInput(input, routing);
|
|
4510
|
+
const decision = normalized.decision;
|
|
4511
|
+
if (decision.kind === "fork") return normalized;
|
|
4512
|
+
if (!routing.registerSubagentRoute) return normalized;
|
|
4513
|
+
const prompt = normalized.input.prompt;
|
|
4514
|
+
if (typeof prompt !== "string") return normalized;
|
|
4515
|
+
const token = routing.registerSubagentRoute(decision.resolvedModelId);
|
|
4516
|
+
const clientInput = { ...normalized.input };
|
|
4517
|
+
const target = routing.models.find((model) => model.id === decision.resolvedModelId);
|
|
4518
|
+
if (target?.family) clientInput.model = target.family;
|
|
4519
|
+
else delete clientInput.model;
|
|
4520
|
+
clientInput.prompt = appendSubagentRouteMarker(prompt, token);
|
|
4521
|
+
return { input: clientInput, decision };
|
|
4522
|
+
}
|
|
4523
|
+
function augmentClaudeAgentTool(tool4, routing) {
|
|
4524
|
+
const inputSchema = isRecord(tool4.input_schema) ? tool4.input_schema : {};
|
|
4525
|
+
const properties = isRecord(inputSchema.properties) ? inputSchema.properties : {};
|
|
4526
|
+
const originalModel = isRecord(properties.model) ? properties.model : {};
|
|
4527
|
+
const smallCatalog = routing.models.length <= MAX_MODEL_CATALOG;
|
|
4528
|
+
const modelProperty = {
|
|
4529
|
+
...originalModel,
|
|
4530
|
+
type: "string"
|
|
4531
|
+
};
|
|
4532
|
+
if (smallCatalog) {
|
|
4533
|
+
const originalEnum = Array.isArray(originalModel.enum) ? originalModel.enum.filter((value) => typeof value === "string") : CLAUDE_MODEL_FAMILIES;
|
|
4534
|
+
modelProperty.enum = [.../* @__PURE__ */ new Set([...originalEnum, ...routing.models.map((model) => model.id)])];
|
|
4535
|
+
} else {
|
|
4536
|
+
delete modelProperty.enum;
|
|
4537
|
+
}
|
|
4538
|
+
const guidance = smallCatalog ? `Relay AI subagent model routing (default: ${routing.parentModelId}). ` + routing.models.map((model) => `${model.displayName}: ${model.id}`).join("; ") : `Relay AI subagent model routing (default: ${routing.parentModelId}). Other explicit model values must be exact ids from the current session catalog.`;
|
|
4539
|
+
return {
|
|
4540
|
+
...tool4,
|
|
4541
|
+
description: [tool4.description?.trim(), guidance].filter(Boolean).join("\n\n"),
|
|
4542
|
+
input_schema: {
|
|
4543
|
+
...inputSchema,
|
|
4544
|
+
properties: {
|
|
4545
|
+
...properties,
|
|
4546
|
+
model: modelProperty
|
|
4547
|
+
}
|
|
4548
|
+
}
|
|
4549
|
+
};
|
|
4550
|
+
}
|
|
4551
|
+
|
|
4312
4552
|
// src/server/models.ts
|
|
4313
4553
|
var CREATED_AT_ISO = "2025-01-01T00:00:00Z";
|
|
4314
4554
|
var CREATED_AT_UNIX = 1735689600;
|
|
@@ -4362,6 +4602,52 @@ function exposedGatewayAliasId(model, opts) {
|
|
|
4362
4602
|
const exposed = opts?.maskGatewayIds ? maskGatewayModelId(alias) : alias;
|
|
4363
4603
|
return singleOneM ? `${stripOneMContextSuffix(exposed)}[1m]` : exposed;
|
|
4364
4604
|
}
|
|
4605
|
+
function gatewayModelIdentity(model, models, opts) {
|
|
4606
|
+
const collisions = openAiIdCollisions(models);
|
|
4607
|
+
const ids = [];
|
|
4608
|
+
const modelIndex = models.indexOf(model);
|
|
4609
|
+
const firstBareIndex = models.findIndex((candidate) => candidate.id === model.id);
|
|
4610
|
+
if (modelIndex === firstBareIndex || modelIndex < 0) ids.push(model.id);
|
|
4611
|
+
const scopedId = openAiExposedId(model, collisions);
|
|
4612
|
+
if (scopedId !== model.id) ids.push(scopedId);
|
|
4613
|
+
const publicId = exposedGatewayAliasId(model, opts);
|
|
4614
|
+
if (publicId !== model.id) ids.push(publicId);
|
|
4615
|
+
const singleOneM = usesSingleOneMEntry(model, opts);
|
|
4616
|
+
if (singleOneM) {
|
|
4617
|
+
const bareModel = { ...model, id: stripOneMContextSuffix(model.id) };
|
|
4618
|
+
const rawBareAlias = gatewayAliasId(bareModel);
|
|
4619
|
+
const exposedBareAlias = opts?.maskGatewayIds ? maskGatewayModelId(rawBareAlias) : rawBareAlias;
|
|
4620
|
+
ids.push(
|
|
4621
|
+
stripOneMContextSuffix(model.id),
|
|
4622
|
+
rawBareAlias,
|
|
4623
|
+
`${rawBareAlias}[1m]`,
|
|
4624
|
+
exposedBareAlias,
|
|
4625
|
+
`${exposedBareAlias}[1m]`
|
|
4626
|
+
);
|
|
4627
|
+
}
|
|
4628
|
+
if (opts?.maskGatewayIds) {
|
|
4629
|
+
const rawAlias = gatewayAliasId(singleOneM ? { ...model, id: stripOneMContextSuffix(model.id) } : model);
|
|
4630
|
+
if (rawAlias !== publicId) ids.push(rawAlias);
|
|
4631
|
+
}
|
|
4632
|
+
return {
|
|
4633
|
+
publicId,
|
|
4634
|
+
compatibilityIds: [...new Set(ids)]
|
|
4635
|
+
};
|
|
4636
|
+
}
|
|
4637
|
+
function buildServerSubagentModelRouting(models, parentModel, opts) {
|
|
4638
|
+
const identities = models.map((model) => gatewayModelIdentity(model, models, opts));
|
|
4639
|
+
const parentIndex = models.indexOf(parentModel);
|
|
4640
|
+
const parentModelId = parentIndex >= 0 ? identities[parentIndex].publicId : exposedGatewayAliasId(parentModel, opts);
|
|
4641
|
+
return {
|
|
4642
|
+
parentModelId,
|
|
4643
|
+
models: models.map((model, index) => ({
|
|
4644
|
+
id: identities[index].publicId,
|
|
4645
|
+
compatibilityIds: identities[index].compatibilityIds,
|
|
4646
|
+
displayName: gatewayDisplayName(model, opts),
|
|
4647
|
+
family: model.modelFormat === "anthropic" ? claudeModelFamily(model.upstreamModelId ?? model.id) : void 0
|
|
4648
|
+
}))
|
|
4649
|
+
};
|
|
4650
|
+
}
|
|
4365
4651
|
function gatewayDisplayName(model, opts) {
|
|
4366
4652
|
const name = opts?.maskGatewayIds ? `${model.name} (${gatewayProviderLabel(model)})` : model.name;
|
|
4367
4653
|
return usesSingleOneMEntry(model, opts) && !/\b1m$/i.test(name) ? `${name} 1M` : name;
|
|
@@ -4381,31 +4667,10 @@ function usesSingleOneMEntry(model, opts) {
|
|
|
4381
4667
|
}
|
|
4382
4668
|
function createGatewayModelCatalog(models, opts) {
|
|
4383
4669
|
const byId = /* @__PURE__ */ new Map();
|
|
4384
|
-
const collisions = openAiIdCollisions(models);
|
|
4385
4670
|
for (const model of models) {
|
|
4386
|
-
|
|
4387
|
-
const
|
|
4388
|
-
|
|
4389
|
-
const alias = exposedGatewayAliasId(model, opts);
|
|
4390
|
-
if (alias !== model.id) byId.set(alias, model);
|
|
4391
|
-
const singleOneM = usesSingleOneMEntry(model, opts);
|
|
4392
|
-
if (singleOneM) {
|
|
4393
|
-
const bareModel = { ...model, id: stripOneMContextSuffix(model.id) };
|
|
4394
|
-
const rawBareAlias = gatewayAliasId(bareModel);
|
|
4395
|
-
const exposedBareAlias = opts?.maskGatewayIds ? maskGatewayModelId(rawBareAlias) : rawBareAlias;
|
|
4396
|
-
for (const compatibleId of [
|
|
4397
|
-
stripOneMContextSuffix(model.id),
|
|
4398
|
-
rawBareAlias,
|
|
4399
|
-
`${rawBareAlias}[1m]`,
|
|
4400
|
-
exposedBareAlias,
|
|
4401
|
-
`${exposedBareAlias}[1m]`
|
|
4402
|
-
]) {
|
|
4403
|
-
byId.set(compatibleId, model);
|
|
4404
|
-
}
|
|
4405
|
-
}
|
|
4406
|
-
if (opts?.maskGatewayIds) {
|
|
4407
|
-
const rawAlias = gatewayAliasId(singleOneM ? { ...model, id: stripOneMContextSuffix(model.id) } : model);
|
|
4408
|
-
if (rawAlias !== alias) byId.set(rawAlias, model);
|
|
4671
|
+
const identity = gatewayModelIdentity(model, models, opts);
|
|
4672
|
+
for (const compatibleId of identity.compatibilityIds) {
|
|
4673
|
+
byId.set(compatibleId, model);
|
|
4409
4674
|
}
|
|
4410
4675
|
}
|
|
4411
4676
|
return {
|
|
@@ -4605,10 +4870,10 @@ async function relayAnthropicMessages(res, messagesUrl, body, apiKey, clientWant
|
|
|
4605
4870
|
}
|
|
4606
4871
|
|
|
4607
4872
|
// src/antigravity/anthropic-to-cloudcode.ts
|
|
4608
|
-
import { randomUUID as
|
|
4873
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
4609
4874
|
|
|
4610
4875
|
// src/antigravity/request-adapter.ts
|
|
4611
|
-
import { randomUUID as
|
|
4876
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
4612
4877
|
import { tool, jsonSchema } from "ai";
|
|
4613
4878
|
|
|
4614
4879
|
// src/proxy-shared.ts
|
|
@@ -4718,6 +4983,83 @@ function serializeToolResultContent(content) {
|
|
|
4718
4983
|
}
|
|
4719
4984
|
|
|
4720
4985
|
// src/antigravity/request-adapter.ts
|
|
4986
|
+
var UNSUPPORTED_VOICE_MESSAGE = "Voice transcription isn\u2019t supported by Relay AI yet. Please type your message. Your coding session remains active.";
|
|
4987
|
+
var OMITTED_VOICE_TEXT = "[Voice recording omitted because transcription is not supported by Relay AI.]";
|
|
4988
|
+
function isSupportedImage(part) {
|
|
4989
|
+
return part.inlineData?.mimeType.toLowerCase().startsWith("image/") ?? false;
|
|
4990
|
+
}
|
|
4991
|
+
function isUnsupportedInlineData(part) {
|
|
4992
|
+
return !!part.inlineData && !isSupportedImage(part);
|
|
4993
|
+
}
|
|
4994
|
+
function sanitizeUnsupportedInlineData(ccReq) {
|
|
4995
|
+
const contents = ccReq.request?.contents ?? [];
|
|
4996
|
+
let latestUserIndex = -1;
|
|
4997
|
+
for (let i = contents.length - 1; i >= 0; i--) {
|
|
4998
|
+
if (contents[i].role === "user") {
|
|
4999
|
+
latestUserIndex = i;
|
|
5000
|
+
break;
|
|
5001
|
+
}
|
|
5002
|
+
}
|
|
5003
|
+
let latestUserTurnHasUnsupportedMedia = false;
|
|
5004
|
+
const sanitizedContents = contents.map((message, index) => ({
|
|
5005
|
+
...message,
|
|
5006
|
+
parts: message.parts.map((part) => {
|
|
5007
|
+
if (!isUnsupportedInlineData(part)) return part;
|
|
5008
|
+
if (index === latestUserIndex) latestUserTurnHasUnsupportedMedia = true;
|
|
5009
|
+
return { text: OMITTED_VOICE_TEXT };
|
|
5010
|
+
})
|
|
5011
|
+
}));
|
|
5012
|
+
return {
|
|
5013
|
+
request: {
|
|
5014
|
+
...ccReq,
|
|
5015
|
+
request: {
|
|
5016
|
+
...ccReq.request,
|
|
5017
|
+
contents: sanitizedContents
|
|
5018
|
+
}
|
|
5019
|
+
},
|
|
5020
|
+
latestUserTurnHasUnsupportedMedia
|
|
5021
|
+
};
|
|
5022
|
+
}
|
|
5023
|
+
function tracePartChars(part) {
|
|
5024
|
+
if (typeof part.text === "string") return part.text.length;
|
|
5025
|
+
if (part.type !== "tool-result") return void 0;
|
|
5026
|
+
const output = part.output;
|
|
5027
|
+
if (typeof output === "string") return output.length;
|
|
5028
|
+
if (output && typeof output === "object" && typeof output.value === "string") {
|
|
5029
|
+
return output.value.length;
|
|
5030
|
+
}
|
|
5031
|
+
try {
|
|
5032
|
+
return output === void 0 ? void 0 : JSON.stringify(output).length;
|
|
5033
|
+
} catch {
|
|
5034
|
+
return void 0;
|
|
5035
|
+
}
|
|
5036
|
+
}
|
|
5037
|
+
function summarizeSdkRequestForTrace(request) {
|
|
5038
|
+
const messages = request.messages.map((message) => {
|
|
5039
|
+
const content = message.content;
|
|
5040
|
+
if (typeof content === "string") {
|
|
5041
|
+
return { role: message.role, parts: [{ type: "text", chars: content.length }] };
|
|
5042
|
+
}
|
|
5043
|
+
const parts = Array.isArray(content) ? content.map((rawPart) => {
|
|
5044
|
+
const part = rawPart;
|
|
5045
|
+
const summary = {
|
|
5046
|
+
type: typeof part.type === "string" ? part.type : typeof rawPart
|
|
5047
|
+
};
|
|
5048
|
+
const chars = tracePartChars(part);
|
|
5049
|
+
if (chars !== void 0) summary.chars = chars;
|
|
5050
|
+
if (typeof part.toolName === "string") summary.toolName = part.toolName;
|
|
5051
|
+
if (typeof part.toolCallId === "string") summary.toolCallId = part.toolCallId;
|
|
5052
|
+
return summary;
|
|
5053
|
+
}) : [{ type: typeof content }];
|
|
5054
|
+
return { role: message.role, parts };
|
|
5055
|
+
});
|
|
5056
|
+
return {
|
|
5057
|
+
systemChars: request.system?.length ?? 0,
|
|
5058
|
+
messages,
|
|
5059
|
+
toolNames: Object.keys(request.tools ?? {}),
|
|
5060
|
+
...request.toolChoice ? { toolChoice: request.toolChoice } : {}
|
|
5061
|
+
};
|
|
5062
|
+
}
|
|
4721
5063
|
var JSON_SCHEMA_TYPES = /* @__PURE__ */ new Map([
|
|
4722
5064
|
["ARRAY", "array"],
|
|
4723
5065
|
["BOOLEAN", "boolean"],
|
|
@@ -4837,13 +5179,17 @@ function translateRequest(ccReq, options = {}) {
|
|
|
4837
5179
|
}
|
|
4838
5180
|
}
|
|
4839
5181
|
} else if (part.inlineData) {
|
|
4840
|
-
|
|
4841
|
-
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
|
|
5182
|
+
if (isSupportedImage(part)) {
|
|
5183
|
+
contentParts.push({
|
|
5184
|
+
type: "image",
|
|
5185
|
+
image: part.inlineData.data,
|
|
5186
|
+
mimeType: part.inlineData.mimeType
|
|
5187
|
+
});
|
|
5188
|
+
} else {
|
|
5189
|
+
contentParts.push({ type: "text", text: OMITTED_VOICE_TEXT });
|
|
5190
|
+
}
|
|
4845
5191
|
} else if (part.functionCall) {
|
|
4846
|
-
const id = "call_" +
|
|
5192
|
+
const id = "call_" + randomUUID3().replace(/-/g, "");
|
|
4847
5193
|
const name = part.functionCall.name;
|
|
4848
5194
|
if (!nameToIdList.has(name)) nameToIdList.set(name, []);
|
|
4849
5195
|
nameToIdList.get(name).push(id);
|
|
@@ -4856,7 +5202,7 @@ function translateRequest(ccReq, options = {}) {
|
|
|
4856
5202
|
} else if (part.functionResponse) {
|
|
4857
5203
|
const name = part.functionResponse.name;
|
|
4858
5204
|
const idList = nameToIdList.get(name) || [];
|
|
4859
|
-
const id = idList.shift() || "call_" +
|
|
5205
|
+
const id = idList.shift() || "call_" + randomUUID3().replace(/-/g, "");
|
|
4860
5206
|
toolResults.push({
|
|
4861
5207
|
type: "tool-result",
|
|
4862
5208
|
toolCallId: id,
|
|
@@ -5065,7 +5411,7 @@ function anthropicToCloudCode(body, realModelId, projectId) {
|
|
|
5065
5411
|
}
|
|
5066
5412
|
return {
|
|
5067
5413
|
project: projectId,
|
|
5068
|
-
requestId:
|
|
5414
|
+
requestId: randomUUID4(),
|
|
5069
5415
|
model: realModelId,
|
|
5070
5416
|
userAgent: ANTIGRAVITY_USER_AGENT2,
|
|
5071
5417
|
requestType: "agent",
|
|
@@ -5075,7 +5421,7 @@ function anthropicToCloudCode(body, realModelId, projectId) {
|
|
|
5075
5421
|
}
|
|
5076
5422
|
|
|
5077
5423
|
// src/antigravity/cloudcode-to-anthropic.ts
|
|
5078
|
-
import { randomUUID as
|
|
5424
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
5079
5425
|
function writeEvent(res, event, data) {
|
|
5080
5426
|
res.write(`event: ${event}
|
|
5081
5427
|
data: ${JSON.stringify(data)}
|
|
@@ -5165,7 +5511,7 @@ function closeBlock(res, state) {
|
|
|
5165
5511
|
}
|
|
5166
5512
|
async function streamCloudCodeToAnthropic(res, upstreamRes, model, log7) {
|
|
5167
5513
|
const state = {
|
|
5168
|
-
messageId: `msg_${
|
|
5514
|
+
messageId: `msg_${randomUUID5().replace(/-/g, "").slice(0, 24)}`,
|
|
5169
5515
|
model,
|
|
5170
5516
|
blockIdx: 0,
|
|
5171
5517
|
textBlockOpen: false,
|
|
@@ -5254,7 +5600,7 @@ async function streamCloudCodeToAnthropic(res, upstreamRes, model, log7) {
|
|
|
5254
5600
|
closeBlock(res, state);
|
|
5255
5601
|
}
|
|
5256
5602
|
for (const tc of state.toolCalls) {
|
|
5257
|
-
const rawToolId = `toolu_${
|
|
5603
|
+
const rawToolId = `toolu_${randomUUID5().replace(/-/g, "").slice(0, 16)}`;
|
|
5258
5604
|
const toolId = encodeToolUseId(rawToolId, tc.signature);
|
|
5259
5605
|
writeEvent(res, "content_block_start", {
|
|
5260
5606
|
type: "content_block_start",
|
|
@@ -5305,7 +5651,7 @@ async function streamCloudCodeToAnthropic(res, upstreamRes, model, log7) {
|
|
|
5305
5651
|
}
|
|
5306
5652
|
async function collectCloudCodeToAnthropic(upstreamRes, model, log7) {
|
|
5307
5653
|
const text4 = await upstreamRes.text();
|
|
5308
|
-
const messageId = `msg_${
|
|
5654
|
+
const messageId = `msg_${randomUUID5().replace(/-/g, "").slice(0, 24)}`;
|
|
5309
5655
|
const content = [];
|
|
5310
5656
|
let stopReason = "end_turn";
|
|
5311
5657
|
let inputTokens = 0;
|
|
@@ -5334,7 +5680,7 @@ async function collectCloudCodeToAnthropic(upstreamRes, model, log7) {
|
|
|
5334
5680
|
else content.push({ type: "text", text: part.text });
|
|
5335
5681
|
} else if (part.functionCall && typeof part.functionCall === "object") {
|
|
5336
5682
|
const fc = part.functionCall;
|
|
5337
|
-
const rawToolId = `toolu_${
|
|
5683
|
+
const rawToolId = `toolu_${randomUUID5().replace(/-/g, "").slice(0, 16)}`;
|
|
5338
5684
|
content.push({
|
|
5339
5685
|
type: "tool_use",
|
|
5340
5686
|
id: encodeToolUseId(rawToolId, signature ?? pendingThoughtSignature),
|
|
@@ -5367,7 +5713,7 @@ async function collectCloudCodeToAnthropic(upstreamRes, model, log7) {
|
|
|
5367
5713
|
}
|
|
5368
5714
|
|
|
5369
5715
|
// src/proxy.ts
|
|
5370
|
-
import { randomUUID as
|
|
5716
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
5371
5717
|
|
|
5372
5718
|
// src/sdk-adapter.ts
|
|
5373
5719
|
import { streamText, generateText, tool as tool2, jsonSchema as jsonSchema2 } from "ai";
|
|
@@ -5430,6 +5776,48 @@ function resolveUpstreamTools(tools, messages) {
|
|
|
5430
5776
|
}
|
|
5431
5777
|
|
|
5432
5778
|
// src/codex/upstream-error.ts
|
|
5779
|
+
var TRACE_FIELD_LIMIT = 4e3;
|
|
5780
|
+
function clipTraceField(value) {
|
|
5781
|
+
return value.length <= TRACE_FIELD_LIMIT ? value : `${value.slice(0, TRACE_FIELD_LIMIT)}\u2026`;
|
|
5782
|
+
}
|
|
5783
|
+
function safeUpstreamErrorFields(err, includeCause) {
|
|
5784
|
+
if (!err || typeof err !== "object") {
|
|
5785
|
+
return { message: clipTraceField(String(err)) };
|
|
5786
|
+
}
|
|
5787
|
+
const rec = err;
|
|
5788
|
+
const result = {};
|
|
5789
|
+
if (rec.name) result.name = rec.name;
|
|
5790
|
+
if (rec.message) result.message = clipTraceField(rec.message);
|
|
5791
|
+
if (rec.statusCode !== void 0) result.statusCode = rec.statusCode;
|
|
5792
|
+
if (rec.responseBody) result.responseBody = clipTraceField(rec.responseBody);
|
|
5793
|
+
if (rec.data?.error) {
|
|
5794
|
+
result.data = {
|
|
5795
|
+
error: {
|
|
5796
|
+
...rec.data.error.type ? { type: rec.data.error.type } : {},
|
|
5797
|
+
...rec.data.error.message ? { message: clipTraceField(rec.data.error.message) } : {}
|
|
5798
|
+
}
|
|
5799
|
+
};
|
|
5800
|
+
}
|
|
5801
|
+
if (rec.lastError) {
|
|
5802
|
+
result.lastError = {
|
|
5803
|
+
...rec.lastError.message ? { message: clipTraceField(rec.lastError.message) } : {},
|
|
5804
|
+
...rec.lastError.statusCode !== void 0 ? { statusCode: rec.lastError.statusCode } : {}
|
|
5805
|
+
};
|
|
5806
|
+
}
|
|
5807
|
+
if (rec.errors?.length) {
|
|
5808
|
+
result.errors = rec.errors.map((item) => ({
|
|
5809
|
+
...item.message ? { message: clipTraceField(item.message) } : {},
|
|
5810
|
+
...item.statusCode !== void 0 ? { statusCode: item.statusCode } : {}
|
|
5811
|
+
}));
|
|
5812
|
+
}
|
|
5813
|
+
if (includeCause && rec.cause !== void 0) {
|
|
5814
|
+
result.cause = safeUpstreamErrorFields(rec.cause, false);
|
|
5815
|
+
}
|
|
5816
|
+
return result;
|
|
5817
|
+
}
|
|
5818
|
+
function formatUpstreamErrorTrace(err) {
|
|
5819
|
+
return JSON.stringify(safeUpstreamErrorFields(err, true));
|
|
5820
|
+
}
|
|
5433
5821
|
function formatUpstreamError(err) {
|
|
5434
5822
|
if (!err || typeof err !== "object") return "Upstream model request failed.";
|
|
5435
5823
|
const rec = err;
|
|
@@ -5658,6 +6046,17 @@ function translateRequest2(body, npm, options) {
|
|
|
5658
6046
|
if (options?.maxTools !== void 0 && upstreamTools.length > options.maxTools) {
|
|
5659
6047
|
upstreamTools = upstreamTools.slice(0, options.maxTools);
|
|
5660
6048
|
}
|
|
6049
|
+
let responseSubagentRouting;
|
|
6050
|
+
if (options?.subagentRouting) {
|
|
6051
|
+
const agentIndex = upstreamTools.findIndex((toolDefinition) => isClaudeAgentTool(toolDefinition));
|
|
6052
|
+
if (agentIndex >= 0) {
|
|
6053
|
+
upstreamTools = upstreamTools.map((toolDefinition, index) => index === agentIndex ? augmentClaudeAgentTool(
|
|
6054
|
+
toolDefinition,
|
|
6055
|
+
options.subagentRouting
|
|
6056
|
+
) : toolDefinition);
|
|
6057
|
+
responseSubagentRouting = options.subagentRouting;
|
|
6058
|
+
}
|
|
6059
|
+
}
|
|
5661
6060
|
const effort = anthropicEffortFromRequest(body) ?? options?.defaultEffort;
|
|
5662
6061
|
let providerOptions = deepMergeProviderOptions(
|
|
5663
6062
|
thinkingProviderOptions(npm),
|
|
@@ -5675,16 +6074,22 @@ function translateRequest2(body, npm, options) {
|
|
|
5675
6074
|
toolChoice: translateToolChoice(body.tool_choice),
|
|
5676
6075
|
maxOutputTokens: options?.openAiOAuth ? void 0 : body.max_tokens,
|
|
5677
6076
|
temperature: body.temperature,
|
|
5678
|
-
providerOptions
|
|
6077
|
+
providerOptions,
|
|
6078
|
+
subagentRouting: responseSubagentRouting
|
|
5679
6079
|
};
|
|
5680
6080
|
}
|
|
5681
|
-
|
|
6081
|
+
function logSubagentDecision(log7, decision) {
|
|
6082
|
+
if (!log7 || decision.kind === "fork" || decision.kind === "explicit") return;
|
|
6083
|
+
log7(() => decision.kind === "family-fallback" ? `sdk Agent model "${decision.requestedModelId}" unavailable; using parent ${decision.resolvedModelId}` : `sdk Agent model ${decision.kind}: ${"requestedModelId" in decision ? `${decision.requestedModelId} -> ` : ""}${decision.resolvedModelId}`);
|
|
6084
|
+
}
|
|
6085
|
+
async function writeAnthropicStream(fullStream, modelId, write, log7, estimatedInputTokens = 0, subagentRouting) {
|
|
5682
6086
|
const messageId = "msg_" + Date.now();
|
|
5683
6087
|
let blockIndex = -1;
|
|
5684
6088
|
let started = false;
|
|
5685
6089
|
let openType = null;
|
|
5686
6090
|
let pendingThinkingSig;
|
|
5687
6091
|
const idToBlock = /* @__PURE__ */ new Map();
|
|
6092
|
+
const bufferedAgentCalls = /* @__PURE__ */ new Map();
|
|
5688
6093
|
let finishReason = "end_turn";
|
|
5689
6094
|
let usage = { input_tokens: estimatedInputTokens, output_tokens: 0 };
|
|
5690
6095
|
const emit = (event, data) => write(sseChunk(event, data));
|
|
@@ -5767,9 +6172,13 @@ async function writeAnthropicStream(fullStream, modelId, write, log7, estimatedI
|
|
|
5767
6172
|
input: {}
|
|
5768
6173
|
});
|
|
5769
6174
|
idToBlock.set(part.id ?? "", blockIndex);
|
|
6175
|
+
if (subagentRouting && part.toolName === "Agent") {
|
|
6176
|
+
bufferedAgentCalls.set(part.id ?? "", { blockIndex });
|
|
6177
|
+
}
|
|
5770
6178
|
break;
|
|
5771
6179
|
}
|
|
5772
6180
|
case "tool-input-delta":
|
|
6181
|
+
if (bufferedAgentCalls.has(part.id ?? "")) break;
|
|
5773
6182
|
emit("content_block_delta", {
|
|
5774
6183
|
type: "content_block_delta",
|
|
5775
6184
|
index: idToBlock.get(part.id ?? "") ?? blockIndex,
|
|
@@ -5780,11 +6189,53 @@ async function writeAnthropicStream(fullStream, modelId, write, log7, estimatedI
|
|
|
5780
6189
|
break;
|
|
5781
6190
|
case "tool-call": {
|
|
5782
6191
|
finishReason = "tool_use";
|
|
5783
|
-
|
|
6192
|
+
const toolCallId = part.toolCallId ?? "";
|
|
6193
|
+
if (subagentRouting && part.toolName === "Agent") {
|
|
6194
|
+
try {
|
|
6195
|
+
const normalized = prepareClaudeAgentInput(part.input, subagentRouting);
|
|
6196
|
+
logSubagentDecision(log7, normalized.decision);
|
|
6197
|
+
const buffered = bufferedAgentCalls.get(toolCallId);
|
|
6198
|
+
if (!buffered && !idToBlock.has(toolCallId)) {
|
|
6199
|
+
const sig = grabRoundTripSignature(part);
|
|
6200
|
+
openBlock("tool", {
|
|
6201
|
+
type: "tool_use",
|
|
6202
|
+
id: encodeToolUseId(toolCallId, sig),
|
|
6203
|
+
name: part.toolName,
|
|
6204
|
+
input: {}
|
|
6205
|
+
});
|
|
6206
|
+
idToBlock.set(toolCallId, blockIndex);
|
|
6207
|
+
}
|
|
6208
|
+
emit("content_block_delta", {
|
|
6209
|
+
type: "content_block_delta",
|
|
6210
|
+
index: buffered?.blockIndex ?? idToBlock.get(toolCallId) ?? blockIndex,
|
|
6211
|
+
delta: {
|
|
6212
|
+
type: "input_json_delta",
|
|
6213
|
+
partial_json: JSON.stringify(stripNullInputs(normalized.input))
|
|
6214
|
+
}
|
|
6215
|
+
});
|
|
6216
|
+
bufferedAgentCalls.delete(toolCallId);
|
|
6217
|
+
} catch (error) {
|
|
6218
|
+
if (error instanceof UnavailableSubagentModelError) {
|
|
6219
|
+
bufferedAgentCalls.delete(toolCallId);
|
|
6220
|
+
closeOpen();
|
|
6221
|
+
emit("error", {
|
|
6222
|
+
type: "error",
|
|
6223
|
+
error: {
|
|
6224
|
+
type: anthropicErrorType(error.statusCode),
|
|
6225
|
+
message: error.message
|
|
6226
|
+
}
|
|
6227
|
+
});
|
|
6228
|
+
return;
|
|
6229
|
+
}
|
|
6230
|
+
throw error;
|
|
6231
|
+
}
|
|
6232
|
+
break;
|
|
6233
|
+
}
|
|
6234
|
+
if (!idToBlock.has(toolCallId) && openType !== "tool") {
|
|
5784
6235
|
const sig = grabRoundTripSignature(part);
|
|
5785
6236
|
openBlock("tool", {
|
|
5786
6237
|
type: "tool_use",
|
|
5787
|
-
id: encodeToolUseId(
|
|
6238
|
+
id: encodeToolUseId(toolCallId, sig),
|
|
5788
6239
|
name: part.toolName,
|
|
5789
6240
|
input: {}
|
|
5790
6241
|
});
|
|
@@ -5812,6 +6263,7 @@ async function writeAnthropicStream(fullStream, modelId, write, log7, estimatedI
|
|
|
5812
6263
|
const errMsg = e?.message || (typeof part.error === "string" ? part.error : JSON.stringify(e?.data ?? part.error));
|
|
5813
6264
|
const errorType = anthropicErrorType(upstreamHttpStatus(part.error, errMsg));
|
|
5814
6265
|
log7?.(() => `sdk stream error (${errorType}): ${errMsg}`);
|
|
6266
|
+
bufferedAgentCalls.clear();
|
|
5815
6267
|
closeOpen();
|
|
5816
6268
|
emit("error", { type: "error", error: { type: errorType, message: errMsg } });
|
|
5817
6269
|
return;
|
|
@@ -5826,7 +6278,8 @@ async function writeAnthropicStream(fullStream, modelId, write, log7, estimatedI
|
|
|
5826
6278
|
emit("message_stop", { type: "message_stop" });
|
|
5827
6279
|
}
|
|
5828
6280
|
async function streamAnthropicResponse(model, params, modelId, write, log7, estimatedInputTokens = 0) {
|
|
5829
|
-
const
|
|
6281
|
+
const { subagentRouting, ...providerParams } = params;
|
|
6282
|
+
const result = streamText({ model, ...providerParams, onError: () => {
|
|
5830
6283
|
} });
|
|
5831
6284
|
Promise.resolve(result.text).catch(() => {
|
|
5832
6285
|
});
|
|
@@ -5843,22 +6296,24 @@ async function streamAnthropicResponse(model, params, modelId, write, log7, esti
|
|
|
5843
6296
|
modelId,
|
|
5844
6297
|
write,
|
|
5845
6298
|
log7,
|
|
5846
|
-
estimatedInputTokens
|
|
6299
|
+
estimatedInputTokens,
|
|
6300
|
+
subagentRouting
|
|
5847
6301
|
);
|
|
5848
6302
|
}
|
|
5849
6303
|
async function generateAnthropicResponse(model, params, modelId, options) {
|
|
6304
|
+
const { subagentRouting, ...providerParams } = params;
|
|
5850
6305
|
let text4;
|
|
5851
6306
|
let toolCalls;
|
|
5852
6307
|
let finishReason;
|
|
5853
6308
|
let usage;
|
|
5854
6309
|
if (options?.forceStream) {
|
|
5855
|
-
const r = streamText({ model, ...
|
|
6310
|
+
const r = streamText({ model, ...providerParams, onError: () => {
|
|
5856
6311
|
} });
|
|
5857
6312
|
Promise.resolve(r.toolResults).catch(() => {
|
|
5858
6313
|
});
|
|
5859
6314
|
[text4, toolCalls, finishReason, usage] = await Promise.all([r.text, r.toolCalls, r.finishReason, r.usage]);
|
|
5860
6315
|
} else {
|
|
5861
|
-
const r = await generateText({ model, ...
|
|
6316
|
+
const r = await generateText({ model, ...providerParams });
|
|
5862
6317
|
({ text: text4, toolCalls, finishReason, usage } = r);
|
|
5863
6318
|
}
|
|
5864
6319
|
return {
|
|
@@ -5868,12 +6323,20 @@ async function generateAnthropicResponse(model, params, modelId, options) {
|
|
|
5868
6323
|
model: modelId,
|
|
5869
6324
|
content: [
|
|
5870
6325
|
...text4 ? [{ type: "text", text: text4 }] : [],
|
|
5871
|
-
...toolCalls.map((tc) =>
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
|
|
5875
|
-
|
|
5876
|
-
|
|
6326
|
+
...toolCalls.map((tc) => {
|
|
6327
|
+
let input = tc.input;
|
|
6328
|
+
if (subagentRouting && tc.toolName === "Agent") {
|
|
6329
|
+
const normalized = prepareClaudeAgentInput(tc.input, subagentRouting);
|
|
6330
|
+
logSubagentDecision(options?.log, normalized.decision);
|
|
6331
|
+
input = normalized.input;
|
|
6332
|
+
}
|
|
6333
|
+
return {
|
|
6334
|
+
type: "tool_use",
|
|
6335
|
+
id: encodeToolUseId(tc.toolCallId, grabRoundTripSignature(tc)),
|
|
6336
|
+
name: tc.toolName,
|
|
6337
|
+
input: stripNullInputs(input)
|
|
6338
|
+
};
|
|
6339
|
+
})
|
|
5877
6340
|
],
|
|
5878
6341
|
stop_reason: finishReason === "tool-calls" ? "tool_use" : "end_turn",
|
|
5879
6342
|
usage: { input_tokens: usage?.inputTokens ?? 0, output_tokens: usage?.outputTokens ?? 0 }
|
|
@@ -5920,6 +6383,21 @@ function aliasModelId(realId, providerId) {
|
|
|
5920
6383
|
const sanitized = providerId.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
5921
6384
|
return `anthropic-${sanitized}__${realId}`;
|
|
5922
6385
|
}
|
|
6386
|
+
function buildProxySubagentModelRouting(routes, parentRoute) {
|
|
6387
|
+
const publicId = (route) => route.gatewayAliasId ?? route.aliasId;
|
|
6388
|
+
return {
|
|
6389
|
+
parentModelId: publicId(parentRoute),
|
|
6390
|
+
models: routes.map((route) => ({
|
|
6391
|
+
id: publicId(route),
|
|
6392
|
+
compatibilityIds: [.../* @__PURE__ */ new Set([
|
|
6393
|
+
...routeLookupIds(route.aliasId),
|
|
6394
|
+
...route.gatewayAliasId ? routeLookupIds(route.gatewayAliasId) : []
|
|
6395
|
+
])],
|
|
6396
|
+
displayName: route.displayName,
|
|
6397
|
+
family: route.modelFormat === "anthropic" ? claudeModelFamily(route.realModelId) : void 0
|
|
6398
|
+
}))
|
|
6399
|
+
};
|
|
6400
|
+
}
|
|
5923
6401
|
function lookupRoute(byAlias, id) {
|
|
5924
6402
|
for (const key of routeLookupIds(id)) {
|
|
5925
6403
|
const route = byAlias.get(key);
|
|
@@ -5928,13 +6406,14 @@ function lookupRoute(byAlias, id) {
|
|
|
5928
6406
|
return void 0;
|
|
5929
6407
|
}
|
|
5930
6408
|
function startProxyCatalog(routes, defaultAliasId, debug = false) {
|
|
5931
|
-
const proxyToken =
|
|
6409
|
+
const proxyToken = randomUUID6();
|
|
5932
6410
|
silenceSdkWarnings();
|
|
5933
6411
|
if (routes.length === 0) {
|
|
5934
6412
|
return Promise.reject(new Error("Proxy catalog requires at least one route"));
|
|
5935
6413
|
}
|
|
5936
6414
|
const byAlias = new Map(routes.map((r) => [r.aliasId, r]));
|
|
5937
6415
|
const defaultRoute = byAlias.get(defaultAliasId) ?? routes[0];
|
|
6416
|
+
const subagentRouteRegistry = new SubagentRouteRegistry();
|
|
5938
6417
|
const plog = makeProxyLog(debug);
|
|
5939
6418
|
const onRejection = (reason) => {
|
|
5940
6419
|
plog(() => `Unhandled Rejection: ${reason instanceof Error ? reason.stack || reason.message : String(reason)}`);
|
|
@@ -5988,9 +6467,12 @@ function startProxyCatalog(routes, defaultAliasId, debug = false) {
|
|
|
5988
6467
|
anthropicError(res, 400, "Invalid JSON body");
|
|
5989
6468
|
return;
|
|
5990
6469
|
}
|
|
6470
|
+
const correlatedSubagent = subagentRouteRegistry.consume(req.headers, anthropicBody);
|
|
6471
|
+
if (correlatedSubagent) anthropicBody = correlatedSubagent.body;
|
|
5991
6472
|
const originalModel = anthropicBody.model;
|
|
5992
6473
|
const clientWantsStream = Boolean(anthropicBody.stream);
|
|
5993
|
-
const
|
|
6474
|
+
const correlatedRoute = correlatedSubagent ? routes.find((candidate) => (candidate.gatewayAliasId ?? candidate.aliasId) === correlatedSubagent.modelId) : void 0;
|
|
6475
|
+
const route = correlatedRoute ?? lookupRoute(byAlias, originalModel) ?? defaultRoute;
|
|
5994
6476
|
const apiKey = route.apiKey;
|
|
5995
6477
|
const upstreamUrl = route.upstreamUrl;
|
|
5996
6478
|
plog(
|
|
@@ -6046,10 +6528,16 @@ function startProxyCatalog(routes, defaultAliasId, debug = false) {
|
|
|
6046
6528
|
}
|
|
6047
6529
|
if (usesSdkAdapter) {
|
|
6048
6530
|
const openAiOAuth = route.npm === "@ai-sdk/openai" && route.authType === "oauth";
|
|
6531
|
+
const subagentRouting = buildProxySubagentModelRouting(routes, route);
|
|
6532
|
+
const sessionId = extractClaudeSessionId(req.headers, anthropicBody);
|
|
6533
|
+
if (sessionId) {
|
|
6534
|
+
subagentRouting.registerSubagentRoute = (modelId) => subagentRouteRegistry.register(sessionId, modelId);
|
|
6535
|
+
}
|
|
6049
6536
|
const params = translateRequest2(anthropicBody, route.npm, {
|
|
6050
6537
|
openAiOAuth,
|
|
6051
6538
|
maxTools: maxToolsForNpm(route.npm),
|
|
6052
6539
|
onDebug: (msg) => plog(() => msg),
|
|
6540
|
+
subagentRouting,
|
|
6053
6541
|
reasoningMetadata: {
|
|
6054
6542
|
providerId: route.providerId,
|
|
6055
6543
|
apiBaseUrl: route.baseURL,
|
|
@@ -6097,7 +6585,7 @@ function startProxyCatalog(routes, defaultAliasId, debug = false) {
|
|
|
6097
6585
|
model,
|
|
6098
6586
|
params,
|
|
6099
6587
|
originalModel,
|
|
6100
|
-
{ forceStream: openAiOAuth }
|
|
6588
|
+
{ forceStream: openAiOAuth, log: plog }
|
|
6101
6589
|
);
|
|
6102
6590
|
sendJson(res, 200, anthropicResponse);
|
|
6103
6591
|
}
|
|
@@ -7838,6 +8326,14 @@ function injectRelayModels(fixture, routes, templateKey) {
|
|
|
7838
8326
|
}]
|
|
7839
8327
|
}
|
|
7840
8328
|
];
|
|
8329
|
+
for (const entry of Object.values(result.models)) {
|
|
8330
|
+
const mimeTypes = entry.supportedMimeTypes;
|
|
8331
|
+
if (!mimeTypes || typeof mimeTypes !== "object" || Array.isArray(mimeTypes)) continue;
|
|
8332
|
+
entry.supportedMimeTypes = Object.fromEntries(
|
|
8333
|
+
Object.entries(mimeTypes).filter(([mime]) => !mime.toLowerCase().includes("audio/"))
|
|
8334
|
+
);
|
|
8335
|
+
}
|
|
8336
|
+
result.audioTranscriptionModelIds = [];
|
|
7841
8337
|
return result;
|
|
7842
8338
|
}
|
|
7843
8339
|
if (!result.agentModelSorts?.[0]?.groups?.[0]) {
|
|
@@ -9833,8 +10329,9 @@ async function startServer(options) {
|
|
|
9833
10329
|
silenceSdkWarnings();
|
|
9834
10330
|
const languageModelCache = /* @__PURE__ */ new Map();
|
|
9835
10331
|
const plog = makeServerLog(options.debugLogPath);
|
|
10332
|
+
const subagentRouteRegistry = new SubagentRouteRegistry();
|
|
9836
10333
|
const server = createServer2((req, res) => {
|
|
9837
|
-
void routeRequest(req, res, options, languageModelCache, plog);
|
|
10334
|
+
void routeRequest(req, res, options, languageModelCache, plog, subagentRouteRegistry);
|
|
9838
10335
|
});
|
|
9839
10336
|
await new Promise((resolve, reject) => {
|
|
9840
10337
|
server.once("error", reject);
|
|
@@ -9857,7 +10354,7 @@ async function startServer(options) {
|
|
|
9857
10354
|
})
|
|
9858
10355
|
};
|
|
9859
10356
|
}
|
|
9860
|
-
async function routeRequest(req, res, options, modelCache, plog) {
|
|
10357
|
+
async function routeRequest(req, res, options, modelCache, plog, subagentRouteRegistry) {
|
|
9861
10358
|
try {
|
|
9862
10359
|
const pathname = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`).pathname;
|
|
9863
10360
|
plog(`${req.method} ${pathname}`);
|
|
@@ -9882,7 +10379,7 @@ async function routeRequest(req, res, options, modelCache, plog) {
|
|
|
9882
10379
|
return;
|
|
9883
10380
|
}
|
|
9884
10381
|
if (req.method === "POST" && pathname === "/anthropic/v1/messages") {
|
|
9885
|
-
await handleAnthropicMessages(req, res, options, modelCache, plog);
|
|
10382
|
+
await handleAnthropicMessages(req, res, options, modelCache, plog, subagentRouteRegistry);
|
|
9886
10383
|
return;
|
|
9887
10384
|
}
|
|
9888
10385
|
if (req.method === "POST" && pathname === "/openai/v1/chat/completions") {
|
|
@@ -9894,13 +10391,16 @@ async function routeRequest(req, res, options, modelCache, plog) {
|
|
|
9894
10391
|
sendJson(res, 500, { error: { message: err instanceof Error ? err.message : String(err) } });
|
|
9895
10392
|
}
|
|
9896
10393
|
}
|
|
9897
|
-
async function handleAnthropicMessages(req, res, options, modelCache, plog) {
|
|
9898
|
-
|
|
10394
|
+
async function handleAnthropicMessages(req, res, options, modelCache, plog, subagentRouteRegistry) {
|
|
10395
|
+
let body = await readJson(req);
|
|
9899
10396
|
if (!body) {
|
|
9900
10397
|
sendJson(res, 400, { error: { message: "Invalid JSON body" } });
|
|
9901
10398
|
return;
|
|
9902
10399
|
}
|
|
9903
|
-
const
|
|
10400
|
+
const correlatedSubagent = subagentRouteRegistry.consume(req.headers, body);
|
|
10401
|
+
if (correlatedSubagent) body = correlatedSubagent.body;
|
|
10402
|
+
const requestedModelId = correlatedSubagent?.modelId ?? body.model;
|
|
10403
|
+
const model = lookupModel(res, options.catalog, requestedModelId);
|
|
9904
10404
|
if (!model) {
|
|
9905
10405
|
plog(`model not found: ${body.model}`);
|
|
9906
10406
|
return;
|
|
@@ -9959,10 +10459,20 @@ async function handleAnthropicMessages(req, res, options, modelCache, plog) {
|
|
|
9959
10459
|
if (npmMaxTools !== void 0 && toolCount > npmMaxTools) {
|
|
9960
10460
|
plog(`tools truncated: ${toolCount} \u2192 ${npmMaxTools} (provider limit)`);
|
|
9961
10461
|
}
|
|
10462
|
+
const subagentRouting = buildServerSubagentModelRouting(
|
|
10463
|
+
options.catalog.list(),
|
|
10464
|
+
model,
|
|
10465
|
+
options.gateway
|
|
10466
|
+
);
|
|
10467
|
+
const sessionId = extractClaudeSessionId(req.headers, body);
|
|
10468
|
+
if (sessionId) {
|
|
10469
|
+
subagentRouting.registerSubagentRoute = (modelId) => subagentRouteRegistry.register(sessionId, modelId);
|
|
10470
|
+
}
|
|
9962
10471
|
const params = translateRequest2(body, model.npm, {
|
|
9963
10472
|
defaultEffort: anthropicEffortFromRequest(body) ? void 0 : model.defaultEffort,
|
|
9964
10473
|
openAiOAuth: model.npm === "@ai-sdk/openai" && model.authType === "oauth",
|
|
9965
10474
|
onDebug: plog,
|
|
10475
|
+
subagentRouting,
|
|
9966
10476
|
reasoningMetadata: {
|
|
9967
10477
|
providerId: model.providerId,
|
|
9968
10478
|
apiBaseUrl: model.apiBaseUrl,
|
|
@@ -9988,12 +10498,17 @@ async function handleAnthropicMessages(req, res, options, modelCache, plog) {
|
|
|
9988
10498
|
params,
|
|
9989
10499
|
responseModelId,
|
|
9990
10500
|
(chunk) => res.write(chunk),
|
|
9991
|
-
|
|
10501
|
+
plog,
|
|
9992
10502
|
estimateAnthropicInputTokens(body)
|
|
9993
10503
|
);
|
|
9994
10504
|
res.end();
|
|
9995
10505
|
} else {
|
|
9996
|
-
const anthropicResponse = await generateAnthropicResponse(
|
|
10506
|
+
const anthropicResponse = await generateAnthropicResponse(
|
|
10507
|
+
languageModel,
|
|
10508
|
+
params,
|
|
10509
|
+
responseModelId,
|
|
10510
|
+
{ log: plog }
|
|
10511
|
+
);
|
|
9997
10512
|
sendJson(res, 200, anthropicResponse);
|
|
9998
10513
|
}
|
|
9999
10514
|
} catch (err) {
|
|
@@ -11973,6 +12488,7 @@ export {
|
|
|
11973
12488
|
getGeminiProxyDebugLogPath,
|
|
11974
12489
|
getUiDebugLogPath,
|
|
11975
12490
|
getServerDebugLogPath,
|
|
12491
|
+
getAntigravityDebugLogPath,
|
|
11976
12492
|
makeTraceLogger,
|
|
11977
12493
|
writeSecureLogLine,
|
|
11978
12494
|
printTraceLog,
|
|
@@ -12004,7 +12520,11 @@ export {
|
|
|
12004
12520
|
splitToolUseId,
|
|
12005
12521
|
encodeToolUseId,
|
|
12006
12522
|
serializeToolResultContent,
|
|
12523
|
+
UNSUPPORTED_VOICE_MESSAGE,
|
|
12524
|
+
sanitizeUnsupportedInlineData,
|
|
12525
|
+
summarizeSdkRequestForTrace,
|
|
12007
12526
|
translateRequest,
|
|
12527
|
+
formatUpstreamErrorTrace,
|
|
12008
12528
|
formatUpstreamError,
|
|
12009
12529
|
upstreamHttpStatus,
|
|
12010
12530
|
aliasModelId,
|
|
@@ -12071,4 +12591,4 @@ export {
|
|
|
12071
12591
|
supportsClaudeTransparentMode,
|
|
12072
12592
|
buildHttpProxyRoutes
|
|
12073
12593
|
};
|
|
12074
|
-
//# sourceMappingURL=chunk-
|
|
12594
|
+
//# sourceMappingURL=chunk-GHSURQOK.js.map
|