steward-cli 0.22.0 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +34 -119
- package/dist/cli.js +661 -651
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -43610,6 +43610,7 @@ async function startOAuthCallbackServer(options) {
|
|
|
43610
43610
|
let settled = false;
|
|
43611
43611
|
let resolvePromise;
|
|
43612
43612
|
let rejectPromise;
|
|
43613
|
+
let abortHandler;
|
|
43613
43614
|
const promise = new Promise((res, rej) => {
|
|
43614
43615
|
resolvePromise = res;
|
|
43615
43616
|
rejectPromise = rej;
|
|
@@ -43619,7 +43620,11 @@ async function startOAuthCallbackServer(options) {
|
|
|
43619
43620
|
const s = server;
|
|
43620
43621
|
server = null;
|
|
43621
43622
|
await new Promise((res) => {
|
|
43622
|
-
|
|
43623
|
+
try {
|
|
43624
|
+
s.close(() => res());
|
|
43625
|
+
} catch {
|
|
43626
|
+
res();
|
|
43627
|
+
}
|
|
43623
43628
|
});
|
|
43624
43629
|
}
|
|
43625
43630
|
};
|
|
@@ -43628,6 +43633,9 @@ async function startOAuthCallbackServer(options) {
|
|
|
43628
43633
|
return;
|
|
43629
43634
|
settled = true;
|
|
43630
43635
|
clearTimeout(timer);
|
|
43636
|
+
if (signal && abortHandler) {
|
|
43637
|
+
signal.removeEventListener("abort", abortHandler);
|
|
43638
|
+
}
|
|
43631
43639
|
await closeServer();
|
|
43632
43640
|
if (error) {
|
|
43633
43641
|
rejectPromise(error);
|
|
@@ -43643,9 +43651,10 @@ async function startOAuthCallbackServer(options) {
|
|
|
43643
43651
|
clearTimeout(timer);
|
|
43644
43652
|
throw new AIError("OAuth login was cancelled.", { code: "aborted" });
|
|
43645
43653
|
}
|
|
43646
|
-
|
|
43654
|
+
abortHandler = () => {
|
|
43647
43655
|
finish(undefined, new AIError("OAuth login was cancelled.", { code: "aborted" }));
|
|
43648
|
-
}
|
|
43656
|
+
};
|
|
43657
|
+
signal.addEventListener("abort", abortHandler, { once: true });
|
|
43649
43658
|
}
|
|
43650
43659
|
server = http.createServer((req, res) => {
|
|
43651
43660
|
try {
|
|
@@ -43694,32 +43703,43 @@ async function startOAuthCallbackServer(options) {
|
|
|
43694
43703
|
}));
|
|
43695
43704
|
}
|
|
43696
43705
|
});
|
|
43697
|
-
|
|
43698
|
-
|
|
43699
|
-
|
|
43700
|
-
|
|
43701
|
-
|
|
43702
|
-
|
|
43703
|
-
|
|
43704
|
-
|
|
43705
|
-
|
|
43706
|
-
|
|
43707
|
-
|
|
43708
|
-
|
|
43709
|
-
|
|
43710
|
-
|
|
43711
|
-
|
|
43712
|
-
|
|
43713
|
-
|
|
43706
|
+
try {
|
|
43707
|
+
await new Promise((resolve, reject) => {
|
|
43708
|
+
server.on("error", async (err) => {
|
|
43709
|
+
clearTimeout(timer);
|
|
43710
|
+
if (signal && abortHandler) {
|
|
43711
|
+
signal.removeEventListener("abort", abortHandler);
|
|
43712
|
+
}
|
|
43713
|
+
await closeServer();
|
|
43714
|
+
if (err.code === "EADDRINUSE") {
|
|
43715
|
+
reject(new AIError(`Port ${port} is already in use. Cannot start OAuth callback server.`, {
|
|
43716
|
+
code: "oauth",
|
|
43717
|
+
cause: err
|
|
43718
|
+
}));
|
|
43719
|
+
} else {
|
|
43720
|
+
reject(new AIError(`Failed to bind OAuth callback server: ${err.message}`, {
|
|
43721
|
+
code: "oauth",
|
|
43722
|
+
cause: err
|
|
43723
|
+
}));
|
|
43724
|
+
}
|
|
43725
|
+
});
|
|
43726
|
+
server.listen(port, host, () => {
|
|
43727
|
+
resolve();
|
|
43728
|
+
});
|
|
43714
43729
|
});
|
|
43715
|
-
})
|
|
43730
|
+
} catch (listenErr) {
|
|
43731
|
+
await closeServer();
|
|
43732
|
+
throw listenErr;
|
|
43733
|
+
}
|
|
43716
43734
|
return {
|
|
43717
43735
|
redirectUri,
|
|
43718
43736
|
wait: () => promise,
|
|
43719
43737
|
cancel: () => {
|
|
43720
43738
|
finish(undefined, new AIError("OAuth callback cancelled.", { code: "aborted" }));
|
|
43721
43739
|
},
|
|
43722
|
-
close:
|
|
43740
|
+
close: async () => {
|
|
43741
|
+
await finish(undefined, new AIError("OAuth callback server closed.", { code: "aborted" }));
|
|
43742
|
+
}
|
|
43723
43743
|
};
|
|
43724
43744
|
}
|
|
43725
43745
|
|
|
@@ -43774,8 +43794,7 @@ async function exchangeAuthorizationCode(code, state, verifier, signal) {
|
|
|
43774
43794
|
signal
|
|
43775
43795
|
});
|
|
43776
43796
|
if (!response.ok) {
|
|
43777
|
-
|
|
43778
|
-
throw new AIError(`Anthropic OAuth token exchange failed (HTTP ${response.status}): ${errText}`, {
|
|
43797
|
+
throw new AIError(`Anthropic OAuth token exchange failed with HTTP status ${response.status}`, {
|
|
43779
43798
|
code: "oauth",
|
|
43780
43799
|
provider: "anthropic",
|
|
43781
43800
|
status: response.status
|
|
@@ -43878,8 +43897,7 @@ async function refreshAnthropic(credential, signal) {
|
|
|
43878
43897
|
signal
|
|
43879
43898
|
});
|
|
43880
43899
|
if (!response.ok) {
|
|
43881
|
-
|
|
43882
|
-
throw new AIError(`Anthropic OAuth token refresh failed (HTTP ${response.status}): ${errText}`, {
|
|
43900
|
+
throw new AIError(`Anthropic OAuth token refresh failed with HTTP status ${response.status}`, {
|
|
43883
43901
|
code: "oauth",
|
|
43884
43902
|
provider: "anthropic",
|
|
43885
43903
|
status: response.status
|
|
@@ -43939,8 +43957,7 @@ async function exchangeAuthorizationCode2(code, verifier, signal) {
|
|
|
43939
43957
|
signal
|
|
43940
43958
|
});
|
|
43941
43959
|
if (!response.ok) {
|
|
43942
|
-
|
|
43943
|
-
throw new AIError(`OpenRouter OAuth key exchange failed (HTTP ${response.status}): ${errText}`, {
|
|
43960
|
+
throw new AIError(`OpenRouter OAuth key exchange failed with HTTP status ${response.status}`, {
|
|
43944
43961
|
code: "oauth",
|
|
43945
43962
|
provider: "openrouter",
|
|
43946
43963
|
status: response.status
|
|
@@ -44040,14 +44057,16 @@ var SLOW_DOWN_INCREMENT_MS = 5000;
|
|
|
44040
44057
|
function abortableSleep(ms, signal) {
|
|
44041
44058
|
return new Promise((resolve, reject) => {
|
|
44042
44059
|
if (signal.aborted) {
|
|
44043
|
-
reject(new
|
|
44060
|
+
reject(new AIError("Login cancelled", { code: "aborted" }));
|
|
44044
44061
|
return;
|
|
44045
44062
|
}
|
|
44063
|
+
let timeout;
|
|
44046
44064
|
const onAbort = () => {
|
|
44047
|
-
|
|
44048
|
-
|
|
44065
|
+
if (timeout)
|
|
44066
|
+
clearTimeout(timeout);
|
|
44067
|
+
reject(new AIError("Login cancelled", { code: "aborted" }));
|
|
44049
44068
|
};
|
|
44050
|
-
|
|
44069
|
+
timeout = setTimeout(() => {
|
|
44051
44070
|
signal.removeEventListener("abort", onAbort);
|
|
44052
44071
|
resolve();
|
|
44053
44072
|
}, ms);
|
|
@@ -44065,14 +44084,14 @@ async function pollOAuthDeviceCodeFlow(options) {
|
|
|
44065
44084
|
}
|
|
44066
44085
|
while (Date.now() < deadline) {
|
|
44067
44086
|
if (options.signal.aborted) {
|
|
44068
|
-
throw new
|
|
44087
|
+
throw new AIError("Login cancelled", { code: "aborted" });
|
|
44069
44088
|
}
|
|
44070
44089
|
const result = await options.poll();
|
|
44071
44090
|
if (result.status === "complete") {
|
|
44072
44091
|
return result.value;
|
|
44073
44092
|
}
|
|
44074
44093
|
if (result.status === "failed") {
|
|
44075
|
-
throw new
|
|
44094
|
+
throw new AIError(result.message, { code: "oauth" });
|
|
44076
44095
|
}
|
|
44077
44096
|
if (result.status === "slow_down") {
|
|
44078
44097
|
intervalMs = typeof result.intervalSeconds === "number" && result.intervalSeconds > 0 ? Math.max(MINIMUM_INTERVAL_MS, Math.floor(result.intervalSeconds * 1000)) : Math.max(MINIMUM_INTERVAL_MS, intervalMs + SLOW_DOWN_INCREMENT_MS);
|
|
@@ -44082,7 +44101,7 @@ async function pollOAuthDeviceCodeFlow(options) {
|
|
|
44082
44101
|
break;
|
|
44083
44102
|
await abortableSleep(Math.min(intervalMs, remaining), options.signal);
|
|
44084
44103
|
}
|
|
44085
|
-
throw new
|
|
44104
|
+
throw new AIError("Device authorization flow timed out.", { code: "oauth" });
|
|
44086
44105
|
}
|
|
44087
44106
|
|
|
44088
44107
|
// src/packages/ai/src/auth/oauth/github-copilot.ts
|
|
@@ -44203,8 +44222,7 @@ async function refreshGitHubCopilotToken(githubAccessToken, signal) {
|
|
|
44203
44222
|
});
|
|
44204
44223
|
}
|
|
44205
44224
|
if (!res.ok) {
|
|
44206
|
-
|
|
44207
|
-
throw new AIError(`GitHub Copilot token exchange failed (HTTP ${res.status}): ${text}`, {
|
|
44225
|
+
throw new AIError(`GitHub Copilot token exchange failed with HTTP status ${res.status}`, {
|
|
44208
44226
|
code: "oauth",
|
|
44209
44227
|
provider: "github-copilot",
|
|
44210
44228
|
status: res.status
|
|
@@ -44239,6 +44257,23 @@ function toGitHubCopilotAuth(credential) {
|
|
|
44239
44257
|
}
|
|
44240
44258
|
|
|
44241
44259
|
// src/packages/ai/src/auth/resolver.ts
|
|
44260
|
+
var OAUTH_ADAPTERS = {
|
|
44261
|
+
anthropic: {
|
|
44262
|
+
login: loginAnthropic,
|
|
44263
|
+
refresh: refreshAnthropic,
|
|
44264
|
+
toResolvedAuth: toAnthropicAuth
|
|
44265
|
+
},
|
|
44266
|
+
openrouter: {
|
|
44267
|
+
login: loginOpenRouter,
|
|
44268
|
+
refresh: refreshOpenRouter,
|
|
44269
|
+
toResolvedAuth: toOpenRouterAuth
|
|
44270
|
+
},
|
|
44271
|
+
"github-copilot": {
|
|
44272
|
+
login: loginGitHubCopilot,
|
|
44273
|
+
refresh: refreshGitHubCopilot,
|
|
44274
|
+
toResolvedAuth: toGitHubCopilotAuth
|
|
44275
|
+
}
|
|
44276
|
+
};
|
|
44242
44277
|
var DEFAULT_REFRESH_MARGIN_MS = 5 * 60 * 1000;
|
|
44243
44278
|
|
|
44244
44279
|
class AuthResolver {
|
|
@@ -44286,32 +44321,21 @@ class AuthResolver {
|
|
|
44286
44321
|
return;
|
|
44287
44322
|
}
|
|
44288
44323
|
async performRefresh(provider, credential, signal) {
|
|
44289
|
-
|
|
44290
|
-
|
|
44291
|
-
|
|
44292
|
-
if (provider === "openrouter") {
|
|
44293
|
-
return await refreshOpenRouter(credential, signal);
|
|
44294
|
-
}
|
|
44295
|
-
if (provider === "github-copilot") {
|
|
44296
|
-
return await refreshGitHubCopilot(credential, signal);
|
|
44324
|
+
const adapter = OAUTH_ADAPTERS[provider];
|
|
44325
|
+
if (adapter) {
|
|
44326
|
+
return await adapter.refresh(credential, signal);
|
|
44297
44327
|
}
|
|
44298
44328
|
return credential;
|
|
44299
44329
|
}
|
|
44300
44330
|
formatOAuth(provider, credential) {
|
|
44301
|
-
|
|
44302
|
-
|
|
44303
|
-
|
|
44304
|
-
if (provider === "openrouter") {
|
|
44305
|
-
return toOpenRouterAuth(credential);
|
|
44306
|
-
}
|
|
44307
|
-
if (provider === "github-copilot") {
|
|
44308
|
-
return toGitHubCopilotAuth(credential);
|
|
44331
|
+
const adapter = OAUTH_ADAPTERS[provider];
|
|
44332
|
+
if (adapter) {
|
|
44333
|
+
return adapter.toResolvedAuth(credential);
|
|
44309
44334
|
}
|
|
44310
|
-
|
|
44311
|
-
|
|
44312
|
-
|
|
44313
|
-
|
|
44314
|
-
};
|
|
44335
|
+
throw new AIError(`Unsupported OAuth provider: "${provider}"`, {
|
|
44336
|
+
code: "oauth",
|
|
44337
|
+
provider
|
|
44338
|
+
});
|
|
44315
44339
|
}
|
|
44316
44340
|
}
|
|
44317
44341
|
|
|
@@ -44319,35 +44343,96 @@ class AuthResolver {
|
|
|
44319
44343
|
import * as fs from "node:fs/promises";
|
|
44320
44344
|
import * as path from "node:path";
|
|
44321
44345
|
import * as os2 from "node:os";
|
|
44346
|
+
var fileMutexes = new Map;
|
|
44347
|
+
function validateAuthStorage(data) {
|
|
44348
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) {
|
|
44349
|
+
return false;
|
|
44350
|
+
}
|
|
44351
|
+
const root = data;
|
|
44352
|
+
if (root.version !== 1) {
|
|
44353
|
+
return false;
|
|
44354
|
+
}
|
|
44355
|
+
if (!root.credentials || typeof root.credentials !== "object" || Array.isArray(root.credentials)) {
|
|
44356
|
+
return false;
|
|
44357
|
+
}
|
|
44358
|
+
const credentials = root.credentials;
|
|
44359
|
+
for (const [_, cred] of Object.entries(credentials)) {
|
|
44360
|
+
if (!cred || typeof cred !== "object" || Array.isArray(cred)) {
|
|
44361
|
+
return false;
|
|
44362
|
+
}
|
|
44363
|
+
const c = cred;
|
|
44364
|
+
if (c.type !== "oauth") {
|
|
44365
|
+
return false;
|
|
44366
|
+
}
|
|
44367
|
+
if (typeof c.accessToken !== "string") {
|
|
44368
|
+
return false;
|
|
44369
|
+
}
|
|
44370
|
+
if (c.refreshToken !== undefined && c.refreshToken !== null && typeof c.refreshToken !== "string") {
|
|
44371
|
+
return false;
|
|
44372
|
+
}
|
|
44373
|
+
if (c.expiresAt !== undefined && c.expiresAt !== null && typeof c.expiresAt !== "number") {
|
|
44374
|
+
return false;
|
|
44375
|
+
}
|
|
44376
|
+
}
|
|
44377
|
+
return true;
|
|
44378
|
+
}
|
|
44379
|
+
|
|
44322
44380
|
class FileCredentialStore {
|
|
44323
44381
|
dirPath;
|
|
44324
44382
|
filePath;
|
|
44325
|
-
|
|
44383
|
+
canonicalPath;
|
|
44326
44384
|
constructor(options) {
|
|
44327
44385
|
this.dirPath = options?.storageDir ?? path.join(os2.homedir(), ".steward");
|
|
44328
44386
|
this.filePath = options?.filePath ?? path.join(this.dirPath, "auth.json");
|
|
44387
|
+
this.canonicalPath = path.resolve(this.filePath);
|
|
44329
44388
|
}
|
|
44330
44389
|
async ensureDirectory() {
|
|
44331
44390
|
try {
|
|
44332
44391
|
await fs.mkdir(this.dirPath, { recursive: true, mode: 448 });
|
|
44333
44392
|
await fs.chmod(this.dirPath, 448).catch(() => {});
|
|
44334
|
-
} catch {
|
|
44393
|
+
} catch (err) {
|
|
44394
|
+
const isExist = err?.code === "EEXIST";
|
|
44395
|
+
if (!isExist) {
|
|
44396
|
+
throw new AIError(`Failed to create directory "${this.dirPath}": ${err instanceof Error ? err.message : String(err)}`, {
|
|
44397
|
+
code: "auth",
|
|
44398
|
+
cause: err
|
|
44399
|
+
});
|
|
44400
|
+
}
|
|
44401
|
+
}
|
|
44335
44402
|
}
|
|
44336
44403
|
async loadData() {
|
|
44337
44404
|
try {
|
|
44338
44405
|
const content = await fs.readFile(this.filePath, "utf-8");
|
|
44339
|
-
|
|
44340
|
-
|
|
44406
|
+
let parsed;
|
|
44407
|
+
try {
|
|
44408
|
+
parsed = JSON.parse(content);
|
|
44409
|
+
} catch (jsonErr) {
|
|
44410
|
+
throw new AIError("Invalid or corrupt auth.json schema.", {
|
|
44411
|
+
code: "auth",
|
|
44412
|
+
cause: jsonErr
|
|
44413
|
+
});
|
|
44414
|
+
}
|
|
44415
|
+
if (!validateAuthStorage(parsed)) {
|
|
44341
44416
|
throw new AIError("Invalid or corrupt auth.json schema.", { code: "auth" });
|
|
44342
44417
|
}
|
|
44418
|
+
for (const cred of Object.values(parsed.credentials)) {
|
|
44419
|
+
if (cred) {
|
|
44420
|
+
if (cred.expiresAt === null) {
|
|
44421
|
+
delete cred.expiresAt;
|
|
44422
|
+
}
|
|
44423
|
+
if (cred.refreshToken === null) {
|
|
44424
|
+
delete cred.refreshToken;
|
|
44425
|
+
}
|
|
44426
|
+
}
|
|
44427
|
+
}
|
|
44343
44428
|
return parsed;
|
|
44344
44429
|
} catch (err) {
|
|
44345
|
-
if (err
|
|
44430
|
+
if (err?.code === "ENOENT") {
|
|
44346
44431
|
return { version: 1, credentials: {} };
|
|
44347
44432
|
}
|
|
44348
44433
|
if (err instanceof AIError)
|
|
44349
44434
|
throw err;
|
|
44350
|
-
throw new AIError(`Failed to read auth store: ${err.message}`, {
|
|
44435
|
+
throw new AIError(`Failed to read auth store: ${err instanceof Error ? err.message : String(err)}`, {
|
|
44351
44436
|
code: "auth",
|
|
44352
44437
|
cause: err
|
|
44353
44438
|
});
|
|
@@ -44364,7 +44449,7 @@ class FileCredentialStore {
|
|
|
44364
44449
|
await fs.chmod(this.filePath, 384).catch(() => {});
|
|
44365
44450
|
} catch (err) {
|
|
44366
44451
|
await fs.unlink(tempFile).catch(() => {});
|
|
44367
|
-
throw new AIError(`Failed to atomically save auth store: ${err.message}`, {
|
|
44452
|
+
throw new AIError(`Failed to atomically save auth store: ${err instanceof Error ? err.message : String(err)}`, {
|
|
44368
44453
|
code: "auth",
|
|
44369
44454
|
cause: err
|
|
44370
44455
|
});
|
|
@@ -44389,24 +44474,21 @@ class FileCredentialStore {
|
|
|
44389
44474
|
return list;
|
|
44390
44475
|
}
|
|
44391
44476
|
async modify(provider, fn) {
|
|
44392
|
-
|
|
44393
|
-
|
|
44394
|
-
|
|
44395
|
-
|
|
44396
|
-
|
|
44397
|
-
|
|
44398
|
-
|
|
44399
|
-
|
|
44400
|
-
|
|
44401
|
-
|
|
44402
|
-
|
|
44403
|
-
|
|
44404
|
-
resolve(updated);
|
|
44405
|
-
} catch (err) {
|
|
44406
|
-
reject(err);
|
|
44407
|
-
}
|
|
44408
|
-
});
|
|
44477
|
+
const currentMutex = fileMutexes.get(this.canonicalPath) ?? Promise.resolve();
|
|
44478
|
+
const nextMutex = currentMutex.then(async () => {
|
|
44479
|
+
const data = await this.loadData();
|
|
44480
|
+
const current = data.credentials[provider];
|
|
44481
|
+
const updated = await fn(current);
|
|
44482
|
+
if (updated) {
|
|
44483
|
+
data.credentials[provider] = updated;
|
|
44484
|
+
} else {
|
|
44485
|
+
delete data.credentials[provider];
|
|
44486
|
+
}
|
|
44487
|
+
await this.saveData(data);
|
|
44488
|
+
return updated;
|
|
44409
44489
|
});
|
|
44490
|
+
fileMutexes.set(this.canonicalPath, nextMutex.catch(() => {}));
|
|
44491
|
+
return await nextMutex;
|
|
44410
44492
|
}
|
|
44411
44493
|
async delete(provider) {
|
|
44412
44494
|
await this.modify(provider, async () => {
|
|
@@ -44465,19 +44547,14 @@ class DefaultAuthManager {
|
|
|
44465
44547
|
return await this.resolver.resolve(provider, signal);
|
|
44466
44548
|
}
|
|
44467
44549
|
async login(provider, interaction) {
|
|
44468
|
-
|
|
44469
|
-
if (
|
|
44470
|
-
credential = await loginAnthropic(interaction);
|
|
44471
|
-
} else if (provider === "openrouter") {
|
|
44472
|
-
credential = await loginOpenRouter(interaction);
|
|
44473
|
-
} else if (provider === "github-copilot") {
|
|
44474
|
-
credential = await loginGitHubCopilot(interaction);
|
|
44475
|
-
} else {
|
|
44550
|
+
const adapter = OAUTH_ADAPTERS[provider];
|
|
44551
|
+
if (!adapter) {
|
|
44476
44552
|
throw new AIError(`No browser OAuth login is available for "${provider}".`, {
|
|
44477
44553
|
code: "oauth",
|
|
44478
44554
|
provider
|
|
44479
44555
|
});
|
|
44480
44556
|
}
|
|
44557
|
+
const credential = await adapter.login(interaction);
|
|
44481
44558
|
await this.store.modify(provider, async () => credential);
|
|
44482
44559
|
return credential;
|
|
44483
44560
|
}
|
|
@@ -44606,6 +44683,13 @@ var PROVIDER_REGISTRY = {
|
|
|
44606
44683
|
envVar: "CUSTOM_API_URL / CUSTOM_API_KEY"
|
|
44607
44684
|
}
|
|
44608
44685
|
};
|
|
44686
|
+
function getProviderDescriptor(provider) {
|
|
44687
|
+
const descriptor = PROVIDER_REGISTRY[provider];
|
|
44688
|
+
if (!descriptor) {
|
|
44689
|
+
throw new Error(`Unsupported provider: "${provider}"`);
|
|
44690
|
+
}
|
|
44691
|
+
return descriptor;
|
|
44692
|
+
}
|
|
44609
44693
|
|
|
44610
44694
|
// src/packages/ai/src/models/selection.ts
|
|
44611
44695
|
var CANONICAL_DEFAULT_EFFORT = "medium";
|
|
@@ -44623,26 +44707,36 @@ var PROVIDER_SELECTION_PRIORITY = [
|
|
|
44623
44707
|
"custom"
|
|
44624
44708
|
];
|
|
44625
44709
|
function inferProviderFromModelId(modelId) {
|
|
44626
|
-
const lower = modelId.toLowerCase();
|
|
44627
|
-
if (
|
|
44710
|
+
const lower = modelId.trim().toLowerCase();
|
|
44711
|
+
if (!lower)
|
|
44712
|
+
return null;
|
|
44713
|
+
if (/^[a-z0-9-_.]+\/[a-z0-9-_.]+$/.test(lower)) {
|
|
44628
44714
|
return "openrouter";
|
|
44629
|
-
|
|
44715
|
+
}
|
|
44716
|
+
if (lower.startsWith("copilot/") || lower.startsWith("github-copilot/")) {
|
|
44630
44717
|
return "github-copilot";
|
|
44631
|
-
|
|
44718
|
+
}
|
|
44719
|
+
if (lower.startsWith("gemini-") || lower.startsWith("gemma-")) {
|
|
44632
44720
|
return "gemini";
|
|
44633
|
-
|
|
44721
|
+
}
|
|
44722
|
+
if (lower.startsWith("claude-")) {
|
|
44634
44723
|
return "anthropic";
|
|
44635
|
-
|
|
44724
|
+
}
|
|
44725
|
+
if (lower.startsWith("gpt-") || lower.startsWith("chatgpt-") || /^o[1-9]($|-)/.test(lower)) {
|
|
44636
44726
|
return "openai";
|
|
44637
44727
|
}
|
|
44638
|
-
if (lower.startsWith("deepseek-"))
|
|
44728
|
+
if (lower.startsWith("deepseek-")) {
|
|
44639
44729
|
return "deepseek";
|
|
44640
|
-
|
|
44730
|
+
}
|
|
44731
|
+
if (lower.startsWith("grok-")) {
|
|
44641
44732
|
return "xai";
|
|
44642
|
-
|
|
44733
|
+
}
|
|
44734
|
+
if (lower.startsWith("codestral") || lower.startsWith("mistral") || lower.startsWith("pixtral") || lower.startsWith("ministral")) {
|
|
44643
44735
|
return "mistral";
|
|
44644
|
-
|
|
44736
|
+
}
|
|
44737
|
+
if (lower.startsWith("ollama/") || /^[a-z0-9_.-]+:[a-z0-9_.-]+$/.test(lower)) {
|
|
44645
44738
|
return "ollama";
|
|
44739
|
+
}
|
|
44646
44740
|
return null;
|
|
44647
44741
|
}
|
|
44648
44742
|
async function resolveModelSelection(requested, context) {
|
|
@@ -44807,6 +44901,9 @@ function parseSSEEvent(raw) {
|
|
|
44807
44901
|
}
|
|
44808
44902
|
|
|
44809
44903
|
// src/packages/ai/src/json.ts
|
|
44904
|
+
function sanitizeSurrogates(text) {
|
|
44905
|
+
return text.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "");
|
|
44906
|
+
}
|
|
44810
44907
|
function parseStreamingJson(partial) {
|
|
44811
44908
|
const trimmed = partial.trim();
|
|
44812
44909
|
if (!trimmed) {
|
|
@@ -44876,82 +44973,78 @@ function parseStreamingJson(partial) {
|
|
|
44876
44973
|
}
|
|
44877
44974
|
|
|
44878
44975
|
// src/packages/ai/src/providers/openai-compatible.ts
|
|
44976
|
+
var STATIC_PROFILES = {
|
|
44977
|
+
openrouter: {
|
|
44978
|
+
provider: "openrouter",
|
|
44979
|
+
baseUrl: "https://openrouter.ai/api/v1",
|
|
44980
|
+
supportsReasoning: true,
|
|
44981
|
+
reasoningFormat: "openrouter",
|
|
44982
|
+
apiPath: "/chat/completions",
|
|
44983
|
+
requiresApiKey: true
|
|
44984
|
+
},
|
|
44985
|
+
deepseek: {
|
|
44986
|
+
provider: "deepseek",
|
|
44987
|
+
baseUrl: "https://api.deepseek.com",
|
|
44988
|
+
supportsReasoning: true,
|
|
44989
|
+
reasoningFormat: "deepseek",
|
|
44990
|
+
apiPath: "/chat/completions",
|
|
44991
|
+
requiresApiKey: true
|
|
44992
|
+
},
|
|
44993
|
+
"github-copilot": {
|
|
44994
|
+
provider: "github-copilot",
|
|
44995
|
+
baseUrl: "https://api.individual.githubcopilot.com",
|
|
44996
|
+
supportsReasoning: true,
|
|
44997
|
+
reasoningFormat: "openai",
|
|
44998
|
+
apiPath: "/chat/completions",
|
|
44999
|
+
requiresApiKey: true
|
|
45000
|
+
},
|
|
45001
|
+
groq: {
|
|
45002
|
+
provider: "groq",
|
|
45003
|
+
baseUrl: "https://api.groq.com/openai/v1",
|
|
45004
|
+
supportsReasoning: true,
|
|
45005
|
+
reasoningFormat: "deepseek",
|
|
45006
|
+
apiPath: "/chat/completions",
|
|
45007
|
+
requiresApiKey: true
|
|
45008
|
+
},
|
|
45009
|
+
xai: {
|
|
45010
|
+
provider: "xai",
|
|
45011
|
+
baseUrl: "https://api.x.ai/v1",
|
|
45012
|
+
supportsReasoning: true,
|
|
45013
|
+
reasoningFormat: "openai",
|
|
45014
|
+
apiPath: "/chat/completions",
|
|
45015
|
+
requiresApiKey: true
|
|
45016
|
+
},
|
|
45017
|
+
mistral: {
|
|
45018
|
+
provider: "mistral",
|
|
45019
|
+
baseUrl: "https://api.mistral.ai/v1",
|
|
45020
|
+
supportsReasoning: false,
|
|
45021
|
+
reasoningFormat: "none",
|
|
45022
|
+
apiPath: "/chat/completions",
|
|
45023
|
+
requiresApiKey: true
|
|
45024
|
+
}
|
|
45025
|
+
};
|
|
44879
45026
|
function getProfile(provider, customBaseUrl) {
|
|
44880
|
-
|
|
44881
|
-
|
|
44882
|
-
return {
|
|
44883
|
-
provider: "openrouter",
|
|
44884
|
-
baseUrl: "https://openrouter.ai/api/v1",
|
|
44885
|
-
supportsReasoning: true,
|
|
44886
|
-
reasoningFormat: "openrouter",
|
|
44887
|
-
apiPath: "/chat/completions",
|
|
44888
|
-
requiresApiKey: true
|
|
44889
|
-
};
|
|
44890
|
-
case "deepseek":
|
|
44891
|
-
return {
|
|
44892
|
-
provider: "deepseek",
|
|
44893
|
-
baseUrl: "https://api.deepseek.com",
|
|
44894
|
-
supportsReasoning: true,
|
|
44895
|
-
reasoningFormat: "deepseek",
|
|
44896
|
-
apiPath: "/chat/completions",
|
|
44897
|
-
requiresApiKey: true
|
|
44898
|
-
};
|
|
44899
|
-
case "github-copilot":
|
|
44900
|
-
return {
|
|
44901
|
-
provider: "github-copilot",
|
|
44902
|
-
baseUrl: "https://api.individual.githubcopilot.com",
|
|
44903
|
-
supportsReasoning: true,
|
|
44904
|
-
reasoningFormat: "openai",
|
|
44905
|
-
apiPath: "/chat/completions",
|
|
44906
|
-
requiresApiKey: true
|
|
44907
|
-
};
|
|
44908
|
-
case "groq":
|
|
44909
|
-
return {
|
|
44910
|
-
provider: "groq",
|
|
44911
|
-
baseUrl: "https://api.groq.com/openai/v1",
|
|
44912
|
-
supportsReasoning: true,
|
|
44913
|
-
reasoningFormat: "deepseek",
|
|
44914
|
-
apiPath: "/chat/completions",
|
|
44915
|
-
requiresApiKey: true
|
|
44916
|
-
};
|
|
44917
|
-
case "xai":
|
|
44918
|
-
return {
|
|
44919
|
-
provider: "xai",
|
|
44920
|
-
baseUrl: "https://api.x.ai/v1",
|
|
44921
|
-
supportsReasoning: true,
|
|
44922
|
-
reasoningFormat: "openai",
|
|
44923
|
-
apiPath: "/chat/completions",
|
|
44924
|
-
requiresApiKey: true
|
|
44925
|
-
};
|
|
44926
|
-
case "mistral":
|
|
44927
|
-
return {
|
|
44928
|
-
provider: "mistral",
|
|
44929
|
-
baseUrl: "https://api.mistral.ai/v1",
|
|
44930
|
-
supportsReasoning: false,
|
|
44931
|
-
reasoningFormat: "none",
|
|
44932
|
-
apiPath: "/chat/completions",
|
|
44933
|
-
requiresApiKey: true
|
|
44934
|
-
};
|
|
44935
|
-
case "ollama":
|
|
44936
|
-
return {
|
|
44937
|
-
provider: "ollama",
|
|
44938
|
-
baseUrl: customBaseUrl || process.env.OLLAMA_BASE_URL || "http://localhost:11434/v1",
|
|
44939
|
-
supportsReasoning: true,
|
|
44940
|
-
reasoningFormat: "deepseek",
|
|
44941
|
-
apiPath: "/chat/completions",
|
|
44942
|
-
requiresApiKey: false
|
|
44943
|
-
};
|
|
44944
|
-
case "custom":
|
|
44945
|
-
default:
|
|
44946
|
-
return {
|
|
44947
|
-
provider: "custom",
|
|
44948
|
-
baseUrl: customBaseUrl || process.env.CUSTOM_API_URL || "http://localhost:11434/v1",
|
|
44949
|
-
supportsReasoning: true,
|
|
44950
|
-
reasoningFormat: "openai",
|
|
44951
|
-
apiPath: "/chat/completions",
|
|
44952
|
-
requiresApiKey: false
|
|
44953
|
-
};
|
|
45027
|
+
if (provider in STATIC_PROFILES) {
|
|
45028
|
+
return STATIC_PROFILES[provider];
|
|
44954
45029
|
}
|
|
45030
|
+
if (provider === "ollama") {
|
|
45031
|
+
return {
|
|
45032
|
+
provider: "ollama",
|
|
45033
|
+
baseUrl: customBaseUrl || process.env.OLLAMA_BASE_URL || "http://localhost:11434/v1",
|
|
45034
|
+
supportsReasoning: true,
|
|
45035
|
+
reasoningFormat: "deepseek",
|
|
45036
|
+
apiPath: "/chat/completions",
|
|
45037
|
+
requiresApiKey: false
|
|
45038
|
+
};
|
|
45039
|
+
}
|
|
45040
|
+
return {
|
|
45041
|
+
provider: "custom",
|
|
45042
|
+
baseUrl: customBaseUrl || process.env.CUSTOM_API_URL || "http://localhost:11434/v1",
|
|
45043
|
+
supportsReasoning: true,
|
|
45044
|
+
reasoningFormat: "openai",
|
|
45045
|
+
apiPath: "/chat/completions",
|
|
45046
|
+
requiresApiKey: false
|
|
45047
|
+
};
|
|
44955
45048
|
}
|
|
44956
45049
|
function mapEffort(effort, format) {
|
|
44957
45050
|
if (effort === "none")
|
|
@@ -44974,13 +45067,13 @@ function convertMessages(messages) {
|
|
|
44974
45067
|
const converted = [];
|
|
44975
45068
|
for (const msg of messages) {
|
|
44976
45069
|
if (msg.role === "system") {
|
|
44977
|
-
converted.push({ role: "system", content: msg.content });
|
|
45070
|
+
converted.push({ role: "system", content: sanitizeSurrogates(msg.content) });
|
|
44978
45071
|
continue;
|
|
44979
45072
|
}
|
|
44980
45073
|
if (msg.role === "user") {
|
|
44981
45074
|
const text = typeof msg.content === "string" ? msg.content : msg.content.map((c) => c.text).join(`
|
|
44982
45075
|
`);
|
|
44983
|
-
converted.push({ role: "user", content: text });
|
|
45076
|
+
converted.push({ role: "user", content: sanitizeSurrogates(text) });
|
|
44984
45077
|
continue;
|
|
44985
45078
|
}
|
|
44986
45079
|
if (msg.role === "assistant") {
|
|
@@ -45005,9 +45098,9 @@ function convertMessages(messages) {
|
|
|
45005
45098
|
}
|
|
45006
45099
|
const item = { role: "assistant" };
|
|
45007
45100
|
if (text)
|
|
45008
|
-
item.content = text;
|
|
45101
|
+
item.content = sanitizeSurrogates(text);
|
|
45009
45102
|
if (reasoning_content)
|
|
45010
|
-
item.reasoning_content = reasoning_content;
|
|
45103
|
+
item.reasoning_content = sanitizeSurrogates(reasoning_content);
|
|
45011
45104
|
if (tool_calls.length > 0)
|
|
45012
45105
|
item.tool_calls = tool_calls;
|
|
45013
45106
|
converted.push(item);
|
|
@@ -45015,10 +45108,11 @@ function convertMessages(messages) {
|
|
|
45015
45108
|
}
|
|
45016
45109
|
if (msg.role === "tool") {
|
|
45017
45110
|
for (const res of msg.content) {
|
|
45111
|
+
const outStr = typeof res.output === "string" ? res.output : JSON.stringify(res.output);
|
|
45018
45112
|
converted.push({
|
|
45019
45113
|
role: "tool",
|
|
45020
45114
|
tool_call_id: res.toolCallId,
|
|
45021
|
-
content:
|
|
45115
|
+
content: sanitizeSurrogates(outStr)
|
|
45022
45116
|
});
|
|
45023
45117
|
}
|
|
45024
45118
|
}
|
|
@@ -45074,7 +45168,7 @@ function streamOpenAICompatible(options) {
|
|
|
45074
45168
|
});
|
|
45075
45169
|
} catch (err) {
|
|
45076
45170
|
const isAbort = request.abortSignal?.aborted;
|
|
45077
|
-
const error = new AIError(isAbort ? "Inference request aborted." : `Network request to ${profile.provider} failed: ${err.message}`, {
|
|
45171
|
+
const error = new AIError(isAbort ? "Inference request aborted." : `Network request to ${profile.provider} failed: ${err instanceof Error ? err.message : String(err)}`, {
|
|
45078
45172
|
code: isAbort ? "aborted" : "network",
|
|
45079
45173
|
provider: request.model.provider,
|
|
45080
45174
|
cause: err
|
|
@@ -45084,10 +45178,9 @@ function streamOpenAICompatible(options) {
|
|
|
45084
45178
|
return;
|
|
45085
45179
|
}
|
|
45086
45180
|
if (!response.ok) {
|
|
45087
|
-
const errBody = await response.text().catch(() => "");
|
|
45088
45181
|
const isRateLimit = response.status === 429;
|
|
45089
45182
|
const isAuth = response.status === 401 || response.status === 403;
|
|
45090
|
-
const error = new AIError(`${profile.provider}
|
|
45183
|
+
const error = new AIError(`${profile.provider} request failed with HTTP status ${response.status}`, {
|
|
45091
45184
|
code: isAuth ? auth?.type === "oauth" ? "oauth" : "auth" : isRateLimit ? "rate-limit" : "provider",
|
|
45092
45185
|
provider: request.model.provider,
|
|
45093
45186
|
status: response.status
|
|
@@ -45105,8 +45198,7 @@ function streamOpenAICompatible(options) {
|
|
|
45105
45198
|
yield { type: "error", error };
|
|
45106
45199
|
return;
|
|
45107
45200
|
}
|
|
45108
|
-
|
|
45109
|
-
let reasoningContent = "";
|
|
45201
|
+
const assistantContent = [];
|
|
45110
45202
|
const toolCallBuffers = new Map;
|
|
45111
45203
|
const usage = {
|
|
45112
45204
|
inputTokens: 0,
|
|
@@ -45124,19 +45216,24 @@ function streamOpenAICompatible(options) {
|
|
|
45124
45216
|
} catch {
|
|
45125
45217
|
continue;
|
|
45126
45218
|
}
|
|
45127
|
-
if (eventData.usage) {
|
|
45219
|
+
if (eventData.usage && typeof eventData.usage === "object") {
|
|
45128
45220
|
const u = eventData.usage;
|
|
45129
|
-
|
|
45130
|
-
|
|
45131
|
-
|
|
45132
|
-
|
|
45133
|
-
|
|
45221
|
+
if (typeof u.prompt_tokens === "number")
|
|
45222
|
+
usage.inputTokens = u.prompt_tokens;
|
|
45223
|
+
if (typeof u.completion_tokens === "number")
|
|
45224
|
+
usage.outputTokens = u.completion_tokens;
|
|
45225
|
+
if (typeof u.total_tokens === "number")
|
|
45226
|
+
usage.totalTokens = u.total_tokens;
|
|
45227
|
+
const details = u.completion_tokens_details;
|
|
45228
|
+
if (typeof details?.reasoning_tokens === "number") {
|
|
45229
|
+
usage.reasoningTokens = details.reasoning_tokens;
|
|
45134
45230
|
}
|
|
45135
45231
|
}
|
|
45136
|
-
const
|
|
45232
|
+
const choices = eventData.choices;
|
|
45233
|
+
const choice = choices?.[0];
|
|
45137
45234
|
if (!choice)
|
|
45138
45235
|
continue;
|
|
45139
|
-
if (choice.finish_reason) {
|
|
45236
|
+
if (choice.finish_reason && typeof choice.finish_reason === "string") {
|
|
45140
45237
|
const fr = choice.finish_reason;
|
|
45141
45238
|
if (fr === "stop")
|
|
45142
45239
|
finishReason = "stop";
|
|
@@ -45148,70 +45245,86 @@ function streamOpenAICompatible(options) {
|
|
|
45148
45245
|
const delta = choice.delta;
|
|
45149
45246
|
if (!delta)
|
|
45150
45247
|
continue;
|
|
45151
|
-
const
|
|
45248
|
+
const reasoningDetails = delta.reasoning_details;
|
|
45249
|
+
const reasoningDelta = typeof delta.reasoning_content === "string" && delta.reasoning_content || typeof delta.reasoning === "string" && delta.reasoning || typeof delta.reasoning_text === "string" && delta.reasoning_text || typeof reasoningDetails?.text === "string" && reasoningDetails.text || "";
|
|
45152
45250
|
if (reasoningDelta) {
|
|
45153
|
-
|
|
45251
|
+
const lastBlock = assistantContent[assistantContent.length - 1];
|
|
45252
|
+
if (lastBlock && lastBlock.type === "thinking") {
|
|
45253
|
+
lastBlock.thinking += reasoningDelta;
|
|
45254
|
+
} else {
|
|
45255
|
+
assistantContent.push({
|
|
45256
|
+
type: "thinking",
|
|
45257
|
+
thinking: reasoningDelta
|
|
45258
|
+
});
|
|
45259
|
+
}
|
|
45154
45260
|
yield { type: "reasoning-delta", delta: reasoningDelta };
|
|
45155
45261
|
}
|
|
45156
|
-
if (delta.content) {
|
|
45157
|
-
|
|
45158
|
-
|
|
45262
|
+
if (typeof delta.content === "string" && delta.content) {
|
|
45263
|
+
const textDelta = delta.content;
|
|
45264
|
+
const lastBlock = assistantContent[assistantContent.length - 1];
|
|
45265
|
+
if (lastBlock && lastBlock.type === "text") {
|
|
45266
|
+
lastBlock.text += textDelta;
|
|
45267
|
+
} else {
|
|
45268
|
+
assistantContent.push({
|
|
45269
|
+
type: "text",
|
|
45270
|
+
text: textDelta
|
|
45271
|
+
});
|
|
45272
|
+
}
|
|
45273
|
+
yield { type: "text-delta", delta: textDelta };
|
|
45159
45274
|
}
|
|
45160
|
-
if (
|
|
45275
|
+
if (Array.isArray(delta.tool_calls)) {
|
|
45161
45276
|
for (const tc of delta.tool_calls) {
|
|
45162
|
-
const idx = tc.index
|
|
45163
|
-
|
|
45164
|
-
|
|
45165
|
-
|
|
45166
|
-
|
|
45167
|
-
|
|
45277
|
+
const idx = typeof tc.index === "number" ? tc.index : 0;
|
|
45278
|
+
const fn = tc.function;
|
|
45279
|
+
const tcId = typeof tc.id === "string" ? tc.id : undefined;
|
|
45280
|
+
const fnName = typeof fn?.name === "string" ? fn.name : undefined;
|
|
45281
|
+
const fnArgs = typeof fn?.arguments === "string" ? fn.arguments : undefined;
|
|
45282
|
+
let entry = toolCallBuffers.get(idx);
|
|
45283
|
+
if (!entry) {
|
|
45284
|
+
const buffer = {
|
|
45285
|
+
id: tcId || `call_${idx}`,
|
|
45286
|
+
name: fnName || "",
|
|
45168
45287
|
rawArgs: ""
|
|
45169
45288
|
};
|
|
45170
|
-
|
|
45289
|
+
const slot = {
|
|
45290
|
+
type: "tool-call",
|
|
45291
|
+
id: buffer.id,
|
|
45292
|
+
name: buffer.name,
|
|
45293
|
+
arguments: {}
|
|
45294
|
+
};
|
|
45295
|
+
assistantContent.push(slot);
|
|
45296
|
+
entry = { buffer, slot };
|
|
45297
|
+
toolCallBuffers.set(idx, entry);
|
|
45171
45298
|
if (buffer.name) {
|
|
45172
45299
|
yield { type: "tool-call-start", id: buffer.id, name: buffer.name };
|
|
45173
45300
|
}
|
|
45174
45301
|
} else {
|
|
45175
|
-
if (
|
|
45176
|
-
buffer.
|
|
45177
|
-
|
|
45302
|
+
if (tcId && entry.buffer.id !== tcId) {
|
|
45303
|
+
entry.buffer.id = tcId;
|
|
45304
|
+
entry.slot.id = tcId;
|
|
45305
|
+
}
|
|
45306
|
+
if (!entry.buffer.name && fnName) {
|
|
45307
|
+
entry.buffer.name = fnName;
|
|
45308
|
+
entry.slot.name = fnName;
|
|
45309
|
+
yield { type: "tool-call-start", id: entry.buffer.id, name: entry.buffer.name };
|
|
45178
45310
|
}
|
|
45179
45311
|
}
|
|
45180
|
-
if (
|
|
45181
|
-
buffer.rawArgs +=
|
|
45312
|
+
if (fnArgs) {
|
|
45313
|
+
entry.buffer.rawArgs += fnArgs;
|
|
45182
45314
|
yield {
|
|
45183
45315
|
type: "tool-call-delta",
|
|
45184
|
-
id: buffer.id,
|
|
45185
|
-
delta:
|
|
45316
|
+
id: entry.buffer.id,
|
|
45317
|
+
delta: fnArgs
|
|
45186
45318
|
};
|
|
45187
45319
|
}
|
|
45188
45320
|
}
|
|
45189
45321
|
}
|
|
45190
45322
|
}
|
|
45191
|
-
const
|
|
45192
|
-
|
|
45193
|
-
|
|
45194
|
-
type: "thinking",
|
|
45195
|
-
thinking: reasoningContent
|
|
45196
|
-
});
|
|
45197
|
-
}
|
|
45198
|
-
if (textContent) {
|
|
45199
|
-
assistantContent.push({
|
|
45200
|
-
type: "text",
|
|
45201
|
-
text: textContent
|
|
45202
|
-
});
|
|
45203
|
-
}
|
|
45204
|
-
for (const [_, buffer] of toolCallBuffers.entries()) {
|
|
45205
|
-
const parsedArgs = parseStreamingJson(buffer.rawArgs);
|
|
45206
|
-
const toolCall = {
|
|
45207
|
-
type: "tool-call",
|
|
45208
|
-
id: buffer.id,
|
|
45209
|
-
name: buffer.name,
|
|
45210
|
-
arguments: parsedArgs
|
|
45211
|
-
};
|
|
45212
|
-
assistantContent.push(toolCall);
|
|
45323
|
+
for (const entry of toolCallBuffers.values()) {
|
|
45324
|
+
const parsedArgs = parseStreamingJson(entry.buffer.rawArgs);
|
|
45325
|
+
entry.slot.arguments = parsedArgs;
|
|
45213
45326
|
finishReason = "tool-use";
|
|
45214
|
-
yield { type: "tool-call-end", toolCall };
|
|
45327
|
+
yield { type: "tool-call-end", toolCall: entry.slot };
|
|
45215
45328
|
}
|
|
45216
45329
|
usage.totalTokens = usage.totalTokens || usage.inputTokens + usage.outputTokens;
|
|
45217
45330
|
const finalMessage = {
|
|
@@ -45231,7 +45344,7 @@ function streamOpenAICompatible(options) {
|
|
|
45231
45344
|
};
|
|
45232
45345
|
} catch (err) {
|
|
45233
45346
|
const isAbort = request.abortSignal?.aborted;
|
|
45234
|
-
const error = new AIError(isAbort ? "Inference request aborted." : `${profile.provider} stream reading failed: ${err.message}`, {
|
|
45347
|
+
const error = new AIError(isAbort ? "Inference request aborted." : `${profile.provider} stream reading failed: ${err instanceof Error ? err.message : String(err)}`, {
|
|
45235
45348
|
code: isAbort ? "aborted" : "provider",
|
|
45236
45349
|
provider: request.model.provider,
|
|
45237
45350
|
cause: err
|
|
@@ -45265,7 +45378,7 @@ function streamOpenAI(options) {
|
|
|
45265
45378
|
request,
|
|
45266
45379
|
auth,
|
|
45267
45380
|
profile: {
|
|
45268
|
-
provider: "
|
|
45381
|
+
provider: "openai",
|
|
45269
45382
|
baseUrl: "https://api.openai.com/v1",
|
|
45270
45383
|
supportsReasoning: true,
|
|
45271
45384
|
reasoningFormat: "openai",
|
|
@@ -45302,11 +45415,11 @@ ${msg.content}` : msg.content;
|
|
|
45302
45415
|
}
|
|
45303
45416
|
if (msg.role === "user") {
|
|
45304
45417
|
if (typeof msg.content === "string") {
|
|
45305
|
-
anthropicMessages.push({ role: "user", content: msg.content });
|
|
45418
|
+
anthropicMessages.push({ role: "user", content: sanitizeSurrogates(msg.content) });
|
|
45306
45419
|
} else {
|
|
45307
45420
|
const text = msg.content.map((c) => c.text).join(`
|
|
45308
45421
|
`);
|
|
45309
|
-
anthropicMessages.push({ role: "user", content: text });
|
|
45422
|
+
anthropicMessages.push({ role: "user", content: sanitizeSurrogates(text) });
|
|
45310
45423
|
}
|
|
45311
45424
|
continue;
|
|
45312
45425
|
}
|
|
@@ -45314,11 +45427,11 @@ ${msg.content}` : msg.content;
|
|
|
45314
45427
|
const content = [];
|
|
45315
45428
|
for (const block of msg.content) {
|
|
45316
45429
|
if (block.type === "text") {
|
|
45317
|
-
content.push({ type: "text", text: block.text });
|
|
45430
|
+
content.push({ type: "text", text: sanitizeSurrogates(block.text) });
|
|
45318
45431
|
} else if (block.type === "thinking") {
|
|
45319
45432
|
content.push({
|
|
45320
45433
|
type: "thinking",
|
|
45321
|
-
thinking: block.thinking,
|
|
45434
|
+
thinking: sanitizeSurrogates(block.thinking),
|
|
45322
45435
|
signature: block.thinkingSignature
|
|
45323
45436
|
});
|
|
45324
45437
|
} else if (block.type === "tool-call") {
|
|
@@ -45336,10 +45449,11 @@ ${msg.content}` : msg.content;
|
|
|
45336
45449
|
if (msg.role === "tool") {
|
|
45337
45450
|
const content = [];
|
|
45338
45451
|
for (const res of msg.content) {
|
|
45452
|
+
const outStr = typeof res.output === "string" ? res.output : JSON.stringify(res.output);
|
|
45339
45453
|
content.push({
|
|
45340
45454
|
type: "tool_result",
|
|
45341
45455
|
tool_use_id: res.toolCallId,
|
|
45342
|
-
content:
|
|
45456
|
+
content: sanitizeSurrogates(outStr),
|
|
45343
45457
|
is_error: res.isError
|
|
45344
45458
|
});
|
|
45345
45459
|
}
|
|
@@ -45352,10 +45466,11 @@ function streamAnthropic(options) {
|
|
|
45352
45466
|
const { request, auth } = options;
|
|
45353
45467
|
const thinkingConfig = mapAnthropicEffort(request.model.effort);
|
|
45354
45468
|
const { system, anthropicMessages } = convertMessages2(request.messages);
|
|
45355
|
-
const tools = request.tools?.map((t) => ({
|
|
45469
|
+
const tools = request.tools?.map((t, idx, arr) => ({
|
|
45356
45470
|
name: t.name,
|
|
45357
45471
|
description: t.description,
|
|
45358
|
-
input_schema: t.inputSchema
|
|
45472
|
+
input_schema: t.inputSchema,
|
|
45473
|
+
...idx === arr.length - 1 ? { cache_control: { type: "ephemeral" } } : {}
|
|
45359
45474
|
}));
|
|
45360
45475
|
const body = {
|
|
45361
45476
|
model: request.model.modelId,
|
|
@@ -45364,7 +45479,13 @@ function streamAnthropic(options) {
|
|
|
45364
45479
|
stream: true
|
|
45365
45480
|
};
|
|
45366
45481
|
if (system) {
|
|
45367
|
-
body.system =
|
|
45482
|
+
body.system = [
|
|
45483
|
+
{
|
|
45484
|
+
type: "text",
|
|
45485
|
+
text: sanitizeSurrogates(system),
|
|
45486
|
+
cache_control: { type: "ephemeral" }
|
|
45487
|
+
}
|
|
45488
|
+
];
|
|
45368
45489
|
}
|
|
45369
45490
|
if (tools && tools.length > 0) {
|
|
45370
45491
|
body.tools = tools;
|
|
@@ -45379,7 +45500,7 @@ function streamAnthropic(options) {
|
|
|
45379
45500
|
"Content-Type": "application/json",
|
|
45380
45501
|
Accept: "text/event-stream",
|
|
45381
45502
|
"anthropic-version": "2023-06-01",
|
|
45382
|
-
...auth.headers
|
|
45503
|
+
...auth.headers || {}
|
|
45383
45504
|
};
|
|
45384
45505
|
if (auth.type === "api-key") {
|
|
45385
45506
|
headers["x-api-key"] = auth.token;
|
|
@@ -45399,7 +45520,7 @@ function streamAnthropic(options) {
|
|
|
45399
45520
|
});
|
|
45400
45521
|
} catch (err) {
|
|
45401
45522
|
const isAbort = request.abortSignal?.aborted;
|
|
45402
|
-
const error = new AIError(isAbort ? "Inference request aborted." : `Network request to Anthropic failed: ${err.message}`, {
|
|
45523
|
+
const error = new AIError(isAbort ? "Inference request aborted." : `Network request to Anthropic failed: ${err instanceof Error ? err.message : String(err)}`, {
|
|
45403
45524
|
code: isAbort ? "aborted" : "network",
|
|
45404
45525
|
provider: "anthropic",
|
|
45405
45526
|
cause: err
|
|
@@ -45409,10 +45530,9 @@ function streamAnthropic(options) {
|
|
|
45409
45530
|
return;
|
|
45410
45531
|
}
|
|
45411
45532
|
if (!response.ok) {
|
|
45412
|
-
const errBody = await response.text().catch(() => "");
|
|
45413
45533
|
const isRateLimit = response.status === 429;
|
|
45414
45534
|
const isAuth = response.status === 401 || response.status === 403;
|
|
45415
|
-
const error = new AIError(`Anthropic
|
|
45535
|
+
const error = new AIError(`Anthropic request failed with HTTP status ${response.status}`, {
|
|
45416
45536
|
code: isAuth ? auth.type === "oauth" ? "oauth" : "auth" : isRateLimit ? "rate-limit" : "provider",
|
|
45417
45537
|
provider: "anthropic",
|
|
45418
45538
|
status: response.status
|
|
@@ -45449,69 +45569,91 @@ function streamAnthropic(options) {
|
|
|
45449
45569
|
} catch {
|
|
45450
45570
|
continue;
|
|
45451
45571
|
}
|
|
45452
|
-
const eventType = sse.event || eventData.type;
|
|
45572
|
+
const eventType = typeof sse.event === "string" && sse.event || typeof eventData.type === "string" && eventData.type || "";
|
|
45453
45573
|
switch (eventType) {
|
|
45454
45574
|
case "message_start": {
|
|
45455
|
-
|
|
45456
|
-
|
|
45457
|
-
|
|
45458
|
-
|
|
45459
|
-
|
|
45575
|
+
const msg = eventData.message;
|
|
45576
|
+
if (msg?.usage && typeof msg.usage === "object") {
|
|
45577
|
+
const u = msg.usage;
|
|
45578
|
+
if (typeof u.input_tokens === "number")
|
|
45579
|
+
usage.inputTokens = u.input_tokens;
|
|
45580
|
+
if (typeof u.cache_read_input_tokens === "number")
|
|
45581
|
+
usage.cacheReadTokens = u.cache_read_input_tokens;
|
|
45582
|
+
if (typeof u.cache_creation_input_tokens === "number")
|
|
45583
|
+
usage.cacheWriteTokens = u.cache_creation_input_tokens;
|
|
45460
45584
|
}
|
|
45461
45585
|
break;
|
|
45462
45586
|
}
|
|
45463
45587
|
case "content_block_start": {
|
|
45464
45588
|
const block = eventData.content_block;
|
|
45589
|
+
if (!block || typeof block !== "object" || typeof block.type !== "string") {
|
|
45590
|
+
const error = new AIError("Malformed Anthropic content_block_start event", {
|
|
45591
|
+
code: "parse",
|
|
45592
|
+
provider: "anthropic"
|
|
45593
|
+
});
|
|
45594
|
+
errorResult = error;
|
|
45595
|
+
yield { type: "error", error };
|
|
45596
|
+
return;
|
|
45597
|
+
}
|
|
45465
45598
|
if (block.type === "text") {
|
|
45466
|
-
|
|
45467
|
-
|
|
45468
|
-
|
|
45599
|
+
const blockText = typeof block.text === "string" ? block.text : "";
|
|
45600
|
+
currentBlock = { type: "text", text: blockText };
|
|
45601
|
+
if (blockText) {
|
|
45602
|
+
yield { type: "text-delta", delta: blockText };
|
|
45469
45603
|
}
|
|
45470
45604
|
} else if (block.type === "thinking") {
|
|
45605
|
+
const blockThinking = typeof block.thinking === "string" ? block.thinking : "";
|
|
45606
|
+
const blockSig = typeof block.signature === "string" ? block.signature : undefined;
|
|
45471
45607
|
currentBlock = {
|
|
45472
45608
|
type: "thinking",
|
|
45473
|
-
thinking:
|
|
45474
|
-
signature:
|
|
45609
|
+
thinking: blockThinking,
|
|
45610
|
+
signature: blockSig
|
|
45475
45611
|
};
|
|
45476
|
-
if (
|
|
45477
|
-
yield { type: "reasoning-delta", delta:
|
|
45612
|
+
if (blockThinking) {
|
|
45613
|
+
yield { type: "reasoning-delta", delta: blockThinking };
|
|
45478
45614
|
}
|
|
45479
45615
|
} else if (block.type === "tool_use") {
|
|
45616
|
+
const blockId = typeof block.id === "string" ? block.id : "";
|
|
45617
|
+
const blockName = typeof block.name === "string" ? block.name : "";
|
|
45480
45618
|
currentBlock = {
|
|
45481
45619
|
type: "tool-call",
|
|
45482
|
-
id:
|
|
45483
|
-
name:
|
|
45620
|
+
id: blockId,
|
|
45621
|
+
name: blockName,
|
|
45484
45622
|
rawArgs: ""
|
|
45485
45623
|
};
|
|
45486
|
-
yield { type: "tool-call-start", id:
|
|
45624
|
+
yield { type: "tool-call-start", id: blockId, name: blockName };
|
|
45487
45625
|
}
|
|
45488
45626
|
break;
|
|
45489
45627
|
}
|
|
45490
45628
|
case "content_block_delta": {
|
|
45491
45629
|
const delta = eventData.delta;
|
|
45492
|
-
if (!delta)
|
|
45630
|
+
if (!delta || typeof delta.type !== "string")
|
|
45493
45631
|
break;
|
|
45494
45632
|
if (delta.type === "text_delta") {
|
|
45633
|
+
const deltaText = typeof delta.text === "string" ? delta.text : "";
|
|
45495
45634
|
if (currentBlock && currentBlock.type === "text") {
|
|
45496
|
-
currentBlock.text +=
|
|
45635
|
+
currentBlock.text += deltaText;
|
|
45497
45636
|
}
|
|
45498
|
-
yield { type: "text-delta", delta:
|
|
45637
|
+
yield { type: "text-delta", delta: deltaText };
|
|
45499
45638
|
} else if (delta.type === "thinking_delta") {
|
|
45639
|
+
const deltaThinking = typeof delta.thinking === "string" ? delta.thinking : "";
|
|
45500
45640
|
if (currentBlock && currentBlock.type === "thinking") {
|
|
45501
|
-
currentBlock.thinking +=
|
|
45641
|
+
currentBlock.thinking += deltaThinking;
|
|
45502
45642
|
}
|
|
45503
|
-
yield { type: "reasoning-delta", delta:
|
|
45643
|
+
yield { type: "reasoning-delta", delta: deltaThinking };
|
|
45504
45644
|
} else if (delta.type === "signature_delta") {
|
|
45645
|
+
const deltaSig = typeof delta.signature === "string" ? delta.signature : "";
|
|
45505
45646
|
if (currentBlock && currentBlock.type === "thinking") {
|
|
45506
|
-
currentBlock.signature = (currentBlock.signature || "") +
|
|
45647
|
+
currentBlock.signature = (currentBlock.signature || "") + deltaSig;
|
|
45507
45648
|
}
|
|
45508
45649
|
} else if (delta.type === "input_json_delta") {
|
|
45650
|
+
const partialJson = typeof delta.partial_json === "string" ? delta.partial_json : "";
|
|
45509
45651
|
if (currentBlock && currentBlock.type === "tool-call") {
|
|
45510
|
-
currentBlock.rawArgs +=
|
|
45652
|
+
currentBlock.rawArgs += partialJson;
|
|
45511
45653
|
yield {
|
|
45512
45654
|
type: "tool-call-delta",
|
|
45513
45655
|
id: currentBlock.id,
|
|
45514
|
-
delta:
|
|
45656
|
+
delta: partialJson
|
|
45515
45657
|
};
|
|
45516
45658
|
}
|
|
45517
45659
|
}
|
|
@@ -45546,8 +45688,9 @@ function streamAnthropic(options) {
|
|
|
45546
45688
|
break;
|
|
45547
45689
|
}
|
|
45548
45690
|
case "message_delta": {
|
|
45549
|
-
|
|
45550
|
-
|
|
45691
|
+
const delta = eventData.delta;
|
|
45692
|
+
if (delta && typeof delta.stop_reason === "string") {
|
|
45693
|
+
const sr = delta.stop_reason;
|
|
45551
45694
|
if (sr === "end_turn" || sr === "stop_sequence")
|
|
45552
45695
|
finishReason = "stop";
|
|
45553
45696
|
else if (sr === "max_tokens")
|
|
@@ -45555,8 +45698,9 @@ function streamAnthropic(options) {
|
|
|
45555
45698
|
else if (sr === "tool_use")
|
|
45556
45699
|
finishReason = "tool-use";
|
|
45557
45700
|
}
|
|
45558
|
-
|
|
45559
|
-
|
|
45701
|
+
const u = eventData.usage;
|
|
45702
|
+
if (typeof u?.output_tokens === "number") {
|
|
45703
|
+
usage.outputTokens = u.output_tokens;
|
|
45560
45704
|
}
|
|
45561
45705
|
break;
|
|
45562
45706
|
}
|
|
@@ -45566,7 +45710,8 @@ function streamAnthropic(options) {
|
|
|
45566
45710
|
}
|
|
45567
45711
|
case "error": {
|
|
45568
45712
|
const err = eventData.error;
|
|
45569
|
-
const
|
|
45713
|
+
const errMsg = typeof err?.message === "string" ? err.message : "Unknown provider error";
|
|
45714
|
+
const error = new AIError(`Anthropic stream error: ${errMsg}`, {
|
|
45570
45715
|
code: "provider",
|
|
45571
45716
|
provider: "anthropic"
|
|
45572
45717
|
});
|
|
@@ -45603,7 +45748,7 @@ function streamAnthropic(options) {
|
|
|
45603
45748
|
};
|
|
45604
45749
|
} catch (err) {
|
|
45605
45750
|
const isAbort = request.abortSignal?.aborted;
|
|
45606
|
-
const error = new AIError(isAbort ? "Inference request aborted." : `Stream reading failed: ${err.message}`, {
|
|
45751
|
+
const error = new AIError(isAbort ? "Inference request aborted." : `Stream reading failed: ${err instanceof Error ? err.message : String(err)}`, {
|
|
45607
45752
|
code: isAbort ? "aborted" : "provider",
|
|
45608
45753
|
provider: "anthropic",
|
|
45609
45754
|
cause: err
|
|
@@ -45661,7 +45806,7 @@ function sanitizeSchema(schema) {
|
|
|
45661
45806
|
function mapGeminiEffort(effort) {
|
|
45662
45807
|
switch (effort) {
|
|
45663
45808
|
case "none":
|
|
45664
|
-
return
|
|
45809
|
+
return;
|
|
45665
45810
|
case "low":
|
|
45666
45811
|
return { thinkingBudget: 2048 };
|
|
45667
45812
|
case "medium":
|
|
@@ -45677,13 +45822,14 @@ function convertMessages3(messages) {
|
|
|
45677
45822
|
const contents = [];
|
|
45678
45823
|
for (const msg of messages) {
|
|
45679
45824
|
if (msg.role === "system") {
|
|
45825
|
+
const sanitized = sanitizeSurrogates(msg.content);
|
|
45680
45826
|
systemText = systemText ? `${systemText}
|
|
45681
45827
|
|
|
45682
|
-
${
|
|
45828
|
+
${sanitized}` : sanitized;
|
|
45683
45829
|
continue;
|
|
45684
45830
|
}
|
|
45685
45831
|
if (msg.role === "user") {
|
|
45686
|
-
const text = typeof msg.content === "string" ? msg.content : msg.content.map((c) => c.text).join(`
|
|
45832
|
+
const text = typeof msg.content === "string" ? sanitizeSurrogates(msg.content) : msg.content.map((c) => sanitizeSurrogates(c.text)).join(`
|
|
45687
45833
|
`);
|
|
45688
45834
|
contents.push({
|
|
45689
45835
|
role: "user",
|
|
@@ -45695,19 +45841,19 @@ ${msg.content}` : msg.content;
|
|
|
45695
45841
|
const parts = [];
|
|
45696
45842
|
for (const block of msg.content) {
|
|
45697
45843
|
if (block.type === "text") {
|
|
45698
|
-
if (!block.text && !block.
|
|
45844
|
+
if (!block.text && !block.textSignature)
|
|
45699
45845
|
continue;
|
|
45700
45846
|
parts.push({
|
|
45701
|
-
text: block.text || "",
|
|
45702
|
-
...block.
|
|
45847
|
+
text: sanitizeSurrogates(block.text || ""),
|
|
45848
|
+
...block.textSignature ? { thoughtSignature: block.textSignature } : {}
|
|
45703
45849
|
});
|
|
45704
45850
|
} else if (block.type === "thinking") {
|
|
45705
|
-
if (!block.thinking && !block.
|
|
45851
|
+
if (!block.thinking && !block.thinkingSignature)
|
|
45706
45852
|
continue;
|
|
45707
45853
|
parts.push({
|
|
45708
45854
|
thought: true,
|
|
45709
|
-
text: block.thinking || "",
|
|
45710
|
-
...block.
|
|
45855
|
+
text: sanitizeSurrogates(block.thinking || ""),
|
|
45856
|
+
...block.thinkingSignature ? { thoughtSignature: block.thinkingSignature } : {}
|
|
45711
45857
|
});
|
|
45712
45858
|
} else if (block.type === "tool-call") {
|
|
45713
45859
|
parts.push({
|
|
@@ -45734,7 +45880,7 @@ ${msg.content}` : msg.content;
|
|
|
45734
45880
|
functionResponse: {
|
|
45735
45881
|
name: res.toolName,
|
|
45736
45882
|
response: {
|
|
45737
|
-
result: res.output
|
|
45883
|
+
result: sanitizeSurrogates(res.output)
|
|
45738
45884
|
}
|
|
45739
45885
|
}
|
|
45740
45886
|
});
|
|
@@ -45783,7 +45929,15 @@ function streamGemini(options) {
|
|
|
45783
45929
|
body.tools = tools;
|
|
45784
45930
|
}
|
|
45785
45931
|
const modelId = request.model.modelId.replace(/^models\//, "");
|
|
45786
|
-
const url = `https://generativelanguage.googleapis.com/v1beta/models/${modelId}:streamGenerateContent?alt=sse
|
|
45932
|
+
const url = `https://generativelanguage.googleapis.com/v1beta/models/${modelId}:streamGenerateContent?alt=sse`;
|
|
45933
|
+
const headers = {
|
|
45934
|
+
"Content-Type": "application/json",
|
|
45935
|
+
Accept: "text/event-stream",
|
|
45936
|
+
...auth.headers || {}
|
|
45937
|
+
};
|
|
45938
|
+
if (auth.token && auth.token !== "none") {
|
|
45939
|
+
headers["x-goog-api-key"] = auth.token;
|
|
45940
|
+
}
|
|
45787
45941
|
let finalResult;
|
|
45788
45942
|
let errorResult;
|
|
45789
45943
|
async function* eventGenerator() {
|
|
@@ -45791,16 +45945,13 @@ function streamGemini(options) {
|
|
|
45791
45945
|
try {
|
|
45792
45946
|
response = await fetch(url, {
|
|
45793
45947
|
method: "POST",
|
|
45794
|
-
headers
|
|
45795
|
-
"Content-Type": "application/json",
|
|
45796
|
-
Accept: "text/event-stream"
|
|
45797
|
-
},
|
|
45948
|
+
headers,
|
|
45798
45949
|
body: JSON.stringify(body),
|
|
45799
45950
|
signal: request.abortSignal
|
|
45800
45951
|
});
|
|
45801
45952
|
} catch (err) {
|
|
45802
45953
|
const isAbort = request.abortSignal?.aborted;
|
|
45803
|
-
const error = new AIError(isAbort ? "Inference request aborted." : `Network request to Gemini failed: ${err.message}`, {
|
|
45954
|
+
const error = new AIError(isAbort ? "Inference request aborted." : `Network request to Gemini failed: ${err instanceof Error ? err.message : String(err)}`, {
|
|
45804
45955
|
code: isAbort ? "aborted" : "network",
|
|
45805
45956
|
provider: "gemini",
|
|
45806
45957
|
cause: err
|
|
@@ -45810,10 +45961,9 @@ function streamGemini(options) {
|
|
|
45810
45961
|
return;
|
|
45811
45962
|
}
|
|
45812
45963
|
if (!response.ok) {
|
|
45813
|
-
const errBody = await response.text().catch(() => "");
|
|
45814
45964
|
const isRateLimit = response.status === 429;
|
|
45815
45965
|
const isAuth = response.status === 401 || response.status === 403;
|
|
45816
|
-
const error = new AIError(`Gemini
|
|
45966
|
+
const error = new AIError(`Gemini request failed with HTTP status ${response.status}`, {
|
|
45817
45967
|
code: isAuth ? "auth" : isRateLimit ? "rate-limit" : "provider",
|
|
45818
45968
|
provider: "gemini",
|
|
45819
45969
|
status: response.status
|
|
@@ -45849,15 +45999,19 @@ function streamGemini(options) {
|
|
|
45849
45999
|
} catch {
|
|
45850
46000
|
continue;
|
|
45851
46001
|
}
|
|
45852
|
-
if (eventData.usageMetadata) {
|
|
46002
|
+
if (eventData.usageMetadata && typeof eventData.usageMetadata === "object") {
|
|
45853
46003
|
const u = eventData.usageMetadata;
|
|
45854
|
-
|
|
45855
|
-
|
|
45856
|
-
|
|
45857
|
-
|
|
45858
|
-
|
|
46004
|
+
if (typeof u.promptTokenCount === "number")
|
|
46005
|
+
usage.inputTokens = u.promptTokenCount;
|
|
46006
|
+
if (typeof u.candidatesTokenCount === "number")
|
|
46007
|
+
usage.outputTokens = u.candidatesTokenCount;
|
|
46008
|
+
if (typeof u.totalTokenCount === "number")
|
|
46009
|
+
usage.totalTokens = u.totalTokenCount;
|
|
46010
|
+
}
|
|
46011
|
+
const candidates = eventData.candidates;
|
|
46012
|
+
const candidate = candidates?.[0];
|
|
45859
46013
|
if (candidate) {
|
|
45860
|
-
if (candidate.finishReason) {
|
|
46014
|
+
if (typeof candidate.finishReason === "string") {
|
|
45861
46015
|
const fr = candidate.finishReason;
|
|
45862
46016
|
if (fr === "STOP")
|
|
45863
46017
|
finishReason = "stop";
|
|
@@ -45866,21 +46020,25 @@ function streamGemini(options) {
|
|
|
45866
46020
|
else if (fr === "SAFETY")
|
|
45867
46021
|
finishReason = "error";
|
|
45868
46022
|
}
|
|
45869
|
-
|
|
45870
|
-
|
|
46023
|
+
const content = candidate.content;
|
|
46024
|
+
if (Array.isArray(content?.parts)) {
|
|
46025
|
+
for (const rawPart of content.parts) {
|
|
46026
|
+
const part = rawPart;
|
|
46027
|
+
const thoughtSig = typeof part.thoughtSignature === "string" ? part.thoughtSignature : undefined;
|
|
45871
46028
|
if (part.thought) {
|
|
45872
|
-
const thoughtDelta = part.text
|
|
45873
|
-
if (thoughtDelta ||
|
|
45874
|
-
const
|
|
45875
|
-
if (
|
|
45876
|
-
|
|
45877
|
-
if (
|
|
45878
|
-
|
|
46029
|
+
const thoughtDelta = typeof part.text === "string" ? part.text : "";
|
|
46030
|
+
if (thoughtDelta || thoughtSig) {
|
|
46031
|
+
const lastBlock = assistantContent[assistantContent.length - 1];
|
|
46032
|
+
if (lastBlock && lastBlock.type === "thinking") {
|
|
46033
|
+
lastBlock.thinking += thoughtDelta;
|
|
46034
|
+
if (thoughtSig) {
|
|
46035
|
+
lastBlock.thinkingSignature = thoughtSig;
|
|
46036
|
+
}
|
|
45879
46037
|
} else {
|
|
45880
46038
|
assistantContent.push({
|
|
45881
46039
|
type: "thinking",
|
|
45882
46040
|
thinking: thoughtDelta,
|
|
45883
|
-
|
|
46041
|
+
...thoughtSig ? { thinkingSignature: thoughtSig } : {}
|
|
45884
46042
|
});
|
|
45885
46043
|
}
|
|
45886
46044
|
if (thoughtDelta) {
|
|
@@ -45888,33 +46046,37 @@ function streamGemini(options) {
|
|
|
45888
46046
|
}
|
|
45889
46047
|
}
|
|
45890
46048
|
} else if (part.text !== undefined) {
|
|
45891
|
-
const textDelta = part.text
|
|
45892
|
-
if (textDelta ||
|
|
45893
|
-
const
|
|
45894
|
-
if (
|
|
45895
|
-
|
|
45896
|
-
if (
|
|
45897
|
-
|
|
46049
|
+
const textDelta = typeof part.text === "string" ? part.text : "";
|
|
46050
|
+
if (textDelta || thoughtSig) {
|
|
46051
|
+
const lastBlock = assistantContent[assistantContent.length - 1];
|
|
46052
|
+
if (lastBlock && lastBlock.type === "text") {
|
|
46053
|
+
lastBlock.text += textDelta;
|
|
46054
|
+
if (thoughtSig) {
|
|
46055
|
+
lastBlock.textSignature = thoughtSig;
|
|
46056
|
+
}
|
|
45898
46057
|
} else {
|
|
45899
46058
|
assistantContent.push({
|
|
45900
46059
|
type: "text",
|
|
45901
46060
|
text: textDelta,
|
|
45902
|
-
|
|
46061
|
+
...thoughtSig ? { textSignature: thoughtSig } : {}
|
|
45903
46062
|
});
|
|
45904
46063
|
}
|
|
45905
46064
|
if (textDelta) {
|
|
45906
46065
|
yield { type: "text-delta", delta: textDelta };
|
|
45907
46066
|
}
|
|
45908
46067
|
}
|
|
45909
|
-
} else if (part.functionCall) {
|
|
46068
|
+
} else if (part.functionCall && typeof part.functionCall === "object") {
|
|
46069
|
+
const fc = part.functionCall;
|
|
45910
46070
|
toolCallIdx++;
|
|
45911
|
-
const toolId = `call_${
|
|
46071
|
+
const toolId = `call_${toolCallIdx}`;
|
|
46072
|
+
const fnName = typeof fc.name === "string" ? fc.name : "";
|
|
46073
|
+
const fnArgs = typeof fc.args === "object" && fc.args !== null ? fc.args : {};
|
|
45912
46074
|
const toolCall = {
|
|
45913
46075
|
type: "tool-call",
|
|
45914
46076
|
id: toolId,
|
|
45915
|
-
name:
|
|
45916
|
-
arguments:
|
|
45917
|
-
thoughtSignature:
|
|
46077
|
+
name: fnName,
|
|
46078
|
+
arguments: fnArgs,
|
|
46079
|
+
...thoughtSig ? { thoughtSignature: thoughtSig } : {}
|
|
45918
46080
|
};
|
|
45919
46081
|
assistantContent.push(toolCall);
|
|
45920
46082
|
finishReason = "tool-use";
|
|
@@ -45947,7 +46109,7 @@ function streamGemini(options) {
|
|
|
45947
46109
|
};
|
|
45948
46110
|
} catch (err) {
|
|
45949
46111
|
const isAbort = request.abortSignal?.aborted;
|
|
45950
|
-
const error = new AIError(isAbort ? "Inference request aborted." : `Gemini stream reading failed: ${err.message}`, {
|
|
46112
|
+
const error = new AIError(isAbort ? "Inference request aborted." : `Gemini stream reading failed: ${err instanceof Error ? err.message : String(err)}`, {
|
|
45951
46113
|
code: isAbort ? "aborted" : "provider",
|
|
45952
46114
|
provider: "gemini",
|
|
45953
46115
|
cause: err
|
|
@@ -45977,8 +46139,16 @@ function streamGemini(options) {
|
|
|
45977
46139
|
// src/packages/ai/src/inference.ts
|
|
45978
46140
|
function streamInference(request, authManager = createAuthManager(), customBaseUrl) {
|
|
45979
46141
|
const provider = request.model.provider;
|
|
45980
|
-
let streamPromise = null;
|
|
45981
46142
|
const getStream = async () => {
|
|
46143
|
+
let descriptor;
|
|
46144
|
+
try {
|
|
46145
|
+
descriptor = getProviderDescriptor(provider);
|
|
46146
|
+
} catch {
|
|
46147
|
+
throw new AIError(`Unsupported provider: "${provider}"`, {
|
|
46148
|
+
code: "invalid-request",
|
|
46149
|
+
provider
|
|
46150
|
+
});
|
|
46151
|
+
}
|
|
45982
46152
|
const resolvedAuth = await authManager.resolve(provider, request.abortSignal);
|
|
45983
46153
|
if (!resolvedAuth && provider !== "custom" && provider !== "ollama") {
|
|
45984
46154
|
throw new AIError(`No credentials configured for provider: ${provider}`, {
|
|
@@ -45991,30 +46161,28 @@ function streamInference(request, authManager = createAuthManager(), customBaseU
|
|
|
45991
46161
|
token: "none",
|
|
45992
46162
|
source: "custom"
|
|
45993
46163
|
};
|
|
45994
|
-
switch (
|
|
45995
|
-
case "anthropic":
|
|
46164
|
+
switch (descriptor.protocol) {
|
|
46165
|
+
case "anthropic-messages":
|
|
45996
46166
|
return streamAnthropic({ request, auth });
|
|
45997
46167
|
case "gemini":
|
|
45998
46168
|
return streamGemini({ request, auth });
|
|
45999
|
-
case "openai":
|
|
46169
|
+
case "openai-chat":
|
|
46000
46170
|
return streamOpenAI({ request, auth });
|
|
46001
|
-
case "
|
|
46002
|
-
case "openrouter":
|
|
46003
|
-
case "github-copilot":
|
|
46004
|
-
case "groq":
|
|
46005
|
-
case "xai":
|
|
46006
|
-
case "mistral":
|
|
46007
|
-
case "ollama":
|
|
46008
|
-
case "custom":
|
|
46171
|
+
case "openai-compatible":
|
|
46009
46172
|
return streamOpenAICompatible({ request, auth, customBaseUrl });
|
|
46173
|
+
case "openai-responses":
|
|
46174
|
+
throw new AIError(`Protocol "openai-responses" is not yet implemented for provider "${provider}"`, {
|
|
46175
|
+
code: "invalid-request",
|
|
46176
|
+
provider
|
|
46177
|
+
});
|
|
46010
46178
|
default:
|
|
46011
|
-
throw new AIError(`Unsupported provider
|
|
46179
|
+
throw new AIError(`Unsupported protocol "${descriptor.protocol}" for provider "${provider}"`, {
|
|
46012
46180
|
code: "invalid-request",
|
|
46013
46181
|
provider
|
|
46014
46182
|
});
|
|
46015
46183
|
}
|
|
46016
46184
|
};
|
|
46017
|
-
streamPromise = getStream();
|
|
46185
|
+
const streamPromise = getStream();
|
|
46018
46186
|
return {
|
|
46019
46187
|
async* [Symbol.asyncIterator]() {
|
|
46020
46188
|
const activeStream = await streamPromise;
|
|
@@ -46058,85 +46226,91 @@ function createAIEngine(options) {
|
|
|
46058
46226
|
// src/packages/ai/src/models/discovery.ts
|
|
46059
46227
|
var NON_CHAT_MODEL_KEYWORDS = /(embed|similarity|search-document|dall-e|imagen|veo|whisper|tts|transcribe|speech|voice|audio|image|video|moderation|rerank|robotics|live|aqa|babbage|davinci|lyria|chirp)/i;
|
|
46060
46228
|
var DEFAULT_FETCH_TIMEOUT_MS = 1e4;
|
|
46061
|
-
async function
|
|
46062
|
-
const
|
|
46229
|
+
async function fetchStandardModelsList(options) {
|
|
46230
|
+
const { provider, url, headers, timeoutMs, transformId, filterItem } = options;
|
|
46231
|
+
const response = await fetch(url, {
|
|
46063
46232
|
method: "GET",
|
|
46064
|
-
headers
|
|
46065
|
-
Authorization: `Bearer ${apiKey}`
|
|
46066
|
-
},
|
|
46233
|
+
headers,
|
|
46067
46234
|
signal: AbortSignal.timeout(timeoutMs)
|
|
46068
46235
|
});
|
|
46069
46236
|
if (!response.ok) {
|
|
46070
|
-
throw new Error(
|
|
46237
|
+
throw new Error(`${provider} API request failed: HTTP ${response.status} (${response.statusText})`);
|
|
46071
46238
|
}
|
|
46072
46239
|
const payload = await response.json();
|
|
46073
46240
|
if (!Array.isArray(payload.data)) {
|
|
46074
46241
|
return [];
|
|
46075
46242
|
}
|
|
46076
|
-
|
|
46243
|
+
const models = [];
|
|
46244
|
+
for (const item of payload.data) {
|
|
46245
|
+
if (!item || typeof item.id !== "string")
|
|
46246
|
+
continue;
|
|
46247
|
+
if (filterItem && !filterItem(item))
|
|
46248
|
+
continue;
|
|
46249
|
+
const finalId = transformId ? transformId(item.id) : item.id;
|
|
46250
|
+
if (!finalId || NON_CHAT_MODEL_KEYWORDS.test(finalId))
|
|
46251
|
+
continue;
|
|
46252
|
+
models.push({
|
|
46253
|
+
provider,
|
|
46254
|
+
modelId: finalId
|
|
46255
|
+
});
|
|
46256
|
+
}
|
|
46257
|
+
return models.sort((a, b) => a.modelId.localeCompare(b.modelId));
|
|
46258
|
+
}
|
|
46259
|
+
async function fetchOpenAIModels(apiKey, timeoutMs = DEFAULT_FETCH_TIMEOUT_MS) {
|
|
46260
|
+
return await fetchStandardModelsList({
|
|
46077
46261
|
provider: "openai",
|
|
46078
|
-
|
|
46079
|
-
|
|
46262
|
+
url: "https://api.openai.com/v1/models",
|
|
46263
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
46264
|
+
timeoutMs
|
|
46265
|
+
});
|
|
46080
46266
|
}
|
|
46081
46267
|
async function fetchGeminiModels(apiKey, timeoutMs = DEFAULT_FETCH_TIMEOUT_MS) {
|
|
46082
|
-
|
|
46083
|
-
method: "GET",
|
|
46084
|
-
headers: {
|
|
46085
|
-
Authorization: `Bearer ${apiKey}`
|
|
46086
|
-
},
|
|
46087
|
-
signal: AbortSignal.timeout(timeoutMs)
|
|
46088
|
-
});
|
|
46089
|
-
if (!response.ok) {
|
|
46090
|
-
throw new Error(`Gemini API request failed: HTTP ${response.status} (${response.statusText})`);
|
|
46091
|
-
}
|
|
46092
|
-
const payload = await response.json();
|
|
46093
|
-
if (!Array.isArray(payload.data)) {
|
|
46094
|
-
return [];
|
|
46095
|
-
}
|
|
46096
|
-
return payload.data.map((item) => typeof item.id === "string" ? item.id.replace(/^models\//, "") : "").filter((id) => id && !NON_CHAT_MODEL_KEYWORDS.test(id)).map((id) => ({
|
|
46268
|
+
return await fetchStandardModelsList({
|
|
46097
46269
|
provider: "gemini",
|
|
46098
|
-
|
|
46099
|
-
|
|
46270
|
+
url: "https://generativelanguage.googleapis.com/v1beta/openai/models",
|
|
46271
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
46272
|
+
timeoutMs,
|
|
46273
|
+
transformId: (id) => id.replace(/^models\//, "")
|
|
46274
|
+
});
|
|
46100
46275
|
}
|
|
46101
46276
|
async function fetchAnthropicModels(apiKey, timeoutMs = DEFAULT_FETCH_TIMEOUT_MS) {
|
|
46102
|
-
|
|
46103
|
-
|
|
46277
|
+
return await fetchStandardModelsList({
|
|
46278
|
+
provider: "anthropic",
|
|
46279
|
+
url: "https://api.anthropic.com/v1/models",
|
|
46104
46280
|
headers: {
|
|
46105
46281
|
"x-api-key": apiKey,
|
|
46106
46282
|
"anthropic-version": "2023-06-01"
|
|
46107
46283
|
},
|
|
46108
|
-
|
|
46284
|
+
timeoutMs
|
|
46109
46285
|
});
|
|
46110
|
-
if (!response.ok) {
|
|
46111
|
-
throw new Error(`Anthropic API request failed: HTTP ${response.status} (${response.statusText})`);
|
|
46112
|
-
}
|
|
46113
|
-
const payload = await response.json();
|
|
46114
|
-
if (!Array.isArray(payload.data)) {
|
|
46115
|
-
return [];
|
|
46116
|
-
}
|
|
46117
|
-
return payload.data.filter((item) => typeof item.id === "string" && !NON_CHAT_MODEL_KEYWORDS.test(item.id)).map((item) => ({
|
|
46118
|
-
provider: "anthropic",
|
|
46119
|
-
modelId: item.id
|
|
46120
|
-
})).sort((a, b) => a.modelId.localeCompare(b.modelId));
|
|
46121
46286
|
}
|
|
46122
46287
|
async function fetchDeepSeekModels(apiKey, timeoutMs = DEFAULT_FETCH_TIMEOUT_MS) {
|
|
46123
|
-
|
|
46124
|
-
|
|
46288
|
+
return await fetchStandardModelsList({
|
|
46289
|
+
provider: "deepseek",
|
|
46290
|
+
url: "https://api.deepseek.com/models",
|
|
46125
46291
|
headers: { Authorization: `Bearer ${apiKey}` },
|
|
46126
|
-
|
|
46292
|
+
timeoutMs
|
|
46127
46293
|
});
|
|
46128
|
-
|
|
46129
|
-
|
|
46294
|
+
}
|
|
46295
|
+
function isValidOpenRouterNextUrl(rawUrl) {
|
|
46296
|
+
try {
|
|
46297
|
+
const parsed = new URL(rawUrl, "https://openrouter.ai");
|
|
46298
|
+
return parsed.protocol === "https:" && parsed.origin === "https://openrouter.ai" && parsed.pathname.startsWith("/api/v1/models");
|
|
46299
|
+
} catch {
|
|
46300
|
+
return false;
|
|
46130
46301
|
}
|
|
46131
|
-
const payload = await response.json();
|
|
46132
|
-
if (!Array.isArray(payload.data))
|
|
46133
|
-
return [];
|
|
46134
|
-
return payload.data.filter((item) => typeof item.id === "string" && !NON_CHAT_MODEL_KEYWORDS.test(item.id)).map((item) => ({ provider: "deepseek", modelId: item.id })).sort((a, b) => a.modelId.localeCompare(b.modelId));
|
|
46135
46302
|
}
|
|
46136
46303
|
async function fetchOpenRouterModels(apiKey, timeoutMs = DEFAULT_FETCH_TIMEOUT_MS) {
|
|
46137
46304
|
const results = [];
|
|
46138
46305
|
let url = "https://openrouter.ai/api/v1/models";
|
|
46139
|
-
|
|
46306
|
+
const seenUrls = new Set;
|
|
46307
|
+
const MAX_PAGES = 20;
|
|
46308
|
+
let pageCount = 0;
|
|
46309
|
+
while (url && pageCount < MAX_PAGES) {
|
|
46310
|
+
if (seenUrls.has(url))
|
|
46311
|
+
break;
|
|
46312
|
+
seenUrls.add(url);
|
|
46313
|
+
pageCount++;
|
|
46140
46314
|
const response = await fetch(url, {
|
|
46141
46315
|
method: "GET",
|
|
46142
46316
|
headers: { Authorization: `Bearer ${apiKey}` },
|
|
@@ -46148,7 +46322,7 @@ async function fetchOpenRouterModels(apiKey, timeoutMs = DEFAULT_FETCH_TIMEOUT_M
|
|
|
46148
46322
|
const payload = await response.json();
|
|
46149
46323
|
if (Array.isArray(payload.data)) {
|
|
46150
46324
|
for (const item of payload.data) {
|
|
46151
|
-
if (typeof item.id !== "string")
|
|
46325
|
+
if (!item || typeof item.id !== "string")
|
|
46152
46326
|
continue;
|
|
46153
46327
|
const hasTextModality = item.architecture?.output_modalities?.includes("text");
|
|
46154
46328
|
if (hasTextModality === false)
|
|
@@ -46158,70 +46332,51 @@ async function fetchOpenRouterModels(apiKey, timeoutMs = DEFAULT_FETCH_TIMEOUT_M
|
|
|
46158
46332
|
}
|
|
46159
46333
|
}
|
|
46160
46334
|
}
|
|
46161
|
-
const next = payload.links?.next
|
|
46162
|
-
|
|
46335
|
+
const next = payload.links?.next;
|
|
46336
|
+
if (typeof next === "string" && next.trim() && isValidOpenRouterNextUrl(next)) {
|
|
46337
|
+
url = new URL(next, "https://openrouter.ai").toString();
|
|
46338
|
+
} else {
|
|
46339
|
+
url = null;
|
|
46340
|
+
}
|
|
46163
46341
|
}
|
|
46164
46342
|
return results.sort((a, b) => a.modelId.localeCompare(b.modelId));
|
|
46165
46343
|
}
|
|
46166
46344
|
async function fetchGroqModels(apiKey, timeoutMs = DEFAULT_FETCH_TIMEOUT_MS) {
|
|
46167
|
-
|
|
46168
|
-
|
|
46345
|
+
return await fetchStandardModelsList({
|
|
46346
|
+
provider: "groq",
|
|
46347
|
+
url: "https://api.groq.com/openai/v1/models",
|
|
46169
46348
|
headers: { Authorization: `Bearer ${apiKey}` },
|
|
46170
|
-
|
|
46349
|
+
timeoutMs
|
|
46171
46350
|
});
|
|
46172
|
-
if (!response.ok) {
|
|
46173
|
-
throw new Error(`Groq API request failed: HTTP ${response.status} (${response.statusText})`);
|
|
46174
|
-
}
|
|
46175
|
-
const payload = await response.json();
|
|
46176
|
-
if (!Array.isArray(payload.data))
|
|
46177
|
-
return [];
|
|
46178
|
-
return payload.data.filter((item) => typeof item.id === "string" && !NON_CHAT_MODEL_KEYWORDS.test(item.id)).map((item) => ({ provider: "groq", modelId: item.id })).sort((a, b) => a.modelId.localeCompare(b.modelId));
|
|
46179
46351
|
}
|
|
46180
46352
|
async function fetchXAIModels(apiKey, timeoutMs = DEFAULT_FETCH_TIMEOUT_MS) {
|
|
46181
|
-
|
|
46182
|
-
|
|
46353
|
+
return await fetchStandardModelsList({
|
|
46354
|
+
provider: "xai",
|
|
46355
|
+
url: "https://api.x.ai/v1/models",
|
|
46183
46356
|
headers: { Authorization: `Bearer ${apiKey}` },
|
|
46184
|
-
|
|
46357
|
+
timeoutMs
|
|
46185
46358
|
});
|
|
46186
|
-
if (!response.ok) {
|
|
46187
|
-
throw new Error(`xAI API request failed: HTTP ${response.status} (${response.statusText})`);
|
|
46188
|
-
}
|
|
46189
|
-
const payload = await response.json();
|
|
46190
|
-
if (!Array.isArray(payload.data))
|
|
46191
|
-
return [];
|
|
46192
|
-
return payload.data.filter((item) => typeof item.id === "string" && !NON_CHAT_MODEL_KEYWORDS.test(item.id)).map((item) => ({ provider: "xai", modelId: item.id })).sort((a, b) => a.modelId.localeCompare(b.modelId));
|
|
46193
46359
|
}
|
|
46194
46360
|
async function fetchMistralModels(apiKey, timeoutMs = DEFAULT_FETCH_TIMEOUT_MS) {
|
|
46195
|
-
|
|
46196
|
-
|
|
46361
|
+
return await fetchStandardModelsList({
|
|
46362
|
+
provider: "mistral",
|
|
46363
|
+
url: "https://api.mistral.ai/v1/models",
|
|
46197
46364
|
headers: { Authorization: `Bearer ${apiKey}` },
|
|
46198
|
-
|
|
46365
|
+
timeoutMs
|
|
46199
46366
|
});
|
|
46200
|
-
if (!response.ok) {
|
|
46201
|
-
throw new Error(`Mistral API request failed: HTTP ${response.status} (${response.statusText})`);
|
|
46202
|
-
}
|
|
46203
|
-
const payload = await response.json();
|
|
46204
|
-
if (!Array.isArray(payload.data))
|
|
46205
|
-
return [];
|
|
46206
|
-
return payload.data.filter((item) => typeof item.id === "string" && !NON_CHAT_MODEL_KEYWORDS.test(item.id)).map((item) => ({ provider: "mistral", modelId: item.id })).sort((a, b) => a.modelId.localeCompare(b.modelId));
|
|
46207
46367
|
}
|
|
46208
46368
|
async function fetchOllamaModels(baseUrl = "http://localhost:11434/v1", timeoutMs = 3000) {
|
|
46209
|
-
const endpoint = baseUrl.replace(/\/+$/, "")
|
|
46210
|
-
|
|
46211
|
-
|
|
46212
|
-
|
|
46369
|
+
const endpoint = `${baseUrl.replace(/\/+$/, "")}/models`;
|
|
46370
|
+
return await fetchStandardModelsList({
|
|
46371
|
+
provider: "ollama",
|
|
46372
|
+
url: endpoint,
|
|
46373
|
+
timeoutMs
|
|
46213
46374
|
});
|
|
46214
|
-
if (!response.ok) {
|
|
46215
|
-
throw new Error(`Ollama API request failed: HTTP ${response.status} (${response.statusText})`);
|
|
46216
|
-
}
|
|
46217
|
-
const payload = await response.json();
|
|
46218
|
-
if (!Array.isArray(payload.data))
|
|
46219
|
-
return [];
|
|
46220
|
-
return payload.data.filter((item) => typeof item.id === "string" && !NON_CHAT_MODEL_KEYWORDS.test(item.id)).map((item) => ({ provider: "ollama", modelId: item.id })).sort((a, b) => a.modelId.localeCompare(b.modelId));
|
|
46221
46375
|
}
|
|
46222
46376
|
async function fetchGitHubCopilotModels(apiKey, timeoutMs = DEFAULT_FETCH_TIMEOUT_MS) {
|
|
46223
|
-
|
|
46224
|
-
|
|
46377
|
+
return await fetchStandardModelsList({
|
|
46378
|
+
provider: "github-copilot",
|
|
46379
|
+
url: "https://api.individual.githubcopilot.com/models",
|
|
46225
46380
|
headers: {
|
|
46226
46381
|
Accept: "application/json",
|
|
46227
46382
|
Authorization: `Bearer ${apiKey}`,
|
|
@@ -46231,16 +46386,21 @@ async function fetchGitHubCopilotModels(apiKey, timeoutMs = DEFAULT_FETCH_TIMEOU
|
|
|
46231
46386
|
"Copilot-Integration-Id": "vscode-chat",
|
|
46232
46387
|
"X-GitHub-Api-Version": "2026-06-01"
|
|
46233
46388
|
},
|
|
46234
|
-
|
|
46389
|
+
timeoutMs,
|
|
46390
|
+
filterItem: (item) => item.capabilities?.supports?.tool_calls !== false
|
|
46235
46391
|
});
|
|
46236
|
-
if (!response.ok) {
|
|
46237
|
-
throw new Error(`GitHub Copilot API request failed: HTTP ${response.status} (${response.statusText})`);
|
|
46238
|
-
}
|
|
46239
|
-
const payload = await response.json();
|
|
46240
|
-
if (!Array.isArray(payload.data))
|
|
46241
|
-
return [];
|
|
46242
|
-
return payload.data.filter((item) => typeof item.id === "string" && !NON_CHAT_MODEL_KEYWORDS.test(item.id) && item.capabilities?.supports?.tool_calls !== false).map((item) => ({ provider: "github-copilot", modelId: item.id })).sort((a, b) => a.modelId.localeCompare(b.modelId));
|
|
46243
46392
|
}
|
|
46393
|
+
var API_KEY_FETCHERS = {
|
|
46394
|
+
openai: fetchOpenAIModels,
|
|
46395
|
+
gemini: fetchGeminiModels,
|
|
46396
|
+
anthropic: fetchAnthropicModels,
|
|
46397
|
+
deepseek: fetchDeepSeekModels,
|
|
46398
|
+
openrouter: fetchOpenRouterModels,
|
|
46399
|
+
"github-copilot": fetchGitHubCopilotModels,
|
|
46400
|
+
groq: fetchGroqModels,
|
|
46401
|
+
xai: fetchXAIModels,
|
|
46402
|
+
mistral: fetchMistralModels
|
|
46403
|
+
};
|
|
46244
46404
|
async function fetchAvailableModels(authCtx, timeoutMs = DEFAULT_FETCH_TIMEOUT_MS) {
|
|
46245
46405
|
const result = {
|
|
46246
46406
|
models: [],
|
|
@@ -46259,152 +46419,30 @@ async function fetchAvailableModels(authCtx, timeoutMs = DEFAULT_FETCH_TIMEOUT_M
|
|
|
46259
46419
|
}
|
|
46260
46420
|
};
|
|
46261
46421
|
const tasks = [];
|
|
46262
|
-
const
|
|
46263
|
-
|
|
46264
|
-
|
|
46265
|
-
|
|
46266
|
-
|
|
46267
|
-
|
|
46268
|
-
|
|
46269
|
-
|
|
46270
|
-
|
|
46271
|
-
|
|
46272
|
-
|
|
46273
|
-
|
|
46274
|
-
|
|
46275
|
-
|
|
46276
|
-
|
|
46277
|
-
|
|
46278
|
-
|
|
46279
|
-
|
|
46280
|
-
|
|
46281
|
-
|
|
46282
|
-
result.providers.gemini = {
|
|
46283
|
-
status: "available",
|
|
46284
|
-
modelCount: models.length
|
|
46285
|
-
};
|
|
46286
|
-
}).catch((err) => {
|
|
46287
|
-
result.providers.gemini = {
|
|
46288
|
-
status: "error",
|
|
46289
|
-
modelCount: 0,
|
|
46290
|
-
error: err instanceof Error ? err.message : String(err)
|
|
46291
|
-
};
|
|
46292
|
-
}));
|
|
46293
|
-
}
|
|
46294
|
-
const anthropicKey = await authCtx.getApiKey("anthropic");
|
|
46295
|
-
if (anthropicKey) {
|
|
46296
|
-
tasks.push(fetchAnthropicModels(anthropicKey, timeoutMs).then((models) => {
|
|
46297
|
-
result.models.push(...models);
|
|
46298
|
-
result.providers.anthropic = {
|
|
46299
|
-
status: "available",
|
|
46300
|
-
modelCount: models.length
|
|
46301
|
-
};
|
|
46302
|
-
}).catch((err) => {
|
|
46303
|
-
result.providers.anthropic = {
|
|
46304
|
-
status: "error",
|
|
46305
|
-
modelCount: 0,
|
|
46306
|
-
error: err instanceof Error ? err.message : String(err)
|
|
46307
|
-
};
|
|
46308
|
-
}));
|
|
46309
|
-
}
|
|
46310
|
-
const deepseekKey = await authCtx.getApiKey("deepseek");
|
|
46311
|
-
if (deepseekKey) {
|
|
46312
|
-
tasks.push(fetchDeepSeekModels(deepseekKey, timeoutMs).then((models) => {
|
|
46313
|
-
result.models.push(...models);
|
|
46314
|
-
result.providers.deepseek = {
|
|
46315
|
-
status: "available",
|
|
46316
|
-
modelCount: models.length
|
|
46317
|
-
};
|
|
46318
|
-
}).catch((err) => {
|
|
46319
|
-
result.providers.deepseek = {
|
|
46320
|
-
status: "error",
|
|
46321
|
-
modelCount: 0,
|
|
46322
|
-
error: err instanceof Error ? err.message : String(err)
|
|
46323
|
-
};
|
|
46324
|
-
}));
|
|
46325
|
-
}
|
|
46326
|
-
const openrouterKey = await authCtx.getApiKey("openrouter");
|
|
46327
|
-
if (openrouterKey) {
|
|
46328
|
-
tasks.push(fetchOpenRouterModels(openrouterKey, timeoutMs).then((models) => {
|
|
46329
|
-
result.models.push(...models);
|
|
46330
|
-
result.providers.openrouter = {
|
|
46331
|
-
status: "available",
|
|
46332
|
-
modelCount: models.length
|
|
46333
|
-
};
|
|
46334
|
-
}).catch((err) => {
|
|
46335
|
-
result.providers.openrouter = {
|
|
46336
|
-
status: "error",
|
|
46337
|
-
modelCount: 0,
|
|
46338
|
-
error: err instanceof Error ? err.message : String(err)
|
|
46339
|
-
};
|
|
46340
|
-
}));
|
|
46341
|
-
}
|
|
46342
|
-
const copilotKey = await authCtx.getApiKey("github-copilot");
|
|
46343
|
-
if (copilotKey) {
|
|
46344
|
-
tasks.push(fetchGitHubCopilotModels(copilotKey, timeoutMs).then((models) => {
|
|
46345
|
-
result.models.push(...models);
|
|
46346
|
-
result.providers["github-copilot"] = {
|
|
46347
|
-
status: "available",
|
|
46348
|
-
modelCount: models.length
|
|
46349
|
-
};
|
|
46350
|
-
}).catch((err) => {
|
|
46351
|
-
result.providers["github-copilot"] = {
|
|
46352
|
-
status: "error",
|
|
46353
|
-
modelCount: 0,
|
|
46354
|
-
error: err instanceof Error ? err.message : String(err)
|
|
46355
|
-
};
|
|
46356
|
-
}));
|
|
46357
|
-
}
|
|
46358
|
-
const groqKey = await authCtx.getApiKey("groq");
|
|
46359
|
-
if (groqKey) {
|
|
46360
|
-
tasks.push(fetchGroqModels(groqKey, timeoutMs).then((models) => {
|
|
46361
|
-
result.models.push(...models);
|
|
46362
|
-
result.providers.groq = {
|
|
46363
|
-
status: "available",
|
|
46364
|
-
modelCount: models.length
|
|
46365
|
-
};
|
|
46366
|
-
}).catch((err) => {
|
|
46367
|
-
result.providers.groq = {
|
|
46368
|
-
status: "error",
|
|
46369
|
-
modelCount: 0,
|
|
46370
|
-
error: err instanceof Error ? err.message : String(err)
|
|
46371
|
-
};
|
|
46372
|
-
}));
|
|
46373
|
-
}
|
|
46374
|
-
const xaiKey = await authCtx.getApiKey("xai");
|
|
46375
|
-
if (xaiKey) {
|
|
46376
|
-
tasks.push(fetchXAIModels(xaiKey, timeoutMs).then((models) => {
|
|
46377
|
-
result.models.push(...models);
|
|
46378
|
-
result.providers.xai = {
|
|
46379
|
-
status: "available",
|
|
46380
|
-
modelCount: models.length
|
|
46381
|
-
};
|
|
46382
|
-
}).catch((err) => {
|
|
46383
|
-
result.providers.xai = {
|
|
46384
|
-
status: "error",
|
|
46385
|
-
modelCount: 0,
|
|
46386
|
-
error: err instanceof Error ? err.message : String(err)
|
|
46387
|
-
};
|
|
46388
|
-
}));
|
|
46389
|
-
}
|
|
46390
|
-
const mistralKey = await authCtx.getApiKey("mistral");
|
|
46391
|
-
if (mistralKey) {
|
|
46392
|
-
tasks.push(fetchMistralModels(mistralKey, timeoutMs).then((models) => {
|
|
46393
|
-
result.models.push(...models);
|
|
46394
|
-
result.providers.mistral = {
|
|
46395
|
-
status: "available",
|
|
46396
|
-
modelCount: models.length
|
|
46397
|
-
};
|
|
46398
|
-
}).catch((err) => {
|
|
46399
|
-
result.providers.mistral = {
|
|
46400
|
-
status: "error",
|
|
46401
|
-
modelCount: 0,
|
|
46402
|
-
error: err instanceof Error ? err.message : String(err)
|
|
46403
|
-
};
|
|
46404
|
-
}));
|
|
46422
|
+
for (const [providerKey, fetcher] of Object.entries(API_KEY_FETCHERS)) {
|
|
46423
|
+
const provider = providerKey;
|
|
46424
|
+
if (!fetcher)
|
|
46425
|
+
continue;
|
|
46426
|
+
const apiKey = await authCtx.getApiKey(provider);
|
|
46427
|
+
if (apiKey) {
|
|
46428
|
+
tasks.push(fetcher(apiKey, timeoutMs).then((models) => {
|
|
46429
|
+
result.models.push(...models);
|
|
46430
|
+
result.providers[provider] = {
|
|
46431
|
+
status: "available",
|
|
46432
|
+
modelCount: models.length
|
|
46433
|
+
};
|
|
46434
|
+
}).catch((err) => {
|
|
46435
|
+
result.providers[provider] = {
|
|
46436
|
+
status: "error",
|
|
46437
|
+
modelCount: 0,
|
|
46438
|
+
error: err instanceof Error ? err.message : String(err)
|
|
46439
|
+
};
|
|
46440
|
+
}));
|
|
46441
|
+
}
|
|
46405
46442
|
}
|
|
46406
46443
|
const ollamaUrl = authCtx.getOllamaEndpoint?.() || "http://localhost:11434/v1";
|
|
46407
|
-
|
|
46444
|
+
const ollamaTimeout = Math.min(timeoutMs, 3000);
|
|
46445
|
+
tasks.push(fetchOllamaModels(ollamaUrl, ollamaTimeout).then((models) => {
|
|
46408
46446
|
if (models.length > 0) {
|
|
46409
46447
|
result.models.push(...models);
|
|
46410
46448
|
result.providers.ollama = {
|
|
@@ -53893,15 +53931,15 @@ var defaultToolCatalog = new ToolCatalog;
|
|
|
53893
53931
|
|
|
53894
53932
|
// src/packages/agents/src/tools/read-file/index.ts
|
|
53895
53933
|
import { existsSync as existsSync4, lstatSync, readFileSync as readFileSync3, realpathSync } from "node:fs";
|
|
53896
|
-
import { isAbsolute, resolve, sep } from "node:path";
|
|
53934
|
+
import { isAbsolute, resolve as resolve2, sep } from "node:path";
|
|
53897
53935
|
var readFileInputSchema = object({
|
|
53898
53936
|
path: string2().describe("The path of the file to read (relative to current working directory or absolute)."),
|
|
53899
53937
|
offset: number2().int().min(1).optional().describe("The line number to start reading from (1-indexed). Defaults to 1."),
|
|
53900
53938
|
limit: number2().int().min(1).max(2000).optional().describe("The maximum number of lines to read. Defaults to 200.")
|
|
53901
53939
|
});
|
|
53902
53940
|
function isPathInside(targetPath, basePath) {
|
|
53903
|
-
const normTarget =
|
|
53904
|
-
const normBase =
|
|
53941
|
+
const normTarget = resolve2(targetPath);
|
|
53942
|
+
const normBase = resolve2(basePath);
|
|
53905
53943
|
if (normTarget === normBase) {
|
|
53906
53944
|
return true;
|
|
53907
53945
|
}
|
|
@@ -53913,7 +53951,7 @@ function resolveSafeReadPath(cwd, filePath) {
|
|
|
53913
53951
|
throw new Error("File path cannot be empty.");
|
|
53914
53952
|
}
|
|
53915
53953
|
const trimmed = filePath.trim();
|
|
53916
|
-
const resolved = isAbsolute(trimmed) ?
|
|
53954
|
+
const resolved = isAbsolute(trimmed) ? resolve2(trimmed) : resolve2(cwd, trimmed);
|
|
53917
53955
|
if (!isPathInside(resolved, cwd)) {
|
|
53918
53956
|
throw new Error(`Access denied: path "${filePath}" resolves outside the trusted workspace root ("${cwd}").`);
|
|
53919
53957
|
}
|
|
@@ -54014,12 +54052,12 @@ import { existsSync as existsSync8, readFileSync as readFileSync5, statSync } fr
|
|
|
54014
54052
|
// src/packages/services/src/checkpoint/path.ts
|
|
54015
54053
|
import { createHash } from "node:crypto";
|
|
54016
54054
|
import { existsSync as existsSync6, lstatSync as lstatSync2, realpathSync as realpathSync3 } from "node:fs";
|
|
54017
|
-
import { dirname as dirname4, isAbsolute as isAbsolute2, relative, resolve as
|
|
54055
|
+
import { dirname as dirname4, isAbsolute as isAbsolute2, relative, resolve as resolve4, sep as sep3 } from "node:path";
|
|
54018
54056
|
|
|
54019
54057
|
// src/packages/services/src/config/settings.ts
|
|
54020
54058
|
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync4, realpathSync as realpathSync2, writeFileSync as writeFileSync3 } from "node:fs";
|
|
54021
54059
|
import { homedir as homedir4 } from "node:os";
|
|
54022
|
-
import { join as join6, resolve as
|
|
54060
|
+
import { join as join6, resolve as resolve3, sep as sep2 } from "node:path";
|
|
54023
54061
|
function getSettingsDir() {
|
|
54024
54062
|
if (process.env.STEWARD_SETTINGS_DIR) {
|
|
54025
54063
|
return process.env.STEWARD_SETTINGS_DIR;
|
|
@@ -54073,7 +54111,7 @@ function saveThemeSelection(name) {
|
|
|
54073
54111
|
});
|
|
54074
54112
|
}
|
|
54075
54113
|
function normalizeFolderPath(inputPath) {
|
|
54076
|
-
let absolute =
|
|
54114
|
+
let absolute = resolve3(inputPath);
|
|
54077
54115
|
try {
|
|
54078
54116
|
absolute = realpathSync2(absolute);
|
|
54079
54117
|
} catch {}
|
|
@@ -54145,7 +54183,7 @@ function resolveDirectMutationPath(cwd, filePath) {
|
|
|
54145
54183
|
}
|
|
54146
54184
|
const trimmed = filePath.trim();
|
|
54147
54185
|
const workspaceRoot = normalizeFolderPath(cwd);
|
|
54148
|
-
const resolved = isAbsolute2(trimmed) ?
|
|
54186
|
+
const resolved = isAbsolute2(trimmed) ? resolve4(trimmed) : resolve4(cwd, trimmed);
|
|
54149
54187
|
if (!isPathInside2(resolved, workspaceRoot)) {
|
|
54150
54188
|
throw new Error(`Access denied: path "${filePath}" resolves outside the trusted workspace root ("${cwd}").`);
|
|
54151
54189
|
}
|
|
@@ -54732,7 +54770,7 @@ var editFileTool = {
|
|
|
54732
54770
|
|
|
54733
54771
|
// src/packages/agents/src/tools/glob/index.ts
|
|
54734
54772
|
import { readdirSync as readdirSync2 } from "node:fs";
|
|
54735
|
-
import { isAbsolute as isAbsolute3, join as join8, relative as relative2, resolve as
|
|
54773
|
+
import { isAbsolute as isAbsolute3, join as join8, relative as relative2, resolve as resolve5 } from "node:path";
|
|
54736
54774
|
var globInputSchema = object({
|
|
54737
54775
|
pattern: string2().describe('Glob pattern to match files against (e.g. "**/*.ts", "src/*.json").'),
|
|
54738
54776
|
path: string2().optional().describe("Starting directory path. Defaults to workspace root."),
|
|
@@ -54785,9 +54823,9 @@ var globTool = {
|
|
|
54785
54823
|
},
|
|
54786
54824
|
execute: async (args, context) => {
|
|
54787
54825
|
const startTime = Date.now();
|
|
54788
|
-
const searchRoot = args.path ? isAbsolute3(args.path) ?
|
|
54789
|
-
const normCwd =
|
|
54790
|
-
const normTarget =
|
|
54826
|
+
const searchRoot = args.path ? isAbsolute3(args.path) ? resolve5(args.path) : resolve5(context.cwd, args.path) : context.cwd;
|
|
54827
|
+
const normCwd = resolve5(context.cwd);
|
|
54828
|
+
const normTarget = resolve5(searchRoot);
|
|
54791
54829
|
const prefix = normCwd.endsWith("/") ? normCwd : `${normCwd}/`;
|
|
54792
54830
|
if (normTarget !== normCwd && !normTarget.startsWith(prefix)) {
|
|
54793
54831
|
throw new Error(`Access denied: path "${args.path}" resolves outside the trusted workspace root ("${context.cwd}").`);
|
|
@@ -54814,7 +54852,7 @@ var globTool = {
|
|
|
54814
54852
|
|
|
54815
54853
|
// src/packages/agents/src/tools/grep/index.ts
|
|
54816
54854
|
import { readFileSync as readFileSync7, readdirSync as readdirSync3 } from "node:fs";
|
|
54817
|
-
import { isAbsolute as isAbsolute4, join as join9, relative as relative3, resolve as
|
|
54855
|
+
import { isAbsolute as isAbsolute4, join as join9, relative as relative3, resolve as resolve6 } from "node:path";
|
|
54818
54856
|
var grepInputSchema = object({
|
|
54819
54857
|
pattern: string2().describe("The regex or text pattern to search for across files."),
|
|
54820
54858
|
path: string2().optional().describe("Target directory or file path to search. Defaults to current directory."),
|
|
@@ -54890,9 +54928,9 @@ var grepTool = {
|
|
|
54890
54928
|
},
|
|
54891
54929
|
execute: async (args, context) => {
|
|
54892
54930
|
const startTime = Date.now();
|
|
54893
|
-
const searchRoot = args.path ? isAbsolute4(args.path) ?
|
|
54894
|
-
const normCwd =
|
|
54895
|
-
const normTarget =
|
|
54931
|
+
const searchRoot = args.path ? isAbsolute4(args.path) ? resolve6(args.path) : resolve6(context.cwd, args.path) : context.cwd;
|
|
54932
|
+
const normCwd = resolve6(context.cwd);
|
|
54933
|
+
const normTarget = resolve6(searchRoot);
|
|
54896
54934
|
const prefix = normCwd.endsWith("/") ? normCwd : `${normCwd}/`;
|
|
54897
54935
|
if (normTarget !== normCwd && !normTarget.startsWith(prefix)) {
|
|
54898
54936
|
throw new Error(`Access denied: path "${args.path}" resolves outside the trusted workspace root ("${context.cwd}").`);
|
|
@@ -54951,7 +54989,7 @@ var grepTool = {
|
|
|
54951
54989
|
|
|
54952
54990
|
// src/packages/agents/src/tools/list-dir/index.ts
|
|
54953
54991
|
import { readdirSync as readdirSync4, statSync as statSync3 } from "node:fs";
|
|
54954
|
-
import { isAbsolute as isAbsolute5, join as join10, resolve as
|
|
54992
|
+
import { isAbsolute as isAbsolute5, join as join10, resolve as resolve7 } from "node:path";
|
|
54955
54993
|
var listDirInputSchema = object({
|
|
54956
54994
|
path: string2().optional().describe("Directory path to list. Defaults to current working directory."),
|
|
54957
54995
|
recursive: boolean2().optional().describe("If true, lists files recursively (depth 2). Defaults to false.")
|
|
@@ -54969,9 +55007,9 @@ var listDirTool = {
|
|
|
54969
55007
|
return `Listed ${files} file${files === 1 ? "" : "s"}, ${dirs} director${dirs === 1 ? "y" : "ies"}`;
|
|
54970
55008
|
},
|
|
54971
55009
|
execute: async (args, context) => {
|
|
54972
|
-
const targetPath = args.path ? isAbsolute5(args.path) ?
|
|
54973
|
-
const normCwd =
|
|
54974
|
-
const normTarget =
|
|
55010
|
+
const targetPath = args.path ? isAbsolute5(args.path) ? resolve7(args.path) : resolve7(context.cwd, args.path) : context.cwd;
|
|
55011
|
+
const normCwd = resolve7(context.cwd);
|
|
55012
|
+
const normTarget = resolve7(targetPath);
|
|
54975
55013
|
const prefix = normCwd.endsWith("/") ? normCwd : `${normCwd}/`;
|
|
54976
55014
|
if (normTarget !== normCwd && !normTarget.startsWith(prefix)) {
|
|
54977
55015
|
throw new Error(`Access denied: path "${args.path}" resolves outside the trusted workspace root ("${context.cwd}").`);
|
|
@@ -55407,18 +55445,18 @@ Process error: ${err.message}
|
|
|
55407
55445
|
// src/packages/agents/src/tools/bash/command-policy.ts
|
|
55408
55446
|
import { existsSync as existsSync10, realpathSync as realpathSync4 } from "node:fs";
|
|
55409
55447
|
import { homedir as homedir6 } from "node:os";
|
|
55410
|
-
import { isAbsolute as isAbsolute6, resolve as
|
|
55448
|
+
import { isAbsolute as isAbsolute6, resolve as resolve8, sep as sep4 } from "node:path";
|
|
55411
55449
|
function expandHome(filepath) {
|
|
55412
55450
|
if (filepath === "~") {
|
|
55413
55451
|
return homedir6();
|
|
55414
55452
|
}
|
|
55415
55453
|
if (filepath.startsWith("~/") || filepath.startsWith("~\\")) {
|
|
55416
|
-
return
|
|
55454
|
+
return resolve8(homedir6(), filepath.slice(2));
|
|
55417
55455
|
}
|
|
55418
55456
|
return filepath;
|
|
55419
55457
|
}
|
|
55420
55458
|
function normalizeRoot(inputPath) {
|
|
55421
|
-
let absolute =
|
|
55459
|
+
let absolute = resolve8(inputPath);
|
|
55422
55460
|
try {
|
|
55423
55461
|
absolute = realpathSync4(absolute);
|
|
55424
55462
|
} catch {}
|
|
@@ -55553,7 +55591,7 @@ function arePathTokensInsideWorkspace(tokens, startIndex, cwd) {
|
|
|
55553
55591
|
continue;
|
|
55554
55592
|
}
|
|
55555
55593
|
const expanded = expandHome(tok);
|
|
55556
|
-
const resolved = isAbsolute6(expanded) ?
|
|
55594
|
+
const resolved = isAbsolute6(expanded) ? resolve8(expanded) : resolve8(cwd, expanded);
|
|
55557
55595
|
if (!isContainedInWorkspace(resolved, cwd)) {
|
|
55558
55596
|
return false;
|
|
55559
55597
|
}
|
|
@@ -55941,8 +55979,8 @@ import { isIP } from "node:net";
|
|
|
55941
55979
|
// package.json
|
|
55942
55980
|
var package_default = {
|
|
55943
55981
|
name: "steward-cli",
|
|
55944
|
-
version: "0.
|
|
55945
|
-
description: "Interactive AI engineering assistant for the terminal
|
|
55982
|
+
version: "0.23.0",
|
|
55983
|
+
description: "Interactive AI engineering assistant for the terminal.",
|
|
55946
55984
|
author: "sapirrior",
|
|
55947
55985
|
license: "MIT",
|
|
55948
55986
|
files: [
|
|
@@ -56296,7 +56334,7 @@ var webSearchTool = {
|
|
|
56296
56334
|
// src/packages/agents/src/skills/discovery.ts
|
|
56297
56335
|
import { existsSync as existsSync11, readdirSync as readdirSync5, readFileSync as readFileSync8, statSync as statSync4 } from "node:fs";
|
|
56298
56336
|
import { homedir as homedir7 } from "node:os";
|
|
56299
|
-
import { isAbsolute as isAbsolute7, join as join12, normalize, resolve as
|
|
56337
|
+
import { isAbsolute as isAbsolute7, join as join12, normalize, resolve as resolve9, sep as sep5 } from "node:path";
|
|
56300
56338
|
var MAX_SKILL_FILE_SIZE = 64 * 1024;
|
|
56301
56339
|
function parseSkillFrontmatter(content) {
|
|
56302
56340
|
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
@@ -56411,8 +56449,8 @@ function readSkillResource(name, relativePath, cwd = process.cwd(), maxBytes = M
|
|
|
56411
56449
|
if (normalizedRelative.startsWith("..") || normalizedRelative.includes(`${sep5}..`)) {
|
|
56412
56450
|
throw new Error(`Invalid skill resource path: directory traversal ("..") is forbidden (${trimmedPath})`);
|
|
56413
56451
|
}
|
|
56414
|
-
targetFilePath =
|
|
56415
|
-
const resolvedDir =
|
|
56452
|
+
targetFilePath = resolve9(skill.dirPath, normalizedRelative);
|
|
56453
|
+
const resolvedDir = resolve9(skill.dirPath);
|
|
56416
56454
|
if (!targetFilePath.startsWith(resolvedDir + sep5) && targetFilePath !== resolvedDir) {
|
|
56417
56455
|
throw new Error(`Resource "${trimmedPath}" resolves outside skill directory`);
|
|
56418
56456
|
}
|
|
@@ -57646,7 +57684,7 @@ import {
|
|
|
57646
57684
|
unlinkSync as unlinkSync5,
|
|
57647
57685
|
writeSync as writeSync5
|
|
57648
57686
|
} from "node:fs";
|
|
57649
|
-
import { dirname as dirname6, join as join15, resolve as
|
|
57687
|
+
import { dirname as dirname6, join as join15, resolve as resolve10 } from "node:path";
|
|
57650
57688
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
57651
57689
|
function getFileStateOnDisk(absPath) {
|
|
57652
57690
|
if (!existsSync15(absPath)) {
|
|
@@ -57744,7 +57782,7 @@ async function executeRewind(options) {
|
|
|
57744
57782
|
if (!fileCp.mutationCommitted)
|
|
57745
57783
|
continue;
|
|
57746
57784
|
const relPath = fileCp.relativePath;
|
|
57747
|
-
const absPath =
|
|
57785
|
+
const absPath = resolve10(workspaceRoot, relPath);
|
|
57748
57786
|
if (!fileOperationsMap.has(relPath)) {
|
|
57749
57787
|
fileOperationsMap.set(relPath, {
|
|
57750
57788
|
relativePath: relPath,
|
|
@@ -57895,7 +57933,7 @@ async function recoverPendingCheckpoint(workspaceRoot, sessionId) {
|
|
|
57895
57933
|
if (journal.phase === "files-restored" || journal.phase === "files-pending") {
|
|
57896
57934
|
for (const file of journal.files) {
|
|
57897
57935
|
if (file.post) {
|
|
57898
|
-
const absPath =
|
|
57936
|
+
const absPath = resolve10(workspaceRoot, file.relativePath);
|
|
57899
57937
|
try {
|
|
57900
57938
|
restoreFileStateToDisk(workspaceHash, absPath, file.post);
|
|
57901
57939
|
} catch {}
|
|
@@ -58669,35 +58707,7 @@ function getDiscoveryAuthContext() {
|
|
|
58669
58707
|
const resolved = await authManager.resolve(provider).catch(() => {
|
|
58670
58708
|
return;
|
|
58671
58709
|
});
|
|
58672
|
-
|
|
58673
|
-
return resolved.token;
|
|
58674
|
-
}
|
|
58675
|
-
switch (provider) {
|
|
58676
|
-
case "openai":
|
|
58677
|
-
return config.openaiApiKey;
|
|
58678
|
-
case "gemini":
|
|
58679
|
-
return config.geminiApiKey;
|
|
58680
|
-
case "anthropic":
|
|
58681
|
-
return config.anthropicApiKey;
|
|
58682
|
-
case "deepseek":
|
|
58683
|
-
return config.deepseekApiKey;
|
|
58684
|
-
case "openrouter":
|
|
58685
|
-
return config.openrouterApiKey;
|
|
58686
|
-
case "github-copilot":
|
|
58687
|
-
return config.copilotGithubToken;
|
|
58688
|
-
case "groq":
|
|
58689
|
-
return config.groqApiKey;
|
|
58690
|
-
case "xai":
|
|
58691
|
-
return config.xaiApiKey;
|
|
58692
|
-
case "mistral":
|
|
58693
|
-
return config.mistralApiKey;
|
|
58694
|
-
case "ollama":
|
|
58695
|
-
return "none";
|
|
58696
|
-
case "custom":
|
|
58697
|
-
return config.custom.apiKey || "none";
|
|
58698
|
-
default:
|
|
58699
|
-
return;
|
|
58700
|
-
}
|
|
58710
|
+
return resolved?.token && resolved.token !== "none" ? resolved.token : undefined;
|
|
58701
58711
|
},
|
|
58702
58712
|
getCustomEndpoint: () => config.custom,
|
|
58703
58713
|
getOllamaEndpoint: () => config.ollamaBaseUrl
|