@ljwei-stak/dsh-model-router 0.21.0 → 0.22.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.
@@ -504,7 +504,7 @@ var OFFICIAL_TOOLS = Object.freeze([
504
504
  installArgs: [],
505
505
  uninstallArgs: [],
506
506
  probeExecutables: ["hermes"],
507
- probeNote: "\u652F\u6301\u5B98\u65B9\u6E90\u7801\u811A\u672C\u5B89\u88C5\uFF1B\u547D\u4EE4\u5165\u53E3\u3001\u6E90\u7801\u548C\u7528\u6237\u6570\u636E\u5206\u522B\u7531\u5382\u5546\u7BA1\u7406\uFF0C\u7EDF\u4E00\u76EE\u5F55\u4E0D\u8FC1\u79FB\u8FD9\u4E9B\u4F4D\u7F6E\u3002\u684C\u9762/MSIX \u522B\u540D\u6682\u4E0D\u7528\u4E8E\u6B64\u5165\u53E3\u3002\u767B\u5F55\u4F7F\u7528 hermes auth\u3002",
507
+ probeNote: "\u652F\u6301\u5B98\u65B9\u6E90\u7801\u811A\u672C\u5B89\u88C5\uFF1B\u81EA\u5B9A\u4E49\u6E90\u7801\u5B89\u88C5\u5230\u7EDF\u4E00\u6839/hermes-agent\uFF0C\u63D2\u4EF6\u4F7F\u7528\u8BE5\u6E90\u7801\u7684 .hermes/bin \u5165\u53E3\u3002\u914D\u7F6E\u3001\u767B\u5F55\u548C\u5386\u53F2\u4FDD\u7559\u539F\u4F4D\u7F6E\u3002\u684C\u9762/MSIX \u522B\u540D\u6682\u4E0D\u7528\u4E8E\u6B64\u5165\u53E3\u3002\u767B\u5F55\u4F7F\u7528 hermes auth\u3002",
508
508
  providerHints: ["hermes", "nousresearch"],
509
509
  scriptBinaryNames: Object.freeze(["hermes", "hermes.exe"]),
510
510
  installMethods: Object.freeze([
@@ -512,8 +512,11 @@ var OFFICIAL_TOOLS = Object.freeze([
512
512
  shell: "bash",
513
513
  scriptUrl: "https://hermes-agent.nousresearch.com/install.sh",
514
514
  platforms: ["linux", "darwin"],
515
- supportsInstallDir: false,
515
+ supportsInstallDir: true,
516
516
  sharedDefaultBin: true,
517
+ installDirSubdir: "hermes-agent",
518
+ installDirSuffix: ".hermes/bin",
519
+ installArg: ["--dir"],
517
520
  extraArgs: ["--non-interactive"],
518
521
  verify: Object.freeze({ mustInclude: ["https://github.com/NousResearch/hermes-agent.git", "hermes_cli/source_completion.py"] }),
519
522
  uninstallUnsupportedReason: "\u8BF7\u5728\u5B98\u65B9\u5DE5\u5177\u7EC8\u7AEF\u8FD0\u884C hermes uninstall \u5378\u8F7D\u6E90\u7801\u5B89\u88C5\uFF1B\u4FDD\u7559\u914D\u7F6E\u4E0E\u5386\u53F2\u65F6\u6309\u5411\u5BFC\u9009\u62E9\u3002\u684C\u9762/MSIX \u5B89\u88C5\u8BF7\u4F7F\u7528\u7CFB\u7EDF\u5E94\u7528\u7BA1\u7406\u3002"
@@ -522,7 +525,10 @@ var OFFICIAL_TOOLS = Object.freeze([
522
525
  shell: "powershell",
523
526
  scriptUrl: "https://hermes-agent.nousresearch.com/install.ps1",
524
527
  platforms: ["win32"],
525
- supportsInstallDir: false,
528
+ supportsInstallDir: true,
529
+ installDirSubdir: "hermes-agent",
530
+ installDirSuffix: ".hermes/bin",
531
+ installArg: ["-InstallDir"],
526
532
  extraArgs: ["-NonInteractive"],
527
533
  verify: Object.freeze({ mustInclude: ["https://github.com/NousResearch/hermes-agent.git", "hermes_cli/source_completion.py"] }),
528
534
  uninstallUnsupportedReason: "\u8BF7\u5728\u5B98\u65B9\u5DE5\u5177\u7EC8\u7AEF\u8FD0\u884C hermes uninstall \u5378\u8F7D\u6E90\u7801\u5B89\u88C5\uFF1B\u4FDD\u7559\u914D\u7F6E\u4E0E\u5386\u53F2\u65F6\u6309\u5411\u5BFC\u9009\u62E9\u3002\u684C\u9762/MSIX \u5B89\u88C5\u8BF7\u4F7F\u7528\u7CFB\u7EDF\u5E94\u7528\u7BA1\u7406\u3002"
@@ -1046,51 +1052,118 @@ function liveBenchRow(snapshot, model) {
1046
1052
  // .dsh-plugin/shared/assignment-solver.mjs
1047
1053
  var EPSILON = 1e-12;
1048
1054
  var compareText = (a, b2) => a < b2 ? -1 : a > b2 ? 1 : 0;
1049
- var signature = (state) => JSON.stringify(state.choices.map((option) => option.route));
1055
+ var choiceSignature = (option) => {
1056
+ if (option.actionId === void 0 && option.effort === void 0 && option.reasoningEffort === void 0) return option.route;
1057
+ return JSON.stringify([option.actionId ?? option.route, option.effort ?? option.reasoningEffort ?? null]);
1058
+ };
1059
+ var signature = (state) => JSON.stringify(state.choices.map(choiceSignature));
1060
+ var compareQuality = (left, right) => left.relaxedCount - right.relaxedCount || left.qualityShortfall - right.qualityShortfall;
1050
1061
  function compareStates(left, right, minimizeCost = false) {
1051
1062
  return left.relaxedCount - right.relaxedCount || left.qualityShortfall - right.qualityShortfall || (minimizeCost ? left.cost - right.cost : right.score - left.score) || (minimizeCost ? right.score - left.score : left.cost - right.cost) || left.switches - right.switches || compareText(signature(left), signature(right));
1052
1063
  }
1064
+ var dominates = (left, right) => {
1065
+ if (left.cost > right.cost) return false;
1066
+ const quality = compareQuality(left, right);
1067
+ if (quality < 0) return true;
1068
+ if (quality > 0) return false;
1069
+ if (left.score > right.score) return true;
1070
+ if (left.score < right.score) return false;
1071
+ if (left.cost < right.cost) return true;
1072
+ if (left.cost > right.cost) return false;
1073
+ if (left.switches !== right.switches) return left.switches < right.switches;
1074
+ return compareText(signature(left), signature(right)) < 0;
1075
+ };
1076
+ var dominanceOrder = (left, right) => left.cost - right.cost || compareQuality(left, right) || right.score - left.score || left.switches - right.switches || compareText(signature(left), signature(right));
1053
1077
  function solveCandidateAssignments({ tasks = [], pools = [], budget = Infinity, beamWidth = 256, exactLimit = 4096, handoffPenalty = 0.015, minimizeCost = false } = {}) {
1054
1078
  if (!tasks.length || tasks.length !== pools.length) return null;
1079
+ const taskCount = tasks.length;
1080
+ const taskIndexById = /* @__PURE__ */ new Map();
1055
1081
  const preceding = /* @__PURE__ */ new Set();
1056
- for (const task of tasks) {
1057
- if (!task.id || preceding.has(task.id) || (task.dependsOn ?? []).some((id2) => !preceding.has(id2))) {
1082
+ for (let index = 0; index < taskCount; index++) {
1083
+ const task = tasks[index];
1084
+ if (!task || !task.id || preceding.has(task.id) || (task.dependsOn ?? []).some((id2) => !preceding.has(id2))) {
1058
1085
  throw new RangeError("Assignment tasks must have unique ids and known dependencies in topological order");
1059
1086
  }
1060
1087
  preceding.add(task.id);
1088
+ taskIndexById.set(task.id, index);
1061
1089
  }
1062
1090
  const constrained = Number.isFinite(budget);
1063
- const options = pools.map((pool) => (Array.isArray(pool) ? pool : []).filter((option) => Number.isFinite(option.cost) && option.cost >= 0 && Number.isFinite(option.score) && (!constrained || option.pricingKnown === true)));
1091
+ const options = pools.map((pool) => (Array.isArray(pool) ? pool : []).filter((option) => option && Number.isFinite(option.cost) && option.cost >= 0 && Number.isFinite(option.score) && (!constrained || option.pricingKnown === true)));
1064
1092
  if (options.some((pool) => pool.length === 0)) return null;
1065
- const suffixMinimum = Array(tasks.length + 1).fill(0);
1093
+ const handoffRoutes = options.map((pool) => pool.map((option) => option.handoffRoute ?? option.route));
1094
+ const maximumScores = options.map((pool) => pool.reduce((best2, option) => Math.max(best2, option.score), -Infinity));
1095
+ const dependencyIndexes = tasks.map((task) => (task.dependsOn ?? []).map((id2) => taskIndexById.get(id2)));
1096
+ const lastUse = new Array(taskCount).fill(-1);
1097
+ for (let index = 0; index < taskCount; index++) {
1098
+ for (const depIndex of dependencyIndexes[index]) {
1099
+ if (index > lastUse[depIndex]) lastUse[depIndex] = index;
1100
+ }
1101
+ }
1102
+ const suffixMinimum = new Array(taskCount + 1).fill(0);
1103
+ const futureRelaxedLower = new Array(taskCount + 1).fill(0);
1104
+ const futureShortfallLower = new Array(taskCount + 1).fill(0);
1066
1105
  let searchSpace = 1;
1067
1106
  let searchSpaceCapped = false;
1068
- for (let index = tasks.length - 1; index >= 0; index--) {
1069
- suffixMinimum[index] = suffixMinimum[index + 1] + Math.min(...options[index].map((option) => option.cost));
1107
+ for (let index = taskCount - 1; index >= 0; index--) {
1108
+ let minCost = Infinity;
1109
+ let minRelaxedFlag = 1;
1110
+ let minShortfallAtFlag = Infinity;
1111
+ for (const option of options[index]) {
1112
+ if (option.cost < minCost) minCost = option.cost;
1113
+ const shortfall = Math.max(0, Number(option.qualityShortfall) || 0);
1114
+ const relaxedFlag = shortfall > 0 ? 1 : 0;
1115
+ if (relaxedFlag < minRelaxedFlag || relaxedFlag === minRelaxedFlag && shortfall < minShortfallAtFlag) {
1116
+ minRelaxedFlag = relaxedFlag;
1117
+ minShortfallAtFlag = shortfall;
1118
+ }
1119
+ }
1120
+ suffixMinimum[index] = suffixMinimum[index + 1] + minCost;
1121
+ futureRelaxedLower[index] = futureRelaxedLower[index + 1] + minRelaxedFlag;
1122
+ futureShortfallLower[index] = futureShortfallLower[index + 1] + minShortfallAtFlag;
1070
1123
  const product = searchSpace * options[index].length;
1071
1124
  if (product > Number.MAX_SAFE_INTEGER) searchSpaceCapped = true;
1072
1125
  searchSpace = Math.min(Number.MAX_SAFE_INTEGER, product);
1073
1126
  }
1074
1127
  if (constrained && suffixMinimum[0] > budget + EPSILON) return null;
1075
- const exact = !searchSpaceCapped && searchSpace <= Math.max(1, exactLimit);
1076
- const width = Math.max(1, Math.floor(beamWidth) || 256);
1077
- const search = { method: exact ? "exact" : "beam", exact, searchSpace, searchSpaceCapped, expandedStates: 0, beamPruned: 0 };
1128
+ const parsedExactLimit = Number(exactLimit);
1129
+ const exactThreshold = Number.isFinite(parsedExactLimit) ? Math.max(1, parsedExactLimit) : exactLimit === Infinity ? Number.MAX_SAFE_INTEGER : 4096;
1130
+ const parsedBeamWidth = Number(beamWidth);
1131
+ const width = Number.isFinite(parsedBeamWidth) ? Math.max(1, Math.floor(parsedBeamWidth) || 256) : 256;
1132
+ const exact = !searchSpaceCapped && searchSpace <= exactThreshold;
1133
+ const search = {
1134
+ method: exact ? "exact" : "beam",
1135
+ exact,
1136
+ strategy: exact ? "exact-enumeration" : "boundary-frontier-lookahead",
1137
+ searchSpace,
1138
+ searchSpaceCapped,
1139
+ expandedStates: 0,
1140
+ beamPruned: 0,
1141
+ dominancePruned: 0,
1142
+ peakFrontier: 0,
1143
+ retainedStates: 0,
1144
+ beamWidth: width,
1145
+ approximate: !exact,
1146
+ optimalityScope: "retained input candidate Cartesian space",
1147
+ boundCertified: exact
1148
+ };
1149
+ const unassignedReward = handoffPenalty < 0 ? -handoffPenalty : 0;
1078
1150
  let states = [{ choices: [], routesByTask: /* @__PURE__ */ new Map(), score: 0, cost: 0, switches: 0, relaxedCount: 0, qualityShortfall: 0 }];
1079
- for (let index = 0; index < tasks.length; index++) {
1080
- const task = tasks[index];
1151
+ for (let index = 0; index < taskCount; index++) {
1081
1152
  const expanded = [];
1082
1153
  for (const state of states) {
1083
- for (const option of options[index]) {
1154
+ for (let optionIndex = 0; optionIndex < options[index].length; optionIndex++) {
1155
+ const option = options[index][optionIndex];
1084
1156
  const cost = state.cost + option.cost;
1085
1157
  if (constrained && cost + suffixMinimum[index + 1] > budget + EPSILON) continue;
1086
- const switches = (task.dependsOn ?? []).reduce((count, dependency) => {
1087
- const route = state.routesByTask.get(dependency);
1088
- return count + (route !== void 0 && route !== option.route ? 1 : 0);
1089
- }, 0);
1158
+ let switches = 0;
1159
+ for (const depIndex of dependencyIndexes[index]) {
1160
+ const route = state.routesByTask.get(tasks[depIndex].id);
1161
+ if (route !== void 0 && route !== handoffRoutes[index][optionIndex]) switches++;
1162
+ }
1090
1163
  const penalty = switches * handoffPenalty;
1091
1164
  const shortfall = Math.max(0, Number(option.qualityShortfall) || 0);
1092
1165
  const routesByTask2 = new Map(state.routesByTask);
1093
- routesByTask2.set(task.id, option.route);
1166
+ routesByTask2.set(tasks[index].id, handoffRoutes[index][optionIndex]);
1094
1167
  expanded.push({
1095
1168
  choices: [...state.choices, { ...option, handoffPenalty: penalty }],
1096
1169
  routesByTask: routesByTask2,
@@ -1104,22 +1177,404 @@ function solveCandidateAssignments({ tasks = [], pools = [], budget = Infinity,
1104
1177
  }
1105
1178
  }
1106
1179
  if (!expanded.length) return null;
1107
- expanded.sort((left, right) => compareStates(left, right, minimizeCost));
1108
- if (!exact && expanded.length > width) {
1109
- states = expanded.slice(0, width);
1110
- if (constrained && index < tasks.length - 1) {
1111
- const cheapest = expanded.reduce((best2, state) => compareStates(state, best2, true) < 0 ? state : best2);
1112
- if (!states.includes(cheapest)) states[width - 1] = cheapest;
1113
- }
1114
- search.beamPruned += expanded.length - states.length;
1115
- } else states = expanded;
1180
+ if (exact) {
1181
+ expanded.sort((left, right) => compareStates(left, right, minimizeCost));
1182
+ states = expanded;
1183
+ } else {
1184
+ const groups = /* @__PURE__ */ new Map();
1185
+ for (const state of expanded) {
1186
+ const boundary = [];
1187
+ for (let prev = 0; prev <= index; prev++) {
1188
+ if (lastUse[prev] > index) boundary.push([tasks[prev].id, state.routesByTask.get(tasks[prev].id)]);
1189
+ }
1190
+ const key = JSON.stringify(boundary);
1191
+ const group = groups.get(key);
1192
+ if (group) group.push(state);
1193
+ else groups.set(key, [state]);
1194
+ }
1195
+ const compressed = [];
1196
+ for (const group of groups.values()) {
1197
+ group.sort(dominanceOrder);
1198
+ const retained = [];
1199
+ for (const candidate of group) {
1200
+ let dominated = false;
1201
+ for (const keeper of retained) {
1202
+ if (dominates(keeper, candidate)) {
1203
+ dominated = true;
1204
+ break;
1205
+ }
1206
+ }
1207
+ if (dominated) search.dominancePruned++;
1208
+ else retained.push(candidate);
1209
+ }
1210
+ for (const state of retained) compressed.push(state);
1211
+ }
1212
+ const scoreUpperCache = /* @__PURE__ */ new Map();
1213
+ const optimisticScore = (state) => {
1214
+ if (scoreUpperCache.has(state)) return scoreUpperCache.get(state);
1215
+ let total = state.score;
1216
+ for (let future = index + 1; future < taskCount; future++) {
1217
+ const deps = dependencyIndexes[future];
1218
+ let unassignedDeps = 0;
1219
+ let assignedCount = 0;
1220
+ const assignedRoutes = /* @__PURE__ */ new Map();
1221
+ for (const depIndex of deps) {
1222
+ if (depIndex > index) unassignedDeps++;
1223
+ else {
1224
+ const route = state.routesByTask.get(tasks[depIndex].id);
1225
+ if (route !== void 0) {
1226
+ assignedCount++;
1227
+ assignedRoutes.set(route, (assignedRoutes.get(route) ?? 0) + 1);
1228
+ }
1229
+ }
1230
+ }
1231
+ if (!assignedCount || handoffPenalty === 0) {
1232
+ total += maximumScores[future] + unassignedReward * unassignedDeps;
1233
+ continue;
1234
+ }
1235
+ let best2 = -Infinity;
1236
+ for (let optionIndex = 0; optionIndex < options[future].length; optionIndex++) {
1237
+ const option = options[future][optionIndex];
1238
+ const matches = assignedRoutes.get(handoffRoutes[future][optionIndex]) ?? 0;
1239
+ let value = option.score - handoffPenalty * (assignedCount - matches);
1240
+ if (unassignedDeps && unassignedReward) value += unassignedReward * unassignedDeps;
1241
+ if (value > best2) best2 = value;
1242
+ }
1243
+ total += best2;
1244
+ }
1245
+ scoreUpperCache.set(state, total);
1246
+ return total;
1247
+ };
1248
+ compressed.sort((left, right) => {
1249
+ const leftRelaxed = left.relaxedCount + futureRelaxedLower[index + 1];
1250
+ const rightRelaxed = right.relaxedCount + futureRelaxedLower[index + 1];
1251
+ if (leftRelaxed !== rightRelaxed) return leftRelaxed - rightRelaxed;
1252
+ const leftShortfall = left.qualityShortfall + futureShortfallLower[index + 1];
1253
+ const rightShortfall = right.qualityShortfall + futureShortfallLower[index + 1];
1254
+ if (leftShortfall !== rightShortfall) return leftShortfall - rightShortfall;
1255
+ const leftScore = optimisticScore(left);
1256
+ const rightScore = optimisticScore(right);
1257
+ const leftCost = left.cost + suffixMinimum[index + 1];
1258
+ const rightCost = right.cost + suffixMinimum[index + 1];
1259
+ if (minimizeCost) {
1260
+ if (leftCost !== rightCost) return leftCost - rightCost;
1261
+ if (leftScore !== rightScore) return rightScore - leftScore;
1262
+ } else {
1263
+ if (leftScore !== rightScore) return rightScore - leftScore;
1264
+ if (leftCost !== rightCost) return leftCost - rightCost;
1265
+ }
1266
+ return compareStates(left, right, minimizeCost);
1267
+ });
1268
+ if (compressed.length > width) {
1269
+ states = compressed.slice(0, width);
1270
+ search.beamPruned += compressed.length - states.length;
1271
+ if (constrained && index < taskCount - 1) {
1272
+ let cheapest = compressed[0];
1273
+ for (const state of compressed) if (compareStates(state, cheapest, true) < 0) cheapest = state;
1274
+ if (!states.includes(cheapest)) states[width - 1] = cheapest;
1275
+ }
1276
+ } else states = compressed;
1277
+ }
1278
+ if (states.length > search.peakFrontier) search.peakFrontier = states.length;
1116
1279
  }
1117
1280
  states.sort((left, right) => compareStates(left, right, minimizeCost));
1281
+ search.retainedStates = states.length;
1118
1282
  const best = states[0];
1119
1283
  const { routesByTask, ...result } = best;
1120
1284
  return { ...result, minimumCostLowerBound: suffixMinimum[0], search };
1121
1285
  }
1122
1286
 
1287
+ // .dsh-plugin/shared/routing-evidence.mjs
1288
+ var SCHEMA_VERSION = 1;
1289
+ var FEATURE_DIMS = 4096;
1290
+ var TOKEN_CAP = 512;
1291
+ var MAX_TEXT_CHARS = 2e4;
1292
+ var MAX_STRING = 200;
1293
+ var MAX_EFFORT = 64;
1294
+ var MAX_QUALITY_SAMPLES = 1e4;
1295
+ var MAX_INPUT_TOKENS = 5e6;
1296
+ var MAX_OUTPUT_TOKENS = 2e6;
1297
+ var MAX_INPUT_CHARS = 5e6;
1298
+ var MAX_DURATION_MS = 864e5;
1299
+ var SIM_THRESHOLD = 0.2;
1300
+ var MAX_NEIGHBORS = 32;
1301
+ var SHRINK_PRIOR = 2;
1302
+ var NORM_TOLERANCE = 0.05;
1303
+ function isObj(o2) {
1304
+ return o2 !== null && typeof o2 === "object" && !Array.isArray(o2);
1305
+ }
1306
+ function boundedString(s15, max = MAX_STRING) {
1307
+ return typeof s15 === "string" && s15.length <= max && !/[\u0000-\u001f\u007f]/.test(s15);
1308
+ }
1309
+ function nonEmpty(s15) {
1310
+ return boundedString(s15) && s15.trim().length > 0;
1311
+ }
1312
+ function effortOk(s15) {
1313
+ return boundedString(s15, MAX_EFFORT);
1314
+ }
1315
+ function finite(n) {
1316
+ return typeof n === "number" && Number.isFinite(n);
1317
+ }
1318
+ function normalizeChannel(c) {
1319
+ if (!nonEmpty(c)) return null;
1320
+ return c === "api" ? "harness-llm" : c;
1321
+ }
1322
+ function isWordCode(c) {
1323
+ return c >= 48 && c <= 57 || c >= 65 && c <= 90 || c >= 97 && c <= 122 || c === 95;
1324
+ }
1325
+ function isCjkCode(c) {
1326
+ return c >= 13312 && c <= 19903 || c >= 19968 && c <= 40959 || c >= 63744 && c <= 64255;
1327
+ }
1328
+ function hashIndex(tok) {
1329
+ let h2 = 2166136261 >>> 0;
1330
+ for (let i = 0; i < tok.length; i++) {
1331
+ h2 ^= tok.charCodeAt(i);
1332
+ h2 = Math.imul(h2, 16777619) >>> 0;
1333
+ }
1334
+ return h2 % FEATURE_DIMS;
1335
+ }
1336
+ function buildFeatures(text7) {
1337
+ const s15 = text7.length > MAX_TEXT_CHARS ? text7.slice(0, MAX_TEXT_CHARS) : text7;
1338
+ const idxCounts = /* @__PURE__ */ new Map();
1339
+ const seen = /* @__PURE__ */ new Set();
1340
+ const addToken = (tok) => {
1341
+ if (!seen.has(tok)) {
1342
+ if (seen.size >= TOKEN_CAP) return;
1343
+ seen.add(tok);
1344
+ }
1345
+ const h2 = hashIndex(tok);
1346
+ idxCounts.set(h2, (idxCounts.get(h2) || 0) + 1);
1347
+ };
1348
+ let word = "";
1349
+ let cjk = "";
1350
+ const flushWord = () => {
1351
+ if (word) {
1352
+ addToken(word.toLowerCase());
1353
+ word = "";
1354
+ }
1355
+ };
1356
+ const flushCjk = () => {
1357
+ if (cjk) {
1358
+ if (cjk.length === 1) addToken(cjk);
1359
+ else for (let i = 0; i + 1 < cjk.length; i++) addToken(cjk.slice(i, i + 2));
1360
+ cjk = "";
1361
+ }
1362
+ };
1363
+ for (let i = 0; i < s15.length; i++) {
1364
+ const c = s15.charCodeAt(i);
1365
+ if (isWordCode(c)) {
1366
+ flushCjk();
1367
+ word += s15[i];
1368
+ } else if (isCjkCode(c)) {
1369
+ flushWord();
1370
+ cjk += s15[i];
1371
+ } else {
1372
+ flushWord();
1373
+ flushCjk();
1374
+ }
1375
+ if (idxCounts.size >= FEATURE_DIMS) break;
1376
+ }
1377
+ flushWord();
1378
+ flushCjk();
1379
+ let sum = 0;
1380
+ for (const c of idxCounts.values()) sum += c * c;
1381
+ if (sum <= 0) return [];
1382
+ const norm = Math.sqrt(sum);
1383
+ const feats = [];
1384
+ for (const [idx, c] of idxCounts) feats.push([idx, c / norm]);
1385
+ feats.sort((a, b2) => a[0] - b2[0]);
1386
+ return feats;
1387
+ }
1388
+ function cosine(a, b2) {
1389
+ let na2 = 0;
1390
+ let nb = 0;
1391
+ for (let i = 0; i < a.length; i++) na2 += a[i][1] * a[i][1];
1392
+ for (let j2 = 0; j2 < b2.length; j2++) nb += b2[j2][1] * b2[j2][1];
1393
+ const denom = Math.sqrt(na2) * Math.sqrt(nb);
1394
+ if (denom === 0) return 0;
1395
+ let dot = 0;
1396
+ let x = 0;
1397
+ let y = 0;
1398
+ while (x < a.length && y < b2.length) {
1399
+ const ai2 = a[x][0];
1400
+ const bi2 = b2[y][0];
1401
+ if (ai2 === bi2) {
1402
+ dot += a[x][1] * b2[y][1];
1403
+ x++;
1404
+ y++;
1405
+ } else if (ai2 < bi2) x++;
1406
+ else y++;
1407
+ }
1408
+ return dot / denom;
1409
+ }
1410
+ function validateFeatures(features) {
1411
+ if (!Array.isArray(features) || features.length === 0 || features.length > FEATURE_DIMS) return false;
1412
+ let sum = 0;
1413
+ let prev = -1;
1414
+ for (const pair of features) {
1415
+ if (!Array.isArray(pair) || pair.length !== 2) return false;
1416
+ const idx = pair[0];
1417
+ const w = pair[1];
1418
+ if (!Number.isInteger(idx) || idx < 0 || idx >= FEATURE_DIMS) return false;
1419
+ if (typeof w !== "number" || !Number.isFinite(w) || w <= 0) return false;
1420
+ if (idx <= prev) return false;
1421
+ prev = idx;
1422
+ sum += w * w;
1423
+ }
1424
+ const norm = Math.sqrt(sum);
1425
+ if (!(norm > 0)) return false;
1426
+ return Math.abs(norm - 1) <= NORM_TOLERANCE;
1427
+ }
1428
+ function validateGroup(g) {
1429
+ if (!isObj(g)) return false;
1430
+ const id2 = g.identity;
1431
+ if (!isObj(id2)) return false;
1432
+ if (!nonEmpty(id2.provider) || !nonEmpty(id2.model)) return false;
1433
+ if (!effortOk(id2.reasoningEffort)) return false;
1434
+ if (!nonEmpty(id2.channel) || normalizeChannel(id2.channel) !== id2.channel) return false;
1435
+ if (!nonEmpty(id2.type)) return false;
1436
+ if (!boundedString(id2.purpose)) return false;
1437
+ if (!Number.isInteger(g.sampleCount) || g.sampleCount < 1 || g.sampleCount > MAX_QUALITY_SAMPLES) return false;
1438
+ for (const k of ["inputTokenRatio", "outputTokens"]) {
1439
+ if (typeof g[k] !== "number" || !Number.isFinite(g[k]) || g[k] < 0) return false;
1440
+ }
1441
+ for (const k of ["durationMs", "promptCharRatio"]) {
1442
+ const v2 = g[k];
1443
+ if (v2 !== null && (typeof v2 !== "number" || !Number.isFinite(v2) || v2 < 0)) return false;
1444
+ }
1445
+ const p90 = g.p90;
1446
+ if (!isObj(p90)) return false;
1447
+ for (const k of ["inputTokenRatio", "outputTokens"]) {
1448
+ if (typeof p90[k] !== "number" || !Number.isFinite(p90[k]) || p90[k] < 0) return false;
1449
+ }
1450
+ for (const k of ["durationMs", "promptCharRatio"]) {
1451
+ const v2 = p90[k];
1452
+ if (v2 !== null && (typeof v2 !== "number" || !Number.isFinite(v2) || v2 < 0)) return false;
1453
+ }
1454
+ for (const k of ["inputTokenRatio", "outputTokens", "durationMs", "promptCharRatio"]) {
1455
+ if (g[k] === null !== (p90[k] === null)) return false;
1456
+ if (g[k] !== null && p90[k] < g[k]) return false;
1457
+ }
1458
+ if (g.inputTokenRatio > MAX_INPUT_TOKENS || p90.inputTokenRatio > MAX_INPUT_TOKENS || g.outputTokens > MAX_OUTPUT_TOKENS || p90.outputTokens > MAX_OUTPUT_TOKENS || g.durationMs !== null && p90.durationMs > MAX_DURATION_MS || g.promptCharRatio !== null && p90.promptCharRatio > MAX_INPUT_CHARS) return false;
1459
+ return true;
1460
+ }
1461
+ function predictResources(evidence, {
1462
+ provider,
1463
+ model,
1464
+ reasoningEffort = "",
1465
+ channel = "harness-llm",
1466
+ type = "general",
1467
+ purpose = "",
1468
+ inputChars
1469
+ } = {}) {
1470
+ if (!isObj(evidence) || evidence.schemaVersion !== SCHEMA_VERSION || !Array.isArray(evidence.resourceGroups)) return null;
1471
+ if (!nonEmpty(provider) || !nonEmpty(model)) return null;
1472
+ if (!effortOk(reasoningEffort)) return null;
1473
+ const ch = normalizeChannel(channel);
1474
+ if (!ch) return null;
1475
+ const ty = nonEmpty(type) ? type : "general";
1476
+ const pu = typeof purpose === "string" ? purpose : "";
1477
+ const minSamples = evidence.minSamples;
1478
+ if (!Number.isInteger(minSamples) || minSamples < 1 || minSamples > 1e3) return null;
1479
+ void inputChars;
1480
+ for (const g of evidence.resourceGroups) {
1481
+ if (!validateGroup(g)) continue;
1482
+ const id2 = g.identity;
1483
+ if (id2.provider === provider && id2.model === model && id2.reasoningEffort === reasoningEffort && id2.channel === ch && id2.type === ty && id2.purpose === pu) {
1484
+ if (!Number.isInteger(g.sampleCount) || g.sampleCount < minSamples) return null;
1485
+ return {
1486
+ inputTokenRatio: g.inputTokenRatio,
1487
+ outputTokens: g.outputTokens,
1488
+ durationMs: g.durationMs,
1489
+ promptCharRatio: g.promptCharRatio,
1490
+ p90: {
1491
+ inputTokenRatio: g.p90.inputTokenRatio,
1492
+ outputTokens: g.p90.outputTokens,
1493
+ durationMs: g.p90.durationMs,
1494
+ promptCharRatio: g.p90.promptCharRatio
1495
+ },
1496
+ sampleCount: g.sampleCount,
1497
+ source: "recorded-resource-evidence"
1498
+ };
1499
+ }
1500
+ }
1501
+ return null;
1502
+ }
1503
+ function predictQuality(evidence, {
1504
+ text: text7,
1505
+ type = "general",
1506
+ provider,
1507
+ model,
1508
+ reasoningEffort = "",
1509
+ channel = "harness-llm"
1510
+ } = {}) {
1511
+ if (!isObj(evidence) || evidence.schemaVersion !== SCHEMA_VERSION || !Array.isArray(evidence.qualitySamples)) return null;
1512
+ if (typeof text7 !== "string" || text7.length === 0) return null;
1513
+ if (!nonEmpty(provider) || !nonEmpty(model)) return null;
1514
+ if (!effortOk(reasoningEffort)) return null;
1515
+ const ch = normalizeChannel(channel);
1516
+ if (!ch) return null;
1517
+ const ty = nonEmpty(type) ? type : "general";
1518
+ const minSamples = evidence.minSamples;
1519
+ if (!Number.isInteger(minSamples) || minSamples < 1 || minSamples > 1e3) return null;
1520
+ const qfeat = buildFeatures(text7);
1521
+ if (qfeat.length === 0) return null;
1522
+ const matched = [];
1523
+ let sumAll = 0;
1524
+ let nAll = 0;
1525
+ for (const s15 of evidence.qualitySamples) {
1526
+ if (!isObj(s15) || !finite(s15.quality) || s15.quality < 0 || s15.quality > 1 || !validateFeatures(s15.features) || s15.split !== "train") continue;
1527
+ if (s15.provider !== provider || s15.model !== model || s15.reasoningEffort !== reasoningEffort || s15.channel !== ch || s15.type !== ty) continue;
1528
+ sumAll += s15.quality;
1529
+ nAll++;
1530
+ const sim = cosine(qfeat, s15.features);
1531
+ if (sim >= SIM_THRESHOLD) matched.push({ s: s15, sim });
1532
+ }
1533
+ if (matched.length === 0) return null;
1534
+ matched.sort((a, b2) => b2.sim - a.sim);
1535
+ const seenGroup = /* @__PURE__ */ new Set();
1536
+ const neighbors = [];
1537
+ for (const m of matched) {
1538
+ if (seenGroup.has(m.s.groupId)) continue;
1539
+ seenGroup.add(m.s.groupId);
1540
+ neighbors.push(m);
1541
+ if (neighbors.length >= MAX_NEIGHBORS) break;
1542
+ }
1543
+ if (neighbors.length < minSamples) return null;
1544
+ const routeTypeMean = nAll > 0 ? sumAll / nAll : neighbors.reduce((a, b2) => a + b2.s.quality, 0) / neighbors.length;
1545
+ let wsum = 0;
1546
+ let wq = 0;
1547
+ for (const n of neighbors) {
1548
+ wsum += n.sim;
1549
+ wq += n.sim * n.s.quality;
1550
+ }
1551
+ if (!(wsum > 0)) return null;
1552
+ const quality = (wq + SHRINK_PRIOR * routeTypeMean) / (wsum + SHRINK_PRIOR);
1553
+ const meanSim = wsum / neighbors.length;
1554
+ const wmean = wq / wsum;
1555
+ let varSum = 0;
1556
+ for (const n of neighbors) {
1557
+ const d = n.s.quality - wmean;
1558
+ varSum += n.sim * d * d;
1559
+ }
1560
+ const spread = Math.sqrt(varSum / wsum);
1561
+ const clamped = Math.min(1, Math.max(0, quality));
1562
+ return {
1563
+ quality: clamped,
1564
+ sampleCount: neighbors.length,
1565
+ similarity: Math.min(1, Math.max(0, meanSim)),
1566
+ source: "recorded-query-evidence",
1567
+ uncertainty: {
1568
+ kind: "empirical-neighbor-spread",
1569
+ neighborCount: neighbors.length,
1570
+ similarityThreshold: SIM_THRESHOLD,
1571
+ spread,
1572
+ calibrated: false,
1573
+ note: "Empirical neighbor spread; not a calibrated probability."
1574
+ }
1575
+ };
1576
+ }
1577
+
1123
1578
  // .dsh-plugin/shared/router.mjs
1124
1579
  var OBJECTIVE_WEIGHTS = Object.freeze({
1125
1580
  simple: Object.freeze({ quality: 0.28, cost: 0.45, latency: 0.14, specialty: 0.04, reasoning: 0.07, risk: 0.02 }),
@@ -1178,7 +1633,7 @@ var OPENCODE_CATALOG_PROVIDERS = Object.freeze([
1178
1633
  ]);
1179
1634
  function classifyTask(text7) {
1180
1635
  const value = String(text7 ?? "");
1181
- if (value.length < 80 && /翻译|解释|translate|explain/i.test(value)) return "general";
1636
+ if (value.length < 80 && /翻译|解释|translate|explain/i.test(value) && !detectTaskTypes(value).some((type) => ["vision", "math", "code", "research"].includes(type))) return "general";
1182
1637
  return detectTaskTypes(value)[0] ?? "general";
1183
1638
  }
1184
1639
  var TASK_TYPE_RULES = Object.freeze([
@@ -1229,10 +1684,12 @@ function specialtyMatch(model, taskType, liveScores = {}) {
1229
1684
  return 0.38;
1230
1685
  }
1231
1686
  function qualityForTask(row, taskType) {
1687
+ if (row?.queryEvidence?.type === taskType) return row.queryEvidence.quality;
1232
1688
  const base = asScore(row?.liveScores?.[taskType]) ?? asScore(row?.liveOverall) ?? row?.metadata?.quality ?? row?.baseQuality ?? row?.quality ?? 0;
1233
1689
  return row?.qualityBias ? clamp(base + row.qualityBias) : base;
1234
1690
  }
1235
1691
  function qualitySourceForTask(row, taskType) {
1692
+ if (row?.queryEvidence?.type === taskType) return "recorded-query-evidence";
1236
1693
  return asScore(row?.liveScores?.[taskType]) !== void 0 || asScore(row?.liveOverall) !== void 0 ? "livebench" : row?.fallbackQualitySource ?? row?.qualitySource ?? "unknown";
1237
1694
  }
1238
1695
  function specialtyForTask(row, taskType) {
@@ -1388,9 +1845,9 @@ function explicitRequirements(text7) {
1388
1845
  const marker = /^(?:[-*•]\s+|\d{1,2}[.)、](?!\d)\s*|[一二三四五六七八九十]{1,3}[、.)]\s*)(.+)$/u;
1389
1846
  const marked = lines.map((line) => marker.exec(line)?.[1]).filter(Boolean).map(requirementText).filter(looksLikeRequirement);
1390
1847
  if (marked.length >= 2) return marked;
1391
- const plain = lines.filter((line) => !/^#{1,6}\s|^```|[::]$/u.test(line));
1392
- if (plain.length >= 2 && plain.every(looksLikeRequirement)) {
1393
- return plain.map(requirementText).filter((item) => item.length >= 3);
1848
+ const plain2 = lines.filter((line) => !/^#{1,6}\s|^```|[::]$/u.test(line));
1849
+ if (plain2.length >= 2 && plain2.every(looksLikeRequirement)) {
1850
+ return plain2.map(requirementText).filter((item) => item.length >= 3);
1394
1851
  }
1395
1852
  const clauses = value.split(/[;;,,]/u).map(requirementText).filter(Boolean);
1396
1853
  if (clauses.length >= 2 && clauses.every(looksLikeRequirement)) {
@@ -1533,7 +1990,7 @@ function compareText2(left, right) {
1533
1990
  return a < b2 ? -1 : a > b2 ? 1 : 0;
1534
1991
  }
1535
1992
  function compareRowsStable(left, right) {
1536
- return compareText2(routeKey(left.provider, left.model), routeKey(right.provider, right.model));
1993
+ return compareText2(routeKey(left.provider, left.model), routeKey(right.provider, right.model)) || compareText2(String(left.actionId ?? ""), String(right.actionId ?? ""));
1537
1994
  }
1538
1995
  function reasoningEffortRank(effort) {
1539
1996
  const normalized2 = String(effort ?? "").trim().toLowerCase().replace(/[\s_-]+/g, "");
@@ -1559,6 +2016,13 @@ function selectReasoningEffort(efforts, preferred = "medium") {
1559
2016
  }
1560
2017
  function reasoningDecision(row, task) {
1561
2018
  const preferred = String(task.preferredReasoningEffort ?? "medium");
2019
+ if (row.reasoningExecution === "unsupported-cli" || row.reasoningExecution === "default") return {
2020
+ reasoningEffort: void 0,
2021
+ reasoningFit: 0.55,
2022
+ preferredReasoningEffort: preferred,
2023
+ multiplier: REASONING_EFFORT_MULTIPLIERS.medium,
2024
+ reasoningExecutionNotice: row.reasoningExecution === "default" ? "\u6307\u5B9A\u6A21\u578B\u4F7F\u7528\u9002\u914D\u5668\u9ED8\u8BA4\u63A8\u7406\u6863\u4F4D\u3002" : "CLI \u63A8\u7406\u6863\u4F4D\u5C1A\u65E0\u7ECF\u9A8C\u8BC1\u7684\u6620\u5C04\uFF0C\u6309\u5382\u5546\u9ED8\u8BA4\u6863\u4F4D\u6267\u884C\u3002"
2025
+ };
1562
2026
  const efforts = Array.isArray(row.reasoningEfforts) ? row.reasoningEfforts : [];
1563
2027
  if (efforts.length === 0) {
1564
2028
  const knownUnsupported = row.reasoningKnown === true;
@@ -1571,7 +2035,7 @@ function reasoningDecision(row, task) {
1571
2035
  };
1572
2036
  }
1573
2037
  const preferredRank = reasoningEffortRank(preferred);
1574
- const chosen = selectReasoningEffort(efforts, preferred);
2038
+ const chosen = Object.hasOwn(row, "actionEffort") ? row.actionEffort : selectReasoningEffort(efforts, preferred);
1575
2039
  const distance = Math.abs(reasoningEffortRank(chosen) - preferredRank);
1576
2040
  return {
1577
2041
  reasoningEffort: chosen,
@@ -1589,16 +2053,88 @@ function candidateUtility(row, task, weights, cacheReadRatio = 0, cacheWriteRati
1589
2053
  const reasoning = reasoningDecision(row, task);
1590
2054
  const reference = taskCost({ pricing: COST_REFERENCE_PRICING, reasoningKnown: true, reasoningEfforts: [] }, task, text7, complexity, cacheReadRatio, cacheWriteRatio);
1591
2055
  const cost = row.pricing === null ? 0 : 1 / (1 + taskCost(row, task, text7, complexity, cacheReadRatio, cacheWriteRatio) / Math.max(reference, 1e-12));
1592
- const score = weights.quality * quality + weights.cost * cost + weights.latency * (1 - clamp(row.latency * reasoning.multiplier.latency)) + weights.specialty * specialtyForTask(row, task.type) + (weights.reasoning ?? 0) * reasoning.reasoningFit - weights.risk * row.risk - qualityGap * (task.criticality ?? 0.75) + preferenceAdjustment;
2056
+ const resources = resourceTokenBudget(row, task, text7, complexity, cacheReadRatio, cacheWriteRatio);
2057
+ const latencyUtility = Number.isFinite(resources.estimatedLatencyMs) ? 1 / (1 + resources.estimatedLatencyMs / 1e4) : 1 - clamp(row.latency * reasoning.multiplier.latency);
2058
+ const score = weights.quality * quality + weights.cost * cost + weights.latency * latencyUtility + weights.specialty * specialtyForTask(row, task.type) + (weights.reasoning ?? 0) * reasoning.reasoningFit - weights.risk * row.risk - qualityGap * (task.criticality ?? 0.75) + preferenceAdjustment;
1593
2059
  return { score, floor, qualityGap, preferenceAdjustment, ...reasoning };
1594
2060
  }
1595
2061
  function taskCost(row, task, text7, complexity, cacheReadRatio = 0, cacheWriteRatio = 0) {
1596
2062
  if (row?.pricing === null) return 0;
1597
- const decision = row === null ? null : reasoningDecision(row, task);
1598
- const tokens = taskTokenBudget(text7, { ...task, reasoningEffort: decision?.reasoningEffort }, complexity, cacheReadRatio, cacheWriteRatio);
2063
+ const tokens = resourceTokenBudget(row, task, text7, complexity, cacheReadRatio, cacheWriteRatio);
1599
2064
  return row === null ? 0 : ((tokens.inputTokens - tokens.cacheReadTokens - tokens.cacheWriteTokens) * row.pricing.input + tokens.cacheReadTokens * row.pricing.cacheRead + tokens.cacheWriteTokens * row.pricing.cacheWrite + tokens.outputTokens * row.pricing.output) / 1e6;
1600
2065
  }
1601
- function dominates(left, right, task, text7, complexity, cacheReadRatio, cacheWriteRatio) {
2066
+ function resourceTokenBudget(row, task, text7, complexity, cacheReadRatio = 0, cacheWriteRatio = 0) {
2067
+ const effort = row ? reasoningDecision(row, task).reasoningEffort : void 0;
2068
+ const fallback = taskTokenBudget(text7, { ...task, reasoningEffort: effort }, complexity, cacheReadRatio, cacheWriteRatio);
2069
+ if (!row?.routingEvidence) return { ...fallback, estimateSource: "heuristic", resourceSampleCount: 0 };
2070
+ const key = JSON.stringify([task.type, task.purpose, effort ?? "", String(text7).length, cacheReadRatio, cacheWriteRatio]);
2071
+ if (row.resourceCache.has(key)) return row.resourceCache.get(key);
2072
+ const observed = predictResources(row.routingEvidence, {
2073
+ provider: row.provider,
2074
+ model: row.model,
2075
+ reasoningEffort: effort ?? "",
2076
+ channel: row.executionChannel ?? "harness-llm",
2077
+ type: task.type,
2078
+ purpose: task.purpose,
2079
+ inputChars: String(text7).length
2080
+ });
2081
+ let result = { ...fallback, estimateSource: "heuristic", resourceSampleCount: 0 };
2082
+ if (observed) {
2083
+ const promptRatio = observed.p90?.promptCharRatio ?? observed.promptCharRatio;
2084
+ const chars = Number.isFinite(promptRatio) ? String(text7).length * promptRatio : fallback.inputTokens * 3.7;
2085
+ const inputTokens = Math.ceil(chars * observed.p90.inputTokenRatio);
2086
+ const outputTokens = Math.ceil(observed.p90.outputTokens);
2087
+ if (Number.isSafeInteger(inputTokens) && inputTokens >= 0 && Number.isSafeInteger(outputTokens) && outputTokens >= 0) {
2088
+ const ratios = normalizedCacheRatios(cacheReadRatio, cacheWriteRatio);
2089
+ const cacheReadTokens = Math.min(inputTokens, Math.round(inputTokens * ratios.read));
2090
+ const cacheWriteTokens = Math.min(inputTokens - cacheReadTokens, Math.round(inputTokens * ratios.write));
2091
+ result = {
2092
+ inputTokens,
2093
+ outputTokens,
2094
+ cacheReadTokens,
2095
+ cacheWriteTokens,
2096
+ estimateSource: observed.source,
2097
+ resourceSampleCount: observed.sampleCount,
2098
+ estimatedLatencyMs: observed.p90.durationMs,
2099
+ empiricalQuantile: 0.9
2100
+ };
2101
+ }
2102
+ }
2103
+ row.resourceCache.set(key, result);
2104
+ return result;
2105
+ }
2106
+ function taskActions(row, task) {
2107
+ const text7 = task.objective || row.routingText;
2108
+ const key = JSON.stringify([task.type, task.purpose, task.preferredReasoningEffort, text7]);
2109
+ if (row.actionCache.has(key)) return row.actionCache.get(key);
2110
+ const nearest = reasoningDecision(row, task).reasoningEffort;
2111
+ const qualityAt = (effort) => {
2112
+ const qualityKey = JSON.stringify([task.type, effort ?? "", text7]);
2113
+ if (!row.qualityCache.has(qualityKey)) row.qualityCache.set(qualityKey, predictQuality(row.routingEvidence, {
2114
+ text: text7,
2115
+ type: task.type,
2116
+ provider: row.provider,
2117
+ model: row.model,
2118
+ reasoningEffort: effort ?? "",
2119
+ channel: row.executionChannel ?? "harness-llm"
2120
+ }));
2121
+ return row.qualityCache.get(qualityKey);
2122
+ };
2123
+ let alternatives = [];
2124
+ if (row.reasoningExecution === "api") alternatives = (row.reasoningEfforts ?? []).map((effort) => ({ effort, quality: qualityAt(effort) })).filter((item) => item.quality);
2125
+ const joint = alternatives.length >= 2;
2126
+ const actions = joint ? alternatives : [{ effort: nearest, quality: qualityAt(nearest) }];
2127
+ const variants = actions.map(({ effort, quality }) => ({
2128
+ ...row,
2129
+ actionEffort: effort,
2130
+ actionId: JSON.stringify([row.provider, row.model, effort ?? null]),
2131
+ jointEffort: joint,
2132
+ queryEvidence: quality ? { ...quality, type: task.type } : null
2133
+ }));
2134
+ row.actionCache.set(key, variants);
2135
+ return variants;
2136
+ }
2137
+ function dominates2(left, right, task, text7, complexity, cacheReadRatio, cacheWriteRatio) {
1602
2138
  if (left.pricing === null || right.pricing === null) return false;
1603
2139
  const leftReasoning = reasoningDecision(left, task);
1604
2140
  const rightReasoning = reasoningDecision(right, task);
@@ -1623,7 +2159,8 @@ function dominates(left, right, task, text7, complexity, cacheReadRatio, cacheWr
1623
2159
  return noWorse && strictlyBetter;
1624
2160
  }
1625
2161
  function eligibleRowsForTask(rows, task) {
1626
- return task.type === "vision" ? rows.filter((row) => row.inputModalities.length === 0 || row.inputModalities.includes("image")) : rows;
2162
+ const eligible = task.type === "vision" ? rows.filter((row) => row.inputModalities.length === 0 || row.inputModalities.includes("image")) : rows;
2163
+ return eligible.flatMap((row) => taskActions(row, task));
1627
2164
  }
1628
2165
  function candidatePool(rows, task, weights, text7, complexity, cacheReadRatio, cacheWriteRatio, dependencySensitive = false) {
1629
2166
  const eligibleRows = eligibleRowsForTask(rows, task);
@@ -1636,7 +2173,7 @@ function candidatePool(rows, task, weights, text7, complexity, cacheReadRatio, c
1636
2173
  decision: candidateUtility(row, task, taskWeights, cacheReadRatio, cacheWriteRatio, text7, complexity),
1637
2174
  cost: taskCost(row, task, text7, complexity, cacheReadRatio, cacheWriteRatio)
1638
2175
  }));
1639
- const frontier = dependencySensitive ? scored : scored.filter((item) => !scored.some((other) => other !== item && other.decision.score >= item.decision.score && dominates(other.row, item.row, task, text7, complexity, cacheReadRatio, cacheWriteRatio)));
2176
+ const frontier = dependencySensitive ? scored : scored.filter((item) => !scored.some((other) => other !== item && other.decision.score >= item.decision.score && dominates2(other.row, item.row, task, text7, complexity, cacheReadRatio, cacheWriteRatio)));
1640
2177
  const essential = [
1641
2178
  scored.filter((item) => item.row.pricing !== null).sort((left, right) => left.cost - right.cost || compareRowsStable(left.row, right.row))[0],
1642
2179
  scored.slice().sort((left, right) => right.decision.score - left.decision.score || compareRowsStable(left.row, right.row))[0],
@@ -1661,6 +2198,8 @@ function solveAssignments({ rows, tasks, weights, text: text7, complexity, budge
1661
2198
  route: routeKey(option.row.provider, option.row.model),
1662
2199
  score: option.decision.score,
1663
2200
  cost: option.cost,
2201
+ actionId: option.row.actionId,
2202
+ handoffRoute: routeKey(option.row.provider, option.row.model),
1664
2203
  qualityShortfall: Math.max(0, option.decision.floor - qualityForTask(option.row, tasks[index].type)),
1665
2204
  pricingKnown: option.row.pricing !== null,
1666
2205
  payload: option
@@ -1670,7 +2209,7 @@ function solveAssignments({ rows, tasks, weights, text: text7, complexity, budge
1670
2209
  ...result,
1671
2210
  assignments: result.choices.map((choice, index) => ({
1672
2211
  task: tasks[index],
1673
- row: choice.payload.row,
2212
+ row: { ...choice.payload.row, estimatedCost: choice.payload.row.pricing === null ? null : choice.cost },
1674
2213
  decision: { ...choice.payload.decision, relaxed: choice.qualityShortfall > 0 },
1675
2214
  estimatedCost: choice.cost,
1676
2215
  handoffPenalty: choice.handoffPenalty
@@ -1680,7 +2219,7 @@ function solveAssignments({ rows, tasks, weights, text: text7, complexity, budge
1680
2219
  minimumFeasibleCost: result.minimumCostLowerBound
1681
2220
  };
1682
2221
  }
1683
- function buildPlan({ text: text7 = "", available = [], mode = "collective", pricing = {}, liveBench = null, liveBenchError = "", dataVersions = null, learning = null, budgetUsd = 0, cacheReadRatio = 0, cacheWriteRatio = 0, preset = DEFAULT_ROUTING_PRESET } = {}) {
2222
+ function buildPlan({ text: text7 = "", available = [], mode = "collective", pricing = {}, liveBench = null, liveBenchError = "", dataVersions = null, learning = null, routingEvidence = null, budgetUsd = 0, cacheReadRatio = 0, cacheWriteRatio = 0, preset = DEFAULT_ROUTING_PRESET } = {}) {
1684
2223
  const presetId = normalizeRoutingPreset(preset);
1685
2224
  const firstLine = String(text7 ?? "").split(/\r?\n/u)[0].trim();
1686
2225
  const transformOnly = /^(?:请|帮我)?(?:总结|概括|翻译|摘要|解释)(?:以下|下列|下面|这份|这些)/u.test(firstLine) && !/(?:执行|完成|实施|分配)/u.test(firstLine);
@@ -1696,6 +2235,8 @@ function buildPlan({ text: text7 = "", available = [], mode = "collective", pric
1696
2235
  return {
1697
2236
  provider: String(entry.provider ?? ""),
1698
2237
  model: String(entry.model ?? ""),
2238
+ executionChannel: ["harness-llm", "official-cli"].includes(entry.executionChannel) ? entry.executionChannel : void 0,
2239
+ reasoningExecution: ["api", "unsupported-cli", "default"].includes(entry.reasoningExecution) ? entry.reasoningExecution : void 0,
1699
2240
  benchmarkModel: typeof entry.benchmarkModel === "string" ? entry.benchmarkModel : void 0,
1700
2241
  reasoningEfforts: [...new Set(reasoningEfforts)],
1701
2242
  defaultReasoningEffort: entry.defaultReasoningEffort === void 0 ? void 0 : String(entry.defaultReasoningEffort),
@@ -1758,6 +2299,13 @@ function buildPlan({ text: text7 = "", available = [], mode = "collective", pric
1758
2299
  reasoningEfforts: route.reasoningEfforts,
1759
2300
  defaultReasoningEffort: route.defaultReasoningEffort,
1760
2301
  reasoningKnown: route.reasoningKnown,
2302
+ reasoningExecution: route.reasoningExecution,
2303
+ executionChannel: route.executionChannel,
2304
+ routingEvidence,
2305
+ routingText: text7,
2306
+ actionCache: /* @__PURE__ */ new Map(),
2307
+ qualityCache: /* @__PURE__ */ new Map(),
2308
+ resourceCache: /* @__PURE__ */ new Map(),
1761
2309
  inputModalities: route.inputModalities,
1762
2310
  pricing: pricingRow,
1763
2311
  score: 0,
@@ -1834,13 +2382,14 @@ function buildPlan({ text: text7 = "", available = [], mode = "collective", pric
1834
2382
  recommendedReasoningEffort: decision?.reasoningEffort,
1835
2383
  preferredReasoningEffort: decision?.preferredReasoningEffort ?? task.preferredReasoningEffort,
1836
2384
  reasoningFit: Number(Number(decision?.reasoningFit ?? 0).toFixed(3)),
2385
+ ...decision?.reasoningExecutionNotice ? { reasoningExecutionNotice: decision.reasoningExecutionNotice } : {},
1837
2386
  purpose: task.purpose,
1838
2387
  criticality: task.criticality,
1839
2388
  qualityFloor: Number(task.qualityFloor.toFixed(3)),
1840
2389
  dependsOn: [...task.dependsOn ?? []]
1841
2390
  }));
1842
2391
  const costBreakdown = assignments.map(({ task, row, decision, estimatedCost, handoffPenalty }, index) => {
1843
- const tokens = taskTokenBudget(text7, { ...task, reasoningEffort: decision?.reasoningEffort }, complexity.band, cacheReadRatio, cacheWriteRatio);
2392
+ const tokens = resourceTokenBudget(row, task, text7, complexity.band, cacheReadRatio, cacheWriteRatio);
1844
2393
  const taskEstimate = estimatedCost ?? taskCost(row, task, text7, complexity.band, cacheReadRatio, cacheWriteRatio);
1845
2394
  return {
1846
2395
  stage: index + 1,
@@ -1856,6 +2405,11 @@ function buildPlan({ text: text7 = "", available = [], mode = "collective", pric
1856
2405
  cacheReadTokens: tokens.cacheReadTokens,
1857
2406
  cacheWriteTokens: tokens.cacheWriteTokens,
1858
2407
  outputTokens: tokens.outputTokens,
2408
+ estimateSource: tokens.estimateSource,
2409
+ resourceSampleCount: tokens.resourceSampleCount,
2410
+ ...Number.isFinite(tokens.estimatedLatencyMs) ? { estimatedLatencyMs: tokens.estimatedLatencyMs } : {},
2411
+ ...tokens.empiricalQuantile ? { empiricalQuantile: tokens.empiricalQuantile } : {},
2412
+ ...row?.queryEvidence ? { queryEvidenceCount: row.queryEvidence.sampleCount, querySimilarity: row.queryEvidence.similarity } : {},
1859
2413
  estimatedCost: row?.pricing === null || row === null ? null : Number(taskEstimate.toFixed(6)),
1860
2414
  quality: qualitySourceForTask(row, task.type) === "unknown" || row === null ? null : Number(qualityForTask(row, task.type).toFixed(3)),
1861
2415
  qualitySource: qualitySourceForTask(row, task.type),
@@ -1870,7 +2424,8 @@ function buildPlan({ text: text7 = "", available = [], mode = "collective", pric
1870
2424
  return { task, strongest };
1871
2425
  });
1872
2426
  const baselineCost = baselineRows.every((item) => item.strongest?.pricing !== null && item.strongest !== null) ? baselineRows.reduce((sum, { task, strongest }) => sum + taskCost(strongest, task, text7, complexity.band, cacheReadRatio, cacheWriteRatio), 0) : null;
1873
- const qualityEvidenceComplete = assignments.length > 0 && assignments.every(({ row, task }) => ["livebench", "route", "user"].includes(qualitySourceForTask(row, task.type))) && baselineRows.every(({ strongest, task }) => ["livebench", "route", "user"].includes(qualitySourceForTask(strongest, task.type)));
2427
+ const knownQualitySources = ["livebench", "route", "user", "recorded-query-evidence"];
2428
+ const qualityEvidenceComplete = assignments.length > 0 && assignments.every(({ row, task }) => knownQualitySources.includes(qualitySourceForTask(row, task.type))) && baselineRows.every(({ strongest, task }) => knownQualitySources.includes(qualitySourceForTask(strongest, task.type)));
1874
2429
  const budgetExceeded = Number(budgetUsd) > 0 && totalEstimate !== null ? totalEstimate > Number(budgetUsd) : null;
1875
2430
  const savings = baselineCost === null || totalEstimate === null || !qualityEvidenceComplete ? null : baselineCost <= 0 ? 0 : clamp((baselineCost - totalEstimate) / baselineCost);
1876
2431
  const paretoPruned = (optimized?.candidatePools ?? []).reduce((sum, pool) => sum + pool.pruned, 0);
@@ -1885,7 +2440,13 @@ function buildPlan({ text: text7 = "", available = [], mode = "collective", pric
1885
2440
  taskType,
1886
2441
  taskTypes: [...new Set(taskNodes.map((task) => task.type).filter((type) => type !== "reasoning"))],
1887
2442
  objectiveWeights: weights,
1888
- candidates: rows.slice(0, 8).map((row) => {
2443
+ candidates: eligibleRowsForTask(rows, taskNodes[0]).map((row) => ({
2444
+ ...row,
2445
+ score: candidateUtility(row, taskNodes[0], weights, cacheReadRatio, cacheWriteRatio, text7, complexity.band).score,
2446
+ quality: qualityForTask(row, taskNodes[0].type),
2447
+ qualitySource: qualitySourceForTask(row, taskNodes[0].type),
2448
+ estimatedCost: row.pricing === null ? null : taskCost(row, taskNodes[0], text7, complexity.band, cacheReadRatio, cacheWriteRatio)
2449
+ })).sort((left, right) => right.score - left.score || compareRowsStable(left, right)).slice(0, 8).map((row) => {
1889
2450
  const decision = candidateUtility(row, taskNodes[0] ?? { type: taskType, qualityFloor: QUALITY_FLOORS[complexity.band], preferredReasoningEffort: complexity.band === "simple" ? "low" : "medium" }, weights, cacheReadRatio, cacheWriteRatio, text7, complexity.band);
1890
2451
  return { provider: row.provider, model: row.model, score: Number(row.score.toFixed(3)), preferenceAdjustment: decision.preferenceAdjustment, quality: row.qualitySource === "unknown" ? null : Number(row.quality.toFixed(3)), qualitySource: row.qualitySource, specialty: Number(row.specialty.toFixed(3)), reasoningEffort: decision.reasoningEffort, preferredReasoningEffort: decision.preferredReasoningEffort, reasoningFit: Number(decision.reasoningFit.toFixed(3)), reasoningKnown: row.reasoningKnown, reasoningEfforts: row.reasoningEfforts, estimatedCost: row.estimatedCost === null ? null : Number(row.estimatedCost.toFixed(6)), inputPrice: row.pricing?.input ?? null, outputPrice: row.pricing?.output ?? null, pricingSource: row.pricingSource, pricingVersion: row.pricingVersion, pricingAsOf: row.snapshotAsOf };
1891
2452
  }),
@@ -1897,6 +2458,11 @@ function buildPlan({ text: text7 = "", available = [], mode = "collective", pric
1897
2458
  costBreakdown,
1898
2459
  optimization: {
1899
2460
  dataVersions,
2461
+ evidenceRevision: routingEvidence?.revision ?? null,
2462
+ qualityEvidenceCount: assignments.filter(({ row }) => row.queryEvidence).length,
2463
+ resourceEvidenceCount: costBreakdown.filter((item) => item.estimateSource === "recorded-resource-evidence").length,
2464
+ jointEffortCandidates: (optimized?.candidatePools ?? []).reduce((sum, pool) => sum + pool.options.filter((item) => item.row.jointEffort).length, 0),
2465
+ uncertaintySemantics: "empirical-prediction-not-calibrated-probability-or-joint-budget-guarantee",
1900
2466
  personalization: learning ? {
1901
2467
  policyVersion: learning.policyVersion,
1902
2468
  revision: learning.revision,
@@ -2009,12 +2575,14 @@ function createPlanFromRoutes(task, availableRoutes, {
2009
2575
  liveBenchError = "",
2010
2576
  dataVersions = null,
2011
2577
  learning = null,
2578
+ routingEvidence = null,
2012
2579
  cacheReadRatio = 0,
2013
2580
  cacheWriteRatio = 0,
2014
2581
  directProvider = "",
2015
2582
  directModel = "",
2016
2583
  preset = "balanced",
2017
- loggedOutToolIds = []
2584
+ loggedOutToolIds = [],
2585
+ forceOfficialCli = false
2018
2586
  } = {}) {
2019
2587
  const taskText = clean2(task);
2020
2588
  if (!taskText) throw new Error("task must contain text");
@@ -2036,13 +2604,28 @@ function createPlanFromRoutes(task, availableRoutes, {
2036
2604
  const channelOf = (provider, model) => {
2037
2605
  const route = routes.find((item) => item.provider === provider && item.model === model);
2038
2606
  const key = `${String(provider ?? "")}\0${String(model ?? "")}\0${route?.execution ?? ""}`;
2039
- if (!channelCache.has(key)) channelCache.set(key, channelForProvider(provider, installedIds, runnableToolIds, route, loggedOutToolIds));
2607
+ if (!channelCache.has(key)) {
2608
+ const tool = forceOfficialCli ? toolForProvider(provider) : null;
2609
+ channelCache.set(key, tool ? {
2610
+ kind: "official-cli",
2611
+ tool: tool.id,
2612
+ label: tool.label,
2613
+ detail: "\u5B98\u65B9 CLI \u56E2\u961F\u8BA1\u5212\u4EC5\u901A\u8FC7\u5DF2\u6838\u9A8C\u7684\u5B98\u65B9 CLI \u6267\u884C\uFF1B\u4F7F\u7528\u5382\u5546\u9ED8\u8BA4\u63A8\u7406\u6863\u4F4D\u3002"
2614
+ } : channelForProvider(provider, installedIds, runnableToolIds, route, loggedOutToolIds));
2615
+ }
2040
2616
  return channelCache.get(key);
2041
2617
  };
2042
2618
  const needsImage = detectTaskTypes(taskText).includes("vision");
2043
2619
  const plan = buildPlan({
2044
2620
  text: taskText,
2045
- available: routes,
2621
+ available: routes.map((route) => {
2622
+ const channel = channelOf(route.provider, route.model);
2623
+ return {
2624
+ ...route,
2625
+ executionChannel: channel.kind,
2626
+ reasoningExecution: directRoute ? "default" : channel.kind === "harness-llm" ? "api" : "unsupported-cli"
2627
+ };
2628
+ }),
2046
2629
  mode: directRoute ? "single" : selectedMode,
2047
2630
  budgetUsd: Math.max(0, Number.isFinite(budgetUsd) ? budgetUsd : 0),
2048
2631
  pricing,
@@ -2050,6 +2633,7 @@ function createPlanFromRoutes(task, availableRoutes, {
2050
2633
  liveBenchError,
2051
2634
  dataVersions,
2052
2635
  learning,
2636
+ routingEvidence,
2053
2637
  cacheReadRatio,
2054
2638
  cacheWriteRatio,
2055
2639
  preset
@@ -2087,6 +2671,7 @@ function createPlanFromRoutes(task, availableRoutes, {
2087
2671
  recommendedModel: item.recommended,
2088
2672
  estimatedCost: plan.costBreakdown[index]?.estimatedCost ?? null,
2089
2673
  ...item.recommendedReasoningEffort ? { recommendedReasoningEffort: item.recommendedReasoningEffort } : {},
2674
+ ...item.reasoningExecutionNotice ? { reasoningExecutionNotice: item.reasoningExecutionNotice } : {},
2090
2675
  ...annotate(channelOf(item.recommendedProvider, item.recommended)),
2091
2676
  verificationChecklist: item.purpose === "synthesis" ? ["\u6838\u5BF9\u5404\u5DE5\u4F5C\u5305\u4EA4\u4ED8\u7269\u4E0E\u4F9D\u8D56", "\u8BB0\u5F55\u51B2\u7A81\u3001\u672A\u89E3\u51B3\u4E8B\u9879\u548C\u6700\u7EC8\u9A8C\u6536\u7ED3\u679C"] : item.type === "code" ? ["\u8BF4\u660E\u6539\u52A8\u6587\u4EF6\u4E0E\u63A5\u53E3", "\u8FD0\u884C\u4E0E\u6539\u52A8\u76F8\u5173\u7684\u9A8C\u8BC1\u5E76\u8BB0\u5F55\u7ED3\u679C", "\u5217\u51FA\u5C1A\u672A\u5B8C\u6210\u7684\u8FB9\u754C\u60C5\u51B5"] : item.type === "research" ? ["\u5217\u51FA\u6765\u6E90\u3001\u65E5\u671F\u548C\u53EF\u6838\u5BF9\u7684\u7ED3\u8BBA", "\u6807\u51FA\u63A8\u65AD\u4E0E\u4E0D\u786E\u5B9A\u4E8B\u9879"] : ["\u5217\u51FA\u4EA4\u4ED8\u5185\u5BB9\u548C\u9A8C\u6536\u4F9D\u636E", "\u6807\u51FA\u672A\u5B8C\u6210\u4E8B\u9879"]
2092
2677
  }))
@@ -2094,13 +2679,68 @@ function createPlanFromRoutes(task, availableRoutes, {
2094
2679
  };
2095
2680
  }
2096
2681
 
2682
+ // .dsh-plugin/shared/package-assignments.mjs
2683
+ var plain = (value) => value !== null && typeof value === "object" && (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null);
2684
+ var safeText = (value, limit) => typeof value === "string" && value.trim().length > 0 && value.trim().length <= limit && !/[\u0000-\u001f\u007f]/u.test(value);
2685
+ var safeKey = (key) => typeof key === "string" && key === key.trim() && safeText(key, 128) && !["__proto__", "constructor", "prototype"].includes(key);
2686
+ function parsePackageAssignments(value) {
2687
+ if (value === void 0) return void 0;
2688
+ if (!plain(value)) throw new TypeError("packageAssignments must be an object");
2689
+ const keys = Reflect.ownKeys(value);
2690
+ if (!keys.length || keys.length > 32 || keys.some((key) => !safeKey(key))) {
2691
+ throw new TypeError("packageAssignments requires 1..32 safe package ids");
2692
+ }
2693
+ const result = {};
2694
+ for (const id2 of keys) {
2695
+ const target = value[id2];
2696
+ if (!plain(target)) throw new TypeError(`Invalid package assignment: ${id2}`);
2697
+ const allowed = target.kind === "api" ? ["kind", "provider", "model"] : target.kind === "tool" ? ["kind", "toolId"] : null;
2698
+ if (!allowed || Reflect.ownKeys(target).some((key) => !allowed.includes(key))) {
2699
+ throw new TypeError(`Unsupported package assignment fields: ${id2}`);
2700
+ }
2701
+ if (target.kind === "api") {
2702
+ if (!safeText(target.provider, 160) || !safeText(target.model, 240)) {
2703
+ throw new TypeError(`API assignment requires provider and model: ${id2}`);
2704
+ }
2705
+ result[id2] = { kind: "api", provider: target.provider.trim(), model: target.model.trim() };
2706
+ } else {
2707
+ if (!safeText(target.toolId, 64) || !getOfficialTool(target.toolId.trim())) {
2708
+ throw new TypeError(`Unknown official tool assignment: ${id2}`);
2709
+ }
2710
+ result[id2] = { kind: "tool", toolId: target.toolId.trim() };
2711
+ }
2712
+ }
2713
+ return result;
2714
+ }
2715
+ function packagePlanKey(plan, task = "") {
2716
+ const packages = plan?.team?.workPackages;
2717
+ if (plan?.routingBypassed || plan?.mode === "direct" || !Array.isArray(packages) || !packages.length) return null;
2718
+ const fields = packages.map((item) => [
2719
+ item?.id ?? "",
2720
+ item?.name ?? "",
2721
+ item?.objective ?? "",
2722
+ item?.type ?? "",
2723
+ item?.purpose ?? "",
2724
+ Array.isArray(item?.dependsOn) ? [...item.dependsOn] : []
2725
+ ]);
2726
+ const text7 = JSON.stringify([String(task ?? "").trim(), fields]);
2727
+ let first = 2166136261;
2728
+ let second = 2246822507;
2729
+ for (let index = 0; index < text7.length; index++) {
2730
+ const code = text7.charCodeAt(index);
2731
+ first = Math.imul(first ^ code, 16777619) >>> 0;
2732
+ second = Math.imul(second ^ code, 3266489909) >>> 0;
2733
+ }
2734
+ return `package-plan-v1-${first.toString(16).padStart(8, "0")}${second.toString(16).padStart(8, "0")}`;
2735
+ }
2736
+
2097
2737
  // .dsh-plugin/shared/run-ledger.mjs
2098
- var finite = (value) => typeof value === "number" && Number.isFinite(value);
2738
+ var finite2 = (value) => typeof value === "number" && Number.isFinite(value);
2099
2739
  var routeKey2 = (provider, model) => `${String(provider ?? "")}\0${String(model ?? "")}`;
2100
2740
  var MAX_SPENDING_DAYS = 90;
2101
2741
  var MAX_SPENDING_MONTHS = 24;
2102
2742
  function periodSpending(value) {
2103
- const amount = (field2) => finite(value?.[field2]) && value[field2] >= 0 ? value[field2] : 0;
2743
+ const amount = (field2) => finite2(value?.[field2]) && value[field2] >= 0 ? value[field2] : 0;
2104
2744
  return {
2105
2745
  costUsd: amount("costUsd"),
2106
2746
  unknown: Math.floor(amount("unknown")),
@@ -2120,9 +2760,9 @@ function formatUsd(value) {
2120
2760
  }
2121
2761
  function budgetCheck({ estimateUsd = null, spent = { today: 0, month: 0 }, dailyLimitUsd = 0, monthlyLimitUsd = 0 } = {}) {
2122
2762
  const limits = [];
2123
- if (finite(dailyLimitUsd) && dailyLimitUsd > 0) limits.push({ period: "daily", label: "\u4ECA\u65E5", limit: dailyLimitUsd, spent: spent.today ?? 0 });
2124
- if (finite(monthlyLimitUsd) && monthlyLimitUsd > 0) limits.push({ period: "monthly", label: "\u672C\u6708", limit: monthlyLimitUsd, spent: spent.month ?? 0 });
2125
- const estimateKnown = finite(estimateUsd);
2763
+ if (finite2(dailyLimitUsd) && dailyLimitUsd > 0) limits.push({ period: "daily", label: "\u4ECA\u65E5", limit: dailyLimitUsd, spent: spent.today ?? 0 });
2764
+ if (finite2(monthlyLimitUsd) && monthlyLimitUsd > 0) limits.push({ period: "monthly", label: "\u672C\u6708", limit: monthlyLimitUsd, spent: spent.month ?? 0 });
2765
+ const estimateKnown = finite2(estimateUsd);
2126
2766
  const remaining = limits.length ? Math.max(0, Math.min(...limits.map((item) => item.limit - item.spent))) : null;
2127
2767
  const exceeded = limits.find((item) => item.spent >= item.limit || estimateKnown && item.spent + estimateUsd > item.limit) ?? null;
2128
2768
  return {
@@ -2138,12 +2778,12 @@ function applyQualityBiases(routes, biases) {
2138
2778
  if (!biases || typeof biases !== "object") return routes;
2139
2779
  return (Array.isArray(routes) ? routes : []).map((route) => {
2140
2780
  const bias = biases[routeKey2(route.provider, route.model)];
2141
- return finite(bias) && bias !== 0 ? { ...route, qualityBias: bias } : route;
2781
+ return finite2(bias) && bias !== 0 ? { ...route, qualityBias: bias } : route;
2142
2782
  });
2143
2783
  }
2144
2784
 
2145
2785
  // .dsh-plugin/shared/adaptive-feedback.mjs
2146
- var finite2 = (value) => typeof value === "number" && Number.isFinite(value);
2786
+ var finite3 = (value) => typeof value === "number" && Number.isFinite(value);
2147
2787
  var clean3 = (value) => typeof value === "string" ? value.trim() : "";
2148
2788
  var routeKey3 = (provider, model) => `${provider}\0${model}`;
2149
2789
  var record = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
@@ -2162,8 +2802,8 @@ function applyFeedbackProfile(routes, profile) {
2162
2802
  if (!Object.hasOwn(profile.adjustments, key)) return base;
2163
2803
  const value = profile.adjustments[key];
2164
2804
  if (!record(value)) return base;
2165
- const limit = finite2(profile.maxAdjustment) && profile.maxAdjustment >= 0 && profile.maxAdjustment <= 0.1 ? profile.maxAdjustment : 0.1;
2166
- const adjustments = Object.fromEntries(Object.entries(value).filter(([, bias]) => finite2(bias) && Math.abs(bias) <= limit));
2805
+ const limit = finite3(profile.maxAdjustment) && profile.maxAdjustment >= 0 && profile.maxAdjustment <= 0.1 ? profile.maxAdjustment : 0.1;
2806
+ const adjustments = Object.fromEntries(Object.entries(value).filter(([, bias]) => finite3(bias) && Math.abs(bias) <= limit));
2167
2807
  if (!Object.keys(adjustments).length) return base;
2168
2808
  return {
2169
2809
  ...base,
@@ -2801,7 +3441,59 @@ function billingSwitchText(item) {
2801
3441
  if (!note?.reason) return null;
2802
3442
  return note.reason;
2803
3443
  }
2804
- function launchRequest({ task, mode, directRoute, budgetUsd, preset, workspace, executor = "routed", executionMode = "read-only", executionEnvironment = "harness" } = {}) {
3444
+ function initialPackageAssignments(plan) {
3445
+ if (plan?.routingBypassed || plan?.mode === "direct") return {};
3446
+ const packages = plan?.team?.workPackages ?? [];
3447
+ if (!packages.length) return {};
3448
+ return parsePackageAssignments(Object.fromEntries(packages.map((item) => [
3449
+ item.id,
3450
+ { kind: "api", provider: item.recommendedProvider, model: item.recommendedModel }
3451
+ ])));
3452
+ }
3453
+ function manualAssignmentOptions(routes = [], toolSnapshot = null, health = null) {
3454
+ const options = [];
3455
+ const seen = /* @__PURE__ */ new Set();
3456
+ for (const route of routes) {
3457
+ if (!route?.provider || !route?.model) continue;
3458
+ const binding = { kind: "api", provider: route.provider, model: route.model };
3459
+ const value = JSON.stringify(binding);
3460
+ if (seen.has(value)) continue;
3461
+ seen.add(value);
3462
+ options.push({ value, binding, label: `${route.provider}/${route.model} \xB7 \u6A21\u578B API`, disabled: false, reason: "" });
3463
+ }
3464
+ for (const tool of OFFICIAL_TOOLS) {
3465
+ const probe = toolSnapshot?.probes?.find((item) => item.id === tool.id);
3466
+ const report = health?.tools?.find((item) => item.id === tool.id);
3467
+ const ready = toolSnapshot?.readiness?.find((item) => item.id === tool.id);
3468
+ const capability = toolSnapshot?.capabilities?.find((item) => item.id === tool.id);
3469
+ const installed = probe ? probe.installed === true : report?.installed === true;
3470
+ const modes = ready?.readyModes ?? ready?.modes ?? capability?.modes;
3471
+ const reason = !installed ? "\u672A\u5B89\u88C5\u6216\u5B89\u88C5\u72B6\u6001\u672A\u77E5" : capability?.supported === false ? capability.reason || "\u4E0D\u652F\u6301\u6258\u7BA1\u6267\u884C" : ready?.ready === false ? ready.reason || "\u6267\u884C\u5165\u53E3\u5C1A\u672A\u5C31\u7EEA" : Array.isArray(modes) && !modes.includes("read-only") ? "\u4E0D\u652F\u6301\u53EA\u8BFB\u4EFB\u52A1" : report?.login?.state === "logged-out" ? "\u5C1A\u672A\u767B\u5F55" : "";
3472
+ const pending = !reason && ready?.ready !== true ? " \xB7 \u5F85\u6267\u884C\u9884\u89C8\u6838\u9A8C" : "";
3473
+ const binding = { kind: "tool", toolId: tool.id };
3474
+ options.push({
3475
+ value: JSON.stringify(binding),
3476
+ binding,
3477
+ disabled: Boolean(reason),
3478
+ reason,
3479
+ label: `${tool.label} \xB7 \u5B98\u65B9\u5DE5\u5177\u9ED8\u8BA4\u6A21\u578B${reason ? `\uFF08${reason}\uFF09` : pending}`
3480
+ });
3481
+ }
3482
+ return options;
3483
+ }
3484
+ function launchRequest({ task, mode, directRoute, budgetUsd, preset, workspace, executor = "routed", executionMode = "read-only", executionEnvironment = "harness", assignmentMode = "algorithm", packageAssignments, packagePlanKey: packagePlanKey2 } = {}) {
3485
+ if (!["algorithm", "manual"].includes(assignmentMode)) throw new Error("\u4E0D\u652F\u6301\u7684\u5206\u914D\u65B9\u5F0F\u3002");
3486
+ let manual = null;
3487
+ if (assignmentMode === "manual") {
3488
+ if (mode === "direct") throw new Error("\u6307\u5B9A\u5355\u4E00\u6A21\u578B\u6A21\u5F0F\u4E0D\u652F\u6301\u624B\u52A8\u5DE5\u4F5C\u5305\u5206\u914D\u3002");
3489
+ if (typeof packagePlanKey2 !== "string" || !packagePlanKey2.trim() || packagePlanKey2.length > 200) throw new Error("\u8BF7\u91CD\u65B0\u751F\u6210\u5DE5\u4F5C\u5305\u540E\u518D\u624B\u52A8\u5206\u914D\u3002");
3490
+ const assignments = parsePackageAssignments(packageAssignments);
3491
+ if (!assignments) throw new Error("\u8BF7\u9009\u62E9\u5DE5\u4F5C\u5305\u6267\u884C\u76EE\u6807\u3002");
3492
+ manual = { assignmentMode: "manual", packagePlanKey: packagePlanKey2, packageAssignments: assignments };
3493
+ executor = "routed";
3494
+ executionMode = "read-only";
3495
+ executionEnvironment = "harness";
3496
+ }
2805
3497
  if (!["routed", "official-team"].includes(executor)) throw new Error("\u4E0D\u652F\u6301\u7684\u6267\u884C\u65B9\u5F0F\u3002");
2806
3498
  if (!["read-only", "workspace-write"].includes(executionMode)) throw new Error("\u4E0D\u652F\u6301\u7684\u4EFB\u52A1\u6743\u9650\u3002");
2807
3499
  if (executor === "official-team" && mode !== "team") throw new Error("\u5B98\u65B9\u5DE5\u5177\u534F\u4F5C\u9700\u8981\u5148\u5728\u4E0A\u65B9\u9009\u62E9\u201C\u56E2\u961F\u5206\u5DE5\u201D\u3002");
@@ -2814,7 +3506,8 @@ function launchRequest({ task, mode, directRoute, budgetUsd, preset, workspace,
2814
3506
  ...executionEnvironment === "cli-native" ? { executionEnvironment } : {},
2815
3507
  ...mode === "direct" && directRoute ? { provider: directRoute.provider, model: directRoute.model } : { planMode: mode === "team" ? "team" : "single", preset },
2816
3508
  ...Number.isFinite(budget) && budget >= 0 ? { budgetUsd: budget } : {},
2817
- ...typeof workspace === "string" && workspace.trim() ? { workspace: workspace.trim() } : {}
3509
+ ...typeof workspace === "string" && workspace.trim() ? { workspace: workspace.trim() } : {},
3510
+ ...manual ?? {}
2818
3511
  };
2819
3512
  }
2820
3513
  function rerunConfirmations({ run, item, override = null, choice = null, ledger = null, health = null, toolFor = () => null, headlessIds = /* @__PURE__ */ new Set() } = {}) {
@@ -2844,6 +3537,10 @@ function rerunConfirmations({ run, item, override = null, choice = null, ledger
2844
3537
 
2845
3538
  // .dsh-plugin/client/adaptive-state.mjs
2846
3539
  var ADAPTIVE_DEFAULTS = Object.freeze({
3540
+ routingEvidenceJson: "",
3541
+ resourceLearningEnabled: true,
3542
+ qualityEscalationEnabled: false,
3543
+ qualityGateJson: "",
2847
3544
  feedbackLearningEnabled: true,
2848
3545
  feedbackHalfLifeDays: 30,
2849
3546
  feedbackPriorWeight: 3,
@@ -3265,7 +3962,7 @@ function RunNode({ run, item, routes, allowReassign, busy, onRate, onRerun }) {
3265
3962
  const rerun = rerunSupport(run);
3266
3963
  const canRerun = rerun.supported && !item.ok && !busy;
3267
3964
  const others = routes.filter((route) => `${route.provider}/${route.model}` !== `${item.provider}/${item.model}`);
3268
- return /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-route" }, item.provider, "/", item.model, item.reassigned ? "\uFF08\u5DF2\u6539\u6D3E\uFF09" : ""), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-channel-line" }, /* @__PURE__ */ import_react2.default.createElement(ChannelText, { item }), /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, "\u9884\u4F30 ", money(item.estimatedCost), " \xB7 \u5B9E\u9645 ", cost.budget, item.difficulty ? ` \xB7 \u96BE\u5EA6 ${BAND[item.difficulty] ?? item.difficulty}` : "")), cost.reference && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, cost.reference), item.actualModel && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, "CLI \u56DE\u62A5\u6A21\u578B\uFF1A", item.actualModel), (item.billing || item.billingMode) && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, "\u8BA1\u8D39\uFF1A", BILLING_CHANNEL_LABEL[item.billing] ?? "\u2014", item.subscriptionRoute ? ` \xB7 \u5957\u9910\u8DEF\u7EBF ${item.subscriptionRoute.provider}/${item.subscriptionRoute.model}` : "", item.billingMode && item.billingMode !== "subscription-first" ? ` \xB7 ${BILLING_MODE_LABEL[item.billingMode] ?? item.billingMode}` : ""), billingSwitchText(item) && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy mr-warn-text" }, "\u8BA1\u8D39\u5207\u6362\uFF1A", billingSwitchText(item)), item.fallback?.reason && item.fallback.reason !== billingSwitchText(item) && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, "\u56DE\u9000\u539F\u56E0\uFF1A", item.fallback.reason), item.fallback?.error && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy mr-fallback-error" }, "CLI \u539F\u59CB\u9519\u8BEF\uFF1A", /* @__PURE__ */ import_react2.default.createElement("code", null, item.fallback.error)), !item.ok && item.error && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy mr-fallback-error" }, item.error), !item.ok && item.diagnostic && /* @__PURE__ */ import_react2.default.createElement("details", { className: "mr-tool-log" }, /* @__PURE__ */ import_react2.default.createElement("summary", null, "CLI \u8BCA\u65AD", Number.isInteger(item.exitCode) ? `\uFF08\u9000\u51FA\u7801 ${item.exitCode}\uFF09` : ""), /* @__PURE__ */ import_react2.default.createElement("pre", null, item.diagnostic)), item.review && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, "\u5F3A\u6A21\u578B\u62BD\u67E5\uFF08", item.review.provider, "/", item.review.model, "\uFF09\uFF1A", item.review.score ? `${item.review.score}/5` : "\u672A\u8BC4\u5206", " ", item.review.summary), item.answer && /* @__PURE__ */ import_react2.default.createElement("details", { className: "mr-tool-log" }, /* @__PURE__ */ import_react2.default.createElement("summary", null, "\u67E5\u770B\u7ED3\u679C"), /* @__PURE__ */ import_react2.default.createElement("pre", null, item.answer, item.answerTruncated ? "\n\u2026\uFF08\u5DF2\u622A\u65AD\uFF09" : "")), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-node-actions" }, item.ok && /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary mr-mini", type: "button", title: item.rating === 1 ? "\u518D\u6B21\u70B9\u51FB\u64A4\u56DE\u6709\u7528\u8BC4\u4EF7" : "\u8BC4\u4EF7\u6B64\u7ED3\u679C\u6709\u7528", "aria-pressed": item.rating === 1, disabled: busy, onClick: () => onRate(...feedbackRatingArguments(run, item, "up")) }, "\u{1F44D} \u6709\u7528"), /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary mr-mini", type: "button", title: item.rating === -1 ? "\u518D\u6B21\u70B9\u51FB\u64A4\u56DE\u4E0D\u597D\u8BC4\u4EF7" : "\u8BC4\u4EF7\u6B64\u7ED3\u679C\u4E0D\u597D", "aria-pressed": item.rating === -1, disabled: busy, onClick: () => onRate(...feedbackRatingArguments(run, item, "down")) }, "\u{1F44E} \u4E0D\u597D")), canRerun && /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-mini", type: "button", title: rerun.confirm || void 0, onClick: () => onRerun(run.id, item.id, null) }, run.kind === "tool" ? "\u91CD\u65B0\u6267\u884C\u6B64\u8C03\u7528" : rerun.writes ? "\u5728\u65B0\u5DE5\u4F5C\u533A\u7EED\u8DD1" : "\u91CD\u8DD1\u6B64\u6B65"), !rerun.supported && !item.ok && item.ran && /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, rerun.reason), rerun.supported && rerun.reassign && !(rerun.writes && item.ok) && allowReassign && others.length > 0 && !busy && /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-inline" }, /* @__PURE__ */ import_react2.default.createElement("select", { className: "mr-input mr-mini-select", "aria-label": "\u6539\u6D3E\u5230", value: target, onChange: (event) => setTarget(event.target.value) }, /* @__PURE__ */ import_react2.default.createElement("option", { value: "" }, "\u6539\u6D3E\u5230\u2026"), others.map((route) => /* @__PURE__ */ import_react2.default.createElement("option", { key: `${route.provider}/${route.model}`, value: `${route.provider}\0${route.model}` }, route.provider, "/", route.model))), /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary mr-mini", type: "button", disabled: !target, onClick: () => {
3965
+ return /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-route" }, item.provider, "/", item.model, item.reassigned ? "\uFF08\u5DF2\u6539\u6D3E\uFF09" : ""), item.manualAssignment && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption" }, "\u624B\u52A8\u5206\u914D \xB7 ", item.manualAssignment.kind === "tool" ? `\u5B98\u65B9\u5DE5\u5177 ${item.manualAssignment.toolId}\uFF08\u5382\u5546\u9ED8\u8BA4\u6A21\u578B\uFF09` : `\u6A21\u578B API ${item.manualAssignment.provider}/${item.manualAssignment.model}`, item.algorithmRecommendation?.provider ? ` \xB7 \u539F\u63A8\u8350 ${item.algorithmRecommendation.provider}/${item.algorithmRecommendation.model}` : ""), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-channel-line" }, /* @__PURE__ */ import_react2.default.createElement(ChannelText, { item }), /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, "\u9884\u4F30 ", money(item.estimatedCost), " \xB7 \u5B9E\u9645 ", cost.budget, item.difficulty ? ` \xB7 \u96BE\u5EA6 ${BAND[item.difficulty] ?? item.difficulty}` : "")), cost.reference && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, cost.reference), item.actualModel && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, "CLI \u56DE\u62A5\u6A21\u578B\uFF1A", item.actualModel), (item.billing || item.billingMode) && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, "\u8BA1\u8D39\uFF1A", BILLING_CHANNEL_LABEL[item.billing] ?? "\u2014", item.subscriptionRoute ? ` \xB7 \u5957\u9910\u8DEF\u7EBF ${item.subscriptionRoute.provider}/${item.subscriptionRoute.model}` : "", item.billingMode && item.billingMode !== "subscription-first" ? ` \xB7 ${BILLING_MODE_LABEL[item.billingMode] ?? item.billingMode}` : ""), billingSwitchText(item) && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy mr-warn-text" }, "\u8BA1\u8D39\u5207\u6362\uFF1A", billingSwitchText(item)), item.fallback?.reason && item.fallback.reason !== billingSwitchText(item) && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, "\u56DE\u9000\u539F\u56E0\uFF1A", item.fallback.reason), item.fallback?.error && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy mr-fallback-error" }, "CLI \u539F\u59CB\u9519\u8BEF\uFF1A", /* @__PURE__ */ import_react2.default.createElement("code", null, item.fallback.error)), !item.ok && item.error && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy mr-fallback-error" }, item.error), !item.ok && item.diagnostic && /* @__PURE__ */ import_react2.default.createElement("details", { className: "mr-tool-log" }, /* @__PURE__ */ import_react2.default.createElement("summary", null, "CLI \u8BCA\u65AD", Number.isInteger(item.exitCode) ? `\uFF08\u9000\u51FA\u7801 ${item.exitCode}\uFF09` : ""), /* @__PURE__ */ import_react2.default.createElement("pre", null, item.diagnostic)), item.review && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, "\u5F3A\u6A21\u578B\u62BD\u67E5\uFF08", item.review.provider, "/", item.review.model, "\uFF09\uFF1A", item.review.score ? `${item.review.score}/5` : "\u672A\u8BC4\u5206", " ", item.review.summary), item.answer && /* @__PURE__ */ import_react2.default.createElement("details", { className: "mr-tool-log" }, /* @__PURE__ */ import_react2.default.createElement("summary", null, "\u67E5\u770B\u7ED3\u679C"), /* @__PURE__ */ import_react2.default.createElement("pre", null, item.answer, item.answerTruncated ? "\n\u2026\uFF08\u5DF2\u622A\u65AD\uFF09" : "")), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-node-actions" }, item.ok && /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary mr-mini", type: "button", title: item.rating === 1 ? "\u518D\u6B21\u70B9\u51FB\u64A4\u56DE\u6709\u7528\u8BC4\u4EF7" : "\u8BC4\u4EF7\u6B64\u7ED3\u679C\u6709\u7528", "aria-pressed": item.rating === 1, disabled: busy, onClick: () => onRate(...feedbackRatingArguments(run, item, "up")) }, "\u{1F44D} \u6709\u7528"), /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary mr-mini", type: "button", title: item.rating === -1 ? "\u518D\u6B21\u70B9\u51FB\u64A4\u56DE\u4E0D\u597D\u8BC4\u4EF7" : "\u8BC4\u4EF7\u6B64\u7ED3\u679C\u4E0D\u597D", "aria-pressed": item.rating === -1, disabled: busy, onClick: () => onRate(...feedbackRatingArguments(run, item, "down")) }, "\u{1F44E} \u4E0D\u597D")), canRerun && /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-mini", type: "button", title: rerun.confirm || void 0, onClick: () => onRerun(run.id, item.id, null) }, run.kind === "tool" ? "\u91CD\u65B0\u6267\u884C\u6B64\u8C03\u7528" : rerun.writes ? "\u5728\u65B0\u5DE5\u4F5C\u533A\u7EED\u8DD1" : "\u91CD\u8DD1\u6B64\u6B65"), !rerun.supported && !item.ok && item.ran && /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, rerun.reason), rerun.supported && rerun.reassign && !(rerun.writes && item.ok) && allowReassign && others.length > 0 && !busy && /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-inline" }, /* @__PURE__ */ import_react2.default.createElement("select", { className: "mr-input mr-mini-select", "aria-label": "\u6539\u6D3E\u5230", value: target, onChange: (event) => setTarget(event.target.value) }, /* @__PURE__ */ import_react2.default.createElement("option", { value: "" }, "\u6539\u6D3E\u5230\u2026"), others.map((route) => /* @__PURE__ */ import_react2.default.createElement("option", { key: `${route.provider}/${route.model}`, value: `${route.provider}\0${route.model}` }, route.provider, "/", route.model))), /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary mr-mini", type: "button", disabled: !target, onClick: () => {
3269
3966
  const [provider, model] = target.split("\0");
3270
3967
  onRerun(run.id, item.id, { provider, model });
3271
3968
  } }, "\u6539\u6D3E\u5E76\u91CD\u8DD1"))));
@@ -3320,18 +4017,23 @@ var STATUS_TEXT = Object.freeze({
3320
4017
  "paused-budget": "\u5DF2\u56E0\u9884\u7B97\u6682\u505C\uFF0C\u672A\u542F\u52A8\u6A21\u578B",
3321
4018
  "paused-subscription-failure": "\u6709\u6B65\u9AA4\u56E0\u8BA2\u9605\u8C03\u7528\u5931\u8D25\u800C\u6682\u505C\uFF0C\u8BF7\u5728\u4E0B\u65B9\u6267\u884C\u8BB0\u5F55\u4E2D\u9009\u62E9\u5982\u4F55\u7EE7\u7EED"
3322
4019
  });
3323
- function RunLauncher({ task, mode, directRoute, budgetUsd, defaultPreset, ledger, previewRun, startRun, onStarted, disabledReason, planningRevision }) {
4020
+ function RunLauncher({ task, mode, directRoute, budgetUsd, defaultPreset, ledger, previewRun, startRun, onStarted, disabledReason, planningRevision, assignmentMode = "algorithm", packageAssignments = {}, packagePlanKey: packagePlanKey2 }) {
3324
4021
  const [preset, setPreset] = import_react3.default.useState(defaultPreset || "balanced");
3325
4022
  const [executor, setExecutor] = import_react3.default.useState("routed");
3326
4023
  const [executionMode, setExecutionMode] = import_react3.default.useState("read-only");
3327
4024
  const [executionEnvironment, setExecutionEnvironment] = import_react3.default.useState("harness");
4025
+ const manual = assignmentMode === "manual" && mode !== "direct";
4026
+ const runExecutor = manual ? "routed" : executor;
4027
+ const runMode = manual ? "read-only" : executionMode;
4028
+ const runEnvironment = manual ? "harness" : executionEnvironment;
4029
+ const runPreset = manual ? defaultPreset || "balanced" : preset;
3328
4030
  const lastWorkspace = [...ledger?.runs ?? []].map((run) => run.workspace).find(Boolean) ?? "";
3329
4031
  const [workspace, setWorkspace] = import_react3.default.useState("");
3330
4032
  const [phase, setPhase] = import_react3.default.useState({ kind: "idle" });
3331
4033
  const mounted = import_react3.default.useRef(true);
3332
4034
  const confirmRef = import_react3.default.useRef(null);
3333
4035
  const pendingRequest = import_react3.default.useRef(0);
3334
- const inputSignature = JSON.stringify([task, mode, directRoute?.provider, directRoute?.model, budgetUsd, preset, workspace, lastWorkspace, planningRevision, disabledReason, executor, executionMode, executionEnvironment]);
4036
+ const inputSignature = JSON.stringify([task, mode, directRoute?.provider, directRoute?.model, budgetUsd, runPreset, workspace, lastWorkspace, planningRevision, disabledReason, runExecutor, runMode, runEnvironment, assignmentMode, packagePlanKey2, packageAssignments]);
3335
4037
  const latestSignature = import_react3.default.useRef(inputSignature);
3336
4038
  latestSignature.current = inputSignature;
3337
4039
  import_react3.default.useEffect(() => {
@@ -3345,12 +4047,12 @@ function RunLauncher({ task, mode, directRoute, budgetUsd, defaultPreset, ledger
3345
4047
  setPreset(defaultPreset || "balanced");
3346
4048
  }, [defaultPreset]);
3347
4049
  import_react3.default.useEffect(() => {
3348
- if (mode !== "team") {
4050
+ if (mode !== "team" || manual) {
3349
4051
  setExecutor("routed");
3350
4052
  setExecutionMode("read-only");
3351
4053
  setExecutionEnvironment("harness");
3352
4054
  }
3353
- }, [mode]);
4055
+ }, [mode, manual]);
3354
4056
  import_react3.default.useEffect(() => {
3355
4057
  pendingRequest.current += 1;
3356
4058
  setPhase((previous) => ["previewing", "preview", "error"].includes(previous.kind) ? { kind: "idle" } : previous);
@@ -3358,16 +4060,35 @@ function RunLauncher({ task, mode, directRoute, budgetUsd, defaultPreset, ledger
3358
4060
  import_react3.default.useEffect(() => {
3359
4061
  if (phase.kind === "preview") confirmRef.current?.focus();
3360
4062
  }, [phase.kind]);
3361
- const request = () => launchRequest({ task, mode, directRoute, budgetUsd, preset, workspace: text2(workspace) || lastWorkspace, executor, executionMode, executionEnvironment });
4063
+ const request = () => launchRequest({
4064
+ task,
4065
+ mode,
4066
+ directRoute,
4067
+ budgetUsd,
4068
+ preset: runPreset,
4069
+ workspace: text2(workspace) || lastWorkspace,
4070
+ executor: runExecutor,
4071
+ executionMode: runMode,
4072
+ executionEnvironment: runEnvironment,
4073
+ assignmentMode: manual ? "manual" : "algorithm",
4074
+ packageAssignments,
4075
+ packagePlanKey: packagePlanKey2
4076
+ });
3362
4077
  const unavailable = typeof previewRun !== "function" || typeof startRun !== "function";
3363
- const blocked = disabledReason || (unavailable ? "\u5DE5\u4F5C\u53F0\u6267\u884C\u670D\u52A1\u5C1A\u672A\u52A0\u8F7D\uFF0C\u8BF7\u66F4\u65B0\u63D2\u4EF6\u540E\u91CD\u8BD5\u3002" : !text2(task) ? "\u5148\u5728\u4E0A\u65B9\u201C\u4EFB\u52A1\u89C4\u5212\u201D\u4E2D\u63CF\u8FF0\u4EFB\u52A1\u3002" : executor === "official-team" && mode !== "team" ? "\u5B98\u65B9\u5DE5\u5177\u534F\u4F5C\u9700\u8981\u5148\u5728\u4E0A\u65B9\u9009\u62E9\u201C\u56E2\u961F\u5206\u5DE5\u201D\u3002" : "");
4078
+ const blocked = disabledReason || (unavailable ? "\u5DE5\u4F5C\u53F0\u6267\u884C\u670D\u52A1\u5C1A\u672A\u52A0\u8F7D\uFF0C\u8BF7\u66F4\u65B0\u63D2\u4EF6\u540E\u91CD\u8BD5\u3002" : !text2(task) ? "\u5148\u5728\u4E0A\u65B9\u201C\u4EFB\u52A1\u89C4\u5212\u201D\u4E2D\u63CF\u8FF0\u4EFB\u52A1\u3002" : manual && !packagePlanKey2 ? "\u8BF7\u91CD\u65B0\u751F\u6210\u5DE5\u4F5C\u5305\u540E\u518D\u624B\u52A8\u5206\u914D\u3002" : runExecutor === "official-team" && mode !== "team" ? "\u5B98\u65B9\u5DE5\u5177\u534F\u4F5C\u9700\u8981\u5148\u5728\u4E0A\u65B9\u9009\u62E9\u201C\u56E2\u961F\u5206\u5DE5\u201D\u3002" : "");
3364
4079
  const busy = phase.kind === "previewing" || phase.kind === "running";
3365
4080
  const recordedRun = phase.kind === "done" && phase.value.runId ? (ledger?.runs ?? []).find((run) => run.id === phase.value.runId) : null;
3366
4081
  const resultStatus = recordedRun?.status ?? phase.value?.status;
3367
4082
  const preview = async () => {
3368
4083
  if (blocked || busy) return;
3369
4084
  const current = ++pendingRequest.current;
3370
- const snapshot = request();
4085
+ let snapshot;
4086
+ try {
4087
+ snapshot = request();
4088
+ } catch (error) {
4089
+ setPhase({ kind: "error", message: text2(error?.message) || "\u5206\u914D\u8BBE\u7F6E\u65E0\u6548\u3002" });
4090
+ return;
4091
+ }
3371
4092
  const signature2 = inputSignature;
3372
4093
  setPhase({ kind: "previewing" });
3373
4094
  try {
@@ -3396,16 +4117,16 @@ function RunLauncher({ task, mode, directRoute, budgetUsd, defaultPreset, ledger
3396
4117
  if (mounted.current) setPhase({ kind: "error", message: text2(error?.message) || "\u6267\u884C\u5931\u8D25\u3002" });
3397
4118
  }
3398
4119
  };
3399
- return /* @__PURE__ */ import_react3.default.createElement("section", { className: "mr-card", "aria-label": "\u5728\u5DE5\u4F5C\u53F0\u6267\u884C" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-card-head" }, /* @__PURE__ */ import_react3.default.createElement("div", null, /* @__PURE__ */ import_react3.default.createElement("h2", { className: "mr-card-title" }, "\u5728\u5DE5\u4F5C\u53F0\u6267\u884C"), /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-card-copy" }, "\u4F7F\u7528\u4E0A\u65B9\u7684\u4EFB\u52A1\u63CF\u8FF0\u4E0E\u89C4\u5212\u6A21\u5F0F\uFF0C\u5148\u9884\u89C8\u6267\u884C\u8BA1\u5212\u3001\u6743\u9650\u4E0E\u9884\u4F30\u8D39\u7528\uFF0C\u786E\u8BA4\u540E\u624D\u4F1A\u542F\u52A8\u6267\u884C\u3002\u6A21\u578B\u8DEF\u7531\u6267\u884C\u53EA\u8BFB\u4EFB\u52A1\uFF1B\u5B98\u65B9\u5DE5\u5177\u534F\u4F5C\u53EF\u9009\u62E9\u53EA\u8BFB\u68C0\u67E5\uFF0C\u6216\u5728\u9694\u79BB\u5DE5\u4F5C\u6811\u4E2D\u4F7F\u7528 CLI \u7684\u6B63\u5E38\u5DE5\u5177\u7F16\u8F91\u6587\u4EF6\u3002"))), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-card-body" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-controls" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-control-group" }, /* @__PURE__ */ import_react3.default.createElement("span", { className: "mr-control-label", id: "mr-launch-executor" }, "\u6267\u884C\u65B9\u5F0F"), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-segment", role: "group", "aria-labelledby": "mr-launch-executor" }, /* @__PURE__ */ import_react3.default.createElement("button", { type: "button", "aria-pressed": executor === "routed", disabled: busy, onClick: () => {
4120
+ return /* @__PURE__ */ import_react3.default.createElement("section", { className: "mr-card", "aria-label": "\u5728\u5DE5\u4F5C\u53F0\u6267\u884C" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-card-head" }, /* @__PURE__ */ import_react3.default.createElement("div", null, /* @__PURE__ */ import_react3.default.createElement("h2", { className: "mr-card-title" }, "\u5728\u5DE5\u4F5C\u53F0\u6267\u884C"), /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-card-copy" }, "\u4F7F\u7528\u4E0A\u65B9\u7684\u4EFB\u52A1\u63CF\u8FF0\u4E0E\u89C4\u5212\u6A21\u5F0F\uFF0C\u5148\u9884\u89C8\u6267\u884C\u8BA1\u5212\u3001\u6743\u9650\u4E0E\u9884\u4F30\u8D39\u7528\uFF0C\u786E\u8BA4\u540E\u624D\u4F1A\u542F\u52A8\u6267\u884C\u3002\u6A21\u578B\u8DEF\u7531\u6267\u884C\u53EA\u8BFB\u4EFB\u52A1\uFF1B\u5B98\u65B9\u5DE5\u5177\u534F\u4F5C\u53EF\u9009\u62E9\u53EA\u8BFB\u68C0\u67E5\uFF0C\u6216\u5728\u9694\u79BB\u5DE5\u4F5C\u6811\u4E2D\u4F7F\u7528 CLI \u7684\u6B63\u5E38\u5DE5\u5177\u7F16\u8F91\u6587\u4EF6\u3002"))), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-card-body" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-controls" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-control-group" }, /* @__PURE__ */ import_react3.default.createElement("span", { className: "mr-control-label", id: "mr-launch-executor" }, "\u6267\u884C\u65B9\u5F0F"), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-segment", role: "group", "aria-labelledby": "mr-launch-executor" }, /* @__PURE__ */ import_react3.default.createElement("button", { type: "button", "aria-pressed": runExecutor === "routed", disabled: busy, onClick: () => {
3400
4121
  setExecutor("routed");
3401
4122
  setExecutionMode("read-only");
3402
4123
  setExecutionEnvironment("harness");
3403
- } }, "\u6A21\u578B\u8DEF\u7531\uFF08\u53EA\u8BFB\uFF09"), /* @__PURE__ */ import_react3.default.createElement("button", { type: "button", "aria-pressed": executor === "official-team", disabled: busy || mode !== "team", title: mode !== "team" ? "\u5148\u5728\u4E0A\u65B9\u9009\u62E9\u201C\u56E2\u961F\u5206\u5DE5\u201D" : "\u7531\u5B98\u65B9 CLI \u534F\u4F5C\u6267\u884C\u5DE5\u4F5C\u5305", onClick: () => {
3404
- if (mode === "team") setExecutor("official-team");
3405
- } }, "\u5B98\u65B9\u5DE5\u5177\u534F\u4F5C"))), executor === "official-team" && /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-control-group" }, /* @__PURE__ */ import_react3.default.createElement("span", { className: "mr-control-label", id: "mr-launch-permission" }, "\u4EFB\u52A1\u6743\u9650"), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-segment", role: "group", "aria-labelledby": "mr-launch-permission" }, /* @__PURE__ */ import_react3.default.createElement("button", { type: "button", "aria-pressed": executionMode === "read-only", disabled: busy, onClick: () => {
4124
+ } }, manual ? "\u624B\u52A8\u5206\u914D\uFF08\u53EA\u8BFB\uFF09" : "\u6A21\u578B\u8DEF\u7531\uFF08\u53EA\u8BFB\uFF09"), /* @__PURE__ */ import_react3.default.createElement("button", { type: "button", "aria-pressed": runExecutor === "official-team", disabled: busy || mode !== "team" || manual, title: manual ? "\u624B\u52A8\u5206\u914D\u6309\u5305\u6267\u884C\u53EA\u8BFB\u4EFB\u52A1" : mode !== "team" ? "\u5148\u5728\u4E0A\u65B9\u9009\u62E9\u201C\u56E2\u961F\u5206\u5DE5\u201D" : "\u7531\u5B98\u65B9 CLI \u534F\u4F5C\u6267\u884C\u5DE5\u4F5C\u5305", onClick: () => {
4125
+ if (mode === "team" && !manual) setExecutor("official-team");
4126
+ } }, "\u5B98\u65B9\u5DE5\u5177\u534F\u4F5C"))), runExecutor === "official-team" && /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-control-group" }, /* @__PURE__ */ import_react3.default.createElement("span", { className: "mr-control-label", id: "mr-launch-permission" }, "\u4EFB\u52A1\u6743\u9650"), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-segment", role: "group", "aria-labelledby": "mr-launch-permission" }, /* @__PURE__ */ import_react3.default.createElement("button", { type: "button", "aria-pressed": executionMode === "read-only", disabled: busy, onClick: () => {
3406
4127
  setExecutionMode("read-only");
3407
4128
  setExecutionEnvironment("harness");
3408
- } }, "\u53EA\u8BFB\u68C0\u67E5"), /* @__PURE__ */ import_react3.default.createElement("button", { type: "button", "aria-pressed": executionMode === "workspace-write", disabled: busy, onClick: () => setExecutionMode("workspace-write") }, "\u7F16\u8F91\u6587\u4EF6"))), executor === "official-team" && executionMode === "workspace-write" && /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-control-group" }, /* @__PURE__ */ import_react3.default.createElement("span", { className: "mr-control-label", id: "mr-launch-environment" }, "\u6267\u884C\u73AF\u5883"), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-segment", role: "group", "aria-labelledby": "mr-launch-environment" }, /* @__PURE__ */ import_react3.default.createElement("button", { type: "button", "aria-pressed": executionEnvironment === "harness", disabled: busy, onClick: () => setExecutionEnvironment("harness") }, "Harness \u53D7\u9650\u73AF\u5883"), /* @__PURE__ */ import_react3.default.createElement("button", { type: "button", "aria-pressed": executionEnvironment === "cli-native", disabled: busy, onClick: () => setExecutionEnvironment("cli-native") }, "\u5382\u5546 CLI \u6B63\u5E38\u73AF\u5883"))), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-control-group" }, /* @__PURE__ */ import_react3.default.createElement("span", { className: "mr-control-label", id: "mr-launch-preset" }, "\u8DEF\u7531\u65B9\u6848"), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-segment", role: "group", "aria-labelledby": "mr-launch-preset" }, Object.values(ROUTING_PRESETS).map((item) => /* @__PURE__ */ import_react3.default.createElement("button", { key: item.id, type: "button", title: item.description, "aria-pressed": preset === item.id, disabled: busy || mode === "direct", onClick: () => setPreset(item.id) }, item.label)))), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-control-group mr-launch-workspace" }, /* @__PURE__ */ import_react3.default.createElement("label", { className: "mr-control-label", htmlFor: "mr-launch-workspace" }, "\u5DE5\u4F5C\u533A\uFF08\u7EDD\u5BF9\u8DEF\u5F84\uFF09"), /* @__PURE__ */ import_react3.default.createElement("input", { className: "mr-input", id: "mr-launch-workspace", value: workspace, disabled: busy, placeholder: lastWorkspace ? `\u7559\u7A7A\u6CBF\u7528\u4E0A\u6B21\u8FD0\u884C\uFF1A${lastWorkspace}` : "\u4F8B\u5982 D:\\projects\\demo \u6216 /home/me/demo", onChange: (event) => setWorkspace(event.target.value) }))), /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-caption" }, executor === "official-team" ? `${executionMode === "workspace-write" ? "\u7F16\u8F91\u6587\u4EF6\uFF1A\u5404\u5DE5\u4F5C\u5305\u5728\u72EC\u7ACB Git \u5DE5\u4F5C\u6811\u4E2D\u4F7F\u7528 CLI \u6B63\u5E38\u5DE5\u5177\uFF0C\u5B8C\u6210\u540E\u6574\u5408\u56DE\u6240\u9009\u5DE5\u4F5C\u533A\u3002" : "\u53EA\u8BFB\u68C0\u67E5\uFF1A\u5404\u5DE5\u4F5C\u5305\u4F7F\u7528\u53D7\u9650\u5DE5\u5177\u96C6\uFF0C\u4E0D\u4FEE\u6539\u5DE5\u4F5C\u533A\u6587\u4EF6\uFF1BClaude \u7981\u7528 shell\u3001\u6280\u80FD\u53CA\u65E2\u6709 MCP\uFF0C\u5E76\u5FFD\u7565\u7528\u6237\u8BBE\u7F6E\u3002"}\u5931\u8D25\u65F6\u4E0D\u4F1A\u81EA\u52A8\u66FF\u6362\u4E3A\u6A21\u578B\u76EE\u5F55 API\u3002\u5DF2\u767B\u5F55\u4E0D\u4EE3\u8868\u5F53\u524D\u4EFB\u52A1\u6743\u9650\u5DF2\u5C31\u7EEA\uFF0C\u9884\u89C8\u4F1A\u68C0\u67E5\u53EF\u7528\u5DE5\u5177\u3002` : mode === "direct" ? `\u6307\u5B9A\u6A21\u578B\uFF1A${directRoute ? `${directRoute.provider}/${directRoute.model}` : "\u672A\u9009\u62E9"}\uFF08\u8DF3\u8FC7\u8DEF\u7531\uFF0C\u65B9\u6848\u4E0D\u751F\u6548\uFF09` : mode === "team" ? "\u56E2\u961F\u5206\u5DE5\uFF1A\u6309\u4F9D\u8D56\u6267\u884C\u53EA\u8BFB\u5DE5\u4F5C\u5305\uFF0C\u6BCF\u6B65\u4F7F\u7528\u53EF\u7528\u7684\u5B98\u65B9 CLI \u6216\u6A21\u578B\u76EE\u5F55 API\u3002" : "\u5355\u4EFB\u52A1\uFF1A\u6309\u65B9\u6848\u9009\u62E9\u4E00\u6761\u8DEF\u7EBF\u3002"), mode !== "team" && /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-caption" }, "\u4F7F\u7528\u5B98\u65B9\u5DE5\u5177\u534F\u4F5C\u524D\uFF0C\u5148\u5728\u4E0A\u65B9\u9009\u62E9\u201C\u56E2\u961F\u5206\u5DE5\u201D\u3002"), executionEnvironment === "cli-native" && /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-caption" }, "\u5382\u5546 CLI \u4F7F\u7528\u539F\u767B\u5F55\u3001\u914D\u7F6E\u3001\u6280\u80FD\u53CA MCP\uFF0C\u4EE5\u5F53\u524D\u7528\u6237\u6743\u9650\u8FD0\u884C\uFF0C\u4E0D\u7ECF\u8FC7 Harness \u8FDB\u7A0B\u6C99\u7BB1\u3002\u72EC\u7ACB Git \u5DE5\u4F5C\u6811\u7528\u4E8E\u6536\u96C6\u6539\u52A8\uFF0C\u4E0D\u662F\u64CD\u4F5C\u7CFB\u7EDF\u8BFB\u5199\u8FB9\u754C\uFF1B\u4E0D\u4F1A\u81EA\u52A8\u5207\u6362\u5230\u6B64\u73AF\u5883\u3002"), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-actions" }, /* @__PURE__ */ import_react3.default.createElement("button", { className: "mr-button", type: "button", disabled: Boolean(blocked) || busy, onClick: () => {
4129
+ } }, "\u53EA\u8BFB\u68C0\u67E5"), /* @__PURE__ */ import_react3.default.createElement("button", { type: "button", "aria-pressed": executionMode === "workspace-write", disabled: busy, onClick: () => setExecutionMode("workspace-write") }, "\u7F16\u8F91\u6587\u4EF6"))), runExecutor === "official-team" && executionMode === "workspace-write" && /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-control-group" }, /* @__PURE__ */ import_react3.default.createElement("span", { className: "mr-control-label", id: "mr-launch-environment" }, "\u6267\u884C\u73AF\u5883"), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-segment", role: "group", "aria-labelledby": "mr-launch-environment" }, /* @__PURE__ */ import_react3.default.createElement("button", { type: "button", "aria-pressed": executionEnvironment === "harness", disabled: busy, onClick: () => setExecutionEnvironment("harness") }, "Harness \u53D7\u9650\u73AF\u5883"), /* @__PURE__ */ import_react3.default.createElement("button", { type: "button", "aria-pressed": executionEnvironment === "cli-native", disabled: busy, onClick: () => setExecutionEnvironment("cli-native") }, "\u5382\u5546 CLI \u6B63\u5E38\u73AF\u5883"))), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-control-group" }, /* @__PURE__ */ import_react3.default.createElement("span", { className: "mr-control-label", id: "mr-launch-preset" }, "\u8DEF\u7531\u65B9\u6848"), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-segment", role: "group", "aria-labelledby": "mr-launch-preset" }, Object.values(ROUTING_PRESETS).map((item) => /* @__PURE__ */ import_react3.default.createElement("button", { key: item.id, type: "button", title: manual ? "\u624B\u52A8\u5206\u914D\u6CBF\u7528\u5F53\u524D\u5EFA\u8BAE\u7684\u65B9\u6848\uFF1B\u66F4\u6539\u65B9\u6848\u540E\u8BF7\u91CD\u65B0\u751F\u6210\u5EFA\u8BAE\u3002" : item.description, "aria-pressed": runPreset === item.id, disabled: busy || mode === "direct" || manual, onClick: () => setPreset(item.id) }, item.label)))), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-control-group mr-launch-workspace" }, /* @__PURE__ */ import_react3.default.createElement("label", { className: "mr-control-label", htmlFor: "mr-launch-workspace" }, "\u5DE5\u4F5C\u533A\uFF08\u7EDD\u5BF9\u8DEF\u5F84\uFF09"), /* @__PURE__ */ import_react3.default.createElement("input", { className: "mr-input", id: "mr-launch-workspace", value: workspace, disabled: busy, placeholder: lastWorkspace ? `\u7559\u7A7A\u6CBF\u7528\u4E0A\u6B21\u8FD0\u884C\uFF1A${lastWorkspace}` : "\u4F8B\u5982 D:\\projects\\demo \u6216 /home/me/demo", onChange: (event) => setWorkspace(event.target.value) }))), /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-caption" }, manual ? "\u624B\u52A8\u5206\u914D\u6309\u5305\u6267\u884C\u53EA\u8BFB\u4EFB\u52A1\uFF0C\u53EF\u6DF7\u5408\u6A21\u578B API \u4E0E\u5B98\u65B9\u5DE5\u5177\uFF1B\u5DE5\u5177\u4F7F\u7528\u9ED8\u8BA4\u6A21\u578B\u3002\u6240\u9009\u76EE\u6807\u3001\u5C31\u7EEA\u72B6\u6001\u548C\u8D39\u7528\u4EE5\u6267\u884C\u9884\u89C8\u4E3A\u51C6\u3002" : executor === "official-team" ? `${executionMode === "workspace-write" ? "\u7F16\u8F91\u6587\u4EF6\uFF1A\u5404\u5DE5\u4F5C\u5305\u5728\u72EC\u7ACB Git \u5DE5\u4F5C\u6811\u4E2D\u4F7F\u7528 CLI \u6B63\u5E38\u5DE5\u5177\uFF0C\u5B8C\u6210\u540E\u6574\u5408\u56DE\u6240\u9009\u5DE5\u4F5C\u533A\u3002" : "\u53EA\u8BFB\u68C0\u67E5\uFF1A\u5404\u5DE5\u4F5C\u5305\u4F7F\u7528\u53D7\u9650\u5DE5\u5177\u96C6\uFF0C\u4E0D\u4FEE\u6539\u5DE5\u4F5C\u533A\u6587\u4EF6\uFF1BClaude \u7981\u7528 shell\u3001\u6280\u80FD\u53CA\u65E2\u6709 MCP\uFF0C\u5E76\u5FFD\u7565\u7528\u6237\u8BBE\u7F6E\u3002"}\u5931\u8D25\u65F6\u4E0D\u4F1A\u81EA\u52A8\u66FF\u6362\u4E3A\u6A21\u578B\u76EE\u5F55 API\u3002\u5DF2\u767B\u5F55\u4E0D\u4EE3\u8868\u5F53\u524D\u4EFB\u52A1\u6743\u9650\u5DF2\u5C31\u7EEA\uFF0C\u9884\u89C8\u4F1A\u68C0\u67E5\u53EF\u7528\u5DE5\u5177\u3002` : mode === "direct" ? `\u6307\u5B9A\u6A21\u578B\uFF1A${directRoute ? `${directRoute.provider}/${directRoute.model}` : "\u672A\u9009\u62E9"}\uFF08\u8DF3\u8FC7\u8DEF\u7531\uFF0C\u65B9\u6848\u4E0D\u751F\u6548\uFF09` : mode === "team" ? "\u56E2\u961F\u5206\u5DE5\uFF1A\u6309\u4F9D\u8D56\u6267\u884C\u53EA\u8BFB\u5DE5\u4F5C\u5305\uFF0C\u6BCF\u6B65\u4F7F\u7528\u53EF\u7528\u7684\u5B98\u65B9 CLI \u6216\u6A21\u578B\u76EE\u5F55 API\u3002" : "\u5355\u4EFB\u52A1\uFF1A\u6309\u65B9\u6848\u9009\u62E9\u4E00\u6761\u8DEF\u7EBF\u3002"), mode !== "team" && !manual && /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-caption" }, "\u4F7F\u7528\u5B98\u65B9\u5DE5\u5177\u534F\u4F5C\u524D\uFF0C\u5148\u5728\u4E0A\u65B9\u9009\u62E9\u201C\u56E2\u961F\u5206\u5DE5\u201D\u3002"), runEnvironment === "cli-native" && /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-caption" }, "\u5382\u5546 CLI \u4F7F\u7528\u539F\u767B\u5F55\u3001\u914D\u7F6E\u3001\u6280\u80FD\u53CA MCP\uFF0C\u4EE5\u5F53\u524D\u7528\u6237\u6743\u9650\u8FD0\u884C\uFF0C\u4E0D\u7ECF\u8FC7 Harness \u8FDB\u7A0B\u6C99\u7BB1\u3002\u72EC\u7ACB Git \u5DE5\u4F5C\u6811\u7528\u4E8E\u6536\u96C6\u6539\u52A8\uFF0C\u4E0D\u662F\u64CD\u4F5C\u7CFB\u7EDF\u8BFB\u5199\u8FB9\u754C\uFF1B\u4E0D\u4F1A\u81EA\u52A8\u5207\u6362\u5230\u6B64\u73AF\u5883\u3002"), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-actions" }, /* @__PURE__ */ import_react3.default.createElement("button", { className: "mr-button", type: "button", disabled: Boolean(blocked) || busy, onClick: () => {
3409
4130
  void preview();
3410
4131
  } }, phase.kind === "previewing" ? "\u6B63\u5728\u751F\u6210\u9884\u89C8\u2026" : "\u9884\u89C8\u6267\u884C\u8BA1\u5212"), blocked && /* @__PURE__ */ import_react3.default.createElement("span", { className: "mr-caption" }, blocked)), phase.kind === "previewing" && /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-empty", role: "status" }, "\u6B63\u5728\u6309\u5F53\u524D\u6A21\u578B\u76EE\u5F55\u3001\u767B\u5F55\u72B6\u6001\u548C\u9884\u7B97\u751F\u6210\u6267\u884C\u8BA1\u5212\u2026"), phase.kind === "error" && /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-error", role: "alert" }, phase.message), (phase.kind === "preview" && phase.signature === inputSignature || phase.kind === "running") && /* @__PURE__ */ import_react3.default.createElement(
3411
4132
  LaunchPreview,
@@ -3430,8 +4151,12 @@ function LaunchPreview({ value, request, changed, running, confirmRef, onConfirm
3430
4151
  const native = (value.executionEnvironment ?? request?.executionEnvironment) === "cli-native";
3431
4152
  const readOnly = value.readOnly !== false && (value.executionMode ?? request?.executionMode) !== "workspace-write";
3432
4153
  const blocked = value.status === "blocked";
4154
+ const manual = request?.assignmentMode === "manual" || value.assignmentMode === "manual";
3433
4155
  const blocking = [...new Set([value.reason, ...Array.isArray(value.blocking) ? value.blocking : [value.blocking]].map(reasonText).filter(Boolean))];
3434
- return /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-launch-preview", role: "region", "aria-label": "\u6267\u884C\u9884\u89C8" }, changed && /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-error", role: "alert" }, "\u786E\u8BA4\u671F\u95F4\u60C5\u51B5\u6709\u53D8\u5316\uFF08\u4F8B\u5982\u9884\u7B97\u6216\u767B\u5F55\u72B6\u6001\uFF09\uFF0C\u8BF7\u91CD\u65B0\u6838\u5BF9\u4E0B\u9762\u7684\u786E\u8BA4\u9879\u3002"), blocked && /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-error", role: "alert" }, /* @__PURE__ */ import_react3.default.createElement("strong", null, "\u5F53\u524D\u65E0\u6CD5\u542F\u52A8\u6267\u884C\u3002"), blocking.length ? /* @__PURE__ */ import_react3.default.createElement("ul", null, blocking.map((message) => /* @__PURE__ */ import_react3.default.createElement("li", { key: message }, message))) : /* @__PURE__ */ import_react3.default.createElement("p", null, "\u6267\u884C\u6761\u4EF6\u5C1A\u672A\u6EE1\u8DB3\uFF0C\u8BF7\u68C0\u67E5\u6240\u9009\u6743\u9650\u3001\u5DE5\u4F5C\u533A\u548C\u5B98\u65B9\u5DE5\u5177\u72B6\u6001\u540E\u91CD\u65B0\u9884\u89C8\u3002")), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-result-grid" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-metric" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-metric-label" }, officialTeam ? "\u6267\u884C\u65B9\u5F0F" : "\u8DEF\u7EBF"), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-metric-value" }, officialTeam ? "\u5B98\u65B9\u5DE5\u5177\u534F\u4F5C" : value.selected ? `${value.selected.provider}/${value.selected.model}` : "\u6682\u65E0\u8DEF\u7EBF", value.routingBypassed ? "\uFF08\u6307\u5B9A\u6A21\u578B\uFF09" : "")), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-metric" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-metric-label" }, "\u9884\u4F30\u8D39\u7528"), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-metric-value" }, value.estimatedCost === null || value.estimatedCost === void 0 ? "\u4EF7\u683C\u5F85\u914D\u7F6E" : money(value.estimatedCost))), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-metric" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-metric-label" }, "\u65B9\u6848"), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-metric-value" }, ROUTING_PRESETS[value.decision?.preset]?.label ?? "\u2014", value.budget?.downgraded ? "\uFF08\u8D85\u9884\u7B97\u81EA\u52A8\u964D\u7EA7\uFF09" : "")), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-metric" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-metric-label" }, "\u5DE5\u4F5C\u533A"), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-metric-value mr-break" }, /* @__PURE__ */ import_react3.default.createElement("code", null, value.workspace)))), officialTeam && /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-caption" }, "\u4EFB\u52A1\u6743\u9650\uFF1A", readOnly ? "\u53EA\u8BFB\u68C0\u67E5\uFF0C\u4E0D\u4FEE\u6539\u5DE5\u4F5C\u533A\u6587\u4EF6\u3002\u4F7F\u7528\u53D7\u9650\u5DE5\u5177\u96C6\uFF1BClaude \u7981\u7528 shell\u3001\u6280\u80FD\u53CA\u65E2\u6709 MCP\uFF0C\u5E76\u5FFD\u7565\u7528\u6237\u8BBE\u7F6E\u3002" : /* @__PURE__ */ import_react3.default.createElement(import_react3.default.Fragment, null, "\u7F16\u8F91\u6587\u4EF6\u3002\u7F16\u8F91\u8303\u56F4\uFF1A\u6240\u9009\u5DE5\u4F5C\u533A ", /* @__PURE__ */ import_react3.default.createElement("code", null, value.workspace), " \u5185\u7684\u4EFB\u52A1\u6587\u4EF6\u3002\u5B98\u65B9 CLI \u4F7F\u7528\u81EA\u5DF1\u7684\u6B63\u5E38\u5DE5\u5177\uFF0C\u5728\u72EC\u7ACB Git \u5DE5\u4F5C\u6811\u4E2D\u5B8C\u6210\u540E\u6574\u5408\u56DE\u8BE5\u5DE5\u4F5C\u533A\u3002"), "\u5931\u8D25\u65F6\u4E0D\u4F1A\u81EA\u52A8\u66FF\u6362\u4E3A\u6A21\u578B\u76EE\u5F55 API\u3002"), native && /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-caption" }, "\u6267\u884C\u73AF\u5883\uFF1A\u5382\u5546 CLI \u6B63\u5E38\u73AF\u5883\uFF0C\u4EE5\u5F53\u524D\u7528\u6237\u6743\u9650\u8FD0\u884C\uFF0C\u4E0D\u7ECF Harness \u8FDB\u7A0B\u6C99\u7BB1\u3002\u5DE5\u4F5C\u6811\u4E0D\u9650\u5236\u5DE5\u5177\u8BBF\u95EE\u5DE5\u4F5C\u6811\u5916\u8D44\u6E90\u3002"), !officialTeam && !readOnly && /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-caption" }, "\u7F16\u8F91\u8303\u56F4\uFF1A\u6240\u9009\u5DE5\u4F5C\u533A ", /* @__PURE__ */ import_react3.default.createElement("code", null, value.workspace), " \u5185\u7684\u4EFB\u52A1\u6587\u4EF6\uFF1B\u5728\u72EC\u7ACB Git \u5DE5\u4F5C\u6811\u4E2D\u5B8C\u6210\u540E\u6574\u5408\u56DE\u8BE5\u5DE5\u4F5C\u533A\u3002\u4E0D\u4F1A\u81EA\u52A8\u56DE\u9000\u6A21\u578B\u76EE\u5F55 API\u3002"), value.budget?.message && /* @__PURE__ */ import_react3.default.createElement("p", { className: value.budget.exceeded ? "mr-error" : "mr-caption" }, "\u9884\u7B97\u68C0\u67E5\uFF1A", value.budget.message), packages.length > 0 && /* @__PURE__ */ import_react3.default.createElement("ol", { className: "mr-launch-packages" }, packages.map((item) => /* @__PURE__ */ import_react3.default.createElement("li", { key: item.id }, /* @__PURE__ */ import_react3.default.createElement("strong", null, item.name), " \u2192 ", item.route || item.toolLabel || item.toolId || item.tool || "\u5F85\u5206\u914D\u8DEF\u7EBF", " ", /* @__PURE__ */ import_react3.default.createElement("span", { className: "mr-caption" }, "\uFF08\u9884\u4F30 ", item.estimatedCost === null || item.estimatedCost === void 0 ? "\u4EF7\u683C\u5F85\u914D\u7F6E" : money(item.estimatedCost), item.channel === "official-cli" ? " \xB7 \u5B98\u65B9 CLI" : item.channel ? " \xB7 \u6A21\u578B\u76EE\u5F55 API" : "", "\uFF09"), item.channelDetail && /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-caption" }, item.channelDetail)))), reasons.length > 0 ? /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-confirm-box" }, /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-section-title" }, "\u6267\u884C\u524D\u9700\u8981\u786E\u8BA4\u4EE5\u4E0B ", reasons.length, " \u9879\uFF1A"), /* @__PURE__ */ import_react3.default.createElement("ol", null, reasons.map((item) => /* @__PURE__ */ import_react3.default.createElement("li", { key: item.code }, reasonText(item))))) : /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-caption" }, "\u5F53\u524D\u6CA1\u6709\u989D\u5916\u786E\u8BA4\u9879\u3002", value.estimatedCost === null || value.estimatedCost === void 0 ? "\u8D39\u7528\u672A\u77E5\uFF0C\u65E0\u6CD5\u786E\u8BA4\u672C\u6B21\u8D39\u7528\u662F\u5426\u5728\u9884\u7B97\u5185\u3002" : value.budget?.limited && value.budget?.estimateKnown && !value.budget?.exceeded ? "\u6309\u5DF2\u63D0\u4F9B\u5355\u4EF7\u4F30\u7B97\uFF0C\u672C\u6B21\u672A\u8D85\u51FA\u9884\u7B97\uFF1B\u5B9E\u9645\u8D39\u7528\u4EE5\u6267\u884C\u8BB0\u5F55\u548C\u8D26\u5355\u4E3A\u51C6\u3002" : "\u5B9E\u9645\u8D39\u7528\u4EE5\u6267\u884C\u8BB0\u5F55\u548C\u8D26\u5355\u4E3A\u51C6\u3002"), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-actions" }, /* @__PURE__ */ import_react3.default.createElement("button", { ref: confirmRef, className: "mr-button", type: "button", disabled: running || blocked, onClick: onConfirm }, running ? "\u6B63\u5728\u6267\u884C\u2026" : reasons.length ? "\u5168\u90E8\u786E\u8BA4\u5E76\u6267\u884C" : "\u786E\u8BA4\u6267\u884C"), /* @__PURE__ */ import_react3.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", disabled: running, onClick: onCancel }, "\u53D6\u6D88"), running && /* @__PURE__ */ import_react3.default.createElement("span", { className: "mr-caption", role: "status", "aria-live": "polite" }, "\u6A21\u578B\u8C03\u7528\u53EF\u80FD\u9700\u8981\u51E0\u5206\u949F\uFF0C\u8BF7\u52FF\u5173\u95ED\u5DE5\u4F5C\u53F0\u3002")));
4156
+ return /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-launch-preview", role: "region", "aria-label": "\u6267\u884C\u9884\u89C8" }, changed && /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-error", role: "alert" }, "\u786E\u8BA4\u671F\u95F4\u60C5\u51B5\u6709\u53D8\u5316\uFF08\u4F8B\u5982\u9884\u7B97\u6216\u767B\u5F55\u72B6\u6001\uFF09\uFF0C\u8BF7\u91CD\u65B0\u6838\u5BF9\u4E0B\u9762\u7684\u786E\u8BA4\u9879\u3002"), blocked && /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-error", role: "alert" }, /* @__PURE__ */ import_react3.default.createElement("strong", null, "\u5F53\u524D\u65E0\u6CD5\u542F\u52A8\u6267\u884C\u3002"), blocking.length ? /* @__PURE__ */ import_react3.default.createElement("ul", null, blocking.map((message) => /* @__PURE__ */ import_react3.default.createElement("li", { key: message }, message))) : /* @__PURE__ */ import_react3.default.createElement("p", null, "\u6267\u884C\u6761\u4EF6\u5C1A\u672A\u6EE1\u8DB3\uFF0C\u8BF7\u68C0\u67E5\u6240\u9009\u6743\u9650\u3001\u5DE5\u4F5C\u533A\u548C\u5B98\u65B9\u5DE5\u5177\u72B6\u6001\u540E\u91CD\u65B0\u9884\u89C8\u3002")), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-result-grid" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-metric" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-metric-label" }, officialTeam || manual ? "\u6267\u884C\u65B9\u5F0F" : "\u8DEF\u7EBF"), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-metric-value" }, manual ? "\u624B\u52A8\u5206\u914D \xB7 \u6309\u5305\u53EA\u8BFB\u6267\u884C" : officialTeam ? "\u5B98\u65B9\u5DE5\u5177\u534F\u4F5C" : value.selected ? `${value.selected.provider}/${value.selected.model}` : "\u6682\u65E0\u8DEF\u7EBF", value.routingBypassed ? "\uFF08\u6307\u5B9A\u6A21\u578B\uFF09" : "")), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-metric" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-metric-label" }, "\u9884\u4F30\u8D39\u7528"), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-metric-value" }, value.estimatedCost === null || value.estimatedCost === void 0 ? "\u4EF7\u683C\u5F85\u914D\u7F6E" : money(value.estimatedCost))), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-metric" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-metric-label" }, "\u65B9\u6848"), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-metric-value" }, ROUTING_PRESETS[value.decision?.preset]?.label ?? "\u2014", value.budget?.downgraded ? "\uFF08\u8D85\u9884\u7B97\u81EA\u52A8\u964D\u7EA7\uFF09" : "")), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-metric" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-metric-label" }, "\u5DE5\u4F5C\u533A"), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-metric-value mr-break" }, /* @__PURE__ */ import_react3.default.createElement("code", null, value.workspace)))), officialTeam && /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-caption" }, "\u4EFB\u52A1\u6743\u9650\uFF1A", readOnly ? "\u53EA\u8BFB\u68C0\u67E5\uFF0C\u4E0D\u4FEE\u6539\u5DE5\u4F5C\u533A\u6587\u4EF6\u3002\u4F7F\u7528\u53D7\u9650\u5DE5\u5177\u96C6\uFF1BClaude \u7981\u7528 shell\u3001\u6280\u80FD\u53CA\u65E2\u6709 MCP\uFF0C\u5E76\u5FFD\u7565\u7528\u6237\u8BBE\u7F6E\u3002" : /* @__PURE__ */ import_react3.default.createElement(import_react3.default.Fragment, null, "\u7F16\u8F91\u6587\u4EF6\u3002\u7F16\u8F91\u8303\u56F4\uFF1A\u6240\u9009\u5DE5\u4F5C\u533A ", /* @__PURE__ */ import_react3.default.createElement("code", null, value.workspace), " \u5185\u7684\u4EFB\u52A1\u6587\u4EF6\u3002\u5B98\u65B9 CLI \u4F7F\u7528\u81EA\u5DF1\u7684\u6B63\u5E38\u5DE5\u5177\uFF0C\u5728\u72EC\u7ACB Git \u5DE5\u4F5C\u6811\u4E2D\u5B8C\u6210\u540E\u6574\u5408\u56DE\u8BE5\u5DE5\u4F5C\u533A\u3002"), "\u5931\u8D25\u65F6\u4E0D\u4F1A\u81EA\u52A8\u66FF\u6362\u4E3A\u6A21\u578B\u76EE\u5F55 API\u3002"), native && /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-caption" }, "\u6267\u884C\u73AF\u5883\uFF1A\u5382\u5546 CLI \u6B63\u5E38\u73AF\u5883\uFF0C\u4EE5\u5F53\u524D\u7528\u6237\u6743\u9650\u8FD0\u884C\uFF0C\u4E0D\u7ECF Harness \u8FDB\u7A0B\u6C99\u7BB1\u3002\u5DE5\u4F5C\u6811\u4E0D\u9650\u5236\u5DE5\u5177\u8BBF\u95EE\u5DE5\u4F5C\u6811\u5916\u8D44\u6E90\u3002"), !officialTeam && !readOnly && /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-caption" }, "\u7F16\u8F91\u8303\u56F4\uFF1A\u6240\u9009\u5DE5\u4F5C\u533A ", /* @__PURE__ */ import_react3.default.createElement("code", null, value.workspace), " \u5185\u7684\u4EFB\u52A1\u6587\u4EF6\uFF1B\u5728\u72EC\u7ACB Git \u5DE5\u4F5C\u6811\u4E2D\u5B8C\u6210\u540E\u6574\u5408\u56DE\u8BE5\u5DE5\u4F5C\u533A\u3002\u4E0D\u4F1A\u81EA\u52A8\u56DE\u9000\u6A21\u578B\u76EE\u5F55 API\u3002"), value.budget?.message && /* @__PURE__ */ import_react3.default.createElement("p", { className: value.budget.exceeded ? "mr-error" : "mr-caption" }, "\u9884\u7B97\u68C0\u67E5\uFF1A", value.budget.message), packages.length > 0 && /* @__PURE__ */ import_react3.default.createElement("ol", { className: "mr-launch-packages" }, packages.map((item) => {
4157
+ const tool = item.manualAssignment?.kind === "tool";
4158
+ return /* @__PURE__ */ import_react3.default.createElement("li", { key: item.id }, /* @__PURE__ */ import_react3.default.createElement("strong", null, item.name), (manual || item.selectionSource === "manual") && /* @__PURE__ */ import_react3.default.createElement("span", { className: "mr-pill" }, "\u624B\u52A8"), " \u2192 ", tool ? `${item.toolLabel || item.toolId || item.manualAssignment.toolId} \xB7 \u9ED8\u8BA4\u6A21\u578B` : item.route || item.toolLabel || item.toolId || item.tool || "\u5F85\u5206\u914D\u8DEF\u7EBF", " ", /* @__PURE__ */ import_react3.default.createElement("span", { className: "mr-caption" }, "\uFF08\u9884\u4F30 ", tool ? "\u8D39\u7528\u672A\u77E5" : item.estimatedCost === null || item.estimatedCost === void 0 ? "\u4EF7\u683C\u5F85\u914D\u7F6E" : money(item.estimatedCost), item.channel === "official-cli" ? " \xB7 \u5B98\u65B9 CLI" : item.channel ? " \xB7 \u6A21\u578B\u76EE\u5F55 API" : "", "\uFF09"), item.channelDetail && /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-caption" }, item.channelDetail));
4159
+ })), reasons.length > 0 ? /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-confirm-box" }, /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-section-title" }, "\u6267\u884C\u524D\u9700\u8981\u786E\u8BA4\u4EE5\u4E0B ", reasons.length, " \u9879\uFF1A"), /* @__PURE__ */ import_react3.default.createElement("ol", null, reasons.map((item) => /* @__PURE__ */ import_react3.default.createElement("li", { key: item.code }, reasonText(item))))) : /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-caption" }, "\u5F53\u524D\u6CA1\u6709\u989D\u5916\u786E\u8BA4\u9879\u3002", value.estimatedCost === null || value.estimatedCost === void 0 ? "\u8D39\u7528\u672A\u77E5\uFF0C\u65E0\u6CD5\u786E\u8BA4\u672C\u6B21\u8D39\u7528\u662F\u5426\u5728\u9884\u7B97\u5185\u3002" : value.budget?.limited && value.budget?.estimateKnown && !value.budget?.exceeded ? "\u6309\u5DF2\u63D0\u4F9B\u5355\u4EF7\u4F30\u7B97\uFF0C\u672C\u6B21\u672A\u8D85\u51FA\u9884\u7B97\uFF1B\u5B9E\u9645\u8D39\u7528\u4EE5\u6267\u884C\u8BB0\u5F55\u548C\u8D26\u5355\u4E3A\u51C6\u3002" : "\u5B9E\u9645\u8D39\u7528\u4EE5\u6267\u884C\u8BB0\u5F55\u548C\u8D26\u5355\u4E3A\u51C6\u3002"), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-actions" }, /* @__PURE__ */ import_react3.default.createElement("button", { ref: confirmRef, className: "mr-button", type: "button", disabled: running || blocked, onClick: onConfirm }, running ? "\u6B63\u5728\u6267\u884C\u2026" : reasons.length ? "\u5168\u90E8\u786E\u8BA4\u5E76\u6267\u884C" : "\u786E\u8BA4\u6267\u884C"), /* @__PURE__ */ import_react3.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", disabled: running, onClick: onCancel }, "\u53D6\u6D88"), running && /* @__PURE__ */ import_react3.default.createElement("span", { className: "mr-caption", role: "status", "aria-live": "polite" }, "\u6A21\u578B\u8C03\u7528\u53EF\u80FD\u9700\u8981\u51E0\u5206\u949F\uFF0C\u8BF7\u52FF\u5173\u95ED\u5DE5\u4F5C\u53F0\u3002")));
3435
4160
  }
