@epoch-agent/core 1.0.0 → 2.0.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 +35 -8
- package/dist/index.d.ts +1511 -1157
- package/dist/index.js +1116 -564
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { PROVIDER_TYPES, OPERATION_TYPES as OPERATION_TYPES$1, COMPLIANCE_CATEGORIES, COMPLIANCE_ACTIONS, SEARCH_PROVIDER_TYPES, CURRENCIES, LANGS, isSettingsPaneId, SETTINGS_PANE_IDS, PERMISSION_LEVELS, SHELL_KINDS, cronTimes, SCHEDULE_MAX_TIMES_PER_DAY, SCHEDULE_INTERVAL_MIN_MINUTES, SCHEDULE_INTERVAL_MAX_MINUTES, ToolExposure,
|
|
1
|
+
import { REASONING_EFFORTS, MODEL_ENTRY_PROTOCOLS, PROVIDER_TYPES, isModelEntryId, parseModelRef, OPERATION_TYPES as OPERATION_TYPES$1, COMPLIANCE_CATEGORIES, COMPLIANCE_ACTIONS, SEARCH_PROVIDER_TYPES, CURRENCIES, LANGS, isSettingsPaneId, SETTINGS_PANE_IDS, BRAND_WHEN_SIDEBAR_COLLAPSED, PERMISSION_LEVELS, SHELL_KINDS, cronTimes, SCHEDULE_MAX_TIMES_PER_DAY, SCHEDULE_INTERVAL_MIN_MINUTES, SCHEDULE_INTERVAL_MAX_MINUTES, ToolExposure, MIN_OPTIONS, MAX_OPTIONS, MAX_HEADER_CHARS, MAX_QUESTIONS, isPermissionLevel, SCHEDULE_DRAFT_TOOL, isValidAgentRoleName, DEFAULT_AGENT_ROLE, isValidCommandName, RESERVED_COMMAND_NAMES, isWirePluginArchiveName, systemNote, formatMoney, totalUsageTokens, NOOP_TELEMETRY, METRIC, contentToText, GEN_AI, isPromptOversize, promptTokens, entrySecretName, isEntrySecretName, entryRef, KEY_ACTIONS, tableOf, perAction, isKeyAction, parseSequence, RESERVED_CHORDS, chordId, sequenceId, ACTION_CONTEXTS, SEQUENCE_CONTEXTS, assertNeverPart, MAX_ATTACH_FILES, MAX_ATTACH_TOTAL_BYTES, MAX_SESSION_REFS, utf8ByteLength, truncateToBudget, attachSessionSurface, isContextNearlyFull, isOperationType, normalizeApproval, imagePartOmittedSummary, ENTRY_REF_PREFIX, MAX_ATTACH_BYTES, SIMULTANEOUS_CONTEXTS, SESSION_REFERENCE_ERROR_CODES, filePartSummary, sessionPartSummary } from '@epoch-agent/protocol';
|
|
2
2
|
export { API_KEY_ENV_VARS, COMPLIANCE_ACTIONS, COMPLIANCE_CATEGORIES, MAX_ATTACH_BYTES, MAX_ATTACH_FILES, MAX_ATTACH_TOTAL_BYTES, MAX_SESSION_ATTACH_BYTES, MAX_SESSION_REFS, OPERATION_TYPES, PERMISSION_LEVELS, PLAN_OUTCOMES, PROVIDER_INFOS, PROVIDER_TYPES, ToolExposure, apiKeyEnvVar, extractAllMentions, extractMentions, extractSessionMentions, getProviderInfo, isOperationType, isPermissionLevel, isPlanOutcome, isProviderType, normalizeApproval } from '@epoch-agent/protocol';
|
|
3
3
|
import { existsSync, statSync, readdirSync, readFileSync, mkdirSync, writeFileSync, rmSync, renameSync, realpathSync, symlinkSync, cpSync, lstatSync, unlinkSync, rmdirSync, mkdtempSync, copyFileSync, openSync, writeSync, closeSync, readSync } from 'fs';
|
|
4
4
|
import { homedir, platform, tmpdir, hostname } from 'os';
|
|
5
5
|
import { join, dirname, resolve, relative, isAbsolute, extname, sep, basename } from 'path';
|
|
6
6
|
import { execFile, execSync, execFileSync, spawn } from 'child_process';
|
|
7
|
-
import { createLogger, dbPath, resolveHomeDir, DEFAULT_MAX_TURNS, PROJECT_DIR_NAME, guardSpawn, t, suggestKey, misspelledKeyIssues, openDatabase, migrate, releaseDatabase, parseLenient, issueDetails, splitWords, unknownKeyIssues, checkpointsDir, expandPath, pluginsDir, projectHooksPath, projectClaudeSettingsPath, projectClaudeLocalSettingsPath, projectSettingsPath, projectLocalSettingsPath, projectPoliciesDir, trustedImportsPath, canProbeGit, budgetStatePath, artifactsDir, isInWorkspace, UNLIMITED_TURNS, turnLimitOf, configPath, managedSettingsPath,
|
|
7
|
+
import { createLogger, dbPath, resolveHomeDir, DEFAULT_MAX_TURNS, PROJECT_DIR_NAME, guardSpawn, t, suggestKey, misspelledKeyIssues, openDatabase, migrate, releaseDatabase, parseLenient, issueDetails, splitWords, unknownKeyIssues, checkpointsDir, expandPath, pluginsDir, projectHooksPath, projectClaudeSettingsPath, projectClaudeLocalSettingsPath, projectSettingsPath, projectLocalSettingsPath, projectPoliciesDir, trustedImportsPath, canProbeGit, budgetStatePath, artifactsDir, isInWorkspace, UNLIMITED_TURNS, turnLimitOf, configPath, managedSettingsPath, listStoredSecretNames, maskApiKey, unsetSectionField, setSectionField, countCodePoints, headCodePoints, attachmentsDir, workspacesPath, samePath, lastSessionsPath, appearancePath, automationLogsDir, projectComplianceDir, projectSkillsDir, trustPath, projectCommandsDir, detectBackend, EXEC_PATH_AS_NODE_ENV, isolate, createStreamDecoder, parseShellCommand, writeArtifact, getSecretValues, envPath, checkDangerousCommand, checkObfuscation, keepHeadAndTail, normalizeForMatch, isReadOnlyCommand, CODE_EXEC_ROOTS, tailCodePoints } from '@epoch-agent/infra';
|
|
8
8
|
export { IS_WINDOWS, WINDOWS_HIDE_FLAGS, buildBwrapArgs, buildProfile, checkDangerousCommand, checkObfuscation, createLogger, detectBackend, getDefaultShell, getPythonCommand, isInWorkspace, isReadOnlyCommand, isolate, maskApiKey, probeBubblewrap, probeSeatbelt, complianceDir as resolveComplianceDir, policiesDir as resolvePoliciesDir, setLogLevel } from '@epoch-agent/infra';
|
|
9
9
|
import { createRequire } from 'module';
|
|
10
10
|
import { randomUUID, createHash, randomBytes } from 'crypto';
|
|
@@ -87,9 +87,9 @@ function isValidInterval(everyMinutes) {
|
|
|
87
87
|
function normalizeTrigger(trigger) {
|
|
88
88
|
if (trigger.kind !== "cron") return trigger;
|
|
89
89
|
const [at = trigger.at, ...rest] = cronTimes(trigger);
|
|
90
|
-
const
|
|
91
|
-
delete
|
|
92
|
-
return rest.length > 0 ? { ...
|
|
90
|
+
const base2 = { ...trigger };
|
|
91
|
+
delete base2.alsoAt;
|
|
92
|
+
return rest.length > 0 ? { ...base2, at, alsoAt: rest } : { ...base2, at };
|
|
93
93
|
}
|
|
94
94
|
function validateWindow(trigger, startDate, endDate) {
|
|
95
95
|
const issues = [];
|
|
@@ -421,21 +421,21 @@ function triggerShape(tag, body2) {
|
|
|
421
421
|
const start = tagText(body2, "StartBoundary");
|
|
422
422
|
const time = /T(\d{2}:\d{2})/.exec(start)?.[1] ?? "";
|
|
423
423
|
const date = /^(\d{4}-\d{2}-\d{2})/.exec(start)?.[1] ?? "";
|
|
424
|
-
const
|
|
424
|
+
const base2 = { time, date, days: "", everyMinutes: 0 };
|
|
425
425
|
const interval = tagText(body2, "Interval");
|
|
426
|
-
if (interval) return { ...
|
|
427
|
-
if (/<ScheduleByDay>/i.test(body2)) return { ...
|
|
426
|
+
if (interval) return { ...base2, kind: "EVERY", everyMinutes: isoDurationMinutes(interval) };
|
|
427
|
+
if (/<ScheduleByDay>/i.test(body2)) return { ...base2, kind: "DAILY" };
|
|
428
428
|
if (/<ScheduleByWeek>/i.test(body2)) {
|
|
429
429
|
const days = WEEKDAY_NAMES.flatMap(
|
|
430
430
|
(name, idx) => new RegExp(`<${name}\\s*/>`, "i").test(body2) ? [WEEKDAY_CODES[idx]] : []
|
|
431
431
|
);
|
|
432
|
-
return { ...
|
|
432
|
+
return { ...base2, kind: "WEEKLY", days: days.join(",") };
|
|
433
433
|
}
|
|
434
434
|
if (/<ScheduleByMonth>/i.test(body2)) {
|
|
435
435
|
const days = [...body2.matchAll(/<Day>\s*(\d+)\s*<\/Day>/gi)].map((m) => Number(m[1]));
|
|
436
|
-
return { ...
|
|
436
|
+
return { ...base2, kind: "MONTHLY", days: days.sort((a, b) => a - b).join(",") };
|
|
437
437
|
}
|
|
438
|
-
return { ...
|
|
438
|
+
return { ...base2, kind: /^TimeTrigger$/i.test(tag) && date ? "ONCE" : "" };
|
|
439
439
|
}
|
|
440
440
|
function cronSpec(kind, times, days) {
|
|
441
441
|
const at = times.join(",");
|
|
@@ -903,11 +903,11 @@ var NO_REVEALED_TOOLS = {
|
|
|
903
903
|
isRevealed: () => false,
|
|
904
904
|
names: () => []
|
|
905
905
|
};
|
|
906
|
-
function revealScopedProvider(
|
|
906
|
+
function revealScopedProvider(base2, deferred, reveal) {
|
|
907
907
|
const revealed = () => deferred().filter((tool) => reveal.isRevealed(tool.name));
|
|
908
908
|
return {
|
|
909
|
-
list: () => [...
|
|
910
|
-
get: (name) =>
|
|
909
|
+
list: () => [...base2.list(), ...revealed()],
|
|
910
|
+
get: (name) => base2.get(name) ?? revealed().find((tool) => tool.name === name)
|
|
911
911
|
};
|
|
912
912
|
}
|
|
913
913
|
// src/tools/registry.ts
|
|
@@ -1158,6 +1158,14 @@ function parsePage(raw) {
|
|
|
1158
1158
|
const label = typeof raw["label"] === "string" ? raw["label"].trim() : "";
|
|
1159
1159
|
return label ? { url: rawUrl, label } : { url: rawUrl };
|
|
1160
1160
|
}
|
|
1161
|
+
function checkOptionCount(count, at, page, rawPage) {
|
|
1162
|
+
if (count === 0 && page) return void 0;
|
|
1163
|
+
if (count === 0 && rawPage !== void 0 && rawPage !== null) {
|
|
1164
|
+
return `questions[${at}] \u7684 page \u6CA1\u88AB\u6536\u4E0B\uFF08url \u53EA\u6536\u672C\u673A\u56DE\u73AF http(s)://127.0.0.1 \u6216 localhost\uFF09\uFF0C\u800C\u8FD9\u4E00\u95EE\u6CA1\u6709\u9009\u9879\u3002\u8981\u4E48\u628A url \u6539\u5BF9\uFF0C\u8981\u4E48\u7ED9 ${MIN_OPTIONS}\u2013${MAX_OPTIONS} \u4E2A\u9009\u9879`;
|
|
1165
|
+
}
|
|
1166
|
+
if (count >= MIN_OPTIONS && count <= MAX_OPTIONS) return void 0;
|
|
1167
|
+
return `questions[${at}] \u6709 ${count} \u4E2A\u9009\u9879\uFF0C\u8981 ${MIN_OPTIONS}\u2013${MAX_OPTIONS} \u4E2A\uFF08\u53EA\u6709\u5E26 page \u7684\u90A3\u4E00\u95EE\u53EF\u4EE5\u4E00\u4E2A\u90FD\u4E0D\u7ED9\uFF09\u3002**\u4E0D\u8981**\u81EA\u5DF1\u52A0\u300C\u5176\u4ED6\u300D\u9009\u9879 \u2014\u2014 \u81EA\u7531\u8F93\u5165\u7531\u754C\u9762\u81EA\u52A8\u63D0\u4F9B`;
|
|
1168
|
+
}
|
|
1161
1169
|
function parseQuestion(raw, at) {
|
|
1162
1170
|
if (!isRecord(raw)) return `questions[${at}] \u4E0D\u662F\u5BF9\u8C61`;
|
|
1163
1171
|
const question = typeof raw["question"] === "string" ? raw["question"].trim() : "";
|
|
@@ -1167,15 +1175,14 @@ function parseQuestion(raw, at) {
|
|
|
1167
1175
|
if (header.length > MAX_HEADER_CHARS) {
|
|
1168
1176
|
return `questions[${at}] \u7684 header \u6709 ${header.length} \u5B57\uFF0C\u8D85\u8FC7 ${MAX_HEADER_CHARS} \u4E0A\u9650`;
|
|
1169
1177
|
}
|
|
1170
|
-
const
|
|
1178
|
+
const page = parsePage(raw["page"]);
|
|
1179
|
+
const rawOptions = raw["options"] ?? (page ? [] : void 0);
|
|
1171
1180
|
if (!Array.isArray(rawOptions)) return `questions[${at}] \u7684 options \u4E0D\u662F\u6570\u7EC4`;
|
|
1172
1181
|
const options = rawOptions.map(parseOption);
|
|
1173
1182
|
const badAt = options.indexOf(null);
|
|
1174
1183
|
if (badAt >= 0) return `questions[${at}].options[${badAt}] \u7F3A label`;
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
}
|
|
1178
|
-
const page = parsePage(raw["page"]);
|
|
1184
|
+
const optionError = checkOptionCount(options.length, at, page, raw["page"]);
|
|
1185
|
+
if (optionError !== void 0) return optionError;
|
|
1179
1186
|
return {
|
|
1180
1187
|
question,
|
|
1181
1188
|
header,
|
|
@@ -1244,9 +1251,8 @@ var PARAMETERS = {
|
|
|
1244
1251
|
},
|
|
1245
1252
|
options: {
|
|
1246
1253
|
type: "array",
|
|
1247
|
-
minItems: MIN_OPTIONS,
|
|
1248
1254
|
maxItems: MAX_OPTIONS,
|
|
1249
|
-
description:
|
|
1255
|
+
description: `${MIN_OPTIONS}\u2013${MAX_OPTIONS} \u4E2A\uFF1B\u5E26 page \u65F6\u53EF\u4EE5\u4E0D\u7ED9\u3002\u4E0D\u8981\u81EA\u5DF1\u52A0\u300C\u5176\u4ED6\u300D\u2014\u2014 \u754C\u9762\u4F1A\u81EA\u52A8\u63D0\u4F9B\u81EA\u7531\u8F93\u5165`,
|
|
1250
1256
|
items: {
|
|
1251
1257
|
type: "object",
|
|
1252
1258
|
properties: {
|
|
@@ -1270,7 +1276,7 @@ var PARAMETERS = {
|
|
|
1270
1276
|
required: ["url"]
|
|
1271
1277
|
}
|
|
1272
1278
|
},
|
|
1273
|
-
required: ["question", "header", "
|
|
1279
|
+
required: ["question", "header", "multiSelect"]
|
|
1274
1280
|
}
|
|
1275
1281
|
}
|
|
1276
1282
|
},
|
|
@@ -1286,7 +1292,7 @@ var DESCRIPTION = [
|
|
|
1286
1292
|
"\u9009\u9879\u91CC**\u4E0D\u8981**\u81EA\u5DF1\u653E\u300C\u5176\u4ED6\u300D\uFF1A\u754C\u9762\u4F1A\u81EA\u52A8\u63D0\u4F9B\u81EA\u7531\u8F93\u5165\u3002",
|
|
1287
1293
|
"\u26A0\uFE0F \u63D2\u4EF6\u7684\u5DE5\u5177\u7ED9\u4E86\u4F60\u4E00\u4E2A**\u672C\u673A\u5730\u5740**\u3001\u800C\u90A3\u4E2A\u9009\u62E9\u7528\u51E0\u4E2A\u9009\u9879\u8BF4\u4E0D\u6E05\u65F6\uFF08\u51E0\u5341\u5F20\u56FE\u91CC\u6311\u4E00\u5F20\u3001",
|
|
1288
1294
|
"\u4E00\u5F20\u8868\u91CC\u5708\u51E0\u884C\uFF09\uFF0C\u628A\u5730\u5740\u653E\u8FDB `questions[].page` \u2014\u2014 \u7528\u6237\u4F1A\u5728\u5F39\u7A97\u91CC\u76F4\u63A5\u770B\u5230\u90A3\u4E00\u9875\u3002",
|
|
1289
|
-
"\
|
|
1295
|
+
"\u5E26\u4E86 page \u7684\u90A3\u4E00\u95EE\uFF0C`options` \u53EF\u4EE5\u4E0D\u7ED9\u3002"
|
|
1290
1296
|
].join("");
|
|
1291
1297
|
function createAskQuestionTool(deps) {
|
|
1292
1298
|
return {
|
|
@@ -1314,7 +1320,9 @@ function createAskQuestionTool(deps) {
|
|
|
1314
1320
|
"**\u6309\u4F60\u81EA\u5DF1\u7684\u5224\u65AD\u6311\u6700\u5408\u7406\u7684\u9ED8\u8BA4\u7EE7\u7EED\u505A**\uFF0C\u5E76\u5728\u56DE\u7B54\u91CC\u8BF4\u660E\u4F60\u9009\u4E86\u4EC0\u4E48\u3001\u4E3A\u4EC0\u4E48\u3002",
|
|
1315
1321
|
"\u4F60\u672C\u6765\u60F3\u95EE\u7684\u662F\uFF1A",
|
|
1316
1322
|
...parsed.questions.map(
|
|
1317
|
-
(q) =>
|
|
1323
|
+
(q) => (
|
|
1324
|
+
`- ${q.question}\uFF08${q.options.length > 0 ? q.options.map((o) => o.label).join(" / ") : q.page?.url}\uFF09`
|
|
1325
|
+
)
|
|
1318
1326
|
),
|
|
1319
1327
|
"",
|
|
1320
1328
|
"\u4E0B\u6B21\u9047\u5230\u540C\u7C7B\u5206\u53C9\uFF0C\u628A\u9700\u8981\u7684\u4FE1\u606F\u5199\u8FDB\u63D0\u793A\u8BCD\u4F1A\u66F4\u7701\u4E00\u8F6E\u3002"
|
|
@@ -2441,22 +2449,22 @@ function timeoutMessage(timeoutMs, toolCalls) {
|
|
|
2441
2449
|
return timeoutNotice(Math.round(timeoutMs / 1e3), toolCalls);
|
|
2442
2450
|
}
|
|
2443
2451
|
function toOutcome(ending, ctx) {
|
|
2444
|
-
const
|
|
2452
|
+
const base2 = { value: "", printed: ctx.printed, toolCalls: ctx.toolCalls };
|
|
2445
2453
|
if (ctx.capped) {
|
|
2446
2454
|
return {
|
|
2447
|
-
...
|
|
2455
|
+
...base2,
|
|
2448
2456
|
ok: false,
|
|
2449
2457
|
error: { kind: "cap", message: capNotice(ctx.limits.maxToolCalls) }
|
|
2450
2458
|
};
|
|
2451
2459
|
}
|
|
2452
2460
|
if (!ending) {
|
|
2453
|
-
return { ...
|
|
2461
|
+
return { ...base2, ok: false, error: { kind: "throw", message: noResultNotice() } };
|
|
2454
2462
|
}
|
|
2455
|
-
if (ending.kind === "done") return { ...
|
|
2463
|
+
if (ending.kind === "done") return { ...base2, ok: true, value: ending.value };
|
|
2456
2464
|
if (ending.kind === "failed") {
|
|
2457
|
-
return { ...
|
|
2465
|
+
return { ...base2, ok: false, error: { kind: "throw", message: ending.message } };
|
|
2458
2466
|
}
|
|
2459
|
-
return { ...
|
|
2467
|
+
return { ...base2, ok: false, error: { kind: ending.kind, message: ending.message } };
|
|
2460
2468
|
}
|
|
2461
2469
|
// src/tools/builtin/run-code.ts
|
|
2462
2470
|
var RUN_CODE_TOOL = "run_code";
|
|
@@ -2940,9 +2948,9 @@ function parseScheduleDraft(raw) {
|
|
|
2940
2948
|
}
|
|
2941
2949
|
function draftWidening(draft, current) {
|
|
2942
2950
|
const out = [];
|
|
2943
|
-
const
|
|
2951
|
+
const base2 = draft.action === "update" && current ? current.permission === "bypass" ? "auto" : current.permission : "default";
|
|
2944
2952
|
const level = draft.permission;
|
|
2945
|
-
if (level !== void 0 && PERMISSION_ORDER.indexOf(level) > PERMISSION_ORDER.indexOf(
|
|
2953
|
+
if (level !== void 0 && PERMISSION_ORDER.indexOf(level) > PERMISSION_ORDER.indexOf(base2)) {
|
|
2946
2954
|
out.push("permission");
|
|
2947
2955
|
}
|
|
2948
2956
|
const has = (list, x) => list?.includes(x) ?? false;
|
|
@@ -3195,18 +3203,18 @@ function contentMatchesCommand(content, text) {
|
|
|
3195
3203
|
}
|
|
3196
3204
|
function resolvePathPattern(content, workDir) {
|
|
3197
3205
|
let raw = content;
|
|
3198
|
-
let
|
|
3206
|
+
let base2 = workDir;
|
|
3199
3207
|
if (raw.startsWith("//")) {
|
|
3200
3208
|
raw = raw.slice(1);
|
|
3201
|
-
|
|
3209
|
+
base2 = "";
|
|
3202
3210
|
} else if (raw.startsWith("~/")) {
|
|
3203
3211
|
raw = raw.slice(2);
|
|
3204
|
-
|
|
3212
|
+
base2 = homedir();
|
|
3205
3213
|
}
|
|
3206
3214
|
const wildcardAt = raw.search(/[*?]/);
|
|
3207
3215
|
const head = wildcardAt === -1 ? raw : raw.slice(0, wildcardAt);
|
|
3208
3216
|
const tail = wildcardAt === -1 ? "" : raw.slice(wildcardAt);
|
|
3209
|
-
const resolvedHead =
|
|
3217
|
+
const resolvedHead = base2 === "" && isAbsolute(head) ? resolve(head) : resolve(base2 || ".", head);
|
|
3210
3218
|
const sep8 = head.endsWith("/") || head.endsWith("\\") ? "/" : "";
|
|
3211
3219
|
return `${resolvedHead}${sep8}${tail}`;
|
|
3212
3220
|
}
|
|
@@ -3807,10 +3815,10 @@ function belowTrigramFloor(safeQuery) {
|
|
|
3807
3815
|
// src/session/trace.ts
|
|
3808
3816
|
var NODE_COLUMNS = `id, title, cwd, started_at, message_count, archived, parent_id`;
|
|
3809
3817
|
function toNode(ctx, row, depth) {
|
|
3810
|
-
const
|
|
3811
|
-
if (!ctx.visible(row.id)) return { ...
|
|
3818
|
+
const base2 = { sessionId: row.id, depth, parentId: row.parent_id ?? void 0 };
|
|
3819
|
+
if (!ctx.visible(row.id)) return { ...base2, visible: false };
|
|
3812
3820
|
return {
|
|
3813
|
-
...
|
|
3821
|
+
...base2,
|
|
3814
3822
|
visible: true,
|
|
3815
3823
|
title: row.title,
|
|
3816
3824
|
cwd: row.cwd ?? void 0,
|
|
@@ -3970,11 +3978,11 @@ var SessionQueries = class {
|
|
|
3970
3978
|
const meta = this.db.prepare(`SELECT s.title FROM sessions s WHERE s.id = @sid AND ${vis.clause}`).get({ ...vis.params, sid: input.sessionId });
|
|
3971
3979
|
if (!meta) return { authorized: false, title: "", before: [], after: [], total: 0 };
|
|
3972
3980
|
const total = this.db.prepare(`SELECT count(*) AS n FROM messages WHERE session_id = @sid`).get({ sid: input.sessionId }).n;
|
|
3973
|
-
const
|
|
3981
|
+
const base2 = { authorized: true, title: meta.title, total };
|
|
3974
3982
|
const anchor = this.recordRow(input.sessionId, input.messageId);
|
|
3975
|
-
if (!anchor) return { ...
|
|
3983
|
+
if (!anchor) return { ...base2, before: [], after: [] };
|
|
3976
3984
|
return {
|
|
3977
|
-
...
|
|
3985
|
+
...base2,
|
|
3978
3986
|
anchor: toRecord(anchor),
|
|
3979
3987
|
before: this.neighbourRows(input.sessionId, input.messageId, "before", input.before).reverse().map(toRecord),
|
|
3980
3988
|
after: this.neighbourRows(input.sessionId, input.messageId, "after", input.after).map(
|
|
@@ -5693,11 +5701,11 @@ function pushNarrowed(notices, kind, incoming, wanted, kept) {
|
|
|
5693
5701
|
cut: wanted.filter((n) => !keptSet.has(n))
|
|
5694
5702
|
});
|
|
5695
5703
|
}
|
|
5696
|
-
function intersectNames(
|
|
5697
|
-
if (!
|
|
5698
|
-
if (!
|
|
5699
|
-
if (!incoming) return [...
|
|
5700
|
-
const baseSet = new Set(
|
|
5704
|
+
function intersectNames(base2, incoming) {
|
|
5705
|
+
if (!base2 && !incoming) return void 0;
|
|
5706
|
+
if (!base2) return incoming ? [...incoming] : void 0;
|
|
5707
|
+
if (!incoming) return [...base2];
|
|
5708
|
+
const baseSet = new Set(base2);
|
|
5701
5709
|
return incoming.filter((t81) => baseSet.has(t81));
|
|
5702
5710
|
}
|
|
5703
5711
|
function findRole(roles, name) {
|
|
@@ -5842,7 +5850,7 @@ function describe2(err, path, lang) {
|
|
|
5842
5850
|
return t("role_write.remove_failed", { path, reason: reason9 }, lang);
|
|
5843
5851
|
}
|
|
5844
5852
|
// src/agent-role/scope.ts
|
|
5845
|
-
function roleScopedProvider(
|
|
5853
|
+
function roleScopedProvider(base2, role, blocked) {
|
|
5846
5854
|
const blockedSet = new Set(blocked);
|
|
5847
5855
|
const allow = role?.tools ? new Set(role.tools) : void 0;
|
|
5848
5856
|
const permitted = (name) => {
|
|
@@ -5850,14 +5858,14 @@ function roleScopedProvider(base, role, blocked) {
|
|
|
5850
5858
|
return allow ? allow.has(name) : true;
|
|
5851
5859
|
};
|
|
5852
5860
|
return {
|
|
5853
|
-
list: () =>
|
|
5854
|
-
get: (name) => permitted(name) ?
|
|
5861
|
+
list: () => base2.list().filter((t81) => permitted(t81.name)),
|
|
5862
|
+
get: (name) => permitted(name) ? base2.get(name) : void 0
|
|
5855
5863
|
};
|
|
5856
5864
|
}
|
|
5857
|
-
function createRoleScope(
|
|
5865
|
+
function createRoleScope(base2, sessionRole, blocked) {
|
|
5858
5866
|
let perMessage;
|
|
5859
5867
|
const current = () => perMessage ?? sessionRole;
|
|
5860
|
-
const scoped = () => roleScopedProvider(
|
|
5868
|
+
const scoped = () => roleScopedProvider(base2, current(), blocked);
|
|
5861
5869
|
return {
|
|
5862
5870
|
provider: {
|
|
5863
5871
|
list: () => scoped().list(),
|
|
@@ -5875,9 +5883,9 @@ function createRoleScope(base, sessionRole, blocked) {
|
|
|
5875
5883
|
}
|
|
5876
5884
|
};
|
|
5877
5885
|
}
|
|
5878
|
-
function unknownRoleTools(
|
|
5886
|
+
function unknownRoleTools(base2, role, blocked) {
|
|
5879
5887
|
if (!role?.tools) return [];
|
|
5880
|
-
const available2 = new Set(
|
|
5888
|
+
const available2 = new Set(base2.list().map((t81) => t81.name));
|
|
5881
5889
|
const blockedSet = new Set(blocked);
|
|
5882
5890
|
return role.tools.filter((name) => !available2.has(name) && !blockedSet.has(name));
|
|
5883
5891
|
}
|
|
@@ -5965,23 +5973,23 @@ function loadCommands(files, source, prefix) {
|
|
|
5965
5973
|
}
|
|
5966
5974
|
function loadCommandFile(file, source, prefix) {
|
|
5967
5975
|
const at = file.relPath;
|
|
5968
|
-
const
|
|
5969
|
-
if (!isValidCommandName(
|
|
5976
|
+
const base2 = commandNameFromRelPath(file.relPath);
|
|
5977
|
+
if (!isValidCommandName(base2)) {
|
|
5970
5978
|
return {
|
|
5971
5979
|
issues: [
|
|
5972
5980
|
{
|
|
5973
5981
|
path: at,
|
|
5974
|
-
message: t("custom_command.bad_name", { name:
|
|
5982
|
+
message: t("custom_command.bad_name", { name: base2 })
|
|
5975
5983
|
}
|
|
5976
5984
|
]
|
|
5977
5985
|
};
|
|
5978
5986
|
}
|
|
5979
|
-
if (!prefix && RESERVED_COMMAND_NAMES.includes(
|
|
5987
|
+
if (!prefix && RESERVED_COMMAND_NAMES.includes(base2)) {
|
|
5980
5988
|
return {
|
|
5981
|
-
issues: [{ path: at, message: t("custom_command.reserved", { name:
|
|
5989
|
+
issues: [{ path: at, message: t("custom_command.reserved", { name: base2 }) }]
|
|
5982
5990
|
};
|
|
5983
5991
|
}
|
|
5984
|
-
const name = prefix ? `${prefix}:${
|
|
5992
|
+
const name = prefix ? `${prefix}:${base2}` : base2;
|
|
5985
5993
|
let raw;
|
|
5986
5994
|
try {
|
|
5987
5995
|
raw = readFileSync(file.path, "utf8");
|
|
@@ -6209,14 +6217,14 @@ function sortByName(commands) {
|
|
|
6209
6217
|
return [...commands].sort((a, b) => a.name.localeCompare(b.name));
|
|
6210
6218
|
}
|
|
6211
6219
|
// src/extensions/tool-scope.ts
|
|
6212
|
-
function createToolScope(
|
|
6220
|
+
function createToolScope(base2) {
|
|
6213
6221
|
let allow;
|
|
6214
6222
|
let names;
|
|
6215
6223
|
const permitted = (name) => allow ? allow.has(name) : true;
|
|
6216
6224
|
return {
|
|
6217
6225
|
provider: {
|
|
6218
|
-
list: () =>
|
|
6219
|
-
get: (name) => permitted(name) ?
|
|
6226
|
+
list: () => base2.list().filter((t81) => permitted(t81.name)),
|
|
6227
|
+
get: (name) => permitted(name) ? base2.get(name) : void 0
|
|
6220
6228
|
},
|
|
6221
6229
|
enter: (next) => {
|
|
6222
6230
|
const prevAllow = allow;
|
|
@@ -6608,11 +6616,11 @@ function expandToken(token) {
|
|
|
6608
6616
|
const tilde = token.startsWith("~");
|
|
6609
6617
|
const body2 = caret || tilde ? token.slice(1) : token;
|
|
6610
6618
|
if (caret || tilde) {
|
|
6611
|
-
const
|
|
6612
|
-
if (!
|
|
6619
|
+
const base2 = parseVersion(body2);
|
|
6620
|
+
if (!base2) return null;
|
|
6613
6621
|
return [
|
|
6614
|
-
{ op: ">=", version:
|
|
6615
|
-
{ op: "<", version: caret ? caretUpper(
|
|
6622
|
+
{ op: ">=", version: base2 },
|
|
6623
|
+
{ op: "<", version: caret ? caretUpper(base2) : tildeUpper(base2) }
|
|
6616
6624
|
];
|
|
6617
6625
|
}
|
|
6618
6626
|
const match = /^(>=|<=|>|<|=)?\s*(.+)$/.exec(body2);
|
|
@@ -7634,9 +7642,9 @@ function resolveEntrySource(entry, marketSource) {
|
|
|
7634
7642
|
if (rel === "" || rel.split("/").includes("..")) return void 0;
|
|
7635
7643
|
const market = marketSource.trim();
|
|
7636
7644
|
if (market.startsWith("github:") || market.startsWith("git+")) {
|
|
7637
|
-
const [
|
|
7645
|
+
const [base2 = "", fragment] = market.split("#", 2);
|
|
7638
7646
|
const prefix = fragment?.startsWith("subdir=") ? `${fragment.slice("subdir=".length)}/` : "";
|
|
7639
|
-
return `${
|
|
7647
|
+
return `${base2}#subdir=${prefix}${rel}`;
|
|
7640
7648
|
}
|
|
7641
7649
|
if (market.startsWith("https://") || market.startsWith("http://")) {
|
|
7642
7650
|
return `${market.replace(/\/[^/]*$/, "")}/${rel}`;
|
|
@@ -9627,7 +9635,7 @@ function admitArtifact(raw, policy) {
|
|
|
9627
9635
|
const dims = size ? ` ${size.width}\xD7${size.height}` : "";
|
|
9628
9636
|
const head = raw.note ? `${raw.note} ` : "";
|
|
9629
9637
|
const reason9 = rejectionReason(raw, policy, byteLen, size);
|
|
9630
|
-
const
|
|
9638
|
+
const base2 = {
|
|
9631
9639
|
kind: raw.kind,
|
|
9632
9640
|
mediaType: raw.mediaType,
|
|
9633
9641
|
bytes: byteLen,
|
|
@@ -9636,13 +9644,13 @@ function admitArtifact(raw, policy) {
|
|
|
9636
9644
|
};
|
|
9637
9645
|
if (reason9) {
|
|
9638
9646
|
return {
|
|
9639
|
-
...
|
|
9647
|
+
...base2,
|
|
9640
9648
|
summary: `${head}[${raw.mediaType}${dims}\uFF0C${formatBytes(byteLen)}] \u672A\u8FDB\u5165\u4E0A\u4E0B\u6587\uFF1A${reason9}${storedAt(path)}\u3002\u8BF7\u636E\u6B64\u544A\u77E5\u7528\u6237\uFF0C\u5FC5\u8981\u65F6\u8BA9\u7528\u6237\u63D0\u4F9B\u66F4\u5C0F\u7684\u56FE\u3002`
|
|
9641
9649
|
};
|
|
9642
9650
|
}
|
|
9643
9651
|
const tokens = size ? estimateImageTokens(size.width, size.height) : void 0;
|
|
9644
9652
|
return {
|
|
9645
|
-
...
|
|
9653
|
+
...base2,
|
|
9646
9654
|
data: raw.base64,
|
|
9647
9655
|
summary: `${head}[${raw.mediaType}${dims}\uFF0C${formatBytes(byteLen)}]${storedAt(path)}`,
|
|
9648
9656
|
...tokens === void 0 ? {} : { estimatedTokens: tokens }
|
|
@@ -10016,8 +10024,8 @@ function isReachable(real, state) {
|
|
|
10016
10024
|
if (within(state.rootReal, real) || within(state.epochHomeReal, real)) return true;
|
|
10017
10025
|
return state.allowExternal(real);
|
|
10018
10026
|
}
|
|
10019
|
-
function within(
|
|
10020
|
-
const rel = relative(
|
|
10027
|
+
function within(base2, target) {
|
|
10028
|
+
const rel = relative(base2, target);
|
|
10021
10029
|
return !rel.startsWith("..") && !isAbsolute(rel);
|
|
10022
10030
|
}
|
|
10023
10031
|
function resolveSpec(spec, fromFile) {
|
|
@@ -10795,13 +10803,54 @@ function classifyProviderError(err) {
|
|
|
10795
10803
|
}
|
|
10796
10804
|
return { code: 9999, recovery: "retry" };
|
|
10797
10805
|
}
|
|
10798
|
-
function toModelMessages(messages) {
|
|
10799
|
-
return messages.map(toModelMessage);
|
|
10806
|
+
function toModelMessages(messages, opts = {}) {
|
|
10807
|
+
if (opts.toolMediaInline !== false) return messages.map(toModelMessage);
|
|
10808
|
+
return hoistToolMedia(messages);
|
|
10809
|
+
}
|
|
10810
|
+
function hoistToolMedia(messages) {
|
|
10811
|
+
const out = [];
|
|
10812
|
+
let pending = [];
|
|
10813
|
+
for (const [i, m] of messages.entries()) {
|
|
10814
|
+
if (m.role !== "tool") {
|
|
10815
|
+
out.push(toModelMessage(m));
|
|
10816
|
+
continue;
|
|
10817
|
+
}
|
|
10818
|
+
const results = (m.toolResults ?? []).map((r) => {
|
|
10819
|
+
const inline = inlineArtifacts(r);
|
|
10820
|
+
if (inline.length === 0) return r;
|
|
10821
|
+
for (const artifact of inline) {
|
|
10822
|
+
pending.push({ label: `${r.toolName} (${r.toolCallId})`, artifact });
|
|
10823
|
+
}
|
|
10824
|
+
const note = `(${inline.length} image(s) from this tool call are attached in the next message.)`;
|
|
10825
|
+
return {
|
|
10826
|
+
...r,
|
|
10827
|
+
output: r.output ? `${r.output}
|
|
10828
|
+
${note}` : note,
|
|
10829
|
+
artifacts: (r.artifacts ?? []).map(({ data: _data, ...rest }) => rest)
|
|
10830
|
+
};
|
|
10831
|
+
});
|
|
10832
|
+
out.push(toModelMessage({ ...m, toolResults: results }));
|
|
10833
|
+
if (pending.length > 0 && messages[i + 1]?.role !== "tool") {
|
|
10834
|
+
out.push({
|
|
10835
|
+
role: "user",
|
|
10836
|
+
content: [
|
|
10837
|
+
{ type: "text", text: "Images returned by the tool call(s) above:" },
|
|
10838
|
+
...pending.flatMap(({ label, artifact }) => [
|
|
10839
|
+
{ type: "text", text: label },
|
|
10840
|
+
{ type: "file", mediaType: artifact.mediaType, data: artifact.data }
|
|
10841
|
+
])
|
|
10842
|
+
]
|
|
10843
|
+
});
|
|
10844
|
+
pending = [];
|
|
10845
|
+
}
|
|
10846
|
+
}
|
|
10847
|
+
return out;
|
|
10848
|
+
}
|
|
10849
|
+
function inlineArtifacts(r) {
|
|
10850
|
+
return (r.artifacts ?? []).filter((a) => Boolean(a.data));
|
|
10800
10851
|
}
|
|
10801
10852
|
function toToolOutput(r) {
|
|
10802
|
-
const inline = (r
|
|
10803
|
-
(a) => Boolean(a.data)
|
|
10804
|
-
);
|
|
10853
|
+
const inline = inlineArtifacts(r);
|
|
10805
10854
|
if (inline.length === 0) return { type: "text", value: r.output };
|
|
10806
10855
|
return {
|
|
10807
10856
|
type: "content",
|
|
@@ -11570,17 +11619,25 @@ function resolveArtifactRetention(partial) {
|
|
|
11570
11619
|
function measure(dir) {
|
|
11571
11620
|
let bytes = 0;
|
|
11572
11621
|
let mtimeMs = 0;
|
|
11573
|
-
|
|
11574
|
-
|
|
11622
|
+
const pending = [dir];
|
|
11623
|
+
while (pending.length > 0) {
|
|
11624
|
+
const at = pending.pop();
|
|
11625
|
+
let names;
|
|
11626
|
+
try {
|
|
11627
|
+
names = readdirSync(at);
|
|
11628
|
+
} catch {
|
|
11629
|
+
continue;
|
|
11630
|
+
}
|
|
11631
|
+
for (const name of names) {
|
|
11575
11632
|
try {
|
|
11576
|
-
const st =
|
|
11633
|
+
const st = lstatSync(join(at, name));
|
|
11634
|
+
if (st.isDirectory()) pending.push(join(at, name));
|
|
11577
11635
|
if (!st.isFile()) continue;
|
|
11578
11636
|
bytes += st.size;
|
|
11579
11637
|
mtimeMs = Math.max(mtimeMs, st.mtimeMs);
|
|
11580
11638
|
} catch {
|
|
11581
11639
|
}
|
|
11582
11640
|
}
|
|
11583
|
-
} catch {
|
|
11584
11641
|
}
|
|
11585
11642
|
return { path: dir, bytes, mtimeMs };
|
|
11586
11643
|
}
|
|
@@ -11834,7 +11891,7 @@ var ToolExecutor = class {
|
|
|
11834
11891
|
pruned = false;
|
|
11835
11892
|
async execute(tc, sessionId, signal, parentSpan, onOutput, depth = 0) {
|
|
11836
11893
|
const et = this.toolFor(tc.toolName, depth);
|
|
11837
|
-
const
|
|
11894
|
+
const base2 = { toolCallId: tc.toolCallId, toolName: tc.toolName, args: tc.input };
|
|
11838
11895
|
const telemetry = this.deps.telemetry ?? NOOP_TELEMETRY;
|
|
11839
11896
|
const startedAt = Date.now();
|
|
11840
11897
|
const span = telemetry.startSpan(
|
|
@@ -11862,7 +11919,7 @@ var ToolExecutor = class {
|
|
|
11862
11919
|
if (!et) {
|
|
11863
11920
|
const declared = this.codeModeDeclared(tc.toolName);
|
|
11864
11921
|
return finish({
|
|
11865
|
-
...
|
|
11922
|
+
...base2,
|
|
11866
11923
|
success: false,
|
|
11867
11924
|
output: declared ? codeModeDeclaredNotice(tc.toolName) : `\u5DE5\u5177 ${tc.toolName} \u5F53\u524D\u4E0D\u53EF\u7528\uFF08\u53EF\u80FD\u5DF2\u88AB MCP server \u64A4\u4E0B\uFF09\u3002\u8BF7\u7528\u5F53\u524D\u5DE5\u5177\u8868\u91CC\u7684\u5DE5\u5177\u91CD\u8BD5\u3002`
|
|
11868
11925
|
});
|
|
@@ -11881,14 +11938,14 @@ var ToolExecutor = class {
|
|
|
11881
11938
|
);
|
|
11882
11939
|
if (blocked?.blocked) {
|
|
11883
11940
|
return finish({
|
|
11884
|
-
...
|
|
11941
|
+
...base2,
|
|
11885
11942
|
success: false,
|
|
11886
11943
|
output: `\u64CD\u4F5C\u88AB hook \u963B\u6B62: ${blocked.reason || "\u672A\u77E5\u539F\u56E0"}`
|
|
11887
11944
|
});
|
|
11888
11945
|
}
|
|
11889
11946
|
const gate = await this.checkPermission(et, tc.input, signal);
|
|
11890
11947
|
if (!gate.allowed) {
|
|
11891
|
-
return finish({ ...
|
|
11948
|
+
return finish({ ...base2, success: false, output: gate.output });
|
|
11892
11949
|
}
|
|
11893
11950
|
const targets = this.writeTargets(et, tc.input);
|
|
11894
11951
|
const snapshotted = await this.snapshot(sessionId, targets);
|
|
@@ -11933,7 +11990,7 @@ var ToolExecutor = class {
|
|
|
11933
11990
|
const artifacts = r.artifacts ? admitArtifacts(r.artifacts, this.policy(sessionId)) : [];
|
|
11934
11991
|
const diffs = before ? await collectDiffs(before, this.deps.workDir()) : [];
|
|
11935
11992
|
return finish({
|
|
11936
|
-
...
|
|
11993
|
+
...base2,
|
|
11937
11994
|
success: true,
|
|
11938
11995
|
output: this.renderOutput(
|
|
11939
11996
|
et,
|
|
@@ -11944,7 +12001,7 @@ var ToolExecutor = class {
|
|
|
11944
12001
|
});
|
|
11945
12002
|
}
|
|
11946
12003
|
return finish({
|
|
11947
|
-
...
|
|
12004
|
+
...base2,
|
|
11948
12005
|
success: false,
|
|
11949
12006
|
output: this.renderOutput(
|
|
11950
12007
|
et,
|
|
@@ -11954,7 +12011,7 @@ var ToolExecutor = class {
|
|
|
11954
12011
|
} catch (err) {
|
|
11955
12012
|
span.recordError(err);
|
|
11956
12013
|
return finish({
|
|
11957
|
-
...
|
|
12014
|
+
...base2,
|
|
11958
12015
|
success: false,
|
|
11959
12016
|
output: `\u5DE5\u5177\u6267\u884C\u5F02\u5E38: ${err instanceof Error ? err.message : String(err)}`
|
|
11960
12017
|
});
|
|
@@ -12009,10 +12066,10 @@ var ToolExecutor = class {
|
|
|
12009
12066
|
return text;
|
|
12010
12067
|
}
|
|
12011
12068
|
policy(sessionId) {
|
|
12012
|
-
const
|
|
12013
|
-
if (
|
|
12014
|
-
this.pruneOnce(
|
|
12015
|
-
return { ...
|
|
12069
|
+
const base2 = this.deps.multimodal?.() ?? resolveMultimodalPolicy();
|
|
12070
|
+
if (base2.artifactsDir) return base2;
|
|
12071
|
+
this.pruneOnce(base2.homeDir);
|
|
12072
|
+
return { ...base2, artifactsDir: artifactsDir(sessionId, base2.homeDir) };
|
|
12016
12073
|
}
|
|
12017
12074
|
pruneOnce(homeDir) {
|
|
12018
12075
|
if (this.pruned) return;
|
|
@@ -13013,16 +13070,147 @@ function readVersion(raw, issues) {
|
|
|
13013
13070
|
function isPlainObject(value) {
|
|
13014
13071
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
13015
13072
|
}
|
|
13016
|
-
var
|
|
13017
|
-
|
|
13018
|
-
|
|
13019
|
-
|
|
13020
|
-
|
|
13021
|
-
|
|
13022
|
-
|
|
13023
|
-
|
|
13024
|
-
|
|
13073
|
+
var MODEL_ENTRIES_FILE = "models.json";
|
|
13074
|
+
var FORMAT_VERSION = 1;
|
|
13075
|
+
function modelEntriesPath(homeDir) {
|
|
13076
|
+
return join(homeDir, MODEL_ENTRIES_FILE);
|
|
13077
|
+
}
|
|
13078
|
+
var positiveInt = z.int().positive();
|
|
13079
|
+
var ModelEntrySchema = z.object({
|
|
13080
|
+
id: z.string().refine(isModelEntryId, { message: "invalid id" }),
|
|
13081
|
+
name: z.string().optional(),
|
|
13082
|
+
provider: z.enum(PROVIDER_TYPES),
|
|
13083
|
+
protocol: z.enum(MODEL_ENTRY_PROTOCOLS).optional(),
|
|
13084
|
+
baseUrl: z.url({ protocol: /^https?$/ }).optional(),
|
|
13085
|
+
rawUrl: z.boolean().optional(),
|
|
13086
|
+
model: z.string().trim().min(1),
|
|
13087
|
+
capabilities: z.object({
|
|
13088
|
+
tools: z.boolean().optional(),
|
|
13089
|
+
images: z.boolean().optional(),
|
|
13090
|
+
reasoning: z.boolean().optional(),
|
|
13091
|
+
reasoningOnly: z.boolean().optional(),
|
|
13092
|
+
reasoningToggle: z.boolean().optional()
|
|
13093
|
+
}).optional(),
|
|
13094
|
+
reasoning: z.object({
|
|
13095
|
+
levels: z.array(z.enum(REASONING_EFFORTS)),
|
|
13096
|
+
default: z.enum(REASONING_EFFORTS).optional()
|
|
13097
|
+
}).optional(),
|
|
13098
|
+
contextLength: positiveInt.optional(),
|
|
13099
|
+
maxOutputTokens: positiveInt.optional(),
|
|
13100
|
+
host: z.literal(true).optional()
|
|
13025
13101
|
});
|
|
13102
|
+
function normalizeModelEntry(entry) {
|
|
13103
|
+
const custom = entry.provider === "openai-compatible";
|
|
13104
|
+
const protocol = custom ? entry.protocol ?? "openai-chat" : void 0;
|
|
13105
|
+
const out = { id: entry.id, provider: entry.provider, model: entry.model.trim() };
|
|
13106
|
+
const name = entry.name?.trim();
|
|
13107
|
+
if (name) out.name = name;
|
|
13108
|
+
if (protocol) out.protocol = protocol;
|
|
13109
|
+
const rawUrl = Boolean(entry.rawUrl && protocol === "openai-chat" && entry.baseUrl?.trim());
|
|
13110
|
+
const baseUrl = entry.baseUrl?.trim();
|
|
13111
|
+
if (baseUrl) out.baseUrl = rawUrl ? baseUrl : toBaseUrl(baseUrl, entry);
|
|
13112
|
+
if (rawUrl) out.rawUrl = true;
|
|
13113
|
+
const caps = entry.capabilities;
|
|
13114
|
+
if (caps && Object.values(caps).some((v) => v !== void 0)) {
|
|
13115
|
+
out.capabilities = Object.fromEntries(
|
|
13116
|
+
Object.entries(caps).filter(([, v]) => v !== void 0)
|
|
13117
|
+
);
|
|
13118
|
+
}
|
|
13119
|
+
if (entry.reasoning) {
|
|
13120
|
+
const levels = REASONING_EFFORTS.filter((l) => entry.reasoning?.levels.includes(l));
|
|
13121
|
+
if (levels.length > 0) {
|
|
13122
|
+
const def = entry.reasoning.default;
|
|
13123
|
+
out.reasoning = def && levels.includes(def) ? { levels, default: def } : { levels };
|
|
13124
|
+
}
|
|
13125
|
+
}
|
|
13126
|
+
if (entry.contextLength) out.contextLength = entry.contextLength;
|
|
13127
|
+
if (entry.maxOutputTokens) out.maxOutputTokens = entry.maxOutputTokens;
|
|
13128
|
+
if (entry.host) out.host = true;
|
|
13129
|
+
return out;
|
|
13130
|
+
}
|
|
13131
|
+
function toBaseUrl(url, entry) {
|
|
13132
|
+
const anthropic = entry.provider === "anthropic" || entry.provider === "openai-compatible" && entry.protocol === "anthropic-messages";
|
|
13133
|
+
const suffix = anthropic ? /\/messages\/?$/ : /\/chat\/completions\/?$/;
|
|
13134
|
+
return url.replace(suffix, "").replace(/\/+$/, "");
|
|
13135
|
+
}
|
|
13136
|
+
function parseModelEntry(raw) {
|
|
13137
|
+
const parsed = ModelEntrySchema.safeParse(raw);
|
|
13138
|
+
if (!parsed.success) {
|
|
13139
|
+
const first2 = parsed.error.issues[0];
|
|
13140
|
+
return {
|
|
13141
|
+
ok: false,
|
|
13142
|
+
reason: first2 ? `${first2.path.join(".") || "(root)"}: ${first2.message}` : ""
|
|
13143
|
+
};
|
|
13144
|
+
}
|
|
13145
|
+
return { ok: true, entry: normalizeModelEntry(parsed.data) };
|
|
13146
|
+
}
|
|
13147
|
+
function loadModelEntries(homeDir) {
|
|
13148
|
+
const path = modelEntriesPath(homeDir);
|
|
13149
|
+
if (!existsSync(path)) return { entries: [], issues: [] };
|
|
13150
|
+
const hostLabelOf = (raw2) => {
|
|
13151
|
+
const label = raw2?.hostLabel;
|
|
13152
|
+
return typeof label === "string" && label.trim() !== "" ? label.trim() : void 0;
|
|
13153
|
+
};
|
|
13154
|
+
let raw;
|
|
13155
|
+
try {
|
|
13156
|
+
raw = JSON.parse(readFileSync(path, "utf-8"));
|
|
13157
|
+
} catch (err) {
|
|
13158
|
+
return {
|
|
13159
|
+
entries: [],
|
|
13160
|
+
issues: [`${path}: ${err instanceof Error ? err.message : String(err)}`]
|
|
13161
|
+
};
|
|
13162
|
+
}
|
|
13163
|
+
const list = raw?.models;
|
|
13164
|
+
if (!Array.isArray(list)) {
|
|
13165
|
+
return { entries: [], issues: [`${path}: "models" is not an array`] };
|
|
13166
|
+
}
|
|
13167
|
+
const hostLabel = hostLabelOf(raw);
|
|
13168
|
+
const entries2 = [];
|
|
13169
|
+
const issues = [];
|
|
13170
|
+
const seen = /* @__PURE__ */ new Set();
|
|
13171
|
+
list.forEach((item, i) => {
|
|
13172
|
+
const parsed = parseModelEntry(item);
|
|
13173
|
+
if (!parsed.ok) {
|
|
13174
|
+
issues.push(`${path} models[${i}]: ${parsed.reason}`);
|
|
13175
|
+
return;
|
|
13176
|
+
}
|
|
13177
|
+
if (seen.has(parsed.entry.id)) {
|
|
13178
|
+
issues.push(`${path} models[${i}]: duplicate id "${parsed.entry.id}"`);
|
|
13179
|
+
return;
|
|
13180
|
+
}
|
|
13181
|
+
seen.add(parsed.entry.id);
|
|
13182
|
+
entries2.push(parsed.entry);
|
|
13183
|
+
});
|
|
13184
|
+
return hostLabel ? { entries: entries2, issues, hostLabel } : { entries: entries2, issues };
|
|
13185
|
+
}
|
|
13186
|
+
function saveModelEntries(homeDir, entries2, hostLabel) {
|
|
13187
|
+
const path = modelEntriesPath(homeDir);
|
|
13188
|
+
if (loadModelEntries(homeDir).issues.length > 0) {
|
|
13189
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
13190
|
+
copyFileSync(path, `${path}.corrupt.${ts}.bak`);
|
|
13191
|
+
}
|
|
13192
|
+
const label = hostLabel !== void 0 ? hostLabel : loadModelEntries(homeDir).hostLabel;
|
|
13193
|
+
const body2 = `${JSON.stringify(
|
|
13194
|
+
{
|
|
13195
|
+
version: FORMAT_VERSION,
|
|
13196
|
+
...label ? { hostLabel: label } : {},
|
|
13197
|
+
models: entries2.map(normalizeModelEntry)
|
|
13198
|
+
},
|
|
13199
|
+
null,
|
|
13200
|
+
2
|
|
13201
|
+
)}
|
|
13202
|
+
`;
|
|
13203
|
+
const tmp = `${path}.${process.pid}.tmp`;
|
|
13204
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
13205
|
+
try {
|
|
13206
|
+
writeFileSync(tmp, body2, { encoding: "utf-8", mode: 384 });
|
|
13207
|
+
renameSync(tmp, path);
|
|
13208
|
+
} catch (err) {
|
|
13209
|
+
rmSync(tmp, { force: true });
|
|
13210
|
+
throw err;
|
|
13211
|
+
}
|
|
13212
|
+
}
|
|
13213
|
+
var EntryRefSchema = z.string().refine((v) => parseModelRef(v) !== void 0, { message: "expected @<model-entry-id>" });
|
|
13026
13214
|
var RetrySchema = z.object({
|
|
13027
13215
|
maxRetries: z.int().min(0).default(3),
|
|
13028
13216
|
baseBackoffMs: z.int().min(0).default(1e3),
|
|
@@ -13145,9 +13333,9 @@ var TelemetrySchema = z.object({
|
|
|
13145
13333
|
endpoint: z.string().optional()
|
|
13146
13334
|
});
|
|
13147
13335
|
var EpochConfigSchema = z.object({
|
|
13148
|
-
|
|
13149
|
-
|
|
13150
|
-
|
|
13336
|
+
model: EntryRefSchema.optional(),
|
|
13337
|
+
models: z.object({ utility: EntryRefSchema.optional() }).optional(),
|
|
13338
|
+
modelEntries: z.array(ModelEntrySchema).optional(),
|
|
13151
13339
|
shell: z.enum(SHELL_KINDS).optional(),
|
|
13152
13340
|
maxTurns: z.int().nonnegative().default(DEFAULT_MAX_TURNS),
|
|
13153
13341
|
contextLength: z.int().positive().default(2e5),
|
|
@@ -13163,10 +13351,7 @@ var EpochConfigSchema = z.object({
|
|
|
13163
13351
|
logLevel: z.enum(["debug", "info", "warn", "error"]).default("info"),
|
|
13164
13352
|
profile: z.string().min(1).default("default"),
|
|
13165
13353
|
retry: RetrySchema.prefault({}),
|
|
13166
|
-
fallback: z.object({
|
|
13167
|
-
providers: z.array(z.enum(PROVIDER_TYPES)).default([]),
|
|
13168
|
-
model: z.string().min(1).optional()
|
|
13169
|
-
}).optional(),
|
|
13354
|
+
fallback: z.object({ model: EntryRefSchema.optional() }).optional(),
|
|
13170
13355
|
hooks: z.record(z.string(), z.unknown()).optional(),
|
|
13171
13356
|
trust: TrustSchema.optional(),
|
|
13172
13357
|
sandbox: SandboxSchema.prefault({}),
|
|
@@ -13186,13 +13371,16 @@ var EpochConfigSchema = z.object({
|
|
|
13186
13371
|
statusLine: StatusLineSchema.optional(),
|
|
13187
13372
|
display: DisplaySchema2.optional(),
|
|
13188
13373
|
brand: z.string().min(1).optional(),
|
|
13374
|
+
brandWhenSidebarCollapsed: z.enum(BRAND_WHEN_SIDEBAR_COLLAPSED).optional(),
|
|
13375
|
+
brandMenuId: z.string().min(1).optional(),
|
|
13189
13376
|
sidebarMenu: z.array(SidebarNavItemSchema).min(1).refine((items) => new Set(items.map((item) => item.id)).size === items.length, {
|
|
13190
13377
|
message: "sidebarMenu: id must be unique"
|
|
13191
13378
|
}).refine((items) => items.filter((item) => item.primary === true).length <= 1, {
|
|
13192
13379
|
message: "sidebarMenu: at most one item can be primary"
|
|
13193
13380
|
}).optional(),
|
|
13194
|
-
|
|
13195
|
-
|
|
13381
|
+
userMenuHost: z.boolean().optional(),
|
|
13382
|
+
sidebarHeadMenu: z.array(SidebarMenuItemSchema).min(1).refine((items) => new Set(items.map((item) => item.id)).size === items.length, {
|
|
13383
|
+
message: "sidebarHeadMenu: id must be unique"
|
|
13196
13384
|
}).optional(),
|
|
13197
13385
|
blankCards: z.array(BlankCardSchema).min(1).refine((items) => new Set(items.map((item) => item.id)).size === items.length, {
|
|
13198
13386
|
message: "blankCards: id must be unique"
|
|
@@ -13276,7 +13464,7 @@ function describeJsonError(err, text) {
|
|
|
13276
13464
|
const reason9 = message.replace(/\s*in JSON at position \d+.*$/, "");
|
|
13277
13465
|
return t("config_load.json_at", { reason: reason9, line, column });
|
|
13278
13466
|
}
|
|
13279
|
-
function readSettingsFile(path, schema, known, empty, issues) {
|
|
13467
|
+
function readSettingsFile(path, schema, known, empty, issues, sanitize = (raw) => raw) {
|
|
13280
13468
|
if (!existsSync(path)) return void 0;
|
|
13281
13469
|
const text = readFileSync(path, "utf-8");
|
|
13282
13470
|
let raw;
|
|
@@ -13287,11 +13475,27 @@ function readSettingsFile(path, schema, known, empty, issues) {
|
|
|
13287
13475
|
issues.push({ path, message: t("config_load.json_invalid", { reason: reason9 }) });
|
|
13288
13476
|
return { failed: reason9 };
|
|
13289
13477
|
}
|
|
13290
|
-
|
|
13291
|
-
|
|
13478
|
+
const cleaned = sanitize(raw, issues);
|
|
13479
|
+
issues.push(...atFile(path, unknownKeyIssues(known, cleaned)));
|
|
13480
|
+
const parsed = parseLenient(schema, cleaned, empty);
|
|
13292
13481
|
issues.push(...atFile(path, parsed.issues));
|
|
13293
13482
|
return { file: parsed.value };
|
|
13294
13483
|
}
|
|
13484
|
+
function stripLegacyModelValue(raw, issues) {
|
|
13485
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return raw;
|
|
13486
|
+
const out = { ...raw };
|
|
13487
|
+
const value = out["model"];
|
|
13488
|
+
if (value === void 0 || typeof value === "string" && parseModelRef(value)) return out;
|
|
13489
|
+
delete out["model"];
|
|
13490
|
+
issues.push({
|
|
13491
|
+
path: "model",
|
|
13492
|
+
message: t("config_load.legacy_ignored", {
|
|
13493
|
+
key: "model",
|
|
13494
|
+
hint: t("config_load.legacy_hint_main")
|
|
13495
|
+
})
|
|
13496
|
+
});
|
|
13497
|
+
return out;
|
|
13498
|
+
}
|
|
13295
13499
|
// src/config/managed.ts
|
|
13296
13500
|
var ManagedSettingsSchema = SettingsFileSchema.extend({
|
|
13297
13501
|
allowManagedPermissionRulesOnly: z.boolean().optional(),
|
|
@@ -13317,7 +13521,8 @@ function readManagedSettings(path, issues) {
|
|
|
13317
13521
|
ManagedSettingsSchema,
|
|
13318
13522
|
MANAGED_KEYS,
|
|
13319
13523
|
{},
|
|
13320
|
-
issues
|
|
13524
|
+
issues,
|
|
13525
|
+
stripLegacyModelValue
|
|
13321
13526
|
);
|
|
13322
13527
|
if (!read || "failed" in read) return { policy: noManagedPolicy(path), read };
|
|
13323
13528
|
return { policy: policyOf(path, read.file), read };
|
|
@@ -13377,11 +13582,11 @@ function countRules(lists) {
|
|
|
13377
13582
|
if (!lists) return 0;
|
|
13378
13583
|
return BUCKETS2.reduce((sum, b) => sum + (lists[b]?.length ?? 0), 0);
|
|
13379
13584
|
}
|
|
13380
|
-
function mergeLists(
|
|
13585
|
+
function mergeLists(base2, add) {
|
|
13381
13586
|
const out = {};
|
|
13382
13587
|
for (const bucket of BUCKETS2) {
|
|
13383
|
-
const merged = [...
|
|
13384
|
-
if (merged.length === 0 &&
|
|
13588
|
+
const merged = [...base2[bucket] ?? [], ...add[bucket] ?? []];
|
|
13589
|
+
if (merged.length === 0 && base2[bucket] === void 0 && add[bucket] === void 0) continue;
|
|
13385
13590
|
out[bucket] = [...new Set(merged)];
|
|
13386
13591
|
}
|
|
13387
13592
|
return out;
|
|
@@ -13412,7 +13617,8 @@ function loadLayer(source, execTrusted, issues) {
|
|
|
13412
13617
|
SettingsFileSchema,
|
|
13413
13618
|
SETTINGS_KEYS,
|
|
13414
13619
|
{},
|
|
13415
|
-
issues
|
|
13620
|
+
issues,
|
|
13621
|
+
stripLegacyModelValue
|
|
13416
13622
|
);
|
|
13417
13623
|
return finishLayer(source, read, execTrusted, issues);
|
|
13418
13624
|
}
|
|
@@ -13455,8 +13661,8 @@ function describe6(layer, gated, lists) {
|
|
|
13455
13661
|
const parts = BUCKETS2.map((b) => `${b} ${lists[b]?.length ?? 0}`).join(" / ");
|
|
13456
13662
|
return t("config_load.loaded", { label, parts });
|
|
13457
13663
|
}
|
|
13458
|
-
function resolveSettings(
|
|
13459
|
-
const config = structuredClone(
|
|
13664
|
+
function resolveSettings(base2, projectRoot, execTrusted, opts = {}) {
|
|
13665
|
+
const config = structuredClone(base2);
|
|
13460
13666
|
const issues = [];
|
|
13461
13667
|
const layers = [];
|
|
13462
13668
|
let lists = {};
|
|
@@ -13485,12 +13691,19 @@ function resolveSettings(base, projectRoot, execTrusted, opts = {}) {
|
|
|
13485
13691
|
lists = managed.policy.allowManagedPermissionRulesOnly ? managed.outcome.lists : mergeLists(lists, managed.outcome.lists);
|
|
13486
13692
|
layers.push({ ...managed.outcome, scalars: applyScalars(config, managed.file) });
|
|
13487
13693
|
}
|
|
13488
|
-
if (countRules(lists) > 0 ||
|
|
13694
|
+
if (countRules(lists) > 0 || base2.permissions !== void 0) config.permissions = lists;
|
|
13489
13695
|
return { config, layers, issues, managed: managed.policy };
|
|
13490
13696
|
}
|
|
13491
13697
|
function loadPluginLayer(entry, issues) {
|
|
13492
13698
|
const { plugin, path } = entry;
|
|
13493
|
-
const read = readSettingsFile(
|
|
13699
|
+
const read = readSettingsFile(
|
|
13700
|
+
path,
|
|
13701
|
+
SettingsFileSchema,
|
|
13702
|
+
SETTINGS_KEYS,
|
|
13703
|
+
{},
|
|
13704
|
+
issues,
|
|
13705
|
+
stripLegacyModelValue
|
|
13706
|
+
);
|
|
13494
13707
|
const label = t("config_load.layer_plugin_named", { plugin });
|
|
13495
13708
|
if (!read) return { layer: "plugin", path, status: "missing", lists: {}, scalars: {} };
|
|
13496
13709
|
if ("failed" in read) {
|
|
@@ -13566,11 +13779,11 @@ function originOf(origins, key) {
|
|
|
13566
13779
|
if (origins.file.has(key)) return "user";
|
|
13567
13780
|
return "builtin";
|
|
13568
13781
|
}
|
|
13569
|
-
function explainSetting(key,
|
|
13570
|
-
const baseLayer = baseLayerOf(
|
|
13782
|
+
function explainSetting(key, base2, file) {
|
|
13783
|
+
const baseLayer = baseLayerOf(base2);
|
|
13571
13784
|
const steps = [
|
|
13572
|
-
step("builtin",
|
|
13573
|
-
baseLayer === "builtin" ? { layer: "user" } : step(baseLayer,
|
|
13785
|
+
step("builtin", base2.builtin),
|
|
13786
|
+
baseLayer === "builtin" ? { layer: "user" } : step(baseLayer, base2.value)
|
|
13574
13787
|
];
|
|
13575
13788
|
if (file) {
|
|
13576
13789
|
for (const layer of SETTINGS_LAYER_ORDER) {
|
|
@@ -13587,10 +13800,10 @@ function explainSetting(key, base, file) {
|
|
|
13587
13800
|
chain: file ? steps : []
|
|
13588
13801
|
};
|
|
13589
13802
|
}
|
|
13590
|
-
function baseLayerOf(
|
|
13591
|
-
if (
|
|
13592
|
-
if (
|
|
13593
|
-
return sameValue(
|
|
13803
|
+
function baseLayerOf(base2) {
|
|
13804
|
+
if (base2.origin === "env") return "env";
|
|
13805
|
+
if (base2.origin === "user") return "user";
|
|
13806
|
+
return sameValue(base2.value, base2.builtin) ? "builtin" : "unknown";
|
|
13594
13807
|
}
|
|
13595
13808
|
function valueOf(file, layer) {
|
|
13596
13809
|
return file.layers.find((l) => l.layer === layer)?.scalars[file.key];
|
|
@@ -13640,8 +13853,9 @@ function _loadImpl(overrideHomeDir) {
|
|
|
13640
13853
|
mkdirSync(homeDir, { recursive: true });
|
|
13641
13854
|
const configPath2 = getConfigPath(homeDir);
|
|
13642
13855
|
const { mtime, size } = fileStamp(configPath2);
|
|
13856
|
+
const entriesStamp = stampKey(modelEntriesPath(homeDir));
|
|
13643
13857
|
const envSnapshot = snapshotEnv();
|
|
13644
|
-
if (_cache && _cache.homeDir === homeDir && _cache.mtime === mtime && _cache.size === size && sameEnv(_cache.envSnapshot, envSnapshot)) {
|
|
13858
|
+
if (_cache && _cache.homeDir === homeDir && _cache.mtime === mtime && _cache.size === size && _cache.entriesStamp === entriesStamp && sameEnv(_cache.envSnapshot, envSnapshot)) {
|
|
13645
13859
|
_lastIssues = _cache.issues;
|
|
13646
13860
|
_lastOrigins = _cache.origins;
|
|
13647
13861
|
return _cache.config;
|
|
@@ -13657,6 +13871,7 @@ function _loadImpl(overrideHomeDir) {
|
|
|
13657
13871
|
homeDir,
|
|
13658
13872
|
mtime,
|
|
13659
13873
|
size,
|
|
13874
|
+
entriesStamp,
|
|
13660
13875
|
envSnapshot,
|
|
13661
13876
|
config: cached.config,
|
|
13662
13877
|
issues,
|
|
@@ -13665,12 +13880,9 @@ function _loadImpl(overrideHomeDir) {
|
|
|
13665
13880
|
return cached.config;
|
|
13666
13881
|
}
|
|
13667
13882
|
const config = parseRawConfig(read.raw, issues, track);
|
|
13668
|
-
|
|
13669
|
-
const pinned = applyProviderOverride(config, track, override);
|
|
13670
|
-
const secretSource = applySecretsFromEnv(config, homeDir, track);
|
|
13671
|
-
if (pinned) applyProviderOverride(config, track, override);
|
|
13883
|
+
reportDroppedEnv(issues);
|
|
13672
13884
|
applyEnvOverrides(config, issues, track);
|
|
13673
|
-
|
|
13885
|
+
applyModelEntries(config, homeDir, issues);
|
|
13674
13886
|
if (overrideHomeDir) {
|
|
13675
13887
|
config.homeDir = overrideHomeDir;
|
|
13676
13888
|
config.dbPath = dbPath(overrideHomeDir);
|
|
@@ -13679,7 +13891,16 @@ function _loadImpl(overrideHomeDir) {
|
|
|
13679
13891
|
_lastIssues = issues;
|
|
13680
13892
|
_lastOrigins = origins;
|
|
13681
13893
|
_lastKnownGood = { homeDir, config: structuredClone(config), origins };
|
|
13682
|
-
_cache = {
|
|
13894
|
+
_cache = {
|
|
13895
|
+
homeDir,
|
|
13896
|
+
mtime,
|
|
13897
|
+
size,
|
|
13898
|
+
entriesStamp,
|
|
13899
|
+
envSnapshot,
|
|
13900
|
+
config: structuredClone(config),
|
|
13901
|
+
issues,
|
|
13902
|
+
origins
|
|
13903
|
+
};
|
|
13683
13904
|
return config;
|
|
13684
13905
|
}
|
|
13685
13906
|
function readRawConfig(configPath2, issues) {
|
|
@@ -13705,106 +13926,122 @@ function parseRawConfig(raw, issues, track) {
|
|
|
13705
13926
|
const expanded = expandEnvVars(raw);
|
|
13706
13927
|
const migrated = migrateRawConfig(expanded);
|
|
13707
13928
|
issues.push(...migrated.issues);
|
|
13708
|
-
|
|
13709
|
-
|
|
13710
|
-
|
|
13711
|
-
|
|
13712
|
-
EpochConfigSchema.parse({})
|
|
13713
|
-
);
|
|
13929
|
+
const legacyStripped = stripLegacyModelConfig(migrated.raw, issues);
|
|
13930
|
+
issues.push(...unknownKeyIssues(CONFIG_KEYS, legacyStripped));
|
|
13931
|
+
const cleaned = dropEmptySections(legacyStripped);
|
|
13932
|
+
const outcome = parseLenient(EpochConfigSchema, cleaned, EpochConfigSchema.parse({}));
|
|
13714
13933
|
issues.push(...outcome.issues);
|
|
13715
13934
|
issues.push(...settingsHiddenIssues(outcome.value));
|
|
13716
13935
|
issues.push(...settingsPaneBeforeIssues(outcome.value));
|
|
13717
|
-
if (outcome.issues.length === 0) collectKeyPaths(
|
|
13936
|
+
if (outcome.issues.length === 0) collectKeyPaths(cleaned, track.file);
|
|
13718
13937
|
return outcome.value;
|
|
13719
13938
|
}
|
|
13939
|
+
function stripLegacyModelConfig(raw, issues) {
|
|
13940
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return raw;
|
|
13941
|
+
const out = { ...raw };
|
|
13942
|
+
const drop = (path, hintKey, messageKey = "config_load.legacy_ignored") => {
|
|
13943
|
+
issues.push({
|
|
13944
|
+
path,
|
|
13945
|
+
message: t(messageKey, { key: path, hint: t(hintKey) })
|
|
13946
|
+
});
|
|
13947
|
+
};
|
|
13948
|
+
for (const key of ["provider", "gateway"]) {
|
|
13949
|
+
if (key in out) {
|
|
13950
|
+
delete out[key];
|
|
13951
|
+
drop(key, "config_load.legacy_hint_endpoint");
|
|
13952
|
+
}
|
|
13953
|
+
}
|
|
13954
|
+
const notEntry = (v) => v !== void 0 && (typeof v !== "string" || parseModelRef(v) === void 0);
|
|
13955
|
+
const dropModel = (path, hintKey, value) => {
|
|
13956
|
+
if (typeof value === "string" && value.trimStart().startsWith(ENTRY_REF_PREFIX))
|
|
13957
|
+
drop(path, hintKey, "config_load.entry_ref_invalid");
|
|
13958
|
+
else drop(path, hintKey);
|
|
13959
|
+
};
|
|
13960
|
+
if (notEntry(out.model)) {
|
|
13961
|
+
const raw2 = out.model;
|
|
13962
|
+
delete out.model;
|
|
13963
|
+
dropModel("model", "config_load.legacy_hint_main", raw2);
|
|
13964
|
+
}
|
|
13965
|
+
const section = (key) => {
|
|
13966
|
+
const v = out[key];
|
|
13967
|
+
if (!v || typeof v !== "object" || Array.isArray(v)) return void 0;
|
|
13968
|
+
const copy = { ...v };
|
|
13969
|
+
out[key] = copy;
|
|
13970
|
+
return copy;
|
|
13971
|
+
};
|
|
13972
|
+
const models = section("models");
|
|
13973
|
+
if (models && notEntry(models.utility)) {
|
|
13974
|
+
const raw2 = models.utility;
|
|
13975
|
+
delete models.utility;
|
|
13976
|
+
dropModel("models.utility", "config_load.legacy_hint_utility", raw2);
|
|
13977
|
+
}
|
|
13978
|
+
const fallback2 = section("fallback");
|
|
13979
|
+
if (fallback2 && notEntry(fallback2.model)) {
|
|
13980
|
+
const raw2 = fallback2.model;
|
|
13981
|
+
delete fallback2.model;
|
|
13982
|
+
dropModel("fallback.model", "config_load.legacy_hint_fallback", raw2);
|
|
13983
|
+
}
|
|
13984
|
+
if (fallback2 && "providers" in fallback2) {
|
|
13985
|
+
delete fallback2.providers;
|
|
13986
|
+
drop("fallback.providers", "config_load.legacy_hint_fallback");
|
|
13987
|
+
}
|
|
13988
|
+
return out;
|
|
13989
|
+
}
|
|
13720
13990
|
function dropEmptySections(raw) {
|
|
13721
13991
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return raw;
|
|
13722
13992
|
const entries2 = Object.entries(raw);
|
|
13723
13993
|
if (!entries2.some(([, v]) => v === null)) return raw;
|
|
13724
13994
|
return Object.fromEntries(entries2.filter(([, v]) => v !== null));
|
|
13725
13995
|
}
|
|
13726
|
-
function
|
|
13727
|
-
const
|
|
13728
|
-
|
|
13729
|
-
|
|
13730
|
-
|
|
13731
|
-
|
|
13732
|
-
|
|
13733
|
-
|
|
13734
|
-
|
|
13735
|
-
|
|
13736
|
-
config.provider.type = info.type;
|
|
13737
|
-
track.env.add("provider.type");
|
|
13738
|
-
return finishSecrets(config, value, "explicit", track);
|
|
13739
|
-
}
|
|
13740
|
-
return "config-file";
|
|
13741
|
-
}
|
|
13742
|
-
function readProviderOverride(issues) {
|
|
13743
|
-
const override = {};
|
|
13744
|
-
const baseUrl = process.env.EPOCH_BASE_URL;
|
|
13745
|
-
if (baseUrl) {
|
|
13746
|
-
override.baseUrl = baseUrl;
|
|
13747
|
-
override.type = "openai-compatible";
|
|
13748
|
-
}
|
|
13749
|
-
const type = process.env.EPOCH_PROVIDER;
|
|
13750
|
-
if (type) {
|
|
13751
|
-
if (isProviderType(type)) {
|
|
13752
|
-
override.type ??= type;
|
|
13753
|
-
} else {
|
|
13754
|
-
issues.push(envIssue("EPOCH_PROVIDER", type, PROVIDER_TYPES.join(" / ")));
|
|
13755
|
-
}
|
|
13996
|
+
function reportDroppedEnv(issues) {
|
|
13997
|
+
for (const name of ["EPOCH_PROVIDER", "EPOCH_BASE_URL"]) {
|
|
13998
|
+
if (!process.env[name]) continue;
|
|
13999
|
+
issues.push({
|
|
14000
|
+
path: name,
|
|
14001
|
+
message: t("config_load.legacy_ignored", {
|
|
14002
|
+
key: name,
|
|
14003
|
+
hint: t("config_load.legacy_hint_endpoint")
|
|
14004
|
+
})
|
|
14005
|
+
});
|
|
13756
14006
|
}
|
|
13757
|
-
return override;
|
|
13758
14007
|
}
|
|
13759
|
-
function
|
|
13760
|
-
|
|
13761
|
-
config.provider.baseUrl = override.baseUrl;
|
|
13762
|
-
track.env.add("provider.baseUrl");
|
|
13763
|
-
}
|
|
13764
|
-
if (!override.type) return false;
|
|
13765
|
-
config.provider.type = override.type;
|
|
13766
|
-
track.env.add("provider.type");
|
|
13767
|
-
return true;
|
|
14008
|
+
function readSecret(name, homeDir) {
|
|
14009
|
+
return process.env[name] || loadEnvFile(getEnvPath(homeDir))[name] || getSecretValues()[name] || void 0;
|
|
13768
14010
|
}
|
|
13769
|
-
function
|
|
13770
|
-
|
|
13771
|
-
const fromStore = getSecretValues();
|
|
13772
|
-
const result = {};
|
|
13773
|
-
for (const name of API_KEY_ENV_VARS) {
|
|
13774
|
-
const value = process.env[name] || dotEnv[name] || fromStore[name];
|
|
13775
|
-
if (value) result[name] = value;
|
|
13776
|
-
}
|
|
13777
|
-
return result;
|
|
14011
|
+
function readEntryKey(id, homeDir = resolveHomeDir()) {
|
|
14012
|
+
return readSecret(entrySecretName(id), homeDir);
|
|
13778
14013
|
}
|
|
13779
|
-
function
|
|
13780
|
-
if (
|
|
13781
|
-
return
|
|
14014
|
+
function hasUsableModel(homeDir = resolveHomeDir()) {
|
|
14015
|
+
if (hasProviderCredentials(homeDir)) return true;
|
|
14016
|
+
return loadModelEntries(homeDir).entries.some(
|
|
14017
|
+
(e) => e.host === true || e.provider === "ollama" || e.provider === "openai-compatible"
|
|
14018
|
+
);
|
|
13782
14019
|
}
|
|
13783
|
-
function
|
|
13784
|
-
const
|
|
13785
|
-
|
|
13786
|
-
|
|
13787
|
-
track.env.add("provider.apiKey");
|
|
13788
|
-
track.env.add("provider.credentials");
|
|
13789
|
-
return source;
|
|
14020
|
+
function hasProviderCredentials(homeDir = resolveHomeDir()) {
|
|
14021
|
+
const dotEnv = loadEnvFile(getEnvPath(homeDir));
|
|
14022
|
+
const named = (n) => isEntrySecretName(n) && Boolean(process.env[n] || dotEnv[n]);
|
|
14023
|
+
return Object.keys(process.env).some(named) || Object.keys(dotEnv).some(named) || listStoredSecretNames(homeDir).some(isEntrySecretName);
|
|
13790
14024
|
}
|
|
13791
14025
|
function applyEnvOverrides(config, issues, track) {
|
|
13792
14026
|
const env = process.env;
|
|
13793
14027
|
if (env.EPOCH_MODEL) {
|
|
13794
|
-
|
|
13795
|
-
|
|
14028
|
+
if (parseModelRef(env.EPOCH_MODEL)) {
|
|
14029
|
+
config.model = env.EPOCH_MODEL;
|
|
14030
|
+
track.env.add("model");
|
|
14031
|
+
} else issues.push(envIssue("EPOCH_MODEL", env.EPOCH_MODEL, "@<model-entry-id>"));
|
|
13796
14032
|
}
|
|
13797
14033
|
if (env.EPOCH_PROFILE) {
|
|
13798
14034
|
config.profile = env.EPOCH_PROFILE;
|
|
13799
14035
|
track.env.add("profile");
|
|
13800
14036
|
}
|
|
13801
14037
|
if (env.EPOCH_FALLBACK_MODEL) {
|
|
13802
|
-
|
|
13803
|
-
|
|
13804
|
-
|
|
13805
|
-
|
|
13806
|
-
|
|
13807
|
-
|
|
14038
|
+
if (parseModelRef(env.EPOCH_FALLBACK_MODEL)) {
|
|
14039
|
+
config.fallback = { model: env.EPOCH_FALLBACK_MODEL };
|
|
14040
|
+
track.env.add("fallback");
|
|
14041
|
+
track.env.add("fallback.model");
|
|
14042
|
+
} else {
|
|
14043
|
+
issues.push(envIssue("EPOCH_FALLBACK_MODEL", env.EPOCH_FALLBACK_MODEL, "@<model-entry-id>"));
|
|
14044
|
+
}
|
|
13808
14045
|
}
|
|
13809
14046
|
if (env.EPOCH_MAX_TURNS) {
|
|
13810
14047
|
const turns = Number(env.EPOCH_MAX_TURNS);
|
|
@@ -13846,11 +14083,6 @@ function applyEnvOverrides(config, issues, track) {
|
|
|
13846
14083
|
function envIssue(name, value, expected) {
|
|
13847
14084
|
return { path: name, message: t("config_load.env_expected", { expected, value }) };
|
|
13848
14085
|
}
|
|
13849
|
-
function detectProviderFromKey(config) {
|
|
13850
|
-
const key = config.provider.apiKey;
|
|
13851
|
-
if (key.startsWith("sk-ant")) config.provider.type = "anthropic";
|
|
13852
|
-
else if (key.startsWith("sk-d")) config.provider.type = "deepseek";
|
|
13853
|
-
}
|
|
13854
14086
|
function backupCorruptConfig(path) {
|
|
13855
14087
|
try {
|
|
13856
14088
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
@@ -13863,7 +14095,6 @@ function backupCorruptConfig(path) {
|
|
|
13863
14095
|
}
|
|
13864
14096
|
function snapshotEnv() {
|
|
13865
14097
|
const keys = [
|
|
13866
|
-
...API_KEY_ENV_VARS,
|
|
13867
14098
|
"EPOCH_HOME",
|
|
13868
14099
|
"EPOCH_PROFILE",
|
|
13869
14100
|
"EPOCH_MODEL",
|
|
@@ -13878,8 +14109,6 @@ function snapshotEnv() {
|
|
|
13878
14109
|
const snapshot = Object.fromEntries(
|
|
13879
14110
|
keys.map((k) => [k, process.env[k]])
|
|
13880
14111
|
);
|
|
13881
|
-
const fromStore = getSecretValues();
|
|
13882
|
-
for (const name of API_KEY_ENV_VARS) snapshot[`secret:${name}`] = fromStore[name];
|
|
13883
14112
|
return snapshot;
|
|
13884
14113
|
}
|
|
13885
14114
|
function sameEnv(a, b) {
|
|
@@ -13919,6 +14148,19 @@ function loadEnvFile(path) {
|
|
|
13919
14148
|
return {};
|
|
13920
14149
|
}
|
|
13921
14150
|
}
|
|
14151
|
+
function applyModelEntries(config, homeDir, issues) {
|
|
14152
|
+
if (config.modelEntries !== void 0) {
|
|
14153
|
+
issues.push({ path: "modelEntries", message: t("config_load.model_entries_in_yaml") });
|
|
14154
|
+
}
|
|
14155
|
+
const loaded = loadModelEntries(homeDir);
|
|
14156
|
+
for (const message of loaded.issues) issues.push({ path: "models.json", message });
|
|
14157
|
+
if (loaded.entries.length > 0) config.modelEntries = loaded.entries;
|
|
14158
|
+
else delete config.modelEntries;
|
|
14159
|
+
}
|
|
14160
|
+
function stampKey(path) {
|
|
14161
|
+
const { mtime, size } = fileStamp(path);
|
|
14162
|
+
return `${mtime}:${size}`;
|
|
14163
|
+
}
|
|
13922
14164
|
function fileStamp(path) {
|
|
13923
14165
|
try {
|
|
13924
14166
|
const st = statSync(path);
|
|
@@ -13927,9 +14169,6 @@ function fileStamp(path) {
|
|
|
13927
14169
|
return { mtime: 0, size: 0 };
|
|
13928
14170
|
}
|
|
13929
14171
|
}
|
|
13930
|
-
function parseKeys(raw) {
|
|
13931
|
-
return raw.split(",").map((s) => s.trim()).filter(Boolean).map((k) => ({ apiKey: k }));
|
|
13932
|
-
}
|
|
13933
14172
|
var PROBE_IMAGE_PNG_BASE64 = "iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAIAAAAlC+aJAAAAS0lEQVR42u3PQQkAAAgAsetfWiP4FgYrsKZeS0BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEDgsqnc8OJg6Ln3AAAAAElFTkSuQmCC";
|
|
13934
14173
|
var PROBE_PROMPT = "What color is this image? Answer with one word.";
|
|
13935
14174
|
var MIN_IMAGE_TOKEN_DELTA = 16;
|
|
@@ -13967,14 +14206,14 @@ async function runImageProbe(model) {
|
|
|
13967
14206
|
};
|
|
13968
14207
|
}
|
|
13969
14208
|
const shot = withImage.inputTokens;
|
|
13970
|
-
const
|
|
13971
|
-
if (shot === void 0 ||
|
|
13972
|
-
return { verdict: "inconclusive", detail: `(usage \u4E0D\u53EF\u7528\uFF1A\u5E26\u56FE ${shot} / \u57FA\u7EBF ${
|
|
14209
|
+
const base2 = baseline.inputTokens;
|
|
14210
|
+
if (shot === void 0 || base2 === void 0 || shot <= 0 || base2 <= 0) {
|
|
14211
|
+
return { verdict: "inconclusive", detail: `(usage \u4E0D\u53EF\u7528\uFF1A\u5E26\u56FE ${shot} / \u57FA\u7EBF ${base2})` };
|
|
13973
14212
|
}
|
|
13974
|
-
const delta = shot -
|
|
14213
|
+
const delta = shot - base2;
|
|
13975
14214
|
return {
|
|
13976
14215
|
verdict: delta >= MIN_IMAGE_TOKEN_DELTA ? "yes" : "no",
|
|
13977
|
-
detail: `+${delta} token\uFF08\u5E26\u56FE ${shot} / \u57FA\u7EBF ${
|
|
14216
|
+
detail: `+${delta} token\uFF08\u5E26\u56FE ${shot} / \u57FA\u7EBF ${base2}\uFF09\uFF1B\u5B83\u8BF4\u300C${withImage.text.slice(0, 40)}\u300D`
|
|
13978
14217
|
};
|
|
13979
14218
|
}
|
|
13980
14219
|
var PROBE_VERSION = 2;
|
|
@@ -14013,67 +14252,6 @@ function writeImageProbe(homeDir, provider, model, supportsImages) {
|
|
|
14013
14252
|
} catch {
|
|
14014
14253
|
}
|
|
14015
14254
|
}
|
|
14016
|
-
// src/provider/fallback.ts
|
|
14017
|
-
var DEFAULT_MODELS = {
|
|
14018
|
-
openai: "gpt-4o-mini",
|
|
14019
|
-
anthropic: "claude-haiku-4-5",
|
|
14020
|
-
google: "gemini-2.0-flash",
|
|
14021
|
-
deepseek: "deepseek-chat",
|
|
14022
|
-
groq: "llama-3.3-70b-versatile",
|
|
14023
|
-
mistral: "ministral-8b-latest",
|
|
14024
|
-
moonshotai: "kimi-latest",
|
|
14025
|
-
zhipu: "glm-4.5-flash",
|
|
14026
|
-
dashscope: "qwen-flash",
|
|
14027
|
-
bytedance: "doubao-seed-2.0-mini",
|
|
14028
|
-
qianfan: "ernie-speed-128k",
|
|
14029
|
-
cohere: "command-r7b-12-2024",
|
|
14030
|
-
xai: "grok-vision-beta",
|
|
14031
|
-
perplexity: "sonar",
|
|
14032
|
-
"amazon-bedrock": "anthropic.claude-instant-v1",
|
|
14033
|
-
azure: "gpt-4o-mini",
|
|
14034
|
-
togetherai: "meta-llama/Llama-3.3-70B-Instruct-Turbo",
|
|
14035
|
-
ollama: "llama3.2",
|
|
14036
|
-
"openai-compatible": "gpt-4o-mini"
|
|
14037
|
-
};
|
|
14038
|
-
function defaultModelOf(type) {
|
|
14039
|
-
return DEFAULT_MODELS[type];
|
|
14040
|
-
}
|
|
14041
|
-
function modelKey(provider, model) {
|
|
14042
|
-
return `${provider}/${model}`;
|
|
14043
|
-
}
|
|
14044
|
-
function nextFallbackModel(provider, current, tried, configured) {
|
|
14045
|
-
for (const candidate of [configured, DEFAULT_MODELS[provider]]) {
|
|
14046
|
-
if (!candidate || candidate === current) continue;
|
|
14047
|
-
if (tried.has(modelKey(provider, candidate))) continue;
|
|
14048
|
-
return candidate;
|
|
14049
|
-
}
|
|
14050
|
-
return void 0;
|
|
14051
|
-
}
|
|
14052
|
-
var MODEL_GIVE_UP_THRESHOLD = 3;
|
|
14053
|
-
var ModelFailureTracker = class {
|
|
14054
|
-
counts = /* @__PURE__ */ new Map();
|
|
14055
|
-
abandoned = /* @__PURE__ */ new Set();
|
|
14056
|
-
recordFailure(provider, model) {
|
|
14057
|
-
const key = modelKey(provider, model);
|
|
14058
|
-
if (this.abandoned.has(key)) return false;
|
|
14059
|
-
const next = (this.counts.get(key) ?? 0) + 1;
|
|
14060
|
-
this.counts.set(key, next);
|
|
14061
|
-
if (next < MODEL_GIVE_UP_THRESHOLD) return false;
|
|
14062
|
-
this.abandoned.add(key);
|
|
14063
|
-
return true;
|
|
14064
|
-
}
|
|
14065
|
-
recordSuccess(provider, model) {
|
|
14066
|
-
this.counts.delete(modelKey(provider, model));
|
|
14067
|
-
}
|
|
14068
|
-
isAbandoned(provider, model) {
|
|
14069
|
-
return this.abandoned.has(modelKey(provider, model));
|
|
14070
|
-
}
|
|
14071
|
-
clear(provider, model) {
|
|
14072
|
-
const key = modelKey(provider, model);
|
|
14073
|
-
this.counts.delete(key);
|
|
14074
|
-
this.abandoned.delete(key);
|
|
14075
|
-
}
|
|
14076
|
-
};
|
|
14077
14255
|
var DEFAULT_CONTEXT_LENGTH2 = 2e5;
|
|
14078
14256
|
var STATIC_CAPABILITIES = {
|
|
14079
14257
|
"gpt-4o": {
|
|
@@ -14462,6 +14640,9 @@ function openaiCompatibleFactory(name, defaultBaseUrl) {
|
|
|
14462
14640
|
});
|
|
14463
14641
|
};
|
|
14464
14642
|
}
|
|
14643
|
+
function base(baseUrl) {
|
|
14644
|
+
return baseUrl ? { baseURL: baseUrl } : {};
|
|
14645
|
+
}
|
|
14465
14646
|
var LOADERS = {
|
|
14466
14647
|
openai: async ({ apiKey, baseUrl }) => {
|
|
14467
14648
|
const { createOpenAI } = await import('@ai-sdk/openai');
|
|
@@ -14469,29 +14650,29 @@ var LOADERS = {
|
|
|
14469
14650
|
if (baseUrl) opts["baseURL"] = baseUrl;
|
|
14470
14651
|
return createOpenAI(opts);
|
|
14471
14652
|
},
|
|
14472
|
-
anthropic: async ({ apiKey }) => {
|
|
14653
|
+
anthropic: async ({ apiKey, baseUrl }) => {
|
|
14473
14654
|
const { createAnthropic } = await import('@ai-sdk/anthropic');
|
|
14474
|
-
return createAnthropic({ apiKey });
|
|
14655
|
+
return createAnthropic({ apiKey, ...base(baseUrl) });
|
|
14475
14656
|
},
|
|
14476
|
-
google: async ({ apiKey }) => {
|
|
14657
|
+
google: async ({ apiKey, baseUrl }) => {
|
|
14477
14658
|
const { createGoogleGenerativeAI } = await import('@ai-sdk/google');
|
|
14478
|
-
return createGoogleGenerativeAI({ apiKey });
|
|
14659
|
+
return createGoogleGenerativeAI({ apiKey, ...base(baseUrl) });
|
|
14479
14660
|
},
|
|
14480
|
-
deepseek: async ({ apiKey }) => {
|
|
14661
|
+
deepseek: async ({ apiKey, baseUrl }) => {
|
|
14481
14662
|
const { createDeepSeek } = await import('@ai-sdk/deepseek');
|
|
14482
|
-
return createDeepSeek({ apiKey });
|
|
14663
|
+
return createDeepSeek({ apiKey, ...base(baseUrl) });
|
|
14483
14664
|
},
|
|
14484
|
-
groq: async ({ apiKey }) => {
|
|
14665
|
+
groq: async ({ apiKey, baseUrl }) => {
|
|
14485
14666
|
const { createGroq } = await import('@ai-sdk/groq');
|
|
14486
|
-
return createGroq({ apiKey });
|
|
14667
|
+
return createGroq({ apiKey, ...base(baseUrl) });
|
|
14487
14668
|
},
|
|
14488
|
-
mistral: async ({ apiKey }) => {
|
|
14669
|
+
mistral: async ({ apiKey, baseUrl }) => {
|
|
14489
14670
|
const { createMistral } = await import('@ai-sdk/mistral');
|
|
14490
|
-
return createMistral({ apiKey });
|
|
14671
|
+
return createMistral({ apiKey, ...base(baseUrl) });
|
|
14491
14672
|
},
|
|
14492
|
-
moonshotai: async ({ apiKey }) => {
|
|
14673
|
+
moonshotai: async ({ apiKey, baseUrl }) => {
|
|
14493
14674
|
const { createMoonshotAI } = await import('@ai-sdk/moonshotai');
|
|
14494
|
-
return createMoonshotAI({ apiKey });
|
|
14675
|
+
return createMoonshotAI({ apiKey, ...base(baseUrl) });
|
|
14495
14676
|
},
|
|
14496
14677
|
zhipu: openaiCompatibleFactory("zhipu", "https://open.bigmodel.cn/api/paas/v4"),
|
|
14497
14678
|
dashscope: async ({ apiKey, baseUrl }) => {
|
|
@@ -14504,17 +14685,17 @@ var LOADERS = {
|
|
|
14504
14685
|
},
|
|
14505
14686
|
bytedance: openaiCompatibleFactory("bytedance", "https://ark.cn-beijing.volces.com/api/v3"),
|
|
14506
14687
|
qianfan: openaiCompatibleFactory("qianfan", "https://qianfan.baidubce.com/v2"),
|
|
14507
|
-
cohere: async ({ apiKey }) => {
|
|
14688
|
+
cohere: async ({ apiKey, baseUrl }) => {
|
|
14508
14689
|
const { createCohere } = await import('@ai-sdk/cohere');
|
|
14509
|
-
return createCohere({ apiKey });
|
|
14690
|
+
return createCohere({ apiKey, ...base(baseUrl) });
|
|
14510
14691
|
},
|
|
14511
|
-
xai: async ({ apiKey }) => {
|
|
14692
|
+
xai: async ({ apiKey, baseUrl }) => {
|
|
14512
14693
|
const { createXai } = await import('@ai-sdk/xai');
|
|
14513
|
-
return createXai({ apiKey });
|
|
14694
|
+
return createXai({ apiKey, ...base(baseUrl) });
|
|
14514
14695
|
},
|
|
14515
|
-
perplexity: async ({ apiKey }) => {
|
|
14696
|
+
perplexity: async ({ apiKey, baseUrl }) => {
|
|
14516
14697
|
const { createPerplexity } = await import('@ai-sdk/perplexity');
|
|
14517
|
-
return createPerplexity({ apiKey });
|
|
14698
|
+
return createPerplexity({ apiKey, ...base(baseUrl) });
|
|
14518
14699
|
},
|
|
14519
14700
|
"amazon-bedrock": async ({ apiKey }) => {
|
|
14520
14701
|
const { createAmazonBedrock } = await import('@ai-sdk/amazon-bedrock');
|
|
@@ -14524,13 +14705,13 @@ var LOADERS = {
|
|
|
14524
14705
|
const { createAzure } = await import('@ai-sdk/azure');
|
|
14525
14706
|
return createAzure({ apiKey });
|
|
14526
14707
|
},
|
|
14527
|
-
togetherai: async ({ apiKey }) => {
|
|
14708
|
+
togetherai: async ({ apiKey, baseUrl }) => {
|
|
14528
14709
|
const { createTogetherAI } = await import('@ai-sdk/togetherai');
|
|
14529
|
-
return createTogetherAI({ apiKey });
|
|
14710
|
+
return createTogetherAI({ apiKey, ...base(baseUrl) });
|
|
14530
14711
|
},
|
|
14531
|
-
ollama: async () => {
|
|
14712
|
+
ollama: async ({ baseUrl }) => {
|
|
14532
14713
|
const { createOllama } = await import('ollama-ai-provider-v2');
|
|
14533
|
-
return createOllama();
|
|
14714
|
+
return createOllama(base(baseUrl));
|
|
14534
14715
|
},
|
|
14535
14716
|
"openai-compatible": openaiCompatibleFactory("custom", "http://localhost:8080/v1")
|
|
14536
14717
|
};
|
|
@@ -14558,6 +14739,62 @@ async function createModelFactory(type, opts) {
|
|
|
14558
14739
|
throw err;
|
|
14559
14740
|
}
|
|
14560
14741
|
}
|
|
14742
|
+
async function createEntryFactory(entry, apiKey) {
|
|
14743
|
+
const type = entry.provider;
|
|
14744
|
+
const pkg = type === "openai-compatible" && entry.protocol === "anthropic-messages" ? "@ai-sdk/anthropic" : PACKAGES[type];
|
|
14745
|
+
try {
|
|
14746
|
+
return await loadEntry(entry, apiKey);
|
|
14747
|
+
} catch (err) {
|
|
14748
|
+
if (isModuleNotFound(err)) throw new ProviderSdkMissingError(type, pkg, err);
|
|
14749
|
+
throw err;
|
|
14750
|
+
}
|
|
14751
|
+
}
|
|
14752
|
+
async function loadEntry(entry, apiKey) {
|
|
14753
|
+
const baseURL = entry.baseUrl;
|
|
14754
|
+
if (entry.provider === "openai-compatible") {
|
|
14755
|
+
if (entry.protocol === "anthropic-messages") {
|
|
14756
|
+
const { createAnthropic } = await import('@ai-sdk/anthropic');
|
|
14757
|
+
return createAnthropic({ apiKey: apiKey ?? "", ...baseURL ? { baseURL } : {} });
|
|
14758
|
+
}
|
|
14759
|
+
const { createOpenAICompatible } = await import('@ai-sdk/openai-compatible');
|
|
14760
|
+
return createOpenAICompatible({
|
|
14761
|
+
...apiKey ? { apiKey } : {},
|
|
14762
|
+
baseURL: baseURL || "http://localhost:8080/v1",
|
|
14763
|
+
name: "custom",
|
|
14764
|
+
includeUsage: true,
|
|
14765
|
+
...entry.rawUrl && baseURL ? { fetch: rawUrlFetch(baseURL) } : {}
|
|
14766
|
+
});
|
|
14767
|
+
}
|
|
14768
|
+
const key = apiKey ?? "";
|
|
14769
|
+
return LOADERS[entry.provider](baseURL ? { apiKey: key, baseUrl: baseURL } : { apiKey: key });
|
|
14770
|
+
}
|
|
14771
|
+
function rawUrlFetch(url) {
|
|
14772
|
+
return (input, init) => globalThis.fetch(input instanceof Request ? new Request(url, input) : url, init);
|
|
14773
|
+
}
|
|
14774
|
+
var TOOL_RESULT_MEDIA = {
|
|
14775
|
+
anthropic: true,
|
|
14776
|
+
openai: true,
|
|
14777
|
+
google: true,
|
|
14778
|
+
azure: true,
|
|
14779
|
+
"amazon-bedrock": true,
|
|
14780
|
+
deepseek: false,
|
|
14781
|
+
"openai-compatible": false,
|
|
14782
|
+
zhipu: false,
|
|
14783
|
+
bytedance: false,
|
|
14784
|
+
qianfan: false,
|
|
14785
|
+
groq: false,
|
|
14786
|
+
mistral: false,
|
|
14787
|
+
dashscope: false,
|
|
14788
|
+
xai: false,
|
|
14789
|
+
cohere: false,
|
|
14790
|
+
moonshotai: false,
|
|
14791
|
+
togetherai: false,
|
|
14792
|
+
perplexity: false,
|
|
14793
|
+
ollama: false
|
|
14794
|
+
};
|
|
14795
|
+
function acceptsToolResultMedia(type) {
|
|
14796
|
+
return TOOL_RESULT_MEDIA[type];
|
|
14797
|
+
}
|
|
14561
14798
|
function isModuleNotFound(err) {
|
|
14562
14799
|
if (!err || typeof err !== "object") return false;
|
|
14563
14800
|
const code = err.code;
|
|
@@ -14565,21 +14802,127 @@ function isModuleNotFound(err) {
|
|
|
14565
14802
|
const msg = err instanceof Error ? err.message : "";
|
|
14566
14803
|
return /Cannot find (module|package)|Failed to resolve/i.test(msg);
|
|
14567
14804
|
}
|
|
14805
|
+
// src/provider/entry-route.ts
|
|
14806
|
+
function buildEntryTable(entries2) {
|
|
14807
|
+
return {
|
|
14808
|
+
entries: new Map((entries2 ?? []).map((e) => [e.id, e]))
|
|
14809
|
+
};
|
|
14810
|
+
}
|
|
14811
|
+
function entryProvider(_table, entry) {
|
|
14812
|
+
return entry.provider;
|
|
14813
|
+
}
|
|
14814
|
+
function entrySelection(table, entry, origin) {
|
|
14815
|
+
return { provider: entryProvider(table, entry), model: entry.model, origin, entry: entry.id };
|
|
14816
|
+
}
|
|
14817
|
+
function routeKey(table, provider, entry) {
|
|
14818
|
+
if (!table.entries.has(entry)) return provider;
|
|
14819
|
+
return `${ENTRY_REF_PREFIX}${entry}`;
|
|
14820
|
+
}
|
|
14821
|
+
function applyEntryCapability(base2, entry) {
|
|
14822
|
+
if (!entry) return base2;
|
|
14823
|
+
const caps = entry.capabilities ?? {};
|
|
14824
|
+
const out = { ...base2 };
|
|
14825
|
+
if (caps.tools !== void 0) out.supportsTools = caps.tools;
|
|
14826
|
+
if (caps.images !== void 0) {
|
|
14827
|
+
out.supportsImages = caps.images;
|
|
14828
|
+
delete out.imagesUnknown;
|
|
14829
|
+
}
|
|
14830
|
+
if (caps.reasoning !== void 0) out.supportsReasoning = caps.reasoning;
|
|
14831
|
+
if (entry.contextLength) {
|
|
14832
|
+
out.contextLength = entry.contextLength;
|
|
14833
|
+
delete out.contextLengthUnknown;
|
|
14834
|
+
}
|
|
14835
|
+
if (entry.maxOutputTokens) out.maxOutputTokens = entry.maxOutputTokens;
|
|
14836
|
+
return out;
|
|
14837
|
+
}
|
|
14838
|
+
function entryAcceptsToolMedia(table, provider, entry) {
|
|
14839
|
+
const e = table.entries.get(entry);
|
|
14840
|
+
if (e?.provider === "openai-compatible" && e.protocol === "anthropic-messages") return true;
|
|
14841
|
+
return acceptsToolResultMedia(provider);
|
|
14842
|
+
}
|
|
14843
|
+
function capabilityOfRef(config, ref = config.model) {
|
|
14844
|
+
const parsed = ref ? parseModelRef(ref) : void 0;
|
|
14845
|
+
const entry = parsed ? config.modelEntries?.find((e) => e.id === parsed.entry) : void 0;
|
|
14846
|
+
if (!entry) return void 0;
|
|
14847
|
+
return applyEntryCapability(getCapability(entry.model, config.homeDir, entry.provider), entry);
|
|
14848
|
+
}
|
|
14849
|
+
// src/provider/fallback.ts
|
|
14850
|
+
function modelKey(provider, model) {
|
|
14851
|
+
return `${provider}/${model}`;
|
|
14852
|
+
}
|
|
14853
|
+
var MODEL_GIVE_UP_THRESHOLD = 3;
|
|
14854
|
+
var ModelFailureTracker = class {
|
|
14855
|
+
counts = /* @__PURE__ */ new Map();
|
|
14856
|
+
abandoned = /* @__PURE__ */ new Set();
|
|
14857
|
+
recordFailure(key) {
|
|
14858
|
+
if (this.abandoned.has(key)) return false;
|
|
14859
|
+
const next = (this.counts.get(key) ?? 0) + 1;
|
|
14860
|
+
this.counts.set(key, next);
|
|
14861
|
+
if (next < MODEL_GIVE_UP_THRESHOLD) return false;
|
|
14862
|
+
this.abandoned.add(key);
|
|
14863
|
+
return true;
|
|
14864
|
+
}
|
|
14865
|
+
recordSuccess(key) {
|
|
14866
|
+
this.counts.delete(key);
|
|
14867
|
+
}
|
|
14868
|
+
isAbandoned(key) {
|
|
14869
|
+
return this.abandoned.has(key);
|
|
14870
|
+
}
|
|
14871
|
+
clear(key) {
|
|
14872
|
+
this.counts.delete(key);
|
|
14873
|
+
this.abandoned.delete(key);
|
|
14874
|
+
}
|
|
14875
|
+
};
|
|
14876
|
+
// src/provider/reasoning-options.ts
|
|
14877
|
+
function maxOptions(dialect) {
|
|
14878
|
+
switch (dialect) {
|
|
14879
|
+
case "anthropic":
|
|
14880
|
+
return { anthropic: { effort: "max" } };
|
|
14881
|
+
case "deepseek":
|
|
14882
|
+
return { deepseek: { reasoningEffort: "max" } };
|
|
14883
|
+
case "openai":
|
|
14884
|
+
return { openai: { reasoningEffort: "max" } };
|
|
14885
|
+
case "openai-compatible":
|
|
14886
|
+
return { openaiCompatible: { reasoningEffort: "max" } };
|
|
14887
|
+
default:
|
|
14888
|
+
return void 0;
|
|
14889
|
+
}
|
|
14890
|
+
}
|
|
14891
|
+
function dialectOf(provider, entry) {
|
|
14892
|
+
if (provider === "openai-compatible") {
|
|
14893
|
+
return entry?.protocol === "anthropic-messages" ? "anthropic" : "openai-compatible";
|
|
14894
|
+
}
|
|
14895
|
+
if (provider === "zhipu" || provider === "bytedance" || provider === "qianfan") {
|
|
14896
|
+
return "openai-compatible";
|
|
14897
|
+
}
|
|
14898
|
+
if (provider === "anthropic" || provider === "deepseek" || provider === "openai") return provider;
|
|
14899
|
+
return "other";
|
|
14900
|
+
}
|
|
14901
|
+
function reasoningArgs(choice, dialect) {
|
|
14902
|
+
if (choice === void 0) return void 0;
|
|
14903
|
+
if (choice === "off") return { reasoning: "none" };
|
|
14904
|
+
if (choice !== "max") return { reasoning: choice };
|
|
14905
|
+
const extra = maxOptions(dialect);
|
|
14906
|
+
return extra ? { reasoning: "xhigh", providerOptions: extra } : { reasoning: "xhigh" };
|
|
14907
|
+
}
|
|
14908
|
+
function mergeProviderOptions(base2, extra) {
|
|
14909
|
+
if (!extra) return base2;
|
|
14910
|
+
const out = { ...base2 };
|
|
14911
|
+
for (const [key, value] of Object.entries(extra)) out[key] = { ...out[key], ...value };
|
|
14912
|
+
return out;
|
|
14913
|
+
}
|
|
14568
14914
|
var CONTEXT_WARN_RATIO = 0.8;
|
|
14569
14915
|
function fmtTokens(n) {
|
|
14570
14916
|
return n >= 1e3 ? `${Math.round(n / 1e3)}k` : String(n);
|
|
14571
14917
|
}
|
|
14572
14918
|
function checkCredential(want, deps) {
|
|
14573
14919
|
if (deps.hasCredential(want.provider)) return null;
|
|
14574
|
-
return { code: "no-credential",
|
|
14920
|
+
return { code: "no-credential", entry: want.entry };
|
|
14575
14921
|
}
|
|
14576
14922
|
function describeRejection(rejection) {
|
|
14577
14923
|
switch (rejection.code) {
|
|
14578
14924
|
case "no-credential":
|
|
14579
|
-
return t("provider_switch.no_credential", {
|
|
14580
|
-
provider: rejection.provider,
|
|
14581
|
-
hint: rejection.envVar ? t("provider_switch.no_credential_env", { envVar: rejection.envVar }) : t("provider_switch.no_credential_plain")
|
|
14582
|
-
});
|
|
14925
|
+
return t("provider_switch.no_credential", { entry: rejection.entry });
|
|
14583
14926
|
case "images-unsupported":
|
|
14584
14927
|
return t("provider_switch.images_unsupported", { model: rejection.model });
|
|
14585
14928
|
case "context-overflow":
|
|
@@ -14588,6 +14931,8 @@ function describeRejection(rejection) {
|
|
|
14588
14931
|
model: rejection.model,
|
|
14589
14932
|
limit: fmtTokens(rejection.contextLength)
|
|
14590
14933
|
});
|
|
14934
|
+
case "unknown-entry":
|
|
14935
|
+
return t("provider_switch.unknown_entry", { entry: rejection.entry });
|
|
14591
14936
|
}
|
|
14592
14937
|
}
|
|
14593
14938
|
function describeWarning(warning) {
|
|
@@ -14639,15 +14984,13 @@ function evaluateSelection(want, deps, ctx = {}) {
|
|
|
14639
14984
|
function reject(rejection) {
|
|
14640
14985
|
return { ok: false, reason: describeRejection(rejection), rejection };
|
|
14641
14986
|
}
|
|
14642
|
-
// src/provider/types.ts
|
|
14643
|
-
var DEFAULT_MODEL = "gpt-4o-mini";
|
|
14644
14987
|
// src/provider/router.ts
|
|
14645
14988
|
var ProviderRouter = class {
|
|
14646
14989
|
config;
|
|
14647
14990
|
available = /* @__PURE__ */ new Map();
|
|
14648
14991
|
factories = /* @__PURE__ */ new Map();
|
|
14649
|
-
|
|
14650
|
-
|
|
14992
|
+
entryTable;
|
|
14993
|
+
effort;
|
|
14651
14994
|
telemetry = NOOP_TELEMETRY;
|
|
14652
14995
|
selection;
|
|
14653
14996
|
configSelection;
|
|
@@ -14658,44 +15001,28 @@ var ProviderRouter = class {
|
|
|
14658
15001
|
}
|
|
14659
15002
|
constructor(config) {
|
|
14660
15003
|
this.config = config;
|
|
14661
|
-
this.
|
|
14662
|
-
this.
|
|
14663
|
-
this.
|
|
14664
|
-
provider: config.provider.type,
|
|
14665
|
-
model: config.model ?? DEFAULT_MODEL,
|
|
14666
|
-
origin: "config"
|
|
14667
|
-
};
|
|
15004
|
+
this.entryTable = buildEntryTable(config.modelEntries);
|
|
15005
|
+
this.configSelection = this.resolveConfigured(config);
|
|
15006
|
+
this.initFactories();
|
|
14668
15007
|
this.selection = { ...this.configSelection };
|
|
14669
15008
|
}
|
|
14670
15009
|
getSelection() {
|
|
14671
15010
|
return { ...this.selection };
|
|
14672
15011
|
}
|
|
14673
15012
|
getConfiguredModel() {
|
|
14674
|
-
return this.configSelection.
|
|
15013
|
+
return entryRef(this.configSelection.entry);
|
|
14675
15014
|
}
|
|
14676
15015
|
setSelection(next, ctx = {}) {
|
|
14677
|
-
|
|
14678
|
-
|
|
14679
|
-
provider: next.provider ?? this.selection.provider,
|
|
14680
|
-
model: next.model,
|
|
14681
|
-
origin: "user-switch"
|
|
14682
|
-
},
|
|
14683
|
-
ctx
|
|
14684
|
-
);
|
|
15016
|
+
const want = this.resolveRef(next, "user-switch");
|
|
15017
|
+
return "ok" in want ? want : this.applySelection(want, ctx);
|
|
14685
15018
|
}
|
|
14686
15019
|
resetSelection(ctx = {}) {
|
|
14687
15020
|
return this.applySelection({ ...this.configSelection }, ctx);
|
|
14688
15021
|
}
|
|
14689
15022
|
enterTurn(ref, ctx = {}) {
|
|
14690
15023
|
const previous = { ...this.selection };
|
|
14691
|
-
const
|
|
14692
|
-
|
|
14693
|
-
provider: ref.provider ?? this.selection.provider,
|
|
14694
|
-
model: ref.model,
|
|
14695
|
-
origin: "command-frontmatter"
|
|
14696
|
-
},
|
|
14697
|
-
ctx
|
|
14698
|
-
);
|
|
15024
|
+
const want = this.resolveRef(ref, "command-frontmatter");
|
|
15025
|
+
const result = "ok" in want ? want : this.applySelection(want, ctx);
|
|
14699
15026
|
return {
|
|
14700
15027
|
result,
|
|
14701
15028
|
restore: () => {
|
|
@@ -14707,15 +15034,16 @@ var ProviderRouter = class {
|
|
|
14707
15034
|
const result = evaluateSelection(
|
|
14708
15035
|
want,
|
|
14709
15036
|
{
|
|
14710
|
-
hasCredential: (p) => this.available.has(p),
|
|
14711
|
-
capability: (model) =>
|
|
15037
|
+
hasCredential: (p) => this.available.has(this.routeOf(p, want.entry)),
|
|
15038
|
+
capability: (model) => this.capabilityFor(want.provider, model, want.entry)
|
|
14712
15039
|
},
|
|
14713
15040
|
ctx
|
|
14714
15041
|
);
|
|
14715
15042
|
if (!result.ok) return result;
|
|
14716
15043
|
const from = this.selection;
|
|
14717
15044
|
this.selection = want;
|
|
14718
|
-
this.
|
|
15045
|
+
this.effort = void 0;
|
|
15046
|
+
this.failures.clear(this.routeOf(want.provider, want.entry));
|
|
14719
15047
|
this.telemetry.counter(METRIC.MODEL_SWITCH, 1, {
|
|
14720
15048
|
"epoch.model.from": modelKey(from.provider, from.model),
|
|
14721
15049
|
"epoch.model.to": modelKey(want.provider, want.model),
|
|
@@ -14723,9 +15051,77 @@ var ProviderRouter = class {
|
|
|
14723
15051
|
});
|
|
14724
15052
|
return result;
|
|
14725
15053
|
}
|
|
15054
|
+
reloadModelEntries(entries2) {
|
|
15055
|
+
this.entryTable = buildEntryTable(entries2);
|
|
15056
|
+
for (const [route, opts] of [...this.available]) {
|
|
15057
|
+
if (!opts.entry) continue;
|
|
15058
|
+
this.available.delete(route);
|
|
15059
|
+
this.factories.delete(route);
|
|
15060
|
+
}
|
|
15061
|
+
this.registerEntries();
|
|
15062
|
+
const configured = this.refreshedSelection(this.configSelection);
|
|
15063
|
+
if (configured) this.configSelection = configured;
|
|
15064
|
+
else if (this.entryTable.entries.has(this.configSelection.entry))
|
|
15065
|
+
this.notices.push(
|
|
15066
|
+
t("provider_route.entry_no_credential", { entry: this.configSelection.entry })
|
|
15067
|
+
);
|
|
15068
|
+
else
|
|
15069
|
+
this.notices.push(t("provider_route.entry_missing", { entry: this.configSelection.entry }));
|
|
15070
|
+
const selection = this.refreshedSelection(this.selection);
|
|
15071
|
+
if (selection) {
|
|
15072
|
+
this.selection = selection;
|
|
15073
|
+
} else if (configured) {
|
|
15074
|
+
const gone = this.selection.entry;
|
|
15075
|
+
this.selection = { ...configured };
|
|
15076
|
+
this.effort = void 0;
|
|
15077
|
+
this.notices.push(
|
|
15078
|
+
this.entryTable.entries.has(gone) ? t("provider_route.entry_unusable", { entry: gone, model: entryRef(configured.entry) }) : t("provider_route.entry_removed", { entry: gone, model: entryRef(configured.entry) })
|
|
15079
|
+
);
|
|
15080
|
+
}
|
|
15081
|
+
if (this.effort !== void 0) {
|
|
15082
|
+
const state = this.getEffort();
|
|
15083
|
+
const ok2 = this.effort === "off" ? state.canTurnOff : state.levels.includes(this.effort);
|
|
15084
|
+
if (!ok2) this.effort = void 0;
|
|
15085
|
+
}
|
|
15086
|
+
}
|
|
15087
|
+
refreshedSelection(sel) {
|
|
15088
|
+
const entry = this.entryTable.entries.get(sel.entry);
|
|
15089
|
+
if (!entry) return void 0;
|
|
15090
|
+
const next = entrySelection(this.entryTable, entry, sel.origin);
|
|
15091
|
+
return this.available.has(this.routeOf(next.provider, next.entry)) ? next : void 0;
|
|
15092
|
+
}
|
|
14726
15093
|
drainNotices() {
|
|
14727
15094
|
return this.notices.splice(0, this.notices.length);
|
|
14728
15095
|
}
|
|
15096
|
+
getEffort() {
|
|
15097
|
+
const entry = this.entryTable.entries.get(this.selection.entry);
|
|
15098
|
+
const caps = entry?.capabilities;
|
|
15099
|
+
if (!entry || !caps?.reasoning)
|
|
15100
|
+
return { levels: [], canTurnOff: false, choice: null, default: null };
|
|
15101
|
+
return {
|
|
15102
|
+
levels: entry.reasoning?.levels ?? [],
|
|
15103
|
+
canTurnOff: caps.reasoningToggle === true && caps.reasoningOnly !== true,
|
|
15104
|
+
choice: this.effort ?? null,
|
|
15105
|
+
default: entry.reasoning?.default ?? null
|
|
15106
|
+
};
|
|
15107
|
+
}
|
|
15108
|
+
setEffort(choice) {
|
|
15109
|
+
const state = this.getEffort();
|
|
15110
|
+
if (choice === null) {
|
|
15111
|
+
this.effort = void 0;
|
|
15112
|
+
return true;
|
|
15113
|
+
}
|
|
15114
|
+
const ok2 = choice === "off" ? state.canTurnOff : state.levels.includes(choice);
|
|
15115
|
+
if (ok2) this.effort = choice;
|
|
15116
|
+
return ok2;
|
|
15117
|
+
}
|
|
15118
|
+
reasoningFor(input, ct, entry) {
|
|
15119
|
+
if (input.entry || entry !== this.selection.entry) return void 0;
|
|
15120
|
+
const e = this.entryTable.entries.get(entry);
|
|
15121
|
+
if (!e?.capabilities?.reasoning) return void 0;
|
|
15122
|
+
const choice = this.effort ?? e.reasoning?.default;
|
|
15123
|
+
return reasoningArgs(choice, dialectOf(ct, e));
|
|
15124
|
+
}
|
|
14729
15125
|
getUtilityInfo() {
|
|
14730
15126
|
const raw = this.config.models?.utility;
|
|
14731
15127
|
if (!raw) return { selection: this.getSelection(), configured: false };
|
|
@@ -14737,149 +15133,129 @@ var ProviderRouter = class {
|
|
|
14737
15133
|
issue: t("provider_route.utility_unparsable", { raw })
|
|
14738
15134
|
};
|
|
14739
15135
|
}
|
|
14740
|
-
|
|
14741
|
-
if (!this.available.has(provider)) {
|
|
14742
|
-
return {
|
|
14743
|
-
selection: this.getSelection(),
|
|
14744
|
-
configured: true,
|
|
14745
|
-
issue: t("provider_route.utility_no_credential", { provider })
|
|
14746
|
-
};
|
|
14747
|
-
}
|
|
14748
|
-
return {
|
|
14749
|
-
selection: { provider, model: ref.model, origin: this.selection.origin },
|
|
14750
|
-
configured: true
|
|
14751
|
-
};
|
|
15136
|
+
return this.entryUtilityInfo(ref.entry);
|
|
14752
15137
|
}
|
|
14753
|
-
initFactories(
|
|
14754
|
-
|
|
14755
|
-
|
|
14756
|
-
|
|
14757
|
-
}
|
|
14758
|
-
for (const type of PROVIDER_TYPES) {
|
|
14759
|
-
const key = this.resolveKey(type, config);
|
|
14760
|
-
if (!key) continue;
|
|
14761
|
-
const isPrimary = type === config.provider.type;
|
|
14762
|
-
this.register(type, key, isPrimary ? config.provider.baseUrl : void 0);
|
|
14763
|
-
}
|
|
14764
|
-
if (!this.available.has(config.provider.type)) {
|
|
15138
|
+
initFactories() {
|
|
15139
|
+
this.registerEntries();
|
|
15140
|
+
const { provider, entry } = this.configSelection;
|
|
15141
|
+
if (!this.available.has(this.routeOf(provider, entry))) {
|
|
14765
15142
|
throw new ProviderError(
|
|
14766
|
-
t("provider_route.
|
|
15143
|
+
t("provider_route.entry_no_credential", { entry }),
|
|
14767
15144
|
PROVIDER_NO_CREDENTIAL,
|
|
14768
15145
|
false
|
|
14769
15146
|
);
|
|
14770
15147
|
}
|
|
14771
|
-
|
|
14772
|
-
|
|
14773
|
-
|
|
14774
|
-
|
|
14775
|
-
|
|
14776
|
-
|
|
15148
|
+
}
|
|
15149
|
+
registerEntries() {
|
|
15150
|
+
for (const entry of this.entryTable.entries.values()) {
|
|
15151
|
+
const key = readEntryKey(entry.id, this.config.homeDir);
|
|
15152
|
+
if (!key && !entry.host && entry.provider !== "openai-compatible" && entry.provider !== "ollama")
|
|
15153
|
+
continue;
|
|
15154
|
+
this.available.set(this.routeOf(entry.provider, entry.id), { apiKey: key ?? "", entry });
|
|
14777
15155
|
}
|
|
14778
15156
|
}
|
|
14779
|
-
|
|
14780
|
-
this.
|
|
14781
|
-
|
|
15157
|
+
routeOf(provider, entry) {
|
|
15158
|
+
return routeKey(this.entryTable, provider, entry);
|
|
15159
|
+
}
|
|
15160
|
+
capabilityFor(provider, model, entry) {
|
|
15161
|
+
return applyEntryCapability(
|
|
15162
|
+
getCapability(model, this.config.homeDir, provider),
|
|
15163
|
+
this.entryTable.entries.get(entry)
|
|
15164
|
+
);
|
|
15165
|
+
}
|
|
15166
|
+
resolveConfigured(config) {
|
|
15167
|
+
const ref = config.model ? parseModelRef(config.model) : void 0;
|
|
15168
|
+
const entry = ref ? this.entryTable.entries.get(ref.entry) : void 0;
|
|
15169
|
+
if (entry) return entrySelection(this.entryTable, entry, "config");
|
|
15170
|
+
throw new ProviderError(
|
|
15171
|
+
ref ? t("provider_route.entry_missing", { entry: ref.entry }) : t("provider_route.no_model"),
|
|
15172
|
+
PROVIDER_NO_CREDENTIAL,
|
|
15173
|
+
false
|
|
15174
|
+
);
|
|
14782
15175
|
}
|
|
14783
|
-
|
|
14784
|
-
|
|
14785
|
-
|
|
15176
|
+
resolveRef(ref, origin) {
|
|
15177
|
+
const entry = this.entryTable.entries.get(ref.entry);
|
|
15178
|
+
if (!entry) {
|
|
15179
|
+
const rejection = { code: "unknown-entry", entry: ref.entry };
|
|
15180
|
+
return { ok: false, reason: describeRejection(rejection), rejection };
|
|
14786
15181
|
}
|
|
14787
|
-
return this.
|
|
15182
|
+
return entrySelection(this.entryTable, entry, origin);
|
|
14788
15183
|
}
|
|
14789
|
-
|
|
14790
|
-
|
|
14791
|
-
|
|
14792
|
-
|
|
14793
|
-
|
|
15184
|
+
entryUtilityInfo(id) {
|
|
15185
|
+
const entry = this.entryTable.entries.get(id);
|
|
15186
|
+
if (!entry) {
|
|
15187
|
+
return {
|
|
15188
|
+
selection: this.getSelection(),
|
|
15189
|
+
configured: true,
|
|
15190
|
+
issue: t("provider_route.utility_unknown_entry", { entry: id })
|
|
15191
|
+
};
|
|
15192
|
+
}
|
|
15193
|
+
const selection = entrySelection(this.entryTable, entry, this.selection.origin);
|
|
15194
|
+
if (!this.available.has(this.routeOf(selection.provider, selection.entry))) {
|
|
15195
|
+
return {
|
|
15196
|
+
selection: this.getSelection(),
|
|
15197
|
+
configured: true,
|
|
15198
|
+
issue: t("provider_route.entry_no_credential", { entry: id })
|
|
15199
|
+
};
|
|
15200
|
+
}
|
|
15201
|
+
return { selection, configured: true };
|
|
14794
15202
|
}
|
|
14795
|
-
async getModel(
|
|
14796
|
-
const cached = this.factories.get(
|
|
15203
|
+
async getModel(route, modelId) {
|
|
15204
|
+
const cached = this.factories.get(route);
|
|
14797
15205
|
if (cached) return cached(modelId ?? this.selection.model);
|
|
14798
|
-
const opts = this.available.get(
|
|
15206
|
+
const opts = this.available.get(route);
|
|
14799
15207
|
if (!opts) {
|
|
14800
|
-
throw new ProviderError(
|
|
15208
|
+
throw new ProviderError(
|
|
15209
|
+
t("provider_route.not_initialized", { provider: route }),
|
|
15210
|
+
1004,
|
|
15211
|
+
false
|
|
15212
|
+
);
|
|
14801
15213
|
}
|
|
14802
15214
|
let factory;
|
|
14803
15215
|
try {
|
|
14804
|
-
factory = await createModelFactory(
|
|
15216
|
+
factory = opts.entry ? await createEntryFactory(opts.entry, opts.apiKey || void 0) : await createModelFactory(route, opts);
|
|
14805
15217
|
} catch (err) {
|
|
14806
15218
|
throw new ProviderError(err instanceof Error ? err.message : String(err), 1005, false);
|
|
14807
15219
|
}
|
|
14808
|
-
this.factories.set(
|
|
15220
|
+
this.factories.set(route, factory);
|
|
14809
15221
|
return factory(modelId ?? this.selection.model);
|
|
14810
15222
|
}
|
|
14811
|
-
rotateCredential() {
|
|
14812
|
-
const creds = this.config.provider.credentials;
|
|
14813
|
-
if (this.credentialIndex + 1 < creds.length) {
|
|
14814
|
-
this.credentialIndex++;
|
|
14815
|
-
const newCred = creds[this.credentialIndex];
|
|
14816
|
-
if (!newCred) return false;
|
|
14817
|
-
this.register(this.config.provider.type, newCred.apiKey, this.config.provider.baseUrl);
|
|
14818
|
-
return true;
|
|
14819
|
-
}
|
|
14820
|
-
return false;
|
|
14821
|
-
}
|
|
14822
|
-
fallbackProvider(currentType, tried) {
|
|
14823
|
-
if (!this.config.fallback) return null;
|
|
14824
|
-
for (const fbType of this.config.fallback.providers) {
|
|
14825
|
-
if (fbType === currentType) continue;
|
|
14826
|
-
if (tried?.has(modelKey(fbType, defaultModelOf(fbType)))) continue;
|
|
14827
|
-
if (this.available.has(fbType)) {
|
|
14828
|
-
this.telemetry.counter(METRIC.PROVIDER_FALLBACK, 1, {
|
|
14829
|
-
"epoch.provider.from": currentType,
|
|
14830
|
-
"epoch.provider.to": fbType
|
|
14831
|
-
});
|
|
14832
|
-
return {
|
|
14833
|
-
from: currentType,
|
|
14834
|
-
fromModel: this.getModelId(),
|
|
14835
|
-
to: fbType,
|
|
14836
|
-
toModel: defaultModelOf(fbType),
|
|
14837
|
-
reason: t("provider_route.fallback_reason")
|
|
14838
|
-
};
|
|
14839
|
-
}
|
|
14840
|
-
}
|
|
14841
|
-
return null;
|
|
14842
|
-
}
|
|
14843
15223
|
startPoint(input) {
|
|
14844
15224
|
if (input.responseFormat && input.tools?.length) {
|
|
14845
15225
|
throw new ProviderError(t("provider.response_format_with_tools"), 1002, false);
|
|
14846
15226
|
}
|
|
14847
|
-
const
|
|
14848
|
-
|
|
14849
|
-
|
|
14850
|
-
|
|
14851
|
-
|
|
14852
|
-
false
|
|
14853
|
-
);
|
|
15227
|
+
const id = input.entry ?? this.selection.entry;
|
|
15228
|
+
const entry = this.entryTable.entries.get(id);
|
|
15229
|
+
const sel = entry ? entrySelection(this.entryTable, entry, "config") : void 0;
|
|
15230
|
+
if (!sel || !this.available.has(this.routeOf(sel.provider, sel.entry))) {
|
|
15231
|
+
throw new ProviderError(t("provider_route.cannot_use", { entry: id }), 1001, false);
|
|
14854
15232
|
}
|
|
14855
|
-
const
|
|
14856
|
-
|
|
14857
|
-
|
|
15233
|
+
const start = input.entry ? sel : this.avoidAbandoned(sel);
|
|
15234
|
+
return {
|
|
15235
|
+
ct: start.provider,
|
|
15236
|
+
cm: start.model,
|
|
15237
|
+
entry: start.entry,
|
|
15238
|
+
attempt: 0,
|
|
15239
|
+
tried: /* @__PURE__ */ new Set([entryTried(start.entry)])
|
|
15240
|
+
};
|
|
14858
15241
|
}
|
|
14859
|
-
avoidAbandoned(
|
|
14860
|
-
if (!this.failures.isAbandoned(
|
|
14861
|
-
const
|
|
14862
|
-
|
|
14863
|
-
const next = nextFallbackModel(ct, cm, seen, this.configuredFallbackModel(ct));
|
|
14864
|
-
if (!next) return cm;
|
|
14865
|
-
if (!this.failures.isAbandoned(ct, next)) return next;
|
|
14866
|
-
seen.add(modelKey(ct, next));
|
|
14867
|
-
}
|
|
15242
|
+
avoidAbandoned(sel) {
|
|
15243
|
+
if (!this.failures.isAbandoned(this.routeOf(sel.provider, sel.entry))) return sel;
|
|
15244
|
+
const fb = this.fallbackEntry(sel.entry, /* @__PURE__ */ new Set([entryTried(sel.entry)]));
|
|
15245
|
+
return fb && !this.failures.isAbandoned(this.routeOf(fb.provider, fb.entry)) ? fb : sel;
|
|
14868
15246
|
}
|
|
14869
15247
|
async recover(err, state) {
|
|
14870
15248
|
const { code, recovery } = classifyProviderError(err);
|
|
14871
15249
|
const message = describeProviderError(err);
|
|
14872
|
-
const stay = { kind: "continue", ct: state.ct, cm: state.cm };
|
|
15250
|
+
const stay = { kind: "continue", ct: state.ct, cm: state.cm, entry: state.entry };
|
|
14873
15251
|
if (code === PROVIDER_ABORTED) return { kind: "give-up", message, code };
|
|
14874
|
-
if (recovery === "rotate_credential" && this.rotateCredential())
|
|
14875
|
-
return { ...stay, attempt: state.attempt + 1 };
|
|
14876
15252
|
if (recovery === "retry") {
|
|
14877
15253
|
await this.backoff(state.attempt);
|
|
14878
15254
|
return { ...stay, attempt: state.attempt + 1 };
|
|
14879
15255
|
}
|
|
14880
|
-
this.noteFailure(state
|
|
15256
|
+
this.noteFailure(state);
|
|
14881
15257
|
if (recovery === "abort") return { kind: "give-up", message, code };
|
|
14882
|
-
const next = this.escalate(state
|
|
15258
|
+
const next = this.escalate(state);
|
|
14883
15259
|
if (next) return next;
|
|
14884
15260
|
return {
|
|
14885
15261
|
kind: "give-up",
|
|
@@ -14890,36 +15266,40 @@ var ProviderRouter = class {
|
|
|
14890
15266
|
code
|
|
14891
15267
|
};
|
|
14892
15268
|
}
|
|
14893
|
-
escalate(state
|
|
14894
|
-
|
|
14895
|
-
|
|
14896
|
-
|
|
14897
|
-
|
|
14898
|
-
|
|
14899
|
-
|
|
14900
|
-
|
|
14901
|
-
|
|
14902
|
-
|
|
14903
|
-
|
|
14904
|
-
|
|
14905
|
-
|
|
14906
|
-
|
|
14907
|
-
|
|
14908
|
-
|
|
14909
|
-
|
|
14910
|
-
|
|
14911
|
-
|
|
14912
|
-
state.tried.add(modelKey(fb.to, fb.toModel));
|
|
14913
|
-
return { kind: "continue", ct: fb.to, cm: fb.toModel, attempt: 0 };
|
|
15269
|
+
escalate(state) {
|
|
15270
|
+
const target = this.fallbackEntry(state.entry, state.tried);
|
|
15271
|
+
if (!target) return null;
|
|
15272
|
+
this.telemetry.counter(METRIC.MODEL_FALLBACK, 1, {
|
|
15273
|
+
"epoch.model.from": modelKey(state.ct, state.cm),
|
|
15274
|
+
"epoch.model.to": modelKey(target.provider, target.model)
|
|
15275
|
+
});
|
|
15276
|
+
state.tried.add(entryTried(target.entry));
|
|
15277
|
+
return {
|
|
15278
|
+
kind: "continue",
|
|
15279
|
+
ct: target.provider,
|
|
15280
|
+
cm: target.model,
|
|
15281
|
+
entry: target.entry,
|
|
15282
|
+
attempt: 0
|
|
15283
|
+
};
|
|
15284
|
+
}
|
|
15285
|
+
afterRetries(state) {
|
|
15286
|
+
this.noteFailure(state);
|
|
15287
|
+
return this.escalate(state);
|
|
14914
15288
|
}
|
|
14915
|
-
|
|
14916
|
-
|
|
15289
|
+
fallbackEntry(current, tried) {
|
|
15290
|
+
const raw = this.config.fallback?.model;
|
|
15291
|
+
const id = raw ? parseModelRef(raw)?.entry : void 0;
|
|
15292
|
+
const entry = id ? this.entryTable.entries.get(id) : void 0;
|
|
15293
|
+
if (!entry || entry.id === current || tried.has(entryTried(entry.id))) return void 0;
|
|
15294
|
+
const sel = entrySelection(this.entryTable, entry, "fallback");
|
|
15295
|
+
return this.available.has(this.routeOf(sel.provider, sel.entry)) ? sel : void 0;
|
|
14917
15296
|
}
|
|
14918
|
-
noteFailure(
|
|
14919
|
-
if (!this.failures.recordFailure(
|
|
15297
|
+
noteFailure(state) {
|
|
15298
|
+
if (!this.failures.recordFailure(this.routeOf(state.ct, state.entry))) return;
|
|
14920
15299
|
this.notices.push(
|
|
14921
15300
|
t("provider_route.give_up_model", {
|
|
14922
|
-
|
|
15301
|
+
entry: state.entry,
|
|
15302
|
+
model: state.cm,
|
|
14923
15303
|
count: MODEL_GIVE_UP_THRESHOLD
|
|
14924
15304
|
})
|
|
14925
15305
|
);
|
|
@@ -14931,30 +15311,50 @@ var ProviderRouter = class {
|
|
|
14931
15311
|
async generate(input) {
|
|
14932
15312
|
const state = this.startPoint(input);
|
|
14933
15313
|
const requestedModel = state.cm;
|
|
14934
|
-
const
|
|
15314
|
+
const messagesFor = messageTranslator(input.messages, this.entryTable);
|
|
14935
15315
|
const tools = input.tools ? toSdkTools(input.tools) : void 0;
|
|
14936
15316
|
const output = input.responseFormat ? toOutputSpec(input.responseFormat) : void 0;
|
|
14937
|
-
|
|
14938
|
-
|
|
15317
|
+
for (; ; ) {
|
|
15318
|
+
if (state.attempt > this.config.retry.maxRetries) {
|
|
15319
|
+
const next = this.afterRetries(state);
|
|
15320
|
+
if (!next) {
|
|
15321
|
+
throw new ProviderError(
|
|
15322
|
+
t("provider_route.retries_exhausted", { count: this.config.retry.maxRetries }),
|
|
15323
|
+
9999,
|
|
15324
|
+
false
|
|
15325
|
+
);
|
|
15326
|
+
}
|
|
15327
|
+
state.ct = next.ct;
|
|
15328
|
+
state.cm = next.cm;
|
|
15329
|
+
state.entry = next.entry;
|
|
15330
|
+
state.attempt = next.attempt;
|
|
15331
|
+
continue;
|
|
15332
|
+
}
|
|
15333
|
+
const { ct, cm, entry } = state;
|
|
14939
15334
|
try {
|
|
14940
|
-
const model = await this.getModel(ct, cm);
|
|
15335
|
+
const model = await this.getModel(this.routeOf(ct, entry), cm);
|
|
15336
|
+
const maxTokens = this.maxTokensFor(input, entry);
|
|
15337
|
+
const effort = this.reasoningFor(input, ct, entry);
|
|
14941
15338
|
const result = await generateText({
|
|
14942
15339
|
model,
|
|
14943
|
-
messages,
|
|
15340
|
+
messages: messagesFor(ct, entry),
|
|
14944
15341
|
tools,
|
|
14945
15342
|
system: input.system,
|
|
14946
15343
|
stopWhen: isStepCount(input.maxSteps ?? 1),
|
|
14947
|
-
...
|
|
15344
|
+
...maxTokens ? { maxOutputTokens: maxTokens } : {},
|
|
14948
15345
|
...input.signal ? { abortSignal: input.signal } : {},
|
|
14949
15346
|
output,
|
|
14950
|
-
providerOptions: toSdkProviderOptions(
|
|
15347
|
+
providerOptions: toSdkProviderOptions(
|
|
15348
|
+
mergeProviderOptions(input.providerOptions, effort?.providerOptions)
|
|
15349
|
+
),
|
|
15350
|
+
...effort ? { reasoning: effort.reasoning } : {}
|
|
14951
15351
|
});
|
|
14952
15352
|
const toolCalls = normalizeToolCalls(
|
|
14953
15353
|
result.toolCalls
|
|
14954
15354
|
);
|
|
14955
15355
|
const steps = result.steps;
|
|
14956
15356
|
const reasoningText = steps?.map((s) => s.reasoningText).filter(Boolean).join("\n") || void 0;
|
|
14957
|
-
this.failures.recordSuccess(ct,
|
|
15357
|
+
this.failures.recordSuccess(this.routeOf(ct, entry));
|
|
14958
15358
|
const usage = toTokenUsage(result.usage);
|
|
14959
15359
|
const warnings = toProviderWarnings(result.warnings);
|
|
14960
15360
|
const applied = input.responseFormat ? didApplyResponseFormat(result.warnings) : void 0;
|
|
@@ -14976,39 +15376,54 @@ var ProviderRouter = class {
|
|
|
14976
15376
|
if (plan.kind === "give-up") throw new ProviderError(plan.message, plan.code, false);
|
|
14977
15377
|
state.ct = plan.ct;
|
|
14978
15378
|
state.cm = plan.cm;
|
|
15379
|
+
state.entry = plan.entry;
|
|
14979
15380
|
state.attempt = plan.attempt;
|
|
14980
15381
|
}
|
|
14981
15382
|
}
|
|
14982
|
-
this.noteFailure(state.ct, state.cm);
|
|
14983
|
-
throw new ProviderError(
|
|
14984
|
-
t("provider_route.retries_exhausted", { count: this.config.retry.maxRetries }),
|
|
14985
|
-
9999,
|
|
14986
|
-
false
|
|
14987
|
-
);
|
|
14988
15383
|
}
|
|
14989
15384
|
async *stream(input) {
|
|
14990
15385
|
const state = this.startPoint(input);
|
|
14991
15386
|
const requestedModel = state.cm;
|
|
14992
15387
|
let emitted = false;
|
|
14993
|
-
const
|
|
15388
|
+
const messagesFor = messageTranslator(input.messages, this.entryTable);
|
|
14994
15389
|
const tools = input.tools ? toSdkTools(input.tools) : void 0;
|
|
14995
15390
|
const output = input.responseFormat ? toOutputSpec(input.responseFormat) : void 0;
|
|
14996
15391
|
let streamPartError;
|
|
14997
|
-
|
|
14998
|
-
|
|
15392
|
+
for (; ; ) {
|
|
15393
|
+
if (state.attempt > this.config.retry.maxRetries) {
|
|
15394
|
+
const next = this.afterRetries(state);
|
|
15395
|
+
if (!next) {
|
|
15396
|
+
yield {
|
|
15397
|
+
type: "error",
|
|
15398
|
+
error: t("provider_route.retries_exhausted", { count: this.config.retry.maxRetries })
|
|
15399
|
+
};
|
|
15400
|
+
return;
|
|
15401
|
+
}
|
|
15402
|
+
state.ct = next.ct;
|
|
15403
|
+
state.cm = next.cm;
|
|
15404
|
+
state.entry = next.entry;
|
|
15405
|
+
state.attempt = next.attempt;
|
|
15406
|
+
continue;
|
|
15407
|
+
}
|
|
15408
|
+
const { ct, cm, entry } = state;
|
|
14999
15409
|
streamPartError = void 0;
|
|
15000
15410
|
try {
|
|
15001
|
-
const model = await this.getModel(ct, cm);
|
|
15411
|
+
const model = await this.getModel(this.routeOf(ct, entry), cm);
|
|
15412
|
+
const maxTokens = this.maxTokensFor(input, entry);
|
|
15413
|
+
const effort = this.reasoningFor(input, ct, entry);
|
|
15002
15414
|
const result = streamText({
|
|
15003
15415
|
model,
|
|
15004
|
-
messages,
|
|
15416
|
+
messages: messagesFor(ct, entry),
|
|
15005
15417
|
tools,
|
|
15006
15418
|
system: input.system,
|
|
15007
15419
|
stopWhen: isStepCount(input.maxSteps ?? 1),
|
|
15008
|
-
...
|
|
15420
|
+
...maxTokens ? { maxOutputTokens: maxTokens } : {},
|
|
15009
15421
|
...input.signal ? { abortSignal: input.signal } : {},
|
|
15010
15422
|
output,
|
|
15011
|
-
providerOptions: toSdkProviderOptions(
|
|
15423
|
+
providerOptions: toSdkProviderOptions(
|
|
15424
|
+
mergeProviderOptions(input.providerOptions, effort?.providerOptions)
|
|
15425
|
+
),
|
|
15426
|
+
...effort ? { reasoning: effort.reasoning } : {}
|
|
15012
15427
|
});
|
|
15013
15428
|
for await (const part of result.fullStream) {
|
|
15014
15429
|
const typed = part;
|
|
@@ -15047,7 +15462,7 @@ var ProviderRouter = class {
|
|
|
15047
15462
|
const warnings = toProviderWarnings(raw);
|
|
15048
15463
|
const applied = input.responseFormat ? didApplyResponseFormat(raw) : void 0;
|
|
15049
15464
|
this.noteResponseFormat(input, applied ?? true, state);
|
|
15050
|
-
this.failures.recordSuccess(ct,
|
|
15465
|
+
this.failures.recordSuccess(this.routeOf(ct, entry));
|
|
15051
15466
|
yield {
|
|
15052
15467
|
type: "finish",
|
|
15053
15468
|
...usage ? { usage } : {},
|
|
@@ -15072,14 +15487,15 @@ var ProviderRouter = class {
|
|
|
15072
15487
|
}
|
|
15073
15488
|
state.ct = plan.ct;
|
|
15074
15489
|
state.cm = plan.cm;
|
|
15490
|
+
state.entry = plan.entry;
|
|
15075
15491
|
state.attempt = plan.attempt;
|
|
15076
15492
|
}
|
|
15077
15493
|
}
|
|
15078
|
-
|
|
15079
|
-
|
|
15080
|
-
|
|
15081
|
-
|
|
15082
|
-
|
|
15494
|
+
}
|
|
15495
|
+
maxTokensFor(input, entry) {
|
|
15496
|
+
const cap = entry ? this.entryTable.entries.get(entry)?.maxOutputTokens : void 0;
|
|
15497
|
+
if (!cap) return input.maxTokens;
|
|
15498
|
+
return input.maxTokens ? Math.min(input.maxTokens, cap) : cap;
|
|
15083
15499
|
}
|
|
15084
15500
|
async backoff(n) {
|
|
15085
15501
|
await new Promise(
|
|
@@ -15096,24 +15512,22 @@ var ProviderRouter = class {
|
|
|
15096
15512
|
return this.selection.provider;
|
|
15097
15513
|
}
|
|
15098
15514
|
getApiKeyHint() {
|
|
15099
|
-
|
|
15100
|
-
|
|
15101
|
-
|
|
15102
|
-
}
|
|
15103
|
-
const opts = this.available.get(this.selection.provider);
|
|
15104
|
-
return opts ? maskApiKey(opts.apiKey) : "none";
|
|
15515
|
+
const { provider, entry } = this.selection;
|
|
15516
|
+
const opts = this.available.get(this.routeOf(provider, entry));
|
|
15517
|
+
return opts?.apiKey ? maskApiKey(opts.apiKey) : "none";
|
|
15105
15518
|
}
|
|
15106
15519
|
getCapability() {
|
|
15107
|
-
|
|
15520
|
+
const { provider, model, entry } = this.selection;
|
|
15521
|
+
return this.capabilityFor(provider, model, entry);
|
|
15108
15522
|
}
|
|
15109
15523
|
async probeImageSupport() {
|
|
15110
|
-
const { provider, model } = this.selection;
|
|
15524
|
+
const { provider, model, entry } = this.selection;
|
|
15111
15525
|
const home = this.config.homeDir;
|
|
15112
15526
|
const cached = readImageProbe(home, provider, model);
|
|
15113
15527
|
if (cached !== void 0) return cached;
|
|
15114
15528
|
let lm;
|
|
15115
15529
|
try {
|
|
15116
|
-
lm = await this.getModel(provider, model);
|
|
15530
|
+
lm = await this.getModel(this.routeOf(provider, entry), model);
|
|
15117
15531
|
} catch {
|
|
15118
15532
|
return false;
|
|
15119
15533
|
}
|
|
@@ -15134,6 +15548,21 @@ var ProviderRouter = class {
|
|
|
15134
15548
|
});
|
|
15135
15549
|
}
|
|
15136
15550
|
};
|
|
15551
|
+
function messageTranslator(messages, table) {
|
|
15552
|
+
const cache = /* @__PURE__ */ new Map();
|
|
15553
|
+
return (type, entry) => {
|
|
15554
|
+
const inline = entryAcceptsToolMedia(table, type, entry);
|
|
15555
|
+
let hit = cache.get(inline);
|
|
15556
|
+
if (!hit) {
|
|
15557
|
+
hit = toModelMessages(messages, { toolMediaInline: inline });
|
|
15558
|
+
cache.set(inline, hit);
|
|
15559
|
+
}
|
|
15560
|
+
return hit;
|
|
15561
|
+
};
|
|
15562
|
+
}
|
|
15563
|
+
function entryTried(id) {
|
|
15564
|
+
return `@${id}`;
|
|
15565
|
+
}
|
|
15137
15566
|
var STATIC_MODEL_CATALOG = {
|
|
15138
15567
|
openai: [
|
|
15139
15568
|
"gpt-4o",
|
|
@@ -15194,6 +15623,9 @@ var PROVIDER_DISCOVERY = {
|
|
|
15194
15623
|
perplexity: { baseUrl: "https://api.perplexity.ai", auth: "bearer" },
|
|
15195
15624
|
togetherai: { baseUrl: "https://api.together.xyz/v1", auth: "bearer" }
|
|
15196
15625
|
};
|
|
15626
|
+
function discoverySpec(provider) {
|
|
15627
|
+
return PROVIDER_DISCOVERY[provider];
|
|
15628
|
+
}
|
|
15197
15629
|
function cachePath(homeDir, provider) {
|
|
15198
15630
|
return join(homeDir, "cache", `models_${provider}.json`);
|
|
15199
15631
|
}
|
|
@@ -15222,14 +15654,14 @@ function saveCache2(homeDir, provider, models) {
|
|
|
15222
15654
|
}
|
|
15223
15655
|
}
|
|
15224
15656
|
function modelListCandidates(baseUrl) {
|
|
15225
|
-
const
|
|
15226
|
-
if (!
|
|
15227
|
-
const alt =
|
|
15228
|
-
return alt ===
|
|
15657
|
+
const base2 = baseUrl.trim().replace(/\/+$/, "");
|
|
15658
|
+
if (!base2) return [];
|
|
15659
|
+
const alt = base2.endsWith("/v1") ? base2.slice(0, -"/v1".length) : `${base2}/v1`;
|
|
15660
|
+
return alt === base2 ? [base2] : [base2, alt];
|
|
15229
15661
|
}
|
|
15230
|
-
function buildRequest(
|
|
15662
|
+
function buildRequest(base2, auth, apiKey) {
|
|
15231
15663
|
const headers = { Accept: "application/json" };
|
|
15232
|
-
let url = `${
|
|
15664
|
+
let url = `${base2}/models`;
|
|
15233
15665
|
if (auth === "x-api-key") {
|
|
15234
15666
|
headers["x-api-key"] = apiKey;
|
|
15235
15667
|
headers["anthropic-version"] = "2023-06-01";
|
|
@@ -15252,9 +15684,9 @@ function parseModelList(payload) {
|
|
|
15252
15684
|
return [...ids, ...names].filter((v) => typeof v === "string" && v.length > 0).map((id) => id.startsWith("models/") ? id.slice("models/".length) : id);
|
|
15253
15685
|
}
|
|
15254
15686
|
async function fetchFromApi(baseUrl, apiKey, auth) {
|
|
15255
|
-
for (const
|
|
15687
|
+
for (const base2 of modelListCandidates(baseUrl)) {
|
|
15256
15688
|
try {
|
|
15257
|
-
const { url, headers } = buildRequest(
|
|
15689
|
+
const { url, headers } = buildRequest(base2, auth, apiKey);
|
|
15258
15690
|
const resp = await fetch(url, { headers, signal: AbortSignal.timeout(1e4) });
|
|
15259
15691
|
if (!resp.ok) continue;
|
|
15260
15692
|
const models = parseModelList(await resp.json());
|
|
@@ -15267,8 +15699,8 @@ async function fetchFromApi(baseUrl, apiKey, auth) {
|
|
|
15267
15699
|
}
|
|
15268
15700
|
async function fetchOllamaModels() {
|
|
15269
15701
|
try {
|
|
15270
|
-
const
|
|
15271
|
-
const resp = await fetch(`${
|
|
15702
|
+
const base2 = process.env.OLLAMA_BASE_URL || "http://localhost:11434";
|
|
15703
|
+
const resp = await fetch(`${base2}/api/tags`, { signal: AbortSignal.timeout(5e3) });
|
|
15272
15704
|
if (!resp.ok) return [];
|
|
15273
15705
|
const data = await resp.json();
|
|
15274
15706
|
return (data.models ?? []).map((m) => m.name).filter((n) => !!n);
|
|
@@ -15284,9 +15716,9 @@ async function discoverModels(homeDir, provider, apiKey, baseUrl) {
|
|
|
15284
15716
|
}
|
|
15285
15717
|
if (apiKey) {
|
|
15286
15718
|
const spec = PROVIDER_DISCOVERY[provider];
|
|
15287
|
-
const
|
|
15288
|
-
if (
|
|
15289
|
-
const models = await fetchFromApi(
|
|
15719
|
+
const base2 = baseUrl?.trim() || spec?.baseUrl || "";
|
|
15720
|
+
if (base2) {
|
|
15721
|
+
const models = await fetchFromApi(base2, apiKey, spec?.auth ?? "bearer");
|
|
15290
15722
|
if (models) {
|
|
15291
15723
|
saveCache2(homeDir, provider, models);
|
|
15292
15724
|
return { models, source: "live" };
|
|
@@ -15304,6 +15736,102 @@ function clearModelCache(homeDir, provider) {
|
|
|
15304
15736
|
} catch {
|
|
15305
15737
|
}
|
|
15306
15738
|
}
|
|
15739
|
+
// src/provider/entry-probe.ts
|
|
15740
|
+
function listTarget(entry) {
|
|
15741
|
+
if (entry.rawUrl) return void 0;
|
|
15742
|
+
if (entry.provider === "ollama") return void 0;
|
|
15743
|
+
const spec = discoverySpec(entry.provider);
|
|
15744
|
+
const base2 = entry.baseUrl?.trim() || spec?.baseUrl;
|
|
15745
|
+
if (!base2) return void 0;
|
|
15746
|
+
const anthropicLike = entry.provider === "anthropic" || entry.provider === "openai-compatible" && entry.protocol === "anthropic-messages";
|
|
15747
|
+
const auth = anthropicLike ? "x-api-key" : spec?.auth ?? "bearer";
|
|
15748
|
+
return { base: base2, auth };
|
|
15749
|
+
}
|
|
15750
|
+
async function listAt(base2, auth, apiKey) {
|
|
15751
|
+
let last = { models: null };
|
|
15752
|
+
for (const candidate of modelListCandidates(base2)) {
|
|
15753
|
+
try {
|
|
15754
|
+
const { url, headers } = buildRequest(candidate, auth, apiKey);
|
|
15755
|
+
const resp = await fetch(url, { headers, signal: AbortSignal.timeout(1e4) });
|
|
15756
|
+
if (!resp.ok) {
|
|
15757
|
+
last = { models: null, httpStatus: resp.status };
|
|
15758
|
+
if (resp.status === 401 || resp.status === 403) return last;
|
|
15759
|
+
continue;
|
|
15760
|
+
}
|
|
15761
|
+
return { models: parseModelList(await resp.json()), httpStatus: resp.status };
|
|
15762
|
+
} catch {
|
|
15763
|
+
last = { models: null, network: true };
|
|
15764
|
+
}
|
|
15765
|
+
}
|
|
15766
|
+
return last;
|
|
15767
|
+
}
|
|
15768
|
+
async function listEntryModels(entry, apiKey) {
|
|
15769
|
+
const target = listTarget(entry);
|
|
15770
|
+
if (!target) return [];
|
|
15771
|
+
const got = await listAt(target.base, target.auth, apiKey ?? "");
|
|
15772
|
+
return got.models ?? [];
|
|
15773
|
+
}
|
|
15774
|
+
function redact(text, apiKey) {
|
|
15775
|
+
if (!apiKey || apiKey.length < 8) return text;
|
|
15776
|
+
return text.split(apiKey).join(maskApiKey(apiKey));
|
|
15777
|
+
}
|
|
15778
|
+
async function testModelEntry(entry, apiKey) {
|
|
15779
|
+
const started = Date.now();
|
|
15780
|
+
const elapsed = () => Date.now() - started;
|
|
15781
|
+
const target = listTarget(entry);
|
|
15782
|
+
let models = [];
|
|
15783
|
+
let listed = false;
|
|
15784
|
+
if (target) {
|
|
15785
|
+
const got = await listAt(target.base, target.auth, apiKey ?? "");
|
|
15786
|
+
listed = got.models !== null;
|
|
15787
|
+
if (got.httpStatus === 401 || got.httpStatus === 403) {
|
|
15788
|
+
return { code: "auth", models: [], httpStatus: got.httpStatus, latencyMs: elapsed() };
|
|
15789
|
+
}
|
|
15790
|
+
if (got.network) return { code: "network", models: [], latencyMs: elapsed() };
|
|
15791
|
+
if (got.models) {
|
|
15792
|
+
models = got.models;
|
|
15793
|
+
if (models.includes(entry.model)) {
|
|
15794
|
+
return { code: "ok", models, ...httpOf(got), latencyMs: elapsed() };
|
|
15795
|
+
}
|
|
15796
|
+
}
|
|
15797
|
+
}
|
|
15798
|
+
if (!entry.model) {
|
|
15799
|
+
return listed ? { code: "ok", models, latencyMs: elapsed() } : { code: "need-model", models: [], latencyMs: elapsed() };
|
|
15800
|
+
}
|
|
15801
|
+
try {
|
|
15802
|
+
const factory = await createEntryFactory(entry, apiKey);
|
|
15803
|
+
await generateText({
|
|
15804
|
+
model: factory(entry.model),
|
|
15805
|
+
messages: [{ role: "user", content: "ping" }],
|
|
15806
|
+
maxOutputTokens: 1,
|
|
15807
|
+
abortSignal: AbortSignal.timeout(2e4)
|
|
15808
|
+
});
|
|
15809
|
+
return { code: "ok", models, latencyMs: elapsed() };
|
|
15810
|
+
} catch (err) {
|
|
15811
|
+
const status = extractStatus(err);
|
|
15812
|
+
const detail = redact(describeProviderError(err), apiKey);
|
|
15813
|
+
return {
|
|
15814
|
+
code: classify(status, detail),
|
|
15815
|
+
models,
|
|
15816
|
+
...status ? { httpStatus: status } : {},
|
|
15817
|
+
latencyMs: elapsed(),
|
|
15818
|
+
detail
|
|
15819
|
+
};
|
|
15820
|
+
}
|
|
15821
|
+
}
|
|
15822
|
+
function httpOf(got) {
|
|
15823
|
+
return got.httpStatus ? { httpStatus: got.httpStatus } : {};
|
|
15824
|
+
}
|
|
15825
|
+
function classify(status, detail) {
|
|
15826
|
+
if (status === 401 || status === 403) return "auth";
|
|
15827
|
+
if (status === 404 || /model[^.]*not.?(found|exist)|no such model|does not exist/i.test(detail)) {
|
|
15828
|
+
return "model-not-found";
|
|
15829
|
+
}
|
|
15830
|
+
if (status === void 0 && /fetch failed|ECONNREFUSED|ENOTFOUND|ETIMEDOUT|timeout|aborted/i.test(detail)) {
|
|
15831
|
+
return "network";
|
|
15832
|
+
}
|
|
15833
|
+
return "error";
|
|
15834
|
+
}
|
|
15307
15835
|
var SETTING_WRITE_LAYERS = ["user", "projectLocal"];
|
|
15308
15836
|
function isSettingWriteLayer(value) {
|
|
15309
15837
|
return SETTING_WRITE_LAYERS.includes(value);
|
|
@@ -15428,6 +15956,25 @@ function writeSettingValue(req) {
|
|
|
15428
15956
|
}
|
|
15429
15957
|
return req.layer === "user" ? writeUserYaml(req.path, req.key, value) : writeSettingsJson(req.path, req.key, value);
|
|
15430
15958
|
}
|
|
15959
|
+
var UNSETTABLE_USER_KEYS = ["models.utility", "fallback.model"];
|
|
15960
|
+
function unsetUserSetting(path, key) {
|
|
15961
|
+
const text = existsSync(path) ? readFileSync(path, "utf-8") : "";
|
|
15962
|
+
const before = asMapping(parseYaml(text));
|
|
15963
|
+
if (before === null) return { ok: false, reason: "unwritable-file" };
|
|
15964
|
+
const dot = key.indexOf(".");
|
|
15965
|
+
const section = key.slice(0, dot);
|
|
15966
|
+
const field = key.slice(dot + 1);
|
|
15967
|
+
const beforeSection = section in before ? asMapping(before[section]) : {};
|
|
15968
|
+
if (beforeSection === null) return { ok: false, reason: "unwritable-file" };
|
|
15969
|
+
if (!(field in beforeSection)) return { ok: true, value: "" };
|
|
15970
|
+
const next = unsetSectionField(text, section, field);
|
|
15971
|
+
const after = asMapping(parseYaml(next));
|
|
15972
|
+
if (after === null) return { ok: false, reason: "unwritable-file" };
|
|
15973
|
+
const afterSection = section in after ? asMapping(after[section]) : {};
|
|
15974
|
+
const intact = afterSection !== null && !(field in afterSection) && sameKeysAndValues(before, after, section) && sameKeysAndValues(beforeSection, afterSection, field);
|
|
15975
|
+
if (!intact) return { ok: false, reason: "unwritable-file" };
|
|
15976
|
+
return commit(path, next, 384, "");
|
|
15977
|
+
}
|
|
15431
15978
|
function writeUserYaml(path, key, value) {
|
|
15432
15979
|
const text = existsSync(path) ? readFileSync(path, "utf-8") : "";
|
|
15433
15980
|
const before = asMapping(parseYaml(text));
|
|
@@ -18818,7 +19365,7 @@ var GoalService = class {
|
|
|
18818
19365
|
}
|
|
18819
19366
|
};
|
|
18820
19367
|
// src/goal/provider.ts
|
|
18821
|
-
function withGoalPrompt(
|
|
19368
|
+
function withGoalPrompt(base2, view) {
|
|
18822
19369
|
const decorate = (input) => {
|
|
18823
19370
|
const now = view();
|
|
18824
19371
|
if (!now) return input;
|
|
@@ -18827,9 +19374,9 @@ function withGoalPrompt(base, view) {
|
|
|
18827
19374
|
|
|
18828
19375
|
${block}` } : input;
|
|
18829
19376
|
};
|
|
18830
|
-
const wrapped = Object.create(
|
|
18831
|
-
wrapped.generate = (input) =>
|
|
18832
|
-
wrapped.stream = (input) =>
|
|
19377
|
+
const wrapped = Object.create(base2);
|
|
19378
|
+
wrapped.generate = (input) => base2.generate(decorate(input));
|
|
19379
|
+
wrapped.stream = (input) => base2.stream(decorate(input));
|
|
18833
19380
|
return wrapped;
|
|
18834
19381
|
}
|
|
18835
19382
|
// src/goal/engine.ts
|
|
@@ -19105,6 +19652,7 @@ function isLastOpenSession(value) {
|
|
|
19105
19652
|
const v = value;
|
|
19106
19653
|
return typeof v["root"] === "string" && v["root"].length > 0 && typeof v["sessionId"] === "string" && v["sessionId"].length > 0 && typeof v["at"] === "number";
|
|
19107
19654
|
}
|
|
19655
|
+
var isWidthShaped = (value) => typeof value === "number" && Number.isInteger(value) && value > 0;
|
|
19108
19656
|
var FILE_VERSION4 = 1;
|
|
19109
19657
|
function describe9(err) {
|
|
19110
19658
|
return err instanceof Error ? err.message : String(err);
|
|
@@ -19127,7 +19675,10 @@ var AppearanceStore = class {
|
|
|
19127
19675
|
const value = patch[key];
|
|
19128
19676
|
if (typeof value === "string" && value.length > 0) next[key] = value;
|
|
19129
19677
|
}
|
|
19130
|
-
if (
|
|
19678
|
+
if (isWidthShaped(patch.sidebarWidth)) next.sidebarWidth = patch.sidebarWidth;
|
|
19679
|
+
if (next.theme === this.prefs.theme && next.lang === this.prefs.lang && next.sidebarWidth === this.prefs.sidebarWidth) {
|
|
19680
|
+
return;
|
|
19681
|
+
}
|
|
19131
19682
|
this.prefs = next;
|
|
19132
19683
|
this.save();
|
|
19133
19684
|
}
|
|
@@ -19151,6 +19702,7 @@ var AppearanceStore = class {
|
|
|
19151
19702
|
if (file.version !== FILE_VERSION4) return;
|
|
19152
19703
|
if (typeof file.theme === "string" && file.theme.length > 0) this.prefs.theme = file.theme;
|
|
19153
19704
|
if (typeof file.lang === "string" && file.lang.length > 0) this.prefs.lang = file.lang;
|
|
19705
|
+
if (isWidthShaped(file.sidebarWidth)) this.prefs.sidebarWidth = file.sidebarWidth;
|
|
19154
19706
|
}
|
|
19155
19707
|
save() {
|
|
19156
19708
|
const body2 = JSON.stringify(
|
|
@@ -19333,9 +19885,9 @@ function matchPreauthorization(policy, target) {
|
|
|
19333
19885
|
}
|
|
19334
19886
|
return null;
|
|
19335
19887
|
}
|
|
19336
|
-
function mergeHeadlessPolicy(
|
|
19888
|
+
function mergeHeadlessPolicy(base2, override) {
|
|
19337
19889
|
return {
|
|
19338
|
-
...
|
|
19890
|
+
...base2,
|
|
19339
19891
|
...override?.allowTools ? { allowTools: override.allowTools } : {},
|
|
19340
19892
|
...override?.allowOperations ? { allowOperations: override.allowOperations } : {}
|
|
19341
19893
|
};
|
|
@@ -21091,14 +21643,14 @@ var TextGuard = class {
|
|
|
21091
21643
|
}
|
|
21092
21644
|
consume(unit) {
|
|
21093
21645
|
const hits = this.host.scan(this.host.skipCodeBlocks ? this.blankCode(unit) : unit);
|
|
21094
|
-
const
|
|
21646
|
+
const base2 = this.consumed;
|
|
21095
21647
|
this.consumed += unit.length;
|
|
21096
21648
|
if (hits.length === 0) {
|
|
21097
21649
|
this.out += unit;
|
|
21098
21650
|
return unit;
|
|
21099
21651
|
}
|
|
21100
21652
|
for (const hit of hits) {
|
|
21101
|
-
this.hits.push({ ...hit, start: hit.start +
|
|
21653
|
+
this.hits.push({ ...hit, start: hit.start + base2, end: hit.end + base2 });
|
|
21102
21654
|
}
|
|
21103
21655
|
const action = strongestOf(hits);
|
|
21104
21656
|
if (action === "block") {
|
|
@@ -21723,7 +22275,7 @@ function loadLexiconDir(dir, source) {
|
|
|
21723
22275
|
}
|
|
21724
22276
|
const out = { rules: [], exempt: [], issues: [] };
|
|
21725
22277
|
for (const file of files.sort()) {
|
|
21726
|
-
const spec =
|
|
22278
|
+
const spec = classify2(file);
|
|
21727
22279
|
if (!spec) {
|
|
21728
22280
|
out.issues.push({
|
|
21729
22281
|
path: file,
|
|
@@ -21747,14 +22299,14 @@ function loadLexiconDir(dir, source) {
|
|
|
21747
22299
|
}
|
|
21748
22300
|
return out;
|
|
21749
22301
|
}
|
|
21750
|
-
function
|
|
21751
|
-
const
|
|
21752
|
-
if (
|
|
21753
|
-
if (
|
|
21754
|
-
const category =
|
|
22302
|
+
function classify2(file) {
|
|
22303
|
+
const base2 = file.slice(0, -".txt".length);
|
|
22304
|
+
if (base2 === "exempt") return { kind: "exempt" };
|
|
22305
|
+
if (base2.endsWith(".regex")) {
|
|
22306
|
+
const category = base2.slice(0, -".regex".length);
|
|
21755
22307
|
return CATEGORY_SET.has(category) ? { kind: "regex", category } : null;
|
|
21756
22308
|
}
|
|
21757
|
-
return CATEGORY_SET.has(
|
|
22309
|
+
return CATEGORY_SET.has(base2) ? { kind: "terms", category: base2 } : null;
|
|
21758
22310
|
}
|
|
21759
22311
|
function collect3(content, file, spec, source, out) {
|
|
21760
22312
|
const lines = content.split("\n");
|
|
@@ -22853,8 +23405,8 @@ function copyTree(from, to, label, budget, issues, lang) {
|
|
|
22853
23405
|
return left;
|
|
22854
23406
|
}
|
|
22855
23407
|
function defaultName(source) {
|
|
22856
|
-
const
|
|
22857
|
-
return extname(
|
|
23408
|
+
const base2 = basename(source);
|
|
23409
|
+
return extname(base2).toLowerCase() === ".md" ? base2.slice(0, -3) : base2;
|
|
22858
23410
|
}
|
|
22859
23411
|
function skillSegmentIssue(segment, lang) {
|
|
22860
23412
|
if (segment.length === 0) return t("skill.import_bad_name_empty", void 0, lang);
|
|
@@ -23037,8 +23589,8 @@ function loadOne(mdFile, catPath, category, dirName, spec, skills, issues) {
|
|
|
23037
23589
|
const parsed = parseSkillFrontmatter(raw, label);
|
|
23038
23590
|
issues.push(...parsed.issues);
|
|
23039
23591
|
const declared = parsed.fm.name?.trim();
|
|
23040
|
-
const
|
|
23041
|
-
const name = spec.prefix ? `${spec.prefix}:${
|
|
23592
|
+
const base2 = declared ? declared : dirName;
|
|
23593
|
+
const name = spec.prefix ? `${spec.prefix}:${base2}` : base2;
|
|
23042
23594
|
const previous = skills.get(name);
|
|
23043
23595
|
if (previous && previous.scope !== scope) {
|
|
23044
23596
|
issues.push({
|
|
@@ -23195,9 +23747,9 @@ var SkillSystem = class {
|
|
|
23195
23747
|
const indexName = declared ? declared : name;
|
|
23196
23748
|
if (this.skills.has(indexName)) throw new Error(`Skill "${indexName}" already exists`);
|
|
23197
23749
|
const dir = join(this.skillsDir, category, indexName);
|
|
23198
|
-
const
|
|
23750
|
+
const base2 = resolve(this.skillsDir);
|
|
23199
23751
|
const resolved = resolve(dir);
|
|
23200
|
-
if (resolved ===
|
|
23752
|
+
if (resolved === base2 || !resolved.startsWith(base2 + sep)) {
|
|
23201
23753
|
throw new Error(
|
|
23202
23754
|
`Skill name or category escapes the skills directory: ${category}/${indexName}`
|
|
23203
23755
|
);
|
|
@@ -23246,12 +23798,12 @@ var SkillSystem = class {
|
|
|
23246
23798
|
this.invalidateCache();
|
|
23247
23799
|
}
|
|
23248
23800
|
resolveInsideSkill(skillPath, relPath) {
|
|
23249
|
-
const
|
|
23801
|
+
const base2 = resolve(skillPath);
|
|
23250
23802
|
const resolved = resolve(join(skillPath, relPath));
|
|
23251
|
-
if (resolved !==
|
|
23803
|
+
if (resolved !== base2 && !resolved.startsWith(base2 + sep)) {
|
|
23252
23804
|
throw new Error("Path traversal denied");
|
|
23253
23805
|
}
|
|
23254
|
-
const realBase = realpathSync(
|
|
23806
|
+
const realBase = realpathSync(base2);
|
|
23255
23807
|
const realTarget = realpathOfDeepestExisting(resolved);
|
|
23256
23808
|
if (realTarget !== realBase && !realTarget.startsWith(realBase + sep)) {
|
|
23257
23809
|
throw new Error("Path traversal denied");
|
|
@@ -24275,7 +24827,7 @@ var ContextCompressor = class {
|
|
|
24275
24827
|
}
|
|
24276
24828
|
async generateSummary(middleText, instruction, plan, goal) {
|
|
24277
24829
|
try {
|
|
24278
|
-
const
|
|
24830
|
+
const base2 = this.lastSummary ? `Update the following summary with new conversation content. Keep the same structure.
|
|
24279
24831
|
|
|
24280
24832
|
## Previous Summary
|
|
24281
24833
|
${this.lastSummary}
|
|
@@ -24287,9 +24839,9 @@ ${SUMMARY_TEMPLATE}
|
|
|
24287
24839
|
|
|
24288
24840
|
## Conversation
|
|
24289
24841
|
${middleText}`;
|
|
24290
|
-
const withPlan = plan?.trim() ? `${
|
|
24842
|
+
const withPlan = plan?.trim() ? `${base2}
|
|
24291
24843
|
|
|
24292
|
-
${planSection(plan.trim())}` :
|
|
24844
|
+
${planSection(plan.trim())}` : base2;
|
|
24293
24845
|
const withGoal = goal?.trim() ? `${withPlan}
|
|
24294
24846
|
|
|
24295
24847
|
${goalSection(goal.trim())}` : withPlan;
|
|
@@ -24359,10 +24911,10 @@ var SCOPES = ["directory", "directory-tree"];
|
|
|
24359
24911
|
function describe14(err) {
|
|
24360
24912
|
return err instanceof Error ? err.message : String(err);
|
|
24361
24913
|
}
|
|
24362
|
-
function covers(
|
|
24363
|
-
if (samePath(
|
|
24914
|
+
function covers(base2, target, scope) {
|
|
24915
|
+
if (samePath(base2, target)) return true;
|
|
24364
24916
|
if (scope === "directory") return false;
|
|
24365
|
-
const rel = relative(
|
|
24917
|
+
const rel = relative(base2, target);
|
|
24366
24918
|
return !rel.startsWith("..") && !isAbsolute(rel);
|
|
24367
24919
|
}
|
|
24368
24920
|
var TrustManager = class {
|
|
@@ -24378,10 +24930,10 @@ var TrustManager = class {
|
|
|
24378
24930
|
let bestLen = -1;
|
|
24379
24931
|
let best;
|
|
24380
24932
|
for (const record of this.records) {
|
|
24381
|
-
const
|
|
24382
|
-
if (!covers(
|
|
24383
|
-
if (
|
|
24384
|
-
bestLen =
|
|
24933
|
+
const base2 = resolve(record.path);
|
|
24934
|
+
if (!covers(base2, target, record.scope)) continue;
|
|
24935
|
+
if (base2.length > bestLen || base2.length === bestLen && record.level === "untrusted") {
|
|
24936
|
+
bestLen = base2.length;
|
|
24385
24937
|
best = record;
|
|
24386
24938
|
}
|
|
24387
24939
|
}
|
|
@@ -24877,4 +25429,4 @@ function trunc(stdout, stderr) {
|
|
|
24877
25429
|
* `--tree` 的命令帮助里,让用户自己知道 `--tree` 意味着什么。
|
|
24878
25430
|
*/
|
|
24879
25431
|
|
|
24880
|
-
export { ABOUT_DESC_MAX_CHARS, ABOUT_README_MAX_CHARS, AGENT_ROLE_DIAG_MODULE, ARTIFACT_MAX_TOTAL_BYTES, ARTIFACT_RETENTION_DEFAULTS, ATTACHMENT_MAX_TOTAL_BYTES, ATTACHMENT_RETENTION_DEFAULTS, AgentLoop, AppearanceStore, ApprovalCache, BLOCKED_TOOLS, BudgetGuard, BudgetStore, CHECKPOINT_MAX_TOTAL_BYTES, CLAUDE_EVENT_NAMES, CLAUDE_TOOL_NAMES, CODE_MODE_LIMITS, CODE_MODE_SDK_TAG, CONFIG_KEYS, CURRENT_CONFIG_VERSION, CheckpointManager, CheckpointStore, CodeSandbox, ComplianceEngine, ContextCompressor, DEFAULT_COMPLIANCE_ACTIONS, DEFAULT_COMPLIANCE_SETTINGS, DEFAULT_COMPRESSION_THRESHOLD, DEFAULT_GOAL_MAX_ROUNDS, DEFAULT_MAX_HOLD_CHARS, DelegateManager, EMPTY_RULES, EPOCH_DISK_LEDGER, EpochConfigSchema, FTS_TABLES, FTS_TRIGGERS, GOAL_BLOCK_CODES, GoalService, GoalStore, HEADLESS_AUDIT_CODES, HookManager, INSTRUCTION_BUDGET_CHARS, INSTRUCTION_FILES, ImageLoadError, ImportTrustStore, LAUNCHD_LABEL_PREFIX, LOCK_HEARTBEAT_MS, LOCK_STALE_MS, LastOpenSessions, MARKETPLACE_DISPLAY_FIELDS, MARKETPLACE_FILE, MAX_AUDIT_ENTRIES, MAX_COMMAND_DEPTH, MAX_GOAL_MAX_ROUNDS, MAX_IMPORT_DEPTH, MAX_LAST_OPEN_SESSIONS, MAX_OBJECTIVE_CHARS, MAX_REASON_CHARS, MAX_RECENT_WORKSPACES, MODEL_GIVE_UP_THRESHOLD, MULTIMODAL_DEFAULTS, ManagedPolicyError, ManagedSettingsSchema, MemoryManager, ModelFailureTracker, NOMINAL_TOTAL_BYTES, NO_REVEALED_TOOLS, PACK_EXCLUDES, PACK_MAX_BYTES, PACK_MAX_FILES, PERMISSION_AUDIT_CODES, PLUGIN_ICON_MAX_BYTES, PLUGIN_LAYOUT, PLUGIN_MANIFEST_FILE, PLUGIN_MANIFEST_KEYS, PLUGIN_NAME_PATTERN, PLUGIN_ROOT_ENV, PLUGIN_ROOT_TOKEN, PLUGIN_VISIBILITY, POLICY_DECISIONS, PROBE_VERSION, PROJECT_AGENTS_SUBDIR, PROVIDER_NO_CREDENTIAL, PermissionManager, PlanModeState, PluginAuthorSchema, PluginStore, PolicyRuleSchema, ProviderError, ProviderRouter, RECORDING_KEEP_RUNS, RECORDING_MAX_BYTES, ROLE_FRONTMATTER_SCHEMA, RUN_CODE_TOOL, RecentWorkspaces, SANDBOX_COVERS, SANDBOX_EXCLUDES, SCALAR_KEYS, SCHEDULE_DEFAULTS, SCHEDULE_DEFAULT_ALLOWLIST, SCHEDULE_FORM_BUDGET_USD, SCHEDULE_ISSUE_CODES, SCHTASKS_FOLDER, SESSION_MESSAGES_READ_TOOL, SESSION_MESSAGES_SEARCH_TOOL, SESSION_QUERY_TOOLS, SESSION_SEARCH_TOOL, SESSION_TRACE_TOOL, SETTINGS_LAYER_ORDER, SETTING_WRITE_KEYS, SETTING_WRITE_LAYERS, SKILL_ASSET_DIRS, SKILL_CREATE_TOOL, SKILL_EDIT_TOOL, SKILL_FILE, SKILL_PATCH_TOOL, SKILL_VIEW_TOOL, SKILL_WRITE_FILE_TOOL, SLASH_COMMANDS_DIAG_MODULE, ScheduleRecorder, ScheduleRegistrar, ScheduleStore, SessionManager, SessionQueries, SessionReferences, SettingsFileError, SettingsFileSchema, SkillLearner, SkillSystem, TOOL_SEARCH_TOOL, TaskNoticeTracker, ToolRegistry, TrackerDB, TrustManager, Workspace, acquireScheduleLock, addMarketplace, addUsage, admitArtifact, admitArtifacts, affectedPaths, approvalEntryFor, approvalRootFor, artifactLaunchArgv, artifactOpenRefusal, assertLevelAllowed, blockedRoleTools, buildContextBreakdown, buildFireArgv, buildFtsMatchQuery, buildSchemaExports, buildSystemPrompt, buildTrigramMatchQuery, builtinRoles, checkEpochVersion, checkFtsHealth, checkShadowing, classifyPersistFailure, classifyProviderError, clearModelCache, coalesceRecording, codeModeWorkerPath, commandNameFromRelPath, compileRuleLists, compressionOptions, computeCost, computeCostMicros, computeExecHash, countRules, createAllowAllGate, createAllowAllImportGate, createAskQuestionTool, createCodeExecTool, createDelegateTool, createDenyAllGate, createDenyAllImportGate, createGoalTool, createImportGate, createMemoryTool, createPlanModeTools, createRoleFile, createRoleScope, createRunCodeTool, createScheduleDraftTool, createSessionMessagesReadTool, createSessionMessagesSearchTool, createSessionSearchTool, createSessionSearchTools, createSessionTraceTool, createSkillViewTool, createSkillWriteTools, createTodoTool, createToolRevealState, createToolScope, createToolSearchTool, createTrustGate, currentEpochVersion, decideImport, decideTrust, defaultConfig,
|
|
25432
|
+
export { ABOUT_DESC_MAX_CHARS, ABOUT_README_MAX_CHARS, AGENT_ROLE_DIAG_MODULE, ARTIFACT_MAX_TOTAL_BYTES, ARTIFACT_RETENTION_DEFAULTS, ATTACHMENT_MAX_TOTAL_BYTES, ATTACHMENT_RETENTION_DEFAULTS, AgentLoop, AppearanceStore, ApprovalCache, BLOCKED_TOOLS, BudgetGuard, BudgetStore, CHECKPOINT_MAX_TOTAL_BYTES, CLAUDE_EVENT_NAMES, CLAUDE_TOOL_NAMES, CODE_MODE_LIMITS, CODE_MODE_SDK_TAG, CONFIG_KEYS, CURRENT_CONFIG_VERSION, CheckpointManager, CheckpointStore, CodeSandbox, ComplianceEngine, ContextCompressor, DEFAULT_COMPLIANCE_ACTIONS, DEFAULT_COMPLIANCE_SETTINGS, DEFAULT_COMPRESSION_THRESHOLD, DEFAULT_GOAL_MAX_ROUNDS, DEFAULT_MAX_HOLD_CHARS, DelegateManager, EMPTY_RULES, EPOCH_DISK_LEDGER, EpochConfigSchema, FTS_TABLES, FTS_TRIGGERS, GOAL_BLOCK_CODES, GoalService, GoalStore, HEADLESS_AUDIT_CODES, HookManager, INSTRUCTION_BUDGET_CHARS, INSTRUCTION_FILES, ImageLoadError, ImportTrustStore, LAUNCHD_LABEL_PREFIX, LOCK_HEARTBEAT_MS, LOCK_STALE_MS, LastOpenSessions, MARKETPLACE_DISPLAY_FIELDS, MARKETPLACE_FILE, MAX_AUDIT_ENTRIES, MAX_COMMAND_DEPTH, MAX_GOAL_MAX_ROUNDS, MAX_IMPORT_DEPTH, MAX_LAST_OPEN_SESSIONS, MAX_OBJECTIVE_CHARS, MAX_REASON_CHARS, MAX_RECENT_WORKSPACES, MODEL_ENTRIES_FILE, MODEL_GIVE_UP_THRESHOLD, MULTIMODAL_DEFAULTS, ManagedPolicyError, ManagedSettingsSchema, MemoryManager, ModelFailureTracker, NOMINAL_TOTAL_BYTES, NO_REVEALED_TOOLS, PACK_EXCLUDES, PACK_MAX_BYTES, PACK_MAX_FILES, PERMISSION_AUDIT_CODES, PLUGIN_ICON_MAX_BYTES, PLUGIN_LAYOUT, PLUGIN_MANIFEST_FILE, PLUGIN_MANIFEST_KEYS, PLUGIN_NAME_PATTERN, PLUGIN_ROOT_ENV, PLUGIN_ROOT_TOKEN, PLUGIN_VISIBILITY, POLICY_DECISIONS, PROBE_VERSION, PROJECT_AGENTS_SUBDIR, PROVIDER_NO_CREDENTIAL, PermissionManager, PlanModeState, PluginAuthorSchema, PluginStore, PolicyRuleSchema, ProviderError, ProviderRouter, RECORDING_KEEP_RUNS, RECORDING_MAX_BYTES, ROLE_FRONTMATTER_SCHEMA, RUN_CODE_TOOL, RecentWorkspaces, SANDBOX_COVERS, SANDBOX_EXCLUDES, SCALAR_KEYS, SCHEDULE_DEFAULTS, SCHEDULE_DEFAULT_ALLOWLIST, SCHEDULE_FORM_BUDGET_USD, SCHEDULE_ISSUE_CODES, SCHTASKS_FOLDER, SESSION_MESSAGES_READ_TOOL, SESSION_MESSAGES_SEARCH_TOOL, SESSION_QUERY_TOOLS, SESSION_SEARCH_TOOL, SESSION_TRACE_TOOL, SETTINGS_LAYER_ORDER, SETTING_WRITE_KEYS, SETTING_WRITE_LAYERS, SKILL_ASSET_DIRS, SKILL_CREATE_TOOL, SKILL_EDIT_TOOL, SKILL_FILE, SKILL_PATCH_TOOL, SKILL_VIEW_TOOL, SKILL_WRITE_FILE_TOOL, SLASH_COMMANDS_DIAG_MODULE, ScheduleRecorder, ScheduleRegistrar, ScheduleStore, SessionManager, SessionQueries, SessionReferences, SettingsFileError, SettingsFileSchema, SkillLearner, SkillSystem, TOOL_SEARCH_TOOL, TaskNoticeTracker, ToolRegistry, TrackerDB, TrustManager, UNSETTABLE_USER_KEYS, Workspace, acquireScheduleLock, addMarketplace, addUsage, admitArtifact, admitArtifacts, affectedPaths, approvalEntryFor, approvalRootFor, artifactLaunchArgv, artifactOpenRefusal, assertLevelAllowed, blockedRoleTools, buildContextBreakdown, buildFireArgv, buildFtsMatchQuery, buildSchemaExports, buildSystemPrompt, buildTrigramMatchQuery, builtinRoles, capabilityOfRef, checkEpochVersion, checkFtsHealth, checkShadowing, classifyPersistFailure, classifyProviderError, clearModelCache, coalesceRecording, codeModeWorkerPath, commandNameFromRelPath, compileRuleLists, compressionOptions, computeCost, computeCostMicros, computeExecHash, countRules, createAllowAllGate, createAllowAllImportGate, createAskQuestionTool, createCodeExecTool, createDelegateTool, createDenyAllGate, createDenyAllImportGate, createGoalTool, createImportGate, createMemoryTool, createPlanModeTools, createRoleFile, createRoleScope, createRunCodeTool, createScheduleDraftTool, createSessionMessagesReadTool, createSessionMessagesSearchTool, createSessionSearchTool, createSessionSearchTools, createSessionTraceTool, createSkillViewTool, createSkillWriteTools, createTodoTool, createToolRevealState, createToolScope, createToolSearchTool, createTrustGate, currentEpochVersion, decideImport, decideTrust, defaultConfig, describeArtifactRefusal, describeBackgroundTask, describeHeadlessPolicy, describeIsolation, describeJsonError, diagnoseSchedules, discoverMarkdown, discoverModels, discoverProject, discoverySpec, displayAllows, displayCurrency, draftCommandLine, draftWidening, escapeRuleContent, estimateImageTokens, estimateRunTokens, estimateTokens, evaluateSelection, expandCommand, expandImports, explainSetting, extractArchive, findInstructionFiles, findProjectInstructions, findRole, formatBytes, formatSkillIndex, formatUsd, gateForTool, getCapability, getConfigIssues, getConfigOrigins, getImportGate, getTrustGate, grantedPathsFor, hasDedicatedExecContent, hasExecTierContent, hasPromptCaching, hasProviderCredentials, hasReadTierContent, hasUsableModel, imageFromBase64, imageFromPath, imagePartTokens, importParts, importSkills, installPlugin, instructionDirs, instructionNotices, intervalSlots, inventoryProjectExtensions, inventoryTotal, isBlockCode, isCodeModeDeferred, isEmptyRules, isGoalPhase, isNonInteractive, isReadOnlySubCall, isSettingWriteLayer, isTransientPersistFailure, isValidInterval, isValidPluginName, isWithinWindow, isWritableSettingKey, labelSlug, layerRank, listEntryModels, listRunningSchedules, listSessionCandidates, listWorkspaceFiles, loadCommandDefinitions, loadCommandFile, loadCommands, loadConfig, loadHookSources, loadKeybindings, loadLexiconDir, loadLexiconFiles, loadModelEntries, loadPlugins, loadPolicyFiles, loadPolicyRules, loadRoleDefinitions, loadRoleDir, loadedImports, localDateString, managedSwitchNotes, mapClaudeToolName, mapEpochToolName, mapOperationType, matchPreauthorization, matchRuleLists, matchesHook, mentionsPluginRoot, mergeHeadlessPolicy, mergeRoles, mergeSchedulePatch, microsToUsd, modelEntriesPath, modelListCandidates, needsAllowlist, nextRunAt, noConfigOrigins, noManagedPolicy, noPlugins, normalizeModelEntry, normalizeTrigger, openArtifact, originOf, packFileName, packPlugin, parseClaudeSettingsHooks, parseClock, parseDate, parseFrontmatter, parseHooksConfig, parseHooksConfigVerbose, parseImportLine, parseModelEntry, parseModelList, parseRoleDefinition, parseScheduleDraft, parseSkillFrontmatter, parseTrigger, pendingImageTokens, persistFailureCode, persistParts, persistToolResults, persistedUsage, planPack, previewSkillImport, pricingFor, projectEntryForDisplay, pruneArtifacts, pruneAttachments, pruneRecordings, pruneSchedules, readEntryKey, readImageSize, readMarketplaces, readPluginAbout, readPluginIcon, readPluginManifest, readPluginRecords, readRecording, readSessionSurface, reclaimFtsSpace, redactLine, refreshOpenRouterMetadata, removeMarketplace, removeRecordings, removeRoleFile, removeSessionArtifacts, removeSessionAttachments, renderImportTree, renderProjectContext, renderSdkDeclaration, renderSelfIndex, repairSchedules, resolveArtifactRetention, resolveCodeModeWorker, resolveComplianceDirs, resolveEntrySource, resolveInWorkspace, resolveMarketplaceRef, resolveMentions, resolveMultimodalPolicy, resolvePolicyDirs, resolvePricingAt, resolveProjectRoot, resolveScheduleBackend, resolveSessionReference, resolveSessionVisibility, resolveSettings, resolveSkillDirs, resolveSource, resolveTar, resolveWorkspace, restoreParts, revealScopedProvider, roleScopedProvider, roleSourceLabel, ruleCovers, ruleFromString, ruleToString, runCode, runCommand2 as runCommand, sanitizeArgNames, sanitizeCommand, sanitizeFtsQuery, sanitizePath, sanitizeToolOutput, saveModelEntries, scanInstructions, scanPluginDir, scanSkillDirs, searchMarketplaces, setDisplayCurrency, setImportGate, setPluginEnabled, setTrustGate, settingValueChoices, settingValueKind, settingWriteApply, settingWriteLayers, settingWriteTargets, skillIndexDescription, skillIndexResidency, skillIndexStats, sniffMediaType, splitFrontmatter, staticToolProvider, stripArtifactData, substitutePluginRoot, suggestRuleFromApproval, systemOpenArgv, systemPromptSegments, testModelEntry, toPosix, toolDefinitionTokens, toolMatches, toolTableTokens, totalTokens, traceSession, triggerPeriodMs, unescapeRuleContent, uninstallPlugin, unknownRoleSkills, unknownRoleTools, unloadedInstructionFiles, unsetUserSetting, updateMarketplace, updatePlugin, usdToMicros, validateSchedule, validateTrigger, validateWindow, withGoalCompression, withGoalPrompt, workspaceDisplayPath, writeSettingValue };
|