@nick3/copilot-api 1.5.2 → 1.5.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 +5 -3
- package/dist/account-AacnHem5.js +362 -0
- package/dist/account-AacnHem5.js.map +1 -0
- package/dist/{accounts-manager-DhwGrhHF.js → accounts-manager-BevCBoaF.js} +96 -7
- package/dist/accounts-manager-BevCBoaF.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-BtUQnU2m.js → auth-B7x3wjry.js} +6 -12
- package/dist/auth-B7x3wjry.js.map +1 -0
- package/dist/{check-usage-DiomC9Dl.js → check-usage-B1cbDEOI.js} +4 -5
- package/dist/{check-usage-DiomC9Dl.js.map → check-usage-B1cbDEOI.js.map} +1 -1
- 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-DN1P6qll.js → get-copilot-token-cha9rQwA.js} +2 -2
- package/dist/{get-copilot-token-DN1P6qll.js.map → get-copilot-token-cha9rQwA.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-53JWve7i.js → poll-access-token-DFooFWhY.js} +339 -46
- package/dist/poll-access-token-DFooFWhY.js.map +1 -0
- package/dist/{server-D9M_wFyO.js → server-CuXJhEMC.js} +495 -140
- package/dist/server-CuXJhEMC.js.map +1 -0
- package/dist/{start-D3yEfZnW.js → start-D6O1XcfI.js} +26 -15
- package/dist/start-D6O1XcfI.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-DhwGrhHF.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-B-G_GrPI.js +0 -57
- package/dist/admin/assets/index-CsAeel_7.css +0 -1
- package/dist/auth-BtUQnU2m.js.map +0 -1
- package/dist/poll-access-token-BrRUFB1F.js +0 -52
- package/dist/poll-access-token-BrRUFB1F.js.map +0 -1
- package/dist/server-D9M_wFyO.js.map +0 -1
- package/dist/start-D3yEfZnW.js.map +0 -1
- package/dist/utils-53JWve7i.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-DFooFWhY.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-AacnHem5.js";
|
|
3
|
+
import { r as ensurePaths, t as PATHS } from "./paths-DGlr310R.js";
|
|
4
|
+
import "./get-copilot-token-cha9rQwA.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-BevCBoaF.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,11 +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,
|
|
2058
2327
|
...account.copilotApiUrl !== void 0 ? { copilotApiUrl: account.copilotApiUrl } : {},
|
|
2059
2328
|
accountType: account.accountType,
|
|
2060
|
-
vsCodeVersion: account.vsCodeVersion
|
|
2329
|
+
vsCodeVersion: account.vsCodeVersion,
|
|
2330
|
+
clientDeviceId: account.clientDeviceId,
|
|
2331
|
+
clientMachineId: account.clientMachineId,
|
|
2332
|
+
clientSessionId: account.clientSessionId
|
|
2061
2333
|
};
|
|
2062
2334
|
}
|
|
2063
2335
|
function extractErrorDetails(error) {
|
|
@@ -2082,6 +2354,9 @@ const FLUSH_INTERVAL_MS = 1e3;
|
|
|
2082
2354
|
const MAX_BUFFER_SIZE = 100;
|
|
2083
2355
|
const logStreams = /* @__PURE__ */ new Map();
|
|
2084
2356
|
const logBuffers = /* @__PURE__ */ new Map();
|
|
2357
|
+
let runtimeInitialized = false;
|
|
2358
|
+
let flushInterval;
|
|
2359
|
+
let cleanupInterval;
|
|
2085
2360
|
const ensureLogDirectory = () => {
|
|
2086
2361
|
if (!fs$1.existsSync(LOG_DIR)) fs$1.mkdirSync(LOG_DIR, { recursive: true });
|
|
2087
2362
|
};
|
|
@@ -2112,17 +2387,8 @@ const sanitizeName = (name) => {
|
|
|
2112
2387
|
const normalized = name.toLowerCase().replaceAll(/[^a-z0-9]+/g, "-").replaceAll(/^-+|-+$/g, "");
|
|
2113
2388
|
return normalized === "" ? "handler" : normalized;
|
|
2114
2389
|
};
|
|
2115
|
-
const
|
|
2116
|
-
|
|
2117
|
-
if (!stream || stream.destroyed) {
|
|
2118
|
-
stream = fs$1.createWriteStream(filePath, { flags: "a" });
|
|
2119
|
-
logStreams.set(filePath, stream);
|
|
2120
|
-
stream.on("error", (error) => {
|
|
2121
|
-
console.warn("Log stream error", error);
|
|
2122
|
-
logStreams.delete(filePath);
|
|
2123
|
-
});
|
|
2124
|
-
}
|
|
2125
|
-
return stream;
|
|
2390
|
+
const maybeUnref = (timer) => {
|
|
2391
|
+
timer.unref();
|
|
2126
2392
|
};
|
|
2127
2393
|
const flushBuffer = (filePath) => {
|
|
2128
2394
|
const buffer = logBuffers.get(filePath);
|
|
@@ -2137,6 +2403,52 @@ const flushBuffer = (filePath) => {
|
|
|
2137
2403
|
const flushAllBuffers = () => {
|
|
2138
2404
|
for (const filePath of logBuffers.keys()) flushBuffer(filePath);
|
|
2139
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
|
+
};
|
|
2140
2452
|
const appendLine = (filePath, line) => {
|
|
2141
2453
|
let buffer = logBuffers.get(filePath);
|
|
2142
2454
|
if (!buffer) {
|
|
@@ -2146,35 +2458,23 @@ const appendLine = (filePath, line) => {
|
|
|
2146
2458
|
buffer.push(line);
|
|
2147
2459
|
if (buffer.length >= MAX_BUFFER_SIZE) flushBuffer(filePath);
|
|
2148
2460
|
};
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
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)]);
|
|
2155
2470
|
};
|
|
2156
|
-
process.on("exit", cleanup);
|
|
2157
|
-
process.on("SIGINT", () => {
|
|
2158
|
-
cleanup();
|
|
2159
|
-
process.exit(0);
|
|
2160
|
-
});
|
|
2161
|
-
process.on("SIGTERM", () => {
|
|
2162
|
-
cleanup();
|
|
2163
|
-
process.exit(0);
|
|
2164
|
-
});
|
|
2165
|
-
let lastCleanup = 0;
|
|
2166
2471
|
const createHandlerLogger = (name) => {
|
|
2167
|
-
ensureLogDirectory();
|
|
2168
2472
|
const sanitizedName = sanitizeName(name);
|
|
2169
2473
|
const instance = consola.withTag(name);
|
|
2170
2474
|
if (state.verbose) instance.level = 5;
|
|
2171
2475
|
instance.setReporters([]);
|
|
2172
2476
|
instance.addReporter({ log(logObj) {
|
|
2173
|
-
|
|
2174
|
-
if (Date.now() - lastCleanup > CLEANUP_INTERVAL_MS) {
|
|
2175
|
-
cleanupOldLogs();
|
|
2176
|
-
lastCleanup = Date.now();
|
|
2177
|
-
}
|
|
2477
|
+
initializeLoggerRuntime();
|
|
2178
2478
|
const traceId = requestContext.getStore()?.traceId;
|
|
2179
2479
|
const date = logObj.date;
|
|
2180
2480
|
const dateKey = date.toLocaleDateString("sv-SE");
|
|
@@ -2504,7 +2804,10 @@ async function handleCompletion$1(c) {
|
|
|
2504
2804
|
reason: "MODEL_NOT_SUPPORTED"
|
|
2505
2805
|
});
|
|
2506
2806
|
}
|
|
2507
|
-
logger$6
|
|
2807
|
+
debugJsonTail(logger$6, "Request payload:", {
|
|
2808
|
+
value: payload,
|
|
2809
|
+
tailLength: 400
|
|
2810
|
+
});
|
|
2508
2811
|
const upstreamRequestId = generateRequestIdFromPayload(payload, normalizedPromptCacheKey);
|
|
2509
2812
|
const selection = await accountsManager.selectAccountForRequest([{
|
|
2510
2813
|
modelId: clientModel,
|
|
@@ -2637,7 +2940,7 @@ function applyDefaultMaxTokens(payload, selectedModel) {
|
|
|
2637
2940
|
...payload,
|
|
2638
2941
|
max_tokens: selectedModel.capabilities.limits.max_output_tokens
|
|
2639
2942
|
};
|
|
2640
|
-
logger$6
|
|
2943
|
+
debugJson(logger$6, "Set max_tokens to:", updated.max_tokens);
|
|
2641
2944
|
return updated;
|
|
2642
2945
|
}
|
|
2643
2946
|
async function handleStreamingRequest(params) {
|
|
@@ -2723,7 +3026,7 @@ async function handleNonStreamingUpstreamResponse(params) {
|
|
|
2723
3026
|
let errorMessage;
|
|
2724
3027
|
const finishedAtMs = Date.now();
|
|
2725
3028
|
try {
|
|
2726
|
-
logger$6
|
|
3029
|
+
debugJson(logger$6, "Non-streaming response:", response);
|
|
2727
3030
|
return c.json(response);
|
|
2728
3031
|
} catch (error) {
|
|
2729
3032
|
const details = extractErrorDetails(error);
|
|
@@ -2773,7 +3076,7 @@ async function streamChatCompletionsAndLog$1(params) {
|
|
|
2773
3076
|
if (ttfbMs === void 0) ttfbMs = Date.now() - request.startedAtMs;
|
|
2774
3077
|
const usage = await extractUsageFromChunk(chunk);
|
|
2775
3078
|
if (usage) lastUsage = usage;
|
|
2776
|
-
logger$6
|
|
3079
|
+
debugJson(logger$6, "Streaming chunk:", chunk);
|
|
2777
3080
|
await stream.writeSSE(chunk);
|
|
2778
3081
|
}
|
|
2779
3082
|
} catch (error) {
|
|
@@ -2845,7 +3148,7 @@ async function handleNonStreamingRequest(params) {
|
|
|
2845
3148
|
});
|
|
2846
3149
|
}
|
|
2847
3150
|
usage = normalizeChatCompletionsUsage(response.usage);
|
|
2848
|
-
logger$6
|
|
3151
|
+
debugJson(logger$6, "Non-streaming response:", response);
|
|
2849
3152
|
return c.json(response);
|
|
2850
3153
|
} catch (error) {
|
|
2851
3154
|
finishedAtMs = Date.now();
|
|
@@ -3111,43 +3414,72 @@ const _normalizeSdkModelId = (sdkModelId) => {
|
|
|
3111
3414
|
};
|
|
3112
3415
|
|
|
3113
3416
|
//#endregion
|
|
3114
|
-
//#region src/routes/messages/
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
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}`
|
|
3128
3451
|
};
|
|
3452
|
+
if (hasToolRef(tr)) return tr;
|
|
3129
3453
|
return {
|
|
3130
|
-
...
|
|
3131
|
-
content: [...
|
|
3454
|
+
...tr,
|
|
3455
|
+
content: [...tr.content, textBlock]
|
|
3132
3456
|
};
|
|
3133
3457
|
};
|
|
3134
|
-
const mergeContentWithTexts = (
|
|
3135
|
-
if (typeof
|
|
3458
|
+
const mergeContentWithTexts = (tr, textBlocks) => {
|
|
3459
|
+
if (typeof tr.content === "string") {
|
|
3136
3460
|
const appendedTexts = textBlocks.map((tb) => tb.text).join("\n\n");
|
|
3137
3461
|
return {
|
|
3138
|
-
...
|
|
3139
|
-
content: `${
|
|
3462
|
+
...tr,
|
|
3463
|
+
content: `${tr.content}\n\n${appendedTexts}`
|
|
3140
3464
|
};
|
|
3141
3465
|
}
|
|
3466
|
+
if (hasToolRef(tr)) return tr;
|
|
3142
3467
|
return {
|
|
3143
|
-
...
|
|
3144
|
-
content: [...
|
|
3468
|
+
...tr,
|
|
3469
|
+
content: [...tr.content, ...textBlocks]
|
|
3145
3470
|
};
|
|
3146
3471
|
};
|
|
3147
3472
|
const mergeToolResult = (toolResults, textBlocks) => {
|
|
3148
|
-
if (toolResults.length === textBlocks.length) return toolResults.map((
|
|
3473
|
+
if (toolResults.length === textBlocks.length) return toolResults.map((tr, i) => mergeContentWithText(tr, textBlocks[i]));
|
|
3149
3474
|
const lastIndex = toolResults.length - 1;
|
|
3150
|
-
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
|
+
}
|
|
3151
3483
|
};
|
|
3152
3484
|
const mergeToolResultForClaude = (anthropicPayload) => {
|
|
3153
3485
|
for (const msg of anthropicPayload.messages) {
|
|
@@ -3165,6 +3497,9 @@ const mergeToolResultForClaude = (anthropicPayload) => {
|
|
|
3165
3497
|
msg.content = mergeToolResult(toolResults, textBlocks);
|
|
3166
3498
|
}
|
|
3167
3499
|
};
|
|
3500
|
+
const hasToolRef = (block) => {
|
|
3501
|
+
return Array.isArray(block.content) && block.content.some((c) => c.type === "tool_reference");
|
|
3502
|
+
};
|
|
3168
3503
|
const stripUnsupportedCacheControl = (block) => {
|
|
3169
3504
|
const cacheControl = block.cache_control;
|
|
3170
3505
|
if (!cacheControl || typeof cacheControl !== "object" || Array.isArray(cacheControl)) return;
|
|
@@ -3181,9 +3516,9 @@ const stripTextBlockCacheControl = (block) => {
|
|
|
3181
3516
|
if (record.type !== "text") return;
|
|
3182
3517
|
stripUnsupportedCacheControl(record);
|
|
3183
3518
|
};
|
|
3184
|
-
const stripCacheControl = (
|
|
3185
|
-
if (Array.isArray(
|
|
3186
|
-
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) {
|
|
3187
3522
|
if (!Array.isArray(msg.content)) continue;
|
|
3188
3523
|
for (const block of msg.content) {
|
|
3189
3524
|
stripTextBlockCacheControl(block);
|
|
@@ -3191,6 +3526,34 @@ const stripCacheControl = (anthropicPayload) => {
|
|
|
3191
3526
|
}
|
|
3192
3527
|
}
|
|
3193
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
|
+
}
|
|
3194
3557
|
const estimateInputTokens = async (payload, selectedModel, logger$7) => {
|
|
3195
3558
|
try {
|
|
3196
3559
|
return (await getTokenCount(payload, selectedModel)).input;
|
|
@@ -3255,28 +3618,6 @@ const maybeBlockOriginalModelName = (context) => {
|
|
|
3255
3618
|
}
|
|
3256
3619
|
});
|
|
3257
3620
|
};
|
|
3258
|
-
const compactSystemPromptStart = "You are a helpful AI assistant tasked with summarizing conversations";
|
|
3259
|
-
const compactTextOnlyGuard = "CRITICAL: Respond with TEXT ONLY. Do NOT call any tools.";
|
|
3260
|
-
const compactSummaryPromptStart = "Your task is to create a detailed summary of the conversation so far";
|
|
3261
|
-
const compactMessageSections = ["Pending Tasks:", "Current Work:"];
|
|
3262
|
-
const getCompactCandidateText = (message) => {
|
|
3263
|
-
if (message.role !== "user") return "";
|
|
3264
|
-
if (typeof message.content === "string") return message.content;
|
|
3265
|
-
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");
|
|
3266
|
-
};
|
|
3267
|
-
const isCompactMessage = (lastMessage) => {
|
|
3268
|
-
const text = getCompactCandidateText(lastMessage);
|
|
3269
|
-
if (!text) return false;
|
|
3270
|
-
return text.includes(compactTextOnlyGuard) && text.includes(compactSummaryPromptStart) && compactMessageSections.some((section) => text.includes(section));
|
|
3271
|
-
};
|
|
3272
|
-
const isCompactRequest = (anthropicPayload) => {
|
|
3273
|
-
const lastMessage = anthropicPayload.messages.at(-1);
|
|
3274
|
-
if (lastMessage && isCompactMessage(lastMessage)) return true;
|
|
3275
|
-
const system = anthropicPayload.system;
|
|
3276
|
-
if (typeof system === "string" && system.startsWith(compactSystemPromptStart)) return true;
|
|
3277
|
-
if (Array.isArray(system) && system.some((msg) => typeof msg.text === "string" && msg.text.startsWith(compactSystemPromptStart))) return true;
|
|
3278
|
-
return false;
|
|
3279
|
-
};
|
|
3280
3621
|
|
|
3281
3622
|
//#endregion
|
|
3282
3623
|
//#region src/routes/messages/non-stream-translation.ts
|
|
@@ -3382,7 +3723,6 @@ function handleAssistantMessage(message, modelId) {
|
|
|
3382
3723
|
function mapContent(content) {
|
|
3383
3724
|
if (typeof content === "string") return content;
|
|
3384
3725
|
if (!Array.isArray(content)) return null;
|
|
3385
|
-
if (!content.some((block) => block.type === "image")) return content.filter((block) => block.type === "text").map((block) => block.text).join("\n\n");
|
|
3386
3726
|
const contentParts = [];
|
|
3387
3727
|
for (const block of content) switch (block.type) {
|
|
3388
3728
|
case "text":
|
|
@@ -3397,6 +3737,12 @@ function mapContent(content) {
|
|
|
3397
3737
|
image_url: { url: `data:${block.source.media_type};base64,${block.source.data}` }
|
|
3398
3738
|
});
|
|
3399
3739
|
break;
|
|
3740
|
+
case "tool_reference":
|
|
3741
|
+
contentParts.push({
|
|
3742
|
+
type: "text",
|
|
3743
|
+
text: `Tool ${block.tool_name} loaded`
|
|
3744
|
+
});
|
|
3745
|
+
break;
|
|
3400
3746
|
}
|
|
3401
3747
|
return contentParts;
|
|
3402
3748
|
}
|
|
@@ -4009,6 +4355,9 @@ const convertToolResultContent = (content) => {
|
|
|
4009
4355
|
case "image":
|
|
4010
4356
|
result.push(createImageContent(block));
|
|
4011
4357
|
break;
|
|
4358
|
+
case "tool_reference":
|
|
4359
|
+
result.push(createTextContent(`Tool ${block.tool_name} loaded`));
|
|
4360
|
+
break;
|
|
4012
4361
|
default: break;
|
|
4013
4362
|
}
|
|
4014
4363
|
return result;
|
|
@@ -4450,7 +4799,7 @@ const extractFunctionCallDetails = (rawEvent) => {
|
|
|
4450
4799
|
//#region src/routes/responses/utils.ts
|
|
4451
4800
|
const getResponsesRequestOptions = (payload) => {
|
|
4452
4801
|
return {
|
|
4453
|
-
vision: hasVisionInput(payload),
|
|
4802
|
+
vision: hasVisionInput$1(payload),
|
|
4454
4803
|
initiator: hasAgentInitiator(payload) ? "agent" : "user"
|
|
4455
4804
|
};
|
|
4456
4805
|
};
|
|
@@ -4465,7 +4814,7 @@ const isAgentRole = (item) => {
|
|
|
4465
4814
|
if (!("role" in item) || !item.role) return true;
|
|
4466
4815
|
return (typeof item.role === "string" ? item.role.toLowerCase() : "") === "assistant";
|
|
4467
4816
|
};
|
|
4468
|
-
const hasVisionInput = (payload) => {
|
|
4817
|
+
const hasVisionInput$1 = (payload) => {
|
|
4469
4818
|
return getPayloadItems(payload).some((item) => containsVisionContent(item));
|
|
4470
4819
|
};
|
|
4471
4820
|
const resolveResponsesCompactThreshold = (maxPromptTokens) => {
|
|
@@ -4542,19 +4891,33 @@ const buildAnthropicBetaHeader = (anthropicBetaHeader, thinking) => {
|
|
|
4542
4891
|
}
|
|
4543
4892
|
if (thinking?.budget_tokens && !isAdaptiveThinking) return INTERLEAVED_THINKING_BETA;
|
|
4544
4893
|
};
|
|
4545
|
-
const
|
|
4546
|
-
|
|
4547
|
-
|
|
4548
|
-
|
|
4549
|
-
|
|
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 }) => {
|
|
4550
4900
|
const headers = {
|
|
4551
4901
|
...copilotHeaders(ctx, enableVision, options?.upstreamRequestId),
|
|
4552
4902
|
"x-initiator": options?.subagentMarker ? "agent" : initiator
|
|
4553
4903
|
};
|
|
4554
4904
|
prepareInteractionHeaders(options?.sessionId, Boolean(options?.subagentMarker), headers);
|
|
4555
4905
|
prepareForCompact(headers, options?.isCompact);
|
|
4906
|
+
if (shouldUseMessageProxyHeaders(payload)) prepareMessageProxyHeaders(headers);
|
|
4556
4907
|
const anthropicBeta = buildAnthropicBetaHeader(options?.anthropicBetaHeader, payload.thinking);
|
|
4557
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
|
+
});
|
|
4558
4921
|
const response = await fetch(`${copilotBaseUrl(ctx)}/v1/messages`, {
|
|
4559
4922
|
method: "POST",
|
|
4560
4923
|
headers,
|
|
@@ -4829,7 +5192,7 @@ function closeThinkingBlockIfOpen(state$1, events$1) {
|
|
|
4829
5192
|
//#region src/routes/messages/subagent-marker.ts
|
|
4830
5193
|
const subagentMarkerPrefix = "__SUBAGENT_MARKER__";
|
|
4831
5194
|
const parseSubagentMarkerFromFirstUser = (payload) => {
|
|
4832
|
-
const firstUserMessage = payload.messages.find((msg) => msg.role === "user");
|
|
5195
|
+
const firstUserMessage = payload.messages.find((msg) => msg.role === "user" && Array.isArray(msg.content));
|
|
4833
5196
|
if (!firstUserMessage || !Array.isArray(firstUserMessage.content)) return null;
|
|
4834
5197
|
for (const block of firstUserMessage.content) {
|
|
4835
5198
|
if (block.type !== "text") continue;
|
|
@@ -4886,10 +5249,10 @@ async function handleCompletion(c) {
|
|
|
4886
5249
|
const { ip: clientIp, source: clientIpSource } = getClientIpInfo(c);
|
|
4887
5250
|
const userAgent = c.req.header("user-agent") ?? void 0;
|
|
4888
5251
|
const anthropicPayload = await c.req.json();
|
|
4889
|
-
logger$5
|
|
5252
|
+
debugJson(logger$5, "Anthropic request payload:", anthropicPayload);
|
|
4890
5253
|
const subagentMarker = parseSubagentMarkerFromFirstUser(anthropicPayload);
|
|
4891
5254
|
const initiatorOverride = subagentMarker ? "agent" : void 0;
|
|
4892
|
-
if (subagentMarker) logger$5
|
|
5255
|
+
if (subagentMarker) debugJson(logger$5, "Detected Subagent marker:", subagentMarker);
|
|
4893
5256
|
const sessionId = getRootSessionId(anthropicPayload, c);
|
|
4894
5257
|
logger$5.debug("Extracted session ID:", sessionId);
|
|
4895
5258
|
const anthropicBeta = c.req.header("anthropic-beta");
|
|
@@ -4898,7 +5261,10 @@ async function handleCompletion(c) {
|
|
|
4898
5261
|
if (isCompact) {
|
|
4899
5262
|
logger$5.debug("Is compact request:", isCompact);
|
|
4900
5263
|
if (shouldCompactUseSmallModel()) anthropicPayload.model = getSmallModel();
|
|
4901
|
-
} else
|
|
5264
|
+
} else {
|
|
5265
|
+
stripToolReferenceTurnBoundary(anthropicPayload);
|
|
5266
|
+
mergeToolResultForClaude(anthropicPayload);
|
|
5267
|
+
}
|
|
4902
5268
|
const upstreamRequestId = generateRequestIdFromPayload(anthropicPayload, sessionId);
|
|
4903
5269
|
logger$5.debug("Generated request ID:", upstreamRequestId);
|
|
4904
5270
|
const clientModel = anthropicPayload.model;
|
|
@@ -5028,7 +5394,7 @@ async function handleCompletion(c) {
|
|
|
5028
5394
|
}
|
|
5029
5395
|
const handleWithChatCompletions = async (params) => {
|
|
5030
5396
|
const { c, openAIPayload, initiatorOverride, subagentMarker, sessionId, selectedModel, instr, isCompact } = params;
|
|
5031
|
-
logger$5
|
|
5397
|
+
debugJson(logger$5, "Translated OpenAI request payload:", openAIPayload);
|
|
5032
5398
|
const ctx = toAccountContext(instr.account);
|
|
5033
5399
|
const initiator = initiatorOverride ?? getChatInitiator(openAIPayload.messages);
|
|
5034
5400
|
instr.initiator = initiator;
|
|
@@ -5075,7 +5441,7 @@ const handleWithResponsesApi = async (params) => {
|
|
|
5075
5441
|
const responsesPayload = translateAnthropicMessagesToResponsesPayload(anthropicPayload, selectedModel.id);
|
|
5076
5442
|
applyResponsesApiContextManagement(responsesPayload, selectedModel.capabilities.limits.max_prompt_tokens);
|
|
5077
5443
|
compactInputByLatestCompaction(responsesPayload);
|
|
5078
|
-
logger$5
|
|
5444
|
+
debugJson(logger$5, "Translated Responses payload:", responsesPayload);
|
|
5079
5445
|
const { vision, initiator } = getResponsesRequestOptions(responsesPayload);
|
|
5080
5446
|
const resolvedInitiator = initiatorOverride ?? initiator;
|
|
5081
5447
|
const ctx = toAccountContext(instr.account);
|
|
@@ -5189,7 +5555,7 @@ async function handleChatCompletionsNonStreaming(params) {
|
|
|
5189
5555
|
try {
|
|
5190
5556
|
logger$5.debug("Non-streaming response from Copilot:", JSON.stringify(response));
|
|
5191
5557
|
const anthropicResponse = translateToAnthropic(response);
|
|
5192
|
-
logger$5
|
|
5558
|
+
debugJson(logger$5, "Translated Anthropic response:", anthropicResponse);
|
|
5193
5559
|
return c.json(anthropicResponse);
|
|
5194
5560
|
} catch (error) {
|
|
5195
5561
|
const details = extractErrorDetails(error);
|
|
@@ -5311,7 +5677,7 @@ async function handleResponsesNonStreaming(params) {
|
|
|
5311
5677
|
usage = extractResponsesUsageFromResult(result);
|
|
5312
5678
|
logger$5.debug("Non-streaming Responses result:", JSON.stringify(result).slice(-400));
|
|
5313
5679
|
const anthropicResponse = translateResponsesResultToAnthropic(result);
|
|
5314
|
-
logger$5
|
|
5680
|
+
debugJson(logger$5, "Translated Anthropic response:", anthropicResponse);
|
|
5315
5681
|
return c.json(anthropicResponse);
|
|
5316
5682
|
} catch (error) {
|
|
5317
5683
|
const details = extractErrorDetails(error);
|
|
@@ -5542,20 +5908,8 @@ async function streamMessagesAndLog(params) {
|
|
|
5542
5908
|
}
|
|
5543
5909
|
const handleWithMessagesApi = async (params) => {
|
|
5544
5910
|
const { c, anthropicPayload, anthropicBetaHeader, initiatorOverride, subagentMarker, sessionId, instr, selectedModel, isCompact } = params;
|
|
5545
|
-
|
|
5546
|
-
|
|
5547
|
-
if (block.type !== "thinking") return true;
|
|
5548
|
-
return block.thinking && block.thinking !== "Thinking..." && block.signature && !block.signature.includes("@");
|
|
5549
|
-
});
|
|
5550
|
-
const toolChoice = anthropicPayload.tool_choice;
|
|
5551
|
-
if (toolChoice?.type === "any" || toolChoice?.type === "tool") {
|
|
5552
|
-
delete anthropicPayload.thinking;
|
|
5553
|
-
delete anthropicPayload.output_config;
|
|
5554
|
-
} else if (selectedModel.capabilities.supports.adaptive_thinking) {
|
|
5555
|
-
anthropicPayload.thinking = { type: "adaptive" };
|
|
5556
|
-
anthropicPayload.output_config = { effort: getAnthropicEffortForModel(anthropicPayload.model) };
|
|
5557
|
-
}
|
|
5558
|
-
logger$5.debug("Translated Messages payload:", JSON.stringify(anthropicPayload));
|
|
5911
|
+
prepareMessagesApiPayload(anthropicPayload, selectedModel);
|
|
5912
|
+
debugJson(logger$5, "Translated Messages payload:", anthropicPayload);
|
|
5559
5913
|
const ctx = toAccountContext(instr.account);
|
|
5560
5914
|
const initiator = initiatorOverride ?? getMessagesInitiator(anthropicPayload);
|
|
5561
5915
|
instr.initiator = initiator;
|
|
@@ -5593,12 +5947,6 @@ const handleWithMessagesApi = async (params) => {
|
|
|
5593
5947
|
};
|
|
5594
5948
|
const isNonStreaming = (response) => Object.hasOwn(response, "choices");
|
|
5595
5949
|
const isAsyncIterable$1 = (value) => Boolean(value) && typeof value[Symbol.asyncIterator] === "function";
|
|
5596
|
-
const getAnthropicEffortForModel = (model) => {
|
|
5597
|
-
const reasoningEffort = getReasoningEffortForModel(model);
|
|
5598
|
-
if (reasoningEffort === "xhigh") return "max";
|
|
5599
|
-
if (reasoningEffort === "none" || reasoningEffort === "minimal") return "low";
|
|
5600
|
-
return reasoningEffort;
|
|
5601
|
-
};
|
|
5602
5950
|
|
|
5603
5951
|
//#endregion
|
|
5604
5952
|
//#region src/routes/messages/route.ts
|
|
@@ -5779,10 +6127,10 @@ async function handleProviderMessages(c) {
|
|
|
5779
6127
|
payload.temperature ??= modelConfig?.temperature;
|
|
5780
6128
|
payload.top_p ??= modelConfig?.topP;
|
|
5781
6129
|
payload.top_k ??= modelConfig?.topK;
|
|
5782
|
-
logger$3
|
|
6130
|
+
debugJson(logger$3, "provider.messages.request", {
|
|
5783
6131
|
payload,
|
|
5784
6132
|
provider
|
|
5785
|
-
})
|
|
6133
|
+
});
|
|
5786
6134
|
const upstreamResponse = await forwardProviderMessages(providerConfig, payload, c.req.raw.headers);
|
|
5787
6135
|
if (!upstreamResponse.ok) {
|
|
5788
6136
|
logger$3.error("Failed to create responses", upstreamResponse);
|
|
@@ -5825,7 +6173,7 @@ async function handleProviderMessages(c) {
|
|
|
5825
6173
|
}
|
|
5826
6174
|
const jsonBody = await upstreamResponse.json();
|
|
5827
6175
|
adjustInputTokens(providerConfig, jsonBody.usage);
|
|
5828
|
-
logger$3
|
|
6176
|
+
debugJson(logger$3, "provider.messages.no_stream result:", jsonBody);
|
|
5829
6177
|
return c.json(jsonBody);
|
|
5830
6178
|
} catch (error) {
|
|
5831
6179
|
logger$3.error("provider.messages.error", {
|
|
@@ -5838,7 +6186,7 @@ async function handleProviderMessages(c) {
|
|
|
5838
6186
|
const adjustInputTokens = (providerConfig, usage) => {
|
|
5839
6187
|
if (!providerConfig.adjustInputTokens || !usage) return;
|
|
5840
6188
|
usage.input_tokens = Math.max(0, (usage.input_tokens ?? 0) - (usage.cache_read_input_tokens ?? 0) - (usage.cache_creation_input_tokens ?? 0));
|
|
5841
|
-
logger$3
|
|
6189
|
+
debugJson(logger$3, "provider.messages.adjusted_usage:", usage);
|
|
5842
6190
|
};
|
|
5843
6191
|
|
|
5844
6192
|
//#endregion
|
|
@@ -5933,7 +6281,7 @@ const handleResponses = async (c) => {
|
|
|
5933
6281
|
const request = buildRequestContext(c);
|
|
5934
6282
|
const payload = await c.req.json();
|
|
5935
6283
|
const clientModel = payload.model;
|
|
5936
|
-
logger$1
|
|
6284
|
+
debugJson(logger$1, "Responses request payload:", payload);
|
|
5937
6285
|
if (!isResponsesApiWebSearchEnabled()) removeWebSearchTool(payload);
|
|
5938
6286
|
compactInputByLatestCompaction(payload);
|
|
5939
6287
|
const streamRequested = Boolean(payload.stream);
|
|
@@ -6181,7 +6529,10 @@ async function handleNonStreamingUpstreamResult(params) {
|
|
|
6181
6529
|
let errorMessage;
|
|
6182
6530
|
const finishedAtMs = Date.now();
|
|
6183
6531
|
try {
|
|
6184
|
-
logger$1
|
|
6532
|
+
debugJsonTail(logger$1, "Forwarding native Responses result:", {
|
|
6533
|
+
value: result,
|
|
6534
|
+
tailLength: 400
|
|
6535
|
+
});
|
|
6185
6536
|
return c.json(result);
|
|
6186
6537
|
} catch (error) {
|
|
6187
6538
|
const details = extractErrorDetails(error);
|
|
@@ -6233,7 +6584,7 @@ async function streamResponsesAndLog(params) {
|
|
|
6233
6584
|
const processedData = fixStreamIds(data ?? "", event, idTracker);
|
|
6234
6585
|
const usage = extractUsageFromChunkData(processedData);
|
|
6235
6586
|
if (usage) lastUsage = usage;
|
|
6236
|
-
logger$1
|
|
6587
|
+
debugJson(logger$1, "Responses stream chunk:", chunk);
|
|
6237
6588
|
await stream.writeSSE({
|
|
6238
6589
|
id,
|
|
6239
6590
|
event,
|
|
@@ -6297,7 +6648,10 @@ async function handleNonStreamingResponses(params) {
|
|
|
6297
6648
|
if (streamResponse) return streamResponse;
|
|
6298
6649
|
const result = response;
|
|
6299
6650
|
usage = extractResponsesUsageFromResult(result);
|
|
6300
|
-
logger$1
|
|
6651
|
+
debugJsonTail(logger$1, "Forwarding native Responses result:", {
|
|
6652
|
+
value: result,
|
|
6653
|
+
tailLength: 400
|
|
6654
|
+
});
|
|
6301
6655
|
return c.json(result);
|
|
6302
6656
|
} catch (error) {
|
|
6303
6657
|
finishedAtMs = Date.now();
|
|
@@ -6344,6 +6698,7 @@ function handleUnexpectedResponsesStream(c, response) {
|
|
|
6344
6698
|
for await (const chunk of response) {
|
|
6345
6699
|
const { id, event, data } = getStreamChunkFields(chunk);
|
|
6346
6700
|
const processedData = fixStreamIds(data ?? "", event, idTracker);
|
|
6701
|
+
debugJson(logger$1, "Responses stream chunk:", chunk);
|
|
6347
6702
|
await stream.writeSSE({
|
|
6348
6703
|
id,
|
|
6349
6704
|
event,
|
|
@@ -6476,4 +6831,4 @@ server.route("/:provider/v1/models", providerModelRoutes);
|
|
|
6476
6831
|
|
|
6477
6832
|
//#endregion
|
|
6478
6833
|
export { server };
|
|
6479
|
-
//# sourceMappingURL=server-
|
|
6834
|
+
//# sourceMappingURL=server-CuXJhEMC.js.map
|