@jacobbd/relay-ai 0.7.5 → 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-HEMJFOXG.js → chunk-GHSURQOK.js} +496 -67
- package/dist/chunk-GHSURQOK.js.map +1 -0
- package/dist/cli.js +52 -3
- package/dist/cli.js.map +1 -1
- package/dist/core/index.js +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/{ui-command-26QQUWTQ.js → ui-command-27X4WJKC.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-HEMJFOXG.js.map +0 -1
- /package/dist/{ui-command-26QQUWTQ.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
|
},
|
|
@@ -4315,6 +4315,240 @@ function maskGatewayModelId(aliasId) {
|
|
|
4315
4315
|
return `anthropic-${reverseSegment(providerSlug)}__${reverseSegment(modelSuffix)}`;
|
|
4316
4316
|
}
|
|
4317
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
|
+
|
|
4318
4552
|
// src/server/models.ts
|
|
4319
4553
|
var CREATED_AT_ISO = "2025-01-01T00:00:00Z";
|
|
4320
4554
|
var CREATED_AT_UNIX = 1735689600;
|
|
@@ -4368,6 +4602,52 @@ function exposedGatewayAliasId(model, opts) {
|
|
|
4368
4602
|
const exposed = opts?.maskGatewayIds ? maskGatewayModelId(alias) : alias;
|
|
4369
4603
|
return singleOneM ? `${stripOneMContextSuffix(exposed)}[1m]` : exposed;
|
|
4370
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
|
+
}
|
|
4371
4651
|
function gatewayDisplayName(model, opts) {
|
|
4372
4652
|
const name = opts?.maskGatewayIds ? `${model.name} (${gatewayProviderLabel(model)})` : model.name;
|
|
4373
4653
|
return usesSingleOneMEntry(model, opts) && !/\b1m$/i.test(name) ? `${name} 1M` : name;
|
|
@@ -4387,31 +4667,10 @@ function usesSingleOneMEntry(model, opts) {
|
|
|
4387
4667
|
}
|
|
4388
4668
|
function createGatewayModelCatalog(models, opts) {
|
|
4389
4669
|
const byId = /* @__PURE__ */ new Map();
|
|
4390
|
-
const collisions = openAiIdCollisions(models);
|
|
4391
4670
|
for (const model of models) {
|
|
4392
|
-
|
|
4393
|
-
const
|
|
4394
|
-
|
|
4395
|
-
const alias = exposedGatewayAliasId(model, opts);
|
|
4396
|
-
if (alias !== model.id) byId.set(alias, model);
|
|
4397
|
-
const singleOneM = usesSingleOneMEntry(model, opts);
|
|
4398
|
-
if (singleOneM) {
|
|
4399
|
-
const bareModel = { ...model, id: stripOneMContextSuffix(model.id) };
|
|
4400
|
-
const rawBareAlias = gatewayAliasId(bareModel);
|
|
4401
|
-
const exposedBareAlias = opts?.maskGatewayIds ? maskGatewayModelId(rawBareAlias) : rawBareAlias;
|
|
4402
|
-
for (const compatibleId of [
|
|
4403
|
-
stripOneMContextSuffix(model.id),
|
|
4404
|
-
rawBareAlias,
|
|
4405
|
-
`${rawBareAlias}[1m]`,
|
|
4406
|
-
exposedBareAlias,
|
|
4407
|
-
`${exposedBareAlias}[1m]`
|
|
4408
|
-
]) {
|
|
4409
|
-
byId.set(compatibleId, model);
|
|
4410
|
-
}
|
|
4411
|
-
}
|
|
4412
|
-
if (opts?.maskGatewayIds) {
|
|
4413
|
-
const rawAlias = gatewayAliasId(singleOneM ? { ...model, id: stripOneMContextSuffix(model.id) } : model);
|
|
4414
|
-
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);
|
|
4415
4674
|
}
|
|
4416
4675
|
}
|
|
4417
4676
|
return {
|
|
@@ -4611,10 +4870,10 @@ async function relayAnthropicMessages(res, messagesUrl, body, apiKey, clientWant
|
|
|
4611
4870
|
}
|
|
4612
4871
|
|
|
4613
4872
|
// src/antigravity/anthropic-to-cloudcode.ts
|
|
4614
|
-
import { randomUUID as
|
|
4873
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
4615
4874
|
|
|
4616
4875
|
// src/antigravity/request-adapter.ts
|
|
4617
|
-
import { randomUUID as
|
|
4876
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
4618
4877
|
import { tool, jsonSchema } from "ai";
|
|
4619
4878
|
|
|
4620
4879
|
// src/proxy-shared.ts
|
|
@@ -4724,6 +4983,43 @@ function serializeToolResultContent(content) {
|
|
|
4724
4983
|
}
|
|
4725
4984
|
|
|
4726
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
|
+
}
|
|
4727
5023
|
function tracePartChars(part) {
|
|
4728
5024
|
if (typeof part.text === "string") return part.text.length;
|
|
4729
5025
|
if (part.type !== "tool-result") return void 0;
|
|
@@ -4883,13 +5179,17 @@ function translateRequest(ccReq, options = {}) {
|
|
|
4883
5179
|
}
|
|
4884
5180
|
}
|
|
4885
5181
|
} else if (part.inlineData) {
|
|
4886
|
-
|
|
4887
|
-
|
|
4888
|
-
|
|
4889
|
-
|
|
4890
|
-
|
|
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
|
+
}
|
|
4891
5191
|
} else if (part.functionCall) {
|
|
4892
|
-
const id = "call_" +
|
|
5192
|
+
const id = "call_" + randomUUID3().replace(/-/g, "");
|
|
4893
5193
|
const name = part.functionCall.name;
|
|
4894
5194
|
if (!nameToIdList.has(name)) nameToIdList.set(name, []);
|
|
4895
5195
|
nameToIdList.get(name).push(id);
|
|
@@ -4902,7 +5202,7 @@ function translateRequest(ccReq, options = {}) {
|
|
|
4902
5202
|
} else if (part.functionResponse) {
|
|
4903
5203
|
const name = part.functionResponse.name;
|
|
4904
5204
|
const idList = nameToIdList.get(name) || [];
|
|
4905
|
-
const id = idList.shift() || "call_" +
|
|
5205
|
+
const id = idList.shift() || "call_" + randomUUID3().replace(/-/g, "");
|
|
4906
5206
|
toolResults.push({
|
|
4907
5207
|
type: "tool-result",
|
|
4908
5208
|
toolCallId: id,
|
|
@@ -5111,7 +5411,7 @@ function anthropicToCloudCode(body, realModelId, projectId) {
|
|
|
5111
5411
|
}
|
|
5112
5412
|
return {
|
|
5113
5413
|
project: projectId,
|
|
5114
|
-
requestId:
|
|
5414
|
+
requestId: randomUUID4(),
|
|
5115
5415
|
model: realModelId,
|
|
5116
5416
|
userAgent: ANTIGRAVITY_USER_AGENT2,
|
|
5117
5417
|
requestType: "agent",
|
|
@@ -5121,7 +5421,7 @@ function anthropicToCloudCode(body, realModelId, projectId) {
|
|
|
5121
5421
|
}
|
|
5122
5422
|
|
|
5123
5423
|
// src/antigravity/cloudcode-to-anthropic.ts
|
|
5124
|
-
import { randomUUID as
|
|
5424
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
5125
5425
|
function writeEvent(res, event, data) {
|
|
5126
5426
|
res.write(`event: ${event}
|
|
5127
5427
|
data: ${JSON.stringify(data)}
|
|
@@ -5211,7 +5511,7 @@ function closeBlock(res, state) {
|
|
|
5211
5511
|
}
|
|
5212
5512
|
async function streamCloudCodeToAnthropic(res, upstreamRes, model, log7) {
|
|
5213
5513
|
const state = {
|
|
5214
|
-
messageId: `msg_${
|
|
5514
|
+
messageId: `msg_${randomUUID5().replace(/-/g, "").slice(0, 24)}`,
|
|
5215
5515
|
model,
|
|
5216
5516
|
blockIdx: 0,
|
|
5217
5517
|
textBlockOpen: false,
|
|
@@ -5300,7 +5600,7 @@ async function streamCloudCodeToAnthropic(res, upstreamRes, model, log7) {
|
|
|
5300
5600
|
closeBlock(res, state);
|
|
5301
5601
|
}
|
|
5302
5602
|
for (const tc of state.toolCalls) {
|
|
5303
|
-
const rawToolId = `toolu_${
|
|
5603
|
+
const rawToolId = `toolu_${randomUUID5().replace(/-/g, "").slice(0, 16)}`;
|
|
5304
5604
|
const toolId = encodeToolUseId(rawToolId, tc.signature);
|
|
5305
5605
|
writeEvent(res, "content_block_start", {
|
|
5306
5606
|
type: "content_block_start",
|
|
@@ -5351,7 +5651,7 @@ async function streamCloudCodeToAnthropic(res, upstreamRes, model, log7) {
|
|
|
5351
5651
|
}
|
|
5352
5652
|
async function collectCloudCodeToAnthropic(upstreamRes, model, log7) {
|
|
5353
5653
|
const text4 = await upstreamRes.text();
|
|
5354
|
-
const messageId = `msg_${
|
|
5654
|
+
const messageId = `msg_${randomUUID5().replace(/-/g, "").slice(0, 24)}`;
|
|
5355
5655
|
const content = [];
|
|
5356
5656
|
let stopReason = "end_turn";
|
|
5357
5657
|
let inputTokens = 0;
|
|
@@ -5380,7 +5680,7 @@ async function collectCloudCodeToAnthropic(upstreamRes, model, log7) {
|
|
|
5380
5680
|
else content.push({ type: "text", text: part.text });
|
|
5381
5681
|
} else if (part.functionCall && typeof part.functionCall === "object") {
|
|
5382
5682
|
const fc = part.functionCall;
|
|
5383
|
-
const rawToolId = `toolu_${
|
|
5683
|
+
const rawToolId = `toolu_${randomUUID5().replace(/-/g, "").slice(0, 16)}`;
|
|
5384
5684
|
content.push({
|
|
5385
5685
|
type: "tool_use",
|
|
5386
5686
|
id: encodeToolUseId(rawToolId, signature ?? pendingThoughtSignature),
|
|
@@ -5413,7 +5713,7 @@ async function collectCloudCodeToAnthropic(upstreamRes, model, log7) {
|
|
|
5413
5713
|
}
|
|
5414
5714
|
|
|
5415
5715
|
// src/proxy.ts
|
|
5416
|
-
import { randomUUID as
|
|
5716
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
5417
5717
|
|
|
5418
5718
|
// src/sdk-adapter.ts
|
|
5419
5719
|
import { streamText, generateText, tool as tool2, jsonSchema as jsonSchema2 } from "ai";
|
|
@@ -5746,6 +6046,17 @@ function translateRequest2(body, npm, options) {
|
|
|
5746
6046
|
if (options?.maxTools !== void 0 && upstreamTools.length > options.maxTools) {
|
|
5747
6047
|
upstreamTools = upstreamTools.slice(0, options.maxTools);
|
|
5748
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
|
+
}
|
|
5749
6060
|
const effort = anthropicEffortFromRequest(body) ?? options?.defaultEffort;
|
|
5750
6061
|
let providerOptions = deepMergeProviderOptions(
|
|
5751
6062
|
thinkingProviderOptions(npm),
|
|
@@ -5763,16 +6074,22 @@ function translateRequest2(body, npm, options) {
|
|
|
5763
6074
|
toolChoice: translateToolChoice(body.tool_choice),
|
|
5764
6075
|
maxOutputTokens: options?.openAiOAuth ? void 0 : body.max_tokens,
|
|
5765
6076
|
temperature: body.temperature,
|
|
5766
|
-
providerOptions
|
|
6077
|
+
providerOptions,
|
|
6078
|
+
subagentRouting: responseSubagentRouting
|
|
5767
6079
|
};
|
|
5768
6080
|
}
|
|
5769
|
-
|
|
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) {
|
|
5770
6086
|
const messageId = "msg_" + Date.now();
|
|
5771
6087
|
let blockIndex = -1;
|
|
5772
6088
|
let started = false;
|
|
5773
6089
|
let openType = null;
|
|
5774
6090
|
let pendingThinkingSig;
|
|
5775
6091
|
const idToBlock = /* @__PURE__ */ new Map();
|
|
6092
|
+
const bufferedAgentCalls = /* @__PURE__ */ new Map();
|
|
5776
6093
|
let finishReason = "end_turn";
|
|
5777
6094
|
let usage = { input_tokens: estimatedInputTokens, output_tokens: 0 };
|
|
5778
6095
|
const emit = (event, data) => write(sseChunk(event, data));
|
|
@@ -5855,9 +6172,13 @@ async function writeAnthropicStream(fullStream, modelId, write, log7, estimatedI
|
|
|
5855
6172
|
input: {}
|
|
5856
6173
|
});
|
|
5857
6174
|
idToBlock.set(part.id ?? "", blockIndex);
|
|
6175
|
+
if (subagentRouting && part.toolName === "Agent") {
|
|
6176
|
+
bufferedAgentCalls.set(part.id ?? "", { blockIndex });
|
|
6177
|
+
}
|
|
5858
6178
|
break;
|
|
5859
6179
|
}
|
|
5860
6180
|
case "tool-input-delta":
|
|
6181
|
+
if (bufferedAgentCalls.has(part.id ?? "")) break;
|
|
5861
6182
|
emit("content_block_delta", {
|
|
5862
6183
|
type: "content_block_delta",
|
|
5863
6184
|
index: idToBlock.get(part.id ?? "") ?? blockIndex,
|
|
@@ -5868,11 +6189,53 @@ async function writeAnthropicStream(fullStream, modelId, write, log7, estimatedI
|
|
|
5868
6189
|
break;
|
|
5869
6190
|
case "tool-call": {
|
|
5870
6191
|
finishReason = "tool_use";
|
|
5871
|
-
|
|
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") {
|
|
5872
6235
|
const sig = grabRoundTripSignature(part);
|
|
5873
6236
|
openBlock("tool", {
|
|
5874
6237
|
type: "tool_use",
|
|
5875
|
-
id: encodeToolUseId(
|
|
6238
|
+
id: encodeToolUseId(toolCallId, sig),
|
|
5876
6239
|
name: part.toolName,
|
|
5877
6240
|
input: {}
|
|
5878
6241
|
});
|
|
@@ -5900,6 +6263,7 @@ async function writeAnthropicStream(fullStream, modelId, write, log7, estimatedI
|
|
|
5900
6263
|
const errMsg = e?.message || (typeof part.error === "string" ? part.error : JSON.stringify(e?.data ?? part.error));
|
|
5901
6264
|
const errorType = anthropicErrorType(upstreamHttpStatus(part.error, errMsg));
|
|
5902
6265
|
log7?.(() => `sdk stream error (${errorType}): ${errMsg}`);
|
|
6266
|
+
bufferedAgentCalls.clear();
|
|
5903
6267
|
closeOpen();
|
|
5904
6268
|
emit("error", { type: "error", error: { type: errorType, message: errMsg } });
|
|
5905
6269
|
return;
|
|
@@ -5914,7 +6278,8 @@ async function writeAnthropicStream(fullStream, modelId, write, log7, estimatedI
|
|
|
5914
6278
|
emit("message_stop", { type: "message_stop" });
|
|
5915
6279
|
}
|
|
5916
6280
|
async function streamAnthropicResponse(model, params, modelId, write, log7, estimatedInputTokens = 0) {
|
|
5917
|
-
const
|
|
6281
|
+
const { subagentRouting, ...providerParams } = params;
|
|
6282
|
+
const result = streamText({ model, ...providerParams, onError: () => {
|
|
5918
6283
|
} });
|
|
5919
6284
|
Promise.resolve(result.text).catch(() => {
|
|
5920
6285
|
});
|
|
@@ -5931,22 +6296,24 @@ async function streamAnthropicResponse(model, params, modelId, write, log7, esti
|
|
|
5931
6296
|
modelId,
|
|
5932
6297
|
write,
|
|
5933
6298
|
log7,
|
|
5934
|
-
estimatedInputTokens
|
|
6299
|
+
estimatedInputTokens,
|
|
6300
|
+
subagentRouting
|
|
5935
6301
|
);
|
|
5936
6302
|
}
|
|
5937
6303
|
async function generateAnthropicResponse(model, params, modelId, options) {
|
|
6304
|
+
const { subagentRouting, ...providerParams } = params;
|
|
5938
6305
|
let text4;
|
|
5939
6306
|
let toolCalls;
|
|
5940
6307
|
let finishReason;
|
|
5941
6308
|
let usage;
|
|
5942
6309
|
if (options?.forceStream) {
|
|
5943
|
-
const r = streamText({ model, ...
|
|
6310
|
+
const r = streamText({ model, ...providerParams, onError: () => {
|
|
5944
6311
|
} });
|
|
5945
6312
|
Promise.resolve(r.toolResults).catch(() => {
|
|
5946
6313
|
});
|
|
5947
6314
|
[text4, toolCalls, finishReason, usage] = await Promise.all([r.text, r.toolCalls, r.finishReason, r.usage]);
|
|
5948
6315
|
} else {
|
|
5949
|
-
const r = await generateText({ model, ...
|
|
6316
|
+
const r = await generateText({ model, ...providerParams });
|
|
5950
6317
|
({ text: text4, toolCalls, finishReason, usage } = r);
|
|
5951
6318
|
}
|
|
5952
6319
|
return {
|
|
@@ -5956,12 +6323,20 @@ async function generateAnthropicResponse(model, params, modelId, options) {
|
|
|
5956
6323
|
model: modelId,
|
|
5957
6324
|
content: [
|
|
5958
6325
|
...text4 ? [{ type: "text", text: text4 }] : [],
|
|
5959
|
-
...toolCalls.map((tc) =>
|
|
5960
|
-
|
|
5961
|
-
|
|
5962
|
-
|
|
5963
|
-
|
|
5964
|
-
|
|
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
|
+
})
|
|
5965
6340
|
],
|
|
5966
6341
|
stop_reason: finishReason === "tool-calls" ? "tool_use" : "end_turn",
|
|
5967
6342
|
usage: { input_tokens: usage?.inputTokens ?? 0, output_tokens: usage?.outputTokens ?? 0 }
|
|
@@ -6008,6 +6383,21 @@ function aliasModelId(realId, providerId) {
|
|
|
6008
6383
|
const sanitized = providerId.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
6009
6384
|
return `anthropic-${sanitized}__${realId}`;
|
|
6010
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
|
+
}
|
|
6011
6401
|
function lookupRoute(byAlias, id) {
|
|
6012
6402
|
for (const key of routeLookupIds(id)) {
|
|
6013
6403
|
const route = byAlias.get(key);
|
|
@@ -6016,13 +6406,14 @@ function lookupRoute(byAlias, id) {
|
|
|
6016
6406
|
return void 0;
|
|
6017
6407
|
}
|
|
6018
6408
|
function startProxyCatalog(routes, defaultAliasId, debug = false) {
|
|
6019
|
-
const proxyToken =
|
|
6409
|
+
const proxyToken = randomUUID6();
|
|
6020
6410
|
silenceSdkWarnings();
|
|
6021
6411
|
if (routes.length === 0) {
|
|
6022
6412
|
return Promise.reject(new Error("Proxy catalog requires at least one route"));
|
|
6023
6413
|
}
|
|
6024
6414
|
const byAlias = new Map(routes.map((r) => [r.aliasId, r]));
|
|
6025
6415
|
const defaultRoute = byAlias.get(defaultAliasId) ?? routes[0];
|
|
6416
|
+
const subagentRouteRegistry = new SubagentRouteRegistry();
|
|
6026
6417
|
const plog = makeProxyLog(debug);
|
|
6027
6418
|
const onRejection = (reason) => {
|
|
6028
6419
|
plog(() => `Unhandled Rejection: ${reason instanceof Error ? reason.stack || reason.message : String(reason)}`);
|
|
@@ -6076,9 +6467,12 @@ function startProxyCatalog(routes, defaultAliasId, debug = false) {
|
|
|
6076
6467
|
anthropicError(res, 400, "Invalid JSON body");
|
|
6077
6468
|
return;
|
|
6078
6469
|
}
|
|
6470
|
+
const correlatedSubagent = subagentRouteRegistry.consume(req.headers, anthropicBody);
|
|
6471
|
+
if (correlatedSubagent) anthropicBody = correlatedSubagent.body;
|
|
6079
6472
|
const originalModel = anthropicBody.model;
|
|
6080
6473
|
const clientWantsStream = Boolean(anthropicBody.stream);
|
|
6081
|
-
const
|
|
6474
|
+
const correlatedRoute = correlatedSubagent ? routes.find((candidate) => (candidate.gatewayAliasId ?? candidate.aliasId) === correlatedSubagent.modelId) : void 0;
|
|
6475
|
+
const route = correlatedRoute ?? lookupRoute(byAlias, originalModel) ?? defaultRoute;
|
|
6082
6476
|
const apiKey = route.apiKey;
|
|
6083
6477
|
const upstreamUrl = route.upstreamUrl;
|
|
6084
6478
|
plog(
|
|
@@ -6134,10 +6528,16 @@ function startProxyCatalog(routes, defaultAliasId, debug = false) {
|
|
|
6134
6528
|
}
|
|
6135
6529
|
if (usesSdkAdapter) {
|
|
6136
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
|
+
}
|
|
6137
6536
|
const params = translateRequest2(anthropicBody, route.npm, {
|
|
6138
6537
|
openAiOAuth,
|
|
6139
6538
|
maxTools: maxToolsForNpm(route.npm),
|
|
6140
6539
|
onDebug: (msg) => plog(() => msg),
|
|
6540
|
+
subagentRouting,
|
|
6141
6541
|
reasoningMetadata: {
|
|
6142
6542
|
providerId: route.providerId,
|
|
6143
6543
|
apiBaseUrl: route.baseURL,
|
|
@@ -6185,7 +6585,7 @@ function startProxyCatalog(routes, defaultAliasId, debug = false) {
|
|
|
6185
6585
|
model,
|
|
6186
6586
|
params,
|
|
6187
6587
|
originalModel,
|
|
6188
|
-
{ forceStream: openAiOAuth }
|
|
6588
|
+
{ forceStream: openAiOAuth, log: plog }
|
|
6189
6589
|
);
|
|
6190
6590
|
sendJson(res, 200, anthropicResponse);
|
|
6191
6591
|
}
|
|
@@ -7926,6 +8326,14 @@ function injectRelayModels(fixture, routes, templateKey) {
|
|
|
7926
8326
|
}]
|
|
7927
8327
|
}
|
|
7928
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 = [];
|
|
7929
8337
|
return result;
|
|
7930
8338
|
}
|
|
7931
8339
|
if (!result.agentModelSorts?.[0]?.groups?.[0]) {
|
|
@@ -9921,8 +10329,9 @@ async function startServer(options) {
|
|
|
9921
10329
|
silenceSdkWarnings();
|
|
9922
10330
|
const languageModelCache = /* @__PURE__ */ new Map();
|
|
9923
10331
|
const plog = makeServerLog(options.debugLogPath);
|
|
10332
|
+
const subagentRouteRegistry = new SubagentRouteRegistry();
|
|
9924
10333
|
const server = createServer2((req, res) => {
|
|
9925
|
-
void routeRequest(req, res, options, languageModelCache, plog);
|
|
10334
|
+
void routeRequest(req, res, options, languageModelCache, plog, subagentRouteRegistry);
|
|
9926
10335
|
});
|
|
9927
10336
|
await new Promise((resolve, reject) => {
|
|
9928
10337
|
server.once("error", reject);
|
|
@@ -9945,7 +10354,7 @@ async function startServer(options) {
|
|
|
9945
10354
|
})
|
|
9946
10355
|
};
|
|
9947
10356
|
}
|
|
9948
|
-
async function routeRequest(req, res, options, modelCache, plog) {
|
|
10357
|
+
async function routeRequest(req, res, options, modelCache, plog, subagentRouteRegistry) {
|
|
9949
10358
|
try {
|
|
9950
10359
|
const pathname = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`).pathname;
|
|
9951
10360
|
plog(`${req.method} ${pathname}`);
|
|
@@ -9970,7 +10379,7 @@ async function routeRequest(req, res, options, modelCache, plog) {
|
|
|
9970
10379
|
return;
|
|
9971
10380
|
}
|
|
9972
10381
|
if (req.method === "POST" && pathname === "/anthropic/v1/messages") {
|
|
9973
|
-
await handleAnthropicMessages(req, res, options, modelCache, plog);
|
|
10382
|
+
await handleAnthropicMessages(req, res, options, modelCache, plog, subagentRouteRegistry);
|
|
9974
10383
|
return;
|
|
9975
10384
|
}
|
|
9976
10385
|
if (req.method === "POST" && pathname === "/openai/v1/chat/completions") {
|
|
@@ -9982,13 +10391,16 @@ async function routeRequest(req, res, options, modelCache, plog) {
|
|
|
9982
10391
|
sendJson(res, 500, { error: { message: err instanceof Error ? err.message : String(err) } });
|
|
9983
10392
|
}
|
|
9984
10393
|
}
|
|
9985
|
-
async function handleAnthropicMessages(req, res, options, modelCache, plog) {
|
|
9986
|
-
|
|
10394
|
+
async function handleAnthropicMessages(req, res, options, modelCache, plog, subagentRouteRegistry) {
|
|
10395
|
+
let body = await readJson(req);
|
|
9987
10396
|
if (!body) {
|
|
9988
10397
|
sendJson(res, 400, { error: { message: "Invalid JSON body" } });
|
|
9989
10398
|
return;
|
|
9990
10399
|
}
|
|
9991
|
-
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);
|
|
9992
10404
|
if (!model) {
|
|
9993
10405
|
plog(`model not found: ${body.model}`);
|
|
9994
10406
|
return;
|
|
@@ -10047,10 +10459,20 @@ async function handleAnthropicMessages(req, res, options, modelCache, plog) {
|
|
|
10047
10459
|
if (npmMaxTools !== void 0 && toolCount > npmMaxTools) {
|
|
10048
10460
|
plog(`tools truncated: ${toolCount} \u2192 ${npmMaxTools} (provider limit)`);
|
|
10049
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
|
+
}
|
|
10050
10471
|
const params = translateRequest2(body, model.npm, {
|
|
10051
10472
|
defaultEffort: anthropicEffortFromRequest(body) ? void 0 : model.defaultEffort,
|
|
10052
10473
|
openAiOAuth: model.npm === "@ai-sdk/openai" && model.authType === "oauth",
|
|
10053
10474
|
onDebug: plog,
|
|
10475
|
+
subagentRouting,
|
|
10054
10476
|
reasoningMetadata: {
|
|
10055
10477
|
providerId: model.providerId,
|
|
10056
10478
|
apiBaseUrl: model.apiBaseUrl,
|
|
@@ -10076,12 +10498,17 @@ async function handleAnthropicMessages(req, res, options, modelCache, plog) {
|
|
|
10076
10498
|
params,
|
|
10077
10499
|
responseModelId,
|
|
10078
10500
|
(chunk) => res.write(chunk),
|
|
10079
|
-
|
|
10501
|
+
plog,
|
|
10080
10502
|
estimateAnthropicInputTokens(body)
|
|
10081
10503
|
);
|
|
10082
10504
|
res.end();
|
|
10083
10505
|
} else {
|
|
10084
|
-
const anthropicResponse = await generateAnthropicResponse(
|
|
10506
|
+
const anthropicResponse = await generateAnthropicResponse(
|
|
10507
|
+
languageModel,
|
|
10508
|
+
params,
|
|
10509
|
+
responseModelId,
|
|
10510
|
+
{ log: plog }
|
|
10511
|
+
);
|
|
10085
10512
|
sendJson(res, 200, anthropicResponse);
|
|
10086
10513
|
}
|
|
10087
10514
|
} catch (err) {
|
|
@@ -12093,6 +12520,8 @@ export {
|
|
|
12093
12520
|
splitToolUseId,
|
|
12094
12521
|
encodeToolUseId,
|
|
12095
12522
|
serializeToolResultContent,
|
|
12523
|
+
UNSUPPORTED_VOICE_MESSAGE,
|
|
12524
|
+
sanitizeUnsupportedInlineData,
|
|
12096
12525
|
summarizeSdkRequestForTrace,
|
|
12097
12526
|
translateRequest,
|
|
12098
12527
|
formatUpstreamErrorTrace,
|
|
@@ -12162,4 +12591,4 @@ export {
|
|
|
12162
12591
|
supportsClaudeTransparentMode,
|
|
12163
12592
|
buildHttpProxyRoutes
|
|
12164
12593
|
};
|
|
12165
|
-
//# sourceMappingURL=chunk-
|
|
12594
|
+
//# sourceMappingURL=chunk-GHSURQOK.js.map
|