@epoch-agent/core 1.1.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 +1501 -1157
- package/dist/index.js +1109 -563
- 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, BRAND_WHEN_SIDEBAR_COLLAPSED, 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({}),
|
|
@@ -13187,13 +13372,15 @@ var EpochConfigSchema = z.object({
|
|
|
13187
13372
|
display: DisplaySchema2.optional(),
|
|
13188
13373
|
brand: z.string().min(1).optional(),
|
|
13189
13374
|
brandWhenSidebarCollapsed: z.enum(BRAND_WHEN_SIDEBAR_COLLAPSED).optional(),
|
|
13375
|
+
brandMenuId: z.string().min(1).optional(),
|
|
13190
13376
|
sidebarMenu: z.array(SidebarNavItemSchema).min(1).refine((items) => new Set(items.map((item) => item.id)).size === items.length, {
|
|
13191
13377
|
message: "sidebarMenu: id must be unique"
|
|
13192
13378
|
}).refine((items) => items.filter((item) => item.primary === true).length <= 1, {
|
|
13193
13379
|
message: "sidebarMenu: at most one item can be primary"
|
|
13194
13380
|
}).optional(),
|
|
13195
|
-
|
|
13196
|
-
|
|
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"
|
|
13197
13384
|
}).optional(),
|
|
13198
13385
|
blankCards: z.array(BlankCardSchema).min(1).refine((items) => new Set(items.map((item) => item.id)).size === items.length, {
|
|
13199
13386
|
message: "blankCards: id must be unique"
|
|
@@ -13277,7 +13464,7 @@ function describeJsonError(err, text) {
|
|
|
13277
13464
|
const reason9 = message.replace(/\s*in JSON at position \d+.*$/, "");
|
|
13278
13465
|
return t("config_load.json_at", { reason: reason9, line, column });
|
|
13279
13466
|
}
|
|
13280
|
-
function readSettingsFile(path, schema, known, empty, issues) {
|
|
13467
|
+
function readSettingsFile(path, schema, known, empty, issues, sanitize = (raw) => raw) {
|
|
13281
13468
|
if (!existsSync(path)) return void 0;
|
|
13282
13469
|
const text = readFileSync(path, "utf-8");
|
|
13283
13470
|
let raw;
|
|
@@ -13288,11 +13475,27 @@ function readSettingsFile(path, schema, known, empty, issues) {
|
|
|
13288
13475
|
issues.push({ path, message: t("config_load.json_invalid", { reason: reason9 }) });
|
|
13289
13476
|
return { failed: reason9 };
|
|
13290
13477
|
}
|
|
13291
|
-
|
|
13292
|
-
|
|
13478
|
+
const cleaned = sanitize(raw, issues);
|
|
13479
|
+
issues.push(...atFile(path, unknownKeyIssues(known, cleaned)));
|
|
13480
|
+
const parsed = parseLenient(schema, cleaned, empty);
|
|
13293
13481
|
issues.push(...atFile(path, parsed.issues));
|
|
13294
13482
|
return { file: parsed.value };
|
|
13295
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
|
+
}
|
|
13296
13499
|
// src/config/managed.ts
|
|
13297
13500
|
var ManagedSettingsSchema = SettingsFileSchema.extend({
|
|
13298
13501
|
allowManagedPermissionRulesOnly: z.boolean().optional(),
|
|
@@ -13318,7 +13521,8 @@ function readManagedSettings(path, issues) {
|
|
|
13318
13521
|
ManagedSettingsSchema,
|
|
13319
13522
|
MANAGED_KEYS,
|
|
13320
13523
|
{},
|
|
13321
|
-
issues
|
|
13524
|
+
issues,
|
|
13525
|
+
stripLegacyModelValue
|
|
13322
13526
|
);
|
|
13323
13527
|
if (!read || "failed" in read) return { policy: noManagedPolicy(path), read };
|
|
13324
13528
|
return { policy: policyOf(path, read.file), read };
|
|
@@ -13378,11 +13582,11 @@ function countRules(lists) {
|
|
|
13378
13582
|
if (!lists) return 0;
|
|
13379
13583
|
return BUCKETS2.reduce((sum, b) => sum + (lists[b]?.length ?? 0), 0);
|
|
13380
13584
|
}
|
|
13381
|
-
function mergeLists(
|
|
13585
|
+
function mergeLists(base2, add) {
|
|
13382
13586
|
const out = {};
|
|
13383
13587
|
for (const bucket of BUCKETS2) {
|
|
13384
|
-
const merged = [...
|
|
13385
|
-
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;
|
|
13386
13590
|
out[bucket] = [...new Set(merged)];
|
|
13387
13591
|
}
|
|
13388
13592
|
return out;
|
|
@@ -13413,7 +13617,8 @@ function loadLayer(source, execTrusted, issues) {
|
|
|
13413
13617
|
SettingsFileSchema,
|
|
13414
13618
|
SETTINGS_KEYS,
|
|
13415
13619
|
{},
|
|
13416
|
-
issues
|
|
13620
|
+
issues,
|
|
13621
|
+
stripLegacyModelValue
|
|
13417
13622
|
);
|
|
13418
13623
|
return finishLayer(source, read, execTrusted, issues);
|
|
13419
13624
|
}
|
|
@@ -13456,8 +13661,8 @@ function describe6(layer, gated, lists) {
|
|
|
13456
13661
|
const parts = BUCKETS2.map((b) => `${b} ${lists[b]?.length ?? 0}`).join(" / ");
|
|
13457
13662
|
return t("config_load.loaded", { label, parts });
|
|
13458
13663
|
}
|
|
13459
|
-
function resolveSettings(
|
|
13460
|
-
const config = structuredClone(
|
|
13664
|
+
function resolveSettings(base2, projectRoot, execTrusted, opts = {}) {
|
|
13665
|
+
const config = structuredClone(base2);
|
|
13461
13666
|
const issues = [];
|
|
13462
13667
|
const layers = [];
|
|
13463
13668
|
let lists = {};
|
|
@@ -13486,12 +13691,19 @@ function resolveSettings(base, projectRoot, execTrusted, opts = {}) {
|
|
|
13486
13691
|
lists = managed.policy.allowManagedPermissionRulesOnly ? managed.outcome.lists : mergeLists(lists, managed.outcome.lists);
|
|
13487
13692
|
layers.push({ ...managed.outcome, scalars: applyScalars(config, managed.file) });
|
|
13488
13693
|
}
|
|
13489
|
-
if (countRules(lists) > 0 ||
|
|
13694
|
+
if (countRules(lists) > 0 || base2.permissions !== void 0) config.permissions = lists;
|
|
13490
13695
|
return { config, layers, issues, managed: managed.policy };
|
|
13491
13696
|
}
|
|
13492
13697
|
function loadPluginLayer(entry, issues) {
|
|
13493
13698
|
const { plugin, path } = entry;
|
|
13494
|
-
const read = readSettingsFile(
|
|
13699
|
+
const read = readSettingsFile(
|
|
13700
|
+
path,
|
|
13701
|
+
SettingsFileSchema,
|
|
13702
|
+
SETTINGS_KEYS,
|
|
13703
|
+
{},
|
|
13704
|
+
issues,
|
|
13705
|
+
stripLegacyModelValue
|
|
13706
|
+
);
|
|
13495
13707
|
const label = t("config_load.layer_plugin_named", { plugin });
|
|
13496
13708
|
if (!read) return { layer: "plugin", path, status: "missing", lists: {}, scalars: {} };
|
|
13497
13709
|
if ("failed" in read) {
|
|
@@ -13567,11 +13779,11 @@ function originOf(origins, key) {
|
|
|
13567
13779
|
if (origins.file.has(key)) return "user";
|
|
13568
13780
|
return "builtin";
|
|
13569
13781
|
}
|
|
13570
|
-
function explainSetting(key,
|
|
13571
|
-
const baseLayer = baseLayerOf(
|
|
13782
|
+
function explainSetting(key, base2, file) {
|
|
13783
|
+
const baseLayer = baseLayerOf(base2);
|
|
13572
13784
|
const steps = [
|
|
13573
|
-
step("builtin",
|
|
13574
|
-
baseLayer === "builtin" ? { layer: "user" } : step(baseLayer,
|
|
13785
|
+
step("builtin", base2.builtin),
|
|
13786
|
+
baseLayer === "builtin" ? { layer: "user" } : step(baseLayer, base2.value)
|
|
13575
13787
|
];
|
|
13576
13788
|
if (file) {
|
|
13577
13789
|
for (const layer of SETTINGS_LAYER_ORDER) {
|
|
@@ -13588,10 +13800,10 @@ function explainSetting(key, base, file) {
|
|
|
13588
13800
|
chain: file ? steps : []
|
|
13589
13801
|
};
|
|
13590
13802
|
}
|
|
13591
|
-
function baseLayerOf(
|
|
13592
|
-
if (
|
|
13593
|
-
if (
|
|
13594
|
-
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";
|
|
13595
13807
|
}
|
|
13596
13808
|
function valueOf(file, layer) {
|
|
13597
13809
|
return file.layers.find((l) => l.layer === layer)?.scalars[file.key];
|
|
@@ -13641,8 +13853,9 @@ function _loadImpl(overrideHomeDir) {
|
|
|
13641
13853
|
mkdirSync(homeDir, { recursive: true });
|
|
13642
13854
|
const configPath2 = getConfigPath(homeDir);
|
|
13643
13855
|
const { mtime, size } = fileStamp(configPath2);
|
|
13856
|
+
const entriesStamp = stampKey(modelEntriesPath(homeDir));
|
|
13644
13857
|
const envSnapshot = snapshotEnv();
|
|
13645
|
-
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)) {
|
|
13646
13859
|
_lastIssues = _cache.issues;
|
|
13647
13860
|
_lastOrigins = _cache.origins;
|
|
13648
13861
|
return _cache.config;
|
|
@@ -13658,6 +13871,7 @@ function _loadImpl(overrideHomeDir) {
|
|
|
13658
13871
|
homeDir,
|
|
13659
13872
|
mtime,
|
|
13660
13873
|
size,
|
|
13874
|
+
entriesStamp,
|
|
13661
13875
|
envSnapshot,
|
|
13662
13876
|
config: cached.config,
|
|
13663
13877
|
issues,
|
|
@@ -13666,12 +13880,9 @@ function _loadImpl(overrideHomeDir) {
|
|
|
13666
13880
|
return cached.config;
|
|
13667
13881
|
}
|
|
13668
13882
|
const config = parseRawConfig(read.raw, issues, track);
|
|
13669
|
-
|
|
13670
|
-
const pinned = applyProviderOverride(config, track, override);
|
|
13671
|
-
const secretSource = applySecretsFromEnv(config, homeDir, track);
|
|
13672
|
-
if (pinned) applyProviderOverride(config, track, override);
|
|
13883
|
+
reportDroppedEnv(issues);
|
|
13673
13884
|
applyEnvOverrides(config, issues, track);
|
|
13674
|
-
|
|
13885
|
+
applyModelEntries(config, homeDir, issues);
|
|
13675
13886
|
if (overrideHomeDir) {
|
|
13676
13887
|
config.homeDir = overrideHomeDir;
|
|
13677
13888
|
config.dbPath = dbPath(overrideHomeDir);
|
|
@@ -13680,7 +13891,16 @@ function _loadImpl(overrideHomeDir) {
|
|
|
13680
13891
|
_lastIssues = issues;
|
|
13681
13892
|
_lastOrigins = origins;
|
|
13682
13893
|
_lastKnownGood = { homeDir, config: structuredClone(config), origins };
|
|
13683
|
-
_cache = {
|
|
13894
|
+
_cache = {
|
|
13895
|
+
homeDir,
|
|
13896
|
+
mtime,
|
|
13897
|
+
size,
|
|
13898
|
+
entriesStamp,
|
|
13899
|
+
envSnapshot,
|
|
13900
|
+
config: structuredClone(config),
|
|
13901
|
+
issues,
|
|
13902
|
+
origins
|
|
13903
|
+
};
|
|
13684
13904
|
return config;
|
|
13685
13905
|
}
|
|
13686
13906
|
function readRawConfig(configPath2, issues) {
|
|
@@ -13706,106 +13926,122 @@ function parseRawConfig(raw, issues, track) {
|
|
|
13706
13926
|
const expanded = expandEnvVars(raw);
|
|
13707
13927
|
const migrated = migrateRawConfig(expanded);
|
|
13708
13928
|
issues.push(...migrated.issues);
|
|
13709
|
-
|
|
13710
|
-
|
|
13711
|
-
|
|
13712
|
-
|
|
13713
|
-
EpochConfigSchema.parse({})
|
|
13714
|
-
);
|
|
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({}));
|
|
13715
13933
|
issues.push(...outcome.issues);
|
|
13716
13934
|
issues.push(...settingsHiddenIssues(outcome.value));
|
|
13717
13935
|
issues.push(...settingsPaneBeforeIssues(outcome.value));
|
|
13718
|
-
if (outcome.issues.length === 0) collectKeyPaths(
|
|
13936
|
+
if (outcome.issues.length === 0) collectKeyPaths(cleaned, track.file);
|
|
13719
13937
|
return outcome.value;
|
|
13720
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
|
+
}
|
|
13721
13990
|
function dropEmptySections(raw) {
|
|
13722
13991
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return raw;
|
|
13723
13992
|
const entries2 = Object.entries(raw);
|
|
13724
13993
|
if (!entries2.some(([, v]) => v === null)) return raw;
|
|
13725
13994
|
return Object.fromEntries(entries2.filter(([, v]) => v !== null));
|
|
13726
13995
|
}
|
|
13727
|
-
function
|
|
13728
|
-
const
|
|
13729
|
-
|
|
13730
|
-
|
|
13731
|
-
|
|
13732
|
-
|
|
13733
|
-
|
|
13734
|
-
|
|
13735
|
-
|
|
13736
|
-
|
|
13737
|
-
config.provider.type = info.type;
|
|
13738
|
-
track.env.add("provider.type");
|
|
13739
|
-
return finishSecrets(config, value, "explicit", track);
|
|
13740
|
-
}
|
|
13741
|
-
return "config-file";
|
|
13742
|
-
}
|
|
13743
|
-
function readProviderOverride(issues) {
|
|
13744
|
-
const override = {};
|
|
13745
|
-
const baseUrl = process.env.EPOCH_BASE_URL;
|
|
13746
|
-
if (baseUrl) {
|
|
13747
|
-
override.baseUrl = baseUrl;
|
|
13748
|
-
override.type = "openai-compatible";
|
|
13749
|
-
}
|
|
13750
|
-
const type = process.env.EPOCH_PROVIDER;
|
|
13751
|
-
if (type) {
|
|
13752
|
-
if (isProviderType(type)) {
|
|
13753
|
-
override.type ??= type;
|
|
13754
|
-
} else {
|
|
13755
|
-
issues.push(envIssue("EPOCH_PROVIDER", type, PROVIDER_TYPES.join(" / ")));
|
|
13756
|
-
}
|
|
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
|
+
});
|
|
13757
14006
|
}
|
|
13758
|
-
return override;
|
|
13759
14007
|
}
|
|
13760
|
-
function
|
|
13761
|
-
|
|
13762
|
-
config.provider.baseUrl = override.baseUrl;
|
|
13763
|
-
track.env.add("provider.baseUrl");
|
|
13764
|
-
}
|
|
13765
|
-
if (!override.type) return false;
|
|
13766
|
-
config.provider.type = override.type;
|
|
13767
|
-
track.env.add("provider.type");
|
|
13768
|
-
return true;
|
|
14008
|
+
function readSecret(name, homeDir) {
|
|
14009
|
+
return process.env[name] || loadEnvFile(getEnvPath(homeDir))[name] || getSecretValues()[name] || void 0;
|
|
13769
14010
|
}
|
|
13770
|
-
function
|
|
13771
|
-
|
|
13772
|
-
const fromStore = getSecretValues();
|
|
13773
|
-
const result = {};
|
|
13774
|
-
for (const name of API_KEY_ENV_VARS) {
|
|
13775
|
-
const value = process.env[name] || dotEnv[name] || fromStore[name];
|
|
13776
|
-
if (value) result[name] = value;
|
|
13777
|
-
}
|
|
13778
|
-
return result;
|
|
14011
|
+
function readEntryKey(id, homeDir = resolveHomeDir()) {
|
|
14012
|
+
return readSecret(entrySecretName(id), homeDir);
|
|
13779
14013
|
}
|
|
13780
|
-
function
|
|
13781
|
-
if (
|
|
13782
|
-
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
|
+
);
|
|
13783
14019
|
}
|
|
13784
|
-
function
|
|
13785
|
-
const
|
|
13786
|
-
|
|
13787
|
-
|
|
13788
|
-
track.env.add("provider.apiKey");
|
|
13789
|
-
track.env.add("provider.credentials");
|
|
13790
|
-
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);
|
|
13791
14024
|
}
|
|
13792
14025
|
function applyEnvOverrides(config, issues, track) {
|
|
13793
14026
|
const env = process.env;
|
|
13794
14027
|
if (env.EPOCH_MODEL) {
|
|
13795
|
-
|
|
13796
|
-
|
|
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>"));
|
|
13797
14032
|
}
|
|
13798
14033
|
if (env.EPOCH_PROFILE) {
|
|
13799
14034
|
config.profile = env.EPOCH_PROFILE;
|
|
13800
14035
|
track.env.add("profile");
|
|
13801
14036
|
}
|
|
13802
14037
|
if (env.EPOCH_FALLBACK_MODEL) {
|
|
13803
|
-
|
|
13804
|
-
|
|
13805
|
-
|
|
13806
|
-
|
|
13807
|
-
|
|
13808
|
-
|
|
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
|
+
}
|
|
13809
14045
|
}
|
|
13810
14046
|
if (env.EPOCH_MAX_TURNS) {
|
|
13811
14047
|
const turns = Number(env.EPOCH_MAX_TURNS);
|
|
@@ -13847,11 +14083,6 @@ function applyEnvOverrides(config, issues, track) {
|
|
|
13847
14083
|
function envIssue(name, value, expected) {
|
|
13848
14084
|
return { path: name, message: t("config_load.env_expected", { expected, value }) };
|
|
13849
14085
|
}
|
|
13850
|
-
function detectProviderFromKey(config) {
|
|
13851
|
-
const key = config.provider.apiKey;
|
|
13852
|
-
if (key.startsWith("sk-ant")) config.provider.type = "anthropic";
|
|
13853
|
-
else if (key.startsWith("sk-d")) config.provider.type = "deepseek";
|
|
13854
|
-
}
|
|
13855
14086
|
function backupCorruptConfig(path) {
|
|
13856
14087
|
try {
|
|
13857
14088
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
@@ -13864,7 +14095,6 @@ function backupCorruptConfig(path) {
|
|
|
13864
14095
|
}
|
|
13865
14096
|
function snapshotEnv() {
|
|
13866
14097
|
const keys = [
|
|
13867
|
-
...API_KEY_ENV_VARS,
|
|
13868
14098
|
"EPOCH_HOME",
|
|
13869
14099
|
"EPOCH_PROFILE",
|
|
13870
14100
|
"EPOCH_MODEL",
|
|
@@ -13879,8 +14109,6 @@ function snapshotEnv() {
|
|
|
13879
14109
|
const snapshot = Object.fromEntries(
|
|
13880
14110
|
keys.map((k) => [k, process.env[k]])
|
|
13881
14111
|
);
|
|
13882
|
-
const fromStore = getSecretValues();
|
|
13883
|
-
for (const name of API_KEY_ENV_VARS) snapshot[`secret:${name}`] = fromStore[name];
|
|
13884
14112
|
return snapshot;
|
|
13885
14113
|
}
|
|
13886
14114
|
function sameEnv(a, b) {
|
|
@@ -13920,6 +14148,19 @@ function loadEnvFile(path) {
|
|
|
13920
14148
|
return {};
|
|
13921
14149
|
}
|
|
13922
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
|
+
}
|
|
13923
14164
|
function fileStamp(path) {
|
|
13924
14165
|
try {
|
|
13925
14166
|
const st = statSync(path);
|
|
@@ -13928,9 +14169,6 @@ function fileStamp(path) {
|
|
|
13928
14169
|
return { mtime: 0, size: 0 };
|
|
13929
14170
|
}
|
|
13930
14171
|
}
|
|
13931
|
-
function parseKeys(raw) {
|
|
13932
|
-
return raw.split(",").map((s) => s.trim()).filter(Boolean).map((k) => ({ apiKey: k }));
|
|
13933
|
-
}
|
|
13934
14172
|
var PROBE_IMAGE_PNG_BASE64 = "iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAIAAAAlC+aJAAAAS0lEQVR42u3PQQkAAAgAsetfWiP4FgYrsKZeS0BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEDgsqnc8OJg6Ln3AAAAAElFTkSuQmCC";
|
|
13935
14173
|
var PROBE_PROMPT = "What color is this image? Answer with one word.";
|
|
13936
14174
|
var MIN_IMAGE_TOKEN_DELTA = 16;
|
|
@@ -13968,14 +14206,14 @@ async function runImageProbe(model) {
|
|
|
13968
14206
|
};
|
|
13969
14207
|
}
|
|
13970
14208
|
const shot = withImage.inputTokens;
|
|
13971
|
-
const
|
|
13972
|
-
if (shot === void 0 ||
|
|
13973
|
-
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})` };
|
|
13974
14212
|
}
|
|
13975
|
-
const delta = shot -
|
|
14213
|
+
const delta = shot - base2;
|
|
13976
14214
|
return {
|
|
13977
14215
|
verdict: delta >= MIN_IMAGE_TOKEN_DELTA ? "yes" : "no",
|
|
13978
|
-
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`
|
|
13979
14217
|
};
|
|
13980
14218
|
}
|
|
13981
14219
|
var PROBE_VERSION = 2;
|
|
@@ -14014,67 +14252,6 @@ function writeImageProbe(homeDir, provider, model, supportsImages) {
|
|
|
14014
14252
|
} catch {
|
|
14015
14253
|
}
|
|
14016
14254
|
}
|
|
14017
|
-
// src/provider/fallback.ts
|
|
14018
|
-
var DEFAULT_MODELS = {
|
|
14019
|
-
openai: "gpt-4o-mini",
|
|
14020
|
-
anthropic: "claude-haiku-4-5",
|
|
14021
|
-
google: "gemini-2.0-flash",
|
|
14022
|
-
deepseek: "deepseek-chat",
|
|
14023
|
-
groq: "llama-3.3-70b-versatile",
|
|
14024
|
-
mistral: "ministral-8b-latest",
|
|
14025
|
-
moonshotai: "kimi-latest",
|
|
14026
|
-
zhipu: "glm-4.5-flash",
|
|
14027
|
-
dashscope: "qwen-flash",
|
|
14028
|
-
bytedance: "doubao-seed-2.0-mini",
|
|
14029
|
-
qianfan: "ernie-speed-128k",
|
|
14030
|
-
cohere: "command-r7b-12-2024",
|
|
14031
|
-
xai: "grok-vision-beta",
|
|
14032
|
-
perplexity: "sonar",
|
|
14033
|
-
"amazon-bedrock": "anthropic.claude-instant-v1",
|
|
14034
|
-
azure: "gpt-4o-mini",
|
|
14035
|
-
togetherai: "meta-llama/Llama-3.3-70B-Instruct-Turbo",
|
|
14036
|
-
ollama: "llama3.2",
|
|
14037
|
-
"openai-compatible": "gpt-4o-mini"
|
|
14038
|
-
};
|
|
14039
|
-
function defaultModelOf(type) {
|
|
14040
|
-
return DEFAULT_MODELS[type];
|
|
14041
|
-
}
|
|
14042
|
-
function modelKey(provider, model) {
|
|
14043
|
-
return `${provider}/${model}`;
|
|
14044
|
-
}
|
|
14045
|
-
function nextFallbackModel(provider, current, tried, configured) {
|
|
14046
|
-
for (const candidate of [configured, DEFAULT_MODELS[provider]]) {
|
|
14047
|
-
if (!candidate || candidate === current) continue;
|
|
14048
|
-
if (tried.has(modelKey(provider, candidate))) continue;
|
|
14049
|
-
return candidate;
|
|
14050
|
-
}
|
|
14051
|
-
return void 0;
|
|
14052
|
-
}
|
|
14053
|
-
var MODEL_GIVE_UP_THRESHOLD = 3;
|
|
14054
|
-
var ModelFailureTracker = class {
|
|
14055
|
-
counts = /* @__PURE__ */ new Map();
|
|
14056
|
-
abandoned = /* @__PURE__ */ new Set();
|
|
14057
|
-
recordFailure(provider, model) {
|
|
14058
|
-
const key = modelKey(provider, model);
|
|
14059
|
-
if (this.abandoned.has(key)) return false;
|
|
14060
|
-
const next = (this.counts.get(key) ?? 0) + 1;
|
|
14061
|
-
this.counts.set(key, next);
|
|
14062
|
-
if (next < MODEL_GIVE_UP_THRESHOLD) return false;
|
|
14063
|
-
this.abandoned.add(key);
|
|
14064
|
-
return true;
|
|
14065
|
-
}
|
|
14066
|
-
recordSuccess(provider, model) {
|
|
14067
|
-
this.counts.delete(modelKey(provider, model));
|
|
14068
|
-
}
|
|
14069
|
-
isAbandoned(provider, model) {
|
|
14070
|
-
return this.abandoned.has(modelKey(provider, model));
|
|
14071
|
-
}
|
|
14072
|
-
clear(provider, model) {
|
|
14073
|
-
const key = modelKey(provider, model);
|
|
14074
|
-
this.counts.delete(key);
|
|
14075
|
-
this.abandoned.delete(key);
|
|
14076
|
-
}
|
|
14077
|
-
};
|
|
14078
14255
|
var DEFAULT_CONTEXT_LENGTH2 = 2e5;
|
|
14079
14256
|
var STATIC_CAPABILITIES = {
|
|
14080
14257
|
"gpt-4o": {
|
|
@@ -14463,6 +14640,9 @@ function openaiCompatibleFactory(name, defaultBaseUrl) {
|
|
|
14463
14640
|
});
|
|
14464
14641
|
};
|
|
14465
14642
|
}
|
|
14643
|
+
function base(baseUrl) {
|
|
14644
|
+
return baseUrl ? { baseURL: baseUrl } : {};
|
|
14645
|
+
}
|
|
14466
14646
|
var LOADERS = {
|
|
14467
14647
|
openai: async ({ apiKey, baseUrl }) => {
|
|
14468
14648
|
const { createOpenAI } = await import('@ai-sdk/openai');
|
|
@@ -14470,29 +14650,29 @@ var LOADERS = {
|
|
|
14470
14650
|
if (baseUrl) opts["baseURL"] = baseUrl;
|
|
14471
14651
|
return createOpenAI(opts);
|
|
14472
14652
|
},
|
|
14473
|
-
anthropic: async ({ apiKey }) => {
|
|
14653
|
+
anthropic: async ({ apiKey, baseUrl }) => {
|
|
14474
14654
|
const { createAnthropic } = await import('@ai-sdk/anthropic');
|
|
14475
|
-
return createAnthropic({ apiKey });
|
|
14655
|
+
return createAnthropic({ apiKey, ...base(baseUrl) });
|
|
14476
14656
|
},
|
|
14477
|
-
google: async ({ apiKey }) => {
|
|
14657
|
+
google: async ({ apiKey, baseUrl }) => {
|
|
14478
14658
|
const { createGoogleGenerativeAI } = await import('@ai-sdk/google');
|
|
14479
|
-
return createGoogleGenerativeAI({ apiKey });
|
|
14659
|
+
return createGoogleGenerativeAI({ apiKey, ...base(baseUrl) });
|
|
14480
14660
|
},
|
|
14481
|
-
deepseek: async ({ apiKey }) => {
|
|
14661
|
+
deepseek: async ({ apiKey, baseUrl }) => {
|
|
14482
14662
|
const { createDeepSeek } = await import('@ai-sdk/deepseek');
|
|
14483
|
-
return createDeepSeek({ apiKey });
|
|
14663
|
+
return createDeepSeek({ apiKey, ...base(baseUrl) });
|
|
14484
14664
|
},
|
|
14485
|
-
groq: async ({ apiKey }) => {
|
|
14665
|
+
groq: async ({ apiKey, baseUrl }) => {
|
|
14486
14666
|
const { createGroq } = await import('@ai-sdk/groq');
|
|
14487
|
-
return createGroq({ apiKey });
|
|
14667
|
+
return createGroq({ apiKey, ...base(baseUrl) });
|
|
14488
14668
|
},
|
|
14489
|
-
mistral: async ({ apiKey }) => {
|
|
14669
|
+
mistral: async ({ apiKey, baseUrl }) => {
|
|
14490
14670
|
const { createMistral } = await import('@ai-sdk/mistral');
|
|
14491
|
-
return createMistral({ apiKey });
|
|
14671
|
+
return createMistral({ apiKey, ...base(baseUrl) });
|
|
14492
14672
|
},
|
|
14493
|
-
moonshotai: async ({ apiKey }) => {
|
|
14673
|
+
moonshotai: async ({ apiKey, baseUrl }) => {
|
|
14494
14674
|
const { createMoonshotAI } = await import('@ai-sdk/moonshotai');
|
|
14495
|
-
return createMoonshotAI({ apiKey });
|
|
14675
|
+
return createMoonshotAI({ apiKey, ...base(baseUrl) });
|
|
14496
14676
|
},
|
|
14497
14677
|
zhipu: openaiCompatibleFactory("zhipu", "https://open.bigmodel.cn/api/paas/v4"),
|
|
14498
14678
|
dashscope: async ({ apiKey, baseUrl }) => {
|
|
@@ -14505,17 +14685,17 @@ var LOADERS = {
|
|
|
14505
14685
|
},
|
|
14506
14686
|
bytedance: openaiCompatibleFactory("bytedance", "https://ark.cn-beijing.volces.com/api/v3"),
|
|
14507
14687
|
qianfan: openaiCompatibleFactory("qianfan", "https://qianfan.baidubce.com/v2"),
|
|
14508
|
-
cohere: async ({ apiKey }) => {
|
|
14688
|
+
cohere: async ({ apiKey, baseUrl }) => {
|
|
14509
14689
|
const { createCohere } = await import('@ai-sdk/cohere');
|
|
14510
|
-
return createCohere({ apiKey });
|
|
14690
|
+
return createCohere({ apiKey, ...base(baseUrl) });
|
|
14511
14691
|
},
|
|
14512
|
-
xai: async ({ apiKey }) => {
|
|
14692
|
+
xai: async ({ apiKey, baseUrl }) => {
|
|
14513
14693
|
const { createXai } = await import('@ai-sdk/xai');
|
|
14514
|
-
return createXai({ apiKey });
|
|
14694
|
+
return createXai({ apiKey, ...base(baseUrl) });
|
|
14515
14695
|
},
|
|
14516
|
-
perplexity: async ({ apiKey }) => {
|
|
14696
|
+
perplexity: async ({ apiKey, baseUrl }) => {
|
|
14517
14697
|
const { createPerplexity } = await import('@ai-sdk/perplexity');
|
|
14518
|
-
return createPerplexity({ apiKey });
|
|
14698
|
+
return createPerplexity({ apiKey, ...base(baseUrl) });
|
|
14519
14699
|
},
|
|
14520
14700
|
"amazon-bedrock": async ({ apiKey }) => {
|
|
14521
14701
|
const { createAmazonBedrock } = await import('@ai-sdk/amazon-bedrock');
|
|
@@ -14525,13 +14705,13 @@ var LOADERS = {
|
|
|
14525
14705
|
const { createAzure } = await import('@ai-sdk/azure');
|
|
14526
14706
|
return createAzure({ apiKey });
|
|
14527
14707
|
},
|
|
14528
|
-
togetherai: async ({ apiKey }) => {
|
|
14708
|
+
togetherai: async ({ apiKey, baseUrl }) => {
|
|
14529
14709
|
const { createTogetherAI } = await import('@ai-sdk/togetherai');
|
|
14530
|
-
return createTogetherAI({ apiKey });
|
|
14710
|
+
return createTogetherAI({ apiKey, ...base(baseUrl) });
|
|
14531
14711
|
},
|
|
14532
|
-
ollama: async () => {
|
|
14712
|
+
ollama: async ({ baseUrl }) => {
|
|
14533
14713
|
const { createOllama } = await import('ollama-ai-provider-v2');
|
|
14534
|
-
return createOllama();
|
|
14714
|
+
return createOllama(base(baseUrl));
|
|
14535
14715
|
},
|
|
14536
14716
|
"openai-compatible": openaiCompatibleFactory("custom", "http://localhost:8080/v1")
|
|
14537
14717
|
};
|
|
@@ -14559,6 +14739,62 @@ async function createModelFactory(type, opts) {
|
|
|
14559
14739
|
throw err;
|
|
14560
14740
|
}
|
|
14561
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
|
+
}
|
|
14562
14798
|
function isModuleNotFound(err) {
|
|
14563
14799
|
if (!err || typeof err !== "object") return false;
|
|
14564
14800
|
const code = err.code;
|
|
@@ -14566,21 +14802,127 @@ function isModuleNotFound(err) {
|
|
|
14566
14802
|
const msg = err instanceof Error ? err.message : "";
|
|
14567
14803
|
return /Cannot find (module|package)|Failed to resolve/i.test(msg);
|
|
14568
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
|
+
}
|
|
14569
14914
|
var CONTEXT_WARN_RATIO = 0.8;
|
|
14570
14915
|
function fmtTokens(n) {
|
|
14571
14916
|
return n >= 1e3 ? `${Math.round(n / 1e3)}k` : String(n);
|
|
14572
14917
|
}
|
|
14573
14918
|
function checkCredential(want, deps) {
|
|
14574
14919
|
if (deps.hasCredential(want.provider)) return null;
|
|
14575
|
-
return { code: "no-credential",
|
|
14920
|
+
return { code: "no-credential", entry: want.entry };
|
|
14576
14921
|
}
|
|
14577
14922
|
function describeRejection(rejection) {
|
|
14578
14923
|
switch (rejection.code) {
|
|
14579
14924
|
case "no-credential":
|
|
14580
|
-
return t("provider_switch.no_credential", {
|
|
14581
|
-
provider: rejection.provider,
|
|
14582
|
-
hint: rejection.envVar ? t("provider_switch.no_credential_env", { envVar: rejection.envVar }) : t("provider_switch.no_credential_plain")
|
|
14583
|
-
});
|
|
14925
|
+
return t("provider_switch.no_credential", { entry: rejection.entry });
|
|
14584
14926
|
case "images-unsupported":
|
|
14585
14927
|
return t("provider_switch.images_unsupported", { model: rejection.model });
|
|
14586
14928
|
case "context-overflow":
|
|
@@ -14589,6 +14931,8 @@ function describeRejection(rejection) {
|
|
|
14589
14931
|
model: rejection.model,
|
|
14590
14932
|
limit: fmtTokens(rejection.contextLength)
|
|
14591
14933
|
});
|
|
14934
|
+
case "unknown-entry":
|
|
14935
|
+
return t("provider_switch.unknown_entry", { entry: rejection.entry });
|
|
14592
14936
|
}
|
|
14593
14937
|
}
|
|
14594
14938
|
function describeWarning(warning) {
|
|
@@ -14640,15 +14984,13 @@ function evaluateSelection(want, deps, ctx = {}) {
|
|
|
14640
14984
|
function reject(rejection) {
|
|
14641
14985
|
return { ok: false, reason: describeRejection(rejection), rejection };
|
|
14642
14986
|
}
|
|
14643
|
-
// src/provider/types.ts
|
|
14644
|
-
var DEFAULT_MODEL = "gpt-4o-mini";
|
|
14645
14987
|
// src/provider/router.ts
|
|
14646
14988
|
var ProviderRouter = class {
|
|
14647
14989
|
config;
|
|
14648
14990
|
available = /* @__PURE__ */ new Map();
|
|
14649
14991
|
factories = /* @__PURE__ */ new Map();
|
|
14650
|
-
|
|
14651
|
-
|
|
14992
|
+
entryTable;
|
|
14993
|
+
effort;
|
|
14652
14994
|
telemetry = NOOP_TELEMETRY;
|
|
14653
14995
|
selection;
|
|
14654
14996
|
configSelection;
|
|
@@ -14659,44 +15001,28 @@ var ProviderRouter = class {
|
|
|
14659
15001
|
}
|
|
14660
15002
|
constructor(config) {
|
|
14661
15003
|
this.config = config;
|
|
14662
|
-
this.
|
|
14663
|
-
this.
|
|
14664
|
-
this.
|
|
14665
|
-
provider: config.provider.type,
|
|
14666
|
-
model: config.model ?? DEFAULT_MODEL,
|
|
14667
|
-
origin: "config"
|
|
14668
|
-
};
|
|
15004
|
+
this.entryTable = buildEntryTable(config.modelEntries);
|
|
15005
|
+
this.configSelection = this.resolveConfigured(config);
|
|
15006
|
+
this.initFactories();
|
|
14669
15007
|
this.selection = { ...this.configSelection };
|
|
14670
15008
|
}
|
|
14671
15009
|
getSelection() {
|
|
14672
15010
|
return { ...this.selection };
|
|
14673
15011
|
}
|
|
14674
15012
|
getConfiguredModel() {
|
|
14675
|
-
return this.configSelection.
|
|
15013
|
+
return entryRef(this.configSelection.entry);
|
|
14676
15014
|
}
|
|
14677
15015
|
setSelection(next, ctx = {}) {
|
|
14678
|
-
|
|
14679
|
-
|
|
14680
|
-
provider: next.provider ?? this.selection.provider,
|
|
14681
|
-
model: next.model,
|
|
14682
|
-
origin: "user-switch"
|
|
14683
|
-
},
|
|
14684
|
-
ctx
|
|
14685
|
-
);
|
|
15016
|
+
const want = this.resolveRef(next, "user-switch");
|
|
15017
|
+
return "ok" in want ? want : this.applySelection(want, ctx);
|
|
14686
15018
|
}
|
|
14687
15019
|
resetSelection(ctx = {}) {
|
|
14688
15020
|
return this.applySelection({ ...this.configSelection }, ctx);
|
|
14689
15021
|
}
|
|
14690
15022
|
enterTurn(ref, ctx = {}) {
|
|
14691
15023
|
const previous = { ...this.selection };
|
|
14692
|
-
const
|
|
14693
|
-
|
|
14694
|
-
provider: ref.provider ?? this.selection.provider,
|
|
14695
|
-
model: ref.model,
|
|
14696
|
-
origin: "command-frontmatter"
|
|
14697
|
-
},
|
|
14698
|
-
ctx
|
|
14699
|
-
);
|
|
15024
|
+
const want = this.resolveRef(ref, "command-frontmatter");
|
|
15025
|
+
const result = "ok" in want ? want : this.applySelection(want, ctx);
|
|
14700
15026
|
return {
|
|
14701
15027
|
result,
|
|
14702
15028
|
restore: () => {
|
|
@@ -14708,15 +15034,16 @@ var ProviderRouter = class {
|
|
|
14708
15034
|
const result = evaluateSelection(
|
|
14709
15035
|
want,
|
|
14710
15036
|
{
|
|
14711
|
-
hasCredential: (p) => this.available.has(p),
|
|
14712
|
-
capability: (model) =>
|
|
15037
|
+
hasCredential: (p) => this.available.has(this.routeOf(p, want.entry)),
|
|
15038
|
+
capability: (model) => this.capabilityFor(want.provider, model, want.entry)
|
|
14713
15039
|
},
|
|
14714
15040
|
ctx
|
|
14715
15041
|
);
|
|
14716
15042
|
if (!result.ok) return result;
|
|
14717
15043
|
const from = this.selection;
|
|
14718
15044
|
this.selection = want;
|
|
14719
|
-
this.
|
|
15045
|
+
this.effort = void 0;
|
|
15046
|
+
this.failures.clear(this.routeOf(want.provider, want.entry));
|
|
14720
15047
|
this.telemetry.counter(METRIC.MODEL_SWITCH, 1, {
|
|
14721
15048
|
"epoch.model.from": modelKey(from.provider, from.model),
|
|
14722
15049
|
"epoch.model.to": modelKey(want.provider, want.model),
|
|
@@ -14724,9 +15051,77 @@ var ProviderRouter = class {
|
|
|
14724
15051
|
});
|
|
14725
15052
|
return result;
|
|
14726
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
|
+
}
|
|
14727
15093
|
drainNotices() {
|
|
14728
15094
|
return this.notices.splice(0, this.notices.length);
|
|
14729
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
|
+
}
|
|
14730
15125
|
getUtilityInfo() {
|
|
14731
15126
|
const raw = this.config.models?.utility;
|
|
14732
15127
|
if (!raw) return { selection: this.getSelection(), configured: false };
|
|
@@ -14738,149 +15133,129 @@ var ProviderRouter = class {
|
|
|
14738
15133
|
issue: t("provider_route.utility_unparsable", { raw })
|
|
14739
15134
|
};
|
|
14740
15135
|
}
|
|
14741
|
-
|
|
14742
|
-
if (!this.available.has(provider)) {
|
|
14743
|
-
return {
|
|
14744
|
-
selection: this.getSelection(),
|
|
14745
|
-
configured: true,
|
|
14746
|
-
issue: t("provider_route.utility_no_credential", { provider })
|
|
14747
|
-
};
|
|
14748
|
-
}
|
|
14749
|
-
return {
|
|
14750
|
-
selection: { provider, model: ref.model, origin: this.selection.origin },
|
|
14751
|
-
configured: true
|
|
14752
|
-
};
|
|
15136
|
+
return this.entryUtilityInfo(ref.entry);
|
|
14753
15137
|
}
|
|
14754
|
-
initFactories(
|
|
14755
|
-
|
|
14756
|
-
|
|
14757
|
-
|
|
14758
|
-
}
|
|
14759
|
-
for (const type of PROVIDER_TYPES) {
|
|
14760
|
-
const key = this.resolveKey(type, config);
|
|
14761
|
-
if (!key) continue;
|
|
14762
|
-
const isPrimary = type === config.provider.type;
|
|
14763
|
-
this.register(type, key, isPrimary ? config.provider.baseUrl : void 0);
|
|
14764
|
-
}
|
|
14765
|
-
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))) {
|
|
14766
15142
|
throw new ProviderError(
|
|
14767
|
-
t("provider_route.
|
|
15143
|
+
t("provider_route.entry_no_credential", { entry }),
|
|
14768
15144
|
PROVIDER_NO_CREDENTIAL,
|
|
14769
15145
|
false
|
|
14770
15146
|
);
|
|
14771
15147
|
}
|
|
14772
|
-
|
|
14773
|
-
|
|
14774
|
-
|
|
14775
|
-
|
|
14776
|
-
|
|
14777
|
-
|
|
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 });
|
|
14778
15155
|
}
|
|
14779
15156
|
}
|
|
14780
|
-
|
|
14781
|
-
this.
|
|
14782
|
-
|
|
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
|
+
);
|
|
14783
15165
|
}
|
|
14784
|
-
|
|
14785
|
-
|
|
14786
|
-
|
|
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
|
+
);
|
|
15175
|
+
}
|
|
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 };
|
|
14787
15181
|
}
|
|
14788
|
-
return this.
|
|
15182
|
+
return entrySelection(this.entryTable, entry, origin);
|
|
14789
15183
|
}
|
|
14790
|
-
|
|
14791
|
-
|
|
14792
|
-
|
|
14793
|
-
|
|
14794
|
-
|
|
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 };
|
|
14795
15202
|
}
|
|
14796
|
-
async getModel(
|
|
14797
|
-
const cached = this.factories.get(
|
|
15203
|
+
async getModel(route, modelId) {
|
|
15204
|
+
const cached = this.factories.get(route);
|
|
14798
15205
|
if (cached) return cached(modelId ?? this.selection.model);
|
|
14799
|
-
const opts = this.available.get(
|
|
15206
|
+
const opts = this.available.get(route);
|
|
14800
15207
|
if (!opts) {
|
|
14801
|
-
throw new ProviderError(
|
|
15208
|
+
throw new ProviderError(
|
|
15209
|
+
t("provider_route.not_initialized", { provider: route }),
|
|
15210
|
+
1004,
|
|
15211
|
+
false
|
|
15212
|
+
);
|
|
14802
15213
|
}
|
|
14803
15214
|
let factory;
|
|
14804
15215
|
try {
|
|
14805
|
-
factory = await createModelFactory(
|
|
15216
|
+
factory = opts.entry ? await createEntryFactory(opts.entry, opts.apiKey || void 0) : await createModelFactory(route, opts);
|
|
14806
15217
|
} catch (err) {
|
|
14807
15218
|
throw new ProviderError(err instanceof Error ? err.message : String(err), 1005, false);
|
|
14808
15219
|
}
|
|
14809
|
-
this.factories.set(
|
|
15220
|
+
this.factories.set(route, factory);
|
|
14810
15221
|
return factory(modelId ?? this.selection.model);
|
|
14811
15222
|
}
|
|
14812
|
-
rotateCredential() {
|
|
14813
|
-
const creds = this.config.provider.credentials;
|
|
14814
|
-
if (this.credentialIndex + 1 < creds.length) {
|
|
14815
|
-
this.credentialIndex++;
|
|
14816
|
-
const newCred = creds[this.credentialIndex];
|
|
14817
|
-
if (!newCred) return false;
|
|
14818
|
-
this.register(this.config.provider.type, newCred.apiKey, this.config.provider.baseUrl);
|
|
14819
|
-
return true;
|
|
14820
|
-
}
|
|
14821
|
-
return false;
|
|
14822
|
-
}
|
|
14823
|
-
fallbackProvider(currentType, tried) {
|
|
14824
|
-
if (!this.config.fallback) return null;
|
|
14825
|
-
for (const fbType of this.config.fallback.providers) {
|
|
14826
|
-
if (fbType === currentType) continue;
|
|
14827
|
-
if (tried?.has(modelKey(fbType, defaultModelOf(fbType)))) continue;
|
|
14828
|
-
if (this.available.has(fbType)) {
|
|
14829
|
-
this.telemetry.counter(METRIC.PROVIDER_FALLBACK, 1, {
|
|
14830
|
-
"epoch.provider.from": currentType,
|
|
14831
|
-
"epoch.provider.to": fbType
|
|
14832
|
-
});
|
|
14833
|
-
return {
|
|
14834
|
-
from: currentType,
|
|
14835
|
-
fromModel: this.getModelId(),
|
|
14836
|
-
to: fbType,
|
|
14837
|
-
toModel: defaultModelOf(fbType),
|
|
14838
|
-
reason: t("provider_route.fallback_reason")
|
|
14839
|
-
};
|
|
14840
|
-
}
|
|
14841
|
-
}
|
|
14842
|
-
return null;
|
|
14843
|
-
}
|
|
14844
15223
|
startPoint(input) {
|
|
14845
15224
|
if (input.responseFormat && input.tools?.length) {
|
|
14846
15225
|
throw new ProviderError(t("provider.response_format_with_tools"), 1002, false);
|
|
14847
15226
|
}
|
|
14848
|
-
const
|
|
14849
|
-
|
|
14850
|
-
|
|
14851
|
-
|
|
14852
|
-
|
|
14853
|
-
false
|
|
14854
|
-
);
|
|
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);
|
|
14855
15232
|
}
|
|
14856
|
-
const
|
|
14857
|
-
|
|
14858
|
-
|
|
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
|
+
};
|
|
14859
15241
|
}
|
|
14860
|
-
avoidAbandoned(
|
|
14861
|
-
if (!this.failures.isAbandoned(
|
|
14862
|
-
const
|
|
14863
|
-
|
|
14864
|
-
const next = nextFallbackModel(ct, cm, seen, this.configuredFallbackModel(ct));
|
|
14865
|
-
if (!next) return cm;
|
|
14866
|
-
if (!this.failures.isAbandoned(ct, next)) return next;
|
|
14867
|
-
seen.add(modelKey(ct, next));
|
|
14868
|
-
}
|
|
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;
|
|
14869
15246
|
}
|
|
14870
15247
|
async recover(err, state) {
|
|
14871
15248
|
const { code, recovery } = classifyProviderError(err);
|
|
14872
15249
|
const message = describeProviderError(err);
|
|
14873
|
-
const stay = { kind: "continue", ct: state.ct, cm: state.cm };
|
|
15250
|
+
const stay = { kind: "continue", ct: state.ct, cm: state.cm, entry: state.entry };
|
|
14874
15251
|
if (code === PROVIDER_ABORTED) return { kind: "give-up", message, code };
|
|
14875
|
-
if (recovery === "rotate_credential" && this.rotateCredential())
|
|
14876
|
-
return { ...stay, attempt: state.attempt + 1 };
|
|
14877
15252
|
if (recovery === "retry") {
|
|
14878
15253
|
await this.backoff(state.attempt);
|
|
14879
15254
|
return { ...stay, attempt: state.attempt + 1 };
|
|
14880
15255
|
}
|
|
14881
|
-
this.noteFailure(state
|
|
15256
|
+
this.noteFailure(state);
|
|
14882
15257
|
if (recovery === "abort") return { kind: "give-up", message, code };
|
|
14883
|
-
const next = this.escalate(state
|
|
15258
|
+
const next = this.escalate(state);
|
|
14884
15259
|
if (next) return next;
|
|
14885
15260
|
return {
|
|
14886
15261
|
kind: "give-up",
|
|
@@ -14891,36 +15266,40 @@ var ProviderRouter = class {
|
|
|
14891
15266
|
code
|
|
14892
15267
|
};
|
|
14893
15268
|
}
|
|
14894
|
-
escalate(state
|
|
14895
|
-
|
|
14896
|
-
|
|
14897
|
-
|
|
14898
|
-
|
|
14899
|
-
|
|
14900
|
-
|
|
14901
|
-
|
|
14902
|
-
|
|
14903
|
-
|
|
14904
|
-
|
|
14905
|
-
|
|
14906
|
-
|
|
14907
|
-
|
|
14908
|
-
|
|
14909
|
-
}
|
|
14910
|
-
}
|
|
14911
|
-
const fb = this.fallbackProvider(state.ct, state.tried);
|
|
14912
|
-
if (!fb) return null;
|
|
14913
|
-
state.tried.add(modelKey(fb.to, fb.toModel));
|
|
14914
|
-
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
|
+
};
|
|
14915
15284
|
}
|
|
14916
|
-
|
|
14917
|
-
|
|
15285
|
+
afterRetries(state) {
|
|
15286
|
+
this.noteFailure(state);
|
|
15287
|
+
return this.escalate(state);
|
|
14918
15288
|
}
|
|
14919
|
-
|
|
14920
|
-
|
|
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;
|
|
15296
|
+
}
|
|
15297
|
+
noteFailure(state) {
|
|
15298
|
+
if (!this.failures.recordFailure(this.routeOf(state.ct, state.entry))) return;
|
|
14921
15299
|
this.notices.push(
|
|
14922
15300
|
t("provider_route.give_up_model", {
|
|
14923
|
-
|
|
15301
|
+
entry: state.entry,
|
|
15302
|
+
model: state.cm,
|
|
14924
15303
|
count: MODEL_GIVE_UP_THRESHOLD
|
|
14925
15304
|
})
|
|
14926
15305
|
);
|
|
@@ -14932,30 +15311,50 @@ var ProviderRouter = class {
|
|
|
14932
15311
|
async generate(input) {
|
|
14933
15312
|
const state = this.startPoint(input);
|
|
14934
15313
|
const requestedModel = state.cm;
|
|
14935
|
-
const
|
|
15314
|
+
const messagesFor = messageTranslator(input.messages, this.entryTable);
|
|
14936
15315
|
const tools = input.tools ? toSdkTools(input.tools) : void 0;
|
|
14937
15316
|
const output = input.responseFormat ? toOutputSpec(input.responseFormat) : void 0;
|
|
14938
|
-
|
|
14939
|
-
|
|
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;
|
|
14940
15334
|
try {
|
|
14941
|
-
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);
|
|
14942
15338
|
const result = await generateText({
|
|
14943
15339
|
model,
|
|
14944
|
-
messages,
|
|
15340
|
+
messages: messagesFor(ct, entry),
|
|
14945
15341
|
tools,
|
|
14946
15342
|
system: input.system,
|
|
14947
15343
|
stopWhen: isStepCount(input.maxSteps ?? 1),
|
|
14948
|
-
...
|
|
15344
|
+
...maxTokens ? { maxOutputTokens: maxTokens } : {},
|
|
14949
15345
|
...input.signal ? { abortSignal: input.signal } : {},
|
|
14950
15346
|
output,
|
|
14951
|
-
providerOptions: toSdkProviderOptions(
|
|
15347
|
+
providerOptions: toSdkProviderOptions(
|
|
15348
|
+
mergeProviderOptions(input.providerOptions, effort?.providerOptions)
|
|
15349
|
+
),
|
|
15350
|
+
...effort ? { reasoning: effort.reasoning } : {}
|
|
14952
15351
|
});
|
|
14953
15352
|
const toolCalls = normalizeToolCalls(
|
|
14954
15353
|
result.toolCalls
|
|
14955
15354
|
);
|
|
14956
15355
|
const steps = result.steps;
|
|
14957
15356
|
const reasoningText = steps?.map((s) => s.reasoningText).filter(Boolean).join("\n") || void 0;
|
|
14958
|
-
this.failures.recordSuccess(ct,
|
|
15357
|
+
this.failures.recordSuccess(this.routeOf(ct, entry));
|
|
14959
15358
|
const usage = toTokenUsage(result.usage);
|
|
14960
15359
|
const warnings = toProviderWarnings(result.warnings);
|
|
14961
15360
|
const applied = input.responseFormat ? didApplyResponseFormat(result.warnings) : void 0;
|
|
@@ -14977,39 +15376,54 @@ var ProviderRouter = class {
|
|
|
14977
15376
|
if (plan.kind === "give-up") throw new ProviderError(plan.message, plan.code, false);
|
|
14978
15377
|
state.ct = plan.ct;
|
|
14979
15378
|
state.cm = plan.cm;
|
|
15379
|
+
state.entry = plan.entry;
|
|
14980
15380
|
state.attempt = plan.attempt;
|
|
14981
15381
|
}
|
|
14982
15382
|
}
|
|
14983
|
-
this.noteFailure(state.ct, state.cm);
|
|
14984
|
-
throw new ProviderError(
|
|
14985
|
-
t("provider_route.retries_exhausted", { count: this.config.retry.maxRetries }),
|
|
14986
|
-
9999,
|
|
14987
|
-
false
|
|
14988
|
-
);
|
|
14989
15383
|
}
|
|
14990
15384
|
async *stream(input) {
|
|
14991
15385
|
const state = this.startPoint(input);
|
|
14992
15386
|
const requestedModel = state.cm;
|
|
14993
15387
|
let emitted = false;
|
|
14994
|
-
const
|
|
15388
|
+
const messagesFor = messageTranslator(input.messages, this.entryTable);
|
|
14995
15389
|
const tools = input.tools ? toSdkTools(input.tools) : void 0;
|
|
14996
15390
|
const output = input.responseFormat ? toOutputSpec(input.responseFormat) : void 0;
|
|
14997
15391
|
let streamPartError;
|
|
14998
|
-
|
|
14999
|
-
|
|
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;
|
|
15000
15409
|
streamPartError = void 0;
|
|
15001
15410
|
try {
|
|
15002
|
-
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);
|
|
15003
15414
|
const result = streamText({
|
|
15004
15415
|
model,
|
|
15005
|
-
messages,
|
|
15416
|
+
messages: messagesFor(ct, entry),
|
|
15006
15417
|
tools,
|
|
15007
15418
|
system: input.system,
|
|
15008
15419
|
stopWhen: isStepCount(input.maxSteps ?? 1),
|
|
15009
|
-
...
|
|
15420
|
+
...maxTokens ? { maxOutputTokens: maxTokens } : {},
|
|
15010
15421
|
...input.signal ? { abortSignal: input.signal } : {},
|
|
15011
15422
|
output,
|
|
15012
|
-
providerOptions: toSdkProviderOptions(
|
|
15423
|
+
providerOptions: toSdkProviderOptions(
|
|
15424
|
+
mergeProviderOptions(input.providerOptions, effort?.providerOptions)
|
|
15425
|
+
),
|
|
15426
|
+
...effort ? { reasoning: effort.reasoning } : {}
|
|
15013
15427
|
});
|
|
15014
15428
|
for await (const part of result.fullStream) {
|
|
15015
15429
|
const typed = part;
|
|
@@ -15048,7 +15462,7 @@ var ProviderRouter = class {
|
|
|
15048
15462
|
const warnings = toProviderWarnings(raw);
|
|
15049
15463
|
const applied = input.responseFormat ? didApplyResponseFormat(raw) : void 0;
|
|
15050
15464
|
this.noteResponseFormat(input, applied ?? true, state);
|
|
15051
|
-
this.failures.recordSuccess(ct,
|
|
15465
|
+
this.failures.recordSuccess(this.routeOf(ct, entry));
|
|
15052
15466
|
yield {
|
|
15053
15467
|
type: "finish",
|
|
15054
15468
|
...usage ? { usage } : {},
|
|
@@ -15073,14 +15487,15 @@ var ProviderRouter = class {
|
|
|
15073
15487
|
}
|
|
15074
15488
|
state.ct = plan.ct;
|
|
15075
15489
|
state.cm = plan.cm;
|
|
15490
|
+
state.entry = plan.entry;
|
|
15076
15491
|
state.attempt = plan.attempt;
|
|
15077
15492
|
}
|
|
15078
15493
|
}
|
|
15079
|
-
|
|
15080
|
-
|
|
15081
|
-
|
|
15082
|
-
|
|
15083
|
-
|
|
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;
|
|
15084
15499
|
}
|
|
15085
15500
|
async backoff(n) {
|
|
15086
15501
|
await new Promise(
|
|
@@ -15097,24 +15512,22 @@ var ProviderRouter = class {
|
|
|
15097
15512
|
return this.selection.provider;
|
|
15098
15513
|
}
|
|
15099
15514
|
getApiKeyHint() {
|
|
15100
|
-
|
|
15101
|
-
|
|
15102
|
-
|
|
15103
|
-
}
|
|
15104
|
-
const opts = this.available.get(this.selection.provider);
|
|
15105
|
-
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";
|
|
15106
15518
|
}
|
|
15107
15519
|
getCapability() {
|
|
15108
|
-
|
|
15520
|
+
const { provider, model, entry } = this.selection;
|
|
15521
|
+
return this.capabilityFor(provider, model, entry);
|
|
15109
15522
|
}
|
|
15110
15523
|
async probeImageSupport() {
|
|
15111
|
-
const { provider, model } = this.selection;
|
|
15524
|
+
const { provider, model, entry } = this.selection;
|
|
15112
15525
|
const home = this.config.homeDir;
|
|
15113
15526
|
const cached = readImageProbe(home, provider, model);
|
|
15114
15527
|
if (cached !== void 0) return cached;
|
|
15115
15528
|
let lm;
|
|
15116
15529
|
try {
|
|
15117
|
-
lm = await this.getModel(provider, model);
|
|
15530
|
+
lm = await this.getModel(this.routeOf(provider, entry), model);
|
|
15118
15531
|
} catch {
|
|
15119
15532
|
return false;
|
|
15120
15533
|
}
|
|
@@ -15135,6 +15548,21 @@ var ProviderRouter = class {
|
|
|
15135
15548
|
});
|
|
15136
15549
|
}
|
|
15137
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
|
+
}
|
|
15138
15566
|
var STATIC_MODEL_CATALOG = {
|
|
15139
15567
|
openai: [
|
|
15140
15568
|
"gpt-4o",
|
|
@@ -15195,6 +15623,9 @@ var PROVIDER_DISCOVERY = {
|
|
|
15195
15623
|
perplexity: { baseUrl: "https://api.perplexity.ai", auth: "bearer" },
|
|
15196
15624
|
togetherai: { baseUrl: "https://api.together.xyz/v1", auth: "bearer" }
|
|
15197
15625
|
};
|
|
15626
|
+
function discoverySpec(provider) {
|
|
15627
|
+
return PROVIDER_DISCOVERY[provider];
|
|
15628
|
+
}
|
|
15198
15629
|
function cachePath(homeDir, provider) {
|
|
15199
15630
|
return join(homeDir, "cache", `models_${provider}.json`);
|
|
15200
15631
|
}
|
|
@@ -15223,14 +15654,14 @@ function saveCache2(homeDir, provider, models) {
|
|
|
15223
15654
|
}
|
|
15224
15655
|
}
|
|
15225
15656
|
function modelListCandidates(baseUrl) {
|
|
15226
|
-
const
|
|
15227
|
-
if (!
|
|
15228
|
-
const alt =
|
|
15229
|
-
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];
|
|
15230
15661
|
}
|
|
15231
|
-
function buildRequest(
|
|
15662
|
+
function buildRequest(base2, auth, apiKey) {
|
|
15232
15663
|
const headers = { Accept: "application/json" };
|
|
15233
|
-
let url = `${
|
|
15664
|
+
let url = `${base2}/models`;
|
|
15234
15665
|
if (auth === "x-api-key") {
|
|
15235
15666
|
headers["x-api-key"] = apiKey;
|
|
15236
15667
|
headers["anthropic-version"] = "2023-06-01";
|
|
@@ -15253,9 +15684,9 @@ function parseModelList(payload) {
|
|
|
15253
15684
|
return [...ids, ...names].filter((v) => typeof v === "string" && v.length > 0).map((id) => id.startsWith("models/") ? id.slice("models/".length) : id);
|
|
15254
15685
|
}
|
|
15255
15686
|
async function fetchFromApi(baseUrl, apiKey, auth) {
|
|
15256
|
-
for (const
|
|
15687
|
+
for (const base2 of modelListCandidates(baseUrl)) {
|
|
15257
15688
|
try {
|
|
15258
|
-
const { url, headers } = buildRequest(
|
|
15689
|
+
const { url, headers } = buildRequest(base2, auth, apiKey);
|
|
15259
15690
|
const resp = await fetch(url, { headers, signal: AbortSignal.timeout(1e4) });
|
|
15260
15691
|
if (!resp.ok) continue;
|
|
15261
15692
|
const models = parseModelList(await resp.json());
|
|
@@ -15268,8 +15699,8 @@ async function fetchFromApi(baseUrl, apiKey, auth) {
|
|
|
15268
15699
|
}
|
|
15269
15700
|
async function fetchOllamaModels() {
|
|
15270
15701
|
try {
|
|
15271
|
-
const
|
|
15272
|
-
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) });
|
|
15273
15704
|
if (!resp.ok) return [];
|
|
15274
15705
|
const data = await resp.json();
|
|
15275
15706
|
return (data.models ?? []).map((m) => m.name).filter((n) => !!n);
|
|
@@ -15285,9 +15716,9 @@ async function discoverModels(homeDir, provider, apiKey, baseUrl) {
|
|
|
15285
15716
|
}
|
|
15286
15717
|
if (apiKey) {
|
|
15287
15718
|
const spec = PROVIDER_DISCOVERY[provider];
|
|
15288
|
-
const
|
|
15289
|
-
if (
|
|
15290
|
-
const models = await fetchFromApi(
|
|
15719
|
+
const base2 = baseUrl?.trim() || spec?.baseUrl || "";
|
|
15720
|
+
if (base2) {
|
|
15721
|
+
const models = await fetchFromApi(base2, apiKey, spec?.auth ?? "bearer");
|
|
15291
15722
|
if (models) {
|
|
15292
15723
|
saveCache2(homeDir, provider, models);
|
|
15293
15724
|
return { models, source: "live" };
|
|
@@ -15305,6 +15736,102 @@ function clearModelCache(homeDir, provider) {
|
|
|
15305
15736
|
} catch {
|
|
15306
15737
|
}
|
|
15307
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
|
+
}
|
|
15308
15835
|
var SETTING_WRITE_LAYERS = ["user", "projectLocal"];
|
|
15309
15836
|
function isSettingWriteLayer(value) {
|
|
15310
15837
|
return SETTING_WRITE_LAYERS.includes(value);
|
|
@@ -15429,6 +15956,25 @@ function writeSettingValue(req) {
|
|
|
15429
15956
|
}
|
|
15430
15957
|
return req.layer === "user" ? writeUserYaml(req.path, req.key, value) : writeSettingsJson(req.path, req.key, value);
|
|
15431
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
|
+
}
|
|
15432
15978
|
function writeUserYaml(path, key, value) {
|
|
15433
15979
|
const text = existsSync(path) ? readFileSync(path, "utf-8") : "";
|
|
15434
15980
|
const before = asMapping(parseYaml(text));
|
|
@@ -18819,7 +19365,7 @@ var GoalService = class {
|
|
|
18819
19365
|
}
|
|
18820
19366
|
};
|
|
18821
19367
|
// src/goal/provider.ts
|
|
18822
|
-
function withGoalPrompt(
|
|
19368
|
+
function withGoalPrompt(base2, view) {
|
|
18823
19369
|
const decorate = (input) => {
|
|
18824
19370
|
const now = view();
|
|
18825
19371
|
if (!now) return input;
|
|
@@ -18828,9 +19374,9 @@ function withGoalPrompt(base, view) {
|
|
|
18828
19374
|
|
|
18829
19375
|
${block}` } : input;
|
|
18830
19376
|
};
|
|
18831
|
-
const wrapped = Object.create(
|
|
18832
|
-
wrapped.generate = (input) =>
|
|
18833
|
-
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));
|
|
18834
19380
|
return wrapped;
|
|
18835
19381
|
}
|
|
18836
19382
|
// src/goal/engine.ts
|
|
@@ -19339,9 +19885,9 @@ function matchPreauthorization(policy, target) {
|
|
|
19339
19885
|
}
|
|
19340
19886
|
return null;
|
|
19341
19887
|
}
|
|
19342
|
-
function mergeHeadlessPolicy(
|
|
19888
|
+
function mergeHeadlessPolicy(base2, override) {
|
|
19343
19889
|
return {
|
|
19344
|
-
...
|
|
19890
|
+
...base2,
|
|
19345
19891
|
...override?.allowTools ? { allowTools: override.allowTools } : {},
|
|
19346
19892
|
...override?.allowOperations ? { allowOperations: override.allowOperations } : {}
|
|
19347
19893
|
};
|
|
@@ -21097,14 +21643,14 @@ var TextGuard = class {
|
|
|
21097
21643
|
}
|
|
21098
21644
|
consume(unit) {
|
|
21099
21645
|
const hits = this.host.scan(this.host.skipCodeBlocks ? this.blankCode(unit) : unit);
|
|
21100
|
-
const
|
|
21646
|
+
const base2 = this.consumed;
|
|
21101
21647
|
this.consumed += unit.length;
|
|
21102
21648
|
if (hits.length === 0) {
|
|
21103
21649
|
this.out += unit;
|
|
21104
21650
|
return unit;
|
|
21105
21651
|
}
|
|
21106
21652
|
for (const hit of hits) {
|
|
21107
|
-
this.hits.push({ ...hit, start: hit.start +
|
|
21653
|
+
this.hits.push({ ...hit, start: hit.start + base2, end: hit.end + base2 });
|
|
21108
21654
|
}
|
|
21109
21655
|
const action = strongestOf(hits);
|
|
21110
21656
|
if (action === "block") {
|
|
@@ -21729,7 +22275,7 @@ function loadLexiconDir(dir, source) {
|
|
|
21729
22275
|
}
|
|
21730
22276
|
const out = { rules: [], exempt: [], issues: [] };
|
|
21731
22277
|
for (const file of files.sort()) {
|
|
21732
|
-
const spec =
|
|
22278
|
+
const spec = classify2(file);
|
|
21733
22279
|
if (!spec) {
|
|
21734
22280
|
out.issues.push({
|
|
21735
22281
|
path: file,
|
|
@@ -21753,14 +22299,14 @@ function loadLexiconDir(dir, source) {
|
|
|
21753
22299
|
}
|
|
21754
22300
|
return out;
|
|
21755
22301
|
}
|
|
21756
|
-
function
|
|
21757
|
-
const
|
|
21758
|
-
if (
|
|
21759
|
-
if (
|
|
21760
|
-
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);
|
|
21761
22307
|
return CATEGORY_SET.has(category) ? { kind: "regex", category } : null;
|
|
21762
22308
|
}
|
|
21763
|
-
return CATEGORY_SET.has(
|
|
22309
|
+
return CATEGORY_SET.has(base2) ? { kind: "terms", category: base2 } : null;
|
|
21764
22310
|
}
|
|
21765
22311
|
function collect3(content, file, spec, source, out) {
|
|
21766
22312
|
const lines = content.split("\n");
|
|
@@ -22859,8 +23405,8 @@ function copyTree(from, to, label, budget, issues, lang) {
|
|
|
22859
23405
|
return left;
|
|
22860
23406
|
}
|
|
22861
23407
|
function defaultName(source) {
|
|
22862
|
-
const
|
|
22863
|
-
return extname(
|
|
23408
|
+
const base2 = basename(source);
|
|
23409
|
+
return extname(base2).toLowerCase() === ".md" ? base2.slice(0, -3) : base2;
|
|
22864
23410
|
}
|
|
22865
23411
|
function skillSegmentIssue(segment, lang) {
|
|
22866
23412
|
if (segment.length === 0) return t("skill.import_bad_name_empty", void 0, lang);
|
|
@@ -23043,8 +23589,8 @@ function loadOne(mdFile, catPath, category, dirName, spec, skills, issues) {
|
|
|
23043
23589
|
const parsed = parseSkillFrontmatter(raw, label);
|
|
23044
23590
|
issues.push(...parsed.issues);
|
|
23045
23591
|
const declared = parsed.fm.name?.trim();
|
|
23046
|
-
const
|
|
23047
|
-
const name = spec.prefix ? `${spec.prefix}:${
|
|
23592
|
+
const base2 = declared ? declared : dirName;
|
|
23593
|
+
const name = spec.prefix ? `${spec.prefix}:${base2}` : base2;
|
|
23048
23594
|
const previous = skills.get(name);
|
|
23049
23595
|
if (previous && previous.scope !== scope) {
|
|
23050
23596
|
issues.push({
|
|
@@ -23201,9 +23747,9 @@ var SkillSystem = class {
|
|
|
23201
23747
|
const indexName = declared ? declared : name;
|
|
23202
23748
|
if (this.skills.has(indexName)) throw new Error(`Skill "${indexName}" already exists`);
|
|
23203
23749
|
const dir = join(this.skillsDir, category, indexName);
|
|
23204
|
-
const
|
|
23750
|
+
const base2 = resolve(this.skillsDir);
|
|
23205
23751
|
const resolved = resolve(dir);
|
|
23206
|
-
if (resolved ===
|
|
23752
|
+
if (resolved === base2 || !resolved.startsWith(base2 + sep)) {
|
|
23207
23753
|
throw new Error(
|
|
23208
23754
|
`Skill name or category escapes the skills directory: ${category}/${indexName}`
|
|
23209
23755
|
);
|
|
@@ -23252,12 +23798,12 @@ var SkillSystem = class {
|
|
|
23252
23798
|
this.invalidateCache();
|
|
23253
23799
|
}
|
|
23254
23800
|
resolveInsideSkill(skillPath, relPath) {
|
|
23255
|
-
const
|
|
23801
|
+
const base2 = resolve(skillPath);
|
|
23256
23802
|
const resolved = resolve(join(skillPath, relPath));
|
|
23257
|
-
if (resolved !==
|
|
23803
|
+
if (resolved !== base2 && !resolved.startsWith(base2 + sep)) {
|
|
23258
23804
|
throw new Error("Path traversal denied");
|
|
23259
23805
|
}
|
|
23260
|
-
const realBase = realpathSync(
|
|
23806
|
+
const realBase = realpathSync(base2);
|
|
23261
23807
|
const realTarget = realpathOfDeepestExisting(resolved);
|
|
23262
23808
|
if (realTarget !== realBase && !realTarget.startsWith(realBase + sep)) {
|
|
23263
23809
|
throw new Error("Path traversal denied");
|
|
@@ -24281,7 +24827,7 @@ var ContextCompressor = class {
|
|
|
24281
24827
|
}
|
|
24282
24828
|
async generateSummary(middleText, instruction, plan, goal) {
|
|
24283
24829
|
try {
|
|
24284
|
-
const
|
|
24830
|
+
const base2 = this.lastSummary ? `Update the following summary with new conversation content. Keep the same structure.
|
|
24285
24831
|
|
|
24286
24832
|
## Previous Summary
|
|
24287
24833
|
${this.lastSummary}
|
|
@@ -24293,9 +24839,9 @@ ${SUMMARY_TEMPLATE}
|
|
|
24293
24839
|
|
|
24294
24840
|
## Conversation
|
|
24295
24841
|
${middleText}`;
|
|
24296
|
-
const withPlan = plan?.trim() ? `${
|
|
24842
|
+
const withPlan = plan?.trim() ? `${base2}
|
|
24297
24843
|
|
|
24298
|
-
${planSection(plan.trim())}` :
|
|
24844
|
+
${planSection(plan.trim())}` : base2;
|
|
24299
24845
|
const withGoal = goal?.trim() ? `${withPlan}
|
|
24300
24846
|
|
|
24301
24847
|
${goalSection(goal.trim())}` : withPlan;
|
|
@@ -24365,10 +24911,10 @@ var SCOPES = ["directory", "directory-tree"];
|
|
|
24365
24911
|
function describe14(err) {
|
|
24366
24912
|
return err instanceof Error ? err.message : String(err);
|
|
24367
24913
|
}
|
|
24368
|
-
function covers(
|
|
24369
|
-
if (samePath(
|
|
24914
|
+
function covers(base2, target, scope) {
|
|
24915
|
+
if (samePath(base2, target)) return true;
|
|
24370
24916
|
if (scope === "directory") return false;
|
|
24371
|
-
const rel = relative(
|
|
24917
|
+
const rel = relative(base2, target);
|
|
24372
24918
|
return !rel.startsWith("..") && !isAbsolute(rel);
|
|
24373
24919
|
}
|
|
24374
24920
|
var TrustManager = class {
|
|
@@ -24384,10 +24930,10 @@ var TrustManager = class {
|
|
|
24384
24930
|
let bestLen = -1;
|
|
24385
24931
|
let best;
|
|
24386
24932
|
for (const record of this.records) {
|
|
24387
|
-
const
|
|
24388
|
-
if (!covers(
|
|
24389
|
-
if (
|
|
24390
|
-
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;
|
|
24391
24937
|
best = record;
|
|
24392
24938
|
}
|
|
24393
24939
|
}
|
|
@@ -24883,4 +25429,4 @@ function trunc(stdout, stderr) {
|
|
|
24883
25429
|
* `--tree` 的命令帮助里,让用户自己知道 `--tree` 意味着什么。
|
|
24884
25430
|
*/
|
|
24885
25431
|
|
|
24886
|
-
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 };
|