codebee 0.1.32 → 0.1.34

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 CHANGED
@@ -6,6 +6,29 @@ README 元数据带回,供老版本在「发现新版本」时展示新版更
6
6
 
7
7
  ## 未发布
8
8
 
9
+ ## v0.1.34(2026-09-21)
10
+
11
+ ### 🛠 问题修复
12
+
13
+ - 手机端操作被「其他设备正在控制」挡住时,现在会弹出「接管并重试」确认框——点一下就接管控制权并自动重做刚才的操作,不再连点全是失败。
14
+ - 微信群列表拉取失败时把读取器的真实报错带出来(依赖缺失/解释器位数不对/微信未登录一眼可辨),不再只说「无有效输出」。
15
+ - 连载「章节评审」现在在侧栏进入的任务级详情也会显示,不再只有打开某一次运行时才能看到。
16
+ - 新建任务会一并记住上次的编排模式与思考程度;非法历史值会丢弃,不会污染表单。
17
+ - 修复扫描 Python 后尚未保存时被后台轮询冲回旧路径;现在点选或自动选中的候选都会保持到用户保存。
18
+
19
+ ## v0.1.33(2026-09-21)
20
+
21
+ ### ✨ 新功能
22
+
23
+ - 创建任务支持快速/自动/专家/手动四档编排,以及独立的自动/快速/标准/深度思考程度;所有预置类型按风险动态决定规划、评审、修复和换将步骤。
24
+ - 对话默认使用系统推荐,也可仅为本次对话指定厂商或模型;指定渠道不可用时明确失败,不修改全局 CLI 绑定。
25
+ - 创建前显示同类历史或流程基线 ETA/P90,运行中显示剩余时间与超出预估状态;满载时不排队,异常退出的运行自动收口为失败。
26
+
27
+ ### 🛠 问题修复
28
+
29
+ - 修复 Windows GBK 控制台因特殊符号导致服务启动崩溃;Aider 启动前增加 Git 仓库可读性诊断,避免损坏仓库长时间卡住。
30
+ - 修复 Anthropic 兼容接口误收到 OpenAI `reasoning_effort` 字段;修复语言切换覆盖端口标题导致桌宠重复开窗;过滤 CLI 历史和评审中间文件,成果页只展示可交付产物。
31
+
9
32
  ## v0.1.32(2026-09-21)
10
33
 
11
34
  ### ✨ 新功能
package/README.md CHANGED
@@ -21,8 +21,30 @@ Kimi Code、MiMo Code、Grok Build、Pi、DeepSeek Harness……),提供
21
21
  不会把你的任务内容交给任何第三方。
22
22
 
23
23
  <!-- relnotes:start -->
24
+ ### 🛠 问题修复 · Fixes
25
+
26
+ - 手机端操作被「其他设备正在控制」挡住时,弹出「接管并重试」——一键接管并自动重做刚才的操作。
27
+ - When a mobile action is blocked by another device in control, a "Take over & retry" prompt appears — one tap retakes control and redoes the action.
28
+
29
+ - 微信群列表拉取失败时会显示读取器的真实报错(依赖缺失/位数不对/微信未登录一眼可辨)。
30
+ - WeChat group-list failures now surface the reader's real error (missing dependency, wrong bitness, or WeChat not logged in).
31
+
32
+ - 连载章节评审在任务级详情与单次运行详情都可见;新建任务会记住上次的编排模式和思考程度。
33
+ - Chapter reviews now appear in both task and run details; new tasks also remember the last orchestration mode and reasoning level.
34
+
35
+ - 扫描 Python 后即使保存前遇到后台刷新,刚选中的路径也不会再被冲掉。
36
+ - A Python selected by Scan now survives background refreshes even before settings are saved.
37
+
24
38
  ### ✨ 新功能 · New
25
39
 
