fourmis-agents-sdk 0.2.3 → 0.2.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/dist/agents/index.js +549 -2
- package/dist/agents/tools.js +549 -2
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +588 -28
- package/dist/auth/gemini-oauth.d.ts +23 -0
- package/dist/auth/gemini-oauth.d.ts.map +1 -0
- package/dist/auth/gemini-oauth.js +110 -0
- package/dist/index.js +588 -28
- package/dist/mcp/client.d.ts.map +1 -1
- package/dist/mcp/client.js +7 -2
- package/dist/mcp/index.js +7 -2
- package/dist/providers/anthropic.js +61 -0
- package/dist/providers/gemini.d.ts +39 -0
- package/dist/providers/gemini.d.ts.map +1 -0
- package/dist/providers/gemini.js +710 -0
- package/dist/providers/openai.js +61 -0
- package/dist/providers/registry.d.ts.map +1 -1
- package/dist/providers/registry.js +549 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +0 -2
- package/dist/utils/cost.d.ts +13 -0
- package/dist/utils/cost.d.ts.map +1 -1
- package/dist/utils/cost.js +66 -0
- package/package.json +2 -1
package/dist/api.js
CHANGED
|
@@ -369,6 +369,95 @@ async function openBrowser(url) {
|
|
|
369
369
|
var CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann", AUTHORIZE_URL = "https://auth.openai.com/oauth/authorize", TOKEN_URL = "https://auth.openai.com/oauth/token", REDIRECT_URI = "http://localhost:1455/auth/callback", SCOPE = "openid profile email offline_access", CALLBACK_PORT = 1455;
|
|
370
370
|
var init_openai_oauth = () => {};
|
|
371
371
|
|
|
372
|
+
// src/auth/gemini-oauth.ts
|
|
373
|
+
var exports_gemini_oauth = {};
|
|
374
|
+
__export(exports_gemini_oauth, {
|
|
375
|
+
loadTokensSync: () => loadTokensSync2,
|
|
376
|
+
loadTokens: () => loadTokens2,
|
|
377
|
+
isLoggedIn: () => isLoggedIn2,
|
|
378
|
+
getValidToken: () => getValidToken2
|
|
379
|
+
});
|
|
380
|
+
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync2 } from "fs";
|
|
381
|
+
import { join as join2 } from "path";
|
|
382
|
+
import { homedir as homedir2 } from "os";
|
|
383
|
+
function getHome2() {
|
|
384
|
+
return process.env.HOME ?? homedir2();
|
|
385
|
+
}
|
|
386
|
+
function tokenPath2() {
|
|
387
|
+
return join2(getHome2(), ".gemini", "oauth_creds.json");
|
|
388
|
+
}
|
|
389
|
+
function loadTokens2() {
|
|
390
|
+
const p = tokenPath2();
|
|
391
|
+
try {
|
|
392
|
+
const raw = readFileSync2(p, "utf-8");
|
|
393
|
+
const data = JSON.parse(raw);
|
|
394
|
+
if (data.access_token && data.refresh_token) {
|
|
395
|
+
return data;
|
|
396
|
+
}
|
|
397
|
+
} catch {}
|
|
398
|
+
return null;
|
|
399
|
+
}
|
|
400
|
+
function loadTokensSync2() {
|
|
401
|
+
return loadTokens2();
|
|
402
|
+
}
|
|
403
|
+
function saveTokens2(tokens) {
|
|
404
|
+
const p = tokenPath2();
|
|
405
|
+
const dir = join2(getHome2(), ".gemini");
|
|
406
|
+
if (!existsSync2(dir)) {
|
|
407
|
+
const { mkdirSync: mkdirSync2 } = __require("fs");
|
|
408
|
+
mkdirSync2(dir, { recursive: true });
|
|
409
|
+
}
|
|
410
|
+
writeFileSync2(p, JSON.stringify(tokens, null, 2), { mode: 384 });
|
|
411
|
+
}
|
|
412
|
+
async function refreshAccessToken2(refreshToken) {
|
|
413
|
+
const res = await fetch(GOOGLE_TOKEN_URL, {
|
|
414
|
+
method: "POST",
|
|
415
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
416
|
+
body: new URLSearchParams({
|
|
417
|
+
grant_type: "refresh_token",
|
|
418
|
+
client_id: GEMINI_CLIENT_ID,
|
|
419
|
+
client_secret: GEMINI_CLIENT_SECRET,
|
|
420
|
+
refresh_token: refreshToken
|
|
421
|
+
})
|
|
422
|
+
});
|
|
423
|
+
if (!res.ok) {
|
|
424
|
+
const text = await res.text();
|
|
425
|
+
throw new Error(`Gemini token refresh failed (${res.status}): ${text}`);
|
|
426
|
+
}
|
|
427
|
+
return res.json();
|
|
428
|
+
}
|
|
429
|
+
async function getValidToken2() {
|
|
430
|
+
const tokens = loadTokens2();
|
|
431
|
+
if (!tokens)
|
|
432
|
+
return null;
|
|
433
|
+
const expiresAt = tokens.expires_at ?? tokens.expiry_date;
|
|
434
|
+
const needsRefresh = expiresAt ? expiresAt <= Date.now() + 300000 : true;
|
|
435
|
+
if (needsRefresh) {
|
|
436
|
+
try {
|
|
437
|
+
const fresh = await refreshAccessToken2(tokens.refresh_token);
|
|
438
|
+
const expiryMs = Date.now() + fresh.expires_in * 1000;
|
|
439
|
+
const updated = {
|
|
440
|
+
access_token: fresh.access_token,
|
|
441
|
+
refresh_token: fresh.refresh_token ?? tokens.refresh_token,
|
|
442
|
+
token_type: fresh.token_type ?? "Bearer",
|
|
443
|
+
expires_in: fresh.expires_in,
|
|
444
|
+
expires_at: expiryMs,
|
|
445
|
+
expiry_date: expiryMs
|
|
446
|
+
};
|
|
447
|
+
saveTokens2(updated);
|
|
448
|
+
return { accessToken: updated.access_token };
|
|
449
|
+
} catch {
|
|
450
|
+
return { accessToken: tokens.access_token };
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
return { accessToken: tokens.access_token };
|
|
454
|
+
}
|
|
455
|
+
function isLoggedIn2() {
|
|
456
|
+
return loadTokens2() !== null;
|
|
457
|
+
}
|
|
458
|
+
var GEMINI_CLIENT_ID = "681255809395-oo8ft2oprdrnp9e3aqf6av3hmdib135j.apps.googleusercontent.com", GEMINI_CLIENT_SECRET = "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", GOOGLE_TOKEN_URL = "https://oauth2.googleapis.com/token";
|
|
459
|
+
var init_gemini_oauth = () => {};
|
|
460
|
+
|
|
372
461
|
// src/types.ts
|
|
373
462
|
function uuid() {
|
|
374
463
|
return crypto.randomUUID();
|
|
@@ -833,6 +922,67 @@ function findOpenAIPricing(model) {
|
|
|
833
922
|
}
|
|
834
923
|
return bestPricing;
|
|
835
924
|
}
|
|
925
|
+
var GEMINI_PRICING = {
|
|
926
|
+
"gemini-2.5-pro": {
|
|
927
|
+
inputPerMillion: 1.25,
|
|
928
|
+
outputPerMillion: 10,
|
|
929
|
+
cacheReadPerMillion: 0.315
|
|
930
|
+
},
|
|
931
|
+
"gemini-2.5-flash": {
|
|
932
|
+
inputPerMillion: 0.15,
|
|
933
|
+
outputPerMillion: 0.6,
|
|
934
|
+
cacheReadPerMillion: 0.0375
|
|
935
|
+
},
|
|
936
|
+
"gemini-2.5-flash-lite": {
|
|
937
|
+
inputPerMillion: 0.075,
|
|
938
|
+
outputPerMillion: 0.3
|
|
939
|
+
},
|
|
940
|
+
"gemini-2.0-flash": {
|
|
941
|
+
inputPerMillion: 0.1,
|
|
942
|
+
outputPerMillion: 0.4,
|
|
943
|
+
cacheReadPerMillion: 0.025
|
|
944
|
+
},
|
|
945
|
+
"gemini-2.0-flash-lite": {
|
|
946
|
+
inputPerMillion: 0.075,
|
|
947
|
+
outputPerMillion: 0.3
|
|
948
|
+
}
|
|
949
|
+
};
|
|
950
|
+
var GEMINI_CONTEXT_WINDOWS = {
|
|
951
|
+
"gemini-2.5-pro": 1048576,
|
|
952
|
+
"gemini-2.5-flash": 1048576,
|
|
953
|
+
"gemini-2.5-flash-lite": 1048576,
|
|
954
|
+
"gemini-2.0-flash": 1048576,
|
|
955
|
+
"gemini-2.0-flash-lite": 1048576
|
|
956
|
+
};
|
|
957
|
+
var GEMINI_MAX_OUTPUT = {
|
|
958
|
+
"gemini-2.5-pro": 65536,
|
|
959
|
+
"gemini-2.5-flash": 65536,
|
|
960
|
+
"gemini-2.5-flash-lite": 65536,
|
|
961
|
+
"gemini-2.0-flash": 8192,
|
|
962
|
+
"gemini-2.0-flash-lite": 8192
|
|
963
|
+
};
|
|
964
|
+
function calculateGeminiCost(model, usage) {
|
|
965
|
+
const pricing = findGeminiPricing(model);
|
|
966
|
+
if (!pricing)
|
|
967
|
+
return 0;
|
|
968
|
+
const inputCost = usage.inputTokens / 1e6 * pricing.inputPerMillion;
|
|
969
|
+
const outputCost = usage.outputTokens / 1e6 * pricing.outputPerMillion;
|
|
970
|
+
const cacheReadCost = usage.cacheReadInputTokens / 1e6 * (pricing.cacheReadPerMillion ?? pricing.inputPerMillion);
|
|
971
|
+
return inputCost + outputCost + cacheReadCost;
|
|
972
|
+
}
|
|
973
|
+
function findGeminiPricing(model) {
|
|
974
|
+
if (GEMINI_PRICING[model])
|
|
975
|
+
return GEMINI_PRICING[model];
|
|
976
|
+
let bestKey = "";
|
|
977
|
+
let bestPricing;
|
|
978
|
+
for (const [key, pricing] of Object.entries(GEMINI_PRICING)) {
|
|
979
|
+
if (model.startsWith(key) && key.length > bestKey.length) {
|
|
980
|
+
bestKey = key;
|
|
981
|
+
bestPricing = pricing;
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
return bestPricing;
|
|
985
|
+
}
|
|
836
986
|
|
|
837
987
|
// src/providers/anthropic.ts
|
|
838
988
|
import Anthropic from "@anthropic-ai/sdk";
|
|
@@ -1469,6 +1619,398 @@ function loadTokensSync() {
|
|
|
1469
1619
|
return null;
|
|
1470
1620
|
}
|
|
1471
1621
|
|
|
1622
|
+
// src/providers/gemini.ts
|
|
1623
|
+
import { GoogleGenAI } from "@google/genai";
|
|
1624
|
+
var CODE_ASSIST_ENDPOINT = "https://cloudcode-pa.googleapis.com";
|
|
1625
|
+
var CODE_ASSIST_API_VERSION = "v1internal";
|
|
1626
|
+
|
|
1627
|
+
class GeminiAdapter {
|
|
1628
|
+
name = "gemini";
|
|
1629
|
+
client = null;
|
|
1630
|
+
oauthMode;
|
|
1631
|
+
currentAccessToken;
|
|
1632
|
+
projectId;
|
|
1633
|
+
constructor(options) {
|
|
1634
|
+
const key = options?.apiKey ?? process.env.GEMINI_API_KEY;
|
|
1635
|
+
if (key) {
|
|
1636
|
+
this.oauthMode = false;
|
|
1637
|
+
this.client = new GoogleGenAI({ apiKey: key });
|
|
1638
|
+
} else {
|
|
1639
|
+
const tokens = loadTokensSync3();
|
|
1640
|
+
if (tokens) {
|
|
1641
|
+
this.oauthMode = true;
|
|
1642
|
+
this.currentAccessToken = tokens.access_token;
|
|
1643
|
+
} else {
|
|
1644
|
+
this.oauthMode = false;
|
|
1645
|
+
this.client = new GoogleGenAI({ apiKey: "" });
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1648
|
+
}
|
|
1649
|
+
async* chat(request) {
|
|
1650
|
+
if (this.oauthMode) {
|
|
1651
|
+
yield* this.chatCodeAssist(request);
|
|
1652
|
+
} else {
|
|
1653
|
+
yield* this.chatSdk(request);
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
calculateCost(model, usage) {
|
|
1657
|
+
return calculateGeminiCost(model, usage);
|
|
1658
|
+
}
|
|
1659
|
+
getContextWindow(model) {
|
|
1660
|
+
return GEMINI_CONTEXT_WINDOWS[model] ?? 1048576;
|
|
1661
|
+
}
|
|
1662
|
+
supportsFeature(feature) {
|
|
1663
|
+
switch (feature) {
|
|
1664
|
+
case "streaming":
|
|
1665
|
+
case "tool_calling":
|
|
1666
|
+
case "image_input":
|
|
1667
|
+
case "structured_output":
|
|
1668
|
+
case "thinking":
|
|
1669
|
+
case "pdf_input":
|
|
1670
|
+
return true;
|
|
1671
|
+
default:
|
|
1672
|
+
return false;
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1675
|
+
async* chatSdk(request) {
|
|
1676
|
+
const contents = this.convertMessages(request.messages);
|
|
1677
|
+
const tools = request.tools ? this.convertTools(request.tools) : undefined;
|
|
1678
|
+
const maxTokens = request.maxTokens ?? GEMINI_MAX_OUTPUT[request.model] ?? 65536;
|
|
1679
|
+
const config = {
|
|
1680
|
+
maxOutputTokens: maxTokens,
|
|
1681
|
+
abortSignal: request.signal ?? undefined
|
|
1682
|
+
};
|
|
1683
|
+
if (request.systemPrompt) {
|
|
1684
|
+
config.systemInstruction = request.systemPrompt;
|
|
1685
|
+
}
|
|
1686
|
+
if (request.temperature !== undefined) {
|
|
1687
|
+
config.temperature = request.temperature;
|
|
1688
|
+
}
|
|
1689
|
+
if (tools) {
|
|
1690
|
+
config.tools = [{ functionDeclarations: tools }];
|
|
1691
|
+
}
|
|
1692
|
+
const stream = await this.client.models.generateContentStream({
|
|
1693
|
+
model: request.model,
|
|
1694
|
+
contents,
|
|
1695
|
+
config
|
|
1696
|
+
});
|
|
1697
|
+
let hasToolCalls = false;
|
|
1698
|
+
for await (const chunk of stream) {
|
|
1699
|
+
if (chunk.usageMetadata) {
|
|
1700
|
+
const u = chunk.usageMetadata;
|
|
1701
|
+
const cached = u.cachedContentTokenCount ?? 0;
|
|
1702
|
+
yield {
|
|
1703
|
+
type: "usage",
|
|
1704
|
+
usage: {
|
|
1705
|
+
inputTokens: (u.promptTokenCount ?? 0) - cached,
|
|
1706
|
+
outputTokens: u.candidatesTokenCount ?? 0,
|
|
1707
|
+
cacheReadInputTokens: cached,
|
|
1708
|
+
cacheCreationInputTokens: 0
|
|
1709
|
+
}
|
|
1710
|
+
};
|
|
1711
|
+
}
|
|
1712
|
+
const candidate = chunk.candidates?.[0];
|
|
1713
|
+
if (!candidate?.content?.parts)
|
|
1714
|
+
continue;
|
|
1715
|
+
for (const part of candidate.content.parts) {
|
|
1716
|
+
if (part.text) {
|
|
1717
|
+
if (part.thought) {
|
|
1718
|
+
yield { type: "thinking_delta", text: part.text };
|
|
1719
|
+
} else {
|
|
1720
|
+
yield { type: "text_delta", text: part.text };
|
|
1721
|
+
}
|
|
1722
|
+
}
|
|
1723
|
+
if (part.functionCall) {
|
|
1724
|
+
hasToolCalls = true;
|
|
1725
|
+
yield {
|
|
1726
|
+
type: "tool_call",
|
|
1727
|
+
id: part.functionCall.id ?? crypto.randomUUID(),
|
|
1728
|
+
name: part.functionCall.name ?? "",
|
|
1729
|
+
input: part.functionCall.args ?? {}
|
|
1730
|
+
};
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
if (candidate.finishReason) {
|
|
1734
|
+
yield {
|
|
1735
|
+
type: "done",
|
|
1736
|
+
stopReason: this.mapFinishReason(candidate.finishReason, hasToolCalls)
|
|
1737
|
+
};
|
|
1738
|
+
return;
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
yield { type: "done", stopReason: hasToolCalls ? "tool_use" : "end_turn" };
|
|
1742
|
+
}
|
|
1743
|
+
async* chatCodeAssist(request) {
|
|
1744
|
+
await this.refreshTokenIfNeeded();
|
|
1745
|
+
await this.ensureProjectId();
|
|
1746
|
+
const contents = this.convertMessages(request.messages);
|
|
1747
|
+
const tools = request.tools ? this.convertTools(request.tools) : undefined;
|
|
1748
|
+
const maxTokens = request.maxTokens ?? GEMINI_MAX_OUTPUT[request.model] ?? 65536;
|
|
1749
|
+
const generationConfig = {
|
|
1750
|
+
maxOutputTokens: maxTokens
|
|
1751
|
+
};
|
|
1752
|
+
if (request.temperature !== undefined) {
|
|
1753
|
+
generationConfig.temperature = request.temperature;
|
|
1754
|
+
}
|
|
1755
|
+
const innerRequest = {
|
|
1756
|
+
contents,
|
|
1757
|
+
generationConfig
|
|
1758
|
+
};
|
|
1759
|
+
if (request.systemPrompt) {
|
|
1760
|
+
innerRequest.systemInstruction = {
|
|
1761
|
+
parts: [{ text: request.systemPrompt }]
|
|
1762
|
+
};
|
|
1763
|
+
}
|
|
1764
|
+
if (tools) {
|
|
1765
|
+
innerRequest.tools = [{ functionDeclarations: tools }];
|
|
1766
|
+
}
|
|
1767
|
+
const body = {
|
|
1768
|
+
model: request.model,
|
|
1769
|
+
project: this.projectId,
|
|
1770
|
+
request: innerRequest
|
|
1771
|
+
};
|
|
1772
|
+
const url = `${CODE_ASSIST_ENDPOINT}/${CODE_ASSIST_API_VERSION}:streamGenerateContent?alt=sse`;
|
|
1773
|
+
const res = await fetch(url, {
|
|
1774
|
+
method: "POST",
|
|
1775
|
+
headers: {
|
|
1776
|
+
"Content-Type": "application/json",
|
|
1777
|
+
Authorization: `Bearer ${this.currentAccessToken}`
|
|
1778
|
+
},
|
|
1779
|
+
body: JSON.stringify(body),
|
|
1780
|
+
signal: request.signal ?? undefined
|
|
1781
|
+
});
|
|
1782
|
+
if (!res.ok) {
|
|
1783
|
+
const text = await res.text();
|
|
1784
|
+
throw new Error(`Gemini Code Assist API error (${res.status}): ${text}`);
|
|
1785
|
+
}
|
|
1786
|
+
yield* this.parseSSEStream(res);
|
|
1787
|
+
}
|
|
1788
|
+
async* parseSSEStream(res) {
|
|
1789
|
+
const reader = res.body.getReader();
|
|
1790
|
+
const decoder = new TextDecoder;
|
|
1791
|
+
let buffer = "";
|
|
1792
|
+
let hasToolCalls = false;
|
|
1793
|
+
let dataLines = [];
|
|
1794
|
+
while (true) {
|
|
1795
|
+
const { done, value } = await reader.read();
|
|
1796
|
+
if (done)
|
|
1797
|
+
break;
|
|
1798
|
+
buffer += decoder.decode(value, { stream: true });
|
|
1799
|
+
const lines = buffer.split(`
|
|
1800
|
+
`);
|
|
1801
|
+
buffer = lines.pop() || "";
|
|
1802
|
+
for (const line of lines) {
|
|
1803
|
+
if (line.startsWith("data: ")) {
|
|
1804
|
+
dataLines.push(line.slice(6));
|
|
1805
|
+
} else if (line.trim() === "" && dataLines.length > 0) {
|
|
1806
|
+
const json = dataLines.join(`
|
|
1807
|
+
`);
|
|
1808
|
+
dataLines = [];
|
|
1809
|
+
let obj;
|
|
1810
|
+
try {
|
|
1811
|
+
obj = JSON.parse(json);
|
|
1812
|
+
} catch {
|
|
1813
|
+
continue;
|
|
1814
|
+
}
|
|
1815
|
+
const response = obj.response ?? obj;
|
|
1816
|
+
const candidate = response?.candidates?.[0];
|
|
1817
|
+
if (candidate?.content?.parts) {
|
|
1818
|
+
for (const part of candidate.content.parts) {
|
|
1819
|
+
if (part.text) {
|
|
1820
|
+
if (part.thought) {
|
|
1821
|
+
yield { type: "thinking_delta", text: part.text };
|
|
1822
|
+
} else {
|
|
1823
|
+
yield { type: "text_delta", text: part.text };
|
|
1824
|
+
}
|
|
1825
|
+
}
|
|
1826
|
+
if (part.functionCall) {
|
|
1827
|
+
hasToolCalls = true;
|
|
1828
|
+
yield {
|
|
1829
|
+
type: "tool_call",
|
|
1830
|
+
id: part.functionCall.id ?? crypto.randomUUID(),
|
|
1831
|
+
name: part.functionCall.name ?? "",
|
|
1832
|
+
input: part.functionCall.args ?? {}
|
|
1833
|
+
};
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
}
|
|
1837
|
+
if (response?.usageMetadata) {
|
|
1838
|
+
const u = response.usageMetadata;
|
|
1839
|
+
const cached = u.cachedContentTokenCount ?? 0;
|
|
1840
|
+
yield {
|
|
1841
|
+
type: "usage",
|
|
1842
|
+
usage: {
|
|
1843
|
+
inputTokens: (u.promptTokenCount ?? 0) - cached,
|
|
1844
|
+
outputTokens: u.candidatesTokenCount ?? 0,
|
|
1845
|
+
cacheReadInputTokens: cached,
|
|
1846
|
+
cacheCreationInputTokens: 0
|
|
1847
|
+
}
|
|
1848
|
+
};
|
|
1849
|
+
}
|
|
1850
|
+
if (candidate?.finishReason) {
|
|
1851
|
+
yield {
|
|
1852
|
+
type: "done",
|
|
1853
|
+
stopReason: this.mapFinishReason(candidate.finishReason, hasToolCalls)
|
|
1854
|
+
};
|
|
1855
|
+
return;
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
}
|
|
1860
|
+
if (dataLines.length > 0) {
|
|
1861
|
+
const json = dataLines.join(`
|
|
1862
|
+
`);
|
|
1863
|
+
try {
|
|
1864
|
+
const obj = JSON.parse(json);
|
|
1865
|
+
const response = obj.response ?? obj;
|
|
1866
|
+
const candidate = response?.candidates?.[0];
|
|
1867
|
+
if (candidate?.finishReason) {
|
|
1868
|
+
yield {
|
|
1869
|
+
type: "done",
|
|
1870
|
+
stopReason: this.mapFinishReason(candidate.finishReason, hasToolCalls)
|
|
1871
|
+
};
|
|
1872
|
+
return;
|
|
1873
|
+
}
|
|
1874
|
+
} catch {}
|
|
1875
|
+
}
|
|
1876
|
+
yield { type: "done", stopReason: hasToolCalls ? "tool_use" : "end_turn" };
|
|
1877
|
+
}
|
|
1878
|
+
async refreshTokenIfNeeded() {
|
|
1879
|
+
if (!this.oauthMode)
|
|
1880
|
+
return;
|
|
1881
|
+
try {
|
|
1882
|
+
const { getValidToken: getValidToken3 } = await Promise.resolve().then(() => (init_gemini_oauth(), exports_gemini_oauth));
|
|
1883
|
+
const result = await getValidToken3();
|
|
1884
|
+
if (result && result.accessToken !== this.currentAccessToken) {
|
|
1885
|
+
this.currentAccessToken = result.accessToken;
|
|
1886
|
+
}
|
|
1887
|
+
} catch {}
|
|
1888
|
+
}
|
|
1889
|
+
async ensureProjectId() {
|
|
1890
|
+
if (this.projectId)
|
|
1891
|
+
return;
|
|
1892
|
+
this.projectId = process.env.GOOGLE_CLOUD_PROJECT ?? process.env.GOOGLE_CLOUD_PROJECT_ID ?? undefined;
|
|
1893
|
+
if (this.projectId)
|
|
1894
|
+
return;
|
|
1895
|
+
try {
|
|
1896
|
+
const res = await fetch(`${CODE_ASSIST_ENDPOINT}/${CODE_ASSIST_API_VERSION}:loadCodeAssist`, {
|
|
1897
|
+
method: "POST",
|
|
1898
|
+
headers: {
|
|
1899
|
+
"Content-Type": "application/json",
|
|
1900
|
+
Authorization: `Bearer ${this.currentAccessToken}`
|
|
1901
|
+
},
|
|
1902
|
+
body: JSON.stringify({
|
|
1903
|
+
metadata: {
|
|
1904
|
+
ideType: "IDE_UNSPECIFIED",
|
|
1905
|
+
platform: "PLATFORM_UNSPECIFIED",
|
|
1906
|
+
pluginType: "GEMINI"
|
|
1907
|
+
}
|
|
1908
|
+
})
|
|
1909
|
+
});
|
|
1910
|
+
if (res.ok) {
|
|
1911
|
+
const data = await res.json();
|
|
1912
|
+
this.projectId = data.cloudaicompanionProject;
|
|
1913
|
+
}
|
|
1914
|
+
} catch {}
|
|
1915
|
+
}
|
|
1916
|
+
convertMessages(messages) {
|
|
1917
|
+
const result = [];
|
|
1918
|
+
for (const msg of messages) {
|
|
1919
|
+
if (typeof msg.content === "string") {
|
|
1920
|
+
result.push({
|
|
1921
|
+
role: msg.role === "assistant" ? "model" : "user",
|
|
1922
|
+
parts: [{ text: msg.content }]
|
|
1923
|
+
});
|
|
1924
|
+
continue;
|
|
1925
|
+
}
|
|
1926
|
+
if (msg.role === "assistant") {
|
|
1927
|
+
const parts = [];
|
|
1928
|
+
for (const block of msg.content) {
|
|
1929
|
+
if (block.type === "text") {
|
|
1930
|
+
parts.push({ text: block.text });
|
|
1931
|
+
} else if (block.type === "tool_use") {
|
|
1932
|
+
parts.push({
|
|
1933
|
+
functionCall: {
|
|
1934
|
+
name: block.name,
|
|
1935
|
+
args: block.input
|
|
1936
|
+
}
|
|
1937
|
+
});
|
|
1938
|
+
}
|
|
1939
|
+
}
|
|
1940
|
+
if (parts.length > 0) {
|
|
1941
|
+
result.push({ role: "model", parts });
|
|
1942
|
+
}
|
|
1943
|
+
} else {
|
|
1944
|
+
const textParts = [];
|
|
1945
|
+
const functionResponseParts = [];
|
|
1946
|
+
for (const block of msg.content) {
|
|
1947
|
+
if (block.type === "text") {
|
|
1948
|
+
textParts.push({ text: block.text });
|
|
1949
|
+
} else if (block.type === "tool_result") {
|
|
1950
|
+
functionResponseParts.push({
|
|
1951
|
+
functionResponse: {
|
|
1952
|
+
name: findToolName(messages, block.tool_use_id) ?? "unknown",
|
|
1953
|
+
response: { result: block.content }
|
|
1954
|
+
}
|
|
1955
|
+
});
|
|
1956
|
+
}
|
|
1957
|
+
}
|
|
1958
|
+
if (functionResponseParts.length > 0) {
|
|
1959
|
+
result.push({ role: "user", parts: functionResponseParts });
|
|
1960
|
+
}
|
|
1961
|
+
if (textParts.length > 0) {
|
|
1962
|
+
result.push({ role: "user", parts: textParts });
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
}
|
|
1966
|
+
return result;
|
|
1967
|
+
}
|
|
1968
|
+
convertTools(tools) {
|
|
1969
|
+
return tools.map((tool) => ({
|
|
1970
|
+
name: tool.name,
|
|
1971
|
+
description: tool.description,
|
|
1972
|
+
parameters: tool.inputSchema
|
|
1973
|
+
}));
|
|
1974
|
+
}
|
|
1975
|
+
mapFinishReason(reason, hasToolCalls) {
|
|
1976
|
+
if (hasToolCalls)
|
|
1977
|
+
return "tool_use";
|
|
1978
|
+
switch (reason) {
|
|
1979
|
+
case "STOP":
|
|
1980
|
+
return "end_turn";
|
|
1981
|
+
case "MAX_TOKENS":
|
|
1982
|
+
return "max_tokens";
|
|
1983
|
+
default:
|
|
1984
|
+
return "end_turn";
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
}
|
|
1988
|
+
function findToolName(messages, toolUseId) {
|
|
1989
|
+
for (const msg of messages) {
|
|
1990
|
+
if (msg.role !== "assistant" || typeof msg.content === "string")
|
|
1991
|
+
continue;
|
|
1992
|
+
for (const block of msg.content) {
|
|
1993
|
+
if (block.type === "tool_use" && block.id === toolUseId) {
|
|
1994
|
+
return block.name;
|
|
1995
|
+
}
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1998
|
+
return;
|
|
1999
|
+
}
|
|
2000
|
+
function loadTokensSync3() {
|
|
2001
|
+
const home = process.env.HOME ?? __require("os").homedir();
|
|
2002
|
+
const path = `${home}/.gemini/oauth_creds.json`;
|
|
2003
|
+
try {
|
|
2004
|
+
const fs = __require("fs");
|
|
2005
|
+
const raw = fs.readFileSync(path, "utf-8");
|
|
2006
|
+
const data = JSON.parse(raw);
|
|
2007
|
+
if (data.access_token && data.refresh_token) {
|
|
2008
|
+
return data;
|
|
2009
|
+
}
|
|
2010
|
+
} catch {}
|
|
2011
|
+
return null;
|
|
2012
|
+
}
|
|
2013
|
+
|
|
1472
2014
|
// src/providers/registry.ts
|
|
1473
2015
|
var providers = new Map;
|
|
1474
2016
|
function registerProvider(name, adapter) {
|
|
@@ -1494,6 +2036,13 @@ function getProvider(name, options) {
|
|
|
1494
2036
|
}
|
|
1495
2037
|
return adapter;
|
|
1496
2038
|
}
|
|
2039
|
+
if (name === "gemini") {
|
|
2040
|
+
const adapter = new GeminiAdapter(options);
|
|
2041
|
+
if (!options?.apiKey && !options?.baseUrl) {
|
|
2042
|
+
providers.set(name, adapter);
|
|
2043
|
+
}
|
|
2044
|
+
return adapter;
|
|
2045
|
+
}
|
|
1497
2046
|
throw new Error(`Unknown provider: "${name}". Register it with registerProvider() first.`);
|
|
1498
2047
|
}
|
|
1499
2048
|
function listProviders() {
|
|
@@ -2058,8 +2607,6 @@ var ALL_TOOLS = {
|
|
|
2058
2607
|
function buildToolRegistry(toolNames, allowedTools, disallowedTools) {
|
|
2059
2608
|
const registry = new ToolRegistry;
|
|
2060
2609
|
for (const name of toolNames) {
|
|
2061
|
-
if (allowedTools && !allowedTools.includes(name))
|
|
2062
|
-
continue;
|
|
2063
2610
|
if (disallowedTools?.includes(name))
|
|
2064
2611
|
continue;
|
|
2065
2612
|
const tool = ALL_TOOLS[name];
|
|
@@ -2212,9 +2759,9 @@ class PermissionManager {
|
|
|
2212
2759
|
}
|
|
2213
2760
|
|
|
2214
2761
|
// src/settings.ts
|
|
2215
|
-
import { existsSync as
|
|
2216
|
-
import { dirname as dirname2, join as
|
|
2217
|
-
import { homedir as
|
|
2762
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
2763
|
+
import { dirname as dirname2, join as join3 } from "path";
|
|
2764
|
+
import { homedir as homedir3 } from "os";
|
|
2218
2765
|
|
|
2219
2766
|
class SettingsManager {
|
|
2220
2767
|
cwd;
|
|
@@ -2292,36 +2839,36 @@ class SettingsManager {
|
|
|
2292
2839
|
return;
|
|
2293
2840
|
}
|
|
2294
2841
|
const dir = dirname2(path);
|
|
2295
|
-
if (!
|
|
2842
|
+
if (!existsSync3(dir))
|
|
2296
2843
|
mkdirSync2(dir, { recursive: true });
|
|
2297
|
-
|
|
2844
|
+
writeFileSync3(path, JSON.stringify(data, null, 2) + `
|
|
2298
2845
|
`);
|
|
2299
2846
|
}
|
|
2300
2847
|
sourceToPath(source) {
|
|
2301
2848
|
switch (source) {
|
|
2302
2849
|
case "user":
|
|
2303
|
-
return
|
|
2850
|
+
return join3(homedir3(), ".claude", "settings.json");
|
|
2304
2851
|
case "project":
|
|
2305
|
-
return
|
|
2852
|
+
return join3(this.cwd, ".claude", "settings.json");
|
|
2306
2853
|
case "local":
|
|
2307
|
-
return
|
|
2854
|
+
return join3(this.cwd, ".claude", "settings.local.json");
|
|
2308
2855
|
}
|
|
2309
2856
|
}
|
|
2310
2857
|
destinationToPath(destination) {
|
|
2311
2858
|
switch (destination) {
|
|
2312
2859
|
case "userSettings":
|
|
2313
|
-
return
|
|
2860
|
+
return join3(homedir3(), ".claude", "settings.json");
|
|
2314
2861
|
case "projectSettings":
|
|
2315
|
-
return
|
|
2862
|
+
return join3(this.cwd, ".claude", "settings.json");
|
|
2316
2863
|
case "localSettings":
|
|
2317
|
-
return
|
|
2864
|
+
return join3(this.cwd, ".claude", "settings.local.json");
|
|
2318
2865
|
default:
|
|
2319
2866
|
return null;
|
|
2320
2867
|
}
|
|
2321
2868
|
}
|
|
2322
2869
|
readJson(path) {
|
|
2323
2870
|
try {
|
|
2324
|
-
const content =
|
|
2871
|
+
const content = readFileSync3(path, "utf-8");
|
|
2325
2872
|
return JSON.parse(content);
|
|
2326
2873
|
} catch {
|
|
2327
2874
|
return null;
|
|
@@ -2339,8 +2886,8 @@ class SettingsManager {
|
|
|
2339
2886
|
}
|
|
2340
2887
|
|
|
2341
2888
|
// src/utils/system-prompt.ts
|
|
2342
|
-
import { readFileSync as
|
|
2343
|
-
import { join as
|
|
2889
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
2890
|
+
import { join as join4 } from "path";
|
|
2344
2891
|
var CORE_IDENTITY = `You are an AI coding agent. You help users with software engineering tasks by reading, writing, and modifying code. You have access to tools that let you interact with the filesystem and execute commands.
|
|
2345
2892
|
|
|
2346
2893
|
You are highly capable and can help users complete complex tasks that would otherwise be too difficult or time-consuming.`;
|
|
@@ -2421,7 +2968,7 @@ ${instructions}`);
|
|
|
2421
2968
|
function readProjectInstructions(cwd) {
|
|
2422
2969
|
for (const name of ["CLAUDE.md", "AGENTS.md"]) {
|
|
2423
2970
|
try {
|
|
2424
|
-
const content =
|
|
2971
|
+
const content = readFileSync4(join4(cwd, name), "utf-8").trim();
|
|
2425
2972
|
if (content)
|
|
2426
2973
|
return content;
|
|
2427
2974
|
} catch {}
|
|
@@ -2542,11 +3089,16 @@ class McpClientManager {
|
|
|
2542
3089
|
await client.connect(clientTransport);
|
|
2543
3090
|
} else if (config.type === "sse") {
|
|
2544
3091
|
const { SSEClientTransport } = await import("@modelcontextprotocol/sdk/client/sse.js");
|
|
2545
|
-
const
|
|
3092
|
+
const opts = config.headers ? {
|
|
3093
|
+
eventSourceInit: { headers: config.headers },
|
|
3094
|
+
requestInit: { headers: config.headers }
|
|
3095
|
+
} : undefined;
|
|
3096
|
+
const transport = new SSEClientTransport(new URL(config.url), opts);
|
|
2546
3097
|
await client.connect(transport);
|
|
2547
3098
|
} else if (config.type === "http") {
|
|
2548
3099
|
const { StreamableHTTPClientTransport } = await import("@modelcontextprotocol/sdk/client/streamableHttp.js");
|
|
2549
|
-
const
|
|
3100
|
+
const requestInit = config.headers ? { headers: config.headers } : undefined;
|
|
3101
|
+
const transport = new StreamableHTTPClientTransport(new URL(config.url), { requestInit });
|
|
2550
3102
|
await client.connect(transport);
|
|
2551
3103
|
} else {
|
|
2552
3104
|
const { StdioClientTransport } = await import("@modelcontextprotocol/sdk/client/stdio.js");
|
|
@@ -2929,21 +3481,21 @@ class TaskManager {
|
|
|
2929
3481
|
}
|
|
2930
3482
|
|
|
2931
3483
|
// src/utils/session-store.ts
|
|
2932
|
-
import { readFileSync as
|
|
2933
|
-
import { join as
|
|
2934
|
-
import { homedir as
|
|
3484
|
+
import { readFileSync as readFileSync5, appendFileSync, mkdirSync as mkdirSync3, readdirSync, statSync } from "fs";
|
|
3485
|
+
import { join as join5 } from "path";
|
|
3486
|
+
import { homedir as homedir4 } from "os";
|
|
2935
3487
|
function sanitizeCwd(cwd) {
|
|
2936
3488
|
return cwd.replace(/[/.]/g, "-");
|
|
2937
3489
|
}
|
|
2938
3490
|
function sessionsDir(cwd) {
|
|
2939
|
-
return
|
|
3491
|
+
return join5(homedir4(), ".claude", "projects", sanitizeCwd(cwd));
|
|
2940
3492
|
}
|
|
2941
3493
|
function ensureDir(dir) {
|
|
2942
3494
|
mkdirSync3(dir, { recursive: true });
|
|
2943
3495
|
}
|
|
2944
3496
|
function logMessage(dir, sessionId, entry) {
|
|
2945
3497
|
ensureDir(dir);
|
|
2946
|
-
const filePath =
|
|
3498
|
+
const filePath = join5(dir, `${sessionId}.jsonl`);
|
|
2947
3499
|
appendFileSync(filePath, JSON.stringify(entry) + `
|
|
2948
3500
|
`);
|
|
2949
3501
|
}
|
|
@@ -2981,7 +3533,7 @@ function findLatestSession(cwd) {
|
|
|
2981
3533
|
const dir = sessionsDir(cwd);
|
|
2982
3534
|
try {
|
|
2983
3535
|
const files = readdirSync(dir).filter((f) => f.endsWith(".jsonl")).map((f) => {
|
|
2984
|
-
const filePath =
|
|
3536
|
+
const filePath = join5(dir, f);
|
|
2985
3537
|
try {
|
|
2986
3538
|
return { name: f, mtime: statSync(filePath).mtimeMs };
|
|
2987
3539
|
} catch {
|
|
@@ -2997,10 +3549,10 @@ function findLatestSession(cwd) {
|
|
|
2997
3549
|
}
|
|
2998
3550
|
function loadSessionMessages(cwd, sessionId) {
|
|
2999
3551
|
const dir = sessionsDir(cwd);
|
|
3000
|
-
const filePath =
|
|
3552
|
+
const filePath = join5(dir, `${sessionId}.jsonl`);
|
|
3001
3553
|
let lines;
|
|
3002
3554
|
try {
|
|
3003
|
-
lines =
|
|
3555
|
+
lines = readFileSync5(filePath, "utf-8").trim().split(`
|
|
3004
3556
|
`).filter(Boolean);
|
|
3005
3557
|
} catch {
|
|
3006
3558
|
return [];
|
|
@@ -3044,7 +3596,7 @@ function query(params) {
|
|
|
3044
3596
|
});
|
|
3045
3597
|
const model = options.model ?? DEFAULT_MODEL;
|
|
3046
3598
|
const toolNames = resolveToolNames(options.tools);
|
|
3047
|
-
const registry = buildToolRegistry(toolNames,
|
|
3599
|
+
const registry = buildToolRegistry(toolNames, undefined, options.disallowedTools);
|
|
3048
3600
|
const systemPrompt = options.systemPrompt ?? buildSystemPrompt({
|
|
3049
3601
|
tools: registry.list(),
|
|
3050
3602
|
cwd: options.cwd,
|
|
@@ -3072,6 +3624,14 @@ function query(params) {
|
|
|
3072
3624
|
mergedPermissions.deny = mergedDeny;
|
|
3073
3625
|
}
|
|
3074
3626
|
}
|
|
3627
|
+
if (options.allowedTools && options.allowedTools.length > 0) {
|
|
3628
|
+
if (!mergedPermissions)
|
|
3629
|
+
mergedPermissions = {};
|
|
3630
|
+
mergedPermissions.allow = [
|
|
3631
|
+
...mergedPermissions.allow ?? [],
|
|
3632
|
+
...options.allowedTools
|
|
3633
|
+
];
|
|
3634
|
+
}
|
|
3075
3635
|
const permissions = new PermissionManager(options.permissionMode ?? "default", options.canUseTool, mergedPermissions, settingsManager);
|
|
3076
3636
|
const cwd = options.cwd ?? process.cwd();
|
|
3077
3637
|
let sessionId = options.sessionId ?? uuid();
|