dsh-more 0.0.5 → 0.0.6

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.
Files changed (27) hide show
  1. package/README.md +15 -7
  2. package/README.zh-CN.md +15 -7
  3. package/dist/client.js +810 -291
  4. package/dist/index.js +312 -131
  5. package/dist/types/generated/host-settings-schema.d.ts +4 -2
  6. package/dist/types/generated/patch-catalog.d.ts +12 -4
  7. package/dist/types/patches/model-capabilities/client/api.d.ts +4 -0
  8. package/dist/types/patches/model-capabilities/client/editor.d.ts +26 -0
  9. package/dist/types/patches/model-capabilities/client/index.d.ts +10 -0
  10. package/dist/types/patches/model-capabilities/client/model-entries.d.ts +15 -0
  11. package/dist/types/patches/model-capabilities/client/styles.d.ts +1 -0
  12. package/dist/types/patches/model-capabilities/host/deepseek.d.ts +11 -0
  13. package/dist/types/patches/{model-reasoning-efforts → model-capabilities}/host/validate.d.ts +3 -2
  14. package/dist/types/patches/model-capabilities/shared.d.ts +38 -0
  15. package/dist/types/patches/projectless-tasks/client/adapter.d.ts +15 -0
  16. package/dist/types/patches/projectless-tasks/client/create-task.d.ts +7 -0
  17. package/dist/types/patches/projectless-tasks/client/draft.d.ts +9 -0
  18. package/dist/types/patches/projectless-tasks/client/index.d.ts +2 -0
  19. package/dist/types/patches/projectless-tasks/client/new-session.d.ts +34 -0
  20. package/dist/types/patches/projectless-tasks/client/picker.d.ts +26 -0
  21. package/dist/types/patches/projectless-tasks/host/index.d.ts +5 -0
  22. package/dist/types/patches/projectless-tasks/shared.d.ts +6 -0
  23. package/dsh.plugin.json +1 -1
  24. package/package.json +1 -1
  25. package/dist/types/patches/model-reasoning-efforts/client/index.d.ts +0 -54
  26. package/dist/types/patches/model-reasoning-efforts/shared.d.ts +0 -51
  27. /package/dist/types/patches/{model-reasoning-efforts → model-capabilities}/host/index.d.ts +0 -0
package/dist/index.js CHANGED
@@ -977,20 +977,38 @@ var hostPatch4 = {
977
977
  })
978
978
  };
979
979
 
980
- // src/patches/model-reasoning-efforts/shared.ts
981
- var MODEL_REASONING_EFFORTS_PATCH_ID = "model-reasoning-efforts";
980
+ // src/patches/model-capabilities/host/index.ts
981
+ import { SettingsConflictError } from "@deepseek-ai/dsh-settings";
982
+
983
+ // src/patches/model-capabilities/shared.ts
984
+ var MODEL_CAPABILITIES_PATCH_ID = "model-capabilities";
982
985
  var LLM_PI_AI_SETTINGS_NAMESPACE = "llm-pi-ai";
986
+ var LLM_DEEPSEEK_SETTINGS_NAMESPACE = "llm-deepseek";
987
+ var DEEPSEEK_PROVIDER = "deepseek-official";
988
+ var CAPABILITIES_PROTOCOL_VERSION = 2;
989
+ var DEEPSEEK_THINKING_LEVELS = ["off", "low", "high", "max"];
983
990
  var THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
984
- var DEFAULT_REASONING_EFFORTS = {
985
- low: "low",
986
- medium: "medium",
987
- high: "high"
988
- };
991
+ var THINKING_FORMATS = [
992
+ "openai",
993
+ "deepseek",
994
+ "openrouter",
995
+ "together",
996
+ "baseten",
997
+ "zai",
998
+ "qwen",
999
+ "chat-template",
1000
+ "qwen-chat-template",
1001
+ "string-thinking",
1002
+ "ant-ling"
1003
+ ];
989
1004
 
990
- // src/patches/model-reasoning-efforts/host/validate.ts
1005
+ // src/patches/model-capabilities/host/deepseek.ts
1006
+ import { LlmError, ReasoningEffortId } from "@deepseek-ai/dsh-llm";
1007
+
1008
+ // src/patches/model-capabilities/host/validate.ts
991
1009
  var LEVEL_SET = new Set(THINKING_LEVELS);
