steward-cli 0.22.0 → 0.25.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 +806 -775
- 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
|
|
@@ -44120,7 +44139,7 @@ async function loginGitHubCopilot(interaction) {
|
|
|
44120
44139
|
signal
|
|
44121
44140
|
});
|
|
44122
44141
|
} catch (err) {
|
|
44123
|
-
throw new AIError(`Failed to initiate GitHub device authorization: ${err.message}`, {
|
|
44142
|
+
throw new AIError(`Failed to initiate GitHub device authorization: ${err instanceof Error ? err.message : String(err)}`, {
|
|
44124
44143
|
code: "oauth",
|
|
44125
44144
|
provider: "github-copilot",
|
|
44126
44145
|
cause: err
|
|
@@ -44161,18 +44180,21 @@ async function loginGitHubCopilot(interaction) {
|
|
|
44161
44180
|
signal
|
|
44162
44181
|
});
|
|
44163
44182
|
const data = await res.json();
|
|
44164
|
-
if (data?.access_token) {
|
|
44183
|
+
if (typeof data?.access_token === "string") {
|
|
44165
44184
|
return { status: "complete", value: data.access_token };
|
|
44166
44185
|
}
|
|
44167
44186
|
if (data?.error === "authorization_pending") {
|
|
44168
44187
|
return { status: "pending" };
|
|
44169
44188
|
}
|
|
44170
44189
|
if (data?.error === "slow_down") {
|
|
44171
|
-
return {
|
|
44190
|
+
return {
|
|
44191
|
+
status: "slow_down",
|
|
44192
|
+
intervalSeconds: typeof data.interval === "number" ? data.interval : undefined
|
|
44193
|
+
};
|
|
44172
44194
|
}
|
|
44173
44195
|
return {
|
|
44174
44196
|
status: "failed",
|
|
44175
|
-
message: data?.error_description
|
|
44197
|
+
message: typeof data?.error_description === "string" ? data.error_description : typeof data?.error === "string" ? data.error : "Authorization failed"
|
|
44176
44198
|
};
|
|
44177
44199
|
}
|
|
44178
44200
|
});
|
|
@@ -44196,15 +44218,14 @@ async function refreshGitHubCopilotToken(githubAccessToken, signal) {
|
|
|
44196
44218
|
signal
|
|
44197
44219
|
});
|
|
44198
44220
|
} catch (err) {
|
|
44199
|
-
throw new AIError(`Failed to fetch Copilot token: ${err.message}`, {
|
|
44221
|
+
throw new AIError(`Failed to fetch Copilot token: ${err instanceof Error ? err.message : String(err)}`, {
|
|
44200
44222
|
code: "oauth",
|
|
44201
44223
|
provider: "github-copilot",
|
|
44202
44224
|
cause: err
|
|
44203
44225
|
});
|
|
44204
44226
|
}
|
|
44205
44227
|
if (!res.ok) {
|
|
44206
|
-
|
|
44207
|
-
throw new AIError(`GitHub Copilot token exchange failed (HTTP ${res.status}): ${text}`, {
|
|
44228
|
+
throw new AIError(`GitHub Copilot token exchange failed with HTTP status ${res.status}`, {
|
|
44208
44229
|
code: "oauth",
|
|
44209
44230
|
provider: "github-copilot",
|
|
44210
44231
|
status: res.status
|
|
@@ -44239,6 +44260,23 @@ function toGitHubCopilotAuth(credential) {
|
|
|
44239
44260
|
}
|
|
44240
44261
|
|
|
44241
44262
|
// src/packages/ai/src/auth/resolver.ts
|
|
44263
|
+
var OAUTH_ADAPTERS = {
|
|
44264
|
+
anthropic: {
|
|
44265
|
+
login: loginAnthropic,
|
|
44266
|
+
refresh: refreshAnthropic,
|
|
44267
|
+
toResolvedAuth: toAnthropicAuth
|
|
44268
|
+
},
|
|
44269
|
+
openrouter: {
|
|
44270
|
+
login: loginOpenRouter,
|
|
44271
|
+
refresh: refreshOpenRouter,
|
|
44272
|
+
toResolvedAuth: toOpenRouterAuth
|
|
44273
|
+
},
|
|
44274
|
+
"github-copilot": {
|
|
44275
|
+
login: loginGitHubCopilot,
|
|
44276
|
+
refresh: refreshGitHubCopilot,
|
|
44277
|
+
toResolvedAuth: toGitHubCopilotAuth
|
|
44278
|
+
}
|
|
44279
|
+
};
|
|
44242
44280
|
var DEFAULT_REFRESH_MARGIN_MS = 5 * 60 * 1000;
|
|
44243
44281
|
|
|
44244
44282
|
class AuthResolver {
|
|
@@ -44286,32 +44324,21 @@ class AuthResolver {
|
|
|
44286
44324
|
return;
|
|
44287
44325
|
}
|
|
44288
44326
|
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);
|
|
44327
|
+
const adapter = OAUTH_ADAPTERS[provider];
|
|
44328
|
+
if (adapter) {
|
|
44329
|
+
return await adapter.refresh(credential, signal);
|
|
44297
44330
|
}
|
|
44298
44331
|
return credential;
|
|
44299
44332
|
}
|
|
44300
44333
|
formatOAuth(provider, credential) {
|
|
44301
|
-
|
|
44302
|
-
|
|
44303
|
-
|
|
44304
|
-
if (provider === "openrouter") {
|
|
44305
|
-
return toOpenRouterAuth(credential);
|
|
44306
|
-
}
|
|
44307
|
-
if (provider === "github-copilot") {
|
|
44308
|
-
return toGitHubCopilotAuth(credential);
|
|
44334
|
+
const adapter = OAUTH_ADAPTERS[provider];
|
|
44335
|
+
if (adapter) {
|
|
44336
|
+
return adapter.toResolvedAuth(credential);
|
|
44309
44337
|
}
|
|
44310
|
-
|
|
44311
|
-
|
|
44312
|
-
|
|
44313
|
-
|
|
44314
|
-
};
|
|
44338
|
+
throw new AIError(`Unsupported OAuth provider: "${provider}"`, {
|
|
44339
|
+
code: "oauth",
|
|
44340
|
+
provider
|
|
44341
|
+
});
|
|
44315
44342
|
}
|
|
44316
44343
|
}
|
|
44317
44344
|
|
|
@@ -44319,35 +44346,96 @@ class AuthResolver {
|
|
|
44319
44346
|
import * as fs from "node:fs/promises";
|
|
44320
44347
|
import * as path from "node:path";
|
|
44321
44348
|
import * as os2 from "node:os";
|
|
44349
|
+
var fileMutexes = new Map;
|
|
44350
|
+
function validateAuthStorage(data) {
|
|
44351
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) {
|
|
44352
|
+
return false;
|
|
44353
|
+
}
|
|
44354
|
+
const root = data;
|
|
44355
|
+
if (root.version !== 1) {
|
|
44356
|
+
return false;
|
|
44357
|
+
}
|
|
44358
|
+
if (!root.credentials || typeof root.credentials !== "object" || Array.isArray(root.credentials)) {
|
|
44359
|
+
return false;
|
|
44360
|
+
}
|
|
44361
|
+
const credentials = root.credentials;
|
|
44362
|
+
for (const [_, cred] of Object.entries(credentials)) {
|
|
44363
|
+
if (!cred || typeof cred !== "object" || Array.isArray(cred)) {
|
|
44364
|
+
return false;
|
|
44365
|
+
}
|
|
44366
|
+
const c = cred;
|
|
44367
|
+
if (c.type !== "oauth") {
|
|
44368
|
+
return false;
|
|
44369
|
+
}
|
|
44370
|
+
if (typeof c.accessToken !== "string") {
|
|
44371
|
+
return false;
|
|
44372
|
+
}
|
|
44373
|
+
if (c.refreshToken !== undefined && c.refreshToken !== null && typeof c.refreshToken !== "string") {
|
|
44374
|
+
return false;
|
|
44375
|
+
}
|
|
44376
|
+
if (c.expiresAt !== undefined && c.expiresAt !== null && typeof c.expiresAt !== "number") {
|
|
44377
|
+
return false;
|
|
44378
|
+
}
|
|
44379
|
+
}
|
|
44380
|
+
return true;
|
|
44381
|
+
}
|
|
44382
|
+
|
|
44322
44383
|
class FileCredentialStore {
|
|
44323
44384
|
dirPath;
|
|
44324
44385
|
filePath;
|
|
44325
|
-
|
|
44386
|
+
canonicalPath;
|
|
44326
44387
|
constructor(options) {
|
|
44327
44388
|
this.dirPath = options?.storageDir ?? path.join(os2.homedir(), ".steward");
|
|
44328
44389
|
this.filePath = options?.filePath ?? path.join(this.dirPath, "auth.json");
|
|
44390
|
+
this.canonicalPath = path.resolve(this.filePath);
|
|
44329
44391
|
}
|
|
44330
44392
|
async ensureDirectory() {
|
|
44331
44393
|
try {
|
|
44332
44394
|
await fs.mkdir(this.dirPath, { recursive: true, mode: 448 });
|
|
44333
44395
|
await fs.chmod(this.dirPath, 448).catch(() => {});
|
|
44334
|
-
} catch {
|
|
44396
|
+
} catch (err) {
|
|
44397
|
+
const isExist = err?.code === "EEXIST";
|
|
44398
|
+
if (!isExist) {
|
|
44399
|
+
throw new AIError(`Failed to create directory "${this.dirPath}": ${err instanceof Error ? err.message : String(err)}`, {
|
|
44400
|
+
code: "auth",
|
|
44401
|
+
cause: err
|
|
44402
|
+
});
|
|
44403
|
+
}
|
|
44404
|
+
}
|
|
44335
44405
|
}
|
|
44336
44406
|
async loadData() {
|
|
44337
44407
|
try {
|
|
44338
44408
|
const content = await fs.readFile(this.filePath, "utf-8");
|
|
44339
|
-
|
|
44340
|
-
|
|
44409
|
+
let parsed;
|
|
44410
|
+
try {
|
|
44411
|
+
parsed = JSON.parse(content);
|
|
44412
|
+
} catch (jsonErr) {
|
|
44413
|
+
throw new AIError("Invalid or corrupt auth.json schema.", {
|
|
44414
|
+
code: "auth",
|
|
44415
|
+
cause: jsonErr
|
|
44416
|
+
});
|
|
44417
|
+
}
|
|
44418
|
+
if (!validateAuthStorage(parsed)) {
|
|
44341
44419
|
throw new AIError("Invalid or corrupt auth.json schema.", { code: "auth" });
|
|
44342
44420
|
}
|
|
44421
|
+
for (const cred of Object.values(parsed.credentials)) {
|
|
44422
|
+
if (cred) {
|
|
44423
|
+
if (cred.expiresAt === null) {
|
|
44424
|
+
delete cred.expiresAt;
|
|
44425
|
+
}
|
|
44426
|
+
if (cred.refreshToken === null) {
|
|
44427
|
+
delete cred.refreshToken;
|
|
44428
|
+
}
|
|
44429
|
+
}
|
|
44430
|
+
}
|
|
44343
44431
|
return parsed;
|
|
44344
44432
|
} catch (err) {
|
|
44345
|
-
if (err
|
|
44433
|
+
if (err?.code === "ENOENT") {
|
|
44346
44434
|
return { version: 1, credentials: {} };
|
|
44347
44435
|
}
|
|
44348
44436
|
if (err instanceof AIError)
|
|
44349
44437
|
throw err;
|
|
44350
|
-
throw new AIError(`Failed to read auth store: ${err.message}`, {
|
|
44438
|
+
throw new AIError(`Failed to read auth store: ${err instanceof Error ? err.message : String(err)}`, {
|
|
44351
44439
|
code: "auth",
|
|
44352
44440
|
cause: err
|
|
44353
44441
|
});
|
|
@@ -44364,7 +44452,7 @@ class FileCredentialStore {
|
|
|
44364
44452
|
await fs.chmod(this.filePath, 384).catch(() => {});
|
|
44365
44453
|
} catch (err) {
|
|
44366
44454
|
await fs.unlink(tempFile).catch(() => {});
|
|
44367
|
-
throw new AIError(`Failed to atomically save auth store: ${err.message}`, {
|
|
44455
|
+
throw new AIError(`Failed to atomically save auth store: ${err instanceof Error ? err.message : String(err)}`, {
|
|
44368
44456
|
code: "auth",
|
|
44369
44457
|
cause: err
|
|
44370
44458
|
});
|
|
@@ -44389,24 +44477,21 @@ class FileCredentialStore {
|
|
|
44389
44477
|
return list;
|
|
44390
44478
|
}
|
|
44391
44479
|
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
|
-
});
|
|
44480
|
+
const currentMutex = fileMutexes.get(this.canonicalPath) ?? Promise.resolve();
|
|
44481
|
+
const nextMutex = currentMutex.then(async () => {
|
|
44482
|
+
const data = await this.loadData();
|
|
44483
|
+
const current = data.credentials[provider];
|
|
44484
|
+
const updated = await fn(current);
|
|
44485
|
+
if (updated) {
|
|
44486
|
+
data.credentials[provider] = updated;
|
|
44487
|
+
} else {
|
|
44488
|
+
delete data.credentials[provider];
|
|
44489
|
+
}
|
|
44490
|
+
await this.saveData(data);
|
|
44491
|
+
return updated;
|
|
44409
44492
|
});
|
|
44493
|
+
fileMutexes.set(this.canonicalPath, nextMutex.catch(() => {}));
|
|
44494
|
+
return await nextMutex;
|
|
44410
44495
|
}
|
|
44411
44496
|
async delete(provider) {
|
|
44412
44497
|
await this.modify(provider, async () => {
|
|
@@ -44465,19 +44550,14 @@ class DefaultAuthManager {
|
|
|
44465
44550
|
return await this.resolver.resolve(provider, signal);
|
|
44466
44551
|
}
|
|
44467
44552
|
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 {
|
|
44553
|
+
const adapter = OAUTH_ADAPTERS[provider];
|
|
44554
|
+
if (!adapter) {
|
|
44476
44555
|
throw new AIError(`No browser OAuth login is available for "${provider}".`, {
|
|
44477
44556
|
code: "oauth",
|
|
44478
44557
|
provider
|
|
44479
44558
|
});
|
|
44480
44559
|
}
|
|
44560
|
+
const credential = await adapter.login(interaction);
|
|
44481
44561
|
await this.store.modify(provider, async () => credential);
|
|
44482
44562
|
return credential;
|
|
44483
44563
|
}
|
|
@@ -44606,6 +44686,13 @@ var PROVIDER_REGISTRY = {
|
|
|
44606
44686
|
envVar: "CUSTOM_API_URL / CUSTOM_API_KEY"
|
|
44607
44687
|
}
|
|
44608
44688
|
};
|
|
44689
|
+
function getProviderDescriptor(provider) {
|
|
44690
|
+
const descriptor = PROVIDER_REGISTRY[provider];
|
|
44691
|
+
if (!descriptor) {
|
|
44692
|
+
throw new Error(`Unsupported provider: "${provider}"`);
|
|
44693
|
+
}
|
|
44694
|
+
return descriptor;
|
|
44695
|
+
}
|
|
44609
44696
|
|
|
44610
44697
|
// src/packages/ai/src/models/selection.ts
|
|
44611
44698
|
var CANONICAL_DEFAULT_EFFORT = "medium";
|
|
@@ -44623,26 +44710,36 @@ var PROVIDER_SELECTION_PRIORITY = [
|
|
|
44623
44710
|
"custom"
|
|
44624
44711
|
];
|
|
44625
44712
|
function inferProviderFromModelId(modelId) {
|
|
44626
|
-
const lower = modelId.toLowerCase();
|
|
44627
|
-
if (
|
|
44713
|
+
const lower = modelId.trim().toLowerCase();
|
|
44714
|
+
if (!lower)
|
|
44715
|
+
return null;
|
|
44716
|
+
if (/^[a-z0-9-_.]+\/[a-z0-9-_.]+$/.test(lower)) {
|
|
44628
44717
|
return "openrouter";
|
|
44629
|
-
|
|
44718
|
+
}
|
|
44719
|
+
if (lower.startsWith("copilot/") || lower.startsWith("github-copilot/")) {
|
|
44630
44720
|
return "github-copilot";
|
|
44631
|
-
|
|
44721
|
+
}
|
|
44722
|
+
if (lower.startsWith("gemini-") || lower.startsWith("gemma-")) {
|
|
44632
44723
|
return "gemini";
|
|
44633
|
-
|
|
44724
|
+
}
|
|
44725
|
+
if (lower.startsWith("claude-")) {
|
|
44634
44726
|
return "anthropic";
|
|
44635
|
-
|
|
44727
|
+
}
|
|
44728
|
+
if (lower.startsWith("gpt-") || lower.startsWith("chatgpt-") || /^o[1-9]($|-)/.test(lower)) {
|
|
44636
44729
|
return "openai";
|
|
44637
44730
|
}
|
|
44638
|
-
if (lower.startsWith("deepseek-"))
|
|
44731
|
+
if (lower.startsWith("deepseek-")) {
|
|
44639
44732
|
return "deepseek";
|
|
44640
|
-
|
|
44733
|
+
}
|
|
44734
|
+
if (lower.startsWith("grok-")) {
|
|
44641
44735
|
return "xai";
|
|
44642
|
-
|
|
44736
|
+
}
|
|
44737
|
+
if (lower.startsWith("codestral") || lower.startsWith("mistral") || lower.startsWith("pixtral") || lower.startsWith("ministral")) {
|
|
44643
44738
|
return "mistral";
|
|
44644
|
-
|
|
44739
|
+
}
|
|
44740
|
+
if (lower.startsWith("ollama/") || /^[a-z0-9_.-]+:[a-z0-9_.-]+$/.test(lower)) {
|
|
44645
44741
|
return "ollama";
|
|
44742
|
+
}
|
|
44646
44743
|
return null;
|
|
44647
44744
|
}
|
|
44648
44745
|
async function resolveModelSelection(requested, context) {
|
|
@@ -44807,6 +44904,15 @@ function parseSSEEvent(raw) {
|
|
|
44807
44904
|
}
|
|
44808
44905
|
|
|
44809
44906
|
// src/packages/ai/src/json.ts
|
|
44907
|
+
function sanitizeSurrogates(text) {
|
|
44908
|
+
if (typeof text !== "string") {
|
|
44909
|
+
if (text === null || text === undefined) {
|
|
44910
|
+
return "";
|
|
44911
|
+
}
|
|
44912
|
+
return sanitizeSurrogates(typeof text === "object" ? JSON.stringify(text) : String(text));
|
|
44913
|
+
}
|
|
44914
|
+
return text.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "");
|
|
44915
|
+
}
|
|
44810
44916
|
function parseStreamingJson(partial) {
|
|
44811
44917
|
const trimmed = partial.trim();
|
|
44812
44918
|
if (!trimmed) {
|
|
@@ -44876,82 +44982,78 @@ function parseStreamingJson(partial) {
|
|
|
44876
44982
|
}
|
|
44877
44983
|
|
|
44878
44984
|
// src/packages/ai/src/providers/openai-compatible.ts
|
|
44985
|
+
var STATIC_PROFILES = {
|
|
44986
|
+
openrouter: {
|
|
44987
|
+
provider: "openrouter",
|
|
44988
|
+
baseUrl: "https://openrouter.ai/api/v1",
|
|
44989
|
+
supportsReasoning: true,
|
|
44990
|
+
reasoningFormat: "openrouter",
|
|
44991
|
+
apiPath: "/chat/completions",
|
|
44992
|
+
requiresApiKey: true
|
|
44993
|
+
},
|
|
44994
|
+
deepseek: {
|
|
44995
|
+
provider: "deepseek",
|
|
44996
|
+
baseUrl: "https://api.deepseek.com",
|
|
44997
|
+
supportsReasoning: true,
|
|
44998
|
+
reasoningFormat: "deepseek",
|
|
44999
|
+
apiPath: "/chat/completions",
|
|
45000
|
+
requiresApiKey: true
|
|
45001
|
+
},
|
|
45002
|
+
"github-copilot": {
|
|
45003
|
+
provider: "github-copilot",
|
|
45004
|
+
baseUrl: "https://api.individual.githubcopilot.com",
|
|
45005
|
+
supportsReasoning: true,
|
|
45006
|
+
reasoningFormat: "openai",
|
|
45007
|
+
apiPath: "/chat/completions",
|
|
45008
|
+
requiresApiKey: true
|
|
45009
|
+
},
|
|
45010
|
+
groq: {
|
|
45011
|
+
provider: "groq",
|
|
45012
|
+
baseUrl: "https://api.groq.com/openai/v1",
|
|
45013
|
+
supportsReasoning: true,
|
|
45014
|
+
reasoningFormat: "deepseek",
|
|
45015
|
+
apiPath: "/chat/completions",
|
|
45016
|
+
requiresApiKey: true
|
|
45017
|
+
},
|
|
45018
|
+
xai: {
|
|
45019
|
+
provider: "xai",
|
|
45020
|
+
baseUrl: "https://api.x.ai/v1",
|
|
45021
|
+
supportsReasoning: true,
|
|
45022
|
+
reasoningFormat: "openai",
|
|
45023
|
+
apiPath: "/chat/completions",
|
|
45024
|
+
requiresApiKey: true
|
|
45025
|
+
},
|
|
45026
|
+
mistral: {
|
|
45027
|
+
provider: "mistral",
|
|
45028
|
+
baseUrl: "https://api.mistral.ai/v1",
|
|
45029
|
+
supportsReasoning: false,
|
|
45030
|
+
reasoningFormat: "none",
|
|
45031
|
+
apiPath: "/chat/completions",
|
|
45032
|
+
requiresApiKey: true
|
|
45033
|
+
}
|
|
45034
|
+
};
|
|
44879
45035
|
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
|
-
};
|
|
45036
|
+
if (provider in STATIC_PROFILES) {
|
|
45037
|
+
return STATIC_PROFILES[provider];
|
|
44954
45038
|
}
|
|
45039
|
+
if (provider === "ollama") {
|
|
45040
|
+
return {
|
|
45041
|
+
provider: "ollama",
|
|
45042
|
+
baseUrl: customBaseUrl || process.env.OLLAMA_BASE_URL || "http://localhost:11434/v1",
|
|
45043
|
+
supportsReasoning: true,
|
|
45044
|
+
reasoningFormat: "deepseek",
|
|
45045
|
+
apiPath: "/chat/completions",
|
|
45046
|
+
requiresApiKey: false
|
|
45047
|
+
};
|
|
45048
|
+
}
|
|
45049
|
+
return {
|
|
45050
|
+
provider: "custom",
|
|
45051
|
+
baseUrl: customBaseUrl || process.env.CUSTOM_API_URL || "http://localhost:11434/v1",
|
|
45052
|
+
supportsReasoning: true,
|
|
45053
|
+
reasoningFormat: "openai",
|
|
45054
|
+
apiPath: "/chat/completions",
|
|
45055
|
+
requiresApiKey: false
|
|
45056
|
+
};
|
|
44955
45057
|
}
|
|
44956
45058
|
function mapEffort(effort, format) {
|
|
44957
45059
|
if (effort === "none")
|
|
@@ -44974,13 +45076,13 @@ function convertMessages(messages) {
|
|
|
44974
45076
|
const converted = [];
|
|
44975
45077
|
for (const msg of messages) {
|
|
44976
45078
|
if (msg.role === "system") {
|
|
44977
|
-
converted.push({ role: "system", content: msg.content });
|
|
45079
|
+
converted.push({ role: "system", content: sanitizeSurrogates(msg.content) });
|
|
44978
45080
|
continue;
|
|
44979
45081
|
}
|
|
44980
45082
|
if (msg.role === "user") {
|
|
44981
45083
|
const text = typeof msg.content === "string" ? msg.content : msg.content.map((c) => c.text).join(`
|
|
44982
45084
|
`);
|
|
44983
|
-
converted.push({ role: "user", content: text });
|
|
45085
|
+
converted.push({ role: "user", content: sanitizeSurrogates(text) });
|
|
44984
45086
|
continue;
|
|
44985
45087
|
}
|
|
44986
45088
|
if (msg.role === "assistant") {
|
|
@@ -45005,9 +45107,9 @@ function convertMessages(messages) {
|
|
|
45005
45107
|
}
|
|
45006
45108
|
const item = { role: "assistant" };
|
|
45007
45109
|
if (text)
|
|
45008
|
-
item.content = text;
|
|
45110
|
+
item.content = sanitizeSurrogates(text);
|
|
45009
45111
|
if (reasoning_content)
|
|
45010
|
-
item.reasoning_content = reasoning_content;
|
|
45112
|
+
item.reasoning_content = sanitizeSurrogates(reasoning_content);
|
|
45011
45113
|
if (tool_calls.length > 0)
|
|
45012
45114
|
item.tool_calls = tool_calls;
|
|
45013
45115
|
converted.push(item);
|
|
@@ -45015,10 +45117,11 @@ function convertMessages(messages) {
|
|
|
45015
45117
|
}
|
|
45016
45118
|
if (msg.role === "tool") {
|
|
45017
45119
|
for (const res of msg.content) {
|
|
45120
|
+
const outStr = typeof res.output === "string" ? res.output : JSON.stringify(res.output);
|
|
45018
45121
|
converted.push({
|
|
45019
45122
|
role: "tool",
|
|
45020
45123
|
tool_call_id: res.toolCallId,
|
|
45021
|
-
content:
|
|
45124
|
+
content: sanitizeSurrogates(outStr)
|
|
45022
45125
|
});
|
|
45023
45126
|
}
|
|
45024
45127
|
}
|
|
@@ -45074,7 +45177,7 @@ function streamOpenAICompatible(options) {
|
|
|
45074
45177
|
});
|
|
45075
45178
|
} catch (err) {
|
|
45076
45179
|
const isAbort = request.abortSignal?.aborted;
|
|
45077
|
-
const error = new AIError(isAbort ? "Inference request aborted." : `Network request to ${profile.provider} failed: ${err.message}`, {
|
|
45180
|
+
const error = new AIError(isAbort ? "Inference request aborted." : `Network request to ${profile.provider} failed: ${err instanceof Error ? err.message : String(err)}`, {
|
|
45078
45181
|
code: isAbort ? "aborted" : "network",
|
|
45079
45182
|
provider: request.model.provider,
|
|
45080
45183
|
cause: err
|
|
@@ -45084,10 +45187,9 @@ function streamOpenAICompatible(options) {
|
|
|
45084
45187
|
return;
|
|
45085
45188
|
}
|
|
45086
45189
|
if (!response.ok) {
|
|
45087
|
-
const errBody = await response.text().catch(() => "");
|
|
45088
45190
|
const isRateLimit = response.status === 429;
|
|
45089
45191
|
const isAuth = response.status === 401 || response.status === 403;
|
|
45090
|
-
const error = new AIError(`${profile.provider}
|
|
45192
|
+
const error = new AIError(`${profile.provider} request failed with HTTP status ${response.status}`, {
|
|
45091
45193
|
code: isAuth ? auth?.type === "oauth" ? "oauth" : "auth" : isRateLimit ? "rate-limit" : "provider",
|
|
45092
45194
|
provider: request.model.provider,
|
|
45093
45195
|
status: response.status
|
|
@@ -45105,8 +45207,7 @@ function streamOpenAICompatible(options) {
|
|
|
45105
45207
|
yield { type: "error", error };
|
|
45106
45208
|
return;
|
|
45107
45209
|
}
|
|
45108
|
-
|
|
45109
|
-
let reasoningContent = "";
|
|
45210
|
+
const assistantContent = [];
|
|
45110
45211
|
const toolCallBuffers = new Map;
|
|
45111
45212
|
const usage = {
|
|
45112
45213
|
inputTokens: 0,
|
|
@@ -45124,19 +45225,24 @@ function streamOpenAICompatible(options) {
|
|
|
45124
45225
|
} catch {
|
|
45125
45226
|
continue;
|
|
45126
45227
|
}
|
|
45127
|
-
if (eventData.usage) {
|
|
45228
|
+
if (eventData.usage && typeof eventData.usage === "object") {
|
|
45128
45229
|
const u = eventData.usage;
|
|
45129
|
-
|
|
45130
|
-
|
|
45131
|
-
|
|
45132
|
-
|
|
45133
|
-
|
|
45230
|
+
if (typeof u.prompt_tokens === "number")
|
|
45231
|
+
usage.inputTokens = u.prompt_tokens;
|
|
45232
|
+
if (typeof u.completion_tokens === "number")
|
|
45233
|
+
usage.outputTokens = u.completion_tokens;
|
|
45234
|
+
if (typeof u.total_tokens === "number")
|
|
45235
|
+
usage.totalTokens = u.total_tokens;
|
|
45236
|
+
const details = u.completion_tokens_details;
|
|
45237
|
+
if (typeof details?.reasoning_tokens === "number") {
|
|
45238
|
+
usage.reasoningTokens = details.reasoning_tokens;
|
|
45134
45239
|
}
|
|
45135
45240
|
}
|
|
45136
|
-
const
|
|
45241
|
+
const choices = eventData.choices;
|
|
45242
|
+
const choice = choices?.[0];
|
|
45137
45243
|
if (!choice)
|
|
45138
45244
|
continue;
|
|
45139
|
-
if (choice.finish_reason) {
|
|
45245
|
+
if (choice.finish_reason && typeof choice.finish_reason === "string") {
|
|
45140
45246
|
const fr = choice.finish_reason;
|
|
45141
45247
|
if (fr === "stop")
|
|
45142
45248
|
finishReason = "stop";
|
|
@@ -45148,70 +45254,86 @@ function streamOpenAICompatible(options) {
|
|
|
45148
45254
|
const delta = choice.delta;
|
|
45149
45255
|
if (!delta)
|
|
45150
45256
|
continue;
|
|
45151
|
-
const
|
|
45257
|
+
const reasoningDetails = delta.reasoning_details;
|
|
45258
|
+
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
45259
|
if (reasoningDelta) {
|
|
45153
|
-
|
|
45260
|
+
const lastBlock = assistantContent[assistantContent.length - 1];
|
|
45261
|
+
if (lastBlock && lastBlock.type === "thinking") {
|
|
45262
|
+
lastBlock.thinking += reasoningDelta;
|
|
45263
|
+
} else {
|
|
45264
|
+
assistantContent.push({
|
|
45265
|
+
type: "thinking",
|
|
45266
|
+
thinking: reasoningDelta
|
|
45267
|
+
});
|
|
45268
|
+
}
|
|
45154
45269
|
yield { type: "reasoning-delta", delta: reasoningDelta };
|
|
45155
45270
|
}
|
|
45156
|
-
if (delta.content) {
|
|
45157
|
-
|
|
45158
|
-
|
|
45271
|
+
if (typeof delta.content === "string" && delta.content) {
|
|
45272
|
+
const textDelta = delta.content;
|
|
45273
|
+
const lastBlock = assistantContent[assistantContent.length - 1];
|
|
45274
|
+
if (lastBlock && lastBlock.type === "text") {
|
|
45275
|
+
lastBlock.text += textDelta;
|
|
45276
|
+
} else {
|
|
45277
|
+
assistantContent.push({
|
|
45278
|
+
type: "text",
|
|
45279
|
+
text: textDelta
|
|
45280
|
+
});
|
|
45281
|
+
}
|
|
45282
|
+
yield { type: "text-delta", delta: textDelta };
|
|
45159
45283
|
}
|
|
45160
|
-
if (
|
|
45284
|
+
if (Array.isArray(delta.tool_calls)) {
|
|
45161
45285
|
for (const tc of delta.tool_calls) {
|
|
45162
|
-
const idx = tc.index
|
|
45163
|
-
|
|
45164
|
-
|
|
45165
|
-
|
|
45166
|
-
|
|
45167
|
-
|
|
45286
|
+
const idx = typeof tc.index === "number" ? tc.index : 0;
|
|
45287
|
+
const fn = tc.function;
|
|
45288
|
+
const tcId = typeof tc.id === "string" ? tc.id : undefined;
|
|
45289
|
+
const fnName = typeof fn?.name === "string" ? fn.name : undefined;
|
|
45290
|
+
const fnArgs = typeof fn?.arguments === "string" ? fn.arguments : undefined;
|
|
45291
|
+
let entry = toolCallBuffers.get(idx);
|
|
45292
|
+
if (!entry) {
|
|
45293
|
+
const buffer = {
|
|
45294
|
+
id: tcId || `call_${idx}`,
|
|
45295
|
+
name: fnName || "",
|
|
45168
45296
|
rawArgs: ""
|
|
45169
45297
|
};
|
|
45170
|
-
|
|
45298
|
+
const slot = {
|
|
45299
|
+
type: "tool-call",
|
|
45300
|
+
id: buffer.id,
|
|
45301
|
+
name: buffer.name,
|
|
45302
|
+
arguments: {}
|
|
45303
|
+
};
|
|
45304
|
+
assistantContent.push(slot);
|
|
45305
|
+
entry = { buffer, slot };
|
|
45306
|
+
toolCallBuffers.set(idx, entry);
|
|
45171
45307
|
if (buffer.name) {
|
|
45172
45308
|
yield { type: "tool-call-start", id: buffer.id, name: buffer.name };
|
|
45173
45309
|
}
|
|
45174
45310
|
} else {
|
|
45175
|
-
if (
|
|
45176
|
-
buffer.
|
|
45177
|
-
|
|
45311
|
+
if (tcId && entry.buffer.id !== tcId) {
|
|
45312
|
+
entry.buffer.id = tcId;
|
|
45313
|
+
entry.slot.id = tcId;
|
|
45314
|
+
}
|
|
45315
|
+
if (!entry.buffer.name && fnName) {
|
|
45316
|
+
entry.buffer.name = fnName;
|
|
45317
|
+
entry.slot.name = fnName;
|
|
45318
|
+
yield { type: "tool-call-start", id: entry.buffer.id, name: entry.buffer.name };
|
|
45178
45319
|
}
|
|
45179
45320
|
}
|
|
45180
|
-
if (
|
|
45181
|
-
buffer.rawArgs +=
|
|
45321
|
+
if (fnArgs) {
|
|
45322
|
+
entry.buffer.rawArgs += fnArgs;
|
|
45182
45323
|
yield {
|
|
45183
45324
|
type: "tool-call-delta",
|
|
45184
|
-
id: buffer.id,
|
|
45185
|
-
delta:
|
|
45325
|
+
id: entry.buffer.id,
|
|
45326
|
+
delta: fnArgs
|
|
45186
45327
|
};
|
|
45187
45328
|
}
|
|
45188
45329
|
}
|
|
45189
45330
|
}
|
|
45190
45331
|
}
|
|
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);
|
|
45332
|
+
for (const entry of toolCallBuffers.values()) {
|
|
45333
|
+
const parsedArgs = parseStreamingJson(entry.buffer.rawArgs);
|
|
45334
|
+
entry.slot.arguments = parsedArgs;
|
|
45213
45335
|
finishReason = "tool-use";
|
|
45214
|
-
yield { type: "tool-call-end", toolCall };
|
|
45336
|
+
yield { type: "tool-call-end", toolCall: entry.slot };
|
|
45215
45337
|
}
|
|
45216
45338
|
usage.totalTokens = usage.totalTokens || usage.inputTokens + usage.outputTokens;
|
|
45217
45339
|
const finalMessage = {
|
|
@@ -45231,7 +45353,7 @@ function streamOpenAICompatible(options) {
|
|
|
45231
45353
|
};
|
|
45232
45354
|
} catch (err) {
|
|
45233
45355
|
const isAbort = request.abortSignal?.aborted;
|
|
45234
|
-
const error = new AIError(isAbort ? "Inference request aborted." : `${profile.provider} stream reading failed: ${err.message}`, {
|
|
45356
|
+
const error = new AIError(isAbort ? "Inference request aborted." : `${profile.provider} stream reading failed: ${err instanceof Error ? err.message : String(err)}`, {
|
|
45235
45357
|
code: isAbort ? "aborted" : "provider",
|
|
45236
45358
|
provider: request.model.provider,
|
|
45237
45359
|
cause: err
|
|
@@ -45265,7 +45387,7 @@ function streamOpenAI(options) {
|
|
|
45265
45387
|
request,
|
|
45266
45388
|
auth,
|
|
45267
45389
|
profile: {
|
|
45268
|
-
provider: "
|
|
45390
|
+
provider: "openai",
|
|
45269
45391
|
baseUrl: "https://api.openai.com/v1",
|
|
45270
45392
|
supportsReasoning: true,
|
|
45271
45393
|
reasoningFormat: "openai",
|
|
@@ -45302,11 +45424,11 @@ ${msg.content}` : msg.content;
|
|
|
45302
45424
|
}
|
|
45303
45425
|
if (msg.role === "user") {
|
|
45304
45426
|
if (typeof msg.content === "string") {
|
|
45305
|
-
anthropicMessages.push({ role: "user", content: msg.content });
|
|
45427
|
+
anthropicMessages.push({ role: "user", content: sanitizeSurrogates(msg.content) });
|
|
45306
45428
|
} else {
|
|
45307
45429
|
const text = msg.content.map((c) => c.text).join(`
|
|
45308
45430
|
`);
|
|
45309
|
-
anthropicMessages.push({ role: "user", content: text });
|
|
45431
|
+
anthropicMessages.push({ role: "user", content: sanitizeSurrogates(text) });
|
|
45310
45432
|
}
|
|
45311
45433
|
continue;
|
|
45312
45434
|
}
|
|
@@ -45314,11 +45436,11 @@ ${msg.content}` : msg.content;
|
|
|
45314
45436
|
const content = [];
|
|
45315
45437
|
for (const block of msg.content) {
|
|
45316
45438
|
if (block.type === "text") {
|
|
45317
|
-
content.push({ type: "text", text: block.text });
|
|
45439
|
+
content.push({ type: "text", text: sanitizeSurrogates(block.text) });
|
|
45318
45440
|
} else if (block.type === "thinking") {
|
|
45319
45441
|
content.push({
|
|
45320
45442
|
type: "thinking",
|
|
45321
|
-
thinking: block.thinking,
|
|
45443
|
+
thinking: sanitizeSurrogates(block.thinking),
|
|
45322
45444
|
signature: block.thinkingSignature
|
|
45323
45445
|
});
|
|
45324
45446
|
} else if (block.type === "tool-call") {
|
|
@@ -45336,10 +45458,11 @@ ${msg.content}` : msg.content;
|
|
|
45336
45458
|
if (msg.role === "tool") {
|
|
45337
45459
|
const content = [];
|
|
45338
45460
|
for (const res of msg.content) {
|
|
45461
|
+
const outStr = typeof res.output === "string" ? res.output : JSON.stringify(res.output);
|
|
45339
45462
|
content.push({
|
|
45340
45463
|
type: "tool_result",
|
|
45341
45464
|
tool_use_id: res.toolCallId,
|
|
45342
|
-
content:
|
|
45465
|
+
content: sanitizeSurrogates(outStr),
|
|
45343
45466
|
is_error: res.isError
|
|
45344
45467
|
});
|
|
45345
45468
|
}
|
|
@@ -45352,10 +45475,11 @@ function streamAnthropic(options) {
|
|
|
45352
45475
|
const { request, auth } = options;
|
|
45353
45476
|
const thinkingConfig = mapAnthropicEffort(request.model.effort);
|
|
45354
45477
|
const { system, anthropicMessages } = convertMessages2(request.messages);
|
|
45355
|
-
const tools = request.tools?.map((t) => ({
|
|
45478
|
+
const tools = request.tools?.map((t, idx, arr) => ({
|
|
45356
45479
|
name: t.name,
|
|
45357
45480
|
description: t.description,
|
|
45358
|
-
input_schema: t.inputSchema
|
|
45481
|
+
input_schema: t.inputSchema,
|
|
45482
|
+
...idx === arr.length - 1 ? { cache_control: { type: "ephemeral" } } : {}
|
|
45359
45483
|
}));
|
|
45360
45484
|
const body = {
|
|
45361
45485
|
model: request.model.modelId,
|
|
@@ -45364,7 +45488,13 @@ function streamAnthropic(options) {
|
|
|
45364
45488
|
stream: true
|
|
45365
45489
|
};
|
|
45366
45490
|
if (system) {
|
|
45367
|
-
body.system =
|
|
45491
|
+
body.system = [
|
|
45492
|
+
{
|
|
45493
|
+
type: "text",
|
|
45494
|
+
text: sanitizeSurrogates(system),
|
|
45495
|
+
cache_control: { type: "ephemeral" }
|
|
45496
|
+
}
|
|
45497
|
+
];
|
|
45368
45498
|
}
|
|
45369
45499
|
if (tools && tools.length > 0) {
|
|
45370
45500
|
body.tools = tools;
|
|
@@ -45379,7 +45509,7 @@ function streamAnthropic(options) {
|
|
|
45379
45509
|
"Content-Type": "application/json",
|
|
45380
45510
|
Accept: "text/event-stream",
|
|
45381
45511
|
"anthropic-version": "2023-06-01",
|
|
45382
|
-
...auth.headers
|
|
45512
|
+
...auth.headers || {}
|
|
45383
45513
|
};
|
|
45384
45514
|
if (auth.type === "api-key") {
|
|
45385
45515
|
headers["x-api-key"] = auth.token;
|
|
@@ -45399,7 +45529,7 @@ function streamAnthropic(options) {
|
|
|
45399
45529
|
});
|
|
45400
45530
|
} catch (err) {
|
|
45401
45531
|
const isAbort = request.abortSignal?.aborted;
|
|
45402
|
-
const error = new AIError(isAbort ? "Inference request aborted." : `Network request to Anthropic failed: ${err.message}`, {
|
|
45532
|
+
const error = new AIError(isAbort ? "Inference request aborted." : `Network request to Anthropic failed: ${err instanceof Error ? err.message : String(err)}`, {
|
|
45403
45533
|
code: isAbort ? "aborted" : "network",
|
|
45404
45534
|
provider: "anthropic",
|
|
45405
45535
|
cause: err
|
|
@@ -45409,10 +45539,9 @@ function streamAnthropic(options) {
|
|
|
45409
45539
|
return;
|
|
45410
45540
|
}
|
|
45411
45541
|
if (!response.ok) {
|
|
45412
|
-
const errBody = await response.text().catch(() => "");
|
|
45413
45542
|
const isRateLimit = response.status === 429;
|
|
45414
45543
|
const isAuth = response.status === 401 || response.status === 403;
|
|
45415
|
-
const error = new AIError(`Anthropic
|
|
45544
|
+
const error = new AIError(`Anthropic request failed with HTTP status ${response.status}`, {
|
|
45416
45545
|
code: isAuth ? auth.type === "oauth" ? "oauth" : "auth" : isRateLimit ? "rate-limit" : "provider",
|
|
45417
45546
|
provider: "anthropic",
|
|
45418
45547
|
status: response.status
|
|
@@ -45449,69 +45578,91 @@ function streamAnthropic(options) {
|
|
|
45449
45578
|
} catch {
|
|
45450
45579
|
continue;
|
|
45451
45580
|
}
|
|
45452
|
-
const eventType = sse.event || eventData.type;
|
|
45581
|
+
const eventType = typeof sse.event === "string" && sse.event || typeof eventData.type === "string" && eventData.type || "";
|
|
45453
45582
|
switch (eventType) {
|
|
45454
45583
|
case "message_start": {
|
|
45455
|
-
|
|
45456
|
-
|
|
45457
|
-
|
|
45458
|
-
|
|
45459
|
-
|
|
45584
|
+
const msg = eventData.message;
|
|
45585
|
+
if (msg?.usage && typeof msg.usage === "object") {
|
|
45586
|
+
const u = msg.usage;
|
|
45587
|
+
if (typeof u.input_tokens === "number")
|
|
45588
|
+
usage.inputTokens = u.input_tokens;
|
|
45589
|
+
if (typeof u.cache_read_input_tokens === "number")
|
|
45590
|
+
usage.cacheReadTokens = u.cache_read_input_tokens;
|
|
45591
|
+
if (typeof u.cache_creation_input_tokens === "number")
|
|
45592
|
+
usage.cacheWriteTokens = u.cache_creation_input_tokens;
|
|
45460
45593
|
}
|
|
45461
45594
|
break;
|
|
45462
45595
|
}
|
|
45463
45596
|
case "content_block_start": {
|
|
45464
45597
|
const block = eventData.content_block;
|
|
45598
|
+
if (!block || typeof block !== "object" || typeof block.type !== "string") {
|
|
45599
|
+
const error = new AIError("Malformed Anthropic content_block_start event", {
|
|
45600
|
+
code: "parse",
|
|
45601
|
+
provider: "anthropic"
|
|
45602
|
+
});
|
|
45603
|
+
errorResult = error;
|
|
45604
|
+
yield { type: "error", error };
|
|
45605
|
+
return;
|
|
45606
|
+
}
|
|
45465
45607
|
if (block.type === "text") {
|
|
45466
|
-
|
|
45467
|
-
|
|
45468
|
-
|
|
45608
|
+
const blockText = typeof block.text === "string" ? block.text : "";
|
|
45609
|
+
currentBlock = { type: "text", text: blockText };
|
|
45610
|
+
if (blockText) {
|
|
45611
|
+
yield { type: "text-delta", delta: blockText };
|
|
45469
45612
|
}
|
|
45470
45613
|
} else if (block.type === "thinking") {
|
|
45614
|
+
const blockThinking = typeof block.thinking === "string" ? block.thinking : "";
|
|
45615
|
+
const blockSig = typeof block.signature === "string" ? block.signature : undefined;
|
|
45471
45616
|
currentBlock = {
|
|
45472
45617
|
type: "thinking",
|
|
45473
|
-
thinking:
|
|
45474
|
-
signature:
|
|
45618
|
+
thinking: blockThinking,
|
|
45619
|
+
signature: blockSig
|
|
45475
45620
|
};
|
|
45476
|
-
if (
|
|
45477
|
-
yield { type: "reasoning-delta", delta:
|
|
45621
|
+
if (blockThinking) {
|
|
45622
|
+
yield { type: "reasoning-delta", delta: blockThinking };
|
|
45478
45623
|
}
|
|
45479
45624
|
} else if (block.type === "tool_use") {
|
|
45625
|
+
const blockId = typeof block.id === "string" ? block.id : "";
|
|
45626
|
+
const blockName = typeof block.name === "string" ? block.name : "";
|
|
45480
45627
|
currentBlock = {
|
|
45481
45628
|
type: "tool-call",
|
|
45482
|
-
id:
|
|
45483
|
-
name:
|
|
45629
|
+
id: blockId,
|
|
45630
|
+
name: blockName,
|
|
45484
45631
|
rawArgs: ""
|
|
45485
45632
|
};
|
|
45486
|
-
yield { type: "tool-call-start", id:
|
|
45633
|
+
yield { type: "tool-call-start", id: blockId, name: blockName };
|
|
45487
45634
|
}
|
|
45488
45635
|
break;
|
|
45489
45636
|
}
|
|
45490
45637
|
case "content_block_delta": {
|
|
45491
45638
|
const delta = eventData.delta;
|
|
45492
|
-
if (!delta)
|
|
45639
|
+
if (!delta || typeof delta.type !== "string")
|
|
45493
45640
|
break;
|
|
45494
45641
|
if (delta.type === "text_delta") {
|
|
45642
|
+
const deltaText = typeof delta.text === "string" ? delta.text : "";
|
|
45495
45643
|
if (currentBlock && currentBlock.type === "text") {
|
|
45496
|
-
currentBlock.text +=
|
|
45644
|
+
currentBlock.text += deltaText;
|
|
45497
45645
|
}
|
|
45498
|
-
yield { type: "text-delta", delta:
|
|
45646
|
+
yield { type: "text-delta", delta: deltaText };
|
|
45499
45647
|
} else if (delta.type === "thinking_delta") {
|
|
45648
|
+
const deltaThinking = typeof delta.thinking === "string" ? delta.thinking : "";
|
|
45500
45649
|
if (currentBlock && currentBlock.type === "thinking") {
|
|
45501
|
-
currentBlock.thinking +=
|
|
45650
|
+
currentBlock.thinking += deltaThinking;
|
|
45502
45651
|
}
|
|
45503
|
-
yield { type: "reasoning-delta", delta:
|
|
45652
|
+
yield { type: "reasoning-delta", delta: deltaThinking };
|
|
45504
45653
|
} else if (delta.type === "signature_delta") {
|
|
45654
|
+
const deltaSig = typeof delta.signature === "string" ? delta.signature : "";
|
|
45505
45655
|
if (currentBlock && currentBlock.type === "thinking") {
|
|
45506
|
-
currentBlock.signature = (currentBlock.signature || "") +
|
|
45656
|
+
currentBlock.signature = (currentBlock.signature || "") + deltaSig;
|
|
45507
45657
|
}
|
|
45508
45658
|
} else if (delta.type === "input_json_delta") {
|
|
45659
|
+
const partialJson = typeof delta.partial_json === "string" ? delta.partial_json : "";
|
|
45509
45660
|
if (currentBlock && currentBlock.type === "tool-call") {
|
|
45510
|
-
currentBlock.rawArgs +=
|
|
45661
|
+
currentBlock.rawArgs += partialJson;
|
|
45511
45662
|
yield {
|
|
45512
45663
|
type: "tool-call-delta",
|
|
45513
45664
|
id: currentBlock.id,
|
|
45514
|
-
delta:
|
|
45665
|
+
delta: partialJson
|
|
45515
45666
|
};
|
|
45516
45667
|
}
|
|
45517
45668
|
}
|
|
@@ -45546,8 +45697,9 @@ function streamAnthropic(options) {
|
|
|
45546
45697
|
break;
|
|
45547
45698
|
}
|
|
45548
45699
|
case "message_delta": {
|
|
45549
|
-
|
|
45550
|
-
|
|
45700
|
+
const delta = eventData.delta;
|
|
45701
|
+
if (delta && typeof delta.stop_reason === "string") {
|
|
45702
|
+
const sr = delta.stop_reason;
|
|
45551
45703
|
if (sr === "end_turn" || sr === "stop_sequence")
|
|
45552
45704
|
finishReason = "stop";
|
|
45553
45705
|
else if (sr === "max_tokens")
|
|
@@ -45555,8 +45707,9 @@ function streamAnthropic(options) {
|
|
|
45555
45707
|
else if (sr === "tool_use")
|
|
45556
45708
|
finishReason = "tool-use";
|
|
45557
45709
|
}
|
|
45558
|
-
|
|
45559
|
-
|
|
45710
|
+
const u = eventData.usage;
|
|
45711
|
+
if (typeof u?.output_tokens === "number") {
|
|
45712
|
+
usage.outputTokens = u.output_tokens;
|
|
45560
45713
|
}
|
|
45561
45714
|
break;
|
|
45562
45715
|
}
|
|
@@ -45566,7 +45719,8 @@ function streamAnthropic(options) {
|
|
|
45566
45719
|
}
|
|
45567
45720
|
case "error": {
|
|
45568
45721
|
const err = eventData.error;
|
|
45569
|
-
const
|
|
45722
|
+
const errMsg = typeof err?.message === "string" ? err.message : "Unknown provider error";
|
|
45723
|
+
const error = new AIError(`Anthropic stream error: ${errMsg}`, {
|
|
45570
45724
|
code: "provider",
|
|
45571
45725
|
provider: "anthropic"
|
|
45572
45726
|
});
|
|
@@ -45603,7 +45757,7 @@ function streamAnthropic(options) {
|
|
|
45603
45757
|
};
|
|
45604
45758
|
} catch (err) {
|
|
45605
45759
|
const isAbort = request.abortSignal?.aborted;
|
|
45606
|
-
const error = new AIError(isAbort ? "Inference request aborted." : `Stream reading failed: ${err.message}`, {
|
|
45760
|
+
const error = new AIError(isAbort ? "Inference request aborted." : `Stream reading failed: ${err instanceof Error ? err.message : String(err)}`, {
|
|
45607
45761
|
code: isAbort ? "aborted" : "provider",
|
|
45608
45762
|
provider: "anthropic",
|
|
45609
45763
|
cause: err
|
|
@@ -45661,7 +45815,7 @@ function sanitizeSchema(schema) {
|
|
|
45661
45815
|
function mapGeminiEffort(effort) {
|
|
45662
45816
|
switch (effort) {
|
|
45663
45817
|
case "none":
|
|
45664
|
-
return
|
|
45818
|
+
return;
|
|
45665
45819
|
case "low":
|
|
45666
45820
|
return { thinkingBudget: 2048 };
|
|
45667
45821
|
case "medium":
|
|
@@ -45677,13 +45831,14 @@ function convertMessages3(messages) {
|
|
|
45677
45831
|
const contents = [];
|
|
45678
45832
|
for (const msg of messages) {
|
|
45679
45833
|
if (msg.role === "system") {
|
|
45834
|
+
const sanitized = sanitizeSurrogates(msg.content);
|
|
45680
45835
|
systemText = systemText ? `${systemText}
|
|
45681
45836
|
|
|
45682
|
-
${
|
|
45837
|
+
${sanitized}` : sanitized;
|
|
45683
45838
|
continue;
|
|
45684
45839
|
}
|
|
45685
45840
|
if (msg.role === "user") {
|
|
45686
|
-
const text = typeof msg.content === "string" ? msg.content : msg.content.map((c) => c.text).join(`
|
|
45841
|
+
const text = typeof msg.content === "string" ? sanitizeSurrogates(msg.content) : msg.content.map((c) => sanitizeSurrogates(c.text)).join(`
|
|
45687
45842
|
`);
|
|
45688
45843
|
contents.push({
|
|
45689
45844
|
role: "user",
|
|
@@ -45695,19 +45850,19 @@ ${msg.content}` : msg.content;
|
|
|
45695
45850
|
const parts = [];
|
|
45696
45851
|
for (const block of msg.content) {
|
|
45697
45852
|
if (block.type === "text") {
|
|
45698
|
-
if (!block.text && !block.
|
|
45853
|
+
if (!block.text && !block.textSignature)
|
|
45699
45854
|
continue;
|
|
45700
45855
|
parts.push({
|
|
45701
|
-
text: block.text || "",
|
|
45702
|
-
...block.
|
|
45856
|
+
text: sanitizeSurrogates(block.text || ""),
|
|
45857
|
+
...block.textSignature ? { thoughtSignature: block.textSignature } : {}
|
|
45703
45858
|
});
|
|
45704
45859
|
} else if (block.type === "thinking") {
|
|
45705
|
-
if (!block.thinking && !block.
|
|
45860
|
+
if (!block.thinking && !block.thinkingSignature)
|
|
45706
45861
|
continue;
|
|
45707
45862
|
parts.push({
|
|
45708
45863
|
thought: true,
|
|
45709
|
-
text: block.thinking || "",
|
|
45710
|
-
...block.
|
|
45864
|
+
text: sanitizeSurrogates(block.thinking || ""),
|
|
45865
|
+
...block.thinkingSignature ? { thoughtSignature: block.thinkingSignature } : {}
|
|
45711
45866
|
});
|
|
45712
45867
|
} else if (block.type === "tool-call") {
|
|
45713
45868
|
parts.push({
|
|
@@ -45730,11 +45885,12 @@ ${msg.content}` : msg.content;
|
|
|
45730
45885
|
if (msg.role === "tool") {
|
|
45731
45886
|
const parts = [];
|
|
45732
45887
|
for (const res of msg.content) {
|
|
45888
|
+
const outStr = typeof res.output === "string" ? res.output : JSON.stringify(res.output);
|
|
45733
45889
|
parts.push({
|
|
45734
45890
|
functionResponse: {
|
|
45735
45891
|
name: res.toolName,
|
|
45736
45892
|
response: {
|
|
45737
|
-
result:
|
|
45893
|
+
result: sanitizeSurrogates(outStr)
|
|
45738
45894
|
}
|
|
45739
45895
|
}
|
|
45740
45896
|
});
|
|
@@ -45783,7 +45939,15 @@ function streamGemini(options) {
|
|
|
45783
45939
|
body.tools = tools;
|
|
45784
45940
|
}
|
|
45785
45941
|
const modelId = request.model.modelId.replace(/^models\//, "");
|
|
45786
|
-
const url = `https://generativelanguage.googleapis.com/v1beta/models/${modelId}:streamGenerateContent?alt=sse
|
|
45942
|
+
const url = `https://generativelanguage.googleapis.com/v1beta/models/${modelId}:streamGenerateContent?alt=sse`;
|
|
45943
|
+
const headers = {
|
|
45944
|
+
"Content-Type": "application/json",
|
|
45945
|
+
Accept: "text/event-stream",
|
|
45946
|
+
...auth.headers || {}
|
|
45947
|
+
};
|
|
45948
|
+
if (auth.token && auth.token !== "none") {
|
|
45949
|
+
headers["x-goog-api-key"] = auth.token;
|
|
45950
|
+
}
|
|
45787
45951
|
let finalResult;
|
|
45788
45952
|
let errorResult;
|
|
45789
45953
|
async function* eventGenerator() {
|
|
@@ -45791,16 +45955,13 @@ function streamGemini(options) {
|
|
|
45791
45955
|
try {
|
|
45792
45956
|
response = await fetch(url, {
|
|
45793
45957
|
method: "POST",
|
|
45794
|
-
headers
|
|
45795
|
-
"Content-Type": "application/json",
|
|
45796
|
-
Accept: "text/event-stream"
|
|
45797
|
-
},
|
|
45958
|
+
headers,
|
|
45798
45959
|
body: JSON.stringify(body),
|
|
45799
45960
|
signal: request.abortSignal
|
|
45800
45961
|
});
|
|
45801
45962
|
} catch (err) {
|
|
45802
45963
|
const isAbort = request.abortSignal?.aborted;
|
|
45803
|
-
const error = new AIError(isAbort ? "Inference request aborted." : `Network request to Gemini failed: ${err.message}`, {
|
|
45964
|
+
const error = new AIError(isAbort ? "Inference request aborted." : `Network request to Gemini failed: ${err instanceof Error ? err.message : String(err)}`, {
|
|
45804
45965
|
code: isAbort ? "aborted" : "network",
|
|
45805
45966
|
provider: "gemini",
|
|
45806
45967
|
cause: err
|
|
@@ -45810,10 +45971,9 @@ function streamGemini(options) {
|
|
|
45810
45971
|
return;
|
|
45811
45972
|
}
|
|
45812
45973
|
if (!response.ok) {
|
|
45813
|
-
const errBody = await response.text().catch(() => "");
|
|
45814
45974
|
const isRateLimit = response.status === 429;
|
|
45815
45975
|
const isAuth = response.status === 401 || response.status === 403;
|
|
45816
|
-
const error = new AIError(`Gemini
|
|
45976
|
+
const error = new AIError(`Gemini request failed with HTTP status ${response.status}`, {
|
|
45817
45977
|
code: isAuth ? "auth" : isRateLimit ? "rate-limit" : "provider",
|
|
45818
45978
|
provider: "gemini",
|
|
45819
45979
|
status: response.status
|
|
@@ -45849,15 +46009,19 @@ function streamGemini(options) {
|
|
|
45849
46009
|
} catch {
|
|
45850
46010
|
continue;
|
|
45851
46011
|
}
|
|
45852
|
-
if (eventData.usageMetadata) {
|
|
46012
|
+
if (eventData.usageMetadata && typeof eventData.usageMetadata === "object") {
|
|
45853
46013
|
const u = eventData.usageMetadata;
|
|
45854
|
-
|
|
45855
|
-
|
|
45856
|
-
|
|
45857
|
-
|
|
45858
|
-
|
|
46014
|
+
if (typeof u.promptTokenCount === "number")
|
|
46015
|
+
usage.inputTokens = u.promptTokenCount;
|
|
46016
|
+
if (typeof u.candidatesTokenCount === "number")
|
|
46017
|
+
usage.outputTokens = u.candidatesTokenCount;
|
|
46018
|
+
if (typeof u.totalTokenCount === "number")
|
|
46019
|
+
usage.totalTokens = u.totalTokenCount;
|
|
46020
|
+
}
|
|
46021
|
+
const candidates = eventData.candidates;
|
|
46022
|
+
const candidate = candidates?.[0];
|
|
45859
46023
|
if (candidate) {
|
|
45860
|
-
if (candidate.finishReason) {
|
|
46024
|
+
if (typeof candidate.finishReason === "string") {
|
|
45861
46025
|
const fr = candidate.finishReason;
|
|
45862
46026
|
if (fr === "STOP")
|
|
45863
46027
|
finishReason = "stop";
|
|
@@ -45866,21 +46030,25 @@ function streamGemini(options) {
|
|
|
45866
46030
|
else if (fr === "SAFETY")
|
|
45867
46031
|
finishReason = "error";
|
|
45868
46032
|
}
|
|
45869
|
-
|
|
45870
|
-
|
|
46033
|
+
const content = candidate.content;
|
|
46034
|
+
if (Array.isArray(content?.parts)) {
|
|
46035
|
+
for (const rawPart of content.parts) {
|
|
46036
|
+
const part = rawPart;
|
|
46037
|
+
const thoughtSig = typeof part.thoughtSignature === "string" ? part.thoughtSignature : undefined;
|
|
45871
46038
|
if (part.thought) {
|
|
45872
|
-
const thoughtDelta = part.text
|
|
45873
|
-
if (thoughtDelta ||
|
|
45874
|
-
const
|
|
45875
|
-
if (
|
|
45876
|
-
|
|
45877
|
-
if (
|
|
45878
|
-
|
|
46039
|
+
const thoughtDelta = typeof part.text === "string" ? part.text : "";
|
|
46040
|
+
if (thoughtDelta || thoughtSig) {
|
|
46041
|
+
const lastBlock = assistantContent[assistantContent.length - 1];
|
|
46042
|
+
if (lastBlock && lastBlock.type === "thinking") {
|
|
46043
|
+
lastBlock.thinking += thoughtDelta;
|
|
46044
|
+
if (thoughtSig) {
|
|
46045
|
+
lastBlock.thinkingSignature = thoughtSig;
|
|
46046
|
+
}
|
|
45879
46047
|
} else {
|
|
45880
46048
|
assistantContent.push({
|
|
45881
46049
|
type: "thinking",
|
|
45882
46050
|
thinking: thoughtDelta,
|
|
45883
|
-
|
|
46051
|
+
...thoughtSig ? { thinkingSignature: thoughtSig } : {}
|
|
45884
46052
|
});
|
|
45885
46053
|
}
|
|
45886
46054
|
if (thoughtDelta) {
|
|
@@ -45888,33 +46056,37 @@ function streamGemini(options) {
|
|
|
45888
46056
|
}
|
|
45889
46057
|
}
|
|
45890
46058
|
} else if (part.text !== undefined) {
|
|
45891
|
-
const textDelta = part.text
|
|
45892
|
-
if (textDelta ||
|
|
45893
|
-
const
|
|
45894
|
-
if (
|
|
45895
|
-
|
|
45896
|
-
if (
|
|
45897
|
-
|
|
46059
|
+
const textDelta = typeof part.text === "string" ? part.text : "";
|
|
46060
|
+
if (textDelta || thoughtSig) {
|
|
46061
|
+
const lastBlock = assistantContent[assistantContent.length - 1];
|
|
46062
|
+
if (lastBlock && lastBlock.type === "text") {
|
|
46063
|
+
lastBlock.text += textDelta;
|
|
46064
|
+
if (thoughtSig) {
|
|
46065
|
+
lastBlock.textSignature = thoughtSig;
|
|
46066
|
+
}
|
|
45898
46067
|
} else {
|
|
45899
46068
|
assistantContent.push({
|
|
45900
46069
|
type: "text",
|
|
45901
46070
|
text: textDelta,
|
|
45902
|
-
|
|
46071
|
+
...thoughtSig ? { textSignature: thoughtSig } : {}
|
|
45903
46072
|
});
|
|
45904
46073
|
}
|
|
45905
46074
|
if (textDelta) {
|
|
45906
46075
|
yield { type: "text-delta", delta: textDelta };
|
|
45907
46076
|
}
|
|
45908
46077
|
}
|
|
45909
|
-
} else if (part.functionCall) {
|
|
46078
|
+
} else if (part.functionCall && typeof part.functionCall === "object") {
|
|
46079
|
+
const fc = part.functionCall;
|
|
45910
46080
|
toolCallIdx++;
|
|
45911
|
-
const toolId = `call_${
|
|
46081
|
+
const toolId = `call_${toolCallIdx}`;
|
|
46082
|
+
const fnName = typeof fc.name === "string" ? fc.name : "";
|
|
46083
|
+
const fnArgs = typeof fc.args === "object" && fc.args !== null ? fc.args : {};
|
|
45912
46084
|
const toolCall = {
|
|
45913
46085
|
type: "tool-call",
|
|
45914
46086
|
id: toolId,
|
|
45915
|
-
name:
|
|
45916
|
-
arguments:
|
|
45917
|
-
thoughtSignature:
|
|
46087
|
+
name: fnName,
|
|
46088
|
+
arguments: fnArgs,
|
|
46089
|
+
...thoughtSig ? { thoughtSignature: thoughtSig } : {}
|
|
45918
46090
|
};
|
|
45919
46091
|
assistantContent.push(toolCall);
|
|
45920
46092
|
finishReason = "tool-use";
|
|
@@ -45947,7 +46119,7 @@ function streamGemini(options) {
|
|
|
45947
46119
|
};
|
|
45948
46120
|
} catch (err) {
|
|
45949
46121
|
const isAbort = request.abortSignal?.aborted;
|
|
45950
|
-
const error = new AIError(isAbort ? "Inference request aborted." : `Gemini stream reading failed: ${err.message}`, {
|
|
46122
|
+
const error = new AIError(isAbort ? "Inference request aborted." : `Gemini stream reading failed: ${err instanceof Error ? err.message : String(err)}`, {
|
|
45951
46123
|
code: isAbort ? "aborted" : "provider",
|
|
45952
46124
|
provider: "gemini",
|
|
45953
46125
|
cause: err
|
|
@@ -45977,8 +46149,16 @@ function streamGemini(options) {
|
|
|
45977
46149
|
// src/packages/ai/src/inference.ts
|
|
45978
46150
|
function streamInference(request, authManager = createAuthManager(), customBaseUrl) {
|
|
45979
46151
|
const provider = request.model.provider;
|
|
45980
|
-
let streamPromise = null;
|
|
45981
46152
|
const getStream = async () => {
|
|
46153
|
+
let descriptor;
|
|
46154
|
+
try {
|
|
46155
|
+
descriptor = getProviderDescriptor(provider);
|
|
46156
|
+
} catch {
|
|
46157
|
+
throw new AIError(`Unsupported provider: "${provider}"`, {
|
|
46158
|
+
code: "invalid-request",
|
|
46159
|
+
provider
|
|
46160
|
+
});
|
|
46161
|
+
}
|
|
45982
46162
|
const resolvedAuth = await authManager.resolve(provider, request.abortSignal);
|
|
45983
46163
|
if (!resolvedAuth && provider !== "custom" && provider !== "ollama") {
|
|
45984
46164
|
throw new AIError(`No credentials configured for provider: ${provider}`, {
|
|
@@ -45991,30 +46171,28 @@ function streamInference(request, authManager = createAuthManager(), customBaseU
|
|
|
45991
46171
|
token: "none",
|
|
45992
46172
|
source: "custom"
|
|
45993
46173
|
};
|
|
45994
|
-
switch (
|
|
45995
|
-
case "anthropic":
|
|
46174
|
+
switch (descriptor.protocol) {
|
|
46175
|
+
case "anthropic-messages":
|
|
45996
46176
|
return streamAnthropic({ request, auth });
|
|
45997
46177
|
case "gemini":
|
|
45998
46178
|
return streamGemini({ request, auth });
|
|
45999
|
-
case "openai":
|
|
46179
|
+
case "openai-chat":
|
|
46000
46180
|
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":
|
|
46181
|
+
case "openai-compatible":
|
|
46009
46182
|
return streamOpenAICompatible({ request, auth, customBaseUrl });
|
|
46183
|
+
case "openai-responses":
|
|
46184
|
+
throw new AIError(`Protocol "openai-responses" is not yet implemented for provider "${provider}"`, {
|
|
46185
|
+
code: "invalid-request",
|
|
46186
|
+
provider
|
|
46187
|
+
});
|
|
46010
46188
|
default:
|
|
46011
|
-
throw new AIError(`Unsupported provider
|
|
46189
|
+
throw new AIError(`Unsupported protocol "${descriptor.protocol}" for provider "${provider}"`, {
|
|
46012
46190
|
code: "invalid-request",
|
|
46013
46191
|
provider
|
|
46014
46192
|
});
|
|
46015
46193
|
}
|
|
46016
46194
|
};
|
|
46017
|
-
streamPromise = getStream();
|
|
46195
|
+
const streamPromise = getStream();
|
|
46018
46196
|
return {
|
|
46019
46197
|
async* [Symbol.asyncIterator]() {
|
|
46020
46198
|
const activeStream = await streamPromise;
|
|
@@ -46058,85 +46236,91 @@ function createAIEngine(options) {
|
|
|
46058
46236
|
// src/packages/ai/src/models/discovery.ts
|
|
46059
46237
|
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
46238
|
var DEFAULT_FETCH_TIMEOUT_MS = 1e4;
|
|
46061
|
-
async function
|
|
46062
|
-
const
|
|
46239
|
+
async function fetchStandardModelsList(options) {
|
|
46240
|
+
const { provider, url, headers, timeoutMs, transformId, filterItem } = options;
|
|
46241
|
+
const response = await fetch(url, {
|
|
46063
46242
|
method: "GET",
|
|
46064
|
-
headers
|
|
46065
|
-
Authorization: `Bearer ${apiKey}`
|
|
46066
|
-
},
|
|
46243
|
+
headers,
|
|
46067
46244
|
signal: AbortSignal.timeout(timeoutMs)
|
|
46068
46245
|
});
|
|
46069
46246
|
if (!response.ok) {
|
|
46070
|
-
throw new Error(
|
|
46247
|
+
throw new Error(`${provider} API request failed: HTTP ${response.status} (${response.statusText})`);
|
|
46071
46248
|
}
|
|
46072
46249
|
const payload = await response.json();
|
|
46073
46250
|
if (!Array.isArray(payload.data)) {
|
|
46074
46251
|
return [];
|
|
46075
46252
|
}
|
|
46076
|
-
|
|
46253
|
+
const models = [];
|
|
46254
|
+
for (const item of payload.data) {
|
|
46255
|
+
if (!item || typeof item.id !== "string")
|
|
46256
|
+
continue;
|
|
46257
|
+
if (filterItem && !filterItem(item))
|
|
46258
|
+
continue;
|
|
46259
|
+
const finalId = transformId ? transformId(item.id) : item.id;
|
|
46260
|
+
if (!finalId || NON_CHAT_MODEL_KEYWORDS.test(finalId))
|
|
46261
|
+
continue;
|
|
46262
|
+
models.push({
|
|
46263
|
+
provider,
|
|
46264
|
+
modelId: finalId
|
|
46265
|
+
});
|
|
46266
|
+
}
|
|
46267
|
+
return models.sort((a, b) => a.modelId.localeCompare(b.modelId));
|
|
46268
|
+
}
|
|
46269
|
+
async function fetchOpenAIModels(apiKey, timeoutMs = DEFAULT_FETCH_TIMEOUT_MS) {
|
|
46270
|
+
return await fetchStandardModelsList({
|
|
46077
46271
|
provider: "openai",
|
|
46078
|
-
|
|
46079
|
-
|
|
46272
|
+
url: "https://api.openai.com/v1/models",
|
|
46273
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
46274
|
+
timeoutMs
|
|
46275
|
+
});
|
|
46080
46276
|
}
|
|
46081
46277
|
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) => ({
|
|
46278
|
+
return await fetchStandardModelsList({
|
|
46097
46279
|
provider: "gemini",
|
|
46098
|
-
|
|
46099
|
-
|
|
46280
|
+
url: "https://generativelanguage.googleapis.com/v1beta/openai/models",
|
|
46281
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
46282
|
+
timeoutMs,
|
|
46283
|
+
transformId: (id) => id.replace(/^models\//, "")
|
|
46284
|
+
});
|
|
46100
46285
|
}
|
|
46101
46286
|
async function fetchAnthropicModels(apiKey, timeoutMs = DEFAULT_FETCH_TIMEOUT_MS) {
|
|
46102
|
-
|
|
46103
|
-
|
|
46287
|
+
return await fetchStandardModelsList({
|
|
46288
|
+
provider: "anthropic",
|
|
46289
|
+
url: "https://api.anthropic.com/v1/models",
|
|
46104
46290
|
headers: {
|
|
46105
46291
|
"x-api-key": apiKey,
|
|
46106
46292
|
"anthropic-version": "2023-06-01"
|
|
46107
46293
|
},
|
|
46108
|
-
|
|
46294
|
+
timeoutMs
|
|
46109
46295
|
});
|
|
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
46296
|
}
|
|
46122
46297
|
async function fetchDeepSeekModels(apiKey, timeoutMs = DEFAULT_FETCH_TIMEOUT_MS) {
|
|
46123
|
-
|
|
46124
|
-
|
|
46298
|
+
return await fetchStandardModelsList({
|
|
46299
|
+
provider: "deepseek",
|
|
46300
|
+
url: "https://api.deepseek.com/models",
|
|
46125
46301
|
headers: { Authorization: `Bearer ${apiKey}` },
|
|
46126
|
-
|
|
46302
|
+
timeoutMs
|
|
46127
46303
|
});
|
|
46128
|
-
|
|
46129
|
-
|
|
46304
|
+
}
|
|
46305
|
+
function isValidOpenRouterNextUrl(rawUrl) {
|
|
46306
|
+
try {
|
|
46307
|
+
const parsed = new URL(rawUrl, "https://openrouter.ai");
|
|
46308
|
+
return parsed.protocol === "https:" && parsed.origin === "https://openrouter.ai" && parsed.pathname.startsWith("/api/v1/models");
|
|
46309
|
+
} catch {
|
|
46310
|
+
return false;
|
|
46130
46311
|
}
|
|
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
46312
|
}
|
|
46136
46313
|
async function fetchOpenRouterModels(apiKey, timeoutMs = DEFAULT_FETCH_TIMEOUT_MS) {
|
|
46137
46314
|
const results = [];
|
|
46138
46315
|
let url = "https://openrouter.ai/api/v1/models";
|
|
46139
|
-
|
|
46316
|
+
const seenUrls = new Set;
|
|
46317
|
+
const MAX_PAGES = 20;
|
|
46318
|
+
let pageCount = 0;
|
|
46319
|
+
while (url && pageCount < MAX_PAGES) {
|
|
46320
|
+
if (seenUrls.has(url))
|
|
46321
|
+
break;
|
|
46322
|
+
seenUrls.add(url);
|
|
46323
|
+
pageCount++;
|
|
46140
46324
|
const response = await fetch(url, {
|
|
46141
46325
|
method: "GET",
|
|
46142
46326
|
headers: { Authorization: `Bearer ${apiKey}` },
|
|
@@ -46148,7 +46332,7 @@ async function fetchOpenRouterModels(apiKey, timeoutMs = DEFAULT_FETCH_TIMEOUT_M
|
|
|
46148
46332
|
const payload = await response.json();
|
|
46149
46333
|
if (Array.isArray(payload.data)) {
|
|
46150
46334
|
for (const item of payload.data) {
|
|
46151
|
-
if (typeof item.id !== "string")
|
|
46335
|
+
if (!item || typeof item.id !== "string")
|
|
46152
46336
|
continue;
|
|
46153
46337
|
const hasTextModality = item.architecture?.output_modalities?.includes("text");
|
|
46154
46338
|
if (hasTextModality === false)
|
|
@@ -46158,70 +46342,51 @@ async function fetchOpenRouterModels(apiKey, timeoutMs = DEFAULT_FETCH_TIMEOUT_M
|
|
|
46158
46342
|
}
|
|
46159
46343
|
}
|
|
46160
46344
|
}
|
|
46161
|
-
const next = payload.links?.next
|
|
46162
|
-
|
|
46345
|
+
const next = payload.links?.next;
|
|
46346
|
+
if (typeof next === "string" && next.trim() && isValidOpenRouterNextUrl(next)) {
|
|
46347
|
+
url = new URL(next, "https://openrouter.ai").toString();
|
|
46348
|
+
} else {
|
|
46349
|
+
url = null;
|
|
46350
|
+
}
|
|
46163
46351
|
}
|
|
46164
46352
|
return results.sort((a, b) => a.modelId.localeCompare(b.modelId));
|
|
46165
46353
|
}
|
|
46166
46354
|
async function fetchGroqModels(apiKey, timeoutMs = DEFAULT_FETCH_TIMEOUT_MS) {
|
|
46167
|
-
|
|
46168
|
-
|
|
46355
|
+
return await fetchStandardModelsList({
|
|
46356
|
+
provider: "groq",
|
|
46357
|
+
url: "https://api.groq.com/openai/v1/models",
|
|
46169
46358
|
headers: { Authorization: `Bearer ${apiKey}` },
|
|
46170
|
-
|
|
46359
|
+
timeoutMs
|
|
46171
46360
|
});
|
|
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
46361
|
}
|
|
46180
46362
|
async function fetchXAIModels(apiKey, timeoutMs = DEFAULT_FETCH_TIMEOUT_MS) {
|
|
46181
|
-
|
|
46182
|
-
|
|
46363
|
+
return await fetchStandardModelsList({
|
|
46364
|
+
provider: "xai",
|
|
46365
|
+
url: "https://api.x.ai/v1/models",
|
|
46183
46366
|
headers: { Authorization: `Bearer ${apiKey}` },
|
|
46184
|
-
|
|
46367
|
+
timeoutMs
|
|
46185
46368
|
});
|
|
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
46369
|
}
|
|
46194
46370
|
async function fetchMistralModels(apiKey, timeoutMs = DEFAULT_FETCH_TIMEOUT_MS) {
|
|
46195
|
-
|
|
46196
|
-
|
|
46371
|
+
return await fetchStandardModelsList({
|
|
46372
|
+
provider: "mistral",
|
|
46373
|
+
url: "https://api.mistral.ai/v1/models",
|
|
46197
46374
|
headers: { Authorization: `Bearer ${apiKey}` },
|
|
46198
|
-
|
|
46375
|
+
timeoutMs
|
|
46199
46376
|
});
|
|
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
46377
|
}
|
|
46208
46378
|
async function fetchOllamaModels(baseUrl = "http://localhost:11434/v1", timeoutMs = 3000) {
|
|
46209
|
-
const endpoint = baseUrl.replace(/\/+$/, "")
|
|
46210
|
-
|
|
46211
|
-
|
|
46212
|
-
|
|
46379
|
+
const endpoint = `${baseUrl.replace(/\/+$/, "")}/models`;
|
|
46380
|
+
return await fetchStandardModelsList({
|
|
46381
|
+
provider: "ollama",
|
|
46382
|
+
url: endpoint,
|
|
46383
|
+
timeoutMs
|
|
46213
46384
|
});
|
|
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
46385
|
}
|
|
46222
46386
|
async function fetchGitHubCopilotModels(apiKey, timeoutMs = DEFAULT_FETCH_TIMEOUT_MS) {
|
|
46223
|
-
|
|
46224
|
-
|
|
46387
|
+
return await fetchStandardModelsList({
|
|
46388
|
+
provider: "github-copilot",
|
|
46389
|
+
url: "https://api.individual.githubcopilot.com/models",
|
|
46225
46390
|
headers: {
|
|
46226
46391
|
Accept: "application/json",
|
|
46227
46392
|
Authorization: `Bearer ${apiKey}`,
|
|
@@ -46231,16 +46396,21 @@ async function fetchGitHubCopilotModels(apiKey, timeoutMs = DEFAULT_FETCH_TIMEOU
|
|
|
46231
46396
|
"Copilot-Integration-Id": "vscode-chat",
|
|
46232
46397
|
"X-GitHub-Api-Version": "2026-06-01"
|
|
46233
46398
|
},
|
|
46234
|
-
|
|
46399
|
+
timeoutMs,
|
|
46400
|
+
filterItem: (item) => item.capabilities?.supports?.tool_calls !== false
|
|
46235
46401
|
});
|
|
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
46402
|
}
|
|
46403
|
+
var API_KEY_FETCHERS = {
|
|
46404
|
+
openai: fetchOpenAIModels,
|
|
46405
|
+
gemini: fetchGeminiModels,
|
|
46406
|
+
anthropic: fetchAnthropicModels,
|
|
46407
|
+
deepseek: fetchDeepSeekModels,
|
|
46408
|
+
openrouter: fetchOpenRouterModels,
|
|
46409
|
+
"github-copilot": fetchGitHubCopilotModels,
|
|
46410
|
+
groq: fetchGroqModels,
|
|
46411
|
+
xai: fetchXAIModels,
|
|
46412
|
+
mistral: fetchMistralModels
|
|
46413
|
+
};
|
|
46244
46414
|
async function fetchAvailableModels(authCtx, timeoutMs = DEFAULT_FETCH_TIMEOUT_MS) {
|
|
46245
46415
|
const result = {
|
|
46246
46416
|
models: [],
|
|
@@ -46259,152 +46429,30 @@ async function fetchAvailableModels(authCtx, timeoutMs = DEFAULT_FETCH_TIMEOUT_M
|
|
|
46259
46429
|
}
|
|
46260
46430
|
};
|
|
46261
46431
|
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
|
-
}));
|
|
46432
|
+
for (const [providerKey, fetcher] of Object.entries(API_KEY_FETCHERS)) {
|
|
46433
|
+
const provider = providerKey;
|
|
46434
|
+
if (!fetcher)
|
|
46435
|
+
continue;
|
|
46436
|
+
const apiKey = await authCtx.getApiKey(provider);
|
|
46437
|
+
if (apiKey) {
|
|
46438
|
+
tasks.push(fetcher(apiKey, timeoutMs).then((models) => {
|
|
46439
|
+
result.models.push(...models);
|
|
46440
|
+
result.providers[provider] = {
|
|
46441
|
+
status: "available",
|
|
46442
|
+
modelCount: models.length
|
|
46443
|
+
};
|
|
46444
|
+
}).catch((err) => {
|
|
46445
|
+
result.providers[provider] = {
|
|
46446
|
+
status: "error",
|
|
46447
|
+
modelCount: 0,
|
|
46448
|
+
error: err instanceof Error ? err.message : String(err)
|
|
46449
|
+
};
|
|
46450
|
+
}));
|
|
46451
|
+
}
|
|
46405
46452
|
}
|
|
46406
46453
|
const ollamaUrl = authCtx.getOllamaEndpoint?.() || "http://localhost:11434/v1";
|
|
46407
|
-
|
|
46454
|
+
const ollamaTimeout = Math.min(timeoutMs, 3000);
|
|
46455
|
+
tasks.push(fetchOllamaModels(ollamaUrl, ollamaTimeout).then((models) => {
|
|
46408
46456
|
if (models.length > 0) {
|
|
46409
46457
|
result.models.push(...models);
|
|
46410
46458
|
result.providers.ollama = {
|
|
@@ -53488,7 +53536,9 @@ function rehydrateSessionHistory(sessionData, projection) {
|
|
|
53488
53536
|
textAccum += (textAccum ? `
|
|
53489
53537
|
` : "") + part.text;
|
|
53490
53538
|
} else if (part.type === "tool-call") {
|
|
53491
|
-
const
|
|
53539
|
+
const callId = part.id ?? part.toolCallId;
|
|
53540
|
+
const toolName = part.name ?? part.toolName;
|
|
53541
|
+
const rawInput = part.arguments ?? part.args ?? part.input;
|
|
53492
53542
|
let parsedInput = rawInput ?? {};
|
|
53493
53543
|
if (typeof rawInput === "string") {
|
|
53494
53544
|
const trimmed = rawInput.trim();
|
|
@@ -53498,12 +53548,14 @@ function rehydrateSessionHistory(sessionData, projection) {
|
|
|
53498
53548
|
} catch {}
|
|
53499
53549
|
}
|
|
53500
53550
|
}
|
|
53501
|
-
|
|
53502
|
-
|
|
53503
|
-
|
|
53504
|
-
|
|
53505
|
-
|
|
53506
|
-
|
|
53551
|
+
if (callId) {
|
|
53552
|
+
toolCallsMap.set(callId, {
|
|
53553
|
+
id: callId,
|
|
53554
|
+
name: toolName,
|
|
53555
|
+
args: parsedInput,
|
|
53556
|
+
isError: false
|
|
53557
|
+
});
|
|
53558
|
+
}
|
|
53507
53559
|
}
|
|
53508
53560
|
}
|
|
53509
53561
|
if (textAccum.trim()) {
|
|
@@ -53518,47 +53570,49 @@ function rehydrateSessionHistory(sessionData, projection) {
|
|
|
53518
53570
|
} else if (msg.role === "tool") {
|
|
53519
53571
|
if (Array.isArray(msg.content)) {
|
|
53520
53572
|
for (const part of msg.content) {
|
|
53521
|
-
|
|
53522
|
-
|
|
53523
|
-
|
|
53524
|
-
|
|
53525
|
-
|
|
53526
|
-
|
|
53527
|
-
|
|
53528
|
-
|
|
53529
|
-
|
|
53530
|
-
|
|
53531
|
-
|
|
53532
|
-
|
|
53533
|
-
|
|
53534
|
-
|
|
53535
|
-
|
|
53536
|
-
|
|
53573
|
+
const callId = part.toolCallId ?? part.id;
|
|
53574
|
+
if (!callId)
|
|
53575
|
+
continue;
|
|
53576
|
+
const existing = toolCallsMap.get(callId);
|
|
53577
|
+
const isError = Boolean(part.isError);
|
|
53578
|
+
const toolName = existing?.name ?? part.toolName ?? part.name ?? "tool";
|
|
53579
|
+
const args = existing?.args;
|
|
53580
|
+
const argsSummary = args ? JSON.stringify(args) : "";
|
|
53581
|
+
let outputVal = part.output ?? part.result;
|
|
53582
|
+
if (outputVal && typeof outputVal === "object" && outputVal.type === "json" && "value" in outputVal) {
|
|
53583
|
+
outputVal = outputVal.value;
|
|
53584
|
+
}
|
|
53585
|
+
if (typeof outputVal === "string") {
|
|
53586
|
+
const trimmed = outputVal.trim();
|
|
53587
|
+
if (trimmed.startsWith("{") && trimmed.endsWith("}")) {
|
|
53588
|
+
try {
|
|
53589
|
+
outputVal = JSON.parse(trimmed);
|
|
53590
|
+
} catch {}
|
|
53537
53591
|
}
|
|
53538
|
-
const canonicalOutputSummary = formatToolOutputSummary(outputVal, isError);
|
|
53539
|
-
const toolLog = turnPresentation?.tools.get(part.toolCallId);
|
|
53540
|
-
const status = toolLog ? toolLog.status === "failed" ? "failed" : "completed" : isError ? "failed" : "completed";
|
|
53541
|
-
const error = toolLog?.errorMessage ? toolLog.errorMessage : isError ? typeof outputVal === "object" && outputVal !== null ? outputVal.message ?? JSON.stringify(outputVal) : String(outputVal) : undefined;
|
|
53542
|
-
const toolOutput = toolLog?.outputSummary ?? canonicalOutputSummary;
|
|
53543
|
-
restoredItems.push({
|
|
53544
|
-
id: `tool-${part.toolCallId}`,
|
|
53545
|
-
turnId: turn.id,
|
|
53546
|
-
type: "tool",
|
|
53547
|
-
content: "",
|
|
53548
|
-
toolData: {
|
|
53549
|
-
toolName,
|
|
53550
|
-
displayName: toolLog?.displayName,
|
|
53551
|
-
icon: toolLog?.icon,
|
|
53552
|
-
argsSummary,
|
|
53553
|
-
args: existing?.args,
|
|
53554
|
-
result: outputVal,
|
|
53555
|
-
status,
|
|
53556
|
-
durationMs: toolLog?.durationMs,
|
|
53557
|
-
error,
|
|
53558
|
-
toolOutput
|
|
53559
|
-
}
|
|
53560
|
-
});
|
|
53561
53592
|
}
|
|
53593
|
+
const canonicalOutputSummary = formatToolOutputSummary(outputVal, isError);
|
|
53594
|
+
const toolLog = turnPresentation?.tools.get(callId);
|
|
53595
|
+
const status = toolLog ? toolLog.status === "failed" ? "failed" : "completed" : isError ? "failed" : "completed";
|
|
53596
|
+
const error = toolLog?.errorMessage ? toolLog.errorMessage : isError ? typeof outputVal === "object" && outputVal !== null ? outputVal.message ?? JSON.stringify(outputVal) : String(outputVal) : undefined;
|
|
53597
|
+
const toolOutput = toolLog?.outputSummary ?? canonicalOutputSummary;
|
|
53598
|
+
restoredItems.push({
|
|
53599
|
+
id: `tool-${callId}`,
|
|
53600
|
+
turnId: turn.id,
|
|
53601
|
+
type: "tool",
|
|
53602
|
+
content: "",
|
|
53603
|
+
toolData: {
|
|
53604
|
+
toolName,
|
|
53605
|
+
displayName: toolLog?.displayName,
|
|
53606
|
+
icon: toolLog?.icon,
|
|
53607
|
+
argsSummary,
|
|
53608
|
+
args,
|
|
53609
|
+
result: outputVal,
|
|
53610
|
+
status,
|
|
53611
|
+
durationMs: toolLog?.durationMs,
|
|
53612
|
+
error,
|
|
53613
|
+
toolOutput
|
|
53614
|
+
}
|
|
53615
|
+
});
|
|
53562
53616
|
}
|
|
53563
53617
|
}
|
|
53564
53618
|
}
|
|
@@ -53893,15 +53947,15 @@ var defaultToolCatalog = new ToolCatalog;
|
|
|
53893
53947
|
|
|
53894
53948
|
// src/packages/agents/src/tools/read-file/index.ts
|
|
53895
53949
|
import { existsSync as existsSync4, lstatSync, readFileSync as readFileSync3, realpathSync } from "node:fs";
|
|
53896
|
-
import { isAbsolute, resolve, sep } from "node:path";
|
|
53950
|
+
import { isAbsolute, resolve as resolve2, sep } from "node:path";
|
|
53897
53951
|
var readFileInputSchema = object({
|
|
53898
53952
|
path: string2().describe("The path of the file to read (relative to current working directory or absolute)."),
|
|
53899
53953
|
offset: number2().int().min(1).optional().describe("The line number to start reading from (1-indexed). Defaults to 1."),
|
|
53900
53954
|
limit: number2().int().min(1).max(2000).optional().describe("The maximum number of lines to read. Defaults to 200.")
|
|
53901
53955
|
});
|
|
53902
53956
|
function isPathInside(targetPath, basePath) {
|
|
53903
|
-
const normTarget =
|
|
53904
|
-
const normBase =
|
|
53957
|
+
const normTarget = resolve2(targetPath);
|
|
53958
|
+
const normBase = resolve2(basePath);
|
|
53905
53959
|
if (normTarget === normBase) {
|
|
53906
53960
|
return true;
|
|
53907
53961
|
}
|
|
@@ -53913,7 +53967,7 @@ function resolveSafeReadPath(cwd, filePath) {
|
|
|
53913
53967
|
throw new Error("File path cannot be empty.");
|
|
53914
53968
|
}
|
|
53915
53969
|
const trimmed = filePath.trim();
|
|
53916
|
-
const resolved = isAbsolute(trimmed) ?
|
|
53970
|
+
const resolved = isAbsolute(trimmed) ? resolve2(trimmed) : resolve2(cwd, trimmed);
|
|
53917
53971
|
if (!isPathInside(resolved, cwd)) {
|
|
53918
53972
|
throw new Error(`Access denied: path "${filePath}" resolves outside the trusted workspace root ("${cwd}").`);
|
|
53919
53973
|
}
|
|
@@ -54014,12 +54068,12 @@ import { existsSync as existsSync8, readFileSync as readFileSync5, statSync } fr
|
|
|
54014
54068
|
// src/packages/services/src/checkpoint/path.ts
|
|
54015
54069
|
import { createHash } from "node:crypto";
|
|
54016
54070
|
import { existsSync as existsSync6, lstatSync as lstatSync2, realpathSync as realpathSync3 } from "node:fs";
|
|
54017
|
-
import { dirname as dirname4, isAbsolute as isAbsolute2, relative, resolve as
|
|
54071
|
+
import { dirname as dirname4, isAbsolute as isAbsolute2, relative, resolve as resolve4, sep as sep3 } from "node:path";
|
|
54018
54072
|
|
|
54019
54073
|
// src/packages/services/src/config/settings.ts
|
|
54020
54074
|
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync4, realpathSync as realpathSync2, writeFileSync as writeFileSync3 } from "node:fs";
|
|
54021
54075
|
import { homedir as homedir4 } from "node:os";
|
|
54022
|
-
import { join as join6, resolve as
|
|
54076
|
+
import { join as join6, resolve as resolve3, sep as sep2 } from "node:path";
|
|
54023
54077
|
function getSettingsDir() {
|
|
54024
54078
|
if (process.env.STEWARD_SETTINGS_DIR) {
|
|
54025
54079
|
return process.env.STEWARD_SETTINGS_DIR;
|
|
@@ -54073,7 +54127,7 @@ function saveThemeSelection(name) {
|
|
|
54073
54127
|
});
|
|
54074
54128
|
}
|
|
54075
54129
|
function normalizeFolderPath(inputPath) {
|
|
54076
|
-
let absolute =
|
|
54130
|
+
let absolute = resolve3(inputPath);
|
|
54077
54131
|
try {
|
|
54078
54132
|
absolute = realpathSync2(absolute);
|
|
54079
54133
|
} catch {}
|
|
@@ -54145,7 +54199,7 @@ function resolveDirectMutationPath(cwd, filePath) {
|
|
|
54145
54199
|
}
|
|
54146
54200
|
const trimmed = filePath.trim();
|
|
54147
54201
|
const workspaceRoot = normalizeFolderPath(cwd);
|
|
54148
|
-
const resolved = isAbsolute2(trimmed) ?
|
|
54202
|
+
const resolved = isAbsolute2(trimmed) ? resolve4(trimmed) : resolve4(cwd, trimmed);
|
|
54149
54203
|
if (!isPathInside2(resolved, workspaceRoot)) {
|
|
54150
54204
|
throw new Error(`Access denied: path "${filePath}" resolves outside the trusted workspace root ("${cwd}").`);
|
|
54151
54205
|
}
|
|
@@ -54261,16 +54315,16 @@ var writeFileTool = {
|
|
|
54261
54315
|
parameters: writeFileInputSchema,
|
|
54262
54316
|
confirmationPolicy: "never",
|
|
54263
54317
|
summarize: (args, result) => {
|
|
54264
|
-
const lines = result?.linesWritten ?? (args
|
|
54265
|
-
const filePath = args
|
|
54318
|
+
const lines = result?.linesWritten ?? (args?.content ? args.content.split(/\r?\n/).length : 0);
|
|
54319
|
+
const filePath = args?.file_path ?? "file";
|
|
54266
54320
|
const isNew = result?.isNew ?? true;
|
|
54267
54321
|
const headline = isNew ? `Created ${filePath} (${lines} line${lines === 1 ? "" : "s"})` : `Wrote ${lines} line${lines === 1 ? "" : "s"} to ${filePath}`;
|
|
54268
54322
|
return {
|
|
54269
54323
|
headline,
|
|
54270
54324
|
detail: {
|
|
54271
54325
|
kind: "code",
|
|
54272
|
-
filePath: args
|
|
54273
|
-
text: args
|
|
54326
|
+
filePath: args?.file_path ?? filePath,
|
|
54327
|
+
text: args?.content ?? "",
|
|
54274
54328
|
totalLines: lines
|
|
54275
54329
|
}
|
|
54276
54330
|
};
|
|
@@ -54609,10 +54663,13 @@ var editFileTool = {
|
|
|
54609
54663
|
parameters: editFileInputSchema,
|
|
54610
54664
|
confirmationPolicy: "never",
|
|
54611
54665
|
summarize: (args, result) => {
|
|
54612
|
-
|
|
54613
|
-
|
|
54666
|
+
const oldStr = args?.old_string ?? "";
|
|
54667
|
+
const newStr = args?.new_string ?? "";
|
|
54668
|
+
const filePath = args?.file_path ?? "file";
|
|
54669
|
+
if (oldStr === newStr) {
|
|
54670
|
+
return { headline: `No changes made to ${filePath}` };
|
|
54614
54671
|
}
|
|
54615
|
-
const diff = buildUnifiedDiff(
|
|
54672
|
+
const diff = buildUnifiedDiff(oldStr, newStr, 2);
|
|
54616
54673
|
let diffAdded = 0;
|
|
54617
54674
|
let diffRemoved = 0;
|
|
54618
54675
|
for (const hunk of diff.hunks) {
|
|
@@ -54630,7 +54687,7 @@ var editFileTool = {
|
|
|
54630
54687
|
headline,
|
|
54631
54688
|
detail: {
|
|
54632
54689
|
kind: "diff",
|
|
54633
|
-
filePath
|
|
54690
|
+
filePath,
|
|
54634
54691
|
hunks: diff.hunks
|
|
54635
54692
|
}
|
|
54636
54693
|
};
|
|
@@ -54732,7 +54789,7 @@ var editFileTool = {
|
|
|
54732
54789
|
|
|
54733
54790
|
// src/packages/agents/src/tools/glob/index.ts
|
|
54734
54791
|
import { readdirSync as readdirSync2 } from "node:fs";
|
|
54735
|
-
import { isAbsolute as isAbsolute3, join as join8, relative as relative2, resolve as
|
|
54792
|
+
import { isAbsolute as isAbsolute3, join as join8, relative as relative2, resolve as resolve5 } from "node:path";
|
|
54736
54793
|
var globInputSchema = object({
|
|
54737
54794
|
pattern: string2().describe('Glob pattern to match files against (e.g. "**/*.ts", "src/*.json").'),
|
|
54738
54795
|
path: string2().optional().describe("Starting directory path. Defaults to workspace root."),
|
|
@@ -54785,9 +54842,9 @@ var globTool = {
|
|
|
54785
54842
|
},
|
|
54786
54843
|
execute: async (args, context) => {
|
|
54787
54844
|
const startTime = Date.now();
|
|
54788
|
-
const searchRoot = args.path ? isAbsolute3(args.path) ?
|
|
54789
|
-
const normCwd =
|
|
54790
|
-
const normTarget =
|
|
54845
|
+
const searchRoot = args.path ? isAbsolute3(args.path) ? resolve5(args.path) : resolve5(context.cwd, args.path) : context.cwd;
|
|
54846
|
+
const normCwd = resolve5(context.cwd);
|
|
54847
|
+
const normTarget = resolve5(searchRoot);
|
|
54791
54848
|
const prefix = normCwd.endsWith("/") ? normCwd : `${normCwd}/`;
|
|
54792
54849
|
if (normTarget !== normCwd && !normTarget.startsWith(prefix)) {
|
|
54793
54850
|
throw new Error(`Access denied: path "${args.path}" resolves outside the trusted workspace root ("${context.cwd}").`);
|
|
@@ -54814,7 +54871,7 @@ var globTool = {
|
|
|
54814
54871
|
|
|
54815
54872
|
// src/packages/agents/src/tools/grep/index.ts
|
|
54816
54873
|
import { readFileSync as readFileSync7, readdirSync as readdirSync3 } from "node:fs";
|
|
54817
|
-
import { isAbsolute as isAbsolute4, join as join9, relative as relative3, resolve as
|
|
54874
|
+
import { isAbsolute as isAbsolute4, join as join9, relative as relative3, resolve as resolve6 } from "node:path";
|
|
54818
54875
|
var grepInputSchema = object({
|
|
54819
54876
|
pattern: string2().describe("The regex or text pattern to search for across files."),
|
|
54820
54877
|
path: string2().optional().describe("Target directory or file path to search. Defaults to current directory."),
|
|
@@ -54890,9 +54947,9 @@ var grepTool = {
|
|
|
54890
54947
|
},
|
|
54891
54948
|
execute: async (args, context) => {
|
|
54892
54949
|
const startTime = Date.now();
|
|
54893
|
-
const searchRoot = args.path ? isAbsolute4(args.path) ?
|
|
54894
|
-
const normCwd =
|
|
54895
|
-
const normTarget =
|
|
54950
|
+
const searchRoot = args.path ? isAbsolute4(args.path) ? resolve6(args.path) : resolve6(context.cwd, args.path) : context.cwd;
|
|
54951
|
+
const normCwd = resolve6(context.cwd);
|
|
54952
|
+
const normTarget = resolve6(searchRoot);
|
|
54896
54953
|
const prefix = normCwd.endsWith("/") ? normCwd : `${normCwd}/`;
|
|
54897
54954
|
if (normTarget !== normCwd && !normTarget.startsWith(prefix)) {
|
|
54898
54955
|
throw new Error(`Access denied: path "${args.path}" resolves outside the trusted workspace root ("${context.cwd}").`);
|
|
@@ -54951,7 +55008,7 @@ var grepTool = {
|
|
|
54951
55008
|
|
|
54952
55009
|
// src/packages/agents/src/tools/list-dir/index.ts
|
|
54953
55010
|
import { readdirSync as readdirSync4, statSync as statSync3 } from "node:fs";
|
|
54954
|
-
import { isAbsolute as isAbsolute5, join as join10, resolve as
|
|
55011
|
+
import { isAbsolute as isAbsolute5, join as join10, resolve as resolve7 } from "node:path";
|
|
54955
55012
|
var listDirInputSchema = object({
|
|
54956
55013
|
path: string2().optional().describe("Directory path to list. Defaults to current working directory."),
|
|
54957
55014
|
recursive: boolean2().optional().describe("If true, lists files recursively (depth 2). Defaults to false.")
|
|
@@ -54969,9 +55026,9 @@ var listDirTool = {
|
|
|
54969
55026
|
return `Listed ${files} file${files === 1 ? "" : "s"}, ${dirs} director${dirs === 1 ? "y" : "ies"}`;
|
|
54970
55027
|
},
|
|
54971
55028
|
execute: async (args, context) => {
|
|
54972
|
-
const targetPath = args.path ? isAbsolute5(args.path) ?
|
|
54973
|
-
const normCwd =
|
|
54974
|
-
const normTarget =
|
|
55029
|
+
const targetPath = args.path ? isAbsolute5(args.path) ? resolve7(args.path) : resolve7(context.cwd, args.path) : context.cwd;
|
|
55030
|
+
const normCwd = resolve7(context.cwd);
|
|
55031
|
+
const normTarget = resolve7(targetPath);
|
|
54975
55032
|
const prefix = normCwd.endsWith("/") ? normCwd : `${normCwd}/`;
|
|
54976
55033
|
if (normTarget !== normCwd && !normTarget.startsWith(prefix)) {
|
|
54977
55034
|
throw new Error(`Access denied: path "${args.path}" resolves outside the trusted workspace root ("${context.cwd}").`);
|
|
@@ -55407,18 +55464,18 @@ Process error: ${err.message}
|
|
|
55407
55464
|
// src/packages/agents/src/tools/bash/command-policy.ts
|
|
55408
55465
|
import { existsSync as existsSync10, realpathSync as realpathSync4 } from "node:fs";
|
|
55409
55466
|
import { homedir as homedir6 } from "node:os";
|
|
55410
|
-
import { isAbsolute as isAbsolute6, resolve as
|
|
55467
|
+
import { isAbsolute as isAbsolute6, resolve as resolve8, sep as sep4 } from "node:path";
|
|
55411
55468
|
function expandHome(filepath) {
|
|
55412
55469
|
if (filepath === "~") {
|
|
55413
55470
|
return homedir6();
|
|
55414
55471
|
}
|
|
55415
55472
|
if (filepath.startsWith("~/") || filepath.startsWith("~\\")) {
|
|
55416
|
-
return
|
|
55473
|
+
return resolve8(homedir6(), filepath.slice(2));
|
|
55417
55474
|
}
|
|
55418
55475
|
return filepath;
|
|
55419
55476
|
}
|
|
55420
55477
|
function normalizeRoot(inputPath) {
|
|
55421
|
-
let absolute =
|
|
55478
|
+
let absolute = resolve8(inputPath);
|
|
55422
55479
|
try {
|
|
55423
55480
|
absolute = realpathSync4(absolute);
|
|
55424
55481
|
} catch {}
|
|
@@ -55553,7 +55610,7 @@ function arePathTokensInsideWorkspace(tokens, startIndex, cwd) {
|
|
|
55553
55610
|
continue;
|
|
55554
55611
|
}
|
|
55555
55612
|
const expanded = expandHome(tok);
|
|
55556
|
-
const resolved = isAbsolute6(expanded) ?
|
|
55613
|
+
const resolved = isAbsolute6(expanded) ? resolve8(expanded) : resolve8(cwd, expanded);
|
|
55557
55614
|
if (!isContainedInWorkspace(resolved, cwd)) {
|
|
55558
55615
|
return false;
|
|
55559
55616
|
}
|
|
@@ -55841,7 +55898,7 @@ var taskReadTool = {
|
|
|
55841
55898
|
parameters: taskReadInputSchema,
|
|
55842
55899
|
confirmationPolicy: "never",
|
|
55843
55900
|
summarize: (args, result) => {
|
|
55844
|
-
return `Task ${args
|
|
55901
|
+
return `Task ${args?.task_id ?? ""} · status: ${result?.status ?? "unknown"}`;
|
|
55845
55902
|
},
|
|
55846
55903
|
execute: async (args, context) => {
|
|
55847
55904
|
if (!context.shellTasks) {
|
|
@@ -55876,7 +55933,7 @@ var taskSendInputTool = {
|
|
|
55876
55933
|
parameters: taskSendInputSchema,
|
|
55877
55934
|
confirmationPolicy: "never",
|
|
55878
55935
|
summarize: (args) => {
|
|
55879
|
-
return `Sent input to ${args
|
|
55936
|
+
return `Sent input to ${args?.task_id ?? ""} (${args?.input?.length ?? 0} chars)`;
|
|
55880
55937
|
},
|
|
55881
55938
|
execute: async (args, context) => {
|
|
55882
55939
|
if (!context.shellTasks) {
|
|
@@ -55900,7 +55957,7 @@ var taskKillTool = {
|
|
|
55900
55957
|
parameters: taskKillInputSchema,
|
|
55901
55958
|
confirmationPolicy: "never",
|
|
55902
55959
|
summarize: (args, result) => {
|
|
55903
|
-
return `Stopped task ${args
|
|
55960
|
+
return `Stopped task ${args?.task_id ?? ""} · status: ${result?.status ?? "killed"}`;
|
|
55904
55961
|
},
|
|
55905
55962
|
execute: async (args, context) => {
|
|
55906
55963
|
if (!context.shellTasks) {
|
|
@@ -55941,8 +55998,8 @@ import { isIP } from "node:net";
|
|
|
55941
55998
|
// package.json
|
|
55942
55999
|
var package_default = {
|
|
55943
56000
|
name: "steward-cli",
|
|
55944
|
-
version: "0.
|
|
55945
|
-
description: "Interactive AI engineering assistant for the terminal
|
|
56001
|
+
version: "0.25.0",
|
|
56002
|
+
description: "Interactive AI engineering assistant for the terminal.",
|
|
55946
56003
|
author: "sapirrior",
|
|
55947
56004
|
license: "MIT",
|
|
55948
56005
|
files: [
|
|
@@ -56127,14 +56184,15 @@ var webFetchTool = {
|
|
|
56127
56184
|
confirmationPolicy: "never",
|
|
56128
56185
|
summarize: (args, result) => {
|
|
56129
56186
|
if (!result)
|
|
56130
|
-
return `Fetching ${args
|
|
56131
|
-
const chars = result
|
|
56132
|
-
const truncated = result
|
|
56133
|
-
const type = result
|
|
56187
|
+
return `Fetching ${args?.url ?? ""}`;
|
|
56188
|
+
const chars = result?.content?.length ?? 0;
|
|
56189
|
+
const truncated = result?.isTruncated ? " (truncated)" : "";
|
|
56190
|
+
const type = result?.contentType?.split(";")[0]?.trim() ?? "text";
|
|
56134
56191
|
const label = type.includes("html") ? "HTML" : type.includes("json") ? "JSON" : type.includes("markdown") || type.includes("text/plain") ? "text" : type.split("/").pop() ?? "content";
|
|
56135
|
-
let host = args
|
|
56192
|
+
let host = args?.url ?? "";
|
|
56136
56193
|
try {
|
|
56137
|
-
|
|
56194
|
+
if (args?.url)
|
|
56195
|
+
host = new URL(args.url).hostname;
|
|
56138
56196
|
} catch {}
|
|
56139
56197
|
return `Fetched ${chars.toLocaleString()} chars of ${label} from ${host}${truncated}`;
|
|
56140
56198
|
},
|
|
@@ -56274,9 +56332,9 @@ var webSearchTool = {
|
|
|
56274
56332
|
confirmationPolicy: "never",
|
|
56275
56333
|
summarize: (args, result) => {
|
|
56276
56334
|
if (!result)
|
|
56277
|
-
return `Searching "${args
|
|
56278
|
-
const count = result
|
|
56279
|
-
const site = args
|
|
56335
|
+
return `Searching "${args?.query ?? ""}"`;
|
|
56336
|
+
const count = result?.resultCount ?? 0;
|
|
56337
|
+
const site = args?.site ? ` on ${args.site}` : "";
|
|
56280
56338
|
return `Found ${count} result${count === 1 ? "" : "s"}${site} via DuckDuckGo`;
|
|
56281
56339
|
},
|
|
56282
56340
|
execute: async (args, context) => {
|
|
@@ -56296,7 +56354,7 @@ var webSearchTool = {
|
|
|
56296
56354
|
// src/packages/agents/src/skills/discovery.ts
|
|
56297
56355
|
import { existsSync as existsSync11, readdirSync as readdirSync5, readFileSync as readFileSync8, statSync as statSync4 } from "node:fs";
|
|
56298
56356
|
import { homedir as homedir7 } from "node:os";
|
|
56299
|
-
import { isAbsolute as isAbsolute7, join as join12, normalize, resolve as
|
|
56357
|
+
import { isAbsolute as isAbsolute7, join as join12, normalize, resolve as resolve9, sep as sep5 } from "node:path";
|
|
56300
56358
|
var MAX_SKILL_FILE_SIZE = 64 * 1024;
|
|
56301
56359
|
function parseSkillFrontmatter(content) {
|
|
56302
56360
|
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
@@ -56411,8 +56469,8 @@ function readSkillResource(name, relativePath, cwd = process.cwd(), maxBytes = M
|
|
|
56411
56469
|
if (normalizedRelative.startsWith("..") || normalizedRelative.includes(`${sep5}..`)) {
|
|
56412
56470
|
throw new Error(`Invalid skill resource path: directory traversal ("..") is forbidden (${trimmedPath})`);
|
|
56413
56471
|
}
|
|
56414
|
-
targetFilePath =
|
|
56415
|
-
const resolvedDir =
|
|
56472
|
+
targetFilePath = resolve9(skill.dirPath, normalizedRelative);
|
|
56473
|
+
const resolvedDir = resolve9(skill.dirPath);
|
|
56416
56474
|
if (!targetFilePath.startsWith(resolvedDir + sep5) && targetFilePath !== resolvedDir) {
|
|
56417
56475
|
throw new Error(`Resource "${trimmedPath}" resolves outside skill directory`);
|
|
56418
56476
|
}
|
|
@@ -56479,17 +56537,17 @@ var skillReadTool = {
|
|
|
56479
56537
|
description: "Loads and reads the instructions (SKILL.md) or a relative resource file of a specialized skill by name. Use this when a task requires specialized guidelines.",
|
|
56480
56538
|
parameters: skillReadParamsSchema,
|
|
56481
56539
|
summarizeArgs(args) {
|
|
56482
|
-
if (args
|
|
56540
|
+
if (args?.path) {
|
|
56483
56541
|
return `name="${args.name}" path="${args.path}"`;
|
|
56484
56542
|
}
|
|
56485
|
-
return `name="${args
|
|
56543
|
+
return `name="${args?.name ?? ""}"`;
|
|
56486
56544
|
},
|
|
56487
56545
|
async execute(args, context) {
|
|
56488
56546
|
const cwd = context?.cwd ?? process.cwd();
|
|
56489
56547
|
return readSkillResource(args.name, args.path, cwd);
|
|
56490
56548
|
},
|
|
56491
56549
|
summarize(args, result) {
|
|
56492
|
-
const target = args
|
|
56550
|
+
const target = args?.path ? `${args.name}/${args.path}` : args?.name ?? "skill";
|
|
56493
56551
|
const len = result?.content ? ` (${result.content.length} bytes)` : "";
|
|
56494
56552
|
return `SkillRead: ${target}${len}`;
|
|
56495
56553
|
}
|
|
@@ -56526,17 +56584,19 @@ function summarizeToolResult(toolDef, args, result, isError = false) {
|
|
|
56526
56584
|
return;
|
|
56527
56585
|
}
|
|
56528
56586
|
if (toolDef.summarize) {
|
|
56529
|
-
|
|
56530
|
-
|
|
56531
|
-
|
|
56587
|
+
try {
|
|
56588
|
+
const sum = toolDef.summarize(args, result);
|
|
56589
|
+
if (typeof sum === "string") {
|
|
56590
|
+
const [headline, ...rest] = sum.split(`
|
|
56532
56591
|
`);
|
|
56533
|
-
|
|
56534
|
-
|
|
56535
|
-
|
|
56592
|
+
return {
|
|
56593
|
+
headline: headline ?? "",
|
|
56594
|
+
detail: rest.length > 0 ? { kind: "text", text: rest.join(`
|
|
56536
56595
|
`) } : undefined
|
|
56537
|
-
|
|
56538
|
-
|
|
56539
|
-
|
|
56596
|
+
};
|
|
56597
|
+
}
|
|
56598
|
+
return sum;
|
|
56599
|
+
} catch {}
|
|
56540
56600
|
}
|
|
56541
56601
|
if (typeof result === "string") {
|
|
56542
56602
|
const firstLine = result.split(`
|
|
@@ -57646,7 +57706,7 @@ import {
|
|
|
57646
57706
|
unlinkSync as unlinkSync5,
|
|
57647
57707
|
writeSync as writeSync5
|
|
57648
57708
|
} from "node:fs";
|
|
57649
|
-
import { dirname as dirname6, join as join15, resolve as
|
|
57709
|
+
import { dirname as dirname6, join as join15, resolve as resolve10 } from "node:path";
|
|
57650
57710
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
57651
57711
|
function getFileStateOnDisk(absPath) {
|
|
57652
57712
|
if (!existsSync15(absPath)) {
|
|
@@ -57744,7 +57804,7 @@ async function executeRewind(options) {
|
|
|
57744
57804
|
if (!fileCp.mutationCommitted)
|
|
57745
57805
|
continue;
|
|
57746
57806
|
const relPath = fileCp.relativePath;
|
|
57747
|
-
const absPath =
|
|
57807
|
+
const absPath = resolve10(workspaceRoot, relPath);
|
|
57748
57808
|
if (!fileOperationsMap.has(relPath)) {
|
|
57749
57809
|
fileOperationsMap.set(relPath, {
|
|
57750
57810
|
relativePath: relPath,
|
|
@@ -57895,7 +57955,7 @@ async function recoverPendingCheckpoint(workspaceRoot, sessionId) {
|
|
|
57895
57955
|
if (journal.phase === "files-restored" || journal.phase === "files-pending") {
|
|
57896
57956
|
for (const file of journal.files) {
|
|
57897
57957
|
if (file.post) {
|
|
57898
|
-
const absPath =
|
|
57958
|
+
const absPath = resolve10(workspaceRoot, file.relativePath);
|
|
57899
57959
|
try {
|
|
57900
57960
|
restoreFileStateToDisk(workspaceHash, absPath, file.post);
|
|
57901
57961
|
} catch {}
|
|
@@ -58669,35 +58729,7 @@ function getDiscoveryAuthContext() {
|
|
|
58669
58729
|
const resolved = await authManager.resolve(provider).catch(() => {
|
|
58670
58730
|
return;
|
|
58671
58731
|
});
|
|
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
|
-
}
|
|
58732
|
+
return resolved?.token && resolved.token !== "none" ? resolved.token : undefined;
|
|
58701
58733
|
},
|
|
58702
58734
|
getCustomEndpoint: () => config.custom,
|
|
58703
58735
|
getOllamaEndpoint: () => config.ollamaBaseUrl
|
|
@@ -61892,22 +61924,14 @@ class CommandPaletteController {
|
|
|
61892
61924
|
const matching = this.getMatchingCommands(value);
|
|
61893
61925
|
if (matching.length === 0)
|
|
61894
61926
|
return false;
|
|
61895
|
-
|
|
61896
|
-
this.dismiss();
|
|
61897
|
-
return false;
|
|
61898
|
-
}
|
|
61899
|
-
this.paletteIdx = this.paletteIdx - 1;
|
|
61927
|
+
this.paletteIdx = this.paletteIdx > 0 ? this.paletteIdx - 1 : matching.length - 1;
|
|
61900
61928
|
return true;
|
|
61901
61929
|
}
|
|
61902
61930
|
onDown(value) {
|
|
61903
61931
|
const matching = this.getMatchingCommands(value);
|
|
61904
61932
|
if (matching.length === 0)
|
|
61905
61933
|
return false;
|
|
61906
|
-
|
|
61907
|
-
this.dismiss();
|
|
61908
|
-
return false;
|
|
61909
|
-
}
|
|
61910
|
-
this.paletteIdx = this.paletteIdx + 1;
|
|
61934
|
+
this.paletteIdx = this.paletteIdx < matching.length - 1 ? this.paletteIdx + 1 : 0;
|
|
61911
61935
|
return true;
|
|
61912
61936
|
}
|
|
61913
61937
|
onTab(value) {
|
|
@@ -62370,25 +62394,32 @@ ${after}`, this.state.cursorPos);
|
|
|
62370
62394
|
const matchingCommands = this.commandPalette.getMatchingCommands(effectiveValue);
|
|
62371
62395
|
const paletteIdx = this.state.paletteIdx ?? this.commandPalette.getSelectedIndex();
|
|
62372
62396
|
if (this.commandPalette.isSlashMode(effectiveValue) && matchingCommands.length > 0 && effectiveValue !== `/${matchingCommands[0]?.name} `) {
|
|
62373
|
-
|
|
62374
|
-
|
|
62375
|
-
|
|
62376
|
-
|
|
62377
|
-
|
|
62378
|
-
|
|
62379
|
-
|
|
62380
|
-
|
|
62381
|
-
}
|
|
62397
|
+
const maxVisible = 5;
|
|
62398
|
+
const startIdx = Math.max(0, Math.min(paletteIdx - Math.floor(maxVisible / 2), matchingCommands.length - maxVisible));
|
|
62399
|
+
const visibleCmds = matchingCommands.slice(Math.max(0, startIdx), Math.max(0, startIdx) + maxVisible);
|
|
62400
|
+
for (let relIdx = 0;relIdx < visibleCmds.length; relIdx++) {
|
|
62401
|
+
const cmd = visibleCmds[relIdx];
|
|
62402
|
+
const actualIdx = Math.max(0, startIdx) + relIdx;
|
|
62403
|
+
const isSelected = actualIdx === paletteIdx;
|
|
62404
|
+
const p = isSelected ? c.info(`${figures.pointer} `) : " ";
|
|
62405
|
+
const namePadded = `/${cmd.name}`.padEnd(16);
|
|
62406
|
+
const nameText = isSelected ? c.info(namePadded) : c.text(namePadded);
|
|
62407
|
+
const descText = isSelected ? c.info(cmd.description) : c.muted(cmd.description);
|
|
62408
|
+
lines.push(truncateToWidth2(`${p}${nameText}${descText}`, maxCols));
|
|
62382
62409
|
}
|
|
62383
62410
|
}
|
|
62384
62411
|
const fileMatches = this.state.fileMatches ?? this.autocomplete.getMatches();
|
|
62385
62412
|
const fileSelectIdx = this.state.fileSelectIdx ?? this.autocomplete.getSelectedIndex();
|
|
62386
62413
|
if (fileMatches.length > 0) {
|
|
62387
62414
|
lines.push(c.muted("Matching files (@):"));
|
|
62388
|
-
|
|
62389
|
-
|
|
62390
|
-
|
|
62391
|
-
|
|
62415
|
+
const maxVisible = 5;
|
|
62416
|
+
const startIdx = Math.max(0, Math.min(fileSelectIdx - Math.floor(maxVisible / 2), fileMatches.length - maxVisible));
|
|
62417
|
+
const visibleFiles = fileMatches.slice(Math.max(0, startIdx), Math.max(0, startIdx) + maxVisible);
|
|
62418
|
+
for (let relIdx = 0;relIdx < visibleFiles.length; relIdx++) {
|
|
62419
|
+
const f = visibleFiles[relIdx];
|
|
62420
|
+
const actualIdx = Math.max(0, startIdx) + relIdx;
|
|
62421
|
+
const isSelected = actualIdx === fileSelectIdx;
|
|
62422
|
+
const p = isSelected ? c.info(`${figures.pointer} `) : " ";
|
|
62392
62423
|
const fileText = isSelected ? c.info(f) : c.muted(f);
|
|
62393
62424
|
lines.push(truncateToWidth2(`${p}${fileText}`, maxCols));
|
|
62394
62425
|
}
|
|
@@ -62451,7 +62482,7 @@ class TrustGate extends Component_default {
|
|
|
62451
62482
|
const { selectedIndex } = this.state;
|
|
62452
62483
|
const isYesSelected = selectedIndex === 0;
|
|
62453
62484
|
const isNoSelected = selectedIndex === 1;
|
|
62454
|
-
const pointer = figures.
|
|
62485
|
+
const pointer = figures.pointer ?? ">";
|
|
62455
62486
|
const yesOptionText = isYesSelected ? `${c.permission(pointer)} ${bold(c.permission("1. Yes, I trust this folder"))}` : ` ${c.text("1. Yes, I trust this folder")}`;
|
|
62456
62487
|
const noOptionText = isNoSelected ? `${c.permission(pointer)} ${bold(c.permission("2. No, exit"))}` : ` ${c.text("2. No, exit")}`;
|
|
62457
62488
|
const safetyCheckText = "You should only proceed if you trust this workspace. Accessing untrusted workspaces may allow malicious code in the repository to compromise security or mislead the assistant.";
|
|
@@ -63342,14 +63373,14 @@ class LoginDock extends Component_default {
|
|
|
63342
63373
|
if (step === "select-provider") {
|
|
63343
63374
|
const items = OAUTH_PROVIDERS.map((p, idx) => {
|
|
63344
63375
|
const isSelected = idx === selectedIndex;
|
|
63345
|
-
const pointer = isSelected ? c.
|
|
63376
|
+
const pointer = isSelected ? c.info(`${figures.pointer} `) : " ";
|
|
63377
|
+
const name = isSelected ? c.info(bold(p.name)) : c.text(bold(p.name));
|
|
63346
63378
|
return /* @__PURE__ */ jsxs(Box, {
|
|
63347
63379
|
direction: "column",
|
|
63348
63380
|
width: maxCols,
|
|
63349
63381
|
children: [
|
|
63350
63382
|
/* @__PURE__ */ jsx(Text, {
|
|
63351
|
-
|
|
63352
|
-
children: `${pointer}${bold(p.name)}`
|
|
63383
|
+
children: `${pointer}${name}`
|
|
63353
63384
|
}),
|
|
63354
63385
|
/* @__PURE__ */ jsx(Text, {
|
|
63355
63386
|
color: "muted",
|
|
@@ -63465,8 +63496,9 @@ class ModelPicker extends SelectList {
|
|
|
63465
63496
|
onCancel: props.onCancel,
|
|
63466
63497
|
renderItem: (m, isSelected, maxCols) => {
|
|
63467
63498
|
const isCurrent = m.provider === props.currentModel.provider && m.modelId === props.currentModel.modelId;
|
|
63468
|
-
const pointer = isSelected ? c.
|
|
63499
|
+
const pointer = isSelected ? c.info(`${figures.pointer} `) : " ";
|
|
63469
63500
|
const activeBadge = isCurrent ? bold(c.current(" (active)")) : "";
|
|
63501
|
+
const title = isSelected ? c.info(m.modelId) : isCurrent ? c.current(m.modelId) : c.text(m.modelId);
|
|
63470
63502
|
const providerName = m.provider.toUpperCase();
|
|
63471
63503
|
const caps = m.reasoning ? "reasoning" : "chat";
|
|
63472
63504
|
const meta = `${providerName} • ${caps}`;
|
|
@@ -63475,11 +63507,10 @@ class ModelPicker extends SelectList {
|
|
|
63475
63507
|
width: maxCols,
|
|
63476
63508
|
children: [
|
|
63477
63509
|
/* @__PURE__ */ jsx(Text, {
|
|
63478
|
-
color: isSelected ? "selected" : isCurrent ? "current" : "text",
|
|
63479
63510
|
wrap: false,
|
|
63480
63511
|
clip: true,
|
|
63481
63512
|
ellipsis: true,
|
|
63482
|
-
children: `${pointer}${
|
|
63513
|
+
children: `${pointer}${title}${activeBadge}`
|
|
63483
63514
|
}),
|
|
63484
63515
|
/* @__PURE__ */ jsx(Text, {
|
|
63485
63516
|
color: "muted",
|
|
@@ -63515,8 +63546,9 @@ class ThemePicker extends SelectList {
|
|
|
63515
63546
|
onCancel: props.onCancel,
|
|
63516
63547
|
renderItem: (t, isSelected, maxCols) => {
|
|
63517
63548
|
const isCurrent = t.name === props.currentTheme;
|
|
63518
|
-
const pointer = isSelected ? c.
|
|
63549
|
+
const pointer = isSelected ? c.info(`${figures.pointer} `) : " ";
|
|
63519
63550
|
const activeBadge = isCurrent ? bold(c.current(" (active)")) : "";
|
|
63551
|
+
const title = isSelected ? c.info(t.label) : isCurrent ? c.current(t.label) : c.text(t.label);
|
|
63520
63552
|
const tBrand = resolveThemeColor(t.theme.brand, false)(figures.blackCircle);
|
|
63521
63553
|
const tSuccess = resolveThemeColor(t.theme.success, false)(figures.blackCircle);
|
|
63522
63554
|
const tWarning = resolveThemeColor(t.theme.warning, false)(figures.blackCircle);
|
|
@@ -63529,11 +63561,10 @@ class ThemePicker extends SelectList {
|
|
|
63529
63561
|
width: maxCols,
|
|
63530
63562
|
children: [
|
|
63531
63563
|
/* @__PURE__ */ jsx(Text, {
|
|
63532
|
-
color: isSelected ? "selected" : isCurrent ? "current" : "text",
|
|
63533
63564
|
wrap: false,
|
|
63534
63565
|
clip: true,
|
|
63535
63566
|
ellipsis: true,
|
|
63536
|
-
children: `${pointer}${
|
|
63567
|
+
children: `${pointer}${title}${activeBadge}`
|
|
63537
63568
|
}),
|
|
63538
63569
|
/* @__PURE__ */ jsx(Text, {
|
|
63539
63570
|
color: "muted",
|
|
@@ -63573,7 +63604,8 @@ class SessionMenu extends SelectList {
|
|
|
63573
63604
|
renderItem: (s, isSelected, maxCols) => {
|
|
63574
63605
|
const rawTitle = s.name || s.turns?.[0]?.userPrompt || "Untitled Session";
|
|
63575
63606
|
const cleanTitle = rawTitle.replace(/\s+/g, " ").trim();
|
|
63576
|
-
const pointer = isSelected ? c.
|
|
63607
|
+
const pointer = isSelected ? c.info(`${figures.pointer} `) : " ";
|
|
63608
|
+
const title = isSelected ? c.info(cleanTitle) : c.text(cleanTitle);
|
|
63577
63609
|
const shortId = s.id.slice(0, 8);
|
|
63578
63610
|
const dateStr = s.date ? s.date.split("T")[0] : "";
|
|
63579
63611
|
const turnsCount = s.turns?.length ?? 0;
|
|
@@ -63588,11 +63620,10 @@ class SessionMenu extends SelectList {
|
|
|
63588
63620
|
width: maxCols,
|
|
63589
63621
|
children: [
|
|
63590
63622
|
/* @__PURE__ */ jsx(Text, {
|
|
63591
|
-
color: isSelected ? "selected" : "text",
|
|
63592
63623
|
wrap: false,
|
|
63593
63624
|
clip: true,
|
|
63594
63625
|
ellipsis: true,
|
|
63595
|
-
children: `${pointer}${
|
|
63626
|
+
children: `${pointer}${title}`
|
|
63596
63627
|
}),
|
|
63597
63628
|
/* @__PURE__ */ jsx(Text, {
|
|
63598
63629
|
color: "muted",
|
|
@@ -63945,7 +63976,8 @@ class RewindMenu extends SelectList {
|
|
|
63945
63976
|
onSelect: props.onSelect,
|
|
63946
63977
|
onCancel: props.onCancel,
|
|
63947
63978
|
renderItem: (item, isSelected, maxCols) => {
|
|
63948
|
-
const pointer = isSelected ? c.
|
|
63979
|
+
const pointer = isSelected ? c.info(`${figures.pointer} `) : " ";
|
|
63980
|
+
const title = isSelected ? c.info(item.promptText) : c.text(item.promptText);
|
|
63949
63981
|
let summaryText;
|
|
63950
63982
|
if (item.hasCodeChanges) {
|
|
63951
63983
|
const filePart = c.muted(`${item.changedFileCount} file${item.changedFileCount !== 1 ? "s" : ""} changed`);
|
|
@@ -63966,11 +63998,10 @@ class RewindMenu extends SelectList {
|
|
|
63966
63998
|
width: maxCols,
|
|
63967
63999
|
children: [
|
|
63968
64000
|
/* @__PURE__ */ jsx(Text, {
|
|
63969
|
-
color: isSelected ? "selected" : "text",
|
|
63970
64001
|
wrap: false,
|
|
63971
64002
|
clip: true,
|
|
63972
64003
|
ellipsis: true,
|
|
63973
|
-
children: `${pointer}${
|
|
64004
|
+
children: `${pointer}${title}`
|
|
63974
64005
|
}),
|
|
63975
64006
|
/* @__PURE__ */ jsx(Text, {
|
|
63976
64007
|
wrap: false,
|
|
@@ -64071,7 +64102,7 @@ class BashPermissionDock extends Component_default {
|
|
|
64071
64102
|
const maxCols = Math.max(1, termWidth);
|
|
64072
64103
|
const { mode, selectedIndex, scrollOffset } = this.state;
|
|
64073
64104
|
const { command, explanation } = this.props;
|
|
64074
|
-
const pointer = figures.
|
|
64105
|
+
const pointer = figures.pointer ?? ">";
|
|
64075
64106
|
const isYesSelected = selectedIndex === 0;
|
|
64076
64107
|
const isNoSelected = selectedIndex === 1;
|
|
64077
64108
|
const divider = c.rule(figures.horizontalLine.repeat(maxCols));
|
|
@@ -64381,7 +64412,7 @@ class FilePermissionDock extends Component_default {
|
|
|
64381
64412
|
const { mode, selectedIndex, scrollOffset } = this.state;
|
|
64382
64413
|
const { request } = this.props;
|
|
64383
64414
|
const { kind, filePath } = request;
|
|
64384
|
-
const pointer = figures.
|
|
64415
|
+
const pointer = figures.pointer ?? ">";
|
|
64385
64416
|
const isYesSelected = selectedIndex === 0;
|
|
64386
64417
|
const isNoSelected = selectedIndex === 1;
|
|
64387
64418
|
const divider = c.rule(figures.horizontalLine.repeat(maxCols));
|