@ljwei-stak/dsh-model-router 0.21.1 → 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.
@@ -1052,51 +1052,118 @@ function liveBenchRow(snapshot, model) {
1052
1052
  // .dsh-plugin/shared/assignment-solver.mjs
1053
1053
  var EPSILON = 1e-12;
1054
1054
  var compareText = (a, b2) => a < b2 ? -1 : a > b2 ? 1 : 0;
1055
- 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;
1056
1061
  function compareStates(left, right, minimizeCost = false) {
1057
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));
1058
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));
1059
1077
  function solveCandidateAssignments({ tasks = [], pools = [], budget = Infinity, beamWidth = 256, exactLimit = 4096, handoffPenalty = 0.015, minimizeCost = false } = {}) {
1060
1078
  if (!tasks.length || tasks.length !== pools.length) return null;
1079
+ const taskCount = tasks.length;
1080
+ const taskIndexById = /* @__PURE__ */ new Map();
1061
1081
  const preceding = /* @__PURE__ */ new Set();
1062
- for (const task of tasks) {
1063
- 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))) {
1064
1085
  throw new RangeError("Assignment tasks must have unique ids and known dependencies in topological order");
1065
1086
  }
1066
1087
  preceding.add(task.id);
1088
+ taskIndexById.set(task.id, index);
1067
1089
  }
1068
1090
  const constrained = Number.isFinite(budget);
1069
- 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)));
1070
1092
  if (options.some((pool) => pool.length === 0)) return null;
1071
- 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);
1072
1105
  let searchSpace = 1;
1073
1106
  let searchSpaceCapped = false;
1074
- for (let index = tasks.length - 1; index >= 0; index--) {
1075
- 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;
1076
1123
  const product = searchSpace * options[index].length;
1077
1124
  if (product > Number.MAX_SAFE_INTEGER) searchSpaceCapped = true;
1078
1125
  searchSpace = Math.min(Number.MAX_SAFE_INTEGER, product);
1079
1126
  }
1080
1127
  if (constrained && suffixMinimum[0] > budget + EPSILON) return null;
1081
- const exact = !searchSpaceCapped && searchSpace <= Math.max(1, exactLimit);
1082
- const width = Math.max(1, Math.floor(beamWidth) || 256);
1083
- 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;
1084
1150
  let states = [{ choices: [], routesByTask: /* @__PURE__ */ new Map(), score: 0, cost: 0, switches: 0, relaxedCount: 0, qualityShortfall: 0 }];
