open-research 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-3GZIDCV2.js +185 -0
- package/dist/{chunk-TQSQRNX6.js → chunk-3KZN54JZ.js} +23 -34
- package/dist/{chunk-I5NVYKG7.js → chunk-4HCPHCC2.js} +4 -0
- package/dist/chunk-77Q5B5H7.js +27 -0
- package/dist/chunk-GVEVKDGV.js +68 -0
- package/dist/{chunk-ZUSIRA5S.js → chunk-HRVDYJEC.js} +1 -1
- package/dist/cli.js +656 -204
- package/dist/gemini-login-EYY3EFH4.js +94 -0
- package/dist/{manager-queue-F4VVZMTE.js → manager-queue-FBAUCAGI.js} +4 -1
- package/dist/{query-agent-LRUUJR4F.js → query-agent-WM6UNZ37.js} +5 -2
- package/dist/{relevance-agent-CCN7JGTM.js → relevance-agent-H3U6TROD.js} +4 -1
- package/dist/{sessions-GRES2MUV.js → sessions-KL4LUGD7.js} +2 -2
- package/dist/{web-search-B7D5WMHU.js → web-search-TKBFSU3M.js} +4 -2
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -2,7 +2,16 @@
|
|
|
2
2
|
import {
|
|
3
3
|
appendSessionEvent,
|
|
4
4
|
loadSessionHistory
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-HRVDYJEC.js";
|
|
6
|
+
import {
|
|
7
|
+
GEMINI_CODE_ASSIST_URL,
|
|
8
|
+
clearStoredAuth,
|
|
9
|
+
loadGeminiAuth,
|
|
10
|
+
loadStoredAuth,
|
|
11
|
+
refreshGeminiAccessToken,
|
|
12
|
+
saveGeminiAuth,
|
|
13
|
+
saveStoredAuth
|
|
14
|
+
} from "./chunk-3GZIDCV2.js";
|
|
6
15
|
import {
|
|
7
16
|
ensureOpenResearchConfig,
|
|
8
17
|
executeFetchUrl,
|
|
@@ -15,19 +24,26 @@ import {
|
|
|
15
24
|
getOpenAlexApiKey,
|
|
16
25
|
getSemanticScholarApiKey,
|
|
17
26
|
loadOpenResearchConfig,
|
|
18
|
-
readJsonFile,
|
|
19
27
|
saveOpenResearchConfig,
|
|
20
|
-
themeValues
|
|
28
|
+
themeValues
|
|
29
|
+
} from "./chunk-3KZN54JZ.js";
|
|
30
|
+
import {
|
|
31
|
+
readJsonFile,
|
|
21
32
|
writeJsonFile
|
|
22
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-77Q5B5H7.js";
|
|
23
34
|
import {
|
|
24
|
-
getOpenResearchAuthFile,
|
|
25
35
|
getOpenResearchRoot,
|
|
26
36
|
getOpenResearchSkillsDir,
|
|
27
37
|
getWorkspaceMetaDir,
|
|
28
38
|
getWorkspaceProjectFile,
|
|
29
39
|
getWorkspaceSessionsDir
|
|
30
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-4HCPHCC2.js";
|
|
41
|
+
import {
|
|
42
|
+
getAvailableModels,
|
|
43
|
+
getDefaultModel,
|
|
44
|
+
getProviderCatalog,
|
|
45
|
+
selectModelForTask
|
|
46
|
+
} from "./chunk-GVEVKDGV.js";
|
|
31
47
|
import {
|
|
32
48
|
__require
|
|
33
49
|
} from "./chunk-3RG5ZIWI.js";
|
|
@@ -164,7 +180,7 @@ ${markdown}`;
|
|
|
164
180
|
}
|
|
165
181
|
|
|
166
182
|
// src/lib/auth/import-codex.ts
|
|
167
|
-
import
|
|
183
|
+
import fs3 from "fs/promises";
|
|
168
184
|
import path3 from "path";
|
|
169
185
|
|
|
170
186
|
// src/lib/storage/credential-types.ts
|
|
@@ -193,30 +209,6 @@ function getBootstrapCredentialValidation() {
|
|
|
193
209
|
};
|
|
194
210
|
}
|
|
195
211
|
|
|
196
|
-
// src/lib/auth/store.ts
|
|
197
|
-
import fs3 from "fs/promises";
|
|
198
|
-
var AUTH_FILE_MODE = 384;
|
|
199
|
-
async function ensureCliHome(options) {
|
|
200
|
-
const root = getOpenResearchRoot(options);
|
|
201
|
-
await fs3.mkdir(root, { recursive: true, mode: 448 });
|
|
202
|
-
return root;
|
|
203
|
-
}
|
|
204
|
-
async function saveStoredAuth(auth2, options) {
|
|
205
|
-
await ensureCliHome(options);
|
|
206
|
-
const authFile = getOpenResearchAuthFile(options);
|
|
207
|
-
await writeJsonFile(authFile, auth2, AUTH_FILE_MODE);
|
|
208
|
-
await fs3.chmod(authFile, AUTH_FILE_MODE);
|
|
209
|
-
return authFile;
|
|
210
|
-
}
|
|
211
|
-
async function loadStoredAuth(options) {
|
|
212
|
-
const authFile = getOpenResearchAuthFile(options);
|
|
213
|
-
return readJsonFile(authFile, null);
|
|
214
|
-
}
|
|
215
|
-
async function clearStoredAuth(options) {
|
|
216
|
-
const authFile = getOpenResearchAuthFile(options);
|
|
217
|
-
await fs3.rm(authFile, { force: true });
|
|
218
|
-
}
|
|
219
|
-
|
|
220
212
|
// src/lib/auth/openai-oauth.ts
|
|
221
213
|
import { createHash, randomBytes } from "crypto";
|
|
222
214
|
var OPENAI_AUTH_URL = "https://auth.openai.com/oauth/authorize";
|
|
@@ -309,7 +301,7 @@ async function importCodexAuth(options = {}) {
|
|
|
309
301
|
const now = options.now ?? Date.now;
|
|
310
302
|
const codexAuthPath = options.codexAuthFilePath ?? path3.join(options.homeDir ?? process.env.HOME ?? "", ".codex", "auth.json");
|
|
311
303
|
const parsed = JSON.parse(
|
|
312
|
-
await
|
|
304
|
+
await fs3.readFile(codexAuthPath, "utf8")
|
|
313
305
|
);
|
|
314
306
|
if (parsed.auth_mode !== "chatgpt") {
|
|
315
307
|
throw new Error("Codex is not signed in with OpenAI on this device.");
|
|
@@ -480,54 +472,6 @@ var OPENAI_AUTH_ONLY = process.env.OPENAI_AUTH_ONLY !== "false";
|
|
|
480
472
|
var CODEX_RESPONSES_URL = process.env.CODEX_RESPONSES_URL ?? "https://chatgpt.com/backend-api/codex/responses";
|
|
481
473
|
var OPENAI_VALIDATION_STALE_MS = 15 * 60 * 1e3;
|
|
482
474
|
|
|
483
|
-
// src/lib/llm/provider-catalog.ts
|
|
484
|
-
var OPENAI_PROVIDER_MODELS = [
|
|
485
|
-
"gpt-5.4",
|
|
486
|
-
"gpt-5.4-mini",
|
|
487
|
-
"o3",
|
|
488
|
-
"o4-mini"
|
|
489
|
-
];
|
|
490
|
-
var OPENAI_CATALOG = {
|
|
491
|
-
family: "openai",
|
|
492
|
-
displayName: "OpenAI",
|
|
493
|
-
models: OPENAI_PROVIDER_MODELS,
|
|
494
|
-
defaultModel: "gpt-5.4",
|
|
495
|
-
backgroundModel: "gpt-5.4-mini"
|
|
496
|
-
};
|
|
497
|
-
function getProviderCatalog(providerKind) {
|
|
498
|
-
switch (providerKind) {
|
|
499
|
-
case "openai_auth":
|
|
500
|
-
case "openai_api_key":
|
|
501
|
-
default:
|
|
502
|
-
return OPENAI_CATALOG;
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
function getAvailableModels(providerKind) {
|
|
506
|
-
return getProviderCatalog(providerKind).models;
|
|
507
|
-
}
|
|
508
|
-
function isSupportedModel(model, providerKind) {
|
|
509
|
-
if (!model) return false;
|
|
510
|
-
return getAvailableModels(providerKind).includes(model);
|
|
511
|
-
}
|
|
512
|
-
function getDefaultModel(providerKind) {
|
|
513
|
-
return getProviderCatalog(providerKind).defaultModel;
|
|
514
|
-
}
|
|
515
|
-
function selectModelForTask(providerKind, requestedModel, task) {
|
|
516
|
-
const catalog = getProviderCatalog(providerKind);
|
|
517
|
-
const selected = isSupportedModel(requestedModel, providerKind) ? requestedModel : catalog.defaultModel;
|
|
518
|
-
switch (task) {
|
|
519
|
-
case "conversation":
|
|
520
|
-
return selected;
|
|
521
|
-
case "compaction":
|
|
522
|
-
return selected.includes("5.4") ? catalog.backgroundModel : selected;
|
|
523
|
-
case "memory":
|
|
524
|
-
case "workspace":
|
|
525
|
-
return catalog.backgroundModel;
|
|
526
|
-
default:
|
|
527
|
-
return selected;
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
|
|
531
475
|
// src/lib/llm/openai-connection.ts
|
|
532
476
|
var VALIDATION_INSTRUCTIONS = "You are validating whether this OpenAI Codex connection can execute a minimal request. Reply with the single word ok.";
|
|
533
477
|
function buildHeaders(token, accountId) {
|
|
@@ -645,44 +589,42 @@ function trimCredential(value) {
|
|
|
645
589
|
const trimmed = value?.trim();
|
|
646
590
|
return trimmed ? trimmed : void 0;
|
|
647
591
|
}
|
|
648
|
-
|
|
649
|
-
const [config, stored] = await Promise.all([
|
|
650
|
-
loadOpenResearchConfig({ homeDir: options?.homeDir }),
|
|
651
|
-
loadStoredAuth({ homeDir: options?.homeDir })
|
|
652
|
-
]);
|
|
592
|
+
function resolveOpenAI(stored, config) {
|
|
653
593
|
if (stored) {
|
|
654
|
-
return {
|
|
655
|
-
kind: "openai_auth",
|
|
656
|
-
source: "stored_auth",
|
|
657
|
-
stored
|
|
658
|
-
};
|
|
594
|
+
return { kind: "openai_auth", source: "stored_auth", stored };
|
|
659
595
|
}
|
|
660
596
|
const envKey = trimCredential(process.env.OPENAI_API_KEY);
|
|
661
|
-
if (envKey) {
|
|
662
|
-
return {
|
|
663
|
-
kind: "openai_api_key",
|
|
664
|
-
source: "env",
|
|
665
|
-
apiKey: envKey
|
|
666
|
-
};
|
|
667
|
-
}
|
|
597
|
+
if (envKey) return { kind: "openai_api_key", source: "env", apiKey: envKey };
|
|
668
598
|
const providerKey = trimCredential(config?.providers?.openai?.apiKey);
|
|
669
|
-
if (providerKey) {
|
|
670
|
-
return {
|
|
671
|
-
kind: "openai_api_key",
|
|
672
|
-
source: "providers.openai.apiKey",
|
|
673
|
-
apiKey: providerKey
|
|
674
|
-
};
|
|
675
|
-
}
|
|
599
|
+
if (providerKey) return { kind: "openai_api_key", source: "providers.openai.apiKey", apiKey: providerKey };
|
|
676
600
|
const legacyKey = trimCredential(config?.apiKeys?.openai);
|
|
677
|
-
if (legacyKey) {
|
|
678
|
-
return {
|
|
679
|
-
kind: "openai_api_key",
|
|
680
|
-
source: "apiKeys.openai",
|
|
681
|
-
apiKey: legacyKey
|
|
682
|
-
};
|
|
683
|
-
}
|
|
601
|
+
if (legacyKey) return { kind: "openai_api_key", source: "apiKeys.openai", apiKey: legacyKey };
|
|
684
602
|
return null;
|
|
685
603
|
}
|
|
604
|
+
function resolveGemini(stored, config) {
|
|
605
|
+
if (stored) {
|
|
606
|
+
return { kind: "gemini_auth", source: "stored_auth", stored };
|
|
607
|
+
}
|
|
608
|
+
const envKey = trimCredential(process.env.GEMINI_API_KEY);
|
|
609
|
+
if (envKey) return { kind: "gemini_api_key", source: "env", apiKey: envKey };
|
|
610
|
+
const providerKey = trimCredential(config?.providers?.gemini?.apiKey);
|
|
611
|
+
if (providerKey) return { kind: "gemini_api_key", source: "providers.gemini.apiKey", apiKey: providerKey };
|
|
612
|
+
const legacyKey = trimCredential(config?.apiKeys?.gemini);
|
|
613
|
+
if (legacyKey) return { kind: "gemini_api_key", source: "apiKeys.gemini", apiKey: legacyKey };
|
|
614
|
+
return null;
|
|
615
|
+
}
|
|
616
|
+
async function resolveConfiguredProvider(options) {
|
|
617
|
+
const [config, openaiStored, geminiStored] = await Promise.all([
|
|
618
|
+
loadOpenResearchConfig({ homeDir: options?.homeDir }),
|
|
619
|
+
loadStoredAuth({ homeDir: options?.homeDir }),
|
|
620
|
+
loadGeminiAuth({ homeDir: options?.homeDir })
|
|
621
|
+
]);
|
|
622
|
+
const activeProvider = config?.activeProvider ?? "openai";
|
|
623
|
+
if (activeProvider === "gemini") {
|
|
624
|
+
return resolveGemini(geminiStored, config) ?? resolveOpenAI(openaiStored, config);
|
|
625
|
+
}
|
|
626
|
+
return resolveOpenAI(openaiStored, config) ?? resolveGemini(geminiStored, config);
|
|
627
|
+
}
|
|
686
628
|
async function hasConfiguredProvider(options) {
|
|
687
629
|
return await resolveConfiguredProvider(options) !== null;
|
|
688
630
|
}
|
|
@@ -691,24 +633,22 @@ async function getConfiguredProviderSummary(options) {
|
|
|
691
633
|
if (!resolved) {
|
|
692
634
|
return {
|
|
693
635
|
connected: false,
|
|
694
|
-
message: "No
|
|
636
|
+
message: "No credentials configured. Run /auth (OpenAI), /auth-gemini (Google), or /config apikey <key>."
|
|
695
637
|
};
|
|
696
638
|
}
|
|
697
639
|
if (resolved.kind === "openai_auth") {
|
|
698
|
-
return {
|
|
699
|
-
connected: true,
|
|
700
|
-
kind: resolved.kind,
|
|
701
|
-
source: resolved.source,
|
|
702
|
-
message: "OpenAI account connected."
|
|
703
|
-
};
|
|
640
|
+
return { connected: true, kind: resolved.kind, source: resolved.source, message: "OpenAI account connected." };
|
|
704
641
|
}
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
source: resolved.source,
|
|
710
|
-
|
|
711
|
-
|
|
642
|
+
if (resolved.kind === "openai_api_key") {
|
|
643
|
+
return { connected: true, kind: resolved.kind, source: resolved.source, message: `OpenAI API key configured via ${resolved.source}.` };
|
|
644
|
+
}
|
|
645
|
+
if (resolved.kind === "gemini_auth") {
|
|
646
|
+
return { connected: true, kind: resolved.kind, source: resolved.source, message: `Google account connected (${resolved.stored.tokens.email}).` };
|
|
647
|
+
}
|
|
648
|
+
if (resolved.kind === "gemini_api_key") {
|
|
649
|
+
return { connected: true, kind: resolved.kind, source: resolved.source, message: `Gemini API key configured via ${resolved.source}.` };
|
|
650
|
+
}
|
|
651
|
+
return { connected: false, message: "Unknown provider state." };
|
|
712
652
|
}
|
|
713
653
|
|
|
714
654
|
// src/lib/auth/status.ts
|
|
@@ -743,7 +683,7 @@ async function getAuthStatus(options) {
|
|
|
743
683
|
}
|
|
744
684
|
|
|
745
685
|
// src/lib/skills/registry.ts
|
|
746
|
-
import
|
|
686
|
+
import fs4 from "fs/promises";
|
|
747
687
|
import fsSync from "fs";
|
|
748
688
|
import path4 from "path";
|
|
749
689
|
import matter from "gray-matter";
|
|
@@ -756,13 +696,13 @@ function normalizeSkillName(name) {
|
|
|
756
696
|
}
|
|
757
697
|
async function ensureUserSkillsDir(options) {
|
|
758
698
|
const dir = getOpenResearchSkillsDir(options);
|
|
759
|
-
await
|
|
699
|
+
await fs4.mkdir(dir, { recursive: true });
|
|
760
700
|
return dir;
|
|
761
701
|
}
|
|
762
702
|
async function readSkillSummary(skillDir, source2) {
|
|
763
703
|
const skillFile = path4.join(skillDir, "SKILL.md");
|
|
764
704
|
try {
|
|
765
|
-
const raw = await
|
|
705
|
+
const raw = await fs4.readFile(skillFile, "utf8");
|
|
766
706
|
const parsed = matter(raw);
|
|
767
707
|
const name = String(parsed.data.name ?? "").trim();
|
|
768
708
|
const description = String(parsed.data.description ?? "").trim();
|
|
@@ -781,7 +721,7 @@ async function readSkillSummary(skillDir, source2) {
|
|
|
781
721
|
}
|
|
782
722
|
}
|
|
783
723
|
async function listSkillsInDirectory(rootDir, source2) {
|
|
784
|
-
const entries = await
|
|
724
|
+
const entries = await fs4.readdir(rootDir, { withFileTypes: true }).catch(() => []);
|
|
785
725
|
const results = await Promise.all(
|
|
786
726
|
entries.filter((entry) => entry.isDirectory()).map((entry) => readSkillSummary(path4.join(rootDir, entry.name), source2))
|
|
787
727
|
);
|
|
@@ -798,7 +738,7 @@ async function listAvailableSkills(options) {
|
|
|
798
738
|
async function validateSkillDirectory(input2) {
|
|
799
739
|
const errors = [];
|
|
800
740
|
const skillFile = path4.join(input2.skillDir, "SKILL.md");
|
|
801
|
-
const raw = await
|
|
741
|
+
const raw = await fs4.readFile(skillFile, "utf8").catch(() => "");
|
|
802
742
|
if (!raw) {
|
|
803
743
|
return { ok: false, errors: ["SKILL.md is missing."] };
|
|
804
744
|
}
|
|
@@ -829,9 +769,9 @@ async function createSkillScaffold(input2) {
|
|
|
829
769
|
const skillsDir = await ensureUserSkillsDir({ homeDir: input2.homeDir });
|
|
830
770
|
const name = normalizeSkillName(input2.name);
|
|
831
771
|
const skillDir = path4.join(skillsDir, name);
|
|
832
|
-
await
|
|
833
|
-
await
|
|
834
|
-
await
|
|
772
|
+
await fs4.mkdir(path4.join(skillDir, "scripts"), { recursive: true });
|
|
773
|
+
await fs4.mkdir(path4.join(skillDir, "references"), { recursive: true });
|
|
774
|
+
await fs4.mkdir(path4.join(skillDir, "assets"), { recursive: true });
|
|
835
775
|
const body = `---
|
|
836
776
|
name: ${name}
|
|
837
777
|
description: ${input2.description}
|
|
@@ -851,7 +791,7 @@ ${input2.examples.map((example) => `- ${example}`).join("\n")}
|
|
|
851
791
|
|
|
852
792
|
${input2.workflow}
|
|
853
793
|
`;
|
|
854
|
-
await
|
|
794
|
+
await fs4.writeFile(path4.join(skillDir, "SKILL.md"), body, "utf8");
|
|
855
795
|
return skillDir;
|
|
856
796
|
}
|
|
857
797
|
|
|
@@ -889,7 +829,7 @@ function formatDateTime(value) {
|
|
|
889
829
|
}
|
|
890
830
|
|
|
891
831
|
// src/lib/cli/version.ts
|
|
892
|
-
var PACKAGE_VERSION = "1.0.
|
|
832
|
+
var PACKAGE_VERSION = "1.0.1";
|
|
893
833
|
function getPackageVersion() {
|
|
894
834
|
return PACKAGE_VERSION;
|
|
895
835
|
}
|
|
@@ -2355,7 +2295,7 @@ function useTheme() {
|
|
|
2355
2295
|
}
|
|
2356
2296
|
|
|
2357
2297
|
// src/lib/workspace/scan.ts
|
|
2358
|
-
import
|
|
2298
|
+
import fs5 from "fs/promises";
|
|
2359
2299
|
import path5 from "path";
|
|
2360
2300
|
var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
2361
2301
|
".md",
|
|
@@ -2386,7 +2326,7 @@ var IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
|
2386
2326
|
".turbo"
|
|
2387
2327
|
]);
|
|
2388
2328
|
async function walkDir(rootDir, currentDir, out) {
|
|
2389
|
-
const entries = await
|
|
2329
|
+
const entries = await fs5.readdir(currentDir, { withFileTypes: true });
|
|
2390
2330
|
for (const entry of entries) {
|
|
2391
2331
|
if (IGNORED_DIRS.has(entry.name)) {
|
|
2392
2332
|
continue;
|
|
@@ -2404,7 +2344,7 @@ async function walkDir(rootDir, currentDir, out) {
|
|
|
2404
2344
|
key: `path:${relativePath}`,
|
|
2405
2345
|
label: relativePath,
|
|
2406
2346
|
path: relativePath,
|
|
2407
|
-
content: await
|
|
2347
|
+
content: await fs5.readFile(fullPath, "utf8")
|
|
2408
2348
|
});
|
|
2409
2349
|
}
|
|
2410
2350
|
}
|
|
@@ -2563,14 +2503,14 @@ function createOpenAIAuthProvider(credentials, onTokenRefresh, onValidationChang
|
|
|
2563
2503
|
}
|
|
2564
2504
|
return creds.accessToken;
|
|
2565
2505
|
}
|
|
2566
|
-
const
|
|
2506
|
+
const sessionId2 = crypto.randomUUID();
|
|
2567
2507
|
function buildHeaders3(token) {
|
|
2568
2508
|
const headers = {
|
|
2569
2509
|
Authorization: `Bearer ${token}`,
|
|
2570
2510
|
"Content-Type": "application/json",
|
|
2571
2511
|
originator: "open-research",
|
|
2572
2512
|
"User-Agent": `open-research/${getPackageVersion()} (${process.platform} ${process.arch})`,
|
|
2573
|
-
session_id:
|
|
2513
|
+
session_id: sessionId2
|
|
2574
2514
|
};
|
|
2575
2515
|
if (creds.accountId) {
|
|
2576
2516
|
headers["ChatGPT-Account-Id"] = creds.accountId;
|
|
@@ -3085,12 +3025,478 @@ function createOpenAIAPIKeyProvider(apiKey) {
|
|
|
3085
3025
|
};
|
|
3086
3026
|
}
|
|
3087
3027
|
|
|
3028
|
+
// src/lib/llm/gemini-format.ts
|
|
3029
|
+
function convertMessagesToGemini(messages) {
|
|
3030
|
+
const systemParts = [];
|
|
3031
|
+
const rawContents = [];
|
|
3032
|
+
const toolCallNames = /* @__PURE__ */ new Map();
|
|
3033
|
+
for (const msg of messages) {
|
|
3034
|
+
if (msg.role === "assistant" && msg.tool_calls) {
|
|
3035
|
+
for (const tc of msg.tool_calls) {
|
|
3036
|
+
toolCallNames.set(tc.id, tc.function.name);
|
|
3037
|
+
}
|
|
3038
|
+
}
|
|
3039
|
+
}
|
|
3040
|
+
for (const msg of messages) {
|
|
3041
|
+
if (msg.role === "system") {
|
|
3042
|
+
const text = typeof msg.content === "string" ? msg.content : "";
|
|
3043
|
+
if (text.trim()) systemParts.push(text);
|
|
3044
|
+
continue;
|
|
3045
|
+
}
|
|
3046
|
+
if (msg.role === "user") {
|
|
3047
|
+
const text = typeof msg.content === "string" ? msg.content : "";
|
|
3048
|
+
if (text.trim()) {
|
|
3049
|
+
rawContents.push({ role: "user", parts: [{ text }] });
|
|
3050
|
+
}
|
|
3051
|
+
continue;
|
|
3052
|
+
}
|
|
3053
|
+
if (msg.role === "assistant") {
|
|
3054
|
+
const parts = [];
|
|
3055
|
+
const text = typeof msg.content === "string" ? msg.content : "";
|
|
3056
|
+
if (text.trim()) {
|
|
3057
|
+
parts.push({ text });
|
|
3058
|
+
}
|
|
3059
|
+
if (msg.tool_calls) {
|
|
3060
|
+
for (const tc of msg.tool_calls) {
|
|
3061
|
+
let args = {};
|
|
3062
|
+
try {
|
|
3063
|
+
args = JSON.parse(tc.function.arguments);
|
|
3064
|
+
} catch {
|
|
3065
|
+
}
|
|
3066
|
+
parts.push({
|
|
3067
|
+
functionCall: { name: tc.function.name, args },
|
|
3068
|
+
thoughtSignature: "skip_thought_signature_validator"
|
|
3069
|
+
});
|
|
3070
|
+
}
|
|
3071
|
+
}
|
|
3072
|
+
if (parts.length > 0) {
|
|
3073
|
+
rawContents.push({ role: "model", parts });
|
|
3074
|
+
}
|
|
3075
|
+
continue;
|
|
3076
|
+
}
|
|
3077
|
+
if (msg.role === "tool") {
|
|
3078
|
+
const name = msg.tool_call_id ? toolCallNames.get(msg.tool_call_id) ?? "unknown" : "unknown";
|
|
3079
|
+
const content = typeof msg.content === "string" ? msg.content : "";
|
|
3080
|
+
rawContents.push({
|
|
3081
|
+
role: "user",
|
|
3082
|
+
parts: [{ functionResponse: { name, response: { content } } }]
|
|
3083
|
+
});
|
|
3084
|
+
continue;
|
|
3085
|
+
}
|
|
3086
|
+
}
|
|
3087
|
+
const merged = [];
|
|
3088
|
+
for (const content of rawContents) {
|
|
3089
|
+
const last = merged[merged.length - 1];
|
|
3090
|
+
if (last && last.role === content.role) {
|
|
3091
|
+
last.parts.push(...content.parts);
|
|
3092
|
+
} else {
|
|
3093
|
+
merged.push({ role: content.role, parts: [...content.parts] });
|
|
3094
|
+
}
|
|
3095
|
+
}
|
|
3096
|
+
return {
|
|
3097
|
+
systemInstruction: systemParts.length > 0 ? { parts: systemParts.map((text) => ({ text })) } : void 0,
|
|
3098
|
+
contents: merged
|
|
3099
|
+
};
|
|
3100
|
+
}
|
|
3101
|
+
function convertToolsToGemini(tools) {
|
|
3102
|
+
if (!tools || tools.length === 0) return void 0;
|
|
3103
|
+
return {
|
|
3104
|
+
functionDeclarations: tools.map((t) => ({
|
|
3105
|
+
name: t.function.name,
|
|
3106
|
+
description: t.function.description,
|
|
3107
|
+
parameters: t.function.parameters
|
|
3108
|
+
}))
|
|
3109
|
+
};
|
|
3110
|
+
}
|
|
3111
|
+
function mapReasoningEffort(effort, model) {
|
|
3112
|
+
if (!effort || effort === "none") return void 0;
|
|
3113
|
+
if (model.includes("gemini-3")) {
|
|
3114
|
+
const levelMap = {
|
|
3115
|
+
low: "THINKING_LEVEL_LOW",
|
|
3116
|
+
medium: "THINKING_LEVEL_MEDIUM",
|
|
3117
|
+
high: "THINKING_LEVEL_HIGH",
|
|
3118
|
+
xhigh: "THINKING_LEVEL_HIGH"
|
|
3119
|
+
};
|
|
3120
|
+
return {
|
|
3121
|
+
thinkingConfig: {
|
|
3122
|
+
thinkingLevel: levelMap[effort] ?? "THINKING_LEVEL_MEDIUM",
|
|
3123
|
+
includeThoughts: true
|
|
3124
|
+
}
|
|
3125
|
+
};
|
|
3126
|
+
}
|
|
3127
|
+
const budgetMap = {
|
|
3128
|
+
low: 1024,
|
|
3129
|
+
medium: 8192,
|
|
3130
|
+
high: 16384,
|
|
3131
|
+
xhigh: 32768
|
|
3132
|
+
};
|
|
3133
|
+
return {
|
|
3134
|
+
thinkingConfig: {
|
|
3135
|
+
thinkingBudget: budgetMap[effort] ?? 8192,
|
|
3136
|
+
includeThoughts: true
|
|
3137
|
+
}
|
|
3138
|
+
};
|
|
3139
|
+
}
|
|
3140
|
+
function mapJsonSchema(schema) {
|
|
3141
|
+
if (!schema) return void 0;
|
|
3142
|
+
return {
|
|
3143
|
+
responseMimeType: "application/json",
|
|
3144
|
+
responseSchema: schema.schema
|
|
3145
|
+
};
|
|
3146
|
+
}
|
|
3147
|
+
|
|
3148
|
+
// src/lib/llm/providers/gemini-auth.ts
|
|
3149
|
+
var REFRESH_BUFFER_MS = 6e4;
|
|
3150
|
+
var GEMINI_CLI_VERSION = "0.30.0";
|
|
3151
|
+
var sessionId = crypto.randomUUID();
|
|
3152
|
+
function createGeminiAuthProvider(credentials, onTokenRefresh) {
|
|
3153
|
+
let currentToken = credentials.accessToken;
|
|
3154
|
+
let currentExpiry = credentials.expiresAt;
|
|
3155
|
+
let refreshing = null;
|
|
3156
|
+
async function ensureValidToken() {
|
|
3157
|
+
if (Date.now() < currentExpiry - REFRESH_BUFFER_MS) {
|
|
3158
|
+
return currentToken;
|
|
3159
|
+
}
|
|
3160
|
+
if (!refreshing) {
|
|
3161
|
+
refreshing = (async () => {
|
|
3162
|
+
const result = await refreshGeminiAccessToken(credentials.refreshToken);
|
|
3163
|
+
currentToken = result.access_token;
|
|
3164
|
+
currentExpiry = Date.now() + result.expires_in * 1e3;
|
|
3165
|
+
if (result.refresh_token) {
|
|
3166
|
+
credentials.refreshToken = result.refresh_token;
|
|
3167
|
+
}
|
|
3168
|
+
await onTokenRefresh({
|
|
3169
|
+
...credentials,
|
|
3170
|
+
accessToken: currentToken,
|
|
3171
|
+
expiresAt: currentExpiry
|
|
3172
|
+
});
|
|
3173
|
+
})();
|
|
3174
|
+
}
|
|
3175
|
+
try {
|
|
3176
|
+
await refreshing;
|
|
3177
|
+
} finally {
|
|
3178
|
+
refreshing = null;
|
|
3179
|
+
}
|
|
3180
|
+
return currentToken;
|
|
3181
|
+
}
|
|
3182
|
+
function buildHeaders3(token, model) {
|
|
3183
|
+
return {
|
|
3184
|
+
Authorization: `Bearer ${token}`,
|
|
3185
|
+
"Content-Type": "application/json",
|
|
3186
|
+
"User-Agent": `GeminiCLI/${GEMINI_CLI_VERSION}/${model} (${process.platform}; ${process.arch})`,
|
|
3187
|
+
"x-activity-request-id": Math.random().toString(36).slice(2, 10)
|
|
3188
|
+
};
|
|
3189
|
+
}
|
|
3190
|
+
function buildRequestBody(options, model) {
|
|
3191
|
+
const messages = options.messages;
|
|
3192
|
+
const { systemInstruction, contents } = convertMessagesToGemini(messages);
|
|
3193
|
+
const generationConfig = {};
|
|
3194
|
+
if (options.temperature !== void 0) generationConfig.temperature = options.temperature;
|
|
3195
|
+
if (options.maxTokens !== void 0) generationConfig.maxOutputTokens = options.maxTokens;
|
|
3196
|
+
const thinking = mapReasoningEffort(options.reasoningEffort, model);
|
|
3197
|
+
if (thinking) Object.assign(generationConfig, thinking);
|
|
3198
|
+
if ("jsonSchema" in options && options.jsonSchema) {
|
|
3199
|
+
const schema = mapJsonSchema(options.jsonSchema);
|
|
3200
|
+
if (schema) Object.assign(generationConfig, schema);
|
|
3201
|
+
}
|
|
3202
|
+
const request = {
|
|
3203
|
+
contents,
|
|
3204
|
+
generationConfig,
|
|
3205
|
+
session_id: sessionId
|
|
3206
|
+
};
|
|
3207
|
+
if (systemInstruction) request.systemInstruction = systemInstruction;
|
|
3208
|
+
if ("tools" in options && options.tools) {
|
|
3209
|
+
const geminiTools = convertToolsToGemini(options.tools);
|
|
3210
|
+
if (geminiTools) request.tools = [geminiTools];
|
|
3211
|
+
}
|
|
3212
|
+
return {
|
|
3213
|
+
project: credentials.projectId,
|
|
3214
|
+
model,
|
|
3215
|
+
user_prompt_id: crypto.randomUUID(),
|
|
3216
|
+
request
|
|
3217
|
+
};
|
|
3218
|
+
}
|
|
3219
|
+
async function callLLM(options) {
|
|
3220
|
+
const model = options.model ?? "gemini-3.1-pro-preview";
|
|
3221
|
+
const token = await ensureValidToken();
|
|
3222
|
+
const body = buildRequestBody(options, model);
|
|
3223
|
+
const startTime = Date.now();
|
|
3224
|
+
const response = await fetch(
|
|
3225
|
+
`${GEMINI_CODE_ASSIST_URL}/v1internal:generateContent`,
|
|
3226
|
+
{
|
|
3227
|
+
method: "POST",
|
|
3228
|
+
headers: buildHeaders3(token, model),
|
|
3229
|
+
body: JSON.stringify(body)
|
|
3230
|
+
}
|
|
3231
|
+
);
|
|
3232
|
+
if (!response.ok) {
|
|
3233
|
+
const text = await response.text();
|
|
3234
|
+
throw new Error(`Gemini API error: ${response.status} ${text}`);
|
|
3235
|
+
}
|
|
3236
|
+
const data = await response.json();
|
|
3237
|
+
const inner = data.response ?? data;
|
|
3238
|
+
const parts = inner.candidates?.[0]?.content?.parts ?? [];
|
|
3239
|
+
const content = parts.map((p) => p.text ?? "").join("");
|
|
3240
|
+
const usage = inner.usageMetadata ?? {};
|
|
3241
|
+
return {
|
|
3242
|
+
content,
|
|
3243
|
+
model,
|
|
3244
|
+
usage: {
|
|
3245
|
+
promptTokens: usage.promptTokenCount ?? 0,
|
|
3246
|
+
completionTokens: usage.candidatesTokenCount ?? 0,
|
|
3247
|
+
totalTokens: usage.totalTokenCount ?? 0
|
|
3248
|
+
},
|
|
3249
|
+
latencyMs: Date.now() - startTime
|
|
3250
|
+
};
|
|
3251
|
+
}
|
|
3252
|
+
async function* callLLMStreaming(options) {
|
|
3253
|
+
const model = options.model ?? "gemini-3.1-pro-preview";
|
|
3254
|
+
const token = await ensureValidToken();
|
|
3255
|
+
const body = buildRequestBody(options, model);
|
|
3256
|
+
const response = await fetch(
|
|
3257
|
+
`${GEMINI_CODE_ASSIST_URL}/v1internal:streamGenerateContent?alt=sse`,
|
|
3258
|
+
{
|
|
3259
|
+
method: "POST",
|
|
3260
|
+
headers: {
|
|
3261
|
+
...buildHeaders3(token, model),
|
|
3262
|
+
Accept: "text/event-stream"
|
|
3263
|
+
},
|
|
3264
|
+
body: JSON.stringify(body),
|
|
3265
|
+
signal: options.signal
|
|
3266
|
+
}
|
|
3267
|
+
);
|
|
3268
|
+
if (!response.ok) {
|
|
3269
|
+
const text = await response.text();
|
|
3270
|
+
throw new Error(`Gemini streaming error: ${response.status} ${text}`);
|
|
3271
|
+
}
|
|
3272
|
+
const reader = response.body?.getReader();
|
|
3273
|
+
if (!reader) throw new Error("No response body");
|
|
3274
|
+
const decoder = new TextDecoder();
|
|
3275
|
+
let buffer = "";
|
|
3276
|
+
let fullText = "";
|
|
3277
|
+
const toolCalls = [];
|
|
3278
|
+
let toolCallIndex = 0;
|
|
3279
|
+
let lastUsage;
|
|
3280
|
+
while (true) {
|
|
3281
|
+
const { done, value } = await reader.read();
|
|
3282
|
+
if (done) break;
|
|
3283
|
+
buffer += decoder.decode(value, { stream: true });
|
|
3284
|
+
while (buffer.includes("\n")) {
|
|
3285
|
+
const lineEnd = buffer.indexOf("\n");
|
|
3286
|
+
const line = buffer.slice(0, lineEnd).trim();
|
|
3287
|
+
buffer = buffer.slice(lineEnd + 1);
|
|
3288
|
+
if (!line.startsWith("data: ")) continue;
|
|
3289
|
+
const jsonStr = line.slice(6);
|
|
3290
|
+
if (jsonStr === "[DONE]") continue;
|
|
3291
|
+
let parsed;
|
|
3292
|
+
try {
|
|
3293
|
+
parsed = JSON.parse(jsonStr);
|
|
3294
|
+
} catch {
|
|
3295
|
+
continue;
|
|
3296
|
+
}
|
|
3297
|
+
const inner = parsed.response ?? parsed;
|
|
3298
|
+
const candidates = inner.candidates ?? [];
|
|
3299
|
+
const candidate = candidates[0];
|
|
3300
|
+
if (!candidate) continue;
|
|
3301
|
+
const parts = candidate.content?.parts ?? [];
|
|
3302
|
+
for (const part of parts) {
|
|
3303
|
+
if (typeof part.text === "string" && part.text) {
|
|
3304
|
+
if (part.thought === true) continue;
|
|
3305
|
+
fullText += part.text;
|
|
3306
|
+
yield { type: "text_delta", content: part.text };
|
|
3307
|
+
}
|
|
3308
|
+
if (part.functionCall) {
|
|
3309
|
+
const fc = part.functionCall;
|
|
3310
|
+
const id = `call_${crypto.randomUUID().slice(0, 8)}`;
|
|
3311
|
+
const args = JSON.stringify(fc.args ?? {});
|
|
3312
|
+
toolCalls.push({ id, name: fc.name, arguments: args });
|
|
3313
|
+
yield { type: "tool_call_start", index: toolCallIndex, id, name: fc.name };
|
|
3314
|
+
yield { type: "tool_call_delta", index: toolCallIndex, arguments: args };
|
|
3315
|
+
toolCallIndex++;
|
|
3316
|
+
}
|
|
3317
|
+
}
|
|
3318
|
+
const usage = inner.usageMetadata;
|
|
3319
|
+
if (usage) {
|
|
3320
|
+
lastUsage = {
|
|
3321
|
+
promptTokens: usage.promptTokenCount ?? 0,
|
|
3322
|
+
completionTokens: usage.candidatesTokenCount ?? 0,
|
|
3323
|
+
totalTokens: usage.totalTokenCount ?? 0,
|
|
3324
|
+
cachedTokens: usage.cachedContentTokenCount ?? 0,
|
|
3325
|
+
reasoningTokens: usage.thoughtsTokenCount ?? 0
|
|
3326
|
+
};
|
|
3327
|
+
}
|
|
3328
|
+
}
|
|
3329
|
+
}
|
|
3330
|
+
yield {
|
|
3331
|
+
type: "done",
|
|
3332
|
+
content: fullText,
|
|
3333
|
+
toolCalls,
|
|
3334
|
+
usage: lastUsage
|
|
3335
|
+
};
|
|
3336
|
+
}
|
|
3337
|
+
return {
|
|
3338
|
+
kind: "gemini_auth",
|
|
3339
|
+
callLLM,
|
|
3340
|
+
callLLMStreaming
|
|
3341
|
+
};
|
|
3342
|
+
}
|
|
3343
|
+
|
|
3344
|
+
// src/lib/llm/providers/gemini-api-key.ts
|
|
3345
|
+
var GEMINI_API_BASE = "https://generativelanguage.googleapis.com/v1beta";
|
|
3346
|
+
function createGeminiAPIKeyProvider(apiKey) {
|
|
3347
|
+
function buildRequestBody(options, model) {
|
|
3348
|
+
const { systemInstruction, contents } = convertMessagesToGemini(options.messages);
|
|
3349
|
+
const generationConfig = {};
|
|
3350
|
+
if (options.temperature !== void 0) generationConfig.temperature = options.temperature;
|
|
3351
|
+
if (options.maxTokens !== void 0) generationConfig.maxOutputTokens = options.maxTokens;
|
|
3352
|
+
const thinking = mapReasoningEffort(options.reasoningEffort, model);
|
|
3353
|
+
if (thinking) Object.assign(generationConfig, thinking);
|
|
3354
|
+
if ("jsonSchema" in options && options.jsonSchema) {
|
|
3355
|
+
const schema = mapJsonSchema(options.jsonSchema);
|
|
3356
|
+
if (schema) Object.assign(generationConfig, schema);
|
|
3357
|
+
}
|
|
3358
|
+
const body = {
|
|
3359
|
+
contents,
|
|
3360
|
+
generationConfig
|
|
3361
|
+
};
|
|
3362
|
+
if (systemInstruction) body.systemInstruction = systemInstruction;
|
|
3363
|
+
if ("tools" in options && options.tools) {
|
|
3364
|
+
const geminiTools = convertToolsToGemini(options.tools);
|
|
3365
|
+
if (geminiTools) body.tools = [geminiTools];
|
|
3366
|
+
}
|
|
3367
|
+
return body;
|
|
3368
|
+
}
|
|
3369
|
+
async function callLLM(options) {
|
|
3370
|
+
const model = options.model ?? "gemini-3-flash-preview";
|
|
3371
|
+
const body = buildRequestBody(options, model);
|
|
3372
|
+
const startTime = Date.now();
|
|
3373
|
+
const response = await fetch(
|
|
3374
|
+
`${GEMINI_API_BASE}/models/${model}:generateContent`,
|
|
3375
|
+
{
|
|
3376
|
+
method: "POST",
|
|
3377
|
+
headers: {
|
|
3378
|
+
"Content-Type": "application/json",
|
|
3379
|
+
"x-goog-api-key": apiKey
|
|
3380
|
+
},
|
|
3381
|
+
body: JSON.stringify(body)
|
|
3382
|
+
}
|
|
3383
|
+
);
|
|
3384
|
+
if (!response.ok) {
|
|
3385
|
+
const text = await response.text();
|
|
3386
|
+
throw new Error(`Gemini API error: ${response.status} ${text}`);
|
|
3387
|
+
}
|
|
3388
|
+
const data = await response.json();
|
|
3389
|
+
const parts = data.candidates?.[0]?.content?.parts ?? [];
|
|
3390
|
+
const content = parts.map((p) => p.text ?? "").join("");
|
|
3391
|
+
const usage = data.usageMetadata ?? {};
|
|
3392
|
+
return {
|
|
3393
|
+
content,
|
|
3394
|
+
model,
|
|
3395
|
+
usage: {
|
|
3396
|
+
promptTokens: usage.promptTokenCount ?? 0,
|
|
3397
|
+
completionTokens: usage.candidatesTokenCount ?? 0,
|
|
3398
|
+
totalTokens: usage.totalTokenCount ?? 0
|
|
3399
|
+
},
|
|
3400
|
+
latencyMs: Date.now() - startTime
|
|
3401
|
+
};
|
|
3402
|
+
}
|
|
3403
|
+
async function* callLLMStreaming(options) {
|
|
3404
|
+
const model = options.model ?? "gemini-3-flash-preview";
|
|
3405
|
+
const body = buildRequestBody(options, model);
|
|
3406
|
+
const response = await fetch(
|
|
3407
|
+
`${GEMINI_API_BASE}/models/${model}:streamGenerateContent?alt=sse`,
|
|
3408
|
+
{
|
|
3409
|
+
method: "POST",
|
|
3410
|
+
headers: {
|
|
3411
|
+
"Content-Type": "application/json",
|
|
3412
|
+
"x-goog-api-key": apiKey,
|
|
3413
|
+
Accept: "text/event-stream"
|
|
3414
|
+
},
|
|
3415
|
+
body: JSON.stringify(body),
|
|
3416
|
+
signal: options.signal
|
|
3417
|
+
}
|
|
3418
|
+
);
|
|
3419
|
+
if (!response.ok) {
|
|
3420
|
+
const text = await response.text();
|
|
3421
|
+
throw new Error(`Gemini streaming error: ${response.status} ${text}`);
|
|
3422
|
+
}
|
|
3423
|
+
const reader = response.body?.getReader();
|
|
3424
|
+
if (!reader) throw new Error("No response body");
|
|
3425
|
+
const decoder = new TextDecoder();
|
|
3426
|
+
let buffer = "";
|
|
3427
|
+
let fullText = "";
|
|
3428
|
+
const toolCalls = [];
|
|
3429
|
+
let toolCallIndex = 0;
|
|
3430
|
+
let lastUsage;
|
|
3431
|
+
while (true) {
|
|
3432
|
+
const { done, value } = await reader.read();
|
|
3433
|
+
if (done) break;
|
|
3434
|
+
buffer += decoder.decode(value, { stream: true });
|
|
3435
|
+
while (buffer.includes("\n")) {
|
|
3436
|
+
const lineEnd = buffer.indexOf("\n");
|
|
3437
|
+
const line = buffer.slice(0, lineEnd).trim();
|
|
3438
|
+
buffer = buffer.slice(lineEnd + 1);
|
|
3439
|
+
if (!line.startsWith("data: ")) continue;
|
|
3440
|
+
const jsonStr = line.slice(6);
|
|
3441
|
+
if (jsonStr === "[DONE]") continue;
|
|
3442
|
+
let parsed;
|
|
3443
|
+
try {
|
|
3444
|
+
parsed = JSON.parse(jsonStr);
|
|
3445
|
+
} catch {
|
|
3446
|
+
continue;
|
|
3447
|
+
}
|
|
3448
|
+
const candidates = parsed.candidates ?? [];
|
|
3449
|
+
const candidate = candidates[0];
|
|
3450
|
+
if (!candidate) continue;
|
|
3451
|
+
const parts = candidate.content?.parts ?? [];
|
|
3452
|
+
for (const part of parts) {
|
|
3453
|
+
if (typeof part.text === "string" && part.text) {
|
|
3454
|
+
if (part.thought === true) continue;
|
|
3455
|
+
fullText += part.text;
|
|
3456
|
+
yield { type: "text_delta", content: part.text };
|
|
3457
|
+
}
|
|
3458
|
+
if (part.functionCall) {
|
|
3459
|
+
const fc = part.functionCall;
|
|
3460
|
+
const id = `call_${crypto.randomUUID().slice(0, 8)}`;
|
|
3461
|
+
const args = JSON.stringify(fc.args ?? {});
|
|
3462
|
+
toolCalls.push({ id, name: fc.name, arguments: args });
|
|
3463
|
+
yield { type: "tool_call_start", index: toolCallIndex, id, name: fc.name };
|
|
3464
|
+
yield { type: "tool_call_delta", index: toolCallIndex, arguments: args };
|
|
3465
|
+
toolCallIndex++;
|
|
3466
|
+
}
|
|
3467
|
+
}
|
|
3468
|
+
const usage = parsed.usageMetadata;
|
|
3469
|
+
if (usage) {
|
|
3470
|
+
lastUsage = {
|
|
3471
|
+
promptTokens: usage.promptTokenCount ?? 0,
|
|
3472
|
+
completionTokens: usage.candidatesTokenCount ?? 0,
|
|
3473
|
+
totalTokens: usage.totalTokenCount ?? 0,
|
|
3474
|
+
cachedTokens: usage.cachedContentTokenCount ?? 0,
|
|
3475
|
+
reasoningTokens: usage.thoughtsTokenCount ?? 0
|
|
3476
|
+
};
|
|
3477
|
+
}
|
|
3478
|
+
}
|
|
3479
|
+
}
|
|
3480
|
+
yield {
|
|
3481
|
+
type: "done",
|
|
3482
|
+
content: fullText,
|
|
3483
|
+
toolCalls,
|
|
3484
|
+
usage: lastUsage
|
|
3485
|
+
};
|
|
3486
|
+
}
|
|
3487
|
+
return {
|
|
3488
|
+
kind: "gemini_api_key",
|
|
3489
|
+
callLLM,
|
|
3490
|
+
callLLMStreaming
|
|
3491
|
+
};
|
|
3492
|
+
}
|
|
3493
|
+
|
|
3088
3494
|
// src/lib/llm/provider-factory.ts
|
|
3089
3495
|
async function createProviderFromStoredAuth(options) {
|
|
3090
3496
|
const resolved = await resolveConfiguredProvider({ homeDir: options?.homeDir });
|
|
3091
3497
|
if (!resolved) {
|
|
3092
3498
|
throw new Error(
|
|
3093
|
-
"No
|
|
3499
|
+
"No credentials found. Run /auth (OpenAI), /auth-gemini (Google), or /config apikey <key>."
|
|
3094
3500
|
);
|
|
3095
3501
|
}
|
|
3096
3502
|
if (resolved.kind === "openai_auth") {
|
|
@@ -3117,7 +3523,32 @@ async function createProviderFromStoredAuth(options) {
|
|
|
3117
3523
|
}
|
|
3118
3524
|
);
|
|
3119
3525
|
}
|
|
3120
|
-
|
|
3526
|
+
if (resolved.kind === "openai_api_key") {
|
|
3527
|
+
return createOpenAIAPIKeyProvider(resolved.apiKey);
|
|
3528
|
+
}
|
|
3529
|
+
if (resolved.kind === "gemini_auth") {
|
|
3530
|
+
const stored = resolved.stored;
|
|
3531
|
+
return createGeminiAuthProvider(
|
|
3532
|
+
{
|
|
3533
|
+
accessToken: stored.tokens.access,
|
|
3534
|
+
refreshToken: stored.tokens.refresh,
|
|
3535
|
+
expiresAt: stored.tokens.expires,
|
|
3536
|
+
email: stored.tokens.email,
|
|
3537
|
+
projectId: stored.tokens.projectId
|
|
3538
|
+
},
|
|
3539
|
+
async (newCreds) => {
|
|
3540
|
+
stored.tokens.access = newCreds.accessToken;
|
|
3541
|
+
stored.tokens.refresh = newCreds.refreshToken;
|
|
3542
|
+
stored.tokens.expires = newCreds.expiresAt;
|
|
3543
|
+
stored.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3544
|
+
await saveGeminiAuth(stored, { homeDir: options?.homeDir });
|
|
3545
|
+
}
|
|
3546
|
+
);
|
|
3547
|
+
}
|
|
3548
|
+
if (resolved.kind === "gemini_api_key") {
|
|
3549
|
+
return createGeminiAPIKeyProvider(resolved.apiKey);
|
|
3550
|
+
}
|
|
3551
|
+
throw new Error("Unknown provider kind.");
|
|
3121
3552
|
}
|
|
3122
3553
|
|
|
3123
3554
|
// src/lib/agent/tool-schemas.ts
|
|
@@ -3668,7 +4099,7 @@ Root: ${process.cwd()}`,
|
|
|
3668
4099
|
}
|
|
3669
4100
|
|
|
3670
4101
|
// src/lib/agent/tools/read-file.ts
|
|
3671
|
-
import
|
|
4102
|
+
import fs6 from "fs/promises";
|
|
3672
4103
|
import { createReadStream } from "fs";
|
|
3673
4104
|
import path6 from "path";
|
|
3674
4105
|
import os2 from "os";
|
|
@@ -3732,7 +4163,7 @@ function isBinaryByExtension(filePath) {
|
|
|
3732
4163
|
return BINARY_EXTENSIONS.has(path6.extname(filePath).toLowerCase());
|
|
3733
4164
|
}
|
|
3734
4165
|
async function isBinaryByContent(filePath) {
|
|
3735
|
-
const handle = await
|
|
4166
|
+
const handle = await fs6.open(filePath, "r");
|
|
3736
4167
|
try {
|
|
3737
4168
|
const buf = Buffer.alloc(4096);
|
|
3738
4169
|
const { bytesRead } = await handle.read(buf, 0, 4096, 0);
|
|
@@ -3797,7 +4228,7 @@ async function executeReadFile(args, ctx) {
|
|
|
3797
4228
|
const resolved = path6.isAbsolute(filePath) ? filePath : path6.resolve(filePath);
|
|
3798
4229
|
let stat;
|
|
3799
4230
|
try {
|
|
3800
|
-
stat = await
|
|
4231
|
+
stat = await fs6.stat(resolved);
|
|
3801
4232
|
} catch {
|
|
3802
4233
|
if (filePath in ctx.workspaceFiles) {
|
|
3803
4234
|
return formatFromString(filePath, ctx.workspaceFiles[filePath], offset, limit);
|
|
@@ -3805,7 +4236,7 @@ async function executeReadFile(args, ctx) {
|
|
|
3805
4236
|
return `Error: File not found: ${args.file_path}`;
|
|
3806
4237
|
}
|
|
3807
4238
|
if (stat.isDirectory()) {
|
|
3808
|
-
const entries = await
|
|
4239
|
+
const entries = await fs6.readdir(resolved, { withFileTypes: true });
|
|
3809
4240
|
const lines = entries.sort((a, b) => {
|
|
3810
4241
|
if (a.isDirectory() !== b.isDirectory()) return a.isDirectory() ? -1 : 1;
|
|
3811
4242
|
return a.name.localeCompare(b.name);
|
|
@@ -3869,7 +4300,7 @@ ${outputLines.join("\n")}${footer}
|
|
|
3869
4300
|
}
|
|
3870
4301
|
|
|
3871
4302
|
// src/lib/agent/tools/list-directory.ts
|
|
3872
|
-
import
|
|
4303
|
+
import fs7 from "fs/promises";
|
|
3873
4304
|
import path7 from "path";
|
|
3874
4305
|
var MAX_ENTRIES = 200;
|
|
3875
4306
|
var DEFAULT_IGNORE = /* @__PURE__ */ new Set([
|
|
@@ -3887,7 +4318,7 @@ var DEFAULT_IGNORE = /* @__PURE__ */ new Set([
|
|
|
3887
4318
|
".env"
|
|
3888
4319
|
]);
|
|
3889
4320
|
async function listEntries(dirPath, ignore) {
|
|
3890
|
-
const entries = await
|
|
4321
|
+
const entries = await fs7.readdir(dirPath, { withFileTypes: true });
|
|
3891
4322
|
const results = [];
|
|
3892
4323
|
for (const entry of entries) {
|
|
3893
4324
|
if (ignore.has(entry.name) || entry.name.startsWith(".") && DEFAULT_IGNORE.has(entry.name)) {
|
|
@@ -3896,7 +4327,7 @@ async function listEntries(dirPath, ignore) {
|
|
|
3896
4327
|
let size = 0;
|
|
3897
4328
|
if (!entry.isDirectory()) {
|
|
3898
4329
|
try {
|
|
3899
|
-
const stat = await
|
|
4330
|
+
const stat = await fs7.stat(path7.join(dirPath, entry.name));
|
|
3900
4331
|
size = stat.size;
|
|
3901
4332
|
} catch {
|
|
3902
4333
|
}
|
|
@@ -3954,7 +4385,7 @@ async function executeListDirectory(args) {
|
|
|
3954
4385
|
const maxDepth = Math.min(args.depth ?? 2, 5);
|
|
3955
4386
|
const ignore = /* @__PURE__ */ new Set([...DEFAULT_IGNORE, ...args.ignore ?? []]);
|
|
3956
4387
|
try {
|
|
3957
|
-
const stat = await
|
|
4388
|
+
const stat = await fs7.stat(dirPath);
|
|
3958
4389
|
if (!stat.isDirectory()) {
|
|
3959
4390
|
return `Error: ${dirPath} is not a directory.`;
|
|
3960
4391
|
}
|
|
@@ -3969,7 +4400,7 @@ async function executeListDirectory(args) {
|
|
|
3969
4400
|
|
|
3970
4401
|
// src/lib/agent/tools/run-command.ts
|
|
3971
4402
|
import { spawn } from "child_process";
|
|
3972
|
-
import
|
|
4403
|
+
import fs8 from "fs/promises";
|
|
3973
4404
|
import path8 from "path";
|
|
3974
4405
|
var DEFAULT_TIMEOUT_MS = 2 * 60 * 1e3;
|
|
3975
4406
|
var MAX_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
@@ -3981,7 +4412,7 @@ async function executeRunCommand(args, signal) {
|
|
|
3981
4412
|
}
|
|
3982
4413
|
const workdir = args.workdir ? path8.isAbsolute(args.workdir) ? args.workdir : path8.resolve(args.workdir) : process.cwd();
|
|
3983
4414
|
try {
|
|
3984
|
-
const stat = await
|
|
4415
|
+
const stat = await fs8.stat(workdir);
|
|
3985
4416
|
if (!stat.isDirectory()) {
|
|
3986
4417
|
return `Error: workdir is not a directory: ${workdir}`;
|
|
3987
4418
|
}
|
|
@@ -4391,7 +4822,7 @@ function executeCreatePaper(args, ctx) {
|
|
|
4391
4822
|
|
|
4392
4823
|
// src/lib/agent/tools/read-pdf.ts
|
|
4393
4824
|
import path9 from "path";
|
|
4394
|
-
import
|
|
4825
|
+
import fs9 from "fs/promises";
|
|
4395
4826
|
var MAX_OUTPUT_BYTES3 = 5e4;
|
|
4396
4827
|
function parsePages(pages) {
|
|
4397
4828
|
const range = pages.match(/^(\d+)\s*-\s*(\d+)$/);
|
|
@@ -4402,7 +4833,7 @@ function parsePages(pages) {
|
|
|
4402
4833
|
}
|
|
4403
4834
|
async function executeReadPdf(args) {
|
|
4404
4835
|
const resolved = path9.resolve(args.file_path);
|
|
4405
|
-
const stat = await
|
|
4836
|
+
const stat = await fs9.stat(resolved).catch(() => null);
|
|
4406
4837
|
if (!stat || !stat.isFile()) {
|
|
4407
4838
|
return `Error: file not found: ${resolved}`;
|
|
4408
4839
|
}
|
|
@@ -5589,7 +6020,7 @@ ${summary}`,
|
|
|
5589
6020
|
}
|
|
5590
6021
|
|
|
5591
6022
|
// src/lib/skills/runtime.ts
|
|
5592
|
-
import
|
|
6023
|
+
import fs10 from "fs/promises";
|
|
5593
6024
|
import path10 from "path";
|
|
5594
6025
|
import matter2 from "gray-matter";
|
|
5595
6026
|
async function loadRuntimeSkillByName(input2) {
|
|
@@ -5598,7 +6029,7 @@ async function loadRuntimeSkillByName(input2) {
|
|
|
5598
6029
|
if (!match) {
|
|
5599
6030
|
return null;
|
|
5600
6031
|
}
|
|
5601
|
-
const raw = await
|
|
6032
|
+
const raw = await fs10.readFile(path10.join(match.skillDir, "SKILL.md"), "utf8");
|
|
5602
6033
|
const parsed = matter2(raw);
|
|
5603
6034
|
return {
|
|
5604
6035
|
id: match.name,
|
|
@@ -5610,14 +6041,15 @@ async function loadRuntimeSkillByName(input2) {
|
|
|
5610
6041
|
}
|
|
5611
6042
|
async function readSkillReferenceFile(skillDir, referencePath) {
|
|
5612
6043
|
const fullPath = path10.join(skillDir, "references", referencePath);
|
|
5613
|
-
return
|
|
6044
|
+
return fs10.readFile(fullPath, "utf8");
|
|
5614
6045
|
}
|
|
5615
6046
|
|
|
5616
6047
|
// src/lib/agent/subagent/configs.ts
|
|
5617
6048
|
var exploreConfig = {
|
|
5618
6049
|
id: "explore",
|
|
5619
6050
|
name: "Explore",
|
|
5620
|
-
model:
|
|
6051
|
+
model: void 0,
|
|
6052
|
+
// Resolved at runtime from provider catalog (backgroundModel)
|
|
5621
6053
|
reasoningEffort: "high",
|
|
5622
6054
|
allowedTools: /* @__PURE__ */ new Set([
|
|
5623
6055
|
"read_file",
|
|
@@ -5714,7 +6146,7 @@ ${input2.context}` : input2.goal;
|
|
|
5714
6146
|
for await (const chunk of input2.provider.callLLMStreaming({
|
|
5715
6147
|
messages,
|
|
5716
6148
|
tools,
|
|
5717
|
-
model: config.model,
|
|
6149
|
+
model: config.model ?? getProviderCatalog(input2.provider.kind).backgroundModel,
|
|
5718
6150
|
reasoningEffort: config.reasoningEffort,
|
|
5719
6151
|
signal: input2.signal
|
|
5720
6152
|
})) {
|
|
@@ -5775,7 +6207,7 @@ ${input2.context}` : input2.goal;
|
|
|
5775
6207
|
let finalText = "";
|
|
5776
6208
|
for await (const chunk of input2.provider.callLLMStreaming({
|
|
5777
6209
|
messages,
|
|
5778
|
-
model: config.model,
|
|
6210
|
+
model: config.model ?? getProviderCatalog(input2.provider.kind).backgroundModel,
|
|
5779
6211
|
reasoningEffort: config.reasoningEffort,
|
|
5780
6212
|
signal: input2.signal
|
|
5781
6213
|
})) {
|
|
@@ -5816,7 +6248,7 @@ function describeSubAgentTool(name, args) {
|
|
|
5816
6248
|
|
|
5817
6249
|
// src/lib/agent/tools/tasks.ts
|
|
5818
6250
|
import path11 from "path";
|
|
5819
|
-
import
|
|
6251
|
+
import fs11 from "fs/promises";
|
|
5820
6252
|
var tasks = [];
|
|
5821
6253
|
var storePath = null;
|
|
5822
6254
|
function shortId() {
|
|
@@ -5826,9 +6258,9 @@ async function persist() {
|
|
|
5826
6258
|
if (!storePath) return;
|
|
5827
6259
|
const live = tasks.filter((t) => t.status !== "deleted");
|
|
5828
6260
|
const tmpPath = storePath + ".tmp";
|
|
5829
|
-
await
|
|
5830
|
-
await
|
|
5831
|
-
await
|
|
6261
|
+
await fs11.mkdir(path11.dirname(storePath), { recursive: true });
|
|
6262
|
+
await fs11.writeFile(tmpPath, JSON.stringify({ version: 1, tasks: live }, null, 2));
|
|
6263
|
+
await fs11.rename(tmpPath, storePath);
|
|
5832
6264
|
}
|
|
5833
6265
|
async function initTaskStore(workspaceDir) {
|
|
5834
6266
|
storePath = path11.join(workspaceDir, ".open-research", "tasks.json");
|
|
@@ -5960,7 +6392,7 @@ async function executeTool(name, args, ctx, activeSkills, homeDir, signal, provi
|
|
|
5960
6392
|
return { result: out.result, searchResults: out.sources };
|
|
5961
6393
|
}
|
|
5962
6394
|
case "web_search": {
|
|
5963
|
-
const { executeWebSearch } = await import("./web-search-
|
|
6395
|
+
const { executeWebSearch } = await import("./web-search-TKBFSU3M.js");
|
|
5964
6396
|
const out = await executeWebSearch(
|
|
5965
6397
|
args,
|
|
5966
6398
|
provider
|
|
@@ -6039,7 +6471,7 @@ ${meta}` };
|
|
|
6039
6471
|
if (!provider) {
|
|
6040
6472
|
return { result: "Error: query_ontology requires an LLM provider." };
|
|
6041
6473
|
}
|
|
6042
|
-
const { runQueryAgent } = await import("./query-agent-
|
|
6474
|
+
const { runQueryAgent } = await import("./query-agent-WM6UNZ37.js");
|
|
6043
6475
|
const workspaceDir = ctx.workspaceDir ?? process.cwd();
|
|
6044
6476
|
const answer = await runQueryAgent({
|
|
6045
6477
|
query: String(args.query ?? ""),
|
|
@@ -6101,7 +6533,9 @@ var MODEL_CONTEXT_WINDOWS = {
|
|
|
6101
6533
|
"gpt-4o": 128e3,
|
|
6102
6534
|
"gpt-5.4-mini": 128e3,
|
|
6103
6535
|
"o3": 2e5,
|
|
6104
|
-
"o4-mini": 2e5
|
|
6536
|
+
"o4-mini": 2e5,
|
|
6537
|
+
"gemini-3.1-pro-preview": 1048576,
|
|
6538
|
+
"gemini-3-flash-preview": 1048576
|
|
6105
6539
|
};
|
|
6106
6540
|
var DEFAULT_CONTEXT_WINDOW = 128e3;
|
|
6107
6541
|
var AUTO_COMPACT_TOKEN_LIMIT = 25e4;
|
|
@@ -6315,7 +6749,7 @@ async function manualCompact(messages, model, provider, usage, customInstruction
|
|
|
6315
6749
|
}
|
|
6316
6750
|
|
|
6317
6751
|
// src/lib/memory/store.ts
|
|
6318
|
-
import
|
|
6752
|
+
import fs12 from "fs/promises";
|
|
6319
6753
|
import path12 from "path";
|
|
6320
6754
|
function getGlobalMemoryFile(options) {
|
|
6321
6755
|
return path12.join(getOpenResearchRoot(options), "memory.json");
|
|
@@ -6325,7 +6759,7 @@ function getProjectMemoryFile(workspaceDir) {
|
|
|
6325
6759
|
}
|
|
6326
6760
|
async function loadMemoryFile(filePath) {
|
|
6327
6761
|
try {
|
|
6328
|
-
const raw = await
|
|
6762
|
+
const raw = await fs12.readFile(filePath, "utf8");
|
|
6329
6763
|
const store = JSON.parse(raw);
|
|
6330
6764
|
return store.memories ?? [];
|
|
6331
6765
|
} catch {
|
|
@@ -6333,9 +6767,9 @@ async function loadMemoryFile(filePath) {
|
|
|
6333
6767
|
}
|
|
6334
6768
|
}
|
|
6335
6769
|
async function saveMemoryFile(filePath, memories) {
|
|
6336
|
-
await
|
|
6770
|
+
await fs12.mkdir(path12.dirname(filePath), { recursive: true });
|
|
6337
6771
|
const store = { version: 2, memories };
|
|
6338
|
-
await
|
|
6772
|
+
await fs12.writeFile(filePath, JSON.stringify(store, null, 2), "utf8");
|
|
6339
6773
|
}
|
|
6340
6774
|
async function loadGlobalMemories(options) {
|
|
6341
6775
|
const mems = await loadMemoryFile(getGlobalMemoryFile(options));
|
|
@@ -6667,18 +7101,18 @@ async function extractAndStoreMemories(input2) {
|
|
|
6667
7101
|
}
|
|
6668
7102
|
|
|
6669
7103
|
// src/lib/workspace/agents-md.ts
|
|
6670
|
-
import
|
|
7104
|
+
import fs13 from "fs/promises";
|
|
6671
7105
|
import path13 from "path";
|
|
6672
7106
|
var AGENTS_MD_FILENAME = "AGENTS.md";
|
|
6673
7107
|
async function readAgentsMd(workspaceDir) {
|
|
6674
7108
|
try {
|
|
6675
|
-
return await
|
|
7109
|
+
return await fs13.readFile(path13.join(workspaceDir, AGENTS_MD_FILENAME), "utf8");
|
|
6676
7110
|
} catch {
|
|
6677
7111
|
return "";
|
|
6678
7112
|
}
|
|
6679
7113
|
}
|
|
6680
7114
|
async function writeAgentsMd(workspaceDir, content) {
|
|
6681
|
-
await
|
|
7115
|
+
await fs13.writeFile(path13.join(workspaceDir, AGENTS_MD_FILENAME), content, "utf8");
|
|
6682
7116
|
}
|
|
6683
7117
|
var UPDATE_SYSTEM_PROMPT = `You maintain an AGENTS.md file for a research workspace. This file gives future agent sessions instant context about the project \u2014 what it's about, what's been done, key files, and current direction.
|
|
6684
7118
|
|
|
@@ -6881,7 +7315,7 @@ async function runAgentTurn(input2) {
|
|
|
6881
7315
|
const isPlanning = input2.mode === "planning";
|
|
6882
7316
|
const tools = isPlanning ? getToolsForMode("planning") : TOOL_SCHEMAS;
|
|
6883
7317
|
const systemPrompt = isPlanning ? buildPlanningSystemPrompt(input2.workspace, activeSkills) : buildSystemPrompt(input2.workspace, activeSkills);
|
|
6884
|
-
const model = input2.model ??
|
|
7318
|
+
const model = input2.model ?? getProviderCatalog(input2.provider.kind).defaultModel;
|
|
6885
7319
|
const usage = input2.sessionUsage ?? createSessionUsage();
|
|
6886
7320
|
const allMemories = await loadAllMemories({
|
|
6887
7321
|
homeDir: input2.homeDir,
|
|
@@ -6895,7 +7329,7 @@ async function runAgentTurn(input2) {
|
|
|
6895
7329
|
if (input2.workspace.workspaceDir) {
|
|
6896
7330
|
try {
|
|
6897
7331
|
const { loadOntology } = await import("./store-LT5EGDOI.js");
|
|
6898
|
-
const { runRelevanceAgent } = await import("./relevance-agent-
|
|
7332
|
+
const { runRelevanceAgent } = await import("./relevance-agent-H3U6TROD.js");
|
|
6899
7333
|
const { buildScaffoldingContext } = await import("./scaffolding-MSAICMWV.js");
|
|
6900
7334
|
const ontology = await loadOntology(input2.workspace.workspaceDir);
|
|
6901
7335
|
const relevantIds = await runRelevanceAgent({
|
|
@@ -6996,7 +7430,7 @@ ${agentsMd}` : null,
|
|
|
6996
7430
|
});
|
|
6997
7431
|
}
|
|
6998
7432
|
if (input2.workspace.workspaceDir) {
|
|
6999
|
-
import("./manager-queue-
|
|
7433
|
+
import("./manager-queue-FBAUCAGI.js").then(({ enqueueOntologyManager }) => {
|
|
7000
7434
|
enqueueOntologyManager({
|
|
7001
7435
|
userMessage: input2.message,
|
|
7002
7436
|
agentResponse: fullText,
|
|
@@ -7155,7 +7589,7 @@ function classifyUpdateRisk(update) {
|
|
|
7155
7589
|
}
|
|
7156
7590
|
|
|
7157
7591
|
// src/lib/workspace/apply-update.ts
|
|
7158
|
-
import
|
|
7592
|
+
import fs14 from "fs/promises";
|
|
7159
7593
|
import path14 from "path";
|
|
7160
7594
|
function resolveRelativePath(update) {
|
|
7161
7595
|
if (update.key.startsWith("path:")) {
|
|
@@ -7178,8 +7612,8 @@ function resolveRelativePath(update) {
|
|
|
7178
7612
|
async function applyProposedUpdate(workspaceDir, update) {
|
|
7179
7613
|
const relativePath = resolveRelativePath(update);
|
|
7180
7614
|
const absolutePath = path14.join(workspaceDir, relativePath);
|
|
7181
|
-
await
|
|
7182
|
-
await
|
|
7615
|
+
await fs14.mkdir(path14.dirname(absolutePath), { recursive: true });
|
|
7616
|
+
await fs14.writeFile(absolutePath, update.content, "utf8");
|
|
7183
7617
|
return absolutePath;
|
|
7184
7618
|
}
|
|
7185
7619
|
|
|
@@ -7397,7 +7831,7 @@ function SessionPicker({ sessions, onSelect, onCancel }) {
|
|
|
7397
7831
|
}
|
|
7398
7832
|
|
|
7399
7833
|
// src/lib/cli/update-check.ts
|
|
7400
|
-
import
|
|
7834
|
+
import fs15 from "fs/promises";
|
|
7401
7835
|
import path15 from "path";
|
|
7402
7836
|
import os3 from "os";
|
|
7403
7837
|
var PACKAGE_NAME = "open-research";
|
|
@@ -7405,7 +7839,7 @@ var CHECK_INTERVAL_MS = 4 * 60 * 60 * 1e3;
|
|
|
7405
7839
|
var STATE_FILE = path15.join(os3.homedir(), ".open-research", "update-check.json");
|
|
7406
7840
|
async function readState() {
|
|
7407
7841
|
try {
|
|
7408
|
-
const raw = await
|
|
7842
|
+
const raw = await fs15.readFile(STATE_FILE, "utf8");
|
|
7409
7843
|
const parsed = JSON.parse(raw);
|
|
7410
7844
|
return {
|
|
7411
7845
|
lastCheck: typeof parsed.lastCheck === "number" ? parsed.lastCheck : 0,
|
|
@@ -7417,8 +7851,8 @@ async function readState() {
|
|
|
7417
7851
|
}
|
|
7418
7852
|
}
|
|
7419
7853
|
async function writeState(state) {
|
|
7420
|
-
await
|
|
7421
|
-
await
|
|
7854
|
+
await fs15.mkdir(path15.dirname(STATE_FILE), { recursive: true });
|
|
7855
|
+
await fs15.writeFile(STATE_FILE, JSON.stringify(state), "utf8");
|
|
7422
7856
|
}
|
|
7423
7857
|
function getCurrentVersion() {
|
|
7424
7858
|
return getPackageVersion();
|
|
@@ -7473,6 +7907,7 @@ async function checkForUpdate() {
|
|
|
7473
7907
|
// src/tui/commands.ts
|
|
7474
7908
|
var SLASH_COMMANDS = [
|
|
7475
7909
|
{ name: "auth", aliases: ["/connect", "/login"], description: "Connect your OpenAI account via browser OAuth", category: "auth" },
|
|
7910
|
+
{ name: "auth-gemini", aliases: ["/login-gemini", "/gemini"], description: "Connect your Google account via browser OAuth (Gemini)", category: "auth" },
|
|
7476
7911
|
{ name: "auth-codex", aliases: ["/import-codex"], description: "Import auth from existing Codex CLI", category: "auth" },
|
|
7477
7912
|
{ name: "auth-status", aliases: [], description: "Check auth connection status", category: "auth" },
|
|
7478
7913
|
{ name: "logout", aliases: [], description: "Clear stored auth", category: "auth" },
|
|
@@ -8331,7 +8766,7 @@ function useTerminalWidth() {
|
|
|
8331
8766
|
import { startTransition } from "react";
|
|
8332
8767
|
|
|
8333
8768
|
// src/lib/workspace/init-agents-md.ts
|
|
8334
|
-
import
|
|
8769
|
+
import fs16 from "fs/promises";
|
|
8335
8770
|
import path16 from "path";
|
|
8336
8771
|
var IGNORED_DIRS2 = /* @__PURE__ */ new Set([
|
|
8337
8772
|
"node_modules",
|
|
@@ -8371,7 +8806,7 @@ async function scanDirectoryShallow(dir, maxDepth = 2, depth = 0) {
|
|
|
8371
8806
|
const results = [];
|
|
8372
8807
|
if (depth > maxDepth) return results;
|
|
8373
8808
|
try {
|
|
8374
|
-
const entries = await
|
|
8809
|
+
const entries = await fs16.readdir(dir, { withFileTypes: true });
|
|
8375
8810
|
for (const entry of entries) {
|
|
8376
8811
|
if (IGNORED_DIRS2.has(entry.name)) continue;
|
|
8377
8812
|
if (entry.name.startsWith(".") && depth === 0 && entry.isDirectory()) continue;
|
|
@@ -8382,7 +8817,7 @@ async function scanDirectoryShallow(dir, maxDepth = 2, depth = 0) {
|
|
|
8382
8817
|
const children = await scanDirectoryShallow(fullPath, maxDepth, depth + 1);
|
|
8383
8818
|
results.push(...children);
|
|
8384
8819
|
} else {
|
|
8385
|
-
const stat = await
|
|
8820
|
+
const stat = await fs16.stat(fullPath).catch(() => null);
|
|
8386
8821
|
results.push({ path: relativePath, size: stat?.size ?? 0, isDir: false });
|
|
8387
8822
|
}
|
|
8388
8823
|
}
|
|
@@ -8394,7 +8829,7 @@ async function readKeyFiles(dir) {
|
|
|
8394
8829
|
const contents = {};
|
|
8395
8830
|
for (const name of INTERESTING_FILES) {
|
|
8396
8831
|
try {
|
|
8397
|
-
const content = await
|
|
8832
|
+
const content = await fs16.readFile(path16.join(dir, name), "utf8");
|
|
8398
8833
|
contents[name] = content.slice(0, 2e3);
|
|
8399
8834
|
} catch {
|
|
8400
8835
|
}
|
|
@@ -8480,7 +8915,7 @@ ${scanData.slice(0, 25e3)}`;
|
|
|
8480
8915
|
|
|
8481
8916
|
// src/lib/preview/server.ts
|
|
8482
8917
|
import http2 from "http";
|
|
8483
|
-
import
|
|
8918
|
+
import fs17 from "fs";
|
|
8484
8919
|
import path17 from "path";
|
|
8485
8920
|
|
|
8486
8921
|
// src/lib/preview/latex-to-html.ts
|
|
@@ -8752,7 +9187,7 @@ function startPreviewServer(texPath) {
|
|
|
8752
9187
|
let currentHash = "";
|
|
8753
9188
|
function getContentHash() {
|
|
8754
9189
|
try {
|
|
8755
|
-
const content =
|
|
9190
|
+
const content = fs17.readFileSync(resolved, "utf8");
|
|
8756
9191
|
return `${content.length}-${content.slice(0, 100)}-${content.slice(-100)}`;
|
|
8757
9192
|
} catch {
|
|
8758
9193
|
return "error";
|
|
@@ -8760,7 +9195,7 @@ function startPreviewServer(texPath) {
|
|
|
8760
9195
|
}
|
|
8761
9196
|
function renderPage() {
|
|
8762
9197
|
try {
|
|
8763
|
-
const latex =
|
|
9198
|
+
const latex = fs17.readFileSync(resolved, "utf8");
|
|
8764
9199
|
const htmlContent = latexToHtml(latex);
|
|
8765
9200
|
currentHash = getContentHash();
|
|
8766
9201
|
return HTML_TEMPLATE.replace("{{CONTENT}}", htmlContent);
|
|
@@ -8807,7 +9242,7 @@ async function executeSlashCommand(cmd, args, ctx) {
|
|
|
8807
9242
|
history,
|
|
8808
9243
|
skills: skills2,
|
|
8809
9244
|
workspaceFiles,
|
|
8810
|
-
sessionId,
|
|
9245
|
+
sessionId: sessionId2,
|
|
8811
9246
|
sessionTokens,
|
|
8812
9247
|
agentMode,
|
|
8813
9248
|
addSystemMessage,
|
|
@@ -8846,6 +9281,23 @@ async function executeSlashCommand(cmd, args, ctx) {
|
|
|
8846
9281
|
}
|
|
8847
9282
|
break;
|
|
8848
9283
|
}
|
|
9284
|
+
case "auth-gemini": {
|
|
9285
|
+
addSystemMessage("Opening browser for Google login...");
|
|
9286
|
+
addSystemMessage("Note: Uses Gemini CLI credentials. Google may restrict third-party use.");
|
|
9287
|
+
setBusy(true);
|
|
9288
|
+
try {
|
|
9289
|
+
const { loginWithGemini } = await import("./gemini-login-EYY3EFH4.js");
|
|
9290
|
+
const result = await loginWithGemini({ homeDir });
|
|
9291
|
+
setAuthStatus("connected");
|
|
9292
|
+
addSystemMessage(`Connected Google account ${result.tokens.email}`);
|
|
9293
|
+
addSystemMessage("Switch to Gemini: /config provider gemini");
|
|
9294
|
+
} catch (err) {
|
|
9295
|
+
addSystemMessage(`Auth failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
9296
|
+
} finally {
|
|
9297
|
+
setBusy(false);
|
|
9298
|
+
}
|
|
9299
|
+
break;
|
|
9300
|
+
}
|
|
8849
9301
|
case "auth-codex": {
|
|
8850
9302
|
addSystemMessage("Importing Codex CLI auth...");
|
|
8851
9303
|
setBusy(true);
|
|
@@ -8934,7 +9386,7 @@ async function executeSlashCommand(cmd, args, ctx) {
|
|
|
8934
9386
|
addSystemMessage("No workspace. Run /init first.");
|
|
8935
9387
|
break;
|
|
8936
9388
|
}
|
|
8937
|
-
const { listSessions: listSessions2 } = await import("./sessions-
|
|
9389
|
+
const { listSessions: listSessions2 } = await import("./sessions-KL4LUGD7.js");
|
|
8938
9390
|
const foundSessions = await listSessions2(workspacePath);
|
|
8939
9391
|
if (foundSessions.length === 0) {
|
|
8940
9392
|
addSystemMessage("No previous sessions found.");
|
|
@@ -9544,7 +9996,7 @@ function App({
|
|
|
9544
9996
|
const [screen, setScreen] = useState6("main");
|
|
9545
9997
|
const [resumeSessions, setResumeSessions] = useState6([]);
|
|
9546
9998
|
const [ctrlCPending, setCtrlCPending] = useState6(false);
|
|
9547
|
-
const
|
|
9999
|
+
const sessionId2 = useMemo3(() => crypto.randomUUID(), []);
|
|
9548
10000
|
const deferredMessages = useDeferredValue(messages);
|
|
9549
10001
|
const deferredPendingUpdates = useDeferredValue(pendingUpdates);
|
|
9550
10002
|
const activityFrame = useAnimatedFrame(busy);
|
|
@@ -9715,7 +10167,7 @@ function App({
|
|
|
9715
10167
|
history,
|
|
9716
10168
|
skills: skills2,
|
|
9717
10169
|
workspaceFiles,
|
|
9718
|
-
sessionId,
|
|
10170
|
+
sessionId: sessionId2,
|
|
9719
10171
|
sessionTokens,
|
|
9720
10172
|
agentMode,
|
|
9721
10173
|
previewRef,
|
|
@@ -9743,7 +10195,7 @@ function App({
|
|
|
9743
10195
|
if (pendingUpdates.length === 0 || !workspacePath) return;
|
|
9744
10196
|
const [next, ...rest] = pendingUpdates;
|
|
9745
10197
|
await applyProposedUpdate(workspacePath, next);
|
|
9746
|
-
await appendSessionEvent(workspacePath,
|
|
10198
|
+
await appendSessionEvent(workspacePath, sessionId2, {
|
|
9747
10199
|
type: "update.accepted",
|
|
9748
10200
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9749
10201
|
payload: { key: next.key, summary: next.summary }
|
|
@@ -9758,7 +10210,7 @@ function App({
|
|
|
9758
10210
|
function rejectNextPendingUpdate() {
|
|
9759
10211
|
if (pendingUpdates.length === 0 || !workspacePath) return;
|
|
9760
10212
|
const [next, ...rest] = pendingUpdates;
|
|
9761
|
-
void appendSessionEvent(workspacePath,
|
|
10213
|
+
void appendSessionEvent(workspacePath, sessionId2, {
|
|
9762
10214
|
type: "update.rejected",
|
|
9763
10215
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9764
10216
|
payload: { key: next.key, summary: next.summary }
|
|
@@ -9822,7 +10274,7 @@ function App({
|
|
|
9822
10274
|
summary: `Research charter: ${charter.researchQuestion}`
|
|
9823
10275
|
};
|
|
9824
10276
|
await applyProposedUpdate(workspacePath, charterUpdate);
|
|
9825
|
-
await appendSessionEvent(workspacePath,
|
|
10277
|
+
await appendSessionEvent(workspacePath, sessionId2, {
|
|
9826
10278
|
type: "charter.approved",
|
|
9827
10279
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9828
10280
|
payload: { charterId: charter.id }
|
|
@@ -9835,7 +10287,7 @@ function App({
|
|
|
9835
10287
|
}
|
|
9836
10288
|
function rejectCharter() {
|
|
9837
10289
|
if (!planningState.charter || !workspacePath) return;
|
|
9838
|
-
void appendSessionEvent(workspacePath,
|
|
10290
|
+
void appendSessionEvent(workspacePath, sessionId2, {
|
|
9839
10291
|
type: "charter.rejected",
|
|
9840
10292
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9841
10293
|
payload: { charterId: planningState.charter.id }
|
|
@@ -10008,7 +10460,7 @@ function App({
|
|
|
10008
10460
|
const workspace = await scanWorkspace(workspacePath);
|
|
10009
10461
|
const workspaceContext = {
|
|
10010
10462
|
workspaceDir: workspacePath,
|
|
10011
|
-
runId:
|
|
10463
|
+
runId: sessionId2,
|
|
10012
10464
|
workspaceFiles: Object.fromEntries(workspace.files.map((f) => [f.key, f.content])),
|
|
10013
10465
|
availableKeys: workspace.files.map((f) => f.key),
|
|
10014
10466
|
fileLabels: Object.fromEntries(workspace.files.map((f) => [f.key, f.label]))
|
|
@@ -10104,7 +10556,7 @@ function App({
|
|
|
10104
10556
|
if (reviewRequired.length > 0) {
|
|
10105
10557
|
startTransition2(() => setPendingUpdates((c) => [...c, ...reviewRequired]));
|
|
10106
10558
|
}
|
|
10107
|
-
await appendSessionEvent(workspacePath,
|
|
10559
|
+
await appendSessionEvent(workspacePath, sessionId2, {
|
|
10108
10560
|
type: "chat.turn",
|
|
10109
10561
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10110
10562
|
payload: {
|
|
@@ -10162,7 +10614,7 @@ ${error.stack}` : String(error)}` }
|
|
|
10162
10614
|
const workspace = await scanWorkspace(workspacePath);
|
|
10163
10615
|
const workspaceContext = {
|
|
10164
10616
|
workspaceDir: workspacePath,
|
|
10165
|
-
runId:
|
|
10617
|
+
runId: sessionId2,
|
|
10166
10618
|
workspaceFiles: Object.fromEntries(workspace.files.map((f) => [f.key, f.content])),
|
|
10167
10619
|
availableKeys: workspace.files.map((f) => f.key),
|
|
10168
10620
|
fileLabels: Object.fromEntries(workspace.files.map((f) => [f.key, f.label]))
|
|
@@ -10202,7 +10654,7 @@ ${error.stack}` : String(error)}` }
|
|
|
10202
10654
|
const charter = parseCharterYaml(result.detectedCharter);
|
|
10203
10655
|
setPlanningState((prev) => ({ ...prev, status: "charter-review", charter }));
|
|
10204
10656
|
if (workspacePath) {
|
|
10205
|
-
await appendSessionEvent(workspacePath,
|
|
10657
|
+
await appendSessionEvent(workspacePath, sessionId2, {
|
|
10206
10658
|
type: "charter.generated",
|
|
10207
10659
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10208
10660
|
payload: { charterId: charter.id, researchQuestion: charter.researchQuestion }
|
|
@@ -10541,7 +10993,7 @@ skills.command("validate").argument("[nameOrPath]").description("Validate one us
|
|
|
10541
10993
|
await ensureOpenResearchConfig();
|
|
10542
10994
|
const skillDir = nameOrPath ? path19.isAbsolute(nameOrPath) ? nameOrPath : path19.join(getOpenResearchSkillsDir(), nameOrPath) : getOpenResearchSkillsDir();
|
|
10543
10995
|
const stat = await import("fs/promises").then(
|
|
10544
|
-
(
|
|
10996
|
+
(fs18) => fs18.stat(skillDir).catch(() => null)
|
|
10545
10997
|
);
|
|
10546
10998
|
if (!stat) {
|
|
10547
10999
|
throw new Error(`Skill path not found: ${skillDir}`);
|