@nick3/copilot-api 1.0.8 → 1.1.5
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/README.md +72 -22
- package/dist/{accounts-manager-CJtqVqDx.js → accounts-manager-MluR5e6y.js} +74 -6
- package/dist/accounts-manager-MluR5e6y.js.map +1 -0
- package/dist/admin/assets/{index-DU5shzxS.js → index-C9gbhsHu.js} +16 -15
- package/dist/admin/index.html +1 -1
- package/dist/main.js +2 -2
- package/dist/main.js.map +1 -1
- package/dist/{server-BnJIteDi.js → server-6hnk3aRJ.js} +209 -117
- package/dist/server-6hnk3aRJ.js.map +1 -0
- package/package.json +4 -1
- package/dist/accounts-manager-CJtqVqDx.js.map +0 -1
- package/dist/server-BnJIteDi.js.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { HTTPError, PATHS, accountFromState, accountsManager, copilotBaseUrl, copilotHeaders, forwardError, getAliasTargetSet, getConfig, getCopilotUsage, getExtraPromptForModel, getModelAliases, getModelAliasesInfo, getModelRefreshIntervalMs, getReasoningEffortForModel, getSmallModel, isForceAgentEnabled, isFreeModelLoadBalancingEnabled, isMessageStartInputTokensFallbackEnabled, isNullish, listAccountsFromRegistry, mergeConfigWithDefaults, shouldCompactUseSmallModel, sleep, state } from "./accounts-manager-
|
|
1
|
+
import { HTTPError, PATHS, accountFromState, accountsManager, copilotBaseUrl, copilotHeaders, forwardError, getAliasTargetSet, getConfig, getCopilotUsage, getExtraPromptForModel, getModelAliases, getModelAliasesInfo, getModelRefreshIntervalMs, getReasoningEffortForModel, getSmallModel, isForceAgentEnabled, isFreeModelLoadBalancingEnabled, isMessageStartInputTokensFallbackEnabled, isNullish, listAccountsFromRegistry, mergeConfigWithDefaults, shouldCompactUseSmallModel, sleep, state } from "./accounts-manager-MluR5e6y.js";
|
|
2
2
|
import consola from "consola";
|
|
3
3
|
import fs, { readFile } from "node:fs/promises";
|
|
4
4
|
import * as path$1 from "node:path";
|
|
@@ -14,27 +14,56 @@ import { streamSSE } from "hono/streaming";
|
|
|
14
14
|
import util from "node:util";
|
|
15
15
|
import { events } from "fetch-event-stream";
|
|
16
16
|
|
|
17
|
-
//#region src/lib/
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
if (
|
|
28
|
-
return
|
|
29
|
-
}
|
|
30
|
-
function
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if (
|
|
17
|
+
//#region src/lib/request-auth.ts
|
|
18
|
+
const LEGACY_API_KEY_ENV_VAR = "COPILOT_API_KEY";
|
|
19
|
+
let warnedLegacyEnvFallback = false;
|
|
20
|
+
let warnedLegacyConfigFallback = false;
|
|
21
|
+
function normalizeApiKeys(apiKeys) {
|
|
22
|
+
if (!Array.isArray(apiKeys)) {
|
|
23
|
+
if (apiKeys !== void 0) consola.warn("Invalid auth.apiKeys config. Expected an array of strings.");
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
const normalizedKeys = apiKeys.filter((key) => typeof key === "string").map((key) => key.trim()).filter((key) => key.length > 0);
|
|
27
|
+
if (normalizedKeys.length !== apiKeys.length) consola.warn("Invalid auth.apiKeys entries found. Only non-empty strings are allowed.");
|
|
28
|
+
return [...new Set(normalizedKeys)];
|
|
29
|
+
}
|
|
30
|
+
function getConfiguredApiKeys() {
|
|
31
|
+
const config = getConfig();
|
|
32
|
+
const configuredApiKeys = normalizeApiKeys(config.auth?.apiKeys);
|
|
33
|
+
if (configuredApiKeys.length > 0) return configuredApiKeys;
|
|
34
|
+
const envApiKey = process.env[LEGACY_API_KEY_ENV_VAR]?.trim();
|
|
35
|
+
if (envApiKey) {
|
|
36
|
+
if (!warnedLegacyEnvFallback) {
|
|
37
|
+
warnedLegacyEnvFallback = true;
|
|
38
|
+
consola.warn(`Using legacy ${LEGACY_API_KEY_ENV_VAR}. Please migrate to config.auth.apiKeys.`);
|
|
39
|
+
}
|
|
40
|
+
return [envApiKey];
|
|
41
|
+
}
|
|
42
|
+
const legacyConfigApiKey = config.apiKey?.trim();
|
|
43
|
+
if (legacyConfigApiKey) {
|
|
44
|
+
if (!warnedLegacyConfigFallback) {
|
|
45
|
+
warnedLegacyConfigFallback = true;
|
|
46
|
+
consola.warn("Using deprecated config.apiKey. Please migrate to config.auth.apiKeys.");
|
|
47
|
+
}
|
|
48
|
+
return [legacyConfigApiKey];
|
|
37
49
|
}
|
|
50
|
+
return configuredApiKeys;
|
|
51
|
+
}
|
|
52
|
+
function extractRequestApiKey(c) {
|
|
53
|
+
const xApiKey = c.req.header("x-api-key")?.trim();
|
|
54
|
+
if (xApiKey) return xApiKey;
|
|
55
|
+
const authorization = c.req.header("authorization");
|
|
56
|
+
if (!authorization) return null;
|
|
57
|
+
const [scheme, ...rest] = authorization.trim().split(/\s+/);
|
|
58
|
+
if (scheme.toLowerCase() !== "bearer") return null;
|
|
59
|
+
return rest.join(" ").trim() || null;
|
|
60
|
+
}
|
|
61
|
+
function createUnauthorizedResponse(c) {
|
|
62
|
+
c.header("WWW-Authenticate", "Bearer realm=\"copilot-api\"");
|
|
63
|
+
return c.json({ error: {
|
|
64
|
+
message: "Unauthorized. Provide Authorization: Bearer <key> or x-api-key.",
|
|
65
|
+
type: "unauthorized"
|
|
66
|
+
} }, 401);
|
|
38
67
|
}
|
|
39
68
|
function normalizePathname(pathname) {
|
|
40
69
|
if (pathname.length > 1 && pathname.endsWith("/")) return pathname.slice(0, -1);
|
|
@@ -43,10 +72,6 @@ function normalizePathname(pathname) {
|
|
|
43
72
|
function hasPrefixBoundary(pathname, prefix) {
|
|
44
73
|
return pathname === prefix || pathname.startsWith(`${prefix}/`);
|
|
45
74
|
}
|
|
46
|
-
function isProtectedPath(pathnameRaw) {
|
|
47
|
-
const pathname = normalizePathname(pathnameRaw);
|
|
48
|
-
return hasPrefixBoundary(pathname, "/v1") || hasPrefixBoundary(pathname, "/token") || hasPrefixBoundary(pathname, "/usage") || hasPrefixBoundary(pathname, "/chat/completions") || hasPrefixBoundary(pathname, "/embeddings") || hasPrefixBoundary(pathname, "/models") || hasPrefixBoundary(pathname, "/responses");
|
|
49
|
-
}
|
|
50
75
|
function timingSafeKeyCompare(a, b) {
|
|
51
76
|
try {
|
|
52
77
|
const aBuf = Buffer.from(a);
|
|
@@ -57,30 +82,21 @@ function timingSafeKeyCompare(a, b) {
|
|
|
57
82
|
return false;
|
|
58
83
|
}
|
|
59
84
|
}
|
|
60
|
-
function
|
|
61
|
-
const
|
|
62
|
-
const
|
|
85
|
+
function createAuthMiddleware(options = {}) {
|
|
86
|
+
const getApiKeys = options.getApiKeys ?? getConfiguredApiKeys;
|
|
87
|
+
const allowUnauthenticatedPaths = new Set((options.allowUnauthenticatedPaths ?? ["/"]).map((path$2) => normalizePathname(path$2)));
|
|
88
|
+
const allowUnauthenticatedPathPrefixes = (options.allowUnauthenticatedPathPrefixes ?? []).map((path$2) => normalizePathname(path$2));
|
|
89
|
+
const allowOptionsBypass = options.allowOptionsBypass ?? true;
|
|
63
90
|
return async (c, next) => {
|
|
64
|
-
if (c.req.method === "OPTIONS")
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const
|
|
69
|
-
if (
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const configuredKey = getConfiguredKey();
|
|
74
|
-
if (!configuredKey) {
|
|
75
|
-
await next();
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
const providedKey = extractRequestApiKey(c.req.raw.headers);
|
|
79
|
-
if (!providedKey || !timingSafeKeyCompare(providedKey, configuredKey)) return c.json({ error: {
|
|
80
|
-
message: "Unauthorized. Provide Authorization: Bearer <key> or x-api-key.",
|
|
81
|
-
type: "unauthorized"
|
|
82
|
-
} }, 401);
|
|
83
|
-
await next();
|
|
91
|
+
if (allowOptionsBypass && c.req.method === "OPTIONS") return next();
|
|
92
|
+
const pathname = normalizePathname(new URL(c.req.url, "http://local").pathname);
|
|
93
|
+
if (allowUnauthenticatedPaths.has(pathname)) return next();
|
|
94
|
+
if (allowUnauthenticatedPathPrefixes.some((prefix) => hasPrefixBoundary(pathname, prefix))) return next();
|
|
95
|
+
const apiKeys = getApiKeys();
|
|
96
|
+
if (apiKeys.length === 0) return next();
|
|
97
|
+
const requestApiKey = extractRequestApiKey(c);
|
|
98
|
+
if (!(requestApiKey ? apiKeys.some((apiKey) => timingSafeKeyCompare(requestApiKey, apiKey)) : false)) return createUnauthorizedResponse(c);
|
|
99
|
+
return next();
|
|
84
100
|
};
|
|
85
101
|
}
|
|
86
102
|
|
|
@@ -705,6 +721,7 @@ function parseTriStateBool(value) {
|
|
|
705
721
|
if (value === "0") return false;
|
|
706
722
|
}
|
|
707
723
|
const CONFIG_KEYS = new Set([
|
|
724
|
+
"auth",
|
|
708
725
|
"extraPrompts",
|
|
709
726
|
"smallModel",
|
|
710
727
|
"freeModelLoadBalancing",
|
|
@@ -755,6 +772,16 @@ function parseOptionalNonNegativeNumber(value, field) {
|
|
|
755
772
|
if (!Number.isFinite(value) || value < 0) return { error: `${field} must be a non-negative number` };
|
|
756
773
|
return { value };
|
|
757
774
|
}
|
|
775
|
+
function parseAuthConfig(value) {
|
|
776
|
+
if (value === null || value === void 0) return { clear: true };
|
|
777
|
+
if (!isPlainObject(value)) return { error: "auth must be an object" };
|
|
778
|
+
for (const key of Object.keys(value)) if (key !== "apiKeys") return { error: `auth.${key} is not supported` };
|
|
779
|
+
if (!("apiKeys" in value) || value.apiKeys === null || value.apiKeys === void 0) return { value: { apiKeys: [] } };
|
|
780
|
+
if (!Array.isArray(value.apiKeys)) return { error: "auth.apiKeys must be an array of strings" };
|
|
781
|
+
const normalizedApiKeys = value.apiKeys.filter((item) => typeof item === "string").map((item) => item.trim()).filter((item) => item.length > 0);
|
|
782
|
+
if (normalizedApiKeys.length !== value.apiKeys.length) return { error: "auth.apiKeys must contain non-empty strings only" };
|
|
783
|
+
return { value: { apiKeys: [...new Set(normalizedApiKeys)] } };
|
|
784
|
+
}
|
|
758
785
|
function parseStringRecord(value, field) {
|
|
759
786
|
if (value === null || value === void 0) return { clear: true };
|
|
760
787
|
if (!isPlainObject(value)) return { error: `${field} must be an object with string values` };
|
|
@@ -830,6 +857,15 @@ function applyOptionalString(next, key, value) {
|
|
|
830
857
|
}
|
|
831
858
|
next[key] = parsed.value;
|
|
832
859
|
}
|
|
860
|
+
function applyAuthConfig(next, value) {
|
|
861
|
+
const parsed = parseAuthConfig(value);
|
|
862
|
+
if ("error" in parsed) return parsed.error;
|
|
863
|
+
if ("clear" in parsed) {
|
|
864
|
+
delete next.auth;
|
|
865
|
+
return;
|
|
866
|
+
}
|
|
867
|
+
next.auth = parsed.value;
|
|
868
|
+
}
|
|
833
869
|
function applyOptionalBoolean(next, key, value) {
|
|
834
870
|
const parsed = parseOptionalBoolean(value, key);
|
|
835
871
|
if ("error" in parsed) return parsed.error;
|
|
@@ -875,51 +911,29 @@ function applyModelAliases(next, value) {
|
|
|
875
911
|
}
|
|
876
912
|
next.modelAliases = parsed.value;
|
|
877
913
|
}
|
|
914
|
+
const CONFIG_PATCH_HANDLERS = {
|
|
915
|
+
auth: applyAuthConfig,
|
|
916
|
+
extraPrompts: applyExtraPrompts,
|
|
917
|
+
smallModel: (next, value) => applyOptionalString(next, "smallModel", value),
|
|
918
|
+
freeModelLoadBalancing: (next, value) => applyOptionalBoolean(next, "freeModelLoadBalancing", value),
|
|
919
|
+
apiKey: (next, value) => applyOptionalString(next, "apiKey", value),
|
|
920
|
+
modelReasoningEfforts: applyReasoningEfforts,
|
|
921
|
+
modelAliases: applyModelAliases,
|
|
922
|
+
allowOriginalModelNamesForAliases: (next, value) => applyOptionalBoolean(next, "allowOriginalModelNamesForAliases", value),
|
|
923
|
+
useFunctionApplyPatch: (next, value) => applyOptionalBoolean(next, "useFunctionApplyPatch", value),
|
|
924
|
+
forceAgent: (next, value) => applyOptionalBoolean(next, "forceAgent", value),
|
|
925
|
+
compactUseSmallModel: (next, value) => applyOptionalBoolean(next, "compactUseSmallModel", value),
|
|
926
|
+
messageStartInputTokensFallback: (next, value) => applyOptionalBoolean(next, "messageStartInputTokensFallback", value),
|
|
927
|
+
modelRefreshIntervalHours: (next, value) => applyOptionalNumber(next, "modelRefreshIntervalHours", value)
|
|
928
|
+
};
|
|
878
929
|
function applyConfigPatch(base, input) {
|
|
879
930
|
const next = { ...base };
|
|
880
931
|
for (const [rawKey, value] of Object.entries(input)) {
|
|
881
932
|
const key = rawKey;
|
|
882
933
|
if (!CONFIG_KEYS.has(key)) return { error: `Unknown config key: ${rawKey}` };
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
error = applyExtraPrompts(next, value);
|
|
887
|
-
break;
|
|
888
|
-
case "smallModel":
|
|
889
|
-
error = applyOptionalString(next, "smallModel", value);
|
|
890
|
-
break;
|
|
891
|
-
case "freeModelLoadBalancing":
|
|
892
|
-
error = applyOptionalBoolean(next, "freeModelLoadBalancing", value);
|
|
893
|
-
break;
|
|
894
|
-
case "apiKey":
|
|
895
|
-
error = applyOptionalString(next, "apiKey", value);
|
|
896
|
-
break;
|
|
897
|
-
case "modelReasoningEfforts":
|
|
898
|
-
error = applyReasoningEfforts(next, value);
|
|
899
|
-
break;
|
|
900
|
-
case "modelAliases":
|
|
901
|
-
error = applyModelAliases(next, value);
|
|
902
|
-
break;
|
|
903
|
-
case "allowOriginalModelNamesForAliases":
|
|
904
|
-
error = applyOptionalBoolean(next, "allowOriginalModelNamesForAliases", value);
|
|
905
|
-
break;
|
|
906
|
-
case "useFunctionApplyPatch":
|
|
907
|
-
error = applyOptionalBoolean(next, "useFunctionApplyPatch", value);
|
|
908
|
-
break;
|
|
909
|
-
case "forceAgent":
|
|
910
|
-
error = applyOptionalBoolean(next, "forceAgent", value);
|
|
911
|
-
break;
|
|
912
|
-
case "compactUseSmallModel":
|
|
913
|
-
error = applyOptionalBoolean(next, "compactUseSmallModel", value);
|
|
914
|
-
break;
|
|
915
|
-
case "messageStartInputTokensFallback":
|
|
916
|
-
error = applyOptionalBoolean(next, "messageStartInputTokensFallback", value);
|
|
917
|
-
break;
|
|
918
|
-
case "modelRefreshIntervalHours":
|
|
919
|
-
error = applyOptionalNumber(next, "modelRefreshIntervalHours", value);
|
|
920
|
-
break;
|
|
921
|
-
default: return { error: `Unsupported config key: ${rawKey}` };
|
|
922
|
-
}
|
|
934
|
+
const handler = CONFIG_PATCH_HANDLERS[rawKey];
|
|
935
|
+
if (!handler) return { error: `Unsupported config key: ${rawKey}` };
|
|
936
|
+
const error = handler(next, value);
|
|
923
937
|
if (error) return { error };
|
|
924
938
|
}
|
|
925
939
|
return { config: next };
|
|
@@ -2237,11 +2251,12 @@ const createResponses = async (payload, { vision, initiator, upstreamRequestId }
|
|
|
2237
2251
|
//#endregion
|
|
2238
2252
|
//#region src/routes/messages/responses-translation.ts
|
|
2239
2253
|
const MESSAGE_TYPE = "message";
|
|
2254
|
+
const CODEX_PHASE_MODEL = "gpt-5.3-codex";
|
|
2240
2255
|
const THINKING_TEXT$1 = "Thinking...";
|
|
2241
2256
|
const translateAnthropicMessagesToResponsesPayload = (payload, modelOverride) => {
|
|
2242
2257
|
const model = modelOverride ?? payload.model;
|
|
2243
2258
|
const input = [];
|
|
2244
|
-
for (const message of payload.messages) input.push(...translateMessage(message));
|
|
2259
|
+
for (const message of payload.messages) input.push(...translateMessage(message, payload.model));
|
|
2245
2260
|
const translatedTools = convertAnthropicTools(payload.tools);
|
|
2246
2261
|
const toolChoice = convertAnthropicToolChoice(payload.tool_choice);
|
|
2247
2262
|
const { safetyIdentifier, promptCacheKey } = parseUserId(payload.metadata?.user_id);
|
|
@@ -2267,9 +2282,9 @@ const translateAnthropicMessagesToResponsesPayload = (payload, modelOverride) =>
|
|
|
2267
2282
|
include: ["reasoning.encrypted_content"]
|
|
2268
2283
|
};
|
|
2269
2284
|
};
|
|
2270
|
-
const translateMessage = (message) => {
|
|
2285
|
+
const translateMessage = (message, model) => {
|
|
2271
2286
|
if (message.role === "user") return translateUserMessage(message);
|
|
2272
|
-
return translateAssistantMessage(message);
|
|
2287
|
+
return translateAssistantMessage(message, model);
|
|
2273
2288
|
};
|
|
2274
2289
|
const translateUserMessage = (message) => {
|
|
2275
2290
|
if (typeof message.content === "string") return [createMessage("user", message.content)];
|
|
@@ -2278,36 +2293,46 @@ const translateUserMessage = (message) => {
|
|
|
2278
2293
|
const pendingContent = [];
|
|
2279
2294
|
for (const block of message.content) {
|
|
2280
2295
|
if (block.type === "tool_result") {
|
|
2281
|
-
flushPendingContent("user"
|
|
2296
|
+
flushPendingContent(pendingContent, items, { role: "user" });
|
|
2282
2297
|
items.push(createFunctionCallOutput(block));
|
|
2283
2298
|
continue;
|
|
2284
2299
|
}
|
|
2285
2300
|
const converted = translateUserContentBlock(block);
|
|
2286
2301
|
if (converted) pendingContent.push(converted);
|
|
2287
2302
|
}
|
|
2288
|
-
flushPendingContent("user"
|
|
2303
|
+
flushPendingContent(pendingContent, items, { role: "user" });
|
|
2289
2304
|
return items;
|
|
2290
2305
|
};
|
|
2291
|
-
const translateAssistantMessage = (message) => {
|
|
2292
|
-
|
|
2306
|
+
const translateAssistantMessage = (message, model) => {
|
|
2307
|
+
const assistantPhase = resolveAssistantPhase(model, message.content);
|
|
2308
|
+
if (typeof message.content === "string") return [createMessage("assistant", message.content, assistantPhase)];
|
|
2293
2309
|
if (!Array.isArray(message.content)) return [];
|
|
2294
2310
|
const items = [];
|
|
2295
2311
|
const pendingContent = [];
|
|
2296
2312
|
for (const block of message.content) {
|
|
2297
2313
|
if (block.type === "tool_use") {
|
|
2298
|
-
flushPendingContent(
|
|
2314
|
+
flushPendingContent(pendingContent, items, {
|
|
2315
|
+
role: "assistant",
|
|
2316
|
+
phase: assistantPhase
|
|
2317
|
+
});
|
|
2299
2318
|
items.push(createFunctionToolCall(block));
|
|
2300
2319
|
continue;
|
|
2301
2320
|
}
|
|
2302
2321
|
if (block.type === "thinking" && block.signature && block.signature.includes("@")) {
|
|
2303
|
-
flushPendingContent(
|
|
2322
|
+
flushPendingContent(pendingContent, items, {
|
|
2323
|
+
role: "assistant",
|
|
2324
|
+
phase: assistantPhase
|
|
2325
|
+
});
|
|
2304
2326
|
items.push(createReasoningContent(block));
|
|
2305
2327
|
continue;
|
|
2306
2328
|
}
|
|
2307
2329
|
const converted = translateAssistantContentBlock(block);
|
|
2308
2330
|
if (converted) pendingContent.push(converted);
|
|
2309
2331
|
}
|
|
2310
|
-
flushPendingContent(
|
|
2332
|
+
flushPendingContent(pendingContent, items, {
|
|
2333
|
+
role: "assistant",
|
|
2334
|
+
phase: assistantPhase
|
|
2335
|
+
});
|
|
2311
2336
|
return items;
|
|
2312
2337
|
};
|
|
2313
2338
|
const translateUserContentBlock = (block) => {
|
|
@@ -2323,17 +2348,26 @@ const translateAssistantContentBlock = (block) => {
|
|
|
2323
2348
|
default: return;
|
|
2324
2349
|
}
|
|
2325
2350
|
};
|
|
2326
|
-
const flushPendingContent = (
|
|
2351
|
+
const flushPendingContent = (pendingContent, target, message) => {
|
|
2327
2352
|
if (pendingContent.length === 0) return;
|
|
2328
2353
|
const messageContent = [...pendingContent];
|
|
2329
|
-
target.push(createMessage(role, messageContent));
|
|
2354
|
+
target.push(createMessage(message.role, messageContent, message.phase));
|
|
2330
2355
|
pendingContent.length = 0;
|
|
2331
2356
|
};
|
|
2332
|
-
const createMessage = (role, content) => ({
|
|
2357
|
+
const createMessage = (role, content, phase) => ({
|
|
2333
2358
|
type: MESSAGE_TYPE,
|
|
2334
2359
|
role,
|
|
2335
|
-
content
|
|
2360
|
+
content,
|
|
2361
|
+
...role === "assistant" && phase ? { phase } : {}
|
|
2336
2362
|
});
|
|
2363
|
+
const resolveAssistantPhase = (model, content) => {
|
|
2364
|
+
if (!shouldApplyCodexPhase(model)) return;
|
|
2365
|
+
if (typeof content === "string") return "final_answer";
|
|
2366
|
+
if (!Array.isArray(content)) return;
|
|
2367
|
+
if (!content.some((block) => block.type === "text")) return;
|
|
2368
|
+
return content.some((block) => block.type === "tool_use") ? "commentary" : "final_answer";
|
|
2369
|
+
};
|
|
2370
|
+
const shouldApplyCodexPhase = (model) => model === CODEX_PHASE_MODEL;
|
|
2337
2371
|
const createTextContent = (text) => ({
|
|
2338
2372
|
type: "input_text",
|
|
2339
2373
|
text
|
|
@@ -2608,7 +2642,7 @@ const createChatCompletions = async (payload, account, options) => {
|
|
|
2608
2642
|
const ctx = account ?? accountFromState();
|
|
2609
2643
|
if (!ctx.copilotToken) throw new Error("Copilot token not found");
|
|
2610
2644
|
const enableVision = payload.messages.some((x) => typeof x.content !== "string" && x.content?.some((x$1) => x$1.type === "image_url"));
|
|
2611
|
-
const initiator = getChatInitiator(payload.messages);
|
|
2645
|
+
const initiator = options?.initiator ?? getChatInitiator(payload.messages);
|
|
2612
2646
|
const headers = {
|
|
2613
2647
|
...copilotHeaders(ctx, enableVision, options?.upstreamRequestId),
|
|
2614
2648
|
"X-Initiator": initiator
|
|
@@ -4057,7 +4091,7 @@ const createMessages = async (payload, account, options) => {
|
|
|
4057
4091
|
const ctx = account ?? accountFromState();
|
|
4058
4092
|
if (!ctx.copilotToken) throw new Error("Copilot token not found");
|
|
4059
4093
|
const enableVision = payload.messages.some((message) => Array.isArray(message.content) && message.content.some((block) => block.type === "image"));
|
|
4060
|
-
const initiator = getMessagesInitiator(payload);
|
|
4094
|
+
const initiator = options?.initiator ?? getMessagesInitiator(payload);
|
|
4061
4095
|
const headers = {
|
|
4062
4096
|
...copilotHeaders(ctx, enableVision, options?.upstreamRequestId),
|
|
4063
4097
|
"X-Initiator": initiator
|
|
@@ -4335,6 +4369,51 @@ function closeThinkingBlockIfOpen(state$1, events$1) {
|
|
|
4335
4369
|
}
|
|
4336
4370
|
}
|
|
4337
4371
|
|
|
4372
|
+
//#endregion
|
|
4373
|
+
//#region src/routes/messages/subagent-marker.ts
|
|
4374
|
+
const subagentMarkerPrefix = "__SUBAGENT_MARKER__";
|
|
4375
|
+
const parseSubagentMarkerFromFirstUser = (payload) => {
|
|
4376
|
+
const firstUserMessage = payload.messages.find((msg) => msg.role === "user");
|
|
4377
|
+
if (!firstUserMessage || !Array.isArray(firstUserMessage.content)) return null;
|
|
4378
|
+
for (const block of firstUserMessage.content) {
|
|
4379
|
+
if (block.type !== "text") continue;
|
|
4380
|
+
const marker = parseSubagentMarkerFromSystemReminder(block.text);
|
|
4381
|
+
if (marker) return marker;
|
|
4382
|
+
}
|
|
4383
|
+
return null;
|
|
4384
|
+
};
|
|
4385
|
+
const parseSubagentMarkerFromSystemReminder = (text) => {
|
|
4386
|
+
const startTag = "<system-reminder>";
|
|
4387
|
+
const endTag = "</system-reminder>";
|
|
4388
|
+
let searchFrom = 0;
|
|
4389
|
+
while (true) {
|
|
4390
|
+
const reminderStart = text.indexOf(startTag, searchFrom);
|
|
4391
|
+
if (reminderStart === -1) break;
|
|
4392
|
+
const contentStart = reminderStart + 17;
|
|
4393
|
+
const reminderEnd = text.indexOf(endTag, contentStart);
|
|
4394
|
+
if (reminderEnd === -1) break;
|
|
4395
|
+
const reminderContent = text.slice(contentStart, reminderEnd);
|
|
4396
|
+
const markerIndex = reminderContent.indexOf(subagentMarkerPrefix);
|
|
4397
|
+
if (markerIndex === -1) {
|
|
4398
|
+
searchFrom = reminderEnd + 18;
|
|
4399
|
+
continue;
|
|
4400
|
+
}
|
|
4401
|
+
const markerJson = reminderContent.slice(markerIndex + 19).trim();
|
|
4402
|
+
try {
|
|
4403
|
+
const parsed = JSON.parse(markerJson);
|
|
4404
|
+
if (!parsed.session_id || !parsed.agent_id || !parsed.agent_type) {
|
|
4405
|
+
searchFrom = reminderEnd + 18;
|
|
4406
|
+
continue;
|
|
4407
|
+
}
|
|
4408
|
+
return parsed;
|
|
4409
|
+
} catch {
|
|
4410
|
+
searchFrom = reminderEnd + 18;
|
|
4411
|
+
continue;
|
|
4412
|
+
}
|
|
4413
|
+
}
|
|
4414
|
+
return null;
|
|
4415
|
+
};
|
|
4416
|
+
|
|
4338
4417
|
//#endregion
|
|
4339
4418
|
//#region src/routes/messages/handler.ts
|
|
4340
4419
|
const logger$2 = createHandlerLogger("messages-handler");
|
|
@@ -4353,6 +4432,9 @@ async function handleCompletion(c) {
|
|
|
4353
4432
|
const userAgent = c.req.header("user-agent") ?? void 0;
|
|
4354
4433
|
const anthropicPayload = await c.req.json();
|
|
4355
4434
|
logger$2.debug("Anthropic request payload:", JSON.stringify(anthropicPayload));
|
|
4435
|
+
const subagentMarker = parseSubagentMarkerFromFirstUser(anthropicPayload);
|
|
4436
|
+
const initiatorOverride = subagentMarker ? "agent" : void 0;
|
|
4437
|
+
if (subagentMarker) logger$2.debug("Detected Subagent marker:", JSON.stringify(subagentMarker));
|
|
4356
4438
|
const anthropicBeta = c.req.header("anthropic-beta");
|
|
4357
4439
|
const isCompact = isCompactRequest(anthropicPayload);
|
|
4358
4440
|
if (anthropicBeta && isWarmupProbeRequest(anthropicPayload)) anthropicPayload.model = getSmallModel();
|
|
@@ -4381,11 +4463,12 @@ async function handleCompletion(c) {
|
|
|
4381
4463
|
userAgent,
|
|
4382
4464
|
userId,
|
|
4383
4465
|
safetyIdentifier: normalizedSafetyIdentifier,
|
|
4384
|
-
promptCacheKey: normalizedPromptCacheKey
|
|
4466
|
+
promptCacheKey: normalizedPromptCacheKey,
|
|
4467
|
+
initiator: initiatorOverride
|
|
4385
4468
|
});
|
|
4386
4469
|
if (blockedResponse) return blockedResponse;
|
|
4387
4470
|
const openAIPayload = translateToOpenAI(anthropicPayload);
|
|
4388
|
-
const fallbackInitiator = getChatInitiator(openAIPayload.messages);
|
|
4471
|
+
const fallbackInitiator = initiatorOverride ?? getChatInitiator(openAIPayload.messages);
|
|
4389
4472
|
const selection = await accountsManager.selectAccountForRequest([
|
|
4390
4473
|
{
|
|
4391
4474
|
modelId: clientModel,
|
|
@@ -4448,6 +4531,7 @@ async function handleCompletion(c) {
|
|
|
4448
4531
|
c,
|
|
4449
4532
|
anthropicPayload,
|
|
4450
4533
|
anthropicBetaHeader: anthropicBeta ?? void 0,
|
|
4534
|
+
initiatorOverride,
|
|
4451
4535
|
instr,
|
|
4452
4536
|
selectedModel
|
|
4453
4537
|
});
|
|
@@ -4455,27 +4539,32 @@ async function handleCompletion(c) {
|
|
|
4455
4539
|
c,
|
|
4456
4540
|
anthropicPayload,
|
|
4457
4541
|
openAIPayload,
|
|
4542
|
+
initiatorOverride,
|
|
4458
4543
|
selectedModel,
|
|
4459
4544
|
instr
|
|
4460
4545
|
});
|
|
4461
4546
|
return await handleWithChatCompletions({
|
|
4462
4547
|
c,
|
|
4463
4548
|
openAIPayload,
|
|
4549
|
+
initiatorOverride,
|
|
4464
4550
|
selectedModel,
|
|
4465
4551
|
instr
|
|
4466
4552
|
});
|
|
4467
4553
|
}
|
|
4468
4554
|
const handleWithChatCompletions = async (params) => {
|
|
4469
|
-
const { c, openAIPayload, selectedModel, instr } = params;
|
|
4555
|
+
const { c, openAIPayload, initiatorOverride, selectedModel, instr } = params;
|
|
4470
4556
|
logger$2.debug("Translated OpenAI request payload:", JSON.stringify(openAIPayload));
|
|
4471
4557
|
const ctx = toAccountContext(instr.account);
|
|
4472
|
-
const initiator = getChatInitiator(openAIPayload.messages);
|
|
4558
|
+
const initiator = initiatorOverride ?? getChatInitiator(openAIPayload.messages);
|
|
4473
4559
|
const upstreamRequestId = randomUUID();
|
|
4474
4560
|
instr.initiator = initiator;
|
|
4475
4561
|
instr.upstreamRequestId = upstreamRequestId;
|
|
4476
4562
|
let response;
|
|
4477
4563
|
try {
|
|
4478
|
-
response = await createChatCompletions(openAIPayload, ctx, {
|
|
4564
|
+
response = await createChatCompletions(openAIPayload, ctx, {
|
|
4565
|
+
upstreamRequestId,
|
|
4566
|
+
initiator
|
|
4567
|
+
});
|
|
4479
4568
|
} catch (error) {
|
|
4480
4569
|
return await handleChatCompletionsCreateError({
|
|
4481
4570
|
error,
|
|
@@ -4505,19 +4594,20 @@ const handleWithChatCompletions = async (params) => {
|
|
|
4505
4594
|
}));
|
|
4506
4595
|
};
|
|
4507
4596
|
const handleWithResponsesApi = async (params) => {
|
|
4508
|
-
const { c, anthropicPayload, openAIPayload, selectedModel, instr } = params;
|
|
4597
|
+
const { c, anthropicPayload, openAIPayload, initiatorOverride, selectedModel, instr } = params;
|
|
4509
4598
|
const responsesPayload = translateAnthropicMessagesToResponsesPayload(anthropicPayload, selectedModel.id);
|
|
4510
4599
|
logger$2.debug("Translated Responses payload:", JSON.stringify(responsesPayload));
|
|
4511
4600
|
const { vision, initiator } = getResponsesRequestOptions(responsesPayload);
|
|
4601
|
+
const resolvedInitiator = initiatorOverride ?? initiator;
|
|
4512
4602
|
const ctx = toAccountContext(instr.account);
|
|
4513
4603
|
const upstreamRequestId = randomUUID();
|
|
4514
|
-
instr.initiator =
|
|
4604
|
+
instr.initiator = resolvedInitiator;
|
|
4515
4605
|
instr.upstreamRequestId = upstreamRequestId;
|
|
4516
4606
|
let response;
|
|
4517
4607
|
try {
|
|
4518
4608
|
response = await createResponses(responsesPayload, {
|
|
4519
4609
|
vision,
|
|
4520
|
-
initiator,
|
|
4610
|
+
initiator: resolvedInitiator,
|
|
4521
4611
|
upstreamRequestId
|
|
4522
4612
|
}, ctx);
|
|
4523
4613
|
} catch (error) {
|
|
@@ -4969,7 +5059,7 @@ async function streamMessagesAndLog(params) {
|
|
|
4969
5059
|
}
|
|
4970
5060
|
}
|
|
4971
5061
|
const handleWithMessagesApi = async (params) => {
|
|
4972
|
-
const { c, anthropicPayload, anthropicBetaHeader, instr, selectedModel } = params;
|
|
5062
|
+
const { c, anthropicPayload, anthropicBetaHeader, initiatorOverride, instr, selectedModel } = params;
|
|
4973
5063
|
for (const msg of anthropicPayload.messages) if (msg.role === "assistant" && Array.isArray(msg.content)) msg.content = msg.content.filter((block) => {
|
|
4974
5064
|
if (block.type !== "thinking") return true;
|
|
4975
5065
|
return block.thinking && block.thinking !== "Thinking..." && block.signature && !block.signature.includes("@");
|
|
@@ -4981,13 +5071,15 @@ const handleWithMessagesApi = async (params) => {
|
|
|
4981
5071
|
logger$2.debug("Translated Messages payload:", JSON.stringify(anthropicPayload));
|
|
4982
5072
|
const ctx = toAccountContext(instr.account);
|
|
4983
5073
|
const upstreamRequestId = randomUUID();
|
|
4984
|
-
|
|
5074
|
+
const initiator = initiatorOverride ?? getMessagesInitiator(anthropicPayload);
|
|
5075
|
+
instr.initiator = initiator;
|
|
4985
5076
|
instr.upstreamRequestId = upstreamRequestId;
|
|
4986
5077
|
let response;
|
|
4987
5078
|
try {
|
|
4988
5079
|
response = await createMessages(anthropicPayload, ctx, {
|
|
4989
5080
|
anthropicBetaHeader,
|
|
4990
|
-
upstreamRequestId
|
|
5081
|
+
upstreamRequestId,
|
|
5082
|
+
initiator
|
|
4991
5083
|
});
|
|
4992
5084
|
} catch (error) {
|
|
4993
5085
|
return await handleMessagesCreateError({
|
|
@@ -5635,7 +5727,7 @@ usageRoute.get("/:accountIndex", async (c) => {
|
|
|
5635
5727
|
const server = new Hono();
|
|
5636
5728
|
server.use(logger());
|
|
5637
5729
|
server.use(cors());
|
|
5638
|
-
server.use("*",
|
|
5730
|
+
server.use("*", createAuthMiddleware({ allowUnauthenticatedPathPrefixes: ["/admin", "/api/admin"] }));
|
|
5639
5731
|
server.get("/", (c) => c.text("Server running"));
|
|
5640
5732
|
server.route("/chat/completions", completionRoutes);
|
|
5641
5733
|
server.route("/models", modelRoutes);
|
|
@@ -5653,4 +5745,4 @@ server.route("/v1/messages", messageRoutes);
|
|
|
5653
5745
|
|
|
5654
5746
|
//#endregion
|
|
5655
5747
|
export { server };
|
|
5656
|
-
//# sourceMappingURL=server-
|
|
5748
|
+
//# sourceMappingURL=server-6hnk3aRJ.js.map
|