40
+ - 创建任务支持快速/自动/专家/手动编排与独立思考程度;代码、写作、翻译、邮件、汇报、调研等全类型按风险动态选择步骤。
41
+ - 新建对话默认系统推荐,也可只对当前对话指定厂商/模型;创建前和运行中显示 ETA/P90 与剩余时间。
42
+ - New tasks support Fast/Auto/Expert/Manual orchestration and an independent reasoning level; every task type adapts its workflow to risk.
43
+ - Conversations use the system recommendation by default, with optional per-conversation provider/model selection plus ETA/P90 visibility.
44
+
45
+ - 修复 Windows 控制台启动崩溃、损坏 Git 仓库导致 Aider 长时间等待、Anthropic 接口收到不兼容思考字段,以及桌宠语言切换后重复开窗。
46
+ - Fixed Windows console startup crashes, Aider stalls on unreadable Git repositories, incompatible Anthropic reasoning fields, and duplicate pet windows after language changes.
47
+
26
48
  - 连载任务详情新增「章节评审」卡片:每章维度评分(低分标红)与达标状态,大纲要点逐项核对结果红绿灰直读。
27
49
  - Serial runs now show a Chapter Reviews card: per-chapter dimension scores (low ones in red) plus beat-by-beat checks color-coded green/red/gray.
28
50
 
package/app/core/prefs.py CHANGED
@@ -14,11 +14,15 @@ from . import paths
14
14
 
15
15
  _LOCK = threading.Lock()
16
16
 
17
- SAVE_KEYS = ("type", "rounds", "threshold", "best_of", "chapters",
18
- "words_per_chapter", "variants")
19
- _LIMITS = {"rounds": (1, 5), "threshold": (1.0, 10.0), "best_of": (1, 3),
20
- "chapters": (1, 2000), "words_per_chapter": (200, 20000),
21
- "variants": (1, 3)}
17
+ SAVE_KEYS = ("type", "rounds", "threshold", "best_of", "chapters",
18
+ "words_per_chapter", "variants", "mode", "thinking")
19
+ _LIMITS = {"rounds": (1, 5), "threshold": (1.0, 10.0), "best_of": (1, 3),
20
+ "chapters": (1, 2000), "words_per_chapter": (200, 20000),
21
+ "variants": (1, 3)}
22
+ _CHOICES = {
23
+ "mode": ("auto", "fast", "expert", "manual"),
24
+ "thinking": ("auto", "low", "standard", "high"),
25
+ }
22
26
 
23
27
 
24
28
  def _file():
@@ -47,12 +51,17 @@ def record(payload):
47
51
  v = payload.get(key)
48
52
  if v is None:
49
53
  continue
50
- if key == "type":
51
- v = str(v)[:32]
52
- if v:
53
- cur["type"] = v
54
- continue
55
- c = _clamp(key, v)
54
+ if key == "type":
55
+ v = str(v)[:32]
56
+ if v:
57
+ cur["type"] = v
58
+ continue
59
+ if key in _CHOICES:
60
+ v = str(v).strip().lower()
61
+ if v in _CHOICES[key]:
62
+ cur[key] = v
63
+ continue
64
+ c = _clamp(key, v)
56
65
  if c is not None:
57
66
  cur[key] = c
58
67
  tmp = _file().with_suffix(".tmp")
@@ -131,17 +131,25 @@ def refresh_groups():
131
131
  _DIR.mkdir(parents=True, exist_ok=True)
132
132
  rf = _DIR / "groups_cache.json"
133
133
  cmd = [rp, str(READER_SCRIPT), "--list-groups", "--result", str(rf)]
134
+ stderr = b""
134
135
  try:
135
- subprocess.run(cmd, timeout=READER_TIMEOUT, capture_output=True,
136
- creationflags=CREATE_NO_WINDOW if os.name == "nt" else 0)
136
+ r = subprocess.run(cmd, timeout=READER_TIMEOUT, capture_output=True,
137
+ creationflags=CREATE_NO_WINDOW if os.name == "nt" else 0)
138
+ stderr = r.stderr or b""
137
139
  except subprocess.TimeoutExpired:
138
140
  raise RuntimeError("读取群列表超时(%ds)" % READER_TIMEOUT)
139
141
  try:
140
142
  res = json.loads(rf.read_text(encoding="utf-8"))
141
143
  except Exception:
142
- raise RuntimeError("群列表无有效输出(检查 reader_python 环境)")
144
+ # 带上 sidecar stderr 尾部:依赖缺失/解释器位数不对/脚本异常的真因
145
+ # 不再被「无有效输出」一句吞掉(用户实测排障反馈)
146
+ detail = (stderr or b"").decode("utf-8", "replace").strip()[-300:]
147
+ raise RuntimeError("群列表无有效输出(检查 reader_python 环境)%s"
148
+ % (":" + detail if detail else ""))
143
149
  if not res.get("ok"):
144
- raise RuntimeError("读取群列表失败")
150
+ errs = ";".join(str(x) for x in (res.get("errors") or [])) or \
151
+ str(res.get("error") or "未知错误")
152
+ raise RuntimeError("读取群列表失败:%s" % errs[:300])
145
153
  groups = res.get("groups") or []
146
154
  with _LOCK:
147
155
  _STATE["groups_cache"] = groups
package/app/ui/app.js CHANGED
@@ -10,7 +10,7 @@ function codebeeDocumentTitle(lang) {
10
10
  document.title = codebeeDocumentTitle((localStorage.getItem("orch.lang") || "zh").toLowerCase());
11
11
 
12
12
  const $ = (id) => document.getElementById(id);
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 };
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, prefsApplied: false };
14
14
 
15
15
  /* ---------------------------------------------------------- 任务类型(流程) */
