codebee 0.1.61 → 0.1.63
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 +14 -11
- package/README.md +4 -6
- package/app/core/catalog.py +19 -14
- package/app/core/pipeline.py +62 -2
- package/app/core/router.py +324 -307
- package/app/core/runner.py +2014 -1991
- package/app/core/skill_scan.py +13 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,23 +6,26 @@ README 元数据带回,供老版本在「发现新版本」时展示新版更
|
|
|
6
6
|
|
|
7
7
|
## 未发布
|
|
8
8
|
|
|
9
|
+
## v0.1.63(2026-09-24)
|
|
10
|
+
|
|
11
|
+
- 0.1.62 被并行发布占位(旧 HEAD,不含 runner 修复);本版为完整版
|
|
12
|
+
|
|
13
|
+
## v0.1.62(2026-09-24)
|
|
14
|
+
|
|
9
15
|
### ✨ 新功能
|
|
10
16
|
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
- 新增**网页成品实时预览**:代码任务跑完,详情页多出「预览」页签——「运行视图」把工作目录里的入口 HTML 真跑起来(iframe 里 style.css / script.js 这些相对引用照常生效,改完点「刷新」即看新版),旁边一排文件页签直接读源码(行号 + 高亮,与成果预览同款),右上角「打开新窗口」给真全屏。借鉴对话式编程产品"边聊边看效果"的体验:终态代码任务跑出了可打开的网页成品时,打开详情直接落在预览;直连对话任务的对话胶囊里也有「预览」入口。没有网页成品的任务(小说、普通脚本)完全不出现这个页签。安全边界:预览是只读静态挂载,只认静态扩展名白名单、拒绝路径穿越与隐藏目录;访问令牌走路径段(iframe 子资源继承不了查询串),鉴权与 /api/* 同一把尺子。
|
|
15
|
-
- **直连对话会动手修了**:内置智能体新增 `run_command`,诊断、修复、改配置、重启服务等命令直接在你机器上真实执行(与 codex/claude 全权沙箱同信任档,无确认闸),退出码与输出回给智能体接力下一步——不再只甩一份「照着做」的指南。超时/取消即强杀进程树;Windows 提权命令自动走 UAC 弹窗(点一次允许即可)。顺带修掉一个底层隐患:子进程杀树后管道关闭不再死等漏网孙进程自然退出(此前会让超时杀进程卡满命令时长)。
|
|
16
|
-
- 连载小说支持**分卷**:新建任务时给「每卷章数」(默认预填 20,清空 = 不分卷),或在「指定卷结构」里写明各卷(也能直接在任务目标里写「第一卷 少年初入江湖 第1-20章;卷二 风云再起 21-40章」,自动识别)。分卷贯穿四个环节:① 大纲按卷规划,产出卷名与卷弧光(用户给了卷名就原样沿用);② 每章起草与评审都带上「第几卷第几章、是不是卷末」,卷末章按本卷高潮与卷末钩子收束;③ 合并成书插入「第 X 卷 《卷名》」分隔;④ 运行详情的章节评审卡与发布页章节清单按卷分组。卷边界由全书章号确定性推导,续写批次自动接上同一卷、不会把一卷劈成两半。
|
|
17
|
+
- **任务计划活起来了**:`.codebee/task_plan.md` 从静态清单升级为随执行实时推进的活文档——每个子任务起跑标 `[>]`、成功翻 `[x]`、失败标 `[!]`(换将重试成功自动翻正)。任务崩溃、续跑、换将时打开计划文件一眼看到断点走到哪步;回写失败静默,绝不影响任务执行。
|
|
18
|
+
- **skill 装前扫描认得投毒特征了**:新增四组静态特征——下载即执行(`curl|sh` 管道、PowerShell `Invoke-Expression` 投放)、持久化驻留(crontab/计划任务/注册表自启动键,直接判高风险)、常见外传信道(pastebin/webhook.site/ngrok 隧道)、挖矿特征(stratum/xmrig)。中文叙述词零误报。
|
|
19
|
+
- **代码实现不再「顺手加固」了**:实现/修复提示词内置 HERO 反过度防御块——不加任务没要的哈希与校验和、不铺层层边界分支、不自造评分标准、不做顺手加固;只约束「提出什么」不约束「检查什么」,真实风险照找、写进总结由你决定。改动面更贴任务目标,少一轮「修过度工程」的返工。
|
|
17
20
|
|
|
18
|
-
###
|
|
21
|
+
### 📒 补记(更早已上线、此前未入版本节)
|
|
19
22
|
|
|
20
|
-
-
|
|
21
|
-
- **超时不再原样重烧**:章稿起草失败重试加「新变量」闸门——上一次就是超时/停滞收场、且这次的提示词与上次逐字节相同时(缩上下文降级未能生效的场景),不再用同样输入再烧一整轮 2400 秒,直接跳到换将(换 CLI/模型才是新机会)。
|
|
23
|
+
- 直连对话流式打印思考过程 + 网页成品实时预览页签 + 直连 `run_command` 真执行 + 连载分卷 + 多评审并发(同章两评审 7.2s→4.2s)+ 超时原样重烧闸门 + 429 限流与欠费同口径切备用 KEY + 工作汇报取禅道数 + 日志抽屉折叠 claude 思考心跳。
|
|
22
24
|
|
|
23
|
-
|
|
25
|
+
## v0.1.62(2026-09-24)
|
|
24
26
|
|
|
25
|
-
-
|
|
27
|
+
- 修复评审「命令行太长」:垫片检测此前只看首个参数(是 cmd.exe),漏判了真正的 .cmd 垫片——中等长度评审提示词直接撞 Windows 8191 上限。现扫全部启动参数
|
|
28
|
+
- 代码修复步超时 1200→2400 秒:真实排查型修复(读仓库/追调用链)不再被中途砍死
|
|
26
29
|
|
|
27
30
|
## v0.1.61(2026-09-24)
|
|
28
31
|
|
package/README.md
CHANGED
|
@@ -20,13 +20,11 @@ Kimi Code、MiMo Code、Grok Build、Pi、DeepSeek Harness……),提供
|
|
|
20
20
|
零第三方依赖:纯 Python 标准库(3.8+),本地 Web 界面,数据全部落盘可回放,
|
|
21
21
|
不会把你的任务内容交给任何第三方。
|
|
22
22
|
|
|
23
|
-
<!-- relnotes:start -->
|
|
24
|
-
### 最新版更新内容(v0.1.61)
|
|
25
23
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
-
|
|
29
|
-
|
|
24
|
+
### 最新版更新内容(v0.1.63)
|
|
25
|
+
|
|
26
|
+
- 修复评审「命令行太长」(垫片漏判);修复步超时提至 2400 秒
|
|
27
|
+
|
|
30
28
|
|
|
31
29
|
---
|
|
32
30
|
|
package/app/core/catalog.py
CHANGED
|
@@ -132,7 +132,11 @@ DEFAULT_CATALOG = [
|
|
|
132
132
|
"note": "xAI 终端编码智能体;可执行名是 grok(不是 grok-build);"
|
|
133
133
|
"默认模型在 ~/.grok/config.toml 的 [models] default(JSON 版配置不存在)",
|
|
134
134
|
"detect": {"cli": "grok"},
|
|
135
|
-
|
|
135
|
+
# Grok's CLI retries a failed reqwest connection internally. Keep the
|
|
136
|
+
# adapter's single attempt bounded so a dead endpoint does not consume
|
|
137
|
+
# the entire 20-minute generic timeout before failover.
|
|
138
|
+
"orch": {"kind": "generic", "command": "grok", "argv_template": ["-p", "{prompt}"],
|
|
139
|
+
"timeout_ms": 180000, "stall_timeout_s": 120},
|
|
136
140
|
"config": {"path": "~/.grok/config.toml", "format": "toml-section",
|
|
137
141
|
"model_key": "models.default"},
|
|
138
142
|
"install": "npm install -g @xai-official/grok",
|
|
@@ -319,19 +323,20 @@ def _apply_config_patch(entries):
|
|
|
319
323
|
e["config"] = dict(patch)
|
|
320
324
|
|
|
321
325
|
|
|
322
|
-
def _apply_stall_patch(entries):
|
|
323
|
-
"""
|
|
324
|
-
|
|
325
|
-
data/catalog.json
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
orch
|
|
326
|
+
def _apply_stall_patch(entries):
|
|
327
|
+
"""为存量清单补齐 CLI 看门狗和单步上限。
|
|
328
|
+
|
|
329
|
+
data/catalog.json 生成后不会再随版本重写,用户手写过的字段不覆盖
|
|
330
|
+
(包括显式写 0 关闭的)。"""
|
|
331
|
+
for e in entries:
|
|
332
|
+
orch = e.get("orch")
|
|
333
|
+
if not isinstance(orch, dict):
|
|
334
|
+
continue
|
|
335
|
+
if e.get("id") == "qwencode":
|
|
336
|
+
orch.setdefault("stall_timeout_s", 900)
|
|
337
|
+
elif e.get("id") == "grok-build":
|
|
338
|
+
orch.setdefault("timeout_ms", 180000)
|
|
339
|
+
orch.setdefault("stall_timeout_s", 120)
|
|
335
340
|
|
|
336
341
|
|
|
337
342
|
def _apply_install_patch(entries):
|
package/app/core/pipeline.py
CHANGED
|
@@ -722,6 +722,10 @@ __CONTEXT__
|
|
|
722
722
|
|
|
723
723
|
## 要求
|
|
724
724
|
- 修改要最小化、聚焦任务目标;不要无关重构。
|
|
725
|
+
- 反过度防御(借鉴 HERO:只约束你「提出」的方案,不约束你「检查」的范围)——
|
|
726
|
+
不要给没要求的数据加哈希/校验和;不要为内部脚本铺层层边界分支;
|
|
727
|
+
不要自造评分标准或质量闸门;超出任务目标的「顺手加固」一律不做。
|
|
728
|
+
真实风险照样要找:发现就写进总结,由人决定要不要修。
|
|
725
729
|
- 完成后只用 3-5 句话总结你改了什么、为什么。
|
|
726
730
|
__VERIFY_HINT__"""
|
|
727
731
|
|
|
@@ -735,6 +739,8 @@ __ISSUES__
|
|
|
735
739
|
|
|
736
740
|
## 要求
|
|
737
741
|
- 只针对上述问题修复;不要无关重构。
|
|
742
|
+
- 修复轮同样反过度防御:不加任务没要的哈希/边界分支/评分标准/顺手加固;
|
|
743
|
+
真实风险写进说明,不擅自扩大改动面。
|
|
738
744
|
- 完成后用 2-3 句话说明改了什么。
|
|
739
745
|
__VERIFY_HINT__"""
|
|
740
746
|
|
|
@@ -1203,6 +1209,7 @@ def _run_code(run, task, agents, ev, stats, mode):
|
|
|
1203
1209
|
store.update_run(run_id, route=route)
|
|
1204
1210
|
|
|
1205
1211
|
def implement_all(impl_agent, prefix_note):
|
|
1212
|
+
nonlocal reviewer
|
|
1206
1213
|
# 会话延续只对原实现者有效;换将后新智能体没有该会话,必须丢弃
|
|
1207
1214
|
use_resume = (resume_ctx["session"]
|
|
1208
1215
|
if (resume_ctx and impl_agent["id"] == resume_ctx["agent"]["id"]) else None)
|
|
@@ -1247,6 +1254,7 @@ def _run_code(run, task, agents, ev, stats, mode):
|
|
|
1247
1254
|
.replace("__CONTEXT__", task.get("context") or "(无)")
|
|
1248
1255
|
.replace("__VERIFY_HINT__", _verify_hint(task))) + prog
|
|
1249
1256
|
role = "implement" if len(subtasks) == 1 else "implement-%d/%d" % (i + 1, len(subtasks))
|
|
1257
|
+
mark_task_plan(workdir, i + 1, "run") # 活计划:起跑即标进行中
|
|
1250
1258
|
res = _run_step(run_id, role, agt_b, prompt, step_wd,
|
|
1251
1259
|
readonly=False, ev=ev,
|
|
1252
1260
|
note=prefix_note if i == 0 else "",
|
|
@@ -1265,7 +1273,9 @@ def _run_code(run, task, agents, ev, stats, mode):
|
|
|
1265
1273
|
except Exception:
|
|
1266
1274
|
pass
|
|
1267
1275
|
if not res["ok"]:
|
|
1276
|
+
mark_task_plan(workdir, i + 1, "fail")
|
|
1268
1277
|
return False, res
|
|
1278
|
+
mark_task_plan(workdir, i + 1, "done")
|
|
1269
1279
|
return True, res
|
|
1270
1280
|
|
|
1271
1281
|
ok, res = _run_one(impl_agent)
|
|
@@ -1293,8 +1303,16 @@ def _run_code(run, task, agents, ev, stats, mode):
|
|
|
1293
1303
|
# (超时等)仍只换一次,防着在坏候选上再烧一整个超时。
|
|
1294
1304
|
if mode == "auto" and impl_agent.get("mode") == "real":
|
|
1295
1305
|
tried = {impl_agent["id"], "mock-a", "mock-b"}
|
|
1306
|
+
dead_ids = {impl_agent["id"]} # 实现步已失败的原实现者,属已知死候选
|
|
1296
1307
|
notes = []
|
|
1297
1308
|
dead_ups = [] # 配额死亡候选的上游集合:换将优先异上游
|
|
1309
|
+
# 原实现者若死于配额,其上游(网关/账号)也一并判入死池——否则第一棒
|
|
1310
|
+
# 换将不知道同网关候选已死,评审者重选更会把「与死者同上游」的候选
|
|
1311
|
+
# 继续当活口,评审撞同一条失效网关白烧一轮后误报「评审器故障」。
|
|
1312
|
+
if runner._quota_error(res.get("error") or ""):
|
|
1313
|
+
ups0 = router.agent_upstreams(impl_agent["id"])
|
|
1314
|
+
if ups0:
|
|
1315
|
+
dead_ups.append(ups0)
|
|
1298
1316
|
prev_id = impl_agent["id"]
|
|
1299
1317
|
other = None
|
|
1300
1318
|
while True:
|
|
@@ -1310,6 +1328,14 @@ def _run_code(run, task, agents, ev, stats, mode):
|
|
|
1310
1328
|
ok2, res = _run_one(other)
|
|
1311
1329
|
if ok2:
|
|
1312
1330
|
route_note = ";".join(notes + [note])
|
|
1331
|
+
# 换将成功:预先选定的评审者可能在实现步走查时被证伪为死链
|
|
1332
|
+
# (quota/限流秒死),且死者同上游(同网关)的候选同样不可信。
|
|
1333
|
+
# 按「排除本轮已知死者 + 同上游候选」的池重选评审者,否则评审
|
|
1334
|
+
# 撞同一条失效网关,白烧一轮后误报「评审器故障」。
|
|
1335
|
+
if workflow["review_required"]:
|
|
1336
|
+
reviewer, route["reviewer"] = router.pick_reviewer(
|
|
1337
|
+
agents, other, "code", stats,
|
|
1338
|
+
exclude=dead_ids, dead_upstreams=dead_ups)
|
|
1313
1339
|
store.update_run(run_id, error="", route_note=route_note)
|
|
1314
1340
|
_record_actual_route(
|
|
1315
1341
|
run_id, task, agents, stats, other,
|
|
@@ -1320,6 +1346,7 @@ def _run_code(run, task, agents, ev, stats, mode):
|
|
|
1320
1346
|
return True
|
|
1321
1347
|
notes.append(note)
|
|
1322
1348
|
tried.add(other["id"])
|
|
1349
|
+
dead_ids.add(other["id"])
|
|
1323
1350
|
prev_id = other["id"]
|
|
1324
1351
|
if runner._quota_error(res.get("error") or ""):
|
|
1325
1352
|
ups = router.agent_upstreams(other["id"])
|
|
@@ -1390,7 +1417,7 @@ def _run_code(run, task, agents, ev, stats, mode):
|
|
|
1390
1417
|
.replace("__VERIFY_HINT__", _verify_hint(task)))
|
|
1391
1418
|
prompt = attachments.append_task_context(prompt, task)
|
|
1392
1419
|
res = _run_step(run_id, "fix-r%d" % round_no, modelhub.bind_agent(impl, difficulty),
|
|
1393
|
-
prompt, workdir, readonly=False, ev=ev,
|
|
1420
|
+
prompt, workdir, readonly=False, ev=ev, timeout=2400,
|
|
1394
1421
|
note="自动修复第 %d 轮" % round_no,
|
|
1395
1422
|
resume=resume_ctx["session"] if resume_ctx else impl_sid[0],
|
|
1396
1423
|
require_tools=True)
|
|
@@ -4106,14 +4133,47 @@ def _write_task_plan(task, workdir, plan):
|
|
|
4106
4133
|
return ""
|
|
4107
4134
|
target.parent.mkdir(parents=True, exist_ok=True)
|
|
4108
4135
|
lines = ["# 任务计划", "", "来源:%s" % (plan or {}).get("source", "?"), ""]
|
|
4136
|
+
# checkbox 形态:mark_task_plan 按行号翻 [x]/[!]/[>],断点一眼可见
|
|
4109
4137
|
for i, s in enumerate((plan or {}).get("steps") or [], 1):
|
|
4110
|
-
lines.append("%d. %s" % (i, str(s.get("detail") or s.get("title") or "")[:200]))
|
|
4138
|
+
lines.append("%d. [ ] %s" % (i, str(s.get("detail") or s.get("title") or "")[:200]))
|
|
4111
4139
|
target.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
|
4112
4140
|
return str(target)
|
|
4113
4141
|
except Exception:
|
|
4114
4142
|
return ""
|
|
4115
4143
|
|
|
4116
4144
|
|
|
4145
|
+
_PLAN_MARK = {"done": "x", "fail": "!", "run": ">"}
|
|
4146
|
+
|
|
4147
|
+
|
|
4148
|
+
def mark_task_plan(workdir, index, status):
|
|
4149
|
+
"""活计划回写:把 .codebee/task_plan.md 第 index 项(1 基)的勾选框
|
|
4150
|
+
翻成 [x](完成)/[!](失败)/[>](进行中)。planning-with-files 的断点
|
|
4151
|
+
可见性:计划文件随执行实时推进,崩溃/换将/续跑时看文件即知走到哪步。
|
|
4152
|
+
幂等覆盖(换将重试成功 [!]→[x]);失败静默——回写绝不挡执行。"""
|
|
4153
|
+
try:
|
|
4154
|
+
from pathlib import Path as _P
|
|
4155
|
+
import re as _re
|
|
4156
|
+
mark = _PLAN_MARK.get(str(status))
|
|
4157
|
+
idx = int(index)
|
|
4158
|
+
if not mark or idx < 1 or not workdir:
|
|
4159
|
+
return False
|
|
4160
|
+
root = _P(workdir).resolve()
|
|
4161
|
+
target = (root / ".codebee" / "task_plan.md").resolve()
|
|
4162
|
+
if root not in target.parents or not target.is_file():
|
|
4163
|
+
return False
|
|
4164
|
+
pat = _re.compile(r"^(\d+)\. \[[ x!>]\] ")
|
|
4165
|
+
lines = target.read_text("utf-8").splitlines()
|
|
4166
|
+
for k, ln in enumerate(lines):
|
|
4167
|
+
m = pat.match(ln)
|
|
4168
|
+
if m and int(m.group(1)) == idx:
|
|
4169
|
+
lines[k] = "%d. [%s] %s" % (idx, mark, ln[m.end():])
|
|
4170
|
+
target.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
|
4171
|
+
return True
|
|
4172
|
+
return False
|
|
4173
|
+
except Exception:
|
|
4174
|
+
return False
|
|
4175
|
+
|
|
4176
|
+
|
|
4117
4177
|
def _workdir_blocker(task, run_id):
|
|
4118
4178
|
"""同工作目录互斥:返回占用该目录的运行中/排队任务(无则 None)。
|
|
4119
4179
|
任务计划/项目记忆/任务分支检出都是工作目录级共享状态,并行会互相覆盖
|