992
1010
  function validateReasoningEfforts(raw) {
993
- if (raw === null || raw === void 0) return null;
1011
+ if (raw === null) return null;
994
1012
  if (raw === false) return false;
995
1013
  if (typeof raw !== "object" || Array.isArray(raw)) {
996
1014
  throw new DshMoreError("bad-request", "reasoningEfforts \u5FC5\u987B\u662F false\u3001\u601D\u8003\u7EA7\u522B\u5B57\u5178\u6216 null\u3002");
@@ -1017,135 +1035,276 @@ function validateReasoningEfforts(raw) {
1017
1035
  if (!offersThinking) throw new DshMoreError("bad-request", "\u9700\u8981\u81F3\u5C11\u58F0\u660E\u4E00\u4E2A\u975E off \u7684\u601D\u8003\u7EA7\u522B\u3002");
1018
1036
  return result;
1019
1037
  }
1038
+ function validateCapabilities(raw) {
1039
+ const { vision, thinkingFormat } = raw;
1040
+ if (vision !== null && typeof vision !== "boolean") {
1041
+ throw new DshMoreError("bad-request", "\u8BF7\u9009\u62E9\u8BC6\u56FE\u80FD\u529B\uFF1A\u6CBF\u7528\u9ED8\u8BA4\u3001\u652F\u6301\u6216\u4E0D\u652F\u6301\u3002");
1042
+ }
1043
+ if (thinkingFormat !== null && !THINKING_FORMATS.some((format) => format === thinkingFormat)) {
1044
+ throw new DshMoreError("bad-request", "\u8BF7\u9009\u62E9 DSH \u652F\u6301\u7684\u601D\u8003\u53C2\u6570\u683C\u5F0F\u3002");
1045
+ }
1046
+ return {
1047
+ vision,
1048
+ reasoningEfforts: validateReasoningEfforts(raw.reasoningEfforts),
1049
+ thinkingFormat
1050
+ };
1051
+ }
1020
1052
 
1021
- // src/patches/model-reasoning-efforts/host/index.ts
1022
- var LLM_PI_AI_NS = LLM_PI_AI_SETTINGS_NAMESPACE;
1023
- function piAiSection(ctx) {
1024
- const value = ctx.settings.get(LLM_PI_AI_NS);
1025
- if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
1026
- const providers = value.providers;
1027
- if (typeof providers !== "object" || providers === null || Array.isArray(providers)) return void 0;
1028
- return { providers };
1029
- }
1030
- function normalizeEfforts(value) {
1031
- if (value === false) return false;
1032
- if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
1033
- return value;
1053
+ // src/patches/model-capabilities/host/deepseek.ts
1054
+ function record(value) {
1055
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
1056
+ }
1057
+ function validateDeepSeekEfforts(efforts) {
1058
+ if (efforts === false || efforts === null) return;
1059
+ for (const [level, wire] of Object.entries(efforts)) {
1060
+ if (!DEEPSEEK_THINKING_LEVELS.some((item) => item === level) || wire !== level && !(level === "off" && wire === null)) {
1061
+ throw new Error("\u539F\u751F DeepSeek \u652F\u6301\u4E0D\u601D\u8003\u3001\u4F4E\u3001\u9AD8\u3001\u6700\u9AD8\uFF0C\u53C2\u6570\u503C\u56FA\u5B9A\u4E3A off\u3001low\u3001high\u3001max\u3002");
1062
+ }
1063
+ }
1064
+ }
1065
+ function ruleFor(ctx, provider, modelId) {
1066
+ if (provider !== DEEPSEEK_PROVIDER) return void 0;
1067
+ const profile = record(ctx.settings.get(LLM_DEEPSEEK_SETTINGS_NAMESPACE));
1068
+ const models = Array.isArray(profile.models) ? profile.models : [];
1069
+ const model = record(models.find((item) => record(item).id === modelId));
1070
+ if (model.reasoningEfforts === void 0) return void 0;
1071
+ const efforts = validateReasoningEfforts(model.reasoningEfforts);
1072
+ validateDeepSeekEfforts(efforts);
1073
+ if (efforts === null) return void 0;
1074
+ const levels = efforts === false ? ["off"] : DEEPSEEK_THINKING_LEVELS.filter((level) => Object.hasOwn(efforts, level));
1075
+ if (profile.thinking === "disabled" && levels.some((level) => level !== "off")) {
1076
+ throw new LlmError("DeepSeek \u63D0\u4F9B\u65B9\u5DF2\u5173\u95ED\u601D\u8003\uFF0C\u8BF7\u5148\u8C03\u6574\u63D0\u4F9B\u65B9\u914D\u7F6E\u3002", "UNSUPPORTED_REASONING_EFFORT");
1077
+ }
1078
+ const preferred = typeof profile.reasoningEffort === "string" ? profile.reasoningEffort : "high";
1079
+ return { levels, defaultEffort: levels.includes(preferred) ? preferred : levels[0] };
1080
+ }
1081
+ function revise(config, rule) {
1082
+ if (rule === void 0) return config;
1083
+ const effort = config.reasoningEffort ?? ReasoningEffortId(rule.defaultEffort);
1084
+ if (!rule.levels.includes(effort)) {
1085
+ throw new LlmError(`\u6A21\u578B ${config.model} \u672A\u914D\u7F6E\u601D\u8003\u7C7B\u578B ${effort}\uFF0C\u8BF7\u91CD\u65B0\u9009\u62E9\u601D\u8003\u7C7B\u578B\u3002`, "UNSUPPORTED_REASONING_EFFORT");
1086
+ }
1087
+ return { ...config, reasoningEffort: effort };
1088
+ }
1089
+ function reasoning(rule) {
1090
+ return {
1091
+ efforts: rule.levels.map((level) => ({ id: ReasoningEffortId(level), name: level })),
1092
+ defaultEffort: ReasoningEffortId(rule.defaultEffort)
1093
+ };
1094
+ }
1095
+ function installDeepSeekReasoning(ctx) {
1096
+ let active = true;
1097
+ let cleanup = () => {
1098
+ };
1099
+ const fiber = ctx.inject(["llm", "settings"], (scope) => {
1100
+ if (!active) return;
1101
+ const llm = scope.llm;
1102
+ const descriptors = {
1103
+ resolveModelInfo: Object.getOwnPropertyDescriptor(llm, "resolveModelInfo"),
1104
+ resolveCallConfig: Object.getOwnPropertyDescriptor(llm, "resolveCallConfig"),
1105
+ prepareCall: Object.getOwnPropertyDescriptor(llm, "prepareCall"),
1106
+ stream: Object.getOwnPropertyDescriptor(llm, "stream")
1107
+ };
1108
+ const originalInfo = llm.resolveModelInfo;
1109
+ const originalResolve = llm.resolveCallConfig;
1110
+ const originalPrepare = llm.prepareCall;
1111
+ const originalStream = llm.stream;
1112
+ const info = async (provider, model, signal) => {
1113
+ const rule = active ? ruleFor(scope, provider, model) : void 0;
1114
+ const result = await originalInfo.call(llm, provider, model, signal);
1115
+ return rule === void 0 ? result : { ...result, reasoning: reasoning(rule) };
1116
+ };
1117
+ const resolve = async (config, signal) => originalResolve.call(llm, revise(config, active ? ruleFor(scope, config.provider, config.model) : void 0), signal);
1118
+ const prepare = async (config, signal) => {
1119
+ const revised = revise(config, active ? ruleFor(scope, config.provider, config.model) : void 0);
1120
+ const prepared = await originalPrepare.call(llm, revised, signal);
1121
+ if (config.reasoningEffort !== void 0 || revised.reasoningEffort === void 0) return prepared;
1122
+ return Object.freeze({ ...prepared, adapterDefaults: Object.freeze({ ...prepared.adapterDefaults, reasoningEffort: true }) });
1123
+ };
1124
+ const stream = (options) => {
1125
+ try {
1126
+ return originalStream.call(llm, revise(options, active ? ruleFor(scope, options.provider, options.model) : void 0));
1127
+ } catch (error) {
1128
+ if (!(error instanceof LlmError)) throw error;
1129
+ return (async function* () {
1130
+ yield { type: "finish", reason: { kind: "error", failure: { code: error.code, message: error.message } } };
1131
+ })();
1132
+ }
1133
+ };
1134
+ llm.resolveModelInfo = info;
1135
+ llm.resolveCallConfig = resolve;
1136
+ llm.prepareCall = prepare;
1137
+ llm.stream = stream;
1138
+ cleanup = () => {
1139
+ const restore = (name2, installed) => {
1140
+ if (Object.getOwnPropertyDescriptor(llm, name2)?.value !== installed) return;
1141
+ const descriptor = descriptors[name2];
1142
+ if (descriptor === void 0) Reflect.deleteProperty(llm, name2);
1143
+ else Object.defineProperty(llm, name2, descriptor);
1144
+ };
1145
+ restore("resolveModelInfo", info);
1146
+ restore("resolveCallConfig", resolve);
1147
+ restore("prepareCall", prepare);
1148
+ restore("stream", stream);
1149
+ };
1150
+ scope.effect(() => cleanup);
1151
+ });
1152
+ return () => {
1153
+ active = false;
1154
+ cleanup();
1155
+ void fiber.dispose();
1156
+ };
1157
+ }
1158
+
1159
+ // src/patches/model-capabilities/host/index.ts
1160
+ function record2(value) {
1161
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
1162
+ }
1163
+ function modelsOf(profile) {
1164
+ return Array.isArray(profile.models) ? profile.models.map(record2) : [];
1165
+ }
1166
+ function capabilitiesOf(model, native = false) {
1167
+ const input = native ? model.inputModalities : model.input;
1168
+ const format = record2(model.compat).thinkingFormat;
1169
+ return {
1170
+ vision: Array.isArray(input) && input.length > 0 ? input.includes("image") : null,
1171
+ reasoningEfforts: model.reasoningEfforts === void 0 ? null : validateReasoningEfforts(model.reasoningEfforts),
1172
+ thinkingFormat: THINKING_FORMATS.find((entry) => entry === format) ?? null
1173
+ };
1034
1174
  }
1035
1175
  function read(ctx) {
1036
- const section = piAiSection(ctx);
1176
+ const descriptor = ctx.settings.describe().find((entry) => entry.ns === LLM_PI_AI_SETTINGS_NAMESPACE);
1037
1177
  const rows = [];
1038
- if (section !== void 0) {
1039
- for (const [provider, profile] of Object.entries(section.providers)) {
1040
- if (typeof profile !== "object" || profile === null || Array.isArray(profile)) continue;
1041
- const models = profile.models;
1042
- if (!Array.isArray(models)) continue;
1043
- const displayName = profile.displayName;
1044
- const providerName = typeof displayName === "string" && displayName.trim() !== "" ? displayName : provider;
1045
- for (const model of models) {
1046
- if (typeof model !== "object" || model === null || Array.isArray(model)) continue;
1047
- const modelId = model.id;
1048
- if (typeof modelId !== "string" || modelId === "") continue;
1049
- rows.push({
1050
- provider,
1051
- providerName,
1052
- modelId,
1053
- reasoningEfforts: normalizeEfforts(model.reasoningEfforts)
1054
- });
1055
- }
1178
+ const providers = record2(record2(descriptor?.value).providers);
1179
+ for (const [provider, raw] of Object.entries(providers)) {
1180
+ const profile = record2(raw);
1181
+ for (const model of modelsOf(profile)) {
1182
+ if (typeof model.id !== "string" || model.id === "") continue;
1183
+ const api = model.api ?? profile.api;
1184
+ rows.push({
1185
+ namespace: LLM_PI_AI_SETTINGS_NAMESPACE,
1186
+ revision: descriptor.revision,
1187
+ provider,
1188
+ providerName: typeof profile.displayName === "string" ? profile.displayName : provider,
1189
+ modelId: model.id,
1190
+ api: typeof api === "string" ? api : null,
1191
+ ...capabilitiesOf(model)
1192
+ });
1056
1193
  }
1057
1194
  }
1058
- return {
1059
- rows,
1060
- writable: ctx.settings.writable,
1061
- documentPath: ctx.settings.documentPath
1062
- };
1195
+ const native = ctx.settings.describe().find((entry) => entry.ns === LLM_DEEPSEEK_SETTINGS_NAMESPACE);
1196
+ for (const model of modelsOf(record2(native?.value))) {
1197
+ if (typeof model.id !== "string" || model.id === "") continue;
1198
+ rows.push({
1199
+ namespace: LLM_DEEPSEEK_SETTINGS_NAMESPACE,
1200
+ revision: native.revision,
1201
+ provider: DEEPSEEK_PROVIDER,
1202
+ providerName: "DeepSeek",
1203
+ modelId: model.id,
1204
+ api: "deepseek-native",
1205
+ ...capabilitiesOf(model, true)
1206
+ });
1207
+ }
1208
+ return { protocolVersion: CAPABILITIES_PROTOCOL_VERSION, rows, writable: ctx.settings.writable, revision: descriptor?.revision ?? null };
1063
1209
  }
1064
1210
  async function save(ctx, payload) {
1211
+ const namespace = requireString(payload, "namespace");
1212
+ if (namespace !== LLM_PI_AI_SETTINGS_NAMESPACE && namespace !== LLM_DEEPSEEK_SETTINGS_NAMESPACE) throw new DshMoreError("bad-request", "\u672A\u77E5\u7684\u6A21\u578B\u914D\u7F6E\u6765\u6E90\uFF0C\u8BF7\u5237\u65B0\u9875\u9762\u3002");
1065
1213
  const provider = requireString(payload, "provider");
1066
1214
  const modelId = requireString(payload, "modelId");
1067
- const reasoningEfforts = validateReasoningEfforts(payload.reasoningEfforts);
1068
- const section = piAiSection(ctx);
1069
- if (section === void 0) throw new DshMoreError("bad-request", "llm-pi-ai \u914D\u7F6E\u5C1A\u672A\u6CE8\u518C\uFF0C\u65E0\u6CD5\u4FDD\u5B58\u601D\u8003\u5F3A\u5EA6\u3002");
1070
- const profile = section.providers[provider];
1071
- if (typeof profile !== "object" || profile === null || Array.isArray(profile)) {
1072
- throw new DshMoreError("bad-request", `\u672A\u627E\u5230\u63D0\u4F9B\u65B9 ${provider} \u7684\u914D\u7F6E\u3002`);
1073
- }
1074
- const models = profile.models;
1075
- if (!Array.isArray(models)) throw new DshMoreError("bad-request", `\u63D0\u4F9B\u65B9 ${provider} \u672A\u914D\u7F6E\u4EFB\u4F55\u6A21\u578B\u3002`);
1076
- const index = models.findIndex((model) => {
1077
- if (typeof model !== "object" || model === null) return false;
1078
- return model.id === modelId;
1079
- });
1080
- if (index === -1) throw new DshMoreError("bad-request", `\u63D0\u4F9B\u65B9 ${provider} \u4E0B\u672A\u627E\u5230\u6A21\u578B ${modelId}\u3002`);
1081
- const descriptor = ctx.settings.describe().find((entry) => entry.ns === LLM_PI_AI_NS);
1082
- if (descriptor === void 0) throw new DshMoreError("bad-request", "llm-pi-ai \u914D\u7F6E\u5C1A\u672A\u6CE8\u518C\uFF0C\u65E0\u6CD5\u4FDD\u5B58\u601D\u8003\u5F3A\u5EA6\u3002");
1083
- const user = descriptor.user;
1084
- const userProviders = typeof user === "object" && user !== null && !Array.isArray(user) ? user.providers : void 0;
1085
- const providers = typeof userProviders === "object" && userProviders !== null && !Array.isArray(userProviders) ? { ...userProviders } : {};
1086
- const userProfile = providers[provider];
1087
- const userModels = typeof userProfile === "object" && userProfile !== null && !Array.isArray(userProfile) ? userProfile.models : void 0;
1088
- const storedModels = Array.isArray(userModels) ? [...userModels] : [];
1089
- const storedIndex = storedModels.findIndex((model) => {
1090
- if (typeof model !== "object" || model === null) return false;
1091
- return model.id === modelId;
1092
- });
1093
- if (storedIndex === -1 && reasoningEfforts === null) {
1094
- return { provider, modelId, reasoningEfforts };
1095
- }
1096
- const stored = storedIndex === -1 ? { id: modelId } : storedModels[storedIndex];
1097
- let nextModel;
1098
- if (reasoningEfforts === null) {
1099
- const { reasoningEfforts: _dropped, ...rest } = stored;
1100
- nextModel = rest;
1101
- } else {
1102
- nextModel = { ...stored, reasoningEfforts };
1215
+ const expectedRevision = requireInteger(payload, "expectedRevision");
1216
+ if (expectedRevision < 0) throw new DshMoreError("bad-request", "\u914D\u7F6E\u7248\u672C\u65E0\u6548\uFF0C\u8BF7\u91CD\u65B0\u6253\u5F00\u6A21\u578B\u80FD\u529B\u3002");
1217
+ const capabilities = validateCapabilities(record2(payload));
1218
+ if (!ctx.settings.writable) throw new DshMoreError("forbidden", "\u5F53\u524D\u914D\u7F6E\u4E3A\u53EA\u8BFB\uFF0C\u65E0\u6CD5\u4FDD\u5B58\u4FEE\u6539\u3002", 403);
1219
+ const descriptor = ctx.settings.describe().find((entry) => entry.ns === namespace);
1220
+ if (descriptor === void 0) throw new DshMoreError("bad-request", "\u6A21\u578B\u914D\u7F6E\u5C1A\u672A\u5C31\u7EEA\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002");
1221
+ if (descriptor.revision !== expectedRevision) throw staleSettings();
1222
+ if (namespace === LLM_DEEPSEEK_SETTINGS_NAMESPACE) {
1223
+ if (provider !== DEEPSEEK_PROVIDER) throw new DshMoreError("bad-request", "DeepSeek \u63D0\u4F9B\u65B9\u4E0D\u5339\u914D\u3002");
1224
+ if (capabilities.thinkingFormat !== null) throw new DshMoreError("bad-request", "\u539F\u751F DeepSeek \u4F7F\u7528\u56FA\u5B9A\u7684\u601D\u8003\u534F\u8BAE\uFF0C\u65E0\u9700\u914D\u7F6E\u53C2\u6570\u683C\u5F0F\u3002");
1225
+ try {
1226
+ validateDeepSeekEfforts(capabilities.reasoningEfforts);
1227
+ } catch (error) {
1228
+ throw new DshMoreError("bad-request", error instanceof Error ? error.message : "DeepSeek \u601D\u8003\u7C7B\u578B\u65E0\u6548\u3002");
1229
+ }
1230
+ const profile2 = record2(descriptor.value);
1231
+ if (profile2.thinking === "disabled" && capabilities.reasoningEfforts !== null && capabilities.reasoningEfforts !== false) {
1232
+ throw new DshMoreError("bad-request", "DeepSeek \u63D0\u4F9B\u65B9\u5DF2\u5173\u95ED\u601D\u8003\uFF0C\u8BF7\u5148\u8C03\u6574\u63D0\u4F9B\u65B9\u914D\u7F6E\u3002");
1233
+ }
1234
+ const user = record2(descriptor.user);
1235
+ const base = record2(descriptor.base);
1236
+ const source2 = Array.isArray(user.models) ? user : Array.isArray(base.models) ? base : profile2;
1237
+ const models = modelsOf(source2);
1238
+ const matches2 = models.filter((model2) => model2.id === modelId);
1239
+ if (matches2.length !== 1) throw new DshMoreError("bad-request", "\u8BF7\u5148\u4FDD\u5B58\u6A21\u578B\u914D\u7F6E\uFF0C\u5E76\u786E\u4FDD\u6A21\u578B\u540D\u79F0\u552F\u4E00\u3002");
1240
+ const next2 = { ...matches2[0] };
1241
+ if (capabilities.vision === null) delete next2.inputModalities;
1242
+ else next2.inputModalities = capabilities.vision ? ["text", "image"] : ["text"];
1243
+ if (capabilities.vision !== true) {
1244
+ delete next2.imagePixelBudget;
1245
+ delete next2.imageMaxBytes;
1246
+ }
1247
+ if (capabilities.reasoningEfforts === null) delete next2.reasoningEfforts;
1248
+ else next2.reasoningEfforts = capabilities.reasoningEfforts;
1249
+ await mutate(ctx, namespace, [{ op: "set", path: ["models"], value: models.map((model2) => model2.id === modelId ? next2 : model2) }], expectedRevision);
1250
+ return read(ctx);
1103
1251
  }
1104
- const nextModels = storedIndex === -1 ? [...storedModels, nextModel] : storedModels.map((model, modelIndex) => modelIndex === storedIndex ? nextModel : model);
1105
- const nextProfile = {
1106
- ...typeof userProfile === "object" && userProfile !== null && !Array.isArray(userProfile) ? userProfile : {},
1107
- models: nextModels
1108
- };
1109
- const ops = [{ op: "set", path: ["providers"], value: { ...providers, [provider]: nextProfile } }];
1110
- await ctx.settings.mutate(LLM_PI_AI_NS, ops, descriptor.revision);
1111
- return { provider, modelId, reasoningEfforts };
1112
- }
1113
- async function fillDefaults(ctx) {
1114
- const descriptor = ctx.settings.describe().find((entry) => entry.ns === LLM_PI_AI_NS);
1115
- if (descriptor === void 0) return { filled: 0 };
1116
- const user = descriptor.user;
1117
- const userProviders = typeof user === "object" && user !== null && !Array.isArray(user) ? user.providers : void 0;
1118
- const providers = typeof userProviders === "object" && userProviders !== null && !Array.isArray(userProviders) ? { ...userProviders } : {};
1119
- let filled = 0;
1120
- const nextProviders = { ...providers };
1121
- for (const [route, profile] of Object.entries(providers)) {
1122
- if (typeof profile !== "object" || profile === null || Array.isArray(profile)) continue;
1123
- const models = profile.models;
1124
- if (!Array.isArray(models)) continue;
1125
- let changed = false;
1126
- const nextModels = models.map((model) => {
1127
- if (typeof model !== "object" || model === null) return model;
1128
- const entry = model;
1129
- if ("reasoningEfforts" in entry) return model;
1130
- changed = true;
1131
- filled += 1;
1132
- return { ...entry, reasoningEfforts: { ...DEFAULT_REASONING_EFFORTS } };
1133
- });
1134
- if (!changed) continue;
1135
- nextProviders[route] = { ...profile, models: nextModels };
1252
+ const providers = record2(record2(descriptor.value).providers);
1253
+ if (!Object.hasOwn(providers, provider)) throw new DshMoreError("not-found", "\u8BE5\u63D0\u4F9B\u65B9\u5DF2\u4E0D\u5B58\u5728\uFF0C\u8BF7\u91CD\u65B0\u6253\u5F00\u6A21\u578B\u914D\u7F6E\u3002", 404);
1254
+ const profile = record2(providers[provider]);
1255
+ const matches = modelsOf(profile).filter((entry) => entry.id === modelId);
1256
+ const model = matches[0];
1257
+ if (matches.length !== 1 || model === void 0) {
1258
+ throw new DshMoreError("bad-request", "\u8BF7\u5148\u4FDD\u5B58\u63D0\u4F9B\u65B9\u914D\u7F6E\uFF0C\u5E76\u786E\u4FDD\u6A21\u578B\u540D\u79F0\u552F\u4E00\u3002");
1259
+ }
1260
+ const api = model.api ?? profile.api;
1261
+ if (capabilities.thinkingFormat !== null && api !== void 0 && api !== "openai-completions") {
1262
+ throw new DshMoreError("bad-request", "\u8BE5\u63A5\u53E3\u4F7F\u7528\u539F\u751F\u601D\u8003\u534F\u8BAE\uFF0C\u65E0\u9700\u6307\u5B9A\u601D\u8003\u53C2\u6570\u683C\u5F0F\u3002");
1263
+ }
1264
+ const userProviders = record2(record2(descriptor.user).providers);
1265
+ const userProfile = record2(Object.hasOwn(userProviders, provider) ? userProviders[provider] : void 0);
1266
+ const baseProfile = record2(record2(record2(descriptor.base).providers)[provider]);
1267
+ const source = Array.isArray(userProfile.models) ? userProfile : baseProfile;
1268
+ const storedModels = modelsOf(source);
1269
+ const index = storedModels.findIndex((entry) => entry.id === modelId);
1270
+ if (index < 0) throw new DshMoreError("bad-request", "\u672A\u627E\u5230\u5DF2\u4FDD\u5B58\u7684\u6A21\u578B\uFF0C\u8BF7\u5148\u4FDD\u5B58\u63D0\u4F9B\u65B9\u914D\u7F6E\u3002");
1271
+ const next = { ...storedModels[index] };
1272
+ if (capabilities.vision === null) delete next.input;
1273
+ else {
1274
+ const input = Array.isArray(model.input) ? model.input.filter((entry) => entry !== "image") : ["text"];
1275
+ if (!input.includes("text")) input.unshift("text");
1276
+ next.input = capabilities.vision ? [...input, "image"] : input;
1136
1277
  }
1137
- if (filled === 0) return { filled: 0 };
1138
- const ops = [{ op: "set", path: ["providers"], value: nextProviders }];
1139
- await ctx.settings.mutate(LLM_PI_AI_NS, ops, descriptor.revision);
1140
- return { filled };
1278
+ if (capabilities.reasoningEfforts === null) delete next.reasoningEfforts;
1279
+ else next.reasoningEfforts = capabilities.reasoningEfforts;
1280
+ const compat = { ...record2(next.compat) };
1281
+ if (capabilities.thinkingFormat === null) delete compat.thinkingFormat;
1282
+ else compat.thinkingFormat = capabilities.thinkingFormat;
1283
+ if (Object.keys(compat).length === 0) delete next.compat;
1284
+ else next.compat = compat;
1285
+ const nextModels = storedModels.map((entry, position) => position === index ? next : entry);
1286
+ await mutate(ctx, namespace, [{
1287
+ op: "set",
1288
+ path: ["providers"],
1289
+ value: { ...userProviders, [provider]: { ...userProfile, models: nextModels } }
1290
+ }], expectedRevision);
1291
+ return read(ctx);
1292
+ }
1293
+ async function mutate(ctx, namespace, ops, revision) {
1294
+ try {
1295
+ await ctx.settings.mutate(namespace, ops, revision);
1296
+ } catch (error) {
1297
+ if (error instanceof SettingsConflictError) throw staleSettings();
1298
+ throw new DshMoreError("bad-request", "\u6A21\u578B\u914D\u7F6E\u672A\u4FDD\u5B58\uFF0C\u8BF7\u68C0\u67E5\u601D\u8003\u7C7B\u578B\u3001\u63A5\u53E3\u683C\u5F0F\u53CA\u63D0\u4F9B\u65B9\u8BBE\u7F6E\u3002");
1299
+ }
1300
+ }
1301
+ function staleSettings() {
1302
+ return new DshMoreError("stale-preview", "\u6A21\u578B\u914D\u7F6E\u5DF2\u53D8\u66F4\uFF0C\u8BF7\u5173\u95ED\u540E\u91CD\u65B0\u6253\u5F00\u6A21\u578B\u80FD\u529B\uFF0C\u518D\u4FDD\u5B58\u4FEE\u6539\u3002", 409);
1141
1303
  }
1142
1304
  var hostPatch5 = {
1143
- id: MODEL_REASONING_EFFORTS_PATCH_ID,
1144
- routes: ({ ctx }) => ({
1145
- read: () => read(ctx),
1146
- save: (payload) => save(ctx, payload),
1147
- fillDefaults: () => fillDefaults(ctx)
1148
- })
1305
+ id: MODEL_CAPABILITIES_PATCH_ID,
1306
+ setup: installDeepSeekReasoning,
1307
+ routes: ({ ctx }) => ({ read: () => read(ctx), save: (payload) => save(ctx, payload) })
1149
1308
  };
1150
1309
 
1151
1310
  // src/patches/session-auto-title/host/index.ts
@@ -1229,8 +1388,8 @@ var hostPatch6 = {
1229
1388
  async function* dispatch() {
1230
1389
  if (revised.reasoningEffort === void 0) {
1231
1390
  const info = await scope.llm.resolveModelInfo(revised.provider, revised.model, signal);
1232
- const reasoning = info.reasoning;
1233
- const effort = reasoning?.defaultEffort ?? (reasoning?.efforts.some((item) => item.id === "off") ? void 0 : reasoning?.efforts[0]?.id);
1391
+ const reasoning2 = info.reasoning;
1392
+ const effort = reasoning2?.defaultEffort ?? (reasoning2?.efforts.some((item) => item.id === "off") ? void 0 : reasoning2?.efforts[0]?.id);
1234
1393
  if (effort !== void 0) revised.reasoningEffort = effort;
1235
1394
  }
1236
1395
  signal.throwIfAborted();
@@ -1268,8 +1427,28 @@ var hostPatch6 = {
1268
1427
  })
1269
1428
  };
1270
1429
 
1430
+ // src/patches/projectless-tasks/host/index.ts
1431
+ import { homedir } from "node:os";
1432
+ import { join } from "node:path";
1433
+
1434
+ // src/patches/projectless-tasks/shared.ts
1435
+ var PROJECTLESS_TASKS_PATCH_ID = "projectless-tasks";
1436
+ var TASK_ID_PATTERN = /^task-[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
1437
+
1438
+ // src/patches/projectless-tasks/host/index.ts
1439
+ function prepareTask(payload, homeDirectory = homedir()) {
1440
+ if (typeof payload !== "object" || payload === null || Array.isArray(payload) || !("sessionId" in payload) || typeof payload.sessionId !== "string" || !TASK_ID_PATTERN.test(payload.sessionId) || Object.keys(payload).length !== 1) {
1441
+ throw new DshMoreError("bad-request", "\u72EC\u7ACB\u4EFB\u52A1\u6807\u8BC6\u65E0\u6548\uFF0C\u8BF7\u91CD\u65B0\u65B0\u5EFA\u4EFB\u52A1\u3002");
1442
+ }
1443
+ return { sessionId: payload.sessionId, cwd: join(homeDirectory, "Documents", "DSH", "tasks", payload.sessionId) };
1444
+ }
1445
+ var hostPatch7 = {
1446
+ id: PROJECTLESS_TASKS_PATCH_ID,
1447
+ routes: () => ({ prepare: (payload) => prepareTask(payload) })
1448
+ };
1449
+
1271
1450
  // src/generated/host-registry.ts
1272
- var HOST_PATCHES = [hostPatch, hostPatch2, hostPatch3, hostPatch4, hostPatch5, hostPatch6];
1451
+ var HOST_PATCHES = [hostPatch, hostPatch2, hostPatch3, hostPatch4, hostPatch5, hostPatch6, hostPatch7];
1273
1452
 
1274
1453
  // src/generated/patch-catalog.ts
1275
1454
  var DEFAULT_PATCH_SETTINGS = {
@@ -1277,8 +1456,9 @@ var DEFAULT_PATCH_SETTINGS = {
1277
1456
  "message-delete": true,
1278
1457
  "session-delete": true,
1279
1458
  "conversation-markdown-export": true,
1280
- "model-reasoning-efforts": true,
1281
- "session-auto-title": true
1459
+ "model-capabilities": true,
1460
+ "session-auto-title": true,
1461
+ "projectless-tasks": true
1282
1462
  };
1283
1463
 
1284
1464
  // src/kernel/host/activation.ts
@@ -1321,8 +1501,9 @@ var PatchSettingsSchema = z2.object({
1321
1501
  "message-delete": z2.boolean().default(true),
1322
1502
  "session-delete": z2.boolean().default(true),
1323
1503
  "conversation-markdown-export": z2.boolean().default(true),
1324
- "model-reasoning-efforts": z2.boolean().default(true),
1325
- "session-auto-title": z2.boolean().default(true)
1504
+ "model-capabilities": z2.boolean().default(true),
1505
+ "session-auto-title": z2.boolean().default(true),
1506
+ "projectless-tasks": z2.boolean().default(true)
1326
1507
  });
1327
1508
 
1328
1509
  // src/kernel/host/settings.ts
@@ -4,13 +4,15 @@ export declare const PatchSettingsSchema: z<Schemastery.ObjectS<{
4
4
  "message-delete": z<boolean, boolean>;
5
5
  "session-delete": z<boolean, boolean>;
6
6
  "conversation-markdown-export": z<boolean, boolean>;
7
- "model-reasoning-efforts": z<boolean, boolean>;
7
+ "model-capabilities": z<boolean, boolean>;
8
8
  "session-auto-title": z<boolean, boolean>;
9
+ "projectless-tasks": z<boolean, boolean>;
9
10
  }>, Schemastery.ObjectT<{
10
11
  "message-edit": z<boolean, boolean>;
11
12
  "message-delete": z<boolean, boolean>;
12
13
  "session-delete": z<boolean, boolean>;
13
14
  "conversation-markdown-export": z<boolean, boolean>;
14
- "model-reasoning-efforts": z<boolean, boolean>;
15
+ "model-capabilities": z<boolean, boolean>;
15
16
  "session-auto-title": z<boolean, boolean>;
17
+ "projectless-tasks": z<boolean, boolean>;
16
18
  }>>;
@@ -27,9 +27,9 @@ export declare const PATCH_CATALOG: readonly [{
27
27
  readonly order: 400;
28
28
  readonly clientKind: "standalone";
29
29
  }, {
30
- readonly id: "model-reasoning-efforts";
31
- readonly name: "模型思考强度";
32
- readonly description: "在模型配置界面为每个模型配置支持的思考强度并写入 settings.yaml 的 reasoningEfforts";
30
+ readonly id: "model-capabilities";
31
+ readonly name: "模型能力配置";
32
+ readonly description: "在模型配置页设置每个模型的识图能力、可选思考类型与思考参数格式";
33
33
  readonly defaultEnabled: true;
34
34
  readonly order: 500;
35
35
  readonly clientKind: "standalone";
@@ -40,6 +40,13 @@ export declare const PATCH_CATALOG: readonly [{
40
40
  readonly defaultEnabled: true;
41
41
  readonly order: 600;
42
42
  readonly clientKind: "standalone";
43
+ }, {
44
+ readonly id: "projectless-tasks";
45
+ readonly name: "独立任务";
46
+ readonly description: "在工作区下拉框选择独立任务,或清除已选目录切换,保留草稿并自动使用独立目录";
47
+ readonly defaultEnabled: true;
48
+ readonly order: 700;
49
+ readonly clientKind: "standalone";
43
50
  }];
44
51
  export type PatchId = typeof PATCH_CATALOG[number]['id'];
45
52
  export interface PatchSettings {
@@ -47,8 +54,9 @@ export interface PatchSettings {
47
54
  "message-delete": boolean;
48
55
  "session-delete": boolean;
49
56
  "conversation-markdown-export": boolean;
50
- "model-reasoning-efforts": boolean;
57
+ "model-capabilities": boolean;
51
58
  "session-auto-title": boolean;
59
+ "projectless-tasks": boolean;
52
60
  }
53
61
  export declare const DEFAULT_PATCH_SETTINGS: PatchSettings;
54
62
  export declare function decodePatchSettings(value: unknown): PatchSettings | undefined;
@@ -0,0 +1,4 @@
1
+ import { type ModelCapabilitiesSnapshot } from '../shared.js';
2
+ /** A stale Host must never look like an editable model or silently skip save. */
3
+ export declare function decodeSnapshot(value: unknown): ModelCapabilitiesSnapshot;
4
+ export declare function readCapabilities(): Promise<ModelCapabilitiesSnapshot>;
@@ -0,0 +1,26 @@
1
+ import { type ModelSettingsNamespace, type ModelCapabilities, type ModelCapabilitiesSnapshot, type ThinkingLevel } from '../shared.js';
2
+ export interface ModelDraft {
3
+ vision: 'default' | 'yes' | 'no';
4
+ thinking: 'default' | 'disabled' | 'custom';
5
+ levels: Partial<Record<ThinkingLevel, {
6
+ enabled: boolean;
7
+ wire: string;
8
+ }>>;
9
+ thinkingFormat: NonNullable<ModelCapabilities['thinkingFormat']> | 'default';
10
+ }
11
+ export declare function draftFromServer(capabilities: ModelCapabilities): ModelDraft;
12
+ export declare function capabilitiesFromDraft(draft: ModelDraft): ModelCapabilities;
13
+ export declare function summaryLabel(capabilities: ModelCapabilities): string;
14
+ export declare function CapabilitiesEditorBody({ draft, setDraft, disabled, api }: {
15
+ draft: ModelDraft;
16
+ setDraft: (updater: (current: ModelDraft) => ModelDraft) => void;
17
+ disabled: boolean;
18
+ api: string | null;
19
+ }): JSX.Element;
20
+ export declare function CapabilitiesEditorModal({ provider, modelId, namespace, onClose, onSaved }: {
21
+ namespace: ModelSettingsNamespace;
22
+ provider: string;
23
+ modelId: string;
24
+ onClose: () => void;
25
+ onSaved: (snapshot: ModelCapabilitiesSnapshot) => void;
26
+ }): JSX.Element;
@@ -0,0 +1,10 @@
1
+ import type { SettingsScope } from '@deepseek-ai/dsh-client-ui-settings/client';
2
+ export type {} from '@deepseek-ai/dsh-client-ui-settings';
3
+ import type { PatchActivationSource } from '../../../kernel/client/activation.js';
4
+ import type { ClientPatch } from '../../../kernel/client/patch.js';
5
+ export declare function ModelsPageCapabilitiesPortal({ scope, deepseekScope, activation }: {
6
+ deepseekScope: SettingsScope<unknown>;
7
+ scope: SettingsScope<unknown>;
8
+ activation: PatchActivationSource;
9
+ }): JSX.Element | null;
10
+ export declare const clientPatch: ClientPatch;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Locate every model row of every open model catalog in the shipped Models
3
+ * settings page (the provider editor's 模型目录). The classes are the models
4
+ * package's own CSS-module hashes, stable per build.
5
+ */
6
+ export declare function findModelEntries(): HTMLElement[];
7
+ /**
8
+ * Resolve the provider route for one model entry: the editor header's route
9
+ * span when the route differs from the display name, else the owning provider
10
+ * card's display name (which for such providers equals the route). Returns
11
+ * null for the custom-provider creation card, which has no saved provider yet.
12
+ */
13
+ export declare function providerRouteOf(entry: HTMLElement): string | null;
14
+ /** Resolve the model id from a model entry's first row input (the id field). */
15
+ export declare function modelIdOf(entry: HTMLElement): string | null;
@@ -0,0 +1 @@
1
+ export declare const capabilitiesStyles = "\n.dshmore-capabilities-button { box-sizing: border-box; width: 100%; min-height: 30px; color: var(--dsw-alias-label-secondary); font: inherit; cursor: pointer; background: transparent; border: 1px dashed var(--dsw-alias-border-l3); border-radius: 6px; margin-top: 6px; padding: 5px 10px; font-size: 12px; line-height: 18px; text-align: left; overflow-wrap: anywhere; }\n.dshmore-capabilities-button:hover { background: var(--dsw-alias-interactive-bg-hover); color: var(--dsw-alias-label-primary); }\n.dshmore-capabilities-dialog, .dshmore-capabilities-fields { display: flex; flex-direction: column; gap: 16px; min-width: 0; }\n.dshmore-capabilities-dialog { max-height: 65vh; overflow-y: auto; }\n.dshmore-capabilities-field { display: flex; flex-direction: column; gap: 6px; font-size: 13px; color: var(--dsw-alias-label-secondary); }\n.dshmore-capabilities-fields select, .dshmore-capabilities-level input[type=\"text\"] { box-sizing: border-box; min-width: 0; width: 100%; height: 32px; padding: 0 8px; border: 1px solid var(--dsw-alias-border-l2); border-radius: 6px; background: var(--dsw-alias-bg-layer-1); color: var(--dsw-alias-label-primary); font: inherit; font-size: 13px; }\n.dshmore-capabilities-fields :disabled { opacity: .55; cursor: default; }\n.dshmore-capabilities-fields :focus-visible, .dshmore-capabilities-button:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary); outline-offset: 2px; }\n.dshmore-capabilities-levels { display: flex; flex-direction: column; gap: 8px; margin: 0; padding: 12px; border: 1px solid var(--dsw-alias-border-l3); border-radius: 8px; min-width: 0; }\n.dshmore-capabilities-levels legend { font-size: 12px; color: var(--dsw-alias-label-secondary); }\n.dshmore-capabilities-level { display: grid; grid-template-columns: minmax(100px, 1fr) minmax(120px, 1.4fr); gap: 8px; align-items: center; min-height: 32px; }\n.dshmore-capabilities-level label { display: flex; align-items: center; gap: 6px; font-size: 13px; color: var(--dsw-alias-label-secondary); }\n.dshmore-capabilities-level input[type=\"checkbox\"] { accent-color: var(--dsw-alias-state-business-primary); }\n.dshmore-capabilities-level small { color: var(--dsw-alias-label-tertiary); }\n.dshmore-capabilities-advanced { border-top: 1px solid var(--dsw-alias-border-l3); padding-top: 12px; }\n.dshmore-capabilities-advanced summary { cursor: pointer; font-size: 13px; color: var(--dsw-alias-label-secondary); }\n.dshmore-capabilities-advanced[open] summary { margin-bottom: 12px; }\n.dshmore-capabilities-note { color: var(--dsw-alias-label-tertiary); margin: 0; font-size: 12px; line-height: 18px; }\n.dshmore-capabilities-error { padding: 8px 10px; border-radius: 8px; background: rgba(208,58,58,.10); color: var(--dsw-alias-state-error-primary, rgb(220,90,90)); font-size: 12px; line-height: 18px; }\n";