@nick3/copilot-api 1.4.9 → 1.5.3
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 +5 -3
- package/dist/account-BMFr8t9b.js +362 -0
- package/dist/account-BMFr8t9b.js.map +1 -0
- package/dist/{accounts-manager-Cjrd_el_.js → accounts-manager-DgQH4KtO.js} +114 -13
- package/dist/accounts-manager-DgQH4KtO.js.map +1 -0
- package/dist/admin/assets/index-519a65q_.js +66 -0
- package/dist/admin/assets/index-ChMaMig2.css +1 -0
- package/dist/admin/index.html +2 -2
- package/dist/{auth-1gAffrpI.js → auth-DipSy-jV.js} +6 -12
- package/dist/auth-DipSy-jV.js.map +1 -0
- package/dist/{check-usage-CsRu467P.js → check-usage-Dbjs73E0.js} +5 -5
- package/dist/check-usage-Dbjs73E0.js.map +1 -0
- package/dist/{debug-BzR5ZQUk.js → debug-BJfZVBB7.js} +2 -2
- package/dist/{debug-BzR5ZQUk.js.map → debug-BJfZVBB7.js.map} +1 -1
- package/dist/{get-copilot-token-BbpphnmV.js → get-copilot-token-DoK8Ia3u.js} +2 -2
- package/dist/{get-copilot-token-BbpphnmV.js.map → get-copilot-token-DoK8Ia3u.js.map} +1 -1
- package/dist/main.js +4 -4
- package/dist/{paths-Cvzy-eLX.js → paths-DGlr310R.js} +2 -2
- package/dist/{paths-Cvzy-eLX.js.map → paths-DGlr310R.js.map} +1 -1
- package/dist/{utils-DY-jLXwO.js → poll-access-token-BzenaGHn.js} +352 -52
- package/dist/poll-access-token-BzenaGHn.js.map +1 -0
- package/dist/{server-DqwhClJ-.js → server-DzE_zmUf.js} +498 -136
- package/dist/server-DzE_zmUf.js.map +1 -0
- package/dist/{start-B1_Ols5Z.js → start-DEugVHqn.js} +26 -15
- package/dist/start-DEugVHqn.js.map +1 -0
- package/package.json +1 -1
- package/dist/account-CipKmikF.js +0 -17
- package/dist/account-CipKmikF.js.map +0 -1
- package/dist/accounts-manager-Cjrd_el_.js.map +0 -1
- package/dist/accounts-registry-CQYvRe65.js +0 -180
- package/dist/accounts-registry-CQYvRe65.js.map +0 -1
- package/dist/admin/assets/index-BFvCJZIK.js +0 -57
- package/dist/admin/assets/index-CsAeel_7.css +0 -1
- package/dist/auth-1gAffrpI.js.map +0 -1
- package/dist/check-usage-CsRu467P.js.map +0 -1
- package/dist/poll-access-token-CGfLFzMq.js +0 -52
- package/dist/poll-access-token-CGfLFzMq.js.map +0 -1
- package/dist/server-DqwhClJ-.js.map +0 -1
- package/dist/start-B1_Ols5Z.js.map +0 -1
- package/dist/utils-DY-jLXwO.js.map +0 -1
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { t as
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import "./get-copilot-token-
|
|
5
|
-
import { _ as isMessagesApiEnabled, a as getClaudeTokenMultiplier, b as mergeConfigWithDefaults, c as getModelAliases, d as getProviderConfig, f as getReasoningEffortForModel, g as isMessageStartInputTokensFallbackEnabled, h as isForceAgentEnabled, i as getAnthropicApiKey, l as getModelAliasesInfo, m as isAccountAffinityEnabled, n as PROVIDER_TYPE_ANTHROPIC, o as getConfig, p as getSmallModel, r as getAliasTargetSet, s as getExtraPromptForModel, t as accountsManager, u as getModelRefreshIntervalMs, v as isResponsesApiContextManagementModel, x as shouldCompactUseSmallModel, y as isResponsesApiWebSearchEnabled } from "./accounts-manager-
|
|
1
|
+
import { A as resolveTraceId, C as normalizeDomain, D as accountFromState, E as prepareMessageProxyHeaders, O as state, T as prepareInteractionHeaders, _ as HTTPError, b as copilotHeaders, c as getRootSessionId, d as parseUserIdMetadata, f as sleep, g as getCopilotUsage, h as getDeviceCode, k as requestContext, l as getUUID, m as getGitHubUser, r as cacheModels, s as generateRequestIdFromPayload, t as pollAccessToken, u as isNullish, v as forwardError, w as prepareForCompact, y as copilotBaseUrl } from "./poll-access-token-BzenaGHn.js";
|
|
2
|
+
import { a as getAccountClientIdentityByLoginAndApp, c as listAccountsFromRegistry, f as removeAccountFromRegistry, g as DEFAULT_IDENTITY_ENTERPRISE_DOMAIN, h as saveRegistry, m as saveAccountToken, p as removeAccountToken, r as addAccountToRegistry, t as isAccountType, u as loadRegistry, y as getCurrentIdentityEnvironment } from "./account-BMFr8t9b.js";
|
|
3
|
+
import { r as ensurePaths, t as PATHS } from "./paths-DGlr310R.js";
|
|
4
|
+
import "./get-copilot-token-DoK8Ia3u.js";
|
|
5
|
+
import { _ as isMessagesApiEnabled, a as getClaudeTokenMultiplier, b as mergeConfigWithDefaults, c as getModelAliases, d as getProviderConfig, f as getReasoningEffortForModel, g as isMessageStartInputTokensFallbackEnabled, h as isForceAgentEnabled, i as getAnthropicApiKey, l as getModelAliasesInfo, m as isAccountAffinityEnabled, n as PROVIDER_TYPE_ANTHROPIC, o as getConfig, p as getSmallModel, r as getAliasTargetSet, s as getExtraPromptForModel, t as accountsManager, u as getModelRefreshIntervalMs, v as isResponsesApiContextManagementModel, x as shouldCompactUseSmallModel, y as isResponsesApiWebSearchEnabled } from "./accounts-manager-DgQH4KtO.js";
|
|
6
6
|
import consola from "consola";
|
|
7
7
|
import fs, { readFile } from "node:fs/promises";
|
|
8
|
+
import { randomUUID, timingSafeEqual } from "node:crypto";
|
|
8
9
|
import * as path$1 from "node:path";
|
|
9
10
|
import path from "node:path";
|
|
10
|
-
import { randomUUID, timingSafeEqual } from "node:crypto";
|
|
11
11
|
import { Hono } from "hono";
|
|
12
12
|
import { cors } from "hono/cors";
|
|
13
13
|
import { logger } from "hono/logger";
|
|
@@ -112,7 +112,9 @@ const traceIdMiddleware = async (c, next) => {
|
|
|
112
112
|
const context = {
|
|
113
113
|
traceId,
|
|
114
114
|
startTime: Date.now(),
|
|
115
|
-
userAgent: c.req.header("user-agent") || ""
|
|
115
|
+
userAgent: c.req.header("user-agent") || "",
|
|
116
|
+
sessionAffinity: c.req.header("x-session-affinity"),
|
|
117
|
+
parentSessionId: c.req.header("x-parent-session-id")
|
|
116
118
|
};
|
|
117
119
|
await requestContext.run(context, async () => {
|
|
118
120
|
await next();
|
|
@@ -690,6 +692,162 @@ function extractResponsesUsageFromResult(result) {
|
|
|
690
692
|
return normalizeResponsesUsage(result.usage);
|
|
691
693
|
}
|
|
692
694
|
|
|
695
|
+
//#endregion
|
|
696
|
+
//#region src/routes/admin-api/auth-sessions.ts
|
|
697
|
+
function buildOauthUrls(enterpriseDomain) {
|
|
698
|
+
if (!enterpriseDomain) return void 0;
|
|
699
|
+
const domain = normalizeDomain(enterpriseDomain);
|
|
700
|
+
if (!domain) return void 0;
|
|
701
|
+
return {
|
|
702
|
+
deviceCodeUrl: `https://${domain}/login/device/code`,
|
|
703
|
+
accessTokenUrl: `https://${domain}/login/oauth/access_token`
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
const CLEANUP_INTERVAL_MS$1 = 6e4;
|
|
707
|
+
var AuthSessionManager = class {
|
|
708
|
+
sessions = /* @__PURE__ */ new Map();
|
|
709
|
+
cleanupTimer = null;
|
|
710
|
+
start() {
|
|
711
|
+
if (this.cleanupTimer) return;
|
|
712
|
+
this.cleanupTimer = setInterval(() => this.cleanupExpired(), CLEANUP_INTERVAL_MS$1);
|
|
713
|
+
}
|
|
714
|
+
stop() {
|
|
715
|
+
if (this.cleanupTimer) {
|
|
716
|
+
clearInterval(this.cleanupTimer);
|
|
717
|
+
this.cleanupTimer = null;
|
|
718
|
+
}
|
|
719
|
+
for (const session of this.sessions.values()) session.abortController.abort();
|
|
720
|
+
this.sessions.clear();
|
|
721
|
+
}
|
|
722
|
+
async startAuth(params) {
|
|
723
|
+
const enterpriseDomain = params.enterpriseDomain ? normalizeDomain(params.enterpriseDomain) : null;
|
|
724
|
+
const overrideUrls = buildOauthUrls(enterpriseDomain);
|
|
725
|
+
await ensurePaths();
|
|
726
|
+
const deviceResponse = await getDeviceCode({ overrideUrls });
|
|
727
|
+
const sessionId = randomUUID();
|
|
728
|
+
const abortController = new AbortController();
|
|
729
|
+
const expiresAt = Date.now() + deviceResponse.expires_in * 1e3;
|
|
730
|
+
const session = {
|
|
731
|
+
sessionId,
|
|
732
|
+
accountType: params.accountType,
|
|
733
|
+
enterpriseDomain,
|
|
734
|
+
status: "pending",
|
|
735
|
+
userCode: deviceResponse.user_code,
|
|
736
|
+
verificationUri: deviceResponse.verification_uri,
|
|
737
|
+
expiresAt,
|
|
738
|
+
interval: deviceResponse.interval,
|
|
739
|
+
abortController,
|
|
740
|
+
reauthAccountId: params.reauthAccountId ?? null
|
|
741
|
+
};
|
|
742
|
+
this.sessions.set(sessionId, session);
|
|
743
|
+
this.runAuthFlow(session, deviceResponse, overrideUrls);
|
|
744
|
+
return {
|
|
745
|
+
sessionId,
|
|
746
|
+
userCode: deviceResponse.user_code,
|
|
747
|
+
verificationUri: deviceResponse.verification_uri,
|
|
748
|
+
expiresIn: deviceResponse.expires_in,
|
|
749
|
+
interval: deviceResponse.interval
|
|
750
|
+
};
|
|
751
|
+
}
|
|
752
|
+
getStatus(sessionId) {
|
|
753
|
+
const session = this.sessions.get(sessionId);
|
|
754
|
+
if (!session) return null;
|
|
755
|
+
if (session.status === "pending" && Date.now() >= session.expiresAt) {
|
|
756
|
+
session.status = "expired";
|
|
757
|
+
session.abortController.abort();
|
|
758
|
+
}
|
|
759
|
+
return {
|
|
760
|
+
status: session.status,
|
|
761
|
+
accountId: session.accountId,
|
|
762
|
+
error: session.error
|
|
763
|
+
};
|
|
764
|
+
}
|
|
765
|
+
cancel(sessionId) {
|
|
766
|
+
const session = this.sessions.get(sessionId);
|
|
767
|
+
if (!session) return false;
|
|
768
|
+
session.abortController.abort();
|
|
769
|
+
this.sessions.delete(sessionId);
|
|
770
|
+
return true;
|
|
771
|
+
}
|
|
772
|
+
getLiveSession(sessionId) {
|
|
773
|
+
const session = this.sessions.get(sessionId);
|
|
774
|
+
if (!session || session.abortController.signal.aborted) return null;
|
|
775
|
+
return session;
|
|
776
|
+
}
|
|
777
|
+
async runAuthFlow(session, deviceResponse, overrideUrls) {
|
|
778
|
+
try {
|
|
779
|
+
const sessionId = session.sessionId;
|
|
780
|
+
const signal = session.abortController.signal;
|
|
781
|
+
const token = await pollAccessToken(deviceResponse, {
|
|
782
|
+
overrideUrls,
|
|
783
|
+
signal
|
|
784
|
+
});
|
|
785
|
+
if (!this.getLiveSession(sessionId)) return;
|
|
786
|
+
const user = await getGitHubUser({
|
|
787
|
+
githubToken: token,
|
|
788
|
+
accountType: session.accountType
|
|
789
|
+
});
|
|
790
|
+
if (!this.getLiveSession(sessionId)) return;
|
|
791
|
+
const accountId = user.login;
|
|
792
|
+
if (session.reauthAccountId && session.reauthAccountId !== accountId) {
|
|
793
|
+
this.failSession(sessionId, `Authenticated as "${accountId}" but expected "${session.reauthAccountId}". Use "Add Account" to add a different account.`);
|
|
794
|
+
return;
|
|
795
|
+
}
|
|
796
|
+
if (!this.getLiveSession(sessionId)) return;
|
|
797
|
+
await saveAccountToken(accountId, token);
|
|
798
|
+
if (!this.getLiveSession(sessionId)) return;
|
|
799
|
+
if (!this.getLiveSession(sessionId)) return;
|
|
800
|
+
const existingAccounts = await listAccountsFromRegistry();
|
|
801
|
+
if (!this.getLiveSession(sessionId)) return;
|
|
802
|
+
if (existingAccounts.some((acc) => acc.id === accountId)) {
|
|
803
|
+
const registry = await loadRegistry();
|
|
804
|
+
if (!this.getLiveSession(sessionId)) return;
|
|
805
|
+
await saveRegistry(registry);
|
|
806
|
+
} else {
|
|
807
|
+
if (!this.getLiveSession(sessionId)) return;
|
|
808
|
+
await addAccountToRegistry({
|
|
809
|
+
id: accountId,
|
|
810
|
+
accountType: session.accountType,
|
|
811
|
+
addedAt: Date.now()
|
|
812
|
+
});
|
|
813
|
+
}
|
|
814
|
+
if (!this.getLiveSession(sessionId)) return;
|
|
815
|
+
this.completeSession(sessionId, accountId);
|
|
816
|
+
} catch (error) {
|
|
817
|
+
if (session.abortController.signal.aborted) return;
|
|
818
|
+
this.failSession(session.sessionId, error instanceof Error ? error.message : String(error));
|
|
819
|
+
consola.error(`Auth session ${session.sessionId} failed:`, error);
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
completeSession(sessionId, accountId) {
|
|
823
|
+
const session = this.sessions.get(sessionId);
|
|
824
|
+
if (!session || session.abortController.signal.aborted) return;
|
|
825
|
+
session.status = "completed";
|
|
826
|
+
session.accountId = accountId;
|
|
827
|
+
}
|
|
828
|
+
failSession(sessionId, error) {
|
|
829
|
+
const session = this.sessions.get(sessionId);
|
|
830
|
+
if (!session || session.abortController.signal.aborted) return;
|
|
831
|
+
session.status = "failed";
|
|
832
|
+
session.error = error;
|
|
833
|
+
}
|
|
834
|
+
cleanupExpired() {
|
|
835
|
+
const now = Date.now();
|
|
836
|
+
const cleanupThreshold = 5 * 6e4;
|
|
837
|
+
for (const [id, session] of this.sessions) {
|
|
838
|
+
if (session.status !== "pending" && now - session.expiresAt > cleanupThreshold) {
|
|
839
|
+
session.abortController.abort();
|
|
840
|
+
this.sessions.delete(id);
|
|
841
|
+
}
|
|
842
|
+
if (session.status === "pending" && now >= session.expiresAt) {
|
|
843
|
+
session.status = "expired";
|
|
844
|
+
session.abortController.abort();
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
};
|
|
849
|
+
const authSessionManager = new AuthSessionManager();
|
|
850
|
+
|
|
693
851
|
//#endregion
|
|
694
852
|
//#region src/routes/admin-api/route.ts
|
|
695
853
|
const ADMIN_TOKEN = process.env.ADMIN_TOKEN?.trim() || void 0;
|
|
@@ -1166,6 +1324,7 @@ adminApiRoutes.use("*", async (c, next) => {
|
|
|
1166
1324
|
} }, decision.status);
|
|
1167
1325
|
await next();
|
|
1168
1326
|
});
|
|
1327
|
+
authSessionManager.start();
|
|
1169
1328
|
adminApiRoutes.get("/meta", (c) => {
|
|
1170
1329
|
const store = getRequestHistoryStore();
|
|
1171
1330
|
return c.json(store.meta());
|
|
@@ -1395,6 +1554,115 @@ adminApiRoutes.get("/requests/:requestId", (c) => {
|
|
|
1395
1554
|
const item = getRequestHistoryStore().getByRequestId(requestId);
|
|
1396
1555
|
return c.json({ item });
|
|
1397
1556
|
});
|
|
1557
|
+
adminApiRoutes.post("/accounts/auth/start", async (c) => {
|
|
1558
|
+
let payload;
|
|
1559
|
+
try {
|
|
1560
|
+
payload = await c.req.json();
|
|
1561
|
+
} catch {
|
|
1562
|
+
return jsonError(c, 400, {
|
|
1563
|
+
message: "Request body must be valid JSON.",
|
|
1564
|
+
type: "bad_request"
|
|
1565
|
+
});
|
|
1566
|
+
}
|
|
1567
|
+
if (!isPlainObject(payload)) return jsonError(c, 400, {
|
|
1568
|
+
message: "Request body must be an object.",
|
|
1569
|
+
type: "bad_request"
|
|
1570
|
+
});
|
|
1571
|
+
const accountType = payload.accountType;
|
|
1572
|
+
if (!isAccountType(accountType)) return jsonError(c, 400, {
|
|
1573
|
+
message: "accountType must be one of: individual, business, enterprise",
|
|
1574
|
+
type: "bad_request"
|
|
1575
|
+
});
|
|
1576
|
+
const enterpriseDomainRaw = payload.enterpriseDomain;
|
|
1577
|
+
let enterpriseDomain;
|
|
1578
|
+
if (accountType === "enterprise" && typeof enterpriseDomainRaw === "string") enterpriseDomain = enterpriseDomainRaw.trim();
|
|
1579
|
+
if (accountType === "enterprise" && !enterpriseDomain) return jsonError(c, 400, {
|
|
1580
|
+
message: "enterpriseDomain is required for enterprise accounts.",
|
|
1581
|
+
type: "bad_request"
|
|
1582
|
+
});
|
|
1583
|
+
try {
|
|
1584
|
+
const result = await authSessionManager.startAuth({
|
|
1585
|
+
accountType,
|
|
1586
|
+
enterpriseDomain
|
|
1587
|
+
});
|
|
1588
|
+
return c.json(result);
|
|
1589
|
+
} catch (error) {
|
|
1590
|
+
return jsonError(c, 500, {
|
|
1591
|
+
message: `Failed to start auth: ${error instanceof Error ? error.message : String(error)}`,
|
|
1592
|
+
type: "internal_error"
|
|
1593
|
+
});
|
|
1594
|
+
}
|
|
1595
|
+
});
|
|
1596
|
+
adminApiRoutes.get("/accounts/auth/status/:sessionId", (c) => {
|
|
1597
|
+
const sessionId = c.req.param("sessionId");
|
|
1598
|
+
const status = authSessionManager.getStatus(sessionId);
|
|
1599
|
+
if (!status) return jsonError(c, 404, {
|
|
1600
|
+
message: "Session not found.",
|
|
1601
|
+
type: "not_found"
|
|
1602
|
+
});
|
|
1603
|
+
return c.json(status);
|
|
1604
|
+
});
|
|
1605
|
+
adminApiRoutes.post("/accounts/auth/cancel/:sessionId", (c) => {
|
|
1606
|
+
const sessionId = c.req.param("sessionId");
|
|
1607
|
+
if (!authSessionManager.cancel(sessionId)) return jsonError(c, 404, {
|
|
1608
|
+
message: "Session not found.",
|
|
1609
|
+
type: "not_found"
|
|
1610
|
+
});
|
|
1611
|
+
return c.json({ cancelled: true });
|
|
1612
|
+
});
|
|
1613
|
+
adminApiRoutes.delete("/accounts/:id", async (c) => {
|
|
1614
|
+
const accountId = c.req.param("id");
|
|
1615
|
+
try {
|
|
1616
|
+
if (!(await listAccountsFromRegistry()).some((a) => a.id === accountId)) return jsonError(c, 404, {
|
|
1617
|
+
message: "Account not found.",
|
|
1618
|
+
type: "not_found"
|
|
1619
|
+
});
|
|
1620
|
+
await removeAccountFromRegistry(accountId);
|
|
1621
|
+
try {
|
|
1622
|
+
await removeAccountToken(accountId);
|
|
1623
|
+
} catch (error) {
|
|
1624
|
+
console.error(`Account ${accountId} deleted but token cleanup failed.`, error);
|
|
1625
|
+
}
|
|
1626
|
+
return c.json({
|
|
1627
|
+
deleted: true,
|
|
1628
|
+
accountId
|
|
1629
|
+
});
|
|
1630
|
+
} catch (error) {
|
|
1631
|
+
return jsonError(c, 500, {
|
|
1632
|
+
message: `Failed to delete account: ${error instanceof Error ? error.message : String(error)}`,
|
|
1633
|
+
type: "internal_error"
|
|
1634
|
+
});
|
|
1635
|
+
}
|
|
1636
|
+
});
|
|
1637
|
+
adminApiRoutes.post("/accounts/:id/reauth", async (c) => {
|
|
1638
|
+
const accountId = c.req.param("id");
|
|
1639
|
+
try {
|
|
1640
|
+
const account = (await listAccountsFromRegistry()).find((a) => a.id === accountId);
|
|
1641
|
+
if (!account) return jsonError(c, 404, {
|
|
1642
|
+
message: "Account not found.",
|
|
1643
|
+
type: "not_found"
|
|
1644
|
+
});
|
|
1645
|
+
const { oauthApp } = getCurrentIdentityEnvironment();
|
|
1646
|
+
const resolvedEnterpriseDomain = (await getAccountClientIdentityByLoginAndApp(accountId, oauthApp))?.enterpriseDomain;
|
|
1647
|
+
let enterpriseDomain;
|
|
1648
|
+
if (resolvedEnterpriseDomain && resolvedEnterpriseDomain !== DEFAULT_IDENTITY_ENTERPRISE_DOMAIN) enterpriseDomain = resolvedEnterpriseDomain;
|
|
1649
|
+
if (account.accountType === "enterprise" && !enterpriseDomain) return jsonError(c, 400, {
|
|
1650
|
+
message: "Cannot re-authenticate enterprise account: enterprise domain could not be resolved from stored identity.",
|
|
1651
|
+
type: "bad_request"
|
|
1652
|
+
});
|
|
1653
|
+
const result = await authSessionManager.startAuth({
|
|
1654
|
+
accountType: account.accountType,
|
|
1655
|
+
enterpriseDomain,
|
|
1656
|
+
reauthAccountId: accountId
|
|
1657
|
+
});
|
|
1658
|
+
return c.json(result);
|
|
1659
|
+
} catch (error) {
|
|
1660
|
+
return jsonError(c, 500, {
|
|
1661
|
+
message: `Failed to start reauth: ${error instanceof Error ? error.message : String(error)}`,
|
|
1662
|
+
type: "internal_error"
|
|
1663
|
+
});
|
|
1664
|
+
}
|
|
1665
|
+
});
|
|
1398
1666
|
|
|
1399
1667
|
//#endregion
|
|
1400
1668
|
//#region src/routes/admin/route.ts
|
|
@@ -2053,10 +2321,15 @@ function computeDiff(before, after) {
|
|
|
2053
2321
|
}
|
|
2054
2322
|
function toAccountContext(account) {
|
|
2055
2323
|
return {
|
|
2324
|
+
accountLogin: account.accountLogin,
|
|
2056
2325
|
githubToken: account.githubToken,
|
|
2057
2326
|
copilotToken: account.copilotToken,
|
|
2327
|
+
...account.copilotApiUrl !== void 0 ? { copilotApiUrl: account.copilotApiUrl } : {},
|
|
2058
2328
|
accountType: account.accountType,
|
|
2059
|
-
vsCodeVersion: account.vsCodeVersion
|
|
2329
|
+
vsCodeVersion: account.vsCodeVersion,
|
|
2330
|
+
clientDeviceId: account.clientDeviceId,
|
|
2331
|
+
clientMachineId: account.clientMachineId,
|
|
2332
|
+
clientSessionId: account.clientSessionId
|
|
2060
2333
|
};
|
|
2061
2334
|
}
|
|
2062
2335
|
function extractErrorDetails(error) {
|
|
@@ -2081,6 +2354,9 @@ const FLUSH_INTERVAL_MS = 1e3;
|
|
|
2081
2354
|
const MAX_BUFFER_SIZE = 100;
|
|
2082
2355
|
const logStreams = /* @__PURE__ */ new Map();
|
|
2083
2356
|
const logBuffers = /* @__PURE__ */ new Map();
|
|
2357
|
+
let runtimeInitialized = false;
|
|
2358
|
+
let flushInterval;
|
|
2359
|
+
let cleanupInterval;
|
|
2084
2360
|
const ensureLogDirectory = () => {
|
|
2085
2361
|
if (!fs$1.existsSync(LOG_DIR)) fs$1.mkdirSync(LOG_DIR, { recursive: true });
|
|
2086
2362
|
};
|
|
@@ -2111,17 +2387,8 @@ const sanitizeName = (name) => {
|
|
|
2111
2387
|
const normalized = name.toLowerCase().replaceAll(/[^a-z0-9]+/g, "-").replaceAll(/^-+|-+$/g, "");
|
|
2112
2388
|
return normalized === "" ? "handler" : normalized;
|
|
2113
2389
|
};
|
|
2114
|
-
const
|
|
2115
|
-
|
|
2116
|
-
if (!stream || stream.destroyed) {
|
|
2117
|
-
stream = fs$1.createWriteStream(filePath, { flags: "a" });
|
|
2118
|
-
logStreams.set(filePath, stream);
|
|
2119
|
-
stream.on("error", (error) => {
|
|
2120
|
-
console.warn("Log stream error", error);
|
|
2121
|
-
logStreams.delete(filePath);
|
|
2122
|
-
});
|
|
2123
|
-
}
|
|
2124
|
-
return stream;
|
|
2390
|
+
const maybeUnref = (timer) => {
|
|
2391
|
+
timer.unref();
|
|
2125
2392
|
};
|
|
2126
2393
|
const flushBuffer = (filePath) => {
|
|
2127
2394
|
const buffer = logBuffers.get(filePath);
|
|
@@ -2136,6 +2403,52 @@ const flushBuffer = (filePath) => {
|
|
|
2136
2403
|
const flushAllBuffers = () => {
|
|
2137
2404
|
for (const filePath of logBuffers.keys()) flushBuffer(filePath);
|
|
2138
2405
|
};
|
|
2406
|
+
const cleanup = () => {
|
|
2407
|
+
if (flushInterval) {
|
|
2408
|
+
clearInterval(flushInterval);
|
|
2409
|
+
flushInterval = void 0;
|
|
2410
|
+
}
|
|
2411
|
+
if (cleanupInterval) {
|
|
2412
|
+
clearInterval(cleanupInterval);
|
|
2413
|
+
cleanupInterval = void 0;
|
|
2414
|
+
}
|
|
2415
|
+
flushAllBuffers();
|
|
2416
|
+
for (const stream of logStreams.values()) stream.end();
|
|
2417
|
+
logStreams.clear();
|
|
2418
|
+
logBuffers.clear();
|
|
2419
|
+
};
|
|
2420
|
+
const initializeLoggerRuntime = () => {
|
|
2421
|
+
if (runtimeInitialized) return;
|
|
2422
|
+
runtimeInitialized = true;
|
|
2423
|
+
ensureLogDirectory();
|
|
2424
|
+
cleanupOldLogs();
|
|
2425
|
+
flushInterval = setInterval(flushAllBuffers, FLUSH_INTERVAL_MS);
|
|
2426
|
+
maybeUnref(flushInterval);
|
|
2427
|
+
cleanupInterval = setInterval(cleanupOldLogs, CLEANUP_INTERVAL_MS);
|
|
2428
|
+
maybeUnref(cleanupInterval);
|
|
2429
|
+
process.once("exit", cleanup);
|
|
2430
|
+
process.once("SIGINT", () => {
|
|
2431
|
+
cleanup();
|
|
2432
|
+
process.exit(0);
|
|
2433
|
+
});
|
|
2434
|
+
process.once("SIGTERM", () => {
|
|
2435
|
+
cleanup();
|
|
2436
|
+
process.exit(0);
|
|
2437
|
+
});
|
|
2438
|
+
};
|
|
2439
|
+
const getLogStream = (filePath) => {
|
|
2440
|
+
initializeLoggerRuntime();
|
|
2441
|
+
let stream = logStreams.get(filePath);
|
|
2442
|
+
if (!stream || stream.destroyed) {
|
|
2443
|
+
stream = fs$1.createWriteStream(filePath, { flags: "a" });
|
|
2444
|
+
logStreams.set(filePath, stream);
|
|
2445
|
+
stream.on("error", (error) => {
|
|
2446
|
+
console.warn("Log stream error", error);
|
|
2447
|
+
logStreams.delete(filePath);
|
|
2448
|
+
});
|
|
2449
|
+
}
|
|
2450
|
+
return stream;
|
|
2451
|
+
};
|
|
2139
2452
|
const appendLine = (filePath, line) => {
|
|
2140
2453
|
let buffer = logBuffers.get(filePath);
|
|
2141
2454
|
if (!buffer) {
|
|
@@ -2145,35 +2458,23 @@ const appendLine = (filePath, line) => {
|
|
|
2145
2458
|
buffer.push(line);
|
|
2146
2459
|
if (buffer.length >= MAX_BUFFER_SIZE) flushBuffer(filePath);
|
|
2147
2460
|
};
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2461
|
+
const debugLazy = (logger$7, factory) => {
|
|
2462
|
+
if (!state.verbose) return;
|
|
2463
|
+
logger$7.debug(...factory());
|
|
2464
|
+
};
|
|
2465
|
+
const debugJson = (logger$7, label, value) => {
|
|
2466
|
+
debugLazy(logger$7, () => [label, JSON.stringify(value)]);
|
|
2467
|
+
};
|
|
2468
|
+
const debugJsonTail = (logger$7, label, { value, tailLength = 400 }) => {
|
|
2469
|
+
debugLazy(logger$7, () => [label, JSON.stringify(value).slice(-tailLength)]);
|
|
2154
2470
|
};
|
|
2155
|
-
process.on("exit", cleanup);
|
|
2156
|
-
process.on("SIGINT", () => {
|
|
2157
|
-
cleanup();
|
|
2158
|
-
process.exit(0);
|
|
2159
|
-
});
|
|
2160
|
-
process.on("SIGTERM", () => {
|
|
2161
|
-
cleanup();
|
|
2162
|
-
process.exit(0);
|
|
2163
|
-
});
|
|
2164
|
-
let lastCleanup = 0;
|
|
2165
2471
|
const createHandlerLogger = (name) => {
|
|
2166
|
-
ensureLogDirectory();
|
|
2167
2472
|
const sanitizedName = sanitizeName(name);
|
|
2168
2473
|
const instance = consola.withTag(name);
|
|
2169
2474
|
if (state.verbose) instance.level = 5;
|
|
2170
2475
|
instance.setReporters([]);
|
|
2171
2476
|
instance.addReporter({ log(logObj) {
|
|
2172
|
-
|
|
2173
|
-
if (Date.now() - lastCleanup > CLEANUP_INTERVAL_MS) {
|
|
2174
|
-
cleanupOldLogs();
|
|
2175
|
-
lastCleanup = Date.now();
|
|
2176
|
-
}
|
|
2477
|
+
initializeLoggerRuntime();
|
|
2177
2478
|
const traceId = requestContext.getStore()?.traceId;
|
|
2178
2479
|
const date = logObj.date;
|
|
2179
2480
|
const dateKey = date.toLocaleDateString("sv-SE");
|
|
@@ -2503,7 +2804,10 @@ async function handleCompletion$1(c) {
|
|
|
2503
2804
|
reason: "MODEL_NOT_SUPPORTED"
|
|
2504
2805
|
});
|
|
2505
2806
|
}
|
|
2506
|
-
logger$6
|
|
2807
|
+
debugJsonTail(logger$6, "Request payload:", {
|
|
2808
|
+
value: payload,
|
|
2809
|
+
tailLength: 400
|
|
2810
|
+
});
|
|
2507
2811
|
const upstreamRequestId = generateRequestIdFromPayload(payload, normalizedPromptCacheKey);
|
|
2508
2812
|
const selection = await accountsManager.selectAccountForRequest([{
|
|
2509
2813
|
modelId: clientModel,
|
|
@@ -2636,7 +2940,7 @@ function applyDefaultMaxTokens(payload, selectedModel) {
|
|
|
2636
2940
|
...payload,
|
|
2637
2941
|
max_tokens: selectedModel.capabilities.limits.max_output_tokens
|
|
2638
2942
|
};
|
|
2639
|
-
logger$6
|
|
2943
|
+
debugJson(logger$6, "Set max_tokens to:", updated.max_tokens);
|
|
2640
2944
|
return updated;
|
|
2641
2945
|
}
|
|
2642
2946
|
async function handleStreamingRequest(params) {
|
|
@@ -2722,7 +3026,7 @@ async function handleNonStreamingUpstreamResponse(params) {
|
|
|
2722
3026
|
let errorMessage;
|
|
2723
3027
|
const finishedAtMs = Date.now();
|
|
2724
3028
|
try {
|
|
2725
|
-
logger$6
|
|
3029
|
+
debugJson(logger$6, "Non-streaming response:", response);
|
|
2726
3030
|
return c.json(response);
|
|
2727
3031
|
} catch (error) {
|
|
2728
3032
|
const details = extractErrorDetails(error);
|
|
@@ -2772,7 +3076,7 @@ async function streamChatCompletionsAndLog$1(params) {
|
|
|
2772
3076
|
if (ttfbMs === void 0) ttfbMs = Date.now() - request.startedAtMs;
|
|
2773
3077
|
const usage = await extractUsageFromChunk(chunk);
|
|
2774
3078
|
if (usage) lastUsage = usage;
|
|
2775
|
-
logger$6
|
|
3079
|
+
debugJson(logger$6, "Streaming chunk:", chunk);
|
|
2776
3080
|
await stream.writeSSE(chunk);
|
|
2777
3081
|
}
|
|
2778
3082
|
} catch (error) {
|
|
@@ -2844,7 +3148,7 @@ async function handleNonStreamingRequest(params) {
|
|
|
2844
3148
|
});
|
|
2845
3149
|
}
|
|
2846
3150
|
usage = normalizeChatCompletionsUsage(response.usage);
|
|
2847
|
-
logger$6
|
|
3151
|
+
debugJson(logger$6, "Non-streaming response:", response);
|
|
2848
3152
|
return c.json(response);
|
|
2849
3153
|
} catch (error) {
|
|
2850
3154
|
finishedAtMs = Date.now();
|
|
@@ -3110,43 +3414,72 @@ const _normalizeSdkModelId = (sdkModelId) => {
|
|
|
3110
3414
|
};
|
|
3111
3415
|
|
|
3112
3416
|
//#endregion
|
|
3113
|
-
//#region src/routes/messages/
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3417
|
+
//#region src/routes/messages/preprocess.ts
|
|
3418
|
+
const compactSystemPromptStart = "You are a helpful AI assistant tasked with summarizing conversations";
|
|
3419
|
+
const compactTextOnlyGuard = "CRITICAL: Respond with TEXT ONLY. Do NOT call any tools.";
|
|
3420
|
+
const compactSummaryPromptStart = "Your task is to create a detailed summary of the conversation so far";
|
|
3421
|
+
const compactMessageSections = ["Pending Tasks:", "Current Work:"];
|
|
3422
|
+
const TOOL_REFERENCE_TURN_BOUNDARY = "Tool loaded.";
|
|
3423
|
+
const getAnthropicEffortForModel = (model) => {
|
|
3424
|
+
const reasoningEffort = getReasoningEffortForModel(model);
|
|
3425
|
+
if (reasoningEffort === "xhigh") return "max";
|
|
3426
|
+
if (reasoningEffort === "none" || reasoningEffort === "minimal") return "low";
|
|
3427
|
+
return reasoningEffort;
|
|
3428
|
+
};
|
|
3429
|
+
const getCompactCandidateText = (message) => {
|
|
3430
|
+
if (message.role !== "user") return "";
|
|
3431
|
+
if (typeof message.content === "string") return message.content;
|
|
3432
|
+
return message.content.filter((block) => block.type === "text").map((block) => block.text.startsWith("<system-reminder>") ? "" : block.text).filter((text) => text.length > 0).join("\n\n");
|
|
3433
|
+
};
|
|
3434
|
+
const isCompactMessage = (lastMessage) => {
|
|
3435
|
+
const text = getCompactCandidateText(lastMessage);
|
|
3436
|
+
if (!text) return false;
|
|
3437
|
+
return text.includes(compactTextOnlyGuard) && text.includes(compactSummaryPromptStart) && compactMessageSections.some((section) => text.includes(section));
|
|
3438
|
+
};
|
|
3439
|
+
const isCompactRequest = (anthropicPayload) => {
|
|
3440
|
+
const lastMessage = anthropicPayload.messages.at(-1);
|
|
3441
|
+
if (lastMessage && isCompactMessage(lastMessage)) return true;
|
|
3442
|
+
const system = anthropicPayload.system;
|
|
3443
|
+
if (typeof system === "string") return system.startsWith(compactSystemPromptStart);
|
|
3444
|
+
if (!Array.isArray(system)) return false;
|
|
3445
|
+
return system.some((msg) => typeof msg.text === "string" && msg.text.startsWith(compactSystemPromptStart));
|
|
3446
|
+
};
|
|
3447
|
+
const mergeContentWithText = (tr, textBlock) => {
|
|
3448
|
+
if (typeof tr.content === "string") return {
|
|
3449
|
+
...tr,
|
|
3450
|
+
content: `${tr.content}\n\n${textBlock.text}`
|
|
3127
3451
|
};
|
|
3452
|
+
if (hasToolRef(tr)) return tr;
|
|
3128
3453
|
return {
|
|
3129
|
-
...
|
|
3130
|
-
content: [...
|
|
3454
|
+
...tr,
|
|
3455
|
+
content: [...tr.content, textBlock]
|
|
3131
3456
|
};
|
|
3132
3457
|
};
|
|
3133
|
-
const mergeContentWithTexts = (
|
|
3134
|
-
if (typeof
|
|
3458
|
+
const mergeContentWithTexts = (tr, textBlocks) => {
|
|
3459
|
+
if (typeof tr.content === "string") {
|
|
3135
3460
|
const appendedTexts = textBlocks.map((tb) => tb.text).join("\n\n");
|
|
3136
3461
|
return {
|
|
3137
|
-
...
|
|
3138
|
-
content: `${
|
|
3462
|
+
...tr,
|
|
3463
|
+
content: `${tr.content}\n\n${appendedTexts}`
|
|
3139
3464
|
};
|
|
3140
3465
|
}
|
|
3466
|
+
if (hasToolRef(tr)) return tr;
|
|
3141
3467
|
return {
|
|
3142
|
-
...
|
|
3143
|
-
content: [...
|
|
3468
|
+
...tr,
|
|
3469
|
+
content: [...tr.content, ...textBlocks]
|
|
3144
3470
|
};
|
|
3145
3471
|
};
|
|
3146
3472
|
const mergeToolResult = (toolResults, textBlocks) => {
|
|
3147
|
-
if (toolResults.length === textBlocks.length) return toolResults.map((
|
|
3473
|
+
if (toolResults.length === textBlocks.length) return toolResults.map((tr, i) => mergeContentWithText(tr, textBlocks[i]));
|
|
3148
3474
|
const lastIndex = toolResults.length - 1;
|
|
3149
|
-
return toolResults.map((
|
|
3475
|
+
return toolResults.map((tr, i) => i === lastIndex ? mergeContentWithTexts(tr, textBlocks) : tr);
|
|
3476
|
+
};
|
|
3477
|
+
const stripToolReferenceTurnBoundary = (anthropicPayload) => {
|
|
3478
|
+
for (const msg of anthropicPayload.messages) {
|
|
3479
|
+
if (msg.role !== "user" || !Array.isArray(msg.content)) continue;
|
|
3480
|
+
if (!msg.content.some((block) => block.type === "tool_result" && hasToolRef(block))) continue;
|
|
3481
|
+
msg.content = msg.content.filter((block) => block.type !== "text" || block.text.trim() !== TOOL_REFERENCE_TURN_BOUNDARY);
|
|
3482
|
+
}
|
|
3150
3483
|
};
|
|
3151
3484
|
const mergeToolResultForClaude = (anthropicPayload) => {
|
|
3152
3485
|
for (const msg of anthropicPayload.messages) {
|
|
@@ -3164,6 +3497,9 @@ const mergeToolResultForClaude = (anthropicPayload) => {
|
|
|
3164
3497
|
msg.content = mergeToolResult(toolResults, textBlocks);
|
|
3165
3498
|
}
|
|
3166
3499
|
};
|
|
3500
|
+
const hasToolRef = (block) => {
|
|
3501
|
+
return Array.isArray(block.content) && block.content.some((c) => c.type === "tool_reference");
|
|
3502
|
+
};
|
|
3167
3503
|
const stripUnsupportedCacheControl = (block) => {
|
|
3168
3504
|
const cacheControl = block.cache_control;
|
|
3169
3505
|
if (!cacheControl || typeof cacheControl !== "object" || Array.isArray(cacheControl)) return;
|
|
@@ -3180,9 +3516,9 @@ const stripTextBlockCacheControl = (block) => {
|
|
|
3180
3516
|
if (record.type !== "text") return;
|
|
3181
3517
|
stripUnsupportedCacheControl(record);
|
|
3182
3518
|
};
|
|
3183
|
-
const stripCacheControl = (
|
|
3184
|
-
if (Array.isArray(
|
|
3185
|
-
for (const msg of
|
|
3519
|
+
const stripCacheControl = (payload) => {
|
|
3520
|
+
if (Array.isArray(payload.system)) for (const block of payload.system) stripTextBlockCacheControl(block);
|
|
3521
|
+
for (const msg of payload.messages) {
|
|
3186
3522
|
if (!Array.isArray(msg.content)) continue;
|
|
3187
3523
|
for (const block of msg.content) {
|
|
3188
3524
|
stripTextBlockCacheControl(block);
|
|
@@ -3190,6 +3526,34 @@ const stripCacheControl = (anthropicPayload) => {
|
|
|
3190
3526
|
}
|
|
3191
3527
|
}
|
|
3192
3528
|
};
|
|
3529
|
+
const filterAssistantThinkingBlocks = (payload) => {
|
|
3530
|
+
for (const msg of payload.messages) if (msg.role === "assistant" && Array.isArray(msg.content)) msg.content = msg.content.filter((block) => {
|
|
3531
|
+
if (block.type !== "thinking") return true;
|
|
3532
|
+
return block.thinking && block.thinking !== "Thinking..." && block.signature && !block.signature.includes("@");
|
|
3533
|
+
});
|
|
3534
|
+
};
|
|
3535
|
+
const prepareMessagesApiPayload = (payload, selectedModel) => {
|
|
3536
|
+
stripCacheControl(payload);
|
|
3537
|
+
filterAssistantThinkingBlocks(payload);
|
|
3538
|
+
const toolChoice = payload.tool_choice;
|
|
3539
|
+
const disableThink = toolChoice?.type === "any" || toolChoice?.type === "tool";
|
|
3540
|
+
if (selectedModel?.capabilities.supports.adaptive_thinking && !disableThink) {
|
|
3541
|
+
payload.thinking = { type: "adaptive" };
|
|
3542
|
+
payload.output_config = { effort: getAnthropicEffortForModel(payload.model) };
|
|
3543
|
+
}
|
|
3544
|
+
};
|
|
3545
|
+
|
|
3546
|
+
//#endregion
|
|
3547
|
+
//#region src/routes/messages/utils.ts
|
|
3548
|
+
function mapOpenAIStopReasonToAnthropic(finishReason) {
|
|
3549
|
+
if (finishReason === null) return null;
|
|
3550
|
+
return {
|
|
3551
|
+
stop: "end_turn",
|
|
3552
|
+
length: "max_tokens",
|
|
3553
|
+
tool_calls: "tool_use",
|
|
3554
|
+
content_filter: "end_turn"
|
|
3555
|
+
}[finishReason];
|
|
3556
|
+
}
|
|
3193
3557
|
const estimateInputTokens = async (payload, selectedModel, logger$7) => {
|
|
3194
3558
|
try {
|
|
3195
3559
|
return (await getTokenCount(payload, selectedModel)).input;
|
|
@@ -3254,21 +3618,6 @@ const maybeBlockOriginalModelName = (context) => {
|
|
|
3254
3618
|
}
|
|
3255
3619
|
});
|
|
3256
3620
|
};
|
|
3257
|
-
const compactSystemPromptStart = "You are a helpful AI assistant tasked with summarizing conversations";
|
|
3258
|
-
const compactUserMessageStart = "CRITICAL: Respond with TEXT ONLY. Do NOT call any tools.";
|
|
3259
|
-
const hasCompactUserMessage = (messages) => {
|
|
3260
|
-
const lastMsg = messages.at(-1);
|
|
3261
|
-
if (!lastMsg || lastMsg.role !== "user") return false;
|
|
3262
|
-
if (typeof lastMsg.content === "string") return lastMsg.content.startsWith(compactUserMessageStart);
|
|
3263
|
-
if (!Array.isArray(lastMsg.content)) return false;
|
|
3264
|
-
return lastMsg.content.some((block) => block.type === "text" && typeof block.text === "string" && block.text.startsWith(compactUserMessageStart));
|
|
3265
|
-
};
|
|
3266
|
-
const isCompactRequest = (anthropicPayload) => {
|
|
3267
|
-
const system = anthropicPayload.system;
|
|
3268
|
-
if (typeof system === "string" && system.startsWith(compactSystemPromptStart)) return true;
|
|
3269
|
-
if (Array.isArray(system) && system.some((msg) => typeof msg.text === "string" && msg.text.startsWith(compactSystemPromptStart))) return true;
|
|
3270
|
-
return hasCompactUserMessage(anthropicPayload.messages);
|
|
3271
|
-
};
|
|
3272
3621
|
|
|
3273
3622
|
//#endregion
|
|
3274
3623
|
//#region src/routes/messages/non-stream-translation.ts
|
|
@@ -3374,7 +3723,6 @@ function handleAssistantMessage(message, modelId) {
|
|
|
3374
3723
|
function mapContent(content) {
|
|
3375
3724
|
if (typeof content === "string") return content;
|
|
3376
3725
|
if (!Array.isArray(content)) return null;
|
|
3377
|
-
if (!content.some((block) => block.type === "image")) return content.filter((block) => block.type === "text").map((block) => block.text).join("\n\n");
|
|
3378
3726
|
const contentParts = [];
|
|
3379
3727
|
for (const block of content) switch (block.type) {
|
|
3380
3728
|
case "text":
|
|
@@ -3389,6 +3737,12 @@ function mapContent(content) {
|
|
|
3389
3737
|
image_url: { url: `data:${block.source.media_type};base64,${block.source.data}` }
|
|
3390
3738
|
});
|
|
3391
3739
|
break;
|
|
3740
|
+
case "tool_reference":
|
|
3741
|
+
contentParts.push({
|
|
3742
|
+
type: "text",
|
|
3743
|
+
text: `Tool ${block.tool_name} loaded`
|
|
3744
|
+
});
|
|
3745
|
+
break;
|
|
3392
3746
|
}
|
|
3393
3747
|
return contentParts;
|
|
3394
3748
|
}
|
|
@@ -3600,7 +3954,7 @@ const translateAnthropicMessagesToResponsesPayload = (payload, modelOverride) =>
|
|
|
3600
3954
|
for (const message of payload.messages) input.push(...translateMessage(message, payload.model, applyPhase));
|
|
3601
3955
|
const translatedTools = convertAnthropicTools(payload.tools);
|
|
3602
3956
|
const toolChoice = convertAnthropicToolChoice(payload.tool_choice);
|
|
3603
|
-
const {
|
|
3957
|
+
const { sessionId: promptCacheKey } = parseUserIdMetadata(payload.metadata?.user_id);
|
|
3604
3958
|
return {
|
|
3605
3959
|
model,
|
|
3606
3960
|
input,
|
|
@@ -3611,7 +3965,6 @@ const translateAnthropicMessagesToResponsesPayload = (payload, modelOverride) =>
|
|
|
3611
3965
|
tools: translatedTools,
|
|
3612
3966
|
tool_choice: toolChoice,
|
|
3613
3967
|
metadata: payload.metadata ? { ...payload.metadata } : null,
|
|
3614
|
-
safety_identifier: safetyIdentifier,
|
|
3615
3968
|
prompt_cache_key: promptCacheKey,
|
|
3616
3969
|
stream: payload.stream ?? null,
|
|
3617
3970
|
store: false,
|
|
@@ -3803,7 +4156,7 @@ const translateSystemPrompt = (system, model) => {
|
|
|
3803
4156
|
const extraPrompt = getExtraPromptForModel(model);
|
|
3804
4157
|
if (typeof system === "string") return system + extraPrompt;
|
|
3805
4158
|
const text = system.map((block, index) => {
|
|
3806
|
-
if (index === 0) return block.text + extraPrompt;
|
|
4159
|
+
if (index === 0) return block.text + "\n\n" + extraPrompt + "\n\n";
|
|
3807
4160
|
return block.text;
|
|
3808
4161
|
}).join(" ");
|
|
3809
4162
|
return text.length > 0 ? text : null;
|
|
@@ -4002,6 +4355,9 @@ const convertToolResultContent = (content) => {
|
|
|
4002
4355
|
case "image":
|
|
4003
4356
|
result.push(createImageContent(block));
|
|
4004
4357
|
break;
|
|
4358
|
+
case "tool_reference":
|
|
4359
|
+
result.push(createTextContent(`Tool ${block.tool_name} loaded`));
|
|
4360
|
+
break;
|
|
4005
4361
|
default: break;
|
|
4006
4362
|
}
|
|
4007
4363
|
return result;
|
|
@@ -4443,7 +4799,7 @@ const extractFunctionCallDetails = (rawEvent) => {
|
|
|
4443
4799
|
//#region src/routes/responses/utils.ts
|
|
4444
4800
|
const getResponsesRequestOptions = (payload) => {
|
|
4445
4801
|
return {
|
|
4446
|
-
vision: hasVisionInput(payload),
|
|
4802
|
+
vision: hasVisionInput$1(payload),
|
|
4447
4803
|
initiator: hasAgentInitiator(payload) ? "agent" : "user"
|
|
4448
4804
|
};
|
|
4449
4805
|
};
|
|
@@ -4458,7 +4814,7 @@ const isAgentRole = (item) => {
|
|
|
4458
4814
|
if (!("role" in item) || !item.role) return true;
|
|
4459
4815
|
return (typeof item.role === "string" ? item.role.toLowerCase() : "") === "assistant";
|
|
4460
4816
|
};
|
|
4461
|
-
const hasVisionInput = (payload) => {
|
|
4817
|
+
const hasVisionInput$1 = (payload) => {
|
|
4462
4818
|
return getPayloadItems(payload).some((item) => containsVisionContent(item));
|
|
4463
4819
|
};
|
|
4464
4820
|
const resolveResponsesCompactThreshold = (maxPromptTokens) => {
|
|
@@ -4535,19 +4891,33 @@ const buildAnthropicBetaHeader = (anthropicBetaHeader, thinking) => {
|
|
|
4535
4891
|
}
|
|
4536
4892
|
if (thinking?.budget_tokens && !isAdaptiveThinking) return INTERLEAVED_THINKING_BETA;
|
|
4537
4893
|
};
|
|
4538
|
-
const
|
|
4539
|
-
|
|
4540
|
-
|
|
4541
|
-
|
|
4542
|
-
|
|
4894
|
+
const hasVisionInput = (payload) => payload.messages.some((message) => Array.isArray(message.content) && message.content.some((block) => block.type === "image" || block.type === "tool_result" && Array.isArray(block.content) && block.content.some((inner) => inner.type === "image")));
|
|
4895
|
+
const shouldUseMessageProxyHeaders = (payload) => {
|
|
4896
|
+
const { safetyIdentifier, sessionId } = parseUserIdMetadata(payload.metadata?.user_id);
|
|
4897
|
+
return Boolean(safetyIdentifier && sessionId);
|
|
4898
|
+
};
|
|
4899
|
+
const buildMessagesHeaders = ({ ctx, enableVision, initiator, options, payload }) => {
|
|
4543
4900
|
const headers = {
|
|
4544
4901
|
...copilotHeaders(ctx, enableVision, options?.upstreamRequestId),
|
|
4545
4902
|
"x-initiator": options?.subagentMarker ? "agent" : initiator
|
|
4546
4903
|
};
|
|
4547
4904
|
prepareInteractionHeaders(options?.sessionId, Boolean(options?.subagentMarker), headers);
|
|
4548
4905
|
prepareForCompact(headers, options?.isCompact);
|
|
4906
|
+
if (shouldUseMessageProxyHeaders(payload)) prepareMessageProxyHeaders(headers);
|
|
4549
4907
|
const anthropicBeta = buildAnthropicBetaHeader(options?.anthropicBetaHeader, payload.thinking);
|
|
4550
4908
|
if (anthropicBeta) headers["anthropic-beta"] = anthropicBeta;
|
|
4909
|
+
return headers;
|
|
4910
|
+
};
|
|
4911
|
+
const createMessages = async (payload, account, options) => {
|
|
4912
|
+
const ctx = account ?? accountFromState();
|
|
4913
|
+
if (!ctx.copilotToken) throw new Error("Copilot token not found");
|
|
4914
|
+
const headers = buildMessagesHeaders({
|
|
4915
|
+
ctx,
|
|
4916
|
+
enableVision: hasVisionInput(payload),
|
|
4917
|
+
initiator: options?.initiator ?? getMessagesInitiator(payload),
|
|
4918
|
+
options,
|
|
4919
|
+
payload
|
|
4920
|
+
});
|
|
4551
4921
|
const response = await fetch(`${copilotBaseUrl(ctx)}/v1/messages`, {
|
|
4552
4922
|
method: "POST",
|
|
4553
4923
|
headers,
|
|
@@ -4822,7 +5192,7 @@ function closeThinkingBlockIfOpen(state$1, events$1) {
|
|
|
4822
5192
|
//#region src/routes/messages/subagent-marker.ts
|
|
4823
5193
|
const subagentMarkerPrefix = "__SUBAGENT_MARKER__";
|
|
4824
5194
|
const parseSubagentMarkerFromFirstUser = (payload) => {
|
|
4825
|
-
const firstUserMessage = payload.messages.find((msg) => msg.role === "user");
|
|
5195
|
+
const firstUserMessage = payload.messages.find((msg) => msg.role === "user" && Array.isArray(msg.content));
|
|
4826
5196
|
if (!firstUserMessage || !Array.isArray(firstUserMessage.content)) return null;
|
|
4827
5197
|
for (const block of firstUserMessage.content) {
|
|
4828
5198
|
if (block.type !== "text") continue;
|
|
@@ -4879,10 +5249,10 @@ async function handleCompletion(c) {
|
|
|
4879
5249
|
const { ip: clientIp, source: clientIpSource } = getClientIpInfo(c);
|
|
4880
5250
|
const userAgent = c.req.header("user-agent") ?? void 0;
|
|
4881
5251
|
const anthropicPayload = await c.req.json();
|
|
4882
|
-
logger$5
|
|
5252
|
+
debugJson(logger$5, "Anthropic request payload:", anthropicPayload);
|
|
4883
5253
|
const subagentMarker = parseSubagentMarkerFromFirstUser(anthropicPayload);
|
|
4884
5254
|
const initiatorOverride = subagentMarker ? "agent" : void 0;
|
|
4885
|
-
if (subagentMarker) logger$5
|
|
5255
|
+
if (subagentMarker) debugJson(logger$5, "Detected Subagent marker:", subagentMarker);
|
|
4886
5256
|
const sessionId = getRootSessionId(anthropicPayload, c);
|
|
4887
5257
|
logger$5.debug("Extracted session ID:", sessionId);
|
|
4888
5258
|
const anthropicBeta = c.req.header("anthropic-beta");
|
|
@@ -4891,7 +5261,10 @@ async function handleCompletion(c) {
|
|
|
4891
5261
|
if (isCompact) {
|
|
4892
5262
|
logger$5.debug("Is compact request:", isCompact);
|
|
4893
5263
|
if (shouldCompactUseSmallModel()) anthropicPayload.model = getSmallModel();
|
|
4894
|
-
} else
|
|
5264
|
+
} else {
|
|
5265
|
+
stripToolReferenceTurnBoundary(anthropicPayload);
|
|
5266
|
+
mergeToolResultForClaude(anthropicPayload);
|
|
5267
|
+
}
|
|
4895
5268
|
const upstreamRequestId = generateRequestIdFromPayload(anthropicPayload, sessionId);
|
|
4896
5269
|
logger$5.debug("Generated request ID:", upstreamRequestId);
|
|
4897
5270
|
const clientModel = anthropicPayload.model;
|
|
@@ -5021,7 +5394,7 @@ async function handleCompletion(c) {
|
|
|
5021
5394
|
}
|
|
5022
5395
|
const handleWithChatCompletions = async (params) => {
|
|
5023
5396
|
const { c, openAIPayload, initiatorOverride, subagentMarker, sessionId, selectedModel, instr, isCompact } = params;
|
|
5024
|
-
logger$5
|
|
5397
|
+
debugJson(logger$5, "Translated OpenAI request payload:", openAIPayload);
|
|
5025
5398
|
const ctx = toAccountContext(instr.account);
|
|
5026
5399
|
const initiator = initiatorOverride ?? getChatInitiator(openAIPayload.messages);
|
|
5027
5400
|
instr.initiator = initiator;
|
|
@@ -5068,7 +5441,7 @@ const handleWithResponsesApi = async (params) => {
|
|
|
5068
5441
|
const responsesPayload = translateAnthropicMessagesToResponsesPayload(anthropicPayload, selectedModel.id);
|
|
5069
5442
|
applyResponsesApiContextManagement(responsesPayload, selectedModel.capabilities.limits.max_prompt_tokens);
|
|
5070
5443
|
compactInputByLatestCompaction(responsesPayload);
|
|
5071
|
-
logger$5
|
|
5444
|
+
debugJson(logger$5, "Translated Responses payload:", responsesPayload);
|
|
5072
5445
|
const { vision, initiator } = getResponsesRequestOptions(responsesPayload);
|
|
5073
5446
|
const resolvedInitiator = initiatorOverride ?? initiator;
|
|
5074
5447
|
const ctx = toAccountContext(instr.account);
|
|
@@ -5182,7 +5555,7 @@ async function handleChatCompletionsNonStreaming(params) {
|
|
|
5182
5555
|
try {
|
|
5183
5556
|
logger$5.debug("Non-streaming response from Copilot:", JSON.stringify(response));
|
|
5184
5557
|
const anthropicResponse = translateToAnthropic(response);
|
|
5185
|
-
logger$5
|
|
5558
|
+
debugJson(logger$5, "Translated Anthropic response:", anthropicResponse);
|
|
5186
5559
|
return c.json(anthropicResponse);
|
|
5187
5560
|
} catch (error) {
|
|
5188
5561
|
const details = extractErrorDetails(error);
|
|
@@ -5304,7 +5677,7 @@ async function handleResponsesNonStreaming(params) {
|
|
|
5304
5677
|
usage = extractResponsesUsageFromResult(result);
|
|
5305
5678
|
logger$5.debug("Non-streaming Responses result:", JSON.stringify(result).slice(-400));
|
|
5306
5679
|
const anthropicResponse = translateResponsesResultToAnthropic(result);
|
|
5307
|
-
logger$5
|
|
5680
|
+
debugJson(logger$5, "Translated Anthropic response:", anthropicResponse);
|
|
5308
5681
|
return c.json(anthropicResponse);
|
|
5309
5682
|
} catch (error) {
|
|
5310
5683
|
const details = extractErrorDetails(error);
|
|
@@ -5535,20 +5908,8 @@ async function streamMessagesAndLog(params) {
|
|
|
5535
5908
|
}
|
|
5536
5909
|
const handleWithMessagesApi = async (params) => {
|
|
5537
5910
|
const { c, anthropicPayload, anthropicBetaHeader, initiatorOverride, subagentMarker, sessionId, instr, selectedModel, isCompact } = params;
|
|
5538
|
-
|
|
5539
|
-
|
|
5540
|
-
if (block.type !== "thinking") return true;
|
|
5541
|
-
return block.thinking && block.thinking !== "Thinking..." && block.signature && !block.signature.includes("@");
|
|
5542
|
-
});
|
|
5543
|
-
const toolChoice = anthropicPayload.tool_choice;
|
|
5544
|
-
if (toolChoice?.type === "any" || toolChoice?.type === "tool") {
|
|
5545
|
-
delete anthropicPayload.thinking;
|
|
5546
|
-
delete anthropicPayload.output_config;
|
|
5547
|
-
} else if (selectedModel.capabilities.supports.adaptive_thinking) {
|
|
5548
|
-
anthropicPayload.thinking = { type: "adaptive" };
|
|
5549
|
-
anthropicPayload.output_config = { effort: getAnthropicEffortForModel(anthropicPayload.model) };
|
|
5550
|
-
}
|
|
5551
|
-
logger$5.debug("Translated Messages payload:", JSON.stringify(anthropicPayload));
|
|
5911
|
+
prepareMessagesApiPayload(anthropicPayload, selectedModel);
|
|
5912
|
+
debugJson(logger$5, "Translated Messages payload:", anthropicPayload);
|
|
5552
5913
|
const ctx = toAccountContext(instr.account);
|
|
5553
5914
|
const initiator = initiatorOverride ?? getMessagesInitiator(anthropicPayload);
|
|
5554
5915
|
instr.initiator = initiator;
|
|
@@ -5586,12 +5947,6 @@ const handleWithMessagesApi = async (params) => {
|
|
|
5586
5947
|
};
|
|
5587
5948
|
const isNonStreaming = (response) => Object.hasOwn(response, "choices");
|
|
5588
5949
|
const isAsyncIterable$1 = (value) => Boolean(value) && typeof value[Symbol.asyncIterator] === "function";
|
|
5589
|
-
const getAnthropicEffortForModel = (model) => {
|
|
5590
|
-
const reasoningEffort = getReasoningEffortForModel(model);
|
|
5591
|
-
if (reasoningEffort === "xhigh") return "max";
|
|
5592
|
-
if (reasoningEffort === "none" || reasoningEffort === "minimal") return "low";
|
|
5593
|
-
return reasoningEffort;
|
|
5594
|
-
};
|
|
5595
5950
|
|
|
5596
5951
|
//#endregion
|
|
5597
5952
|
//#region src/routes/messages/route.ts
|
|
@@ -5772,10 +6127,10 @@ async function handleProviderMessages(c) {
|
|
|
5772
6127
|
payload.temperature ??= modelConfig?.temperature;
|
|
5773
6128
|
payload.top_p ??= modelConfig?.topP;
|
|
5774
6129
|
payload.top_k ??= modelConfig?.topK;
|
|
5775
|
-
logger$3
|
|
6130
|
+
debugJson(logger$3, "provider.messages.request", {
|
|
5776
6131
|
payload,
|
|
5777
6132
|
provider
|
|
5778
|
-
})
|
|
6133
|
+
});
|
|
5779
6134
|
const upstreamResponse = await forwardProviderMessages(providerConfig, payload, c.req.raw.headers);
|
|
5780
6135
|
if (!upstreamResponse.ok) {
|
|
5781
6136
|
logger$3.error("Failed to create responses", upstreamResponse);
|
|
@@ -5818,7 +6173,7 @@ async function handleProviderMessages(c) {
|
|
|
5818
6173
|
}
|
|
5819
6174
|
const jsonBody = await upstreamResponse.json();
|
|
5820
6175
|
adjustInputTokens(providerConfig, jsonBody.usage);
|
|
5821
|
-
logger$3
|
|
6176
|
+
debugJson(logger$3, "provider.messages.no_stream result:", jsonBody);
|
|
5822
6177
|
return c.json(jsonBody);
|
|
5823
6178
|
} catch (error) {
|
|
5824
6179
|
logger$3.error("provider.messages.error", {
|
|
@@ -5831,7 +6186,7 @@ async function handleProviderMessages(c) {
|
|
|
5831
6186
|
const adjustInputTokens = (providerConfig, usage) => {
|
|
5832
6187
|
if (!providerConfig.adjustInputTokens || !usage) return;
|
|
5833
6188
|
usage.input_tokens = Math.max(0, (usage.input_tokens ?? 0) - (usage.cache_read_input_tokens ?? 0) - (usage.cache_creation_input_tokens ?? 0));
|
|
5834
|
-
logger$3
|
|
6189
|
+
debugJson(logger$3, "provider.messages.adjusted_usage:", usage);
|
|
5835
6190
|
};
|
|
5836
6191
|
|
|
5837
6192
|
//#endregion
|
|
@@ -5926,7 +6281,7 @@ const handleResponses = async (c) => {
|
|
|
5926
6281
|
const request = buildRequestContext(c);
|
|
5927
6282
|
const payload = await c.req.json();
|
|
5928
6283
|
const clientModel = payload.model;
|
|
5929
|
-
logger$1
|
|
6284
|
+
debugJson(logger$1, "Responses request payload:", payload);
|
|
5930
6285
|
if (!isResponsesApiWebSearchEnabled()) removeWebSearchTool(payload);
|
|
5931
6286
|
compactInputByLatestCompaction(payload);
|
|
5932
6287
|
const streamRequested = Boolean(payload.stream);
|
|
@@ -6174,7 +6529,10 @@ async function handleNonStreamingUpstreamResult(params) {
|
|
|
6174
6529
|
let errorMessage;
|
|
6175
6530
|
const finishedAtMs = Date.now();
|
|
6176
6531
|
try {
|
|
6177
|
-
logger$1
|
|
6532
|
+
debugJsonTail(logger$1, "Forwarding native Responses result:", {
|
|
6533
|
+
value: result,
|
|
6534
|
+
tailLength: 400
|
|
6535
|
+
});
|
|
6178
6536
|
return c.json(result);
|
|
6179
6537
|
} catch (error) {
|
|
6180
6538
|
const details = extractErrorDetails(error);
|
|
@@ -6226,7 +6584,7 @@ async function streamResponsesAndLog(params) {
|
|
|
6226
6584
|
const processedData = fixStreamIds(data ?? "", event, idTracker);
|
|
6227
6585
|
const usage = extractUsageFromChunkData(processedData);
|
|
6228
6586
|
if (usage) lastUsage = usage;
|
|
6229
|
-
logger$1
|
|
6587
|
+
debugJson(logger$1, "Responses stream chunk:", chunk);
|
|
6230
6588
|
await stream.writeSSE({
|
|
6231
6589
|
id,
|
|
6232
6590
|
event,
|
|
@@ -6290,7 +6648,10 @@ async function handleNonStreamingResponses(params) {
|
|
|
6290
6648
|
if (streamResponse) return streamResponse;
|
|
6291
6649
|
const result = response;
|
|
6292
6650
|
usage = extractResponsesUsageFromResult(result);
|
|
6293
|
-
logger$1
|
|
6651
|
+
debugJsonTail(logger$1, "Forwarding native Responses result:", {
|
|
6652
|
+
value: result,
|
|
6653
|
+
tailLength: 400
|
|
6654
|
+
});
|
|
6294
6655
|
return c.json(result);
|
|
6295
6656
|
} catch (error) {
|
|
6296
6657
|
finishedAtMs = Date.now();
|
|
@@ -6337,6 +6698,7 @@ function handleUnexpectedResponsesStream(c, response) {
|
|
|
6337
6698
|
for await (const chunk of response) {
|
|
6338
6699
|
const { id, event, data } = getStreamChunkFields(chunk);
|
|
6339
6700
|
const processedData = fixStreamIds(data ?? "", event, idTracker);
|
|
6701
|
+
debugJson(logger$1, "Responses stream chunk:", chunk);
|
|
6340
6702
|
await stream.writeSSE({
|
|
6341
6703
|
id,
|
|
6342
6704
|
event,
|
|
@@ -6469,4 +6831,4 @@ server.route("/:provider/v1/models", providerModelRoutes);
|
|
|
6469
6831
|
|
|
6470
6832
|
//#endregion
|
|
6471
6833
|
export { server };
|
|
6472
|
-
//# sourceMappingURL=server-
|
|
6834
|
+
//# sourceMappingURL=server-DzE_zmUf.js.map
|