3436
4161
 
3437
4162
  // .dsh-plugin/client/cli-terminal.jsx
@@ -12864,7 +13589,7 @@ var xterm_default = `/**
12864
13589
  `;
12865
13590
 
12866
13591
  // .dsh-plugin/client/host-version.mjs
12867
- var ROUTER_CLIENT_VERSION = true ? "0.21.0" : "";
13592
+ var ROUTER_CLIENT_VERSION = true ? "0.22.0" : "";
12868
13593
  var STALE_HOST_MESSAGE = "\u63D2\u4EF6\u540E\u53F0\u7248\u672C\u8F83\u65E7\uFF0C\u8BF7\u5B8C\u5168\u9000\u51FA\u5E76\u91CD\u542F Harness\uFF08\u5305\u62EC\u6258\u76D8\u56FE\u6807\uFF09\u540E\u518D\u4F7F\u7528\u3002";
12869
13594
  function isMissingRemoteMethod(message) {
12870
13595
  const value = String(message ?? "");
@@ -13475,6 +14200,11 @@ var router_main_default = `.mr-workspace {
13475
14200
  .mr-package-name { font-size: 12px; font-weight: 750; }
13476
14201
  .mr-package-route { color: var(--mr-accent); font-size: 11px; font-weight: 700; }
13477
14202
  .mr-package-copy { margin: 7px 0 0; color: var(--mr-muted); font-size: 12px; line-height: 1.65; overflow-wrap: anywhere; }
14203
+ .mr-assignment-controls { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin: 12px 0; }
14204
+ .mr-assignment-target { display: grid; gap: 7px; margin: 12px 0; min-width: 0; }
14205
+ .mr-assignment-target label { font-size: 12px; font-weight: 650; }
14206
+ .mr-assignment-target select { width: 100%; min-width: 0; max-width: 100%; text-overflow: ellipsis; }
14207
+ .mr-launch-packages .mr-pill { margin-left: 6px; }
13478
14208
  .mr-notice { margin-top: 18px; padding: 14px 16px; border: 1px solid rgba(49, 92, 200, .16); border-radius: 12px; background: #eff4ff; color: #354d7a; font-size: 11px; line-height: 1.65; }
13479
14209
  .mr-notice code { font-family: ui-monospace, SFMono-Regular, Consolas, monospace; font-size: 11px; }
13480
14210
  @media (max-width: 850px) { .mr-grid { grid-template-columns: 1fr; } .mr-shell { padding: 24px 18px 48px; } }
@@ -13698,6 +14428,7 @@ var text5 = (value) => typeof value === "string" ? value.trim() : "";
13698
14428
  var routingSettingsSignature = (value) => JSON.stringify([
13699
14429
  value.modelProfilesJson,
13700
14430
  value.routingPreset,
14431
+ value.allowManualReassign,
13701
14432
  ...Object.keys(ADAPTIVE_DEFAULTS).map((key) => value[key] ?? ADAPTIVE_DEFAULTS[key])
13702
14433
  ]);
13703
14434
  function RouterPanelIcon({ size = 20, active = false }) {
@@ -13716,12 +14447,25 @@ function ChannelBadge({ item }) {
13716
14447
  const official = item.executionChannel === "official-cli";
13717
14448
  return /* @__PURE__ */ import_react5.default.createElement("span", { className: official ? "mr-pill mr-pill-channel-ok" : "mr-pill", title: item.channelDetail ?? "" }, official ? `\u5B98\u65B9 CLI \xB7 ${item.channelLabel ?? item.channelTool}` : "\u6A21\u578B\u76EE\u5F55 API");
13718
14449
  }
13719
- function PlanResults({ plan, ledger, headingRef }) {
14450
+ function PlanResults({ plan, ledger, headingRef, assignmentMode = "algorithm", packageAssignments = {}, assignmentOptions = [], manualAllowed = true, onAssignmentModeChange, onPackageAssignmentChange, onRestoreAssignments }) {
13720
14451
  const selected = plan.selected;
13721
14452
  const budget = planBudget(ledger, plan.estimatedCost);
13722
14453
  const packageNames = new Map((plan.team?.workPackages ?? []).map((item) => [item.id, item.name]));
13723
14454
  const purposeLabel = { analysis: "\u4EFB\u52A1\u5206\u6790", execution: "\u4EFB\u52A1\u5B9E\u65BD", verification: "\u72EC\u7ACB\u9A8C\u8BC1", synthesis: "\u7ED3\u679C\u6574\u5408" };
13724
- return /* @__PURE__ */ import_react5.default.createElement("section", { className: "mr-card mr-results", "aria-label": "\u8DEF\u7531\u5EFA\u8BAE" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-card-head" }, /* @__PURE__ */ import_react5.default.createElement("div", null, /* @__PURE__ */ import_react5.default.createElement("h2", { className: "mr-card-title", ref: headingRef, tabIndex: -1 }, "\u8DEF\u7531\u5EFA\u8BAE"), /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-card-copy" }, "\u672C\u5730\u8BA1\u7B97\u5B8C\u6210\uFF0C\u672A\u5411\u6A21\u578B\u53D1\u9001\u4EFB\u52A1\u5185\u5BB9\u3002"))), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-card-body" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-result-grid" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-metric" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-metric-label" }, "\u63A8\u8350\u8DEF\u7EBF"), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-metric-value" }, selected ? `${selected.provider}/${selected.model}` : "\u6682\u65E0\u8DEF\u7EBF")), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-metric" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-metric-label" }, "\u4EFB\u52A1\u590D\u6742\u5EA6 \xB7 \u5206\u503C\uFF080\u20131\uFF09"), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-metric-value" }, { simple: "\u7B80\u5355", balanced: "\u4E2D\u7B49", complex: "\u590D\u6742" }[plan.complexity.band] || plan.complexity.band, " \xB7 ", plan.complexity.value)), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-metric" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-metric-label" }, "\u4F30\u7B97\u603B\u6210\u672C"), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-metric-value" }, money2(plan.estimatedCost))), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-metric" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-metric-label" }, "\u8DEF\u7531\u65B9\u6848"), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-metric-value" }, ROUTING_PRESETS[plan.preset]?.label ?? "\u5747\u8861"))), budget?.exceeded && /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-error", role: "alert" }, "\u6267\u884C\u524D\u9884\u7B97\u68C0\u67E5\uFF1A", budget.message, " \u5B9E\u9645\u6267\u884C\u65F6\u5C06\u6309\u8BBE\u7F6E\u81EA\u52A8\u964D\u7EA7\u6216\u6682\u505C\u8BE2\u95EE\u3002"), budget?.limited && !budget.exceeded && budget.estimateKnown && /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-caption" }, "\u6267\u884C\u524D\u9884\u7B97\u68C0\u67E5\uFF1A\u672C\u6B21\u9884\u4F30 ", money2(plan.estimatedCost), "\uFF0C\u5269\u4F59\u989D\u5EA6 ", money2(budget.remainingUsd), "\u3002"), plan.loginRequired && /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-caption" }, "\u63A8\u8350\u6A21\u578B\u7684\u5B98\u65B9 CLI \u672A\u767B\u5F55\uFF0C\u6267\u884C\u65F6\u6309\u8BA1\u8D39\u8BBE\u7F6E\u6682\u505C\u6216\u4F7F\u7528\u6A21\u578B\u76EE\u5F55 API\uFF1B\u53EF\u5728\u201C\u5B98\u65B9\u5DE5\u5177\u201D\u5361\u7247\u70B9\u201C\u53BB\u767B\u5F55\u201D\u3002"), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-channel-line" }, /* @__PURE__ */ import_react5.default.createElement("span", { className: "mr-control-label" }, "\u6267\u884C\u6E20\u9053"), /* @__PURE__ */ import_react5.default.createElement(ChannelBadge, { item: plan })), /* @__PURE__ */ import_react5.default.createElement("details", { className: "mr-plan-explanation" }, /* @__PURE__ */ import_react5.default.createElement("summary", null, "\u67E5\u770B\u89C4\u5212\u4F9D\u636E\u4E0E\u4F30\u7B97\u8BF4\u660E"), /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-caption" }, plan.reason), /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-caption" }, plan.pricingNotice, " ", plan.qualityNotice, " ", plan.availabilityNotice, " ", plan.modalityNotice || "")), plan.optimization.budgetExceeded && /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-error" }, "\u6309\u5DF2\u63D0\u4F9B\u5355\u4EF7\u4F30\u7B97\uFF0C\u4EFB\u52A1\u53EF\u80FD\u8D85\u8FC7\u672C\u6B21\u9884\u7B97\u3002\u9884\u7B97\u53EA\u5F71\u54CD\u5EFA\u8BAE\uFF0C\u4E0D\u4F1A\u963B\u6B62\u5B9E\u9645\u6263\u8D39\u3002"), plan.mode === "team" && /* @__PURE__ */ import_react5.default.createElement(import_react5.default.Fragment, null, /* @__PURE__ */ import_react5.default.createElement("h3", { className: "mr-section-title" }, "\u56E2\u961F\u5DE5\u4F5C\u5305"), /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-caption" }, "\u4E0B\u65B9\u6A21\u578B\u662F\u5DE5\u4F5C\u5305\u7684\u89C4\u5212\u5EFA\u8BAE\u3002\u6A21\u578B\u8DEF\u7531\u6309\u53EA\u8BFB\u65B9\u5F0F\u6267\u884C\uFF0C\u6BCF\u6B65\u53EF\u4F7F\u7528\u5B98\u65B9 CLI \u6216\u6A21\u578B\u76EE\u5F55 API\uFF1B\u5982\u9700\u7F16\u8F91\u6587\u4EF6\uFF0C\u53EF\u5728\u4E0B\u65B9\u6267\u884C\u533A\u9009\u62E9\u201C\u5B98\u65B9\u5DE5\u5177\u534F\u4F5C\u201D\u548C\u201C\u7F16\u8F91\u6587\u4EF6\u201D\u6743\u9650\u3002CLI \u8BF7\u6C42\u6A21\u578B\u540D\u9700\u7B26\u5408\u5382\u5546\u89C4\u5219\uFF0C\u5B9E\u9645\u6A21\u578B\u4EE5\u8FD0\u884C\u8BB0\u5F55\u4E3A\u51C6\u3002"), plan.team.workPackages.length > 1 && /* @__PURE__ */ import_react5.default.createElement(DagView, { packages: plan.team.workPackages, label: "\u5DE5\u4F5C\u5305\u4F9D\u8D56\u56FE", renderNode: (item) => /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-package-route" }, item.recommendedProvider, "/", item.recommendedModel) }), plan.team.workPackages.length === 0 ? /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-empty" }, "\u5F53\u524D\u76EE\u5F55\u6CA1\u6709\u53EF\u5206\u914D\u7684\u6A21\u578B\u8DEF\u7EBF\u3002") : plan.team.workPackages.map((item, index) => /* @__PURE__ */ import_react5.default.createElement("details", { className: "mr-package mr-package-details", key: item.id }, /* @__PURE__ */ import_react5.default.createElement("summary", null, /* @__PURE__ */ import_react5.default.createElement("span", { className: "mr-package-name" }, index + 1, ". ", item.name), /* @__PURE__ */ import_react5.default.createElement("span", { className: "mr-package-route" }, item.recommendedProvider, "/", item.recommendedModel)), item.objective && /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-package-copy" }, "\u5177\u4F53\u76EE\u6807\uFF1A", item.objective), /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-package-copy" }, purposeLabel[item.purpose] ?? item.purpose, item.dependsOn.length > 0 ? ` \xB7 \u4F9D\u8D56\uFF1A${item.dependsOn.map((id2) => packageNames.get(id2) ?? id2).join("\u3001")}` : ""), /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-package-copy" }, "\u96BE\u5EA6\uFF1A", { simple: "\u7B80\u5355", balanced: "\u4E2D\u7B49", complex: "\u56F0\u96BE" }[item.difficulty] || item.difficulty || "\u5F85\u8BC4\u4F30", " \xB7 \u8D39\u7528\uFF1A", money2(item.estimatedCost)), /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-package-copy" }, "\u9A8C\u6536\uFF1A", item.verificationChecklist.join("\uFF1B")), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-channel-line" }, /* @__PURE__ */ import_react5.default.createElement(ChannelBadge, { item }), item.loginRequired && /* @__PURE__ */ import_react5.default.createElement("span", { className: "mr-pill" }, "CLI \u672A\u767B\u5F55")), item.channelDetail && /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-package-copy" }, "\u6E20\u9053\u8BF4\u660E\uFF1A", item.channelDetail)))), plan.routingBypassed && /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-caption" }, "\u5DF2\u6307\u5B9A\u5355\u4E00\u6A21\u578B\uFF0C\u672A\u4E0E\u5176\u4ED6\u8DEF\u7EBF\u6BD4\u8F83\u3002\u5728\u5B98\u65B9\u4F1A\u8BDD\u4E2D\u8C03\u7528 ", /* @__PURE__ */ import_react5.default.createElement("code", null, "model_router_execute"), " \u5E76\u4F20\u5165\u8BE5 provider \u4E0E model \u5373\u53EF\u76F4\u63A5\u6267\u884C\uFF1B\u82E5\u8BE5\u6A21\u578B\u5141\u8BB8\u5B98\u65B9\u5DE5\u5177\uFF0C\u4F1A\u4F18\u5148\u4F7F\u7528\u5BF9\u5E94 CLI\u3002"), plan.mode === "team" && /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-caption" }, plan.team.handoff)));
14455
+ return /* @__PURE__ */ import_react5.default.createElement("section", { className: "mr-card mr-results", "aria-label": "\u8DEF\u7531\u5EFA\u8BAE" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-card-head" }, /* @__PURE__ */ import_react5.default.createElement("div", null, /* @__PURE__ */ import_react5.default.createElement("h2", { className: "mr-card-title", ref: headingRef, tabIndex: -1 }, "\u8DEF\u7531\u5EFA\u8BAE"), /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-card-copy" }, "\u672C\u5730\u8BA1\u7B97\u5B8C\u6210\uFF0C\u672A\u5411\u6A21\u578B\u53D1\u9001\u4EFB\u52A1\u5185\u5BB9\u3002"))), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-card-body" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-result-grid" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-metric" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-metric-label" }, assignmentMode === "manual" ? "\u7B97\u6CD5\u539F\u63A8\u8350\u8DEF\u7EBF" : "\u63A8\u8350\u8DEF\u7EBF"), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-metric-value" }, selected ? `${selected.provider}/${selected.model}` : "\u6682\u65E0\u8DEF\u7EBF")), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-metric" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-metric-label" }, "\u4EFB\u52A1\u590D\u6742\u5EA6 \xB7 \u5206\u503C\uFF080\u20131\uFF09"), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-metric-value" }, { simple: "\u7B80\u5355", balanced: "\u4E2D\u7B49", complex: "\u590D\u6742" }[plan.complexity.band] || plan.complexity.band, " \xB7 ", plan.complexity.value)), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-metric" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-metric-label" }, assignmentMode === "manual" ? "\u8C03\u6574\u524D\u7B97\u6CD5\u65B9\u6848\u4F30\u7B97" : "\u4F30\u7B97\u603B\u6210\u672C"), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-metric-value" }, money2(plan.estimatedCost))), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-metric" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-metric-label" }, "\u8DEF\u7531\u65B9\u6848"), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-metric-value" }, ROUTING_PRESETS[plan.preset]?.label ?? "\u5747\u8861"))), budget?.exceeded && /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-error", role: "alert" }, assignmentMode === "manual" ? "\u7B97\u6CD5\u539F\u65B9\u6848\u9884\u7B97\u63D0\u793A\uFF1A" : "\u6267\u884C\u524D\u9884\u7B97\u68C0\u67E5\uFF1A", budget.message, " ", assignmentMode === "manual" ? "\u624B\u52A8\u65B9\u6848\u5C06\u91CD\u65B0\u9884\u89C8\u8D39\u7528\uFF0C\u8D85\u9884\u7B97\u65F6\u6682\u505C\u786E\u8BA4\uFF0C\u4E0D\u81EA\u52A8\u66FF\u6362\u76EE\u6807\u3002" : "\u5B9E\u9645\u6267\u884C\u65F6\u5C06\u6309\u8BBE\u7F6E\u81EA\u52A8\u964D\u7EA7\u6216\u6682\u505C\u8BE2\u95EE\u3002"), budget?.limited && !budget.exceeded && budget.estimateKnown && /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-caption" }, assignmentMode === "manual" ? "\u7B97\u6CD5\u539F\u65B9\u6848\uFF1A" : "\u6267\u884C\u524D\u9884\u7B97\u68C0\u67E5\uFF1A\u672C\u6B21\u9884\u4F30 ", money2(plan.estimatedCost), "\uFF0C\u5269\u4F59\u989D\u5EA6 ", money2(budget.remainingUsd), "\u3002"), plan.loginRequired && /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-caption" }, "\u63A8\u8350\u6A21\u578B\u7684\u5B98\u65B9 CLI \u672A\u767B\u5F55\uFF0C\u6267\u884C\u65F6\u6309\u8BA1\u8D39\u8BBE\u7F6E\u6682\u505C\u6216\u4F7F\u7528\u6A21\u578B\u76EE\u5F55 API\uFF1B\u53EF\u5728\u201C\u5B98\u65B9\u5DE5\u5177\u201D\u5361\u7247\u70B9\u201C\u53BB\u767B\u5F55\u201D\u3002"), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-channel-line" }, /* @__PURE__ */ import_react5.default.createElement("span", { className: "mr-control-label" }, assignmentMode === "manual" ? "\u7B97\u6CD5\u539F\u63A8\u8350\u6E20\u9053" : "\u6267\u884C\u6E20\u9053"), /* @__PURE__ */ import_react5.default.createElement(ChannelBadge, { item: plan })), /* @__PURE__ */ import_react5.default.createElement("details", { className: "mr-plan-explanation" }, /* @__PURE__ */ import_react5.default.createElement("summary", null, "\u67E5\u770B\u89C4\u5212\u4F9D\u636E\u4E0E\u4F30\u7B97\u8BF4\u660E"), /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-caption" }, plan.reason), /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-caption" }, plan.pricingNotice, " ", plan.qualityNotice, " ", plan.availabilityNotice, " ", plan.modalityNotice || "")), plan.optimization.budgetExceeded && /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-error" }, "\u6309\u5DF2\u63D0\u4F9B\u5355\u4EF7\u4F30\u7B97\uFF0C\u4EFB\u52A1\u53EF\u80FD\u8D85\u8FC7\u672C\u6B21\u9884\u7B97\u3002\u9884\u7B97\u53EA\u5F71\u54CD\u5EFA\u8BAE\uFF0C\u4E0D\u4F1A\u963B\u6B62\u5B9E\u9645\u6263\u8D39\u3002"), !plan.routingBypassed && plan.team && /* @__PURE__ */ import_react5.default.createElement(import_react5.default.Fragment, null, /* @__PURE__ */ import_react5.default.createElement("h3", { className: "mr-section-title" }, plan.mode === "team" ? "\u56E2\u961F\u5DE5\u4F5C\u5305" : "\u4EFB\u52A1\u5DE5\u4F5C\u5305"), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-assignment-controls" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-segment", role: "group", "aria-label": "\u5DE5\u4F5C\u5305\u5206\u914D\u65B9\u5F0F" }, /* @__PURE__ */ import_react5.default.createElement("button", { type: "button", "aria-pressed": assignmentMode === "algorithm", onClick: () => onAssignmentModeChange?.("algorithm") }, "\u6309\u7B97\u6CD5\u63A8\u8350\u5206\u914D"), /* @__PURE__ */ import_react5.default.createElement("button", { type: "button", "aria-pressed": assignmentMode === "manual", disabled: !manualAllowed || !plan.team.workPackages.length, title: !manualAllowed ? "\u8BBE\u7F6E\u4E2D\u5DF2\u5173\u95ED\u624B\u52A8\u6539\u6D3E" : "\u4E3A\u6BCF\u4E2A\u5DE5\u4F5C\u5305\u9009\u62E9\u6A21\u578B API \u6216\u5B98\u65B9\u5DE5\u5177", onClick: () => onAssignmentModeChange?.("manual") }, "\u624B\u52A8\u8C03\u6574\u5206\u914D")), assignmentMode === "manual" && /* @__PURE__ */ import_react5.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", onClick: onRestoreAssignments }, "\u6062\u590D\u63A8\u8350\u5206\u914D")), !manualAllowed && /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-caption" }, "\u8BBE\u7F6E\u4E2D\u5DF2\u5173\u95ED\u624B\u52A8\u6539\u6D3E\u3002"), /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-caption" }, assignmentMode === "manual" ? "\u624B\u52A8\u5206\u914D\u53EF\u6DF7\u5408\u6A21\u578B API \u4E0E\u5B98\u65B9\u5DE5\u5177\uFF0C\u6309\u4F9D\u8D56\u6267\u884C\u53EA\u8BFB\u4EFB\u52A1\uFF1B\u5DE5\u5177\u4F7F\u7528\u9ED8\u8BA4\u6A21\u578B\u3002\u4E0A\u65B9\u8D39\u7528\u4ECD\u662F\u7B97\u6CD5\u5EFA\u8BAE\u4F30\u7B97\uFF0C\u624B\u52A8\u76EE\u6807\u4E0E\u8D39\u7528\u4EE5\u6267\u884C\u9884\u89C8\u4E3A\u51C6\uFF0C\u9884\u89C8\u4F1A\u6838\u9A8C\u5957\u9910\u8BA1\u8D39\u4E0E\u5DE5\u5177\u5C31\u7EEA\u72B6\u6001\u3002" : "\u4E0B\u65B9\u6A21\u578B\u662F\u5DE5\u4F5C\u5305\u7684\u89C4\u5212\u5EFA\u8BAE\u3002\u6A21\u578B\u8DEF\u7531\u6309\u53EA\u8BFB\u65B9\u5F0F\u6267\u884C\uFF0C\u6BCF\u6B65\u53EF\u4F7F\u7528\u5B98\u65B9 CLI \u6216\u6A21\u578B\u76EE\u5F55 API\uFF1B\u5982\u9700\u7F16\u8F91\u6587\u4EF6\uFF0C\u53EF\u5728\u4E0B\u65B9\u6267\u884C\u533A\u9009\u62E9\u201C\u5B98\u65B9\u5DE5\u5177\u534F\u4F5C\u201D\u548C\u201C\u7F16\u8F91\u6587\u4EF6\u201D\u6743\u9650\u3002CLI \u8BF7\u6C42\u6A21\u578B\u540D\u9700\u7B26\u5408\u5382\u5546\u89C4\u5219\uFF0C\u5B9E\u9645\u6A21\u578B\u4EE5\u8FD0\u884C\u8BB0\u5F55\u4E3A\u51C6\u3002"), plan.team.workPackages.length > 1 && /* @__PURE__ */ import_react5.default.createElement(DagView, { packages: plan.team.workPackages, label: "\u5DE5\u4F5C\u5305\u4F9D\u8D56\u56FE", renderNode: (item) => /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-package-route" }, "\u7B97\u6CD5\u63A8\u8350\uFF1A", item.recommendedProvider, "/", item.recommendedModel) }), plan.team.workPackages.length === 0 ? /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-empty" }, "\u5F53\u524D\u76EE\u5F55\u6CA1\u6709\u53EF\u5206\u914D\u7684\u6A21\u578B\u8DEF\u7EBF\u3002") : plan.team.workPackages.map((item, index) => /* @__PURE__ */ import_react5.default.createElement("details", { className: "mr-package mr-package-details", key: item.id, open: assignmentMode === "manual" ? true : void 0 }, /* @__PURE__ */ import_react5.default.createElement("summary", null, /* @__PURE__ */ import_react5.default.createElement("span", { className: "mr-package-name" }, index + 1, ". ", item.name), /* @__PURE__ */ import_react5.default.createElement("span", { className: "mr-package-route" }, "\u7B97\u6CD5\u63A8\u8350\uFF1A", item.recommendedProvider, "/", item.recommendedModel)), assignmentMode === "manual" && /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-assignment-target" }, /* @__PURE__ */ import_react5.default.createElement("label", { htmlFor: `mr-assignment-${item.id}` }, item.name, " \u6267\u884C\u76EE\u6807"), /* @__PURE__ */ import_react5.default.createElement(
14456
+ "select",
14457
+ {
14458
+ className: "mr-input",
14459
+ id: `mr-assignment-${item.id}`,
14460
+ value: JSON.stringify(packageAssignments[item.id] ?? {}),
14461
+ onChange: (event) => {
14462
+ const option = assignmentOptions.find((candidate) => candidate.value === event.target.value && !candidate.disabled);
14463
+ if (option) onPackageAssignmentChange?.(item.id, { ...option.binding });
14464
+ }
14465
+ },
14466
+ /* @__PURE__ */ import_react5.default.createElement("optgroup", { label: "\u6A21\u578B\u76EE\u5F55 API" }, assignmentOptions.filter((option) => option.binding.kind === "api").map((option) => /* @__PURE__ */ import_react5.default.createElement("option", { key: option.value, value: option.value, disabled: option.disabled }, option.label))),
14467
+ /* @__PURE__ */ import_react5.default.createElement("optgroup", { label: "\u5B98\u65B9\u5DE5\u5177\uFF08\u9ED8\u8BA4\u6A21\u578B\uFF09" }, assignmentOptions.filter((option) => option.binding.kind === "tool").map((option) => /* @__PURE__ */ import_react5.default.createElement("option", { key: option.value, value: option.value, disabled: option.disabled }, option.label)))
14468
+ )), item.objective && /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-package-copy" }, "\u5177\u4F53\u76EE\u6807\uFF1A", item.objective), /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-package-copy" }, purposeLabel[item.purpose] ?? item.purpose, item.dependsOn.length > 0 ? ` \xB7 \u4F9D\u8D56\uFF1A${item.dependsOn.map((id2) => packageNames.get(id2) ?? id2).join("\u3001")}` : ""), /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-package-copy" }, "\u96BE\u5EA6\uFF1A", { simple: "\u7B80\u5355", balanced: "\u4E2D\u7B49", complex: "\u56F0\u96BE" }[item.difficulty] || item.difficulty || "\u5F85\u8BC4\u4F30", " \xB7 ", assignmentMode === "manual" ? "\u539F\u63A8\u8350\u8D39\u7528" : "\u8D39\u7528", "\uFF1A", money2(item.estimatedCost)), /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-package-copy" }, "\u9A8C\u6536\uFF1A", item.verificationChecklist.join("\uFF1B")), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-channel-line" }, assignmentMode === "manual" && /* @__PURE__ */ import_react5.default.createElement("span", { className: "mr-caption" }, "\u539F\u63A8\u8350\u6E20\u9053\uFF1A"), /* @__PURE__ */ import_react5.default.createElement(ChannelBadge, { item }), item.loginRequired && /* @__PURE__ */ import_react5.default.createElement("span", { className: "mr-pill" }, "CLI \u672A\u767B\u5F55")), item.channelDetail && /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-package-copy" }, "\u6E20\u9053\u8BF4\u660E\uFF1A", item.channelDetail)))), plan.routingBypassed && /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-caption" }, "\u5DF2\u6307\u5B9A\u5355\u4E00\u6A21\u578B\uFF0C\u672A\u4E0E\u5176\u4ED6\u8DEF\u7EBF\u6BD4\u8F83\u3002\u5728\u5B98\u65B9\u4F1A\u8BDD\u4E2D\u8C03\u7528 ", /* @__PURE__ */ import_react5.default.createElement("code", null, "model_router_execute"), " \u5E76\u4F20\u5165\u8BE5 provider \u4E0E model \u5373\u53EF\u76F4\u63A5\u6267\u884C\uFF1B\u82E5\u8BE5\u6A21\u578B\u5141\u8BB8\u5B98\u65B9\u5DE5\u5177\uFF0C\u4F1A\u4F18\u5148\u4F7F\u7528\u5BF9\u5E94 CLI\u3002"), plan.mode === "team" && assignmentMode !== "manual" && /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-caption" }, plan.team.handoff)));
13725
14469
  }
13726
14470
  var remoteError = (response, fallback) => remoteErrorText(
13727
14471
  text5(response?.error?.message) || text5(response?.value?.error) || (typeof response?.error === "string" ? response.error : ""),
@@ -14379,6 +15123,8 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, loadOff
14379
15123
  const [budget, setBudget] = import_react5.default.useState(() => String(settingsScope.getSnapshot().value?.budgetUsd ?? 0));
14380
15124
  const [query, setQuery] = import_react5.default.useState("");
14381
15125
  const [plan, setPlan] = import_react5.default.useState(null);
15126
+ const [assignmentMode, setAssignmentMode] = import_react5.default.useState("algorithm");
15127
+ const [packageAssignments, setPackageAssignments] = import_react5.default.useState({});
14382
15128
  const [planError, setPlanError] = import_react5.default.useState("");
14383
15129
  const [toolProbes, setToolProbes] = import_react5.default.useState(null);
14384
15130
  const [routingSettings, setRoutingSettings] = import_react5.default.useState(() => {
@@ -14434,12 +15180,18 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, loadOff
14434
15180
  }, [routingSettings]);
14435
15181
  const dataRevision = workbench.ledger.value?.dynamicData?.revision ?? "";
14436
15182
  const feedbackRevision = workbench.ledger.value?.learning?.revision ?? "";
15183
+ const evidenceRevision = workbench.ledger.value?.routingEvidence?.revision ?? "";
15184
+ const manualAllowed = workbench.ledger.value?.settings?.allowManualReassign !== false && settingsScope.getSnapshot().value?.allowManualReassign !== false;
14437
15185
  import_react5.default.useEffect(() => {
14438
- if (plan && plan.workbenchRevision !== `${dataRevision}/${feedbackRevision}`) {
15186
+ setAssignmentMode("algorithm");
15187
+ setPackageAssignments({});
15188
+ }, [plan, task, mode, routingSettings, catalogState.catalog, toolProbes, workbench.health.report?.tools, manualAllowed]);
15189
+ import_react5.default.useEffect(() => {
15190
+ if (plan && plan.workbenchRevision !== `${dataRevision}/${feedbackRevision}/${evidenceRevision}`) {
14439
15191
  setPlan(null);
14440
15192
  setPlanError("");
14441
15193
  }
14442
- }, [dataRevision, feedbackRevision, plan]);
15194
+ }, [dataRevision, feedbackRevision, evidenceRevision, plan]);
14443
15195
  import_react5.default.useEffect(() => {
14444
15196
  mounted.current = true;
14445
15197
  const request = ++catalogRequest.current;
@@ -14456,6 +15208,23 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, loadOff
14456
15208
  };
14457
15209
  }, []);
14458
15210
  const routes = routesFromModelCatalog(catalogState.catalog);
15211
+ const assignmentOptions = manualAssignmentOptions(routes, toolProbes, workbench.health.report);
15212
+ const currentPackagePlanKey = packagePlanKey(plan, task);
15213
+ const effectiveAssignmentMode = assignmentMode === "manual" && currentPackagePlanKey && manualAllowed && mode !== "direct" ? "manual" : "algorithm";
15214
+ const changeAssignmentMode = (next) => {
15215
+ if (next === "manual") {
15216
+ if (!manualAllowed || !currentPackagePlanKey) return;
15217
+ setPackageAssignments(initialPackageAssignments(plan));
15218
+ setAssignmentMode("manual");
15219
+ } else {
15220
+ setAssignmentMode("algorithm");
15221
+ setPackageAssignments({});
15222
+ }
15223
+ };
15224
+ const changePackageAssignment = (id2, binding) => {
15225
+ if (!manualAllowed || effectiveAssignmentMode !== "manual" || !plan.team.workPackages.some((item) => item.id === id2)) return;
15226
+ setPackageAssignments((previous) => ({ ...previous, [id2]: binding }));
15227
+ };
14459
15228
  const providerCount = new Set(routes.map((route) => route.provider)).size;
14460
15229
  const refresh = async () => {
14461
15230
  const request = ++catalogRequest.current;
@@ -14502,6 +15271,7 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, loadOff
14502
15271
  const next = createWorkspacePlan(task, catalogState.catalog, {
14503
15272
  ...ledger.routingData,
14504
15273
  learning: ledger.learning,
15274
+ routingEvidence: ledger.routingEvidence,
14505
15275
  mode,
14506
15276
  ...direct ? { directProvider: direct.provider, directModel: direct.model } : {},
14507
15277
  budgetUsd: parsedBudget,
@@ -14511,7 +15281,7 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, loadOff
14511
15281
  preset: settingsScope.getSnapshot().value?.routingPreset ?? "balanced",
14512
15282
  loggedOutToolIds: (workbench.health.report?.tools ?? []).filter((item) => item.installed && item.login?.state === "logged-out").map((item) => item.id)
14513
15283
  });
14514
- setPlan({ ...next, workbenchRevision: `${ledger.dynamicData?.revision ?? ""}/${ledger.learning?.revision ?? ""}` });
15284
+ setPlan({ ...next, workbenchRevision: `${ledger.dynamicData?.revision ?? ""}/${ledger.learning?.revision ?? ""}/${ledger.routingEvidence?.revision ?? ""}` });
14515
15285
  } catch (error) {
14516
15286
  if (request !== generationRequest.current || inputs !== generationInputs.current || !mounted.current) return;
14517
15287
  setPlan(null);
@@ -14541,7 +15311,23 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, loadOff
14541
15311
  budgetValue.current = event.target.value;
14542
15312
  setBudget(event.target.value);
14543
15313
  invalidatePlan();
14544
- } }))), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-actions" }, /* @__PURE__ */ import_react5.default.createElement("button", { className: "mr-button", type: "button", disabled: catalogState.status !== "ready" || routes.length === 0 || toolProbes === null, onClick: generate }, "\u751F\u6210\u8DEF\u7531\u5EFA\u8BAE"), /* @__PURE__ */ import_react5.default.createElement("span", { className: "mr-caption" }, catalogState.status === "loading" ? "\u6B63\u5728\u8BFB\u53D6\u6A21\u578B\u76EE\u5F55\u2026" : catalogState.status === "error" ? "\u76EE\u5F55\u8BFB\u53D6\u5931\u8D25\uFF0C\u8BF7\u5728\u201C\u6A21\u578B\u914D\u7F6E\u201D\u4E2D\u5237\u65B0\u3002" : routes.length === 0 ? "\u8BF7\u5148\u5728 Harness \u7684\u201C\u6A21\u578B\u201D\u9875\u6DFB\u52A0\u6A21\u578B\u3002" : toolProbes === null ? "\u6B63\u5728\u68C0\u6D4B\u5B98\u65B9\u5DE5\u5177\u2026" : "\u9884\u7B97 0 \u4E3A\u4E0D\u9650\uFF1B\u8D39\u7528\u4E3A\u4F30\u7B97\u3002")), planError && /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-error", role: "alert" }, planError))), /* @__PURE__ */ import_react5.default.createElement("aside", { className: "mr-planning-guide", "aria-label": "\u89C4\u5212\u4F7F\u7528\u63D0\u793A" }, /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-eyebrow" }, "\u5DE5\u4F5C\u6D41\u7A0B"), /* @__PURE__ */ import_react5.default.createElement("ol", { className: "mr-workflow" }, /* @__PURE__ */ import_react5.default.createElement("li", null, /* @__PURE__ */ import_react5.default.createElement("span", null, "01"), /* @__PURE__ */ import_react5.default.createElement("div", null, /* @__PURE__ */ import_react5.default.createElement("strong", null, "\u63CF\u8FF0\u4EFB\u52A1"), /* @__PURE__ */ import_react5.default.createElement("p", null, "\u9009\u62E9\u5355\u4EFB\u52A1\u3001\u56E2\u961F\u5206\u5DE5\u6216\u6307\u5B9A\u6A21\u578B\u3002"))), /* @__PURE__ */ import_react5.default.createElement("li", null, /* @__PURE__ */ import_react5.default.createElement("span", null, "02"), /* @__PURE__ */ import_react5.default.createElement("div", null, /* @__PURE__ */ import_react5.default.createElement("strong", null, "\u6838\u5BF9\u8DEF\u7531\u5EFA\u8BAE"), /* @__PURE__ */ import_react5.default.createElement("p", null, "\u68C0\u67E5\u6A21\u578B\u3001\u5DE5\u4F5C\u5305\u4F9D\u8D56\u548C\u4F30\u7B97\u8D39\u7528\u3002"))), /* @__PURE__ */ import_react5.default.createElement("li", null, /* @__PURE__ */ import_react5.default.createElement("span", null, "03"), /* @__PURE__ */ import_react5.default.createElement("div", null, /* @__PURE__ */ import_react5.default.createElement("strong", null, "\u9884\u89C8\u5E76\u786E\u8BA4\u6267\u884C"), /* @__PURE__ */ import_react5.default.createElement("p", null, "\u786E\u8BA4\u540E\u8C03\u7528\u6A21\u578B\uFF0C\u7ED3\u679C\u5199\u5165\u6267\u884C\u8BB0\u5F55\u3002")))), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-guide-links" }, /* @__PURE__ */ import_react5.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", onClick: () => selectView("models") }, "\u914D\u7F6E\u6A21\u578B\u4EF7\u683C\u4E0E\u80FD\u529B \u2192"), /* @__PURE__ */ import_react5.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", onClick: () => selectView("tools") }, "\u68C0\u67E5\u5B98\u65B9\u5DE5\u5177\u4E0E\u767B\u5F55 \u2192")), /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-caption" }, "\u7F3A\u5C11\u4EF7\u683C\u65F6\u4F1A\u63D0\u793A\u201C\u4EF7\u683C\u5F85\u914D\u7F6E\u201D\u3002\u53EA\u8BFB\u68C0\u67E5\u53EF\u76F4\u63A5\u9884\u89C8\uFF1B\u4FEE\u6539\u6587\u4EF6\u65F6\uFF0C\u5148\u9009\u201C\u56E2\u961F\u5206\u5DE5\u201D\uFF0C\u518D\u5728\u6267\u884C\u533A\u9009\u62E9\u201C\u5B98\u65B9\u5DE5\u5177\u534F\u4F5C\u201D\u548C\u201C\u7F16\u8F91\u6587\u4EF6\u201D\u6743\u9650\uFF0C\u6838\u5BF9\u9884\u89C8\u540E\u786E\u8BA4\u6267\u884C\u3002"))), plan && /* @__PURE__ */ import_react5.default.createElement(PlanResults, { plan, ledger: workbench.ledger.value, headingRef: resultHeading }), /* @__PURE__ */ import_react5.default.createElement(
15314
+ } }))), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-actions" }, /* @__PURE__ */ import_react5.default.createElement("button", { className: "mr-button", type: "button", disabled: catalogState.status !== "ready" || routes.length === 0 || toolProbes === null, onClick: generate }, "\u751F\u6210\u8DEF\u7531\u5EFA\u8BAE"), /* @__PURE__ */ import_react5.default.createElement("span", { className: "mr-caption" }, catalogState.status === "loading" ? "\u6B63\u5728\u8BFB\u53D6\u6A21\u578B\u76EE\u5F55\u2026" : catalogState.status === "error" ? "\u76EE\u5F55\u8BFB\u53D6\u5931\u8D25\uFF0C\u8BF7\u5728\u201C\u6A21\u578B\u914D\u7F6E\u201D\u4E2D\u5237\u65B0\u3002" : routes.length === 0 ? "\u8BF7\u5148\u5728 Harness \u7684\u201C\u6A21\u578B\u201D\u9875\u6DFB\u52A0\u6A21\u578B\u3002" : toolProbes === null ? "\u6B63\u5728\u68C0\u6D4B\u5B98\u65B9\u5DE5\u5177\u2026" : "\u9884\u7B97 0 \u4E3A\u4E0D\u9650\uFF1B\u8D39\u7528\u4E3A\u4F30\u7B97\u3002")), planError && /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-error", role: "alert" }, planError))), /* @__PURE__ */ import_react5.default.createElement("aside", { className: "mr-planning-guide", "aria-label": "\u89C4\u5212\u4F7F\u7528\u63D0\u793A" }, /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-eyebrow" }, "\u5DE5\u4F5C\u6D41\u7A0B"), /* @__PURE__ */ import_react5.default.createElement("ol", { className: "mr-workflow" }, /* @__PURE__ */ import_react5.default.createElement("li", null, /* @__PURE__ */ import_react5.default.createElement("span", null, "01"), /* @__PURE__ */ import_react5.default.createElement("div", null, /* @__PURE__ */ import_react5.default.createElement("strong", null, "\u63CF\u8FF0\u4EFB\u52A1"), /* @__PURE__ */ import_react5.default.createElement("p", null, "\u9009\u62E9\u5355\u4EFB\u52A1\u3001\u56E2\u961F\u5206\u5DE5\u6216\u6307\u5B9A\u6A21\u578B\u3002"))), /* @__PURE__ */ import_react5.default.createElement("li", null, /* @__PURE__ */ import_react5.default.createElement("span", null, "02"), /* @__PURE__ */ import_react5.default.createElement("div", null, /* @__PURE__ */ import_react5.default.createElement("strong", null, "\u6838\u5BF9\u8DEF\u7531\u5EFA\u8BAE"), /* @__PURE__ */ import_react5.default.createElement("p", null, "\u68C0\u67E5\u6A21\u578B\u3001\u5DE5\u4F5C\u5305\u4F9D\u8D56\u548C\u4F30\u7B97\u8D39\u7528\u3002"))), /* @__PURE__ */ import_react5.default.createElement("li", null, /* @__PURE__ */ import_react5.default.createElement("span", null, "03"), /* @__PURE__ */ import_react5.default.createElement("div", null, /* @__PURE__ */ import_react5.default.createElement("strong", null, "\u9884\u89C8\u5E76\u786E\u8BA4\u6267\u884C"), /* @__PURE__ */ import_react5.default.createElement("p", null, "\u786E\u8BA4\u540E\u8C03\u7528\u6A21\u578B\uFF0C\u7ED3\u679C\u5199\u5165\u6267\u884C\u8BB0\u5F55\u3002")))), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-guide-links" }, /* @__PURE__ */ import_react5.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", onClick: () => selectView("models") }, "\u914D\u7F6E\u6A21\u578B\u4EF7\u683C\u4E0E\u80FD\u529B \u2192"), /* @__PURE__ */ import_react5.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", onClick: () => selectView("tools") }, "\u68C0\u67E5\u5B98\u65B9\u5DE5\u5177\u4E0E\u767B\u5F55 \u2192")), /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-caption" }, "\u7F3A\u5C11\u4EF7\u683C\u65F6\u4F1A\u63D0\u793A\u201C\u4EF7\u683C\u5F85\u914D\u7F6E\u201D\u3002\u53EA\u8BFB\u68C0\u67E5\u53EF\u76F4\u63A5\u9884\u89C8\uFF1B\u4FEE\u6539\u6587\u4EF6\u65F6\uFF0C\u5148\u9009\u201C\u56E2\u961F\u5206\u5DE5\u201D\uFF0C\u518D\u5728\u6267\u884C\u533A\u9009\u62E9\u201C\u5B98\u65B9\u5DE5\u5177\u534F\u4F5C\u201D\u548C\u201C\u7F16\u8F91\u6587\u4EF6\u201D\u6743\u9650\uFF0C\u6838\u5BF9\u9884\u89C8\u540E\u786E\u8BA4\u6267\u884C\u3002"))), plan && /* @__PURE__ */ import_react5.default.createElement(
15315
+ PlanResults,
15316
+ {
15317
+ plan,
15318
+ ledger: workbench.ledger.value,
15319
+ headingRef: resultHeading,
15320
+ assignmentMode: effectiveAssignmentMode,
15321
+ packageAssignments,
15322
+ assignmentOptions,
15323
+ manualAllowed,
15324
+ onAssignmentModeChange: changeAssignmentMode,
15325
+ onPackageAssignmentChange: changePackageAssignment,
15326
+ onRestoreAssignments: () => {
15327
+ if (manualAllowed && currentPackagePlanKey) setPackageAssignments(initialPackageAssignments(plan));
15328
+ }
15329
+ }
15330
+ ), /* @__PURE__ */ import_react5.default.createElement(
14545
15331
  RunLauncher,
14546
15332
  {
14547
15333
  task,
@@ -14550,7 +15336,10 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, loadOff
14550
15336
  ledger: workbench.ledger.value,
14551
15337
  previewRun,
14552
15338
  startRun,
14553
- planningRevision: `${routingSettings}/${dataRevision}/${feedbackRevision}`,
15339
+ planningRevision: `${routingSettings}/${dataRevision}/${feedbackRevision}/${evidenceRevision}`,
15340
+ assignmentMode: effectiveAssignmentMode,
15341
+ packageAssignments: effectiveAssignmentMode === "manual" ? packageAssignments : {},
15342
+ packagePlanKey: currentPackagePlanKey,
14554
15343
  directRoute: mode === "direct" ? routes.find((route) => `${route.provider}/${route.model}` === directKey) ?? routes[0] ?? null : null,
14555
15344
  defaultPreset: settingsScope.getSnapshot().value?.routingPreset ?? "balanced",
14556
15345
  disabledReason: catalogState.status === "loading" ? "\u6B63\u5728\u8BFB\u53D6\u6A21\u578B\u76EE\u5F55\u2026" : catalogState.status === "error" ? "\u6A21\u578B\u76EE\u5F55\u8BFB\u53D6\u5931\u8D25\uFF0C\u8BF7\u5728\u201C\u6A21\u578B\u914D\u7F6E\u201D\u4E2D\u5237\u65B0\u3002" : routes.length === 0 ? "\u8BF7\u5148\u5728\u5B98\u65B9\u201C\u6A21\u578B\u201D\u9875\u914D\u7F6E\u81F3\u5C11\u4E00\u6761\u6A21\u578B\u8DEF\u7EBF\u3002" : "",
@@ -14575,7 +15364,14 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, loadOff
14575
15364
  void workbench.rerun(runId, packageId, override, choice);
14576
15365
  }
14577
15366
  }
14578
- )), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-view", role: "tabpanel", id: "mr-panel-models", "aria-labelledby": "mr-tab-models", hidden: view !== "models" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-view-heading" }, /* @__PURE__ */ import_react5.default.createElement("h2", null, "\u6A21\u578B\u914D\u7F6E"), /* @__PURE__ */ import_react5.default.createElement("p", null, "\u4ECE\u5B98\u65B9\u76EE\u5F55\u9009\u62E9\u8DEF\u7EBF\uFF0C\u8865\u9F50\u6BD4\u8F83\u6240\u9700\u7684\u4EF7\u683C\u3001\u80FD\u529B\u4E0E\u6267\u884C\u65B9\u5F0F\u3002")), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-grid mr-model-grid" }, /* @__PURE__ */ import_react5.default.createElement(ModelProfileEditor, { routes, settingsScope, onSaved: invalidatePlan }), /* @__PURE__ */ import_react5.default.createElement("section", { className: "mr-card", "aria-label": "\u6A21\u578B\u76EE\u5F55" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-card-head" }, /* @__PURE__ */ import_react5.default.createElement("div", null, /* @__PURE__ */ import_react5.default.createElement("h2", { className: "mr-card-title" }, "\u6A21\u578B\u76EE\u5F55"), /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-card-copy" }, routes.length, " \u6761\u8DEF\u7EBF \xB7 \u53EA\u8BFB\u53D6\u5B98\u65B9\u76EE\u5F55\uFF0C\u4E0D\u8BFB\u53D6 API Key\u3002")), /* @__PURE__ */ import_react5.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", disabled: catalogState.status === "loading", onClick: refresh }, "\u5237\u65B0")), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-card-body" }, catalogState.status === "error" && /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-error", role: "alert" }, catalogState.error), catalogState.status === "loading" && /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-empty" }, "\u6B63\u5728\u52A0\u8F7D\u5B98\u65B9\u6A21\u578B\u76EE\u5F55\u2026"), catalogState.status === "ready" && /* @__PURE__ */ import_react5.default.createElement(import_react5.default.Fragment, null, /* @__PURE__ */ import_react5.default.createElement("label", { className: "mr-label", htmlFor: "mr-model-search" }, "\u641C\u7D22\u8DEF\u7EBF"), /* @__PURE__ */ import_react5.default.createElement("input", { className: "mr-input mr-search", id: "mr-model-search", value: query, onChange: (event) => setQuery(event.target.value), placeholder: "\u6A21\u578B\u6216\u4F9B\u5E94\u5546" }), /* @__PURE__ */ import_react5.default.createElement(RouteList, { routes, query }), catalogState.catalog?.failures?.length > 0 && /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-caption" }, catalogState.catalog.failures.length, " \u4E2A\u4F9B\u5E94\u5546\u7684\u76EE\u5F55\u8BFB\u53D6\u5931\u8D25\uFF0C\u8BF7\u5728\u5B98\u65B9\u6A21\u578B\u9875\u68C0\u67E5\u914D\u7F6E\u3002")), /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-caption", style: { marginTop: 13 } }, "\u76EE\u5F55\u767B\u8BB0\u4E0D\u4EE3\u8868\u51ED\u636E\u6216\u7F51\u7EDC\u5F53\u524D\u53EF\u7528\uFF1B\u56FE\u50CF\u80FD\u529B\u9700\u8981\u5728\u5B9E\u9645\u4F7F\u7528\u524D\u6838\u5BF9\u3002"))))), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-view mr-stack", role: "tabpanel", id: "mr-panel-tools", "aria-labelledby": "mr-tab-tools", hidden: view !== "tools" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-view-heading" }, /* @__PURE__ */ import_react5.default.createElement("h2", null, "\u5B98\u65B9\u5DE5\u5177"), /* @__PURE__ */ import_react5.default.createElement("p", null, "\u7BA1\u7406\u672C\u673A CLI\u3001\u68C0\u67E5\u767B\u5F55\u72B6\u6001\uFF0C\u6216\u6253\u5F00\u4EA4\u4E92\u5F0F\u7EC8\u7AEF\u3002")), workbench.health.report && !workbench.health.report.onboarding?.completedAt && /* @__PURE__ */ import_react5.default.createElement(
15367
+ )), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-view", role: "tabpanel", id: "mr-panel-models", "aria-labelledby": "mr-tab-models", hidden: view !== "models" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-view-heading" }, /* @__PURE__ */ import_react5.default.createElement("h2", null, "\u6A21\u578B\u914D\u7F6E"), /* @__PURE__ */ import_react5.default.createElement("p", null, "\u4ECE\u5B98\u65B9\u76EE\u5F55\u9009\u62E9\u8DEF\u7EBF\uFF0C\u8865\u9F50\u6BD4\u8F83\u6240\u9700\u7684\u4EF7\u683C\u3001\u80FD\u529B\u4E0E\u6267\u884C\u65B9\u5F0F\u3002")), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-grid mr-model-grid" }, /* @__PURE__ */ import_react5.default.createElement(ModelProfileEditor, { routes, settingsScope, onSaved: invalidatePlan }), /* @__PURE__ */ import_react5.default.createElement("section", { className: "mr-card", "aria-label": "\u6A21\u578B\u76EE\u5F55" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-card-head" }, /* @__PURE__ */ import_react5.default.createElement("div", null, /* @__PURE__ */ import_react5.default.createElement("h2", { className: "mr-card-title" }, "\u6A21\u578B\u76EE\u5F55"), /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-card-copy" }, routes.length, " \u6761\u8DEF\u7EBF \xB7 \u53EA\u8BFB\u53D6\u5B98\u65B9\u76EE\u5F55\uFF0C\u4E0D\u8BFB\u53D6 API Key\u3002")), /* @__PURE__ */ import_react5.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", disabled: catalogState.status === "loading", onClick: refresh }, "\u5237\u65B0")), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-card-body" }, catalogState.status === "error" && /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-error", role: "alert" }, catalogState.error), catalogState.status === "loading" && /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-empty" }, "\u6B63\u5728\u52A0\u8F7D\u5B98\u65B9\u6A21\u578B\u76EE\u5F55\u2026"), catalogState.status === "ready" && /* @__PURE__ */ import_react5.default.createElement(import_react5.default.Fragment, null, /* @__PURE__ */ import_react5.default.createElement("label", { className: "mr-label", htmlFor: "mr-model-search" }, "\u641C\u7D22\u8DEF\u7EBF"), /* @__PURE__ */ import_react5.default.createElement("input", { className: "mr-input mr-search", id: "mr-model-search", value: query, onChange: (event) => setQuery(event.target.value), placeholder: "\u6A21\u578B\u6216\u4F9B\u5E94\u5546" }), /* @__PURE__ */ import_react5.default.createElement(RouteList, { routes, query }), catalogState.catalog?.failures?.length > 0 && /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-caption" }, catalogState.catalog.failures.length, " \u4E2A\u4F9B\u5E94\u5546\u7684\u76EE\u5F55\u8BFB\u53D6\u5931\u8D25\uFF0C\u8BF7\u5728\u5B98\u65B9\u6A21\u578B\u9875\u68C0\u67E5\u914D\u7F6E\u3002")), /* @__PURE__ */ import_react5.default.createElement("p", { className: "mr-caption", style: { marginTop: 13 } }, "\u76EE\u5F55\u767B\u8BB0\u4E0D\u4EE3\u8868\u51ED\u636E\u6216\u7F51\u7EDC\u5F53\u524D\u53EF\u7528\uFF1B\u56FE\u50CF\u80FD\u529B\u9700\u8981\u5728\u5B9E\u9645\u4F7F\u7528\u524D\u6838\u5BF9\u3002"))))), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-view mr-stack", role: "tabpanel", id: "mr-panel-tools", "aria-labelledby": "mr-tab-tools", hidden: view !== "tools" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-view-heading" }, /* @__PURE__ */ import_react5.default.createElement("h2", null, "\u5B98\u65B9\u5DE5\u5177"), /* @__PURE__ */ import_react5.default.createElement("p", null, "\u7BA1\u7406\u672C\u673A CLI\u3001\u68C0\u67E5\u767B\u5F55\u72B6\u6001\uFF0C\u6216\u6253\u5F00\u4EA4\u4E92\u5F0F\u7EC8\u7AEF\u3002")), terminalApi && /* @__PURE__ */ import_react5.default.createElement(
15368
+ CliTerminalCard,
15369
+ {
15370
+ api: terminalApi,
15371
+ health: workbench.health.report,
15372
+ settingsSignature: toolInstallSettingsSignature(settingsScope?.getSnapshot?.()?.value)
15373
+ }
15374
+ ), workbench.health.report && !workbench.health.report.onboarding?.completedAt && /* @__PURE__ */ import_react5.default.createElement(
14579
15375
  OnboardingBanner,
14580
15376
  {
14581
15377
  health: workbench.health.report,
@@ -14605,13 +15401,6 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, loadOff
14605
15401
  void workbench.refreshHealth(true);
14606
15402
  }
14607
15403
  }
14608
- ), terminalApi && /* @__PURE__ */ import_react5.default.createElement(
14609
- CliTerminalCard,
14610
- {
14611
- api: terminalApi,
14612
- health: workbench.health.report,
14613
- settingsSignature: toolInstallSettingsSignature(settingsScope?.getSnapshot?.()?.value)
14614
- }
14615
15404
  )), /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-view mr-stack", role: "tabpanel", id: "mr-panel-controls", "aria-labelledby": "mr-tab-controls", hidden: view !== "controls" }, /* @__PURE__ */ import_react5.default.createElement("div", { className: "mr-view-heading" }, /* @__PURE__ */ import_react5.default.createElement("h2", null, "\u9884\u7B97\u4E0E\u5B89\u5168"), /* @__PURE__ */ import_react5.default.createElement("p", null, "\u8BBE\u7F6E\u6210\u672C\u4E0E\u8D28\u91CF\u7B56\u7565\uFF0C\u6838\u5BF9\u8BA2\u9605\u8BA1\u8D39\u548C\u6267\u884C\u8FB9\u754C\u3002")), /* @__PURE__ */ import_react5.default.createElement(
14616
15405
  CostControlCard,
14617
15406
  {
@@ -14756,6 +15545,14 @@ function parseRunRequest(value) {
14756
15545
  if (request.executionMode !== void 0 && !["read-only", "workspace-write"].includes(request.executionMode)) throw new TypeError("executionMode is invalid");
14757
15546
  if (request.executionEnvironment !== void 0 && !["harness", "cli-native"].includes(request.executionEnvironment)) throw new TypeError("executionEnvironment is invalid");
14758
15547
  const officialTeam = request.executor === "official-team";
15548
+ if (request.assignmentMode !== void 0 && !["algorithm", "manual"].includes(request.assignmentMode)) throw new TypeError("assignmentMode is invalid");
15549
+ const manual = request.assignmentMode === "manual";
15550
+ if (manual && (officialTeam || provider || model)) throw new TypeError("manual assignments require routed work packages without an explicit global route");
15551
+ if (!manual && (request.packageAssignments !== void 0 || request.packagePlanKey !== void 0)) throw new TypeError("package assignments require manual assignmentMode");
15552
+ const packageAssignments = manual ? parsePackageAssignments(request.packageAssignments) : void 0;
15553
+ if (manual && (!packageAssignments || typeof request.packagePlanKey !== "string" || request.packagePlanKey.length > 200 || !request.packagePlanKey)) {
15554
+ throw new TypeError("manual assignments require packageAssignments and a current packagePlanKey");
15555
+ }
14759
15556
  if (officialTeam && (request.planMode !== "team" || provider || model)) throw new TypeError("official-team requires a team plan without an explicit route");
14760
15557
  if (!officialTeam && request.executionMode === "workspace-write") throw new TypeError("workspace-write requires official-team");
14761
15558
  if (request.executionEnvironment === "cli-native" && (!officialTeam || request.executionMode !== "workspace-write")) {
@@ -14772,6 +15569,7 @@ function parseRunRequest(value) {
14772
15569
  ...request.executionEnvironment === void 0 ? {} : { executionEnvironment: request.executionEnvironment }
14773
15570
  } : {},
14774
15571
  ...provider ? { provider, model } : {},
15572
+ ...manual ? { assignmentMode: "manual", packageAssignments, packagePlanKey: request.packagePlanKey } : {},
14775
15573
  ...["single", "team"].includes(request.planMode) ? { planMode: request.planMode } : {},
14776
15574
  ...["economy", "balanced", "quality"].includes(request.preset) ? { preset: request.preset } : {},
14777
15575
  ...request.budgetUsd !== void 0 ? { budgetUsd: request.budgetUsd } : {},