1085
- for (let index = 0; index < tasks.length; index++) {
1086
- const task = tasks[index];
1151
+ for (let index = 0; index < taskCount; index++) {
1087
1152
  const expanded = [];
1088
1153
  for (const state of states) {
1089
- for (const option of options[index]) {
1154
+ for (let optionIndex = 0; optionIndex < options[index].length; optionIndex++) {
1155
+ const option = options[index][optionIndex];
1090
1156
  const cost = state.cost + option.cost;
1091
1157
  if (constrained && cost + suffixMinimum[index + 1] > budget + EPSILON) continue;
1092
- const switches = (task.dependsOn ?? []).reduce((count, dependency) => {
1093
- const route = state.routesByTask.get(dependency);
1094
- return count + (route !== void 0 && route !== option.route ? 1 : 0);
1095
- }, 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
+ }
1096
1163
  const penalty = switches * handoffPenalty;
1097
1164
  const shortfall = Math.max(0, Number(option.qualityShortfall) || 0);
1098
1165
  const routesByTask2 = new Map(state.routesByTask);
1099
- routesByTask2.set(task.id, option.route);
1166
+ routesByTask2.set(tasks[index].id, handoffRoutes[index][optionIndex]);
1100
1167
  expanded.push({
1101
1168
  choices: [...state.choices, { ...option, handoffPenalty: penalty }],
1102
1169
  routesByTask: routesByTask2,
@@ -1110,22 +1177,404 @@ function solveCandidateAssignments({ tasks = [], pools = [], budget = Infinity,
1110
1177
  }
1111
1178
  }
1112
1179
  if (!expanded.length) return null;
1113
- expanded.sort((left, right) => compareStates(left, right, minimizeCost));
1114
- if (!exact && expanded.length > width) {
1115
- states = expanded.slice(0, width);
1116
- if (constrained && index < tasks.length - 1) {
1117
- const cheapest = expanded.reduce((best2, state) => compareStates(state, best2, true) < 0 ? state : best2);
1118
- if (!states.includes(cheapest)) states[width - 1] = cheapest;
1119
- }
1120
- search.beamPruned += expanded.length - states.length;
1121
- } 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;
1122
1279
  }
1123
1280
  states.sort((left, right) => compareStates(left, right, minimizeCost));
1281
+ search.retainedStates = states.length;
1124
1282
  const best = states[0];
1125
1283
  const { routesByTask, ...result } = best;
1126
1284
  return { ...result, minimumCostLowerBound: suffixMinimum[0], search };
1127
1285
  }
1128
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
+
1129
1578
  // .dsh-plugin/shared/router.mjs
1130
1579
  var OBJECTIVE_WEIGHTS = Object.freeze({
1131
1580
  simple: Object.freeze({ quality: 0.28, cost: 0.45, latency: 0.14, specialty: 0.04, reasoning: 0.07, risk: 0.02 }),
@@ -1184,7 +1633,7 @@ var OPENCODE_CATALOG_PROVIDERS = Object.freeze([
1184
1633
  ]);
1185
1634
  function classifyTask(text7) {
1186
1635
  const value = String(text7 ?? "");
1187
- 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";
1188
1637
  return detectTaskTypes(value)[0] ?? "general";
1189
1638
  }
1190
1639
  var TASK_TYPE_RULES = Object.freeze([
@@ -1235,10 +1684,12 @@ function specialtyMatch(model, taskType, liveScores = {}) {
1235
1684
  return 0.38;
1236
1685
  }
1237
1686
  function qualityForTask(row, taskType) {
1687
+ if (row?.queryEvidence?.type === taskType) return row.queryEvidence.quality;
1238
1688
  const base = asScore(row?.liveScores?.[taskType]) ?? asScore(row?.liveOverall) ?? row?.metadata?.quality ?? row?.baseQuality ?? row?.quality ?? 0;
1239
1689
  return row?.qualityBias ? clamp(base + row.qualityBias) : base;
1240
1690
  }
1241
1691
  function qualitySourceForTask(row, taskType) {
1692
+ if (row?.queryEvidence?.type === taskType) return "recorded-query-evidence";
1242
1693
  return asScore(row?.liveScores?.[taskType]) !== void 0 || asScore(row?.liveOverall) !== void 0 ? "livebench" : row?.fallbackQualitySource ?? row?.qualitySource ?? "unknown";
1243
1694
  }
1244
1695
  function specialtyForTask(row, taskType) {
@@ -1394,9 +1845,9 @@ function explicitRequirements(text7) {
1394
1845
  const marker = /^(?:[-*•]\s+|\d{1,2}[.)、](?!\d)\s*|[一二三四五六七八九十]{1,3}[、.)]\s*)(.+)$/u;
1395
1846
  const marked = lines.map((line) => marker.exec(line)?.[1]).filter(Boolean).map(requirementText).filter(looksLikeRequirement);
1396
1847
  if (marked.length >= 2) return marked;
1397
- const plain = lines.filter((line) => !/^#{1,6}\s|^```|[::]$/u.test(line));
1398
- if (plain.length >= 2 && plain.every(looksLikeRequirement)) {
1399
- 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);
1400
1851
  }
1401
1852
  const clauses = value.split(/[;;,,]/u).map(requirementText).filter(Boolean);
1402
1853
  if (clauses.length >= 2 && clauses.every(looksLikeRequirement)) {
@@ -1539,7 +1990,7 @@ function compareText2(left, right) {
1539
1990
  return a < b2 ? -1 : a > b2 ? 1 : 0;
1540
1991
  }
1541
1992
  function compareRowsStable(left, right) {
1542
- 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 ?? ""));
1543
1994
  }
1544
1995
  function reasoningEffortRank(effort) {
1545
1996
  const normalized2 = String(effort ?? "").trim().toLowerCase().replace(/[\s_-]+/g, "");
@@ -1565,6 +2016,13 @@ function selectReasoningEffort(efforts, preferred = "medium") {
1565
2016
  }
1566
2017
  function reasoningDecision(row, task) {
1567
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
+ };
1568
2026
  const efforts = Array.isArray(row.reasoningEfforts) ? row.reasoningEfforts : [];
1569
2027
  if (efforts.length === 0) {
1570
2028
  const knownUnsupported = row.reasoningKnown === true;
@@ -1577,7 +2035,7 @@ function reasoningDecision(row, task) {
1577
2035
  };
1578
2036
  }
1579
2037
  const preferredRank = reasoningEffortRank(preferred);
1580
- const chosen = selectReasoningEffort(efforts, preferred);
2038
+ const chosen = Object.hasOwn(row, "actionEffort") ? row.actionEffort : selectReasoningEffort(efforts, preferred);
1581
2039
  const distance = Math.abs(reasoningEffortRank(chosen) - preferredRank);
1582
2040
  return {
1583
2041
  reasoningEffort: chosen,
@@ -1595,16 +2053,88 @@ function candidateUtility(row, task, weights, cacheReadRatio = 0, cacheWriteRati
1595
2053
  const reasoning = reasoningDecision(row, task);
1596
2054
  const reference = taskCost({ pricing: COST_REFERENCE_PRICING, reasoningKnown: true, reasoningEfforts: [] }, task, text7, complexity, cacheReadRatio, cacheWriteRatio);
1597
2055
  const cost = row.pricing === null ? 0 : 1 / (1 + taskCost(row, task, text7, complexity, cacheReadRatio, cacheWriteRatio) / Math.max(reference, 1e-12));
1598
- 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;
1599
2059
  return { score, floor, qualityGap, preferenceAdjustment, ...reasoning };
1600
2060
  }
1601
2061
  function taskCost(row, task, text7, complexity, cacheReadRatio = 0, cacheWriteRatio = 0) {
1602
2062
  if (row?.pricing === null) return 0;
1603
- const decision = row === null ? null : reasoningDecision(row, task);
1604
- const tokens = taskTokenBudget(text7, { ...task, reasoningEffort: decision?.reasoningEffort }, complexity, cacheReadRatio, cacheWriteRatio);
2063
+ const tokens = resourceTokenBudget(row, task, text7, complexity, cacheReadRatio, cacheWriteRatio);
1605
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;
1606
2065
  }
1607
- 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) {
1608
2138
  if (left.pricing === null || right.pricing === null) return false;
1609
2139
  const leftReasoning = reasoningDecision(left, task);
1610
2140
  const rightReasoning = reasoningDecision(right, task);
@@ -1629,7 +2159,8 @@ function dominates(left, right, task, text7, complexity, cacheReadRatio, cacheWr
1629
2159
  return noWorse && strictlyBetter;
1630
2160
  }
1631
2161
  function eligibleRowsForTask(rows, task) {
1632
- 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));
1633
2164
  }
1634
2165
  function candidatePool(rows, task, weights, text7, complexity, cacheReadRatio, cacheWriteRatio, dependencySensitive = false) {
1635
2166
  const eligibleRows = eligibleRowsForTask(rows, task);
@@ -1642,7 +2173,7 @@ function candidatePool(rows, task, weights, text7, complexity, cacheReadRatio, c
1642
2173
  decision: candidateUtility(row, task, taskWeights, cacheReadRatio, cacheWriteRatio, text7, complexity),
1643
2174
  cost: taskCost(row, task, text7, complexity, cacheReadRatio, cacheWriteRatio)
1644
2175
  }));
1645
- 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)));
1646
2177
  const essential = [
1647
2178
  scored.filter((item) => item.row.pricing !== null).sort((left, right) => left.cost - right.cost || compareRowsStable(left.row, right.row))[0],
1648
2179
  scored.slice().sort((left, right) => right.decision.score - left.decision.score || compareRowsStable(left.row, right.row))[0],
@@ -1667,6 +2198,8 @@ function solveAssignments({ rows, tasks, weights, text: text7, complexity, budge
1667
2198
  route: routeKey(option.row.provider, option.row.model),
1668
2199
  score: option.decision.score,
1669
2200
  cost: option.cost,
2201
+ actionId: option.row.actionId,
2202
+ handoffRoute: routeKey(option.row.provider, option.row.model),
1670
2203
  qualityShortfall: Math.max(0, option.decision.floor - qualityForTask(option.row, tasks[index].type)),
1671
2204
  pricingKnown: option.row.pricing !== null,
1672
2205
  payload: option
@@ -1676,7 +2209,7 @@ function solveAssignments({ rows, tasks, weights, text: text7, complexity, budge
1676
2209
  ...result,
1677
2210
  assignments: result.choices.map((choice, index) => ({
1678
2211
  task: tasks[index],
1679
- row: choice.payload.row,
2212
+ row: { ...choice.payload.row, estimatedCost: choice.payload.row.pricing === null ? null : choice.cost },
1680
2213
  decision: { ...choice.payload.decision, relaxed: choice.qualityShortfall > 0 },
1681
2214
  estimatedCost: choice.cost,
1682
2215
  handoffPenalty: choice.handoffPenalty
@@ -1686,7 +2219,7 @@ function solveAssignments({ rows, tasks, weights, text: text7, complexity, budge
1686
2219
  minimumFeasibleCost: result.minimumCostLowerBound
1687
2220
  };
1688
2221
  }
1689
- 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 } = {}) {
1690
2223
  const presetId = normalizeRoutingPreset(preset);
1691
2224
  const firstLine = String(text7 ?? "").split(/\r?\n/u)[0].trim();
1692
2225
  const transformOnly = /^(?:请|帮我)?(?:总结|概括|翻译|摘要|解释)(?:以下|下列|下面|这份|这些)/u.test(firstLine) && !/(?:执行|完成|实施|分配)/u.test(firstLine);
@@ -1702,6 +2235,8 @@ function buildPlan({ text: text7 = "", available = [], mode = "collective", pric
1702
2235
  return {
1703
2236
  provider: String(entry.provider ?? ""),
1704
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,
1705
2240
  benchmarkModel: typeof entry.benchmarkModel === "string" ? entry.benchmarkModel : void 0,
1706
2241
  reasoningEfforts: [...new Set(reasoningEfforts)],
1707
2242
  defaultReasoningEffort: entry.defaultReasoningEffort === void 0 ? void 0 : String(entry.defaultReasoningEffort),
@@ -1764,6 +2299,13 @@ function buildPlan({ text: text7 = "", available = [], mode = "collective", pric
1764
2299
  reasoningEfforts: route.reasoningEfforts,
1765
2300
  defaultReasoningEffort: route.defaultReasoningEffort,
1766
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(),
1767
2309
  inputModalities: route.inputModalities,
1768
2310
  pricing: pricingRow,
1769
2311
  score: 0,
@@ -1840,13 +2382,14 @@ function buildPlan({ text: text7 = "", available = [], mode = "collective", pric
1840
2382
  recommendedReasoningEffort: decision?.reasoningEffort,
1841
2383
  preferredReasoningEffort: decision?.preferredReasoningEffort ?? task.preferredReasoningEffort,
1842
2384
  reasoningFit: Number(Number(decision?.reasoningFit ?? 0).toFixed(3)),
2385
+ ...decision?.reasoningExecutionNotice ? { reasoningExecutionNotice: decision.reasoningExecutionNotice } : {},
1843
2386
  purpose: task.purpose,
1844
2387
  criticality: task.criticality,
1845
2388
  qualityFloor: Number(task.qualityFloor.toFixed(3)),
1846
2389
  dependsOn: [...task.dependsOn ?? []]
1847
2390
  }));
1848
2391
  const costBreakdown = assignments.map(({ task, row, decision, estimatedCost, handoffPenalty }, index) => {
1849
- const tokens = taskTokenBudget(text7, { ...task, reasoningEffort: decision?.reasoningEffort }, complexity.band, cacheReadRatio, cacheWriteRatio);
2392
+ const tokens = resourceTokenBudget(row, task, text7, complexity.band, cacheReadRatio, cacheWriteRatio);
1850
2393
  const taskEstimate = estimatedCost ?? taskCost(row, task, text7, complexity.band, cacheReadRatio, cacheWriteRatio);
1851
2394
  return {
1852
2395
  stage: index + 1,
@@ -1862,6 +2405,11 @@ function buildPlan({ text: text7 = "", available = [], mode = "collective", pric
1862
2405
  cacheReadTokens: tokens.cacheReadTokens,
1863
2406
  cacheWriteTokens: tokens.cacheWriteTokens,
1864
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 } : {},
1865
2413
  estimatedCost: row?.pricing === null || row === null ? null : Number(taskEstimate.toFixed(6)),
1866
2414
  quality: qualitySourceForTask(row, task.type) === "unknown" || row === null ? null : Number(qualityForTask(row, task.type).toFixed(3)),
1867
2415
  qualitySource: qualitySourceForTask(row, task.type),
@@ -1876,7 +2424,8 @@ function buildPlan({ text: text7 = "", available = [], mode = "collective", pric
1876
2424
  return { task, strongest };
1877
2425
  });
1878
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;
1879
- 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)));
1880
2429
  const budgetExceeded = Number(budgetUsd) > 0 && totalEstimate !== null ? totalEstimate > Number(budgetUsd) : null;
1881
2430
  const savings = baselineCost === null || totalEstimate === null || !qualityEvidenceComplete ? null : baselineCost <= 0 ? 0 : clamp((baselineCost - totalEstimate) / baselineCost);
1882
2431
  const paretoPruned = (optimized?.candidatePools ?? []).reduce((sum, pool) => sum + pool.pruned, 0);
@@ -1891,7 +2440,13 @@ function buildPlan({ text: text7 = "", available = [], mode = "collective", pric
1891
2440
  taskType,
1892
2441
  taskTypes: [...new Set(taskNodes.map((task) => task.type).filter((type) => type !== "reasoning"))],
1893
2442
  objectiveWeights: weights,
1894
- 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) => {
1895
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);
1896
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 };
1897
2452
  }),
@@ -1903,6 +2458,11 @@ function buildPlan({ text: text7 = "", available = [], mode = "collective", pric
1903
2458
  costBreakdown,
1904
2459
  optimization: {
1905
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",
1906
2466
  personalization: learning ? {
1907
2467
  policyVersion: learning.policyVersion,
1908
2468
  revision: learning.revision,
@@ -2015,12 +2575,14 @@ function createPlanFromRoutes(task, availableRoutes, {
2015
2575
  liveBenchError = "",
2016
2576
  dataVersions = null,
2017
2577
  learning = null,
2578
+ routingEvidence = null,
2018
2579
  cacheReadRatio = 0,
2019
2580
  cacheWriteRatio = 0,
2020
2581
  directProvider = "",
2021
2582
  directModel = "",
2022
2583
  preset = "balanced",
2023
- loggedOutToolIds = []
2584
+ loggedOutToolIds = [],
2585
+ forceOfficialCli = false
2024
2586
  } = {}) {
2025
2587
  const taskText = clean2(task);
2026
2588
  if (!taskText) throw new Error("task must contain text");
@@ -2042,13 +2604,28 @@ function createPlanFromRoutes(task, availableRoutes, {
2042
2604
  const channelOf = (provider, model) => {
2043
2605
  const route = routes.find((item) => item.provider === provider && item.model === model);
2044
2606
  const key = `${String(provider ?? "")}\0${String(model ?? "")}\0${route?.execution ?? ""}`;
2045
- 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
+ }
2046
2616
  return channelCache.get(key);
2047
2617
  };
2048
2618
  const needsImage = detectTaskTypes(taskText).includes("vision");
2049
2619
  const plan = buildPlan({
2050
2620
  text: taskText,
2051
- 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
+ }),
2052
2629
  mode: directRoute ? "single" : selectedMode,
2053
2630
  budgetUsd: Math.max(0, Number.isFinite(budgetUsd) ? budgetUsd : 0),
2054
2631
  pricing,
@@ -2056,6 +2633,7 @@ function createPlanFromRoutes(task, availableRoutes, {
2056
2633
  liveBenchError,
2057
2634
  dataVersions,
2058
2635
  learning,
2636
+ routingEvidence,
2059
2637
  cacheReadRatio,
2060
2638
  cacheWriteRatio,
2061
2639
  preset
@@ -2093,6 +2671,7 @@ function createPlanFromRoutes(task, availableRoutes, {
2093
2671
  recommendedModel: item.recommended,
2094
2672
  estimatedCost: plan.costBreakdown[index]?.estimatedCost ?? null,
2095
2673
  ...item.recommendedReasoningEffort ? { recommendedReasoningEffort: item.recommendedReasoningEffort } : {},
2674
+ ...item.reasoningExecutionNotice ? { reasoningExecutionNotice: item.reasoningExecutionNotice } : {},
2096
2675
  ...annotate(channelOf(item.recommendedProvider, item.recommended)),
2097
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"]
2098
2677
  }))
@@ -2100,13 +2679,68 @@ function createPlanFromRoutes(task, availableRoutes, {
2100
2679
  };
2101
2680
  }
2102
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
+
2103
2737
  // .dsh-plugin/shared/run-ledger.mjs
2104
- var finite = (value) => typeof value === "number" && Number.isFinite(value);
2738
+ var finite2 = (value) => typeof value === "number" && Number.isFinite(value);
2105
2739
  var routeKey2 = (provider, model) => `${String(provider ?? "")}\0${String(model ?? "")}`;
2106
2740
  var MAX_SPENDING_DAYS = 90;
2107
2741
  var MAX_SPENDING_MONTHS = 24;
2108
2742
  function periodSpending(value) {
2109
- 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;
2110
2744
  return {
2111
2745
  costUsd: amount("costUsd"),
2112
2746
  unknown: Math.floor(amount("unknown")),
@@ -2126,9 +2760,9 @@ function formatUsd(value) {
2126
2760
  }
2127
2761
  function budgetCheck({ estimateUsd = null, spent = { today: 0, month: 0 }, dailyLimitUsd = 0, monthlyLimitUsd = 0 } = {}) {
2128
2762
  const limits = [];
2129
- if (finite(dailyLimitUsd) && dailyLimitUsd > 0) limits.push({ period: "daily", label: "\u4ECA\u65E5", limit: dailyLimitUsd, spent: spent.today ?? 0 });
2130
- if (finite(monthlyLimitUsd) && monthlyLimitUsd > 0) limits.push({ period: "monthly", label: "\u672C\u6708", limit: monthlyLimitUsd, spent: spent.month ?? 0 });
2131
- 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);
2132
2766
  const remaining = limits.length ? Math.max(0, Math.min(...limits.map((item) => item.limit - item.spent))) : null;
2133
2767
  const exceeded = limits.find((item) => item.spent >= item.limit || estimateKnown && item.spent + estimateUsd > item.limit) ?? null;
2134
2768
  return {
@@ -2144,12 +2778,12 @@ function applyQualityBiases(routes, biases) {
2144
2778
  if (!biases || typeof biases !== "object") return routes;
2145
2779
  return (Array.isArray(routes) ? routes : []).map((route) => {
2146
2780
  const bias = biases[routeKey2(route.provider, route.model)];
2147
- return finite(bias) && bias !== 0 ? { ...route, qualityBias: bias } : route;
2781
+ return finite2(bias) && bias !== 0 ? { ...route, qualityBias: bias } : route;
2148
2782
  });
2149
2783
  }
2150
2784
 
2151
2785
  // .dsh-plugin/shared/adaptive-feedback.mjs
2152
- var finite2 = (value) => typeof value === "number" && Number.isFinite(value);
2786
+ var finite3 = (value) => typeof value === "number" && Number.isFinite(value);
2153
2787
  var clean3 = (value) => typeof value === "string" ? value.trim() : "";
2154
2788
  var routeKey3 = (provider, model) => `${provider}\0${model}`;
2155
2789
  var record = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
@@ -2168,8 +2802,8 @@ function applyFeedbackProfile(routes, profile) {
2168
2802
  if (!Object.hasOwn(profile.adjustments, key)) return base;
2169
2803
  const value = profile.adjustments[key];
2170
2804
  if (!record(value)) return base;
2171
- const limit = finite2(profile.maxAdjustment) && profile.maxAdjustment >= 0 && profile.maxAdjustment <= 0.1 ? profile.maxAdjustment : 0.1;
2172
- 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));
2173
2807
  if (!Object.keys(adjustments).length) return base;
2174
2808
  return {
2175
2809
  ...base,
@@ -2807,7 +3441,59 @@ function billingSwitchText(item) {
2807
3441
  if (!note?.reason) return null;
2808
3442
  return note.reason;
2809
3443
  }
2810
- 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
+ }
2811
3497
  if (!["routed", "official-team"].includes(executor)) throw new Error("\u4E0D\u652F\u6301\u7684\u6267\u884C\u65B9\u5F0F\u3002");
2812
3498
  if (!["read-only", "workspace-write"].includes(executionMode)) throw new Error("\u4E0D\u652F\u6301\u7684\u4EFB\u52A1\u6743\u9650\u3002");
2813
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");
@@ -2820,7 +3506,8 @@ function launchRequest({ task, mode, directRoute, budgetUsd, preset, workspace,
2820
3506
  ...executionEnvironment === "cli-native" ? { executionEnvironment } : {},
2821
3507
  ...mode === "direct" && directRoute ? { provider: directRoute.provider, model: directRoute.model } : { planMode: mode === "team" ? "team" : "single", preset },
2822
3508
  ...Number.isFinite(budget) && budget >= 0 ? { budgetUsd: budget } : {},
2823
- ...typeof workspace === "string" && workspace.trim() ? { workspace: workspace.trim() } : {}
3509
+ ...typeof workspace === "string" && workspace.trim() ? { workspace: workspace.trim() } : {},
3510
+ ...manual ?? {}
2824
3511
  };
2825
3512
  }
2826
3513
  function rerunConfirmations({ run, item, override = null, choice = null, ledger = null, health = null, toolFor = () => null, headlessIds = /* @__PURE__ */ new Set() } = {}) {
@@ -2850,6 +3537,10 @@ function rerunConfirmations({ run, item, override = null, choice = null, ledger
2850
3537
 
2851
3538
  // .dsh-plugin/client/adaptive-state.mjs
2852
3539
  var ADAPTIVE_DEFAULTS = Object.freeze({
3540
+ routingEvidenceJson: "",
3541
+ resourceLearningEnabled: true,
3542
+ qualityEscalationEnabled: false,
3543
+ qualityGateJson: "",
2853
3544
  feedbackLearningEnabled: true,
2854
3545
  feedbackHalfLifeDays: 30,
2855
3546
  feedbackPriorWeight: 3,
@@ -3271,7 +3962,7 @@ function RunNode({ run, item, routes, allowReassign, busy, onRate, onRerun }) {
3271
3962
  const rerun = rerunSupport(run);
3272
3963
  const canRerun = rerun.supported && !item.ok && !busy;
3273
3964
  const others = routes.filter((route) => `${route.provider}/${route.model}` !== `${item.provider}/${item.model}`);
3274
- 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: () => {
3275
3966
  const [provider, model] = target.split("\0");
3276
3967
  onRerun(run.id, item.id, { provider, model });
3277
3968
  } }, "\u6539\u6D3E\u5E76\u91CD\u8DD1"))));
@@ -3326,18 +4017,23 @@ var STATUS_TEXT = Object.freeze({
3326
4017
  "paused-budget": "\u5DF2\u56E0\u9884\u7B97\u6682\u505C\uFF0C\u672A\u542F\u52A8\u6A21\u578B",
3327
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"
3328
4019
  });
3329
- 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 }) {
3330
4021
  const [preset, setPreset] = import_react3.default.useState(defaultPreset || "balanced");
3331
4022
  const [executor, setExecutor] = import_react3.default.useState("routed");
3332
4023
  const [executionMode, setExecutionMode] = import_react3.default.useState("read-only");
3333
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;
3334
4030
  const lastWorkspace = [...ledger?.runs ?? []].map((run) => run.workspace).find(Boolean) ?? "";
3335
4031
  const [workspace, setWorkspace] = import_react3.default.useState("");
3336
4032
  const [phase, setPhase] = import_react3.default.useState({ kind: "idle" });
3337
4033
  const mounted = import_react3.default.useRef(true);
3338
4034
  const confirmRef = import_react3.default.useRef(null);
3339
4035
  const pendingRequest = import_react3.default.useRef(0);
3340
- 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]);
3341
4037
  const latestSignature = import_react3.default.useRef(inputSignature);
3342
4038
  latestSignature.current = inputSignature;
3343
4039
  import_react3.default.useEffect(() => {
@@ -3351,12 +4047,12 @@ function RunLauncher({ task, mode, directRoute, budgetUsd, defaultPreset, ledger
3351
4047
  setPreset(defaultPreset || "balanced");
3352
4048
  }, [defaultPreset]);
3353
4049
  import_react3.default.useEffect(() => {
3354
- if (mode !== "team") {
4050
+ if (mode !== "team" || manual) {
3355
4051
  setExecutor("routed");
3356
4052
  setExecutionMode("read-only");
3357
4053
  setExecutionEnvironment("harness");
3358
4054
  }
3359
- }, [mode]);
4055
+ }, [mode, manual]);
3360
4056
  import_react3.default.useEffect(() => {
3361
4057
  pendingRequest.current += 1;
3362
4058
  setPhase((previous) => ["previewing", "preview", "error"].includes(previous.kind) ? { kind: "idle" } : previous);
@@ -3364,16 +4060,35 @@ function RunLauncher({ task, mode, directRoute, budgetUsd, defaultPreset, ledger
3364
4060
  import_react3.default.useEffect(() => {
3365
4061
  if (phase.kind === "preview") confirmRef.current?.focus();
3366
4062
  }, [phase.kind]);
3367
- 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
+ });
3368
4077
  const unavailable = typeof previewRun !== "function" || typeof startRun !== "function";
3369
- 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" : "");
3370
4079
  const busy = phase.kind === "previewing" || phase.kind === "running";
3371
4080
  const recordedRun = phase.kind === "done" && phase.value.runId ? (ledger?.runs ?? []).find((run) => run.id === phase.value.runId) : null;
3372
4081
  const resultStatus = recordedRun?.status ?? phase.value?.status;
3373
4082
  const preview = async () => {
3374
4083
  if (blocked || busy) return;
3375
4084
  const current = ++pendingRequest.current;
3376
- 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
+ }
3377
4092
  const signature2 = inputSignature;
3378
4093
  setPhase({ kind: "previewing" });
3379
4094
  try {
@@ -3402,16 +4117,16 @@ function RunLauncher({ task, mode, directRoute, budgetUsd, defaultPreset, ledger
3402
4117
  if (mounted.current) setPhase({ kind: "error", message: text2(error?.message) || "\u6267\u884C\u5931\u8D25\u3002" });
3403
4118
  }
3404
4119
  };
3405
- 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: () => {
3406
4121
  setExecutor("routed");
3407
4122
  setExecutionMode("read-only");
3408
4123
  setExecutionEnvironment("harness");
3409
- } }, "\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: () => {
3410
- if (mode === "team") setExecutor("official-team");
3411
- } }, "\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: () => {
3412
4127
  setExecutionMode("read-only");
3413
4128
  setExecutionEnvironment("harness");
3414
- } }, "\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: () => {
3415
4130
  void preview();
3416
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(
3417
4132
  LaunchPreview,
@@ -3436,8 +4151,12 @@ function LaunchPreview({ value, request, changed, running, confirmRef, onConfirm
3436
4151
  const native = (value.executionEnvironment ?? request?.executionEnvironment) === "cli-native";
3437
4152
  const readOnly = value.readOnly !== false && (value.executionMode ?? request?.executionMode) !== "workspace-write";
3438
4153
  const blocked = value.status === "blocked";
4154
+ const manual = request?.assignmentMode === "manual" || value.assignmentMode === "manual";
3439
4155
  const blocking = [...new Set([value.reason, ...Array.isArray(value.blocking) ? value.blocking : [value.blocking]].map(reasonText).filter(Boolean))];
3440
- 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")));
3441
4160
  }
3442
4161
 
3443
4162
  // .dsh-plugin/client/cli-terminal.jsx
@@ -12870,7 +13589,7 @@ var xterm_default = `/**
12870
13589
  `;
12871
13590
 
12872
13591
  // .dsh-plugin/client/host-version.mjs
12873
- var ROUTER_CLIENT_VERSION = true ? "0.21.1" : "";
13592
+ var ROUTER_CLIENT_VERSION = true ? "0.22.0" : "";
12874
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";
12875
13594
  function isMissingRemoteMethod(message) {
12876
13595
  const value = String(message ?? "");
@@ -13481,6 +14200,11 @@ var router_main_default = `.mr-workspace {
13481
14200
  .mr-package-name { font-size: 12px; font-weight: 750; }
13482
14201
  .mr-package-route { color: var(--mr-accent); font-size: 11px; font-weight: 700; }
13483
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; }
13484
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; }
13485
14209
  .mr-notice code { font-family: ui-monospace, SFMono-Regular, Consolas, monospace; font-size: 11px; }
13486
14210
  @media (max-width: 850px) { .mr-grid { grid-template-columns: 1fr; } .mr-shell { padding: 24px 18px 48px; } }
@@ -13704,6 +14428,7 @@ var text5 = (value) => typeof value === "string" ? value.trim() : "";
13704
14428
  var routingSettingsSignature = (value) => JSON.stringify([
13705
14429
  value.modelProfilesJson,
13706
14430
  value.routingPreset,
14431
+ value.allowManualReassign,
13707
14432
  ...Object.keys(ADAPTIVE_DEFAULTS).map((key) => value[key] ?? ADAPTIVE_DEFAULTS[key])
13708
14433
  ]);
13709
14434
  function RouterPanelIcon({ size = 20, active = false }) {
@@ -13722,12 +14447,25 @@ function ChannelBadge({ item }) {
13722
14447
  const official = item.executionChannel === "official-cli";
13723
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");
13724
14449
  }
13725
- function PlanResults({ plan, ledger, headingRef }) {
14450
+ function PlanResults({ plan, ledger, headingRef, assignmentMode = "algorithm", packageAssignments = {}, assignmentOptions = [], manualAllowed = true, onAssignmentModeChange, onPackageAssignmentChange, onRestoreAssignments }) {
13726
14451
  const selected = plan.selected;
13727
14452
  const budget = planBudget(ledger, plan.estimatedCost);
13728
14453
  const packageNames = new Map((plan.team?.workPackages ?? []).map((item) => [item.id, item.name]));
13729
14454
  const purposeLabel = { analysis: "\u4EFB\u52A1\u5206\u6790", execution: "\u4EFB\u52A1\u5B9E\u65BD", verification: "\u72EC\u7ACB\u9A8C\u8BC1", synthesis: "\u7ED3\u679C\u6574\u5408" };
13730
- 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)));
13731
14469
  }
13732
14470
  var remoteError = (response, fallback) => remoteErrorText(
13733
14471
  text5(response?.error?.message) || text5(response?.value?.error) || (typeof response?.error === "string" ? response.error : ""),
@@ -14385,6 +15123,8 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, loadOff
14385
15123
  const [budget, setBudget] = import_react5.default.useState(() => String(settingsScope.getSnapshot().value?.budgetUsd ?? 0));
14386
15124
  const [query, setQuery] = import_react5.default.useState("");
14387
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({});
14388
15128
  const [planError, setPlanError] = import_react5.default.useState("");
14389
15129
  const [toolProbes, setToolProbes] = import_react5.default.useState(null);
14390
15130
  const [routingSettings, setRoutingSettings] = import_react5.default.useState(() => {
@@ -14440,12 +15180,18 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, loadOff
14440
15180
  }, [routingSettings]);
14441
15181
  const dataRevision = workbench.ledger.value?.dynamicData?.revision ?? "";
14442
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;
15185
+ import_react5.default.useEffect(() => {
15186
+ setAssignmentMode("algorithm");
15187
+ setPackageAssignments({});
15188
+ }, [plan, task, mode, routingSettings, catalogState.catalog, toolProbes, workbench.health.report?.tools, manualAllowed]);
14443
15189
  import_react5.default.useEffect(() => {
14444
- if (plan && plan.workbenchRevision !== `${dataRevision}/${feedbackRevision}`) {
15190
+ if (plan && plan.workbenchRevision !== `${dataRevision}/${feedbackRevision}/${evidenceRevision}`) {
14445
15191
  setPlan(null);
14446
15192
  setPlanError("");
14447
15193
  }
14448
- }, [dataRevision, feedbackRevision, plan]);
15194
+ }, [dataRevision, feedbackRevision, evidenceRevision, plan]);
14449
15195
  import_react5.default.useEffect(() => {
14450
15196
  mounted.current = true;
14451
15197
  const request = ++catalogRequest.current;
@@ -14462,6 +15208,23 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, loadOff
14462
15208
  };
14463
15209
  }, []);
14464
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
+ };
14465
15228
  const providerCount = new Set(routes.map((route) => route.provider)).size;
14466
15229
  const refresh = async () => {
14467
15230
  const request = ++catalogRequest.current;
@@ -14508,6 +15271,7 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, loadOff
14508
15271
  const next = createWorkspacePlan(task, catalogState.catalog, {
14509
15272
  ...ledger.routingData,
14510
15273
  learning: ledger.learning,
15274
+ routingEvidence: ledger.routingEvidence,
14511
15275
  mode,
14512
15276
  ...direct ? { directProvider: direct.provider, directModel: direct.model } : {},
14513
15277
  budgetUsd: parsedBudget,
@@ -14517,7 +15281,7 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, loadOff
14517
15281
  preset: settingsScope.getSnapshot().value?.routingPreset ?? "balanced",
14518
15282
  loggedOutToolIds: (workbench.health.report?.tools ?? []).filter((item) => item.installed && item.login?.state === "logged-out").map((item) => item.id)
14519
15283
  });
14520
- setPlan({ ...next, workbenchRevision: `${ledger.dynamicData?.revision ?? ""}/${ledger.learning?.revision ?? ""}` });
15284
+ setPlan({ ...next, workbenchRevision: `${ledger.dynamicData?.revision ?? ""}/${ledger.learning?.revision ?? ""}/${ledger.routingEvidence?.revision ?? ""}` });
14521
15285
  } catch (error) {
14522
15286
  if (request !== generationRequest.current || inputs !== generationInputs.current || !mounted.current) return;
14523
15287
  setPlan(null);
@@ -14547,7 +15311,23 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, loadOff
14547
15311
  budgetValue.current = event.target.value;
14548
15312
  setBudget(event.target.value);
14549
15313
  invalidatePlan();
14550
- } }))), /* @__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(
14551
15331
  RunLauncher,
14552
15332
  {
14553
15333
  task,
@@ -14556,7 +15336,10 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, loadOff
14556
15336
  ledger: workbench.ledger.value,
14557
15337
  previewRun,
14558
15338
  startRun,
14559
- planningRevision: `${routingSettings}/${dataRevision}/${feedbackRevision}`,
15339
+ planningRevision: `${routingSettings}/${dataRevision}/${feedbackRevision}/${evidenceRevision}`,
15340
+ assignmentMode: effectiveAssignmentMode,
15341
+ packageAssignments: effectiveAssignmentMode === "manual" ? packageAssignments : {},
15342
+ packagePlanKey: currentPackagePlanKey,
14560
15343
  directRoute: mode === "direct" ? routes.find((route) => `${route.provider}/${route.model}` === directKey) ?? routes[0] ?? null : null,
14561
15344
  defaultPreset: settingsScope.getSnapshot().value?.routingPreset ?? "balanced",
14562
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" : "",
@@ -14762,6 +15545,14 @@ function parseRunRequest(value) {
14762
15545
  if (request.executionMode !== void 0 && !["read-only", "workspace-write"].includes(request.executionMode)) throw new TypeError("executionMode is invalid");
14763
15546
  if (request.executionEnvironment !== void 0 && !["harness", "cli-native"].includes(request.executionEnvironment)) throw new TypeError("executionEnvironment is invalid");
14764
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
+ }
14765
15556
  if (officialTeam && (request.planMode !== "team" || provider || model)) throw new TypeError("official-team requires a team plan without an explicit route");
14766
15557
  if (!officialTeam && request.executionMode === "workspace-write") throw new TypeError("workspace-write requires official-team");
14767
15558
  if (request.executionEnvironment === "cli-native" && (!officialTeam || request.executionMode !== "workspace-write")) {
@@ -14778,6 +15569,7 @@ function parseRunRequest(value) {
14778
15569
  ...request.executionEnvironment === void 0 ? {} : { executionEnvironment: request.executionEnvironment }
14779
15570
  } : {},
14780
15571
  ...provider ? { provider, model } : {},
15572
+ ...manual ? { assignmentMode: "manual", packageAssignments, packagePlanKey: request.packagePlanKey } : {},
14781
15573
  ...["single", "team"].includes(request.planMode) ? { planMode: request.planMode } : {},
14782
15574
  ...["economy", "balanced", "quality"].includes(request.preset) ? { preset: request.preset } : {},
14783
15575
  ...request.budgetUsd !== void 0 ? { budgetUsd: request.budgetUsd } : {},