codebee 0.1.30 → 0.1.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/README.md +8 -0
- package/app/core/builtin_agent.py +3 -1
- package/app/core/modelhub.py +11 -5
- package/app/core/pipeline.py +4 -2
- package/app/core/planner.py +34 -10
- package/app/core/repeat_guard.py +3 -3
- package/app/core/task_compile.py +3 -2
- package/app/pet.py +1564 -1564
- package/app/ui/app.js +77 -61
- package/app/ui/i18n.js +6 -0
- package/app/ui/index.html +1172 -1185
- package/app/ui/style.css +13 -0
- package/package.json +1 -1
package/app/ui/app.js
CHANGED
|
@@ -2,7 +2,12 @@
|
|
|
2
2
|
"use strict";
|
|
3
3
|
|
|
4
4
|
// 桌宠通过窗口标题里的端口识别并复用当前 CodeBee 实例。
|
|
5
|
-
|
|
5
|
+
function codebeeDocumentTitle(lang) {
|
|
6
|
+
const brand = lang === "en" ? "CodeBee · Multi-agent Orchestrator" : "CodeBee · 多智能体编排台";
|
|
7
|
+
const port = location.port || (location.protocol === "https:" ? "443" : "80");
|
|
8
|
+
return brand + " [" + port + "]";
|
|
9
|
+
}
|
|
10
|
+
document.title = codebeeDocumentTitle((localStorage.getItem("orch.lang") || "zh").toLowerCase());
|
|
6
11
|
|
|
7
12
|
const $ = (id) => document.getElementById(id);
|
|
8
13
|
const S = { state: null, catalog: null, catSig: "", providers: null, bindings: null, modelsSig: "", bindSig: "", tab: "tasks", detailRunId: null, pollTimer: null, showArchived: false, /* 会话内开关:每次加载默认隐藏已归档、图标不选中(不持久化,见 btn-side-arch) */ selProvs: {}, selModels: {}, selRuns: {}, bindSel: {}, catalogChecking: false, updateCheckAt: 0, control: null, sseLive: false, es: null, flows: null, orch: null, skills: null, orchSig: "", settings: null, sessionAgents: new Set(), atts: [], gitInfo: null, gitWb: null, gitWbKey: "", gitWbAt: 0, gitWbBusy: false, creatingTask: false, inspKey: null, inspData: null, inspSig: "", inspAt: 0, inspTab: "git", inspAutoSig: "", rdTab: null, rdTabSig: "", rdTabPin: false, _rdCtx: {}, lastPrefs: null };
|
|
@@ -272,8 +277,8 @@ function cmpGreeting() {
|
|
|
272
277
|
: "晚上好呀,有什么想让我帮忙的吗");
|
|
273
278
|
}
|
|
274
279
|
|
|
275
|
-
/*
|
|
276
|
-
*
|
|
280
|
+
/* 开跑前成本与耗时预估(借鉴 omnigent 的 pre-run estimate):优先读同类台账,
|
|
281
|
+
* 无历史时显示流程基线;快速切类型用序号丢弃过期响应。 */
|
|
277
282
|
let _estSeq = 0;
|
|
278
283
|
async function refreshEstimate() {
|
|
279
284
|
const el = $("cmp-estimate");
|
|
@@ -281,13 +286,26 @@ async function refreshEstimate() {
|
|
|
281
286
|
if (!el) return;
|
|
282
287
|
const seq = ++_estSeq;
|
|
283
288
|
try {
|
|
284
|
-
const
|
|
289
|
+
const mode = ($("f-mode") || {}).value || "auto";
|
|
290
|
+
const thinking = ($("f-thinking") || {}).value || "standard";
|
|
291
|
+
const rounds = ($("f-rounds") || {}).value || "";
|
|
292
|
+
const query = new URLSearchParams({ type: flowId, days: "90", mode, thinking });
|
|
293
|
+
if (rounds) query.set("rounds", rounds);
|
|
294
|
+
const d = await api("/api/usage/estimate?" + query.toString());
|
|
285
295
|
if (seq !== _estSeq) return;
|
|
286
|
-
if (!d || !(d.
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
const
|
|
290
|
-
|
|
296
|
+
if (!d || !(d.estimated_duration_s > 0)) {
|
|
297
|
+
el.classList.add("hidden"); el.textContent = ""; return;
|
|
298
|
+
}
|
|
299
|
+
const duration = chatDurTxt(d.estimated_duration_s);
|
|
300
|
+
const p90 = chatDurTxt(d.p90_duration_s || d.estimated_duration_s);
|
|
301
|
+
const source = d.samples > 0
|
|
302
|
+
? t("近{0}天 · {1}次同类", d.days || 90, d.samples)
|
|
303
|
+
: t("暂无同类历史,按流程基线估算");
|
|
304
|
+
const tokenPart = d.samples > 0 && d.median_tokens != null
|
|
305
|
+
? " · ≈" + fmtTok(d.median_tokens) + " tokens" : "";
|
|
306
|
+
const cost = (d.median_cost_usd || 0) > 0 ? " · ≈$" + Number(d.median_cost_usd).toFixed(2) : "";
|
|
307
|
+
el.textContent = t("预计完成约 {0},保守不超过 {1}", duration, p90) +
|
|
308
|
+
tokenPart + cost + "(" + source + ")";
|
|
291
309
|
el.classList.remove("hidden");
|
|
292
310
|
} catch (e) {
|
|
293
311
|
if (seq === _estSeq) { el.classList.add("hidden"); el.textContent = ""; }
|
|
@@ -4032,7 +4050,8 @@ async function renderRunDetail() {
|
|
|
4032
4050
|
'<span class="stat">' + t("创建 ") + '<b>' + esc(run.created_at) + "</b></span>" +
|
|
4033
4051
|
'<span class="stat">' + t("成本 ") + '<b>$' + Number(run.cost_usd || 0).toFixed(3) + "</b></span>" +
|
|
4034
4052
|
'<span class="stat">tokens <b>' + (run.tokens || 0) + "</b></span>" +
|
|
4035
|
-
(run.mode ? '<span class="stat">' + t("模式 ") + '<b>' + (
|
|
4053
|
+
(run.mode ? '<span class="stat">' + t("模式 ") + '<b>' + ({auto: t("自动"), fast: t("快速"), expert: t("专家"), manual: t("手动")}[run.mode] || esc(run.mode)) + "</b></span>" : "") +
|
|
4054
|
+
(runEtaText(run) ? '<span class="stat eta">' + esc(runEtaText(run)) + "</span>" : "") +
|
|
4036
4055
|
(run.error ? '<span class="stat err">' + errTag(run.error) + esc(run.error.slice(0, 200)) + "</span>" : "") +
|
|
4037
4056
|
'<span class="stat tasksum hidden" id="rd-meta-task"></span>';
|
|
4038
4057
|
if (S.detailSide) fillMetaTask(S.detailSide.stats || {}); // 缓存命中:轮询重画不闪丢累计组
|
|
@@ -6513,6 +6532,29 @@ function runDurationSeconds(run) {
|
|
|
6513
6532
|
return Number.isFinite(a) && Number.isFinite(b) && b >= a ? (b - a) / 1000 : null;
|
|
6514
6533
|
}
|
|
6515
6534
|
|
|
6535
|
+
function runEtaText(run) {
|
|
6536
|
+
if (!run || !Number.isFinite(Number(run.estimated_duration_s))) return "";
|
|
6537
|
+
const estimate = Math.max(1, Number(run.estimated_duration_s));
|
|
6538
|
+
const p90 = Math.max(estimate, Number(run.estimated_p90_s) || estimate);
|
|
6539
|
+
const active = run.status === "running" || run.status === "queued";
|
|
6540
|
+
if (!active) return t("预计完成约 {0}", chatDurTxt(Math.round(estimate)));
|
|
6541
|
+
if (run.status === "queued" || !run.started_at) {
|
|
6542
|
+
return t("预计完成约 {0},保守不超过 {1}",
|
|
6543
|
+
chatDurTxt(Math.round(estimate)), chatDurTxt(Math.round(p90)));
|
|
6544
|
+
}
|
|
6545
|
+
const started = Date.parse(String(run.started_at).replace(" ", "T"));
|
|
6546
|
+
if (!Number.isFinite(started)) return "";
|
|
6547
|
+
const elapsed = Math.max(0, (Date.now() - started) / 1000);
|
|
6548
|
+
if (elapsed < estimate) {
|
|
6549
|
+
return t("预计剩余约 {0}", chatDurTxt(Math.max(1, Math.round(estimate - elapsed))));
|
|
6550
|
+
}
|
|
6551
|
+
if (elapsed < p90) {
|
|
6552
|
+
return t("已超过常规预估,仍在保守区间内(约剩 {0})",
|
|
6553
|
+
chatDurTxt(Math.max(1, Math.round(p90 - elapsed))));
|
|
6554
|
+
}
|
|
6555
|
+
return t("已超过保守预估,正在继续执行,可查看步骤日志");
|
|
6556
|
+
}
|
|
6557
|
+
|
|
6516
6558
|
function runOutcomeSummary(run) {
|
|
6517
6559
|
if (!run) return "";
|
|
6518
6560
|
const last = (run.steps || []).slice().reverse().find((s) => s.output || s.summary);
|
|
@@ -6579,6 +6621,7 @@ function renderDetailOverview(run, runs, task) {
|
|
|
6579
6621
|
const lastText = last ? String(last.summary || last.output || last.note || "").replace(/\s+/g, " ").trim().slice(0, 240) : "";
|
|
6580
6622
|
const executor = run.executor || run.implementer || (last && (last.agent_label || last.agent)) || "";
|
|
6581
6623
|
const duration = runDurationSeconds(run);
|
|
6624
|
+
const etaText = runEtaText(run);
|
|
6582
6625
|
const goal = (task && task.goal) || run.goal || run.title || "";
|
|
6583
6626
|
const actions = [];
|
|
6584
6627
|
if (total) actions.push('<button type="button" class="ghost" data-overview-tab="steps">' + esc(t("查看步骤")) + "</button>");
|
|
@@ -6599,6 +6642,7 @@ function renderDetailOverview(run, runs, task) {
|
|
|
6599
6642
|
'<span><b>' + settled + "/" + total + '</b> ' + esc(t("步骤已完成")) + '</span>' +
|
|
6600
6643
|
(executor ? '<span>' + esc(t("执行者")) + ' <b>' + esc(executor) + '</b></span>' : "") +
|
|
6601
6644
|
(duration != null ? '<span>' + esc(t("耗时")) + ' <b>' + esc(chatDurTxt(duration < 1 ? Number(duration.toFixed(1)) : Math.round(duration))) + '</b></span>' : "") +
|
|
6645
|
+
(etaText ? '<span class="rd-eta">' + esc(etaText) + '</span>' : "") +
|
|
6602
6646
|
'</div>' +
|
|
6603
6647
|
(lastText ? '<div class="rd-overview-last"><b>' + esc(t("最近一步")) + '</b><span>' + esc(lastText) + '</span></div>' : "") +
|
|
6604
6648
|
(actions.length ? '<div class="rd-overview-actions">' + actions.join("") + '</div>' : "");
|
|
@@ -9857,7 +9901,7 @@ async function suStartupCheck() {
|
|
|
9857
9901
|
}
|
|
9858
9902
|
|
|
9859
9903
|
/* ---------------------------------------------------------- 页签 & 初始化 */
|
|
9860
|
-
const TAB_TITLES = { tasks: "任务", runs: "运行记录", automation: "自动化", zentao: "禅道 Bug 自动修复", usage: "用量统计", agents: "本机智能体", models: "模型接入", bindings: "模型调度(可选)", skills: "经验库", knowledge: "知识库", market: "插件市场", orch: "编排设置", data: "数据与备份", appearance: "皮肤", about: "关于与更新" };
|
|
9904
|
+
const TAB_TITLES = { tasks: "任务", runs: "运行记录", automation: "自动化", zentao: "禅道 Bug 自动修复", __wxdigest: "群摘要", usage: "用量统计", agents: "本机智能体", models: "模型接入", bindings: "模型调度(可选)", skills: "经验库", knowledge: "知识库", market: "插件市场", orch: "编排设置", data: "数据与备份", appearance: "皮肤", about: "关于与更新" };
|
|
9861
9905
|
const SET_TABS = new Set(Object.keys(TAB_TITLES)); // 设置导航里的子页(__phone 是弹框,不算)
|
|
9862
9906
|
|
|
9863
9907
|
function tabTitle(name) {
|
|
@@ -10013,7 +10057,7 @@ function setLangBtn(lang) {
|
|
|
10013
10057
|
S.catSig = ""; S.modelsSig = ""; S.bindSig = ""; S.orchSig = ""; S.sideSig = ""; S.taskSig = ""; S.mgmt = {};
|
|
10014
10058
|
syncLangMode();
|
|
10015
10059
|
// 浏览器标签标题
|
|
10016
|
-
document.title = (lang
|
|
10060
|
+
document.title = codebeeDocumentTitle(lang);
|
|
10017
10061
|
applyI18n();
|
|
10018
10062
|
render();
|
|
10019
10063
|
cmpGreeting(); // 问候语跟随语言重算
|
|
@@ -10696,11 +10740,6 @@ function collapseDrawerIfMobile() {
|
|
|
10696
10740
|
function switchTab(name) {
|
|
10697
10741
|
if (name === "__phone") { openPhoneConnect(); return; } // 手机连接是弹框,不切页
|
|
10698
10742
|
if (name === "__guide") { welcomeOpen(); return; } // 帮助中心是弹层,不切页(设置导航「软件」组)
|
|
10699
|
-
if (name === "__wxdigest") { // 群聊汇报沿用全局蜜蜂面板
|
|
10700
|
-
beeRefresh().finally(() => beeToggle(true));
|
|
10701
|
-
collapseDrawerIfMobile();
|
|
10702
|
-
return;
|
|
10703
|
-
}
|
|
10704
10743
|
// 导航收进「设置」:进设置后左栏整体换成设置导航,内容铺满
|
|
10705
10744
|
S.tab = name;
|
|
10706
10745
|
if (SET_TABS.has(name)) localStorage.setItem("orch.setTab", name);
|
|
@@ -10719,6 +10758,7 @@ function switchTab(name) {
|
|
|
10719
10758
|
if (name === "automation") { loadAutomation(); startAutoPoll(); } // 进自动化页:拉取 + 页面可见时每 8s 轮询
|
|
10720
10759
|
else stopAutoPoll(); // 离开自动化页(或切到别的子页)即停表
|
|
10721
10760
|
if (name === "zentao") loadZentao(); // 进禅道页:拉配置与修复记录回填表单
|
|
10761
|
+
if (name === "__wxdigest") { beeRefresh(); beeMarkSeen(); } // 进群摘要页:拉取视图并清未读
|
|
10722
10762
|
if (name === "market") loadMarket(); // 进插件市场页拉取目录
|
|
10723
10763
|
if (name === "data") loadDataPage(); // 进数据与备份页:清理配置/状态/可清理预估
|
|
10724
10764
|
if (name === "usage") { syncUsageRange(); loadUsage(); } // 进用量页:对齐范围选中态并拉取
|
|
@@ -11186,7 +11226,10 @@ document.addEventListener("DOMContentLoaded", () => {
|
|
|
11186
11226
|
});
|
|
11187
11227
|
$("f-mode").addEventListener("change", () => {
|
|
11188
11228
|
$("f-manual-only").classList.toggle("hidden", $("f-mode").value !== "manual");
|
|
11229
|
+
refreshEstimate();
|
|
11189
11230
|
});
|
|
11231
|
+
$("f-thinking").addEventListener("change", refreshEstimate);
|
|
11232
|
+
$("f-rounds").addEventListener("change", refreshEstimate);
|
|
11190
11233
|
$("f-direct-provider").addEventListener("change", renderDirectModelPicker);
|
|
11191
11234
|
$("f-type").dispatchEvent(new Event("change"));
|
|
11192
11235
|
cmpGreeting(); // 问候语按时段刷新(切语言/回任务页也会重算)
|
|
@@ -11413,46 +11456,20 @@ function bindCmdK() {
|
|
|
11413
11456
|
});
|
|
11414
11457
|
}
|
|
11415
11458
|
|
|
11416
|
-
/* ------------------------------------------------
|
|
11417
|
-
*
|
|
11418
|
-
*
|
|
11419
|
-
* 60s 轮询 /api/wxdigest
|
|
11420
|
-
const Bee = { view: null, lastUnseen: 0, bubT: 0, selGroups: null };
|
|
11459
|
+
/* ------------------------------------------------ 群摘要页(core/wxdigest.py)
|
|
11460
|
+
* 设置 → 群摘要:配置监控文件夹/微信直连 + 各群摘要列表(替代原右下角悬浮
|
|
11461
|
+
* 蜜蜂坞——用户拍板去掉悬浮组件,配置与总结统一进页面)。桌面蜜蜂 pet.py
|
|
11462
|
+
* 右键「群摘要」直达本页。进页/60s 轮询 /api/wxdigest;进页 POST seen 清未读。 */
|
|
11463
|
+
const Bee = { view: null, lastUnseen: 0, bubT: 0, selGroups: null, pyDirty: false };
|
|
11421
11464
|
|
|
11422
11465
|
function beeShow(el, on) { if (el) el.classList.toggle("hidden", !on); }
|
|
11423
|
-
function beePanelOpen() { const p = $("bee-panel"); return !!p && !p.classList.contains("hidden"); }
|
|
11424
11466
|
|
|
11425
11467
|
async function beeRefresh() {
|
|
11426
11468
|
let v;
|
|
11427
11469
|
try { v = await api("/api/wxdigest"); } catch (e) { return; } // 静默:不打扰主流程
|
|
11428
11470
|
Bee.view = v;
|
|
11429
|
-
beeShow($("bee-dock"), true);
|
|
11430
|
-
beeShow($("bee-badge"), v.unseen > 0);
|
|
11431
|
-
if (v.unseen > 0) $("bee-badge").textContent = v.unseen > 99 ? "99+" : String(v.unseen);
|
|
11432
|
-
if (v.unseen > Bee.lastUnseen && !beePanelOpen()) { // 新摘要到:气泡 6s
|
|
11433
|
-
const b = $("bee-bubble");
|
|
11434
|
-
const names = [...new Set((v.digests || []).slice(0, v.unseen).map((d) => d.group))];
|
|
11435
|
-
b.textContent = t("嗡!新群摘要好了:") + (names.join(t("、")) || "…");
|
|
11436
|
-
beeShow(b, true);
|
|
11437
|
-
clearTimeout(Bee.bubT);
|
|
11438
|
-
Bee.bubT = setTimeout(() => beeShow(b, false), 6000);
|
|
11439
|
-
}
|
|
11440
11471
|
Bee.lastUnseen = v.unseen;
|
|
11441
|
-
|
|
11442
|
-
}
|
|
11443
|
-
|
|
11444
|
-
function beeToggle(force) {
|
|
11445
|
-
const p = $("bee-panel");
|
|
11446
|
-
if (!p) return;
|
|
11447
|
-
const open = force !== undefined ? force : p.classList.contains("hidden");
|
|
11448
|
-
beeShow(p, open);
|
|
11449
|
-
if (open) {
|
|
11450
|
-
const v = Bee.view || {};
|
|
11451
|
-
const hasData = (v.digests || []).length > 0;
|
|
11452
|
-
beeShow($("bee-cfg"), !hasData); // 没数据时首开直落配置(可发现性)
|
|
11453
|
-
beeRenderPanel();
|
|
11454
|
-
beeMarkSeen();
|
|
11455
|
-
}
|
|
11472
|
+
beeRenderPanel();
|
|
11456
11473
|
}
|
|
11457
11474
|
|
|
11458
11475
|
async function beeMarkSeen() {
|
|
@@ -11460,7 +11477,6 @@ async function beeMarkSeen() {
|
|
|
11460
11477
|
const r = await api("/api/wxdigest/seen", { method: "POST", body: "{}" });
|
|
11461
11478
|
Bee.view = r.view;
|
|
11462
11479
|
Bee.lastUnseen = 0;
|
|
11463
|
-
beeShow($("bee-badge"), false);
|
|
11464
11480
|
beeRenderPanel();
|
|
11465
11481
|
} catch (e) { /* ignore */ }
|
|
11466
11482
|
}
|
|
@@ -11472,7 +11488,10 @@ function beeRenderPanel() {
|
|
|
11472
11488
|
$("bee-enabled").checked = !!cfg.enabled;
|
|
11473
11489
|
$("bee-reader").checked = !!cfg.reader_enabled;
|
|
11474
11490
|
beeShow($("bee-reader-box"), !!cfg.reader_enabled);
|
|
11475
|
-
|
|
11491
|
+
// 用户手动改过路径(选候选/输入)后轮询不再回填覆盖——否则 60s 一冲,
|
|
11492
|
+
// 选择根本存不进去(「选不中 Python」根因之一)
|
|
11493
|
+
if (!Bee.pyDirty && document.activeElement !== $("bee-reader-py"))
|
|
11494
|
+
$("bee-reader-py").value = cfg.reader_python || "";
|
|
11476
11495
|
if (!Bee.selGroups) Bee.selGroups = (cfg.groups || []).slice();
|
|
11477
11496
|
const n = Bee.selGroups.length;
|
|
11478
11497
|
$("bee-groups-btn").textContent = t("选择要监听的群({0})", n);
|
|
@@ -11585,6 +11604,7 @@ async function beeSave() {
|
|
|
11585
11604
|
}) });
|
|
11586
11605
|
if (Bee.view) Bee.view.config = r.config;
|
|
11587
11606
|
Bee.selGroups = null; // 下次渲染用保存后的值重建勾选态
|
|
11607
|
+
Bee.pyDirty = false; // 保存成功:服务端值与所见一致,恢复轮询回填
|
|
11588
11608
|
toast(t("群摘要设置已保存"));
|
|
11589
11609
|
beeRefresh();
|
|
11590
11610
|
} catch (e) { toast(t("保存失败:") + (e.message || e), true); }
|
|
@@ -11606,13 +11626,7 @@ async function beeScan() {
|
|
|
11606
11626
|
|
|
11607
11627
|
(function () {
|
|
11608
11628
|
const boot = () => {
|
|
11609
|
-
if (!$("bee-
|
|
11610
|
-
$("bee-fab").addEventListener("click", () => beeToggle());
|
|
11611
|
-
$("bee-x").addEventListener("click", () => beeToggle(false));
|
|
11612
|
-
$("bee-cfg-btn").addEventListener("click", () => {
|
|
11613
|
-
const c = $("bee-cfg");
|
|
11614
|
-
beeShow(c, c.classList.contains("hidden"));
|
|
11615
|
-
});
|
|
11629
|
+
if (!$("bee-save")) return;
|
|
11616
11630
|
$("bee-save").addEventListener("click", beeSave);
|
|
11617
11631
|
$("bee-scan-btn").addEventListener("click", beeScan);
|
|
11618
11632
|
$("bee-dir-pick").addEventListener("click", () => window.pickFolder("bee-dir", true));
|
|
@@ -11632,11 +11646,13 @@ async function beeScan() {
|
|
|
11632
11646
|
// 别手填解释器路径、别自己开 conda 装依赖)
|
|
11633
11647
|
$("bee-py-scan").addEventListener("click", beePyScan);
|
|
11634
11648
|
$("bee-py-install").addEventListener("click", beePyInstall);
|
|
11649
|
+
// 用户手动改过路径(选候选/输入)后置脏:轮询不再回填覆盖
|
|
11650
|
+
$("bee-reader-py").addEventListener("input", () => { Bee.pyDirty = true; });
|
|
11635
11651
|
beeRefresh();
|
|
11636
11652
|
setInterval(beeRefresh, 60000);
|
|
11637
|
-
// 桌宠右键「群摘要」直达:URL 带 #
|
|
11638
|
-
if (/
|
|
11639
|
-
setTimeout(() =>
|
|
11653
|
+
// 桌宠右键「群摘要」直达:URL 带 #goto=__wxdigest 时切到群摘要子页
|
|
11654
|
+
if (/goto=__wxdigest/.test(location.hash)) {
|
|
11655
|
+
setTimeout(() => switchTab("__wxdigest"), 800);
|
|
11640
11656
|
history.replaceState(null, "", location.pathname + location.search);
|
|
11641
11657
|
}
|
|
11642
11658
|
};
|
package/app/ui/i18n.js
CHANGED
|
@@ -766,6 +766,12 @@
|
|
|
766
766
|
"常用类型": "Common types",
|
|
767
767
|
"预估:≈{0}{1}": "Est: ≈{0}{1}",
|
|
768
768
|
"近{0}天 · {1}次同类": "last {0}d · {1} similar runs",
|
|
769
|
+
"暂无同类历史,按流程基线估算": "No similar history; estimated from the workflow baseline",
|
|
770
|
+
"预计完成约 {0}": "Estimated completion: about {0}",
|
|
771
|
+
"预计完成约 {0},保守不超过 {1}": "Estimated completion: about {0}; conservative bound {1}",
|
|
772
|
+
"预计剩余约 {0}": "About {0} remaining",
|
|
773
|
+
"已超过常规预估,仍在保守区间内(约剩 {0})": "Past the usual estimate but within the conservative bound (about {0} remaining)",
|
|
774
|
+
"已超过保守预估,正在继续执行,可查看步骤日志": "Past the conservative estimate; still running. Check step logs for progress",
|
|
769
775
|
"候选并发数(单稿赛马,1=关)": "Best-of-N candidates (1=off)",
|
|
770
776
|
"同时起草多份候选稿,终审择优并吸收落选稿优点;每多一路多一份起草开销": "Draft several candidates in parallel; a final selector picks the best and harvests the losers' strengths. Each extra candidate costs one more draft",
|
|
771
777
|
"建 PR": "Create PR",
|