16
16
  async function loadFlows() {
@@ -65,10 +65,22 @@ function renderTypeOptions() {
65
65
  // 首屏默认落在「直接执行」(快档位:单 CLI 直达,无拆解/评审);
66
66
  // 老数据或该流程被删时保持首项,不硬造一个不存在的值
67
67
  else if (flowById("direct")) sel.value = "direct";
68
+ applyTaskPreferenceDefaults();
68
69
  renderTypeMenu();
69
70
  onTypeChange();
70
71
  }
71
72
 
73
+ function applyTaskPreferenceDefaults() {
74
+ if (S.prefsApplied || !S.lastPrefs) return;
75
+ const lp = S.lastPrefs;
76
+ const mode = $("f-mode"), thinking = $("f-thinking");
77
+ if (mode && ["auto", "fast", "expert", "manual"].includes(lp.mode)) mode.value = lp.mode;
78
+ if (thinking && ["auto", "low", "standard", "high"].includes(lp.thinking)) thinking.value = lp.thinking;
79
+ const manual = $("f-manual-only");
80
+ if (manual && mode) manual.classList.toggle("hidden", mode.value !== "manual");
81
+ S.prefsApplied = true;
82
+ }
83
+
72
84
  /* 类型选择的可见层:带黑白图标的按钮 + 弹出菜单。原生 <option> 渲染不了 SVG,
73
85
  * 所以隐藏 select 只当值真源(既有联动/测试全部照旧),外观全靠这层。 */
74
86
  function renderTypeMenu() {
@@ -590,6 +602,7 @@ async function api(path, opts) {
590
602
  delete fetchOpts.busy;
591
603
  delete fetchOpts.busyElement;
592
604
  delete fetchOpts.operation;
605
+ delete fetchOpts._ctrlRetry;
593
606
  try {
594
607
  let res;
595
608
  try {
@@ -602,7 +615,27 @@ async function api(path, opts) {
602
615
  if (res.status === 401) { showTokenGate(t("令牌不正确或已更换,请重新输入")); throw new Error(t("需要访问令牌")); }
603
616
  let data = null;
604
617
  try { data = await res.json(); } catch (e) { /* ignore */ }
605
- if (res.status === 423 && data && data.control) setControl(data.control);
618
+ if (res.status === 423 && data && data.control) {
619
+ setControl(data.control);
620
+ // 手机端接管闭环(用户实测反馈:423 连点全失败不知道怎么办):
621
+ // 他人持有时弹「接管并重试」确认,确认后抢夺控制权并自动重试一次。
622
+ // uiConfirm 弹窗互斥防堆叠;重试标记防递归;GET 轮询不会走到这里。
623
+ const holder = data.control.holder || "";
624
+ if (!opts._ctrlRetry && !opts.silent && !api._ctrlPrompting && holder) {
625
+ api._ctrlPrompting = true;
626
+ try {
627
+ if (await uiConfirm(t("「{0}」正在控制,接管后自动重试「{1}」?",
628
+ holder, operationLabel(path, { ...opts, operation: true }) || t("提交操作")),
629
+ { ok: t("接管并重试") })) {
630
+ await ctrlAction("acquire", true);
631
+ api._ctrlPrompting = false;
632
+ return api(path, Object.assign({}, opts, { _ctrlRetry: true }));
633
+ }
634
+ } finally {
635
+ api._ctrlPrompting = false;
636
+ }
637
+ }
638
+ }
606
639
  if (!res.ok) throw new Error((data && data.error) || ("HTTP " + res.status));
607
640
  operationFinish(operationToken, "done");
608
641
  return data;
@@ -3672,6 +3705,7 @@ function drawTaskDetail(key, runs) {
3672
3705
  (latest.error ? '<span class="stat err">' + errTag(latest.error) + esc(latest.error.slice(0, 200)) + "</span>" : "");
3673
3706
  $("rd-plan").classList.add("hidden");
3674
3707
  renderRouting(latest);
3708
+ renderChapterScores(latest);
3675
3709
  let html = "";
3676
3710
  ordered.forEach((r, i) => {
3677
3711
  const runNo = runs.length - i;
@@ -11733,6 +11767,7 @@ function beePyRender(list) {
11733
11767
  b.addEventListener("click", () => {
11734
11768
  const p = list[Number(b.dataset.i)];
11735
11769
  $("bee-reader-py").value = p.path;
11770
+ Bee.pyDirty = true;
11736
11771
  box.querySelectorAll(".bee-py-item").forEach((x) => x.classList.remove("on"));
11737
11772
  b.classList.add("on");
11738
11773
  beePyInstallVisibility(p);
@@ -11744,6 +11779,7 @@ function beePyRender(list) {
11744
11779
  list.find((p) => p.bits >= 64);
11745
11780
  if (hit) {
11746
11781
  $("bee-reader-py").value = hit.path;
11782
+ if (hit.path !== cur) Bee.pyDirty = true;
11747
11783
  beePyInstallVisibility(hit);
11748
11784
  const btn = box.querySelector('.bee-py-item[data-i="' + list.indexOf(hit) + '"]');
11749
11785
  if (btn) btn.classList.add("on");
package/app/ui/i18n.js CHANGED
@@ -409,6 +409,8 @@
409
409
  "%(失败 ": "% (failed ",
410
410
  "「": "\"",
411
411
  "」将变为只读。": "\" will become read-only.",
412
+ "「{0}」正在控制,接管后自动重试「{1}」?": "\"{0}\" is in control. Take over and retry \"{1}\"?",
413
+ "接管并重试": "Take over & retry",
412
414
  "」恢复为内置默认配置?": "\" restored to built-in defaults?",
413
415
  "{0} 正在控制(点击接管)": "{0} is in control (click to take over)",
414
416
  "」?已有任务不受影响。": "\"? Existing tasks are not affected.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codebee",
3
- "version": "0.1.32",
3
+ "version": "0.1.34",
4
4
  "description": "CodeBee · 多智能体编排台 — 统一编排本机 AI 编码 CLI(Codex / Claude Code / QwenCode / OpenCode / Aider…):目标→拆解→路由→执行→验证→跨厂商评审→修复→报告。纯 Python 标准库实现,本地 Web UI。",
5
5
  "license": "MIT",
6
6
  "bin": {